openHAB I: Heimautomatisierung mit openHAB

This entry is part 1 of 10 in the series openHAB
Geschätzte Lesezeit: 8 Minuten.

Heimautomatisierung, bzw. Smart Home, ist ein Projekt, das ich im Rahmen meines Umzuges in ein Haus angehen möchte. Dazu nutze ich diesen Blog, um Ideen zu sammeln, und Umsetzungen zu dokumentieren. Dieser Eintrag stellt eine Übersicht der einzelnen Projekte dar, und eine generelle Einführung in das Thema.

Warum Heimautomatisierung?

Smart Home, da kann man sein Haus mit dem Handy steuern. Das ist doch toll, oder?

Ja, aber es ist nicht der Grund, warum ich Heimautomatisierung als Projekt angehe. Vielmehr möchte ich, dass ich das Haus nicht steuern muss, sondern dass sich das Haus selbst steuert. Dass das Haus weiß, was ich möchte.

Natürlich möchte ich mein Heim auch automatisieren, weil es geht. Weil ich es kann (bzw. können möchte).

Warum openHAB?

Ich möchte ein flexibles System, das mich nicht an einen Hersteller und dessen Ökosystem kettet, sondern das es ermöglicht, die besten Teile der verschiedenen Hersteller miteinander in Interaktion zu bewegen. Außerdem möchte ich nicht, dass meine Daten in irgendeine Cloud geschickt werden, weil mein Smart Home der Meinung ist, es müsse ständig nach Hause telefonieren. Und ein Fan freier Software bin ich sowieso.

Damit bleiben nicht wahnsinnig viele Systeme übrig. openHAB ist eines der bekanntesten Open-Source-Systeme. FHEM hat viele Fans, und ich habe auch eine Weile überlegt, ob ich nicht FHEM nutzen möchte. Ich kann keine belastbaren Argumente nennen, warum das eine oder das andere System besser sei. Den Ausschlag pro openHAB haben zwei Dinge geliefert: Erstens unterstützt openHAB Homematic IP,1Mit Homematic IP will ich einige Projekte realisieren, vor allem die Heizungssteuerung. FHEM (noch) nicht. Zweitens wurde mir openHAB von zwei Personen empfohlen. Von jemand anderem wurde mir Home Assistant empfohlen, aber zu diesem Zeitpunkt wollte ich kein drittes System mehr in meine Entscheidungsfindung aufnehmen.

Grundsystem

Ich habe seit einiger Zeit einen Raspberry Pi 4B in der 4GB-Variante2Ich habe ein Kit von LABISTS auf amazon.de für 87,99€ gekauft. herumliegen. Da ist ja klar, dass ich diesen auch als Smart-Home-Zentrale einsetze. Auf diesen installierte ich openHABian, in der aktuellsten Version. Dies ist derzeit openHAB 2.5.3.

Über meine erste Schritte mit openHAB (Installation, Grundkonfiguration, erstes Binding) berichtet der folgende Artikel.

openHAB II: Grundkonfiguration

Nach einiger Zeit war ein Update auf openHAB 3.0 nötig. Dieses große Update habe ich ebenfalls ausführlich beschrieben. Daher ist der vorige Artikel nicht mehr aktuell.

openHAB VIII: Update auf openHAB 3.0

Projekt: Fritz!Box einbinden

Ich habe eine Fritz!Box 7590.3Im Mesh-Set mit dem Repeater 1750 für 231,98€ bei Expert gekauft. Diese kann man auch einbinden. So kann z.B. im Falle eines Anrufes automatisch das Fernsehprogramm pausiert oder stummgeschaltet werden. Das hätte ich gerne.

Tatsächlich ist die Einbindung der Fritz!Box das erste Projekt, das ich realisiere. Dies liegt unter anderem daran, dass für die anderen Projekte noch nicht alle Teile geliefert wurden. Demzufolge sind die Anfänge relativ holprig, da ich zum Beispiel erstmalig eine Sitemap anlege. Nichtsdestotrotz habe ich die Konfiguration und die Schritte in einem separaten Artikel hinterlegt.

openHAB III: Fritz!Box-Einbindung

Nachdem ich openHAB auf Version 3.0 aktualisiert habe, musste ich mit der Fritz!Box-Einbindung von vorne beginnen. Dies habe ich getrennt dokumentiert.

openHAB IX: Fritz!Box und OH 3.0

Projekt: Temperatur, Heizung und Raumklima

Das zweite echte Projekt, das ich angehen werde, ist die Kontrolle von Temperatur und Raumklima, verbunden mit einer Heizungssteuerung. Dieses Projekt ist vermutlich der Klassiker der Heimautomatisierung.

Ich habe mich für das System Homematic IP entschieden. Ich beginne mit der Messung der Temperatur und der Luftfeuchtigkeit. Dazu bestellte ich mir vier HmIP-WTH-2,4Alle bei elektroprofi.com für 39,99€ pro Stück. um zu lernen, wie diese eingebunden werden. Diese werde ich im Haus an relevanten Stellen positionieren: Im Schlafzimmer, im Kellerraum mit dem Feuchtigkeitsproblem, und an zwei weiteren noch zu identifizierenden Stellen. Im nächsten Schritt folgen weitere Temperatursensoren, und die Heizkörperthermostate, die damit gesteuert werden sollen. Ich werde in einigen Räumen ohne Heizkörper (z.B. unter dem Dach) nur Sensoren ohne Steuerungsmöglichkeit anbringen. Und ich möchte auch gerne die Außentemperatur und -luftfeuchtigkeit messen. Wie das geht, muss ich mich noch informieren.

Ach, und dann gibt es ja noch Sensoren, die erkennen, ob die Fenster geöffnet oder geschlossen sind. Das wäre praktisch, um die Heizung zu steuern. Und wenn das System erkennt, ob ich an- oder abwesend bin, könnte man sicher Heizkosten sparen. Das Projekt scheint recht groß zu werden, daher lagere ich alle Details in einen separaten Artikel aus:

openHAB IV: Temperatur, Heizung und Raumklima mit Homematic IP

Abfallkalender

Mein Haus kann mich erinnern, wann ich Mülltonnen herausrollen soll. Das klingt doch super, das will ich haben. Leider stellt sich dies als ganz schön kompliziert an, doch ich scheine es per CalDAV-Einbindung hinbekommen zu haben. Jeder, der sich schon einmal an einer CalDAV-Einbindung versucht hat, hat vermutlich gerade nervöse Zuckungen bekommen. Warum das eine ziemliche Frickelei ist, erkläre ich ausführlich hier:

openHAB V: Müllkalender per ical einbinden

Projekt: Licht

Wenig überraschend: Ich möchte das Licht im Haus steuern. Dabei geht es mir weniger um eine Simulation von Anwesenheit. Viel lieber möchte ich, dass die Helligkeit des Tageslichtes erkannt wird und die Lampen automatisch so hell gedreht werden, dass die Helligkeit im Raum konstant ist. Das Ganze soll über Philips Hue laufen. Von diesem Zielzustand bin ich noch weit entfernt, aber trotzdem habe ich meine bisherigen Erfolge bereits dokumentiert.

openHAB VI: Beleuchtung mit Philips Hue

Projekt: Waschmaschine und Trockner melden sich, wenn sie fertig sind

In unserem Haus stehen Waschmaschine und Trockner im Keller. Möchte ich wissen, ob eines der Geräte fertig ist, muss ich also in den Keller laufen. Das ist unpraktisch. Da muss es doch etwas besseres geben. Das gibt es auch, und das habe ich im folgenden Artikel dokumentiert.

openHAB VII: Waschmaschine und Trockner melden sich, wenn sie fertig sind

Weitere Projektideen

  • Rollladen: Rollladensteuerung ist ein Klassiker der Heimautomatisierung. Ich möchte gerne in Abhängigkeit von Sonnenauf- und Untergang sowie in Abhängigkeit von An- und Abwesenheit die Rollläden hoch- und herunterfahren lassen. Vermutlich werde ich dies mit Homematic IP umsetzen.
  • Rauchmelder: Es gibt Rauchmelder von Homematic IP. Also möchte ich die auch haben und in mein System einbauen.
  • Alarmanlage: Es gibt eine Alarmanlage von Homematic IP. Also möchte ich die auch haben und in mein System einbauen.
  • Grill: Temperatursteuerung ist für den ambitionierten Griller ein zentrales Thema. Ich möchte gerne die Temperatur des Garraumes und die Kerntemperatur des Grillguts kontrollieren können. Es gibt auf dem Markt einige Funkthermometer, da muss es doch möglich sein, dies zu koppeln. Vielleicht kann ich dies mit den farbigen LEDs kombinieren, um eine Blau-grün-rot-Temperaturanzeige zu basteln.
  • Gartenbewässerung: Eine automatische Gartenbewässerung, gekoppelt mit Wetterdaten, An- und Abwesenheit, Jahreszeit, Sonnenstand, und so weiter. Das wäre doch klasse. Das will ich haben.
  • Garagentor: Für das Garagentor hätte ich gerne einen Sensor, der mir anzeigt, ob das Tor offen oder geschlossen ist. Und vielleicht auch eine Steuerung des Tores, da es derzeit schon per Funk gesteuert werden kann. Vielleicht kann man das integrieren.

openHAB II: Grundkonfiguration

This entry is part 2 of 10 in the series openHAB
Geschätzte Lesezeit: 4 Minuten.

Ich habe seit einiger Zeit einen Raspberry Pi 4B in der 4GB-Variante1Ich habe ein Kit von LABISTS auf amazon.de für 87,99€ gekauft. herumliegen. Da ist ja klar, dass ich diesen auch als Smart-Home-Zentrale einsetze. Auf diesen installierte ich openHABian, in der aktuellsten Version. Dies ist derzeit openHAB 2.5.3.

Tag 1: Installation von openHAB

openHABian installierte ich gemäß der Dokumentation mit aktiviertem WLAN. Ich wollte natürlich den Status regelmäßig prüfen, indem ich auf http://openhab/ ging, allerdings funktionierte dies nicht so schnell wie erhofft. Stattdessen suchte ich über meinen Router die IP-Adresse heraus und surfte diese an. Hier erhielt ich immerhin einen „openHABian Installation Status“, aber die Logs funktionierten nicht. Möglicherweise gab es hier ein Problem damit, dass ich schonmal RaspBian auf dem RasPi installiert hatte und dem RasPi eine feste IP und einen festen Netzwerknamen zugeteilt hatte. Die Installation funktionierte trotzdem.

Als nächstes folgt das Setup. Glücklicherweise gibt es auch hier ein Tutorial. Ich wähle das Standard-Setup. Das war bisher alles ziemlich einfach.

Viel mehr ändere ich an der Konfiguration nicht. Allerdings weiß ich ja bereits, dass ich als nächstes die Projekte Temperatur und Licht angehen möchte, also installiere ich die Bindungs für Homematic IP (binding-homematic – 2.5.3) und Philips Hue (binding-hue – 2.5.3). Und das Network-Binding (binding-network – 2.5.3), das eigentlich als nächstes im Tutorial beschrieben wird. Da war ich wohl zu voreilig.

Tag 2: Mein erstes Binding

Also binde ich ein paar Tage später mein Smartphone per Network-Binding ein. In der Zwischenzeit habe ich den Router gewechselt, und damit auch die SSID. Das war keine besonders gute Idee, denn jetzt muss ich die voreingestellte SSID ändern. Dazu verbinde ich den RasPi per Kabel mit meinem neuen Router, einer Fritz!Box 7590.2Im Mesh-Set mit dem Repeater 1750 für 231,98€ bei Expert gekauft. Dann wähle ich mich per SSH ein, und folge dieser Anleitung und bearbeite folgende Datei: /rootfs/etc/wpa_supplicant/wpa_supplicant.conf. Als Editor benutze ich VIM, das glücklicherweise vorinstalliert ist. Also: sudo vim wpa_supplicant.conf; „i“ drücken, um in den Bearbeitungsmodus zu kommen, entsprechende Zeile bearbeiten, „ESC“ drücken, :wq tippen, „Enter“ drücken.

Das war allerdings nur die Vorarbeit, die ich mir auch hätte sparen können. Eigentlich wollte ich ja mein Smartphone einbinden. Für dieses habe ich in meiner Fritz!Box die feste IP x.x.x.42 vergeben. Also zurück zum Tutorial. Ich installiere das Network-Binding (binding-network – 2.5.3) im Add-on-Menü. Anschließend suche ich in der Inbox für das Network-Binding neue Things, und dort wird die IP-Adresse meines Smartphones gefunden. Anklicken, Name „Telefon-Andi“ vergeben, fertig. Wie in der Anleitung empfohlen, aktivierte ich danach in der Konfiguration den Simple Mode für das Item Linking.

