Commit Graph
29 Commits
Author SHA1 Message Date
adminandClaude Opus 5 93fb054b66 Zeilenenden vereinheitlichen: LF ueberall ausser im Fremdcode
Bisher entschied jede Arbeitsstation fuer sich, welche Zeilenenden ein
Commit bekommt: Git fuer Windows setzt core.autocrlf=input in seiner
System-Konfiguration, die NAS setzt nichts. Weil der Bestand ausserdem in
sich gemischt war - 34 PHP-Dateien mit LF, 30 mit CRLF -, gab es gar kein
richtiges Zeilenende, an das ein Werkzeug sich haette halten koennen. Jede
Ergaenzung mit LF in einer CRLF-Datei ergab eine gemischte Datei; helper.php
und js/solar/homeMQTT.js waren bereits so entstanden.

.gitattributes macht die Zeilenenden zur Eigenschaft des Repositorys. LF,
weil dieses Verzeichnis der Web-Ordner der NAS ist und von Linux
ausgeliefert wird - und weil Git fuer Windows mit "input" ohnehin schon
dorthin zeigt.

restricted/WebAuthn bleibt ausgenommen und damit byteweise so, wie die
Bibliothek geliefert wurde; darunter liegen 292 Zertifikate.

Von den 47 geaenderten Dateien wurde nachgerechnet keine einzige im Inhalt
angefasst - der Vergleich HEAD-ohne-CR gegen Index ist ueberall gleich.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 23:04:00 +02:00
adminandClaude Opus 5 d3c1cb4682 Aussengelaende wird die vierte Etage AG
Was draussen haengt, stand als Pseudo-Raum "aussen" in automations.php:
"Geraete, die zu keinem Raum der Wohnung gehoeren - Carport, Garage,
Regensensor". Das waren inzwischen sechzehn Geraete in einem einzigen Topf,
der groesste ueberhaupt - in der Geraeteauswahl des Editors standen die fuenf
Carport-Wechselrichter, das Sonnensegel und das Garagentor unsortiert
nebeneinander.

Jetzt ist das Aussengelaende eine Etage wie OG, EG und UG, mit eigenen
Raeumen: Carport, Veranda, Pergola, Terrasse, Garage und Garten. Die Namen
stammen von den Geraeten selbst - die Hoymiles heissen seit jeher "Carport
1-6" und "Veranda OG2-4". Die achtzehn Geraete sind verteilt (die beiden
Ventilsteuerungen hatten noch gar keinen Raum und stehen jetzt im Garten,
zusammen mit dem Regensensor, der ihre Automatiken ausloest); "Ohne Raum"
faellt damit von acht auf sechs, und die sind es zu Recht.

AG ist eine Etage ohne Grundriss. Ihre Raeume haben kein x/y, es gibt ja
kein Bild, auf das eine Kachel zeigen koennte - ein Raum ohne Position war
schon immer vorgesehen. Das Menue und das SVG richten sich deshalb nach der
neuen floorsWithPlan(), die Zuordnung und die Automatiken nach $floors.
Bekommt der erste AG-Raum eine Kachel, erscheint die Etage von selbst auch
im Menue; es gibt keine zweite Liste, die man nachziehen muesste.

Die Etagen standen bisher an sechs Stellen noch einmal im Code. Jetzt gibt
es genau eine Liste, $floors in rooms.php, und alles andere leitet sich ab:

  * automations.php prueft gegen $floors statt gegen eine eigene Whitelist
  * ajax/AutoAction.php baut seine Etagen-Auswahl daraus
  * home.php baut Reiter und Inhaltsbereiche in einer Schleife statt
    dreimal fast dasselbe HTML
  * header.php baut die Menuepunkte daraus
  * switchTab() in homeMQTT.js liest die Reiter aus der Seite, statt OG, EG
    und UG sechsmal aufzuzaehlen
  * refreshAutomations() ebenso - eine feste Liste haette die vierte Etage
    still ausgelassen, die Tabelle waere ohne Fehlermeldung leer geblieben

Dazu die ausgeschriebenen Namen in $floorLabels: das Kuerzel steht in URLs,
SVG-Ids und im MQTT-Baum, "Aussengelaende" nur als Titel am Menuepunkt und
am Reiter.

In der Datenbank ist das Enum um 'AG' erweitert; header.php laedt rooms.php
jetzt selbst, weil es vor home.php eingebunden wird.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 17:55:11 +02:00
adminandClaude Opus 5 2ef946c732 Akkugroessen der beiden Autos stimmen und stehen in der Konfiguration
Der Schieberegler "Zusaetzlich benoetigte Ladung" rechnet Prozent in
Wattstunden um. Dafuer standen bisher drei verschiedene Zahlen im Code:
140 Wh/% beim Schreiben der EG-Wallbox, 90 Wh/% beim go-e und feste
14000 Wh beim Zurueckrechnen im Browser - fuer beide Autos dieselbe. Eine
Einstellung von 50 % am go-e schrieb 4500 Wh und las sich beim naechsten
Oeffnen als 32 % zurueck.

Jetzt kommt die Zahl aus einer Quelle: restricted/config.php nennt Auto
und nutzbare Akkukapazitaet je Wallbox, carForm.php gibt die Wattstunden
je Prozent als data-Attribut an den Schieberegler weiter, und das
JavaScript rechnet damit zurueck statt mit einer festen Konstanten.

EG: Skoda Enyaq RS, Modelljahr 2026, 84 kWh brutto / 79 kWh nutzbar
OG: Jeep Compass 4xe, 11,4 kWh

Das Lademodal zeigt Auto und Kapazitaet jetzt unter dem Regler an - damit
sichtbar ist, worauf sich die Prozente beziehen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 08:53:03 +02:00
adminandClaude Opus 5 61ea98db1d Beide Wallboxen werden gleich bedient
carEG.php und carOG.php waren fast identisch und unterschieden sich nur im
Transportweg: der go-e bekam seine Kommandos per HTTP, die Wattpilot ueber
einen eigenen Python-Prozess je Klick, der sich einen Websocket aufbaute,
anmeldete, ein Feld setzte und wieder starb.

