Beschattungslösung mit Node-RED und BOSCH SMART HOME

HINWEIS: Unbezahlte Werbung aufgrund von Markennennungen

Im Smart-Home Bereich gibt es mittlerweile wirklich viele Dinge, die eher nur Spielereien sind und wenig Nutzen schaffen. Eine smarte Rollladensteuerung, die je nach Sonnenstand und Tageszeit die Fenster beschattet und für ein angenehmes Raumklima sorgt, ist jedoch ein echter Gewinn und möchte ich nicht mehr missen.

Nach einigen Recherchen habe ich mich Anfang des Jahres für die Lösung von Bosch Smart Home entschieden. Das Bosch Smart-Home System baut teilweise auf der Technik von Homematic IP auf, kommt jedoch mit einigen Verbesserungen daher. Das kaufentscheidende Argument war für mich, dass ich bei Bosch, die Steuerung auch autark im Heimnetzwerk nutzen kann und auf keine Internetverbindung oder Clouddienst angewiesen bin. Daneben ist die Smartphone App von Bosch deutlich besser als die von Homematic IP und wer ein Philipps Hue System hat, kann seine Leuchten auch mit der Bosch-App steuern und automatisieren.

Bisher wurden meine Rollläden, wie man das kennt, über normale Taster in den jeweiligen Räumen bedient. Das bedeutet für mich, morgens bevor ich aus dem Haus gehe, einmal in allen Zimmern die Rollläden öffnen und abends wiederum herunterfahren. Zusätzlich auch überlegen, ob es an dem Tag heiß wird und ggf. morgens schon mal die Süd-Westseite beschatten damit Tagsüber nicht die Sonne reinknallt. Als Programmierer reizt es mich natürlich diesen Prozess zu automatisieren. Wie ich dabei vorgegangen bin könnt ihr nun in folgendem Artikel lesen.

Schritt 1. Hardware

Zunächst wurden alle Rollladentaster im Haus durch die Bosch Unterputz Rollladensteuerungsmodule ersetzt. Hierzu musste ich in die bestehenden Steckdosen ein Neutralleiter einziehen, da die analogen Rolladenschalter ohne Neutralleiter funktionierten. Glücklicherweise war in den benachbarten Steckdosen Neutralleiter vorhanden die ich brücken konnte. Das bestehende Schalterprogramm konnte durch die Verwendung von passenden Adaptern erhalten bleiben.

Da hier arbeiten am 230V Netz durchgeführt werden, muss ich an dieser Stelle darauf hinweisen, bitte nur selbst machen, wenn ihr wisst, was ihr tut und eine entsprechenden fachlichen Hintergrund aufweist! Wenn ihr euch nicht auskennt lasst die Finger weg und überlasst die Installation einer Fachkraft.

Die Installation und Inbetriebnahme verlief problemlos und ohne nennenswerte Schwierigkeiten. Von Haus aus bietet die Bosch App leider nur die Möglichkeit, die Rollläden zum Sonnenaufgang bzw. Sonnenuntergang oder einer speziellen Uhrzeit zu steuern. Hier kommt nun die zweite Hardware, ein Raspberry PI ins Spiel.

Im Zuge meines Projekts habe ich mir einen Raspberry Pi 4 mit 4 GB Arbeitsspeicher gekauft der als Smart-Home Zentrale dienen soll. Die Dokumentationsunterlagen der PI-Foundation sind wirklich gut und ermöglichen einem den einfachen Einstieg. Empfehlen kann ich zusätzlich folgenden Leitfaden zur Erstinbetriebnahme. Nach etwa zwei Stunden Installation und Konfiguration des Betriebssystems konnte ich mit Node-RED beginnen das Bosch System zu steuern.

Schritt 2. Software

Bei der Frage nach der richtigen IoT-Software gibt es mittlerweile eine große Auswahl für den Raspberry PI, ich habe mich für Node-RED entschieden, da mir die Arbeitsweise mit den Flows am Besten gefallen hat, wer alternativen sucht dem sei hier noch ioBroker ans Herz gelegt. Wer sich einen Überblick verschaffen möchte, dem empfehle ich noch folgendes Video von c’t, in dem verschiedene Systeme verglichen werden.