Es folgt die Navigation zu Configuration > Things. Yeah, mein Smartphone ist online. Durch Anklicken erfahre ich, dass drei Channels verfügbar sind: Online, Latency und Last Seen. Ich aktiviere alle drei durch jeweiliges Klicken auf den Radio-Button, und nun folgt ein Dialog, das in der Anleitung nicht beschrieben wird. Ich werde nach einer Konfiguration gefragt. Ich stelle oben Standard ein, und unten wähle ich Create new item. Es öffnet sich ein weiterer Dialog, der einige Standard-Parameter vorschlägt, die ich so übernehme, indem ich rechts unten auf Link klicke. Die abschließende Navigation zu Control zeigt mir nun genau diese drei Channels, gruppiert unter „Telefon-Andi“. Das sieht sehr gut aus. Mit diesem Erfolg beende ich die Sitzung.

openHAB III: Fritz!Box-Einbindung

This entry is part 3 of 10 in the series openHAB
Geschätzte Lesezeit: 11 Minuten.

Ich habe eine Fritz!Box 7590.1Im Mesh-Set mit dem Repeater 1750 für 231,98€ bei Expert gekauft. Außerden habe ich einen Repeater 2400 für 79,90€ bei Amazon gekauft. Diese kann man auch einbinden. So kann z.B. im Falle eines Anrufes automatisch das Fernsehprogramm pausiert oder stummgeschaltet werden. Das hätte ich gerne.

Achtung: Seit meinem Update auf openHAB 3.0 funktioniert das in diesem Artikel beschriebene Vorgehen nicht mehr. Die openHAB 3.0-Variante habe ich in einem separaten Artikel dokumentiert.

Tag 1: Installation des Bindings

Es gibt eine wunderbare Anleitung für die Fritz!Box-Integration: Hier zu finden. Die arbeite ich jetzt ab. Als Vorarbeit erhält mein RasPi im Fritz!Box-Menü eine feste IP (x.x.x.10). Außerdem lege ich einen Benutzer für openhab auf der Fritz!Box an, und aktiviere „Zugriff für Anwendungen zulassen“. Anschließend installiere ich das Fritz!Box-Binding. Aber, oh weh, es gibt tatsächlich drei verschiedene Bindings für die Fritz!Box:

  • AVM FRITZ!Box Binding (binding-avmfritz – 2.5.3): Das ist wohl aktuell, für die Smart-Home-Features der Fritz!Box. Diese nutze ich (noch) nicht, daher ist es (derzeit) nicht relevant für mich.
  • Fritzbox Binding (binding-fritzbox1 – 1.14.0): Das ist wohl veraltet, und nutzt veraltete Schnittstellen. Also wird es nicht installiert.
  • Fritzbox TR064 Binding (binding-fritzboxtr0641 – 1.14.0): Das ist das aktuelle Binding. Das wird genutzt.

In Configuration > Bindings hinterlege ich noch Benutzername und Passwort für den openhab-Zugang, damit das Binding auch wirklich etwas schalten kann.

Gut. Nachdem das Binding installiert wurde, lege ich ein Item an. Also navigiere ich per SSH in das Items-Verzeichnis, und lege die Datei fritzbox.items an. Im Prinzip mache ich einmal copy&paste der Item-Konfiguration. Diese Datei enhält also folgende Zeilen:

String  fboxName            "FBox Model [%s]"           {fritzboxtr064="modelName"}
String  fboxManufacturer    "FBox Manufacturer [%s]"    {fritzboxtr064="manufacturerName"}
String  fboxSerial          "FBox Serial [%s]"          {fritzboxtr064="serialNumber"}
String  fboxVersion         "FBox Version [%s]"         {fritzboxtr064="softwareVersion"}
Number  fboxUptime          "FBox Uptime [%d s]"        {fritzboxtr064="upTime"}                 
// get wan ip if FritzBox establishes the internet connection (e. g. via DSL)
String  fboxWanIP           "FBox WAN IP [%s]"          {fritzboxtr064="wanip"}
// get wan ip if FritzBox uses internet connection of external router
String  fboxWanIPExternal   "FBox external WAN IP [%s]" {fritzboxtr064="externalWanip"}
Switch  fboxWifi24          "2,4GHz Wi-Fi"               {fritzboxtr064="wifi24Switch"}
Switch  fboxWifi50          "5,0GHz Wi-Fi"               {fritzboxtr064="wifi50Switch"}
Switch  fboxGuestWifi       "Guest Wi-Fi"                {fritzboxtr064="wifiGuestSwitch"}    
Contact cFboxMacOnline      "Presence (Wi-Fi) [%s]"      {fritzboxtr064="maconline:11-11-11-11-11-11" }
Switch  fboxReboot          "Reboot"                    {fritzboxtr064="reboot"}

// WAN statistics

String  fboxWanAccessType "FBox WAN access type [%s]" {fritzboxtr064="wanWANAccessType"}
Number  fboxWanLayer1UpstreamMaxBitRate "FBox WAN us max bit rate [%s]" {fritzboxtr064="wanLayer1UpstreamMaxBitRate"}
Number  fboxWanLayer1DownstreamMaxBitRate "FBox WAN ds max bit rate [%s]" {fritzboxtr064="wanLayer1DownstreamMaxBitRate"}
String  fboxWanPhysicalLinkStatus "FBox WAN physical link status [%s]" {fritzboxtr064="wanPhysicalLinkStatus"}
Number  fboxWanTotalBytesSent "WAN total bytes sent [%s]" {fritzboxtr064="wanTotalBytesSent"}
Number  fboxWanTotalBytesReceived "WAN total bytes received [%s]" {fritzboxtr064="wanTotalBytesReceived"}

// DSL statistics

Contact fboxDslEnable       "FBox DSL Enable [%s]"      {fritzboxtr064="dslEnable"}
String  fboxDslStatus       "FBox DSL Status [%s]"      {fritzboxtr064="dslStatus"}
Number  fboxDslUpstreamCurrRate "DSL Upstream Current [%s kbit/s]" {fritzboxtr064="dslUpstreamCurrRate"}    
Number  fboxDslDownstreamCurrRate "DSL Downstream Current [%s kbit/s]" {fritzboxtr064="dslDownstreamCurrRate"} 
Number  fboxDslUpstreamMaxRate "DSL Upstream Max [%s kbit/s]" {fritzboxtr064="dslUpstreamMaxRate"}               
Number  fboxDslDownstreamMaxRate "DSL Downstream Max [%s kbit/s]" {fritzboxtr064="dslDownstreamMaxRate"}        
Number  fboxDslUpstreamNoiseMargin "DSL Upstream Noise Margin [%s dB*10]" {fritzboxtr064="dslUpstreamNoiseMargin"}
Number  fboxDslDownstreamNoiseMargin "DSL Downstream Noise Margin [%s dB*10]" {fritzboxtr064="dslDownstreamNoiseMargin"}
Number  fboxDslUpstreamAttenuation "DSL Upstream Attenuation [%s dB*10]" {fritzboxtr064="dslUpstreamAttenuation"}
Number  fboxDslDownstreamAttenuation "DSL Downstream Attenuation [%s dB*10]" {fritzboxtr064="dslDownstreamAttenuation"}
Number  fboxDslFECErrors "DSL FEC Errors [%s]" {fritzboxtr064="dslFECErrors"}
Number  fboxDslHECErrors "DSL HEC Errors [%s]" {fritzboxtr064="dslHECErrors"}
Number  fboxDslCRCErrors "DSL CRC Errors [%s]" {fritzboxtr064="dslCRCErrors"}

// only when using call monitor
Switch  fboxRinging         "Phone ringing [%s]"                {fritzboxtr064="callmonitor_ringing" }
Switch  fboxRinging_Out     "Phone ringing [%s]"                {fritzboxtr064="callmonitor_outgoing" }
Switch  fboxCallConnecting  "Call established [%s]"             {fritzboxtr064="callmonitor_active" }
Call    fboxIncomingCall    "Incoming call: [%1$s to %2$s]"     {fritzboxtr064="callmonitor_ringing" } 
Call    fboxOutgoingCall    "Outgoing call: [%1$s to %2$s]"     {fritzboxtr064="callmonitor_outgoing" }
Call    fboxConnectedCall   "Call established [%1$s to %2$s]"   {fritzboxtr064="callmonitor_active" }

// resolve numbers to names based on phonebook
Call    fboxIncomingCallResolved    "Incoming call: [%1$s to %2$s]"     {fritzboxtr064="callmonitor_ringing:resolveName" } 

// Telephone answering machine (TAM) items
// Number after tamSwitch is ID of configured TAM, start with 0
Switch  fboxTAM0Switch   "Answering machine ID 0"       {fritzboxtr064="tamSwitch:0"}
Number  fboxTAM0NewMsg   "New Messages TAM 0 [%s]"      {fritzboxtr064="tamNewMessages:0"}

// Missed calls: specify the number of last days which should be searched for missed calls
Number  fboxMissedCalls  "Missed Calls [%s]"            {fritzboxtr064="missedCallsInDays:5"}

// Call deflection items
// Number after callDeflectionSwitch is ID of configured call deflection
// The ID count includes the entries from the "Call Blocks" page.
// If you have no "Call Blocks", the first entry on the "Call Diversions" page has ID 0.
// If you have 3 "Call Blocks", the first entry on the "Call Diversions" page has ID 3.
Switch  fboxCD0Switch    "Call Deflection ID 0"         {fritzboxtr064="callDeflectionSwitch:0"}

Als nächstes geht es an die Sitemap. Ich ergänze in der default.sitemap folgendes Snippet:

    Frame label="Fritz!Box" {
        Text item=fboxDslDownstreamCurrRate label="Downloadspeed aktuell"
        Text item=fboxDslUpstreamCurrRate label="Uploadspeed aktuell"
        Text item=fboxUptime label="Fritz!Box-Uptime"
        Switch item=fboxGuestWifi label="Gäste-WLAN"
    }

Nun besitze ich einen Schalter, mit dem ich das Gäste-WLAN an- und ausschalten kann. Außerdem bekomme ich die aktuellen Upload- und Downloadgeschwindigkeiten sowie die Uptime der Fritz!Box angezeigt. Das ist natürlich noch nicht optimal, denn ich bekomme die Geschwindigkeiten in kbit/s angezeigt. Dies ist in obiger Item-Konfiguration, Zeile 29-32, bereits korrigiert. Im Tutorial ist es falsch hinterlegt, dort steht mbit/s. Was ich eigentlich gerne hätte. Außerdem hätte ich gerne Icons auf der Sitemap. Das ist ein Projekt für einen anderen Tag.

Tag 2: Schönere Icons und bessere Textformatierung

Wie erwähnt möchte ich schönere Icons auf meiner Sitemap. Also bearbeite ich die Sitemap. Ich ändere daher obiges Snippet in der default.sitemap zu:

    Frame label="Fritz!Box" {
        Text item=fboxDslDownstreamCurrRate icon="returnpipe" label="Downloadspeed aktuell"
        Text item=fboxDslUpstreamCurrRate icon="flowpipe" label="Uploadspeed aktuell"
        Text item=fboxUptime icon="line" label="Fritz!Box-Uptime"
        Switch item=fboxGuestWifi icon="network" label="Gäste-WLAN"
    }

Nun wollte ich einen Screenshot einbinden, wie dies derzeit aussieht. Aber, oh weh, es funktioniert nicht. In den Logs standen folgende verräterische Zeilen:

2020-04-27 11:56:49.235 [ERROR] [ing.fritzboxtr064.internal.Tr064Comm] - Failed to receive valid response from httpGet: Read timed out
[ERROR] [ing.fritzboxtr064.internal.Tr064Comm] - Releasing connection to FritzBox because of error!
[ERROR] [ing.fritzboxtr064.internal.Tr064Comm] - Could not read xml response services
[ERROR] [ing.fritzboxtr064.internal.Tr064Comm] - Failed to receive valid response from httpGet: Connect to fritz.box:49443 [fritz.box/192.168.178.1, fritz.box/2a01:c22:b40b:500:de39:6fff:fede:37f4, fritz.box/fd00:0:0:0:de39:6fff:fede:37f4] failed: connect timed out
[ERROR] [ing.fritzboxtr064.internal.Tr064Comm] - Releasing connection to FritzBox because of error!
[ERROR] [ing.fritzboxtr064.internal.Tr064Comm] - Could not read xml response services
[WARN ] [ing.fritzboxtr064.internal.Tr064Comm] - No tr064 service found for service id urn:WLANConfiguration-com:serviceId:WLANConfiguration2
[WARN ] [ing.fritzboxtr064.internal.Tr064Comm] - No tr064 service found for service id urn:WLANConfiguration-com:serviceId:WLANConfiguration3