Beide haengen laengst am selben MQTT-Broker - der go-e mit eigenem Client
(mce=true, mcr=false), die Wattpilot ueber wattpilot_bruecke.py. Beide
nehmen Kommandos auf <praefix><schluessel>/set entgegen. Die Steuerung
steht deshalb jetzt einmal in carSteuerung.php; was die Wallboxen wirklich
trennt - Topic-Praefix, die Namen ate/att gegen ftt/fte und die
Wattstunden je Prozent - steht als Tabelle in restricted/wallboxen.php.

Nebenbei faellt damit /volume1/homes/wagner/wattpilot.py weg, das das
Passwort der Wallbox im Klartext enthaelt und ausserhalb jedes Repos liegt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 08:36:13 +02:00
adminandClaude Opus 5 cbc9353f0a Protokollseite: die Logs der Hintergrundprozesse im Dashboard
Bisher kam man an die Ausgabe von solarManager.py, dem Runner und den
uebrigen Dauerprozessen nur per SSH heran. Die neue Seite unter
"Protokolle" zeigt sie: links die fuenf Dateien mit Groesse und Alter -
eine, die seit Tagen nicht gewachsen ist, faellt so auf, ohne dass man sie
oeffnet -, rechts die Zeilen mit Volltextsuche, Level-Filter und
optionalem Nachladen.

Nicht in die Datenbank geschrieben, obwohl das naheliegt: ein Log muss
genau dann noch funktionieren, wenn die Datenbank es nicht tut. Ein
Handler, der nach MySQL schreibt, verschluckt ausgerechnet die Meldung
"Datenbank nicht erreichbar" - also die, wegen der man nachsieht. Was
strukturiert ausgewertet werden soll, steht ohnehin in automation_log.

Gelesen wird rueckwaerts in Bloecken: 5000 Zeilen aus der 329 MB grossen
Datei der Wallbox kosten 46 ms, weil nur das Ende angefasst wird. Sucht
man ins Leere, bricht der Server nach 4 MB ab und sagt es auch.

Die beiden Prozesse formatieren unterschiedlich - der eine stellt das
Level voran, der andere den Zeitstempel -, deshalb wird beides gesucht
statt an fester Stelle erwartet. Zeilen ohne eigenes Level erben das der
Zeile darueber, sonst filterte "nur Fehler" die Tracebacks weg, die den
Fehler erklaeren.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 19:26:56 +02:00
adminandClaude Opus 5 d285ec27a0 Einstellungsseite, Skoda-Seite und Karte
Sammelstand der offenen Arbeit im Dashboard.

Einstellungen: eine Eingabemaske fuer die Energiepreise unter
restricted/settings.php, dazu costs.php als Modell und ajax/settings.php
als Endpunkt. Die Preise stehen jetzt in gridCosts, gasCosts und fuelCosts
statt fest im Quelltext von getStats.php; die Jahresstatistik rechnet ueber
LEAD() den jeweils gueltigen Zeitraum aus und schlaegt den anteiligen
Grundpreis auf. Eingegeben wird in gewohnten Einheiten - l/100 km,
kWh/100 km, Euro je Liter -, umgerechnet wird beim Speichern.
solarLog_costs.sql beschreibt die Umstellung der Tabellen.

Skoda: eigene Seite mit Live-Werten ueber MQTT und Historie ueber
ajax/skoda.php, Kommandos ueber ajax/skodaCmd.php.

Karte: Leaflet mit einem eigenen Kachel-Zwischenspeicher (ajax/tile.php),
dessen Ablage unter tiles/ nicht ins Repository gehoert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 21:21:27 +02:00
adminandClaude Opus 5 79a55aeafd Kacheln zeigen frei gewaehlte Messwerte, Heizung nur wo eine haengt
Drei Sachen an den Kacheln und am Raum-Modal.

Die Platzhalter sind nur noch Striche. Vorher stand dort "---- hPa",
gefuellt wurde die Zeile aber mit der Solltemperatur - eine Einheit, die
nie kam, an einer Stelle, die etwas anderes zeigt.

Der Heizungs-Reiter erscheint nur noch, wo auch ein Thermostat haengt.
Frueher entschied das "hat der Raum eine Kachel" - das stimmte, solange
nur Raeume mit Thermostat eine bekamen. Seit auch Kueche und Technik eine
haben, bekamen sie einen Regler fuer eine Heizung, die es dort nicht gibt.
Jetzt zaehlt, ob im Raum ein Geraet der Bedienform "heizung" steht.

Und eine Kachel zeigt jetzt ein bis drei frei gewaehlte Messwerte. In
restricted/rooms.php steht je Raum unter "werte", welche das sind: topic,
optional pfad (Schluessel in einer JSON-Nachricht), einheit, stellen. Ohne
Angabe bleibt es bei den drei Thermostatwerten samt Heizsymbol, also genau
wie bisher - nur diese Raeume zeichnen die Symbole ueberhaupt noch.

Alles kommt live ueber MQTT, auch die beiden Shelly-Zaehler: sie schicken
ihre sechzehn Werte als eine JSON-Nachricht auf Power_EG/status/em:0, aus
der "pfad" den gesuchten Schluessel greift. Der Browser meldet nicht mehr
fest "Raumtemp/#" an, sondern die Zweige, die die Raumtabelle nennt -
derzeit vier statt vierzig einzelner Topics.

Damit steht auf EG Technik die Leistung (gemessen 293 W), auf UG Technik
Leistung und Wasserzaehler (78 W, 400,4 m3). Raeume ohne Messwert tragen
ihren Namen und oeffnen den Raum; ein leeres Rechteck saehe aus wie ein
Fehler.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 22:55:58 +02:00
adminandClaude Opus 5 08e8e68a61 Raum-Modal: Solltemperatur wirkt sofort, kein Speichern-Knopf mehr
Im Raum-Modal wirkt jedes Bedienelement sofort - der Rollladen faehrt, der
Schalter schaltet, die Lampe leuchtet. Nur die Solltemperatur wartete auf
"Save changes". Sie schickt jetzt beim Loslassen des Reglers, wie die
Rollladenregler daneben, und der Knopf ist im Raum-Modal weg. Einer, der
nur fuer einen der Reiter gilt, waere in allen anderen eine Falle.

