Kapitel 3: IoBroker Scripting

Nachdem wir jetzt verschiedene Komponenten eingerichtet und im ioBroker UI geschaltet und ausgelesen haben, wollen wir anfangen, ein paar Sachen zu automatisieren. Dazu verwenden wir den in ioBroker enthaltenen Scripting-Host.

Es sei an dieser Stelle darauf hingewiesen, dass ioBroker verschiedene Methoden kennt, um Abläufe zu automatisieren. Ich verwende hier die “pure” JavaScript-Methode. Dies einerseits, weil es mir gefällt, exakt kontrollieren zu können, was ein Programm tut, und andererseits, weil man auf diese Weise “alles” kann und nicht auf die Möglichkeiten der jeweiligen Automatisierungstechnik begrenzt ist bzw. nur mit zusätzlicher Mühe daraus ausbrechen kann. Dennoch: Wenn Ihnen visuelles Programmieren besser liegt, schauen Sie sich zum Beispiel den “Node Red”-Adapter oder den “Scenes”-Adapter genauer an. Insbesondere Node Red ist inzwischen eine weitverbreitete Programmiertechnik geworden, für die es auch viel Hilfe im Netz gibt. (https://nodered.org).

Falls Sie aber auf die “harte Tour” skripten wollen, folgen Sie mir bitte weiter.

Alle Skripte, die ich im Folgenden zeige, finden Sie auch im Verzeichnis “iobroker-scripts” der Begleitsoftware, welche Sie wie im Anhang gezeigt auf Ihren Computer klonen können. Ich empfehle Ihnen, dies auch zu tun da ich hier machmal nur die relevanten Teile der Skripte vorstelle. Den vollständigen Code finden Sie dann jeweils im Quellen-Verzeichnis.

Die hier besprochenen Skripts befinden sich im Unterverzeichnis ‘iobroker’, wenn Sie das Repository wie im Anhang gezeigt clonen, und dann den Haupt-Branch auschecken:

git checkout master

Einrichtung des Script-Hosts

Richten Sie Ihren Browser auf ioBroker (http://homeview.local:8081) und öffnen Sie den Reiter ‘Adapter’. Sie benötigen die ‘Script Engine’, bzw. ‘Skriptausführung’ in der deutschen Version (Sie können auch einfach ‘script’ in das Feld ‘Filter’ eingeben, dann brauchen Sie nicht die immer länger werdende Liste zu durchsuchen). Installieren Sie diesen Adapter in gleicher Weise wie im vorigen Kapitel gezeigt, und lassen Sie alle Einstellungen der Instanz-Konfiguration auf den Vorgaben. Nach erfolgreicher Installation haben Sie einen neuen Reiter namens ‘Skripte’ in Ihrer ioBroker Oberfläche.

Abb. 3.1: Scripting-Host
Abb. 3.1: Scripting-Host

Klicken Sie dann zunächst auf den runden plus-Button für neue Gruppe und erstellen Sie eine Gruppe namens Smarthome-diy (Oder einen Namen Ihrer Wahl). Markieren Sie dann die eben erstellte Gruppe und klicken Sie auf den Button ‘neues Script’. Wählen Sie als “type” dann ‘JavaScript’. Geben Sie dem Script, das nun als “Skript1” aufgelistet ist, den wesentlich fantasievolleren Namen “test” und klicken Sie ›speichern‹.

Schreiben Sie dann in das weiße Feld rechts:

log("Hello, World", "info")

Klicken Sie auf ›speichern‹, und dann auf den ›Play‹ Button im linken Teilfenster neben dem Namen des Scripts. Im rechten unteren Teilfenster erscheinen einige Textzeilen, darunter auch eine, die “Hello, World” enthält. Gratuliere, Sie haben Ihr erstes ioBroker Script geschrieben.

“log” bedeutet dabei, dass das Skript etwas in die “Log-Datei” schreiben soll. Den Inhalt der Log-Datei sehen Sie jeweils im Reiter Log der Admin-Oberfläche. In diese Log-Datei schreiben alle Adapter und Skripte, was sie dem Administrator mitteilen wollen. Das sind Mitteilungen unterschiedlicher Wichtigkeitsstufen, die man als “debug”, “info”, “warn” und “error” bezeichnet. Allgemeine Informationen erhalten die Stufe ›info‹, während Nachrichten, die man nur zum Debuggen benötigt, mit dem Level ›debug‹ geschrieben werden. Ernsthaftere Störungen werden als ›warn‹ ausgegeben, und Fehler, die die normale Funktion unmöglich machen, als ›error‹. Im Log-Fenster kann man einstellen, welche Stufen man sehen will. Ich werde Ihnen das später noch zeigen. Interessant ist für uns, dass wir Log-Meldungen unserer Skripte auch direkt im untersten Teilfenster der Skript-View sehen.

Also zusammengefasst: Unser erstes Skript schreibt “Hello World” mit der Wichtigkeit “info” in die Logdatei, was der Skripting-Host uns gleichzeitig auch hier im Fenster anzeigt.

Abb. 3.1: Log Ausgabe
Abb. 3.1: Log Ausgabe

Im Folgenden werden wir einige einfache Aufgaben per Skript erledigen.

Doch zunächst eine kurze Erklärung zu den Ordnern, die wir bereits in ‘Skripte’ vorgefunden haben:

  • Common ist einfach ein Ablageort, gedacht für allgemein benötigte Code Teile
  • Mit ‘Global’ hat es aber eine besondere Bewandtnis: Was hier drin steht, wird an den Anfang jedes anderen Skripts gesetzt. Wir nutzen da gleich aus, um ein paar Konstanten zu definieren:
Abb. 3.2: Globale Skripte
Abb. 3.2: Globale Skripte

Wir werden diese Konstanten später benutzen und ggf. erweitern. Beachten Sie, dass das Skript in “global” gestartet (grün) sein muss, damit andere Skripte die Werte benutzen können.

Einfache Skripte

Lampe einschalten

Erstellen Sie ein neues Skript und geben Sie ein:

setState(

Klicken Sie dann rechts oben im Skriptfenster auf ‘ID Einfügen’. Im daraufhin öffnenden Dialog wählen Sie das gewünschte Licht oder die gewünschte Lichtgruppe aus, markieren dort die Eigenschaft ‘on’ und klicken danach ‘Auswählen’.

Abb. 3.3: IoBroker State auswählen
Abb. 3.3: IoBroker State auswählen

Im Skriptfenster steht dann etwas wie:

setState("lightify.0.663254887.on"/*on*/)

IoBroker hat also für uns die manchmal etwas unhandliche Geräte-ID eingefügt (die bei Ihnen selbstverständlich anders aussehen wird, als hier gezeigt). Den Teil zwischen /* und */ kann man löschen, der ist nur als Information gedacht (und hier überflüssig), dafür muss man aber noch ergänzen, was ioBroker nun genau tun soll:

setState("lightify.0.663254887.on", true);

Speichern und starten Sie dieses Skript, und wenn Sie alles richtig gemacht haben, wird dadurch das genannte Licht oder die genannte Lichtgruppe eingeschaltet. Wenn Sie ‘false’ statt ‘true’ einsetzen, wird es ausgeschaltet. True und false sind sogenannte bool’sche Konstanten, die nur diese beiden Werte annehmen können, und darum gern für Schaltvorgänge verwendet werden. Man hätte auch 1 und 0 schreiben können, JavaScript (und damit ioBroker) ist da recht tolerant.

Zwei Lampen miteinander verknüpfen

Nun wollen wir Folgendes erreichen: Wann immer jemand das Licht im Esszimmer einschaltet, soll auch das Licht im Korridor angehen. Das könne man bei Hue und Lightify auch dadurch erreichen, dass man diese beiden Lampen in einer Gruppe zusammenfasst, und dann die Gruppe ein- und ausschaltet, aber die Lösung mit dem Skript ist besser, da es egal ist, in welcher Weise das Esszimmerlicht eingeschaltet wird (Lichtschalter oder App), und da es nur in einer Richtung geht (man kann das Korridorlicht ohne Esszimmer schalten, aber nicht umgekehrt), und nicht zuletzt auch, weil es flexibler ist (Das Korridorlicht kann in mehreren verschiedenen Schaltlogiken eingebunden sein), und die beiden Lichter können zu ganz verschiedenen Gerätearten gehören: Man kann auf diese Weise problemlos Hue, Lightify und myStrom Lampen verknüpfen (und hier sehen Sie nun somit das erste Mal in diesem Buch den ‘Mehrwert’, den Sie dank der Bemühungen mit ioBroker erzielen).

const esszimmerlicht = "lightify.0.64EADA0002261884"
const korridorlicht = "hue.0.Philips_hue.Korridor"
const esszimmer_an_aus = esszimmerlicht + ".on"
const korridor_an_aus = korridorlicht + ".on"
on({ id: esszimmer_an_aus }, function () {
  var State = getState(esszimmer_an_aus).val
  setState(korridor_an_aus, state)
}

In den ersten beiden Zeilen weisen wir die unhandlichen Objekt-IDs etwas menschenverständlicheren Variablen zu. Es bewährt sich, das immer so zu halten. Wenn man später einmal eine Lampe auswechselt oder ändern möchte, muss man nur die Zuweisung am Anfang ändern, und nicht das ganze Skript nach Verweisen auf das betroffene Gerät durchsuchen.

Die nächsten zwei Zeilen sind vielleicht etwas schwieriger zu verstehen: Wir addieren “.on” zu einem Variablennamen? Nanu?

Dazu ein kleiner Exkurs, den Sie überspringen können, wenn Sie sich mit JavaScript schon ein wenig auskennen:

Ein besonders vielseitiger Variablentyp ist das “Objekt”. Ein Objekt wird so dargestellt:

var irgendein_name = {
  frage: "Das Leben etc.",
  antwort: 42,
  attribut3: false,
  attribut4: [1, 2, 3],
  attribut5: { name: "Rumpelstilzchen" }
}

Jedes Attribut kann nur einmal vorkommen und einen beliebigen Namen haben, der aus Buchstaben, Zahlen und Unterstrich bestehen darf, aber mit einem Buchstaben beginnen muss. Jeder Wert kann vom Typ String (Zeichenkette), number (Zahl), Array, Boolean oder Object sein. Attribute innerhalb eines Objekts sind durch Komma getrennt, das ganze Objekt ist immer mit geschweiften Klammern umschlossen.

In ioBroker wird ein Objekt, das einen Zustand eines Gerätes anzeigt, ›State‹ genannt, und solche State-Objekte sind entsprechend aufgebaut, zum Beispiel könnte eine Lampe so definiert sein:

const licht = {
  on: true,
  bri: 70,
  r: 255,
  g: 255,
  b: 125,
  reachable: true
}

Wenn ich jetzt ›licht.on‹ referenziere, dann meine ich damit das Attribut on des Objekts licht. Somit ist klar: esszimmer_an_aus ist hier: esszimmerlicht.on.

Die Bedeutung der einzelnen Attribute eines ioBroker-Objekts ist übrigens rein vom Adapter abhängig. Hier bedeutet on den Einschaltzustand, bri die Helligkeit in Prozent, r, g und b sind die Rot- Grün- und Blau-Anteile des Lichts, als Werte zwischen 0 und 255 ausgedrückt. Reachable gibt Auskunft darüber, ob das Gerät überhaupt per Software erreichbar ist (das ist nicht der Fall, wenn es zum Beispiel mit dem Lichtschalter ausgeschaltet wurde). All diese Attribute können wir in ioBroker Skripten auslesen und setzen. Nicht alle Adapter sind gleich gut dokumentiert, und manchmal muss man ein wenig mit den Werten herumspielen, um herauszufinden, was sie bedeuten.

Zeitschaltuhr

Zum Aufwärmen bilden wir jetzt eine dieser billigen Zeitschaltuhren mit unserem teuren Smarthome-System in Software nach.

Erstellen Sie ein neues Skript namens ‘zeitschaltuhr’ in der Gruppe smarthome-diy.

 1 const licht ="lightify.0.904AA200AA3EB07C.on"
 2 
 3 schedule("0 17 * * *", function(){
 4     log("Schaltuhr ein")
 5     setState(licht,true)
 6 })
 7 
 8 schedule("30 23 * * *",function(){
 9     log("Schaltuhr aus")
10     setState(licht,false)
11 })

Eingangs definieren wir die Lampe, die wir steuern wollen. Dann folgen zwei schedule Ausdrücke, die vielleicht noch näher erklärt werden sollten: Damit teilt man ioBroker (bzw. der V8-Engine, auf der ioBroker läuft) mit, dass eine Funktion zu bestimmten Zeiten ausgeführt werden soll. Die allgemeine Syntax ist:

schedule("Zeitausdruck", function () { })

Der Zeitausdruck kann ganz einfach vom Skripteditor aus eingegeben werden, indem man rechts oben auf ›Cron‹ klickt. Es geht dann dieses Fenster auf:

Abb. 3.5: Cron-Ausdruck per UI
Abb. 3.5: Cron-Ausdruck per UI

Hier kann man ganz einfach die gewünschten Zeiten angeben. Die Syntax ist dem Unix cron nachempfunden, daher der Name: Man schreibt 5 oder 6 Sterne (je nachdem, ob Sekunden benutzt werden sollen). der Stern ganz links ist jeweils die kleinste Maßeinheit, dann bedeutet jedes Feld von links nach rechts: (Sekunde, wenn vorhanden), Minute, Stunde, Tag des Monats, Monat, Tag der Woche. So würde etwa “30 3,15 * * *” bedeuten: Um 3:30 und 15:30 Uhr an jedem Tag in jedem Monat, während “0 12 * 5,7,9 1-6” bedeuten würde: Jeden Montag bis Samstag im Mai, Juli und September um 12:00 Uhr. Sie sehen, man kann da ziemlich jeden gewünschten Zeitpunkt ausdrücken. Und glücklicherweise muss man dank der intuitiven Cron-Auswahlbox von ioBroker diese komplizierte Schreibweise gar nicht kennen.

Erweitern Sie unser allererstes Script so:

log("Hello, World", "info")
schedule("*/10 * * * * *", function () {
  log("und wieder 10 Sekunden vergangen", "info")
})

Wenn Sie es starten, wird es brav alle 10 Sekunden laut geben. Allerdings nicht ganz genau. Cron ist kein Präzisionstimer, sondern wird die Zeiten nur ungefähr einhalten. Wenn es auf Sekundengenauigkeit ankommt, braucht man andere Methoden.

Bewaffnet mit diesen Wissen erkennen Sie nun, dass obige Zeitschaltuhr das Licht um 17:00 Uhr ein- und um 23:30 Uhr wieder ausschaltet, und zwar jeden Tag.

Wir werden das Konzept gleich ein wenig ausbauen:

Außenbeleuchtung nach Sonnenuntergang bis Mitternacht einschalten

Die Außentreppe ist im Dunkeln ein wenig gefährlich. Damit niemand hinauf- und hinunterstolpert, soll sie wenigstens bis Mitternacht beleuchtet sein. Das soll einerseits automatisch erfolgen, andererseits muss es aber auch möglich sein, manuell einzugreifen, damit man die Treppe etwa für späte Partygäste auch (und gerade!) zu vorgerückter Stunde beleuchten kann.

Erstellen Sie ein neues Skript namens ›aussenlicht_nachts‹ in der Gruppe smarthome-diy.

 1 createState("aussenlicht_manuell",AUTO)
 2 
 3 const treppenlicht ="lightify.0.904AA200AA3EB07C.on"
 4 
 5 function toggle(mode){
 6     log("toggle "+mode)
 7     if(getState("aussenlicht_manuell").val==AUTO){
 8         setState(treppenlicht,mode)
 9     }
10 }
11 
12 
13 on({id: "javascript.0.aussenlicht_manuell", val:OFF}, function(){
14     log("manuell aus")
15     setState(treppenlicht,false)
16 })
17 on({id: "javascript.0.aussenlicht_manuell",val: ON},function(){
18     log("manuell an")
19     setState(treppenlicht,true)
20 })
21 on({id: "javascript.0.aussenlicht_manuell", val:AUTO},function(){
22     log("switched to auto")
23     if(isAstroDay()){
24         log("it's day")
25         setState(treppenlicht,false)
26     }else{
27         log("it's night")
28         setState(treppenlicht,compareTime('sunset','23:59','between'))
29     }
30 })
31 
32 schedule({astro: "sunset", shift: 15}, function(){
33     log("sunset, lights on")
34     toggle(true)
35 })
36 
37 schedule("30 23 * * *", function(){
38     log("23:30, lights off")
39     toggle(false)
40 })

Hier geschieht eine ganze Menge Neues. Zunächst erstellen wir mit “createState” einen Software-State. Sie erinnern sich: Ein ‘State’ ist der Zustand eines von ioBroker kontrollierten Objektes, also etwa der Zustand “an” einer Lampe. Wir können in einem Skript aber auch Pseudo-States oder Software-States mit beliebigen Namen erstellen, auf die man dann genau so zugreifen kann, wie auf “echte” States. Hier erstellen wir einen solchen State mit einem initialen Wert von AUTO. AUTO wiederum ist (ebenso wie ON und OFF) eine Konstante, die wir vorhin in unserem globals-skript in der global-Gruppe des Skripting Hosts erstellt hatten.

Nachdem Sie dieses Skript zum ersten Mal ausgeführt haben, finden Sie unter Objekte in der Gruppe javascript.0 einen neuen State namens ‘aussenlicht_manuell’:

Abb. 3.4: Software-State ›aussenlicht_manuell‹
Abb. 3.4: Software-State ›aussenlicht_manuell‹

Diesen State können Sie genauso wie “echte” States von dieser Admin- Benutzeroberfläche (und später von Visualisierungen und Programmen) aus schalten, wie z.B. den “on” Status einer Lampe.

Dann folgt eine Funktion toggle, die das Treppenlicht je nach mode-Argment ein- oder ausschaltet, aber nur, wenn unser vorhin definierter aussenlicht_manuell State auf AUTO steht. Wenn der State also nicht auf “Automatik” steht, dann ignoriert diese Funktion die Aufforderung.

Ab Zeile 13 reagieren wir auf manuelles Ausschalten also auf die Situation, dass “irgendwer” den state aussenlicht_manuell auf OFF stellt. ioBroker schickt uns dann eine Benachrichtigung, die wir mit einer solchen “on({state},value)” Funktion verarbeiten können. In diesem Fall schalten wir das Treppenlicht aus. Unser Software-State ist jetzt nicht mehr auf AUTO, sondern auf OFF. Künftige automatische Schaltvorgänge werden also nicht ausgeführt.

Ab Zeile 17 kommt dasselbe für den Fall dass der State ON geht.

Ab Zeile 21 verarbeiten wir die Nachricht dass unser State auf AUTO geschaltet wurde. In diesem Fall schauen wir zuerst mal nach, ob es gerade Tag oder Nacht ist.

Dabei kommt eine schöne Funktion von ioBroker zum Zuge: isAstroDay() liefert true zurück, wenn es Tag ist, sonst false. Das ist übrigens auch der Grund, warum ioBroker bei der Installation Ihre Koordinaten wissen wollte. Nur so kann diese Funktion (und andere tageszeitabhängige Funktionen, die wir später noch sehen werden) korrekt funktionieren.

Die letzten beiden Funktionen implementieren nun die Automatik: Um 15 Minuten nach Sonnenuntergang ({astro: sunset, shift 15}) wird das Licht eingeschaltet (falls es im Automatik-Modus ist), und um 23:30 wird es ausgeschaltet (falls es im Automatik-Modus ist).

Außenbeleuchtung mit Bewegungssensor verknüpfen

Wir wollen das Außenlicht nicht nur zu bestimmten Zeiten einschalten, sondern auch, wenn jemand sich in der Nähe der Treppe befindet. Wir möchten sie also mit einem Bewegungssensor zusammenschalten. Ausserdem soll es, wenn es manuell oder per Sensor aktiviert wurde, heller leuchten, als das Dauerlicht. Und last but not least soll auch ein Licht an der Eingangstür angehen, wenn jemand in die Nähe kommt.

Dazu müssen wir nur relativ wenig ergänzen:

 1 const DEFAULT_BRI=35;
 2 
 3 createState("aussenlicht_manuell",AUTO)
 4 
 5 const treppenlicht="lightify.0.904AA200AA3EB07C.on"
 6 const treppenlicht_bri="lightify.0.904AA200AA3EB07C.bri"
 7 const tuerlicht="lightify.0.64EADA0000261884.on"
 8 const sensor="hm-rpc.0.NEQ0320745.1.MOTION"
 9 const helligkeit="hm-rpc.0.NEQ0320745.1.BRIGHTNESS"
10 
11 function toggle(mode){
12     log("toggle "+mode)
13     if(getState("aussenlicht_manuell").val==AUTO){
14         setState(treppenlicht,mode)
15     }
16 }
17 
18 
19 on({id: "javascript.0.aussenlicht_manuell", val:OFF}, function(){
20     log("manuell aus")
21     setState(treppenlicht,false)
22     setState(treppenlicht_bri,DEFAULT_BRI)
23     setState(tuerlicht,false)
24 })
25 
26 on({id: "javascript.0.aussenlicht_manuell",val: ON},function(){
27     log("manuell an")
28     setState(treppenlicht,true)
29     setState(treppenlicht_bri,90)
30     setState(tuerlicht,true)
31 })
32 
33 on({id: "javascript.0.aussenlicht_manuell", val:AUTO},function(){
34     log("switched to auto")
35     setState(treppenlicht_bri,DEFAULT_BRI)
36     setState(tuerlicht,false)
37     if(isAstroDay()){
38         log("it's day")
39         setState(treppenlicht,false)
40     }else{
41         log("it's night")
42         setState(treppenlicht,compareTime('sunset','23:59','between'))
43     }
44 })
45 
46 // (1)
47 on({id: sensor, val: true},function(){
48     if(getState(helligkeit).val<90){
49         setTimeout(function(){
50             log("motion sensor timeout")
51             setState("javascript.0.aussenlicht_manuell",AUTO)
52         },120000)
53         log("motion sensor activated")
54         setState(tuerlicht,true)
55         setState(treppenlicht, true)
56         setState(treppenlicht_bri,100)
57     }
58 })
59 
60 schedule({astro: "sunset", shift: 15}, function(){
61     log("sunset, lights on")
62     toggle(true)
63 })
64 
65 schedule("30 23 * * *", function(){
66     log("23:30, lights off")
67     toggle(false)
68 })

Grundsätzlich hat sich nicht viel geändert, ausser dass beim manuellen Ein- und Ausschalten auch das Türlicht geschaltet wird, und dass ein zusätzlicher setState() auf die Helligkeit des Treppenlichts erfolgt. Im Auto-Modus wird die Helligkeit auf 35% zurückgefahren und das Türlicht ausgeschaltet. Bis jemand in den Erfassungsbereich des Sensors tritt, was in der Funktion bei (1) behandelt wird: Hier wird für 120 Sekunden alles eingeschaltet, falls die Hellgkeit unter einem bestimmten Grenzwert ist. Der Grenzwert von 90 ist empirisch gefunden: Wenn es vor unserer Haustür so dunkel ist, dass man gerne etwas mehr Licht hätte, dann zeigt der Sensor bei uns diesen Wert an.

Die Schaltfunktion bedarf vielleicht einer kleinen Erklärung:

 setTimeout(function(){
        setState("javascript.0.aussenlicht_manuell",AUTO)
    },120000)
    
    setState(tuerlicht,true)
    setState(treppenlicht, true)
    setState(treppenlicht_bri,100)

Die JavaScript Standardfunktion setTimeout führt die im ersten Argument genannte Funktion nach der im zweiten Argumenten Zeit (in Millisekunden) aus. Hier wird also nach 120 Sekunden der State für den Aussenlicht-Schalter auf “AUTO” gestellt. Das bewirkt, dass dann die on(…,AUTO) Funktion ausgeführt wird, die alles auf den Normalzustand setzt. Nach dieser Vorbereitung werden sofort drei setState-Kommandos abgesetzt, die die Lichter einschalten und auf volle Helligkeit setzen.

Fernsehbeleuchtung einschalten, wenn der Fernseher läuft

Wenn der Fernseher läuft, sollen die Philips Living-Colors Leuchten angehen.

var hue="hue.0.Philips_hue.Wohnzimmer.on";
createState("fernsehlicht_manuell",AUTO)

// (1)
function licht(val){
    if(getState("fernsehlicht_manuell").val==AUTO){
        if(getState("hue.0.Philips_hue.Wohnzimmer.on").val!=val){
            console.log("schalte Licht: "+val,'debug')
            setState(hue,val)
        }
    }
}

// (2)
on({id: "javascript.0.fernsehlicht_manuell", val:ON},function(){
    setState(hue,true)
})

// (3)
on({id: "javascript.0.fernsehlicht_manuell", val:OFF},function(){
    setState(hue,false)
})

// (4)
schedule({astro: "sunset", shift: -15}, function () {
    log("sunset",'info')
    if(getState("lgtv.0.on"/*TV is ON*/).val){
        licht(true)
    }
});

// (5)
on({id: 'lgtv.0.on', val: true, change: "ne"}, function(){
    log("tv switched on",'info')
    if(!isAstroDay()){
        licht(true)
    }
})

// (6)
on({id: 'lgtv.0.on', val: false, change: "ne"}, function(){
    log("tv switched off",'info')
    licht(false)
})

// (7)
schedule({astro: "sunrise"}, function(){
    log("sunrise")
    licht(false)
})

Am Anfang definieren wir, wie nun schon gewohnt, die benötigten Geräte-IDs und den State, den wir zur Kontrolle verwenden wollen. Dann folgt bei (1) die Funktion “licht”, die die Aufgabe hat, dann, im Automatik Modus das Licht ein oder auszuschalten. Bei (2) und (3) werden manuelle Schaltvorgänge verarbeitet. Die Schedule Funktion bei (4) wird nur einmal pro Tag 15 Minuten vor Sonnenuntergang aufgerufen und prüft dann, ob der Fernseher läuft. Wenn ja, schaltet sie das Licht ein. (5) und (6) werden aufgerufen, wenn der Fernseher ein- oder ausgeschaltet wird. (7) schliesslich kommt dann zum Zug, wenn der Fernseher bei Sonnenaufgang immer noch läuft und schaltet dann das Licht aus.

Auto laden, wenn genug Strom von der Solaranlage da ist

Vermutlich sind unter den ‘early adoptern’ der Heimautomation auch viele Menschen, die auch in anderen Dingen einen Hang zu moderner Technik haben. Zum Beispiel Photovoltaik oder Elektroautos. Daher wollen wir in dieser Übung den Strom vom Dach, das Auto und die Heimautomation miteinander verknüpfen:

Das Auto soll tagsüber nur dann laden, wenn die Photovoltaikanlage dafür ausreichend Strom liefert. Andernfalls soll es nachts den Niedertarif nutzen.

Wir brauchen also die momentane Leistung des Solardachs und wir brauchen eine Möglichkeit, das Ladegerät des Autos davon abhängig zu schalten. Ersteres ist einfach: Es gibt ja bereits einen ioBroker Adapter für den Fronius Wechselrichter, der unter Anderem auch die Momentleistung und die Netto-Leistung vom/ins Netz ausspuckt. Falls Sie einen Wechselrichter eines anderen Herstellers haben, gibt es auch da vermutlich Möglichkeiten, die Leistungsdaten auszulesen.

Unser Auto lässt sich zwar ebenfalls per Fernsteuerung laden, aber die Schnittstelle dafür ist leider nicht offengelegt, und ausserdem lässt der Hersteller sich die Fernsteuerung recht teuer per Jahresabo bezahlen.

Aber es gibt eine andere Möglichkeit: Man kann das Ladegerät an eine schaltbare Steckdose anschließen. Davon gibt es eine ganze Reihe, zum Beispiel von Homematic etc. Ich hatte aber ein Exemplar von myStrom.

Für dieses existierte zum Zeitpunkt dieses Schreibens noch kein “offizieller” ioBroker Adapter. Ich habe Ihnen im vorigen Kapitel gezeigt, wie man einen inoffiziellen Adapter verwenden kann. Da das myStrom API offengelegt und recht simpel ist, wollen wir die Gelegenheit auch nutzen, einen eigenen ioBroker Adapter für myStrom zu programmieren. Um den Schreib- und Lesefluss hier nicht zu unterbrechen, habe ich das aber in Kapitel 5 ausgelagert. Lesen Sie dort weiter, wenn Sie wissen wollen, wie man einen eigenen Adapter programmiert, oder lesen Sie hier weiter, wenn Sie zunächst sehen wollen, wie die Lade-Logik programmiert wird.

Theorie

Wir beginnen damit, uns zu überlegen. wie wir die Schaltvorgänge steuern wollen. Dazu gibt es folgende Bedingungen zu berücksichtigen.

Erstens: Der myStrom Switch ist für 9.9 A Last spezifiziert, und der Hersteller bestätigte mir auf Anfrage, dass dies als mögliche Dauerlast zu verstehen ist. Allerdings zieht der Netzadapter des Autos in Standardeinstellung 10A. Auch wenn das nur ein kleiner Unterschied zu sein scheint, möchte ich das dem filigranen myStrom-Switch nicht zumuten (Haben Sie schon einmal die Temperatur an einem gewöhnlichen Netzstecker gefühlt, durch den einige Stunden lang 10 A, also 2400 Watt geflossen sind?)

Glücklicherweise lässt sich das Ladegerät auch so konfigurieren, dass es maximal 5A, also 1200 Watt zieht. Das geht dann problemlos, allerdings dauern Ladevorgänge dann natürlich doppelt so lang. Da unser typischer Auto-Tagesbedarf aber nur bei 6-8 kWh liegt, ist das gerade noch erträglich.

Zweitens: Vermutlich ist es nicht so gut, wenn das Ladegerät bei jeder vorbeiziehenden Wolke aus- und dann wieder eingeschaltet wird.

Um diese Anforderungen zu erreichen, spezifizieren wir:

Das Ladegerät wird eingeschaltet, wenn ausreichend Strom zur Verfügung stehen, und es zieht dann maximal 1200 Watt.

Wenn es einmal eingeschaltet ist, soll es mindestens 10 Minuten lang eingeschaltet bleiben, egal wieviel Strom hereinkommt.

Danach soll es sich wieder ausschalten, sobald die Solaranlage nicht mehr ausreichend Strom liefert. Frühestens nach 10 Minuten kann es sich wieder einschalten.

Praxis

Obige Spezifikation wollen wir nun als Script formulieren. Das erweist sich dann, nach diesen Vorüberlegungen, doch wieder als erstaunlich einfach:

var power_pv="fronius.0.powerflow.P_PV" // was wir produzieren
var power_use="fronius.0.powerflow.P_Grid" // was wir vom/zum Netz beziehen/liefe\
rn
var mystrom_switch="mystrom.1.switchState" // Der Schalter

createState("loadcar_manual",2) // Ein, Aus oder Automatik.

/*
 Ladevorgang Ein oder Ausschalten, falls Automatik-Modus
*/
function toggle(mode){
    if(getState("javascript.0.loadcar_manual").val==2){
        if(getState(mystrom_switch).val != mode){
            log("toggle "+mode, 'info')
            setState(mystrom_switch,mode)
        }
    }
}

/*
  manuelles Einschalten
*/
on({id: "javascript.0.loadcar_manual",val: 0},function(){
    log("manual on")
    setState(mystrom_switch,true)
})

/*
  manuelles Ausschalten 
*/  
on({id: "javascript.0.loadcar_manual",val: 1},function(){
    log("manual off")
    setState(mystrom_switch,false)
})

schedule("*/10 7-19 * * *",function(){
    var net_flow=getState(power_use).val
    // log("available: "+getState(power_pv).val+", net flow: "+net_flow,'info')
    if( net_flow < -1500){
        toggle(true)
    }else if(net_flow > 0){
        toggle(false)
    }
})

schedule("5 21 * * *", function(){
    log("night schedule: on")
    toggle(true)
    
})

schedule("59 6 * * *", function(){
    log("day schedule: off")
    toggle(false)
})

Die Funktion toggle schaltet an oder aus, wenn der Schalter auf ‘automatik’ steht, ganz ähnlich, wie wir es bei der Außenbeleuchtung gemacht haben. Die nächsten zwei Funktionen schalten den Strom absolut ein oder aus. Das können wir brauchen, wenn wir das Auto auch mal aufladen wollen, ohne genug Sonnenstrom zu haben.

Mittels der dann folgenden Schedule-Funktion führen wir alle 10 Minuten zwischen 7 und 19 Uhr Code aus, welcher den Stromfluss vom bzw. zum Netz prüft. Wenn mehr als 1500 Watt exportiert werden, wird das Ladegerät eingeschaltet und zieht dann maximal 1200 Watt. Wenn kein Strom mehr exportiert wird (also die Leistung nicht mehr reicht, um den Verbrauch zu decken), wird die Steckdose wieder ausgeschaltet. Da die Funktion nur alle 10 Minuten ausgeführt wird, werden zu schnelle Schaltvorgänge von vornherein vermieden.

Die nächsten beiden Schedule-Funktionen sorgen dafür, dass die Steckdose jeden Abend um 21:05 eingeschaltet, und jeden Morgen um 06:59 wieder ausgeschaltet wird, falls sie auf ‘Automatik’ steht. (Niedertarif ausnutzen, falls die Sonne des vorigen Tages nicht für volle Ladung gereicht hat).

Den etwas kryptischen Inhalt der “schedule” Ausdrücke können Sie automatisch setzen lassen, wenn Sie oben rechts auf “Cron” klicken.

Fernseher leise stellen, wenn das Telefon klingelt

Das ist nun sehr einfach:

const ringing="tr-064.0.callmonitor.ringing"
const volume="lgtv.0.volume"

on({id: ringing,val:true},function(){
    setState(volume,10);
})

Heizung regeln

Hier sind ein wenig mehr Gedanken notwendig. Die Heizung ist ja, vor allem in ihrer modernen Ausprägung als Bodenheizung, ein vergleichsweise träges Gerät. Wenn wir sie erst dann aufdrehen, wenn die Temperatur unangenehm kühl ist, dann werden wir einige Zeit frieren, und dann schwitzen, weil die Regelung überschießt. Wir müssen zusätzliche Parameter in die Regelung einbeziehen. Dies ist in erster Linie die Außentemperatur, und die Erwartung über deren künftigen Verlauf. Wenn die Aussentemperatur sinkt, müssen wir damit rechnen, heizen zu müssen. Wenn die Vorhersage aber baldiges Steigen prophezeit, müssen wir weniger heizen, als wenn es noch kälter wird. Wenn die Innentemperatur nur sinkt, weil jemand ein Fenster geöffnet hat, dann sollten wir überhaupt nicht nachheizen, sondern warten, bis das Fenster wieder geschlossen ist.

Das sind bereits eine ganze Menge “unscharfer” Parameter, die berücksichtigt werden müssen. Dazu kommt eine ganz wesentliche weitere einzubindende Konstante: Die Qualität der Isolation. Bei exakt gleicher Außen- und Innentemperatur wird der Heizbedarf dennoch je nach Isolation unterschiedlich sein. Wir brauchen also eine Konstante, die dieses Element berücksichtigen kann. Leider ist das nicht wirklich eine Konstante: Bei hoher Differenz von Außen- zu Innentemperatur wird der Wärmeverlust größer sein, als bei kleiner Differenz. Es wird also eine von Haus zu Haus unterschiedliche Kennlinie sein, die den Heizbedarf je nach Außen- und Innentemperatur angibt. Wenn Ihr Haus neuer als vielleicht ca. 20 Jahre ist, wird der Hersteller Ihrer Heizung diese Kennlinie bereits in die Regelung eingespeist haben. Leider nützt das nicht unbedingt viel für eine in der Heimautomation einzubindende Steuerung. Wir wollen eher die Temperatur je Zimmer regeln, während die Heizungsautomatik die gesamte Heizleistung der Anlage anpasst.

Derartige Aufgaben übernimmt in vielen Fällen bereits ein Raum- oder Etagenthermostat. Dieser berücksichtigt aber oben genannte Faktoren gar nicht, sondern vergleicht nur eine Soll- mit einer Ist-Temperatur.

Damit wird unsere Aufgabe ein wenig einfacher: Wir brauchen nicht bei Adam und Eva anfangen, sondern wir müssen nur den existierenden Thermostaten so “aufbohren”, dass er mehr Parameter einbezieht, und dass er zu spezifischen Regelprogrammen je nach Tages- und Nachtzeit, Ferien etc. fähig ist.

Die für jeden zu regelnden Raum gültige Kennlinie kann man nicht errechnen. Zu vielfältig sind die Einflüsse. Man kann der Steuerung aber einen Lernmodus spendieren: Eine Zeit lang soll sie nur beobachten, wie die existierende Regelung bei jeder Außen- und Innentemperatur reagiert, und wie gross der Fehler jeweils ist. Dann beginnt sie, in die Regelung einzugreifen, wobei sie weiterhin versucht zu lernen, in welchen Fällen sie nicht perfekt reguliert hat (nämlich dann, wenn die Ist-Temperatur von der Soll-Temperatur abweicht), und die Kennlinie so laufend anzupassen.

Sie sehen, dieses Projekt wird deutlich aufwändiger, als die Bisherigen. Ich werde es deshalb in mehreren Etappen angehen, und im Rahmen dieses Buches auch nicht ganz vollständig implementieren.

Wetterdaten bereitstellen

Wir möchten, dass unsere Skripte und später auch externe Programme eine gewisse Vorstellung davon haben können, wie das Wetter ist und wird. Dinge wie Heizungsautomatisierung, Markisensteuerung und Schaltung von grösseren Stromverbrauchern in Abhängigkeit von der erwarteten Windstärke und Sonneneinstrahlung lassen sich dann besser planen.

Es gibt mehrere Wetterdienste, die ein REST-API bieten. Ich habe mehr oder weniger zufällig den Dienst von DarkSky ausgewählt. Ein Hauptvorteil dieses Dienstes ist, dass tausend Abfragen pro Tag kostenlos sind. Das sollte für unsere Zwecke reichen. Bevor man den Dienst nutzen kann, muss man sich allerdings registrieren: https://darksky.net/register. Man erhält dann einen API-KEY, denn man seinen Abfragen mitgeben muss (und den man tunlichst geheim halten sollte).

Um lokale Wetterdaten zu erhalten, benötigt man die exakten Koordinaten als dezimale Längen, und Breitengrade. Die kann man zum Beispiel bei http://mygeoposition.com durch Angabe der Adresse erfahren.

Eine simple Abfrage wie https://api.darksky.net/forecast/API_KEY/BREITENGRAD,LAENGENGRAD?units=si&lang=de liefert dann eine ganze Reihe von Wetterdaten im handlichen JSON-Format.

Wir wollen diese Daten alle paar Stunden abholen und für uns relevante Teile davon für interne Abfragen vorhalten.

Für einfacheres Rechnen mit Datum und Zeit verwenden wir die moment.js Library. Diese ist leider nicht standardmässig mit ioBroker installiert. Das wollen wir nun nachholen:

ssh pi@homeview.local
cd /opt/iobroker/node_modules/iobroker.javascript
sudo npm install --save moment

Vielleicht wundern Sie sich über das Verzeichnis, in das wir wechseln, um moment.js zu installieren. Es ist das Verzeichnis des javasript-interpreters in ioBroker, der wiederum, wie alle Adapter, im node_modules-Verzeichnis der ioBroker Installation gespeichert ist. Generell müssen alle Libraries, die man in Skripten verwenden will, in iobroker.javascript installiert sein.

Dann richten Sie Ihren Browser auf die Skript-Konsole http://homeview.local:8081/#javascript und geben dort folgendes Skript ein (oder fügen es mit copy&paste aus der Begleitsoftware dieses Buchs ein):

Wetterbericht einlesen
  1 var latitude=46.631094047;
  2 var longitude=7.72370708; 
  3 var API_KEY="Sollte geheim bleiben"; 
  4 
  5 const request=require('request')
  6 const moment=require('moment')
  7 
  8 const NOW = "wetter.darksky.jetzt."
  9 const TODAY = "wetter.darksky.heute."
 10 const TOMORROW = "wetter.darksky.morgen."
 11 
 12 const attribution = "https://darksky.net/poweredby/"
 13 createState("wetter.darksky.lastcall","")
 14 createState(NOW + "temp", 0)
 15 createState(NOW + "bedeckt", 0)
 16 createState(NOW + "wind", 0)
 17 createState(NOW + "niederschlag", 0)
 18 
 19 
 20 createState(TODAY + "maxtemp", 0)
 21 createState(TODAY + "mintemp", 0)
 22 createState(TODAY + "bedeckt", 0)
 23 createState(TODAY + "wind", 0)
 24 createState(TODAY + "niederschlag", 0)
 25 
 26 createState(TOMORROW + "maxtemp", 0)
 27 createState(TOMORROW + "mintemp", 0)
 28 createState(TOMORROW + "bedeckt", 0)
 29 createState(TOMORROW + "wind", 0)
 30 createState(TOMORROW + "niederschlag", 0)
 31 
 32 
 33 var call = "https://api.darksky.net/forecast/" + API_KEY + "/" + latitude + "," +\
 34  longitude + "?units=si&lang=de&exclude=minutely,daily,flags,alerts"
 35 
 36 console.log("powered by: " + attribution)
 37 
 38 const getMinMax = function (range, curr, accum) {
 39   const currtime = moment(curr.time * 1000)
 40   if (currtime.isAfter(range[0]) && currtime.isBefore(range[1])) {
 41     accum.minTemp = Math.min(accum.minTemp, curr.temperature)
 42     accum.maxTemp = Math.max(accum.maxTemp, curr.temperature)
 43     accum.wind = Math.max(accum.wind, curr.windSpeed)
 44     accum.cloudsum = accum.cloudsum + curr.cloudCover
 45     accum.precipsum = accum.precipsum + curr.precipIntensity * curr.precipProbabi\
 46 lity
 47     accum.counter = accum.counter + 1
 48     accum.cloud = accum.cloudsum / accum.counter
 49     accum.precip = accum.precipsum / accum.counter
 50   }
 51   return accum
 52 }
 53 
 54 function fetch() {
 55   request(call, function (error, response, body) {
 56     if (error) {
 57       log.warning("Error! " + error)
 58     } else {
 59       if (response && (response.statusCode == 200)) {
 60         const forecast = JSON.parse(body)
 61         const today = forecast.hourly.data
 62         const now = forecast.currently
 63         setState(NOW + "temp", now.temperature)
 64         setState(NOW + "bedeckt", Math.round(100 * now.cloudCover))
 65         setState(NOW + "wind", now.windSpeed)
 66         setState(NOW + "niederschlag", now.precipIntensity)
 67 
 68         const tsNow = moment(now.time * 1000)
 69         const tomorrow = tsNow.clone()
 70         tomorrow.add(1, 'days')
 71         const spanToday = [tsNow.clone(), tsNow.clone().endOf('day')]
 72         const spanTomorrow = [tomorrow.clone().startOf("day"), tomorrow.clone().e\
 73 ndOf("day")]
 74         const accumTemplate = {
 75           minTemp: 100, maxTemp: -100, wind: -5, cloudsum: 0, counter: 0, precips\
 76 um: 0
 77         }
 78 
 79         const todayMinMax = today.reduce((accum, curr) => getMinMax(spanToday, cu\
 80 rr, accum)
 81           , Object.assign({}, accumTemplate))
 82         const tomorrowMinMax = today.reduce((accum, curr) => getMinMax(spanTomorr\
 83 ow, curr, accum), Object.assign({}, accumTemplate))
 84         setState(TODAY + "mintemp", todayMinMax.minTemp)
 85         setState(TODAY + "maxtemp", todayMinMax.maxTemp)
 86         setState(TODAY + "wind", todayMinMax.wind)
 87         setState(TODAY + "bedeckt", Math.round(100 * todayMinMax.cloud))
 88         setState(TODAY + "niederschlag", Math.round(100 * todayMinMax.precipsum))
 89         setState(TOMORROW + "mintemp", tomorrowMinMax.minTemp)
 90         setState(TOMORROW + "maxtemp", tomorrowMinMax.maxTemp)
 91         setState(TOMORROW + "wind", tomorrowMinMax.wind)
 92         setState(TOMORROW + "bedeckt", Math.round(100 * tomorrowMinMax.cloud))
 93         setState(TOMORROW + "niederschlag", Math.round(100 * tomorrowMinMax.preci\
 94 psum))
 95         setState("wetter.darksky.lastcall",tsNow.toString())
 96       } else {
 97         console.log("no response")
 98       }
 99     }
100   })
101 }
102 
103 schedule("20 4,8,11,14,17,20,23 * * *",fetch)

Ganz am Anfang kommen die Daten zu Koordinaten und API-KEY, die Sie für Ihre Gegebenheiten anpassen müssen.

Dann muss man sich überlegen, welche der Angaben von DarkSky man überhaupt benötigt. Ich habe mich hier entschlossen, nur die momentanen Daten, sowie die Tageshöchst- und Tiefstwerte der Temperatur für den Rest des heutigen und des ganzen morgigen Tages zu speichern, ausserdem die maximale Windgeschwindigkeit, den durchschnittlichen Bewölkungsgrad und schließlich die erwartete Niederschlagsmenge und -wahrscheinlichkeit. Für jede dieser Positionen wird ein State erstellt. Die Attribution geben wir auf der Konsole aus, da die Lizenzbedingungen von DarkSky verlangen, dass man die Herkunft der Daten deklariert. Ein kleiner Preis für so einen Service.

Die Funktion getMinMax() in Zeile 30ff werden wir später nutzen, um die jeweiligen Daten aus einem zu übergebenden Zeitraum zu fischen. Dann kommt in der Funktion fetch() ab Zeile 44 die eigentliche Arbeit: Mit “request” setzen wir einen REST-Call an DarkSky ab, und wenn die Antwort Erfolg signalisiert, parsen wir deren body nach JSON. Dann kommt ein wenig Zeit-Rechnerei, um Anfang und Ende der interessierenden Zeiträume zu definieren, und mit diesen (spanToday und spanTomorrow) füttern wir eine reduce-Funktion, welche die vorhin genannte getMinMax()-Funktion als Parameter erhält. Reduce gehört zu den Funktionen, die man fast bei jeder funktionalen Sprache findet. Sie “reduziert” eine Collection auf einen einzigen Wert, indem sie auf jedes Element dieselbe Funktion anwendet, die jeweils das bisherige Ergebnis (accum) und das momentane Element (curr) als Parameter erhält. Was wir am Ende erhalten, ist todayMinMax resp. tomorrowMinMax, also zwei Objekte mit den jeweils zu speichernden Extrakten aus den Gesamtdaten. Diese Extrakte schreiben wir dann in die vorhin definierten States.

Ganz am Ende sorgen wir dafür, dass fetch() 7 Mal pro Tag ausgeführt wird, damit die Daten einigermassen aktuell sind (Das ist natürlich eine vollkommen willkürliche Zahl, die weit genug von den 1000 Abfragen weg ist, die DarkSky erlaubt, so dass Sie sie gern auch erhöhen dürfen.)

Vielleicht ist Ihnen aufgefallen, dass der Ausdruck am Ende lautet:

schedule("* 4,8,11,14,17,20,23 * * *",fetch)

und nicht etwa:

schedule("* 4,8,11,14,17,20,23 * * *",fetch())

Man muss als Callback den Namen der Funktion angeben, und nicht etwa die Funktion ausführen. Also immer ohne Klammerpaar schreiben.

Und weiter? Nun, die so erzeugten States können wir mit anderen Skripten oder von aussen auslesen. Ich werde das später noch zeigen.

SMS Warnung

Es kommt zwar nicht sehr häufig vor, aber stellen Sie sich vor, während Ihres Skiurlaubs fällt die Heizung aus. Die Leitungen frieren ein, und wenn Sie nach Hause kommen, empfängt Sie eine Überschwemmung. An sich hätten Sie ja eine Fernabfrage für alle Bestandteile Ihres Smarthomes gehabt, aber Sie hatten nicht daran gedacht, jeden Tag darauf zu schauen. Wieso auch.

Das ist verhinderbar, wenn Ihr Smarthome Ihnen bei Problemen aller Art eine SMS schickt. Die werden Sie vermutlich bemerken.

Aber wie kann der Raspberry Pi eine SMS versenden? Nun, dafür gibt es mehrere Möglichkeiten. Beispielsweise könnten Sie ihn via Bluetooth oder Kabel mit einem nicht mehr gebrauchten Handy oder einem GSM-Modem verbinden. Allerdings brauchen Sie dann immer noch eine SIM-Karte, die mehr kostet, als die Lösung, die ich Ihnen im Folgenden vorstellen werde. Falls Sie doch den Weg übers Handy gehen wollen, liefere ich Ihnen das Stichwort ›Gnokii‹ zum weiter googeln.

Ich werden Ihnen hier aber den Weg über einen SMS Service zeigen. Von diesen gibt es mehrere (Such-Stichwörter sind z.b. ›sms api‹ pder ŝeb sms‹), allen gemeinsam ist, dass man über ein vorgegebenes API via Internet darauf zugreifen und SMS versenden kann. Ich verwende hier aspsms.com, weil es unter Anderem ein nodejs.api hat, und weil man einige kostenlose SMS zum Testen bekommt, und weil einmal gekaufte SMS Credits ungegrenzt gültig bleiben. Der Preis einer SMS ist bei ungefähr 8 cents.

Als Erstes müssen Sie einen Account bei https://www.aspsms.com/de/registration/ eröffnen. Sie bekommen dann einen Userkey und ein Passwort. Mit diesen Credentials kann der Raspberry dann auf das API zugreifen.

Zunächst installieren Sie das NodeJS API:

ssh pi@homeview.local
cd /opt/ioBroker
npm install mod-aspsms

— tbd —

Skripte sichern

Nachdem wir jetzt schon so viel Arbeit in unsere Skripte gesteckt haben, stellen Sie sich vermutlich die Frage, was eigentlich bei einem Defekt des Raspberry oder der SD-Karte geschieht. Nun, wenn die SD-Karte beschädigt wird, dann sind die Skripte futsch. Und leider geht eine SD-Karte mit Sicherheit früher oder später kaputt, denn sie erträgt prinzipbedingt nur eine begrenzte Zahl von Schreibzugriffen. Der Wert der Skripte für Sie dürfte zu diesem Zeitpunkt den Wert der SD-Karte um ein Vielfaches übersteigen. Stellen Sie sich vor, Sie haben Ihr Smarthome perfekt eingerichtet, wie viel Zeit würde es Sie wohl kosten, alle Skripte wieder zu rekonstruieren? Eben.

Abb. 3.6: Skripte exportieren
Abb. 3.6: Skripte exportieren

Glücklicherweise ist guter Rat hier ausnahmsweise billig (und im Preis dieses Buches bereits enthalten): Klicken Sie im Skripte Fenster einfach auf den Button ‘Export von allen Skripten’. Diese werden dann gezippt, je nach Einstellung Ihres Browsers entweder mit oder ohne Frage nach dem gewünschten Speicherort, als Backup heruntergeladen. Falls Sie Ihren Heimserver jemals neu aufsetzen müssen, können Sie die so gespeicherten Skripte ebenso einfach mit dem daneben liegenden Button “Import von allen Skripten” im neuen ioBroker wieder installieren. Sie müssen nur noch dafür sorgen, dass die heruntergeladene .zip Datei an einem sicheren und wiederauffindbaren Ort gespeichert wird. Idealerweise bewahrt man mehrere Generationen von mehreren Daten auf, damit man einen eventuell irgendwann eingeschlichenen Fehler ausbügeln kann.