Gegenstueck zur Runner-Seite. Der Editor selbst braucht nichts: das
gerechnete Geraet "Automatiken" fuehrt jede Automatik als Messwert, und
der Geraetewaehler listet ohnehin Geraete und deren Messwerte. Neu sind
nur die Operatoren des Datentyps `elapsed` ("+", "ab +", "vor +") und
sein Eingabefeld.
Drei Stellen wissen trotzdem davon:
pruefeKreis() lehnt beim Speichern ab, was sich mittelbar selbst
ausloesen wuerde. Im Betrieb waere ein Kreis kaum zu bemerken - die
Sperrzeit begrenzt ihn auf eine Ausloesung je lockout_secs, und im
Protokoll sieht das aus wie eine Automatik, die halt oft laeuft.
deleteAutomation() fragt vorher. fk_cond_state steht auf ON DELETE
CASCADE, mit dem Messwert verschwaende also still die Bedingung des
Nachfolgers: aus "zehn Minuten nach dem Wecker UND es ist hell" wuerde
ein blosses "es ist hell", und der Rollladen fuehre ab morgen jeden Tag
bei Sonnenaufgang hoch. Bei der letzten Bedingung faengt der Runner das
ab, bei einer von zweien niemand. Zur Wahl stehen Abhaengen (Bedingung
raus, Nachfolger pausiert) und Mitloeschen; automatisch mitzuloeschen
waere die falsche Vorgabe.
Die Uebersicht rueckt Nachfolger unter ihren Ausloeser ein und zeigt sie
eingeklappt. Das ist nur die Darstellung - jeder Nachfolger bleibt eine
vollwertige Automatik mit eigener Pause, eigenen Rahmenbedingungen und
eigener Zeile im Protokoll. Sobald "gruppiert" auch "gehoert dazu"
hiesse, waere zu klaeren, wem die Pause gehoert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ganz selten liegt waehrend der Fahrt eine Bananenschale auf der Strasse, und
der Wagen kommt kurz ins Schleudern - erst bricht er nach rechts aus, dann
faengt ihn das Gegenlenken, dann trudelt er aus.
Der Vorlauf der zweiten Bewegung ist gerechnet, nicht geschaetzt: die Schale
startet 40 Einheiten ueber dem Bild, die Vorderachse liegt bei 97, macht 137
Einheiten - bei 437 Einheiten je Sekunde sind das 0,31 s. Genau dann setzt
das Schleudern ein.
Damit das Fahrzeug sich drehen kann, ohne die Strasse mitzunehmen, liegt es
jetzt in einer eigenen Gruppe (#wagen); die Schale sitzt im Markup davor und
verschwindet unter der Karosserie, sobald der Wagen darueberrollt.
Ausgeloest wird alle 20 Sekunden mit zwei Prozent Wahrscheinlichkeit und nur
waehrend der Fahrt - im Mittel einmal in gut sechzehn Minuten. Selten genug,
dass man es nicht erwartet; haeufig genug, dass es irgendwann jemand sieht.
Wer Bewegungen abbestellt hat, bekommt den Gruss gar nicht erst: ein
schleuderndes Auto ist genau das, was damit gemeint ist. Haelt der Wagen
mitten im Ausrutscher an, wird die Schale eingesammelt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ob das Auto gerade rollt, stand bisher nur klein unter der Karte
("unterwegs - zuletzt: ..."). Im Bild selbst, wo man zuerst hinsieht, war es
nicht zu erkennen.
Waehrend der Fahrt liegt jetzt eine Strasse unter dem Wagen: dunkler Belag,
gestrichelte Markierungen in den Randstreifen neben den Raedern, zwei Reihen
Baumkronen, die aussen vorbeiziehen. Dazu vier Windstreifen - zwei neben dem
Wagen, zwei ueber ihn hinweg, heller und duenner, damit sie wie Luft wirken
und nicht wie ein Riss im Lack.
Beide Schleifen treffen sich selbst: die Striche wandern um eine
Strich-Lueckenlaenge (46), die Baumreihen um ihren Abstand (160) - danach
steht das Muster wieder wie zuvor, und man sieht keinen Sprung. Markierungen
und Baeume laufen mit derselben Geschwindigkeit, der Wind etwa anderthalbmal
so schnell; groessere Unterschiede lassen die Bewegungen auseinanderfallen.
Dazu ein Abzeichen "unterwegs" im Kopf der Kachel - fuer Vorleseprogramme und
fuer alle, die Bewegungen abgeschaltet haben; dann steht die Landschaft still
(prefers-reduced-motion).
Der Zustand kommt aus park_state der juengsten Zeile; der Broker kennt ihn
nicht, unter solarManager/ev* stehen nur Ladestand, Reichweite und Ladung.
Unterwegs fragt der Sammler alle vier Minuten, die Seite laedt jede Minute
nach - die Anzeige hinkt also um wenige Minuten hinterher.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ein Zaehler zaehlt in der Richtung, in der er eingebaut ist - einer der
beiden Shelly-Zaehler meldet deshalb mit umgekehrtem Vorzeichen, und auf der
Kachel stand der Verbrauch als Minusleistung.
Neu gibt es dafuer den Schalter "negativ" an jedem Kachelwert. Er sitzt in
der Anzeige und nicht am Zaehler: Automatiken und Statistik lesen dieselbe
Nachricht und rechnen mit dem Vorzeichen, das dort steht - wer es an der
Quelle drehte, verschoebe die Rechnung an einer Stelle, an der niemand
danach sucht.
Gedreht wird vor dem Formatieren, also auch vor der Skalierung zwischen W
und kW und vor den Nachkommastellen. Aus -0 wird wieder 0. Was keine Zahl
ist, bleibt unangetastet und die Kachel zeigt weiter ihren Strich.
In der eingeklappten Kurzfassung steht das Minus vor dem Namen, damit man
ohne Aufklappen sieht, welcher Wert gedreht ist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fuer die Symbole vor den Zahlen standen drei Codepunkte von Hand in
rooms.php, und die Maske bot genau diese drei an. Jedes weitere Symbol waere
eine Codeaenderung gewesen.
Die Codepunkte stehen aber schon im Haus: css/bootstrap-icons.min.css fuehrt
sie unter .bi-<name>::before - dieselbe Quelle, aus der auch der Browser
liest. bootstrapIcons() liest sie von dort (2078 Stueck, einmal je Anfrage),
kachelIcon() schlaegt darin nach. Damit ist jedes Symbol moeglich, und ein
Versionswechsel der Icons zieht von selbst mit.
In der Maske steht deshalb kein Auswahlfeld mehr, sondern der Name mit einer
Vorschau davor - "moisture" sagt nichts, das Symbol daneben schon. Die
Vorschlagsliste beginnt mit gut dreissig ueblichen Symbolen unter deutscher
Beschriftung (Temperatur, Feuchte, Leistung, Wallbox, Regen ...), danach folgt
der ganze Rest alphabetisch. Ein Name, den es nicht gibt, zeigt sofort ein
rotes Fragezeichen und wird beim Speichern mit Begruendung abgelehnt.
In der eingeklappten Kurzfassung einer Kachel steht das Symbol jetzt mit -
es ist das schnellste Erkennungszeichen dafuer, welcher der drei Werte
gemeint ist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Tafel stand in einem Kasten mit fester Hoehe und eigenem Balken - in einer
Karte, die selbst in einer scrollenden Seite steht. Beim Wischen wusste man
nie, welcher der beiden Balken gerade dran ist. Jetzt ist die Tafel so lang,
wie sie ist, und die Seite scrollt als Ganzes.
Der Tabellenkopf klebt weiterhin oben: gescrollt wird bei AdminLTE in
.app-main, dort sitzt oben die Navigation, und daran haelt er sich fest. Dafuer
muss die Tafel selbst frei von overflow bleiben - sonst waere sie wieder ein
eigener Scroll-Kasten, und der Kopf hinge an ihr statt an der Seite.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Einstellungen bestanden aus einer einzigen langen Spalte mit den
Preistabellen. Sie bekommt Reiter, und darin zwei neue Flaechen: was die
Kacheln der Startseite anzeigen, und welche Geraete es ueberhaupt gibt.
Reiter gab es vorher dreimal in drei Ausfuehrungen - die Etagen ueber den
Automatismen mit eigenem switchTab(), die Geraetegruppen im Raum-Modal mit
eigener CSS-Klasse, und die Einstellungsseite noch einmal anders. Jetzt baut
restricted/reiter.php alle drei; umgeschaltet wird von Bootstrap, den offenen
Reiter merkt reiterMerken() in der Adresse.
Kachelwerte: was auf einer Kachel steht, stand fest in rooms.php. Die neue
Tabelle kachelWerte (solarLog_kacheln.sql) ueberschreibt das je Raum, die
Maske waehlt die Messwerte aus demselben Katalog wie der Automatik-Editor -
nach Etage sortiert, die Geraete des eigenen Raums oben. Fehlt die Tabelle,
zeigt jede Kachel weiter ihre Vorgabe.
Geraete: die Zuordnung Geraet -> Raum war ein Modal, erreichbar ueber ein
Zahnrad neben den Automatismen. Sie ist eine Einstellung und steht jetzt dort,
zusammen mit dem, was fehlte: seit wann ein Geraet nichts mehr meldet, in
welchen Automatiken es steckt, und ein Loeschknopf. Letzterer ist noetig, weil
der Suchlauf nichts loescht - er legt an und frischt auf, damit die Automatiken
ihre Zustands-Ids behalten; ein abgebautes Geraet bliebe sonst fuer immer
stehen. Geloescht wird nur, was in keiner Automatik vorkommt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drei Teile, die zusammengehoeren: die Fahrzeugkachel zeigt den Ladestand
jetzt dort, wo er im Auto auch sitzt, die Jahresstatistik wird zur
Tabelle, und beide holen ihr Aussehen aus einer gemeinsamen Farbwelt.
Fahrzeugkachel. Die Kachel "Batterie und Ladung" ist aufgeloest; ihr
Inhalt steckt in der Draufsicht. Die Akkuplatte im Fahrzeugboden fuellt
sich von hinten nach vorn, traegt die Zielmarke und nennt Prozent und
Reichweite in ihrer Mitte - auf dem Goldenen Schnitt der Plattenhoehe,
damit sie nicht am Klimaknopf klebt. Beim Laden wandern Streifen im
gefuellten Teil nach vorn, und vom Anschluss fliesst eine Linie in die
Platte. Was sich nicht zeichnen laesst - Ladeleistung, Restzeit,
Kilometerstand, Klima, Herkunft des Ladestroms - steht als Kennzahl
daneben, dazu die gefahrenen Kilometer aus Woche, Monat und Jahr
(?was=strecke, gerechnet aus dem Zaehlerstand statt aus erkannten
Fahrten - faellt eine Zeile aus, fehlt die Fahrt in der Liste, im
Zaehlerstand aber nicht).
Am Fahrzeug selbst: kein Panoramadach, das hat unseres nicht. Reling und
Spoiler neu gezeichnet, weil sie mit der Dachplatte bisher ein
geschlossenes Rechteck bildeten und das wie ein Fensterrahmen aussah. Die
Blende am Bug ist schmaler und heller - von oben sieht man von einer
senkrechten Flaeche nur einen Streifen, und fast schwarz las sie sich wie
ein Loch. Die Ladeklappe ist gebaut wie der Klimaknopf; den Ladezustand
traegt jetzt die Farbe statt einer zweiten Form.
Jahresvergleich. getStats.php liefert kein fertiges HTML mehr, sondern
JSON, das sich selbst beschreibt: Einheit, Nachkommastellen, Richtung und
Zugehoerigkeit stehen als Metadaten je Kennzahl, nicht mehr als zwei
Arrays, die ueber den Spaltenindex mit der Abfrage synchron gehalten
werden mussten. Zwei Abfragen holen alles - Monatswerte, daraus die
ganzen Jahre und die Summe seit Aufzeichnungsbeginn, dazu der gleiche
Zeitraum jedes Jahres bis zum heutigen Kalendertag. Verhaeltniszahlen
sind auf jeder Ebene neu aus den Summen gerechnet; ein Jahresmittel ist
nicht das Mittel der Monatsmittel.
Gezeichnet wird das von einem Baustein, den sich Historien- und
Solarseite teilen: dort drei Jahresspalten mit verschiebbarem Fenster bis
zurueck zum ersten Jahr, hier eine Spalte ohne Werkzeuge. Zeilen, die
sich aufteilen, stehen als "davon" darunter; was bereits in einer anderen
Zeile steckt, als "darin" samt Namen der Zeile - der Beitrag der Batterie
und die Solarladung des Autos duerfen nicht zum Ertrag addiert werden.
Zusammengefasste Zeilen sind zunaechst zu, damit die Tabelle mit zehn
Zeilen aufmacht statt mit siebzehn.
Farbwelt. Statt Regel fuer Regel zu ueberschreiben, ist die Palette
ausgetauscht: ein paar Variablen am Wurzelelement, an die Bootstrap und
AdminLTE ohnehin gehen. Anthrazit als Grund, eine Spur hellere Flaeche
fuer Karten, Haarlinien statt Rahmen und Schatten, Bernstein als einzige
Akzentfarbe - auf dieser Seite die Farbe der eigenen Anlage. Gruen, Rot
und Gelb bleiben den Zustaenden vorbehalten.
Erklaerungen, die bisher nur im title standen, sind antippbar: am Finger
gibt es kein Ueberfahren, und ein title erscheint dort nie.
Die beiden neuen Abfragen sind gegen solarLog gelaufen, die Monatswerte
decken sich mit einer unabhaengigen Zerlegung. grundpreisImJahr() ruft
jetzt grundpreisImZeitraum() auf - dieselbe Rechnung, nur auch fuer einen
Teil des Jahres.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Zwei Sachen, beide an der Anlage aufgefallen.
Abgebrochen wurde auf "core:MovingState ist false". Das ist nicht
dasselbe wie "steht": die Box meldet das Ende der Fahrt, bevor Hoehe und
Neigung darauf nachgezogen haben, und der festgehaltene Wert war dann der
von kurz davor. Statt einer festen Wartezeit zaehlt jetzt Stabilitaet -
fertig ist es, wenn zwei Abfragen hintereinander dasselbe sagen. Damit
faellt auch die Bestaetigungsrunde weg, sie war genau dieser Gedanke in
schlechter.
Der Umweg der Kugelschreiber-Mechanik hing am Neigungswert (ueber 30 %).
Er gehoert an die Hoehe: eine Hoehenfahrt rastet die Lamellen um, danach
muss die Neigung ueber 0 % wieder angefahren werden - bei jedem Winkel,
nicht erst bei grossen. Wird dagegen nur die Neigung verstellt, steht die
Mechanik schon richtig und es geht direkt. NEIGUNG_DIREKT_MAX faellt
damit weg.
Der Hoehenregler im Raum-Modal schickt deshalb jetzt das kombinierte
Kommando mit der Stellung des Neigungsreglers daneben - ohne
Neigungsziel gaebe es nichts, was der Umweg anfahren koennte.
Geprueft an der Wozi-Schiebetuer: Hoehe von 48 auf 60 bei Neigung 76, und
die 76 standen danach wieder da. Vorher htte die Mechanik sie auf einen
beliebigen Wert umgerastet. Dazu elf Faelle im Browser und sieben gegen
transports.py, darunter "Position+Neigung 10" (Umweg) gegen "nur Neigung
80" (direkt).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Wer eine Jalousie faehrt, das Modal zumacht und gleich wieder aufmacht,
sah weiter den alten Stand - und zwar dauerhaft. Zwei Gruende, die sich
uebereinandergelegt haben: die Box meldet die neue Hoehe erst am Ende der
Fahrt (gemessen rund zwanzig Sekunden), und das Modal zeichnet sich nur
beim Oeffnen. Was in diesen zwanzig Sekunden aufgemacht wurde, blieb
falsch stehen, bis jemand es noch einmal aufmachte.
Beim Oeffnen wird jetzt jede Jalousie des Raums einmal an der Box
nachgefragt. Faehrt sie gerade, bleibt die Verfolgung dran, bis sie steht.
Ohne Auftrag gibt es keinen Vorlauf abzuwarten und nichts nachzureichen,
deshalb genau eine Abfrage je Jalousie - drei fuer das Wohnzimmer.
Damit stimmt die Anzeige in beiden Richtungen: der Server schreibt nach
jedem Fahrbefehl nach (67c2a77), das Modal holt sich beim Aufmachen den
Stand.
Geprueft an der Wozi-Schiebetuer, genau der gemeldete Ablauf: Regler auf
10, Modal nach 1,2 s zu, nach weiteren 0,6 s wieder auf. Es zeigte 60,
verfolgte weiter und sprang nach 28 Sekunden auf 10. Ein ruhiges Oeffnen
kostet drei Abfragen fuer drei Jalousien. Dazu dreizehn Faelle gegen die
echte Datei.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Am echten Geraet ausprobiert - drei Sachen stimmten nicht.
Die PHP-Warnung. Nicht jeder Tahoma-Zustand ist eine Zahl oder ein Wort,
manche sind Listen. strval() darauf ist eine Warnung, und die stand mitten
in der JSON-Antwort. response.json() scheiterte, raumRequest() lieferte
{error}, die Verfolgung brach still ab - genau das Bild, das der Bericht
"kommt noch nicht" beschreibt. Nicht-Skalare werden jetzt uebergangen, und
ein Fehler wird gemeldet statt verschluckt.
Das Mitziehen waehrend der Fahrt. Gemessen: die Hoehe steht die ganzen
zwanzig Sekunden auf dem alten Wert und springt erst am Ende um. Wer das
anzeigte, holte den Regler vom eingestellten Ziel auf den alten Stand
zurueck, um ihn danach wieder vorzuschieben. Angezeigt wird deshalb erst,
was am Ende dasteht.
Das vorzeitige Aufhoeren. Zwischen den beiden Befehlen der
Kugelschreiber-Mechanik steht die Jalousie still und meldet die
Zwischenstellung. Die Verfolgung hielt das fuer das Ende und schrieb
"Neigung 0" fest, wo 62 gemeint war. Sie laeuft jetzt mindestens, bis der
Auftrag zurueck ist, und danach noch vierzehn Sekunden - so lange dauert
eine Neigungsfahrt von Anschlag zu Anschlag.
Gegengeprueft an der Wozi-Schiebetuer: Position 49 -> 20 -> 100 -> 49,
Neigung 62 -> 80 -> 62. Die Neigung wanderte beim Herunterfahren von
selbst auf 100 und stand sofort richtig im Modal. Waehrend des zehn
Sekunden langen Kugelschreiber-Auftrags liefen drei Verfolgungsanfragen
durch - die Sitzungssperre ist also wirklich weg.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
actor_states.current_value schrieb bisher nur der AutoAction-Runner fort,
und der fragt die Tahoma-Box alle fuenf Minuten ab (poll_tahoma). Nach
einem Fahrbefehl stand im Raum-Modal deshalb bis zu fuenf Minuten lang
der Stand von vor der Fahrt.
Das Modal verfolgt die Jalousie jetzt selbst: alle zwei Sekunden fragt es
ueber ajax/room.php?action=jalousie genau dieses eine Geraet, zieht die
beiden Regler und das Lamellenbild nach und hoert auf, sobald die Box
keinen Fahrbefehl mehr meldet. Der gelesene Stand landet dabei gleich in
actor_states, damit auch das naechste Oeffnen und der Automatik-Editor
ihn haben. Wer gerade selbst am Schieber zieht, behaelt ihn.
Drei Dinge mussten dafuer aus dem Weg:
- Die Sitzung. helper.php startet sie und schliesst sie nie, PHP sperrt
ihre Datei bis zum Ende der Anfrage. Ein "Zu" wartet ueber eine Minute
auf das Ende der Fahrt - solange stand jede weitere Anfrage desselben
Browsers still. ajax/room.php gibt sie jetzt frei, sobald checkLogin()
durch ist; danach wird $_SESSION nur noch gelesen.
- Drei Abfragen je Warterunde. warteAufJalousie() holte MovingState,
Neigung und Position einzeln, obwohl eine Abfrage die ganze Liste
liefert. Jetzt eine.
- Kein Ort, an dem der frische Stand haengenbleibt. Dafuer
tahomaZustaende(), tahomaZustaendeSchreiben() und tahomaAktorUrl().
Wahrheitswerte werden dabei als "True"/"False" geschrieben - so macht es
der Runner mit str(), und zwei Schreibweisen fuer denselben Zustand
waeren schlimmer als eine schraege.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Wer im Editor die Etage aenderte, fand die Automatik danach unter beiden
Reitern. In der Datenbank stand sie nur einmal - saveAutomation() macht
bei bekannter id ein UPDATE samt floor, einen INSERT gibt es auf dem Weg
gar nicht. Nachgeladen wurde nach dem Speichern aber nur die Liste der
neuen Etage; die alte behielt ihre Zeile, bis jemand die Seite neu lud.
Der Editor merkt sich jetzt die Etage, mit der er aufgemacht wurde, und
holt bei einem Wechsel beide Listen neu. Ohne Wechsel bleibt es bei der
einen Anfrage.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Anzeigen im Realtime-SVG und im Bewaesserungs-Modal lasen ihre
Vorschau aus Gartenwasser/AutoWatering/<zone>/RainStatus, das
auto_watering.py stuendlich veroeffentlicht hat. Solange das so blieb,
konnte das Skript nicht abgeschaltet werden, obwohl die Entscheidung
laengst in den Automatiken des Runners steht - und dessen Schwellen
haetten dann neben denen im Skript gestanden, ohne dass ein Unterschied
auffaellt.
Statt des Plans zeigen die Anzeigen jetzt, ob die Zone heute schon
gelaufen ist. Die Regenautomatik loest zum Sonnenaufgang aus; wer
spaeter auf das Dashboard schaut, will ohnehin wissen, was passiert ist,
nicht was vorgesehen war. Die Angabe kommt aus dem retained
WateringToday der Ventilsteuerung, das die Karte schon fuer "Heute"
auswertet - eine Bewaesserung von Hand zaehlt damit genauso, was hier
richtig ist.
Im SVG steht dafuer ein Haekchen statt der Stoppuhr, das Schild im
Modal sagt "heute bewaessert" statt "heute geplant", und die
Planungszeilen in der Automatik-Karte entfallen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nicht nachgebaut, sondern powerToString() aus common.js benutzt. Sie macht
genau das schon, und die Solarseite formatiert dieselben Zahlen damit - eine
eigene Regel hier haette bedeutet, dass dieselbe Wallbox an zwei Stellen
verschieden gerundet dasteht. common.js wird im Fuss vor den seitenweisen
Skripten geladen, steht homeMQTT.js also zur Verfuegung.
Ersetzt wird nur das Trennzeichen: auf der Kachel steht ein schmales
Leerzeichen zwischen Zahl und Einheit, im Rest der Anwendung ein
gewoehnliches.
Betroffen sind Garage, Carport OG und Carport EG. Der Regen im Garten bleibt
bei Einheit und Nachkommastelle - Millimeter skalieren nicht.
Nebenbei: mein Einschub von wechselrichterWert() hatte den Kommentarblock
von kachelWert() von seiner Funktion getrennt, er stand seitdem ueber der
falschen. Zurueckgesetzt und um die beiden neuen Schluessel ergaenzt.
Geprueft mit dem echten Code im Browser: 0 -> "0 W", 999 -> "999 W",
1234 -> "1.23 kW", 12340 -> "12.3 kW"; die Summe der vier Carport-Hoymiles
ergibt 1.23 kW und laesst einen fuenften Wechselrichter richtigerweise
draussen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Das Rendering des Grundstuecks fuellt seinen Rahmen aus, die drei
Geschossbilder haben dagegen breiten schwarzen Rand - deshalb lagen die
Etagenschalter nur beim Aussengelaende auf dem Bild. Der Rand gehoert ins
Bild und nicht in home.php: dort einzurechnen haette auch OG, EG und UG
verschoben, und deren Kacheln sind auf die heutige Lage eingemessen. AG.png
bekommt links 114 durchsichtige Punkte, die sieben Kacheln sind mit
derselben Abbildung umgerechnet.
Die Schalter selbst waren ein duenner Ring in einem Amber (#aa7713), das in
css/solar.css nirgends vorkommt - und alle vier sahen immer gleich aus, die
aktive Etage war an ihnen nicht zu erkennen. Jetzt tragen sie dieselbe
Scheibe wie die Kacheln im Grundriss, die aktive bekommt die Akzentfarbe der
Schieberegler (#00788F), und switchFloor() zieht den Zustand mit. Dort
classList und nicht die Helfer addClass/removeClass weiter oben: deren
"el.className +=" wirkt auf SVG-Elementen nicht, className ist dort
schreibgeschuetzt.
Ausserdem sind sie kleiner. Mit halber Groesse beherrschten sie die linke
Seite, und das Bild musste umso weiter einruecken, um sie freizuhalten - bei
0,38 statt 0,50 bleibt mehr Plan uebrig. Die Groesse steht als eine Zahl in
home.php, Abstand und Bildrand folgen daraus.
Die Beschriftung sitzt jetzt ueber text-anchor und dominant-baseline im
Kreismittelpunkt. Vorher stand je Etage ein von Hand gesuchter Versatz im
Markup (62, 63, 64), und jede neue Etage haette einen neuen gebraucht.
Weggefallen: die Definition #floorBtn, die nur einmal benutzt wurde, und die
Animationen showOG/showEG/showUG, die ein bereits sichtbares Element von 0
auf 1 blendeten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Carport EG und Veranda OG zeigen jetzt ihre PV-Leistung. Der SolarManager
legt die Hoymiles unter solarManager/inverters0, /inverters1 und so fort ab
- die Nummer ist eine Position in einer Liste und sagt nichts darueber,
welches Dach gemeint ist. Ein fest eingetragenes "inverters3" waere
stillschweigend falsch, sobald sich die Reihenfolge aendert.
Deshalb sucht wechselrichterWert() ueber invertersN/name und summiert die
genannten auf. Das ist eine dritte Art, an einen Wert zu kommen - neben
Topic und JSON-Pfad -, aber sie bleibt auf einen Schluessel und eine
Funktion beschraenkt, und der gewoehnliche Weg ist unberuehrt. Findet sie
keinen der Namen, schreibt sie die vorhandenen in die Konsole; sonst stuende
nur ein Strich auf der Kachel und niemand wuesste, woran es liegt.
tileTopics() unterschied bisher nicht und machte aus dem fehlenden Topic ein
"/#" - eine Anmeldung auf einen leeren Zweig.
Dazu Symbole vor den Zahlen. "0 W" allein sagt nicht, ob geladen, erzeugt
oder verbraucht wird, und die Einheit beantwortet das nicht. Die Icons
kommen als Schrift und nicht als Pfad: css/bootstrap-icons.min.css ist
ohnehin eingebunden, und SVG-Text darf dieselbe Schriftfamilie benutzen wie
HTML. Die Codepunkte stehen in kachelIcon() mit dem Namen daneben,
abgeschrieben aus genau dieser CSS.
Wegen font-display:block bleiben die Symbole bis zum Laden der Schriftdatei
unsichtbar - die Werte stehen so lange ohnehin auf einem Strich.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
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>
Bisher stand ueber jedem Raum eine eigene Ueberschrift - bei neunzehn
Raeumen aus rooms.php also neunzehn Stueck, jede mit der Etage davor:
"OG - Bad", "EG - Bad", "UG - Bad". In einer Liste, die fuenfzehn Zeilen
hoch ist, sah man dadurch mehr Ueberschrift als Geraet.
Jetzt gruppiert nur die Etage, und der Raum steht klein und rechtsbuendig
an jeder Zeile. Sortiert wird innerhalb der Etage nach Raum (Reihenfolge
wie in rooms.php), sodass die Geraete eines Raums trotzdem beisammen
stehen; aus zwanzig Ueberschriften werden fuenf. Ein Raum, den es in
rooms.php nicht mehr gibt, bekommt eine eigene Ueberschrift, statt
stillschweigend unter den Unzugeordneten zu verschwinden.
Die Suche laeuft zusaetzlich ueber den Raum: "bad" findet damit auch den
Handtuchtrockner, der im Bad haengt, ohne es im Namen zu tragen. In der
Trefferliste steht keine Ueberschrift darueber, deshalb traegt dort jede
Zeile die volle Angabe "OG - Bad".
Dafuer nennt raumListe() Etage und Raum jetzt auch einzeln, und der
Geraetekatalog reicht beides durch - der Schluessel "Etage/Raum" muss so
nicht im JavaScript wieder auseinandergenommen werden.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
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>
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>
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>
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>
Die Regler standen neben den Fahrknoepfen - beim Antippen von "Auf" fand
der Daumen leicht den Schieber daneben. Jetzt stehen sie rechts aussen,
zwischen ihnen und den Knoepfen liegt das Fensterbild.
Das Bild zeigt jetzt beides. Die Hoehe schneidet ab: gezeichnet wird nur
der Teil des Fensters, den die Jalousie bedeckt, mit der Endschiene an
seiner Unterkante; darunter sieht man hinaus. Die Neigung bestimmt wie
bisher, wie dick eine Lamelle von vorn erscheint. Ein Rollladen ohne
Neigung bekommt volle Lamellenhoehe - er ist ja ein geschlossener Panzer.
Die Prozentzahl unter den Reglern ist weg; die Bahn ist dafuer von 5 auf
6,5 rem gewachsen. Was der Regler bewirkt, zeigt das Bild ohnehin besser
als eine Zahl.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der senkrechte Positionsregler liess sich am Handy kaum anfassen. Gebaut
war er mit writing-mode: vertical-rl - dem neueren Weg, aber einem, bei
dem der Browser das Ziehen als Wischen liest und die Seite blaettert,
statt den Griff mitzunehmen.
Jetzt liegt ein gewoehnlicher waagerechter Regler um 90 Grad gedreht in
seinem Kasten. Er bewegt sich in seinen eigenen Koordinaten waagerecht,
wird von jedem Browser unterstuetzt, traegt dieselbe Gestaltung wie die
liegenden Regler, und touch-action: none haelt die Seite still, solange
der Finger auf ihm liegt. Die Trefferflaeche ist 1,6 rem breit statt der
sichtbaren 0,375 rem Bahn. Nachgemessen: oben angeklickt 8 %, unten 93 %.
Die Neigung steht jetzt ebenfalls senkrecht, daneben das Lamellenbild -
Regler und Bild bilden ein Paar, das Bild sagt, was der Regler bedeutet.
Beide Spalten sind mit Namen und den Worten an ihren Enden beschriftet
("oben"/"unten", "auf"/"zu"); die Zahl darunter ist das, was sich beim
Ziehen aendert.
Die Regler im Raum-Modal haben ein eigenes Aussehen bekommen: Bootstraps
weisse Bahn mit blauem Griff stach auf den dunklen Karten heraus, und in
einem Raum mit drei Jalousien waren das sechs helle Balken. Bahn und Griff
tragen jetzt dasselbe Blaugruen wie der Temperaturregler, der ungefuellte
Teil ein Grau, das in beiden Themes traegt. Die Regler in den Modals fuer
Autoladung und Heizstab bleiben unberuehrt.
Die Regel fuer die Bahn steht zweimal da, einmal fuer -webkit- und einmal
fuer -moz-: als Liste notiert wirft der Browser beim unbekannten Selektor
die ganze Regel weg, und dann bliebe die Bahn ungestaltet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
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>
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>
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>
Die Gruppierung nach Geraeteart hat die Liste geordnet, aber nicht kuerzer
gemacht: elf Jalousien bleiben elf Zeilen. Statt der Auswahlliste steht
jetzt ein Knopf in der Zeile, der eine durchsuchbare Liste ueber die volle
Breite aufklappt.
Gesucht wird ueber Beschriftung UND Geraeteart zugleich, also quer zur
Gruppierung: "bad" findet die drei Thermostate im Bad und die zwei
Jalousien "Bad Links"/"Bad Rechts", "jalousie" alle elf auf einmal. Ohne
Suchtext bleiben die Zwischenueberschriften stehen; sobald gefiltert wird,
faellt die Gruppierung weg, weil die Treffer dann quer ueber alle Arten
stehen und die Art ohnehin an jeder Zeile mitlaeuft.
Die aufgeklappte Liste haengt unter der ganzen Zeile und nicht in ihr - in
einer input-group stuende sie sonst als weitere Spalte daneben. Escape
schliesst, Enter nimmt den ersten Treffer, ein Klick daneben schliesst
ebenfalls. Es ist immer hoechstens eine offen.
Dabei zwei Sachen mitgenommen:
Eine neue Zeile beginnt jetzt ohne Geraet ("Gerät wählen …"). Bisher stand
dort das erste Geraet mit seinem ersten Messwert - nach dem
Geraete-Discovery also "Bad Links: Gesperrt = ", eine Bedingung, die
niemand gemeint hat und die man erst wegklicken musste. Der Satz darunter
sagt in dem Fall "Noch kein Ausloeser", und der Server lehnt eine
unvollstaendige Bedingung ohnehin ab.
Auf dem Handy quetschten sich Geraet, Messwert, Vergleich, Wert, Einheit
und Papierkorb in eine Zeile - uebrig blieben "Therm OG Bad" und "te".
Unter 576 px bekommen Geraet und Messwert jetzt je eine eigene Zeile. Die
Regel braucht die input-group-Klasse im Selektor: solar.css wird vor
AdminLTE geladen, und Bootstraps ".input-group > .form-select" haette sonst
dasselbe Gewicht und wuerde als spaeteres Blatt gewinnen.
Geprueft bei 375 px und am Rechner.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nach dem Discovery-Lauf stehen 51 Geraete in der Ausloeser-Liste und 40 in
der Aktions-Liste, flach und alphabetisch. Dreizehn Thermostate und elf
Jalousien sind darin vierundzwanzig gleich aussehende Zeilen.
Die Liste ist jetzt nach Geraeteart gruppiert (optgroup, das koennen auch
die Handy-Browser nativ). Arten mit nur einem Geraet bekommen keine eigene
Ueberschrift - fuenfzehn Gruppen mit je einem Eintrag waeren
unuebersichtlicher als die flache Liste -, sie sammeln sich am Ende unter
"Einzelne".
Dazu ein Namensputz fuer die Anzeige, ohne die Datenbank anzufassen: das
u_-Praefix aus den Tahoma-Beschriftungen faellt weg, ebenso eine
angehaengte einstellige Kanalnummer, und Unterstriche werden zu Leerzeichen.
Aus "u_Wozi Terrassentuer" wird "Wozi Terrassentuer", aus "Power_EG_EM_0"
wird "Power EG EM". Bewusst nur die einstellige Endung - "go-eCharger_270003"
traegt seine Seriennummer und behaelt sie.
Beschriftungen, die danach doppelt waeren, bekommen die Geraeteart dazu: die
Tahoma-Box nennt sowohl den Rollladen als auch den Sonnenenergie-Sensor
"Terasse", in der Liste untereinander waren sie nicht zu unterscheiden.
Daraus wird "Terasse (Rollladen)" und "Terasse (Licht)".
Die Beschriftung entsteht an einer Stelle und wird von Editor und
Uebersicht geteilt - sonst stuende in der Auswahlliste "Wozi Terrassentuer"
und im Satz darunter weiter "u_Wozi Terrassentuer".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Manche Geraete schicken eine Zahl und meinen einen Zustand - der
go-eCharger etwa 2 fuer "Charging". Welche Zahl welchen Namen hat, steht im
value_template der Home-Assistant-Discovery als Werttabelle:
{{ ['Unknown','Idle','Charging','WaitCar','Complete','Error'][value_json|int] }}
Das ist kein Pfad in die Nutzlast, sondern eine Uebersetzung, und sie wurde
bisher verworfen. Jetzt landet sie in possible_values - in derselben
Schreibweise, die WLED fuer seine Effektliste schon benutzt, naemlich einer
Liste aus {Wert: Bezeichnung}. Damit versteht der Editor sie ohne
Zusatzarbeit und macht eine Auswahlliste daraus. Ein Versatz im Ausdruck
wandert in die Schluessel: aus [value_json|int-3] wird {"3":"Default"}.
Der Runner uebersetzt beim Lesen, eine Bedingung vergleicht also den
Klartext. Steht die Zahl nicht in der Tabelle, bleibt sie stehen - ein
erfundener Name waere schlimmer als ein roher Wert.
Dabei ist ein Folgefehler aufgefallen: Auswahlwerte in dieser Schreibweise
kamen im Editor als "[object Object]" an. Die alte addOptions kannte die
Form, der neue Modell-Aufbau nicht - aufgefallen ist es nie, weil WLED beim
Umbau abgeschaltet war. Der Katalog liefert Auswahlwerte jetzt einheitlich
als {value, label}, und zwar seitenrichtig:
Messwert angezeigt Charging, gespeichert Charging
(der Runner hat schon uebersetzt)
Parameter angezeigt Blink, gespeichert 1
(das Geraet will die Zahl)
Die Uebersicht loest die Bezeichnung ebenfalls auf: "Effekt (Blink)" statt
"Effekt (1)".
Nachgemessen: alle fuenf Werttabellen auf dem Broker richtig erkannt,
einschliesslich des Versatzes, kein Fehltreffer unter den 35 uebrigen
Vorlagen. Live liefert der go-eCharger jetzt Idle, Neutral, Auto, Eco und
None statt 1, 0, 0, 4, 0. Ein weiterer Discovery-Lauf aendert keine Zeile.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
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>
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>
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 ("≠",
">") 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>
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>
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>
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>
Die Funktion soll laut eigener Beschreibung den Zwischenspeicher verwerfen,
falls das SVG neu gezeichnet wird, leerte aber nur svgElements. letzterWert
blieb gefuellt, sodass setText, setAttr und setDisplay jeden Schreibzugriff
danach als "unveraendert" ueberspringen: die Knoten werden zwar neu
nachgeschlagen, aber nie beschrieben. Das frische SVG behaelt dann seine
Platzhalter, bis sich der jeweilige Messwert zufaellig aendert - bei den
Boegen mit auf volle Grad gerundeten Winkeln kann das dauern.
Nachgestellt: nach dem Verwerfen nur der Knoten bleibt die Anzeige auf "--",
mit geleertem letzterWert wird wieder geschrieben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Funktion bildete die uebergebene Breite mit Math.abs() ab. Die Aufrufer
rechnen 55*(1-exp(x)); kehrt sich das Vorzeichen der Messgroesse um, wird der
Ausdruck negativ. Frueher setzte das eine negative stroke-width und die Linie
blieb leer. Mit Math.abs() erschien stattdessen eine Animation voller Staerke
in unveraenderter Laufrichtung - das Diagramm zeigte einen Fluss, den es nicht
gab.
Beispiel: speist die EG-Etage mit 900 W zurueck, ergibt 55*(1-exp(900/2000))
den Wert -31,3. Math.abs machte daraus eine 31,3 breite laufende Linie, jetzt
sind es 0 und die Linie ruht.
Positive Breiten und die Schwelle von 0,5 bleiben unveraendert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
- 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>