make a mqtt ble bridge service for meterkast

This commit is contained in:
tthden 2026-05-15 10:23:00 +02:00
parent 62e0977c85
commit 6c2ad11d6a

221
mqtt_meterkast_bt_bridge.py Normal file
View File

@ -0,0 +1,221 @@
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.")