<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>http://wiki.fhem.de/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Dora71</id>
	<title>FHEMWiki - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.fhem.de/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Dora71"/>
	<link rel="alternate" type="text/html" href="http://wiki.fhem.de/wiki/Spezial:Beitr%C3%A4ge/Dora71"/>
	<updated>2026-04-11T12:26:01Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten&amp;diff=39338</id>
		<title>DbRep - Reporting und Management von DbLog-Datenbankinhalten</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten&amp;diff=39338"/>
		<updated>2024-05-26T09:13:41Z</updated>

		<summary type="html">&lt;p&gt;Dora71: /* (regelmäßiges) Löschen von Datenbanksätzen */ at Befehl auf täglich geändert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Reporting und Management von DbLog-Datenbankinhalten&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModCmdRef=DbRep&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbRep.pm&lt;br /&gt;
|ModOwner=DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS Starter|Wiki]])}}&lt;br /&gt;
&lt;br /&gt;
== Zweck und Einsatz des Moduls ==&lt;br /&gt;
Zweck des Moduls ist es, den Inhalt von [[DbLog]]-Datenbanken nach bestimmten Kriterien zu durchsuchen, zu managen, das Ergebnis hinsichtlich verschiedener Aggregationen auszuwerten und als Readings darzustellen. Die Abgrenzung der zu berücksichtigenden Datenbankinhalte erfolgt durch die Angabe von Device, Reading und die Zeitgrenzen für Auswertungsbeginn bzw. Auswertungsende.&lt;br /&gt;
&lt;br /&gt;
Alle Datenbankoperationen werden nichtblockierend ausgeführt. Die Ausführungszeit der (SQL)-Hintergrundoperationen kann optional ebenfalls als Reading bereitgestellt werden (siehe Attribute).&lt;br /&gt;
Alle vorhandenen Readings werden vor einer neuen Operation gelöscht. Durch das Attribut &amp;quot;readingPreventFromDel&amp;quot; kann eine Komma separierte Liste von Readings angegeben werden die nicht gelöscht werden sollen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Zur Zeit ist das Modul durch folgende Leistungsmerkmale gekennzeichnet:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Selektion und Anzeige aller Datensätze innerhalb einstellbarer Zeitgrenzen.&lt;br /&gt;
* Darstellung der Datensätze einer Device/Reading-Kombination innerhalb einstellbarer Zeitgrenzen.&lt;br /&gt;
* Selektion der Datensätze unter Verwendung von dynamisch berechneter Zeitgrenzen zum Ausführungszeitpunkt.&lt;br /&gt;
* Dubletten-Hervorhebung bei Datensatzanzeige (fetchrows) &lt;br /&gt;
* Berechnung der Anzahl von Datensätzen einer Device/Reading-Kombination unter Berücksichtigung von Zeitgrenzen und verschiedenen Aggregationen.&lt;br /&gt;
* Die Berechnung von Summen-, Differenz-, Maximum-, Minimum- und Durchschnittswerten numerischer Readings in Zeitgrenzen und verschiedenen Aggregationen.&lt;br /&gt;
* Speichern von Summen-, Differenz- , Maximum- , Minimum- und Durchschnittswertberechnungen in der Datenbank&lt;br /&gt;
* Löschung von Datensätzen. Die Eingrenzung der Löschung kann durch Device und/oder Reading sowie fixer oder dynamisch berechneter Zeitgrenzen zum Ausführungszeitpunkt erfolgen.&lt;br /&gt;
* Export von Datensätzen in ein File im CSV-Format. &lt;br /&gt;
* Import von Datensätzen aus File im CSV-Format.&lt;br /&gt;
* Umbenennen von Device/Readings in Datenbanksätzen &lt;br /&gt;
* Änderung von gespeicherten Reading-Werten (VALUES) in der Datenbank (changeValue)  &lt;br /&gt;
* automatisches Umbenennen (Autorename) von Device-Namen in Datenbanksätzen und DbRep-Definitionen nach FHEM &amp;quot;rename&amp;quot; Befehl (siehe [[#DbRep_Agent_-_automatisches_.C3.84ndern_von_Device-Namen_in_Datenbanken_und_DbRep-Definitionen_nach_FHEM_.22rename.22 | DbRep-Agent]]) &lt;br /&gt;
* Ausführen von beliebigen benutzerspezifischen SQL-Kommandos &lt;br /&gt;
* Backups der FHEM-Datenbank im laufenden Betrieb erstellen (MySQL, SQLite) mit/ohne Komprimierung der Dumpfiles&lt;br /&gt;
* senden und versionieren Dumpfiles nach dem Backup an einen FTP-Server&lt;br /&gt;
* Restore von SQLite-Dumps und MySQL serverSide-Backups &lt;br /&gt;
* Optimierung der angeschlossenen Datenbank (optimizeTables, vacuum) &lt;br /&gt;
* Ausgabe der existierenden Datenbankprozesse (MySQL) &lt;br /&gt;
* leeren der current-Tabelle&lt;br /&gt;
* Auffüllen der current-Tabelle mit einem (einstellbaren) Extrakt der history-Tabelle&lt;br /&gt;
* Bereinigung sequentiell aufeinander folgender Datensätze (sequentielle Dublettenbereinigung) &lt;br /&gt;
* Reparatur einer korrupten SQLite Datenbank (&amp;quot;database disk image is malformed&amp;quot;)&lt;br /&gt;
* Übertragung von Datensätzen aus der Quelldatenbank in eine andere (Standby) Datenbank (syncStandby)&lt;br /&gt;
* Reduktion der Anzahl von Datensätzen in der Datenbank (reduceLog)&lt;br /&gt;
* Löschen von doppelten Datensätzen (delDoublets)&lt;br /&gt;
* Löschen und (Wieder)anlegen der für DbLog und DbRep benötigten Indizes (index) &lt;br /&gt;
&lt;br /&gt;
Zur Aktivierung der Funktion &amp;quot;Autorename&amp;quot; wird dem definierten DbRep-Device mit dem Attribut &amp;quot;role&amp;quot; die Rolle &amp;quot;Agent&amp;quot; zugewiesen. Die Standardrolle nach Definition ist &amp;quot;Client&amp;quot;. Mehr ist dazu im Abschnitt [[#DbRep_Agent_-_automatisches_.C3.84ndern_von_Device-Namen_in_Datenbanken_und_DbRep-Definitionen_nach_FHEM_.22rename.22 | DbRep-Agent]] beschrieben. &lt;br /&gt;
&lt;br /&gt;
DbRep stellt dem Nutzer einen UserExit zur Verfügung. Über diese Schnittstelle kann der Nutzer in Abhängigkeit von frei definierbaren Reading/Value-Kombinationen (Regex) eigenen Code zur Ausführung bringen. Diese Schnittstelle arbeitet unabhängig von einer Eventgenerierung. Weitere Informationen dazu ist unter [[#Attribute | Attribut]] &amp;quot;userExitFn&amp;quot; beschrieben.&lt;br /&gt;
&lt;br /&gt;
FHEM-Forum:&lt;br /&gt;
{{Link2Forum|Topic=53584|Message=452567|LinkText=Modul 93_DbRep - Reporting und Management von Datenbankinhalten (DbLog)}}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen und Abgrenzungen ==&lt;br /&gt;
Das Modul setzt den Einsatz einer oder mehrerer DBLog-Instanzen voraus (bisher getestet mit PostgreSQL, MySQL und SQLite). Es werden die Zugangsdaten dieser Datenbankdefinition aus der Konfiguration des entsprechenden DbLog-Device genutzt.&lt;br /&gt;
Es werden nur Inhalte der Tabelle &amp;quot;history&amp;quot; berücksichtigt (Ausnahme Kommando &amp;quot;sqlCmd&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Überblick, welche anderen Perl-Module DbRep verwendet:&lt;br /&gt;
&lt;br /&gt;
::POSIX&lt;br /&gt;
::Time::HiRes&lt;br /&gt;
::Time::Local&lt;br /&gt;
::Scalar::Util&lt;br /&gt;
::DBI&lt;br /&gt;
::Blocking (FHEM-Modul)&lt;br /&gt;
::Encode&lt;br /&gt;
&lt;br /&gt;
== Definition ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 define &amp;lt;name&amp;gt; DbRep &amp;lt;Name der DbLog-instanz&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(*) &#039;&#039;&#039;&amp;lt;Name der DbLog-Instanz&amp;gt;:&#039;&#039;&#039; Es wird der Name der auszuwertenden DbLog-Datenbankdefinition angegeben, &#039;&#039;&#039;NICHT&#039;&#039;&#039; die Datenbank selbst.&lt;br /&gt;
&lt;br /&gt;
Aus Performancegründen sollte der Index &amp;quot;Report_Idx&amp;quot; in der Datenbank (Tabelle history) angelegt sein. Ab der DbRep-Version 8.20.0 kann dieser Index, sowie die für DbLog benötigten Indizes, verwaltet werden.&lt;br /&gt;
Der Index &amp;quot;Report_Idx&amp;quot; wird einfach mit dem DbRep-Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 set &amp;lt;name&amp;gt; index recreate_Report_Idx &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auf der Datenbank angelegt. Ist er bereits vorhanden, wird er gelöscht und erneut angelegt.&lt;br /&gt;
&lt;br /&gt;
== Set ==&lt;br /&gt;
Siehe {{Link2CmdRef|Lang=de|Anker=DbRep-set}}&lt;br /&gt;
&lt;br /&gt;
== Get ==&lt;br /&gt;
Siehe {{Link2CmdRef|Lang=de|Anker=DbRep-get}}&lt;br /&gt;
&lt;br /&gt;
== Attribute ==  &lt;br /&gt;
Siehe {{Link2CmdRef|Lang=de|Anker=DbRep-attr}}&lt;br /&gt;
&lt;br /&gt;
== DbRep Agent - automatisches Ändern von Device-Namen in Datenbanken und DbRep-Definitionen nach FHEM &amp;quot;rename&amp;quot; ==&lt;br /&gt;
&lt;br /&gt;
Mit dem Attribut &amp;quot;role&amp;quot; wird die Rolle des DbRep-Device festgelegt. Die Standardrolle ist &amp;quot;Client&amp;quot;. Mit der Änderung der Rolle in &amp;quot;Agent&amp;quot; wird das Device veranlasst auf Umbenennungen von Geräten in der FHEM Installation zu reagieren.&lt;br /&gt;
&lt;br /&gt;
Durch den DbRep-Agenten werden folgende Features aktiviert wenn ein Gerät in FHEM mit &amp;quot;rename&amp;quot; umbenannt wird: &lt;br /&gt;
&lt;br /&gt;
*  in der dem DbRep-Agenten zugeordneten Datenbank (Internal Database) wird nach Datensätzen mit dem alten Gerätenamen gesucht und dieser Gerätename in allen betroffenen Datensätzen in den neuen Namen geändert.&lt;br /&gt;
&lt;br /&gt;
* in dem DbRep-Agenten zugeordneten DbLog-Device wird in der Definition das alte durch das umbenannte Device ersetzt. Dadurch erfolgt ein weiteres Logging des umbenannten Device in der Datenbank. &lt;br /&gt;
&lt;br /&gt;
* in den existierenden DbRep-Definitionen vom Typ &amp;quot;Client&amp;quot; wird ein evtl. gesetztes Attribut &amp;quot;device = alter Devicename&amp;quot; in &amp;quot;device = neuer Devicename&amp;quot; geändert. Dadurch werden Auswertungsdefinitionen bei Geräteumbenennungen automatisch konsistent gehalten. &lt;br /&gt;
&lt;br /&gt;
Mit der Änderung in einen Agenten sind folgende Restriktionen verbunden, die mit dem Setzen des Attributes &amp;quot;role = Agent&amp;quot; eingeschaltet und geprüft werden: &lt;br /&gt;
&lt;br /&gt;
*  es kann nur einen Agenten pro Datenbank in der FHEM-Installation geben. Ist mehr als eine Datenbank mit DbLog definiert, können ebenso viele DbRep-Agenten eingerichtet werden&lt;br /&gt;
&lt;br /&gt;
* mit der Umwandlung in einen Agenten wird nur noch das Set-Komando &amp;quot;renameDevice&amp;quot; verfügbar sein sowie nur ein eingeschränkter Satz von DbRep-spezifischen Attributen zugelassen. Wird ein DbRep-Device vom bisherigen Typ &amp;quot;Client&amp;quot; in einen Agenten geändert, werden evtl. gesetzte und nun nicht mehr zugelassene Attribute glöscht. &lt;br /&gt;
&lt;br /&gt;
Die Aktivitäten wie Datenbankänderungen bzw. Änderungen an anderen DbRep-Definitionen werden im Logfile mit verbose=3 protokolliert. Damit die renameDevice-Funktion bei großen Datenbanken nicht in ein timeout läuft, sollte das Attribut &amp;quot;timeout&amp;quot; entsprechend dimensioniert werden. Wie alle Datenbankoperationen des Moduls wird auch das Autorename nonblocking ausgeführt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel&#039;&#039;&#039; für die Definition eines DbRep-Device als Agent:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.Agent DbRep LogDB&lt;br /&gt;
attr Rep.Agent devStateIcon connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.Agent icon security&lt;br /&gt;
attr Rep.Agent role Agent&lt;br /&gt;
attr Rep.Agent room DbLog&lt;br /&gt;
attr Rep.Agent showproctime 1&lt;br /&gt;
attr Rep.Agent stateFormat { ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;state&amp;quot;, undef) eq &amp;quot;running&amp;quot; ? &amp;quot;renaming&amp;quot; : ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;state&amp;quot;, undef). &amp;quot; » ProcTime: &amp;quot;.ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;sql_processing_time&amp;quot;, undef).&amp;quot; sec&amp;quot;}&lt;br /&gt;
attr Rep.Agent timeout 86400&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== hilfreiche SQL Statements ==&lt;br /&gt;
&lt;br /&gt;
In dieser Rubrik werden SQL-Statements zusammengetragen, die User für ihre Auswertungen hilfreich fanden und anderen Anwendern zur Verfügung stellen.&lt;br /&gt;
&lt;br /&gt;
Im Folgenden wird MySQL auch stellvertretend für MariaDB verwendet.&lt;br /&gt;
&lt;br /&gt;
Die Statements können mit dem Befehl:&lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;DbRep&amp;gt; sqlCmd &amp;lt;SQL-Statement&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ausgeführt werden. Für die Anpassung der Ergebnisdarstellung ist das Attribut &#039;&#039;&#039;sqlResultFormat&#039;&#039;&#039; verfügbar.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis: &amp;lt;br&amp;gt; &#039;&#039;&#039;&lt;br /&gt;
Die Statements sind durch den DbRep-Modulautor nicht in jedem Fall getestet und dem Anwender obliegt vor Anwendung der Statements eine Datenbanksicherung durchzuführen um im Fehlerfall diese wieder herstellen zu können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Den ersten und den letzten Wert eines Zeitraums selektieren bzw. deren Differenz (MySQL) ===&lt;br /&gt;
Beispiel 1:&lt;br /&gt;
&lt;br /&gt;
Den ersten und letzten Wert der durch die DB-Variablen bestimmten Parameter ausgegeben. &lt;br /&gt;
&lt;br /&gt;
Verwendete DB-Variablen (@) : begin_time, end_time, device und reading&lt;br /&gt;
 SET @begin_time=&#039;2020-06-02 12:30:00&#039;;SET @end_time=&#039;2020-06-02 18:00:00&#039;;&lt;br /&gt;
 SET @device=&#039;shelly02&#039;;SET @reading=&#039;energy_0&#039;;&lt;br /&gt;
 &lt;br /&gt;
 SELECT TIMESTAMP,READING,round(VALUE/1000,0) AS VALUE&lt;br /&gt;
    FROM (&lt;br /&gt;
      (SELECT TIMESTAMP,READING,VALUE FROM history WHERE DEVICE = @device AND READING = @reading AND TIMESTAMP &amp;gt;= @begin_time and TIMESTAMP &amp;lt;= @end_time ORDER BY TIMESTAMP LIMIT 1)&lt;br /&gt;
      UNION ALL &lt;br /&gt;
      (SELECT TIMESTAMP,READING,VALUE FROM history WHERE DEVICE = @device AND READING = @reading AND TIMESTAMP &amp;gt;= @begin_time and TIMESTAMP &amp;lt;= @end_time ORDER BY TIMESTAMP DESC LIMIT 1)&lt;br /&gt;
   ) AS X1;&lt;br /&gt;
 &lt;br /&gt;
 +---------------------+----------+-------+&lt;br /&gt;
 | TIMESTAMP           | READING  | VALUE |&lt;br /&gt;
 +---------------------+----------+-------+&lt;br /&gt;
 | 2020-06-02 12:31:01 | energy_0 |    69 |&lt;br /&gt;
 | 2020-06-02 16:31:05 | energy_0 |    73 |&lt;br /&gt;
 +---------------------+----------+-------+&lt;br /&gt;
Beispiel 2:&lt;br /&gt;
&lt;br /&gt;
Berechnung der Differenz vom 1. Beispiel&lt;br /&gt;
&lt;br /&gt;
Verwendete DB-Variablen (@) : begin_time, end_time, device und reading&lt;br /&gt;
&lt;br /&gt;
Hierbei ist zu beachten, dass die einzelnen Selects vertauscht wurden um den TIMESTAMP des ersten Select zu bekommen.&lt;br /&gt;
&lt;br /&gt;
Die Subtraktion wurde durch &amp;quot;mal Minus Eins&amp;quot; des kleineren Wertes erreicht.&amp;lt;pre&amp;gt;&lt;br /&gt;
SET @begin_time=&#039;2020-06-02 12:30:00&#039;;SET @end_time=&#039;2020-06-02 18:00:00&#039;;&lt;br /&gt;
SET @device=&#039;shelly02&#039;;SET @reading=&#039;energy_0&#039;;&lt;br /&gt;
&lt;br /&gt;
SELECT TIMESTAMP,READING,round(sum(VALUE)/1000,0) AS VALUE&lt;br /&gt;
   FROM (&lt;br /&gt;
     (SELECT TIMESTAMP,READING,VALUE FROM history WHERE DEVICE = @device AND READING = @reading AND TIMESTAMP &amp;gt;= @begin_time and TIMESTAMP &amp;lt;= @end_time ORDER BY TIMESTAMP DESC LIMIT 1)&lt;br /&gt;
     UNION ALL &lt;br /&gt;
     (SELECT TIMESTAMP,READING,(VALUE * -1) AS VALUE FROM history WHERE DEVICE = @device  AND  READING = @reading AND TIMESTAMP &amp;gt;= @begin_time and TIMESTAMP &amp;lt;= @end_time ORDER BY TIMESTAMP LIMIT 1)&lt;br /&gt;
  ) AS X1;&lt;br /&gt;
&lt;br /&gt;
+---------------------+----------+-------+&lt;br /&gt;
| TIMESTAMP           | READING  | VALUE |&lt;br /&gt;
+---------------------+----------+-------+&lt;br /&gt;
| 2020-06-02 16:31:05 | energy_0 |     4 |&lt;br /&gt;
+---------------------+----------+-------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Formatierung &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 round(sum(VALUE)/1000,0) AS VALUE&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sollte eventuell angepasst werden, da diese natürlich zum zu erwartenden Ergebnis passen muss.&lt;br /&gt;
&lt;br /&gt;
=== Wieviel PV-Leistung wird von einem Starkverbraucher verwendet (MySQL) ===&lt;br /&gt;
Hier wurde versucht aus der Datenbank zu ermitteln, wieviel PV-Leistung für z.B. eine Wärmepumpe übrig bleibt.&lt;br /&gt;
Dabei wurde der grundlegenden Hausverbrauch als erstes in Abzug gebracht und der Rest mit dem Starkverbraucher verrechnet.&lt;br /&gt;
Natürlich kann man solch einen Report nur für einen Verbraucher verwenden, da man ja den Überschuss nur einem zuordnen kann.&lt;br /&gt;
Auch wird hier mit einem Stundendurchschnitt gerechnet, was somit als Näherung angesehen werden muss.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1) Zuerst werden die einzelnen Daten für einen Tag zusammen gesucht&lt;br /&gt;
2) Jeder Datenteil wird mit einem Durchschnitt auf Stundenbasis berechnet&lt;br /&gt;
3) Im JOIN werden alle Datenteile für die relevanten Stunden zusammen geführt. Maßgeblich hierbei ist der Betrieb des Starkverbrauchers&lt;br /&gt;
   zu dem Zeitpunkt wo auch PV-Leistung verfügbar war. Alle anderen Stunden tauchen danach nicht mehr auf. Ist es ein Hybrid WR mit&lt;br /&gt;
   Speicher, dann kann auch in der Nacht eine Unterstützung erfolgen, was im Winter jedoch recht selten sein wird.&lt;br /&gt;
4) Es wird der restliche Hausverbrauch ermittelt. Achtung, der Starkverbraucher ist ein Verbraucher, weshalb die Leistung im Zähler&lt;br /&gt;
   negativ summiert wird. Ein &amp;quot;+consumer_P&amp;quot; ist somit eine Summe mit einem negativen Wert ;-)&lt;br /&gt;
5) Im letzten SELECT werden dann die Daten final aufbereitet und formatiert, auch der prozentuale Anteil der PV-Leistung am gesamten&lt;br /&gt;
   Starkverbrauch wird noch berechnet.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
consumer          = Das Zähler Device des Starkverbrauchers&lt;br /&gt;
consumer_P        = negative Leistungsanzeige in Watt, da es ein Verbraucher ist!&lt;br /&gt;
generator         = Das Wechselrichter Device&lt;br /&gt;
generator_P       = Die AC Ausgangsleistung in Watt&lt;br /&gt;
Home_consumtion_P = Der Hausverbrauch inklusive des Starkverbrauchers in Watt (ebenfalls im generator Device)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die verwendeten Variablen lassen sich im DbRep als Attribut setzen&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlCmdVars	&lt;br /&gt;
SET @date:=&#039;2022-12-07&#039;, @consumer:=&#039;StromZaehler_Heizung&#039;, @consumer_P:=&#039;SMAEM1901401955_Saldo_Wirkleistung&#039;, @generator:=&#039;WR_1&#039;, @generator_P:=&#039;SW_Total_AC_Active_P&#039;, @Home_Consumtion_P:=&#039;SW_Home_own_consumption&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Hier nun das SELECT Statement&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 SET @date:=&#039;2022-12-07&#039;,&lt;br /&gt;
     @consumer:=&#039;StromZaehler_Heizung&#039;, @consumer_P:=&#039;SMAEM1901401955_Saldo_Wirkleistung&#039;,&lt;br /&gt;
     @generator:=&#039;WR_1&#039;, @generator_P:=&#039;SW_Total_AC_Active_P&#039;, @Home_Consumtion_P:=&#039;SW_Home_own_consumption&#039;;&lt;br /&gt;
&lt;br /&gt;
 SELECT&lt;br /&gt;
   X.HOUR,&lt;br /&gt;
   cast(X.generator_P AS decimal(7,2)) AS generator_P,&lt;br /&gt;
   cast(X.Home_Consumption AS decimal(7,2)) AS Home_Consumption,&lt;br /&gt;
   cast(X.PV_after_Home_Consumtion AS decimal(7,2)) AS PV_after_Home_Consumtion,&lt;br /&gt;
   cast(X.consumer_P AS decimal(7,2)) AS consumer_P,&lt;br /&gt;
   if(consumer_P+PV_after_Home_Consumtion &amp;lt;= 0,cast(round(abs(consumer_P+PV_after_Home_Consumtion),2) AS decimal(7,2)),0) AS from_Grid,&lt;br /&gt;
   if(round(consumer_P+PV_after_Home_Consumtion,2) &amp;lt;= 0,round(abs(PV_after_Home_Consumtion*100/consumer_P),0),100) AS Percent&lt;br /&gt;
 FROM (&lt;br /&gt;
   SELECT&lt;br /&gt;
     X1.HOUR,&lt;br /&gt;
     generator_P,&lt;br /&gt;
     round(Home_Consumtion_P+consumer_P,2) AS Home_Consumption,&lt;br /&gt;
     if(Home_Consumtion_P+consumer_P-generator_P &amp;lt; 0,round(abs(Home_Consumtion_P+consumer_P-generator_P),2),0) AS PV_after_Home_Consumtion,&lt;br /&gt;
     consumer_P&lt;br /&gt;
   FROM (&lt;br /&gt;
     SELECT&lt;br /&gt;
       hour(TIMESTAMP) AS HOUR,&lt;br /&gt;
       round(avg(value),2) AS consumer_P&lt;br /&gt;
     FROM history&lt;br /&gt;
     WHERE&lt;br /&gt;
       TIMESTAMP &amp;gt; @date and TIMESTAMP &amp;lt; DATE_ADD(@date,INTERVAL 1 DAY) AND&lt;br /&gt;
       DEVICE = @Consumer AND&lt;br /&gt;
       READING = @consumer_P&lt;br /&gt;
     GROUP BY 1&lt;br /&gt;
     ) X1&lt;br /&gt;
   JOIN (&lt;br /&gt;
     SELECT&lt;br /&gt;
       hour(TIMESTAMP) AS HOUR,&lt;br /&gt;
       round(avg(value),2) AS generator_P&lt;br /&gt;
     FROM history&lt;br /&gt;
     WHERE&lt;br /&gt;
       TIMESTAMP &amp;gt; @date AND TIMESTAMP &amp;lt; DATE_ADD(@date,INTERVAL 1 DAY) AND&lt;br /&gt;
       DEVICE    = @generator AND&lt;br /&gt;
       READING   = @generator_P AND&lt;br /&gt;
       VALUE     &amp;gt; 10&lt;br /&gt;
     GROUP BY 1&lt;br /&gt;
     ) X2&lt;br /&gt;
   JOIN (&lt;br /&gt;
     SELECT&lt;br /&gt;
       hour(TIMESTAMP) AS HOUR,&lt;br /&gt;
       round(avg(value),2) AS Home_Consumtion_P&lt;br /&gt;
     FROM history&lt;br /&gt;
     WHERE&lt;br /&gt;
       TIMESTAMP &amp;gt; @date and TIMESTAMP &amp;lt; DATE_ADD(@date,INTERVAL 1 DAY) AND&lt;br /&gt;
       DEVICE  = @generator AND&lt;br /&gt;
       READING = @Home_Consumtion_P AND&lt;br /&gt;
       VALUE   &amp;gt; 10&lt;br /&gt;
     GROUP BY 1&lt;br /&gt;
     ) X3&lt;br /&gt;
   ON    X1.HOUR = X2.HOUR&lt;br /&gt;
     AND X1.HOUR = X3.HOUR&lt;br /&gt;
   ) X;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Temperaturdifferenz eines Pufferspeichers über die Zeit ermitteln (MySQL) ===&lt;br /&gt;
Beispiel 1:&lt;br /&gt;
&lt;br /&gt;
Temperaturdifferenz des Pufferspeichers über die Zeit in Minuten&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;Referenz zum DBRep : set [device] sqlSpecial readingsDifferenceByTimeDelta&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendete DB-Variablen (@) :  device und reading, diff und delta werden für Berechnungen benötigt und sind bei erneutem Aufruf zurück zu setzen&lt;br /&gt;
&lt;br /&gt;
Anmerkungen:&lt;br /&gt;
&lt;br /&gt;
- Der Zeitraum wird in diesem Beispiel als die letzten 24 Stunden festgelegt&lt;br /&gt;
&lt;br /&gt;
- Die Zeitdifferenz für die Änderung steht in der Spalte DELTA in Minuten&lt;br /&gt;
&lt;br /&gt;
- In FHEM wurde mit event-on-change-reading ins DBLog geschrieben&lt;br /&gt;
&lt;br /&gt;
- Bei dieser Ausgabe kann man erkennen, dass die Temperaturänderung im Wärmespeicher oft sehr klein ist, was ja auch so gewollt ist&lt;br /&gt;
&lt;br /&gt;
- Um 14:00 Uhr beginnt die Wärmepumpe das Warmwasser wieder aufzuheizen&amp;lt;pre&amp;gt;&lt;br /&gt;
SET @device=&#039;Heizung&#039;;SET @reading=&#039;hotWaterTemperature&#039;;&lt;br /&gt;
SET @diff=0;SET @delta=NULL;&lt;br /&gt;
&lt;br /&gt;
SELECT t1.TIMESTAMP,t1.READING,t1.VALUE,t1.DIFF,t1.DELTA&lt;br /&gt;
  FROM&lt;br /&gt;
    (&lt;br /&gt;
SELECT TIMESTAMP,READING,VALUE,&lt;br /&gt;
       if(@diff = 0,NULL, cast((VALUE-@diff) AS DECIMAL(3,1))) AS DIFF,&lt;br /&gt;
       @diff:=VALUE                                            AS curr_V,&lt;br /&gt;
       TIMESTAMPDIFF(MINUTE,@delta,TIMESTAMP)                  AS DELTA,&lt;br /&gt;
       @delta:=TIMESTAMP                                       AS curr_T&lt;br /&gt;
  FROM  history&lt;br /&gt;
  WHERE DEVICE     = @device  AND&lt;br /&gt;
        READING    = @reading AND&lt;br /&gt;
        TIMESTAMP &amp;gt;= NOW() - INTERVAL 1 DAY&lt;br /&gt;
  ORDER BY TIMESTAMP&lt;br /&gt;
    ) t1;&lt;br /&gt;
+---------------------+---------------------+-------+------+-------+&lt;br /&gt;
| TIMESTAMP           | READING             | VALUE | DIFF | DELTA |&lt;br /&gt;
+---------------------+---------------------+-------+------+-------+&lt;br /&gt;
| 2020-06-04 10:05:20 | hotWaterTemperature | 46.5  | NULL |  NULL |&lt;br /&gt;
| 2020-06-04 10:35:31 | hotWaterTemperature | 46.4  | -0.1 |    30 |&lt;br /&gt;
| 2020-06-04 11:00:31 | hotWaterTemperature | 46.2  | -0.2 |    25 |&lt;br /&gt;
snip...&lt;br /&gt;
| 2020-06-04 13:50:42 | hotWaterTemperature | 44.5  | -1.0 |     5 |&lt;br /&gt;
| 2020-06-04 13:55:42 | hotWaterTemperature | 44.0  | -0.5 |     5 |&lt;br /&gt;
| 2020-06-04 14:00:42 | hotWaterTemperature | 43.9  | -0.1 |     5 |&lt;br /&gt;
| 2020-06-04 14:10:42 | hotWaterTemperature | 41.7  | -1.8 |     5 |&lt;br /&gt;
snip...&lt;br /&gt;
| 2020-06-04 14:51:05 | hotWaterTemperature | 51.3  |  0.1 |     5 |&lt;br /&gt;
| 2020-06-04 17:01:15 | hotWaterTemperature | 51.2  | -0.1 |   130 |&lt;br /&gt;
snip...&lt;br /&gt;
| 2020-06-04 22:10:30 | hotWaterTemperature | 50.5  | -0.2 |     5 |&lt;br /&gt;
snip...&lt;br /&gt;
| 2020-06-05 11:30:45 | hotWaterTemperature | 46.1  | -0.1 |    25 |&lt;br /&gt;
+---------------------+---------------------+-------+------+-------+&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;Beispiel 2:&lt;br /&gt;
&lt;br /&gt;
Summieren der Temperaturdifferenz auf Stundenbasis&lt;br /&gt;
&lt;br /&gt;
Verwendete DB-Variablen (@) :  device und reading, diff und delta werden für Berechnungen benötigt und sind bei erneutem Aufruf zurück zu setzen&lt;br /&gt;
&lt;br /&gt;
Anmerkungen:&lt;br /&gt;
&lt;br /&gt;
- Der Zeitraum wird in diesem Beispiel als die letzten 24 Stunden festgelegt&lt;br /&gt;
&lt;br /&gt;
- Die Zeitdifferenz wird bei diesem Beispiel nicht ausgegeben&lt;br /&gt;
&lt;br /&gt;
- Alle Differenzen nach der Vollen Stunde werden der nächsten Stunde zugeschlagen&lt;br /&gt;
&lt;br /&gt;
- In FHEM wurde mit event-on-change-reading ins DBLog geschrieben&lt;br /&gt;
&lt;br /&gt;
- Um 14:00 Uhr beginnt die Wärmepumpe das Warmwasser wieder aufzuheizen&lt;br /&gt;
&lt;br /&gt;
- Bei diesem Wärmespeicher sind Temperaturschwankungen im Bereich von Null Komma irgendwas, als Messwert recht ungenau, was im Beispiel 1 zu der Vielzahl an Messwerten geführt hat. Durch Beispiel 2 auf Stundenbasis lässt sich bereits mehr erkennen.  Hier wäre der Wärmepumpeneinsatz um 14:00 Uhr mit dem Pumpenvorlauf (-1.8) und der Lauf der Zirkulationspumpe um 8:00 und 8:30 Uhr zu sehen, was den Wärmespeicher um 1,2 Grad abgekühlt hat.&amp;lt;pre&amp;gt;&lt;br /&gt;
SET @device=&#039;Heizung&#039;;SET @reading=&#039;hotWaterTemperature&#039;;&lt;br /&gt;
SET @diff=0;SET @delta=NULL;&lt;br /&gt;
&lt;br /&gt;
SELECT xTIMESTAMP AS TIMESTAMP,READING,xVALUE AS VALUE,xDIFF AS DIFF&lt;br /&gt;
  FROM&lt;br /&gt;
    (SELECT DATE_FORMAT(DATE_ADD(t1.TIMESTAMP,INTERVAL (IF(MINUTE(t1.TIMESTAMP) &amp;gt; 0, 60, 0)-MINUTE(t1.TIMESTAMP)) MINUTE),&#039;%Y-%m-%d %H:00:00&#039;) AS xTIMESTAMP,&lt;br /&gt;
            t1.READING,&lt;br /&gt;
            round(t1.VALUE) AS xVALUE,&lt;br /&gt;
            sum(t1.DIFF)    AS xDIFF,&lt;br /&gt;
            sum(t1.DELTA)   AS xDELTA&lt;br /&gt;
       FROM&lt;br /&gt;
         (SELECT TIMESTAMP,READING,VALUE,&lt;br /&gt;
                 if(@diff = 0,NULL, cast((VALUE-@diff) AS DECIMAL(3,1))) AS DIFF,&lt;br /&gt;
                 @diff:=VALUE                                            AS curr_V,&lt;br /&gt;
                 TIMESTAMPDIFF(MINUTE,@delta,TIMESTAMP)                  AS DELTA,&lt;br /&gt;
                 @delta:=TIMESTAMP                                       AS curr_T&lt;br /&gt;
            FROM  history&lt;br /&gt;
            WHERE DEVICE     = @device  AND&lt;br /&gt;
                  READING    = @reading AND&lt;br /&gt;
                  TIMESTAMP &amp;gt;= NOW() - INTERVAL 1 DAY&lt;br /&gt;
            ORDER BY TIMESTAMP&lt;br /&gt;
         ) t1&lt;br /&gt;
       GROUP BY xTIMESTAMP WITH ROLLUP) x1&lt;br /&gt;
&lt;br /&gt;
   WHERE xDELTA     IS NOT NULL AND&lt;br /&gt;
         xTIMESTAMP IS NOT NULL;&lt;br /&gt;
+---------------------+---------------------+-------+------+&lt;br /&gt;
| TIMESTAMP           | READING             | VALUE | DIFF |&lt;br /&gt;
+---------------------+---------------------+-------+------+&lt;br /&gt;
| 2020-06-04 12:00:00 | hotWaterTemperature |    46 | -0.1 |&lt;br /&gt;
| 2020-06-04 13:00:00 | hotWaterTemperature |    46 | -0.4 |&lt;br /&gt;
| 2020-06-04 14:00:00 | hotWaterTemperature |    44 | -1.8 |&lt;br /&gt;
| 2020-06-04 15:00:00 | hotWaterTemperature |    51 |  7.4 |&lt;br /&gt;
| 2020-06-04 18:00:00 | hotWaterTemperature |    51 | -0.1 |&lt;br /&gt;
snip...&lt;br /&gt;
| 2020-06-05 07:00:00 | hotWaterTemperature |    48 | -0.3 |&lt;br /&gt;
| 2020-06-05 08:00:00 | hotWaterTemperature |    48 | -0.2 |&lt;br /&gt;
| 2020-06-05 09:00:00 | hotWaterTemperature |    48 | -1.2 |&lt;br /&gt;
| 2020-06-05 10:00:00 | hotWaterTemperature |    47 | -0.3 |&lt;br /&gt;
snip...&lt;br /&gt;
+---------------------+---------------------+-------+------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Alle aktuellsten readings eines Device im letzten Tagesverlauf (MySQL) ===&lt;br /&gt;
Beispiel :&lt;br /&gt;
&lt;br /&gt;
Verwendete DB-Variablen (@) :  device&lt;br /&gt;
&lt;br /&gt;
Anmerkungen:&lt;br /&gt;
&lt;br /&gt;
- Der Zeitraum wird in diesem Beispiel als die letzten 24 Stunden festgelegt&lt;br /&gt;
&lt;br /&gt;
- In FHEM wurde mit event-on-change-reading ins DBLog geschrieben, wodurch nicht alle readings immer zeitlich in der DB aufeinander folgen oder einige im Zeitraum mehrfach geschrieben wurden. Mit diesem Aufruf erscheinen immer die letzten Aktualisierungen.&lt;br /&gt;
&lt;br /&gt;
- Sollten einige readings nicht erscheinen, so wurden diese vor dem Zeitraum letztmalig aktualisiert. Mit &amp;quot;INTERVAL [n] DAY könnten diese eventuell auch noch gefunden werden. Bitte hier die Laufzeit beachten!&amp;lt;pre&amp;gt;&lt;br /&gt;
SET @device = &#039;Heizung&#039;;&lt;br /&gt;
&lt;br /&gt;
SELECT t1.TIMESTAMP,t1.DEVICE,t1.READING,t1.VALUE&lt;br /&gt;
  FROM history t1&lt;br /&gt;
  INNER JOIN&lt;br /&gt;
   (select max(TIMESTAMP) AS TIMESTAMP,DEVICE,READING&lt;br /&gt;
      from history&lt;br /&gt;
      where DEVICE    = @device and&lt;br /&gt;
            TIMESTAMP &amp;gt; NOW() - INTERVAL 1 DAY&lt;br /&gt;
      group by READING) x&lt;br /&gt;
  ON x.TIMESTAMP = t1.TIMESTAMP AND&lt;br /&gt;
     x.DEVICE    = t1.DEVICE    AND&lt;br /&gt;
     x.READING   = t1.READING;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Device / Reading Daten in eine CSV-Datei exportieren (MySQL) ===&lt;br /&gt;
&lt;br /&gt;
Grundsätzlich gibt es für diesen Zweck das eingebaute Set-Kommando &#039;&#039;&#039;exportToFile&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit ist die Verwendung von spezifischen Datenbankkommandos, die mit Set &#039;&#039;&#039;sqlCmd&#039;&#039;&#039; ausgeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird ein CSV File geschrieben, welches die Daten des Devices &#039;SMA_Energymeter&#039; enthält.&lt;br /&gt;
Wichtig ist, nicht zur Trennung von SQL-Befehlen dienende Semikolons durch Verdopplung zu escapen (z.B. im String &amp;quot;...TERMINATED BY &#039;;;&#039;...&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SET @TS = DATE_FORMAT(NOW(),&#039;_%Y_%m_%d&#039;);&lt;br /&gt;
&lt;br /&gt;
SET @FOLDER = &#039;/volume1/ApplicationBackup/&#039;;&lt;br /&gt;
SET @PREFIX = &#039;export&#039;;&lt;br /&gt;
SET @EXT    = &#039;.csv&#039;;&lt;br /&gt;
&lt;br /&gt;
SET @CMD = CONCAT(&amp;quot;	SELECT *&lt;br /&gt;
			FROM `fhemtest`.`history`&lt;br /&gt;
			WHERE `DEVICE`=&#039;SMA_Energymeter&#039; AND TIMESTAMP &amp;gt; DATE_SUB(CURRENT_DATE(),INTERVAL 1 DAY)&lt;br /&gt;
			INTO OUTFILE &#039;&amp;quot;,@FOLDER,@PREFIX,@TS,@EXT,&amp;quot;&#039;&lt;br /&gt;
			FIELDS ENCLOSED BY &#039;\&amp;quot;&#039;&lt;br /&gt;
			TERMINATED BY &#039;;;&#039;&lt;br /&gt;
			ESCAPED BY &#039;\&amp;quot;&#039;&amp;quot;,&amp;quot; &lt;br /&gt;
			LINES TERMINATED BY &#039;\r\n&#039;;;&lt;br /&gt;
		&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
PREPARE statement FROM @CMD;&lt;br /&gt;
&lt;br /&gt;
EXECUTE statement;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039;  der verwendete Datenbank-User benötigt das &#039;&#039;&#039;FILE&#039;&#039;&#039; Recht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Gewichtete Mittelwerte von Zeitreihen (MySQL) ===&lt;br /&gt;
Zeitreihen in FHEM sind Messwerte (z.B. Temperatur, Stromverbrauch, PV-Produktion ...), die i.A. nicht zu äquidistanten Zeitpunkten ermittelt und protokolliert werden, sondern nur bei Änderung der Messgröße. Für eine Zusammenfassung (&amp;quot;Aggregation&amp;quot;) zum Zwecke statistischer Auswertung oder einfach zur Datenreduktion sollte daher bei einer Mittelwertbildung auf zeitlich gewichtete Mittelung zurückgegriffen werden. Ein interessierender Gesamtzeitraum wird dazu in einzelne Mittelungsintervalle (sogen. &#039;&#039;bins&#039;&#039;) aufgeteilt, für die dann der gewichtete Mittelwert berechnet wird. &lt;br /&gt;
&lt;br /&gt;
Die (non-blocking) DbRep-Funktion &#039;set averageValue&#039; macht, in Verbindung mit den Attributen &#039;aggregation&#039; (Auswahl einer Aggregationsperiode) und &#039;averageCalcForm&#039;=&#039;avgTimeWeightMean&#039; genau das. Dabei hat man als &#039;&#039;bin&#039;&#039;-Länge &#039;minute&#039;, &#039;hour&#039;, &#039;day&#039;, &#039;week&#039;, &#039;month&#039; und &#039;year&#039; zur Verfügung. &lt;br /&gt;
&lt;br /&gt;
Der folgende &#039;&#039;SQL-Query 1&#039;&#039; kennt als &#039;&#039;bin&#039;&#039;-Längen auch Viertelstunden (&#039;qhour&#039;), Vierteltage (&#039;qday&#039;) und Quartal (&#039;qyear&#039;). Die Parameter @sortformat=[&amp;quot;minute&amp;quot;|&amp;quot;qhour&amp;quot;|&amp;quot;hour&amp;quot;|&amp;quot;qday&amp;quot;|&amp;quot;day&amp;quot;|&amp;quot;week&amp;quot;|&amp;quot;month&amp;quot;|&amp;quot;qyear&amp;quot;|&amp;quot;year&amp;quot;] und @weighted=[&amp;quot;yes&amp;quot;|&amp;quot;no&amp;quot;] müssen vor Ausführung des Codes mit dem Attribut sqlCmdVars gesetzt werden, z.B. &lt;br /&gt;
 &amp;lt;small&amp;gt;set &amp;lt;Your_DbRep_device&amp;gt; sqlCmdVars SET @sortformat=&amp;quot;hour&amp;quot;, @weighted=&amp;quot;yes&amp;quot;, @count=&amp;lt;n&amp;gt;;&amp;lt;/small&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-customtoggle-query1 wikia-menu-button&amp;quot;&amp;gt;&#039;&#039;&#039;&#039;&#039;SQL-Query 1&#039;&#039; ein/ausblenden&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot; id=&amp;quot;mw-customcollapsible-query1&amp;gt;&amp;lt;small&amp;gt;&lt;br /&gt;
 SELECT&lt;br /&gt;
   avgtim, &lt;br /&gt;
   CASE @weighted &lt;br /&gt;
     WHEN &amp;quot;yes&amp;quot; THEN &lt;br /&gt;
       CAST((SUM(val * weight)/SUM(weight)) AS DECIMAL(12,4)) &lt;br /&gt;
     ELSE &lt;br /&gt;
       CAST(sum(val) / count(val) AS DECIMAL(12,4)) &lt;br /&gt;
   END AS avrg&lt;br /&gt;
 FROM (&lt;br /&gt;
   SELECT  &lt;br /&gt;
     tim,&lt;br /&gt;
     avgtim,&lt;br /&gt;
     CASE @sortformat &lt;br /&gt;
       WHEN &amp;quot;week&amp;quot; THEN &lt;br /&gt;
         date_format(tim, &amp;quot;%Y-%u 00:00:00&amp;quot;)&lt;br /&gt;
       ELSE &lt;br /&gt;
         avgtim &lt;br /&gt;
     END AS grouptim,&lt;br /&gt;
     CASE&lt;br /&gt;
       WHEN avgtim!=nextavgtim THEN &lt;br /&gt;
         to_seconds(nextavgtim)-to_seconds(tim)&lt;br /&gt;
       WHEN avgtim!=preavgtim THEN &lt;br /&gt;
         to_seconds(nexttim)-to_seconds(avgtim)&lt;br /&gt;
       ELSE &lt;br /&gt;
         to_seconds(nexttim)-to_seconds(tim) &lt;br /&gt;
     END AS weight,  &lt;br /&gt;
     CASE&lt;br /&gt;
       WHEN avgtim!=preavgtim THEN &lt;br /&gt;
         CASE @weighted WHEN &amp;quot;yes&amp;quot;&lt;br /&gt;
           THEN&lt;br /&gt;
             CASE &lt;br /&gt;
               WHEN avgtim!=nextavgtim THEN &lt;br /&gt;
                 (preval*(to_seconds(tim)-to_seconds(avgtim)) + val*(to_seconds(nextavgtim)-to_seconds(tim)))/ &lt;br /&gt;
                   (to_seconds(nextavgtim)-to_seconds(avgtim))&lt;br /&gt;
               ELSE &lt;br /&gt;
                 (preval*(to_seconds(tim)-to_seconds(avgtim)) + val*(to_seconds(nexttim)-to_seconds(tim)))/&lt;br /&gt;
                   (to_seconds(nexttim)-to_seconds(avgtim))&lt;br /&gt;
             END&lt;br /&gt;
           ELSE &lt;br /&gt;
             val&lt;br /&gt;
         END&lt;br /&gt;
       ELSE &lt;br /&gt;
         val&lt;br /&gt;
     END AS val&lt;br /&gt;
   FROM ( &lt;br /&gt;
     SELECT &lt;br /&gt;
       tim, &lt;br /&gt;
       nexttim, &lt;br /&gt;
       val, &lt;br /&gt;
       preval, &lt;br /&gt;
       avgtim,&lt;br /&gt;
       LAG(avgtim) OVER (ORDER BY tim) AS preavgtim,&lt;br /&gt;
       LEAD(avgtim) OVER (ORDER BY tim) AS nextavgtim&lt;br /&gt;
     FROM ( &lt;br /&gt;
       SELECT &lt;br /&gt;
         timestamp AS tim, &lt;br /&gt;
         LEAD(timestamp) OVER (ORDER BY timestamp) AS nexttim, &lt;br /&gt;
         value AS val, &lt;br /&gt;
         LAG(value) OVER (ORDER BY timestamp) AS preval,&lt;br /&gt;
         CASE @sortformat&lt;br /&gt;
           WHEN &amp;quot;minute&amp;quot; THEN &lt;br /&gt;
             date_format(timestamp, &amp;quot;%Y-%m-%d %H:%i:00&amp;quot;) &lt;br /&gt;
           WHEN &amp;quot;qhour&amp;quot;  THEN &lt;br /&gt;
             concat(date_format(timestamp, &amp;quot;%Y-%m-%d %H:&amp;quot;),LPAD(15*(date_format(timestamp,&amp;quot;%i&amp;quot;) div 15),2,&amp;quot;0&amp;quot;),&amp;quot;:00&amp;quot;) &lt;br /&gt;
           WHEN &amp;quot;hour&amp;quot; THEN &lt;br /&gt;
             date_format(timestamp, &amp;quot;%Y-%m-%d %H:00:00&amp;quot;) &lt;br /&gt;
           WHEN &amp;quot;qday&amp;quot; THEN &lt;br /&gt;
             concat(date_format(timestamp, &amp;quot;%Y-%m-%d &amp;quot;),LPAD(6*(date_format(timestamp, &amp;quot;%H&amp;quot;) div 6),2,&amp;quot;0&amp;quot;),&amp;quot;:00:00&amp;quot;)&lt;br /&gt;
           WHEN &amp;quot;day&amp;quot; THEN &lt;br /&gt;
             date_format(timestamp, &amp;quot;%Y-%m-%d 00:00:00&amp;quot;) &lt;br /&gt;
           WHEN &amp;quot;week&amp;quot; THEN &lt;br /&gt;
             date_format(timestamp, &amp;quot;%Y-%m-%d 00:00:00&amp;quot;) &lt;br /&gt;
           WHEN &amp;quot;month&amp;quot; THEN &lt;br /&gt;
             date_format(timestamp, &amp;quot;%Y-%m-01 00:00:00&amp;quot;) &lt;br /&gt;
           WHEN &amp;quot;qyear&amp;quot; THEN &lt;br /&gt;
             concat(date_format(timestamp, &amp;quot;%Y-&amp;quot;),LPAD(3*(date_format(timestamp, &amp;quot;%m&amp;quot;) div 3),2,&amp;quot;0&amp;quot;),&amp;quot;-01 00:00:00&amp;quot;)&lt;br /&gt;
           ELSE &lt;br /&gt;
             date_format(timestamp, &amp;quot;%Y-01-01 00:00:00&amp;quot;)&lt;br /&gt;
         END AS avgtim      &lt;br /&gt;
       FROM &lt;br /&gt;
         history WHERE TIMESTAMP BETWEEN §timestamp_begin§ AND §timestamp_end§ AND §device§ AND §reading§ ORDER BY timestamp &lt;br /&gt;
     ) select3 &lt;br /&gt;
   ) select2&lt;br /&gt;
 ) select1 GROUP BY grouptim&lt;br /&gt;
&amp;lt;/small&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Die Auswahl von &#039;&#039;device&#039;&#039;, &#039;&#039;reading&#039;&#039; und Zeitraum erfolgt über die üblichen Attribute. Es darf nur ein &#039;&#039;device&#039;&#039; und ein &#039;&#039;reading&#039;&#039; gewählt werden. Für den Zeitraum btte nur &#039;&#039;timestamp_begin&#039;&#039; und &#039;&#039;timestamp_end&#039;&#039; nutzen. &lt;br /&gt;
&lt;br /&gt;
Will man keine festen Zeitraster, kann man den Gesamtzeitraum auch einfach in eine Anzahl &#039;&#039;count&#039;&#039; von bins aufteilen. Dafür steht der Parameter @count im obigen sqlCmdVars statement (für Query 1 hat er keine Bedeutung). &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-customtoggle-query2 wikia-menu-button&amp;quot;&amp;gt;&#039;&#039;&#039;&#039;&#039;SQL-Query 2&#039;&#039; ein/ausblenden&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot; id=&amp;quot;mw-customcollapsible-query2&amp;gt;&amp;lt;small&amp;gt;&lt;br /&gt;
 SELECT &lt;br /&gt;
   tim, &lt;br /&gt;
   CASE &lt;br /&gt;
     WHEN @weighted=&amp;quot;yes&amp;quot; THEN &lt;br /&gt;
       CAST(sum(val * (to_seconds(nexttim)-to_seconds(tim))) / sum((to_seconds(nexttim)-to_seconds(tim))) AS DECIMAL(12,4))&lt;br /&gt;
     ELSE CAST(sum(val) / count(val) AS DECIMAL(12,4)) &lt;br /&gt;
   END AS avrg&lt;br /&gt;
 FROM ( &lt;br /&gt;
   SELECT &lt;br /&gt;
     timestamp as tim, &lt;br /&gt;
     value as val, &lt;br /&gt;
     LEAD(timestamp) over (order by timestamp) nexttim, &lt;br /&gt;
     truncate(@count * (to_seconds(timestamp)-to_seconds(§timestamp_begin§)) / (to_seconds(§timestamp_end§)-to_seconds(§timestamp_begin§)),0)/@count as avgtim &lt;br /&gt;
   FROM &lt;br /&gt;
     history WHERE TIMESTAMP BETWEEN §timestamp_begin§ AND §timestamp_end§ AND §device§ AND §reading§  &lt;br /&gt;
 ) select1 group by avgtim&lt;br /&gt;
&amp;lt;/small&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur Ausführung der Queries den Code entweder ins Eingabefeld hinter set sqlCmd (bzw. sqlCmdBlocking) kopieren und dann mit &#039;&#039;set&#039;&#039; ausführen, oder per &#039;&#039;set &amp;lt;Your_DbRep_device&amp;gt; sqlCmd &amp;lt;kopierter Code&amp;gt;&#039;&#039; in der FHEM Kommandozeile ausführen.&lt;br /&gt;
&lt;br /&gt;
Getestet wurden diese MySQL Beispiele mit MariaDB 10.3&lt;br /&gt;
&lt;br /&gt;
Siehe auch im Forum: https://forum.fhem.de/index.php/topic,53584.msg1254036.html#msg1254036&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Praxisbeispiele / Hinweise und Lösungsansätze für verschiedene Aufgaben ==&lt;br /&gt;
=== Definieren eines DbRep-Devices ===&lt;br /&gt;
[[Bild:DbRep_initialized.PNG|right|thumb|400px|]]&lt;br /&gt;
&lt;br /&gt;
Das DbRep-Device wird bei der Definition mit der DbLog-Instanz verbunden, in deren angeschlossener Datenbank später die Auswertungen und Operationen stattfinden sollen. Es ist also nicht die Datenbank selbst, sondern das vorher definierte DbLog-Device anzugeben.&lt;br /&gt;
Die Definition erfolgt z.B. durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.Energy DbRep LogDB       #LogDB ist das zu verbindende DbLog-Device&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der Definition werden die Zugangsdaten aus der DbLog-Instanz gelesen und das DbRep-Device mit der Datenbank verbunden. Nach der Definition ist der Status &amp;quot;initialized&amp;quot;. Die Verbindung zur Datenbank wird mit der ersten abzuarbeitenden Aufgabe hergestellt. Das Verhalten kann mit dem &#039;&#039;&#039;Attribut fastStart&#039;&#039;&#039; beeinflusst werden.&lt;br /&gt;
Zu welcher Datanbank das DbRep-Device sich verbunden hat, zeigt das &#039;&#039;Internal&#039;&#039; DATABASE.&lt;br /&gt;
&lt;br /&gt;
Damit ist das DbRep-Device grundsätzlich einsatzbereit, aber noch nicht praxistauglich. Werden keine weiteren Eingrenzungen angegeben, kann mit dem so definierten Device mit dem Befehl&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Rep.Energy countEntries&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
die gesamte Anzahl der Datensätze in der Datenbank ermittelt werden.&lt;br /&gt;
Für eine weitere Verwendung sind weitere &#039;&#039;Attribute&#039;&#039; zu setzen.&lt;br /&gt;
Um die Funktionen von &amp;quot;Rep.Energy&amp;quot; nur auf z.B. Datensätze in der Datenbank anzuwenden die &amp;quot;STP_5000&amp;quot; im Feld &amp;quot;DEVICE&amp;quot; enthalten, wird das &#039;&#039;&#039;Attribut&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.Energy device STP_5000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt. Weitere Begrenzungen der gerätespezifischen Selektion erfolgt durch das &#039;&#039;&#039;Attribut&#039;&#039;&#039; &amp;quot;reading&amp;quot;. So wird durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.Energy reading etotal&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Bild:Rep_configured.PNG|right|thumb|300px|]]&lt;br /&gt;
festgelegt, dass sich die (allermeisten) Operationen auf die Kombination aus dem &amp;quot;device STP_5000&amp;quot; und dem &amp;quot;reading etotal&amp;quot; beziehen.&lt;br /&gt;
Eine zeitliche Eingrenzung der Ergebnisse erfolgt durch die &#039;&#039;&#039;Attribute&#039;&#039;&#039; &amp;quot;timeDiffToNow&amp;quot;, &amp;quot;timeOlderThan&amp;quot;, &amp;quot;timestamp_begin&amp;quot;, &amp;quot;timestamp_end&amp;quot;.&lt;br /&gt;
In dem Beispiel sollen sich die Selektionsergebnisse immer auf die letzten 120 Minuten beziehen.&lt;br /&gt;
Dazu wird das Attribut&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.Energy timeDiffToNow 7200    #Der Wert für timeDiffToNow ist in Sekunden anzugeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt. Es wird dynamisch bei jeder Operation &amp;quot;&amp;lt;Selektionsbeginn&amp;gt; = &amp;lt;aktuelle Zeit&amp;gt; - 3600s&amp;quot; berechnet und die Datensätze bis zu &amp;lt;aktuelle Zeit&amp;gt; berücksichtigt. &lt;br /&gt;
Die gesamten Datenbankoperationen und teilweise auch Auswertungen von Selektionen erfolgt mit Blockingcall im Hintergrund. Der Timeout für die Operationen ist per Default auf 86400 Sekunden gesetzt. Über das &#039;&#039;Attribut&#039;&#039; &amp;quot;timeout&amp;quot; kann es den eigenen Bedingungen angepasst werden. In dem Beispiel wird timeout auf 300s geändert um auch bei sehr großen Selektionen und Auswertungen nicht in einen timeout zu laufen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.Energy timeout 300&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um auch die Verarbeitungszeiten im Hintergrund als Reading anzeigen zu lassen wird mit mit dem &#039;&#039;Attribut&#039;&#039;          &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.Energy showproctime 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
erreicht. &lt;br /&gt;
Mit diesen Einstellungen ist das Device für den konfiguriert und man kann sich zum Beispiel mit&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Rep.Energy fetchrows&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
die Datensätze mit der Gesamtenergierzeugung &amp;quot;etotal&amp;quot; des Wechselrichters &amp;quot;STP_5000&amp;quot; die in den letzten 2 Stunden in die DB geschrieben wurden.&lt;br /&gt;
Nachdem der Befehl in der Gerätedetailsicht ausgeführt wurde, wechselt der state im DeviceOverview auf &amp;quot;running&amp;quot;. Sobald im DeviceOverview &amp;quot;done&amp;quot; angezeigt wird sieht man die Ergebnisse nach einem Browserrefresh.&lt;br /&gt;
Bei der Ausführung einer erneuten Operation werden alle Readings gelöscht. Sollen bestimmte Readings davon ausgenommen werden, kann dem &#039;&#039;Attribut&#039;&#039; eine kommaseparierte Liste von zu schützenden Readings übergeben werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Allgemein wird empfohlen sich für jede Aufgabe eine separates DbRep-Device anzulegen und entsprechend zu konfigurieren anstatt die Einstellungen ständig den neuen Aufgaben anzupassen. &lt;br /&gt;
Um den Prozess zu vereinfachen, kann das einmal angelegte Device für eine neue Selektionsaufgabe (zum Beispiel die Datensätze eines SMA Energymeters anzuzeigen bzw. auszuwerten) auf ein neues DbRep-Device kopiert&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
copy Rep.Energy Rep.SMAMeter&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und entsprechend angepasst werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis zur Eventgenerierung:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Je nach genutzter Funktion können sehr viele Readings als Ergebnis generiert werden. Zum Beispiel können mit &amp;quot;fetchrows&amp;quot;, wobei jedes Reading einer selektierte Zeile aus der Datenbank entspricht, durchaus mehrere hundert Readings entstehen.&lt;br /&gt;
Sollte man nicht durch die Attribute &amp;quot;event-on-update-reading&amp;quot; bzw. &amp;quot;event-on-change-reading&amp;quot; die Eventerzeugung auf nur relevante Readings begrenzt haben, können in diesem Fall ebenso viele Events enstehen die das System entsprechend belasten können. &amp;lt;br&amp;gt;&lt;br /&gt;
Deswegen wird empfohlen die &#039;&#039;&#039;Eventerzeugung sofort&#039;&#039;&#039; auf z.B. &amp;quot;state&amp;quot; zu &#039;&#039;&#039;begrenzen&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Ermittlung des monatlichen und durchschnittlichen Gasverbrauches mit Vaillant und eBusd MQTT ===&lt;br /&gt;
&lt;br /&gt;
Es soll der monatliche Gasverbrauch in m3, kWh und Euro sowie der Durchnschnitt in Euro über die gewählten Monate ermittelt werden.&lt;br /&gt;
&lt;br /&gt;
Die auswertenden Daten liegen als &amp;quot;Tics&amp;quot; vor. Es ist eine stetig steigende Zahl ohne Einheit. Sie wird als Reading aus einem Vaillant eBus-MQTT Device  geloggt. eBusd liefert PrEnergySumHc2 und PrEnergySumHwc1.&lt;br /&gt;
Beide Werte werden als Summe im Reading &#039;&#039;&#039;1_Brenner_Energy_Summe&#039;&#039;&#039; zusammengeführt und geloggt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Screenshot_2022-12-25_143631.png|right|thumb|400px|]]&lt;br /&gt;
&lt;br /&gt;
Für die Bereitstellung diverser Hilfswerte zur Berechnung existiert ein Dummy-Device &#039;&#039;&#039;VaillantControlDummy&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dieses Device enthält in User-Attributen folgende Werte:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Brennwert_kWh/m3&#039;&#039;&#039; -&amp;gt; den Brennwert in kWh pro m3 lt. Angaben des Lieferanten&lt;br /&gt;
* &#039;&#039;&#039;Zustandszahl&#039;&#039;&#039;     -&amp;gt; die Zustandszahl lt. Angaben des Lieferanten&lt;br /&gt;
* &#039;&#039;&#039;Multiplikator&#039;&#039;&#039;    -&amp;gt; ein Faktor zur Umrechnung der geloggten Tics (1_Brenner_Energy_Summe) in m3&lt;br /&gt;
* &#039;&#039;&#039;Tarif&#039;&#039;&#039;            -&amp;gt; der persönliche Tarif (Brutto) in €/kWh&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Multiplikator wurde empirisch als Quotient aus den real verbrauchten m3 und den Tics über einem längeren Zeitraum ermittelt.&lt;br /&gt;
Das Ergebnis der Multiplikaktion von geloggten Tics und und Multiplikator ergibt die jeweilig verbrauchten m3. Dieser Wert&lt;br /&gt;
ist Toleranz/Fehler behaftet, hat sich aber in der Praxis als hinreichend genau zur Ermittlung des Gasverbrauchs erwiesen.&lt;br /&gt;
&lt;br /&gt;
Das DbRep Device wird wie weiter oben beschrieben angelegt:&lt;br /&gt;
&lt;br /&gt;
 define Rep.gas.allmonths DbRep &amp;lt;DbLog-Devicename&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Bild:Screenshot_2022-12-25_143912.png|right|thumb|400px|]]&lt;br /&gt;
&lt;br /&gt;
Die folgenden Attribute werden gesetzt um den auszuwertenden Zeitraum, sowie das auszuwertende Device und Reading in der Datenbank auszuwerten.&lt;br /&gt;
Die Werte für die Attribute sind natürlich dem persönlichen Umfeld anzupassen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;device&#039;&#039;&#039; -&amp;gt; auszuwertendes Device (MQTT2_ebusd_bai)&lt;br /&gt;
* &#039;&#039;&#039;reading&#039;&#039;&#039; -&amp;gt; auszuwertendes Reading (1_Brenner_Energy_Summe)&lt;br /&gt;
* &#039;&#039;&#039;timestamp_begin&#039;&#039;&#039; -&amp;gt; Auswertungszeitraum Beginn (2022-10-01 00:00:00)&lt;br /&gt;
* &#039;&#039;&#039;timestamp_end&#039;&#039;&#039; -&amp;gt; Auswertungszeitraum Ende (current_year_end)&lt;br /&gt;
* &#039;&#039;&#039;aggregation&#039;&#039;&#039; -&amp;gt; month, d.h. der Auswertungszeitraum wird in Monatsscheiben aufgeteilt&lt;br /&gt;
* &#039;&#039;&#039;numDecimalPlaces&#039;&#039;&#039; -&amp;gt; die gwünschte Anzahl der Nachkommastellen im Ergebnis (0)&lt;br /&gt;
* &#039;&#039;&#039;diffAccept&#039;&#039;&#039; -&amp;gt; die akzeptierte Differenz von Diffenzwerten (1000000000), den Wert sehr hoch setzen um keine Datensätze von der Berechnung auszuschließen&lt;br /&gt;
* &#039;&#039;&#039;event-on-update-reading&#039;&#039;&#039; -&amp;gt; state,gas_.*&lt;br /&gt;
* &#039;&#039;&#039;eventMap&#039;&#039;&#039; -&amp;gt; /diffValue display:diffValuedisplay/ (für Verwendung in webCmd)&lt;br /&gt;
* &#039;&#039;&#039;webCmd&#039;&#039;&#039; -&amp;gt; diffValuedisplay&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wird das Device mit diesen Einstellungen gestartet, werden Readings der Form:&lt;br /&gt;
&lt;br /&gt;
 2022-10-31_07-05-03__MQTT2_ebusd_bai__1_Brenner_Energy_Summe__DIFF__2022-10&lt;br /&gt;
 2022-11-30_08-38-18__MQTT2_ebusd_bai__1_Brenner_Energy_Summe__DIFF__2022-11&lt;br /&gt;
 2022-12-25_12-29-47__MQTT2_ebusd_bai__1_Brenner_Energy_Summe__DIFF__2022-12&lt;br /&gt;
&lt;br /&gt;
generiert. Sie liefern die Anzahl der Tics im jeweiligen Monat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Herzstück&amp;quot; zur Ermittlung der Zielwerte ist eine Perl Routine die im Attribut &#039;&#039;&#039;userExitFn&#039;&#039;&#039; hinterlegt wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  if ($READING =~ /1_Brenner_Energy_Summe__DIFF/ &amp;amp;&amp;amp; $VALUE ne &#039;-&#039;) {&lt;br /&gt;
    my $date      = (split &#039;__&#039;, $READING)[0];&lt;br /&gt;
    my ($y,$m,$d) = $date =~ /^(\d{4})-(\d{2})-(\d{2})/x;&lt;br /&gt;
    &lt;br /&gt;
    my $cdd   = &amp;quot;VaillantControlDummy&amp;quot;;                            # Steuerungsdummy Device&lt;br /&gt;
    my $mpk   = AttrVal($cdd,  &#039;Multiplikator&#039;,    &#039;0&#039;);&lt;br /&gt;
    my $zz    = AttrVal($cdd,  &#039;Zustandszahl&#039;,     &#039;0&#039;);           # Zustandszahl lt. Lieferant&lt;br /&gt;
    my $bw    = AttrVal($cdd,  &#039;Brennwert_kWh/m3&#039;, &#039;0&#039;);           # Brennwert kWh/m3 lt. Lieferant&lt;br /&gt;
    my $tarf  = AttrVal($cdd,  &#039;Tarif&#039;,            &#039;0&#039;);           # Kosten €/kWh    &lt;br /&gt;
    my $ndp   = AttrVal($NAME, &#039;numDecimalPlaces&#039;, &#039;3&#039;);&lt;br /&gt;
    my $m3    = sprintf &amp;quot;%.${ndp}f&amp;quot;, $VALUE/10000 * $mpk;          # verbrauchte m3&lt;br /&gt;
    my $kwh   = sprintf &amp;quot;%.${ndp}f&amp;quot;, $m3 * $zz * $bw;              # Umrechnung m3 -&amp;gt; kWh&lt;br /&gt;
    my $cost  = sprintf &amp;quot;%.${ndp}f&amp;quot;, $kwh * $tarf;                 # Kosten = kWh * Tarif&lt;br /&gt;
    my $hash  = $defs{$NAME};&lt;br /&gt;
    &lt;br /&gt;
    readingsBulkUpdate ($hash, $date.&#039;_gas_consumption_m3&#039;,         $m3);&lt;br /&gt;
    readingsBulkUpdate ($hash, $date.&#039;_gas_consumption_kwh&#039;,       $kwh);&lt;br /&gt;
    readingsBulkUpdate ($hash, &#039;gas_cost_euro_&#039;.$y.&#039;-&#039;.$m.&#039;-&#039;.$d, $cost);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if ($READING eq &#039;state&#039; &amp;amp;&amp;amp; $VALUE eq &#039;done&#039;) {&lt;br /&gt;
      my $n   = 0;&lt;br /&gt;
      my $v   = 0;&lt;br /&gt;
      my $avg = 0;&lt;br /&gt;
	  &lt;br /&gt;
      for my $rdg ( grep { /gas_cost_euro_/x } keys %{$hash-&amp;gt;{READINGS}} ) {&lt;br /&gt;
          $n++;&lt;br /&gt;
          $v += ReadingsNum ($name, $rdg, 0);&lt;br /&gt;
      }&lt;br /&gt;
	  &lt;br /&gt;
      if ($n) {&lt;br /&gt;
          my $ndp3 = AttrVal($NAME, &#039;numDecimalPlaces&#039;, &#039;3&#039;);&lt;br /&gt;
          $avg    = sprintf &amp;quot;%.${ndp3}f&amp;quot;, $v / $n;&lt;br /&gt;
          readingsBulkUpdate ($hash, &#039;gas_cost_euro_average&#039;, $avg);&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald ein Reading erstellt wird welches auf den Regex /1_Brenner_Energy_Summe__DIFF/ matcht, werden die User-Attribute aus dem Steuerungsdummy&lt;br /&gt;
&#039;&#039;VaillantControlDummy&#039;&#039; abgerufen und damit die Berechnung der resultierenden m3 ($m3), kWh ($kwh) und Euro ($cost) durchgeführt.&lt;br /&gt;
Die User-Attribute können natürlich auch im DbRep Device selbst hinterlegt werden. Im vorliegenden Fall wird der Steuerungsdummy noch zu weiteren Aufgabe der Heizungssteuerung verwendet, sodass sich die Verwendung auch für diesen Use Case anbietet.&lt;br /&gt;
&lt;br /&gt;
Es werden die Readings &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;Datum&amp;gt;_&amp;lt;Zeit&amp;gt;_gas_consumption_m3&lt;br /&gt;
* &amp;lt;Datum&amp;gt;_&amp;lt;Zeit&amp;gt;_gas_consumption_kwh&lt;br /&gt;
* gas_cost_euro_&amp;lt;Datum&amp;gt;&lt;br /&gt;
&lt;br /&gt;
erzeugt. &lt;br /&gt;
&lt;br /&gt;
Hat das Device den Report erfolgreich beendet, wird &amp;quot;state&amp;quot; mit dem Wert &amp;quot;done&amp;quot; erzeugt.&lt;br /&gt;
Dieser Wert wird ebenfalls in der Routine überwacht und dann der Durchschnittswert ($avg) aus den ermittelten Monatsergebnissen berechnet.&lt;br /&gt;
Mit dem Ergebnis wird das Reading:&lt;br /&gt;
&lt;br /&gt;
* gas_cost_euro_average &lt;br /&gt;
&lt;br /&gt;
erstellt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zur Gestaltung des Device Overwiew wird im Attribut &#039;&#039;&#039;stateFormat&#039;&#039;&#039; ebenfalls eine Perl Routine hinterlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
 my $state = ReadingsVal($name, &#039;state&#039;,                    &#039;&#039;);&lt;br /&gt;
 my $gca   = ReadingsVal($name, &#039;gas_cost_euro_average&#039;, undef);&lt;br /&gt;
 &lt;br /&gt;
 my $show  = &#039;&#039;;&lt;br /&gt;
 $show    .= defined $gca ? &#039;Durchschnittskosten Gas: &#039;.$gca.&#039; €&#039; : &lt;br /&gt;
             $state;&lt;br /&gt;
 $show    .= &#039;&amp;lt;br&amp;gt;&#039;;&lt;br /&gt;
 $show    .= $state =~ /connected/xs   ? FW_makeImage(&#039;10px-kreis-gelb&#039;)         :&lt;br /&gt;
             $state =~ /initialized/xs ? FW_makeImage(&#039;control_3dot_hor_s&#039;)      :&lt;br /&gt;
             $state =~ /disconnect/xs  ? FW_makeImage(&#039;10px-kreis-rot&#039;)          :&lt;br /&gt;
             $state =~ /done/xs        ? FW_makeImage(&#039;10px-kreis-gruen&#039;)        :&lt;br /&gt;
             $state =~ /Warning/xs     ? FW_makeImage(&#039;info_warning@darkorange&#039;) :&lt;br /&gt;
             $state =~ /running/xs     ? &#039;&#039;                                      :&lt;br /&gt;
             FW_makeImage(&#039;10px-kreis-rot&#039;); &lt;br /&gt;
&lt;br /&gt;
 &amp;quot;&amp;lt;div&amp;gt;&amp;quot;.$show.&amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Datensätze (Devices) von einer Datenbank in eine andere umziehen (Export/Import)===&lt;br /&gt;
==== Zweck ====&lt;br /&gt;
&lt;br /&gt;
Oft ist es so, dass man zunächst eine DbLog-Datenbank anlegt um alle zu loggenden Events dort aufzuzeichnen. Mit zunehmender Größe bzw. mit dem Wunsch nach einer höheren Strukturierung werden weitere DbLog-Instanzen angelegt.&lt;br /&gt;
Die bereits geschriebenen Datensätze eines Gerätes müssen nun in eine andere Datenbank verlagert werden, weil das Device nunmehr in dieser Datenbank geloggt und ausgewertet werden soll.&lt;br /&gt;
&lt;br /&gt;
Für das Beispielszenario gilt folgendes:&lt;br /&gt;
&lt;br /&gt;
* Quelldatenbank aus der das Device entfernt werden soll ist &amp;quot;fhem&amp;quot; mit der DbLog-Instanz &amp;quot;LogDB&amp;quot;&lt;br /&gt;
* Zieldatenbank ist &amp;quot;fhemshort&amp;quot; mit der DbLog-Instanz &amp;quot;LogDBShort&amp;quot;&lt;br /&gt;
* das umzuziehende Device ist &amp;quot;MelderCP1&amp;quot;&lt;br /&gt;
* das definierte DbRep-Device ist &amp;quot;Rep.MelderCP1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==== Vorbereitung ====&lt;br /&gt;
&lt;br /&gt;
Zunächst wird ein DbRep-Device definiert (falls es noch nicht existiert) und für den Verwendungszweck vorbereitet (zur Definition siehe [[#Definieren_eines_DbRep-Devices | Definieren eines DbRep-Devices]]).&lt;br /&gt;
&lt;br /&gt;
Das Attribut &amp;quot;reading&amp;quot; bzw. eventuell gesetzte Attribute zur Zeiteingrenzung werden gelöscht, damit alle Datensätze des Devices erfasst werden können.&lt;br /&gt;
Für den bevorstehenden Export wird mit dem Attribut &amp;quot;expimpFile&amp;quot; der (beschreibbare) Pfad und Dateiname festgelegt, hier im Beispiel ist es &amp;quot;/media/sf_Backup_FHEM/meldercp1.txt&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Rep_MelderCP1_count.PNG|right|thumb|200px|Anzahl Einträge von MelderCP1]]&lt;br /&gt;
&lt;br /&gt;
Mit &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Rep.MelderCP1 countEntries&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann man sich nun einen Überblick verschaffen, wie viele Datensätze in der Quelldatenbank für &amp;quot;MelderCP1&amp;quot; enthalten sind und wie viele auch exportiert werden müssen. In dem Beispiel sind es, wie in dem Screenshot zu sehen, 1144 Datensätze.&lt;br /&gt;
&lt;br /&gt;
Falls noch nicht geschehen, wird in der bisherigen DbLog-Definition das zu loggende Device entfernt und in der neuen DbLog-Definition aufgenommen, damit nun keine neuen Datensätze mehr für &amp;quot;MelderCP1&amp;quot; in die bisherige Datenbank geschrieben werden. Hierzu siehe die {{Link2CmdRef|Lang=de|Anker=DbLog}} zu DbLog.&lt;br /&gt;
&lt;br /&gt;
==== Export ====&lt;br /&gt;
Nun werden die Datensätze des Devices &amp;quot;MelderCP1&amp;quot; mit dem folgenden set-Kommando in die Datei &amp;quot;/media/sf_Backup_FHEM/meldercp1.txt&amp;quot; exportiert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Rep.MelderCP1 exportToFile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach dem Export sollte natürlich im Reading von &amp;quot;Rep.MelderCP1&amp;quot; die gleiche Anzahl von exportierten Datensätzen ausgegeben werden wie vorher mit &amp;quot;countEntries&amp;quot; ermittelt wurde (siehe Screenshot). &lt;br /&gt;
&lt;br /&gt;
[[Datei:Rep_MelderCP1_exported.PNG|right|thumb|300px|Anzahl exportierter Dataensätze]]&lt;br /&gt;
&lt;br /&gt;
In der Export-Datei sind nun die Datensätze im CVS-Format enthalten. &lt;br /&gt;
&lt;br /&gt;
Ein Ausschnitt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
............&lt;br /&gt;
&amp;quot;2016-07-04 17:32:03&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-05 07:47:50&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-05 08:27:57&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-05 08:28:25&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-05 10:23:42&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-05 10:27:35&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-05 17:26:30&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-06 07:46:31&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-06 11:24:04&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-06 11:25:43&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-06 11:26:52&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
...........&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der bisherigen Datenbank &amp;quot;fhem&amp;quot; können die Datensätze nun gelöscht werden. Dazu versehen wir das DbRep-Device &amp;quot;Rep.MelderCP1&amp;quot; mit dem Attribut &amp;quot;allowDeletion&amp;quot; um die Löschfunktion des Moduls freizuschalten. &lt;br /&gt;
Nun werden die vorher exportierten Datensätze gelöscht mit:  &lt;br /&gt;
&lt;br /&gt;
[[Bild:Rep_MelderCP1_delrows.PNG|right|thumb|300px|Anzahl gelöschter Datensätze]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Rep.MelderCP1 delEntries&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch jetzt ist wieder sicherzustellen dass in der Ausgabe der gelöschten Rows dieselbe Anzahl (1144) der exportierten Datensätze enthalten ist.&lt;br /&gt;
&lt;br /&gt;
==== Import ====&lt;br /&gt;
Im nächsten Schritt werden die exportierten Daten in die neue Datenbank importiert. Dazu wird in unserem DbRep-Device &amp;quot;Rep.MelderCP1&amp;quot; zunächst das &amp;quot;allowDeletion&amp;quot;-Attribut sicherheitshalber gelöscht. &lt;br /&gt;
Zur Umstellung des DbRep-Devices auf die neue Datenbank wird die Definition von &amp;quot;Rep.MelderCP1&amp;quot; editiert und dort die neue DbLog-Instanz &amp;quot;LogDBShort&amp;quot; angegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
modify Rep.MelderCP1 LogDBShort&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach einem kurzen Moment wird der state von &amp;quot;Rep.MelderCP1&amp;quot; sich wieder von &amp;quot;initialized&amp;quot; auf &amp;quot;connected&amp;quot; ändern sofern die Umstellung funktioniert hat. Mit einem Vergleichslauf durch&lt;br /&gt;
[[Datei:Rep_MelderCP1_Vergleichslauf.PNG|right|thumb|300px|Anzahl Einträge von MelderCP1 in neuer Datenbank nach Umstellung]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Rep.MelderCP1 countEntries&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird ersichtlich, dass sich in dieser Datenbank noch keine (oder vllt. bereits neu geloggte) Einträge von &amp;quot;MelderCP1&amp;quot; befinden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nun kann der Import erfolgen. Mit &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Rep.MelderCP1 importFromFile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die Daten aus der immer noch im Attribut &amp;quot;expimpFile&amp;quot; hinterlegten Datei &amp;quot;/media/sf_Backup_FHEM/meldercp1.txt&amp;quot; in die Datenbank &amp;quot;fhemshort&amp;quot; importiert. Dieser Vorgang wird als Transaktion ausgeführt. Es werden immer alle Daten oder, im Falle eines Fehlers, &#039;&#039;&#039;KEINE&#039;&#039;&#039; Daten importiert. Damit ist sichergestellt dass der Datenimport immer einen definierten Zustand hat. Sollte der Datenimport sehr umfangreiche Datensätze enthalten und somit schon absehbar sein dass der voreingestellte timeout-Wert von 86400 Sekunden nicht ausreichen wird, kann man vorsorglich das Attribut &amp;quot;timeout&amp;quot; höher setzen, z.B. auf das Doppelte).&lt;br /&gt;
&lt;br /&gt;
Hier in dem Beispiel sind es lediglich 1144 Datensätze die sehr schnell importiert werden. Somit ist keine Anpassung des timeout-Parameters notwendig.&lt;br /&gt;
Auch nach dem Import wird wieder die Anzahl der verarbeiteten Dataensätze ausgegeben. Sie sollte natürlich ebenfalls mit den vorab ermittelten Zahlen der exportierten Datensätze übereinstimmen.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Rep_MelderCP1_importedrows.PNG|right|thumb|300px|Anzahl importierter Datensätze]]&lt;br /&gt;
&lt;br /&gt;
==== Abschluss ====&lt;br /&gt;
&lt;br /&gt;
Nach dem erfolgreichen Import sollte das Attribut &amp;quot;expimpFile&amp;quot; wieder entfernt werden damit man nicht versehentlich einen erneuten Import durchführt.&lt;br /&gt;
Das verwendete Rep.MelderCP1 kann nun wieder mit Zeitbegrenzungsattributen usw. versehen werden um die ursprüngliche Verwendung wieder herzustellen.&lt;br /&gt;
&lt;br /&gt;
Die dargestellte Prozedur stellt einen Komplettumzug eines Devices dar, kann jedoch durch die Angabe der bekannten Attribute auch auf bestimmte Readings in der Datenbank oder Zeitgrenzen eingeschränkt werden. Dadurch würden nur bestimmte Datensätze exportiert und wieder importiert werden. Weiterhin sollte man daran denken die vorhandenen Auswertungsszenarien mit DbRep-Devices bzw. SVG-Diagrammen usw. auch auf die neue Datanbank umzustellen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Inhalte einer primären Datenbank in eine andere Standby-Datenbank übertragen (syncStandby) ===&lt;br /&gt;
&lt;br /&gt;
==== Zweck ====&lt;br /&gt;
&lt;br /&gt;
Mit dem Export/Import Verfahren kann man, wie oben beschrieben, Daten zwischen verschiedenen Datenbanken austauschen. Mit dem Befehl &amp;quot;syncStandby&amp;quot; ist es aber mit relativ wenig Aufwand möglich, ein regelmäßig automatisch laufendes Übertragungsverfahren zwischen zwei Datenbanken zu etablieren. &lt;br /&gt;
&lt;br /&gt;
Im vorliegenden Beispiel werden Datenbanken des gleichen Typs verwendet. Das Verfahren ist aber auch zwischen Datenbanken unterschiedlichen Typs anwendbar.&lt;br /&gt;
&lt;br /&gt;
Anwendungsfälle dafür sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
* die Einrichtung eines Archivsystems &lt;br /&gt;
* die längerfristge Datenhaltung von Datensätzen eines bestimmten Devices/Readings in einer weiteren Datenbank um sie dort für umfangreiche Auswertungen zu nutzen (z.B. Daten einer PV-Anlage)&lt;br /&gt;
* Wechsel des Datenbanksystems, z.B. von SQLite zu MySQL/MariaDB&lt;br /&gt;
* säubern der Quelldaten von doppelten Datensätzen und nicht mehr benötigten Daten, Weiternutzung der aufgebauten Standbydatenbank als primäre Datenbank nach der Syncronisierung&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Für das hier gezeigte Beispielszenario sollen Daten einer MariaDB-Quelldatenbank in einer andere MariaDB-Datenbank regelmäßig übertragen werden. Auf der Quelldatenbank werden die Daten aber nicht gelöscht, d.h. es entstehen zwei Datenbanken mit gleicher Datenbasis.&lt;br /&gt;
&lt;br /&gt;
* Quelldatenbank ist eine MariaDB &amp;quot;fhemtest1&amp;quot; mit der DbLog-Instanz &amp;quot;LogDB1&amp;quot;&lt;br /&gt;
* Zieldatenbank ist eine MariaDB &amp;quot;fhemtest&amp;quot; mit der DbLog-Instanz &amp;quot;LogStby&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Die Quelldatenbank ====&lt;br /&gt;
&lt;br /&gt;
Im vorliegenden Beispiel sind die Quelldatenbank und die Standby-Datenbank vom gleichen Typ MariaDB. Das Verfahren funktioniert ebenso zwischen Datenbanken unterschiedlichen Typs, also z.B. zur Übertragung von SQLite Daten in eine MariaDB.&lt;br /&gt;
&lt;br /&gt;
Die Quelldatenbank ist im normalen Kontext die produktive FHEM-Logdatenbank. D.h. sie ist bereits eingerichtet und läuft mit einem entsprechenden DbLog-Device.&lt;br /&gt;
Die hier verwendete Quelldatenbank heißt &amp;quot;fhemtest1&amp;quot;. Die Definition des dazu gehörenden DbLog-Devices &amp;quot;LogDB1&amp;quot; sei der Vollständigkeit halber hier erwähnt, wird aber natürlich bei jedem Nutzer individuell aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define LogDB1 DbLog ./fhemtest1maria10.conf .*:(?!done).*&lt;br /&gt;
attr LogDB1 DbLogInclude CacheUsage&lt;br /&gt;
attr LogDB1 DbLogSelectionMode Exclude/Include&lt;br /&gt;
attr LogDB1 DbLogType History&lt;br /&gt;
attr LogDB1 addStateEvent 0&lt;br /&gt;
attr LogDB1 asyncMode 1&lt;br /&gt;
attr LogDB1 bulkInsert 1&lt;br /&gt;
attr LogDB1 cacheEvents 2&lt;br /&gt;
attr LogDB1 cacheLimit 2000&lt;br /&gt;
attr LogDB1 dbSchema fhemtest1&lt;br /&gt;
attr LogDB1 devStateIcon .*active:10px-kreis-gelb connected:10px-kreis-gruen .*disconnect:10px-kreis-rot&lt;br /&gt;
attr LogDB1 disable 0&lt;br /&gt;
attr LogDB1 room DbLog&lt;br /&gt;
attr LogDB1 showproctime 1&lt;br /&gt;
attr LogDB1 syncInterval 120&lt;br /&gt;
attr LogDB1 useCharfilter 1&lt;br /&gt;
attr LogDB1 verbose 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Die Standby Datenbank ====&lt;br /&gt;
&lt;br /&gt;
Die Standby Datenbank ist das Synchronisationsziel. &lt;br /&gt;
Die Erstellung der Datenbank und die nachfolgende Definition des dazu gehörigen DbLog-Devices erfolgt weitgehend wie für eine &amp;quot;normale&amp;quot; Log-Datenbank wie in der Commandref beschrieben mit geringfügigen Anpassungen.&lt;br /&gt;
&lt;br /&gt;
; 1. Anlegen der DB und Tabellen: wie in diesem [https://svn.fhem.de/trac/browser/trunk/fhem/contrib/dblog Link] hinterlegt, erfolgt die Erstellung mit den Befehlen  für MySQL:&lt;br /&gt;
::: CREATE DATABASE `fhemtest` DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;&lt;br /&gt;
::: CREATE USER &#039;fhemuser&#039;@&#039;%&#039; IDENTIFIED BY &#039;fhempassword&#039;;&lt;br /&gt;
::: CREATE TABLE `fhemtest`.`history` (TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
::: ALTER TABLE `fhemtest`.`history` ADD PRIMARY KEY(TIMESTAMP, DEVICE, READING);&lt;br /&gt;
::: CREATE TABLE `fhemtest`.`current` (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
::: GRANT SELECT, INSERT, DELETE, UPDATE ON `fhemtest`.* TO &#039;fhemuser&#039;@&#039;%&#039;;&lt;br /&gt;
&lt;br /&gt;
Es wird zusätzlich zu der normalen Erstellung der history-Tabelle ein primary Key für die Tabelle erstellt. Dieser Key verhindert dass Duplikate in der Tabelle erstellt werden und erleichtert dadurch sehr das Verfahren der Datenübertragung in der Folge.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:stb1.PNG|right|thumb|300px|DbLog-Device für Standby-Datenbank]]&lt;br /&gt;
; 2. Erstellung der Konfigurationsdatei und Definition des DbLog-Devices für die Standby-Datenbank: Die Konfigurationsdatei (mariastby.conf) beinhaltet die Verbindunginformationen für das DbLog-Device und wird genau wie in der DbLog-Commandref beschrieben angelegt. Das DbLog-Device für die Standby-Datenbank wird nur für die nachfolgende Definition des benötigten DbRep-Devices gebraucht und wird so definiert, dass keinerlei Events geloggt werden. Das kann auf verschiedenen Wegen erreicht werden. Im Beispiel wird der Regex im DEF entsprechend aufgebaut.&lt;br /&gt;
::: define LogStby DbLog ./mariastby.conf aaaaaa:bbbbbb&lt;br /&gt;
::: attr LogStby DbLogType History&lt;br /&gt;
::: attr LogStby asyncMode 1&lt;br /&gt;
::: attr LogStby bulkInsert 1&lt;br /&gt;
::: attr LogStby cacheEvents 2&lt;br /&gt;
::: attr LogStby devStateIcon .*active:10px-kreis-gelb connected:10px-kreis-gruen .*disconnect:10px-kreis-rot&lt;br /&gt;
::: attr LogStby disable 0&lt;br /&gt;
::: attr LogStby room DbLog&lt;br /&gt;
::: attr LogStby showNotifyTime 1&lt;br /&gt;
::: attr LogStby showproctime 1&lt;br /&gt;
::: attr LogStby syncEvents 1 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Erstellung der current-Tabelle ist für den vorgesehenen Zweck eigentlich nicht nötig, wird der Vollständigkeit halber mit dokumentiert.&lt;br /&gt;
Ist das DbLog-Device definiert und erfolgreich verbunden, ist dessen state &amp;quot;connected&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Definition des DbRep-Devices zur Synchronisation Quell- und Standby-Datenbank ====&lt;br /&gt;
&lt;br /&gt;
Es existieren nun das DbLog Device &amp;quot;LogDB1&amp;quot; für die produktive Quelldatenbank und das DbLog Device &amp;quot;LogStby&amp;quot; für die Standby-Datenbank.&lt;br /&gt;
Für die Synchronisation wird ein DbRep-Device erstellt, welches mit dem DbLog-Device der &#039;&#039;&#039;produktiven Quelldatenbank&#039;&#039;&#039;, also LogDB1, verbunden wird.&lt;br /&gt;
Wie üblich, wird im DEF der Name des entsprechenden DbLog-Devices angegeben, hier &amp;quot;LogDB1&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod Rep.LogDB1.syncStandby DbRep LogDB1&lt;br /&gt;
attr Rep.LogDB1.syncStandby aggregation no&lt;br /&gt;
attr Rep.LogDB1.syncStandby event-on-update-reading state&lt;br /&gt;
attr Rep.LogDB1.syncStandby fastStart 1&lt;br /&gt;
attr Rep.LogDB1.syncStandby role Client&lt;br /&gt;
attr Rep.LogDB1.syncStandby room DbLog&lt;br /&gt;
attr Rep.LogDB1.syncStandby showproctime 1&lt;br /&gt;
attr Rep.LogDB1.syncStandby stateFormat { (ReadingsVal($name,&amp;quot;state&amp;quot;, &amp;quot;&amp;quot;)).&amp;quot; : SQL-Zeit: &amp;quot;.(ReadingsVal($name,&amp;quot;sql_processing_time&amp;quot;, &amp;quot;&amp;quot;)) }&lt;br /&gt;
attr Rep.LogDB1.syncStandby timeDiffToNow d:6&lt;br /&gt;
attr Rep.LogDB1.syncStandby verbose 3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den verschiedenen möglichen Zeitattributen (&#039;&#039;&#039;time.*&#039;&#039;&#039;) und den Attributen &#039;&#039;&#039;device&#039;&#039;&#039; und &#039;&#039;&#039;reading&#039;&#039;&#039; wird abgegrenzt, welche Datensätze in die Standby-Datenbak übertragen werden sollen. In dem vorliegenden Beispiel werden mit jedem Synchronisationslauf alle in der DB vorhandenen Datensätze, die nicht älter als 6 Tage Tage sind, in die Standby-Datenbank übertragen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Würde man einen solchen Lauf z.B. alle 4 Stunden durchführen, würden viele doppelte Datensätze in der  Datenbank entstehen. Der bei der Tabellenerstellung für die history-Tabelle angelegte primary Key verhindert das !&lt;br /&gt;
[[Datei:stb2.PNG|right|thumb|300px|syncStandby gestartet]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Synchronisationslauf wird nun gestartet mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 set &amp;lt;DbRep-Name&amp;gt; syncStandby &amp;lt;DbLog-Device Standby&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dem vorliegenden Beispiel ist das:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 set Rep.LogDB1.syncStandby syncStandby LogStby&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:stb3.PNG|right|thumb|300px|syncStandby erfolgreich ausgeführt]]&lt;br /&gt;
Der Fortschritt der Datenübertragung sowie die evtl. eingefügten Datensätze (number of lines inserted) ist im Logfile mit verbose 3 ersichtlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2020.01.24 17:12:41.186 3: DbRep Rep.LogDB1.syncStandby - total lines transfered to standby database: 6177&lt;br /&gt;
2020.01.24 17:12:46.411 3: DbRep Rep.LogDB1.syncStandby - total lines transfered to standby database: 23957&lt;br /&gt;
2020.01.24 17:12:51.710 3: DbRep Rep.LogDB1.syncStandby - total lines transfered to standby database: 24048&lt;br /&gt;
2020.01.24 17:12:57.733 3: DbRep Rep.LogDB1.syncStandby - total lines transfered to standby database: 24092&lt;br /&gt;
2020.01.24 17:13:03.505 3: DbRep Rep.LogDB1.syncStandby - total lines transfered to standby database: 24059&lt;br /&gt;
2020.01.24 17:13:08.891 3: DbRep Rep.LogDB1.syncStandby - total lines transfered to standby database: 23969&lt;br /&gt;
2020.01.24 17:13:13.420 3: DbRep Rep.LogDB1.syncStandby - total lines transfered to standby database: 17871&lt;br /&gt;
2020.01.24 17:13:13.421 3: DbRep Rep.LogDB1.syncStandby - number of lines inserted into &amp;quot;LogStby&amp;quot;: 104&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:stb4.PNG|right|thumb|300px|nur Daten der Devices SMA_Energymeter,MySTP_5000 werden übertragen]]&lt;br /&gt;
Nach dem erfolgreichen Sync-Lauf wird die benötigte Laufzeit und die Anzahl der übertragenen Datensätze in Readings angezeigt.&lt;br /&gt;
&lt;br /&gt;
Mit dem Attribut &#039;&#039;&#039;aggragation&#039;&#039;&#039; wird gesteuert, in welchen &#039;&#039;&#039;Zeitscheiben&#039;&#039;&#039; die Selektion der Daten aus der Quelldatenbank und die Übertragung ausgeführt wird. Im Standard ohne gesetztes aggregation-Attribut ist es ein Tag. Stehen genügend Ressourcen zur Verfügung, kann dieser Wert auch auf &amp;quot;week&amp;quot; oder &amp;quot;month&amp;quot; gesetzt werden. Der Wert &amp;quot;hour&amp;quot; zur Verkleinerung des Datenpaketes ist ebenfalls möglich. &lt;br /&gt;
&lt;br /&gt;
Bei jedem Lauf wird die Anzahl der eingefügten Datensätze im Reading &#039;&#039;&#039;number_lines_inserted_Standby&#039;&#039;&#039; angezeigt.&lt;br /&gt;
&lt;br /&gt;
Die zu übertragenden Daten können natürlich sehr granular bestimmt werden. Sollen zum Beispiel nur die Daten der Devices SMA_Energymeter und MySTP_5000 übertragen werden, wird das Attribut &#039;&#039;&#039;device&#039;&#039;&#039; entsprechend gesetzt:&lt;br /&gt;
&lt;br /&gt;
 attr Rep.LogDB1.syncStandby SMA_Energymeter,MySTP_5000&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weiterhin bietet das Attribut &#039;&#039;&#039;reading&#039;&#039;&#039; eine Eingrenzung auf die gewünschten Readings und mit dem Attribut &#039;&#039;&#039;valueFilter&#039;&#039;&#039; kann eine Eingrenzung nur auf bestimmte vorkommende Werte vorgenommen werden.&lt;br /&gt;
&lt;br /&gt;
Der Erfolg der Synchronisationsläufe kann sehr einfach mit einem separaten DbRep-Device (mit dem &#039;&#039;&#039;fetchrows&#039;&#039;&#039; Kommando) oder dem Tool phpMyAdmin (nebenstehend) überprüft werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== regelmäßige Ausführung der Synchronisation ====&lt;br /&gt;
&lt;br /&gt;
Ein einfaches AT kann dazu dienen den Synchronisationslauf mit dem Device &#039;&#039;&#039;Rep.LogDB1.syncStandby&#039;&#039;&#039; regelmäßig alle x-Stunden/Minuten auszuführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define At.Sync.Stby at +*04:00:00 set Rep.LogDB1.syncStandby syncStandby LogStby&lt;br /&gt;
attr At.Sync.Stby room DbLog&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ermittlung und Darstellung der täglichen Energieerzeugung eines Wechselrichters ===&lt;br /&gt;
&lt;br /&gt;
Gegeben sei dass die Energiedaten eines Wechselrichters (STP_5000) mit SMAUtils in Verbindung mit SBFSpot in die Datenbank geschrieben werden.&lt;br /&gt;
Neben vielen anderen Werten liefert SMAUtils die Tageserzeugung in kWh als Reading &amp;quot;etoday&amp;quot;. Der Wert dieses Readings wird alle paar Minuten in der Datenbank gespeichert.&lt;br /&gt;
&lt;br /&gt;
Zur Auswertung wird ein DbRep-Device angelegt wie unter [[#Definieren_eines_DbRep-Devices | Definieren eines DbRep-Devices]]) beschrieben (z.B. Rep.etoday.STP_5000).&lt;br /&gt;
&lt;br /&gt;
[[Datei:Rep.STP_5000.configured.PNG|right|thumb|300px|Rep.STP_5000 konfiguriert]]&lt;br /&gt;
&lt;br /&gt;
Es soll die täglich erzeugte Energiemenge beginnend ab dem 01.10.2016 0 Uhr bis zum 13. Oktober angezeigt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.etoday.STP_5000 timestamp_begin 2016-10-01 00:00:00&lt;br /&gt;
attr Rep.etoday.STP_5000 timestamp_end 2016-10-13 23:59:59&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da es sich um eine tägliche Aggregation handelt (es soll pro Auswertung der Tag von 00:00:00 bis 23:59:59 betrachtet werden) und die Hintergrundverarbeitungszeit dargestellt werden soll, wird eingestellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.etoday.STP_5000 aggregation day&lt;br /&gt;
attr Rep.etoday.STP_5000 showproctime 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Eingrenzung der auszuwertenden Devices und der dazu gehörigen Readings wird durch die entsprechenden Attribute gewährleistet.&lt;br /&gt;
Es soll nur das Device &amp;quot;STP_5000&amp;quot; mit dem Reading &amp;quot;etoday&amp;quot; berücksichtigt werden. Dazu stellen wir ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.etoday.STP_5000 reading etoday&lt;br /&gt;
attr Rep.etoday.STP_5000 device STP_5000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DbRep ist nun grundsätzlich zur Auswertung konfiguriert (siehe Screenshot).&lt;br /&gt;
&lt;br /&gt;
Zur Selektion und Berechnung von numerischen Daten stehen die Funktionen average-, max-, min-, sum- und diffValue zur Verfügung.&lt;br /&gt;
Bei den in diesem Beispiel verwendeten Daten handelt es sich um einen täglich neu erzeugten und über 24 Stunden stetig steigenden Wert.&lt;br /&gt;
Demnach ist für diese Art Daten die Funktion &amp;quot;maxValue&amp;quot; geeignet. Sie gibt den maximalen Wert des Readings im eingestellten Aggregationszeitraum (day) aus.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Rep.STP_5000.max.PNG|right|thumb|300px|Rep.STP_5000 maxValue]]&lt;br /&gt;
&lt;br /&gt;
Die Berechnung wird ausgeführt durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Rep.etoday.STP_5000 maxValue&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kurzer Zeit ist die Berechnung erfolgt (state=done in DeviceOverview). Nach einem Browserrefresh in der Detailansicht sind die erzeugten Readings sichtbar.&lt;br /&gt;
&lt;br /&gt;
Jedes Reading hat einen bestimmten Aufbau. Die Funktion &amp;quot;maxValue&amp;quot; erzeugt Readings folgenden Aufbaus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2016-10-07_18-19-03__STP_5000__etoday__MAX__2016-10-07&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zunächst wird der Timestring &amp;quot;2016-10-07_18-19-03&amp;quot; ausgegeben der in diesem Kontext den höchsten (d.h. letzten) Wert von &amp;quot;etotal&amp;quot; an dem Tag markiert.&lt;br /&gt;
Bei dem Wechselrichter ist es damit auch der Zeitpunkt zu dem die Energieerzeugung eingestellt wurde (in dem Fall 07.10.2016 um 18:19:03).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anmerkung:&#039;&#039;&#039; Die Notation des Timestrings wurde so gewählt, um die Regeln für in Readings erlaubte Zeichen einzuhalten. Wenn keine auswertbaren Daten vorliegen und berechnet werden konnten, werden Readings mit &amp;quot;-&amp;quot; ausgegeben. Im Beispiel sind es die Tagesaggregate 10.10.-13.10. da diese Tage in der Zukunft liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Danach folgt im Reading die Angabe des Devices (STP_5000), des augewerteten Readings (etoday) und der Funktion die dieses Ergebnis ermittelt hat (MAX für maxValue).&lt;br /&gt;
Die letzte Angabe, hier &amp;quot;2016-10-07&amp;quot; definiert den Auswertungszeitraum. In dem Beispiel ist es der 07.10.2016. Würde die Berechnung mit aggregation=week durchgeführt werden, würde man als an dieser Stelle den Auswertungszeitraum &amp;quot;week_39&amp;quot; erhalten, also die Kalenderwoche 39.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Um die Auswertungsergebniss etwas benutzerfreundlicher zu gestalten, steht das Attribut &amp;quot;readingNameMap&amp;quot; zur Verfügung. [[Datei:Rep.STP_5000.max.namemap.PNG|right|thumb|300px|Rep.STP_5000 maxValue mit readingNameMap]]&lt;br /&gt;
Im Beispiel setzen wir es auf:   &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.etoday.STP_5000 readingNameMap Tageserzeugung_kWh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein erneuter maxValue-Lauf erzeugt Readings des folgenden Aufbaus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2016-10-01_18-03-13__Tageserzeugung_kWh__2016-10-01   4.9870&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== (regelmäßiges) Löschen von Datenbanksätzen ===&lt;br /&gt;
&lt;br /&gt;
Dieses DbRep-Device dient dazu einmalig oder verbunden mit einem AT-Device Einträge aus einer Datenbank zu löschen.&lt;br /&gt;
Zunächst wird das Device wie im Abschnitt [[#Definieren_eines_DbRep-Devices | &amp;quot;Definieren eines DbRep-Devices&amp;quot;]] beschrieben definiert. &lt;br /&gt;
Dadurch wird das DbRep-Device mit einem DbLog-Device assoziiert und mit der Datenbank des DbLogs verbunden. Alle weiteren Operationen werden mit dieser Datenbank durchgeführt. In diesem Beispiel heißt das DbRep-Device &amp;quot;Rep.Del.DbShort&amp;quot; und das DbLog-Device &amp;quot;LogDBShort&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In diesem Beispiel sollen alle Datensätze gelöscht werden die älter als 180 Tage sind. Die Löschfunktion des Moduls ist standardmäßig ausgeschaltet und muß per Attribut enabled werden.&lt;br /&gt;
Dazu werden die Attribute                                      [[Datei:Rep.Del.DbShort.PNG|right|thumb|300px|Rep.Del.DbShort konfiguriert]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr Rep.Del.DbShort timeOlderThan d:180&lt;br /&gt;
 attr Rep.Del.DbShort allowDeletion 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt. Die Zeitangabe erfolgt in Sekunden.&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei SQLite sollte während des Löschens kein paralleler Schreibprozess in Form des normalen Eventloggings stattfinden. MySQL/MariaDB kann es durchaus parallel verarbeiten. Um einen parallelen Zugriff durch DbLog-Loggging zu verhindern, kann das Logging vor der Löschung geschlossen und danach wieder geöffnet werden.&lt;br /&gt;
Dafür werden die Attribute mit z.B. diesen Werten gesetzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr Rep.Del.DbShort executeBeforeProc set LogDBShort reopen 7200&lt;br /&gt;
 attr Rep.Del.DbShort executeAfterProc set LogDBShort reopen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei ist &amp;quot;LogDBShort&amp;quot; das mit dem DbRep-Device assoziierte DbLog-Device.&lt;br /&gt;
 &lt;br /&gt;
Der Löschvorgang wird mit&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 set Rep.Del.DbShort delEntries&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gestartet. Nach Abschluß der Datenlöschung wird die Anzahl der deleted rows als Reading ausgegeben und auch im Log mit verbose=3 geschrieben.&lt;br /&gt;
&lt;br /&gt;
Müssen sehr viele Datensätze gelöscht werden, könnte nach 86400 Sekunden der Standardtimeout erreicht werden und der Vorgang mit &amp;quot;error&amp;quot; enden.&lt;br /&gt;
In diesem Fall ist der timeout mit dem Attribut:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr Rep.Del.DbShort timeout &amp;lt;timeout in Sekunden&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
entsprechend angepasst werden.&lt;br /&gt;
&lt;br /&gt;
Ein fhem.cfg Eintrag für das beschriebene Beispieldevice sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.Del.DbShort DbRep LogDBShort&lt;br /&gt;
attr Rep.Del.DbShort allowDeletion 1&lt;br /&gt;
attr Rep.Del.DbShort comment löschen aller Einträge in LogDBShort älter als 180 Tage&lt;br /&gt;
attr Rep.Del.DbShort devStateIcon connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.Del.DbShort event-on-update-reading state&lt;br /&gt;
attr Rep.Del.DbShort room DbLog&lt;br /&gt;
attr Rep.Del.DbShort timeOlderThan 15552000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Löschung der relevanten Datensätze kann weiterhin über Attribute &amp;quot;device&amp;quot; bzw. &amp;quot;reading&amp;quot; kann selektiv auf bestimmte Device- und/oder Readingloggings beschränkt werden. Wird keine Zeitbeschränkung (z.B. durch &amp;quot;timeOlderThan&amp;quot;) vorgenommen, erfolgt die Löschung aller Datensätze der durch &amp;quot;device&amp;quot; bzw. &amp;quot;reading&amp;quot; spezifizierten Datenbankinhalte.&lt;br /&gt;
&lt;br /&gt;
Als Argument der Attribute &amp;quot;device&amp;quot; bzw. &amp;quot;reading&amp;quot; kann SQL-Wildcard &amp;quot;%&amp;quot; verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%     substituiert ein oder mehrere Zeichen&lt;br /&gt;
_     Platzhalter für ein einzelnes Zeichen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr Rep.Del.DbShort device %5000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Spezifikation werden alle Devices gelöscht die auf &amp;quot;5000&amp;quot; enden, also z.B. STP_5000 und MySTP_5000.&lt;br /&gt;
(siehe auch Attribut &amp;quot;reading&amp;quot; in der {{Link2CmdRef|Anker=DbRepattr|Lang=de|Label=Commandref}}).&lt;br /&gt;
&lt;br /&gt;
Ein einfaches AT kann dazu dienen den Löschjob über das Device Rep.Del.DbShort regelmäßig alle x-Stunden/Minuten auszuführen. Dadurch werden nicht mehr benötigte Datenbankeinträge automatisiert und nicht FHEM blockierend im Hintergrund entfernt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define At.Del.DbShort at *23:45:00 set Rep.Del.DbShort delEntries&lt;br /&gt;
attr At.Del.DbShort room DbLog&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== erweiterte Anwendung von Selektionsbedingungen zur Löschung ====&lt;br /&gt;
&lt;br /&gt;
Wie bei allen selektiven Funktionen im DbRep kann auch bei der Löschfunktion über die Attribute &#039;&#039;&#039;device&#039;&#039;&#039; und &#039;&#039;&#039;reading&#039;&#039;&#039; eine Beschränkung der zu löschenden Datensätze erfolgen. D.h. es werden nur Datensätze gelöscht, wenn die Selektionskriterien dieser Attribute erfüllt sind.&lt;br /&gt;
&lt;br /&gt;
Im Attribut &#039;&#039;&#039;device&#039;&#039;&#039; können einzelne Geräte, Geräte-Spezifikationen ({{Link2CmdRef|Anker=devspec|Lang=de|Label=devspec}}) sowie Geräte mit Wildcards angegeben werden.&lt;br /&gt;
Ist eine devspec angegeben, werden die Devicenamen vor der Selektion aus der Geräte-Spezifikationen und den aktuell in FHEM vorhandenen Devices aufgelöst sofern möglich.&lt;br /&gt;
Wird dem Device bzw. der Device-Liste oder Geräte-Spezifikation ein &amp;quot;EXCLUDE=&amp;quot; vorangestellt, werden diese Devices von der Selektion und damit von dem Löschvorgang ausgeschlossen.   &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;name&amp;gt; device TYPE=DbRep                    # es werden nur Datensätze der Geräte vom Modul-Typ &amp;quot;DbRep&amp;quot; &lt;br /&gt;
                                                   eingeschlossen&lt;br /&gt;
attr &amp;lt;name&amp;gt; device MySTP_5000                    # nur Datensätze des Gerätes &amp;quot;MySTP_5000&amp;quot; werden selektiert/gelöscht&lt;br /&gt;
attr &amp;lt;name&amp;gt; device SMA.*,MySTP.*                 # Daten von Geräten die mit &amp;quot;SMA&amp;quot; oder &amp;quot;MySTP&amp;quot; beginnen, werden &lt;br /&gt;
                                                   selektiert/gelöscht&lt;br /&gt;
attr &amp;lt;name&amp;gt; device SMA_Energymeter,MySTP_5000    # nur Daten der beiden angegebenen Geräten werden selektiert/gelöscht&lt;br /&gt;
attr &amp;lt;name&amp;gt; device %5000                         # Daten von Geräten die mit &amp;quot;5000&amp;quot; enden werden in die Operation &lt;br /&gt;
                                                   eingeschlossen&lt;br /&gt;
attr &amp;lt;name&amp;gt; device TYPE=SSCam EXCLUDE=SDS1_SVS   # es werden Datensätze der Geräte vom Modul-Typ &amp;quot;SSCam&amp;quot; eingeschlossen, &lt;br /&gt;
                                                   aber das Gerät &amp;quot;SDS1_SVS&amp;quot; (auch vom Typ SSCam) wird ausgeschlosssen&lt;br /&gt;
attr &amp;lt;name&amp;gt; device EXCLUDE=SDS1_SVS              # Datensätze aller Geräte mit Ausnahme des Gerätes &amp;quot;SDS1_SVS&amp;quot; werden          &lt;br /&gt;
                                                   selektiert/gelöscht&lt;br /&gt;
attr &amp;lt;name&amp;gt; device EXCLUDE=TYPE=SSCam            # Datensätze aller Geräte mit Ausnahme der Geräte vom Typ &amp;quot;SSCam&amp;quot; werden          &lt;br /&gt;
                                                   selektiert/gelöscht&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ähnlich erfolgt die Abgrenzung der DB-Selektionen auf ein bestimmtes oder mehrere Readings sowie exkludieren von Readings. Mehrere Readings werden als Komma separierte Liste angegeben. Es können SQL Wildcard (%) verwendet werden, aber &#039;&#039;&#039;keine Perl-Regex&#039;&#039;&#039;.&lt;br /&gt;
Wird dem Reading bzw. der Reading-Liste ein &amp;quot;EXCLUDE=&amp;quot; vorangestellt, werden diese Readings von der Selektion ausgeschlossen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;name&amp;gt; reading etotal                                     # Datensätze mit dem Reading &amp;quot;etotal&amp;quot; werden eingeschlossen &lt;br /&gt;
attr &amp;lt;name&amp;gt; reading et%                                        # Datensätze deren Reading mit &amp;quot;et&amp;quot; beginnt werden &lt;br /&gt;
                                                                 eingeschlossen&lt;br /&gt;
attr &amp;lt;name&amp;gt; reading etotal,etoday                              # Datensätze mit den Readings &amp;quot;etotal&amp;quot; und &amp;quot;etoday&amp;quot; werden &lt;br /&gt;
                                                                 in die Selektion eingeschlossen&lt;br /&gt;
attr &amp;lt;name&amp;gt; reading eto%,Einspeisung EXCLUDE=etoday            # Datensätze mit Readings beginnend mit &amp;quot;eto&amp;quot; sowie das &lt;br /&gt;
                                                                 Reading &amp;quot;Einspeisung&amp;quot; werden selektiert, aber das Reading &lt;br /&gt;
                                                                 &amp;quot;etoday&amp;quot; wiederum ausgeschlossen&lt;br /&gt;
attr &amp;lt;name&amp;gt; reading etotal,etoday,Ein% EXCLUDE=%Wirkleistung   # Datensätze mit Readings beginnend mit &amp;quot;Ein&amp;quot; sowie die &lt;br /&gt;
                                                                 Readings &amp;quot;etotal&amp;quot; und &amp;quot;etoday&amp;quot; werden selektiert, wobei &lt;br /&gt;
                                                                 Readings, die auf &amp;quot;Wirkleistung&amp;quot; enden, von der Löschung&lt;br /&gt;
                                                                 ausgeschlossen werden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Natürlich werden die Selektionsbedingungen durch eventuell gesetzte Zeiteingrenzungen mit den time*- Attributen ergänzt.&lt;br /&gt;
Die time*- Attribute sind mehrere im DbRep vorhandene Attribute, die alle mit &amp;quot;time&amp;quot; beginnen, z.B. timeOlderThan.&lt;br /&gt;
&lt;br /&gt;
=== Auffinden von alten Devicenamen in der DB und versenden/loggen einer Negativliste ===&lt;br /&gt;
&lt;br /&gt;
Wenn eine Datenbank längere Zeit gelaufen ist, Devices in FHEM hinzugefügt, geändert oder gelöscht wurden oder auch das DEF des DbLog-Devices angepasst wurde, befinden sich aller Wahrscheinlichkeit nach nicht mehr gewünschte/gebrauchte Datensätze von nicht mehr bestehenden Devices in der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Dieses Beispiel soll einen Weg zeigen, wie man mit Hilfe der DbRep-sqlCmd Funktion (ab Version 4.14.0) alle in der DB enthältenen Devicenamen ermitteln und mit einer Positivliste vergleichen kann. &lt;br /&gt;
Die Negativliste der nicht gewünschten Devices wird im Logfile ausgegeben bzw. mit TelegramBot versendet.&lt;br /&gt;
&lt;br /&gt;
Zunächst wird das Device wieder wie im Abschnitt [[#Definieren_eines_DbRep-Devices | &amp;quot;Definieren eines DbRep-Devices&amp;quot;]] beschrieben definiert. &lt;br /&gt;
Dadurch wird das DbRep-Device mit einem DbLog-Device assoziiert und mit der Datenbank des DbLog-Device verbunden. In diesem Beispiel heißt das DbRep-Device &amp;quot;Report&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Nach der Definition setzen wir die Attribute:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Report event-on-update-reading state&lt;br /&gt;
attr Report sqlResultFormat separated&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es werden nun nur noch Events für &amp;quot;state&amp;quot; generiert. Das Attribut &amp;quot;sqlResultFormat = separated&amp;quot; bewirkt, dass das Ergebnis des SQL-Statements zeilenweise in Einzelreadings zur Verfügung gestellt wird.&lt;br /&gt;
&lt;br /&gt;
Nun kann getestet werden, ob die Abfrage grundsätzlich funktioniert. Dazu wird das benutzerspezifische Kommando &amp;quot;sqlCmd&amp;quot; verwendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Report sqlCmd select device, count(*) from history group by DEVICE;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sollte nun eine entsprechende Anzahl von Readings erzeugt werden (Browserrefresh), die als Wert eine Kombination aus &amp;lt;Device&amp;gt;|&amp;lt;Anzahl Datensätze in DB&amp;gt; enthalten wie im nebenstehenden Screenshot ersichtlich.&lt;br /&gt;
&lt;br /&gt;
[[Datei:sqlCmd.PNG|right|thumb|300px|sqlCmd ausgeführt]]&lt;br /&gt;
&lt;br /&gt;
Wenn das Ergebnis wie gewünscht vorliegt, wird nun die Benutzerschnittstelle aktiviert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Report userExitFn NaDevs .*:.*&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;NaDevs&amp;quot; ist die Funktion in 99_myUtils.pm die aufgerufen werden soll. Die Angabe des Regex &amp;quot;.*:.*&amp;quot; ist optional. Nähere Informationen zur Funktionsweise sind in der {{Link2CmdRef|Lang=de|Anker=DbRep}} zu finden.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;Positivliste&amp;quot; der in der Datenbank erlaubten Devices wird für das Beispiel in dem Attribut &amp;quot;comment&amp;quot; für den späteren Vergleich als String angegeben. Es ist natürlich auch z.B. eine Ableitung der im DEF-Regex des zugeordneten DbLog-Devices enthaltenen Device-Definitionen vorstellbar, sodass immer der aktuelle Stand dieser Definition als Grundlage für den Vergleich herangezogen wird. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Report comment (sysmon|MyWetter|SMA_Energymeter|STP_5000|Cam.*)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Somit werden die Devices &amp;quot;sysmon&amp;quot;, &amp;quot;MyWetter&amp;quot;, &amp;quot;SMA_Energymeter&amp;quot;, &amp;quot;STP_5000&amp;quot; und alle Cam-Devices als in der DB erlaubt definiert.&lt;br /&gt;
Alle anderen gefundenen Devices sollen in einer Negativliste aufgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Schritt wird die aufzurufende Funktion in der vorhandenen 99_myUtils.pm ergänzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
############################################################################&lt;br /&gt;
# $Id: myUtilsTemplate.pm 7570 2015-01-14 18:31:44Z rudolfkoenig $&lt;br /&gt;
#&lt;br /&gt;
# Save this file as 99_myUtils.pm, and create your own functions in the new&lt;br /&gt;
# file. They are then available in every Perl expression.&lt;br /&gt;
&lt;br /&gt;
package main;&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
myUtils_Initialize($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Enter you functions below _this_ line.&lt;br /&gt;
&lt;br /&gt;
my @dna;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
######################################################&lt;br /&gt;
########    Not allowed Devs in DB ermitteln&lt;br /&gt;
########          UserExitFn in DbRep           &lt;br /&gt;
######################################################&lt;br /&gt;
sub NaDevs {&lt;br /&gt;
 my ($name,$reading,$value) = @_;&lt;br /&gt;
 my $hash   = $defs{$name};&lt;br /&gt;
 &lt;br /&gt;
 # DB Namen ermitteln&lt;br /&gt;
 my $dbconn = $hash-&amp;gt;{dbloghash}{dbconn};&lt;br /&gt;
 my $db = (split(&amp;quot;=&amp;quot;,(split(&amp;quot;;&amp;quot;,$dbconn))[0]))[1];&lt;br /&gt;
 &lt;br /&gt;
 # Liste der erlaubten Devices aus Attribut &amp;quot;comment&amp;quot; lesen&lt;br /&gt;
 my $adevs = AttrVal($name, &amp;quot;comment&amp;quot;,&amp;quot;-&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
 if($reading eq &amp;quot;state&amp;quot; &amp;amp;&amp;amp; $value eq &amp;quot;running&amp;quot;) {&lt;br /&gt;
     # der Select wurde gestartet&lt;br /&gt;
     Log3 $name, 1, &amp;quot;UserExitFn called by $name - new Select has been startet. Allowed devices in database are: $adevs&amp;quot;; &lt;br /&gt;
     @dna=();&lt;br /&gt;
     return;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 my $d = (split(&amp;quot;\\|&amp;quot;,$value))[0];&lt;br /&gt;
 &lt;br /&gt;
 # das Selektionsergebnis bewerten und ggf. dem Ergebnisarray hinzufügen&lt;br /&gt;
 if ( $reading =~ m/SqlResultRow.*/ &amp;amp;&amp;amp; $d !~ m/$adevs/) {&lt;br /&gt;
   push(@dna,$d.&amp;quot;\n&amp;quot;)&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 if ($reading eq &amp;quot;state&amp;quot; &amp;amp;&amp;amp; $value eq &amp;quot;done&amp;quot;) {&lt;br /&gt;
     # Ergebnis versenden bzw. loggen&lt;br /&gt;
     Log3 $name, 1, &amp;quot;UserExitFn called by $name - Devices not allowd found in $db: @dna&amp;quot;; &lt;br /&gt;
     fhem(&amp;quot;set teleBot message Devices are not allowed found in $db: \n @dna&amp;quot;);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schnittstelle übergibt der aufgerufenen Funktion den Namen des DbRep-Devices, den Namen des erstellten Readings und dessen Wert.&lt;br /&gt;
Der Aufruf erfolgt nach jeder Readingserstellung. Die Schnittstelle arbeitet OHNE Eventgenerierung bzw. benötigt keine Events.&lt;br /&gt;
Über den Namen des DbRep-Devices kann auf dessen Hash zugegriffen werden bzw. über $hash-&amp;gt;{dbloghash}{...} ebenfalls auf den Hash des angeschlossenen DbLog-Devices.&lt;br /&gt;
&lt;br /&gt;
In der Funktion &amp;quot;NaDevs&amp;quot; wird der Device-Teilstring des erzeugten Readings mit dem Wertevorrat aus dem &amp;quot;comment&amp;quot;-Attribut verglichen und bei einem negativen Ergebnis der Negativliste @dna hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Nach Abschluss der Operation (signalisiert durch Reading &amp;quot;state = done&amp;quot;) wird die erzeugte Liste im Logfile ausgegeben bzw. ein TelgramBot-Device versendet.&lt;br /&gt;
&lt;br /&gt;
=== Größe der FHEM-Datenbank ermitteln ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird das Device wie im Abschnitt [[#Definieren_eines_DbRep-Devices | &amp;quot;Definieren eines DbRep-Devices&amp;quot;]] beschrieben definiert. &lt;br /&gt;
Dadurch wird das DbRep-Device mit einem DbLog-Device assoziiert und mit der Datenbank des DbLogs verbunden. Alle weiteren Operationen werden mit dieser Datenbank durchgeführt. In diesem Beispiel heißt das DbRep-Device &amp;quot;Rep.Fhem.Size&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Um die Größe der Datenbank beziehungsweise der Tabellen &amp;quot;history&amp;quot; und &amp;quot;current&amp;quot; zu ermitteln steht das Kommando:&lt;br /&gt;
                                                                        [[Datei:tableinfo.PNG|right|thumb|300px|get Rep.Fhem.Size tableinfo]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 get Rep.Fhem.Size tableinfo  (MySQL, PostgreSQL)&lt;br /&gt;
 get Rep.Fhem.Size svrinfo    (SQLite)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
zur Verfügung. &lt;br /&gt;
&lt;br /&gt;
Mit diesem Befehl werden sehr viele Informationen bezüglich der Tabellen des FHEM-Schemas (MySQL) als Readings ausgegeben.&lt;br /&gt;
&lt;br /&gt;
Um nur die relevanten bzw. interessierenden Selektionsergebnisse auszugeben, kann mit dem Attribut showTableInfo (MySQL, PostgreSQL) bzw. showSvrInfo (SQLite) eine kommaseparierte Liste der relevanten Tabellen angegeben werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.Fhem.Size showTableInfo %history%,%current%    (MySQL, PostgreSQL)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So wie in diesem Beispiel gesetzt, werden nur Informationen der Tabellen &amp;quot;history&amp;quot; und &amp;quot;current&amp;quot; ausgegeben.&lt;br /&gt;
Diese Ausgabe ich bereits recht übersichtlich, kann aber durch die Verwendung des Attributs suppressReading&lt;br /&gt;
weiter eingegrenzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.Fhem.Size suppressReading ^(?!.*INFO_history.data_index_length_MB).*$&lt;br /&gt;
attr Rep.Fhem.Size suppressReading ^(?!.*(INFO_history.data_index_length_MB)|(state)).*$&lt;br /&gt;
attr Rep.Fhem.Size suppressReading ^(?!.*(INFO_history.data_index_length_MB)|(background_processing_time)|(state)).*$&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit diesen Attributwerten wird nur das Reading &amp;quot;INFO_history.data_index_length_MB&amp;quot; oder aber &amp;quot;INFO_history.data_index_length_MB&amp;quot;&lt;br /&gt;
und &amp;quot;state&amp;quot; bzw. &amp;quot;INFO_history.data_index_length_MB&amp;quot;, &amp;quot;state&amp;quot; und &amp;quot;background_processing_time&amp;quot; dargestellt.&lt;br /&gt;
&lt;br /&gt;
Die Datenbankgröße (MB) ist je nach DB-Typ in den Readings:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
INFO_current.data_index_lenth_MB&lt;br /&gt;
INFO_history.data_index_lenth_MB&lt;br /&gt;
SQLITE_FILE_SIZE_MB&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
enhalten. &lt;br /&gt;
&lt;br /&gt;
Wird z.B. über ein AT dieser Befehl regelmäßig durch Rep.Fhem.Size ausgeführt und die Ergebnisse wiederum in DbLog gespeichert kann die Größenentwicklung der Datenbank mittels SVG-Diagrammen dargestellt werden.                                                                            [[Datei:tableinfo_SVG.PNG|left|thumb|300px|get Rep.Fhem.Size tableinfo]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot;&amp;gt;&lt;br /&gt;
=== Readingwerte von DbRep in ein anderes Device übertragen ===&lt;br /&gt;
&lt;br /&gt;
Um Readings aus DbRep in einen Dummy zu übertragen, können verschiedene Verfahren angewendet werden. Allen Verfahren ist gemeinsam, dass sie auf die Arbeitsweise der Funktionen von DbRep Rücksicht nehmen. Alle Funktionen von DbRep arbeiten, von Ausnahmen abgesehen, asynchron (non-blocking). Das hat den Vorteil, dass die Datenbankverarbeitungszeit bzw. eine Nichtverfügbarkeit der DB nicht hemmend auf FHEM wirkt, hat andererseits aber den Nachteil, dass die Ergebnisse einer Funktion nicht direkt nach dem Funktionsaufruf zur Verfügung stehen und somit nicht trivial innerhalb der FHEM-Hauptschleife weiterverarbeitet werden können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Werte automatisch durch DbRep übertragen lassen (ab Version 8.40.0) ====&lt;br /&gt;
&lt;br /&gt;
Ab DbRep Version 8.40.0 gibt es das Attribut &#039;&#039;&#039;autoForward&#039;&#039;&#039; mit dem eine integrierte Übertragung der DbRep-Ergebnissreadings in andere Devices (z.B. Dummy) eingerichtet werden kann. Diese Variante wird hier beschrieben.&lt;br /&gt;
&lt;br /&gt;
Die weiteren manuellen Varianten werden in den nachfolgenden Abschnitten behandelt.&lt;br /&gt;
&lt;br /&gt;
Um die integrierte Reading-Weiterleitung zu aktivieren wird des Attribut autoForward nach folgender Systematik gesetzt:&lt;br /&gt;
[[Datei:aforw1.PNG|right|thumb|300px|Übertragung aller Readings nach Dum.Rep.all]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;&amp;lt;source-reading&amp;gt; =&amp;gt; &amp;quot;&amp;lt;destination device&amp;gt; [=&amp;gt; &amp;lt;destination-reading&amp;gt;]&amp;quot;,&lt;br /&gt;
  &amp;quot;&amp;lt;source-reading&amp;gt; =&amp;gt; &amp;quot;&amp;lt;destination device&amp;gt; [=&amp;gt; &amp;lt;destination-reading&amp;gt;]&amp;quot;,&lt;br /&gt;
  ...&lt;br /&gt;
}          &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Datei:aforw2.PNG|right|thumb|300px|Übertragung Readings mit &amp;quot;SUM&amp;quot; nach Dum.Rep.Sum]]&lt;br /&gt;
In der Spezifikation von &#039;&#039;&#039;&amp;lt;source-reading&amp;gt;&#039;&#039;&#039; können Wildcards (.*) verwendet werden um nur eine Auswahl der erzeugten Readings an das Zieldevice &#039;&#039;&#039;&amp;lt;destination device&amp;gt;&#039;&#039;&#039; weiterzuleiten. Ist der optionale Zusatz &#039;&#039;&#039;&amp;lt;destination-reading&amp;gt;&#039;&#039;&#039; angegeben, erfolgt die Speicherung der übertragenen Readings im Zieldevice mit dem angegebenen Namen. &amp;lt;br&amp;gt;&lt;br /&gt;
Der angegebene Readingname muss den Regularien zur Namensgebung von Readings genügen. Eventuell vorhandene nicht erlaubte Zeichen werden durch &amp;quot;_&amp;quot; ersetzt.&lt;br /&gt;
&lt;br /&gt;
Fehlt der Zusatz &amp;lt;destination-reading&amp;gt;, werden die in DbRep erzeugten Readings 1:1 in das Zieldevice übertragen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr DbRepDev autoForward {&lt;br /&gt;
                            &amp;quot;.*&amp;quot;        =&amp;gt; &amp;quot;Dum.Rep.All&amp;quot;,     &lt;br /&gt;
                            &amp;quot;.*AVGAM.*&amp;quot; =&amp;gt; &amp;quot;Dum.Rep     =&amp;gt; average&amp;quot;,&lt;br /&gt;
                            &amp;quot;.*SUM.*&amp;quot;   =&amp;gt; &amp;quot;Dum.Rep.Sum =&amp;gt; summary&amp;quot;,&lt;br /&gt;
                          }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Spezifikation erfüllt folgende Ziele:&lt;br /&gt;
&lt;br /&gt;
# alle Readings werden zum Device &amp;quot;Dum.Rep.All&amp;quot; übertragen, der ursprüngliche Readingname bleibt im Ziel erhalten (Screenshot 1)&lt;br /&gt;
# Readings mit &amp;quot;AVGAM&amp;quot; im Namen werden zum Device &amp;quot;Dum.Rep&amp;quot; in das Reading &amp;quot;average&amp;quot; übertragen&lt;br /&gt;
# Readings mit &amp;quot;SUM&amp;quot; im Namen werden zum Device &amp;quot;Dum.Rep.Sum&amp;quot; in das Reading &amp;quot;summary&amp;quot; übertragen (Screenshot 2)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Werte mittels Event übetragen ====&lt;br /&gt;
&lt;br /&gt;
Es sollen zum Beispiel die erzeugten Readings aus einem DbRep-Device in einen Dummy übetragen werden, die den Term &amp;quot;Grid&amp;quot; im Wortstamm tragen.&lt;br /&gt;
&lt;br /&gt;
Ein Reading-Name der fetchrows-Funktion in DbRep ist immer nach folgendem Schema aufgebaut:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;Datum&amp;gt;_&amp;lt;Zeit&amp;gt;__&amp;lt;Unique-Index&amp;gt;__&amp;lt;Device&amp;gt;__&amp;lt;Reading&amp;gt;__&amp;lt;Dopplerindex&amp;gt;&lt;br /&gt;
 z.B.: 2018-10-14_18-30-25__1__MyWetter__humidity&lt;br /&gt;
&lt;br /&gt;
Datum und Zeit entsprechen dem Timestamp des gespeicherten Events in der Datenbank. Der Unique-Index identifiziert eventuell vorhandene Doubletten in der DB und der Dopplerindex ist ein Hilfsmittel, Datensätze die sich allein durch den Value-Wert unterscheiden, auch als unterschiedliche Readings erstellen zu können. Dieser Index wird nur bei Bedarf erstellt. &lt;br /&gt;
&lt;br /&gt;
Im folgenden Beispiel werden alle erzeugten Readings mit &amp;quot;Grid&amp;quot; im Namen des DbRep-Devices &amp;quot;Rep.SMAEM&amp;quot; in den Dummy übertragen und heißen dann genauso. Zunächst wird der Dummy angelegt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Dum.Rep dummy&lt;br /&gt;
attr Dum.Rep room DbLog&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dem nachfolgend angelegten Notify wird auf die z.B. von fetchrows erzeugten Events reagiert, der Event entsprechend gesplittet und verarbeitet in den Dummy übertragen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define N.Dum.Rep notify Rep.SMAEM:(\d).*Grid.* { fhem &amp;quot;setreading Dum.Rep &amp;quot;.(split(&amp;quot;:&amp;quot;,$EVTPART0))[0].&amp;quot; $EVTPART1&amp;quot;}&lt;br /&gt;
attr N.Dum.Rep room DbLog&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll im Dummy ein Reading mit eigenem Namen gefüllt werden, sieht das Notify z.B. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define N.Dum.Rep notify Rep.SMAEM:(\d).*Grid.* { fhem &amp;quot;setreading Dum.Rep DeinReading&amp;quot;.&amp;quot; $EVTPART1&amp;quot;}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Werte mittels Funktion DbReadingsVal übertragen ====&lt;br /&gt;
&lt;br /&gt;
Sobald ein DbRep-Device definiert ist, wird die Funktion DbReadingsVal zur Verfügung gestellt. Mit dieser Funktion läßt sich, ähnlich dem allgemeinen ReadingsVal, der Wert eines Readings aus der Datenbank abrufen.  Die Befehlssyntax ist:&lt;br /&gt;
&lt;br /&gt;
    DbReadingsVal(&amp;quot;&amp;lt;name&amp;gt;&amp;quot;,&amp;quot;&amp;lt;device:reading&amp;gt;&amp;quot;,&amp;quot;&amp;lt;timestamp&amp;gt;&amp;quot;,&amp;quot;&amp;lt;default&amp;gt;&amp;quot;) &lt;br /&gt;
&lt;br /&gt;
Mit dieser Funktion kann ein Device/Reading-Wert aus der Datenbank gelesen und in einen Dummy gesetzt werden. &amp;quot;Name&amp;quot; ist dabei das abzufragende DbRep-Device. &lt;br /&gt;
Der Timestamp muss nicht genau bekannt sein. Es wird der zeitlich zu &amp;lt;timestamp&amp;gt; passendste Readingwert zurück geliefert, falls kein Wert exakt zu dem angegebenen Zeitpunkt geloggt wurde. &lt;br /&gt;
Um den aktuellsten in der Datenbank gespeicherten Wert eines Readings abzurufen, kann als Timestamp die aktuelle Zeit entsprechend formatiert verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Es soll z.B. der aktuellste Datenbankwert des Readings &amp;quot;etoday&amp;quot; vom Device &amp;quot;MySTP_5000&amp;quot; ausgelesen und im Dummy &amp;quot;Dum.Rep&amp;quot; als Reading &amp;quot;EnergyToday&amp;quot; gespeichert werden. Das beteilgte DbRep-Device heißt &amp;quot;Rep.Energy&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Für die Formatierung des aktuellen Timestamps kann die FHEM-Funktion FmtDateTime verwendet werden, welcher der aktuelle UNIX-Timestamp übergeben wird:&lt;br /&gt;
&lt;br /&gt;
 FmtDateTime(time)&lt;br /&gt;
&lt;br /&gt;
Der Datenabruf aus der Datenbank sieht dann folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 DbReadingsVal(&amp;quot;Rep.Energy&amp;quot;,&amp;quot;MySTP_5000:etoday&amp;quot;,FmtDateTime(time),&amp;quot;&amp;quot;) &lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl setreading kann der abgerufene Wert in dem Dummy-Device gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
 { fhem &amp;quot;setreading Dum.Rep EnergyToday &amp;quot;.DbReadingsVal(&amp;quot;Rep.Energy&amp;quot;,&amp;quot;MySTP_5000:etoday&amp;quot;,FmtDateTime(time),&amp;quot;&amp;quot;) }&lt;br /&gt;
&lt;br /&gt;
Damit der Datenabruf regelmäßig erfolgt und immer der aktuellste Wert von &amp;quot;etoday&amp;quot; in den Dummy eingtragen wird, kann dieses AT verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 define set.Dum.Rep_EnergyToday at +*00:01:00 { fhem &amp;quot;setreading Dum.Rep EnergyToday &amp;quot;.DbReadingsVal(&amp;quot;Rep.Energy&amp;quot;,&amp;quot;MySTP_5000:etoday&amp;quot;,FmtDateTime(time),&amp;quot;&amp;quot;) }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Somit erfogt eine Aktualisierung des Readings &amp;quot;EnergyToday&amp;quot; im Dummy &amp;quot;Dum.Rep&amp;quot; jede Minute.&lt;br /&gt;
Es ist zu beachten, dass die Funktionsausführung von DbReadingsVal blockierend erfolgt. Sollte die Datenbank nicht verfügbar sein oder lange Antwortzeiten besitzen, hat dies negative Auswirkungen auf FHEM, was sich in Blockierungszuständen äußert.&lt;br /&gt;
Möchte man solche eventuell möglichen Zustände vermeiden, kann die Befehlsausführung in die 99_myUtils unter Verwendung von [[Blocking_Call | Blocking Call]] ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Diese non-blocking Variante der Verwendung von DbReadingsVal wird nachfolgend skizziert.&lt;br /&gt;
In der 99_myUtils wird dazu der nachfolgende Code eingefügt, der drei kleine Subroutinen enthält:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
############################################################################################################&lt;br /&gt;
#         DumRepEnergyToday (Reading mittels DbReadingsVal non-blocking setzen)    &lt;br /&gt;
############################################################################################################&lt;br /&gt;
sub DumRepEnergyToday ($$$$$) {&lt;br /&gt;
 # initiale Routine zum Aufruf von BlockingCall unter Berücksichtigung von &amp;quot;RUNNING_SET_READING&amp;quot;&lt;br /&gt;
 my ($name,$device,$reading,$destdev,$destread) = @_;&lt;br /&gt;
 my $hash = $defs{$name};&lt;br /&gt;
&lt;br /&gt;
 return if($hash-&amp;gt;{HELPER}{RUNNING_SET_READING});&lt;br /&gt;
 $hash-&amp;gt;{HELPER}{RUNNING_SET_READING} = BlockingCall(&amp;quot;DumRepEnergyTodayNbl&amp;quot;, &amp;quot;$name|$device|$reading|$destdev|$destread&amp;quot;, &amp;quot;DumRepEnergyTodayNblDone&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub DumRepEnergyTodayNbl($) {&lt;br /&gt;
 # die eigentliche Routine in der die potentiell blockierende Funktion ausgeführt wird&lt;br /&gt;
 my ($string) = @_;&lt;br /&gt;
 my ($name,$device,$reading,$destdev,$destread) = split(&amp;quot;\\|&amp;quot;, $string);&lt;br /&gt;
 my $hash = $defs{$name};&lt;br /&gt;
&lt;br /&gt;
 my $val = DbReadingsVal($name,&amp;quot;$device:$reading&amp;quot;,FmtDateTime(time),&amp;quot;&amp;quot;);&lt;br /&gt;
 $val = encode_base64($val,&amp;quot;&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
return &amp;quot;$name|$val|$destdev|$destread&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub DumRepEnergyTodayNblDone($) {&lt;br /&gt;
 # Rückkherfunktion zur Ergebnisauswertung und Löschen von &amp;quot;RUNNING_SET_READING&amp;quot;&lt;br /&gt;
 my ($string) = @_;&lt;br /&gt;
 my @a    = split(&amp;quot;\\|&amp;quot;,$string);&lt;br /&gt;
 my $hash = $defs{$a[0]};&lt;br /&gt;
 my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
 my $val  = decode_base64($a[1]);&lt;br /&gt;
 my $destdev  = $a[2];&lt;br /&gt;
 my $destread = $a[3];&lt;br /&gt;
 &lt;br /&gt;
 fhem &amp;quot;setreading $destdev $destread $val&amp;quot;;&lt;br /&gt;
 delete $hash-&amp;gt;{HELPER}{RUNNING_SET_READING};&lt;br /&gt;
 &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der oben gezeigte Code stellt die einfachste Form der BlockingCall-Implementierung dar. Weitere Informationen dazu sind im weiter oben angegebenen Link zum BlockingCall-Wiki enthalten.&lt;br /&gt;
&lt;br /&gt;
Um non-blocking Funktion aufzurufen, ist das bereits beschriebene AT-Device wie folgt zu ändern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define set.Dum.Rep_EnergyTodayNbl at +*00:01:00 { DumRepEnergyToday(&amp;quot;Rep.Energy&amp;quot;,&amp;quot;MySTP_5000&amp;quot;,&amp;quot;etoday&amp;quot;,&amp;quot;Dum.Rep&amp;quot;,&amp;quot;EnergyToday&amp;quot;) }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der aufgerufenen Funktion &amp;quot;DumRepEnergyToday&amp;quot; werden hierbei der Name des abzufragenden DbRep-Devices, der Name des auszuwertenden Devices, der Name des auszuwertenden Readings sowie das Ziel-Device und das Ziel-Reading als Argumente mitgegeben. Dieser Aufruf kann dadurch universell zum Abruf und Setzen verschiedener auszuwertender Device/Reading-Kombinationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Ein Userreading anlegen und für stateformat verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit beim Modul DbRep besteht darin, dass sich die Namen der generierten Readings ändern können. Dadurch kann man für die Erstellung eines eigenen Userreading nicht einfach den Namen eines erstellten Readings, z.B. in der Funktion ReadingsVal, verwenden um dessen Wert zu ermitteln.&lt;br /&gt;
&lt;br /&gt;
Eine Möglichkeit besteht darin eine Funktion in 99_myUtils anzulegen und mit dem Attribut &amp;quot;userExitFn&amp;quot; diese Funktion zu aktivieren/zu verwenden.&lt;br /&gt;
&lt;br /&gt;
Für das Beispiel soll ein Reading &amp;quot;power_max&amp;quot; für den erreichten Maximalwert im Monat und der Timestamp des Maximalwertes aus einem Beispielreading &amp;quot;2018-06-01_13-23-02__STP_5000__total_pac__MAX__no_aggregation&amp;quot; (Name kann sich durch das jeweilige Datum ändern) extrahiert werden.&lt;br /&gt;
&lt;br /&gt;
Zunächst wird die Funktion &amp;quot;calcpomax&amp;quot; in 99_myUtils angelegt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
############################################################################################################&lt;br /&gt;
########           power_max in DbRep erstellen   &lt;br /&gt;
############################################################################################################&lt;br /&gt;
sub calcpomax {&lt;br /&gt;
 my ($name,$reading,$value) = @_;&lt;br /&gt;
 my $hash = $defs{$name};&lt;br /&gt;
 &lt;br /&gt;
 if($reading =~ /^.*total_pac__MAX.*$/) {&lt;br /&gt;
     $reading =~ /^(\d+)-(\d+)-(\d+)_(\d+)-(\d+)-(\d+)_.*$/;&lt;br /&gt;
     my $pmts = &amp;quot;$3.$2.$1 $4:$5:$6&amp;quot;;&lt;br /&gt;
     my $fmtDateTime = FmtDateTime(gettimeofday());&lt;br /&gt;
     setReadingsVal($hash,&amp;quot;power_max&amp;quot;,$value,$fmtDateTime);&lt;br /&gt;
     setReadingsVal($hash,&amp;quot;power_max_ts&amp;quot;,$pmts,$fmtDateTime);&lt;br /&gt;
 }&lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie in der Commandref zu {{Link2CmdRef|Anker=DbRep|Lang=de|Label=DbRep}} zu lesen ist, werden der im Attribut &amp;quot;userExitFn&amp;quot; hinterlegten Funktion jeweils der Name des aufrufenden Devices, das Reading und dessen Wert übergeben. Mit ein paar Zeilen Code wird der übergebene Readingsname auf das Vorhandensein des statischen Namensteils (total_pac__MAX) geprüft und dementsprechend der Readingname &amp;quot;power_max&amp;quot; mit dem Wert angelegt, wenn der Regex auf den Readingnamen matched.&lt;br /&gt;
Gleiches gilt für das Reading &amp;quot;power_max_ts&amp;quot;, welches den Zeitpunkt des erreichten Maximalwertes enthalten soll.&lt;br /&gt;
&lt;br /&gt;
Die Aktivierung der Schnittstelle erfolgt im DbRep-Device mit &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;name&amp;gt; userExitFn calcpomax .*:.*&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit jedem Lauf von &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set &amp;lt;name&amp;gt; maxvalue&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird nun das neue Reading mit angelegt und kann z.B. in einem stateformat verwendet werden:  &lt;br /&gt;
 [[Datei:Rep.powmax.PNG|right|thumb|500px|]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;name&amp;gt; stateformat&lt;br /&gt;
{  &lt;br /&gt;
 if(ReadingsVal($name,&amp;quot;power_max&amp;quot;,0)) {&lt;br /&gt;
   (ReadingsVal($name,&amp;quot;power_max&amp;quot;,0)*1000).&amp;quot; Watt (erreicht am &amp;quot;.ReadingsVal($name,&amp;quot;power_max_ts&amp;quot;,&amp;quot;&amp;quot;).&amp;quot;)&amp;quot;;&lt;br /&gt;
 } else {&lt;br /&gt;
   ReadingsVal($name,&amp;quot;state&amp;quot;,&amp;quot;done&amp;quot;);&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachfolgend noch die Definition des verwendeten DbRep-Devices für diese Auswertung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod Rep.total_pac.currmonth DbRep LogDB&lt;br /&gt;
attr Rep.total_pac.currmonth aggregation no&lt;br /&gt;
attr Rep.total_pac.currmonth alias Peak WR-Leistung aktueller Monat&lt;br /&gt;
attr Rep.total_pac.currmonth allowDeletion 0&lt;br /&gt;
attr Rep.total_pac.currmonth devStateIcon connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.total_pac.currmonth device STP_5000&lt;br /&gt;
attr Rep.total_pac.currmonth disable 0&lt;br /&gt;
attr Rep.total_pac.currmonth event-on-update-reading state&lt;br /&gt;
attr Rep.total_pac.currmonth group SMA Inverter&lt;br /&gt;
attr Rep.total_pac.currmonth reading total_pac&lt;br /&gt;
attr Rep.total_pac.currmonth room Energie&lt;br /&gt;
attr Rep.total_pac.currmonth showproctime 1&lt;br /&gt;
attr Rep.total_pac.currmonth stateFormat {  \&lt;br /&gt;
 if(ReadingsVal($name,&amp;quot;power_max&amp;quot;,0)) {\&lt;br /&gt;
   (ReadingsVal($name,&amp;quot;power_max&amp;quot;,0)*1000).&amp;quot; Watt (erreicht am &amp;quot;.ReadingsVal($name,&amp;quot;power_max_ts&amp;quot;,&amp;quot;&amp;quot;).&amp;quot;)&amp;quot;;;\&lt;br /&gt;
 } else {\&lt;br /&gt;
   ReadingsVal($name,&amp;quot;state&amp;quot;,&amp;quot;done&amp;quot;);;\&lt;br /&gt;
 }\&lt;br /&gt;
}&lt;br /&gt;
attr Rep.total_pac.currmonth timestamp_begin current_month_begin&lt;br /&gt;
attr Rep.total_pac.currmonth timestamp_end current_month_end&lt;br /&gt;
attr Rep.total_pac.currmonth userExitFn calcpomax .*:.*&lt;br /&gt;
attr Rep.total_pac.currmonth verbose 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== datenbankgestützte Erstellung der Energiebilanz einer SMA PV-Anlage mit Überschußeinspeisung ===&lt;br /&gt;
[[datenbankgestützte Erstellung der Energiebilanz einer SMA PV-Anlage mit Überschußeinspeisung | Hier]] geht&#039;s zum Beitrag.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== DbRep-Kommandos regelmäßig mit einem at-Device ausführen ===&lt;br /&gt;
&lt;br /&gt;
Sollen DbRep-Kommandos regelmäßig zu bestimmten Zeiten bzw. Intervallen ausgeführt werden, kann ein at-Device (alternativ z.B. DOIF) dafür definiert und verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel soll die regelmäßige Ausführung des SQL-Statements &lt;br /&gt;
&lt;br /&gt;
 select device, count(*) from history group by DEVICE&lt;br /&gt;
&lt;br /&gt;
über das Set-Kommando &#039;&#039;&#039;sqlCmd&#039;&#039;&#039; dienen.&lt;br /&gt;
&lt;br /&gt;
Zunächst wird das DbRep-Device definiert welches zur Ausführung des Kommandos dienen soll.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.LogDB.sqlResult DbRep LogDB&lt;br /&gt;
attr Rep.LogDB.sqlResult devStateIcon initialized:control_3dot_hor_s connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.LogDB.sqlResult event-on-update-reading state&lt;br /&gt;
attr Rep.LogDB.sqlResult fastStart 1&lt;br /&gt;
attr Rep.LogDB.sqlResult room Datenbank&lt;br /&gt;
attr Rep.LogDB.sqlResult showproctime 1&lt;br /&gt;
attr Rep.LogDB.sqlResult sqlResultFormat table&lt;br /&gt;
attr Rep.LogDB.sqlResult verbose 2&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Definition Syntax wurde bereits weiter oben erläutert. Das Attribut &#039;&#039;&#039;sqlResultFormat&#039;&#039;&#039; legt fest in welcher Art und Weise das Ergebnis präsentiert werden soll. Auch andere Formate wie JSON sind implementiert.&lt;br /&gt;
&lt;br /&gt;
Um das oben angegbene SQL-Statement zum Beispiel alle 6 Stunden 20 Minuten auszuführen, dient die folgende at-Definition: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define At.LogDB.sqlResult at +*06:20:00 set Rep.LogDB.sqlResult sqlCmd select device, count(*) from history group by DEVICE&lt;br /&gt;
attr At.LogDB.sqlResult icon clock&lt;br /&gt;
attr At.LogDB.sqlResult room Datenbank-&amp;gt;Produktiv&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen sind in der [https://fhem.de/commandref_DE.html#at at-CommandRef] beschrieben.&lt;br /&gt;
&lt;br /&gt;
Natürlich sind alle verfügbaren DbRep Set/Get-Befehle über diesen Weg ausführbar.&lt;br /&gt;
Es wird empfohlen das definierte DbRep-Device nur für Auswertungsaufgaben zu verwenden die eine gleiche Attributierung des Devices verlangen. &lt;br /&gt;
Für weitere Aufgaben sollten weitere DbRep-Devices definiert und entsprechend eingestellt werden. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbRep Chain - die Kommandokette ===&lt;br /&gt;
&lt;br /&gt;
Für regelmäßig auszuführende Aufgaben erstellt man sich je ein separates DbRep-Device und setzt die Attribute wie es für die spezifische Aufgabe erforderlich ist. &lt;br /&gt;
Zu diesem Zweck kopiert man sich einfach ein vorhandenes DbRep-Device mit dem FHEM &#039;&#039;copy&#039;&#039; Kommando und passt es danach entsprechend an. Es können beliebig viele DbRep-Devices angelegt werden.&lt;br /&gt;
&lt;br /&gt;
Häufig besteht der Wunsch, verschiedene Datenbankaktionen nacheinander ablaufen zu lassen, z.B. erst &#039;&#039;minValue writeToDB&#039;&#039; danach &#039;&#039;maxValue writeToDB&#039;&#039; sowie &#039;&#039;averageValue writeToDB&#039;&#039; nacheinander auszuführen.&lt;br /&gt;
Eine solche Verkettung kann durch die Nutzung des &#039;&#039;executeAfterProc&#039;&#039; Attributes erstellt werden.&lt;br /&gt;
&lt;br /&gt;
Für das Beispiel werden 3 DbRep-Devices angelegt:&lt;br /&gt;
&lt;br /&gt;
 * Rep_Min&lt;br /&gt;
 * Rep_Max&lt;br /&gt;
 * Rep_Avg&lt;br /&gt;
&lt;br /&gt;
Das erste Device (Rep_Min) soll für &#039;&#039;minValue writeToDB&#039;&#039; genutzt werden. In diesem Device wird das Attribut gesetzt:&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
 attr Rep_Min executeAfterProc set Rep_Max maxValue writeToDB&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dieses Attribut zeigt auf das zweite Device (Rep_Max), welche für &#039;&#039;maxValue writeToDB&#039;&#039; verwendet werden soll. &lt;br /&gt;
In diesem Device wird wiederum das Attribut gesetzt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 attr Rep_Min executeAfterProc set Rep_Avg averageValue writeToDB&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die gesamte Chain startet mit einem at-Device, notify oder dergleichen mit dem Kommando:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  set Rep_Min minValue writeToDB&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Funktionsweise:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Die Attribute executeBeforeProc und executeAfterProc führen jeweils das in ihnen hinterlegte FHEM-Kommando oder Perl-Funktion vor bzw. nach der Ausführung des gestarteten DbRep-Kommandos aus. &amp;lt;br&amp;gt;&lt;br /&gt;
In dem dargestellten Beispiel startet das at-Device mit &lt;br /&gt;
&lt;br /&gt;
  set Rep_Min minValue writeToDB&lt;br /&gt;
&lt;br /&gt;
die Funktion &#039;&#039;minValue writeToDB&#039;&#039; im Device &#039;&#039;Rep_Min&#039;&#039;.  Wenn das DbRep &#039;&#039;Rep_Min&#039;&#039; mit seiner Aufgabe fertig ist, startet es automatisch über executeAfterProc das DbRep &#039;&#039;Rep_Max&#039;&#039; die Ausführung von &#039;&#039;&#039;maxValue writeToDB&#039;&#039;&#039;. Hat DbRep &#039;&#039;Rep_Max&#039;&#039;seine Aufgabe beendet, startet es wiederum im letzten Device &#039;&#039;Rep_Avg&#039;&#039; das Kommando &#039;&#039;averageValue writeToDB&#039;&#039;. &amp;lt;br&amp;gt;&lt;br /&gt;
Mit diesem Verfahren läuft immer nur ein Kommando der Reihe nach über die Datenbank.&lt;br /&gt;
&lt;br /&gt;
Natürlich kann diese Chain beliebig erweitert werden, oder im ersten DbRep vor dem Start der Chain das angeschlossene DbLog-Device pausiert werden mit:&lt;br /&gt;
&lt;br /&gt;
 attr Rep_Min executeBeforeProc set &amp;lt;DbLog-Device&amp;gt; reopen 7200&lt;br /&gt;
&lt;br /&gt;
Die Beendigung der Pause des DbLog kann das letzte DbRep &#039;&#039;Rep_Avg&#039;&#039; übernehmen durch Setzen des Attributes:&lt;br /&gt;
&lt;br /&gt;
 attr Rep_Avg executeAfterProc set &amp;lt;DbLog-Device&amp;gt; reopen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Abarbeitung einer sequentiellen Befehlskette mittels non-blocking sqlCmd-Kommandos ===&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; In der aktuellen DbRep-Version kann die seqientielle Kommandoabarbeitung über die [[#DbRep_Chain_-_die_Kommandokette|Chain-Funktionalität]] realisiert werden.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vorteil von DbRep ist die nicht blockierende Arbeitsweise fast aller DbRep-Befehle (auf Ausnahmen wird in der Commandref hingewiesen).&lt;br /&gt;
Daraus ergibt sich aber auch das Merkmal, dass nach dem Funktionsaufruf nicht auf das Ergebnis gewartet wird und die FHEM-Schleife  &lt;br /&gt;
unmittelbar nach dem Start des Befehls weiterläuft. Um mehrere voneinander abhängige Befehle, die eine festgelegte Reihenfolge erfüllen müssen, abzuarbeiten, kann die nachfolgend beschriebene Technik zur Kettenbildung angewendet werden. Diese Technik wird anhand einer einfachen Demo erläutert. &lt;br /&gt;
&lt;br /&gt;
Das konstruierte Ziel der Kette ist folgendes:&lt;br /&gt;
&lt;br /&gt;
* Es soll nacheinander der Insert eines Datensatzes durchgeführt, danach ein Select dieses Datensatzes, ein Update auf ein Feld und zuletzt ein delete dieses Datensatzes in dieser Reihenfolge ausgeführt werden&lt;br /&gt;
&lt;br /&gt;
Demnach sich insgesamt vier SQL-Befehle nacheinander auszuführen (Insert, Select, Update, Delete).&lt;br /&gt;
Zunächst werden vier identische DbRep-Devices angelegt, die sich lediglich durch ihren Namen unterscheiden. Das wären die Devices:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Rep.insert, Rep.select, Rep.update, Rep.delete&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur Anlage kann ein DbRep-Device als Vorlage erstellt und nachfolgend dreimal kopiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.insert DbRep LogDB&lt;br /&gt;
attr Rep.insert allowDeletion 1&lt;br /&gt;
attr Rep.insert showproctime 1&lt;br /&gt;
attr Rep.insert userExitFn chain&lt;br /&gt;
&lt;br /&gt;
copy Rep.insert Rep.select&lt;br /&gt;
copy Rep.insert Rep.update&lt;br /&gt;
copy Rep.insert Rep.delete&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das wichtige Detail dieser Devices ist das gesetzte Attribut &amp;quot;userExitFn = chain&amp;quot;. Dieses Attribut aktiviert eine Schnittstelle zum Aufruf von benutzereigenem Code nach dem Abschluß eines Datenbank-Calls (siehe Commandref).&lt;br /&gt;
&lt;br /&gt;
Der benutzereigene Code wird nach folgendem Schema in die 99_myUtils.pm eingebaut:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
#############################################################################################&lt;br /&gt;
#  sqlCmd Commandchain zur sequentiellen Abarbeitung von non-blocking DbRep-Befehlen&lt;br /&gt;
#&lt;br /&gt;
#  genutzte werden vier DbRep-Devices:  Rep.insert, Rep.select, Rep.update, Rep.delete&lt;br /&gt;
#       &lt;br /&gt;
#############################################################################################&lt;br /&gt;
sub chain {&lt;br /&gt;
 my ($name,$reading,$value) = @_;&lt;br /&gt;
 my $hash   = $defs{$name};&lt;br /&gt;
 my $device = &amp;quot;Testdevice&amp;quot;;&lt;br /&gt;
 my $model  = &amp;quot;Testmodel&amp;quot;;&lt;br /&gt;
 my $rc;&lt;br /&gt;
 &lt;br /&gt;
 if ($name eq &amp;quot;Rep.insert&amp;quot; &amp;amp;&amp;amp; $reading eq &amp;quot;chainstart&amp;quot;) {&lt;br /&gt;
     # Start der 1. Operation (insert)&lt;br /&gt;
     CommandSet(undef,&amp;quot;Rep.insert sqlCmd insert into history (DEVICE, TYPE, EVENT, READING, VALUE, UNIT)&lt;br /&gt;
	   values (\&amp;quot;$device\&amp;quot;, \&amp;quot;$model\&amp;quot;, \&amp;quot;definition\&amp;quot;, \&amp;quot;DEF\&amp;quot;, \&amp;quot;Hugo\&amp;quot;, \&amp;quot;Emma\&amp;quot;)&amp;quot;);&lt;br /&gt;
     Log3 $name, 1, &amp;quot;$name - Start chain with insert&amp;quot;;&lt;br /&gt;
     return;   &lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 if($reading eq &amp;quot;state&amp;quot; &amp;amp;&amp;amp; $value eq &amp;quot;done&amp;quot;) {&lt;br /&gt;
     if ($name eq &amp;quot;Rep.insert&amp;quot;) {&lt;br /&gt;
         # Auswertung der 1. Operation (insert)&lt;br /&gt;
         $rc = ReadingsVal($name, &amp;quot;SqlResultRow_1&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
         Log3 $name, 1, &amp;quot;$name - number of inserts: $rc&amp;quot;;&lt;br /&gt;
         &lt;br /&gt;
         # Start der 2. Operation (select)&lt;br /&gt;
         CommandSet(undef,&amp;quot;Rep.select sqlCmd select VALUE from history where device=\&amp;quot;$device\&amp;quot; and READING=\&amp;quot;DEF\&amp;quot; LIMIT 1;&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
   &lt;br /&gt;
     if ($name eq &amp;quot;Rep.select&amp;quot;) {&lt;br /&gt;
         # Auswertung der 2. Operation (select)&lt;br /&gt;
         $rc = ReadingsVal($name, &amp;quot;SqlResultRow_1&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
         Log3 $name, 1, &amp;quot;$name - return of select: $rc&amp;quot;;&lt;br /&gt;
         &lt;br /&gt;
         # Start der 3. Operation (update)&lt;br /&gt;
         CommandSet(undef,&amp;quot;Rep.update sqlCmd update history set UNIT=\&amp;quot;neu\&amp;quot; where DEVICE=\&amp;quot;$device\&amp;quot;;&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     if ($name eq &amp;quot;Rep.update&amp;quot;) {&lt;br /&gt;
         # Auswertung der 3. Operation (update)&lt;br /&gt;
         $rc = ReadingsVal($name, &amp;quot;SqlResultRow_1&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
         Log3 $name, 1, &amp;quot;$name - number of updates: $rc&amp;quot;;&lt;br /&gt;
         &lt;br /&gt;
         # Start der 4. Operation (delete)&lt;br /&gt;
         CommandSet(undef,&amp;quot;Rep.delete sqlCmd delete from history where device = \&amp;quot;$device\&amp;quot; and UNIT = \&amp;quot;neu\&amp;quot;;&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     if ($name eq &amp;quot;Rep.delete&amp;quot;) {&lt;br /&gt;
         # Auswertung der 4. Operation (delete)&lt;br /&gt;
         $rc = ReadingsVal($name, &amp;quot;SqlResultRow_1&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
         Log3 $name, 1, &amp;quot;$name - number of deletes: $rc&amp;quot;;&lt;br /&gt;
     } &lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die gesamte Abarbeitung wird gestartet mit dem Aufruf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chain(&amp;quot;Rep.insert&amp;quot;, &amp;quot;chainstart&amp;quot;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. in der Kommandozeile im FHEMWEB mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{chain(&amp;quot;Rep.insert&amp;quot;, &amp;quot;chainstart&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach dem initialen Aufruf des Insert wird die sub &amp;quot;chain&amp;quot; beendet und nach dem insert-Befehl durch das Device &amp;quot;Rep.insert&amp;quot; wieder aufgerufen. Dabei wird diese sub nach jedem erstellten Reading aufgerufen und dabei neben dem eigenen Devicenamen auch der Readingname sowie dessen Wert zur Auswertung übergeben. &lt;br /&gt;
Durch die if-Zweige wird bei jedem Durchlauf der richtige Entrypoint für den nachfolgenden Funktionsaufruf ermittelt und ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Nach der Abarbeitung aller vorgesehenen SQL-Statements ist die erfolgreiche Chainverkettung anhand der Ergebnisse im Logfile ersichtlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2018.10.24 23:57:37.602 1: Rep.insert - Start chain with insert&lt;br /&gt;
2018.10.24 23:57:37.815 1: Rep.insert - number of inserts: 1&lt;br /&gt;
2018.10.24 23:57:37.901 1: Rep.select - return of select: Hugo&lt;br /&gt;
2018.10.24 23:57:38.033 1: Rep.update - number of updates: 1&lt;br /&gt;
2018.10.24 23:57:38.176 1: Rep.delete - number of deletes: 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Summe aller Einschaltzeiten eines Gerätes ===&lt;br /&gt;
&lt;br /&gt;
[[Summe aller Einschaltzeiten eines Gerätes | Hier]] geht&#039;s zum Beitrag.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eine Device spezifische Aufbewahrungszeit (Retention Time) in der Datenbank realisieren ===&lt;br /&gt;
&lt;br /&gt;
In der DbLog Datenbank gespeicherte Daten müssen bzw. sollten regelmäßig gelöscht werden sofern sie nicht mehr für Auswertungen verwendet werden. Über DbRep Kommandos gibt es dafür viele Möglichkeiten.&lt;br /&gt;
&lt;br /&gt;
Vorgestellt wird hier die Möglichkeit, bereits beim Logging des Events mittels Attribut eine Device spezifische Aufbewahrungszeit festzulegen. Diese Möglichkeit ist ab der DbLog Version 5.9.4 gegeben. Die einzelnen Schritte zur Umsetzung werden nachfolgend beschrieben.&lt;br /&gt;
&lt;br /&gt;
* im global Device das userattr &#039;&#039;&#039;retentionPeriod&#039;&#039;&#039; definieren. Der Name des Attributes ist natürlich frei wählbar. Das Attribut steht in allen Devices zur Verfügung und bestimmt die Aufbewahrungszeit in Sekunden wenn im Device oder im DbLog Device gesetzt.&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
attr global userattr retentionPeriod &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Für die Speicherung des Verfallzeitpunktes in der Datenbank wird die Tabellenspalte EVENT in der Tabelle history verwendet. Normalerweise wird der komplette zu loggende Event in dieser Spalte gespeichert, was im Allgemeinen nicht benötigt wird. Dazu wird in dem relevanten DbLog Device das Attribut &#039;&#039;&#039;valueFn&#039;&#039;&#039; gesetzt um in jedem zu loggenden Datensatz die Spalte EVENT die spezifische Verfallszeit zu speichern: &lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
{ &lt;br /&gt;
  my $def = AttrVal ($NAME, &#039;retentionPeriod&#039;,  9999999999);&lt;br /&gt;
  $EVENT  = int(time) + AttrVal ($DEVICE, &#039;retentionPeriod&#039;,  $def);&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
* Im gleichen DbLog Device wird das Attribut &#039;&#039;&#039;retentionPeriod&#039;&#039;&#039; auf einen Default Wert gesetzt der gelten soll, wenn im zu loggenden Datensatz kein retentionPeriod-Attribut im Quellen-Device hinterlegt ist. Wenn kein retentionPeriod-Attribut im DbLog Device gesetzt ist, erfolgt mit dem Wert &#039;int(time) + 9999999999&#039; de facto eine zeitlich unbegrenzte Speicherung.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
attr &amp;lt;DbLog&amp;gt; retentionPeriod 7776000    (Beispiel für Haltezeit von 90 Tagen = 7776000 Sekunden)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Bild:Screenshot 2024-01-03 210126.png|right|thumb|400px|]]&lt;br /&gt;
* In jedem relevanten Device wird nun die gewünschte Aufbewahrungszeit in Sekunden gesetzt:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
attr &amp;lt;Device&amp;gt; retentionPeriod xxxxxxxxx&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Cache des DbLog Devices findet man jetzt in jedem Datensatz den Verfallszeitpunkt des Datensatzes. Er wird in der EVENT-Spalte der Tabelle gespeichert. &lt;br /&gt;
&lt;br /&gt;
Vor den nächsten Schritten ist sicherzustellen, dass EVENT bei bereits vorhandenen Datensätzen auf einen passenden Verfallszeitpunkt upgedated werden.&lt;br /&gt;
Dazu eignet sich sqlCmd im DbRep oder ein SQL Editor deiner Wahl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
UPDATE history SET TIMESTAMP=TIMESTAMP, EVENT=&amp;quot;1704353074&amp;quot;;    (setzt den Verfall auf  04.01.2024 08:24:34)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit die Datenlöschungen über eine Auswertung von EVENT später performant ausgeführt werden, sollte ein ensprechender Index angelegt werden und der Datentyp von EVENT in Integer geändert werden. Beide SQL Statements können mit einem DbRep-Device mit gesetzten &#039;&#039;&#039;adminCredentials&#039;&#039;&#039; über &amp;quot;set ... sqlCmd&amp;quot; ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE INDEX Retention_Idx ON `history` (EVENT);&lt;br /&gt;
ALTER TABLE history MODIFY COLUMN EVENT INT(15); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die veränderte Spaltenbreite von EVENT wird im DbLog-Device nachgezogen und das Attribut &#039;&#039;&#039;colEvent&#039;&#039;&#039; gesetzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;DbLog&amp;gt; colEvent 15&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur regelmäßigen Löschung der Datensätze mit einer abgelaufenen Aufbewahrungszeit dient ein at-Device in Verbindung mit einem DbRep-Device (z.B. Rep.fhemtest1) welches täglich abgelaufene Datensätze sucht und löscht. &lt;br /&gt;
Die Definition des at-Devices:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define At.RetentionDel at *23:15:00 {&lt;br /&gt;
  my $t = int(time);&lt;br /&gt;
  fhem &amp;quot;set Rep.fhemtest1 sqlCmd delete from history where EVENT&amp;lt;&#039;$t&#039;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das DbRep-Device benötigt keine besonderen Einstellungen außer einem gesetzten Attribut &#039;&#039;&#039;allowDeletion=1&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.fhemtest1 DbRep &amp;lt;DbLog-Device&amp;gt;&lt;br /&gt;
attr Rep.fhemtest1 allowDeletion 1&lt;br /&gt;
attr Rep.fhemtest1 event-on-update-reading state&lt;br /&gt;
attr Rep.fhemtest1 room DbLog&lt;br /&gt;
attr Rep.fhemtest1 showproctime 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Welche Device/Reading-Kombinationen gibt es in der Datenbank ? ===&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Im aktuellen DbRep-Release kann die nachfolgend beschriebene Auswertung alternativ durch die eingebauten Kommmandos:&lt;br /&gt;
 set &amp;lt;DbRep-Device&amp;gt; sqlSpecial allDevCount&lt;br /&gt;
 set &amp;lt;DbRep-Device&amp;gt; sqlSpecial allDevReadCount&lt;br /&gt;
ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Wenn eine Datenbank längere Zeit gelaufen ist und die zu loggenden Devices bzw. Readings immer mal wieder geändert wurden, wächst der Wunsch einen Überblick über die in der DB enthaltenen Devices/Reading-Kombinationen zu erhalten.&lt;br /&gt;
Dadurch können wiederum gezielt Auswertungen auf eine bestimmte Device/Reading-Kombination durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Um dieses Ziel zu erreichen, wird ein DbRep-Device erstellt welches für die Verwendung eines User-Command eingerichtet wird.&lt;br /&gt;
Je nach dem gewünschten Ergebnis wird eines der nachfolgenden SQL-Kommandos verwendet.&lt;br /&gt;
&lt;br /&gt;
Selektion aller Device/Reading-Kombinationen in der Datenbank:&lt;br /&gt;
 select device, reading, count(*) from history group by DEVICE, READING&lt;br /&gt;
&lt;br /&gt;
Nur die geloggten Devices in der Datenbank reporten:&lt;br /&gt;
 select device, count(*) from history group by DEVICE&lt;br /&gt;
&lt;br /&gt;
Zur Einrichtung wird zunächst das DbRep-Device definiert:&lt;br /&gt;
&lt;br /&gt;
 define Rep.LogDB.sqlResult DbRep LogDB&lt;br /&gt;
&lt;br /&gt;
Dabei ist &amp;quot;LogDB&amp;quot; &#039;&#039;&#039;nicht&#039;&#039;&#039; die Datenbank, sondern das DbLog-Device mit dem das DbRep-Device assoziiert und derüber mit der Datenbank verbunden wird !&lt;br /&gt;
 &lt;br /&gt;
Um die Ausgabe des späteren Ergebnisses zu formatieren wird das Attribut &amp;quot;sqlResultFormat&amp;quot; auf &amp;quot;table&amp;quot; gesetzt. Dadurch wird das Selektergebnis als Tabelle mit den Spalten Device, Reading und der Anzahl der enthaltenen Datensätze der jeweiligen Kombination erzeugt.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.LogDB.sqlResult sqlResultFormat table&lt;br /&gt;
&lt;br /&gt;
Natürlich kann auch ein anderes Ausgabeformat gewählt werden wenn gewünscht oder benötigt. Bei großen Ergebnismengen ist zur Erhöhung der Übersichtlichkeit das Attribut &amp;quot;sqlResultFormat = separated&amp;quot; wahrscheinlich eher geeignet.&lt;br /&gt;
&lt;br /&gt;
Das Attribut stateFormat wird entsprechend gesetzt um nach einem erfolgten Selektionslauf die erzeugte Tabelle in der Raumansicht bzw. im DeviceOverview anzuzeigen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr Rep.LogDB.sqlResult stateFormat { if (defined($defs{$name}{READINGS}{SqlResult})) {&lt;br /&gt;
                                            ReadingsVal($name,&amp;quot;SqlResult&amp;quot;,undef);&lt;br /&gt;
                                        } else {&lt;br /&gt;
                                            ReadingsVal($name,&amp;quot;state&amp;quot;,undef);&lt;br /&gt;
                                        } &lt;br /&gt;
                                      }                                        &lt;br /&gt;
&amp;lt;/pre&amp;gt;                                   &lt;br /&gt;
&lt;br /&gt;
Damit ist das DbRep-Device vorbereitet und der Auswertungslauf kann gestartet werden mit:&lt;br /&gt;
&lt;br /&gt;
 set Rep.LogDB.sqlResult sqlCmd select device, reading, count(*) from history group by DEVICE, READING;&lt;br /&gt;
&lt;br /&gt;
bzw. wenn nur die in der Datenbank enthaltenen Devices zu erhalten:&lt;br /&gt;
&lt;br /&gt;
 set Rep.LogDB.sqlResult sqlCmd select device, count(*) from history group by DEVICE;&lt;br /&gt;
&lt;br /&gt;
[[Datei:sqlResult.PNG|right|thumb|600px|set Rep.LogDB.sqlResult sqlCmd select device, count(*) from history group by DEVICE;]]&lt;br /&gt;
&lt;br /&gt;
Nebenstehender Screenshot zeigt das Ergebnis der Auswertung aller in der Datenbank enthalteten Devices und deren Anzahl von Datensätzen.&lt;br /&gt;
&lt;br /&gt;
Zum leichteren Nachnutzung hier noch die Raw-Definition des fertigen DbRep-Devices:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod Rep.LogDB.sqlResult DbRep LogDB&lt;br /&gt;
attr Rep.LogDB.sqlResult aggregation no&lt;br /&gt;
attr Rep.LogDB.sqlResult comment Device zum freien Report mit sqlCmd.\&lt;br /&gt;
- Auswahl von Statements -\&lt;br /&gt;
\&lt;br /&gt;
Device/Reading-Kombinationen in der Datenbank:\&lt;br /&gt;
select device, reading, count(*) from history group by DEVICE, READING\&lt;br /&gt;
\&lt;br /&gt;
geloggte Devices in der Datenbank:\&lt;br /&gt;
select device, count(*) from history group by DEVICE\&lt;br /&gt;
&lt;br /&gt;
attr Rep.LogDB.sqlResult devStateIcon connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.LogDB.sqlResult disable 0&lt;br /&gt;
attr Rep.LogDB.sqlResult event-on-update-reading state&lt;br /&gt;
attr Rep.LogDB.sqlResult group Datenbankauswertungen&lt;br /&gt;
attr Rep.LogDB.sqlResult room DbLog&lt;br /&gt;
attr Rep.LogDB.sqlResult showproctime 1&lt;br /&gt;
attr Rep.LogDB.sqlResult sqlResultFormat table&lt;br /&gt;
attr Rep.LogDB.sqlResult stateFormat { if (defined($defs{$name}{READINGS}{SqlResult})) {\&lt;br /&gt;
   ReadingsVal($name,&amp;quot;SqlResult&amp;quot;,undef);;\&lt;br /&gt;
  } else {\&lt;br /&gt;
   ReadingsVal($name,&amp;quot;state&amp;quot;,undef);;\&lt;br /&gt;
  } \&lt;br /&gt;
}&lt;br /&gt;
attr Rep.LogDB.sqlResult timeout 1000&lt;br /&gt;
attr Rep.LogDB.sqlResult verbose 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Die current-Tabelle mit einem Extrakt der history-Tabelle füllen (tableCurrentFillup) ===&lt;br /&gt;
&lt;br /&gt;
Diese Funktion erweitert die Möglichkeiten der Verwendung der current-Tabelle des DbLog-Devices. In den meisten Fällen wird im DbLog das Attribut &amp;quot;DbLogType = Current/History&amp;quot; gesetzt sein um bei der Erstellung von SVG&#039;s entsprechende Vorschläge in der DropDown-Liste des Editors zur Verfügung zu haben. &lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart werden in der current-Tabelle neu auftretende Events von Device/Reading-Kombinationen eingetragen, sofern sie noch nicht enthalten sind. Werden neue Geräte definiert, erscheinen deren Events in der current-Tabelle, auch wenn man später nicht benötigte Readings mit den Attributen &amp;quot;event-on-change-reading&amp;quot; oder &amp;quot;event-on-update-reading&amp;quot; ausgrenzt. &lt;br /&gt;
Die einmal gespeicherten Werte bleiben in der current Tabelle erhalten. Sicherlich wird deswegen der eine oder andere User den Inhalt der current Tabelle löschen um wieder eine leere und saubere current-Tabelle zu haben. Das führt dann natürlich dazu, dass nur die ab diesem Zeitpunkt auftretenden Events in der Vorschlagsliste aufgenommen werden und nur selten auftretende Device/Reading-Kombinationen erst nach längerer Zeit wieder in der Tabelle current gespeichert werden. &lt;br /&gt;
&lt;br /&gt;
Darüber hinaus möchte man vielleicht nur für SVG-Diagramme relevante Device/Reading-Einträge in der Vorschagsliste vorfinden oder dort nur Einträge vorhalten, die in den letzten drei Monaten aufgetreten sind. Die Beweggründe können sehr vielfältig sein.&lt;br /&gt;
Um diesem Wunsch zu entsprechen, gibt es im DbRep die Funktion &amp;quot;set &amp;lt;DbRep-Device&amp;gt; tableCurrentFillup&amp;quot;, die eng mit dem dem DbLog-Attribut &amp;quot;DbLogType = SampleFill/History&amp;quot; zusammenarbeitet. &lt;br /&gt;
&lt;br /&gt;
Nachfolgend wird an einem Beispiel erläutert, welche Konfigurationen vorgenommen werden können, um aus der history-Tabelle alle vorhandenen Device/Reading-Kombinationen zu extrahieren und sie zur Verwendung als SVG-Vorschlagsliste in die current-Tabelle einzufügen.  &lt;br /&gt;
&lt;br /&gt;
Für das Beispiel wird folgendes angenommen:&lt;br /&gt;
&lt;br /&gt;
* es gibt ein definiertes DbLog-Device &amp;quot;LogDB&amp;quot; welches in die Datenbank &amp;quot;fhem&amp;quot; loggt&lt;br /&gt;
* das DbRep-Device zur Ausführung der Funktion wird &amp;quot;Rep.FillCurr.fhem&amp;quot; genannt&lt;br /&gt;
* es sollen alle in der history-Tabelle vorkommenden Device/Reading-Kombinationen extrahiert und in die current-Tabelle eingetragen werden (Einschränkungen auf bestimmte Devices / Readings oder zeitliche Abgrenzungen werden nicht vorgenommen, aber es wird darauf hingewiesen, wie es gemacht werden kann)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== a) Anlegen des DbRep-Devices ====&lt;br /&gt;
&lt;br /&gt;
Die Definition des DbRep-Devices erfolgt unter Angabe des zu verbindenden DbLog-Devices (nicht der Datenbank selbst): &lt;br /&gt;
&lt;br /&gt;
 define Rep.FillCurr.fhem DbRep LogDB&lt;br /&gt;
&lt;br /&gt;
In dem so definierten neuen Device sind die Attribute zu setzen, die für die beabsichtigte Funktion wichtig sind.&lt;br /&gt;
Da dieses Device auch für die Löschung der in der current-Tabelle gespeicherten Datensätze verwendet wird, muss das Attribut &amp;quot;allowDeletion = 1&amp;quot; gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.FillCurr.fhem allowDeletion 1&lt;br /&gt;
&lt;br /&gt;
Ebenfalls wichtig ist die Erzeugung eines Events sobald der Löschvorgang der current-Tabelle abgeschlossen ist. Dazu wird im Beispiel &amp;quot;event-on-update-reading&amp;quot; verwendet:&lt;br /&gt;
&lt;br /&gt;
 attr Rep.FillCurr.fhem event-on-update-reading state,--DELETED_ROWS_CURRENT-- &lt;br /&gt;
&lt;br /&gt;
[[Datei:currdelete.PNG|right|thumb|300px|set Rep.FillCurr.fhem tableCurrentPurge]]&lt;br /&gt;
Für die Funktion nicht wichtig, aber durchaus interessant zu wissen wie lange die Laufzeit der Funktionen ist, wird noch das Attribut &amp;quot;showproctime = 1&amp;quot; gesetzt. Damit werden die Readings &amp;quot;background_processing_time&amp;quot; und &amp;quot;sql_processing_time&amp;quot; erzeugt welche die jeweilige Bearbeitungszeit in Sekunden darstellen.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.FillCurr.fhem showproctime 1&lt;br /&gt;
&lt;br /&gt;
Mit dem so vorbereiteten Device können schon die Funktionen &amp;quot;tableCurrentPurge&amp;quot; und &amp;quot;tableCurrentFillup&amp;quot; manuell getestet werden. Mit dem Kommando:&lt;br /&gt;
&lt;br /&gt;
 set Rep.FillCurr.fhem tableCurrentPurge&lt;br /&gt;
&lt;br /&gt;
wird der gesamte aktuelle Inhalt der current-Tabelle gelöscht. Wie im nebenstehenden Screenshot sichtbar, wurden in dem Beispiel 170 Datensätze gelöscht und dazu rund 30ms benötigt. Mit Abschluß der Operation wird der Delete-Event erzeugt:&lt;br /&gt;
&lt;br /&gt;
 2018-01-05 13:38:14.596 DbRep Rep.FillCurr.fhem --DELETED_ROWS_CURRENT--: 170&lt;br /&gt;
&lt;br /&gt;
[[Datei:currfillup.PNG|right|thumb|300px|set Rep.FillCurr.fhem tableCurrentFillup]]&lt;br /&gt;
Dieser Event wird später in einem Notify für die automatisierte Current-Auffüllung verwendet.&lt;br /&gt;
Eine Zählug der current-EInträge mit &amp;quot;set Rep.FillCurr.fhem countEntries current&amp;quot; ergibt &amp;quot;-&amp;quot;, d.h. der Löschbefehl war erfolgreich. Im jetzigen Zustand würde bei der Erstellung eines SVG keine DropDown-Liste angezeigt werden und statt dessen die Felder im SVG-Editor frei bearbeitbar sein, weil die current-Tabelle keine Werte enthält. &lt;br /&gt;
Gleichermaßen kann bereits die Auffüllung der current-Tabelle getestet werden mit dem Befehl:&lt;br /&gt;
&lt;br /&gt;
 set Rep.FillCurr.fhem tableCurrentFillup&lt;br /&gt;
&lt;br /&gt;
Je nach Größe der Datenbank kann diese Operation einige Zeit in Anspruch nehmen. Da dieser Befehl wie bei DbRep üblich non-blocking abgearbeitet wird, hat die Laufzeit keinen Einfluss auf die sonstige FHEM-Verfügbarkeit. &lt;br /&gt;
&lt;br /&gt;
[[Datei:svgfillup.PNG|left|thumb|300px|set Rep.FillCurr.fhem tableCurrentFillup]]&lt;br /&gt;
Wie bereits erwähnt, wird in dem Beispiel die gesamte history-Tabelle ausgewertet. Sollen zum Beispiel nur die letzten 90 Tage zur Auswertung herangezogen werden, kann das &#039;&#039;&#039;Attribut &amp;quot;timeDiffToNow = d:90&amp;quot;&#039;&#039;&#039; gesetzt werden. &lt;br /&gt;
&lt;br /&gt;
Weitere Eingrenzungen bzgl. der auszuwertenden Devices bzw. Readings können über die &#039;&#039;&#039;Attribute &amp;quot;device&amp;quot; und &amp;quot;reading&amp;quot;&#039;&#039;&#039; vorgenommen werden. Die Syntax für die genannten Attribute und deren Auswirkung entnehme bitte der {{Link2CmdRef|Lang=de|Anker=DbRepattr}}.&lt;br /&gt;
&lt;br /&gt;
In unserem Beispiel wurde die current-Tabelle mit 170 Datensätzen aufgefüllt. Dazu wurden 291 Sekunden benötigt (die Tabelle history enthält insgesamt rund 11 Mio. Einträge)&lt;br /&gt;
&lt;br /&gt;
Wird jetzt ein neues SVG erstellt, erscheinen alle in selektierten Device/Reading-Kombinationen alphabetisch sortiert als DopDown-Liste im SVG-Editor.&lt;br /&gt;
&lt;br /&gt;
==== b) Konfiguration des DbLog-Devices ==== &lt;br /&gt;
&lt;br /&gt;
In dem bestehen DbLog-Device &amp;quot;LogDB&amp;quot; ist zur Vorbereitung lediglich das Attribut &amp;quot;DbLogType = SampleFill/History&amp;quot; zu setzen:&lt;br /&gt;
&lt;br /&gt;
 attr LogDB DbLogType SampleFill/History&lt;br /&gt;
&lt;br /&gt;
Entgegen der Arebeitsweise mit &amp;quot;Current/History&amp;quot; wird die current-Tabelle nicht mehr durch die Logging-Engine aktiv mit jedem relevanten Event gefüllt, sondern kann durch externe Tools, in dem Fall dem DbRep-Device, beschrieben werden.&lt;br /&gt;
Die current-Tabelle wird aber, ob leer oder gefüllt, bei der Erstellung eines SVG-Devices ausgewertet und in Abhängigeit des Ergebnisses eine DropDown-Liste zur Verfügung gestellt, oder frei editierbare Felder angeboten falls die current-Tabelle leer ist.&lt;br /&gt;
&lt;br /&gt;
==== c) Definition der at/notify-Devices zum automatisierten Ablauf ==== &lt;br /&gt;
&lt;br /&gt;
Die Löschung des Inhaltes der current-Tabelle und die erneute Ausffüllung soll natürlich autmatisiert regelmäßig erfolgen. Zu diesem Zweck wird ein at-Device (At.LogDB.currentPurge) und ein notify-Device angelegt. Das AT-Device soll alle 8 Stunden und 15 Minuten das Löschen des current-Inhaltes steuern und nach Abschluß der Löschung soll getriggert durch das &amp;quot;--DELETED_ROWS_CURRENT--&amp;quot;-Event die Neubefüllung der current-Tabelle veranlasst werden.&lt;br /&gt;
&lt;br /&gt;
Zur Definition dieser Devices gibt es nicht viel zu erläutern. Die entsprechende Syntax kann bei {{Link2CmdRef|Lang=de|Anker=at|Label=at}} bzw. {{Link2CmdRef|Lang=de|Anker=notify|Label=notify}} in der {{Link2CmdRef|Lang=de}} nachgelesen werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;at-Device:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 define At.LogDB.currentPurge at +*08:15:00 set Rep.FillCurr.fhem tableCurrentPurge&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;notify-Device:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 define N.LogDB.Fillup notify Rep.FillCurr.fhem:.*DELETED_ROWS_CURRENT.*  sleep 5;set Rep.FillCurr.fhem tableCurrentFillup&lt;br /&gt;
&lt;br /&gt;
Das nichtblockierende FHEM-sleep (sleep gefolgt von einem weiteren Befehl) wird lediglich dazu verwendet, um zwischen der Delete- und Auffüll-Operation eine kleine Pause einzufügen.&lt;br /&gt;
 &lt;br /&gt;
==== d) Arbeitsweise und Raw-Definitionen ====&lt;br /&gt;
&lt;br /&gt;
Durch das &amp;quot;At.LogDB.currentPurge&amp;quot;-Device wird alle 8 Stunden und 15 Minuten ein Löschlauf der current-Tabelle gestartet. Dadurch wird dem darauf folgenden Auffüllprozess immer eine saubere leere current-Tabelle zur Verfügung gestellt. Nach dem Löschlauf wird durch das &amp;quot;N.LogDB.Fillup&amp;quot;-Device, getriggert durch den Event &amp;quot;Rep.FillCurr.fhem:.*DELETED_ROWS_CURRENT.*&amp;quot;, die erneute Auffüllung der current-Tabelle gestartet.&lt;br /&gt;
Die Daten werden aus der histrory-Tabelle gelesen, wobei der Zeitraum der Selektion und die zu betrachtenden Devices / Readings durch die Zeit-Attribute bzw. device / reading-Attribute des DbRep-Devices &amp;quot;Rep.FillCurr.fhem&amp;quot; individuell beeinflußt werden können.&lt;br /&gt;
&lt;br /&gt;
Hilfreich ist es auch, wenn das DbLog-Device &amp;quot;LogDB&amp;quot; im asynchronen Modus betrieben wird.&lt;br /&gt;
&lt;br /&gt;
Abschließend sind hier noch die Raw-Definitionen der beteiligten Devices (außer LogDB) angehängt, um das Beispiel leicht nachvollziehbar zu gestalten.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbRep-Device &amp;quot;Rep.FillCurr.fhem&amp;quot;:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod Rep.FillCurr.fhem DbRep LogDB&lt;br /&gt;
attr Rep.FillCurr.fhem allowDeletion 1&lt;br /&gt;
attr Rep.FillCurr.fhem comment Current Fillup für DB fhem&lt;br /&gt;
attr Rep.FillCurr.fhem devStateIcon connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.FillCurr.fhem event-on-update-reading state,--DELETED_ROWS_CURRENT--&lt;br /&gt;
attr Rep.FillCurr.fhem icon icoTool&lt;br /&gt;
attr Rep.FillCurr.fhem room DbLog&lt;br /&gt;
attr Rep.FillCurr.fhem showproctime 1&lt;br /&gt;
attr Rep.FillCurr.fhem verbose 3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;at-Device &amp;quot;At.LogDB.currentPurge&amp;quot;:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod At.LogDB.currentPurge at +*08:15:00 set Rep.FillCurr.fhem tableCurrentPurge&lt;br /&gt;
attr At.LogDB.currentPurge comment Zeitgesteuertes Löschen der Current Tabelle. Nach dem Löschen wird \&lt;br /&gt;
Eventgesteuert (Notify N.LogDB.Fillup) ein tableCurrentFillup gestartet.&lt;br /&gt;
attr At.LogDB.currentPurge room DbLog&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;notify-Device &amp;quot;N.LogDB.Fillup&amp;quot;:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod N.LogDB.Fillup notify Rep.FillCurr.fhem:.*DELETED_ROWS_CURRENT.*  sleep 5;;set Rep.FillCurr.fhem tableCurrentFillup&lt;br /&gt;
attr N.LogDB.Fillup disable 1&lt;br /&gt;
attr N.LogDB.Fillup room DbLog&lt;br /&gt;
attr N.LogDB.Fillup verbose 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
=== Backup/Restore einer SQLite Datenbank im laufenden Betrieb ===&lt;br /&gt;
&lt;br /&gt;
In diesem Beitrag wird erläutert, wie man mit DbRep ein Backup der SQLite-Datenbank erstellen kann, welche Möglichkeiten es dabei gibt und wie man eine Datenbank aus einem Backup wieder herstellen kann. &lt;br /&gt;
Die Backup-Funktion nutzt die SQLite Online Backup API und ermöglicht es, konsistente Backups der SQLite-DB in laufenden Betrieb zu erstellen ohne die Datenbank schließen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Neben dem eigentlichen Backup kann optional einstellt werden, dass:&lt;br /&gt;
&lt;br /&gt;
* vor dem Backup eine Datenbankoptimierung (Vacuum) ausgeführt werden soll. Dadurch wird Plattenplatz freigegeben sofern möglich.&lt;br /&gt;
* vor und nach dem Backup kann ein FHEM-Kommando oder eine Perl-Routine ausgeführt werden (eine Perl-Routine ist in {} einzuschließen)&lt;br /&gt;
* das erstellte Dumpfile kann über FTP(S) an einen FTP-Server übertragen werden&lt;br /&gt;
* über die interne Versionsverwaltung kann festgelegt werden, wieviele erstellte Backups auf dem Datenträger erhalten bleiben sollen (default: 3)&lt;br /&gt;
&lt;br /&gt;
Für die Erläuterung des Beispiels soll ein DbRep-Device erstellt werden, welches:&lt;br /&gt;
&lt;br /&gt;
* die SQLite Datenbank im Betrieb (Online) sichert&lt;br /&gt;
* vor dem Dump einen Vacuum-Lauf durchführt&lt;br /&gt;
* das erstellte Dumpfile auf einen FTP-Server überträgt&lt;br /&gt;
* 2 Versionen (Dumpfiles) nach einem erfolgreichen Backuplauf im Dumpverzeichnis verbleiben sollen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 1. Anlegen des DbRep-Devices ====&lt;br /&gt;
&lt;br /&gt;
Das anzulegende Device wird sowohl für das (regelmäßige) Backup als auch für einen eventuellen Restore verwendet.&lt;br /&gt;
Die Definition des DbRep-Devices erfolgt unter Angabe des zu verbindenden DbLog-Devices (nicht der Datenbank selbst): &lt;br /&gt;
&lt;br /&gt;
 define Rep.SQLite DbRep LogSQLITE&lt;br /&gt;
&lt;br /&gt;
Ist das Device definiert, verbindet es sich zu der Datenbank und wechselt in den state &amp;quot;connected&amp;quot;, sofern die Verbindung erfolgreich verlief.&lt;br /&gt;
Prinzipiell funktioniert der hier beschriebene Backup-Prozess mit einem im synchronous Mode betriebenen DbLog-Device, aber es ist dringend angeraten das DbLog-Device (LogSQLITE) in den asynchronen Modus umzuschalten. Dadurch werden Blockierungen von FHEM und Verluste von Daten verhindert.&lt;br /&gt;
&lt;br /&gt;
==== 2. Einstellungen des DbRep-Devices (Attribute) ====&lt;br /&gt;
&lt;br /&gt;
Für die gewünschte Funktion müssen in diesem neu definierten Device einige relevante Attribute gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;a) dumpDirLocal&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Das zu erstellende Backupfile wird per default im log-Verzeichnis ./log (meist /opt/fhem/log) des FHEM-Rechners gespeichert. Um es an einer anderen Stelle zu speichern kann das Attribut &amp;quot;dumpDirLocal&amp;quot; gesetzt werdden. Dieses Verzeichnis kann sich auf dem lokalen Datenträger befinden, oder ein per NFS gemountetes Verzeichnis sein. In jedem Fall muss der User unter dem FHEM läuft Schreibrechte auf dieses Verzeichnis besitzen.&lt;br /&gt;
&lt;br /&gt;
Für das Beispiel sollen die Dump-Files auf einen NFS-Mount einer Synology Diskstation gespeichert werden. Dazu wurde ein Mount in /etc/fstab erstellt um den Pfad &amp;quot;/sds1/backup&amp;quot; lokal verfügbar zu machen:&lt;br /&gt;
&lt;br /&gt;
 sds1.&amp;lt;fqdn&amp;gt;:/volume1/ApplicationBackup /sds1/backup nfs auto,defaults,tcp,intr 0 0&lt;br /&gt;
&lt;br /&gt;
In diesem Verzeichnis exsitiert das Directory &amp;quot;dumps_FHEM&amp;quot;, in dem die Backup-Files gespeichert werden sollen. Das Attribut wird entsprechend gesetzt auf:&lt;br /&gt;
&lt;br /&gt;
 attr Rep.SQLite dumpDirLocal /sds1/backup/dumps_FHEM&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
:&#039;&#039;&#039;b) dumpFilesKeep&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Dieser Parameter stellt die Anzahl der aufzubewahrenden Backup-Files ein. Es werden immer die &amp;quot;x&amp;quot; neuesten bzw. letzten Files im Verzeichnis gehalten, wobei nur die zu der jeweiligen Datenbank gehörenden Files betrachtet werden. &lt;br /&gt;
Die Zugehörigkeit des Dump-Files zur Quelldatenbank wird anhand des Filenamens ermittelt, der sich zusammensetzt aus &amp;lt;DB-Name ohne Endung&amp;gt;_&amp;lt;Erstellungsdatum und Uhrzeit&amp;gt;.sqlitebkp&lt;br /&gt;
&lt;br /&gt;
 Beispiel: fhem_2018_01_12_17_15.sqlitebkp&lt;br /&gt;
&lt;br /&gt;
Ist dieses Attribut nicht gesetzt, werden die 3 neuesten Backup-Files im Verzeichnis behalten.&lt;br /&gt;
Um nur 2 Files zu behalten wird das Attribut gesetzt auf:&lt;br /&gt;
&lt;br /&gt;
 attr Rep.SQLite dumpFilesKeep 2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;c) Aktionen vor und nach dem Backup ausführen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Vor und nach der Dump-Ausführung kann ein FHEM-Kommando bzw. Perl-Befehle ausgeführt werden. Perl-Befehle müssen in {} eingeschlossen werden.&lt;br /&gt;
Für das Beispiel soll vor dem Backup die Verbindung des DbLog-Devices LogSQLITE zur Datenbank getrennt werden. Dadurch werden keine neuen Daten in die Datenbank geschrieben. Wenn das DbLog-Device LogSQLITE im asynchronen Modus wie empfohlen betrieben wird, tritt auch kein Datenverlust ein, da die zu loggenden Daten im Cache verbleiben bis die Verbindung zur DB wieder hergestellt wird.&lt;br /&gt;
&lt;br /&gt;
Es sei noch einmal darauf hingewiesen, dass das Backup-Verfahren ebenso im synchronen Modus und ohne das DbLog-Device von der Datenbank zu trennen, funktioniert und nur im Beispiel zur Demonstation der Möglichkeiten dient. &lt;br /&gt;
&lt;br /&gt;
Um die Verbindung zu trennen wird ein &amp;quot;set LogSQLITE reopen &amp;lt;Zeit in Sekunden&amp;gt;&amp;quot; verwendet. Die Zeitspanne wird sehr großzügig gewählt und soll sicherstellen, dass innerhalb dieser Zeit das Backup abgeschlossen ist. Nach dem Backup wird sofort ein &amp;quot;set LogSQLITE reopen&amp;quot; ausgeführt, was die Verbindung des DbLog-Devices LogSQLITE zur Datenbank unmittelbar wieder herstellt..&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 attr Rep.SQLite executeBeforeProc set LogSQLITE reopen 3600&lt;br /&gt;
 attr Rep.SQLite executeAfterProc set LogSQLITE reopen&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;d) vor dem Backup Datenbank verkleinern (vacuum)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese optionale Funktion wird durch das Attribut &amp;quot;optimizeTablesBeforeDump&amp;quot; eingeschaltet. Dadurch die Vacuum-Funktion wird Platz innerhalb der Datenbank freigegeben der durch Löschvorgänge entstanden ist und dadurch die Größe des Datenbankfiles verringert.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.SQLite optimizeTablesBeforeDump 1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;e) das Dump-File nach dem Backup zum FTP-Server übertragen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
DbRep bietet die Möglichkeit, das erstellte Dump-File per FTP oder verschlüsselt per FTP(S) zum Server zu übertragen. Dazu gibt es einen Satz von Attributen um dem Device alle notwendigen Angaben für den FTP-Transfer zur Verfügung zu stellen. Es gibt dafür folgende Attribute:&lt;br /&gt;
&lt;br /&gt;
* ftpUse 	: FTP Transfer nach dem Dump wird eingeschaltet (bzw. ftpUseSSL mit SSL Verschlüsselung)&lt;br /&gt;
* ftpUser 	: User zur Anmeldung am FTP-Server, default: anonymous&lt;br /&gt;
* ftpPwd 	: Passwort des FTP-Users, default nicht gesetzt&lt;br /&gt;
* ftpDir 	: Verzeichnis auf dem FTP-Server in welches das File übertragen werden soll (default: FTP-root)&lt;br /&gt;
* ftpPort 	: FTP-Port, default: 21&lt;br /&gt;
* ftpServer 	: Name oder IP-Adresse des FTP-Servers&lt;br /&gt;
* ftpTimeout 	: Timeout für die FTP-Verbindung in Sekunden (default: 30)&lt;br /&gt;
* ftpPassive 	: setzen wenn passives FTP verwendet werden soll&lt;br /&gt;
* ftpDebug 	: Debugging des FTP Verkehrs zur Fehlersuche&lt;br /&gt;
&lt;br /&gt;
Gemäß dieser Beschreibung und dem Ziel dieses Beispiels werden die für FTP-Transfer relevanten Attribute wie folgt gesetzt:&lt;br /&gt;
&lt;br /&gt;
 attr Rep.SQLite ftpDir /ftp                # Subdirectory ftp unter FTP-root als Zielverzeichnis&lt;br /&gt;
 attr Rep.SQLite ftpPwd ftpftp1&lt;br /&gt;
 attr Rep.SQLite ftpServer sds1.&amp;lt;fqdn&amp;gt;&lt;br /&gt;
 attr Rep.SQLite ftpUse 1&lt;br /&gt;
 attr Rep.SQLite ftpUser ftpuser&lt;br /&gt;
&lt;br /&gt;
Der FTP-Server muss natürlich vorab eingerichtet und funktionsfähig sein. Sollten beim FTP-Transfer Fehler auftreten, kann das Attribut ftpDebug = 1 gesetzt werden um entsprechende Ausgaben zur Analyse des Problems im Log zu erhalten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:repsqlite_defined.PNG|right|thumb|300px|Fertig definiertes Device Rep.SQLite]]&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;f) Hilfsattribute&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Für die Funktion nicht relevant aber informativ ist das Attribut &amp;quot;showproctime = 1&amp;quot;. Ist das Attribut gesetzt, wird das Reading &amp;quot;background_processing_time&amp;quot; angelegt. Es enthält nach der Ausführung die verbrauchte Prozesszeit.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das fertig konfigurierte Device hier als RAW-Definition zur einfachen Nachnutzung. Die Angaben sind natürlich anzupassen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.SQLite DbRep LogSQLITE&lt;br /&gt;
attr Rep.SQLite devStateIcon connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.SQLite dumpDirLocal /sds1/backup/dumps_FHEM&lt;br /&gt;
attr Rep.SQLite dumpFilesKeep 2&lt;br /&gt;
attr Rep.SQLite event-on-update-reading state&lt;br /&gt;
attr Rep.SQLite executeAfterProc set LogSQLITE reopen&lt;br /&gt;
attr Rep.SQLite executeBeforeProc set LogSQLITE reopen 3600&lt;br /&gt;
attr Rep.SQLite ftpDir /ftp&lt;br /&gt;
attr Rep.SQLite ftpPwd ftpftp1&lt;br /&gt;
attr Rep.SQLite ftpServer sds1.&amp;lt;fqdn&amp;gt;&lt;br /&gt;
attr Rep.SQLite ftpUse 1&lt;br /&gt;
attr Rep.SQLite ftpUser ftpuser&lt;br /&gt;
attr Rep.SQLite optimizeTablesBeforeDump 1&lt;br /&gt;
attr Rep.SQLite room DbLog&lt;br /&gt;
attr Rep.SQLite showproctime 1&lt;br /&gt;
attr Rep.SQLite verbose 3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 3. Backup durchführen ====&lt;br /&gt;
 &lt;br /&gt;
Mit dem wie beschrieben eingerichteten DbRep-Device kann das Buckup gestartet werden mit:&lt;br /&gt;
&lt;br /&gt;
 set Rep.SQLite dumpSQLite&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:logsqlite_closeduntil.PNG|right|thumb|500px|LogSQLITE ist geschlossen bis ...]]&lt;br /&gt;
&lt;br /&gt;
Zu Beginn des Vorgangs wird sofort die Verbindung des DbLog-Devices zur Datenbank getrennt.&lt;br /&gt;
Ein laufendes Backup kann mit &amp;quot;set Rep.SQLite cancelDump&amp;quot; abgebrochen werden.&lt;br /&gt;
Das laufende Backup wird im state angezeigt mit &amp;quot;SQLite Dump is running - be patient and see Logfile !&amp;quot;. Dieser Satz ist ernst gemeint, wobei der Dump über die Online-API sehr schnell arbeitet. Im Logfile mit (mindestens) verbose 3 werden die relevanten Informationen zur Verfügung gestellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2018.01.13 08:35:10.745 3: DbRep Rep.SQLite - ################################################################           &lt;br /&gt;
2018.01.13 08:35:10.746 3: DbRep Rep.SQLite - ###                    New SQLite dump                       ###&lt;br /&gt;
2018.01.13 08:35:10.747 3: DbRep Rep.SQLite - ################################################################&lt;br /&gt;
2018.01.13 08:35:10.748 3: DbRep Rep.SQLite - execute command before dump: &#039;set LogSQLITE reopen 3600&#039; &lt;br /&gt;
2018.01.13 08:35:10.883 2: DbLog LogSQLITE: Connection closed until 09:35:10 (3600 seconds).&lt;br /&gt;
2018.01.13 08:35:10.917 3: DbRep Rep.SQLite - Size of database /opt/fhem/fhem.db before optimize (MB): 2554&lt;br /&gt;
2018.01.13 08:35:10.918 3: DbRep Rep.SQLite - VACUUM database /opt/fhem/fhem.db....&lt;br /&gt;
2018.01.13 08:44:04.216 3: DbRep Rep.SQLite - Size of database /opt/fhem/fhem.db after optimize (MB): 2554&lt;br /&gt;
2018.01.13 08:44:04.280 3: DbRep Rep.SQLite - Starting dump of database &#039;fhem.db&#039;&lt;br /&gt;
2018.01.13 08:45:03.810 3: DbRep Rep.SQLite - Size of backupfile: 2553.64 MB&lt;br /&gt;
2018.01.13 08:45:04.579 3: DbRep Rep.SQLite - FTP: transferring /sds1/backup/dumps_FHEM/fhem_2018_01_13_08_44.sqlitebkp&lt;br /&gt;
2018.01.13 08:45:48.838 3: DbRep Rep.SQLite - FTP: fhem_2018_01_13_08_44.sqlitebkp transferred successfully to sds1.myds.me into dir /ftp&lt;br /&gt;
2018.01.13 08:45:48.844 3: DbRep Rep.SQLite - Deleting old dumpfile &#039;fhem_2018_01_13_08_13.sqlitebkp&#039; &lt;br /&gt;
2018.01.13 08:45:49.273 3: DbRep Rep.SQLite - Finished backup of database fhem - total time used: 638 seconds&lt;br /&gt;
2018.01.13 08:45:49.358 2: DbRep Rep.SQLite - command after dump message: &amp;quot;Reopen executed.&amp;quot; &lt;br /&gt;
2018.01.13 08:45:49.370 3: DbRep Rep.SQLite - Database dump finished successfully. &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:repsqlite_dumprunning.PNG|right|thumb|400px|das Backup läuft]]&lt;br /&gt;
&lt;br /&gt;
Setzt man das Attribut &amp;quot;ftpDebug=1&amp;quot;, erhält man im Log zusätzliche Informationen zum FTP-Transfer:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2018.01.13 08:50:22.385 3: DbRep Rep.SQLite - Size of backupfile: 2553.82 MB&lt;br /&gt;
Net::FTP&amp;gt;&amp;gt;&amp;gt; Net::FTP(2.79)&lt;br /&gt;
Net::FTP&amp;gt;&amp;gt;&amp;gt;   Exporter(5.72)&lt;br /&gt;
Net::FTP&amp;gt;&amp;gt;&amp;gt;   Net::Cmd(2.30)&lt;br /&gt;
Net::FTP&amp;gt;&amp;gt;&amp;gt;   IO::Socket::INET(1.35)&lt;br /&gt;
Net::FTP&amp;gt;&amp;gt;&amp;gt;     IO::Socket(1.38)&lt;br /&gt;
Net::FTP&amp;gt;&amp;gt;&amp;gt;       IO::Handle(1.35)&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 220 SDS1 FTP server ready.&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;gt;&amp;gt;&amp;gt; USER ftpuser&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 331 Password required for ftpuser.&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;gt;&amp;gt;&amp;gt; PASS ....&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 230 User ftpuser logged in, access restrictions apply.&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;gt;&amp;gt;&amp;gt; TYPE I&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 200 Type set to I.&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;gt;&amp;gt;&amp;gt; CWD /ftp&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 250 CWD command successful.&lt;br /&gt;
2018.01.13 08:50:22.880 3: DbRep Rep.SQLite - FTP: transferring /sds1/backup/dumps_FHEM/fhem_2018_01_13_08_49.sqlitebkp&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;gt;&amp;gt;&amp;gt; PORT 192,168,2,45,145,42&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 200 PORT command successful.&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;gt;&amp;gt;&amp;gt; ALLO 2677867520&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 202 ALLO command ignored.&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;gt;&amp;gt;&amp;gt; STOR fhem_2018_01_13_08_49.sqlitebkp&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 150 Opening BINARY mode data connection for &#039;fhem_2018_01_13_08_49.sqlitebkp&#039;.&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 226 Transfer complete.&lt;br /&gt;
2018.01.13 08:51:06.859 3: DbRep Rep.SQLite - FTP: fhem_2018_01_13_08_49.sqlitebkp transferred successfully to sds1.myds.me into dir /ftp&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:repsqlite_dumpfinished.PNG|right|thumb|500px|Backup ist erfolgreich abgeschlossen]]&lt;br /&gt;
&lt;br /&gt;
In vorliegenden Beispiel wird nach einem erfolgreichen Backup im state &amp;quot;Warning - dump finished, but command after dump message appeared&amp;quot; angezeigt. Diese Warnung rührt daher, dass das Reopen-Kommando eine Rückkehrinfo mitteilt, die als Problem gewertet und im Reading &amp;quot;afterdump_message&amp;quot; ausgegeben wird. In diesem Fall ist es nur eine Information.&lt;br /&gt;
&lt;br /&gt;
In den erstellten Readings wird zusammengetragen welches File mit welcher Größe ertsellt wurde, welche alten Files gelöscht wurden, Informationen zum FTP-Transfer und welche Zeit der Gesamtprozess benötgt hat.&lt;br /&gt;
 &lt;br /&gt;
Das so vorbereitete Backup kann nun z.B. täglich oder auch mit einer wesentlich kürzeren Wiederholungsperiode (alle x Stunden) über ein at-Device eingeplant werden:&lt;br /&gt;
&lt;br /&gt;
 define At.SQLite.Dump at *23:52:00 set Rep.SQLite dumpSQLite&lt;br /&gt;
&lt;br /&gt;
==== 4. Restore ====&lt;br /&gt;
&lt;br /&gt;
Sollte es einmal zur Korruption des Datenbankfiles kommen (database disk image is malformed) und Reparaturversuche erfolglos bleiben, kann ein Restore die einzigste oder vielleicht auch einfachste Möglichkeit sein die Datenbank wieder herzustellen.&lt;br /&gt;
&lt;br /&gt;
Das kann mit dem angelegten DbRep-Device im laufenden Betrieb geschehen. Auch hier ist wieder wichtig, dass das DbLog-Device LogSQLITE im asynchronen Modus betrieben wird und die Reopen-Zeit (siehe Einstellung Attribut &amp;quot;executeBeforeProc&amp;quot;) sehr großzügig eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Zum Restore gibt es den Befehl &amp;quot;set ... restoreSQLite &amp;lt;File&amp;gt;&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
[[Datei:sqliterestore_auswahl.PNG|right|thumb|500px|Auswahlmenü Files für Restore ]]&lt;br /&gt;
&lt;br /&gt;
Im FHEMWEB öffnet sich dazu eine DropDown-Liste die alle vorhandenen und zur Quelldatenbank passenden Dumpfiles auflistet. Das herzustellende File&lt;br /&gt;
kann so bequem ausgewählt werden. Die Dumpfiles müssen sich in dem Verzeichnis befinden, welches durch das Attribut &amp;quot;dumpDirLocal&amp;quot; festgelegt wurde (default (./log).  [[Datei:sqliterestore_running.PNG|left|thumb|500px|Restore läuft]]&lt;br /&gt;
&lt;br /&gt;
Der restore wird demzufolge gestartet mit:&lt;br /&gt;
&lt;br /&gt;
 set Rep.SQLite restoreSQLite &amp;lt;File&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wieder wird zu Beginn des Vorgangs das verbundene DbLog-Device von der Datenbank abgekoppelt und nach dem Restore wieder automatisch verbunden. Eine Datenbankoptimierung bzw. FTP-Transfer wird bei diesem Vorgang natürlich nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Log zeigt den Prozessverlauf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2018.01.13 09:21:55.435 3: DbRep Rep.SQLite - ################################################################&lt;br /&gt;
2018.01.13 09:21:55.437 3: DbRep Rep.SQLite - ###             New database Restore/Recovery                ###&lt;br /&gt;
2018.01.13 09:21:55.437 3: DbRep Rep.SQLite - ################################################################&lt;br /&gt;
2018.01.13 09:21:55.438 3: DbRep Rep.SQLite - execute command before restore: &#039;set LogSQLITE reopen 3600&#039; &lt;br /&gt;
2018.01.13 09:21:55.448 2: DbLog LogSQLITE: Connection closed until 10:21:55 (3600 seconds).&lt;br /&gt;
2018.01.13 09:21:55.477 3: DbRep Rep.SQLite - Starting restore of database &#039;fhem.db&#039;&lt;br /&gt;
2018.01.13 09:30:12.533 3: DbRep Rep.SQLite - Restore of /sds1/backup/dumps_FHEM/fhem_2018_01_13_08_49.sqlitebkp into &#039;fhem.db&#039; finished - total time used: 497 seconds.&lt;br /&gt;
2018.01.13 09:30:12.685 2: DbRep Rep.SQLite - command after restore message: &amp;quot;Reopen executed.&amp;quot; &lt;br /&gt;
2018.01.13 09:30:12.700 3: DbRep Rep.SQLite - Database restore finished successfully.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Restore wurde erfolgreich abgeschlossen und die Verbindung des DbLog-Device LogSQLITE zur Datenbank wiederhergestellt. Das Logging wird nahtlos fortgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:sqliterestore_finished.PNG|right|thumb|300px|Restore erfolgreich abgeschlossen]]&lt;br /&gt;
&lt;br /&gt;
Es ist natürlich zu beachten, dass die Daten zwischen dem Erstellungszeitpunkt des eingespielten Backups und der aktuellen Zeit verloren sind !&lt;br /&gt;
Die bestehende (korrupte) Datenbank wird überschrieben. Deswegen ist es ratsam immer ein aktuelles (zeitnahes) Backup zu haben um den Datenverlust möglichst gering zu halten.&lt;br /&gt;
&lt;br /&gt;
==== 5. Links ====&lt;br /&gt;
* Diskussion im Forum: &amp;quot;{{Link2Forum|Topic=82674|LinkText=Erstellung konsistentes Online-Backup im laufenden Betrieb}}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Backup/Restore einer MySQL/MariaDB Datenbank im laufenden Betrieb ===&lt;br /&gt;
&lt;br /&gt;
Das Backup einer MySQL/MariaDB Datenbank (im Folgenden stellvertretend als MySQL bezeichnet) kann als Varianten&lt;br /&gt;
&lt;br /&gt;
* clientSide&lt;br /&gt;
* serverSide&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;clientSide&#039;&#039;&#039; Backup werden die Daten über SQL-Statements aus der Datenbank gelesen, auf dem Client (dem FHEM-Server) verarbeitet und das Dumpfile geschrieben. Es werden history- und current-Tabelle gesichert, sowie eventuell weitere angelegte Tabellen und Views. Allerdings benötigt dieser Modus umfangreichere RAM und CPU-Ressorcen auf dem Client.&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;serverSide&#039;&#039;&#039; Option wird nachfolgend beispielhaft genauer erläutert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== serverSide Backup Option ====&lt;br /&gt;
&lt;br /&gt;
Neben dem eigentlichen Backup kann optional einstellt werden, dass:&lt;br /&gt;
&lt;br /&gt;
* vor dem Backup eine Tabellenoptimierung (optimizeTables) ausgeführt werden soll. Dadurch wird Plattenplatz freigegeben sofern möglich.&lt;br /&gt;
* vor und nach dem Backup kann ein FHEM-Kommando oder eine Perl-Routine ausgeführt werden (eine Perl-Routine ist in &#039;&#039;&#039;{ }&#039;&#039;&#039; einzuschließen)&lt;br /&gt;
* das erstellte Dumpfile kann komprimiert werden&lt;br /&gt;
* das erstellte Dumpfile kann über FTP(S) an einen FTP-Server übertragen werden&lt;br /&gt;
* über die interne Versionsverwaltung kann die Anzahl der im Backupverzeichnis zu verbleibenden Backup-Files festgelegt werden (default: 3)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Für die Erläuterung des Beispiels soll ein DbRep-Device erstellt werden, welches:&lt;br /&gt;
&lt;br /&gt;
* die MySQL Datenbank im Betrieb (Online) sichert&lt;br /&gt;
* vor dem Dump die history-Tabelle optimiert&lt;br /&gt;
* das erstellte Dumpfile auf einen FTP-Server überträgt&lt;br /&gt;
* drei Versionen (Dumpfiles) nach der Übertragung auf dem FTP-Server belässt, ältere Files werden vom FTP-Server gelöscht&lt;br /&gt;
* eine Version (Dumpfile) nach einem erfolgreichen Backuplauf im Dumpverzeichnis verbleiben sollen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 1. Anlegen des DbRep-Devices =====&lt;br /&gt;
&lt;br /&gt;
Das anzulegende Device wird sowohl für das (regelmäßige) Backup als auch für ein eventuelles Restore verwendet.&lt;br /&gt;
Die Definition des DbRep-Devices erfolgt unter Angabe des zu verbindenden DbLog-Devices (nicht der Datenbank selbst): &lt;br /&gt;
&lt;br /&gt;
 define Rep.fhemtest.Dump.ServerSide DbRep LogDB&lt;br /&gt;
&lt;br /&gt;
Ist das Device definiert, verbindet es sich zu der Datenbank und wechselt in den state &amp;quot;connected&amp;quot;, sofern die Verbindung erfolgreich verlief.&lt;br /&gt;
Prinzipiell funktioniert der hier beschriebene Backup-Prozess mit einem im synchronous Mode betriebenen DbLog-Device, aber es ist dringend angeraten das DbLog-Device (LogDB) in den asynchronen Modus umzuschalten. Dadurch werden FHEM-Blockierungen vermieden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 2. Einstellungen des DbRep-Devices (Attribute) ===== &lt;br /&gt;
&lt;br /&gt;
Für die gewünschte Funktion müssen in diesem neu definierten Device einige relevante Attribute gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;a) dumpDirRemote&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Dump wird durch den MySQL-Server erstellt und per default im Home-Verzeichnis des MySQL-Servers gespeichert.&lt;br /&gt;
Es wird die gesamte history-Tabelle (nicht current-Tabelle) im &#039;&#039;&#039;CSV-Format&#039;&#039;&#039; ohne Einschränkungen exportiert. &lt;br /&gt;
&lt;br /&gt;
Um es an einer anderen Stelle zu speichern, wird das Attribut &amp;quot;dumpDirRemote&amp;quot; gesetzt. &lt;br /&gt;
&lt;br /&gt;
Auch wenn der MySQL-Server mit auf dem FHEM-Server läuft, also lokal, wird dieses Attribut zur Veränderung des Zielverzeichnisses verwendet.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;b) dumpDirLocal&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Soll die interne Versionsverwaltung und die Dumpfilekompression des Moduls genutzt, sowie die Größe des erzeugten Dumpfiles ausgegeben werden, ist das Verzeichnis &amp;quot;dumpDirRemote&amp;quot; des MySQL-Servers auf dem Client zu mounten und im Attribut &amp;quot;dumpDirLocal&amp;quot; dem DbRep-Device bekannt zu machen.&lt;br /&gt;
Gleiches gilt wenn der FTP-Transfer nach dem Dump genutzt werden soll (Attribut &amp;quot;ftpUse&amp;quot; bzw. &amp;quot;ftpUseSSL&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Im Beispiel läuft der MySQL-Server auf einem entfernten Server und die Dump-Files werden dort im Verzeichnis&lt;br /&gt;
&lt;br /&gt;
 /volume1/ApplicationBackup&lt;br /&gt;
&lt;br /&gt;
angelegt. Dieses Verzeichnis soll auf dem FHEM-Server als Verzeichnis &amp;quot;/sds1/backup&amp;quot; gemountet werden.&lt;br /&gt;
Wenn noch nicht passiert, den NFS Client auf dem FHEM Server installieren:&lt;br /&gt;
&lt;br /&gt;
 sudo apt-get update&lt;br /&gt;
 sudo apt-get install nfs-common&lt;br /&gt;
&lt;br /&gt;
Auf dem entfernten Server wird das Verzeichnis freigegeben/exportiert.&lt;br /&gt;
Wenn noch nicht vorhanden, ist zunächst das notwendige Paket zu installieren:&lt;br /&gt;
&lt;br /&gt;
 sudo apt-get install nfs-kernel-server&lt;br /&gt;
&lt;br /&gt;
Ist das Paket vorhanden, existiert die Datei &#039;&#039;&#039;/etc/exports&#039;&#039;&#039;.&lt;br /&gt;
In der Datei &#039;&#039;&#039;/etc/exports&#039;&#039;&#039; wird das zu exportierende Verzeichnis hinzugefügt. Aus Gründen der Sicherheit wird nur die IP-Adresse des FHEM Servers für den Zugriff zugelassen:&lt;br /&gt;
&lt;br /&gt;
  /volume1/ApplicationBackup 192.168.50.33(rw,root_squash,async,no_subtree_check)&lt;br /&gt;
&lt;br /&gt;
Soll ein Netzwerk freigegeben werden, kann der Eintrag in in dieser Form erfolgen:&lt;br /&gt;
&lt;br /&gt;
 /volume1/ApplicationBackup 192.168.XXX.0/24(rw,root_squash,async,no_subtree_check)&lt;br /&gt;
&lt;br /&gt;
Danach Datei /etc/exports neu einlesen:&lt;br /&gt;
&lt;br /&gt;
 sudo exportfs -ra&lt;br /&gt;
&lt;br /&gt;
Auf dem Client Rechner wird der Mountpoint für das einzuhängende Verzeichnus erstellt:&lt;br /&gt;
&lt;br /&gt;
 sudo mkdir /sds1/backup&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag in &#039;&#039;&#039;/etc/fstab&#039;&#039;&#039; erstellen:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;IP-Adresse&amp;gt;:/volume1/ApplicationBackup /sds1/backup nfs auto,defaults,tcp,intr 0 0&lt;br /&gt;
&lt;br /&gt;
Ausführen:&lt;br /&gt;
&lt;br /&gt;
 mount /sds1/backup&lt;br /&gt;
&lt;br /&gt;
In diesem Verzeichnis existiert das Directory &amp;quot;dumps_FHEM&amp;quot;, in dem die Backup-Files gespeichert werden sollen. Das Attribut wird entsprechend gesetzt auf:&lt;br /&gt;
&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide dumpDirLocal /sds1/backup/dumps_FHEM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Läuft der MySQL-Server mit FHEM lokal auf dem gleichen Server, zeigen die Attribute &#039;&#039;&#039;dumpDirRemote und dumpDirLocal&#039;&#039;&#039; auf das &#039;&#039;&#039;identische Verzeichnis&#039;&#039;&#039;. Trotzdem sind beide Attribute zu definieren.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:&#039;&#039;&#039;b) dumpFilesKeep&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Dieser Parameter stellt die Anzahl der aufzubewahrenden Backup-Files ein. Es werden immer die &amp;quot;x&amp;quot; neuesten bzw. letzten Files im Verzeichnis gehalten, wobei nur die zu der jeweiligen Datenbank gehörenden Files betrachtet werden. &lt;br /&gt;
Die Zugehörigkeit des Dump-Files zur Quelldatenbank wird anhand des Filenamens ermittelt.&lt;br /&gt;
&lt;br /&gt;
Er setzt sich zusammen aus &amp;lt;Datenbankname&amp;gt;_history_&amp;lt;Erstellungsdatum_Uhrzeit&amp;gt;.csv&lt;br /&gt;
&lt;br /&gt;
 Beispiel: fhemtest_history_2020_05_14_03_42.csv (+.gzip falls Dumpfiles komprimiert werden) &lt;br /&gt;
&lt;br /&gt;
Ist dieses Attribut nicht gesetzt, werden die 3 neuesten Backup-Files im Verzeichnis behalten.&lt;br /&gt;
Um nur 2 Files zu behalten wird das Attribut gesetz:&lt;br /&gt;
&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide dumpFilesKeep 2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;c) Aktionen vor und nach dem Backup ausführen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Vor und nach der Dump-Ausführung kann ein FHEM-Kommando bzw. Perl-Befehle ausgeführt werden. Perl-Befehle müssen in &#039;&#039;&#039;{ }&#039;&#039;&#039; eingeschlossen werden.&lt;br /&gt;
&lt;br /&gt;
Für das Beispiel soll vor dem Backup die Verbindung des DbLog-Devices LogDB zur Datenbank getrennt werden. Dadurch werden keine neuen Daten in die Datenbank geschrieben. Wenn das DbLog-Device LogDB im asynchronen Modus wie empfohlen betrieben wird, tritt auch kein Datenverlust ein, da die zu loggenden Daten im Cache verbleiben bis die Verbindung zur DB wiederhergestellt wird.&lt;br /&gt;
&lt;br /&gt;
Um die Verbindung zu trennen wird ein &lt;br /&gt;
&lt;br /&gt;
 set LogDB reopen &amp;lt;Zeit in Sekunden&amp;gt; &lt;br /&gt;
&lt;br /&gt;
verwendet. Die Zeitspanne wird sehr großzügig gewählt und soll sicherstellen, dass innerhalb dieser Zeit das Backup abgeschlossen ist. Nach dem Backup wird sofort &lt;br /&gt;
&lt;br /&gt;
 set LogDB reopen &lt;br /&gt;
&lt;br /&gt;
ausgeführt, was die Verbindung des DbLog-Devices LogDB zur Datenbank unmittelbar wiederherstellt.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide executeBeforeProc set LogDB reopen 3600&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide executeAfterProc set LogDB reopen&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;d) vor dem Backup Datenbank verkleinern (optimizeTables)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese optionale Funktion wird durch das Attribut &amp;quot;optimizeTablesBeforeDump&amp;quot; eingeschaltet. Dadurch diese Funktion wird Platz innerhalb der Datenbank freigegeben der durch Löschvorgänge entstanden ist und dadurch die Größe des Datenbankfiles verringert.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide optimizeTablesBeforeDump 1&lt;br /&gt;
&lt;br /&gt;
Durch die Tabellenoptimierung verlängert sich die Dumpzeit.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;e) das angelegte Dump-File nach dem Backup zum FTP-Server übertragen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
DbRep bietet die Möglichkeit, das erstellte Dump-File per FTP oder verschlüsselt per FTPS zum FTP-Server zu übertragen. Dazu gibt es einen Satz von Attributen um dem Device alle notwendigen Angaben für den FTP-Transfer zur Verfügung zu stellen. Es gibt dafür folgende Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* ftpUse 	: FTP Transfer nach dem Dump wird eingeschaltet (bzw. ftpUseSSL mit SSL Verschlüsselung)&lt;br /&gt;
* ftpUser 	: User zur Anmeldung am FTP-Server, default: anonymous&lt;br /&gt;
* ftpPwd 	: Passwort des FTP-Users, default nicht gesetzt&lt;br /&gt;
* ftpDir 	: Verzeichnis auf dem FTP-Server in welches das File übertragen werden soll (default: FTP-root)&lt;br /&gt;
* ftpPort 	: FTP-Port, default: 21&lt;br /&gt;
* ftpServer 	: Name oder IP-Adresse des FTP-Servers&lt;br /&gt;
* ftpTimeout 	: Timeout für die FTP-Verbindung in Sekunden (default: 30)&lt;br /&gt;
* ftpPassive 	: setzen wenn passives FTP verwendet werden soll&lt;br /&gt;
* ftpDebug 	: Debugging des FTP Verkehrs zur Fehlersuche&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Gemäß dieser Beschreibung und dem Ziel dieses Beispiels werden die für FTP-Transfer relevanten Attribute wie folgt gesetzt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide ftpDir /ftp                # Subdirectory ftp unter FTP-root als Zielverzeichnis&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide ftpPwd ftpftp1&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide ftpServer sds1.&amp;lt;fqdn&amp;gt;&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide ftpUse 1&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide ftpUser ftpuser&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der FTP-Server muss natürlich vorab eingerichtet und funktionsfähig sein. Sollten beim FTP-Transfer Fehler auftreten, kann das Attribut ftpDebug = 1 gesetzt werden um entsprechende Ausgaben zur Analyse des Problems im Log zu erhalten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:dumpmysql1.PNG|right|thumb|300px|Fertig definiertes Device Rep.fhemtest.Dump.ServerSide]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:&#039;&#039;&#039;f) Hilfsattribute&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Für die Funktion nicht relevant aber informativ ist das Attribut &amp;quot;showproctime = 1&amp;quot;. Ist das Attribut gesetzt, wird das Reading &amp;quot;background_processing_time&amp;quot; angelegt. Es enthält nach der Ausführung die verbrauchte Prozesszeit.&lt;br /&gt;
&lt;br /&gt;
Im Attribut &amp;quot;userExitFn&amp;quot; kann eine Perl-Routine hinterlegt werden, um zum Beispiel nach dem Backup eine Mail oder Telegram-Message mit dem Erfolgsstatus zu versenden. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das fertig konfigurierte Device hier als RAW-Definition zur einfachen Nachnutzung. Die Angaben sind natürlich anzupassen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.fhemtest.Dump.ServerSide DbRep LogDB&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide devStateIcon connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen .*Dump.*is.*running.*:remotecontrol/black_btn_PLAYgreen Database.*backup.*finished.*:remotecontrol/black_btn_GREEN tn_GREEN error.*:remotecontrol/black_btn_RED&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide dumpCompress 1&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide dumpDirLocal /sds1/backup/dumps_FHEM&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide dumpDirRemote /volume1/ApplicationBackup/dumps_FHEM&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide dumpFilesKeep 1&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide event-on-update-reading state&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide executeAfterProc set LogDB reopen&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide executeBeforeProc set LogDB reopen 3600&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide fastStart 1&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide ftpDebug 0&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide ftpDir /ftp&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide ftpDumpFilesKeep 3&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide ftpPwd ftpftp1&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide ftpServer sds1.&amp;lt;fqdn&amp;gt;&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide ftpUse 0&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide ftpUser ftpuser&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide optimizeTablesBeforeDump 1&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide showproctime 1&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide userExitFn doafterdump&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 3. Backup durchführen ===== &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Voraussetzung ist, dass der verwendete Datenbankuser das globale Privileg &#039;&#039;&#039;FILE&#039;&#039;&#039; besitzt. &lt;br /&gt;
Falls der User dieses Recht nicht hat, kann man es mit dem definierten DbRep-Device wie folgt erledigen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;(optional) dem Datenbankuser das FILE Privileg zuweisen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Da die Rechtezuweisung nur mit einem administrativen DB-User (i.A. root) funktioniert, wird dieser User im DbRep-Device angelegt und aktiviert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 set  Rep.fhemtest.Dump.ServerSide adminCredentials &amp;lt;Admin-User&amp;gt; &amp;lt;Passwort&amp;gt;&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide useAdminCredentials 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun kann dem verwendeten Datenbankuser das FILE Privileg zugeordnet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set  Rep.fhemtest.Dump.ServerSide sqlCmd GRANT FILE ON *.* TO &#039;&amp;lt;DB-User&amp;gt;&#039;@&#039;%&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei ist &#039;&#039;&#039;&amp;lt;DB-User&amp;gt;&#039;&#039;&#039; durch den in der DbLog-Konfiguration angegebenen User zu ersetzen da die gesamte Datenbankarbeit mit diesem User erfolgt.&lt;br /&gt;
&lt;br /&gt;
Nach der Ausführung schaltet man die Nutzung des administrativen Users wieder ab mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide useAdminCredentials 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Rechte können nun überprüft werden mit&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set  Rep.fhemtest.Dump.ServerSide sqlCmd show grants;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und werden im Ergebnis dargestellt (z.B. für den DB-User fhemtest):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SqlResultRow_1  GRANTS FOR FHEMTEST@%&lt;br /&gt;
SqlResultRow_2  GRANT PROCESS, FILE, INDEX ON *.* TO &#039;fhemtest&#039;@&#039;%&#039; IDENTIFIED BY PASSWORD &#039;*...............&#039;&lt;br /&gt;
SqlResultRow_3  GRANT SELECT, INSERT, UPDATE, DELETE, ALTER, EXECUTE, SHOW VIEW ON `fhemtest`.* TO &#039;fhemtest&#039;@&#039;%&#039;&lt;br /&gt;
SqlResultRow_4  GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, INDEX ON `fhemtest`.`fhemtest` TO &#039;fhemtest&#039;@&#039;%&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:&#039;&#039;&#039;Backup starten&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:dumpmysql2.PNG|right|thumb|400px|Backup läuft ...]]&lt;br /&gt;
Mit dem wie beschrieben eingerichteten DbRep-Device kann das Backup gestartet werden mit:&lt;br /&gt;
&lt;br /&gt;
 set Rep.fhemtest.Dump.ServerSide dumpMySQL serverSide&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zu Beginn des Vorgangs wird sofort die Verbindung des DbLog-Devices zur Datenbank getrennt.&lt;br /&gt;
Ein laufendes serverSide Backup kann mit FHEM-Mitteln nicht abgebrochen werden !&lt;br /&gt;
&lt;br /&gt;
Das laufende Backup wird im state angezeigt mit &amp;quot;serverSide Dump is running - be patient and see Logfile !&amp;quot;.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Logfile mit (mindestens) verbose 3 werden die relevanten Informationen zur Verfügung gestellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2020.05.14 22:59:18.610 3: DbRep Rep.fhemtest.Dump.ServerSide - ########################################&lt;br /&gt;
2020.05.14 22:59:18.611 3: DbRep Rep.fhemtest.Dump.ServerSide - ###   New database serverSide dump   ###&lt;br /&gt;
2020.05.14 22:59:18.611 3: DbRep Rep.fhemtest.Dump.ServerSide - ########################################&lt;br /&gt;
2020.05.14 22:59:18.612 3: DbRep Rep.fhemtest.Dump.ServerSide - execute command before dump: &#039;set LogDB reopen 3600&#039; &lt;br /&gt;
2020.05.14 22:59:18.613 2: DbLog LogDB: Connection closed until 23:59:18 (3600 seconds).&lt;br /&gt;
2020.05.14 22:59:18.660 3: DbRep Rep.fhemtest.Dump.ServerSide - Searching for tables inside database fhemtest....&lt;br /&gt;
2020.05.14 22:59:18.664 3: DbRep Rep.fhemtest.Dump.ServerSide - Size of database fhemtest before optimize (MB): 8298.98&lt;br /&gt;
2020.05.14 22:59:18.665 3: DbRep Rep.fhemtest.Dump.ServerSide - Optimizing tables&lt;br /&gt;
2020.05.14 22:59:18.665 3: DbRep Rep.fhemtest.Dump.ServerSide - Optimizing table `current` (INNODB). It will take a while.&lt;br /&gt;
2020.05.14 22:59:19.560 3: DbRep Rep.fhemtest.Dump.ServerSide - Table 1 `current` optimized successfully.&lt;br /&gt;
2020.05.14 22:59:19.560 3: DbRep Rep.fhemtest.Dump.ServerSide - Optimizing table `history` (INNODB). It will take a while.&lt;br /&gt;
2020.05.14 23:30:09.183 3: DbRep Rep.fhemtest.Dump.ServerSide - Table 2 `history` optimized successfully.&lt;br /&gt;
2020.05.14 23:30:09.186 3: DbRep Rep.fhemtest.Dump.ServerSide - 2 tables have been optimized.&lt;br /&gt;
2020.05.14 23:30:09.253 3: DbRep Rep.fhemtest.Dump.ServerSide - Size of database fhemtest after optimize (MB): 8298.98&lt;br /&gt;
2020.05.14 23:30:09.254 3: DbRep Rep.fhemtest.Dump.ServerSide - Starting dump of database &#039;fhemtest&#039;, table &#039;history&#039;&lt;br /&gt;
2020.05.14 23:39:47.566 3: DbRep Rep.fhemtest.Dump.ServerSide - compress file /sds1/backup/dumps_FHEM/fhemtest_history_2020_05_14_23_30.csv&lt;br /&gt;
2020.05.14 23:42:33.138 3: DbRep Rep.fhemtest.Dump.ServerSide - file compressed to output file: /sds1/backup/dumps_FHEM/fhemtest_history_2020_05_14_23_30.csv.gzip&lt;br /&gt;
2020.05.14 23:42:34.784 3: DbRep Rep.fhemtest.Dump.ServerSide - input file deleted: /sds1/backup/dumps_FHEM/fhemtest_history_2020_05_14_23_30.csv&lt;br /&gt;
2020.05.14 23:42:34.785 3: DbRep Rep.fhemtest.Dump.ServerSide - Number of exported datasets: 49032159&lt;br /&gt;
2020.05.14 23:42:34.787 3: DbRep Rep.fhemtest.Dump.ServerSide - Size of backupfile: 419.73 MB&lt;br /&gt;
2020.05.14 23:42:37.082 3: DbRep Rep.fhemtest.Dump.ServerSide - FTP: transferring /sds1/backup/dumps_FHEM/fhemtest_history_2020_05_14_23_30.csv.gzip&lt;br /&gt;
2020.05.14 23:42:47.511 3: DbRep Rep.fhemtest.Dump.ServerSide - FTP: fhemtest_history_2020_05_14_23_30.csv.gzip transferred successfully to sds1.myds.me into dir /ftp&lt;br /&gt;
2020.05.14 23:42:47.558 3: DbRep Rep.fhemtest.Dump.ServerSide - Deleting old dumpfile &#039;fhemtest_history_2020_05_14_22_12.csv.gzip&#039; &lt;br /&gt;
2020.05.14 23:42:47.857 3: DbRep Rep.fhemtest.Dump.ServerSide - Finished backup of database fhemtest - total time used (hh:mm:ss): 00:43:29&lt;br /&gt;
2020.05.14 23:42:47.948 2: DbRep Rep.fhemtest.Dump.ServerSide - command message after dump: &amp;quot;Reopen executed.&amp;quot; &lt;br /&gt;
2020.05.14 23:42:47.973 3: DbRep Rep.fhemtest.Dump.ServerSide - Database dump finished successfully. &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:dumpmysql3.PNG|right|thumb|500px|Backup ist erfolgreich abgeschlossen]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In vorliegenden Beispiel wird nach einem erfolgreichen Backup im state &#039;&#039;&#039;&amp;quot;Warning - dump finished, but command after dump message appeared&amp;quot;&#039;&#039;&#039; angezeigt. &lt;br /&gt;
&lt;br /&gt;
Diese Warnung rührt daher, dass das Reopen-Kommando eine Rückkehrinfo mitteilt, die als Issue gewertet und im Reading &amp;quot;afterdump_message&amp;quot; ausgegeben wird. In diesem Fall ist es nur eine Information.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In den erstellten Readings wird zusammengetragen welches File mit welcher Größe erstellt wurde, welche alten Files gelöscht wurden, Informationen zum FTP-Transfer und welche Zeit der Gesamtprozess benötgt hat.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das so vorbereitete Backup kann nun z.B. täglich oder auch mit einer wesentlich kürzeren Wiederholungsperiode (alle x Stunden) über ein at-Device eingeplant werden:&lt;br /&gt;
&lt;br /&gt;
 define At.MySQL.Dump at *23:52:00 set Rep.fhemtest.Dump.ServerSide dumpMySQL serverSide&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 4. Restore ===== &lt;br /&gt;
&lt;br /&gt;
Voraussetzung für das Restore ist, dass der verwendete Datenbankuser das globale Privileg &#039;&#039;&#039;FILE&#039;&#039;&#039; besitzt. &lt;br /&gt;
Falls der User dieses Recht nicht hat, kann man es mit dem definierten DbRep-Device wie folgt erledigen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;(optional) dem Datenbankuser das FILE Privileg zuweisen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Da die Rechtezuweisung nur mit einem administrativen DB-User (i.A. root) funktioniert, wird dieser User im DbRep-Device angelegt und aktiviert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 set  Rep.fhemtest.Dump.ServerSide adminCredentials &amp;lt;Admin-User&amp;gt; &amp;lt;Passwort&amp;gt;&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide useAdminCredentials 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun kann dem verwendeten Datenbankuser das FILE Privileg zugeordnet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set  Rep.fhemtest.Dump.ServerSide sqlCmd GRANT FILE ON *.* TO &#039;&amp;lt;DB-User&amp;gt;&#039;@&#039;%&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei ist &#039;&#039;&#039;&amp;lt;DB-User&amp;gt;&#039;&#039;&#039; durch den in der DbLog-Konfiguration angegebenen User zu ersetzen da die gesamte Datenbankarbeit mit diesem User erfolgt.&lt;br /&gt;
&lt;br /&gt;
Nach der Ausführung schaltet man die Nutzung des administrativen Users wieder ab mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide useAdminCredentials 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alternativ&#039;&#039;&#039; kann die Rechtezuweisung auf der Konsole des MySQL-Servers ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo mysql -u root -p&lt;br /&gt;
GRANT File ON *.* TO &#039;&amp;lt;DB-User&amp;gt;&#039;@&#039;%&#039;;&lt;br /&gt;
exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Rechte können nun überprüft werden mit&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set  Rep.fhemtest.Dump.ServerSide sqlCmd show grants;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nun kann der Restore ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein mit der Option &#039;&#039;&#039;serverSide&#039;&#039;&#039; erstellter Dump enthält ausschließlich die Daten der history-Tabelle. &lt;br /&gt;
Der Restore kann nur in eine bestehende Datenabank und entsprechend angelegte history Tabelle erfolgen.&lt;br /&gt;
Ist nach einem Datenbankfehler die Tabelle/Datenbank zerstört, must sie zunächst mit den vorbereiteten Befehlen in den &lt;br /&gt;
[https://svn.fhem.de/trac/browser/trunk/fhem/contrib/dblog/db_create_mysql.sql SVN-Skripten] bereitgestellt werden.&lt;br /&gt;
&lt;br /&gt;
Ist die history-Tabelle noch intakt und soll nur der Inhalt ersetzt werden, muss die Tabelle vorab geleert werden da sonst unter Umständen doppelte Datensätze enstehen wenn kein primary Key verwendet ist.&lt;br /&gt;
Das kann einfach mit dem Befehl&lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; sqlCmd truncate table history&lt;br /&gt;
&lt;br /&gt;
erreicht werden. Damit werden &#039;&#039;&#039;alle&#039;&#039;&#039; Daten der history-Tabelle hochperformant gelöscht. Die Tabelle selbst bleibt erhalten. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sollte der truncate Befehl wegen nicht ausreichender Rechte des DB-Users mit Fehler enden, kann ein administrativer Datenbankuser (i.A. &#039;root&#039;) mit dem Kommando und Attribut&lt;br /&gt;
&lt;br /&gt;
 set  &amp;lt;Name&amp;gt; adminCredentials &amp;lt;Name&amp;gt; &amp;lt;Passwort&amp;gt;&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; useAdminCredentials 1&lt;br /&gt;
&lt;br /&gt;
hinterlegt und aktiviert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Restore kann mit dem angelegten DbRep-Device im laufenden Betrieb geschehen. Auch hier ist wieder wichtig, dass das DbLog-Device  im asynchronen Modus betrieben wird und die Reopen-Zeit (siehe Einstellung Attribut &amp;quot;executeBeforeProc&amp;quot;) sehr großzügig eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Zum Restore dient der Befehl &lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; restoreMySQL &amp;lt;File&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:dumpmysql4.PNG|right|thumb|500px|Auswahlmenü Files für Restore ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im FHEMWEB öffnet sich dazu eine DropDown-Liste die alle vorhandenen und zur Quelldatenbank passenden Dumpfiles auflistet. &lt;br /&gt;
Das herzustellende File kann so bequem ausgewählt werden. Die Dumpfiles müssen sich in dem Verzeichnis befinden, welches durch das Attribut &#039;&#039;&#039;dumpDirLocal&#039;&#039;&#039; festgelegt wurde (default (./log).  [[Datei:dumpmysql5.PNG|left|thumb|500px|Restore läuft]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der restore wird demzufolge gestartet mit:&lt;br /&gt;
&lt;br /&gt;
 set Rep.fhemtest.Dump.ServerSide restoreMySQL &amp;lt;File&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Restore der history-Tabelle kann nun online im laufenden FHEM-Betrieb erfolgen.&lt;br /&gt;
Wieder wird zu Beginn des Vorgangs das verbundene DbLog-Device von der Datenbank abgekoppelt und nach dem Restore wieder automatisch verbunden. Eine Datenbankoptimierung bzw. FTP-Transfer wird bei diesem Vorgang natürlich nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:dumpmysql6.PNG|left|thumb|500px|Fortschrittsanzeige in einem zweiten DbRep Device]]&lt;br /&gt;
In einem zweiten (z.B. kopierten) DbRep Device kann mit Hilfe des Befehls&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 get Rep.fhemtest.Dump.ServerSide procinfo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
der Fortschritt des Restores überwacht werden. In der Spalte &#039;&#039;&#039;PROGRESS&#039;&#039;&#039; wird der Fortschritt in &#039;&#039;&#039;%&#039;&#039;&#039; angegeben.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Log zeigt den Ablauf des Restores:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2020.05.15 13:07:24.369 3: DbRep Rep.fhemtest.Dump.ServerSide - #######################################&lt;br /&gt;
2020.05.15 13:07:24.370 3: DbRep Rep.fhemtest.Dump.ServerSide - ###   New database Restore/Recovery ###&lt;br /&gt;
2020.05.15 13:07:24.371 3: DbRep Rep.fhemtest.Dump.ServerSide - #######################################&lt;br /&gt;
2020.05.15 13:07:24.371 3: DbRep Rep.fhemtest.Dump.ServerSide - execute command before restore: &#039;set LogDB reopen 3600&#039; &lt;br /&gt;
2020.05.15 13:07:24.429 3: DbRep Rep.fhemtest.Dump.ServerSide - uncompress file /sds1/backup/dumps_FHEM/fhemtest_history_2020_05_15_11_47.csv.gzip&lt;br /&gt;
2020.05.15 13:09:31.152 3: DbRep Rep.fhemtest.Dump.ServerSide - file uncompressed to output file: /sds1/backup/dumps_FHEM/fhemtest_history_2020_05_15_11_47.csv&lt;br /&gt;
2020.05.15 13:09:31.237 3: DbRep Rep.fhemtest.Dump.ServerSide - Size of uncompressed file: 5511.52 MB&lt;br /&gt;
2020.05.15 13:09:31.242 3: DbRep Rep.fhemtest.Dump.ServerSide - Starting restore of database &#039;fhemtest&#039;, table &#039;history&#039;. &lt;br /&gt;
2020.05.15 14:42:28.566 2: DbLog LogDB: Connection closed until 16:42:28 (7200 seconds).&lt;br /&gt;
2020.05.15 14:46:22.391 3: DbRep Rep.fhemtest.Dump.ServerSide - Restore of /volume1/ApplicationBackup/dumps_FHEM/fhemtest_history_2020_05_15_11_47.csv into &#039;fhemtest&#039;, &#039;history&#039; finished - total time used (hh:mm:ss): 01:38:57&lt;br /&gt;
2020.05.15 14:46:22.457 2: DbRep Rep.fhemtest.Dump.ServerSide - command message after restore: &amp;quot;Reopen executed.&amp;quot; &lt;br /&gt;
2020.05.15 14:46:22.481 3: DbRep Rep.fhemtest.Dump.ServerSide - Database restore finished successfully.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:dumpmysql7.PNG|right|thumb|300px|Restore erfolgreich abgeschlossen]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Restore wurde erfolgreich abgeschlossen und die Verbindung des DbLog-Device LogDB zur Datenbank wiederhergestellt. Das Logging wird nahtlos fortgeführt.&lt;br /&gt;
&lt;br /&gt;
Es ist natürlich zu beachten, dass die Daten zwischen dem Erstellungszeitpunkt des eingespielten Backups und der aktuellen Zeit verloren sind !&lt;br /&gt;
Eventuell in der Tabelle &#039;&#039;&#039;history&#039;&#039;&#039; noch vorhandene Daten werden &#039;&#039;&#039;nicht&#039;&#039;&#039; überschrieben. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Speichern von Berechnungswerten in der Datenbank und Erstellen eines Plots (ab Version 7.5.1) ===&lt;br /&gt;
&lt;br /&gt;
Es sollen aus in der Datenbank vorhandenen minütlichen Leistungswerten eines Wechselrichters die maximal-, minimal- und durchschnittlichen Leistungswerte pro Tag berechnet werden und diese Ergebnisse wieder in die Datenbank geschrieben werden um daraus einen Plot zu erstellen.&lt;br /&gt;
Diese Berechnung soll immer aktuell für den laufenden Monat erfolgen.&lt;br /&gt;
&lt;br /&gt;
Die Ausgangswerte liegen in der Datenbank als minütliche kW-Einträge vor (Auszug DbRep fetchrows):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2018-01-18_15-55-49__MySTP_5000__total_pac 0.200&lt;br /&gt;
2018-01-18_15-56-50__MySTP_5000__total_pac 0.218&lt;br /&gt;
2018-01-18_16-00-14__MySTP_5000__total_pac 0.209&lt;br /&gt;
2018-01-18_16-00-39__MySTP_5000__total_pac 0.198&lt;br /&gt;
2018-01-18_16-01-39__MySTP_5000__total_pac 0.142&lt;br /&gt;
2018-01-18_16-02-39__MySTP_5000__total_pac 0.130&lt;br /&gt;
2018-01-18_16-03-39__MySTP_5000__total_pac 0.117&lt;br /&gt;
2018-01-18_16-05-39__MySTP_5000__total_pac 0.087&lt;br /&gt;
2018-01-18_16-06-39__MySTP_5000__total_pac 0.071&lt;br /&gt;
2018-01-18_16-07-39__MySTP_5000__total_pac 0.076&lt;br /&gt;
2018-01-18_16-08-39__MySTP_5000__total_pac 0.065&lt;br /&gt;
2018-01-18_16-09-39__MySTP_5000__total_pac 0.069&lt;br /&gt;
2018-01-18_16-10-39__MySTP_5000__total_pac 0.060&lt;br /&gt;
2018-01-18_16-12-39__MySTP_5000__total_pac 0.065&lt;br /&gt;
2018-01-18_16-13-39__MySTP_5000__total_pac 0.068&lt;br /&gt;
2018-01-18_16-14-39__MySTP_5000__total_pac 0.054&lt;br /&gt;
2018-01-18_16-15-39__MySTP_5000__total_pac 0.041&lt;br /&gt;
2018-01-18_16-16-39__MySTP_5000__total_pac 0.027&lt;br /&gt;
2018-01-18_16-17-39__MySTP_5000__total_pac 0.026&lt;br /&gt;
2018-01-18_16-18-39__MySTP_5000__total_pac 0.021&lt;br /&gt;
2018-01-18_16-19-39__MySTP_5000__total_pac 0.018&lt;br /&gt;
2018-01-18_16-20-39__MySTP_5000__total_pac 0.012&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für die Berechnung der Ergenisse stehen in DbRep die Kommandos maxValue, minValue und averageValue zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== 1. Anlegen des DbRep-Devices ====&lt;br /&gt;
&lt;br /&gt;
Das anzulegende Device wird für alle notwendigen Berechnungen verwendet.&lt;br /&gt;
Die Definition des DbRep-Devices erfolgt unter Angabe des zu verbindenden DbLog-Devices (nicht der Datenbank selbst): &lt;br /&gt;
&lt;br /&gt;
 define Rep.total_pac DbRep LogDB&lt;br /&gt;
&lt;br /&gt;
Ist das Device definiert, verbindet es sich zu der Datenbank und wechselt in den state &amp;quot;connected&amp;quot;, sofern die Verbindung erfolgreich verlief.&lt;br /&gt;
Prinzipiell funktioniert der hier Ablauf mit einem im synchronous Mode betriebenen DbLog-Device, aber es ist dringend angeraten das DbLog-Device (LogDB) in den asynchronen Modus umzuschalten. Dadurch werden Blockierungen von FHEM verhindert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 2. Einstellungen des DbRep-Devices (Attribute) ====&lt;br /&gt;
[[Datei:Rep.total_pac.PNG|right|thumb|400px|Device definiert]]&lt;br /&gt;
Für die gewünschte Funktion müssen in diesem neu definierten Device einige relevante Attribute gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Da die Berechnungen immer für den aktuellen Monat erstellt werden sollen, wird das Attribut &amp;quot;timestamp_begin&amp;quot; so definiert, dass immer der Beginn des aktuellen Monats dynamisch ermittelt wird. Das ist gegeben durch den Eintrag:&lt;br /&gt;
&lt;br /&gt;
 attr Rep.total_pac timestamp_begin current_month_begin&lt;br /&gt;
&lt;br /&gt;
Es soll ein Ergebniswert pro Tag erzeugt werden. Damit dies erreicht werden kann, wird das Attribut &amp;quot;aggregation&amp;quot; verwendet und auf den Wert &amp;quot;day&amp;quot; eingestellt. Würde zum Beispiel ein stündlicher Ergebniswert gewünscht sein, hätte &amp;quot;aggregation = hour&amp;quot; den benötigten Effekt.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.total_pac aggregation day&lt;br /&gt;
&lt;br /&gt;
Die Attribute &amp;quot;device&amp;quot; und &amp;quot;reading&amp;quot; legen das auszuwertende Device und Reading fest. Dabei ist zu beachten, dass im Gegensatz zur allgemein gültigen Möglichkeit devspec bzw. SQL-Wildcards zu verwenden bei dieser speziellen Detailfunktion dies nicht der Fall ist. Das heißt. es muß immer ein eindeutiges Device und ein eindeutiges Reading angegeben werden. Der Grund liegt in der Notwendigkeit, bei der Speicherung der Ergebnisdaten der Datenbank ebenfalls ein eindeutiges Device und Reading zu übergeben. In dem Beispiel wird das Device &amp;quot;MySTP_5000&amp;quot; und das Reading &amp;quot;total_pac&amp;quot; ausgewertet.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.total_pac device MySTP_5000&lt;br /&gt;
 attr Rep.total_pac reading total_pac&lt;br /&gt;
&lt;br /&gt;
Hilfreiche Attribute sind noch &amp;quot;event-on-update-reading&amp;quot; zur Begrenzung der Events, &amp;quot;showproctime&amp;quot; zur Bestimmung der Prozesszeiten und &amp;quot;allowDeletion&amp;quot; um die Möglichkeit zu haben, irrtümlich gespeicherte Berechnungsergebnisse wieder über das DbRep-Device zu löschen.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.total_pac event-on-update-reading state&lt;br /&gt;
 attr Rep.total_pac showproctime 1&lt;br /&gt;
 attr Rep.total_pac allowDeletion 1&lt;br /&gt;
&lt;br /&gt;
Das so vorbereitete Device ist für die benötigten Funktionen ausreichend eingestellt. &lt;br /&gt;
Zur einfachen Nachnutzung nochfolgend die Raw-Definition des DbRep-Devices:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.total_pac DbRep LogDB&lt;br /&gt;
attr Rep.total_pac aggregation day&lt;br /&gt;
attr Rep.total_pac allowDeletion 1&lt;br /&gt;
attr Rep.total_pac device MySTP_5000&lt;br /&gt;
attr Rep.total_pac event-on-update-reading state&lt;br /&gt;
attr Rep.total_pac reading total_pac&lt;br /&gt;
attr Rep.total_pac room DbLog&lt;br /&gt;
attr Rep.total_pac showproctime 1&lt;br /&gt;
attr Rep.total_pac timestamp_begin current_month_begin&lt;br /&gt;
attr Rep.total_pac verbose 3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== 3. Berechnung ausführen und Speicherung der Ergebnisse ====&lt;br /&gt;
[[Datei:average.PNG|right|thumb|300px|Average Berechnung]]&lt;br /&gt;
Die benötigten Funktionen sind:&lt;br /&gt;
&lt;br /&gt;
 averageValue - Berechnung des täglichen Durchschnittswert der Wechselrichterleistung (kW)&lt;br /&gt;
 maxValue - Berechnung des täglichen Maximalwertes der Wechselrichterleistung (kW)&lt;br /&gt;
 minValue - Berechnung des täglichen Minimalwertes der Wechselrichterleistung (kW)&lt;br /&gt;
&lt;br /&gt;
[[Datei:minval.PNG|right|thumb|300px|MinVal Berechnung]]&lt;br /&gt;
Für jede dieser Kommandos stehen im DbRep die Optionen &amp;quot;display&amp;quot; und &amp;quot;writeToDB&amp;quot; zur Verfügung. Die Option &amp;quot;display&amp;quot; stellt die Ergenisse lediglich im Browser dar und kann dazu die Ergebnisse vor der Speicherung zu bewerten.&lt;br /&gt;
So ergeben die Kommandos:&lt;br /&gt;
&lt;br /&gt;
 set Rep.total_pac minValue display &lt;br /&gt;
 set Rep.total_pac maxValue display &lt;br /&gt;
 set Rep.total_pac avarageValue display &lt;br /&gt;
&lt;br /&gt;
die täglichen Min-, Max- und Durchschnittswerte der WR-Leistung.&lt;br /&gt;
&lt;br /&gt;
[[Datei:maxval.PNG|right|thumb|300px|MaxVal Berechnung]]&lt;br /&gt;
Entsprechen die Ergebnisse den Erwartungen bzw. dem Ziel, können die folgenden Kommandos verwendet werden um die Berechnung durchzuführen und dabei gleichzeitig in die Datenbank zu schreiben. &lt;br /&gt;
&lt;br /&gt;
 set Rep.total_pac minValue writeToDB&lt;br /&gt;
 set Rep.total_pac maxValue writeToDB&lt;br /&gt;
 set Rep.total_pac avarageValue writeToDB&lt;br /&gt;
&lt;br /&gt;
Die Daten werden in der history-Tabelle gespeichert und, sofern das DbLog-Device das Attribut &amp;quot;DbLogType = Current...&amp;quot; gestzt hat, auch in der current-Tabelle gespeichert. Im letzteren Fall kann das Ergebnisreading bei der Erstellung des Plots sofort aus der DropDown-Liste ausgewählt werden.&lt;br /&gt;
&lt;br /&gt;
Bei der Datenspeicherung wird der neue Readingnamen aus einem Präfix und dem originalen Readingnamen gebildet. Der Präfix setzt sich aus der Bildungsfunktion und der gewählten Aggregation zusammen, d.h. im Fall des Beispiels aus &amp;quot;min&amp;quot;,&amp;quot;max&amp;quot;,&amp;quot;avg&amp;quot; und &amp;quot;day&amp;quot;.&lt;br /&gt;
Der Timestamp der neuen Readings in der Datenbank wird von der eingestellten Aggregationsperiode abgeleitet, sofern kein eindeutiger Zeitpunkt des Ergebnisses bestimmt werden kann. So kann der Zeitpunkt des Maximalwertes eines Tages wertes genau bestimmt werden und der resultierende Datensatz wird mit diesem Timestamp gespeichert.&lt;br /&gt;
&lt;br /&gt;
Die resultierende Readingnamen werden somit wie folgt in der Datenbank gespeichert:&lt;br /&gt;
&lt;br /&gt;
 max_day_total_pac&lt;br /&gt;
 min_day_total_pac&lt;br /&gt;
 avg_day_total_pac&lt;br /&gt;
&lt;br /&gt;
Die Berechnungen können regelmäßig über ein at eingeplant werden. Die Funktionen vermeiden dabei die Speicherung von doppelten Datensätzen. Wurde bei einem Berechnungslauf der Datensatz bereits gespeichert, wird dieser Datensatz beim nächsten Durchlauf nicht noch einmal gespeichert.&lt;br /&gt;
&lt;br /&gt;
==== 4. Erstellung des Plots ====&lt;br /&gt;
[[Datei:dropdown.PNG|right|thumb|300px|Drop-Down Liste SVG]]&lt;br /&gt;
Wird durch das DbLog-Device die current-Tabelle genutzt, das Attribut &amp;quot;DbLogType = Current...&amp;quot; ist gesetzt, können die Ergebnisreadings max_day_total_pac, min_day_total_pac und avg_day_total_pac im SVG-Editor ausgewählt werden (siehe {{Link2CmdRef|Lang=de|Anker=SVG}} bei SVG).&lt;br /&gt;
&lt;br /&gt;
Durch die Wahl des Plot-Typ &amp;quot;bars&amp;quot; und dem SVG-Attribut &amp;quot;fixedrange = month&amp;quot; entsteht der gewünschte Tageswert-Plot über den aktuellen Monat.&lt;br /&gt;
&lt;br /&gt;
[[Datei:svg_calc.PNG|right|thumb|300px|Ergebnis SVG]]&lt;br /&gt;
Hier wiederum die Raw-Definition des SVG-Device:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod SVG_LogDB_16 SVG LogDB:SVG_LogDB_16:HISTORY&lt;br /&gt;
attr SVG_LogDB_16 fixedrange month&lt;br /&gt;
attr SVG_LogDB_16 room Energie,SVG_MySQL&lt;br /&gt;
attr SVG_LogDB_16 title &amp;quot;Max Leistung: $data{max1} kW, Min Leistung: $data{min3} kW,Max. Durchschnittsleistung: $data{max2} kW &amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sowie des gplot-Files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2018-01-18 21:45:27&lt;br /&gt;
set terminal png transparent size &amp;lt;SIZE&amp;gt; crop&lt;br /&gt;
set output &#039;&amp;lt;OUT&amp;gt;.png&#039;&lt;br /&gt;
set xdata time&lt;br /&gt;
set timefmt &amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;&lt;br /&gt;
set xlabel &amp;quot; &amp;quot;&lt;br /&gt;
set title &#039;&amp;lt;TL&amp;gt;&#039;&lt;br /&gt;
set ytics &lt;br /&gt;
set y2tics &lt;br /&gt;
set grid&lt;br /&gt;
set ylabel &amp;quot;kW&amp;quot;&lt;br /&gt;
set y2label &amp;quot;kW&amp;quot;&lt;br /&gt;
set yrange [0:6]&lt;br /&gt;
set y2range [0:6]&lt;br /&gt;
&lt;br /&gt;
#LogDB MySTP_5000:max_day_total_pac&lt;br /&gt;
#LogDB MySTP_5000:avg_day_total_pac&lt;br /&gt;
#LogDB MySTP_5000:min_day_total_pac&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Max-Leistung / Tag&#039; ls l0fill lw 1 with bars,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Average / Tag&#039; ls l2fill lw 1 with bars,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Min-Leistung / Tag&#039; ls l1fill lw 1 with bars&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Öffnungszeiten von Fenster/Türen aus der Datenbank ermitteln ===&lt;br /&gt;
&lt;br /&gt;
Ziel ist es, die Öffnungszeit eines Fensters oder einer Tür zu ermitteln sobald es wieder geschlossen wird und ein Reading mit der Zeit in dem entsprechenden Device (Fensterkontakt) zu setzen.&lt;br /&gt;
&lt;br /&gt;
Für das Beispiel wird angenommen dass:&lt;br /&gt;
&lt;br /&gt;
* der Status der Sensoren als &amp;quot;open&amp;quot; und &amp;quot;close&amp;quot; in der Datenbank gespeichert ist&lt;br /&gt;
* das verwendetet DbRep-Device &amp;quot;Rep.WindowOpenTime&amp;quot; heißt&lt;br /&gt;
* das verwendete DbLog-Device &amp;quot;LogDBShort&amp;quot; heißt (wird mit dem DbRep-Device bei der Definition assoziiert)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zunächst wird zur Auswertung von closed-Events ein entsprechendes Notify definiert (Raw-Format):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod N.WindowClosed notify (.*fenster.*|.*terrasse.tuer.*):closed \&lt;br /&gt;
  { \&lt;br /&gt;
    if($NAME =~ /(.*fenster.*|.*terrasse.tuer.*)/) {\&lt;br /&gt;
      fhem(&amp;quot;set LogDBShort commitCache;; defmod calcse at +00:00:10 {SwitchEvent(\&amp;quot;$NAME\&amp;quot;)};;&amp;quot;);; \&lt;br /&gt;
    }\&lt;br /&gt;
  }&lt;br /&gt;
attr N.WindowClosed alias Starte Ermittlung der Fenster Öffnungszeiten&lt;br /&gt;
attr N.WindowClosed comment Wird verwendet um die letzte Öffnungszeit der Fenster aus der Datenbank zu ermitteln und im Reading &amp;quot;LastOpenTime&amp;quot; abzulegen.&lt;br /&gt;
attr N.WindowClosed room Datenbank-&amp;gt;Produktiv&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zusätzlich if-Abfrage im Notify behob ein Problem bei den Fensterkontakten die mit einem Thermostat gepeert sind, da in diesem Fall der Event mehrfach auftrat. Der Teil &#039;&#039;&#039;&amp;quot;set LogDBShort commitCache&amp;quot;&#039;&#039;&#039; speichert den Cache der Datenbank vor der Berechnung sofern sie im asynchronen Modus betrieben wird (kann entfallen wenn im synchronen Mode betrieben). Um der DB Zeit zur Abspeicherung zu geben, wird die Berechnung über ein AT mit einer entsprechenden Verzögerung (hier 10 Sekunden) angestartet. Die Zeit im AT bitte entsprechend der eigenen Systembedingungen anpassen.  &lt;br /&gt;
&lt;br /&gt;
Im Notify wird die Funktion &#039;&#039;&#039;SwitchEvent($NAME)&#039;&#039;&#039; aufgerufen. Diese Funktion wird in der 99_myUtils.pm eingefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
##########################################################################################################&lt;br /&gt;
#  This function is used to start an SQL query for the given device &amp;quot;$name&amp;quot; and check&lt;br /&gt;
#  how much time the device was in state defined by $usedVal1 today.&lt;br /&gt;
#  The query is done by a DbRep device.&lt;br /&gt;
# &lt;br /&gt;
#  In this example the name of the DbRep device is &amp;quot;Rep.WindowOpenTime&amp;quot;&lt;br /&gt;
##########################################################################################################&lt;br /&gt;
sub SwitchEvent($) {&lt;br /&gt;
  my ($name) = @_;&lt;br /&gt;
 &lt;br /&gt;
  Log3 $name , 5, &amp;quot;$name - SwitchEvent called&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
  my $usedReading = &amp;quot;\&amp;quot;state\&amp;quot;&amp;quot;;           # name of the reading which holds the state which has to be checked&lt;br /&gt;
  my $usedVal1 = &amp;quot;\&amp;quot;open\&amp;quot;&amp;quot;;               # reading value which starts the time measuerement&lt;br /&gt;
  my $usedVal2 = &amp;quot;\&amp;quot;closed\&amp;quot;&amp;quot;;             # reading value which stops the time measurement&lt;br /&gt;
  my $device = &amp;quot;\&amp;quot;$name\&amp;quot;&amp;quot;;                # name of the device which state has to be checked&lt;br /&gt;
  my $dbRepDevice = &amp;quot;Rep.WindowOpenTime&amp;quot;;  # name of the DbRep device&lt;br /&gt;
 &lt;br /&gt;
  # The query will check for the current day the cumulated time difference between $usedVal1 and $usedVal2&lt;br /&gt;
  # the example here is a homemetic threeStateSensor (window contact) and the result of the query is&lt;br /&gt;
  # the cumulated time which the window was open (in seconds) and the name of the device whic is&lt;br /&gt;
  # used by the function SwitchQueryResult()&lt;br /&gt;
  # Example:  AZ_Fensterkontakt|433.2&lt;br /&gt;
  my $sql = &amp;quot;SET \@topen = NULL,\@closed = NULL, \@popen = NULL;;&amp;quot;.&lt;br /&gt;
            &amp;quot; SELECT  DEVICE ,SUM(TIMEDIFF(tclosed, topen)) AS duration FROM (&amp;quot;.&lt;br /&gt;
            &amp;quot; SELECT TIMESTAMP, VALUE, DEVICE,&amp;quot;.&lt;br /&gt;
            &amp;quot; \@topen   := \@popen AS topen,&amp;quot;.&lt;br /&gt;
            &amp;quot; \@closed  := IF(VALUE = $usedVal2,  TIMESTAMP, NULL) AS tclosed,&amp;quot;.&lt;br /&gt;
            &amp;quot; \@popen   := IF(VALUE = $usedVal1, TIMESTAMP, NULL) AS prev_open&amp;quot;.&lt;br /&gt;
            &amp;quot; FROM history WHERE&amp;quot;.&lt;br /&gt;
            &amp;quot; DATE(TIMESTAMP) = CURDATE() AND&amp;quot;.&lt;br /&gt;
            &amp;quot; DEVICE = $device AND&amp;quot;.&lt;br /&gt;
            &amp;quot; READING = $usedReading AND &amp;quot;.&lt;br /&gt;
            &amp;quot; (VALUE = $usedVal1 OR VALUE = $usedVal2)&amp;quot;.&lt;br /&gt;
            &amp;quot; ORDER BY  TIMESTAMP&amp;quot;.&lt;br /&gt;
            &amp;quot; ) AS tmp&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
  Log3 $name , 5, &amp;quot;$name - SwitchEvent start query: $sql&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
  fhem(&amp;quot;set $dbRepDevice sqlCmd $sql&amp;quot;); # start the query now. Result will be processed in the function SwitchQueryResult() below&lt;br /&gt;
 &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;SwitchEvent()&#039;&#039;&#039; bekommt den Namen des Gerätes übergeben welches Event ausgelöst hat. Damit wird eine SQL-Abfrage für dieses Gerät gestartet und ermittelt,  wieviel Zeit (in Sekunden) für das übergebene Gerät zwischen dem Zustand &amp;quot;open&amp;quot; und &amp;quot;closed&amp;quot; verbracht wurde. &lt;br /&gt;
&lt;br /&gt;
Das verwendete DbRep-Device wird wie folgt definiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.WindowOpenTime DbRep LogDBShort&lt;br /&gt;
attr Rep.WindowOpenTime aggregation no&lt;br /&gt;
attr Rep.WindowOpenTime comment Öffnungszeit der Fenster ermitteln&lt;br /&gt;
attr Rep.WindowOpenTime initialized:control_3dot_hor_s connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.WindowOpenTime reading state&lt;br /&gt;
attr Rep.WindowOpenTime showproctime 1&lt;br /&gt;
attr Rep.WindowOpenTime sqlResultFormat sline&lt;br /&gt;
attr Rep.WindowOpenTime event-on-update-reading state&lt;br /&gt;
attr Rep.WindowOpenTime userExitFn SwitchQueryResult&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ganz wichtig ist die Zeile &#039;&#039;&#039;attr Rep.powerOnTime userExitFn SwitchQueryResult&#039;&#039;&#039;. Hier ist die Funktion &#039;&#039;&#039;SwitchQueryResult()&#039;&#039;&#039; angegeben, die nach erfolgreicher Bearbeitung der SQL-Query aufgerufen wird. Diese Funktion wird ebenfalls in der 99_myUtils.pm eingefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
##########################################################################################################&lt;br /&gt;
# This function will be called from the DbRep device if the attribute&lt;br /&gt;
# attr Name_of_your_DbRep_device userExitFn SwitchQueryResult is set.&lt;br /&gt;
# This function create a new reading &amp;quot;LastOpenTime&amp;quot; at the device for which the query is done and&lt;br /&gt;
# set the cumulated time difference in seconds to the reading.&lt;br /&gt;
##########################################################################################################&lt;br /&gt;
sub SwitchQueryResult($$$) {&lt;br /&gt;
   my ($name,$reading,$value) = @_;  # $name is the name of the DbRep device which is calling this function&lt;br /&gt;
   my $hash = $defs{$name};&lt;br /&gt;
 &lt;br /&gt;
   # DbRep calls this function several times with different informations.&lt;br /&gt;
   # if $reading is SqlResult, $value will hold the result of the query&lt;br /&gt;
   if ($reading eq &amp;quot;SqlResult&amp;quot;) {&lt;br /&gt;
       my ($dev,$val) = split(&#039;\|&#039;,$value);                   # split the result in two parts&lt;br /&gt;
       $dev = $dev?$dev:&amp;quot;&amp;quot;;&lt;br /&gt;
       $val = $val?$val:&amp;quot;&amp;quot;;&lt;br /&gt;
       if (!$val) {&lt;br /&gt;
           $val = 0;&lt;br /&gt;
       }&lt;br /&gt;
       $val = DbRep_sec2hms($val);                            # calculate seconds to hms&lt;br /&gt;
       Log3 $name , 5, &amp;quot;DbRep $name - SwitchQueryResult splitted result: $dev $val&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
       fhem(&amp;quot;setreading $dev LastOpenTime $val&amp;quot;) if($dev);    # set the reading &amp;quot;LastOpenTime&amp;quot; with the time if device-Log was found&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion SwitchQueryResult() rechnet das Ergebnis der query aus dem DbRep Device von Sekunden in das Format hh:mm:ss um und trägt die berechnete Zeit als Reading &amp;quot;&#039;&#039;&#039;LastOpenTime&#039;&#039;&#039;&amp;quot; im jeweiligen Fensterkontakt ein.&lt;br /&gt;
&lt;br /&gt;
So kann man z.B. im Device mit dem Namen &amp;quot;eg.bad.fenster&amp;quot; das Reading &amp;quot;&#039;&#039;&#039;LastOpenTime 00:03:58&#039;&#039;&#039;&amp;quot; finden.&lt;br /&gt;
Die vorliegende Berechnung liefert immer die Zeit des letzten Öffnungszyklus des aktuellen Tages. Möchte man die Zeiten des gesamten Tages akkumulieren, kann nach der Beschreibung &amp;quot;[[Summe aller Einschaltzeiten eines Gerätes]]&amp;quot; verfahren werden.&lt;br /&gt;
&lt;br /&gt;
=== Grünlandtemperatursumme ermitteln und in SVG-Grafik darstellen ===&lt;br /&gt;
Nachfolgendes Beispiel zeigt, wie die Grünlandtemperatursumme ermittelt werden kann und die Ermittlungsergebnisse in der Datenbank gespeichert werden, um sie in einer SVG-Grafik darzustellen. Das beschriebene Verfahren kann natürlich auch für andere Anwendungsfälle angewendet werden.&lt;br /&gt;
&lt;br /&gt;
Was ist die Grünlandtemperatursumme?&lt;br /&gt;
&lt;br /&gt;
Hier die Definition aus [https://de.wikipedia.org/wiki/Grünlandtemperatursumme Wikipedia]:&lt;br /&gt;
&lt;br /&gt;
: &#039;&#039;Die Grünlandtemperatursumme (GTS) ist eine Spezialform der Wachstumsgradtage, die in der Agrarmeteorologie verwendet wird. Sie wird herangezogen, um in Mitteleuropa den Termin für das Einsetzen der Feldarbeit nach dem Winter zu bestimmen.&#039;&#039;&lt;br /&gt;
: &#039;&#039;Eine Wärmesumme ist allgemein eine gewisse Lufttemperatur eines Tages über die Tage einer Periode summiert. Dabei verwendet man besonders die kumulierte korrigierte GTS, die nach Monat gewichtet wird.&#039;&#039;&lt;br /&gt;
: &#039;&#039;Wird im Frühjahr die Summe von 200 überschritten, ist der nachhaltige Vegetationsbeginn erreicht. Hintergrund ist die Stickstoffaufnahme und -verarbeitung des Bodens, welcher von dieser Temperatursumme abhängig ist. In mittleren Breiten wird das meist im Laufe des März, an der Wende von Vorfrühling zu Mittfrühling erreicht. &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Zur Bestimmung der GTS benötigt man zunächst die Aufzeichnung der örtlichen Temperaturwerte in einer DbLog-Datenbank. &lt;br /&gt;
Im Beispiel wird dazu das Reading &#039;&#039;&#039;temperature&#039;&#039;&#039; des Device &#039;&#039;&#039;MyWetter&#039;&#039;&#039; (Device vom Typ [[Weather]]) genutzt. Die Einrichtung des Weather- und DbLog-Devices wird an dieser Stelle nicht beschrieben und als bekannt vorausgesetzt.&lt;br /&gt;
&lt;br /&gt;
Es wird angenommen, dass:&lt;br /&gt;
* das definierte DbLog-Device im asynchronen Mode betrieben wird &lt;br /&gt;
* das Reading &#039;&#039;&#039;temperature&#039;&#039;&#039; des Device &#039;&#039;&#039;MyWetter&#039;&#039;&#039; in der DbLog-Datenbank gespeichert wird&lt;br /&gt;
&lt;br /&gt;
Zur Ermittlung des GTS wird ein DbRep-Device mit gesetztem [[Attribut]] &#039;&#039;&#039;avgDailyMeanGWSwithGTS&#039;&#039;&#039; verwendet. Dieses Attribut legt die Berechnung der täglichen Durchschnittstemperatur nach den Regularien des Deutschen Wetterdienstes fest und aktiviert auf dieser Grundlage weiterhin die Berechnung der Grünlandtemperatursumme.&lt;br /&gt;
&lt;br /&gt;
[[Datei:gts1.PNG|right|thumb|400px|GTS Ermittlung]]&lt;br /&gt;
Die RAW-Definition des verwendeten DbRep-Devices:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.GTS DbRep LogDB&lt;br /&gt;
attr Rep.GTS averageCalcForm avgDailyMeanGWSwithGTS&lt;br /&gt;
attr Rep.GTS device MyWetter&lt;br /&gt;
attr Rep.GTS fastStart 1&lt;br /&gt;
attr Rep.GTS reading temperature&lt;br /&gt;
attr Rep.GTS room DbLog&lt;br /&gt;
attr Rep.GTS showproctime 1&lt;br /&gt;
attr Rep.GTS timestamp_begin current_year_begin&lt;br /&gt;
attr Rep.GTS timestamp_end previous_day_end&lt;br /&gt;
attr Rep.GTS userExitFn saveGTS&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LogDB&#039;&#039;&#039; ist der Name des angeschlossenen DbLog-Devices und ist natürlich entsprechend anzupassen. Unbedingt wichtig ist es, den Start der Auswertung auf den Beginn des Jahres zu setzen. Das Attribut &#039;&#039;&#039;timestamp_begin = current_year_begin&#039;&#039;&#039; erfüllt diese Bedingung. Das Attribut &#039;&#039;&#039;userExitFn&#039;&#039;&#039; wird noch erläutert.&lt;br /&gt;
&lt;br /&gt;
Die Berechnung wird gestartet mit dem Befehl:&lt;br /&gt;
:&amp;lt;code&amp;gt;set Rep.GTS averageValue display&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Ergebnis entstehen Readings die auszugsweise nachfolgend aufgelistet sind:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-11__MyWetter__temperature__AVGDMGWS__2021-02-11 -5.1&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-11__MyWetter__temperature__GrasslandTemperatureSum 40.1&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-12__MyWetter__temperature__AVGDMGWS__2021-02-12 -6.8&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-12__MyWetter__temperature__GrasslandTemperatureSum 40.1&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-13__MyWetter__temperature__AVGDMGWS__2021-02-13 insufficient values - execute get Rep.GTS versionNotes 2 for further information&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-14__MyWetter__temperature__AVGDMGWS__2021-02-14 -8.2&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-14__MyWetter__temperature__GrasslandTemperatureSum 40.1&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-15__MyWetter__temperature__AVGDMGWS__2021-02-15 -3.2&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-15__MyWetter__temperature__GrasslandTemperatureSum 40.1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Meldung &amp;quot;insufficient values - execute get Rep.GTS versionNotes 2 for further information&amp;quot; ist ein Hinweis darauf, dass die für die Ermittlung der Durchschnittstagestemperaturen nach den Regularien des Deutschen Wettederdienstes geforderten Werte nicht (komplett) in der DB vorhanden sind.&lt;br /&gt;
&lt;br /&gt;
Um die Grünlandtemperatursumme im SVG-Diagramm anzuzeigen, muss dieser Wert für jeden Tag in der DB gespeichert werden. Alle benötigten Informationen sind in den DbRep-Readings, z.B.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2021-02-12__MyWetter__temperature__GrasslandTemperatureSum 40.1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
vorhanden. Der Präfix enthält das Datum, für das die jeweilige Grünlandtemperatursumme gilt, d.h., es muß nur noch ein neuer Wert zu diesem korrespondierenden Datum in die DB geschrieben werden. Das wird durch eine kleine Routine in 99_myUtils.pm erreicht, die im Attribut &#039;&#039;&#039;userExitFn&#039;&#039;&#039; des DbRep-Devices hinterlegt wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Routine in 99_myUtils.pm einfügen:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
##############################################################&lt;br /&gt;
#              speichern GTS in Datenbank&lt;br /&gt;
##############################################################&lt;br /&gt;
sub saveGTS {&lt;br /&gt;
  my $name    = shift;&lt;br /&gt;
  my $reading = shift;&lt;br /&gt;
  my $value   = shift;&lt;br /&gt;
  my $device  = &amp;quot;MyWetter&amp;quot;;  # Weather-Device -&amp;gt; anpassen !&lt;br /&gt;
  my $logdev  = &amp;quot;LogDB&amp;quot;;     # DbLOg Device   -&amp;gt; anpassen !&lt;br /&gt;
  &lt;br /&gt;
  # 2021-02-14__MyWetter__temperature__GrasslandTemperatureSum 40.1&lt;br /&gt;
  if($reading =~ /GrasslandTemperatureSum/x) {&lt;br /&gt;
      my ($date)    = (split &amp;quot;__&amp;quot;, $reading)[0];&lt;br /&gt;
	  my ($y,$m,$d) = split &amp;quot;-&amp;quot;, $date;&lt;br /&gt;
	  my $ts        = &amp;quot;$y-$m-$d 12:00:00&amp;quot;;&lt;br /&gt;
	  &lt;br /&gt;
      CommandSet(undef, &amp;quot;$logdev addCacheLine $ts|$device|calculated|calculated|GrasslandTemperatureSum|$value|&amp;quot;);  &lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Routine sind die Variablen &#039;&#039;&#039;$device&#039;&#039;&#039; und &#039;&#039;&#039;$logdev&#039;&#039;&#039; mit den realen Devices Weather und DbLog zu ersetzen.&lt;br /&gt;
&lt;br /&gt;
;Funktionsweise&lt;br /&gt;
:Ist der Auswertungslauf beendet, werden alle Readings, die &amp;quot;GrasslandTemperatureSum&amp;quot; enthalten, in ihre Bestandteile aufgesplittet, der Timestamp zum Speichern in der Datenbank formatiert und der neu in der DB zu erstellende Datensatz in den Cache des DbLog-Devices eingefügt. Der nächste Sync-Lauf in DbLog fügt die im Cache erstellten Datensätze in die Datenbank ein.&lt;br /&gt;
&lt;br /&gt;
==== Definition des SVG-Devices ====&lt;br /&gt;
[[Datei:gts2.PNG|right|thumb|400px|GTS SVG Darstellung]]&lt;br /&gt;
In der Datenbank befinden sich nun Datensätze mit dem &#039;&#039;&#039;Device MyWetter&#039;&#039;&#039; und dem &#039;&#039;&#039;Reading GrasslandTemperatureSum&#039;&#039;&#039; die im SVG Diagramm ausgewertet werden können.&lt;br /&gt;
&lt;br /&gt;
RAW-Definition des SVG-Devices:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod SVG_GTS SVG LogDB:SVG_GTS:HISTORY&lt;br /&gt;
attr SVG_GTS fixedrange month&lt;br /&gt;
attr SVG_GTS label &amp;quot;aktuelle Grünlandtemperatur $data{max1}&amp;quot;&lt;br /&gt;
attr SVG_GTS room SVG&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die dazugehörige GPLOT-Datei (SVG_GTS.gplot):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2021-02-27 08:08:30&lt;br /&gt;
set terminal png transparent size &amp;lt;SIZE&amp;gt; crop&lt;br /&gt;
set output &#039;&amp;lt;OUT&amp;gt;.png&#039;&lt;br /&gt;
set xdata time&lt;br /&gt;
set timefmt &amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;&lt;br /&gt;
set xlabel &amp;quot; &amp;quot;&lt;br /&gt;
set title &#039;&amp;lt;L1&amp;gt;&#039;&lt;br /&gt;
set ytics &lt;br /&gt;
set y2tics &lt;br /&gt;
set grid&lt;br /&gt;
set ylabel &amp;quot;Score&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Score&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#LogDB MyWetter:GrasslandTemperatureSum::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;GTS&#039; ls l1fill lw 1 with ibars&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Logging]]&lt;/div&gt;</summary>
		<author><name>Dora71</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten&amp;diff=39337</id>
		<title>DbRep - Reporting und Management von DbLog-Datenbankinhalten</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten&amp;diff=39337"/>
		<updated>2024-05-26T08:41:13Z</updated>

		<summary type="html">&lt;p&gt;Dora71: /* (regelmäßiges) Löschen von Datenbanksätzen */ Typo behoben Datenbankdevice angepasst&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Reporting und Management von DbLog-Datenbankinhalten&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModCmdRef=DbRep&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbRep.pm&lt;br /&gt;
|ModOwner=DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS Starter|Wiki]])}}&lt;br /&gt;
&lt;br /&gt;
== Zweck und Einsatz des Moduls ==&lt;br /&gt;
Zweck des Moduls ist es, den Inhalt von [[DbLog]]-Datenbanken nach bestimmten Kriterien zu durchsuchen, zu managen, das Ergebnis hinsichtlich verschiedener Aggregationen auszuwerten und als Readings darzustellen. Die Abgrenzung der zu berücksichtigenden Datenbankinhalte erfolgt durch die Angabe von Device, Reading und die Zeitgrenzen für Auswertungsbeginn bzw. Auswertungsende.&lt;br /&gt;
&lt;br /&gt;
Alle Datenbankoperationen werden nichtblockierend ausgeführt. Die Ausführungszeit der (SQL)-Hintergrundoperationen kann optional ebenfalls als Reading bereitgestellt werden (siehe Attribute).&lt;br /&gt;
Alle vorhandenen Readings werden vor einer neuen Operation gelöscht. Durch das Attribut &amp;quot;readingPreventFromDel&amp;quot; kann eine Komma separierte Liste von Readings angegeben werden die nicht gelöscht werden sollen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Zur Zeit ist das Modul durch folgende Leistungsmerkmale gekennzeichnet:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Selektion und Anzeige aller Datensätze innerhalb einstellbarer Zeitgrenzen.&lt;br /&gt;
* Darstellung der Datensätze einer Device/Reading-Kombination innerhalb einstellbarer Zeitgrenzen.&lt;br /&gt;
* Selektion der Datensätze unter Verwendung von dynamisch berechneter Zeitgrenzen zum Ausführungszeitpunkt.&lt;br /&gt;
* Dubletten-Hervorhebung bei Datensatzanzeige (fetchrows) &lt;br /&gt;
* Berechnung der Anzahl von Datensätzen einer Device/Reading-Kombination unter Berücksichtigung von Zeitgrenzen und verschiedenen Aggregationen.&lt;br /&gt;
* Die Berechnung von Summen-, Differenz-, Maximum-, Minimum- und Durchschnittswerten numerischer Readings in Zeitgrenzen und verschiedenen Aggregationen.&lt;br /&gt;
* Speichern von Summen-, Differenz- , Maximum- , Minimum- und Durchschnittswertberechnungen in der Datenbank&lt;br /&gt;
* Löschung von Datensätzen. Die Eingrenzung der Löschung kann durch Device und/oder Reading sowie fixer oder dynamisch berechneter Zeitgrenzen zum Ausführungszeitpunkt erfolgen.&lt;br /&gt;
* Export von Datensätzen in ein File im CSV-Format. &lt;br /&gt;
* Import von Datensätzen aus File im CSV-Format.&lt;br /&gt;
* Umbenennen von Device/Readings in Datenbanksätzen &lt;br /&gt;
* Änderung von gespeicherten Reading-Werten (VALUES) in der Datenbank (changeValue)  &lt;br /&gt;
* automatisches Umbenennen (Autorename) von Device-Namen in Datenbanksätzen und DbRep-Definitionen nach FHEM &amp;quot;rename&amp;quot; Befehl (siehe [[#DbRep_Agent_-_automatisches_.C3.84ndern_von_Device-Namen_in_Datenbanken_und_DbRep-Definitionen_nach_FHEM_.22rename.22 | DbRep-Agent]]) &lt;br /&gt;
* Ausführen von beliebigen benutzerspezifischen SQL-Kommandos &lt;br /&gt;
* Backups der FHEM-Datenbank im laufenden Betrieb erstellen (MySQL, SQLite) mit/ohne Komprimierung der Dumpfiles&lt;br /&gt;
* senden und versionieren Dumpfiles nach dem Backup an einen FTP-Server&lt;br /&gt;
* Restore von SQLite-Dumps und MySQL serverSide-Backups &lt;br /&gt;
* Optimierung der angeschlossenen Datenbank (optimizeTables, vacuum) &lt;br /&gt;
* Ausgabe der existierenden Datenbankprozesse (MySQL) &lt;br /&gt;
* leeren der current-Tabelle&lt;br /&gt;
* Auffüllen der current-Tabelle mit einem (einstellbaren) Extrakt der history-Tabelle&lt;br /&gt;
* Bereinigung sequentiell aufeinander folgender Datensätze (sequentielle Dublettenbereinigung) &lt;br /&gt;
* Reparatur einer korrupten SQLite Datenbank (&amp;quot;database disk image is malformed&amp;quot;)&lt;br /&gt;
* Übertragung von Datensätzen aus der Quelldatenbank in eine andere (Standby) Datenbank (syncStandby)&lt;br /&gt;
* Reduktion der Anzahl von Datensätzen in der Datenbank (reduceLog)&lt;br /&gt;
* Löschen von doppelten Datensätzen (delDoublets)&lt;br /&gt;
* Löschen und (Wieder)anlegen der für DbLog und DbRep benötigten Indizes (index) &lt;br /&gt;
&lt;br /&gt;
Zur Aktivierung der Funktion &amp;quot;Autorename&amp;quot; wird dem definierten DbRep-Device mit dem Attribut &amp;quot;role&amp;quot; die Rolle &amp;quot;Agent&amp;quot; zugewiesen. Die Standardrolle nach Definition ist &amp;quot;Client&amp;quot;. Mehr ist dazu im Abschnitt [[#DbRep_Agent_-_automatisches_.C3.84ndern_von_Device-Namen_in_Datenbanken_und_DbRep-Definitionen_nach_FHEM_.22rename.22 | DbRep-Agent]] beschrieben. &lt;br /&gt;
&lt;br /&gt;
DbRep stellt dem Nutzer einen UserExit zur Verfügung. Über diese Schnittstelle kann der Nutzer in Abhängigkeit von frei definierbaren Reading/Value-Kombinationen (Regex) eigenen Code zur Ausführung bringen. Diese Schnittstelle arbeitet unabhängig von einer Eventgenerierung. Weitere Informationen dazu ist unter [[#Attribute | Attribut]] &amp;quot;userExitFn&amp;quot; beschrieben.&lt;br /&gt;
&lt;br /&gt;
FHEM-Forum:&lt;br /&gt;
{{Link2Forum|Topic=53584|Message=452567|LinkText=Modul 93_DbRep - Reporting und Management von Datenbankinhalten (DbLog)}}&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen und Abgrenzungen ==&lt;br /&gt;
Das Modul setzt den Einsatz einer oder mehrerer DBLog-Instanzen voraus (bisher getestet mit PostgreSQL, MySQL und SQLite). Es werden die Zugangsdaten dieser Datenbankdefinition aus der Konfiguration des entsprechenden DbLog-Device genutzt.&lt;br /&gt;
Es werden nur Inhalte der Tabelle &amp;quot;history&amp;quot; berücksichtigt (Ausnahme Kommando &amp;quot;sqlCmd&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Überblick, welche anderen Perl-Module DbRep verwendet:&lt;br /&gt;
&lt;br /&gt;
::POSIX&lt;br /&gt;
::Time::HiRes&lt;br /&gt;
::Time::Local&lt;br /&gt;
::Scalar::Util&lt;br /&gt;
::DBI&lt;br /&gt;
::Blocking (FHEM-Modul)&lt;br /&gt;
::Encode&lt;br /&gt;
&lt;br /&gt;
== Definition ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 define &amp;lt;name&amp;gt; DbRep &amp;lt;Name der DbLog-instanz&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(*) &#039;&#039;&#039;&amp;lt;Name der DbLog-Instanz&amp;gt;:&#039;&#039;&#039; Es wird der Name der auszuwertenden DbLog-Datenbankdefinition angegeben, &#039;&#039;&#039;NICHT&#039;&#039;&#039; die Datenbank selbst.&lt;br /&gt;
&lt;br /&gt;
Aus Performancegründen sollte der Index &amp;quot;Report_Idx&amp;quot; in der Datenbank (Tabelle history) angelegt sein. Ab der DbRep-Version 8.20.0 kann dieser Index, sowie die für DbLog benötigten Indizes, verwaltet werden.&lt;br /&gt;
Der Index &amp;quot;Report_Idx&amp;quot; wird einfach mit dem DbRep-Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 set &amp;lt;name&amp;gt; index recreate_Report_Idx &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
auf der Datenbank angelegt. Ist er bereits vorhanden, wird er gelöscht und erneut angelegt.&lt;br /&gt;
&lt;br /&gt;
== Set ==&lt;br /&gt;
Siehe {{Link2CmdRef|Lang=de|Anker=DbRep-set}}&lt;br /&gt;
&lt;br /&gt;
== Get ==&lt;br /&gt;
Siehe {{Link2CmdRef|Lang=de|Anker=DbRep-get}}&lt;br /&gt;
&lt;br /&gt;
== Attribute ==  &lt;br /&gt;
Siehe {{Link2CmdRef|Lang=de|Anker=DbRep-attr}}&lt;br /&gt;
&lt;br /&gt;
== DbRep Agent - automatisches Ändern von Device-Namen in Datenbanken und DbRep-Definitionen nach FHEM &amp;quot;rename&amp;quot; ==&lt;br /&gt;
&lt;br /&gt;
Mit dem Attribut &amp;quot;role&amp;quot; wird die Rolle des DbRep-Device festgelegt. Die Standardrolle ist &amp;quot;Client&amp;quot;. Mit der Änderung der Rolle in &amp;quot;Agent&amp;quot; wird das Device veranlasst auf Umbenennungen von Geräten in der FHEM Installation zu reagieren.&lt;br /&gt;
&lt;br /&gt;
Durch den DbRep-Agenten werden folgende Features aktiviert wenn ein Gerät in FHEM mit &amp;quot;rename&amp;quot; umbenannt wird: &lt;br /&gt;
&lt;br /&gt;
*  in der dem DbRep-Agenten zugeordneten Datenbank (Internal Database) wird nach Datensätzen mit dem alten Gerätenamen gesucht und dieser Gerätename in allen betroffenen Datensätzen in den neuen Namen geändert.&lt;br /&gt;
&lt;br /&gt;
* in dem DbRep-Agenten zugeordneten DbLog-Device wird in der Definition das alte durch das umbenannte Device ersetzt. Dadurch erfolgt ein weiteres Logging des umbenannten Device in der Datenbank. &lt;br /&gt;
&lt;br /&gt;
* in den existierenden DbRep-Definitionen vom Typ &amp;quot;Client&amp;quot; wird ein evtl. gesetztes Attribut &amp;quot;device = alter Devicename&amp;quot; in &amp;quot;device = neuer Devicename&amp;quot; geändert. Dadurch werden Auswertungsdefinitionen bei Geräteumbenennungen automatisch konsistent gehalten. &lt;br /&gt;
&lt;br /&gt;
Mit der Änderung in einen Agenten sind folgende Restriktionen verbunden, die mit dem Setzen des Attributes &amp;quot;role = Agent&amp;quot; eingeschaltet und geprüft werden: &lt;br /&gt;
&lt;br /&gt;
*  es kann nur einen Agenten pro Datenbank in der FHEM-Installation geben. Ist mehr als eine Datenbank mit DbLog definiert, können ebenso viele DbRep-Agenten eingerichtet werden&lt;br /&gt;
&lt;br /&gt;
* mit der Umwandlung in einen Agenten wird nur noch das Set-Komando &amp;quot;renameDevice&amp;quot; verfügbar sein sowie nur ein eingeschränkter Satz von DbRep-spezifischen Attributen zugelassen. Wird ein DbRep-Device vom bisherigen Typ &amp;quot;Client&amp;quot; in einen Agenten geändert, werden evtl. gesetzte und nun nicht mehr zugelassene Attribute glöscht. &lt;br /&gt;
&lt;br /&gt;
Die Aktivitäten wie Datenbankänderungen bzw. Änderungen an anderen DbRep-Definitionen werden im Logfile mit verbose=3 protokolliert. Damit die renameDevice-Funktion bei großen Datenbanken nicht in ein timeout läuft, sollte das Attribut &amp;quot;timeout&amp;quot; entsprechend dimensioniert werden. Wie alle Datenbankoperationen des Moduls wird auch das Autorename nonblocking ausgeführt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel&#039;&#039;&#039; für die Definition eines DbRep-Device als Agent:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.Agent DbRep LogDB&lt;br /&gt;
attr Rep.Agent devStateIcon connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.Agent icon security&lt;br /&gt;
attr Rep.Agent role Agent&lt;br /&gt;
attr Rep.Agent room DbLog&lt;br /&gt;
attr Rep.Agent showproctime 1&lt;br /&gt;
attr Rep.Agent stateFormat { ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;state&amp;quot;, undef) eq &amp;quot;running&amp;quot; ? &amp;quot;renaming&amp;quot; : ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;state&amp;quot;, undef). &amp;quot; » ProcTime: &amp;quot;.ReadingsVal(&amp;quot;$name&amp;quot;,&amp;quot;sql_processing_time&amp;quot;, undef).&amp;quot; sec&amp;quot;}&lt;br /&gt;
attr Rep.Agent timeout 86400&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== hilfreiche SQL Statements ==&lt;br /&gt;
&lt;br /&gt;
In dieser Rubrik werden SQL-Statements zusammengetragen, die User für ihre Auswertungen hilfreich fanden und anderen Anwendern zur Verfügung stellen.&lt;br /&gt;
&lt;br /&gt;
Im Folgenden wird MySQL auch stellvertretend für MariaDB verwendet.&lt;br /&gt;
&lt;br /&gt;
Die Statements können mit dem Befehl:&lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;DbRep&amp;gt; sqlCmd &amp;lt;SQL-Statement&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ausgeführt werden. Für die Anpassung der Ergebnisdarstellung ist das Attribut &#039;&#039;&#039;sqlResultFormat&#039;&#039;&#039; verfügbar.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis: &amp;lt;br&amp;gt; &#039;&#039;&#039;&lt;br /&gt;
Die Statements sind durch den DbRep-Modulautor nicht in jedem Fall getestet und dem Anwender obliegt vor Anwendung der Statements eine Datenbanksicherung durchzuführen um im Fehlerfall diese wieder herstellen zu können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Den ersten und den letzten Wert eines Zeitraums selektieren bzw. deren Differenz (MySQL) ===&lt;br /&gt;
Beispiel 1:&lt;br /&gt;
&lt;br /&gt;
Den ersten und letzten Wert der durch die DB-Variablen bestimmten Parameter ausgegeben. &lt;br /&gt;
&lt;br /&gt;
Verwendete DB-Variablen (@) : begin_time, end_time, device und reading&lt;br /&gt;
 SET @begin_time=&#039;2020-06-02 12:30:00&#039;;SET @end_time=&#039;2020-06-02 18:00:00&#039;;&lt;br /&gt;
 SET @device=&#039;shelly02&#039;;SET @reading=&#039;energy_0&#039;;&lt;br /&gt;
 &lt;br /&gt;
 SELECT TIMESTAMP,READING,round(VALUE/1000,0) AS VALUE&lt;br /&gt;
    FROM (&lt;br /&gt;
      (SELECT TIMESTAMP,READING,VALUE FROM history WHERE DEVICE = @device AND READING = @reading AND TIMESTAMP &amp;gt;= @begin_time and TIMESTAMP &amp;lt;= @end_time ORDER BY TIMESTAMP LIMIT 1)&lt;br /&gt;
      UNION ALL &lt;br /&gt;
      (SELECT TIMESTAMP,READING,VALUE FROM history WHERE DEVICE = @device AND READING = @reading AND TIMESTAMP &amp;gt;= @begin_time and TIMESTAMP &amp;lt;= @end_time ORDER BY TIMESTAMP DESC LIMIT 1)&lt;br /&gt;
   ) AS X1;&lt;br /&gt;
 &lt;br /&gt;
 +---------------------+----------+-------+&lt;br /&gt;
 | TIMESTAMP           | READING  | VALUE |&lt;br /&gt;
 +---------------------+----------+-------+&lt;br /&gt;
 | 2020-06-02 12:31:01 | energy_0 |    69 |&lt;br /&gt;
 | 2020-06-02 16:31:05 | energy_0 |    73 |&lt;br /&gt;
 +---------------------+----------+-------+&lt;br /&gt;
Beispiel 2:&lt;br /&gt;
&lt;br /&gt;
Berechnung der Differenz vom 1. Beispiel&lt;br /&gt;
&lt;br /&gt;
Verwendete DB-Variablen (@) : begin_time, end_time, device und reading&lt;br /&gt;
&lt;br /&gt;
Hierbei ist zu beachten, dass die einzelnen Selects vertauscht wurden um den TIMESTAMP des ersten Select zu bekommen.&lt;br /&gt;
&lt;br /&gt;
Die Subtraktion wurde durch &amp;quot;mal Minus Eins&amp;quot; des kleineren Wertes erreicht.&amp;lt;pre&amp;gt;&lt;br /&gt;
SET @begin_time=&#039;2020-06-02 12:30:00&#039;;SET @end_time=&#039;2020-06-02 18:00:00&#039;;&lt;br /&gt;
SET @device=&#039;shelly02&#039;;SET @reading=&#039;energy_0&#039;;&lt;br /&gt;
&lt;br /&gt;
SELECT TIMESTAMP,READING,round(sum(VALUE)/1000,0) AS VALUE&lt;br /&gt;
   FROM (&lt;br /&gt;
     (SELECT TIMESTAMP,READING,VALUE FROM history WHERE DEVICE = @device AND READING = @reading AND TIMESTAMP &amp;gt;= @begin_time and TIMESTAMP &amp;lt;= @end_time ORDER BY TIMESTAMP DESC LIMIT 1)&lt;br /&gt;
     UNION ALL &lt;br /&gt;
     (SELECT TIMESTAMP,READING,(VALUE * -1) AS VALUE FROM history WHERE DEVICE = @device  AND  READING = @reading AND TIMESTAMP &amp;gt;= @begin_time and TIMESTAMP &amp;lt;= @end_time ORDER BY TIMESTAMP LIMIT 1)&lt;br /&gt;
  ) AS X1;&lt;br /&gt;
&lt;br /&gt;
+---------------------+----------+-------+&lt;br /&gt;
| TIMESTAMP           | READING  | VALUE |&lt;br /&gt;
+---------------------+----------+-------+&lt;br /&gt;
| 2020-06-02 16:31:05 | energy_0 |     4 |&lt;br /&gt;
+---------------------+----------+-------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Formatierung &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 round(sum(VALUE)/1000,0) AS VALUE&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sollte eventuell angepasst werden, da diese natürlich zum zu erwartenden Ergebnis passen muss.&lt;br /&gt;
&lt;br /&gt;
=== Wieviel PV-Leistung wird von einem Starkverbraucher verwendet (MySQL) ===&lt;br /&gt;
Hier wurde versucht aus der Datenbank zu ermitteln, wieviel PV-Leistung für z.B. eine Wärmepumpe übrig bleibt.&lt;br /&gt;
Dabei wurde der grundlegenden Hausverbrauch als erstes in Abzug gebracht und der Rest mit dem Starkverbraucher verrechnet.&lt;br /&gt;
Natürlich kann man solch einen Report nur für einen Verbraucher verwenden, da man ja den Überschuss nur einem zuordnen kann.&lt;br /&gt;
Auch wird hier mit einem Stundendurchschnitt gerechnet, was somit als Näherung angesehen werden muss.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1) Zuerst werden die einzelnen Daten für einen Tag zusammen gesucht&lt;br /&gt;
2) Jeder Datenteil wird mit einem Durchschnitt auf Stundenbasis berechnet&lt;br /&gt;
3) Im JOIN werden alle Datenteile für die relevanten Stunden zusammen geführt. Maßgeblich hierbei ist der Betrieb des Starkverbrauchers&lt;br /&gt;
   zu dem Zeitpunkt wo auch PV-Leistung verfügbar war. Alle anderen Stunden tauchen danach nicht mehr auf. Ist es ein Hybrid WR mit&lt;br /&gt;
   Speicher, dann kann auch in der Nacht eine Unterstützung erfolgen, was im Winter jedoch recht selten sein wird.&lt;br /&gt;
4) Es wird der restliche Hausverbrauch ermittelt. Achtung, der Starkverbraucher ist ein Verbraucher, weshalb die Leistung im Zähler&lt;br /&gt;
   negativ summiert wird. Ein &amp;quot;+consumer_P&amp;quot; ist somit eine Summe mit einem negativen Wert ;-)&lt;br /&gt;
5) Im letzten SELECT werden dann die Daten final aufbereitet und formatiert, auch der prozentuale Anteil der PV-Leistung am gesamten&lt;br /&gt;
   Starkverbrauch wird noch berechnet.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
consumer          = Das Zähler Device des Starkverbrauchers&lt;br /&gt;
consumer_P        = negative Leistungsanzeige in Watt, da es ein Verbraucher ist!&lt;br /&gt;
generator         = Das Wechselrichter Device&lt;br /&gt;
generator_P       = Die AC Ausgangsleistung in Watt&lt;br /&gt;
Home_consumtion_P = Der Hausverbrauch inklusive des Starkverbrauchers in Watt (ebenfalls im generator Device)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die verwendeten Variablen lassen sich im DbRep als Attribut setzen&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlCmdVars	&lt;br /&gt;
SET @date:=&#039;2022-12-07&#039;, @consumer:=&#039;StromZaehler_Heizung&#039;, @consumer_P:=&#039;SMAEM1901401955_Saldo_Wirkleistung&#039;, @generator:=&#039;WR_1&#039;, @generator_P:=&#039;SW_Total_AC_Active_P&#039;, @Home_Consumtion_P:=&#039;SW_Home_own_consumption&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Hier nun das SELECT Statement&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 SET @date:=&#039;2022-12-07&#039;,&lt;br /&gt;
     @consumer:=&#039;StromZaehler_Heizung&#039;, @consumer_P:=&#039;SMAEM1901401955_Saldo_Wirkleistung&#039;,&lt;br /&gt;
     @generator:=&#039;WR_1&#039;, @generator_P:=&#039;SW_Total_AC_Active_P&#039;, @Home_Consumtion_P:=&#039;SW_Home_own_consumption&#039;;&lt;br /&gt;
&lt;br /&gt;
 SELECT&lt;br /&gt;
   X.HOUR,&lt;br /&gt;
   cast(X.generator_P AS decimal(7,2)) AS generator_P,&lt;br /&gt;
   cast(X.Home_Consumption AS decimal(7,2)) AS Home_Consumption,&lt;br /&gt;
   cast(X.PV_after_Home_Consumtion AS decimal(7,2)) AS PV_after_Home_Consumtion,&lt;br /&gt;
   cast(X.consumer_P AS decimal(7,2)) AS consumer_P,&lt;br /&gt;
   if(consumer_P+PV_after_Home_Consumtion &amp;lt;= 0,cast(round(abs(consumer_P+PV_after_Home_Consumtion),2) AS decimal(7,2)),0) AS from_Grid,&lt;br /&gt;
   if(round(consumer_P+PV_after_Home_Consumtion,2) &amp;lt;= 0,round(abs(PV_after_Home_Consumtion*100/consumer_P),0),100) AS Percent&lt;br /&gt;
 FROM (&lt;br /&gt;
   SELECT&lt;br /&gt;
     X1.HOUR,&lt;br /&gt;
     generator_P,&lt;br /&gt;
     round(Home_Consumtion_P+consumer_P,2) AS Home_Consumption,&lt;br /&gt;
     if(Home_Consumtion_P+consumer_P-generator_P &amp;lt; 0,round(abs(Home_Consumtion_P+consumer_P-generator_P),2),0) AS PV_after_Home_Consumtion,&lt;br /&gt;
     consumer_P&lt;br /&gt;
   FROM (&lt;br /&gt;
     SELECT&lt;br /&gt;
       hour(TIMESTAMP) AS HOUR,&lt;br /&gt;
       round(avg(value),2) AS consumer_P&lt;br /&gt;
     FROM history&lt;br /&gt;
     WHERE&lt;br /&gt;
       TIMESTAMP &amp;gt; @date and TIMESTAMP &amp;lt; DATE_ADD(@date,INTERVAL 1 DAY) AND&lt;br /&gt;
       DEVICE = @Consumer AND&lt;br /&gt;
       READING = @consumer_P&lt;br /&gt;
     GROUP BY 1&lt;br /&gt;
     ) X1&lt;br /&gt;
   JOIN (&lt;br /&gt;
     SELECT&lt;br /&gt;
       hour(TIMESTAMP) AS HOUR,&lt;br /&gt;
       round(avg(value),2) AS generator_P&lt;br /&gt;
     FROM history&lt;br /&gt;
     WHERE&lt;br /&gt;
       TIMESTAMP &amp;gt; @date AND TIMESTAMP &amp;lt; DATE_ADD(@date,INTERVAL 1 DAY) AND&lt;br /&gt;
       DEVICE    = @generator AND&lt;br /&gt;
       READING   = @generator_P AND&lt;br /&gt;
       VALUE     &amp;gt; 10&lt;br /&gt;
     GROUP BY 1&lt;br /&gt;
     ) X2&lt;br /&gt;
   JOIN (&lt;br /&gt;
     SELECT&lt;br /&gt;
       hour(TIMESTAMP) AS HOUR,&lt;br /&gt;
       round(avg(value),2) AS Home_Consumtion_P&lt;br /&gt;
     FROM history&lt;br /&gt;
     WHERE&lt;br /&gt;
       TIMESTAMP &amp;gt; @date and TIMESTAMP &amp;lt; DATE_ADD(@date,INTERVAL 1 DAY) AND&lt;br /&gt;
       DEVICE  = @generator AND&lt;br /&gt;
       READING = @Home_Consumtion_P AND&lt;br /&gt;
       VALUE   &amp;gt; 10&lt;br /&gt;
     GROUP BY 1&lt;br /&gt;
     ) X3&lt;br /&gt;
   ON    X1.HOUR = X2.HOUR&lt;br /&gt;
     AND X1.HOUR = X3.HOUR&lt;br /&gt;
   ) X;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Temperaturdifferenz eines Pufferspeichers über die Zeit ermitteln (MySQL) ===&lt;br /&gt;
Beispiel 1:&lt;br /&gt;
&lt;br /&gt;
Temperaturdifferenz des Pufferspeichers über die Zeit in Minuten&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;Referenz zum DBRep : set [device] sqlSpecial readingsDifferenceByTimeDelta&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verwendete DB-Variablen (@) :  device und reading, diff und delta werden für Berechnungen benötigt und sind bei erneutem Aufruf zurück zu setzen&lt;br /&gt;
&lt;br /&gt;
Anmerkungen:&lt;br /&gt;
&lt;br /&gt;
- Der Zeitraum wird in diesem Beispiel als die letzten 24 Stunden festgelegt&lt;br /&gt;
&lt;br /&gt;
- Die Zeitdifferenz für die Änderung steht in der Spalte DELTA in Minuten&lt;br /&gt;
&lt;br /&gt;
- In FHEM wurde mit event-on-change-reading ins DBLog geschrieben&lt;br /&gt;
&lt;br /&gt;
- Bei dieser Ausgabe kann man erkennen, dass die Temperaturänderung im Wärmespeicher oft sehr klein ist, was ja auch so gewollt ist&lt;br /&gt;
&lt;br /&gt;
- Um 14:00 Uhr beginnt die Wärmepumpe das Warmwasser wieder aufzuheizen&amp;lt;pre&amp;gt;&lt;br /&gt;
SET @device=&#039;Heizung&#039;;SET @reading=&#039;hotWaterTemperature&#039;;&lt;br /&gt;
SET @diff=0;SET @delta=NULL;&lt;br /&gt;
&lt;br /&gt;
SELECT t1.TIMESTAMP,t1.READING,t1.VALUE,t1.DIFF,t1.DELTA&lt;br /&gt;
  FROM&lt;br /&gt;
    (&lt;br /&gt;
SELECT TIMESTAMP,READING,VALUE,&lt;br /&gt;
       if(@diff = 0,NULL, cast((VALUE-@diff) AS DECIMAL(3,1))) AS DIFF,&lt;br /&gt;
       @diff:=VALUE                                            AS curr_V,&lt;br /&gt;
       TIMESTAMPDIFF(MINUTE,@delta,TIMESTAMP)                  AS DELTA,&lt;br /&gt;
       @delta:=TIMESTAMP                                       AS curr_T&lt;br /&gt;
  FROM  history&lt;br /&gt;
  WHERE DEVICE     = @device  AND&lt;br /&gt;
        READING    = @reading AND&lt;br /&gt;
        TIMESTAMP &amp;gt;= NOW() - INTERVAL 1 DAY&lt;br /&gt;
  ORDER BY TIMESTAMP&lt;br /&gt;
    ) t1;&lt;br /&gt;
+---------------------+---------------------+-------+------+-------+&lt;br /&gt;
| TIMESTAMP           | READING             | VALUE | DIFF | DELTA |&lt;br /&gt;
+---------------------+---------------------+-------+------+-------+&lt;br /&gt;
| 2020-06-04 10:05:20 | hotWaterTemperature | 46.5  | NULL |  NULL |&lt;br /&gt;
| 2020-06-04 10:35:31 | hotWaterTemperature | 46.4  | -0.1 |    30 |&lt;br /&gt;
| 2020-06-04 11:00:31 | hotWaterTemperature | 46.2  | -0.2 |    25 |&lt;br /&gt;
snip...&lt;br /&gt;
| 2020-06-04 13:50:42 | hotWaterTemperature | 44.5  | -1.0 |     5 |&lt;br /&gt;
| 2020-06-04 13:55:42 | hotWaterTemperature | 44.0  | -0.5 |     5 |&lt;br /&gt;
| 2020-06-04 14:00:42 | hotWaterTemperature | 43.9  | -0.1 |     5 |&lt;br /&gt;
| 2020-06-04 14:10:42 | hotWaterTemperature | 41.7  | -1.8 |     5 |&lt;br /&gt;
snip...&lt;br /&gt;
| 2020-06-04 14:51:05 | hotWaterTemperature | 51.3  |  0.1 |     5 |&lt;br /&gt;
| 2020-06-04 17:01:15 | hotWaterTemperature | 51.2  | -0.1 |   130 |&lt;br /&gt;
snip...&lt;br /&gt;
| 2020-06-04 22:10:30 | hotWaterTemperature | 50.5  | -0.2 |     5 |&lt;br /&gt;
snip...&lt;br /&gt;
| 2020-06-05 11:30:45 | hotWaterTemperature | 46.1  | -0.1 |    25 |&lt;br /&gt;
+---------------------+---------------------+-------+------+-------+&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;Beispiel 2:&lt;br /&gt;
&lt;br /&gt;
Summieren der Temperaturdifferenz auf Stundenbasis&lt;br /&gt;
&lt;br /&gt;
Verwendete DB-Variablen (@) :  device und reading, diff und delta werden für Berechnungen benötigt und sind bei erneutem Aufruf zurück zu setzen&lt;br /&gt;
&lt;br /&gt;
Anmerkungen:&lt;br /&gt;
&lt;br /&gt;
- Der Zeitraum wird in diesem Beispiel als die letzten 24 Stunden festgelegt&lt;br /&gt;
&lt;br /&gt;
- Die Zeitdifferenz wird bei diesem Beispiel nicht ausgegeben&lt;br /&gt;
&lt;br /&gt;
- Alle Differenzen nach der Vollen Stunde werden der nächsten Stunde zugeschlagen&lt;br /&gt;
&lt;br /&gt;
- In FHEM wurde mit event-on-change-reading ins DBLog geschrieben&lt;br /&gt;
&lt;br /&gt;
- Um 14:00 Uhr beginnt die Wärmepumpe das Warmwasser wieder aufzuheizen&lt;br /&gt;
&lt;br /&gt;
- Bei diesem Wärmespeicher sind Temperaturschwankungen im Bereich von Null Komma irgendwas, als Messwert recht ungenau, was im Beispiel 1 zu der Vielzahl an Messwerten geführt hat. Durch Beispiel 2 auf Stundenbasis lässt sich bereits mehr erkennen.  Hier wäre der Wärmepumpeneinsatz um 14:00 Uhr mit dem Pumpenvorlauf (-1.8) und der Lauf der Zirkulationspumpe um 8:00 und 8:30 Uhr zu sehen, was den Wärmespeicher um 1,2 Grad abgekühlt hat.&amp;lt;pre&amp;gt;&lt;br /&gt;
SET @device=&#039;Heizung&#039;;SET @reading=&#039;hotWaterTemperature&#039;;&lt;br /&gt;
SET @diff=0;SET @delta=NULL;&lt;br /&gt;
&lt;br /&gt;
SELECT xTIMESTAMP AS TIMESTAMP,READING,xVALUE AS VALUE,xDIFF AS DIFF&lt;br /&gt;
  FROM&lt;br /&gt;
    (SELECT DATE_FORMAT(DATE_ADD(t1.TIMESTAMP,INTERVAL (IF(MINUTE(t1.TIMESTAMP) &amp;gt; 0, 60, 0)-MINUTE(t1.TIMESTAMP)) MINUTE),&#039;%Y-%m-%d %H:00:00&#039;) AS xTIMESTAMP,&lt;br /&gt;
            t1.READING,&lt;br /&gt;
            round(t1.VALUE) AS xVALUE,&lt;br /&gt;
            sum(t1.DIFF)    AS xDIFF,&lt;br /&gt;
            sum(t1.DELTA)   AS xDELTA&lt;br /&gt;
       FROM&lt;br /&gt;
         (SELECT TIMESTAMP,READING,VALUE,&lt;br /&gt;
                 if(@diff = 0,NULL, cast((VALUE-@diff) AS DECIMAL(3,1))) AS DIFF,&lt;br /&gt;
                 @diff:=VALUE                                            AS curr_V,&lt;br /&gt;
                 TIMESTAMPDIFF(MINUTE,@delta,TIMESTAMP)                  AS DELTA,&lt;br /&gt;
                 @delta:=TIMESTAMP                                       AS curr_T&lt;br /&gt;
            FROM  history&lt;br /&gt;
            WHERE DEVICE     = @device  AND&lt;br /&gt;
                  READING    = @reading AND&lt;br /&gt;
                  TIMESTAMP &amp;gt;= NOW() - INTERVAL 1 DAY&lt;br /&gt;
            ORDER BY TIMESTAMP&lt;br /&gt;
         ) t1&lt;br /&gt;
       GROUP BY xTIMESTAMP WITH ROLLUP) x1&lt;br /&gt;
&lt;br /&gt;
   WHERE xDELTA     IS NOT NULL AND&lt;br /&gt;
         xTIMESTAMP IS NOT NULL;&lt;br /&gt;
+---------------------+---------------------+-------+------+&lt;br /&gt;
| TIMESTAMP           | READING             | VALUE | DIFF |&lt;br /&gt;
+---------------------+---------------------+-------+------+&lt;br /&gt;
| 2020-06-04 12:00:00 | hotWaterTemperature |    46 | -0.1 |&lt;br /&gt;
| 2020-06-04 13:00:00 | hotWaterTemperature |    46 | -0.4 |&lt;br /&gt;
| 2020-06-04 14:00:00 | hotWaterTemperature |    44 | -1.8 |&lt;br /&gt;
| 2020-06-04 15:00:00 | hotWaterTemperature |    51 |  7.4 |&lt;br /&gt;
| 2020-06-04 18:00:00 | hotWaterTemperature |    51 | -0.1 |&lt;br /&gt;
snip...&lt;br /&gt;
| 2020-06-05 07:00:00 | hotWaterTemperature |    48 | -0.3 |&lt;br /&gt;
| 2020-06-05 08:00:00 | hotWaterTemperature |    48 | -0.2 |&lt;br /&gt;
| 2020-06-05 09:00:00 | hotWaterTemperature |    48 | -1.2 |&lt;br /&gt;
| 2020-06-05 10:00:00 | hotWaterTemperature |    47 | -0.3 |&lt;br /&gt;
snip...&lt;br /&gt;
+---------------------+---------------------+-------+------+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Alle aktuellsten readings eines Device im letzten Tagesverlauf (MySQL) ===&lt;br /&gt;
Beispiel :&lt;br /&gt;
&lt;br /&gt;
Verwendete DB-Variablen (@) :  device&lt;br /&gt;
&lt;br /&gt;
Anmerkungen:&lt;br /&gt;
&lt;br /&gt;
- Der Zeitraum wird in diesem Beispiel als die letzten 24 Stunden festgelegt&lt;br /&gt;
&lt;br /&gt;
- In FHEM wurde mit event-on-change-reading ins DBLog geschrieben, wodurch nicht alle readings immer zeitlich in der DB aufeinander folgen oder einige im Zeitraum mehrfach geschrieben wurden. Mit diesem Aufruf erscheinen immer die letzten Aktualisierungen.&lt;br /&gt;
&lt;br /&gt;
- Sollten einige readings nicht erscheinen, so wurden diese vor dem Zeitraum letztmalig aktualisiert. Mit &amp;quot;INTERVAL [n] DAY könnten diese eventuell auch noch gefunden werden. Bitte hier die Laufzeit beachten!&amp;lt;pre&amp;gt;&lt;br /&gt;
SET @device = &#039;Heizung&#039;;&lt;br /&gt;
&lt;br /&gt;
SELECT t1.TIMESTAMP,t1.DEVICE,t1.READING,t1.VALUE&lt;br /&gt;
  FROM history t1&lt;br /&gt;
  INNER JOIN&lt;br /&gt;
   (select max(TIMESTAMP) AS TIMESTAMP,DEVICE,READING&lt;br /&gt;
      from history&lt;br /&gt;
      where DEVICE    = @device and&lt;br /&gt;
            TIMESTAMP &amp;gt; NOW() - INTERVAL 1 DAY&lt;br /&gt;
      group by READING) x&lt;br /&gt;
  ON x.TIMESTAMP = t1.TIMESTAMP AND&lt;br /&gt;
     x.DEVICE    = t1.DEVICE    AND&lt;br /&gt;
     x.READING   = t1.READING;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Device / Reading Daten in eine CSV-Datei exportieren (MySQL) ===&lt;br /&gt;
&lt;br /&gt;
Grundsätzlich gibt es für diesen Zweck das eingebaute Set-Kommando &#039;&#039;&#039;exportToFile&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Eine weitere Möglichkeit ist die Verwendung von spezifischen Datenbankkommandos, die mit Set &#039;&#039;&#039;sqlCmd&#039;&#039;&#039; ausgeführt werden können.&lt;br /&gt;
&lt;br /&gt;
Im Beispiel wird ein CSV File geschrieben, welches die Daten des Devices &#039;SMA_Energymeter&#039; enthält.&lt;br /&gt;
Wichtig ist, nicht zur Trennung von SQL-Befehlen dienende Semikolons durch Verdopplung zu escapen (z.B. im String &amp;quot;...TERMINATED BY &#039;;;&#039;...&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SET @TS = DATE_FORMAT(NOW(),&#039;_%Y_%m_%d&#039;);&lt;br /&gt;
&lt;br /&gt;
SET @FOLDER = &#039;/volume1/ApplicationBackup/&#039;;&lt;br /&gt;
SET @PREFIX = &#039;export&#039;;&lt;br /&gt;
SET @EXT    = &#039;.csv&#039;;&lt;br /&gt;
&lt;br /&gt;
SET @CMD = CONCAT(&amp;quot;	SELECT *&lt;br /&gt;
			FROM `fhemtest`.`history`&lt;br /&gt;
			WHERE `DEVICE`=&#039;SMA_Energymeter&#039; AND TIMESTAMP &amp;gt; DATE_SUB(CURRENT_DATE(),INTERVAL 1 DAY)&lt;br /&gt;
			INTO OUTFILE &#039;&amp;quot;,@FOLDER,@PREFIX,@TS,@EXT,&amp;quot;&#039;&lt;br /&gt;
			FIELDS ENCLOSED BY &#039;\&amp;quot;&#039;&lt;br /&gt;
			TERMINATED BY &#039;;;&#039;&lt;br /&gt;
			ESCAPED BY &#039;\&amp;quot;&#039;&amp;quot;,&amp;quot; &lt;br /&gt;
			LINES TERMINATED BY &#039;\r\n&#039;;;&lt;br /&gt;
		&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
PREPARE statement FROM @CMD;&lt;br /&gt;
&lt;br /&gt;
EXECUTE statement;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039;  der verwendete Datenbank-User benötigt das &#039;&#039;&#039;FILE&#039;&#039;&#039; Recht.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Gewichtete Mittelwerte von Zeitreihen (MySQL) ===&lt;br /&gt;
Zeitreihen in FHEM sind Messwerte (z.B. Temperatur, Stromverbrauch, PV-Produktion ...), die i.A. nicht zu äquidistanten Zeitpunkten ermittelt und protokolliert werden, sondern nur bei Änderung der Messgröße. Für eine Zusammenfassung (&amp;quot;Aggregation&amp;quot;) zum Zwecke statistischer Auswertung oder einfach zur Datenreduktion sollte daher bei einer Mittelwertbildung auf zeitlich gewichtete Mittelung zurückgegriffen werden. Ein interessierender Gesamtzeitraum wird dazu in einzelne Mittelungsintervalle (sogen. &#039;&#039;bins&#039;&#039;) aufgeteilt, für die dann der gewichtete Mittelwert berechnet wird. &lt;br /&gt;
&lt;br /&gt;
Die (non-blocking) DbRep-Funktion &#039;set averageValue&#039; macht, in Verbindung mit den Attributen &#039;aggregation&#039; (Auswahl einer Aggregationsperiode) und &#039;averageCalcForm&#039;=&#039;avgTimeWeightMean&#039; genau das. Dabei hat man als &#039;&#039;bin&#039;&#039;-Länge &#039;minute&#039;, &#039;hour&#039;, &#039;day&#039;, &#039;week&#039;, &#039;month&#039; und &#039;year&#039; zur Verfügung. &lt;br /&gt;
&lt;br /&gt;
Der folgende &#039;&#039;SQL-Query 1&#039;&#039; kennt als &#039;&#039;bin&#039;&#039;-Längen auch Viertelstunden (&#039;qhour&#039;), Vierteltage (&#039;qday&#039;) und Quartal (&#039;qyear&#039;). Die Parameter @sortformat=[&amp;quot;minute&amp;quot;|&amp;quot;qhour&amp;quot;|&amp;quot;hour&amp;quot;|&amp;quot;qday&amp;quot;|&amp;quot;day&amp;quot;|&amp;quot;week&amp;quot;|&amp;quot;month&amp;quot;|&amp;quot;qyear&amp;quot;|&amp;quot;year&amp;quot;] und @weighted=[&amp;quot;yes&amp;quot;|&amp;quot;no&amp;quot;] müssen vor Ausführung des Codes mit dem Attribut sqlCmdVars gesetzt werden, z.B. &lt;br /&gt;
 &amp;lt;small&amp;gt;set &amp;lt;Your_DbRep_device&amp;gt; sqlCmdVars SET @sortformat=&amp;quot;hour&amp;quot;, @weighted=&amp;quot;yes&amp;quot;, @count=&amp;lt;n&amp;gt;;&amp;lt;/small&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-customtoggle-query1 wikia-menu-button&amp;quot;&amp;gt;&#039;&#039;&#039;&#039;&#039;SQL-Query 1&#039;&#039; ein/ausblenden&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot; id=&amp;quot;mw-customcollapsible-query1&amp;gt;&amp;lt;small&amp;gt;&lt;br /&gt;
 SELECT&lt;br /&gt;
   avgtim, &lt;br /&gt;
   CASE @weighted &lt;br /&gt;
     WHEN &amp;quot;yes&amp;quot; THEN &lt;br /&gt;
       CAST((SUM(val * weight)/SUM(weight)) AS DECIMAL(12,4)) &lt;br /&gt;
     ELSE &lt;br /&gt;
       CAST(sum(val) / count(val) AS DECIMAL(12,4)) &lt;br /&gt;
   END AS avrg&lt;br /&gt;
 FROM (&lt;br /&gt;
   SELECT  &lt;br /&gt;
     tim,&lt;br /&gt;
     avgtim,&lt;br /&gt;
     CASE @sortformat &lt;br /&gt;
       WHEN &amp;quot;week&amp;quot; THEN &lt;br /&gt;
         date_format(tim, &amp;quot;%Y-%u 00:00:00&amp;quot;)&lt;br /&gt;
       ELSE &lt;br /&gt;
         avgtim &lt;br /&gt;
     END AS grouptim,&lt;br /&gt;
     CASE&lt;br /&gt;
       WHEN avgtim!=nextavgtim THEN &lt;br /&gt;
         to_seconds(nextavgtim)-to_seconds(tim)&lt;br /&gt;
       WHEN avgtim!=preavgtim THEN &lt;br /&gt;
         to_seconds(nexttim)-to_seconds(avgtim)&lt;br /&gt;
       ELSE &lt;br /&gt;
         to_seconds(nexttim)-to_seconds(tim) &lt;br /&gt;
     END AS weight,  &lt;br /&gt;
     CASE&lt;br /&gt;
       WHEN avgtim!=preavgtim THEN &lt;br /&gt;
         CASE @weighted WHEN &amp;quot;yes&amp;quot;&lt;br /&gt;
           THEN&lt;br /&gt;
             CASE &lt;br /&gt;
               WHEN avgtim!=nextavgtim THEN &lt;br /&gt;
                 (preval*(to_seconds(tim)-to_seconds(avgtim)) + val*(to_seconds(nextavgtim)-to_seconds(tim)))/ &lt;br /&gt;
                   (to_seconds(nextavgtim)-to_seconds(avgtim))&lt;br /&gt;
               ELSE &lt;br /&gt;
                 (preval*(to_seconds(tim)-to_seconds(avgtim)) + val*(to_seconds(nexttim)-to_seconds(tim)))/&lt;br /&gt;
                   (to_seconds(nexttim)-to_seconds(avgtim))&lt;br /&gt;
             END&lt;br /&gt;
           ELSE &lt;br /&gt;
             val&lt;br /&gt;
         END&lt;br /&gt;
       ELSE &lt;br /&gt;
         val&lt;br /&gt;
     END AS val&lt;br /&gt;
   FROM ( &lt;br /&gt;
     SELECT &lt;br /&gt;
       tim, &lt;br /&gt;
       nexttim, &lt;br /&gt;
       val, &lt;br /&gt;
       preval, &lt;br /&gt;
       avgtim,&lt;br /&gt;
       LAG(avgtim) OVER (ORDER BY tim) AS preavgtim,&lt;br /&gt;
       LEAD(avgtim) OVER (ORDER BY tim) AS nextavgtim&lt;br /&gt;
     FROM ( &lt;br /&gt;
       SELECT &lt;br /&gt;
         timestamp AS tim, &lt;br /&gt;
         LEAD(timestamp) OVER (ORDER BY timestamp) AS nexttim, &lt;br /&gt;
         value AS val, &lt;br /&gt;
         LAG(value) OVER (ORDER BY timestamp) AS preval,&lt;br /&gt;
         CASE @sortformat&lt;br /&gt;
           WHEN &amp;quot;minute&amp;quot; THEN &lt;br /&gt;
             date_format(timestamp, &amp;quot;%Y-%m-%d %H:%i:00&amp;quot;) &lt;br /&gt;
           WHEN &amp;quot;qhour&amp;quot;  THEN &lt;br /&gt;
             concat(date_format(timestamp, &amp;quot;%Y-%m-%d %H:&amp;quot;),LPAD(15*(date_format(timestamp,&amp;quot;%i&amp;quot;) div 15),2,&amp;quot;0&amp;quot;),&amp;quot;:00&amp;quot;) &lt;br /&gt;
           WHEN &amp;quot;hour&amp;quot; THEN &lt;br /&gt;
             date_format(timestamp, &amp;quot;%Y-%m-%d %H:00:00&amp;quot;) &lt;br /&gt;
           WHEN &amp;quot;qday&amp;quot; THEN &lt;br /&gt;
             concat(date_format(timestamp, &amp;quot;%Y-%m-%d &amp;quot;),LPAD(6*(date_format(timestamp, &amp;quot;%H&amp;quot;) div 6),2,&amp;quot;0&amp;quot;),&amp;quot;:00:00&amp;quot;)&lt;br /&gt;
           WHEN &amp;quot;day&amp;quot; THEN &lt;br /&gt;
             date_format(timestamp, &amp;quot;%Y-%m-%d 00:00:00&amp;quot;) &lt;br /&gt;
           WHEN &amp;quot;week&amp;quot; THEN &lt;br /&gt;
             date_format(timestamp, &amp;quot;%Y-%m-%d 00:00:00&amp;quot;) &lt;br /&gt;
           WHEN &amp;quot;month&amp;quot; THEN &lt;br /&gt;
             date_format(timestamp, &amp;quot;%Y-%m-01 00:00:00&amp;quot;) &lt;br /&gt;
           WHEN &amp;quot;qyear&amp;quot; THEN &lt;br /&gt;
             concat(date_format(timestamp, &amp;quot;%Y-&amp;quot;),LPAD(3*(date_format(timestamp, &amp;quot;%m&amp;quot;) div 3),2,&amp;quot;0&amp;quot;),&amp;quot;-01 00:00:00&amp;quot;)&lt;br /&gt;
           ELSE &lt;br /&gt;
             date_format(timestamp, &amp;quot;%Y-01-01 00:00:00&amp;quot;)&lt;br /&gt;
         END AS avgtim      &lt;br /&gt;
       FROM &lt;br /&gt;
         history WHERE TIMESTAMP BETWEEN §timestamp_begin§ AND §timestamp_end§ AND §device§ AND §reading§ ORDER BY timestamp &lt;br /&gt;
     ) select3 &lt;br /&gt;
   ) select2&lt;br /&gt;
 ) select1 GROUP BY grouptim&lt;br /&gt;
&amp;lt;/small&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Die Auswahl von &#039;&#039;device&#039;&#039;, &#039;&#039;reading&#039;&#039; und Zeitraum erfolgt über die üblichen Attribute. Es darf nur ein &#039;&#039;device&#039;&#039; und ein &#039;&#039;reading&#039;&#039; gewählt werden. Für den Zeitraum btte nur &#039;&#039;timestamp_begin&#039;&#039; und &#039;&#039;timestamp_end&#039;&#039; nutzen. &lt;br /&gt;
&lt;br /&gt;
Will man keine festen Zeitraster, kann man den Gesamtzeitraum auch einfach in eine Anzahl &#039;&#039;count&#039;&#039; von bins aufteilen. Dafür steht der Parameter @count im obigen sqlCmdVars statement (für Query 1 hat er keine Bedeutung). &lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-customtoggle-query2 wikia-menu-button&amp;quot;&amp;gt;&#039;&#039;&#039;&#039;&#039;SQL-Query 2&#039;&#039; ein/ausblenden&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;mw-collapsible mw-collapsed&amp;quot; id=&amp;quot;mw-customcollapsible-query2&amp;gt;&amp;lt;small&amp;gt;&lt;br /&gt;
 SELECT &lt;br /&gt;
   tim, &lt;br /&gt;
   CASE &lt;br /&gt;
     WHEN @weighted=&amp;quot;yes&amp;quot; THEN &lt;br /&gt;
       CAST(sum(val * (to_seconds(nexttim)-to_seconds(tim))) / sum((to_seconds(nexttim)-to_seconds(tim))) AS DECIMAL(12,4))&lt;br /&gt;
     ELSE CAST(sum(val) / count(val) AS DECIMAL(12,4)) &lt;br /&gt;
   END AS avrg&lt;br /&gt;
 FROM ( &lt;br /&gt;
   SELECT &lt;br /&gt;
     timestamp as tim, &lt;br /&gt;
     value as val, &lt;br /&gt;
     LEAD(timestamp) over (order by timestamp) nexttim, &lt;br /&gt;
     truncate(@count * (to_seconds(timestamp)-to_seconds(§timestamp_begin§)) / (to_seconds(§timestamp_end§)-to_seconds(§timestamp_begin§)),0)/@count as avgtim &lt;br /&gt;
   FROM &lt;br /&gt;
     history WHERE TIMESTAMP BETWEEN §timestamp_begin§ AND §timestamp_end§ AND §device§ AND §reading§  &lt;br /&gt;
 ) select1 group by avgtim&lt;br /&gt;
&amp;lt;/small&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur Ausführung der Queries den Code entweder ins Eingabefeld hinter set sqlCmd (bzw. sqlCmdBlocking) kopieren und dann mit &#039;&#039;set&#039;&#039; ausführen, oder per &#039;&#039;set &amp;lt;Your_DbRep_device&amp;gt; sqlCmd &amp;lt;kopierter Code&amp;gt;&#039;&#039; in der FHEM Kommandozeile ausführen.&lt;br /&gt;
&lt;br /&gt;
Getestet wurden diese MySQL Beispiele mit MariaDB 10.3&lt;br /&gt;
&lt;br /&gt;
Siehe auch im Forum: https://forum.fhem.de/index.php/topic,53584.msg1254036.html#msg1254036&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Praxisbeispiele / Hinweise und Lösungsansätze für verschiedene Aufgaben ==&lt;br /&gt;
=== Definieren eines DbRep-Devices ===&lt;br /&gt;
[[Bild:DbRep_initialized.PNG|right|thumb|400px|]]&lt;br /&gt;
&lt;br /&gt;
Das DbRep-Device wird bei der Definition mit der DbLog-Instanz verbunden, in deren angeschlossener Datenbank später die Auswertungen und Operationen stattfinden sollen. Es ist also nicht die Datenbank selbst, sondern das vorher definierte DbLog-Device anzugeben.&lt;br /&gt;
Die Definition erfolgt z.B. durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.Energy DbRep LogDB       #LogDB ist das zu verbindende DbLog-Device&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der Definition werden die Zugangsdaten aus der DbLog-Instanz gelesen und das DbRep-Device mit der Datenbank verbunden. Nach der Definition ist der Status &amp;quot;initialized&amp;quot;. Die Verbindung zur Datenbank wird mit der ersten abzuarbeitenden Aufgabe hergestellt. Das Verhalten kann mit dem &#039;&#039;&#039;Attribut fastStart&#039;&#039;&#039; beeinflusst werden.&lt;br /&gt;
Zu welcher Datanbank das DbRep-Device sich verbunden hat, zeigt das &#039;&#039;Internal&#039;&#039; DATABASE.&lt;br /&gt;
&lt;br /&gt;
Damit ist das DbRep-Device grundsätzlich einsatzbereit, aber noch nicht praxistauglich. Werden keine weiteren Eingrenzungen angegeben, kann mit dem so definierten Device mit dem Befehl&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Rep.Energy countEntries&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
die gesamte Anzahl der Datensätze in der Datenbank ermittelt werden.&lt;br /&gt;
Für eine weitere Verwendung sind weitere &#039;&#039;Attribute&#039;&#039; zu setzen.&lt;br /&gt;
Um die Funktionen von &amp;quot;Rep.Energy&amp;quot; nur auf z.B. Datensätze in der Datenbank anzuwenden die &amp;quot;STP_5000&amp;quot; im Feld &amp;quot;DEVICE&amp;quot; enthalten, wird das &#039;&#039;&#039;Attribut&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.Energy device STP_5000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt. Weitere Begrenzungen der gerätespezifischen Selektion erfolgt durch das &#039;&#039;&#039;Attribut&#039;&#039;&#039; &amp;quot;reading&amp;quot;. So wird durch&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.Energy reading etotal&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Bild:Rep_configured.PNG|right|thumb|300px|]]&lt;br /&gt;
festgelegt, dass sich die (allermeisten) Operationen auf die Kombination aus dem &amp;quot;device STP_5000&amp;quot; und dem &amp;quot;reading etotal&amp;quot; beziehen.&lt;br /&gt;
Eine zeitliche Eingrenzung der Ergebnisse erfolgt durch die &#039;&#039;&#039;Attribute&#039;&#039;&#039; &amp;quot;timeDiffToNow&amp;quot;, &amp;quot;timeOlderThan&amp;quot;, &amp;quot;timestamp_begin&amp;quot;, &amp;quot;timestamp_end&amp;quot;.&lt;br /&gt;
In dem Beispiel sollen sich die Selektionsergebnisse immer auf die letzten 120 Minuten beziehen.&lt;br /&gt;
Dazu wird das Attribut&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.Energy timeDiffToNow 7200    #Der Wert für timeDiffToNow ist in Sekunden anzugeben&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt. Es wird dynamisch bei jeder Operation &amp;quot;&amp;lt;Selektionsbeginn&amp;gt; = &amp;lt;aktuelle Zeit&amp;gt; - 3600s&amp;quot; berechnet und die Datensätze bis zu &amp;lt;aktuelle Zeit&amp;gt; berücksichtigt. &lt;br /&gt;
Die gesamten Datenbankoperationen und teilweise auch Auswertungen von Selektionen erfolgt mit Blockingcall im Hintergrund. Der Timeout für die Operationen ist per Default auf 86400 Sekunden gesetzt. Über das &#039;&#039;Attribut&#039;&#039; &amp;quot;timeout&amp;quot; kann es den eigenen Bedingungen angepasst werden. In dem Beispiel wird timeout auf 300s geändert um auch bei sehr großen Selektionen und Auswertungen nicht in einen timeout zu laufen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.Energy timeout 300&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um auch die Verarbeitungszeiten im Hintergrund als Reading anzeigen zu lassen wird mit mit dem &#039;&#039;Attribut&#039;&#039;          &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.Energy showproctime 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
erreicht. &lt;br /&gt;
Mit diesen Einstellungen ist das Device für den konfiguriert und man kann sich zum Beispiel mit&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Rep.Energy fetchrows&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
die Datensätze mit der Gesamtenergierzeugung &amp;quot;etotal&amp;quot; des Wechselrichters &amp;quot;STP_5000&amp;quot; die in den letzten 2 Stunden in die DB geschrieben wurden.&lt;br /&gt;
Nachdem der Befehl in der Gerätedetailsicht ausgeführt wurde, wechselt der state im DeviceOverview auf &amp;quot;running&amp;quot;. Sobald im DeviceOverview &amp;quot;done&amp;quot; angezeigt wird sieht man die Ergebnisse nach einem Browserrefresh.&lt;br /&gt;
Bei der Ausführung einer erneuten Operation werden alle Readings gelöscht. Sollen bestimmte Readings davon ausgenommen werden, kann dem &#039;&#039;Attribut&#039;&#039; eine kommaseparierte Liste von zu schützenden Readings übergeben werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Allgemein wird empfohlen sich für jede Aufgabe eine separates DbRep-Device anzulegen und entsprechend zu konfigurieren anstatt die Einstellungen ständig den neuen Aufgaben anzupassen. &lt;br /&gt;
Um den Prozess zu vereinfachen, kann das einmal angelegte Device für eine neue Selektionsaufgabe (zum Beispiel die Datensätze eines SMA Energymeters anzuzeigen bzw. auszuwerten) auf ein neues DbRep-Device kopiert&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
copy Rep.Energy Rep.SMAMeter&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und entsprechend angepasst werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis zur Eventgenerierung:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Je nach genutzter Funktion können sehr viele Readings als Ergebnis generiert werden. Zum Beispiel können mit &amp;quot;fetchrows&amp;quot;, wobei jedes Reading einer selektierte Zeile aus der Datenbank entspricht, durchaus mehrere hundert Readings entstehen.&lt;br /&gt;
Sollte man nicht durch die Attribute &amp;quot;event-on-update-reading&amp;quot; bzw. &amp;quot;event-on-change-reading&amp;quot; die Eventerzeugung auf nur relevante Readings begrenzt haben, können in diesem Fall ebenso viele Events enstehen die das System entsprechend belasten können. &amp;lt;br&amp;gt;&lt;br /&gt;
Deswegen wird empfohlen die &#039;&#039;&#039;Eventerzeugung sofort&#039;&#039;&#039; auf z.B. &amp;quot;state&amp;quot; zu &#039;&#039;&#039;begrenzen&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Ermittlung des monatlichen und durchschnittlichen Gasverbrauches mit Vaillant und eBusd MQTT ===&lt;br /&gt;
&lt;br /&gt;
Es soll der monatliche Gasverbrauch in m3, kWh und Euro sowie der Durchnschnitt in Euro über die gewählten Monate ermittelt werden.&lt;br /&gt;
&lt;br /&gt;
Die auswertenden Daten liegen als &amp;quot;Tics&amp;quot; vor. Es ist eine stetig steigende Zahl ohne Einheit. Sie wird als Reading aus einem Vaillant eBus-MQTT Device  geloggt. eBusd liefert PrEnergySumHc2 und PrEnergySumHwc1.&lt;br /&gt;
Beide Werte werden als Summe im Reading &#039;&#039;&#039;1_Brenner_Energy_Summe&#039;&#039;&#039; zusammengeführt und geloggt.&lt;br /&gt;
&lt;br /&gt;
[[Bild:Screenshot_2022-12-25_143631.png|right|thumb|400px|]]&lt;br /&gt;
&lt;br /&gt;
Für die Bereitstellung diverser Hilfswerte zur Berechnung existiert ein Dummy-Device &#039;&#039;&#039;VaillantControlDummy&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dieses Device enthält in User-Attributen folgende Werte:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Brennwert_kWh/m3&#039;&#039;&#039; -&amp;gt; den Brennwert in kWh pro m3 lt. Angaben des Lieferanten&lt;br /&gt;
* &#039;&#039;&#039;Zustandszahl&#039;&#039;&#039;     -&amp;gt; die Zustandszahl lt. Angaben des Lieferanten&lt;br /&gt;
* &#039;&#039;&#039;Multiplikator&#039;&#039;&#039;    -&amp;gt; ein Faktor zur Umrechnung der geloggten Tics (1_Brenner_Energy_Summe) in m3&lt;br /&gt;
* &#039;&#039;&#039;Tarif&#039;&#039;&#039;            -&amp;gt; der persönliche Tarif (Brutto) in €/kWh&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Multiplikator wurde empirisch als Quotient aus den real verbrauchten m3 und den Tics über einem längeren Zeitraum ermittelt.&lt;br /&gt;
Das Ergebnis der Multiplikaktion von geloggten Tics und und Multiplikator ergibt die jeweilig verbrauchten m3. Dieser Wert&lt;br /&gt;
ist Toleranz/Fehler behaftet, hat sich aber in der Praxis als hinreichend genau zur Ermittlung des Gasverbrauchs erwiesen.&lt;br /&gt;
&lt;br /&gt;
Das DbRep Device wird wie weiter oben beschrieben angelegt:&lt;br /&gt;
&lt;br /&gt;
 define Rep.gas.allmonths DbRep &amp;lt;DbLog-Devicename&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Bild:Screenshot_2022-12-25_143912.png|right|thumb|400px|]]&lt;br /&gt;
&lt;br /&gt;
Die folgenden Attribute werden gesetzt um den auszuwertenden Zeitraum, sowie das auszuwertende Device und Reading in der Datenbank auszuwerten.&lt;br /&gt;
Die Werte für die Attribute sind natürlich dem persönlichen Umfeld anzupassen:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;device&#039;&#039;&#039; -&amp;gt; auszuwertendes Device (MQTT2_ebusd_bai)&lt;br /&gt;
* &#039;&#039;&#039;reading&#039;&#039;&#039; -&amp;gt; auszuwertendes Reading (1_Brenner_Energy_Summe)&lt;br /&gt;
* &#039;&#039;&#039;timestamp_begin&#039;&#039;&#039; -&amp;gt; Auswertungszeitraum Beginn (2022-10-01 00:00:00)&lt;br /&gt;
* &#039;&#039;&#039;timestamp_end&#039;&#039;&#039; -&amp;gt; Auswertungszeitraum Ende (current_year_end)&lt;br /&gt;
* &#039;&#039;&#039;aggregation&#039;&#039;&#039; -&amp;gt; month, d.h. der Auswertungszeitraum wird in Monatsscheiben aufgeteilt&lt;br /&gt;
* &#039;&#039;&#039;numDecimalPlaces&#039;&#039;&#039; -&amp;gt; die gwünschte Anzahl der Nachkommastellen im Ergebnis (0)&lt;br /&gt;
* &#039;&#039;&#039;diffAccept&#039;&#039;&#039; -&amp;gt; die akzeptierte Differenz von Diffenzwerten (1000000000), den Wert sehr hoch setzen um keine Datensätze von der Berechnung auszuschließen&lt;br /&gt;
* &#039;&#039;&#039;event-on-update-reading&#039;&#039;&#039; -&amp;gt; state,gas_.*&lt;br /&gt;
* &#039;&#039;&#039;eventMap&#039;&#039;&#039; -&amp;gt; /diffValue display:diffValuedisplay/ (für Verwendung in webCmd)&lt;br /&gt;
* &#039;&#039;&#039;webCmd&#039;&#039;&#039; -&amp;gt; diffValuedisplay&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wird das Device mit diesen Einstellungen gestartet, werden Readings der Form:&lt;br /&gt;
&lt;br /&gt;
 2022-10-31_07-05-03__MQTT2_ebusd_bai__1_Brenner_Energy_Summe__DIFF__2022-10&lt;br /&gt;
 2022-11-30_08-38-18__MQTT2_ebusd_bai__1_Brenner_Energy_Summe__DIFF__2022-11&lt;br /&gt;
 2022-12-25_12-29-47__MQTT2_ebusd_bai__1_Brenner_Energy_Summe__DIFF__2022-12&lt;br /&gt;
&lt;br /&gt;
generiert. Sie liefern die Anzahl der Tics im jeweiligen Monat.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Herzstück&amp;quot; zur Ermittlung der Zielwerte ist eine Perl Routine die im Attribut &#039;&#039;&#039;userExitFn&#039;&#039;&#039; hinterlegt wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  if ($READING =~ /1_Brenner_Energy_Summe__DIFF/ &amp;amp;&amp;amp; $VALUE ne &#039;-&#039;) {&lt;br /&gt;
    my $date      = (split &#039;__&#039;, $READING)[0];&lt;br /&gt;
    my ($y,$m,$d) = $date =~ /^(\d{4})-(\d{2})-(\d{2})/x;&lt;br /&gt;
    &lt;br /&gt;
    my $cdd   = &amp;quot;VaillantControlDummy&amp;quot;;                            # Steuerungsdummy Device&lt;br /&gt;
    my $mpk   = AttrVal($cdd,  &#039;Multiplikator&#039;,    &#039;0&#039;);&lt;br /&gt;
    my $zz    = AttrVal($cdd,  &#039;Zustandszahl&#039;,     &#039;0&#039;);           # Zustandszahl lt. Lieferant&lt;br /&gt;
    my $bw    = AttrVal($cdd,  &#039;Brennwert_kWh/m3&#039;, &#039;0&#039;);           # Brennwert kWh/m3 lt. Lieferant&lt;br /&gt;
    my $tarf  = AttrVal($cdd,  &#039;Tarif&#039;,            &#039;0&#039;);           # Kosten €/kWh    &lt;br /&gt;
    my $ndp   = AttrVal($NAME, &#039;numDecimalPlaces&#039;, &#039;3&#039;);&lt;br /&gt;
    my $m3    = sprintf &amp;quot;%.${ndp}f&amp;quot;, $VALUE/10000 * $mpk;          # verbrauchte m3&lt;br /&gt;
    my $kwh   = sprintf &amp;quot;%.${ndp}f&amp;quot;, $m3 * $zz * $bw;              # Umrechnung m3 -&amp;gt; kWh&lt;br /&gt;
    my $cost  = sprintf &amp;quot;%.${ndp}f&amp;quot;, $kwh * $tarf;                 # Kosten = kWh * Tarif&lt;br /&gt;
    my $hash  = $defs{$NAME};&lt;br /&gt;
    &lt;br /&gt;
    readingsBulkUpdate ($hash, $date.&#039;_gas_consumption_m3&#039;,         $m3);&lt;br /&gt;
    readingsBulkUpdate ($hash, $date.&#039;_gas_consumption_kwh&#039;,       $kwh);&lt;br /&gt;
    readingsBulkUpdate ($hash, &#039;gas_cost_euro_&#039;.$y.&#039;-&#039;.$m.&#039;-&#039;.$d, $cost);&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if ($READING eq &#039;state&#039; &amp;amp;&amp;amp; $VALUE eq &#039;done&#039;) {&lt;br /&gt;
      my $n   = 0;&lt;br /&gt;
      my $v   = 0;&lt;br /&gt;
      my $avg = 0;&lt;br /&gt;
	  &lt;br /&gt;
      for my $rdg ( grep { /gas_cost_euro_/x } keys %{$hash-&amp;gt;{READINGS}} ) {&lt;br /&gt;
          $n++;&lt;br /&gt;
          $v += ReadingsNum ($name, $rdg, 0);&lt;br /&gt;
      }&lt;br /&gt;
	  &lt;br /&gt;
      if ($n) {&lt;br /&gt;
          my $ndp3 = AttrVal($NAME, &#039;numDecimalPlaces&#039;, &#039;3&#039;);&lt;br /&gt;
          $avg    = sprintf &amp;quot;%.${ndp3}f&amp;quot;, $v / $n;&lt;br /&gt;
          readingsBulkUpdate ($hash, &#039;gas_cost_euro_average&#039;, $avg);&lt;br /&gt;
      }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald ein Reading erstellt wird welches auf den Regex /1_Brenner_Energy_Summe__DIFF/ matcht, werden die User-Attribute aus dem Steuerungsdummy&lt;br /&gt;
&#039;&#039;VaillantControlDummy&#039;&#039; abgerufen und damit die Berechnung der resultierenden m3 ($m3), kWh ($kwh) und Euro ($cost) durchgeführt.&lt;br /&gt;
Die User-Attribute können natürlich auch im DbRep Device selbst hinterlegt werden. Im vorliegenden Fall wird der Steuerungsdummy noch zu weiteren Aufgabe der Heizungssteuerung verwendet, sodass sich die Verwendung auch für diesen Use Case anbietet.&lt;br /&gt;
&lt;br /&gt;
Es werden die Readings &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;Datum&amp;gt;_&amp;lt;Zeit&amp;gt;_gas_consumption_m3&lt;br /&gt;
* &amp;lt;Datum&amp;gt;_&amp;lt;Zeit&amp;gt;_gas_consumption_kwh&lt;br /&gt;
* gas_cost_euro_&amp;lt;Datum&amp;gt;&lt;br /&gt;
&lt;br /&gt;
erzeugt. &lt;br /&gt;
&lt;br /&gt;
Hat das Device den Report erfolgreich beendet, wird &amp;quot;state&amp;quot; mit dem Wert &amp;quot;done&amp;quot; erzeugt.&lt;br /&gt;
Dieser Wert wird ebenfalls in der Routine überwacht und dann der Durchschnittswert ($avg) aus den ermittelten Monatsergebnissen berechnet.&lt;br /&gt;
Mit dem Ergebnis wird das Reading:&lt;br /&gt;
&lt;br /&gt;
* gas_cost_euro_average &lt;br /&gt;
&lt;br /&gt;
erstellt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zur Gestaltung des Device Overwiew wird im Attribut &#039;&#039;&#039;stateFormat&#039;&#039;&#039; ebenfalls eine Perl Routine hinterlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
 my $state = ReadingsVal($name, &#039;state&#039;,                    &#039;&#039;);&lt;br /&gt;
 my $gca   = ReadingsVal($name, &#039;gas_cost_euro_average&#039;, undef);&lt;br /&gt;
 &lt;br /&gt;
 my $show  = &#039;&#039;;&lt;br /&gt;
 $show    .= defined $gca ? &#039;Durchschnittskosten Gas: &#039;.$gca.&#039; €&#039; : &lt;br /&gt;
             $state;&lt;br /&gt;
 $show    .= &#039;&amp;lt;br&amp;gt;&#039;;&lt;br /&gt;
 $show    .= $state =~ /connected/xs   ? FW_makeImage(&#039;10px-kreis-gelb&#039;)         :&lt;br /&gt;
             $state =~ /initialized/xs ? FW_makeImage(&#039;control_3dot_hor_s&#039;)      :&lt;br /&gt;
             $state =~ /disconnect/xs  ? FW_makeImage(&#039;10px-kreis-rot&#039;)          :&lt;br /&gt;
             $state =~ /done/xs        ? FW_makeImage(&#039;10px-kreis-gruen&#039;)        :&lt;br /&gt;
             $state =~ /Warning/xs     ? FW_makeImage(&#039;info_warning@darkorange&#039;) :&lt;br /&gt;
             $state =~ /running/xs     ? &#039;&#039;                                      :&lt;br /&gt;
             FW_makeImage(&#039;10px-kreis-rot&#039;); &lt;br /&gt;
&lt;br /&gt;
 &amp;quot;&amp;lt;div&amp;gt;&amp;quot;.$show.&amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Datensätze (Devices) von einer Datenbank in eine andere umziehen (Export/Import)===&lt;br /&gt;
==== Zweck ====&lt;br /&gt;
&lt;br /&gt;
Oft ist es so, dass man zunächst eine DbLog-Datenbank anlegt um alle zu loggenden Events dort aufzuzeichnen. Mit zunehmender Größe bzw. mit dem Wunsch nach einer höheren Strukturierung werden weitere DbLog-Instanzen angelegt.&lt;br /&gt;
Die bereits geschriebenen Datensätze eines Gerätes müssen nun in eine andere Datenbank verlagert werden, weil das Device nunmehr in dieser Datenbank geloggt und ausgewertet werden soll.&lt;br /&gt;
&lt;br /&gt;
Für das Beispielszenario gilt folgendes:&lt;br /&gt;
&lt;br /&gt;
* Quelldatenbank aus der das Device entfernt werden soll ist &amp;quot;fhem&amp;quot; mit der DbLog-Instanz &amp;quot;LogDB&amp;quot;&lt;br /&gt;
* Zieldatenbank ist &amp;quot;fhemshort&amp;quot; mit der DbLog-Instanz &amp;quot;LogDBShort&amp;quot;&lt;br /&gt;
* das umzuziehende Device ist &amp;quot;MelderCP1&amp;quot;&lt;br /&gt;
* das definierte DbRep-Device ist &amp;quot;Rep.MelderCP1&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==== Vorbereitung ====&lt;br /&gt;
&lt;br /&gt;
Zunächst wird ein DbRep-Device definiert (falls es noch nicht existiert) und für den Verwendungszweck vorbereitet (zur Definition siehe [[#Definieren_eines_DbRep-Devices | Definieren eines DbRep-Devices]]).&lt;br /&gt;
&lt;br /&gt;
Das Attribut &amp;quot;reading&amp;quot; bzw. eventuell gesetzte Attribute zur Zeiteingrenzung werden gelöscht, damit alle Datensätze des Devices erfasst werden können.&lt;br /&gt;
Für den bevorstehenden Export wird mit dem Attribut &amp;quot;expimpFile&amp;quot; der (beschreibbare) Pfad und Dateiname festgelegt, hier im Beispiel ist es &amp;quot;/media/sf_Backup_FHEM/meldercp1.txt&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Rep_MelderCP1_count.PNG|right|thumb|200px|Anzahl Einträge von MelderCP1]]&lt;br /&gt;
&lt;br /&gt;
Mit &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Rep.MelderCP1 countEntries&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
kann man sich nun einen Überblick verschaffen, wie viele Datensätze in der Quelldatenbank für &amp;quot;MelderCP1&amp;quot; enthalten sind und wie viele auch exportiert werden müssen. In dem Beispiel sind es, wie in dem Screenshot zu sehen, 1144 Datensätze.&lt;br /&gt;
&lt;br /&gt;
Falls noch nicht geschehen, wird in der bisherigen DbLog-Definition das zu loggende Device entfernt und in der neuen DbLog-Definition aufgenommen, damit nun keine neuen Datensätze mehr für &amp;quot;MelderCP1&amp;quot; in die bisherige Datenbank geschrieben werden. Hierzu siehe die {{Link2CmdRef|Lang=de|Anker=DbLog}} zu DbLog.&lt;br /&gt;
&lt;br /&gt;
==== Export ====&lt;br /&gt;
Nun werden die Datensätze des Devices &amp;quot;MelderCP1&amp;quot; mit dem folgenden set-Kommando in die Datei &amp;quot;/media/sf_Backup_FHEM/meldercp1.txt&amp;quot; exportiert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Rep.MelderCP1 exportToFile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach dem Export sollte natürlich im Reading von &amp;quot;Rep.MelderCP1&amp;quot; die gleiche Anzahl von exportierten Datensätzen ausgegeben werden wie vorher mit &amp;quot;countEntries&amp;quot; ermittelt wurde (siehe Screenshot). &lt;br /&gt;
&lt;br /&gt;
[[Datei:Rep_MelderCP1_exported.PNG|right|thumb|300px|Anzahl exportierter Dataensätze]]&lt;br /&gt;
&lt;br /&gt;
In der Export-Datei sind nun die Datensätze im CVS-Format enthalten. &lt;br /&gt;
&lt;br /&gt;
Ein Ausschnitt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
............&lt;br /&gt;
&amp;quot;2016-07-04 17:32:03&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-05 07:47:50&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-05 08:27:57&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-05 08:28:25&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-05 10:23:42&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-05 10:27:35&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-05 17:26:30&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-06 07:46:31&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-06 11:24:04&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-06 11:25:43&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
&amp;quot;2016-07-06 11:26:52&amp;quot;,&amp;quot;MelderCP1&amp;quot;,&amp;quot;FS20&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;on-old-for-timer 60&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
...........&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der bisherigen Datenbank &amp;quot;fhem&amp;quot; können die Datensätze nun gelöscht werden. Dazu versehen wir das DbRep-Device &amp;quot;Rep.MelderCP1&amp;quot; mit dem Attribut &amp;quot;allowDeletion&amp;quot; um die Löschfunktion des Moduls freizuschalten. &lt;br /&gt;
Nun werden die vorher exportierten Datensätze gelöscht mit:  &lt;br /&gt;
&lt;br /&gt;
[[Bild:Rep_MelderCP1_delrows.PNG|right|thumb|300px|Anzahl gelöschter Datensätze]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Rep.MelderCP1 delEntries&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auch jetzt ist wieder sicherzustellen dass in der Ausgabe der gelöschten Rows dieselbe Anzahl (1144) der exportierten Datensätze enthalten ist.&lt;br /&gt;
&lt;br /&gt;
==== Import ====&lt;br /&gt;
Im nächsten Schritt werden die exportierten Daten in die neue Datenbank importiert. Dazu wird in unserem DbRep-Device &amp;quot;Rep.MelderCP1&amp;quot; zunächst das &amp;quot;allowDeletion&amp;quot;-Attribut sicherheitshalber gelöscht. &lt;br /&gt;
Zur Umstellung des DbRep-Devices auf die neue Datenbank wird die Definition von &amp;quot;Rep.MelderCP1&amp;quot; editiert und dort die neue DbLog-Instanz &amp;quot;LogDBShort&amp;quot; angegeben.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
modify Rep.MelderCP1 LogDBShort&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach einem kurzen Moment wird der state von &amp;quot;Rep.MelderCP1&amp;quot; sich wieder von &amp;quot;initialized&amp;quot; auf &amp;quot;connected&amp;quot; ändern sofern die Umstellung funktioniert hat. Mit einem Vergleichslauf durch&lt;br /&gt;
[[Datei:Rep_MelderCP1_Vergleichslauf.PNG|right|thumb|300px|Anzahl Einträge von MelderCP1 in neuer Datenbank nach Umstellung]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Rep.MelderCP1 countEntries&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird ersichtlich, dass sich in dieser Datenbank noch keine (oder vllt. bereits neu geloggte) Einträge von &amp;quot;MelderCP1&amp;quot; befinden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nun kann der Import erfolgen. Mit &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Rep.MelderCP1 importFromFile&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
werden die Daten aus der immer noch im Attribut &amp;quot;expimpFile&amp;quot; hinterlegten Datei &amp;quot;/media/sf_Backup_FHEM/meldercp1.txt&amp;quot; in die Datenbank &amp;quot;fhemshort&amp;quot; importiert. Dieser Vorgang wird als Transaktion ausgeführt. Es werden immer alle Daten oder, im Falle eines Fehlers, &#039;&#039;&#039;KEINE&#039;&#039;&#039; Daten importiert. Damit ist sichergestellt dass der Datenimport immer einen definierten Zustand hat. Sollte der Datenimport sehr umfangreiche Datensätze enthalten und somit schon absehbar sein dass der voreingestellte timeout-Wert von 86400 Sekunden nicht ausreichen wird, kann man vorsorglich das Attribut &amp;quot;timeout&amp;quot; höher setzen, z.B. auf das Doppelte).&lt;br /&gt;
&lt;br /&gt;
Hier in dem Beispiel sind es lediglich 1144 Datensätze die sehr schnell importiert werden. Somit ist keine Anpassung des timeout-Parameters notwendig.&lt;br /&gt;
Auch nach dem Import wird wieder die Anzahl der verarbeiteten Dataensätze ausgegeben. Sie sollte natürlich ebenfalls mit den vorab ermittelten Zahlen der exportierten Datensätze übereinstimmen.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Rep_MelderCP1_importedrows.PNG|right|thumb|300px|Anzahl importierter Datensätze]]&lt;br /&gt;
&lt;br /&gt;
==== Abschluss ====&lt;br /&gt;
&lt;br /&gt;
Nach dem erfolgreichen Import sollte das Attribut &amp;quot;expimpFile&amp;quot; wieder entfernt werden damit man nicht versehentlich einen erneuten Import durchführt.&lt;br /&gt;
Das verwendete Rep.MelderCP1 kann nun wieder mit Zeitbegrenzungsattributen usw. versehen werden um die ursprüngliche Verwendung wieder herzustellen.&lt;br /&gt;
&lt;br /&gt;
Die dargestellte Prozedur stellt einen Komplettumzug eines Devices dar, kann jedoch durch die Angabe der bekannten Attribute auch auf bestimmte Readings in der Datenbank oder Zeitgrenzen eingeschränkt werden. Dadurch würden nur bestimmte Datensätze exportiert und wieder importiert werden. Weiterhin sollte man daran denken die vorhandenen Auswertungsszenarien mit DbRep-Devices bzw. SVG-Diagrammen usw. auch auf die neue Datanbank umzustellen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Inhalte einer primären Datenbank in eine andere Standby-Datenbank übertragen (syncStandby) ===&lt;br /&gt;
&lt;br /&gt;
==== Zweck ====&lt;br /&gt;
&lt;br /&gt;
Mit dem Export/Import Verfahren kann man, wie oben beschrieben, Daten zwischen verschiedenen Datenbanken austauschen. Mit dem Befehl &amp;quot;syncStandby&amp;quot; ist es aber mit relativ wenig Aufwand möglich, ein regelmäßig automatisch laufendes Übertragungsverfahren zwischen zwei Datenbanken zu etablieren. &lt;br /&gt;
&lt;br /&gt;
Im vorliegenden Beispiel werden Datenbanken des gleichen Typs verwendet. Das Verfahren ist aber auch zwischen Datenbanken unterschiedlichen Typs anwendbar.&lt;br /&gt;
&lt;br /&gt;
Anwendungsfälle dafür sind zum Beispiel:&lt;br /&gt;
&lt;br /&gt;
* die Einrichtung eines Archivsystems &lt;br /&gt;
* die längerfristge Datenhaltung von Datensätzen eines bestimmten Devices/Readings in einer weiteren Datenbank um sie dort für umfangreiche Auswertungen zu nutzen (z.B. Daten einer PV-Anlage)&lt;br /&gt;
* Wechsel des Datenbanksystems, z.B. von SQLite zu MySQL/MariaDB&lt;br /&gt;
* säubern der Quelldaten von doppelten Datensätzen und nicht mehr benötigten Daten, Weiternutzung der aufgebauten Standbydatenbank als primäre Datenbank nach der Syncronisierung&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
Für das hier gezeigte Beispielszenario sollen Daten einer MariaDB-Quelldatenbank in einer andere MariaDB-Datenbank regelmäßig übertragen werden. Auf der Quelldatenbank werden die Daten aber nicht gelöscht, d.h. es entstehen zwei Datenbanken mit gleicher Datenbasis.&lt;br /&gt;
&lt;br /&gt;
* Quelldatenbank ist eine MariaDB &amp;quot;fhemtest1&amp;quot; mit der DbLog-Instanz &amp;quot;LogDB1&amp;quot;&lt;br /&gt;
* Zieldatenbank ist eine MariaDB &amp;quot;fhemtest&amp;quot; mit der DbLog-Instanz &amp;quot;LogStby&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Die Quelldatenbank ====&lt;br /&gt;
&lt;br /&gt;
Im vorliegenden Beispiel sind die Quelldatenbank und die Standby-Datenbank vom gleichen Typ MariaDB. Das Verfahren funktioniert ebenso zwischen Datenbanken unterschiedlichen Typs, also z.B. zur Übertragung von SQLite Daten in eine MariaDB.&lt;br /&gt;
&lt;br /&gt;
Die Quelldatenbank ist im normalen Kontext die produktive FHEM-Logdatenbank. D.h. sie ist bereits eingerichtet und läuft mit einem entsprechenden DbLog-Device.&lt;br /&gt;
Die hier verwendete Quelldatenbank heißt &amp;quot;fhemtest1&amp;quot;. Die Definition des dazu gehörenden DbLog-Devices &amp;quot;LogDB1&amp;quot; sei der Vollständigkeit halber hier erwähnt, wird aber natürlich bei jedem Nutzer individuell aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define LogDB1 DbLog ./fhemtest1maria10.conf .*:(?!done).*&lt;br /&gt;
attr LogDB1 DbLogInclude CacheUsage&lt;br /&gt;
attr LogDB1 DbLogSelectionMode Exclude/Include&lt;br /&gt;
attr LogDB1 DbLogType History&lt;br /&gt;
attr LogDB1 addStateEvent 0&lt;br /&gt;
attr LogDB1 asyncMode 1&lt;br /&gt;
attr LogDB1 bulkInsert 1&lt;br /&gt;
attr LogDB1 cacheEvents 2&lt;br /&gt;
attr LogDB1 cacheLimit 2000&lt;br /&gt;
attr LogDB1 dbSchema fhemtest1&lt;br /&gt;
attr LogDB1 devStateIcon .*active:10px-kreis-gelb connected:10px-kreis-gruen .*disconnect:10px-kreis-rot&lt;br /&gt;
attr LogDB1 disable 0&lt;br /&gt;
attr LogDB1 room DbLog&lt;br /&gt;
attr LogDB1 showproctime 1&lt;br /&gt;
attr LogDB1 syncInterval 120&lt;br /&gt;
attr LogDB1 useCharfilter 1&lt;br /&gt;
attr LogDB1 verbose 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Die Standby Datenbank ====&lt;br /&gt;
&lt;br /&gt;
Die Standby Datenbank ist das Synchronisationsziel. &lt;br /&gt;
Die Erstellung der Datenbank und die nachfolgende Definition des dazu gehörigen DbLog-Devices erfolgt weitgehend wie für eine &amp;quot;normale&amp;quot; Log-Datenbank wie in der Commandref beschrieben mit geringfügigen Anpassungen.&lt;br /&gt;
&lt;br /&gt;
; 1. Anlegen der DB und Tabellen: wie in diesem [https://svn.fhem.de/trac/browser/trunk/fhem/contrib/dblog Link] hinterlegt, erfolgt die Erstellung mit den Befehlen  für MySQL:&lt;br /&gt;
::: CREATE DATABASE `fhemtest` DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;&lt;br /&gt;
::: CREATE USER &#039;fhemuser&#039;@&#039;%&#039; IDENTIFIED BY &#039;fhempassword&#039;;&lt;br /&gt;
::: CREATE TABLE `fhemtest`.`history` (TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
::: ALTER TABLE `fhemtest`.`history` ADD PRIMARY KEY(TIMESTAMP, DEVICE, READING);&lt;br /&gt;
::: CREATE TABLE `fhemtest`.`current` (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
::: GRANT SELECT, INSERT, DELETE, UPDATE ON `fhemtest`.* TO &#039;fhemuser&#039;@&#039;%&#039;;&lt;br /&gt;
&lt;br /&gt;
Es wird zusätzlich zu der normalen Erstellung der history-Tabelle ein primary Key für die Tabelle erstellt. Dieser Key verhindert dass Duplikate in der Tabelle erstellt werden und erleichtert dadurch sehr das Verfahren der Datenübertragung in der Folge.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:stb1.PNG|right|thumb|300px|DbLog-Device für Standby-Datenbank]]&lt;br /&gt;
; 2. Erstellung der Konfigurationsdatei und Definition des DbLog-Devices für die Standby-Datenbank: Die Konfigurationsdatei (mariastby.conf) beinhaltet die Verbindunginformationen für das DbLog-Device und wird genau wie in der DbLog-Commandref beschrieben angelegt. Das DbLog-Device für die Standby-Datenbank wird nur für die nachfolgende Definition des benötigten DbRep-Devices gebraucht und wird so definiert, dass keinerlei Events geloggt werden. Das kann auf verschiedenen Wegen erreicht werden. Im Beispiel wird der Regex im DEF entsprechend aufgebaut.&lt;br /&gt;
::: define LogStby DbLog ./mariastby.conf aaaaaa:bbbbbb&lt;br /&gt;
::: attr LogStby DbLogType History&lt;br /&gt;
::: attr LogStby asyncMode 1&lt;br /&gt;
::: attr LogStby bulkInsert 1&lt;br /&gt;
::: attr LogStby cacheEvents 2&lt;br /&gt;
::: attr LogStby devStateIcon .*active:10px-kreis-gelb connected:10px-kreis-gruen .*disconnect:10px-kreis-rot&lt;br /&gt;
::: attr LogStby disable 0&lt;br /&gt;
::: attr LogStby room DbLog&lt;br /&gt;
::: attr LogStby showNotifyTime 1&lt;br /&gt;
::: attr LogStby showproctime 1&lt;br /&gt;
::: attr LogStby syncEvents 1 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Erstellung der current-Tabelle ist für den vorgesehenen Zweck eigentlich nicht nötig, wird der Vollständigkeit halber mit dokumentiert.&lt;br /&gt;
Ist das DbLog-Device definiert und erfolgreich verbunden, ist dessen state &amp;quot;connected&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Definition des DbRep-Devices zur Synchronisation Quell- und Standby-Datenbank ====&lt;br /&gt;
&lt;br /&gt;
Es existieren nun das DbLog Device &amp;quot;LogDB1&amp;quot; für die produktive Quelldatenbank und das DbLog Device &amp;quot;LogStby&amp;quot; für die Standby-Datenbank.&lt;br /&gt;
Für die Synchronisation wird ein DbRep-Device erstellt, welches mit dem DbLog-Device der &#039;&#039;&#039;produktiven Quelldatenbank&#039;&#039;&#039;, also LogDB1, verbunden wird.&lt;br /&gt;
Wie üblich, wird im DEF der Name des entsprechenden DbLog-Devices angegeben, hier &amp;quot;LogDB1&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod Rep.LogDB1.syncStandby DbRep LogDB1&lt;br /&gt;
attr Rep.LogDB1.syncStandby aggregation no&lt;br /&gt;
attr Rep.LogDB1.syncStandby event-on-update-reading state&lt;br /&gt;
attr Rep.LogDB1.syncStandby fastStart 1&lt;br /&gt;
attr Rep.LogDB1.syncStandby role Client&lt;br /&gt;
attr Rep.LogDB1.syncStandby room DbLog&lt;br /&gt;
attr Rep.LogDB1.syncStandby showproctime 1&lt;br /&gt;
attr Rep.LogDB1.syncStandby stateFormat { (ReadingsVal($name,&amp;quot;state&amp;quot;, &amp;quot;&amp;quot;)).&amp;quot; : SQL-Zeit: &amp;quot;.(ReadingsVal($name,&amp;quot;sql_processing_time&amp;quot;, &amp;quot;&amp;quot;)) }&lt;br /&gt;
attr Rep.LogDB1.syncStandby timeDiffToNow d:6&lt;br /&gt;
attr Rep.LogDB1.syncStandby verbose 3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit den verschiedenen möglichen Zeitattributen (&#039;&#039;&#039;time.*&#039;&#039;&#039;) und den Attributen &#039;&#039;&#039;device&#039;&#039;&#039; und &#039;&#039;&#039;reading&#039;&#039;&#039; wird abgegrenzt, welche Datensätze in die Standby-Datenbak übertragen werden sollen. In dem vorliegenden Beispiel werden mit jedem Synchronisationslauf alle in der DB vorhandenen Datensätze, die nicht älter als 6 Tage Tage sind, in die Standby-Datenbank übertragen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Würde man einen solchen Lauf z.B. alle 4 Stunden durchführen, würden viele doppelte Datensätze in der  Datenbank entstehen. Der bei der Tabellenerstellung für die history-Tabelle angelegte primary Key verhindert das !&lt;br /&gt;
[[Datei:stb2.PNG|right|thumb|300px|syncStandby gestartet]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Synchronisationslauf wird nun gestartet mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 set &amp;lt;DbRep-Name&amp;gt; syncStandby &amp;lt;DbLog-Device Standby&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In dem vorliegenden Beispiel ist das:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 set Rep.LogDB1.syncStandby syncStandby LogStby&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:stb3.PNG|right|thumb|300px|syncStandby erfolgreich ausgeführt]]&lt;br /&gt;
Der Fortschritt der Datenübertragung sowie die evtl. eingefügten Datensätze (number of lines inserted) ist im Logfile mit verbose 3 ersichtlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2020.01.24 17:12:41.186 3: DbRep Rep.LogDB1.syncStandby - total lines transfered to standby database: 6177&lt;br /&gt;
2020.01.24 17:12:46.411 3: DbRep Rep.LogDB1.syncStandby - total lines transfered to standby database: 23957&lt;br /&gt;
2020.01.24 17:12:51.710 3: DbRep Rep.LogDB1.syncStandby - total lines transfered to standby database: 24048&lt;br /&gt;
2020.01.24 17:12:57.733 3: DbRep Rep.LogDB1.syncStandby - total lines transfered to standby database: 24092&lt;br /&gt;
2020.01.24 17:13:03.505 3: DbRep Rep.LogDB1.syncStandby - total lines transfered to standby database: 24059&lt;br /&gt;
2020.01.24 17:13:08.891 3: DbRep Rep.LogDB1.syncStandby - total lines transfered to standby database: 23969&lt;br /&gt;
2020.01.24 17:13:13.420 3: DbRep Rep.LogDB1.syncStandby - total lines transfered to standby database: 17871&lt;br /&gt;
2020.01.24 17:13:13.421 3: DbRep Rep.LogDB1.syncStandby - number of lines inserted into &amp;quot;LogStby&amp;quot;: 104&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:stb4.PNG|right|thumb|300px|nur Daten der Devices SMA_Energymeter,MySTP_5000 werden übertragen]]&lt;br /&gt;
Nach dem erfolgreichen Sync-Lauf wird die benötigte Laufzeit und die Anzahl der übertragenen Datensätze in Readings angezeigt.&lt;br /&gt;
&lt;br /&gt;
Mit dem Attribut &#039;&#039;&#039;aggragation&#039;&#039;&#039; wird gesteuert, in welchen &#039;&#039;&#039;Zeitscheiben&#039;&#039;&#039; die Selektion der Daten aus der Quelldatenbank und die Übertragung ausgeführt wird. Im Standard ohne gesetztes aggregation-Attribut ist es ein Tag. Stehen genügend Ressourcen zur Verfügung, kann dieser Wert auch auf &amp;quot;week&amp;quot; oder &amp;quot;month&amp;quot; gesetzt werden. Der Wert &amp;quot;hour&amp;quot; zur Verkleinerung des Datenpaketes ist ebenfalls möglich. &lt;br /&gt;
&lt;br /&gt;
Bei jedem Lauf wird die Anzahl der eingefügten Datensätze im Reading &#039;&#039;&#039;number_lines_inserted_Standby&#039;&#039;&#039; angezeigt.&lt;br /&gt;
&lt;br /&gt;
Die zu übertragenden Daten können natürlich sehr granular bestimmt werden. Sollen zum Beispiel nur die Daten der Devices SMA_Energymeter und MySTP_5000 übertragen werden, wird das Attribut &#039;&#039;&#039;device&#039;&#039;&#039; entsprechend gesetzt:&lt;br /&gt;
&lt;br /&gt;
 attr Rep.LogDB1.syncStandby SMA_Energymeter,MySTP_5000&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weiterhin bietet das Attribut &#039;&#039;&#039;reading&#039;&#039;&#039; eine Eingrenzung auf die gewünschten Readings und mit dem Attribut &#039;&#039;&#039;valueFilter&#039;&#039;&#039; kann eine Eingrenzung nur auf bestimmte vorkommende Werte vorgenommen werden.&lt;br /&gt;
&lt;br /&gt;
Der Erfolg der Synchronisationsläufe kann sehr einfach mit einem separaten DbRep-Device (mit dem &#039;&#039;&#039;fetchrows&#039;&#039;&#039; Kommando) oder dem Tool phpMyAdmin (nebenstehend) überprüft werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== regelmäßige Ausführung der Synchronisation ====&lt;br /&gt;
&lt;br /&gt;
Ein einfaches AT kann dazu dienen den Synchronisationslauf mit dem Device &#039;&#039;&#039;Rep.LogDB1.syncStandby&#039;&#039;&#039; regelmäßig alle x-Stunden/Minuten auszuführen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define At.Sync.Stby at +*04:00:00 set Rep.LogDB1.syncStandby syncStandby LogStby&lt;br /&gt;
attr At.Sync.Stby room DbLog&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ermittlung und Darstellung der täglichen Energieerzeugung eines Wechselrichters ===&lt;br /&gt;
&lt;br /&gt;
Gegeben sei dass die Energiedaten eines Wechselrichters (STP_5000) mit SMAUtils in Verbindung mit SBFSpot in die Datenbank geschrieben werden.&lt;br /&gt;
Neben vielen anderen Werten liefert SMAUtils die Tageserzeugung in kWh als Reading &amp;quot;etoday&amp;quot;. Der Wert dieses Readings wird alle paar Minuten in der Datenbank gespeichert.&lt;br /&gt;
&lt;br /&gt;
Zur Auswertung wird ein DbRep-Device angelegt wie unter [[#Definieren_eines_DbRep-Devices | Definieren eines DbRep-Devices]]) beschrieben (z.B. Rep.etoday.STP_5000).&lt;br /&gt;
&lt;br /&gt;
[[Datei:Rep.STP_5000.configured.PNG|right|thumb|300px|Rep.STP_5000 konfiguriert]]&lt;br /&gt;
&lt;br /&gt;
Es soll die täglich erzeugte Energiemenge beginnend ab dem 01.10.2016 0 Uhr bis zum 13. Oktober angezeigt werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.etoday.STP_5000 timestamp_begin 2016-10-01 00:00:00&lt;br /&gt;
attr Rep.etoday.STP_5000 timestamp_end 2016-10-13 23:59:59&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da es sich um eine tägliche Aggregation handelt (es soll pro Auswertung der Tag von 00:00:00 bis 23:59:59 betrachtet werden) und die Hintergrundverarbeitungszeit dargestellt werden soll, wird eingestellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.etoday.STP_5000 aggregation day&lt;br /&gt;
attr Rep.etoday.STP_5000 showproctime 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Eingrenzung der auszuwertenden Devices und der dazu gehörigen Readings wird durch die entsprechenden Attribute gewährleistet.&lt;br /&gt;
Es soll nur das Device &amp;quot;STP_5000&amp;quot; mit dem Reading &amp;quot;etoday&amp;quot; berücksichtigt werden. Dazu stellen wir ein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.etoday.STP_5000 reading etoday&lt;br /&gt;
attr Rep.etoday.STP_5000 device STP_5000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
DbRep ist nun grundsätzlich zur Auswertung konfiguriert (siehe Screenshot).&lt;br /&gt;
&lt;br /&gt;
Zur Selektion und Berechnung von numerischen Daten stehen die Funktionen average-, max-, min-, sum- und diffValue zur Verfügung.&lt;br /&gt;
Bei den in diesem Beispiel verwendeten Daten handelt es sich um einen täglich neu erzeugten und über 24 Stunden stetig steigenden Wert.&lt;br /&gt;
Demnach ist für diese Art Daten die Funktion &amp;quot;maxValue&amp;quot; geeignet. Sie gibt den maximalen Wert des Readings im eingestellten Aggregationszeitraum (day) aus.&lt;br /&gt;
&lt;br /&gt;
[[Datei:Rep.STP_5000.max.PNG|right|thumb|300px|Rep.STP_5000 maxValue]]&lt;br /&gt;
&lt;br /&gt;
Die Berechnung wird ausgeführt durch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Rep.etoday.STP_5000 maxValue&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kurzer Zeit ist die Berechnung erfolgt (state=done in DeviceOverview). Nach einem Browserrefresh in der Detailansicht sind die erzeugten Readings sichtbar.&lt;br /&gt;
&lt;br /&gt;
Jedes Reading hat einen bestimmten Aufbau. Die Funktion &amp;quot;maxValue&amp;quot; erzeugt Readings folgenden Aufbaus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2016-10-07_18-19-03__STP_5000__etoday__MAX__2016-10-07&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zunächst wird der Timestring &amp;quot;2016-10-07_18-19-03&amp;quot; ausgegeben der in diesem Kontext den höchsten (d.h. letzten) Wert von &amp;quot;etotal&amp;quot; an dem Tag markiert.&lt;br /&gt;
Bei dem Wechselrichter ist es damit auch der Zeitpunkt zu dem die Energieerzeugung eingestellt wurde (in dem Fall 07.10.2016 um 18:19:03).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Anmerkung:&#039;&#039;&#039; Die Notation des Timestrings wurde so gewählt, um die Regeln für in Readings erlaubte Zeichen einzuhalten. Wenn keine auswertbaren Daten vorliegen und berechnet werden konnten, werden Readings mit &amp;quot;-&amp;quot; ausgegeben. Im Beispiel sind es die Tagesaggregate 10.10.-13.10. da diese Tage in der Zukunft liegen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Danach folgt im Reading die Angabe des Devices (STP_5000), des augewerteten Readings (etoday) und der Funktion die dieses Ergebnis ermittelt hat (MAX für maxValue).&lt;br /&gt;
Die letzte Angabe, hier &amp;quot;2016-10-07&amp;quot; definiert den Auswertungszeitraum. In dem Beispiel ist es der 07.10.2016. Würde die Berechnung mit aggregation=week durchgeführt werden, würde man als an dieser Stelle den Auswertungszeitraum &amp;quot;week_39&amp;quot; erhalten, also die Kalenderwoche 39.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Um die Auswertungsergebniss etwas benutzerfreundlicher zu gestalten, steht das Attribut &amp;quot;readingNameMap&amp;quot; zur Verfügung. [[Datei:Rep.STP_5000.max.namemap.PNG|right|thumb|300px|Rep.STP_5000 maxValue mit readingNameMap]]&lt;br /&gt;
Im Beispiel setzen wir es auf:   &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.etoday.STP_5000 readingNameMap Tageserzeugung_kWh&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein erneuter maxValue-Lauf erzeugt Readings des folgenden Aufbaus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2016-10-01_18-03-13__Tageserzeugung_kWh__2016-10-01   4.9870&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== (regelmäßiges) Löschen von Datenbanksätzen ===&lt;br /&gt;
&lt;br /&gt;
Dieses DbRep-Device dient dazu einmalig oder verbunden mit einem AT-Device Einträge aus einer Datenbank zu löschen.&lt;br /&gt;
Zunächst wird das Device wie im Abschnitt [[#Definieren_eines_DbRep-Devices | &amp;quot;Definieren eines DbRep-Devices&amp;quot;]] beschrieben definiert. &lt;br /&gt;
Dadurch wird das DbRep-Device mit einem DbLog-Device assoziiert und mit der Datenbank des DbLogs verbunden. Alle weiteren Operationen werden mit dieser Datenbank durchgeführt. In diesem Beispiel heißt das DbRep-Device &amp;quot;Rep.Del.DbShort&amp;quot; und das DbLog-Device &amp;quot;LogDBShort&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In diesem Beispiel sollen alle Datensätze gelöscht werden die älter als 180 Tage sind. Die Löschfunktion des Moduls ist standardmäßig ausgeschaltet und muß per Attribut enabled werden.&lt;br /&gt;
Dazu werden die Attribute                                      [[Datei:Rep.Del.DbShort.PNG|right|thumb|300px|Rep.Del.DbShort konfiguriert]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr Rep.Del.DbShort timeOlderThan d:180&lt;br /&gt;
 attr Rep.Del.DbShort allowDeletion 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gesetzt. Die Zeitangabe erfolgt in Sekunden.&lt;br /&gt;
&lt;br /&gt;
Insbesondere bei SQLite sollte während des Löschens kein paralleler Schreibprozess in Form des normalen Eventloggings stattfinden. MySQL/MariaDB kann es durchaus parallel verarbeiten. Um einen parallelen Zugriff durch DbLog-Loggging zu verhindern, kann das Logging vor der Löschung geschlossen und danach wieder geöffnet werden.&lt;br /&gt;
Dafür werden die Attribute mit z.B. diesen Werten gesetzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr Rep.Del.DbShort executeBeforeProc set LogDBShort reopen 7200&lt;br /&gt;
 attr Rep.Del.DbShort executeAfterProc set LogDBShort reopen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei ist &amp;quot;LogDBShort&amp;quot; das mit dem DbRep-Device assoziierte DbLog-Device.&lt;br /&gt;
 &lt;br /&gt;
Der Löschvorgang wird mit&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 set Rep.Del.DbShort delEntries&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
gestartet. Nach Abschluß der Datenlöschung wird die Anzahl der deleted rows als Reading ausgegeben und auch im Log mit verbose=3 geschrieben.&lt;br /&gt;
&lt;br /&gt;
Müssen sehr viele Datensätze gelöscht werden, könnte nach 86400 Sekunden der Standardtimeout erreicht werden und der Vorgang mit &amp;quot;error&amp;quot; enden.&lt;br /&gt;
In diesem Fall ist der timeout mit dem Attribut:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr Rep.Del.DbShort timeout &amp;lt;timeout in Sekunden&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
entsprechend angepasst werden.&lt;br /&gt;
&lt;br /&gt;
Ein fhem.cfg Eintrag für das beschriebene Beispieldevice sieht folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.Del.DbShort DbRep LogDBShort&lt;br /&gt;
attr Rep.Del.DbShort allowDeletion 1&lt;br /&gt;
attr Rep.Del.DbShort comment löschen aller Einträge in LogDBShort älter als 180 Tage&lt;br /&gt;
attr Rep.Del.DbShort devStateIcon connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.Del.DbShort event-on-update-reading state&lt;br /&gt;
attr Rep.Del.DbShort room DbLog&lt;br /&gt;
attr Rep.Del.DbShort timeOlderThan 15552000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Löschung der relevanten Datensätze kann weiterhin über Attribute &amp;quot;device&amp;quot; bzw. &amp;quot;reading&amp;quot; kann selektiv auf bestimmte Device- und/oder Readingloggings beschränkt werden. Wird keine Zeitbeschränkung (z.B. durch &amp;quot;timeOlderThan&amp;quot;) vorgenommen, erfolgt die Löschung aller Datensätze der durch &amp;quot;device&amp;quot; bzw. &amp;quot;reading&amp;quot; spezifizierten Datenbankinhalte.&lt;br /&gt;
&lt;br /&gt;
Als Argument der Attribute &amp;quot;device&amp;quot; bzw. &amp;quot;reading&amp;quot; kann SQL-Wildcard &amp;quot;%&amp;quot; verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%     substituiert ein oder mehrere Zeichen&lt;br /&gt;
_     Platzhalter für ein einzelnes Zeichen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr Rep.Del.DbShort device %5000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dieser Spezifikation werden alle Devices gelöscht die auf &amp;quot;5000&amp;quot; enden, also z.B. STP_5000 und MySTP_5000.&lt;br /&gt;
(siehe auch Attribut &amp;quot;reading&amp;quot; in der {{Link2CmdRef|Anker=DbRepattr|Lang=de|Label=Commandref}}).&lt;br /&gt;
&lt;br /&gt;
Ein einfaches AT kann dazu dienen den Löschjob über das Device Rep.Del.DbShort regelmäßig alle x-Stunden/Minuten auszuführen. Dadurch werden nicht mehr benötigte Datenbankeinträge automatisiert und nicht FHEM blockierend im Hintergrund entfernt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define At.Del.DbShort at +*23:45:00 set Rep.Del.DbShort delEntries&lt;br /&gt;
attr At.Del.DbShort room DbLog&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== erweiterte Anwendung von Selektionsbedingungen zur Löschung ====&lt;br /&gt;
&lt;br /&gt;
Wie bei allen selektiven Funktionen im DbRep kann auch bei der Löschfunktion über die Attribute &#039;&#039;&#039;device&#039;&#039;&#039; und &#039;&#039;&#039;reading&#039;&#039;&#039; eine Beschränkung der zu löschenden Datensätze erfolgen. D.h. es werden nur Datensätze gelöscht, wenn die Selektionskriterien dieser Attribute erfüllt sind.&lt;br /&gt;
&lt;br /&gt;
Im Attribut &#039;&#039;&#039;device&#039;&#039;&#039; können einzelne Geräte, Geräte-Spezifikationen ({{Link2CmdRef|Anker=devspec|Lang=de|Label=devspec}}) sowie Geräte mit Wildcards angegeben werden.&lt;br /&gt;
Ist eine devspec angegeben, werden die Devicenamen vor der Selektion aus der Geräte-Spezifikationen und den aktuell in FHEM vorhandenen Devices aufgelöst sofern möglich.&lt;br /&gt;
Wird dem Device bzw. der Device-Liste oder Geräte-Spezifikation ein &amp;quot;EXCLUDE=&amp;quot; vorangestellt, werden diese Devices von der Selektion und damit von dem Löschvorgang ausgeschlossen.   &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;name&amp;gt; device TYPE=DbRep                    # es werden nur Datensätze der Geräte vom Modul-Typ &amp;quot;DbRep&amp;quot; &lt;br /&gt;
                                                   eingeschlossen&lt;br /&gt;
attr &amp;lt;name&amp;gt; device MySTP_5000                    # nur Datensätze des Gerätes &amp;quot;MySTP_5000&amp;quot; werden selektiert/gelöscht&lt;br /&gt;
attr &amp;lt;name&amp;gt; device SMA.*,MySTP.*                 # Daten von Geräten die mit &amp;quot;SMA&amp;quot; oder &amp;quot;MySTP&amp;quot; beginnen, werden &lt;br /&gt;
                                                   selektiert/gelöscht&lt;br /&gt;
attr &amp;lt;name&amp;gt; device SMA_Energymeter,MySTP_5000    # nur Daten der beiden angegebenen Geräten werden selektiert/gelöscht&lt;br /&gt;
attr &amp;lt;name&amp;gt; device %5000                         # Daten von Geräten die mit &amp;quot;5000&amp;quot; enden werden in die Operation &lt;br /&gt;
                                                   eingeschlossen&lt;br /&gt;
attr &amp;lt;name&amp;gt; device TYPE=SSCam EXCLUDE=SDS1_SVS   # es werden Datensätze der Geräte vom Modul-Typ &amp;quot;SSCam&amp;quot; eingeschlossen, &lt;br /&gt;
                                                   aber das Gerät &amp;quot;SDS1_SVS&amp;quot; (auch vom Typ SSCam) wird ausgeschlosssen&lt;br /&gt;
attr &amp;lt;name&amp;gt; device EXCLUDE=SDS1_SVS              # Datensätze aller Geräte mit Ausnahme des Gerätes &amp;quot;SDS1_SVS&amp;quot; werden          &lt;br /&gt;
                                                   selektiert/gelöscht&lt;br /&gt;
attr &amp;lt;name&amp;gt; device EXCLUDE=TYPE=SSCam            # Datensätze aller Geräte mit Ausnahme der Geräte vom Typ &amp;quot;SSCam&amp;quot; werden          &lt;br /&gt;
                                                   selektiert/gelöscht&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ähnlich erfolgt die Abgrenzung der DB-Selektionen auf ein bestimmtes oder mehrere Readings sowie exkludieren von Readings. Mehrere Readings werden als Komma separierte Liste angegeben. Es können SQL Wildcard (%) verwendet werden, aber &#039;&#039;&#039;keine Perl-Regex&#039;&#039;&#039;.&lt;br /&gt;
Wird dem Reading bzw. der Reading-Liste ein &amp;quot;EXCLUDE=&amp;quot; vorangestellt, werden diese Readings von der Selektion ausgeschlossen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;name&amp;gt; reading etotal                                     # Datensätze mit dem Reading &amp;quot;etotal&amp;quot; werden eingeschlossen &lt;br /&gt;
attr &amp;lt;name&amp;gt; reading et%                                        # Datensätze deren Reading mit &amp;quot;et&amp;quot; beginnt werden &lt;br /&gt;
                                                                 eingeschlossen&lt;br /&gt;
attr &amp;lt;name&amp;gt; reading etotal,etoday                              # Datensätze mit den Readings &amp;quot;etotal&amp;quot; und &amp;quot;etoday&amp;quot; werden &lt;br /&gt;
                                                                 in die Selektion eingeschlossen&lt;br /&gt;
attr &amp;lt;name&amp;gt; reading eto%,Einspeisung EXCLUDE=etoday            # Datensätze mit Readings beginnend mit &amp;quot;eto&amp;quot; sowie das &lt;br /&gt;
                                                                 Reading &amp;quot;Einspeisung&amp;quot; werden selektiert, aber das Reading &lt;br /&gt;
                                                                 &amp;quot;etoday&amp;quot; wiederum ausgeschlossen&lt;br /&gt;
attr &amp;lt;name&amp;gt; reading etotal,etoday,Ein% EXCLUDE=%Wirkleistung   # Datensätze mit Readings beginnend mit &amp;quot;Ein&amp;quot; sowie die &lt;br /&gt;
                                                                 Readings &amp;quot;etotal&amp;quot; und &amp;quot;etoday&amp;quot; werden selektiert, wobei &lt;br /&gt;
                                                                 Readings, die auf &amp;quot;Wirkleistung&amp;quot; enden, von der Löschung&lt;br /&gt;
                                                                 ausgeschlossen werden&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Natürlich werden die Selektionsbedingungen durch eventuell gesetzte Zeiteingrenzungen mit den time*- Attributen ergänzt.&lt;br /&gt;
Die time*- Attribute sind mehrere im DbRep vorhandene Attribute, die alle mit &amp;quot;time&amp;quot; beginnen, z.B. timeOlderThan.&lt;br /&gt;
&lt;br /&gt;
=== Auffinden von alten Devicenamen in der DB und versenden/loggen einer Negativliste ===&lt;br /&gt;
&lt;br /&gt;
Wenn eine Datenbank längere Zeit gelaufen ist, Devices in FHEM hinzugefügt, geändert oder gelöscht wurden oder auch das DEF des DbLog-Devices angepasst wurde, befinden sich aller Wahrscheinlichkeit nach nicht mehr gewünschte/gebrauchte Datensätze von nicht mehr bestehenden Devices in der Datenbank.&lt;br /&gt;
&lt;br /&gt;
Dieses Beispiel soll einen Weg zeigen, wie man mit Hilfe der DbRep-sqlCmd Funktion (ab Version 4.14.0) alle in der DB enthältenen Devicenamen ermitteln und mit einer Positivliste vergleichen kann. &lt;br /&gt;
Die Negativliste der nicht gewünschten Devices wird im Logfile ausgegeben bzw. mit TelegramBot versendet.&lt;br /&gt;
&lt;br /&gt;
Zunächst wird das Device wieder wie im Abschnitt [[#Definieren_eines_DbRep-Devices | &amp;quot;Definieren eines DbRep-Devices&amp;quot;]] beschrieben definiert. &lt;br /&gt;
Dadurch wird das DbRep-Device mit einem DbLog-Device assoziiert und mit der Datenbank des DbLog-Device verbunden. In diesem Beispiel heißt das DbRep-Device &amp;quot;Report&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Nach der Definition setzen wir die Attribute:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Report event-on-update-reading state&lt;br /&gt;
attr Report sqlResultFormat separated&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es werden nun nur noch Events für &amp;quot;state&amp;quot; generiert. Das Attribut &amp;quot;sqlResultFormat = separated&amp;quot; bewirkt, dass das Ergebnis des SQL-Statements zeilenweise in Einzelreadings zur Verfügung gestellt wird.&lt;br /&gt;
&lt;br /&gt;
Nun kann getestet werden, ob die Abfrage grundsätzlich funktioniert. Dazu wird das benutzerspezifische Kommando &amp;quot;sqlCmd&amp;quot; verwendet.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set Report sqlCmd select device, count(*) from history group by DEVICE;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es sollte nun eine entsprechende Anzahl von Readings erzeugt werden (Browserrefresh), die als Wert eine Kombination aus &amp;lt;Device&amp;gt;|&amp;lt;Anzahl Datensätze in DB&amp;gt; enthalten wie im nebenstehenden Screenshot ersichtlich.&lt;br /&gt;
&lt;br /&gt;
[[Datei:sqlCmd.PNG|right|thumb|300px|sqlCmd ausgeführt]]&lt;br /&gt;
&lt;br /&gt;
Wenn das Ergebnis wie gewünscht vorliegt, wird nun die Benutzerschnittstelle aktiviert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Report userExitFn NaDevs .*:.*&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;quot;NaDevs&amp;quot; ist die Funktion in 99_myUtils.pm die aufgerufen werden soll. Die Angabe des Regex &amp;quot;.*:.*&amp;quot; ist optional. Nähere Informationen zur Funktionsweise sind in der {{Link2CmdRef|Lang=de|Anker=DbRep}} zu finden.&lt;br /&gt;
&lt;br /&gt;
Die &amp;quot;Positivliste&amp;quot; der in der Datenbank erlaubten Devices wird für das Beispiel in dem Attribut &amp;quot;comment&amp;quot; für den späteren Vergleich als String angegeben. Es ist natürlich auch z.B. eine Ableitung der im DEF-Regex des zugeordneten DbLog-Devices enthaltenen Device-Definitionen vorstellbar, sodass immer der aktuelle Stand dieser Definition als Grundlage für den Vergleich herangezogen wird. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Report comment (sysmon|MyWetter|SMA_Energymeter|STP_5000|Cam.*)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Somit werden die Devices &amp;quot;sysmon&amp;quot;, &amp;quot;MyWetter&amp;quot;, &amp;quot;SMA_Energymeter&amp;quot;, &amp;quot;STP_5000&amp;quot; und alle Cam-Devices als in der DB erlaubt definiert.&lt;br /&gt;
Alle anderen gefundenen Devices sollen in einer Negativliste aufgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Im weiteren Schritt wird die aufzurufende Funktion in der vorhandenen 99_myUtils.pm ergänzt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
############################################################################&lt;br /&gt;
# $Id: myUtilsTemplate.pm 7570 2015-01-14 18:31:44Z rudolfkoenig $&lt;br /&gt;
#&lt;br /&gt;
# Save this file as 99_myUtils.pm, and create your own functions in the new&lt;br /&gt;
# file. They are then available in every Perl expression.&lt;br /&gt;
&lt;br /&gt;
package main;&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
&lt;br /&gt;
sub&lt;br /&gt;
myUtils_Initialize($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# Enter you functions below _this_ line.&lt;br /&gt;
&lt;br /&gt;
my @dna;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
######################################################&lt;br /&gt;
########    Not allowed Devs in DB ermitteln&lt;br /&gt;
########          UserExitFn in DbRep           &lt;br /&gt;
######################################################&lt;br /&gt;
sub NaDevs {&lt;br /&gt;
 my ($name,$reading,$value) = @_;&lt;br /&gt;
 my $hash   = $defs{$name};&lt;br /&gt;
 &lt;br /&gt;
 # DB Namen ermitteln&lt;br /&gt;
 my $dbconn = $hash-&amp;gt;{dbloghash}{dbconn};&lt;br /&gt;
 my $db = (split(&amp;quot;=&amp;quot;,(split(&amp;quot;;&amp;quot;,$dbconn))[0]))[1];&lt;br /&gt;
 &lt;br /&gt;
 # Liste der erlaubten Devices aus Attribut &amp;quot;comment&amp;quot; lesen&lt;br /&gt;
 my $adevs = AttrVal($name, &amp;quot;comment&amp;quot;,&amp;quot;-&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
 if($reading eq &amp;quot;state&amp;quot; &amp;amp;&amp;amp; $value eq &amp;quot;running&amp;quot;) {&lt;br /&gt;
     # der Select wurde gestartet&lt;br /&gt;
     Log3 $name, 1, &amp;quot;UserExitFn called by $name - new Select has been startet. Allowed devices in database are: $adevs&amp;quot;; &lt;br /&gt;
     @dna=();&lt;br /&gt;
     return;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 my $d = (split(&amp;quot;\\|&amp;quot;,$value))[0];&lt;br /&gt;
 &lt;br /&gt;
 # das Selektionsergebnis bewerten und ggf. dem Ergebnisarray hinzufügen&lt;br /&gt;
 if ( $reading =~ m/SqlResultRow.*/ &amp;amp;&amp;amp; $d !~ m/$adevs/) {&lt;br /&gt;
   push(@dna,$d.&amp;quot;\n&amp;quot;)&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 if ($reading eq &amp;quot;state&amp;quot; &amp;amp;&amp;amp; $value eq &amp;quot;done&amp;quot;) {&lt;br /&gt;
     # Ergebnis versenden bzw. loggen&lt;br /&gt;
     Log3 $name, 1, &amp;quot;UserExitFn called by $name - Devices not allowd found in $db: @dna&amp;quot;; &lt;br /&gt;
     fhem(&amp;quot;set teleBot message Devices are not allowed found in $db: \n @dna&amp;quot;);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
....&lt;br /&gt;
1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Schnittstelle übergibt der aufgerufenen Funktion den Namen des DbRep-Devices, den Namen des erstellten Readings und dessen Wert.&lt;br /&gt;
Der Aufruf erfolgt nach jeder Readingserstellung. Die Schnittstelle arbeitet OHNE Eventgenerierung bzw. benötigt keine Events.&lt;br /&gt;
Über den Namen des DbRep-Devices kann auf dessen Hash zugegriffen werden bzw. über $hash-&amp;gt;{dbloghash}{...} ebenfalls auf den Hash des angeschlossenen DbLog-Devices.&lt;br /&gt;
&lt;br /&gt;
In der Funktion &amp;quot;NaDevs&amp;quot; wird der Device-Teilstring des erzeugten Readings mit dem Wertevorrat aus dem &amp;quot;comment&amp;quot;-Attribut verglichen und bei einem negativen Ergebnis der Negativliste @dna hinzugefügt.&lt;br /&gt;
&lt;br /&gt;
Nach Abschluss der Operation (signalisiert durch Reading &amp;quot;state = done&amp;quot;) wird die erzeugte Liste im Logfile ausgegeben bzw. ein TelgramBot-Device versendet.&lt;br /&gt;
&lt;br /&gt;
=== Größe der FHEM-Datenbank ermitteln ===&lt;br /&gt;
&lt;br /&gt;
Zunächst wird das Device wie im Abschnitt [[#Definieren_eines_DbRep-Devices | &amp;quot;Definieren eines DbRep-Devices&amp;quot;]] beschrieben definiert. &lt;br /&gt;
Dadurch wird das DbRep-Device mit einem DbLog-Device assoziiert und mit der Datenbank des DbLogs verbunden. Alle weiteren Operationen werden mit dieser Datenbank durchgeführt. In diesem Beispiel heißt das DbRep-Device &amp;quot;Rep.Fhem.Size&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Um die Größe der Datenbank beziehungsweise der Tabellen &amp;quot;history&amp;quot; und &amp;quot;current&amp;quot; zu ermitteln steht das Kommando:&lt;br /&gt;
                                                                        [[Datei:tableinfo.PNG|right|thumb|300px|get Rep.Fhem.Size tableinfo]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 get Rep.Fhem.Size tableinfo  (MySQL, PostgreSQL)&lt;br /&gt;
 get Rep.Fhem.Size svrinfo    (SQLite)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
zur Verfügung. &lt;br /&gt;
&lt;br /&gt;
Mit diesem Befehl werden sehr viele Informationen bezüglich der Tabellen des FHEM-Schemas (MySQL) als Readings ausgegeben.&lt;br /&gt;
&lt;br /&gt;
Um nur die relevanten bzw. interessierenden Selektionsergebnisse auszugeben, kann mit dem Attribut showTableInfo (MySQL, PostgreSQL) bzw. showSvrInfo (SQLite) eine kommaseparierte Liste der relevanten Tabellen angegeben werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.Fhem.Size showTableInfo %history%,%current%    (MySQL, PostgreSQL)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So wie in diesem Beispiel gesetzt, werden nur Informationen der Tabellen &amp;quot;history&amp;quot; und &amp;quot;current&amp;quot; ausgegeben.&lt;br /&gt;
Diese Ausgabe ich bereits recht übersichtlich, kann aber durch die Verwendung des Attributs suppressReading&lt;br /&gt;
weiter eingegrenzt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr Rep.Fhem.Size suppressReading ^(?!.*INFO_history.data_index_length_MB).*$&lt;br /&gt;
attr Rep.Fhem.Size suppressReading ^(?!.*(INFO_history.data_index_length_MB)|(state)).*$&lt;br /&gt;
attr Rep.Fhem.Size suppressReading ^(?!.*(INFO_history.data_index_length_MB)|(background_processing_time)|(state)).*$&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit diesen Attributwerten wird nur das Reading &amp;quot;INFO_history.data_index_length_MB&amp;quot; oder aber &amp;quot;INFO_history.data_index_length_MB&amp;quot;&lt;br /&gt;
und &amp;quot;state&amp;quot; bzw. &amp;quot;INFO_history.data_index_length_MB&amp;quot;, &amp;quot;state&amp;quot; und &amp;quot;background_processing_time&amp;quot; dargestellt.&lt;br /&gt;
&lt;br /&gt;
Die Datenbankgröße (MB) ist je nach DB-Typ in den Readings:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
INFO_current.data_index_lenth_MB&lt;br /&gt;
INFO_history.data_index_lenth_MB&lt;br /&gt;
SQLITE_FILE_SIZE_MB&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
enhalten. &lt;br /&gt;
&lt;br /&gt;
Wird z.B. über ein AT dieser Befehl regelmäßig durch Rep.Fhem.Size ausgeführt und die Ergebnisse wiederum in DbLog gespeichert kann die Größenentwicklung der Datenbank mittels SVG-Diagrammen dargestellt werden.                                                                            [[Datei:tableinfo_SVG.PNG|left|thumb|300px|get Rep.Fhem.Size tableinfo]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=&amp;quot;all&amp;quot;&amp;gt;&lt;br /&gt;
=== Readingwerte von DbRep in ein anderes Device übertragen ===&lt;br /&gt;
&lt;br /&gt;
Um Readings aus DbRep in einen Dummy zu übertragen, können verschiedene Verfahren angewendet werden. Allen Verfahren ist gemeinsam, dass sie auf die Arbeitsweise der Funktionen von DbRep Rücksicht nehmen. Alle Funktionen von DbRep arbeiten, von Ausnahmen abgesehen, asynchron (non-blocking). Das hat den Vorteil, dass die Datenbankverarbeitungszeit bzw. eine Nichtverfügbarkeit der DB nicht hemmend auf FHEM wirkt, hat andererseits aber den Nachteil, dass die Ergebnisse einer Funktion nicht direkt nach dem Funktionsaufruf zur Verfügung stehen und somit nicht trivial innerhalb der FHEM-Hauptschleife weiterverarbeitet werden können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Werte automatisch durch DbRep übertragen lassen (ab Version 8.40.0) ====&lt;br /&gt;
&lt;br /&gt;
Ab DbRep Version 8.40.0 gibt es das Attribut &#039;&#039;&#039;autoForward&#039;&#039;&#039; mit dem eine integrierte Übertragung der DbRep-Ergebnissreadings in andere Devices (z.B. Dummy) eingerichtet werden kann. Diese Variante wird hier beschrieben.&lt;br /&gt;
&lt;br /&gt;
Die weiteren manuellen Varianten werden in den nachfolgenden Abschnitten behandelt.&lt;br /&gt;
&lt;br /&gt;
Um die integrierte Reading-Weiterleitung zu aktivieren wird des Attribut autoForward nach folgender Systematik gesetzt:&lt;br /&gt;
[[Datei:aforw1.PNG|right|thumb|300px|Übertragung aller Readings nach Dum.Rep.all]]&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;&amp;lt;source-reading&amp;gt; =&amp;gt; &amp;quot;&amp;lt;destination device&amp;gt; [=&amp;gt; &amp;lt;destination-reading&amp;gt;]&amp;quot;,&lt;br /&gt;
  &amp;quot;&amp;lt;source-reading&amp;gt; =&amp;gt; &amp;quot;&amp;lt;destination device&amp;gt; [=&amp;gt; &amp;lt;destination-reading&amp;gt;]&amp;quot;,&lt;br /&gt;
  ...&lt;br /&gt;
}          &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Datei:aforw2.PNG|right|thumb|300px|Übertragung Readings mit &amp;quot;SUM&amp;quot; nach Dum.Rep.Sum]]&lt;br /&gt;
In der Spezifikation von &#039;&#039;&#039;&amp;lt;source-reading&amp;gt;&#039;&#039;&#039; können Wildcards (.*) verwendet werden um nur eine Auswahl der erzeugten Readings an das Zieldevice &#039;&#039;&#039;&amp;lt;destination device&amp;gt;&#039;&#039;&#039; weiterzuleiten. Ist der optionale Zusatz &#039;&#039;&#039;&amp;lt;destination-reading&amp;gt;&#039;&#039;&#039; angegeben, erfolgt die Speicherung der übertragenen Readings im Zieldevice mit dem angegebenen Namen. &amp;lt;br&amp;gt;&lt;br /&gt;
Der angegebene Readingname muss den Regularien zur Namensgebung von Readings genügen. Eventuell vorhandene nicht erlaubte Zeichen werden durch &amp;quot;_&amp;quot; ersetzt.&lt;br /&gt;
&lt;br /&gt;
Fehlt der Zusatz &amp;lt;destination-reading&amp;gt;, werden die in DbRep erzeugten Readings 1:1 in das Zieldevice übertragen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr DbRepDev autoForward {&lt;br /&gt;
                            &amp;quot;.*&amp;quot;        =&amp;gt; &amp;quot;Dum.Rep.All&amp;quot;,     &lt;br /&gt;
                            &amp;quot;.*AVGAM.*&amp;quot; =&amp;gt; &amp;quot;Dum.Rep     =&amp;gt; average&amp;quot;,&lt;br /&gt;
                            &amp;quot;.*SUM.*&amp;quot;   =&amp;gt; &amp;quot;Dum.Rep.Sum =&amp;gt; summary&amp;quot;,&lt;br /&gt;
                          }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Spezifikation erfüllt folgende Ziele:&lt;br /&gt;
&lt;br /&gt;
# alle Readings werden zum Device &amp;quot;Dum.Rep.All&amp;quot; übertragen, der ursprüngliche Readingname bleibt im Ziel erhalten (Screenshot 1)&lt;br /&gt;
# Readings mit &amp;quot;AVGAM&amp;quot; im Namen werden zum Device &amp;quot;Dum.Rep&amp;quot; in das Reading &amp;quot;average&amp;quot; übertragen&lt;br /&gt;
# Readings mit &amp;quot;SUM&amp;quot; im Namen werden zum Device &amp;quot;Dum.Rep.Sum&amp;quot; in das Reading &amp;quot;summary&amp;quot; übertragen (Screenshot 2)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Werte mittels Event übetragen ====&lt;br /&gt;
&lt;br /&gt;
Es sollen zum Beispiel die erzeugten Readings aus einem DbRep-Device in einen Dummy übetragen werden, die den Term &amp;quot;Grid&amp;quot; im Wortstamm tragen.&lt;br /&gt;
&lt;br /&gt;
Ein Reading-Name der fetchrows-Funktion in DbRep ist immer nach folgendem Schema aufgebaut:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;Datum&amp;gt;_&amp;lt;Zeit&amp;gt;__&amp;lt;Unique-Index&amp;gt;__&amp;lt;Device&amp;gt;__&amp;lt;Reading&amp;gt;__&amp;lt;Dopplerindex&amp;gt;&lt;br /&gt;
 z.B.: 2018-10-14_18-30-25__1__MyWetter__humidity&lt;br /&gt;
&lt;br /&gt;
Datum und Zeit entsprechen dem Timestamp des gespeicherten Events in der Datenbank. Der Unique-Index identifiziert eventuell vorhandene Doubletten in der DB und der Dopplerindex ist ein Hilfsmittel, Datensätze die sich allein durch den Value-Wert unterscheiden, auch als unterschiedliche Readings erstellen zu können. Dieser Index wird nur bei Bedarf erstellt. &lt;br /&gt;
&lt;br /&gt;
Im folgenden Beispiel werden alle erzeugten Readings mit &amp;quot;Grid&amp;quot; im Namen des DbRep-Devices &amp;quot;Rep.SMAEM&amp;quot; in den Dummy übertragen und heißen dann genauso. Zunächst wird der Dummy angelegt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Dum.Rep dummy&lt;br /&gt;
attr Dum.Rep room DbLog&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit dem nachfolgend angelegten Notify wird auf die z.B. von fetchrows erzeugten Events reagiert, der Event entsprechend gesplittet und verarbeitet in den Dummy übertragen. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define N.Dum.Rep notify Rep.SMAEM:(\d).*Grid.* { fhem &amp;quot;setreading Dum.Rep &amp;quot;.(split(&amp;quot;:&amp;quot;,$EVTPART0))[0].&amp;quot; $EVTPART1&amp;quot;}&lt;br /&gt;
attr N.Dum.Rep room DbLog&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll im Dummy ein Reading mit eigenem Namen gefüllt werden, sieht das Notify z.B. so aus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define N.Dum.Rep notify Rep.SMAEM:(\d).*Grid.* { fhem &amp;quot;setreading Dum.Rep DeinReading&amp;quot;.&amp;quot; $EVTPART1&amp;quot;}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Werte mittels Funktion DbReadingsVal übertragen ====&lt;br /&gt;
&lt;br /&gt;
Sobald ein DbRep-Device definiert ist, wird die Funktion DbReadingsVal zur Verfügung gestellt. Mit dieser Funktion läßt sich, ähnlich dem allgemeinen ReadingsVal, der Wert eines Readings aus der Datenbank abrufen.  Die Befehlssyntax ist:&lt;br /&gt;
&lt;br /&gt;
    DbReadingsVal(&amp;quot;&amp;lt;name&amp;gt;&amp;quot;,&amp;quot;&amp;lt;device:reading&amp;gt;&amp;quot;,&amp;quot;&amp;lt;timestamp&amp;gt;&amp;quot;,&amp;quot;&amp;lt;default&amp;gt;&amp;quot;) &lt;br /&gt;
&lt;br /&gt;
Mit dieser Funktion kann ein Device/Reading-Wert aus der Datenbank gelesen und in einen Dummy gesetzt werden. &amp;quot;Name&amp;quot; ist dabei das abzufragende DbRep-Device. &lt;br /&gt;
Der Timestamp muss nicht genau bekannt sein. Es wird der zeitlich zu &amp;lt;timestamp&amp;gt; passendste Readingwert zurück geliefert, falls kein Wert exakt zu dem angegebenen Zeitpunkt geloggt wurde. &lt;br /&gt;
Um den aktuellsten in der Datenbank gespeicherten Wert eines Readings abzurufen, kann als Timestamp die aktuelle Zeit entsprechend formatiert verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Es soll z.B. der aktuellste Datenbankwert des Readings &amp;quot;etoday&amp;quot; vom Device &amp;quot;MySTP_5000&amp;quot; ausgelesen und im Dummy &amp;quot;Dum.Rep&amp;quot; als Reading &amp;quot;EnergyToday&amp;quot; gespeichert werden. Das beteilgte DbRep-Device heißt &amp;quot;Rep.Energy&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Für die Formatierung des aktuellen Timestamps kann die FHEM-Funktion FmtDateTime verwendet werden, welcher der aktuelle UNIX-Timestamp übergeben wird:&lt;br /&gt;
&lt;br /&gt;
 FmtDateTime(time)&lt;br /&gt;
&lt;br /&gt;
Der Datenabruf aus der Datenbank sieht dann folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 DbReadingsVal(&amp;quot;Rep.Energy&amp;quot;,&amp;quot;MySTP_5000:etoday&amp;quot;,FmtDateTime(time),&amp;quot;&amp;quot;) &lt;br /&gt;
&lt;br /&gt;
Mit dem Befehl setreading kann der abgerufene Wert in dem Dummy-Device gespeichert werden:&lt;br /&gt;
&lt;br /&gt;
 { fhem &amp;quot;setreading Dum.Rep EnergyToday &amp;quot;.DbReadingsVal(&amp;quot;Rep.Energy&amp;quot;,&amp;quot;MySTP_5000:etoday&amp;quot;,FmtDateTime(time),&amp;quot;&amp;quot;) }&lt;br /&gt;
&lt;br /&gt;
Damit der Datenabruf regelmäßig erfolgt und immer der aktuellste Wert von &amp;quot;etoday&amp;quot; in den Dummy eingtragen wird, kann dieses AT verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 define set.Dum.Rep_EnergyToday at +*00:01:00 { fhem &amp;quot;setreading Dum.Rep EnergyToday &amp;quot;.DbReadingsVal(&amp;quot;Rep.Energy&amp;quot;,&amp;quot;MySTP_5000:etoday&amp;quot;,FmtDateTime(time),&amp;quot;&amp;quot;) }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Somit erfogt eine Aktualisierung des Readings &amp;quot;EnergyToday&amp;quot; im Dummy &amp;quot;Dum.Rep&amp;quot; jede Minute.&lt;br /&gt;
Es ist zu beachten, dass die Funktionsausführung von DbReadingsVal blockierend erfolgt. Sollte die Datenbank nicht verfügbar sein oder lange Antwortzeiten besitzen, hat dies negative Auswirkungen auf FHEM, was sich in Blockierungszuständen äußert.&lt;br /&gt;
Möchte man solche eventuell möglichen Zustände vermeiden, kann die Befehlsausführung in die 99_myUtils unter Verwendung von [[Blocking_Call | Blocking Call]] ausgelagert werden.&lt;br /&gt;
&lt;br /&gt;
Diese non-blocking Variante der Verwendung von DbReadingsVal wird nachfolgend skizziert.&lt;br /&gt;
In der 99_myUtils wird dazu der nachfolgende Code eingefügt, der drei kleine Subroutinen enthält:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
############################################################################################################&lt;br /&gt;
#         DumRepEnergyToday (Reading mittels DbReadingsVal non-blocking setzen)    &lt;br /&gt;
############################################################################################################&lt;br /&gt;
sub DumRepEnergyToday ($$$$$) {&lt;br /&gt;
 # initiale Routine zum Aufruf von BlockingCall unter Berücksichtigung von &amp;quot;RUNNING_SET_READING&amp;quot;&lt;br /&gt;
 my ($name,$device,$reading,$destdev,$destread) = @_;&lt;br /&gt;
 my $hash = $defs{$name};&lt;br /&gt;
&lt;br /&gt;
 return if($hash-&amp;gt;{HELPER}{RUNNING_SET_READING});&lt;br /&gt;
 $hash-&amp;gt;{HELPER}{RUNNING_SET_READING} = BlockingCall(&amp;quot;DumRepEnergyTodayNbl&amp;quot;, &amp;quot;$name|$device|$reading|$destdev|$destread&amp;quot;, &amp;quot;DumRepEnergyTodayNblDone&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub DumRepEnergyTodayNbl($) {&lt;br /&gt;
 # die eigentliche Routine in der die potentiell blockierende Funktion ausgeführt wird&lt;br /&gt;
 my ($string) = @_;&lt;br /&gt;
 my ($name,$device,$reading,$destdev,$destread) = split(&amp;quot;\\|&amp;quot;, $string);&lt;br /&gt;
 my $hash = $defs{$name};&lt;br /&gt;
&lt;br /&gt;
 my $val = DbReadingsVal($name,&amp;quot;$device:$reading&amp;quot;,FmtDateTime(time),&amp;quot;&amp;quot;);&lt;br /&gt;
 $val = encode_base64($val,&amp;quot;&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
return &amp;quot;$name|$val|$destdev|$destread&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub DumRepEnergyTodayNblDone($) {&lt;br /&gt;
 # Rückkherfunktion zur Ergebnisauswertung und Löschen von &amp;quot;RUNNING_SET_READING&amp;quot;&lt;br /&gt;
 my ($string) = @_;&lt;br /&gt;
 my @a    = split(&amp;quot;\\|&amp;quot;,$string);&lt;br /&gt;
 my $hash = $defs{$a[0]};&lt;br /&gt;
 my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
 my $val  = decode_base64($a[1]);&lt;br /&gt;
 my $destdev  = $a[2];&lt;br /&gt;
 my $destread = $a[3];&lt;br /&gt;
 &lt;br /&gt;
 fhem &amp;quot;setreading $destdev $destread $val&amp;quot;;&lt;br /&gt;
 delete $hash-&amp;gt;{HELPER}{RUNNING_SET_READING};&lt;br /&gt;
 &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der oben gezeigte Code stellt die einfachste Form der BlockingCall-Implementierung dar. Weitere Informationen dazu sind im weiter oben angegebenen Link zum BlockingCall-Wiki enthalten.&lt;br /&gt;
&lt;br /&gt;
Um non-blocking Funktion aufzurufen, ist das bereits beschriebene AT-Device wie folgt zu ändern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define set.Dum.Rep_EnergyTodayNbl at +*00:01:00 { DumRepEnergyToday(&amp;quot;Rep.Energy&amp;quot;,&amp;quot;MySTP_5000&amp;quot;,&amp;quot;etoday&amp;quot;,&amp;quot;Dum.Rep&amp;quot;,&amp;quot;EnergyToday&amp;quot;) }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der aufgerufenen Funktion &amp;quot;DumRepEnergyToday&amp;quot; werden hierbei der Name des abzufragenden DbRep-Devices, der Name des auszuwertenden Devices, der Name des auszuwertenden Readings sowie das Ziel-Device und das Ziel-Reading als Argumente mitgegeben. Dieser Aufruf kann dadurch universell zum Abruf und Setzen verschiedener auszuwertender Device/Reading-Kombinationen verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Ein Userreading anlegen und für stateformat verwenden ===&lt;br /&gt;
&lt;br /&gt;
Eine Besonderheit beim Modul DbRep besteht darin, dass sich die Namen der generierten Readings ändern können. Dadurch kann man für die Erstellung eines eigenen Userreading nicht einfach den Namen eines erstellten Readings, z.B. in der Funktion ReadingsVal, verwenden um dessen Wert zu ermitteln.&lt;br /&gt;
&lt;br /&gt;
Eine Möglichkeit besteht darin eine Funktion in 99_myUtils anzulegen und mit dem Attribut &amp;quot;userExitFn&amp;quot; diese Funktion zu aktivieren/zu verwenden.&lt;br /&gt;
&lt;br /&gt;
Für das Beispiel soll ein Reading &amp;quot;power_max&amp;quot; für den erreichten Maximalwert im Monat und der Timestamp des Maximalwertes aus einem Beispielreading &amp;quot;2018-06-01_13-23-02__STP_5000__total_pac__MAX__no_aggregation&amp;quot; (Name kann sich durch das jeweilige Datum ändern) extrahiert werden.&lt;br /&gt;
&lt;br /&gt;
Zunächst wird die Funktion &amp;quot;calcpomax&amp;quot; in 99_myUtils angelegt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
############################################################################################################&lt;br /&gt;
########           power_max in DbRep erstellen   &lt;br /&gt;
############################################################################################################&lt;br /&gt;
sub calcpomax {&lt;br /&gt;
 my ($name,$reading,$value) = @_;&lt;br /&gt;
 my $hash = $defs{$name};&lt;br /&gt;
 &lt;br /&gt;
 if($reading =~ /^.*total_pac__MAX.*$/) {&lt;br /&gt;
     $reading =~ /^(\d+)-(\d+)-(\d+)_(\d+)-(\d+)-(\d+)_.*$/;&lt;br /&gt;
     my $pmts = &amp;quot;$3.$2.$1 $4:$5:$6&amp;quot;;&lt;br /&gt;
     my $fmtDateTime = FmtDateTime(gettimeofday());&lt;br /&gt;
     setReadingsVal($hash,&amp;quot;power_max&amp;quot;,$value,$fmtDateTime);&lt;br /&gt;
     setReadingsVal($hash,&amp;quot;power_max_ts&amp;quot;,$pmts,$fmtDateTime);&lt;br /&gt;
 }&lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie in der Commandref zu {{Link2CmdRef|Anker=DbRep|Lang=de|Label=DbRep}} zu lesen ist, werden der im Attribut &amp;quot;userExitFn&amp;quot; hinterlegten Funktion jeweils der Name des aufrufenden Devices, das Reading und dessen Wert übergeben. Mit ein paar Zeilen Code wird der übergebene Readingsname auf das Vorhandensein des statischen Namensteils (total_pac__MAX) geprüft und dementsprechend der Readingname &amp;quot;power_max&amp;quot; mit dem Wert angelegt, wenn der Regex auf den Readingnamen matched.&lt;br /&gt;
Gleiches gilt für das Reading &amp;quot;power_max_ts&amp;quot;, welches den Zeitpunkt des erreichten Maximalwertes enthalten soll.&lt;br /&gt;
&lt;br /&gt;
Die Aktivierung der Schnittstelle erfolgt im DbRep-Device mit &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;name&amp;gt; userExitFn calcpomax .*:.*&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit jedem Lauf von &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set &amp;lt;name&amp;gt; maxvalue&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
wird nun das neue Reading mit angelegt und kann z.B. in einem stateformat verwendet werden:  &lt;br /&gt;
 [[Datei:Rep.powmax.PNG|right|thumb|500px|]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;name&amp;gt; stateformat&lt;br /&gt;
{  &lt;br /&gt;
 if(ReadingsVal($name,&amp;quot;power_max&amp;quot;,0)) {&lt;br /&gt;
   (ReadingsVal($name,&amp;quot;power_max&amp;quot;,0)*1000).&amp;quot; Watt (erreicht am &amp;quot;.ReadingsVal($name,&amp;quot;power_max_ts&amp;quot;,&amp;quot;&amp;quot;).&amp;quot;)&amp;quot;;&lt;br /&gt;
 } else {&lt;br /&gt;
   ReadingsVal($name,&amp;quot;state&amp;quot;,&amp;quot;done&amp;quot;);&lt;br /&gt;
 }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nachfolgend noch die Definition des verwendeten DbRep-Devices für diese Auswertung:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod Rep.total_pac.currmonth DbRep LogDB&lt;br /&gt;
attr Rep.total_pac.currmonth aggregation no&lt;br /&gt;
attr Rep.total_pac.currmonth alias Peak WR-Leistung aktueller Monat&lt;br /&gt;
attr Rep.total_pac.currmonth allowDeletion 0&lt;br /&gt;
attr Rep.total_pac.currmonth devStateIcon connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.total_pac.currmonth device STP_5000&lt;br /&gt;
attr Rep.total_pac.currmonth disable 0&lt;br /&gt;
attr Rep.total_pac.currmonth event-on-update-reading state&lt;br /&gt;
attr Rep.total_pac.currmonth group SMA Inverter&lt;br /&gt;
attr Rep.total_pac.currmonth reading total_pac&lt;br /&gt;
attr Rep.total_pac.currmonth room Energie&lt;br /&gt;
attr Rep.total_pac.currmonth showproctime 1&lt;br /&gt;
attr Rep.total_pac.currmonth stateFormat {  \&lt;br /&gt;
 if(ReadingsVal($name,&amp;quot;power_max&amp;quot;,0)) {\&lt;br /&gt;
   (ReadingsVal($name,&amp;quot;power_max&amp;quot;,0)*1000).&amp;quot; Watt (erreicht am &amp;quot;.ReadingsVal($name,&amp;quot;power_max_ts&amp;quot;,&amp;quot;&amp;quot;).&amp;quot;)&amp;quot;;;\&lt;br /&gt;
 } else {\&lt;br /&gt;
   ReadingsVal($name,&amp;quot;state&amp;quot;,&amp;quot;done&amp;quot;);;\&lt;br /&gt;
 }\&lt;br /&gt;
}&lt;br /&gt;
attr Rep.total_pac.currmonth timestamp_begin current_month_begin&lt;br /&gt;
attr Rep.total_pac.currmonth timestamp_end current_month_end&lt;br /&gt;
attr Rep.total_pac.currmonth userExitFn calcpomax .*:.*&lt;br /&gt;
attr Rep.total_pac.currmonth verbose 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== datenbankgestützte Erstellung der Energiebilanz einer SMA PV-Anlage mit Überschußeinspeisung ===&lt;br /&gt;
[[datenbankgestützte Erstellung der Energiebilanz einer SMA PV-Anlage mit Überschußeinspeisung | Hier]] geht&#039;s zum Beitrag.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== DbRep-Kommandos regelmäßig mit einem at-Device ausführen ===&lt;br /&gt;
&lt;br /&gt;
Sollen DbRep-Kommandos regelmäßig zu bestimmten Zeiten bzw. Intervallen ausgeführt werden, kann ein at-Device (alternativ z.B. DOIF) dafür definiert und verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel soll die regelmäßige Ausführung des SQL-Statements &lt;br /&gt;
&lt;br /&gt;
 select device, count(*) from history group by DEVICE&lt;br /&gt;
&lt;br /&gt;
über das Set-Kommando &#039;&#039;&#039;sqlCmd&#039;&#039;&#039; dienen.&lt;br /&gt;
&lt;br /&gt;
Zunächst wird das DbRep-Device definiert welches zur Ausführung des Kommandos dienen soll.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.LogDB.sqlResult DbRep LogDB&lt;br /&gt;
attr Rep.LogDB.sqlResult devStateIcon initialized:control_3dot_hor_s connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.LogDB.sqlResult event-on-update-reading state&lt;br /&gt;
attr Rep.LogDB.sqlResult fastStart 1&lt;br /&gt;
attr Rep.LogDB.sqlResult room Datenbank&lt;br /&gt;
attr Rep.LogDB.sqlResult showproctime 1&lt;br /&gt;
attr Rep.LogDB.sqlResult sqlResultFormat table&lt;br /&gt;
attr Rep.LogDB.sqlResult verbose 2&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Definition Syntax wurde bereits weiter oben erläutert. Das Attribut &#039;&#039;&#039;sqlResultFormat&#039;&#039;&#039; legt fest in welcher Art und Weise das Ergebnis präsentiert werden soll. Auch andere Formate wie JSON sind implementiert.&lt;br /&gt;
&lt;br /&gt;
Um das oben angegbene SQL-Statement zum Beispiel alle 6 Stunden 20 Minuten auszuführen, dient die folgende at-Definition: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define At.LogDB.sqlResult at +*06:20:00 set Rep.LogDB.sqlResult sqlCmd select device, count(*) from history group by DEVICE&lt;br /&gt;
attr At.LogDB.sqlResult icon clock&lt;br /&gt;
attr At.LogDB.sqlResult room Datenbank-&amp;gt;Produktiv&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Weitere Informationen sind in der [https://fhem.de/commandref_DE.html#at at-CommandRef] beschrieben.&lt;br /&gt;
&lt;br /&gt;
Natürlich sind alle verfügbaren DbRep Set/Get-Befehle über diesen Weg ausführbar.&lt;br /&gt;
Es wird empfohlen das definierte DbRep-Device nur für Auswertungsaufgaben zu verwenden die eine gleiche Attributierung des Devices verlangen. &lt;br /&gt;
Für weitere Aufgaben sollten weitere DbRep-Devices definiert und entsprechend eingestellt werden. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DbRep Chain - die Kommandokette ===&lt;br /&gt;
&lt;br /&gt;
Für regelmäßig auszuführende Aufgaben erstellt man sich je ein separates DbRep-Device und setzt die Attribute wie es für die spezifische Aufgabe erforderlich ist. &lt;br /&gt;
Zu diesem Zweck kopiert man sich einfach ein vorhandenes DbRep-Device mit dem FHEM &#039;&#039;copy&#039;&#039; Kommando und passt es danach entsprechend an. Es können beliebig viele DbRep-Devices angelegt werden.&lt;br /&gt;
&lt;br /&gt;
Häufig besteht der Wunsch, verschiedene Datenbankaktionen nacheinander ablaufen zu lassen, z.B. erst &#039;&#039;minValue writeToDB&#039;&#039; danach &#039;&#039;maxValue writeToDB&#039;&#039; sowie &#039;&#039;averageValue writeToDB&#039;&#039; nacheinander auszuführen.&lt;br /&gt;
Eine solche Verkettung kann durch die Nutzung des &#039;&#039;executeAfterProc&#039;&#039; Attributes erstellt werden.&lt;br /&gt;
&lt;br /&gt;
Für das Beispiel werden 3 DbRep-Devices angelegt:&lt;br /&gt;
&lt;br /&gt;
 * Rep_Min&lt;br /&gt;
 * Rep_Max&lt;br /&gt;
 * Rep_Avg&lt;br /&gt;
&lt;br /&gt;
Das erste Device (Rep_Min) soll für &#039;&#039;minValue writeToDB&#039;&#039; genutzt werden. In diesem Device wird das Attribut gesetzt:&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
 attr Rep_Min executeAfterProc set Rep_Max maxValue writeToDB&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dieses Attribut zeigt auf das zweite Device (Rep_Max), welche für &#039;&#039;maxValue writeToDB&#039;&#039; verwendet werden soll. &lt;br /&gt;
In diesem Device wird wiederum das Attribut gesetzt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 attr Rep_Min executeAfterProc set Rep_Avg averageValue writeToDB&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die gesamte Chain startet mit einem at-Device, notify oder dergleichen mit dem Kommando:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  set Rep_Min minValue writeToDB&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Funktionsweise:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Die Attribute executeBeforeProc und executeAfterProc führen jeweils das in ihnen hinterlegte FHEM-Kommando oder Perl-Funktion vor bzw. nach der Ausführung des gestarteten DbRep-Kommandos aus. &amp;lt;br&amp;gt;&lt;br /&gt;
In dem dargestellten Beispiel startet das at-Device mit &lt;br /&gt;
&lt;br /&gt;
  set Rep_Min minValue writeToDB&lt;br /&gt;
&lt;br /&gt;
die Funktion &#039;&#039;minValue writeToDB&#039;&#039; im Device &#039;&#039;Rep_Min&#039;&#039;.  Wenn das DbRep &#039;&#039;Rep_Min&#039;&#039; mit seiner Aufgabe fertig ist, startet es automatisch über executeAfterProc das DbRep &#039;&#039;Rep_Max&#039;&#039; die Ausführung von &#039;&#039;&#039;maxValue writeToDB&#039;&#039;&#039;. Hat DbRep &#039;&#039;Rep_Max&#039;&#039;seine Aufgabe beendet, startet es wiederum im letzten Device &#039;&#039;Rep_Avg&#039;&#039; das Kommando &#039;&#039;averageValue writeToDB&#039;&#039;. &amp;lt;br&amp;gt;&lt;br /&gt;
Mit diesem Verfahren läuft immer nur ein Kommando der Reihe nach über die Datenbank.&lt;br /&gt;
&lt;br /&gt;
Natürlich kann diese Chain beliebig erweitert werden, oder im ersten DbRep vor dem Start der Chain das angeschlossene DbLog-Device pausiert werden mit:&lt;br /&gt;
&lt;br /&gt;
 attr Rep_Min executeBeforeProc set &amp;lt;DbLog-Device&amp;gt; reopen 7200&lt;br /&gt;
&lt;br /&gt;
Die Beendigung der Pause des DbLog kann das letzte DbRep &#039;&#039;Rep_Avg&#039;&#039; übernehmen durch Setzen des Attributes:&lt;br /&gt;
&lt;br /&gt;
 attr Rep_Avg executeAfterProc set &amp;lt;DbLog-Device&amp;gt; reopen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Abarbeitung einer sequentiellen Befehlskette mittels non-blocking sqlCmd-Kommandos ===&lt;br /&gt;
&lt;br /&gt;
(&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; In der aktuellen DbRep-Version kann die seqientielle Kommandoabarbeitung über die [[#DbRep_Chain_-_die_Kommandokette|Chain-Funktionalität]] realisiert werden.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Vorteil von DbRep ist die nicht blockierende Arbeitsweise fast aller DbRep-Befehle (auf Ausnahmen wird in der Commandref hingewiesen).&lt;br /&gt;
Daraus ergibt sich aber auch das Merkmal, dass nach dem Funktionsaufruf nicht auf das Ergebnis gewartet wird und die FHEM-Schleife  &lt;br /&gt;
unmittelbar nach dem Start des Befehls weiterläuft. Um mehrere voneinander abhängige Befehle, die eine festgelegte Reihenfolge erfüllen müssen, abzuarbeiten, kann die nachfolgend beschriebene Technik zur Kettenbildung angewendet werden. Diese Technik wird anhand einer einfachen Demo erläutert. &lt;br /&gt;
&lt;br /&gt;
Das konstruierte Ziel der Kette ist folgendes:&lt;br /&gt;
&lt;br /&gt;
* Es soll nacheinander der Insert eines Datensatzes durchgeführt, danach ein Select dieses Datensatzes, ein Update auf ein Feld und zuletzt ein delete dieses Datensatzes in dieser Reihenfolge ausgeführt werden&lt;br /&gt;
&lt;br /&gt;
Demnach sich insgesamt vier SQL-Befehle nacheinander auszuführen (Insert, Select, Update, Delete).&lt;br /&gt;
Zunächst werden vier identische DbRep-Devices angelegt, die sich lediglich durch ihren Namen unterscheiden. Das wären die Devices:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Rep.insert, Rep.select, Rep.update, Rep.delete&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur Anlage kann ein DbRep-Device als Vorlage erstellt und nachfolgend dreimal kopiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.insert DbRep LogDB&lt;br /&gt;
attr Rep.insert allowDeletion 1&lt;br /&gt;
attr Rep.insert showproctime 1&lt;br /&gt;
attr Rep.insert userExitFn chain&lt;br /&gt;
&lt;br /&gt;
copy Rep.insert Rep.select&lt;br /&gt;
copy Rep.insert Rep.update&lt;br /&gt;
copy Rep.insert Rep.delete&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das wichtige Detail dieser Devices ist das gesetzte Attribut &amp;quot;userExitFn = chain&amp;quot;. Dieses Attribut aktiviert eine Schnittstelle zum Aufruf von benutzereigenem Code nach dem Abschluß eines Datenbank-Calls (siehe Commandref).&lt;br /&gt;
&lt;br /&gt;
Der benutzereigene Code wird nach folgendem Schema in die 99_myUtils.pm eingebaut:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
#############################################################################################&lt;br /&gt;
#  sqlCmd Commandchain zur sequentiellen Abarbeitung von non-blocking DbRep-Befehlen&lt;br /&gt;
#&lt;br /&gt;
#  genutzte werden vier DbRep-Devices:  Rep.insert, Rep.select, Rep.update, Rep.delete&lt;br /&gt;
#       &lt;br /&gt;
#############################################################################################&lt;br /&gt;
sub chain {&lt;br /&gt;
 my ($name,$reading,$value) = @_;&lt;br /&gt;
 my $hash   = $defs{$name};&lt;br /&gt;
 my $device = &amp;quot;Testdevice&amp;quot;;&lt;br /&gt;
 my $model  = &amp;quot;Testmodel&amp;quot;;&lt;br /&gt;
 my $rc;&lt;br /&gt;
 &lt;br /&gt;
 if ($name eq &amp;quot;Rep.insert&amp;quot; &amp;amp;&amp;amp; $reading eq &amp;quot;chainstart&amp;quot;) {&lt;br /&gt;
     # Start der 1. Operation (insert)&lt;br /&gt;
     CommandSet(undef,&amp;quot;Rep.insert sqlCmd insert into history (DEVICE, TYPE, EVENT, READING, VALUE, UNIT)&lt;br /&gt;
	   values (\&amp;quot;$device\&amp;quot;, \&amp;quot;$model\&amp;quot;, \&amp;quot;definition\&amp;quot;, \&amp;quot;DEF\&amp;quot;, \&amp;quot;Hugo\&amp;quot;, \&amp;quot;Emma\&amp;quot;)&amp;quot;);&lt;br /&gt;
     Log3 $name, 1, &amp;quot;$name - Start chain with insert&amp;quot;;&lt;br /&gt;
     return;   &lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 if($reading eq &amp;quot;state&amp;quot; &amp;amp;&amp;amp; $value eq &amp;quot;done&amp;quot;) {&lt;br /&gt;
     if ($name eq &amp;quot;Rep.insert&amp;quot;) {&lt;br /&gt;
         # Auswertung der 1. Operation (insert)&lt;br /&gt;
         $rc = ReadingsVal($name, &amp;quot;SqlResultRow_1&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
         Log3 $name, 1, &amp;quot;$name - number of inserts: $rc&amp;quot;;&lt;br /&gt;
         &lt;br /&gt;
         # Start der 2. Operation (select)&lt;br /&gt;
         CommandSet(undef,&amp;quot;Rep.select sqlCmd select VALUE from history where device=\&amp;quot;$device\&amp;quot; and READING=\&amp;quot;DEF\&amp;quot; LIMIT 1;&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
   &lt;br /&gt;
     if ($name eq &amp;quot;Rep.select&amp;quot;) {&lt;br /&gt;
         # Auswertung der 2. Operation (select)&lt;br /&gt;
         $rc = ReadingsVal($name, &amp;quot;SqlResultRow_1&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
         Log3 $name, 1, &amp;quot;$name - return of select: $rc&amp;quot;;&lt;br /&gt;
         &lt;br /&gt;
         # Start der 3. Operation (update)&lt;br /&gt;
         CommandSet(undef,&amp;quot;Rep.update sqlCmd update history set UNIT=\&amp;quot;neu\&amp;quot; where DEVICE=\&amp;quot;$device\&amp;quot;;&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     if ($name eq &amp;quot;Rep.update&amp;quot;) {&lt;br /&gt;
         # Auswertung der 3. Operation (update)&lt;br /&gt;
         $rc = ReadingsVal($name, &amp;quot;SqlResultRow_1&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
         Log3 $name, 1, &amp;quot;$name - number of updates: $rc&amp;quot;;&lt;br /&gt;
         &lt;br /&gt;
         # Start der 4. Operation (delete)&lt;br /&gt;
         CommandSet(undef,&amp;quot;Rep.delete sqlCmd delete from history where device = \&amp;quot;$device\&amp;quot; and UNIT = \&amp;quot;neu\&amp;quot;;&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     if ($name eq &amp;quot;Rep.delete&amp;quot;) {&lt;br /&gt;
         # Auswertung der 4. Operation (delete)&lt;br /&gt;
         $rc = ReadingsVal($name, &amp;quot;SqlResultRow_1&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
         Log3 $name, 1, &amp;quot;$name - number of deletes: $rc&amp;quot;;&lt;br /&gt;
     } &lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die gesamte Abarbeitung wird gestartet mit dem Aufruf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chain(&amp;quot;Rep.insert&amp;quot;, &amp;quot;chainstart&amp;quot;)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bzw. in der Kommandozeile im FHEMWEB mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{chain(&amp;quot;Rep.insert&amp;quot;, &amp;quot;chainstart&amp;quot;)}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach dem initialen Aufruf des Insert wird die sub &amp;quot;chain&amp;quot; beendet und nach dem insert-Befehl durch das Device &amp;quot;Rep.insert&amp;quot; wieder aufgerufen. Dabei wird diese sub nach jedem erstellten Reading aufgerufen und dabei neben dem eigenen Devicenamen auch der Readingname sowie dessen Wert zur Auswertung übergeben. &lt;br /&gt;
Durch die if-Zweige wird bei jedem Durchlauf der richtige Entrypoint für den nachfolgenden Funktionsaufruf ermittelt und ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Nach der Abarbeitung aller vorgesehenen SQL-Statements ist die erfolgreiche Chainverkettung anhand der Ergebnisse im Logfile ersichtlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2018.10.24 23:57:37.602 1: Rep.insert - Start chain with insert&lt;br /&gt;
2018.10.24 23:57:37.815 1: Rep.insert - number of inserts: 1&lt;br /&gt;
2018.10.24 23:57:37.901 1: Rep.select - return of select: Hugo&lt;br /&gt;
2018.10.24 23:57:38.033 1: Rep.update - number of updates: 1&lt;br /&gt;
2018.10.24 23:57:38.176 1: Rep.delete - number of deletes: 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Summe aller Einschaltzeiten eines Gerätes ===&lt;br /&gt;
&lt;br /&gt;
[[Summe aller Einschaltzeiten eines Gerätes | Hier]] geht&#039;s zum Beitrag.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eine Device spezifische Aufbewahrungszeit (Retention Time) in der Datenbank realisieren ===&lt;br /&gt;
&lt;br /&gt;
In der DbLog Datenbank gespeicherte Daten müssen bzw. sollten regelmäßig gelöscht werden sofern sie nicht mehr für Auswertungen verwendet werden. Über DbRep Kommandos gibt es dafür viele Möglichkeiten.&lt;br /&gt;
&lt;br /&gt;
Vorgestellt wird hier die Möglichkeit, bereits beim Logging des Events mittels Attribut eine Device spezifische Aufbewahrungszeit festzulegen. Diese Möglichkeit ist ab der DbLog Version 5.9.4 gegeben. Die einzelnen Schritte zur Umsetzung werden nachfolgend beschrieben.&lt;br /&gt;
&lt;br /&gt;
* im global Device das userattr &#039;&#039;&#039;retentionPeriod&#039;&#039;&#039; definieren. Der Name des Attributes ist natürlich frei wählbar. Das Attribut steht in allen Devices zur Verfügung und bestimmt die Aufbewahrungszeit in Sekunden wenn im Device oder im DbLog Device gesetzt.&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
attr global userattr retentionPeriod &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Für die Speicherung des Verfallzeitpunktes in der Datenbank wird die Tabellenspalte EVENT in der Tabelle history verwendet. Normalerweise wird der komplette zu loggende Event in dieser Spalte gespeichert, was im Allgemeinen nicht benötigt wird. Dazu wird in dem relevanten DbLog Device das Attribut &#039;&#039;&#039;valueFn&#039;&#039;&#039; gesetzt um in jedem zu loggenden Datensatz die Spalte EVENT die spezifische Verfallszeit zu speichern: &lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
{ &lt;br /&gt;
  my $def = AttrVal ($NAME, &#039;retentionPeriod&#039;,  9999999999);&lt;br /&gt;
  $EVENT  = int(time) + AttrVal ($DEVICE, &#039;retentionPeriod&#039;,  $def);&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
* Im gleichen DbLog Device wird das Attribut &#039;&#039;&#039;retentionPeriod&#039;&#039;&#039; auf einen Default Wert gesetzt der gelten soll, wenn im zu loggenden Datensatz kein retentionPeriod-Attribut im Quellen-Device hinterlegt ist. Wenn kein retentionPeriod-Attribut im DbLog Device gesetzt ist, erfolgt mit dem Wert &#039;int(time) + 9999999999&#039; de facto eine zeitlich unbegrenzte Speicherung.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
attr &amp;lt;DbLog&amp;gt; retentionPeriod 7776000    (Beispiel für Haltezeit von 90 Tagen = 7776000 Sekunden)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Bild:Screenshot 2024-01-03 210126.png|right|thumb|400px|]]&lt;br /&gt;
* In jedem relevanten Device wird nun die gewünschte Aufbewahrungszeit in Sekunden gesetzt:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
attr &amp;lt;Device&amp;gt; retentionPeriod xxxxxxxxx&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Cache des DbLog Devices findet man jetzt in jedem Datensatz den Verfallszeitpunkt des Datensatzes. Er wird in der EVENT-Spalte der Tabelle gespeichert. &lt;br /&gt;
&lt;br /&gt;
Vor den nächsten Schritten ist sicherzustellen, dass EVENT bei bereits vorhandenen Datensätzen auf einen passenden Verfallszeitpunkt upgedated werden.&lt;br /&gt;
Dazu eignet sich sqlCmd im DbRep oder ein SQL Editor deiner Wahl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
UPDATE history SET TIMESTAMP=TIMESTAMP, EVENT=&amp;quot;1704353074&amp;quot;;    (setzt den Verfall auf  04.01.2024 08:24:34)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit die Datenlöschungen über eine Auswertung von EVENT später performant ausgeführt werden, sollte ein ensprechender Index angelegt werden und der Datentyp von EVENT in Integer geändert werden. Beide SQL Statements können mit einem DbRep-Device mit gesetzten &#039;&#039;&#039;adminCredentials&#039;&#039;&#039; über &amp;quot;set ... sqlCmd&amp;quot; ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE INDEX Retention_Idx ON `history` (EVENT);&lt;br /&gt;
ALTER TABLE history MODIFY COLUMN EVENT INT(15); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die veränderte Spaltenbreite von EVENT wird im DbLog-Device nachgezogen und das Attribut &#039;&#039;&#039;colEvent&#039;&#039;&#039; gesetzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
attr &amp;lt;DbLog&amp;gt; colEvent 15&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zur regelmäßigen Löschung der Datensätze mit einer abgelaufenen Aufbewahrungszeit dient ein at-Device in Verbindung mit einem DbRep-Device (z.B. Rep.fhemtest1) welches täglich abgelaufene Datensätze sucht und löscht. &lt;br /&gt;
Die Definition des at-Devices:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define At.RetentionDel at *23:15:00 {&lt;br /&gt;
  my $t = int(time);&lt;br /&gt;
  fhem &amp;quot;set Rep.fhemtest1 sqlCmd delete from history where EVENT&amp;lt;&#039;$t&#039;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das DbRep-Device benötigt keine besonderen Einstellungen außer einem gesetzten Attribut &#039;&#039;&#039;allowDeletion=1&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.fhemtest1 DbRep &amp;lt;DbLog-Device&amp;gt;&lt;br /&gt;
attr Rep.fhemtest1 allowDeletion 1&lt;br /&gt;
attr Rep.fhemtest1 event-on-update-reading state&lt;br /&gt;
attr Rep.fhemtest1 room DbLog&lt;br /&gt;
attr Rep.fhemtest1 showproctime 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Welche Device/Reading-Kombinationen gibt es in der Datenbank ? ===&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Im aktuellen DbRep-Release kann die nachfolgend beschriebene Auswertung alternativ durch die eingebauten Kommmandos:&lt;br /&gt;
 set &amp;lt;DbRep-Device&amp;gt; sqlSpecial allDevCount&lt;br /&gt;
 set &amp;lt;DbRep-Device&amp;gt; sqlSpecial allDevReadCount&lt;br /&gt;
ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Wenn eine Datenbank längere Zeit gelaufen ist und die zu loggenden Devices bzw. Readings immer mal wieder geändert wurden, wächst der Wunsch einen Überblick über die in der DB enthaltenen Devices/Reading-Kombinationen zu erhalten.&lt;br /&gt;
Dadurch können wiederum gezielt Auswertungen auf eine bestimmte Device/Reading-Kombination durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Um dieses Ziel zu erreichen, wird ein DbRep-Device erstellt welches für die Verwendung eines User-Command eingerichtet wird.&lt;br /&gt;
Je nach dem gewünschten Ergebnis wird eines der nachfolgenden SQL-Kommandos verwendet.&lt;br /&gt;
&lt;br /&gt;
Selektion aller Device/Reading-Kombinationen in der Datenbank:&lt;br /&gt;
 select device, reading, count(*) from history group by DEVICE, READING&lt;br /&gt;
&lt;br /&gt;
Nur die geloggten Devices in der Datenbank reporten:&lt;br /&gt;
 select device, count(*) from history group by DEVICE&lt;br /&gt;
&lt;br /&gt;
Zur Einrichtung wird zunächst das DbRep-Device definiert:&lt;br /&gt;
&lt;br /&gt;
 define Rep.LogDB.sqlResult DbRep LogDB&lt;br /&gt;
&lt;br /&gt;
Dabei ist &amp;quot;LogDB&amp;quot; &#039;&#039;&#039;nicht&#039;&#039;&#039; die Datenbank, sondern das DbLog-Device mit dem das DbRep-Device assoziiert und derüber mit der Datenbank verbunden wird !&lt;br /&gt;
 &lt;br /&gt;
Um die Ausgabe des späteren Ergebnisses zu formatieren wird das Attribut &amp;quot;sqlResultFormat&amp;quot; auf &amp;quot;table&amp;quot; gesetzt. Dadurch wird das Selektergebnis als Tabelle mit den Spalten Device, Reading und der Anzahl der enthaltenen Datensätze der jeweiligen Kombination erzeugt.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.LogDB.sqlResult sqlResultFormat table&lt;br /&gt;
&lt;br /&gt;
Natürlich kann auch ein anderes Ausgabeformat gewählt werden wenn gewünscht oder benötigt. Bei großen Ergebnismengen ist zur Erhöhung der Übersichtlichkeit das Attribut &amp;quot;sqlResultFormat = separated&amp;quot; wahrscheinlich eher geeignet.&lt;br /&gt;
&lt;br /&gt;
Das Attribut stateFormat wird entsprechend gesetzt um nach einem erfolgten Selektionslauf die erzeugte Tabelle in der Raumansicht bzw. im DeviceOverview anzuzeigen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr Rep.LogDB.sqlResult stateFormat { if (defined($defs{$name}{READINGS}{SqlResult})) {&lt;br /&gt;
                                            ReadingsVal($name,&amp;quot;SqlResult&amp;quot;,undef);&lt;br /&gt;
                                        } else {&lt;br /&gt;
                                            ReadingsVal($name,&amp;quot;state&amp;quot;,undef);&lt;br /&gt;
                                        } &lt;br /&gt;
                                      }                                        &lt;br /&gt;
&amp;lt;/pre&amp;gt;                                   &lt;br /&gt;
&lt;br /&gt;
Damit ist das DbRep-Device vorbereitet und der Auswertungslauf kann gestartet werden mit:&lt;br /&gt;
&lt;br /&gt;
 set Rep.LogDB.sqlResult sqlCmd select device, reading, count(*) from history group by DEVICE, READING;&lt;br /&gt;
&lt;br /&gt;
bzw. wenn nur die in der Datenbank enthaltenen Devices zu erhalten:&lt;br /&gt;
&lt;br /&gt;
 set Rep.LogDB.sqlResult sqlCmd select device, count(*) from history group by DEVICE;&lt;br /&gt;
&lt;br /&gt;
[[Datei:sqlResult.PNG|right|thumb|600px|set Rep.LogDB.sqlResult sqlCmd select device, count(*) from history group by DEVICE;]]&lt;br /&gt;
&lt;br /&gt;
Nebenstehender Screenshot zeigt das Ergebnis der Auswertung aller in der Datenbank enthalteten Devices und deren Anzahl von Datensätzen.&lt;br /&gt;
&lt;br /&gt;
Zum leichteren Nachnutzung hier noch die Raw-Definition des fertigen DbRep-Devices:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod Rep.LogDB.sqlResult DbRep LogDB&lt;br /&gt;
attr Rep.LogDB.sqlResult aggregation no&lt;br /&gt;
attr Rep.LogDB.sqlResult comment Device zum freien Report mit sqlCmd.\&lt;br /&gt;
- Auswahl von Statements -\&lt;br /&gt;
\&lt;br /&gt;
Device/Reading-Kombinationen in der Datenbank:\&lt;br /&gt;
select device, reading, count(*) from history group by DEVICE, READING\&lt;br /&gt;
\&lt;br /&gt;
geloggte Devices in der Datenbank:\&lt;br /&gt;
select device, count(*) from history group by DEVICE\&lt;br /&gt;
&lt;br /&gt;
attr Rep.LogDB.sqlResult devStateIcon connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.LogDB.sqlResult disable 0&lt;br /&gt;
attr Rep.LogDB.sqlResult event-on-update-reading state&lt;br /&gt;
attr Rep.LogDB.sqlResult group Datenbankauswertungen&lt;br /&gt;
attr Rep.LogDB.sqlResult room DbLog&lt;br /&gt;
attr Rep.LogDB.sqlResult showproctime 1&lt;br /&gt;
attr Rep.LogDB.sqlResult sqlResultFormat table&lt;br /&gt;
attr Rep.LogDB.sqlResult stateFormat { if (defined($defs{$name}{READINGS}{SqlResult})) {\&lt;br /&gt;
   ReadingsVal($name,&amp;quot;SqlResult&amp;quot;,undef);;\&lt;br /&gt;
  } else {\&lt;br /&gt;
   ReadingsVal($name,&amp;quot;state&amp;quot;,undef);;\&lt;br /&gt;
  } \&lt;br /&gt;
}&lt;br /&gt;
attr Rep.LogDB.sqlResult timeout 1000&lt;br /&gt;
attr Rep.LogDB.sqlResult verbose 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Die current-Tabelle mit einem Extrakt der history-Tabelle füllen (tableCurrentFillup) ===&lt;br /&gt;
&lt;br /&gt;
Diese Funktion erweitert die Möglichkeiten der Verwendung der current-Tabelle des DbLog-Devices. In den meisten Fällen wird im DbLog das Attribut &amp;quot;DbLogType = Current/History&amp;quot; gesetzt sein um bei der Erstellung von SVG&#039;s entsprechende Vorschläge in der DropDown-Liste des Editors zur Verfügung zu haben. &lt;br /&gt;
&lt;br /&gt;
Bei dieser Betriebsart werden in der current-Tabelle neu auftretende Events von Device/Reading-Kombinationen eingetragen, sofern sie noch nicht enthalten sind. Werden neue Geräte definiert, erscheinen deren Events in der current-Tabelle, auch wenn man später nicht benötigte Readings mit den Attributen &amp;quot;event-on-change-reading&amp;quot; oder &amp;quot;event-on-update-reading&amp;quot; ausgrenzt. &lt;br /&gt;
Die einmal gespeicherten Werte bleiben in der current Tabelle erhalten. Sicherlich wird deswegen der eine oder andere User den Inhalt der current Tabelle löschen um wieder eine leere und saubere current-Tabelle zu haben. Das führt dann natürlich dazu, dass nur die ab diesem Zeitpunkt auftretenden Events in der Vorschlagsliste aufgenommen werden und nur selten auftretende Device/Reading-Kombinationen erst nach längerer Zeit wieder in der Tabelle current gespeichert werden. &lt;br /&gt;
&lt;br /&gt;
Darüber hinaus möchte man vielleicht nur für SVG-Diagramme relevante Device/Reading-Einträge in der Vorschagsliste vorfinden oder dort nur Einträge vorhalten, die in den letzten drei Monaten aufgetreten sind. Die Beweggründe können sehr vielfältig sein.&lt;br /&gt;
Um diesem Wunsch zu entsprechen, gibt es im DbRep die Funktion &amp;quot;set &amp;lt;DbRep-Device&amp;gt; tableCurrentFillup&amp;quot;, die eng mit dem dem DbLog-Attribut &amp;quot;DbLogType = SampleFill/History&amp;quot; zusammenarbeitet. &lt;br /&gt;
&lt;br /&gt;
Nachfolgend wird an einem Beispiel erläutert, welche Konfigurationen vorgenommen werden können, um aus der history-Tabelle alle vorhandenen Device/Reading-Kombinationen zu extrahieren und sie zur Verwendung als SVG-Vorschlagsliste in die current-Tabelle einzufügen.  &lt;br /&gt;
&lt;br /&gt;
Für das Beispiel wird folgendes angenommen:&lt;br /&gt;
&lt;br /&gt;
* es gibt ein definiertes DbLog-Device &amp;quot;LogDB&amp;quot; welches in die Datenbank &amp;quot;fhem&amp;quot; loggt&lt;br /&gt;
* das DbRep-Device zur Ausführung der Funktion wird &amp;quot;Rep.FillCurr.fhem&amp;quot; genannt&lt;br /&gt;
* es sollen alle in der history-Tabelle vorkommenden Device/Reading-Kombinationen extrahiert und in die current-Tabelle eingetragen werden (Einschränkungen auf bestimmte Devices / Readings oder zeitliche Abgrenzungen werden nicht vorgenommen, aber es wird darauf hingewiesen, wie es gemacht werden kann)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== a) Anlegen des DbRep-Devices ====&lt;br /&gt;
&lt;br /&gt;
Die Definition des DbRep-Devices erfolgt unter Angabe des zu verbindenden DbLog-Devices (nicht der Datenbank selbst): &lt;br /&gt;
&lt;br /&gt;
 define Rep.FillCurr.fhem DbRep LogDB&lt;br /&gt;
&lt;br /&gt;
In dem so definierten neuen Device sind die Attribute zu setzen, die für die beabsichtigte Funktion wichtig sind.&lt;br /&gt;
Da dieses Device auch für die Löschung der in der current-Tabelle gespeicherten Datensätze verwendet wird, muss das Attribut &amp;quot;allowDeletion = 1&amp;quot; gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.FillCurr.fhem allowDeletion 1&lt;br /&gt;
&lt;br /&gt;
Ebenfalls wichtig ist die Erzeugung eines Events sobald der Löschvorgang der current-Tabelle abgeschlossen ist. Dazu wird im Beispiel &amp;quot;event-on-update-reading&amp;quot; verwendet:&lt;br /&gt;
&lt;br /&gt;
 attr Rep.FillCurr.fhem event-on-update-reading state,--DELETED_ROWS_CURRENT-- &lt;br /&gt;
&lt;br /&gt;
[[Datei:currdelete.PNG|right|thumb|300px|set Rep.FillCurr.fhem tableCurrentPurge]]&lt;br /&gt;
Für die Funktion nicht wichtig, aber durchaus interessant zu wissen wie lange die Laufzeit der Funktionen ist, wird noch das Attribut &amp;quot;showproctime = 1&amp;quot; gesetzt. Damit werden die Readings &amp;quot;background_processing_time&amp;quot; und &amp;quot;sql_processing_time&amp;quot; erzeugt welche die jeweilige Bearbeitungszeit in Sekunden darstellen.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.FillCurr.fhem showproctime 1&lt;br /&gt;
&lt;br /&gt;
Mit dem so vorbereiteten Device können schon die Funktionen &amp;quot;tableCurrentPurge&amp;quot; und &amp;quot;tableCurrentFillup&amp;quot; manuell getestet werden. Mit dem Kommando:&lt;br /&gt;
&lt;br /&gt;
 set Rep.FillCurr.fhem tableCurrentPurge&lt;br /&gt;
&lt;br /&gt;
wird der gesamte aktuelle Inhalt der current-Tabelle gelöscht. Wie im nebenstehenden Screenshot sichtbar, wurden in dem Beispiel 170 Datensätze gelöscht und dazu rund 30ms benötigt. Mit Abschluß der Operation wird der Delete-Event erzeugt:&lt;br /&gt;
&lt;br /&gt;
 2018-01-05 13:38:14.596 DbRep Rep.FillCurr.fhem --DELETED_ROWS_CURRENT--: 170&lt;br /&gt;
&lt;br /&gt;
[[Datei:currfillup.PNG|right|thumb|300px|set Rep.FillCurr.fhem tableCurrentFillup]]&lt;br /&gt;
Dieser Event wird später in einem Notify für die automatisierte Current-Auffüllung verwendet.&lt;br /&gt;
Eine Zählug der current-EInträge mit &amp;quot;set Rep.FillCurr.fhem countEntries current&amp;quot; ergibt &amp;quot;-&amp;quot;, d.h. der Löschbefehl war erfolgreich. Im jetzigen Zustand würde bei der Erstellung eines SVG keine DropDown-Liste angezeigt werden und statt dessen die Felder im SVG-Editor frei bearbeitbar sein, weil die current-Tabelle keine Werte enthält. &lt;br /&gt;
Gleichermaßen kann bereits die Auffüllung der current-Tabelle getestet werden mit dem Befehl:&lt;br /&gt;
&lt;br /&gt;
 set Rep.FillCurr.fhem tableCurrentFillup&lt;br /&gt;
&lt;br /&gt;
Je nach Größe der Datenbank kann diese Operation einige Zeit in Anspruch nehmen. Da dieser Befehl wie bei DbRep üblich non-blocking abgearbeitet wird, hat die Laufzeit keinen Einfluss auf die sonstige FHEM-Verfügbarkeit. &lt;br /&gt;
&lt;br /&gt;
[[Datei:svgfillup.PNG|left|thumb|300px|set Rep.FillCurr.fhem tableCurrentFillup]]&lt;br /&gt;
Wie bereits erwähnt, wird in dem Beispiel die gesamte history-Tabelle ausgewertet. Sollen zum Beispiel nur die letzten 90 Tage zur Auswertung herangezogen werden, kann das &#039;&#039;&#039;Attribut &amp;quot;timeDiffToNow = d:90&amp;quot;&#039;&#039;&#039; gesetzt werden. &lt;br /&gt;
&lt;br /&gt;
Weitere Eingrenzungen bzgl. der auszuwertenden Devices bzw. Readings können über die &#039;&#039;&#039;Attribute &amp;quot;device&amp;quot; und &amp;quot;reading&amp;quot;&#039;&#039;&#039; vorgenommen werden. Die Syntax für die genannten Attribute und deren Auswirkung entnehme bitte der {{Link2CmdRef|Lang=de|Anker=DbRepattr}}.&lt;br /&gt;
&lt;br /&gt;
In unserem Beispiel wurde die current-Tabelle mit 170 Datensätzen aufgefüllt. Dazu wurden 291 Sekunden benötigt (die Tabelle history enthält insgesamt rund 11 Mio. Einträge)&lt;br /&gt;
&lt;br /&gt;
Wird jetzt ein neues SVG erstellt, erscheinen alle in selektierten Device/Reading-Kombinationen alphabetisch sortiert als DopDown-Liste im SVG-Editor.&lt;br /&gt;
&lt;br /&gt;
==== b) Konfiguration des DbLog-Devices ==== &lt;br /&gt;
&lt;br /&gt;
In dem bestehen DbLog-Device &amp;quot;LogDB&amp;quot; ist zur Vorbereitung lediglich das Attribut &amp;quot;DbLogType = SampleFill/History&amp;quot; zu setzen:&lt;br /&gt;
&lt;br /&gt;
 attr LogDB DbLogType SampleFill/History&lt;br /&gt;
&lt;br /&gt;
Entgegen der Arebeitsweise mit &amp;quot;Current/History&amp;quot; wird die current-Tabelle nicht mehr durch die Logging-Engine aktiv mit jedem relevanten Event gefüllt, sondern kann durch externe Tools, in dem Fall dem DbRep-Device, beschrieben werden.&lt;br /&gt;
Die current-Tabelle wird aber, ob leer oder gefüllt, bei der Erstellung eines SVG-Devices ausgewertet und in Abhängigeit des Ergebnisses eine DropDown-Liste zur Verfügung gestellt, oder frei editierbare Felder angeboten falls die current-Tabelle leer ist.&lt;br /&gt;
&lt;br /&gt;
==== c) Definition der at/notify-Devices zum automatisierten Ablauf ==== &lt;br /&gt;
&lt;br /&gt;
Die Löschung des Inhaltes der current-Tabelle und die erneute Ausffüllung soll natürlich autmatisiert regelmäßig erfolgen. Zu diesem Zweck wird ein at-Device (At.LogDB.currentPurge) und ein notify-Device angelegt. Das AT-Device soll alle 8 Stunden und 15 Minuten das Löschen des current-Inhaltes steuern und nach Abschluß der Löschung soll getriggert durch das &amp;quot;--DELETED_ROWS_CURRENT--&amp;quot;-Event die Neubefüllung der current-Tabelle veranlasst werden.&lt;br /&gt;
&lt;br /&gt;
Zur Definition dieser Devices gibt es nicht viel zu erläutern. Die entsprechende Syntax kann bei {{Link2CmdRef|Lang=de|Anker=at|Label=at}} bzw. {{Link2CmdRef|Lang=de|Anker=notify|Label=notify}} in der {{Link2CmdRef|Lang=de}} nachgelesen werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;at-Device:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 define At.LogDB.currentPurge at +*08:15:00 set Rep.FillCurr.fhem tableCurrentPurge&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;notify-Device:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 define N.LogDB.Fillup notify Rep.FillCurr.fhem:.*DELETED_ROWS_CURRENT.*  sleep 5;set Rep.FillCurr.fhem tableCurrentFillup&lt;br /&gt;
&lt;br /&gt;
Das nichtblockierende FHEM-sleep (sleep gefolgt von einem weiteren Befehl) wird lediglich dazu verwendet, um zwischen der Delete- und Auffüll-Operation eine kleine Pause einzufügen.&lt;br /&gt;
 &lt;br /&gt;
==== d) Arbeitsweise und Raw-Definitionen ====&lt;br /&gt;
&lt;br /&gt;
Durch das &amp;quot;At.LogDB.currentPurge&amp;quot;-Device wird alle 8 Stunden und 15 Minuten ein Löschlauf der current-Tabelle gestartet. Dadurch wird dem darauf folgenden Auffüllprozess immer eine saubere leere current-Tabelle zur Verfügung gestellt. Nach dem Löschlauf wird durch das &amp;quot;N.LogDB.Fillup&amp;quot;-Device, getriggert durch den Event &amp;quot;Rep.FillCurr.fhem:.*DELETED_ROWS_CURRENT.*&amp;quot;, die erneute Auffüllung der current-Tabelle gestartet.&lt;br /&gt;
Die Daten werden aus der histrory-Tabelle gelesen, wobei der Zeitraum der Selektion und die zu betrachtenden Devices / Readings durch die Zeit-Attribute bzw. device / reading-Attribute des DbRep-Devices &amp;quot;Rep.FillCurr.fhem&amp;quot; individuell beeinflußt werden können.&lt;br /&gt;
&lt;br /&gt;
Hilfreich ist es auch, wenn das DbLog-Device &amp;quot;LogDB&amp;quot; im asynchronen Modus betrieben wird.&lt;br /&gt;
&lt;br /&gt;
Abschließend sind hier noch die Raw-Definitionen der beteiligten Devices (außer LogDB) angehängt, um das Beispiel leicht nachvollziehbar zu gestalten.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DbRep-Device &amp;quot;Rep.FillCurr.fhem&amp;quot;:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod Rep.FillCurr.fhem DbRep LogDB&lt;br /&gt;
attr Rep.FillCurr.fhem allowDeletion 1&lt;br /&gt;
attr Rep.FillCurr.fhem comment Current Fillup für DB fhem&lt;br /&gt;
attr Rep.FillCurr.fhem devStateIcon connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.FillCurr.fhem event-on-update-reading state,--DELETED_ROWS_CURRENT--&lt;br /&gt;
attr Rep.FillCurr.fhem icon icoTool&lt;br /&gt;
attr Rep.FillCurr.fhem room DbLog&lt;br /&gt;
attr Rep.FillCurr.fhem showproctime 1&lt;br /&gt;
attr Rep.FillCurr.fhem verbose 3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;at-Device &amp;quot;At.LogDB.currentPurge&amp;quot;:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod At.LogDB.currentPurge at +*08:15:00 set Rep.FillCurr.fhem tableCurrentPurge&lt;br /&gt;
attr At.LogDB.currentPurge comment Zeitgesteuertes Löschen der Current Tabelle. Nach dem Löschen wird \&lt;br /&gt;
Eventgesteuert (Notify N.LogDB.Fillup) ein tableCurrentFillup gestartet.&lt;br /&gt;
attr At.LogDB.currentPurge room DbLog&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;notify-Device &amp;quot;N.LogDB.Fillup&amp;quot;:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod N.LogDB.Fillup notify Rep.FillCurr.fhem:.*DELETED_ROWS_CURRENT.*  sleep 5;;set Rep.FillCurr.fhem tableCurrentFillup&lt;br /&gt;
attr N.LogDB.Fillup disable 1&lt;br /&gt;
attr N.LogDB.Fillup room DbLog&lt;br /&gt;
attr N.LogDB.Fillup verbose 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
=== Backup/Restore einer SQLite Datenbank im laufenden Betrieb ===&lt;br /&gt;
&lt;br /&gt;
In diesem Beitrag wird erläutert, wie man mit DbRep ein Backup der SQLite-Datenbank erstellen kann, welche Möglichkeiten es dabei gibt und wie man eine Datenbank aus einem Backup wieder herstellen kann. &lt;br /&gt;
Die Backup-Funktion nutzt die SQLite Online Backup API und ermöglicht es, konsistente Backups der SQLite-DB in laufenden Betrieb zu erstellen ohne die Datenbank schließen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Neben dem eigentlichen Backup kann optional einstellt werden, dass:&lt;br /&gt;
&lt;br /&gt;
* vor dem Backup eine Datenbankoptimierung (Vacuum) ausgeführt werden soll. Dadurch wird Plattenplatz freigegeben sofern möglich.&lt;br /&gt;
* vor und nach dem Backup kann ein FHEM-Kommando oder eine Perl-Routine ausgeführt werden (eine Perl-Routine ist in {} einzuschließen)&lt;br /&gt;
* das erstellte Dumpfile kann über FTP(S) an einen FTP-Server übertragen werden&lt;br /&gt;
* über die interne Versionsverwaltung kann festgelegt werden, wieviele erstellte Backups auf dem Datenträger erhalten bleiben sollen (default: 3)&lt;br /&gt;
&lt;br /&gt;
Für die Erläuterung des Beispiels soll ein DbRep-Device erstellt werden, welches:&lt;br /&gt;
&lt;br /&gt;
* die SQLite Datenbank im Betrieb (Online) sichert&lt;br /&gt;
* vor dem Dump einen Vacuum-Lauf durchführt&lt;br /&gt;
* das erstellte Dumpfile auf einen FTP-Server überträgt&lt;br /&gt;
* 2 Versionen (Dumpfiles) nach einem erfolgreichen Backuplauf im Dumpverzeichnis verbleiben sollen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== 1. Anlegen des DbRep-Devices ====&lt;br /&gt;
&lt;br /&gt;
Das anzulegende Device wird sowohl für das (regelmäßige) Backup als auch für einen eventuellen Restore verwendet.&lt;br /&gt;
Die Definition des DbRep-Devices erfolgt unter Angabe des zu verbindenden DbLog-Devices (nicht der Datenbank selbst): &lt;br /&gt;
&lt;br /&gt;
 define Rep.SQLite DbRep LogSQLITE&lt;br /&gt;
&lt;br /&gt;
Ist das Device definiert, verbindet es sich zu der Datenbank und wechselt in den state &amp;quot;connected&amp;quot;, sofern die Verbindung erfolgreich verlief.&lt;br /&gt;
Prinzipiell funktioniert der hier beschriebene Backup-Prozess mit einem im synchronous Mode betriebenen DbLog-Device, aber es ist dringend angeraten das DbLog-Device (LogSQLITE) in den asynchronen Modus umzuschalten. Dadurch werden Blockierungen von FHEM und Verluste von Daten verhindert.&lt;br /&gt;
&lt;br /&gt;
==== 2. Einstellungen des DbRep-Devices (Attribute) ====&lt;br /&gt;
&lt;br /&gt;
Für die gewünschte Funktion müssen in diesem neu definierten Device einige relevante Attribute gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;a) dumpDirLocal&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Das zu erstellende Backupfile wird per default im log-Verzeichnis ./log (meist /opt/fhem/log) des FHEM-Rechners gespeichert. Um es an einer anderen Stelle zu speichern kann das Attribut &amp;quot;dumpDirLocal&amp;quot; gesetzt werdden. Dieses Verzeichnis kann sich auf dem lokalen Datenträger befinden, oder ein per NFS gemountetes Verzeichnis sein. In jedem Fall muss der User unter dem FHEM läuft Schreibrechte auf dieses Verzeichnis besitzen.&lt;br /&gt;
&lt;br /&gt;
Für das Beispiel sollen die Dump-Files auf einen NFS-Mount einer Synology Diskstation gespeichert werden. Dazu wurde ein Mount in /etc/fstab erstellt um den Pfad &amp;quot;/sds1/backup&amp;quot; lokal verfügbar zu machen:&lt;br /&gt;
&lt;br /&gt;
 sds1.&amp;lt;fqdn&amp;gt;:/volume1/ApplicationBackup /sds1/backup nfs auto,defaults,tcp,intr 0 0&lt;br /&gt;
&lt;br /&gt;
In diesem Verzeichnis exsitiert das Directory &amp;quot;dumps_FHEM&amp;quot;, in dem die Backup-Files gespeichert werden sollen. Das Attribut wird entsprechend gesetzt auf:&lt;br /&gt;
&lt;br /&gt;
 attr Rep.SQLite dumpDirLocal /sds1/backup/dumps_FHEM&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
:&#039;&#039;&#039;b) dumpFilesKeep&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Dieser Parameter stellt die Anzahl der aufzubewahrenden Backup-Files ein. Es werden immer die &amp;quot;x&amp;quot; neuesten bzw. letzten Files im Verzeichnis gehalten, wobei nur die zu der jeweiligen Datenbank gehörenden Files betrachtet werden. &lt;br /&gt;
Die Zugehörigkeit des Dump-Files zur Quelldatenbank wird anhand des Filenamens ermittelt, der sich zusammensetzt aus &amp;lt;DB-Name ohne Endung&amp;gt;_&amp;lt;Erstellungsdatum und Uhrzeit&amp;gt;.sqlitebkp&lt;br /&gt;
&lt;br /&gt;
 Beispiel: fhem_2018_01_12_17_15.sqlitebkp&lt;br /&gt;
&lt;br /&gt;
Ist dieses Attribut nicht gesetzt, werden die 3 neuesten Backup-Files im Verzeichnis behalten.&lt;br /&gt;
Um nur 2 Files zu behalten wird das Attribut gesetzt auf:&lt;br /&gt;
&lt;br /&gt;
 attr Rep.SQLite dumpFilesKeep 2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;c) Aktionen vor und nach dem Backup ausführen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Vor und nach der Dump-Ausführung kann ein FHEM-Kommando bzw. Perl-Befehle ausgeführt werden. Perl-Befehle müssen in {} eingeschlossen werden.&lt;br /&gt;
Für das Beispiel soll vor dem Backup die Verbindung des DbLog-Devices LogSQLITE zur Datenbank getrennt werden. Dadurch werden keine neuen Daten in die Datenbank geschrieben. Wenn das DbLog-Device LogSQLITE im asynchronen Modus wie empfohlen betrieben wird, tritt auch kein Datenverlust ein, da die zu loggenden Daten im Cache verbleiben bis die Verbindung zur DB wieder hergestellt wird.&lt;br /&gt;
&lt;br /&gt;
Es sei noch einmal darauf hingewiesen, dass das Backup-Verfahren ebenso im synchronen Modus und ohne das DbLog-Device von der Datenbank zu trennen, funktioniert und nur im Beispiel zur Demonstation der Möglichkeiten dient. &lt;br /&gt;
&lt;br /&gt;
Um die Verbindung zu trennen wird ein &amp;quot;set LogSQLITE reopen &amp;lt;Zeit in Sekunden&amp;gt;&amp;quot; verwendet. Die Zeitspanne wird sehr großzügig gewählt und soll sicherstellen, dass innerhalb dieser Zeit das Backup abgeschlossen ist. Nach dem Backup wird sofort ein &amp;quot;set LogSQLITE reopen&amp;quot; ausgeführt, was die Verbindung des DbLog-Devices LogSQLITE zur Datenbank unmittelbar wieder herstellt..&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 attr Rep.SQLite executeBeforeProc set LogSQLITE reopen 3600&lt;br /&gt;
 attr Rep.SQLite executeAfterProc set LogSQLITE reopen&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;d) vor dem Backup Datenbank verkleinern (vacuum)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese optionale Funktion wird durch das Attribut &amp;quot;optimizeTablesBeforeDump&amp;quot; eingeschaltet. Dadurch die Vacuum-Funktion wird Platz innerhalb der Datenbank freigegeben der durch Löschvorgänge entstanden ist und dadurch die Größe des Datenbankfiles verringert.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.SQLite optimizeTablesBeforeDump 1&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;e) das Dump-File nach dem Backup zum FTP-Server übertragen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
DbRep bietet die Möglichkeit, das erstellte Dump-File per FTP oder verschlüsselt per FTP(S) zum Server zu übertragen. Dazu gibt es einen Satz von Attributen um dem Device alle notwendigen Angaben für den FTP-Transfer zur Verfügung zu stellen. Es gibt dafür folgende Attribute:&lt;br /&gt;
&lt;br /&gt;
* ftpUse 	: FTP Transfer nach dem Dump wird eingeschaltet (bzw. ftpUseSSL mit SSL Verschlüsselung)&lt;br /&gt;
* ftpUser 	: User zur Anmeldung am FTP-Server, default: anonymous&lt;br /&gt;
* ftpPwd 	: Passwort des FTP-Users, default nicht gesetzt&lt;br /&gt;
* ftpDir 	: Verzeichnis auf dem FTP-Server in welches das File übertragen werden soll (default: FTP-root)&lt;br /&gt;
* ftpPort 	: FTP-Port, default: 21&lt;br /&gt;
* ftpServer 	: Name oder IP-Adresse des FTP-Servers&lt;br /&gt;
* ftpTimeout 	: Timeout für die FTP-Verbindung in Sekunden (default: 30)&lt;br /&gt;
* ftpPassive 	: setzen wenn passives FTP verwendet werden soll&lt;br /&gt;
* ftpDebug 	: Debugging des FTP Verkehrs zur Fehlersuche&lt;br /&gt;
&lt;br /&gt;
Gemäß dieser Beschreibung und dem Ziel dieses Beispiels werden die für FTP-Transfer relevanten Attribute wie folgt gesetzt:&lt;br /&gt;
&lt;br /&gt;
 attr Rep.SQLite ftpDir /ftp                # Subdirectory ftp unter FTP-root als Zielverzeichnis&lt;br /&gt;
 attr Rep.SQLite ftpPwd ftpftp1&lt;br /&gt;
 attr Rep.SQLite ftpServer sds1.&amp;lt;fqdn&amp;gt;&lt;br /&gt;
 attr Rep.SQLite ftpUse 1&lt;br /&gt;
 attr Rep.SQLite ftpUser ftpuser&lt;br /&gt;
&lt;br /&gt;
Der FTP-Server muss natürlich vorab eingerichtet und funktionsfähig sein. Sollten beim FTP-Transfer Fehler auftreten, kann das Attribut ftpDebug = 1 gesetzt werden um entsprechende Ausgaben zur Analyse des Problems im Log zu erhalten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:repsqlite_defined.PNG|right|thumb|300px|Fertig definiertes Device Rep.SQLite]]&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;f) Hilfsattribute&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Für die Funktion nicht relevant aber informativ ist das Attribut &amp;quot;showproctime = 1&amp;quot;. Ist das Attribut gesetzt, wird das Reading &amp;quot;background_processing_time&amp;quot; angelegt. Es enthält nach der Ausführung die verbrauchte Prozesszeit.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das fertig konfigurierte Device hier als RAW-Definition zur einfachen Nachnutzung. Die Angaben sind natürlich anzupassen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.SQLite DbRep LogSQLITE&lt;br /&gt;
attr Rep.SQLite devStateIcon connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.SQLite dumpDirLocal /sds1/backup/dumps_FHEM&lt;br /&gt;
attr Rep.SQLite dumpFilesKeep 2&lt;br /&gt;
attr Rep.SQLite event-on-update-reading state&lt;br /&gt;
attr Rep.SQLite executeAfterProc set LogSQLITE reopen&lt;br /&gt;
attr Rep.SQLite executeBeforeProc set LogSQLITE reopen 3600&lt;br /&gt;
attr Rep.SQLite ftpDir /ftp&lt;br /&gt;
attr Rep.SQLite ftpPwd ftpftp1&lt;br /&gt;
attr Rep.SQLite ftpServer sds1.&amp;lt;fqdn&amp;gt;&lt;br /&gt;
attr Rep.SQLite ftpUse 1&lt;br /&gt;
attr Rep.SQLite ftpUser ftpuser&lt;br /&gt;
attr Rep.SQLite optimizeTablesBeforeDump 1&lt;br /&gt;
attr Rep.SQLite room DbLog&lt;br /&gt;
attr Rep.SQLite showproctime 1&lt;br /&gt;
attr Rep.SQLite verbose 3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 3. Backup durchführen ====&lt;br /&gt;
 &lt;br /&gt;
Mit dem wie beschrieben eingerichteten DbRep-Device kann das Buckup gestartet werden mit:&lt;br /&gt;
&lt;br /&gt;
 set Rep.SQLite dumpSQLite&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:logsqlite_closeduntil.PNG|right|thumb|500px|LogSQLITE ist geschlossen bis ...]]&lt;br /&gt;
&lt;br /&gt;
Zu Beginn des Vorgangs wird sofort die Verbindung des DbLog-Devices zur Datenbank getrennt.&lt;br /&gt;
Ein laufendes Backup kann mit &amp;quot;set Rep.SQLite cancelDump&amp;quot; abgebrochen werden.&lt;br /&gt;
Das laufende Backup wird im state angezeigt mit &amp;quot;SQLite Dump is running - be patient and see Logfile !&amp;quot;. Dieser Satz ist ernst gemeint, wobei der Dump über die Online-API sehr schnell arbeitet. Im Logfile mit (mindestens) verbose 3 werden die relevanten Informationen zur Verfügung gestellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2018.01.13 08:35:10.745 3: DbRep Rep.SQLite - ################################################################           &lt;br /&gt;
2018.01.13 08:35:10.746 3: DbRep Rep.SQLite - ###                    New SQLite dump                       ###&lt;br /&gt;
2018.01.13 08:35:10.747 3: DbRep Rep.SQLite - ################################################################&lt;br /&gt;
2018.01.13 08:35:10.748 3: DbRep Rep.SQLite - execute command before dump: &#039;set LogSQLITE reopen 3600&#039; &lt;br /&gt;
2018.01.13 08:35:10.883 2: DbLog LogSQLITE: Connection closed until 09:35:10 (3600 seconds).&lt;br /&gt;
2018.01.13 08:35:10.917 3: DbRep Rep.SQLite - Size of database /opt/fhem/fhem.db before optimize (MB): 2554&lt;br /&gt;
2018.01.13 08:35:10.918 3: DbRep Rep.SQLite - VACUUM database /opt/fhem/fhem.db....&lt;br /&gt;
2018.01.13 08:44:04.216 3: DbRep Rep.SQLite - Size of database /opt/fhem/fhem.db after optimize (MB): 2554&lt;br /&gt;
2018.01.13 08:44:04.280 3: DbRep Rep.SQLite - Starting dump of database &#039;fhem.db&#039;&lt;br /&gt;
2018.01.13 08:45:03.810 3: DbRep Rep.SQLite - Size of backupfile: 2553.64 MB&lt;br /&gt;
2018.01.13 08:45:04.579 3: DbRep Rep.SQLite - FTP: transferring /sds1/backup/dumps_FHEM/fhem_2018_01_13_08_44.sqlitebkp&lt;br /&gt;
2018.01.13 08:45:48.838 3: DbRep Rep.SQLite - FTP: fhem_2018_01_13_08_44.sqlitebkp transferred successfully to sds1.myds.me into dir /ftp&lt;br /&gt;
2018.01.13 08:45:48.844 3: DbRep Rep.SQLite - Deleting old dumpfile &#039;fhem_2018_01_13_08_13.sqlitebkp&#039; &lt;br /&gt;
2018.01.13 08:45:49.273 3: DbRep Rep.SQLite - Finished backup of database fhem - total time used: 638 seconds&lt;br /&gt;
2018.01.13 08:45:49.358 2: DbRep Rep.SQLite - command after dump message: &amp;quot;Reopen executed.&amp;quot; &lt;br /&gt;
2018.01.13 08:45:49.370 3: DbRep Rep.SQLite - Database dump finished successfully. &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:repsqlite_dumprunning.PNG|right|thumb|400px|das Backup läuft]]&lt;br /&gt;
&lt;br /&gt;
Setzt man das Attribut &amp;quot;ftpDebug=1&amp;quot;, erhält man im Log zusätzliche Informationen zum FTP-Transfer:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2018.01.13 08:50:22.385 3: DbRep Rep.SQLite - Size of backupfile: 2553.82 MB&lt;br /&gt;
Net::FTP&amp;gt;&amp;gt;&amp;gt; Net::FTP(2.79)&lt;br /&gt;
Net::FTP&amp;gt;&amp;gt;&amp;gt;   Exporter(5.72)&lt;br /&gt;
Net::FTP&amp;gt;&amp;gt;&amp;gt;   Net::Cmd(2.30)&lt;br /&gt;
Net::FTP&amp;gt;&amp;gt;&amp;gt;   IO::Socket::INET(1.35)&lt;br /&gt;
Net::FTP&amp;gt;&amp;gt;&amp;gt;     IO::Socket(1.38)&lt;br /&gt;
Net::FTP&amp;gt;&amp;gt;&amp;gt;       IO::Handle(1.35)&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 220 SDS1 FTP server ready.&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;gt;&amp;gt;&amp;gt; USER ftpuser&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 331 Password required for ftpuser.&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;gt;&amp;gt;&amp;gt; PASS ....&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 230 User ftpuser logged in, access restrictions apply.&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;gt;&amp;gt;&amp;gt; TYPE I&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 200 Type set to I.&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;gt;&amp;gt;&amp;gt; CWD /ftp&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 250 CWD command successful.&lt;br /&gt;
2018.01.13 08:50:22.880 3: DbRep Rep.SQLite - FTP: transferring /sds1/backup/dumps_FHEM/fhem_2018_01_13_08_49.sqlitebkp&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;gt;&amp;gt;&amp;gt; PORT 192,168,2,45,145,42&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 200 PORT command successful.&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;gt;&amp;gt;&amp;gt; ALLO 2677867520&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 202 ALLO command ignored.&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;gt;&amp;gt;&amp;gt; STOR fhem_2018_01_13_08_49.sqlitebkp&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 150 Opening BINARY mode data connection for &#039;fhem_2018_01_13_08_49.sqlitebkp&#039;.&lt;br /&gt;
Net::FTP=GLOB(0xb12ed20)&amp;lt;&amp;lt;&amp;lt; 226 Transfer complete.&lt;br /&gt;
2018.01.13 08:51:06.859 3: DbRep Rep.SQLite - FTP: fhem_2018_01_13_08_49.sqlitebkp transferred successfully to sds1.myds.me into dir /ftp&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:repsqlite_dumpfinished.PNG|right|thumb|500px|Backup ist erfolgreich abgeschlossen]]&lt;br /&gt;
&lt;br /&gt;
In vorliegenden Beispiel wird nach einem erfolgreichen Backup im state &amp;quot;Warning - dump finished, but command after dump message appeared&amp;quot; angezeigt. Diese Warnung rührt daher, dass das Reopen-Kommando eine Rückkehrinfo mitteilt, die als Problem gewertet und im Reading &amp;quot;afterdump_message&amp;quot; ausgegeben wird. In diesem Fall ist es nur eine Information.&lt;br /&gt;
&lt;br /&gt;
In den erstellten Readings wird zusammengetragen welches File mit welcher Größe ertsellt wurde, welche alten Files gelöscht wurden, Informationen zum FTP-Transfer und welche Zeit der Gesamtprozess benötgt hat.&lt;br /&gt;
 &lt;br /&gt;
Das so vorbereitete Backup kann nun z.B. täglich oder auch mit einer wesentlich kürzeren Wiederholungsperiode (alle x Stunden) über ein at-Device eingeplant werden:&lt;br /&gt;
&lt;br /&gt;
 define At.SQLite.Dump at *23:52:00 set Rep.SQLite dumpSQLite&lt;br /&gt;
&lt;br /&gt;
==== 4. Restore ====&lt;br /&gt;
&lt;br /&gt;
Sollte es einmal zur Korruption des Datenbankfiles kommen (database disk image is malformed) und Reparaturversuche erfolglos bleiben, kann ein Restore die einzigste oder vielleicht auch einfachste Möglichkeit sein die Datenbank wieder herzustellen.&lt;br /&gt;
&lt;br /&gt;
Das kann mit dem angelegten DbRep-Device im laufenden Betrieb geschehen. Auch hier ist wieder wichtig, dass das DbLog-Device LogSQLITE im asynchronen Modus betrieben wird und die Reopen-Zeit (siehe Einstellung Attribut &amp;quot;executeBeforeProc&amp;quot;) sehr großzügig eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Zum Restore gibt es den Befehl &amp;quot;set ... restoreSQLite &amp;lt;File&amp;gt;&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
[[Datei:sqliterestore_auswahl.PNG|right|thumb|500px|Auswahlmenü Files für Restore ]]&lt;br /&gt;
&lt;br /&gt;
Im FHEMWEB öffnet sich dazu eine DropDown-Liste die alle vorhandenen und zur Quelldatenbank passenden Dumpfiles auflistet. Das herzustellende File&lt;br /&gt;
kann so bequem ausgewählt werden. Die Dumpfiles müssen sich in dem Verzeichnis befinden, welches durch das Attribut &amp;quot;dumpDirLocal&amp;quot; festgelegt wurde (default (./log).  [[Datei:sqliterestore_running.PNG|left|thumb|500px|Restore läuft]]&lt;br /&gt;
&lt;br /&gt;
Der restore wird demzufolge gestartet mit:&lt;br /&gt;
&lt;br /&gt;
 set Rep.SQLite restoreSQLite &amp;lt;File&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wieder wird zu Beginn des Vorgangs das verbundene DbLog-Device von der Datenbank abgekoppelt und nach dem Restore wieder automatisch verbunden. Eine Datenbankoptimierung bzw. FTP-Transfer wird bei diesem Vorgang natürlich nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Log zeigt den Prozessverlauf:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2018.01.13 09:21:55.435 3: DbRep Rep.SQLite - ################################################################&lt;br /&gt;
2018.01.13 09:21:55.437 3: DbRep Rep.SQLite - ###             New database Restore/Recovery                ###&lt;br /&gt;
2018.01.13 09:21:55.437 3: DbRep Rep.SQLite - ################################################################&lt;br /&gt;
2018.01.13 09:21:55.438 3: DbRep Rep.SQLite - execute command before restore: &#039;set LogSQLITE reopen 3600&#039; &lt;br /&gt;
2018.01.13 09:21:55.448 2: DbLog LogSQLITE: Connection closed until 10:21:55 (3600 seconds).&lt;br /&gt;
2018.01.13 09:21:55.477 3: DbRep Rep.SQLite - Starting restore of database &#039;fhem.db&#039;&lt;br /&gt;
2018.01.13 09:30:12.533 3: DbRep Rep.SQLite - Restore of /sds1/backup/dumps_FHEM/fhem_2018_01_13_08_49.sqlitebkp into &#039;fhem.db&#039; finished - total time used: 497 seconds.&lt;br /&gt;
2018.01.13 09:30:12.685 2: DbRep Rep.SQLite - command after restore message: &amp;quot;Reopen executed.&amp;quot; &lt;br /&gt;
2018.01.13 09:30:12.700 3: DbRep Rep.SQLite - Database restore finished successfully.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Restore wurde erfolgreich abgeschlossen und die Verbindung des DbLog-Device LogSQLITE zur Datenbank wiederhergestellt. Das Logging wird nahtlos fortgeführt.&lt;br /&gt;
&lt;br /&gt;
[[Datei:sqliterestore_finished.PNG|right|thumb|300px|Restore erfolgreich abgeschlossen]]&lt;br /&gt;
&lt;br /&gt;
Es ist natürlich zu beachten, dass die Daten zwischen dem Erstellungszeitpunkt des eingespielten Backups und der aktuellen Zeit verloren sind !&lt;br /&gt;
Die bestehende (korrupte) Datenbank wird überschrieben. Deswegen ist es ratsam immer ein aktuelles (zeitnahes) Backup zu haben um den Datenverlust möglichst gering zu halten.&lt;br /&gt;
&lt;br /&gt;
==== 5. Links ====&lt;br /&gt;
* Diskussion im Forum: &amp;quot;{{Link2Forum|Topic=82674|LinkText=Erstellung konsistentes Online-Backup im laufenden Betrieb}}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Backup/Restore einer MySQL/MariaDB Datenbank im laufenden Betrieb ===&lt;br /&gt;
&lt;br /&gt;
Das Backup einer MySQL/MariaDB Datenbank (im Folgenden stellvertretend als MySQL bezeichnet) kann als Varianten&lt;br /&gt;
&lt;br /&gt;
* clientSide&lt;br /&gt;
* serverSide&lt;br /&gt;
&lt;br /&gt;
Beim &#039;&#039;&#039;clientSide&#039;&#039;&#039; Backup werden die Daten über SQL-Statements aus der Datenbank gelesen, auf dem Client (dem FHEM-Server) verarbeitet und das Dumpfile geschrieben. Es werden history- und current-Tabelle gesichert, sowie eventuell weitere angelegte Tabellen und Views. Allerdings benötigt dieser Modus umfangreichere RAM und CPU-Ressorcen auf dem Client.&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;serverSide&#039;&#039;&#039; Option wird nachfolgend beispielhaft genauer erläutert. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== serverSide Backup Option ====&lt;br /&gt;
&lt;br /&gt;
Neben dem eigentlichen Backup kann optional einstellt werden, dass:&lt;br /&gt;
&lt;br /&gt;
* vor dem Backup eine Tabellenoptimierung (optimizeTables) ausgeführt werden soll. Dadurch wird Plattenplatz freigegeben sofern möglich.&lt;br /&gt;
* vor und nach dem Backup kann ein FHEM-Kommando oder eine Perl-Routine ausgeführt werden (eine Perl-Routine ist in &#039;&#039;&#039;{ }&#039;&#039;&#039; einzuschließen)&lt;br /&gt;
* das erstellte Dumpfile kann komprimiert werden&lt;br /&gt;
* das erstellte Dumpfile kann über FTP(S) an einen FTP-Server übertragen werden&lt;br /&gt;
* über die interne Versionsverwaltung kann die Anzahl der im Backupverzeichnis zu verbleibenden Backup-Files festgelegt werden (default: 3)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Für die Erläuterung des Beispiels soll ein DbRep-Device erstellt werden, welches:&lt;br /&gt;
&lt;br /&gt;
* die MySQL Datenbank im Betrieb (Online) sichert&lt;br /&gt;
* vor dem Dump die history-Tabelle optimiert&lt;br /&gt;
* das erstellte Dumpfile auf einen FTP-Server überträgt&lt;br /&gt;
* drei Versionen (Dumpfiles) nach der Übertragung auf dem FTP-Server belässt, ältere Files werden vom FTP-Server gelöscht&lt;br /&gt;
* eine Version (Dumpfile) nach einem erfolgreichen Backuplauf im Dumpverzeichnis verbleiben sollen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 1. Anlegen des DbRep-Devices =====&lt;br /&gt;
&lt;br /&gt;
Das anzulegende Device wird sowohl für das (regelmäßige) Backup als auch für ein eventuelles Restore verwendet.&lt;br /&gt;
Die Definition des DbRep-Devices erfolgt unter Angabe des zu verbindenden DbLog-Devices (nicht der Datenbank selbst): &lt;br /&gt;
&lt;br /&gt;
 define Rep.fhemtest.Dump.ServerSide DbRep LogDB&lt;br /&gt;
&lt;br /&gt;
Ist das Device definiert, verbindet es sich zu der Datenbank und wechselt in den state &amp;quot;connected&amp;quot;, sofern die Verbindung erfolgreich verlief.&lt;br /&gt;
Prinzipiell funktioniert der hier beschriebene Backup-Prozess mit einem im synchronous Mode betriebenen DbLog-Device, aber es ist dringend angeraten das DbLog-Device (LogDB) in den asynchronen Modus umzuschalten. Dadurch werden FHEM-Blockierungen vermieden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 2. Einstellungen des DbRep-Devices (Attribute) ===== &lt;br /&gt;
&lt;br /&gt;
Für die gewünschte Funktion müssen in diesem neu definierten Device einige relevante Attribute gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;a) dumpDirRemote&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Dump wird durch den MySQL-Server erstellt und per default im Home-Verzeichnis des MySQL-Servers gespeichert.&lt;br /&gt;
Es wird die gesamte history-Tabelle (nicht current-Tabelle) im &#039;&#039;&#039;CSV-Format&#039;&#039;&#039; ohne Einschränkungen exportiert. &lt;br /&gt;
&lt;br /&gt;
Um es an einer anderen Stelle zu speichern, wird das Attribut &amp;quot;dumpDirRemote&amp;quot; gesetzt. &lt;br /&gt;
&lt;br /&gt;
Auch wenn der MySQL-Server mit auf dem FHEM-Server läuft, also lokal, wird dieses Attribut zur Veränderung des Zielverzeichnisses verwendet.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;b) dumpDirLocal&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Soll die interne Versionsverwaltung und die Dumpfilekompression des Moduls genutzt, sowie die Größe des erzeugten Dumpfiles ausgegeben werden, ist das Verzeichnis &amp;quot;dumpDirRemote&amp;quot; des MySQL-Servers auf dem Client zu mounten und im Attribut &amp;quot;dumpDirLocal&amp;quot; dem DbRep-Device bekannt zu machen.&lt;br /&gt;
Gleiches gilt wenn der FTP-Transfer nach dem Dump genutzt werden soll (Attribut &amp;quot;ftpUse&amp;quot; bzw. &amp;quot;ftpUseSSL&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Im Beispiel läuft der MySQL-Server auf einem entfernten Server und die Dump-Files werden dort im Verzeichnis&lt;br /&gt;
&lt;br /&gt;
 /volume1/ApplicationBackup&lt;br /&gt;
&lt;br /&gt;
angelegt. Dieses Verzeichnis soll auf dem FHEM-Server als Verzeichnis &amp;quot;/sds1/backup&amp;quot; gemountet werden.&lt;br /&gt;
Wenn noch nicht passiert, den NFS Client auf dem FHEM Server installieren:&lt;br /&gt;
&lt;br /&gt;
 sudo apt-get update&lt;br /&gt;
 sudo apt-get install nfs-common&lt;br /&gt;
&lt;br /&gt;
Auf dem entfernten Server wird das Verzeichnis freigegeben/exportiert.&lt;br /&gt;
Wenn noch nicht vorhanden, ist zunächst das notwendige Paket zu installieren:&lt;br /&gt;
&lt;br /&gt;
 sudo apt-get install nfs-kernel-server&lt;br /&gt;
&lt;br /&gt;
Ist das Paket vorhanden, existiert die Datei &#039;&#039;&#039;/etc/exports&#039;&#039;&#039;.&lt;br /&gt;
In der Datei &#039;&#039;&#039;/etc/exports&#039;&#039;&#039; wird das zu exportierende Verzeichnis hinzugefügt. Aus Gründen der Sicherheit wird nur die IP-Adresse des FHEM Servers für den Zugriff zugelassen:&lt;br /&gt;
&lt;br /&gt;
  /volume1/ApplicationBackup 192.168.50.33(rw,root_squash,async,no_subtree_check)&lt;br /&gt;
&lt;br /&gt;
Soll ein Netzwerk freigegeben werden, kann der Eintrag in in dieser Form erfolgen:&lt;br /&gt;
&lt;br /&gt;
 /volume1/ApplicationBackup 192.168.XXX.0/24(rw,root_squash,async,no_subtree_check)&lt;br /&gt;
&lt;br /&gt;
Danach Datei /etc/exports neu einlesen:&lt;br /&gt;
&lt;br /&gt;
 sudo exportfs -ra&lt;br /&gt;
&lt;br /&gt;
Auf dem Client Rechner wird der Mountpoint für das einzuhängende Verzeichnus erstellt:&lt;br /&gt;
&lt;br /&gt;
 sudo mkdir /sds1/backup&lt;br /&gt;
&lt;br /&gt;
Einen Eintrag in &#039;&#039;&#039;/etc/fstab&#039;&#039;&#039; erstellen:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;IP-Adresse&amp;gt;:/volume1/ApplicationBackup /sds1/backup nfs auto,defaults,tcp,intr 0 0&lt;br /&gt;
&lt;br /&gt;
Ausführen:&lt;br /&gt;
&lt;br /&gt;
 mount /sds1/backup&lt;br /&gt;
&lt;br /&gt;
In diesem Verzeichnis existiert das Directory &amp;quot;dumps_FHEM&amp;quot;, in dem die Backup-Files gespeichert werden sollen. Das Attribut wird entsprechend gesetzt auf:&lt;br /&gt;
&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide dumpDirLocal /sds1/backup/dumps_FHEM&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; &amp;lt;br&amp;gt;&lt;br /&gt;
Läuft der MySQL-Server mit FHEM lokal auf dem gleichen Server, zeigen die Attribute &#039;&#039;&#039;dumpDirRemote und dumpDirLocal&#039;&#039;&#039; auf das &#039;&#039;&#039;identische Verzeichnis&#039;&#039;&#039;. Trotzdem sind beide Attribute zu definieren.&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:&#039;&#039;&#039;b) dumpFilesKeep&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Dieser Parameter stellt die Anzahl der aufzubewahrenden Backup-Files ein. Es werden immer die &amp;quot;x&amp;quot; neuesten bzw. letzten Files im Verzeichnis gehalten, wobei nur die zu der jeweiligen Datenbank gehörenden Files betrachtet werden. &lt;br /&gt;
Die Zugehörigkeit des Dump-Files zur Quelldatenbank wird anhand des Filenamens ermittelt.&lt;br /&gt;
&lt;br /&gt;
Er setzt sich zusammen aus &amp;lt;Datenbankname&amp;gt;_history_&amp;lt;Erstellungsdatum_Uhrzeit&amp;gt;.csv&lt;br /&gt;
&lt;br /&gt;
 Beispiel: fhemtest_history_2020_05_14_03_42.csv (+.gzip falls Dumpfiles komprimiert werden) &lt;br /&gt;
&lt;br /&gt;
Ist dieses Attribut nicht gesetzt, werden die 3 neuesten Backup-Files im Verzeichnis behalten.&lt;br /&gt;
Um nur 2 Files zu behalten wird das Attribut gesetz:&lt;br /&gt;
&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide dumpFilesKeep 2&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;c) Aktionen vor und nach dem Backup ausführen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Vor und nach der Dump-Ausführung kann ein FHEM-Kommando bzw. Perl-Befehle ausgeführt werden. Perl-Befehle müssen in &#039;&#039;&#039;{ }&#039;&#039;&#039; eingeschlossen werden.&lt;br /&gt;
&lt;br /&gt;
Für das Beispiel soll vor dem Backup die Verbindung des DbLog-Devices LogDB zur Datenbank getrennt werden. Dadurch werden keine neuen Daten in die Datenbank geschrieben. Wenn das DbLog-Device LogDB im asynchronen Modus wie empfohlen betrieben wird, tritt auch kein Datenverlust ein, da die zu loggenden Daten im Cache verbleiben bis die Verbindung zur DB wiederhergestellt wird.&lt;br /&gt;
&lt;br /&gt;
Um die Verbindung zu trennen wird ein &lt;br /&gt;
&lt;br /&gt;
 set LogDB reopen &amp;lt;Zeit in Sekunden&amp;gt; &lt;br /&gt;
&lt;br /&gt;
verwendet. Die Zeitspanne wird sehr großzügig gewählt und soll sicherstellen, dass innerhalb dieser Zeit das Backup abgeschlossen ist. Nach dem Backup wird sofort &lt;br /&gt;
&lt;br /&gt;
 set LogDB reopen &lt;br /&gt;
&lt;br /&gt;
ausgeführt, was die Verbindung des DbLog-Devices LogDB zur Datenbank unmittelbar wiederherstellt.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide executeBeforeProc set LogDB reopen 3600&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide executeAfterProc set LogDB reopen&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;d) vor dem Backup Datenbank verkleinern (optimizeTables)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Diese optionale Funktion wird durch das Attribut &amp;quot;optimizeTablesBeforeDump&amp;quot; eingeschaltet. Dadurch diese Funktion wird Platz innerhalb der Datenbank freigegeben der durch Löschvorgänge entstanden ist und dadurch die Größe des Datenbankfiles verringert.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide optimizeTablesBeforeDump 1&lt;br /&gt;
&lt;br /&gt;
Durch die Tabellenoptimierung verlängert sich die Dumpzeit.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;e) das angelegte Dump-File nach dem Backup zum FTP-Server übertragen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
DbRep bietet die Möglichkeit, das erstellte Dump-File per FTP oder verschlüsselt per FTPS zum FTP-Server zu übertragen. Dazu gibt es einen Satz von Attributen um dem Device alle notwendigen Angaben für den FTP-Transfer zur Verfügung zu stellen. Es gibt dafür folgende Attribute:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* ftpUse 	: FTP Transfer nach dem Dump wird eingeschaltet (bzw. ftpUseSSL mit SSL Verschlüsselung)&lt;br /&gt;
* ftpUser 	: User zur Anmeldung am FTP-Server, default: anonymous&lt;br /&gt;
* ftpPwd 	: Passwort des FTP-Users, default nicht gesetzt&lt;br /&gt;
* ftpDir 	: Verzeichnis auf dem FTP-Server in welches das File übertragen werden soll (default: FTP-root)&lt;br /&gt;
* ftpPort 	: FTP-Port, default: 21&lt;br /&gt;
* ftpServer 	: Name oder IP-Adresse des FTP-Servers&lt;br /&gt;
* ftpTimeout 	: Timeout für die FTP-Verbindung in Sekunden (default: 30)&lt;br /&gt;
* ftpPassive 	: setzen wenn passives FTP verwendet werden soll&lt;br /&gt;
* ftpDebug 	: Debugging des FTP Verkehrs zur Fehlersuche&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Gemäß dieser Beschreibung und dem Ziel dieses Beispiels werden die für FTP-Transfer relevanten Attribute wie folgt gesetzt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide ftpDir /ftp                # Subdirectory ftp unter FTP-root als Zielverzeichnis&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide ftpPwd ftpftp1&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide ftpServer sds1.&amp;lt;fqdn&amp;gt;&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide ftpUse 1&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide ftpUser ftpuser&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der FTP-Server muss natürlich vorab eingerichtet und funktionsfähig sein. Sollten beim FTP-Transfer Fehler auftreten, kann das Attribut ftpDebug = 1 gesetzt werden um entsprechende Ausgaben zur Analyse des Problems im Log zu erhalten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:dumpmysql1.PNG|right|thumb|300px|Fertig definiertes Device Rep.fhemtest.Dump.ServerSide]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:&#039;&#039;&#039;f) Hilfsattribute&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Für die Funktion nicht relevant aber informativ ist das Attribut &amp;quot;showproctime = 1&amp;quot;. Ist das Attribut gesetzt, wird das Reading &amp;quot;background_processing_time&amp;quot; angelegt. Es enthält nach der Ausführung die verbrauchte Prozesszeit.&lt;br /&gt;
&lt;br /&gt;
Im Attribut &amp;quot;userExitFn&amp;quot; kann eine Perl-Routine hinterlegt werden, um zum Beispiel nach dem Backup eine Mail oder Telegram-Message mit dem Erfolgsstatus zu versenden. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das fertig konfigurierte Device hier als RAW-Definition zur einfachen Nachnutzung. Die Angaben sind natürlich anzupassen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.fhemtest.Dump.ServerSide DbRep LogDB&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide devStateIcon connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen .*Dump.*is.*running.*:remotecontrol/black_btn_PLAYgreen Database.*backup.*finished.*:remotecontrol/black_btn_GREEN tn_GREEN error.*:remotecontrol/black_btn_RED&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide dumpCompress 1&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide dumpDirLocal /sds1/backup/dumps_FHEM&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide dumpDirRemote /volume1/ApplicationBackup/dumps_FHEM&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide dumpFilesKeep 1&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide event-on-update-reading state&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide executeAfterProc set LogDB reopen&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide executeBeforeProc set LogDB reopen 3600&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide fastStart 1&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide ftpDebug 0&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide ftpDir /ftp&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide ftpDumpFilesKeep 3&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide ftpPwd ftpftp1&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide ftpServer sds1.&amp;lt;fqdn&amp;gt;&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide ftpUse 0&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide ftpUser ftpuser&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide optimizeTablesBeforeDump 1&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide showproctime 1&lt;br /&gt;
attr Rep.fhemtest.Dump.ServerSide userExitFn doafterdump&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 3. Backup durchführen ===== &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Voraussetzung ist, dass der verwendete Datenbankuser das globale Privileg &#039;&#039;&#039;FILE&#039;&#039;&#039; besitzt. &lt;br /&gt;
Falls der User dieses Recht nicht hat, kann man es mit dem definierten DbRep-Device wie folgt erledigen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;(optional) dem Datenbankuser das FILE Privileg zuweisen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Da die Rechtezuweisung nur mit einem administrativen DB-User (i.A. root) funktioniert, wird dieser User im DbRep-Device angelegt und aktiviert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 set  Rep.fhemtest.Dump.ServerSide adminCredentials &amp;lt;Admin-User&amp;gt; &amp;lt;Passwort&amp;gt;&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide useAdminCredentials 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun kann dem verwendeten Datenbankuser das FILE Privileg zugeordnet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set  Rep.fhemtest.Dump.ServerSide sqlCmd GRANT FILE ON *.* TO &#039;&amp;lt;DB-User&amp;gt;&#039;@&#039;%&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei ist &#039;&#039;&#039;&amp;lt;DB-User&amp;gt;&#039;&#039;&#039; durch den in der DbLog-Konfiguration angegebenen User zu ersetzen da die gesamte Datenbankarbeit mit diesem User erfolgt.&lt;br /&gt;
&lt;br /&gt;
Nach der Ausführung schaltet man die Nutzung des administrativen Users wieder ab mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide useAdminCredentials 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Rechte können nun überprüft werden mit&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set  Rep.fhemtest.Dump.ServerSide sqlCmd show grants;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und werden im Ergebnis dargestellt (z.B. für den DB-User fhemtest):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SqlResultRow_1  GRANTS FOR FHEMTEST@%&lt;br /&gt;
SqlResultRow_2  GRANT PROCESS, FILE, INDEX ON *.* TO &#039;fhemtest&#039;@&#039;%&#039; IDENTIFIED BY PASSWORD &#039;*...............&#039;&lt;br /&gt;
SqlResultRow_3  GRANT SELECT, INSERT, UPDATE, DELETE, ALTER, EXECUTE, SHOW VIEW ON `fhemtest`.* TO &#039;fhemtest&#039;@&#039;%&#039;&lt;br /&gt;
SqlResultRow_4  GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, INDEX ON `fhemtest`.`fhemtest` TO &#039;fhemtest&#039;@&#039;%&#039;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
:&#039;&#039;&#039;Backup starten&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[Datei:dumpmysql2.PNG|right|thumb|400px|Backup läuft ...]]&lt;br /&gt;
Mit dem wie beschrieben eingerichteten DbRep-Device kann das Backup gestartet werden mit:&lt;br /&gt;
&lt;br /&gt;
 set Rep.fhemtest.Dump.ServerSide dumpMySQL serverSide&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zu Beginn des Vorgangs wird sofort die Verbindung des DbLog-Devices zur Datenbank getrennt.&lt;br /&gt;
Ein laufendes serverSide Backup kann mit FHEM-Mitteln nicht abgebrochen werden !&lt;br /&gt;
&lt;br /&gt;
Das laufende Backup wird im state angezeigt mit &amp;quot;serverSide Dump is running - be patient and see Logfile !&amp;quot;.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im Logfile mit (mindestens) verbose 3 werden die relevanten Informationen zur Verfügung gestellt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2020.05.14 22:59:18.610 3: DbRep Rep.fhemtest.Dump.ServerSide - ########################################&lt;br /&gt;
2020.05.14 22:59:18.611 3: DbRep Rep.fhemtest.Dump.ServerSide - ###   New database serverSide dump   ###&lt;br /&gt;
2020.05.14 22:59:18.611 3: DbRep Rep.fhemtest.Dump.ServerSide - ########################################&lt;br /&gt;
2020.05.14 22:59:18.612 3: DbRep Rep.fhemtest.Dump.ServerSide - execute command before dump: &#039;set LogDB reopen 3600&#039; &lt;br /&gt;
2020.05.14 22:59:18.613 2: DbLog LogDB: Connection closed until 23:59:18 (3600 seconds).&lt;br /&gt;
2020.05.14 22:59:18.660 3: DbRep Rep.fhemtest.Dump.ServerSide - Searching for tables inside database fhemtest....&lt;br /&gt;
2020.05.14 22:59:18.664 3: DbRep Rep.fhemtest.Dump.ServerSide - Size of database fhemtest before optimize (MB): 8298.98&lt;br /&gt;
2020.05.14 22:59:18.665 3: DbRep Rep.fhemtest.Dump.ServerSide - Optimizing tables&lt;br /&gt;
2020.05.14 22:59:18.665 3: DbRep Rep.fhemtest.Dump.ServerSide - Optimizing table `current` (INNODB). It will take a while.&lt;br /&gt;
2020.05.14 22:59:19.560 3: DbRep Rep.fhemtest.Dump.ServerSide - Table 1 `current` optimized successfully.&lt;br /&gt;
2020.05.14 22:59:19.560 3: DbRep Rep.fhemtest.Dump.ServerSide - Optimizing table `history` (INNODB). It will take a while.&lt;br /&gt;
2020.05.14 23:30:09.183 3: DbRep Rep.fhemtest.Dump.ServerSide - Table 2 `history` optimized successfully.&lt;br /&gt;
2020.05.14 23:30:09.186 3: DbRep Rep.fhemtest.Dump.ServerSide - 2 tables have been optimized.&lt;br /&gt;
2020.05.14 23:30:09.253 3: DbRep Rep.fhemtest.Dump.ServerSide - Size of database fhemtest after optimize (MB): 8298.98&lt;br /&gt;
2020.05.14 23:30:09.254 3: DbRep Rep.fhemtest.Dump.ServerSide - Starting dump of database &#039;fhemtest&#039;, table &#039;history&#039;&lt;br /&gt;
2020.05.14 23:39:47.566 3: DbRep Rep.fhemtest.Dump.ServerSide - compress file /sds1/backup/dumps_FHEM/fhemtest_history_2020_05_14_23_30.csv&lt;br /&gt;
2020.05.14 23:42:33.138 3: DbRep Rep.fhemtest.Dump.ServerSide - file compressed to output file: /sds1/backup/dumps_FHEM/fhemtest_history_2020_05_14_23_30.csv.gzip&lt;br /&gt;
2020.05.14 23:42:34.784 3: DbRep Rep.fhemtest.Dump.ServerSide - input file deleted: /sds1/backup/dumps_FHEM/fhemtest_history_2020_05_14_23_30.csv&lt;br /&gt;
2020.05.14 23:42:34.785 3: DbRep Rep.fhemtest.Dump.ServerSide - Number of exported datasets: 49032159&lt;br /&gt;
2020.05.14 23:42:34.787 3: DbRep Rep.fhemtest.Dump.ServerSide - Size of backupfile: 419.73 MB&lt;br /&gt;
2020.05.14 23:42:37.082 3: DbRep Rep.fhemtest.Dump.ServerSide - FTP: transferring /sds1/backup/dumps_FHEM/fhemtest_history_2020_05_14_23_30.csv.gzip&lt;br /&gt;
2020.05.14 23:42:47.511 3: DbRep Rep.fhemtest.Dump.ServerSide - FTP: fhemtest_history_2020_05_14_23_30.csv.gzip transferred successfully to sds1.myds.me into dir /ftp&lt;br /&gt;
2020.05.14 23:42:47.558 3: DbRep Rep.fhemtest.Dump.ServerSide - Deleting old dumpfile &#039;fhemtest_history_2020_05_14_22_12.csv.gzip&#039; &lt;br /&gt;
2020.05.14 23:42:47.857 3: DbRep Rep.fhemtest.Dump.ServerSide - Finished backup of database fhemtest - total time used (hh:mm:ss): 00:43:29&lt;br /&gt;
2020.05.14 23:42:47.948 2: DbRep Rep.fhemtest.Dump.ServerSide - command message after dump: &amp;quot;Reopen executed.&amp;quot; &lt;br /&gt;
2020.05.14 23:42:47.973 3: DbRep Rep.fhemtest.Dump.ServerSide - Database dump finished successfully. &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:dumpmysql3.PNG|right|thumb|500px|Backup ist erfolgreich abgeschlossen]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In vorliegenden Beispiel wird nach einem erfolgreichen Backup im state &#039;&#039;&#039;&amp;quot;Warning - dump finished, but command after dump message appeared&amp;quot;&#039;&#039;&#039; angezeigt. &lt;br /&gt;
&lt;br /&gt;
Diese Warnung rührt daher, dass das Reopen-Kommando eine Rückkehrinfo mitteilt, die als Issue gewertet und im Reading &amp;quot;afterdump_message&amp;quot; ausgegeben wird. In diesem Fall ist es nur eine Information.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In den erstellten Readings wird zusammengetragen welches File mit welcher Größe erstellt wurde, welche alten Files gelöscht wurden, Informationen zum FTP-Transfer und welche Zeit der Gesamtprozess benötgt hat.&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das so vorbereitete Backup kann nun z.B. täglich oder auch mit einer wesentlich kürzeren Wiederholungsperiode (alle x Stunden) über ein at-Device eingeplant werden:&lt;br /&gt;
&lt;br /&gt;
 define At.MySQL.Dump at *23:52:00 set Rep.fhemtest.Dump.ServerSide dumpMySQL serverSide&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 4. Restore ===== &lt;br /&gt;
&lt;br /&gt;
Voraussetzung für das Restore ist, dass der verwendete Datenbankuser das globale Privileg &#039;&#039;&#039;FILE&#039;&#039;&#039; besitzt. &lt;br /&gt;
Falls der User dieses Recht nicht hat, kann man es mit dem definierten DbRep-Device wie folgt erledigen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;(optional) dem Datenbankuser das FILE Privileg zuweisen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Da die Rechtezuweisung nur mit einem administrativen DB-User (i.A. root) funktioniert, wird dieser User im DbRep-Device angelegt und aktiviert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 set  Rep.fhemtest.Dump.ServerSide adminCredentials &amp;lt;Admin-User&amp;gt; &amp;lt;Passwort&amp;gt;&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide useAdminCredentials 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun kann dem verwendeten Datenbankuser das FILE Privileg zugeordnet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set  Rep.fhemtest.Dump.ServerSide sqlCmd GRANT FILE ON *.* TO &#039;&amp;lt;DB-User&amp;gt;&#039;@&#039;%&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei ist &#039;&#039;&#039;&amp;lt;DB-User&amp;gt;&#039;&#039;&#039; durch den in der DbLog-Konfiguration angegebenen User zu ersetzen da die gesamte Datenbankarbeit mit diesem User erfolgt.&lt;br /&gt;
&lt;br /&gt;
Nach der Ausführung schaltet man die Nutzung des administrativen Users wieder ab mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 attr Rep.fhemtest.Dump.ServerSide useAdminCredentials 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alternativ&#039;&#039;&#039; kann die Rechtezuweisung auf der Konsole des MySQL-Servers ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo mysql -u root -p&lt;br /&gt;
GRANT File ON *.* TO &#039;&amp;lt;DB-User&amp;gt;&#039;@&#039;%&#039;;&lt;br /&gt;
exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Rechte können nun überprüft werden mit&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set  Rep.fhemtest.Dump.ServerSide sqlCmd show grants;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nun kann der Restore ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein mit der Option &#039;&#039;&#039;serverSide&#039;&#039;&#039; erstellter Dump enthält ausschließlich die Daten der history-Tabelle. &lt;br /&gt;
Der Restore kann nur in eine bestehende Datenabank und entsprechend angelegte history Tabelle erfolgen.&lt;br /&gt;
Ist nach einem Datenbankfehler die Tabelle/Datenbank zerstört, must sie zunächst mit den vorbereiteten Befehlen in den &lt;br /&gt;
[https://svn.fhem.de/trac/browser/trunk/fhem/contrib/dblog/db_create_mysql.sql SVN-Skripten] bereitgestellt werden.&lt;br /&gt;
&lt;br /&gt;
Ist die history-Tabelle noch intakt und soll nur der Inhalt ersetzt werden, muss die Tabelle vorab geleert werden da sonst unter Umständen doppelte Datensätze enstehen wenn kein primary Key verwendet ist.&lt;br /&gt;
Das kann einfach mit dem Befehl&lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; sqlCmd truncate table history&lt;br /&gt;
&lt;br /&gt;
erreicht werden. Damit werden &#039;&#039;&#039;alle&#039;&#039;&#039; Daten der history-Tabelle hochperformant gelöscht. Die Tabelle selbst bleibt erhalten. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Sollte der truncate Befehl wegen nicht ausreichender Rechte des DB-Users mit Fehler enden, kann ein administrativer Datenbankuser (i.A. &#039;root&#039;) mit dem Kommando und Attribut&lt;br /&gt;
&lt;br /&gt;
 set  &amp;lt;Name&amp;gt; adminCredentials &amp;lt;Name&amp;gt; &amp;lt;Passwort&amp;gt;&lt;br /&gt;
 attr &amp;lt;Name&amp;gt; useAdminCredentials 1&lt;br /&gt;
&lt;br /&gt;
hinterlegt und aktiviert werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Ein Restore kann mit dem angelegten DbRep-Device im laufenden Betrieb geschehen. Auch hier ist wieder wichtig, dass das DbLog-Device  im asynchronen Modus betrieben wird und die Reopen-Zeit (siehe Einstellung Attribut &amp;quot;executeBeforeProc&amp;quot;) sehr großzügig eingestellt ist. &lt;br /&gt;
&lt;br /&gt;
Zum Restore dient der Befehl &lt;br /&gt;
&lt;br /&gt;
 set &amp;lt;Name&amp;gt; restoreMySQL &amp;lt;File&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:dumpmysql4.PNG|right|thumb|500px|Auswahlmenü Files für Restore ]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im FHEMWEB öffnet sich dazu eine DropDown-Liste die alle vorhandenen und zur Quelldatenbank passenden Dumpfiles auflistet. &lt;br /&gt;
Das herzustellende File kann so bequem ausgewählt werden. Die Dumpfiles müssen sich in dem Verzeichnis befinden, welches durch das Attribut &#039;&#039;&#039;dumpDirLocal&#039;&#039;&#039; festgelegt wurde (default (./log).  [[Datei:dumpmysql5.PNG|left|thumb|500px|Restore läuft]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der restore wird demzufolge gestartet mit:&lt;br /&gt;
&lt;br /&gt;
 set Rep.fhemtest.Dump.ServerSide restoreMySQL &amp;lt;File&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Restore der history-Tabelle kann nun online im laufenden FHEM-Betrieb erfolgen.&lt;br /&gt;
Wieder wird zu Beginn des Vorgangs das verbundene DbLog-Device von der Datenbank abgekoppelt und nach dem Restore wieder automatisch verbunden. Eine Datenbankoptimierung bzw. FTP-Transfer wird bei diesem Vorgang natürlich nicht ausgeführt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Datei:dumpmysql6.PNG|left|thumb|500px|Fortschrittsanzeige in einem zweiten DbRep Device]]&lt;br /&gt;
In einem zweiten (z.B. kopierten) DbRep Device kann mit Hilfe des Befehls&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 get Rep.fhemtest.Dump.ServerSide procinfo&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
der Fortschritt des Restores überwacht werden. In der Spalte &#039;&#039;&#039;PROGRESS&#039;&#039;&#039; wird der Fortschritt in &#039;&#039;&#039;%&#039;&#039;&#039; angegeben.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Log zeigt den Ablauf des Restores:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2020.05.15 13:07:24.369 3: DbRep Rep.fhemtest.Dump.ServerSide - #######################################&lt;br /&gt;
2020.05.15 13:07:24.370 3: DbRep Rep.fhemtest.Dump.ServerSide - ###   New database Restore/Recovery ###&lt;br /&gt;
2020.05.15 13:07:24.371 3: DbRep Rep.fhemtest.Dump.ServerSide - #######################################&lt;br /&gt;
2020.05.15 13:07:24.371 3: DbRep Rep.fhemtest.Dump.ServerSide - execute command before restore: &#039;set LogDB reopen 3600&#039; &lt;br /&gt;
2020.05.15 13:07:24.429 3: DbRep Rep.fhemtest.Dump.ServerSide - uncompress file /sds1/backup/dumps_FHEM/fhemtest_history_2020_05_15_11_47.csv.gzip&lt;br /&gt;
2020.05.15 13:09:31.152 3: DbRep Rep.fhemtest.Dump.ServerSide - file uncompressed to output file: /sds1/backup/dumps_FHEM/fhemtest_history_2020_05_15_11_47.csv&lt;br /&gt;
2020.05.15 13:09:31.237 3: DbRep Rep.fhemtest.Dump.ServerSide - Size of uncompressed file: 5511.52 MB&lt;br /&gt;
2020.05.15 13:09:31.242 3: DbRep Rep.fhemtest.Dump.ServerSide - Starting restore of database &#039;fhemtest&#039;, table &#039;history&#039;. &lt;br /&gt;
2020.05.15 14:42:28.566 2: DbLog LogDB: Connection closed until 16:42:28 (7200 seconds).&lt;br /&gt;
2020.05.15 14:46:22.391 3: DbRep Rep.fhemtest.Dump.ServerSide - Restore of /volume1/ApplicationBackup/dumps_FHEM/fhemtest_history_2020_05_15_11_47.csv into &#039;fhemtest&#039;, &#039;history&#039; finished - total time used (hh:mm:ss): 01:38:57&lt;br /&gt;
2020.05.15 14:46:22.457 2: DbRep Rep.fhemtest.Dump.ServerSide - command message after restore: &amp;quot;Reopen executed.&amp;quot; &lt;br /&gt;
2020.05.15 14:46:22.481 3: DbRep Rep.fhemtest.Dump.ServerSide - Database restore finished successfully.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Datei:dumpmysql7.PNG|right|thumb|300px|Restore erfolgreich abgeschlossen]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Restore wurde erfolgreich abgeschlossen und die Verbindung des DbLog-Device LogDB zur Datenbank wiederhergestellt. Das Logging wird nahtlos fortgeführt.&lt;br /&gt;
&lt;br /&gt;
Es ist natürlich zu beachten, dass die Daten zwischen dem Erstellungszeitpunkt des eingespielten Backups und der aktuellen Zeit verloren sind !&lt;br /&gt;
Eventuell in der Tabelle &#039;&#039;&#039;history&#039;&#039;&#039; noch vorhandene Daten werden &#039;&#039;&#039;nicht&#039;&#039;&#039; überschrieben. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Speichern von Berechnungswerten in der Datenbank und Erstellen eines Plots (ab Version 7.5.1) ===&lt;br /&gt;
&lt;br /&gt;
Es sollen aus in der Datenbank vorhandenen minütlichen Leistungswerten eines Wechselrichters die maximal-, minimal- und durchschnittlichen Leistungswerte pro Tag berechnet werden und diese Ergebnisse wieder in die Datenbank geschrieben werden um daraus einen Plot zu erstellen.&lt;br /&gt;
Diese Berechnung soll immer aktuell für den laufenden Monat erfolgen.&lt;br /&gt;
&lt;br /&gt;
Die Ausgangswerte liegen in der Datenbank als minütliche kW-Einträge vor (Auszug DbRep fetchrows):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2018-01-18_15-55-49__MySTP_5000__total_pac 0.200&lt;br /&gt;
2018-01-18_15-56-50__MySTP_5000__total_pac 0.218&lt;br /&gt;
2018-01-18_16-00-14__MySTP_5000__total_pac 0.209&lt;br /&gt;
2018-01-18_16-00-39__MySTP_5000__total_pac 0.198&lt;br /&gt;
2018-01-18_16-01-39__MySTP_5000__total_pac 0.142&lt;br /&gt;
2018-01-18_16-02-39__MySTP_5000__total_pac 0.130&lt;br /&gt;
2018-01-18_16-03-39__MySTP_5000__total_pac 0.117&lt;br /&gt;
2018-01-18_16-05-39__MySTP_5000__total_pac 0.087&lt;br /&gt;
2018-01-18_16-06-39__MySTP_5000__total_pac 0.071&lt;br /&gt;
2018-01-18_16-07-39__MySTP_5000__total_pac 0.076&lt;br /&gt;
2018-01-18_16-08-39__MySTP_5000__total_pac 0.065&lt;br /&gt;
2018-01-18_16-09-39__MySTP_5000__total_pac 0.069&lt;br /&gt;
2018-01-18_16-10-39__MySTP_5000__total_pac 0.060&lt;br /&gt;
2018-01-18_16-12-39__MySTP_5000__total_pac 0.065&lt;br /&gt;
2018-01-18_16-13-39__MySTP_5000__total_pac 0.068&lt;br /&gt;
2018-01-18_16-14-39__MySTP_5000__total_pac 0.054&lt;br /&gt;
2018-01-18_16-15-39__MySTP_5000__total_pac 0.041&lt;br /&gt;
2018-01-18_16-16-39__MySTP_5000__total_pac 0.027&lt;br /&gt;
2018-01-18_16-17-39__MySTP_5000__total_pac 0.026&lt;br /&gt;
2018-01-18_16-18-39__MySTP_5000__total_pac 0.021&lt;br /&gt;
2018-01-18_16-19-39__MySTP_5000__total_pac 0.018&lt;br /&gt;
2018-01-18_16-20-39__MySTP_5000__total_pac 0.012&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für die Berechnung der Ergenisse stehen in DbRep die Kommandos maxValue, minValue und averageValue zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== 1. Anlegen des DbRep-Devices ====&lt;br /&gt;
&lt;br /&gt;
Das anzulegende Device wird für alle notwendigen Berechnungen verwendet.&lt;br /&gt;
Die Definition des DbRep-Devices erfolgt unter Angabe des zu verbindenden DbLog-Devices (nicht der Datenbank selbst): &lt;br /&gt;
&lt;br /&gt;
 define Rep.total_pac DbRep LogDB&lt;br /&gt;
&lt;br /&gt;
Ist das Device definiert, verbindet es sich zu der Datenbank und wechselt in den state &amp;quot;connected&amp;quot;, sofern die Verbindung erfolgreich verlief.&lt;br /&gt;
Prinzipiell funktioniert der hier Ablauf mit einem im synchronous Mode betriebenen DbLog-Device, aber es ist dringend angeraten das DbLog-Device (LogDB) in den asynchronen Modus umzuschalten. Dadurch werden Blockierungen von FHEM verhindert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 2. Einstellungen des DbRep-Devices (Attribute) ====&lt;br /&gt;
[[Datei:Rep.total_pac.PNG|right|thumb|400px|Device definiert]]&lt;br /&gt;
Für die gewünschte Funktion müssen in diesem neu definierten Device einige relevante Attribute gesetzt werden.&lt;br /&gt;
&lt;br /&gt;
Da die Berechnungen immer für den aktuellen Monat erstellt werden sollen, wird das Attribut &amp;quot;timestamp_begin&amp;quot; so definiert, dass immer der Beginn des aktuellen Monats dynamisch ermittelt wird. Das ist gegeben durch den Eintrag:&lt;br /&gt;
&lt;br /&gt;
 attr Rep.total_pac timestamp_begin current_month_begin&lt;br /&gt;
&lt;br /&gt;
Es soll ein Ergebniswert pro Tag erzeugt werden. Damit dies erreicht werden kann, wird das Attribut &amp;quot;aggregation&amp;quot; verwendet und auf den Wert &amp;quot;day&amp;quot; eingestellt. Würde zum Beispiel ein stündlicher Ergebniswert gewünscht sein, hätte &amp;quot;aggregation = hour&amp;quot; den benötigten Effekt.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.total_pac aggregation day&lt;br /&gt;
&lt;br /&gt;
Die Attribute &amp;quot;device&amp;quot; und &amp;quot;reading&amp;quot; legen das auszuwertende Device und Reading fest. Dabei ist zu beachten, dass im Gegensatz zur allgemein gültigen Möglichkeit devspec bzw. SQL-Wildcards zu verwenden bei dieser speziellen Detailfunktion dies nicht der Fall ist. Das heißt. es muß immer ein eindeutiges Device und ein eindeutiges Reading angegeben werden. Der Grund liegt in der Notwendigkeit, bei der Speicherung der Ergebnisdaten der Datenbank ebenfalls ein eindeutiges Device und Reading zu übergeben. In dem Beispiel wird das Device &amp;quot;MySTP_5000&amp;quot; und das Reading &amp;quot;total_pac&amp;quot; ausgewertet.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.total_pac device MySTP_5000&lt;br /&gt;
 attr Rep.total_pac reading total_pac&lt;br /&gt;
&lt;br /&gt;
Hilfreiche Attribute sind noch &amp;quot;event-on-update-reading&amp;quot; zur Begrenzung der Events, &amp;quot;showproctime&amp;quot; zur Bestimmung der Prozesszeiten und &amp;quot;allowDeletion&amp;quot; um die Möglichkeit zu haben, irrtümlich gespeicherte Berechnungsergebnisse wieder über das DbRep-Device zu löschen.&lt;br /&gt;
&lt;br /&gt;
 attr Rep.total_pac event-on-update-reading state&lt;br /&gt;
 attr Rep.total_pac showproctime 1&lt;br /&gt;
 attr Rep.total_pac allowDeletion 1&lt;br /&gt;
&lt;br /&gt;
Das so vorbereitete Device ist für die benötigten Funktionen ausreichend eingestellt. &lt;br /&gt;
Zur einfachen Nachnutzung nochfolgend die Raw-Definition des DbRep-Devices:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.total_pac DbRep LogDB&lt;br /&gt;
attr Rep.total_pac aggregation day&lt;br /&gt;
attr Rep.total_pac allowDeletion 1&lt;br /&gt;
attr Rep.total_pac device MySTP_5000&lt;br /&gt;
attr Rep.total_pac event-on-update-reading state&lt;br /&gt;
attr Rep.total_pac reading total_pac&lt;br /&gt;
attr Rep.total_pac room DbLog&lt;br /&gt;
attr Rep.total_pac showproctime 1&lt;br /&gt;
attr Rep.total_pac timestamp_begin current_month_begin&lt;br /&gt;
attr Rep.total_pac verbose 3&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== 3. Berechnung ausführen und Speicherung der Ergebnisse ====&lt;br /&gt;
[[Datei:average.PNG|right|thumb|300px|Average Berechnung]]&lt;br /&gt;
Die benötigten Funktionen sind:&lt;br /&gt;
&lt;br /&gt;
 averageValue - Berechnung des täglichen Durchschnittswert der Wechselrichterleistung (kW)&lt;br /&gt;
 maxValue - Berechnung des täglichen Maximalwertes der Wechselrichterleistung (kW)&lt;br /&gt;
 minValue - Berechnung des täglichen Minimalwertes der Wechselrichterleistung (kW)&lt;br /&gt;
&lt;br /&gt;
[[Datei:minval.PNG|right|thumb|300px|MinVal Berechnung]]&lt;br /&gt;
Für jede dieser Kommandos stehen im DbRep die Optionen &amp;quot;display&amp;quot; und &amp;quot;writeToDB&amp;quot; zur Verfügung. Die Option &amp;quot;display&amp;quot; stellt die Ergenisse lediglich im Browser dar und kann dazu die Ergebnisse vor der Speicherung zu bewerten.&lt;br /&gt;
So ergeben die Kommandos:&lt;br /&gt;
&lt;br /&gt;
 set Rep.total_pac minValue display &lt;br /&gt;
 set Rep.total_pac maxValue display &lt;br /&gt;
 set Rep.total_pac avarageValue display &lt;br /&gt;
&lt;br /&gt;
die täglichen Min-, Max- und Durchschnittswerte der WR-Leistung.&lt;br /&gt;
&lt;br /&gt;
[[Datei:maxval.PNG|right|thumb|300px|MaxVal Berechnung]]&lt;br /&gt;
Entsprechen die Ergebnisse den Erwartungen bzw. dem Ziel, können die folgenden Kommandos verwendet werden um die Berechnung durchzuführen und dabei gleichzeitig in die Datenbank zu schreiben. &lt;br /&gt;
&lt;br /&gt;
 set Rep.total_pac minValue writeToDB&lt;br /&gt;
 set Rep.total_pac maxValue writeToDB&lt;br /&gt;
 set Rep.total_pac avarageValue writeToDB&lt;br /&gt;
&lt;br /&gt;
Die Daten werden in der history-Tabelle gespeichert und, sofern das DbLog-Device das Attribut &amp;quot;DbLogType = Current...&amp;quot; gestzt hat, auch in der current-Tabelle gespeichert. Im letzteren Fall kann das Ergebnisreading bei der Erstellung des Plots sofort aus der DropDown-Liste ausgewählt werden.&lt;br /&gt;
&lt;br /&gt;
Bei der Datenspeicherung wird der neue Readingnamen aus einem Präfix und dem originalen Readingnamen gebildet. Der Präfix setzt sich aus der Bildungsfunktion und der gewählten Aggregation zusammen, d.h. im Fall des Beispiels aus &amp;quot;min&amp;quot;,&amp;quot;max&amp;quot;,&amp;quot;avg&amp;quot; und &amp;quot;day&amp;quot;.&lt;br /&gt;
Der Timestamp der neuen Readings in der Datenbank wird von der eingestellten Aggregationsperiode abgeleitet, sofern kein eindeutiger Zeitpunkt des Ergebnisses bestimmt werden kann. So kann der Zeitpunkt des Maximalwertes eines Tages wertes genau bestimmt werden und der resultierende Datensatz wird mit diesem Timestamp gespeichert.&lt;br /&gt;
&lt;br /&gt;
Die resultierende Readingnamen werden somit wie folgt in der Datenbank gespeichert:&lt;br /&gt;
&lt;br /&gt;
 max_day_total_pac&lt;br /&gt;
 min_day_total_pac&lt;br /&gt;
 avg_day_total_pac&lt;br /&gt;
&lt;br /&gt;
Die Berechnungen können regelmäßig über ein at eingeplant werden. Die Funktionen vermeiden dabei die Speicherung von doppelten Datensätzen. Wurde bei einem Berechnungslauf der Datensatz bereits gespeichert, wird dieser Datensatz beim nächsten Durchlauf nicht noch einmal gespeichert.&lt;br /&gt;
&lt;br /&gt;
==== 4. Erstellung des Plots ====&lt;br /&gt;
[[Datei:dropdown.PNG|right|thumb|300px|Drop-Down Liste SVG]]&lt;br /&gt;
Wird durch das DbLog-Device die current-Tabelle genutzt, das Attribut &amp;quot;DbLogType = Current...&amp;quot; ist gesetzt, können die Ergebnisreadings max_day_total_pac, min_day_total_pac und avg_day_total_pac im SVG-Editor ausgewählt werden (siehe {{Link2CmdRef|Lang=de|Anker=SVG}} bei SVG).&lt;br /&gt;
&lt;br /&gt;
Durch die Wahl des Plot-Typ &amp;quot;bars&amp;quot; und dem SVG-Attribut &amp;quot;fixedrange = month&amp;quot; entsteht der gewünschte Tageswert-Plot über den aktuellen Monat.&lt;br /&gt;
&lt;br /&gt;
[[Datei:svg_calc.PNG|right|thumb|300px|Ergebnis SVG]]&lt;br /&gt;
Hier wiederum die Raw-Definition des SVG-Device:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod SVG_LogDB_16 SVG LogDB:SVG_LogDB_16:HISTORY&lt;br /&gt;
attr SVG_LogDB_16 fixedrange month&lt;br /&gt;
attr SVG_LogDB_16 room Energie,SVG_MySQL&lt;br /&gt;
attr SVG_LogDB_16 title &amp;quot;Max Leistung: $data{max1} kW, Min Leistung: $data{min3} kW,Max. Durchschnittsleistung: $data{max2} kW &amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sowie des gplot-Files:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2018-01-18 21:45:27&lt;br /&gt;
set terminal png transparent size &amp;lt;SIZE&amp;gt; crop&lt;br /&gt;
set output &#039;&amp;lt;OUT&amp;gt;.png&#039;&lt;br /&gt;
set xdata time&lt;br /&gt;
set timefmt &amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;&lt;br /&gt;
set xlabel &amp;quot; &amp;quot;&lt;br /&gt;
set title &#039;&amp;lt;TL&amp;gt;&#039;&lt;br /&gt;
set ytics &lt;br /&gt;
set y2tics &lt;br /&gt;
set grid&lt;br /&gt;
set ylabel &amp;quot;kW&amp;quot;&lt;br /&gt;
set y2label &amp;quot;kW&amp;quot;&lt;br /&gt;
set yrange [0:6]&lt;br /&gt;
set y2range [0:6]&lt;br /&gt;
&lt;br /&gt;
#LogDB MySTP_5000:max_day_total_pac&lt;br /&gt;
#LogDB MySTP_5000:avg_day_total_pac&lt;br /&gt;
#LogDB MySTP_5000:min_day_total_pac&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Max-Leistung / Tag&#039; ls l0fill lw 1 with bars,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Average / Tag&#039; ls l2fill lw 1 with bars,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Min-Leistung / Tag&#039; ls l1fill lw 1 with bars&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Öffnungszeiten von Fenster/Türen aus der Datenbank ermitteln ===&lt;br /&gt;
&lt;br /&gt;
Ziel ist es, die Öffnungszeit eines Fensters oder einer Tür zu ermitteln sobald es wieder geschlossen wird und ein Reading mit der Zeit in dem entsprechenden Device (Fensterkontakt) zu setzen.&lt;br /&gt;
&lt;br /&gt;
Für das Beispiel wird angenommen dass:&lt;br /&gt;
&lt;br /&gt;
* der Status der Sensoren als &amp;quot;open&amp;quot; und &amp;quot;close&amp;quot; in der Datenbank gespeichert ist&lt;br /&gt;
* das verwendetet DbRep-Device &amp;quot;Rep.WindowOpenTime&amp;quot; heißt&lt;br /&gt;
* das verwendete DbLog-Device &amp;quot;LogDBShort&amp;quot; heißt (wird mit dem DbRep-Device bei der Definition assoziiert)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zunächst wird zur Auswertung von closed-Events ein entsprechendes Notify definiert (Raw-Format):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod N.WindowClosed notify (.*fenster.*|.*terrasse.tuer.*):closed \&lt;br /&gt;
  { \&lt;br /&gt;
    if($NAME =~ /(.*fenster.*|.*terrasse.tuer.*)/) {\&lt;br /&gt;
      fhem(&amp;quot;set LogDBShort commitCache;; defmod calcse at +00:00:10 {SwitchEvent(\&amp;quot;$NAME\&amp;quot;)};;&amp;quot;);; \&lt;br /&gt;
    }\&lt;br /&gt;
  }&lt;br /&gt;
attr N.WindowClosed alias Starte Ermittlung der Fenster Öffnungszeiten&lt;br /&gt;
attr N.WindowClosed comment Wird verwendet um die letzte Öffnungszeit der Fenster aus der Datenbank zu ermitteln und im Reading &amp;quot;LastOpenTime&amp;quot; abzulegen.&lt;br /&gt;
attr N.WindowClosed room Datenbank-&amp;gt;Produktiv&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zusätzlich if-Abfrage im Notify behob ein Problem bei den Fensterkontakten die mit einem Thermostat gepeert sind, da in diesem Fall der Event mehrfach auftrat. Der Teil &#039;&#039;&#039;&amp;quot;set LogDBShort commitCache&amp;quot;&#039;&#039;&#039; speichert den Cache der Datenbank vor der Berechnung sofern sie im asynchronen Modus betrieben wird (kann entfallen wenn im synchronen Mode betrieben). Um der DB Zeit zur Abspeicherung zu geben, wird die Berechnung über ein AT mit einer entsprechenden Verzögerung (hier 10 Sekunden) angestartet. Die Zeit im AT bitte entsprechend der eigenen Systembedingungen anpassen.  &lt;br /&gt;
&lt;br /&gt;
Im Notify wird die Funktion &#039;&#039;&#039;SwitchEvent($NAME)&#039;&#039;&#039; aufgerufen. Diese Funktion wird in der 99_myUtils.pm eingefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
##########################################################################################################&lt;br /&gt;
#  This function is used to start an SQL query for the given device &amp;quot;$name&amp;quot; and check&lt;br /&gt;
#  how much time the device was in state defined by $usedVal1 today.&lt;br /&gt;
#  The query is done by a DbRep device.&lt;br /&gt;
# &lt;br /&gt;
#  In this example the name of the DbRep device is &amp;quot;Rep.WindowOpenTime&amp;quot;&lt;br /&gt;
##########################################################################################################&lt;br /&gt;
sub SwitchEvent($) {&lt;br /&gt;
  my ($name) = @_;&lt;br /&gt;
 &lt;br /&gt;
  Log3 $name , 5, &amp;quot;$name - SwitchEvent called&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
  my $usedReading = &amp;quot;\&amp;quot;state\&amp;quot;&amp;quot;;           # name of the reading which holds the state which has to be checked&lt;br /&gt;
  my $usedVal1 = &amp;quot;\&amp;quot;open\&amp;quot;&amp;quot;;               # reading value which starts the time measuerement&lt;br /&gt;
  my $usedVal2 = &amp;quot;\&amp;quot;closed\&amp;quot;&amp;quot;;             # reading value which stops the time measurement&lt;br /&gt;
  my $device = &amp;quot;\&amp;quot;$name\&amp;quot;&amp;quot;;                # name of the device which state has to be checked&lt;br /&gt;
  my $dbRepDevice = &amp;quot;Rep.WindowOpenTime&amp;quot;;  # name of the DbRep device&lt;br /&gt;
 &lt;br /&gt;
  # The query will check for the current day the cumulated time difference between $usedVal1 and $usedVal2&lt;br /&gt;
  # the example here is a homemetic threeStateSensor (window contact) and the result of the query is&lt;br /&gt;
  # the cumulated time which the window was open (in seconds) and the name of the device whic is&lt;br /&gt;
  # used by the function SwitchQueryResult()&lt;br /&gt;
  # Example:  AZ_Fensterkontakt|433.2&lt;br /&gt;
  my $sql = &amp;quot;SET \@topen = NULL,\@closed = NULL, \@popen = NULL;;&amp;quot;.&lt;br /&gt;
            &amp;quot; SELECT  DEVICE ,SUM(TIMEDIFF(tclosed, topen)) AS duration FROM (&amp;quot;.&lt;br /&gt;
            &amp;quot; SELECT TIMESTAMP, VALUE, DEVICE,&amp;quot;.&lt;br /&gt;
            &amp;quot; \@topen   := \@popen AS topen,&amp;quot;.&lt;br /&gt;
            &amp;quot; \@closed  := IF(VALUE = $usedVal2,  TIMESTAMP, NULL) AS tclosed,&amp;quot;.&lt;br /&gt;
            &amp;quot; \@popen   := IF(VALUE = $usedVal1, TIMESTAMP, NULL) AS prev_open&amp;quot;.&lt;br /&gt;
            &amp;quot; FROM history WHERE&amp;quot;.&lt;br /&gt;
            &amp;quot; DATE(TIMESTAMP) = CURDATE() AND&amp;quot;.&lt;br /&gt;
            &amp;quot; DEVICE = $device AND&amp;quot;.&lt;br /&gt;
            &amp;quot; READING = $usedReading AND &amp;quot;.&lt;br /&gt;
            &amp;quot; (VALUE = $usedVal1 OR VALUE = $usedVal2)&amp;quot;.&lt;br /&gt;
            &amp;quot; ORDER BY  TIMESTAMP&amp;quot;.&lt;br /&gt;
            &amp;quot; ) AS tmp&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
  Log3 $name , 5, &amp;quot;$name - SwitchEvent start query: $sql&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
  fhem(&amp;quot;set $dbRepDevice sqlCmd $sql&amp;quot;); # start the query now. Result will be processed in the function SwitchQueryResult() below&lt;br /&gt;
 &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &#039;&#039;&#039;SwitchEvent()&#039;&#039;&#039; bekommt den Namen des Gerätes übergeben welches Event ausgelöst hat. Damit wird eine SQL-Abfrage für dieses Gerät gestartet und ermittelt,  wieviel Zeit (in Sekunden) für das übergebene Gerät zwischen dem Zustand &amp;quot;open&amp;quot; und &amp;quot;closed&amp;quot; verbracht wurde. &lt;br /&gt;
&lt;br /&gt;
Das verwendete DbRep-Device wird wie folgt definiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.WindowOpenTime DbRep LogDBShort&lt;br /&gt;
attr Rep.WindowOpenTime aggregation no&lt;br /&gt;
attr Rep.WindowOpenTime comment Öffnungszeit der Fenster ermitteln&lt;br /&gt;
attr Rep.WindowOpenTime initialized:control_3dot_hor_s connected:10px-kreis-gelb .*disconnect:10px-kreis-rot .*done:10px-kreis-gruen&lt;br /&gt;
attr Rep.WindowOpenTime reading state&lt;br /&gt;
attr Rep.WindowOpenTime showproctime 1&lt;br /&gt;
attr Rep.WindowOpenTime sqlResultFormat sline&lt;br /&gt;
attr Rep.WindowOpenTime event-on-update-reading state&lt;br /&gt;
attr Rep.WindowOpenTime userExitFn SwitchQueryResult&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ganz wichtig ist die Zeile &#039;&#039;&#039;attr Rep.powerOnTime userExitFn SwitchQueryResult&#039;&#039;&#039;. Hier ist die Funktion &#039;&#039;&#039;SwitchQueryResult()&#039;&#039;&#039; angegeben, die nach erfolgreicher Bearbeitung der SQL-Query aufgerufen wird. Diese Funktion wird ebenfalls in der 99_myUtils.pm eingefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
##########################################################################################################&lt;br /&gt;
# This function will be called from the DbRep device if the attribute&lt;br /&gt;
# attr Name_of_your_DbRep_device userExitFn SwitchQueryResult is set.&lt;br /&gt;
# This function create a new reading &amp;quot;LastOpenTime&amp;quot; at the device for which the query is done and&lt;br /&gt;
# set the cumulated time difference in seconds to the reading.&lt;br /&gt;
##########################################################################################################&lt;br /&gt;
sub SwitchQueryResult($$$) {&lt;br /&gt;
   my ($name,$reading,$value) = @_;  # $name is the name of the DbRep device which is calling this function&lt;br /&gt;
   my $hash = $defs{$name};&lt;br /&gt;
 &lt;br /&gt;
   # DbRep calls this function several times with different informations.&lt;br /&gt;
   # if $reading is SqlResult, $value will hold the result of the query&lt;br /&gt;
   if ($reading eq &amp;quot;SqlResult&amp;quot;) {&lt;br /&gt;
       my ($dev,$val) = split(&#039;\|&#039;,$value);                   # split the result in two parts&lt;br /&gt;
       $dev = $dev?$dev:&amp;quot;&amp;quot;;&lt;br /&gt;
       $val = $val?$val:&amp;quot;&amp;quot;;&lt;br /&gt;
       if (!$val) {&lt;br /&gt;
           $val = 0;&lt;br /&gt;
       }&lt;br /&gt;
       $val = DbRep_sec2hms($val);                            # calculate seconds to hms&lt;br /&gt;
       Log3 $name , 5, &amp;quot;DbRep $name - SwitchQueryResult splitted result: $dev $val&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
       fhem(&amp;quot;setreading $dev LastOpenTime $val&amp;quot;) if($dev);    # set the reading &amp;quot;LastOpenTime&amp;quot; with the time if device-Log was found&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion SwitchQueryResult() rechnet das Ergebnis der query aus dem DbRep Device von Sekunden in das Format hh:mm:ss um und trägt die berechnete Zeit als Reading &amp;quot;&#039;&#039;&#039;LastOpenTime&#039;&#039;&#039;&amp;quot; im jeweiligen Fensterkontakt ein.&lt;br /&gt;
&lt;br /&gt;
So kann man z.B. im Device mit dem Namen &amp;quot;eg.bad.fenster&amp;quot; das Reading &amp;quot;&#039;&#039;&#039;LastOpenTime 00:03:58&#039;&#039;&#039;&amp;quot; finden.&lt;br /&gt;
Die vorliegende Berechnung liefert immer die Zeit des letzten Öffnungszyklus des aktuellen Tages. Möchte man die Zeiten des gesamten Tages akkumulieren, kann nach der Beschreibung &amp;quot;[[Summe aller Einschaltzeiten eines Gerätes]]&amp;quot; verfahren werden.&lt;br /&gt;
&lt;br /&gt;
=== Grünlandtemperatursumme ermitteln und in SVG-Grafik darstellen ===&lt;br /&gt;
Nachfolgendes Beispiel zeigt, wie die Grünlandtemperatursumme ermittelt werden kann und die Ermittlungsergebnisse in der Datenbank gespeichert werden, um sie in einer SVG-Grafik darzustellen. Das beschriebene Verfahren kann natürlich auch für andere Anwendungsfälle angewendet werden.&lt;br /&gt;
&lt;br /&gt;
Was ist die Grünlandtemperatursumme?&lt;br /&gt;
&lt;br /&gt;
Hier die Definition aus [https://de.wikipedia.org/wiki/Grünlandtemperatursumme Wikipedia]:&lt;br /&gt;
&lt;br /&gt;
: &#039;&#039;Die Grünlandtemperatursumme (GTS) ist eine Spezialform der Wachstumsgradtage, die in der Agrarmeteorologie verwendet wird. Sie wird herangezogen, um in Mitteleuropa den Termin für das Einsetzen der Feldarbeit nach dem Winter zu bestimmen.&#039;&#039;&lt;br /&gt;
: &#039;&#039;Eine Wärmesumme ist allgemein eine gewisse Lufttemperatur eines Tages über die Tage einer Periode summiert. Dabei verwendet man besonders die kumulierte korrigierte GTS, die nach Monat gewichtet wird.&#039;&#039;&lt;br /&gt;
: &#039;&#039;Wird im Frühjahr die Summe von 200 überschritten, ist der nachhaltige Vegetationsbeginn erreicht. Hintergrund ist die Stickstoffaufnahme und -verarbeitung des Bodens, welcher von dieser Temperatursumme abhängig ist. In mittleren Breiten wird das meist im Laufe des März, an der Wende von Vorfrühling zu Mittfrühling erreicht. &#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Zur Bestimmung der GTS benötigt man zunächst die Aufzeichnung der örtlichen Temperaturwerte in einer DbLog-Datenbank. &lt;br /&gt;
Im Beispiel wird dazu das Reading &#039;&#039;&#039;temperature&#039;&#039;&#039; des Device &#039;&#039;&#039;MyWetter&#039;&#039;&#039; (Device vom Typ [[Weather]]) genutzt. Die Einrichtung des Weather- und DbLog-Devices wird an dieser Stelle nicht beschrieben und als bekannt vorausgesetzt.&lt;br /&gt;
&lt;br /&gt;
Es wird angenommen, dass:&lt;br /&gt;
* das definierte DbLog-Device im asynchronen Mode betrieben wird &lt;br /&gt;
* das Reading &#039;&#039;&#039;temperature&#039;&#039;&#039; des Device &#039;&#039;&#039;MyWetter&#039;&#039;&#039; in der DbLog-Datenbank gespeichert wird&lt;br /&gt;
&lt;br /&gt;
Zur Ermittlung des GTS wird ein DbRep-Device mit gesetztem [[Attribut]] &#039;&#039;&#039;avgDailyMeanGWSwithGTS&#039;&#039;&#039; verwendet. Dieses Attribut legt die Berechnung der täglichen Durchschnittstemperatur nach den Regularien des Deutschen Wetterdienstes fest und aktiviert auf dieser Grundlage weiterhin die Berechnung der Grünlandtemperatursumme.&lt;br /&gt;
&lt;br /&gt;
[[Datei:gts1.PNG|right|thumb|400px|GTS Ermittlung]]&lt;br /&gt;
Die RAW-Definition des verwendeten DbRep-Devices:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define Rep.GTS DbRep LogDB&lt;br /&gt;
attr Rep.GTS averageCalcForm avgDailyMeanGWSwithGTS&lt;br /&gt;
attr Rep.GTS device MyWetter&lt;br /&gt;
attr Rep.GTS fastStart 1&lt;br /&gt;
attr Rep.GTS reading temperature&lt;br /&gt;
attr Rep.GTS room DbLog&lt;br /&gt;
attr Rep.GTS showproctime 1&lt;br /&gt;
attr Rep.GTS timestamp_begin current_year_begin&lt;br /&gt;
attr Rep.GTS timestamp_end previous_day_end&lt;br /&gt;
attr Rep.GTS userExitFn saveGTS&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LogDB&#039;&#039;&#039; ist der Name des angeschlossenen DbLog-Devices und ist natürlich entsprechend anzupassen. Unbedingt wichtig ist es, den Start der Auswertung auf den Beginn des Jahres zu setzen. Das Attribut &#039;&#039;&#039;timestamp_begin = current_year_begin&#039;&#039;&#039; erfüllt diese Bedingung. Das Attribut &#039;&#039;&#039;userExitFn&#039;&#039;&#039; wird noch erläutert.&lt;br /&gt;
&lt;br /&gt;
Die Berechnung wird gestartet mit dem Befehl:&lt;br /&gt;
:&amp;lt;code&amp;gt;set Rep.GTS averageValue display&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im Ergebnis entstehen Readings die auszugsweise nachfolgend aufgelistet sind:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-11__MyWetter__temperature__AVGDMGWS__2021-02-11 -5.1&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-11__MyWetter__temperature__GrasslandTemperatureSum 40.1&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-12__MyWetter__temperature__AVGDMGWS__2021-02-12 -6.8&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-12__MyWetter__temperature__GrasslandTemperatureSum 40.1&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-13__MyWetter__temperature__AVGDMGWS__2021-02-13 insufficient values - execute get Rep.GTS versionNotes 2 for further information&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-14__MyWetter__temperature__AVGDMGWS__2021-02-14 -8.2&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-14__MyWetter__temperature__GrasslandTemperatureSum 40.1&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-15__MyWetter__temperature__AVGDMGWS__2021-02-15 -3.2&lt;br /&gt;
     2021-02-26 22:32:08   2021-02-15__MyWetter__temperature__GrasslandTemperatureSum 40.1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Meldung &amp;quot;insufficient values - execute get Rep.GTS versionNotes 2 for further information&amp;quot; ist ein Hinweis darauf, dass die für die Ermittlung der Durchschnittstagestemperaturen nach den Regularien des Deutschen Wettederdienstes geforderten Werte nicht (komplett) in der DB vorhanden sind.&lt;br /&gt;
&lt;br /&gt;
Um die Grünlandtemperatursumme im SVG-Diagramm anzuzeigen, muss dieser Wert für jeden Tag in der DB gespeichert werden. Alle benötigten Informationen sind in den DbRep-Readings, z.B.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2021-02-12__MyWetter__temperature__GrasslandTemperatureSum 40.1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
vorhanden. Der Präfix enthält das Datum, für das die jeweilige Grünlandtemperatursumme gilt, d.h., es muß nur noch ein neuer Wert zu diesem korrespondierenden Datum in die DB geschrieben werden. Das wird durch eine kleine Routine in 99_myUtils.pm erreicht, die im Attribut &#039;&#039;&#039;userExitFn&#039;&#039;&#039; des DbRep-Devices hinterlegt wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Routine in 99_myUtils.pm einfügen:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
##############################################################&lt;br /&gt;
#              speichern GTS in Datenbank&lt;br /&gt;
##############################################################&lt;br /&gt;
sub saveGTS {&lt;br /&gt;
  my $name    = shift;&lt;br /&gt;
  my $reading = shift;&lt;br /&gt;
  my $value   = shift;&lt;br /&gt;
  my $device  = &amp;quot;MyWetter&amp;quot;;  # Weather-Device -&amp;gt; anpassen !&lt;br /&gt;
  my $logdev  = &amp;quot;LogDB&amp;quot;;     # DbLOg Device   -&amp;gt; anpassen !&lt;br /&gt;
  &lt;br /&gt;
  # 2021-02-14__MyWetter__temperature__GrasslandTemperatureSum 40.1&lt;br /&gt;
  if($reading =~ /GrasslandTemperatureSum/x) {&lt;br /&gt;
      my ($date)    = (split &amp;quot;__&amp;quot;, $reading)[0];&lt;br /&gt;
	  my ($y,$m,$d) = split &amp;quot;-&amp;quot;, $date;&lt;br /&gt;
	  my $ts        = &amp;quot;$y-$m-$d 12:00:00&amp;quot;;&lt;br /&gt;
	  &lt;br /&gt;
      CommandSet(undef, &amp;quot;$logdev addCacheLine $ts|$device|calculated|calculated|GrasslandTemperatureSum|$value|&amp;quot;);  &lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Routine sind die Variablen &#039;&#039;&#039;$device&#039;&#039;&#039; und &#039;&#039;&#039;$logdev&#039;&#039;&#039; mit den realen Devices Weather und DbLog zu ersetzen.&lt;br /&gt;
&lt;br /&gt;
;Funktionsweise&lt;br /&gt;
:Ist der Auswertungslauf beendet, werden alle Readings, die &amp;quot;GrasslandTemperatureSum&amp;quot; enthalten, in ihre Bestandteile aufgesplittet, der Timestamp zum Speichern in der Datenbank formatiert und der neu in der DB zu erstellende Datensatz in den Cache des DbLog-Devices eingefügt. Der nächste Sync-Lauf in DbLog fügt die im Cache erstellten Datensätze in die Datenbank ein.&lt;br /&gt;
&lt;br /&gt;
==== Definition des SVG-Devices ====&lt;br /&gt;
[[Datei:gts2.PNG|right|thumb|400px|GTS SVG Darstellung]]&lt;br /&gt;
In der Datenbank befinden sich nun Datensätze mit dem &#039;&#039;&#039;Device MyWetter&#039;&#039;&#039; und dem &#039;&#039;&#039;Reading GrasslandTemperatureSum&#039;&#039;&#039; die im SVG Diagramm ausgewertet werden können.&lt;br /&gt;
&lt;br /&gt;
RAW-Definition des SVG-Devices:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod SVG_GTS SVG LogDB:SVG_GTS:HISTORY&lt;br /&gt;
attr SVG_GTS fixedrange month&lt;br /&gt;
attr SVG_GTS label &amp;quot;aktuelle Grünlandtemperatur $data{max1}&amp;quot;&lt;br /&gt;
attr SVG_GTS room SVG&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die dazugehörige GPLOT-Datei (SVG_GTS.gplot):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2021-02-27 08:08:30&lt;br /&gt;
set terminal png transparent size &amp;lt;SIZE&amp;gt; crop&lt;br /&gt;
set output &#039;&amp;lt;OUT&amp;gt;.png&#039;&lt;br /&gt;
set xdata time&lt;br /&gt;
set timefmt &amp;quot;%Y-%m-%d_%H:%M:%S&amp;quot;&lt;br /&gt;
set xlabel &amp;quot; &amp;quot;&lt;br /&gt;
set title &#039;&amp;lt;L1&amp;gt;&#039;&lt;br /&gt;
set ytics &lt;br /&gt;
set y2tics &lt;br /&gt;
set grid&lt;br /&gt;
set ylabel &amp;quot;Score&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Score&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#LogDB MyWetter:GrasslandTemperatureSum::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;GTS&#039; ls l1fill lw 1 with ibars&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Logging]]&lt;/div&gt;</summary>
		<author><name>Dora71</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=MQTT2_DEVICE_-_Schritt_f%C3%BCr_Schritt&amp;diff=39331</id>
		<title>MQTT2 DEVICE - Schritt für Schritt</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=MQTT2_DEVICE_-_Schritt_f%C3%BCr_Schritt&amp;diff=39331"/>
		<updated>2024-05-15T07:22:13Z</updated>

		<summary type="html">&lt;p&gt;Dora71: /* json2nameValue() */ Erklärung des 4. und 5. Parameters ergänzt inklusive Codebeispiel&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einführung ==&lt;br /&gt;
Das Protokoll [[MQTT]] ermöglicht einen flexiblen Datenaustausch zwischen unterschiedlichsten Geräten und FHEM und insbesondere auch bidirektionale Kommunikation von und zu FHEM. Es gibt dabei jedoch nur einen geringen Grad der Standardisierung des Datenaustauschs. In der Praxis sind daher relative viele unterschiedliche Wege aufzufinden, wie die Kommunikation via MQTT in den externen Geräten und Diensten konkret umgesetzt wurde - jeder Autor einer firmware oder Software kann dies so lösen, wie es ihm beliebt, und nicht jeder beherzigt dabei den Grundsatz, dass die Kommunikation via MQTT &amp;quot;leichtgewichtig&amp;quot; sein sollte.&lt;br /&gt;
&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=Grundsätzlich stehen für viele Geräte-Typen bereits Vorlagen zur Verfügung, siehe [[AttrTemplate|attrTemplate]], die einem die wesentliche Konfigurationsarbeit abnehmen können, wie sie hier beschrieben ist. Die damit jeweils erzeugten Konfigurationen sind Einrichtungsbeispiele, die v.a. eine in sich konsistenze Zusammenstellung der verschiedenen Attribute enthalten. Es steht jedem User frei, diese Ausgangsbasis dann nach seinem Belieben zu ändern. Spätere Änderungen des verwendeten attrTemplate wirken sich nicht automatisch auf die durch frühere Versionen oder den User nachkonfigurierte Geräte aus! Da es vorkommen kann, dass sich die per MQTT übermittelten Daten und Topics ändern, wenn z.B. eine firmware aktualisiert wurden, kann dies Anpassungen am jeweiligen Template erforderlich machen. Grundsätzlich sollen die per attrTemplate für MQTT2_DEVICE verfügbaren attrTemplate jeweils für die aktuellste verfügbare stabile firmware-Version passen.}}&lt;br /&gt;
&lt;br /&gt;
Das Modul [[MQTT2_DEVICE]] bietet eine Vielzahl von Möglichkeiten, auf die verschiedensten Anforderungen einzugehen, und die ein- und ausgehenden Daten zu einem oder mehreren FHEM-[[Gerät|Gerät/en]] zusammenzufassen. Eine Übersicht häufig vorkommender MQTT-Geräte ist in [[MQTT2-Module - Praxisbeispiele]] zu finden.&lt;br /&gt;
 &lt;br /&gt;
Ziel dieses Artikels ist die Darstellung der Schritte, die sich als zweckmäßig erwiesen haben zur Einrichtung von &amp;quot;guten&amp;quot; FHEM-Geräten.&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=Viele - teils komplexe Beispiele und weitere Verweise sind im {{Link2Forum|Topic=116162|LinkText=MQTT-Workshop für MQTT2-Module}} zu finden.}}Dabei soll am Ende erreicht werden:&lt;br /&gt;
* standardisierte set- (und ggf. get-)-Kommandos, insbesondere unter Beachtung der [[DevelopmentGuidelinesReadings| Developer Guidelines für Readings]]&lt;br /&gt;
* Schließen des Informationskreises von eventuellen Kommandos bis zur Rückmeldung des (externen) Gerätes oder Dienstes (im Folgenden: &amp;quot;Gegenstelle&amp;quot;)&lt;br /&gt;
* standardisierte Reading-Namen, damit möglichst Auswertungen nicht speziell an das jeweilige Gerät angepasst werden müssen&lt;br /&gt;
* Reduzierung und Vermeidung von unnötigen Datenpunkten und Events&lt;br /&gt;
* Einrichten von regelmäßigen Abfrage-Timern (falls erforderlich!).&lt;br /&gt;
&lt;br /&gt;
== Vorbereitung ==&lt;br /&gt;
=== MQTT2_SERVER ===&lt;br /&gt;
Selbst, wenn grundsätzlich ein externer MQTT-Server IO-Device zum Einsatz kommt, ist sehr zu empfehlen, für die Beschäftigung mit einem neuen, unbekannten Device zunächst einen {{Link2CmdRef|Anker=MQTT2_SERVER|Lang=en|Label=MQTT2_SERVER}} einzurichten. Ist der Port 1883 bereits belegt, nimmt man einfach einen anderen Port, z.B. 1884: &amp;lt;code&amp;gt;define m2server MQTT2_SERVER 1884 global&amp;lt;/code&amp;gt;. Sollte die Gegenstelle tiefer strukturierte Daten im JSON-Format über verschiedene Topics als Payload übermittelt, kann es ausnahmsweise hilfreich sein, &#039;&#039;&#039;in der Einrichtungsphase&#039;&#039;&#039; auch das Attribut &amp;quot;autocreate&amp;quot; am MQTT2_SERVER auf &amp;quot;complex&amp;quot; zu stellen: &amp;lt;code&amp;gt;attr m2server autocreate complex&amp;lt;/code&amp;gt;. Weiter muss die allgemeine [[Autocreate|autocreate-Instanz]] aktiv sein. Für den Regelbetrieb und für einfache, bekannte Devices (sowie für solche, für die bereits attrTemplate vorhanden sind), wird ausdrücklich empfohlen, das &#039;&#039;autocreate&#039;&#039;-Atribut am m2server gar nicht erst zu setzten, dann wird &#039;&#039;autocreate&#039;&#039; mit der (default) Einstellung &#039;&#039;simple&#039;&#039; verwendet. Die Hintergründe sind nachfolgend im Abschnitt zu &#039;&#039;json2nameValue()&#039;&#039; zu finden.&lt;br /&gt;
&lt;br /&gt;
=== Begrifflichkeiten ===&lt;br /&gt;
Ein typisches, von &amp;quot;autocreate&amp;quot; erstelltes Gerät sieht dann z.b. (mit autocreate = simple am MQTT2_SERVER) so aus:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 defmod MQTT2_DVES_9B01BD MQTT2_DEVICE DVES_9B01BD&lt;br /&gt;
 attr MQTT2_DVES_9B01BD readingList DVES_9B01BD:tele/DVES_9B01BD/STATE:.* { json2nameValue($EVENT) }\&lt;br /&gt;
    DVES_9B01BD:tele/DVES_9B01BD/LWT:.* LWT\&lt;br /&gt;
    DVES_9B01BD:tele/DVES_9B01BD/UPTIME:.* { json2nameValue($EVENT) }\&lt;br /&gt;
    DVES_9B01BD:tele/DVES_9B01BD/SENSOR:.* { json2nameValue($EVENT) }\&lt;br /&gt;
    DVES_9B01BD:tele/DVES_9B01BD/INFO1:.* { json2nameValue($EVENT) }\&lt;br /&gt;
    DVES_9B01BD:tele/DVES_9B01BD/INFO2:.* { json2nameValue($EVENT) }\&lt;br /&gt;
    DVES_9B01BD:tele/DVES_9B01BD/INFO3:.* { json2nameValue($EVENT) }\&lt;br /&gt;
    DVES_9B01BD:stat/DVES_9B01BD/RESULT:.* { json2nameValue($EVENT) }\&lt;br /&gt;
    DVES_9B01BD:stat/DVES_9B01BD/STATE:.* { json2nameValue($EVENT) }&lt;br /&gt;
 attr MQTT2_DVES_9B01BD room MQTT2_DEVICE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Wir unterscheiden bei jeder Zeile des readingList-Attributs vier Elemente, die ersten drei werden jeweils durch einen Doppelpunkt voneinander getrennt:&lt;br /&gt;
==== CID ====&lt;br /&gt;
Dies ist die Gerätekennung (hier: DVES_9B01BD). Diese ist auch Bestandteil des &#039;&#039;define&#039;&#039;, über diese wird ermittelt, zu welchem FHEM-Gerät via MQTT eingehende Informationen zugeordnet werden sollen. Diese Angabe ist weder im define noch in der readingList zwingend, aber in der Definition für den Hauptkanal eines Gerätes empfohlen. Es empfiehlt sich, die CID-Angaben bei eigenen readingList-Einträgen wegzulassen bzw. diese zu löschen. So kann einfacher zwischen automatisch generierten und eigenen Angaben unterschieden werden und die Geräte sind leichter zwischen verschiedenen IO-Modulen zu verschieben.&lt;br /&gt;
==== Topic ====&lt;br /&gt;
Datenpunkt, an den eine Information gesendet wird. Empfangsseitig sind dies hier z.B. &#039;&#039;tele/DVES_9B01BD/STATE&#039;&#039; oder &#039;&#039;tele/DVES_9B01BD/LWT&#039;&#039;.&lt;br /&gt;
Enthält der Topic Doppelpunkte oder Sonderzeichen, kann dies zu Problemen führen. Da der Topic intern als &#039;&#039;regex&#039;&#039; behandelt wird, kann man sich das in solchen Sonderfälle dadurch zu nutze machen, dass man z.B problematische Zeichen durch Punkte oder &amp;quot;beliebige Zeichenfolgen&amp;quot; ersetzt. So kann z.B. aus dem per autocreate erstellten &amp;lt;code&amp;gt;attr reader readingList SMLReader/Strom/sensor/1/obis/1-0:1.8.0/255/value:.* Strombezug_tariflos&amp;lt;/code&amp;gt; folgendes abgeleitet werden: &amp;lt;code&amp;gt;attr reader readingList SMLReader/Strom/sensor/1/obis/1-0.1.8.0/255/value:.* Strombezug_tariflos&amp;lt;/code&amp;gt;,&lt;br /&gt;
&lt;br /&gt;
==== Payload ====&lt;br /&gt;
Der jeweilige Nachrichteninhalt. Da dieser nicht im vorhinein bekannt ist, wird er in der readingList typischerweise als &amp;quot;beliebige Zeichenfolge&amp;quot; (&amp;quot;.*&amp;quot;) notiert.&lt;br /&gt;
&lt;br /&gt;
==== Auswertung ====&lt;br /&gt;
Dies kann entweder direkt der Reading-Name sein, dem die Payload zugeordnet werden soll, oder ein Perl-Ausdruck. Hier wird z.B. die eingehende Information für &#039;&#039;tele/DVES_9B01BD/LWT&#039;&#039; dem Reading &#039;&#039;LWT&#039;&#039; zugeordnet, während die Informationen aus &#039;&#039;tele/DVES_9B01BD/STATE&#039;&#039; an die Funktion &#039;&#039;json2nameValue()&#039;&#039; übergeben werden. &#039;&#039;$EVENT&#039;&#039; entspricht dabei der &#039;&#039;Payload&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== defaults ===&lt;br /&gt;
Für unbekannte Gegenstellen ist zunächst immer zu empfehlen, deren &amp;quot;Grundeinstellungen&amp;quot; zu verwenden, und Anpassungen erst und nur insoweit vorzunehmen, als es für ein besseres Zusammenspiel mit FHEM sinnvoll ist. Abzuraten ist insbesondere von:&lt;br /&gt;
* Änderungen der Topics und Topic-Sturkturen (ausgenommen den Fall, dass schon andere Gegenstellen im Einsatz sind, die identische Topics verwenden)&lt;br /&gt;
* Vergabe von &#039;&#039;friendly names&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Bestandsaufnahme ==&lt;br /&gt;
=== Projektseiten finden ===&lt;br /&gt;
Viele Geräte, die das MQTT-Protokoll verwenden, haben eigene Projektseiten oder API-Beschreibungen, denen man entnehmen kann, wie mit dieser Gegenstelle Daten ausgetauscht werden können. Diese sollte man bei allen weiteren Schritten stets zur Hand haben. Dabei kann es neben der allgemeinen Beschreibung auch ein oder mehrere Detail-Seiten geben, auf denen nähere Informationen zu den spezifischen Geräte zu finden sein können.&lt;br /&gt;
&lt;br /&gt;
=== MQTT - Datenverkehr mitlesen ===&lt;br /&gt;
Sowohl MQTT2_SERVER wie MQTT2_CLIENT bieten in der Detailansicht die Option &#039;&#039;Show MQTT traffic&#039;&#039;. Darüber läßt sich der ein- und ausgehende Datenverkehr bequem mitlesen. Bei MQTT2_CLIENT wird dabei allerdings vorausgesetzt, dass er überhaupt Daten vom MQTT-Server erhält, also insbesondere die &#039;&#039;subscriptions&#039;&#039; korrekt gesetzt sind (falls per Attribut eingeschränkt).&lt;br /&gt;
 &lt;br /&gt;
=== readingList ===&lt;br /&gt;
Zunächst empfiehlt es sich, einfach die Gegenstelle neu zu starten und (ggf. über ein FileLog) aufzuzuzeichnen, was über welchen Topic wie oft an Informationen gesendet wird. Dabei kann und sollte durchaus - sofern dies möglich ist - der eine oder andere Schaltvorgang (z.B. über das Web-Interface der Gegenstelle) durchgeführt werden, sofern dies möglich ist (oder allgemeiner: möglichst viele bekannte Anweisungen   ausführen lassen). Am Ende sollte man eine möglichst vollständige Auflistung in der readingList erhalten haben.&lt;br /&gt;
Falls strukturierte Daten im JSON-Format als Payload verwendet werden, kann man diese auch zusätzlich ohne die Verarbeitung durch &#039;&#039;json2nameValue()&#039;&#039; aufzeichnen, z.B. indem man die betreffende Zeile in der readingList doppelt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 defmod MQTT2_DVES_9B01BD MQTT2_DEVICE DVES_9B01BD&lt;br /&gt;
 attr MQTT2_DVES_9B01BD readingList tele/DVES_9B01BD/STATE:.* { json2nameValue($EVENT) }\&lt;br /&gt;
    tele/DVES_9B01BD/STATE:.* json_STATE\&lt;br /&gt;
    tele/DVES_9B01BD/LWT:.* LWT\&lt;br /&gt;
    [...]&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So kann man mit Hilfe des betreffenden Logs ggf. auch über ein externes Tool wie mosquitto_pub MQTT-Nachrichten an FHEM generieren, ohne darauf warten zu müssen, dass diese von der Gegenstelle selbst erzeugt werden.&lt;br /&gt;
&lt;br /&gt;
=== ignoreRegexp ===&lt;br /&gt;
Manche Gegenstellen senden beim Start Konfigurationsinformationen, die allerdings nicht durch FHEM ausgewertet werden können. Die betreffenden Topics sollte man (allgemein) so in die ignoreRegexp beim MQTT2_SERVER bzw. [[MQTT2_CLIENT]] aufnehmen, dass derartige Informationen künftig gar nicht mehr an MQTT2_DEVICE weitergereicht werden. Danach kann man die betreffenden Zeilen aus der readingList löschen! Entsprechendes gilt für die hierüber generierten Readings.&lt;br /&gt;
&lt;br /&gt;
Weiter ist zu empfehlen, in diese ignoreRegexp auch die Topics aufzunehmen, über die Gegenstellen typischerweise Kommandos entgegennehmen. Dies könnte z.B. so aussehen:&lt;br /&gt;
&amp;lt;code&amp;gt;attr m2server ignoreRegexp shellies/[^/]+/command|cmnd/[^/]+/|homeassistant/.*/config|tasmota/discovery&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Viele Geräte? ===&lt;br /&gt;
==== &amp;quot;split&amp;quot; ====&lt;br /&gt;
Sind auf einer Hardware mehrere &amp;quot;Hauptschalter&amp;quot; vorhanden (z.B. ein Relay-Board), ist sehr zu empfehlen, für jeden dieser &amp;quot;Hauptschalter&amp;quot; ein eigenes FHEM-Gerät anzulegen (für logische Einheiten wie Rollladenaktoren ggf. paarweise). MQTT2_DEVICE unterstützt [[DevelopmentModuleAPI#SetExtensions|SetExtensions]] und kann daher Kommandos wie &amp;quot;on-for-timer&amp;quot; über FHEM-interne Mechanismen gut umsetzen. Dies erfordert allerdings, dass die Kommandos &amp;quot;on&amp;quot; und &amp;quot;off&amp;quot; verfügbar sein müssen.&lt;br /&gt;
Wird ein Gerät gesplittet, sollten im Gerät, das den ersten (Haupt-) Kanal repräsentiert dann alle Kommunikationsdaten gebündelt werden, Querverweise zu den weiteren Kanälen kann man über das spezielle Readings &amp;quot;associatedWith&amp;quot; herstellen.&lt;br /&gt;
&lt;br /&gt;
==== bridgeRegexp ====&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=Manche derartige Interfaces müssen zunächst entsprechend konfiguriert werden, dass die zu einem Sensor oder Aktor gehörenden Daten jeweils auf einem eigenen Topic ausgegeben werden. Insbesondere ist dies bei Tasmota (ZigBee oder Bluetooth) der Fall!}}&lt;br /&gt;
In eher seltenen Fällen kommt es vor, dass eine Gegenstelle eine Art &amp;quot;Brücke&amp;quot; zu einer Mehrzahl über diese Brücke anzusteuernder (oder zu empfangender) Aktoren oder Sensoren darstellt. In diesen Fällen kann es geboten sein, die eigentliche Gegenstelle als Hauptdevice darzustellen und für jede weitere Hardware (Sensor oder Aktor) dann ein oder mehrere Einzeldevices anzulegen. Dies kann mit Hilfe des Attributs bridgeRegexp automatisiert erfolgen, wenn sich der Sensor/Aktor aus der Topic-Struktur ablesen läßt. &lt;br /&gt;
&lt;br /&gt;
== readingList optimieren==&lt;br /&gt;
=== gute Reading-Namen - Klartext ===&lt;br /&gt;
Viele Gegenstellen senden z.B. einen online/offline-Status als &amp;quot;last will and testament&amp;quot; unter einem Topic, der mit &amp;quot;LWT&amp;quot; endet. Dieses hier sendet zwar passende Daten, aber an einen anderen Topic. In solchen Fällen man kann den von autocreate erzeugten Eintrag einfach anpassen:&lt;br /&gt;
 attr DEVICE readingList /dingtian/DEVNAME/out/lwt_availability:.* LWT&lt;br /&gt;
&lt;br /&gt;
=== Bedingte Hash-Rückgaben ===&lt;br /&gt;
Manchmal erfolgt zwar die Übergabe eines Klartextes als $EVENT - allerdings nicht in der Form, wie man das in FHEM gerne hätte. Hier als &amp;quot;0&amp;quot; oder &amp;quot;1&amp;quot;. Mit etwas Perl und der Rückgabe eines Hashes kann man so etwas beliebig umformen:&lt;br /&gt;
 attr DEVICE readingList DEVNAME/relay/0:.* { $EVENT ? {state=&amp;gt;&#039;on&#039;} : {state=&amp;gt;&#039;off&#039;} }\&lt;br /&gt;
  DEVNAME/status:.* { $EVENT ? {LWT=&amp;gt;&#039;Online&#039;} : {LWT=&amp;gt;&#039;Offline&#039;} }&lt;br /&gt;
Weiteres Beispiel: Der &amp;quot;state&amp;quot; kommt in Großschreibung und soll in Kleinschreibung geändert werden:&lt;br /&gt;
 attr DEVICE readingList switchbot/esp32_2/bot/switchbottwo/state:.* { { state =&amp;gt; lc $EVENT } }&lt;br /&gt;
&lt;br /&gt;
=== json2nameValue() ===&lt;br /&gt;
Mit Hilfe der Funktion json2nameValue() (in Verbindung mit dem attribut &#039;&#039;jsonMap&#039;&#039;) lassen sich &lt;br /&gt;
* &amp;quot;gute Reading-Namen&amp;quot; auch aus JSON-Payloads erzeugen (2. und 3. Argument)&lt;br /&gt;
* unnötige Readings vorab ausfiltern (3., 4. und 5. Argument)&lt;br /&gt;
Beispiele für die Verwendung der Argumente 1 bis 3 sind der commandref in der Erläuterung des Attributs &#039;&#039;jsonMap&#039;&#039; bei MQTT2_DEVICE zu entnehmen, die (wie die Argumente 2 und 3 optionalen) Argumente 4 und 5 entsprechen einem Positiv- bzw.- Negativ-Filter.&lt;br /&gt;
Wird (übergangsweise!) &#039;&#039;autocreate&#039;&#039; in der &#039;&#039;complex&#039;&#039;-Variante eingestellt, werden für alle (bisher nicht bekannten) Topics Einträge wie dieser erzeugt:&lt;br /&gt;
 attr MQTT2_DVES_9B01BD readingList tele/DVES_9B01BD/STATE:.* { json2nameValue($EVENT,&#039;STATE_&#039;,$JSONMAP) }&lt;br /&gt;
* Das zweite Argument (&#039;&#039;STATE_&#039;&#039;) ist ein &amp;quot;Präfix&amp;quot;, der allen aus dem JSON erzeugten Readings aus diesem Topic (zunächst) vorangestellt wird. Mit dessen Hilfe läßt sich rekonsturieren, aus welchem Topic die betreffende Information ursprünglich kam. In der Regel ist dies nach der Einrichtung nicht mehr wichtig, und dieses Argument kann auf &amp;quot;nichts&amp;quot; (zwei einfache Quotes) gestellt werden. Allerdings kann es in Einzelfällen sinnvoll sein, Präfixe zu verwenden, um zwischen scheinbar gleichen Werte aus unterschiedlichen Quellen zu unterscheiden. Dies kann man erst abschließend entscheiden, wenn alle von der Gegenstelle gelieferten Daten bekannt sind.&lt;br /&gt;
* Das dritte Argument &#039;&#039;$JSONMAP&#039;&#039; kann in der Regel so belassen werden. Dann kann mit Hilfe des Attributs &#039;&#039;jsonMap&#039;&#039; eine Zuordnungstabelle für Namensänderungen für die zu generierenden Readingnamen erzeugt und/oder Werte schlicht gelöscht werden. &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl6&amp;quot;&amp;gt;&lt;br /&gt;
json2nameValue($EVENT,&#039;STATE_&#039;,$JSONMAP,&#039;positiv&#039;)&lt;br /&gt;
json2nameValue($EVENT,&#039;STATE_&#039;,$JSONMAP,&#039;&#039;,&#039;negativ&#039;)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* Das vierte Argument ist optional. Es ist ein (Positiv) Filter, der nur Readings in der Liste erscheinen lässt, die diesem Argument entsprechen, im Beispiel oben wären das alle Readings, die den Ausdruck &amp;quot;positiv&amp;quot; enthalten. Das Argument selber ist eine regexp. &#039;&#039;&#039;Achtung:&#039;&#039;&#039; Wird dieses Argument zusammen mit dem Attribut jsonMap benutzt, so wird zuerst das Mapping auf die neuen Readings-Bezeichnungen ausgeführt und erst dann dieser Filter ausgeführt, d. h. es muss auf die ersetzten Readings-Bezeichnungen gefiltert werden! &lt;br /&gt;
* Das fünfte Argument ist ebenfalls optional. Es ist ein (Negativ) Filter. Im obigen Beispiel werden alle Readings, die den Ausdruck &amp;quot;negativ&amp;quot; enthalten, aus den Readings ausgefiltert. Ebenso wie das 4. Argument ist es eine regexp. Im Zusammenspiel mit jsonMap wird hier zuerst der Filter ausgewertet und erst anschließend erfolgt das Mapping auf die neuen Readings-Namen.&lt;br /&gt;
&lt;br /&gt;
=== Auswertung unterbinden ===&lt;br /&gt;
Indem man einen Perl-Aufruf festlegt (geschweifte Klammern), dieser aber nichts zurückgibt, kann man bestimmte unerwünschte Readings ausfiltern. Im einfachsten Fall wäre dies:&lt;br /&gt;
  shellies/DEVNAME/temperature_f:.* {}&lt;br /&gt;
&lt;br /&gt;
Komplexer mit Auswertung der Payload:&lt;br /&gt;
  STATTOPIC/RESULT:.* { $EVENT =~ m{HSBColor...(\d+),(\d+),(\d+)} ? $2 eq ReadingsVal($NAME,&#039;saturation&#039;,&#039;unknown&#039;) ? return : { saturation=&amp;gt;$2 } : return }&lt;br /&gt;
&lt;br /&gt;
=== Perl ===&lt;br /&gt;
Wie am Beispiel der speziellen Funktion &amp;lt;code&amp;gt;json2nameValue()&amp;lt;/code&amp;gt; sowie der Hash-Rückgabe bereits dargestellt, ist es möglich, bei der Auswertung auch Perl-Funktionen einzusetzen, und diverse Variablen an diese zu übergeben. Ebenso ist es möglich, eigenen Perl-Code zu verwenden, der z.B. dann längere Zuordnungstabellen, Event-Reduzierungsmechanismen, ... enthalten kann.&lt;br /&gt;
&lt;br /&gt;
== Events optimieren==&lt;br /&gt;
Leider senden relativ viele Gegenstellen in ihren Standardeinstellungen sehr viele Daten, auch ohne dass sich etwas geändert hätte. Dies erzeugt uU. in FHEM eine erhebliche Last, so dass es dringend zu empfehlen ist, alle Maßnahmen zu prüfen, durch die dieses Verhalten unterbunden oder vermindert werden kann. Dabei sollte möglichst frühzeitig eingegriffen werden, entsprechend den folgenden Handlungsoptionen: Daten, die die firmware gar nicht erst sendet, muss FHEM nicht am Interface-Modul entgegennehmen. Daten, die direkt am Interface-Modul verworfen werden (ignoreRegexp), muss das Client-Modul nicht auswerten. Readings, die man (an einem bestimmten Device) nicht benötigt, sollte man nicht erzeugen, damit keine [[Eventhandler]] aktiv werden müssen, unveränderte, aber gewünschte Daten müssen nicht unbedingt (immer) Events erzeugen.&lt;br /&gt;
&lt;br /&gt;
=== firmware-Einstellungen ===&lt;br /&gt;
Bei manchen firmwares kann man einstellen, ob bzw. wie oft oder aus welchem Anlass Daten gesendet werden sollen. Es wird dringlich empfohlen, bei &amp;quot;gesprächigen&amp;quot; Gegenstellen zu recherchieren, ob und in welcher Weise diese derartige Möglichkeiten bietet. Falls solche nicht vorhanden sind, lohnt es sich, auf den betreffenden Projektseiten nachzufragen, ob es diese Optionen gibt - nicht selten hat sich ein firmware-Autor darüber schlicht noch keine Gedanken gemacht und baut in der nächsten Version ggf. entsprechende Optionen ein?&lt;br /&gt;
&lt;br /&gt;
=== Auswertung unterbinden ===&lt;br /&gt;
(s.o. für Topics/Readings, die gar nicht benötigt werden). Dies ist insbesondere auch zu empfehlen, wenn identische Daten zum gleichen Zeitpunkt sowohl als Klartext wie auch in einem JSON-Format übermittelt werden. In solchen Fällen ist es eher zu empfehlen, die JSON-Zweige zu abonnieren und den Rest (ggf. über einen ignoreRegexp-Eintrag) gar nicht auszuwerten.&lt;br /&gt;
Doppelungen sollten in jedem Fall vermieden werden!&lt;br /&gt;
Grundsätzlich erzeugt jeder Topic eine eigene Event-Loop, mehrere Readings-updates, die aus einer einzigen JSON-Payload abgeleitet werden, erzeugen dagegen eine gemeinsame Event-Loop. Durch solche Maßnahmen läßt sich die Systembelastung deutlich reduzieren.&lt;br /&gt;
Für Daten aus JSON-Payloads besteht darüber hinaus die Möglichkeit, diese komplett auszufiltern (siehe jsonMap weiter oben)&lt;br /&gt;
&lt;br /&gt;
=== event-on-change-reading und Co. ===&lt;br /&gt;
Da die firmwares häufig recht gesprächig programmiert sind, sollte man auf eine sinnvolle Begrenzung der durch Aktualisierungen verursachten Events besonderen Wert legen. Dazu ist in erster Linie das Attribut [[Event-on-change-reading|event-on-change-reading]] zu bearbeiten und ggf. passende threshold-Werte zu setzen, es empfiehlt sich allerdings, dabei auch zu untersuchen, inwieweit die weiteren, funktional ergänzenden Attribute zu setzen sind:&lt;br /&gt;
* [[Event-min-interval|event-min-interval]]&lt;br /&gt;
* [[Event-on-update-reading|event-on-update-reading]]&lt;br /&gt;
* timestamp-on-change-reading und&lt;br /&gt;
* [[Event-aggregator|event-aggregator]]&lt;br /&gt;
&lt;br /&gt;
=== gute Reading-Namen - userReadings ===&lt;br /&gt;
Manchmal werden per MQTT Werte übersendet, die so nicht direkt verwendbar sind. Beispiele hierfür wären:&lt;br /&gt;
* Batteriespannungen in mV (z.B. bei zigbee2mqtt)&lt;br /&gt;
* Farbwerte als Einzelreading (für die Anzeige in FHEM wird aber ein RGB-Wert benötigt)&lt;br /&gt;
In diesen Fällen kann man zwar nicht ohne weiteres den Ausgangswert umrechnen lassen, (über externe Module wie readingsChange sehr wohl), aber es ist über den Weg &amp;quot;userReadings&amp;quot; ohne weiteres möglich, passende Readings zu generieren. Dabei sollte aber zur Verringerung der Systembelastung allgemein sowie ggf. falscher Ergebnisse unbedingt darauf geachtet werden, dass diese auch mit einer &#039;&#039;trigger&#039;&#039;-Angabe versehen sind!&lt;br /&gt;
&lt;br /&gt;
== setList ==&lt;br /&gt;
&lt;br /&gt;
=== Allgemeines ===&lt;br /&gt;
Die setList ist dazu gedacht, Kommandos zu definieren, welche an die Gegenstelle gesendet werden können. &lt;br /&gt;
&lt;br /&gt;
=== Syntax ===&lt;br /&gt;
In der Regel besteht jede Zeile aus folgenden, per Leerzeichen getrennten Argumenten: &lt;br /&gt;
* einem setter-Namen, ggf. ergänzt durch ein widget (s.u.)&lt;br /&gt;
* einem Topic, unter dem die Payload gesendet werden soll und&lt;br /&gt;
* der Payload.&lt;br /&gt;
Es können diverse Variablen genutzt werden, u.A. auch $EVENT und $EVTPARTx-Elemente, die der Rückgabe (setter-Name und ggf. gesetzter Wert) aus dem jeweiligen &amp;quot;widget&amp;quot; entsprechen.&lt;br /&gt;
&lt;br /&gt;
=== on und off ===&lt;br /&gt;
Damit die weitergehenden Befehle wie &#039;&#039;on-for-timer&#039;&#039; aus den [[DevelopmentModuleIntro#X_Set|SetExtensions]] funktionieren, muss ein MQTT2_DEVICE die Befehle &amp;quot;on&amp;quot; und &amp;quot;off&amp;quot; kennen. Diese sollten also - wenn es eine Art &amp;quot;Hauptschalter&amp;quot; gibt - gesondert für diesen Hauptschalter angegeben werden.&lt;br /&gt;
Hat ein Gerät mehrerer solcher &amp;quot;Hauptschalter&amp;quot;, empfiehlt es sich, für jeden dieser Schalter eine eigene MQTT2_DEVICE-Instanz anzulegen.&lt;br /&gt;
&lt;br /&gt;
==== setStateList ====&lt;br /&gt;
Gibt es in einem Device neben dem &amp;quot;Hauptschalter&amp;quot; weitere setzbare Readings (z.B. für Helligkeit und Farbe oder eine Temperatur), empfiehlt es sich v.a. dann, wenn das Gerät den Empfang (bzw. die Ausführung) von Befehlen bestätigt, nur die auf den jeweiligen Hauptschalter bezogenen &#039;&#039;set&#039;&#039;-Anweisungen in &#039;&#039;state&#039;&#039; zu schreiben. Diese (z.B. on, off und toggle) wären dann in das &#039;&#039;setStateList&#039;&#039;-Attribut aufzunehmen.&lt;br /&gt;
&lt;br /&gt;
==== setExtensionsEvent ====&lt;br /&gt;
Will man per SetExtensions realisierte laufende Timer visualisieren, empfiehlt es sich, dieses Attribut zu setzen. &lt;br /&gt;
&lt;br /&gt;
=== widgets ===&lt;br /&gt;
In der setList können prinzipiell alle in [[FHEMWEB/Widgets|Widgets]] dargestellten Eingabemöglichkeiten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Perl-Kommandos ===&lt;br /&gt;
Eigentlich ist die setList dazu gedacht, eine publish-Anweisungen über das IODev abzusetzen. Da aber generisch auch Perl-Code aufgerufen werden kann, ist dies nicht zwingend, so dass zum einen aus Perl heraus auch mehrfach-Publishes ebenso möglich sind wie beliebige &amp;quot;fhem&amp;quot;-Kommandos, die gar nichts mit MQTT zu tun haben müssen (z.B. das regelmäßige Löschen von evtl. veralteten Readings am betreffenden Gerät). Wird Text zurückgegeben, wird dies als &amp;quot;&amp;lt;topic&amp;gt; &amp;lt;payload&amp;gt;&amp;quot; interpretiert und dies gepublisht, erfolgt gar keine Rückgabe, unterbleibt dies.&lt;br /&gt;
&lt;br /&gt;
== getList ==&lt;br /&gt;
Die getList ist dazu gedacht, asynchrone Abfragen an die Gegenstelle zu ermöglichen. &lt;br /&gt;
Die Syntax dabei ist&lt;br /&gt;
* einem getter-Namen, ggf. ergänzt durch ein widget (s.u.)&lt;br /&gt;
* Reading-Name, unter dem die Antwort erwartet wird&lt;br /&gt;
* einem Topic, unter dem die Payload gesendet werden soll und&lt;br /&gt;
* (optional) der Payload.&lt;br /&gt;
Auch hier können diverse Variablen genutzt werden, und/oder das ganze für die Ausführung von Perl-Code genutzt werden.&lt;br /&gt;
&lt;br /&gt;
== periodicCmd ==&lt;br /&gt;
Für regelmäßige Aufgaben (mit mind. minütlicher Frequenz) kann eine Liste von get- oder set-Kommandos angegeben werden. Dies kann für regelmäßige Abfragen ebenso genutzt werden wie z.B. zum Löschen veralteter Informationen/Readings.&lt;br /&gt;
&lt;br /&gt;
== Abschließende Aufgaben ==&lt;br /&gt;
Um das finale Aussehen des Gerätes zu beeinflussen, stehen dann noch die weiteren allgemeinen Attribute zur Verfügung, die in [[DeviceOverview anpassen]] beschrieben sind.&lt;br /&gt;
&lt;br /&gt;
== Hinweise ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;br /&gt;
[[Kategorie:MQTT]]&lt;/div&gt;</summary>
		<author><name>Dora71</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Alexa-Fhem&amp;diff=32586</id>
		<title>Alexa-Fhem</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Alexa-Fhem&amp;diff=32586"/>
		<updated>2020-02-02T11:16:29Z</updated>

		<summary type="html">&lt;p&gt;Dora71: /* Alexa-Fhem als Service (systemd) installieren */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Randnotiz|RNTyp=r|RNText=&#039;&#039;&#039;ACHTUNG&#039;&#039;&#039;: Diese Seite beschreibt nicht mehr exakt die jeweils nötigen Amazon Seiten. Es ist etwas Interpretation und Verständnis nötig. &lt;br /&gt;
&lt;br /&gt;
Für alle, die neu einsteigen und sich mit dem FHEM Vereinsserver als Proxy anfreunden können, empfiehlt es sich hier: [[FHEM Connector für Amazon Alexa]] einzusteigen.}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;alexa-fhem&#039;&#039;&#039; ist eine in JavaScript geschriebene und auf NodeJS basierende Software, welche es ermöglicht, der digitalen Amazon Assistentin Alexa zusätzliche Skills für die Heimautomatisierung via FHEM beizubringen. Eine erste funktionierende Version wurde von [https://forum.fhem.de/index.php?action=profile;u=430 justme1968] im {{Link2Forum|Topic=60244|LinkText=Forum}} veröffentlicht.&lt;br /&gt;
Das ist eine erste Version der Dokumentation zur Installation und Einrichtung, eine Erweiterung wird sicherlich in nächster Zeit noch folgen.&lt;br /&gt;
{{Infobox Modul&lt;br /&gt;
|ModPurpose=Anbindung von FHEM an Amazon Assistent Alexa&lt;br /&gt;
|ModType=x&lt;br /&gt;
|ModTechName=&lt;br /&gt;
|ModForumArea=Frontends/Sprachsteuerung&lt;br /&gt;
|ModOwner=justme1968&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Einführung==&lt;br /&gt;
===Glossar===&lt;br /&gt;
*Echo bzw. Echo Dot (im Folgenden maskulin bezeichnet) sind die derzeit verfügbaren Geräte des Alexa-Systems &#039;&#039;&#039;BILDER EINSTELLEN - Achtung Urheberrecht&#039;&#039;&#039;&lt;br /&gt;
*AVS ist der Amazon Voice Service, d.h. die Spracherkennungskomponente des Systems.&lt;br /&gt;
{{Randnotiz|RNTyp=r|RNText=Für die Nutzung der Amazon AWS-Dienste müssen zwingend die Daten einer Kreditkarte angegeben werden. Nach gegenwärtigem Kenntnisstand sollen jedoch keine Kosten für die Nutzung der im Rahmen dieses How To beschriebenen Dienste anfallen, sofern diese in einem Rahmen genutzt werden, der selbst eine intensive private Nutzung nicht überschreitet. Der Benutzer sei an dieser Stelle auf die von Amazon veröffentlichten Preislisten verwiesen. Die Autoren dieser Anleitung und der darin beschriebenen Module übernehmen keine Haftung für eventuelle Kosten, die aus der Nutzung der AWS entstehen. }}&lt;br /&gt;
*AWS sind die Amazon Web Services, also per URL erreichbare Dienste zur Ausführung von Berechnungen etc. Im Rahmen von Alexa-Fhem wird bei AWS eine eigene JavaScript-Funktion hinterlegt, die zur Kommunikation mit dem FHEM-Server dient. Im Jargon von Amazon ist dies eine so genannte Lambda-Funktion &#039;&#039;&#039;WARUM ? Nachlesen bei Amazon&#039;&#039;&#039;.&lt;br /&gt;
*Card bezeichnet einen Eintrag in der Alexa-App, der die erkannte Sprachnachricht, sowie weitergehende Informationen über die Reaktion von Alexa enthält und Rückmeldung an Amazon erlaubt.&lt;br /&gt;
*Skill (engl. für Fertigkeit, Können) ist die Bezeichnung für eine per Spracherkennung zu bedienende Funktionalität des Alexa-Systems, z.B. zur Nachrichtenansage, Wettervorhersage oder zur Steuerung von FHEM&lt;br /&gt;
&lt;br /&gt;
===Arbeitsweise und Datenfluss===&lt;br /&gt;
[[Datei:2gpXyLN.jpg|200px|thumb|right|Grafische Darstellung der beteiligten Komponenten]]&lt;br /&gt;
Echo → AVS → AWS Lambda → alexa-fhem → AWS Lambda → AVS → Echo&lt;br /&gt;
 &lt;br /&gt;
*Der Echo (oder ein anderes Alexa/AVS fähiges Gerät)  nimmt Audiodaten auf und schickt diese an AVS  (Amazon Voice Service) zur Erkennung&lt;br /&gt;
*AVS führt die Spracherkennung durch und erzeugt ein Event mit Informationen zu den erkannten Daten&lt;br /&gt;
:*Beim Alexa SmartHome Skill sind die möglichen Sätze für die Spracherkennung relativ fest vorgegeben &lt;br /&gt;
:*Beim Alexa Custom Skill kommen die dazu nötigen Informationen aus dem &#039;&#039;Interaction Model&#039;&#039; der Alexa Skills Configuration&lt;br /&gt;
*Das Event wird an den unter &#039;&#039;Configuration&#039;&#039; in der Alexa Skills Configuration hinterlegten Endpoint geschickt&lt;br /&gt;
:*Beim Alexa SmartHome Skill ist das zwingend eine AWS Lambda Routine&lt;br /&gt;
:*Beim Alexa Custom Skill kann das im Prinzip auch ein eigener Web Service sein&lt;br /&gt;
*Das Event wird vom &amp;lt;code&amp;gt;lambda.js&amp;lt;/code&amp;gt; code an alexa-fhem weitergeleitet&lt;br /&gt;
*alexa-fhem steuert FHEM und sendet ein Antwort-Event zurück&lt;br /&gt;
*&amp;lt;code&amp;gt;lambda.js&amp;lt;/code&amp;gt; nimmt diese Antwort entgegen und gibt sie an AVS zurück&lt;br /&gt;
*AVS sogt dafür das der Echo &#039;antwortet&#039; und dass die Card in der Alexa App erscheint&lt;br /&gt;
&lt;br /&gt;
===Anmerkungen===&lt;br /&gt;
*Ein Skill hat keinen Zugriff auf die Audiodaten&lt;br /&gt;
*Mit dem Skill API kann ein Skill zu zu keiner Zeit von sich aus aktiv werden und &#039;einfach&#039; Daten an den Echo schicken oder ihn dazu bringen irgendetwas zu tun.&lt;br /&gt;
*Wenn man berücksichtigt welchen Weg die Daten insgesamt gehen, ist es erstaunlich, wie schnell die Reaktion auf einen gesprochenen Satz erfolgt.&lt;br /&gt;
&lt;br /&gt;
=== Abgrenzung des &#039;&#039;&#039;Alexa Smart Home Skills&#039;&#039;&#039; und des &#039;&#039;&#039;Alexa Custom Skills&#039;&#039;&#039; ===&lt;br /&gt;
&lt;br /&gt;
Der [[Alexa-Fhem#Smart_Home|Alexa Smart Home Skill]] ist ein Amazon-Alexa-Standard-Skill, der wesentliche Basisfunktionalitäten bereitstellt. Zu diesen gehört im Wesentlichen die Funktionalität, durch Alexa-FHEM bereitgestellte Devices im Alexa-Account des Benutzers anzulegen. Der Alexa Smart Home Skill reagiert auf gesprochene Interaktion in einem beschränkten Umfang. Beispielsweise genügt ein &amp;quot;Alexa, schalte die Wohnzimmerlampe an&amp;quot; um eine Interaktion zwischen Alexa Smart Home Skill und FHEM-Alexa auszulösen. Nach erfolgreicher Einrichtung wird dieser Skill in der Alexa-App bzw. im Web in der Rubrik &amp;quot;Smart Home&amp;quot; als Skill angezeigt.&lt;br /&gt;
&lt;br /&gt;
Der [[Alexa-Fhem#Custom|Alexa Custom Skill]] ist kein Standard-Smart-Home-Skill, sondern ein individuell entwickelter Skill, so wie alle anderen Skills auch. Er wird daher auch nicht in der Alexa-App unter der Rubrik &amp;quot;Smart Home&amp;quot; angezeigt. Gesprochene Interaktion mit diesem Skill erfolgt dadurch, dass entweder der Skill explizit gestartet wird (z.B. &amp;quot;Alexa, starte [Name des Skills]&amp;quot;) oder direkt angesprochen wird (z.B. &amp;quot;Alexa, frage [Name des Skills] wie ist der Status von [Device] &amp;quot;). Der Alexa Custom Skill befindet sich in Entwicklung und wird hinsichtlich seiner Funktionalitäten laufend weiterentwickelt. Die Einrichtung dieses Skills ist grundsätzlich optional, jedoch werden anspruchsvollere Steuerungsmöglichkeiten nur mit diesem realisiert werden können.&lt;br /&gt;
&lt;br /&gt;
==Installation==&lt;br /&gt;
{{Randnotiz|RNTyp=[g|Info]|RNText=Da die einzelnen Schritte der Anleitung an verschiedenen Stellen unterbrochen und später fortgesetzt werden müssen, empfiehlt es sich, die Anleitung einmal vollständig gelesen zu haben. Während der Konfiguration sollten alle nachfolgenden Abschnitte parallel in gleichzeitig geöffneten Browserfenstern durchgeführt werden, die jeweils bis zum Abschluss geöffnet bleiben müssen. }}&lt;br /&gt;
Grundvoraussetzung für alle folgenden Schritte ist das Vorhandensein eines Amazon-Accounts. Es wird davon ausgegangen, dass die Konten für alle im Folgenden genutzten Amazon-Dienste eingerichtet wurden.&lt;br /&gt;
&lt;br /&gt;
===node.js installieren===&lt;br /&gt;
Zunächst wird das Betriebssystem (in diesem Falle Debian oder Ubuntu) auf den aktuellen Stand gebracht:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt-get update&lt;br /&gt;
sudo apt-get upgrade&lt;br /&gt;
sudo apt-get install build-essential libssl-dev&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun muss NodeJS installiert werden. Leider ist die Version im Debian Repository deutlich zu alt, daher wird mit den folgenden Befehlen das Node Repository hinzugefügt und NodeJS (in der LTS Version) entsprechend installiert:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -&lt;br /&gt;
sudo apt-get install -y nodejs&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===node.js updaten===&lt;br /&gt;
Um node.js aus einer vorherige Installation zu updaten (Version 4 ist deprecated und in AWS Amazon nicht mehr unterstützt):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
# Zuerst alexa-fhem stoppen, dann&lt;br /&gt;
sudo apt-get remove nodejs&lt;br /&gt;
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -&lt;br /&gt;
sudo apt-get install -y nodejs&lt;br /&gt;
sudo npm install npm@latest -g&lt;br /&gt;
&lt;br /&gt;
#--&amp;gt; in Amazon Konsole NodeJs auf 8.1 umstellen (Funktion in AWS Konsole editieren, im Pulldown-Menü &amp;quot;Runtime&amp;quot; eine neuere Version auswählen, und speichern)&lt;br /&gt;
#--&amp;gt; Raspi Reboot (vielleicht nicht nötig)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Alexa-Fhem installieren ===&lt;br /&gt;
&#039;&#039;&#039;Aus gegebenem Anlass: Dies ist weder eine Einführung in Linux, noch eine Anfängerdokumentation für FHEM.&#039;&#039;&#039; Also erst die Grundlagen lernen, und dann mit Alexa beginnen!&lt;br /&gt;
&lt;br /&gt;
Die aktuelle Version ist jeweils  {{Link2Forum|Topic=81324|Message=733986|LinkText=hier}} zu finden.&lt;br /&gt;
Wer bisher noch keinen Alexa-FHEM Skill angelegt hat, bitte {{Link2Forum|Topic=81324|Message=733986|LinkText=diesen Forumsbeitrag}} beachten!&lt;br /&gt;
&lt;br /&gt;
====Erstinstallation====&lt;br /&gt;
Hier wird die Erstinstallation von Alexa-Fhem beschrieben.&lt;br /&gt;
===== Linux =====&lt;br /&gt;
# Die tgz-Datei unter Linux im Hauptverzeichnis von FHEM (typischerweise &amp;lt;code&amp;gt;/opt/fhem&amp;lt;/code&amp;gt;) entpacken (&#039;&#039;nicht&#039;&#039; unter Windows, das zerstört die Rechteeinstellungen).&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;tar -xvzf dateiname.tgz&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Das dabei entstandene Verzeichnis &#039;&#039;package&#039;&#039; in &#039;&#039;alexa-fhem&#039;&#039; umbenennen &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;mv package alexa-fhem&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Durch &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;cd alexa-fhem&amp;lt;/syntaxhighlight&amp;gt; in das Verzeichnis wechseln&lt;br /&gt;
# Mit &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;npm install&amp;lt;/syntaxhighlight&amp;gt; alle Abhängigkeiten installieren (kein sudo!).&lt;br /&gt;
{{Randnotiz|RNTyp=[g|Info]|RNText=createKey.sh erzeugt ein Zertifikat, das 365 Tage gültig ist. Dies muss deswegen jedes Jahr erneuert werden }}&lt;br /&gt;
# SSL Zertifikat erzeugen durch Aufruf von &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;./createKey.sh&amp;lt;/syntaxhighlight&amp;gt; (kein sudo!). Hierbei beachten, dass ein Kennwort vergeben werden muss, das mindestens aus 4 Zeichen besteht, dieses Kennwort bitte merken.&lt;br /&gt;
# Das Verzeichnis &#039;&#039;.alexa&#039;&#039; anlegen, &#039;&#039;und zwar im Home-Verzeichnis desjenigen Benutzers, unter dem Alexa-Fhem laufen soll.&#039;&#039; Insbesondere ist zu beachten, dass dieser Nutzer u.U. im Startskript explizit gesetzt wird. Mit dem untenstehenden Skript ist das &#039;&#039;nicht&#039;&#039; der User fhem, sondern der User &#039;&#039;pi&#039;&#039;. Das Symbol &#039;&#039;~/&#039;&#039; verweist auf das Home-Verzeichnis des Benutzers, der gerade die Installation vornimmt.&lt;br /&gt;
# Die Datei &#039;&#039;config-sample.json&#039;&#039; nach &#039;&#039;.alexa/config.json&#039;&#039; kopieren. Achtung: Installiert man alexa-fhem als root-user, zeigt das Symbol &#039;&#039;~/&#039;&#039; auf &#039;&#039;/root&#039;&#039; - und die Konfigurationsdatei wird ggf. bei einem manuellen Start von Alexa-Fhem nicht gefunden.&lt;br /&gt;
# Achtung: Ggf. müssen auch die Dateien key.pem und cert.pem ins entsprechende Verzeichnis kopiert werden.&lt;br /&gt;
===== Windows =====&lt;br /&gt;
&#039;&#039;Vor&#039;&#039; der Installation von Alexa-Fhem muss man folgende Anwendungen installieren:&lt;br /&gt;
* Node.js (die aktuelle Version findet man unter https://nodejs.org/en/download/)&lt;br /&gt;
* OpenSSL (http://slproweb.com/products/Win32OpenSSL.html oder https://www.heise.de/download/product/win32-openssl-47316/download)&lt;br /&gt;
Erst dann fängt man mit Alexa-Fhem an.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# Die tgz-Datei im Hauptverzeichnis von FHEM (z.B. &amp;lt;code&amp;gt;С:\Program Files (x86)\fhem&amp;lt;/code&amp;gt;) entpacken.&lt;br /&gt;
# Das dabei entstandene Verzeichnis &#039;&#039;package&#039;&#039; in &#039;&#039;alexa-fhem&#039;&#039; umbenennen&lt;br /&gt;
# Windows-Shell (Kommandozeile, Eingabeaufforderung usw.) öffnen. &amp;quot;Start&amp;quot; -&amp;gt; &amp;quot;Ausführen&amp;quot; (oder [Windows-Taste]+[R]) -&amp;gt; cmd -&amp;gt; Ok. Durch &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;cd &amp;quot;&amp;lt;FHEM-Hauptverzeichis&amp;gt;\alexa-fhem&amp;quot;&amp;lt;/syntaxhighlight&amp;gt; in das Verzeichnis wechseln. &amp;lt;br /&amp;gt;Dabei ist natürlich das &amp;lt;FHEM-Hauptverzeichis&amp;gt; durch den entsprechenden Pfad aus dem Schritt 1 zu ersetzen. Im o.g. Beispiel wäre es &amp;lt;code&amp;gt;cd &amp;quot;С:\Program Files (x86)\fhem\alexa-fhem&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
# Mit &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;npm install&amp;lt;/syntaxhighlight&amp;gt; alle Abhängigkeiten installieren.&amp;lt;br /&amp;gt; Bei der Fehlermeldung wie &amp;quot;Der Befehl &amp;quot;npm&amp;quot; ist entweder falsch geschrieben oder konnte nicht gefunden werden.&amp;quot; ist die Installation von Node.js zu überprüfen.&lt;br /&gt;
# SSL Zertifikat erzeugen. Dafür muss man alle Befehle aus dem Skript &#039;&#039;createKey.sh&#039;&#039; nacheinander manuell ausführen. Hierbei beachten, dass ein Kennwort vergeben werden muss, das mindestens aus 4 Zeichen besteht, dieses Kennwort bitte merken. Der Windows-Welt unbekannter Befehl &amp;lt;code&amp;gt;mv&amp;lt;/code&amp;gt; ist durch &amp;lt;code&amp;gt;move /y&amp;lt;/code&amp;gt; zu ersetzen:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;openssl rsa -in key.pem -out newkey.pem&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;move /y newkey.pem key.pem&amp;lt;/syntaxhighlight&amp;gt; Eventuelle Fehlermeldung &amp;quot;can&#039;t open config file: /usr/local/ssl/openssl.cnf&amp;quot; o.Ä. lässt sich durch Befehl &amp;lt;code&amp;gt;set OPENSSL_CONF=&amp;lt;OpenSSL-Verzeichnis&amp;gt;\bin\openssl.cfg&amp;lt;/code&amp;gt; beheben, wobei &amp;lt;OpenSSL-Verzeichnis&amp;gt; durch den entsprechenden Installationspfad (typischerweise &amp;lt;code&amp;gt;c:\OpenSSL-Win32&amp;lt;/code&amp;gt;) zu ersetzen ist.&lt;br /&gt;
# Das Verzeichnis &#039;&#039;.alexa&#039;&#039; anlegen, &#039;&#039;und zwar im Benutzerverzeichnis desjenigen Benutzers, unter dem Alexa-Fhem laufen soll.&#039;&#039; In aktuellen Versionen von Windows (ab Windows 7 bzw. ab Windows Server 2008 R2) liegt das Verzeichnis unter &amp;lt;code&amp;gt;C:\Users\&amp;lt;Benutzername&amp;gt;&amp;lt;/code&amp;gt;, also z.B. für Benutzer &amp;quot;Administrator&amp;quot; - unter &amp;lt;code&amp;gt;C:\Users\Administrator&amp;lt;/code&amp;gt;.&amp;lt;br /&amp;gt;Falls Windows sich weigert das Verzichniss mit dem Punkt am Anfang zu erstellen, kann man das aus der Kommandozeile machen:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;cd &amp;quot;C:\Users\&amp;lt;Benutzername&amp;gt;&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
mkdir &amp;quot;.alexa&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
# Die Datei &#039;&#039;config-sample.json&#039;&#039; nach &#039;&#039;C:\Users\&amp;lt;Benutzername&amp;gt;\.alexa\config.json&#039;&#039; kopieren.&lt;br /&gt;
# Achtung: Ggf. müssen auch die Dateien key.pem und cert.pem ins entsprechende Verzeichnis (&amp;lt;code&amp;gt;&amp;quot;&amp;lt;FHEM-Hauptverzeichis&amp;gt;\alexa-fhem\bin&amp;lt;/code&amp;gt;) kopiert werden.&lt;br /&gt;
&lt;br /&gt;
====Update====&lt;br /&gt;
Hier wir das Update auf eine neue Version von Alexa-Fhem beschrieben&lt;br /&gt;
===== Linux =====&lt;br /&gt;
# Das Verzeichnis &#039;&#039;alexa-fhem&#039;&#039; im Hauptverzeichnis von FHEM (typischerweise &amp;lt;code&amp;gt;/opt/fhem&amp;lt;/code&amp;gt;) umbenennen in &#039;&#039;alexa-fhem.old&#039;&#039;.&lt;br /&gt;
# Die tgz-Datei der neuen Alexa-Fhem-Version unter Linux im Hauptverzeichnis von FHEM (typischerweise &amp;lt;code&amp;gt;/opt/fhem&amp;lt;/code&amp;gt;) entpacken (&#039;&#039;nicht&#039;&#039; unter Windows, das zerstört die Rechteeinstellungen).&lt;br /&gt;
# Das dabei entstandene Verzeichnis &#039;&#039;package&#039;&#039; in &#039;&#039;alexa-fhem&#039;&#039; umbenennen, in das Verzeichnis wechseln&lt;br /&gt;
# Mit &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;npm install&amp;lt;/syntaxhighlight&amp;gt; alle Abhängigkeiten installieren (kein sudo!).&lt;br /&gt;
# Die Zertifikatsdateien key.pem und cert.pem aus dem alten Verzeichnis &#039;&#039;alexa-fhem.old&#039;&#039; ins neue Verzeichnis &#039;&#039;alexa-fhem&#039;&#039; kopieren.&lt;br /&gt;
Natürlich dann den Dienst neu starten, auch müssen selbstredend irgendwelche Modifikationen an der Datei server.js in der neuen Version nachgezogen werden. Wenn alles läuft, kann das alte Verzeichnis &#039;&#039;alexa-fhem.old&#039;&#039; gelöscht werden.&lt;br /&gt;
===== Windows =====&lt;br /&gt;
# Das Verzeichnis &#039;&#039;alexa-fhem&#039;&#039; im Hauptverzeichnis von FHEM (z.B. &amp;lt;code&amp;gt;С:\Program Files (x86)\fhem&amp;lt;/code&amp;gt;) umbenennen in &#039;&#039;alexa-fhem.old&#039;&#039;.&lt;br /&gt;
# Die tgz-Datei der neuen Alexa-Fhem-Version im Hauptverzeichnis von FHEM entpacken.&lt;br /&gt;
# Das dabei entstandene Verzeichnis &#039;&#039;package&#039;&#039; in &#039;&#039;alexa-fhem&#039;&#039; umbenennen&lt;br /&gt;
# Windows-Shell (Kommandozeile, Eingabeaufforderung usw.) öffnen. &amp;quot;Start&amp;quot; -&amp;gt; &amp;quot;Ausführen&amp;quot; (oder [Windows-Taste]+[R]) -&amp;gt; cmd -&amp;gt; Ok. Durch &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;cd &amp;quot;&amp;lt;FHEM-Hauptverzeichis&amp;gt;\alexa-fhem&amp;quot;&amp;lt;/syntaxhighlight&amp;gt; in das Verzeichnis wechseln. &amp;lt;br /&amp;gt;Dabei ist natürlich das &amp;lt;FHEM-Hauptverzeichis&amp;gt; durch den entsprechenden Pfad aus dem Schritt 1 zu ersetzen. Im o.g. Beispiel wäre es &amp;lt;code&amp;gt;cd &amp;quot;С:\Program Files (x86)\fhem\alexa-fhem&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
# Mit &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;npm install&amp;lt;/syntaxhighlight&amp;gt; alle Abhängigkeiten installieren.&lt;br /&gt;
# Die Zertifikatsdateien key.pem und cert.pem aus dem alten Verzeichnis &#039;&#039;alexa-fhem.old&#039;&#039; ins neue Verzeichnis &#039;&#039;alexa-fhem&#039;&#039; kopieren.&lt;br /&gt;
Natürlich dann den Dienst neu starten, auch müssen selbstredend irgendwelche Modifikationen an der Datei server.js in der neuen Version nachgezogen werden. Wenn alles läuft, kann das alte Verzeichnis &#039;&#039;alexa-fhem.old&#039;&#039; gelöscht werden.&lt;br /&gt;
&lt;br /&gt;
==== Alexa-Fhem konfigurieren ====&lt;br /&gt;
Der Inhalt der Datei &#039;&#039;~/.alexa/config.json&#039;&#039; muss an die eigene Umgebung angepasst werden. &lt;br /&gt;
# &#039;&#039;nat-pmp&#039;&#039; -&amp;gt; wenn nat-pmp verwendet werden soll: die ip des eigenen routers, sonst die Zeile löschen!&lt;br /&gt;
# &#039;&#039;nat-upnp&#039;&#039; -&amp;gt; wenn nat-upnp verwendet werden soll: &#039;&#039;true&#039;&#039;, sonst die Zeile löschen!&lt;br /&gt;
# &#039;&#039;applicationId&#039;&#039; &lt;br /&gt;
#:* Wenn man nur den SmartHome-Skill verwenden möchte, kann dieser Eintrag leer bleiben.&lt;br /&gt;
#:* Ansonsten ist er mit der SkillID des Alexa Custom Skills zu belegen, siehe Abschnitt [[#Skill_Id_bestimmen | Skill Id bestimmen]]&lt;br /&gt;
# &#039;&#039;oauthClientID&#039;&#039; -&amp;gt; &#039;&#039;Client ID&#039;&#039; dem Abschnitt [[#Login_with_Amazon | Login with Amazon]], Punkt 1&lt;br /&gt;
# &#039;&#039;server&#039;&#039; -&amp;gt; IP-Adresse des eigenen FHEM-Servers&lt;br /&gt;
# &#039;&#039;port&#039;&#039; -&amp;gt; Portnummer des eigenen FHEM-Servers&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel a) Offenes fhem - System ohne Absicherung:&lt;br /&gt;
 {&lt;br /&gt;
    &amp;quot;alexa&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Alexa TEST&amp;quot;,&lt;br /&gt;
        &amp;quot;keyFile&amp;quot;: &amp;quot;./key.pem&amp;quot;,&lt;br /&gt;
        &amp;quot;certFile&amp;quot;: &amp;quot;./cert.pem&amp;quot;,&lt;br /&gt;
        &amp;quot;applicationId&amp;quot;: &amp;quot;amzn1.ask.skill.xxxxxxxxxxxxxxxxxxxxxxxxxxxx&amp;quot;,&lt;br /&gt;
        &amp;quot;oauthClientID&amp;quot;: &amp;quot;amzn1.application-oa2-client.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;connections&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;FHEM&amp;quot;,&lt;br /&gt;
            &amp;quot;server&amp;quot;: &amp;quot;192.168.0.xxx.xxx&amp;quot;,&lt;br /&gt;
            &amp;quot;port&amp;quot;: &amp;quot;8083&amp;quot;,&lt;br /&gt;
            &amp;quot;filter&amp;quot;: &amp;quot;room=AlexaRoom&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Beispiel b) Abgesichertes fhem - System mit TLS/SSL und HTTP Basic-Authentication:&lt;br /&gt;
 {&lt;br /&gt;
    &amp;quot;alexa&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Alexa TEST&amp;quot;,&lt;br /&gt;
        &amp;quot;keyFile&amp;quot;: &amp;quot;./key.pem&amp;quot;,&lt;br /&gt;
        &amp;quot;certFile&amp;quot;: &amp;quot;./cert.pem&amp;quot;,&lt;br /&gt;
        &amp;quot;applicationId&amp;quot;: &amp;quot;amzn1.ask.skill.xxxxxxxxxxxxxxxxxxxxxxxxxxxx&amp;quot;,&lt;br /&gt;
        &amp;quot;oauthClientID&amp;quot;: &amp;quot;amzn1.application-oa2-client.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;connections&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;FHEM&amp;quot;,&lt;br /&gt;
            &amp;quot;server&amp;quot;: &amp;quot;192.168.0.xxx.xxx&amp;quot;,&lt;br /&gt;
            &amp;quot;auth&amp;quot;: {&amp;quot;user&amp;quot;: &amp;quot;fhemuser&amp;quot;, &amp;quot;pass&amp;quot;: &amp;quot;fhempassword&amp;quot;},&lt;br /&gt;
            &amp;quot;ssl&amp;quot;: true,&lt;br /&gt;
            &amp;quot;port&amp;quot;: &amp;quot;8083&amp;quot;,&lt;br /&gt;
            &amp;quot;filter&amp;quot;: &amp;quot;room=AlexaRoom&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
    ]&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Mehrere Custom Skills lassen sich mit der folgenden Syntax eintragen&lt;br /&gt;
         &amp;quot;applicationId&amp;quot;: [ &amp;quot;amzn1.ask.skill.1&amp;quot; , &amp;quot;amzn1.ask.skill.2&amp;quot; ],&lt;br /&gt;
         &amp;quot;oauthClientID&amp;quot;: [ &amp;quot;amzn1.application-oa2-client.1&amp;quot; , &amp;quot;amzn1.application-oa2-client.1&amp;quot; ]&lt;br /&gt;
&lt;br /&gt;
Danach durch Aufruf von &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;./bin/alexa&amp;lt;/syntaxhighlight&amp;gt; den Dienst starten (kein sudo!)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Unter Windows startet man den Alexa-Dienst durch &amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;node alexa&amp;lt;/syntaxhighlight&amp;gt; aus der &amp;lt;code&amp;gt;alex-fhem/bin&amp;lt;/code&amp;gt; (also erst z.B. durch &amp;lt;code&amp;gt;cd &amp;quot;&amp;lt;FHEM-Hauptverzeichis&amp;gt;\alexa-fhem\bin&amp;quot;&amp;lt;/code&amp;gt; ins richtige Verzeichnis kommen)&lt;br /&gt;
&lt;br /&gt;
Der Start des Alexa-Dienstes auf der Console ist immer dann zu empfehlen, wenn man auf die Ausgaben des Dienstes angewiesen ist und beispielsweise sehen möchte, welche Devices durch den Dienst bereitgestellt werden oder ob Fehler auftreten. Beendet man die Console-Session wird auch der Dienst wieder beendet. Insofern ist die vorgenannte Vorgehensweise nur für ein Debugging zu empfehlen und nicht im Regelbetrieb. Nachfolgend ist beschrieben, wie man den Alexa-Dienst aus FHEM heraus starten / stoppen und neu starten kann.&lt;br /&gt;
&lt;br /&gt;
==== Alexa-Fhem aus FHEM heraus starten ====&lt;br /&gt;
Hierbei muss man zunächst herausfinden, welche der folgenden Startvarianten sein LINUX - OS zum Einsatz kommen:&lt;br /&gt;
initd.d oder systemd.&lt;br /&gt;
&lt;br /&gt;
Auf keinen Fall darf man &amp;quot;sicherheitshalber&amp;quot; beide Varianten installieren, dies zu Fehler(-meldungen) führt.&lt;br /&gt;
&lt;br /&gt;
===== Vorgehen bei init.d =====&lt;br /&gt;
Diese Variante kommt unter anderem auf dem Raspberry Pi mit dem OS-Varianten &amp;quot;Wheezy&amp;quot; zum Einsatz&lt;br /&gt;
&lt;br /&gt;
Zunächst das Start-up-Skript aus diesem Post herunterladen {{Link2Forum|Topic=60244|Message=517271|LinkText=https://forum.fhem.de/index.php/topic,60244.msg517271.html#msg517271}} und unter /etc/init.d/alexa speichern.&lt;br /&gt;
&lt;br /&gt;
Das Script geht davon aus, das der alexa-fhem script unter /opt/fhem/alexa-fhem liegt, und die logfiles später unter /opt/fhem/log. Sollte das nicht der Fall sein, muss das Skript angepasst werden.&lt;br /&gt;
&lt;br /&gt;
Nun folgende Kommandos ausführen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;sudo chmod 755 /etc/init.d/alexa&lt;br /&gt;
sudo update-rc.d alexa defaults&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt; den User fhem für die Nutzung von sudo zulassen (&amp;lt;code&amp;gt;sudo nano /etc/sudoers&amp;lt;/code&amp;gt;), z.B. durch Anfügen der nachfolgenden Zeile:&lt;br /&gt;
&amp;lt;code&amp;gt;fhem ALL=(ALL) NOPASSWD: ALL&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun folgende Devices anlegen (ggf. einem Raum zuordnen, z.B. AlexaControl):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:75%;&amp;quot;&amp;gt;define FHEM.Alexa.Status dummy&lt;br /&gt;
&lt;br /&gt;
define FHEM.Alexa dummy&lt;br /&gt;
attr FHEM.Alexa event-on-change-reading state&lt;br /&gt;
attr FHEM.Alexa webCmd status:start:stop:restart&lt;br /&gt;
&lt;br /&gt;
define FHEM.Alexa.DOIF DOIF ([FHEM.Alexa] eq &amp;quot;start&amp;quot;) &lt;br /&gt;
(set FHEM.Alexa on, {system (&amp;quot;sudo /etc/init.d/alexa start &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;quot;)}) &lt;br /&gt;
DOELSEIF ([FHEM.Alexa] eq &amp;quot;stop&amp;quot;)&lt;br /&gt;
(set FHEM.Alexa on, {system (&amp;quot;sudo /etc/init.d/alexa stop &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;quot;)}) &lt;br /&gt;
DOELSEIF ([FHEM.Alexa] eq &amp;quot;restart&amp;quot;) &lt;br /&gt;
(set FHEM.Alexa on, {system (&amp;quot;sudo /etc/init.d/alexa restart &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;quot;)}) &lt;br /&gt;
DOELSEIF ([FHEM.Alexa] eq &amp;quot;status&amp;quot;) &lt;br /&gt;
(set FHEM.Alexa on, {system (&amp;quot;sudo /etc/init.d/alexa status &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;quot;)})&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Vorgehen bei systemd =====&lt;br /&gt;
Diese Variante kommt unter anderem auf dem Raspberry Pi ab/seit der OS-Variante &amp;quot;Jessie&amp;quot; zum Einsatz&lt;br /&gt;
&lt;br /&gt;
Bei systemd Systemen funktioniert das obengenannte vorgehen leider nicht, man kann das gleiche aber mit dem Modul &lt;br /&gt;
[https://forum.fhem.de/index.php/topic,79952.0.html 98_serviced.pm - systemd und initd Dienste steuern] umsetzen.&lt;br /&gt;
Wichtig ist das der User unter dem alexa ausgeführt wird ohne Passwort abfrage sudo ausführen darf, dazu muss er in der &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt; eingetragen werden, z.b. so &amp;lt;code&amp;gt;fhem ALL=(ALL) NOPASSWD: ALL&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollte es dann nicht funktionieren kann das an einem Gruppen eintrag unterhalb der User definition in der &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt;, in diesem Fall den user am ende der &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt; eintragen, dann sollte es funktionieren&lt;br /&gt;
&lt;br /&gt;
==== Alexa-Fhem als Service (systemd) installieren ====&lt;br /&gt;
Auf neueren Installationen (z.B. RPi Jessie) wird init.d durch systemd ersetzt. Folgend die Anleitung um alexa-fhem als Service zu installieren auf einem Raspberry Pi mit Jessie.&lt;br /&gt;
&lt;br /&gt;
Zunächst einen neuen Benutzer anlegen unter dem alexa-fhem laufen soll, falls man nicht möchtet dass alexa-fhem z.B. mit dem fhem User ausgeführt wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code style=&amp;quot;width:75%;&amp;quot; lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo useradd -M --system alexa&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eigentlich braucht der Benutzer keine Gruppen, aber man kann den Benutzer auch der Gruppe &amp;lt;code&amp;gt;dialout&amp;lt;/code&amp;gt; hinzufügen (&amp;lt;code&amp;gt;sudo usermod -a -G dialout alexa&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Datei &amp;quot;alexa.service&amp;quot; unter &amp;lt;code&amp;gt;/etc/systemd/system&amp;lt;/code&amp;gt; anlegen:&lt;br /&gt;
&lt;br /&gt;
 [Unit]&lt;br /&gt;
 Description=Node.js Alexa Server &lt;br /&gt;
 After=syslog.target network-online.target&lt;br /&gt;
 &lt;br /&gt;
 [Service]&lt;br /&gt;
 Type=simple&lt;br /&gt;
 User=alexa &lt;br /&gt;
 WorkingDirectory=/opt/fhem/alexa-fhem&lt;br /&gt;
 ExecStart=/opt/fhem/alexa-fhem/bin/alexa -U /home/alexa/.alexa&lt;br /&gt;
 Restart=on-failure&lt;br /&gt;
 RestartSec=10&lt;br /&gt;
 KillMode=process&lt;br /&gt;
 &lt;br /&gt;
 [Install]&lt;br /&gt;
 WantedBy=multi-user.target &lt;br /&gt;
&lt;br /&gt;
Den Pfad &amp;lt;code&amp;gt;/home/alexa/.alexa&amp;lt;/code&amp;gt; an die Systemgegebenheiten anpassen. Letztendlich kann die config.json irgendwo liegen, hauptsache alexa-fhem weiß wo. &lt;br /&gt;
&lt;br /&gt;
Im WorkingDirectory wird der alexa Dienst die Zertifikate suchen.&lt;br /&gt;
&lt;br /&gt;
Achtung: Natürlich muss der Benutzer auch Zugriff sowohl auf das Verzeichnis mit der config als auch das alexa-fhem Verzeichnis und das WorkingDirectory haben.&lt;br /&gt;
&lt;br /&gt;
Um den Service zu aktiveren und zu starten helfen folgende Befehle:&lt;br /&gt;
 sudo systemctl daemon-reload&lt;br /&gt;
 sudo systemctl enable alexa&lt;br /&gt;
 sudo systemctl start alexa&lt;br /&gt;
&lt;br /&gt;
Status abfragen mit&lt;br /&gt;
 sudo systemctl status alexa&lt;br /&gt;
&lt;br /&gt;
Sollte sich der Service nicht starten lassen und endet die Ausgabe ähnlich wie hier:&lt;br /&gt;
 &amp;lt;code&amp;gt;Feb 02 09:47:25 inet alexa[2738]: STDIN EOF&amp;lt;/code&amp;gt;&lt;br /&gt;
kann eine Anpassung der ExecStart Zeile aus der alexa.service Datei helfen:&lt;br /&gt;
 ExecStart=/opt/fhem/alexa-fhem/bin/alexa --dockerDetached -U /home/alexa/.alexa&lt;br /&gt;
Log einsehen?&lt;br /&gt;
 sudo journalctl -u alexa&lt;br /&gt;
&lt;br /&gt;
(mit &amp;lt;code&amp;gt;-f&amp;lt;/code&amp;gt; kann man den follow Modus aktivieren, wie &amp;lt;code&amp;gt;tail -f&amp;lt;/code&amp;gt;).&lt;br /&gt;
Bei einen reboot startet alexa-fhem jetzt automatisch.&lt;br /&gt;
&lt;br /&gt;
==== Alexa-Fhem testen ====&lt;br /&gt;
Node.Js stellt einen Web-Server am Port 3000 bereit, das oben erzeugte Zertifikat sichert diesen Zugang per SSL ab. Durch Aufruf der Adresse&lt;br /&gt;
&amp;lt;code&amp;gt;https://&amp;lt;IP-Adresse des Servers&amp;gt;:3000&amp;lt;/code&amp;gt; kann man testen, ob der Alexa-Fhem Service läuft - der Seitenaufruf liefert eine Zeile JSON-Code, beginnend mit&lt;br /&gt;
&amp;lt;code&amp;gt;{&amp;quot;header&amp;quot;:{&amp;quot;name&amp;quot;:&amp;quot;UnsupportedOperationError&amp;quot;...&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Alexa Device anlegen ===&lt;br /&gt;
Das Modul 39_alexa.pm stellt innerhalb von FHEM verschiedene Attribute z.B. alexaName oder alexaRoom zur Verfügung. Manche dieser Attribute (wie z.b. alexaName) werden in beiden Skills verwendet, andere werden ausschließlich bei einer Nutzung des Alexa Custom Skill verwendet.&lt;br /&gt;
&lt;br /&gt;
Die Einrichtung des Alexa Device geschieht durch die nachfolgende Definition:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:70%;&amp;quot;&amp;gt;define MyAlexa alexa&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Alexa Skills ===&lt;br /&gt;
Für folgende Schritte muss man unter der Adresse http://developer.amazon.com angemeldet sein&lt;br /&gt;
# Anmeldung auswählen&amp;lt;br /&amp;gt;[[Datei:Amazon Developer.jpg||200px]]&lt;br /&gt;
# Anmeldedaten eingeben&amp;lt;br /&amp;gt;[[Datei:LogIn.jpg||200px]]&lt;br /&gt;
&lt;br /&gt;
==== Security Profile anlegen ====&lt;br /&gt;
Die Erzeugung eines Sicherheitsprofils muss nur einmal erfolgen, es wird dann für alle weiteren Skills verwendet.&lt;br /&gt;
# Nach der Anmeldung Auswahl von &#039;&#039;APPS &amp;amp; SERVICES&#039;&#039;&amp;lt;br /&amp;gt;[[Datei:Apps1.jpg|200px]]&lt;br /&gt;
# Anschließend auswählen &#039;&#039;Security Profiles&#039;&#039;&amp;lt;br /&amp;gt;[[Datei:Security.jpg|200px]]&lt;br /&gt;
# Auswählen &#039;&#039;Create a New Security Profile&#039;&#039; aus&amp;lt;br /&amp;gt;[[Datei:Newsecurity.jpg|200px]]&lt;br /&gt;
# Dann einen Namen und eine Beschreibung für das Profil eingeben und mit &#039;&#039;Save&#039;&#039; bestätigen&amp;lt;br /&amp;gt;[[Datei:SecurityName.jpg|200px]]&lt;br /&gt;
# Anschließend den die Daten unter &#039;&#039;Gneral&#039;&#039; merken oder Kopieren &amp;lt;br /&amp;gt;[[Datei:SecurityGenerl.jpg|200px]]&lt;br /&gt;
# Im Anschluß daran auf &#039;&#039;Web Settings&#039;&#039; klicken und dort auf &#039;&#039;Edit&#039;&#039; &amp;lt;br /&amp;gt;[[Datei:WebSettings.jpg|200px]] &lt;br /&gt;
# Und nun die im Bild gezeigten Daten eintragen &amp;lt;br /&amp;gt;[[Datei:WebSettingsDaten.jpg|200px]] &lt;br /&gt;
## https://layla.amazon.co.uk/api/skill/link/xxx&lt;br /&gt;
## https://pitangui.amazon.com/api/skill/link/xxx&lt;br /&gt;
## https://layla.amazon.com/api/skill/link/xxx&lt;br /&gt;
# Das xxx muss hierbei durch den Wert ersetzt werden, der in den beiden Abschnitten SmartHome Skill anlegen bzw. Custom Skill anlegen jeweils unter Punkt 4 (Seite Configuration) bei Redirect Urls am Ende der URLs angezeigt wird&lt;br /&gt;
# Im anschluß daran oben auf &#039;&#039;Login with Amazon&#039;&#039; &amp;lt;br /&amp;gt;[[Datei:LoginwithAlexa.jpg|200px]]&lt;br /&gt;
&lt;br /&gt;
===== Login with Amazon =====&lt;br /&gt;
{{Randnotiz|RNTyp=[g|Info]|RNText=Hier wird beschrieben, wo &#039;&#039;Client Id&#039;&#039; und &#039;&#039;Client Secret&#039;&#039; zu finden sind}}&lt;br /&gt;
# In der neu geladenen Seite im Dropdown Menü das vorher angelegte Profil unter &#039;&#039;Select a Security Profil&#039;&#039; auswählen und mit &#039;&#039;Confirm&#039;&#039; bestätigen&amp;lt;br /&amp;gt;[[Datei:LoginwithAlexaSelect.jpg|200px]]&lt;br /&gt;
# Im folgenden Fenster die Adresse https://www.amazon.com/gp/help/customer/display.html?nodeId=468496 eingeben und mit &#039;&#039;Save&#039;&#039; bestätigen. &#039;&#039;&#039;Todo Erklärungsbedarf: WARUM diese Adresse&#039;&#039;&#039;&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-12-login_with_amazon_-_enter_consent_screen_information.png|200px]]&lt;br /&gt;
# Anschließend bei dem neu angelegten Eintrag auf der rechten Seite auf das Zahnrad klicken und &#039;&#039;Web Settings&#039;&#039; auswählen&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-13-login_with_amazon_-_web_settings.png|200px]]&lt;br /&gt;
&lt;br /&gt;
==== Skills bearbeiten (Gilt für den SmartHome wie auch den Customer Skill)====&lt;br /&gt;
# Im Menü den Punkt &#039;&#039;ALEXA&#039;&#039; auswählen und anschließend im Dropdown Menü &#039;&#039;Alexa Skills Kit&#039;&#039; auswählen&amp;lt;br /&amp;gt;[[Datei:AlexaSkill.jpg|200px]]&lt;br /&gt;
&lt;br /&gt;
===== SmartHome Skill anlegen =====&lt;br /&gt;
# Rechts &#039;&#039;Create Skill&#039;&#039; auswählen&amp;lt;br /&amp;gt;[[Datei:AlexaSkillCreate.jpg|200px]]&lt;br /&gt;
# Auf der folgenden Seite die folgenden Daten eingeben und dann mit &#039;&#039;Next&#039;&#039; bestätigen:&lt;br /&gt;
#:* &#039;&#039;Skill Name&#039;&#039; -&amp;gt; beliebiger Name, z.B. &amp;quot;MySmartHome Basic&amp;quot;)&lt;br /&gt;
#:* &#039;&#039;Default Language&#039;&#039; -&amp;gt; &#039;&#039;German&#039;&#039;&lt;br /&gt;
#:* &#039;&#039;Choose a model to add to your skill&#039;&#039; -&amp;gt; &#039;&#039;SmartHome &#039;&#039; anklicken&lt;br /&gt;
#:* &#039;&#039;Anschließend rechts oben auf &#039;&#039;Create Skill&#039;&#039; &amp;lt;br /&amp;gt;[[Datei:AlexaSkillCreateSmart.jpg|200px]]&#039;&#039;&lt;br /&gt;
# Nun auf die Seite https://signin.aws.amazon.com/ wechseln und dort einloggen &amp;lt;br /&amp;gt;[[Datei:AWSAnmelden.jpg|200px]]&lt;br /&gt;
# Nach dem Login links oben auf &#039;&#039;Services&#039;&#039; klicken und anschließend auf &#039;&#039;Lambda&#039;&#039; &amp;lt;br /&amp;gt; [[Datei:AWSLambda.jpg|200px]]&lt;br /&gt;
# Auf der &#039;&#039;Lambda&#039;&#039; Seite &amp;quot;Funktion&#039; auswählen links im Menü und dann rechts auf &#039;&#039;Funktion erstellen&#039;&#039; klicken &amp;lt;br /&amp;gt; [[Datei:LamdbaFunktion.jpg|200px]]&lt;br /&gt;
# Auf der Seite &#039;&#039;Funktion erstellen&#039;&#039; dann auf &#039;&#039;Ohne Vorgabe erstellen&#039; klicken&#039;&#039;&lt;br /&gt;
#:* Unter &#039;&#039;Name&#039;&#039; einen Beliebigen Namen eingeben&lt;br /&gt;
#:* Bei &#039;&#039;Laufzeit&#039;&#039; &#039;&#039;Nodejs 8.10&#039;&#039; auswählen&lt;br /&gt;
#:* Unter &#039;&#039;Rolle&#039;&#039; &#039;&#039;Erstellen einer benutzerdefinierten Rolle&#039;&#039; auswählen, im anschluß daran sollte sich automatisch ein neues Fenster öffnen, in diesem alles unverändert lassen und auf &#039;&#039;Allow&#039;&#039; klicken wodurch sich das Fenster wieder schließen sollte&amp;lt;br /&amp;gt; [[Datei:IAM.jpg|200px]] &lt;br /&gt;
#:*Jetzt noch rechts unten auf &#039;&#039;Funktion erstellen klicken&#039;&#039;&amp;lt;br /&amp;gt; [[Datei:OhneVorgabe.jpg|200px]]&lt;br /&gt;
&lt;br /&gt;
# Auf der jetzt geöffneten Seite rechts oben die &#039;&#039;arn&#039;&#039; Nummer aufschreiben/kopieren und links aus dem Menü &#039;&#039;Alexa Smart Home&#039;&#039; hinzufügen &amp;lt;br /&amp;gt;[[Datei:Arn.jpg|200px]]&lt;br /&gt;
# Unten auf der Seite unter &#039;&#039;Auslöser konfigurieren&#039;&#039; muss die Skill ID eingetragen werden, diese findet man im developer account unter dem Skill. &amp;lt;br /&amp;gt; [[Datei:Auslöser.jpg|200px]] &lt;br /&gt;
# Anschließend noch rechts unten auf &#039;&#039;Hinzufügen&#039;&#039; klicken und danach rechts oben auf &#039;&#039;Speichern&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Nun auf &#039;&#039;Test&#039;&#039; (Bezeichnung im Screenshot) klicken und unten auf der Seite unter &#039;&#039;Funktionscode&#039;&#039; den vorhanden Code löschen und den Code aus der &#039;&#039;Lambda.js&#039;&#039; welche sich in dem &#039;&#039;alexa-fhem-0.4.4&#039;&#039; Paket befindet einfügen &amp;lt;br /&amp;gt; [[Datei:Arn2.jpg|200px]]&lt;br /&gt;
&amp;lt;br /&amp;gt; [[Datei:Code.jpg|200px]]&lt;br /&gt;
# Im Code müsst ihr den &#039;&#039;Hostname&#039;&#039; anpassen, also eure DynDns z.b. einfügen, anschließen rechts oben wieder auf &#039;&#039;Speichern&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# Nun wieder zurück in den Alexa developer Account und hier folgende Daten eintragen&lt;br /&gt;
{{Randnotiz|RNTyp=r|RNText=Es funktioniert nur noch die v3 api (v2 ist deprecated). Die Version hierfür gibt es im {{Link2Forum|Topic=81324|LinkText=Forum}}. bitte die hinweise dort lesen und beachten.}}&lt;br /&gt;
#:* &#039;&#039;Payload Version&#039;&#039; -&amp;gt; &#039;&#039;v3(preferred)&#039;&#039; auswählen  &amp;lt;br /&amp;gt;&lt;br /&gt;
#:* &#039;&#039;Default endpoint&#039;&#039; -&amp;gt; Hier die &#039;&#039;arn&#039;&#039; Nummer von oben eintragen/einfügen&lt;br /&gt;
#:* Dann den Haken setzen bei &#039;&#039;Europe, India&#039;&#039; und nochmals die &#039;&#039;arn&#039;&#039; eintragen und anschließend rechts oben auf &#039;&#039;Save&#039;&#039;&lt;br /&gt;
# Links im Menü &#039;&#039;Account Linking&#039;&#039; auswählen und dort folgende Daten eintragen&lt;br /&gt;
#:* &#039;&#039;Authorization URI&#039;&#039; -&amp;gt; &#039;&#039;https://www.amazon.com/ap/oa&#039;&#039;&lt;br /&gt;
#:* &#039;&#039;Access Token URI&#039;&#039; -&amp;gt;  &#039;&#039;https://api.amazon.com/auth/o2/token&#039;&#039;&lt;br /&gt;
#:* &#039;&#039;Client ID&#039;&#039; -&amp;gt; &#039;&#039;Eure Client ID, fängt mit amzn......an&#039;&#039;, zu finden unter &#039;&#039;https://developer.amazon.com&#039;&#039; -&amp;gt; &#039;&#039; Apps &amp;amp; Services&#039;&#039; -&amp;gt; &#039;&#039;Security Profiles&#039;&#039; -&amp;gt; Profil auswählen  -&amp;gt; &#039;&#039;Web Settings&#039;&#039; &lt;br /&gt;
#.* &#039;&#039;Client Secret&#039;&#039; -&amp;gt; &#039;&#039;Euer Client Secret&#039;&#039;, siehe &#039;&#039;Client ID&#039;&#039;&lt;br /&gt;
#:* &#039;&#039;Scope&#039;&#039; -&amp;gt; &#039;&#039;Add Scope&#039;&#039; auswählen und &#039;&#039;profile:user_id&#039;&#039;  (wörtlich 1:1 eintragen)  eintragen&lt;br /&gt;
#:* &#039;&#039;Redirect URLs&#039;&#039; -&amp;gt; Diese 3 Adressen merken/kopieren&lt;br /&gt;
# Alles andere auf dieser seite kann gleich bleiben, jetzt noch rechts oben wieder auf &#039;&#039;Save&#039;&#039; klicken &amp;lt;br /&amp;gt; [[Datei:Account.jpg|200px]]&lt;br /&gt;
&lt;br /&gt;
# Anschließend wieder zurück zum &#039;&#039;Amazon developer Account&#039;&#039; https://developer.amazon.com , dort wieder das &#039;&#039;Security Profile&#039;&#039; aufrufen und die drei Links unter &#039;&#039;Allowed Return URLs&#039;&#039; (wo eingangs am Schluss 3 XXX gesetzt wurden) mit den Links von Oben &#039;&#039;Redirect URLs&#039;&#039; ersetzen&lt;br /&gt;
# Nun geht es weiter in der &#039;&#039;Amazon Alexa developer Konsole&#039;&#039; https://developer.amazon.com/alexa/console/ask wo oben auf &#039;&#039;Distribution&#039;&#039; geklickt werden muss.&lt;br /&gt;
# Hier sind alle angaben Freiwillig bzw. können Frei gewählt werden bis auf die &#039;&#039;Privacy Policy URL&#039;&#039; , diese muss eingetragen werden, anschließend links unten auf &#039;&#039;Save and continue&#039;&#039;&amp;lt;br /&amp;gt;[[Datei:German.jpg|200px]]&lt;br /&gt;
&lt;br /&gt;
# Auf der kommenden Seite &#039;&#039;Privacy &amp;amp; Compliance&#039;&#039; entsprechend anklicken und wieder unter auf &#039;&#039;Save and continue&#039;&#039;&lt;br /&gt;
# Unter &#039;&#039;Availability&#039;&#039; alles so lassen und wieder auf &#039;&#039;Save and continue&#039;&#039;&lt;br /&gt;
# Nun den Alexa Service auf dem Fhem Rechner neustarten, den Skill in der Alexa App aktivieren und nach geräten suchen lassen&lt;br /&gt;
&lt;br /&gt;
===== Custom Skill anlegen =====&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Oben rechts &#039;&#039;Add a New Skill&#039;&#039; auswählen&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-18-alexa_-_alex_skills_kit_-_add_a_new_skill.png|200px]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; Auf der folgenden Seite (&#039;&#039;Skill Information&#039;&#039;) die nachstehenden Daten eingeben und dann mit &#039;&#039;Next&#039;&#039; bestätigen:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; &#039;&#039;Skill Type&#039;&#039; -&amp;gt; &#039;&#039;Custom Interaction Model&#039;&#039; &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; &#039;&#039;Language&#039;&#039; -&amp;gt; &#039;&#039;German&#039;&#039;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; &#039;&#039;Name&#039;&#039; -&amp;gt; beliebiger Name, z.B. &amp;quot;MySmartHome Advanced&amp;quot;. Dieser wird in der Alexa App unter &amp;quot;Meine Skills&amp;quot; angezeigt.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; &#039;&#039;Invocation Name&#039;&#039; -&amp;gt; Aufruf des Skills, unter dem dieser später gestartet wird. Z.B. &amp;quot;Alexa, starte James&amp;quot;&amp;lt;br /&amp;gt;[[Datei:CustomSkill_2.PNG|400px]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Auf der Seite &#039;&#039;Interaction Model&#039;&#039; folgende Eingaben tätigen und mit &#039;&#039;Next&#039;&#039; abschließen&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;In einem separaten Browserfenster FHEM aufrufen, und für das bereits definierte Alexa-Gerät das Kommando &amp;lt;code&amp;gt;get MyAlexa interactionModel&amp;lt;/code&amp;gt; aufrufen. Es erscheint ein Popup-Fenster mit ziemlich vielen Zeilen.&lt;br /&gt;
&amp;lt;li&amp;gt;In die Box &#039;&#039;Intent Schema&#039;&#039; kopiert man den ersten Teil dieser FHEM-Ausgabe hinein, also:&amp;lt;br /&amp;gt;&amp;lt;blockquote&amp;gt;&lt;br /&gt;
 { &lt;br /&gt;
   &amp;quot;intents&amp;quot; : [ &lt;br /&gt;
    &amp;lt;hier ziemlich viele Zeilen&amp;gt;  &lt;br /&gt;
   ]&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Nun die &#039;&#039;Custom Slot Types&#039;&#039; einrichten. Dazu muss aus dem zweiten Teil der FHEM-Ausgabe jeweils der Slot-Type (z.B. &amp;lt;code&amp;gt;FHEM_article&amp;lt;/code&amp;gt;) in das Feld &#039;&#039;TYPE&#039;&#039; eingetragen werden, das nach dem Anklicken von &#039;&#039;Add Slot Type&#039;&#039; erscheint. In das darunter liegende größere Textfeld kommen die möglichen Werte für diesen Slot, so wie sie aus der FHEM-Ausgabe abzulesen sind. Dann mit &#039;&#039;Save&#039;&#039; sichern. Als Custom Slot Type erscheint dann für diesen Beispiel-Slot&lt;br /&gt;
 FHEM_article 	der | die | das | den&lt;br /&gt;
d.h., die Zeilenumbrüche bei den möglichen Werten werden als &amp;quot;|&amp;quot; dargestellt.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Hier muss nun ein Bruch im Arbeitsfluss durchgeführt werden, denn bei der Erstellung des Custom Skills kommt es auf die Reihenfolge der Einträge an. Deshalb zunächst diese FHEM-Ausgabe schließen, und für dasselbe FHEM-Device &amp;lt;code&amp;gt;get MyAlexa customSlotTypes&amp;lt;/code&amp;gt; ausführen. Auch diese Ausgabe wird, wie oben beschriebeen, in Custom Slot Types eingetragen (erst der TYPE, dann die möglichen Werte)&lt;br /&gt;
&amp;lt;li&amp;gt;Anschließend erneut die FHEM-Ausgabe schließen und erneut  für das bereits definierte Alexa-Gerät das Kommando &amp;lt;code&amp;gt;get MyAlexa interactionModel&amp;lt;/code&amp;gt; aufrufen.&lt;br /&gt;
&amp;lt;li&amp;gt;Unter &#039;&#039;Sample Utterances&#039;&#039; nun den Text aus dem dritten Teil dieser FHEM-Ausgabe hineinkopieren&amp;lt;br /&amp;gt;[[Datei:CustomSkill_5.PNG|400px]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Auf der Seite &#039;&#039;Configuration&#039;&#039; Folgendes eingeben und mit &#039;&#039;Next&#039;&#039; bestätigen:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;Service Endpoint Type&#039;&#039; -&amp;gt; &#039;&#039;AWS Lambda&#039;&#039; auswählen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;Geographical Region&#039;&#039; -&amp;gt; &#039;&#039;Europe&#039;&#039; auswählen und im Textfeld den Wert aus Abschnitt [[#AWS_Lambda_Funktion_anlegen | AWS Lambda Funktion anlegen]] (Punkt 12) eintragen. &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;Do you allow users to create an account or link to an existing account with you?&#039;&#039; -&amp;gt; &#039;&#039;Yes&#039;&#039;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;Authorization URL&#039;&#039; -&amp;gt; &amp;lt;code&amp;gt;https://www.amazon.com/ap/oa&amp;lt;/code&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;Client ID&#039;&#039; -&amp;gt; &#039;&#039;Client ID&#039;&#039; aus Abschnitt [[#Login_with_Amazon | Login with Amazon]], Punkt 1&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;Scope&#039;&#039; -&amp;gt; &#039;&#039;&#039;&amp;lt;code&amp;gt;profile:user_id&amp;lt;/code&amp;gt;&#039;&#039;&#039; (wörtlich 1:1 eintragen)&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;Redirect URLs&#039;&#039; - sollten vorbelegt sein&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;Authorization Grant Type&#039;&#039; -&amp;gt; &#039;&#039;Auth Code Grant&#039;&#039; auswählen&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;Access Token URI&#039;&#039; -&amp;gt; &amp;lt;code&amp;gt;https://api.amazon.com/auth/o2/token&amp;lt;/code&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;Client Secret&#039;&#039; -&amp;gt; &#039;&#039;Client Secret&#039;&#039; aus Abschnitt [[#Login_with_Amazon | Login with Amazon]], Punkt 1&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;Client Authentication Scheme&#039;&#039; -&amp;gt; &#039;&#039;HTTP Basic&#039;&#039;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&#039;&#039;Privacy Policy URL&#039;&#039; -&amp;gt; &amp;lt;code&amp;gt;https://www.amazon.com/gp/help/customer/display.html?nodeId=468496&amp;lt;/code&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
Beim Sichern dieser Seite mit &#039;&#039;Next&#039;&#039; kann es zu einer Fehlermeldung kommen, wenn man seine Skill-Definitionen mit dem einfachen SmartHome-Skill begonnen hat. Deshalb muss noch der entsprechende Trigger für die [[#AWS_Lambda-Funktion | AWS Lambda Funktion]] nachgetragen werden, dies wird in Abschnitt [[#Trigger_f.C3.BCr_Custom_Skill_hinzuf.C3.BCgen | Trigger für Custom Skill hinzufügen]] beschrieben.&lt;br /&gt;
&amp;lt;br /&amp;gt;[[Datei:CustomSkill_6.PNG|400px]] [[Datei:CustomSkill_7.PNG|400px]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Testen ====&lt;br /&gt;
Hat man den Custom Skill angelegt, bietet dieser auch eine komfortable Testmöglichkeit. Dazu wählt man in der Übersichtsseite &#039;&#039;All Skills&#039;&#039; den Button &#039;&#039;Edit&#039;&#039; des Alexa Custom Skill aus. Auf der nachfolgenden Seite dann links &#039;&#039;Test&#039;&#039;. &lt;br /&gt;
Die Testseite enthält &lt;br /&gt;
* ein Feld &#039;&#039;Voice Simulator&#039;&#039;, mit dem man die Sprachsausgabe testen kann, &lt;br /&gt;
* ein Feld &#039;&#039;Service Simulator&#039;&#039;, mit dem die Verarbeitung von Alexa-Kommandois getestet werden kann. Hier kann man z.B. eintragen&lt;br /&gt;
 &amp;quot;Alexa, sage &amp;lt;Custom Skill Invocation Name&amp;gt;: Stelle Weckzeit auf Neunzehn Siebenundzwanzig Uhr&amp;quot;&lt;br /&gt;
 &amp;quot;Alexa, frage &amp;lt;Custom Skill Invocation Name&amp;gt; nach dem Status von Weckzeit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Dabei ist natürlich der &#039;&#039;Custom Skill Invocation Name&#039;&#039; durch den Wert zu ersetzen, den man im Abschnitt [[#Custom_Skill_anlegen | Custom Skill Anlegen]] unter Punkt 2 eingetragen hat.&lt;br /&gt;
&lt;br /&gt;
==== Skill Id bestimmen ====&lt;br /&gt;
{{Randnotiz|RNTyp=[g|Info]|RNText=Hier wird beschrieben, wo die &#039;&#039;Alexa Skill Id&#039;&#039; zu finden ist}}&lt;br /&gt;
Für das [[#AWS_Lamba_Funktion_anlegen | Anlegen einer &#039;&#039;AWS Lambda Funktion&#039;&#039;]] bzw für die [[#Alexa-Fhem_konfigurieren | Konfiguration von Alexa-Fhem]] wird die &#039;&#039;Alexa Skill Id&#039;&#039; benötigt. An diese kommt man wie folgt:&lt;br /&gt;
# Anmelden wie unter [[#Alexa_Skills | Alexa Skills]] beschrieben.&lt;br /&gt;
# Menüpunkt &#039;&#039;ALEXA&#039;&#039; auswählen, wie [[#Skills_bearbeiten | Skills bearbeiten]] erklärt.&lt;br /&gt;
# Beim gewünschten Eintrag auf &#039;&#039;Edit&#039;&#039; klicken&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-23-alexa_-_alex_skills_kit_-_overview.png|200px]]&lt;br /&gt;
# Die Id, die nun oben angezeigt wird, ist die gesuchte. Sie hat typischerweise das Format &amp;lt;code&amp;gt;amzn1.ask.skill.[Zahlen und Bindestriche]&amp;lt;/code&amp;gt;&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-06-configure_triggers2.png|200px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AWS Lambda Funktion ===&lt;br /&gt;
Für folgende Schritte muss man unter der Adresse http://aws.amazon.com angemeldet sein&lt;br /&gt;
# Anmeldung auswählen&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-01-site.png|200px]]&lt;br /&gt;
# Anmeldedaten eingeben&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-02-login.png|200px]]&lt;br /&gt;
# Den Punkt &#039;&#039;Lambda&#039;&#039; links auf der Startseite auswählen, bzw. im Menü &#039;&#039;Services&#039;&#039; unter &#039;&#039;Compute&#039;&#039; den Menüpunkt &#039;&#039;Lambda&#039;&#039; auswählen&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-03-lambda.png|200px]]&lt;br /&gt;
&lt;br /&gt;
==== AWS Lambda Funktion anlegen ===={{Randnotiz|RNTyp=r|RNText=Die AWS Seiten sehen inzwischen etwas anders aus. Eine angepasste Beschreibung findet sich im  {{Link2Forum|Topic=81790|Message=739211|LinkText=Forum}}. Bitte auch die Hinweise dort lesen.}}&lt;br /&gt;
# Für eine erste Lambda-Funktion den Punkt &#039;&#039;Get Started Now&#039;&#039; auswählen&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-04-get_started_now.png|200px]]&lt;br /&gt;
# Den Blueprint &#039;&#039;Blank function&#039;&#039; auswählen&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-05-select_blueprint.png|200px]]&lt;br /&gt;
# Im neuen Fenster dann auf den gestrichelten Kasten klicken und &#039;&#039;Alexa Smart Home&#039;&#039; auswählen und mit &#039;&#039;Next&#039;&#039; bestätigen&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-06-configure_triggers1.png|200px]]&lt;br /&gt;
## Achtung, es ist möglich, dass dabei &#039;&#039;Alexa Smart Home&#039;&#039; überhaupt nicht angeboten wird. Dann bitte ganz rechts oben in der Ecke nachsehen, welche Region bzw. welches Land ausgewählt ist. Empfohlen wird, &#039;&#039;Ireland&#039;&#039; auszuwählen. Dann erscheint bei den Funktionen auch &#039;&#039;Alexa Smart Home&#039;&#039;.&lt;br /&gt;
# Bei &#039;&#039;Application Id&#039;&#039; den Wert eintragen, dessen Ermittlung im Abschnitt [[#Skill_Id_bestimmen | Skill Id bestimmen]] beschrieben wird, den Haken bei &#039;&#039;Enable trigger&#039;&#039; setzen und mit &#039;&#039;Next&#039;&#039; bestätigen &amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-06-configure_triggers3.png|200px]]&lt;br /&gt;
# Auf der Konfigurationsseite eingeben:&lt;br /&gt;
## &#039;&#039;Name&#039;&#039; -&amp;gt; &#039;&#039;FHEM&#039;&#039;&lt;br /&gt;
## &#039;&#039;Runtime&#039;&#039; -&amp;gt; Node.js 6.10 (oder 8.10). &lt;br /&gt;
## &#039;&#039;Role&#039;&#039; -&amp;gt; &#039;&#039;Choose an existing role&#039;&#039; &lt;br /&gt;
### Achtung: wenn es noch keine existing role gibt, zuerst &#039;&#039;Create a custom role&#039;&#039; -&amp;gt; in dem Popup dann &#039;&#039;lambda_basic_execution&#039;&#039; auswählen und auf &#039;&#039;Allow&#039;&#039; klicken sowie bei &#039;&#039;Existing role&#039;&#039; dann &#039;&#039;x&#039;&#039; wählen.&lt;br /&gt;
# Auf der Code-Seite ist im großen Textfeld dann der Code aus der Datei &#039;&#039;lambda.js&#039;&#039; im Paket [[#Alexa-Fhem_installieren | Alexa-Fhem]] vollständig einzufügen. Dabei muss der vorhandene Code im Texteil komplett gelöscht, der Teil aus der &#039;&#039;lamda.js&#039;&#039; eingefügt und noch der Hostname im Quellcode an den eigenen Hostnamen angepasst werden. &lt;br /&gt;
# Anschließend alles mit &#039;&#039;Next&#039;&#039; bestätigen.&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-07-configure_function.png|200px]]&lt;br /&gt;
# Auf der Übersichtsseite dann &#039;&#039;Create function&#039;&#039; anklicken&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-08-review.png|200px]]&lt;br /&gt;
&lt;br /&gt;
==== Trigger für Custom Skill hinzufügen ====&lt;br /&gt;
Editiert man eine Lambda-Funktion, werden auf der Seite &#039;&#039;Triggers&#039;&#039; diejenigen Dienste angezeigt, die diese Funktion aufrufen.&lt;br /&gt;
* Hier taucht der Trigger &#039;&#039;Alexa Smart Home&#039;&#039; zusammen mit der &#039;&#039;Application Id&#039;&#039; auf, der bei der Einrichtung des SmartHome-Skills eingetragen wurde.&lt;br /&gt;
* Zur Verbindung mit dem Custom Skill ist es nötig, einen zweiten Trigger hinzuzufügen. Durch Anklicken von &#039;&#039;Add Trigger&#039;&#039; wird eine Auswahlseite eingeblendet. Im neuen Fenster dann auf den gestrichelten Kasten klicken und &#039;&#039;Alexa Skills Kit&#039; auswählen und mit &#039;&#039;Next&#039;&#039; bestätigen&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== ARN der AWS Lambda Funktion bestimmen ====&lt;br /&gt;
# Auf der Übersichtsseite oben links den Menüpunkt &#039;&#039;Functions&#039;&#039; aúswählen&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-09-go_overview.png|200px]]&lt;br /&gt;
# Anschließend den Radiobutton der angelegten Funktion &#039;&#039;FHEM&#039;&#039; markieren und im Menü &#039;&#039;Action&#039;&#039; den Punkt &#039;&#039;Show ARN&#039;&#039; auswählen&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-10-1-show_arn.png|200px]]&lt;br /&gt;
# Es wird nun eine ARN Adresse angezeigt, die für den Abschnitt [[#SmartHome_Skill_anlegen| SmartHome Skill anlegen]] benötigt wird&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-10-2-arn.png|200px]]&lt;br /&gt;
&lt;br /&gt;
=== Absichern des Zugriffs ===&lt;br /&gt;
Natürlich muss der Zugriff auf den von Alexa-Fhem verwendeten Port (default: 3000, Bestandteil des Codes in der AWS Lambda-Funktion) durch die Firewall freigeschaltet werden (auf einer FritzBox unter &amp;quot;Portfreigaben&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
==== Absicherung direkt in Alexa-FHEM ====&lt;br /&gt;
Die Kommunikation zwischen Amazon AWS und Alexa-FHEM ist auf die folgenden Arten gesichert:&lt;br /&gt;
* Die Verbindung erfolgt per HTTPS&lt;br /&gt;
* Es werden nur Verbindung angenommen auf denen ein gültiges Alexa-Event gesendet wird. &lt;br /&gt;
* Es werden nur Verbindungen angenommen die ein gültiges und noch nicht abgelaufenes OAuth-Token enthalten. Jedes neue Token wird live bei Amazon auf Gültigkeit geprüft.    &lt;br /&gt;
* Es werden nur Verbindungen mit lokal konfigurierter Skill-ID angenommen.&lt;br /&gt;
* Es ist nicht möglich von außen beliebige FHEM Kommandos zu senden. Die FHEM Kommandos werden nur lokal erzeugt.&lt;br /&gt;
&lt;br /&gt;
Wer möchte kann Alexa-FHEM natürlich noch weiter absichern. Es gilt aber, dass nicht jedes zusätzliche Glied in der Kette die Sicherheit sondern unter Umständen nur die Angriffsfläche erhöht. Ein falsch konfigurierter und nach aussen offener Apache (oder anderer ReverseProxy) ist unter Umständen ein größeres Risiko als Alexa-FHEM alleine.&lt;br /&gt;
&lt;br /&gt;
==== Absicherung per ReverseProxy ====&lt;br /&gt;
&amp;lt;s&amp;gt;Die Kommunikation zwischen Amazon und FHEM ist wegen der Verwendung von SSL schon verschlüsselt - prinzipiell kann aber jeder von außen mit Alexa-Fhem kommunizieren. Man sollte sich deshalb im Klaren darüber sein, dass dies eine Sicherheitslücke darstellt:&amp;lt;/s&amp;gt; Jeder offene Port verleitet zu Angriffen, und mit zunehmender Verbreitung von Alexa steigt auch das Risiko. Es wird deshalb empfohlen, vor den eigentlichen Alexa-Server zur Absicherung einen Apache-Webserver als ReverseProxy zu setzen. Nicht nur ist der Apache eine hervorragend stabile und seit Jahrzehnten getestete Software, sondern die Konfiguration als ReverseProxy erlaubt auch, den Zugriff auf den Alexa-Fhem-Rechner auf die Amazon-Maschinen zu beschränken.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung: Dies ist keine allgemeine Anleitung in Sachen Computersicherheit.&#039;&#039;&#039; Im Folgenden gehen wir davon aus, dass &lt;br /&gt;
* Grundbegriffe wie Firewall, IP-Ports, SSL und Dynamic DNS vertraut sind&lt;br /&gt;
* Ein Apache Webserver (idealerweise auf einer zweiten Maschine) bereits installiert ist und die Konfiguration verstanden wurde (wenn nicht: Es gibt im Netz &#039;&#039;tausende&#039;&#039; von Anleitungen dafür...)&lt;br /&gt;
* Ein Servername von einem DynDNS-Anbieter - sagen wir &#039;&#039;myhome.is-my-castle.com&#039;&#039; - bereits von &#039;&#039;außen&#039;&#039; auf unser SmartHome zeigt.&lt;br /&gt;
* Alexa-Fhem in einer der oben beschriebenen Basiskonfigurationen läuft, d.h. der Zugriff auf &amp;lt;code&amp;gt;https://myhome.is-my-castle.com:3000&amp;lt;/code&amp;gt; ergibt, wie im Punkt [[#Alexa-Fhem_testen|Alexa-Fhem testen]] beschrieben, eine Antwort des Node.js Servers.&lt;br /&gt;
&lt;br /&gt;
Als erster Schritt zur Absicherung muss das ReverseProxy Modul für den Apache installiert und mit &amp;lt;code&amp;gt;a2enmod&amp;lt;/code&amp;gt; aktiviert werden, hierzu sei auf [https://www.digitalocean.com/community/tutorials/how-to-use-apache-http-server-as-reverse-proxy-using-mod_proxy-extension diese Anleitung] verwiesen. Der zweite Schritt besteht darin, den SSL-Zugriff durch ein Passwort abzusichern. Dazu wird auf dem Apache-Rechner das Programm &lt;br /&gt;
 &lt;br /&gt;
htpasswd &amp;lt;passwdfile&amp;gt; &amp;lt;username&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ausgeführt, das Programm fragt dann nach dem gewünschten Passwort. Wir nehmen im Folgenden an, dass das Passwortfile &#039;&#039;/etc/apache2/htpasswd&#039;&#039; ist, der gesetzte Username &#039;&#039;alexa&#039;&#039; lautet und das Passwort &#039;&#039;my_smarthome&#039;&#039; ist.&lt;br /&gt;
&lt;br /&gt;
Im dritten Schritt wird nun in das Apache-Konfigurationsfile die Weiterleitung auf den eigentlichen Alexa-Fhem-Rechner eingetragen. Dazu wählen wir, dass von außen der Standard-SSL-Port 443 benutzt werden soll, sowie als Verzeichnisname &#039;&#039;/alexa&#039;&#039;. &#039;&#039;&#039;Achtung:&#039;&#039;&#039; Dieser Code soll &#039;&#039;&#039;nicht&#039;&#039;&#039; in die Default-Konfiguration des Apache-Webservers. Sondern in eine separate Datei (Dateiname z.B. &amp;quot;fhem&amp;quot;), die ins Unterverzeichnis /etc/apache2/conf.d gestellt wird.&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;VirtualHost *:443&amp;gt;&lt;br /&gt;
  ServerName myhome.is-my-castle.com&lt;br /&gt;
  SSLEngine on&lt;br /&gt;
  SSLProxyEngine on&lt;br /&gt;
  SSLProxyCheckPeerCN off&lt;br /&gt;
  SSLProxyCheckPeerName off&lt;br /&gt;
  SSLCertificateKeyFile /etc/apache2/mycert/server.key&lt;br /&gt;
  SSLCertificateFile /etc/apache2/mycert/server.crt&lt;br /&gt;
 &amp;lt;Location /alexa&amp;gt;&lt;br /&gt;
  AuthType Basic&lt;br /&gt;
  AuthName &amp;quot;Authentication Required&amp;quot;&lt;br /&gt;
  AuthUserFile &amp;quot;/etc/apache2/htpasswd&amp;quot;&lt;br /&gt;
  Require valid-user&lt;br /&gt;
  ProxyPass https://&amp;lt;hier IP-Adresse des Alexa-Fhem-Rechners&amp;gt;:3000/&lt;br /&gt;
  ProxyPassReverse https://&amp;lt;hier IP-Adresse des Alexa-Fhem-Rechners&amp;gt;:3000/&lt;br /&gt;
  Order deny,allow&lt;br /&gt;
  Allow from All&lt;br /&gt;
 &amp;lt;/Location&amp;gt;&lt;br /&gt;
 (... Hier eventuell weitere Umleitungen)&lt;br /&gt;
 &amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach einem Neustart des Apache-Servers, dem Schließen des Ports 3000 in der Firewall sowie dem Öffnen des Ports 443 ist der Alexa-Fhem-Rechner von außen nur noch erreichbar durch den Aufruf von &amp;lt;code&amp;gt;https://myhome.is-my-castle.com/alexa&amp;lt;/code&amp;gt; und verlangt unmittelbar die Eingabe von Username und Passwort.&lt;br /&gt;
&lt;br /&gt;
Der vierte Schritt ist nun, den Code der AWS Lambda-Funktion an fünf Stellen zu verändern&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;&#039;const PORT=443;&#039;&#039;&#039;&lt;br /&gt;
 const HOST=&#039;myhome.is-my-castle.com&#039;;&lt;br /&gt;
 &#039;&#039;&#039;const PATH=&#039;/alexa&#039;;&#039;&#039;&#039;&lt;br /&gt;
 &#039;&#039;&#039;const AUTH=&#039;alexa:my_smarthome&#039;;&#039;&#039;&#039;&lt;br /&gt;
 // entry&lt;br /&gt;
 exports.handler = function(event, context, callback) {&lt;br /&gt;
  console.log(`EVENT: ${event}`);&lt;br /&gt;
  console.log(`CONTEXT: ${context}`);  &lt;br /&gt;
  var post_data = JSON.stringify(event);&lt;br /&gt;
  var options = {&lt;br /&gt;
    hostname: HOST,&lt;br /&gt;
    port: PORT,&lt;br /&gt;
    //family: 6,&lt;br /&gt;
    &#039;&#039;&#039;path: PATH,&#039;&#039;&#039;&lt;br /&gt;
    method: &#039;POST&#039;,&lt;br /&gt;
    &#039;&#039;&#039;auth: AUTH,&#039;&#039;&#039;&lt;br /&gt;
    rejectUnauthorized: false, // accept self-signed&lt;br /&gt;
 (etc., Rest des Codes wie gehabt)&lt;br /&gt;
&lt;br /&gt;
Natürlich muss der Zugriff getestet werden. Bei Beachtung aller dieser Schritte werden alle un-autorisierten Zugriffe von außen abgewehrt. Eine noch weiter gehende Sicherung ist möglich, dazu kann in der Serverkonfiguration der Zugriff auf die Amazon-Domains beschränkt werden. Das ganze Alexa-System ist aber noch in konstanter Weiterentwicklung, diese Domain-Namen können sich also noch ändern.&lt;br /&gt;
&lt;br /&gt;
== Einrichtung in der Alexa App==&lt;br /&gt;
Nachdem die Alexa Skills angelegt wurden, müssen diese noch in der Alexa App eingerichtet werden.&lt;br /&gt;
Dafür jeweils per Desktop-Browser auf [http://alexa.amazon.de alexa.amazon.de] anmelden, nicht die App unter iOS oder Android verwenden. Diese hat Probleme mit der OAuth Verknüpfung.&lt;br /&gt;
&lt;br /&gt;
=== Alexa Skill ===&lt;br /&gt;
# Auf &#039;&#039;Skills&#039;&#039; klicken&amp;lt;br /&amp;gt;[[Datei:Alexa.amazon.de-01-startseite.png|200px]]&lt;br /&gt;
# Oben rechts &#039;&#039;Meine Skills&#039;&#039;  bzw. &#039;&#039;Ihre Skills&#039;&#039; auswählen&amp;lt;br /&amp;gt;[[Datei:Alexa.amazon.de-03-meine_skills.png|200px]]&lt;br /&gt;
# In der Liste der Skills sollte das angelegte FHEM Skill angezeigt werden. Dieses anklicken&amp;lt;br /&amp;gt;[[Datei:Alexa.amazon.de-02-liste_skills.png|200px]]&lt;br /&gt;
# Oben Rechts in den Details des Skills auf &#039;&#039;Skill aktivieren&#039;&#039; klicken&amp;lt;br /&amp;gt;[[Datei:Alexa.amazon.de-04-skill_details.png|200px]]&lt;br /&gt;
# In dem neu geöffneten Fenster die Autorisierung bestätigen&amp;lt;br /&amp;gt;[[Datei:Alexa.amazon.de-05-amazon_auth.png|200px]]&lt;br /&gt;
# Anschließend sollte die Verbindung erfolgreich aufgebaut worden sein &amp;lt;br /&amp;gt;[[Datei:Alexa.amazon.de-06-success.png|200px]]&lt;br /&gt;
&lt;br /&gt;
=== Geräte ===&lt;br /&gt;
# Auf http://alexa.amazon.de anmelden&lt;br /&gt;
# Auf &#039;&#039;Smart Home&#039;&#039; klicken&amp;lt;br /&amp;gt;[[Datei:Alexa.amazon.de-01-startseite.png|200px]]&lt;br /&gt;
# Anschließend den Punkt &#039;&#039;Geräte suchen&#039;&#039; anklicken&amp;lt;br /&amp;gt;[[Datei:Alexa.amazon.de-07-Gerätesuche.png|200px]]&lt;br /&gt;
# Wurde soweit alles korrekt eingerichtet, werden nun die gefundenen Geräte angezeigt.&lt;br /&gt;
&lt;br /&gt;
Tip: Es macht Sinn, unter &#039;&#039;Meine Gruppen&#039;&#039; Gruppen benannt nach den Räumen einzurichten. Hierdurch kann Alexa die Geräte besser auseinander halten, vor allem wenn die den gleichen Alias (z.B. &amp;quot;Licht&amp;quot;) haben.&lt;br /&gt;
&lt;br /&gt;
== Einrichtung unter FHEM ==&lt;br /&gt;
Im Folgenden werden exemplarisch ein paar Geräte beschrieben, die man nutzbringend mit FHEM einsetzen kann.&lt;br /&gt;
&lt;br /&gt;
Bei Verwendung des Custom Skills übersetzt die Kombination der Attribute &#039;&#039;alexaMapping&#039;&#039; und &#039;&#039;homebridgeMapping&#039;&#039; Sprachbefehle (&amp;quot;Intents&amp;quot;) in gerätespezifische Kommandos. &lt;br /&gt;
* Das Attribut alexaMapping wird am Alexa-Device gesetzt und dient dazu, erkannte Sprachkommandos in abstrakte Characteristiken zu überführen. Für den einfacheren SmartHome Skill hat darum das Attribut &#039;&#039;alexaMapping&#039;&#039; keine Bedeutung, sondern nur der &#039;&#039;genericDeviceType&#039;&#039; des zu steuernden Gerätes.&lt;br /&gt;
* Das Attribut homebridgeMapping wird für beide Skills am zu steuernden Gerät gesetzt und übersetzt diese Charakteristiken in die konkreten Befehle, die das Gerät versteht. Der inhalt des Attributs wird von links nach rechts ausgewertet und ist wie folgt aufgebaut:&lt;br /&gt;
** Das Attribut enthält eine durch Leerzeichen getrennte Liste aus Konfigurationen für jeweils eine Characteristik&lt;br /&gt;
** Jede einzelne der Characteristik-Konfigurationen besteht aus dem Namen der Characteristik, gefolgt von &amp;quot;=&amp;quot; und einer kommaseparierten Liste von Parametern.&lt;br /&gt;
  attr &amp;lt;device&amp;gt; homebridgeMapping &amp;lt;Characteristic1&amp;gt;=&amp;lt;param1.1&amp;gt;,&amp;lt;param1.2&amp;gt;,... &amp;lt;Characteristic2&amp;gt;=&amp;lt;param2.1&amp;gt;,&amp;lt;param2.2&amp;gt;,...&lt;br /&gt;
** Jeder Parameter besteht entweder aus&lt;br /&gt;
*** &amp;lt;code&amp;gt;&amp;lt;cmd&amp;gt;:&amp;lt;device&amp;gt;:&amp;lt;reading&amp;gt;&amp;lt;/code&amp;gt;, hier können nicht verwendete Elemente von links nach rechts weg gelassen werden.&lt;br /&gt;
*** &amp;lt;code&amp;gt;&amp;lt;name&amp;gt;=&amp;lt;value&amp;gt;&amp;lt;/code&amp;gt;, hier kann &amp;lt;code&amp;gt;&amp;lt;value&amp;gt;&amp;lt;/code&amp;gt; entweder ein Wert oder semikolonseparierte Liste sein.&lt;br /&gt;
*** Oder dem schlüsselwort &amp;lt;code&amp;gt;clear&amp;lt;/code&amp;gt;, welches alle vorhandenen (default) Parameter dieser Characteristik löscht. &amp;lt;code&amp;gt;clear&amp;lt;/code&amp;gt; kann auch an Stelle einer ganzen Characteristik-Konfiguration stehen&lt;br /&gt;
Weiter führende Dokumentation zum homebridgeMapping findet sich unter https://forum.fhem.de/index.php/topic,48558.0.html&lt;br /&gt;
&lt;br /&gt;
=== Einfacher Schalter ===&lt;br /&gt;
* Ein einfacher Schalter, der die set-Kommandos &#039;&#039;on&#039;&#039; und &#039;&#039;off&#039;&#039; kennt, kann direkt mit Alexa-Fhem gekoppelt werden &lt;br /&gt;
* Für kompliziertere Aktionen, etwa das Übermitteln eines spezifischen Schaltbefehls an FHEM, ist die Einrichtung eines Dummies zu empfehlen. &lt;br /&gt;
Ob Dummy oder nicht, wichtig sind die drei fett gedruckten Zeilen&lt;br /&gt;
 &lt;br /&gt;
define Alexa.Party dummy&lt;br /&gt;
 &#039;&#039;&#039;attr Alexa.Party alexaName party&#039;&#039;&#039;&lt;br /&gt;
 &#039;&#039;&#039;attr Alexa.Party alexaRoom alexaroom&#039;&#039;&#039;&lt;br /&gt;
 &#039;&#039;&#039;attr Alexa.Party genericDeviceType switch&#039;&#039;&#039;&lt;br /&gt;
 attr Alexa.Party group AlexaGeräte&lt;br /&gt;
 attr Alexa.Party room AlexaRoom&lt;br /&gt;
 attr Alexa.Party setList on off&lt;br /&gt;
&lt;br /&gt;
Selbstverständlich kann man diesen Dummy mit einem notify oder DOIF abfangen, um die gewünschte Schaltaktion auszuführen. &lt;br /&gt;
&lt;br /&gt;
Ein Alternative zum Dummy ist das Anlegen eines readingsProxy, dem die entsprechenden Attribute gegeben werden.&lt;br /&gt;
&lt;br /&gt;
Weil es sich hierbei um eines der einfachen Geräte handelt, die Alexa selbst im SmartHome Skill bearbeiten kann, ist auch der zweite Schritt bei der Einrichtung in der Alexa App sinnvoll: Der Schalter wird dann im Bereich Smart Home der Alexa App erkannt. Wer ihn auch mit dem Custon Skill bedienen möchte, muss natürlich Sorge tragen, dass der Alexa-Name &#039;&#039;party&#039;&#039; bei den FHEM_Devices auftaucht und die entsprechenden weiteren Slot Types und Example Utterances in der Konfiguration des Custom Skills vorhanden sind (siehe Abschnitt [[##Custom_Skill_anlegen | Custom Skill Anlegen]]).&lt;br /&gt;
&lt;br /&gt;
=== Wecker ===&lt;br /&gt;
Dieses Gerät kann man nur mit dem Custom Skill bedienen, es wird also &#039;&#039;&#039;nicht&#039;&#039;&#039; im Bereich Smart Home der Alexa App auftauchen. Wir richten einen Dummy ein, wichtig sind wieder die fett gedruckten Zeilen:&lt;br /&gt;
 &lt;br /&gt;
define Alexa.Weckzeit dummy&lt;br /&gt;
 &#039;&#039;&#039;attr Alexa.Weckzeit alexaName weckzeit&#039;&#039;&#039;&lt;br /&gt;
 &#039;&#039;&#039;attr Alexa.Weckzeit alexaRoom alexaroom&#039;&#039;&#039;&lt;br /&gt;
 attr Alexa.Weckzeit genericDeviceType clock&lt;br /&gt;
 attr Alexa.Weckzeit group AlexaGeräte&lt;br /&gt;
 &#039;&#039;&#039;attr Alexa.Weckzeit homebridgeMapping Weckzeit=state,cmd=+&#039;&#039;&#039;&lt;br /&gt;
 attr Alexa.Weckzeit room AlexaRoom&lt;br /&gt;
 &#039;&#039;&#039;attr Alexa.Weckzeit setList Weckzeit:time&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Das Attribut &#039;&#039;genericDeviceTye&#039;&#039; ist nicht wichtig, weil es ein generisches Device dieser Art gar nicht gibt. Wichtig hingegen ist das Attribut &#039;&#039;homebridgeMapping&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Für das Gerät &#039;&#039;MyAlexa&#039;&#039;, das in Abschnitt definiert wurde, muss im Attribut &#039;&#039;alexaMapping&#039;&#039; auftauchen&lt;br /&gt;
 &lt;br /&gt;
Weckzeit=verb=stelle,valuePrefix=für;auf,values=AMAZON.TIME,valueSuffix=uhr&lt;br /&gt;
&lt;br /&gt;
Darüber hinaus muss der Alexa-Name &#039;&#039;weckzeit&#039;&#039; bei den FHEM_Devices auftauchen und die entsprechenden weiteren Slot Types und Example Utterances in der Konfiguration des Custom Skills vorhanden sein (siehe Abschnitt [[#Custom_Skill_anlegen | Custom Skill Anlegen]]).&lt;br /&gt;
&lt;br /&gt;
Der Aufruf dieses Gerätes mit Alexa erfolgt dann z.B. mit den Sätzen&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;quot;Alexa, sage &amp;lt;Custom Skill Invocation Name&amp;gt;: Stelle Weckzeit auf Neunzehn Uhr Siebenundzwanzig&amp;quot;&lt;br /&gt;
&amp;quot;Alexa, frage &amp;lt;Custom Skill Invocation Name&amp;gt; nach dem Status von Weckzeit&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Dabei ist natürlich der &#039;&#039;Custom Skill Invocation Name&#039;&#039; durch den Wert zu ersetzen, den man im Abschnitt [[#Custom_Skill_anlegen | Custom Skill Anlegen]] unter Punkt 2 eingetragen hat.&lt;br /&gt;
&lt;br /&gt;
Zur weiteren Bearbeitung kann man jetzt mit einem DOIF Statusänderungen des Dummies abfangen und durch eine kleine Helperfunktion ins &amp;quot;echte&amp;quot; FHEM weiterleiten.&lt;br /&gt;
  &lt;br /&gt;
define Alexa.Weckzeit.N DOIF ([&amp;quot;Alexa.Weckzeit:.*&amp;quot;])({AlexaHelper(&amp;quot;Alexa.Weckzeit&amp;quot;,&amp;quot;$EVENT&amp;quot;)}) &lt;br /&gt;
&lt;br /&gt;
Die Helperfunktion (z.B. in 99_myUtils.pm) stellt aus der übergebenen Zeit (immer im Format dd:mm) eine sprachkompatible Nachricht $nc und einen mit den FHEM-Zeitangaben kompatiblen String $nt zusammen und reicht beide an eine Routine &#039;&#039;changeWakeTime&#039;&#039; weiter (dokumentiert in den [https://www.dpunkt.de/buecher/12387/9783960090120-smarthome-hacks.html Smart Home Hacks]).&lt;br /&gt;
 sub AlexaHelper($$){&lt;br /&gt;
  my ($name,$event)=@_;&lt;br /&gt;
  if( $name eq &amp;quot;Alexa.Weckzeit&amp;quot; ){ &lt;br /&gt;
    my ($nc,$nt);&lt;br /&gt;
    #-- volle Stunde----------------------------------------&lt;br /&gt;
    if( $event =~ /(\d+):00/ ){&lt;br /&gt;
      $nc=sprintf(&amp;quot;%d Uhr&amp;quot;,$1);&lt;br /&gt;
      $nt=sprintf(&amp;quot;%02d:00:00&amp;quot;,$1);&lt;br /&gt;
    #-- nicht volle Stunde---------------------------------&lt;br /&gt;
    }elsif( $event =~ /(\d+):(\d+)/ ){&lt;br /&gt;
      $nc=sprintf(&amp;quot;%d Uhr %d&amp;quot;,$1,$2);&lt;br /&gt;
      $nt=sprintf(&amp;quot;%02d:%02d:00&amp;quot;,$1,$2);&lt;br /&gt;
    }&lt;br /&gt;
    changeWakeTime(\&#039;GalaxyTab.EG\&#039;,\&#039;$nc\&#039;,\&#039;$nt\&#039;);&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;hr /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Lichtszene ===&lt;br /&gt;
Eine Lichtszene wird mit dem Modul LightScene angelegt. Wir gehen davon aus, dass in der Lichtszene die beiden Szenen Alle_An und Alle_Aus, sowie mindestens eine weitere Szene (hier: Sitzgruppe) definiert wurde.&lt;br /&gt;
* Nachfolgend wird ein Beispiel beschrieben, wie man eine Lichtszene mit dem einfachen SmartHome Skill steuern kann. Die verwendeten Kommandos sind dann&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;Alexa, schalte (die) Beleuchtung an&amp;quot; -&amp;gt; LightScene Alle_An wird ausgewählt&lt;br /&gt;
&amp;quot;Alexa, schalte (die) Beleuchtungsitzgruppe an&amp;quot; -&amp;gt; LightScene Sitzgruppe wird ausgewählt&lt;br /&gt;
...&lt;br /&gt;
&amp;quot;Alexa, schalte (die) Beleuchtung aus&amp;quot; -&amp;gt; LightScene Alle_Aus wird ausgewählt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Künftig wird man LightScene mit dem Custom Skill eventuell direkt steuern können - allerdings hat das einen geringeren WAF, als die Steuerung über den SmartHome Skill: Der Einschaltsatz muss dann mindestens lauten&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;Alexa, sage &amp;lt;Custom Skill Invocation Name&amp;gt;: schalte (die) Beleuchtung an&amp;quot; -&amp;gt; LightScene Alle_An wird ausgewählt&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Dafür wird es aber auch möglich sein direkt die SzenenNamen im gesprochenen Kommando zu verwenden und so auf die Umwege über dummys und ähnliches zu verzichten.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Im ersten Schritt wird ein Dummy für die Gesamtbeleuchtung eingerichtet:&lt;br /&gt;
 &lt;br /&gt;
define Alexa.Beleuchtung dummy &lt;br /&gt;
 attr Beleuchtung setList on off&lt;br /&gt;
 &#039;&#039;&#039;attr Alexa.Beleuchtung alexaName beleuchtung&#039;&#039;&#039;&lt;br /&gt;
 &#039;&#039;&#039;attr Alexa.Beleuchtung alexaRoom alexaroom&#039;&#039;&#039;&lt;br /&gt;
 &#039;&#039;&#039;attr Alexa.Beleuchtung genericDeviceType switch&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Anschließend wird für jede vorhandene Lichtszene (außer Alle_An und Alle_Aus) ein weiterer Dummy angelegt:&lt;br /&gt;
 &lt;br /&gt;
define Alexa.Beleuchtung.Sitzgruppe dummy &lt;br /&gt;
 attr Beleuchtung setList on off&lt;br /&gt;
 &#039;&#039;&#039;attr Alexa.Beleuchtung.Sitzgruppe alexaName beleuchtungsitzgruppe&#039;&#039;&#039;&lt;br /&gt;
 &#039;&#039;&#039;attr Alexa.Beleuchtung.Sitzgruppe alexaRoom alexaroom&#039;&#039;&#039;&lt;br /&gt;
 &#039;&#039;&#039;attr Alexa.Beleuchtung.Sitzgruppe genericDeviceType switch&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Die eigentliche Steuerung übernimmt dann ein DOIF&lt;br /&gt;
 &lt;br /&gt;
define Alexa.Beleuchtung.N DOIF&lt;br /&gt;
 ([&amp;quot;Alexa.Beleuchtung.Sitzgruppe:on&amp;quot;])&lt;br /&gt;
 (set &amp;lt;devicename der Lichtszene&amp;gt; scene Sitzgruppe,&lt;br /&gt;
  set Alexa.Beleuchtung off,&lt;br /&gt;
   ...&amp;lt;weitere dummies der anderen Szenen werden ebenfalls ausgeschaltet&amp;gt;&lt;br /&gt;
 )&lt;br /&gt;
 DOELSIF&lt;br /&gt;
 ... &amp;lt;weitere on-Events der anderen Szenen werden abgefangen&amp;gt;&lt;br /&gt;
 DOELSEIF&lt;br /&gt;
 ([&amp;quot;Alexa.Beleuchtung:on&amp;quot;])&lt;br /&gt;
 (set &amp;lt;devicename der Lichtszene&amp;gt; scene Alle_An,&lt;br /&gt;
  set Alexa.Beleuchtung.Sitzgruppe off,&lt;br /&gt;
  ...&amp;lt;weitere dummies der anderen Szenen werden ebenfalls ausgeschaltet&amp;gt;&lt;br /&gt;
 )&lt;br /&gt;
 DOELSEIF&lt;br /&gt;
 ([&amp;quot;Alexa.Beleuchtung:off&amp;quot;])&lt;br /&gt;
 (set &amp;lt;devicename der Lichtszene&amp;gt; scene Alle_Aus,&lt;br /&gt;
  set Alexa.Beleuchtung.Sitzgruppe off,&lt;br /&gt;
  ...&amp;lt;weitere dummies der anderen Szenen werden ebenfalls ausgeschaltet&amp;gt;&lt;br /&gt;
 )&lt;br /&gt;
&lt;br /&gt;
Mit diesem DOIF wird ein Radiobutton simuliert, d.h. wie bei den Stationstasten vor Uralt-Radios sorgt die Auswahl einer Szene immer dafür, dass alle anderen Dummies ausgeschaltet werden.&lt;br /&gt;
Natürlich kann man das auch mit einem kleinen Perl-Programm erreichen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zwei andere Ansätze Lichtszenen zu schalten die ohne DOIF auskommen sind im Folgenden beschrieben:&lt;br /&gt;
&lt;br /&gt;
* Wenn es von Interesse ist die Steuerung mit einer Darstellung in FTUI zu verbinden: Statt der oben beschriebenen dummy Devices kann man readingsProxy Devices mit passenden setFn und valueFn analog zum  [[Harmony#Button_f.C3.BCr_eine_bestimmte_Activity_im_Frontend_und_Homekit_.C3.BCber_readingsProxy|diesem Beispiel für harmony aktivitäten]] verwenden.&lt;br /&gt;
&lt;br /&gt;
* Für jede zu schaltende Szene wird ein dummy angelegt dessen homebridgeMapping direkt auf das LightScene Device zeigt:&lt;br /&gt;
&lt;br /&gt;
 define &amp;lt;dummy&amp;gt; dummy&lt;br /&gt;
 attr &amp;lt;dummy&amp;gt; setList on off&lt;br /&gt;
 attr &amp;lt;dummy&amp;gt; genericDeviceType switch&lt;br /&gt;
 attr &amp;lt;dummy&amp;gt; homebridgeMapping On=&amp;lt;light scene&amp;gt;::state,valueOn=&amp;lt;szene&amp;gt;,cmdOn=scene+&amp;lt;szene&amp;gt;,cmdOff=scene+&amp;lt;szene aus&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der zweiten Variante wird davon ausgegangen das der aktuelle status nicht abgefragt oder angezeigt werden soll. Deshalb gibt es keine direkte RadioButton Funktionalität.&lt;br /&gt;
&lt;br /&gt;
=== Harmony Hub ===&lt;br /&gt;
Ein [https://forum.fhem.de/index.php/topic,60244.msg550298.html#msg550298 HowTo-Beitrag im Forum] beschreibt die Ansteuerung von Harmony-Aktionen über den Custom Skill, beispielsweise für eine Aktion &#039;&#039;ARD&#039;&#039;: „Alexa, sage FHEM stelle Anlage auf ARD“.&lt;br /&gt;
&lt;br /&gt;
== Nutzung ==&lt;br /&gt;
Um den Namen zu bestimmen, unter dem ein Gerät mit Alexa angesprochen wird, verwendet Alexa-Fhem mit absteigender Priorität:&lt;br /&gt;
* das alexaName Attribut&lt;br /&gt;
* das alias Attribut&lt;br /&gt;
* das NAME Internal&lt;br /&gt;
Damit Alexa ein Gerät eindeutig identifizieren kann, sollten eindeutige Gerätenamen verwendet werden, bestehed möglichst aus einem Wort und ohne Ziffern. Wenn Alexa einen Namen nicht versteht, kann man unter [http://alexa.amazon.de/spa/index.html] nachsehen was tatsächlich verstanden wurde und den Gerätenamen ggf. anpassen.&lt;br /&gt;
&lt;br /&gt;
=== SmartHome Skill ===&lt;br /&gt;
Gruppen (Räume) müssen in der Alexa App konfiguriert werden. Über das API lassen sich nur der Name und die Schalteigenschaften übergeben.&lt;br /&gt;
&lt;br /&gt;
Nach erfolgreicher Einrichtung des SmartHome Skills sollte Alexa mit den folgenden Befehlen nutzbar sein:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
“alexa, schalte &amp;lt;gerät/gruppe&amp;gt; ein”&lt;br /&gt;
“alexa, schalte &amp;lt;gerät/gruppe&amp;gt; aus”&lt;br /&gt;
“alexa, stelle &amp;lt;gerät/gruppe&amp;gt; auf &amp;lt;wert&amp;gt; prozent”&lt;br /&gt;
“alexa, stelle &amp;lt;gerät/gruppe&amp;gt; auf &amp;lt;anzahl&amp;gt; grad”&lt;br /&gt;
“alexa, erhöhe &amp;lt;gerät/gruppe&amp;gt; um &amp;lt;anzahl&amp;gt; prozent”&lt;br /&gt;
“alexa, reduziere &amp;lt;gerät/gruppe&amp;gt; um &amp;lt;anzahl&amp;gt; prozent”&lt;br /&gt;
“alexa, erhöhe &amp;lt;gerät/gruppe&amp;gt; um &amp;lt;anzahl&amp;gt; grad”&lt;br /&gt;
“alexa, reduziere &amp;lt;gerät/gruppe&amp;gt; um &amp;lt;anzahl&amp;gt; grad”&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Custom Skill ===&lt;br /&gt;
Der Custom Skill erlaubt im Gegensatz zum SmartHome Skill eine weitreichende Konfiguration der möglichen Kommandos. Die Dokumentation ist aktuell noch über diverse Artikel im Wiki verstreut:&lt;br /&gt;
&lt;br /&gt;
*Das Prinzip der Kommandokonfiguration ist {{Link2Forum|Topic=60244|Message=532513|LinkText=hier}} beschrieben.&lt;br /&gt;
*Die erste Umsetzung ist {{Link2Forum|Topic=60244|Message=540117|LinkText=hier}} beschrieben.&lt;br /&gt;
*Mehr zu FHEM-Intents und deren Möglichkeiten gibt es {{Link2Forum|Topic=67490|Message=589378|LinkText=hier}}.&lt;br /&gt;
*Wie man die Alexa TTS-Engine bei den Antworten auf FHEM-Intents beeinflussen kann {{Link2Forum|Topic=77421|Message=693631|LinkText=hier}}.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: Abfragen, Attribute (alexaMapping, alexaTypes, fhemIntents, articles, prepositions), alles hier sammeln.&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
====Allgemeiner Hinweis====&lt;br /&gt;
Besonders wichtig ist, dass man sich sehr genau an diese Anleitung hält. Ein häufiger Fehler ist, dass die einfachen Anführungszeichen in der Anleitung unter &#039;&#039;&#039;AWS Lambda Funktion anlegen&#039;&#039;&#039; Punkt 8 einfach weggelassen werden. Diese sind zwingend notwendig. Es darf auch nur der reine Hostname eingetragen werden. Also kein &#039;&#039;http://&#039;&#039; davor. Entweder eine feste IP Adresse oder den Hostnamen, um den Rechner zu erreichen, den ihr über den Port 3000 freigegeben habt. Das sollte dann so aussehen:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
const PORT=3000;&lt;br /&gt;
const HOST=&#039;mein.host.name&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Freigabe von Port 3000====&lt;br /&gt;
{{Randnotiz|RNTyp=Fehl|RNText=Derzeit müsst ihr über einen echten IPv4 Anschluss verfügen, damit der Amazon Lambda-Server euch erreichen kann. DS-Lite Anschlüsse wie die von &amp;lt;b&amp;gt;UnityMedia&amp;lt;/b&amp;gt; z.B. funktionieren derzeit leider nicht. Eine möglicher &amp;quot;Workaround&amp;quot; wird hier beschrieben: https://forum.fhem.de/index.php/topic,60244.msg518276.html#msg518276}}&lt;br /&gt;
&lt;br /&gt;
Auf dem Router muss der Port 3000 Protokoll TCP freigegeben werden. D.h. von außen muss man wenn man den Port 3000 aufruft, auf dem intern laufenden node.js Alexa-Dienst zugreifen können.&lt;br /&gt;
Je nach Router gestaltet sich das Portforwarding bzw. die Portumleitung etwas schwieriger.&lt;br /&gt;
&lt;br /&gt;
Bei einem Speedport Router der Telekom beispielsweise, muss der Router komplett neu gestartet werden, wenn die Portfreigabe eingerichtet wurde. &lt;br /&gt;
&lt;br /&gt;
Bei der Fritz!Box ist das nicht nötig, bei dieser finden die Freigabe unter &#039;&#039;Internet -&amp;gt; Freigaben -&amp;gt; Portfreigaben&#039;&#039; statt. Dort wählt man dann den Rechner aus und richtet eine neue Freigabe ein. Wichtig hierbei ist, dass man Portfreigabe auswählt und nicht MyFRITZ!-Freigabe. Bei Port von bis trägt man 3000 ein, bei Port extern ebenfalls.&lt;br /&gt;
&lt;br /&gt;
Um die Portweiterleitung zu testen, solltet ihr euch auch nicht im gleichen Netz befinden. Viele Router blockieren den Netzaufruf aus dem gleichen Netz. Am besten testet ihr es, wenn ihr an eurem Mobiltelefon W-LAN deaktiviert und im Browser folgende Seite aufruft: &#039;&#039;https://mein.hostname:3000&#039;&#039;. Wenn ihr im Browser dann einen Quellcode von Alexa seht, funktioniert die Portumleitung.&lt;br /&gt;
&lt;br /&gt;
Wenn bis hier alles funktioniert und es läuft dennoch nicht rund, liegt das Problem woanders. Kommt z.B. bei der Gerätesuche kein Request rein (sichtbar auf dem Bildschirm, wenn bin/alexa gestartet wurden), kann evtl. der Lambda-Dienst falsch konfiguriert sein.&lt;br /&gt;
&lt;br /&gt;
====Probleme mit node.js - npm install====&lt;br /&gt;
&lt;br /&gt;
Falls eine Fehlermeldung auftritt, dass &amp;quot;npm&amp;quot; nicht gefunden werden kann, bitte NodeJS entsprechend der Anleitung im Homebridge-Artikel vorgehen: [[Homebridge_einrichten#NodeJS_installieren|NodeJS installieren]] sowie [[Homebridge_einrichten#Python.2C_g.2B.2B.2C_MDNS_installieren|Python, g++, MDNS installieren]], siehe auch folgenden Abschnitt.&lt;br /&gt;
&lt;br /&gt;
====Es kommen diverse Fehlermeldungen beim Starten von alexa-fhem und es beendet sich====&lt;br /&gt;
Wenn man auf der Konsole angemeldet ist, den Befehl&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;node -v&amp;lt;/syntaxhighlight&amp;gt;eingeben. Ist die Version niedriger als die geforderte 0.12, muss eine neuere installiert werden. Hier darf man dann im Wiki unter [[Homebridge_einrichten#NodeJS_installieren]] nachschauen. NodeJS V4 sollte hierbei schon ausreichen. Solange die node.js Version nicht passt, gar nicht groß rum experimentieren! Bitte beachtet, dass alle Voraussetzungen unter [[Alexa-Fhem#Voraussetzungen]] erfüllt sind! Keine Experimente mit Versionen die darunter liegen.&lt;br /&gt;
&lt;br /&gt;
====Fehlermeldung &#039;&#039;NAT-PMP failed: Error: timeout&#039;&#039; Fehler angezeigt beim Start von alexa-fhem====&lt;br /&gt;
Wenn ihr dann alexa-fhem über die Konsole startet und bekommt folgenden Fehler: &#039;&#039;NAT-PMP failed: Error: timeout&#039;&#039;, lasst euch davon nicht irritieren. Das bedeutet lediglich, dass der Port nicht automatich freigegeben wurde über uPNP. Alternativ prüft, ob die Funktion der Portfreigabe via uPNP auf eurem Router aktiviert ist.&lt;br /&gt;
&lt;br /&gt;
====Nach Start auf der Console beendet sich Alexa-FHEM sofort wieder====&lt;br /&gt;
Unmittelbar nach dem Start von Alexa-FHEM werden ein paar UPNP Fehlermeldungen ausgegeben. Unmittelbar danach beendet sich Alexa-FHEM wieder. &lt;br /&gt;
&lt;br /&gt;
Viele scheinen ein Problem mit UPNP auf dem Raspberry Pi zu haben. Wenn dieses Problem auftritt einfach in der &amp;lt;code&amp;gt;~/.alexa/config.json&amp;lt;/code&amp;gt; die folgenden Zeilen rauslöschen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;quot;nat-pmp&amp;quot;: &amp;quot;10.0.1.1&amp;quot;,&lt;br /&gt;
        &amp;quot;nat-upnp&amp;quot;: true,&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt erneut Alexa-FHEM starten. Sollte nun laufen.&lt;br /&gt;
&lt;br /&gt;
====Was ist zu tun, wenn alexa-fhem keine Geräte findet?====&lt;br /&gt;
Zunächst müssen die Geräte, die angesprochen werden wollen, unter FHEM ein neues Attribut zugewiesen bekommen. Dazu das Gerät in FHEM öffnen und das Attribut &#039;&#039;genericDeviceType switch&#039;&#039; hinzufügen, wenn es ein Schalter mit der Funktiona AN/AUS sein soll. Wenn man will, kann man dem Gerät jetzt noch über das Attribut &#039;&#039;alias&#039;&#039; eine besseren Namen geben, mit dem Alexa das Gerät dann auch finden kann.&lt;br /&gt;
Anschließend muss alexa-fhem neu gestartet werden und die definierten Geräte sollten nun gefunden werden.&lt;br /&gt;
&lt;br /&gt;
====Was ist zu tun, wenn Alexa zwar Geräte findet, diese aber nicht angesprochen werden können?====&lt;br /&gt;
Zuerst die Informationen zum Datenfluss ganz oben ansehen. Dann am besten von hinten nach vorne vorgehen:&lt;br /&gt;
* wenn nichts bei alexa-fhem ankommt: port forwarding prüfen&lt;br /&gt;
* wenn lambda.js nichts los wird: im cloudwatch log nachsehen&lt;br /&gt;
* wenn bei lambda.js nichts ankommt: den trigger prüfen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zunächst sollte man sich unter &#039;&#039;http://aws.amazon.com&#039;&#039; das Logfile seiner erstellten Funktion anschauen. Ist überhaupt ein Logfile vorhanden? Falls nicht, liegt es vermutlich am Trigger.&lt;br /&gt;
Den solltet ihr überprüfen. Scheinbar kommt es hin und wieder vor, dass dieser nicht gesetzt ist. Dazu einfach auf &#039;&#039;Triggers&#039;&#039; klicken und mit &#039;&#039;Add trigger&#039;&#039; erneut einen anlegen. Hier muss, wie in der Anleitung unter &#039;&#039;&#039;AWS Lambda Funktion anlegen&#039;&#039;&#039; Punkt 7, die &#039;&#039;Application Id&#039;&#039; stehen und der Haken bei &#039;&#039;Enable trigger&#039;&#039; gesetzt sein. Dann alexa-fhem neu starten.&lt;br /&gt;
Wenn ihr Änderungen gemacht habt und den alexa-fhem Dienst noch nicht neu gestartet habt, wäre jetzt der richtige Zeitpunkt. Fürs Debugging empfiehlt es sich, alexa-fhem in einer Konsole laufen zu lassen, um eingehende Anfragen mitverfolgen zu können.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Es kann sein, dass immer noch keine Log im Cloudwatch ([http://docs.aws.amazon.com/de_de/lambda/latest/dg/monitoring-functions-logs.html]) zu sehen ist. In dem Fall hilft es, eine neue Role Policy anzulegen. &lt;br /&gt;
* in der AWS Console [https://console.aws.amazon.com] oben links auf Services klicken, und in der Gruppe &amp;quot;Security, Identity &amp;amp; Compliance&amp;quot; auf IAM klicken&lt;br /&gt;
* links auf Roles klicken&lt;br /&gt;
* Auf dem Knopf &amp;quot;Create Role&amp;quot; klicken&lt;br /&gt;
* AWS Services &amp;gt; Lambda auswählen, unten auf Next:Permissions klicken&lt;br /&gt;
* im Filter / Policy Type,  &amp;quot;log&amp;quot; eintragen (ohne quotes)&lt;br /&gt;
* CloudWatchLogsFullAccess hacken, auf Next:Review unten klicken&lt;br /&gt;
* Name vergeben und mit &amp;quot;Create role&amp;quot; bestätigen&lt;br /&gt;
* Oben links auf Services klicken, und in der Gruppe &amp;quot;Compute&amp;quot;, auf Lambda klicken&lt;br /&gt;
* auf den Name der Funktion klicken&lt;br /&gt;
* Reiter Configuration auswählen&lt;br /&gt;
* in Existing Role, den neukreierten Role auswählen&lt;br /&gt;
* oben auf Save (und Testen wenn gewünscht) klicken.&lt;br /&gt;
Schon sollte eine neue Gruppe im Cloudwatch sichtbar sein. Die Suche von den Devices in Alexa wiederholen, und die Logs analysieren&lt;br /&gt;
&lt;br /&gt;
====Was ist zu tun, wenn sich der Alexa-Service nicht starten lässt?====&lt;br /&gt;
{{Randnotiz|RNTyp=[g|Info]|RNText=Der User in der User= Directive von alexa.service muss Ausführungsrecht auf dem alexa binary haben (x), so wie auch mind. Lesezugriff auf dem Verzeichnis nach -U Option in der ExecStart= Directive und auch auf dem WorkingDirectory }}&lt;br /&gt;
Schaut bitte in das Unterverzeichnis [alexa-fhem (also dort, wo Ihr Alexa-FHEM instelliert habt]/bin. Die dort befindliche Datei &#039;&#039;alexa&#039;&#039; muss ausführbar sein. Also z.B. so:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:70%;&amp;quot;&amp;gt;2755327 4 -rwxr-xr-x 1 pi pi  339 Nov 26 23:20 alexa&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Sollte dies nicht der Fall sein bitte mit:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:70%;&amp;quot;&amp;gt;chmod +x alexa&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
die Datei ausführbar machen. Sofern der User &amp;quot;pi&amp;quot; Eigentümer ist, ist kein sudo erforderlich.&lt;br /&gt;
&lt;br /&gt;
Eine lauffähige Konfiguration ist {{Link2Forum|Topic=71612|Message=668383|LinkText=hier}} zu sehen.&lt;br /&gt;
&lt;br /&gt;
Ein Fehler in der Rechtekonfiguration führt in der Regel zu folgendem Ergebnis nach &amp;lt;code&amp;gt;sudo systemctl status alexa&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;   Loaded: loaded (/etc/systemd/system/alexa.service; enabled)&lt;br /&gt;
   Active: activating (auto-restart) (Result: exit-code) since mer. 2017-09-06 02:33:23 CEST; 3s ago&lt;br /&gt;
  Process: 18332 ExecStart=/opt/fhem/alexa-fhem/bin/alexa -U /home/alexa/.alexa (code=exited, status=217/USER)&lt;br /&gt;
 Main PID: 18332 (code=exited, status=217/USER)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Wie kann ich via Alexa-FHEM auf FHEM zugreifen, wenn der Port mit Benutzername/Kennwort geschützt ist?====&lt;br /&gt;
&lt;br /&gt;
Hierzu muss die Datei &amp;lt;code&amp;gt;~/.alexa/config.json&amp;lt;/code&amp;gt; geöffnet werden und der Abschnitt &amp;quot;connections&amp;quot; um folgende Zeile ergänzt werden:&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;quot;auth&amp;quot;: {&amp;quot;user&amp;quot;: &amp;quot;fhem&amp;quot;, &amp;quot;pass&amp;quot;: &amp;quot;fhempassword&amp;quot;},&amp;lt;/pre&amp;gt;&lt;br /&gt;
Bei Verwendung von SSL bei FHEM muss auch noch &amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;quot;ssl&amp;quot;: true,&amp;lt;/pre&amp;gt; hinzugefügt werden&lt;br /&gt;
&lt;br /&gt;
==Weitergehende Informationen==&lt;br /&gt;
*[[Alexa_und_Mappings]]&lt;br /&gt;
*[[Alexa_Tipps_und_Kniffe]]&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;br /&gt;
[[Kategorie:Sprachsteuerung]]&lt;/div&gt;</summary>
		<author><name>Dora71</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FTUI_Widget_Tts&amp;diff=29702</id>
		<title>FTUI Widget Tts</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FTUI_Widget_Tts&amp;diff=29702"/>
		<updated>2019-03-03T14:20:52Z</updated>

		<summary type="html">&lt;p&gt;Dora71: /* Attribute */ Beispiel für Stimmenauswahl korrigiert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Das [[{{PAGENAME}}|TTS Widget]] ist ein Widget für [[FHEM Tablet UI]], mit dem man eine Sprachansage  ausgeben kann. Damit lassen sich z.B. Warnungen ausgeben oder der Wetterbericht ansagen.&lt;br /&gt;
&lt;br /&gt;
==Attribute==&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Attribut&lt;br /&gt;
!Beschreibung&lt;br /&gt;
!Standard-Wert&lt;br /&gt;
!Beispiel&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-get&#039;&#039;&#039;||Reading, aus dem der angesagte Text ausgelesen wird||state||data-get=&amp;quot;state&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-voice&#039;&#039;&#039;||Auswahl der Stimme||Deutsch Female||data-voice=&amp;quot;UK English Male&amp;quot;&amp;lt;br /&amp;gt; data-voice=&amp;quot;Chinese Female&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-pitch&#039;&#039;&#039;||Tonhöhe (Bereich 0 bis 2)||1.0||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-rate&#039;&#039;&#039;||Sprachgeschwindigkeit (Bereich 0 bis 1.5)||1.0||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-volume&#039;&#039;&#039;||Lautstärke (Bereich 0 bis 1)||1.0||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==CSS Klassen==&lt;br /&gt;
Keine&lt;br /&gt;
&lt;br /&gt;
==Hinweise==&lt;br /&gt;
* Nach der Ersteinrichtung oder Änderung einer Stimme kann die erste Sprachausgabe verzögert werden, da die Daten der Stimme über das Internet geladen werden müssen.&lt;br /&gt;
* Um eine kurze Pause zwischen zwei Worten zu erzeugen, z.B. um Hinweise deutlicher erscheinen zu lassen kann das Komma verwendet werden: set speak Bewegung,Haustür&lt;br /&gt;
* Im iOS-Umfeld werden Sounds (und auch Videos) nicht immer automatisch abgespielt. Nähere Details [https://developers.google.com/web/updates/2017/09/autoplay-policy-changes hier]. Um das tts-Widget auf einem Apple-Tablet nutzen zu können, muss man die Seite (..../fhem/ftui/) zum Homebildschirm hinzufügen.&lt;br /&gt;
&lt;br /&gt;
==Beispiele==&lt;br /&gt;
===Einfache Ansage===&lt;br /&gt;
In FHEM einen Dummy definieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
define speak dummy&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf der gewünschten FTUI Seite ganz am Anfang nach dem &amp;lt;body&amp;gt; Tag das Widget einbauen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div data-type=&amp;quot;tts&amp;quot; data-device=&amp;quot;speak&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In FHEM in der Eingabezeile eingeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
set speak Guten Morgen&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt sollte das Tablet &amp;quot;Guten Morgen&amp;quot; sagen. Das ganze lässt sich jetzt z.B. in notifys oder at-Befehle einbauen.&lt;br /&gt;
&lt;br /&gt;
===Zeitansage===&lt;br /&gt;
Dummy definieren und Widget einbauen wie in Beispiel 1&lt;br /&gt;
&lt;br /&gt;
In FHEM ein at definieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
defmod EveryHour at +*01:00:00 {fhem (&amp;quot;set speak Es ist &amp;quot;.strftime(&#039;%H&#039;, localtime).&amp;quot; Uhr&amp;quot;);}&lt;br /&gt;
attr EveryHour alignTime 00:00&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu jeder vollen Stunde wird jetzt die Zeit angesagt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Einstellbare Zeitansage===&lt;br /&gt;
Montag bis Freitag wird zu einer einstellbaren Zeit selbige angesagt.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
defmod Sprachausgabe dummy&lt;br /&gt;
attr Sprachausgabe readingList Ist_aktiv&lt;br /&gt;
attr Sprachausgabe userReadings Ist_aktiv&lt;br /&gt;
&lt;br /&gt;
defmod Sprachausgabe_Hauptschalter dummy&lt;br /&gt;
attr Sprachausgabe_Hauptschalter setList on off&lt;br /&gt;
&lt;br /&gt;
defmod Sprachbefehl at *07:00 { if ( !($we) &amp;amp;&amp;amp; Value(&amp;quot;Sprachausgabe_Hauptschalter&amp;quot;) eq &amp;quot;on&amp;quot; ) {fhem(&amp;quot;set Sprachausgabe Es ist [Sprachbefehl:TIMESPEC]&amp;quot;)}}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div data-type=&amp;quot;tts&amp;quot; data-device=&amp;quot;Sprachausgabe&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;div data-type=&amp;quot;checkbox&amp;quot; data-device=&amp;quot;Sprachausgabe_Hauptschalter&amp;quot;&lt;br /&gt;
	class=&amp;quot;inline top-space&amp;quot; data-on-background-color=&amp;quot;SeaGreen&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
	&amp;lt;div data-type=&amp;quot;label&amp;quot; class=&amp;quot;inline&amp;quot; data-limits-get=&amp;quot;Sprachausgabe_Hauptschalter:STATE&amp;quot; data-states=&#039;[&amp;quot;on&amp;quot;,&amp;quot;off&amp;quot;]&#039; data-colors=&#039;[&amp;quot;SeaGreen&amp;quot;,&amp;quot;grey&amp;quot;]&#039;&amp;gt;Zeitansage &amp;lt;u&amp;gt;Mo ... Fr&amp;lt;/u&amp;gt; um&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div data-type=&amp;quot;datetimepicker&amp;quot;&lt;br /&gt;
     data-device=&amp;quot;Sprachbefehl&amp;quot;&lt;br /&gt;
     data-datepicker=&amp;quot;false&amp;quot;&lt;br /&gt;
     data-step=&amp;quot;5&amp;quot;&lt;br /&gt;
	 data-format=&amp;quot;H:i&amp;quot;&lt;br /&gt;
	 data-get=&amp;quot;TIMESPEC&amp;quot; data-set-value=&amp;quot;*$v&amp;quot; data-cmd=&amp;quot;modify&amp;quot;&lt;br /&gt;
	 data-limits-get=&amp;quot;Sprachausgabe_Hauptschalter:STATE&amp;quot; data-states=&#039;[&amp;quot;on&amp;quot;,&amp;quot;off&amp;quot;]&#039; data-colors=&#039;[&amp;quot;SeaGreen&amp;quot;,&amp;quot;grey&amp;quot;]&#039;&lt;br /&gt;
     class=&amp;quot;inline&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[Datei:Zeitansage.png]]&lt;br /&gt;
&lt;br /&gt;
Teile dieses Codes stammen aus [[FTUI_Beispiel_Datetimepicker_f%C3%BCr_Timer]].&lt;br /&gt;
&lt;br /&gt;
==Links== &lt;br /&gt;
[https://responsivevoice.org/faq/ ResponsiveVoice F.A.Q.]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:FHEM Tablet UI|TTS]]&lt;br /&gt;
[[Kategorie:Akustische Ausgabe]]&lt;/div&gt;</summary>
		<author><name>Dora71</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=SIGNALduino&amp;diff=29342</id>
		<title>SIGNALduino</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=SIGNALduino&amp;diff=29342"/>
		<updated>2019-02-02T10:49:29Z</updated>

		<summary type="html">&lt;p&gt;Dora71: /* Unterstützte Geräte */ Conrad Wetterstation KW9110 Details ergänzt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Empfang und Verarbeitung von digitalen Signalen&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModFTopic=38402&lt;br /&gt;
|ModCmdRef=SIGNALduino&lt;br /&gt;
|ModForumArea=Sonstige Systeme&lt;br /&gt;
|ModTechName=00_SIGNALduino.pm&lt;br /&gt;
|ModOwner=Sidey ({{Link2FU|8018|Forum}}/[[Benutzer Diskussion:Sidey|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
=== Übersicht ===&lt;br /&gt;
Unter den Namen SIGNALduino versteht man sowohl den Low-Cost Empfänger für digitale Signale (vergleichbar dem  [[CUL]]) als auch das gleichnamige Modul mit dem Dateinamen 00_SIGNALduino.pm. Mit dem SIGNALduino kann man entweder 433 oder 868 MHz-Geräte auslesen und ansprechen. Der SIGNALduino funktioniert auch mit anderen Medien wie Infrarot oder direkter Kabelverbindung.&lt;br /&gt;
&lt;br /&gt;
Der SIGNALduino(-Stick) erkennt Signale anhand von Mustern und gibt sie (als maximal detaillierte Beschreibung einer Signalabfolge) dann schlicht sofort nur noch an FHEM zur Auswertung (Dekodierung) weiter. Auch nicht erkannte Signale werden an FHEM übergeben.&lt;br /&gt;
Aufgabe des SIGNALduino (Firmware/Stick) ist es also nur, Signal-Aktivitäten zu erfassen und maximal präzise (als kurzer Text-String) zu beschreiben.&lt;br /&gt;
Alles weitere (echte, finale Auswertung / Zuweisung dieser Signal-Daten) wird dann auf einer großen Box (Raspberry o.ä.) gemacht.&lt;br /&gt;
&lt;br /&gt;
=== Vorteil gegenüber einem CUL/FHEMduino ===&lt;br /&gt;
Der SIGNALduino hat den Vorteil einer sehr schnellen Demodulation des Funksignals. Sollen weitere Protokolle dekodiert werden, so muss dazu nur ein passendes FHEM Modul entwickelt oder ein universelles Modul erweitert werden (also auf flexibel direkt updatebarer Rechner-Seite!!). Änderungen am Arduino-Firmware-Code sind normalerweise nicht notwendig (sofern die grundsätzliche Signal-Klassifizierung des Sticks korrekt funktioniert - leider nicht immer, z.B.: [https://github.com/RFD-FHEM/SIGNALDuino/issues/65 MU-Nachrichten werden z.T. als MC erkannt]).&lt;br /&gt;
&lt;br /&gt;
Ein großer Vorteil des SIGNALduino besteht darin, dass auch Geräte mit leicht abweichende Frequenzen steuerbar sind; so empfangen die Somfy-Rolladenmotoren beispielsweise auf 433.42 und nicht, wie bei anderen Geräten sehr oft üblich, auf 433.92 MHz. Die Frequenzumstellung stellt für den SIGNALduino kein Problem dar.&lt;br /&gt;
&lt;br /&gt;
Ebenso kann man den SIGNALduino direkt an den Sendeausgang eines Sensors anbinden und die digitalen Signale empfangen, dabei bitte aber auf die passenden Spannungen achten, denn ein Arduino Nano verträgt nur 5V.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der SIGNALduino (Hardware) wird über den USB Port angeschlossen, er kann aber auch mit zusätzlichen ESP Modulen über ein WLAN angebunden werden. &lt;br /&gt;
&lt;br /&gt;
Der SIGNALduino basiert auf einem [http://arduino.cc/de/Main/ArduinoBoardNano Arduino Nano], die Schaltung entspricht der des [[FHEMduino]] oder dem [[Selbstbau_CUL]]:&lt;br /&gt;
* Entweder wird ein Arduino mit einfachen Sende- und Empfangsmodulen verwendet, dann ist die Verkabelung identisch zum [[FHEMduino]] &lt;br /&gt;
* Oder es wird ein CC1101 Transceiver verwendet, dann ist die Verkabelung identisch zum [[Selbstbau_CUL]].&lt;br /&gt;
* Zuletzt gibt es ein fertig konfektioniertes Modul von In-Circuit mit Radino CC1101 Varianten, link zum [http://shop.in-circuit.de/index.php Hersteller]. &lt;br /&gt;
&lt;br /&gt;
Achten Sie beim Selbstbau auf die entsprechenden Sender-Empfänger. Der sehr preiswert angebotene XY-MK-5V hat sich als zu unzuverlässig erwiesen, während anscheinend beim CC1101 (insbesondere der &amp;quot;grünen Version&amp;quot;) keine Probleme auftreten. &lt;br /&gt;
&lt;br /&gt;
Es stehen auch für den [https://www.arduino.cc/en/Main/arduinoBoardUno UNO] und [https://www.arduino.cc/en/Main/ArduinoBoardProMini PRO Mini] Firmware-Dateien zur Verfügung. Die ausgelieferte Firmware läuft nur auf Mikrocontrollern mit 16 MHz; wer einen Mikrocontroller mit 8 MHz verwenden möchte, muss die Firmware selbst compilieren. Die SW ist auf [https://github.com/RFD-FHEM/SIGNALDuino github]. Vorgesehen ist nur die Übersetzung unter Windows mit Visual Studio und dem Visual Micro Zusatz. Es gibt aber auch eine Anleitung, wie man mit der [[Arduino]] IDE oder einem Makefile [[SIGNALduino Compilieren]] kann.&lt;br /&gt;
&lt;br /&gt;
Es gibt auch eine Variante des SIGNALduino, die auf einem [[ESP8266]] nativ läuft, diese funktioniert seit Anfang 2018 annehmbar, allerdings befindet diese sich noch in einer Entwicklungsphase.&lt;br /&gt;
&lt;br /&gt;
An den &amp;quot;SIGNALESP&amp;quot; kann auch ein CC1101 via SPI angebunden werden:&lt;br /&gt;
&lt;br /&gt;
{||&lt;br /&gt;
!CC1101 Bezeichnung&lt;br /&gt;
!ESP Pin&lt;br /&gt;
|-&lt;br /&gt;
|CLK||GPIO14&lt;br /&gt;
|-&lt;br /&gt;
|MOSI||GPIO13&lt;br /&gt;
|-&lt;br /&gt;
|MISO||GPIO12&lt;br /&gt;
|-&lt;br /&gt;
|CSN||GPIO15&lt;br /&gt;
|-&lt;br /&gt;
|GDO0||GPIO4&lt;br /&gt;
|-&lt;br /&gt;
|GDO2||GPIO5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wird ein einfacher Empfänger / Sender Kombination verwendet, dann über die PINs:&lt;br /&gt;
&lt;br /&gt;
{||&lt;br /&gt;
! Bezeichnung &lt;br /&gt;
! ESP Pin&lt;br /&gt;
|-&lt;br /&gt;
|Transmitter||GPIO4&lt;br /&gt;
|-&lt;br /&gt;
|Receiver||GPIO5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Einfache Module ===&lt;br /&gt;
{{Randnotiz|RNTyp=r|RNText=Viele user berichten über Empfangsprobleme bei Nutzung des XY-MK-5V; es wird ausdrücklich empfohlen, ein anderes Empfangsmodul zu nutzen!}}&lt;br /&gt;
[[Datei:Fhemduino_schematic.png|200px|thumb|right|Beispielschaltplan SIGNAL(FHEM)duino]]  &lt;br /&gt;
&lt;br /&gt;
Mit einem Arduino-Nano und folgenden, billigen Sende- und Empfangsmodulen können Sie einen SIGNALduino bauen:&lt;br /&gt;
* FS1000A Dies ist das Sendemodul (TX) und wird oft im Set mit dem billigen XY-MK-5V-Empfänger angeboten (etwa 5€). &lt;br /&gt;
* RXB6 Das ist das empfohlene Empfangsmodul (RX), statt XY-MK-5V, etwa 5€ aus Deutschland.&lt;br /&gt;
&lt;br /&gt;
Die Verkabelung erfolgt analog zum [[FHEMduino]] und das bedeutet (Arduino -&amp;gt; Modul):&lt;br /&gt;
* PIN D2 an DATA des RX-Moduls&lt;br /&gt;
* PIN D11 an DATA des TX-Moduls (PIN links mit Beschriftung ATAD)&lt;br /&gt;
&lt;br /&gt;
Zusätzlich muss noch jeweils GND und 5V des Arduino mit dem GND bzw. VCC des Moduls verbunden werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt fehlen noch die Antennen. Dafür braucht man ein 17,2 cm langes Stück Kupferdraht, das wird beim Anschluss &amp;quot;ANT&amp;quot; jeweils am Modul angelötet (anfängergeeignet).&lt;br /&gt;
&lt;br /&gt;
== Software: Modul ==&lt;br /&gt;
&lt;br /&gt;
===  USB-ID ermitteln  ===&lt;br /&gt;
Bevor der SIGNALduino mit dem FHEM Server (im Beispiel hier ein Raspberry PI) verbunden werden kann, muss die USB-Schnittstelle ermittelt werden. Hierzu bitte auf dem Terminal den Befehl&lt;br /&gt;
&amp;lt;pre&amp;gt; ls -l /dev/serial/by-id &amp;lt;/pre&amp;gt;&lt;br /&gt;
ausführen. Beispielhaft sieht das Ergebnis etwa so aus: &lt;br /&gt;
&#039;&#039;lrwxrwxrwx 1 root root 13 Jan 31 00:02 &#039;&#039;&#039;usb-FTDI_FT232R_USB_UART_A903N5T5-if00-port&#039;&#039;&#039; -&amp;gt; ../../ttyUSB0&#039;&#039; &lt;br /&gt;
Damit ist der Anschluss des SIGNALduino bestimmt und das Gerät kann wie im nächsten Abschnitt beschrieben definiert werden. Zuvor muss noch das Modul geladen werden.&lt;br /&gt;
&lt;br /&gt;
=== FHEM-Modul laden ===&lt;br /&gt;
Die SIGNALduino Module werden über das FHEM [[update]] verteilt, sobald die Änderungen einen &amp;quot;stabilen&amp;quot; und alltags tauglichen Zustand haben.&lt;br /&gt;
&lt;br /&gt;
Die in der Entwicklung befindliche Version kann mit folgenden Befehlen geladen werden:&lt;br /&gt;
&lt;br /&gt;
* FHEM aktualisieren: &amp;lt;code&amp;gt;update&amp;lt;/code&amp;gt; &lt;br /&gt;
* SIGNALduino Modul aktualisieren: &amp;lt;code&amp;gt;update all &amp;lt;nowiki&amp;gt;https://raw.githubusercontent.com/RFD-FHEM/RFFHEM/dev-r33/controls_signalduino.txt&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;  Durch das Update von FHEM wird sichergestellt, dass das Modul mit FHEM arbeitet.&lt;br /&gt;
*Danach kann das Gerät wie folgt definiert werden (die Spezifikation des USB-Anschlusses muss dabei an die aktuellen Gegebenheiten angepasst werden):&lt;br /&gt;
:&amp;lt;code&amp;gt;define &amp;lt;eigener-SIGNALduino-Name&amp;gt; SIGNALduino /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A903N5T5-if00-port0@57600&amp;lt;/code&amp;gt;&lt;br /&gt;
* Solltet Ihr einen SIGNALESP via IP einbinden wollen ist die Syntax&lt;br /&gt;
:&amp;lt;code&amp;gt;define &amp;lt;eigener-SIGNALESP-Name&amp;gt; SIGNALduino &amp;lt;ip-adresse&amp;gt;:23&amp;lt;/code&amp;gt;&lt;br /&gt;
Nach dem Einbinden wird der SIGNALduino, falls er erkannt wird, im Status &amp;quot;Opened&amp;quot; angezeigt. &lt;br /&gt;
&lt;br /&gt;
Für neuere Entwicklungen kann in FHEM auch dauerhaft die developer Version aktualisiert werden:&lt;br /&gt;
&amp;lt;code&amp;gt;update add &amp;lt;nowiki&amp;gt;https://raw.githubusercontent.com/RFD-FHEM/RFFHEM/dev-r33/controls_signalduino.txt&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
Danach wird FHEM bei dem normalen Update-Befehl immer automatisch die aktuelle dev Version laden.&lt;br /&gt;
&lt;br /&gt;
Die nachfolgenden Beispiel-Befehle verwenden &amp;quot;sduino&amp;quot; als &amp;lt;eigener-SIGNALduino-Name&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Flashen des Arduino mit der SIGNALduino Firmware ====&lt;br /&gt;
Falls avrdude noch nicht vorhanden ist, kann es mit folgendem Befehl installiert werden:&lt;br /&gt;
:&amp;lt;code&amp;gt;sudo apt-get install avrdude&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In FHEM ist der SIGNALduino mit dem Status &amp;quot;Open&amp;quot; vorhanden. Jetzt müssen wir FHEM noch mitteilen, welche Hardware wir angeschlossen haben. Über das Attribut &#039;&#039;hardware&#039;&#039; lässt sich zwischen den mitgelieferten Firmware-Dateien wechseln. Solltet ihr einen Nano mit cc1101 Transceiver verwenden, so wählt bitte folgende Hardware&lt;br /&gt;
&amp;lt;code&amp;gt;attr sduino hardware nanoCC1101&amp;lt;/code&amp;gt;&lt;br /&gt;
sonst üblicherweise&lt;br /&gt;
&amp;lt;code&amp;gt;attr sduino hardware nano328&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der &#039;&#039;flash&#039;&#039; Befehl abgesetzt werden: &lt;br /&gt;
&amp;lt;code&amp;gt;set sduino flash &amp;lt;/code&amp;gt;&lt;br /&gt;
Dadurch wird der Arduino mit der gewählten Firmware geflasht. Das Ergebnis wird im Webinterface direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
Alternativ kann auch der Flash-Befehl mit einem Dateinamen aufgerufen werden. Diese Möglichkeit sollte jedoch nur verwendet werden, wenn die SIGNALduino Firmware selbst compiliert wurde und eine andere Hardware verwendet wird. Der Flash-Befehl wird wie folgt aufgerufen:&lt;br /&gt;
:&amp;lt;code&amp;gt;set sduino flash FHEM/firmware/SIGNALduino_mega2560.hex&amp;lt;/code&amp;gt;&lt;br /&gt;
(je nachdem wo und unter welchem Namen die .hex Datei abgelegt wurde)&lt;br /&gt;
&lt;br /&gt;
==== Flashen einer Firmware über HTTP ====&lt;br /&gt;
Die Firmware wird in absehbarer Zeit nicht mehr über den Update Mechanismus verteilt werden. &lt;br /&gt;
Damit die passende Firmware auf den SIGNALduino geladen werden kann, wird diese dann über HTTP geladen.&lt;br /&gt;
&lt;br /&gt;
==== Vorabversion einer Firmware ====&lt;br /&gt;
Die Firmware des SIGNALduino wird ebenso wie das FHEM Modul auch weiter entwickelt.&lt;br /&gt;
Die Entwickler sind auf Tests und Rückmeldungen der Nutzer angewiesen, da leider nicht alle Sensoren vorher getestet werden können.&lt;br /&gt;
&lt;br /&gt;
Aktuell (November 2018) ist noch die Version 3.3.1 in Entwicklung. Diese steht aktuell in einer Release Candidate Version zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die folgenden Microcontroller kann man die Firmware downloaden. (Die Download Links funktionieren erst ab einer FHEM-Modul Version vom 11.03.2018!)&lt;br /&gt;
&lt;br /&gt;
SIGNALDuino_nanocc1101.hex für einen Nano mit CC1101&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set sduino flash https://github.com/RFD-FHEM/SIGNALDuino/releases/download/3.3.1-RC10/SIGNALDuino_nanocc1101release.hex&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNALDuino_nano.hex für einen Nano ohne cc1101&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set sduino flash https://github.com/RFD-FHEM/SIGNALDuino/releases/download/3.3.1-RC10/SIGNALDuino_nanorelease.hex&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNALDuino_promini.hex für einen  promini 3,3v / 8 Mhz ohne cc1101&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set sduino flash https://github.com/RFD-FHEM/SIGNALDuino/releases/download/3.3.1-RC10/SIGNALDuino_prominirelease.hex&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNALDuino_miniculcc1101.hex für einen promini 3,3v / 8 Mhz mit cc1101 (minicul)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set sduino flash https://github.com/RFD-FHEM/SIGNALDuino/releases/download/3.3.1-RC10/SIGNALDuino_miniculcc1101release.hex&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNALDuino_radinocc1101.hex für einen radino mit cc1101 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set sduino flash https://github.com/RFD-FHEM/SIGNALDuino/releases/download/3.3.1-RC10/SIGNALDuino_radinocc1101release.hex&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
SIGNALESP_331rc4.bin (mit cc1101) für einen ESP8266 mit 1 MB Flash&lt;br /&gt;
&amp;lt;code&amp;gt;set ipduino flash https://github.com/RFD-FHEM/SIGNALESP/releases/download/3.3.1RC4/SIGNALESP_331RC4_1M.bin&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
!Achtung, aktuell wird die Firmware für den ESP dadurch nur herunter geladen. Flashen müsst ihr leider immer noch über ein passendes Tool &lt;br /&gt;
z.B. [https://github.com/nodemcu/nodemcu-flasher ESP8266Flasher.exe] oder Esptool und einer seriellen Konsole.&lt;br /&gt;
&lt;br /&gt;
Nach dem Booten des ESPs, spannt dieser ein eigenes WLAN auf. Habt ihr euch damit verbunden, könnt ihr den ESP mit eurem vorhandenen WLAN verbinden.&lt;br /&gt;
&lt;br /&gt;
Die Haupt-Seite für Firmware-Releases findet sich unter https://github.com/RFD-FHEM/SIGNALDuino/releases/ .&lt;br /&gt;
&lt;br /&gt;
==== Flashen eines radino Boards mit ATmega32U4 ====&lt;br /&gt;
&lt;br /&gt;
Diese Funktion steht nur in der Entwicklungsversion dev-r33 zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
Auch sind Berichte bekannt, dass der Radino beim Neustart von FHEM nicht korrekt initialisiert wird.&lt;br /&gt;
&lt;br /&gt;
==== Fehler beim Flashen ====&lt;br /&gt;
Sollte bei einem Flash Vorgang ein Fehler auftreten, solltet ihr zunächst im Logfile mit Verbose 5 nachsehen.&lt;br /&gt;
&lt;br /&gt;
Findet ihr dort keine Fehlermeldung, gibt es noch ein separates Flashlog, welches ihr über einen Browser aufrufen könnt. Dazu müsst ihr nur den Folgenden Pfad an euren Servernamen anhängen:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
/fhem/FileLog_logWrapper?dev=Logfile&amp;amp;type=text&amp;amp;file=SIGNALduino-Flash.log&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Geräteerkennung ===&lt;br /&gt;
==== Unterstützte Geräte ====&lt;br /&gt;
Für die folgenden Geräte gibt es derzeit (2017) eine Unterstützung für den Betrieb mit FHEM. Die Geräte werden [[autocreate|automatisch erkannt]] und in der Konfiguration eingetragen, wenn der SIGNALduino läuft.&lt;br /&gt;
Bitte Geräte mit sehr präzisen Referenzen hier listen (Produktnummer, Protokoll-Variantenname etc.), damit eine globale Suche/Verifikation maximal erfolgreich ist.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot;| Produkt &lt;br /&gt;
! (E)mpfangen&amp;lt;br /&amp;gt;(S)enden &lt;br /&gt;
! Hinweise &lt;br /&gt;
! Verwendetes Modul &lt;br /&gt;
! Protokoll ID&lt;br /&gt;
|-&lt;br /&gt;
|Conrad Wetterstation KW9110||E S||Sensor: KW9010, neben Temperatur u. Luftfeuchte werden auch Trend, Batterie u. Kanal erfasst|| CUL_TCM97001  || 0.3&lt;br /&gt;
|-&lt;br /&gt;
|TCM Wetterstation (97001 und 21xxx Serie)||E|| || CUL_TCM97001 || 0&lt;br /&gt;
|-&lt;br /&gt;
|ABS Wetterstation (ABS 700)||E|| || CUL_TCM97001  || 0&lt;br /&gt;
|-&lt;br /&gt;
|Prologue Wetterstation ||E|| ||CUL_TCM97001 || 0&lt;br /&gt;
|-&lt;br /&gt;
|Rubicson Wetterstation ||E|| ||CUL_TCM97001 ||0 &lt;br /&gt;
|-&lt;br /&gt;
|NC_WS Wetterstation ||E|| ||CUL_TCM97001 || 0&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.gt-support.de/ GT-WT-02 Wetterstation]||E|| ||CUL_TCM97001 || 0&lt;br /&gt;
|-&lt;br /&gt;
|AURIOL Wetterstation ||E|| ||CUL_TCM97001 || 0&lt;br /&gt;
|-&lt;br /&gt;
|Mebus Wetterstation ||E|| ||CUL_TCM97001 || 0&lt;br /&gt;
|-&lt;br /&gt;
|Intertechno Funkschalter||E S|| ||IT || 3,4,5,17&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;strike&amp;gt;Conrad RSL Funkschalter&amp;lt;/strike&amp;gt;||E S|| Funktioniert aktuell nicht || SIGNALduino_RSL  || &lt;br /&gt;
|-&lt;br /&gt;
|[http://global.oregonscientific.com/product_view.php?id=5 Oregon Scientific Wettersensoren]||E || Protokoll V2 &amp;amp; V3 implementiert || OREGON || 10&lt;br /&gt;
|-&lt;br /&gt;
|Bresser Temp/Hydro Sensor||E || || Hideki || 12&lt;br /&gt;
|-&lt;br /&gt;
|[https://de.hama.com/00104985/hama-aussensensor-ts33c-fuer-wetterstation Hama TS33C]||E || || Hideki || 12&lt;br /&gt;
|-&lt;br /&gt;
|TFA Temp/Hydro Sensor||E || || Hideki || 12&lt;br /&gt;
|-&lt;br /&gt;
|Lacrosse TX2/TX3 Sensoren||E || || CUL_TX || 8&lt;br /&gt;
|-&lt;br /&gt;
|TFA 30320902||E || || SD_WS07 || 7&lt;br /&gt;
|-&lt;br /&gt;
|Eurochron eas800z||E || || SD_WS07  || 7&lt;br /&gt;
|-&lt;br /&gt;
|Technoline WS6750/TX70DTH||E || || SD_WS07 || 7&lt;br /&gt;
|-&lt;br /&gt;
|FreeTec Außenmodul NC-7344||E || || SD_WS07 || 7&lt;br /&gt;
|-&lt;br /&gt;
|CTW600||E || || SD_WS09 || 9&lt;br /&gt;
|-&lt;br /&gt;
|WH1080||E || || SD_WS09 || 9&lt;br /&gt;
|-&lt;br /&gt;
|Visivon remote pt4450||E || || none || 24&lt;br /&gt;
|-&lt;br /&gt;
|Einhell HS 434/6||E || || none || 21&lt;br /&gt;
|-&lt;br /&gt;
|Flamingo FA20RF / FA21RF Rauchmelder||E || || none || 13&lt;br /&gt;
|-&lt;br /&gt;
|mumbi m-FS300||E || || none || 26,27&lt;br /&gt;
|-&lt;br /&gt;
|TFA 30.3200||E || || none || 33&lt;br /&gt;
|-&lt;br /&gt;
|Livolo||E|| || none || 20&lt;br /&gt;
|-&lt;br /&gt;
|Smartwares RM174RF/2 (RM174RF-001CPR) 4500177571 ||E [S?]|| IT EV1527; TODO herausfinden: Alarmierung (wie Alarmton getriggered werden kann); Batterieinfo? || IT || 3&lt;br /&gt;
|-&lt;br /&gt;
|Smartwares SH5-TSO-A||E S|| || IT || ?&lt;br /&gt;
|-&lt;br /&gt;
|X10 Security Devices||E|| ||  || 39&lt;br /&gt;
|-&lt;br /&gt;
|[[Somfy_via_SIGNALduino|Somfy RTS]]||E S|| || SOMFY || 43&lt;br /&gt;
|}&lt;br /&gt;
Bei einigen Intertechno-Funksteckdosen (Brennenstuhl) kann es zu Empfangsproblemen kommen. Hier muss die Taktrate, mit der gesendet wird, angepasst werden. Dazu muss für &#039;&#039;Funksteckdose&#039;&#039; (also sauber per-Client-Instanz-spezifisch, NICHT SIGNALduino-Transceiver-global) das Attribut &lt;br /&gt;
:&amp;lt;code&amp;gt;attr &amp;lt;Funksteckdose&amp;gt; ITclock 300&amp;lt;/code&amp;gt; &lt;br /&gt;
gesetzt werden, der Standardwert ist 250.&lt;br /&gt;
&lt;br /&gt;
==== Mein Gerät wird in FHEM nicht erkannt ====&lt;br /&gt;
1. Prüfen, ob vom Sensor die Signaldaten (verbose &amp;gt;=4) erkannt werden. Sobald ihr die empfangenen Signaldaten im Logfile zuordnen könnt, geht es weiter mit:&lt;br /&gt;
&lt;br /&gt;
2. Eröffnet ein Thema unter [https://github.com/RFD-FHEM/RFFHEM/issues github] nach folgendem Muster:&lt;br /&gt;
:&#039;&#039;Thema :  Protokoll für &amp;lt;Das verwendete Gerät&amp;gt;&lt;br /&gt;
:Inhalt:  Eure Hardware z.B. Arduino Nano mit XYZ Empfänger, oder Arduino Nano direkt an Gerät x&lt;br /&gt;
&lt;br /&gt;
3. Auszug aus dem Logfile, welches zum Gerät gehört.&lt;br /&gt;
:&#039;&#039;Alles was ihr sonst noch über das Gerät und die übertragenen Daten wisst.&lt;br /&gt;
&lt;br /&gt;
Im Forum solltet ihr solche Fragen besser nicht posten, wenn das Gerät noch nicht unterstützt wird, dazu ist Github besser geeignet. Inzwischen wurde im Wiki eine eigene Seite eröffnet, die sich mit der Erkennung unbekannter Protokolle beschäftigt: [[Unbekannte_Funkprotokolle#Ansatz_1_-_Versuchen|Unbekannte_Funkprotokolle]].&lt;br /&gt;
&lt;br /&gt;
==== Es wird ein Protokoll erkannt, Autocreate legt aber kein device an ====&lt;br /&gt;
Im SIGNALduino sind &amp;gt;30 Protokolle implementiert. Jedoch gibt es nur wenige Module, welche diese Protokolle verarbeiten.&lt;br /&gt;
Teilweise ist das auch nicht zwingend notwendig, um seine Anforderungen zu erfüllen. Insbesondere für Schalter bzw. Sensoren, die nur zwei Zustände kennen, geht es meist ohne Modul und automatisch angelegtem Gerät.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir an, wir haben einen Schalter. Dieser kann einen oder zwei Zustände senden.&lt;br /&gt;
Im FHEM Log (und, insbesondere, im FHEMWEB Event Monitor) tauchen Meldungen ähnlich dieser auf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
2015.11.15 15:52:23 4: SIGNALduino_unknown incomming msg: u85#FF8081&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir können mit Hilfe des Modules DOIF auf diese Nachricht eine Aktion ausführen:&lt;br /&gt;
&lt;br /&gt;
Entweder, wenn wir den Inhalt der Nachricht nur zu Teilen wissen, da er sich ändert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
define mydoif DOIF ([sduino:&amp;amp;DMSG] =~ &amp;quot;u85#FF8081&amp;quot;) (set Lamp on)&lt;br /&gt;
attr mydoif do always&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oder, wenn wir den Inhalt exakt kennen, dann auch als Vergleichsstring&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
define mydoif DOIF ([sduino:&amp;amp;DMSG] eq &amp;quot;u85#FF8081&amp;quot;) (set relais on)&lt;br /&gt;
attr mydoif do always&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Teil u85#FF8081 muss individuell angepasst werden, der Name eures SIGNALduino möglicherweise auch.&lt;br /&gt;
&lt;br /&gt;
Als Alternative zu DOIF hier ein regex-verwendendes notify-Beispiel für einen Sender, der meint, zwei Codes alternierend senden zu müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
define n_sender_trigger notify sduino:UNKNOWNCODE.*u41#(13B72253|163873B3) { my_sender_trigger_indicate();; }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Selbstverfreilich muss in diesem Moment auch eine sub my_sender_trigger_indicate() definiert werden (z.B. in FHEM/99_myUtils.pm), die dort z.B. als Test eine Log-Ausgabe (Log3()) machen kann.&lt;br /&gt;
&lt;br /&gt;
=== Das Logfile ===&lt;br /&gt;
Im Logfile ab [[Verbose]] 4 tauchen diverse Meldungen auf, deren Bedeutung kurz erläutert wird (verbose 3 unterdrückt diese Meldungen).&lt;br /&gt;
&lt;br /&gt;
UPDATE: der folgende Bereich ist von einem weniger erfahrenen Zeitgenossen früher nach Kräften erweitert/geschrieben worden. Mittlerweile existiert aber ein neuer Inhalt [[Unbekannte_Funkprotokolle]] (siehe auch Erwähnung weiter oben), der als sehr gut beschrieben und unvergleichlich detailreicher bezeichnet werden muss (&amp;quot;endlich gibt es sowas!&amp;quot;). Der Bereich hier dürfte somit zwar für grundlegende Verdeutlichungen noch recht sinnvoll sein, der Inhalt sollte allerdings evt. in eine konsistente Dokumentation überarbeitet (verlagert/dedupliziert/reduziert) werden.&lt;br /&gt;
&lt;br /&gt;
Die Protokolle (von der SIGNALDuino-Firmware gesendete Signal-Beschreibungs-Strings) können wie folgt unterschieden werden:&lt;br /&gt;
&lt;br /&gt;
*MS - Nachricht mit Sync Puls: Hierzu ein Beispiel&lt;br /&gt;
:&amp;lt;code&amp;gt;MS;P0=-108;P1=395;P2=-1033;P3=-547;P4=-19932;P5=-8916;P6=1368;D=151313131312131313131313131313131312121212121313131313131312131212132;CP=1;SP=5;&amp;lt;/code&amp;gt; P0-P6 sind die Signalpegel (Dauer und positiv/negativ). Hinter D= befindet sich die Abfolge der Signale. Die ersten beiden Ziffern 15 in D sind wie folgt zu lesen. Zuerst wurde ein Signal &amp;quot;1&amp;quot; also P1 mit 395 Mikrosekunden high (die Zeitdauer ergibt sich aufgrund der Mitteilung &amp;quot;P1=395&amp;quot;) und anschließend ein Signal &amp;quot;5&amp;quot; also P5 mit 8916 Mikrosekunden low (die Zeitdauer ergibt sich aufgrund der Mitteilung &amp;quot;P5=-8916&amp;quot;) gemessen. CP=1 ist die Referenz auf den Takt des Signales - der Basistakt ist in diesem Fall ~395 Mikrosekunden. SP=5 gibt die Referenz zum Syncpuls an, der das gesamte Signal einleitet. Welche Signalfolge nun eine binäre 1 bzw. 0 bedeutet, wird im SIGNALduino über die integrierte Protokoll Liste realisiert.&lt;br /&gt;
&lt;br /&gt;
*MC - Nachricht vom Typ Manchester: Manchesterkodierte Signale können bereits sehr einfach im Arduino in eine Binärform umgewandelt werden. Es wird hier nach IEEE 802.3 umgewandelt. In Manchester Signalen gibt es lange und kurze Pulse. Deren Durchschnittswert wird mit LL (long low), LH (long high), SL (short low) und SH (short high) übermittelt. Zusätzlich, um das Protokoll schneller erkennen zu können, wird die Taktfrequenz mit übermittelt (C=429 Mikrosekunden). Die Daten befinden sich hinter D= und werden in HEX Form übergeben.&lt;br /&gt;
:&amp;lt;code&amp;gt;MC;LL=-1066;LH=904;SL=-562;SH=385;D=332B4B4D54D5554B552CD2D554B2B5354A;C=429;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*MU - Message unsynced: Diese Art von Nachrichten sind nicht nach Manchester codiert und haben auch keinen erkennbaren Sync / Clock Signalpegel am Start der Nachricht. Bei diesen Nachrichtentypen ist es, im Vergleich zu den anderen, am wahrscheinlichsten, dass das übermittelte Signal unvollständig oder überhaupt kein Signal ist. Wie bei MS sind P0-P6 die Signalpegel und in D= wird die Abfolge der Signalpegel referenziert. CP=2 gibt auch hier die Referenz zum Takt an, allerdings muss dieser nicht korrekt erkannt worden sein.&lt;br /&gt;
:&amp;lt;code&amp;gt;MU;P0=1372;P1=-580;P2=362;P3=-1047;D=01212321212321212121212121212123212123212321232121212121212321;CP=2;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es erscheinen viele Meldungen dieser Art:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Fingerprint for MU Protocol id xxxx -&amp;gt; yyy matches, trying to demodulate&lt;br /&gt;
sduino: Starting demodulation at Position 1&lt;br /&gt;
Fingerprint for MU Protocol id 28 -&amp;gt; IC Ledspot matches, trying to demodulate&lt;br /&gt;
sduino: Starting demodulation at Position 1&lt;br /&gt;
Fingerprint for MU Protocol id 29 -&amp;gt; HT12e remote matches, trying to demodulate&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies sind nun Bemühungen, anhand der von der SIGNALDuino-Firmware gelieferten rohen aber detaillierten Signal-Strings eine Vor-Analyse / Fingerprinting vorzunehmen.&lt;br /&gt;
Man könnte nun z.B. bei solchen Fingerprinting-Analysen erkennen:&lt;br /&gt;
* dass der Basis-Takt-Wert innerhalb eines charakteristischen Zeit-Bereichs liegt&lt;br /&gt;
* dass die Anzahl der Sync-Pulse eine präzise Zahl ist&lt;br /&gt;
* dass Längen erkannter Puls-Typen innerhalb eines Bereichs liegen&lt;br /&gt;
&lt;br /&gt;
Mittels solcher Untersuchungen kann man also final hoffentlich hinreichend plausibel feststellen, &amp;quot;dass diese Aktivitäten offensichtlich(?) zu einer Funk-Komponente Rauchmelder von Hersteller XYZ gehören müssen, und man somit weiterleiten muss an ein (möglicherweise bereits existierendes) Userdaten-Dekodier-Modul für diese Herstellerkomponente&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei einer dann erfolgenden Demodulation des noch rohen SIGNALDuino-Strings könnte man z.B. (hoffentlich richtigerweise) annehmen, dass eine Signalpegel-Typ-Folge &amp;quot;13&amp;quot; eine binäre 1 bedeuten soll, während eine Folge &amp;quot;12&amp;quot; eine binäre 0 bedeuten soll. Man erhält aus dem Gesamt-Puls-String also nach vollständiger Demodulation eine Abfolge von vielen 0/1 Bits, die insgesamt ein Datenwort darstellen, mit einer gewissen Länge von NN bits (diese Längen-Angabe könnte übrigens - neben Namenssuche nach Hersteller oder Produkt etc. - ein wichtiges Such-Merkmal sein, ob andere Frameworks tatsächlich bereits wissen, wie Daten dieser Funk-Komponente zu dekodieren sind!). Dieses demodulierte Datenwort ist nun das finale Datenwort, welches einen Container für die Funk-Komponenten-Informationen darstellt (in diesem Container also beispielsweise enthalten: Temperatur, Feuchte, Akku-Status, ID, Alarm, ... - zumindest wenn nicht dummerweise der ganze Container erst einmal CRC- oder Crypto-verschlüsselt ist...).&lt;br /&gt;
&lt;br /&gt;
Man muss an dieser Stelle unbedingt sagen, dass dieses Userdaten-Datenwort (einer bestimmten Hersteller-Funk-Komponente!) natürlich bei &#039;&#039;jeglichen&#039;&#039; Transceiver-Systemen &#039;&#039;immer&#039;&#039; gleich erkannt werden &#039;&#039;muss&#039;&#039; - an dieser Stelle ist also ganz klar, dass diese Daten an &#039;&#039;allgemeine&#039;&#039; FHEM-Module weitergeleitet (Dispatched) werden müssen, die nach Übernahme von Daten von &#039;&#039;jeglichen&#039;&#039; Transceiver-Systemen diese Daten immer auf die gleiche Weise (&#039;&#039;&#039;&#039;&#039;generisch/zentral&#039;&#039;&#039;&#039;&#039;) für die jeweilige Hersteller-Funk-Komponente erledigen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Die Abfolge ist also ganz klar:&#039;&#039;&#039;&lt;br /&gt;
Funk-Aktivität --&amp;gt; Transceiver-Gerät/Firmware (SIGNALDuino) --&amp;gt; maximal detailreich beschreibender Rx-Analyse-Output-String --&amp;gt; Fingerprinting-Grobzuordnung des (SIGNALDuino-Firmware-)Outputs (durch 00_SIGNALduino.pm) auf gerätespezifisches Verhalten --&amp;gt; &#039;&#039;generische/zentrale&#039;&#039; Dekodierung des gerätespezifischen Protokoll-Datenworts, in zentralen Grundsatz-Modulen wie z.B. &amp;lt;code&amp;gt;14_SD_WS.pm&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Und wenn dann bei einer solchen Schritte-Abfolge irgendetwas noch fehlen/unpassend sein sollte, dann muss eben entsprechendes Development an gewissen Stellen erfolgen ;-)&lt;br /&gt;
&lt;br /&gt;
====Minimieren (whitelist/blacklist) von unerwünschter Kommunikations-Aktivität/Einträgen====&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Unknown Code&amp;quot; bedeutet, dass der SIGNALduino Signaldaten empfangen und diese binär interpretiert hat. Diese Meldung soll uns nun aber mitteilen, dass es dann nicht weiter verarbeitet werden kann, da kein Modul  existiert (oder kein Weiterleitungs-Dispatch zu einem bereits existierenden), welches diese Daten jetzt in ihre Bedeutung umwandeln kann. &lt;br /&gt;
:&amp;lt;code&amp;gt;sduino: Unknown code u1FFFFF0, help me!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Außerdem kommt es gehäuft zu Logmeldungen und auch Events in ähnlicher Form:&lt;br /&gt;
:&amp;lt;code&amp;gt;SIGNALduino_unknown incomming msg: u85#FF8081&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittlerweile sind über 50 Protokolle für den SIGNALduino definiert. Dadurch kommt es vor, dass sich ein Signal mit mehr als einem Protokoll demodulieren lässt. Meist führt dies dann zu zusätzlichen &amp;quot;Unknown code&amp;quot;-Einträgen.&lt;br /&gt;
&lt;br /&gt;
Derartige Einträge können mit dem Attribut WhitelistID minimiert werden. Dabei werden die Geräte, die nach Daten-Empfang tatsächlich verarbeitet werden sollen (also welche Protokolle vom FHEM Modul berücksichtigt werden), mit ihrer Protokollnummer in die WhitelistID aufgenommen.&lt;br /&gt;
Für Protokolle, die nicht berücksichtigt werden, gibt es weder Logeinträge noch Events. Diese werden im Programmablauf nicht berücksichtigt. Das spart zum einen Ressourcen und trägt auch zur Übersichtlichkeit bei. &lt;br /&gt;
Die Protokollnummer kann Tabelle [[#Unterst.C3.BCtzte_Ger.C3.A4te]] entnommen werden (hilfreich ist es auch, wenn in den verwendeten Geräten im Internal &amp;lt;gerätename&amp;gt;_DMSG nachgesehen wird). So bedeutet beispielsweise ein Eintrag der Form &amp;lt;code&amp;gt;W50#FF553335FFBC&amp;lt;/code&amp;gt; dass dann das Protokoll  #50 in die Whitelist aufzunehmen wäre (&amp;lt;code&amp;gt;attr sduino whitelist_IDs 50&amp;lt;/code&amp;gt;).&lt;br /&gt;
{{Randnotiz|RNTyp=r|RNText=Achtung Schreibweise: Dokumentation oft als WhitelistID o.ä., aber Name ist whitelist_IDs!!&lt;br /&gt;
}}&lt;br /&gt;
Die Angabe erfolgt durch Komma getrennt: z.B.:&lt;br /&gt;
:&amp;lt;code&amp;gt;1,2,5,10&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden mit dem SIGNALduino ===&lt;br /&gt;
Der SIGNALduino kann etwas &amp;quot;raw senden&amp;quot;, indem ihm das SIGNAL so übermittelt wird, wie er es moduliert. Hierzu  muss der Befehl wie folgt eingegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
set sduino sendMsg P3#00111010#R4&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Befehl moduliert die Bitfolge 00111010 mittels Protokoll #3 und wiederholt die Nachricht 4x.&lt;br /&gt;
Die Protokoll Nummer kann aus einer empfangenen Nachricht extrahiert werden. Ebenso die Bits.&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
sduino: extracted data 00111010 (bin)&lt;br /&gt;
sduino: Found Protocol id 3 &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann das Signal auch in einer &amp;quot;rohform&amp;quot; angegeben werden. Dies ist manchmal in speziellen Fällen notwendig:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
set sduino raw SR;;R=3;;P0=4742;;P1=-1554;;P2=286;;P3=-786;;P4=649;;P5=-420;;D=0123234545234545452323232323454523234523454523232345454523232323452345234523452345;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
R=3 bedeutet, das Signal wird 3x gesendet.&lt;br /&gt;
Die Übertragung besteht aus den in D angegeben Pulsen, welche in P0-P5 definiert werden.&lt;br /&gt;
Die Daten kann man aus einer empfangenen MS oder MU Nachricht extrahieren.&lt;br /&gt;
&lt;br /&gt;
Alternativ kann ab Version 3.2 auch eine vereinfachte Form eingegeben werden.&lt;br /&gt;
&lt;br /&gt;
====Fehlersuche====&lt;br /&gt;
&lt;br /&gt;
(Zielgerät reagiert nicht, etc.)&lt;br /&gt;
&lt;br /&gt;
* Nachrichtenwiederholungsanzahl muss evt. für manche Geräte entsprechend groß eingestellt sein&lt;br /&gt;
* Sende-Takt-Wert (Clock) passt evt. nicht ganz, siehe z.B. Thread-Antwort [https://forum.fhem.de/index.php/topic,58397.msg775434.html#msg775434 Signalduino Version 3.3.1], wo für IT-Geräte ein Attribut anhand der CP= des Empfangsdaten-Logs modifiziert wird. ACHTUNG: dies kann entweder global das Internal-Attribut ITClock eines SIGNALduino-Transceiver-Devices sein, oder (viel besser da korrekt geräte-instanz-spezifische Konfiguration) das ITclock eines IT-Client-Devices.&lt;br /&gt;
{{Randnotiz|RNTyp=r|RNText=VORSICHT blöder Schreibweisen-Mismatch ITClock vs. ITclock!!&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Fehlerbehandlung ==&lt;br /&gt;
Der SIGNALduino kann mit folgendem Befehl auf Werkseinstellungen zurückgesetzt werden:&lt;br /&gt;
:&amp;lt;code&amp;gt;get raw e&amp;lt;/code&amp;gt;&lt;br /&gt;
als Antwort kommt dann &amp;quot;ccFactoryReset done&amp;quot;. Ob ein solcher Reset nötig ist, erkennt man an der Antwort auf den Befehl &amp;quot;get config&amp;quot;, auf den dann die Meldung &amp;quot;config: MS=1;MU=1;MC=1&amp;quot; folgen sollte.&lt;br /&gt;
&lt;br /&gt;
In der Firmware sind die folgenden Befehle eingebaut&lt;br /&gt;
&lt;br /&gt;
*:&amp;lt;code&amp;gt;get raw C&amp;lt;reg&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;reg&amp;gt; is a (two digit) hex number: return the value of the cc1101 register. &amp;lt;reg&amp;gt;=99 dumps the first 48 registers.&lt;br /&gt;
Example: C35 -&amp;gt; C35 = 0D&lt;br /&gt;
*:&amp;lt;code&amp;gt;get raw e&amp;lt;/code&amp;gt;&lt;br /&gt;
EEPROM / factory reset.  resets all eeprom values without reboot&lt;br /&gt;
*:&amp;lt;code&amp;gt;get raw r&amp;lt;AA&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
Read eeprom  (da das &amp;quot;R&amp;quot; beim SIGNALDuino bereits mit freeram belegt ist, habe ich das &amp;quot;r&amp;quot; verwendet)&lt;br /&gt;
*:&amp;lt;code&amp;gt;get raw r&amp;lt;AA&amp;gt;n&amp;lt;/code&amp;gt;&lt;br /&gt;
Read 16 byte from eeprom (z.B. r00n)&lt;br /&gt;
*:&amp;lt;code&amp;gt;get raw W&amp;lt;AA&amp;gt;&amp;lt;DD&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
Write eeprom (schreibt einen Wert ins EEPROM und ins CC1101 Register. Die eeprom Adresse hat einen Offset von 2. z.B W041D schreibt 1D ins Register 2)&lt;br /&gt;
&lt;br /&gt;
Die Sendeleistung lässt sich mit &lt;br /&gt;
:&amp;lt;code&amp;gt;get sduino ccreg 3E&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
prüfen, wobei die Rückmeldung wie folgt zu lesen ist: &lt;br /&gt;
  &amp;quot;-10_dBm&amp;quot;  =&amp;gt; &#039;34&#039;,&lt;br /&gt;
  &amp;quot;-5_dBm&amp;quot;   =&amp;gt; &#039;68&#039;,&lt;br /&gt;
  &amp;quot;0_dBm&amp;quot;    =&amp;gt; &#039;60&#039;,&lt;br /&gt;
  &amp;quot;5_dBm&amp;quot;    =&amp;gt; &#039;84&#039;,&lt;br /&gt;
  &amp;quot;7_dBm&amp;quot;    =&amp;gt; &#039;C8&#039;,&lt;br /&gt;
  &amp;quot;10_dBm&amp;quot;   =&amp;gt; &#039;C0&#039; &lt;br /&gt;
Dabei wird die Sendeleistung dauerhaft mit dem Befehl&lt;br /&gt;
:&amp;lt;code&amp;gt;set sduino cc1101_patable &amp;lt;value&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
hochgeschaltet (&amp;lt;value&amp;gt; durch den Wert ersetzen).&lt;br /&gt;
&lt;br /&gt;
Weitere Firmware-Befehle sind im Thread-Beitrag {{Link2Forum|Topic=58396|Message=497921}} zu finden.&lt;br /&gt;
&lt;br /&gt;
== Foren Links ==&lt;br /&gt;
* {{Link2Forum|Topic=38402|LinkText=Forenthread - Ankündigung}}&lt;br /&gt;
* {{Link2Forum|Topic=58396|LinkText=SIGNALDuino Empfänger Firm- und Hardware}}&lt;br /&gt;
* {{Link2Forum|Topic=58397|LinkText=Signalduino Entwicklung Version 3.3.1 }}&lt;br /&gt;
* [http://www.rflink.nl/blog2/wiring Beschreibung zu diversen Empfängern und Verbesserung der Empfangsleistung]&lt;br /&gt;
* [[SIGNALduino in die Arduino Entwicklungsumgebung einbinden]]&lt;br /&gt;
* [[Somfy via SIGNALduino]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Interfaces]]&lt;br /&gt;
[[Kategorie:Arduino]]&lt;br /&gt;
[[Kategorie:433MHz]]&lt;br /&gt;
[[Kategorie:868MHz]]&lt;/div&gt;</summary>
		<author><name>Dora71</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=STV&amp;diff=29216</id>
		<title>STV</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=STV&amp;diff=29216"/>
		<updated>2019-01-25T06:42:24Z</updated>

		<summary type="html">&lt;p&gt;Dora71: Link zur Dokumentation im Forum eingefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Client for Samsung TV&lt;br /&gt;
|ModType=x&lt;br /&gt;
|ModFTopic=82890&lt;br /&gt;
|ModForumArea=Multimedia&lt;br /&gt;
|ModTechName=70_STV.pm&lt;br /&gt;
|ModOwner=Zwiebel ({{Link2FU|103|Forum}}/[[Benutzer Diskussion:Plin53177|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Das Modul &#039;&#039;&#039;STV&#039;&#039;&#039; ermöglicht die Steuerung von Samsung Fernsehern und Bluray-Playern.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
&lt;br /&gt;
=== Historie===&lt;br /&gt;
&lt;br /&gt;
Im Ursprung wurde das Modul 70_STV.pm entwickelt, welches bis F-Serie funktionieren sollte. Das Modul ist im Standardumfang von FHEM enthalten.&lt;br /&gt;
Für die neue Zugriffsmethode per websocket ab K-Serie wurde das Modul so erweitert, dass per Python-Skript samsungctl die TV&#039;s problemlos ansteuerbar sind. Die größten Probleme scheinen die H- u. J-Serie zu bereiten. Diese nutzen neben websocket zusätzlich eine Verschlüsselung. Für den Zugriff wurde ein Python-Skript nach Perl übersetzt, das wiederum in die nicht offizielle STV eingebunden wurde. Schließlich wurde noch die Möglichkeit der Ausgabe von Medien-Streams für alle Serien über DLNA eingebaut. Die aktuelle inoffizielle Version, in der ALLE features enthalten sind, findet Ihr [https://forum.fhem.de/index.php/topic,57595.msg770704.html#msg770704 &amp;gt;&amp;gt;&amp;gt;hier&amp;lt;&amp;lt;&amp;lt;] neben der zusammengefassten Doku [https://forum.fhem.de/index.php/topic,82890.msg756500.html#msg756500 &amp;gt;&amp;gt;&amp;gt;hier&amp;lt;&amp;lt;&amp;lt;]. &lt;br /&gt;
&lt;br /&gt;
Neben den unterschiedlichsten Servern in der Firmware der TV&#039;s gibt es auch Unterschiede bei der Berechtigungssteuerung. Nachfolgend findet Ihr relevante Informationen zu den unterschiedlichen Serien.&lt;br /&gt;
&lt;br /&gt;
===Steuerungsmöglichkeiten (allgemein)===&lt;br /&gt;
&lt;br /&gt;
Steuerungsmöglichkeiten(Voraussetzung also eine LAN- oder WLAN-Verbindung u. berechtigter Zugriff) aus FHEM heraus sind&lt;br /&gt;
* senden von remote control Befehlen an den  TV&lt;br /&gt;
* Bildschirmnachrichten wie z.B. InfoScreen bei eingehendem Anruf&lt;br /&gt;
* Abspielen von Video, Audio, Foto Konserven&lt;br /&gt;
&lt;br /&gt;
===B/C/D-Series(vor 2012) ===&lt;br /&gt;
&lt;br /&gt;
Da diese TV&#039;s schon relativ alt sind hier nur eine Zusammenfassung von Funktionen, die sich je nach Modell unterscheiden:&lt;br /&gt;
* Älteste Modelle(B-Serie ?):  mute, volume, call, sms, date über port 52235  UPNP/SOAP&lt;br /&gt;
* Jüngere Modelle(C-/D-Serie): sämtliche remote control commands port über 55000, keine Bildschirmnachrichten ?, Port 7676 u. service urn:samsung.com:service:MainTVAgent2:1 nicht verfügbar ?&lt;br /&gt;
&lt;br /&gt;
===E-Series(2012)===&lt;br /&gt;
sämtliche remote control commands über port 55000; Definition mit FHEM-Standard-Modul STV und Port &#039;&#039;&#039;55000&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Zugriffsberechtigung:&#039;&#039;&#039;&lt;br /&gt;
Einstellungen findet man unter:&lt;br /&gt;
Menü-&amp;gt;Netzwerk-&amp;gt;AllShare-Einstellungen-&amp;gt;Liste der registrierten Endgeräte-&amp;gt;Zulassen|Verbieten|Aus der Liste löschen&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Registrierung:&#039;&#039;&#039;&lt;br /&gt;
Bei erstmaligem Zugriff eines Endgeräts erscheint auf dem TV ein pop-up: Neues Netzwerkgerät erkannt......: Zulassen|Verbieten&lt;br /&gt;
&lt;br /&gt;
Eine Pin-Eingabe ist nicht erforderlich. Eine Einmalige Registrierung berechtigt für den Dauerbetrieb des Endgeräts, wobei (bei mir)&lt;br /&gt;
das Gerät &amp;quot;Perl Samsung Remote&amp;quot; mit der IP 127.0.0.1 angelegt wurde und mit dieser Zugangsberechtigung nun ALLE devices über&lt;br /&gt;
FHEM für die Steuerung zugelassen sind.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bildschirmnachrichten:&#039;&#039;&#039;&lt;br /&gt;
über port 7676  ist UPNP/SOAP (z.B. Browseraufruf mit InfoDisplay f. Anruferinfo) verfügbar. Ebenso ist die Medienausgabe per DLNA möglich&lt;br /&gt;
nur mit der inoffiziellen Version !)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bestätigte Modelle:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
ganze Serie&lt;br /&gt;
&lt;br /&gt;
===F-Series(2013) ===&lt;br /&gt;
Vermutlich kein Unterschied zur E-Serie.&lt;br /&gt;
&lt;br /&gt;
===H-Series(2014) ===&lt;br /&gt;
&lt;br /&gt;
Mit dieser Serie führte Samsung die verschlüsselte Kommunikation über websockets und Port 8000 ein. Für die Zugriffsberechtigung muss einmalig das Perl-Skript&lt;br /&gt;
regapp.pl aus diesem [https://forum.fhem.de/index.php/topic,57595.msg748445.html#msg748445 Post] (https://forum.fhem.de/index.php/topic,57595.msg748445.html#msg748445) zur Erzeugung des session-key ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notwendige Vorbereitung:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 1. Erstellen der Datei samsung_session_key.txt mit dem Perl-Skript regappl.pl (Pin-Eingabe über FB des TV erforderlich)&lt;br /&gt;
 2. Bei ERFOLG(lieber mal reingucken  ;)) die Datei in das FHEM-Verzeichnis kopieren, wo auch die fhem.cfg liegt&lt;br /&gt;
 3. Manchen wird noch das Perl-Modul Crypt::Rijndael fehlen. Das installiert man(für Debian):&lt;br /&gt;
    a) sudo apt-get update&lt;br /&gt;
    b) sudo apt-get upgrade&lt;br /&gt;
    c) sudo apt-get install libcrypt-rijndael-perl&lt;br /&gt;
    d) ein reboot schadet an dieser Stelle nie&lt;br /&gt;
 4. define MeinTV STV MEINE_IP wse  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Zugriffsberechtigung:&#039;&#039;&#039;&lt;br /&gt;
Menü/Netzwerk/Multimedia-Geräteeinstellungen. Dort kann man &amp;quot;SmartDevices&amp;quot; erlauben, verbieten und löschen. &lt;br /&gt;
- einmal verboten und der Key ist unbrauchbar. Also neu pairen. &lt;br /&gt;
- die IP scheint egal zu sein. Mit den passenden session_key und session_id kann man von jedem Rechner aus zugreifen. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Registrierung:&#039;&#039;&#039;&lt;br /&gt;
Mittlerweile ist das Perl-Skript do_v2.pl von Raymund auch in das inoffizielle70_STV eingebunden. Das Skript regapp.pl muss nach wie vor einmalig(?) für die Schlüsselerzeugung ausgeführt werden. Bitte Vorgehensweise im vorgenannten Post beachten !&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bildschirmnachrichten:&#039;&#039;&#039;&lt;br /&gt;
Bildschirmnachrichten bzw. generelle Medienausgabe über DLNA möglich&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bestätigte Modelle:&#039;&#039;&#039;&lt;br /&gt;
UE-55H6740SV, UExyHU6900, UE40H6400, UE55H6700&lt;br /&gt;
&lt;br /&gt;
Info des Python-Skript-Autors:&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039; that the following TV Models are most likely incompatible for one reason or another H4xxx, H510x, H52xx, H53x3, H5403, H6003, H61x3, H6201, H6203, S9, S9C&lt;br /&gt;
&lt;br /&gt;
===J-Series(2015) ===&lt;br /&gt;
Die verschlüsselte Kommunikation erfolgt über websockets und Port 8000. Für die Zugriffsberechtigung muss einmalig das Perl-Skript&lt;br /&gt;
regapp.pl aus diesem [https://forum.fhem.de/index.php/topic,57595.msg748445.html#msg748445 Post] (https://forum.fhem.de/index.php/topic,57595.msg748445.html#msg748445) zur Erzeugung des session-key ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notwendige Vorbereitung:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1. Erstellen der Datei samsung_session_key.txt mit dem Perl-Skript regappl.pl, und zwar auf dem Rechner, der später den Fernseher steuern soll. &lt;br /&gt;
In dieser Serie erzeugt der Fernseher die Pin, diese wird von regappl.pl abgefragt&lt;br /&gt;
2. Bei Erfolg die Datei in das FHEM-Verzeichnis kopieren (dort wo auch die fhem.cfg liegt). Umbenennen,so dass es den unter Punkt 4 vewendeten Devicenamen (beispielsweise MeinTV) im eigenen Namen hat. Zugriffsrechte setzen mit &lt;br /&gt;
  chown fhem:dialout samsung_session_key.txt&lt;br /&gt;
  mv samsung_session_key.txt MeinTV_session_key.txt&lt;br /&gt;
3. Ggf. das Perl-Modul Crypt::Rijndael nachinstallieren mit &lt;br /&gt;
  sudo apt-get install libcrypt-rijndael-perl&lt;br /&gt;
4. Fernseher in FHEM definieren als&lt;br /&gt;
  define MeinTV STV &amp;lt;ip-adresse des Fernsehers&amp;gt; 8000  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Zugriffsberechtigung:&#039;&#039;&#039;&lt;br /&gt;
Auf dem Fernseher unter Menü/Netzwerk/Multimedia-Geräteeinstellungen. Dort kann man &amp;quot;SmartDevices&amp;quot; erlauben, verbieten und löschen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bildschirmnachrichten:&#039;&#039;&#039;&lt;br /&gt;
Bildschirmnachrichten bzw. generelle Medienausgabe über DLNA möglich&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bestätigte Modelle:&#039;&#039;&#039; &lt;br /&gt;
UE55UJ7000, UE48JU7590(THKMDEUC-1452)&lt;br /&gt;
&lt;br /&gt;
Info des Python-Skript-Autors:&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039; the the following TV Models are most likely incompatible for one reason or another&lt;br /&gt;
J4xxx, J50xx, J51xx, J52xx, J53xx, UNxxJ6200, J6201, J6203, J620D&lt;br /&gt;
&lt;br /&gt;
Tizen 2.3(nicht aber UJ4300, UJ5300)&lt;br /&gt;
&lt;br /&gt;
Spätere Modelle wie K-Serie ?&lt;br /&gt;
&lt;br /&gt;
===K-Series(2016)===&lt;br /&gt;
&lt;br /&gt;
Der Port ist durch Samsung auf 8001 geändert. Eine Verschlüsselung gibt es nicht mehr. Dominik hat den Aufruf des im Netz verfügbaren Python-Skripts samsungctl in das STV-Modul integriert. Die Installation des Python-Skripts samsungctl ist zusätzlich erforderlich.&lt;br /&gt;
&lt;br /&gt;
Für die Definition des TV in FHEM ist anstatt des tatsächlichen Ports &amp;quot;ws&amp;quot; anzugeben.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notwendige Vorbereitung:&#039;&#039;&#039;&lt;br /&gt;
 1. sudo apt-get update&lt;br /&gt;
 2. sudo apt-get upgrade&lt;br /&gt;
 3. pip installieren. wheezy bietet nur eine V. 1.1, von der im Inet abgeraten wird. Deshalb habe ich die v 9.0.1 mit&lt;br /&gt;
    wget https://bootstrap.pypa.io/get-pip.py geladen.&lt;br /&gt;
 4. sudo python get-pip.py    (ohne sudo gab es eine exception meldung)&lt;br /&gt;
 5. sudo pip install websocket-client&lt;br /&gt;
 6. sudo pip install samsungctl  (das package wird dann automatisch heruntergeladen und installiert)&lt;br /&gt;
 7. das File /usr/local/lib/python2.7/dist-packages/samsungctl/__main__.py editiert&lt;br /&gt;
    o except FileNotFoundError: in except: ändern&lt;br /&gt;
    o Folgende Zeile löschen directories.append(os.path.join(os.getenv(&amp;quot;HOME&amp;quot;), &amp;quot;.config&amp;quot;))&lt;br /&gt;
 8. Damit das geänderte 70_STV lief, musste ich einerseits &amp;quot;use Blocking;&amp;quot; am Anfang des Moduls hinzufügen und scheinbar ist der /usr/local/bin bei mir nicht bekannt, so  dass ich den einfachen Aufruf von samsungctl in /usr/local/bin/samsungctl ändern musste.&lt;br /&gt;
&lt;br /&gt;
Das Senden der üblichen remote control commands funktioniert. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Definition in FHEM:&#039;&#039;&#039; &lt;br /&gt;
anstatt einem numerischen Port ist der String ws einzugeben&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Zugriffsberechtigung:&#039;&#039;&#039;&lt;br /&gt;
* Popup mit Bestätigung&lt;br /&gt;
* dann unter Einstellungen -&amp;gt; Allgemein -&amp;gt; Geräteverbindungsmanager findet man eine Geräteliste (da kann man die einzelnen zugriffsberechtigten Geräte auch wieder rausschmeissen) und man kann sogar die Zugriffsbenachrichtigung verändern (nur einmal od. jedes Mal).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bildschirmnachrichten:&#039;&#039;&#039;&lt;br /&gt;
Bildschirmnachrichten bzw. generelle Medienausgabe über DLNA möglich &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bestätigte Modelle:&#039;&#039;&#039;&lt;br /&gt;
ganze Serie ?: UE40K5579, UE55KU6170, UE65ks7090 &lt;br /&gt;
&lt;br /&gt;
Tizen 2.4 (nicht aber: UBD-K8500, UBD-KM85C, UBD-KM85)&lt;br /&gt;
&lt;br /&gt;
===M-Series(2017)===&lt;br /&gt;
&lt;br /&gt;
Vermutlich kein Unterschied zur K-Serie.   :-\&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bestätigte Modelle:&#039;&#039;&#039;&lt;br /&gt;
ganze Serie ?&lt;br /&gt;
&lt;br /&gt;
Tizen 3.0(UMU6100 Tizen 2.4)&lt;br /&gt;
&lt;br /&gt;
===Q-Series(2018)===&lt;br /&gt;
&lt;br /&gt;
Mal sehen, was uns da wieder erwartet. Vorstellung wohl im Februar 2018.&lt;br /&gt;
&lt;br /&gt;
Tizen 4.0&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
=== Erste Schritte ===&lt;br /&gt;
&lt;br /&gt;
(für Version aus dem FHEM Repository) &lt;br /&gt;
&lt;br /&gt;
Device in FHEM anlegen:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;define &amp;lt;name&amp;gt; STV &amp;lt;ip&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach sollte das Device bei eingeschaltetem Fernseher oder Bluray-Player in den Status &#039;&#039;&#039;opened&#039;&#039;&#039; gehen.&lt;br /&gt;
&lt;br /&gt;
Ist dies nicht der Fall ist die Modellreihe zu ermitteln. Am Command-Prompt kann mittels nmap ermittelt werden welche Ports geöffnet sind (xxx.xxx.xxx.xxx ist in diesem Beispiel die IP-Adresse des Samsung-Gerätes)&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;nmap xxx.xxx.xxx.xxx&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;Starting Nmap 6.47 ( http://nmap.org ) at 2018-02-18 09:22 CET&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;Nmap scan report for xxx.xxx.xxx.xxx&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;Host is up (0.0095s latency).&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;Not shown: 997 closed ports&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;PORT     STATE SERVICE&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;7676/tcp open  imqbrokerd&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;8080/tcp open  http-proxy&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;8443/tcp open  https-alt&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
anschließend die High Ports scannen&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;code&amp;gt;nmap -sT -p50000-65534 xxx.xxx.xxx.xxx&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;Starting Nmap 6.47 ( http://nmap.org ) at 2018-02-18 09:25 CET&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;Nmap scan report for xxx.xxx.xxx.xxx&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;Host is up (0.015s latency).&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;Not shown: 15532 closed ports&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;PORT      STATE SERVICE&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;52345/tcp open  unknown&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;55000/tcp open  unknown&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;55001/tcp open  unknown&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt gilt es folgende Fälle zu unterscheiden:&lt;br /&gt;
* Port 52235 wird ausgewiesen &lt;br /&gt;
: define &amp;lt;device&amp;gt; STV &amp;lt;ip&amp;gt;&lt;br /&gt;
: ohne Portangabe&lt;br /&gt;
&lt;br /&gt;
Jetzt gilt es folgende Fälle zu unterscheiden:&lt;br /&gt;
* Port 55000 wird ausgewiesen &lt;br /&gt;
: define &amp;lt;device&amp;gt; STV &amp;lt;ip&amp;gt;&lt;br /&gt;
: ohne Portangabe&lt;br /&gt;
&lt;br /&gt;
Bei dem aktuellen Modul aus dem FHEM-Repository kann kein Port mitgegeben werden. Die Definition kann dann nachträglich im angelisteten Device durch Bearbeitung der DEF erfolgen. Die IP-Adresse um den Port ergänzen &lt;br /&gt;
 xxx.xxx.xxx.xxx 55000&lt;br /&gt;
und speichern.&lt;br /&gt;
&lt;br /&gt;
===Alternative: Aktuelle Version aus dem Forum===&lt;br /&gt;
&lt;br /&gt;
Die im Anhang zu [https://forum.fhem.de/index.php/topic,57595.msg770704.html#msg770704 Post #298] vom 21.2.2018 publizierte Fassung ist in der Entwicklung, bietet aber weitergehende Funktionen.&lt;br /&gt;
&lt;br /&gt;
Diese dort gepostete Version des 70_STV sollte für ALLE Serien die Ausgabe von Bildschirmnachrichten per DLNA ermöglichen. Bitte lest den Post, um auf dem laufenden zu bleiben. Wenn die Version fertig ist wird sie hier dokumentiert.&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Basics &amp;amp; Allgemeines&#039;&#039; &lt;br /&gt;
* &#039;&#039;&#039;MAC&#039;&#039;&#039;&lt;br /&gt;
: nur bei Bedarf: die IP-Adresse des Rechners auf dem FHEM läuft. Sollte das zu steuernde Gerät nicht reagieren und im FHEM-Log &amp;quot;[STV] mymac&amp;quot; mit &amp;quot;invalid format&amp;quot; auftauchen, dann bitte dieses Attribut setzen.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;fork&#039;&#039;&#039;&lt;br /&gt;
: enable|disable&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;setWhenOffline&#039;&#039;&#039;&lt;br /&gt;
: execute|ignore Ist der Fehnseher ausgeschaltet hängt ein Set-Befehl, bis das Modul realisiert, dass der Fernseher ausgeschaltet ist. Mit &#039;&#039;setWhenOffline ignore&#039;&#039; lässt sich dieses Verhalten verhindern. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;nur in aktueller Entwicklerversion&#039;&#039; &lt;br /&gt;
* &#039;&#039;&#039;callerURI &#039;&#039;&#039;&lt;br /&gt;
: callerURI ist für die Nutzung von Anruferinfo gedacht.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;screenURI&#039;&#039;&#039;&lt;br /&gt;
: Über screenURI und einen Dummy kann z.B. ein InfoDisplay von FHEM auf dem TV ausgeben. In die URIs wird der jeweilige Pfad zum Bild eingetragen(also so, wie man es bereits beim Test des DLNAClient gemacht hatte).&lt;br /&gt;
&lt;br /&gt;
Durch die Definition der jeweiligen URI, wurde dynamisch das Befehlsset des TV-devices erweitert. Für den Aufruf der callerURI steht der neue set-Befehl caller und für screenURI entsprechend screen zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
=== Set ===&lt;br /&gt;
&lt;br /&gt;
Das Modul kennt derzeit folgende Commands&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Basis-Commandset&#039;&#039; (bei Nutzung von Port 52235)&lt;br /&gt;
&lt;br /&gt;
* set &amp;lt;name&amp;gt; &#039;&#039;&#039;mute&#039;&#039;&#039; {on|off]&lt;br /&gt;
: schaltet den Ton ein/aus&lt;br /&gt;
&lt;br /&gt;
* set &amp;lt;name&amp;gt; &#039;&#039;&#039;volume&#039;&#039;&#039; &amp;lt;nummer&amp;gt;&lt;br /&gt;
: Lautstärke ändern. &amp;lt;nummer&amp;gt; kann zwischen 0 und 100 liegen&lt;br /&gt;
&lt;br /&gt;
* set &amp;lt;name&amp;gt; &#039;&#039;&#039;call&#039;&#039;&#039; &amp;lt;von_name&amp;gt; &amp;lt;von_number&amp;gt; &amp;lt;an_name&amp;gt; &amp;lt;an_number&amp;gt;&lt;br /&gt;
: &amp;lt;von_name&amp;gt; = Name des Anrufers&lt;br /&gt;
: &amp;lt;von_number&amp;gt; = Nummer des Anrufers&lt;br /&gt;
: &amp;lt;an_name&amp;gt; = Name des Anzurufenden&lt;br /&gt;
: &amp;lt;an_number&amp;gt; = Nummer des Anzurufenden&lt;br /&gt;
&lt;br /&gt;
* set &amp;lt;name&amp;gt; &#039;&#039;&#039;sms&#039;&#039;&#039; &amp;lt;von_name&amp;gt; &amp;lt;von_number&amp;gt; &amp;lt;an_name&amp;gt; &amp;lt;an_number&amp;gt; &amp;lt;text&amp;gt;&lt;br /&gt;
: &amp;lt;von_name&amp;gt; = Name des Anrufers&lt;br /&gt;
: &amp;lt;von_number&amp;gt; = Nummer des Anrufers&lt;br /&gt;
: &amp;lt;an_name&amp;gt; = Name des Anzurufenden&lt;br /&gt;
: &amp;lt;an_number&amp;gt; = Nummer des Anzurufenden&lt;br /&gt;
: &amp;lt;text&amp;gt; = SMS Nachrichtentext&lt;br /&gt;
&lt;br /&gt;
* set &amp;lt;name&amp;gt; &#039;&#039;&#039;date&#039;&#039;&#039; &amp;lt;start_datum&amp;gt; &amp;lt;start_zeit&amp;gt; &amp;lt;an_name&amp;gt; &amp;lt;an_number&amp;gt; &amp;lt;betreff&amp;gt; &amp;lt;ende_datum&amp;gt; &amp;lt;ende_zeit&amp;gt; &amp;lt;ort&amp;gt; &amp;lt;nachrichtentext&amp;gt;&lt;br /&gt;
: Termineinladung mit&lt;br /&gt;
: &amp;lt;start_datum&amp;gt; = Startdatum&lt;br /&gt;
: &amp;lt;start_zeit&amp;gt; = Startzeit&lt;br /&gt;
: &amp;lt;an_name&amp;gt; = Name des Einzuladenden&lt;br /&gt;
: &amp;lt;an_number&amp;gt; = Nummer des Einzuladenden&lt;br /&gt;
: &amp;lt;betreff&amp;gt; = Betreff der Einladung&lt;br /&gt;
: &amp;lt;ende_datum&amp;gt; = Enddatum&lt;br /&gt;
: &amp;lt;ende_zeit&amp;gt; = Endzeit&lt;br /&gt;
: &amp;lt;ort&amp;gt; = Treffpunkt&lt;br /&gt;
: &amp;lt;nachrichtentext&amp;gt; = Text der Einladung&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Erweitertes Commandset&#039;&#039; (bei Nutzung von Port 55000)&lt;br /&gt;
&lt;br /&gt;
* set &amp;lt;name&amp;gt; &#039;&#039;&#039;connect&#039;&#039;&#039;&lt;br /&gt;
: Mit dem Fernseher/Player verbinden&lt;br /&gt;
&lt;br /&gt;
* set &amp;lt;name&amp;gt; &#039;&#039;&#039;button&#039;&#039;&#039;&lt;br /&gt;
: button ist ein Befehl der Fernbedienung aus dieser Liste: &lt;br /&gt;
: [ 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 3SPEED | 4_3 | 16_9 | AD | ADDDEL | ALT_MHP | ANGLE | ANTENA | ANYNET | ANYVIEW | APP_LIST | ASPECT | AUTO_ARC_ANTENNA_AIR | AUTO_ARC_ANTENNA_CABLE | AUTO_ARC_ANTENNA_SATELLITE | AUTO_ARC_ANYNET_AUTO_START | AUTO_ARC_ANYNET_MODE_OK | AUTO_ARC_AUTOCOLOR_FAIL | AUTO_ARC_AUTOCOLOR_SUCCESS | AUTO_ARC_CAPTION_ENG | AUTO_ARC_CAPTION_KOR | AUTO_ARC_CAPTION_OFF | AUTO_ARC_CAPTION_ON | AUTO_ARC_C_FORCE_AGING | AUTO_ARC_JACK_IDENT | AUTO_ARC_LNA_OFF | AUTO_ARC_LNA_ON | AUTO_ARC_PIP_CH_CHANGE | AUTO_ARC_PIP_DOUBLE | AUTO_ARC_PIP_LARGE | AUTO_ARC_PIP_LEFT_BOTTOM | AUTO_ARC_PIP_LEFT_TOP | AUTO_ARC_PIP_RIGHT_BOTTOM | AUTO_ARC_PIP_RIGHT_TOP | AUTO_ARC_PIP_SMALL | AUTO_ARC_PIP_SOURCE_CHANGE | AUTO_ARC_PIP_WIDE | AUTO_ARC_RESET | AUTO_ARC_USBJACK_INSPECT | AUTO_FORMAT | AUTO_PROGRAM | AV1 | AV2 | AV3 | BACK_MHP | BOOKMARK | CALLER_ID | CAPTION | CATV_MODE | CHDOWN | CHUP | CH_LIST | CLEAR | CLOCK_DISPLAY | COMPONENT1 | COMPONENT2 | CONTENTS | CONVERGENCE | CONVERT_AUDIO_MAINSUB | CUSTOM | CYAN | DEVICE_CONNECT | DISC_MENU | DMA | DNET | DNIe | DNSe | DOOR | DOWN | DSS_MODE | DTV | DTV_LINK | DTV_SIGNAL | DVD_MODE | DVI | DVR | DVR_MENU | DYNAMIC | ENTER | ENTERTAINMENT | ESAVING | EXIT | EXT1 | EXT2 | EXT3 | EXT4 | EXT5 | EXT6 | EXT7 | EXT8 | EXT9 | EXT10 | EXT11 | EXT12 | EXT13 | EXT14 | EXT15 | EXT16 | EXT17 | EXT18 | EXT19 | EXT20 | EXT21 | EXT22 | EXT23 | EXT24 | EXT25 | EXT26 | EXT27 | EXT28 | EXT29 | EXT30 | EXT31 | EXT32 | EXT33 | EXT34 | EXT35 | EXT36 | EXT37 | EXT38 | EXT39 | EXT40 | EXT41 | FACTORY | FAVCH | FF | FF_ | FM_RADIO | GAME | GREEN | GUIDE | HDMI | HDMI1 | HDMI2 | HDMI3 | HDMI4 | HELP | HOME | ID_INPUT | ID_SETUP | INFO | INSTANT_REPLAY | LEFT | LINK | LIVE | MAGIC_BRIGHT | MAGIC_CHANNEL | MDC | MENU | MIC | MORE | MOVIE1 | MS | MTS | MUTE | NINE_SEPERATE | OPEN | PANNEL_CHDOWN | PANNEL_CHUP | PANNEL_ENTER | PANNEL_MENU | PANNEL_POWER | PANNEL_SOURCE | PANNEL_VOLDOW | PANNEL_VOLUP | PANORAMA | PAUSE | PCMODE | PERPECT_FOCUS | PICTURE_SIZE | PIP_CHDOWN | PIP_CHUP | PIP_ONOFF | PIP_SCAN | PIP_SIZE | PIP_SWAP | PLAY | PLUS100 | PMODE | POWER | POWEROFF | POWERON | PRECH | PRINT | PROGRAM | QUICK_REPLAY | REC | RED | REPEAT | RESERVED1 | RETURN | REWIND | REWIND_ | RIGHT | RSS | RSURF | SCALE | SEFFECT | SETUP_CLOCK_TIMER | SLEEP | SOUND_MODE | SOURCE | SRS | STANDARD | STB_MODE | STILL_PICTURE | STOP | SUB_TITLE | SVIDEO1 | SVIDEO2 | SVIDEO3 | TOOLS | TOPMENU | TTX_MIX | TTX_SUBFACE | TURBO | TV | TV_MODE | UP | VCHIP | VCR_MODE | VOLDOWN | VOLUP | WHEEL_LEFT | WHEEL_RIGHT | W_LINK | YELLOW | ZOOM1 | ZOOM2 | ZOOM_IN | ZOOM_MOVE | ZOOM_OUT ]&lt;br /&gt;
&lt;br /&gt;
=== Get ===&lt;br /&gt;
&lt;br /&gt;
Derzeit keine Funktionen realisiert.&lt;br /&gt;
&lt;br /&gt;
=== Readings ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;state&#039;&#039;&#039;: Status der Verbindung&lt;br /&gt;
&lt;br /&gt;
== Anwendungsbeispiele ==&lt;br /&gt;
&lt;br /&gt;
folgen noch&lt;br /&gt;
&lt;br /&gt;
== Bekannte Probleme / Fehlersuche ==&lt;br /&gt;
&lt;br /&gt;
Im Foum gibt es zwei Threads&lt;br /&gt;
* [https://forum.fhem.de/index.php/topic,12988.msg79079.html Original Thread]&lt;br /&gt;
* [https://forum.fhem.de/index.php/topic,57595.msg490160.html#msg490160 für Fernseher ab J-Serie]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* https://forum.fhem.de/index.php/topic,82890.0.html  Zusammenfassende Doku: Samsung TV und FHEM&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Unterhaltungselektronik]] &lt;br /&gt;
&amp;lt;!-- (Modulkategorie wird automatisch gesetzt) --&amp;gt;&lt;/div&gt;</summary>
		<author><name>Dora71</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=SIGNALduino&amp;diff=28543</id>
		<title>SIGNALduino</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=SIGNALduino&amp;diff=28543"/>
		<updated>2018-11-26T17:07:20Z</updated>

		<summary type="html">&lt;p&gt;Dora71: /* Unterstützte Geräte */ Conrad KW9110 hinzugefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Empfang und Verarbeitung von digitalen Signalen&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModFTopic=38402&lt;br /&gt;
|ModCmdRef=SIGNALduino&lt;br /&gt;
|ModForumArea=Sonstige Systeme&lt;br /&gt;
|ModTechName=00_SIGNALduino.pm&lt;br /&gt;
|ModOwner=Sidey ({{Link2FU|8018|Forum}}/[[Benutzer Diskussion:Sidey|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
=== Übersicht ===&lt;br /&gt;
Unter den Namen SIGNALduino versteht man sowohl den Low-Cost Empfänger für digitale Signale (vergleichbar dem  [[CUL]]) als auch das gleichnamige Modul mit dem Dateinamen 00_SIGNALduino.pm. Mit dem SIGNALduino kann man entweder 433 oder 868 MHz-Geräte auslesen und ansprechen. Der SIGNALduino funktioniert auch mit anderen Medien wie Infrarot oder direkter Kabelverbindung.&lt;br /&gt;
&lt;br /&gt;
Der SIGNALduino(-Stick) erkennt Signale anhand von Mustern und gibt sie (als maximal detaillierte Beschreibung einer Signalabfolge) dann schlicht sofort nur noch an FHEM zur Auswertung (Dekodierung) weiter. Auch nicht erkannte Signale werden an FHEM übergeben.&lt;br /&gt;
Aufgabe des SIGNALduino (Firmware/Stick) ist es also nur, Signal-Aktivitäten zu erfassen und maximal präzise (als kurzer Text-String) zu beschreiben.&lt;br /&gt;
Alles weitere (echte, finale Auswertung / Zuweisung dieser Signal-Daten) wird dann auf einer großen Box (Raspberry o.ä.) gemacht.&lt;br /&gt;
&lt;br /&gt;
=== Vorteil gegenüber einem CUL/FHEMduino ===&lt;br /&gt;
Der SIGNALduino hat den Vorteil einer sehr schnellen Demodulation des Funksignals. Sollen weitere Protokolle dekodiert werden, so muss dazu nur ein passendes FHEM Modul entwickelt oder ein universelles Modul erweitert werden (also auf flexibel direkt updatebarer Rechner-Seite!!). Änderungen am Arduino-Firmware-Code sind normalerweise nicht notwendig (sofern die grundsätzliche Signal-Klassifizierung des Sticks korrekt funktioniert - leider nicht immer, z.B.: [https://github.com/RFD-FHEM/SIGNALDuino/issues/65 MU-Nachrichten werden z.T. als MC erkannt]).&lt;br /&gt;
&lt;br /&gt;
Ein großer Vorteil des SIGNALduino besteht darin, dass auch Geräte mit leicht abweichende Frequenzen steuerbar sind; so empfangen die Somfy-Rolladenmotoren beispielsweise auf 433.42 und nicht, wie bei anderen Geräten sehr oft üblich, auf 433.92 MHz. Die Frequenzumstellung stellt für den SIGNALduino kein Problem dar.&lt;br /&gt;
&lt;br /&gt;
Ebenso kann man den SIGNALduino direkt an den Sendeausgang eines Sensors anbinden und die digitalen Signale empfangen, dabei bitte aber auf die passenden Spannungen achten, denn ein Arduino Nano verträgt nur 5V.&lt;br /&gt;
&lt;br /&gt;
== Hardware ==&lt;br /&gt;
&lt;br /&gt;
Der SIGNALduino (Hardware) wird über den USB Port angeschlossen, er kann aber auch mit zusätzlichen ESP Modulen über ein WLAN angebunden werden. &lt;br /&gt;
&lt;br /&gt;
Der SIGNALduino basiert auf einem [http://arduino.cc/de/Main/ArduinoBoardNano Arduino Nano], die Schaltung entspricht der des [[FHEMduino]] oder dem [[Selbstbau_CUL]]:&lt;br /&gt;
* Entweder wird ein Arduino mit einfachen Sende- und Empfangsmodulen verwendet, dann ist die Verkabelung identisch zum [[FHEMduino]] &lt;br /&gt;
* Oder es wird ein CC1101 Transceiver verwendet, dann ist die Verkabelung identisch zum [[Selbstbau_CUL]].&lt;br /&gt;
* Zuletzt gibt es ein fertig konfektioniertes Modul von In-Circuit mit Radino CC1101 Varianten, link zum [http://shop.in-circuit.de/index.php Hersteller]. &lt;br /&gt;
&lt;br /&gt;
Achten Sie beim Selbstbau auf die entsprechenden Sender-Empfänger. Der sehr preiswert angebotene XY-MK-5V hat sich als zu unzuverlässig erwiesen, während anscheinend beim CC1101 (insbesondere der &amp;quot;grünen Version&amp;quot;) keine Probleme auftreten. &lt;br /&gt;
&lt;br /&gt;
Es stehen auch für den [https://www.arduino.cc/en/Main/arduinoBoardUno UNO] und [https://www.arduino.cc/en/Main/ArduinoBoardProMini PRO Mini] Firmware-Dateien zur Verfügung. Die ausgelieferte Firmware läuft nur auf Mikrocontrollern mit 16 MHz; wer einen Mikrocontroller mit 8 MHz verwenden möchte, muss die Firmware selbst compilieren. Die SW ist auf [https://github.com/RFD-FHEM/SIGNALDuino github]. Vorgesehen ist nur die Übersetzung unter Windows mit Visual Studio und dem Visual Micro Zusatz. Es gibt aber auch eine Anleitung, wie man mit der [[Arduino]] IDE oder einem Makefile [[SIGNALduino Compilieren]] kann.&lt;br /&gt;
&lt;br /&gt;
Es gibt auch eine Variante des SIGNALduino, die auf einem [[ESP8266]] nativ läuft, diese funktioniert seit Anfang 2018 annehmbar, allerdings befindet diese sich noch in einer Entwicklungsphase.&lt;br /&gt;
&lt;br /&gt;
An den &amp;quot;SIGNALESP&amp;quot; kann auch ein CC1101 via SPI angebunden werden:&lt;br /&gt;
&lt;br /&gt;
{||&lt;br /&gt;
!CC1101 Bezeichnung&lt;br /&gt;
!ESP Pin&lt;br /&gt;
|-&lt;br /&gt;
|CLK||GPIO14&lt;br /&gt;
|-&lt;br /&gt;
|MOSI||GPIO13&lt;br /&gt;
|-&lt;br /&gt;
|MISO||GPIO12&lt;br /&gt;
|-&lt;br /&gt;
|CSN||GPIO15&lt;br /&gt;
|-&lt;br /&gt;
|GDO0||GPIO4&lt;br /&gt;
|-&lt;br /&gt;
|GDO2||GPIO5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Wird ein einfacher Empfänger / Sender Kombination verwendet, dann über die PINs:&lt;br /&gt;
&lt;br /&gt;
{||&lt;br /&gt;
! Bezeichnung &lt;br /&gt;
! ESP Pin&lt;br /&gt;
|-&lt;br /&gt;
|Transmitter||GPIO4&lt;br /&gt;
|-&lt;br /&gt;
|Receiver||GPIO5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Einfache Module ===&lt;br /&gt;
&lt;br /&gt;
[[Datei:Fhemduino_schematic.png|200px|thumb|right|Beispielschaltplan SIGNAL(FHEM)duino]]  &lt;br /&gt;
&lt;br /&gt;
Mit einem Arduino-Nano und folgenden, billigen Sende- und Empfangsmodulen können Sie einen SIGNALduino bauen:&lt;br /&gt;
* FS1000A Dies ist das Sendemodul (TX) und wird oft im Set mit dem billigen XY-MK-5V-Empfänger angeboten (etwa 5€). &lt;br /&gt;
* RXB6 Das ist das empfohlene Empfangsmodul (RX), statt XY-MK-5V, etwa 5€ aus Deutschland.&lt;br /&gt;
&lt;br /&gt;
Die Verkabelung erfolgt analog zum [[FHEMduino]] und das bedeutet (Arduino -&amp;gt; Modul):&lt;br /&gt;
* PIN D2 an DATA des RX-Moduls&lt;br /&gt;
* PIN D11 an DATA des TX-Moduls (PIN links mit Beschriftung ATAD)&lt;br /&gt;
&lt;br /&gt;
Zusätzlich muss noch jeweils GND und 5V des Arduino mit dem GND bzw. VCC des Moduls verbunden werden.&lt;br /&gt;
&lt;br /&gt;
Jetzt fehlen noch die Antennen. Dafür braucht man ein 17,2 cm langes Stück Kupferdraht, das wird beim Anschluss &amp;quot;ANT&amp;quot; jeweils am Modul angelötet (anfängergeeignet).&lt;br /&gt;
&lt;br /&gt;
== Software: Modul ==&lt;br /&gt;
&lt;br /&gt;
===  USB-ID ermitteln  ===&lt;br /&gt;
Bevor der SIGNALduino mit dem FHEM Server (im Beispiel hier ein Raspberry PI) verbunden werden kann, muss die USB-Schnittstelle ermittelt werden. Hierzu bitte auf dem Terminal den Befehl&lt;br /&gt;
&amp;lt;pre&amp;gt; ls -l /dev/serial/by-id &amp;lt;/pre&amp;gt;&lt;br /&gt;
ausführen. Beispielhaft sieht das Ergebnis etwa so aus: &lt;br /&gt;
&#039;&#039;lrwxrwxrwx 1 root root 13 Jan 31 00:02 &#039;&#039;&#039;usb-FTDI_FT232R_USB_UART_A903N5T5-if00-port&#039;&#039;&#039; -&amp;gt; ../../ttyUSB0&#039;&#039; &lt;br /&gt;
Damit ist der Anschluss des SIGNALduino bestimmt und das Gerät kann wie im nächsten Abschnitt beschrieben definiert werden. Zuvor muss noch das Modul geladen werden.&lt;br /&gt;
&lt;br /&gt;
=== FHEM-Modul laden ===&lt;br /&gt;
Die SIGNALduino Module werden über das FHEM [[update]] verteilt, sobald die Änderungen einen &amp;quot;stabilen&amp;quot; und alltags tauglichen Zustand haben.&lt;br /&gt;
&lt;br /&gt;
Die in der Entwicklung befindliche Version kann mit folgenden Befehlen geladen werden:&lt;br /&gt;
&lt;br /&gt;
* FHEM aktualisieren: &amp;lt;code&amp;gt;update&amp;lt;/code&amp;gt; &lt;br /&gt;
* SIGNALduino Modul aktualisieren: &amp;lt;code&amp;gt;update all &amp;lt;nowiki&amp;gt;https://raw.githubusercontent.com/RFD-FHEM/RFFHEM/dev-r33/controls_signalduino.txt&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;  Durch das Update von FHEM wird sichergestellt, dass das Modul mit FHEM arbeitet.&lt;br /&gt;
*Danach kann das Gerät wie folgt definiert werden (die Spezifikation des USB-Anschlusses muss dabei an die aktuellen Gegebenheiten angepasst werden):&lt;br /&gt;
:&amp;lt;code&amp;gt;define &amp;lt;eigener-SIGNALduino-Name&amp;gt; SIGNALduino /dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A903N5T5-if00-port0@57600&amp;lt;/code&amp;gt;&lt;br /&gt;
* Solltet Ihr einen SIGNALESP via IP einbinden wollen ist die Syntax&lt;br /&gt;
:&amp;lt;code&amp;gt;define &amp;lt;eigener-SIGNALESP-Name&amp;gt; SIGNALduino &amp;lt;ip-adresse&amp;gt;:23&amp;lt;/code&amp;gt;&lt;br /&gt;
Nach dem Einbinden wird der SIGNALduino, falls er erkannt wird, im Status &amp;quot;Opened&amp;quot; angezeigt. &lt;br /&gt;
&lt;br /&gt;
Für neuere Entwicklungen kann in FHEM auch dauerhaft die developer Version aktualisiert werden:&lt;br /&gt;
&amp;lt;code&amp;gt;update add &amp;lt;nowiki&amp;gt;https://raw.githubusercontent.com/RFD-FHEM/RFFHEM/dev-r33/controls_signalduino.txt&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; &lt;br /&gt;
Danach wird FHEM bei dem normalen Update-Befehl immer automatisch die aktuelle dev Version laden.&lt;br /&gt;
&lt;br /&gt;
Die nachfolgenden Beispiel-Befehle verwenden &amp;quot;sduino&amp;quot; als &amp;lt;eigener-SIGNALduino-Name&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Flashen des Arduino mit der SIGNALduino Firmware ====&lt;br /&gt;
Falls avrdude noch nicht vorhanden ist, kann es mit folgendem Befehl installiert werden:&lt;br /&gt;
:&amp;lt;code&amp;gt;sudo apt-get install avrdude&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In FHEM ist der SIGNALduino mit dem Status &amp;quot;Open&amp;quot; vorhanden. Jetzt müssen wir FHEM noch mitteilen, welche Hardware wir angeschlossen haben. Über das Attribut &#039;&#039;hardware&#039;&#039; lässt sich zwischen den mitgelieferten Firmware-Dateien wechseln. Solltet ihr einen Nano mit cc1101 Transceiver verwenden, so wählt bitte folgende Hardware&lt;br /&gt;
&amp;lt;code&amp;gt;attr sduino hardware nanoCC1101&amp;lt;/code&amp;gt;&lt;br /&gt;
sonst üblicherweise&lt;br /&gt;
&amp;lt;code&amp;gt;attr sduino hardware nano328&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend kann der &#039;&#039;flash&#039;&#039; Befehl abgesetzt werden: &lt;br /&gt;
&amp;lt;code&amp;gt;set sduino flash &amp;lt;/code&amp;gt;&lt;br /&gt;
Dadurch wird der Arduino mit der gewählten Firmware geflasht. Das Ergebnis wird im Webinterface direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
Alternativ kann auch der Flash-Befehl mit einem Dateinamen aufgerufen werden. Diese Möglichkeit sollte jedoch nur verwendet werden, wenn die SIGNALduino Firmware selbst compiliert wurde und eine andere Hardware verwendet wird. Der Flash-Befehl wird wie folgt aufgerufen:&lt;br /&gt;
:&amp;lt;code&amp;gt;set sduino flash FHEM/firmware/SIGNALduino_mega2560.hex&amp;lt;/code&amp;gt;&lt;br /&gt;
(je nachdem wo und unter welchem Namen die .hex Datei abgelegt wurde)&lt;br /&gt;
&lt;br /&gt;
==== Flashen einer Firmware über HTTP ====&lt;br /&gt;
Die Firmware wird in absehbarer Zeit nicht mehr über den Update Mechanismus verteilt werden. &lt;br /&gt;
Damit die passende Firmware auf den SIGNALduino geladen werden kann, wird diese dann über HTTP geladen.&lt;br /&gt;
&lt;br /&gt;
==== Vorabversion einer Firmware ====&lt;br /&gt;
Die Firmware des SIGNALduino wird ebenso wie das FHEM Modul auch weiter entwickelt.&lt;br /&gt;
Die Entwickler sind auf Tests und Rückmeldungen der Nutzer angewiesen, da leider nicht alle Sensoren vorher getestet werden können.&lt;br /&gt;
&lt;br /&gt;
Aktuell (November 2018) ist noch die Version 3.3.1 in Entwicklung. Diese steht aktuell in einer Release Candidate Version zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
Für die folgenden Microcontroller kann man die Firmware downloaden. (Die Download Links funktionieren erst ab einer FHEM-Modul Version vom 11.03.2018!)&lt;br /&gt;
&lt;br /&gt;
SIGNALDuino_nanocc1101.hex für einen Nano mit CC1101&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set sduino flash https://github.com/RFD-FHEM/SIGNALDuino/releases/download/3.3.1-RC10/SIGNALDuino_nanocc1101.hex&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNALDuino_nano.hex für einen Nano ohne cc1101&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set sduino flash https://github.com/RFD-FHEM/SIGNALDuino/releases/download/3.3.1-RC10/SIGNALDuino_nano.hex&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNALDuino_promini.hex für einen  promini 3,3v / 8 Mhz ohne cc1101&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set sduino flash https://github.com/RFD-FHEM/SIGNALDuino/releases/download/3.3.1-RC10/SIGNALDuino_promini.hex&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNALDuino_miniculcc1101.hex für einen promini 3,3v / 8 Mhz mit cc1101 (minicul)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set sduino flash https://github.com/RFD-FHEM/SIGNALDuino/releases/download/3.3.1-RC10/SIGNALDuino_miniculcc1101.hex&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SIGNALDuino_radinocc1101.hex für einen radino mit cc1101 &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set sduino flash https://github.com/RFD-FHEM/SIGNALDuino/releases/download/3.3.1-RC10/SIGNALDuino_radinocc1101.hex&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
SIGNALESP_331rc4.bin (mit cc1101) für einen ESP8266 mit 1 MB Flash&lt;br /&gt;
&amp;lt;code&amp;gt;set ipduino flash https://github.com/RFD-FHEM/SIGNALESP/releases/download/3.3.1RC4/SIGNALESP_331RC4_1M.bin&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
!Achtung, aktuell wird die Firmware für den ESP dadurch nur herunter geladen. Flashen müsst ihr leider immer noch über ein passendes Tool &lt;br /&gt;
z.B. [https://github.com/nodemcu/nodemcu-flasher ESP8266Flasher.exe] oder Esptool und einer seriellen Konsole.&lt;br /&gt;
&lt;br /&gt;
Nach dem Booten des ESPs, spannt dieser ein eigenes WLAN auf. Habt ihr euch damit verbunden, könnt ihr den ESP mit eurem vorhandenen WLAN verbinden.&lt;br /&gt;
&lt;br /&gt;
==== Flashen eines radino Boards mit ATmega32U4 ====&lt;br /&gt;
&lt;br /&gt;
Diese Funktion steht nur in der Entwicklungsversion dev-r33 zur Verfügung:&lt;br /&gt;
&lt;br /&gt;
Auch sind Berichte bekannt, dass der Radino beim Neustart von FHEM nicht korrekt initialisiert wird.&lt;br /&gt;
&lt;br /&gt;
==== Fehler beim Flashen ====&lt;br /&gt;
Sollte bei einem Flash Vorgang ein Fehler auftreten, solltet ihr zunächst im Logfile mit Verbose 5 nachsehen.&lt;br /&gt;
&lt;br /&gt;
Findet ihr dort keine Fehlermeldung, gibt es noch ein separates Flashlog, welches ihr über einen Browser aufrufen könnt. Dazu müsst ihr nur den Folgenden Pfad an euren Servernamen anhängen:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
/fhem/FileLog_logWrapper?dev=Logfile&amp;amp;type=text&amp;amp;file=SIGNALduino-Flash.log&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Geräteerkennung ===&lt;br /&gt;
==== Unterstützte Geräte ====&lt;br /&gt;
Für die folgenden Geräte gibt es derzeit (2017) eine Unterstützung für den Betrieb mit FHEM. Die Geräte werden [[autocreate|automatisch erkannt]] und in der Konfiguration eingetragen, wenn der SIGNALduino läuft.&lt;br /&gt;
Bitte Geräte mit sehr präzisen Referenzen hier listen (Produktnummer, Protokoll-Variantenname etc.), damit eine globale Suche/Verifikation maximal erfolgreich ist.&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! style=&amp;quot;text-align:left;&amp;quot;| Produkt &lt;br /&gt;
! (E)mpfangen&amp;lt;br /&amp;gt;(S)enden &lt;br /&gt;
! Hinweise &lt;br /&gt;
! Verwendetes Modul &lt;br /&gt;
! Protokoll ID&lt;br /&gt;
|-&lt;br /&gt;
|Conrad Wetterstation KW9110||E||Sensor: KW9010, neben Temperatur u. Luftfeuchte werden auch Trend, Batterie u. Kanal erfasst|| CUL_TCM97001  || 0&lt;br /&gt;
|-&lt;br /&gt;
|TCM Wetterstation (97001 und 21xxx Serie)||E|| || CUL_TCM97001 || 0&lt;br /&gt;
|-&lt;br /&gt;
|ABS Wetterstation (ABS 700)||E|| || CUL_TCM97001  || 0&lt;br /&gt;
|-&lt;br /&gt;
|Prologue Wetterstation ||E|| ||CUL_TCM97001 || 0&lt;br /&gt;
|-&lt;br /&gt;
|Rubicson Wetterstation ||E|| ||CUL_TCM97001 ||0 &lt;br /&gt;
|-&lt;br /&gt;
|NC_WS Wetterstation ||E|| ||CUL_TCM97001 || 0&lt;br /&gt;
|-&lt;br /&gt;
|[http://www.gt-support.de/ GT-WT-02 Wetterstation]||E|| ||CUL_TCM97001 || 0&lt;br /&gt;
|-&lt;br /&gt;
|AURIOL Wetterstation ||E|| ||CUL_TCM97001 || 0&lt;br /&gt;
|-&lt;br /&gt;
|Mebus Wetterstation ||E|| ||CUL_TCM97001 || 0&lt;br /&gt;
|-&lt;br /&gt;
|Intertechno Funkschalter||E S|| ||IT || 3,4,5,17&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;strike&amp;gt;Conrad RSL Funkschalter&amp;lt;/strike&amp;gt;||E S|| Funktioniert aktuell nicht || SIGNALduino_RSL  || &lt;br /&gt;
|-&lt;br /&gt;
|[http://global.oregonscientific.com/product_view.php?id=5 Oregon Scientific Wettersensoren]||E || Protokoll V2 &amp;amp; V3 implementiert || OREGON || 10&lt;br /&gt;
|-&lt;br /&gt;
|Bresser Temp/Hydro Sensor||E || || Hideki || 12&lt;br /&gt;
|-&lt;br /&gt;
|[https://de.hama.com/00104985/hama-aussensensor-ts33c-fuer-wetterstation Hama TS33C]||E || || Hideki || 12&lt;br /&gt;
|-&lt;br /&gt;
|TFA Temp/Hydro Sensor||E || || Hideki || 12&lt;br /&gt;
|-&lt;br /&gt;
|Lacrosse TX2/TX3 Sensoren||E || || CUL_TX || 8&lt;br /&gt;
|-&lt;br /&gt;
|TFA 30320902||E || || SD_WS07 || 7&lt;br /&gt;
|-&lt;br /&gt;
|Eurochron eas800z||E || || SD_WS07  || 7&lt;br /&gt;
|-&lt;br /&gt;
|Technoline WS6750/TX70DTH||E || || SD_WS07 || 7&lt;br /&gt;
|-&lt;br /&gt;
|FreeTec Außenmodul NC-7344||E || || SD_WS07 || 7&lt;br /&gt;
|-&lt;br /&gt;
|CTW600||E || || SD_WS09 || 9&lt;br /&gt;
|-&lt;br /&gt;
|WH1080||E || || SD_WS09 || 9&lt;br /&gt;
|-&lt;br /&gt;
|Visivon remote pt4450||E || || none || 24&lt;br /&gt;
|-&lt;br /&gt;
|Einhell HS 434/6||E || || none || 21&lt;br /&gt;
|-&lt;br /&gt;
|Flamingo FA20RF / FA21RF Rauchmelder||E || || none || 13&lt;br /&gt;
|-&lt;br /&gt;
|mumbi m-FS300||E || || none || 26,27&lt;br /&gt;
|-&lt;br /&gt;
|TFA 30.3200||E || || none || 33&lt;br /&gt;
|-&lt;br /&gt;
|Livolo||E|| || none || 20&lt;br /&gt;
|-&lt;br /&gt;
|Smartwares RM174RF/2 (RM174RF-001CPR) 4500177571 ||E [S?]|| IT EV1527; TODO herausfinden: Alarmierung (wie Alarmton getriggered werden kann); Batterieinfo? || IT || 3&lt;br /&gt;
|-&lt;br /&gt;
|Smartwares SH5-TSO-A||E S|| || IT || ?&lt;br /&gt;
|-&lt;br /&gt;
|X10 Security Devices||E|| ||  || 39&lt;br /&gt;
|-&lt;br /&gt;
|[[Somfy_via_SIGNALduino|Somfy RTS]]||E S|| || SOMFY || 43&lt;br /&gt;
|}&lt;br /&gt;
Bei einigen Intertechno-Funksteckdosen (Brennenstuhl) kann es zu Empfangsproblemen kommen. Hier muss die Taktrate, mit der gesendet wird, angepasst werden. Dazu muss für &#039;&#039;Funksteckdose&#039;&#039; (also sauber per-Client-Instanz-spezifisch, NICHT SIGNALduino-Transceiver-global) das Attribut &lt;br /&gt;
:&amp;lt;code&amp;gt;attr &amp;lt;Funksteckdose&amp;gt; ITclock 300&amp;lt;/code&amp;gt; &lt;br /&gt;
gesetzt werden, der Standardwert ist 250.&lt;br /&gt;
&lt;br /&gt;
==== Mein Gerät wird in FHEM nicht erkannt ====&lt;br /&gt;
1. Prüfen, ob vom Sensor die Signaldaten (verbose &amp;gt;=4) erkannt werden. Sobald ihr die empfangenen Signaldaten im Logfile zuordnen könnt, geht es weiter mit:&lt;br /&gt;
&lt;br /&gt;
2. Eröffnet ein Thema unter [https://github.com/RFD-FHEM/RFFHEM/issues github] nach folgendem Muster:&lt;br /&gt;
:&#039;&#039;Thema :  Protokoll für &amp;lt;Das verwendete Gerät&amp;gt;&lt;br /&gt;
:Inhalt:  Eure Hardware z.B. Arduino Nano mit XYZ Empfänger, oder Arduino Nano direkt an Gerät x&lt;br /&gt;
&lt;br /&gt;
3. Auszug aus dem Logfile, welches zum Gerät gehört.&lt;br /&gt;
:&#039;&#039;Alles was ihr sonst noch über das Gerät und die übertragenen Daten wisst.&lt;br /&gt;
&lt;br /&gt;
Im Forum solltet ihr solche Fragen besser nicht posten, wenn das Gerät noch nicht unterstützt wird, dazu ist Github besser geeignet. Inzwischen wurde im Wiki eine eigene Seite eröffnet, die sich mit der Erkennung unbekannter Protokolle beschäftigt: [[Unbekannte_Funkprotokolle#Ansatz_1_-_Versuchen|Unbekannte_Funkprotokolle]].&lt;br /&gt;
&lt;br /&gt;
==== Es wird ein Protokoll erkannt, Autocreate legt aber kein device an ====&lt;br /&gt;
Im SIGNALduino sind &amp;gt;30 Protokolle implementiert. Jedoch gibt es nur wenige Module, welche diese Protokolle verarbeiten.&lt;br /&gt;
Teilweise ist das auch nicht zwingend notwendig, um seine Anforderungen zu erfüllen. Insbesondere für Schalter bzw. Sensoren, die nur zwei Zustände kennen, geht es meist ohne Modul und automatisch angelegtem Gerät.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir an, wir haben einen Schalter. Dieser kann einen oder zwei Zustände senden.&lt;br /&gt;
Im FHEM Log (und, insbesondere, im FHEMWEB Event Monitor) tauchen Meldungen ähnlich dieser auf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
2015.11.15 15:52:23 4: SIGNALduino_unknown incomming msg: u85#FF8081&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wir können mit Hilfe des Modules DOIF auf diese Nachricht eine Aktion ausführen:&lt;br /&gt;
&lt;br /&gt;
Entweder, wenn wir den Inhalt der Nachricht nur zu Teilen wissen, da er sich ändert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
define mydoif DOIF ([sduino:&amp;amp;DMSG] =~ &amp;quot;u85#FF8081&amp;quot;) (set Lamp on)&lt;br /&gt;
attr mydoif do always&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Oder, wenn wir den Inhalt exakt kennen, dann auch als Vergleichsstring&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
define mydoif DOIF ([sduino:&amp;amp;DMSG] eq &amp;quot;u85#FF8081&amp;quot;) (set relais on)&lt;br /&gt;
attr mydoif do always&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Teil u85#FF8081 muss individuell angepasst werden, der Name eures SIGNALduino möglicherweise auch.&lt;br /&gt;
&lt;br /&gt;
Als Alternative zu DOIF hier ein regex-verwendendes notify-Beispiel für einen Sender, der meint, zwei Codes alternierend senden zu müssen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
define n_sender_trigger notify sduino:UNKNOWNCODE.*u41#(13B72253|163873B3) { my_sender_trigger_indicate();; }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Selbstverfreilich muss in diesem Moment auch eine sub my_sender_trigger_indicate() definiert werden (z.B. in FHEM/99_myUtils.pm), die dort z.B. als Test eine Log-Ausgabe (Log3()) machen kann.&lt;br /&gt;
&lt;br /&gt;
=== Das Logfile ===&lt;br /&gt;
Im Logfile ab [[Verbose]] 4 tauchen diverse Meldungen auf, deren Bedeutung kurz erläutert wird (verbose 3 unterdrückt diese Meldungen).&lt;br /&gt;
&lt;br /&gt;
UPDATE: der folgende Bereich ist von einem weniger erfahrenen Zeitgenossen früher nach Kräften erweitert/geschrieben worden. Mittlerweile existiert aber ein neuer Inhalt [[Unbekannte_Funkprotokolle]] (siehe auch Erwähnung weiter oben), der als sehr gut beschrieben und unvergleichlich detailreicher bezeichnet werden muss (&amp;quot;endlich gibt es sowas!&amp;quot;). Der Bereich hier dürfte somit zwar für grundlegende Verdeutlichungen noch recht sinnvoll sein, der Inhalt sollte allerdings evt. in eine konsistente Dokumentation überarbeitet (verlagert/dedupliziert/reduziert) werden.&lt;br /&gt;
&lt;br /&gt;
Die Protokolle (von der SIGNALDuino-Firmware gesendete Signal-Beschreibungs-Strings) können wie folgt unterschieden werden:&lt;br /&gt;
&lt;br /&gt;
*MS - Nachricht mit Sync Puls: Hierzu ein Beispiel&lt;br /&gt;
:&amp;lt;code&amp;gt;MS;P0=-108;P1=395;P2=-1033;P3=-547;P4=-19932;P5=-8916;P6=1368;D=151313131312131313131313131313131312121212121313131313131312131212132;CP=1;SP=5;&amp;lt;/code&amp;gt; P0-P6 sind die Signalpegel (Dauer und positiv/negativ). Hinter D= befindet sich die Abfolge der Signale. Die ersten beiden Ziffern 15 in D sind wie folgt zu lesen. Zuerst wurde ein Signal &amp;quot;1&amp;quot; also P1 mit 395 Mikrosekunden high (die Zeitdauer ergibt sich aufgrund der Mitteilung &amp;quot;P1=395&amp;quot;) und anschließend ein Signal &amp;quot;5&amp;quot; also P5 mit 8916 Mikrosekunden low (die Zeitdauer ergibt sich aufgrund der Mitteilung &amp;quot;P5=-8916&amp;quot;) gemessen. CP=1 ist die Referenz auf den Takt des Signales - der Basistakt ist in diesem Fall ~395 Mikrosekunden. SP=5 gibt die Referenz zum Syncpuls an, der das gesamte Signal einleitet. Welche Signalfolge nun eine binäre 1 bzw. 0 bedeutet, wird im SIGNALduino über die integrierte Protokoll Liste realisiert.&lt;br /&gt;
&lt;br /&gt;
*MC - Nachricht vom Typ Manchester: Manchesterkodierte Signale können bereits sehr einfach im Arduino in eine Binärform umgewandelt werden. Es wird hier nach IEEE 802.3 umgewandelt. In Manchester Signalen gibt es lange und kurze Pulse. Deren Durchschnittswert wird mit LL (long low), LH (long high), SL (short low) und SH (short high) übermittelt. Zusätzlich, um das Protokoll schneller erkennen zu können, wird die Taktfrequenz mit übermittelt (C=429 Mikrosekunden). Die Daten befinden sich hinter D= und werden in HEX Form übergeben.&lt;br /&gt;
:&amp;lt;code&amp;gt;MC;LL=-1066;LH=904;SL=-562;SH=385;D=332B4B4D54D5554B552CD2D554B2B5354A;C=429;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*MU - Message unsynced: Diese Art von Nachrichten sind nicht nach Manchester codiert und haben auch keinen erkennbaren Sync / Clock Signalpegel am Start der Nachricht. Bei diesen Nachrichtentypen ist es, im Vergleich zu den anderen, am wahrscheinlichsten, dass das übermittelte Signal unvollständig oder überhaupt kein Signal ist. Wie bei MS sind P0-P6 die Signalpegel und in D= wird die Abfolge der Signalpegel referenziert. CP=2 gibt auch hier die Referenz zum Takt an, allerdings muss dieser nicht korrekt erkannt worden sein.&lt;br /&gt;
:&amp;lt;code&amp;gt;MU;P0=1372;P1=-580;P2=362;P3=-1047;D=01212321212321212121212121212123212123212321232121212121212321;CP=2;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es erscheinen viele Meldungen dieser Art:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Fingerprint for MU Protocol id xxxx -&amp;gt; yyy matches, trying to demodulate&lt;br /&gt;
sduino: Starting demodulation at Position 1&lt;br /&gt;
Fingerprint for MU Protocol id 28 -&amp;gt; IC Ledspot matches, trying to demodulate&lt;br /&gt;
sduino: Starting demodulation at Position 1&lt;br /&gt;
Fingerprint for MU Protocol id 29 -&amp;gt; HT12e remote matches, trying to demodulate&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies sind nun Bemühungen, anhand der von der SIGNALDuino-Firmware gelieferten rohen aber detaillierten Signal-Strings eine Vor-Analyse / Fingerprinting vorzunehmen.&lt;br /&gt;
Man könnte nun z.B. bei solchen Fingerprinting-Analysen erkennen:&lt;br /&gt;
* dass der Basis-Takt-Wert innerhalb eines charakteristischen Zeit-Bereichs liegt&lt;br /&gt;
* dass die Anzahl der Sync-Pulse eine präzise Zahl ist&lt;br /&gt;
* dass Längen erkannter Puls-Typen innerhalb eines Bereichs liegen&lt;br /&gt;
&lt;br /&gt;
Mittels solcher Untersuchungen kann man also final hoffentlich hinreichend plausibel feststellen, &amp;quot;dass diese Aktivitäten offensichtlich(?) zu einer Funk-Komponente Rauchmelder von Hersteller XYZ gehören müssen, und man somit weiterleiten muss an ein (möglicherweise bereits existierendes) Userdaten-Dekodier-Modul für diese Herstellerkomponente&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei einer dann erfolgenden Demodulation des noch rohen SIGNALDuino-Strings könnte man z.B. (hoffentlich richtigerweise) annehmen, dass eine Signalpegel-Typ-Folge &amp;quot;13&amp;quot; eine binäre 1 bedeuten soll, während eine Folge &amp;quot;12&amp;quot; eine binäre 0 bedeuten soll. Man erhält aus dem Gesamt-Puls-String also nach vollständiger Demodulation eine Abfolge von vielen 0/1 Bits, die insgesamt ein Datenwort darstellen, mit einer gewissen Länge von NN bits (diese Längen-Angabe könnte übrigens - neben Namenssuche nach Hersteller oder Produkt etc. - ein wichtiges Such-Merkmal sein, ob andere Frameworks tatsächlich bereits wissen, wie Daten dieser Funk-Komponente zu dekodieren sind!). Dieses demodulierte Datenwort ist nun das finale Datenwort, welches einen Container für die Funk-Komponenten-Informationen darstellt (in diesem Container also beispielsweise enthalten: Temperatur, Feuchte, Akku-Status, ID, Alarm, ... - zumindest wenn nicht dummerweise der ganze Container erst einmal CRC- oder Crypto-verschlüsselt ist...).&lt;br /&gt;
&lt;br /&gt;
Man muss an dieser Stelle unbedingt sagen, dass dieses Userdaten-Datenwort (einer bestimmten Hersteller-Funk-Komponente!) natürlich bei &#039;&#039;jeglichen&#039;&#039; Transceiver-Systemen &#039;&#039;immer&#039;&#039; gleich erkannt werden &#039;&#039;muss&#039;&#039; - an dieser Stelle ist also ganz klar, dass diese Daten an &#039;&#039;allgemeine&#039;&#039; FHEM-Module weitergeleitet (Dispatched) werden müssen, die nach Übernahme von Daten von &#039;&#039;jeglichen&#039;&#039; Transceiver-Systemen diese Daten immer auf die gleiche Weise (&#039;&#039;&#039;&#039;&#039;generisch/zentral&#039;&#039;&#039;&#039;&#039;) für die jeweilige Hersteller-Funk-Komponente erledigen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Die Abfolge ist also ganz klar:&#039;&#039;&#039;&lt;br /&gt;
Funk-Aktivität --&amp;gt; Transceiver-Gerät/Firmware (SIGNALDuino) --&amp;gt; maximal detailreich beschreibender Rx-Analyse-Output-String --&amp;gt; Fingerprinting-Grobzuordnung des (SIGNALDuino-Firmware-)Outputs (durch 00_SIGNALduino.pm) auf gerätespezifisches Verhalten --&amp;gt; &#039;&#039;generische/zentrale&#039;&#039; Dekodierung des gerätespezifischen Protokoll-Datenworts, in zentralen Grundsatz-Modulen wie z.B. &amp;lt;code&amp;gt;14_SD_WS.pm&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Und wenn dann bei einer solchen Schritte-Abfolge irgendetwas noch fehlen/unpassend sein sollte, dann muss eben entsprechendes Development an gewissen Stellen erfolgen ;-)&lt;br /&gt;
&lt;br /&gt;
====Minimieren (whitelist/blacklist) von unerwünschter Kommunikations-Aktivität/Einträgen====&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Unknown Code&amp;quot; bedeutet, dass der SIGNALduino Signaldaten empfangen und diese binär interpretiert hat. Diese Meldung soll uns nun aber mitteilen, dass es dann nicht weiter verarbeitet werden kann, da kein Modul  existiert (oder kein Weiterleitungs-Dispatch zu einem bereits existierenden), welches diese Daten jetzt in ihre Bedeutung umwandeln kann. &lt;br /&gt;
:&amp;lt;code&amp;gt;sduino: Unknown code u1FFFFF0, help me!&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Außerdem kommt es gehäuft zu Logmeldungen und auch Events in ähnlicher Form:&lt;br /&gt;
:&amp;lt;code&amp;gt;SIGNALduino_unknown incomming msg: u85#FF8081&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittlerweile sind über 50 Protokolle für den SIGNALduino definiert. Dadurch kommt es vor, dass sich ein Signal mit mehr als einem Protokoll demodulieren lässt. Meist führt dies dann zu zusätzlichen &amp;quot;Unknown code&amp;quot;-Einträgen.&lt;br /&gt;
&lt;br /&gt;
Derartige Einträge können mit dem Attribut WhitelistID minimiert werden. Dabei werden die Geräte, die nach Daten-Empfang tatsächlich verarbeitet werden sollen (also welche Protokolle vom FHEM Modul berücksichtigt werden), mit ihrer Protokollnummer in die WhitelistID aufgenommen.&lt;br /&gt;
Für Protokolle, die nicht berücksichtigt werden, gibt es weder Logeinträge noch Events. Diese werden im Programmablauf nicht berücksichtigt. Das spart zum einen Ressourcen und trägt auch zur Übersichtlichkeit bei. &lt;br /&gt;
Die Protokollnummer kann Tabelle [[#Unterst.C3.BCtzte_Ger.C3.A4te]] entnommen werden (hilfreich ist es auch, wenn in den verwendeten Geräten im Internal &amp;lt;gerätename&amp;gt;_DMSG nachgesehen wird). So bedeutet beispielsweise ein Eintrag der Form &amp;lt;code&amp;gt;W50#FF553335FFBC&amp;lt;/code&amp;gt; dass dann das Protokoll  #50 in die Whitelist aufzunehmen wäre (&amp;lt;code&amp;gt;attr sduino whitelist_IDs 50&amp;lt;/code&amp;gt;).&lt;br /&gt;
{{Randnotiz|RNTyp=r|RNText=Achtung Schreibweise: Dokumentation oft als WhitelistID o.ä., aber Name ist whitelist_IDs!!&lt;br /&gt;
}}&lt;br /&gt;
Die Angabe erfolgt durch Komma getrennt: z.B.:&lt;br /&gt;
:&amp;lt;code&amp;gt;1,2,5,10&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Senden mit dem SIGNALduino ===&lt;br /&gt;
Der SIGNALduino kann etwas &amp;quot;raw senden&amp;quot;, indem ihm das SIGNAL so übermittelt wird, wie er es moduliert. Hierzu  muss der Befehl wie folgt eingegeben werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
set sduino sendMsg P3#00111010#R4&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieser Befehl moduliert die Bitfolge 00111010 mittels Protokoll #3 und wiederholt die Nachricht 4x.&lt;br /&gt;
Die Protokoll Nummer kann aus einer empfangenen Nachricht extrahiert werden. Ebenso die Bits.&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
sduino: extracted data 00111010 (bin)&lt;br /&gt;
sduino: Found Protocol id 3 &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann das Signal auch in einer &amp;quot;rohform&amp;quot; angegeben werden. Dies ist manchmal in speziellen Fällen notwendig:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
set sduino raw SR;;R=3;;P0=4742;;P1=-1554;;P2=286;;P3=-786;;P4=649;;P5=-420;;D=0123234545234545452323232323454523234523454523232345454523232323452345234523452345;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
R=3 bedeutet, das Signal wird 3x gesendet.&lt;br /&gt;
Die Übertragung besteht aus den in D angegeben Pulsen, welche in P0-P5 definiert werden.&lt;br /&gt;
Die Daten kann man aus einer empfangenen MS oder MU Nachricht extrahieren.&lt;br /&gt;
&lt;br /&gt;
Alternativ kann ab Version 3.2 auch eine vereinfachte Form eingegeben werden.&lt;br /&gt;
&lt;br /&gt;
====Fehlersuche====&lt;br /&gt;
&lt;br /&gt;
(Zielgerät reagiert nicht, etc.)&lt;br /&gt;
&lt;br /&gt;
* Nachrichtenwiederholungsanzahl muss evt. für manche Geräte entsprechend groß eingestellt sein&lt;br /&gt;
* Sende-Takt-Wert (Clock) passt evt. nicht ganz, siehe z.B. Thread-Antwort [https://forum.fhem.de/index.php/topic,58397.msg775434.html#msg775434 Signalduino Version 3.3.1], wo für IT-Geräte ein Attribut anhand der CP= des Empfangsdaten-Logs modifiziert wird. ACHTUNG: dies kann entweder global das Internal-Attribut ITClock eines SIGNALduino-Transceiver-Devices sein, oder (viel besser da korrekt geräte-instanz-spezifische Konfiguration) das ITclock eines IT-Client-Devices.&lt;br /&gt;
{{Randnotiz|RNTyp=r|RNText=VORSICHT blöder Schreibweisen-Mismatch ITClock vs. ITclock!!&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Fehlerbehandlung ==&lt;br /&gt;
Der SIGNALduino kann mit folgendem Befehl auf Werkseinstellungen zurückgesetzt werden:&lt;br /&gt;
:&amp;lt;code&amp;gt;get raw e&amp;lt;/code&amp;gt;&lt;br /&gt;
als Antwort kommt dann &amp;quot;ccFactoryReset done&amp;quot;. Ob ein solcher Reset nötig ist, erkennt man an der Antwort auf den Befehl &amp;quot;get config&amp;quot;, auf den dann die Meldung &amp;quot;config: MS=1;MU=1;MC=1&amp;quot; folgen sollte.&lt;br /&gt;
&lt;br /&gt;
In der Firmware sind die folgenden Befehle eingebaut&lt;br /&gt;
&lt;br /&gt;
*:&amp;lt;code&amp;gt;get raw C&amp;lt;reg&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;reg&amp;gt; is a (two digit) hex number: return the value of the cc1101 register. &amp;lt;reg&amp;gt;=99 dumps the first 48 registers.&lt;br /&gt;
Example: C35 -&amp;gt; C35 = 0D&lt;br /&gt;
*:&amp;lt;code&amp;gt;get raw e&amp;lt;/code&amp;gt;&lt;br /&gt;
EEPROM / factory reset.  resets all eeprom values without reboot&lt;br /&gt;
*:&amp;lt;code&amp;gt;get raw r&amp;lt;AA&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
Read eeprom  (da das &amp;quot;R&amp;quot; beim SIGNALDuino bereits mit freeram belegt ist, habe ich das &amp;quot;r&amp;quot; verwendet)&lt;br /&gt;
*:&amp;lt;code&amp;gt;get raw r&amp;lt;AA&amp;gt;n&amp;lt;/code&amp;gt;&lt;br /&gt;
Read 16 byte from eeprom (z.B. r00n)&lt;br /&gt;
*:&amp;lt;code&amp;gt;get raw W&amp;lt;AA&amp;gt;&amp;lt;DD&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
Write eeprom (schreibt einen Wert ins EEPROM und ins CC1101 Register. Die eeprom Adresse hat einen Offset von 2. z.B W041D schreibt 1D ins Register 2)&lt;br /&gt;
&lt;br /&gt;
Die Sendeleistung lässt sich mit &lt;br /&gt;
:&amp;lt;code&amp;gt;get sduino ccreg 3E&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
prüfen, wobei die Rückmeldung wie folgt zu lesen ist: &lt;br /&gt;
  &amp;quot;-10_dBm&amp;quot;  =&amp;gt; &#039;34&#039;,&lt;br /&gt;
  &amp;quot;-5_dBm&amp;quot;   =&amp;gt; &#039;68&#039;,&lt;br /&gt;
  &amp;quot;0_dBm&amp;quot;    =&amp;gt; &#039;60&#039;,&lt;br /&gt;
  &amp;quot;5_dBm&amp;quot;    =&amp;gt; &#039;84&#039;,&lt;br /&gt;
  &amp;quot;7_dBm&amp;quot;    =&amp;gt; &#039;C8&#039;,&lt;br /&gt;
  &amp;quot;10_dBm&amp;quot;   =&amp;gt; &#039;C0&#039; &lt;br /&gt;
Dabei wird die Sendeleistung dauerhaft mit dem Befehl&lt;br /&gt;
:&amp;lt;code&amp;gt;set sduino cc1101_patable &amp;lt;value&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
hochgeschaltet (&amp;lt;value&amp;gt; durch den Wert ersetzen).&lt;br /&gt;
&lt;br /&gt;
Weitere Firmware-Befehle sind im Thread-Beitrag {{Link2Forum|Topic=58396|Message=497921}} zu finden.&lt;br /&gt;
&lt;br /&gt;
== Foren Links ==&lt;br /&gt;
* {{Link2Forum|Topic=38402|LinkText=Forenthread - Ankündigung}}&lt;br /&gt;
* {{Link2Forum|Topic=58396|LinkText=SIGNALDuino Empfänger Firm- und Hardware}}&lt;br /&gt;
* {{Link2Forum|Topic=58397|LinkText=Signalduino Entwicklung Version 3.3.1 }}&lt;br /&gt;
* [http://www.rflink.nl/blog2/wiring Beschreibung zu diversen Empfängern und Verbesserung der Empfangsleistung]&lt;br /&gt;
* [[SIGNALduino in die Arduino Entwicklungsumgebung einbinden]]&lt;br /&gt;
* [[Somfy via SIGNALduino]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Interfaces]]&lt;br /&gt;
[[Kategorie:Arduino]]&lt;br /&gt;
[[Kategorie:433MHz]]&lt;br /&gt;
[[Kategorie:868MHz]]&lt;/div&gt;</summary>
		<author><name>Dora71</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=TelegramBot&amp;diff=28139</id>
		<title>TelegramBot</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=TelegramBot&amp;diff=28139"/>
		<updated>2018-10-21T17:23:34Z</updated>

		<summary type="html">&lt;p&gt;Dora71: /* Gruppen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Senden und Empfangen von Nachrichten (Text und Fotos) mit dem freien Messagingdienst Telegram&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModForumArea=Unterstützende Dienste&lt;br /&gt;
|ModFTopic=38328&lt;br /&gt;
|ModTechName=[https://github.com/viegener/Telegram-fhem/blob/master/50_TelegramBot.pm 50_TelegramBot.pm]&lt;br /&gt;
|ModOwner=[[Benutzer:Viegener|Viegener]] ({{Link2FU|12772|Forum}} / [[Benutzer Diskussion:Viegener|Wiki]])}}&lt;br /&gt;
&lt;br /&gt;
Das [[TelegramBot]]-Modul ermöglicht das Senden und Empfangen von Nachrichten über den Telegram-instant messaging Dienst (https://telegram.org/). &lt;br /&gt;
Es entsteht eine Möglichkeit Benachrichtungen aus FHEM zu versenden, zum Beispiel Alarmmeldungen.&lt;br /&gt;
Ausserdem können auch Kommandos über Telegram an FHEM gesendet werden um Steuerungsbefehle in FHEM auszulösen.&lt;br /&gt;
&lt;br /&gt;
Das TelegramBot-Modul benötigt keine Zusatzsoftware auf dem FHEM-Server (anders als die Variante [[Telegram]]), sondern verwendet das [https://core.telegram.org/bots/api TelegramBot-API] über https-Aufrufe. Es muss jedoch das [http://www.fhemwiki.de/wiki/Raspberry_Pi#N.C3.BCtzliche_Zusatzpakete perl JSON modul] installiert sein. &lt;br /&gt;
&lt;br /&gt;
== Über Telegram Instant Messaging ==&lt;br /&gt;
Telegram-IDs und Versand/Empfang von Nachrichten sind kostenfrei. &lt;br /&gt;
Clients sind für gängige Smartphonesysteme erhältlich (iOS iPhone und Tablet, Android, Windows Phone) und &lt;br /&gt;
können auch aus dem WebBrowser verwendet werden. &lt;br /&gt;
Es gibt auch einen Kommandozeilen-Client für Linux, der die Grundlage dieses Moduls darstellt. &lt;br /&gt;
Mehrfachanmeldungen, auch parallel mit verschiedenen Geräten (z.B. Tablet und Smartphone), sind möglich.&lt;br /&gt;
Gruppenchats und Chats mit End-2-End-Verschlüsselung werden ebenfalls unterstützt.&lt;br /&gt;
&lt;br /&gt;
Für die Unterstützung von &#039;&#039;WhatsApp&#039;&#039; siehe Modul [[yowsup]].&lt;br /&gt;
&lt;br /&gt;
== Features ==&lt;br /&gt;
Unterstützt werden:&lt;br /&gt;
&lt;br /&gt;
* Versand von Textnachrichten &lt;br /&gt;
* Versand und Empfang von Bildern/Audio/etc &lt;br /&gt;
* Empfang von Textnachrichten von beliebigen Kontakten&lt;br /&gt;
* Kommandos in FHEM über Telegram-Nachrichten von aussen auslösen&lt;br /&gt;
* Ergebnisse der Kommandos zusenden lassen&lt;br /&gt;
&lt;br /&gt;
Eine detaillierte Beschreibung des Moduls ist im FHEM Forum und in der (englischen) Dokumentation zum Modul in der {{Link2CmdRef|Anker=TelegramBot}} und in diesem  {{Link2Forum|Topic=38328|LinkText=Diskussionsthread}} zu finden. Seit Oktober 2015 wird das Modul offiziell über FHEM-Update verteilt.&lt;br /&gt;
&lt;br /&gt;
Die jeweils aktuellste Entwicklungs-Version des Moduls ist in Github [https://github.com/viegener/Telegram-fhem/blob/master/50_TelegramBot.pm 50_TelegramBot.pm] verfügbar.&lt;br /&gt;
&lt;br /&gt;
== Hinweise zum Betrieb mit FHEM ==&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=Achtung: Dieses Authtoken ist die einzige Authentifizierung für den Bot und sollte deshalb nicht aus der Hand gegeben werden. Die verwendeten Urls sind deshalb auch in den Log-Files nicht enthalten, da diese das Authtoken in Klartext enthalten. Auch im Forum sollte dieses Token nicht aufgenommen werden.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Für die Anlage eines TelegramBot Devices in FHEM ist ein Authtoken erforderlich. Dieses Token wird über Anlegen eines neuen Bots im [https://core.telegram.org/bots#botfather BotFather] erzeugt. Dafür muss der BotFather mit einem Telegram-Client kontaktiert werden. Dort mit dem Telegram-Befehl &amp;lt;code&amp;gt;/newbot&amp;lt;/code&amp;gt; einen neuen Bot anlegen und mit einem Namen versehen. Hinweis: Die Namen für Bots müssen auf &amp;quot;Bot&amp;quot; enden.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Anlegen eines TelegramBot devices erfolgt durch die Angabe dieses Tokens:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;define &amp;amp;lt;name&amp;amp;gt; TelegramBot  &amp;amp;lt;token&amp;amp;gt; &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel: &amp;lt;code&amp;gt;define teleBot TelegramBot 110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Das Empfangen von Nachrichten (polling) erfordert die Einstellung des Attributes&#039;&#039;&#039; &lt;br /&gt;
&amp;lt;code&amp;gt;pollingTimeout&amp;lt;/code&amp;gt; &#039;&#039;&#039;auf einen Wert der grösser als Null ist. Beim Wert 0 oder ohne Setzen des Attributes findet kein Polling und damit auch kein Empfang statt.&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Beispiel: &amp;lt;code&amp;gt;attr teleBot pollingTimeout 120&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Der TelegramBot kann erst dann Nachrichten an einen telegram user schicken, wenn dieser zuerst an den telegram bot eine Nachricht gesendet hat.&#039;&#039;&#039;&lt;br /&gt;
Dafür muss man in seinem Telegram-Client den Kontakt @botName suchen und dann eine Nachricht darn versenden.&lt;br /&gt;
&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=TelegramBot setzt eine aktuelle Version von FHEM voraus, insbesondere Versionen weit vor der Umstellung auf 5.7 (also vor Herbst 2015) können mit einem TelegramBot-Modul nicht funktionieren, da insbesondere das HTTPSRV-Modul dann veraltet ist. Am besten auch den TelegramBot über den offiziellen Update mit dem Rest von FHEM installieren/aktualisieren.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Registrierung eines neuen Bot ==&lt;br /&gt;
Zur Registrierung wird ein funktionierender Telegram-Client (egal ob Web, App oder Programm)benötigt. Hier wird ein Chat zum BotFather gestartet und der Befehl /newbot gesendet. Nun fragt der BotFather die benötigten Angaben ab und liefert am Ende des Dialogs die Informationen für den neuen Bot.&lt;br /&gt;
Hier ein Beispiel, wie so ein Chat aussehen könnte:&lt;br /&gt;
&amp;lt;pre&amp;gt;Client: &lt;br /&gt;
/newbot&lt;br /&gt;
----------------&lt;br /&gt;
BotFather:&lt;br /&gt;
Alright, a new bot. How are we going to call it? Please choose a name for your bot. &lt;br /&gt;
----------------&lt;br /&gt;
Client: &lt;br /&gt;
Mein Name &lt;br /&gt;
----------------&lt;br /&gt;
BotFather: &lt;br /&gt;
Good. Now let&#039;s choose a username for your bot. It must end in `bot`. Like this, for example: TetrisBot or tetris_bot.&lt;br /&gt;
----------------&lt;br /&gt;
Client: &lt;br /&gt;
fhem_bot&lt;br /&gt;
----------------&lt;br /&gt;
BotFather: &lt;br /&gt;
Sorry, this username is already taken. Think of something different.&lt;br /&gt;
----------------&lt;br /&gt;
Client: &lt;br /&gt;
fhem1234_bot&lt;br /&gt;
----------------&lt;br /&gt;
BotFather: &lt;br /&gt;
Done! Congratulations on your new bot.&lt;br /&gt;
You will find it at telegram.me/fhem1234_bot.&lt;br /&gt;
You can now add a description, about section and profile picture for your bot, see /help for a list of commands.&lt;br /&gt;
----------------&lt;br /&gt;
Use this token to access the HTTP API:&lt;br /&gt;
1234567890:AbCdefgHIJklmnOPQRst-uvwxyz &lt;br /&gt;
&lt;br /&gt;
For a description of the Bot API, see this page: https://core.telegram.org/bots/api &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Um einen Chat an einen &amp;quot;Contact&amp;quot; versenden zu können, muss zuerst in Contacts (bei Readings) ein Kontakt auftauchen. Wenn man sich zum allerersten Mal bei Telegram angemeldet hat, gibt es noch keinen Chat mit irgendjemanden. Man muss sich zuerstmal selbst eine Nachricht im Smartphone zusenden, dann taucht unter Readings der Eintrag Contacts auf. Erst dann kann man eine Nachricht mit @msgPeerId (das ist Ziffernfolge des Contacts ) oder mit @msgPeer (das ist der Name nach dem Doppelpunkt) vom TelegramBot auf sein Smartphone senden.&lt;br /&gt;
&lt;br /&gt;
== Tipps ==&lt;br /&gt;
&lt;br /&gt;
=== Privacyeinstellungen ===&lt;br /&gt;
&lt;br /&gt;
Damit der TelegramBot auch Meldungen in Gruppen sieht, müssen über den BotFather die Privacy-Einstellungen geändert werden.&amp;lt;br&amp;gt;Beispielchat:&amp;lt;pre&amp;gt;Client:&lt;br /&gt;
/setprivacy&lt;br /&gt;
----------------&lt;br /&gt;
BotFather:&lt;br /&gt;
Choose a bot to change group messages settings.&lt;br /&gt;
----------------&lt;br /&gt;
Client:&lt;br /&gt;
@fhem1234_bot&lt;br /&gt;
----------------&lt;br /&gt;
BotFather:&lt;br /&gt;
&#039;Enable&#039; - your bot will only receive messages that either start with the &#039;/&#039; symbol or mention the bot by username.&lt;br /&gt;
&#039;Disable&#039; - your bot will receive all messages that people send to groups.&lt;br /&gt;
Current status is: ENABLED&lt;br /&gt;
----------------&lt;br /&gt;
Client:&lt;br /&gt;
Disable&lt;br /&gt;
----------------&lt;br /&gt;
BotFather:&lt;br /&gt;
Success! The new status is: DISABLED. /help&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Kontakte ===&lt;br /&gt;
&lt;br /&gt;
Der Bot merkt sich die bereits bekannten Kontakte im Reading &amp;lt;code&amp;gt;Contacts&amp;lt;/code&amp;gt;. Dabei werden die einzelnen Kontakte jeweils als 3-teilige Einträge bestehend aus UserID, Vor- und Nachname des Benutzers (mit _ verbunden) und dem Username (mit vorangestelltem @). &lt;br /&gt;
&lt;br /&gt;
Beispiel: &amp;lt;code&amp;gt;123456:Ralf_Mustermann:@ralf&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verschiedene Einträge werden durch Leerzeichen getrennt.&lt;br /&gt;
&lt;br /&gt;
Man kann die Kontakte auch manuell überschreiben (z.B. wenn das Reading fehlerhaft oder verloren sein sollte). Dazu gibt es den Set-Befehl &amp;lt;code&amp;gt;replaceContacts&amp;lt;/code&amp;gt;. Dieser nimmt die Kontakte ebenfalls in der gleichen Form wie oben beschrieben entgegen.&lt;br /&gt;
&lt;br /&gt;
Die Kontaktliste wird ansonsten nur durch den Empfang von Nachrichten erweitert, da es im TelegramBot-API keine Möglichkeit gibt Kontaktdaten von Telegram abzufragen (siehe auch pollingTimeout)&lt;br /&gt;
&lt;br /&gt;
=== Reset ===&lt;br /&gt;
&lt;br /&gt;
Es ist möglich den Bot im laufenden Betrieb zurückzusetzen (Set-Befehl &amp;lt;code&amp;gt;reset&amp;lt;/code&amp;gt;). Dabei werden noch nicht abgeschlossene Übetragungen entfernt und die internen Zustände des Devices zurückgesetzt. &lt;br /&gt;
&lt;br /&gt;
=== Gruppen ===&lt;br /&gt;
&lt;br /&gt;
Um eine Nachricht von FHEM an eine Gruppe zu senden, muss der BOT in die Gruppe aufgenommen werden. Nach dem Senden einer Nachricht an die Gruppe kann im Modul die Gruppen-ID ermittelt werden und zum Senden von Nachrichten verwendet werden. Die Gruppen-ID ist eine negative Zahl. Wenn die Privacy-Einstellungen nicht auf &#039;Disabled&#039; gesetzt wurden, muss die Nachricht mit einem Slash (/) beginnen.&lt;br /&gt;
&lt;br /&gt;
==== Supergroups / Supergruppen ====&lt;br /&gt;
&lt;br /&gt;
Auch die neuen Supergruppen werden mit dem Bot unterstützt, es ist allerdings zu beachten, dass bei der Umwandlung einer Gruppe in eine Supergruppe eine neue ID in den Kontakten von Telegram vergeben wird. Wenn man also wie empfohlen IDs zur Identifikation von Benutzern einsetzt, muss entsprechend angepasst werden.&lt;br /&gt;
&lt;br /&gt;
== Beispielszenarien ==&lt;br /&gt;
&lt;br /&gt;
=== Benachrichtigungen über Ereignisse ===&lt;br /&gt;
&lt;br /&gt;
Das einfachste Szenario für die Integration von Messaging-Diensten mit FHEM ist zur Benachrichtigung über Ereignisse. Diese Funktion kann zum Beispiel verwendet werden, um über einen erfolgten Neustart von FHEM zu informieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;define notify_fhem_reload notify global:INITIALIZED set telebotdevice message fhem newly started - just now !&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In diesem Beispiel wird der Nachrichtentext &amp;quot;fhem newly started - just now !&amp;quot; an den als default eingestellten Kontakt (Attribut: defaultPeer) gesendet, sobald FHEM neu gestartet wurde. Natürlich kann man auch beliebige andere Benachrichtigungen einführen.&lt;br /&gt;
&lt;br /&gt;
=== Versand von Bildern ===&lt;br /&gt;
&lt;br /&gt;
Es ist auch möglich Bilder auf dem FHEM-Server, die zum Beispiel von einer Kamera oder einem Wettermodul stammen über Telegram zu versenden. So wäre es z.B. möglich jeweils morgens die aktuelle Wetterkarte zu erhalten.&lt;br /&gt;
&lt;br /&gt;
ACHTUNG: TelegramBot verwendet das HTTPUtils-Modul zur Kommunikation mit dem TelegramBot-API. Erst mit der Version, die seit 22.10.2015 &lt;br /&gt;
([r9576] HttpUtils.pm: Async write for POST Requests {{Link2Forum|Topic=41583|LinkText=FHEM-Forum}}) verteilt wird, erlaubt auch den Transfer grösserer Bilder. Die Grenze liegt ansonsten bei ca. 14kb auf Raspberries (Plattformspezifische Grenze).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;define notify_fhem_reload notify wetter:report set telebotdevice sendImage /opt/fhem/wetter.jpg&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei Erreichen des entsprechenden Status am Wetter-Modul wird ein Image über Telegram versendet. Hier sind lokale Pfade (relativ zu fhem) oder absolute Pfade wie oben möglich.&lt;br /&gt;
&lt;br /&gt;
=== Versand von SVG-Plots ===&lt;br /&gt;
&lt;br /&gt;
SVG-Plots können mit dem Befehl &lt;br /&gt;
&amp;lt;code&amp;gt;cmdSend [ @&amp;lt;peer1&amp;gt; ... @&amp;lt;peerN&amp;gt; ] &amp;lt;fhem command&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
verschickt werden.&lt;br /&gt;
&lt;br /&gt;
Das angegebene FHEM-Kommando wird ausgeführt und das Ergebnis an die angegebenen Peers bzw. den Standard-Peer verschickt.&lt;br /&gt;
&lt;br /&gt;
Mit dem folgenden Befehl wird der SVG-Plot SVG_FileLog_Aussen an den Standard-Peer geschickt: &lt;br /&gt;
&amp;lt;code&amp;gt;set mein_telegramBot cmdSend { plotAsPng(&#039;SVG_FileLog_Aussen&#039;) }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nach &amp;lt;code&amp;gt;define cmd_sendTelegramSVG cmdalias TGSVG .* AS set mein_telegramBot cmdSend { plotAsPng(&amp;quot;$EVENT&amp;quot;) }&amp;lt;/code&amp;gt;&lt;br /&gt;
kann man mit einem kurzen &lt;br /&gt;
&amp;lt;code&amp;gt;TGSVG SVG_Garten&amp;lt;/code&amp;gt;&lt;br /&gt;
ein beliebiges SVG über die Kommandozeile per Telegram versenden.&lt;br /&gt;
&lt;br /&gt;
Um das SVG nun noch mit einem Text zu versehen, muss eine Textnachricht dazu gesendet werden, was sich am einfachsten durch das Ausführen eines FHEM-Befehls auf Perl-Ebene realisieren lässt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;{fhem &amp;quot;set mein_telegramBot message Bildbeschreibung;; set mein_telegramBot cmdSend { plotAsPng(&#039;mein_SVG&#039;) }&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; früher wurde zum Verschicken von Plots auch die interne Funktion TelegramBot_ExecuteCommand verwendet; mit dem Update Ende Februar 2017 hat diese Funktion einen zusätzlichen Parameter erhalten und lautet nun &lt;br /&gt;
&amp;lt;code&amp;gt;TelegramBot_ExecuteCommand($defs{&amp;quot;mein_telegramBot&amp;quot;}, meine_ZielID, undef, &#039;{plotAsPng(&amp;quot;mein_SVG&amp;quot;)}&#039;);&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Voraussetzungen für den Versand von SVG-Plots ====&lt;br /&gt;
Es muss das Modul libimage-librsvg-perl installiert sein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install libimage-librsvg-perl&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Evtl. sind weitere Module erforderlich:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;sudo apt-get install libgd-graph-perl&lt;br /&gt;
&lt;br /&gt;
sudo apt-get install libgd-text-perl&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Empfang von Bildern oder ähnlichem ===&lt;br /&gt;
&lt;br /&gt;
Beim Empfang von Bildern wird zuerst nur eine ID vom Telegram-Server empfangen, diese befindet sich im Reading &amp;lt;code&amp;gt;msgFileId&amp;lt;/code&amp;gt; angelegt (&amp;lt;code&amp;gt;123456:xxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxx&amp;lt;/code&amp;gt;) und im Reading &amp;lt;code&amp;gt;msgText&amp;lt;/code&amp;gt; steht dann so etwas wie&lt;br /&gt;
&amp;lt;code&amp;gt;received photo # Size: 107701&amp;lt;/code&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
Über das Get-Kommando &amp;lt;code&amp;gt;urlForFile&amp;lt;/code&amp;gt; mit der ID aus dem msgFileId Reading lässt sich dann daraus ein URL ableiten, der dann zur eigentlichen Datei führt: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;https://api.telegram.org/file/bot123456:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/photo/file_25.jpg&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Versand von Emojis (Smileys) ===&lt;br /&gt;
&lt;br /&gt;
Es ist auch möglich Emojis mit den (Text-)Nachrichten zu versenden. Die entsprechenden (Unicode-)Zeichen werden einfach direkt mit in den Text der Nachricht aufgenommen. Um das zu vereinfachen kann man das einfach per Copy und Paste von dieser Seite &lt;br /&gt;
&lt;br /&gt;
http://apps.timwhitlock.info/emoji/tables/unicode (Spalte &amp;quot;Native&amp;quot;) &lt;br /&gt;
&lt;br /&gt;
übernehmen und mit der Nachricht verschicken. &lt;br /&gt;
&lt;br /&gt;
Die Emojis können auch empfangen werden und werden so auch in FHEM / FHEMWeb angezeigt. Plattformspezifische (z.B. von iOS oder Android) Emojis werden dabei nicht unterstützt (gerade mit iOS sind viele neue farbige Emojis hinzugekommen, die wohl leider nur auf Apple-devices funktionieren).&lt;br /&gt;
&lt;br /&gt;
=== Kommandos auslösen ===&lt;br /&gt;
&lt;br /&gt;
Ein wichtiges Szenario ist die Möglichkeit Kommandos in FHEM ausführen zu können, ohne einen Zugang durch die Firewall einrichten zu müssen. Dazu ist die Definition eines Schlüsselwortes (Attribut: &amp;quot;cmdKeyword&amp;quot;)erforderlich, mit dem man die Nachrichten beginnen muss, damit der TelegramBot die Kommandos erkennt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr telebotdevice cmdKeyword doit&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Somit kann man dann durch Nachrichten die mit &amp;quot;doit&amp;quot; beginnen Kommandos an FHEM senden, die ähnlich wie im Kommandoeingabefeld von FHEMweb dann von FHEM ausgeführt werden. Das Ergebnis der Ausführung wird zurück an den Sender (und an den definierten defaultPeer) geschickt.&lt;br /&gt;
&lt;br /&gt;
Somit können nicht nur Aktionen angestossen werden, sondern auch Infos abgefragt werden.&lt;br /&gt;
&lt;br /&gt;
Beispiele&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;doit set schalter on&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;doit list telegrambot&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Randnotiz|RNTyp=Warn|RNText=Achtung: Bei den Kommandos sollten man unbedingt das Attribut &amp;quot;cmdRestrictedPeer&amp;quot; setzen, damit nicht jeder Kommandos auf dem FHEM-Server ausführen kann. Dazu sollten die BenutzerIDs der erlaubten Benutzer (durch Leerzeichen getrennt angeben). Da Benutzernamen selber vergeben werden und nicht unbedingt eindeutig sind, sollten hier auch NUR BenutzerIDs verwendet werden.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==== Favoriten für Kommandos anlegen ====&lt;br /&gt;
&lt;br /&gt;
Grundidee bei den Favoriten ist, dass man lange Befehle, die man häufig braucht auf &amp;quot;Kurzwahl&amp;quot; legt.&lt;br /&gt;
&lt;br /&gt;
Beispiel-Kommandos wie z.B. &amp;lt;code&amp;gt;set TYPE=ROLLADEN pos 100&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;set TYPE=ROLLADEN pos 0&amp;lt;/code&amp;gt;, die man immer wieder braucht. Um nicht jedes mal dieses Kommando eintippen zu müssen auf dem Smartphone, kann man auch dafür Favoriten anlegen.&lt;br /&gt;
&lt;br /&gt;
Dazu gibt man erst mal die beiden Kommandos getrennt durch Semikolon im Attribut favorites an:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr telegrambotdevice favorites set TYPE=ROLLADEN pos 100;set TYPE=ROLLADEN pos 0&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die Favorites jetzt ausführen zu können braucht man noch ein Schlüsselwort dafür.&lt;br /&gt;
Nehmen wir mal an man möchte die Favoriten mit &amp;lt;code&amp;gt;/short&amp;lt;/code&amp;gt; ausführen können. Dazu muss dann das Attribut &amp;quot;cmdFavorites&amp;quot; setzen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr telegrambotdevice cmdFavorites /short&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man nun im Telegram Client &lt;br /&gt;
&amp;lt;code&amp;gt;/short 1&amp;lt;/code&amp;gt; an den Bot schickt führt der Bot den ersten Favoriten aus und das Ergebnis der Ausführung wird zurückgeschickt.&lt;br /&gt;
&lt;br /&gt;
Ausserdem kann man im Telegram Client &lt;br /&gt;
&amp;lt;code&amp;gt;/short&amp;lt;/code&amp;gt; an den Bot schicken, dann antwortet der Bot mit&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Favorites&lt;br /&gt;
&lt;br /&gt;
/short1 = set TYPE=ROLLADEN pos 100&lt;br /&gt;
&lt;br /&gt;
/short2 = set TYPE=ROLLADEN pos 0&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Antworten werden als Schaltflächen dargestellt (Telegram inline Keyboard) und können am Mobile-Client direkt angeklickt werden um sie auszuführen.&lt;br /&gt;
Um die Beschriftung der Schaltflächen zu optimieren, können die Befehle im Attribut favorites mit Beschreibungen versehen werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;/[Rollaeden zu ]=set TYPE=ROLLADEN pos 100;/[Rollaeden auf]=set TYPE=ROLLADEN pos 0&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun antwortet der Bot auf das Schlüsselwort für die Favoriten mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Favorites&lt;br /&gt;
&lt;br /&gt;
/short1 = Rollaeden zu&lt;br /&gt;
&lt;br /&gt;
/short2 = Rollaeden auf&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Github Repository für die Telegram-FHEM Entwicklung: https://github.com/viegener/Telegram-fhem&lt;br /&gt;
* Infos zum Telegram BotFather: https://core.telegram.org/bots#botfather&lt;br /&gt;
&lt;br /&gt;
* Source code für das 50_TelegramBot.pm-Modul: https://github.com/viegener/Telegram-fhem/blob/master/50_TelegramBot.pm&lt;br /&gt;
&lt;br /&gt;
* Forum-Thread in dem das Modul vorgestellt wurde {{Link2Forum|Topic=38328|LinkText=FHEM-Forum}}&lt;br /&gt;
* Telegram messaging system https://telegram.org/&lt;br /&gt;
* TelegramBot API https://core.telegram.org/bots/api&lt;/div&gt;</summary>
		<author><name>Dora71</name></author>
	</entry>
</feed>