import re with open('esp32_serial_backend/esp32_serial_backend.ino', 'r', encoding='utf8') as f: content = f.read() # 1. Add gIsWebJob if 'bool gIsWebJob = false;' not in content: content = content.replace('String gKeepTryingCmd = "";', 'String gKeepTryingCmd = "";\nbool gIsWebJob = false;') # 2. Add postJobResult function if 'void postJobResult' not in content: post_job_result_func = '''void postJobResult(bool ok) { if (!gIsWebJob) return; String type = gKeepTryingCmd.startsWith("ENROLL") ? "ENROLL" : "RECOG"; String name = type == "ENROLL" ? gKeepTryingCmd.substring(7) : ""; String result = "{\\"from\\":\\"esp32\\",\\"type\\":\\"" + type + "\\",\\"ok\\":" + (ok ? "true" : "false"); if (name.length()) result += ",\\"name\\":\\"" + name + "\\""; if (gJobRecogUid.length()) result += ",\\"uid\\":\\"" + gJobRecogUid + "\\""; if (gJobPersonId.length()) result += ",\\"person_id\\":\\"" + gJobPersonId + "\\""; if (type == "RECOG") result += ",\\"threshold\\":" + String(gJobRecogThreshold, 2); result += "}"; String jr = postJson(String(SERVER_BASE) + "/job/result", result, 6000); Serial.println(String("[JOB] result post=") + jr); } ''' content = content.replace('bool handleCommand(const String &cmd);', 'bool handleCommand(const String &cmd);\n\n' + post_job_result_func) # 3. Replace Web Job trigger logic in pollAndRunJob old_poll_logic = ''' if (type == "ENROLL") { showStatus("TAKE ENROLL", TFT_CYAN); Serial.println(String("[JOB] ENROLL start name=") + name); gCameraActive = true; bool enrollOk = handleCommand(String("ENROLL ") + name); gCameraActive = false; String result = "{\\"from\\":\\"esp32\\",\\"type\\":\\"ENROLL\\",\\"name\\":\\"" + name + "\\",\\"ok\\":" + (enrollOk ? "true" : "false"); if (uid.length()) result += ",\\"uid\\":\\"" + uid + "\\""; if (personId.length()) result += ",\\"person_id\\":\\"" + personId + "\\""; result += "}"; String jr = postJson(String(SERVER_BASE) + "/job/result", result, 6000); Serial.println(String("[JOB] ENROLL result post=") + jr); if (jr.startsWith("HTTP") || jr.startsWith("WIFI")) showStatus("SERVER DISABLE/RTO", TFT_RED, ""); return; } if (type == "RECOG") { gJobRecogThreshold = thr; gJobRecogUid = uid; gJobPersonId = personId; showStatus("TAKE RECOG", TFT_CYAN); Serial.println(String("[JOB] RECOG start thr=") + String(thr, 2) + String(" uid=") + uid); Serial.println(String("[JOB] WiFi=") + (WiFi.status() == WL_CONNECTED ? "OK" : "DISCONNECTED")); showStatus("RECOG...", TFT_CYAN); gCameraActive = true; bool recogOk = handleCommand("RECOG"); gCameraActive = false; String result = String("{\\"from\\":\\"esp32\\",\\"type\\":\\"RECOG\\",\\"threshold\\":") + String(thr, 2) + String(",\\"uid\\":\\"") + uid + String("\\",\\"ok\\":") + (recogOk ? "true" : "false") + String("}"); String jr = postJson(String(SERVER_BASE) + "/job/result", result, 6000); Serial.println(String("[JOB] RECOG result post=") + jr); if (jr.startsWith("HTTP") || jr.startsWith("WIFI")) showStatus("SERVER DISABLE/RTO", TFT_RED, ""); return; }''' new_poll_logic = ''' if (type == "ENROLL") { Serial.println(String("[JOB] ENROLL start name=") + name); gKeepTryingCmd = String("ENROLL ") + name; gKeepTryingUntil = millis() + 15000; gLastActionSuccess = false; gIsWebJob = true; // Tunda capture pertama selama 3 detik agar pengguna bisa melihat preview wajahnya gNextCaptureTime = millis() + 3000; gCameraActive = true; return; } if (type == "RECOG") { gJobRecogThreshold = thr; gJobRecogUid = uid; gJobPersonId = personId; Serial.println(String("[JOB] RECOG start thr=") + String(thr, 2) + String(" uid=") + uid); gKeepTryingCmd = "RECOG"; gKeepTryingUntil = millis() + 15000; gLastActionSuccess = false; gIsWebJob = true; // Tunda capture pertama selama 3 detik agar pengguna bisa melihat preview wajahnya gNextCaptureTime = millis() + 3000; gCameraActive = true; return; }''' content = content.replace(old_poll_logic, new_poll_logic) # 4. Modify loop() timeout and success old_timeout = ''' // Timeout gKeepTryingCmd = ""; gCameraActive = false; showStatus("TIMEOUT", TFT_RED, "Wajah tidak dikenali", 3000);''' new_timeout = ''' // Timeout postJobResult(false); gKeepTryingCmd = ""; gCameraActive = false; showStatus("TIMEOUT", TFT_RED, "Wajah tidak dikenali", 3000);''' content = content.replace(old_timeout, new_timeout) old_success = ''' if (gLastActionSuccess) { gKeepTryingCmd = ""; // Stop trying gCameraActive = false; // Turn off camera on success showStatus("BERHASIL", TFT_GREEN, "", 3000); }''' new_success = ''' if (gLastActionSuccess) { postJobResult(true); gKeepTryingCmd = ""; // Stop trying gCameraActive = false; // Turn off camera on success showStatus("BERHASIL", TFT_GREEN, "", 3000); }''' content = content.replace(old_success, new_success) # 5. Set gIsWebJob = false in RFID if 'gKeepTryingUntil = millis() + 15000;' in content: content = content.replace('gKeepTryingUntil = millis() + 15000;\n gLastActionSuccess = false;\n // Tunda capture pertama selama 3 detik', 'gKeepTryingUntil = millis() + 15000;\n gLastActionSuccess = false;\n gIsWebJob = false;\n // Tunda capture pertama selama 3 detik') with open('esp32_serial_backend/esp32_serial_backend.ino', 'w', encoding='utf8') as f: f.write(content) print("Update complete")