Wetterstation erkennen, Einheiten nicht mehr ueberschreiben

Die Station haengt laengst am Websocket und wsMQTTbridge.py legt ihre
Felder unter weatherStation/# ab - nur wusste die Automatikdatenbank
nichts davon. Das neue Modul macht daraus ein Geraet mit elf
Messwerten: Aussentemperatur, Taupunkt, Feuchte, Druck, Wind, Boe und
die beiden Richtungen. Wie das Gartenwasser- und das
SolarManager-Modul sucht es nichts, sondern schreibt ein festes Geraet
hin; die Station meldet sich nicht per Home-Assistant-Discovery.

Gebraucht wird sie fuer den Hitzeschutz: dafuer ist die
Aussentemperatur die bessere Bedingung als die Raumtemperatur - ist es
drinnen schon warm, ist es zum Verschatten zu spaet. Und Wind gab es
hier bisher ueberhaupt nicht; ohne ihn laesst sich fuer Markise und
Sonnensegel kein Sturmschutz bauen.

Nicht dabei sind zwei Topics. IP ist keine Messung. Und QNH steht bei
uns gut hundert Hektopascal neben qff (1133 gegen 1023) und ist
offenbar nicht auf die Stationshoehe eingestellt - zwei Druecke
nebeneinander, von denen einer falsch ist, waeren im Editor eine Falle.

Dabei aufgefallen: der UPSERT schrieb unit = VALUES(unit)
bedingungslos. Ein Modul, das keine Einheit mitliefert, meint aber
"weiss ich nicht" und nicht "loesch die vorhandene" - am 10.09. hat ein
Suchlauf den drei Sonnensensoren ihr "Lux" genommen, weil das
Tahoma-Modul keine Einheiten kennt. Jetzt COALESCE, wie bei
current_value schon laenger.