Das Modal bleibt nach dem Schicken offen; frueher schloss es sich, weil
der Knopf das Ende der Bedienung war. Wer dreimal nachjustiert, will nicht
dreimal neu aufmachen. Die Heizungskarte traegt dafuer dieselbe Sperre wie
die Geraetekarten - waehrend das Kommando unterwegs ist, ist sie gesperrt.

Dabei einen Fehler mitgenommen: der Speichern-Knopf gehoert allen Modals
gemeinsam, und wer ihn versteckt, versteckt ihn fuer alle. Ein Raum ohne
Thermostat liess ihn schon bisher verschwinden - danach hatten der
AutoAction-Editor und die Raumzuordnung bis zum Neuladen keinen
Speichern-Knopf mehr. Beide holen ihn jetzt wieder hervor. Nachgemessen:
im Raum-Modal versteckt, im Editor und in der Raumzuordnung sichtbar.

Die Fahrknoepfe sind von 34 auf 50 Pixel gewachsen - sie sind das, was man
im Vorbeigehen antippt, die Regler daneben braucht man seltener.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 22:20:15 +02:00
adminandClaude Opus 5 e8612bebb8 Raum-Modal: Heizung in einen eigenen Reiter, Spalten buendig
Die Heizung stand ueber den Tabs und war in jedem Raum sichtbar, auch wenn
man wegen der Rollladen gekommen war. Sie ist jetzt eine Gruppe wie die
anderen - nur baut sie sich nicht aus einer Geraeteliste, sondern ist ein
Regler fuer den Raum. zeichneGruppen() nimmt dafuer fertige Inhalte
entgegen. Bei einem Raum mit nur einem Thermostat bleibt die Leiste weg,
dort sieht es aus wie vorher.

Aus den Pillen sind Reiter geworden - sie sehen jetzt aus wie das, was sie
sind, und nicht wie eine Knopfreihe.

Position und Neigung standen um 0,35 rem versetzt: die Regel fuer
gestapelte Regler (Helligkeit ueber Farbe) griff auch auf die zweite
Spalte, die aber danebensteht. Sie gilt jetzt nur noch fuer Regler, die
keine Spalte sind - nachgemessen stehen beide Bahnen auf 181/104.

Das Fensterbild ist von "so breit wie moeglich" auf 5 rem zurueckgenommen;
der uebrige Platz verteilt sich auf die Zwischenraeume, statt das Bild
breitzuziehen. Zwischen ihm und den Bedienelementen liegt jetzt Luft.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 22:11:42 +02:00
adminandClaude Opus 5 f6e0414866 Raum-Modal: Gruppen als Tabs, Jalousien senkrecht bedienen
Die Gruppenueberschriften sind Tabs geworden, Beschattung zuerst - das ist
es, was man in einem Raum am haeufigsten anfasst. Vorher scrollte man an
drei Rollladen vorbei, um zum Licht zu kommen. Bei nur einer Gruppe bleibt
die Leiste weg; auf schmalen Schirmen scrollt sie seitwaerts, statt die
Namen umzubrechen.

Bei den Jalousien stehen Auf, Stop und Zu jetzt untereinander - so zeigen
die Knoepfe in die Richtung, in die sie fahren. Die Hoehe daneben ist ein
senkrechter Regler mit "oben" und "unten" an den Enden: der Griff steht
dort, wo der Rollladen steht, und niemand muss "100 %" uebersetzen.

Die Neigung hat ein Bild dazubekommen. "30 %" oder "70 %" sagt nicht, ob
man noch hinaussieht; vier Streifen sagen es. Gezeichnet wird die Hoehe,
die eine gekippte Lamelle von vorn zeigt (w * sin(Winkel)) - bei "auf"
sieht man nur ihre Kante und viel Luft dazwischen, bei "zu" deckt sie den
Abstand zur naechsten vollstaendig ab. Genau das, was das Licht abhaelt.

Die Regler faerben ihre Bahn jetzt mit, wie es der Temperaturregler seit
jeher tut - dieselbe Eigenschaft --background-size, nur beim senkrechten
von oben nach unten.

css/solar.css hatte gemischte Zeilenenden und ist dabei durchgehend auf LF
gebracht worden; das erklaert den grossen Diff bei kleiner Aenderung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 21:38:09 +02:00
adminandClaude Opus 5 17f972d081 Raum-Modal: Bedienelemente statt Kommandoliste
Das Modal zeigte jedes Kommando so, wie es in der Datenbank steht: eine
Jalousie kam mit acht Rohbefehlen daher, darunter "Winken", die Farbe einer
Lampe als drei Zahlenfelder 0-255, ein Stromzaehler als abgeschnittene
Textzeile. Die Solltemperatur stand doppelt da - einmal als Schieberegler,
einmal als Zahlenfeld der Thermostat-Karte.

Jetzt entscheidet restricted/roomControls.php, welches Geraet welches
Bedienelement bekommt, und zwar an seinen Faehigkeiten statt am Typnamen:
hat es ein "setClosure"? ein An/Aus-Paar? drei Parameter rot/gruen/blau?
Damit bekommt auch ein neu gefundenes Geraet sein passendes Element, ohne
dass jemand einen Typnamen nachtraegt.

  Beschattung   Auf/Stop/Zu, darunter Position und Neigung als Schieber
                auf dem zuletzt gemeldeten Stand; "Winken" und
                "my-Position" im Mehr-Menue
  Licht         Kippschalter, Helligkeit, Farbwaehler des Browsers,
                Effekt und Preset als Auswahl
  Schalten      ein Kippschalter statt dreier Knoepfe
  Messwerte     Wertegitter, die sechs wichtigsten sichtbar - beim
                Wasserzaehler standen vorher MAC-Adresse und freier
                Speicher vorn und der Verbrauch hinter "12 weitere"
  Heizung       nur noch der Schieberegler, daneben Ist-Temperatur und
                Feuchte live aus dem MQTT-Strom