Node-RED ist ein flussbasiertes Programmiertool, das ursprünglich von IBM entwickelt wurde und seit 2016 Teil der JS Foundation ist. Die flussbasierte Programmierung wurde in den 1970er Jahren von J. Paul Morrison erfunden und beschreibt das Verhalten einer Anwendung als Netzwerk von Blackboxes oder „Nodes“, wie sie in Node-RED genannt werden. Jede Node hat einen genau definierten Zweck. Es werden einige Daten gegeben, es macht etwas mit diesen Daten und dann gibt es diese Daten weiter. Das Netzwerk ist für den Datenfluss zwischen den Knoten verantwortlich. Dieses Modell eignet sich sehr gut für die visuelle Darstellung und macht es einem breiteren Benutzerkreis zugänglicher. Wenn jemand ein Problem in einzelne Schritte aufteilen kann, kann er einen Fluss betrachten und ein Gefühl dafür bekommen, was er tut.​[1]​

Für Node-RED gib es eine große Anzahl an Zusatzmodulen u.a. auch für das Bosch Smart Home System. https://flows.nodered.org/node/node-red-contrib-bosch-shc

Schritt 3. Steuerung in Node-RED

Der Sonnenstand

Um meine Steuerung zu Programmieren war es zunächst nötig eine Messung an den Hausseiten durchzuführen. Denn ich musste wissen, wann die Sonne auf ein Fenster scheint und wann das Fenster im Schatten liegt. Die Uhrzeit kann man hierfür nicht nutzen, denn diese ändert sich mit den Jahreszeiten. Der Begriff der Stunde lautet Azimut, ein Begriff aus der Astronomie der ein nach einer Himmelsrichtung orientierten Horizontalwinkel darstellt.

Zunächst wurde der Sonnenverlauf beobachtet und die Azimut-Winkel bestimmt, wann ein Fenster im Bereich der Sonneneinstrahlung liegt.

Beispiel: Auf der Ostseite beginnt die Sonne ab einem Winkel von 60° (Nord-Ost) bis zu einem Winkel von ca. 140° (Süd-Ost) in das Fenster zu scheinen. Diese Zahlen habe ich nun für alle Hausseiten bestimmt.

In Node-RED benötigt man lediglich noch eine Funktion, welche eine Message sendet, sobald die Sonne den jeweiligen Azimut erreicht hat. Glücklicherweise gibt es hierfür eine fertige Node Names node-red-contrib-sun-position. Durch Eingabe meines Standorts per Geokoordinaten berechnet diese NODE alle wichtigen Werte zur Sonnenposition, perfekt.

Die Regel für die Ost-Fenster habe ich wie folgend definiert:
BESCHATTUNG AKTIVIEREN WENN: AZIMUT ZWISCHEN 60° und 140° und SONNENHÖHE (Meridian) über dem Horizont >5°. (Wenn die Sonne gerade erst aufgegangen ist möchte ich noch nicht beschatten.)

Dieses Regelwerk alleine reicht jedoch noch nicht, denn ein Faktor ist noch nicht berücksichtigt. Das Wetter, aktuell würde bei Regen und Bewölkung diese Regel ebenfalls greifen.

Wetter

Für die Steuerung habe ich zusätzliche Regeln definiert. Zum einen wird die Beschattung anhand der vorhergesagten Tages-Höchsttemperatur geregelt. Wenn es an einem Frühlingstag maximal 20°C warm wird, dann werden die Fenster nur zur hälfte beschattet (Rollladenposition 50%), bei mehr wie 25°C wird zu 80% beschattet und liegt die Temperatur über 30°C dann wird zu 100% beschattet. Aktuelle Wetterdaten für den persönlichen Standort kann man mit der “ONE-CALL-API” von openweathermap abrufen siehe: https://openweathermap.org/api/one-call-api.

Zusätzlich nutze ich standardisierte Wetterzustände der API, wie REGEN oder BEWÖLKT um die Steuerung noch besser auf das Wetter abzustimmen.

Wie das ganze nun in Node-Red aussieht könnt ihr als nächstes sehen.

Node-RED Flows

