diff --git a/backend/controllers/hardwarecontroller.go b/backend/controllers/hardwarecontroller.go index 8d7b53d..b8c79ff 100644 --- a/backend/controllers/hardwarecontroller.go +++ b/backend/controllers/hardwarecontroller.go @@ -35,7 +35,7 @@ var DeviceStatusCache = map[string]string{ } // ========================================================================= -// FUNGSI 1: VERIFIKASI TOKEN (Dan Buka Gembok Relay) +// FUNGSI 1: VERIFIKASI TOKEN (Bisa Master & Mahasiswa) // ========================================================================= func VerifyHardwareCode(c *gin.Context) { var req VerifyRequest @@ -47,103 +47,87 @@ func VerifyHardwareCode(c *gin.Context) { tokenInput := req.Token sekarang := time.Now() - - // 1. Token Master CS (Cleaning Service) - if tokenInput == "CS2026" { - fmt.Printf("[VERIFY] Master Token CS digunakan\n") - c.JSON(http.StatusOK, gin.H{ - "status": "success", "message": "Token CS Valid", "duration_minutes": 60, - }) - return - } - - // 2. Token Master Admin - if tokenInput == "ADM999" { - fmt.Printf("[VERIFY] Master Token Admin digunakan\n") - c.JSON(http.StatusOK, gin.H{ - "status": "success", "message": "Token Admin Valid", "duration_minutes": 999, - }) - return - } - - // ------------------------------------------------------------- - // TARIK DATA START_TIME & END_TIME MENGGUNAKAN time.Time - // ------------------------------------------------------------- - var jamMulai time.Time - var jamSelesai time.Time + sisaMenit := 0 isTokenValid := false - type ResultTime struct { - JamMulai time.Time `gorm:"column:start_time"` - JamSelesai time.Time `gorm:"column:end_time"` - } - var result ResultTime - - errBooking := config.DB.Table("bookings"). - Select("start_time, end_time"). - Where("redeem_code = ?", tokenInput). - Scan(&result).Error - - if errBooking == nil && !result.JamSelesai.IsZero() { - jamMulai = result.JamMulai - jamSelesai = result.JamSelesai + // ========================================================= + // 1. PENGECEKAN KELOMPOK TOKEN MASTER (Tanpa Cek Waktu) + // ========================================================= + if tokenInput == "CS2026" { + fmt.Printf("[VERIFY] Master Token CS digunakan\n") + sisaMenit = 60 + isTokenValid = true + } else if tokenInput == "ADM999" { + fmt.Printf("[VERIFY] Master Token Admin digunakan\n") + sisaMenit = 999 isTokenValid = true } else { - errSchedule := config.DB.Table("class_schedules"). + // ========================================================= + // 2. PENGECEKAN KELOMPOK TOKEN MAHASISWA (Cek Database & Waktu) + // ========================================================= + var jamMulai time.Time + var jamSelesai time.Time + type ResultTime struct { + JamMulai time.Time `gorm:"column:start_time"` + JamSelesai time.Time `gorm:"column:end_time"` + } + var result ResultTime + + // Cek Bookings + errBooking := config.DB.Table("bookings"). Select("start_time, end_time"). - Where("kode_mk = ?", tokenInput). + Where("redeem_code = ?", tokenInput). Scan(&result).Error - if errSchedule == nil && !result.JamSelesai.IsZero() { + if errBooking == nil && !result.JamSelesai.IsZero() { jamMulai = result.JamMulai jamSelesai = result.JamSelesai isTokenValid = true + } else { + // Cek Jadwal Kelas Tetap + errSchedule := config.DB.Table("class_schedules"). + Select("start_time, end_time"). + Where("kode_mk = ?", tokenInput). + Scan(&result).Error + + if errSchedule == nil && !result.JamSelesai.IsZero() { + jamMulai = result.JamMulai + jamSelesai = result.JamSelesai + isTokenValid = true + } + } + + if !isTokenValid { + c.JSON(http.StatusUnauthorized, gin.H{"error": "Token salah atau tidak ditemukan"}) + return + } + + jamMulaiHariIni := time.Date(sekarang.Year(), sekarang.Month(), sekarang.Day(), + jamMulai.Hour(), jamMulai.Minute(), jamMulai.Second(), 0, sekarang.Location()) + jamSelesaiHariIni := time.Date(sekarang.Year(), sekarang.Month(), sekarang.Day(), + jamSelesai.Hour(), jamSelesai.Minute(), jamSelesai.Second(), 0, sekarang.Location()) + + batasMasukAwal := jamMulaiHariIni.Add(-15 * time.Minute) + if sekarang.Before(batasMasukAwal) { + c.JSON(http.StatusForbidden, gin.H{"error": "Jadwal kelas belum dimulai"}) + return + } + + selisihWaktu := jamSelesaiHariIni.Sub(sekarang) + sisaMenit = int(selisihWaktu.Minutes()) + + if sisaMenit <= 0 { + c.JSON(http.StatusForbidden, gin.H{"error": "Waktu peminjaman sudah habis"}) + return } } - if !isTokenValid { - c.JSON(http.StatusUnauthorized, gin.H{"error": "Token salah atau tidak ditemukan"}) - return - } - - // Ekstrak Jam, Menit, Detik langsung dari objek Time (Sangat Aman & Akurat) - jamMulaiHariIni := time.Date(sekarang.Year(), sekarang.Month(), sekarang.Day(), - jamMulai.Hour(), jamMulai.Minute(), jamMulai.Second(), 0, sekarang.Location()) - - jamSelesaiHariIni := time.Date(sekarang.Year(), sekarang.Month(), sekarang.Day(), - jamSelesai.Hour(), jamSelesai.Minute(), jamSelesai.Second(), 0, sekarang.Location()) - - // ------------------------------------------------------------- - // LOGIKA PENOLAKAN TOKEN MASA DEPAN - // ------------------------------------------------------------- - // Toleransi masuk: Hanya bisa masuk 15 menit sebelum jadwal. - batasMasukAwal := jamMulaiHariIni.Add(-15 * time.Minute) - - if sekarang.Before(batasMasukAwal) { - fmt.Printf("[VERIFY] Ditolak: Kelas %s belum mulai\n", tokenInput) - c.JSON(http.StatusForbidden, gin.H{"error": "Jadwal kelas belum dimulai"}) - return - } - - // ------------------------------------------------------------- - // LOGIKA PENOLAKAN TOKEN KADALUARSA - // ------------------------------------------------------------- - selisihWaktu := jamSelesaiHariIni.Sub(sekarang) - sisaMenit := int(selisihWaktu.Minutes()) - - if sisaMenit <= 0 { - fmt.Printf("[VERIFY] Ditolak: Waktu kelas %s sudah habis\n", tokenInput) - c.JSON(http.StatusForbidden, gin.H{"error": "Waktu peminjaman sudah habis"}) - return - } - - // ------------------------------------------------------------- - // JIKA LOLOS SEMUA UJIAN: IZINKAN MASUK & BUKA GEMBOK - // ------------------------------------------------------------- + // ========================================================= + // 3. JIKA VALID (Master / Mahasiswa), BUKA GEMBOK VIA MQTT + // ========================================================= DeviceStatusCache["lampu1"] = "on" DeviceStatusCache["lampu2"] = "on" - // --- START BLOK MQTT UNLOCK --- broker := os.Getenv("MQTT_BROKER") user := os.Getenv("MQTT_USER") pass := os.Getenv("MQTT_PASSWORD") @@ -156,19 +140,18 @@ func VerifyHardwareCode(c *gin.Context) { client := mqtt.NewClient(opts) if tokenMQTT := client.Connect(); tokenMQTT.Wait() && tokenMQTT.Error() == nil { - // 1. Mengirim sinyal otorisasi "UNLOCK" ke Relay dengan retain=true + // Buka Gembok Relay client.Publish("sclass/d101/auth", 0, true, "UNLOCK").Wait() - - // 2. Opsional: otomatis nyalakan lampu saat token dimasukkan + + // Opsional: Nyalakan Lampu Otomatis saat login client.Publish("sclass/d101/lampu1", 0, false, "on").Wait() client.Publish("sclass/d101/lampu2", 0, false, "on").Wait() - + client.Disconnect(250) - fmt.Printf("[MQTT] Perintah UNLOCK dan ON berhasil dikirim ke Relay D101\n") + fmt.Printf("[MQTT] Perintah UNLOCK dan ON dikirim (Sisa Menit: %d)\n", sisaMenit) } else { fmt.Printf("[MQTT ERROR] Gagal menghubungi broker: %v\n", tokenMQTT.Error()) } - // --- END BLOK MQTT UNLOCK --- c.JSON(http.StatusOK, gin.H{ "status": "success", @@ -177,9 +160,6 @@ func VerifyHardwareCode(c *gin.Context) { }) } -// ========================================================================= -// FUNGSI 2: KONTROL DEVICE VIA HOME ASSISTANT & MQTT -// ========================================================================= // ========================================================================= // FUNGSI 2: KONTROL DEVICE VIA HOME ASSISTANT & MQTT // ========================================================================= @@ -246,9 +226,17 @@ func ControlHardware(c *gin.Context) { switch req.Device { case "ac": - if req.Action == "on" { entityID = "scene.ac_d101_on" } else { entityID = "scene.ac_d101_off" } + if req.Action == "on" { + entityID = "scene.ac_d101_on" + } else { + entityID = "scene.ac_d101_off" + } case "projector": - if req.Action == "on" { entityID = "scene.projector_d101_on" } else { entityID = "scene.projector_d101_off" } + if req.Action == "on" { + entityID = "scene.projector_d101_on" + } else { + entityID = "scene.projector_d101_off" + } } apiURL := fmt.Sprintf("%s/api/services/scene/turn_on", haURL) @@ -261,7 +249,9 @@ func ControlHardware(c *gin.Context) { reqHA.Header.Set("Content-Type", "application/json") httpClient := &http.Client{Timeout: 5 * time.Second} resp, _ := httpClient.Do(reqHA) - if resp != nil { resp.Body.Close() } + if resp != nil { + resp.Body.Close() + } } DeviceStatusCache[req.Device] = req.Action @@ -314,4 +304,4 @@ func GetHardwareStatus(c *gin.Context) { "status": "success", "data": DeviceStatusCache, }) -} \ No newline at end of file +}