#!/usr/bin/env python3 """ SolarManager Module Die wenigen Messwerte aus solarManager/#, die eine Automatik braucht KEINE Datenbank-Operationen! """ import logging from typing import List, Dict, Tuple from modules.base_module import BaseModule logger = logging.getLogger(__name__) # Wurzel aller Topics, die solarManager.py über mqttClient.publish() abgibt. # Dasselbe Wort steht dort als Vorgabe im Quelltext; bewusst kein # Konfigurationseintrag, er müsste in beiden Repositorien gleich lauten. WURZEL = "solarManager" # Aus dieser Nachricht wird hier bewusst nur ein kleiner Teil zu Messwerten. # Sie trägt gut 130 Topics: jeden Wechselrichter einzeln, jede Strangleistung, # jeden Phasenstrom, jede Vorlauf- und Rücklauftemperatur. Alles anzulegen # hieße, die Auswahl im Automatik-Editor unbrauchbar zu machen - man sucht # dann in einer Liste, statt eine Regel zu schreiben. Aufgenommen ist, was # das Realtime-SVG auf der Solarseite anzeigt, und von der PV nur die Summe. # # Nicht dabei sind die beiden Shelly EM3 (Erd- und Untergeschoss). Die melden # sich per Home-Assistant-Discovery und stehen über das MQTT-Modul längst als # eigene Geräte in der Datenbank; ein zweiter Satz derselben Zahlen machte die # Auswahl nur unübersichtlich. Zu beachten: solarManager/eg wäre ohnehin nicht # dasselbe - dort ist die Wallbox im Carport mit eingerechnet. # # Die beiden Modbus-Zähler am Gen24 gibt es dagegen *nur* hier. Sie hängen # unter 192.168.179.155 an derselben Adresse wie der Wechselrichter und # sprechen sonst mit niemandem (siehe gatherModbusData.py im SolarManager, # meter0 und meter1). # # Jedes dieser Topics trägt eine blanke Zahl, kein JSON - deshalb hat keiner # der Messwerte einen value_path. Wer später einen vermisst, trägt hier eine # Zeile ein; die vollständige Liste steht im Browser unter mqttData auf der # Solarseite. # # Die URL eines Geräts ist das Topic seines ersten Messwerts. Ein eigener # Namensraum wäre erfunden: der SolarManager kennt keine Geräte, nur Zahlen. # Sie muss nur eindeutig sein und mit mqtt:// beginnen, damit der Runner den # MQTT-Transport wählt; abonniert wird über die Topics der Messwerte. GERAETE = [ { # meter0 am Gen24, der Zähler am Hausanschluss. # # Bezug und Einspeisung statt P_Grid: P_Grid ist eine Zahl mit # Vorzeichen (negativ heißt Einspeisung), und eine Bedingung darauf # liest sich falsch herum. Diese beiden sind immer positiv, jeweils # eine von beiden ist null. "type": "Stromzähler", "name": "Netzanschluss", "messwerte": [ ("Bezug", "P_GridCons", "W"), ("Einspeisung", "P_GridFeed", "W"), ], }, { # meter1 am Gen24, der Zähler für das Obergeschoss. # # ACHTUNG, Vorzeichen: der Zähler meldet Verbrauch negativ, und # solarManager.py rechnet die Wallbox im Obergeschoss heraus, ohne das # Vorzeichen zu drehen. Das Realtime-SVG zeigt deshalb -og an. Eine # Bedingung "das Obergeschoss verbraucht mehr als 500 W" heißt hier # also "Leistung < -500". "type": "Stromzähler", "name": "Stromzähler Obergeschoss", "messwerte": [("Leistung", "og", "W")], }, { "type": "Solaranlage", "name": "Solaranlage", "messwerte": [ # Summe aller Stränge. Die einzelnen stehen in P_PVn, einem # JSON-Feld - für eine Bedingung taugt die Summe besser, und # genau darum ging es bei der Auswahl. ("PV-Leistung", "P_PV", "W"), ("Batterie-Ladestand", "SOC", "%"), ], }, { "type": "Heizung", "name": "Pufferspeicher", "messwerte": [ ("Temperatur oben", "t_buffT", "°C"), ("Temperatur Mitte", "t_buffM", "°C"), ("Temperatur unten", "t_buffB", "°C"), # Was der Heizstab gerade in den Puffer schiebt. Nur zum Lesen: # gestellt wird er über ajax/heater.php, nicht über MQTT. ("Heizstab-Leistung", "pHeat", "W"), ], }, ] class SolarManagerModule(BaseModule): """ SolarManager Modul - Implementiert BaseModule Interface Wie das Gartenwasser- und das Logic-Modul sucht dieses nichts, sondern schreibt feste Geräte hin. Der SolarManager meldet sich nicht per Home-Assistant-Discovery; das MQTT-Modul findet ihn also nicht, und ohne dieses Modul kann eine Automatik weder auf den Netzbezug noch auf den Ladestand der Batterie oder die Temperatur im Puffer reagieren. Alles hier ist zum Lesen. Geschaltet wird am SolarManager nichts: die Wallbox und die Heizung regelt er selbst, und wo das Web-UI eingreift, tut es das über eigene Endpunkte (ajax/heater.php, ajax/carSteuerung.php) und nicht über diese Topics. """ def is_enabled(self) -> bool: """Prüft ob das SolarManager-Modul aktiviert ist""" return self.config.solarmanager_enable def discover(self) -> Tuple[List[Dict], List[Dict]]: """ Erzeugt die Geräte aus GERAETE - alle als Sensoren, ohne Kommandos. Returns: Tuple (actors, sensors) """ logger.info("\n" + "=" * 60) logger.info("SOLARMANAGER-MESSWERTE WERDEN ERZEUGT") logger.info("=" * 60) sensoren = [self._geraet(g) for g in GERAETE] anzahl = sum(len(g["messwerte"]) for g in GERAETE) logger.info("%d Geräte mit %d Messwerten", len(sensoren), anzahl) return [], sensoren @staticmethod def _geraet(geraet: Dict) -> Dict: """Ein Gerät mit seinen Messwerten.""" states = [{ "name": name, "url": WURZEL + "/" + topic, "type": "float", "unit": einheit, } for name, topic, einheit in geraet["messwerte"]] return { "type": geraet["type"], "name": geraet["name"], "url": "mqtt://" + states[0]["url"], "states": states, }