TKK_E32230273/api_wifi.php

614 lines
29 KiB
PHP

<?php
// Set header ke format JSON karena API ini selalu mengembalikan data JSON
header('Content-Type: application/json');
// File yang digunakan untuk menyimpan status dan perintah sementara ESP32
$stateFile = 'esp32_state.json';
// Inisialisasi file status dengan nilai bawaan jika file tersebut belum ada
if (!file_exists($stateFile)) {
$initialState = [
"command" => "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"]);
?>