Ab zwei Geraeten einer Art ordnet eine Ueberschrift; darunter waere sie
nur Aufwand. Schieberegler schicken beim Loslassen, nicht beim Ziehen -
sonst bekaeme eine Jalousie bei jedem Pixel ein neues Ziel. Waehrend ein
Kommando unterwegs ist, sperrt sich die Karte sichtbar: ein "Zu" dauert
ueber eine Minute, und ohne Sperre tippt man in der Zeit nach.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 21:18:46 +02:00
adminandClaude Opus 5 131d7cc46d Jalousien: Zu heisst zweifach schwenken, Versand laeuft im Hintergrund
Die Kugelschreiber-Mechanik betrifft jedes Kommando, nicht nur
setOrientation, und sie betrifft auch das Schliessen: ein "Zu" faehrt die
Jalousie zwar herunter, die Lamellen bleiben aber bei etwa 30 % offen
stehen. Dicht wird sie erst durch das zweifache Schwenken.

Die Regel lautet jetzt einheitlich: derselbe Befehl zweimal - erst mit
Neigung 0, dann warten, bis die Fahrt steht, dann mit dem gewuenschten Wert.
Die Position bleibt dabei erhalten, die Jalousie faehrt also nur einmal.

    Zu                     -> [100,0] warten [100,100]
    Neigung 20             -> direkt, unter der Schwelle
    Neigung 80             -> [0] warten [80]
    Position 40 Neigung 50 -> [40,0] warten [40,50]

"Zu" wird dabei nur umgeschrieben, wenn das Geraet Position und Neigung
zusammen setzen kann - der einfache Rollladen "Terasse" hat keine Lamellen
und bekommt weiter das rohe down.

Gewartet wird auf zwei Auskuenfte zusammen, weil einzeln keine traegt:
core:MovingState trug die lange Fahrt (gemessen 61 s), wird bei kurzen
Neigungsfahrten aber nie gesetzt; die Zustandswerte sind die Wahrheit,
zeigen direkt nach dem Kommando aber noch den alten Stand. Fertig heisst:
nichts faehrt mehr, die Ziele stimmen, und es wurde entweder ein "faehrt"
gesehen oder der Vorlauf von acht Sekunden ist um.

Und weil ein "Zu" damit ueber eine Minute dauert, schickt der Runner nicht
mehr selbst: der neue Versand nimmt die Kommandos entgegen und arbeitet sie
in eigenen Faeden ab - je Geraet der Reihe nach, ueber Geraete hinweg
nebeneinander. Sonst haette eine einzige Jalousie die ganze Auswertung fuer
eine Minute angehalten: keine Zeit-Ausloeser, keine Messwerte, und mehrere
Rollladen in einer Automatik haetten sich aufaddiert. Die Datenbank bleibt
dabei im Hauptfaden - die Faeden melden nur ihr Ergebnis zurueck, eine
pymysql-Verbindung ist nicht fuer mehrere Faeden gedacht.

Nachgemessen: Einreihen kehrt sofort zurueck, zwei Kommandos an dasselbe
Geraet laufen nacheinander (6 s, dann 12 s), drei an verschiedene Geraete
gleichzeitig, Fehler kommen zurueck, und die Schleife lief in 20 Sekunden
20 Takte durch.

Die Bedienung im Modal wartet weiterhin - dort sitzt ein Mensch vor einem
Fortschritt und klickt einmal. ajax/room.php hebt dafuer die Zeitgrenze auf
180 Sekunden an, wie es ajax/tahoma.php an derselben Stelle auch tut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 20:29:40 +02:00
adminandClaude Opus 5 98a1da6cd5 Raum-Modal: Heizung, Rollladen und Licht auf einer Seite
Der Grundriss zeigt Raeume, ein Klick darauf zeigte aber nur den
Temperaturregler. Die Rollladen im selben Zimmer waren von dort gar nicht
erreichbar, obwohl die Karte genau sagt, wo sie haengen.

Das Modal zeigt jetzt alles, was dem Raum zugeordnet ist: die
Solltemperatur wie bisher, dazu je Geraet seine Kommandos. Kommandos ohne
Parameter werden Knoepfe nebeneinander - "Auf", "Zu", "Stop" will man
antippen und nicht erst einstellen -, Kommandos mit Parametern bekommen
ihre Eingabefelder und einen Ausloeser. Geraete ohne Kommandos zeigen ihre
Messwerte. Die Reihenfolge der Knoepfe kommt nicht mehr aus dem
Geraete-Suchlauf: die haeufig gebrauchten stehen vorn.

Ausgefuehrt wird ueber restricted/commands.php - das Gegenstueck zu
transports.py, nur fuer den Browser. Dieselbe Zuordnung "welche URL gehoert
zu welchem Weg", nur eben in PHP:

    mqtt://   Nutzlast auf das Topic des Parameters
    wled://   JSON-Vorlage mit Platzhaltern an /json/state
    http://   Abfrageargumente an die Geraete-URL
    Tahoma    exec/apply, erkannt an der Box-Kennung in der URL

Zwei Umsetzungen derselben Sache sind nicht schoen. Die Alternative waere,
den Runner um eine HTTP-Schnittstelle zu erweitern - dann haengt die
Bedienung daran, dass er laeuft - oder das Web-UI Python aufrufen zu
lassen. Beides waere teurer als diese Datei; der Kommentar sagt das an
beiden Stellen.

Der Endpunkt kennt einen Probelauf: mit "probe" beschreibt er, was er
schicken wuerde, ohne es zu schicken. Damit sind alle vier Wege geprueft,
ohne einen Rollladen in Bewegung zu setzen - Tahoma mit und ohne Parameter,
WLED mit gefuellter Vorlage, Shelly Gen1 und MQTT.

