import asyncio import paho.mqtt.client as mqtt from bleak import BleakClient, BleakScanner import time import sys from collections import deque # Efficiƫnte wachtrij voor de cach MAX_CACHE_SIZE = 1000 # BLE CONFIGURATIE (Moet exact matchen met de ESP32) ble_pin = 341055 ESP32_NAME = "ESP32_Meterkast_BLE" SERVICE_UUID = "538b94e9-d5a3-4f16-877f-d208af9e9db9" CHARACTERISTIC_TX_UUID = "637ce3a9-9203-42a6-87f0-bdbecf7b5b5e" # ESP32 -> PC CHARACTERISTIC_RX_UUID = "56e9ab79-d1a8-497e-b08e-19922f7eeacf" # PC -> ESP32 MQTT_PORT = 1883 MQTT_BROKER = "192.168.200.26" MQTT_USER = "vlc_viewer" MQTT_PASSWORD = "vlc_viewer12" mqttClientId = "MeterKastController" # Topics configureren # De PC luistert naar dit topic en stuurt ALLES wat hier binnenkomt door naar de ESP32 via BT mqttTopicBelVoorTest = "BelVoorTest" mqttTopicRestarCamera = "RestartCamera" mqttTopicDisableVAlarm = "DisableVAlarm" mqttTopicEnableVAlarm = "EnableVAlarm" mqttTopicActivateFan = "ActivateFanMeterkast" mqttTopicDeactivateFan = "DeactivateFanMeterkast" mqttTopicActivateBuzzer = "ActivateAlarmBuzzer" mqttTopicDeactivateBuzzer = "DeactivateAlarmBuzzer" mqttTopicAlarmLampAan = "AlarmLampAan" mqttTopicAlarmLampUit = "AlarmLAmpUit" # mqttTopicDalVerbruikt = "DalVerbruikt_kwh" # mqttTopicPiekVerbruikt = "PiekVerbruikt_kwh" # mqttTopicDalGeleverd = "DalGeleverd_kwh" # mqttTopicPiekGeleverd = "PiekGeleverd_kwh" # mqttTopicHuidigVerbruik = "HuidigVerbruik_kw" # mqttTopicHuidigGeleverd = "HuidigGeleverd_kw" # mqttTopicGasTotaal = "gas_totaal_m3" # # mqttTopicTempMeterKast = "TempMeterKast" # mqttTopicTempThermostaat = "TempThermostaat" # mqttTopicTempBuiten = "TempBuiten" # mqttTopicTempKamer = "TempKamer" # mqttTopicTempOverloop = "TempOverloop" # mqttTopicTempMeterKastController = "TempMeterKastController" # # mqttTopicHcProtected = "HcProtected" # mqttTopicHcAlarm = "HcAlarm" # mqttTopicHcVAlarm = "HcVAlarm" # mqttTopicHcAlarmBuzzer = "HcAlarmBuzzer" # mqttTopicHcAlarmLamp = "HCLampAlarm" # mqttTopicFanMeterkast = "FanMeterKast" # mqttTopicAlarmStatus = "AlarmStatus" # mqttTopicP1Line = "P1Line" # mqttTopicP1InvalidLine = "P1InvalidLine" # mqttTopicSerialOutput = "SerialOutput" # # mqtt_temperature_topic = "BrievenbusTemperatuur" # mqtt_bel_topic = "BelVoor" # mqtt_bel_state_topic = "BelVoorStatus" # mqtt_test_topic = "BrievenbusTest" # mqtt_debug_topic = "BrievenbusDebug" # mqtt_box_state_topic = "BrievenbusStatus" # mqtt_box_opened_topic = "BrievenbusOpen" # mqtt_box_closed_topic = "BrievenbusDicht" # mqtt_info_topic = "BrievenbusInfo" # ====================================================== mqtt_client = None mqtt_connected = False message_cache = deque(maxlen=MAX_CACHE_SIZE) ble_client = None # --- CALLBACKS: MQTT STATUS EN ONTVANGST --- def on_connect(client, userdata, flags, rc): """Wordt aangeroepen als de verbinding met de MQTT broker slaagt of faalt.""" global mqtt_connected if rc == 0: print("[MQTT] Succesvol ingelogd en verbonden met de broker!") mqtt_connected = True client.subscribe(mqttTopicBelVoorTest) client.subscribe(mqttTopicRestarCamera) client.subscribe(mqttTopicDisableVAlarm) client.subscribe(mqttTopicEnableVAlarm) client.subscribe(mqttTopicActivateFan) client.subscribe(mqttTopicDeactivateFan) client.subscribe(mqttTopicActivateBuzzer) client.subscribe(mqttTopicDeactivateBuzzer) client.subscribe(mqttTopicAlarmLampAan) client.subscribe(mqttTopicAlarmLampUit) print(f"[MQTT] Geabonneerd op topics") flush_cache() elif rc == 4: print("[MQTT Fout] Verbinding geweigerd: Verkeerde gebruikersnaam of wachtwoord!") mqtt_connected = False else: print(f"[MQTT Fout] Verbinding geweigerd met code: {rc}") mqtt_connected = False def on_disconnect(client, userdata, rc): """Wordt aangeroepen als de verbinding met de MQTT broker wegvalt.""" global mqtt_connected mqtt_connected = False print("[MQTT] Verbinding verloren! Inkomende Bluetooth data wordt vanaf nu gecacht...") def on_message(client, userdata, msg): """Wordt geactiveerd als er een MQTT bericht binnenkomt voor de ESP32.""" global ser try: payload = msg.payload.decode('utf-8') print(f"[MQTT -> BT] Ontvangen op {msg.topic}: {payload}") if ble_client and ble_client.is_connected: # BLE vereist een asyncio-aanroep, paho-mqtt draait in een aparte thread. # We schrijven de data asynchroon weg via de loop. coro = ble_client.write_gatt_char(CHARACTERISTIC_RX_UUID, f"{msg.topic}\n".encode('utf-8')) asyncio.run_coroutine_threadsafe(coro, asyncio.get_event_loop()) except Exception as e: print(f"[Fout] Probleem bij doorsturen naar Bluetooth: {e}") # --- BLE NOTIFICATIE CALLBACK --- def ble_notification_handler(sender: int, data: bytearray): """Wordt getriggerd als de ESP32 data pushed (ESP32 -> PC)""" global mqtt_connected, mqtt_client try: line = data.decode('utf-8').strip() if "|" in line: topic, value = line.split("|", 1) if mqtt_connected: print(f"[BLE -> MQTT] Publiceren op {topic}: {value}") mqtt_client.publish(topic, value) else: message_cache.append((topic, value)) print(f"[Cache] MQTT offline. Opgeslagen: {topic} -> {value}") except Exception as e: print(f"[BLE Fout] Data verwerkingsfout: {e}") # --- CACHE LOGICA --- def flush_cache(): """Stuurt alle opgeslagen berichten in de cache alsnog naar MQTT.""" global mqtt_client, mqtt_connected if not message_cache: return print(f"[Cache] MQTT hersteld. {len(message_cache)} berichten in de wachtrij verzenden...") while message_cache and mqtt_connected: try: topic, value = message_cache.popleft() mqtt_client.publish(topic, value) time.sleep(0.02) except Exception as e: print(f"[Cache Fout] Kon gecacht bericht niet verzenden: {e}") break if not message_cache: print("[Cache] Alle gecachte berichten succesvol verzonden!") # ==================== HOOFDPROGRAMMA ==================== async def main(): global ser, mqtt_client, mqtt_connected # 1. MQTT Client initialiseren en beveiliging instellen print("[MQTT] Initialiseren...") mqtt_client = mqtt.Client() mqtt_client.on_connect = on_connect mqtt_client.on_disconnect = on_disconnect mqtt_client.on_message = on_message # Gebruikersnaam en wachtwoord koppelen aan de MQTT sessie if MQTT_USER and MQTT_PASSWORD: mqtt_client.username_pw_set(username=MQTT_USER, password=MQTT_PASSWORD) mqtt_client.loop_start() try: mqtt_client.connect(MQTT_BROKER, MQTT_PORT, 60) except Exception as e: print(f"[MQTT Waarschuwing] Eerste broker-verbinding mislukt ({e}). Controleer inloggegevens en broker-status.") print("[Systeem] Bridge opgestart. Starten van Bluetooth-verbinding...") # 2. BLE Verbindings- en herstelloop print("[Systeem] BLE Bridge actief. Zoeken naar ESP32...") while True: try: # Zoek het apparaat op basis van de naam device = await BleakScanner.find_device_by_name(ESP32_NAME) if not device: print(f"[BLE] Apparaat '{ESP32_NAME}' niet gevonden. Opnieuw scannen in 5s...") await asyncio.sleep(5) continue print(f"[BLE] Apparaat gevonden ({device.address}). Verbinden en authenticeren...") async with BleakClient(device.address) as client: ble_client = client print("[BLE] Verbonden! Activeren van data-notificaties...") # Start met luisteren naar de TX-characteristic van de ESP32 await client.start_notify(CHARACTERISTIC_TX_UUID, ble_notification_handler) # Blijf in deze loop zolang de BLE verbinding in stand blijft while client.is_connected: await asyncio.sleep(1) except Exception as e: print(f"[BLE Fout] Verbinding verbroken of geweigerd ({e}). Herstellen over 5s...") ble_client = None await asyncio.sleep(5) if __name__ == '__main__': try: asyncio.run(main()) except KeyboardInterrupt: print("\n[Systeem] Gestopt.")