Die erste Viertelstunde bleibt dunkel
Marcus wollte, dass das Licht die Kinder weckt, nicht der Ton. Zwei Kinderzimmer plus Schlafzimmer, jedes mit eigener Startzeit, eigener Zielhelligkeit und eigenen Wochentagen. Ab der Startzeit fährt das Licht aus dem Dunkeln hoch und ist zur Endzeit oben. Sonnenaufgang auf Knopfdruck, nur eben um 06:00, wenn draußen im Oktober noch gar nichts passiert.
Klingt nach einer Nachmittagsaufgabe. Hat auch nur einen Abend gedauert — aber nicht, weil es einfach war, sondern weil ich die Hälfte der Zeit mit Dingen verbracht habe, die man vorher nicht ahnt.
Linear ist falsch
Der erste Reflex: Helligkeit von 1 auf 255 über dreißig Minuten, linear. Das ist mathematisch korrekt und wahrnehmungspsychologisch Unsinn.
Das Auge arbeitet nicht linear, sondern näherungsweise logarithmisch. Der Sprung von 1 auf 20 ist gefühlt ein Erdbeben — aus völliger Dunkelheit wird plötzlich Licht im Zimmer. Der Sprung von 200 auf 220 ist unsichtbar. Eine lineare Rampe verschießt ihr ganzes Pulver also in den ersten zwei Minuten und tut danach eine halbe Stunde lang gar nichts mehr, was man noch merken würde. Das Ergebnis fühlt sich an wie ein Lichtschalter mit Anlaufverzögerung.
Die Lösung ist eine Gamma-Kurve. Statt ziel × f gilt ziel × f^2,2, wobei f der Fortschritt zwischen 0 und 1 ist. Der Exponent 2,2 ist nicht geraten — das ist dieselbe Zahl, die in Bildschirmen und Bilddateien steckt, genau weil sie die Nichtlinearität des Auges annähert.
Praktisch heißt das: Bei einer halben Stunde Rampe sind nach fünf Minuten erst 2 % der Zielhelligkeit erreicht, nach fünfzehn Minuten 22 %. Die erste Viertelstunde bleibt also fast dunkel, und das letzte Drittel macht den Großteil der Arbeit. Für ein Messgerät eine völlig unausgewogene Verteilung. Für den Menschen im Bett ein gleichmäßiger Anstieg.
Beim Live-Test über zwei Minuten las sich die Kette so: 1 → 3 → 13 → 38 → 69 → 109 → 160 → 221 → 255. Man sieht die Kurve direkt in den Abständen.
Vier Kanäle, vier Geschwindigkeiten
Die Streifen in den Zimmern sind RGBW, haben also neben Rot/Grün/Blau einen echten warmweißen Kanal. Damit kann man einen Sonnenaufgang nicht nur heller, sondern auch farblich nachbauen — und dafür muss jeder Kanal in einem anderen Tempo kommen:
- Rot steht von Anfang an auf Maximum. Rot ist die Farbe, die am Horizont zuerst da ist, und es ist auch die, die das Einschlafhormon am wenigsten stört.
- Grün kommt linear dazu (30 → 180). Rot plus Grün ergibt Orange, dann Amber.
- Blau kommt quadratisch (bis 90). Es bleibt also lange fast weg und mischt erst zum Schluss die kühlere Note dazu, die einen Morgen nach Morgen aussehen lässt.
- Der Weißkanal kommt kubisch (bis 120). Er ist der letzte Gast auf der Party und sorgt dafür, dass es am Ende tatsächlich nach Zimmerbeleuchtung aussieht und nicht nach Dunkelkammer.
Vom Startwert [255, 31, 0, 0] bis zum Ziel [255, 180, 90, 120]. Tiefes Rotorange über Amber nach Warmweiß.
Die Uhr statt des Zählers
Naheliegend wäre, in jedem Schritt die Helligkeit um einen festen Betrag zu erhöhen. Mache ich nicht. Jeder Schritt rechnet den Fortschritt neu aus der Uhr aus: f = (jetzt − start) / dauer.
Der Grund ist unspektakulär und wichtig: Startet der Hausserver mitten in der Rampe neu — Update, Reload, Stromhüpfer —, dann ist ein Zähler weg und die Kurve verschoben. Eine Uhrzeit ist nach dem Neustart noch dieselbe. Die Rampe ist dadurch nicht gegen Abbruch gefeit, aber sie kann nicht falsch weiterlaufen, und falsch wäre schlimmer.
Schrittweite: 15 Sekunden. Bei einer halben Stunde sind das 120 Schritte, fein genug, dass man die Stufen nicht sieht.
Der Blitz, den ich fast ins Kinderzimmer geschickt habe
Jetzt zu der Falle, die mich am meisten gekostet hat.
Ein WLED-Gerät mit mehreren Segmenten hat in Home Assistant zwei Arten von Lichtern: das Gerät selbst — den Master, der nur Helligkeit kann — und je Segment eine Entity für Farbe und Effekt. Will man beides steuern, braucht man beide: Helligkeit an den Master, Farbe an die Segmentgruppe. Bei Geräten mit nur einem Segment ist es dieselbe Entity. Jede Lampe in meiner Konfiguration hat deshalb ein Feld bri und optional ein Feld farbe, und bei manchen steht zweimal dasselbe drin.
Und hier kommt der Haken: Eine Segmentgruppe einzuschalten schaltet auch das Gerät ein. Wenn ich also zuerst die Farbe setze und die Segmentgruppe dabei anschaltet, während der Master noch auf dem Wert von gestern Abend steht — sagen wir 255 —, dann gibt das einen Blitz auf voller Helligkeit in ein stockdunkles Kinderzimmer. Um sechs Uhr morgens. An ein schlafendes Kind.
Technisch ist das eine funktionierende Weckmethode. Pädagogisch vermutlich nicht.
Die Reihenfolge ist deshalb hart verdrahtet und im Code auskommentiert: Helligkeit immer zuerst, Farbe danach. Erst auf 1 dimmen, dann färben. Und die Segmentgruppe bekommt zusätzlich eine eigene Helligkeit von 255 mitgeliefert — nicht, damit sie hell ist, sondern damit sie den Master nicht ausbremst. Zwei Helligkeitswerte in Reihe multiplizieren sich nämlich, und eine halb heruntergedimmte Segmentgruppe würde die ganze Kurve verfälschen.
Keine Überblendzeit, bitte
Man könnte jedem Lichtbefehl eine Überblendzeit mitgeben, damit der Sprung von 38 auf 69 weich wird. Tue ich bewusst nicht — die Streifen haben hausweit ihre eigene Überblendzeit von 0,7 Sekunden eingestellt, das genügt bei 15-Sekunden-Schritten vollkommen.
Der eigentliche Grund ist aber ein anderer: Ein von außen gesetzter Überblendwert brennt sich in die Presets ein. Speichert irgendwer später ein Preset, während der Wert noch anders steht, wandert er mit hinein und taucht Wochen danach an einer völlig anderen Stelle wieder auf. Dasselbe Muster wie bei dem Einfrier-Flag, mit dem ich mich vor ein paar Tagen herumgeärgert habe. Nicht anfassen, was man nicht anfassen muss.
Wer das Licht ausmacht, ist wach
Eine Weckrampe braucht einen Notausgang. Wenn jemand um 06:08 die Lampe von Hand ausschaltet, soll das Ding nicht alle fünfzehn Sekunden fröhlich weiterdimmen und gegen den Menschen anarbeiten.
Also: Geht während der Rampe eine der Lampen des Zimmers aus, wird das Lauf-Flag gelöscht, und die Schleife beendet sich beim nächsten Durchgang. Das Flag liegt sichtbar auf dem Dashboard als Kachel „Läuft gerade” — man kann die Rampe damit auch von dort abschalten.
Und hier die Art von Detail, für die man dieses Blog vermutlich liest: Ich hatte in den Kommentar geschrieben, der Abbruch laufe über automation.aufwachmodus_abbruch. Falsch. Home Assistant bildet den Namen einer YAML-Automation nicht aus ihrer id, sondern aus ihrem Alias — und der lautete „Aufwachmodus: Abbruch bei Handbetrieb”. Die Entity heißt deshalb automation.aufwachmodus_abbruch_bei_handbetrieb. Funktioniert hat es trotzdem, weil der Code das Flag direkt setzt und nicht die Automation adressiert. Aber mein Kommentar hat auf eine Entity verwiesen, die es nicht gibt, und genau solche Kommentare sind die, die einen ein halbes Jahr später eine Stunde kosten. Am nächsten Morgen nachgezogen.
Ein Script, drei Zimmer, ein Testknopf
Die ganze Mechanik steckt in einem Script mit den Feldern zimmer und optional minuten. Die Automation ist nur Zeitgeber: drei Zeit-Trigger auf die drei Startzeiten, die Trigger-ID ist gleich der Zimmerschlüssel, und das war’s.
Das hat einen konkreten Nutzen. Weil die Dauer ein Feld ist, kann derselbe Ablauf vom Dashboard-Knopf als Dreiminuten-Test laufen. Ich muss nicht bis morgen früh warten, um zu sehen, ob die Kurve stimmt, und niemand muss um 06:00 im Kinderzimmer stehen und zuschauen. Hätte ich die Rampe in die Automation geschrieben, gäbe es diesen Knopf nicht.
Dazu die Bedingungen, die ein Weckruf eben braucht: Zimmerschalter an, Wochentag passend (täglich / Mo-Fr / Sa+So), und bei den Kinderzimmern kein Ausschlafen markiert. Letzteres ist ein Helfer, der ohnehin schon die Rollos am Wochenende unten lässt — es wäre absurd gewesen, dafür eine zweite Wahrheit zu erfinden. Ausschlafen schlägt Weckruf.
Getestet im leeren Zimmer
Ich habe die Rampe nicht nachts im Kinderzimmer erprobt, sondern auf den Streifen in der Bibliothek, bei geschlossener Tür und niemandem drin. Zwei Minuten statt dreißig, Werte mitgeschrieben, Farbwanderung geprüft, Lauf-Flag danach kontrolliert. Beide Streifen gingen im Gleichschritt, das Flag war am Ende aus.
Was dabei nicht getestet wurde: die Topologie in Marvins Zimmer, also Master plus separate Segmentgruppe. Die Bibliothek hat genau ein Segment, dort fällt der Zweig mit den zwei Entities gar nicht an. Der erste echte Lauf dieser Variante war am Morgen danach — ungetestet an genau der Stelle, an der ich die unangenehmste Falle vermutet hatte. Nicht ideal, aber die Alternative wäre gewesen, ein Kind für einen Integrationstest zu wecken.
Nebenbei eine Falle beim Testen selbst: Ruft man ein Script über die HTTP-Schnittstelle auf, wartet der Aufruf, bis das Script fertig ist. Bei einer Rampe läuft man also direkt in den Zeitüberlauf und denkt, es sei abgestürzt. Der Aufruf muss in einen eigenen Thread, damit man parallel messen kann. Fünf Minuten Verwirrung, die ich mir hätte sparen können.
Archäologie
Beim Anlegen der Helfer stieß ich auf zwei Leichen: ein Schalter namens „Aufwachen aktiv” und eine Uhrzeit namens „Aufwachen”. Beide ohne Raum, ohne Dashboard-Karte, ohne Automation — Überreste eines früheren Weckruf-Anlaufs, von dem niemand mehr wusste. Ich habe alle zehn Dashboards geprüft, ob die irgendwo auftauchen, und sie dann gelöscht.
Interessanter war der Fund dahinter. In der Datei, die festhält, welche Entities für Sprachassistenten freigegeben sind, lagen fünf Einträge zum Thema Aufwachen — davon drei für Entities, die es gar nicht mehr gibt. Eine Automation „Aufwachen”, eine „Aufwachen 2”, ein Sensor. Home Assistant räumt diese Freigabesätze beim Löschen einer Entity nicht mit auf; sie liegen dort und zeigen ins Nichts.
Rausgeworfen, mit Backup. Mit einer Einschränkung, die man wissen muss: Der Store lebt im Speicher. Löscht man in der Datei, ohne neu zu starten, und ändert danach irgendwo eine Freigabe, schreibt das Programm seinen Speicherstand komplett zurück — und die Geister sind wieder da. Die Änderung hält also erst ab dem nächsten Neustart sicher. Hausaufgabe für später.
Was jetzt da ist
Drei Zimmer, je mit Schalter, Startzeit, Endzeit, Zielhelligkeit in Prozent, Wochentagsauswahl, einer Kachel „Läuft gerade” zum Abbrechen und einem Testknopf über drei Minuten. Alles in einer Datei, alles über den Dashboard-Generator ausgerollt, sodass ein viertes Zimmer drei Handgriffe wäre: Helferblock kopieren, Eintrag in die Zimmerliste, Trigger ergänzen.
Und eine Lichtkurve, die dreißig Minuten braucht, um das zu tun, was ein Schalter in einer Millisekunde könnte. Das ist kein Bug.