"IDLE", // Perintah awal (Tidak ada perintah) "ssid" => "", // Nama WiFi untuk diubah "password" => "", // Password WiFi untuk diubah "scan_results" => [], // Tempat menyimpan hasil scan WiFi "status" => "offline", // Status koneksi ESP32 "current_ssid" => "Unknown", // WiFi yang sedang terhubung ke ESP32 "last_seen" => 0, // Waktu terakhir ESP32 mengirim data (timestamp) "esp_ip" => "Unknown", // Alamat IP dari ESP32 "change_status" => "idle", // Status saat proses penggantian WiFi "rssi" => -90, // Kekuatan sinyal WiFi "tft_force_screen" => "none" // Perintah paksa pindah layar pada layar sentuh TFT ]; // Simpan nilai bawaan ke dalam file JSON file_put_contents($stateFile, json_encode($initialState)); } // Baca isi file status dan ubah dari format JSON menjadi array PHP $state = json_decode(file_get_contents($stateFile), true); // Jika hasil bacaan bukan array (mungkin file rusak), atur ulang ke nilai bawaan if (!is_array($state)) { $state = [ "command" => "IDLE", "ssid" => "", "password" => "", "scan_results" => [], "status" => "offline", "current_ssid" => "Unknown", "last_seen" => 0, "esp_ip" => "Unknown", "change_status" => "idle", "rssi" => -90, "tft_force_screen" => "none" ]; } // Ambil parameter 'action' dari URL (GET) atau form (POST) $action = isset($_GET['action']) ? $_GET['action'] : (isset($_POST['action']) ? $_POST['action'] : ''); // ========================================== // ENDPOINT UNTUK DASHBOARD WEB (DIPANGGIL VIA AJAX) // ========================================== // Aksi untuk menyuruh ESP32 memindai daftar WiFi di sekitarnya if ($action == 'trigger_scan') { $state['command'] = 'SCAN'; $state['scan_results'] = []; // Hapus hasil scan sebelumnya agar data bersih file_put_contents($stateFile, json_encode($state)); // Simpan perintah ke file echo json_encode(["success" => true, "message" => "Scan triggered"]); exit; } // Aksi untuk menyalakan Bluetooth pada ESP32 if ($action == 'trigger_bt_on') { $state['command'] = 'BT_ON'; $state['bt_status'] = 'pending'; // Status menunggu ESP32 merespons file_put_contents($stateFile, json_encode($state)); echo json_encode(["success" => true, "message" => "Bluetooth ON command queued"]); exit; } // Aksi untuk mematikan Bluetooth pada ESP32 if ($action == 'trigger_bt_off') { $state['command'] = 'BT_OFF'; $state['bt_status'] = 'off'; file_put_contents($stateFile, json_encode($state)); echo json_encode(["success" => true, "message" => "Bluetooth OFF command queued"]); exit; } // Mengambil status Bluetooth saat ini untuk ditampilkan di dashboard if ($action == 'get_bt_status') { echo json_encode([ "success" => true, "bt_status" => isset($state['bt_status']) ? $state['bt_status'] : 'off' ]); exit; } // Mengambil hasil scan WiFi yang sudah selesai diproses ESP32 if ($action == 'get_scan_results') { // Jika ESP32 sudah selesai memindai if ($state['command'] == 'SCAN_COMPLETE') { echo json_encode(["success" => true, "results" => $state['scan_results']]); // Kembalikan perintah ke IDLE agar tidak mengulang scan $state['command'] = 'IDLE'; file_put_contents($stateFile, json_encode($state)); } else { // Jika masih dalam proses pemindaian echo json_encode(["success" => false, "message" => "Scanning in progress"]); } exit; } // Aksi untuk memberikan instruksi ke ESP32 agar pindah/koneksi ke WiFi lain if ($action == 'change_wifi') { $new_ssid = isset($_POST['ssid']) ? $_POST['ssid'] : ''; $new_pass = isset($_POST['password']) ? $_POST['password'] : ''; // SSID tidak boleh kosong if (empty($new_ssid)) { echo json_encode(["success" => false, "message" => "SSID cannot be empty"]); exit; } // Set perintah ganti WiFi (CHANGE) beserta SSID dan Password baru $state['command'] = 'CHANGE'; $state['ssid'] = $new_ssid; $state['password'] = $new_pass; $state['change_status'] = 'processing'; $state['change_message'] = 'Menunggu ESP32 memproses pergantian WiFi...'; file_put_contents($stateFile, json_encode($state)); echo json_encode(["success" => true, "message" => "Change command queued"]); exit; } // Aksi untuk melihat status keseluruhan ESP32 dari dashboard if ($action == 'get_status') { // Hitung selisih waktu dari laporan terakhir ESP32 ke waktu sekarang $time_diff = time() - $state['last_seen']; // ESP32 dianggap online jika melapor dalam 10 detik terakhir $is_online = $time_diff < 10; $previousStatus = $state['status']; if ($time_diff <= 10) { $state['status'] = 'online'; // Hapus tanda pemberitahuan offline jika sudah kembali online if (isset($state['wifi_notified_offline']) && $state['wifi_notified_offline']) { $state['wifi_notified_offline'] = false; } } else if ($time_diff <= 70) { // Toleransi waktu 60 detik tambahan dengan status 'connecting' sebelum dicap offline $state['status'] = 'connecting'; } else { $state['status'] = 'offline'; // Kirim notifikasi jika sebelumnya belum dikirim (agar tidak spam) if (empty($state['wifi_notified_offline'])) { $state['wifi_notified_offline'] = true; include_once 'koneksi.php'; include_once 'notification_helper.php'; require_once 'PushHelper.php'; $ssidInfo = isset($state['current_ssid']) ? $state['current_ssid'] : 'Unknown'; send_notification($pdo, 'mdi-wifi-off', 'Wi-Fi Disconnected', "The ESP32 microcontroller has disconnected from {$ssidInfo}.", "The system detected that the device went offline and is unresponsive. Last seen: " . date('d M Y H:i:s', $state['last_seen']) . ".", true); PushHelper::broadcastToAdmins($pdo, 'Wi-Fi Disconnected', "The ESP32 microcontroller has disconnected from {$ssidInfo}.", '/dashboard.php'); } } file_put_contents($stateFile, json_encode($state)); // Tampilkan data ke dashboard echo json_encode([ "status" => $state['status'], "current_ssid" => $state['current_ssid'], "last_seen" => $state['last_seen'], "esp_ip" => isset($state['esp_ip']) ? $state['esp_ip'] : "Unknown", "rssi" => isset($state['rssi']) ? $state['rssi'] : -90 ]); exit; } // Mengecek apakah proses pergantian WiFi di ESP32 sudah berhasil, gagal, atau masih proses if ($action == 'check_change_status') { echo json_encode([ "status" => isset($state['change_status']) ? $state['change_status'] : "idle", "message" => isset($state['change_message']) ? $state['change_message'] : "", "reason" => isset($state['change_reason']) ? $state['change_reason'] : "" ]); // Jika proses ganti WiFi sudah mencapai akhir (berhasil/gagal), kembalikan ke status idle if (isset($state['change_status']) && ($state['change_status'] == 'success' || $state['change_status'] == 'failed')) { $state['change_status'] = 'idle'; file_put_contents($stateFile, json_encode($state)); } exit; } // ========================================== // ENDPOINT UNTUK ESP32 (MEMBACA/MENGIRIM DATA DARI HARDWARE) // ========================================== // Endpoint yang dipanggil terus-menerus oleh ESP32 untuk melapor dan mengambil instruksi if ($action == 'esp_poll') { // Tangkap data status terbaru dari ESP32 $current_ssid = isset($_GET['ssid']) ? $_GET['ssid'] : 'Unknown'; $esp_ip = isset($_GET['ip']) ? $_GET['ip'] : 'Unknown'; $rssi = isset($_GET['rssi']) ? (int) $_GET['rssi'] : -90; $bt_on = isset($_GET['bt_on']) ? ($_GET['bt_on'] == '1') : false; // Status bluetooth aktual pada alat // Perbarui data terakhir ESP32 ke dalam file state $state['status'] = 'online'; $state['current_ssid'] = $current_ssid; $state['esp_ip'] = $esp_ip; $state['rssi'] = $rssi; $state['last_seen'] = time(); // Catat waktu lapor agar tahu jika alat mati // Lacak status Bluetooth yang dilaporkan oleh hardware if ($bt_on) { $state['bt_status'] = 'active'; } else if (isset($state['bt_status']) && $state['bt_status'] === 'active') { $state['bt_status'] = 'off'; // Tandai mati jika alat bilang mati } // Respons yang akan diberikan ke ESP32, termasuk perintah saat ini $response = [ "command" => $state['command'], "ssid" => $state['ssid'], "password" => $state['password'], "tft_force_screen" => isset($state['tft_force_screen']) ? $state['tft_force_screen'] : "none" ]; // Sisipkan flag jika ada foto baru yang berhasil diambil oleh sistem web if (isset($state['photo_done']) && $state['photo_done']) { $response['photo_done'] = true; $state['photo_done'] = false; // Reset flag agar tidak dikirim lagi } // Sisipkan flag jika ada form laporan yang sudah disubmit if (isset($state['report_submitted']) && $state['report_submitted']) { $response['report_submitted'] = true; $state['report_submitted'] = false; } // Kirim kode pendaftaran (form code) ke ESP32 jika sedang ada proses registrasi if (isset($state['form_code'])) { $response['form_code'] = $state['form_code']; } // Baca status kunci pintu dari file teks $doorState = 'locked'; // Secara bawaan terkunci if (file_exists('door_status.txt')) { $doorState = trim(file_get_contents('door_status.txt')); } $response["remote_door_state"] = $doorState; // Pengecekan apakah ruangan dikunci secara otomatis berdasarkan jadwal di database include_once 'koneksi.php'; date_default_timezone_set('Asia/Jakarta'); $currentDay = date('l'); $currentTime = date('H:i:s'); try { // Cek apakah ada jadwal pembatasan (room restriction) di waktu ini $stmtRestrict = $pdo->prepare("SELECT COUNT(*) FROM room_restrictions WHERE (day_of_week = ? OR day_of_week = 'Every Day') AND ? BETWEEN time_from AND time_to"); $stmtRestrict->execute([$currentDay, $currentTime]); $response["room_locked"] = $stmtRestrict->fetchColumn() > 0; } catch (Exception $e) { $response["room_locked"] = false; } // Ambil daftar PIN yang sah dan aktif dari penghuni yang berhak $valid_pins = []; $active_fingers = []; try { $stmtPins = $pdo->query("SELECT id_occupant, pin, no_hp, finger_id FROM occupant WHERE progress='used' AND status='approved' AND access='active'"); while ($row = $stmtPins->fetch(PDO::FETCH_ASSOC)) { if (!empty($row['pin'])) { // Ambil 3 digit angka terakhir dari nomor HP untuk keperluan verifikasi khusus Telegram $hp_trim = preg_replace('/[^0-9]/', '', $row['no_hp']); $hp3 = substr($hp_trim, -3); if (empty($hp3)) $hp3 = "000"; // Nilai default jika gagal ambil $valid_pins[] = [ "id" => (int) $row['id_occupant'], "pin" => $row['pin'], "hp3" => $hp3 ]; } if (!empty($row['finger_id'])) { // Kumpulkan ID sidik jari untuk mencocokan pada alat ESP32 $active_fingers[] = $row['finger_id']; } } } catch (Exception $e) { } $response["valid_pins"] = $valid_pins; $response["active_fingers"] = $active_fingers; // Mengecek apakah ada perintah hapus sidik jari dari web yang perlu diteruskan ke alat $deleteQueueFile = 'esp32_finger_delete_queue.json'; if (file_exists($deleteQueueFile)) { $queueData = json_decode(file_get_contents($deleteQueueFile), true); if (is_array($queueData) && count($queueData) > 0) { $response["delete_virtual_fingers"] = $queueData; } } // Hapus instruksi lama agar tidak berulang kali dijalankan oleh ESP32 if ($state['command'] == 'CHANGE' || $state['command'] == 'SCAN') { $state['command'] = 'IDLE'; } else if ($state['command'] == 'BT_ON' && $bt_on) { $state['command'] = 'IDLE'; // Bluetooth sudah dinyalakan alat, perintah direset } else if ($state['command'] == 'BT_OFF' && !$bt_on) { $state['command'] = 'IDLE'; // Bluetooth sudah dimatikan alat, perintah direset } // Reset instruksi paksa layar TFT setelah berhasil dikirim ke ESP32 if (isset($state['tft_force_screen']) && $state['tft_force_screen'] !== 'none') { $state['tft_force_screen'] = 'none'; unset($state['form_code']); } // Simpan semua pembaruan state file_put_contents($stateFile, json_encode($state)); echo json_encode($response); exit; } // Endpoint yang digunakan ESP32 untuk mengirimkan hasil pemindaian jaringan WiFi if ($action == 'esp_post_scan') { $data = json_decode(file_get_contents('php://input'), true); // Simpan hasil jaringan yang ditemukan ESP32 ke dalam file state if (isset($data['networks'])) { $state['scan_results'] = $data['networks']; $state['command'] = 'SCAN_COMPLETE'; // Tandakan scan sudah tuntas $state['last_seen'] = time(); file_put_contents($stateFile, json_encode($state)); echo json_encode(["success" => true]); } else { echo json_encode(["success" => false, "message" => "Invalid data"]); } exit; } // Endpoint yang digunakan ESP32 untuk melapor keberhasilan atau kegagalan menghubungkan WiFi baru if ($action == 'report_change') { $status = isset($_GET['status']) ? $_GET['status'] : 'idle'; $msg = isset($_GET['msg']) ? $_GET['msg'] : ''; $reason = isset($_GET['reason']) ? $_GET['reason'] : ''; // JANGAN TERIMA LAPORAN KADALUWARSA // Jika sistem masih meminta pergantian ('CHANGE'), laporan yang masuk berarti dari proses masa lalu yang lambat. if ($state['command'] == 'CHANGE') { echo json_encode(["success" => true, "ignored_stale" => true, "message" => "Stale report ignored due to pending new command"]); exit; } // Perbarui status sesuai laporan terbaru ESP32 $state['change_status'] = $status; $state['change_message'] = $msg; $state['change_reason'] = $reason; file_put_contents($stateFile, json_encode($state)); echo json_encode(["success" => true]); exit; } // Endpoint untuk mendaftarkan penghuni baru yang diisi lewat layar sentuh TFT alat if ($action == 'register_occupant') { include 'koneksi.php'; include 'notification_helper.php'; $data = json_decode(file_get_contents('php://input'), true); if (!$data) { echo json_encode(["status" => "error", "message" => "Invalid JSON data"]); exit; } // Pastikan field penting tidak kosong $required = ['name', 'no_hp', 'pin']; foreach ($required as $field) { if (!isset($data[$field]) || trim($data[$field]) === '') { echo json_encode(["status" => "error", "message" => "Field $field wajib diisi"]); exit; } } $name = trim($data['name']); $nik_nip = isset($data['nik_nip']) ? trim($data['nik_nip']) : ''; $no_hp = trim($data['no_hp']); $pin = trim($data['pin']); // PIN harus terdiri dari angka dan tepat 6 digit if (!preg_match('/^\d{6}$/', $pin)) { echo json_encode(["status" => "error", "message" => "PIN harus 6 digit angka"]); exit; } // Cegah nomor HP ganda di sistem $cek = $pdo->prepare("SELECT COUNT(*) FROM occupant WHERE no_hp = ?"); $cek->execute([$no_hp]); if ($cek->fetchColumn() > 0) { echo json_encode(["status" => "error", "message" => "Nomor Handphone sudah digunakan"]); exit; } // Fungsi membuat kode register 6 digit acak dan memastikan belum dipakai di database function generateCodeRegisterESP($pdo) { do { $code = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT); $check = $pdo->prepare("SELECT COUNT(*) FROM occupant WHERE code_register = ?"); $check->execute([$code]); } while ($check->fetchColumn() > 0); return $code; } $code_register = generateCodeRegisterESP($pdo); $foto = ''; // Foto awal dikosongkan karena belum ambil gambar try { // Simpan data calon penghuni baru dengan status tertunda (suspend/rejected) sampai disetujui admin $stmt = $pdo->prepare(" INSERT INTO occupant (name, nik_nip, no_hp, pin, foto, code_register, progress, status, access) VALUES (?, ?, ?, ?, ?, ?, 'unused', 'rejected', 'suspend') "); $stmt->execute([$name, $nik_nip, $no_hp, $pin, $foto, $code_register]); $newId = $pdo->lastInsertId(); // Kirim notifikasi sistem send_notification($pdo, 'mdi-account-multiple-plus', 'New Occupant Registration', "A new occupant registered via the TFT touchscreen: {$name}.", "Phone: {$no_hp}. Pending verification.", false); require_once 'PushHelper.php'; PushHelper::broadcastToAdmins($pdo, 'New Occupant Registration', "{$name} registered and awaits verification.", '/manage_occupant.php'); // Buat token unik sementara untuk pendaftaran foto dari web/ponsel $token = hash('sha256', random_bytes(32) . $newId . microtime(true)); $stmtToken = $pdo->prepare("INSERT INTO foto_tokens (id_occupant, token, status) VALUES (?, ?, 'active')"); $stmtToken->execute([$newId, $token]); // Buat link (URL) upload foto $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST']; $basePath = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/\\'); $url = $protocol . '://' . $host . $basePath . '/registrasi_foto.php?token=' . $token; // Balas ESP32 dengan sukses dan URL registrasi fotonya echo json_encode([ "status" => "success", "code_register" => $code_register, "id_occupant" => $newId, "foto_url" => $url ]); } catch (PDOException $e) { echo json_encode([ "status" => "error", "message" => "Gagal menyimpan: " . $e->getMessage() ]); } exit; } // Mengecek validitas kode pendaftaran yang diinput di ESP32 if ($action == 'check_code_register') { include 'koneksi.php'; $code = isset($_GET['code']) ? trim($_GET['code']) : (isset($_POST['code']) ? trim($_POST['code']) : ''); if (empty($code)) { echo json_encode(["status" => "error", "message" => "Kode pendaftaran kosong"]); exit; } $stmt = $pdo->prepare("SELECT id_occupant, foto, progress FROM occupant WHERE code_register = ?"); $stmt->execute([$code]); $occupant = $stmt->fetch(); if (!$occupant) { echo json_encode(["status" => "error", "message" => "Kode tidak ditemukan"]); exit; } // Jika kodenya sudah pernah digunakan, tolak if ($occupant['progress'] === 'used') { echo json_encode(["status" => "error", "message" => "Kode sudah digunakan!"]); exit; } // Cek apakah pengguna sudah melengkapi upload foto if (!empty($occupant['foto'])) { echo json_encode([ "status" => "success", "code_register" => $code, "id_occupant" => $occupant['id_occupant'], "has_foto" => true ]); } else { // Jika belum ada foto, buatkan token link lagi $token = hash('sha256', random_bytes(32) . $occupant['id_occupant'] . microtime(true)); $stmtToken = $pdo->prepare("INSERT INTO foto_tokens (id_occupant, token, status) VALUES (?, ?, 'active')"); $stmtToken->execute([$occupant['id_occupant'], $token]); $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST']; $basePath = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/\\'); $url = $protocol . '://' . $host . $basePath . '/registrasi_foto.php?token=' . $token; echo json_encode([ "status" => "success", "code_register" => $code, "id_occupant" => $occupant['id_occupant'], "has_foto" => false, "foto_url" => $url ]); } exit; } // Mengecek apakah alat ESP32 sudah selesai mengambil sidik jari (via web dashboard biasanya) if ($action == 'check_finger_status') { include_once 'koneksi.php'; $code = isset($_REQUEST['code']) ? trim($_REQUEST['code']) : ''; if (empty($code)) { echo json_encode(["status" => "error", "message" => "Code is empty"]); exit; } $stmt = $pdo->prepare("SELECT finger_id FROM occupant WHERE code_register = ?"); $stmt->execute([$code]); $row = $stmt->fetch(); // Jika sidik jari tidak kosong dan valid, potong ID dengan hash agar aman dikirim ulang if ($row && !empty($row['finger_id']) && $row['finger_id'] !== '-' && $row['finger_id'] !== '0' && strtolower(trim($row['finger_id'])) !== 'null') { $hashedFingerId = substr(hash('sha256', $row['finger_id']), 0, 9); echo json_encode(["status" => "success", "finger_id" => $hashedFingerId]); } else { echo json_encode(["status" => "waiting"]); // Tunggu alat kirim ID jari } exit; } // Menyimpan data ID sidik jari (finger_id) yang baru saja berhasil di-scan oleh alat ESP32 if ($action == 'register_finger') { include_once 'koneksi.php'; // Ambil kode register dan ID jari dari URL atau body JSON $code = isset($_REQUEST['code']) ? trim($_REQUEST['code']) : ''; $finger_id = isset($_REQUEST['finger_id']) ? trim($_REQUEST['finger_id']) : ''; if (empty($code) || empty($finger_id)) { $data = json_decode(file_get_contents('php://input'), true); if ($data) { $code = isset($data['code']) ? trim($data['code']) : $code; $finger_id = isset($data['finger_id']) ? trim($data['finger_id']) : $finger_id; } } if (!empty($code) && !empty($finger_id)) { try { // Update tabel penghuni, tandakan kodenya sudah terpakai $stmt = $pdo->prepare("UPDATE occupant SET finger_id = ?, progress = 'used' WHERE code_register = ?"); $stmt->execute([$finger_id, $code]); echo json_encode(["status" => "success", "message" => "Finger ID $finger_id berhasil tersimpan"]); } catch (PDOException $e) { echo json_encode(["status" => "error", "message" => "Database error: " . $e->getMessage()]); } } else { echo json_encode(["status" => "error", "message" => "Data req kosong"]); } exit; } // Memproses akses buka pintu dengan sidik jari if ($action == 'fingerprint_access') { include 'koneksi.php'; include 'notification_helper.php'; require_once 'PushHelper.php'; $finger_id_req = isset($_GET['finger_id']) ? trim($_GET['finger_id']) : (isset($_POST['finger_id']) ? trim($_POST['finger_id']) : ''); // Buka kunci pintu dengan menuliskan status "unlocked" ke file $statusFile = 'door_status.txt'; file_put_contents($statusFile, 'unlocked'); $id_occupant = 1; // Nilai bawaan jika ID tidak ditemukan if (!empty($finger_id_req)) { $stmt = $pdo->prepare("SELECT id_occupant, name FROM occupant WHERE finger_id = ?"); $stmt->execute([$finger_id_req]); $occ = $stmt->fetch(); if ($occ) { $id_occupant = $occ['id_occupant']; // Kirim notifikasi jika pengguna dikenali send_notification($pdo, 'mdi-fingerprint', 'Door Unlocked (Biometric)', "The FTM room door was unlocked via fingerprint by {$occ['name']}.", 'System recorded a door unlock event.', false); PushHelper::broadcastToAdmins($pdo, 'Door Unlocked', "The FTM room door was unlocked via fingerprint by {$occ['name']}.", '/access.php'); } else { // Kirim peringatan jika sidik jari belum dikenal/misterius send_notification($pdo, 'mdi-fingerprint', 'Door Unlocked', "The door was unlocked using an unrecognized Finger ID ({$finger_id_req}).", 'Please consult logs.', false); } } else { send_notification($pdo, 'mdi-fingerprint', 'Door Unlocked', 'The door was unlocked via fingerprint (No ID parsed).', 'Please consult logs.', false); } // Membuat token validasi laporan jika masuk via kode QR $report_token = isset($_GET['report_token']) ? trim($_GET['report_token']) : ''; if (!empty($report_token)) { $tokenFile = __DIR__ . '/valid_report_tokens.json'; $validTokens = []; if (file_exists($tokenFile)) { $validTokens = json_decode(file_get_contents($tokenFile), true) ?: []; } $validTokens[$report_token] = [ 'occ_id' => $id_occupant, 'created' => time(), 'type' => 'fingerprint' ]; file_put_contents($tokenFile, json_encode($validTokens, JSON_PRETTY_PRINT)); } echo json_encode([ "status" => "success", "id_occupant" => $id_occupant, "message" => "Door unlocked" ]); exit; } // Memproses akses buka pintu dengan kode PIN darurat if ($action == 'emergency_access') { include 'koneksi.php'; include 'notification_helper.php'; require_once 'PushHelper.php'; $pin = isset($_GET['pin']) ? trim($_GET['pin']) : (isset($_POST['pin']) ? trim($_POST['pin']) : ''); // Buka kunci pintu dengan menuliskan status "unlocked" ke file $statusFile = 'door_status.txt'; file_put_contents($statusFile, 'unlocked'); $id_occupant = 1; // Nilai bawaan if (!empty($pin)) { $stmt = $pdo->prepare("SELECT id_occupant, name FROM occupant WHERE pin = ?"); $stmt->execute([$pin]); $occ = $stmt->fetch(); if ($occ) { $id_occupant = $occ['id_occupant']; send_notification($pdo, 'mdi-door-open', 'Door Unlocked', "The FTM room door was unlocked via emergency access by {$occ['name']}.", 'System recorded a door unlock event.', false); PushHelper::broadcastToAdmins($pdo, 'Door Unlocked', "The FTM room door was unlocked via emergency contact by {$occ['name']}.", '/access.php'); } else { send_notification($pdo, 'mdi-door-open', 'Door Unlocked', "The door was unlocked using an unknown PIN.", 'Please consult the system logs.', false); } } else { // Fallback kalau kosong send_notification($pdo, 'mdi-door-open', 'Door Unlocked', 'The door was unlocked via emergency access (No PIN parsed).', 'Please consult the system logs.', false); } // Membuat token validasi laporan jika alat membutuhkannya $report_token = isset($_GET['report_token']) ? trim($_GET['report_token']) : ''; if (!empty($report_token)) { $tokenFile = __DIR__ . '/valid_report_tokens.json'; $validTokens = []; if (file_exists($tokenFile)) { $validTokens = json_decode(file_get_contents($tokenFile), true) ?: []; } $validTokens[$report_token] = [ 'occ_id' => $id_occupant, 'created' => time(), 'type' => 'emergency' ]; file_put_contents($tokenFile, json_encode($validTokens, JSON_PRETTY_PRINT)); } echo json_encode([ "status" => "success", "id_occupant" => $id_occupant, "message" => "Door unlocked" ]); exit; } // Mengubah tampilan layar pada ESP32 secara paksa dari sistem if ($action == 'set_tft_screen') { $screen = isset($_POST['screen']) ? $_POST['screen'] : ''; $form_code = isset($_POST['form_code']) ? $_POST['form_code'] : ''; if (!empty($screen)) { $state['tft_force_screen'] = $screen; if (!empty($form_code)) { $state['form_code'] = $form_code; // Selipkan kode registrasi } else { unset($state['form_code']); } file_put_contents($stateFile, json_encode($state)); echo json_encode(["success" => true, "message" => "Screen updated"]); } else { echo json_encode(["success" => false, "message" => "Screen cannot be empty"]); } exit; } // Aksi untuk mengosongkan antrean hapus sidik jari setelah berhasil diproses ESP32 if ($action == 'clear_delete_queue') { $deleteQueueFile = 'esp32_finger_delete_queue.json'; if (file_exists($deleteQueueFile)) { unlink($deleteQueueFile); // Hapus antrean } echo json_encode(["status" => "success", "message" => "Queue cleared"]); exit; } // Jika 'action' tidak terdaftar atau kosong, respon invalid action echo json_encode(["success" => false, "message" => "Invalid action"]); ?>