<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Beispiel-Implementierungen &#8211; SmartHomeNG | smarthome knx homematic mqtt hue 1wire home automation</title>
	<atom:link href="https://www.smarthomeng.de/category/beispiel_implementierungen/feed" rel="self" type="application/rss+xml" />
	<link>https://www.smarthomeng.de</link>
	<description>Die Device Integrations-Plattform für Dein Smart Home</description>
	<lastBuildDate>Wed, 27 Apr 2022 17:04:08 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.5.5</generator>

<image>
	<url>https://www.smarthomeng.de/wp-content/uploads/global/logo_small_152x152-150x150.png</url>
	<title>Beispiel-Implementierungen &#8211; SmartHomeNG | smarthome knx homematic mqtt hue 1wire home automation</title>
	<link>https://www.smarthomeng.de</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Nutzung von Szenen in SmartHomeNG</title>
		<link>https://www.smarthomeng.de/nutzung-von-szenen-in-smarthomeng</link>
					<comments>https://www.smarthomeng.de/nutzung-von-szenen-in-smarthomeng#comments</comments>
		
		<dc:creator><![CDATA[Martin Sinn]]></dc:creator>
		<pubDate>Mon, 25 May 2020 13:51:44 +0000</pubDate>
				<category><![CDATA[Beispiel-Implementierungen]]></category>
		<category><![CDATA[Tipps & Tricks]]></category>
		<category><![CDATA[eval]]></category>
		<category><![CDATA[HUE]]></category>
		<category><![CDATA[hue2]]></category>
		<category><![CDATA[Items]]></category>
		<category><![CDATA[Szenen]]></category>
		<guid isPermaLink="false">https://www.smarthomeng.de/?p=2574</guid>

					<description><![CDATA[Die Implementierung von Szenen in SmartHomeNG orientiert sich an der Art wie Szenen in KNX implementiert sind. Da nicht jeder Nutzer von SmartHomeNG mit KNX-Szenen vertraut ist, fasse ich im Folgenden die Funktionsweise von KNX-Szenen kurz zusammen. Wer mit KNX-Szenen bereits vertraut ist, kann diesen Abschnitt überspringen und sich gleich<a class="moretag" href="https://www.smarthomeng.de/nutzung-von-szenen-in-smarthomeng"> Weiterlesen&#8230;</a>]]></description>
										<content:encoded><![CDATA[<p>Die Implementierung von Szenen in SmartHomeNG orientiert sich an der Art wie Szenen in KNX implementiert sind. Da nicht jeder Nutzer von SmartHomeNG mit KNX-Szenen vertraut ist, fasse ich im Folgenden die Funktionsweise von KNX-Szenen kurz zusammen. Wer mit KNX-Szenen bereits vertraut ist, kann diesen Abschnitt überspringen und sich gleich die Beispiel-Implementierung anschauen.</p>
<h3>Funktionsweise von KNX-Szenen</h3>
<p>Um Szenen in KNX nutzen zu können, müssen Aktoren verwendet werden, die Szenen unterstützen. Diese Aktoren verfügen über ein Kommunikationsobjekt, über welches Szenen angesteuert werden können. Szenen werden ausgelöst, indem auf die Gruppenadresse (GA) die mit diesem Kommunikationsobjekt verbunden ist, ein numerischer 8-Bit Wert gesendet wird. Gültige Werte sind hierfür 0 bis 63. Über eine Szene können also nur Aktoren angesteuert werden, die mit der selben Gruppenadresse verbunden sind.</p>
<p>Auf welche Werte (Szenennummern) der jeweilige Aktor reagiert, muss mit der ETS konfiguriert werden. In der ETS wird auch festgelegt, welchen Zustand der Aktor einnehmen soll, wenn über die Gruppenadresse eine Szene angesteuert wird.</p>
<p>Optional kann in der ETS eingestellt werden, dass Aktoren die Zustände die zu einer Szene gehören lernen kann. Das geschieht folgendermaßen: Die Aktoren werden manuell in den Zustand gebracht, den sie für eine Szene einnehmen sollen. Anschließend wird auf die Guppenadresse der Wert Szenennummer+128 (also 128 bis 191) gesendet. Dadurch werden die Aktoren, bei denen das &#8222;Lernen&#8220; aktiviert ist veranlasst, sich den aktuellen Zustand als Zustand für die entsprechende Szene zu merken.</p>
<h3>Ansteuerung von KNX-Szenen aus SmartHomeNG</h3>
<p>Um KNX-Szenen aus SmartHomeNG anzusteuern, muss ein numerisches Item angelegt werden und es muss den Wert auf die Gruppenadresse senden, die zur Szenensteuerung verwendet wird:</p>
<pre><code class="language-yaml">meine_items:
    knx_szenen:
        name: Beispiel für KNX-Szenen
        type: num
        enforce_updates: True
        knx_dpt: 5
        knx_send: 0/0/3
</code></pre>
<p><strong>enforce_updates</strong> muss auf True gesetzt werden, damit wenn mehrfach hintereinander die selbe Szene angesteuert werden soll, der Wert auch auf den KNX Bus geschrieben wird.<br />
Der <strong>knx_dpt</strong> Wert 5 (8 Bit 0-255) ist die ursprüngliche Art Szenen in KNX Anzusteuern. Hier werden die Szenen über Werte von 0 bis 63 angesteuert.</p>
<p>Es gibt inzwischen in KNX eigene DPT Werte um Szene anzusteuern (DPT 17 und 18). Bei diesen DPTs werden die Szenen von 1 bis 64 nummeriert. Es ist zu beachten, dass ganz alte KNX Aktoren diese DPTs eventuell nicht kennen (weshalb ich mir angewöhnt habe immer DPT 5 zu verwenden).</p>
<h3>Szenen in SmartHomeNG</h3>
<p>Szenen in SmartHomeNG funktionieren nach dem gleichen Prinzip wie KNX-Szenen. Damit SmartHomeNG erkennt, dass ein Item die Ansteuerung einer Szene bewirken soll, muss der Item-Type <strong>scene</strong> gewählt werden. Ein Szenen Item für SmartHomeNG Szenen sieht somit folgendermaßen aus:</p>
<pre><code class="language-yaml">meine_items:
    szenen:
        name: Beispiel für SmartHomeNG-Szenen
        type: scene
        enforce_updates: True
</code></pre>
<p>Die Festlegungen, welche Items auf die Szene reagieren sollen und welchen Zustand sie einnehmen sollen (was bei KNX in der ETS festgelegt wird), erfolgt im Verzeichnis <strong>../scenes</strong> in einer YAML Datei, die den Namen des Szenen-Items trägt. Im obigen Beispiel also <strong>meine_items.szenen.yaml</strong>. Detals zum Aufbau dieser Datei sind in der Anwender Dokumentation in der Beschreibung der <a href="http://www.smarthomeng.de/dev/user/konfiguration/konfigurationsdateien/scenes.html">Konfigurationsdateien</a> zu finiden.</p>
<p>Die Ansteuerung der Szenen erfolgt analog zur Ansteuerung von KNX-Szenen durch Zuweisung eines Wertes zwischen 0 und 63 zum Abruf (und 128 bis 191 zum speichern) von Szenen.</p>
<h3>Gemischte Szenen in SmartHomeNG und KNX</h3>
<p>Es ist auch möglich, Szenen zwischen SmartHomeNG und KNX zu mischen, ohne die Items der KNX Aktoren in die SmartHome Szenen Definition im <strong>../scenes</strong> Verzeichnis aufnehmen zu müssen. Es kann dabei auf die KNX Funktionalität zurück gegriffen werden. Dazu muss das Szenen Item einer SmartHomeNG Szene nur um die KNX Attribute erweitert werden:</p>
<pre><code class="language-yaml">meine_items:
    szenen:
        name: Beispiel für gemischte Szenen
        type: num
        enforce_updates: True
        knx_dpt: 5
        knx_send: 0/0/3
</code></pre>
<p>Wenn die Szenen Ansteuerung auch aus dem KNX Bus heraus erfolgen soll, also z.B. über einen Tastsensor der Szenen abruft oder speichert, muss das Beispiel nur noch um den KNX Listen-Befehl auf die Szenen-GA erweitert werden:</p>
<pre><code class="language-yaml">meine_items:
    szenen:
        name: Beispiel für gemischte Szenen
        type: num
        enforce_updates: True
        knx_dpt: 5
        knx_send: 0/0/3
        knx_listen: 0/0/3
</code></pre>
<p>Nun bewirkt ein Szenen Abruf durch einen KNX Taster, dass auch die Items der SmartHomeNG Szene mit angesteuert werden.</p>
<h3>Beispiel Implementierung</h3>
<p>Im folgenden wird die Implementierung der Lichtsteuerung gezeigt, bei der fast alle Leuchten über KNX-Szenen gesteuert werden. Allerdings soll eine Philips HUE Leuchte mit angesteuert werden, welche aus SmartHomeNG heraus mit dem hue Plugin gesteuert wird.</p>
<p>Das Szenen Item wird azu folgendermaßen definiert:</p>
<pre><code class="language-yaml">wohnung:
    buero:
        szenen_knx:
            comment: 0=aus, 1=Ambiente, 2=hell, 3=Putzen, 4=Party
            name: Beleuchtungsszenen Büro
            type: scene
            enforce_updates: 'on'
            knx_dpt: 5
            knx_send: 1/0/16
            knx_listen: 1/0/16
</code></pre>
<p>Die KNX Aktoren (Dimmer) sind entsprechend konfiguriert, dass sie auf der GA 1/0/16 auf die Szenennummern 0 bis 4 (gemäß KNX Definition Szenen 1 bis 5) reagieren und dass sie für die Szenennummern 1 und 2 das lernen zulassen.</p>
<p>Die SmartHomeNG Szene ist im Verzeichnis <strong>../scenes</strong> in der Datei <strong>wohnung.buero.szenen_knx.yaml</strong> folgendermaßen definiert:</p>
<pre><code class="language-yaml">
0:
    name: Aus
    # Leuchte Dreieckschrank ausschalten, falls die Schreibtischleuchte nicht eingeschaltet ist, sonst level 126 setzen
    actions:
        - {item: wohnung.buero.dreieckschrank.level, value: 0 if (sh.wohnung.buero.schreibtischleuchte.status() &lt; 2) else 126}
        - {item: wohnung.buero.dreieckschrank.ct, value: 345, learn: false}
        - {item: wohnung.buero.dreieckschrank.onoff, value: False if (sh.wohnung.buero.schreibtischleuchte.status() &lt; 2) else True}

1:
    name: Ambiente
    actions:
        - {item: wohnung.buero.dreieckschrank.level, value: sh...dreieckschrank.ambiente_level()+3, learn: false}
        - {item: wohnung.buero.dreieckschrank.ct, value: 345, learn: true}
        - {item: wohnung.buero.dreieckschrank.onoff, value: True, learn: true}

2:
    name: Hell
    actions:
        - {item: wohnung.buero.dreieckschrank.level, value: 126, learn: true}
        - {item: wohnung.buero.dreieckschrank.ct, value: 345, learn: true}

3:
    name: Putzen
    actions:
        - {item: wohnung.buero.dreieckschrank.onoff, value: True, learn: false}
        - {item: wohnung.buero.dreieckschrank.level, value: 255, learn: false}
        - {item: wohnung.buero.dreieckschrank.ct, value: 345, learn: false}

4:
    name: Party
    actions:
        - {item: wohnung.buero.dreieckschrank.level, value: 200, learn: false}
        - {item: wohnung.buero.dreieckschrank.hue, value: 59635, learn: false}
        - {item: wohnung.buero.dreieckschrank.sat, value: 230, learn: false}
        - {item: wohnung.buero.dreieckschrank.onoff, value: True, learn: false}
</code></pre>
<p>Zur Ansteuerung der Hue Leuchte über die Szene werden 3 Items gesteuert, welche die Leuchte ein- und ausschalten, den Dimmwert und die Farbe setzen.</p>
<p>Für jede der Szenen (0 bis 4) wird über <strong>value</strong> für diese Werte ein Wert gesetzt (der bei Abruf der Szene eingestellt werden soll) und über <strong>learn</strong> wird festgelegt, ob andere Werte gelernt werden können/dürfen. Für <strong>value</strong> können dabei nicht nur konstante Werte festgelegt werden, sondern auch <strong>eval Ausdrücke</strong>. Wenn ein eval Ausdruck angegeben wird, ist <strong>learn</strong> zwangsweise false. In diesem Fall können also keine Szenen Zustände gelernt werden.</p>
<p>Ein Beispiel für die Nutzung von eval Ausdrücken in Szenen ist bei Szene Nummer <strong>0</strong> zu finden, welche die Beleuchtung ausschalten soll. Falls im Büro die Schreibtisch Leuchte (die nicht Teil der Szenen ist) eingeschaltet ist, wird die HUE Leuchte jedoch nicht ausgeschaltet, sondern als Hintergrund Beleuchtung in einem gedimmten warmen Weiss eingeschaltet.</p>
<p>Bei Szene Nummer <strong>1</strong> wird eine andere eval Nutzung mit Anwendung relativer Item Adressierung gezeigt. Hier wird der Wert auf den Wert eines anderen Items plus 3 gesetzt. Hier kann nur der Weisston und Ein/Aus über die Szene gelernt werden.</p>
<p>Bei Szene Nummer <strong>4</strong> wird eine vorgewählte Farbe und Helligkeit der HUE Leuchte eingestellt, die bewusst nicht durch &#8222;Lernen&#8220; geändert werden kann.</p>
<h3>Ansteuerung der Szenen über smartVISU</h3>
<p>Die Ansteuerung der Szenen kann am besten über Buttons in der smartVISU erfolgen. Für das obige Beispiel wird die folgende Definition für die Autogenerierung verwendet:</p>
<p>&nbsp;</p>
<pre><code class="language-yaml">wohnung:
    buero:
        sv_page: room
        name: Büro
        sv_img: scene_office.svg

        visu:
            szenen_beleuchtung:
                name: Szenen Beleuchtung

                sv_widget: 'Szenen abrufen:
                    &lt;span data-role="controlgroup" data-type="horizontal"&gt;
                    {{ basic.button(''szenen_buero0'', ''wohnung.buero.szenen_knx'', ''Aus'', icon0~"control_standby.svg", ''0'', ''midi'') }}
                    {{ basic.button(''szenen_buero1'', ''wohnung.buero.szenen_knx'', ''Ambiente'', icon0~"light_light_dim_00.svg", ''1'', ''midi'') }}
                    {{ basic.button(''szenen_buero2'', ''wohnung.buero.szenen_knx'', ''Hell'', icon0~"light_ceiling_light.svg", ''2'', ''midi'') }}
                    {{ basic.button(''szenen_buero3'', ''wohnung.buero.szenen_knx'', ''Putzen'', icon0~"scene_cleaning.svg", ''3'', ''midi'') }}
                    {{ basic.button(''szenen_buero6'', ''wohnung.buero.szenen_knx'', ''Party'', icon0~"light_party.svg", ''4'', ''midi'') }}
                    &lt;/span&gt;&lt;br&gt;
                    &lt;div style="line-height: 30%;"&gt;
                         &lt;br&gt;br&gt;
                    &lt;/div&gt;
                    &lt;div&gt;Szenen speichern:&lt;br&gt;
                    &lt;span data-role="controlgroup" data-type="horizontal"&gt;
                    {{ basic.button(''szenen_bueroS0'', ''wohnung.buero.szenen_knx'', ''Aus'', icon0~''control_standby.svg'', ''128'', ''midi'', ''#4176a9'') }} 
                    {{ basic.button(''szenen_bueroS1'', ''wohnung.buero.szenen_knx'', ''Ambiente'', icon0~"light_light_dim_00.svg", ''129'', ''midi'', ''#4176a9'') }} 
                    {{ basic.button(''szenen_bueroS2'', ''wohnung.buero.szenen_knx'', ''Hell'', icon0~''light_ceiling_light.svg'', ''130'', ''midi'', ''#4176a9'') }} 
                    {{ basic.button(''szenen_bueroS3'', ''wohnung.buero.szenen_knx'', ''Putzen'', icon0~''scene_cleaning.svg'', ''131'', ''midi'', ''#4176a9'') }} 
                    {{ basic.button(''szenen_bueroS6'', ''wohnung.buero.szenen_knx'', ''Party'', icon0~''light_party.svg'', ''132'', ''midi'', ''#4176a9'') }} 
                    &lt;/span&gt;&lt;/div&gt;
</code></pre>
<p>Das erzeugt in der Visu:</p>
<ul>
<li>eine Seite &#8222;Büro&#8220; in der normalen Raum-Navigation.</li>
<li>auf dieser Seite einen Block mit dem Namen &#8218;Szenen Beleuchtung&#8216;</li>
<li>in diesem Block erzeuge ich zwei Button Reihen zur Szenen Steuerung: Der obere Block zum Abruf der Szenen, der untere Block zum Speichern von Szenen</li>
</ul>
<p>Das sieht dann so aus:</p>
<p><img fetchpriority="high" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2020/05/smartVISU-Szenen-300x280.jpg" alt="" width="300" height="280" class="alignnone size-medium wp-image-2591" srcset="https://www.smarthomeng.de/wp-content/uploads/2020/05/smartVISU-Szenen-300x280.jpg 300w, https://www.smarthomeng.de/wp-content/uploads/2020/05/smartVISU-Szenen.jpg 398w" sizes="(max-width: 300px) 100vw, 300px" /></p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.smarthomeng.de/nutzung-von-szenen-in-smarthomeng/feed</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Extremwerte des Tages speichern</title>
		<link>https://www.smarthomeng.de/__trashed</link>
					<comments>https://www.smarthomeng.de/__trashed#comments</comments>
		
		<dc:creator><![CDATA[kex]]></dc:creator>
		<pubDate>Sun, 24 May 2020 19:59:01 +0000</pubDate>
				<category><![CDATA[Beispiel-Implementierungen]]></category>
		<category><![CDATA[Tipps & Tricks]]></category>
		<category><![CDATA[eval]]></category>
		<category><![CDATA[Items]]></category>
		<guid isPermaLink="false">https://www.smarthomeng.de/?p=2546</guid>

					<description><![CDATA[Meine Aufgabenstellung war die folgende: Speichern des höchsten gemessenen Temperaturwertes eines Tages und diesen einmal pro Tag zurücksetzten. Das ursprüngliche Item dessen Maximalwert ich speichern hat folgenden Aufbau. Wetterstation: Temperatur: type: num visu_acl: ro Um jetzt zusätzlich zum aktuellen Messwert den maximalen Messwert zu speichern habe ich das Item etwas<a class="moretag" href="https://www.smarthomeng.de/__trashed"> Weiterlesen&#8230;</a>]]></description>
										<content:encoded><![CDATA[<p>Meine Aufgabenstellung war die folgende: Speichern des höchsten gemessenen Temperaturwertes eines Tages und diesen einmal pro Tag zurücksetzten.</p>
<p>Das ursprüngliche Item dessen Maximalwert ich speichern hat folgenden Aufbau.</p>
<pre class="line-numbers"><code class="language-yaml">
Wetterstation:
    Temperatur:
        type: num
        visu_acl: ro
</code></pre>
<p>Um jetzt zusätzlich zum aktuellen Messwert den maximalen Messwert zu speichern habe ich das Item etwas angepasst:</p>
<pre class="line-numbers"><code class="language-yaml">
Wetterstation:
    Temperatur:
        Aktuell:
            type: num
            visu_acl: ro
        Maximum_Heute:
            type: num
            visu_acl: ro
</code></pre>
<p>Für Wetterstation.Tempertatur.Maximum_Heute müssen jetzt noch folgende Dinge hinzufefügt werden:</p>
<ul>
<li><a href="#d1">Überprüfen auf Änderungen</a><br />
Immer wenn sich der Wert von <strong>Wetterstation.Temperatur.Aktuell</strong> ändert, soll überprüft werden, ob <strong>Wetterstation.Temperatur.Maximum_Heute</strong> aktualisiert werden muss.</li>
<li><a href="#d2">Aktualisieren des Wertes</a><br />
Wenn bei dieser Überprüfung der aktuelle Wert höher ist als der bisher in <strong>Wetterstation.Temperatur.Maxiumum_Heute</strong> gespeicherte Wert, soll der Wert aktualisiert werden.</li>
<li><a href="#d3">Zurücksetzen des Wertes</a><br />
Einmal am Tag muss <strong>Wetterstation.Temperatur.Maximum_Heute</strong> zurückgesetzt werden.</li>
</ul>
<h3 id="d1">Überprüfen auf Änderungen</h3>
<p>Das überprüfen, ob sich die aktuelle Temperatur ändert, geht ganz einfach indem man <strong>Wetterstation.Temperatur.Maximum_Heute</strong> einen eval_trigger auf das Item <strong>Wetterstation.Temperatur.Aktuell</strong> hinzufügt. Seit SmartHomeNG kann man das Item auch relativ angeben: ..Aktuell.<br />
es entsteht also folgender <strong>eval_trigger</strong>:</p>
<pre><code class="language-yaml">eval_trigger: ..Aktuell</code></pre>
<h3 id="d2">Aktualisieren des Wertes</h3>
<p>Wenn bei der Überprüfung der aktuelle Wert höher ist als der bisher in Wetterstation.Temperatur.Maxiumum_Heute gespeicherte Wert, soll der Wert aktualisiert werden.<br />
Das geht über ein <strong>eval</strong>:</p>
<pre><code class="language-yaml">eval: sh...Aktuell() if float(sh..self()) &gt; sh...Aktuell() else sh..self.property.value</code></pre>
<h3 id="d3">Zurücksetzen des Wertes</h3>
<p>Der Wert soll einmal pro Tag und bei einem Neustart zurückgesetzt werden. Das wird über ein <strong>crontab </strong>und ein <em>Magic Value</em> (99.9) erledigt. Es wird davon ausgegangen, dass ein Temperaturwert von 99.9 nie vorkommt und deshalb zum Zurücksetzen verwendet werden kann.</p>
<p>Verzögerung bei der Initialisierung: Meine Wetterstation liefert nur alle 5 Minuten (alle 300 Sekunden) einen neuen Temperaturwert. Das heisst nach einem Neustart von SmartHomeNG steht also für maximal 5 Minuten kein aktueller Messwert in <strong>Wetterstation.Temperatur.Aktuell</strong> sondern 0.0. Deshalb wird nach 5 Minuten und 10 Sekunden <strong>Wetterstation.Temperatur.Maximum_Heute</strong> auf die aktuelle Temperatur zurückgesetzt.</p>
<pre><code class="language-yaml">crontab:
 -init+310 = 99.9
 -0 0 * *  = 99.9
</code></pre>
<p>Auch der <strong>eval</strong> Ausdruck muss für das Zurücksetzen erweitert werden:</p>
<pre><code class="language-yaml">eval: sh...Aktuell() if <strong>value == 99.9 or</strong> float(sh..self()) &gt; sh...Aktuell() else sh..self.property.value
</code></pre>
<p>Natürlich kann dieses Prinzip Minimal-Werte angewendet werden. Dafür muss nur der Vergleich im <strong>eval</strong> Ausdruck angepasst werden.</p>
<p>Das endgültige Item sieht dann so aus:</p>
<pre class="line-numbers"><code class="language-yaml">
Wetterstation:
    Temperatur:
        Aktuell:
            type: num
            visu_acl: ro
        Maximum_Heute:
            type: num
            visu_acl: ro
            eval: sh...Aktuell() if value == 99.9 or float(sh..self()) &lt; sh...Aktuell() else sh..self.property.value
            eval_trigger: ..Aktuell
            # Reset am Ende des Tages durch "MagicValue" 99.9. Es wird angenommen, 
            # dass dieser Temperaturwert in der Praxis niemals auftritt. 
            crontab: 
             - 0 3 * * = 99.9
             # +310 weil die Wetterstation mindestens alle 300 Sekunden einen aktualisierten Temperatur Wert sendet
             # dadurch wird sichergestellt, dass nach einem Neustart von SmartHomeNG in ..Aktuell auf jeden Fall ein gültiger Wert steht. 
             - init+310 = 99.9
</code></pre>
<p>Wenn etwas anderes als Temperaturwerte gespeichert werden sollen, kann es auch sein, dass als <em>Magic Value</em> eine andere Zahl verwendet werden muss.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.smarthomeng.de/__trashed/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>wie ich meinen ersten Shelly über das Plugin mit MQTT eingebunden habe</title>
		<link>https://www.smarthomeng.de/wie-ich-meinen-ersten-shelly-ueber-das-plugin-mit-mqtt-eingebunden-habe</link>
					<comments>https://www.smarthomeng.de/wie-ich-meinen-ersten-shelly-ueber-das-plugin-mit-mqtt-eingebunden-habe#comments</comments>
		
		<dc:creator><![CDATA[Wil Heynen]]></dc:creator>
		<pubDate>Fri, 08 May 2020 19:28:05 +0000</pubDate>
				<category><![CDATA[Beispiel-Implementierungen]]></category>
		<category><![CDATA[Plugins]]></category>
		<category><![CDATA[shelly]]></category>
		<guid isPermaLink="false">https://www.smarthomeng.de/?p=2521</guid>

					<description><![CDATA[Da ich mit der Implementierung mehrere Stunden gekämpft habe, möchte ich meine Erfahrungen niederschreiben und eine kleine Anleitung erstellen, wie man am einfachsten vorgeht und welche Fehler man dabei machen kann. Zur Vorgeschichte: Meinen ersten Shelly nutze ich dazu, mein Garagentor über SmartVISU zu öffnen; es gibt zwar eine eigene<a class="moretag" href="https://www.smarthomeng.de/wie-ich-meinen-ersten-shelly-ueber-das-plugin-mit-mqtt-eingebunden-habe"> Weiterlesen&#8230;</a>]]></description>
										<content:encoded><![CDATA[<p>Da ich mit der Implementierung mehrere Stunden gekämpft habe, möchte ich meine Erfahrungen niederschreiben und eine kleine Anleitung erstellen, wie man am einfachsten vorgeht und welche Fehler man dabei machen kann.</p>
<p><strong>Zur Vorgeschichte:</strong><br />
Meinen ersten Shelly nutze ich dazu, mein Garagentor über SmartVISU zu öffnen; es gibt zwar eine eigene App, wie zu allen Smart Home Devices, aber wozu sollte ich für jedes Device eine andere App benutzen. <span id="more-2521"></span></p>
<p>Zunächst musste ich natürlich den Shelly elektrisch anschließen; hierzu gibt es auch schon reichlich Diskussionen in diversen Foren. Ich habe mich für folgende einfache Lösung entschieden und meinen Shelly, wie den Tastschalter oder den Schlüsselschalter angeschlossen.</p>
<p>Da Shelly keine spezielle Taster Funktion kennt, habe ich den Schalter über die App wie ein Treppenlicht konfiguriert; Nach der Betätigung des Schalters wird nach einer Sekunde wieder ausgeschaltet. Das funktioniert dann zunächst schon mal über die App ganz prima.</p>
<p><img decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2020/05/Config1-300x124.png" alt="" class="alignnone size-medium wp-image-2523" width="300" height="124" srcset="https://www.smarthomeng.de/wp-content/uploads/2020/05/Config1-300x124.png 300w, https://www.smarthomeng.de/wp-content/uploads/2020/05/Config1-768x316.png 768w, https://www.smarthomeng.de/wp-content/uploads/2020/05/Config1.png 881w" sizes="(max-width: 300px) 100vw, 300px" /> <img decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2020/05/Config2-300x115.png" alt="" class="alignnone size-medium wp-image-2524" width="300" height="115" srcset="https://www.smarthomeng.de/wp-content/uploads/2020/05/Config2-300x115.png 300w, https://www.smarthomeng.de/wp-content/uploads/2020/05/Config2-768x295.png 768w, https://www.smarthomeng.de/wp-content/uploads/2020/05/Config2.png 874w" sizes="(max-width: 300px) 100vw, 300px" /></p>
<p>In SmarthomeNG habe ich dann mein Item wie folgt konfiguriert:</p>
<p style="padding-left: 30px">Tor:<br />
name: Tor<br />
enforce_updates: true<br />
type: bool<br />
visu: &#8218;yes&#8216;<br />
sv_widget: &#8222;{{ basic.stateswitch(&#8218;item&#8216;, &#8218;item&#8216;, &#8218;midi&#8216;, 1, &#8220;, &#8220;, &#8220;, &#8218;blink&#8216;) }}&#8220;</p>
<p>Ohne MQTT habe ich dann einfach eine Logik implementiert, die bei jedem Klick getriggert wird:</p>
<p>/etc/logic.yaml</p>
<p style="padding-left: 30px">Garagentor:<br />
filename: Garage.py<br />
watch_item: Aussen.Garage.Tor</p>
<p>/logics/Garage.py</p>
<p style="padding-left: 30px"># Shelly1<br />
# Garagentor</p>
<p style="padding-left: 30px">import urllib.request</p>
<p style="padding-left: 30px">myurl = &#8222;http://192.168.178.28/relay/0?turn=on&#8220;</p>
<p style="padding-left: 30px">req = urllib.request.urlopen(myurl)<br />
logger.info(&#8222;Garagentor&#8220;)</p>
<p>Damit funktioniert das ganze &#8222;zu Fuß&#8220; und ohne MQTT und Shelly Plugin in SmarthomeNG 1.5</p>
<p><strong>Vorbereitungen in SmarthomeNG 1.7</strong></p>
<p>Zunächst mal muss MQTT aufgesetzt werden; dies habe ich natürlich, dank &#8222;Onkelandy&#8220; über das <strong>setup_all</strong> script gemacht.</p>
<p>Wer nicht das Image benutz muss dann anhand der Komplettanleitung den mosquitto Broker installieren und konfigurieren.</p>
<p>Danach muss MQTT als Modul aktiviert werden; mit 1.7 ist MQTT in den Core gewandert und wird wie folgt aktiviert:</p>
<p>/etc/module.yaml</p>
<p style="padding-left: 30px">mqtt:<br />
module_name: mqtt<br />
broker_monitoring: true</p>
<p>Als einzigen Parameter habe ich das Monitoring eingeschaltet, damit man über das Web GUI die Aktivitäten verfolgen kann.</p>
<p>Dann wir das Shelly Plugin aktiviert:</p>
<p>/etc/plugin.yaml</p>
<p style="padding-left: 30px">Shelly:<br />
plugin_name: shelly</p>
<p>Hier gibt es keinerlei Parameter.</p>
<p>Das Shelly Plugin wird über Item-Parameter getriggert, deshalb habe ich Folgendes in meinem Garagentor Item ergänzt:</p>
<p style="padding-left: 30px">Tor:<br />
name: Tor<br />
<strong><span style="color: #3366ff">shelly_id: 8CAAB505559E</span></strong><br />
<strong><span style="color: #3366ff">shelly_type: shelly1</span></strong><br />
<strong><span style="color: #3366ff">shelly_attr: relay</span></strong><br />
<strong><span style="color: #3366ff">shelly_relay: 0</span></strong><br />
enforce_updates: true<br />
type: bool<br />
sv_widget: &#8222;{{ basic.stateswitch(&#8218;item&#8216;, &#8218;item&#8216;, &#8218;midi&#8216;, 1, &#8220;, &#8220;, &#8220;, &#8218;blink&#8216;) }}&#8220;</p>
<p>(Hier hatte ich den Fehler gemacht und shelly_<strong><span style="color: #ff0000">ID</span></strong> definiert, was mich zwei Stunden gekostet hat)</p>
<p>Danach erscheint dann (fast) alles im Web-GUI.</p>
<p>Zunächst die Definitionen im Item:</p>
<p><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2020/05/shellyPlug1-1024x311.png" alt="" class="alignnone wp-image-2530 size-large" width="750" height="228" srcset="https://www.smarthomeng.de/wp-content/uploads/2020/05/shellyPlug1-1024x311.png 1024w, https://www.smarthomeng.de/wp-content/uploads/2020/05/shellyPlug1-300x91.png 300w, https://www.smarthomeng.de/wp-content/uploads/2020/05/shellyPlug1-768x233.png 768w, https://www.smarthomeng.de/wp-content/uploads/2020/05/shellyPlug1.png 1046w" sizes="(max-width: 750px) 100vw, 750px" /></p>
<p>Ob der Broker läuft findet Ihr dann unter dem Tab: Broker Information:</p>
<p><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2020/05/shellyPlug2-1024x383.png" alt="" class="alignnone size-large wp-image-2531" width="750" height="281" srcset="https://www.smarthomeng.de/wp-content/uploads/2020/05/shellyPlug2-1024x383.png 1024w, https://www.smarthomeng.de/wp-content/uploads/2020/05/shellyPlug2-300x112.png 300w, https://www.smarthomeng.de/wp-content/uploads/2020/05/shellyPlug2-768x287.png 768w, https://www.smarthomeng.de/wp-content/uploads/2020/05/shellyPlug2.png 1032w" sizes="(max-width: 750px) 100vw, 750px" /></p>
<p>wenn nicht, schaut bitte in Eure Konfiguration, ob MQTT enabled ist:</p>
<p><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2020/05/mqtt1-1024x411.png" alt="" class="alignnone size-large wp-image-2533" width="750" height="301" srcset="https://www.smarthomeng.de/wp-content/uploads/2020/05/mqtt1-1024x411.png 1024w, https://www.smarthomeng.de/wp-content/uploads/2020/05/mqtt1-300x120.png 300w, https://www.smarthomeng.de/wp-content/uploads/2020/05/mqtt1-768x308.png 768w, https://www.smarthomeng.de/wp-content/uploads/2020/05/mqtt1.png 1054w" sizes="(max-width: 750px) 100vw, 750px" /></p>
<p>In der GUI des Shelly Plugin findet Ihr aber zunächst nichts, oder nur den <strong>Namen</strong> des Devices.</p>
<p>Jetzt geht&#8217;s wieder zur Shelly App um die Broker Verbindung zu konfigurieren. In der SmarthomeNG Konfiguration seht Ihr im letzten Screenshot, dass der MQTT Broker auf dem SmarthomeNG Rechner läuft und auf den Port 1883 hört. Das müsst Ihr jetzt noch dem Shelly Device beibringen. (In der Shelly App):</p>
<p><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2020/05/Config3.png" alt="" class="alignnone size-full wp-image-2529" width="880" height="824" srcset="https://www.smarthomeng.de/wp-content/uploads/2020/05/Config3.png 880w, https://www.smarthomeng.de/wp-content/uploads/2020/05/Config3-300x281.png 300w, https://www.smarthomeng.de/wp-content/uploads/2020/05/Config3-768x719.png 768w" sizes="(max-width: 880px) 100vw, 880px" /></p>
<p>Zunächst müsst Ihr den haken bei &#8222;Enable action execution via MQTT&#8220; und dann noch unter Server: wo und wie er zu erreichen ist. (IP-Adresse des Raspi und Port 1883).</p>
<p>Selbstverständlich kann ich weiterhin mein Garagentor über die HTTP Logik im 1.5er System öffnen und auch über die Shelly App (hier aber nicht mehr über die Cloud, wie Ihr es in der WARNING findet)</p>
<p>Danach ist dann Eure Konfiguration komplett und das Shelly Device auch im Shelly Plugin zu finden:</p>
<p><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2020/05/shellyPlug3-1024x326.png" alt="" class="alignnone size-large wp-image-2532" width="750" height="239" srcset="https://www.smarthomeng.de/wp-content/uploads/2020/05/shellyPlug3-1024x326.png 1024w, https://www.smarthomeng.de/wp-content/uploads/2020/05/shellyPlug3-300x96.png 300w, https://www.smarthomeng.de/wp-content/uploads/2020/05/shellyPlug3-768x245.png 768w, https://www.smarthomeng.de/wp-content/uploads/2020/05/shellyPlug3.png 1048w" sizes="(max-width: 750px) 100vw, 750px" /></p>
<p>Diese ganzen Zusammenhänge müssen erst mal verstanden werden, ehe man eine solche Konfiguration aufsetzen kann. Deshalb habe ich das hier mal zusammengestellt, weil das so nicht aus der Dokumentation hervor geht.</p>
<p>Viel Spaß mit Euren Shelly Devices. Wie Ihr andere Devices mit mehr Funktionen konfiguriert und wie ich sie über SmartVISU administriere, beschreibe ich (vielleicht) in weiteren Blogs.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.smarthomeng.de/wie-ich-meinen-ersten-shelly-ueber-das-plugin-mit-mqtt-eingebunden-habe/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Itemvorlagen nutzen (structs)</title>
		<link>https://www.smarthomeng.de/itemvorlagen-nutzen-structs</link>
					<comments>https://www.smarthomeng.de/itemvorlagen-nutzen-structs#comments</comments>
		
		<dc:creator><![CDATA[onkelandy]]></dc:creator>
		<pubDate>Thu, 30 May 2019 10:59:55 +0000</pubDate>
				<category><![CDATA[Beispiel-Implementierungen]]></category>
		<category><![CDATA[Tipps & Tricks]]></category>
		<category><![CDATA[Items]]></category>
		<category><![CDATA[Konfiguration]]></category>
		<category><![CDATA[struct]]></category>
		<category><![CDATA[Vorlagen]]></category>
		<guid isPermaLink="false">https://www.smarthomeng.de/?p=2381</guid>

					<description><![CDATA[Hinweis: Die hier beschriebene Funktion ist erst ab SmartHomeNG 1.6 verfügbar. Structs aus Plugins Die &#8222;struct&#8220; Vorlagen ermöglichen es zum einen, vorgegebene Item-Strukturen aus Plugins zu integrieren, zum anderen aber auch, eigene Vorlagen für immer wiederkehrende Item-Bäume bereitzustellen. Bei gleichen Gerätetypen ist die Struktur oft sehr ähnlich, was zu sehr<a class="moretag" href="https://www.smarthomeng.de/itemvorlagen-nutzen-structs"> Weiterlesen&#8230;</a>]]></description>
										<content:encoded><![CDATA[<p><strong>Hinweis</strong>: Die hier beschriebene Funktion ist erst ab <strong>SmartHomeNG 1.6</strong> verfügbar.</p>
<h5>Structs aus Plugins</h5>
<p>Die &#8222;struct&#8220; Vorlagen ermöglichen es zum einen, vorgegebene Item-Strukturen aus Plugins zu integrieren, zum anderen aber auch, eigene Vorlagen für immer wiederkehrende Item-Bäume bereitzustellen.</p>
<p>Bei gleichen Gerätetypen ist die Struktur oft sehr ähnlich, was zu sehr vielen gleich aufgebauten Item-Definitionen führt. Leuchten, Jalousien, Bewegungsmelder können dabei ähnlich leicht vereinfacht werden wie Sollwerte, Vorgaben für Zustände des stateengine Plugins und vieles mehr. Zuerst ein Beispiel, wie mit nur einer Zeile ein Zeitschalt-Unteritem aus dem UZSU Plugin eingefügt werden kann. Da diese Vorlage bereits mit dem Plugin mitgeliefert wird, ist nichts weiter zu tun, also mittels struct Attribug darauf zuzugreifen. Welche Vorlagen von welchen Plugins bereitgestellt werden, ist zum einen in den jeweiligen plugin.yaml Dateien, zum anderen im Admin Tool einzusehen.</p>
<pre><code class="language-yaml">
    # item.yaml
    item:
        struct: uzsu.child
</code></pre>
<p>Durch diese Angabe entsteht beim Start von SmartHomeNG folgende Struktur, die zuvor noch bei jedem Item, das die Zeitschaltuhr nutzt, manuell angegeben werden musste:</p>
<pre><code class="language-yaml">
  # item.yaml
  item:
        uzsu:
            type: dict
            uzsu_item: ..
            cache: True
            visu_acl: rw
            
            active:
                type: bool
                eval: sh...activate(value)
                visu_acl: rw
</code></pre>
<h5>Eigene Vorlagen</h5>
<p>Folgendes Beispiel zeigt, wie Vorlagen selbst in der Datei etc/struct.yaml hinterlegt werden können. Generell bedarf es keinerlei besonderer Syntax. Strukturen werden dort genauso angelegt wie normale Item-Bäume. Die jeweils oberste Hierarchieebene definiert den Namen der Vorlage. Und genau auf dieses Item wird dann in der eigentlichen item.yaml Datei durch das Attribut <em>struct</em> verwiesen. Zuerst der Inhalt der struct.yaml für ein dimmbares Licht mit einigen Zusatzfunktionen:</p>
<pre><code class="language-yaml">
    # struct.yaml
    dimmervorlage:
        name: Vorlage für dimmbare Leuchten
	knx_dpt: 1
	visu_acl: rw
	type: bool
        knx_send: 0/0/0
        knx_cache: 0/0/0

	bwm:
		knx_dpt: 1
		visu_acl: rw
		type: bool

	zwangvalue:
		knx_dpt: 5001
		visu_acl: rw
		type: num
		cache: True

		zwangsstellung:
			knx_dpt: 2
			visu_acl: rw
			type: list
			enforce_updates: yes

	dimmen:
		knx_dpt: 5001
		visu_acl: rw
		type: num
		database: yes
		influxdb: yes
		sim: track

		sollwert:
			knx_dpt: 5001
			visu_acl: rw
			type: num
			enforce_updates: yes
			cache: True
</code></pre>
<p>Und hier die Einbindung in die yaml Datei im items Verzeichnis für ein Licht mit separaten warm- und kaltweißen Leuchtmitteln:</p>
<pre><code class="language-yaml">
# item.yaml
licht1:
    warm:
        <strong>struct: dimmervorlage</strong>
	knx_send: 3/0/21
	knx_cache: 3/0/27

	bwm:
		knx_send: 3/3/67
		knx_cache: 3/3/67

	zwangvalue:
		zwangsstellung:
			knx_send: 3/0/50
			knx_cache: 3/0/50

	dimmen:
		knx_send: 3/0/23
		knx_cache: 3/0/28

		sollwert:
			knx_send: 3/3/53

    kalt:
        <strong>struct: dimmervorlage</strong>
	knx_send: 3/0/24
	knx_cache: 3/0/29

	bwm:
		knx_send: 3/3/68
		knx_cache: 3/3/68

	zwangvalue:
		zwangsstellung:
			knx_send: 3/0/51
			knx_cache: 3/0/51

	dimmen:
		knx_send: 3/0/26
		knx_cache: 3/0/30

		sollwert:
			knx_send: 3/3/54
</code></pre>
<p>Was passiert hier? Es wird jeweils zuerst die Vorlage geladen, in der die Grundinformationen zu den Items verankert ist. Also Infos zu Typ, Cache, KNX DPT, Visu, Datenbank, etc. Diese Vorlage wird nun im tatsächlichen Item nur noch durch die KNX Adressen ergänzt. Etwaige gleich benannte Attribute werden dabei überschrieben (im Beispiel knx_send: 0/0/0).</p>
<p>In der Version 1.6.0 ist zu beachten, dass auch tatsächlich alle bereits definierten Attribute überschrieben werden, also beispielsweise auch Listeneinträge für eval_trigger. Möchte man also verschiedene Vorlagen mit verschiedenen eval_trigger Einträgen miteinander kombinieren, muss das Attribut nach den struct Einträgen manuell mit allen gewünschten Listeneinträgen überschrieben werden.</p>
<p>Zum Abschluss noch ein weiteres Beispiel, das neben einer klassischen Schaltfunktion für das entsprechende Item auch die Einschaltdauer über die Datenbankeinträge evaluiert. Dank relativer Item-Angaben können somit komplexere Konfigurationsblöcke problemlos wiederverwendet werden. Zum einen erleichtert dieser Ansatz ein Aktualisieren und Erweitern von Itemconfigs ungemein, zum anderen bleibt in den yaml Files die Übersicht bewahrt (auch wenn Letzteres Dank Admin Tool zukünftig weniger Relevanz haben wird). Folgend zwei separate Vorlagen:</p>
<pre><code class="language-yaml">
#struct.yaml
schaltervorlage:
    name: einfache Vorlage für Schalter
    sa:
        knx_dpt: 1
        visu_acl: rw
        type: bool
        database: yes
        cache: yes

    sperren:
        knx_dpt: 1
        visu_acl: rw
        type: bool

laufzeitvorlage:
    name: Vorlage für Laufzeitmessung
    laufzeit_1w:
        type: num
        visu_acl: ro
        crontab: init
        eval: (sh...sa.db('on', '1w') or 0) * 10080
        eval_trigger:
          - ..sa

    laufzeit_24h:
        type: num
        visu_acl: ro
        crontab: init
        eval: (sh...sa.db('on', '24h') or 0) * 1440
        eval_trigger:
          - ..sa
</code></pre>
<p>Die tatsächlichen Items benötigen schließlich wieder nur eine Angabe der KNX Adressen, alle anderen Funktionalitäten und Definition werden über die Vorlage eingebunden:</p>
<pre><code class="language-yaml">
#item.yaml
schalter1:
    <strong>struct: schaltervorlage</strong>
    sa: 
        knx_send: 1/1/1
    sperren:
        knx_send: 1/1/2

schalter2:
    <strong>struct: 
      - schaltervorlage
      - laufzeitvorlage</strong>

    sa: 
        knx_send: 1/1/3

    sperren:
        knx_send: 1/1/4

    laufzeit_1w:
        eval_trigger:
          - ..sa
          - ..sperren

schalter3:
    <strong>struct: schaltervorlage</strong>

    laufzeit:
          <strong>struct: laufzeitvorlage</strong>
</code></pre>
<p>Schalter1 greift ledigilich auf eine Vorlage (schaltervorlage) zurück, während Schalter2 mehrere Vorlagen als Liste miteinander kombiniert. Für Schalter2 existieren also auch die Einträge laufzeit_1w und laufzeit_24h, die eben die Einschaltdauer während der letzten Woche bzw. 24 Stunden von der Datenbank auslesen. Die Triggerung der laufzeit_1w soll in diesem Fall auch vom Sperritem erfolgen, während dies bei Schalter3 lediglich durch das in der Vorlage angegebene Schaltitem geschieht. Da Schalter3 keine Angaben zu den KNX Gruppenadressen macht, handelt es sich hier auch nicht um einen KNX Schalter, sondern z.B. ein Item, das nur über eine Logik oder die Visu geschalten werden kann/soll. Das schalter3 Beispiel zeigt auch, dass die Structs auf jeder beliebigen Hierarchieebene eingebunden werden können.</p>
<h5>Limitierungen</h5>
<p>Aktuell (inkl. Version 1.9) ist es nicht möglich, structs beliebig zu verschachteln. Das Einbeziehen von anderen structs in einem selbst kreierten struct ist nur auf Rootebene möglich, also beispielsweise so:</p>
<pre><code class="language-yaml">
    # struct.yaml
    beispielstruct1:
        test1:
            type: bool
            initial_value: True

    beispielstruct2:
        test2:
            type: bool
            initial_value: False

    combinedstruct:
        struct:
          - beispielstruct1
          - beispielstruct2
</code></pre>
<h5>Debugging</h5>
<p>Im Admin Interface sind alle structs, sowohl die von den aktiven Plugins als auch die selbst erstellten einsehbar. Hier lässt sich somit auch prüfen, wie die Structs miteinander kombiniert wurden. Ob und wie der Merge mit Attributen aus den Items geklappt hat, lässt sich am besten im Item Baum nachvollziehen.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.smarthomeng.de/itemvorlagen-nutzen-structs/feed</wfw:commentRss>
			<slash:comments>8</slash:comments>
		
		
			</item>
		<item>
		<title>RaspBee Gateway Bridge mit dem SmartHomeNG Plugin ‚HUE‘</title>
		<link>https://www.smarthomeng.de/raspbee-gateway-bridge-mit-dem-smarthomeng-plugin-hue</link>
					<comments>https://www.smarthomeng.de/raspbee-gateway-bridge-mit-dem-smarthomeng-plugin-hue#respond</comments>
		
		<dc:creator><![CDATA[Marcus Schulte]]></dc:creator>
		<pubDate>Tue, 04 Dec 2018 06:52:07 +0000</pubDate>
				<category><![CDATA[Beispiel-Implementierungen]]></category>
		<category><![CDATA[Plugins]]></category>
		<category><![CDATA[ConBee]]></category>
		<category><![CDATA[Dresden Elektronik]]></category>
		<category><![CDATA[HUE]]></category>
		<category><![CDATA[hue2]]></category>
		<category><![CDATA[Phoscon]]></category>
		<category><![CDATA[RaspBee]]></category>
		<category><![CDATA[ZigBee]]></category>
		<guid isPermaLink="false">https://www.smarthomeng.de/?p=2260</guid>

					<description><![CDATA[Hallo, ich möchte Euch in diesem Blog die Implementierung der RaspBee Gateway Bridge von Dresden Elektronik mittels des HUE-Plugins vorstellen. Das Aufsatzmodul erweitert den Raspberry Pi um die ZigBee-Funktionalität und ermöglicht über die REST API Schnittstelle der deCONZ Applikation die direkte Kommunikation mit einer Vielzahl von ZigBee 3.0 bzw. ZigBee<a class="moretag" href="https://www.smarthomeng.de/raspbee-gateway-bridge-mit-dem-smarthomeng-plugin-hue"> Weiterlesen&#8230;</a>]]></description>
										<content:encoded><![CDATA[<p>Hallo,</p>
<p>ich möchte Euch in diesem Blog die Implementierung der RaspBee Gateway Bridge von Dresden Elektronik mittels des HUE-Plugins vorstellen. Das Aufsatzmodul erweitert den Raspberry Pi um die ZigBee-Funktionalität und ermöglicht über die REST API Schnittstelle der deCONZ Applikation die direkte Kommunikation mit einer Vielzahl von ZigBee 3.0 bzw. ZigBee PRO Geräten, darunter beispielsweise Philips‘ Hue, die Vorschaltgeräteserie von Dresden Elektronik oder der XBee Serie 2. Die Kompatibilitätsliste der RaspBee Gateway Bridge findet Ihr <span><a href="https://www.dresden-elektronik.de/funktechnik/solutions/wireless-light-control/kompatibilitaet/">hier</a></span>.</p>
<p>In meinem Fall verwende ich die RaspBee Bridge in einem dezentralen Raspberry Pi 3 Model B um über das HUE-Plugin schaltbare Steckdosen (Osram Smart+ Plug und Paulmann Cephei Schaltcontroller) und vor allem mehrere RGB/W LED-Streifenlichter (24V mit 5050 SMD’s) mittels ZigBee LED Vorschaltgeräte (Funkvorschaltgerät FLS-PP lp von Dresden Elektronik) in SmarthomeNG einzubinden. Die Anzahl der ZigBee 3.0 Geräte wächst rasant und der Open Source REST-API Standard sorgt dafür das die Preise wettbewerbsfähig bleiben &#8211; Insofern sollten die hier erwähnten Geräte nur einen kleinen beispielhaften Ausschnitt darstellen, was (theoretisch) mit ZigBee Geräten und der RaspBee Bridge möglich ist.</p>
<p>Zu beachten ist, dass das SmarthomeNG HUE-Plugin nur auf Geräte aus dem „Licht“-Bereich zugreifen kann. ZigBee Geräte für Schalter und Sensoren lassen sich zwar in das RaspBee Gateway einbinden, aber nicht mit dem HUE-Plugin ansprechen (falls dem nicht so ist, lasst es mich bitte wissen).</p>
<p>Das System läuft jetzt bei mir seit über einem halben Jahr sehr stabil zusammen mit anderen Systemen (u.a. KNX, Sonos und MQTT) in SmarthomeNG bzw. SmartVISU &#8211; einschließlich des Widgets hue.control zur Farbsteuerung der LED Stripes. Die Reaktionszeit steht KNX in nichts nach.</p>
<p><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2018/12/ZigBee_Beispiel.jpg" alt="" width="720" height="475" class="alignleft size-full wp-image-2275" srcset="https://www.smarthomeng.de/wp-content/uploads/2018/12/ZigBee_Beispiel.jpg 720w, https://www.smarthomeng.de/wp-content/uploads/2018/12/ZigBee_Beispiel-300x198.jpg 300w" sizes="(max-width: 720px) 100vw, 720px" /></p>
<p>Allerdings hatte ich noch nicht die Zeit mich mit allen Funktionalitäten zu befassen. Auf meiner ToDo – Liste steht u.a. noch die Konfiguration der Szenensteuerung.</p>
<p>&nbsp;</p>
<h4>Installation des Bridge-Moduls</h4>
<p>Die Installation der Bridge-Moduls selbst auf einem Raspberry Pi Model B (RasPi2 oder 3) wird von Dresden Elektronik gut beschrieben. Das Modul wird auf die GPIO Stiftleiste aufgesteckt. In meinem Fall habe ich den RasPi samt Modul als feste Installation im Erdgeschoss platziert und über LAN mit dem Netzwerk verbunden. Eine Verbindung über WLAN sollte genauso gehen, aber da ich die Möglichkeit einer LAN-Verkabelung hatte, wollte ich vermeiden das der WLAN Chip des RasPi das ZigBee Signal des benachbarten Bridge-Moduls stört, das nur mit einer Chip-Keramik-Antenne ausgestattet ist. Damit sollte zwar laut Datenblatt eine Reichweite von 500m (freies Feld) möglich sein, aber da die wenigsten von uns Ihr Bett im Kornfeld aufgestellt haben, kann im realen Betrieb nach einigen wenigen massiven Wänden schon Funkschatten entsteht. Nun übertragen die ZigBee Geräte die Signale jedoch nicht nur zentral zur Bridge sondern kommunizieren auch untereinander als sogenanntes Mesh-Netzwerk. Somit lösen sich solche Probleme mit steigender Anzahl von Geräten von selbst. Trotzdem habe ich mich wie bereits erwähnt entschieden LAN zu nutzen und WLAN und Bluetooth im Raspberry auszuschalten in dem man das Laden der Treiber verhindert.</p>
<p>&nbsp;</p>
<p>Wer es genauso machen möchte: Zuerst die Datei /etc/modprobe.d/raspi-blacklist.conf mit einem Editor bearbeiten. Dazu öffnet man die Konfigurationsdatei:</p>
<pre><code class="language-actionscript">sudo nano /etc/modprobe.d/raspi-blacklist.conf</code></pre>
<p>In der Datei die folgenden Treiber deaktivieren:<code class="language-actionscript"><br />
</code></p>
<pre><code class="language-markup"># WLAN abschalten
blacklist brcmfmac
blacklist brcmutil
#blacklist cfg80211
#blacklist rfkill
# Bluetooth abschalten
blacklist btbcm
blacklist hci_uart</code></pre>
<p>Anschließend speichern und schließen. Zusätzlich muss man noch einen Dienst abschalten, der sonst versucht Bluetooth zu nutzen.</p>
<pre><code class="language-markup">sudo systemctl disable hciuart</code></pre>
<p>Danach ist ein Neustart notwendig.</p>
<p>&nbsp;</p>
<h4>Installation der Applikationssoftware deCONZ</h4>
<p>Für das Aufsetzen des Raspbian Stretch selbst und der Applikationssoftware deCONZ (mit dem Namen der Software konnte ich mich nie anfreunden) für das eigentliche Bridge-Modul gibt es eine ziemlich gute Anleitung von <span><a href="https://www.dresden-elektronik.de/fileadmin/Downloads/Dokumente/Produkte/ZLL/RaspBee-BHB-en.pdf">Dresden Elektronik</a></span>.</p>
<p><u>Vor</u> der Installation von deCONZ habe ich einen eigenen User bzw. Usergruppen für SmarthomeNG anlegen. Ich habe mich dabei an die gleichen Einstellungen wie für die <span><a href="https://www.smarthomeng.de/user/installation/komplettanleitung_debian.html">Installation von SmarthomeNG</a></span> gehalten:</p>
<pre><code class="language-markup">sudo adduser smarthome --disabled-password --gecos "First Last,RoomNumber,WorkPhone,HomePhone"
sudo usermod -aG www-data,sudo smarthome
sudo usermod -aG www-data smarthome</code></pre>
<p>&nbsp;</p>
<p>Der Benutzer ‚smarthome‘ muss nun noch abgemeldet und neu angemeldet werden, damit die Rechte neu eingelesen werden. Dazu am besten das System neu booten mit:</p>
<pre><code class="language-markup">sudo poweroff</code></pre>
<p>&nbsp;</p>
<p>Nach der Installation von deConz sollte der ‚deCONZ systemd headless service‘ laufen und man kann über das RaspBee Gateway die einzelnen ZigBee Geräte anschließen.</p>
<p>&nbsp;</p>
<h4>Anmelden neuer ZigBee Geräte an der Bridge</h4>
<p>Das Hinzufügen und Einrichten neuer ZigBee Geräte geschieht am besten mit dem deCONZ Webinterface ‚<span><a href="https://www.dresden-elektronik.de/funktechnik/solutions/wireless-light-control/wireless-ballasts/?eID=dam_frontend_push&amp;docID=4620">Wireless Light Control WebApp</a></span>‘. Daneben gibt es noch eine sehr umfangreichere deCONZ GUI, die eher für Entwickler gedacht ist. Für die reine Einbindung neuer Geräte ist die GUI nicht notwendig.</p>
<p>Wichtig ist, dass die elektrische Installation der ZigBee Geräte erst nach der Anmeldung in der RaspBee Bridge erfolgt, da zum Anmelden der Abstand zwischen Gerät und Bridge nur max. 50 cm betragen darf. Wer vorher schon z.B. das LED Vorschaltgerät im Gebäude verdrahtet hat, muss nochmal zum Schraubenzieher greifen, um beide Geräte zueinander zu führen.</p>
<p>Das eigentliche Anmelden neuer Licht-Geräte ist relativ einfach. Im Webinterface (‚Settings‘) das ZigBee-Netzwerk der Bridge öffnen (‚Open Network‘; ‚Done‘) und dann kurz warten bis das neue Gerät (sofern es sich in unmittelbarer Nähe zur Bridge befindet!) in der Geräteübersicht auftaucht.</p>
<p>&nbsp;</p>
<h4>Einrichten des HUE-Plugins in SmarthomeNG</h4>
<p>Egal ob HUE-Beleuchtung oder ZigBee (Licht-) Gerät eines anderen Anbieters – durch das HUE-Plugin kann man auf die Bridge zugreifen und die Werte auslesen bzw. neu setzen.</p>
<p>Die Installation des HUE-Plugins für die RaspBee-Bridge erfolgt analog wie für eine HUE-Bridge und ist in der SHNG <span><a href="https://www.smarthomeng.de/user/plugins/hue/README.html">Anwenderdokumentation</a></span> ganz gut beschrieben, deshalb gehe ich hier nicht näher darauf ein.</p>
<p>Damit sich anschließend das HUE-Plugin mit der Bridge verbinden kann, muss sich SHNG als neue Applikation einmalig in der Bridge registrieren. Dazu wird in der plugin.conf (/usr/smarthome/etc/plugin.conf) ein beliebig wählbarer User-Token hue_user als Listenwert eingetragen:</p>
<pre><code class="language-yaml">HUE:
    class_name: HUE
    class_path: plugins.hue
    hue_user:
      - 38f625a739562a8bd261ab9c7f5e62c8
    hue_ip:
      - 192.168.178.99
    hue_port:
      - '80'
    cycle_lamps: 10
    cycle_bridges: 30
    default_transitionTime: '0.4'</code></pre>
<p>&nbsp;</p>
<p>Die User ID dient, wenn autorisiert, als API-Key für die Kommunikation zwischen SHNG und der RaspBee-Bridge.</p>
<p>Für das eigentliche Registrieren benötigt man noch die Methode sh.hue.authorizeuser(), die man in eine kleine Logik einbinden kann:</p>
<pre class="line-numbers"><code class="language-python">#!/usr/bin/env python3
# hue_autorisierung.py
# 1) Unlock Gateway in deCONZ Webapp (System Settings)
# 2) Start logic ('Hue_autorisierung')
sh.HUE.authorizeuser()</code></pre>
<p>Und so läuft die Registrierung ab:</p>
<ul>
<li>Das Webinterface ‚Wireless Light Control WebApp‘ der Bridge öffnen</li>
<li>Über Menu &gt; Settings &gt; ‚Unlock Gateway‘ auswählen (nicht mit ‚Open Network‘ verwechseln, das nur für das Hinzufügen von Geräten verwendet wird).</li>
<li>Innerhalb von 60 Sekunden muss jetzt die Autorisierungs-Logik (s.o.) ausgelöst werden.</li>
</ul>
<p>Alternativ kann man die User-ID auch mit einem Rest Client in der Bridge generieren und anschließend auslesen um sie in der plugin.conf einzutragen.</p>
<p>&nbsp;</p>
<h4>Aufbau der Items</h4>
<p>Der Aufbau der Items in SmarthomeNG erfolgt wie in der Anleitung zum HUE-Plugin beschrieben. Die benötigte hue_lamp_id der jeweiligen Lampen kann mit einem REST-Client (z.B. ‚Postman‘ als Chrome Plugin) aus der Bridge ausgelesen werden:</p>
<pre><code class="language-rest">GET &lt;IP.VON.RASPBEE&gt;/api/&lt;USER-ID&gt;/lights</code></pre>
<p>&nbsp;</p>
<p><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2018/12/GET_Postman.jpg" alt="" width="792" height="692" class="alignleft size-full wp-image-2267" srcset="https://www.smarthomeng.de/wp-content/uploads/2018/12/GET_Postman.jpg 792w, https://www.smarthomeng.de/wp-content/uploads/2018/12/GET_Postman-300x262.jpg 300w, https://www.smarthomeng.de/wp-content/uploads/2018/12/GET_Postman-768x671.jpg 768w" sizes="(max-width: 792px) 100vw, 792px" /></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Die Antwort vom RaspBee listet alle verbundenen Geräte der Kategorie /lights in der Reihenfolge auf wie sie für die hue_lamp_id in der Itemdefinition verwendet wird.</p>
<p>Ich weiß leider nicht wie ein HUE-Gateway die RGB/W Steuerung vornimmt, aber in der RaspBee-Bridge wird bei der Verwendung von RGB/W LED-Stripes (zumindest bei einigen LED Controllern) der RGB-Bereich und der W-Bereich als getrennte Lampen – ID ausgewiesen. Das macht die Erstellung der Items etwas aufwändig, aber funktioniert problemlos.</p>
<p>Am einfachsten definiert man die ‚System‘ Items (also alles, was nicht speziell einem Gerät zugeordnet wird) in einer separaten raspbee.yaml.</p>
<pre class="line-numbers"><code class="language-yaml">raspbee:
  bridge_0:
    # if hue_lamp_id and hue_bridge_id is not set, it is searched in a higher layer
    # all light control specfic items are defined in 'smartvisu.conf' or 'smartvisu.yaml'
    hue_bridge_id: 0

    bridge_name:
        type: str
        hue_listen: bridge_name

    zigbeechannel:
        type: num
        hue_listen: zigbeechannel

    mac:
        type: str
        hue_listen: mac

    dhcp:
        type: bool
        hue_listen: dhcp

    ipaddress:
        type: str
        hue_listen: ipaddress

    netmask:
        type: str
        hue_listen: netmask

    gateway:
        type: str
        hue_listen: gateway

    utc:
        type: str
        hue_listen: UTC

    localtime:
        type: str
        hue_listen: localtime

    timezone:
        type: str
        hue_listen: timezone

    whitelist:
        type: dict
        hue_listen: whitelist

    bridge_swversion:
        type: str
        hue_listen: bridge_swversion

    apiversion:
        type: str
        hue_listen: apiversion

    swupdate:
        type: dict
        hue_listen: swupdate

    linkbutton:
        type: bool
        hue_listen: linkbutton

    portalservices:
        type: bool
        hue_listen: portalservices

    portalconnection:
        type: str
        hue_listen: portalconnection

    portalstate:
        type: dict
        hue_listen: portalstate

    scene:
        type: str
        hue_send: scene
        enforce_updates: 'true'</code></pre>
<p>&nbsp;</p>
<p>Die Items der Geräte werden dann in der smartvisu.yaml definiert. Das angehängte Beispiel zeigt die Steuerung für ein RGB/W Vorschaltgerät, unterteilt in einen RGB Teil und den dazugehörigen Weiss-Teil.</p>
<pre class="line-numbers"><code class="language-yaml">obergeschoss:

    seperator_obergeschoss:
        name: Obergeschoss
        sv_page: seperator

    gallerie:
        name: Gallerie
        sv_page: room
        sv_img: light_indoor_og_1.svg

        skulptur_rgb:
            name: Wand (Skulptur)
            visu_acl: rw
            sv_blocksize: 3
            sv_widget: "{{ hue.control('item','item.power','item.reachable','item.col_r','item.col_g','item.col_b','item.alert','item.effect','item.bri','item.sat','item.hue') }}"
            hue_lamp_id: 2
            hue_bridge_id: 0
            hue_lamp_type: 0

            power_knx:
                type: bool
                value: 'true'
                knx_dpt: 1
                knx_send: 7/0/11
                knx_listen: 7/0/11
                knx_cache: 7/0/11

            power:
                type: bool
                hue_send: 'on'
                hue_listen: 'on'
                knx_dpt: 1
                knx_send: 7/0/23
                knx_listen: 7/0/23
                knx_cache: 7/0/23

            reachable:
                type: bool
                hue_listen: reachable

            ct:
                type: num
                hue_send: ct
                hue_listen: ct

            bri:
                type: num
                cache: 'on'
                hue_send: bri
                hue_listen: bri
                hue_transitionTime: '0.2'

                dim:
                    type: list
                    # knx_dpt = 3
                    # knx_listen = x/x/x
                    hue_dim_max: 255
                    hue_dim_step: 10
                    hue_dim_time: '0.2'

            sat:
                type: num
                cache: 'on'
                hue_send: sat
                hue_listen: sat

            col_r:
                type: num
                cache: 'on'
                hue_send: col_r

            col_g:
                type: num
                cache: 'on'
                hue_send: col_g

            col_b:
                type: num
                cache: 'on'
                hue_send: col_b

            hue:
                type: num
                cache: 'on'
                hue_send: hue
                hue_listen: hue
                hue_transitionTime: '0.2'

                dim:
                    type: list
                    # knx_dpt = 3
                    # knx_listen = x/x/x
                    hue_dim_max: 65535
                    hue_dim_step: 2000
                    hue_dim_time: '0.2'

            effect:
                type: str
                hue_send: effect
                hue_listen: effect

            alert:
                type: str
                hue_send: alert
                hue_listen: alert

            modeltype:
                type: str
                hue_listen: type

            lampname:
                type: str
                hue_listen: name

            modelid:
                type: str
                hue_listen: modelid

            swversion:
                type: str
                hue_listen: swversion

        skulptur_white:
            name: Wandskulptur Weiss
            sv_blocksize: 3
            hue_lamp_id: 3
            hue_bridge_id: 0
            hue_lamp_type: 0
            visu_acl: rw
            sv_widget: "{{ basic.stateswitch('item1', 'item.power', 'mini', '', '', '', '') }}{{ basic.slider('item2', 'item.bri') }}"

            power:
                type: bool
                hue_send: 'on'
                hue_listen: 'on'
                knx_dpt: 1
                knx_send: 7/0/23
                knx_listen: 7/0/23
                knx_cache: 7/0/23

            reachable:
                type: bool
                hue_listen: reachable

            bri:
                type: num
                cache: 'on'
                hue_send: bri
                hue_listen: bri
                hue_transitionTime: '0.2'

                dim:
                    type: list
                    # knx_dpt = 3
                    # knx_listen = x/x/x
                    hue_dim_max: 255
                    hue_dim_step: 10
                    hue_dim_time: '0.2'

            effect:
                type: str
                hue_send: effect
                hue_listen: effect

            alert:
                type: str
                hue_send: alert
                hue_listen: alert

            modeltype:
                type: str
                hue_listen: type

            lampname:
                type: str
                hue_listen: name

            modelid:
                type: str
                hue_listen: modelid

            swversion:
                type: str
                hue_listen: swversion</code></pre>
<p>&nbsp;</p>
<h4>Nachtrag</h4>
<p>Die Anzahl der ZigBee Geräte nimmt wie schon erwähnt rasant zu. Am Anfang waren es hauptsächlich (LED)-Lampen und Leuchten wie z.B. das Phillips HUE Programm. Das HUE Plugin in SmarthomeNG deckt das sehr schon ab und damit lässt sich alles steuern. Mittlerweile erlauben die Gateway Bridges der verschiedenen Hersteller aber auch die Kommunikation mit ZigBee Schaltern und Sensoren (Temperatur, Feuchtigkeit, Bewegungsmelder, Rauchmelder, Luftqualität, etc.). Leider scheint das HUE-Plugin dafür nicht ausgelegt zu sein, so dass sich die Anwendung auf den Bereich ‚Licht schalten‘ beschränkt. Langfristig würde wahrscheinlich ein Plugin Sinn machen das alle Applikationen des RaspBee aus dem ZigBee 3.0 Bereich abdeckt.</p>
<p>&nbsp;</p>
<p>TODO:</p>
<ul>
<li>Implementierung der Szenensteuerung</li>
<li>Integration von ZigBee Sensoren (z.B aus der Xiaomi-Serie)</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.smarthomeng.de/raspbee-gateway-bridge-mit-dem-smarthomeng-plugin-hue/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Blinken &#8211; per Logik</title>
		<link>https://www.smarthomeng.de/blinken-per-logik</link>
					<comments>https://www.smarthomeng.de/blinken-per-logik#comments</comments>
		
		<dc:creator><![CDATA[gama]]></dc:creator>
		<pubDate>Sun, 04 Nov 2018 09:16:27 +0000</pubDate>
				<category><![CDATA[Beispiel-Implementierungen]]></category>
		<category><![CDATA[Logics]]></category>
		<category><![CDATA[Tipps & Tricks]]></category>
		<category><![CDATA[Alarm]]></category>
		<category><![CDATA[Blinklicht]]></category>
		<category><![CDATA[Logiken]]></category>
		<guid isPermaLink="false">https://www.smarthomeng.de/?p=2188</guid>

					<description><![CDATA[Die Idee für die Logik kam aus meinem Alarmmodul. Dort sollten im Alarm-/Feuer-/Panikfall die Lichter an der Hauswand und die Gartenbeleuchtung im Wechsel blinken. Aber auch für die Signalisieren auf Taster LEDs könnte die Logik ihren Einsatz finden. Beispielsweise um im Schlafzimmer einen Status per LED auszugeben, ohne dass die<a class="moretag" href="https://www.smarthomeng.de/blinken-per-logik"> Weiterlesen&#8230;</a>]]></description>
										<content:encoded><![CDATA[<p>Die Idee für die Logik kam aus meinem Alarmmodul. Dort sollten im Alarm-/Feuer-/Panikfall die Lichter an der Hauswand und die Gartenbeleuchtung im Wechsel blinken. Aber auch für die Signalisieren auf Taster LEDs könnte die Logik ihren Einsatz finden. Beispielsweise um im Schlafzimmer einen Status per LED auszugeben, ohne dass die LED die ganze Nacht leuchtet.</p>
<p>Somit war der Grundstein für die Logik gelegt, die möglichst flexibel, für unterschiedliche Anwendungen, konfigurierbar sein soll.</p>
<h4>Funktion:</h4>
<p>Gesteuert wird die Logik über ein control-item, welches zugleich in der Logik Konfiguration als watch-item dient. Wir das control-item auf True gesetzt, so läuft die Logik. False stoppt die Blinkroutine.</p>
<p><span><strong>blink_cycles</strong> setzt Initial den Countdown, der mit jedem Aufruf um 1 nach unten zählt. Bei 0 stoppt die Logik und setzt zudem das control-item auf False. </span></p>
<p><span><strong>blink_interval</strong> bestimmt die Blinkabstand in Sekunden. </span></p>
<p><span>Mit <strong>items_cyclic</strong> und </span><span><strong>items_anticyclic</strong> werden die Items übergeben, die blinken sollen. Antizyklische Items werden immer dann aus/an wenn die zyklischen Items an/aus sind. </span></p>
<p>Bevor die items zum blinken gebracht werden, sichert die Logik die ursprünglichen Werte, die auch am Ende wieder hergestellt werden.</p>
<p>Um die Routine ohne aktives schalten der Items testen zu können, wurde noch dry_run aufgenommen.</p>
<p>Zusammengefasst ergibt sich folgende Beispiel-Konfiguration für <strong>../etc/logic.yaml</strong>:</p>
<pre class="line-numbers"><code class="language-yaml">visual_alarm:
    filename: blinker.py
    watch_item: HOME.alarm.action.blinkinglight
    set_items_cyclic:
        - AUSSEN.south.light.outer
        - AUSSEN.west.light.outer
    set_items_anticyclic:
        - AUSSEN.south.light.inner
        - AUSSEN.east.light.door
        - AUSSEN.west.light.nested
    set_blink_cycles: 4 # -1 for endless loop 
    set_blink_interval: 4
    dry_run: 1</code></pre>
<p>Alle items müssen vom Typ boolean sein, da nur einfache Schaltoperationen durchgeführt werden.</p>
<p>Die Logik lässt sich relativ einfach nutzen, indem das im watch_item verbundene Item, also im Beispiel <code class="language-python">HOME.alarm.action.blinkinglight</code>, auf True gesetzt wird. Die Logik ruft sich selbst solange auf, bis <code class="language-python">set_blink_cycles</code> erreicht ist oder vorher das watch_item auf False gesetzt wurde. Am Ende wird das watch_item auf von der Logik auf False gesetzt.</p>
<p>Die Logik selbst ist in <strong>../logics/blinker.py</strong>:</p>
<pre class="line-numbers"><code class="language-python">
#!/usr/bin/env python3
# vim: tabstop=2 softtabstop=2 shiftwidth=2 expandtab

# Logic blinker.py

# LOGIC config
# -------------------------------------------------------
# visual_alarm:
#   filename: blinker.py
#   watch_item: HOME.alarm.action.blinkinglight
#   set_items_cyclic:
#     - AUSSEN.south.light.outer
#     - AUSSEN.west.light.outer
#   set_items_anticyclic: 
#     - AUSSEN.south.light.inner
#     - AUSSEN.east.light.door 
#     - AUSSEN.west.light.nested
#   set_blink_cycles: -1 " -1: endless 
#   set_blink_interval: 2
#   dry_run: 0 

control_item_name = trigger['source'] 
control_item = sh.return_item(control_item_name)
triggered_by = trigger['by'] 


if control_item is not None:
  logger.info("LOGIC blinker.py: {0} triggered from source {1} by {2}".format(logic.name, control_item_name, triggered_by))

  # control_item_active = trigger['value']
  control_item_active = control_item()

  # set parameters from logic config
  if hasattr(logic, 'set_items_cyclic'):
    blink_items_cyclic = logic.set_items_cyclic

  if hasattr(logic, 'set_items_anticyclic'):
    blink_items_anticyclic = logic.set_items_anticyclic

  if hasattr(logic, 'set_blink_cycles'):
    blink_cycles = int(logic.set_blink_cycles)
  else:
    blink_cycles = 2
    logger.info("LOGIC blinker.py: no blink cycles set, setting to default: {2}".format(blink_cycles))

  if hasattr(logic, 'set_blink_interval'):
    blink_interval = int(logic.set_blink_interval)
  else:
    blink_interval = 2
    logger.info("LOGIC blinker.py: no blink interval set, setting to default: {2}".format(blink_interval))

  if hasattr(logic, 'dry_run'):
    dry_run = (int(logic.dry_run) &gt; 0)
  else:
    dry_run = False

  if control_item_active == True:

    # first call?
    if not hasattr(logic, 'blink_init'):
      logger.info("LOGIC blinker.py: initiating {0} blinks for {1} cyclic and {2} anti-cyclic items".format(blink_cycles, len(blink_items_cyclic), len(blink_items_anticyclic)))
      logic.blink_init = True

      blink_items = blink_items_cyclic + blink_items_anticyclic
      if len(blink_items) &gt; 0:
        # SAVE item states
        logic.saved_items = {}
        for item_name in blink_items:
          item = sh.return_item(item_name)
          logic.saved_items[item_name] = item()
          logger.info("LOGIC blinker.py: save {0} = {1}".format(item_name, item()))

      if dry_run == True:
        logger.warn("LOGIC blinker.py: test mode, dry run active")

      else:
        logger.warn("LOGIC blinker.py: no blink items set")
 

    # prepare blink
    if hasattr(logic, 'blink_value'):
      logic.blink_value = not logic.blink_value
    else:
      logic.blink_value = True

    if hasattr(logic, 'blink_countdown'):
      logic.blink_countdown -= 1
    else:
      logic.blink_countdown = blink_cycles
     
    # any blinks left?
    if logic.blink_countdown &gt; 0 or blink_cycles == -1:
      if blink_cycles == -1:
        logger.info("LOGIC blinker.py BLINK (endless)")
      else:
        logger.info("LOGIC blinker.py BLINK ({0} cycles remaining)".format(logic.blink_countdown))

      # set all cyclic items
      for item_name in blink_items_cyclic:
        item = sh.return_item(item_name)
        logger.info("LOGIC blinker.py SET {item} to {value})".format(item=item_name, value=logic.blink_value))
        if dry_run == False:
          item(logic.blink_value)

      # set all anti-cyclic items
      for item_name in blink_items_anticyclic:
        item = sh.return_item(item_name)
        logger.info("LOGIC blinker.py SET {item} to {value})".format(item=item_name, value=(not logic.blink_value)))
        if dry_run == False:
          item(not logic.blink_value)

      sh.trigger("logics.{0}".format(logic.name), by=trigger['by'], source=trigger['source'], value=trigger['value'], dt=sh.now() + datetime.timedelta(seconds=blink_interval))

    else:
      logger.info("LOGIC blinker.py countdown finished, set control item to False")
      control_item(False) # this triggers logic again!

  else:
    logger.info("LOGIC blinker.py control item set to false: stop blink logic, restore saved item values")

    # RESTORE item states
    if hasattr(logic, 'saved_items'):
      for item_name in logic.saved_items:
        item = sh.return_item(item_name)
        value = logic.saved_items[item_name]
        logger.info("LOGIC blinker.py: restore {0} = {1}".format(item_name, item()))
        if dry_run == False:
          item(value)
      
    # reset persistent values
    if hasattr(logic, 'saved_items'):
      delattr(logic, 'saved_items')  
    if hasattr(logic, 'blink_countdown'):
      delattr(logic, 'blink_countdown')  
    if hasattr(logic, 'blink_value'):
      delattr(logic, 'blink_value')  
    if hasattr(logic, 'blink_init'):
      delattr(logic, 'blink_init')  
    
else:
  logger.info("LOGIC blinker.py triggered without source... ignoring...")</code></pre>
<p>&nbsp;</p>
<h4>Überarbeitung im Januar 2021:</h4>
<p>Diese Logik wurde aufgrund weitere Anforderungen im Januar 2021 ergänzt. (siehe <a href="https://knx-user-forum.de/forum/supportforen/smarthome-py/1600954-blinker-py-konfigurieren">Thread im Forum</a>) Die folgende Version vom 18. Januar 2021 blinkt mit einem oder zwei Items:</p>
<p>&nbsp;</p>
<pre class="line-numbers"><code class="language-python">
#!/usr/bin/env python3
# vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab

# Logic blinker2.py

# LOGIC config
# -------------------------------------------------------
# visual_alarm:
#     filename: blinker2.py
#     watch_item: HOME.alarm.action.blinkinglight
#     set_items_cyclic:
#         - AUSSEN.south.light.outer
#         - AUSSEN.west.light.outer
#     set_items_anticyclic:
#         - AUSSEN.south.light.inner
#         - AUSSEN.east.light.door
#         - AUSSEN.west.light.nested
#     set_blink_cycles: -1 ' -1: endless
#     set_blink_interval: 2
#     dry_run: 0

control_item_name = trigger['source']
control_item = sh.return_item(control_item_name)
triggered_by = trigger['by']

if control_item is not None:
    logger.info(f'LOGIC blinker.py: {logic.name} triggered from source {control_item_name} by {triggered_by}')

    # control_item_active = trigger['value']
    control_item_active = control_item()

    # set parameters from logic config
    blink_items_cyclic = []
    if hasattr(logic, 'set_items_cyclic'):
        blink_items_cyclic = logic.set_items_cyclic
    if blink_items_cyclic == 'None':
        blink_items_cyclic = []
    if isinstance(blink_items_cyclic, str):
        blink_items_cyclic = [blink_items_cyclic, ]

    # do we have blink items?
    if not isinstance(blink_items_cyclic, list) or len(blink_items_cyclic) == 0:
        logger.info('LOGIC blinker.py: error, no cyclic blink items set. Aborting')
    else:

        blink_items_anticyclic = []
        if hasattr(logic, 'set_items_anticyclic'):
            blink_items_anticyclic = logic.set_items_anticyclic
        if blink_items_anticyclic == 'None':
            blink_items_anticyclic = []
        elif isinstance(blink_items_anticyclic, str):
            blink_items_anticyclic = [blink_items_anticyclic, ]

        # did we get strange data?
        if not isinstance(blink_items_anticyclic, list):
            blink_items_anticyclic = []

        if hasattr(logic, 'set_blink_cycles'):
            blink_cycles = int(logic.set_blink_cycles)
        else:
            blink_cycles = 2
            logger.info(f'LOGIC blinker.py: no blink cycles set, setting to default: {blink_cycles}')

        if hasattr(logic, 'set_blink_interval'):
            blink_interval = int(logic.set_blink_interval)
        else:
            blink_interval = 2
            logger.info(f'LOGIC blinker.py: no blink interval set, setting to default: {blink_interval}')

        if hasattr(logic, 'dry_run'):
            dry_run = (int(logic.dry_run) &gt; 0)
        else:
            dry_run = False

        if control_item_active:

            # first call?
            if not hasattr(logic, 'blink_init'):
                logger.info(f'LOGIC blinker.py: initiating {blink_cycles} blinks for {len(blink_items_cyclic)} cyclic and {len(blink_items_anticyclic)} anti-cyclic items')
                logic.blink_init = True

                blink_items = blink_items_cyclic + blink_items_anticyclic
                if len(blink_items) &gt; 0:
                    # SAVE item states
                    logic.saved_items = {}
                    for item_name in blink_items:
                        item = sh.return_item(item_name)
                        if item:
                            logic.saved_items[item_name] = item()
                            logger.info(f'LOGIC blinker.py: save {item_name} = {item()}')

                    if dry_run:
                        logger.warn('LOGIC blinker.py: test mode, dry run active')

                else:
                    logger.warn('LOGIC blinker.py: no blink items set')

            # prepare blink
            if hasattr(logic, 'blink_value'):
                logic.blink_value = not logic.blink_value
            else:
                logic.blink_value = True

            if hasattr(logic, 'blink_countdown'):
                logic.blink_countdown -= 1
            else:
                logic.blink_countdown = blink_cycles

            # any blinks left?
            if logic.blink_countdown &gt; 0 or blink_cycles == -1:
                if blink_cycles == -1:
                    logger.info('LOGIC blinker.py BLINK (endless)')
                else:
                    logger.info(f'LOGIC blinker.py BLINK ({logic.blink_countdown} cycles remaining)')

                # set all cyclic items
                for item_name in blink_items_cyclic:
                    item = sh.return_item(item_name)
                    logger.info(f'LOGIC blinker.py SET {item_name} to {logic.blink_value}')
                    if not dry_run:
                        item(logic.blink_value)
                        print(f'    - {item.id()}:{item()}')

                # set all anti-cyclic items
                for item_name in blink_items_anticyclic:
                    item = sh.return_item(item_name)
                    logger.info(f'LOGIC blinker.py SET {item_name} to {not logic.blink_value}')
                    if not dry_run:
                        item(not logic.blink_value)
                        print(f'    - {item.id()}:{item()}')

                sh.trigger(f'logics.{logic.name}', by=trigger['by'], source=trigger['source'], value=trigger['value'], dt=sh.now() + datetime.timedelta(seconds=blink_interval))

            else:
                logger.info('LOGIC blinker.py countdown finished, set control item to False')
                control_item(False)  # this triggers logic again!

        else:
            logger.info('LOGIC blinker.py control item set to false: stop blink logic, restore saved item values')

            # RESTORE item states
            if hasattr(logic, 'saved_items'):
                for item_name in logic.saved_items:
                    item = sh.return_item(item_name)
                    value = logic.saved_items[item_name]
                    logger.info(f'LOGIC blinker.py: restore {item_name} = {item()}')
                    if not dry_run:
                        item(value)

            # reset persistent values
            if hasattr(logic, 'saved_items'):
                delattr(logic, 'saved_items')
            if hasattr(logic, 'blink_countdown'):
                delattr(logic, 'blink_countdown')
            if hasattr(logic, 'blink_value'):
                delattr(logic, 'blink_value')
            if hasattr(logic, 'blink_init'):
                delattr(logic, 'blink_init')

else:
    logger.info('LOGIC blinker.py triggered without source... ignoring...')
</code></pre>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.smarthomeng.de/blinken-per-logik/feed</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Google Maps Widget für SmartVISU 2.9</title>
		<link>https://www.smarthomeng.de/google-maps-widget-fuer-smartvisu-2-9</link>
					<comments>https://www.smarthomeng.de/google-maps-widget-fuer-smartvisu-2-9#respond</comments>
		
		<dc:creator><![CDATA[Marc René Frieß]]></dc:creator>
		<pubDate>Sat, 18 Aug 2018 14:52:28 +0000</pubDate>
				<category><![CDATA[Beispiel-Implementierungen]]></category>
		<category><![CDATA[Plugins]]></category>
		<category><![CDATA[egigeozone]]></category>
		<category><![CDATA[Google Maps]]></category>
		<category><![CDATA[smartVISU]]></category>
		<category><![CDATA[smartVISU 2.9]]></category>
		<category><![CDATA[Traffic Plugin]]></category>
		<category><![CDATA[Widget]]></category>
		<guid isPermaLink="false">https://www.smarthomeng.de/?p=1995</guid>

					<description><![CDATA[Auch in der Visualisierung kann es von Nutzen sein, seine aktuelle Position und ggf. eine Fahrtroute auf einer Google Maps Karte zu visualisieren. SmartHomeNG bietet eine Reihe an Plugins und Möglichkeiten, um die jeweiligen Geokoordinaten zu erfassen (EgiGeoZone mit Network oder Webservices Plugin) und die Fahrzeit zu berechnen (Traffic Plugin).<a class="moretag" href="https://www.smarthomeng.de/google-maps-widget-fuer-smartvisu-2-9"> Weiterlesen&#8230;</a>]]></description>
										<content:encoded><![CDATA[<p>Auch in der Visualisierung kann es von Nutzen sein, seine aktuelle Position und ggf. eine Fahrtroute auf einer Google Maps Karte zu visualisieren. SmartHomeNG bietet eine Reihe an Plugins und Möglichkeiten, um die jeweiligen Geokoordinaten zu erfassen (EgiGeoZone mit Network oder Webservices Plugin) und die Fahrzeit zu berechnen (Traffic Plugin). Die Anzeige dieser Daten muss jedoch in der Visualisierung geschehen. <span id="more-1995"></span><br />
Aus dieser Motivation heraus habe ich ein Google Maps Widget für die SmartVISU geschrieben, welches mir meine aktuelle Position, meine Heimkoordinaten und den Weg zur Arbeit bzw. den Weg von der aktuellen Position nach Hause anzeigt.</p>
<p>In <a href="https://www.smarthomeng.de/geozonen-basierte-services-mit-der-egigeozone-app-und-dem-network-plugin" target="_blank" rel="noopener">https://www.smarthomeng.de/geozonen-basierte-services-mit-der-egigeozone-app-und-dem-network-plugin</a> wurde beschrieben, wie man Positionsdaten in SmartHomeNG übertragen kann. In <a href="https://www.smarthomeng.de/das-traffic-plugin-am-beispiel-eines-staualarms" target="_blank" rel="noopener">https://www.smarthomeng.de/das-traffic-plugin-am-beispiel-eines-staualarms</a> wurde beschrieben, wie das Traffic Plugin (Google Directions) in SmartHomeNG eingebunden werden kann. Items aus beiden Artikeln werden für das Plugin benötigt!</p>
<p><strong>Tipp:</strong> Am Ende des Artikels findet sich noch eine einfachere Version, die nur die aktuelle Position benötigt (und anzeigt)!</p>
<h1>Items</h1>
<p>Basis bilden ein Teil bzw. eine Erweiterung der Items aus dem EgiGeoZone und den Traffic Plugin Artikel. Bitte auf diese Artikel referenzieren, wie die Items belegt werden können.</p>
<pre><code class="language-yaml">
%YAML 1.1
---
location:

	home:
		lat:
			type: str
			value: 48.263530
			cache: 'yes'

		lon:
			type: str
			value: 11.443952
			cache: 'yes'
	
	work:

		lat:
			type: str
			value: 48.143158
			cache: 'yes'

		lon:
			type: str
			value: 11.547755
			cache: 'yes'
			
	lat:
		type: str
		cache: 'yes'

	lon:
		type: str
		cache: 'yes'

travel_info:
       
        calculate_way_home:
                type: bool
                cache: 'yes'

        calculate_way_work:
                type: bool
                cache: 'yes'

        travel_summary:
                type: str
 
</code></pre>
<p>Neben den jeweiligen GPS Koordinaten der aktuellen Position (<code>location.lat, location.lon</code>), der Home-Koordinate (<code>location.home.lat, location.home.lon</code>) und der Koordinate der Arbeitsstelle (<code>location.work.lat, location.work.lon</code>), sind hier auch zwei boolsche Items, die angeben, ob jeweils der Weg nach Hause oder in die Arbeit angezeigt werden soll (<code>travel_info.calculate_way_home</code> und <code>travel_info.calculate_way_work</code>) Für die Anzeige darf jeweils nur eines dieser Items <code>True</code> sein. Näheres ist in <a href="https://www.smarthomeng.de/das-traffic-plugin-am-beispiel-eines-staualarms" target="_blank" rel="noopener">https://www.smarthomeng.de/das-traffic-plugin-am-beispiel-eines-staualarms</a> beschrieben.</p>
<p><strong>Tipp:</strong> Man kann natürlich auch die &#8222;aktuelle&#8220; Koordinate <code>location.lat, location.lon</code> fest im Item hinterlegen, ohne dieses via EgiGeoZone zu bedaten. Diese Koordinate ist, so lange keine Route angezeigt wird, das Zentrum der Karte!</p>
<h1>Google Maps Widget</h1>
<p>Für das Widget sind im Ordner <code>dropins/widgets</code> zwei Dateien zu erstellen. Im Javascript File wird dabei die Karte initialisiert und falls angegeben, die Verkehrsanzeige eingeschaltet. Daneben wird ein Marker auf die Home Koordinate und die aktuelle Position gesetzt. Letzterer ist mit einem Foto hinterlegt.</p>
<p>Im Update-Block werden die jeweiligen Positionen aktualisiert und die aktuelle Route gesetzt (falls eine Route aktuell angezeigt werden soll).</p>
<p>Im JS-File muss bedacht werden, dass der im Rahmen des Traffic Plugins eingerichtete API Key für die Google Directions API angegeben werden muss (<code>&lt;apikey&gt</code>)! Zudem kann als Title noch der jeweilige Name <code>&lt;Mein Name&gt;</code> gesetzt werden.</p>
<p><strong><code>dropins/widgets/gmaps.js</code>:</strong></p>
<pre><code class="language-javascript">
// dropins/widgets/gmaps.js
$.widget("sv.gmaps_map", $.sv.widget, {
    initSelector: 'div[data-widget="gmaps_map"]',

    options: {
        'traffic': null,
    },

    _create: function () {
        this._super();
        this._create_map();
    },

    _create_map: function () {
        try {
            this.map = new google.maps.Map(this.element[0], {
                zoom: 11,
                disableDefaultUI: false,
                clickableIcons: false,
                mapTypeControl: false,
                streetViewControl: false
            });
        }
        catch(e) {
            if(e.name == "ReferenceError") { // google maps script not loaded yet
                var that = this;
                // google maps script is already loading in another widget
                if(window.google_maps_loading) { 
                    window.setTimeout(function() { that._create_map() }, 100)
                    return;
                }
                // google maps script is not loading
                window.google_maps_loading = true;
                $.ajax({
                    url: 'https://maps.googleapis.com/maps/api/js?key=&lt;apikey&gt;',
                    dataType: "script",
                    complete: function() { window.google_maps_loading = false; that._create_map() }
                });
                return;
            }
            else  // other exceptions should be trown
                throw e;
        }

        this.directionsService = new google.maps.DirectionsService;
        this.directionsDisplay = new google.maps.DirectionsRenderer();
        this.directionsDisplay.setMap(this.map);

        if (this.options['traffic']) {
            var trafficLayer = new google.maps.TrafficLayer();
            trafficLayer.setMap(this.map);
        }

        var pinColorGreen = "20d24a";
        var pinImageGreen = new google.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColorGreen,
            new google.maps.Size(21, 34),
            new google.maps.Point(0,0),
            new google.maps.Point(10, 34));
        var pinImageMyself = new google.maps.MarkerImage("/smartVISU/pics/phone/myself.jpg" ,
            null, null, null, new google.maps.Size(40, 40));
        var pinShadow = new google.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_shadow",
            new google.maps.Size(40, 37),
            new google.maps.Point(0, 0),
            new google.maps.Point(12, 35));

        this.marker_home = new google.maps.Marker({
            map: this.map,
            icon: pinImageGreen,
            shadow: pinShadow,
            title:' Home '
        });

        this.marker_myself = new google.maps.Marker({
            map: this.map,
            icon: pinImageMyself,
            title:' &lt;Mein Name&gt;',
            zIndex:99999999
        });
    },

    _update: function(response) {

        if(!this.map) {
            var that = this;
            window.setTimeout(function() { that._update(response) }, 100)
            return;
        }

        this.map.setCenter({lat: response[0], lng: response[1]});
        var pos = new google.maps.LatLng(parseFloat(response[0]),parseFloat(response[1]));
        this.marker_myself.setPosition(pos);

        var pos_home = new google.maps.LatLng(parseFloat(response[2]),parseFloat(response[3]));
        this.marker_home.setPosition(pos_home);
        if (response[6] && response[7] && (response[4] == 1 || response[5] == 1)) {
            if (response[4] == 1) {
                var destination = { lat: parseFloat(response[2]), lng: parseFloat(response[3]) };
            } else if (response[5] == 1) {
                var destination = { lat: parseFloat(response[6]), lng: parseFloat(response[7]) };
            };
            var directionsDisplay = this.directionsDisplay;
            this.directionsDisplay.setMap(this.map);
            this.directionsService.route({
                origin: pos,
                //provideRouteAlternatives: true,
                destination: destination,
                travelMode: google.maps.TravelMode.DRIVING
            }, function(result, status) {
                if (status === google.maps.DirectionsStatus.OK) {
                    directionsDisplay.setDirections(result);
                } else {
                    console.log('Directions request failed due to ' + status);
                }
            });
        } else {
            if(this.directionsDisplay != null) {
                this.directionsDisplay.setMap(null);
            }
        }
    }
})
</code></pre>
<p>&nbsp;</p>
<p>und <strong><code>dropins/widgets/gmaps.html</code>:</strong></p>
<pre><code class="language-twig">
// dropins/widgets/gmaps.html - API Key für Google Directions angeben!
/**
 * Displays a google maps map with current, home and work position, as well as routing
 *
 * @param unique id for this widget
 * @param a gad/item for latitude
 * @param a gad/item for longitude
 * @param a gad/item for home latitude
 * @param a gad/item for home longitude
 * @param a gad/item for bool flag, if route to home shall be calculated
 * @param a gad/item for bool flag, if route to work shall be calculated
 * @param a gad/item for work latitude
 * @param a gad/item for work longitude
 * @param traffic information on (=1) or off (=0)
 */
{% macro map(id, gad_lat, gad_lon, gad_home_lat, gad_home_lon, gad_calculate_way_home, gad_calculate_way_work, gad_work_lat, gad_work_lon, traffic) %}
    &lt;div id="{{ uid(page, id) }}" style="width: 100%; height: 400px;" data-traffic="{{ traffic }}" data-widget="gmaps_map" data-item="{{ gad_lat }}, {{ gad_lon}}, {{ gad_home_lat }}, {{ gad_home_lon }}, {{ gad_calculate_way_home }}, {{ gad_calculate_way_work }}, {{ gad_work_lat }}, {{ gad_work_lon }}"&gt;&lt;/div&gt;
{% endmacro %}
</code></pre>
<p>Weiterhin wird ein Bild, welches unter <code>/smartVISU/pics/phone/myself.jpg</code> abgelegt ist, verwendet! Damit das Widget ordentlich funktioniert, sollte dieses Bild auch dort existieren.</p>
<h1>Einbindung in smartVISU 2.9</h1>
<p>Da die Widgets mit smartVISU 2.9 automatisch geladen werden, muss nun nur noch das Widget auf der entsprechenden Seite eingebunden werden:</p>
<pre><code class="language-twig">
&lt;div class="block" style="width: 100%;"&gt;
  &lt;div class="set-2" data-role="collapsible-set" data-theme="c" data-content-theme="a" data-mini="true"&gt;
    &lt;div data-role="collapsible" data-collapsed="false"&gt;
      &lt;h3&gt;Verkehr:
      {{ basic.symbol('travel_info.calculate_way_home', 'travel_info.calculate_way_home', 'Nach Hause via ', '', '1') }}
      {{ basic.symbol('travel_info.calculate_way_work', 'travel_info.calculate_way_work', 'Zur Arbeit via ', '', '1') }}
      {{ basic.print('travel_info.travel_summary', 'travel_info.travel_summary', 'text') }}
      &lt;/h3&gt;
      {{ gmaps.map('trafficmap', 'location.lat', 'location.lon', 'location.home.lat', 'location.home.lon', 'travel_info.calculate_way_home', 'travel_info.calculate_way_work', 'location.work.lat', 'location.work.lon', '1') }}
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;  
</code></pre>
<p>Das Ergebnis mit aktivem Arbeitsweg, sieht dann wie folgt aus:</p>
<p><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2018/08/kartenansicht.jpg" alt="" width="830" height="472" class="alignnone size-full wp-image-2016" srcset="https://www.smarthomeng.de/wp-content/uploads/2018/08/kartenansicht.jpg 830w, https://www.smarthomeng.de/wp-content/uploads/2018/08/kartenansicht-300x171.jpg 300w, https://www.smarthomeng.de/wp-content/uploads/2018/08/kartenansicht-768x437.jpg 768w" sizes="(max-width: 830px) 100vw, 830px" /></p>
<h1>Einfachere Version:</h1>
<p>Hier noch eine einfachere Version des Widgets, nur mit Koordinaten der aktuellen Position:</p>
<pre><code class="language-twig">
{{ gmaps.simple_map('trafficmap_simple', 'location.lat', 'location.lon') }}
</code></pre>
<pre><code class="language-twig">
/**
 * Displays a simple google maps map
 *
 * @param unique id for this widget
 * @param a gad/item for latitude
 * @param a gad/item for longitude
 */
{% macro simple_map(id, gad_lat, gad_lon) %}
&lt;div id="{{ uid(page, id) }}" style="width: 100%; height: 400px;" data-widget="gmaps_simplemap" data-item="{{ gad_lat }}, {{ gad_lon}}"&gt;&lt;/div&gt;
{% endmacro %}
</code></pre>
<pre><code class="language-javascript">
$.widget("sv.gmaps_simplemap", $.sv.widget, {
    initSelector: 'div[data-widget="gmaps_simplemap"]',

    _create: function () {
        this._super();
        this._create_map();
    },

    _create_map: function () {
        try {
            this.map = new google.maps.Map(this.element[0], {
                zoom: 11,
                disableDefaultUI: false,
                clickableIcons: false,
                mapTypeControl: false,
                streetViewControl: false
            });
        }
        catch (e) {
            if (e.name == "ReferenceError") { // google maps script not loaded yet
                var that = this;
                // google maps script is already loading in another widget
                if (window.google_maps_loading) {
                    window.setTimeout(function () {
                        that._create_map()
                    }, 100)
                    return;
                }
                // google maps script is not loading
                window.google_maps_loading = true;
                $.ajax({
                    url: 'https://maps.googleapis.com/maps/api/js?key=&lt;apikey&gt;',
                    dataType: "script",
                    complete: function () {
                        window.google_maps_loading = false;
                        that._create_map()
                    }
                });
                return;
            }
            else  // other exceptions should be thrown
                throw e;
        }

        var pinColorGreen = "20d24a";
        var pinImageGreen = new google.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColorGreen,
            new google.maps.Size(21, 34),
            new google.maps.Point(0,0),
            new google.maps.Point(10, 34));
        this.marker_myself = new google.maps.Marker({
            map: this.map,
            icon: pinImageGreen,
            title:' &lt;Mein Name&gt;',
            zIndex:99999999
        });
    },

    _update: function(response) {
        if(!this.map) {
            var that = this;
            window.setTimeout(function() { that._update(response) }, 100)
            return;
        }

        this.map.setCenter({lat: response[0], lng: response[1]});
        var pos = new google.maps.LatLng(parseFloat(response[0]),parseFloat(response[1]));
        this.marker_myself.setPosition(pos);
    }
});</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://www.smarthomeng.de/google-maps-widget-fuer-smartvisu-2-9/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>E-Paper Wetterstation mit Daten aus SmartHomeNG</title>
		<link>https://www.smarthomeng.de/epaper-wetterstation-mit-daten-aus-smarthomeng</link>
					<comments>https://www.smarthomeng.de/epaper-wetterstation-mit-daten-aus-smarthomeng#comments</comments>
		
		<dc:creator><![CDATA[Marc René Frieß]]></dc:creator>
		<pubDate>Sun, 22 Jul 2018 09:28:47 +0000</pubDate>
				<category><![CDATA[Beispiel-Implementierungen]]></category>
		<category><![CDATA[Plugins]]></category>
		<category><![CDATA[Arduino IDE]]></category>
		<category><![CDATA[EPaper]]></category>
		<category><![CDATA[ESP32]]></category>
		<category><![CDATA[Waveshare]]></category>
		<category><![CDATA[Wetterstation]]></category>
		<guid isPermaLink="false">https://www.smarthomeng.de/?p=1929</guid>

					<description><![CDATA[E-Paper Displays, wie sie etwa im Amazon Kindle zum Einsatz kommen, stellen eine hervoragende und energiesparende Möglichkeit dar, Daten von SmartHomeNG zu visualisieren. Im folgenden Artikel soll als Beispiel eine Wetterstation auf Basis eines ESP32 und eines Waveshare 4.2 Zoll E-Paper Display mit 400 x 300 Pixel Auflösung erstellt werden.<a class="moretag" href="https://www.smarthomeng.de/epaper-wetterstation-mit-daten-aus-smarthomeng"> Weiterlesen&#8230;</a>]]></description>
										<content:encoded><![CDATA[<p>E-Paper Displays, wie sie etwa im Amazon Kindle zum Einsatz kommen, stellen eine hervoragende und energiesparende Möglichkeit dar, Daten von SmartHomeNG zu visualisieren.</p>
<p>Im folgenden Artikel soll als Beispiel eine Wetterstation auf Basis eines ESP32 und eines Waveshare 4.2 Zoll E-Paper Display mit 400 x 300 Pixel Auflösung erstellt werden.</p>
<p>Das Display kann bspw. via <a href="https://www.amazon.de/Module-Resolution-Electronic-Controller-Communicating/dp/B074NR1SW2" target="_blank" rel="noopener">Amazon</a> bezogen werden. Der verwendete ESP32 kann <a href="https://www.amazon.de/Espressif-ESP32-DEVKITC-Genuine-ESP-WROOM-32-Marked/dp/B07DZP27TH/ref=sr_1_5?s=computers&amp;ie=UTF8&amp;qid=1532247493&amp;sr=1-5&amp;keywords=espressif+esp32" target="_blank" rel="noopener">hier</a> gefunden werden. Als Bibliothek zum Rendern des Bildes kommt <a href="https://github.com/ZinggJM/GxEPD2" target="_blank" rel="noopener">GxEPD2</a> zum Einsatz.  Vorab muss allerdings gesagt werden, dass die Fonts der Bibliothek nur Standard ASCII Zeichen könne, also weder ein Grad Zeichen (°) noch deutsche Sonderzeichen. Als Icons wurden Teile aus <a href="http://adamwhitcroft.com/climacons/" target="_blank" rel="noopener">http://adamwhitcroft.com/climacons/</a> und den SmartVISU Icons verwendet, die via <a href="http://www.digole.com/tools/PicturetoC_Hex_converter.php" target="_blank" rel="noopener">http://www.digole.com/tools/PicturetoC_Hex_converter.php</a> von einem PNG in C-Arrays konvertiert wurden.</p>
<p>Der Artikel erhebt keinen Anspruch auf vollständige Korrektheit und Schönheit des C Codes und dient lediglich als Proof of Concept. Auch sind die Icons derzeit noch nicht vollständig. Der Artikel wird dazu sukzessive aktualisiert.</p>
<p>Für die Items wurden sowohl Systemdaten aus SHNG direkt, dem KNX Plugin, dem ETA PU Plugin, dem SMA EM, als auch des DarkSky Plugins verwendet, die via Webservices Plugin (siehe Tutorial <a href="https://www.smarthomeng.de/das-smarthomeng-webservices-plugin" target="_blank" rel="noopener">https://www.smarthomeng.de/das-smarthomeng-webservices-plugin</a>) verfügbar gemacht wurden. Teilweise wurden die Rohdaten der Plugins über Child-Items noch formatiert oder in Strings (bspw. beim UV Wert) umgerechnet.</p>
<h1>Verkabelung</h1>
<p>Dem Display ist ein Kabel beigelegt, das wie folgt an den ESP32 angeschlossen wird:</p>
<p><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2018/07/Kabel_Waveshare.jpg" alt="" width="733" height="638" class="alignnone size-full wp-image-1935" srcset="https://www.smarthomeng.de/wp-content/uploads/2018/07/Kabel_Waveshare.jpg 733w, https://www.smarthomeng.de/wp-content/uploads/2018/07/Kabel_Waveshare-300x261.jpg 300w" sizes="(max-width: 733px) 100vw, 733px" /></p>
<table border="1" style="border-collapse: collapse; width: 100%;">
<tbody>
<tr style="height: 29px;">
<td style="width: 50%; height: 29px;"><strong>Farbe</strong></td>
<td style="width: 50%; height: 29px;"><strong>Anschluß</strong></td>
</tr>
<tr style="height: 29px;">
<td style="width: 50%; height: 29px;"><strong><span style="color: #ff0000;">rot</span></strong></td>
<td style="width: 50%; height: 29px;">3v3</td>
</tr>
<tr style="height: 29px;">
<td style="width: 50%; height: 29px;"><strong>schwarz</strong></td>
<td style="width: 50%; height: 29px;">GND</td>
</tr>
<tr style="height: 29px;">
<td style="width: 50%; height: 29px;"><strong><span style="color: #0000ff;">blau</span></strong></td>
<td style="width: 50%; height: 29px;">IO23</td>
</tr>
<tr style="height: 29px;">
<td style="width: 50%; height: 29px;"><span style="color: #e3eb15;"><strong>gelb</strong></span></td>
<td style="width: 50%; height: 29px;">IO18</td>
</tr>
<tr style="height: 29px;">
<td style="width: 50%; height: 29px;"><span style="color: #ff9900;"><strong>orange</strong></span></td>
<td style="width: 50%; height: 29px;">IO17</td>
</tr>
<tr style="height: 29px;">
<td style="width: 50%; height: 29px;"><span style="color: #339966;"><strong>grün</strong></span></td>
<td style="width: 50%; height: 29px;">IO16</td>
</tr>
<tr style="height: 29px;">
<td style="width: 50%; height: 29px;"><span style="color: #d6d6d6;"><strong>weiß</strong></span></td>
<td style="width: 50%; height: 29px;">IO5</td>
</tr>
<tr style="height: 29px;">
<td style="width: 50%; height: 29px;"><span style="color: #800080;"><strong>lila</strong></span></td>
<td style="width: 50%; height: 29px;">IO19</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<h1>Items (via Webservices Plugin)</h1>
<p>Folgende Items werden als Itemset vom Webservices Plugin als JSON bereitgestellt:</p>
<p><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2018/07/epaper_json.jpg" alt="" width="685" height="786" class="alignnone size-full wp-image-1936" srcset="https://www.smarthomeng.de/wp-content/uploads/2018/07/epaper_json.jpg 685w, https://www.smarthomeng.de/wp-content/uploads/2018/07/epaper_json-261x300.jpg 261w" sizes="(max-width: 685px) 100vw, 685px" /></p>
<h1>Code</h1>
<p>Das Programm wurde mit der Arduino IDE geschrieben und auf den ESP32 übertragen. Wie oben bereits erwähnt, müssen dazu die notwendigen Bibliotheken vorhanden sein. Wie der ESP32 in der Arduino IDE eingerichtet werden kann, kann <a href="https://www.smarthomeng.de/entfernungsmessung-auf-basis-eines-esp32-und-smarthomeng" target="_blank" rel="noopener">https://www.smarthomeng.de/entfernungsmessung-auf-basis-eines-esp32-und-smarthomeng</a> entnommen werden.</p>
<p>In der Methode <code>void setup(void)</code> werden zunächst das Display und die WLAN Verbindung initialisiert.<br />
In <code>void loop()</code> findet dann der Datenzugriff und das Rendering statt. Nach erfolgreicher Verbindung zu SmartHomeNG wird zuerst der HTTP Header herausgefiltert. Sobald in der Response eine geschweifte Klammer erkannt wird, werden die JSON Daten in ein Char Array (<code>inData</code>) eingelesen. Ist dies vollendet, wird die Verbindung getrennt und der JSON Datensatz geparsed (<code>JsonObject&amp; root = jsonBuffer.parseObject(inData);</code>). Bei Erfolg, werden die Daten mit <code>printJsonData(JsonObject&amp; root)</code> auf dem Display ausgegeben. Dafür werden sie aus den JSON Daten extrahiert und ggf. noch formatiert.</p>
<p>Im &#8222;do &#8230; while&#8220; Block findet die konkrete Ausgabe statt. <code>display.setCursor</code> setzt den Textcursor an bestimmte Positionen. Danach wird Text ausgegeben. Die Methode <code>void display_icon(int x, int y, String icon_name)</code> gibt an entsprechenden Positionen das Icon aus.<br />
Die Methoden <code>void dashedRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)</code> und <code>void drawDashedHLine(int16_t x, int16_t y, int16_t w, uint16_t color)</code> wurden von <a href="https://forum.arduino.cc/index.php?topic=487007.225" target="_blank" rel="noopener">https://forum.arduino.cc/index.php?topic=487007.225</a> übernommen und zeichnen Linien oder Rechtecke.</p>
<pre><code class="language-c">
#include &lt;WiFi.h&gt;
#include &lt;WiFiServer.h&gt;
#include &lt;WiFiClient.h&gt;
#include &lt;WiFiUdp.h&gt;
#include &lt;JsonParser.h&gt;
#include &lt;ArduinoJson.h&gt;
#include &lt;GxEPD2_BW.h&gt;
#include &lt;GxEPD2_3C.h&gt;
#define EPD_CS 17 //SS
#include &lt;Fonts/FreeMonoBold9pt7b.h&gt;
#include &lt;Fonts/FreeMonoBold12pt7b.h&gt;
#include &lt;Fonts/FreeMonoBold18pt7b.h&gt;

// Define each of the *icons for display
const unsigned char gate_open[] PROGMEM = {
0x00,0x00,0x1f,0xff,0xff,0xe0,0x00,0x00,0x00,0x3f,0xff,0xff,0xf0,0x00,0x00,0x00,0x70,0x00,0x00,0x38,0x00,0x00,0x00,0x70,0x00,0x00,0x38,0x00
,0x00,0x00,0x60,0x00,0x00,0x18,0x00,0x00,0x00,0x60,0x00,0x00,0x18,0x00,0x00,0x00,0xe0,0x00,0x00,0x1c,0x00,0x00,0x00,0xe0,0x00,0x00,0x1c,0x00
,0x00,0x00,0xe0,0x00,0x00,0x1c,0x00,0x00,0x00,0xff,0xff,0xff,0xfc,0x00,0x00,0x03,0xff,0xff,0xff,0xfe,0x00,0x00,0x03,0x03,0xc0,0x0f,0x03,0x00
,0x00,0x02,0x03,0xff,0xff,0x01,0x00,0x00,0x02,0x03,0xff,0xff,0x01,0x00,0x00,0x03,0x07,0x80,0x07,0x83,0x00,0x00,0x03,0xff,0xff,0xff,0xff,0x00
,0x00,0x03,0xff,0xff,0xff,0xff,0x00,0x00,0x03,0xff,0xff,0xff,0xff,0x00,0x00,0x01,0xff,0xff,0xff,0xfe,0x00,0x00,0x01,0xf8,0x00,0x00,0x7c,0x00
,0x00,0x01,0xf8,0x00,0x00,0x7c,0x00,0x00,0x01,0xf8,0x00,0x00,0x7c,0x00,0x00,0x00,0xf0,0x00,0x00,0x7c,0x00,0x1c,0x00,0x00,0x00,0x00,0x00,0x00
,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xf8,0x00,0x00,0x00,0x00,0x80,0xff,0xf8,0x00,0x00,0x00,0x00,0xc0
,0xff,0x38,0x00,0x00,0x00,0x00,0xc0,0xdf,0x18,0x00,0x00,0x00,0x00,0xc0,0xdf,0x18,0x00,0x00,0x00,0x00,0xc0,0xdf,0x18,0x00,0x00,0x00,0x00,0xc0
,0xdf,0x18,0x00,0x00,0x00,0x00,0xc0,0xdf,0x18,0x00,0x00,0x00,0x00,0xc0,0xdf,0x18,0x00,0x00,0x00,0x00,0xc0,0xdf,0x18,0x00,0x00,0x00,0x00,0xc0
,0xdf,0x18,0x00,0x00,0x00,0x00,0xc0,0xdf,0x18,0x00,0x00,0x00,0x00,0xc0,0xdf,0x18,0x00,0x00,0x00,0x00,0xc0,0xdf,0x18,0x00,0x00,0x00,0x00,0xc0
,0xdf,0x18,0x00,0x00,0x00,0x00,0xc0,0xdf,0x18,0x00,0x00,0x00,0x00,0xc0,0xdf,0x18,0x00,0x00,0x00,0x00,0xc0,0xdf,0x18,0x00,0x00,0x00,0x00,0xc0
,0xdf,0x18,0x00,0x00,0x00,0x00,0xc0,0xdf,0x18,0x00,0x00,0x00,0x00,0xc0,0xff,0xf8,0x00,0x00,0x00,0x00,0xc0,0xff,0xf8,0x00,0x00,0x00,0x00,0xc0
,0xff,0xf8,0x00,0x00,0x00,0x00,0xc0,0x1f,0xff,0xff,0xff,0xff,0xff,0xc0
};

const unsigned char gate_closed[] PROGMEM = {
0x00,0x00,0xff,0xff,0xff,0x00,0x00,0x01,0xff,0xff,0xff,0x80,0x00,0x03,0x80,0x00,0x01,0xc0,0x00,0x03,0x80,0x00,0x01,0xc0
,0x00,0x03,0x00,0x00,0x00,0xc0,0x00,0x03,0x00,0x00,0x00,0xc0,0x00,0x07,0x00,0x00,0x00,0xe0,0x00,0x07,0x00,0x00,0x00,0xe0
,0x00,0x07,0x00,0x00,0x00,0xe0,0x00,0x07,0xff,0xff,0xff,0xe0,0x00,0x0f,0xff,0xff,0xff,0xf0,0x00,0x18,0x1e,0x00,0x78,0x18
,0x00,0x10,0x1f,0xff,0xf8,0x08,0x00,0x10,0x1f,0xff,0xf8,0x08,0x00,0x18,0x3c,0x00,0x3c,0x18,0x00,0x1f,0xff,0xff,0xff,0xf8
,0x00,0x1f,0xff,0xff,0xff,0xf8,0x00,0x1f,0xff,0xff,0xff,0xf8,0x00,0x0f,0xff,0xff,0xff,0xf0,0x00,0x0f,0xc0,0x00,0x03,0xe0
,0x00,0x0f,0xc0,0x00,0x03,0xe0,0x00,0x0f,0xc0,0x00,0x03,0xe0,0x00,0x07,0x80,0x00,0x03,0xe0,0x60,0x00,0x00,0x00,0x00,0x00
,0xe0,0x00,0x00,0x00,0x00,0x00,0xe0,0x00,0x00,0x00,0x00,0x00,0xe7,0xff,0xff,0xff,0xff,0xf4,0xef,0xff,0xff,0xff,0xff,0xf6
,0xee,0x71,0xce,0x71,0xce,0x76,0xec,0x31,0x8e,0x31,0x8e,0x36,0xec,0x31,0x8e,0x31,0x8e,0x36,0xec,0x31,0x8e,0x31,0x8e,0x36
,0xec,0x31,0x8e,0x31,0x8e,0x36,0xec,0x31,0x8e,0x31,0x8e,0x36,0xec,0x31,0x8e,0x31,0x8e,0x36,0xec,0x31,0x8e,0x31,0x8e,0x36
,0xec,0x31,0x8e,0x31,0x8e,0x36,0xec,0x31,0x8e,0x31,0x8e,0x36,0xec,0x31,0x8e,0x31,0x8e,0x36,0xec,0x31,0x8e,0x31,0x8e,0x36
,0xec,0x31,0x8e,0x31,0x8e,0x36,0xec,0x31,0x8e,0x31,0x8e,0x36,0xec,0x31,0x8e,0x31,0x8e,0x36,0xec,0x31,0x8e,0x31,0x8e,0x36
,0xec,0x31,0x8e,0x31,0x8e,0x36,0xec,0x31,0x8e,0x31,0x8e,0x36,0xef,0xff,0xff,0xff,0xff,0xf6,0xef,0xff,0xff,0xff,0xff,0xf6
,0xe7,0xff,0xff,0xff,0xff,0xf6,0x7f,0xff,0xff,0xff,0xff,0xfe
};

const unsigned char light_off[] PROGMEM = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xfc,0x00,0x00,0x00,0x00,0x00,0x3f,0xff,0x00,0x00,0x00
,0x00,0x00,0x7e,0x1f,0x80,0x00,0x00,0x00,0x00,0xf0,0x03,0xc0,0x00,0x00,0x00,0x01,0xe0,0x01,0xe0,0x00,0x00,0x00,0x03,0xc0,0x00,0xf0,0x00,0x00
,0x00,0x03,0x80,0x00,0x70,0x00,0x00,0x00,0x07,0x00,0x00,0x38,0x00,0x00,0x00,0x07,0x00,0x00,0x38,0x00,0x00,0x00,0x07,0x00,0x00,0x38,0x00,0x00
,0x00,0x07,0x00,0x00,0x38,0x00,0x00,0x00,0x07,0x00,0x00,0x38,0x00,0x00,0x00,0x07,0x00,0x00,0x38,0x00,0x00,0x00,0x07,0x00,0x00,0x38,0x00,0x00
,0x00,0x07,0x00,0x00,0x38,0x00,0x00,0x00,0x07,0x00,0x00,0x38,0x00,0x00,0x00,0x03,0x80,0x00,0x70,0x00,0x00,0x00,0x03,0x80,0x00,0x70,0x00,0x00
,0x00,0x01,0xc0,0x00,0xe0,0x00,0x00,0x00,0x01,0xc0,0x00,0xe0,0x00,0x00,0x00,0x00,0xe0,0x01,0xc0,0x00,0x00,0x00,0x00,0xf0,0x03,0xc0,0x00,0x00
,0x00,0x00,0x70,0x03,0x80,0x00,0x00,0x00,0x00,0x38,0x07,0x00,0x00,0x00,0x00,0x00,0x38,0x07,0x00,0x00,0x00,0x00,0x00,0x38,0x07,0x00,0x00,0x00
,0x00,0x00,0x1c,0x0e,0x00,0x00,0x00,0x00,0x00,0x1c,0x0e,0x00,0x00,0x00,0x00,0x00,0x08,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x0f,0xfc,0x00,0x00,0x00,0x00,0x00,0x0f,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x0f,0xfc,0x00,0x00,0x00,0x00,0x00,0x0f,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xf0,0x00,0x00,0x00
,0x00,0x00,0x03,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00
};

const unsigned char light_on[] PROGMEM = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x00,0x00,0x00,0x00,0x03,0x00,0xe0,0x30,0x00,0x00
,0x00,0x03,0x80,0xe0,0x70,0x00,0x00,0x00,0x03,0x80,0xe0,0x70,0x00,0x00,0x00,0x01,0xc0,0x00,0xe0,0x00,0x00,0x00,0x01,0xc0,0x00,0xe0,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x20,0x00,0x03,0xc0,0x0f,0xfc,0x00,0xf0,0x00,0x03,0xe0,0x3f,0xff,0x01,0xf0,0x00
,0x01,0xf0,0xff,0x3f,0xc3,0xe0,0x00,0x00,0x61,0xf0,0x03,0xe1,0x80,0x00,0x00,0x01,0xe0,0x01,0xe0,0x00,0x00,0x00,0x03,0xc0,0x00,0xf0,0x00,0x00
,0x00,0x03,0x80,0x00,0x70,0x00,0x00,0x00,0x07,0x00,0x00,0x38,0x00,0x00,0x00,0x07,0x00,0x00,0x38,0x00,0x00,0x08,0x87,0x00,0x00,0x38,0x44,0x00
,0x1f,0x87,0x00,0x00,0x38,0x7e,0x00,0x1f,0x87,0x00,0x00,0x38,0x7e,0x00,0x00,0x07,0x00,0x00,0x38,0x00,0x00,0x00,0x07,0x00,0x00,0x38,0x00,0x00
,0x00,0x07,0x00,0x00,0x38,0x00,0x00,0x00,0x07,0x00,0x00,0x38,0x00,0x00,0x00,0x03,0x80,0x00,0x70,0x00,0x00,0x00,0x03,0x80,0x00,0x70,0x00,0x00
,0x00,0xe1,0xc0,0x00,0xe1,0x80,0x00,0x03,0xe1,0xe0,0x01,0xe1,0xf0,0x00,0x07,0xe0,0xe0,0x01,0xc1,0xf8,0x00,0x03,0x80,0xf0,0x03,0xc0,0x70,0x00
,0x00,0x00,0x70,0x03,0x80,0x00,0x00,0x00,0x00,0x78,0x07,0x80,0x00,0x00,0x00,0x00,0x38,0x07,0x00,0x00,0x00,0x00,0x00,0x38,0x07,0x00,0x00,0x00
,0x00,0x00,0x1c,0x0e,0x00,0x00,0x00,0x00,0x00,0x1c,0x0e,0x00,0x00,0x00,0x00,0x00,0x18,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x0f,0xfc,0x00,0x00,0x00,0x00,0x00,0x0f,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x0f,0xfc,0x00,0x00,0x00,0x00,0x00,0x0f,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xf0,0x00,0x00,0x00
,0x00,0x00,0x03,0xf0,0x00,0x00,0x00,0x00,0x00,0x01,0xc0,0x00,0x00,0x00
};

const unsigned char moon_0_8[] PROGMEM = {
0x00,0x3c,0x00,0x01,0xff,0x80,0x07,0xff,0xe0,0x0f,0xff,0xf0,0x1f,0xff,0xf8,0x3f,0xff,0xfc,0x3f,0xff,0xfc,0x7f,0xff,0xfe
,0x7f,0xff,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,0xff,0xfe
,0x7f,0xff,0xfe,0x3f,0xff,0xfc,0x3f,0xff,0xfc,0x1f,0xff,0xf8,0x0f,0xff,0xf0,0x07,0xff,0xe0,0x01,0xff,0x80,0x00,0x3c,0x00
};

const unsigned char moon_1[] PROGMEM = {
0x00,0x3c,0x00,0x01,0xff,0x80,0x07,0xff,0xe0,0x0f,0xff,0xf0,0x1f,0xe1,0xf8,0x3f,0xf0,0x7c,0x3f,0xfc,0x3c,0x7f,0xfc,0x1e
,0x7f,0xfe,0x1e,0xff,0xfe,0x0f,0xff,0xff,0x0f,0xff,0xff,0x0f,0xff,0xff,0x0f,0xff,0xff,0x0f,0xff,0xfe,0x0f,0x7f,0xfe,0x1e
,0x7f,0xfc,0x1e,0x3f,0xfc,0x3c,0x3f,0xf0,0x7c,0x1f,0xe1,0xf8,0x0f,0xff,0xf0,0x07,0xff,0xe0,0x01,0xff,0x80,0x00,0x3c,0x00
};

const unsigned char moon_2[] PROGMEM = {
0x00,0x3c,0x00,0x01,0xff,0x80,0x07,0xff,0xe0,0x0f,0xff,0xf0,0x1f,0xe1,0xf8,0x3f,0xf0,0x7c,0x3f,0xfc,0x3c,0x7f,0xfc,0x1e
,0x7f,0xfe,0x1e,0xff,0xfe,0x0f,0xff,0xff,0x0f,0xff,0xff,0x0f,0xff,0xff,0x0f,0xff,0xff,0x0f,0xff,0xfe,0x0f,0x7f,0xfe,0x1e
,0x7f,0xfc,0x1e,0x3f,0xfc,0x3c,0x3f,0xf0,0x7c,0x1f,0xe1,0xf8,0x0f,0xff,0xf0,0x07,0xff,0xe0,0x01,0xff,0x80,0x00,0x3c,0x00
};

const unsigned char moon_3[] PROGMEM = {
0x00,0x3c,0x00,0x01,0xff,0x80,0x07,0xff,0xe0,0x0f,0xff,0xf0,0x1f,0xf1,0xf8,0x3f,0xf0,0x7c,0x3f,0xf0,0x3c,0x7f,0xf0,0x1e
,0x7f,0xf0,0x1e,0xff,0xf0,0x0f,0xff,0xf0,0x0f,0xff,0xf0,0x0f,0xff,0xf0,0x0f,0xff,0xf0,0x0f,0xff,0xf0,0x0f,0x7f,0xf0,0x1e
,0x7f,0xf0,0x1e,0x3f,0xf0,0x3c,0x3f,0xf0,0x7c,0x1f,0xf1,0xf8,0x0f,0xff,0xf0,0x07,0xff,0xe0,0x01,0xff,0x80,0x00,0x3c,0x00
};

const unsigned char moon_4[] PROGMEM = {
0x00,0x3c,0x00,0x01,0xff,0x80,0x07,0xff,0xe0,0x0f,0xff,0xf0,0x1f,0xc1,0xf8,0x3f,0xc0,0x7c,0x3f,0x80,0x3c,0x7f,0x00,0x1e
,0x7f,0x00,0x1e,0xff,0x00,0x0f,0xff,0x00,0x0f,0xff,0x00,0x0f,0xff,0x00,0x0f,0xff,0x00,0x0f,0xff,0x00,0x0f,0x7f,0x00,0x1e
,0x7f,0x00,0x1e,0x3f,0x80,0x3c,0x3f,0xc0,0x7c,0x1f,0xc1,0xf8,0x0f,0xff,0xf0,0x07,0xff,0xe0,0x01,0xff,0x80,0x00,0x3c,0x00
};

const unsigned char moon_5[] PROGMEM = {
0x00,0x3c,0x00,0x01,0xff,0x80,0x07,0xff,0xe0,0x0f,0xff,0xf0,0x1f,0x81,0xf8,0x3e,0x00,0x7c,0x3c,0x00,0x3c,0x78,0x00,0x1e
,0x78,0x00,0x1e,0xf0,0x00,0x0f,0xf0,0x00,0x0f,0xf0,0x00,0x0f,0xf0,0x00,0x0f,0xf0,0x00,0x0f,0xf0,0x00,0x0f,0x78,0x00,0x1e
,0x78,0x00,0x1e,0x3c,0x00,0x3c,0x3e,0x00,0x7c,0x1f,0x81,0xf8,0x0f,0xff,0xf0,0x07,0xff,0xe0,0x01,0xff,0x80,0x00,0x3c,0x00
};

const unsigned char moon_6[] PROGMEM = {
0x00,0x3c,0x00,0x01,0xff,0x80,0x07,0xff,0xe0,0x0f,0xff,0xf0,0x1f,0x8f,0xf8,0x3e,0x0f,0xfc,0x3c,0x0f,0xfc,0x78,0x0f,0xfe
,0x78,0x0f,0xfe,0xf0,0x0f,0xff,0xf0,0x0f,0xff,0xf0,0x0f,0xff,0xf0,0x0f,0xff,0xf0,0x0f,0xff,0xf0,0x0f,0xff,0x78,0x0f,0xfe
,0x78,0x0f,0xfe,0x3c,0x0f,0xfc,0x3e,0x0f,0xfc,0x1f,0x8f,0xf8,0x0f,0xff,0xf0,0x07,0xff,0xe0,0x01,0xff,0x80,0x00,0x3c,0x00
};

const unsigned char moon_7[] PROGMEM = {
0x00,0x3c,0x00,0x01,0xff,0x80,0x07,0xff,0xe0,0x0f,0xff,0xf0,0x1f,0x87,0xf8,0x3e,0x0f,0xfc,0x3c,0x3f,0xfc,0x78,0x3f,0xfe
,0x78,0x7f,0xfe,0xf0,0x7f,0xff,0xf0,0xff,0xff,0xf0,0xff,0xff,0xf0,0xff,0xff,0xf0,0xff,0xff,0xf0,0x7f,0xff,0x78,0x7f,0xfe
,0x78,0x3f,0xfe,0x3c,0x3f,0xfc,0x3e,0x0f,0xfc,0x1f,0x87,0xf8,0x0f,0xff,0xf0,0x07,0xff,0xe0,0x01,0xff,0x80,0x00,0x3c,0x00
};

const unsigned char moon_8[] PROGMEM = {
0x00,0x3c,0x00,0x01,0xff,0x80,0x07,0xff,0xe0,0x0f,0xff,0xf0,0x1f,0x87,0xf8,0x3e,0x0f,0xfc,0x3c,0x3f,0xfc,0x78,0x3f,0xfe
,0x78,0x7f,0xfe,0xf0,0x7f,0xff,0xf0,0xff,0xff,0xf0,0xff,0xff,0xf0,0xff,0xff,0xf0,0xff,0xff,0xf0,0x7f,0xff,0x78,0x7f,0xfe
,0x78,0x3f,0xfe,0x3c,0x3f,0xfc,0x3e,0x0f,0xfc,0x1f,0x87,0xf8,0x0f,0xff,0xf0,0x07,0xff,0xe0,0x01,0xff,0x80,0x00,0x3c,0x00
};

const unsigned char pressure[] PROGMEM = {
0x00, 0x00, 0x84, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x84, 0x44, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc4, 0x44, 0x44, 0x00, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x4c, 0x88, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x88, 0x88, 0x80, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x40, 0x00, 0x88, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x11, 0x08, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x01, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x30, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xc0, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x30, 0x00
, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x01, 0x80, 0x00
, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x04, 0x18, 0x00
, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x01, 0x00
, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe0, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xc0, 0x00, 0x18, 0x00
, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x80, 0x00, 0x01, 0x80
, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x80, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x00, 0x61, 0x80, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x80, 0x00, 0x03, 0xc0
, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x80
, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00
, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00
, 0x00, 0x08, 0x00, 0x00, 0x03, 0x80, 0x3f, 0x80, 0x00, 0x00, 0x10, 0x00, 0x00, 0x03, 0x80, 0x3f, 0xe0, 0x00
, 0x00, 0x20, 0x00, 0x00, 0x03, 0x80, 0x30, 0xe0, 0x00, 0x3c, 0xc0, 0x00, 0x00, 0x03, 0xfc, 0x30, 0xe7, 0xf0
, 0xff, 0x80, 0x00, 0x00, 0x03, 0xfc, 0x30, 0xe3, 0xf0, 0xe3, 0x80, 0x00, 0x00, 0x03, 0x8e, 0x3b, 0xe0, 0x38
, 0x01, 0x80, 0x00, 0x00, 0x03, 0x8e, 0x3f, 0xc1, 0xf8, 0x01, 0x80, 0x00, 0x00, 0x03, 0x8e, 0x3f, 0x07, 0xf8
, 0x01, 0x80, 0x00, 0x00, 0x03, 0x8e, 0x30, 0x0e, 0x38, 0x01, 0x80, 0x00, 0x00, 0x03, 0x8e, 0x30, 0x0e, 0x38
, 0x03, 0x80, 0x00, 0x00, 0x03, 0x8e, 0x30, 0x07, 0xf8, 0x01, 0x00, 0x00, 0x00, 0x01, 0x8e, 0x30, 0x03, 0xf0
};

const unsigned char sunrise_sunset[] PROGMEM = {
0x00,0x00,0x01,0x80,0x00,0x00,0x00,0x00,0x03,0xc0,0x00,0x00,0x00,0x00,0x03,0xc0,0x00,0x00,0x00,0x00,0x03,0xc0,0x00,0x00
,0x00,0x00,0x03,0xc0,0x00,0x00,0x00,0x00,0x03,0xc0,0x00,0x00,0x00,0x00,0x03,0xc0,0x00,0x00,0x01,0xc0,0x01,0x80,0x03,0x80
,0x01,0xe0,0x00,0x00,0x07,0x80,0x01,0xf0,0x00,0x00,0x0f,0x80,0x00,0xf8,0x00,0x00,0x1f,0x00,0x00,0x78,0x00,0x00,0x1e,0x00
,0x00,0x38,0x03,0xc0,0x1c,0x00,0x00,0x00,0x1f,0xf8,0x00,0x00,0x00,0x00,0x7f,0xfe,0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x00
,0x00,0x01,0xf8,0x1f,0x80,0x00,0x00,0x03,0xe0,0x07,0xc0,0x00,0x00,0x03,0xc0,0x03,0xc0,0x00,0x00,0x07,0x80,0x01,0xe0,0x00
,0x00,0x07,0x80,0x01,0xe0,0x00,0x00,0x0f,0x00,0x00,0xf0,0x00,0x7e,0x0f,0x00,0x00,0xf0,0x7e,0xff,0x0f,0x00,0x00,0xf0,0xff
,0xff,0x0f,0x00,0x00,0xf0,0xff,0x7e,0x0f,0x00,0x00,0xf0,0x7e,0x00,0x07,0x00,0x00,0xe0,0x00,0x00,0x07,0x80,0x01,0xe0,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x07,0xff,0xff,0xe0,0x00,0x00,0x0f,0xff,0xff,0xf0,0x00,0x00,0x0f,0xff,0xff,0xf0,0x00,0x00,0x07,0xff,0xff,0xe0,0x00
};

const unsigned char clear_day[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00
, 0x00, 0x38, 0x00, 0x30, 0x00, 0x70, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xf0, 0x00
, 0x00, 0x3e, 0x00, 0x00, 0x01, 0xf0, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x03, 0xe0, 0x00
, 0x00, 0x0f, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x07, 0x00, 0x78, 0x03, 0x80, 0x00
, 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xc0, 0x00, 0x00
, 0x00, 0x00, 0x1f, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x03, 0xf0, 0x00, 0x00
, 0x00, 0x00, 0x7c, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x78, 0x00, 0x00
, 0x00, 0x00, 0xf0, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x3c, 0x00, 0x00
, 0x00, 0x01, 0xe0, 0x00, 0x1e, 0x00, 0x00, 0x0f, 0xc1, 0xe0, 0x00, 0x1e, 0x0f, 0xc0
, 0x1f, 0xe1, 0xe0, 0x00, 0x1e, 0x1f, 0xe0, 0x1f, 0xe1, 0xe0, 0x00, 0x1e, 0x1f, 0xe0
, 0x0f, 0xc1, 0xe0, 0x00, 0x1e, 0x0f, 0xc0, 0x00, 0x01, 0xe0, 0x00, 0x1e, 0x00, 0x00
, 0x00, 0x00, 0xf0, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x3c, 0x00, 0x00
, 0x00, 0x00, 0x78, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0xf8, 0x00, 0x00
, 0x00, 0x00, 0x3f, 0x03, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xe0, 0x00, 0x00
, 0x00, 0x00, 0x0f, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x00
, 0x00, 0x07, 0x00, 0x78, 0x03, 0x80, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x03, 0xc0, 0x00
, 0x00, 0x1f, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x01, 0xf0, 0x00
, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x38, 0x00, 0x30, 0x00, 0x70, 0x00
, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

const unsigned char temperature[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x01, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xcf, 0x80, 0x00, 0x00
, 0x00, 0x00, 0x07, 0x87, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0x87, 0x80, 0x00, 0x00
, 0x00, 0x00, 0x07, 0x87, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0x87, 0x80, 0x00, 0x00
, 0x00, 0x00, 0x07, 0x87, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0x87, 0x80, 0x00, 0x00
, 0x00, 0x00, 0x07, 0x87, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0x87, 0x80, 0x00, 0x00
, 0x00, 0x00, 0x07, 0x87, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0x87, 0x80, 0x00, 0x00
, 0x00, 0x00, 0x07, 0xb7, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb7, 0x80, 0x00, 0x00
, 0x00, 0x00, 0x07, 0xb7, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb7, 0x80, 0x00, 0x00
, 0x00, 0x00, 0x07, 0xb7, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb7, 0x80, 0x00, 0x00
, 0x00, 0x00, 0x07, 0xb7, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0xb7, 0x80, 0x00, 0x00
, 0x00, 0x00, 0x07, 0xb7, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xb7, 0xc0, 0x00, 0x00
, 0x00, 0x00, 0x0f, 0x33, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x79, 0xe0, 0x00, 0x00
, 0x00, 0x00, 0x1e, 0xfd, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xfd, 0xe0, 0x00, 0x00
, 0x00, 0x00, 0x1e, 0x79, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x33, 0xc0, 0x00, 0x00
, 0x00, 0x00, 0x0f, 0x87, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0x80, 0x00, 0x00
, 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

const unsigned char prob_rain[] PROGMEM = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x00,0x00,0x07,0xff,0xc0,0x00,0x00
,0x00,0x00,0x3f,0xff,0xf0,0x00,0x00,0x00,0x00,0x7f,0xff,0xfc,0x00,0x00,0x00,0x01,0xff,0xff,0xfe,0x00,0x00,0x00,0x03,0xff,0xff,0xff,0x00,0x00
,0x00,0x07,0xff,0xff,0xff,0x80,0x00,0x00,0x07,0xff,0xff,0xff,0xc0,0x00,0x00,0x0f,0xff,0xff,0xff,0xe0,0x00,0x00,0x0f,0xff,0xff,0xff,0xe0,0x00
,0x00,0x00,0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00
,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00
,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x40,0x00,0x00,0x00,0x00,0x00,0x38,0xe0,0x00,0x00,0x00,0x00,0x00,0x38,0xe0,0x00,0x00
,0x00,0x00,0x00,0x1f,0xc0,0x00,0x00,0x00,0x00,0x00,0x1f,0x80,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};

const unsigned char barometer[] PROGMEM = {
0x00,0x1f,0xe0,0x00,0x00,0x7f,0xf8,0x00,0x01,0xff,0xfe,0x00,0x03,0xf0,0x3f,0x00,0x0f,0x80,0x07,0xc0,0x0f,0x00,0x03,0xc0,0x1e,0x00,0x01,0xe0,0x3c,0x00,0x00,0xf0
,0x38,0x00,0x3c,0x70,0x70,0x00,0x3c,0x38,0x70,0x00,0x7c,0x38,0xf0,0x00,0xfc,0x3c,0xe0,0x07,0xf0,0x1c,0xe0,0x0f,0xe0,0x1c,0xe0,0x0f,0xc0,0x1c,0xe0,0x0f,0xc0,0x1c
,0xe0,0x1f,0x80,0x1c,0xe0,0x3f,0x80,0x1c,0xf3,0xfc,0x00,0x3c,0x71,0xf8,0x00,0x38,0x70,0xf0,0x00,0x38,0x38,0x70,0x00,0x70,0x3c,0x30,0x00,0xf0,0x1e,0x10,0x01,0xe0
,0x0f,0x00,0x03,0xc0,0x0f,0x80,0x07,0xc0,0x03,0xf0,0x3f,0x00,0x01,0xff,0xfe,0x00,0x00,0x7f,0xf8,0x00,0x00,0x1f,0xe0,0x00
};

const unsigned char wind_speed[] PROGMEM = {
0x00,0x00,0x00,0x00,0x01,0xfc,0x00,0x00,0x03,0x3f,0xf0,0x00,0x85,0x3e,0x1f,0x80,0xd9,0x3e,0x1f,0x38,0xf1,0x3e,0x0f,0x0c,0xc1,0x3e,0x0f,0x0c,0xc1,0x3e,0x0f,0x0c
,0xf1,0x3e,0x0f,0x0c,0xd9,0x3e,0x0f,0x18,0xc5,0x3e,0x1f,0xc0,0xc3,0x3f,0xf0,0x00,0xc1,0xfe,0x00,0x00,0xc0,0x80,0x00,0x00,0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00
,0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00
,0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};

const unsigned char rain[] PROGMEM = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x00
,0x00,0x01,0xff,0xe0,0x00,0x00,0x00,0x00,0x07,0xff,0xf8,0x00,0x00,0x00,0x00,0x0f,0xff,0xfc,0x00,0x00,0x00,0x00,0x3f,0x80,0x7f,0x00,0x00,0x00
,0x00,0x7e,0x00,0x1f,0x80,0x00,0x00,0x00,0x78,0x00,0x07,0x80,0x00,0x00,0x00,0xf0,0x00,0x03,0xc0,0x00,0x00,0x01,0xe0,0x00,0x01,0xec,0x00,0x00
,0x01,0xe0,0x00,0x01,0xff,0xc0,0x00,0x03,0xc0,0x00,0x00,0xff,0xf0,0x00,0x03,0xc0,0x00,0x00,0xff,0xf8,0x00,0x03,0x80,0x00,0x00,0x40,0x7c,0x00
,0x03,0x80,0x00,0x00,0x00,0x3e,0x00,0x07,0x80,0x00,0x00,0x00,0x1e,0x00,0x07,0x80,0x00,0x00,0x00,0x0f,0x00,0x07,0x80,0x00,0x00,0x00,0x07,0x00
,0x07,0x80,0x00,0x00,0x00,0x07,0x00,0x03,0x80,0x00,0x00,0x00,0x07,0x80,0x03,0x80,0x00,0x00,0x00,0x07,0x80,0x03,0xc0,0x00,0x00,0x00,0x07,0x80
,0x03,0xc0,0x78,0x00,0x78,0x07,0x80,0x01,0xe0,0x78,0x00,0x78,0x07,0x00,0x01,0xe0,0x78,0x00,0x78,0x0f,0x00,0x00,0xf0,0x78,0x00,0x78,0x0f,0x00
,0x00,0x78,0x78,0x78,0x78,0x1e,0x00,0x00,0x78,0x78,0x78,0x78,0x3e,0x00,0x00,0x38,0x30,0x78,0x30,0x7c,0x00,0x00,0x08,0x00,0x78,0x00,0x78,0x00
,0x00,0x00,0x00,0x78,0x00,0x70,0x00,0x00,0x00,0x00,0x78,0x00,0x40,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x78,0x00,0x78,0x00,0x00,0x00,0x00,0x78,0x00,0x78,0x00,0x00,0x00,0x00,0x78,0x00,0x78,0x00,0x00,0x00,0x00,0x78,0x00,0x78,0x00,0x00
,0x00,0x00,0x78,0x78,0x78,0x00,0x00,0x00,0x00,0x78,0x78,0x78,0x00,0x00,0x00,0x00,0x30,0x78,0x30,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00
,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};

const unsigned char partly_cloudy_day[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00
, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x06, 0x00, 0x0e, 0x00
, 0x00, 0x07, 0x80, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x3e, 0x00
, 0x00, 0x03, 0xe0, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x78, 0x00
, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xe0, 0x00, 0x00
, 0x00, 0x00, 0x01, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xfc, 0x00, 0x00
, 0x00, 0x07, 0xe7, 0xe0, 0x7e, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x80, 0x1f, 0x00, 0x00
, 0x00, 0xff, 0xff, 0x00, 0x0f, 0x00, 0x00, 0x03, 0xff, 0xff, 0x80, 0x07, 0x80, 0x00
, 0x07, 0xf0, 0x0f, 0xe0, 0x07, 0x80, 0x00, 0x0f, 0xc0, 0x03, 0xf0, 0x03, 0xc0, 0x00
, 0x1f, 0x00, 0x00, 0xf8, 0x03, 0xc1, 0xf8, 0x1e, 0x00, 0x00, 0x78, 0x03, 0xc3, 0xfc
, 0x3c, 0x00, 0x00, 0x3f, 0xc3, 0xc3, 0xfc, 0x3c, 0x00, 0x00, 0x3f, 0xfb, 0xc1, 0xf8
, 0x78, 0x00, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x78, 0x00, 0x00, 0x1f, 0xff, 0x80, 0x00
, 0x70, 0x00, 0x00, 0x08, 0x1f, 0x80, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00
, 0xf0, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00
, 0xf0, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00
, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00
, 0x78, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00
, 0x3c, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00
, 0x1e, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00
, 0x0f, 0xc0, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x07, 0xf0, 0x00, 0x00, 0x1f, 0x80, 0x00
, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00
, 0x00, 0x3f, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xc0, 0x00, 0x00
};

GxEPD2_BW&lt;GxEPD2_420, GxEPD2_420::HEIGHT&gt; display(GxEPD2_420(/*CS=D8*/ EPD_CS, /*DC=D3*/ 16, /*RST=D4*/ 5, /*BUSY=D2*/ 19));

char ssid[] = "xxxxxx"; // Your network SSID (name)
char password[] = "xxxxxxxx"; // Your network key
char host[] = "192.168.178.100"; // IP of SmartHomeNG
int port = 4321;

WiFiClient client;
JsonParser&lt;32&gt; parser;
String currentLine = "";
String currRates = "";
boolean readingJson = false;
#define BUFFERSIZE 5800
static char inData[BUFFERSIZE + 1]; // string + terminator

void setup(void) {
  Serial.begin(115200);
  initialise_wifi();
  display.init(115200);
}

void loop() {
  connect_to_server();
  Serial.println("new loop");
  if (client.connected())
  {
    Serial.println("[isConnected]");
    int index = 0;
    while (!client.available()) {}
    while (client.available()) {
      char inChar = client.read();
      if (inChar == '{') {
        readingJson = true;
      }
      if (readingJson) {
        if (index &lt; BUFFERSIZE - 1)
        {
          inData[index] = inChar; // Store it
          index++;
        }
        if (inChar == '}') {
          readingJson = false;
          inData[index] = '\0';
        }
      }
    }
    client.stop();
    Serial.println("[Disconnected]");
     
    // JSON Daten parsen
    StaticJsonBuffer&lt;5800&gt; jsonBuffer;
    JsonObject&amp; root = jsonBuffer.parseObject(inData);
    if (!root.success())
    {
      Serial.print("parseObject(");
      Serial.print(inData);
      Serial.println(") failed");
    } else {
      Serial.println("[Parse Success]");
      printJsonData(root);
    }
  }
  delay(1000 * 60 * 10); // 10 Minuten warten
}

void printJsonData(JsonObject&amp; root)
{
  int dateSubstringStart = 11;
  int dateSubstringEnd = 16;
  double ozone = root["weather.darksky.home.currently.ozone"];
  String uvIndex = root["weather.darksky.home.currently.uvIndex"];
  String uvIndexStr = root["weather.darksky.home.currently.uvIndex.string"];
  uvIndexStr.replace("u00e4", "ae");
  uvIndexStr.replace("u00df", "ss");
  String curTime = root["weather.darksky.home.currently.time.timeStr"];
  String curDate = root["weather.darksky.home.currently.time.dateStr"];
  double outsideHumidity = root["weather.darksky.home.currently.humidity"];
  double surplus = root["sma.smaem.surplus.kw"];
  double regard = root["sma.smaem.regard.kw"];
  double precipProb = root["weather.darksky.home.currently.precipProbability"];
  double pressure = root["weather.darksky.home.currently.pressure"];
  String currentIcon = root["weather.darksky.home.currently.icon"];
  String moonLight = root["env.location.moonlight"];
  String knx_light_status = root["knx.light_status"];
  String gate_state = root["knx.outside.gate.driveway.state"];
  String moonRise = root["env.location.moonrise"];
  moonRise = moonRise.substring(dateSubstringStart, dateSubstringEnd);
  String moonSet = root["env.location.moonset"];
  moonSet = moonSet.substring(dateSubstringStart, dateSubstringEnd);
  String moonPhase = root["env.location.moonphase"];
  String sunSet = root["env.location.sunset"];
  sunSet = sunSet.substring(dateSubstringStart, dateSubstringEnd);;
  String sunRise = root["env.location.sunrise"];
  sunRise = sunRise.substring(dateSubstringStart, dateSubstringEnd);
  String temperature = root["eta_unit.temperature_outside.value"];
  String temperatureMin = root["eta_unit.temperature_outside.value.min_24"];
  String temperatureMax = root["eta_unit.temperature_outside.value.max_24"];
  String currentlySummary = root["weather.darksky.home.currently.summary"];
  String dailySummary = root["weather.darksky.home.daily.summary"];
  double windSpeed = root["weather.darksky.home.currently.windSpeed"];
  dailySummary.replace("u00b0C", " Grad");
  dailySummary.replace("u00e4", "ae");
  display.setRotation(2);
  display.setFont(&amp;FreeMonoBold18pt7b);
  display.setTextColor(GxEPD_BLACK);
  display.setFullWindow();
  display.firstPage();

  do
  {
    display.fillScreen(GxEPD_WHITE);
    display.setFont(&amp;FreeMonoBold9pt7b);

    display.setCursor(0, 11); display.println(curTime);
    display.setCursor(150, 11); display.println("Mein Ort");
    display.setCursor(290, 11); display.println(curDate);
    drawDashedHLine(0, 20, 420, GxEPD_BLACK);

    display_icon(330, 25, currentIcon);
    display.setCursor(270, 42);
    if (surplus &gt; 0) {
      display.println(surplus);
    } else {
      display.println((-1)*regard);
    }
    display.setCursor(270, 62); display.println("kW");
    display.setCursor(270, 100); display.println("UV: "+uvIndex);
    display.setCursor(270, 115); display.println(uvIndexStr);
    display.setCursor(270, 140); display.print("Ozon: "); display.println((int)ozone, DEC);

    display.setFont(&amp;FreeMonoBold12pt7b);
    dashedRect(0,25,260,125, GxEPD_BLACK);
    display_icon(2, 25, "temperature");
    display.setCursor(50, 47); display.println(temperature+" Grad");
    display.setCursor(50, 67); display.print(outsideHumidity*100,0); display.println("%");
    display_icon(2, 65, "prob_rain");
    display.setCursor(50, 98); display.print(precipProb*100,0); display.println("%");
    display_icon(100, 76, "wind_speed");
    display.setCursor(140, 98); display.print((int)windSpeed, DEC); display.println("km/h");

    //display.setFont(&amp;FreeMonoBold9pt7b);
    display_icon(15, 113, "barometer");
    display.setCursor(50, 135); display.print((int)pressure, DEC); display.println("hPa");

    display.setFont(&amp;FreeMonoBold9pt7b);
    drawDashedHLine(0, 155, 420, GxEPD_BLACK);
    display.setCursor(0, 170); display.println(dailySummary);
    drawDashedHLine(0, 220, 420, GxEPD_BLACK);
    display.setFont(&amp;FreeMonoBold9pt7b);
    display_icon(0, 255, "sunrise_sunset");
    display.setCursor(60, 275); display.println(sunRise);
    display.setCursor(60, 289); display.println(sunSet);
    display.setFont(&amp;FreeMonoBold18pt7b);
    display_icon(137, 265, "moon_"+moonPhase);
    display.setFont(&amp;FreeMonoBold9pt7b);
    display.setCursor(170, 275); display.println(moonRise);
    display.setCursor(170, 289); display.println(moonSet);
    display.setCursor(260, 242); display.println("Tor:");
    
    if (gate_state.equals("false")) {
      display_icon(257, 249, "gate_closed");
    } else {
      display_icon(257, 249, "gate_open");
    }
    display.setCursor(325, 242); display.println("Licht:");
    if (knx_light_status.equals("false")) {
      display_icon(330, 247, "light_off");
    } else {
      display_icon(330, 247, "light_on");
    }
  }
  while (display.nextPage());
}

void drawDashedHLine(int16_t x, int16_t y, int16_t w, uint16_t color) {
  display.startWrite();
  writeDashedLine(x, y, x + w - 1, y, color);
  display.endWrite();
}

#ifndef _swap_int16_t
#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; }
#endif

void writeDashedLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color) {
  int16_t steep = abs(y1 - y0) &gt; abs(x1 - x0);
  bool hole = false; //make it dash by using a hole every second pixel
  if (steep) {
    _swap_int16_t(x0, y0);
    _swap_int16_t(x1, y1);
  }

  if (x0 &gt; x1) {
    _swap_int16_t(x0, x1);
    _swap_int16_t(y0, y1);
  }

  int16_t dx, dy;
  dx = x1 - x0;
  dy = abs(y1 - y0);

  int16_t err = dx / 2;
  int16_t ystep;

  if (y0 &lt; y1) {
    ystep = 1;
  }
  else {
    ystep = -1;
  }

  for (; x0 &lt;= x1; x0++) {
    if (steep) {
      if (!hole) display.writePixel(y0, x0, color);
      hole = !hole;
    } else {
      if (!hole) display.writePixel(x0, y0, color);
      hole = !hole;
    }
    err -= dy;
    if (err &lt; 0) {
      y0 += ystep;
      err += dx;
    }
  }
}

void dashedRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
  display.startWrite();
  writeDashedLine(x, y, x + w - 1, y, color);
  writeDashedLine(x, y, x, y + h - 1, color);
  writeDashedLine(x + w - 1, y, x + w - 1, y + h - 1, color);
  writeDashedLine(x, y + h - 1, x + w - 1, y + h - 1, color);
  display.endWrite();
}

void display_icon(int x, int y, String icon_name) {
  int scale = 10; // Adjust size as necessary
  if (icon_name == "partly-cloudy-day")
    display.drawBitmap(x, y, partly_cloudy_day, 54, 50, GxEPD_BLACK);
  else if (icon_name == "clear-day")
    display.drawBitmap(x, y, clear_day, 54, 50, GxEPD_BLACK);
  else if (icon_name == "rain")
    display.drawBitmap(x, y, rain, 54, 50, GxEPD_BLACK);
  else if (icon_name == "sunrise_sunset")
    display.drawBitmap(x, y, sunrise_sunset, 48, 36, GxEPD_BLACK);
  else if (icon_name == "prob_rain")
    display.drawBitmap(x, y, prob_rain, 54, 50, GxEPD_BLACK);
  else if (icon_name == "temperature")
    display.drawBitmap(x, y, temperature, 54, 50, GxEPD_BLACK);
  else if (icon_name == "pressure")
    display.drawBitmap(x, y, pressure, 69, 64, GxEPD_BLACK);
  else if (icon_name == "moon_0")
    display.drawBitmap(x, y, moon_0_8, 24, 24, GxEPD_BLACK);
  else if (icon_name == "moon_1")
    display.drawBitmap(x, y, moon_2, 24, 24, GxEPD_BLACK);
  else if (icon_name == "moon_2")
    display.drawBitmap(x, y, moon_3, 24, 24, GxEPD_BLACK);
  else if (icon_name == "moon_3")
    display.drawBitmap(x, y, moon_4, 24, 24, GxEPD_BLACK);
  else if (icon_name == "moon_4")
    display.drawBitmap(x, y, moon_5, 24, 24, GxEPD_BLACK);
  else if (icon_name == "moon_5")
    display.drawBitmap(x, y, moon_6, 24, 24, GxEPD_BLACK);
  else if (icon_name == "moon_6")
    display.drawBitmap(x, y, moon_7, 24, 24, GxEPD_BLACK);
  else if (icon_name == "moon_7")
    display.drawBitmap(x, y, moon_8, 24, 24, GxEPD_BLACK);
  else if (icon_name == "moon_8")
    display.drawBitmap(x, y, moon_0_8, 24, 24, GxEPD_BLACK);
  else if (icon_name == "barometer")
    display.drawBitmap(x, y, barometer, 30, 30, GxEPD_BLACK);
  else if (icon_name == "wind_speed")
    display.drawBitmap(x, y, wind_speed, 30, 30, GxEPD_BLACK);
  else if (icon_name == "light_on")
    display.drawBitmap(x, y, light_on, 50, 50, GxEPD_BLACK);
  else if (icon_name == "light_off")
    display.drawBitmap(x, y, light_off, 50, 50, GxEPD_BLACK);
  else if (icon_name == "gate_open")
    display.drawBitmap(x, y, gate_open, 50, 50, GxEPD_BLACK);
  else if (icon_name == "gate_closed")
    display.drawBitmap(x, y, gate_closed, 47, 50, GxEPD_BLACK);
}

// NETWORK STUFF

void connect_to_server() {
  if (client.connect(host, port)) {
    Serial.println("[Connected to server]");
    // HTTP Request gegen SmartHomeNG / das Webservices-Plugin durchführen
    client.println("GET /rest/itemset/weather/ HTTP/1.0");
    //client.println("Host: 192.168.178.100");
    client.println();
  }
  else
  {
    Serial.println("[Connection Failed!]");
    client.stop();
  }
}

static void initialise_wifi(void)
{
  // Versuche ins WiFi Netzwerk zu verbinden
  int connAttempts = 0;
  Serial.print(F("\r\nConnecting to: "));
  Serial.println(String(ssid));
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
  delay(1000); Serial.print(".");
  if (connAttempts &gt; 30) return;
    connAttempts++;
  }
  Serial.println("Connected to wifi");
  print_wifi_status();
}

void print_wifi_status() {
  // SSID des WiFi Netzwerkes ausgeben:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // WiFi IP Adresse des ESP32 ausgeben:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // WiFi Signalstärke ausgeben:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}
</code></pre>
<p>Lädt man das Programm nun auf den verkabelten ESP32 hoch, erscheint nach kurzer Zeit folgende Ausgabe. Nach 10 Minuten erfolgt ein automatischer Refresh:</p>
<p><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2018/07/result_epaper.jpg" alt="" width="1082" height="770" class="alignnone size-full wp-image-1944" srcset="https://www.smarthomeng.de/wp-content/uploads/2018/07/result_epaper.jpg 1082w, https://www.smarthomeng.de/wp-content/uploads/2018/07/result_epaper-300x213.jpg 300w, https://www.smarthomeng.de/wp-content/uploads/2018/07/result_epaper-768x547.jpg 768w, https://www.smarthomeng.de/wp-content/uploads/2018/07/result_epaper-1024x729.jpg 1024w" sizes="(max-width: 1082px) 100vw, 1082px" /></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.smarthomeng.de/epaper-wetterstation-mit-daten-aus-smarthomeng/feed</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>Jetzt wird es bunt! Wie man ein Web-Interface für ein Plugin schreibt (ab SmartHomeNG 1.5)</title>
		<link>https://www.smarthomeng.de/jetzt-wird-es-bunt-wie-man-ein-plugin-webinterface-schreibt-in-kuerze-ab-smarthomeng-1-5</link>
					<comments>https://www.smarthomeng.de/jetzt-wird-es-bunt-wie-man-ein-plugin-webinterface-schreibt-in-kuerze-ab-smarthomeng-1-5#respond</comments>
		
		<dc:creator><![CDATA[Marc René Frieß]]></dc:creator>
		<pubDate>Tue, 26 Jun 2018 17:00:46 +0000</pubDate>
				<category><![CDATA[Beispiel-Implementierungen]]></category>
		<category><![CDATA[Plugins]]></category>
		<category><![CDATA[Bootstrap]]></category>
		<category><![CDATA[CherryPy]]></category>
		<category><![CDATA[Jinja2]]></category>
		<category><![CDATA[SmartHomeNG 1.5]]></category>
		<category><![CDATA[WebInterface]]></category>
		<guid isPermaLink="false">https://www.smarthomeng.de/?p=1740</guid>

					<description><![CDATA[Um SmartHomeNG einsteigerfreundlicher zu machen, bieten wir ab Version 1.5 die Möglichkeit, Plugins mit eigenen Web-Interfaces (ähnlich dem Backend Plugin) zu versehen. Web-Interface bezeichnet hierbei eine kleine Webseite, die Infos zum Plugin ausgibt oder die Bedienung und Konfiguration vereinfacht. Existierende Web-Interfaces von Plugins, lassen sich in der Plugin-Liste des Backend-Plugins<a class="moretag" href="https://www.smarthomeng.de/jetzt-wird-es-bunt-wie-man-ein-plugin-webinterface-schreibt-in-kuerze-ab-smarthomeng-1-5"> Weiterlesen&#8230;</a>]]></description>
										<content:encoded><![CDATA[<p>Um SmartHomeNG einsteigerfreundlicher zu machen, bieten wir ab Version 1.5 die Möglichkeit, Plugins mit eigenen Web-Interfaces (ähnlich dem Backend Plugin) zu versehen. Web-Interface bezeichnet hierbei eine kleine Webseite, die Infos zum Plugin ausgibt oder die Bedienung und Konfiguration vereinfacht.</p>
<p><span id="more-1740"></span></p>
<p>Existierende Web-Interfaces von Plugins, lassen sich in der Plugin-Liste des Backend-Plugins finden (siehe Artikel <a href="https://www.smarthomeng.de/das-backend-plugin" target="_blank" rel="noopener"><em>Das Backend-Plugin</em></a>). Ein Web-Interface ist instanzspezifisch, die URL wird dementsprechend aufgebaut.</p>
<p>Die Web-Interfaces des AVM, KNX, Wundergrund und Webservices Plugins sind nur einige Beispiele:<br />
<a href="https://www.smarthomeng.de/wp-content/uploads/2018/06/webif_1.jpg"><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2018/06/webif_1-150x150.jpg" alt="" width="150" height="150" class="alignnone wp-image-1834 size-thumbnail" /></a> <a href="https://www.smarthomeng.de/wp-content/uploads/2018/06/webif_2.jpg"><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2018/06/webif_2-150x150.jpg" alt="" width="150" height="150" class="alignnone wp-image-1835 size-thumbnail" /></a> <a href="https://www.smarthomeng.de/wp-content/uploads/2018/06/webif_3.jpg"><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2018/06/webif_3-150x150.jpg" alt="" width="150" height="150" class="alignnone wp-image-1836 size-thumbnail" /></a> <a href="https://www.smarthomeng.de/wp-content/uploads/2018/06/webif_4.jpg"><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2018/06/webif_4-150x150.jpg" alt="" width="150" height="150" class="alignnone wp-image-1837 size-thumbnail" /></a></p>
<p>Basis hierzu stellt die auf <a href="https://cherrypy.org" target="_blank" rel="noopener">CherryPy</a> (und <a href="http://jinja.pocoo.org/docs/2.10/" target="_blank" rel="noopener">Jinja2</a> als Template-Engine) basierende Implementierung im http module dar. Für detaillierte Infos zur Template Syntax wird auf die o.g. Doku von Jinja2 verwiesen. Dieser Artikel setzt Grundkenntnisse darin voraus.</p>
<h1>Frameworks</h1>
<p>SmartHomeNG bringt dabei für alle Plugins nutzbar eine ganze Reihe an Frameworks bereits mit. Diese finden derzeit vor allem im Backend Plugin ihren Einsatz.</p>
<p>Vorhanden sind per Default (Versionen Stand Release 1.5):</p>
<table border="1" style="border-collapse: collapse; width: 81.4293%;" height="457">
<tbody>
<tr style="height: 29px;">
<td style="width: 31.8412%; height: 29px;"><strong>Framework</strong></td>
<td style="width: 65.1885%; height: 29px;"><strong>Beschreibung / Link</strong></td>
</tr>
<tr style="height: 58px;">
<td style="width: 31.8412%; height: 58px;">Bootstrap 4.1.1</td>
<td style="width: 65.1885%; height: 58px;">Web GUI Framework<br />
<a href="http://getbootstrap.com/" target="_blank" rel="noopener">http://getbootstrap.com/</a></td>
</tr>
<tr style="height: 87px;">
<td style="width: 31.8412%; height: 87px;">Bootstrap Datepicker Widget 1.8.0</td>
<td style="width: 65.1885%; height: 87px;"><a href="https://github.com/uxsolutions/bootstrap-datepicker" target="_blank" rel="noopener">https://github.com/uxsolutions/bootstrap-datepicker</a></td>
</tr>
<tr>
<td style="width: 31.8412%;">Bootstrap Tree View<br />(selber angepasst auf Bootstrap 4)</td>
<td style="width: 65.1885%;"><a href="https://github.com/jonmiles/bootstrap-treeview" target="_blank" rel="noopener">https://github.com/jonmiles/bootstrap-treeview</a></td>
</tr>
<tr style="height: 58px;">
<td style="width: 31.8412%; height: 58px;">JQuery 3.3.1</td>
<td style="width: 65.1885%; height: 58px;">Javascript Framework<br />
<a href="https://jquery.org" target="_blank" rel="noopener">https://jquery.org</a></td>
</tr>
<tr style="height: 58px;">
<td style="width: 31.8412%; height: 58px;">Codemirror 5.39.0</td>
<td style="width: 65.1885%; height: 58px;">Online Code Editor<br />
<a href="https://codemirror.net/" target="_blank" rel="noopener">https://codemirror.net/</a></td>
</tr>
<tr style="height: 58px;">
<td style="width: 31.8412%; height: 58px;">Font Awesome 5.1.0</td>
<td style="width: 65.1885%; height: 58px;">Icon-Font für Webseiten<br />
<a href="http://fontawesome.com" target="_blank" rel="noopener">http://fontawesome.com</a></td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<p>Abgelegt sind die Frameworks unter <code>/smarthome/modules/http/webif/gstatic</code> und können direkt in einer Webseite genutzt werden.</p>
<p>Am Beispiel Bootstrap sieht das wie folgt aus:</p>
<pre><code class="language-html">&lt;script src="/gstatic/bootstrap/js/bootstrap.min.js"&gt;&lt;/script&gt;</code></pre>
<h1>Beispielplugin mit Web-Interface</h1>
<p>Das jeweils aktuelle Beispiel für ein Plugin mit Web-Interface findet sich unter <code>/dev/sample_plugin_webif</code>.<br />
In der Methode init_webinterface (als Teil des Plugin-Codes in der Datei <code>__init__.py</code>) wird dabei das Web-Interface initialisiert:</p>
<pre><code class="language-python">
from lib.model.smartplugin import *
[...]
    def init_webinterface(self):
        """"
        Initialize the web interface for this plugin

        This method is only needed if the plugin is implementing a web interface
        """
        try:
            self.mod_http = Modules.get_instance().get_module('http')   # try/except to handle running in a core version that does not support modules
        except:
             self.mod_http = None
        if self.mod_http == None:
            self.logger.error("Plugin '{}': Not initializing the web interface".format(self.get_shortname()))
            return False
        
        # set application configuration for cherrypy
        webif_dir = self.path_join(self.get_plugin_dir(), 'webif')
        config = {
            '/': {
                'tools.staticdir.root': webif_dir,
            },
            '/static': {
                'tools.staticdir.on': True,
                'tools.staticdir.dir': 'static'
            }
        }
        
        # Register the web interface as a cherrypy app
        self.mod_http.register_webif(WebInterface(webif_dir, self), 
                                     self.get_shortname(), 
                                     config, 
                                     self.get_classname(), self.get_instance_name(),
                                     description='')
                                   
        return True
</code></pre>
<p>Diese Methode muss dann natürlich in der <code>def __init__</code> eingebunden werden:</p>
<pre><code class="language-python">
    def __init__(self, sh, *args, **kwargs):
        [...]
        if not self.init_webinterface():
            self._init_complete = False
        [...]</pre>
<p></code>Die eigentliche Implementierung befindet sich in einer eigenen Klasse, die ebenfalls in der <code>__init__.py</code> definiert wird:</p>
<pre><code class="language-python">
import cherrypy
from jinja2 import Environment, FileSystemLoader

class WebInterface(SmartPluginWebIf):


    def __init__(self, webif_dir, plugin):
        """
        Initialization of instance of class WebInterface
        
        :param webif_dir: directory where the webinterface of the plugin resides
        :param plugin: instance of the plugin
        :type webif_dir: str
        :type plugin: object
        """
        self.logger = logging.getLogger(__name__)
        self.webif_dir = webif_dir
        self.plugin = plugin
        self.tplenv = Environment(loader=FileSystemLoader(self.plugin.path_join( self.webif_dir, 'templates' ) ))


    @cherrypy.expose
    def index(self, reload=None):
        """
        Build index.html for cherrypy
        
        Render the template and return the html file to be delivered to the browser
            
        :return: contents of the template after beeing rendered 
        """
        tmpl = self.tplenv.get_template('index.html')
        return tmpl.render(plugin_shortname=self.plugin.get_shortname(), plugin_version=self.plugin.get_version(),
                           plugin_info=self.plugin.get_info(), p=self.plugin)
</code></pre>
<p>Der Ausdruck <code>@cherrypy.exposed</code> legt fest, dass die Methode über http-Requests aufrufbar (also über den Browser zugreifbar) ist und diese beantworten kann. Methoden bei denen dies nicht der Fall sein soll, brauchen diese Angabe nicht.<br />
In <code>self.tplenv.get_template</code> wird das <code>index.html</code> Template geladen, in <code>tmpl.render</code> werden Variablen übergeben und das Template als HTML gerendert.</p>
<p><strong>Tipp:</strong> Für ein minimales Plugin Web-Interface reicht es im Übrigen, nur die notwendigen Parameter der <code>tmpl.render</code> Methode zu übergeben und eine minimale <code>index.html</code> zu haben.</p>
<h1>Das Plugin gestalten</h1>
<p>Wie im nachfolgenden Bild am Beispiel des Web-Interfaces des Database Plugins zu sehen, werden statische Dateien wie etwa Bilder im Ordner <code>static</code> direkt unter dem Pluginordner abgelegt. Die eigentlichen Templates für das Web-Interface des Plugins liegen unter <code>templates</code>.</p>
<p><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2018/06/plugin_webif.jpg" alt="" width="237" height="330" class="alignnone size-full wp-image-1758" srcset="https://www.smarthomeng.de/wp-content/uploads/2018/06/plugin_webif.jpg 237w, https://www.smarthomeng.de/wp-content/uploads/2018/06/plugin_webif-215x300.jpg 215w" sizes="(max-width: 237px) 100vw, 237px" /></p>
<p>Einstiegs-Template ist die Datei <code>index.html</code> im <code>templates</code> Ordner, die oben in der Methode <code>index</code> definiert wurde. Das Default Template zu dieser Seite ist wie folgt aufgebaut:</p>
<ol>
<li>Mittels <code>{% extends "base_plugin.html" %}</code> wird von einer globalen Vorlage für Plugin-Web-Interfaces abgeleitet. Diese ist unter <code>/smarthome/modules/http/webif/gtemplates/base_plugin.html</code> zu finden. Über dieses Basis-Template werden bereits Bootstrap, JQuery, Font Awesome und CodeMirror eingebunden. Die Verwendung des Templates stellt sicher, dass Web-Interfaces für Plugins ein homogenes Look and Feel haben.</li>
<li>Da wir SmartHomeNG-weit auf Bootstrap 4 setzen, bitten wir, die Stilmittel und CSS Klassen von Bootstrap zu verwenden. Details siehe <a href="http://getbootstrap.com/" target="_blank" rel="noopener">http://getbootstrap.com/</a></li>
<li>Es gibt eine Reihe an Bereichen, die man nun in seiner <code>index.html</code> individuell ausgestalten kann. Diese sog. Blöcke sind <code>headtable</code>, <code>buttons</code> und <code>bodytab1</code> bis <code>bodytab4</code></li>
<li>Ein Block kann im Template wie folgt befüllt werden:
<pre class="language-django"><code>{% block headtable %}
...
{% endblock headtable %}</code></pre>
</li>
<li>Im Fall mehrerer <code>bodytab</code>'s, können auch noch <code>tab1title</code> bis <code>tab4title</code> vergeben werden. Wird dies nicht gemacht, werden Default Titel erzeugt.</li>
</ol>
<p>Nachfolgend sind die Blöcke am Beispiel des Web-Interfaces des AVM Plugins nochmals dargestellt:<br />
<img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2018/06/webif_blocks-1.jpg" alt="" width="1294" height="597" class="alignnone size-full wp-image-1813" srcset="https://www.smarthomeng.de/wp-content/uploads/2018/06/webif_blocks-1.jpg 1294w, https://www.smarthomeng.de/wp-content/uploads/2018/06/webif_blocks-1-300x138.jpg 300w, https://www.smarthomeng.de/wp-content/uploads/2018/06/webif_blocks-1-768x354.jpg 768w, https://www.smarthomeng.de/wp-content/uploads/2018/06/webif_blocks-1-1024x472.jpg 1024w" sizes="(max-width: 1294px) 100vw, 1294px" /></p>
<h1>Tipps und Tricks</h1>
<h2>Eigene CSS und JS Dateien einbinden</h2>
<p>Will man eigene JavaScript oder CSS Files einbinden, so kann man die Blöcke des <code>base_plugin</code> einfach erweitern. Ein Beispiel wäre wie folgt:</p>
<pre class="language-django"><code>{%- block scripts %}
{{ super() }}
<span>&lt;script </span><span>src=</span><span>"/gstatic/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js"</span><span>&gt;&lt;/script&gt;
</span><span>&lt;script </span><span>src=</span><span>"/gstatic/bootstrap-datepicker/dist/locales/bootstrap-datepicker.de.min.js"</span><span>&gt;&lt;/script&gt;
</span><span>&lt;script </span><span>src=</span><span>"/gstatic/bootstrap-datepicker/dist/locales/bootstrap-datepicker.fr.min.js"</span><span>&gt;&lt;/script&gt;
</span>{%- endblock scripts %}

{%- block styles %}
{{ super() }}
<span>&lt;link </span><span>rel=</span><span>"stylesheet" </span><span>href=</span><span>"/gstatic/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css" </span><span>type=</span><span>"text/css"</span><span>/&gt;
</span>{%- endblock styles %}
</code></pre>
<p>Über <code>super()</code> wird der Block eines der übergeordneten Templates, in diesem Fall in der <code>../modules/http/webif/gtemplates/base.html</code>, die der <code>plugin_base.html</code> übergeordnet ist, aufgerufen. Die nach <code>super()</code> folgenden Teile, werden dem Blockinhalt angehängt. Würde man <code>super()</code> weglassen, würde der Block komplett überschrieben.</p>
<h2>Unterschiedliche Seiten / Aktionen über Seitenaufrufe auslösen.</h2>
<p>Will man nicht nur eine Seite für das Plugin haben, so kann man entweder analog zur Methode <code>def index(self)</code> in der <code>__init__.py</code> weitere Methoden (und damit Seiten) definieren. In jeder dieser Methoden muss via <code>get_template</code> ein jeweils anderes Templates aus dem <code>templates</code> Ordner des Plugins geladen werden.</p>
<p>Eine Alternative zu diesem Vorgehen ist, nur die <code>index</code> Methode zu verwenden und die Auswahl des Templates in der <code>def index(self)</code> via Kommando-Parameter zu steuern. Neue Parameter kann man einfach in der Methode ergänzen, also bspw. <code>def index(self, <i>cmd=None</i>)</code>. Die Vorbelegung mit <code>None</code> ist dafür, dass der Parameter auch weggelassen werden kann.</p>
<p>Über <code>cmd</code> kann man nun im Python Code Aktionen auslösen oder abhängig vom Wert in <code>cmd</code> via <code>tmpl = self.tplenv.get_template('...')</code> andere Templates auswählen. <code>cmd</code> muss in einem Link / Formular als GET-Parameter übergeben werden, als bspw. mit <code>?cmd=...</code> an eine URL angehängt werden.</p>
<p>Ein Beispiel dazu kann im Web-Interface des Database-Plugin gefunden werden.</p>
<p>Wurde eine Aktion erfolgreich ausgeführt, empfiehlt es sich, die Info in <code>tmpl.render</code> an das Template zu übergeben. Über Bootstrap kann man nun schöne Status-Meldungen anzeigen:</p>
<pre class="language-django"><code>{% if delete_triggered %}
&lt;div class="mb-2 alert alert-success alert-dismissible fade show" role="alert"&gt;
    &lt;strong&gt;Löschauftrag für die Einträge von Item ID {{ item_id }} in der Tabelle "log" wurde erfolgreich initiiert!&lt;/strong&gt;&lt;br/&gt;
    Das Löschen kann noch kurze Zeit dauern, da die Ausführung des Delete Queries erst nach Abschluß der bestehenden Transaktionen erfolgen kann.
    &lt;button type="button" class="close" data-dismiss="alert" aria-label="Close"&gt;
        &lt;span aria-hidden="true"&gt;&amp;times;&lt;/span&gt;
    &lt;/button&gt;
&lt;/div&gt;
{% endif %}
</code></pre>
<h2>Internationalisierung</h2>
<p>Das Übersetzungssystem von SmartHomeNG kann auch für Plugins genutzt werden. Dazu muss direkt im Pluginverzeichnis eine Datei <code>locale.yaml</code> angelegt werden. In dieser werden Übersetzungen wie folgt definiert:</p>
<pre class="language-yaml"><code>
plugin_translations:
    # Translations for the plugin specially for the web interface
    'Aktionen':        {'de': 'Aktionen', 'en': 'Actions'}
    'Verbunden':       {'de': 'Verbunden', 'en': 'Connected'}
    'Ja':              {'de': 'Ja', 'en': 'Yes'}
    'Nein':            {'de': 'Nein', 'en': 'No'}
</code></pre>
<p>Im Template kann nun über die Syntax <code>{{ _('<em>&lt;Schlüssel aus der language.yaml&gt;</em>') }}</code> darauf zugegriffen werden:</p>
<pre class="language-django"><code>{% if check %}{{ _('Ja') }}{% else %}{{ _('Nein') }}{% endif %}</code></pre>
<p><span style="color: #ff0000;"><strong>Wir bitten darum, Web-Interfaces für Plugins immer mindestens auf Englisch und Deutsch zu erstellen!</strong></span></p>
<p><em>(Die in diesem Artikel verwendeten Screenshots wurden selber erstellt. Das Titelbild ist unter der Creative Commons Zero (CC0) Lizenz veröffentlicht und wurde von www.pexels.com bezogen.)</em></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.smarthomeng.de/jetzt-wird-es-bunt-wie-man-ein-plugin-webinterface-schreibt-in-kuerze-ab-smarthomeng-1-5/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Entfernungsmessung mit ESP32 und SmartHomeNG &#8211; Teil 2: Anbindung eines SSD1306 OLED Displays</title>
		<link>https://www.smarthomeng.de/entfernungsmessung-mit-esp32-und-smarthomeng-teil-2-anbindung-eines-ssd1306-oled-displays</link>
					<comments>https://www.smarthomeng.de/entfernungsmessung-mit-esp32-und-smarthomeng-teil-2-anbindung-eines-ssd1306-oled-displays#respond</comments>
		
		<dc:creator><![CDATA[Marc René Frieß]]></dc:creator>
		<pubDate>Sat, 19 May 2018 17:46:32 +0000</pubDate>
				<category><![CDATA[Beispiel-Implementierungen]]></category>
		<category><![CDATA[Tipps & Tricks]]></category>
		<category><![CDATA[ESP32]]></category>
		<category><![CDATA[OLED Display]]></category>
		<category><![CDATA[SSD1306]]></category>
		<category><![CDATA[Wasserstand]]></category>
		<category><![CDATA[Webservices]]></category>
		<category><![CDATA[Zisterne]]></category>
		<guid isPermaLink="false">https://www.smarthomeng.de/?p=1671</guid>

					<description><![CDATA[Als nächsten Schritt meines Projekts zur Überwachung des Wasserstands meiner Zisterne, habe ich ein SSD1306 OLED Display ergänzt, das in meiner Garage in der Nähe der Pumpe installiert werden soll. Hier möchte ich die Wasserhöhe und &#8211; eines Tages &#8211; den Wasserstand anzeigen. Als Vorarbeiten wird im Wesentlichen der Artikel<a class="moretag" href="https://www.smarthomeng.de/entfernungsmessung-mit-esp32-und-smarthomeng-teil-2-anbindung-eines-ssd1306-oled-displays"> Weiterlesen&#8230;</a>]]></description>
										<content:encoded><![CDATA[<p>Als nächsten Schritt meines Projekts zur Überwachung des Wasserstands meiner Zisterne, habe ich ein SSD1306 OLED Display ergänzt, das in meiner Garage in der Nähe der Pumpe installiert werden soll. Hier möchte ich die Wasserhöhe und &#8211; eines Tages &#8211; den Wasserstand anzeigen.</p>
<p>Als Vorarbeiten wird im Wesentlichen der Artikel &#8222;<a href="https://www.smarthomeng.de/entfernungsmessung-auf-basis-eines-esp32-und-smarthomeng" target="_blank" rel="noopener">Entfernungsmessung auf Basis eines ESP32 und SmartHomeNG</a>&#8220; vorausgesetzt.<a href="https://www.smarthomeng.de/entfernungsmessung-auf-basis-eines-esp32-und-smarthomeng"></a></p>
<p>Mittlerweile bin ich bei der Messung des Ultraschallsignales des HR-SR04 allerdings auf die Arduino Bibliothek <a href="https://playground.arduino.cc/Code/NewPing" target="_blank" rel="noopener">NewPing</a> umgestiegen, die die Messung übernimmt:<img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2018/05/newping_lib.jpg" alt="" width="726" height="96" class="alignnone size-full wp-image-1710" srcset="https://www.smarthomeng.de/wp-content/uploads/2018/05/newping_lib.jpg 726w, https://www.smarthomeng.de/wp-content/uploads/2018/05/newping_lib-300x40.jpg 300w" sizes="(max-width: 726px) 100vw, 726px" />Als Display kommt ein kleines OLED Display mit SSD 1306 Controller zum Einsatz (<a href="https://www.az-delivery.de/products/0-96zolldisplay" target="_blank" rel="noopener">https://www.az-delivery.de/products/0-96zolldisplay</a>, ca. 7 Euro).</p>
<p>Das Display wird wie folgt zusätzlich zum HC-SR04 angeschlossen:</p>
<p><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2018/05/Untitled-Sketch-2_Steckplatine.png" alt="" width="529" height="460" class="alignnone wp-image-1673" srcset="https://www.smarthomeng.de/wp-content/uploads/2018/05/Untitled-Sketch-2_Steckplatine.png 1098w, https://www.smarthomeng.de/wp-content/uploads/2018/05/Untitled-Sketch-2_Steckplatine-300x261.png 300w, https://www.smarthomeng.de/wp-content/uploads/2018/05/Untitled-Sketch-2_Steckplatine-768x667.png 768w, https://www.smarthomeng.de/wp-content/uploads/2018/05/Untitled-Sketch-2_Steckplatine-1024x890.png 1024w" sizes="(max-width: 529px) 100vw, 529px" /></p>
<p>Als Bibliothek kommt die &#8222;<a href="https://github.com/ThingPulse/esp8266-oled-ssd1306" target="_blank" rel="noopener">ESP8266 and ESP32 Oled Driver for SSD1306 display</a>&#8220; von Daniel Eichhorn und Fabrice Weinberg in Version 4.0.0 zum Einsatz:</p>
<p><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2018/05/display_lib.jpg" alt="" width="734" height="90" class="alignnone size-full wp-image-1685" srcset="https://www.smarthomeng.de/wp-content/uploads/2018/05/display_lib.jpg 734w, https://www.smarthomeng.de/wp-content/uploads/2018/05/display_lib-300x37.jpg 300w" sizes="(max-width: 734px) 100vw, 734px" /></p>
<p>Der angepasst Sourcecode sieht nun wie folgt aus. Es ist dabei zu beachten, dass das Webservices- Plugin inzwischen über die REST Schnittstelle angesteuert wird. Weiterhin sind alle Log-Outputs für den Seriellen Monitor entfernt worden. Sämtliche relevanten Outputs werden jetzt auf dem Display ausgegeben.</p>
<pre><code class="language-c">
#include &lt;HTTPClient.h&gt;
#include &lt;NewPing.h&gt;
#include &lt;WiFi.h&gt;
#include &lt;WiFiClient.h&gt;
#include &lt;WiFiServer.h&gt;
#include &lt;WiFiUdp.h&gt;
#include &lt;Wire.h&gt;
#include "SSD1306.h"

const int triggerPin = 15;   // HC-SR04: Trigger Pin
const int echoPin = 4;       // HC-SR04: Echo Pin
const int sdaPin = 21;       // Display: SDA Pin
const int sclPin = 22;       // Display: SCL Pin
const int sensorRange = 280; // Reichweite (&lt; maximale Reichweite)

// HC-SR04
NewPing sonar(triggerPin, echoPin, sensorRange); // create NewPing instance

// Wifi
char ssid[] = "&lt;wlan_ssid&gt;";     // your network SSID (name)
char pass[] = "&lt;wlan_password&gt;"; // your network password
char host[] = "192.168.178.100"; // ip of service interface in SmartHomeNG
int port = 4321;                 // port of service interface in SmartHomeNG
char cm_item[] = "test.numeric";
int status = WL_IDLE_STATUS;
WiFiClient client;

// Display
SSD1306Wire display(0x3c, sdaPin, sclPin);

void setup() {
  Serial.begin(19200);
  // Init Display
  display.init();
  display.flipScreenVertically();
  // Init Wifi
  initialise_wifi();
}

void loop() {
  delay(50);
  unsigned int result = sonar.ping();
  float cm = sonar.convert_cm(result); //(result/2)/29.1; //

  while (client.available()) {
    char c = client.read();
    Serial.write(c);
  }

  if (cm &gt; 0) {
    if (client.connect(host, port)) {
      HTTPClient http;
      char portStr[12];
      sprintf(portStr, "%d", port);
      char url[128];
      sprintf(url, "http://%s:%s/rest/items/%s", host, portStr, cm_item);
      http.begin(url);
      http.addHeader("Content-Type", "application/json");
      char cmStr[12];
      char cmStrFull[24];
      sprintf(cmStr, "%f", cm);
      sprintf(cmStrFull, "%i cm", (int)cm);
      int httpCode = http.PUT(cmStr);
      String payload = http.getString();
      http.end();

      display.clear();
      display.setFont(ArialMT_Plain_16);
      display.drawString(0, 0, "Wasserstand:");
      display.setFont(ArialMT_Plain_24);
      display.drawString(0, 17, cmStrFull);
      display.display();
    }
  }
  // Wait 5 seconds and do next measurement
  delay(5000);
}

static void initialise_wifi(void) {
  display.setFont(ArialMT_Plain_10);
  display.drawString(0, 0, "Setting up wifi...");
  display.display();

  while (status != WL_CONNECTED) {
    display.drawString(0, 11, "Attempting to connect...");
    display.display();
    status = WiFi.begin(ssid, pass);
    delay(10000);
  }
  display.drawString(0, 22, "Connected to wifi!");
  display.display();
  printWifiStatus();
}

void printWifiStatus() {
  IPAddress ip = WiFi.localIP();
  String ipStr = String(ip[0]) + '.' + String(ip[1]) + '.' + String(ip[2]) + '.' + String(ip[3]);
  display.drawString(0, 33, "IP: "+ipStr);
  display.display();
  long rssi = WiFi.RSSI();
  char rssiStr[255];
  sprintf(rssiStr, "RSSI: %d dBm", rssi);
  display.drawString(0, 44, rssiStr);
  display.display();
}</code></pre>
<p>Nach der Initialisierung des Displays in der Methode <code>setup</code>, kann mittels <code>display.drawString(...)</code> Text auf das Display gesendet werden. Der erste Parameter ist dabei die X-, der zweite die Y-Koordinate. Im dritten Parameter wird der String gesetzt. Mit <code>display.display();</code> werden alle zuvor eingesteuerten Operationen dann wirklich auf dem Display ausgegeben. Ein <code>display.clear();</code> leert die auf dem Display ausgegebenen Werte, damit neuer Text dargestellt werden kann. Wird kein <code>display.display();</code> aufgerufen, so wird immer mehr auf das Display geschrieben. Dies kann soweit führen, dass Text über bestehenden Text geschrieben wird.</p>
<p>Dies kann am Beispiel der WLAN-Initialisierung gut gesehen werden. Das Gehäuse ist dabei wieder von Thingiverse bezogen und via 3D Drucker gedruckt worden (<a href="https://www.thingiverse.com/thing:2176764" target="_blank" rel="noopener">https://www.thingiverse.com/thing:2176764</a>):</p>
<p><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2018/05/IMAG0778-1.jpg" alt="" width="546" height="418" class="alignnone wp-image-1699" srcset="https://www.smarthomeng.de/wp-content/uploads/2018/05/IMAG0778-1.jpg 730w, https://www.smarthomeng.de/wp-content/uploads/2018/05/IMAG0778-1-300x230.jpg 300w" sizes="(max-width: 546px) 100vw, 546px" /><br />
Nach der Initialisierung wird nun auf dem Display der Abstand angezeigt:</p>
<p><img loading="lazy" decoding="async" src="https://www.smarthomeng.de/wp-content/uploads/2018/05/IMAG0776.jpg" alt="" width="516" height="556" class="alignnone wp-image-1690" srcset="https://www.smarthomeng.de/wp-content/uploads/2018/05/IMAG0776.jpg 3024w, https://www.smarthomeng.de/wp-content/uploads/2018/05/IMAG0776-278x300.jpg 278w, https://www.smarthomeng.de/wp-content/uploads/2018/05/IMAG0776-768x827.jpg 768w, https://www.smarthomeng.de/wp-content/uploads/2018/05/IMAG0776-950x1024.jpg 950w" sizes="(max-width: 516px) 100vw, 516px" /></p>
<p>In Teil 3 folgt nun in Kürze noch ein Update zu meiner tatsächlichen Installation in der Zisterne und der Garage. Die Arbeiten sind bereits vollendet und die Werte werden via Kabel in die Garage auf den ESP32 übertragen. Mehr dazu aber in der nächsten Zeit&#8230;</p>
<p><em>(Die in diesem Artikel verwendeten Screenshots und Fotos wurden selber erstellt. Für den Schaltplan wurde das Tool<span> </span><a href="http://fritzing.org/home/" target="_blank" rel="noopener">Fritzing</a><span> </span>verwendet.)</em></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.smarthomeng.de/entfernungsmessung-mit-esp32-und-smarthomeng-teil-2-anbindung-eines-ssd1306-oled-displays/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