Nach kurzer Analyse zeigte sich, dass in der Konfigurations-Datei services/fritzboxtr064.cfg ein falscher Username und Passwort eingetragen war. Anscheinend speichert die PaperUI den Benutzernamen und das Passwort nicht (oder inkorrekt), denn eigentlich hatte ich das geändert. Also trage ich die richtigen Zugangsdaten ein, starte openHAB neu (sudo systemctl restart openhab2.service), und es funktioniert. Yeah!

Hier ist der versprochene Screenshot:
2020-04-27_sitemap-fritz

So weit, so gut. Mir gefällt die Textformatierung noch nicht. Erstens steht da kbit/s, ich hätte aber gerne Mbit/s. Und die Uptime in Sekunden ist auch seltsam. Leider kann man die Formatierung von Items nicht direkt ändern, sondern muss den veränderten Wert über eine Rule in ein neues Item schreiben. Naja, ich wollte sowieso lernen, wie das mit den Rules geht.

Als erstes lege ich mir various.items an, in der ich die neuen Items definiere:

Number fboxDslUpstreamCurrRate_mbit "DSL Upstream Current [%.1f Mbit/s]"
Number fboxDslDownstreamCurrRate_mbit "DSL Downstream Current [%.1f Mbit/s]"
String fboxUptime_hms "FBox Uptime [%s]"

Ich bin nicht sicher, ob dies in einer separaten Items-Datei sein muss, oder ob ich dies auch in fritzbox.items hätte ergänzen können. Zuerst habe ich es natürlich in fritzbox.items ergänzt, aber dort erhielt ich einen Fehler, dass diese Items nicht mit echten Werten verknüpft werden können. Vermutlich benötigt jedes Item eines Bindings ein echtes Gegenstück im Thing. Ich glaube, ich habe gerade gelernt, was ein Channel ist…

Und ich ändere das Sitemap-Snippet in default.sitemap zu:

    Frame label="Fritz!Box" {
        Text item=fboxDslDownstreamCurrRate_mbit icon="returnpipe" label="Downloadspeed aktuell"
        Text item=fboxDslUpstreamCurrRate_mbit icon="flowpipe" label="Uploadspeed aktuell"
        Text item=fboxUptime_hms icon="line" label="Fritz!Box-Uptime"
        Switch item=fboxGuestWifi icon="network" label="Gäste-WLAN"
    }

Jetzt fehlen noch die Rules. Ich erstelle eine neue Datei fritzbox.rules. Als erstes möchte ich, dass Up- und Downstream in Mbit/s anstatt kbit/s angezeigt werden. Das ist einfach, ich muss nur durch 1000 teilen:

rule "Adjust fboxUpstream"
when
    Item fboxDslUpstreamCurrRate received update
then
    fboxDslUpstreamCurrRate_mbit.sendCommand((fboxDslUpstreamCurrRate.state as DecimalType) / 1000)
end

rule "Adjust fboxDownstream"
when
    Item fboxDslDownstreamCurrRate received update
then
    fboxDslDownstreamCurrRate_mbit.sendCommand((fboxDslDownstreamCurrRate.state as DecimalType) / 1000)
end

Das ist im Prinzip zweimal die gleiche Regel. Für die Uptime habe ich diese Anleitung genutzt, und noch etwas mit if-else rumgespielt:

rule "Adjust fboxUptime"
when
    Item fboxUptime changed
then
    val totalSecs = (fboxUptime.state as Number).intValue
    val sec = totalSecs % 60
    val min = (totalSecs / 60) % 60
    val hrs = (totalSecs / (60*60)) % 24
    val day = totalSecs / (60*60*24)
    val tage = "Tage"
    val stunden = "Stunden"
    val minuten = "Minuten"
    val sekunden = "Sekunden"
    
    if (day==1){
        tage = "Tag"
    }
    if (hrs==1){
        stunden = "Stunde"
    }
    if  (min==1){
        minuten = "Minute"
    }
    if  (sec==1){
        sekunden = "Sekunde"
    }
       
    if (day==0){
        if (hrs==0){
            fboxUptime_hms.postUpdate(String::format("%d %s, %d %s", min, minuten, sec, sekunden))
        }
        fboxUptime_hms.postUpdate(String::format("%d %s, %d %s", hrs, stunden, min, minuten))
    } else {
        fboxUptime_hms.postUpdate(String::format("%d %s, %d %s", day, tage, hrs, stunden))
    } 
end

Nun werden immer zwei Zahlen angezeigt: In der ersten Stunde werden nur Minuten und Sekunden angezeigt. Am restlichen ersten Tag werden Stunden und Minuten angezeigt, und danach Tage und Stunden. Das hat erstens Platzgründe, und zweitens interessiert es mich wenig, wie viele Sekunden das System schon läuft, wenn es seit 243 Tagen und 16 Stunden läuft. Und außerdem ist eine kleine Umformatierung der Worte vorhanden, da beim Zahlenwert von 1 die Einzahl, ansonsten die Mehrzahl verwendet wird.

Das Ganze sieht jetzt so aus:
2020-04-28_sitemap-fritz

Tag 3: openHAB 3.0

Seit meinem Update auf openHAB 3.0 funktioniert das in diesem Artikel beschriebene Vorgehen nicht mehr. Die openHAB 3.0-Variante habe ich in einem separaten Artikel dokumentiert.

openHAB IV: Temperatur, Heizung und Raumklima mit Homematic IP

This entry is part 4 of 10 in the series openHAB
Geschätzte Lesezeit: 10 Minuten.

Das zweite echte Projekt ist die Kontrolle von Temperatur und Raumklima, verbunden mit einer Heizungssteuerung. Dieses Projekt ist vermutlich der Klassiker der Heimautomatisierung.

Ich habe mich für das System Homematic IP entschieden. In diesem System gibt es vier Komponenten, die für mich relevant sind:

  • HmIP-WTH-2: Thermostat, bestehend aus Temperatur- und Luftfeuchtemessung sowie einem Temperaturregler
  • HmIP-STHD: Temperatur- und Luftfeuchtesensor
  • HmIP-eTRV-2: Heizkörperthermostat, das die Leistung des Heizkörpers steuert
  • HmIP-CCU3: Die Steuerzentrale.

Ich beginne mit der Messung der Temperatur und der Luftfeuchtigkeit. Dazu bestellte ich mir vier HmIP-WTH-2,1Alle bei elektroprofi.com für 39,99€ pro Stück. um zu lernen, wie diese eingebunden werden. Diese werde ich im Haus an relevanten Stellen positionieren: Im Schlafzimmer, im Kellerraum mit dem Feuchtigkeitsproblem, und an zwei weiteren noch zu identifizierenden Stellen.

Die Zentrale CCU3 habe ich ebenfalls zu Beginn bestellt.2Einzeln bei Böttcher AG für 139,99€. Hier werde ich raspberryMATIC installieren. Vielleicht. Oder ich nutze die normale Software. Da muss ich mich nochmal informieren.

Im nächsten Schritt folgen weitere Temperatursensoren, und die Heizkörperthermostate, die damit gesteuert werden sollen. Ich werde in einigen Räumen ohne Heizkörper (z.B. unter dem Dach) nur Sensoren ohne Steuerungsmöglichkeit anbringen. Und ich möchte auch gerne die Außentemperatur und -luftfeuchtigkeit messen. Wie das geht, muss ich mich noch informieren.

Ach, und dann gibt es ja noch Sensoren, die erkennen, ob die Fenster geöffnet oder geschlossen sind. Das wäre praktisch, um die Heizung zu steuern. Und wenn das System erkennt, ob ich an- oder abwesend bin, könnte man sicher Heizkosten sparen. Das Projekt scheint recht groß zu werden.

Tag 1: Einbindung der CCU3

Okay, beginnen wir ganz langsam. Ich habe das Binding für Homematic IP (binding-homematic – 2.5.3) installiert. Als nächstes schließe ich die CCU3 an, und setze eine feste IP (x.x.x.12). Diese hat eine Web-Oberfläche, also navigiere ich direkt dort hin. Als erstes aktualisiere ich die CCU3-Firmware von 3.0.16 auf 3.51.6 mit dem integrierten Updater. Das dauert insgesamt etwa fünf Minuten.

Anschließend beginnt die Ersteinrichtung. Ich vergebe ein Admin-Passwort, wähle Express-Setup und wähle die Sicherheitsstufe Relaxed (weil dies so benötigt wird). Außerdem trage ich unter Einstellungen > Systemsteuerung > Firewall konfigurieren unter IP-Adressen für den eingeschränkten Zugriff: die IP-Adresse meines RasPi ein. Die vorhandenen Einträge lösche ich.

Nun ist es konfiguriert, es wird Zeit, den ersten Thermostaten (HmIP-WTH-2) einzubinden. Ich bin immer noch in der grafischen Oberfläche der CCU3, und drücke Geräte anlernen > Homematic Geräte direkt anlernen. Ich drücke den Pairing-Knopf am Thermostaten, der beginnt, fröhlich orange zu blinken. Das Gerät wird nicht gefunden, das hat also direkt nicht geklappt. Also probiere ich etwas herum. Einen Fehler bemerke ich, ich muss Geräte anlernen > Homematic IP Gerät mit Internetzugang anlernen auswählen. Ansonsten entferne ich die Batterie (um einen Reset herzustellen), drücke die Taste mal kurz, mal vier Sekunden, und irgendwann erscheint der Thermostat im Posteingang. Den Trick kann ich noch nicht erkennen. Jedenfalls ändere ich nichts an den Einstellungen, und drücke einfach Speichern.

Tag 2: Der erste Temperatursensor

Zurück in der PaperUI von openHAB. Über Inbox > + > Homematic Binding finde ich die CCU3 und füge diese hinzu. Direkt im Anschluss werden automatisch vier weitere Things in der Inbox angezeigt:

  • GATEWAY-EXTRAS
  • HM-RCV-50 BidCoS-RF (Virtual remote control (wireless))
  • HmIP-RCV-50 (Homematic IP virtual remote control)
  • HmIP-WTH-2 (Homematic IP Wall thermostat with humidity sensor)

Ich füge alle vier hinzu, obwohl mich nur der letzte Eintrag interessiert. Keine Ahnung, wozu die anderen gut sind, irgendwann werde ich diese vielleicht brauchen. Diesen nenne ich „HmIP-WTH-2_Schlafzimmer“. Jetzt möchte ich mir natürlich auch Temperatur und Luftfeuchtigkeit in meiner Sitemap anzeigen lassen.

Dazu muss ich als erstes die Channels aktivieren. Also wähle ich Things > HmIP-WTH-2_Schlafzimmer aus, und sehe direkt, dass der Channel für die tatsächliche Temperatur bereits existiert (Actual Temperature). Den Channel für die Luftfeuchte (Humidity) finde ich unter Show More. Ich kopiere jeweils die Channel, und lege mir eine neue Datei homematicip.items an:

// Schlafzimmer
// HmIP-WTH-2

Switch  ThermostatSchlafzimmer_BatteryStatus       "Thermostat Schlafzimmer Batterie [%s]"              {channel="homematic:HmIP-WTH-2:3014F711A0001F9A499D1959:000A9A49A6EF49:0#LOW_BAT" }
Number  ThermostatSchlafzimmer_TempSet             "Thermostat Schlafzimmer Temperatur Set [%.1f°C]"    {channel="homematic:HmIP-WTH-2:3014F711A0001F9A499D1959:000A9A49A6EF49:1#SET_POINT_TEMPERATURE" }
Number  ThermostatSchlafzimmer_TempAct             "Thermostat Schlafzimmer Temperatur Act [%.1f°C]"    {channel="homematic:HmIP-WTH-2:3014F711A0001F9A499D1959:000A9A49A6EF49:1#ACTUAL_TEMPERATURE" }
Number  ThermostatSchlafzimmer_Luftfeuchte         "Thermostat Schlafzimmer Luftfeuchte [%.0f %%]"      {channel="homematic:HmIP-WTH-2:3014F711A0001F9A499D1959:000A9A49A6EF49:1#HUMIDITY" }