ajax/roomtemp.php entfaellt, es ging darin ausschliesslich um den Regler,
den das Raum-Modal jetzt enthaelt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 16:43:21 +02:00
adminandClaude Opus 5 4b47372015 Geraete den Raeumen zuordnen
Beim Anlegen einer Automatik denkt man in Raeumen ("im Bad soll ..."), nicht
in Geraetearten. Ein Raummodell gibt es schon - restricted/rooms.php, die
einzige Quelle fuer die Home-Ansicht -, nur fehlte die Zuordnung Geraet ->
Raum.

Die neue Spalte actors.room haelt sie als "Etage/Raum" (OG/Bad), dazu
"aussen" fuer Carport, Garage und Regensensor. Etage und Raum zusammen,
weil "Bad" allein auf drei Etagen vorkommt. Discovery fasst die Spalte nicht
an - der Upsert setzt nur type und name -, die Zuordnung ueberlebt also
jeden Suchlauf.

Zugeordnet wird ueber das Haus-Symbol in der Karte "Automatismen". Wo der
Raum aus dem MQTT-Topic hervorgeht ("Raumtemp/OG/Bad/Temp[degC]"), schlaegt
die Ansicht ihn vor und uebernimmt ihn auf Knopfdruck - das sind die
dreizehn Thermostate, ohne jedes Raten. Bei allen anderen wird bewusst nicht
geraten: "Bad Links" sagt nichts darueber, auf welcher der drei Etagen
dieses Bad liegt. Noch nicht zugeordnete Geraete stehen in der Tabelle oben.

