HAOS Add-on v1.1.0: Multi-Wechselrichter Support
- Unbegrenzt viele Wechselrichter über Web UI verwaltbar (Add/Edit/Delete) - Pro Wechselrichter: eigener Poll-Thread, MQTT-Topic-Präfix, HA Device - Shared MQTT-Publisher: eine Verbindung für alle Wechselrichter - Migration: bestehende Single-Inverter-Config wird automatisch übernommen - Live-Daten: pro Wechselrichter mit Online/Offline-Badge - config.yaml: nur noch MQTT global, Wechselrichter über /data/config.json Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
from typing import Dict, Optional
|
||||
from typing import List, Tuple
|
||||
|
||||
import paho.mqtt.client as mqtt
|
||||
|
||||
@@ -9,28 +9,26 @@ from inverters import Inverter
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
DEVICE_ID = "growatt_shinelanx"
|
||||
|
||||
|
||||
class MqttPublisher:
|
||||
def __init__(self, broker: str, port: int, user: str, password: str, topic_prefix: str):
|
||||
self.topic_prefix = topic_prefix.rstrip("/")
|
||||
self._client = mqtt.Client(client_id=DEVICE_ID, clean_session=True)
|
||||
def __init__(self, broker: str, port: int, user: str, password: str):
|
||||
self._broker = broker
|
||||
self._port = port
|
||||
self._connected = False
|
||||
self._registered: List[Tuple[Inverter, str, str]] = [] # (inverter, device_id, topic_prefix)
|
||||
|
||||
self._client = mqtt.Client(client_id="growatt_shinelanx_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._broker = broker
|
||||
self._port = port
|
||||
self._connected = False
|
||||
self._inverter: Optional[Inverter] = None
|
||||
|
||||
def _on_connect(self, client, userdata, flags, rc):
|
||||
if rc == 0:
|
||||
self._connected = True
|
||||
log.info("MQTT verbunden: %s:%d", self._broker, self._port)
|
||||
if self._inverter:
|
||||
self._publish_discovery(self._inverter)
|
||||
for inv, dev_id, prefix in self._registered:
|
||||
self._publish_discovery(inv, dev_id, prefix)
|
||||
else:
|
||||
log.error("MQTT Verbindungsfehler rc=%d", rc)
|
||||
|
||||
@@ -53,23 +51,28 @@ class MqttPublisher:
|
||||
def connected(self) -> bool:
|
||||
return self._connected
|
||||
|
||||
def setup_inverter(self, inverter: Inverter):
|
||||
self._inverter = inverter
|
||||
def register_inverter(self, inverter: Inverter, device_id: str, topic_prefix: str):
|
||||
entry = (inverter, device_id, topic_prefix)
|
||||
self._registered = [r for r in self._registered if r[1] != device_id]
|
||||
self._registered.append(entry)
|
||||
if self._connected:
|
||||
self._publish_discovery(inverter)
|
||||
self._publish_discovery(inverter, device_id, topic_prefix)
|
||||
|
||||
def _publish_discovery(self, inverter: Inverter):
|
||||
def unregister_inverter(self, device_id: str):
|
||||
self._registered = [r for r in self._registered if r[1] != device_id]
|
||||
|
||||
def _publish_discovery(self, inverter: Inverter, device_id: str, topic_prefix: str):
|
||||
device_payload = {
|
||||
"identifiers": [DEVICE_ID],
|
||||
"name": "Growatt ShineLAN-X",
|
||||
"identifiers": [device_id],
|
||||
"name": f"Growatt {inverter.name}",
|
||||
"manufacturer": inverter.manufacturer,
|
||||
"model": inverter.name,
|
||||
}
|
||||
for sensor in inverter.sensors:
|
||||
config = {
|
||||
"name": sensor.name,
|
||||
"unique_id": f"{DEVICE_ID}_{sensor.id}",
|
||||
"state_topic": f"{self.topic_prefix}/state",
|
||||
"unique_id": f"{device_id}_{sensor.id}",
|
||||
"state_topic": f"{topic_prefix}/state",
|
||||
"value_template": f"{{{{ value_json.{sensor.id} }}}}",
|
||||
"unit_of_measurement": sensor.unit,
|
||||
"state_class": sensor.state_class,
|
||||
@@ -78,17 +81,14 @@ class MqttPublisher:
|
||||
}
|
||||
if sensor.device_class:
|
||||
config["device_class"] = sensor.device_class
|
||||
|
||||
topic = f"homeassistant/sensor/{DEVICE_ID}/{sensor.id}/config"
|
||||
topic = f"homeassistant/sensor/{device_id}/{sensor.id}/config"
|
||||
self._client.publish(topic, json.dumps(config), retain=True, qos=1)
|
||||
log.info("MQTT Discovery für %d Sensoren veröffentlicht", len(inverter.sensors))
|
||||
log.info("MQTT Discovery: %d Sensoren für %s", len(inverter.sensors), device_id)
|
||||
|
||||
def publish_data(self, values: Dict[str, float]):
|
||||
def publish_data(self, values: dict, topic_prefix: str):
|
||||
if not self._connected:
|
||||
log.warning("MQTT nicht verbunden, Daten verworfen")
|
||||
return
|
||||
payload = json.dumps(values)
|
||||
self._client.publish(f"{self.topic_prefix}/state", payload, retain=True, qos=0)
|
||||
self._client.publish(f"{topic_prefix}/state", json.dumps(values), retain=True, qos=0)
|
||||
|
||||
def publish_status(self, status: str):
|
||||
self._client.publish(f"{self.topic_prefix}/status", status, retain=True, qos=1)
|
||||
def publish_status(self, status: str, topic_prefix: str):
|
||||
self._client.publish(f"{topic_prefix}/status", status, retain=True, qos=1)
|
||||
|
||||
Reference in New Issue
Block a user