Und nun ergänze ich folgendes Snippet in default.sitemap:

    Frame label="Schlafzimmer" {
        Text item=ThermostatSchlafzimmer_TempAct icon="temperature" label="Temperatur aktuell" 
        Text item=ThermostatSchlafzimmer_Luftfeuchte icon="humidity" label="Luftfeuchte"
    }

Und schon ist das fertig. So sieht es aus:
2020-04-30_sitemap-schafzimmer

Tag 3: Temperatursensor Nummer Zwei, und Firmware-Update

Heute binde ich den nächsten Temperatursensor ein. Das Anlernen über das WebUI von Homematic IP funktioniert diesmal auf Anhieb, warum auch immer. Die Einbindung als Thing in openHAB ebenfalls („HmIP-WTH-2_Wohnzimmer“), allerdings bekomme ich die Channels in anderer Reihenfolge als beim vorigen Sensor angezeigt. Seltsam.

In der WebUI der CCU3 bekomme ich angezeigt, dass ein Firmware-Update für meine Temperatursensoren vorliegt. Vielleicht löst dies ja das Problemchen. Ich klicke also auf die Anzeige, dass ein Firmware-Update vorliegt, und lade es herunter. Anschließend folge ich dieser Anleitung (Achtung, pdf). Ich klicke also auf Einstellungen > Geräte-Firmware > Neu und lade die Datei hoch. So kann ich von Version 2.0.2 auf 2.6.0 updaten. Dies prüfe ich unter Einstellungen > Geräte-Firmware – Übersicht. Hier steht unter „Aktion“, dass die Datei automatisch auf das Gerät übertragen wird, und dass ich, wenn dies fertig ist, auf Update klicken kann. Mal sehen.

In der Zwischenzeit ergänze ich homematicip.items um folgendes Snippet:

// Wohnzimmer
// HmIP-WTH-2

Switch  ThermostatWohnzimmer_BatteryStatus       "Thermostat Wohnzimmer Batterie [%s]"              {channel="homematic:HmIP-WTH-2:3014F711A0001F9A499D1959:000A9A49A6F185:0#LOW_BAT" }
Number  ThermostatWohnzimmer_TempSet             "Thermostat Wohnzimmer Temperatur Set [%.1f°C]"    {channel="homematic:HmIP-WTH-2:3014F711A0001F9A499D1959:000A9A49A6F185:1#SET_POINT_TEMPERATURE" }
Number  ThermostatWohnzimmer_TempAct             "Thermostat Wohnzimmer Temperatur Act [%.1f°C]"    {channel="homematic:HmIP-WTH-2:3014F711A0001F9A499D1959:000A9A49A6F185:1#ACTUAL_TEMPERATURE" }
Number  ThermostatWohnzimmer_Luftfeuchte         "Thermostat Wohnzimmer Luftfeuchte [%.0f %%]"      {channel="homematic:HmIP-WTH-2:3014F711A0001F9A499D1959:000A9A49A6F185:1#HUMIDITY" }

Und nun ergänze ich folgendes Snippet in default.sitemap:

	Frame label="Wohnzimmer" {
        Text item=ThermostatWohnzimmer_TempAct icon="temperature" label="Temperatur aktuell" 
        Text item=ThermostatWohnzimmer_Luftfeuchte icon="humidity" label="Luftfeuchte"
    }

Tag 4: Firmware-Update und Sensor Nummer Drei und Vier

Es hat etwas gedauert, aber nun ist die Update-Datei auf alle Geräte übertragen. Ich klicke auf Einstellungen > Geräte-Firmware – Übersicht > Update, und siehe da, die neueste Version der Firmware wird installiert. Das ist sehr einfach und nutzerfreundlich, sehr gut!

So, es folgen die nächsten zwei Temperatursensoren. Das Vorgehen ist ja mittlerweile bekannt. Mittlerweile habe ich auch den Trick identifiziert: Das Gerät sucht automatisch nach der CCU3, wenn ich die Batterie einlege. Daher muss ich nur schnell genug Geräte anlernen > Homematic IP Gerät mit Internetzugang anlernen auswählen. Das funktioniert wunderbar. Ich nenne die Geräte „HmIP-WTH-2_Arbeitszimmer“ und „HmIP-WTH-2_Technikkeller“. Zweiteres ist ein provisorischer Name, denn eigentlich möchte ich hier einen Sensor ohne Steuermöglichkeit installieren. Das ist günstiger. Außerdem habe ich in diesem Raum keine Heizung, und selbst wenn, muss im Keller nicht per Thermostat an der Temperatur gespielt werden.

Nun noch die openHAB-Snippets; hier ist homematicip.items:

// Arbeitszimmer
// HmIP-WTH-2

Switch  ThermostatArbeitszimmer_BatteryStatus       "Thermostat Arbeitszimmer Batterie [%s]"              {channel="homematic:HmIP-WTH-2:3014F711A0001F9A499D1959:000A9A49A6F962:1:0#LOW_BAT" }
Number  ThermostatArbeitszimmer_TempSet             "Thermostat Arbeitszimmer Temperatur Set [%.1f°C]"    {channel="homematic:HmIP-WTH-2:3014F711A0001F9A499D1959:000A9A49A6F962:1#SET_POINT_TEMPERATURE" }
Number  ThermostatArbeitszimmer_TempAct             "Thermostat Arbeitszimmer Temperatur Act [%.1f°C]"    {channel="homematic:HmIP-WTH-2:3014F711A0001F9A499D1959:000A9A49A6F962:1#ACTUAL_TEMPERATURE" }
Number  ThermostatArbeitszimmer_Luftfeuchte         "Thermostat Arbeitszimmer Luftfeuchte [%.0f %%]"      {channel="homematic:HmIP-WTH-2:3014F711A0001F9A499D1959:000A9A49A6F962:1#HUMIDITY" }


// Technikkeller
// HmIP-WTH-2

Switch  ThermostatTechnikkeller_BatteryStatus       "Thermostat Technikkeller Batterie [%s]"              {channel="homematic:HmIP-WTH-2:3014F711A0001F9A499D1959:000A9A49A6F9BA:0#LOW_BAT" }
Number  ThermostatTechnikkeller_TempSet             "Thermostat Technikkeller Temperatur Set [%.1f°C]"    {channel="homematic:HmIP-WTH-2:3014F711A0001F9A499D1959:000A9A49A6F9BA:1#SET_POINT_TEMPERATURE" }
Number  ThermostatTechnikkeller_TempAct             "Thermostat Technikkeller Temperatur Act [%.1f°C]"    {channel="homematic:HmIP-WTH-2:3014F711A0001F9A499D1959:000A9A49A6F9BA:1#ACTUAL_TEMPERATURE" }
Number  ThermostatTechnikkeller_Luftfeuchte         "Thermostat Technikkeller Luftfeuchte [%.0f %%]"      {channel="homematic:HmIP-WTH-2:3014F711A0001F9A499D1959:000A9A49A6F9BA:1#HUMIDITY" }

Und default.sitemap:

	Frame label="Arbeitszimmer" {
        Text item=ThermostatArbeitszimmer_TempAct icon="temperature" label="Temperatur aktuell" 
        Text item=ThermostatArbeitszimmer_Luftfeuchte icon="humidity" label="Luftfeuchte"
    }
	Frame label="Technikkeller" {
        Text item=ThermostatTechnikkeller_TempAct icon="temperature" label="Temperatur aktuell" 
        Text item=ThermostatTechnikkeller_Luftfeuchte icon="humidity" label="Luftfeuchte"
    }

Tag 5: Bridge offline: Communication error

Seit einigen Tagen werden keine Daten mehr aktualisiert. Ein Blick in PaperUI > Configuration > Things > eQ3-HmIP-CCU3-App verrät folgendes:

