This commit is contained in:
[Valentino Heman Budiarto] 2026-06-05 20:46:51 +07:00
parent 679461d3a3
commit 89c68f340f
2 changed files with 24 additions and 23 deletions

View File

@ -153,17 +153,19 @@ func ControlHardware(c *gin.Context) {
haToken := os.Getenv("HA_TOKEN")
var entityID string
switch req.Device {
switch req.Device {
case "ac":
if req.Action == "on" {
switch req.Action {
case "on":
entityID = "scene.ac_d101_on"
} else {
default:
entityID = "scene.ac_d101_off"
}
case "projector":
if req.Action == "on" {
switch req.Action {
case "on":
entityID = "scene.projector_d101_on"
} else {
case "off":
entityID = "scene.projector_d101_off"
}
}

View File

@ -38,7 +38,6 @@ export default function PowerMonitoringPage() {
const backendData = result.data;
// Kita terjemahkan data Golang ("on"/"off") menjadi boolean (true/false)
// dan masukkan khusus untuk Kelas D101 (id: 1)
setRoomDeviceStatus(prev => ({
...prev,
"room_1": {
@ -59,7 +58,7 @@ export default function PowerMonitoringPage() {
// 3. INISIALISASI & POLLING (AUTO-REFRESH)
// =========================================================================
useEffect(() => {
// A. Simulasi Data Ruangan dari DB/IoT
// Simulasi Data Ruangan
const dummyRooms = [
{ id: 1, name: "Kelas D101", power: 0, isRelayOn: true, lastUpdate: "Real-time" },
{ id: 2, name: "Kelas D102", power: 0, isRelayOn: false, lastUpdate: "2 mnt lalu" },
@ -68,7 +67,7 @@ export default function PowerMonitoringPage() {
];
setRooms(dummyRooms);
// B. Inisialisasi status default semua device OFF
// Inisialisasi status default semua device OFF
const initialStatus: { [roomId: string]: { [deviceName: string]: boolean } } = {};
dummyRooms.forEach(room => {
initialStatus[`room_${room.id}`] = {
@ -80,17 +79,15 @@ export default function PowerMonitoringPage() {
});
setRoomDeviceStatus(initialStatus);
// C. Tarik data pertama kali saat halaman dibuka
// Tarik data pertama kali & set Interval 2 detik
fetchPowerStatus();
fetchDeviceStatus();
// D. Jalankan Polling setiap 2 detik (2000 ms)
const interval = setInterval(() => {
fetchPowerStatus();
fetchDeviceStatus();
}, 2000);
// E. Bersihkan interval jika admin pindah halaman
return () => clearInterval(interval);
}, []);
@ -100,18 +97,24 @@ export default function PowerMonitoringPage() {
const handleDeviceToggle = async (roomId: number, roomName: string, deviceName: string) => {
const roomIdKey = `room_${roomId}`;
const currentStatus = roomDeviceStatus[roomIdKey]?.[deviceName] || false;
// Logika Pintar: Jika sedang ON, maka klik selanjutnya adalah OFF. Sebaliknya.
const actionType = currentStatus ? "off" : "on";
const confirmMsg = `Apakah Anda yakin ingin mematikan ${deviceName} di ${roomName}?`;
if (currentStatus && !confirm(confirmMsg)) return;
// Konfirmasi mematikan (Biar admin gak salah pencet)
if (currentStatus) {
const confirmMsg = `Apakah Anda yakin ingin mematikan ${deviceName} di ${roomName}?`;
if (!window.confirm(confirmMsg)) return; // Jika di-cancel, berhenti di sini
}
// Mapping nama device untuk backend
let backendDevice = "";
if (deviceName === 'AC 1') backendDevice = "ac";
else if (deviceName === 'Proyektor') backendDevice = "projector";
else if (deviceName === 'Lampu 1') backendDevice = "lampu1";
else if (deviceName === 'Lampu 2') backendDevice = "lampu2";
// Update UI seketika agar terasa responsif bagi admin yang menekan tombol
// 1. UBAH UI SEKETIKA (Optimistic UI) agar layar merespons tanpa delay
setRoomDeviceStatus(prev => ({
...prev,
[roomIdKey]: {
@ -120,6 +123,7 @@ export default function PowerMonitoringPage() {
},
}));
// 2. KIRIM KE GOLANG
try {
const response = await fetch("http://172.17.172.17:8080/api/hardware/control", {
method: "POST",
@ -127,8 +131,8 @@ export default function PowerMonitoringPage() {
body: JSON.stringify({ device: backendDevice, action: actionType }),
});
// 3. JIKA GAGAL, KEMBALIKAN UI KE POSISI SEMULA
if (!response.ok) {
// Jika gagal, kembalikan posisi tombol ke keadaan semula
setRoomDeviceStatus(prev => ({
...prev,
[roomIdKey]: {
@ -137,11 +141,11 @@ export default function PowerMonitoringPage() {
},
}));
const errorData = await response.json();
alert(`GAGAL: ${errorData.error || response.statusText}`);
alert(`GAGAL: ${errorData.error || "Server menolak perintah"}`);
}
} catch (error) {
console.error("Error API:", error);
// Jika server mati, kembalikan posisi tombol
// Rollback UI jika koneksi mati
setRoomDeviceStatus(prev => ({
...prev,
[roomIdKey]: {
@ -154,7 +158,7 @@ export default function PowerMonitoringPage() {
};
const handleCutOff = (roomName: string) => {
if (confirm(`PERINGATAN: Anda yakin ingin mematikan daya secara paksa di ${roomName}?`)) {
if (window.confirm(`PERINGATAN: Anda yakin ingin mematikan daya secara paksa di ${roomName}?`)) {
alert(`Sinyal pemutusan daya dikirim ke Relay Master ${roomName}.`);
}
};
@ -164,7 +168,6 @@ export default function PowerMonitoringPage() {
// =========================================================================
return (
<div className="space-y-6">
{/* Header section */}
<div className="flex items-center gap-3 mb-6">
<div className="bg-blue-100 p-3 rounded-lg text-blue-600">
<Activity size={28} />
@ -184,7 +187,6 @@ export default function PowerMonitoringPage() {
return (
<div key={room.id} className="bg-white p-6 rounded-xl border border-gray-100 shadow-sm relative overflow-hidden flex flex-col">
{/* Status Badge */}
<div className={`absolute top-0 right-0 px-4 py-1.5 text-[10px] font-black uppercase tracking-wider rounded-bl-xl
${room.isRelayOn ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}`}>
{room.isRelayOn ? 'Sirkuit Aktif' : 'Sirkuit Terputus'}
@ -192,7 +194,6 @@ export default function PowerMonitoringPage() {
<h3 className="text-lg font-bold text-gray-800 mb-4">{room.name}</h3>
{/* Power display */}
<div className="flex items-end gap-2 mb-6">
<span className={`text-5xl font-black tracking-tight ${room.power > 1000 ? 'text-orange-500' : 'text-gray-800'}`}>
{room.power}
@ -206,7 +207,6 @@ export default function PowerMonitoringPage() {
</div>
)}
{/* PANEL KONTROL IoT */}
<div className="mt-2 mb-6 pt-4 border-t border-gray-100">
<p className="text-[10px] font-bold text-gray-400 uppercase tracking-wider mb-3">IoT Device Control</p>
<div className="grid grid-cols-2 gap-2">
@ -262,7 +262,6 @@ export default function PowerMonitoringPage() {
</div>
</div>
{/* Footer */}
<div className="flex items-center justify-between mt-auto pt-4 border-t border-gray-100">
<span className="text-xs text-gray-400 font-medium">Update: {room.lastUpdate}</span>
<button