config.ini liegt nicht im Git; der Abschnitt [wetterstation] muss dort
von Hand stehen, sonst bleibt das Modul aus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-10 20:08:10 +02:00
co-authored by Claude Opus 5
parent b607d4a064
commit 38e6c3f96b
2 changed files with 148 additions and 3 deletions
+26 -3
View File
@@ -21,6 +21,7 @@ from modules.mqtt_module import MQTTModule
from modules.shelly_module import ShellyModule
from modules.gartenwasser_module import GartenwasserModule
from modules.solarmanager_module import SolarManagerModule
from modules.wetterstation_module import WetterstationModule
from modules.logic_module import LogicModule #nicht Vergessen neue Module auch unten in die Liste "modules" einzufügen!!
# Logging
@@ -166,6 +167,13 @@ class Config:
return False
return self._get_bool('solarmanager', 'enable', False)
# WETTERSTATION
@property
def wetterstation_enable(self) -> bool:
if not self.config.has_section('wetterstation'):
return False
return self._get_bool('wetterstation', 'enable', False)
# MQTT
@property
@@ -431,7 +439,14 @@ class DatabaseManager:
-- Wert zufaellig einmal aenderte.
current_value = COALESCE(VALUES(current_value), current_value),
possible_values = VALUES(possible_values),
unit = VALUES(unit),
-- Dieselbe Ueberlegung wie bei current_value: ein Modul,
-- das keine Einheit mitliefert, meint "weiss ich nicht"
-- und nicht "loesch die vorhandene". Ohne COALESCE hat
-- ein Suchlauf am 10.09.2026 den Sonnensensoren ihr
-- "Lux" genommen, weil das Tahoma-Modul keine Einheiten
-- kennt - im Automatik-Editor stand danach "877" ohne
-- Angabe, wovon.
unit = COALESCE(VALUES(unit), unit),
url = VALUES(url),
value_path = VALUES(value_path)
"""
@@ -504,7 +519,14 @@ class DatabaseManager:
-- Siehe insert_actor: NULL heisst "kein Startwert",
-- nicht "Messwert loeschen".
current_value = COALESCE(VALUES(current_value), current_value),
unit = VALUES(unit),
-- Dieselbe Ueberlegung wie bei current_value: ein Modul,
-- das keine Einheit mitliefert, meint "weiss ich nicht"
-- und nicht "loesch die vorhandene". Ohne COALESCE hat
-- ein Suchlauf am 10.09.2026 den Sonnensensoren ihr
-- "Lux" genommen, weil das Tahoma-Modul keine Einheiten
-- kennt - im Automatik-Editor stand danach "877" ohne
-- Angabe, wovon.
unit = COALESCE(VALUES(unit), unit),
url = VALUES(url),
value_path = VALUES(value_path),
possible_values = VALUES(possible_values)
@@ -589,7 +611,8 @@ def main():
ShellyModule(config),
LogicModule(config),
GartenwasserModule(config),
SolarManagerModule(config)
SolarManagerModule(config),
WetterstationModule(config)
]
# Jedes Modul durchlaufen
@@ -0,0 +1,122 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Wetterstation Module
Die eigene Wetterstation am Haus - fest beschrieben, nicht gesucht
KEINE Datenbank-Operationen!
"""
import logging
from typing import List, Dict, Tuple
from modules.base_module import BaseModule
logger = logging.getLogger(__name__)
# Wurzel aller Topics. wsMQTTbridge.py im SolarManager hängt am Websocket der
# Station (ws://192.168.179.42/ws) und legt jedes Feld der Nachricht einzeln
# unter diese Wurzel - retained, deshalb hat ein frisch verbundener Leser
# sofort alle Werte.
#
# Zwei Eigenheiten der Brücke, die man kennen muss:
#
# * Die trägen Werte gehen nicht bei jeder Nachricht raus. Die Brücke wirft
# tempAmb, hum, qff, dewpt, iaq, avgWindspeed und avgWindDeg aus jeder
# Nachricht heraus außer jeder einundzwanzigsten. Wind und Böe kommen
# dagegen immer. Für eine Automatik macht das keinen Unterschied - der
# Runner hält den zuletzt gesehenen Wert -, für die Fehlersuche schon:
# eine Außentemperatur, die minutenlang steht, ist kein Ausfall.
#
# * Der Zeitstempel kommt von der Station selbst, nicht vom Broker.
WURZEL = "weatherStation"
# Was von der Station als Messwert im Editor auftaucht.
#
# Nicht dabei sind zwei Topics. "IP" ist keine Messung. Und "QNH [hPa]" ist
# der auf Meereshöhe gerechnete Druck nach Standardatmosphäre - er steht bei
# uns gut hundert Hektopascal neben qff (1133 gegen 1023) und ist offenbar
# nicht auf die Stationshöhe eingestellt. qff rechnet mit der tatsächlichen
# Temperatur und ist der Wert, den auch die Wetterkachel auf der Startseite
# zeigt. Zwei Drücke nebeneinander, von denen einer falsch ist, wären im
# Editor eine Falle.
#
# Die Windgeschwindigkeiten stehen in km/h - so beschriftet sie
# restricted/weather_cards.html, und so passen die Zahlen zur Historie in
# solarLog.weatherStation.
#
# Windrichtung und Böenrichtung sind Grad im Uhrzeigersinn ab Nord, wie beim
# Kompass: 0 = aus Norden, 90 = aus Osten, 180 = aus Süden, 270 = aus Westen.
# Eine Bedingung darauf ist unhandlich, weil Nord bei 0 und bei 360 liegt -
# für "Wind aus Westen" schreibt man zwei Bedingungen (> 225 und < 315).
MESSWERTE = [
# (Name im Editor, Topic, Typ, Einheit)
("Außentemperatur", "tempAmb", "float", "°C"),
("Luftfeuchte", "hum", "float", "%"),
("Taupunkt", "dewpt", "float", "°C"),
("Luftdruck", "qff", "float", "hPa"),
("Wind", "windspeed", "float", "km/h"),
("Wind im Mittel", "avgWindspeed", "float", "km/h"),
("Böe", "maxgust", "float", "km/h"),
("Windrichtung", "windDeg", "float", "°"),
("Böenrichtung", "gustDeg", "float", "°"),
("Luftgüte", "iaq", "float", None),
# Kein Wert zum Vergleichen, sondern zum Nachsehen: im Editor steht er
# hinter dem Namen und verrät, ob die Brücke noch läuft. Genauso hält es
# der Regen mit seinem "Stand".
("Stand", "Timestamp", "string", None),
]
class WetterstationModule(BaseModule):
"""
Wetterstation - Implementiert BaseModule Interface
Wie das Gartenwasser-, das Logic- und das SolarManager-Modul sucht dieses
nichts, sondern schreibt ein festes Gerät hin. Die Station meldet sich
nicht per Home-Assistant-Discovery; das MQTT-Modul findet sie also nicht.
Warum sie überhaupt hineingehört: für den Hitzeschutz ist die
Außentemperatur die bessere Bedingung als die Raumtemperatur. Wenn es
drinnen schon warm ist, ist es zum Verschatten zu spät - die Wärme steht
dann bereits im Raum und muss wieder heraus. Draußen sieht man sie
kommen. Und Wind gab es hier bisher gar nicht: ohne ihn lässt sich für
Markise und Sonnensegel kein Sturmschutz bauen.
Alles hier ist zum Lesen. Zu schalten gibt es an einer Wetterstation
nichts.
"""
def is_enabled(self) -> bool:
"""Prüft ob das Wetterstation-Modul aktiviert ist"""
return self.config.wetterstation_enable
def discover(self) -> Tuple[List[Dict], List[Dict]]:
"""
Erzeugt das eine Gerät aus MESSWERTE - als Sensor, ohne Kommandos.
Returns:
Tuple (actors, sensors)
"""
logger.info("\n" + "=" * 60)
logger.info("WETTERSTATION WIRD ERZEUGT")
logger.info("=" * 60)
states = [{
"name": name,
"url": WURZEL + "/" + topic,
"type": typ,
"unit": einheit,
} for name, topic, typ, einheit in MESSWERTE]
geraet = {
"type": "Wetter",
"name": "Wetterstation",
# Die URL eines Geräts ist das Topic seines ersten Messwerts, wie
# beim SolarManager. Sie muss nur eindeutig sein und mit mqtt://
# beginnen, damit der Runner den MQTT-Transport wählt; abonniert
# wird über die Topics der Messwerte.
"url": "mqtt://" + states[0]["url"],
"states": states,
}
logger.info("1 Gerät mit %d Messwerten", len(states))
return [], [geraet]