openHAB XIII: Weihnachtsbeleuchtung mit Twinkly

Geschätzte Lesezeit: 9 Minuten.

In der Adventszeit wurde es Zeit für ein neues Projekt. Ich möchte gerne Lichterketten in mein Smart Home integrieren. Grundsätzlich sind mir dafür zwei Strategien eingefallen:

  • Lichterketten an schaltbare Steckdosen anschließen, dann kann ich sie an- und ausschalten
  • Smarte Lichterketten nutzen, die vielleicht noch mehr können als „nur“ an oder aus

Ich habe mich für die zweite Variante entschieden und zwei Lichterketten von Twinkly, genauer gesagt Twinkly Strings 400 RBG,1einmal für 98,99€ bei click-licht.de, und einmal für 129,90€ bei lampenwelt.de organisiert.

Tag 1: Die Einbindung

Grundsätzlich ist die Installation der Twinkly-Lichterketten simpel. Es sollte aber erwähnt werden, dass eine App und ein Account benötigt werden. Möglicherweise ist diese Lösung daher nicht datensparsam – damit habe ich mich noch nicht auseinander gesetzt. Jedenfalls kann man die Lichterketten über eine spezielle App ein- und ausschalten, Farben ändern, Animationen wählen, das ist schon ziemlich cool. Es gibt auch eine Zeitschalt-Funktion, allerdings kann man damit die Lichterkette nur einmal pro Tag an- und einmal ausschalten. Und es ist auch eine feste Uhrzeit, zum Beispiel geht die Lichterkette jeden tag um 16:30 Uhr an.

Ich möchte aber mehr.

Ich möchte, dass die Lichterkette morgens an- und ausgeht, und abends ebenfalls an und aus. Und außerdem soll der Beginn und das Ende helligkeitsabhängig sein. Also werde ich die Twinkly-Lichterkette in openHAB einbinden. Leider gibt es kein Binding dafür, aber trotzdem ein gutes Tutorial. Hinter diesem Link versteckt sich ein Python-Skript, das ich jetzt erst einmal hier dokumentieren möchte.

import sys
import json
import urllib.request
import codecs

ARG_ON = 'on'
ARG_OFF = 'off'
ARG_STATE = 'state'
ARG_BRIGHT = 'brightness'

ARG_IP = sys.argv[1]
ARG_ACTION = sys.argv[2]


URL = "http://" + ARG_IP + "/xled/v1/"

LOGIN_URL = URL + "login"
VERIFY_URL = URL + "verify"
MODE_URL = URL + "led/mode"
BRIGHT_URL = URL + "led/out/brightness"

AUTH_HEADER = 'X-Auth-Token'

AUTHENTICATION_TOKEN = 'authentication_token'
CHALLENGE_RESPONSE = 'challenge-response'
MODE = 'mode'
MODE_ON = 'movie'
MODE_OFF = 'off'