Es macht wenig Sinn, den ganzen Code der Flows zu veröffentlichen, da dieser auf das Gebäude angepasst wurde. Ich habe daher nur den Subflows der Rollladensteuerung publiziert. Dieser enthält die oben erläuterten Logikfunktionen und kann gerne für eigene Projekte genutzt werden kann.

[{"id":"3056b376.48edbc","type":"change","z":"238dca10.02b2b6","name":"payload = Beschattung.Level","rules":[{"t":"set","p":"payload","pt":"msg","to":"Beschattung.Level","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":2000,"y":700,"wires":[[]]},{"id":"c49bbd67.885b","type":"function","z":"238dca10.02b2b6","name":"Beschattung_SetValues","func":"let c = msg.sun\nmsg.Beschattung.Azimuth = Number(c.azimuth.toFixed(1));\nmsg.Beschattung.Altitude = Number(c.altitude.toFixed(1));\nmsg.Beschattung.Level = Number(msg.Beschattung.Level);\nmsg.Beschattung.DailyMaxTemp = global.get('dailymaxtemp');\nmsg.Beschattung.RainProtection = global.get('RainProtection');\nmsg.Beschattung.SunProtection = global.get('SunProtection');\nmsg.Beschattung.WeatherCondition = global.get('WeatherCondition');\n\nreturn msg;","outputs":1,"noerr":0,"x":270,"y":180,"wires":[["112bb28b.d1ff8d"]]},{"id":"ffccc5a2.ce7b48","type":"function","z":"238dca10.02b2b6","name":"SetFinalValues","func":"\nmsg.Beschattung.Percentage = Number(((1 - msg.Beschattung.Level) *100).toFixed(1));\nmsg.Beschattung.ImageURL = 'http://openweathermap.org/img/wn/' + global.get('openweather.current.weather[0].icon') + \"@2x.png\";\nmsg.payload = msg.Beschattung.Protection;\nreturn msg;","outputs":1,"noerr":0,"x":2140,"y":500,"wires":[["af049009.a16ee"]]},{"id":"1eb400c4.aedd0f","type":"rbe","z":"238dca10.02b2b6","name":"","func":"rbei","gap":"","start":"","inout":"out","property":"Beschattung.Protection","x":1470,"y":700,"wires":[["73bb07a6.76d728"]]},{"id":"19afff6e.ed3631","type":"switch","z":"238dca10.02b2b6","name":"Tageshöchsttemperatur","property":"dailymaxtemp","propertyType":"global","rules":[{"t":"lte","v":"15","vt":"str"},{"t":"lt","v":"20","vt":"str"},{"t":"lt","v":"24","vt":"str"},{"t":"lt","v":"30","vt":"str"},{"t":"gte","v":"30","vt":"str"}],"checkall":"false","repair":false,"outputs":5,"x":890,"y":420,"wires":[["27699aa3.f1c626"],["65f25e5f.27f79"],["9d4f078a.a35528"],["304a7578.3b7b1a"],["126fdcc.3e36b23"]]},{"id":"304a7578.3b7b1a","type":"change","z":"238dca10.02b2b6","name":"","rules":[{"t":"set","p":"Beschattung.Level","pt":"msg","to":"0.2","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":1180,"y":460,"wires":[["82baa30c.5791d"]]},{"id":"1c276bcd.62d574","type":"comment","z":"238dca10.02b2b6","name":"0 = geschlossen | 1 = geöffnet","info":"0 = geschlossen\n1 = geöffnet","x":1180,"y":300,"wires":[]},{"id":"126fdcc.3e36b23","type":"change","z":"238dca10.02b2b6","name":"","rules":[{"t":"set","p":"Beschattung.Level","pt":"msg","to":"0","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":1180,"y":500,"wires":[["82baa30c.5791d"]]},{"id":"9d4f078a.a35528","type":"change","z":"238dca10.02b2b6","name":"","rules":[{"t":"set","p":"Beschattung.Level","pt":"msg","to":"0.6","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":1180,"y":420,"wires":[["82baa30c.5791d"]]},{"id":"65f25e5f.27f79","type":"change","z":"238dca10.02b2b6","name":"","rules":[{"t":"set","p":"Beschattung.Level","pt":"msg","to":"0.8","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":1180,"y":380,"wires":[["82baa30c.5791d"]]},{"id":"27699aa3.f1c626","type":"change","z":"238dca10.02b2b6","name":"","rules":[{"t":"set","p":"Beschattung.Level","pt":"msg","to":"1","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":1180,"y":340,"wires":[["82baa30c.5791d"]]},{"id":"af049009.a16ee","type":"template","z":"238dca10.02b2b6","name":"INFO","field":"Beschattung.Info","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"🌞 *Schutz für {{topic}}: {{Beschattung.Protection}}*\nRolladen-Sollwert: {{Beschattung.Percentage}}%\nStatus: {{Beschattung.Status}}\nWetter: {{Beschattung.WeatherCondition}} / {{Beschattung.DailyMaxTemp}}°C\nAzimuth: {{Beschattung.Azimuth}} / {{Beschattung.AzimuthWindow}} \nAltitude: {{Beschattung.Altitude}}","output":"str","x":1310,"y":700,"wires":[["1eb400c4.aedd0f"]]},{"id":"73bb07a6.76d728","type":"within-time-switch","z":"238dca10.02b2b6","name":"","positionConfig":"d836509a.67bc4","startTime":"sunriseEnd","startTimeType":"pdsTime","startOffset":0,"startOffsetType":"none","startOffsetMultiplier":60000,"endTime":"sunset","endTimeType":"pdsTime","endOffset":0,"endOffsetType":"none","endOffsetMultiplier":60000,"timeDays":"*","timeOnlyOddDays":false,"timeOnlyEvenDays":false,"timeMonths":"*","timedatestart":"","timedateend":"","propertyStart":"","propertyStartType":"none","propertyStartCompare":"true","propertyStartThreshold":"","propertyStartThresholdType":"num","startTimeAlt":"","startTimeAltType":"entered","startOffsetAlt":0,"startOffsetAltType":"none","startOffsetAltMultiplier":60000,"propertyEnd":"","propertyEndType":"none","propertyEndCompare":"true","propertyEndThreshold":"","propertyEndThresholdType":"num","endTimeAlt":"","endTimeAltType":"entered","endOffsetAlt":0,"endOffsetAltType":"none","endOffsetAltMultiplier":60000,"tsCompare":"0","x":1700,"y":700,"wires":[["3056b376.48edbc"],[]]},{"id":"112bb28b.d1ff8d","type":"switch","z":"238dca10.02b2b6","name":"Hausseite","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"Ostseite","vt":"str"},{"t":"eq","v":"Südseite","vt":"str"},{"t":"eq","v":"Westseite","vt":"str"},{"t":"eq","v":"Nordseite","vt":"str"}],"checkall":"false","repair":false,"outputs":4,"x":200,"y":420,"wires":[["b0ee3967.d11488"],["e158912a.7588c"],["ff963db0.5195e"],["bd5be89c.151818"]]},{"id":"b0ee3967.d11488","type":"change","z":"238dca10.02b2b6","name":"Ostseite AltitudeLimit = 14","rules":[{"t":"set","p":"Beschattung.AltitudeLimit","pt":"msg","to":"15","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":360,"wires":[["19afff6e.ed3631"]]},{"id":"82baa30c.5791d","type":"function","z":"238dca10.02b2b6","name":"","func":"\nreturn msg;","outputs":1,"noerr":0,"x":1490,"y":420,"wires":[["e341e34f.10384"]]},{"id":"e158912a.7588c","type":"change","z":"238dca10.02b2b6","name":"Südseite AltitudeLimit = 5","rules":[{"t":"set","p":"Beschattung.AltitudeLimit","pt":"msg","to":"5","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":400,"wires":[["19afff6e.ed3631"]]},{"id":"ff963db0.5195e","type":"change","z":"238dca10.02b2b6","name":"Westseite AltitudeLimit = 5","rules":[{"t":"set","p":"Beschattung.AltitudeLimit","pt":"msg","to":"5","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":440,"wires":[["19afff6e.ed3631"]]},{"id":"bd5be89c.151818","type":"change","z":"238dca10.02b2b6","name":"Nordseite AltitudeLimit = 5","rules":[{"t":"set","p":"Beschattung.AltitudeLimit","pt":"msg","to":"5","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":510,"y":480,"wires":[["19afff6e.ed3631"]]},{"id":"e341e34f.10384","type":"function","z":"238dca10.02b2b6","name":"Schutzstatus prüfen","func":"if (msg.Beschattung.RainProtection === true) {\n    msg.Beschattung.Status = \"Regenschutz\";\n    return [ msg, null, null ];\n} else if (msg.Beschattung.SunProtection === true) {\n    msg.Beschattung.Status = \"Sonnenschutz\";\n    return [ null, msg, null ];\n} else {\n    msg.Beschattung.Status = \"Kein Schutz nötig\";\n    return [ null, null, msg ];\n}","outputs":3,"noerr":0,"x":1540,"y":500,"wires":[["3ea768c4.f74d78"],["fa8b2dc6.afd3a"],["71b3a68e.cf2788"]]},{"id":"fa8b2dc6.afd3a","type":"function","z":"238dca10.02b2b6","name":"Prüfungen Sonnenschutz","func":"if (msg.Beschattung.AzimuthWindow === true &&\n    msg.Beschattung.Level != 1 &&\n    msg.Beschattung.Altitude >= msg.Beschattung.AltitudeLimit \n    ) {\n    msg.Beschattung.Protection = true;\n} else {\n    msg.Beschattung.Protection = false;\n    msg.Beschattung.Level = 1;\n}\nreturn msg;","outputs":1,"noerr":0,"x":1850,"y":500,"wires":[["ffccc5a2.ce7b48"]]},{"id":"3ea768c4.f74d78","type":"function","z":"238dca10.02b2b6","name":"Prüfungen Regenschutz","func":"if (msg.topic === \"Westseite\") {\n    msg.Beschattung.Protection = true;\n    msg.Beschattung.Level = 0;\n} else {\n    msg.Beschattung.Protection = false;\n    msg.Beschattung.Level = 1;\n}\nreturn msg;","outputs":1,"noerr":0,"x":1850,"y":460,"wires":[["ffccc5a2.ce7b48"]]},{"id":"71b3a68e.cf2788","type":"function","z":"238dca10.02b2b6","name":"Kein Schutz","func":"msg.Beschattung.Protection = false;\nmsg.Beschattung.Level = 1;\n\nreturn msg;\n","outputs":1,"noerr":0,"x":1810,"y":540,"wires":[["ffccc5a2.ce7b48"]]},{"id":"44ba237e.7c889c","type":"function","z":"238dca10.02b2b6","name":"BackupCode_Beschattung","func":"if (msg.Beschattung.AzimuthWindow === true &&\n    msg.Beschattung.Level != 1 &&\n    msg.Beschattung.WindowProtection === false &&\n    msg.Beschattung.Altitude >= msg.Beschattung.AltitudeLimit \n    ) {\n    msg.Beschattung.Beschatten = true;\n} else if (msg.Beschattung.WindowProtection === true && msg.topic === \"Westseite\") {\n    msg.Beschattung.Beschatten = true;\n    msg.Beschattung.Level = 0;\n} else {\n    msg.Beschattung.Beschatten = false;\n    msg.Beschattung.Level = 1;\n}\nmsg.Beschattung.Percentage = Number(((1 - msg.Beschattung.Level) *100).toFixed(1));\n\nmsg.Beschattung.ImageURL = 'http://openweathermap.org/img/wn/' + global.get('openweather.current.weather[0].icon') + \"@2x.png\";\nmsg.payload = msg.Beschattung.Beschatten;\nreturn msg;","outputs":1,"noerr":0,"x":1860,"y":400,"wires":[[]]},{"id":"d836509a.67bc4","type":"position-config","z":"","name":"Mahlberg","isValide":"true","longitude":"0","latitude":"0","angleType":"deg","timeZoneOffset":"99","timeZoneDST":"0","stateTimeFormat":"3","stateDateFormat":"12"}]

Benachrichtigungen über TELEGRAM-BOT

Zur Kontrolle und für Status-Informationen sende ich mir bei Schaltzeitpunkten eine Nachricht auf das Smartphone über einen Telegram-Bot. Das war vor allem zu Beginn sehr hilfreich, um die Funktion des Systems überwachen zu können und Bugs zu beheben.

Nachfolgender Auszug an Statusmeldungen zeigt, wie im Verlaufe des Tages die Beschattung durchgeführt wird. Pünktlich zum Sonnenaufgang werden alle Rollläden, ausgenommen die im Schlafzimmer 😉, geöffnet. Danach beginnt die Regelung zunächst mit der Ostseite und im weiteren Tagesverlauf wird die West- und Nordseite beschattet. Auf der Südseite habe ich eine Gaube ohne Rollläden und eine Terrassentür, die aufgrund des Vordachs immer im Schatten liegt, daher findet hier keine Beschattung statt. Etwa eine halbe Stunde nach dem Sonnenuntergang werden dann am Abend alle Rollläden geschlossen.

SmartHome, [31.07.20 06:04]
🌞 Beschattung:
Alle Rollläden wurden geöffnet.

SmartHome, [31.07.20 07:30]
🌤 Aktuelles Wetter
Wetterlage: Klarer Himmel
Temperatur: 16.8°C / 67 % rel.Feuchte
tMinMax: 18.63°C / 32.31°C
Taupunkt: 10.6
Luftdruck: 1017 hPa
Regenmenge: l/m²
Wind: 3.6 km/h aus West (340°)

🌞 Aktueller Sonnenstand
Azimuth: 76.7
Altitude: 12.7
Sonnenaufgang: 06:04 Uhr
Sonnenuntergang: 21:04 Uhr

SmartHome, [31.07.20 07:47]
🌞 Schutz für Ostseite: true
Rollladen-Sollwert: 100%
Status: Sonnenschutz
Wetter: Klarer Himmel / 32.32°C
Azimuth: 79.7 / true
Altitude: 15.4

SmartHome, [31.07.20 12:07]
🌞 Schutz für Ostseite: false
Rollladen-Sollwert: 0%
Status: Sonnenschutz
Wetter: Klarer Himmel / 31.42°C
Azimuth: 141.4 / false
Altitude: 54.9

SmartHome, [31.07.20 13:22]
🌞 Schutz für Westseite: true
Rollladen-Sollwert: 100%
Status: Sonnenschutz
Wetter: Klarer Himmel / 31.92°C
Azimuth: 173.6 / true
Altitude: 59.8

SmartHome, [31.07.20 18:22]
🌞 Schutz für Nordseite: true
Rollladen-Sollwert: 100%
Status: Sonnenschutz
Wetter: Klarer Himmel / 36.03°C
Azimuth: 268.9 / true
Altitude: 25.6

SmartHome, [31.07.20 20:32]
🌞 Schutz für Westseite: false
Rollladen-Sollwert: 0%
Status: Sonnenschutz
Wetter: Klarer Himmel / 32.79°C
Azimuth: 292.4 / true
Altitude: 4.5

SmartHome, [31.07.20 20:32]
🌞 Schutz für Nordseite: false
Rollladen-Sollwert: 0%
Status: Sonnenschutz
Wetter: Klarer Himmel / 32.79°C
Azimuth: 292.4 / true
Altitude: 4.5

SmartHome, [31.07.20 21:31]
🌞 Beschattung:
Alle Rollläden wurden geschlossen.

Fazit

Mittlerweile bin ich ziemlich zufrieden mit der Steuerung. Nach einer Testphase, läuft das System nun problemlos und autark. Für die Zukunft überlege ich noch, eventuell eine Wetterstation nachzurüsten, da die Daten der WetterAPI manchmal von der Realität abweichen, fürs erste genügt mir aber der Service. Ich erspare mir das tägliche manuelle Rollladen hoch-/runterfahren und an heißen Tagen bleibt die Hitze draußen. Falls es noch Fragen oder Anmerkungen gibt dürft ihr mich gerne kontaktieren.

Quellen

  1. [1]
    “About Node-RED,” Node-RED, Jul. 2020. https://nodered.org/about/ (accessed Jul.2020).
  2. [2]
    “Datei:Kompassrose.svg,” wikipedia.org. https://de.m.wikipedia.org/wiki/Datei:Kompassrose.svg (accessed Jul. 31, 2020).
  3. [3]
    “File:HorSys.svg,” commons.wikimedia.org. https://commons.wikimedia.org/wiki/File:HorSys.svg (accessed Jul. 31, 2020).

Schreibe einen Kommentar