614 lines
29 KiB
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"]);
|
|
?>
|