Die Geraeteauswahl im Editor gruppiert danach nach Raum, in der Reihenfolge
aus rooms.php. Solange niemand zugeordnet hat, bleibt es bei der Gruppierung
nach Geraeteart - eine einzige Gruppe "Nicht zugeordnet" waere ein
Rueckschritt. Und weil auch nach der ersten Zuordnung noch viel ohne Raum
ist, wird dieser Rest weiter nach Geraeteart unterteilt ("Ohne Raum ·
Aussenjalousie (11)").

Anders als bei den Gerätearten behalten Raeume mit nur einem Geraet ihre
Ueberschrift: "OG · Bad" ist auch mit einem einzigen Thermostat genau die
Zeile, die man sucht - und mehr als eines haengt selten in einem Raum.

Die dreizehn Thermostate sind bereits zugeordnet, weil das ihre eigenen
Topics hergeben; alles andere steht auf "-" und will von Hand vergeben
werden.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 16:16:48 +02:00
adminandClaude Opus 5 0cb1fc635e Etage ist Pflicht
Die Auswahlliste im Editor bot ein "-" an. Eine Automatik ohne Etage taucht
aber in keinem der drei Reiter auf und ist danach nur noch in der Datenbank
zu finden - anlegen und sofort verlieren.

Der Eintrag ist weg, und der Zustand ist jetzt an drei Stellen unmoeglich
statt nur unwahrscheinlich: die Liste kennt nur noch OG, EG und UG,
saveAutomation lehnt alles andere mit einer Meldung ab statt still auf ""
zurueckzufallen, und im Enum der Spalte gibt es den leeren Wert nicht mehr.

Die Reihenfolge in der Liste ist jetzt OG, EG, UG wie bei den Reitern -
damit trifft der Rueckfall auf den ersten Eintrag dieselbe Etage wie die
Startseite. Kennt die Liste die Etage eines Datensatzes nicht, faellt sie
darauf zurueck, statt leer stehen zu bleiben und beim Speichern "" zu
schicken.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 13:32:58 +02:00
adminandClaude Opus 5 27e5c0d331 Sperrzeit je Automatik in drei Stufen
Die steigende Flanke allein schuetzt nicht gegen einen Messwert, der um die
Schwelle pendelt: "Temperatur > 22" bei 22,1 / 21,9 / 22,1 Grad ist jedes
Mal eine echte Flanke, und ueber MQTT koennen die Werte im Sekundentakt
hereinkommen. Gemessen: fuenf Kommandos in einer halben Minute.

automations.lockout_secs sagt jetzt, wie lange nach einer Ausloesung nicht
wieder geschaltet wird. Der Editor bietet drei Stufen an - ohne, eine
Minute, eine Viertelstunde -, weil die passende Wahl am Geraet haengt und
nicht an einer Zahl: ein Rollladen soll nicht alle zwanzig Sekunden
losfahren, eine Lichtfarbe darf das. Gespeichert werden Sekunden, damit eine
vierte Stufe eine Zeile in lockoutChoices() ist und keine Wanderung durch
die Datenbank. Vorbelegt ist eine Minute.

Eine Flanke in der Sperrzeit wird verworfen, nicht aufgehoben. Ein
Rollladen, der eine Viertelstunde spaeter doch noch losfaehrt, weil vor
langer Zeit einmal eine Schwelle gestreift wurde, waere unangenehmer als
einer, der gar nicht faehrt - und der naechste echte Anlass nach Ablauf der
Sperre kommt ohnehin durch. Verworfene Flanken stehen auf DEBUG und nicht in
automation_log, sonst waere die Tabelle bei einem zappelnden Sensor voll
davon.

force_once bleibt unberuehrt: es greift nur, wenn im Fenster gar nichts
gelaufen ist - dann ist auch keine Sperre aktiv.

Nachgemessen am pendelnden Sensor: mit 60 s Sperre ein Kommando, ohne Sperre
fuenf.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 13:26:18 +02:00
adminandClaude Opus 5 5f643b9844 AutoAction-Runner: Automatiken ausfuehren
Bisher konnte man Automatiken nur anlegen - ausgefuehrt hat sie niemand.
restricted/autoActions/autoaction_runner.py holt das nach.

Dauerlaeufer statt Cronjob, aus zwei Gruenden: Schwellwert-Ausloeser sollen
greifen, wenn die MQTT-Nachricht hereinkommt, und actor_states.current_value
wird sonst von niemandem fortgeschrieben - beim Discovery einmal gesetzt und
danach nie wieder. Ein zustandsloser Lauf haette gar nichts, womit er
vergleichen koennte. Der Runner pflegt den Wert nebenbei mit, wovon auch der
Editor profitiert: er zeigt neben jedem Messwert den aktuellen Stand.

Ausgeloest wird nur auf der steigenden Flanke (automations.cond_met), sonst
wuerde "Temperatur ueber 22 Grad" bei jedem Takt erneut feuern. Aus
demselben Grund heissen Zeit-Ausloeser jetzt "ab 16:30" statt "gleich
16:30": ein Gleichheitsvergleich waere nur in einer einzigen Minute wahr,
und ein Ausfall in genau dieser Minute kostet den ganzen Tag. Dieselbe
Ueberlegung steht hinter den breiten Zeitfenstern in auto_watering.py.

Der Weg zum Geraet haengt an der URL des Aktors: mqtt:// abonniert und
publiziert, http:// pollt und haengt Parameter an, io:// spricht mit der
Tahoma-Box, Logic rechnet Uhrzeit, Datum und Sonnenzeiten. Alle vier stehen
in transports.py; eine fuenfte Geraeteart ist eine weitere Klasse mit
passt(), zustaende_lesen() und senden().

fetch_calendar.py fuellt calendar_days aus openholidaysapi.org, damit "in
den Ferien" und "an Feiertagen" eine Grundlage haben. Einmal jaehrlich per
Cron.

Die Uebersicht blendet auf schmalen Schirmen Spalten aus, statt sie
wegzuschieben - auf dem Handy waren die Knoepfe zum Pausieren und Loeschen
sonst nur per Seitwaertsscrollen erreichbar.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 19:21:07 +02:00
adminandClaude Opus 5 eee3a89b4e AutoAction-Editor: Speichern, Laden und Loeschen
Der Editor las seine Geraete aus homeMesh, schrieb aber nach solarLog in
Tabellen, deren Fremdschluessel auf solarLog.actors/sensors zeigten. Jedes
INSERT lief damit gegen den Fremdschluessel - autoactionsSensors und
autoactionsActors sind deshalb leer geblieben, und die Aktor-Schleife war
ohnehin nie geschrieben worden.

Die Automatiken liegen jetzt in homeMesh neben dem Geraetemodell
(homeMesh_automations.sql). Eine Bedingung verweist mit einem einzigen
Fremdschluessel auf actor_states statt auf Geraet, State-URL und ein
unklares valID; Kommandoparameter stehen zeilenweise statt in vier festen
Spalten "Wert 1" bis "Wert 4". Verknuepft wird ueber group_no: gleiche
Nummer UND, verschiedene ODER, ausgewertet als any(all(gruppe)).

Im Editor ist eine Gruppe ein gerahmter Block mit eigenem "+ Bedingung",
dazwischen steht ODER - die Klammerung ist damit gezeichnet und nicht
vereinbart, und darunter steht derselbe Satz noch einmal in Worten. Die
Uebersicht zeigt ihn in der Spalte "Ausloeser" und ersetzt die bisher fest
verdrahtete Beispielzeile.

Nebenbei behoben: die Operator-Knoepfe schickten ihr innerHTML ("≠",
"&gt;") an eine Whitelist, die "!=" erwartete - jede Bedingung "ungleich"
wurde still zu "gleich". Ausgeblendete Wertfelder sendeten ihren Inhalt
trotzdem mit. Beides entfaellt, weil der Editor jetzt JSON aus einem
Modell schickt statt durchnummerierter Formularfelder.

Die vier Endpunkte fillSensorDD, fillActorDD, sensorDetails und
actorDetails entfallen: der Geraetekatalog reist einmal mit dem Editor
mit, statt je Bedingungszeile zwei Anfragen nachzuladen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 19:07:29 +02:00
adminandClaude Opus 5 fd27fe168e Handy-Darstellung: Diagramme, Meteogramm und Abstaende
Auf dem Handy war die Seite bisher nur mit der Desktopversion des Browsers
brauchbar. Drei Ursachen, alle nachgemessen bei 375 px:

Die Diagramme standen im Hochformat, 301 x 400 px - die Zeitachse lag auf
der kurzen Seite. Die Hoehe kam nicht aus dem Container: .chart-container
hatte inline height:100% in einem Elternelement ohne Hoehe, und die Canvas
trugen height="400px;", was kein gueltiger Zahlenwert ist. Die Hoehe steht
jetzt im Stylesheet, 400 px am Rechner und 250 px unter 768 px Breite, die
Beschriftung dort 8 statt 10 px.

Das Meteogramm hat sich nicht angepasst, sondern gequetscht. meteoblue legt
sein Bild einmal beim Laden fest und zeichnet es bei einer Breitenaenderung
nicht neu; unter etwa 600 px ueberlagern sich Wochentage und Stundenachse.
Es wird jetzt in mindestens 640 px Breite geladen - auf breiten Karten
gleich in Kartenbreite, dort aendert sich nichts - und, wenn die Karte
schmaler ist, als Ganzes verkleinert. Zuschnitt und Skalierung setzt das
Skript direkt am Element, nicht ueber das Stylesheet: sonst haengt die
Darstellung an einer womoeglich veralteten solar.css im Browser-Cache, und
der breit geladene Rahmen ragt ungeschnitten aus der Karte.

Passend dazu bekommt solar.css eine Versionskennung aus filemtime(), wie sie
die Skripte in footer.php und adminlte.min.css laengst tragen. Ohne sie kam
auf schon einmal besuchten Geraeten die alte Datei aus dem Cache.

Die Statistik-Kacheln lagen in nackten .col ohne Breitenangabe; Bootstrap
laesst sie dann nach Textlaenge umbrechen, mal zwei, mal drei je Zeile und
unterschiedlich breit. Die Breite kommt jetzt aus row-cols-2/row-cols-md-4,
und die Karte fuellt die Zeilenhoehe, damit die Werte trotz ein- oder
zweizeiliger Titel auf einer Linie stehen.

Zuletzt sind die Abstaende unter 768 px enger gefasst - auf .app-content
beschraenkt, damit Seitenleiste, Kopfzeile und Modals unberuehrt bleiben:
Spaltenabstand 24 -> 8 px, Kartenabstand 24 -> 12 px, Polsterung 16 -> 8 px.
Die nutzbare Breite steigt damit von 301 auf 317 px.

Geprueft bei 375 und 1200 px auf Solar, Historie, Heizung, Home und Wetter:
kein Querlauf, am Rechner unveraenderte Werte.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 23:05:33 +02:00
adminandClaude Opus 5 6aac3326a9 Bewaesserungs-Modal: Tagessummen aus WateringToday und LastWateringDay
Die Ventilsteuerung veroeffentlicht neuerdings je Steuerung zwei weitere
Topics: WateringToday mit der heute schon gelaufenen Zeit je Ventil und
LastWateringDay mit derselben Summe fuer den letzten Tag, an dem ueberhaupt
bewaessert wurde. Beide tauchen als zweite Zeile in der Zonenkarte auf:
"Heute 35m - Vortag 30m".

WateringToday wird gegen das heutige Datum geprueft. Die Nachricht ist
retained; nach einem Tageswechsel ohne Bewaesserung stuende sonst die Summe
von gestern als heutige da. Passt der Tag nicht, zeigt die Karte "--" und
nennt den Stand im Titel. Die Spalte heisst "Vortag" und nicht "Gestern",
weil LastWateringDay einen eigenen wateringDay mitfuehrt - es ist der letzte
Tag mit Bewaesserung, nicht zwangslaeufig der gestrige; das Datum steht im
Titel.

Geprueft: leeres Topic, Nutzlast mit 0 Sekunden, gefuellte Werte, veraltete
Nutzlast, fehlendes Feld, kaputtes JSON, Datumsformat - zehn Faelle, keine
Abweichung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 22:48:25 +02:00
adminandClaude Opus 5 eca5c8f041 Bewaesserungs-Modal: Zonenkarten statt Tabelle
Das Modal ist 500 px breit (modal-dialog ohne Groessenklasse). Die Tabelle
mit fuenf Spalten samt Zahlenfeld war darin schon eng; jede weitere Angabe
haette sie gesprengt. Statt der Tabelle bekommt jede Zone eine eigene Karte
mit Zustandsschild, Steuerzeile und den Zeiten darunter, die Automatik eine
eigene Karte darueber - dort sitzt auch die kombinierte Automatik, die zwei
Zonen bedient.

Die Automatik-Karte zeigt zusaetzlich, was auto_watering.py je Zone vorhat,
und begruendet es: "heute geplant - nur 0,2 mm Regen in 2 Tagen". Die Zahlen
stammen aus dem RainStatus-Topic, das die Seite ohnehin schon abonniert hat,
bisher aber nur fuer das Uhr-Symbol im SVG benutzte. Die genaue Regel steht
im Titel der Zeile, weil sich die Schwellen je Zone deutlich unterscheiden.
Die letzte Bewaesserung bleibt in der Zonenkarte, sie ist eine Eigenschaft
der Zone; die Automatik nennt sie nur als Begruendung.

Nebenbei sind die drei getrennten Zuordnungstabellen (wateringZones,
wateringIndicators, wateringHistoryRows) zu einer verschmolzen, die Ventil,
Automatik-Zone, Beschriftung und die SVG-Anzeige-Ids traegt. Damit wird aus
updateWateringStatus eine Schleife statt dreier, und die doppelt vorhandene
Knopflogik faellt weg. Alle 18 Zuordnungen und die Historienzeilen sind
gegen den vorherigen Stand geprueft, ohne Abweichung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 22:47:50 +02:00
adminandClaude Opus 5 768520cedd Geraete-Endpunkte: Anmeldung voraussetzen
actorDetails.php, sensorDetails.php, fillActorDD.php und fillSensorDD.php
fragten die homeMesh-Datenbank ab und gaben Geraetenamen samt Steuer-URLs
(command_url, command_parameters.url) aus - ohne jede Zugriffspruefung.

Alle vier pruefen jetzt checkLogin(), bevor sie etwas ausgeben. Aufgerufen
werden sie ausschliesslich aus js/solar/autoActionFuncs.js auf der Home-Seite,
also aus einer angemeldeten Sitzung; der Regeleditor funktioniert unveraendert.

Die SQL war bereits sauber (intval), es fehlte nur die Autorisierung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 15:37:49 +02:00
adminandClaude Opus 5 0075f803d0 tahoma.php: Anmeldung voraussetzen
Die Datei hatte keinerlei Zugriffspruefung. Damit liessen sich von beliebiger
Stelle die Somfy-Geraete auflisten (?action=devlist) sowie Rollladen abfragen
und verfahren (?action=move&device=...&pos=...&angle=...). Das Tahoma-Token
liegt serverseitig, ein Angreifer brauchte es also nicht.

checkLogin() ist jetzt vorgeschaltet, dafuer wird helper.php eingebunden. Im
Frontend gibt es derzeit keinen Aufrufer der Datei, ein bestehender Ablauf
kann also nicht brechen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 15:37:48 +02:00
adminandClaude Opus 5 464c08f414 carEG.php: Befehlseinschleusung ueber das Zeitfeld schliessen
$_POST["ftt"] wurde nur auf "nicht leer" geprueft und dann roh in die
exec()-Kommandozeile gehaengt. Ein Wert wie "12:00 2>&1; befehl #" fuehrte
damit beliebige Shell-Befehle als Web-Benutzer aus.

Das Formularfeld ist ein <input type="time">, liefert also HH:MM. Der Wert
wird jetzt gegen genau dieses Format geprueft; alles andere gilt als "keine
Ladeplanung" und nimmt den vorhandenen else-Zweig. Zusaetzlich gehen ftt und
setTime durch escapeshellarg().

fte und evAmp waren durch die Vergleichs-Klemmung bereits faktisch numerisch,
laufen jetzt aber ueber intval() und eigene Variablen statt ueber $_POST.
Fuer gueltige Eingaben ist die erzeugte Kommandozeile unveraendert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 15:35:59 +02:00
adminandClaude Opus 5 64445e95fe Zugangsdaten nur noch aus restricted/mysql.php, Datei nicht mehr versioniert
authServer.php und ajax/getForecastData.php hatten Server, Benutzer, Passwort
und Datenbank noch einmal fest eingetragen und damit die Werte ueberschrieben,
die restricted/mysql.php ohnehin liefert. Beide Dateien binden die Konfiguration
bereits ein; die Doppelung ist entfallen.

getForecastData.php benutzt jetzt $mysql_solarUser/-Pass/-DB wie die uebrigen
Endpunkte, statt dieselben Werte unter den generischen Namen zu setzen.

restricted/mysql.php steht jetzt in .gitignore, restricted/mysql.php.example
dient als Vorlage fuer neue Arbeitskopien.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 11:50:45 +02:00
adminandClaude Opus 5 6b8556567b Dashboard: Doppelungen zusammengelegt, Lade- und Renderlast gesenkt
Zusammenlegung
- Die sechs Endpunkte getConsData_/getProdData_{month,year,decade}.php sind
  ajax/energyHistory.php?series=&range= gewichen, der gemeinsame Aufbau der
  Chart-Daten steht in ajax/chartData.php. Die erzeugte SQL wurde gegen alle
  sechs Originale abgeglichen.
- Die 13 Raeume der Home-Ansicht stehen nur noch in restricted/rooms.php.
  home.php rendert die SVG-Kacheln daraus, homeMQTT.js bekommt dieselbe
  Tabelle als JSON (346 -> 222 bzw. 428 -> 197 Zeilen).
- Gemeinsame JS-Helfer in js/solar/common.js; geteilte Bausteine fuer
  Wetterkacheln, Chart-Skripte und das Formular der Ladesteuerung.
- Seitenvorlagen sind jetzt .php und werden eingebunden, statt per
  file_get_contents zusammengesetzt zu werden.

Fehlerbehebungen
- ?action=weather fuehrte auf eine leere Seite, es gibt jetzt weather.php.
- homeMQTT.js griff auf ein #meteogram zu, das home.php nie hatte, und brach
  damit den readystatechange-Handler ab.
- Die Heizungsseite abonnierte weatherStation/#, zeigte die Werte aber nie an.

Ladelast
- Chart.js, MQTT und Meteogramm werden nur noch auf den Seiten geladen, die
  sie brauchen; die Seitentabelle dafuer steht in index.php.
- mqtt.js durch die minifizierte Fassung ersetzt (859 -> 359 KB). MQTT.js
  5.14.1, byte-identisch mit dem signierten npm-Tarball.
- Rund 3,1 MB ungenutzte Vendor-Reste und AdminLTE-Demobilder entfernt.
- Solar 1796 -> 1240 KB, Home 1796 -> 873 KB, Historie 1796 -> 843 KB.

Speicher im Browser
- Chart.defaults.devicePixelRatio auf 1.5 gedeckelt. Die Zeichenpuffer der
  Diagramme waren mit 20-45 MB der groesste Posten der Seite.
- Das Meteogramm-iframe laedt erst beim Hinscrollen.

Realtime-SVG
- Filterbereiche auf das noetige Mass verkleinert und den wirkungslosen
  feOffset-Durchgang entfernt: 60 % weniger gerasterte Filterflaeche bei
  pixelgleichem Ergebnis.
- Die Fluss-Animation laeuft ueber transform statt stroke-dashoffset, mit
  Punkten statt Strichmuster; sie ruht, wo kein Fluss anliegt.
- Der Wertupdate schreibt nur noch bei echter Aenderung und benutzt
  textContent statt innerHTML: 9 statt 86 Elemente pro Update, davon keines
  mit Filter. describeArc rundet auf volle Grad.
- waterInfo auf scale(0.90) wie die uebrigen Info-Gruppen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 22:54:28 +02:00
adminandClaude Sonnet 5 29f752eafe Regenbasierte automatische Gartenbewässerung + homeMesh-Anbindung für AutoAction-Regeleditor
- Neues stündliches Python-Script (restricted/gartenbewaesserung/auto_watering.py): holt Niederschlags-
  und Sonnenuntergangsdaten von Open-Meteo, wertet je Zone (Hochbeete/Tröge/Garten vorn) individuelle
  Regen-Schwellenwerte und Mindest-Bewässerungsdauern aus und startet bei Bedarf per MQTT den passenden
  Automatik-Modus der Ventilsteuerung im Zeitraum vor Sonnenuntergang; veröffentlicht den Regen-Status
  zusätzlich stündlich als retained MQTT-Nachricht je Zone.
- Web-UI: neue Bewässerungs-Steuerung/-Anzeige (ajax/watering.php, js/solar/solarMQTT.js,
  assets/img/realtime.svg) inkl. Live-Status-Icons (aus/geplant/pausiert/aktiv per Farbe und Symbol).
- AutoAction-Regeleditor (ajax/actorDetails.php, sensorDetails.php, fillActorDD.php, fillSensorDD.php,
  tahoma.php, AutoAction.php) liest Actor-/Sensor-Parameter jetzt live aus der homeMesh-Datenbank statt
  aus stark vereinfachten Altfeldern, inkl. zugehöriger Anpassungen am Device-Discovery-Tooling
  (restricted/deviceDiscovery/*, neues logic_module.py).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 20:19:27 +02:00
admin ae455dba20 modify filemode for linux 2026-02-14 20:08:34 +01:00
admin 0e78302640 Initial commit 2026-02-14 19:47:21 +01:00