Status: OFFLINE - COMMUNICATION_ERROR java.util.concurrent.ExecutionException: java.util.concurrent.RejectedExecutionException: Max requests queued per destination 1024 exceeded for HttpDestination[http://<IP>]@935fc2,queue=1024,pool=DuplexConnectionPool@1c7fdae[c=2/2,a=2,i=0]

In den Logs steht folgende Zeile:

[hingStatusInfoChangedEvent] - 'homematic:bridge:3014F711A0001F9A499D1959' changed from ONLINE to OFFLINE (COMMUNICATION_ERROR): Connection lost

Tja. Warum die Verbindung zur Bridge verloren wurde, steht da leider nicht. Die erste, schnelle Analyse zeigt mir, dass die Bridge im Netzwerk verfügbar ist, denn ich kann per WebUI normal darauf zugreifen. Und jetzt? Vielleicht hilft es, wenn ich das Binding neu installiere – also mache ich das. Leider hilft das nicht.

Okay, also wird das Binding vernünftig gelöscht und neu installiert. Ich wähle Add-ons > Bindings und drücke Uninstall beim Homematic-Binding. Unter Configuration > Things entferne ich alle Einträge, die irgendwie mit Homematic zusammenhängen. Das hätte ich vielleicht vor dem Löschen des Bindings machen sollen, denn so muss ich für alle Things force-remove wählen. Anschließend starte ich openHAB neu:

sudo systemctl stop openhab2.service
sudo systemctl start openhab2.service

Anschließend istalliere ich das Binding wieder, und auch alle Things, die ich vorher gelöscht hatte. Und wie durch Magie funktioniert es wieder. Das war dann doch recht einfach.

openHAB V: Müllkalender per ical einbinden

This entry is part 5 of 10 in the series openHAB
Geschätzte Lesezeit: < 1 Minute.

Mein Haus soll mich erinnern, wann ich Mülltonnen herausrollen soll. Dazu möchte ich den Abfallkalender, den die Stadt Langenfeld freundlicherweise zur Verfügung stellt, in openHAB einbinden.

Tag 1: Einbindung der ical-Datei

Glücklicherweise gibt es für dieses Projekt eine Anleitung, die ich Schritt-für-Schritt befolgen werde. Als erstes organisiere ich den Link für den Kalender: https://www.langenfeld.de/city_info/display/ical/m_calendar_ical.cfm?region_id=138&year=2020&city_id=298&street_id=8415. Sofort fällt das Fragment year=2020 auf. Das muss ich wohl jährlich ändern, fürchte ich. Nun installiere ich das HTTP-Binding /(binding-http1 – 1.14.0). So weit, so einfach.

Als nächstes werden Items erstellt. Diese lege ich in einer neuen Datei abfall.items an:

String ABFALL_ICAL { http="<[https://www.langenfeld.de/city_info/display/ical/m_calendar_ical.cfm?region_id=138&year=2020&city_id=298&street_id=8415:360000:JS(abfall_heute.js)] }"
Switch ABFALL_AKTIV
String ABFALL_NAME

Nun wird es komplizierter. Also: Komplizierter für das Verständnis, nicht für die tatsächliche Einbindung. Ich kopiere das 138-Zeilen-JavaScript von obiger Anleitung in eine neue Datei /ect/openhab2/transform/abfall_heute.js. Weil ich neugierig bin, schaue ich in die Logs. Aber, oh weh, so einfach funktioniert das wohl doch nicht. Ich bekomme einen Fehler:

2020-05-06 16:19:48.054 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'abfall.items'
2020-05-06 16:19:49.113 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'abfall.items' has errors, therefore ignoring it: [2,1]: missing '}' at 'Switch'

Okay, das kann ich korrigieren - ich habe das entsprechende Snippet (oben) bereits korrigiert. Bei reinem copy-und-paste von meiner Seite sollte dieser Fehler also nicht auftreten - in der Anleitung ist der Fehler (Stand heute) vorhanden. Trotzdem erhalte ich den nächsten Fehler:

[INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model 'abfall.items'
[WARN ] [.core.transform.TransformationHelper] - Cannot get service reference for transformation service of type JS
[WARN ] [ab.binding.http.internal.HttpBinding] - Couldn't transform response because transformationService of type 'JS' is unavailable

Okay, kurzes Suchen verrät mir, dass ich den JavaScript Transformation Service (transformation-javascript - 2.5.3) installieren muss. Also Add-ons > Transformations, installiert.

Es folgen die Rules, wie immer in einer neuen Datei abfall.rules:

rule "Abfallkalender"
when
   Item ABFALL_ICAL changed
then
   //Prüfen ob Abfall abgeholt wird
	if (ABFALL_ICAL.state != "false" && ABFALL_ICAL.state != "Uninitialized"){
      postUpdate(ABFALL_AKTIV,ON)
      postUpdate(ABFALL_NAME,ABFALL_ICAL.state)
   } else {
      postUpdate(ABFALL_AKTIV,OFF)
      postUpdate(ABFALL_NAME,"false")
   }
end

Und dann folgt noch das Snippet für die Sitemap:

    Text item=ABFALL_NAME label="Abholung heute: [%s]" icon="abfall" visibility=[ABFALL_AKTIV==ON]

Als Icon "abfall" nutze ich dieses Icon, welches ich unter icons/classic/ speichere.

Nun sollte der Kalender also funktionieren. Mal sehen, wie es aussieht und was die Logs sagen.

[WARN ] [ab.binding.http.internal.HttpBinding] - Transformation 'JS(abfall_heute.js)' threw an exception. [response=]
org.openhab.core.transform.TransformationException: An error occurred while executing script. TypeError: Cannot read property "events" from undefined in <eval> at line number 120
	at org.openhab.core.transform.TransformationHelper$TransformationServiceDelegate.transform(TransformationHelper.java:71) ~[bundleFile:?]
	at org.openhab.binding.http.internal.HttpBinding.execute(HttpBinding.java:218) [bundleFile:?]
	at org.openhab.core.binding.AbstractActiveBinding$BindingActiveService.execute(AbstractActiveBinding.java:146) [bundleFile:?]
	at org.openhab.core.service.AbstractActiveService$RefreshThread.run(AbstractActiveService.java:169) [bundleFile:?]

Oh-oh. Das klingt nicht gut. Tja. Vielleicht liegt dieser Fehler daran, dass heute kein Müll-Termin vorhanden ist. Ich schaue mir das Ganze morgen nochmal an, denn dann müsste ein Termin angezeigt werden.

Tag 2: Neuer Tag, neuer Fehler

Es gibt gute und schlechte Neuigkeiten. Die gute Neuigkeit ist, dass ich heute den Hinweis auf "Abholung heute:" in der Sitemap sehe. Der zugehörige Sichtbarkeits-Switch ABFALL_AKTIV muss also auf ON gesprungen sein, was wiederum bedeutet, dass ein Kalendereintrag für heute gefunden wurde. Das ist schon mal sehr gut.

Leider ist der Fehler trotzdem noch vorhanden. Als Ergebnis bekomme ich nicht angezeigt, welcher Abfall denn heute abgeholt wird. Anscheinend kann das Event nicht ordentlich ausgelesen werden.

In der Anleitung wird als Quelle für das Javascript dieses Skript genannt. Also kopiere ich dieses ijp-0.6.js, und ändere in den abfall.items die erste Zeile:

String ABFALL_ICAL { http="<[https://www.langenfeld.de/city_info/display/ical/m_calendar_ical.cfm?region_id=138&year=2020&city_id=298&street_id=8415:360000:JS(ijp-0.6.js)]" }

Ein Blick in die Logs zeigt diese Zeilen:

[vent.ItemStateChangedEvent] - ABFALL_ICAL changed from  to null
[vent.ItemStateChangedEvent] - ABFALL_NAME changed from  to null

Naja, immerhin keine Exception mehr.

Tag 3: Andere Strategie - CalDAV

So richtig funktioniert das nicht. Ich befürchte, dass meine Stadt die ics-Datei irgendwie nicht so ganz sauber zum Download anbietet, und dass die obigen Skripte deshalb die ics-Datei nicht ordentlich parsen können. Dummerweise habe ich zu wenig Ahnung von Parsen und JavaScript, sodass ich es auch nicht reparieren kann. Also mache ich es anders.

Ich synchronisiere meine Kalender ja sowieso per CalDAV. Und es gibt ein CalDAV-Binding für obenHAB. Und eine Anleitung gibt es auch. Dies probiere ich jetzt. Ich installiere also binding-caldav-personal1 (1.14.0).

Analog zur Anleitung bearbeite ich die Datei services/caldavio.cfg und ergänze folgende Zeilen am Ende:

caldavio:Muellkalender:url=<url>
caldavio:Muellkalender:username=<Username>
caldavio:Muellkalender:password=<Passwort>
caldavio:Muellkalender:reloadInterval=1440
caldavio:Muellkalender:preloadTime=20000
caldavio:Muellkalender:disableCertificateVerification=true

Nun lösche ich den ganzen nicht funktionierenden Kram (d.h. alles) aus abfall.items und ergänze:

String CalDav_Muelltonne   "[%s]"  <calendar> { caldavPersonal="calendar:Muellkalender type:UPCOMING eventNr:1 value:NAME" }
DateTime CalDav_Date "Datum der Abholung [%1$td.%1$tm.%1$tY]"    <calendar&g t;   { caldavPersonal="calendar:Muellkalender type:UPCOMING eventNr:1 value:START" }

Und ein Snippet in die default.sitemap, welches den vorigen Kram ersetzt:

    Frame {
        Text item=CalDav_Muelltonne
        Text item=CalDav_Date
    }

Tja, nur leider funktioniert auch das nicht. Hier ist der Log:

[vent.ItemStateChangedEvent] - CalDav_Muelltonne changed from NULL to UNDEF
[vent.ItemStateChangedEvent] - CalDav_Date changed from NULL to UNDEF

Also nutze ich Google. Dabei finde ich einige Dinge heraus: Zuerst einmal bin ich nicht der Einzige mit diesem Fehler. Leider wird hier keine echte Lösung vorgeschlagen, aber zumindest stelle ich fest, dass ich unter /var/lib/openhab2/etc/caldav einen Ordner Muellkalender habe, indem sich viele .ics-Dateien befinden. Das Binding funktioniert also, das Problem muss in der Item-Definition sein.

Tag 4: Möglicherweise funktioniert es

Der Titel ist bewusst vorsichtig gewählt, aber ich glaube, es funktioniert jetzt. Kommen wir zuerst zu den vermeidbaren Fehlern meinerseits.

In der Datei caldavPersonal.cfg ergänze ich folgende Zeile:

caldavPersonal:usedCalendars=caldavio:Muellkalender

In der Datei caldavCommand.cfg ergänze ich folgende Zeile:

caldavCommand:readCalendars=caldavio:Muellkalender

Das hätte ich beides schon längst machen sollen. Anschließend spiele ich etwas mit der caldavio.cfg herum. Ich weiß nicht genau, was ich alles ausprobiert habe, aber hiermit funktioniert es (derzeit):

caldavio:Muellkalender:url=<Url>
caldavio:Muellkalender:username=<Username>
caldavio:Muellkalender:password=<Passwort>
caldavio:Muellkalender:reloadInterval=1440
caldavio:Muellkalender:preloadTime=7000
caldavio:Muellkalender:disableCertificateVerification=true
#caldavio:Muellkalender:lastModifiedFileTimeStampValid=false

Als nächstes werden Traces für org.openhab.binding.caldav_personal, org.openhab.binding.caldav_command, und org.openhab.io.caldav aktiviert (Anleitung hier). Dies führte mich zu folgendem Problem:

[TRACE] [caldav.internal.job.EventReloaderJob] - loadFrom = 2020-05-11T11:17:11.677+01:00
[TRACE] [caldav.internal.job.EventReloaderJob] - loadTo = 2020-05-25T08:37:11.678+01:00
[TRACE] [caldav.internal.job.EventReloaderJob] - loading event: e7f8b797-ac65-403f-806f-e99efcc870f0:Restmüll graue Tonne
[DEBUG] [caldav.internal.job.EventReloaderJob] - Processing event 'Restmüll graue Tonne'
[DEBUG] [caldav.internal.job.EventReloaderJob] - No periods exist for event 'Restmüll graue Tonne'
[DEBUG] [caldav.internal.job.EventReloaderJob] - changing eventcontainer last modified to 2020-05-06T16:02:33.000+01:00
[TRACE] [.io.caldav.internal.CalDavLoaderImpl] - listeners for events: 2
[TRACE] [caldav.internal.job.EventReloaderJob] - eventContainer found: true
[TRACE] [caldav.internal.job.EventReloaderJob] - last resource modification: 2020-05-06T16:02:13.000+01:00
[TRACE] [caldav.internal.job.EventReloaderJob] - last change of already loaded event: 2020-05-06T16:02:13.000+01:00
[DEBUG] [caldav.internal.job.EventReloaderJob] - loading resource: <Url> (FSchangedTS not valid)
[TRACE] [caldav.internal.job.EventReloaderJob] - Raw URL: <url>
[TRACE] [caldav.internal.job.EventReloaderJob] - URL after encoding: <url>

Da steht No periods exist for event , und das hat mich stutzig gemacht. Und dann habe ich viel Hexerei betrieben. Die genaue Reihenfolge weiß ich nicht mehr, aber ich habe unter anderem die Bindings deinstalliert, den Cache geleert, und die Bindings neu installiert (Anleitung hier). Und ich habe in meinem CalDAV-Server (ownCloud) einen neuen Kalender angelegt, einen Kalendereintrag dorthin verschoben, und das Binding auf den neuen Kalender verwiesen. Und ich habe für einige Kalendereinträge die Dauer von 0 Minuten auf 1 Stunde verändert. Und irgendwie hat es dann geklappt. Ich vermute, dass vor allem die letzte Änderung, den Events einen Zeitraum zu geben, sehr wichtig war. Jedenfalls sehen die Traces jetzt so aus:

[DEBUG] [.io.caldav.internal.CalDavLoaderImpl] - return event list for CalDavQuery [calendarIds=[Muellkalender], from=2020-05-11T11:52:45.848+01:00, to=null, sort=ASCENDING, filterName=null] with 1 entries
[DEBUG] [ldav_personal.internal.CalDavBinding] - found 1 events for config: CalDavPresenceConfig [calendar=[Muellkalender], type=UPCOMING, eventNr=1, value=START, filterName=null, categoriesFiltersAny=false]
[TRACE] [ldav_personal.internal.CalDavBinding] - found event 19874479-6f54-45db-8d44-ddeddf80d04c(Restmüll graue Tonne@14.05.2020/02:00-14.05.2020/03:00) for config CalDavPresenceConfig [calendar=[Muellkalender], type=UPCOMING, eventNr=1, value=START, filterName=null, categoriesFiltersAny=false]
[DEBUG] [ldav_personal.internal.CalDavBinding] - sending command 2020-05-14T02:00:00 for item CalDav_Date
[TRACE] [ldav_personal.internal.CalDavBinding] - command 2020-05-14T02:00:00 successfully sent
[DEBUG] [ldav_personal.internal.CalDavBinding] - found 1 events for config: CalDavPresenceConfig [calendar=[Muellkalender], type=EVENT, eventNr=1, value=NAME, filterName=null, categoriesFiltersAny=false]
[TRACE] [ldav_personal.internal.CalDavBinding] - found event 19874479-6f54-45db-8d44-ddeddf80d04c(Restmüll graue Tonne@14.05.2020/02:00-14.05.2020/03:00) for config CalDavPresenceConfig [calendar=[Muellkalender], type=EVENT, eventNr=1, value=NAME, filterName=null, categoriesFiltersAny=false]
[DEBUG] [ldav_personal.internal.CalDavBinding] - sending command Restmüll graue Tonne for item CalDav_Muelltonne
[TRACE] [ldav_personal.internal.CalDavBinding] - command Restmüll graue Tonne successfully sent
[DEBUG] [ldav_personal.internal.CalDavBinding] - found 1 events for config: CalDavPresenceConfig [calendar=[Muellkalender], type=UPCOMING, eventNr=1, value=NAME, filterName=null, categoriesFiltersAny=false]
[TRACE] [ldav_personal.internal.CalDavBinding] - found event 19874479-6f54-45db-8d44-ddeddf80d04c(Restmüll graue Tonne@14.05.2020/02:00-14.05.2020/03:00) for config CalDavPresenceConfig [calendar=[Muellkalender], type=UPCOMING, eventNr=1, value=NAME, filterName=null, categoriesFiltersAny=false]
[DEBUG] [ldav_personal.internal.CalDavBinding] - sending command Restmüll graue Tonne for item CalDav_NaechsteMuelltonne
[TRACE] [ldav_personal.internal.CalDavBinding] - command Restmüll graue Tonne successfully sent

Abschließend verschiebe ich alle Einträge in meinen neuen Kalender, und stelle sicher, dass alle auch eine echte Dauer haben.

Dann kann ich mich ja jetzt darum kümmern, dass das Ganze in meiner Sitemap auch so aussieht, wie ich es gerne hätte. Dazu installiere ich als erstes das NTP-Binding (binding-ntp - 2.5.3), da mir dieses die aktuelle Zeit ausgibt. Anschließend ergänze ich in der abfall.items:

Switch Abfall_Aktiv
String Abfall_Name
DateTime date_today "Heute [%1$td.%1$tm.%1$tY]"   <calendar>  { channel="ntp:ntp:local:dateTime" }

Es folgt abfall.rules, eine leicht modifizierte Variante von oben:

rule "Abfallkalender"
when
   Item CalDav_Date changed
then
   //Prüfen ob Abfall abgeholt wird
	if (CalDav_Date == date_today){
      postUpdate(Abfall_Aktiv,ON)
      postUpdate(Abfall_Name,CalDav_NaechsteMuelltonne)
   } else {
      postUpdate(Abfall_Aktiv,OFF)
      postUpdate(Abfall_Name,"false")
   }
end

Eigentlich habe ich nur den Vergleich angepasst. Hoffentlich führt dieser Vergleich dazu, dass mir immer dann Mülltonnen angezeigt werden, wenn im Kalender ein Eintrag für den aktuellen Tag vorhanden ist. Und nun noch die Sitemap:

    Frame {
	Text item=Abfall_Name label="Abholung heute: [%s]" icon="abfall" visibility=[Abfall_Aktiv==ON]
    }

Ob das alles wirklich so klappt, werde ich erst in ein paar Tagen sehen. Aber bisher sieht es gut aus.

Tag 4: Kontrolle - Bisher funktioniert es

Grundsätzlich funktioniert es. Ich bekomme regelmäßig die Abholtemine angezeigt - und zwar, wie gewünscht, immer den nächsten Termin.

Leider klappt es aber nicht, dass ich immer nur den aktuellsten Abholtermin an dem Abholtag angezeigt bekomme. Daran ist vermutlich mein Kalendereintrag Schuld. Alle Abholtermine sind früh morgens, von 03:00 bis 04:00 Uhr hinterlegt. Das bedeutet, dass der Vergleich eines UPCOMING-Events mit dem aktuellen Tag nur nachts von 00:00 bis 03:00 positiv ist, und nur um diese Zeit sichtbar ist. Das ist natürlich doof, aber ich wüsste nicht, wie es anders gehen soll. Vielleicht muss ich hier auf Warnhinweise oder Alerts umsteigen, aber das ist ein Problem für einen anderen Tag.

Tag 5: Notification

Wie bereits angesprochen, möchte ich Benachrichtigungen auf mein Handy erhalten, wenn eine Mülltonne abgeholt wird. Die einfachste Variante wäre, den Kalender auf dem Handy zu abonnieren und einfach dort Termin-Erinnerungen zu aktivieren, aber ich möchte es gerne via openHAB ausprobieren.

Es gibt, wie so oft, eine Anleitung für Benachrichtigungen. Dort wird als erster Schritt erwähnt, dass an sein openHAB mit myopenhab.com verbinden solle, mit einer genauen Anleitung hier. Also installiere ich das Binding openHAB Cloud Connector (misc-openhabcloud - 2.5.3). Anschließend navigiere ich zu Configuration > Services > openHAB Cloud > Configure und ändere den Zugriff auf Notifications. Remote-Zugriff möchte ich (noch) nicht.

Es folgt die Registrierung auf myopenhab.org. Dazu benötige ich UUID und ein Passwort, die ich aus /var/lib/openhab2/uuid und /var/lib/openhab2/openhabcloud/secret extrahiere - und natürlich einen Benutzernamen und ein Passwort. Nun wird mir meine openHAB-Instanz als offline angezeigt - wenig verwunderlich, da ich ja keinen Remote-Zugriff erlaube.

Weiter geht es. Ich möchte testen, ob die Benachrichtigungen funktionieren. Dazu lege ich mir testweise benachrichtigung.items an:

Switch  Send_via_App_Switch         "Benachrichtigung per App"      

Außerdem ergänze ich in der Sitemap:

    Frame {
        Switch item=Send_via_App_Switch
    }

Und nun lege ich benachrichtigung.rules mit einer Testnachricht an:

rule "Sende Benachrichtigung"
when
    Item Send_via_App_Switch changed
then
    if(Send_via_App_Switch.state == ON)
    {
        sendNotification("<E-Mail-Adresse>", "Dies ist eine Benachrichtigung.")

        Send_via_App_Switch.postUpdate(OFF)
    }
end

Natürlich funktioniert dies nicht direkt. Naja, ich habe vergessen, die openHAB-App auf meinem Smartphone mit dem Account auf myopencloud.org zu verbinden. Das hole ich nach, und schon funktioniert es.

Sehr gut. Aber eigentlich möchte ich ja etwas anderes. Eigentlich möchte ich ja, dass mir die nächste Mülltonne zur Abholung angezeigt wird. Also ändere ich Zeile 7 der benachrichtigung.rules:

        sendNotification("<E-Mail-Adresse>", "Abholung heute: " + CalDav_NaechsteMuelltonne.state)

Das funktioniert tatsächlich. Ich bin begeistert. Nun überarbeite ich meine abfall.rules:

rule "Abfallkalender"
when
   Item CalDav_Date changed
then
   //Prüfen ob Abfall abgeholt wird
    if (CalDav_Date == date_today){
        sendNotification("<E-Mail-Adresse>", "Abholung heute: " + CalDav_NaechsteMuelltonne.state)
   } 
end

Ob dies tatsächlich funktioniert, werde ich erst in ein paar Tagen wissen, wenn tatsächlich etwas abgeholt wird.

Tag 6: Notifications, Teil 2

Leider funktionieren die Benachrichtigungen nicht. Möglicherweise liegt es daran, dass die Events nachts um 03:00 Uhr aktiv werden, das Item aber mittags aktualisiert wird. Dann wäre CalDav_Date bereits der nächste Tag, und somit würde der Vergleich CalDav_Date == date_today niemals TRUE werden. Das ist nur eine Vermutung, jedenfalls probiere ich es jetzt anders.

Die Idee, die ich jetzt verfolge ist, dass ich eine Benachrichtigung erhalte, wenn ein Event auf aktiv geschaltet wird. Das sollte dazu führen, dass ich nachts eine Nachricht bekomme, und dann morgens sehe, dass ich die Mülltonne bereitstellen muss. Also ändere ich in der abfall.items die zweite Zeile:

String CalDav_Muelltonne   "[%s]"   { caldavPersonal="calendar:'Muellkalender' type:'ACTIVE' eventNr:'1' value:'NAME'" }

Und ich ändere die abfall.rules:

rule "Abfallkalender"
when
   Item CalDav_Muelltonne changed
then
   sendNotification("<E-Mail-Adresse>", "Abholung heute: " + CalDav_Muelltonne.state) 
end

Mal schauen, ob es diesmal klappt. Falls ja, muss ich die Sitemap noch bearbeiten. Und vielleicht ist die Bedingung Item CalDav_Muelltonne changed noch zu generisch, ich könnte mir vorstellen, dass ich hier zwei Benachrichtigungen erhalte: Eine zu Beginn und eine am Ende des Events. Wenn das so wäre, wäre dies vermutlich leicht zu beheben - ich würde mich freuen, wenn ich überhaupt Nachrichten bekomme.

Okay, direkt bei Upload erhalte ich eine Nachricht: Abholung heute: UNDEF. Okay, es hat also geklappt, aber diesen Fehler möchte ich direkt abfangen. Ich füge eine kleine Bedingung in meine abfall.rules ein:

rule "Abfallkalender"
when
   Item CalDav_Muelltonne changed
then
   if (CalDav_Muelltonne.state != UNDEF) {
      sendNotification("<E-Mail-Adresse>", "Abholung heute: " + CalDav_Muelltonne.state) 
   }
end

Erneut, mal sehen, ob dies funktioniert. Ich bin ausnahmsweise optimistisch.

Tag 7: Es funktioniert!

Folgende Meldung fand sich heute Morgen auf meinem Smartphone:

2020-07-12_abfallkalender-notification

Hurra, es funktioniert!

Schlussworte

Diese Implementierung funktionierte tatsächlich verlässlich, bis ich auf openHAB 3.0 umgestiegen bin. Dort gibt es das bisherige Caldav-Binding nicht mehr, also musste ich von vorne beginnen. Meine Bemühungen sind hier dokumentiert.

openHAB VI: Beleuchtung mit Philips Hue

This entry is part 6 of 10 in the series openHAB
Geschätzte Lesezeit: 4 Minuten.

Ich möchte das Licht im Haus steuern. Dabei geht es mir weniger um eine Simulation von Anwesenheit. Viel lieber möchte ich, dass die Helligkeit des Tageslichtes erkannt wird und die Lampen automatisch so hell gedreht werden, dass die Helligkeit im Raum konstant ist.

Das Ganze soll mit Philips Hue laufen. Dazu habe ich mir schonmal die zentrale Hue Bridge1Im Set mit zwei E27-LEDs bei amazon.de für 95,76€. und vier E27-LEDs2Zweimal amazon.de für je 39,99€, die anderen beiden wurden im Set mit der Bridge gekauft. bestellt. Die LEDs können kalt- und warmweiß sowie 16 Millionen Farben darstellen. Ich bin gespannt, was man damit für lustige Beleuchtungsszenarien erstellen kann. Und vier Pillar-Spots habe ich auch bestellt.3amazon.de; je 48,99€. Tja, und dann gab es da noch die GU10-LEDs im Angebot, also habe ich auch davon noch zwölf bestellt.4proshop.de; 29,83€ pro Stück. Die möchte ich in die Pillar-Spots und in bereits vorhandene Lampen einbauen.

Tag 1: Setup

Die Montage der Spots und der Einbau der Leuchten war genau so einfach oder schwer, wie es bei Lampen immer der Fall ist. Ich hasse Über-Kopf-Arbeit, dementsprechend habe ich geflucht. Aber dies ist keine Handwerker-Blogserie, sondern eine über openHAB. Daher komme ich nun zum Thema und merke an, dass ich mir vor der Montage sämtliche Seriennummern der Leuchtmittel aufgeschrieben habe. Ich habe nämlich irgendwo gelesen, dass die Verknüpfung der Leuchtmittel mit der Bridge nicht immer reibungslos funktioniert, und dass man dann die Seriennummer benötigt.

Die Bridge mit den Leuchten zu verknüpfen ist extrem einfach. Der erste Schritt ist, die Hue-App mit der Bridge zu verknüpfen, dazu folge ich einfach der Anleitung in der Hue-App. Alle Leuchtmittel werden auch direkt gefunden, auch dies ist wunderbar einfach. Ich füge alle Leuchtmittel zum Raum „Wohnzimmer“ hinzu, und definiere darin zwei Zonen, „Deckenleuchte 1“ und „Spots“. Das ist alles völlig simpel und funktioniert einfach und ohne Probleme, ich bin begeistert.

Auch die Verknüpfung mit openHAB ist einfach. Die Leuchten tauchen direkt in der Inbox auf, und ich füge die Leuchten als Things hinzu. Zu mehr fehlt mir erst einmal die Zeit.

Tag 2: Anwesenheitssimulation

Für einen Urlaub (der in der Vergangenheit liegt, ich dokumentiere dies erst nach dem Urlaub) möchte ich, dass meine Spots zu bestimmten Zeiten an- und wieder ausgeschaltet werden. Ich möchte, dass das Haus von außen teilweise beleuchtet aussieht.

Als erstes benötige ich ein Item, genauer gesagt einen Switch (hue.items):

Switch Hue_Wohnzimmer_Spots "Hue Wohnzimmer Spots" { channel="hue:group:ecb5fa0b0612:3:switch" }

Und dann muss ich diesen Switch noch irgendwie bedienen. Irgendwie? Naja, ich möchte ja bestimmte Uhrzeiten, das bedeutet: Ich möchte einen Cronjob. Die entsprechenden Ausdrücke generiere ich hier: http://www.cronmaker.com/?0. Und so sieht die entsprechende hue.rules aus:

rule "Simuliere Anwesenheit Licht an"
when
   Time cron "0 17 18 ? * * *"
then
   Hue_Wohnzimmer_Spots.sendCommand(ON)
end

rule "Simuliere Anwesenheit Licht aus"
when
   Time cron "0 44 21 ? * * *"
then
   Hue_Wohnzimmer_Spots.sendCommand(OFF) 
end

Dabei musste ich beachten, dass mein openHAB irgendwie auf Winterzeit steht. 18:17 entspricht also 19:17 Sommerzeit, und somit gehen die Lichter um 19:17 an. Das hat eine Weile gedauert, bis ich diesen Fehler gefunden hatte. In der Zwischenzeit war ich verwundert, warum die blöden Lampen nicht angingen. (Update: Dieses Problem der falschen Uhrzeit habe ich mittlerweile gelöst.)

Und so sieht es in den Logs aus:

[ome.event.ItemCommandEvent] - Item 'Hue_Wohnzimmer_Spots' received command ON
[nt.ItemStatePredictedEvent] - Hue_Wohnzimmer_Spots predicted to become ON
[vent.ItemStateChangedEvent] - Hue_Wohnzimmer_Spots changed from NULL to ON

Warum da in der mittleren Zeile etwas predicted wird, ist mir schleierhaft. Aber es funktioniert, das genügt mir.

openHAB VII: Waschmaschine und Trockner melden sich, wenn sie fertig sind

This entry is part 7 of 10 in the series openHAB
Geschätzte Lesezeit: 7 Minuten.

In unserem Haus stehen Waschmaschine und Trockner im Keller. Möchte ich wissen, ob eines der Geräte fertig ist, muss ich also in den Keller laufen. Das ist unpraktisch. Da muss es doch etwas besseres geben.

Natürlich gibt es etwas besseres. Es gibt dieses tolle Tutorial.

Tag 1: Installation, Items, Regeln

Das Konzept ist relativ simpel. Ich schließe die Waschmaschine und den Trockner jeweils an eine Steckdose, die den Verbrauch messen kann. Wenn der Verbrauch von einer hohen Last auf eine sehr niedrige Last fällt, muss der Waschgang fertig sein. Dann bekomme ich eine Nachricht.

Ich installiere an Waschmaschine und Trockner jeweils eine Schalt-Mess-Steckdose von Homematic IP, HmIP-PSM.1Bei amazon für 44,99€ pro Stück gekauft. Anschließend definiere ich die Items in homematic.items:

// Waschkeller
// HmIP-PSM_Waschmaschine
Number  SteckdoseWaschmaschine_Power  "Waschmaschine Verbrauch [%.1f W]" {channel="homematic:HMIP-PSM:3014F711A0001F9A499D1959:0001DBE9913DBB:6#POWER" }
Number Waschmaschine_Status "Waschmaschine [%d]"

// HmIP-PSM_Trockner
Number  SteckdoseTrockner_Power  "Trockner Verbrauch [%.1f W]" {channel="homematic:HMIP-PSM:3014F711A0001F9A499D1959:0001DA49A3492E:6#POWER" }
Number Trockner_Status "Trockner [%d]"

Es folgt die waschen.rules:

import java.util.concurrent.locks.ReentrantLock

val Number MODE_OFF = 0
val Number MODE_STANDBY = 1
val Number MODE_ACTIVE = 2
val Number MODE_FINISHED = 3

val ReentrantLock Waschmaschine_lock = new ReentrantLock()
val ReentrantLock Trockner_lock = new ReentrantLock()

rule "Status Waschmaschine"
when
    Item SteckdoseWaschmaschine_Power changed
then
    if (SteckdoseWaschmaschine_Power.state < 0.5) Waschmaschine_Status.postUpdate(MODE_OFF)
    else if (SteckdoseWaschmaschine_Power.state > 10) Waschmaschine_Status.postUpdate(MODE_ACTIVE)
    else if (SteckdoseWaschmaschine_Power.state < 4.5) {
        if (Waschmaschine_Status.state == MODE_OFF) Waschmaschine_Status.postUpdate(MODE_STANDBY)
        else if (Waschmaschine_Status.state == MODE_ACTIVE) {
			Waschmaschine_lock.lock()
			try {
				Thread::sleep(10000) // Debounce for 10 seconds
				if (SteckdoseWaschmaschine_Power.state < 4.5) { 
					Waschmaschine_Status.postUpdate(MODE_FINISHED)
					sendNotification("mail@dresse", "Waschmaschine ist fertig!")
				}
			} finally {
				Waschmaschine_lock.unlock()
			}
		}
    }
end

rule "Status Trockner"
when
    Item SteckdoseTrockner_Power changed
then
    if (SteckdoseTrockner_Power.state < 0.5) Trockner_Status.postUpdate(MODE_OFF)
    else if (SteckdoseTrockner_Power.state > 10) Trockner_Status.postUpdate(MODE_ACTIVE)
    else if (SteckdoseTrockner_Power.state < 4.5) {
        if (Trockner_Status.state == MODE_OFF) Trockner_Status.postUpdate(MODE_STANDBY)
        else if (Trockner_Status.state == MODE_ACTIVE) {
			Trockner_lock.lock()
			try {
				Thread::sleep(10000) // Debounce for 10 seconds
				if (SteckdoseTrockner_Power.state < 4.5) {
					Trockner_Status.postUpdate(MODE_FINISHED)
					sendNotification("mail@dresse", "Trockner ist fertig!")
				}
			} finally {
				Trockner_lock.unlock()
			}
		}
    }
end

Diese rules-datei ist im Prinzip copy-paste von obiger Anleitung. Okay, ich habe die entsprechende Regel verdoppelt, weil ich ja Waschmaschine und Trockner habe. Ansonsten habe ich nur noch das Lock verändert, indem ich den Import an den Anfang der Datei gesetzt habe. Das war nötig, weil ich sonst folgenden Fehler erhalten hatte:

[WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'waschen.rules' has errors, therefore ignoring it: [6,1]: missing EOF at 'import'

Zum Schluss noch die default.sitemap:

Frame label="Waschküche" {
        Text item=Waschmaschine_Status icon="whitegood" label="Waschmaschine" 
        Text item=Trockner_Status icon="whitegood" label="Trockner"
    }

Und dann schauen wir mal, ob es funktioniert.

Tag 2: Feinschliff

2020-09-21_trockner-notification

Es funktioniert.

2020-09-21_waschmaschine-notification

Öh. Es funktioniert zu gut. Das sid zu viele Nachrichten. Ich muss die Grenzen, an denen sich der Status ändert, etwas anpassen. So sieht die waschen.rules jetzt aus:

import java.util.concurrent.locks.ReentrantLock

val Number MODE_OFF = 0
val Number MODE_STANDBY = 1
val Number MODE_ACTIVE = 2
val Number MODE_FINISHED = 3

val ReentrantLock Waschmaschine_lock = new ReentrantLock()
val ReentrantLock Trockner_lock = new ReentrantLock()

rule "Status Waschmaschine"
when
    Item SteckdoseWaschmaschine_Power changed
then
    if (SteckdoseWaschmaschine_Power.state < 0.5) Waschmaschine_Status.postUpdate(MODE_OFF)
    else if (SteckdoseWaschmaschine_Power.state > 200) Waschmaschine_Status.postUpdate(MODE_ACTIVE)
    else if (SteckdoseWaschmaschine_Power.state < 2.2) {
        if (Waschmaschine_Status.state == MODE_OFF) Waschmaschine_Status.postUpdate(MODE_STANDBY)
        else if (Waschmaschine_Status.state == MODE_ACTIVE) {
			Waschmaschine_lock.lock()
			try {
				Thread::sleep(100000) // Debounce for 100 seconds
				if (SteckdoseWaschmaschine_Power.state < 2.2) { 
					Waschmaschine_Status.postUpdate(MODE_FINISHED)
					sendNotification("andreas@kolmer.one", "Waschmaschine ist fertig!")
				}
			} finally {
				Waschmaschine_lock.unlock()
			}
		}
    }
end

rule "Status Trockner"
when
    Item SteckdoseTrockner_Power changed
then
    if (SteckdoseTrockner_Power.state < 0.5) Trockner_Status.postUpdate(MODE_OFF)
    else if (SteckdoseTrockner_Power.state > 200) Trockner_Status.postUpdate(MODE_ACTIVE)
    else if (SteckdoseTrockner_Power.state < 200) {
        if (Trockner_Status.state == MODE_OFF) Trockner_Status.postUpdate(MODE_STANDBY)
        else if (Trockner_Status.state == MODE_ACTIVE) {
			Trockner_lock.lock()
			try {
				Thread::sleep(10000) // Debounce for 10 seconds
				if (SteckdoseTrockner_Power.state < 200) {
					Trockner_Status.postUpdate(MODE_FINISHED)
					sendNotification("andreas@kolmer.one", "Trockner ist fertig!")
				}
			} finally {
				Trockner_lock.unlock()
			}
		}
    }
end

Das hat geklappt, jetzt kriege ich nur noch eine Nachricht, wenn die Waschmaschine fertig ist.

Nächster Punkt: Die Sitemap ist noch nicht ganz perfekt. Da steht jetzt für die Waschmaschine eine Zahl als Status, also entweder 0, 1, 2 oder 3. Ich möchte aber viel lieber, dass da "Aus" oder "Standby" steht. Da es wie immer tolle Anleitungen gibt, hoffe ich, dass dieser Schritt einfach ist.

Ich bearbeite also meine homematicip.items:

// Waschkeller
// HmIP-PSM_Waschmaschine
Number  SteckdoseWaschmaschine_Power  "Waschmaschine Verbrauch [%.1f W]" {channel="homematic:HMIP-PSM:3014F711A0001F9A499D1959:0001DBE9913DBB:6#POWER" }
Number Waschmaschine_Status "Waschmaschine [%d]"
String Waschmaschine_Anzeige "[MAP(waschen.map):%s]"

// HmIP-PSM_Trockner
Number  SteckdoseTrockner_Power  "Trockner Verbrauch [%.1f W]" {channel="homematic:HMIP-PSM:3014F711A0001F9A499D1959:0001DA49A3492E:6#POWER" }
Number Trockner_Status "Trockner [%d]"
String Trockner_Anzeige "[MAP(waschen.map):%s]"

Und dann erstelle ich noch eine transform/waschen.map:

0=Aus
1=Standby
2=Läuft
3=Fertig
-=-
undefined=-
uninitialized=-
NULL=-

Und schon erhalte ich einen Fehler in den Logs:

[WARN ] [rest.core.item.EnrichedItemDTOMapper] - Failed transforming the state 'NULL' on item 'Waschmaschine_Anzeige' with pattern 'MAP(waschen.map):%s': Couldn't transform value because transformation service of type 'MAP' is not available.
[WARN ] [ui.internal.items.ItemUIRegistryImpl] - couldn't transform value in label because transformationService of type 'MAP' is unavailable
[WARN ] [rest.core.item.EnrichedItemDTOMapper] - Failed transforming the state 'NULL' on item 'Trockner_Anzeige' with pattern 'MAP(waschen.map):%s': Couldn't transform value because transformation service of type 'MAP' is not available.
[WARN ] [ui.internal.items.ItemUIRegistryImpl] - couldn't transform value in label because transformationService of type 'MAP' is unavailable

Tja, das wäre auch zu einfach gewesen. Aber auch für dieses Problem gibt es eine Lösung: Ich muss das Addon "MAP" installieren. Sonst geht das nicht. Also wähle ich in der PaperUI Addons > Transformations das Addon Map Transformation (transformation-map - 2.5.3). Und schon gibt es keine Fehlermeldungen mehr.

Okay, noch schnell die default.sitemap überarbeiten:

	Frame label="Waschküche" {
        Text item=Waschmaschine_Anzeige icon="whitegood" label="Waschmaschine" 
        Text item=Trockner_Anzeige icon="whitegood" label="Trockner"
    }

Sollte klappen, oder? Nein, leider nicht. Die Anzeige funktioniert nicht. Keine Ahnung, woran dies liegt. Da muss ich noch etwas basteln.

openHAB VIII: Update auf openHAB 3.0

This entry is part 8 of 10 in the series openHAB
Geschätzte Lesezeit: 7 Minuten.

Es gibt eine neue Version, und ich möchte auf openHAB 3 aktualisieren. Vielleicht lösen sich so die Probleme mit homematic von selbst.

Also gut. Ich folge dieser Anleitung. Die sieht nämlich sehr leicht aus. Spoiler: Leider wird sich herausstellen, dass es nicht ganz so einfach ist.

Tag 1: Erst einmal alles kaputtmachen

Tolle Überschrift, oder?

Die folgenden Schritte werden alle per SSH in der Konsole durchgeführt. Zuerst mache ich ein Backup, mit diesem Befehl:

sudo $OPENHAB_RUNTIME/bin/backup

Dann öffne ich das openHAB-Konfigurations-Tool:

sudo openhabian-config

Mich begrüßt direkt diese Meldung:
2021-01-02-update-1

Wenn das Update empfohlen ist, dann mache ich das doch direkt. Ich wähle also Continue, und das System rattert vor sich hin.

Nach ein paar Augenblicken kommt der nächste Screen:
2021-01-02-update-2

Öh, ja, ich möchte openHAB 3. Also wähle ich mit dem Cursor den unteren Branch, und drücke Enter.

Es folgt das Hauptmenü. Ich wähle den zweiten Menüpunkt Upgrade System.
2021-01-02-update-3

Das System arbeitet jetzt. Nach einer Weile erhalte ich folgenden Dialog:
2021-01-02-update-4

Öh, ja, also wenn ich Java 11 brauche, dann soll das bitte auch installiert werden. Continue.

Anschließend geht es zurück in das hauptmenü. Hier wähle ich nun Punkt 3, Install openHAB. In der beschreibung steht ja, dass hier openHAB 3 installiert wird, also sollte das passen.
2021-01-02-update-5

Als nächstes werde ich gefragt, ob ich ein Backup anlegen möchte. Das habe ich zwar schon vorhin manuell gemacht, aber es schadet ja nicht. Continue.
2021-01-02-update-6

Und eine Warnmeldung, dass das Update zu openHAB 3 nicht rückgängig zu machen ist. Jaja, Continue.
2021-01-02-update-7

Jetzt rödelt das System. Nach einer Weile erhalte ich eine Erfolgsmeldung:
2021-01-02-update-10

Und direkt danach folgt eine Fehlermeldung:
2021-01-02-update-8

Das ist seltsam. Mal sehen, ob es funktioniert hat. Ich gebe http://openhab/8080 in den Browser ein.

2021-01-02-update-8-2

Okay, da ist also etwas kaputt. Ein Reboot (sudo reboot) hilft nichts. Eine schnelle Google-Recherche führt mich zu dieser Diskussion. Hier wird der Fehler genau beschrieben, aber es wird gesagt, dass das ab openHAB 2.5.1 behoben sein sollte. Ich habe doch openHAB 3 installiert?

Ich gehe also erneut per SSH auf den RasPi. Der Begrüßungsdialog von openhabian sagt aber immernoch, dass ich openHAB 2.5 installiert hätte. Seltsam. Da hat wohl etwas nicht geklappt.

Also nochmal.

sudo openhabian-config

Diesman wähle ich Menüpunkt 1, Select Branch. Und siehe da, ich habe angeblich noch den stable-Branch ausgewählt. Ich muss das Sternchen auf openHAB3 verschieben, und erst dann Enter drücken. Aber wie geht das? Keine Ahnung. Ich drücke wild auf meiner Tastatur herum, und irgendwann ist das Sternchen an der richtigen Stelle. Ich glaube, die Leertaste war richtig, aber ich bin mir nicht sicher. Egal.

2021-01-02-update-9

Weiter geht es. Punkt 3, Install openHAB.
2021-01-02-update-10

Diesmal erhalte ich nur die Erfolgsmeldung, und keine Fehlermeldung. Und der Screen per SSH sieht so aus:

2021-01-02-update-11

Das ist doch super! Erstmal rebooten (sudo reboot), und dann auf zu http://openhab/8080:
2021-01-02-update-8-2

Verdammt.

Okay, die obige Lösung sagte, man müsse openHAB einfach nur löschen und neu installieren. Das würde ich gerne vermeiden, vielleicht geht es auch anders.

Eine andere Lösung sagt, man solle den Cache leeren.

openhab-cli stop
openhab-cli clean-cache
openhab-cli start

Und nun geht es erneut zu http://openhab/8080: Die Website ist nicht erreichbar. ERR_CONNECTION_REFUSED. Verdammt.

Tag 2: Reparieren

Sieh mal einer an, jemand anderes hat den gleichen Fehler wie ich. Die Lösung: Zwinge das System, Java 11 zu nutzen. Nur wie das geht, steht hier leider nicht.

Besser gesagt, da steht, derjenige habe es über das openHAB-Konfigurations-Tool geschafft:

sudo openhabian-config

Okay, ich wähle erstmal Menüpunkt 10, Apply Improvements > Packages. Kann ja nicht schaden. Der Installer läuft erfolgreich durch – aber das war nicht, was ich gesucht habe.

Menüpunkt 40, openHAB Related, listet als Punkt 46 Zulu Java 11 auf.
2021-01-02-update-12

Super, das will ich installieren, in der 32-Bit-Variante. Der Installer läuft erneut erfolgreich durch:
2021-01-02-update-13

Okay, Reboot.

Und siehe da, es funktioniert! Ich werde in das initiale Setup von openHAB 3 geleitet:
2021-01-02-update-14

2021-01-02-update-15

Ich vergebe einen Admin-Account, lege ein paar Daten und meinen Standort fest, und komme in die neutrale Übersichtsseite. Super!

2021-01-02-update-16

Jetzt muss ich das „nur noch“ konfigurieren.

Etwas rumspielen zeigt mir, dass meine Things, Channels und Rules übernommen wurden. Meine Sitemap auch, aber irgendwie funktioniert diese (noch) nicht. Anscheinend gab es da Änderungen. Glücklicherweise sind die wichtigsten Änderungen dokumentiert, insbesondere der Abschnitt über erforderliche manuelle Anpassungen ist sehr interessant. Besonders relevant für mich: Das Gardena-Binding ist komplett neu geschrieben worden. Das muss ich mir ansehen.

Außerdem wird das Fritzbox-064-Binding nicht mehr unterstützt. Vielen Dank an Christian für den Hinweis! Auch hier muss ich eine Lösung finden, die ich hier dokumentieren werde.

openHAB IX: Fritz!Box und OH 3.0

This entry is part 9 of 10 in the series openHAB
Geschätzte Lesezeit: 5 Minuten.

Seit meinem Update auf openHAB 3.0 funktioniert die Einbindung meiner Fritz!Box 7590 nicht mehr. Es wird Zeit, das zu beheben.

Ich möchte zwei Dinge realisieren:

  • Ein An/Aus-Schalter für das Gäste-WLAN
  • Anwesenheitserkennung durch Erkennung, welches Smartphone im WLAN eingewählt ist

Einen besonderen Dank möchte ich übrigens an die Leser Christian und Ralf aussprechen, die mich darauf hingewiesen haben, dass das alte Binding nicht mehr funktioniert – und die durch Nachfragen dafür gesorgt haben, dass ich mich mit dem Thema auseinander gesetzt habe.

Grundkonfiguration

Los geht es. Administration > Einstellungen > Bindings > +. Hier finden sich zwei Bindings, die prinzipiell in Frage kämen:

  • AVM FRITZ! Binding – dies scheint speziell für die Smart-Home-Features der Fritz!Box zu sein, die ich nicht benutze. Das heißt, dies ist das falsche Binding.
  • TR-064 Binding – hier finden sich viele Channels für die Fritz!Box, insbesondere sind die LAN channels interessant. Dies ist das richtige Binding.

Also gut. Ich installiere das Binding TR-064 in der Version 3.0.0. Leider finden sich keine neuen Things in meiner Inbox, ich muss das Binding also manuell konfigurieren. Glücklicherweise habe ich das ja schon einmal gemacht, daher kann ich hoffentlich viel kopieren. Und tatsächlich steht in der Dokumentation, dass das Gateway-Device manuell verbunden werden muss, der Rest funktioniert dann angeblich automatisch. Okay, dann mache ich das so.

Ich klicke Einstellungen > Things > + und wähle das TR-064-Binding. Hier wird mir direkt eine Fritz!Box vorgeschlagen, also wähne ich diese aus. Ich gebe IP-Adresse, username und Passwort ein und klicke auf Create Thing. Und siehe da, die Fritz!Box ist vorhanden, und der Status ist online. Das ist ja extrem einfach – ich mag openHAB 3.

Ich klicke das Thing an, und wähle oben Channel. Ich möchte jetzt probeweise mal die Uptime verlinken, also klicke ich auf diesen Channel und erzeuge ein neues Item für diesen Channel. Okay, jetzt sehe ich die Fritz!Box-Uptime in Sekunden. Das ist natürlich recht albern, aber es funktioniert.

Gäste-WLAN

Okay, ich möchte ja eigentlich den Schalter für das Gäste-WLAN. Also gehe ich erneut unter Einstellungen > Things > + auf das TR064-Binding. Diesmal drücke ich Scan. Jetzt wird mir ein LAN-Device angezeigt – aus der Dokumentation weiß ich, dass dieses den Channel für das WLAN besitzt. Also füge ich es hinzu, und erzeuge erneut über die Channels ein entsprechendes Item für das Gäste-WLAN. Es wird automatisch vorgeschlagen, dass es ein Switch sein soll. Das ist alles extrem einfach und straight-forward (wenn man weiß, wo man suchen muss) – ich finde das super!

Anwesenheit im WLAN

Okay, kommen wir zum (vermutlich) schwierigen Teil. Ich möchte erkennen, ob jemand anwesend ist, indem ich prüfe, ob sich das entsprechende Smartphone im WLAN befindet.

Mein erster Gedanke war, dass ich vermutlich irgendwie einen Eintrag in einer .items-Datei ergänzen muss. Aber dieser Thread hat mich auf den richtigen Weg geführt. Das Konzept ist, dass das Binding für jede MAC-Adresse einen eigenen Channel anlegen kann, den macOnline-Channel. Dazu muss man dem Binding nur sagen, welche MAC-Adressen man denn bitte überwacht haben will.

Also gehe ich über Things > LANDevice in die Konfiguration des Fritzbox-LANDevices. Im unteren Teil gibt es den Punkt Configuration, dort trage ich unter MAC Online die MAC-Adresse meines Telefons ein. Die findet man übrigens am einfachsten in der Netzwerkübersicht des Fritzbox-Menüs.

2021-01-24_fritzbox-maconline

Anschließend klicke ich auf Channels und füge den (jetzt erstmals erscheinenden) MAC Online-Channel mit der richtigen MAC-Adresse einem neuen Item hinzu. Und schon funktioniert es, ich bekomme als Status tatsächlich „ON“ angezeigt.

2021-01-24_fritzbox-maconline-2

Dieses Item kann ich jetzt nutzen, um meine Anwesenheit zu detektieren. Alles funktioniert, Haken dran.

openHAB X: Endlich die korrekte Uhrzeit

This entry is part 10 of 10 in the series openHAB
Geschätzte Lesezeit: 2 Minuten.

Es gibt da eine Kleinigkeit, die mich schon lange stört: Die Uhrzeit meiner openHAB-Installation ist falsch. Sie geht eine Stunde nach, anstatt z.B. 10:43 steht dort 09:43. Das erste Mal ist mir dies aufgefallen, als ich eine zeitgesteuerte Regel zur Beleuchtung eingerichtet habe.

Ich bin bisher davon ausgegangen, dass mein openHAB nicht weiß, dass es Sommer- und Winterzeit gibt. Diese Annahme war zu dem Zeitpunkt, als ich die Beleuchtungsregeln schrieb, durchaus valide, denn es war Juli, also Sommerzeit. Derzeit ist aber Winterzeit, und jetzt müsste die Uhr richtig gehen. Leider ist dies nicht der Fall, es ist also etwas anderes falsch eingestellt.

Zum Glück ist die Lösung total einfach. Und gefunden habe ich sie hier. Die Lösung ist: Mein openHAB hatte die falsche Zeitzone eingestellt.

So kann dies behoben werden:

  1. Ich verbinde mich per SSH und tippe sudo openhabian-config.
  2. Im sich öffnenden Menü wähle ich Option 30 – System Settings.
  3. Anschließend wähle ich Option 33 – Set System Timezone. Hier steht schon in der Beschreibung dabei, dass man diese Option wählen soll, falls die echte Zeit nicht der Systemzeit entspricht (was ja bei mir der Fall ist).
  4. Zum Schluss muss ich die korrekte Zeitzone auswählen. Das war für mich Europe > Berlin (vorher war tatsächlich Europe > London eingestellt).

Das war es auch schon. Ein kurzer Fix, aber extrem wichtig.