HEADERS = {'Content-Type': 'application/json'}
LOGIN_DATA = {'challenge': 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8='}
TURN_ON_DATA = {MODE: MODE_ON}
TURN_OFF_DATA = {MODE: MODE_OFF}

if len(sys.argv) > 3: #check if the thirth argument is given, save it in a variable and create the brightness data variable
    ARG_ACTION2 = int(sys.argv[3])
    BRIGHTNESS_DATA = {'value': ARG_ACTION2, 'type': 'A'}


def formatData(data):
  return json.dumps(data).encode('utf8')

def processRequest(request):
  return urllib.request.urlopen(request)

def processRequestJSON(request):
  loginResponse = processRequest(request)
  reader = codecs.getreader("utf-8")
  return json.load(reader(loginResponse))

# login to api - get challenge response and auth token
loginRequest = urllib.request.Request(url = LOGIN_URL, headers = HEADERS, data = formatData(LOGIN_DATA))
loginData = processRequestJSON(loginRequest)

challengeResponse = loginData[CHALLENGE_RESPONSE]
authToken = loginData[AUTHENTICATION_TOKEN]

HEADERS[AUTH_HEADER] = authToken
verifyData = {CHALLENGE_RESPONSE: challengeResponse}

# verify token by responding with challenge response
verifyRequest = urllib.request.Request(url = VERIFY_URL, headers = HEADERS, data = formatData(verifyData))
verifyData = processRequestJSON(verifyRequest)

def turnOn():
  onRequest = urllib.request.Request(url = MODE_URL, headers = HEADERS, data = formatData(TURN_ON_DATA))
  processRequest(onRequest)
  print(1)

def turnOff():
  offRequest = urllib.request.Request(url = MODE_URL, headers = HEADERS, data = formatData(TURN_OFF_DATA))
  processRequest(offRequest)
  print(0)
  
def setBrightness():
  brightRequest = urllib.request.Request(url = BRIGHT_URL, headers = HEADERS, data = formatData(BRIGHTNESS_DATA))
  processRequest(brightRequest)

def getState():
  modeRequest = urllib.request.Request(url = MODE_URL, headers = HEADERS)
  modeData = processRequestJSON(modeRequest)

  if modeData[MODE] != MODE_OFF:
    print(1)
  else:
    print(0)
    
if ARG_ACTION == ARG_ON:
  turnOn()
elif ARG_ACTION == ARG_OFF:
  turnOff()
elif ARG_ACTION == ARG_STATE:
  getState()
elif ARG_ACTION == ARG_BRIGHT:
  setBrightness()

Ganz ehrlich, ich kann kein Python, ich weiß nicht genau, was dieses Skript macht. Ich kann nur sagen, dass es funktioniert. Wie? Dazu kommen wir jetzt.

Als erstes habe ich dieses Skript als twinkly.py im Ordner /var/lib/openhab/ abgespeichert. Dann habe ich mir über das openHAB-Benutzerinterface ein neues Item Lichterketten vom Typ Switch angelegt.

Und dann habe ich mir folgende lichterkette.rules gebastelt:

rule "Twinkly Christmas Tree Power State"
when 
  Item Lichterketten received command
then
  if (receivedCommand == ON) {
    executeCommandLine("python3","twinkly.py","x.x.x.37","on")
    executeCommandLine("python3","twinkly.py","x.x.x.43","on")
  }
  else {
    executeCommandLine("python3","twinkly.py","x.x.x.37","off")
    executeCommandLine("python3","twinkly.py","x.x.x.43","off")
  }
end

Hier sind zwei Dinge zu beachten. Erstens benötigt man die IP von jeder Lichterkette. Ich habe in meinem Fritzbox-Router feste IP-Adressen für die Lichterketten hinterlegt, damit dieser hart gecodede Befehl funktioniert. Zweitens ist bei dieser Rule die Syntax extrem wichtig. Ursprünglich habe ich die folgende, nicht funktionierende Zeile benutzt:

executeCommandLine("python3 twinkly.py 192.168.0.209 on")

Aber damit erhält man eine Fehlermeldung: File or directory not found when running executeCommandLine. Man muss wirklich darauf achten, dass die einzelnen Befehls-Elemente mit "," voneinander getrennt sind. Das ist ganz wichtig!

So, abschließend habe ich noch den Switch auf meiner openHAB-Startseite eingebunden. Damit bin ich erst einmal zufrieden.

Tag 2: Zeitgesteuertes Ein- und Ausschalten

ich möchte ja, dass die Lichterketten morgens und abends jeweils an- und wieder aus gehen. Dazu nutze ich Sonnenauf- und -untergang, die zugehörigen Daten fallen aus dem Astro-Binding. Das muss ich noch dokumentieren, hier gibt es erst einmal nur die Code-Schnipsel.

Für die lichterkette.rules

rule "Lichterketten morgens an"
when
   Item Tageszeit changed to "Morgen"
then
   Lichterketten.sendCommand(ON)
end

rule "Lichterketten morgens aus"
when
   Item Tageszeit changed to "Tag"
then
   Lichterketten.sendCommand(OFF)
end

rule "Lichterketten abends an"
when
   Item Tageszeit changed to "Vorabend"
then
   Lichterketten.sendCommand(ON)
end

rule "Lichterketten abends aus"
when
   Item Tageszeit changed to "Nacht"
then
   Lichterketten.sendCommand(OFF)
end

Und dann gibt es noch die tageszeit.rules, welches das oben genutzte Item Tageszeit verändert:

rule "Es wird morgen"
when
   Item Sonne_Hohenwinkel changed
then
   if (Sonne_Hohenwinkel.state == NULL || Sonne_Hohenwinkel.state > -12|"°") {
	  if (Tageszeit.state == "Nacht") {
         Tageszeit.sendCommand("Morgen")
	  }
   }
end

rule "Es wird Vormittag"
when
    Channel "astro:sun:local:rise#event" triggered START
then
    Tageszeit.sendCommand("Vormittag")
end

rule "Es wird Tag"
when
   Item Sonne_Hohenwinkel changed
then
   if (Sonne_Hohenwinkel.state == NULL || Sonne_Hohenwinkel.state > 3|"°") {
	  if (Tageszeit.state == "Vormittag") {
         Tageszeit.sendCommand("Tag")
	  }
   }
end

rule "Es ist Tag"
when
   Time cron "0 0 12 ? * * *"
then
   Tageszeit.sendCommand("Tag")
end

rule "Es wird Vorabend"
when
   Item Sonne_Hohenwinkel changed
then
   if (Sonne_Hohenwinkel.state == NULL || Sonne_Hohenwinkel.state < 3|"°") {
	  if (Tageszeit.state == "Tag") {
         Tageszeit.sendCommand("Vorabend")
	  }
   }
end

rule "Es wird Abend"
when
   Item Sonne_Hohenwinkel changed
then
   if (Sonne_Hohenwinkel.state == NULL || Sonne_Hohenwinkel.state < -2|"°") {
	  if (Tageszeit.state == "Vorabend") {
         Tageszeit.sendCommand("Abend")
	  }
   }
end

rule "Es ist Nacht 1"
when
   Time cron "0 15 22 ? * * *"
then
   Tageszeit.sendCommand("Nacht")
end

rule "Es ist Nacht 2"
when
   Time cron "0 0 4 ? * * *"
then
   Tageszeit.sendCommand("Nacht")
end

rule "Debugging"
when
   Item Tageszeit changed
then
   logWarn("Tageszeit", "Tageszeit changed. Ist jetzt: {}", Tageszeit.state)
end

Und es funktioniert. Wie durch Magie gehen die Lichterketten in der Morgen- und Abenddämmerung an und wieder aus. Jeden Tag zu einer leicht anderen Uhrzeit, immer so, dass es gerade dunkel ist.

Tag 3: Nächstes Jahr

Die obigen Schritte habe ich im Jahr 2021 durchgeführt (aber den Artikel nie veröffentlicht). Irgendwann nach Weihnachten habe ich damals die Lichterketten wieder abgebaut und die lichterkette.rules entfernt. Jetzt ist November 2022, und ich habe die Lichterketten wieder aufgehängt. Und sie sollen auch bitte wieder funktionieren, also lade ich die unveränderte lichterkette.rules wieder auf meinen openHAB-Server. Und siehe da, ich kann die Lichterketten immer noch ausschalten.

Aber nur ausschalten. Anschalten funktioniert nicht mehr. Das ist sehr seltsam.

Ich gehe in den Debugging-Modus, versuche herauszufinden, ob das if-else-Konstrukt der lichterkette.rules funktioniert. Und was soll ich sagen, daran liegt es nicht, denn es funktioniert. Also muss ja die twinkly.py defekt sein, denn was soll es sonst sein? Aber, oh Schreck, ich kann ja immer noch kein Python, was mache ich denn jetzt?

Naja, ich nutze Google. Ich finde andere Menschen mit dem gleichen Problem, aber leider ohne Lösung. Das wäre ja auch zu einfach gewesen.

Ich finde ein (inoffizielles) Twinkly-Binding, hier. Das ist doch vielversprechend, aber leider unterstützt es erst openHAB ab Version 3.2, und ich bin ja noch bei 3.0.1. - ich sollte sowieso updaten. Wie das geht, beschreibe ich in einem anderen Artikel, denn hier habe ich wieder ziemlich geflucht. Aber jetzt kommt die Magie: Nach dem Update auf openHAB 3.3 funktioniert twinkly.py wieder problemlos. Anscheinend hat das Update alles gelöst, und ich muss das Binding nicht ausprobieren. Und mein Garten erstrahlt wieder im Twinkly-Glanz.

TBD19 Kerstin, warum sind Männer der Standard und Frauen die Ausnahme?

Männer sind der Standard, Frauen die Ausnahme. Das klingt völlig überholt? Leider ist das in vielen Produktentwicklungen und in vielen datengetriebenen Vorgehen die Realität. Zu welchen Problemen dies führt, und wie mehr Diversität in Teams und Unternehmen diesen Gender Data Gap beseitigen kann, darüber spreche ich mit Kerstin Fels.


Kerstin Fels erreicht ihr am besten über LinkedIn: https://www.linkedin.com/in/kerstin-fels-5800a915/. Alternativ könnt ihr auch eine E-Mail an kerstin.fels „at“ valtech.com schreiben.

Die angesprochene McKinsey-Studie zu verbesserten Geschäftserfolgen durch mehr Diversität findet ihr hier: https://www.mckinsey.de/news/presse/2020-05-19-diversity-wins.


TBD18 Mark, wie funktioniert Ownership in Großkonzernen?

Viele Konzerne, die sich agil aufstellen wollen, schaffen die Rolle des Product Owners. Diese Rolle hat oft Vorgesetzte, und im Zweifel entscheidet der Vorstand – kann ein Product Owner in einer solchen Situation überhaupt ein „Owner“ sein? Was können Unternehmen tun, um echte Ownership zu ermöglichen? Darüber spreche ich mit Mark Bregenzer, der nach Folge #4 erneut zu Gast ist.


Mark Bregenzer erreicht ihr am besten über seine Webseite (https://bregenzer.eu) oder über LinkedIn: https://www.linkedin.com/in/mark-bregenzer-52603b6/.

Die folgenden Themen sprechen wir an:

  • COPE: Peter Kruse, next practice: Erfolgreiches Management von Instabilität. Veränderung durch Vernetzung, ISBN 978-3-86936-962-4.
  • Taylorismus: Seite „Taylorismus“. In: Wikipedia – Die freie Enzyklopädie. Bearbeitungsstand: 11. Juli 2021, 23:01 UTC. URL: https://de.wikipedia.org/w/index.php?title=Taylorismus&oldid=213774512 (Abgerufen: 4. Oktober 2022, 06:59 UTC).
  • Reduktionismus: Seite „Reduktionismus“. In: Wikipedia – Die freie Enzyklopädie. Bearbeitungsstand: 20. Juni 2022, 19:37 UTC. URL: https://de.wikipedia.org/w/index.php?title=Reduktionismus&oldid=223861781 (Abgerufen: 4. Oktober 2022, 06:59 UTC).
  • Change Management: John P. Kotter, Leading Change: Wie Sie Ihr Unternehmen in acht Schritten erfolgreich verändern, ISBN 978-3-8006-3789-8.

TBD17 Andrea, was ist Female Leadership?

In dieser Folge spreche ich mit Andrea Nebel darüber, wie Menschen ihre „männlichen“ und „weiblichen“ Eigenschaften in Balance bringen können, um ihr volles Potential zu entfalten. Außerdem zeigen wir Wege für Unternehmen auf, Mitarbeitende dabei zu unterstützen und die eigene Kultur zu reflektieren.


Andrea Nebel könnt ihr auf verschiedene Wege erreichen:

Die anderen Podcastfolgen mit ihr findet ihr hier:


TBD16 Tom, wie entkommt man der Ambivalenzfalle?

„Zwei Seelen wohnen, ach! in meiner Brust“. Schon Goethe wusste, dass Menschen ambivalent sind. Wie Unternehmen und Coaches damit umgehen, wie sie die Stärken verschiedener Denkweisen nutzen können, und wie man der Ambivalenzfalle entkommt, darüber spreche ich mit Tom Latka.


Tom Latka erreicht ihr am besten über LinkedIn: https://www.linkedin.com/in/drtom/.


TBD15 Axel, woher kommt der Hype um OKRs?

Objectives and Key Results (OKR) sind derzeit in aller Munde. Warum ist das so? Und wie viel Substanz versteckt sich hinter dem Hype? Darüber spreche ich mit Axel Hermes.


Axel Hermes erreicht ihr am besten über LinkedIn: https://www.linkedin.com/in/axelhermes/.
Oder über Xing: https://www.xing.com/profile/Axel_Hermes/

Diese Themen wurden in der Folge angesprochen:

  • Christina R. Wodtke, Radical Focus: Achieving Your Goals with Objectives and Key Results, ISBN 978-1955469012.
  • MbO: Management by Objectives, Wikipedia – Die freie Enzyklopädie, 4. April 2022, 14:24 UTC.
  • VUCA: VUCA, Wikipedia – Die freie Enzyklopädie, 7. Juni 2021, 12:06 UTC.
  • Nordstern: Die Vision und der Nordstern, St. Gallen Business School
  • John Doerr, Measure What Matters: OKRs: The Simple Idea that Drives 10x Growth, ISBN 978-0241348482.

TBD14 Elisabeth, was kann man tun, wenn es nach einer SAFe-Einführung nicht rund läuft?

In den meisten Fällen liegt die Ursache für den mangelnden Erfolg agiler Transformationen, die sich an Rahmenwerken wie SAFe orientieren, in einem unzureichenden Verständnis der zugrundeliegenden Lean- und Agile-Prinzipien und der Vernachlässigung der Tatsache, dass deren Anwendung einen tiefgreifenden kulturellen Wandel erfordert. In dieser Folge beschreibt Elisabeth Liberda einen 10-Schritte-Leitfaden für die Bewältigung der organisatorischen Dilemmata, die durch eine SAFe-Implementierung entstehen können, und wie diese überwunden werden können.


Elisabeth Liberda erreicht ihr über LinkedIn: https://www.linkedin.com/in/elisabeth-liberda-a9a556b8/

Das Whitepaper mit dem Titel „10 Steps to Improve Your SAFe Adoption“ findet ihr hier: https://www.valtech.com/de-de/whitepapers/10-steps-to-improve-your-safe-adoption/


TBD13 Michael, wie hilft Agilität BMW dabei, autonomes Fahren zu entwickeln?

„Wir wollen am höchsten Kundennutzen schneller als die Konkurrenz entwickeln!“ Dies hat die BMW Group dazu bewogen, neue organisatorische Schritte zu gehen. Eine leicht anpassbare, leistungsstarke und optimal für diese Aufgabe ausgerichtete Organisation wurde gesucht – und mit LeSS (Large-Scale Scrum) als Organisationsprinzip gefunden. In dieser Folge berichtet Michael Mai von dessen Einführung, und von den kleinen und großen Herausforderungen der ersten Jahre der Adoption.


Weitere Details können in der Case-Study „Huge LeSS Huge at BMW Group — Autonomous Driving“ gefunden werden: https://less.works/case-studies/bmw-group-autonomous-driving

Michael Mai erreicht ihr über LinkedIn: https://www.linkedin.com/in/michael-mai-coach/


TBD12 Anne, Nils, welche Superkräfte brauchen LeSS-Scrum-Master?

Skalierte agile Frameworks wie LeSS (Large-Scale Scrum) schaffen einen Rahmen, wie mehrere Teams gemeinsam an einem Produkt arbeiten. Diese Teams benötigen Unterstützung durch Scrum-Master, um effektiv arbeiten zu können. Wie sieht diese Unterstützung aus, und welche Superkräfte werden benötigt, um LeSS-Teams effektiv zu coachen? Darüber diskutiere ich mit Anne Hübner und Nils Bernert.


Ihr könnt Anne Hübner über LinkedIn erreichen: https://de.linkedin.com/in/anne-hübner-b50a28168
Gleiches gilt für Nils Bernert: https://de.linkedin.com/in/nilsbernert

Die LeSS-Konferenzen findet ihr hier: https://less.works/less-conferences
Und die LeSS-Community München findet ihr hier: https://www.meetup.com/de-DE/Large-Scale-Scrum-Community-Munich/


TBD11 Andreas, wie kann man der Ukraine helfen?

Aufgrund des Krieges in der Ukraine wird in diesem Monat keine reguläre Folge veröffentlicht.

Falls ihr spenden könnt oder wollt, findet sich hier eine Übersicht einiger Organisationen: https://www.tagesschau.de/spendenkonten/spendenkonten-133.html.