#!/usr/bin/env python3 import cv2 import numpy as np import paho.mqtt.client as mqtt from paho.mqtt import client as mqtt_client import time import base64 import threading import json import subprocess import os from datetime import datetime # ====================== KONFIGURASI ====================== MQTT_BROKER = "localhost" MQTT_PORT = 1883 PRESENCE_TOPIC = "classroom/presence" CONTROL_TOPIC = "classroom/ac/control" WEBCAM_TOPIC = "classroom/webcam" STATUS_TOPIC = "classroom/ac/status" WEIGHTS = "yolov4-tiny.weights" CFG = "yolov4-tiny.cfg" NAMES = "coco.names" CONFIDENCE_THRESHOLD = 0.5 INPUT_SIZE = (256, 256) WEBCAM_INTERVAL = 2 WEBCAM_QUALITY = 60 WEBCAM_WIDTH = 640 WEBCAM_HEIGHT = 480 # ======================================================== print("=" * 60) print("šŸš€ AC CONTROL SYSTEM - DENGAN WEBCAM MONITORING") print("=" * 60) # ====================== CLEANUP WEBCAM ====================== print("Membersihkan proses yang menggunakan webcam...") try: # Kill semua proses yang menggunakan webcam subprocess.run(["sudo", "fuser", "-k", "/dev/video0"], capture_output=True) time.sleep(1) except: pass # ====================== INISIALISASI WEBCAM ====================== print("Membuka webcam...") cap = None # Coba buka webcam dengan berbagai metode for i in range(3): # Coba 3 kali cap = cv2.VideoCapture(0, cv2.CAP_V4L2) if cap.isOpened(): print(f"āœ… Webcam terbuka pada percobaan ke-{i+1}") break time.sleep(1) if cap is None or not cap.isOpened(): print("āŒ Gagal membuka webcam!") print("\nTroubleshooting:") print("1. Cek koneksi USB webcam") print("2. Jalankan: sudo fuser -k /dev/video0") print("3. Restart: sudo reboot") exit(1) # Set properti webcam cap.set(cv2.CAP_PROP_FRAME_WIDTH, WEBCAM_WIDTH) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, WEBCAM_HEIGHT) cap.set(cv2.CAP_PROP_FPS, 30) # Test baca frame ret, test_frame = cap.read() if not ret: print("āŒ Webcam terbuka tapi gagal membaca frame!") cap.release() exit(1) print(f"āœ… Webcam siap: {int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))}x{int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))}") # ====================== LOAD MODEL YOLO ====================== print("Memuat model YOLOv4-tiny...") for file in [WEIGHTS, CFG, NAMES]: if not os.path.exists(file): print(f"āŒ File {file} tidak ditemukan!") cap.release() exit(1) net = cv2.dnn.readNet(WEIGHTS, CFG) net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV) net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU) with open(NAMES, "r") as f: classes = [line.strip() for line in f.readlines()] layer_names = net.getLayerNames() output_layers = [layer_names[i - 1] for i in net.getUnconnectedOutLayers()] print("āœ… Model YOLO berhasil dimuat") # ====================== MQTT SETUP ====================== def on_connect(client, userdata, flags, reason_code, properties): if reason_code == 0: print("āœ… MQTT terhubung ke broker!") client.subscribe("classroom/webcam/request") else: print(f"āŒ MQTT gagal connect: {reason_code}") def on_message(client, userdata, msg): if msg.topic == "classroom/webcam/request": print("šŸ“ø Request frame webcam diterima") send_webcam_frame() client = mqtt.Client(mqtt_client.CallbackAPIVersion.VERSION2) client.on_connect = on_connect client.on_message = on_message try: client.connect(MQTT_BROKER, MQTT_PORT, 60) client.loop_start() print("āœ… MQTT client siap") except Exception as e: print(f"āŒ Gagal konek MQTT: {e}") cap.release() exit(1) # ====================== FUNGSI CAPTURE ====================== def get_frame(): """Ambil frame dari webcam""" if cap is None or not cap.isOpened(): return False, None return cap.read() def send_webcam_frame(): """Mengirim frame webcam via MQTT""" try: ret, frame = get_frame() if ret and frame is not None: frame = cv2.resize(frame, (WEBCAM_WIDTH, WEBCAM_HEIGHT)) _, buffer = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, WEBCAM_QUALITY]) frame_base64 = base64.b64encode(buffer).decode('utf-8') client.publish(WEBCAM_TOPIC, frame_base64) print(f"šŸ“ø Frame terkirim ({len(frame_base64) // 1024} KB)") return True return False except Exception as e: print(f"āŒ Error: {e}") return False def send_webcam_periodic(): while True: send_webcam_frame() time.sleep(WEBCAM_INTERVAL) # ====================== DETEKSI KEHADIRAN ====================== def detect_person(frame): """Deteksi orang dalam frame""" blob = cv2.dnn.blobFromImage(frame, 0.00392, INPUT_SIZE, (0, 0, 0), True, crop=False) net.setInput(blob) outs = net.forward(output_layers) for out in outs: for detection in out: scores = detection[5:] class_id = np.argmax(scores) confidence = scores[class_id] if confidence > CONFIDENCE_THRESHOLD and classes[class_id] == "person": return True return False # ====================== MAIN LOOP ====================== def main(): # Start thread webcam webcam_thread = threading.Thread(target=send_webcam_periodic, daemon=True) webcam_thread.start() print("āœ… Thread webcam dimulai") last_presence = None frame_count = 0 detection_skip = 2 print("\n" + "=" * 60) print("šŸš€ Sistem deteksi kehadiran berjalan...") print(f" - Webcam streaming: setiap {WEBCAM_INTERVAL} detik") print(f" - Deteksi kehadiran: setiap {detection_skip} frame") print(" - Tekan Ctrl+C untuk berhenti") print("=" * 60 + "\n") try: while True: ret, frame = get_frame() if not ret or frame is None: print("āš ļø Gagal membaca frame, mencoba reset...") time.sleep(1) continue frame_count += 1 if frame_count % detection_skip == 0: person_detected = detect_person(frame) current_presence = "ada" if person_detected else "tidak ada" client.publish(PRESENCE_TOPIC, current_presence) if current_presence != last_presence: command = "on" if person_detected else "off" client.publish(CONTROL_TOPIC, command) status_msg = { "ac_state": command, "temperature": 24, "mode": "cool", "presence": current_presence, "timestamp": datetime.now().isoformat() } client.publish(STATUS_TOPIC, json.dumps(status_msg)) print(f"šŸ‘„ {current_presence} → AC {command.upper()}") last_presence = current_presence status_indicator = "šŸ‘„ ADA" if person_detected else "🚪 KOSONG" print(f"[{datetime.now().strftime('%H:%M:%S')}] {status_indicator} | Frame: {frame_count}") time.sleep(0.05) except KeyboardInterrupt: print("\n\nšŸ›‘ Berhenti...") finally: if cap: cap.release() client.disconnect() print("āœ… Selesai") if __name__ == "__main__": main()