diff --git a/haos-addon/src/main.py b/haos-addon/src/main.py index d06273e..a11ebad 100644 --- a/haos-addon/src/main.py +++ b/haos-addon/src/main.py @@ -76,6 +76,7 @@ class State: inv_data: Dict[str, Dict[str, Any]] = {} surplus_devices_cfg: List[Dict[str, Any]] = [] z2m_base: str = "zigbee2mqtt" + z2m_devices: List[Dict[str, Any]] = [] _publisher: Optional[MqttPublisher] = None _surplus_ctrl: Optional[SurplusDeviceController] = None @@ -370,6 +371,28 @@ def _restart_all(): _surplus_ctrl.set_config(devices, z2m_base) _start_surplus_loop() + def _on_z2m_devices(topic, payload): + try: + devices_raw = json.loads(payload) + if not isinstance(devices_raw, list): + return + parsed = [ + { + "friendly_name": d.get("friendly_name", ""), + "description": (d.get("definition") or {}).get("description", ""), + "type": d.get("type", ""), + } + for d in devices_raw + if d.get("friendly_name") and d.get("friendly_name") != "Coordinator" + ] + with State.lock: + State.z2m_devices = parsed + log.info("Z2M: %d Geräte empfangen", len(parsed)) + except Exception as e: + log.warning("Z2M bridge/devices Fehler: %s", e) + + _publisher.subscribe(f"{z2m_base}/bridge/devices", _on_z2m_devices) + for inv_cfg in State.inverters_cfg: _start_inverter(inv_cfg) @@ -488,6 +511,11 @@ def api_period_energy(): return jsonify(result) +@app.get("/api/z2m-devices") +def api_z2m_devices(): + with State.lock: + return jsonify(State.z2m_devices) + @app.get("/api/surplus-devices") def api_get_surplus_devices(): with State.lock: diff --git a/haos-addon/src/mqtt_publisher.py b/haos-addon/src/mqtt_publisher.py index 2d49b01..06ec735 100644 --- a/haos-addon/src/mqtt_publisher.py +++ b/haos-addon/src/mqtt_publisher.py @@ -21,11 +21,13 @@ class MqttPublisher: self._registered: List[Tuple] = [] self._agg_meta: Dict = agg_meta or {} + self._subscriptions: List[Tuple[str, any]] = [] self._client = mqtt.Client(client_id="shinebridge_hub", clean_session=True) if user: self._client.username_pw_set(user, password) self._client.on_connect = self._on_connect self._client.on_disconnect = self._on_disconnect + self._client.on_message = self._on_message def _on_connect(self, client, userdata, flags, rc): if rc == 0: @@ -35,6 +37,8 @@ class MqttPublisher: self._publish_discovery(*entry) if self._agg_meta: self._publish_aggregate_discovery() + for topic, _ in self._subscriptions: + client.subscribe(topic) else: log.error("MQTT Verbindungsfehler rc=%d", rc) @@ -42,6 +46,19 @@ class MqttPublisher: self._connected = False log.warning("MQTT getrennt rc=%d", rc) + def _on_message(self, client, userdata, msg): + for topic, callback in self._subscriptions: + if mqtt.topic_matches_sub(topic, msg.topic): + try: + callback(msg.topic, msg.payload) + except Exception as e: + log.error("MQTT message handler Fehler [%s]: %s", msg.topic, e) + + def subscribe(self, topic: str, callback): + self._subscriptions.append((topic, callback)) + if self._connected: + self._client.subscribe(topic) + def connect(self): try: self._client.connect_async(self._broker, self._port, keepalive=60) diff --git a/haos-addon/src/web/index.html b/haos-addon/src/web/index.html index 8b91dc9..957dac7 100644 --- a/haos-addon/src/web/index.html +++ b/haos-addon/src/web/index.html @@ -335,6 +335,7 @@ +