<?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=Stephan</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=Stephan"/>
	<link rel="alternate" type="text/html" href="http://wiki.fhem.de/wiki/Spezial:Beitr%C3%A4ge/Stephan"/>
	<updated>2026-04-12T14:53:53Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Victron&amp;diff=40054</id>
		<title>Victron</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Victron&amp;diff=40054"/>
		<updated>2025-03-10T10:34:22Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Multiplus II */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Übersicht==&lt;br /&gt;
Die Familie der Solarlade und -speichergeräte von Victron verwenden verschiedene Bussysteme, um untereinander zu kommunizieren. Sehr angenehm ist, daß die zentrale Steuerung namens Venus (Victron ENergy Unix System :-) frei verfügbar ist, und gern auf einem RaspberryPi läuft. Integriert ist gleich ein [https://de.wikipedia.org/wiki/MQTT MQTT] Server (mit und ohne SSL), der die Meßwerte der angeschlossenen Geräte - und das sind viele - per MQTT anbietet.&lt;br /&gt;
&lt;br /&gt;
Victron ist generell gut dokumentiert: https://www.victronenergy.com/support-and-downloads/technical-information&lt;br /&gt;
&lt;br /&gt;
==Einbindung in fhem als MQTT2_DEVICE==&lt;br /&gt;
Über MQTT2_CLIENT wird zunächst die Verbindung zum Broker auf dem Victron Venus Gerät angelegt. (Hier ist die IP noch anzupasssen.)&lt;br /&gt;
 defmod venus MQTT2_CLIENT 10.1.x.x:1883&lt;br /&gt;
 attr venus autocreate complex&lt;br /&gt;
 attr venus clientOrder MQTT_GENERIC_BRIDGE MQTT2_DEVICE&lt;br /&gt;
 attr venus room MQTT2_DEVICE&lt;br /&gt;
 attr venus verbose 4&lt;br /&gt;
&lt;br /&gt;
Das Wechselrichter wird dann als MQTT2_DEVICE angelegt: &lt;br /&gt;
 defmod MQTT2_mp2 MQTT2_DEVICE venus&lt;br /&gt;
 attr MQTT2_mp2 IODev venus&lt;br /&gt;
&lt;br /&gt;
===== Aufbau der Topics von Victron =====&lt;br /&gt;
Jede Installation &#039;&#039;Venus&#039;&#039; gibt sich eine vrm_ID, die initial aus der Hardwareadresse abgeleitet wird. Über diese ID wird die Installation auf dem [https:///vrm.victronenergy.com Victron VRM Portal] erkannt. In den Beispielen unten taucht sie als &amp;lt;vrm_ID&amp;gt; auf. Die einzelnen Geräte einer Installation erhalten Nummern, die eine Unterscheidung auch mehrerer Wechselrichter (etwa im 3-Phasenbetrieb) oder mehrerer Solarlader ermöglichen. &lt;br /&gt;
&lt;br /&gt;
Nach Verbindung erscheint eine umfangreiche readingList, aus der hier nur einige Elemente verwendet werden. &lt;br /&gt;
* system - Konfiguration des Gesamtsystems bzw. konsolidierte Werte&lt;br /&gt;
* solarcharger - MPPT-DC-Lader, Anschluß VE-Direct (seriell, baut auf RS-485) , Wartung per Bluetooth und App&lt;br /&gt;
* grid - [https://www.victronenergy.com/accessories/energy-meter Stromzähler] , Anschluß über RS-485 / wahlweise Ethernet &lt;br /&gt;
* vebus - Wechselrichter, Anschluß über Cat5, serielles Protokoll&lt;br /&gt;
&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/system/0/Batteries:.* { json2nameValue($EVENT, &#039;Batteries_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/solarcharger/290/Yield/Power:.* { json2nameValue($EVENT, &#039;PVPower_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/grid/30/Ac/Power:.* { json2nameValue($EVENT, &#039;GridPower_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/vebus/288/Ac/Out/P:.* { json2nameValue($EVENT, &#039;P_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/system/0/SystemState/State:.* { json2nameValue($EVENT, &#039;State_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode:.* { json2nameValue($EVENT, &#039;Mode_&#039;, $JSONMAP) }&lt;br /&gt;
&lt;br /&gt;
===== Topics zum Schreiben =====&lt;br /&gt;
Einige Enstellungen sind schreibbar. Die entsprechenden Topics beginnen mit einem W. Eine setList kann beispielsweise so aussehen und erlaubt dann &lt;br /&gt;
* An- und Abschalten&lt;br /&gt;
* Inverter- und Charger Mode&lt;br /&gt;
* Mode: Betriebszustand&lt;br /&gt;
* CurrentLimit: max. Strom aus/in das Netz&lt;br /&gt;
* MinimumSocLimit: Entladegranze im ESS (Speicher-)Betrieb, solange das Netz anliegt&lt;br /&gt;
* Relay[0|1]: Schalte interne Relais&lt;br /&gt;
Eine einfache Setlist:&lt;br /&gt;
&lt;br /&gt;
 off W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:4}&lt;br /&gt;
 on W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:3}&lt;br /&gt;
 inv W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:2}&lt;br /&gt;
 chg W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:1}&lt;br /&gt;
 Mode W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 GridCurrentLimit W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Ac/In/1/CurrentLimit {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 MinSOC W/&amp;lt;vrm_ID&amp;gt;/settings/0/Settings/CGwacs/BatteryLife/MinimumSocLimit {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 Relay0 W/&amp;lt;vrm_ID&amp;gt;/system/0/Relay/0/State:.* {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 Relay1 W/&amp;lt;vrm_ID&amp;gt;/system/0/Relay/1/State:.* {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einbindung über ModbusAttr== &lt;br /&gt;
Die verschiedenen Devices werden intern über verschieden Modbusadressen abgefragt. &lt;br /&gt;
Dies kann man dem Excel &amp;quot;Modbus-TCP Register List unter Link 1 entnehmen. &lt;br /&gt;
&lt;br /&gt;
Hier ist der Code für ein MultiPlus II-5000 Device (die Unit ID 227 verweist auf den VE.Bus-Port an ttyS4, an welchem ausschließlich jener Multiplus angeschlossen ist). Leider kann ich es mangels Geräten nicht testen, aber ich vermute, dass wenn der Multiplus an einen anderen Port angeschlossen wird, lediglich die Modbus-ID zu ändern ist.&lt;br /&gt;
&lt;br /&gt;
=== Multiplus II ===&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
defmod MultiPlusII_227 ModbusAttr 227 5 192.168.243.38:502 TCP&lt;br /&gt;
attr MultiPlusII_227 dev-h-defPoll 1&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-reading AC_Input_Voltage_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-reading AC_Input_Voltage_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-reading AC_Input_Voltage_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-reading AC_Input_Current_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-reading AC_Input_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-reading AC_Input_Current_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-reading AC_Input_Frequency_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-reading AC_Input_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-reading AC_Input_Frequency_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-reading AC_Input_Power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-reading AC_Input_Power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-reading AC_Input_Power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-reading AC_Output_Voltage_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-reading AC_Output_Voltage_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-reading AC_Output_Voltage_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-reading AC_Output_Current_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-reading AC_Output_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-reading AC_Output_Current_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-reading AC_Output_Frequency&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-reading Active_Input_current_Limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-reading AC_Output_Power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-reading AC_Output_Power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-reading AC_Output_Power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-reading Battery_Voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-reading Battery_Current&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0028-reading Phase_Count&lt;br /&gt;
attr MultiPlusII_227 obj-h0028-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-map 0:AC Input 1,1:AC Input 2,240:Disconnected&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-reading Active_Input&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-reading vebus_soc&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-map 0:Off,1:Low Power,2:Fault,3:Bulk,4:Absorption,5:Float,6:Storage,7:Equalize,8:Passthru,9:Inverting,10:Power assist,11:Power supply,244:Sustain,252:External controL&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-reading vebus_state&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-map 0:No error,1:VE.Bus Error Device is switched off because one of the other phases in the system has switched off,2:VE.Bus Error New and old types MK2 are mixed in the system,3:VE.Bus Error Not all- or more than- the expected devices were found in the system,4:VE.Bus Error No other device whatsoever detected,5:VE.Bus Error Overvoltage on AC-out,6:VE.Bus Error  Error in DDC Program,7:VE.Bus BMS connected- which requires an Assistant- but no assistant found,10:VE.Bus Error System time synchronisation problem occurred,14:VE.Bus Error Device cannot transmit data,16:VE.Bus Error Dongle missing,17:VE.Bus Error One of the devices assumed master status because the original master failed,18:VE.Bus Error AC Overvoltage on the output of a slave has occurred while already switched off,22:VE.Bus Error This device cannot function as slave,24:VE.Bus Error Switch-over system protection initiated,25:VE.Bus Error Firmware incompatibility. The firmware of one of the connected device is not sufficiently up to date to operate in conjunction with this device,26:VE.Bus Error Internal error&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-reading vebus_error&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-map 1:Charger Only,2:Inverter Only,3:Charger and Inverter,4:Off&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-reading Device_Status&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-set 1&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-reading temperature_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-reading low_battery_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-reading overload_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0037-reading ESS_Power_setpoint_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0037-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-map 0:Charge allowed,1:Charge disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-reading ESS_disable_charge_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-map 0:Feed in allowed,1:Feed in disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-reading ESS_disable_feedback_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0040-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0040-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0041-reading ESS_Power_setpoint_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0041-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-reading temperature_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-reading voltage_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-reading temperature_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-reading low_battery_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-reading overload_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-reading ripple_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-reading temperature_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-reading low_battery_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-reading overload_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-reading ripple_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-reading temperature_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-reading low_battery_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-reading overload_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-reading ripple_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-map 0:PV enabled,1:PV disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-reading disable_pv_inverter&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-reading bms_allow_charge&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-reading bms_allow_discharge&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-reading bms_expected&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-reading bms_error&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-reading temperature_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-map 1:VE.Bus reset&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-reading vebus_reset&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-map 0:Ok,1:Warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-reading phase_rotation_warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-map 0:Ok,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-reading grid_lost_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-map 0:Feed in overvoltage,1:Do not feed in overvoltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-reading feed_dc_overvoltage_into_grid&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-reading maximum_overvoltage_feedin_power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-reading maximum_overvoltage_feedin_power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-reading maximum_overvoltage_feedin_power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-map 0:AC input not ignored,1:AC input ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-reading ac_input_1_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-map 0:AC input not ignored,1:AC input ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-reading ac_input_2_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-map 0:AcPowerSetpoint interpreted normally, 1:AcPowerSetpoint is OvervoltageFeedIn limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-reading ac_power_setpoint_acts_as_feedin_limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-map 0:OvervoltageFeedIn uses 1V offset, 1:OvervoltageFeedIn uses 0.1V offset&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-reading solar_offset_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-map 0:Sustain inactive, 1:Sustain active&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-reading sustain_active&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-reading energy_from_acIn1_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-reading energy_from_acIn1_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-reading energy_from_acIn2_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-reading energy_from_acIn2_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-reading energy_from_acOut_to_acIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-reading energy_from_acOut_to_acIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-reading energy_from_battery_to_AcIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-reading energy_from_battery_to_AcIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-reading energy_from_battery_to_AcOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-reading energy_from_AcOut_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-map 0:OK,1:Warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-reading low_cell_voltage_imminent&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-map 0:Initialising,1:Bulk,2:Absorption,3:Float,4:Storage,5:Absorb repeat,6:Forced absorb,7:Equalise,8:Bulk stopped,9:Unknown&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-reading charge_state&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0096-reading ESS_Power_setpoint_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0096-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-map 0:Renewable energy not preferred,1:Renewable energy preferred&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-reading prefer_renewable_energy&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-map 0:Generator not selected,1:Generator selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-reading select_remote_generator&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-map 0:Generator not selected,1:Generator selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-reading remote_generator_selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-map 0:No Action, 1:Redetect System&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-reading redetect_vebus_system&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-reading powerAssist_boost_factor&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-reading configured_output_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-map 0:disabled, 1:enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-reading powerAssist_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-map 0:disabled, 1:enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-reading ups_function_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-unpack n&lt;br /&gt;
attr MultiPlusII_227 room Solar-&amp;gt;Victron&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Cerbo GX mit Unit ID 100 ===&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
defmod Cerbo_100 ModbusAttr 100 5 192.168.243.38:502 TCP&lt;br /&gt;
attr Cerbo_100 dev-h-defPoll 1&lt;br /&gt;
attr Cerbo_100 obj-h0800-len 6&lt;br /&gt;
attr Cerbo_100 obj-h0800-reading system_serial&lt;br /&gt;
attr Cerbo_100 obj-h0800-unpack Z12&lt;br /&gt;
attr Cerbo_100 obj-h0806-map 0:Open,1:Closed&lt;br /&gt;
attr Cerbo_100 obj-h0806-reading CCGX_relay_state_1&lt;br /&gt;
attr Cerbo_100 obj-h0806-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0807-map 0:Open,1:Closed&lt;br /&gt;
attr Cerbo_100 obj-h0807-reading CCGX_relay_state_2&lt;br /&gt;
attr Cerbo_100 obj-h0807-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0808-reading PV_AC_Coupled_output_L1&lt;br /&gt;
attr Cerbo_100 obj-h0808-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0809-reading PV_AC_Coupled_output_L2&lt;br /&gt;
attr Cerbo_100 obj-h0809-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0810-reading PV_AC_Coupled_output_L3&lt;br /&gt;
attr Cerbo_100 obj-h0810-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0811-reading PV_AC_Coupled_input_L1&lt;br /&gt;
attr Cerbo_100 obj-h0811-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0812-reading PV_AC_Coupled_input_L2&lt;br /&gt;
attr Cerbo_100 obj-h0812-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0813-reading PV_AC_Coupled_input_L3&lt;br /&gt;
attr Cerbo_100 obj-h0813-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0814-reading PV_AC_Coupled_generator_L1&lt;br /&gt;
attr Cerbo_100 obj-h0814-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0815-reading PV_AC_Coupled_generator_L2&lt;br /&gt;
attr Cerbo_100 obj-h0815-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0816-reading PV_AC_Coupled_generator_L3&lt;br /&gt;
attr Cerbo_100 obj-h0816-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0817-reading AC_consumption_L1&lt;br /&gt;
attr Cerbo_100 obj-h0817-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0818-reading AC_consumption_L2&lt;br /&gt;
attr Cerbo_100 obj-h0818-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0819-reading AC_consumption_L3&lt;br /&gt;
attr Cerbo_100 obj-h0819-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0820-reading grid_L1&lt;br /&gt;
attr Cerbo_100 obj-h0820-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0821-reading grid_L2&lt;br /&gt;
attr Cerbo_100 obj-h0821-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0822-reading grid_L3&lt;br /&gt;
attr Cerbo_100 obj-h0822-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0823-reading genset_L1&lt;br /&gt;
attr Cerbo_100 obj-h0823-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0824-reading genset_L2&lt;br /&gt;
attr Cerbo_100 obj-h0824-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0825-reading genset_L3&lt;br /&gt;
attr Cerbo_100 obj-h0825-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0826-map 0:Unknown,1:Grid,2:Generator,3:Shore power,240:Not connected&lt;br /&gt;
attr Cerbo_100 obj-h0826-reading active_input_source&lt;br /&gt;
attr Cerbo_100 obj-h0826-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0830-len 4&lt;br /&gt;
attr Cerbo_100 obj-h0830-reading system_UTC_time&lt;br /&gt;
attr Cerbo_100 obj-h0830-unpack Q&lt;br /&gt;
attr Cerbo_100 obj-h0840-expr $val / 10&lt;br /&gt;
attr Cerbo_100 obj-h0840-reading battery_voltage_system&lt;br /&gt;
attr Cerbo_100 obj-h0840-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0841-expr $val / 10&lt;br /&gt;
attr Cerbo_100 obj-h0841-reading battery_current_system&lt;br /&gt;
attr Cerbo_100 obj-h0841-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0842-reading battery_power_system&lt;br /&gt;
attr Cerbo_100 obj-h0842-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0843-reading battery_soc_system&lt;br /&gt;
attr Cerbo_100 obj-h0843-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0844-map 0:idle,1:charging,2:discharging&lt;br /&gt;
attr Cerbo_100 obj-h0844-reading battery_state_system&lt;br /&gt;
attr Cerbo_100 obj-h0844-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0845-expr $val /-10&lt;br /&gt;
attr Cerbo_100 obj-h0845-reading battery_consumed_amphours_system&lt;br /&gt;
attr Cerbo_100 obj-h0845-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0846-expr $val * 100&lt;br /&gt;
attr Cerbo_100 obj-h0846-reading battery_timetogo_system&lt;br /&gt;
attr Cerbo_100 obj-h0846-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0850-reading PV_DC_Coupled_power&lt;br /&gt;
attr Cerbo_100 obj-h0850-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0851-expr $val /10&lt;br /&gt;
attr Cerbo_100 obj-h0851-reading PV_DC_Coupled_current&lt;br /&gt;
attr Cerbo_100 obj-h0851-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0855-reading charger_power&lt;br /&gt;
attr Cerbo_100 obj-h0855-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0860-reading dc_system_power&lt;br /&gt;
attr Cerbo_100 obj-h0860-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0865-expr $val /10&lt;br /&gt;
attr Cerbo_100 obj-h0865-reading vebus_charge_current_system&lt;br /&gt;
attr Cerbo_100 obj-h0865-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0866-reading vebus_charge_power_system&lt;br /&gt;
attr Cerbo_100 obj-h0866-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0867-reading RESERVED&lt;br /&gt;
attr Cerbo_100 obj-h0867-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0868-expr $val /10&lt;br /&gt;
attr Cerbo_100 obj-h0868-reading inverter_charger_current&lt;br /&gt;
attr Cerbo_100 obj-h0868-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0870-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0870-reading inverter_charger_power&lt;br /&gt;
attr Cerbo_100 obj-h0870-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0872-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0872-reading power_between_meter_and_charger_L1&lt;br /&gt;
attr Cerbo_100 obj-h0872-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0874-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0874-reading power_between_meter_and_charger_L2&lt;br /&gt;
attr Cerbo_100 obj-h0874-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0876-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0876-reading power_between_meter_and_charger_L3&lt;br /&gt;
attr Cerbo_100 obj-h0876-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0878-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0878-reading power_output_inverter_charger_L1&lt;br /&gt;
attr Cerbo_100 obj-h0878-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0880-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0880-reading power_output_inverter_charger_L2&lt;br /&gt;
attr Cerbo_100 obj-h0880-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0882-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0882-reading power_output_inverter_charger_L3&lt;br /&gt;
attr Cerbo_100 obj-h0882-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 room Solar-&amp;gt;Victron&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Visualisierung==&lt;br /&gt;
&#039;&#039;&#039;stateFormat&#039;&#039;&#039; kann so aussehen:&lt;br /&gt;
 SSOC Batteries_value_1_soc, &lt;br /&gt;
 SOC: Batteries_value_1_soc % (MinimumSocLimit_value), &lt;br /&gt;
 Bat: Batteries_value_1_power W,&lt;br /&gt;
 PV: PVPower_value W,&lt;br /&gt;
 ACin: GridPower_value W, &lt;br /&gt;
 ACout2: P_value W,&lt;br /&gt;
 State: State_value,&lt;br /&gt;
 Mode: Mode_value&lt;br /&gt;
SSOC wird später für das devStateIcon genutzt.&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;devStateIcon&#039;&#039;&#039; bietet einen ersten Blick auf den Ladestand:&lt;br /&gt;
 SSOC\s[2|3|4].+:measure_battery_25&lt;br /&gt;
 SSOC\s[5|6].+:measure_battery_50&lt;br /&gt;
 SSOC\s[7|8].+:measure_battery_75&lt;br /&gt;
 SSOC\s[9].+:measure_battery_100&lt;br /&gt;
&lt;br /&gt;
Derzeit existiert noch kein Template, aber das kann sich ja ändern.&lt;br /&gt;
&lt;br /&gt;
==Externe Links==&lt;br /&gt;
*[https://www.victronenergy.com/support-and-downloads/technical-information Victron Technical Info]&lt;br /&gt;
*[https://vrm-api-docs.victronenergy.com/#/ VRm API]&lt;br /&gt;
*[https://github.com/victronenergy/dbus-mqtt Venus MQTT Broker]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Victron&amp;diff=40053</id>
		<title>Victron</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Victron&amp;diff=40053"/>
		<updated>2025-03-10T10:30:35Z</updated>

		<summary type="html">&lt;p&gt;Stephan: Doppel-Paste gelöscht&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Übersicht==&lt;br /&gt;
Die Familie der Solarlade und -speichergeräte von Victron verwenden verschiedene Bussysteme, um untereinander zu kommunizieren. Sehr angenehm ist, daß die zentrale Steuerung namens Venus (Victron ENergy Unix System :-) frei verfügbar ist, und gern auf einem RaspberryPi läuft. Integriert ist gleich ein [https://de.wikipedia.org/wiki/MQTT MQTT] Server (mit und ohne SSL), der die Meßwerte der angeschlossenen Geräte - und das sind viele - per MQTT anbietet.&lt;br /&gt;
&lt;br /&gt;
Victron ist generell gut dokumentiert: https://www.victronenergy.com/support-and-downloads/technical-information&lt;br /&gt;
&lt;br /&gt;
==Einbindung in fhem als MQTT2_DEVICE==&lt;br /&gt;
Über MQTT2_CLIENT wird zunächst die Verbindung zum Broker auf dem Victron Venus Gerät angelegt. (Hier ist die IP noch anzupasssen.)&lt;br /&gt;
 defmod venus MQTT2_CLIENT 10.1.x.x:1883&lt;br /&gt;
 attr venus autocreate complex&lt;br /&gt;
 attr venus clientOrder MQTT_GENERIC_BRIDGE MQTT2_DEVICE&lt;br /&gt;
 attr venus room MQTT2_DEVICE&lt;br /&gt;
 attr venus verbose 4&lt;br /&gt;
&lt;br /&gt;
Das Wechselrichter wird dann als MQTT2_DEVICE angelegt: &lt;br /&gt;
 defmod MQTT2_mp2 MQTT2_DEVICE venus&lt;br /&gt;
 attr MQTT2_mp2 IODev venus&lt;br /&gt;
&lt;br /&gt;
===== Aufbau der Topics von Victron =====&lt;br /&gt;
Jede Installation &#039;&#039;Venus&#039;&#039; gibt sich eine vrm_ID, die initial aus der Hardwareadresse abgeleitet wird. Über diese ID wird die Installation auf dem [https:///vrm.victronenergy.com Victron VRM Portal] erkannt. In den Beispielen unten taucht sie als &amp;lt;vrm_ID&amp;gt; auf. Die einzelnen Geräte einer Installation erhalten Nummern, die eine Unterscheidung auch mehrerer Wechselrichter (etwa im 3-Phasenbetrieb) oder mehrerer Solarlader ermöglichen. &lt;br /&gt;
&lt;br /&gt;
Nach Verbindung erscheint eine umfangreiche readingList, aus der hier nur einige Elemente verwendet werden. &lt;br /&gt;
* system - Konfiguration des Gesamtsystems bzw. konsolidierte Werte&lt;br /&gt;
* solarcharger - MPPT-DC-Lader, Anschluß VE-Direct (seriell, baut auf RS-485) , Wartung per Bluetooth und App&lt;br /&gt;
* grid - [https://www.victronenergy.com/accessories/energy-meter Stromzähler] , Anschluß über RS-485 / wahlweise Ethernet &lt;br /&gt;
* vebus - Wechselrichter, Anschluß über Cat5, serielles Protokoll&lt;br /&gt;
&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/system/0/Batteries:.* { json2nameValue($EVENT, &#039;Batteries_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/solarcharger/290/Yield/Power:.* { json2nameValue($EVENT, &#039;PVPower_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/grid/30/Ac/Power:.* { json2nameValue($EVENT, &#039;GridPower_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/vebus/288/Ac/Out/P:.* { json2nameValue($EVENT, &#039;P_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/system/0/SystemState/State:.* { json2nameValue($EVENT, &#039;State_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode:.* { json2nameValue($EVENT, &#039;Mode_&#039;, $JSONMAP) }&lt;br /&gt;
&lt;br /&gt;
===== Topics zum Schreiben =====&lt;br /&gt;
Einige Enstellungen sind schreibbar. Die entsprechenden Topics beginnen mit einem W. Eine setList kann beispielsweise so aussehen und erlaubt dann &lt;br /&gt;
* An- und Abschalten&lt;br /&gt;
* Inverter- und Charger Mode&lt;br /&gt;
* Mode: Betriebszustand&lt;br /&gt;
* CurrentLimit: max. Strom aus/in das Netz&lt;br /&gt;
* MinimumSocLimit: Entladegranze im ESS (Speicher-)Betrieb, solange das Netz anliegt&lt;br /&gt;
* Relay[0|1]: Schalte interne Relais&lt;br /&gt;
Eine einfache Setlist:&lt;br /&gt;
&lt;br /&gt;
 off W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:4}&lt;br /&gt;
 on W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:3}&lt;br /&gt;
 inv W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:2}&lt;br /&gt;
 chg W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:1}&lt;br /&gt;
 Mode W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 GridCurrentLimit W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Ac/In/1/CurrentLimit {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 MinSOC W/&amp;lt;vrm_ID&amp;gt;/settings/0/Settings/CGwacs/BatteryLife/MinimumSocLimit {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 Relay0 W/&amp;lt;vrm_ID&amp;gt;/system/0/Relay/0/State:.* {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 Relay1 W/&amp;lt;vrm_ID&amp;gt;/system/0/Relay/1/State:.* {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einbindung über ModbusAttr== &lt;br /&gt;
Die verschiedenen Devices werden intern über verschieden Modbusadressen abgefragt. &lt;br /&gt;
Dies kann man dem Excel &amp;quot;Modbus-TCP Register List unter Link 1 entnehmen. &lt;br /&gt;
&lt;br /&gt;
Hier ist der Code für ein MultiPlus II-5000 Device (die Unit ID 227 verweist auf den VE.Bus-Port an ttyS4, an welchem ausschließlich jener Multiplus angeschlossen ist). Leider kann ich es mangels Geräten nicht testen, aber ich vermute, dass wenn der Multiplus an einen anderen Port angeschlossen wird, lediglich die Modbus-ID zu ändern ist.&lt;br /&gt;
&lt;br /&gt;
=== Multiplus II ===&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
defmod MultiPlusII_227 ModbusAttr 227 5 192.168.243.38:502 TCP&lt;br /&gt;
attr MultiPlusII_227 dev-h-defPoll 1&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-reading AC_Input_Voltage_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-reading AC_Input_Voltage_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-reading AC_Input_Voltage_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-reading AC_Input_Current_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-reading AC_Input_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-reading AC_Input_Current_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-reading AC_Input_Frequency_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-reading AC_Input_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-reading AC_Input_Frequency_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-reading AC_Input_Power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-reading AC_Input_Power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-reading AC_Input_Power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-reading AC_Output_Voltage_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-reading AC_Output_Voltage_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-reading AC_Output_Voltage_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-reading AC_Output_Current_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-reading AC_Output_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-reading AC_Output_Current_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-reading AC_Output_Frequency&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-reading Active_Input_current_Limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-reading AC_Output_Power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-reading AC_Output_Power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-reading AC_Output_Power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-reading Battery_Voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-reading Battery_Current&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0028-reading Phase_Count&lt;br /&gt;
attr MultiPlusII_227 obj-h0028-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-map 0:AC Input 1,1:AC Input 2,240:Disconnected&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-reading Active_Input&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-reading vebus_soc&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-map 0:Off,1:Low Power,2:Fault,3:Bulk,4:Absorption,5:Float,6:Storage,7:Equalize,8:Passthru,9:Inverting,10:Power assist,11:Power supply,244:Sustain,252:External controL&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-reading vebus_state&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-map 0:No error,1:VE.Bus Error Device is switched off because one of the other phases in the system has switched off,2:VE.Bus Error New and old types MK2 are mixed in the system,3:VE.Bus Error Not all- or more than- the expected devices were found in the system,4:VE.Bus Error No other device whatsoever detected,5:VE.Bus Error Overvoltage on AC-out,6:VE.Bus Error  Error in DDC Program,7:VE.Bus BMS connected- which requires an Assistant- but no assistant found,10:VE.Bus Error System time synchronisation problem occurred,14:VE.Bus Error Device cannot transmit data,16:VE.Bus Error Dongle missing,17:VE.Bus Error One of the devices assumed master status because the original master failed,18:VE.Bus Error AC Overvoltage on the output of a slave has occurred while already switched off,22:VE.Bus Error This device cannot function as slave,24:VE.Bus Error Switch-over system protection initiated,25:VE.Bus Error Firmware incompatibility. The firmware of one of the connected device is not sufficiently up to date to operate in conjunction with this device,26:VE.Bus Error Internal error&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-reading vebus_error&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-map 1:Charger Only,2:Inverter Only,3:On,4:Off&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-reading switch_position&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-reading temperature_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-reading low_battery_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-reading overload_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0037-reading ESS_Power_setpoint_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0037-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-map 0:Charge allowed,1:Charge disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-reading ESS_disable_charge_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-map 0:Feed in allowed,1:Feed in disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-reading ESS_disable_feedback_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0040-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0040-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0041-reading ESS_Power_setpoint_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0041-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-reading temperature_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-reading voltage_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-reading temperature_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-reading low_battery_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-reading overload_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-reading ripple_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-reading temperature_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-reading low_battery_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-reading overload_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-reading ripple_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-reading temperature_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-reading low_battery_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-reading overload_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-reading ripple_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-map 0:PV enabled,1:PV disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-reading disable_pv_inverter&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-reading bms_allow_charge&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-reading bms_allow_discharge&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-reading bms_expected&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-reading bms_error&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-reading temperature_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-map 1:VE.Bus reset&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-reading vebus_reset&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-map 0:Ok,1:Warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-reading phase_rotation_warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-map 0:Ok,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-reading grid_lost_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-map 0:Feed in overvoltage,1:Do not feed in overvoltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-reading feed_dc_overvoltage_into_grid&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-reading maximum_overvoltage_feedin_power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-reading maximum_overvoltage_feedin_power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-reading maximum_overvoltage_feedin_power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-map 0:AC input not ignored,1:AC input ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-reading ac_input_1_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-map 0:AC input not ignored,1:AC input ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-reading ac_input_2_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-map 0:AcPowerSetpoint interpreted normally, 1:AcPowerSetpoint is OvervoltageFeedIn limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-reading ac_power_setpoint_acts_as_feedin_limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-map 0:OvervoltageFeedIn uses 1V offset, 1:OvervoltageFeedIn uses 0.1V offset&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-reading solar_offset_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-map 0:Sustain inactive, 1:Sustain active&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-reading sustain_active&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-reading energy_from_acIn1_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-reading energy_from_acIn1_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-reading energy_from_acIn2_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-reading energy_from_acIn2_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-reading energy_from_acOut_to_acIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-reading energy_from_acOut_to_acIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-reading energy_from_battery_to_AcIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-reading energy_from_battery_to_AcIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-reading energy_from_battery_to_AcOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-reading energy_from_AcOut_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-map 0:OK,1:Warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-reading low_cell_voltage_imminent&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-map 0:Initialising,1:Bulk,2:Absorption,3:Float,4:Storage,5:Absorb repeat,6:Forced absorb,7:Equalise,8:Bulk stopped,9:Unknown&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-reading charge_state&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0096-reading ESS_Power_setpoint_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0096-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-map 0:Renewable energy not preferred,1:Renewable energy preferred&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-reading prefer_renewable_energy&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-map 0:Generator not selected,1:Generator selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-reading select_remote_generator&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-map 0:Generator not selected,1:Generator selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-reading remote_generator_selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-map 0:No Action, 1:Redetect System&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-reading redetect_vebus_system&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-reading powerAssist_boost_factor&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-reading configured_output_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-map 0:disabled, 1:enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-reading powerAssist_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-map 0:disabled, 1:enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-reading ups_function_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-unpack n&lt;br /&gt;
attr MultiPlusII_227 room Solar-&amp;gt;Victron&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Cerbo GX mit Unit ID 100 ===&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
defmod Cerbo_100 ModbusAttr 100 5 192.168.243.38:502 TCP&lt;br /&gt;
attr Cerbo_100 dev-h-defPoll 1&lt;br /&gt;
attr Cerbo_100 obj-h0800-len 6&lt;br /&gt;
attr Cerbo_100 obj-h0800-reading system_serial&lt;br /&gt;
attr Cerbo_100 obj-h0800-unpack Z12&lt;br /&gt;
attr Cerbo_100 obj-h0806-map 0:Open,1:Closed&lt;br /&gt;
attr Cerbo_100 obj-h0806-reading CCGX_relay_state_1&lt;br /&gt;
attr Cerbo_100 obj-h0806-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0807-map 0:Open,1:Closed&lt;br /&gt;
attr Cerbo_100 obj-h0807-reading CCGX_relay_state_2&lt;br /&gt;
attr Cerbo_100 obj-h0807-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0808-reading PV_AC_Coupled_output_L1&lt;br /&gt;
attr Cerbo_100 obj-h0808-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0809-reading PV_AC_Coupled_output_L2&lt;br /&gt;
attr Cerbo_100 obj-h0809-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0810-reading PV_AC_Coupled_output_L3&lt;br /&gt;
attr Cerbo_100 obj-h0810-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0811-reading PV_AC_Coupled_input_L1&lt;br /&gt;
attr Cerbo_100 obj-h0811-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0812-reading PV_AC_Coupled_input_L2&lt;br /&gt;
attr Cerbo_100 obj-h0812-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0813-reading PV_AC_Coupled_input_L3&lt;br /&gt;
attr Cerbo_100 obj-h0813-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0814-reading PV_AC_Coupled_generator_L1&lt;br /&gt;
attr Cerbo_100 obj-h0814-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0815-reading PV_AC_Coupled_generator_L2&lt;br /&gt;
attr Cerbo_100 obj-h0815-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0816-reading PV_AC_Coupled_generator_L3&lt;br /&gt;
attr Cerbo_100 obj-h0816-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0817-reading AC_consumption_L1&lt;br /&gt;
attr Cerbo_100 obj-h0817-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0818-reading AC_consumption_L2&lt;br /&gt;
attr Cerbo_100 obj-h0818-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0819-reading AC_consumption_L3&lt;br /&gt;
attr Cerbo_100 obj-h0819-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0820-reading grid_L1&lt;br /&gt;
attr Cerbo_100 obj-h0820-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0821-reading grid_L2&lt;br /&gt;
attr Cerbo_100 obj-h0821-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0822-reading grid_L3&lt;br /&gt;
attr Cerbo_100 obj-h0822-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0823-reading genset_L1&lt;br /&gt;
attr Cerbo_100 obj-h0823-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0824-reading genset_L2&lt;br /&gt;
attr Cerbo_100 obj-h0824-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0825-reading genset_L3&lt;br /&gt;
attr Cerbo_100 obj-h0825-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0826-map 0:Unknown,1:Grid,2:Generator,3:Shore power,240:Not connected&lt;br /&gt;
attr Cerbo_100 obj-h0826-reading active_input_source&lt;br /&gt;
attr Cerbo_100 obj-h0826-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0830-len 4&lt;br /&gt;
attr Cerbo_100 obj-h0830-reading system_UTC_time&lt;br /&gt;
attr Cerbo_100 obj-h0830-unpack Q&lt;br /&gt;
attr Cerbo_100 obj-h0840-expr $val / 10&lt;br /&gt;
attr Cerbo_100 obj-h0840-reading battery_voltage_system&lt;br /&gt;
attr Cerbo_100 obj-h0840-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0841-expr $val / 10&lt;br /&gt;
attr Cerbo_100 obj-h0841-reading battery_current_system&lt;br /&gt;
attr Cerbo_100 obj-h0841-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0842-reading battery_power_system&lt;br /&gt;
attr Cerbo_100 obj-h0842-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0843-reading battery_soc_system&lt;br /&gt;
attr Cerbo_100 obj-h0843-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0844-map 0:idle,1:charging,2:discharging&lt;br /&gt;
attr Cerbo_100 obj-h0844-reading battery_state_system&lt;br /&gt;
attr Cerbo_100 obj-h0844-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0845-expr $val /-10&lt;br /&gt;
attr Cerbo_100 obj-h0845-reading battery_consumed_amphours_system&lt;br /&gt;
attr Cerbo_100 obj-h0845-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0846-expr $val * 100&lt;br /&gt;
attr Cerbo_100 obj-h0846-reading battery_timetogo_system&lt;br /&gt;
attr Cerbo_100 obj-h0846-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0850-reading PV_DC_Coupled_power&lt;br /&gt;
attr Cerbo_100 obj-h0850-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0851-expr $val /10&lt;br /&gt;
attr Cerbo_100 obj-h0851-reading PV_DC_Coupled_current&lt;br /&gt;
attr Cerbo_100 obj-h0851-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0855-reading charger_power&lt;br /&gt;
attr Cerbo_100 obj-h0855-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0860-reading dc_system_power&lt;br /&gt;
attr Cerbo_100 obj-h0860-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0865-expr $val /10&lt;br /&gt;
attr Cerbo_100 obj-h0865-reading vebus_charge_current_system&lt;br /&gt;
attr Cerbo_100 obj-h0865-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0866-reading vebus_charge_power_system&lt;br /&gt;
attr Cerbo_100 obj-h0866-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0867-reading RESERVED&lt;br /&gt;
attr Cerbo_100 obj-h0867-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0868-expr $val /10&lt;br /&gt;
attr Cerbo_100 obj-h0868-reading inverter_charger_current&lt;br /&gt;
attr Cerbo_100 obj-h0868-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0870-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0870-reading inverter_charger_power&lt;br /&gt;
attr Cerbo_100 obj-h0870-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0872-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0872-reading power_between_meter_and_charger_L1&lt;br /&gt;
attr Cerbo_100 obj-h0872-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0874-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0874-reading power_between_meter_and_charger_L2&lt;br /&gt;
attr Cerbo_100 obj-h0874-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0876-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0876-reading power_between_meter_and_charger_L3&lt;br /&gt;
attr Cerbo_100 obj-h0876-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0878-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0878-reading power_output_inverter_charger_L1&lt;br /&gt;
attr Cerbo_100 obj-h0878-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0880-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0880-reading power_output_inverter_charger_L2&lt;br /&gt;
attr Cerbo_100 obj-h0880-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0882-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0882-reading power_output_inverter_charger_L3&lt;br /&gt;
attr Cerbo_100 obj-h0882-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 room Solar-&amp;gt;Victron&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Visualisierung==&lt;br /&gt;
&#039;&#039;&#039;stateFormat&#039;&#039;&#039; kann so aussehen:&lt;br /&gt;
 SSOC Batteries_value_1_soc, &lt;br /&gt;
 SOC: Batteries_value_1_soc % (MinimumSocLimit_value), &lt;br /&gt;
 Bat: Batteries_value_1_power W,&lt;br /&gt;
 PV: PVPower_value W,&lt;br /&gt;
 ACin: GridPower_value W, &lt;br /&gt;
 ACout2: P_value W,&lt;br /&gt;
 State: State_value,&lt;br /&gt;
 Mode: Mode_value&lt;br /&gt;
SSOC wird später für das devStateIcon genutzt.&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;devStateIcon&#039;&#039;&#039; bietet einen ersten Blick auf den Ladestand:&lt;br /&gt;
 SSOC\s[2|3|4].+:measure_battery_25&lt;br /&gt;
 SSOC\s[5|6].+:measure_battery_50&lt;br /&gt;
 SSOC\s[7|8].+:measure_battery_75&lt;br /&gt;
 SSOC\s[9].+:measure_battery_100&lt;br /&gt;
&lt;br /&gt;
Derzeit existiert noch kein Template, aber das kann sich ja ändern.&lt;br /&gt;
&lt;br /&gt;
==Externe Links==&lt;br /&gt;
*[https://www.victronenergy.com/support-and-downloads/technical-information Victron Technical Info]&lt;br /&gt;
*[https://vrm-api-docs.victronenergy.com/#/ VRm API]&lt;br /&gt;
*[https://github.com/victronenergy/dbus-mqtt Venus MQTT Broker]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Victron&amp;diff=40052</id>
		<title>Victron</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Victron&amp;diff=40052"/>
		<updated>2025-03-09T18:32:52Z</updated>

		<summary type="html">&lt;p&gt;Stephan: ModbusAttr für Cerbo GX hinzugefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Übersicht==&lt;br /&gt;
Die Familie der Solarlade und -speichergeräte von Victron verwenden verschiedene Bussysteme, um untereinander zu kommunizieren. Sehr angenehm ist, daß die zentrale Steuerung namens Venus (Victron ENergy Unix System :-) frei verfügbar ist, und gern auf einem RaspberryPi läuft. Integriert ist gleich ein [https://de.wikipedia.org/wiki/MQTT MQTT] Server (mit und ohne SSL), der die Meßwerte der angeschlossenen Geräte - und das sind viele - per MQTT anbietet.&lt;br /&gt;
&lt;br /&gt;
Victron ist generell gut dokumentiert: https://www.victronenergy.com/support-and-downloads/technical-information&lt;br /&gt;
&lt;br /&gt;
==Einbindung in fhem als MQTT2_DEVICE==&lt;br /&gt;
Über MQTT2_CLIENT wird zunächst die Verbindung zum Broker auf dem Victron Venus Gerät angelegt. (Hier ist die IP noch anzupasssen.)&lt;br /&gt;
 defmod venus MQTT2_CLIENT 10.1.x.x:1883&lt;br /&gt;
 attr venus autocreate complex&lt;br /&gt;
 attr venus clientOrder MQTT_GENERIC_BRIDGE MQTT2_DEVICE&lt;br /&gt;
 attr venus room MQTT2_DEVICE&lt;br /&gt;
 attr venus verbose 4&lt;br /&gt;
&lt;br /&gt;
Das Wechselrichter wird dann als MQTT2_DEVICE angelegt: &lt;br /&gt;
 defmod MQTT2_mp2 MQTT2_DEVICE venus&lt;br /&gt;
 attr MQTT2_mp2 IODev venus&lt;br /&gt;
&lt;br /&gt;
===== Aufbau der Topics von Victron =====&lt;br /&gt;
Jede Installation &#039;&#039;Venus&#039;&#039; gibt sich eine vrm_ID, die initial aus der Hardwareadresse abgeleitet wird. Über diese ID wird die Installation auf dem [https:///vrm.victronenergy.com Victron VRM Portal] erkannt. In den Beispielen unten taucht sie als &amp;lt;vrm_ID&amp;gt; auf. Die einzelnen Geräte einer Installation erhalten Nummern, die eine Unterscheidung auch mehrerer Wechselrichter (etwa im 3-Phasenbetrieb) oder mehrerer Solarlader ermöglichen. &lt;br /&gt;
&lt;br /&gt;
Nach Verbindung erscheint eine umfangreiche readingList, aus der hier nur einige Elemente verwendet werden. &lt;br /&gt;
* system - Konfiguration des Gesamtsystems bzw. konsolidierte Werte&lt;br /&gt;
* solarcharger - MPPT-DC-Lader, Anschluß VE-Direct (seriell, baut auf RS-485) , Wartung per Bluetooth und App&lt;br /&gt;
* grid - [https://www.victronenergy.com/accessories/energy-meter Stromzähler] , Anschluß über RS-485 / wahlweise Ethernet &lt;br /&gt;
* vebus - Wechselrichter, Anschluß über Cat5, serielles Protokoll&lt;br /&gt;
&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/system/0/Batteries:.* { json2nameValue($EVENT, &#039;Batteries_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/solarcharger/290/Yield/Power:.* { json2nameValue($EVENT, &#039;PVPower_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/grid/30/Ac/Power:.* { json2nameValue($EVENT, &#039;GridPower_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/vebus/288/Ac/Out/P:.* { json2nameValue($EVENT, &#039;P_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/system/0/SystemState/State:.* { json2nameValue($EVENT, &#039;State_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode:.* { json2nameValue($EVENT, &#039;Mode_&#039;, $JSONMAP) }&lt;br /&gt;
&lt;br /&gt;
===== Topics zum Schreiben =====&lt;br /&gt;
Einige Enstellungen sind schreibbar. Die entsprechenden Topics beginnen mit einem W. Eine setList kann beispielsweise so aussehen und erlaubt dann &lt;br /&gt;
* An- und Abschalten&lt;br /&gt;
* Inverter- und Charger Mode&lt;br /&gt;
* Mode: Betriebszustand&lt;br /&gt;
* CurrentLimit: max. Strom aus/in das Netz&lt;br /&gt;
* MinimumSocLimit: Entladegranze im ESS (Speicher-)Betrieb, solange das Netz anliegt&lt;br /&gt;
* Relay[0|1]: Schalte interne Relais&lt;br /&gt;
Eine einfache Setlist:&lt;br /&gt;
&lt;br /&gt;
 off W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:4}&lt;br /&gt;
 on W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:3}&lt;br /&gt;
 inv W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:2}&lt;br /&gt;
 chg W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:1}&lt;br /&gt;
 Mode W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 GridCurrentLimit W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Ac/In/1/CurrentLimit {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 MinSOC W/&amp;lt;vrm_ID&amp;gt;/settings/0/Settings/CGwacs/BatteryLife/MinimumSocLimit {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 Relay0 W/&amp;lt;vrm_ID&amp;gt;/system/0/Relay/0/State:.* {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 Relay1 W/&amp;lt;vrm_ID&amp;gt;/system/0/Relay/1/State:.* {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einbindung über ModbusAttr== &lt;br /&gt;
Die verschiedenen Devices werden intern über verschieden Modbusadressen abgefragt. &lt;br /&gt;
Dies kann man dem Excel &amp;quot;Modbus-TCP Register List unter Link 1 entnehmen. &lt;br /&gt;
&lt;br /&gt;
Hier ist der Code für ein MultiPlus II-5000 Device (die Unit ID 227 verweist auf den VE.Bus-Port an ttyS4, an welchem ausschließlich jener Multiplus angeschlossen ist). Leider kann ich es mangels Geräten nicht testen, aber ich vermute, dass wenn der Multiplus an einen anderen Port angeschlossen wird, lediglich die Modbus-ID zu ändern ist.&lt;br /&gt;
&lt;br /&gt;
=== Multiplus II ===&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
defmod MultiPlusII_227 ModbusAttr 227 5 192.168.243.38:502 TCP&lt;br /&gt;
attr MultiPlusII_227 dev-h-defPoll 1&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-reading AC_Input_Voltage_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-reading AC_Input_Voltage_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-reading AC_Input_Voltage_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-reading AC_Input_Current_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-reading AC_Input_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-reading AC_Input_Current_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-reading AC_Input_Frequency_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-reading AC_Input_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-reading AC_Input_Frequency_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-reading AC_Input_Power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-reading AC_Input_Power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-reading AC_Input_Power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-reading AC_Output_Voltage_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-reading AC_Output_Voltage_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-reading AC_Output_Voltage_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-reading AC_Output_Current_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-reading AC_Output_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-reading AC_Output_Current_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-reading AC_Output_Frequency&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-reading Active_Input_current_Limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-reading AC_Output_Power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-reading AC_Output_Power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-reading AC_Output_Power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-reading Battery_Voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-reading Battery_Current&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0028-reading Phase_Count&lt;br /&gt;
attr MultiPlusII_227 obj-h0028-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-map 0:AC Input 1,1:AC Input 2,240:Disconnected&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-reading Active_Input&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-reading vebus_soc&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-map 0:Off,1:Low Power,2:Fault,3:Bulk,4:Absorption,5:Float,6:Storage,7:Equalize,8:Passthru,9:Inverting,10:Power assist,11:Power supply,244:Sustain,252:External controL&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-reading vebus_state&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-map 0:No error,1:VE.Bus Error Device is switched off because one of the other phases in the system has switched off,2:VE.Bus Error New and old types MK2 are mixed in the system,3:VE.Bus Error Not all- or more than- the expected devices were found in the system,4:VE.Bus Error No other device whatsoever detected,5:VE.Bus Error Overvoltage on AC-out,6:VE.Bus Error  Error in DDC Program,7:VE.Bus BMS connected- which requires an Assistant- but no assistant found,10:VE.Bus Error System time synchronisation problem occurred,14:VE.Bus Error Device cannot transmit data,16:VE.Bus Error Dongle missing,17:VE.Bus Error One of the devices assumed master status because the original master failed,18:VE.Bus Error AC Overvoltage on the output of a slave has occurred while already switched off,22:VE.Bus Error This device cannot function as slave,24:VE.Bus Error Switch-over system protection initiated,25:VE.Bus Error Firmware incompatibility. The firmware of one of the connected device is not sufficiently up to date to operate in conjunction with this device,26:VE.Bus Error Internal error&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-reading vebus_error&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-map 1:Charger Only,2:Inverter Only,3:On,4:Off&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-reading switch_position&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-reading temperature_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-reading low_battery_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-reading overload_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0037-reading ESS_Power_setpoint_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0037-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-map 0:Charge allowed,1:Charge disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-reading ESS_disable_charge_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-map 0:Feed in allowed,1:Feed in disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-reading ESS_disable_feedback_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0040-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0040-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0041-reading ESS_Power_setpoint_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0041-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-reading temperature_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-reading voltage_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-reading temperature_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-reading low_battery_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-reading overload_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-reading ripple_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-reading temperature_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-reading low_battery_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-reading overload_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-reading ripple_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-reading temperature_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-reading low_battery_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-reading overload_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-reading ripple_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-map 0:PV enabled,1:PV disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-reading disable_pv_inverter&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-reading bms_allow_charge&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-reading bms_allow_discharge&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-reading bms_expected&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-reading bms_error&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-reading temperature_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-map 1:VE.Bus reset&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-reading vebus_reset&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-map 0:Ok,1:Warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-reading phase_rotation_warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-map 0:Ok,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-reading grid_lost_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-map 0:Feed in overvoltage,1:Do not feed in overvoltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-reading feed_dc_overvoltage_into_grid&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-reading maximum_overvoltage_feedin_power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-reading maximum_overvoltage_feedin_power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-reading maximum_overvoltage_feedin_power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-map 0:AC input not ignored,1:AC input ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-reading ac_input_1_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-map 0:AC input not ignored,1:AC input ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-reading ac_input_2_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-map 0:AcPowerSetpoint interpreted normally, 1:AcPowerSetpoint is OvervoltageFeedIn limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-reading ac_power_setpoint_acts_as_feedin_limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-map 0:OvervoltageFeedIn uses 1V offset, 1:OvervoltageFeedIn uses 0.1V offset&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-reading solar_offset_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-map 0:Sustain inactive, 1:Sustain active&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-reading sustain_active&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-reading energy_from_acIn1_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-reading energy_from_acIn1_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-reading energy_from_acIn2_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-reading energy_from_acIn2_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-reading energy_from_acOut_to_acIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-reading energy_from_acOut_to_acIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-reading energy_from_battery_to_AcIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-reading energy_from_battery_to_AcIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-reading energy_from_battery_to_AcOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-reading energy_from_AcOut_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-map 0:OK,1:Warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-reading low_cell_voltage_imminent&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-map 0:Initialising,1:Bulk,2:Absorption,3:Float,4:Storage,5:Absorb repeat,6:Forced absorb,7:Equalise,8:Bulk stopped,9:Unknown&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-reading charge_state&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0096-reading ESS_Power_setpoint_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0096-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-map 0:Renewable energy not preferred,1:Renewable energy preferred&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-reading prefer_renewable_energy&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-map 0:Generator not selected,1:Generator selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-reading select_remote_generator&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-map 0:Generator not selected,1:Generator selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-reading remote_generator_selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-map 0:No Action, 1:Redetect System&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-reading redetect_vebus_system&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-reading powerAssist_boost_factor&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-reading configured_output_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-map 0:disabled, 1:enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-reading powerAssist_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-map 0:disabled, 1:enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-reading ups_function_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h10-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h10-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h10-reading AC_Input_Frequency_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h10-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h100-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h100-reading ESS_Power_setpoint_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h100-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h102-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h102-reading prefer_renewable_energy&lt;br /&gt;
attr MultiPlusII_227 obj-h102-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h103-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h103-reading select_remote_generator&lt;br /&gt;
attr MultiPlusII_227 obj-h103-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h104-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h104-reading remote_generator_selected&lt;br /&gt;
attr MultiPlusII_227 obj-h104-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h105-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h105-reading redetect_vebus_system&lt;br /&gt;
attr MultiPlusII_227 obj-h105-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h106-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h106-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h106-reading powerAssist_boost_factor&lt;br /&gt;
attr MultiPlusII_227 obj-h106-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h107-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h107-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h107-reading configured_output_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h107-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h108-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h108-reading powerAssist_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h108-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h109-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h109-reading ups_function_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h109-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h11-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h11-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h11-reading AC_Input_Frequency_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h11-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h12-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h12-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h12-reading AC_Input_Power_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h12-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h13-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h13-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h13-reading AC_Input_Power_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h13-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h14-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h14-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h14-reading AC_Input_Power_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h14-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h15-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h15-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h15-reading AC_Output_Voltage_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h15-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h16-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h16-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h16-reading AC_Output_Voltage_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h16-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h17-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h17-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h17-reading AC_Output_Voltage_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h17-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h18-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h18-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h18-reading AC_Output_Current_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h18-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h19-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h19-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h19-reading AC_Output_Current_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h19-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h20-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h20-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h20-reading AC_Output_Current_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h20-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h21-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h21-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h21-reading AC_Output_Frequency&lt;br /&gt;
attr MultiPlusII_227 obj-h21-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h22-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h22-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h22-reading Active_Input_current_Limit&lt;br /&gt;
attr MultiPlusII_227 obj-h22-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h23-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h23-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h23-reading AC_Output_Power_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h23-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h24-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h24-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h24-reading AC_Output_Power_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h24-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h25-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h25-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h25-reading AC_Output_Power_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h25-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h26-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h26-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h26-reading Battery_Voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h26-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h27-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h27-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h27-reading Battery_Current&lt;br /&gt;
attr MultiPlusII_227 obj-h27-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h28-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h28-reading Phase_Count&lt;br /&gt;
attr MultiPlusII_227 obj-h28-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h29-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h29-reading Active_Input&lt;br /&gt;
attr MultiPlusII_227 obj-h29-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h3-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h3-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h3-reading AC_Input_Voltage_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h3-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h30-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h30-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h30-reading vebus_soc&lt;br /&gt;
attr MultiPlusII_227 obj-h30-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h31-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h31-reading vebus_state&lt;br /&gt;
attr MultiPlusII_227 obj-h31-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h32-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h32-reading vebus_error&lt;br /&gt;
attr MultiPlusII_227 obj-h32-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h33-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h33-reading switch_position&lt;br /&gt;
attr MultiPlusII_227 obj-h33-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h34-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h34-reading temperature_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h34-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h35-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h35-reading low_battery_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h35-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h36-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h36-reading overload_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h36-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h37-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h37-reading ESS_Power_setpoint_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h37-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h38-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h38-reading ESS_disable_charge_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h38-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h39-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h39-reading ESS_disable_feedback_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h39-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h4-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h4-len 1	2&lt;br /&gt;
attr MultiPlusII_227 obj-h4-reading AC_Input_Voltage_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h4-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h40-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h40-reading ESS_Power_setpoint_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h40-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h41-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h41-reading ESS_Power_setpoint_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h41-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h42-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h42-reading temperature_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h42-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h43-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h43-reading voltage_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h43-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h44-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h44-reading temperature_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h44-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h45-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h45-reading low_battery_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h45-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h46-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h46-reading overload_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h46-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h47-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h47-reading ripple_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h47-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h48-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h48-reading temperature_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h48-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h49-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h49-reading low_battery_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h49-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h5-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h5-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h5-reading AC_Input_Voltage_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h5-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h50-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h50-reading overload_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h50-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h51-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h51-reading ripple_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h51-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h52-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h52-reading temperature_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h52-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h53-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h53-reading low_battery_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h53-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h54-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h54-reading overload_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h54-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h55-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h55-reading ripple_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h55-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h56-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h56-reading disable_pv_inverter&lt;br /&gt;
attr MultiPlusII_227 obj-h56-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h57-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h57-reading bms_allow_charge&lt;br /&gt;
attr MultiPlusII_227 obj-h57-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h58-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h58-reading bms_allow_discharge&lt;br /&gt;
attr MultiPlusII_227 obj-h58-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h59-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h59-reading bms_expected&lt;br /&gt;
attr MultiPlusII_227 obj-h59-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h6-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h6-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h6-reading AC_Input_Current_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h6-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h60-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h60-reading bms_error&lt;br /&gt;
attr MultiPlusII_227 obj-h60-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h61-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h61-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h61-reading temperature_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h61-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h62-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h62-reading vebus_reset&lt;br /&gt;
attr MultiPlusII_227 obj-h62-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h63-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h63-reading phase_rotation_warning&lt;br /&gt;
attr MultiPlusII_227 obj-h63-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h64-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h64-reading grid_lost_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h64-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h65-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h65-reading feed_dc_overvoltage_into_grid&lt;br /&gt;
attr MultiPlusII_227 obj-h65-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h66-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h66-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h66-reading maximum_overvoltage_feedin_power_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h66-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h67-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h67-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h67-reading maximum_overvoltage_feedin_power_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h67-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h68-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h68-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h68-reading maximum_overvoltage_feedin_power_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h68-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h69-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h69-reading ac_input_1_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h69-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h7-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h7-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h7-reading AC_Input_Current_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h7-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h70-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h70-reading ac_input_2_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h70-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h71-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h71-reading ac_power_setpoint_acts_as_feedin_limit&lt;br /&gt;
attr MultiPlusII_227 obj-h71-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h72-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h72-reading solar_offset_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h72-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h73-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h73-reading sustain_active&lt;br /&gt;
attr MultiPlusII_227 obj-h73-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h74-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h74-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h74-reading energy_from_acIn1_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h74-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h76-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h76-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h76-reading energy_from_acIn1_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h76-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h78-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h78-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h78-reading energy_from_acIn2_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h78-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h8-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h8-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h8-reading AC_Input_Current_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h8-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h80-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h80-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h80-reading energy_from_acIn2_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h80-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h82-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h82-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h82-reading energy_from_acOut_to_acIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h82-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h84-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h84-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h84-reading energy_from_acOut_to_acIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h84-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h86-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h86-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h86-reading energy_from_battery_to_AcIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h86-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h88-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h88-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h88-reading energy_from_battery_to_AcIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h88-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h9-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h9-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h9-reading AC_Input_Frequency_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h9-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h90-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h90-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h90-reading energy_from_battery_to_AcOut&lt;br /&gt;
attr MultiPlusII_227 obj-h90-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h92-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h92-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h92-reading energy_from_AcOut_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h92-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h94-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h94-reading low_cell_voltage_imminent&lt;br /&gt;
attr MultiPlusII_227 obj-h94-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h95-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h95-reading charge_state&lt;br /&gt;
attr MultiPlusII_227 obj-h95-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h96-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h96-reading ESS_Power_setpoint_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h96-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h98-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h98-reading ESS_Power_setpoint_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h98-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 room Solar-&amp;gt;Victron&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Cerbo GX mit Unit ID 100 ===&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
defmod Cerbo_100 ModbusAttr 100 5 192.168.243.38:502 TCP&lt;br /&gt;
attr Cerbo_100 dev-h-defPoll 1&lt;br /&gt;
attr Cerbo_100 obj-h0800-len 6&lt;br /&gt;
attr Cerbo_100 obj-h0800-reading system_serial&lt;br /&gt;
attr Cerbo_100 obj-h0800-unpack Z12&lt;br /&gt;
attr Cerbo_100 obj-h0806-map 0:Open,1:Closed&lt;br /&gt;
attr Cerbo_100 obj-h0806-reading CCGX_relay_state_1&lt;br /&gt;
attr Cerbo_100 obj-h0806-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0807-map 0:Open,1:Closed&lt;br /&gt;
attr Cerbo_100 obj-h0807-reading CCGX_relay_state_2&lt;br /&gt;
attr Cerbo_100 obj-h0807-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0808-reading PV_AC_Coupled_output_L1&lt;br /&gt;
attr Cerbo_100 obj-h0808-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0809-reading PV_AC_Coupled_output_L2&lt;br /&gt;
attr Cerbo_100 obj-h0809-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0810-reading PV_AC_Coupled_output_L3&lt;br /&gt;
attr Cerbo_100 obj-h0810-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0811-reading PV_AC_Coupled_input_L1&lt;br /&gt;
attr Cerbo_100 obj-h0811-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0812-reading PV_AC_Coupled_input_L2&lt;br /&gt;
attr Cerbo_100 obj-h0812-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0813-reading PV_AC_Coupled_input_L3&lt;br /&gt;
attr Cerbo_100 obj-h0813-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0814-reading PV_AC_Coupled_generator_L1&lt;br /&gt;
attr Cerbo_100 obj-h0814-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0815-reading PV_AC_Coupled_generator_L2&lt;br /&gt;
attr Cerbo_100 obj-h0815-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0816-reading PV_AC_Coupled_generator_L3&lt;br /&gt;
attr Cerbo_100 obj-h0816-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0817-reading AC_consumption_L1&lt;br /&gt;
attr Cerbo_100 obj-h0817-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0818-reading AC_consumption_L2&lt;br /&gt;
attr Cerbo_100 obj-h0818-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0819-reading AC_consumption_L3&lt;br /&gt;
attr Cerbo_100 obj-h0819-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0820-reading grid_L1&lt;br /&gt;
attr Cerbo_100 obj-h0820-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0821-reading grid_L2&lt;br /&gt;
attr Cerbo_100 obj-h0821-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0822-reading grid_L3&lt;br /&gt;
attr Cerbo_100 obj-h0822-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0823-reading genset_L1&lt;br /&gt;
attr Cerbo_100 obj-h0823-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0824-reading genset_L2&lt;br /&gt;
attr Cerbo_100 obj-h0824-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0825-reading genset_L3&lt;br /&gt;
attr Cerbo_100 obj-h0825-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0826-map 0:Unknown,1:Grid,2:Generator,3:Shore power,240:Not connected&lt;br /&gt;
attr Cerbo_100 obj-h0826-reading active_input_source&lt;br /&gt;
attr Cerbo_100 obj-h0826-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0830-len 4&lt;br /&gt;
attr Cerbo_100 obj-h0830-reading system_UTC_time&lt;br /&gt;
attr Cerbo_100 obj-h0830-unpack Q&lt;br /&gt;
attr Cerbo_100 obj-h0840-expr $val / 10&lt;br /&gt;
attr Cerbo_100 obj-h0840-reading battery_voltage_system&lt;br /&gt;
attr Cerbo_100 obj-h0840-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0841-expr $val / 10&lt;br /&gt;
attr Cerbo_100 obj-h0841-reading battery_current_system&lt;br /&gt;
attr Cerbo_100 obj-h0841-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0842-reading battery_power_system&lt;br /&gt;
attr Cerbo_100 obj-h0842-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0843-reading battery_soc_system&lt;br /&gt;
attr Cerbo_100 obj-h0843-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0844-map 0:idle,1:charging,2:discharging&lt;br /&gt;
attr Cerbo_100 obj-h0844-reading battery_state_system&lt;br /&gt;
attr Cerbo_100 obj-h0844-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0845-expr $val /-10&lt;br /&gt;
attr Cerbo_100 obj-h0845-reading battery_consumed_amphours_system&lt;br /&gt;
attr Cerbo_100 obj-h0845-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0846-expr $val * 100&lt;br /&gt;
attr Cerbo_100 obj-h0846-reading battery_timetogo_system&lt;br /&gt;
attr Cerbo_100 obj-h0846-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0850-reading PV_DC_Coupled_power&lt;br /&gt;
attr Cerbo_100 obj-h0850-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0851-expr $val /10&lt;br /&gt;
attr Cerbo_100 obj-h0851-reading PV_DC_Coupled_current&lt;br /&gt;
attr Cerbo_100 obj-h0851-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0855-reading charger_power&lt;br /&gt;
attr Cerbo_100 obj-h0855-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0860-reading dc_system_power&lt;br /&gt;
attr Cerbo_100 obj-h0860-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0865-expr $val /10&lt;br /&gt;
attr Cerbo_100 obj-h0865-reading vebus_charge_current_system&lt;br /&gt;
attr Cerbo_100 obj-h0865-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0866-reading vebus_charge_power_system&lt;br /&gt;
attr Cerbo_100 obj-h0866-unpack s&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0867-reading RESERVED&lt;br /&gt;
attr Cerbo_100 obj-h0867-unpack n&lt;br /&gt;
attr Cerbo_100 obj-h0868-expr $val /10&lt;br /&gt;
attr Cerbo_100 obj-h0868-reading inverter_charger_current&lt;br /&gt;
attr Cerbo_100 obj-h0868-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0870-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0870-reading inverter_charger_power&lt;br /&gt;
attr Cerbo_100 obj-h0870-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0872-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0872-reading power_between_meter_and_charger_L1&lt;br /&gt;
attr Cerbo_100 obj-h0872-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0874-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0874-reading power_between_meter_and_charger_L2&lt;br /&gt;
attr Cerbo_100 obj-h0874-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0876-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0876-reading power_between_meter_and_charger_L3&lt;br /&gt;
attr Cerbo_100 obj-h0876-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0878-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0878-reading power_output_inverter_charger_L1&lt;br /&gt;
attr Cerbo_100 obj-h0878-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0880-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0880-reading power_output_inverter_charger_L2&lt;br /&gt;
attr Cerbo_100 obj-h0880-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 obj-h0882-len 2&lt;br /&gt;
attr Cerbo_100 obj-h0882-reading power_output_inverter_charger_L3&lt;br /&gt;
attr Cerbo_100 obj-h0882-unpack f&amp;gt;&lt;br /&gt;
attr Cerbo_100 room Solar-&amp;gt;Victron&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Visualisierung==&lt;br /&gt;
&#039;&#039;&#039;stateFormat&#039;&#039;&#039; kann so aussehen:&lt;br /&gt;
 SSOC Batteries_value_1_soc, &lt;br /&gt;
 SOC: Batteries_value_1_soc % (MinimumSocLimit_value), &lt;br /&gt;
 Bat: Batteries_value_1_power W,&lt;br /&gt;
 PV: PVPower_value W,&lt;br /&gt;
 ACin: GridPower_value W, &lt;br /&gt;
 ACout2: P_value W,&lt;br /&gt;
 State: State_value,&lt;br /&gt;
 Mode: Mode_value&lt;br /&gt;
SSOC wird später für das devStateIcon genutzt.&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;devStateIcon&#039;&#039;&#039; bietet einen ersten Blick auf den Ladestand:&lt;br /&gt;
 SSOC\s[2|3|4].+:measure_battery_25&lt;br /&gt;
 SSOC\s[5|6].+:measure_battery_50&lt;br /&gt;
 SSOC\s[7|8].+:measure_battery_75&lt;br /&gt;
 SSOC\s[9].+:measure_battery_100&lt;br /&gt;
&lt;br /&gt;
Derzeit existiert noch kein Template, aber das kann sich ja ändern.&lt;br /&gt;
&lt;br /&gt;
==Externe Links==&lt;br /&gt;
*[https://www.victronenergy.com/support-and-downloads/technical-information Victron Technical Info]&lt;br /&gt;
*[https://vrm-api-docs.victronenergy.com/#/ VRm API]&lt;br /&gt;
*[https://github.com/victronenergy/dbus-mqtt Venus MQTT Broker]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Victron&amp;diff=40051</id>
		<title>Victron</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Victron&amp;diff=40051"/>
		<updated>2025-03-09T18:15:46Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Einbindung über ModbusAttr */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Übersicht==&lt;br /&gt;
Die Familie der Solarlade und -speichergeräte von Victron verwenden verschiedene Bussysteme, um untereinander zu kommunizieren. Sehr angenehm ist, daß die zentrale Steuerung namens Venus (Victron ENergy Unix System :-) frei verfügbar ist, und gern auf einem RaspberryPi läuft. Integriert ist gleich ein [https://de.wikipedia.org/wiki/MQTT MQTT] Server (mit und ohne SSL), der die Meßwerte der angeschlossenen Geräte - und das sind viele - per MQTT anbietet.&lt;br /&gt;
&lt;br /&gt;
Victron ist generell gut dokumentiert: https://www.victronenergy.com/support-and-downloads/technical-information&lt;br /&gt;
&lt;br /&gt;
==Einbindung in fhem als MQTT2_DEVICE==&lt;br /&gt;
Über MQTT2_CLIENT wird zunächst die Verbindung zum Broker auf dem Victron Venus Gerät angelegt. (Hier ist die IP noch anzupasssen.)&lt;br /&gt;
 defmod venus MQTT2_CLIENT 10.1.x.x:1883&lt;br /&gt;
 attr venus autocreate complex&lt;br /&gt;
 attr venus clientOrder MQTT_GENERIC_BRIDGE MQTT2_DEVICE&lt;br /&gt;
 attr venus room MQTT2_DEVICE&lt;br /&gt;
 attr venus verbose 4&lt;br /&gt;
&lt;br /&gt;
Das Wechselrichter wird dann als MQTT2_DEVICE angelegt: &lt;br /&gt;
 defmod MQTT2_mp2 MQTT2_DEVICE venus&lt;br /&gt;
 attr MQTT2_mp2 IODev venus&lt;br /&gt;
&lt;br /&gt;
===== Aufbau der Topics von Victron =====&lt;br /&gt;
Jede Installation &#039;&#039;Venus&#039;&#039; gibt sich eine vrm_ID, die initial aus der Hardwareadresse abgeleitet wird. Über diese ID wird die Installation auf dem [https:///vrm.victronenergy.com Victron VRM Portal] erkannt. In den Beispielen unten taucht sie als &amp;lt;vrm_ID&amp;gt; auf. Die einzelnen Geräte einer Installation erhalten Nummern, die eine Unterscheidung auch mehrerer Wechselrichter (etwa im 3-Phasenbetrieb) oder mehrerer Solarlader ermöglichen. &lt;br /&gt;
&lt;br /&gt;
Nach Verbindung erscheint eine umfangreiche readingList, aus der hier nur einige Elemente verwendet werden. &lt;br /&gt;
* system - Konfiguration des Gesamtsystems bzw. konsolidierte Werte&lt;br /&gt;
* solarcharger - MPPT-DC-Lader, Anschluß VE-Direct (seriell, baut auf RS-485) , Wartung per Bluetooth und App&lt;br /&gt;
* grid - [https://www.victronenergy.com/accessories/energy-meter Stromzähler] , Anschluß über RS-485 / wahlweise Ethernet &lt;br /&gt;
* vebus - Wechselrichter, Anschluß über Cat5, serielles Protokoll&lt;br /&gt;
&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/system/0/Batteries:.* { json2nameValue($EVENT, &#039;Batteries_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/solarcharger/290/Yield/Power:.* { json2nameValue($EVENT, &#039;PVPower_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/grid/30/Ac/Power:.* { json2nameValue($EVENT, &#039;GridPower_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/vebus/288/Ac/Out/P:.* { json2nameValue($EVENT, &#039;P_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/system/0/SystemState/State:.* { json2nameValue($EVENT, &#039;State_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode:.* { json2nameValue($EVENT, &#039;Mode_&#039;, $JSONMAP) }&lt;br /&gt;
&lt;br /&gt;
===== Topics zum Schreiben =====&lt;br /&gt;
Einige Enstellungen sind schreibbar. Die entsprechenden Topics beginnen mit einem W. Eine setList kann beispielsweise so aussehen und erlaubt dann &lt;br /&gt;
* An- und Abschalten&lt;br /&gt;
* Inverter- und Charger Mode&lt;br /&gt;
* Mode: Betriebszustand&lt;br /&gt;
* CurrentLimit: max. Strom aus/in das Netz&lt;br /&gt;
* MinimumSocLimit: Entladegranze im ESS (Speicher-)Betrieb, solange das Netz anliegt&lt;br /&gt;
* Relay[0|1]: Schalte interne Relais&lt;br /&gt;
Eine einfache Setlist:&lt;br /&gt;
&lt;br /&gt;
 off W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:4}&lt;br /&gt;
 on W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:3}&lt;br /&gt;
 inv W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:2}&lt;br /&gt;
 chg W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:1}&lt;br /&gt;
 Mode W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 GridCurrentLimit W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Ac/In/1/CurrentLimit {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 MinSOC W/&amp;lt;vrm_ID&amp;gt;/settings/0/Settings/CGwacs/BatteryLife/MinimumSocLimit {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 Relay0 W/&amp;lt;vrm_ID&amp;gt;/system/0/Relay/0/State:.* {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 Relay1 W/&amp;lt;vrm_ID&amp;gt;/system/0/Relay/1/State:.* {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einbindung über ModbusAttr== &lt;br /&gt;
Die verschiedenen Devices werden intern über verschieden Modbusadressen abgefragt. &lt;br /&gt;
Dies kann man dem Excel &amp;quot;Modbus-TCP Register List unter Link 1 entnehmen. &lt;br /&gt;
&lt;br /&gt;
Hier ist der Code für ein MultiPlus II-5000 Device (die Unit ID 227 verweist auf den VE.Bus-Port an ttyS4, an welchem ausschließlich jener Multiplus angeschlossen ist). Leider kann ich es mangels Geräten nicht testen, aber ich vermute, dass wenn der Multiplus an einen anderen Port angeschlossen wird, lediglich die Modbus-ID zu ändern ist.&lt;br /&gt;
&lt;br /&gt;
=== Multiplus II ===&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
defmod MultiPlusII_227 ModbusAttr 227 5 192.168.243.38:502 TCP&lt;br /&gt;
attr MultiPlusII_227 dev-h-defPoll 1&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-reading AC_Input_Voltage_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-reading AC_Input_Voltage_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-reading AC_Input_Voltage_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-reading AC_Input_Current_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-reading AC_Input_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-reading AC_Input_Current_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-reading AC_Input_Frequency_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-reading AC_Input_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-reading AC_Input_Frequency_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-reading AC_Input_Power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-reading AC_Input_Power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-reading AC_Input_Power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-reading AC_Output_Voltage_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-reading AC_Output_Voltage_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-reading AC_Output_Voltage_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-reading AC_Output_Current_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-reading AC_Output_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-reading AC_Output_Current_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-reading AC_Output_Frequency&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-reading Active_Input_current_Limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-reading AC_Output_Power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-reading AC_Output_Power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-reading AC_Output_Power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-reading Battery_Voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-reading Battery_Current&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0028-reading Phase_Count&lt;br /&gt;
attr MultiPlusII_227 obj-h0028-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-map 0:AC Input 1,1:AC Input 2,240:Disconnected&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-reading Active_Input&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-reading vebus_soc&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-map 0:Off,1:Low Power,2:Fault,3:Bulk,4:Absorption,5:Float,6:Storage,7:Equalize,8:Passthru,9:Inverting,10:Power assist,11:Power supply,244:Sustain,252:External controL&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-reading vebus_state&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-map 0:No error,1:VE.Bus Error Device is switched off because one of the other phases in the system has switched off,2:VE.Bus Error New and old types MK2 are mixed in the system,3:VE.Bus Error Not all- or more than- the expected devices were found in the system,4:VE.Bus Error No other device whatsoever detected,5:VE.Bus Error Overvoltage on AC-out,6:VE.Bus Error  Error in DDC Program,7:VE.Bus BMS connected- which requires an Assistant- but no assistant found,10:VE.Bus Error System time synchronisation problem occurred,14:VE.Bus Error Device cannot transmit data,16:VE.Bus Error Dongle missing,17:VE.Bus Error One of the devices assumed master status because the original master failed,18:VE.Bus Error AC Overvoltage on the output of a slave has occurred while already switched off,22:VE.Bus Error This device cannot function as slave,24:VE.Bus Error Switch-over system protection initiated,25:VE.Bus Error Firmware incompatibility. The firmware of one of the connected device is not sufficiently up to date to operate in conjunction with this device,26:VE.Bus Error Internal error&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-reading vebus_error&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-map 1:Charger Only,2:Inverter Only,3:On,4:Off&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-reading switch_position&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-reading temperature_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-reading low_battery_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-reading overload_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0037-reading ESS_Power_setpoint_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0037-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-map 0:Charge allowed,1:Charge disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-reading ESS_disable_charge_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-map 0:Feed in allowed,1:Feed in disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-reading ESS_disable_feedback_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0040-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0040-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0041-reading ESS_Power_setpoint_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0041-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-reading temperature_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-reading voltage_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-reading temperature_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-reading low_battery_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-reading overload_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-reading ripple_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-reading temperature_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-reading low_battery_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-reading overload_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-reading ripple_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-reading temperature_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-reading low_battery_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-reading overload_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-reading ripple_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-map 0:PV enabled,1:PV disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-reading disable_pv_inverter&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-reading bms_allow_charge&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-reading bms_allow_discharge&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-reading bms_expected&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-reading bms_error&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-reading temperature_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-map 1:VE.Bus reset&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-reading vebus_reset&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-map 0:Ok,1:Warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-reading phase_rotation_warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-map 0:Ok,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-reading grid_lost_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-map 0:Feed in overvoltage,1:Do not feed in overvoltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-reading feed_dc_overvoltage_into_grid&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-reading maximum_overvoltage_feedin_power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-reading maximum_overvoltage_feedin_power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-reading maximum_overvoltage_feedin_power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-map 0:AC input not ignored,1:AC input ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-reading ac_input_1_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-map 0:AC input not ignored,1:AC input ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-reading ac_input_2_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-map 0:AcPowerSetpoint interpreted normally, 1:AcPowerSetpoint is OvervoltageFeedIn limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-reading ac_power_setpoint_acts_as_feedin_limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-map 0:OvervoltageFeedIn uses 1V offset, 1:OvervoltageFeedIn uses 0.1V offset&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-reading solar_offset_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-map 0:Sustain inactive, 1:Sustain active&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-reading sustain_active&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-reading energy_from_acIn1_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-reading energy_from_acIn1_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-reading energy_from_acIn2_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-reading energy_from_acIn2_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-reading energy_from_acOut_to_acIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-reading energy_from_acOut_to_acIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-reading energy_from_battery_to_AcIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-reading energy_from_battery_to_AcIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-reading energy_from_battery_to_AcOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-reading energy_from_AcOut_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-map 0:OK,1:Warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-reading low_cell_voltage_imminent&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-map 0:Initialising,1:Bulk,2:Absorption,3:Float,4:Storage,5:Absorb repeat,6:Forced absorb,7:Equalise,8:Bulk stopped,9:Unknown&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-reading charge_state&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0096-reading ESS_Power_setpoint_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0096-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-map 0:Renewable energy not preferred,1:Renewable energy preferred&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-reading prefer_renewable_energy&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-map 0:Generator not selected,1:Generator selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-reading select_remote_generator&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-map 0:Generator not selected,1:Generator selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-reading remote_generator_selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-map 0:No Action, 1:Redetect System&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-reading redetect_vebus_system&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-reading powerAssist_boost_factor&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-reading configured_output_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-map 0:disabled, 1:enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-reading powerAssist_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-map 0:disabled, 1:enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-reading ups_function_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h10-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h10-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h10-reading AC_Input_Frequency_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h10-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h100-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h100-reading ESS_Power_setpoint_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h100-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h102-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h102-reading prefer_renewable_energy&lt;br /&gt;
attr MultiPlusII_227 obj-h102-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h103-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h103-reading select_remote_generator&lt;br /&gt;
attr MultiPlusII_227 obj-h103-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h104-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h104-reading remote_generator_selected&lt;br /&gt;
attr MultiPlusII_227 obj-h104-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h105-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h105-reading redetect_vebus_system&lt;br /&gt;
attr MultiPlusII_227 obj-h105-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h106-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h106-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h106-reading powerAssist_boost_factor&lt;br /&gt;
attr MultiPlusII_227 obj-h106-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h107-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h107-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h107-reading configured_output_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h107-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h108-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h108-reading powerAssist_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h108-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h109-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h109-reading ups_function_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h109-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h11-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h11-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h11-reading AC_Input_Frequency_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h11-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h12-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h12-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h12-reading AC_Input_Power_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h12-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h13-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h13-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h13-reading AC_Input_Power_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h13-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h14-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h14-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h14-reading AC_Input_Power_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h14-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h15-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h15-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h15-reading AC_Output_Voltage_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h15-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h16-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h16-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h16-reading AC_Output_Voltage_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h16-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h17-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h17-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h17-reading AC_Output_Voltage_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h17-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h18-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h18-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h18-reading AC_Output_Current_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h18-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h19-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h19-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h19-reading AC_Output_Current_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h19-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h20-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h20-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h20-reading AC_Output_Current_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h20-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h21-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h21-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h21-reading AC_Output_Frequency&lt;br /&gt;
attr MultiPlusII_227 obj-h21-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h22-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h22-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h22-reading Active_Input_current_Limit&lt;br /&gt;
attr MultiPlusII_227 obj-h22-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h23-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h23-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h23-reading AC_Output_Power_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h23-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h24-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h24-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h24-reading AC_Output_Power_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h24-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h25-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h25-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h25-reading AC_Output_Power_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h25-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h26-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h26-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h26-reading Battery_Voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h26-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h27-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h27-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h27-reading Battery_Current&lt;br /&gt;
attr MultiPlusII_227 obj-h27-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h28-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h28-reading Phase_Count&lt;br /&gt;
attr MultiPlusII_227 obj-h28-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h29-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h29-reading Active_Input&lt;br /&gt;
attr MultiPlusII_227 obj-h29-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h3-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h3-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h3-reading AC_Input_Voltage_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h3-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h30-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h30-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h30-reading vebus_soc&lt;br /&gt;
attr MultiPlusII_227 obj-h30-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h31-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h31-reading vebus_state&lt;br /&gt;
attr MultiPlusII_227 obj-h31-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h32-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h32-reading vebus_error&lt;br /&gt;
attr MultiPlusII_227 obj-h32-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h33-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h33-reading switch_position&lt;br /&gt;
attr MultiPlusII_227 obj-h33-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h34-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h34-reading temperature_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h34-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h35-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h35-reading low_battery_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h35-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h36-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h36-reading overload_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h36-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h37-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h37-reading ESS_Power_setpoint_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h37-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h38-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h38-reading ESS_disable_charge_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h38-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h39-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h39-reading ESS_disable_feedback_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h39-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h4-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h4-len 1	2&lt;br /&gt;
attr MultiPlusII_227 obj-h4-reading AC_Input_Voltage_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h4-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h40-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h40-reading ESS_Power_setpoint_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h40-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h41-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h41-reading ESS_Power_setpoint_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h41-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h42-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h42-reading temperature_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h42-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h43-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h43-reading voltage_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h43-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h44-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h44-reading temperature_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h44-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h45-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h45-reading low_battery_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h45-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h46-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h46-reading overload_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h46-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h47-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h47-reading ripple_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h47-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h48-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h48-reading temperature_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h48-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h49-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h49-reading low_battery_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h49-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h5-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h5-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h5-reading AC_Input_Voltage_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h5-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h50-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h50-reading overload_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h50-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h51-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h51-reading ripple_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h51-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h52-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h52-reading temperature_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h52-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h53-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h53-reading low_battery_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h53-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h54-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h54-reading overload_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h54-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h55-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h55-reading ripple_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h55-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h56-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h56-reading disable_pv_inverter&lt;br /&gt;
attr MultiPlusII_227 obj-h56-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h57-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h57-reading bms_allow_charge&lt;br /&gt;
attr MultiPlusII_227 obj-h57-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h58-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h58-reading bms_allow_discharge&lt;br /&gt;
attr MultiPlusII_227 obj-h58-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h59-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h59-reading bms_expected&lt;br /&gt;
attr MultiPlusII_227 obj-h59-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h6-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h6-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h6-reading AC_Input_Current_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h6-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h60-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h60-reading bms_error&lt;br /&gt;
attr MultiPlusII_227 obj-h60-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h61-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h61-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h61-reading temperature_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h61-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h62-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h62-reading vebus_reset&lt;br /&gt;
attr MultiPlusII_227 obj-h62-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h63-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h63-reading phase_rotation_warning&lt;br /&gt;
attr MultiPlusII_227 obj-h63-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h64-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h64-reading grid_lost_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h64-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h65-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h65-reading feed_dc_overvoltage_into_grid&lt;br /&gt;
attr MultiPlusII_227 obj-h65-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h66-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h66-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h66-reading maximum_overvoltage_feedin_power_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h66-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h67-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h67-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h67-reading maximum_overvoltage_feedin_power_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h67-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h68-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h68-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h68-reading maximum_overvoltage_feedin_power_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h68-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h69-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h69-reading ac_input_1_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h69-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h7-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h7-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h7-reading AC_Input_Current_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h7-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h70-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h70-reading ac_input_2_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h70-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h71-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h71-reading ac_power_setpoint_acts_as_feedin_limit&lt;br /&gt;
attr MultiPlusII_227 obj-h71-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h72-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h72-reading solar_offset_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h72-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h73-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h73-reading sustain_active&lt;br /&gt;
attr MultiPlusII_227 obj-h73-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h74-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h74-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h74-reading energy_from_acIn1_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h74-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h76-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h76-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h76-reading energy_from_acIn1_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h76-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h78-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h78-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h78-reading energy_from_acIn2_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h78-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h8-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h8-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h8-reading AC_Input_Current_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h8-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h80-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h80-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h80-reading energy_from_acIn2_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h80-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h82-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h82-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h82-reading energy_from_acOut_to_acIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h82-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h84-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h84-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h84-reading energy_from_acOut_to_acIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h84-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h86-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h86-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h86-reading energy_from_battery_to_AcIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h86-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h88-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h88-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h88-reading energy_from_battery_to_AcIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h88-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h9-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h9-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h9-reading AC_Input_Frequency_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h9-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h90-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h90-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h90-reading energy_from_battery_to_AcOut&lt;br /&gt;
attr MultiPlusII_227 obj-h90-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h92-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h92-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h92-reading energy_from_AcOut_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h92-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h94-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h94-reading low_cell_voltage_imminent&lt;br /&gt;
attr MultiPlusII_227 obj-h94-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h95-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h95-reading charge_state&lt;br /&gt;
attr MultiPlusII_227 obj-h95-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h96-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h96-reading ESS_Power_setpoint_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h96-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h98-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h98-reading ESS_Power_setpoint_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h98-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 room Solar-&amp;gt;Victron&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Visualisierung==&lt;br /&gt;
&#039;&#039;&#039;stateFormat&#039;&#039;&#039; kann so aussehen:&lt;br /&gt;
 SSOC Batteries_value_1_soc, &lt;br /&gt;
 SOC: Batteries_value_1_soc % (MinimumSocLimit_value), &lt;br /&gt;
 Bat: Batteries_value_1_power W,&lt;br /&gt;
 PV: PVPower_value W,&lt;br /&gt;
 ACin: GridPower_value W, &lt;br /&gt;
 ACout2: P_value W,&lt;br /&gt;
 State: State_value,&lt;br /&gt;
 Mode: Mode_value&lt;br /&gt;
SSOC wird später für das devStateIcon genutzt.&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;devStateIcon&#039;&#039;&#039; bietet einen ersten Blick auf den Ladestand:&lt;br /&gt;
 SSOC\s[2|3|4].+:measure_battery_25&lt;br /&gt;
 SSOC\s[5|6].+:measure_battery_50&lt;br /&gt;
 SSOC\s[7|8].+:measure_battery_75&lt;br /&gt;
 SSOC\s[9].+:measure_battery_100&lt;br /&gt;
&lt;br /&gt;
Derzeit existiert noch kein Template, aber das kann sich ja ändern.&lt;br /&gt;
&lt;br /&gt;
==Externe Links==&lt;br /&gt;
*[https://www.victronenergy.com/support-and-downloads/technical-information Victron Technical Info]&lt;br /&gt;
*[https://vrm-api-docs.victronenergy.com/#/ VRm API]&lt;br /&gt;
*[https://github.com/victronenergy/dbus-mqtt Venus MQTT Broker]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Victron&amp;diff=40050</id>
		<title>Victron</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Victron&amp;diff=40050"/>
		<updated>2025-03-09T18:08:25Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Multiplus II */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Übersicht==&lt;br /&gt;
Die Familie der Solarlade und -speichergeräte von Victron verwenden verschiedene Bussysteme, um untereinander zu kommunizieren. Sehr angenehm ist, daß die zentrale Steuerung namens Venus (Victron ENergy Unix System :-) frei verfügbar ist, und gern auf einem RaspberryPi läuft. Integriert ist gleich ein [https://de.wikipedia.org/wiki/MQTT MQTT] Server (mit und ohne SSL), der die Meßwerte der angeschlossenen Geräte - und das sind viele - per MQTT anbietet.&lt;br /&gt;
&lt;br /&gt;
Victron ist generell gut dokumentiert: https://www.victronenergy.com/support-and-downloads/technical-information&lt;br /&gt;
&lt;br /&gt;
==Einbindung in fhem als MQTT2_DEVICE==&lt;br /&gt;
Über MQTT2_CLIENT wird zunächst die Verbindung zum Broker auf dem Victron Venus Gerät angelegt. (Hier ist die IP noch anzupasssen.)&lt;br /&gt;
 defmod venus MQTT2_CLIENT 10.1.x.x:1883&lt;br /&gt;
 attr venus autocreate complex&lt;br /&gt;
 attr venus clientOrder MQTT_GENERIC_BRIDGE MQTT2_DEVICE&lt;br /&gt;
 attr venus room MQTT2_DEVICE&lt;br /&gt;
 attr venus verbose 4&lt;br /&gt;
&lt;br /&gt;
Das Wechselrichter wird dann als MQTT2_DEVICE angelegt: &lt;br /&gt;
 defmod MQTT2_mp2 MQTT2_DEVICE venus&lt;br /&gt;
 attr MQTT2_mp2 IODev venus&lt;br /&gt;
&lt;br /&gt;
===== Aufbau der Topics von Victron =====&lt;br /&gt;
Jede Installation &#039;&#039;Venus&#039;&#039; gibt sich eine vrm_ID, die initial aus der Hardwareadresse abgeleitet wird. Über diese ID wird die Installation auf dem [https:///vrm.victronenergy.com Victron VRM Portal] erkannt. In den Beispielen unten taucht sie als &amp;lt;vrm_ID&amp;gt; auf. Die einzelnen Geräte einer Installation erhalten Nummern, die eine Unterscheidung auch mehrerer Wechselrichter (etwa im 3-Phasenbetrieb) oder mehrerer Solarlader ermöglichen. &lt;br /&gt;
&lt;br /&gt;
Nach Verbindung erscheint eine umfangreiche readingList, aus der hier nur einige Elemente verwendet werden. &lt;br /&gt;
* system - Konfiguration des Gesamtsystems bzw. konsolidierte Werte&lt;br /&gt;
* solarcharger - MPPT-DC-Lader, Anschluß VE-Direct (seriell, baut auf RS-485) , Wartung per Bluetooth und App&lt;br /&gt;
* grid - [https://www.victronenergy.com/accessories/energy-meter Stromzähler] , Anschluß über RS-485 / wahlweise Ethernet &lt;br /&gt;
* vebus - Wechselrichter, Anschluß über Cat5, serielles Protokoll&lt;br /&gt;
&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/system/0/Batteries:.* { json2nameValue($EVENT, &#039;Batteries_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/solarcharger/290/Yield/Power:.* { json2nameValue($EVENT, &#039;PVPower_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/grid/30/Ac/Power:.* { json2nameValue($EVENT, &#039;GridPower_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/vebus/288/Ac/Out/P:.* { json2nameValue($EVENT, &#039;P_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/system/0/SystemState/State:.* { json2nameValue($EVENT, &#039;State_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode:.* { json2nameValue($EVENT, &#039;Mode_&#039;, $JSONMAP) }&lt;br /&gt;
&lt;br /&gt;
===== Topics zum Schreiben =====&lt;br /&gt;
Einige Enstellungen sind schreibbar. Die entsprechenden Topics beginnen mit einem W. Eine setList kann beispielsweise so aussehen und erlaubt dann &lt;br /&gt;
* An- und Abschalten&lt;br /&gt;
* Inverter- und Charger Mode&lt;br /&gt;
* Mode: Betriebszustand&lt;br /&gt;
* CurrentLimit: max. Strom aus/in das Netz&lt;br /&gt;
* MinimumSocLimit: Entladegranze im ESS (Speicher-)Betrieb, solange das Netz anliegt&lt;br /&gt;
* Relay[0|1]: Schalte interne Relais&lt;br /&gt;
Eine einfache Setlist:&lt;br /&gt;
&lt;br /&gt;
 off W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:4}&lt;br /&gt;
 on W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:3}&lt;br /&gt;
 inv W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:2}&lt;br /&gt;
 chg W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:1}&lt;br /&gt;
 Mode W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 GridCurrentLimit W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Ac/In/1/CurrentLimit {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 MinSOC W/&amp;lt;vrm_ID&amp;gt;/settings/0/Settings/CGwacs/BatteryLife/MinimumSocLimit {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 Relay0 W/&amp;lt;vrm_ID&amp;gt;/system/0/Relay/0/State:.* {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 Relay1 W/&amp;lt;vrm_ID&amp;gt;/system/0/Relay/1/State:.* {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einbindung über ModbusAttr== &lt;br /&gt;
Die verschiedenen Devices werden intern über verschieden Modbusadressen abgefragt. &lt;br /&gt;
Dies kann man dem Excel &amp;quot;Modbus-TCP Register List unter Link 1 entnehmen. &lt;br /&gt;
&lt;br /&gt;
Hier ist der Code für ein MultiPlus II-5000 Device (die Unit ID 227 verweist auf den VE.Bus-Port an ttyS4, an welchem ausschließlich jener Multiplus angeschlossen ist). Leider kann ich es mangels Geräten nicht testen, aber ich vermute, dass wenn der Multiplus an einen anderen Port angeschlossen wird, lediglich die Modbus-ID zu ändern ist.&lt;br /&gt;
&lt;br /&gt;
=== Multiplus II ===&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
defmod MultiPlusII_227 ModbusAttr 227 5 192.168.243.38:502 TCP&lt;br /&gt;
attr MultiPlusII_227 dev-h-combine 5&lt;br /&gt;
attr MultiPlusII_227 dev-h-defPoll 1&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-reading AC_Input_Voltage_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-reading AC_Input_Voltage_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-reading AC_Input_Voltage_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-reading AC_Input_Current_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-reading AC_Input_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-reading AC_Input_Current_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-reading AC_Input_Frequency_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-reading AC_Input_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-reading AC_Input_Frequency_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-reading AC_Input_Power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-reading AC_Input_Power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-reading AC_Input_Power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-reading AC_Output_Voltage_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-reading AC_Output_Voltage_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-reading AC_Output_Voltage_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-reading AC_Output_Current_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-reading AC_Output_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-reading AC_Output_Current_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-reading AC_Output_Frequency&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-reading Active_Input_current_Limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-reading AC_Output_Power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-reading AC_Output_Power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-reading AC_Output_Power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-reading Battery_Voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-reading Battery_Current&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0028-reading Phase_Count&lt;br /&gt;
attr MultiPlusII_227 obj-h0028-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-map 0:AC Input 1,1:AC Input 2,240:Disconnected&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-reading Active_Input&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-reading vebus_soc&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-map 0:Off,1:Low Power,2:Fault,3:Bulk,4:Absorption,5:Float,6:Storage,7:Equalize,8:Passthru,9:Inverting,10:Power assist,11:Power supply,244:Sustain,252:External controL&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-reading vebus_state&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-map 0:No error,1:VE.Bus Error Device is switched off because one of the other phases in the system has switched off,2:VE.Bus Error New and old types MK2 are mixed in the system,3:VE.Bus Error Not all- or more than- the expected devices were found in the system,4:VE.Bus Error No other device whatsoever detected,5:VE.Bus Error Overvoltage on AC-out,6:VE.Bus Error  Error in DDC Program,7:VE.Bus BMS connected- which requires an Assistant- but no assistant found,10:VE.Bus Error System time synchronisation problem occurred,14:VE.Bus Error Device cannot transmit data,16:VE.Bus Error Dongle missing,17:VE.Bus Error One of the devices assumed master status because the original master failed,18:VE.Bus Error AC Overvoltage on the output of a slave has occurred while already switched off,22:VE.Bus Error This device cannot function as slave,24:VE.Bus Error Switch-over system protection initiated,25:VE.Bus Error Firmware incompatibility. The firmware of one of the connected device is not sufficiently up to date to operate in conjunction with this device,26:VE.Bus Error Internal error&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-reading vebus_error&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-map 1:Charger Only,2:Inverter Only,3:On,4:Off&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-reading switch_position&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-reading temperature_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-reading low_battery_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-reading overload_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0037-reading ESS_Power_setpoint_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0037-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-map 0:Charge allowed,1:Charge disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-reading ESS_disable_charge_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-map 0:Feed in allowed,1:Feed in disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-reading ESS_disable_feedback_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0040-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0040-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0041-reading ESS_Power_setpoint_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0041-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-reading temperature_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-reading voltage_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-reading temperature_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-reading low_battery_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-reading overload_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-reading ripple_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-reading temperature_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-reading low_battery_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-reading overload_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-reading ripple_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-reading temperature_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-reading low_battery_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-reading overload_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-reading ripple_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-map 0:PV enabled,1:PV disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-reading disable_pv_inverter&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-reading bms_allow_charge&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-reading bms_allow_discharge&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-reading bms_expected&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-reading bms_error&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-reading temperature_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-map 1:VE.Bus reset&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-reading vebus_reset&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-map 0:Ok,1:Warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-reading phase_rotation_warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-map 0:Ok,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-reading grid_lost_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-map 0:Feed in overvoltage,1:Do not feed in overvoltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-reading feed_dc_overvoltage_into_grid&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-reading maximum_overvoltage_feedin_power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-reading maximum_overvoltage_feedin_power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-reading maximum_overvoltage_feedin_power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-map 0:AC input not ignored,1:AC input ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-reading ac_input_1_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-map 0:AC input not ignored,1:AC input ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-reading ac_input_2_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-map 0:AcPowerSetpoint interpreted normally, 1:AcPowerSetpoint is OvervoltageFeedIn limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-reading ac_power_setpoint_acts_as_feedin_limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-map 0:OvervoltageFeedIn uses 1V offset, 1:OvervoltageFeedIn uses 0.1V offset&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-reading solar_offset_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-map 0:Sustain inactive, 1:Sustain active&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-reading sustain_active&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-reading energy_from_acIn1_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-reading energy_from_acIn1_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-reading energy_from_acIn2_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-reading energy_from_acIn2_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-reading energy_from_acOut_to_acIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-reading energy_from_acOut_to_acIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-reading energy_from_battery_to_AcIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-reading energy_from_battery_to_AcIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-reading energy_from_battery_to_AcOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-reading energy_from_AcOut_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-unpack N&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-map 0:OK,1:Warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-reading low_cell_voltage_imminent&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-map 0:Initialising,1:Bulk,2:Absorption,3:Float,4:Storage,5:Absorb repeat,6:Forced absorb,7:Equalise,8:Bulk stopped,9:Unknown&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-reading charge_state&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0096-reading ESS_Power_setpoint_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0096-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-map 0:Renewable energy not preferred,1:Renewable energy preferred&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-reading prefer_renewable_energy&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-map 0:Generator not selected,1:Generator selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-reading select_remote_generator&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-map 0:Generator not selected,1:Generator selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-reading remote_generator_selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-map 0:No Action, 1:Redetect System&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-reading redetect_vebus_system&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-reading powerAssist_boost_factor&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-reading configured_output_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-map 0:disabled, 1:enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-reading powerAssist_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-map 0:disabled, 1:enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-reading ups_function_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h10-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h10-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h10-reading AC_Input_Frequency_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h10-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h100-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h100-reading ESS_Power_setpoint_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h100-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h102-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h102-reading prefer_renewable_energy&lt;br /&gt;
attr MultiPlusII_227 obj-h102-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h103-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h103-reading select_remote_generator&lt;br /&gt;
attr MultiPlusII_227 obj-h103-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h104-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h104-reading remote_generator_selected&lt;br /&gt;
attr MultiPlusII_227 obj-h104-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h105-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h105-reading redetect_vebus_system&lt;br /&gt;
attr MultiPlusII_227 obj-h105-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h106-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h106-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h106-reading powerAssist_boost_factor&lt;br /&gt;
attr MultiPlusII_227 obj-h106-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h107-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h107-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h107-reading configured_output_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h107-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h108-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h108-reading powerAssist_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h108-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h109-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h109-reading ups_function_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h109-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h11-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h11-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h11-reading AC_Input_Frequency_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h11-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h12-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h12-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h12-reading AC_Input_Power_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h12-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h13-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h13-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h13-reading AC_Input_Power_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h13-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h14-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h14-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h14-reading AC_Input_Power_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h14-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h15-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h15-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h15-reading AC_Output_Voltage_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h15-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h16-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h16-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h16-reading AC_Output_Voltage_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h16-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h17-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h17-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h17-reading AC_Output_Voltage_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h17-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h18-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h18-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h18-reading AC_Output_Current_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h18-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h19-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h19-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h19-reading AC_Output_Current_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h19-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h20-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h20-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h20-reading AC_Output_Current_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h20-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h21-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h21-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h21-reading AC_Output_Frequency&lt;br /&gt;
attr MultiPlusII_227 obj-h21-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h22-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h22-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h22-reading Active_Input_current_Limit&lt;br /&gt;
attr MultiPlusII_227 obj-h22-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h23-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h23-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h23-reading AC_Output_Power_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h23-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h24-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h24-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h24-reading AC_Output_Power_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h24-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h25-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h25-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h25-reading AC_Output_Power_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h25-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h26-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h26-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h26-reading Battery_Voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h26-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h27-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h27-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h27-reading Battery_Current&lt;br /&gt;
attr MultiPlusII_227 obj-h27-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h28-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h28-reading Phase_Count&lt;br /&gt;
attr MultiPlusII_227 obj-h28-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h29-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h29-reading Active_Input&lt;br /&gt;
attr MultiPlusII_227 obj-h29-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h3-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h3-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h3-reading AC_Input_Voltage_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h3-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h30-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h30-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h30-reading vebus_soc&lt;br /&gt;
attr MultiPlusII_227 obj-h30-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h31-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h31-reading vebus_state&lt;br /&gt;
attr MultiPlusII_227 obj-h31-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h32-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h32-reading vebus_error&lt;br /&gt;
attr MultiPlusII_227 obj-h32-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h33-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h33-reading switch_position&lt;br /&gt;
attr MultiPlusII_227 obj-h33-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h34-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h34-reading temperature_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h34-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h35-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h35-reading low_battery_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h35-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h36-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h36-reading overload_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h36-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h37-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h37-reading ESS_Power_setpoint_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h37-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h38-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h38-reading ESS_disable_charge_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h38-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h39-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h39-reading ESS_disable_feedback_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h39-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h4-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h4-len 1	2&lt;br /&gt;
attr MultiPlusII_227 obj-h4-reading AC_Input_Voltage_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h4-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h40-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h40-reading ESS_Power_setpoint_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h40-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h41-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h41-reading ESS_Power_setpoint_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h41-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h42-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h42-reading temperature_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h42-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h43-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h43-reading voltage_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h43-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h44-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h44-reading temperature_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h44-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h45-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h45-reading low_battery_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h45-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h46-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h46-reading overload_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h46-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h47-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h47-reading ripple_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h47-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h48-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h48-reading temperature_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h48-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h49-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h49-reading low_battery_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h49-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h5-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h5-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h5-reading AC_Input_Voltage_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h5-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h50-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h50-reading overload_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h50-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h51-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h51-reading ripple_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h51-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h52-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h52-reading temperature_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h52-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h53-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h53-reading low_battery_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h53-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h54-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h54-reading overload_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h54-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h55-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h55-reading ripple_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h55-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h56-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h56-reading disable_pv_inverter&lt;br /&gt;
attr MultiPlusII_227 obj-h56-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h57-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h57-reading bms_allow_charge&lt;br /&gt;
attr MultiPlusII_227 obj-h57-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h58-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h58-reading bms_allow_discharge&lt;br /&gt;
attr MultiPlusII_227 obj-h58-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h59-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h59-reading bms_expected&lt;br /&gt;
attr MultiPlusII_227 obj-h59-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h6-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h6-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h6-reading AC_Input_Current_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h6-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h60-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h60-reading bms_error&lt;br /&gt;
attr MultiPlusII_227 obj-h60-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h61-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h61-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h61-reading temperature_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h61-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h62-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h62-reading vebus_reset&lt;br /&gt;
attr MultiPlusII_227 obj-h62-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h63-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h63-reading phase_rotation_warning&lt;br /&gt;
attr MultiPlusII_227 obj-h63-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h64-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h64-reading grid_lost_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h64-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h65-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h65-reading feed_dc_overvoltage_into_grid&lt;br /&gt;
attr MultiPlusII_227 obj-h65-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h66-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h66-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h66-reading maximum_overvoltage_feedin_power_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h66-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h67-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h67-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h67-reading maximum_overvoltage_feedin_power_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h67-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h68-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h68-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h68-reading maximum_overvoltage_feedin_power_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h68-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h69-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h69-reading ac_input_1_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h69-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h7-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h7-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h7-reading AC_Input_Current_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h7-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h70-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h70-reading ac_input_2_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h70-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h71-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h71-reading ac_power_setpoint_acts_as_feedin_limit&lt;br /&gt;
attr MultiPlusII_227 obj-h71-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h72-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h72-reading solar_offset_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h72-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h73-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h73-reading sustain_active&lt;br /&gt;
attr MultiPlusII_227 obj-h73-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h74-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h74-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h74-reading energy_from_acIn1_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h74-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h76-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h76-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h76-reading energy_from_acIn1_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h76-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h78-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h78-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h78-reading energy_from_acIn2_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h78-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h8-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h8-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h8-reading AC_Input_Current_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h8-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h80-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h80-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h80-reading energy_from_acIn2_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h80-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h82-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h82-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h82-reading energy_from_acOut_to_acIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h82-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h84-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h84-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h84-reading energy_from_acOut_to_acIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h84-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h86-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h86-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h86-reading energy_from_battery_to_AcIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h86-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h88-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h88-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h88-reading energy_from_battery_to_AcIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h88-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h9-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h9-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h9-reading AC_Input_Frequency_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h9-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h90-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h90-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h90-reading energy_from_battery_to_AcOut&lt;br /&gt;
attr MultiPlusII_227 obj-h90-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h92-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h92-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h92-reading energy_from_AcOut_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h92-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h94-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h94-reading low_cell_voltage_imminent&lt;br /&gt;
attr MultiPlusII_227 obj-h94-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h95-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h95-reading charge_state&lt;br /&gt;
attr MultiPlusII_227 obj-h95-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h96-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h96-reading ESS_Power_setpoint_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h96-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h98-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h98-reading ESS_Power_setpoint_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h98-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 room Solar-&amp;gt;Victron&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Visualisierung==&lt;br /&gt;
&#039;&#039;&#039;stateFormat&#039;&#039;&#039; kann so aussehen:&lt;br /&gt;
 SSOC Batteries_value_1_soc, &lt;br /&gt;
 SOC: Batteries_value_1_soc % (MinimumSocLimit_value), &lt;br /&gt;
 Bat: Batteries_value_1_power W,&lt;br /&gt;
 PV: PVPower_value W,&lt;br /&gt;
 ACin: GridPower_value W, &lt;br /&gt;
 ACout2: P_value W,&lt;br /&gt;
 State: State_value,&lt;br /&gt;
 Mode: Mode_value&lt;br /&gt;
SSOC wird später für das devStateIcon genutzt.&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;devStateIcon&#039;&#039;&#039; bietet einen ersten Blick auf den Ladestand:&lt;br /&gt;
 SSOC\s[2|3|4].+:measure_battery_25&lt;br /&gt;
 SSOC\s[5|6].+:measure_battery_50&lt;br /&gt;
 SSOC\s[7|8].+:measure_battery_75&lt;br /&gt;
 SSOC\s[9].+:measure_battery_100&lt;br /&gt;
&lt;br /&gt;
Derzeit existiert noch kein Template, aber das kann sich ja ändern.&lt;br /&gt;
&lt;br /&gt;
==Externe Links==&lt;br /&gt;
*[https://www.victronenergy.com/support-and-downloads/technical-information Victron Technical Info]&lt;br /&gt;
*[https://vrm-api-docs.victronenergy.com/#/ VRm API]&lt;br /&gt;
*[https://github.com/victronenergy/dbus-mqtt Venus MQTT Broker]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Victron&amp;diff=40049</id>
		<title>Victron</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Victron&amp;diff=40049"/>
		<updated>2025-03-09T17:22:34Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Einbindung über ModbusAttr */  Überschrift eingefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Übersicht==&lt;br /&gt;
Die Familie der Solarlade und -speichergeräte von Victron verwenden verschiedene Bussysteme, um untereinander zu kommunizieren. Sehr angenehm ist, daß die zentrale Steuerung namens Venus (Victron ENergy Unix System :-) frei verfügbar ist, und gern auf einem RaspberryPi läuft. Integriert ist gleich ein [https://de.wikipedia.org/wiki/MQTT MQTT] Server (mit und ohne SSL), der die Meßwerte der angeschlossenen Geräte - und das sind viele - per MQTT anbietet.&lt;br /&gt;
&lt;br /&gt;
Victron ist generell gut dokumentiert: https://www.victronenergy.com/support-and-downloads/technical-information&lt;br /&gt;
&lt;br /&gt;
==Einbindung in fhem als MQTT2_DEVICE==&lt;br /&gt;
Über MQTT2_CLIENT wird zunächst die Verbindung zum Broker auf dem Victron Venus Gerät angelegt. (Hier ist die IP noch anzupasssen.)&lt;br /&gt;
 defmod venus MQTT2_CLIENT 10.1.x.x:1883&lt;br /&gt;
 attr venus autocreate complex&lt;br /&gt;
 attr venus clientOrder MQTT_GENERIC_BRIDGE MQTT2_DEVICE&lt;br /&gt;
 attr venus room MQTT2_DEVICE&lt;br /&gt;
 attr venus verbose 4&lt;br /&gt;
&lt;br /&gt;
Das Wechselrichter wird dann als MQTT2_DEVICE angelegt: &lt;br /&gt;
 defmod MQTT2_mp2 MQTT2_DEVICE venus&lt;br /&gt;
 attr MQTT2_mp2 IODev venus&lt;br /&gt;
&lt;br /&gt;
===== Aufbau der Topics von Victron =====&lt;br /&gt;
Jede Installation &#039;&#039;Venus&#039;&#039; gibt sich eine vrm_ID, die initial aus der Hardwareadresse abgeleitet wird. Über diese ID wird die Installation auf dem [https:///vrm.victronenergy.com Victron VRM Portal] erkannt. In den Beispielen unten taucht sie als &amp;lt;vrm_ID&amp;gt; auf. Die einzelnen Geräte einer Installation erhalten Nummern, die eine Unterscheidung auch mehrerer Wechselrichter (etwa im 3-Phasenbetrieb) oder mehrerer Solarlader ermöglichen. &lt;br /&gt;
&lt;br /&gt;
Nach Verbindung erscheint eine umfangreiche readingList, aus der hier nur einige Elemente verwendet werden. &lt;br /&gt;
* system - Konfiguration des Gesamtsystems bzw. konsolidierte Werte&lt;br /&gt;
* solarcharger - MPPT-DC-Lader, Anschluß VE-Direct (seriell, baut auf RS-485) , Wartung per Bluetooth und App&lt;br /&gt;
* grid - [https://www.victronenergy.com/accessories/energy-meter Stromzähler] , Anschluß über RS-485 / wahlweise Ethernet &lt;br /&gt;
* vebus - Wechselrichter, Anschluß über Cat5, serielles Protokoll&lt;br /&gt;
&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/system/0/Batteries:.* { json2nameValue($EVENT, &#039;Batteries_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/solarcharger/290/Yield/Power:.* { json2nameValue($EVENT, &#039;PVPower_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/grid/30/Ac/Power:.* { json2nameValue($EVENT, &#039;GridPower_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/vebus/288/Ac/Out/P:.* { json2nameValue($EVENT, &#039;P_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/system/0/SystemState/State:.* { json2nameValue($EVENT, &#039;State_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode:.* { json2nameValue($EVENT, &#039;Mode_&#039;, $JSONMAP) }&lt;br /&gt;
&lt;br /&gt;
===== Topics zum Schreiben =====&lt;br /&gt;
Einige Enstellungen sind schreibbar. Die entsprechenden Topics beginnen mit einem W. Eine setList kann beispielsweise so aussehen und erlaubt dann &lt;br /&gt;
* An- und Abschalten&lt;br /&gt;
* Inverter- und Charger Mode&lt;br /&gt;
* Mode: Betriebszustand&lt;br /&gt;
* CurrentLimit: max. Strom aus/in das Netz&lt;br /&gt;
* MinimumSocLimit: Entladegranze im ESS (Speicher-)Betrieb, solange das Netz anliegt&lt;br /&gt;
* Relay[0|1]: Schalte interne Relais&lt;br /&gt;
Eine einfache Setlist:&lt;br /&gt;
&lt;br /&gt;
 off W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:4}&lt;br /&gt;
 on W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:3}&lt;br /&gt;
 inv W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:2}&lt;br /&gt;
 chg W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:1}&lt;br /&gt;
 Mode W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 GridCurrentLimit W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Ac/In/1/CurrentLimit {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 MinSOC W/&amp;lt;vrm_ID&amp;gt;/settings/0/Settings/CGwacs/BatteryLife/MinimumSocLimit {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 Relay0 W/&amp;lt;vrm_ID&amp;gt;/system/0/Relay/0/State:.* {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 Relay1 W/&amp;lt;vrm_ID&amp;gt;/system/0/Relay/1/State:.* {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einbindung über ModbusAttr== &lt;br /&gt;
Die verschiedenen Devices werden intern über verschieden Modbusadressen abgefragt. &lt;br /&gt;
Dies kann man dem Excel &amp;quot;Modbus-TCP Register List unter Link 1 entnehmen. &lt;br /&gt;
&lt;br /&gt;
Hier ist der Code für ein MultiPlus II-5000 Device (die Unit ID 227 verweist auf den VE.Bus-Port an ttyS4, an welchem ausschließlich jener Multiplus angeschlossen ist). Leider kann ich es mangels Geräten nicht testen, aber ich vermute, dass wenn der Multiplus an einen anderen Port angeschlossen wird, lediglich die Modbus-ID zu ändern ist.&lt;br /&gt;
&lt;br /&gt;
=== Multiplus II ===&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
defmod MultiPlusII_227 ModbusAttr 227 5 192.168.243.38:502 TCP&lt;br /&gt;
attr MultiPlusII_227 dev-h-combine 5&lt;br /&gt;
attr MultiPlusII_227 dev-h-defPoll 1&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-reading AC_Input_Voltage_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-reading AC_Input_Voltage_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-reading AC_Input_Voltage_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-reading AC_Input_Current_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-reading AC_Input_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-reading AC_Input_Current_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-reading AC_Input_Frequency_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-reading AC_Input_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-reading AC_Input_Frequency_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-reading AC_Input_Power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-reading AC_Input_Power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-reading AC_Input_Power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-reading AC_Output_Voltage_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-reading AC_Output_Voltage_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-reading AC_Output_Voltage_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-reading AC_Output_Current_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-reading AC_Output_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-reading AC_Output_Current_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-reading AC_Output_Frequency&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-reading Active_Input_current_Limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-reading AC_Output_Power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-reading AC_Output_Power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-reading AC_Output_Power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-reading Battery_Voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-reading Battery_Current&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0028-reading Phase_Count&lt;br /&gt;
attr MultiPlusII_227 obj-h0028-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-map 0:AC Input 1,1:AC Input 2,240:Disconnected&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-reading Active_Input&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-reading vebus_soc&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-map 0:Off,1:Low Power,2:Fault,3:Bulk,4:Absorption,5:Float,6:Storage,7:Equalize,8:Passthru,9:Inverting,10:Power assist,11:Power supply,244:Sustain,252:External controL&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-reading vebus_state&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-map 0:No error,1:VE.Bus Error Device is switched off because one of the other phases in the system has switched off,2:VE.Bus Error New and old types MK2 are mixed in the system,3:VE.Bus Error Not all- or more than- the expected devices were found in the system,4:VE.Bus Error No other device whatsoever detected,5:VE.Bus Error Overvoltage on AC-out,6:VE.Bus Error  Error in DDC Program,7:VE.Bus BMS connected- which requires an Assistant- but no assistant found,10:VE.Bus Error System time synchronisation problem occurred,14:VE.Bus Error Device cannot transmit data,16:VE.Bus Error Dongle missing,17:VE.Bus Error One of the devices assumed master status because the original master failed,18:VE.Bus Error AC Overvoltage on the output of a slave has occurred while already switched off,22:VE.Bus Error This device cannot function as slave,24:VE.Bus Error Switch-over system protection initiated,25:VE.Bus Error Firmware incompatibility. The firmware of one of the connected device is not sufficiently up to date to operate in conjunction with this device,26:VE.Bus Error Internal error&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-reading vebus_error&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-map 1:Charger Only,2:Inverter Only,3:On,4:Off&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-reading switch_position&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-reading temperature_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-reading low_battery_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-reading overload_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0037-reading ESS_Power_setpoint_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0037-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-map 0:Charge allowed,1:Charge disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-reading ESS_disable_charge_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-map 0:Feed in allowed,1:Feed in disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-reading ESS_disable_feedback_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0040-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0040-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0041-reading ESS_Power_setpoint_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0041-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-reading temperature_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-reading voltage_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-reading temperature_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-reading low_battery_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-reading overload_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-reading ripple_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-reading temperature_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-reading low_battery_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-reading overload_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-reading ripple_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-reading temperature_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-reading low_battery_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-reading overload_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-reading ripple_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-map 0:PV enabled,1:PV disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-reading disable_pv_inverter&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-reading bms_allow_charge&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-reading bms_allow_discharge&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-reading bms_expected&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-reading bms_error&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-reading temperature_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-map 1:VE.Bus reset&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-reading vebus_reset&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-map 0:Ok,1:Warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-reading phase_rotation_warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-map 0:Ok,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-reading grid_lost_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-map 0:Feed in overvoltage,1:Do not feed in overvoltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-reading feed_dc_overvoltage_into_grid&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-reading maximum_overvoltage_feedin_power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-reading maximum_overvoltage_feedin_power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-reading maximum_overvoltage_feedin_power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-map 0:AC input not ignored,1:AC input ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-reading ac_input_1_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-map 0:AC input not ignored,1:AC input ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-reading ac_input_2_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-map 0:AcPowerSetpoint interpreted normally, 1:AcPowerSetpoint is OvervoltageFeedIn limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-reading ac_power_setpoint_acts_as_feedin_limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-map 0:OvervoltageFeedIn uses 1V offset, 1:OvervoltageFeedIn uses 0.1V offset&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-reading solar_offset_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-map 0:Sustain inactive, 1:Sustain active&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-reading sustain_active&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-reading energy_from_acIn1_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-reading energy_from_acIn1_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-reading energy_from_acIn2_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-reading energy_from_acIn2_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-reading energy_from_acOut_to_acIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-reading energy_from_acOut_to_acIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-reading energy_from_battery_to_AcIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-reading energy_from_battery_to_AcIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-reading energy_from_battery_to_AcOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-reading energy_from_AcOut_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-map 0:OK,1:Warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-reading low_cell_voltage_imminent&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-map 0:Initialising,1:Bulk,2:Absorption,3:Float,4:Storage,5:Absorb repeat,6:Forced absorb,7:Equalise,8:Bulk stopped,9:Unknown&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-reading charge_state&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0096-reading ESS_Power_setpoint_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0096-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-map 0:Renewable energy not preferred,1:Renewable energy preferred&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-reading prefer_renewable_energy&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-map 0:Generator not selected,1:Generator selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-reading select_remote_generator&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-map 0:Generator not selected,1:Generator selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-reading remote_generator_selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-map 0:No Action, 1:Redetect System&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-reading redetect_vebus_system&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-reading powerAssist_boost_factor&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-reading configured_output_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-map 0:disabled, 1:enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-reading powerAssist_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-map 0:disabled, 1:enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-reading ups_function_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h10-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h10-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h10-reading AC_Input_Frequency_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h10-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h100-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h100-reading ESS_Power_setpoint_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h100-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h102-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h102-reading prefer_renewable_energy&lt;br /&gt;
attr MultiPlusII_227 obj-h102-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h103-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h103-reading select_remote_generator&lt;br /&gt;
attr MultiPlusII_227 obj-h103-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h104-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h104-reading remote_generator_selected&lt;br /&gt;
attr MultiPlusII_227 obj-h104-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h105-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h105-reading redetect_vebus_system&lt;br /&gt;
attr MultiPlusII_227 obj-h105-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h106-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h106-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h106-reading powerAssist_boost_factor&lt;br /&gt;
attr MultiPlusII_227 obj-h106-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h107-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h107-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h107-reading configured_output_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h107-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h108-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h108-reading powerAssist_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h108-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h109-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h109-reading ups_function_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h109-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h11-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h11-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h11-reading AC_Input_Frequency_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h11-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h12-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h12-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h12-reading AC_Input_Power_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h12-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h13-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h13-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h13-reading AC_Input_Power_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h13-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h14-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h14-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h14-reading AC_Input_Power_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h14-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h15-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h15-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h15-reading AC_Output_Voltage_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h15-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h16-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h16-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h16-reading AC_Output_Voltage_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h16-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h17-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h17-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h17-reading AC_Output_Voltage_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h17-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h18-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h18-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h18-reading AC_Output_Current_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h18-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h19-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h19-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h19-reading AC_Output_Current_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h19-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h20-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h20-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h20-reading AC_Output_Current_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h20-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h21-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h21-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h21-reading AC_Output_Frequency&lt;br /&gt;
attr MultiPlusII_227 obj-h21-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h22-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h22-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h22-reading Active_Input_current_Limit&lt;br /&gt;
attr MultiPlusII_227 obj-h22-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h23-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h23-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h23-reading AC_Output_Power_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h23-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h24-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h24-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h24-reading AC_Output_Power_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h24-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h25-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h25-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h25-reading AC_Output_Power_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h25-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h26-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h26-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h26-reading Battery_Voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h26-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h27-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h27-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h27-reading Battery_Current&lt;br /&gt;
attr MultiPlusII_227 obj-h27-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h28-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h28-reading Phase_Count&lt;br /&gt;
attr MultiPlusII_227 obj-h28-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h29-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h29-reading Active_Input&lt;br /&gt;
attr MultiPlusII_227 obj-h29-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h3-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h3-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h3-reading AC_Input_Voltage_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h3-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h30-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h30-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h30-reading vebus_soc&lt;br /&gt;
attr MultiPlusII_227 obj-h30-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h31-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h31-reading vebus_state&lt;br /&gt;
attr MultiPlusII_227 obj-h31-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h32-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h32-reading vebus_error&lt;br /&gt;
attr MultiPlusII_227 obj-h32-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h33-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h33-reading switch_position&lt;br /&gt;
attr MultiPlusII_227 obj-h33-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h34-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h34-reading temperature_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h34-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h35-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h35-reading low_battery_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h35-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h36-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h36-reading overload_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h36-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h37-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h37-reading ESS_Power_setpoint_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h37-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h38-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h38-reading ESS_disable_charge_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h38-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h39-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h39-reading ESS_disable_feedback_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h39-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h4-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h4-len 1	2&lt;br /&gt;
attr MultiPlusII_227 obj-h4-reading AC_Input_Voltage_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h4-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h40-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h40-reading ESS_Power_setpoint_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h40-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h41-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h41-reading ESS_Power_setpoint_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h41-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h42-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h42-reading temperature_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h42-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h43-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h43-reading voltage_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h43-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h44-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h44-reading temperature_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h44-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h45-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h45-reading low_battery_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h45-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h46-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h46-reading overload_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h46-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h47-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h47-reading ripple_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h47-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h48-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h48-reading temperature_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h48-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h49-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h49-reading low_battery_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h49-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h5-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h5-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h5-reading AC_Input_Voltage_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h5-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h50-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h50-reading overload_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h50-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h51-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h51-reading ripple_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h51-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h52-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h52-reading temperature_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h52-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h53-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h53-reading low_battery_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h53-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h54-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h54-reading overload_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h54-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h55-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h55-reading ripple_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h55-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h56-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h56-reading disable_pv_inverter&lt;br /&gt;
attr MultiPlusII_227 obj-h56-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h57-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h57-reading bms_allow_charge&lt;br /&gt;
attr MultiPlusII_227 obj-h57-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h58-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h58-reading bms_allow_discharge&lt;br /&gt;
attr MultiPlusII_227 obj-h58-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h59-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h59-reading bms_expected&lt;br /&gt;
attr MultiPlusII_227 obj-h59-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h6-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h6-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h6-reading AC_Input_Current_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h6-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h60-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h60-reading bms_error&lt;br /&gt;
attr MultiPlusII_227 obj-h60-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h61-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h61-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h61-reading temperature_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h61-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h62-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h62-reading vebus_reset&lt;br /&gt;
attr MultiPlusII_227 obj-h62-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h63-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h63-reading phase_rotation_warning&lt;br /&gt;
attr MultiPlusII_227 obj-h63-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h64-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h64-reading grid_lost_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h64-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h65-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h65-reading feed_dc_overvoltage_into_grid&lt;br /&gt;
attr MultiPlusII_227 obj-h65-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h66-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h66-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h66-reading maximum_overvoltage_feedin_power_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h66-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h67-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h67-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h67-reading maximum_overvoltage_feedin_power_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h67-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h68-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h68-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h68-reading maximum_overvoltage_feedin_power_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h68-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h69-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h69-reading ac_input_1_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h69-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h7-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h7-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h7-reading AC_Input_Current_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h7-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h70-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h70-reading ac_input_2_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h70-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h71-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h71-reading ac_power_setpoint_acts_as_feedin_limit&lt;br /&gt;
attr MultiPlusII_227 obj-h71-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h72-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h72-reading solar_offset_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h72-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h73-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h73-reading sustain_active&lt;br /&gt;
attr MultiPlusII_227 obj-h73-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h74-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h74-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h74-reading energy_from_acIn1_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h74-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h76-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h76-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h76-reading energy_from_acIn1_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h76-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h78-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h78-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h78-reading energy_from_acIn2_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h78-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h8-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h8-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h8-reading AC_Input_Current_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h8-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h80-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h80-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h80-reading energy_from_acIn2_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h80-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h82-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h82-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h82-reading energy_from_acOut_to_acIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h82-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h84-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h84-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h84-reading energy_from_acOut_to_acIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h84-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h86-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h86-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h86-reading energy_from_battery_to_AcIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h86-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h88-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h88-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h88-reading energy_from_battery_to_AcIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h88-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h9-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h9-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h9-reading AC_Input_Frequency_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h9-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h90-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h90-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h90-reading energy_from_battery_to_AcOut&lt;br /&gt;
attr MultiPlusII_227 obj-h90-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h92-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h92-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h92-reading energy_from_AcOut_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h92-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h94-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h94-reading low_cell_voltage_imminent&lt;br /&gt;
attr MultiPlusII_227 obj-h94-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h95-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h95-reading charge_state&lt;br /&gt;
attr MultiPlusII_227 obj-h95-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h96-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h96-reading ESS_Power_setpoint_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h96-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h98-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h98-reading ESS_Power_setpoint_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h98-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 room Solar-&amp;gt;Victron&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Visualisierung==&lt;br /&gt;
&#039;&#039;&#039;stateFormat&#039;&#039;&#039; kann so aussehen:&lt;br /&gt;
 SSOC Batteries_value_1_soc, &lt;br /&gt;
 SOC: Batteries_value_1_soc % (MinimumSocLimit_value), &lt;br /&gt;
 Bat: Batteries_value_1_power W,&lt;br /&gt;
 PV: PVPower_value W,&lt;br /&gt;
 ACin: GridPower_value W, &lt;br /&gt;
 ACout2: P_value W,&lt;br /&gt;
 State: State_value,&lt;br /&gt;
 Mode: Mode_value&lt;br /&gt;
SSOC wird später für das devStateIcon genutzt.&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;devStateIcon&#039;&#039;&#039; bietet einen ersten Blick auf den Ladestand:&lt;br /&gt;
 SSOC\s[2|3|4].+:measure_battery_25&lt;br /&gt;
 SSOC\s[5|6].+:measure_battery_50&lt;br /&gt;
 SSOC\s[7|8].+:measure_battery_75&lt;br /&gt;
 SSOC\s[9].+:measure_battery_100&lt;br /&gt;
&lt;br /&gt;
Derzeit existiert noch kein Template, aber das kann sich ja ändern.&lt;br /&gt;
&lt;br /&gt;
==Externe Links==&lt;br /&gt;
*[https://www.victronenergy.com/support-and-downloads/technical-information Victron Technical Info]&lt;br /&gt;
*[https://vrm-api-docs.victronenergy.com/#/ VRm API]&lt;br /&gt;
*[https://github.com/victronenergy/dbus-mqtt Venus MQTT Broker]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Victron&amp;diff=40048</id>
		<title>Victron</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Victron&amp;diff=40048"/>
		<updated>2025-03-09T17:21:50Z</updated>

		<summary type="html">&lt;p&gt;Stephan: fertiges, funktionsfähiges Modbusattr für einen Multiplus II hinzugefügt.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Übersicht==&lt;br /&gt;
Die Familie der Solarlade und -speichergeräte von Victron verwenden verschiedene Bussysteme, um untereinander zu kommunizieren. Sehr angenehm ist, daß die zentrale Steuerung namens Venus (Victron ENergy Unix System :-) frei verfügbar ist, und gern auf einem RaspberryPi läuft. Integriert ist gleich ein [https://de.wikipedia.org/wiki/MQTT MQTT] Server (mit und ohne SSL), der die Meßwerte der angeschlossenen Geräte - und das sind viele - per MQTT anbietet.&lt;br /&gt;
&lt;br /&gt;
Victron ist generell gut dokumentiert: https://www.victronenergy.com/support-and-downloads/technical-information&lt;br /&gt;
&lt;br /&gt;
==Einbindung in fhem als MQTT2_DEVICE==&lt;br /&gt;
Über MQTT2_CLIENT wird zunächst die Verbindung zum Broker auf dem Victron Venus Gerät angelegt. (Hier ist die IP noch anzupasssen.)&lt;br /&gt;
 defmod venus MQTT2_CLIENT 10.1.x.x:1883&lt;br /&gt;
 attr venus autocreate complex&lt;br /&gt;
 attr venus clientOrder MQTT_GENERIC_BRIDGE MQTT2_DEVICE&lt;br /&gt;
 attr venus room MQTT2_DEVICE&lt;br /&gt;
 attr venus verbose 4&lt;br /&gt;
&lt;br /&gt;
Das Wechselrichter wird dann als MQTT2_DEVICE angelegt: &lt;br /&gt;
 defmod MQTT2_mp2 MQTT2_DEVICE venus&lt;br /&gt;
 attr MQTT2_mp2 IODev venus&lt;br /&gt;
&lt;br /&gt;
===== Aufbau der Topics von Victron =====&lt;br /&gt;
Jede Installation &#039;&#039;Venus&#039;&#039; gibt sich eine vrm_ID, die initial aus der Hardwareadresse abgeleitet wird. Über diese ID wird die Installation auf dem [https:///vrm.victronenergy.com Victron VRM Portal] erkannt. In den Beispielen unten taucht sie als &amp;lt;vrm_ID&amp;gt; auf. Die einzelnen Geräte einer Installation erhalten Nummern, die eine Unterscheidung auch mehrerer Wechselrichter (etwa im 3-Phasenbetrieb) oder mehrerer Solarlader ermöglichen. &lt;br /&gt;
&lt;br /&gt;
Nach Verbindung erscheint eine umfangreiche readingList, aus der hier nur einige Elemente verwendet werden. &lt;br /&gt;
* system - Konfiguration des Gesamtsystems bzw. konsolidierte Werte&lt;br /&gt;
* solarcharger - MPPT-DC-Lader, Anschluß VE-Direct (seriell, baut auf RS-485) , Wartung per Bluetooth und App&lt;br /&gt;
* grid - [https://www.victronenergy.com/accessories/energy-meter Stromzähler] , Anschluß über RS-485 / wahlweise Ethernet &lt;br /&gt;
* vebus - Wechselrichter, Anschluß über Cat5, serielles Protokoll&lt;br /&gt;
&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/system/0/Batteries:.* { json2nameValue($EVENT, &#039;Batteries_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/solarcharger/290/Yield/Power:.* { json2nameValue($EVENT, &#039;PVPower_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/grid/30/Ac/Power:.* { json2nameValue($EVENT, &#039;GridPower_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/vebus/288/Ac/Out/P:.* { json2nameValue($EVENT, &#039;P_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/system/0/SystemState/State:.* { json2nameValue($EVENT, &#039;State_&#039;, $JSONMAP) }&lt;br /&gt;
 N/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode:.* { json2nameValue($EVENT, &#039;Mode_&#039;, $JSONMAP) }&lt;br /&gt;
&lt;br /&gt;
===== Topics zum Schreiben =====&lt;br /&gt;
Einige Enstellungen sind schreibbar. Die entsprechenden Topics beginnen mit einem W. Eine setList kann beispielsweise so aussehen und erlaubt dann &lt;br /&gt;
* An- und Abschalten&lt;br /&gt;
* Inverter- und Charger Mode&lt;br /&gt;
* Mode: Betriebszustand&lt;br /&gt;
* CurrentLimit: max. Strom aus/in das Netz&lt;br /&gt;
* MinimumSocLimit: Entladegranze im ESS (Speicher-)Betrieb, solange das Netz anliegt&lt;br /&gt;
* Relay[0|1]: Schalte interne Relais&lt;br /&gt;
Eine einfache Setlist:&lt;br /&gt;
&lt;br /&gt;
 off W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:4}&lt;br /&gt;
 on W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:3}&lt;br /&gt;
 inv W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:2}&lt;br /&gt;
 chg W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:1}&lt;br /&gt;
 Mode W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Mode {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 GridCurrentLimit W/&amp;lt;vrm_ID&amp;gt;/vebus/288/Ac/In/1/CurrentLimit {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 MinSOC W/&amp;lt;vrm_ID&amp;gt;/settings/0/Settings/CGwacs/BatteryLife/MinimumSocLimit {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 Relay0 W/&amp;lt;vrm_ID&amp;gt;/system/0/Relay/0/State:.* {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
 Relay1 W/&amp;lt;vrm_ID&amp;gt;/system/0/Relay/1/State:.* {&amp;quot;value&amp;quot;:$EVTPART1}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einbindung über ModbusAttr== &lt;br /&gt;
Die verschiedenen Devices werden intern über verschieden Modbusadressen abgefragt. &lt;br /&gt;
Dies kann man dem Excel &amp;quot;Modbus-TCP Register List unter Link 1 entnehmen. &lt;br /&gt;
&lt;br /&gt;
Hier ist der Code für ein MultiPlus II-5000 Device (die Unit ID 227 verweist auf den VE.Bus-Port an ttyS4, an welchem ausschließlich jener Multiplus angeschlossen ist). Leider kann ich es mangels Geräten nicht testen, aber ich vermute, dass wenn der Multiplus an einen anderen Port angeschlossen wird, lediglich die Modbus-ID zu ändern ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
defmod MultiPlusII_227 ModbusAttr 227 5 192.168.243.38:502 TCP&lt;br /&gt;
attr MultiPlusII_227 dev-h-combine 5&lt;br /&gt;
attr MultiPlusII_227 dev-h-defPoll 1&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-reading AC_Input_Voltage_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0003-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-reading AC_Input_Voltage_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0004-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-reading AC_Input_Voltage_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0005-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-reading AC_Input_Current_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0006-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-reading AC_Input_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0007-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-reading AC_Input_Current_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0008-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-reading AC_Input_Frequency_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0009-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-reading AC_Input_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0010-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-reading AC_Input_Frequency_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0011-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-reading AC_Input_Power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0012-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-reading AC_Input_Power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0013-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-reading AC_Input_Power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0014-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-reading AC_Output_Voltage_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0015-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-reading AC_Output_Voltage_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0016-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-reading AC_Output_Voltage_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0017-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-reading AC_Output_Current_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0018-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-reading AC_Output_Current_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0019-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-reading AC_Output_Current_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0020-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-reading AC_Output_Frequency&lt;br /&gt;
attr MultiPlusII_227 obj-h0021-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-reading Active_Input_current_Limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0022-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-reading AC_Output_Power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0023-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-reading AC_Output_Power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0024-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-reading AC_Output_Power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0025-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-reading Battery_Voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0026-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-reading Battery_Current&lt;br /&gt;
attr MultiPlusII_227 obj-h0027-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0028-reading Phase_Count&lt;br /&gt;
attr MultiPlusII_227 obj-h0028-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-map 0:AC Input 1,1:AC Input 2,240:Disconnected&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-reading Active_Input&lt;br /&gt;
attr MultiPlusII_227 obj-h0029-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-reading vebus_soc&lt;br /&gt;
attr MultiPlusII_227 obj-h0030-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-map 0:Off,1:Low Power,2:Fault,3:Bulk,4:Absorption,5:Float,6:Storage,7:Equalize,8:Passthru,9:Inverting,10:Power assist,11:Power supply,244:Sustain,252:External controL&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-reading vebus_state&lt;br /&gt;
attr MultiPlusII_227 obj-h0031-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-map 0:No error,1:VE.Bus Error Device is switched off because one of the other phases in the system has switched off,2:VE.Bus Error New and old types MK2 are mixed in the system,3:VE.Bus Error Not all- or more than- the expected devices were found in the system,4:VE.Bus Error No other device whatsoever detected,5:VE.Bus Error Overvoltage on AC-out,6:VE.Bus Error  Error in DDC Program,7:VE.Bus BMS connected- which requires an Assistant- but no assistant found,10:VE.Bus Error System time synchronisation problem occurred,14:VE.Bus Error Device cannot transmit data,16:VE.Bus Error Dongle missing,17:VE.Bus Error One of the devices assumed master status because the original master failed,18:VE.Bus Error AC Overvoltage on the output of a slave has occurred while already switched off,22:VE.Bus Error This device cannot function as slave,24:VE.Bus Error Switch-over system protection initiated,25:VE.Bus Error Firmware incompatibility. The firmware of one of the connected device is not sufficiently up to date to operate in conjunction with this device,26:VE.Bus Error Internal error&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-reading vebus_error&lt;br /&gt;
attr MultiPlusII_227 obj-h0032-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-map 1:Charger Only,2:Inverter Only,3:On,4:Off&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-reading switch_position&lt;br /&gt;
attr MultiPlusII_227 obj-h0033-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-reading temperature_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0034-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-reading low_battery_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0035-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-reading overload_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0036-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0037-reading ESS_Power_setpoint_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0037-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-map 0:Charge allowed,1:Charge disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-reading ESS_disable_charge_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h0038-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-map 0:Feed in allowed,1:Feed in disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-reading ESS_disable_feedback_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h0039-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0040-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0040-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0041-reading ESS_Power_setpoint_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0041-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-reading temperature_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0042-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-reading voltage_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0043-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-reading temperature_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0044-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-reading low_battery_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0045-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-reading overload_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0046-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-reading ripple_alarm_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0047-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-reading temperature_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0048-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-reading low_battery_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0049-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-reading overload_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0050-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-reading ripple_alarm_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0051-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-reading temperature_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0052-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-reading low_battery_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0053-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-reading overload_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0054-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-map 0:Ok,1:Warning,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-reading ripple_alarm_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0055-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-map 0:PV enabled,1:PV disabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-reading disable_pv_inverter&lt;br /&gt;
attr MultiPlusII_227 obj-h0056-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-reading bms_allow_charge&lt;br /&gt;
attr MultiPlusII_227 obj-h0057-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-reading bms_allow_discharge&lt;br /&gt;
attr MultiPlusII_227 obj-h0058-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-reading bms_expected&lt;br /&gt;
attr MultiPlusII_227 obj-h0059-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-map 0:No,1:Yes&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-reading bms_error&lt;br /&gt;
attr MultiPlusII_227 obj-h0060-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-reading temperature_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0061-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-map 1:VE.Bus reset&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-reading vebus_reset&lt;br /&gt;
attr MultiPlusII_227 obj-h0062-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-map 0:Ok,1:Warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-reading phase_rotation_warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0063-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-map 0:Ok,2:Alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-reading grid_lost_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h0064-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-map 0:Feed in overvoltage,1:Do not feed in overvoltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-reading feed_dc_overvoltage_into_grid&lt;br /&gt;
attr MultiPlusII_227 obj-h0065-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-reading maximum_overvoltage_feedin_power_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0066-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-reading maximum_overvoltage_feedin_power_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0067-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-reading maximum_overvoltage_feedin_power_L3&lt;br /&gt;
attr MultiPlusII_227 obj-h0068-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-map 0:AC input not ignored,1:AC input ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-reading ac_input_1_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0069-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-map 0:AC input not ignored,1:AC input ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-reading ac_input_2_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h0070-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-map 0:AcPowerSetpoint interpreted normally, 1:AcPowerSetpoint is OvervoltageFeedIn limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-reading ac_power_setpoint_acts_as_feedin_limit&lt;br /&gt;
attr MultiPlusII_227 obj-h0071-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-map 0:OvervoltageFeedIn uses 1V offset, 1:OvervoltageFeedIn uses 0.1V offset&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-reading solar_offset_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0072-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-map 0:Sustain inactive, 1:Sustain active&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-reading sustain_active&lt;br /&gt;
attr MultiPlusII_227 obj-h0073-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-reading energy_from_acIn1_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0074-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-reading energy_from_acIn1_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0076-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-reading energy_from_acIn2_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0078-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-reading energy_from_acIn2_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0080-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-reading energy_from_acOut_to_acIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h0082-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-reading energy_from_acOut_to_acIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h0084-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-reading energy_from_battery_to_AcIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h0086-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-reading energy_from_battery_to_AcIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h0088-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-reading energy_from_battery_to_AcOut&lt;br /&gt;
attr MultiPlusII_227 obj-h0090-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-reading energy_from_AcOut_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h0092-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-map 0:OK,1:Warning&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-reading low_cell_voltage_imminent&lt;br /&gt;
attr MultiPlusII_227 obj-h0094-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-map 0:Initialising,1:Bulk,2:Absorption,3:Float,4:Storage,5:Absorb repeat,6:Forced absorb,7:Equalise,8:Bulk stopped,9:Unknown&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-reading charge_state&lt;br /&gt;
attr MultiPlusII_227 obj-h0095-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0096-reading ESS_Power_setpoint_L1&lt;br /&gt;
attr MultiPlusII_227 obj-h0096-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0098-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-len 2&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-reading ESS_Power_setpoint_L2&lt;br /&gt;
attr MultiPlusII_227 obj-h0100-unpack f&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-map 0:Renewable energy not preferred,1:Renewable energy preferred&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-reading prefer_renewable_energy&lt;br /&gt;
attr MultiPlusII_227 obj-h0102-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-map 0:Generator not selected,1:Generator selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-reading select_remote_generator&lt;br /&gt;
attr MultiPlusII_227 obj-h0103-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-map 0:Generator not selected,1:Generator selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-reading remote_generator_selected&lt;br /&gt;
attr MultiPlusII_227 obj-h0104-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-map 0:No Action, 1:Redetect System&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-reading redetect_vebus_system&lt;br /&gt;
attr MultiPlusII_227 obj-h0105-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-reading powerAssist_boost_factor&lt;br /&gt;
attr MultiPlusII_227 obj-h0106-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-reading configured_output_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h0107-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-map 0:disabled, 1:enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-reading powerAssist_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0108-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-map 0:disabled, 1:enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-reading ups_function_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h0109-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h10-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h10-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h10-reading AC_Input_Frequency_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h10-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h100-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h100-reading ESS_Power_setpoint_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h100-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h102-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h102-reading prefer_renewable_energy&lt;br /&gt;
attr MultiPlusII_227 obj-h102-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h103-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h103-reading select_remote_generator&lt;br /&gt;
attr MultiPlusII_227 obj-h103-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h104-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h104-reading remote_generator_selected&lt;br /&gt;
attr MultiPlusII_227 obj-h104-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h105-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h105-reading redetect_vebus_system&lt;br /&gt;
attr MultiPlusII_227 obj-h105-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h106-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h106-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h106-reading powerAssist_boost_factor&lt;br /&gt;
attr MultiPlusII_227 obj-h106-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h107-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h107-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h107-reading configured_output_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h107-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h108-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h108-reading powerAssist_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h108-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h109-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h109-reading ups_function_enabled&lt;br /&gt;
attr MultiPlusII_227 obj-h109-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h11-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h11-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h11-reading AC_Input_Frequency_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h11-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h12-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h12-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h12-reading AC_Input_Power_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h12-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h13-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h13-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h13-reading AC_Input_Power_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h13-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h14-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h14-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h14-reading AC_Input_Power_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h14-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h15-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h15-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h15-reading AC_Output_Voltage_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h15-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h16-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h16-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h16-reading AC_Output_Voltage_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h16-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h17-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h17-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h17-reading AC_Output_Voltage_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h17-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h18-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h18-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h18-reading AC_Output_Current_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h18-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h19-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h19-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h19-reading AC_Output_Current_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h19-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h20-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h20-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h20-reading AC_Output_Current_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h20-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h21-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h21-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h21-reading AC_Output_Frequency&lt;br /&gt;
attr MultiPlusII_227 obj-h21-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h22-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h22-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h22-reading Active_Input_current_Limit&lt;br /&gt;
attr MultiPlusII_227 obj-h22-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h23-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h23-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h23-reading AC_Output_Power_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h23-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h24-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h24-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h24-reading AC_Output_Power_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h24-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h25-expr $val * 10&lt;br /&gt;
attr MultiPlusII_227 obj-h25-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h25-reading AC_Output_Power_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h25-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h26-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h26-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h26-reading Battery_Voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h26-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h27-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h27-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h27-reading Battery_Current&lt;br /&gt;
attr MultiPlusII_227 obj-h27-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h28-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h28-reading Phase_Count&lt;br /&gt;
attr MultiPlusII_227 obj-h28-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h29-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h29-reading Active_Input&lt;br /&gt;
attr MultiPlusII_227 obj-h29-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h3-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h3-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h3-reading AC_Input_Voltage_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h3-unpack n&lt;br /&gt;
attr MultiPlusII_227 obj-h30-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h30-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h30-reading vebus_soc&lt;br /&gt;
attr MultiPlusII_227 obj-h30-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h31-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h31-reading vebus_state&lt;br /&gt;
attr MultiPlusII_227 obj-h31-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h32-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h32-reading vebus_error&lt;br /&gt;
attr MultiPlusII_227 obj-h32-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h33-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h33-reading switch_position&lt;br /&gt;
attr MultiPlusII_227 obj-h33-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h34-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h34-reading temperature_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h34-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h35-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h35-reading low_battery_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h35-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h36-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h36-reading overload_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h36-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h37-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h37-reading ESS_Power_setpoint_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h37-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h38-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h38-reading ESS_disable_charge_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h38-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h39-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h39-reading ESS_disable_feedback_flag_phase&lt;br /&gt;
attr MultiPlusII_227 obj-h39-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h4-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h4-len 1	2&lt;br /&gt;
attr MultiPlusII_227 obj-h4-reading AC_Input_Voltage_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h4-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h40-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h40-reading ESS_Power_setpoint_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h40-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h41-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h41-reading ESS_Power_setpoint_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h41-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h42-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h42-reading temperature_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h42-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h43-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h43-reading voltage_sensor_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h43-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h44-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h44-reading temperature_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h44-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h45-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h45-reading low_battery_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h45-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h46-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h46-reading overload_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h46-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h47-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h47-reading ripple_alarm_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h47-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h48-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h48-reading temperature_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h48-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h49-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h49-reading low_battery_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h49-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h5-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h5-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h5-reading AC_Input_Voltage_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h5-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h50-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h50-reading overload_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h50-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h51-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h51-reading ripple_alarm_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h51-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h52-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h52-reading temperature_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h52-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h53-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h53-reading low_battery_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h53-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h54-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h54-reading overload_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h54-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h55-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h55-reading ripple_alarm_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h55-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h56-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h56-reading disable_pv_inverter&lt;br /&gt;
attr MultiPlusII_227 obj-h56-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h57-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h57-reading bms_allow_charge&lt;br /&gt;
attr MultiPlusII_227 obj-h57-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h58-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h58-reading bms_allow_discharge&lt;br /&gt;
attr MultiPlusII_227 obj-h58-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h59-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h59-reading bms_expected&lt;br /&gt;
attr MultiPlusII_227 obj-h59-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h6-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h6-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h6-reading AC_Input_Current_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h6-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h60-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h60-reading bms_error&lt;br /&gt;
attr MultiPlusII_227 obj-h60-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h61-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h61-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h61-reading temperature_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h61-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h62-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h62-reading vebus_reset&lt;br /&gt;
attr MultiPlusII_227 obj-h62-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h63-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h63-reading phase_rotation_warning&lt;br /&gt;
attr MultiPlusII_227 obj-h63-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h64-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h64-reading grid_lost_alarm&lt;br /&gt;
attr MultiPlusII_227 obj-h64-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h65-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h65-reading feed_dc_overvoltage_into_grid&lt;br /&gt;
attr MultiPlusII_227 obj-h65-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h66-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h66-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h66-reading maximum_overvoltage_feedin_power_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h66-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h67-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h67-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h67-reading maximum_overvoltage_feedin_power_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h67-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h68-expr $val * 100&lt;br /&gt;
attr MultiPlusII_227 obj-h68-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h68-reading maximum_overvoltage_feedin_power_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h68-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h69-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h69-reading ac_input_1_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h69-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h7-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h7-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h7-reading AC_Input_Current_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h7-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h70-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h70-reading ac_input_2_ignored&lt;br /&gt;
attr MultiPlusII_227 obj-h70-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h71-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h71-reading ac_power_setpoint_acts_as_feedin_limit&lt;br /&gt;
attr MultiPlusII_227 obj-h71-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h72-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h72-reading solar_offset_voltage&lt;br /&gt;
attr MultiPlusII_227 obj-h72-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h73-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h73-reading sustain_active&lt;br /&gt;
attr MultiPlusII_227 obj-h73-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h74-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h74-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h74-reading energy_from_acIn1_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h74-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h76-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h76-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h76-reading energy_from_acIn1_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h76-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h78-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h78-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h78-reading energy_from_acIn2_to_acOut&lt;br /&gt;
attr MultiPlusII_227 obj-h78-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h8-expr $val / 10&lt;br /&gt;
attr MultiPlusII_227 obj-h8-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h8-reading AC_Input_Current_L	3&lt;br /&gt;
attr MultiPlusII_227 obj-h8-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h80-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h80-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h80-reading energy_from_acIn2_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h80-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h82-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h82-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h82-reading energy_from_acOut_to_acIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h82-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h84-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h84-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h84-reading energy_from_acOut_to_acIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h84-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h86-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h86-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h86-reading energy_from_battery_to_AcIn1&lt;br /&gt;
attr MultiPlusII_227 obj-h86-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h88-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h88-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h88-reading energy_from_battery_to_AcIn2&lt;br /&gt;
attr MultiPlusII_227 obj-h88-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h9-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h9-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h9-reading AC_Input_Frequency_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h9-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h90-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h90-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h90-reading energy_from_battery_to_AcOut&lt;br /&gt;
attr MultiPlusII_227 obj-h90-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h92-expr $val / 100&lt;br /&gt;
attr MultiPlusII_227 obj-h92-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h92-reading energy_from_AcOut_to_battery&lt;br /&gt;
attr MultiPlusII_227 obj-h92-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h94-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h94-reading low_cell_voltage_imminent&lt;br /&gt;
attr MultiPlusII_227 obj-h94-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h95-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h95-reading charge_state&lt;br /&gt;
attr MultiPlusII_227 obj-h95-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h96-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h96-reading ESS_Power_setpoint_L	1&lt;br /&gt;
attr MultiPlusII_227 obj-h96-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 obj-h98-len 1&lt;br /&gt;
attr MultiPlusII_227 obj-h98-reading ESS_Power_setpoint_L	2&lt;br /&gt;
attr MultiPlusII_227 obj-h98-unpack s&amp;gt;&lt;br /&gt;
attr MultiPlusII_227 room Solar-&amp;gt;Victron&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Visualisierung==&lt;br /&gt;
&#039;&#039;&#039;stateFormat&#039;&#039;&#039; kann so aussehen:&lt;br /&gt;
 SSOC Batteries_value_1_soc, &lt;br /&gt;
 SOC: Batteries_value_1_soc % (MinimumSocLimit_value), &lt;br /&gt;
 Bat: Batteries_value_1_power W,&lt;br /&gt;
 PV: PVPower_value W,&lt;br /&gt;
 ACin: GridPower_value W, &lt;br /&gt;
 ACout2: P_value W,&lt;br /&gt;
 State: State_value,&lt;br /&gt;
 Mode: Mode_value&lt;br /&gt;
SSOC wird später für das devStateIcon genutzt.&lt;br /&gt;
&lt;br /&gt;
Das &#039;&#039;&#039;devStateIcon&#039;&#039;&#039; bietet einen ersten Blick auf den Ladestand:&lt;br /&gt;
 SSOC\s[2|3|4].+:measure_battery_25&lt;br /&gt;
 SSOC\s[5|6].+:measure_battery_50&lt;br /&gt;
 SSOC\s[7|8].+:measure_battery_75&lt;br /&gt;
 SSOC\s[9].+:measure_battery_100&lt;br /&gt;
&lt;br /&gt;
Derzeit existiert noch kein Template, aber das kann sich ja ändern.&lt;br /&gt;
&lt;br /&gt;
==Externe Links==&lt;br /&gt;
*[https://www.victronenergy.com/support-and-downloads/technical-information Victron Technical Info]&lt;br /&gt;
*[https://vrm-api-docs.victronenergy.com/#/ VRm API]&lt;br /&gt;
*[https://github.com/victronenergy/dbus-mqtt Venus MQTT Broker]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=39601</id>
		<title>Strombörse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Stromb%C3%B6rse&amp;diff=39601"/>
		<updated>2024-10-07T12:02:16Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Vorausetzungen */ Abruf der homeID und des Access-Token hinzugefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Bild:Screenshot 2024-01-23 122115.png|mini|400px|rechts|EVU_Tibber_connect]]&lt;br /&gt;
Diese Seite beinhaltet Informationen für die Anbindung von Energieversorgungsunternehmen (EVU), die ihre Preise nach der Strombörse z.B. im Stundenrhythmus anbieten.&lt;br /&gt;
&lt;br /&gt;
Als Beispiel wären da:&lt;br /&gt;
# {{Link2Forum|Topic=130407|LinkText=Tibber - im Forum}}&lt;br /&gt;
# {{Link2Forum|Topic=135906|LinkText=aWATTar - im Forum}}&lt;br /&gt;
&lt;br /&gt;
Diese Wiki Seite ist noch in Bearbeitung!!!&lt;br /&gt;
&lt;br /&gt;
== 1. Tibber mit Tibber Pulse ==&lt;br /&gt;
Eine momentan verbreitete Methode zur Anbindung an Tibber ist ein Lesekopf auf dem EVU Zähler, der als &amp;quot;Tibber Pulse&amp;quot; vertrieben wird. Damit wird ein bestehender digitaler EVU Zähler ausgelesen und die Werte per LAN zu Tibber gesendet. Über eine API bei Tibber können dann die Verbrauchswerte und Kosten, sowie ein Live Stream (WebSocket) abgefragt werden.&lt;br /&gt;
&lt;br /&gt;
Die im Folgenden beschriebenen Beispieldevices bieten die oben genannte Abfrage der API mit WebSocket und HTTPMOD. Das EVU_Tibber Device dient der Darstellung und Steuerung.&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht unterstützt)&lt;br /&gt;
* KeyValue() (wird hier beschrieben)&lt;br /&gt;
* homeID und personal Token/Access Token/Connect Token&lt;br /&gt;
* Tibber Pulse Lesekopf auf dem digitalen EVU Zähler&lt;br /&gt;
* Man sollte bereits Tibber Kunde sein, da ansonsten nur Demo Daten abgrufen werden können&lt;br /&gt;
&lt;br /&gt;
=== Token ===&lt;br /&gt;
Der Token wird leider unterschiedlich bezeichnet, aber egal ob er personal Token, Access Token oder Connect Token heisst, es bezieht sich auf den gleichen. &lt;br /&gt;
&lt;br /&gt;
Erhältlich ist dieser aktuell (Oktober 24) unter https://developer.tibber.com/ &lt;br /&gt;
&lt;br /&gt;
Dort bitte mit deinen Zugangsdaten einloggen (&amp;quot;Sign In&amp;quot;, oben rechts) und man bekommt den Access Token direkt angezeigt.&lt;br /&gt;
&lt;br /&gt;
=== homeID ===&lt;br /&gt;
Um eine HomeID zu erhalten, muss zuerst der Pulse mit dem Internet bzw. tibber verbunden werden. Wenn die App glücklich ist, gehts hier weiter: &lt;br /&gt;
&lt;br /&gt;
Man loggt sich wieder ein unter https://developer.tibber.com/. Dort geht man dann zum API Explorer. &lt;br /&gt;
&lt;br /&gt;
Leider ist keine der Demo-Abfragen geeignet, die homeID zu erfahren. Dafür gibt man ins linke fenster folgenden Code ein: &amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  viewer {&lt;br /&gt;
    homes {&lt;br /&gt;
      id&lt;br /&gt;
      address {&lt;br /&gt;
        address1&lt;br /&gt;
        postalCode&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Dann startet man mit dem Play-Pfeil und bekommt auf der rechten Seite etwa folgende Ausgabe: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;data&amp;quot;: {&lt;br /&gt;
    &amp;quot;viewer&amp;quot;: {&lt;br /&gt;
      &amp;quot;homes&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;id&amp;quot;: &amp;quot;1234abcd-12ab-34cd-56ef-1234abcd12ab&lt;br /&gt;
          &amp;quot;address&amp;quot;: {&lt;br /&gt;
            &amp;quot;address1&amp;quot;: &amp;quot;Musterstr.1&amp;quot;,&lt;br /&gt;
            &amp;quot;postalCode&amp;quot;: &amp;quot;12345&amp;quot;&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Diese ID ist die homeID und kann unten verwendet werden.&lt;br /&gt;
&lt;br /&gt;
=== EVU_Tibber_connect ===&lt;br /&gt;
Das EVU_Tibber_connect fragt mit HTTPMOD Statistiken auf der Tibber API Webseite ab. Zusätzlich wurde in den userReadings die WebSocket Verbindung für die Live Daten integriert.&lt;br /&gt;
* Die WebSocket Verbindung wurde leicht angepasst &#039;&#039;&#039;[[Websocket|von hier]]&#039;&#039;&#039; übernommen&lt;br /&gt;
* Im Forum wurde dies &#039;&#039;&#039;{{Link2Forum|Topic=130407|Message=1298863|LinkText=ab hier}}&#039;&#039;&#039; bearbeitet&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
* DOIF&lt;br /&gt;
* KeyValue()&lt;br /&gt;
&lt;br /&gt;
==== Ablage der homeID und des Tokens ====&lt;br /&gt;
Eine Verbindung zur Tibber API mit den &#039;&#039;&#039;tatsächlichen Preisen bekommen nur Kunden&#039;&#039;&#039;, ansonsten kann man eine Demo mit Kosten in Schwedischen Kronen testweise abfragen.&lt;br /&gt;
&lt;br /&gt;
In diesem Beispiel Device kann man seine homeID und sein Token entweder als Attribut ablegen oder man macht einen Eintrag im KeyStore von FHEM, wodurch man nicht so leicht versehentlich seine Daten im Forum postet.&lt;br /&gt;
&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im Datensystem oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils.pm .&lt;br /&gt;
&lt;br /&gt;
===== Beispiel KeyValue() =====&lt;br /&gt;
&#039;&#039;&#039;Achtung, der KeyValue ist kein Schutz für Passwort oder Keys!&#039;&#039;&#039;&lt;br /&gt;
Es dient lediglich der Ablage von Keys, damit sie nicht direkt offen im FHEM Device oder im Konfigurationsfile zu finden sind.&lt;br /&gt;
Die Funktion befindet sich in der 99_myUtils .&lt;br /&gt;
&lt;br /&gt;
====== KeyValue() Funktion in der ~/FHEM/99_myUtils.pm ======&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub KeyValue {&lt;br /&gt;
    my ($step, $index, $value) = @_;&lt;br /&gt;
    my $key = getUniqueId().$index;&lt;br /&gt;
    my $e_value = &amp;quot;&amp;quot;;&lt;br /&gt;
    my $error;&lt;br /&gt;
&lt;br /&gt;
    if (eval &amp;quot;use Digest::MD5;1&amp;quot;) {&lt;br /&gt;
      $key    = Digest::MD5::md5_hex(unpack &amp;quot;H*&amp;quot;, $key);&lt;br /&gt;
      $key   .= Digest::MD5::md5_hex($key);&lt;br /&gt;
    }&lt;br /&gt;
   &lt;br /&gt;
    if ($step eq &amp;quot;read&amp;quot;) {&lt;br /&gt;
      ($error, $value) = getKeyValue($index);&lt;br /&gt;
&lt;br /&gt;
      if ( defined($error) ) {&lt;br /&gt;
        Log3 $index,3, &amp;quot;$index, can&#039;t read key from FhemUtils/uniqueID: $error&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      if ( defined($value) ) {&lt;br /&gt;
        my $dec_value = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        for my $char (map { pack(&#039;C&#039;, hex($_)) } ($value =~ /(..)/g)) {&lt;br /&gt;
          my $decode  = chop($key);&lt;br /&gt;
          $dec_value .= chr(ord($char)^ord($decode));&lt;br /&gt;
          $key        = $decode.$key;&lt;br /&gt;
        }&lt;br /&gt;
        return $dec_value;&lt;br /&gt;
      }&lt;br /&gt;
      else {&lt;br /&gt;
        Log3 $index,3,&amp;quot;$index, no key found in FhemUtils/uniqueID&amp;quot;;&lt;br /&gt;
        return undef;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($step eq &amp;quot;store&amp;quot;) {&lt;br /&gt;
      for my $char (split //, $value) {&lt;br /&gt;
        my $encode = chop($key);&lt;br /&gt;
        $e_value  .= sprintf(&amp;quot;%.2x&amp;quot;,ord($char)^ord($encode));&lt;br /&gt;
        $key       = $encode.$key;&lt;br /&gt;
      }&lt;br /&gt;
      $error = setKeyValue($index, $e_value);&lt;br /&gt;
      return &amp;quot;error while saving key : $error&amp;quot; if(defined($error));&lt;br /&gt;
      return &amp;quot;Key successfully saved in FhemUtils/uniqueID Key $index&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== Ablegen der homeID und des Token ======&lt;br /&gt;
In der FHEM Kommandozeile legt man seinen Daten wie folgt im KeyValue ab. Hier wären als Beispiel der Demo Zugang von Tibber.&lt;br /&gt;
Das &amp;quot;read&amp;quot; ist dann auch direkt der erste Test, bevor man weiter macht.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}&lt;br /&gt;
&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
{KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Beispiel Attribut für homeID und Token =====&lt;br /&gt;
Oder, sobald das EVU_Tibber_connect Device erstellt wurde, als Attribut.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
attr EVU_Tibber_connect ws_homeId 96a14971-525a-4420-aae9-e5aedaa129ff&lt;br /&gt;
attr EVU_Tibber_connect ws_myId 96a14971-525a-4420-aae9-e5aedaa129ff          &amp;lt;&amp;lt;&amp;lt;&amp;lt; Dies kann wohl eine frei gewählte ID sein, was hier der Einfachheithalber gleich der homeId gesetzt wurde.&lt;br /&gt;
attr EVU_Tibber_connect ws_token 5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber_connect RAW Device====&lt;br /&gt;
Das Device EVU_Tibber_connect dient der Verbindung zur Tibber API. Zusätzlich wird für die Abfrege der Live Daten innerhalb der userReadings eine WebSocket Verbindung zu Tibber aufgebaut. Tibber stellt jeweils gegen 14:00 Uhr die Daten für den nächsten Tag bereit, wodurch von 00:00 - 14:00 Uhr diese Information im EVU_Tibber_connect noch nicht angezeigt werden kann.&lt;br /&gt;
Das Device arbeitet über die userReadings mit einem MySQL DbRep Device, was somit eine MySQL DbLog Verwendung im FHEM voraussetzt. &#039;&#039;&#039;Die Verwendung von FileLog ist nicht unterstützt!&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber_connect HTTPMOD https://api.tibber.com/v1-beta/gql 0&lt;br /&gt;
attr EVU_Tibber_connect userattr ws_homeId ws_minInterval ws_myId ws_token ws_websocketURL&lt;br /&gt;
attr EVU_Tibber_connect DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber_connect DbLogInclude total_cost_.*,fc0_trigger.*&lt;br /&gt;
attr EVU_Tibber_connect comment Version 2024.01.22 14:00 \&lt;br /&gt;
https://developer.tibber.com/explorer\&lt;br /&gt;
\&lt;br /&gt;
In der FHEM Kommandozeile könnt Ihr das token und die homeID im KeyStore ablegen.\&lt;br /&gt;
Bitte lest dazu das Thema KeyStore im Wiki und legt die Funktion in Eure 99_myUtils.\&lt;br /&gt;
\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;,&amp;quot;5K4MVS-OjfWhK_4yrjOlFe1F6kJXPVf7eQYggo8ebAE&amp;quot;)}\&lt;br /&gt;
{KeyValue(&amp;quot;store&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;,&amp;quot;96a14971-525a-4420-aae9-e5aedaa129ff&amp;quot;)}\&lt;br /&gt;
\&lt;br /&gt;
Hierduch kann man einen wirtschaftlicheren trigger_price berechnen lassen:\&lt;br /&gt;
setreading EVU_Tibber compensation_grid &amp;lt;Eure Einspeisevergütung&amp;gt;\&lt;br /&gt;
\&lt;br /&gt;
# Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
    (fc_avg - fc_min)  /2 + fc_min\&lt;br /&gt;
\&lt;br /&gt;
# Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
# compensation_grid kann auch auf einen für Euch passenden Wert gesetzt werden\&lt;br /&gt;
    (fc_avg - compensation_grid) *0.85&lt;br /&gt;
attr EVU_Tibber_connect disable 0&lt;br /&gt;
attr EVU_Tibber_connect enableControlSet 1&lt;br /&gt;
attr EVU_Tibber_connect get01-1Name current_currency&lt;br /&gt;
attr EVU_Tibber_connect get01-2Name current_level&lt;br /&gt;
attr EVU_Tibber_connect get01-3Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get01-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get01-4Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get01-4OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get01Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt currency level}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get01Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get01Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get01JSON data_viewer_home_currentSubscription_priceInfo_current&lt;br /&gt;
attr EVU_Tibber_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get01URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get02-10Name fc0_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-11Name fc0_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-11OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-12Name fc0_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-13Name fc0_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-14Name fc0_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-15Name fc0_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-15OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-16Name fc0_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-17Name fc0_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-17OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-18Name fc0_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-19Name fc0_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-1Name current_date&lt;br /&gt;
attr EVU_Tibber_connect get02-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-20Name fc0_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-21Name fc0_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-21OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-22Name fc0_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-23Name fc0_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-23OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-24Name fc0_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-25Name fc0_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-26Name fc0_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-27Name fc0_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-27OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-28Name fc0_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-29Name fc0_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-29OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-2Name current_price&lt;br /&gt;
attr EVU_Tibber_connect get02-2OExpr $val *100&lt;br /&gt;
attr EVU_Tibber_connect get02-30Name fc0_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-31Name fc0_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-32Name fc0_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-33Name fc0_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-33OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-34Name fc0_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-35Name fc0_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-35OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-36Name fc0_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-37Name fc0_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-38Name fc0_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-39Name fc0_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-39OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-3Name fc0_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-3OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-40Name fc0_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-41Name fc0_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-41OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-42Name fc0_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-43Name fc0_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-44Name fc0_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-45Name fc0_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-45OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-46Name fc0_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-47Name fc0_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-47OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-48Name fc0_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-49Name fc0_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-4Name fc0_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-50Name fc0_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-51Name fc1_00_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-51OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-52Name fc1_00_total&lt;br /&gt;
attr EVU_Tibber_connect get02-53Name fc1_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-53OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-54Name fc1_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-55Name fc1_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-56Name fc1_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-57Name fc1_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-57OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-58Name fc1_03_total&lt;br /&gt;
attr EVU_Tibber_connect get02-59Name fc1_04_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-59OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-5Name fc0_01_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-5OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-60Name fc1_04_total&lt;br /&gt;
attr EVU_Tibber_connect get02-61Name fc1_05_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-62Name fc1_05_total&lt;br /&gt;
attr EVU_Tibber_connect get02-63Name fc1_06_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-63OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-64Name fc1_06_total&lt;br /&gt;
attr EVU_Tibber_connect get02-65Name fc1_07_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-65OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-66Name fc1_07_total&lt;br /&gt;
attr EVU_Tibber_connect get02-67Name fc1_08_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-68Name fc1_08_total&lt;br /&gt;
attr EVU_Tibber_connect get02-69Name fc1_09_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-69OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-6Name fc0_01_total&lt;br /&gt;
attr EVU_Tibber_connect get02-70Name fc1_09_total&lt;br /&gt;
attr EVU_Tibber_connect get02-71Name fc1_10_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-71OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-72Name fc1_10_total&lt;br /&gt;
attr EVU_Tibber_connect get02-73Name fc1_11_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-73OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-74Name fc1_11_total&lt;br /&gt;
attr EVU_Tibber_connect get02-75Name fc1_12_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-75OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-76Name fc1_12_total&lt;br /&gt;
attr EVU_Tibber_connect get02-77Name fc1_13_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-77OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-78Name fc1_13_total&lt;br /&gt;
attr EVU_Tibber_connect get02-79Name fc1_14_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-79OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-7Name fc0_02_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-80Name fc1_14_total&lt;br /&gt;
attr EVU_Tibber_connect get02-81Name fc1_15_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-81OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-82Name fc1_15_total&lt;br /&gt;
attr EVU_Tibber_connect get02-83Name fc1_16_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-83OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-84Name fc1_16_total&lt;br /&gt;
attr EVU_Tibber_connect get02-85Name fc1_17_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-85OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-86Name fc1_17_total&lt;br /&gt;
attr EVU_Tibber_connect get02-87Name fc1_18_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-87OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-88Name fc1_18_total&lt;br /&gt;
attr EVU_Tibber_connect get02-89Name fc1_19_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-89OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-8Name fc0_02_total&lt;br /&gt;
attr EVU_Tibber_connect get02-90Name fc1_19_total&lt;br /&gt;
attr EVU_Tibber_connect get02-91Name fc1_20_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-91OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-92Name fc1_20_total&lt;br /&gt;
attr EVU_Tibber_connect get02-93Name fc1_21_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-93OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-94Name fc1_21_total&lt;br /&gt;
attr EVU_Tibber_connect get02-95Name fc1_22_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-95OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-96Name fc1_22_total&lt;br /&gt;
attr EVU_Tibber_connect get02-97Name fc1_23_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-97OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02-98Name fc1_23_total&lt;br /&gt;
attr EVU_Tibber_connect get02-9Name fc0_03_startsAt&lt;br /&gt;
attr EVU_Tibber_connect get02-9OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get02Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total startsAt} today {total startsAt} tomorrow {total startsAt}}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get02Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get02Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get02JSON data_viewer_home_currentSubscription_priceInfo&lt;br /&gt;
attr EVU_Tibber_connect get02Name 02_priceAll&lt;br /&gt;
attr EVU_Tibber_connect get02URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get03-1Name nodes_00_00_from&lt;br /&gt;
attr EVU_Tibber_connect get03-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-2Name nodes_00_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-3Name nodes_00_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-4Name nodes_00_01_from&lt;br /&gt;
attr EVU_Tibber_connect get03-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-50Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-5Name nodes_00_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-6Name nodes_00_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03-7Name nodes_00_02_from&lt;br /&gt;
attr EVU_Tibber_connect get03-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get03-8Name nodes_00_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get03-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get03-9Name nodes_00_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get03Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 3) {nodes {from cost consumption }}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get03Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get03Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get03Name 03_consumption_hour&lt;br /&gt;
attr EVU_Tibber_connect get03RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get03Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get03URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get04-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get04-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get04-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get04-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get04-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-1Name nodes_24_00_from&lt;br /&gt;
attr EVU_Tibber_connect get04-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get04-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get04-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get04-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-2Name nodes_24_00_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get04-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get04-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get04-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get04-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get04-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get04-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get04-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get04-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get04-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get04-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get04-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get04-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get04-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get04-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get04-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get04-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get04-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get04-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get04-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get04Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 24) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get04Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get04Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get04Name 04_consumption_hour_24&lt;br /&gt;
attr EVU_Tibber_connect get04RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get04Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get04URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get05AutoNumLen 4&lt;br /&gt;
attr EVU_Tibber_connect get05Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {consumption(resolution: HOURLY, last: 100) {nodes {from cost consumption}}}}}&amp;quot;}&lt;br /&gt;
attr EVU_Tibber_connect get05Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get05Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get05JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get05Name 05_consumption_hourly_100&lt;br /&gt;
attr EVU_Tibber_connect get05URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get06-10Name Adresse_07_Telefon&lt;br /&gt;
attr EVU_Tibber_connect get06-11Name Adresse_01_Vorname&lt;br /&gt;
attr EVU_Tibber_connect get06-12Name Adresse_02_Nachname&lt;br /&gt;
attr EVU_Tibber_connect get06-1Name Adresse_03_Strasse&lt;br /&gt;
attr EVU_Tibber_connect get06-2Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-3Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-4Name Adresse_05_Ort&lt;br /&gt;
attr EVU_Tibber_connect get06-5Name Adresse_06_Land&lt;br /&gt;
attr EVU_Tibber_connect get06-6Name Adresse_09_latitude&lt;br /&gt;
attr EVU_Tibber_connect get06-7Name Adresse_10_longitude&lt;br /&gt;
attr EVU_Tibber_connect get06-8Name Adresse_04_Plz&lt;br /&gt;
attr EVU_Tibber_connect get06-9Name Adresse_08_eMail&lt;br /&gt;
attr EVU_Tibber_connect get06Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {address {address1 address2 address3 postalCode city country latitude longitude} owner {firstName lastName contactInfo {email mobile}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get06Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get06Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get06JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get06Name 06_address&lt;br /&gt;
attr EVU_Tibber_connect get06URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect get07-10Name nodes_24_03_from&lt;br /&gt;
attr EVU_Tibber_connect get07-10OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-11Name nodes_24_03_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-11OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-12Name nodes_24_03_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-13Name nodes_24_04_from&lt;br /&gt;
attr EVU_Tibber_connect get07-13OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-14Name nodes_24_04_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-14OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-15Name nodes_24_04_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-16Name nodes_24_05_from&lt;br /&gt;
attr EVU_Tibber_connect get07-16OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-17Name nodes_24_05_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-17OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-18Name nodes_24_05_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-19Name nodes_24_06_from&lt;br /&gt;
attr EVU_Tibber_connect get07-19OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-1Name features_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07-1OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-20Name nodes_24_06_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-20OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-21Name nodes_24_06_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-22Name nodes_24_07_from&lt;br /&gt;
attr EVU_Tibber_connect get07-22OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-23Name nodes_24_07_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-23OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-24Name nodes_24_07_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-25Name nodes_24_08_from&lt;br /&gt;
attr EVU_Tibber_connect get07-25OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-26Name nodes_24_08_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-26OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-27Name nodes_24_08_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-28Name nodes_24_09_from&lt;br /&gt;
attr EVU_Tibber_connect get07-28OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-29Name nodes_24_09_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-29OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-2Name features_id&lt;br /&gt;
attr EVU_Tibber_connect get07-2OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-30Name nodes_24_09_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-31Name nodes_24_10_from&lt;br /&gt;
attr EVU_Tibber_connect get07-31OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-32Name nodes_24_10_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-32OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-33Name nodes_24_10_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-34Name nodes_24_11_from&lt;br /&gt;
attr EVU_Tibber_connect get07-34OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-35Name nodes_24_11_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-35OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-36Name nodes_24_11_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-37Name nodes_24_12_from&lt;br /&gt;
attr EVU_Tibber_connect get07-37OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-38Name nodes_24_12_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-38OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-39Name nodes_24_12_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-3Name nodes_24_00_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-40Name nodes_24_13_from&lt;br /&gt;
attr EVU_Tibber_connect get07-40OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-41Name nodes_24_13_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-41OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-42Name nodes_24_13_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-43Name nodes_24_14_from&lt;br /&gt;
attr EVU_Tibber_connect get07-43OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-44Name nodes_24_14_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-44OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-45Name nodes_24_14_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-46Name nodes_24_15_from&lt;br /&gt;
attr EVU_Tibber_connect get07-46OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-47Name nodes_24_15_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-47OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-48Name nodes_24_15_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-49Name nodes_24_16_from&lt;br /&gt;
attr EVU_Tibber_connect get07-49OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-4Name nodes_24_01_from&lt;br /&gt;
attr EVU_Tibber_connect get07-4OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-50Name nodes_24_16_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-50OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-51Name nodes_24_16_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-52Name nodes_24_17_from&lt;br /&gt;
attr EVU_Tibber_connect get07-52OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-53Name nodes_24_17_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-53OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-54Name nodes_24_17_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-55Name nodes_24_18_from&lt;br /&gt;
attr EVU_Tibber_connect get07-55OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-56Name nodes_24_18_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-56OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-57Name nodes_24_18_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-58Name nodes_24_19_from&lt;br /&gt;
attr EVU_Tibber_connect get07-58OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-59Name nodes_24_19_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-59OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-5Name nodes_24_01_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-5OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-60Name nodes_24_19_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-61Name nodes_24_20_from&lt;br /&gt;
attr EVU_Tibber_connect get07-61OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-62Name nodes_24_20_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-62OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-63Name nodes_24_20_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-64Name nodes_24_21_from&lt;br /&gt;
attr EVU_Tibber_connect get07-64OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-65Name nodes_24_21_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-65OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-66Name nodes_24_21_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-67Name nodes_24_22_from&lt;br /&gt;
attr EVU_Tibber_connect get07-67OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-68Name nodes_24_22_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-68OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-69Name nodes_24_22_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-6Name nodes_24_01_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-70Name nodes_24_23_from&lt;br /&gt;
attr EVU_Tibber_connect get07-70OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-71Name nodes_24_23_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-71OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-72Name nodes_24_23_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07-7Name nodes_24_02_from&lt;br /&gt;
attr EVU_Tibber_connect get07-7OExpr $val =~ s/T/ /g ;; substr($val,0,19)&lt;br /&gt;
attr EVU_Tibber_connect get07-8Name nodes_24_02_cost&lt;br /&gt;
attr EVU_Tibber_connect get07-8OExpr ($val ne &amp;quot;0&amp;quot; and $val ne &amp;quot;null&amp;quot;)? round($val,4) : $val&lt;br /&gt;
attr EVU_Tibber_connect get07-9Name nodes_24_02_consumption&lt;br /&gt;
attr EVU_Tibber_connect get07Data { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {id features{realTimeConsumptionEnabled} } } }&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect get07Header01 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect get07Header02 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect get07JSON data_viewer_home&lt;br /&gt;
attr EVU_Tibber_connect get07Name 07_realTimeConsumptionEnabled&lt;br /&gt;
attr EVU_Tibber_connect get07RegOpt g&lt;br /&gt;
attr EVU_Tibber_connect get07Regex \{&amp;quot;from&amp;quot;:&amp;quot;([\d+-]+T[\d+:]+\.000[+-][\d+:]+)&amp;quot;,&amp;quot;cost&amp;quot;:(null|\d+\.\d+|0),&amp;quot;consumption&amp;quot;:(null|\d+\.\d+|0)\}&lt;br /&gt;
attr EVU_Tibber_connect get07URL https://api.tibber.com/v1-beta/gql&lt;br /&gt;
attr EVU_Tibber_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber_connect icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber_connect replacement01Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement01Regex %%token%%&lt;br /&gt;
attr EVU_Tibber_connect replacement01Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect replacement02Mode expression&lt;br /&gt;
attr EVU_Tibber_connect replacement02Regex %%homeID%%&lt;br /&gt;
attr EVU_Tibber_connect replacement02Value {KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;)}&lt;br /&gt;
attr EVU_Tibber_connect requestData { &amp;quot;query&amp;quot;: &amp;quot;{viewer {home(id:\&amp;quot;%%homeID%%\&amp;quot;) {currentSubscription {priceInfo {current {total energy tax startsAt }}}}}}&amp;quot; }&lt;br /&gt;
attr EVU_Tibber_connect requestHeader1 Content-Type: application/json&lt;br /&gt;
attr EVU_Tibber_connect requestHeader2 Authorization: Bearer %%token%%&lt;br /&gt;
attr EVU_Tibber_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber_connect showBody 1&lt;br /&gt;
attr EVU_Tibber_connect showError 1&lt;br /&gt;
attr EVU_Tibber_connect sortby 313&lt;br /&gt;
attr EVU_Tibber_connect timeout 30&lt;br /&gt;
attr EVU_Tibber_connect userReadings ws_connect:ws_cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    # establish connection to websocket\&lt;br /&gt;
    # format must also include portnumber if a path is to be specified\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;ws_websocketURL&amp;quot;, &amp;quot;wss:echo.websocket.org:443&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # special headers needed for Tibber, see also Developer Tools in Browser\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Sec-WebSocket-Protocol&#039;} = &#039;graphql-transport-ws&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = &#039;websocket-api.tibber.com&#039;;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Origin&#039;} = &#039;https://developer.tibber.com&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # callback function when &amp;quot;select()&amp;quot; signals data for us\&lt;br /&gt;
    # websocket Ping/Pongs are treated in DevIo but still call this function\&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        \&lt;br /&gt;
        # we can read without closing the DevIo, because select() signalled data\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # if read fails, close device\&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and if last update is older than minInterval\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot;) {\&lt;br /&gt;
            my $websocketDataAge = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 3600);;\&lt;br /&gt;
            my $minInterval = AttrVal($name, &amp;quot;ws_minInterval&amp;quot;, 0);;\&lt;br /&gt;
            my $isNext = ($buf =~ /.*id.*type.*next.*payload.*data.*liveMeasurement.*/s);;\&lt;br /&gt;
            \&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($isNext &amp;amp;&amp;amp; $websocketDataAge &amp;gt; $minInterval);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if (!$isNext);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
            #Log(3, &amp;quot;$name:$reading: websocket data written to reading&amp;quot;);;\&lt;br /&gt;
        }\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    # open DevIo websocket\&lt;br /&gt;
    DevIo_OpenDev($hash, 0, undef, sub(){\&lt;br /&gt;
        my ($hash, $error) = @_;;\&lt;br /&gt;
        return &amp;quot;$error&amp;quot; if ($error);;\&lt;br /&gt;
        \&lt;br /&gt;
        my $token = AttrVal($name, &amp;quot;ws_token&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
           $token = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_token&amp;quot;) if ($token eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
        DevIo_SimpleWrite($hash, &#039;{&amp;quot;type&amp;quot;:&amp;quot;connection_init&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;token&amp;quot;:&amp;quot;&#039;.$token.&#039;&amp;quot;}}&#039;, 2);;\&lt;br /&gt;
    });;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
    #Log(3, &amp;quot;$name:$reading: websocket data cleared in reading&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_disconnect:ws_cmd:.disconnect {\&lt;br /&gt;
    Log(3, &amp;quot;$name: disconnect&amp;quot;);;\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    RemoveInternalTimer($hash);;\&lt;br /&gt;
    DevIo_SimpleRead($hash);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onDisconnect {\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
	## timer callback function, called after a few seconds to initiate a reconnect\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
		\&lt;br /&gt;
		# only re-connect if device is not connected\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
	};;\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	# wait a random time before reconnect (exponential backoff TBD):\&lt;br /&gt;
	my $rwait = int(rand(200)) + 30;;\&lt;br /&gt;
	InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set cmd to a new value, informs user and allows to retrigger when timer expires\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onTimeout:ws_websocketData:.* {\&lt;br /&gt;
	#re-establish websocket connection if no data received in the past ten minutes\&lt;br /&gt;
	#but only if our reading &amp;quot;ws_cmd&amp;quot; was not set to the value &amp;quot;disconnect&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: websocket data triggert ws_onTimeout&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#timeout in seconds when the connection is considered dead\&lt;br /&gt;
	my $timeoutTime = 600;;\&lt;br /&gt;
	\&lt;br /&gt;
	# function to execute when timeout expired\&lt;br /&gt;
	# defining the function here in the userReading, allows us to insert variables directly\&lt;br /&gt;
	my $timerFunction = sub() {\&lt;br /&gt;
		my ($arg) = @_;;\&lt;br /&gt;
		my $hash = $defs{$name};;\&lt;br /&gt;
		my $rCmd = ReadingsVal($name, &amp;quot;ws_cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
		my $age  = ReadingsAge($name, &amp;quot;ws_websocketData&amp;quot;, 0);;\&lt;br /&gt;
		\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer triggered &amp;gt;&amp;gt;$arg&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		#do not do anything further if disconnect is on purpose\&lt;br /&gt;
		if ( $rCmd eq &amp;quot;disconnect&amp;quot; ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_cmd was set to disconnect&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		# for whatever reason, we triggered to soon (80%)\&lt;br /&gt;
		if ( $age &amp;lt; $timeoutTime*0.8 ) {\&lt;br /&gt;
			Log(3, &amp;quot;$name: ws_websocketData is not outdated&amp;quot;);;\&lt;br /&gt;
			return;;\&lt;br /&gt;
		}\&lt;br /&gt;
		\&lt;br /&gt;
		DevIo_CloseDev($hash);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer closed DevIo...&amp;quot;);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsSingleUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
		Log(3, &amp;quot;$name: onTimeoutTimer set ws_cmd to value &#039;connect&#039;&amp;quot;);;\&lt;br /&gt;
	};;\&lt;br /&gt;
\&lt;br /&gt;
        #Log(3, &amp;quot;$name:$reading: onTimeout function defined&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
	#remove/cancel previous timers, because we got fresh data and countdown starts again\&lt;br /&gt;
	RemoveInternalTimer($name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	#set timer to expire and execute function defined above, give special arg as identifier\&lt;br /&gt;
	InternalTimer(gettimeofday() + $timeoutTime, $timerFunction, $name.$reading.&#039;Timer&#039;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onConnectionAck:ws_websocketData:.*connection_ack.* {\&lt;br /&gt;
    #ws_websocketData contains the string &amp;quot;connection_ack&amp;quot;\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # do not proceed if connection is lost\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device not open&amp;quot; if (!defined($devState));;\&lt;br /&gt;
\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;ws_cmd&amp;quot;, &amp;quot;got connection ack&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $homeId = AttrVal($name, &amp;quot;ws_homeId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $homeId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($homeId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
    my $myId = AttrVal($name, &amp;quot;ws_myId&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
       $myId = KeyValue(&amp;quot;read&amp;quot;,&amp;quot;EVU_Tibber_connect_homeID&amp;quot;) if ($myId eq &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    # build the query, do it in pieces, the comma at the end caused perl errors\&lt;br /&gt;
    # so we put it together in this not very elegant way\&lt;br /&gt;
    my $json = &#039;{ &amp;quot;id&amp;quot;:&amp;quot;&#039;. $myId .&#039;&amp;quot;, &amp;quot;type&amp;quot;:&amp;quot;subscribe&amp;quot;&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;payload&amp;quot;:{&#039;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;variables&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;extensions&amp;quot;:{}&#039;.&amp;quot;, &amp;quot;;;\&lt;br /&gt;
    $json .= &#039;&amp;quot;query&amp;quot;:&amp;quot;subscription { liveMeasurement( homeId: \&amp;quot;&#039;.$homeId.&#039;\&amp;quot; ) &#039;;;\&lt;br /&gt;
    #$json .= &#039;{ timestamp power accumulatedConsumption accumulatedCost currency minPower averagePower maxPower signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;{ timestamp power lastMeterConsumption accumulatedConsumption accumulatedProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;accumulatedProductionLastHour accumulatedCost accumulatedReward currency minPower averagePower maxPower &#039;;;\&lt;br /&gt;
    $json .= &#039;powerProduction powerReactive powerProductionReactive minPowerProduction maxPowerProduction lastMeterProduction &#039;;;\&lt;br /&gt;
    $json .= &#039;powerFactor voltagePhase1 voltagePhase2 voltagePhase3 signalStrength }}&amp;quot;&#039;;;\&lt;br /&gt;
    $json .= &#039;}}&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #send the string via websocket as ASCII\&lt;br /&gt;
    Log(3, &amp;quot;$name:$reading: sending JSON: &amp;gt;&amp;gt;&amp;gt;$json&amp;lt;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    DevIo_SimpleWrite($hash, $json, 2);;\&lt;br /&gt;
        \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
ws_onNextLiveMeasurement:ws_websocketData:.*next.*payload.*data.*liveMeasurement.* {\&lt;br /&gt;
    #websocketData contains next-live-measurement-data\&lt;br /&gt;
    my $val = ReadingsVal($name, &amp;quot;ws_websocketData&amp;quot;, &amp;quot;{}&amp;quot;);;\&lt;br /&gt;
    my %res = %{json2nameValue($val, undef, undef, &amp;quot;payload_data_liveMeasurement.*&amp;quot;)};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $ret = &amp;quot;got values for:\n&amp;quot;;;\&lt;br /&gt;
    foreach my $k (sort keys %res) {\&lt;br /&gt;
        $ret .= &amp;quot;$k\n&amp;quot;;;\&lt;br /&gt;
        readingsBulkUpdate($hash, makeReadingName($k), $res{$k});;\&lt;br /&gt;
    }\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_00_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 2;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_00_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:nodes_24_00_cost.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
my $tmp = 0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 0;; $loop_last &amp;lt;= 23;; $loop_last++) {\&lt;br /&gt;
  $timestamp = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_from&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_cost&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_24_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_last).&amp;quot;_consumption&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $tmp = &amp;quot;null&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
if ($tmp eq &amp;quot;null&amp;quot;) {\&lt;br /&gt;
  $timestamp = $tmp\&lt;br /&gt;
}\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_TIMESTAMP:05_consumption_hourly_100-0001.* {\&lt;br /&gt;
my ($timestamp,$value) = 2x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_last = 1;; $loop_last &amp;lt;= 300;; $loop_last += 3) {\&lt;br /&gt;
\&lt;br /&gt;
  $timestamp =  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+2),&amp;quot;null&amp;quot;);; # timestamp\&lt;br /&gt;
  $timestamp =~ s/T/ /g ;;\&lt;br /&gt;
  $timestamp =  substr($timestamp,0,19);;\&lt;br /&gt;
  $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last+1),&amp;quot;null&amp;quot;);;  # cost\&lt;br /&gt;
\&lt;br /&gt;
  if ( $value ne &amp;quot;&amp;quot; ) {\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
# print $timestamp.&amp;quot; &amp;quot;.$value.&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
    # Eintragen der Kosten für die Stunde\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_cost&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    # Eintragen des Verbrauchs für die Stunde\&lt;br /&gt;
    $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;05_consumption_hourly_100-&amp;quot;.sprintf(&amp;quot;%04d&amp;quot;,$loop_last),&amp;quot;null&amp;quot;);;  # consumption\&lt;br /&gt;
    $value = round($value,4);;\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
              INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;nodes_consumption&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
              ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
  }\&lt;br /&gt;
} # end for\&lt;br /&gt;
\&lt;br /&gt;
$timestamp;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_avg:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(avg(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_avg&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_min:nodes_TIMESTAMP.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(min(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
} else {\&lt;br /&gt;
  ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_min&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
}\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_cost_max:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Ermittlung des maximal Wertes\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_TIMESTAMP&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(max(VALUE)*100 AS DECIMAL(4,2)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND TIMESTAMP &amp;gt;= curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;nodes_cost_max&amp;quot;,&amp;quot;null&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Monats Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
nodes_consumption_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Verbrauches\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT cast(sum(VALUE) AS DECIMAL(10,4)) FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_consumption&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_avg:current_price.* {\&lt;br /&gt;
## Berechnung des durchschnitt Wertes\&lt;br /&gt;
  my $fc_avg = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    if (AttrVal(&amp;quot;$NAME&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$NAME cmd_1  : Tibber Daten für fc1 sind bereits da&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {                                          ## Summe  berechnen\&lt;br /&gt;
        $fc_avg += ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
        } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_avg / (24 *(1+$fc)) *100 ,2);;                     ## Durchschnitt berechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_med:current_price.* {\&lt;br /&gt;
## Berechnung des median Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT\&lt;br /&gt;
             cast( (   (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, floor(1+((count(VALUE)-1) / 2)))  , &#039;,&#039;, -1))\&lt;br /&gt;
                       + (SUBSTRING_INDEX(SUBSTRING_INDEX(group_concat(VALUE order by VALUE), &#039;,&#039;, ceiling(1+((count(VALUE)-1) / 2))), &#039;,&#039;, -1))\&lt;br /&gt;
                     )/2 *100\&lt;br /&gt;
             AS DECIMAL(4,2))\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND (   READING=&#039;fc0_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW(), &#039;%Y-%m-%d 00:00:00&#039;)\&lt;br /&gt;
                     OR READING=&#039;fc1_total&#039; AND TIMESTAMP &amp;gt;= DATE_FORMAT(NOW() + INTERVAL 1 DAY, &#039;%Y-%m-%d 00:00:00&#039;) ) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_min:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_min = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;lt; $fc_min) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_min = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_min *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_max:current_price.* {\&lt;br /&gt;
##  Ermittlung des minimal Wertes\&lt;br /&gt;
  my $fc_max = 0;;\&lt;br /&gt;
  my $fc_tmp = 0;;\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist bereits da\&lt;br /&gt;
    $fc = 1;;\&lt;br /&gt;
  } # end if\&lt;br /&gt;
\&lt;br /&gt;
    for (my $j=0;;$j&amp;lt;=$fc;;$j++){\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=23;;$k++) {\&lt;br /&gt;
        $fc_tmp = ReadingsVal(&amp;quot;$NAME&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j,$k),0);;\&lt;br /&gt;
  \&lt;br /&gt;
        if (($fc_tmp &amp;gt; $fc_max) or ($j == 0 and $k == 0)) {\&lt;br /&gt;
          $fc_max = $fc_tmp;;\&lt;br /&gt;
        }\&lt;br /&gt;
      } # end $k\&lt;br /&gt;
    } # end $j\&lt;br /&gt;
\&lt;br /&gt;
    return round($fc_max *100 ,2);;                             ## Von Euro auf Cent umrechnen\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
  \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_trigger_max:fc_avg.* {\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_max&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Schwellwertes als täglichen Maximalpreises\&lt;br /&gt;
  my $price_level = round( ($fc_max - $fc_avg)/2 + $fc_avg , 0);;\&lt;br /&gt;
  \&lt;br /&gt;
$price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger_max Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_max:fc0_trigger_max_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des maximum Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,0)  &amp;gt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_start:fc_trigger_max.* {\&lt;br /&gt;
  if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
    # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc1_trigger_max_stop:fc0_trigger_max_start.* {\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_max = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_max&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_max_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_trigger_max_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_max_stop = $fc_trigger_max_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_max_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_max_start =~ /(\d\d):/;; $fc_trigger_max_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_max_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;gt; $fc_trigger_max ) {\&lt;br /&gt;
        $fc_trigger_max_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_max_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_day:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Tages Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND date(TIMESTAMP) = curdate() ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_month:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des monats Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate())\&lt;br /&gt;
             AND MONTH(TIMESTAMP) = MONTH(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
total_cost_year:nodes_TIMESTAMP.* {\&lt;br /&gt;
## Berechnung des Jahres Wertes\&lt;br /&gt;
::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
           SELECT CAST(SUM(VALUE) AS DECIMAL(8,2)) AS VALUE\&lt;br /&gt;
           FROM history\&lt;br /&gt;
           WHERE DEVICE=&#039;&amp;quot;.$NAME.&amp;quot;&#039;\&lt;br /&gt;
             AND READING=&#039;nodes_cost&#039;\&lt;br /&gt;
             AND YEAR(TIMESTAMP) = YEAR(curdate()) ;;&amp;quot;) ;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) eq ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc_next.&amp;quot;_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;)) {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc&amp;quot;.$loop_fc_next.&amp;quot;_.*&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsTimestamp(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_Tibber_connect verbose 0&lt;br /&gt;
attr EVU_Tibber_connect ws_minInterval 54&lt;br /&gt;
attr EVU_Tibber_connect ws_websocketURL wss:websocket-api.tibber.com:443/v1-beta/gql/subscriptions&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== EVU_Tibber RAW Device ====&lt;br /&gt;
Das Device dient der Anzeige und Steuerung des EVU_Tibber_connect Devices.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_Tibber DOIF ## Startup Befehle für den WebSocket vom EVU_Tibber_connect\&lt;br /&gt;
init\&lt;br /&gt;
{ \&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : ▶️  EVU_Tibber init&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd connect&amp;quot;);; \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 0   WebSocket  : ▶️  EVU_Tibber_connect start Websocket&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  Log(0, &amp;quot;$SELF 0   init       : 🏁 EVU_Tibber init done&amp;quot;);;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## EVU_Tibber_connect start/stop WebSocket\&lt;br /&gt;
EVU_Tibber_connect_ws\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
#    or  [$SELF:ui_command_2]                                             ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
    and [$SELF:ui_command_2] ne &amp;quot;---&amp;quot;\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
    fhem(&amp;quot;setreading EVU_Tibber_connect ws_cmd &amp;quot;.[?$SELF:ui_command_2]);; \&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_2&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 1 Scheduling für das Abholen von Tibber Daten\&lt;br /&gt;
1_EVU_Tibber_PriceInfo\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [:03]                                                          ## Kurz nach jeder vollen Stunde\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;1_EVU_Tibber_PriceInfo&amp;quot;                  ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Preis für die aktuelle Stunde\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 03_consumption_hour&amp;quot;);;         ## Kosten der letzen drei Stunden\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 1   PriceInfo  : Abfrage von Tibber&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Scheduling für das Abholen der Tibber Preise\&lt;br /&gt;
2_EVU_Tibber_PriceAll\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    ([00:03] or [14:03]                                                  ## Ab 14:00 Uhr gibt es die Preise für den nächsten Tag\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;2_EVU_Tibber_PriceAll&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 02_priceAll&amp;quot;);;\&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 2   priceAll   : Abfrage von Tibber für den nächsten Tag&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
################################################################################################################\&lt;br /&gt;
## 2 Erstellen des Diagramms im uiTable\&lt;br /&gt;
3_EVU_Tibber_Diagramm\&lt;br /&gt;
{if( !([$SELF:state] eq &amp;quot;off&amp;quot;)                                           ## DOIF enabled\&lt;br /&gt;
    and\&lt;br /&gt;
    (     [00:05] or [14:05]                                             ## Kurz nach Mitternacht\&lt;br /&gt;
    )\&lt;br /&gt;
    or [$SELF:ui_command_1] eq &amp;quot;3_EVU_Tibber_Diagramm&amp;quot;                   ## Hier wird das uiTable select ausgewertet\&lt;br /&gt;
   ) {\&lt;br /&gt;
\&lt;br /&gt;
  my (@out) = (&amp;quot;&amp;quot;) x 2;;\&lt;br /&gt;
  my $timestamp;;\&lt;br /&gt;
\&lt;br /&gt;
  for (my $j=0;;$j&amp;lt;=1;;$j++){\&lt;br /&gt;
\&lt;br /&gt;
    if (ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_00_startsAt&amp;quot;,$j),&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot;) {       ## Der nächste Tag ist noch nicht da\&lt;br /&gt;
      if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
        Log 3, &amp;quot;$SELF 3   Diagramm   : Tibber Daten für fc&amp;quot;.$j.&amp;quot; sind noch nicht da&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
      $timestamp = POSIX::strftime(&amp;quot;%Y-%m-%d&amp;quot;,localtime(time+86400));;    ## Setze das Datum auf morgen\&lt;br /&gt;
      for (my $k=0;;$k&amp;lt;=24;;$k++) {\&lt;br /&gt;
        $out[$j] .= sprintf(&amp;quot;%s_%02d:00:00 0.0\n&amp;quot;, $timestamp, $k);;      ## Die Daten mit 0 ergänzen\&lt;br /&gt;
      }\&lt;br /&gt;
      $j = 2;;                                                            ## Aus der Schleife springen\&lt;br /&gt;
\&lt;br /&gt;
    } else {\&lt;br /&gt;
\&lt;br /&gt;
      for (my $i=0;;$i&amp;lt;=23;;$i++){\&lt;br /&gt;
        $timestamp = ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_startsAt&amp;quot;,$j,$i),&amp;quot;&amp;quot;);;\&lt;br /&gt;
        $timestamp =~ s/ /_/g;;\&lt;br /&gt;
        $out[$j] .=$timestamp.&amp;quot; &amp;quot;.::round(ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,sprintf(&amp;quot;fc%d_%02d_total&amp;quot;,$j, $i),0)*100,1).&amp;quot;\n&amp;quot;;;\&lt;br /&gt;
      } # End $i\&lt;br /&gt;
\&lt;br /&gt;
    } # End if\&lt;br /&gt;
  } # End $j\&lt;br /&gt;
  \&lt;br /&gt;
  if (AttrVal(&amp;quot;$SELF&amp;quot;,&amp;quot;verbose&amp;quot;,0) &amp;gt;=3) {\&lt;br /&gt;
      Log 3, &amp;quot;$SELF 3   Diagramm   : Werte für das Diagramm&amp;quot;;;\&lt;br /&gt;
      print($out[0]);;\&lt;br /&gt;
      print($out[1]);;\&lt;br /&gt;
    }\&lt;br /&gt;
  ## Die readings current_price und current_level dienen hier nur als Trigger Events, und müssen für\&lt;br /&gt;
  ## jedes Diagramm unterschiedlich sein, die Daten werden über $out[] bereit gestellt !!\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;bar1day&amp;quot;,0,$out[0]);;\&lt;br /&gt;
  ::DOIF_modify_card_data(&amp;quot;EVU_Tibber&amp;quot;,&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;current_level&amp;quot;,&amp;quot;bar1day&amp;quot;,-86400,$out[1]);;\&lt;br /&gt;
  ## Mit der Abfrage 03_consumption_hour werden die Trigger erneut ausgelöst\&lt;br /&gt;
  ::CommandGet(undef, &amp;quot;EVU_Tibber_connect 01_priceInfo&amp;quot;);;                ## Kosten der letzen drei Stunden\&lt;br /&gt;
##  fhem(&amp;quot;get EVU_Tibber_connect 01_priceInfo&amp;quot;);;                           ## Kosten der letzen drei Stunden\&lt;br /&gt;
\&lt;br /&gt;
    set_Reading(&amp;quot;ui_command_1&amp;quot;,&amp;quot;---&amp;quot;);;                                   ## Hier wird das uiTable select wieder zurückgesetzt, ansonsten\&lt;br /&gt;
                                                                         ## kann das Kommando nicht sofort wiederholt werden\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
&lt;br /&gt;
attr EVU_Tibber DbLogExclude .*&lt;br /&gt;
attr EVU_Tibber comment Version 2023.12.06 13:00 \&lt;br /&gt;
Dieses Device benötigt EVU_Tibber_connect als Verbindung zu Tibber.&lt;br /&gt;
attr EVU_Tibber disable 0&lt;br /&gt;
attr EVU_Tibber group PV Steuerung EVU&lt;br /&gt;
attr EVU_Tibber icon stromzaehler_icon&lt;br /&gt;
attr EVU_Tibber room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_Tibber sortby 315&lt;br /&gt;
attr EVU_Tibber uiState {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  $TD{0..6}{0} = &amp;quot;style=&#039;border-top-style:solid;;border-bottom-style:solid;;border-left-style:solid;;border-left-width:2px;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:36%;;font-weight:bold;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{1..4} = &amp;quot;style=&#039;border-top-style:solid;;border-bottom-style:solid;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:8%;;text-align:center;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..6}{5} = &amp;quot;style=&#039;border-top-style:solid;;border-bottom-style:solid;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:2px;;width:8%;;text-align:center;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Kommando&amp;lt;dd&amp;gt;Auswahl&amp;lt;/dd&amp;gt;&amp;quot; |widget([$SELF:ui_command_1],&amp;quot;uzsuDropDown,---,1_EVU_Tibber_PriceInfo,2_EVU_Tibber_PriceAll,3_EVU_Tibber_Diagramm&amp;quot;) |&amp;quot;LiveMessurement &amp;lt;br&amp;gt;&amp;quot;.widget([$SELF:ui_command_2],&amp;quot;uzsuDropDown,---,connect,disconnect&amp;quot;)|&amp;quot;&amp;quot;|[EVU_Tibber_connect:ws_cmd]\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strompreis&amp;lt;br&amp;gt;&amp;quot;.card([EVU_Tibber_connect:current_price:bar1day],undef,undef,0,60,90,0,&amp;quot;fc0  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;).card([EVU_Tibber_connect:current_level:bar1day],undef,undef,0,60,90,0,&amp;quot;fc1  &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;),undef,&amp;quot;1&amp;quot;,&amp;quot;130,,,,,,220&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;nächste 3h&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Price(1).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(2).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Price(3)|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Statistik fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_min&amp;quot;,0).&amp;quot; min&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_avg&amp;quot;,0).&amp;quot; avg&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_med&amp;quot;,0).&amp;quot; med&amp;lt;br&amp;gt;&amp;quot;.::ReadingsVal(Device(),&amp;quot;fc_max&amp;quot;,0).&amp;quot; max&amp;quot;|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc0&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;Basis &amp;quot;.widget([EVU_Tibber_connect:compensation_grid],&amp;quot;selectnumbers,0,0.1,12,1,lin&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_0&amp;quot;)|\&lt;br /&gt;
&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Trigger fc1&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;.Format(&amp;quot;trigger_1&amp;quot;)&lt;br /&gt;
attr EVU_Tibber uiTable {\&lt;br /&gt;
package ui_Table;;\&lt;br /&gt;
  $TABLE = &amp;quot;style=&#039;width:100%;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
   $TD{0..18}{0} = &amp;quot;style=&#039;border-top-style:solid;;border-bottom-style:solid;;border-left-style:solid;;border-left-width:2px;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:36%;;font-weight:bold;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{1..5} = &amp;quot;style=&#039;border-top-style:solid;;border-bottom-style:solid;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:1px;;width:8%;;text-align:center;;&#039;&amp;quot;;;\&lt;br /&gt;
  $TD{0..18}{6} = &amp;quot;style=&#039;border-top-style:solid;;border-bottom-style:solid;;border-right-style:solid;;border-color:darkgreen;;border-top-width:2px;;border-bottom-width:2px;;border-right-width:2px;;width:8%;;text-align:center;;&#039;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
sub FUNC_Status {\&lt;br /&gt;
    my($value, $min, $colorMin,  $statusMin,  $colorMiddel, $statusMiddle, $max, $colorMax, $statusMax)=@_;;\&lt;br /&gt;
    my $ret = ($value &amp;lt; $min)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMin.&#039;&amp;quot;&amp;gt;&#039;.$statusMin.&#039;&amp;lt;/span&amp;gt;&#039; : ($value &amp;gt; $max)? &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMax.&#039;&amp;quot;&amp;gt;&#039;.$statusMax.&#039;&amp;lt;/span&amp;gt;&#039; : &#039;&amp;lt;span style=&amp;quot;color:&#039;.$colorMiddel.&#039;&amp;quot;&amp;gt;&#039;.$statusMiddle.&#039;&amp;lt;/span&amp;gt;&#039;;;\&lt;br /&gt;
    return $ret;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Device {\&lt;br /&gt;
    return &amp;quot;$SELF&amp;quot;.&amp;quot;_connect&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
\&lt;br /&gt;
sub Cost {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $currency = (::ReadingsVal(Device(),&amp;quot;current_currency&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;EUR&amp;quot;)? &amp;quot; €&amp;quot; : &amp;quot;&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  return ::ReadingsVal(Device(),&amp;quot;total_cost_&amp;quot;.$i,0).$currency;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
sub Price {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
  my $j;;\&lt;br /&gt;
  my $value;;\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i == 0) {\&lt;br /&gt;
    $value = ::ReadingsVal(Device(),&amp;quot;current_price&amp;quot;,0);;\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc0_trigger&amp;quot;,&amp;quot;off&amp;quot;) eq &amp;quot;on&amp;quot; ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value .= &amp;quot; ct/kWh&amp;quot;;;\&lt;br /&gt;
  } else {\&lt;br /&gt;
    $j       = $i+$hour;;\&lt;br /&gt;
    if ($j &amp;lt; 24) {\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
      $j = $j - 24;;\&lt;br /&gt;
      $value = ::round(::ReadingsVal(Device(),&amp;quot;fc1_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot;_total&amp;quot;,0)*100,1);;\&lt;br /&gt;
    }\&lt;br /&gt;
    $value = ( ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;0&amp;quot;) &amp;gt; $value ) ?\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;&amp;quot;.$value.&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; ;;\&lt;br /&gt;
    $value = sprintf(&amp;quot;%02d&amp;quot;,$j).&amp;quot; :  &amp;quot;.$value ;;\&lt;br /&gt;
  }\&lt;br /&gt;
  return  $value;;\&lt;br /&gt;
 }\&lt;br /&gt;
\&lt;br /&gt;
sub Format {\&lt;br /&gt;
  my($i)=@_;;\&lt;br /&gt;
\&lt;br /&gt;
  my $MonthBefore   = &amp;quot;LogDBRep_Statistic_previous_Month&amp;quot;;;\&lt;br /&gt;
  my $MonthPrevious = ::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $MonthPrevious = ($MonthPrevious ne &amp;quot;null&amp;quot;) ?    POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  my $YearBefore   = &amp;quot;LogDBRep_Statistic_previous_Year&amp;quot;;;\&lt;br /&gt;
  my $YearPrevious = ::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
       $YearPrevious = ($YearPrevious ne &amp;quot;null&amp;quot;) ? POSIX::strftime(&amp;quot;%Y&amp;quot;,localtime(::time_str2num(::ReadingsTimestamp(&amp;quot;$YearBefore&amp;quot;,Device().&amp;quot;_nodes_consumption_year&amp;quot;,&amp;quot;null&amp;quot;)))) : &amp;quot;null&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
  if ($i eq &amp;quot;day&amp;quot;) {\&lt;br /&gt;
      return sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_day&amp;quot;,0));;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;month&amp;quot;) {\&lt;br /&gt;
      my $evu_em = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_month&amp;quot;,0));;\&lt;br /&gt;
      $evu_em .= ($MonthPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$MonthBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_em;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;year&amp;quot;) {\&lt;br /&gt;
      my $evu_ey = sprintf(&amp;quot;%04d&amp;quot;,::ReadingsVal(Device(),&amp;quot;nodes_consumption_year&amp;quot;,0));;\&lt;br /&gt;
      $evu_ey .= ($YearPrevious ne &amp;quot;null&amp;quot;) ? sprintf(&amp;quot; / %04d&amp;quot;, ::ReadingsVal(&amp;quot;$YearBefore&amp;quot;,&amp;quot;EVU_Tibber_Pulse_nodes_consumption_month&amp;quot;,0) ) : &amp;quot;&amp;quot;;;\&lt;br /&gt;
      return $evu_ey;;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_0&amp;quot;) {\&lt;br /&gt;
      return ((::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Heute kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
            &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc0_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                 ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
    } elsif ($i eq &amp;quot;trigger_1&amp;quot;) {\&lt;br /&gt;
      if (::ReadingsVal(Device(),&amp;quot;fc1_00_startsAt&amp;quot;,&amp;quot;null&amp;quot;) ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
        return ((::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;null&amp;quot;) eq &amp;quot;null&amp;quot; ) ?\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Morgen kein Trigger &amp;lt;br&amp;gt;mehr unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; :\&lt;br /&gt;
              &amp;quot;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Morgen ein Trigger von&amp;lt;br&amp;gt;&amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc1_trigger_start&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot; bis &amp;quot;.::ReadingsVal(Device(),&amp;quot;fc1_trigger_stop&amp;quot;,&amp;quot;00:00&amp;quot;).&amp;quot;&amp;lt;br&amp;gt;unter &amp;quot;.\&lt;br /&gt;
                   ::ReadingsVal(Device(),&amp;quot;fc_trigger_price&amp;quot;,&amp;quot;null&amp;quot;).&amp;quot; ct&amp;lt;/span&amp;gt;&amp;quot; );;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return &amp;quot;Morgen noch kein Trigger vorhanden&amp;quot;;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  return &amp;quot;null&amp;quot;;;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Statistiken &amp;quot;.::ReadingsVal(Device(),&amp;quot;current_date&amp;quot;,0).&amp;quot; in kWh&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;aktuell&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;heute&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Monat&amp;lt;/span&amp;gt;&amp;quot;|&amp;quot;&amp;lt;span style=font-weight:bold&amp;gt;Jahr&amp;lt;/span&amp;gt;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Strom&amp;lt;dd&amp;gt;Preis / Kosten&amp;lt;/dd&amp;gt;&amp;quot;|Price(0)|Cost(&amp;quot;day&amp;quot;)|Cost(&amp;quot;month&amp;quot;)|Cost(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man das liveMessurment von Tibber verwendet\&lt;br /&gt;
&amp;quot;Bezug vom Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_power]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_power&amp;quot;,0) : 0))|\&lt;br /&gt;
Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
\&lt;br /&gt;
&amp;quot;Einspeisung ins Netz&amp;quot;|\&lt;br /&gt;
sprintf(&amp;quot;%04d W&amp;quot;,([EVU_Tibber_connect:payload_data_liveMeasurement_powerProduction]  &amp;gt;= 0 ? ::ReadingsVal(&amp;quot;EVU_Tibber_connect&amp;quot;,&amp;quot;payload_data_liveMeasurement_powerProduction&amp;quot;,0) : 0))|\&lt;br /&gt;
&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;\&lt;br /&gt;
\&lt;br /&gt;
## Wenn man einen eigenes SmartMeter verwendet\&lt;br /&gt;
## &amp;quot;Bezug vom Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;gt;= 0 ? ::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0) : 0) )|Format(&amp;quot;day&amp;quot;)|Format(&amp;quot;month&amp;quot;)|Format(&amp;quot;year&amp;quot;)\&lt;br /&gt;
## &amp;quot;Einspeisung ins Netz&amp;quot;|sprintf(&amp;quot;%04d W&amp;quot;,([WR_0_KSEM:M_AC_Power] &amp;lt;= 0 ? abs(::round(::ReadingsVal(&amp;quot;WR_0_KSEM&amp;quot;,&amp;quot;M_AC_Power&amp;quot;,0),0)) :  0) )|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;|&amp;quot;&amp;quot;&lt;br /&gt;
attr EVU_Tibber verbose 3&lt;br /&gt;
&lt;br /&gt;
setstate EVU_Tibber 2024-01-23 10:03:00 ui_command_1 ---&lt;br /&gt;
setstate EVU_Tibber 2024-01-20 17:40:53 ui_command_2 ---&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_Tibber_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements wertden im EVU_Tibber_connect Device generiert und über dieses Device in der MySQL DbLog ausgeführt. Der Name des Devices wird ebenfalls generiert und liegt somit fest.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_Tibber_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_Tibber_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 2. aWATTar ==&lt;br /&gt;
&lt;br /&gt;
=== Vorausetzungen ===&lt;br /&gt;
* FHEM mit einer installierten MySQL DbLog (FileLog wird nicht untertsützt)&lt;br /&gt;
* Für die Berechnung der tatsächlichen Kosten benötigt man seine lokalen fix Kosten, die je nach Region unterschiedlich sind.&lt;br /&gt;
* Die bisherige Berechnung mit Stand 20240123 wäre wie folgt: round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
&lt;br /&gt;
=== EVU_aWATTar_connect ===&lt;br /&gt;
Das EVU_aWATTar_connect fragt mit HTTPMOD die aWATTar API Webseite ab. Es werden keine Live Verbrauchdaten geliefert.&lt;br /&gt;
&lt;br /&gt;
==== Verwendete Module ====&lt;br /&gt;
* HTTPMOD&lt;br /&gt;
* DbLog&lt;br /&gt;
* DbRep&lt;br /&gt;
&lt;br /&gt;
==== EVU_aWATTar_connect  RAW Device ====&lt;br /&gt;
Momentan fragt das EVU_aWattar_connect lediglich die aktuellen Preise des Tages ab. Die readings sind an die des EVU_Tibber_connect angelehnt, um im weiteren Verlauf evetuell ein gemeinsames Device für die Anzeige und Steuerung zu erarbeiten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod EVU_aWATTar_connect HTTPMOD https://api.awattar.de/v1/marketdata/current.yaml 0&lt;br /&gt;
attr EVU_aWATTar_connect DbLogExclude .*&lt;br /&gt;
attr EVU_aWATTar_connect comment Version 2024.01.23 13:00\&lt;br /&gt;
\&lt;br /&gt;
Achtung, momentan werden nur die Börsenpreise ohne die fix Kosten dargestellt.\&lt;br /&gt;
\&lt;br /&gt;
https://api.awattar.de/v1/marketdata&lt;br /&gt;
attr EVU_aWATTar_connect enableControlSet 1&lt;br /&gt;
attr EVU_aWATTar_connect get01-15Name fc_max&lt;br /&gt;
attr EVU_aWATTar_connect get01-15OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-1Name current_date&lt;br /&gt;
attr EVU_aWATTar_connect get01-22Name fc_med&lt;br /&gt;
attr EVU_aWATTar_connect get01-22OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-29Name fc_avg&lt;br /&gt;
attr EVU_aWATTar_connect get01-29OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-36Name current_price&lt;br /&gt;
attr EVU_aWATTar_connect get01-36OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01-8Name fc_min&lt;br /&gt;
attr EVU_aWATTar_connect get01-8OExpr round(($val+$val*0.03+1.785)*1.19,2)&lt;br /&gt;
attr EVU_aWATTar_connect get01Name 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get01RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get01Regex date_now: (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})|price_low: ([0-9]+[0-9\.]+)|price_high: ([0-9]+[0-9\.]+)|price_median: ([0-9]+[0-9\.]+)|price_average: ([0-9]+[0-9\.]+)|price_current: ([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get01URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get02-10Name fc0_09_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-10OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-11Name fc0_10_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-11OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-12Name fc0_11_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-12OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-13Name fc0_12_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-13OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-14Name fc0_13_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-14OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-15Name fc0_14_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-15OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-16Name fc0_15_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-16OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-17Name fc0_16_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-17OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-18Name fc0_17_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-18OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-19Name fc0_18_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-19OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-1Name fc0_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-1OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-20Name fc0_19_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-20OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-21Name fc0_20_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-21OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-22Name fc0_21_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-22OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-23Name fc0_22_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-23OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-24Name fc0_23_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-24OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-2Name fc0_01_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-2OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-3Name fc0_02_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-3OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-4Name fc0_03_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-4OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-5Name fc0_04_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-5OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-6Name fc0_05_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-6OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-7Name fc0_06_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-7OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-8Name fc0_07_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-8OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02-9Name fc0_08_total&lt;br /&gt;
attr EVU_aWATTar_connect get02-9OExpr round(($val+$val*0.03+1.785)*1.19/100,4)&lt;br /&gt;
attr EVU_aWATTar_connect get02FollowGet 01_priceInfo&lt;br /&gt;
attr EVU_aWATTar_connect get02Name 02_priceDay&lt;br /&gt;
attr EVU_aWATTar_connect get02RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get02Regex (?&amp;lt;=abs_[0-9]{2}_amount: )([0-9]+[0-9\.]+)&lt;br /&gt;
attr EVU_aWATTar_connect get02URL https://api.awattar.de/v1/marketdata/current.yaml&lt;br /&gt;
attr EVU_aWATTar_connect get03-1Name fc3_00_startsAt&lt;br /&gt;
attr EVU_aWATTar_connect get03-1OExpr POSIX::strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;,localtime(substr($val,0,10)))&lt;br /&gt;
attr EVU_aWATTar_connect get03-4Name fc3_00_total&lt;br /&gt;
attr EVU_aWATTar_connect get03-4OExpr round($val/1000,4)&lt;br /&gt;
attr EVU_aWATTar_connect get03Name 02_priceAll&lt;br /&gt;
attr EVU_aWATTar_connect get03RegOpt g&lt;br /&gt;
attr EVU_aWATTar_connect get03Regex &amp;quot;start_timestamp&amp;quot;: (\d{10})|&amp;quot;marketprice&amp;quot;: (\d*\.\d*)&lt;br /&gt;
attr EVU_aWATTar_connect get03URL https://api.awattar.de/v1/marketdata?start=1701990000000&amp;amp;end=1702159200000&lt;br /&gt;
attr EVU_aWATTar_connect group PV Steuerung EVU&lt;br /&gt;
attr EVU_aWATTar_connect icon sani_pump&lt;br /&gt;
attr EVU_aWATTar_connect room Strom-&amp;gt;Boerse&lt;br /&gt;
attr EVU_aWATTar_connect sortby 213&lt;br /&gt;
attr EVU_aWATTar_connect userReadings fc_trigger_price:fc_avg.* {\&lt;br /&gt;
## fc_trigger_price:[fc_avg|compensation_grid].* {\\&lt;br /&gt;
  my $fc_avg = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_avg&amp;quot;,0);;\&lt;br /&gt;
  my $fc_min = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_min&amp;quot;,0);;\&lt;br /&gt;
\&lt;br /&gt;
  # Berechnung eines Default Schwellwertes als täglicher Niedrigpreis\&lt;br /&gt;
  my $price_level = round( ($fc_avg - $fc_min)/2 + $fc_min , 1);;\&lt;br /&gt;
 \&lt;br /&gt;
  # Abschätzung von Wirtschaftlichkeit beim Speicher Laden, falls Tibber zu teuer wird\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0) != 0 ) {\&lt;br /&gt;
    my $price_level_battery = round( ($fc_avg - ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;compensation_grid&amp;quot;,0)) *0.85 , 1) ;;\&lt;br /&gt;
    if ( $price_level &amp;gt; $price_level_battery ) {\&lt;br /&gt;
      $price_level = $price_level_battery;;\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  $price_level;;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_start:fc_trigger_price.* {\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);; $year += 1900;; $mon += 1 ;;\&lt;br /&gt;
  my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for (my $loop_hour = $hour;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour)) ;;\&lt;br /&gt;
      }\&lt;br /&gt;
    } # end  for loop_hour\&lt;br /&gt;
\&lt;br /&gt;
  return(&amp;quot;null&amp;quot;);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger_stop:fc0_trigger_start.* {\&lt;br /&gt;
  my $fc = 0;;\&lt;br /&gt;
  my $loop_hour = 0;;\&lt;br /&gt;
  my $fc_trigger_price = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) /100;;\&lt;br /&gt;
\&lt;br /&gt;
  # Ermitteln des nächsten Trigger Fensters\&lt;br /&gt;
  my $fc_trigger_start = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc0_trigger_start&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  my $fc_trigger_stop = $fc_trigger_start;;\&lt;br /&gt;
\&lt;br /&gt;
  if ( $fc_trigger_start ne &amp;quot;null&amp;quot; ) {\&lt;br /&gt;
    $fc_trigger_start =~ /(\d\d):/;; $fc_trigger_start = $1 ;;\&lt;br /&gt;
    my $fc_total = 0;;\&lt;br /&gt;
\&lt;br /&gt;
    for ($loop_hour = $fc_trigger_start;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $fc_total = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$fc.&amp;quot;_&amp;quot;.sprintf(&amp;quot;%02d&amp;quot;,$loop_hour).&amp;quot;_total&amp;quot;,0);;\&lt;br /&gt;
      if ( $fc_total &amp;lt; $fc_trigger_price ) {\&lt;br /&gt;
        $fc_trigger_stop = sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour) ;;\&lt;br /&gt;
      } else {\&lt;br /&gt;
        return(sprintf(&amp;quot;%02d:00&amp;quot;,$loop_hour));;\&lt;br /&gt;
      }\&lt;br /&gt;
\&lt;br /&gt;
     # wechsel zum nächsten Tag\&lt;br /&gt;
     if ( $loop_hour == 23 and $fc == 0 ) {\&lt;br /&gt;
       $fc = 1;;\&lt;br /&gt;
       $loop_hour = -1;;\&lt;br /&gt;
     }\&lt;br /&gt;
\&lt;br /&gt;
    } # end for loop_hour\&lt;br /&gt;
  }\&lt;br /&gt;
 \&lt;br /&gt;
  return($fc_trigger_stop);;\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc0_trigger:fc0_trigger_stop.* {\&lt;br /&gt;
\&lt;br /&gt;
  # Setzen des Triggers für die aktuelle Stunde\&lt;br /&gt;
  if ( ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_price&amp;quot;,100)  &amp;lt; ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc_trigger_price&amp;quot;,0) ) {\&lt;br /&gt;
    return(&amp;quot;on&amp;quot;)\&lt;br /&gt;
  } else {\&lt;br /&gt;
    return(&amp;quot;off&amp;quot;)\&lt;br /&gt;
  }\&lt;br /&gt;
},\&lt;br /&gt;
\&lt;br /&gt;
fc_DbLog:fc0_00_total.* {\&lt;br /&gt;
my ($timestamp,$date,$hour,$value,$loop_fc_next) = 5x0;;\&lt;br /&gt;
\&lt;br /&gt;
for (my $loop_fc = 0;; $loop_fc &amp;lt;= 1;; $loop_fc++) {\&lt;br /&gt;
  $loop_fc_next = $loop_fc +1;;\&lt;br /&gt;
  $date = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
  if ($date ne &amp;quot;null&amp;quot;) {\&lt;br /&gt;
    $date =~ /([\d+-]+)/;; $date = $1 ;;\&lt;br /&gt;
    for (my $loop_hour = 0;; $loop_hour &amp;lt;= 23;; $loop_hour++) {\&lt;br /&gt;
      $hour = sprintf(&amp;quot;%02d&amp;quot;,$loop_hour);;\&lt;br /&gt;
      $timestamp = $date.&amp;quot; &amp;quot;.$hour.&amp;quot;:00:00&amp;quot;;;\&lt;br /&gt;
      $value = ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;fc&amp;quot;.$loop_fc.&amp;quot;_&amp;quot;.$hour.&amp;quot;_total&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
      ::CommandGet(undef, &amp;quot;LogDBRep_&amp;quot;.$NAME.&amp;quot;_SQL sqlCmdBlocking\&lt;br /&gt;
                        INSERT INTO history (TIMESTAMP,DEVICE,TYPE,READING,VALUE)\&lt;br /&gt;
                          VALUES(&#039;&amp;quot;.$timestamp.&amp;quot;&#039;,&#039;$NAME&#039;,&#039;Tibber&#039;,&#039;fc&amp;quot;.$loop_fc.&amp;quot;_total&#039;,&#039;&amp;quot;.$value.&amp;quot;&#039;)\&lt;br /&gt;
                        ON DUPLICATE KEY UPDATE\&lt;br /&gt;
                          VALUE=&#039;&amp;quot;.$value.&amp;quot;&#039;;;&amp;quot;) ;;\&lt;br /&gt;
    }\&lt;br /&gt;
  } else {\&lt;br /&gt;
      fhem(&amp;quot;deletereading $NAME fc1_.*&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
}\&lt;br /&gt;
ReadingsVal(&amp;quot;$NAME&amp;quot;,&amp;quot;current_date&amp;quot;,&amp;quot;null&amp;quot;);;\&lt;br /&gt;
}&lt;br /&gt;
attr EVU_aWATTar_connect verbose 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== LogDBRep_EVU_aWATTar_connect_SQL RAW Device ====&lt;br /&gt;
Dieses DbRep Device dient der Verbindung zur MySQL DbLog. Die SQL SELECT Statements wertden im EVU_Tibber_connect Device generiert und über dieses Device in der MySQL DbLog ausgeführt. Der Name des Devices wird ebenfalls generiert und liegt somit fest.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod LogDBRep_EVU_aWATTar_connect_SQL DbRep LogDB&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL DbLogExclude .*&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL room System&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL sqlCmdHistoryLength 5&lt;br /&gt;
attr LogDBRep_EVU_aWATTar_connect_SQL timeout 20&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Website (deutsch) des Anbieters [https://tibber.com/de Tibber]&lt;br /&gt;
* Website (deutsch) des Anbieters [https://www.awattar.de/ aWATTar]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FTUI_Widget_Checkbox&amp;diff=37375</id>
		<title>FTUI Widget Checkbox</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FTUI_Widget_Checkbox&amp;diff=37375"/>
		<updated>2022-04-03T05:57:35Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Attribute */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Das [[{{PAGENAME}}|Widget Checkbox]] ist ein Widget für [[FHEM Tablet UI]], welches als Umschalter zwischen zwei Zuständen eingesetzt wird. Es verhält sich ähnlich wie das [[FTUI_Widget_Switch]] und orientiert sich optisch an Konfigurationsschaltern moderner grafischer Benutzeroberflächen. Daher empfiehlt es sich, die Checkbox weniger als Schalter für Devices einzusetzen, sondern vornehmlich für die Aktivierung von Funktionalitäten, zumal in dieses Widget keine Symbole integriert werden können.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery height=&amp;quot;100&amp;quot; widths=&amp;quot;300&amp;quot;&amp;gt;&lt;br /&gt;
File:FTUI_Widget_Checkbox_01.png&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&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-device&#039;&#039;&#039;||FHEM-Device, dessen Reading gesetzt werden soll (&amp;lt;command&amp;gt; &#039;&#039;&#039;&amp;lt;device&amp;gt;&#039;&#039;&#039; &amp;lt;reading&amp;gt; &amp;lt;value&amp;gt;)||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-get&#039;&#039;&#039;||Reading, dessen Inhalt den aktuellen Wert wiedergibt||&#039;STATE&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-set&#039;&#039;&#039;||Reading, das gesetzt werden soll (&amp;lt;command&amp;gt; &amp;lt;device&amp;gt; &#039;&#039;&#039;&amp;lt;reading&amp;gt;&#039;&#039;&#039; &amp;lt;value&amp;gt;)||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-cmd&#039;&#039;&#039;||Befehl, der gesendet werden soll (&#039;&#039;&#039;&amp;lt;command&amp;gt;&#039;&#039;&#039; &amp;lt;device&amp;gt; &amp;lt;reading&amp;gt; &amp;lt;value&amp;gt;) (z.B. setstate, set, setreading, trigger)||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-doubleclick (n/a)&#039;&#039;&#039;||Zeit, die vergehen muss, bis das Element ein zweites mal betätigt werden kann. &#039;0&#039; schaltet dieses Verhalten ab.||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-lock (n/a)&#039;&#039;&#039;||Reading, mit dem das Element für eine Bedienung gesperrt wird. Gütige Werte sind &#039;true&#039;, &#039;1&#039; oder &#039;on&#039; ||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-color&#039;&#039;&#039;||Farbe des Icon (RGB-Angabe oder Farbname)||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-background-color&#039;&#039;&#039;||Hintergrundfarbe des Widgets||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-hide&#039;&#039;&#039;||Reading zum Verstecken des Elementes. Das Element verschwindet, wenn der Wert mit &#039;&#039;&#039;data-hide-on&#039;&#039;&#039; übereinstimmt.||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-hide-on&#039;&#039;&#039;||Zeichenkette, bei der das Element versteckt wird||&amp;lt;nowiki&amp;gt;&#039;true|1|on&#039;&amp;lt;/nowiki&amp;gt;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-hide-off&#039;&#039;&#039;||Zeichenkette, bei der das Element angezeigt wird||&#039;!on&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-hideparents&#039;&#039;&#039;||jQuery-Selector, um auch die Eltern-Elemente zu verstecken||||data-hideparents=&amp;quot;#Name&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-reachable&#039;&#039;&#039;||Reading, dessen Inhalt angibt, ob das Device erreichbar ist, oder nicht. Der Inhalt des Readings muss eine Ganzzahl (Integer) sein.||||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==CSS Klassen==&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Klasse!!Beschreibung&lt;br /&gt;
{{FTUI Klasse|small}}{{FTUI Klasse|normal}}{{FTUI Klasse|large}}{{FTUI Klasse|transparent}}{{FTUI Klasse|half-transparent}}{{FTUI Klasse|hide}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Zusätzlich sind alle Vordergrundfarben-[[FHEM_Tablet_UI#CSS-Klassen|Klassen]] als Hintergrundfarbe für den Zustand &amp;quot;ON&amp;quot; verfügbar.&lt;br /&gt;
&lt;br /&gt;
==Beispiele==&lt;br /&gt;
[[File:FTUI_Widget_Checkbox_01.png]]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div data-type=&amp;quot;checkbox&amp;quot; class=&amp;quot;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:FTUI_Widget_Checkbox_03.png]]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div data-type=&amp;quot;checkbox&amp;quot; &lt;br /&gt;
     data-on-color=&amp;quot;green&amp;quot; &lt;br /&gt;
     data-on-background-color=&amp;quot;blue&amp;quot; &lt;br /&gt;
     data-off-color=&amp;quot;red&amp;quot; &lt;br /&gt;
     data-off-background-color=&amp;quot;white&amp;quot; &lt;br /&gt;
     class=&amp;quot;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
[[FTUI_Widget_Switch]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:FHEM Tablet UI|Checkbox]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=SVG&amp;diff=37267</id>
		<title>SVG</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=SVG&amp;diff=37267"/>
		<updated>2022-02-26T01:55:22Z</updated>

		<summary type="html">&lt;p&gt;Stephan: Links zur commandref &amp;quot;repariert&amp;quot;. Sieht nicht mehr ganz so schön aus wie vorher, funktioniert dafür aber wieder;)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Erstellung von Diagrammen aus Logdaten&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Frontends&lt;br /&gt;
|ModTechName=98_SVG.pm &lt;br /&gt;
|ModOwner=rudolfkoenig ({{Link2FU|8|Forum}} / [[Benutzer Diskussion:Rudolfkoenig|Wiki]])}}&lt;br /&gt;
&lt;br /&gt;
Das Modul [[SVG]] generiert Diagramme aus [[FileLog|Logdateien]] oder [[DbLog|-datenbanken]].&lt;br /&gt;
&lt;br /&gt;
Die Eigenschaften des zu erstellenden Diagrams werden in einer .gplot-Datei festgelegt.&lt;br /&gt;
&lt;br /&gt;
== Definition ==&lt;br /&gt;
Die Definition &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define &amp;lt;name&amp;gt; SVG &amp;lt;logDevice&amp;gt;:&amp;lt;gplotfile&amp;gt;:&amp;lt;logfile&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;besteht lediglich in der Angabe einer Datenquelle, einer .gplot-File und der Angabe von entweder &#039;&#039;CURRENT&#039;&#039; (für LogFile-Quellen) bzw. &#039;&#039;HISTORY&#039;&#039; (für Datenbanken).&lt;br /&gt;
&lt;br /&gt;
Details zur Definition sind in der {{Link2CmdRef|Anker=SVG-define}} dargestellt.&lt;br /&gt;
&lt;br /&gt;
== Hintergründe ==&lt;br /&gt;
Ursprünglich wurde für das Rendern von Diagrammen in FHEM das Programmpaket &#039;&#039;[http://gnuplot.sourceforge.net/ gnuplot]&#039;&#039; verwendet. Daher entspricht die Syntax der &#039;&#039;.gplot&#039;&#039;-Files in großen Teilen derjenigen von &#039;&#039;gnuplot&#039;&#039; (z.B. zu den Linientypen und -farben oder zur Gestaltung des Hintergrundes). Allerdings eben nur in großen Teilen, denn das Modul &#039;&#039;SVG&#039;&#039; implementiert eigenständige Rendering-Routinen, so dass die grundlegenden Formatierungsvorgaben dort, wo dies über spezielle Attribute gesteuert werden kann (z.B. zur Zahl der Achsen), auch über die Attribute vorgenommen werden sollte. Weiter sollte man nicht davon ausgehen, dass alle eventuell von gnuplot her bekannten Funktionen unterstützt werden.&lt;br /&gt;
Weitere Hinweise hierzu und zum Aufbau der &#039;&#039;.gplot&#039;&#039;-Files sind in [[Plots_erzeugen#Individuell / Manuell|Plots erzeugen]] zu finden.&lt;br /&gt;
{{Hinweis|Der Artikel [[Plots_erzeugen|Plots erzeugen]] enthält auch eine Darstellung des gplot-Editors. Dieser kann ein gutes Hilfsmittel sein, um ein grundlegendes Verständnis der Funktionsweise zu erhalten. Man erhält damit allerdings &#039;&#039;.gplot&#039;&#039;-Dateien, die sämtliche Angaben hart vercodet beinhalten. Falls Sie beabsichtigen, eine &#039;&#039;.gplot&#039;&#039;-File zur Verwendung mit mehreren gleichartigen Geräten anzulegen, empfiehlt es sich, die Dateien direkt zu bearbeiten und die Verweise auf die darzustellenden Daten über Attribute zu steuern, wie nachfolgend dargestellt.}}&lt;br /&gt;
== Attribute ==&lt;br /&gt;
Das wichtigste spezifische Attribut ist  &#039;&#039;plotReplace&#039;&#039;, das einige andere, ältere Attribute ersetzen kann. Durch dieses können Angaben in den .gplot-Dateien dynamisch ersetzt werden, wodurch es auf einfache Weise möglich ist, dieselbe .gplot-Datei zur Erstellung von mehreren Plots zu nutzen, die dann auch in einem Plot unterschiedliche Datenquellen verwenden können. Dabei ist zu beachten, dass die in &#039;&#039;plotReplace&#039;&#039; angegebenen Parameter-Wert-Paare in zwei Stufen ersetzt werden: &lt;br /&gt;
&lt;br /&gt;
Wird in der .gplot-Datei die Schreibweise &#039;&#039;%key%&#039;&#039; verwendet, wird der Parameter bereits ersetzt, bevor die Datenquelle ausgewertet wurde, in der Schreibweise &#039;&#039;&amp;lt;key&amp;gt;&#039;&#039; erfolgt die Auswertung erst danach. Dies ermöglicht es z.B. auch, Informationen aus unterschiedlichen FileLog-Dateien zu visualisieren, ohne die betreffende Datenquelle hart in der .gplot-Datei zu verankern.&lt;br /&gt;
&lt;br /&gt;
Weiter ist es über diesen Mechanismus möglich, auch angepaßten Perl-Code zur Datenauswertung und -formatierung zu verwenden.&lt;br /&gt;
&lt;br /&gt;
Siehe dazu insbesondere auch die nachfolgenden beiden Beispiele &amp;quot;Leistungsdaten einer Steckdose (FileLog)&amp;quot; und &amp;quot;Temperatur- und Heizungsdaten eines Zimmers&amp;quot; (DBLog).&lt;br /&gt;
&lt;br /&gt;
Weitere Details in der {{Link2CmdRef|Anker=SVG-attr}}.&lt;br /&gt;
&lt;br /&gt;
== Funktionen ==&lt;br /&gt;
Details in der {{Link2CmdRef|Anker=SVG-attr-plotfunction}}.&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
&lt;br /&gt;
=== Leistungsdaten einer Steckdose (FileLog) ===&lt;br /&gt;
&lt;br /&gt;
* Inhalt der Datei &#039;&#039;&#039;SVG_PlugX.gplot&#039;&#039;&#039;&lt;br /&gt;
{{Randnotiz|RNTyp=g|RNText=Gegebenenfalls sind die Feldnamen anzupassen}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Gnuplot&amp;quot;&amp;gt;&lt;br /&gt;
# Leistungsdaten einer Steckdose (FileLog)&lt;br /&gt;
#&lt;br /&gt;
# plotReplace-Parameter: DataDevice        (von diesem Gerät stammen die Daten)&lt;br /&gt;
#                        FileLogDevice     (in diesem Gerät werden die Daten geloggt)&lt;br /&gt;
#                        stateOFFvalue     (y-Wert zur Darstellung von ausgeschaltetem Zustand)&lt;br /&gt;
#                        stateONvalue      (y-Wert zur Darstellung von eingeschaltetem Zustand)&lt;br /&gt;
#                        stateUNKNOWNvalue (y-Wert zur Darstellung von unbekanntem Zustand)&lt;br /&gt;
#                        title             (in der Regel ein perl-Ausdruck zur Darstellung von Eckwerten in der Titelzeile)&lt;br /&gt;
#                        yrange            (Wertebereich für Darstellung in W vorgeben)&lt;br /&gt;
#                        y2range           (Wertebereich für Darstellung in mA vorgeben)&lt;br /&gt;
&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;title&amp;gt;&#039;&lt;br /&gt;
set ytics &lt;br /&gt;
set y2tics &lt;br /&gt;
set grid ytics y2tics&lt;br /&gt;
set ylabel &amp;quot;Leistung (W)&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Strom (mA)&amp;quot;&lt;br /&gt;
set yrange %yrange%&lt;br /&gt;
set y2range %y2range%&lt;br /&gt;
&lt;br /&gt;
#%FileLogDevice% 4:%DataDevice%.power\x3a:0:&lt;br /&gt;
#%FileLogDevice% 4:%DataDevice%.current\x3a:&amp;quot;&amp;quot;:($fld[3]*1000)&lt;br /&gt;
#%FileLogDevice% 4:%DataDevice%.userRead_socket_state\x3a:&amp;quot;&amp;quot;:($fld[3]=~&amp;quot;off&amp;quot;?%stateOFFvalue%:($fld[3]=~&amp;quot;on&amp;quot;?%stateONvalue%:%stateUNKNOWNvalue%))&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Leistung&#039; ls l0 lw 1 with steps,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Strom&#039; ls l1fill_stripe lw 1 with steps,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Schaltzustand&#039; ls l3 lw 1 with steps&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Definition von SVG-Device &#039;&#039;&#039;SVG_Test&#039;&#039;&#039;&lt;br /&gt;
{{Randnotiz|RNTyp=g|RNText=Alle Vorkommen von ...Data-Device... und ...FileLog-Device... sind zu ersetzen}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod SVG_Test SVG ...FileLog-Device...:SVG_PlugX:CURRENT&lt;br /&gt;
attr SVG_Test captionPos auto&lt;br /&gt;
attr SVG_Test plotReplace DataDevice=&amp;quot;...Data-Device...&amp;quot; FileLogDevice=&amp;quot;...FileLog-Device...&amp;quot; stateOFFvalue=&amp;quot;20&amp;quot; stateONvalue=&amp;quot;30&amp;quot; stateUNKNOWNvalue=&amp;quot;10&amp;quot; yrange=&amp;quot;[0:75]&amp;quot; y2range=&amp;quot;[0:300]&amp;quot; title={&amp;quot;Steckdose(Testgerät) --- W: $data{min1}...$data{max1} / $data{currval1} --- mA: $data{min2}...$data{max2} / $data{currval2}&amp;quot;}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* So sieht das resultierende Diagramm aus&lt;br /&gt;
[[Datei:SVG Leistungsdaten einer Steckdose.png|thumb|left|300px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Temperatur- und Heizungsdaten eines Zimmers === &lt;br /&gt;
Quelle: {{Link2Forum|Topic=104935|Message=1011527}}&lt;br /&gt;
(Datenquelle ist ein DBLog-Device namens LogDB):&lt;br /&gt;
&lt;br /&gt;
[[Datei:PlotReplace Example DBLog.png|thumb|right|300px]]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Gnuplot&amp;quot;&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2020-01-06&lt;br /&gt;
# to replace variables, use plotReplace like&lt;br /&gt;
# plotReplace-Parameter: SPEC1=Thermostat  (von diesem Gerät stammen die Thermostat-Daten)&lt;br /&gt;
#                        DBLogDevice       (in diesem Gerät werden die Daten geloggt)&lt;br /&gt;
#       Die übrigen plotReplace-Parameter sind selbsterklärend, Beispiel:&lt;br /&gt;
# attr SVG_Thermostat plotReplace DBLogDevice=DBLog SPEC1=Thermostat TL=&amp;quot;My example thermostat&amp;quot; EXTERNALTEMPDEVICE=MYSENSOR_97 EXTERNALTEMPREADING=temperature2 \&lt;br /&gt;
# ROOMHUMIDITYDEVICE=HUEDevice3 EXTERNALHUMIDITYREADING=humidity ROOMTEMPDEVICE=HUEDevice4 ROOMTEMPREADING=temperature WINDOW1=Window1_MyRoom WINDOW2=Window2_MyRoom&lt;br /&gt;
&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;Valve/Window&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Temperature&amp;quot;&lt;br /&gt;
set yrange [-5:105]&lt;br /&gt;
set y2range [-15:35]&lt;br /&gt;
set isosample 20&lt;br /&gt;
&lt;br /&gt;
#%DBLogDevice% %SPEC1%:desired-temp::&lt;br /&gt;
#%DBLogDevice% %SPEC1%:measured-temp:0:&lt;br /&gt;
#%DBLogDevice% %EXTERNALTEMPDEVICE%:%EXTERNALTEMPREADING%::&lt;br /&gt;
#%DBLogDevice% %SPEC1%:actuator:0:int&lt;br /&gt;
#%DBLogDevice% %ROOMHUMIDITYDEVICE%:%EXTERNALHUMIDITYREADING%:0:&lt;br /&gt;
#%DBLogDevice% %ROOMTEMPDEVICE%:%ROOMTEMPREADING%:0:&lt;br /&gt;
#%DBLogDevice% %WINDOW1%:state:::$val=~s/(open|closed|tilted)(\d*).*/$1eq&amp;quot;open&amp;quot;?60:$1eq&amp;quot;tilted&amp;quot;?45:0/eg&lt;br /&gt;
#%DBLogDevice% %WINDOW2%:state:::$val=~s/(open|closed|tilted)(\d*).*/$1eq&amp;quot;open&amp;quot;?60:$1eq&amp;quot;tilted&amp;quot;?45:0/eg&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Desired&#039; ls l0 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 2:2 axes x1y2 title &#039;Measured actuator&#039; ls l7 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Aussen&#039; ls l1 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 y2title &#039;Valve&#039; ls l5 lw 1 with steps,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Humidity&#039; ls l2 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Measured roomsensor&#039; ls l3 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 y2title &#039;%WINDOW1%&#039; ls l4fill lw 0.5 with steps,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 y2title &#039;%WINDOW2%&#039; ls l5fill lw 0.5 with steps&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== Hinweise ==&lt;br /&gt;
* Plot-Abrisse vermeiden&lt;br /&gt;
Sind in den Quelldaten nicht genügend Datenpunkte vorhanden, kann es zu Lücken in der Darstellung kommen. Abhilfe kann man mit [[LogProxy]] schaffen oder, indem man zusätzliche Datenpunkte in den Datenquellen generiert, z.B. durch das Setzen von &#039;&#039;addLog&#039;&#039;-Attributen an FileLog bzw. DBLog-Geräten. Weitere Hinweise sind in [[Plot-Abriss vermeiden]] zu finden.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Plots erzeugen]]&lt;br /&gt;
* [[Creating Plots]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
[[Kategorie:Logging]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Event-aggregator&amp;diff=34636</id>
		<title>Event-aggregator</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Event-aggregator&amp;diff=34636"/>
		<updated>2021-01-18T19:20:04Z</updated>

		<summary type="html">&lt;p&gt;Stephan: Alternative: DOIF hinzugefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{SEITENTITEL:event-aggregator}}  &amp;lt;!-- da richtige Schreibweise kleinen Anfangsbuchstaben hat --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Infobox Attribut sinnvoll? --&amp;gt;&lt;br /&gt;
{{Baustelle}}&lt;br /&gt;
Mit dem Attribut [[event-aggregator]] können (nach Wunsch zeitlich gewichtete) Durchschnittswerte, Minima, Maxima oder Median etc. berechnet werden. Der Median kann hilfreich sein, um Messwerte mit Ausreissern (unsinnige Werte, z.B. durch Übertragungsfehler) zu glätten.&lt;br /&gt;
&lt;br /&gt;
== Syntax ==&lt;br /&gt;
Das &#039;&#039;event-aggregator&#039;&#039; Attribut wird in der folgenden Weise spezifiziert:&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;attr &amp;lt;device&amp;gt; event-aggregator reading:interval:method:function:holdTime&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Readings werden als kommagetrennte Liste angegeben.&lt;br /&gt;
&lt;br /&gt;
Die einzelnen Teile haben folgende Bedeutung:&lt;br /&gt;
=== reading ===&lt;br /&gt;
Das zu aggregierende Reading des aktuellen Gerätes. Das Reading selbst muss seine Werte aus einer Aktion oder einem Event in FHEM erhalten (beispielsweise, indem Temperaturwerte ausgegeben oder andere Größen in das Reading geschrieben werden). Indem dann der event-aggregator auf dieses Reading angewandt wird, werden die erhaltenen Größen bearbeitet - je nach Installation wird dann also das Minimum, das Maximum oder der Median der Werte beim Reading ausgegeben.&lt;br /&gt;
&lt;br /&gt;
Wichtig: Es kann immer nur einen event-aggregator pro Reading geben. Will man daher mehrere Werte (z.B. min, max, avg), muss man weitere Readings erzeugen (z.B. mit userReadings oder notify). Der Aggregator kann als regulärer Ausdruck angegeben werden (bsp. .*_rain.*)&lt;br /&gt;
&lt;br /&gt;
=== interval === &lt;br /&gt;
&lt;br /&gt;
Updates des &amp;lt;readings&amp;gt; werden ignoriert, Events werden für mindestens &amp;lt;interval&amp;gt; Sekunden unterdrückt.&lt;br /&gt;
&lt;br /&gt;
Nach der interval-periode wird das reading mit einem Wert upgedated, der sich aus den Werten und Zeitstempeln der vorher ignorierten Updates zusammensetzt.&lt;br /&gt;
&lt;br /&gt;
=== method === &lt;br /&gt;
&lt;br /&gt;
betrifft die Gewichtung nach Zeitintervallen&lt;br /&gt;
* &amp;lt;code&amp;gt;none&amp;lt;/code&amp;gt;: keine zeitliche Gewichtung&lt;br /&gt;
* &amp;lt;code&amp;gt;const&amp;lt;/code&amp;gt;: Annahme, dass zwischen den zwei Messpunkten keine Veränderung stattgefunden hat&lt;br /&gt;
* &amp;lt;code&amp;gt;linear&amp;lt;/code&amp;gt;: Annahme, dass der Wert sich zwischen zwei Messpunkten linear verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== function === &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; Anzahl&lt;br /&gt;
* &amp;lt;code&amp;gt;min&amp;lt;/code&amp;gt; Minimum&lt;br /&gt;
* &amp;lt;code&amp;gt;max&amp;lt;/code&amp;gt; Maximum&lt;br /&gt;
* &amp;lt;code&amp;gt;mean&amp;lt;/code&amp;gt; artihmetischer Mittelwert&lt;br /&gt;
* &amp;lt;code&amp;gt;sd&amp;lt;/code&amp;gt; Standardabweichung&lt;br /&gt;
* &amp;lt;code&amp;gt;integral&amp;lt;/code&amp;gt; Summe (falls holdTime nicht angegeben) oder Integral für den Zeitraum holdTime&lt;br /&gt;
* &amp;lt;code&amp;gt;median&amp;lt;/code&amp;gt; [https://de.wikipedia.org/wiki/Median Median] (nur für method &amp;lt;code&amp;gt;none&amp;lt;/code&amp;gt; und gesetzte holdTime) - im Gegensatz zum Mittelwert nicht anfällig für Ausreisser, hilfreich bei Sensoren mit sporadisch unsinnigen Messwerten&lt;br /&gt;
&lt;br /&gt;
=== holdTime === &lt;br /&gt;
&lt;br /&gt;
Zeitfenster in Sekunden, für die die vergangenen Werte gehalten werden, um die Aggregatfunktion zu berechnen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Duplizieren von Readings==&lt;br /&gt;
Wenn mehrere Funktionen für ein Reading berechnet werden sollen, muss dieses Reading zuvor dupliziert werden.&lt;br /&gt;
In der Commandref wird ein notify vorgeschlagen, evtl. können aber auch DOIF_Readings, event_Readings und userReadings verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
original_reading:interval:method:function:holdTime&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== DOIF_Readings ===&lt;br /&gt;
TODO!&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
original_reading:interval:method:function:holdTime&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== event_Readings ===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
attr DOIF-Device event_Readings &lt;br /&gt;
original_reading:[&amp;lt;Device&amp;gt;:&amp;lt;Reading&amp;gt;], &amp;lt;br /&amp;gt;&lt;br /&gt;
copy_1:[$SELF:original_reading], &amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;s&amp;gt;copy_2:int(10*[$SELF:copy_1])/10&amp;lt;/s&amp;gt; Funktioniert so nicht!!! &amp;lt;br /&amp;gt;&lt;br /&gt;
attr DOIF-Device event-aggregator copy_1:interval:method:function:holdTime&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== userReadings ===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
attr DOIF-Device userReadings &lt;br /&gt;
original_reading {ReadingsVal(&amp;quot;&amp;lt;Device&amp;gt;&amp;quot;,&amp;quot;&amp;lt;Reading&amp;gt;&amp;quot;,0)}, &amp;lt;br /&amp;gt;&lt;br /&gt;
copy_1 {ReadingsVal($name,&amp;quot;original_reading&amp;quot;,0)}, &amp;lt;br /&amp;gt;&lt;br /&gt;
copy_2 {int(10*ReadingsVal($name,&amp;quot;copy_1&amp;quot;,0))/10}&amp;lt;br /&amp;gt;&lt;br /&gt;
attr DOIF-Device event-aggregator copy_1:interval:method:function:holdTime&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Dabei darf anscheinend kein Leerzeichen zwischen mehreren Readings vorkommen: https://forum.fhem.de/index.php/topic,114947.msg1091775.html#msg1091775&lt;br /&gt;
&lt;br /&gt;
== Wechselwirkungen == &lt;br /&gt;
- keine bekannt - &lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
; aus der {{Link2CmdRef|Anker=Event-aggregator}}&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
attr myPowerMeter event-aggregator EP_POWER_METER:300:linear:mean,EP_ENERGY_METER:300:none:v&lt;br /&gt;
&lt;br /&gt;
attr myBadSensor event-aggregator TEMP::none:median:300&lt;br /&gt;
&lt;br /&gt;
attr mySunMeter event-aggregator SUN_INTENSITY_24H::const:integral:86400&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
*[[event-on-update-reading]]&lt;br /&gt;
*[[event-min-interval]]&lt;br /&gt;
*[[event-aggregator]]&lt;br /&gt;
Es gibt mittlerweile auch bei DOIF eine Funktion, die diverse Mittelwerte berechnet. Für alle, die mit event-aggregator hadern, ggf. eine Alternative.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Benutzungstipps (&#039;&#039;Best Practice&#039;&#039;) für das Attribut in {{Link2Forum|Topic=36522|LinkText=diesem Forenthread}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Attribut (allgemeingültig)]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=NUKI&amp;diff=34518</id>
		<title>NUKI</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=NUKI&amp;diff=34518"/>
		<updated>2021-01-01T02:46:56Z</updated>

		<summary type="html">&lt;p&gt;Stephan: Rechtschreibfehler behoben&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div style=&amp;quot;float:right&amp;quot;&amp;gt;&lt;br /&gt;
{{Infobox Modul&lt;br /&gt;
|ModPurpose=Einbindung der Nuki Bridge HW/APP&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=NUKIBridge&lt;br /&gt;
|ModForumArea=Sonstige Systeme&lt;br /&gt;
|ModTechName=73_NUKIBridge.pm&lt;br /&gt;
|ModOwner=CoolTux &amp;lt;br /&amp;gt;({{Link2FU|13684|Forum}} / [[Benutzer:CoolTux|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
{{Infobox Modul&lt;br /&gt;
|ModPurpose=Steuern von Nuki Smartlocks über NUKIBridge&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=NUKIDevice&lt;br /&gt;
|ModForumArea=Sonstige Systeme&lt;br /&gt;
|ModTechName=73_NUKIDevice.pm&lt;br /&gt;
|ModOwner=CoolTux &amp;lt;br /&amp;gt;({{Link2FU|13684|Forum}} / [[Benutzer:CoolTux|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==NUKIBridge==&lt;br /&gt;
NUKIBridge - Steuert das Nuki Smartlock über die Nuki Bridge &lt;br /&gt;
&lt;br /&gt;
Das Nuki Bridge Modul verbindet FHEM mit der Nuki Bridge und liest dann alle auf der Bridge verfügbaren Smartlocks ein. Desweiteren werden automatisch die erkannten Smartlocks als eigenständige Devices angelegt.&lt;br /&gt;
&lt;br /&gt;
Für eine aktive Callback Funktion müssen noch zwei Attribute der Bridge eingerichtet werden:&lt;br /&gt;
* webhookFWinstance - zu verwendende Webinstanz (darf keine Passwortabfrage beinhalten)&lt;br /&gt;
* webhookHttpHostname - IP oder FQDN des FHEM Servers&lt;br /&gt;
&lt;br /&gt;
Voraussetzung für ein fehlerfreies Laden ist die Installation von JSON (libperl-json).&lt;br /&gt;
&lt;br /&gt;
===Definition===&lt;br /&gt;
:&amp;lt;code&amp;gt;define &amp;lt;name&amp;gt; NUKIBridge &amp;lt;HOST&amp;gt; &amp;lt;API-TOKEN&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
:&amp;lt;code&amp;gt;define NBridge1 NUKIBridge 192.168.0.23 F34HK6&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Anweisung erstellt ein NUKIBridge Device mit Namen NBridge1 und der IP 192.168.0.23 sowie dem Token F34HK6. Nach dem Anlegen des Bridge Devices werden alle zur Verfügung stehende Smartlock automatisch in FHEM angelegt.&lt;br /&gt;
&lt;br /&gt;
===Readings===&lt;br /&gt;
* 0_nukiId - ID des ersten gefundenen Nuki Smartlocks&lt;br /&gt;
* 0_name - Name des ersten gefunden Nuki Smartlocks&lt;br /&gt;
* smartlockCount - Anzahl aller gefundenen Smartlock&lt;br /&gt;
* bridgeAPI - API Version der Bridge&lt;br /&gt;
* bridgeType - Hardware oder Software/App Bridge&lt;br /&gt;
* currentTime - aktuelle Zeit auf der Bridge zum zeitpunkt des Info holens&lt;br /&gt;
* firmwareVersion - aktuell auf der Bridge verwendete Firmwareversion&lt;br /&gt;
* hardwareId - ID der Hardware Bridge&lt;br /&gt;
* lastError - gibt die letzte HTTP Errormeldung wieder&lt;br /&gt;
* serverConnected - true/false gibt an ob die Hardwarebridge Verbindung zur Nuki-Cloude hat.&lt;br /&gt;
* serverId - gibt die ID des Cloudeservers wieder&lt;br /&gt;
* uptime - Uptime der Bridge in Sekunden&lt;br /&gt;
* wifiFirmwareVersion- Firmwareversion des Wifi Modules der Bridge&lt;br /&gt;
* Die vorangestellte Zahl ist fortlaufend und gibt beginnend bei 0 die Eigenschaften eines Smartlocks wieder.&lt;br /&gt;
&lt;br /&gt;
===Set===&lt;br /&gt;
* &amp;lt;s&amp;gt;autocreate - veranlasst ein erneutes Einlesen aller Smartlocks von der Bridge und falls noch nicht in FHEM vorhanden das automatische Anlegen.&amp;lt;/s&amp;gt;&lt;br /&gt;
* callbackRemove - löschen einer Callback Instanz auf der Bridge. Die Instanz ID kann mittels get callbackList ermittelt werden&lt;br /&gt;
* clearLog - löscht das Logfile auf der Bridge&lt;br /&gt;
* fwUpdate - schaut nach einer neueren Firmware und installiert diese, sofern vorhanden&lt;br /&gt;
* info - holt aktuelle Informationen über die Bridge&lt;br /&gt;
* reboot - veranlasst einen reboot der Bridge&lt;br /&gt;
&lt;br /&gt;
===Get===&lt;br /&gt;
* callbackList - gibt die Liste der eingetragenen Callback URLs wieder. Die Bridge nimmt maximal drei auf&lt;br /&gt;
* logFile - zeigt das Logfile der Bridge an&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
* disable - deaktiviert die Nuki Bridge&lt;br /&gt;
&lt;br /&gt;
==NUKIDevice==&lt;br /&gt;
NUKIDevice - Steuert das Nuki Smartlock&lt;br /&gt;
&lt;br /&gt;
Das Nuki Modul verbindet FHEM über die Nuki Bridge mit einem Nuki Smartlock. Es ist dann möglich, das Schloss zu ver- und entriegeln. In der Regel werden die Nuki Devices automatisch durch das Bridgemodul angelegt. &lt;br /&gt;
&lt;br /&gt;
===Definition===&lt;br /&gt;
:&amp;lt;code&amp;gt;define &amp;lt;name&amp;gt; NUKIDevice &amp;lt;Nuki-Id&amp;gt; &amp;lt;IODev-Device&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Beispiel:&#039;&#039;&#039;&lt;br /&gt;
:&amp;lt;code&amp;gt;define Haustuer NUKIDevice 1 NBridge1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Anweisung erstellt ein NUKIDevice mit Namen Haustuer, der NukiId 1 sowie dem IODev Device NBridge1. Nach dem Anlegen des Devices wird automatisch der aktuelle Zustand des Smartlocks aus der Bridge gelesen.&lt;br /&gt;
Damit das NUKIDevice auch Statusänderungen mitbekommt, die beispielsweise aus der nativen NUKI-App oder der manuellen Betätigung des Devices herrühren, ist es notwendig, die Attribute webhookFWinstance und webhookHttpHostname zu setzen.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;attr Haustuer webhookFWinstance WEB (Name der FHEMWEB Instanz)&amp;lt;/code&amp;gt;&lt;br /&gt;
:attr Haustuer webhookHttpHostname 192.168.0.1 (IP/FQDN vom FHEM Server)&lt;br /&gt;
&lt;br /&gt;
Zum Überprüfen kann die Funktion&lt;br /&gt;
:&amp;lt;code&amp;gt;get NUKIBridge callbackList&amp;lt;/code&amp;gt;&lt;br /&gt;
aufgerufen werden. Es sollte nur ein Callback eingetragen&lt;br /&gt;
sein, der in dieser Form hinterlegt sein sollte.&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;0 http://192.168.0.1:8083/fhem/NUKIDevice-123456789&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Readings===&lt;br /&gt;
* state - Status des Smartlock bzw. Fehlermeldung von Fehler vorhanden.&lt;br /&gt;
* lockState - aktueller Schließstatus: uncalibrated, locked, unlocked, unlocked (lock ‘n’ go), unlatched, locking, unlocking, unlatching, motor blocked, undefined&lt;br /&gt;
* succes - true, false; gibt des Status des letzten Schließbefehles wieder. Geklappt oder nicht geklappt.&lt;br /&gt;
* batteryCritical - ist die Batterie in einem kritischen Zustand? true, false&lt;br /&gt;
* battery - Status der Batterie, ok, low&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Set===&lt;br /&gt;
* statusRequest - ruft den aktuellen Status des Smartlocks von der Bridge ab&lt;br /&gt;
* lock - verschließen&lt;br /&gt;
* unlock - aufschließen&lt;br /&gt;
* unlatch - entriegeln/Falle öffnen&lt;br /&gt;
* locknGo - verschließen wenn gegangen&lt;br /&gt;
* locknGoWithUnlatch - verschließen nachdem die Falle geöffnet wurde&lt;br /&gt;
&lt;br /&gt;
===Attribute===&lt;br /&gt;
* disable - deaktiviert das Nuki Device&lt;br /&gt;
* webhookFWinstance - zu verwendene Webinstanz für den Callbackaufruf&lt;br /&gt;
* webhookHttpHostname - IP oder FQDN vom FHEM Server für den Callbackaufruf&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [https://nuki.io/de/ Webseite des Herstellers]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Türschlosssteuerung]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Codemirror&amp;diff=33890</id>
		<title>Codemirror</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Codemirror&amp;diff=33890"/>
		<updated>2020-09-07T10:37:52Z</updated>

		<summary type="html">&lt;p&gt;Stephan: Warnung bei STRG-Q hinzugefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Randnotiz|RNTyp=Fehl|RNText=Die Befehlsvervollständigungsfunktion funktioniert Stand 05/2019 nur, wenn der [[CsrfToken-HowTo|csrfToken]] in der entsprechenden FHEMWEB-Instanz abgeschaltet ist (&amp;lt;code&amp;gt;attr &amp;lt;FHEMWEB&amp;gt; csrfToken none&amp;lt;/code&amp;gt;). Aus Sicherheitsgründen empfiehlt sich den csrfToken nur zur Bearbeitung zu deaktivieren und/oder andere geeignete Sicherheitsmaßnahmen zu ergreifen ( -&amp;gt; Aufmerksamkeit des Anwenders! ).}}&lt;br /&gt;
&lt;br /&gt;
{{SEITENTITEL:codemirror}}&lt;br /&gt;
[[Bild:ConfEditDEFAutocomplete.png|mini|300px|rechts|Automatische Vervollständigung / Befehlsauswahlfenster (&amp;quot;se&amp;quot; getippt, dann Strg+Leertaste gedrückt)]]&lt;br /&gt;
Bei der Bearbeitung der [[Konfiguration]] über das [[FHEMWEB]]-Frontend bietet FHEM optional Anwenderunterstützung durch Syntaxhervorhebungs-, Befehlsauswahl- und Befehlsvervollständigungsfunktionen. Hierzu ist der Editor [https://codemirror.net codemirror] in FHEM eingebunden.&lt;br /&gt;
&lt;br /&gt;
== Aktivierung in FHEMWEB ==&lt;br /&gt;
Um die Funktionalität in FHEM zu aktivieren, muss folgendes Attribut auf die entsprechende FHEMWEB-Instanz gesetzt werden:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr &amp;lt;FHEMWEB-Devicename&amp;gt; JavaScripts codemirror/fhem_codemirror.js&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für alle FHEMWEB-Instanzen der aktuellen Konfiguration wird die Funktionalität folgendermaßen aktiviert.&lt;br /&gt;
:&amp;lt;code&amp;gt;attr TYPE=FHEMWEB JavaScripts codemirror/fhem_codemirror.js&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wichtige Tastenkombinationen ==&lt;br /&gt;
Im erweiterten Editor ist Suchen und Ersetzen mit folgenden Tastenkombinationen möglich:&lt;br /&gt;
* &#039;STRG + F&#039; =&amp;gt; Suche starten&lt;br /&gt;
* &#039;STRG + G&#039; =&amp;gt; Zum nächsten Treffer&lt;br /&gt;
* &#039;SHIFT + Ctrl + G&#039; =&amp;gt; Zum vorherigen Treffer&lt;br /&gt;
* &#039;SHIFT + Ctrl + F&#039; =&amp;gt; Einzeln Ersetzen&lt;br /&gt;
* &#039;SHIFT + Ctrl + R&#039; =&amp;gt; Im gesamten Dokument Ersetzen&lt;br /&gt;
&lt;br /&gt;
Folgende hilfreiche Tastenkombinationen werden desweiteren automatisch erstellt:&lt;br /&gt;
* &#039;TAB&#039; =&amp;gt; Einrücken von Text, auch mehrerer markierter Zeilen auf einmal, oder mindestens einer komplett markierten Zeile. &lt;br /&gt;
* &#039;SHIFT + TAB&#039; =&amp;gt; Verringern der Einrückung der aktuellen Zeile, oder auch mehrerer markierter Zeilen auf einmal.&lt;br /&gt;
* &#039;STRG + Q&#039; =&amp;gt; Auskommentieren oder Kommentar der Zeile entfernen in welcher sich der Cursor gerade befindet, oder mehrerer markierter Zeilen auf einmal.  &#039;&#039;&#039;ACHTUNG: Beendet ggf. auch ohne Nachfrage den Browser (Firefox), wodurch sämtliche Bearbeitungen unwiderruflich verloren sind!&#039;&#039;&#039; &lt;br /&gt;
* &#039;STRG + UP&#039; oder &#039;STRG + DOWN&#039; =&amp;gt; Hoch-/Runterscrollen des Textes ohne den Cursor umzusetzen.&lt;br /&gt;
* &#039;ALT + G&#039; =&amp;gt; Jump-To-Line, falls das Modul jump-to-line.js über den entsprechenden codemirrorParam geladen wurde.&lt;br /&gt;
&lt;br /&gt;
== Anpassung ==&lt;br /&gt;
Der Editor kann mit dem FHEMWEB-Attribut &#039;&#039;codemirrorParam&#039;&#039; und den Parametern, welche auf der [https://codemirror.net/doc/manual.html#config codemirror Seite] zu finden sind, noch weiter angepasst werden, z.B.:  &lt;br /&gt;
:&amp;lt;code&amp;gt;attr TYPE=FHEMWEB codemirrorParam { &amp;quot;indentWithTabs&amp;quot;:false, &amp;quot;indentUnit&amp;quot;:2, &amp;quot;autocomplete&amp;quot;:false, &amp;quot;height&amp;quot;:&amp;quot;auto&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
In diesem Beispiel würde der Text statt mit Tabulatoren mit Leerzeichen und statt der standardmäßigen vier Leerzeichen, nur noch mit zwei Leerzeichen bei Verwenden von &#039;TAB&#039; oder &#039;SHIFT+TAB&#039; eingerückt werden, und Autovervollständigung mit STRG+SPACE wird deaktiviert (die Dateien show-hint.js und show-hint.css werden nicht mehr geladen); darüber hinaus wird die Höhe des Editors automatisch dem Inhalt angepasst.&lt;br /&gt;
&lt;br /&gt;
Folgende codemirror-Parameter werden standardmäßig gesetzt und können unter anderen mit dem Attribut &#039;&#039;codemirrorParam&#039;&#039; überschrieben werden:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Standardwert !! Wert !! Bemerkungen&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;matchBrackets&amp;quot;&lt;br /&gt;
| true&lt;br /&gt;
| boolean&lt;br /&gt;
| Lädt das Modul &#039;&#039;matchbrackets.js&#039;&#039; und aktiviert die Markierung zusammengehörender Klammern.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;autoRefresh&amp;quot;&lt;br /&gt;
| true&lt;br /&gt;
| boolean&lt;br /&gt;
| Lädt das Modul &#039;&#039;autorefresh.js&#039;&#039;, durch welches unter anderem verhindert wird, dass beim zweiten Aufruf von Codemirror ein schwarzer Editor ohne Inhalt angezeigt wird.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;search&amp;quot;&lt;br /&gt;
| true&lt;br /&gt;
| boolean&lt;br /&gt;
| Lädt die Module &#039;&#039;search.js, searchcursor.js, dialog.js&#039;&#039;, sowie den stylesheet &#039;&#039;dialog.css&#039;&#039; und aktiviert die Suchen &amp;amp; Ersetzen Funktionalität.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;comment&amp;quot;&lt;br /&gt;
| true&lt;br /&gt;
| boolean&lt;br /&gt;
| Lädt das Modul &#039;&#039;comment.js&#039;&#039; und aktiviert die Funktionalität mit STRG+Q Zeilen auszukommentieren / Kommentare zu entfernen.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;autocomplete&amp;quot;&lt;br /&gt;
| true&lt;br /&gt;
| boolean&lt;br /&gt;
| Lädt das Modul &#039;&#039;show-hint.js&#039;&#039; sowie den stylesheet &#039;&#039;show-hint.css&#039;&#039; und aktiviert die Funktionalität, mit STRG+SPACE die Autovervollständigung zu verwenden.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;autocompleteAlways&amp;quot;&lt;br /&gt;
| false&lt;br /&gt;
| boolean&lt;br /&gt;
| Wenn zusätzlich zu &amp;quot;autocomplete&amp;quot; aktiviert, wird die Befehlsvervollständigung bei jedem Tastendruck durchgeführt (nicht nur durch STRG+SPACE).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;autoCloseBrackets&amp;quot;&lt;br /&gt;
| true&lt;br /&gt;
| boolean&lt;br /&gt;
| Lädt das Modul &#039;&#039;closebrackets.js&#039;&#039; und aktiviert die Funktionalität, dass ()[]{}&#039;&#039;&amp;quot;&amp;quot; automatisch geschlossen werden.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;jumpToLine&amp;quot;&lt;br /&gt;
| false&lt;br /&gt;
| boolean&lt;br /&gt;
| Lädt das Modul &#039;&#039;jump-to-line.js&#039;&#039; und aktiviert die Funktionalität, dass mit ALT+G zu bestimmten Zeilen gesprungen werden kann.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;jumpToLine_extraKey&amp;quot;&lt;br /&gt;
| false&lt;br /&gt;
| z.B. &amp;quot;ALT-H&amp;quot;&lt;br /&gt;
| Hierdurch lässt sich die Standardtastenbelegung ALT+G für jumpToLine anpassen.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;keyMap&amp;quot;&lt;br /&gt;
| false&lt;br /&gt;
| &amp;quot;vim&amp;quot;&lt;br /&gt;
| Hierdurch wird wie in dem Beispiel die Datei fhem/www/codemirror/vim.js oder eine andere in diesem Ordner befindliche *.js Datei geladen.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;indentUnit&amp;quot;&lt;br /&gt;
| 4&lt;br /&gt;
| integer&lt;br /&gt;
| Gibt an, wieviele Leerzeichen ein TAB-Block umfasst.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;type&amp;quot;&lt;br /&gt;
| &amp;quot;fhem&amp;quot;&lt;br /&gt;
| string&lt;br /&gt;
| Der Standard codemirror-mode, default wird hier die &#039;&#039;fhem.js&#039;&#039; geladen. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;theme&amp;quot;&lt;br /&gt;
| &amp;quot;blackboard&amp;quot;&lt;br /&gt;
| string&lt;br /&gt;
| Das Standard codemirrer-theme, default wird hier die &#039;&#039;blackboard.css&#039;&#039; geladen.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;indentWithTabs&amp;quot;&lt;br /&gt;
| true&lt;br /&gt;
| boolean&lt;br /&gt;
| Wenn aktiviert, wird mit Tabulator statt Leerzeichen eingerückt.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;autofocus&amp;quot;&lt;br /&gt;
| true&lt;br /&gt;
| boolean&lt;br /&gt;
| Wenn aktiviert, wird der Fokus beim laden von codemirror automatisch in das Editor-Fenster gesetzt.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;lineNumbers&amp;quot;&lt;br /&gt;
| true&lt;br /&gt;
| boolean&lt;br /&gt;
| Wenn aktiviert, werden die Zeilennummern im Editor angezeigt.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;smartIndent&amp;quot;&lt;br /&gt;
| false&lt;br /&gt;
| boolean&lt;br /&gt;
| Kontext-sensitive Einrückung (&#039;&#039;true&#039;&#039;) oder &amp;quot;gleiche Einrückung wie vorherige Zeile&amp;quot; (&#039;&#039;false&#039;&#039;).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;height&amp;quot;&lt;br /&gt;
| false&lt;br /&gt;
| integer / &amp;quot;auto&amp;quot;&lt;br /&gt;
| Entweder die standard, automatische, oder benutzerdefinierte Höhe des Editorfensters verwenden. (Im DEF sowie bei Attributen)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
Der Forums-Thread unter der Überschrift {{Link2Forum|Topic=20444|LinkText=Javascript Editor einbinden}} enthält die Diskussion zum Entwicklungsprozess.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Glossary]]&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Event-aggregator&amp;diff=32841</id>
		<title>Event-aggregator</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Event-aggregator&amp;diff=32841"/>
		<updated>2020-02-24T12:13:43Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Duplizieren von Readings */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{SEITENTITEL:event-aggregator}}  &amp;lt;!-- da richtige Schreibweise kleinen Anfangsbuchstaben hat --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Infobox Attribut sinnvoll? --&amp;gt;&lt;br /&gt;
{{Baustelle}}&lt;br /&gt;
Mit dem Attribut [[event-aggregator]] können (nach Wunsch zeitlich gewichtete) Durchschnittswerte, Minima, Maxima oder Median etc. berechnet werden. Der Median kann hilfreich sein, um Messwerte mit Ausreissern (unsinnige Werte, z.B. durch Übertragungsfehler) zu glätten.&lt;br /&gt;
&lt;br /&gt;
== Syntax ==&lt;br /&gt;
Das &#039;&#039;event-aggregator&#039;&#039; Attribut wird in der folgenden Weise spezifiziert:&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;attr &amp;lt;device&amp;gt; event-aggregator reading:interval:method:function:holdTime&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Readings werden als kommagetrennte Liste angegeben.&lt;br /&gt;
&lt;br /&gt;
Die einzelnen Teile haben folgende Bedeutung:&lt;br /&gt;
=== reading ===&lt;br /&gt;
Das zu aggregierende Reading des aktuellen Gerätes. Das Reading selbst muss seine Werte aus einer Aktion oder einem Event in FHEM erhalten (beispielsweise, indem Temperaturwerte ausgegeben oder andere Größen in das Reading geschrieben werden). Indem dann der event-aggregator auf dieses Reading angewandt wird, werden die erhaltenen Größen bearbeitet - je nach Installation wird dann also das Minimum, das Maximum oder der Median der Werte beim Reading ausgegeben.&lt;br /&gt;
&lt;br /&gt;
Wichtig: Es kann immer nur einen event-aggregator pro Reading geben. Will man daher mehrere Werte (z.B. min, max, avg), muss man weitere Readings erzeugen (z.B. mit userReadings oder notify). Der Aggregator kann als regulärer Ausdruck angegeben werden (bsp. .*_rain.*)&lt;br /&gt;
&lt;br /&gt;
=== interval === &lt;br /&gt;
&lt;br /&gt;
Updates des &amp;lt;readings&amp;gt; werden ignoriert, Events werden für mindestens &amp;lt;interval&amp;gt; Sekunden unterdrückt.&lt;br /&gt;
&lt;br /&gt;
Nach der interval-periode wird das reading mit einem Wert upgedated, der sich aus den Werten und Zeitstempeln der vorher ignorierten Updates zusammensetzt.&lt;br /&gt;
&lt;br /&gt;
=== method === &lt;br /&gt;
&lt;br /&gt;
betrifft die Gewichtung nach Zeitintervallen&lt;br /&gt;
* &amp;lt;code&amp;gt;none&amp;lt;/code&amp;gt;: keine zeitliche Gewichtung&lt;br /&gt;
* &amp;lt;code&amp;gt;const&amp;lt;/code&amp;gt;: Annahme, dass zwischen den zwei Messpunkten keine Veränderung stattgefunden hat&lt;br /&gt;
* &amp;lt;code&amp;gt;linear&amp;lt;/code&amp;gt;: Annahme, dass der Wert sich zwischen zwei Messpunkten linear verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== function === &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; Anzahl&lt;br /&gt;
* &amp;lt;code&amp;gt;min&amp;lt;/code&amp;gt; Minimum&lt;br /&gt;
* &amp;lt;code&amp;gt;max&amp;lt;/code&amp;gt; Maximum&lt;br /&gt;
* &amp;lt;code&amp;gt;mean&amp;lt;/code&amp;gt; artihmetischer Mittelwert&lt;br /&gt;
* &amp;lt;code&amp;gt;sd&amp;lt;/code&amp;gt; Standardabweichung&lt;br /&gt;
* &amp;lt;code&amp;gt;integral&amp;lt;/code&amp;gt; Summe (falls holdTime nicht angegeben) oder Integral für den Zeitraum holdTime&lt;br /&gt;
* &amp;lt;code&amp;gt;median&amp;lt;/code&amp;gt; [https://de.wikipedia.org/wiki/Median Median] (nur für method &amp;lt;code&amp;gt;none&amp;lt;/code&amp;gt; und gesetzte holdTime) - im Gegensatz zum Mittelwert nicht anfällig für Ausreisser, hilfreich bei Sensoren mit sporadisch unsinnigen Messwerten&lt;br /&gt;
&lt;br /&gt;
=== holdTime === &lt;br /&gt;
&lt;br /&gt;
Zeitfenster in Sekunden, für die die vergangenen Werte gehalten werden, um die Aggregatfunktion zu berechnen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Duplizieren von Readings==&lt;br /&gt;
Wenn mehrere Funktionen für ein Reading berechnet werden sollen, muss dieses Reading zuvor dupliziert werden.&lt;br /&gt;
In der Commandref wird ein notify vorgeschlagen, evtl. können aber auch DOIF_Readings, event_Readings und userReadings verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
original_reading:interval:method:function:holdTime&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== DOIF_Readings ===&lt;br /&gt;
TODO!&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
original_reading:interval:method:function:holdTime&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== event_Readings ===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
attr DOIF-Device event_Readings &lt;br /&gt;
original_reading:[&amp;lt;Device&amp;gt;:&amp;lt;Reading&amp;gt;], &amp;lt;br /&amp;gt;&lt;br /&gt;
copy_1:[$SELF:original_reading], &amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;s&amp;gt;copy_2:int(10*[$SELF:copy_1])/10&amp;lt;/s&amp;gt; Funktioniert so nicht!!! &amp;lt;br /&amp;gt;&lt;br /&gt;
attr DOIF-Device event-aggregator copy_1:interval:method:function:holdTime&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== userReadings ===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
attr DOIF-Device userReadings &lt;br /&gt;
original_reading {ReadingsVal(&amp;quot;&amp;lt;Device&amp;gt;&amp;quot;,&amp;quot;&amp;lt;Reading&amp;gt;&amp;quot;,0)}, &amp;lt;br /&amp;gt;&lt;br /&gt;
copy_1 {ReadingsVal($name,&amp;quot;original_reading&amp;quot;,0)}, &amp;lt;br /&amp;gt;&lt;br /&gt;
copy_2 {int(10*ReadingsVal($name,&amp;quot;copy_1&amp;quot;,0))/10}&amp;lt;br /&amp;gt;&lt;br /&gt;
attr DOIF-Device event-aggregator copy_1:interval:method:function:holdTime&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Wechselwirkungen == &lt;br /&gt;
- keine bekannt - &lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
; aus der {{Link2CmdRef|Anker=Event-aggregator}}&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
attr myPowerMeter event-aggregator EP_POWER_METER:300:linear:mean,EP_ENERGY_METER:300:none:v&lt;br /&gt;
&lt;br /&gt;
attr myBadSensor event-aggregator TEMP::none:median:300&lt;br /&gt;
&lt;br /&gt;
attr mySunMeter event-aggregator SUN_INTENSITY_24H::const:integral:86400&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
*[[event-on-update-reading]]&lt;br /&gt;
*[[event-min-interval]]&lt;br /&gt;
*[[event-aggregator]]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Benutzungstipps (&#039;&#039;Best Practice&#039;&#039;) für das Attribut in {{Link2Forum|Topic=36522|LinkText=diesem Forenthread}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Attribut (allgemeingültig)]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Event-aggregator&amp;diff=32840</id>
		<title>Event-aggregator</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Event-aggregator&amp;diff=32840"/>
		<updated>2020-02-24T12:13:21Z</updated>

		<summary type="html">&lt;p&gt;Stephan: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{SEITENTITEL:event-aggregator}}  &amp;lt;!-- da richtige Schreibweise kleinen Anfangsbuchstaben hat --&amp;gt;&lt;br /&gt;
&amp;lt;!-- Infobox Attribut sinnvoll? --&amp;gt;&lt;br /&gt;
{{Baustelle}}&lt;br /&gt;
Mit dem Attribut [[event-aggregator]] können (nach Wunsch zeitlich gewichtete) Durchschnittswerte, Minima, Maxima oder Median etc. berechnet werden. Der Median kann hilfreich sein, um Messwerte mit Ausreissern (unsinnige Werte, z.B. durch Übertragungsfehler) zu glätten.&lt;br /&gt;
&lt;br /&gt;
== Syntax ==&lt;br /&gt;
Das &#039;&#039;event-aggregator&#039;&#039; Attribut wird in der folgenden Weise spezifiziert:&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;attr &amp;lt;device&amp;gt; event-aggregator reading:interval:method:function:holdTime&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mehrere Readings werden als kommagetrennte Liste angegeben.&lt;br /&gt;
&lt;br /&gt;
Die einzelnen Teile haben folgende Bedeutung:&lt;br /&gt;
=== reading ===&lt;br /&gt;
Das zu aggregierende Reading des aktuellen Gerätes. Das Reading selbst muss seine Werte aus einer Aktion oder einem Event in FHEM erhalten (beispielsweise, indem Temperaturwerte ausgegeben oder andere Größen in das Reading geschrieben werden). Indem dann der event-aggregator auf dieses Reading angewandt wird, werden die erhaltenen Größen bearbeitet - je nach Installation wird dann also das Minimum, das Maximum oder der Median der Werte beim Reading ausgegeben.&lt;br /&gt;
&lt;br /&gt;
Wichtig: Es kann immer nur einen event-aggregator pro Reading geben. Will man daher mehrere Werte (z.B. min, max, avg), muss man weitere Readings erzeugen (z.B. mit userReadings oder notify). Der Aggregator kann als regulärer Ausdruck angegeben werden (bsp. .*_rain.*)&lt;br /&gt;
&lt;br /&gt;
=== interval === &lt;br /&gt;
&lt;br /&gt;
Updates des &amp;lt;readings&amp;gt; werden ignoriert, Events werden für mindestens &amp;lt;interval&amp;gt; Sekunden unterdrückt.&lt;br /&gt;
&lt;br /&gt;
Nach der interval-periode wird das reading mit einem Wert upgedated, der sich aus den Werten und Zeitstempeln der vorher ignorierten Updates zusammensetzt.&lt;br /&gt;
&lt;br /&gt;
=== method === &lt;br /&gt;
&lt;br /&gt;
betrifft die Gewichtung nach Zeitintervallen&lt;br /&gt;
* &amp;lt;code&amp;gt;none&amp;lt;/code&amp;gt;: keine zeitliche Gewichtung&lt;br /&gt;
* &amp;lt;code&amp;gt;const&amp;lt;/code&amp;gt;: Annahme, dass zwischen den zwei Messpunkten keine Veränderung stattgefunden hat&lt;br /&gt;
* &amp;lt;code&amp;gt;linear&amp;lt;/code&amp;gt;: Annahme, dass der Wert sich zwischen zwei Messpunkten linear verändert hat.&lt;br /&gt;
&lt;br /&gt;
=== function === &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt; Anzahl&lt;br /&gt;
* &amp;lt;code&amp;gt;min&amp;lt;/code&amp;gt; Minimum&lt;br /&gt;
* &amp;lt;code&amp;gt;max&amp;lt;/code&amp;gt; Maximum&lt;br /&gt;
* &amp;lt;code&amp;gt;mean&amp;lt;/code&amp;gt; artihmetischer Mittelwert&lt;br /&gt;
* &amp;lt;code&amp;gt;sd&amp;lt;/code&amp;gt; Standardabweichung&lt;br /&gt;
* &amp;lt;code&amp;gt;integral&amp;lt;/code&amp;gt; Summe (falls holdTime nicht angegeben) oder Integral für den Zeitraum holdTime&lt;br /&gt;
* &amp;lt;code&amp;gt;median&amp;lt;/code&amp;gt; [https://de.wikipedia.org/wiki/Median Median] (nur für method &amp;lt;code&amp;gt;none&amp;lt;/code&amp;gt; und gesetzte holdTime) - im Gegensatz zum Mittelwert nicht anfällig für Ausreisser, hilfreich bei Sensoren mit sporadisch unsinnigen Messwerten&lt;br /&gt;
&lt;br /&gt;
=== holdTime === &lt;br /&gt;
&lt;br /&gt;
Zeitfenster in Sekunden, für die die vergangenen Werte gehalten werden, um die Aggregatfunktion zu berechnen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Duplizieren von Readings==&lt;br /&gt;
Wenn mehrere Funktionen für ein Reading berechnet werden sollen, muss dieses Reading zuvor dupliziert werden.&lt;br /&gt;
In der Commandref wird ein notify vorgeschlagen, evtl. können aber auch DOIF_Readings, event_Readings und userReadings verwendet werden:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
original_reading:interval:method:function:holdTime&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== DOIF_Readings ===&lt;br /&gt;
TODO!&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
original_reading:interval:method:function:holdTime&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== event_Readings ===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
attr DOIF-Device event_Readings &lt;br /&gt;
original_reading:[&amp;lt;Device&amp;gt;:&amp;lt;Reading&amp;gt;], &amp;lt;br /&amp;gt;&lt;br /&gt;
copy_1:[$SELF:original_reading], &amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;s&amp;gt;copy_2:int(10*[$SELF:copy_1])/10&amp;lt;/s&amp;gt; Funktioniert so nicht!!! &amp;lt;br /&amp;gt;&lt;br /&gt;
attr DOIF-Device event-aggregator copy_1:interval:method:function:holdTime&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
=== userReadings ===&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
attr DOIF-Device userReadings &lt;br /&gt;
original_reading {ReadingsVal(&amp;quot;&amp;lt;Device&amp;gt;&amp;quot;,&amp;quot;&amp;lt;Reading&amp;gt;&amp;quot;,0)}, &amp;lt;br /&amp;gt;&lt;br /&gt;
copy_1 {ReadingsVal($name,&amp;quot;original_reading&amp;quot;,0)}, &amp;lt;br /&amp;gt;&lt;br /&gt;
copy_2 {int(10*ReadingsVal($name,&amp;quot;copy_1&amp;quot;,0))/10}&amp;lt;br /&amp;gt;&lt;br /&gt;
attr DOIF-Device event-aggregator copy_1:interval:method:function:holdTime&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
== Wechselwirkungen == &lt;br /&gt;
- keine bekannt - &lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
; aus der {{Link2CmdRef|Anker=Event-aggregator}}&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
attr myPowerMeter event-aggregator EP_POWER_METER:300:linear:mean,EP_ENERGY_METER:300:none:v&lt;br /&gt;
&lt;br /&gt;
attr myBadSensor event-aggregator TEMP::none:median:300&lt;br /&gt;
&lt;br /&gt;
attr mySunMeter event-aggregator SUN_INTENSITY_24H::const:integral:86400&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Siehe auch ==&lt;br /&gt;
*[[event-on-update-reading]]&lt;br /&gt;
*[[event-min-interval]]&lt;br /&gt;
*[[event-aggregator]]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Benutzungstipps (&#039;&#039;Best Practice&#039;&#039;) für das Attribut in {{Link2Forum|Topic=36522|LinkText=diesem Forenthread}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Attribut (allgemeingültig)]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEM_User_Interface_Painter&amp;diff=31643</id>
		<title>FHEM User Interface Painter</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEM_User_Interface_Painter&amp;diff=31643"/>
		<updated>2019-11-08T11:32:41Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:FUIP_Symbolbild.png|mini|400px|FUIP in Aktion]]&lt;br /&gt;
Der &#039;&#039;&#039;FHEM User Interface Painter (FUIP)&#039;&#039;&#039; ist ein FHEM-Modul, mit dem man [[FHEM Tablet UI]] (FTUI) Seiten graphisch (&amp;quot;klickibunti&amp;quot;) erstellen kann. D.h. man kann sich ein UI zusammenklicken, welches die Tablet UI Technik verwendet, aber ohne sich selbst unbedingt mit HTML und CSS herumschlagen zu müssen.&lt;br /&gt;
Hier ist ein kurzes Video, welches FUIP in Aktion zeigt:&lt;br /&gt;
&lt;br /&gt;
[https://forum.fhem.de/index.php?action=dlattach;topic=85869.0;attach=98455 Einführungsvideo]&lt;br /&gt;
&lt;br /&gt;
(Das Video zeigt nicht unbedingt den neusten Stand, aber es dürfte klar werden, wofür FUIP gut sein soll.)&lt;br /&gt;
&lt;br /&gt;
== Installation und Upgrade in FHEM ==&lt;br /&gt;
Genau wie FTUI ist auch FUIP nicht automatisch Teil von FHEM. Es muss explizit installiert bzw. hinzugefügt werden. Die Installation funktioniert folgendermaßen:&lt;br /&gt;
* FUIP verwendet FTUI. D.h. man sollte zuerst einmal FTUI installieren, falls noch nicht passiert. Wie das geht steht bei [[FHEM Tablet UI#Installation|FHEM Tablet UI]]. Dabei reicht der erste Schritt aus, da lediglich die FTUI-Dateien von FUIP benötigt werden.&lt;br /&gt;
* Dann das FUIP-Repository zum FHEM Update hinzufügen: &lt;br /&gt;
** &amp;lt;code&amp;gt;update add https://raw.githubusercontent.com/ThorstenPferdekaemper/FHEM-FUIP/master/controls_fuip.txt&amp;lt;/code&amp;gt;&lt;br /&gt;
* Jetzt &amp;lt;code&amp;gt;update all fuip&amp;lt;/code&amp;gt; machen.&lt;br /&gt;
* Warten, bis man ganz sicher ist, dass alles installiert ist. Das sieht man normalerweise an der Meldung &amp;lt;code&amp;gt;update finished, &amp;quot;shutdown restart&amp;quot; is needed to activate the changes.&amp;lt;/code&amp;gt;. Das ganze kann etwas länger dauern, da FUIP aus einige Dateien besteht, von denen ein paar etwas größer sind. &lt;br /&gt;
* Jetzt erst &amp;lt;code&amp;gt;shutdown restart&amp;lt;/code&amp;gt;. &lt;br /&gt;
* FUIP sollte jetzt installiert sein.&lt;br /&gt;
&lt;br /&gt;
Zum Upgrade reicht ein &amp;lt;code&amp;gt;update&amp;lt;/code&amp;gt;, wenn man FUIP wie oben beschrieben installiert hat. Will man nur den FUIP-Teil aktualisieren und nicht alles in FHEM, dann geht das mit &amp;lt;code&amp;gt;update all fuip&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Falls es bei oder nach der Installation oder dem Upgrade Probleme gibt (welcher Art auch immer), sollten die folgenden Punkte geprüft werden:&lt;br /&gt;
* Wird das richtige Repository benutzt? Man kann das mit &amp;lt;code&amp;gt;update list&amp;lt;/code&amp;gt; überprüfen. Wenn der Eintrag für fuip nicht genau so aussieht wie oben beschrieben, dann sollte er per &amp;lt;code&amp;gt;update delete&amp;lt;/code&amp;gt; gelöscht werden und dann genau so eingetragen werden wie oben beschrieben. &lt;br /&gt;
* Ist der update-Prozess wirklich komplett durchgelaufen? Auch wenn es etwas länger dauert, muss man auf die Meldung &amp;lt;code&amp;gt;update finished, &amp;quot;shutdown restart&amp;quot; is needed to activate the changes.&amp;lt;/code&amp;gt; warten, bevor man durchstartet. Im Zweifelsfall einfach noch einmal &amp;lt;code&amp;gt;update all fuip&amp;lt;/code&amp;gt; machen und diesmal mehr Geduld haben.&lt;br /&gt;
* FHEM braucht Schreibrechte im Verzeichnis &amp;lt;fhem&amp;gt;/FHEM/lib/FUIP/config (&amp;lt;fhem&amp;gt; steht meistens für /opt/fhem) und allen enthaltenen Dateien. Auf Betriebssystemebene (Linux) kann man das mit &amp;lt;code&amp;gt;ls -ls&amp;lt;/code&amp;gt; überprüfen. Meistens kann es mit folgendem Befehl repariert werden: &amp;lt;code&amp;gt;chown -R fhem:dialout /opt/fhem/FHEM/lib/FUIP/config&amp;lt;/code&amp;gt;. (Dieser Punkt gilt nicht bzw. nicht genau so, wenn man die configDB benutzt.)&lt;br /&gt;
* Steht etwas verdächtiges im Logfile?&lt;br /&gt;
&lt;br /&gt;
== Erste Schritte ==&lt;br /&gt;
Zuerst legt man ein &amp;quot;FUIP Device&amp;quot; an, also etwa &amp;lt;code&amp;gt;define ui FUIP&amp;lt;/code&amp;gt;. Dieses Device meldet sich sozusagen als Webserver bei FHEM an (wie z.B. auch [[HTTPSRV]]), d.h. es erscheint im Menu oben links (über den Räumen) ein Link &amp;quot;ui&amp;quot;. Beim Klick auf diesen Link generiert FUIP eine FTUI Übersichtsseite aus den Räumen im FHEM System. (Das kann ein bisschen dauern, wenn man viele Devices hat.) Nun kann man durch Verschieben und größer/kleiner-Ziehen der einzelnen Zellen bzw. durch Verschieben des Zelleninhalts die Gestaltung ändern. Außerdem kann man über das Zahnrad rechts oben in den Zellen neuen Inhalt hinzufügen, den bestehenden Inhalt ändern und vieles mehr.&lt;br /&gt;
Durch Klicken auf die Räume im Menu der Übersichtsseite werden einzelne Raum-Seiten generiert. Diese kann man ebenfalls entsprechend bearbeiten. Natürlich können auch eigene Seiten hinzugefügt werden. Wie das geht sieht man im Video, welches oben verlinkt ist.&lt;br /&gt;
&lt;br /&gt;
Nachdem man die Seiten entsprechend bearbeitet hat sollte man ein &amp;lt;code&amp;gt;set ui save&amp;lt;/code&amp;gt; machen. Ansonsten gehen die Änderungen beim nächsten FHEM-Neustart verloren. Ein normales &amp;lt;code&amp;gt;save config&amp;lt;/code&amp;gt; reicht nicht aus und ist auch nicht notwendig.&lt;br /&gt;
&lt;br /&gt;
Die folgenden detaillierten Informationen können zum Teil veraltet sein, da FUIP noch weitereintwickelt wird. Es sollten allerdings keine Funktionen verschwinden.&lt;br /&gt;
&lt;br /&gt;
== Das Konfigurations-Popup ==&lt;br /&gt;
=== ...für Zellen ===&lt;br /&gt;
[[Datei:FUIP_KonfigZelle.png|mini|600px]]&lt;br /&gt;
Beim Klick auf das Zahnrad rechts oben in einer Zelle wird der Konfigurations-Dialog für diese Zelle geöffnet. Im Folgenden werden die Funktionen der einzelnen Elemente erklärt.&lt;br /&gt;
;Zur Seiten-Konfiguration: Damit kann man zum Konfig-Dialog für die ganze Seite wechseln. &lt;br /&gt;
;Zellen-Überschrift: Hier wird die Überschrift der Zelle festgelegt. Eine Zelle muss keine Überschrift haben. (Die Zahlen hinter der Überschrift in der Zelle selbst verschwinden, wenn man das Attribut &#039;&#039;locked&#039;&#039; setzt.)&lt;br /&gt;
;Neuer View: Hiermit fügt man einen neuen View, also neuen Inhalt, zu einer Zelle hinzu. Man wählt dann aus, welcher Art View angelegt werden soll und füllt die Details, wie z.B. das zugehörige Device. Der neue View wird an einer freien Stelle (falls möglich) in der Zelle eingefügt. Nach schließen des Konfig-Dialogs kann man den View dann per Drag&amp;amp;Drop geeignet positionieren.&lt;br /&gt;
;Neuer View (Device): Dies dient ebenfalls dazu, neue Views in die Zelle zu packen. Allerdings wählt man nicht die Art des Views aus, sondern die FHEM-Devices, die dargestellt werden sollen. Das System sucht dann jeweils einen geeigneten View aus. Bisher funktioniert das nur sehr eingeschränkt, hat aber den Vorteil, dass man gleich mehrere Devices auswählen kann.&lt;br /&gt;
;Abbrechen: Damit schließt man den Dialog ohne die Änderungen zu übernehmen.&lt;br /&gt;
;Details zum View: Hier klappt man die Details zum View aus und kann diese ändern. Was man damit genau machen kann kommt auf die Art des View an. Per Drag&amp;amp;Drop kann man auch die Reihenfolge der Views im Konfig-Dialog ändern. Das hat allerdings keinen Effekt auf die Positonierung bereits existierender Views in der Zelle. &lt;br /&gt;
;View löschen: Das löscht den View aus der Zelle. Wenn man den View eigentlich nicht löschen, sondern in einer anderen Zelle haben will, dann kann man ihn auch per Drag&amp;amp;Drop in die andere Zelle verschieben. Lässt man einen View in einer anderen Zelle &amp;quot;fallen&amp;quot;, dann wird dieser automatisch der neuen Zelle zugeordnet.&lt;br /&gt;
;Übernehmen: Das schließt den Dialog und übernimmt die Änderungen. Dadurch kann man die Auswirkungen auf dem UI selbst sehen. Um die Änderungen allerdings den nächsten FHEM Neustart überleben zu lassen muss man noch ein &amp;lt;code&amp;gt;set ui save&amp;lt;/code&amp;gt; machen.&lt;br /&gt;
;Auto-Anordnen: Hiermit kann man die einzelnen Views in der Zelle automatisch anordnen. Es kann momentan sein, dass dadurch Änderungen verloren gehen. D.h. dieser Button sollte nur benutzt werden, wenn man seit dem Öffnen des Dialogs keine Änderungen gemacht hat. Außerdem schließt der Button dan Dialog. FUIP ordnet die Views in der Reihenfolge an, in der sie im Konfig-Popup erscheinen. Das zugrunde liegende Layout ist sehr einfach gehalten und kann sich auch noch ändern. Diese Funktion ist vor Allem brauchbar für mehrere gleichartige Views in derselben Zelle, wie z.B. bei Menus. &lt;br /&gt;
;Neue Zelle: Legt eine neue (leere) Zelle auf der aktuellen Seite an. Der Konfig-Dialog wird dabei geschlossen und Änderungen gehen verloren.&lt;br /&gt;
;Zelle kopieren: Kopiert die aktuelle Zelle. Man muss eine Seite angeben, zu der die kopierte Zelle gehören soll. Das entsprechende Feld ist mit der aktuellen Seite vorbelegt. Falls man dies nicht ändert, wird einfach eine Kopie der Zelle auf derselben Seite erzeugt. Ansonsten wird die Zelle auf die angegebene Seite kopiert. Die Funktion schließt den Konfig-Dialog und Änderungen gehen verloren. Falls man eine andere Seite angegeben hat, wird zu dieser gewechselt.&lt;br /&gt;
;Zelle exportieren: Dies erlaubt die Definition der Zelle herunterzuladen und auf dem Client (also dem Rechner, auf dem der Browser läuft) zu speichern.&lt;br /&gt;
;Zelle importieren: Damit lädt man eine exportierte Zelle wieder hoch. FUIP erzeugt dann auf der aktuellen Seite eine neue Zelle mit dem entsprechenden Inhalt. Das funktioniert auch mit Zellen, die von einer anderen FUIP-Seite, einem anderen FUIP-Device oder von einer anderen FHEM-Installation kommen.&lt;br /&gt;
;Zelle löschen: Damit löscht man die aktuelle Zelle. Sie verschwindet dann von der aktuellen Seite.&lt;br /&gt;
;Toggle editOnly: Bei manchen Views ist es schwierig, sie mit der Maus &amp;quot;anzufassen&amp;quot;, da sie sofort eine Aktion auslösen (z.B. bei Links). Mit &amp;quot;Toggle editOnly&amp;quot; wird eine graue &amp;quot;Schicht&amp;quot; über die Views gelegt. Dadurch weiß man besser, wo man den View anfassen kann und Mausklicks haben keine Wirkung mehr, außer Drag&amp;amp;Drop.&lt;br /&gt;
&lt;br /&gt;
=== ...für Seiten ===&lt;br /&gt;
[[Datei:FUIP_SeitenKonfig.png|mini|600px]]&lt;br /&gt;
Drückt man im Konfig-Dialog für Zellen auf &amp;quot;Cell/Page&amp;quot; kommt man auf den Konfig-Dialog für ganze Seiten. Die Bedeutung der Einzelnen Elemente ist wie folgt.&lt;br /&gt;
;Zur Zellen-Konfiguration:Das bringt einen zurück zum Konfig-Dialog für Zellen.&lt;br /&gt;
;Seiten-Überschrift:Hier kann ein Titel für die Seite eingetragen werden. Das erscheint dann je nach Browser irgendwo oben. (Technisch ist es der Inhalt des &amp;lt;title&amp;gt; Tags in &amp;lt;head&amp;gt;.)&lt;br /&gt;
;Abbrechen:Damit schließt man den Dialog ohne die Änderungen zu übernehmen.&lt;br /&gt;
;Übernehmen: Das schließt den Dialog und übernimmt die Änderungen. Dadurch kann man die Auswirkungen auf dem UI selbst sehen. Um die Änderungen allerdings den nächsten FHEM Neustart überleben zu lassen muss man noch ein &amp;lt;code&amp;gt;set ui save&amp;lt;/code&amp;gt; machen.&lt;br /&gt;
;Seite kopieren:Dadurch wird eine neue Seite angelegt, die so aussieht wie die aktuelle. Der Dialog wird dann geschlossen und automatisch zur neuen Seite gesprungen.&lt;br /&gt;
;Seite exportieren:Das ist dasselbe wie für Zellen, nur für die ganze aktuelle Seite.&lt;br /&gt;
;Seite importieren:Das ist im Prinzip auch wie für Zellen, nur dass eine neue Seite angelegt wird. Exportierte Zellen können nur als Zellen importiert werden und exportierte Seiten nur als Seiten.&lt;br /&gt;
&lt;br /&gt;
== Das FUIP-Device ==&lt;br /&gt;
Wie in [[#Erste Schritte|Erste Schritte]] bereits beschrieben, muss man zur Verwendung von FUIP zuerst ein FUIP-Device anlegen. Alles, was man mit FUIP macht (bzw. was das System macht) passiert in Bezug auf ein FUIP-Device. Man kann auch jederzeit mehrere FUIP-Devices anlegen. Verschiedene Instanzen von FUIP stören sich nicht gegenseitig, man kann aber (z.B. in einem Menu) auf eine andere FUIP-Instanz verweisen (verlinken).&lt;br /&gt;
&lt;br /&gt;
Das FUIP-Device hat die folgenden Eigenschaften.&lt;br /&gt;
=== Sets ===&lt;br /&gt;
Die folgenden set-Kommandos sind für den Benutzer gedacht. Es gibt noch weitere set-Kommandos, welche aber vor Allem für FUIP-interne Zwecke gedacht sind. &lt;br /&gt;
;save: Mit &amp;lt;code&amp;gt;set &amp;lt;name&amp;gt; save&amp;lt;/code&amp;gt; wird der momentane Zustand der FUIP-Seiten zu diesem FUIP-Device gespeichert. Das betrifft nicht die Definition des FUIP-Device selbst (also auch nicht dessen Attribute). Diese müssen nach wie vor über &amp;lt;code&amp;gt;save&amp;lt;/code&amp;gt; (also &amp;quot;Save config&amp;quot;) gespeichert werden. Die FUIP-Seiten stehen in der Datei &amp;lt;fhem&amp;gt;/FHEM/lib/FUIP/config/FUIP_&amp;lt;name&amp;gt;.cfg. Dabei steht &amp;lt;fhem&amp;gt; in der Regel für /opt/fhem und &amp;lt;name&amp;gt; für den Namen des FUIP-Devices. Ohne ein &amp;lt;code&amp;gt;set &amp;lt;name&amp;gt; save&amp;lt;/code&amp;gt; gehen alle Änderungen beim nächsten FHEM Restart verloren. &lt;br /&gt;
;load: Mit &amp;lt;code&amp;gt;set &amp;lt;name&amp;gt; load&amp;lt;/code&amp;gt; stellt man den Zustand wieder her, der mit &amp;lt;code&amp;gt;set &amp;lt;name&amp;gt; save&amp;lt;/code&amp;gt; gespeichert wurde. Das ist vor Allem dann nützlich, wenn man sich beim Bearbeiten der Seiten vertan hat. Ansonsten wird beim FHEM Start das &amp;quot;load&amp;quot; automatisch ausgeführt.  &lt;br /&gt;
;pagedelete: Mit &amp;lt;code&amp;gt;set &amp;lt;name&amp;gt; pagedelete &amp;lt;page&amp;gt;&amp;lt;/code&amp;gt; wird die Seite &amp;lt;page&amp;gt; gelöscht. Das kann auch dazu genutzt werden, FUIP zum Neugenerieren einer Seite zu &amp;quot;zwingen&amp;quot;, indem man die Seite nach dem Löschen einfach wieder aufruft. Auch das Löschen einer Seite geht ohne ein &amp;quot;save&amp;quot; verloren, d.h. die Seite ist dann nach einem FHEM Neustart wieder da. &lt;br /&gt;
;refreshBuffer: FUIP verwendet Informationen aus dem &amp;quot;eigentlichen&amp;quot; FHEM, wie z.B. die Liste aller Devices sowie bestimmte Readings, Internals und Attribute. Insbesondere bei &amp;quot;entferntem&amp;quot; FUIP, also bei Verwendung des Attributs &#039;&#039;fhemwebUrl&#039;&#039;, kann die Ermittlung dieser Daten länger dauern. Daher wird praktisch alles durch FUIP zwischengespeichert (&amp;quot;gepuffert&amp;quot;). Wenn man nun neue Devices anlegt bzw. bestehende Devices ändert, dann bekommt das FUIP-Device davon unter Umständen nichts mit. In so einem Fall kann man mit &amp;lt;code&amp;gt;set &amp;lt;name&amp;gt; refreshBuffer&amp;lt;/code&amp;gt; den Zwischenspeicher löschen, um FUIP dazu zu zwingen, die Informationen erneut zu ermitteln.&lt;br /&gt;
&lt;br /&gt;
=== Gets ===&lt;br /&gt;
Alle Get-Kommandos sind nur für FUIP-interne Zwecke bestimmt.&lt;br /&gt;
&lt;br /&gt;
=== Internals ===&lt;br /&gt;
;editOnly: Hier wird der &amp;quot;editOnly-Modus&amp;quot; gespeichert, der auf der Konfig-Oberfläche ein- und ausgeschaltet werden kann. Siehe auch [[#Das Konfigurations-Popup|Das Konfigurations-Popup]].&lt;br /&gt;
&lt;br /&gt;
=== Readings ===&lt;br /&gt;
FUIP-Devices haben keine Readings.&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
;baseHeight: Basishöhe einer Zelle, d.h. eine 1x1-Zelle ist &#039;&#039;baseHeight&#039;&#039; Pixel hoch. Standardwert ist 108.&lt;br /&gt;
;baseWidth: Basisbreite einer Zelle, d.h. eine 1x1-Zelle ist &#039;&#039;baseWidth&#039;&#039; Pixel breit. Standardwert ist 142. Diese Abmessungen beziehen sich nur auf die Zelle selbst, nicht auf den Zwischenraum zwischen zwei Zellen. Der Zwischenraum beträgt (horizontal und vertikal) 10 Pixel.&lt;br /&gt;
;pageWidth: Seitenbreite in Pixel. Wenn &#039;&#039;pageWidth&#039;&#039; nicht gesetzt ist (das ist der Default), dann wird die Seitenbreite nicht festgelegt. Sie ergibt sich dann aus &#039;&#039;baseWidth&#039;&#039; (d.h. die Breite einer 1er-Zelle) und der Anzahl der verwendeten Spalten plus die Breite der Zwischenräume. D.h. in der Regel muss man &#039;&#039;pageWidth&#039;&#039; nicht angeben.&lt;br /&gt;
:Die Angabe in &#039;&#039;pageWidth&#039;&#039; beeinflusst auch die Darstellung des Hintergrundbilds, falls das Attribut &#039;&#039;styleBackgroundImage&#039;&#039; gesetzt ist. Siehe &#039;&#039;styleBackgroundImage&#039;&#039; für Details.&lt;br /&gt;
;fhemwebUrl: Mit FUIP kann man sich auch an ein &amp;quot;entferntes&amp;quot; FHEM ankoppeln. Das Attribut &#039;&#039;fhemwebUrl&#039;&#039; hat dabei in etwa die Bedeutung wie &#039;&#039;fhemweb_url&#039;&#039; bei FTUI. (Siehe auch hier: [[FHEM Tablet UI#Verbindung zu FHEM|Verbindung zu FHEM (FTUI)]].) Genau wie beim &amp;quot;normalen&amp;quot; FTUI muss das Attribut &#039;&#039;CORS&#039;&#039; der FHEMWEB-Instanz des Ziels auf &amp;quot;1&amp;quot; stehen. Ansonsten würde schon FTUI nicht funktionieren. Außerdem darf die FHEMWEB-Instanz keine Passwort-Prüfung haben. Stattdessen kann man mit dem Attribut &#039;&#039;allowedfrom&#039;&#039; oder einer allowed-Instanz den Zugriff einschränken. &lt;br /&gt;
:Man darf &#039;&#039;fhemwebUrl&#039;&#039; auf keinen Fall setzen (auch nicht auf 127.0.0.1 oder so), wenn sich die FUIP-Instanz auf das lokale FHEM beziehen soll. In dem Fall würde FHEM ewig auf sich selbst warten.&lt;br /&gt;
:Wenn man ein &amp;quot;entferntes&amp;quot; FHEM benutzt, dann können einige Funktionen der Konfigurationsoberfläche etwas Zeit brauchen. Zum Beispiel müssen für die Eingabehilfe für Devices alle Devices aus dem entfernten FHEM gelesen werden. Das ist so implementiert, dass das entfernte FHEM möglichst wenig belastet wird, was aber zu Lasten des FUIP-FHEM geht. Siehe auch das Set-Kommando &#039;&#039;refreshBuffer&#039;&#039; zu diesem Thema.&lt;br /&gt;
;locked: Wenn &#039;&#039;locked&#039;&#039; auf &amp;quot;1&amp;quot; gesetzt wird, dann sind die FUIP-Seiten gegen Bearbeitung gesperrt. Das Zahnrad-Icon oben rechts erscheint dann nicht mehr. Dadurch kann ein reiner &amp;quot;Tablet-UI Benutzer&amp;quot; die Seiten nicht mehr ändern und die Oberfläche sieht komplett wie normalerweise bei FTUI aus. Zusätzlich verschwinden auch die Zellennummern rechts neben den Zellenüberschriften und Zellen ohne Überschrift haben dann auch keinen &amp;quot;Titelbalken&amp;quot; mehr.&lt;br /&gt;
;styleBackgroundImage: Dateiname des Hintergrundbilds. Die Bilddatei muss sich im Verzeichnis &amp;lt;fhem&amp;gt;/FHEM/lib/FUIP/images befinden. (&amp;lt;fhem&amp;gt; steht meistens für /opt/fhem) Unterstützt werden jpg- und png- Dateien. Nachdem eine neue Datei hochgeladen wurde, muss man im Browser (in FHEMWEB) die Seite einmal auffrischen (neu laden), um die neue Datei verwenden zu können.   &lt;br /&gt;
:Falls das Attribut &#039;&#039;pageWidth&#039;&#039; gesetzt ist, dann wird die Breite des Hintergrundbilds auf die angegebene Größe gesetzt. Ansonsten (ohne &#039;&#039;pageWidth&#039;&#039;) nimmt das Bild die Breite des Browser-Fensters ein. Die Höhe des Bilds wird entsprechend skaliert, man muss sich also selbst darum kümmern, dass das Bild ein passendes Seitenverhältnis hat.&lt;br /&gt;
:Bei Verwendung eines Hintergrundbilds werden die Zellenhintergründe automatisch auf halbtransparent gesetzt, so dass das Bild durchscheint. &lt;br /&gt;
;styleColor: Standard-Textfarbe (Vordergrundfarbe) für alle Views. Der Defaultwert ist #808080, also ein helles Grau. Man kann die Farbe nicht für alle FTUI-Widgets festlegen. D.h. &#039;&#039;styleColor&#039;&#039; funktioniert nicht unbedingt überall.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* {{Link2Forum|Topic=85869|LinkText=Foren-Thread zur Geburt von FUIP}}&lt;br /&gt;
* [https://www.youtube.com/watch?v=9DPrMOvZ1wo Youtube-Webinar von Thorsten]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:FHEM Frontends]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FTUI_Widget_Chart&amp;diff=31159</id>
		<title>FTUI Widget Chart</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FTUI_Widget_Chart&amp;diff=31159"/>
		<updated>2019-08-29T13:51:52Z</updated>

		<summary type="html">&lt;p&gt;Stephan: Benötigten Parameter bei DbLog hinzugefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Das [[{{PAGENAME}}|Chart Widget]] ist ein Widget für [[FHEM Tablet UI]], mit dem sich verschiedenste Diagramme darstellen lassen. Die Aneinanderreihung mehrerer Werte eines Device-Readings zu einem zeitlichen Verlauf wird dabei als Graph bezeichnet.&lt;br /&gt;
&lt;br /&gt;
Es können beliebige Werte dargestellt und entsprechend der Sinnhaftigkeit, oder persönlichem Geschmack, formatiert werden. Farbe und Form der Linien sind je Graph einstellbar, auch wenn mehrere gleichzeitig in einem Diagramm angezeigt werden.&lt;br /&gt;
&lt;br /&gt;
Jedes Diagramm kann zwei Y-Achsen besitzen. Die primäre Y-Achse (primary) wird auf der linken Seite angezeigt, die sekundäre Y-Achse (secondary) auf der rechten Seite. Beide Achsen können unterschiedlich formatiert werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Chart_tabletUI.png&lt;br /&gt;
Datei:FTUI Widget Chart Stacked.png&lt;br /&gt;
Datei:FTUI Widget Chart-fc-Proplanta.png&lt;br /&gt;
Datei:Wetterchart2.png&lt;br /&gt;
Datei:PieChart.png&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Attribute==&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! style=&amp;quot;width:150px&amp;quot;|Attribut&lt;br /&gt;
!Beschreibung&lt;br /&gt;
!Standard-Wert&lt;br /&gt;
!Beispiel&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-device&#039;&#039;&#039;||Name des FHEM-Device, das die Aktualisierung des Charts triggert||||data-device=&amp;quot;WohnzimmerHeizung&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-get&#039;&#039;&#039;||Reading, das das Update des Diagramms triggert||&#039;STATE&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-logdevice&#039;&#039;&#039;||Name des Log-Device, das dargestellt werden soll, oder ein Array, um mehrere Werte in einem Diagramm darzustellen||||data-logdevice=&amp;quot;FileLog_WohnzimmerHeizung&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-logfile&#039;&#039;&#039;|| Name des Log-Files, aus dem die Daten entnommen werden sollen (oder Array)||&#039;-&#039; = aktuelle Datei||data-logfile=&amp;quot;WohnzimmerHeizung-2015.log&amp;quot;&amp;lt;br&amp;gt;Beachte: Der Wert &amp;quot;CURRENT&amp;quot; ermöglicht die Navigation auch zu älterne Logfiles (Jahreswechsel)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-columnspec&#039;&#039;&#039;||Ermittelt den Wert aus dem Log-File, der angezeigt werden soll (oder Array)||||data-columnspec=&amp;quot;4:meas.*&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-style&#039;&#039;&#039;||Stil, wie die Graph-Linien dargestellt werden sollen (z.B. &#039;SVGplot l0&#039; oder &#039;ftui l0dash&#039;), oder ein Array, wenn mehrere Linien unterschiedlich dargestellt werden sollen (siehe [[#Aussehen_der_Linien|Hinweise]])||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-ptype&#039;&#039;&#039;||Form, wie die Graphen dargestellt werden sollen (z.B. &#039;lines&#039;, &#039;cubic&#039; oder &#039;fa-cog&#039;), oder ein Array (siehe [[#Form_der_Linien|Hinweise]])||&#039;lines&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-uaxis&#039;&#039;&#039;||Name der Achse, die verwendet werden soll (&#039;primary&#039; = links, oder &#039;secondary&#039; = rechts), oder ein Array||&#039;primary&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-legend&#039;&#039;&#039;||Bezeichnung des Graphen (wird in Legende und am Cursor angezeigt), oder ein Array||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-minvalue&#039;&#039;&#039;||Minimaler Wert, der auf der linken Y-Achse (&#039;primary&#039;) angezeigt werden soll. &#039;auto&#039; = automatische Berechnung||&#039;10&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-maxvalue&#039;&#039;&#039;||Maximaler Wert, der auf der linken Y-Achse (&#039;primary&#039;) angezeigt werden soll. &#039;auto&#039; = automatische Berechnung||&#039;30&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-minvalue_sec&#039;&#039;&#039;||Minimaler Wert, der auf der rechten Y-Achse (&#039;secondary&#039;) angezeigt werden soll. &#039;auto&#039; = automatische Berechnung||&#039;auto&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-maxvalue_sec&#039;&#039;&#039;||Maximaler Wert, der auf der rechten Y-Achse (&#039;secondary&#039;) angezeigt werden soll. &#039;auto&#039; = automatische Berechnung||&#039;auto&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-xticks&#039;&#039;&#039;||Abstand zwischen den vertikalen Hilfslinien (bezogen auf die X-Achse) in Minuten. &#039;auto&#039; = automatische Berechnung||&#039;auto&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-yticks&#039;&#039;&#039;||Abstand zwischen den horizontalen Hilfslinien (bezogen auf die linke Y-Achse). Kann auch ein Array mit Werte-Paaren enthalten, um die Linien mit Text zu beschriften.||&#039;auto&#039;||data-yticks=&#039;[[0,&amp;quot;open&amp;quot;],[1,&amp;quot;closed&amp;quot;]]&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-yticks_format&#039;&#039;&#039;||Dient zur Formatierung der Ticks der Y-Achse. Die Formatierung geschieht über Platzhalter, Trenner und einen beliebigen durch &#039; &#039; getrennten Text. Als Platzhalter dient ein oder mehrere &#039;#&#039;, als Trenner können &#039;.&#039;, &#039;,&#039; und &#039;:&#039; verwendet werden. Ist ein Trenner enthalten (z.B. &#039;#.##&#039;) dann bedeutet das in dem Beispiel, dass der Ytick mit 2 Nachkommastellen versehen wird und vorne Platz für eine Stelle vor dem Komma vorgehalten wird (führende Nullen werden nicht dargestellt, aber de Platz wird reserviert so dass das ganze rechtsbündig immer passt). Ist kein Trenner vorhanden, dann wird der Ytick auf die Summe der Platzhalter mit einer festen Gesamtbreite gesetzt (#### würde also bedeuten, dass immer 4 Stellen (ohne Trenner) verwendet werden. aus 0.4 würde 0.400 aus 10.437 würde 10.44). Als Trenner kann man z.B. für Zeiten auch einen &#039;:&#039; verwenden und dadurch auch so etwas wie &amp;quot;12:00 Uhr&amp;quot; realisieren (in dem Beispiel wäre data-yticks_format=&amp;quot;##:## Uhr&amp;quot; und kein data-yunit oder data-yticks=&amp;quot;##:##&amp;quot; und data-yunit=&amp;quot;Uhr&amp;quot;).||||data-yticks_format=&amp;quot;#.##&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-yticks_sec&#039;&#039;&#039;||Abstand zwischen den horizontalen Hilfslinien (bezogen auf die rechte Y-Achse). Kann auch ein Array mit Werte-Paaren enthalten, um die Linien mit Text zu beschriften.||&#039;auto&#039;||data-yticks=&#039;[[0,&amp;quot;open&amp;quot;],[1,&amp;quot;closed&amp;quot;]]&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-yticks_format_sec&#039;&#039;&#039;||Dient zur Formatierung der Ticks der Y-Achse. Die Formatierung geschieht über Platzhalter, Trenner und einen beliebigen durch &#039; &#039; getrennten Text. Als Platzhalter dient ein oder mehrere &#039;#&#039;, als Trenner können &#039;.&#039;, &#039;,&#039; und &#039;:&#039; verwendet werden. Ist ein Trenner enthalten (z.B. &#039;#.##&#039;) dann bedeutet das in dem Beispiel, dass der Ytick mit 2 Nachkommastellen versehen wird und vorne Platz für eine Stelle vor dem Komma vorgehalten wird (führende Nullen werden nicht dargestellt, aber de Platz wird reserviert so dass das ganze rechtsbündig immer passt). Ist kein Trenner vorhanden, dann wird der Ytick auf die Summe der Platzhalter mit einer festen Gesamtbreite gesetzt (#### würde also bedeuten, dass immer 4 Stellen (ohne Trenner) verwendet werden. aus 0.4 würde 0.400 aus 10.437 würde 10.44). Als Trenner kann man z.B. für Zeiten auch einen &#039;:&#039; verwenden und dadurch auch so etwas wie &amp;quot;12:00 Uhr&amp;quot; realisieren (in dem Beispiel wäre data-yticks_format=&amp;quot;##:## Uhr&amp;quot; und kein data-yunit oder data-yticks=&amp;quot;##:##&amp;quot; und data-yunit=&amp;quot;Uhr&amp;quot;).||||data-yticks_format_sec=&amp;quot;#.##&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-yticks_prio&#039;&#039;&#039;||Legt fest, ob die horizontalen Hilfslinien der linken (primary) oder der rechten (secondary) Y-Achse zugeordnet werden sollen||||data-yticks_prio=&#039;secondary&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-ytype&#039;&#039;&#039;||Legt fest, ob die primäre y Achse logarithmisch sein soll (wert &amp;quot;log&amp;quot;)||||data-ytype=&amp;quot;log&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-ytype_sec&#039;&#039;&#039;||Legt fest, ob die sekundäre y Achse logarithmisch sein soll (wert &amp;quot;log&amp;quot;)||||data-ytype_sec=&amp;quot;log&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-y_margin&#039;&#039;&#039;|||Gibt die Möglichkeit, Abstände zwischen den Graphen und dem oberen Rand des Plots zu definieren (Einheit Pixel). Falls der Wert skalar ist, werden oben und unten die gleichen Abstände eingehalten. Falls ein 2D Array angegeben wird, können die Werte unten (erster Wert im Array) und oben (zweiter Wert im Array) getrennt festgelegt werden||&amp;quot;0&amp;quot;|||data-y_margin=&#039;[&amp;quot;10&amp;quot;,&amp;quot;20&amp;quot;]&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-y_margin_sec&#039;&#039;&#039;|||Gibt die Möglichkeit, Abstände zwischen den Graphen und dem oberen Rand des Plots zu definieren (Einheit Pixel). Falls der Wert skalar ist, werden oben und unten die gleichen Abstände eingehalten. Falls ein 2D Array angegeben wird, können die Werte unten (erster Wert im Array) und oben (zweiter Wert im Array) getrennt festgelegt werden||&amp;quot;0&amp;quot;|||data-y_margin=&#039;[&amp;quot;10&amp;quot;,&amp;quot;20&amp;quot;]&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-daysago_start&#039;&#039;&#039;||Anzahl der vergangenen Tage, wo das Diagramm beginnen soll. &#039;0&#039; = Beginn heute 0:00 Uhr. (siehe [[#Zeitstrahl_.2F_Start_.26_Ende_auf_der_X-Achse|Hinweise]])||&#039;0&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-daysago_end&#039;&#039;&#039;||Anzahl der vergangenen Tage, wo das Diagramm enden soll. &#039;-1&#039; = Ende heute 24:00 Uhr. (siehe [[#Zeitstrahl_.2F_Start_.26_Ende_auf_der_X-Achse|Hinweise]])||&#039;-1&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-xticks_round&#039;&#039;&#039;||Wenn vorhanden und entweder &#039;h&#039;, &#039;d&#039;, &#039;w&#039;, wird auf Stunde, Tag oder Woche bei den xticks gerundet (also die Tickmarks und die Gridlines bei den entsprechend gerundeten Zeiten gesetzt). Es kann auch &#039;auto&#039; angegeben werden, um eine autmoatische Rundung durchzuführen||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-nofulldays&#039;&#039;&#039;||Aktiviert/deaktiviert die Rundung der X-Achse auf ganze Tage. Binärwert (&#039;true&#039; oder &#039;false&#039;)||&#039;false&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-timeformat&#039;&#039;&#039;||Zeitformat für die Anzeige an der X-Achse (siehe [[#Zeitformat_der_X-Achse|Hinweise]])||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-timeranges&#039;&#039;&#039;||Hierdurch können vordefinierte Zeiträume für die X-Achse festgelegt werden, die dann durch eine pulldown menu (neuer Button oben neben dem &amp;quot;-&amp;quot; Button) direkt ausgewählt werden können. Der Parameter ist ein Array aus Array Einträgen, welche jeweils [&amp;lt;Name&amp;gt;,&amp;lt;daysago_start&amp;gt;,&amp;lt;daysago_end&amp;gt;] enthalten)||&lt;br /&gt;
||data-timeranges=&#039;[&amp;lt;br&amp;gt;&lt;br /&gt;
[&amp;quot;Actual Year&amp;quot;,&amp;quot;0Y&amp;quot;,&amp;quot;-1Y&amp;quot;],&amp;lt;br&amp;gt;&lt;br /&gt;
[&amp;quot;Last Year&amp;quot;,&amp;quot;1Y&amp;quot;,&amp;quot;0Y&amp;quot;],&amp;lt;br&amp;gt;&lt;br /&gt;
[&amp;quot;Actual Month&amp;quot;,&amp;quot;0M&amp;quot;,&amp;quot;-1M&amp;quot;],&amp;lt;br&amp;gt;&lt;br /&gt;
[&amp;quot;Last Month&amp;quot;,&amp;quot;1M&amp;quot;,&amp;quot;0M&amp;quot;],&amp;lt;br&amp;gt;&lt;br /&gt;
[&amp;quot;Actual Week&amp;quot;,&amp;quot;0W&amp;quot;,&amp;quot;-1W&amp;quot;],&amp;lt;br&amp;gt;&lt;br /&gt;
[&amp;quot;Last Week&amp;quot;,&amp;quot;1W&amp;quot;,&amp;quot;0W&amp;quot;],&amp;lt;br&amp;gt;&lt;br /&gt;
[&amp;quot;Today&amp;quot;,&amp;quot;0D&amp;quot;,&amp;quot;-1D&amp;quot;],&amp;lt;br&amp;gt;&lt;br /&gt;
[&amp;quot;Yesterday&amp;quot;,&amp;quot;1D&amp;quot;,&amp;quot;0D&amp;quot;]&amp;lt;br&amp;gt;&lt;br /&gt;
]&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-ytext&#039;&#039;&#039;||Text, der neben der linken Y-Achse angezeigt wird||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-ytext_sec&#039;&#039;&#039;||Text, der neben der rechten Y-Achse angezeigt wird||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-yunit&#039;&#039;&#039;||Einheit, die an der linken Y-Achse angezeigt wird||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-yunit_sec&#039;&#039;&#039;||Einheit, die an der rechten Y-Achse angezeigt wird||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-show_both_axes&#039;&#039;&#039;||Legt fest ob beide Y-Achsen Linien gezeichnet werden sollen||false||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-crosshair&#039;&#039;&#039;||Aktiviert/deaktiviert den Fadenkreuz-Cursor. Binärwert (&#039;true&#039; oder &#039;false&#039;)||&#039;false&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-cursorgroup&#039;&#039;&#039;||Zahl zur Gruppierung der Werte am Fadenkreuz-Cursor ([[#Fadenkreuz-Cursor|Hinweise]])||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-scrollgroup&#039;&#039;&#039;||Zahl zur Gruppierung der Graphen beim Bewegen und Zoomen. Alle Linien mit der selben Zahl werden miteinander gekoppelt und bewegen sich gemeinsam.||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-showlegend&#039;&#039;&#039;||Aktiviert/deaktiviert die Anzeige der Legene. Binärwert (&#039;true&#039; oder &#039;false&#039;)||&#039;false&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-legendpos&#039;&#039;&#039;||Array von zwei Werten, die die horizontale und vertikale Position der Legende festlegen ([[#Legende|Hinweise]])||&#039;[&amp;quot;top&amp;quot;,&amp;quot;right&amp;quot;]&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-legend_horiz&#039;&#039;&#039;||legt fest, dass die Legendeneinträge horizontal angeordnet sind (anstatt vertikal wie im default Fall)||false||data-legend_horiz=&amp;quot;true&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-width&#039;&#039;&#039;||Breite des Diagramms (in % oder px)||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-height&#039;&#039;&#039;||Höhe des Diagramms (in % oder px)||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-graphsshown&#039;&#039;&#039;||Aktiviert/deaktiviert die initiale Anzeige von Graphen. Binärwert (&#039;true&#039; oder &#039;false&#039;). Array, wenn mehrere Linien angezeigt werden sollen.||||data-graphsshown=&#039;[true,false,true]&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-ddd&#039;&#039;&#039;||Einstellung für die 3D-Drehung ([[#3-dimensionale_Drehung|Hinweise]])||||data-ddd=&#039;[&amp;quot;40&amp;quot;,&amp;quot;60&amp;quot;,&amp;quot;0&amp;quot;]&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-dddspace&#039;&#039;&#039;||Abstand zwischen zwei Graphen, wenn die 3D-Anzeige aktiviert wurde (px)||&#039;15&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-dddwidth&#039;&#039;&#039;||Breite, bzw. Tiefe der Graphen, wenn diese 3-dimensional angezeigt werden (px)||&#039;10&#039;||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-title&#039;&#039;&#039;||Titel, der über dem Diagramm angezeigt werden soll. Der Inhalt kann auch dynamisch erzeugt werden ([[#Diagrammtitel|Hinweise]])||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-title_class&#039;&#039;&#039;||Klassenname für die Formatierung des Titels. Die Eigenschaften müssen dann entsprechend in einem CSS File angegeben werden (z.B. in fhem-tablet-ui-user.css)||||&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;data-prefetch&#039;&#039;&#039;||Legt fest, ob zusätzliche Daten rechts und links des Plots im Hintergrund vom Server geholt werden sollen||false||data-prefetch=&amp;quot;true&amp;quot;&lt;br /&gt;
|}&lt;br /&gt;
Einige Parameter (style, maxvalue, minvalue, maxvalue_sec, minvalue_sec) können auch aus Readings dynamisch gesetzt werden wenn &amp;quot;&amp;lt;device&amp;gt;:&amp;lt;reading&amp;gt;&amp;quot; als Parameter gesetzt wird. Damit kann man z.B. in FHEM über notify etc. die Linientypen dynamisch anpassen (z.B. wenn der Wert eines Devices in einem bestimmten Bereich liegt, ändert sich die Farbe des Graphen).&lt;br /&gt;
&lt;br /&gt;
==CSS Klassen==&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Klasse!!Beschreibung&lt;br /&gt;
{{FTUI Klasse|fullsize}}{{FTUI Klasse|noticks}}{{FTUI Klasse|nobuttons}}{{FTUI Klasse|small}}{{FTUI Klasse|normal}}{{FTUI Klasse|big}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Folgende Widget-spezifsche Klassen können zusätzlich in einer eigenen CSS-Datei definiert werden:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Klasse&lt;br /&gt;
!Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;chart-background&#039;&#039;&#039;||Hintergrundfarbe des Diagramms&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;buttons&#039;&#039;&#039;||Größe und Farbe der Buttons&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;text.axes&#039;&#039;&#039;||Generelle Schriftart und Farbe der Achsen&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;gridlines&#039;&#039;&#039;||Generelle Farbe und Größe der Gitternetzlinien&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;xaxis&#039;&#039;&#039;||Schriftart, Größe und Farbe der X-Achse&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;yaxis&#039;&#039;&#039;||Schriftart, Größe und Farbe der Y-Achse&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;xticks&#039;&#039;&#039;||Schriftart, Größe und Farbe der X-Achse (Zwischenlinien)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;yticks&#039;&#039;&#039;||Schriftart, Größe und Farbe der Y-Achse (Zwischenlinien)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;crosshair&#039;&#039;&#039;||Schriftart, Größe und Vordergrund/Hintergrundfarbe der Momentanwerte am Fadenkreuzcursor&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;caption&#039;&#039;&#039;||Schriftart, Größe und Farbe der Text-Buttons für Legende und Cursor&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;legend&#039;&#039;&#039;||Schriftart, Größe und Farbe der Legende&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Standardwerte sind in der Datei &amp;lt;code&amp;gt;css/ftui_chart.css&amp;lt;/code&amp;gt; zu finden.&lt;br /&gt;
&lt;br /&gt;
==Datenquellen==&lt;br /&gt;
Beim Chart-Widget können die gleichen Datenquellen genutzt werden, die auch für SVG-Plots verwendet werden können:&lt;br /&gt;
# [[FileLog]]: Verlaufsdaten einer Textdatei entnehmen&lt;br /&gt;
# [[DbLog]]: Verlaufsdaten einer Datenbank entnehmen&lt;br /&gt;
# [[LogProxy]]: Daten dynamisch berechnet&lt;br /&gt;
&lt;br /&gt;
===FileLog===&lt;br /&gt;
Um [[FileLog]] zu nutzen, wird als &#039;&#039;&#039;data-logdevice&#039;&#039;&#039; das FHEM-Device für das FileLog angegeben. In der Regel entstehen hier im Laufe der Zeit mehrere Log-Dateien. Name und Anzahl sind von der Definition abhängig - meist wird jeden Monat oder jedes Jahr eine neue Datei angelegt. Die gewünschte Datei kann mit &#039;&#039;&#039;data-logfile&#039;&#039;&#039; ausgewählt werden. Möchte man stets die aktuelle Datei verwenden (macht vor allem dann Sinn, wenn man die neusten Daten anzeigen will), kann das Attribut weggelassen, oder explizit &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; eingetragen werden. Zuletzt wird &#039;&#039;&#039;data-columnspec&#039;&#039;&#039; benötigt, um die gewünschten Daten zu in der Logdatei zu identifizieren. Hier wird die Spalte, in der die Daten stehen, gefolgt von Doppelpunkt und Readingname angegeben.&lt;br /&gt;
&lt;br /&gt;
Für ein Heizungsthermostat von Homematic mit dem Namen &#039;&#039;DG.wz.HZ.Heizungsventil&#039;&#039; ergibt sich somit beispielhaft folgende Definition, um gemessene Temperatur, Sollwert und Ventilstellung im Diagramm darzustellen:&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;chart&amp;quot;&lt;br /&gt;
	 data-device=&amp;quot;DG.wz.HZ.Heizungsventil&amp;quot;&lt;br /&gt;
	 data-logdevice=&amp;quot;FileLog_DG.wz.HZ.Heizungsventil&amp;quot;&lt;br /&gt;
	 data-logfile=&amp;quot;-&amp;quot;&lt;br /&gt;
	 data-columnspec=&#039;[&amp;quot;4:measured-temp&amp;quot;,&amp;quot;4:desired-temp&amp;quot;,&amp;quot;4:actuator&amp;quot;]&#039;&lt;br /&gt;
	 ...&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollen Daten von unterschiedlichen Geräten in einem Diagramm angezeigt werden, muss &#039;&#039;&#039;data-logdevice&#039;&#039;&#039; als Array nach dem Schema &amp;lt;code&amp;gt;data-logdevice=&#039;[&amp;quot;&amp;lt;Logdatei_1&amp;gt;&amp;quot;,&amp;quot;&amp;lt;Logdatei_2&amp;gt;&amp;quot;,&amp;quot;&amp;lt;Logdatei_3&amp;gt;&amp;quot;]&#039;&amp;lt;/code&amp;gt; definiert werden. Für jeden Eintrag in &#039;&#039;&#039;data-columnspec&#039;&#039;&#039; muss es auch den passenden Eintrag in &#039;&#039;&#039;data-logdevice&#039;&#039;&#039; geben (auch die Reihenfolge ist relevant).&lt;br /&gt;
&lt;br /&gt;
===DbLog===&lt;br /&gt;
Um die Daten aus [[DbLog]] anzeigen zu können, werden die gleichen Attribute verwendet und mit für die Datenbank angepassten Werten beschrieben. Bei &#039;&#039;&#039;data-logdevice&#039;&#039;&#039; das FHEM-Device für die Datenbank angegeben. Im nachfolgenden Beispiel heißt diese &amp;lt;code&amp;gt;logdb&amp;lt;/code&amp;gt; und besitzt wie üblich zwei Tabellen: &amp;lt;code&amp;gt;current&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;history&amp;lt;/code&amp;gt; (der zeitliche Verlauf liegt in letzterer). Der Tabellenname wird bei &#039;&#039;&#039;data-logfile&#039;&#039;&#039; eingetragen. Da die Daten in der Datenbank etwas anders abgelegt werden, muss auch &#039;&#039;&#039;data-columnspec&#039;&#039;&#039; entsprechend angepasst werden. Statt der Spalte wird hier das FHEM-Device, gefolgt von Doppelpunkt und Readingname angegeben.&lt;br /&gt;
&lt;br /&gt;
Für das oben beschriebene Homematic-Heizungsthermostat ergibt sich dann folgende Definition, um die gleichen Daten aus einer Datenbank, statt einem LogFile zu lesen:&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;chart&amp;quot;&lt;br /&gt;
	 data-device=&amp;quot;DG.wz.HZ.Heizungsventil&amp;quot;&lt;br /&gt;
         data-get=&amp;quot;measured-temp&amp;quot;&lt;br /&gt;
         data-style=&#039;[&amp;quot;ftui l0&amp;quot;]&#039;&lt;br /&gt;
	 data-logdevice=&amp;quot;logdb&amp;quot;&lt;br /&gt;
	 data-logfile=&amp;quot;HISTORY&amp;quot;&lt;br /&gt;
	 data-columnspec=&#039;[&amp;quot;DG.wz.HZ.Heizungsventil:measured-temp&amp;quot;,&amp;quot;DG.wz.HZ.Heizungsventil:desired-temp&amp;quot;,&amp;quot;DG.wz.HZ.Heizungsventil:actuator&amp;quot;]&#039;&lt;br /&gt;
	 ...&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für die Anzeige von unterschiedlichen Geräten in einem Diagramm, muss nur &#039;&#039;&#039;data-columnspec&#039;&#039;&#039; entsprechend angepasst werden, solange sich alle Daten in der Datenbank befinden.&lt;br /&gt;
&lt;br /&gt;
===LogProxy===&lt;br /&gt;
Um die Daten mittels [[LogProxy]] berechnen und anzeigen zu können, muss in FHEM ein LogProxy-Device definiert sein:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define myLogProxy logProxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Weitere Einstellungen am LogProxy sind nicht nötig, die bloße Existenz reicht.&lt;br /&gt;
&lt;br /&gt;
Bei &#039;&#039;&#039;data-logdevice&#039;&#039;&#039; wird das FHEM-Device für den LogProxy angegeben. Im nachfolgenden Beispiel heißt dieses &amp;lt;code&amp;gt;myLogProxy&amp;lt;/code&amp;gt;. Das Attribut &#039;&#039;&#039;data-logfile&#039;&#039;&#039; wird für LogProxy nicht benötigt. Befinden sich nur LogProxy-Werte im Diagramm kann das Attribut komplett entfallen. Sollen weitere Werte angezeigt werden, bleibt die Definition im Array einfach leer.&lt;br /&gt;
&lt;br /&gt;
Im Attribut &#039;&#039;&#039;data-columnspec&#039;&#039;&#039; wird eine Formel angegeben, wie die Werte berechnet werden sollen. Hier können die Formeln 1:1 von einem eventuell vorhandenen SVG-Plot übernommen werden. &#039;&#039;&#039;Dabei gibt es jedoch folgendes zu beachten:&#039;&#039;&#039; Befindet sich die Formel in einem Array, dürfen die Formeln keine Anführungszeichen (&amp;lt;code&amp;gt;&amp;quot;&amp;lt;/code&amp;gt;) beinhalten. Stattdessen müssen sie als escapter Ascii-Code (&amp;lt;code&amp;gt;\\x22&amp;lt;/code&amp;gt;) eingefügt werden.&lt;br /&gt;
&lt;br /&gt;
Das nachfolgende Beispiel zeigt, wie Vorhersagewerte aus einem FHEM-Device vom Typ Proplanta (Name hier &amp;lt;code&amp;gt;AU.xx.WE.Proplanta&amp;lt;/code&amp;gt;) angezeigt werden können.&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;chart&amp;quot;&lt;br /&gt;
	 data-device=&amp;quot;AU.xx.WE.Proplanta&amp;quot;&lt;br /&gt;
	 data-logdevice=&#039;[&lt;br /&gt;
						&amp;quot;myLogProxy&amp;quot;,&lt;br /&gt;
						&amp;quot;myLogProxy&amp;quot;,&lt;br /&gt;
						&amp;quot;myLogProxy&amp;quot;,&lt;br /&gt;
						&amp;quot;myLogProxy&amp;quot;&lt;br /&gt;
					]&#039;&lt;br /&gt;
	 data-columnspec=&#039;[&lt;br /&gt;
						&amp;quot;Func:logProxy_proplanta2Plot(\\x22AU.xx.WE.Proplanta\\x22,\\x22temp_\\x22,$from,$to,12,\\x22day\\x22)&amp;quot;,&lt;br /&gt;
						&amp;quot;Func:logProxy_proplanta2Plot(\\x22AU.xx.WE.Proplanta\\x22,\\x22rain_\\x22,$from,$to,12,\\x22day\\x22)&amp;quot;,&lt;br /&gt;
						&amp;quot;Func:logProxy_proplanta2Plot(\\x22AU.xx.WE.Proplanta\\x22,\\x22chOfRain_\\x22,$from,$to,12,\\x22day\\x22)&amp;quot;,&lt;br /&gt;
						&amp;quot;Func:logProxy_proplanta2Plot(\\x22AU.xx.WE.Proplanta\\x22,\\x22cloud_\\x22,$from,$to,12,\\x22day\\x22)&amp;quot;&lt;br /&gt;
					]&#039;&lt;br /&gt;
	 ...&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auch alle anderen Funktionen, die [[LogProxy]] bietet, können hier angewendet werden.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==Hinweise==&lt;br /&gt;
===Aktualisierung des Charts===&lt;br /&gt;
Damit der Refresh des Charts funktioniert, muss auch ein Device angegeben werden, der das Refresh triggert. Das Diagramm wird immer dann aktualisiert, wenn sich der Inhalt von &#039;&#039;&#039;data-get&#039;&#039;&#039; ändert.&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;chart&amp;quot;&lt;br /&gt;
	data-device=&amp;quot;WohnzimmerHeizung&amp;quot;&lt;br /&gt;
	data-logdevice=&amp;quot;FileLog_WohnzimmerHeizung&amp;quot;&lt;br /&gt;
	...&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Aussehen der Linien===&lt;br /&gt;
Mit dem Attribut &#039;&#039;&#039;data-style&#039;&#039;&#039; kann das Aussehen der Linien des jeweiligen Graphen verändert werden. Hierfür können die Standard-FHEM-Styles verwendet werden. Dazu wird das Attribut mit &amp;lt;code&amp;gt;SVGplot&amp;lt;/code&amp;gt;, gefolgt von einem Leerzeichen und der gewünschten Farbe/Stil befüllt. Es existieren jedoch auch noch weitere, an FTUI angepasste Styles, zu finden in der CSS-Datei &amp;lt;code&amp;gt;css/ftui_chart.css&amp;lt;/code&amp;gt;. Um diese zu verwenden, wird das Attribut mit &amp;lt;code&amp;gt;ftui&amp;lt;/code&amp;gt;, gefolgt von einem Leerzeichen und der gewünschten Farbe/Stil befüllt. Eigene Styles können zum Beispiel in der Datei &amp;lt;code&amp;gt;css/fhem-table-ui-user.css&amp;lt;/code&amp;gt; definiert werden.&lt;br /&gt;
&lt;br /&gt;
Folgende Übersicht zeigt die im Standard verfügbaren &#039;&#039;&#039;Farben&#039;&#039;&#039;, alle Abbildungen sind mit im FTUI-Style entstanden:&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[File:FTUI_Widget_Chart_Farbe-f0.png|thumb|none|150px|Farbe &amp;quot;l0&amp;quot;]] &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[File:FTUI_Widget_Chart_Farbe-f1.png|thumb|none|150px|Farbe &amp;quot;l1&amp;quot;]] &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[File:FTUI_Widget_Chart_Farbe-f2.png|thumb|none|150px|Farbe &amp;quot;l2&amp;quot;]] &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[File:FTUI_Widget_Chart_Farbe-f3.png|thumb|none|150px|Farbe &amp;quot;l3&amp;quot;]] &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[File:FTUI_Widget_Chart_Farbe-f4.png|thumb|none|150px|Farbe &amp;quot;l4&amp;quot;]] &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[File:FTUI_Widget_Chart_Farbe-f5.png|thumb|none|150px|Farbe &amp;quot;l5&amp;quot;]] &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[File:FTUI_Widget_Chart_Farbe-f6.png|thumb|none|150px|Farbe &amp;quot;l6&amp;quot;]] &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Angabe zur Farbe kann dann mit der Linienart kombiniert werden. Dazu stehen folgende &#039;&#039;&#039;Stile&#039;&#039;&#039; zur Verfügung:&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[File:FTUI_Widget_Chart_Form-2D-normal.png|thumb|none|225px|Darstellung in 2D: Stil &amp;quot;normal&amp;quot;]] &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[File:FTUI_Widget_Chart_Form-2D-dot.png|thumb|none|225px|Darstellung in 2D: Stil &amp;quot;dot&amp;quot;]] &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[File:FTUI_Widget_Chart_Form-2D-dash.png|thumb|none|225px|Darstellung in 2D: Stil &amp;quot;dash&amp;quot;]] &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[File:FTUI_Widget_Chart_Form-2D-fill.png|thumb|none|225px|Darstellung in 2D: Stil &amp;quot;fill&amp;quot;]] &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[File:FTUI_Widget_Chart_Form-2D-sym.png|thumb|none|225px|Darstellung in 2D: Stil &amp;quot;sym&amp;quot;]] &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&amp;lt;ul&amp;gt; &lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[File:FTUI_Widget_Chart_Form-3D-normal.png|thumb|none|225px|Darstellung in 3D: Stil &amp;quot;normal&amp;quot;]] &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[File:FTUI_Widget_Chart_Form-3D-dot.png|thumb|none|225px|Darstellung in 3D: Stil &amp;quot;dot&amp;quot;]] &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[File:FTUI_Widget_Chart_Form-3D-dash.png|thumb|none|225px|Darstellung in 3D: Stil &amp;quot;dash&amp;quot;]] &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[File:FTUI_Widget_Chart_Form-3D-fill.png|thumb|none|225px|Darstellung in 3D: Stil &amp;quot;fill&amp;quot;]] &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li style=&amp;quot;display: inline-block;&amp;quot;&amp;gt; [[File:FTUI_Widget_Chart_Form-3D-sym.png|thumb|none|225px|Darstellung in 3D: Stil &amp;quot;sym&amp;quot;]] &amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Farbe und Stil werden kombiniert (zusammengeschrieben) beim Attribut &#039;&#039;&#039;data-style&#039;&#039;&#039; angegeben, sodass sich beispielsweise für eine graue Punktlinie folgendes ergibt: &amp;lt;code&amp;gt;data-style=&amp;quot;ftui l1dot&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
Um die Darstellung als normale Linie zu erhalten, darf im Gegensatz zu den anderen Linienformen der Stil &amp;lt;code&amp;gt;normal&amp;lt;/code&amp;gt; nicht angegeben werden. Für eine einfache graue Linie ist also die Angabe &amp;lt;code&amp;gt;data-style=&amp;quot;ftui l1&amp;quot;&amp;lt;/code&amp;gt; korrekt, wohingegen &amp;lt;code&amp;gt;data-style=&amp;quot;ftui l1normal&amp;quot;&amp;lt;/code&amp;gt; zu einer fehlerhaften Anzeige führt.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Der Stil &#039;&#039;sym&#039;&#039; ist speziell dafür geeignet, Symbole statt Linien zu zeichnen. Dazu kann beim Attribut &#039;&#039;&#039;data-ptype&#039;&#039;&#039; als Linienform ein beliebiges Font-Awsome-, oder Open Automation-Icon angegeben werden. Alle in diesem Abschnitt enthaltenen Abbildungen sind mit &amp;lt;code&amp;gt;data-ptype=&amp;quot;lines&amp;quot;&amp;lt;/code&amp;gt; entstanden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Übergänge mit datenabhängigen Grenzen:&#039;&#039;&#039; Plotstile (data-style) können auch direkt als Gradienten auf Basis von Plot-Datenwerten definiert werden. Dazu muss der Plotstil als Array angegeben werden. Der erste Wert des Arrays gibt an, ob nur die Linie gezeichnet werden soll (Zahl angeben, die die Dicke der Linie definiert) oder gefüllt (&amp;quot;fill&amp;quot; eintragen). Alle danach folgenden Array Elemente sind beliebig viele Stopp Punkte für die Farbübergänge, welche wiederum aus Arrays mit 3 Parametern bestehen. Für jeden Stopp Punkt werden der Datenwert, die Farbe und die Durchsichtigkeit gesetzt. Hierdurch lassen sich z.B. Einfärbungen setzen, die für Temperaturplots immer negative Werte blau einfärben und positive Werte rot. Zwischen den Stop Punkten wird die Farbe interpoliert, also ein weicher Übergang generiert. Will man harte Übergänge muss man Zwei Stopp Punkte mit unterschiedlichen Farbwerten aber dem gleichen Datenwert erzeugen.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel 1 für einen weichen Gradienten, der bei 0 von blau nach rot übergeht, bei diesem Übergang durchsichtig ist und von dort nach negativen bzw. positiven Werden immer deckender wird:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;data-style=&#039;[&amp;quot;fill&amp;quot;,[&amp;quot;-20&amp;quot;,&amp;quot;#0000ff&amp;quot;,&amp;quot;0.7&amp;quot;],[&amp;quot;0&amp;quot;,&amp;quot;#0000ff&amp;quot;,&amp;quot;0&amp;quot;],[&amp;quot;0&amp;quot;,&amp;quot;#ff0000&amp;quot;,&amp;quot;0&amp;quot;],[&amp;quot;302&amp;quot;,&amp;quot;#ff0000&amp;quot;,&amp;quot;0.7&amp;quot;]]&#039;&amp;lt;/code&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel 2 mit einem harten Übergang von blau nach rot bei 0:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;data-style=&#039;[&amp;quot;fill&amp;quot;,[&amp;quot;-20&amp;quot;,&amp;quot;#0000ff&amp;quot;,&amp;quot;0.7&amp;quot;],[&amp;quot;0&amp;quot;,&amp;quot;#0000ff&amp;quot;,&amp;quot;0.7&amp;quot;],[&amp;quot;0&amp;quot;,&amp;quot;#ff0000&amp;quot;,&amp;quot;0.7&amp;quot;],[&amp;quot;50&amp;quot;,&amp;quot;#ff0000&amp;quot;,&amp;quot;0.7&amp;quot;]]&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
Es gibt auch die Möglichkeit den Bereich zwischen zwei Graphen einzufärben. Dazu muss ein Wert in Columnspec als Array angegeben werden. Ist dies der Fall, dann wird der zweite Graph umgekehrt an den ersten angehängt.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
data-columnspec=&#039;[&lt;br /&gt;
    &amp;quot;Func:logProxy_proplanta2Plot(\\x22AgroWeather\\x22,\\x22weatherIcon\\x22,$from,$to,12)&amp;quot;,&lt;br /&gt;
    [&lt;br /&gt;
        &amp;quot;Func:logProxy_proplanta2Plot(\\x22AgroWeather\\x22,\\x22tempMax\\x22,$from,$to,12)&amp;quot;,&lt;br /&gt;
        &amp;quot;Func:logProxy_proplanta2Plot(\\x22AgroWeather\\x22,\\x22tempMin\\x22,$from,$to,12)&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;Func:logProxy_proplanta2Plot(\\x22AgroWeather\\x22,\\x22rain\\x22,$from,$to,0,\\x22day\\x22)&amp;quot;&lt;br /&gt;
]&#039;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
[[Datei:FTUI Widget Chart DynamicStyles.png|thumb|none|225px|dynamischer Übergang]]&lt;br /&gt;
&lt;br /&gt;
===Form der Linien===&lt;br /&gt;
Das Attribut &#039;&#039;&#039;data-ptype&#039;&#039;&#039; beeinflusst die Form der Linien. Hier sind folgende Werte möglich:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;lines&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;points&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;steps&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;fsteps&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;histeps&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;bars&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;ibars&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;cubic&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;quadratic&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;quadraticSmooth&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich ist es möglich, Symbole anzeigen zu lassen. Unterstützt werden Font-Awesome (&#039;fa-...&#039;), Open Automation (&#039;oa-...&#039;) und FHEM-Symbole (&#039;fs-...&#039;)). Damit die Symbole korrekt angezeigt werden, muss  im Attribut &#039;&#039;&#039;data-style&#039;&#039;&#039; der Stil &amp;lt;code&amp;gt;sym&amp;lt;/code&amp;gt; gewählt werden, da sonst nur Punkte, statt der Symbole gezeichnet werden.&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;chart&amp;quot;&lt;br /&gt;
	 data-device=&amp;quot;DG.wz.HZ.Heizungsventil&amp;quot;&lt;br /&gt;
	 data-logdevice=&amp;quot;FileLog_DG.wz.HZ.Heizungsventil&amp;quot;&lt;br /&gt;
	 ...&lt;br /&gt;
	 data-style=&amp;quot;ftui l1sym&amp;quot;&lt;br /&gt;
	 data-ptype=&amp;quot;fa-cog&amp;quot;&lt;br /&gt;
	 ...&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Größe der Symbole ist in der Datei &amp;lt;code&amp;gt;css/ftui_chart.css&amp;lt;/code&amp;gt; auf 12px festgelegt. Dieser Wert kann in einer eigenen CSS-Datei durch Anpassung von &amp;lt;code&amp;gt;stroke-width&amp;lt;/code&amp;gt; überschrieben werden.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;css&amp;quot;&amp;gt;&lt;br /&gt;
.ftui.l0sym		{ stroke:#DDA400; stroke-width:12px; fill:none; }&lt;br /&gt;
.ftui.l1sym		{ stroke:#BBBBBB; stroke-width:12px; fill:none; }&lt;br /&gt;
.ftui.l2sym		{ stroke:#CC0000; stroke-width:12px; fill:none; }&lt;br /&gt;
.ftui.l3sym		{ stroke:#CCCC00; stroke-width:12px; fill:none; }&lt;br /&gt;
.ftui.l4sym		{ stroke:#33CC33; stroke-width:12px; fill:none; }&lt;br /&gt;
.ftui.l5sym		{ stroke:#33CCCC; stroke-width:12px; fill:none; }&lt;br /&gt;
.ftui.l6sym		{ stroke:#3333CC; stroke-width:12px; fill:none; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;data-ptype&#039;&#039;&#039; kann auch Inhalt im Format &amp;lt;code&amp;gt;&#039;icon:1&#039;&amp;lt;/code&amp;gt; verarbeiten. Dann muss der zugehörige Wert in &#039;&#039;&#039;data-columnspec&#039;&#039;&#039; den Pfad zu einem Icon (z.B. für Wettervorhersagen) beinhalten. Der Y-Wert wird dann vom ersten Graphen übernommen. Weitere Ausführungen hierzu im Beispiel [[#Darstellung der Wetter Icons im Diagramm]].&lt;br /&gt;
&lt;br /&gt;
===Stapeln von Linien===&lt;br /&gt;
Über &#039;&#039;&#039;data-ptype&#039;&#039;&#039; kann zusätzlich festgelegt werden, ob Graphen übereinander gestapelt werden sollen. &amp;lt;code&amp;gt;data-ptype=&#039;lines:1&#039;&amp;lt;/code&amp;gt; bedeutet, dass der zugehörige Graph auf den Graph mit der Nummer 1 gestapelt werden soll. So etwas kann z.B. Sinn machen, wenn man den Stromverbrauch einzelner Devices darstellen und zusätzlich sehen will, wie hoch die Summe ist. Beispiel siehe unten.&lt;br /&gt;
&lt;br /&gt;
[[Datei:FTUI Widget Chart Stacked.png]]&lt;br /&gt;
&lt;br /&gt;
===Zeitstrahl / Start &amp;amp; Ende auf der X-Achse===&lt;br /&gt;
Die Attribute &#039;&#039;&#039;data-daysago_start&#039;&#039;&#039; und &#039;&#039;&#039;data-daysago_end&#039;&#039;&#039; dienen der Definition von Anfang und Ende der X-Achse. Im einfachsten Fall wird eine Anzahl an Tagen eingegeben, die sich auf das aktuelle Datum beziehen. Dabei gilt es zu beachten, dass es sich um &#039;&#039;vergangene Tage&#039;&#039; handelt. Das bedeutet, dass Tage in der Vergangenheit als positive Zahl angegeben werden, Tage in der Zukunft hingegen als negative Zahl. Es kann jeweils  auch ein fixes Datum (z.B. &#039;2013-10-23&#039;) angegeben werden. Uhrzeitangaben werden nur berücksichtigt, wenn &#039;&#039;&#039;data-nofulldays=&#039;true&#039; &#039;&#039;&#039; verwendet wird.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Relative Zeitangabe in Stunden/Tagen/Wochen/Monaten/Jahren&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Zur Ausgabe einer Anzahl an zurückliegenden Stunden bis zum aktuellen Zeitpunkt wird als Startzeitpunkt die Anzahl der Stunden/Tage/Wochen/Monate/Jahre angegeben, die angezeigt werden sollen, gefolgt vom Kleinbuchstaben &#039;&#039;&#039;h&#039;&#039;&#039; für Stunden, &#039;&#039;&#039;d&#039;&#039;&#039; für Tage, &#039;&#039;&#039;w&#039;&#039;&#039; für Wochen, &#039;&#039;&#039;m&#039;&#039;&#039; für Monate, &#039;&#039;&#039;y&#039;&#039;&#039; für Jahre, . Als Endzeitpunkt wird &#039;&#039;&#039;now&#039;&#039;&#039; gewählt.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das nachfolgende Beispiel zeigt die Werte der vergangenen 3 Stunden an:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
data-nofulldays=&amp;quot;true&amp;quot;&lt;br /&gt;
data-daysago_start=&amp;quot;3h&amp;quot;&lt;br /&gt;
data-daysago_end=&amp;quot;now&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fester Zeitbereich des heutigen Tages (Stunden/Tage/Wochen/Monate/Jahre)&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Um ein festes Stunden/Tages/Wochen/Monats/Jahresfenster anzuzeigen, werden die absoluten Stunden/Tages/Wochen/Monats/Jahreszahlen mit negativem Vorzeichen, gefolgt vom Großbuchstaben &#039;&#039;&#039;H&#039;&#039;&#039; (entsprechend D/W/M/Y für Tage/Wochen/Monate/Jahre) angegeben. Wird &#039;&#039;&#039;data-daysago_start&#039;&#039;&#039; als positiver Wert angegeben, wird die Anzahl der Stunden/Tage/Wochen/Monate/Jahre von heute 0:00 Uhr subtrahiert (siehe Rechenweg weiter unten).&amp;lt;br /&amp;gt;&lt;br /&gt;
Das Beispiel zeigt den Zeitbereich von heute 5:00 Uhr bis heute 22:00 Uhr:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
data-nofulldays=&amp;quot;true&amp;quot;&lt;br /&gt;
data-daysago_start=&amp;quot;-5H&amp;quot;&lt;br /&gt;
data-daysago_end=&amp;quot;-22H&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fester Zeitbereich Tage-übergreifend&#039;&#039;&#039;&amp;lt;br /&amp;gt;&lt;br /&gt;
Die Zeit in Tagen kann als Gleitkommazahl angegeben werden. Damit ist es möglich, Tage und Uhrzeiten zu kombinieren. Die Werte sind dann als Teil eines ganzen Tages, bezogen auf heute 0:00 Uhr zu errechnen und mit &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; als Teiler anzugeben.&amp;lt;br /&amp;gt;&lt;br /&gt;
Das nachfolgende Beispiel zeigt einen Zeitbereich von &#039;&#039;&#039;gestern 15:00 Uhr&#039;&#039;&#039; bis &#039;&#039;&#039;morgen 3:00 Uhr&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
data-nofulldays=&amp;quot;true&amp;quot;&lt;br /&gt;
data-daysago_start=&amp;quot;0.375&amp;quot;&lt;br /&gt;
data-daysago_end=&amp;quot;-1.125&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Für die nachfolgenden Rechenwege sind die Einheiten nur zur Verdeutlichung angegeben. Die Einheiten werden im Attribut nicht angegeben.&lt;br /&gt;
&lt;br /&gt;
Der Wert für &#039;&#039;&#039;GESTERN&#039;&#039;&#039; wird wie folgt ermittelt:&amp;lt;br /&amp;gt;&lt;br /&gt;
Ausgangspunkt ist heute 0:00 Uhr, gestern 15:00 Uhr liegt also 9 Stunden davor. Diese 9 Stunden sind ein &amp;lt;code&amp;gt;9/24 Tag&amp;lt;/code&amp;gt; und errechnet sich so:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1d / 24h = 0.0416666...d/h&lt;br /&gt;
0.0416d/h * 9h = 0.375d&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der Wert für &#039;&#039;&#039;MORGEN&#039;&#039;&#039; wird wie folgt ermittelt:&amp;lt;br /&amp;gt;&lt;br /&gt;
Ausgangspunkt ist wieder heute 0:00 Uhr, morgen 3:00 Uhr liegt dann 27 Stunden dahinter. Der Einfachheit halber werden hier nur die 3 Stunden errechnet und dann ein ganzer Tag addiert:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1d / 24h = 0.0416666...d/h&lt;br /&gt;
0.0416d/h * 3h = 0.125d&lt;br /&gt;
&lt;br /&gt;
0.125d + 1d = 1.125d&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Da das Attribut Tage in der Vergangenheit erwartet, muss für einen Wert in der Zukunft wieder eine negative Zahl angegeben werden.&lt;br /&gt;
&lt;br /&gt;
===Zeitformat der X-Achse===&lt;br /&gt;
Die Zeitanzeige auf der X-Achse kann sehr flexibel eingestellt werden. Dafür stehen verschiedene Platzhalter zur Verfügung, die durch spezielle Zeichen (&amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;/&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt; &amp;lt;/code&amp;gt; (Leerzeichen), &amp;lt;code&amp;gt;:&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;,&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;\&amp;lt;/code&amp;gt;) getrennt werden. Alle Zeichen werden trotz Escape-Zeichen (&amp;lt;code&amp;gt;\&amp;lt;/code&amp;gt;) in der Ausgabe angezeigt.&lt;br /&gt;
&lt;br /&gt;
Folgende Platzhalter werden unterstützt:&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;mm&#039;&amp;lt;/code&amp;gt;: Minuten als zweistellige Zahl&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;hh&#039;&amp;lt;/code&amp;gt;: Stunden als zweistellige Zahl&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;dd&#039;&amp;lt;/code&amp;gt;: Tag als zweistellige Zahl (Kalenderdatum)&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;MM&#039;&amp;lt;/code&amp;gt;: Monat als zweistellige Zahl (z.B. 02 für Februar)&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;MMM&#039;&amp;lt;/code&amp;gt;: Monat als dreistellige Abkürzung (z.B. Dec für Dezember)&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;MMMM&#039;&amp;lt;/code&amp;gt;: Langname des Monats (z.B. March)&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;ee&#039;&amp;lt;/code&amp;gt;: Wochentag als zweistellige Zahl (z.B. 00 für Sonntag)&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;eee&#039;&amp;lt;/code&amp;gt;: Wochentag als dreistellige Abkürzung (z.B. Mon für Montag)&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;eeee&#039;&amp;lt;/code&amp;gt;: Langname des Wochentags (z.B. Tuesday)&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;yy&#039;&amp;lt;/code&amp;gt;: Jahr als zweistellige Zahl (z.B. 16 für 2016)&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;yyyy&#039;&amp;lt;/code&amp;gt;: Jahr als vierstellige Zahl (z.B. 2016)&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;LF&#039;&amp;lt;/code&amp;gt;: Fügt einen Zeilenumbruch hinzu&lt;br /&gt;
&lt;br /&gt;
Beispiel: Der String &amp;lt;code&amp;gt;&#039;MMM\LF\yyyy&#039;&amp;lt;/code&amp;gt; zeigt &amp;lt;code&amp;gt;&#039;Jan&#039;&amp;lt;/code&amp;gt; in der ersten, und &amp;lt;code&amp;gt;&#039;2016&#039;&amp;lt;/code&amp;gt; in der zweiten Zeile. &amp;lt;code&amp;gt;&#039;MM.dd 2016&#039;&amp;lt;/code&amp;gt; wird zu &amp;lt;code&amp;gt;&#039;03.05 2016&#039;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Fadenkreuz-Cursor===&lt;br /&gt;
Der Fadenkreuz-Cursor zeigt die Momentanwerte, indem man ihn über die Graphen bewegt. In Desktop-Browsern reicht einfaches Bewegen des Maus. Unter iOS und Android kann der Cursor wird der Cursor durch einfaches Tippen auf die neue Position bewegt.&lt;br /&gt;
&lt;br /&gt;
Mit dem Attribut &#039;&#039;&#039;data-cursorgroup&#039;&#039;&#039; können Graphen gruppiert werden. Am Cursor werden dann die Momentanwerte aller Graphen gleichzeitig angezeigt, die die selbe Zahl besitzen, sobald man die Maus über einen aus der Gruppe bewegt.&lt;br /&gt;
&lt;br /&gt;
===Legende===&lt;br /&gt;
Mit dem Attribut &#039;&#039;&#039;data-legendpos&#039;&#039;&#039; kann die Position der Legende innerhalb des Diagramms festgelegt werden. Die Position wird mit einem Array, bestehend aus zwei Werten im Format &amp;lt;code&amp;gt;&#039;[&amp;quot;&amp;lt;horizontal&amp;gt;&amp;quot;,&amp;quot;&amp;lt;vertikal&amp;gt;&amp;quot;]&#039;&amp;lt;/code&amp;gt; angegeben. Für die horizontale Positionierung sind &amp;lt;code&amp;gt;&#039;left&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;right&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;before&#039;&amp;lt;/code&amp;gt;, und &amp;lt;code&amp;gt;&#039;behind&#039;&amp;lt;/code&amp;gt;, die vertikale Positionierung &amp;lt;code&amp;gt;&#039;top&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;bottom&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;above&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;below&#039;&amp;lt;/code&amp;gt; erlaubt (der Unterschied zwischen &amp;lt;code&amp;gt;&#039;left&#039;&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;&#039;before&#039;&amp;lt;/code&amp;gt; liegt darin, dass im zweiten Fall die Legende nicht in den Zeichenbereich gesetzt wird sondern vor das ganze Chart (entsprechend für &amp;lt;code&amp;gt;&#039;after&#039;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&#039;above&#039;&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;&#039;below&#039;&amp;lt;/code&amp;gt;). Alternativ können auch Zahlen verwendet werden, die die Position in Prozent angeben. Durch verschieben mit der Maus oder durch verschieben mit dem Finger oder Stift auf Touch Devices kann die Legende auch an eine andere Position verschoben werden.&lt;br /&gt;
&lt;br /&gt;
Wenn die Legende eingeblendet ist, kann mittels Klick auf einen Legendeneintrag der zugehörige Graph ein- und ausgeblendet werden.&lt;br /&gt;
&lt;br /&gt;
===3-dimensionale Drehung===&lt;br /&gt;
&#039;&#039;&#039;data-ddd&#039;&#039;&#039; ermöglicht, den Graphen 3-dimensional zu drehen. Als Wert wird ein Array mit den 3 Winkeln für x, y und z erwartet, wobei z selbst bisher nicht unterstützt wird.&lt;br /&gt;
&lt;br /&gt;
Beispiel: &amp;lt;code&amp;gt;data-ddd=&#039;[&amp;quot;40&amp;quot;,&amp;quot;60&amp;quot;,&amp;quot;0&amp;quot;]&#039;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Wenn der 3D Modus aktiv ist (&#039;&#039;&#039;data-ddd&#039;&#039;&#039; gesetzt) sind 2 zusätzliche Parameter verfügbar um das Aussehen der Graphen zu beeinflussen. &#039;&#039;&#039;data-dddspace&#039;&#039;&#039; gibt an, wie viele pixel der Raum zwischen den einzelnen in z-Richtung hintereinander angeordneten Graphen betragen soll.&lt;br /&gt;
&#039;&#039;&#039;data-dddwidth&#039;&#039;&#039; legt fest, wie viele pixel die einzelnen Graphen tief (oder dick) sein sollen.&lt;br /&gt;
&lt;br /&gt;
Wenn das Array angegeben wird, erscheinen zwei zusätzliche Buttons im Diagramm, mit denen die Drehung in X- und Y-Richtung verändert werden kann.&lt;br /&gt;
&lt;br /&gt;
===Diagrammtitel===&lt;br /&gt;
Mit dem Attribut &#039;&#039;&#039;data-title&#039;&#039;&#039; kann dem Diagramm, ähnlich wie in FHEM-SVG-Plots, ein Titel hinzugefügt werden.&lt;br /&gt;
&lt;br /&gt;
Folgende Platzhalter werden unterstützt:&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;min1&#039;&amp;lt;/code&amp;gt;: Minimaler Y-Wert des ersten Graphs&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;max1&#039;&amp;lt;/code&amp;gt;: Maximaler Y-Wert des ersten Graphs&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;avg1&#039;&amp;lt;/code&amp;gt;: Mittlerer Y-Wert des ersten Graphs&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;cnt1&#039;&amp;lt;/code&amp;gt;: Anzahl der dargestellten Einzelwerte im ersten Graph&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;currval1&#039;&amp;lt;/code&amp;gt;: Letzter, bzw. aktuellster Y-Wert des ersten Graphs&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;mindate1&#039;&amp;lt;/code&amp;gt;: Niedrigster Wert auf der X-Achse des ersten Graphs&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;maxdate1&#039;&amp;lt;/code&amp;gt;: Höchster Wert auf der X-Achse vom ersten Graphs&lt;br /&gt;
*&amp;lt;code&amp;gt;&#039;currdate1&#039;&amp;lt;/code&amp;gt;: Letzter, bzw. aktuellster Wert auf der X-Achse des ersten Graphs&lt;br /&gt;
&lt;br /&gt;
Durch Einsetzen einer anderen Zahl statt &#039;1&#039; können auch die Werte der anderen Graphen angezeigt werden. Das Weglassen der Zahl bewirkt, dass der jeweils zutreffende Wert automatisch ermittelt wird. Bedeutet: &amp;lt;code&amp;gt;max&amp;lt;/code&amp;gt; führt dazu, dass der höchste Wert aller angezeigter Graphen verwendet wird.&lt;br /&gt;
Zusätzlich ist es möglich durch &amp;quot;$eval(&amp;lt;regexp&amp;gt;)&amp;quot; regular Expressions auszuwerten (also z.B. Berechnungen durchzuführen). In &amp;lt;regexp&amp;gt; können auch &amp;quot;$data()&amp;quot; vorkommen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: &amp;lt;code&amp;gt;data-title=&amp;quot;Klima Wohnzimmer Average: $eval(parseInt($data{avg1}*10)/10)°C / Max: $eval(parseInt($data{max1}*10)/10)°C&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Buttons im Diagramm===&lt;br /&gt;
Es gibt mehrere Buttons, mit denen sich die Anzeige des Diagramms verändern lässt. &amp;lt;code&amp;gt;&amp;lt;-&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;-&amp;gt;&amp;lt;/code&amp;gt; bewegen die Graphen nach links und rechts. &amp;lt;code&amp;gt;+&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; zoomen die Anzeige. &amp;lt;code&amp;gt;legend&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;cursor&amp;lt;/code&amp;gt; schalten die zugehörigen Anzeigen ein und aus. Falls der 3D Modus eingeschaltet ist, gibt es Buttons zum Drehen der Darstellung um die X- und Y-Achse. Falls &amp;lt;code&amp;gt;data-timeranges&amp;lt;/code&amp;gt; gesetzt ist, wird ein Pulldown Menü dargestellt, welches die Auswahl von dort definierten Zeiträumen für die X-Achse erlaubt.&lt;br /&gt;
&lt;br /&gt;
==Beispiele==&lt;br /&gt;
===Einfaches Diagramm===&lt;br /&gt;
Das Beispiel zeigt ein einfaches Diagramm mit 4 unterschiedlich formatierten Graphen, Legende und Momentanwerten am Fadenkreuz-Cursor.&lt;br /&gt;
&lt;br /&gt;
[[File:Chart_tabletUI.png]]&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;chart&amp;quot;&lt;br /&gt;
	data-logdevice=&#039;[&amp;quot;Log.Garden&amp;quot;,&amp;quot;Log.Garden&amp;quot;,&amp;quot;Log.Garden&amp;quot;,&amp;quot;Log.Predicted&amp;quot;]&#039;&lt;br /&gt;
	data-columnspec=&#039;[&amp;quot;4:Garden.T:15:&amp;quot;,&amp;quot;10:Garden.T:0:delta-h&amp;quot;,&amp;quot;10:Garden.T:0:delta-d&amp;quot;,&amp;quot;4:predicted.*:15:&amp;quot;]&#039;&lt;br /&gt;
	data-style=&#039;[&amp;quot;ftui l0fill&amp;quot;,&amp;quot;ftui l1fill&amp;quot;,&amp;quot;ftui l2&amp;quot;,&amp;quot;ftui l3dot&amp;quot;]&#039;&lt;br /&gt;
	data-ptype=&#039;[&amp;quot;lines&amp;quot;,&amp;quot;histeps&amp;quot;,&amp;quot;histeps&amp;quot;,&amp;quot;cubic&amp;quot;]&#039;&lt;br /&gt;
	data-uaxis=&#039;[&amp;quot;primary&amp;quot;,&amp;quot;secondary&amp;quot;,&amp;quot;secondary&amp;quot;,&amp;quot;primary&amp;quot;]&#039;&lt;br /&gt;
	data-legend=&#039;[&amp;quot;Temperature&amp;quot;,&amp;quot;Rain/hour&amp;quot;,&amp;quot;Rain/day&amp;quot;,&amp;quot;Predicted Temp.&amp;quot;]&#039;&lt;br /&gt;
	data-yunit=&amp;quot;°C&amp;quot;&lt;br /&gt;
	data-ytext=&amp;quot;Temperature&amp;quot;&lt;br /&gt;
	data-minvalue=&amp;quot;auto&amp;quot;&lt;br /&gt;
	data-maxvalue=&amp;quot;auto&amp;quot;&lt;br /&gt;
	data-yunit_sec=&amp;quot;mm&amp;quot;&lt;br /&gt;
	data-ytext_sec=&amp;quot;Rain (mm)&amp;quot;&lt;br /&gt;
	data-height=&amp;quot;250&amp;quot;&lt;br /&gt;
	data-yticks=&amp;quot;auto&amp;quot;&lt;br /&gt;
	data-minvalue_sec=&amp;quot;auto&amp;quot;&lt;br /&gt;
	data-maxvalue_sec=&amp;quot;auto&amp;quot;&lt;br /&gt;
	data-nofulldays=&amp;quot;true&amp;quot;&lt;br /&gt;
	data-daysago_start=&amp;quot;2013-08-13T00:00:00&amp;quot;&lt;br /&gt;
	data-daysago_end=&amp;quot;2013-08-14T00:00:00&amp;quot;&lt;br /&gt;
	data-cursorgroup=&amp;quot;1&amp;quot;&lt;br /&gt;
	data-scrollgroup=&amp;quot;1&amp;quot;&lt;br /&gt;
	data-xticks=&amp;quot;auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===7-Tage-Wettervorhersage mit Proplanta===&lt;br /&gt;
In diesem Beispiel wird gezeigt, wie die Vorhersagewerte von [[PROPLANTA]] in einem Diagramm dargestellt werden können. Da die Werte nicht in einer Datenbank oder einem FileLog vorliegen, müssen sie über [[LogProxy]] verarbeitet werden. Dafür sind einige Vorbereitungen in FHEM nötig.&lt;br /&gt;
&lt;br /&gt;
[[File:FTUI_Widget_Chart-fc-Proplanta.png|941px]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.&#039;&#039;&#039; Ein LogProxy-Device muss vorhanden sein:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define myLogProxy logProxy&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2.&#039;&#039;&#039; In der Datei &amp;lt;code&amp;gt;99_myUtils.pm&amp;lt;/code&amp;gt; muss folgende Routine hinzugefügt werden, die die Daten bereitstellt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
#---------------------------------------&lt;br /&gt;
# Proplanta LogProxy-Funktion&lt;br /&gt;
#---------------------------------------&lt;br /&gt;
sub logProxy_proplanta2Plot($$$$;$$) {&lt;br /&gt;
	my ($device, $fcValue, $from, $to, $fcHour, $expMode) = @_;&lt;br /&gt;
    my $regex;&lt;br /&gt;
    my @rl;&lt;br /&gt;
    &lt;br /&gt;
	return undef if(!$device);&lt;br /&gt;
    &lt;br /&gt;
    if($fcValue =~ s/_$//) {&lt;br /&gt;
        $regex = &amp;quot;^fc[\\d]+_&amp;quot;.$fcValue.&amp;quot;[\\d]{2}\$&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    else {&lt;br /&gt;
        $regex = &amp;quot;^fc[\\d]+_&amp;quot;.$fcValue.&amp;quot;\$&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    $fcHour = 12 if(!defined $fcHour);&lt;br /&gt;
    $expMode = &amp;quot;point&amp;quot; if(!defined $expMode);&lt;br /&gt;
&lt;br /&gt;
	if( defined($defs{$device}) ) {&lt;br /&gt;
		if( $defs{$device}{TYPE} eq &amp;quot;PROPLANTA&amp;quot; ) {&lt;br /&gt;
            @rl = sort{&lt;br /&gt;
                my ($an) = ($a =~ m/fc(\d+)_.*/);&lt;br /&gt;
                my ($bn) = ($b =~ m/fc(\d+)_.*/);&lt;br /&gt;
                $an &amp;lt;=&amp;gt; $bn or $a cmp $b;&lt;br /&gt;
                }( grep /${regex}/,keys %{$defs{$device}{READINGS}} );&lt;br /&gt;
			return undef if( !@rl );&lt;br /&gt;
		} else {&lt;br /&gt;
			Log3 undef, 2, &amp;quot;logProxy_proplanta2Plot: $device is not a PROPLANTA device&amp;quot;;&lt;br /&gt;
			return undef;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	my $fromsec = SVG_time_to_sec($from);&lt;br /&gt;
	my $tosec   = SVG_time_to_sec($to);&lt;br /&gt;
	my $sec = $fromsec;&lt;br /&gt;
	my ($h,$fcDay,$mday,$mon,$year);&lt;br /&gt;
	my $timestamp;&lt;br /&gt;
    &lt;br /&gt;
	my $reading;&lt;br /&gt;
	my $value;&lt;br /&gt;
	my $prev_value;&lt;br /&gt;
	my $min = 999999;&lt;br /&gt;
	my $max = -999999;&lt;br /&gt;
	my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
	# while not end of plot range reached&lt;br /&gt;
	while(($sec &amp;lt; $tosec) &amp;amp;&amp;amp; @rl) {&lt;br /&gt;
		#remember previous value for start of plot range&lt;br /&gt;
		$prev_value = $value;&lt;br /&gt;
&lt;br /&gt;
		$reading = shift @rl;&lt;br /&gt;
        ($fcDay) = $reading =~ m/^fc(\d+).*/;&lt;br /&gt;
   		$h = ($reading =~ m/.*(\d\d)$/)?$1:$fcHour;&lt;br /&gt;
		$value = ReadingsVal($device,$reading,undef);&lt;br /&gt;
        &lt;br /&gt;
		($mday,$mon,$year) = split(&#039;\.&#039;,ReadingsVal($device,&amp;quot;fc&amp;quot;.$fcDay.&amp;quot;_date&amp;quot;,undef));&lt;br /&gt;
		$timestamp = sprintf(&amp;quot;%04d-%02d-%02d_%02d:%02d:%02d&amp;quot;, $year, $mon, $mday, $h, 0, 0);&lt;br /&gt;
		$sec = SVG_time_to_sec($timestamp);&lt;br /&gt;
        &lt;br /&gt;
		# skip all values before start of plot range&lt;br /&gt;
		next if( SVG_time_to_sec($timestamp) &amp;lt; $fromsec );&lt;br /&gt;
&lt;br /&gt;
		# add first value at start of plot range&lt;br /&gt;
		if( !$ret &amp;amp;&amp;amp; $prev_value ) {&lt;br /&gt;
		  $min = $prev_value if( (looks_like_number($prev_value) &amp;amp;&amp;amp; ($prev_value &amp;lt; $min)) || ($prev_value lt $min) );&lt;br /&gt;
		  $max = $prev_value if( (looks_like_number($prev_value) &amp;amp;&amp;amp; ($prev_value &amp;gt; $max)) || ($prev_value gt $max) );&lt;br /&gt;
		  $ret .= &amp;quot;$from $prev_value\n&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		# done if after end of plot range&lt;br /&gt;
		last if($sec &amp;gt; $tosec);&lt;br /&gt;
&lt;br /&gt;
		$min = $value if( (looks_like_number($value) &amp;amp;&amp;amp; ($value &amp;lt; $min )) || ($value lt $min) );&lt;br /&gt;
		$max = $value if( (looks_like_number($value) &amp;amp;&amp;amp; ($value &amp;gt; $max )) || ($value gt $max) );&lt;br /&gt;
&lt;br /&gt;
		# add actual controll point&lt;br /&gt;
		$ret .= &amp;quot;$timestamp $value\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
#		Log 1, &amp;quot;$timestamp $value -0- $reading&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
    if(($sec &amp;lt; $tosec) &amp;amp;&amp;amp; !@rl &amp;amp;&amp;amp; ($expMode eq &amp;quot;day&amp;quot;)) {&lt;br /&gt;
    	$timestamp = sprintf(&amp;quot;%04d-%02d-%02d_%02d:%02d:%02d&amp;quot;, $year, $mon, $mday, 23, 59, 59);&lt;br /&gt;
    	if(SVG_time_to_sec($timestamp) &amp;lt; $tosec) {&lt;br /&gt;
        	$ret .= &amp;quot;$timestamp $value\n&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        else {&lt;br /&gt;
			$ret .= &amp;quot;$to $value\n&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    elsif(($sec &amp;gt; $tosec) &amp;amp;&amp;amp; ($expMode eq &amp;quot;day&amp;quot;)) {&lt;br /&gt;
       	$value = $prev_value + ($value - $prev_value)*(86400 + ($tosec - $sec))/86400;&lt;br /&gt;
       	$ret .= &amp;quot;$to $value\n&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
	return ($ret,$min,$max,$prev_value);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anschließend können die Daten im Chart-Widget angezeigt werden. Der Device-Name von Proplanta heißt hier im Beispiel &amp;lt;code&amp;gt;AU.xx.WE.Proplanta&amp;lt;/code&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;chart&amp;quot;&lt;br /&gt;
	 data-device=&amp;quot;AU.xx.WE.Proplanta&amp;quot;&lt;br /&gt;
	 data-logdevice=&#039;[&lt;br /&gt;
	 					&amp;quot;myLogProxy&amp;quot;,&lt;br /&gt;
	 					&amp;quot;myLogProxy&amp;quot;,&lt;br /&gt;
	 					&amp;quot;myLogProxy&amp;quot;&lt;br /&gt;
	 				]&#039;&lt;br /&gt;
	 data-columnspec=&#039;[&lt;br /&gt;
	 					&amp;quot;Func:logProxy_proplanta2Plot(\\x22AU.xx.WE.Proplanta\\x22,\\x22rain_\\x22,$from,$to,12,\\x22day\\x22)&amp;quot;,&lt;br /&gt;
	 					&amp;quot;Func:logProxy_proplanta2Plot(\\x22AU.xx.WE.Proplanta\\x22,\\x22chOfRain_\\x22,$from,$to,12,\\x22day\\x22)&amp;quot;,&lt;br /&gt;
	 					&amp;quot;Func:logProxy_proplanta2Plot(\\x22AU.xx.WE.Proplanta\\x22,\\x22cloud_\\x22,$from,$to,12,\\x22day\\x22)&amp;quot;&lt;br /&gt;
	 				]&#039;&lt;br /&gt;
	 data-style=&#039;[&lt;br /&gt;
	 					&amp;quot;ftui l6fill&amp;quot;,&lt;br /&gt;
	 					&amp;quot;ftui l5fill&amp;quot;,&lt;br /&gt;
	 					&amp;quot;ftui l1fill&amp;quot;&lt;br /&gt;
	 			]&#039;&lt;br /&gt;
	 data-ptype=&#039;[&lt;br /&gt;
	 					&amp;quot;steps&amp;quot;,&lt;br /&gt;
	 					&amp;quot;quadraticSmooth&amp;quot;,&lt;br /&gt;
	 					&amp;quot;quadraticSmooth&amp;quot;&lt;br /&gt;
	 			]&#039;&lt;br /&gt;
	 data-uaxis=&#039;[&lt;br /&gt;
	 					&amp;quot;primary&amp;quot;,&lt;br /&gt;
	 					&amp;quot;secondary&amp;quot;,&lt;br /&gt;
	 					&amp;quot;secondary&amp;quot;&lt;br /&gt;
	 			]&#039;&lt;br /&gt;
	 data-legend=&#039;[&lt;br /&gt;
	 					&amp;quot;Regen&amp;quot;,&lt;br /&gt;
	 					&amp;quot;Regenwahrscheinlichkeit&amp;quot;,&lt;br /&gt;
	 					&amp;quot;Wolken&amp;quot;&lt;br /&gt;
	 			]&#039;&lt;br /&gt;
	 data-yunit=&amp;quot;mm&amp;quot;&lt;br /&gt;
	 data-ytext=&amp;quot;Regen&amp;quot;&lt;br /&gt;
	 data-yunit_sec=&amp;quot;%&amp;quot;&lt;br /&gt;
	 data-ytext_sec=&amp;quot;Chance auf Regen / Wolken&amp;quot;&lt;br /&gt;
	 data-timeformat=&amp;quot;eeee&amp;quot;&lt;br /&gt;
	 data-minvalue=&amp;quot;auto&amp;quot;&lt;br /&gt;
	 data-maxvalue=&amp;quot;auto&amp;quot;&lt;br /&gt;
	 data-minvalue_sec=&amp;quot;auto&amp;quot;&lt;br /&gt;
	 data-maxvalue_sec=&amp;quot;auto&amp;quot;&lt;br /&gt;
	 data-daysago_start = &amp;quot;0&amp;quot;&lt;br /&gt;
	 data-daysago_end = &amp;quot;-7&amp;quot;&lt;br /&gt;
	 data-xticks=&amp;quot;1440&amp;quot;&lt;br /&gt;
	 data-yticks=&amp;quot;auto&amp;quot;&lt;br /&gt;
	 data-title=&amp;quot;7-Tage-Wettervorhersage&amp;quot;&lt;br /&gt;
	 data-showlegend=&amp;quot;true&amp;quot;&lt;br /&gt;
	 class=&amp;quot;nobuttons fullsize&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfreiche Links und Quellen zu diesem Beispiel:&#039;&#039;&#039;&lt;br /&gt;
*[[LogProxy|LogProxy im FHEM-Wiki]]&lt;br /&gt;
*{{Link2Forum|Topic=22967|Message=246973|LinkText=Stundengenaue Wettervorhersage (#1) im FHEM-Forum}}&lt;br /&gt;
*{{Link2Forum|Topic=22967|Message=334713|LinkText=Stundengenaue Wettervorhersage (#2) im FHEM-Forum}}&lt;br /&gt;
&lt;br /&gt;
===Darstellung der Wetter Icons im Diagramm===&lt;br /&gt;
&lt;br /&gt;
[[File:Wetterchart2.png]]&lt;br /&gt;
&lt;br /&gt;
Wie oben bereits beschrieben, gibt es beim Chart grundsätzlich die Möglichkeit, Icons, welche in Form von URLs in den Logs abgelegt sind oder welche per logProxy generiert werden, darzustellen. Die Icons werden auf genau dem gleichen Weg von FHEM gelesen, wie alle anderen Datenpunkte. Im Folgenden wird ein Beispiel gezeigt, mit dem die im Proplanta Modul als Readings abgelegten Icons per logProxy Funktion gelesen und in ein Chart eingebaut werden könnnen.&lt;br /&gt;
Da es beim Proplanta Modul für die ersten 7 Tage nicht das Reading &amp;lt;code&amp;gt;fc#_weatherIcon&amp;lt;/code&amp;gt; gibt, sondern mehrere Readings für unterschiedliche Tageszeiten wogegen für die zweiten 7 Tage ausschließlich das Reading &amp;lt;code&amp;gt;fc#_weatherIcon&amp;lt;/code&amp;gt; vorhanden ist, sollte per &amp;lt;code&amp;gt;attr device userReading&amp;lt;/code&amp;gt; mit folgendem Eintrag dafür gesorgt werden, dass für alle Tage ein Reading &amp;lt;code&amp;gt;fc#_weatherIcon&amp;lt;/code&amp;gt; vorhanden ist (alternativ könnten auch 2 Graphen gezeichnet werden, wobei der erste dann nur die ersten 7 Tage enthält und der zweite die letzen 7 Tage, will man nur die ersten 7 Tage darstellen braucht man das userReading nicht unbedingt).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
fc0_weatherIcon {ReadingsVal(&amp;quot;AU.xx.WE.Proplanta&amp;quot;,&amp;quot;fc0_weatherDayIcon&amp;quot;,&amp;quot;&amp;quot;);},&lt;br /&gt;
fc1_weatherIcon {ReadingsVal(&amp;quot;AU.xx.WE.Proplanta&amp;quot;,&amp;quot;fc1_weatherDayIcon&amp;quot;,&amp;quot;&amp;quot;);},&lt;br /&gt;
fc2_weatherIcon {ReadingsVal(&amp;quot;AU.xx.WE.Proplanta&amp;quot;,&amp;quot;fc2_weatherDayIcon&amp;quot;,&amp;quot;&amp;quot;);},&lt;br /&gt;
fc3_weatherIcon {ReadingsVal(&amp;quot;AU.xx.WE.Proplanta&amp;quot;,&amp;quot;fc3_weatherDayIcon&amp;quot;,&amp;quot;&amp;quot;);},&lt;br /&gt;
fc4_weatherIcon {ReadingsVal(&amp;quot;AU.xx.WE.Proplanta&amp;quot;,&amp;quot;fc4_weatherDayIcon&amp;quot;,&amp;quot;&amp;quot;);},&lt;br /&gt;
fc5_weatherIcon {ReadingsVal(&amp;quot;AU.xx.WE.Proplanta&amp;quot;,&amp;quot;fc5_weatherDayIcon&amp;quot;,&amp;quot;&amp;quot;);},&lt;br /&gt;
fc6_weatherIcon {ReadingsVal(&amp;quot;AU.xx.WE.Proplanta&amp;quot;,&amp;quot;fc6_weatherDayIcon&amp;quot;,&amp;quot;&amp;quot;);}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Um die Icons darzustellen muss ein zusätzlicher Graph definiert werden. Dieser nutzt neben der Columnspec, die die URLs abruft den Parameter &amp;lt;code&amp;gt;data-ptype=&amp;quot;icons:#&amp;quot;&amp;lt;/code&amp;gt; (# ist eine Zahl und steht für die Nummer, beginnend bei 0 des Graphen, welcher für die y-Position der Icons verwendet werden soll) und den Stil &amp;lt;code&amp;gt;sym&amp;lt;/code&amp;gt;. Der Wert für die Symbolgröße sollte z.B. durch eine zusätzliche Definition im File fhem-tablet-ui-user.css in der Form:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;css&amp;quot;&amp;gt;&lt;br /&gt;
/* icon lines */&lt;br /&gt;
.ftui.l99icon	{ stroke:#DDA400; stroke-width:48px; fill:none; }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
angepasst werden.&lt;br /&gt;
&lt;br /&gt;
Im folgenden ein Beispiel, welches eine Linie für die Maximale Tagestemperatur zeichnet und auf dieser Linie die Wetter Icons darstellt.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div data-type=&amp;quot;chart&amp;quot;&lt;br /&gt;
	 data-device=&amp;quot;AU.xx.WE.Proplanta&amp;quot;&lt;br /&gt;
	 data-logdevice=&#039;[&lt;br /&gt;
	 					&amp;quot;myLogProxy&amp;quot;,&lt;br /&gt;
	 					&amp;quot;myLogProxy&amp;quot;&lt;br /&gt;
	 				]&#039;&lt;br /&gt;
	 data-columnspec=&#039;[&lt;br /&gt;
	 					&amp;quot;Func:logProxy_proplanta2Plot(\\x22AU.xx.WE.Proplanta\\x22,\\x22weatherIcon\\x22,$from,$to,12,\\x22day\\x22)&amp;quot;,&lt;br /&gt;
	 					&amp;quot;Func:logProxy_proplanta2Plot(\\x22AU.xx.WE.Proplanta\\x22,\\x22tempMax\\x22,$from,$to,12,\\x22day\\x22)&amp;quot;&lt;br /&gt;
	 				]&#039;&lt;br /&gt;
	 data-style=&#039;[&lt;br /&gt;
	 					&amp;quot;ftui l99icon&amp;quot;,&lt;br /&gt;
	 					&amp;quot;ftui l1fill&amp;quot;&lt;br /&gt;
	 			]&#039;&lt;br /&gt;
	 data-ptype=&#039;[&lt;br /&gt;
	 					&amp;quot;icons:1&amp;quot;,&lt;br /&gt;
	 					&amp;quot;quadraticSmooth&amp;quot;&lt;br /&gt;
	 			]&#039;&lt;br /&gt;
	 data-uaxis=&#039;[&lt;br /&gt;
	 					&amp;quot;primary&amp;quot;,&lt;br /&gt;
	 					&amp;quot;primary&amp;quot;&lt;br /&gt;
	 			]&#039;&lt;br /&gt;
	 data-legend=&#039;[&lt;br /&gt;
	 					&amp;quot;Wetterbedingung&amp;quot;,&lt;br /&gt;
	 					&amp;quot;Max. Temperature&amp;quot;&lt;br /&gt;
	 			]&#039;&lt;br /&gt;
	 data-yunit=&amp;quot;°C&amp;quot;&lt;br /&gt;
	 data-ytext=&amp;quot;Temperature (°C)&amp;quot;&lt;br /&gt;
	 data-timeformat=&amp;quot;ee\LF\dd.MM&amp;quot;&lt;br /&gt;
	 data-minvalue=&amp;quot;auto&amp;quot;&lt;br /&gt;
	 data-maxvalue=&amp;quot;auto&amp;quot;&lt;br /&gt;
	 data-minvalue_sec=&amp;quot;auto&amp;quot;&lt;br /&gt;
	 data-maxvalue_sec=&amp;quot;auto&amp;quot;&lt;br /&gt;
	 data-daysago_start=&amp;quot;-1w&amp;quot;&lt;br /&gt;
	 data-y_margin=&amp;quot;20&amp;quot;&lt;br /&gt;
	 data-daysago_end=&amp;quot;-3w&amp;quot;&lt;br /&gt;
	 data-xticks=&amp;quot;1440&amp;quot;&lt;br /&gt;
	 data-yticks=&amp;quot;auto&amp;quot;&lt;br /&gt;
	 data-title=&amp;quot;14-Tage-Wettervorhersage&amp;quot;&lt;br /&gt;
	 data-showlegend=&amp;quot;true&amp;quot;&lt;br /&gt;
	 class=&amp;quot;nobuttons fullsize&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Kuchendiagramme===&lt;br /&gt;
In folgendem Beispiel wird gezeigt, wie man ein &amp;quot;Kuchendiagramm&amp;quot; darstellen kann.&lt;br /&gt;
&lt;br /&gt;
[[File:PieChart.png]]&lt;br /&gt;
&lt;br /&gt;
Ähnlich wie bei den Beispielen für die Wetter Darstellungen wird hierzu auch wieder logProxy benötigt. Zunächst muss die folgende zusätzliche Funktion in &amp;lt;code&amp;gt;99_myUtils.pm&amp;lt;/code&amp;gt; einfügen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
#---------------------------------------&lt;br /&gt;
# Funktion zum Erzeugen der Inputs für Kuchendiagramme&lt;br /&gt;
#---------------------------------------&lt;br /&gt;
sub logProxy_values2PieChart($$$$;$$) {&lt;br /&gt;
	my ($device, $reading, $angle_start, $angle_dif, $inner_rad, $show_text) = @_;&lt;br /&gt;
	Log3 undef, 1, &amp;quot;$device, $reading, $angle_start, $angle_dif, $inner_rad, $show_text\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
	use constant PI =&amp;gt; 4 * atan2(1,1);&lt;br /&gt;
	&lt;br /&gt;
	my $value=ReadingsVal($device,$reading,0);&lt;br /&gt;
&lt;br /&gt;
	my $angle_delta = $value/100*360;&lt;br /&gt;
	$angle_start = $angle_start/100*360;&lt;br /&gt;
	&lt;br /&gt;
	my $rad=10;&lt;br /&gt;
	my $irad=0;&lt;br /&gt;
	if ($inner_rad) {&lt;br /&gt;
		$irad = $rad*$inner_rad;&lt;br /&gt;
	}&lt;br /&gt;
	my $angle=$angle_start/360*2.0*PI;&lt;br /&gt;
	my $x=$irad*sin($angle);&lt;br /&gt;
	my $y=$irad*cos($angle);&lt;br /&gt;
	my $ret .= &amp;quot;;p &amp;quot;.$x.&amp;quot; &amp;quot;.$y.&amp;quot;\n&amp;quot;; # add segment at angle $angle&lt;br /&gt;
	&lt;br /&gt;
	for (my $i=$angle_start; $i&amp;lt;=$angle_start+$angle_delta; $i+=$angle_dif) {&lt;br /&gt;
		$angle = $i/360*2.0*PI;&lt;br /&gt;
		$x = $rad*sin($angle);&lt;br /&gt;
		$y = $rad*cos($angle);&lt;br /&gt;
		$ret .= &amp;quot;;p &amp;quot;.$x.&amp;quot; &amp;quot;.$y.&amp;quot;\n&amp;quot;; # add segment at angle $angle&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	$angle = ($angle_start+$angle_delta)/360*2.0*PI; # add last segment &lt;br /&gt;
	$ret .= &amp;quot;;p &amp;quot;.$rad*sin($angle).&amp;quot; &amp;quot;.$rad*cos($angle).&amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
	if ($inner_rad) {&lt;br /&gt;
		for (my $i=$angle_start; $i&amp;lt;$angle_start+$angle_delta; $i+=$angle_dif) {&lt;br /&gt;
			$angle = ($angle_start+$angle_start+$angle_delta-$i)/360*2.0*PI;&lt;br /&gt;
			$x = $irad*sin($angle);&lt;br /&gt;
			$y = $irad*cos($angle);&lt;br /&gt;
			$ret .= &amp;quot;;p &amp;quot;.$x.&amp;quot; &amp;quot;.$y.&amp;quot;\n&amp;quot;; # add segment at angle $angle&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	$angle = ($angle_start)/360*2.0*PI; # add last segment &lt;br /&gt;
	$ret .= &amp;quot;;p &amp;quot;.$irad*sin($angle).&amp;quot; &amp;quot;.$irad*cos($angle).&amp;quot;\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
	if ($show_text) { # show text values&lt;br /&gt;
		$x = ($rad+$irad)/2*sin((2*$angle_start+$angle_delta)/2/360*2.0*PI);&lt;br /&gt;
		$y = ($rad+$irad)/2*cos((2*$angle_start+$angle_delta)/2/360*2.0*PI);&lt;br /&gt;
&lt;br /&gt;
		$ret .= &amp;quot;;t &amp;quot;.$x.&amp;quot; &amp;quot;.$y.&amp;quot; middle &amp;quot;.$show_text.&amp;quot;:&amp;quot;.$value.&amp;quot;%\n&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	return($ret);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In FHEM braucht man Readings, welche eine Zahl enthalten, die als Prozentwert interpretiert wird. Für jeden Prozentwert (also für jedes Reading) generiert die o.a. Funktion nun den Chart Input für ein Kuchenstück und liefert diesen als Antwort auf das GET, welches das Chart Widget auslöst. Dazu braucht die Funktion folgende Parameter: (Name des FHEM Devices, Name des Readings, Start Winkel des Kuchenstücks (Mathematisch gegen den Uhrzeigersinn in Grad), Delta Winkel zum Zeichnen (legt fest in welchen Schritten der Teilkreis des Kuchenstücks gezeichnet wird), Skalierungsfaktor für inneren Ring wenn ein Ring gezeichtnet werden soll (0 bedeutet komplette Kuchenstücke), optionaler Text der ins Kuchenstück vor die Prozentzahl geschrieben wird).&lt;br /&gt;
Im Folgenden eine Beispielkonfiguration für die Darstellung als Kuchendiagramm, die Readings heißen hier dPer1 bis dPer4. Der Startwinkel wird duch Aufsummierung der jeweils vorher schon gezeichneten Kuchenstücke gebildet, dadurch entstehen aneinander hängende Stücke.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
[[Datei:[[Datei:Beispiel.jpg]]]]&amp;lt;div class=&amp;quot;normal noaxes nobuttons&amp;quot;&lt;br /&gt;
        data-type=&amp;quot;chart&amp;quot;&lt;br /&gt;
        data-logdevice=&#039;[&amp;quot;lp&amp;quot;]&#039;&lt;br /&gt;
		data-logfile=&amp;quot;CURRENT&amp;quot;&lt;br /&gt;
        data-columnspec=&#039;[&lt;br /&gt;
			&amp;quot;Func:logProxy_values2PieChart(\&amp;quot;dPer1\&amp;quot;,\&amp;quot;state\&amp;quot;,ReadingsVal(\&amp;quot;dPer4\&amp;quot;,\&amp;quot;state\&amp;quot;,0)+ReadingsVal(\&amp;quot;dPer3\&amp;quot;,\&amp;quot;state\&amp;quot;,0),5,0,\&amp;quot;first\&amp;quot;)&amp;quot;,&lt;br /&gt;
			&amp;quot;Func:logProxy_values2PieChart(\&amp;quot;dPer2\&amp;quot;,\&amp;quot;state\&amp;quot;,ReadingsVal(\&amp;quot;dPer4\&amp;quot;,\&amp;quot;state\&amp;quot;,0)+ReadingsVal(\&amp;quot;dPer3\&amp;quot;,\&amp;quot;state\&amp;quot;,0)+ReadingsVal(\&amp;quot;dPer1\&amp;quot;,\&amp;quot;state\&amp;quot;,0),5,0,\&amp;quot;second\&amp;quot;)&amp;quot;,&lt;br /&gt;
			&amp;quot;Func:logProxy_values2PieChart(\&amp;quot;dPer3\&amp;quot;,\&amp;quot;state\&amp;quot;,ReadingsVal(\&amp;quot;dPer4\&amp;quot;,\&amp;quot;state\&amp;quot;,0),5,0,\&amp;quot;third\&amp;quot;)&amp;quot;,&lt;br /&gt;
			&amp;quot;Func:logProxy_values2PieChart(\&amp;quot;dPer4\&amp;quot;,\&amp;quot;state\&amp;quot;,0,5,0,\&amp;quot;fourth\&amp;quot;)&amp;quot;&lt;br /&gt;
		]&#039;&lt;br /&gt;
        data-style=&#039;[&amp;quot;ftui l0fill&amp;quot;,&amp;quot;ftui l1fill&amp;quot;,&amp;quot;ftui l2fill&amp;quot;,&amp;quot;ftui l3fill&amp;quot;]&#039;&lt;br /&gt;
        data-ptype=&#039;[&amp;quot;lines&amp;quot;]&#039;&lt;br /&gt;
        data-uaxis=&#039;[&amp;quot;primary&amp;quot;]&#039;&lt;br /&gt;
        data-legend=&#039;[&amp;quot;First&amp;quot;,&amp;quot;Second&amp;quot;,&amp;quot;Third&amp;quot;,&amp;quot;Fourth&amp;quot;]&#039;&lt;br /&gt;
		data-legendpos=&#039;[&amp;quot;left&amp;quot;,&amp;quot;top&amp;quot;]&#039;&lt;br /&gt;
        data-yunit=&amp;quot;&amp;quot;&lt;br /&gt;
        data-height=&amp;quot;300&amp;quot;&lt;br /&gt;
        data-width=&amp;quot;300&amp;quot;&lt;br /&gt;
		data-ddd=&#039;[&amp;quot;-40&amp;quot;,&amp;quot;0&amp;quot;,&amp;quot;0&amp;quot;]&#039;&lt;br /&gt;
		data-dddspace=&#039;[&amp;quot;-10&amp;quot;]&#039;&lt;br /&gt;
		data-dddwidth=&#039;[&amp;quot;10&amp;quot;]&#039;&lt;br /&gt;
		data-showlegend=&amp;quot;true&amp;quot;&lt;br /&gt;
		data-legend_horiz=&amp;quot;true&amp;quot;&lt;br /&gt;
        data-xticks=&amp;quot;auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Fensterstatus offen/geschlossen===&lt;br /&gt;
Dieses Beispiel zeigt, wie ein Fensterkontakt, dessen Reading die Werte &amp;lt;code&amp;gt;closed&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;open&amp;lt;/code&amp;gt; einnimmt, als Graph gezeichnet werden kann. Technisch gesehen werden hier die Werte &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; gezeichnet, indem über das Attribut &#039;&#039;&#039;data-columnspec&#039;&#039;&#039; dem Zustand &amp;lt;code&amp;gt;open&amp;lt;/code&amp;gt; der Wert &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; und allen anderen Zuständen der Wert &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; zugeordnet wird. Über das Attribut &#039;&#039;&#039;data-yticks&#039;&#039;&#039; wird die Beschriftung an der Y-Achse (&amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;) gegen einen frei definierbaren Text ausgetauscht.&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;chart&amp;quot;&lt;br /&gt;
	data-device=&amp;quot;wz_fensterstatus&amp;quot;&lt;br /&gt;
	data-logdevice=&#039;[&amp;quot;myDbLog&amp;quot;]&#039;&lt;br /&gt;
	data-logfile=&#039;[&amp;quot;HISTORY&amp;quot;]&#039;&lt;br /&gt;
	data-columnspec=&#039;[&amp;quot;wz_fensterstatus:state:0::$val=($val=~\\x22open\\x22?1:0)&amp;quot;]&#039;&lt;br /&gt;
	data-style=&#039;[&amp;quot;ftui l4fill&amp;quot;]&#039;&lt;br /&gt;
	data-ptype=&#039;[&amp;quot;steps&amp;quot;]&#039;&lt;br /&gt;
	data-height=&amp;quot;290&amp;quot;&lt;br /&gt;
	data-yticks=&#039;[[0,&amp;quot;geschlossen&amp;quot;],[1,&amp;quot;offen&amp;quot;]]&#039;&lt;br /&gt;
	data-minvalue=&amp;quot;0&amp;quot;&lt;br /&gt;
	data-maxvalue=&amp;quot;1.1&amp;quot;&lt;br /&gt;
	data-nofulldays=&amp;quot;true&amp;quot;&lt;br /&gt;
	data-daysago_start=&amp;quot;1&amp;quot;&lt;br /&gt;
	data-daysago_end=&amp;quot;-1&amp;quot;&lt;br /&gt;
	data-cursorgroup=&amp;quot;1&amp;quot;&lt;br /&gt;
	data-scrollgroup=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Das Beispiel funktioniert nur mit DbLog. Falls Logfiles verwendet werden muss statt &#039;$val&#039; &#039;$fld[&#039;&#039;num&#039;&#039;]&#039; verwendet werden. Hierbei steht &#039;&#039;num&#039;&#039; für die Spalte (beginnend bei 0) in der die Daten stehen.&lt;br /&gt;
&lt;br /&gt;
==Links==&lt;br /&gt;
{{Link2Forum|Topic= 48450 |Message=401006|LinkText=Thread im FHEM-Forum}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:FHEM Tablet UI|Chart]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog&amp;diff=31058</id>
		<title>DbLog</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog&amp;diff=31058"/>
		<updated>2019-07-29T10:54:57Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Beispiel: Anlegen und Nutzung einer Mysql-Datenbank */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Mit der Zeit entstehen in FHEM recht umfangreiche Log-Daten für die verschiedensten konfigurierten Devices. Die übliche Einstiegs-[[Konfiguration]] sieht vor, dass die Logs als {{Link2CmdRef|Lang=de|Anker=FileLog|Label=FileLog}} gespeichert werden - je nach Einstellung in wenigen sehr großen oder vielen kleineren Dateien. Der Datei-basierte Zugriff ist allerdings nicht wirklick performant und kann schnell zum Flaschenhals werden (z.B. bei der Darstellung von Graphen über einen längeren Zeitraum).&lt;br /&gt;
&lt;br /&gt;
Alternativ kann FHEM die Log-Daten mittels {{Link2CmdRef|Lang=de|Anker=DbLog|Label=DbLog}} in einer Datenbank speichern. Diese kann lokal als einfache SQLite- oder als zentrale Server-Datenbank (s.u.) gestaltet sein. Schon eine lokale einfache SQLite-Datenbank ist in der Regel deutlich performanter als File-basierte Logs.&lt;br /&gt;
&lt;br /&gt;
Damit eine Datenbank-Nutzung möglich ist, müssen folgende Anpassungen gemacht werden:&lt;br /&gt;
# [[#Datenbank|Erstellen einer entsprechenden Datenbank]]&lt;br /&gt;
# [[#Datenbank-Anbindung mittels db.conf|Konfiguration der Datenbank-Anbindung in FHEM]]&lt;br /&gt;
# [[#Konfiguration als Device in fhem.cfg|Anpassen aller (oder einzelner) Konfigurationen von FileLog nach DbLog]]&lt;br /&gt;
# [[#Anpassen der gplot-Konfigurationen|Ggf. Anpassen der gplot-Konfigurationen]]&lt;br /&gt;
&lt;br /&gt;
;Hinweis:&lt;br /&gt;
Reporting und Management von DbLog-Datenbankinhalten kann mit dem Modul [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] stattfinden.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
=== Datenbank-Anbindung mittels db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MySQL-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration als Device ===&lt;br /&gt;
Das DbLog Device wird dann definiert mit&lt;br /&gt;
:&amp;lt;code&amp;gt;define &amp;lt;name&amp;gt; DbLog &amp;lt;configfilename&amp;gt; &amp;lt;regexp&amp;gt; &amp;lt;/code&amp;gt;&lt;br /&gt;
wobei &#039;&#039;&amp;lt;configfilename&amp;gt;&#039;&#039; dem Pfad zur zuvor angelegten db.conf entspricht.&lt;br /&gt;
Ein Beispiel hierfür wäre:&lt;br /&gt;
:&amp;lt;code&amp;gt;define logdb DbLog ./db.conf .*:.* &amp;lt;/code&amp;gt;&lt;br /&gt;
Die Angabe von &amp;lt;code&amp;gt;.*:.*&amp;lt;/code&amp;gt; bedeutet, dass sämtliche DeviceMessages (Messwerte, Batteriestatus, KeepAlives, etc.) in die Datenbank geschrieben werden. Dies führt u.U. dazu, dass die Datenbank auch mit vielen teils irrelevanten Werten gefüllt wird. Man kann daher die zu loggenden Werte einschränken, indem man genau angibt welche Werte übertragen werden sollen. Dies ist in [[#Finetuning des Loggings]] beschrieben.&lt;br /&gt;
&lt;br /&gt;
Unbedingt beachten: bei Verwendung des Moduls configdb wird die Konfigurationsdatei aus der &#039;&#039;&#039;&#039;&#039;Datenbank&#039;&#039;&#039;&#039;&#039; gelesen. Deshalb ist es erforderlich, das File mittels &amp;lt;code&amp;gt;configdb fileimport db.conf &amp;lt;/code&amp;gt; vorher zu importieren !&lt;br /&gt;
&lt;br /&gt;
=== Finetuning des Loggings ===&lt;br /&gt;
Bei der Konfiguration des Log-Devices werden die zu loggenden Daten definiert - in der einfachsten Form sieht das so aus: &amp;lt;code&amp;gt;define logdb DbLog ./db.conf .*:.* &amp;lt;/code&amp;gt;. Die Angabe von &amp;lt;code&amp;gt;.*:.*&amp;lt;/code&amp;gt; bedeutet, dass sämtliche DeviceMessages (Messwerte, Batteriestatus, KeepAlives, etc.) in die Datenbank geschrieben werden. Dies führt u.U. dazu, dass die Datenbank auch mit sehr vielen und teils nicht benötigten Werten gefüllt wird und schnell wächst. Die Datenbank ist zwar deutlich leistungsfähiger, was große Datenmengen angeht, Datensparsamkeit kann aber schnell sinnvoll werden...&lt;br /&gt;
&lt;br /&gt;
Um das Log-Aufkommen einzugrenzen gibt es mehrere Ansätze:&lt;br /&gt;
* Einschränkung über den &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Eintrag&lt;br /&gt;
* Einschränkung über DbLogExclude-Einträge der jeweiligen Devices&lt;br /&gt;
* Einschränkung über DbLogInclude-Einträge des jeweiligen Devices&lt;br /&gt;
* Ausschluß von Device/Reading-Kombinationen über das Attribut &amp;quot;excludeDevs&amp;quot;. Es können [https://fhem.de/commandref_DE.html#devspec devspec] verwendet werden. &lt;br /&gt;
&lt;br /&gt;
==== Einschränkung über den zentralen &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Eintrag ====&lt;br /&gt;
Man kann die zu loggenden Werte einschränken, indem man genau angibt welche Werte übertragen werden sollen. Die erste Wildcard, also das erste &amp;lt;code&amp;gt;.*&amp;lt;/code&amp;gt;, entspricht dem in FHEM verwendeten Device-Namen. Die zweite Wildcard entspricht dem vom Device ausgegebenen zu loggenden Wert. Separiert werden beiden Angaben durch einen Doppelpunkt. &lt;br /&gt;
&lt;br /&gt;
Ein Beispiel, um zwar alle definierten Devices zu erfassen, aber nur die Werte Temperatur, Ventilposition und Luftfeuchte in die Datenbank zu schreiben wäre:&lt;br /&gt;
:&amp;lt;code&amp;gt;define myDbLog DbLog ./db.conf .*:(temperature|valveposition|humidity).* &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einschränkung über die jeweiligen Devices ====&lt;br /&gt;
Man kann die zu loggenden Werte für einzelne Devices separat einschränken, ohne dies im zentralen define-Eintrag machen zu müssen. Dies kann interessant sein, wenn beispielsweise ein Device Fehlerwerte meldet, die uninteressant sind, oder es meldet unnötig häufig Werte - beides ist z.B. bei 1-wire-Temperatursensoren gerne der Fall.&lt;br /&gt;
&lt;br /&gt;
Um das einzuschränken gibt es 2 Stellparameter, die als Attribute direkt zum jeweiligen Device konfiguriert werden:&lt;br /&gt;
* DbLogExclude - definiert Werte, die nicht geloggt werden sollen&lt;br /&gt;
* DbLogInclude - definiert Werte, die geloggt werden sollen ( siehe attr DbLogSelectionMode )&lt;br /&gt;
* event-min-interval, event-on-change-reading und event-on-update-reading beeinflussen, wie häufig Werte geloggt werden (vgl. {{Link2CmdRef|Lang=de|Anker=event-on-update-reading}})&lt;br /&gt;
&lt;br /&gt;
Eine konkrete Konfiguration für einen sehr gesprächigen 1-wire-Temperatursensor könnte wie folgt aussehen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define EG_Balkon GPIO4 BUSMASTER&lt;br /&gt;
attr EG_Balkon DbLogExclude failures,T,85     # logge keine &amp;quot;failures&amp;quot;, &amp;quot;T&amp;quot;-Werte und &amp;quot;85&amp;quot;-Werte (default-Werte, wenn keine Temperatur gelesen werden kann)&lt;br /&gt;
attr EG_Balkon event-on-change-reading state  # logge nur, wenn sich ein Wert ändert (wenn sich die Temperatur nicht ändert, logge das nicht)&lt;br /&gt;
attr EG_Balkon event-min-interval state:900   # logge spätestens alle 900sec = 15min&lt;br /&gt;
attr EG_Balkon event-on-update-reading .*     # logge alle Werte, die aktualisiert werden&lt;br /&gt;
&lt;br /&gt;
attr &amp;lt;1-Wire-Device vom Typ OWTHERM oder OWSWITCH&amp;gt; DbLogExclude data.*      # verhindert das Logging der state-Eintragungen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine in diesem {{Link2Forum|Topic=33697|Message=264127}} vorgestellte Strategie zur Vermeidung unnötigen Loggings ist, dass bei der Definition von Devices durch das nachfolgende &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; automatisch ein DbLogExclude für alle Werte (.*) des Devices zugewiesen wird und dies nur bei Interesse an geloggten Werten gelöscht bzw. angepasst wird:&lt;br /&gt;
&amp;lt;code&amp;gt;define nDbLogExclude notify global:DEFINED.* attr $EVTPART1 DbLogExclude .*&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebenso ist es mittlerweile möglich, lediglich erwünschte Werte (Positiv-Liste) zu loggen und alle anderen zu verwerfen. Hierfür wird im LogDevice das attribut DbLogSelectionMode Include verwendet. Nun kann für jedes Device mit DbLogInclude &amp;lt;Reading1&amp;gt;,&amp;lt;Reading2&amp;gt;,... angegeben werden, welche Readings geloggt werden sollen. &lt;br /&gt;
Integriert ist ebenfalls ein &amp;quot;min-interval&amp;quot;, siehe {{Link2CmdRef}}.&lt;br /&gt;
&lt;br /&gt;
== Datenbank ==&lt;br /&gt;
Unterstützte Datenbanksysteme (Auswahl):&lt;br /&gt;
* Sqlite&lt;br /&gt;
* MySQL&lt;br /&gt;
* PostGreSql&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen zur Anlage von Tabellen und Indizes sind für jeden unterstützten Datenbanktyp im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation, oder hier zu finden: [https://svn.fhem.de/trac/browser/trunk/fhem/contrib/dblog/ Link]. Das MySQL-Skript (db_create_mysql.sql) legt eine neue Datenbank, das PostGres-Skript (db_create_postgresql.sql) ein neues Schema mit Namen &amp;quot;fhem&amp;quot; an.&lt;br /&gt;
&lt;br /&gt;
==== current ====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== history ====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
{{Todo|Ausbauen}}&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
== Anpassen der gplot-Konfigurationen ==&lt;br /&gt;
Die meisten gplot-Konfigurationen sind bisher lediglich auf FileLog-Konfigurationen ausgelegt. Deshalb müssen sie für die Verwendung mit DbLog angepasst werden. Glücklicherweise beschränkt sich dies auf die reinen FileLog-Zeilen - es müssen die DbLog-Äquivalente hinzugefügt werden. Die FileLog-Einträge müssen zwar nicht gelöscht werden, wenn man aber FileLog und DbLog parallel betreibt, sollte man getrennte gplot-Dateien für beide Logging-Typen haben um Auswertungsprobleme erkennen zu können.&lt;br /&gt;
&lt;br /&gt;
Für die fht.gplot Konfiguration sähe die Anpassung wie folgt aus (lediglich die vier DbLog-Zeilen wurden hinzugefügt):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2014-12-25 21:53: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 nomirror&lt;br /&gt;
set y2tics &lt;br /&gt;
set grid y2tics&lt;br /&gt;
set ylabel &amp;quot;Actuator/Window (%)&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Temperature in C&amp;quot;&lt;br /&gt;
set yrange 0:100&lt;br /&gt;
set y2range 5:25&lt;br /&gt;
&lt;br /&gt;
#FileLog 4:.measured-temp\x3a:0:&lt;br /&gt;
#FileLog 4:.actuator\x3a:0:int&lt;br /&gt;
#FileLog 4:.desired-temp::&lt;br /&gt;
#FileLog 4:.window\x3a::&lt;br /&gt;
&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.measured-temp:0:&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.actuator:0:int&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.desired-temp::&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.window::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Measured temperature&#039; ls l0 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Actuator (%)&#039; ls l1 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Desired Temperature&#039; ls l2 lw 1 with steps,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Window&#039; ls l3 lw 1 with steps&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Des Weiteren ist zu beachten: &lt;br /&gt;
&lt;br /&gt;
On-Off-Plots&lt;br /&gt;
&lt;br /&gt;
 EG_Bad:window:::$val=~s/(on|off)(\d*).*/$1eq&amp;quot;on&amp;quot;?1:0/eg&lt;br /&gt;
&lt;br /&gt;
unter Berücksichtigung von dim-Werten:&lt;br /&gt;
&lt;br /&gt;
 EG_WoZi_Licht:value:::$val=~s/(on|off)(\d*).*/$1eq&amp;quot;on&amp;quot;?1:($1eq&amp;quot;dim&amp;quot;?$2*0.01:0)/eg&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer SQLite-Datenbank ==&lt;br /&gt;
Im folgenden wird eine lokale SQLite-Datenbank auf einen Ubuntu-System angelegt (nach Quelle: [http://www.tatsch-it.de/fhem-dblog/ http://www.tatsch-it.de/fhem-dblog/])&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Installation von SQLite:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo aptitude install sqlite3 libdbi-perl libdbd-sqlite3-perl&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anlegen der SQLite-Datenbank fhem.db&#039;&#039; (öffnet auch direkt eine SQL-Kommandozeile):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 /opt/fhem/fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der geöffneten SQL-Kommandozeile eingeben:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE &#039;history&#039; (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
CREATE TABLE &#039;current&#039; (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
CREATE INDEX Search_Idx ON `history` (DEVICE, READING, TIMESTAMP);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Kommandozeile verlässt man mit &amp;lt;code&amp;gt;.exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anpassen des Besitzers und der Rechte der Datenbank-Datei:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo chown fhem /opt/fhem/fhem.db&lt;br /&gt;
sudo chmod 666 /opt/fhem/fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Datenbank-Anbindung des FHEM konfigurieren:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo nano /opt/fhem/db.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
Inhalt:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%dbconfig= (&lt;br /&gt;
  connection =&amp;gt; &amp;quot;SQLite:dbname=/opt/fhem/fhem.db&amp;quot;,&lt;br /&gt;
  user =&amp;gt; &amp;quot;&amp;quot;,&lt;br /&gt;
  password =&amp;gt; &amp;quot;&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Logging des FHEM auf die Datenbank konfigurieren:&#039;&#039; (hier sind nur die Anpassungen aufgeführt)&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo nano /opt/fhem/fhem.cfg&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
attr global userattr DbLogExclude ...  # erlaubt es einzelne Einträge nicht zu loggen&lt;br /&gt;
...&lt;br /&gt;
define logdb DbLog ./db.conf .*:.*     # logt alle(!) auflaufenden Events aller Konfigurationen&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Da durch diese &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Definition alle auflaufenden Events gelogt werden, müssen keine weiteren Anpassungen in der Konfiguration gemacht werden. Die FileLog-Einträge können bedenkenlos bestehen bleiben - dann wird in Datenbank und FileLog gelogt und man verliert keine Daten, falls etwas nicht klappt. Wenn alles wie geplant läuft, können die FileLog-Definitionen gelöscht werden (ebenso die Log-Dateien). Ebenso können die zu loggenden Daten später eingegrenzt werden (s. [[#Finetuning des Loggings]]).&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;FHEM neu starten:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo service fhem stop&lt;br /&gt;
sudo service fhem start&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Kontrollieren, ob Logs in die Datenbank geschrieben werden:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 /opt/fhem/fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der geöffneten SQL-Kommandozeile eingeben:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
select * from history order by TIMESTAMP;       # dies gibt alle(!) Logs chronologisch aus (kann nach längerem Betrieb recht lange dauern)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Kommandozeile verlässt man mit &amp;lt;code&amp;gt;.exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anpassung der glot-Dateien:&#039;&#039; siehe [[#Anpassen der gplot-Konfigurationen]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer Mysql-Datenbank ==&lt;br /&gt;
Hierfür gibt es eine [[DbLog-MySQL|extra Seite]], die die Unterschiede und Feinheiten zwischen den verschiedenen Versionen berücksichtigt&lt;br /&gt;
&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 apt-get update &amp;amp;&amp;amp; apt-get install mysql-server mysql-client libdbd-mysql libdbd-mysql-perl&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Installation sollte man aus Sicherheitsgründen ein Passwort für den mysql-root vergeben, wenn man nicht sogar ganz den Login verbietet.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der normale Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Zum Test mal mit mysql verbinden:&lt;br /&gt;
 # mysql -p -u root&lt;br /&gt;
 Enter password:&lt;br /&gt;
 mysql&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen (root Passwort wird abgefragt): &lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; show databases;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Integration von DBLog in eigene Module ==&lt;br /&gt;
=== Bereitstellung der UNITS ===&lt;br /&gt;
Mit der DbLog_splitFn kann der Modulautor selbst festlegen, wie die Events des Moduls in die Bestandteile Reading/Value/Unit zerlegt werden um ein korrektes Logging per DbLog zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
Dazu muss der Modulautor in der [[DevelopmentModuleIntro#X_Initialize|Initialize-Funktion]] eine &amp;lt;code&amp;gt;DbLog_splitFn&amp;lt;/code&amp;gt; bereitstellen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	...&lt;br /&gt;
	$hash-&amp;gt;{DbLog_splitFn}      = &amp;quot;X_DbLog_splitFn&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die genaue Aufrufsyntax und Funktionweise einer DbLog_split-Funktion findet man [[DevelopmentModuleIntro#X_DbLog_split|hier]].&lt;br /&gt;
&lt;br /&gt;
== Werte auslesen ==&lt;br /&gt;
Manchmal möchte man Daten aus den Logs abrufen ohne händisch in der Datenbank herumzuwühlen (s.u.). Dies ist insb. auch dann hilfreich, wenn man eigenen Funktionen, Notifys oder spezielle Plots entwirft, bei denen man auf Logdaten zugreifen möchte.&lt;br /&gt;
&lt;br /&gt;
Grundsätzlich beschrieben ist dies in der {{Link2CmdRef|Lang=de|Anker=DbLog}} und unterscheidet sich minimal (aber entscheidend) von der Struktur bei [[FileLog#Werte_auslesen|FileLogs]].&lt;br /&gt;
&lt;br /&gt;
Hier ein paar Beispiele, was man damit anstellen kann:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01 2016-10-03 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor vom 01.10.-03.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor von 8-16 Uhr am 01.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor:temperature&amp;lt;/code&amp;gt; nur die temperature Werte&lt;br /&gt;
* &amp;lt;code&amp;gt;{ ReadingsTimestamp(&amp;quot;meinSensor&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;0&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des aktuellen state des meinSensor&lt;br /&gt;
* &amp;lt;code&amp;gt;{ OldTimestamp(&amp;quot;meinSensor&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des letzten state des FHT_3a32&lt;br /&gt;
* &amp;lt;code&amp;gt;{ time_str2num(OldTimestamp(&amp;quot;meinSensor&amp;quot;)) }&amp;lt;/code&amp;gt; Timestamp in Sekunden des letzten state des meinSensor&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
&#039;&#039;&#039;Öffnen der DB unter Linux:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(Es werden Schreibrechte benötigt,ohne kann man die DB zwar öffnen, aber nichts machen)&lt;br /&gt;
 sudo sqlite3 fhem.db&lt;br /&gt;
Dadurch öffnet sich ein SQL-Konsole, auf der alle weiteren Befehle ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schliessen der DB:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .exit&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfe anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .help&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Tabellen anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .tables&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Das Schema der DB anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(vgl. oben [[DbLog#Datenbanken]] und [[DbLog#Beispiel: Anlegen und Nutzung einer SQLite-Datenbank]])&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .schema&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Eintäge anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Die Einträge liegen alle in der Tabelle &amp;quot;History&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ganz wichtig&#039;&#039;&#039; ist immer das &amp;quot;;&amp;quot; am Ende Zeile (bei allen Kommandos, die nicht mit einem &amp;quot;.&amp;quot; anfangen). Wenn es vergessen wurde zeigt die Konsole solange neue Zeilen bis ein &amp;quot;;&amp;quot; eingegeben wird. So kann ein Befehl auch bequem über mehrere Zeilen geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY;&lt;br /&gt;
&lt;br /&gt;
Dies kann sehr lange dauern und kann ggf. mit &amp;lt;code&amp;gt;STRG-C&amp;lt;/code&amp;gt; abgebrochen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;where&amp;lt;/code&amp;gt;-Statements werden Strings in einfache Anführungsstriche gesetzt, Zahlen nicht.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LÖSCHEN aller Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Löschen kann nicht rückgängig gemacht werden!! Also IMMER erst die entsprechenden SELECT-Statements solange verfeinern bis wirklich nur die gewünschten Einträge angezeigt werden. Dann das &amp;lt;code&amp;gt;select *&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; ersetzen.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; delete from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank reparieren ==&lt;br /&gt;
Es kann immer wieder mal vorkommen, dass Datenbanken Fehler enthalten. Das muss im Alltag garnicht auffallen und auch nicht immer schlimm enden. Wenn man auf der SQL-Konsole aber bspw. eine Meldung &amp;lt;code&amp;gt;Error: database disk image is malformed&amp;lt;/code&amp;gt; erhält, sollte man ein Reparatur vornehmen.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
Die folgenden Schritte beschreiben, wie man eine SQLite-DB reparieren kann (Quelle: [http://techblog.dorogin.com/2011/05/sqliteexception-database-disk-image-is.html]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
DB öffnen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Integritäts-Check durchführen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sqlite&amp;gt; pragma integrity_check;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kommt hier ein &amp;quot;ok&amp;quot; ist die DB gesund. Ansonsten erscheint etwas wie&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
*** in database main ***&lt;br /&gt;
On tree page 118786 cell 1: Rowid 75 out of order (previous was 816660)&lt;br /&gt;
On tree page 118786 cell 4: Rowid 815704 out of order (previous was 816727)&lt;br /&gt;
Corruption detected in cell 0 on page 118786&lt;br /&gt;
Multiple uses for byte 132 of page 118786&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Datenbank-Dump erstellen (Export gesamten DB in die Datei &amp;quot;dump_all_20160516_1043.sql&amp;quot;) und DB verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .mode insert&lt;br /&gt;
sqlite&amp;gt; .output dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; .dump&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Neue Datenbank erstellen und den Dump einlesen, Integritäts-Check machen und verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem-neu.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .read dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; pragma integrity_check;&lt;br /&gt;
ok&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Spätestens jetzt FHEM stoppen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem stop&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Alte DB sichern und neue aktivieren:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo mv fhem.db fhem.db.sv_20160516&lt;br /&gt;
sudo mv fhem-neu.db fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Kontrollieren, dass die neue DB die gleichen Rechte wie die alte DB hat (und ggf. korrigieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 root root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
~/fhem$ sudo chown fhem:root fhem.db&lt;br /&gt;
&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 fhem root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
FHEM wieder starten (und natürlich kontrollieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem start&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. Diese geschieht mittels des Userreadings, welches man vorteilshafterweise mit im DbLog-device anlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;attr myDbLog userReadings DbFileSize:reduceLogState.* { (split(&#039; &#039;,`du -m fhem.db`))[0] }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels dieses Attributs wird die Grösse der .db-Datei immer nach dem Ausführen des ReduceLog in das Reading &amp;quot;DbFileSize&amp;quot; in ganzzahligen MByte abgelegt.&lt;br /&gt;
&lt;br /&gt;
Basierend auf diesem Reading können dann weitere Aktionen, beispielsweise ein Plot, erstellt werden.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist für SQLite verwendbar. Zur Ermittlung der DB-Größe andere DB-Typen (aber auch für SQLite nutzbar) kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Performance-Optimierung ==&lt;br /&gt;
Auch eine Datenbank kann mit der Zeit langsamer werden. Dies hängt von mehreren Faktoren ab:&lt;br /&gt;
* Menge der gelogten Daten (zB. &amp;gt; 4-5 GB)&lt;br /&gt;
* Eingesetzte Hardware (zB. langsame SD-Karte vs. schnelle SSD)&lt;br /&gt;
* Eingesetztes Datenbanksystem (zB. SQLite, MySQL)&lt;br /&gt;
* Komplexität der Abfragen (zB. für aufwändige Graphen oder Berechnungen)&lt;br /&gt;
&lt;br /&gt;
Diese Punkte sollen im folgenden diskutiert werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komplexität der Abfragen ===&lt;br /&gt;
Dies ist kein Problem der Datenbank, sondern rein der Abfrage. Dem entsprechend muss die Optimierung auch in der Abfrage oder im Skript gesucht werden. Dies ist nicht Ziel dieses Abschnittes und wird hier nicht weiter behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetztes Datenbanksystem ===&lt;br /&gt;
Welches Datenbanksystem eingesetzt wird (zB. SQLite oder MySQL) hat auf die Performance der Datenbank gar keinen so großen Einfluss, wie vielleicht zuerst gedacht. Selbst SQLite kann problemlos Datenbanken mit etlichen GB Größe performant verarbeiten. Der Flaschenhals ist hier viel mehr die darunter liegende Hardware (s.u.).&lt;br /&gt;
&lt;br /&gt;
Die Performance der Datenbank an sich, kann aber durch verschiedene Maßnahmen verbessert werden:&lt;br /&gt;
* Pflegemaßnahmen bzgl. der Daten&lt;br /&gt;
* &#039;&#039;&#039;Erstellung von Indizes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Menge der Daten und Pflegemaßnahmen bzgl. der Daten ====&lt;br /&gt;
Die Menge der geloggten Daten hat natürlich Einfluss auf die Geschwindigkeit von Abfragen - je mehr Daten vorhanden sind, desto mehr Daten müssen auch durchforstet werden um eine Abfrage zu bedienen. Die Reduzierung der geloggten Datenmenge hat also direkten Einfluss auf die Größe und damit auch die Geschwindigkeit der Datenbank. Die Menge der zu loggenden Daten lässt sich an zwei Stellen einschränken:&lt;br /&gt;
* bei der Definition jedes Devices (s. Kapitel oben)&lt;br /&gt;
* bei der Festlegung des fhem-weiten Log-Levels (s. [[Loglevel]])&lt;br /&gt;
&lt;br /&gt;
Die Menge der bereits geloggten Daten kann zB. mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] verringert und optimiert werden, bspw.&lt;br /&gt;
* löschen unnötiger Daten&lt;br /&gt;
* vacuum der Datenbank&lt;br /&gt;
&lt;br /&gt;
Insgesamt haben diese Maßnahmen aber nur einen eingeschränkten Effekt auf die Performance der DB. Deutlich effektiver ist die Erstellung eines Index (s.u.).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Erstellung von Indizes ====&lt;br /&gt;
Die Erstellung von Indizes hat mit Abstand den größten Einfluss auf die Performance einer Datenbank (auf unveränderter Hardware). Extrem zusammengefasst ist ein Index eine extrem optimiertes Nachschlageverzeichnis für einen bestimmten Typ Daten (ein Index wie im Buch halt). Eine wunderbare Einführung in Indizes bietet [[https://use-the-index-luke.com/de|https://use-the-index-luke.com]].&lt;br /&gt;
&lt;br /&gt;
In fhem sind Indizes sogar sehr einfach einzurichten da die Datenbank-Nutzung sehr stark vorgegeben ist. Nahezu jede Abfrage folgt dem Schema &#039;&#039;Device -&amp;gt; Reading -&amp;gt; Datum -&amp;gt; Wert&#039;&#039;. Ein Index kann genau diese Abfrage bedienen und beschleunigen. Ein Index nur über die Devices wäre ein erster Schritt, brächte aber noch keinen großen Gewinn (wie um Link oben gut beschrieben). Über die gesamten ersten drei Schritte erstellt (Device -&amp;gt; Reading -&amp;gt; Datum) bringt der Index aber sofort eine deutliche Geschwindigkeitssteigerung.&lt;br /&gt;
&lt;br /&gt;
Die Erstellung eines Index erfolgt direkt in der Datenbank (und nicht aus fhem heraus), hier am Beispiel einer SQLite-DB:&lt;br /&gt;
&lt;br /&gt;
Öffnen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; sudo sqlite3 fhem.db &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugen des Index auf der DB-Konsole (das Semikolon am Ende ist wichtig):&lt;br /&gt;
&amp;lt;pre&amp;gt; create index idx_device_reading_timestamp on history (device, reading, timestamp); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verlassen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; .exit &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzig zu berücksichtigen ist, dass dieser Index die Datenbank um bis zu 1/3 vergrößert. Er kann aber bei Bedarf auch wieder entfernt werden. Bei meiner 15 GB SQLite-Datei (auf einem Mac Mini mit SSD) hat dies ca. 15 min gedauert (den fhem hatte ich vorsichtshalber währenddessen deaktiviert).&lt;br /&gt;
&lt;br /&gt;
Sollte jemand spezielle Berechnungen oder Skripte ausführen, die nach einem anderen Abfrage-Schema arbeiten, könnte man dafür spezialisierte zusätzliche Indizes erstellen. Das sollte aber dann mit dem Wissen des obigen Links erarbeitet werden, da dann etwas mehr Hintergrundwissen sehr hilfreich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== DB-Backup ====&lt;br /&gt;
Ein anderer Aspekt, der eigentlich nichts mit der Performance der DB zu tun hat, ist der Einfluss aufs Backup. Wird bspw. ein Systembackup per RSYNC gemacht, muss bei SQLite immer die komplette ggf. riesige Datei gesynct werden - bei MySQL würden nur die veränderten DB-Elemente gesynct. Dies soll hier nicht weiter vertieft werden, sollte aber bei einer Gesamtstrategie bedacht werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetzte Hardware ===&lt;br /&gt;
fhem hat grundsätzlich sehr viele kleine Datenzugriffe, unabhängig davon ob FileLog oder DbLog eingesetzt wird. Deshalb ist der absolut größte Performance-Gewinn durch den Einsatz schneller Festplatten zu erreichen - ganz einfache Aussage: &#039;&#039;je schneller desto besser ;-)&#039;&#039;. Konkret bietet eine SSD mit mind. 250MB/s Datenzugriff eine ordentliche Basis für jedes datengestützte System, wie den fhem.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenmenge größer und die Abfragen komplexer werden, müssen natürlich irgendwann auch die Prozessorleistung und der Arbeitsspeicher mit wachsen. Aber auch an einem Raspi wird eine SSD deutlich performanter sein, als eine einfache SD-Karte oder eine klassische rotierende Festplatte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog&amp;diff=31057</id>
		<title>DbLog</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog&amp;diff=31057"/>
		<updated>2019-07-29T10:53:41Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Datenbank */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Mit der Zeit entstehen in FHEM recht umfangreiche Log-Daten für die verschiedensten konfigurierten Devices. Die übliche Einstiegs-[[Konfiguration]] sieht vor, dass die Logs als {{Link2CmdRef|Lang=de|Anker=FileLog|Label=FileLog}} gespeichert werden - je nach Einstellung in wenigen sehr großen oder vielen kleineren Dateien. Der Datei-basierte Zugriff ist allerdings nicht wirklick performant und kann schnell zum Flaschenhals werden (z.B. bei der Darstellung von Graphen über einen längeren Zeitraum).&lt;br /&gt;
&lt;br /&gt;
Alternativ kann FHEM die Log-Daten mittels {{Link2CmdRef|Lang=de|Anker=DbLog|Label=DbLog}} in einer Datenbank speichern. Diese kann lokal als einfache SQLite- oder als zentrale Server-Datenbank (s.u.) gestaltet sein. Schon eine lokale einfache SQLite-Datenbank ist in der Regel deutlich performanter als File-basierte Logs.&lt;br /&gt;
&lt;br /&gt;
Damit eine Datenbank-Nutzung möglich ist, müssen folgende Anpassungen gemacht werden:&lt;br /&gt;
# [[#Datenbank|Erstellen einer entsprechenden Datenbank]]&lt;br /&gt;
# [[#Datenbank-Anbindung mittels db.conf|Konfiguration der Datenbank-Anbindung in FHEM]]&lt;br /&gt;
# [[#Konfiguration als Device in fhem.cfg|Anpassen aller (oder einzelner) Konfigurationen von FileLog nach DbLog]]&lt;br /&gt;
# [[#Anpassen der gplot-Konfigurationen|Ggf. Anpassen der gplot-Konfigurationen]]&lt;br /&gt;
&lt;br /&gt;
;Hinweis:&lt;br /&gt;
Reporting und Management von DbLog-Datenbankinhalten kann mit dem Modul [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] stattfinden.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
=== Datenbank-Anbindung mittels db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MySQL-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration als Device ===&lt;br /&gt;
Das DbLog Device wird dann definiert mit&lt;br /&gt;
:&amp;lt;code&amp;gt;define &amp;lt;name&amp;gt; DbLog &amp;lt;configfilename&amp;gt; &amp;lt;regexp&amp;gt; &amp;lt;/code&amp;gt;&lt;br /&gt;
wobei &#039;&#039;&amp;lt;configfilename&amp;gt;&#039;&#039; dem Pfad zur zuvor angelegten db.conf entspricht.&lt;br /&gt;
Ein Beispiel hierfür wäre:&lt;br /&gt;
:&amp;lt;code&amp;gt;define logdb DbLog ./db.conf .*:.* &amp;lt;/code&amp;gt;&lt;br /&gt;
Die Angabe von &amp;lt;code&amp;gt;.*:.*&amp;lt;/code&amp;gt; bedeutet, dass sämtliche DeviceMessages (Messwerte, Batteriestatus, KeepAlives, etc.) in die Datenbank geschrieben werden. Dies führt u.U. dazu, dass die Datenbank auch mit vielen teils irrelevanten Werten gefüllt wird. Man kann daher die zu loggenden Werte einschränken, indem man genau angibt welche Werte übertragen werden sollen. Dies ist in [[#Finetuning des Loggings]] beschrieben.&lt;br /&gt;
&lt;br /&gt;
Unbedingt beachten: bei Verwendung des Moduls configdb wird die Konfigurationsdatei aus der &#039;&#039;&#039;&#039;&#039;Datenbank&#039;&#039;&#039;&#039;&#039; gelesen. Deshalb ist es erforderlich, das File mittels &amp;lt;code&amp;gt;configdb fileimport db.conf &amp;lt;/code&amp;gt; vorher zu importieren !&lt;br /&gt;
&lt;br /&gt;
=== Finetuning des Loggings ===&lt;br /&gt;
Bei der Konfiguration des Log-Devices werden die zu loggenden Daten definiert - in der einfachsten Form sieht das so aus: &amp;lt;code&amp;gt;define logdb DbLog ./db.conf .*:.* &amp;lt;/code&amp;gt;. Die Angabe von &amp;lt;code&amp;gt;.*:.*&amp;lt;/code&amp;gt; bedeutet, dass sämtliche DeviceMessages (Messwerte, Batteriestatus, KeepAlives, etc.) in die Datenbank geschrieben werden. Dies führt u.U. dazu, dass die Datenbank auch mit sehr vielen und teils nicht benötigten Werten gefüllt wird und schnell wächst. Die Datenbank ist zwar deutlich leistungsfähiger, was große Datenmengen angeht, Datensparsamkeit kann aber schnell sinnvoll werden...&lt;br /&gt;
&lt;br /&gt;
Um das Log-Aufkommen einzugrenzen gibt es mehrere Ansätze:&lt;br /&gt;
* Einschränkung über den &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Eintrag&lt;br /&gt;
* Einschränkung über DbLogExclude-Einträge der jeweiligen Devices&lt;br /&gt;
* Einschränkung über DbLogInclude-Einträge des jeweiligen Devices&lt;br /&gt;
* Ausschluß von Device/Reading-Kombinationen über das Attribut &amp;quot;excludeDevs&amp;quot;. Es können [https://fhem.de/commandref_DE.html#devspec devspec] verwendet werden. &lt;br /&gt;
&lt;br /&gt;
==== Einschränkung über den zentralen &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Eintrag ====&lt;br /&gt;
Man kann die zu loggenden Werte einschränken, indem man genau angibt welche Werte übertragen werden sollen. Die erste Wildcard, also das erste &amp;lt;code&amp;gt;.*&amp;lt;/code&amp;gt;, entspricht dem in FHEM verwendeten Device-Namen. Die zweite Wildcard entspricht dem vom Device ausgegebenen zu loggenden Wert. Separiert werden beiden Angaben durch einen Doppelpunkt. &lt;br /&gt;
&lt;br /&gt;
Ein Beispiel, um zwar alle definierten Devices zu erfassen, aber nur die Werte Temperatur, Ventilposition und Luftfeuchte in die Datenbank zu schreiben wäre:&lt;br /&gt;
:&amp;lt;code&amp;gt;define myDbLog DbLog ./db.conf .*:(temperature|valveposition|humidity).* &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einschränkung über die jeweiligen Devices ====&lt;br /&gt;
Man kann die zu loggenden Werte für einzelne Devices separat einschränken, ohne dies im zentralen define-Eintrag machen zu müssen. Dies kann interessant sein, wenn beispielsweise ein Device Fehlerwerte meldet, die uninteressant sind, oder es meldet unnötig häufig Werte - beides ist z.B. bei 1-wire-Temperatursensoren gerne der Fall.&lt;br /&gt;
&lt;br /&gt;
Um das einzuschränken gibt es 2 Stellparameter, die als Attribute direkt zum jeweiligen Device konfiguriert werden:&lt;br /&gt;
* DbLogExclude - definiert Werte, die nicht geloggt werden sollen&lt;br /&gt;
* DbLogInclude - definiert Werte, die geloggt werden sollen ( siehe attr DbLogSelectionMode )&lt;br /&gt;
* event-min-interval, event-on-change-reading und event-on-update-reading beeinflussen, wie häufig Werte geloggt werden (vgl. {{Link2CmdRef|Lang=de|Anker=event-on-update-reading}})&lt;br /&gt;
&lt;br /&gt;
Eine konkrete Konfiguration für einen sehr gesprächigen 1-wire-Temperatursensor könnte wie folgt aussehen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define EG_Balkon GPIO4 BUSMASTER&lt;br /&gt;
attr EG_Balkon DbLogExclude failures,T,85     # logge keine &amp;quot;failures&amp;quot;, &amp;quot;T&amp;quot;-Werte und &amp;quot;85&amp;quot;-Werte (default-Werte, wenn keine Temperatur gelesen werden kann)&lt;br /&gt;
attr EG_Balkon event-on-change-reading state  # logge nur, wenn sich ein Wert ändert (wenn sich die Temperatur nicht ändert, logge das nicht)&lt;br /&gt;
attr EG_Balkon event-min-interval state:900   # logge spätestens alle 900sec = 15min&lt;br /&gt;
attr EG_Balkon event-on-update-reading .*     # logge alle Werte, die aktualisiert werden&lt;br /&gt;
&lt;br /&gt;
attr &amp;lt;1-Wire-Device vom Typ OWTHERM oder OWSWITCH&amp;gt; DbLogExclude data.*      # verhindert das Logging der state-Eintragungen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine in diesem {{Link2Forum|Topic=33697|Message=264127}} vorgestellte Strategie zur Vermeidung unnötigen Loggings ist, dass bei der Definition von Devices durch das nachfolgende &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; automatisch ein DbLogExclude für alle Werte (.*) des Devices zugewiesen wird und dies nur bei Interesse an geloggten Werten gelöscht bzw. angepasst wird:&lt;br /&gt;
&amp;lt;code&amp;gt;define nDbLogExclude notify global:DEFINED.* attr $EVTPART1 DbLogExclude .*&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebenso ist es mittlerweile möglich, lediglich erwünschte Werte (Positiv-Liste) zu loggen und alle anderen zu verwerfen. Hierfür wird im LogDevice das attribut DbLogSelectionMode Include verwendet. Nun kann für jedes Device mit DbLogInclude &amp;lt;Reading1&amp;gt;,&amp;lt;Reading2&amp;gt;,... angegeben werden, welche Readings geloggt werden sollen. &lt;br /&gt;
Integriert ist ebenfalls ein &amp;quot;min-interval&amp;quot;, siehe {{Link2CmdRef}}.&lt;br /&gt;
&lt;br /&gt;
== Datenbank ==&lt;br /&gt;
Unterstützte Datenbanksysteme (Auswahl):&lt;br /&gt;
* Sqlite&lt;br /&gt;
* MySQL&lt;br /&gt;
* PostGreSql&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen zur Anlage von Tabellen und Indizes sind für jeden unterstützten Datenbanktyp im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation, oder hier zu finden: [https://svn.fhem.de/trac/browser/trunk/fhem/contrib/dblog/ Link]. Das MySQL-Skript (db_create_mysql.sql) legt eine neue Datenbank, das PostGres-Skript (db_create_postgresql.sql) ein neues Schema mit Namen &amp;quot;fhem&amp;quot; an.&lt;br /&gt;
&lt;br /&gt;
==== current ====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== history ====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
{{Todo|Ausbauen}}&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
== Anpassen der gplot-Konfigurationen ==&lt;br /&gt;
Die meisten gplot-Konfigurationen sind bisher lediglich auf FileLog-Konfigurationen ausgelegt. Deshalb müssen sie für die Verwendung mit DbLog angepasst werden. Glücklicherweise beschränkt sich dies auf die reinen FileLog-Zeilen - es müssen die DbLog-Äquivalente hinzugefügt werden. Die FileLog-Einträge müssen zwar nicht gelöscht werden, wenn man aber FileLog und DbLog parallel betreibt, sollte man getrennte gplot-Dateien für beide Logging-Typen haben um Auswertungsprobleme erkennen zu können.&lt;br /&gt;
&lt;br /&gt;
Für die fht.gplot Konfiguration sähe die Anpassung wie folgt aus (lediglich die vier DbLog-Zeilen wurden hinzugefügt):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2014-12-25 21:53: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 nomirror&lt;br /&gt;
set y2tics &lt;br /&gt;
set grid y2tics&lt;br /&gt;
set ylabel &amp;quot;Actuator/Window (%)&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Temperature in C&amp;quot;&lt;br /&gt;
set yrange 0:100&lt;br /&gt;
set y2range 5:25&lt;br /&gt;
&lt;br /&gt;
#FileLog 4:.measured-temp\x3a:0:&lt;br /&gt;
#FileLog 4:.actuator\x3a:0:int&lt;br /&gt;
#FileLog 4:.desired-temp::&lt;br /&gt;
#FileLog 4:.window\x3a::&lt;br /&gt;
&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.measured-temp:0:&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.actuator:0:int&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.desired-temp::&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.window::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Measured temperature&#039; ls l0 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Actuator (%)&#039; ls l1 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Desired Temperature&#039; ls l2 lw 1 with steps,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Window&#039; ls l3 lw 1 with steps&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Des Weiteren ist zu beachten: &lt;br /&gt;
&lt;br /&gt;
On-Off-Plots&lt;br /&gt;
&lt;br /&gt;
 EG_Bad:window:::$val=~s/(on|off)(\d*).*/$1eq&amp;quot;on&amp;quot;?1:0/eg&lt;br /&gt;
&lt;br /&gt;
unter Berücksichtigung von dim-Werten:&lt;br /&gt;
&lt;br /&gt;
 EG_WoZi_Licht:value:::$val=~s/(on|off)(\d*).*/$1eq&amp;quot;on&amp;quot;?1:($1eq&amp;quot;dim&amp;quot;?$2*0.01:0)/eg&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer SQLite-Datenbank ==&lt;br /&gt;
Im folgenden wird eine lokale SQLite-Datenbank auf einen Ubuntu-System angelegt (nach Quelle: [http://www.tatsch-it.de/fhem-dblog/ http://www.tatsch-it.de/fhem-dblog/])&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Installation von SQLite:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo aptitude install sqlite3 libdbi-perl libdbd-sqlite3-perl&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anlegen der SQLite-Datenbank fhem.db&#039;&#039; (öffnet auch direkt eine SQL-Kommandozeile):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 /opt/fhem/fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der geöffneten SQL-Kommandozeile eingeben:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE &#039;history&#039; (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
CREATE TABLE &#039;current&#039; (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
CREATE INDEX Search_Idx ON `history` (DEVICE, READING, TIMESTAMP);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Kommandozeile verlässt man mit &amp;lt;code&amp;gt;.exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anpassen des Besitzers und der Rechte der Datenbank-Datei:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo chown fhem /opt/fhem/fhem.db&lt;br /&gt;
sudo chmod 666 /opt/fhem/fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Datenbank-Anbindung des FHEM konfigurieren:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo nano /opt/fhem/db.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
Inhalt:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%dbconfig= (&lt;br /&gt;
  connection =&amp;gt; &amp;quot;SQLite:dbname=/opt/fhem/fhem.db&amp;quot;,&lt;br /&gt;
  user =&amp;gt; &amp;quot;&amp;quot;,&lt;br /&gt;
  password =&amp;gt; &amp;quot;&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Logging des FHEM auf die Datenbank konfigurieren:&#039;&#039; (hier sind nur die Anpassungen aufgeführt)&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo nano /opt/fhem/fhem.cfg&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
attr global userattr DbLogExclude ...  # erlaubt es einzelne Einträge nicht zu loggen&lt;br /&gt;
...&lt;br /&gt;
define logdb DbLog ./db.conf .*:.*     # logt alle(!) auflaufenden Events aller Konfigurationen&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Da durch diese &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Definition alle auflaufenden Events gelogt werden, müssen keine weiteren Anpassungen in der Konfiguration gemacht werden. Die FileLog-Einträge können bedenkenlos bestehen bleiben - dann wird in Datenbank und FileLog gelogt und man verliert keine Daten, falls etwas nicht klappt. Wenn alles wie geplant läuft, können die FileLog-Definitionen gelöscht werden (ebenso die Log-Dateien). Ebenso können die zu loggenden Daten später eingegrenzt werden (s. [[#Finetuning des Loggings]]).&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;FHEM neu starten:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo service fhem stop&lt;br /&gt;
sudo service fhem start&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Kontrollieren, ob Logs in die Datenbank geschrieben werden:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 /opt/fhem/fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der geöffneten SQL-Kommandozeile eingeben:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
select * from history order by TIMESTAMP;       # dies gibt alle(!) Logs chronologisch aus (kann nach längerem Betrieb recht lange dauern)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Kommandozeile verlässt man mit &amp;lt;code&amp;gt;.exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anpassung der glot-Dateien:&#039;&#039; siehe [[#Anpassen der gplot-Konfigurationen]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer Mysql-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 apt-get update &amp;amp;&amp;amp; apt-get install mysql-server mysql-client libdbd-mysql libdbd-mysql-perl&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Installation sollte man aus Sicherheitsgründen ein Passwort für den mysql-root vergeben, wenn man nicht sogar ganz den Login verbietet.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der normale Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Zum Test mal mit mysql verbinden:&lt;br /&gt;
 # mysql -p -u root&lt;br /&gt;
 Enter password:&lt;br /&gt;
 mysql&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen (root Passwort wird abgefragt): &lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; show databases;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Integration von DBLog in eigene Module ==&lt;br /&gt;
=== Bereitstellung der UNITS ===&lt;br /&gt;
Mit der DbLog_splitFn kann der Modulautor selbst festlegen, wie die Events des Moduls in die Bestandteile Reading/Value/Unit zerlegt werden um ein korrektes Logging per DbLog zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
Dazu muss der Modulautor in der [[DevelopmentModuleIntro#X_Initialize|Initialize-Funktion]] eine &amp;lt;code&amp;gt;DbLog_splitFn&amp;lt;/code&amp;gt; bereitstellen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	...&lt;br /&gt;
	$hash-&amp;gt;{DbLog_splitFn}      = &amp;quot;X_DbLog_splitFn&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die genaue Aufrufsyntax und Funktionweise einer DbLog_split-Funktion findet man [[DevelopmentModuleIntro#X_DbLog_split|hier]].&lt;br /&gt;
&lt;br /&gt;
== Werte auslesen ==&lt;br /&gt;
Manchmal möchte man Daten aus den Logs abrufen ohne händisch in der Datenbank herumzuwühlen (s.u.). Dies ist insb. auch dann hilfreich, wenn man eigenen Funktionen, Notifys oder spezielle Plots entwirft, bei denen man auf Logdaten zugreifen möchte.&lt;br /&gt;
&lt;br /&gt;
Grundsätzlich beschrieben ist dies in der {{Link2CmdRef|Lang=de|Anker=DbLog}} und unterscheidet sich minimal (aber entscheidend) von der Struktur bei [[FileLog#Werte_auslesen|FileLogs]].&lt;br /&gt;
&lt;br /&gt;
Hier ein paar Beispiele, was man damit anstellen kann:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01 2016-10-03 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor vom 01.10.-03.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor von 8-16 Uhr am 01.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor:temperature&amp;lt;/code&amp;gt; nur die temperature Werte&lt;br /&gt;
* &amp;lt;code&amp;gt;{ ReadingsTimestamp(&amp;quot;meinSensor&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;0&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des aktuellen state des meinSensor&lt;br /&gt;
* &amp;lt;code&amp;gt;{ OldTimestamp(&amp;quot;meinSensor&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des letzten state des FHT_3a32&lt;br /&gt;
* &amp;lt;code&amp;gt;{ time_str2num(OldTimestamp(&amp;quot;meinSensor&amp;quot;)) }&amp;lt;/code&amp;gt; Timestamp in Sekunden des letzten state des meinSensor&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
&#039;&#039;&#039;Öffnen der DB unter Linux:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(Es werden Schreibrechte benötigt,ohne kann man die DB zwar öffnen, aber nichts machen)&lt;br /&gt;
 sudo sqlite3 fhem.db&lt;br /&gt;
Dadurch öffnet sich ein SQL-Konsole, auf der alle weiteren Befehle ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schliessen der DB:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .exit&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfe anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .help&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Tabellen anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .tables&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Das Schema der DB anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(vgl. oben [[DbLog#Datenbanken]] und [[DbLog#Beispiel: Anlegen und Nutzung einer SQLite-Datenbank]])&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .schema&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Eintäge anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Die Einträge liegen alle in der Tabelle &amp;quot;History&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ganz wichtig&#039;&#039;&#039; ist immer das &amp;quot;;&amp;quot; am Ende Zeile (bei allen Kommandos, die nicht mit einem &amp;quot;.&amp;quot; anfangen). Wenn es vergessen wurde zeigt die Konsole solange neue Zeilen bis ein &amp;quot;;&amp;quot; eingegeben wird. So kann ein Befehl auch bequem über mehrere Zeilen geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY;&lt;br /&gt;
&lt;br /&gt;
Dies kann sehr lange dauern und kann ggf. mit &amp;lt;code&amp;gt;STRG-C&amp;lt;/code&amp;gt; abgebrochen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;where&amp;lt;/code&amp;gt;-Statements werden Strings in einfache Anführungsstriche gesetzt, Zahlen nicht.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LÖSCHEN aller Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Löschen kann nicht rückgängig gemacht werden!! Also IMMER erst die entsprechenden SELECT-Statements solange verfeinern bis wirklich nur die gewünschten Einträge angezeigt werden. Dann das &amp;lt;code&amp;gt;select *&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; ersetzen.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; delete from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank reparieren ==&lt;br /&gt;
Es kann immer wieder mal vorkommen, dass Datenbanken Fehler enthalten. Das muss im Alltag garnicht auffallen und auch nicht immer schlimm enden. Wenn man auf der SQL-Konsole aber bspw. eine Meldung &amp;lt;code&amp;gt;Error: database disk image is malformed&amp;lt;/code&amp;gt; erhält, sollte man ein Reparatur vornehmen.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
Die folgenden Schritte beschreiben, wie man eine SQLite-DB reparieren kann (Quelle: [http://techblog.dorogin.com/2011/05/sqliteexception-database-disk-image-is.html]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
DB öffnen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Integritäts-Check durchführen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sqlite&amp;gt; pragma integrity_check;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kommt hier ein &amp;quot;ok&amp;quot; ist die DB gesund. Ansonsten erscheint etwas wie&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
*** in database main ***&lt;br /&gt;
On tree page 118786 cell 1: Rowid 75 out of order (previous was 816660)&lt;br /&gt;
On tree page 118786 cell 4: Rowid 815704 out of order (previous was 816727)&lt;br /&gt;
Corruption detected in cell 0 on page 118786&lt;br /&gt;
Multiple uses for byte 132 of page 118786&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Datenbank-Dump erstellen (Export gesamten DB in die Datei &amp;quot;dump_all_20160516_1043.sql&amp;quot;) und DB verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .mode insert&lt;br /&gt;
sqlite&amp;gt; .output dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; .dump&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Neue Datenbank erstellen und den Dump einlesen, Integritäts-Check machen und verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem-neu.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .read dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; pragma integrity_check;&lt;br /&gt;
ok&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Spätestens jetzt FHEM stoppen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem stop&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Alte DB sichern und neue aktivieren:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo mv fhem.db fhem.db.sv_20160516&lt;br /&gt;
sudo mv fhem-neu.db fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Kontrollieren, dass die neue DB die gleichen Rechte wie die alte DB hat (und ggf. korrigieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 root root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
~/fhem$ sudo chown fhem:root fhem.db&lt;br /&gt;
&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 fhem root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
FHEM wieder starten (und natürlich kontrollieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem start&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. Diese geschieht mittels des Userreadings, welches man vorteilshafterweise mit im DbLog-device anlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;attr myDbLog userReadings DbFileSize:reduceLogState.* { (split(&#039; &#039;,`du -m fhem.db`))[0] }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels dieses Attributs wird die Grösse der .db-Datei immer nach dem Ausführen des ReduceLog in das Reading &amp;quot;DbFileSize&amp;quot; in ganzzahligen MByte abgelegt.&lt;br /&gt;
&lt;br /&gt;
Basierend auf diesem Reading können dann weitere Aktionen, beispielsweise ein Plot, erstellt werden.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist für SQLite verwendbar. Zur Ermittlung der DB-Größe andere DB-Typen (aber auch für SQLite nutzbar) kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Performance-Optimierung ==&lt;br /&gt;
Auch eine Datenbank kann mit der Zeit langsamer werden. Dies hängt von mehreren Faktoren ab:&lt;br /&gt;
* Menge der gelogten Daten (zB. &amp;gt; 4-5 GB)&lt;br /&gt;
* Eingesetzte Hardware (zB. langsame SD-Karte vs. schnelle SSD)&lt;br /&gt;
* Eingesetztes Datenbanksystem (zB. SQLite, MySQL)&lt;br /&gt;
* Komplexität der Abfragen (zB. für aufwändige Graphen oder Berechnungen)&lt;br /&gt;
&lt;br /&gt;
Diese Punkte sollen im folgenden diskutiert werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komplexität der Abfragen ===&lt;br /&gt;
Dies ist kein Problem der Datenbank, sondern rein der Abfrage. Dem entsprechend muss die Optimierung auch in der Abfrage oder im Skript gesucht werden. Dies ist nicht Ziel dieses Abschnittes und wird hier nicht weiter behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetztes Datenbanksystem ===&lt;br /&gt;
Welches Datenbanksystem eingesetzt wird (zB. SQLite oder MySQL) hat auf die Performance der Datenbank gar keinen so großen Einfluss, wie vielleicht zuerst gedacht. Selbst SQLite kann problemlos Datenbanken mit etlichen GB Größe performant verarbeiten. Der Flaschenhals ist hier viel mehr die darunter liegende Hardware (s.u.).&lt;br /&gt;
&lt;br /&gt;
Die Performance der Datenbank an sich, kann aber durch verschiedene Maßnahmen verbessert werden:&lt;br /&gt;
* Pflegemaßnahmen bzgl. der Daten&lt;br /&gt;
* &#039;&#039;&#039;Erstellung von Indizes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Menge der Daten und Pflegemaßnahmen bzgl. der Daten ====&lt;br /&gt;
Die Menge der geloggten Daten hat natürlich Einfluss auf die Geschwindigkeit von Abfragen - je mehr Daten vorhanden sind, desto mehr Daten müssen auch durchforstet werden um eine Abfrage zu bedienen. Die Reduzierung der geloggten Datenmenge hat also direkten Einfluss auf die Größe und damit auch die Geschwindigkeit der Datenbank. Die Menge der zu loggenden Daten lässt sich an zwei Stellen einschränken:&lt;br /&gt;
* bei der Definition jedes Devices (s. Kapitel oben)&lt;br /&gt;
* bei der Festlegung des fhem-weiten Log-Levels (s. [[Loglevel]])&lt;br /&gt;
&lt;br /&gt;
Die Menge der bereits geloggten Daten kann zB. mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] verringert und optimiert werden, bspw.&lt;br /&gt;
* löschen unnötiger Daten&lt;br /&gt;
* vacuum der Datenbank&lt;br /&gt;
&lt;br /&gt;
Insgesamt haben diese Maßnahmen aber nur einen eingeschränkten Effekt auf die Performance der DB. Deutlich effektiver ist die Erstellung eines Index (s.u.).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Erstellung von Indizes ====&lt;br /&gt;
Die Erstellung von Indizes hat mit Abstand den größten Einfluss auf die Performance einer Datenbank (auf unveränderter Hardware). Extrem zusammengefasst ist ein Index eine extrem optimiertes Nachschlageverzeichnis für einen bestimmten Typ Daten (ein Index wie im Buch halt). Eine wunderbare Einführung in Indizes bietet [[https://use-the-index-luke.com/de|https://use-the-index-luke.com]].&lt;br /&gt;
&lt;br /&gt;
In fhem sind Indizes sogar sehr einfach einzurichten da die Datenbank-Nutzung sehr stark vorgegeben ist. Nahezu jede Abfrage folgt dem Schema &#039;&#039;Device -&amp;gt; Reading -&amp;gt; Datum -&amp;gt; Wert&#039;&#039;. Ein Index kann genau diese Abfrage bedienen und beschleunigen. Ein Index nur über die Devices wäre ein erster Schritt, brächte aber noch keinen großen Gewinn (wie um Link oben gut beschrieben). Über die gesamten ersten drei Schritte erstellt (Device -&amp;gt; Reading -&amp;gt; Datum) bringt der Index aber sofort eine deutliche Geschwindigkeitssteigerung.&lt;br /&gt;
&lt;br /&gt;
Die Erstellung eines Index erfolgt direkt in der Datenbank (und nicht aus fhem heraus), hier am Beispiel einer SQLite-DB:&lt;br /&gt;
&lt;br /&gt;
Öffnen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; sudo sqlite3 fhem.db &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugen des Index auf der DB-Konsole (das Semikolon am Ende ist wichtig):&lt;br /&gt;
&amp;lt;pre&amp;gt; create index idx_device_reading_timestamp on history (device, reading, timestamp); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verlassen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; .exit &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzig zu berücksichtigen ist, dass dieser Index die Datenbank um bis zu 1/3 vergrößert. Er kann aber bei Bedarf auch wieder entfernt werden. Bei meiner 15 GB SQLite-Datei (auf einem Mac Mini mit SSD) hat dies ca. 15 min gedauert (den fhem hatte ich vorsichtshalber währenddessen deaktiviert).&lt;br /&gt;
&lt;br /&gt;
Sollte jemand spezielle Berechnungen oder Skripte ausführen, die nach einem anderen Abfrage-Schema arbeiten, könnte man dafür spezialisierte zusätzliche Indizes erstellen. Das sollte aber dann mit dem Wissen des obigen Links erarbeitet werden, da dann etwas mehr Hintergrundwissen sehr hilfreich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== DB-Backup ====&lt;br /&gt;
Ein anderer Aspekt, der eigentlich nichts mit der Performance der DB zu tun hat, ist der Einfluss aufs Backup. Wird bspw. ein Systembackup per RSYNC gemacht, muss bei SQLite immer die komplette ggf. riesige Datei gesynct werden - bei MySQL würden nur die veränderten DB-Elemente gesynct. Dies soll hier nicht weiter vertieft werden, sollte aber bei einer Gesamtstrategie bedacht werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetzte Hardware ===&lt;br /&gt;
fhem hat grundsätzlich sehr viele kleine Datenzugriffe, unabhängig davon ob FileLog oder DbLog eingesetzt wird. Deshalb ist der absolut größte Performance-Gewinn durch den Einsatz schneller Festplatten zu erreichen - ganz einfache Aussage: &#039;&#039;je schneller desto besser ;-)&#039;&#039;. Konkret bietet eine SSD mit mind. 250MB/s Datenzugriff eine ordentliche Basis für jedes datengestützte System, wie den fhem.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenmenge größer und die Abfragen komplexer werden, müssen natürlich irgendwann auch die Prozessorleistung und der Arbeitsspeicher mit wachsen. Aber auch an einem Raspi wird eine SSD deutlich performanter sein, als eine einfache SD-Karte oder eine klassische rotierende Festplatte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog&amp;diff=31056</id>
		<title>DbLog</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog&amp;diff=31056"/>
		<updated>2019-07-29T10:50:39Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Datenbank */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Mit der Zeit entstehen in FHEM recht umfangreiche Log-Daten für die verschiedensten konfigurierten Devices. Die übliche Einstiegs-[[Konfiguration]] sieht vor, dass die Logs als {{Link2CmdRef|Lang=de|Anker=FileLog|Label=FileLog}} gespeichert werden - je nach Einstellung in wenigen sehr großen oder vielen kleineren Dateien. Der Datei-basierte Zugriff ist allerdings nicht wirklick performant und kann schnell zum Flaschenhals werden (z.B. bei der Darstellung von Graphen über einen längeren Zeitraum).&lt;br /&gt;
&lt;br /&gt;
Alternativ kann FHEM die Log-Daten mittels {{Link2CmdRef|Lang=de|Anker=DbLog|Label=DbLog}} in einer Datenbank speichern. Diese kann lokal als einfache SQLite- oder als zentrale Server-Datenbank (s.u.) gestaltet sein. Schon eine lokale einfache SQLite-Datenbank ist in der Regel deutlich performanter als File-basierte Logs.&lt;br /&gt;
&lt;br /&gt;
Damit eine Datenbank-Nutzung möglich ist, müssen folgende Anpassungen gemacht werden:&lt;br /&gt;
# [[#Datenbank|Erstellen einer entsprechenden Datenbank]]&lt;br /&gt;
# [[#Datenbank-Anbindung mittels db.conf|Konfiguration der Datenbank-Anbindung in FHEM]]&lt;br /&gt;
# [[#Konfiguration als Device in fhem.cfg|Anpassen aller (oder einzelner) Konfigurationen von FileLog nach DbLog]]&lt;br /&gt;
# [[#Anpassen der gplot-Konfigurationen|Ggf. Anpassen der gplot-Konfigurationen]]&lt;br /&gt;
&lt;br /&gt;
;Hinweis:&lt;br /&gt;
Reporting und Management von DbLog-Datenbankinhalten kann mit dem Modul [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] stattfinden.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
=== Datenbank-Anbindung mittels db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MySQL-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration als Device ===&lt;br /&gt;
Das DbLog Device wird dann definiert mit&lt;br /&gt;
:&amp;lt;code&amp;gt;define &amp;lt;name&amp;gt; DbLog &amp;lt;configfilename&amp;gt; &amp;lt;regexp&amp;gt; &amp;lt;/code&amp;gt;&lt;br /&gt;
wobei &#039;&#039;&amp;lt;configfilename&amp;gt;&#039;&#039; dem Pfad zur zuvor angelegten db.conf entspricht.&lt;br /&gt;
Ein Beispiel hierfür wäre:&lt;br /&gt;
:&amp;lt;code&amp;gt;define logdb DbLog ./db.conf .*:.* &amp;lt;/code&amp;gt;&lt;br /&gt;
Die Angabe von &amp;lt;code&amp;gt;.*:.*&amp;lt;/code&amp;gt; bedeutet, dass sämtliche DeviceMessages (Messwerte, Batteriestatus, KeepAlives, etc.) in die Datenbank geschrieben werden. Dies führt u.U. dazu, dass die Datenbank auch mit vielen teils irrelevanten Werten gefüllt wird. Man kann daher die zu loggenden Werte einschränken, indem man genau angibt welche Werte übertragen werden sollen. Dies ist in [[#Finetuning des Loggings]] beschrieben.&lt;br /&gt;
&lt;br /&gt;
Unbedingt beachten: bei Verwendung des Moduls configdb wird die Konfigurationsdatei aus der &#039;&#039;&#039;&#039;&#039;Datenbank&#039;&#039;&#039;&#039;&#039; gelesen. Deshalb ist es erforderlich, das File mittels &amp;lt;code&amp;gt;configdb fileimport db.conf &amp;lt;/code&amp;gt; vorher zu importieren !&lt;br /&gt;
&lt;br /&gt;
=== Finetuning des Loggings ===&lt;br /&gt;
Bei der Konfiguration des Log-Devices werden die zu loggenden Daten definiert - in der einfachsten Form sieht das so aus: &amp;lt;code&amp;gt;define logdb DbLog ./db.conf .*:.* &amp;lt;/code&amp;gt;. Die Angabe von &amp;lt;code&amp;gt;.*:.*&amp;lt;/code&amp;gt; bedeutet, dass sämtliche DeviceMessages (Messwerte, Batteriestatus, KeepAlives, etc.) in die Datenbank geschrieben werden. Dies führt u.U. dazu, dass die Datenbank auch mit sehr vielen und teils nicht benötigten Werten gefüllt wird und schnell wächst. Die Datenbank ist zwar deutlich leistungsfähiger, was große Datenmengen angeht, Datensparsamkeit kann aber schnell sinnvoll werden...&lt;br /&gt;
&lt;br /&gt;
Um das Log-Aufkommen einzugrenzen gibt es mehrere Ansätze:&lt;br /&gt;
* Einschränkung über den &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Eintrag&lt;br /&gt;
* Einschränkung über DbLogExclude-Einträge der jeweiligen Devices&lt;br /&gt;
* Einschränkung über DbLogInclude-Einträge des jeweiligen Devices&lt;br /&gt;
* Ausschluß von Device/Reading-Kombinationen über das Attribut &amp;quot;excludeDevs&amp;quot;. Es können [https://fhem.de/commandref_DE.html#devspec devspec] verwendet werden. &lt;br /&gt;
&lt;br /&gt;
==== Einschränkung über den zentralen &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Eintrag ====&lt;br /&gt;
Man kann die zu loggenden Werte einschränken, indem man genau angibt welche Werte übertragen werden sollen. Die erste Wildcard, also das erste &amp;lt;code&amp;gt;.*&amp;lt;/code&amp;gt;, entspricht dem in FHEM verwendeten Device-Namen. Die zweite Wildcard entspricht dem vom Device ausgegebenen zu loggenden Wert. Separiert werden beiden Angaben durch einen Doppelpunkt. &lt;br /&gt;
&lt;br /&gt;
Ein Beispiel, um zwar alle definierten Devices zu erfassen, aber nur die Werte Temperatur, Ventilposition und Luftfeuchte in die Datenbank zu schreiben wäre:&lt;br /&gt;
:&amp;lt;code&amp;gt;define myDbLog DbLog ./db.conf .*:(temperature|valveposition|humidity).* &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einschränkung über die jeweiligen Devices ====&lt;br /&gt;
Man kann die zu loggenden Werte für einzelne Devices separat einschränken, ohne dies im zentralen define-Eintrag machen zu müssen. Dies kann interessant sein, wenn beispielsweise ein Device Fehlerwerte meldet, die uninteressant sind, oder es meldet unnötig häufig Werte - beides ist z.B. bei 1-wire-Temperatursensoren gerne der Fall.&lt;br /&gt;
&lt;br /&gt;
Um das einzuschränken gibt es 2 Stellparameter, die als Attribute direkt zum jeweiligen Device konfiguriert werden:&lt;br /&gt;
* DbLogExclude - definiert Werte, die nicht geloggt werden sollen&lt;br /&gt;
* DbLogInclude - definiert Werte, die geloggt werden sollen ( siehe attr DbLogSelectionMode )&lt;br /&gt;
* event-min-interval, event-on-change-reading und event-on-update-reading beeinflussen, wie häufig Werte geloggt werden (vgl. {{Link2CmdRef|Lang=de|Anker=event-on-update-reading}})&lt;br /&gt;
&lt;br /&gt;
Eine konkrete Konfiguration für einen sehr gesprächigen 1-wire-Temperatursensor könnte wie folgt aussehen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define EG_Balkon GPIO4 BUSMASTER&lt;br /&gt;
attr EG_Balkon DbLogExclude failures,T,85     # logge keine &amp;quot;failures&amp;quot;, &amp;quot;T&amp;quot;-Werte und &amp;quot;85&amp;quot;-Werte (default-Werte, wenn keine Temperatur gelesen werden kann)&lt;br /&gt;
attr EG_Balkon event-on-change-reading state  # logge nur, wenn sich ein Wert ändert (wenn sich die Temperatur nicht ändert, logge das nicht)&lt;br /&gt;
attr EG_Balkon event-min-interval state:900   # logge spätestens alle 900sec = 15min&lt;br /&gt;
attr EG_Balkon event-on-update-reading .*     # logge alle Werte, die aktualisiert werden&lt;br /&gt;
&lt;br /&gt;
attr &amp;lt;1-Wire-Device vom Typ OWTHERM oder OWSWITCH&amp;gt; DbLogExclude data.*      # verhindert das Logging der state-Eintragungen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine in diesem {{Link2Forum|Topic=33697|Message=264127}} vorgestellte Strategie zur Vermeidung unnötigen Loggings ist, dass bei der Definition von Devices durch das nachfolgende &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; automatisch ein DbLogExclude für alle Werte (.*) des Devices zugewiesen wird und dies nur bei Interesse an geloggten Werten gelöscht bzw. angepasst wird:&lt;br /&gt;
&amp;lt;code&amp;gt;define nDbLogExclude notify global:DEFINED.* attr $EVTPART1 DbLogExclude .*&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebenso ist es mittlerweile möglich, lediglich erwünschte Werte (Positiv-Liste) zu loggen und alle anderen zu verwerfen. Hierfür wird im LogDevice das attribut DbLogSelectionMode Include verwendet. Nun kann für jedes Device mit DbLogInclude &amp;lt;Reading1&amp;gt;,&amp;lt;Reading2&amp;gt;,... angegeben werden, welche Readings geloggt werden sollen. &lt;br /&gt;
Integriert ist ebenfalls ein &amp;quot;min-interval&amp;quot;, siehe {{Link2CmdRef}}.&lt;br /&gt;
&lt;br /&gt;
== Datenbank ==&lt;br /&gt;
Unterstützte Datenbanksysteme (Auswahl):&lt;br /&gt;
* Sqlite&lt;br /&gt;
* [[DbLog-MySQL|MySQL]]&lt;br /&gt;
* PostGreSql&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen zur Anlage von Tabellen und Indizes sind für jeden unterstützten Datenbanktyp im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation, oder hier zu finden: [https://svn.fhem.de/trac/browser/trunk/fhem/contrib/dblog/ Link]. Das MySQL-Skript (db_create_mysql.sql) legt eine neue Datenbank, das PostGres-Skript (db_create_postgresql.sql) ein neues Schema mit Namen &amp;quot;fhem&amp;quot; an.&lt;br /&gt;
&lt;br /&gt;
==== current ====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== history ====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
{{Todo|Ausbauen}}&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
== Anpassen der gplot-Konfigurationen ==&lt;br /&gt;
Die meisten gplot-Konfigurationen sind bisher lediglich auf FileLog-Konfigurationen ausgelegt. Deshalb müssen sie für die Verwendung mit DbLog angepasst werden. Glücklicherweise beschränkt sich dies auf die reinen FileLog-Zeilen - es müssen die DbLog-Äquivalente hinzugefügt werden. Die FileLog-Einträge müssen zwar nicht gelöscht werden, wenn man aber FileLog und DbLog parallel betreibt, sollte man getrennte gplot-Dateien für beide Logging-Typen haben um Auswertungsprobleme erkennen zu können.&lt;br /&gt;
&lt;br /&gt;
Für die fht.gplot Konfiguration sähe die Anpassung wie folgt aus (lediglich die vier DbLog-Zeilen wurden hinzugefügt):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2014-12-25 21:53: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 nomirror&lt;br /&gt;
set y2tics &lt;br /&gt;
set grid y2tics&lt;br /&gt;
set ylabel &amp;quot;Actuator/Window (%)&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Temperature in C&amp;quot;&lt;br /&gt;
set yrange 0:100&lt;br /&gt;
set y2range 5:25&lt;br /&gt;
&lt;br /&gt;
#FileLog 4:.measured-temp\x3a:0:&lt;br /&gt;
#FileLog 4:.actuator\x3a:0:int&lt;br /&gt;
#FileLog 4:.desired-temp::&lt;br /&gt;
#FileLog 4:.window\x3a::&lt;br /&gt;
&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.measured-temp:0:&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.actuator:0:int&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.desired-temp::&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.window::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Measured temperature&#039; ls l0 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Actuator (%)&#039; ls l1 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Desired Temperature&#039; ls l2 lw 1 with steps,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Window&#039; ls l3 lw 1 with steps&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Des Weiteren ist zu beachten: &lt;br /&gt;
&lt;br /&gt;
On-Off-Plots&lt;br /&gt;
&lt;br /&gt;
 EG_Bad:window:::$val=~s/(on|off)(\d*).*/$1eq&amp;quot;on&amp;quot;?1:0/eg&lt;br /&gt;
&lt;br /&gt;
unter Berücksichtigung von dim-Werten:&lt;br /&gt;
&lt;br /&gt;
 EG_WoZi_Licht:value:::$val=~s/(on|off)(\d*).*/$1eq&amp;quot;on&amp;quot;?1:($1eq&amp;quot;dim&amp;quot;?$2*0.01:0)/eg&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer SQLite-Datenbank ==&lt;br /&gt;
Im folgenden wird eine lokale SQLite-Datenbank auf einen Ubuntu-System angelegt (nach Quelle: [http://www.tatsch-it.de/fhem-dblog/ http://www.tatsch-it.de/fhem-dblog/])&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Installation von SQLite:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo aptitude install sqlite3 libdbi-perl libdbd-sqlite3-perl&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anlegen der SQLite-Datenbank fhem.db&#039;&#039; (öffnet auch direkt eine SQL-Kommandozeile):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 /opt/fhem/fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der geöffneten SQL-Kommandozeile eingeben:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE &#039;history&#039; (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
CREATE TABLE &#039;current&#039; (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
CREATE INDEX Search_Idx ON `history` (DEVICE, READING, TIMESTAMP);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Kommandozeile verlässt man mit &amp;lt;code&amp;gt;.exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anpassen des Besitzers und der Rechte der Datenbank-Datei:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo chown fhem /opt/fhem/fhem.db&lt;br /&gt;
sudo chmod 666 /opt/fhem/fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Datenbank-Anbindung des FHEM konfigurieren:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo nano /opt/fhem/db.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
Inhalt:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%dbconfig= (&lt;br /&gt;
  connection =&amp;gt; &amp;quot;SQLite:dbname=/opt/fhem/fhem.db&amp;quot;,&lt;br /&gt;
  user =&amp;gt; &amp;quot;&amp;quot;,&lt;br /&gt;
  password =&amp;gt; &amp;quot;&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Logging des FHEM auf die Datenbank konfigurieren:&#039;&#039; (hier sind nur die Anpassungen aufgeführt)&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo nano /opt/fhem/fhem.cfg&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
attr global userattr DbLogExclude ...  # erlaubt es einzelne Einträge nicht zu loggen&lt;br /&gt;
...&lt;br /&gt;
define logdb DbLog ./db.conf .*:.*     # logt alle(!) auflaufenden Events aller Konfigurationen&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Da durch diese &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Definition alle auflaufenden Events gelogt werden, müssen keine weiteren Anpassungen in der Konfiguration gemacht werden. Die FileLog-Einträge können bedenkenlos bestehen bleiben - dann wird in Datenbank und FileLog gelogt und man verliert keine Daten, falls etwas nicht klappt. Wenn alles wie geplant läuft, können die FileLog-Definitionen gelöscht werden (ebenso die Log-Dateien). Ebenso können die zu loggenden Daten später eingegrenzt werden (s. [[#Finetuning des Loggings]]).&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;FHEM neu starten:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo service fhem stop&lt;br /&gt;
sudo service fhem start&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Kontrollieren, ob Logs in die Datenbank geschrieben werden:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 /opt/fhem/fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der geöffneten SQL-Kommandozeile eingeben:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
select * from history order by TIMESTAMP;       # dies gibt alle(!) Logs chronologisch aus (kann nach längerem Betrieb recht lange dauern)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Kommandozeile verlässt man mit &amp;lt;code&amp;gt;.exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anpassung der glot-Dateien:&#039;&#039; siehe [[#Anpassen der gplot-Konfigurationen]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer Mysql-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 apt-get update &amp;amp;&amp;amp; apt-get install mysql-server mysql-client libdbd-mysql libdbd-mysql-perl&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Installation sollte man aus Sicherheitsgründen ein Passwort für den mysql-root vergeben, wenn man nicht sogar ganz den Login verbietet.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der normale Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Zum Test mal mit mysql verbinden:&lt;br /&gt;
 # mysql -p -u root&lt;br /&gt;
 Enter password:&lt;br /&gt;
 mysql&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen (root Passwort wird abgefragt): &lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; show databases;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Integration von DBLog in eigene Module ==&lt;br /&gt;
=== Bereitstellung der UNITS ===&lt;br /&gt;
Mit der DbLog_splitFn kann der Modulautor selbst festlegen, wie die Events des Moduls in die Bestandteile Reading/Value/Unit zerlegt werden um ein korrektes Logging per DbLog zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
Dazu muss der Modulautor in der [[DevelopmentModuleIntro#X_Initialize|Initialize-Funktion]] eine &amp;lt;code&amp;gt;DbLog_splitFn&amp;lt;/code&amp;gt; bereitstellen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	...&lt;br /&gt;
	$hash-&amp;gt;{DbLog_splitFn}      = &amp;quot;X_DbLog_splitFn&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die genaue Aufrufsyntax und Funktionweise einer DbLog_split-Funktion findet man [[DevelopmentModuleIntro#X_DbLog_split|hier]].&lt;br /&gt;
&lt;br /&gt;
== Werte auslesen ==&lt;br /&gt;
Manchmal möchte man Daten aus den Logs abrufen ohne händisch in der Datenbank herumzuwühlen (s.u.). Dies ist insb. auch dann hilfreich, wenn man eigenen Funktionen, Notifys oder spezielle Plots entwirft, bei denen man auf Logdaten zugreifen möchte.&lt;br /&gt;
&lt;br /&gt;
Grundsätzlich beschrieben ist dies in der {{Link2CmdRef|Lang=de|Anker=DbLog}} und unterscheidet sich minimal (aber entscheidend) von der Struktur bei [[FileLog#Werte_auslesen|FileLogs]].&lt;br /&gt;
&lt;br /&gt;
Hier ein paar Beispiele, was man damit anstellen kann:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01 2016-10-03 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor vom 01.10.-03.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor von 8-16 Uhr am 01.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor:temperature&amp;lt;/code&amp;gt; nur die temperature Werte&lt;br /&gt;
* &amp;lt;code&amp;gt;{ ReadingsTimestamp(&amp;quot;meinSensor&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;0&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des aktuellen state des meinSensor&lt;br /&gt;
* &amp;lt;code&amp;gt;{ OldTimestamp(&amp;quot;meinSensor&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des letzten state des FHT_3a32&lt;br /&gt;
* &amp;lt;code&amp;gt;{ time_str2num(OldTimestamp(&amp;quot;meinSensor&amp;quot;)) }&amp;lt;/code&amp;gt; Timestamp in Sekunden des letzten state des meinSensor&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
&#039;&#039;&#039;Öffnen der DB unter Linux:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(Es werden Schreibrechte benötigt,ohne kann man die DB zwar öffnen, aber nichts machen)&lt;br /&gt;
 sudo sqlite3 fhem.db&lt;br /&gt;
Dadurch öffnet sich ein SQL-Konsole, auf der alle weiteren Befehle ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schliessen der DB:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .exit&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfe anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .help&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Tabellen anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .tables&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Das Schema der DB anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(vgl. oben [[DbLog#Datenbanken]] und [[DbLog#Beispiel: Anlegen und Nutzung einer SQLite-Datenbank]])&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .schema&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Eintäge anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Die Einträge liegen alle in der Tabelle &amp;quot;History&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ganz wichtig&#039;&#039;&#039; ist immer das &amp;quot;;&amp;quot; am Ende Zeile (bei allen Kommandos, die nicht mit einem &amp;quot;.&amp;quot; anfangen). Wenn es vergessen wurde zeigt die Konsole solange neue Zeilen bis ein &amp;quot;;&amp;quot; eingegeben wird. So kann ein Befehl auch bequem über mehrere Zeilen geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY;&lt;br /&gt;
&lt;br /&gt;
Dies kann sehr lange dauern und kann ggf. mit &amp;lt;code&amp;gt;STRG-C&amp;lt;/code&amp;gt; abgebrochen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;where&amp;lt;/code&amp;gt;-Statements werden Strings in einfache Anführungsstriche gesetzt, Zahlen nicht.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LÖSCHEN aller Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Löschen kann nicht rückgängig gemacht werden!! Also IMMER erst die entsprechenden SELECT-Statements solange verfeinern bis wirklich nur die gewünschten Einträge angezeigt werden. Dann das &amp;lt;code&amp;gt;select *&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; ersetzen.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; delete from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank reparieren ==&lt;br /&gt;
Es kann immer wieder mal vorkommen, dass Datenbanken Fehler enthalten. Das muss im Alltag garnicht auffallen und auch nicht immer schlimm enden. Wenn man auf der SQL-Konsole aber bspw. eine Meldung &amp;lt;code&amp;gt;Error: database disk image is malformed&amp;lt;/code&amp;gt; erhält, sollte man ein Reparatur vornehmen.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
Die folgenden Schritte beschreiben, wie man eine SQLite-DB reparieren kann (Quelle: [http://techblog.dorogin.com/2011/05/sqliteexception-database-disk-image-is.html]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
DB öffnen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Integritäts-Check durchführen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sqlite&amp;gt; pragma integrity_check;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kommt hier ein &amp;quot;ok&amp;quot; ist die DB gesund. Ansonsten erscheint etwas wie&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
*** in database main ***&lt;br /&gt;
On tree page 118786 cell 1: Rowid 75 out of order (previous was 816660)&lt;br /&gt;
On tree page 118786 cell 4: Rowid 815704 out of order (previous was 816727)&lt;br /&gt;
Corruption detected in cell 0 on page 118786&lt;br /&gt;
Multiple uses for byte 132 of page 118786&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Datenbank-Dump erstellen (Export gesamten DB in die Datei &amp;quot;dump_all_20160516_1043.sql&amp;quot;) und DB verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .mode insert&lt;br /&gt;
sqlite&amp;gt; .output dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; .dump&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Neue Datenbank erstellen und den Dump einlesen, Integritäts-Check machen und verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem-neu.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .read dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; pragma integrity_check;&lt;br /&gt;
ok&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Spätestens jetzt FHEM stoppen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem stop&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Alte DB sichern und neue aktivieren:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo mv fhem.db fhem.db.sv_20160516&lt;br /&gt;
sudo mv fhem-neu.db fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Kontrollieren, dass die neue DB die gleichen Rechte wie die alte DB hat (und ggf. korrigieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 root root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
~/fhem$ sudo chown fhem:root fhem.db&lt;br /&gt;
&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 fhem root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
FHEM wieder starten (und natürlich kontrollieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem start&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. Diese geschieht mittels des Userreadings, welches man vorteilshafterweise mit im DbLog-device anlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;attr myDbLog userReadings DbFileSize:reduceLogState.* { (split(&#039; &#039;,`du -m fhem.db`))[0] }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels dieses Attributs wird die Grösse der .db-Datei immer nach dem Ausführen des ReduceLog in das Reading &amp;quot;DbFileSize&amp;quot; in ganzzahligen MByte abgelegt.&lt;br /&gt;
&lt;br /&gt;
Basierend auf diesem Reading können dann weitere Aktionen, beispielsweise ein Plot, erstellt werden.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist für SQLite verwendbar. Zur Ermittlung der DB-Größe andere DB-Typen (aber auch für SQLite nutzbar) kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Performance-Optimierung ==&lt;br /&gt;
Auch eine Datenbank kann mit der Zeit langsamer werden. Dies hängt von mehreren Faktoren ab:&lt;br /&gt;
* Menge der gelogten Daten (zB. &amp;gt; 4-5 GB)&lt;br /&gt;
* Eingesetzte Hardware (zB. langsame SD-Karte vs. schnelle SSD)&lt;br /&gt;
* Eingesetztes Datenbanksystem (zB. SQLite, MySQL)&lt;br /&gt;
* Komplexität der Abfragen (zB. für aufwändige Graphen oder Berechnungen)&lt;br /&gt;
&lt;br /&gt;
Diese Punkte sollen im folgenden diskutiert werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komplexität der Abfragen ===&lt;br /&gt;
Dies ist kein Problem der Datenbank, sondern rein der Abfrage. Dem entsprechend muss die Optimierung auch in der Abfrage oder im Skript gesucht werden. Dies ist nicht Ziel dieses Abschnittes und wird hier nicht weiter behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetztes Datenbanksystem ===&lt;br /&gt;
Welches Datenbanksystem eingesetzt wird (zB. SQLite oder MySQL) hat auf die Performance der Datenbank gar keinen so großen Einfluss, wie vielleicht zuerst gedacht. Selbst SQLite kann problemlos Datenbanken mit etlichen GB Größe performant verarbeiten. Der Flaschenhals ist hier viel mehr die darunter liegende Hardware (s.u.).&lt;br /&gt;
&lt;br /&gt;
Die Performance der Datenbank an sich, kann aber durch verschiedene Maßnahmen verbessert werden:&lt;br /&gt;
* Pflegemaßnahmen bzgl. der Daten&lt;br /&gt;
* &#039;&#039;&#039;Erstellung von Indizes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Menge der Daten und Pflegemaßnahmen bzgl. der Daten ====&lt;br /&gt;
Die Menge der geloggten Daten hat natürlich Einfluss auf die Geschwindigkeit von Abfragen - je mehr Daten vorhanden sind, desto mehr Daten müssen auch durchforstet werden um eine Abfrage zu bedienen. Die Reduzierung der geloggten Datenmenge hat also direkten Einfluss auf die Größe und damit auch die Geschwindigkeit der Datenbank. Die Menge der zu loggenden Daten lässt sich an zwei Stellen einschränken:&lt;br /&gt;
* bei der Definition jedes Devices (s. Kapitel oben)&lt;br /&gt;
* bei der Festlegung des fhem-weiten Log-Levels (s. [[Loglevel]])&lt;br /&gt;
&lt;br /&gt;
Die Menge der bereits geloggten Daten kann zB. mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] verringert und optimiert werden, bspw.&lt;br /&gt;
* löschen unnötiger Daten&lt;br /&gt;
* vacuum der Datenbank&lt;br /&gt;
&lt;br /&gt;
Insgesamt haben diese Maßnahmen aber nur einen eingeschränkten Effekt auf die Performance der DB. Deutlich effektiver ist die Erstellung eines Index (s.u.).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Erstellung von Indizes ====&lt;br /&gt;
Die Erstellung von Indizes hat mit Abstand den größten Einfluss auf die Performance einer Datenbank (auf unveränderter Hardware). Extrem zusammengefasst ist ein Index eine extrem optimiertes Nachschlageverzeichnis für einen bestimmten Typ Daten (ein Index wie im Buch halt). Eine wunderbare Einführung in Indizes bietet [[https://use-the-index-luke.com/de|https://use-the-index-luke.com]].&lt;br /&gt;
&lt;br /&gt;
In fhem sind Indizes sogar sehr einfach einzurichten da die Datenbank-Nutzung sehr stark vorgegeben ist. Nahezu jede Abfrage folgt dem Schema &#039;&#039;Device -&amp;gt; Reading -&amp;gt; Datum -&amp;gt; Wert&#039;&#039;. Ein Index kann genau diese Abfrage bedienen und beschleunigen. Ein Index nur über die Devices wäre ein erster Schritt, brächte aber noch keinen großen Gewinn (wie um Link oben gut beschrieben). Über die gesamten ersten drei Schritte erstellt (Device -&amp;gt; Reading -&amp;gt; Datum) bringt der Index aber sofort eine deutliche Geschwindigkeitssteigerung.&lt;br /&gt;
&lt;br /&gt;
Die Erstellung eines Index erfolgt direkt in der Datenbank (und nicht aus fhem heraus), hier am Beispiel einer SQLite-DB:&lt;br /&gt;
&lt;br /&gt;
Öffnen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; sudo sqlite3 fhem.db &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugen des Index auf der DB-Konsole (das Semikolon am Ende ist wichtig):&lt;br /&gt;
&amp;lt;pre&amp;gt; create index idx_device_reading_timestamp on history (device, reading, timestamp); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verlassen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; .exit &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzig zu berücksichtigen ist, dass dieser Index die Datenbank um bis zu 1/3 vergrößert. Er kann aber bei Bedarf auch wieder entfernt werden. Bei meiner 15 GB SQLite-Datei (auf einem Mac Mini mit SSD) hat dies ca. 15 min gedauert (den fhem hatte ich vorsichtshalber währenddessen deaktiviert).&lt;br /&gt;
&lt;br /&gt;
Sollte jemand spezielle Berechnungen oder Skripte ausführen, die nach einem anderen Abfrage-Schema arbeiten, könnte man dafür spezialisierte zusätzliche Indizes erstellen. Das sollte aber dann mit dem Wissen des obigen Links erarbeitet werden, da dann etwas mehr Hintergrundwissen sehr hilfreich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== DB-Backup ====&lt;br /&gt;
Ein anderer Aspekt, der eigentlich nichts mit der Performance der DB zu tun hat, ist der Einfluss aufs Backup. Wird bspw. ein Systembackup per RSYNC gemacht, muss bei SQLite immer die komplette ggf. riesige Datei gesynct werden - bei MySQL würden nur die veränderten DB-Elemente gesynct. Dies soll hier nicht weiter vertieft werden, sollte aber bei einer Gesamtstrategie bedacht werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetzte Hardware ===&lt;br /&gt;
fhem hat grundsätzlich sehr viele kleine Datenzugriffe, unabhängig davon ob FileLog oder DbLog eingesetzt wird. Deshalb ist der absolut größte Performance-Gewinn durch den Einsatz schneller Festplatten zu erreichen - ganz einfache Aussage: &#039;&#039;je schneller desto besser ;-)&#039;&#039;. Konkret bietet eine SSD mit mind. 250MB/s Datenzugriff eine ordentliche Basis für jedes datengestützte System, wie den fhem.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenmenge größer und die Abfragen komplexer werden, müssen natürlich irgendwann auch die Prozessorleistung und der Arbeitsspeicher mit wachsen. Aber auch an einem Raspi wird eine SSD deutlich performanter sein, als eine einfache SD-Karte oder eine klassische rotierende Festplatte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31055</id>
		<title>DbLog-MySQL</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31055"/>
		<updated>2019-07-29T10:49:19Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* phpMyAdmin */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Da bei der Inbetriebnahme und der Migration von MySQL bzw MariaDB-Datenbanken einige kleine Fallen auftreten können, gibt&#039;s hier ein paar Hinweise.&lt;br /&gt;
&lt;br /&gt;
== Server version: 10.1.40-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04 ==&lt;br /&gt;
=== db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MariaDB-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp. Bei der Verwendung von MariaDB muss die MySQL-Datei verwendet werden.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; befindet sich das Script &#039;&#039;&#039;db_create_mysql.sql&#039;&#039;&#039; , welches eine neue Datenbank samt Tabellenstruktur anlegt. Ausserdem wird ein Benutzer &amp;quot;fhemuser&amp;quot; mit einem (zu ändernden) &amp;quot;fhempasswort&amp;quot; angelegt.&lt;br /&gt;
==== Primary Keys ====&lt;br /&gt;
===== current =====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== history =====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`history` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`TIMESTAMP`,`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer MariaDB-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der Shell-Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der MariaDB-Prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 # apt-get update &amp;amp;&amp;amp; apt-get install mariadb-server&lt;br /&gt;
&lt;br /&gt;
Nach der Installation muss das Script &lt;br /&gt;
 # mysql_secure_installation&lt;br /&gt;
ausgeführt werden. Dieses legt ein root-Passwort fest, entfernt die anonymen User und entfernt die test-Datenbank.&lt;br /&gt;
Empfohlen wird, alle Fragen mit &#039;&#039;&#039;Y&#039;&#039;&#039; zu beantworten.&lt;br /&gt;
&lt;br /&gt;
Zum Test mit MariaDB verbinden:&lt;br /&gt;
 # mysql -u root -p&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen:&lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  exit&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang als fhemuser testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show databases;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show Databases einfügen &amp;gt;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden.&lt;br /&gt;
Um die Tabellen zu sehen, kann man den folgenden Befehl eingeben: &lt;br /&gt;
&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show tables;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show tables einfügen &amp;gt;&amp;gt;&lt;br /&gt;
, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
=== phpMyAdmin ===&lt;br /&gt;
Ein sehr beliebtes Tool ist phpMyAdmin. &lt;br /&gt;
 # apt-get install phpMyAdmin&lt;br /&gt;
Bei der Installation wird man nach einem Passwort für den User &amp;quot;phpMyAdmin&amp;quot; gefragt, der im weiteren Verlauf erstmal nutzlos ist.&lt;br /&gt;
Nun kann man sich unter &amp;lt;meine-fhem-Domain&amp;gt;/phpMyAdmin mit der Seite verbinden.&lt;br /&gt;
Zum einloggen kann nun der in der config-datei angegebene fhemuser mit dem fhempasswort verwendet werden, oder, sofern ein Passwort vergeben ist, auch den User root. &lt;br /&gt;
Der Unterschied liegt darin, dass root *alle* Datenbanken sehen und ändern darf (was zum ändern von Datenbankeigenschaften sowie Benutzerkonten notwendig ist), während dem fhemuser nur die fhemdatenbank angezeigt wird(was für suchabfragen, deletes und inserts sowie Tabellenänderungen völlig ausreicht.&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. &lt;br /&gt;
Zur Ermittlung der MariaDB-Größe kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31054</id>
		<title>DbLog-MySQL</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31054"/>
		<updated>2019-07-29T10:49:04Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Bearbeitung von Datenbank-Einträgen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Da bei der Inbetriebnahme und der Migration von MySQL bzw MariaDB-Datenbanken einige kleine Fallen auftreten können, gibt&#039;s hier ein paar Hinweise.&lt;br /&gt;
&lt;br /&gt;
== Server version: 10.1.40-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04 ==&lt;br /&gt;
=== db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MariaDB-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp. Bei der Verwendung von MariaDB muss die MySQL-Datei verwendet werden.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; befindet sich das Script &#039;&#039;&#039;db_create_mysql.sql&#039;&#039;&#039; , welches eine neue Datenbank samt Tabellenstruktur anlegt. Ausserdem wird ein Benutzer &amp;quot;fhemuser&amp;quot; mit einem (zu ändernden) &amp;quot;fhempasswort&amp;quot; angelegt.&lt;br /&gt;
==== Primary Keys ====&lt;br /&gt;
===== current =====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== history =====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`history` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`TIMESTAMP`,`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer MariaDB-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der Shell-Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der MariaDB-Prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 # apt-get update &amp;amp;&amp;amp; apt-get install mariadb-server&lt;br /&gt;
&lt;br /&gt;
Nach der Installation muss das Script &lt;br /&gt;
 # mysql_secure_installation&lt;br /&gt;
ausgeführt werden. Dieses legt ein root-Passwort fest, entfernt die anonymen User und entfernt die test-Datenbank.&lt;br /&gt;
Empfohlen wird, alle Fragen mit &#039;&#039;&#039;Y&#039;&#039;&#039; zu beantworten.&lt;br /&gt;
&lt;br /&gt;
Zum Test mit MariaDB verbinden:&lt;br /&gt;
 # mysql -u root -p&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen:&lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  exit&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang als fhemuser testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show databases;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show Databases einfügen &amp;gt;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden.&lt;br /&gt;
Um die Tabellen zu sehen, kann man den folgenden Befehl eingeben: &lt;br /&gt;
&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show tables;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show tables einfügen &amp;gt;&amp;gt;&lt;br /&gt;
, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
=== phpMyAdmin ===&lt;br /&gt;
Ein sehr beliebtes Tool ist phpMyAdmin. &lt;br /&gt;
 # apt-get install phpMyAdmin&lt;br /&gt;
Bei der Installation wird man nach einem Passwort für den User &amp;quot;phpMyAdmin&amp;quot; gefragt, der im weiteren Verlauf erstmal nutzlos ist.&lt;br /&gt;
Nun kann man sich unter &amp;lt;meine-fhem-Domain&amp;gt;/phpMyAdmin mit der Seite verbinden.&lt;br /&gt;
Zum einloggen kann nun der in der config-datei angegebene fhemuser mit dem fhempasswort verwendet werden, oder, sofern ein Passwort vergeben ist, auch den User root. &lt;br /&gt;
Der Unterschied liegt darin, dass root *alle* Datenbanken sehen und ändern darf (was zum ändern von Datenbankeigenschaften sowie Benutzerkonten notwendig ist), während dem fhemuser nur die fhemdatenbank angezeigt wird(was für suchabfragen, deletes und inserts sowie Tabellenänderungen völlig ausreicht.&lt;br /&gt;
&lt;br /&gt;
Für die&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. &lt;br /&gt;
Zur Ermittlung der MariaDB-Größe kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31053</id>
		<title>DbLog-MySQL</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31053"/>
		<updated>2019-07-29T10:44:00Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Performance-Optimierung */ gelöscht&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Da bei der Inbetriebnahme und der Migration von MySQL bzw MariaDB-Datenbanken einige kleine Fallen auftreten können, gibt&#039;s hier ein paar Hinweise.&lt;br /&gt;
&lt;br /&gt;
== Server version: 10.1.40-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04 ==&lt;br /&gt;
=== db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MariaDB-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp. Bei der Verwendung von MariaDB muss die MySQL-Datei verwendet werden.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; befindet sich das Script &#039;&#039;&#039;db_create_mysql.sql&#039;&#039;&#039; , welches eine neue Datenbank samt Tabellenstruktur anlegt. Ausserdem wird ein Benutzer &amp;quot;fhemuser&amp;quot; mit einem (zu ändernden) &amp;quot;fhempasswort&amp;quot; angelegt.&lt;br /&gt;
==== Primary Keys ====&lt;br /&gt;
===== current =====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== history =====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`history` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`TIMESTAMP`,`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer MariaDB-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der Shell-Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der MariaDB-Prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 # apt-get update &amp;amp;&amp;amp; apt-get install mariadb-server&lt;br /&gt;
&lt;br /&gt;
Nach der Installation muss das Script &lt;br /&gt;
 # mysql_secure_installation&lt;br /&gt;
ausgeführt werden. Dieses legt ein root-Passwort fest, entfernt die anonymen User und entfernt die test-Datenbank.&lt;br /&gt;
Empfohlen wird, alle Fragen mit &#039;&#039;&#039;Y&#039;&#039;&#039; zu beantworten.&lt;br /&gt;
&lt;br /&gt;
Zum Test mit MariaDB verbinden:&lt;br /&gt;
 # mysql -u root -p&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen:&lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  exit&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang als fhemuser testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show databases;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show Databases einfügen &amp;gt;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden.&lt;br /&gt;
Um die Tabellen zu sehen, kann man den folgenden Befehl eingeben: &lt;br /&gt;
&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show tables;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show tables einfügen &amp;gt;&amp;gt;&lt;br /&gt;
, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. &lt;br /&gt;
Zur Ermittlung der MariaDB-Größe kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31052</id>
		<title>DbLog-MySQL</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31052"/>
		<updated>2019-07-29T10:43:42Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Datenbank reparieren */  gelöscht&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Da bei der Inbetriebnahme und der Migration von MySQL bzw MariaDB-Datenbanken einige kleine Fallen auftreten können, gibt&#039;s hier ein paar Hinweise.&lt;br /&gt;
&lt;br /&gt;
== Server version: 10.1.40-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04 ==&lt;br /&gt;
=== db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MariaDB-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp. Bei der Verwendung von MariaDB muss die MySQL-Datei verwendet werden.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; befindet sich das Script &#039;&#039;&#039;db_create_mysql.sql&#039;&#039;&#039; , welches eine neue Datenbank samt Tabellenstruktur anlegt. Ausserdem wird ein Benutzer &amp;quot;fhemuser&amp;quot; mit einem (zu ändernden) &amp;quot;fhempasswort&amp;quot; angelegt.&lt;br /&gt;
==== Primary Keys ====&lt;br /&gt;
===== current =====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== history =====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`history` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`TIMESTAMP`,`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer MariaDB-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der Shell-Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der MariaDB-Prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 # apt-get update &amp;amp;&amp;amp; apt-get install mariadb-server&lt;br /&gt;
&lt;br /&gt;
Nach der Installation muss das Script &lt;br /&gt;
 # mysql_secure_installation&lt;br /&gt;
ausgeführt werden. Dieses legt ein root-Passwort fest, entfernt die anonymen User und entfernt die test-Datenbank.&lt;br /&gt;
Empfohlen wird, alle Fragen mit &#039;&#039;&#039;Y&#039;&#039;&#039; zu beantworten.&lt;br /&gt;
&lt;br /&gt;
Zum Test mit MariaDB verbinden:&lt;br /&gt;
 # mysql -u root -p&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen:&lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  exit&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang als fhemuser testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show databases;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show Databases einfügen &amp;gt;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden.&lt;br /&gt;
Um die Tabellen zu sehen, kann man den folgenden Befehl eingeben: &lt;br /&gt;
&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show tables;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show tables einfügen &amp;gt;&amp;gt;&lt;br /&gt;
, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. &lt;br /&gt;
Zur Ermittlung der MariaDB-Größe kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
== Performance-Optimierung ==&lt;br /&gt;
Auch eine Datenbank kann mit der Zeit langsamer werden. Dies hängt von mehreren Faktoren ab:&lt;br /&gt;
* Menge der gelogten Daten (zB. &amp;gt; 4-5 GB)&lt;br /&gt;
* Eingesetzte Hardware (zB. langsame SD-Karte vs. schnelle SSD)&lt;br /&gt;
* Eingesetztes Datenbanksystem (zB. SQLite, MySQL)&lt;br /&gt;
* Komplexität der Abfragen (zB. für aufwändige Graphen oder Berechnungen)&lt;br /&gt;
&lt;br /&gt;
Diese Punkte sollen im folgenden diskutiert werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komplexität der Abfragen ===&lt;br /&gt;
Dies ist kein Problem der Datenbank, sondern rein der Abfrage. Dem entsprechend muss die Optimierung auch in der Abfrage oder im Skript gesucht werden. Dies ist nicht Ziel dieses Abschnittes und wird hier nicht weiter behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetztes Datenbanksystem ===&lt;br /&gt;
Welches Datenbanksystem eingesetzt wird (zB. SQLite oder MySQL) hat auf die Performance der Datenbank gar keinen so großen Einfluss, wie vielleicht zuerst gedacht. Selbst SQLite kann problemlos Datenbanken mit etlichen GB Größe performant verarbeiten. Der Flaschenhals ist hier viel mehr die darunter liegende Hardware (s.u.).&lt;br /&gt;
&lt;br /&gt;
Die Performance der Datenbank an sich, kann aber durch verschiedene Maßnahmen verbessert werden:&lt;br /&gt;
* Pflegemaßnahmen bzgl. der Daten&lt;br /&gt;
* &#039;&#039;&#039;Erstellung von Indizes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Menge der Daten und Pflegemaßnahmen bzgl. der Daten ====&lt;br /&gt;
Die Menge der geloggten Daten hat natürlich Einfluss auf die Geschwindigkeit von Abfragen - je mehr Daten vorhanden sind, desto mehr Daten müssen auch durchforstet werden um eine Abfrage zu bedienen. Die Reduzierung der geloggten Datenmenge hat also direkten Einfluss auf die Größe und damit auch die Geschwindigkeit der Datenbank. Die Menge der zu loggenden Daten lässt sich an zwei Stellen einschränken:&lt;br /&gt;
* bei der Definition jedes Devices (s. Kapitel oben)&lt;br /&gt;
* bei der Festlegung des fhem-weiten Log-Levels (s. [[Loglevel]])&lt;br /&gt;
&lt;br /&gt;
Die Menge der bereits geloggten Daten kann zB. mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] verringert und optimiert werden, bspw.&lt;br /&gt;
* löschen unnötiger Daten&lt;br /&gt;
* vacuum der Datenbank&lt;br /&gt;
&lt;br /&gt;
Insgesamt haben diese Maßnahmen aber nur einen eingeschränkten Effekt auf die Performance der DB. Deutlich effektiver ist die Erstellung eines Index (s.u.).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Erstellung von Indizes ====&lt;br /&gt;
Die Erstellung von Indizes hat mit Abstand den größten Einfluss auf die Performance einer Datenbank (auf unveränderter Hardware). Extrem zusammengefasst ist ein Index eine extrem optimiertes Nachschlageverzeichnis für einen bestimmten Typ Daten (ein Index wie im Buch halt). Eine wunderbare Einführung in Indizes bietet [[https://use-the-index-luke.com/de|https://use-the-index-luke.com]].&lt;br /&gt;
&lt;br /&gt;
In fhem sind Indizes sogar sehr einfach einzurichten da die Datenbank-Nutzung sehr stark vorgegeben ist. Nahezu jede Abfrage folgt dem Schema &#039;&#039;Device -&amp;gt; Reading -&amp;gt; Datum -&amp;gt; Wert&#039;&#039;. Ein Index kann genau diese Abfrage bedienen und beschleunigen. Ein Index nur über die Devices wäre ein erster Schritt, brächte aber noch keinen großen Gewinn (wie um Link oben gut beschrieben). Über die gesamten ersten drei Schritte erstellt (Device -&amp;gt; Reading -&amp;gt; Datum) bringt der Index aber sofort eine deutliche Geschwindigkeitssteigerung.&lt;br /&gt;
&lt;br /&gt;
Die Erstellung eines Index erfolgt direkt in der Datenbank (und nicht aus fhem heraus), hier am Beispiel einer SQLite-DB:&lt;br /&gt;
&lt;br /&gt;
Öffnen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; sudo sqlite3 fhem.db &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugen des Index auf der DB-Konsole (das Semikolon am Ende ist wichtig):&lt;br /&gt;
&amp;lt;pre&amp;gt; create index idx_device_reading_timestamp on history (device, reading, timestamp); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verlassen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; .exit &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzig zu berücksichtigen ist, dass dieser Index die Datenbank um bis zu 1/3 vergrößert. Er kann aber bei Bedarf auch wieder entfernt werden. Bei meiner 15 GB SQLite-Datei (auf einem Mac Mini mit SSD) hat dies ca. 15 min gedauert (den fhem hatte ich vorsichtshalber währenddessen deaktiviert).&lt;br /&gt;
&lt;br /&gt;
Sollte jemand spezielle Berechnungen oder Skripte ausführen, die nach einem anderen Abfrage-Schema arbeiten, könnte man dafür spezialisierte zusätzliche Indizes erstellen. Das sollte aber dann mit dem Wissen des obigen Links erarbeitet werden, da dann etwas mehr Hintergrundwissen sehr hilfreich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== DB-Backup ====&lt;br /&gt;
Ein anderer Aspekt, der eigentlich nichts mit der Performance der DB zu tun hat, ist der Einfluss aufs Backup. Wird bspw. ein Systembackup per RSYNC gemacht, muss bei SQLite immer die komplette ggf. riesige Datei gesynct werden - bei MySQL würden nur die veränderten DB-Elemente gesynct. Dies soll hier nicht weiter vertieft werden, sollte aber bei einer Gesamtstrategie bedacht werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetzte Hardware ===&lt;br /&gt;
fhem hat grundsätzlich sehr viele kleine Datenzugriffe, unabhängig davon ob FileLog oder DbLog eingesetzt wird. Deshalb ist der absolut größte Performance-Gewinn durch den Einsatz schneller Festplatten zu erreichen - ganz einfache Aussage: &#039;&#039;je schneller desto besser ;-)&#039;&#039;. Konkret bietet eine SSD mit mind. 250MB/s Datenzugriff eine ordentliche Basis für jedes datengestützte System, wie den fhem.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenmenge größer und die Abfragen komplexer werden, müssen natürlich irgendwann auch die Prozessorleistung und der Arbeitsspeicher mit wachsen. Aber auch an einem Raspi wird eine SSD deutlich performanter sein, als eine einfache SD-Karte oder eine klassische rotierende Festplatte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31051</id>
		<title>DbLog-MySQL</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31051"/>
		<updated>2019-07-29T09:37:24Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Dateigrösse mitloggen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Da bei der Inbetriebnahme und der Migration von MySQL bzw MariaDB-Datenbanken einige kleine Fallen auftreten können, gibt&#039;s hier ein paar Hinweise.&lt;br /&gt;
&lt;br /&gt;
== Server version: 10.1.40-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04 ==&lt;br /&gt;
=== db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MariaDB-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp. Bei der Verwendung von MariaDB muss die MySQL-Datei verwendet werden.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; befindet sich das Script &#039;&#039;&#039;db_create_mysql.sql&#039;&#039;&#039; , welches eine neue Datenbank samt Tabellenstruktur anlegt. Ausserdem wird ein Benutzer &amp;quot;fhemuser&amp;quot; mit einem (zu ändernden) &amp;quot;fhempasswort&amp;quot; angelegt.&lt;br /&gt;
==== Primary Keys ====&lt;br /&gt;
===== current =====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== history =====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`history` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`TIMESTAMP`,`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer MariaDB-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der Shell-Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der MariaDB-Prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 # apt-get update &amp;amp;&amp;amp; apt-get install mariadb-server&lt;br /&gt;
&lt;br /&gt;
Nach der Installation muss das Script &lt;br /&gt;
 # mysql_secure_installation&lt;br /&gt;
ausgeführt werden. Dieses legt ein root-Passwort fest, entfernt die anonymen User und entfernt die test-Datenbank.&lt;br /&gt;
Empfohlen wird, alle Fragen mit &#039;&#039;&#039;Y&#039;&#039;&#039; zu beantworten.&lt;br /&gt;
&lt;br /&gt;
Zum Test mit MariaDB verbinden:&lt;br /&gt;
 # mysql -u root -p&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen:&lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  exit&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang als fhemuser testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show databases;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show Databases einfügen &amp;gt;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden.&lt;br /&gt;
Um die Tabellen zu sehen, kann man den folgenden Befehl eingeben: &lt;br /&gt;
&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show tables;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show tables einfügen &amp;gt;&amp;gt;&lt;br /&gt;
, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
== Datenbank reparieren ==&lt;br /&gt;
Es kann immer wieder mal vorkommen, dass Datenbanken Fehler enthalten. Das muss im Alltag garnicht auffallen und auch nicht immer schlimm enden. Wenn man auf der SQL-Konsole aber bspw. eine Meldung &amp;lt;code&amp;gt;Error: database disk image is malformed&amp;lt;/code&amp;gt; erhält, sollte man ein Reparatur vornehmen.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
Die folgenden Schritte beschreiben, wie man eine SQLite-DB reparieren kann (Quelle: [http://techblog.dorogin.com/2011/05/sqliteexception-database-disk-image-is.html]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
DB öffnen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Integritäts-Check durchführen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sqlite&amp;gt; pragma integrity_check;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kommt hier ein &amp;quot;ok&amp;quot; ist die DB gesund. Ansonsten erscheint etwas wie&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
*** in database main ***&lt;br /&gt;
On tree page 118786 cell 1: Rowid 75 out of order (previous was 816660)&lt;br /&gt;
On tree page 118786 cell 4: Rowid 815704 out of order (previous was 816727)&lt;br /&gt;
Corruption detected in cell 0 on page 118786&lt;br /&gt;
Multiple uses for byte 132 of page 118786&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Datenbank-Dump erstellen (Export gesamten DB in die Datei &amp;quot;dump_all_20160516_1043.sql&amp;quot;) und DB verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .mode insert&lt;br /&gt;
sqlite&amp;gt; .output dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; .dump&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Neue Datenbank erstellen und den Dump einlesen, Integritäts-Check machen und verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem-neu.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .read dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; pragma integrity_check;&lt;br /&gt;
ok&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Spätestens jetzt FHEM stoppen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem stop&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Alte DB sichern und neue aktivieren:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo mv fhem.db fhem.db.sv_20160516&lt;br /&gt;
sudo mv fhem-neu.db fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Kontrollieren, dass die neue DB die gleichen Rechte wie die alte DB hat (und ggf. korrigieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 root root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
~/fhem$ sudo chown fhem:root fhem.db&lt;br /&gt;
&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 fhem root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
FHEM wieder starten (und natürlich kontrollieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem start&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. &lt;br /&gt;
Zur Ermittlung der MariaDB-Größe kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
== Performance-Optimierung ==&lt;br /&gt;
Auch eine Datenbank kann mit der Zeit langsamer werden. Dies hängt von mehreren Faktoren ab:&lt;br /&gt;
* Menge der gelogten Daten (zB. &amp;gt; 4-5 GB)&lt;br /&gt;
* Eingesetzte Hardware (zB. langsame SD-Karte vs. schnelle SSD)&lt;br /&gt;
* Eingesetztes Datenbanksystem (zB. SQLite, MySQL)&lt;br /&gt;
* Komplexität der Abfragen (zB. für aufwändige Graphen oder Berechnungen)&lt;br /&gt;
&lt;br /&gt;
Diese Punkte sollen im folgenden diskutiert werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komplexität der Abfragen ===&lt;br /&gt;
Dies ist kein Problem der Datenbank, sondern rein der Abfrage. Dem entsprechend muss die Optimierung auch in der Abfrage oder im Skript gesucht werden. Dies ist nicht Ziel dieses Abschnittes und wird hier nicht weiter behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetztes Datenbanksystem ===&lt;br /&gt;
Welches Datenbanksystem eingesetzt wird (zB. SQLite oder MySQL) hat auf die Performance der Datenbank gar keinen so großen Einfluss, wie vielleicht zuerst gedacht. Selbst SQLite kann problemlos Datenbanken mit etlichen GB Größe performant verarbeiten. Der Flaschenhals ist hier viel mehr die darunter liegende Hardware (s.u.).&lt;br /&gt;
&lt;br /&gt;
Die Performance der Datenbank an sich, kann aber durch verschiedene Maßnahmen verbessert werden:&lt;br /&gt;
* Pflegemaßnahmen bzgl. der Daten&lt;br /&gt;
* &#039;&#039;&#039;Erstellung von Indizes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Menge der Daten und Pflegemaßnahmen bzgl. der Daten ====&lt;br /&gt;
Die Menge der geloggten Daten hat natürlich Einfluss auf die Geschwindigkeit von Abfragen - je mehr Daten vorhanden sind, desto mehr Daten müssen auch durchforstet werden um eine Abfrage zu bedienen. Die Reduzierung der geloggten Datenmenge hat also direkten Einfluss auf die Größe und damit auch die Geschwindigkeit der Datenbank. Die Menge der zu loggenden Daten lässt sich an zwei Stellen einschränken:&lt;br /&gt;
* bei der Definition jedes Devices (s. Kapitel oben)&lt;br /&gt;
* bei der Festlegung des fhem-weiten Log-Levels (s. [[Loglevel]])&lt;br /&gt;
&lt;br /&gt;
Die Menge der bereits geloggten Daten kann zB. mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] verringert und optimiert werden, bspw.&lt;br /&gt;
* löschen unnötiger Daten&lt;br /&gt;
* vacuum der Datenbank&lt;br /&gt;
&lt;br /&gt;
Insgesamt haben diese Maßnahmen aber nur einen eingeschränkten Effekt auf die Performance der DB. Deutlich effektiver ist die Erstellung eines Index (s.u.).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Erstellung von Indizes ====&lt;br /&gt;
Die Erstellung von Indizes hat mit Abstand den größten Einfluss auf die Performance einer Datenbank (auf unveränderter Hardware). Extrem zusammengefasst ist ein Index eine extrem optimiertes Nachschlageverzeichnis für einen bestimmten Typ Daten (ein Index wie im Buch halt). Eine wunderbare Einführung in Indizes bietet [[https://use-the-index-luke.com/de|https://use-the-index-luke.com]].&lt;br /&gt;
&lt;br /&gt;
In fhem sind Indizes sogar sehr einfach einzurichten da die Datenbank-Nutzung sehr stark vorgegeben ist. Nahezu jede Abfrage folgt dem Schema &#039;&#039;Device -&amp;gt; Reading -&amp;gt; Datum -&amp;gt; Wert&#039;&#039;. Ein Index kann genau diese Abfrage bedienen und beschleunigen. Ein Index nur über die Devices wäre ein erster Schritt, brächte aber noch keinen großen Gewinn (wie um Link oben gut beschrieben). Über die gesamten ersten drei Schritte erstellt (Device -&amp;gt; Reading -&amp;gt; Datum) bringt der Index aber sofort eine deutliche Geschwindigkeitssteigerung.&lt;br /&gt;
&lt;br /&gt;
Die Erstellung eines Index erfolgt direkt in der Datenbank (und nicht aus fhem heraus), hier am Beispiel einer SQLite-DB:&lt;br /&gt;
&lt;br /&gt;
Öffnen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; sudo sqlite3 fhem.db &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugen des Index auf der DB-Konsole (das Semikolon am Ende ist wichtig):&lt;br /&gt;
&amp;lt;pre&amp;gt; create index idx_device_reading_timestamp on history (device, reading, timestamp); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verlassen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; .exit &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzig zu berücksichtigen ist, dass dieser Index die Datenbank um bis zu 1/3 vergrößert. Er kann aber bei Bedarf auch wieder entfernt werden. Bei meiner 15 GB SQLite-Datei (auf einem Mac Mini mit SSD) hat dies ca. 15 min gedauert (den fhem hatte ich vorsichtshalber währenddessen deaktiviert).&lt;br /&gt;
&lt;br /&gt;
Sollte jemand spezielle Berechnungen oder Skripte ausführen, die nach einem anderen Abfrage-Schema arbeiten, könnte man dafür spezialisierte zusätzliche Indizes erstellen. Das sollte aber dann mit dem Wissen des obigen Links erarbeitet werden, da dann etwas mehr Hintergrundwissen sehr hilfreich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== DB-Backup ====&lt;br /&gt;
Ein anderer Aspekt, der eigentlich nichts mit der Performance der DB zu tun hat, ist der Einfluss aufs Backup. Wird bspw. ein Systembackup per RSYNC gemacht, muss bei SQLite immer die komplette ggf. riesige Datei gesynct werden - bei MySQL würden nur die veränderten DB-Elemente gesynct. Dies soll hier nicht weiter vertieft werden, sollte aber bei einer Gesamtstrategie bedacht werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetzte Hardware ===&lt;br /&gt;
fhem hat grundsätzlich sehr viele kleine Datenzugriffe, unabhängig davon ob FileLog oder DbLog eingesetzt wird. Deshalb ist der absolut größte Performance-Gewinn durch den Einsatz schneller Festplatten zu erreichen - ganz einfache Aussage: &#039;&#039;je schneller desto besser ;-)&#039;&#039;. Konkret bietet eine SSD mit mind. 250MB/s Datenzugriff eine ordentliche Basis für jedes datengestützte System, wie den fhem.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenmenge größer und die Abfragen komplexer werden, müssen natürlich irgendwann auch die Prozessorleistung und der Arbeitsspeicher mit wachsen. Aber auch an einem Raspi wird eine SSD deutlich performanter sein, als eine einfache SD-Karte oder eine klassische rotierende Festplatte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31050</id>
		<title>DbLog-MySQL</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31050"/>
		<updated>2019-07-29T09:35:13Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* SQLite-Datenbanken */ gelöscht&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Da bei der Inbetriebnahme und der Migration von MySQL bzw MariaDB-Datenbanken einige kleine Fallen auftreten können, gibt&#039;s hier ein paar Hinweise.&lt;br /&gt;
&lt;br /&gt;
== Server version: 10.1.40-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04 ==&lt;br /&gt;
=== db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MariaDB-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp. Bei der Verwendung von MariaDB muss die MySQL-Datei verwendet werden.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; befindet sich das Script &#039;&#039;&#039;db_create_mysql.sql&#039;&#039;&#039; , welches eine neue Datenbank samt Tabellenstruktur anlegt. Ausserdem wird ein Benutzer &amp;quot;fhemuser&amp;quot; mit einem (zu ändernden) &amp;quot;fhempasswort&amp;quot; angelegt.&lt;br /&gt;
==== Primary Keys ====&lt;br /&gt;
===== current =====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== history =====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`history` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`TIMESTAMP`,`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer MariaDB-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der Shell-Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der MariaDB-Prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 # apt-get update &amp;amp;&amp;amp; apt-get install mariadb-server&lt;br /&gt;
&lt;br /&gt;
Nach der Installation muss das Script &lt;br /&gt;
 # mysql_secure_installation&lt;br /&gt;
ausgeführt werden. Dieses legt ein root-Passwort fest, entfernt die anonymen User und entfernt die test-Datenbank.&lt;br /&gt;
Empfohlen wird, alle Fragen mit &#039;&#039;&#039;Y&#039;&#039;&#039; zu beantworten.&lt;br /&gt;
&lt;br /&gt;
Zum Test mit MariaDB verbinden:&lt;br /&gt;
 # mysql -u root -p&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen:&lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  exit&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang als fhemuser testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show databases;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show Databases einfügen &amp;gt;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden.&lt;br /&gt;
Um die Tabellen zu sehen, kann man den folgenden Befehl eingeben: &lt;br /&gt;
&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show tables;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show tables einfügen &amp;gt;&amp;gt;&lt;br /&gt;
, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
== Datenbank reparieren ==&lt;br /&gt;
Es kann immer wieder mal vorkommen, dass Datenbanken Fehler enthalten. Das muss im Alltag garnicht auffallen und auch nicht immer schlimm enden. Wenn man auf der SQL-Konsole aber bspw. eine Meldung &amp;lt;code&amp;gt;Error: database disk image is malformed&amp;lt;/code&amp;gt; erhält, sollte man ein Reparatur vornehmen.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
Die folgenden Schritte beschreiben, wie man eine SQLite-DB reparieren kann (Quelle: [http://techblog.dorogin.com/2011/05/sqliteexception-database-disk-image-is.html]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
DB öffnen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Integritäts-Check durchführen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sqlite&amp;gt; pragma integrity_check;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kommt hier ein &amp;quot;ok&amp;quot; ist die DB gesund. Ansonsten erscheint etwas wie&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
*** in database main ***&lt;br /&gt;
On tree page 118786 cell 1: Rowid 75 out of order (previous was 816660)&lt;br /&gt;
On tree page 118786 cell 4: Rowid 815704 out of order (previous was 816727)&lt;br /&gt;
Corruption detected in cell 0 on page 118786&lt;br /&gt;
Multiple uses for byte 132 of page 118786&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Datenbank-Dump erstellen (Export gesamten DB in die Datei &amp;quot;dump_all_20160516_1043.sql&amp;quot;) und DB verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .mode insert&lt;br /&gt;
sqlite&amp;gt; .output dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; .dump&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Neue Datenbank erstellen und den Dump einlesen, Integritäts-Check machen und verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem-neu.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .read dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; pragma integrity_check;&lt;br /&gt;
ok&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Spätestens jetzt FHEM stoppen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem stop&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Alte DB sichern und neue aktivieren:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo mv fhem.db fhem.db.sv_20160516&lt;br /&gt;
sudo mv fhem-neu.db fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Kontrollieren, dass die neue DB die gleichen Rechte wie die alte DB hat (und ggf. korrigieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 root root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
~/fhem$ sudo chown fhem:root fhem.db&lt;br /&gt;
&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 fhem root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
FHEM wieder starten (und natürlich kontrollieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem start&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. Diese geschieht mittels des Userreadings, welches man vorteilshafterweise mit im DbLog-device anlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;attr myDbLog userReadings DbFileSize:reduceLogState.* { (split(&#039; &#039;,`du -m fhem.db`))[0] }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels dieses Attributs wird die Grösse der .db-Datei immer nach dem Ausführen des ReduceLog in das Reading &amp;quot;DbFileSize&amp;quot; in ganzzahligen MByte abgelegt.&lt;br /&gt;
&lt;br /&gt;
Basierend auf diesem Reading können dann weitere Aktionen, beispielsweise ein Plot, erstellt werden.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist für SQLite verwendbar. Zur Ermittlung der DB-Größe andere DB-Typen (aber auch für SQLite nutzbar) kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Performance-Optimierung ==&lt;br /&gt;
Auch eine Datenbank kann mit der Zeit langsamer werden. Dies hängt von mehreren Faktoren ab:&lt;br /&gt;
* Menge der gelogten Daten (zB. &amp;gt; 4-5 GB)&lt;br /&gt;
* Eingesetzte Hardware (zB. langsame SD-Karte vs. schnelle SSD)&lt;br /&gt;
* Eingesetztes Datenbanksystem (zB. SQLite, MySQL)&lt;br /&gt;
* Komplexität der Abfragen (zB. für aufwändige Graphen oder Berechnungen)&lt;br /&gt;
&lt;br /&gt;
Diese Punkte sollen im folgenden diskutiert werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komplexität der Abfragen ===&lt;br /&gt;
Dies ist kein Problem der Datenbank, sondern rein der Abfrage. Dem entsprechend muss die Optimierung auch in der Abfrage oder im Skript gesucht werden. Dies ist nicht Ziel dieses Abschnittes und wird hier nicht weiter behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetztes Datenbanksystem ===&lt;br /&gt;
Welches Datenbanksystem eingesetzt wird (zB. SQLite oder MySQL) hat auf die Performance der Datenbank gar keinen so großen Einfluss, wie vielleicht zuerst gedacht. Selbst SQLite kann problemlos Datenbanken mit etlichen GB Größe performant verarbeiten. Der Flaschenhals ist hier viel mehr die darunter liegende Hardware (s.u.).&lt;br /&gt;
&lt;br /&gt;
Die Performance der Datenbank an sich, kann aber durch verschiedene Maßnahmen verbessert werden:&lt;br /&gt;
* Pflegemaßnahmen bzgl. der Daten&lt;br /&gt;
* &#039;&#039;&#039;Erstellung von Indizes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Menge der Daten und Pflegemaßnahmen bzgl. der Daten ====&lt;br /&gt;
Die Menge der geloggten Daten hat natürlich Einfluss auf die Geschwindigkeit von Abfragen - je mehr Daten vorhanden sind, desto mehr Daten müssen auch durchforstet werden um eine Abfrage zu bedienen. Die Reduzierung der geloggten Datenmenge hat also direkten Einfluss auf die Größe und damit auch die Geschwindigkeit der Datenbank. Die Menge der zu loggenden Daten lässt sich an zwei Stellen einschränken:&lt;br /&gt;
* bei der Definition jedes Devices (s. Kapitel oben)&lt;br /&gt;
* bei der Festlegung des fhem-weiten Log-Levels (s. [[Loglevel]])&lt;br /&gt;
&lt;br /&gt;
Die Menge der bereits geloggten Daten kann zB. mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] verringert und optimiert werden, bspw.&lt;br /&gt;
* löschen unnötiger Daten&lt;br /&gt;
* vacuum der Datenbank&lt;br /&gt;
&lt;br /&gt;
Insgesamt haben diese Maßnahmen aber nur einen eingeschränkten Effekt auf die Performance der DB. Deutlich effektiver ist die Erstellung eines Index (s.u.).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Erstellung von Indizes ====&lt;br /&gt;
Die Erstellung von Indizes hat mit Abstand den größten Einfluss auf die Performance einer Datenbank (auf unveränderter Hardware). Extrem zusammengefasst ist ein Index eine extrem optimiertes Nachschlageverzeichnis für einen bestimmten Typ Daten (ein Index wie im Buch halt). Eine wunderbare Einführung in Indizes bietet [[https://use-the-index-luke.com/de|https://use-the-index-luke.com]].&lt;br /&gt;
&lt;br /&gt;
In fhem sind Indizes sogar sehr einfach einzurichten da die Datenbank-Nutzung sehr stark vorgegeben ist. Nahezu jede Abfrage folgt dem Schema &#039;&#039;Device -&amp;gt; Reading -&amp;gt; Datum -&amp;gt; Wert&#039;&#039;. Ein Index kann genau diese Abfrage bedienen und beschleunigen. Ein Index nur über die Devices wäre ein erster Schritt, brächte aber noch keinen großen Gewinn (wie um Link oben gut beschrieben). Über die gesamten ersten drei Schritte erstellt (Device -&amp;gt; Reading -&amp;gt; Datum) bringt der Index aber sofort eine deutliche Geschwindigkeitssteigerung.&lt;br /&gt;
&lt;br /&gt;
Die Erstellung eines Index erfolgt direkt in der Datenbank (und nicht aus fhem heraus), hier am Beispiel einer SQLite-DB:&lt;br /&gt;
&lt;br /&gt;
Öffnen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; sudo sqlite3 fhem.db &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugen des Index auf der DB-Konsole (das Semikolon am Ende ist wichtig):&lt;br /&gt;
&amp;lt;pre&amp;gt; create index idx_device_reading_timestamp on history (device, reading, timestamp); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verlassen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; .exit &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzig zu berücksichtigen ist, dass dieser Index die Datenbank um bis zu 1/3 vergrößert. Er kann aber bei Bedarf auch wieder entfernt werden. Bei meiner 15 GB SQLite-Datei (auf einem Mac Mini mit SSD) hat dies ca. 15 min gedauert (den fhem hatte ich vorsichtshalber währenddessen deaktiviert).&lt;br /&gt;
&lt;br /&gt;
Sollte jemand spezielle Berechnungen oder Skripte ausführen, die nach einem anderen Abfrage-Schema arbeiten, könnte man dafür spezialisierte zusätzliche Indizes erstellen. Das sollte aber dann mit dem Wissen des obigen Links erarbeitet werden, da dann etwas mehr Hintergrundwissen sehr hilfreich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== DB-Backup ====&lt;br /&gt;
Ein anderer Aspekt, der eigentlich nichts mit der Performance der DB zu tun hat, ist der Einfluss aufs Backup. Wird bspw. ein Systembackup per RSYNC gemacht, muss bei SQLite immer die komplette ggf. riesige Datei gesynct werden - bei MySQL würden nur die veränderten DB-Elemente gesynct. Dies soll hier nicht weiter vertieft werden, sollte aber bei einer Gesamtstrategie bedacht werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetzte Hardware ===&lt;br /&gt;
fhem hat grundsätzlich sehr viele kleine Datenzugriffe, unabhängig davon ob FileLog oder DbLog eingesetzt wird. Deshalb ist der absolut größte Performance-Gewinn durch den Einsatz schneller Festplatten zu erreichen - ganz einfache Aussage: &#039;&#039;je schneller desto besser ;-)&#039;&#039;. Konkret bietet eine SSD mit mind. 250MB/s Datenzugriff eine ordentliche Basis für jedes datengestützte System, wie den fhem.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenmenge größer und die Abfragen komplexer werden, müssen natürlich irgendwann auch die Prozessorleistung und der Arbeitsspeicher mit wachsen. Aber auch an einem Raspi wird eine SSD deutlich performanter sein, als eine einfache SD-Karte oder eine klassische rotierende Festplatte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31049</id>
		<title>DbLog-MySQL</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31049"/>
		<updated>2019-07-29T09:34:24Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Werte auslesen */ gelöscht&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Da bei der Inbetriebnahme und der Migration von MySQL bzw MariaDB-Datenbanken einige kleine Fallen auftreten können, gibt&#039;s hier ein paar Hinweise.&lt;br /&gt;
&lt;br /&gt;
== Server version: 10.1.40-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04 ==&lt;br /&gt;
=== db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MariaDB-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp. Bei der Verwendung von MariaDB muss die MySQL-Datei verwendet werden.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; befindet sich das Script &#039;&#039;&#039;db_create_mysql.sql&#039;&#039;&#039; , welches eine neue Datenbank samt Tabellenstruktur anlegt. Ausserdem wird ein Benutzer &amp;quot;fhemuser&amp;quot; mit einem (zu ändernden) &amp;quot;fhempasswort&amp;quot; angelegt.&lt;br /&gt;
==== Primary Keys ====&lt;br /&gt;
===== current =====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== history =====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`history` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`TIMESTAMP`,`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer MariaDB-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der Shell-Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der MariaDB-Prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 # apt-get update &amp;amp;&amp;amp; apt-get install mariadb-server&lt;br /&gt;
&lt;br /&gt;
Nach der Installation muss das Script &lt;br /&gt;
 # mysql_secure_installation&lt;br /&gt;
ausgeführt werden. Dieses legt ein root-Passwort fest, entfernt die anonymen User und entfernt die test-Datenbank.&lt;br /&gt;
Empfohlen wird, alle Fragen mit &#039;&#039;&#039;Y&#039;&#039;&#039; zu beantworten.&lt;br /&gt;
&lt;br /&gt;
Zum Test mit MariaDB verbinden:&lt;br /&gt;
 # mysql -u root -p&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen:&lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  exit&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang als fhemuser testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show databases;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show Databases einfügen &amp;gt;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden.&lt;br /&gt;
Um die Tabellen zu sehen, kann man den folgenden Befehl eingeben: &lt;br /&gt;
&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show tables;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show tables einfügen &amp;gt;&amp;gt;&lt;br /&gt;
, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
&#039;&#039;&#039;Öffnen der DB unter Linux:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(Es werden Schreibrechte benötigt,ohne kann man die DB zwar öffnen, aber nichts machen)&lt;br /&gt;
 sudo sqlite3 fhem.db&lt;br /&gt;
Dadurch öffnet sich ein SQL-Konsole, auf der alle weiteren Befehle ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schliessen der DB:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .exit&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfe anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .help&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Tabellen anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .tables&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Das Schema der DB anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(vgl. oben [[DbLog#Datenbanken]] und [[DbLog#Beispiel: Anlegen und Nutzung einer SQLite-Datenbank]])&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .schema&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Eintäge anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Die Einträge liegen alle in der Tabelle &amp;quot;History&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ganz wichtig&#039;&#039;&#039; ist immer das &amp;quot;;&amp;quot; am Ende Zeile (bei allen Kommandos, die nicht mit einem &amp;quot;.&amp;quot; anfangen). Wenn es vergessen wurde zeigt die Konsole solange neue Zeilen bis ein &amp;quot;;&amp;quot; eingegeben wird. So kann ein Befehl auch bequem über mehrere Zeilen geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY;&lt;br /&gt;
&lt;br /&gt;
Dies kann sehr lange dauern und kann ggf. mit &amp;lt;code&amp;gt;STRG-C&amp;lt;/code&amp;gt; abgebrochen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;where&amp;lt;/code&amp;gt;-Statements werden Strings in einfache Anführungsstriche gesetzt, Zahlen nicht.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LÖSCHEN aller Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Löschen kann nicht rückgängig gemacht werden!! Also IMMER erst die entsprechenden SELECT-Statements solange verfeinern bis wirklich nur die gewünschten Einträge angezeigt werden. Dann das &amp;lt;code&amp;gt;select *&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; ersetzen.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; delete from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank reparieren ==&lt;br /&gt;
Es kann immer wieder mal vorkommen, dass Datenbanken Fehler enthalten. Das muss im Alltag garnicht auffallen und auch nicht immer schlimm enden. Wenn man auf der SQL-Konsole aber bspw. eine Meldung &amp;lt;code&amp;gt;Error: database disk image is malformed&amp;lt;/code&amp;gt; erhält, sollte man ein Reparatur vornehmen.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
Die folgenden Schritte beschreiben, wie man eine SQLite-DB reparieren kann (Quelle: [http://techblog.dorogin.com/2011/05/sqliteexception-database-disk-image-is.html]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
DB öffnen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Integritäts-Check durchführen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sqlite&amp;gt; pragma integrity_check;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kommt hier ein &amp;quot;ok&amp;quot; ist die DB gesund. Ansonsten erscheint etwas wie&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
*** in database main ***&lt;br /&gt;
On tree page 118786 cell 1: Rowid 75 out of order (previous was 816660)&lt;br /&gt;
On tree page 118786 cell 4: Rowid 815704 out of order (previous was 816727)&lt;br /&gt;
Corruption detected in cell 0 on page 118786&lt;br /&gt;
Multiple uses for byte 132 of page 118786&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Datenbank-Dump erstellen (Export gesamten DB in die Datei &amp;quot;dump_all_20160516_1043.sql&amp;quot;) und DB verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .mode insert&lt;br /&gt;
sqlite&amp;gt; .output dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; .dump&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Neue Datenbank erstellen und den Dump einlesen, Integritäts-Check machen und verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem-neu.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .read dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; pragma integrity_check;&lt;br /&gt;
ok&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Spätestens jetzt FHEM stoppen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem stop&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Alte DB sichern und neue aktivieren:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo mv fhem.db fhem.db.sv_20160516&lt;br /&gt;
sudo mv fhem-neu.db fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Kontrollieren, dass die neue DB die gleichen Rechte wie die alte DB hat (und ggf. korrigieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 root root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
~/fhem$ sudo chown fhem:root fhem.db&lt;br /&gt;
&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 fhem root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
FHEM wieder starten (und natürlich kontrollieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem start&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. Diese geschieht mittels des Userreadings, welches man vorteilshafterweise mit im DbLog-device anlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;attr myDbLog userReadings DbFileSize:reduceLogState.* { (split(&#039; &#039;,`du -m fhem.db`))[0] }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels dieses Attributs wird die Grösse der .db-Datei immer nach dem Ausführen des ReduceLog in das Reading &amp;quot;DbFileSize&amp;quot; in ganzzahligen MByte abgelegt.&lt;br /&gt;
&lt;br /&gt;
Basierend auf diesem Reading können dann weitere Aktionen, beispielsweise ein Plot, erstellt werden.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist für SQLite verwendbar. Zur Ermittlung der DB-Größe andere DB-Typen (aber auch für SQLite nutzbar) kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Performance-Optimierung ==&lt;br /&gt;
Auch eine Datenbank kann mit der Zeit langsamer werden. Dies hängt von mehreren Faktoren ab:&lt;br /&gt;
* Menge der gelogten Daten (zB. &amp;gt; 4-5 GB)&lt;br /&gt;
* Eingesetzte Hardware (zB. langsame SD-Karte vs. schnelle SSD)&lt;br /&gt;
* Eingesetztes Datenbanksystem (zB. SQLite, MySQL)&lt;br /&gt;
* Komplexität der Abfragen (zB. für aufwändige Graphen oder Berechnungen)&lt;br /&gt;
&lt;br /&gt;
Diese Punkte sollen im folgenden diskutiert werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komplexität der Abfragen ===&lt;br /&gt;
Dies ist kein Problem der Datenbank, sondern rein der Abfrage. Dem entsprechend muss die Optimierung auch in der Abfrage oder im Skript gesucht werden. Dies ist nicht Ziel dieses Abschnittes und wird hier nicht weiter behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetztes Datenbanksystem ===&lt;br /&gt;
Welches Datenbanksystem eingesetzt wird (zB. SQLite oder MySQL) hat auf die Performance der Datenbank gar keinen so großen Einfluss, wie vielleicht zuerst gedacht. Selbst SQLite kann problemlos Datenbanken mit etlichen GB Größe performant verarbeiten. Der Flaschenhals ist hier viel mehr die darunter liegende Hardware (s.u.).&lt;br /&gt;
&lt;br /&gt;
Die Performance der Datenbank an sich, kann aber durch verschiedene Maßnahmen verbessert werden:&lt;br /&gt;
* Pflegemaßnahmen bzgl. der Daten&lt;br /&gt;
* &#039;&#039;&#039;Erstellung von Indizes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Menge der Daten und Pflegemaßnahmen bzgl. der Daten ====&lt;br /&gt;
Die Menge der geloggten Daten hat natürlich Einfluss auf die Geschwindigkeit von Abfragen - je mehr Daten vorhanden sind, desto mehr Daten müssen auch durchforstet werden um eine Abfrage zu bedienen. Die Reduzierung der geloggten Datenmenge hat also direkten Einfluss auf die Größe und damit auch die Geschwindigkeit der Datenbank. Die Menge der zu loggenden Daten lässt sich an zwei Stellen einschränken:&lt;br /&gt;
* bei der Definition jedes Devices (s. Kapitel oben)&lt;br /&gt;
* bei der Festlegung des fhem-weiten Log-Levels (s. [[Loglevel]])&lt;br /&gt;
&lt;br /&gt;
Die Menge der bereits geloggten Daten kann zB. mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] verringert und optimiert werden, bspw.&lt;br /&gt;
* löschen unnötiger Daten&lt;br /&gt;
* vacuum der Datenbank&lt;br /&gt;
&lt;br /&gt;
Insgesamt haben diese Maßnahmen aber nur einen eingeschränkten Effekt auf die Performance der DB. Deutlich effektiver ist die Erstellung eines Index (s.u.).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Erstellung von Indizes ====&lt;br /&gt;
Die Erstellung von Indizes hat mit Abstand den größten Einfluss auf die Performance einer Datenbank (auf unveränderter Hardware). Extrem zusammengefasst ist ein Index eine extrem optimiertes Nachschlageverzeichnis für einen bestimmten Typ Daten (ein Index wie im Buch halt). Eine wunderbare Einführung in Indizes bietet [[https://use-the-index-luke.com/de|https://use-the-index-luke.com]].&lt;br /&gt;
&lt;br /&gt;
In fhem sind Indizes sogar sehr einfach einzurichten da die Datenbank-Nutzung sehr stark vorgegeben ist. Nahezu jede Abfrage folgt dem Schema &#039;&#039;Device -&amp;gt; Reading -&amp;gt; Datum -&amp;gt; Wert&#039;&#039;. Ein Index kann genau diese Abfrage bedienen und beschleunigen. Ein Index nur über die Devices wäre ein erster Schritt, brächte aber noch keinen großen Gewinn (wie um Link oben gut beschrieben). Über die gesamten ersten drei Schritte erstellt (Device -&amp;gt; Reading -&amp;gt; Datum) bringt der Index aber sofort eine deutliche Geschwindigkeitssteigerung.&lt;br /&gt;
&lt;br /&gt;
Die Erstellung eines Index erfolgt direkt in der Datenbank (und nicht aus fhem heraus), hier am Beispiel einer SQLite-DB:&lt;br /&gt;
&lt;br /&gt;
Öffnen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; sudo sqlite3 fhem.db &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugen des Index auf der DB-Konsole (das Semikolon am Ende ist wichtig):&lt;br /&gt;
&amp;lt;pre&amp;gt; create index idx_device_reading_timestamp on history (device, reading, timestamp); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verlassen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; .exit &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzig zu berücksichtigen ist, dass dieser Index die Datenbank um bis zu 1/3 vergrößert. Er kann aber bei Bedarf auch wieder entfernt werden. Bei meiner 15 GB SQLite-Datei (auf einem Mac Mini mit SSD) hat dies ca. 15 min gedauert (den fhem hatte ich vorsichtshalber währenddessen deaktiviert).&lt;br /&gt;
&lt;br /&gt;
Sollte jemand spezielle Berechnungen oder Skripte ausführen, die nach einem anderen Abfrage-Schema arbeiten, könnte man dafür spezialisierte zusätzliche Indizes erstellen. Das sollte aber dann mit dem Wissen des obigen Links erarbeitet werden, da dann etwas mehr Hintergrundwissen sehr hilfreich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== DB-Backup ====&lt;br /&gt;
Ein anderer Aspekt, der eigentlich nichts mit der Performance der DB zu tun hat, ist der Einfluss aufs Backup. Wird bspw. ein Systembackup per RSYNC gemacht, muss bei SQLite immer die komplette ggf. riesige Datei gesynct werden - bei MySQL würden nur die veränderten DB-Elemente gesynct. Dies soll hier nicht weiter vertieft werden, sollte aber bei einer Gesamtstrategie bedacht werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetzte Hardware ===&lt;br /&gt;
fhem hat grundsätzlich sehr viele kleine Datenzugriffe, unabhängig davon ob FileLog oder DbLog eingesetzt wird. Deshalb ist der absolut größte Performance-Gewinn durch den Einsatz schneller Festplatten zu erreichen - ganz einfache Aussage: &#039;&#039;je schneller desto besser ;-)&#039;&#039;. Konkret bietet eine SSD mit mind. 250MB/s Datenzugriff eine ordentliche Basis für jedes datengestützte System, wie den fhem.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenmenge größer und die Abfragen komplexer werden, müssen natürlich irgendwann auch die Prozessorleistung und der Arbeitsspeicher mit wachsen. Aber auch an einem Raspi wird eine SSD deutlich performanter sein, als eine einfache SD-Karte oder eine klassische rotierende Festplatte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31048</id>
		<title>DbLog-MySQL</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31048"/>
		<updated>2019-07-29T09:32:27Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Integration von DBLog in eigene Module */  gelöscht&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Da bei der Inbetriebnahme und der Migration von MySQL bzw MariaDB-Datenbanken einige kleine Fallen auftreten können, gibt&#039;s hier ein paar Hinweise.&lt;br /&gt;
&lt;br /&gt;
== Server version: 10.1.40-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04 ==&lt;br /&gt;
=== db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MariaDB-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp. Bei der Verwendung von MariaDB muss die MySQL-Datei verwendet werden.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; befindet sich das Script &#039;&#039;&#039;db_create_mysql.sql&#039;&#039;&#039; , welches eine neue Datenbank samt Tabellenstruktur anlegt. Ausserdem wird ein Benutzer &amp;quot;fhemuser&amp;quot; mit einem (zu ändernden) &amp;quot;fhempasswort&amp;quot; angelegt.&lt;br /&gt;
==== Primary Keys ====&lt;br /&gt;
===== current =====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== history =====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`history` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`TIMESTAMP`,`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer MariaDB-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der Shell-Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der MariaDB-Prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 # apt-get update &amp;amp;&amp;amp; apt-get install mariadb-server&lt;br /&gt;
&lt;br /&gt;
Nach der Installation muss das Script &lt;br /&gt;
 # mysql_secure_installation&lt;br /&gt;
ausgeführt werden. Dieses legt ein root-Passwort fest, entfernt die anonymen User und entfernt die test-Datenbank.&lt;br /&gt;
Empfohlen wird, alle Fragen mit &#039;&#039;&#039;Y&#039;&#039;&#039; zu beantworten.&lt;br /&gt;
&lt;br /&gt;
Zum Test mit MariaDB verbinden:&lt;br /&gt;
 # mysql -u root -p&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen:&lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  exit&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang als fhemuser testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show databases;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show Databases einfügen &amp;gt;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden.&lt;br /&gt;
Um die Tabellen zu sehen, kann man den folgenden Befehl eingeben: &lt;br /&gt;
&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show tables;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show tables einfügen &amp;gt;&amp;gt;&lt;br /&gt;
, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Werte auslesen ==&lt;br /&gt;
Manchmal möchte man Daten aus den Logs abrufen ohne händisch in der Datenbank herumzuwühlen (s.u.). Dies ist insb. auch dann hilfreich, wenn man eigenen Funktionen, Notifys oder spezielle Plots entwirft, bei denen man auf Logdaten zugreifen möchte.&lt;br /&gt;
&lt;br /&gt;
Grundsätzlich beschrieben ist dies in der {{Link2CmdRef|Lang=de|Anker=DbLog}} und unterscheidet sich minimal (aber entscheidend) von der Struktur bei [[FileLog#Werte_auslesen|FileLogs]].&lt;br /&gt;
&lt;br /&gt;
Hier ein paar Beispiele, was man damit anstellen kann:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01 2016-10-03 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor vom 01.10.-03.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor von 8-16 Uhr am 01.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor:temperature&amp;lt;/code&amp;gt; nur die temperature Werte&lt;br /&gt;
* &amp;lt;code&amp;gt;{ ReadingsTimestamp(&amp;quot;meinSensor&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;0&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des aktuellen state des meinSensor&lt;br /&gt;
* &amp;lt;code&amp;gt;{ OldTimestamp(&amp;quot;meinSensor&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des letzten state des FHT_3a32&lt;br /&gt;
* &amp;lt;code&amp;gt;{ time_str2num(OldTimestamp(&amp;quot;meinSensor&amp;quot;)) }&amp;lt;/code&amp;gt; Timestamp in Sekunden des letzten state des meinSensor&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
&#039;&#039;&#039;Öffnen der DB unter Linux:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(Es werden Schreibrechte benötigt,ohne kann man die DB zwar öffnen, aber nichts machen)&lt;br /&gt;
 sudo sqlite3 fhem.db&lt;br /&gt;
Dadurch öffnet sich ein SQL-Konsole, auf der alle weiteren Befehle ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schliessen der DB:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .exit&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfe anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .help&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Tabellen anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .tables&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Das Schema der DB anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(vgl. oben [[DbLog#Datenbanken]] und [[DbLog#Beispiel: Anlegen und Nutzung einer SQLite-Datenbank]])&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .schema&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Eintäge anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Die Einträge liegen alle in der Tabelle &amp;quot;History&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ganz wichtig&#039;&#039;&#039; ist immer das &amp;quot;;&amp;quot; am Ende Zeile (bei allen Kommandos, die nicht mit einem &amp;quot;.&amp;quot; anfangen). Wenn es vergessen wurde zeigt die Konsole solange neue Zeilen bis ein &amp;quot;;&amp;quot; eingegeben wird. So kann ein Befehl auch bequem über mehrere Zeilen geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY;&lt;br /&gt;
&lt;br /&gt;
Dies kann sehr lange dauern und kann ggf. mit &amp;lt;code&amp;gt;STRG-C&amp;lt;/code&amp;gt; abgebrochen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;where&amp;lt;/code&amp;gt;-Statements werden Strings in einfache Anführungsstriche gesetzt, Zahlen nicht.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LÖSCHEN aller Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Löschen kann nicht rückgängig gemacht werden!! Also IMMER erst die entsprechenden SELECT-Statements solange verfeinern bis wirklich nur die gewünschten Einträge angezeigt werden. Dann das &amp;lt;code&amp;gt;select *&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; ersetzen.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; delete from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank reparieren ==&lt;br /&gt;
Es kann immer wieder mal vorkommen, dass Datenbanken Fehler enthalten. Das muss im Alltag garnicht auffallen und auch nicht immer schlimm enden. Wenn man auf der SQL-Konsole aber bspw. eine Meldung &amp;lt;code&amp;gt;Error: database disk image is malformed&amp;lt;/code&amp;gt; erhält, sollte man ein Reparatur vornehmen.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
Die folgenden Schritte beschreiben, wie man eine SQLite-DB reparieren kann (Quelle: [http://techblog.dorogin.com/2011/05/sqliteexception-database-disk-image-is.html]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
DB öffnen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Integritäts-Check durchführen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sqlite&amp;gt; pragma integrity_check;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kommt hier ein &amp;quot;ok&amp;quot; ist die DB gesund. Ansonsten erscheint etwas wie&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
*** in database main ***&lt;br /&gt;
On tree page 118786 cell 1: Rowid 75 out of order (previous was 816660)&lt;br /&gt;
On tree page 118786 cell 4: Rowid 815704 out of order (previous was 816727)&lt;br /&gt;
Corruption detected in cell 0 on page 118786&lt;br /&gt;
Multiple uses for byte 132 of page 118786&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Datenbank-Dump erstellen (Export gesamten DB in die Datei &amp;quot;dump_all_20160516_1043.sql&amp;quot;) und DB verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .mode insert&lt;br /&gt;
sqlite&amp;gt; .output dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; .dump&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Neue Datenbank erstellen und den Dump einlesen, Integritäts-Check machen und verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem-neu.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .read dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; pragma integrity_check;&lt;br /&gt;
ok&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Spätestens jetzt FHEM stoppen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem stop&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Alte DB sichern und neue aktivieren:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo mv fhem.db fhem.db.sv_20160516&lt;br /&gt;
sudo mv fhem-neu.db fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Kontrollieren, dass die neue DB die gleichen Rechte wie die alte DB hat (und ggf. korrigieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 root root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
~/fhem$ sudo chown fhem:root fhem.db&lt;br /&gt;
&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 fhem root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
FHEM wieder starten (und natürlich kontrollieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem start&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. Diese geschieht mittels des Userreadings, welches man vorteilshafterweise mit im DbLog-device anlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;attr myDbLog userReadings DbFileSize:reduceLogState.* { (split(&#039; &#039;,`du -m fhem.db`))[0] }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels dieses Attributs wird die Grösse der .db-Datei immer nach dem Ausführen des ReduceLog in das Reading &amp;quot;DbFileSize&amp;quot; in ganzzahligen MByte abgelegt.&lt;br /&gt;
&lt;br /&gt;
Basierend auf diesem Reading können dann weitere Aktionen, beispielsweise ein Plot, erstellt werden.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist für SQLite verwendbar. Zur Ermittlung der DB-Größe andere DB-Typen (aber auch für SQLite nutzbar) kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Performance-Optimierung ==&lt;br /&gt;
Auch eine Datenbank kann mit der Zeit langsamer werden. Dies hängt von mehreren Faktoren ab:&lt;br /&gt;
* Menge der gelogten Daten (zB. &amp;gt; 4-5 GB)&lt;br /&gt;
* Eingesetzte Hardware (zB. langsame SD-Karte vs. schnelle SSD)&lt;br /&gt;
* Eingesetztes Datenbanksystem (zB. SQLite, MySQL)&lt;br /&gt;
* Komplexität der Abfragen (zB. für aufwändige Graphen oder Berechnungen)&lt;br /&gt;
&lt;br /&gt;
Diese Punkte sollen im folgenden diskutiert werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komplexität der Abfragen ===&lt;br /&gt;
Dies ist kein Problem der Datenbank, sondern rein der Abfrage. Dem entsprechend muss die Optimierung auch in der Abfrage oder im Skript gesucht werden. Dies ist nicht Ziel dieses Abschnittes und wird hier nicht weiter behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetztes Datenbanksystem ===&lt;br /&gt;
Welches Datenbanksystem eingesetzt wird (zB. SQLite oder MySQL) hat auf die Performance der Datenbank gar keinen so großen Einfluss, wie vielleicht zuerst gedacht. Selbst SQLite kann problemlos Datenbanken mit etlichen GB Größe performant verarbeiten. Der Flaschenhals ist hier viel mehr die darunter liegende Hardware (s.u.).&lt;br /&gt;
&lt;br /&gt;
Die Performance der Datenbank an sich, kann aber durch verschiedene Maßnahmen verbessert werden:&lt;br /&gt;
* Pflegemaßnahmen bzgl. der Daten&lt;br /&gt;
* &#039;&#039;&#039;Erstellung von Indizes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Menge der Daten und Pflegemaßnahmen bzgl. der Daten ====&lt;br /&gt;
Die Menge der geloggten Daten hat natürlich Einfluss auf die Geschwindigkeit von Abfragen - je mehr Daten vorhanden sind, desto mehr Daten müssen auch durchforstet werden um eine Abfrage zu bedienen. Die Reduzierung der geloggten Datenmenge hat also direkten Einfluss auf die Größe und damit auch die Geschwindigkeit der Datenbank. Die Menge der zu loggenden Daten lässt sich an zwei Stellen einschränken:&lt;br /&gt;
* bei der Definition jedes Devices (s. Kapitel oben)&lt;br /&gt;
* bei der Festlegung des fhem-weiten Log-Levels (s. [[Loglevel]])&lt;br /&gt;
&lt;br /&gt;
Die Menge der bereits geloggten Daten kann zB. mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] verringert und optimiert werden, bspw.&lt;br /&gt;
* löschen unnötiger Daten&lt;br /&gt;
* vacuum der Datenbank&lt;br /&gt;
&lt;br /&gt;
Insgesamt haben diese Maßnahmen aber nur einen eingeschränkten Effekt auf die Performance der DB. Deutlich effektiver ist die Erstellung eines Index (s.u.).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Erstellung von Indizes ====&lt;br /&gt;
Die Erstellung von Indizes hat mit Abstand den größten Einfluss auf die Performance einer Datenbank (auf unveränderter Hardware). Extrem zusammengefasst ist ein Index eine extrem optimiertes Nachschlageverzeichnis für einen bestimmten Typ Daten (ein Index wie im Buch halt). Eine wunderbare Einführung in Indizes bietet [[https://use-the-index-luke.com/de|https://use-the-index-luke.com]].&lt;br /&gt;
&lt;br /&gt;
In fhem sind Indizes sogar sehr einfach einzurichten da die Datenbank-Nutzung sehr stark vorgegeben ist. Nahezu jede Abfrage folgt dem Schema &#039;&#039;Device -&amp;gt; Reading -&amp;gt; Datum -&amp;gt; Wert&#039;&#039;. Ein Index kann genau diese Abfrage bedienen und beschleunigen. Ein Index nur über die Devices wäre ein erster Schritt, brächte aber noch keinen großen Gewinn (wie um Link oben gut beschrieben). Über die gesamten ersten drei Schritte erstellt (Device -&amp;gt; Reading -&amp;gt; Datum) bringt der Index aber sofort eine deutliche Geschwindigkeitssteigerung.&lt;br /&gt;
&lt;br /&gt;
Die Erstellung eines Index erfolgt direkt in der Datenbank (und nicht aus fhem heraus), hier am Beispiel einer SQLite-DB:&lt;br /&gt;
&lt;br /&gt;
Öffnen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; sudo sqlite3 fhem.db &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugen des Index auf der DB-Konsole (das Semikolon am Ende ist wichtig):&lt;br /&gt;
&amp;lt;pre&amp;gt; create index idx_device_reading_timestamp on history (device, reading, timestamp); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verlassen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; .exit &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzig zu berücksichtigen ist, dass dieser Index die Datenbank um bis zu 1/3 vergrößert. Er kann aber bei Bedarf auch wieder entfernt werden. Bei meiner 15 GB SQLite-Datei (auf einem Mac Mini mit SSD) hat dies ca. 15 min gedauert (den fhem hatte ich vorsichtshalber währenddessen deaktiviert).&lt;br /&gt;
&lt;br /&gt;
Sollte jemand spezielle Berechnungen oder Skripte ausführen, die nach einem anderen Abfrage-Schema arbeiten, könnte man dafür spezialisierte zusätzliche Indizes erstellen. Das sollte aber dann mit dem Wissen des obigen Links erarbeitet werden, da dann etwas mehr Hintergrundwissen sehr hilfreich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== DB-Backup ====&lt;br /&gt;
Ein anderer Aspekt, der eigentlich nichts mit der Performance der DB zu tun hat, ist der Einfluss aufs Backup. Wird bspw. ein Systembackup per RSYNC gemacht, muss bei SQLite immer die komplette ggf. riesige Datei gesynct werden - bei MySQL würden nur die veränderten DB-Elemente gesynct. Dies soll hier nicht weiter vertieft werden, sollte aber bei einer Gesamtstrategie bedacht werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetzte Hardware ===&lt;br /&gt;
fhem hat grundsätzlich sehr viele kleine Datenzugriffe, unabhängig davon ob FileLog oder DbLog eingesetzt wird. Deshalb ist der absolut größte Performance-Gewinn durch den Einsatz schneller Festplatten zu erreichen - ganz einfache Aussage: &#039;&#039;je schneller desto besser ;-)&#039;&#039;. Konkret bietet eine SSD mit mind. 250MB/s Datenzugriff eine ordentliche Basis für jedes datengestützte System, wie den fhem.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenmenge größer und die Abfragen komplexer werden, müssen natürlich irgendwann auch die Prozessorleistung und der Arbeitsspeicher mit wachsen. Aber auch an einem Raspi wird eine SSD deutlich performanter sein, als eine einfache SD-Karte oder eine klassische rotierende Festplatte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31047</id>
		<title>DbLog-MySQL</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31047"/>
		<updated>2019-07-29T09:18:02Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Beispiel: Anlegen und Nutzung einer MariaDB-Datenbank */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Da bei der Inbetriebnahme und der Migration von MySQL bzw MariaDB-Datenbanken einige kleine Fallen auftreten können, gibt&#039;s hier ein paar Hinweise.&lt;br /&gt;
&lt;br /&gt;
== Server version: 10.1.40-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04 ==&lt;br /&gt;
=== db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MariaDB-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp. Bei der Verwendung von MariaDB muss die MySQL-Datei verwendet werden.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; befindet sich das Script &#039;&#039;&#039;db_create_mysql.sql&#039;&#039;&#039; , welches eine neue Datenbank samt Tabellenstruktur anlegt. Ausserdem wird ein Benutzer &amp;quot;fhemuser&amp;quot; mit einem (zu ändernden) &amp;quot;fhempasswort&amp;quot; angelegt.&lt;br /&gt;
==== Primary Keys ====&lt;br /&gt;
===== current =====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== history =====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`history` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`TIMESTAMP`,`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer MariaDB-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der Shell-Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der MariaDB-Prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 # apt-get update &amp;amp;&amp;amp; apt-get install mariadb-server&lt;br /&gt;
&lt;br /&gt;
Nach der Installation muss das Script &lt;br /&gt;
 # mysql_secure_installation&lt;br /&gt;
ausgeführt werden. Dieses legt ein root-Passwort fest, entfernt die anonymen User und entfernt die test-Datenbank.&lt;br /&gt;
Empfohlen wird, alle Fragen mit &#039;&#039;&#039;Y&#039;&#039;&#039; zu beantworten.&lt;br /&gt;
&lt;br /&gt;
Zum Test mit MariaDB verbinden:&lt;br /&gt;
 # mysql -u root -p&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen:&lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
 Enter password:&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  exit&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang als fhemuser testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show databases;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show Databases einfügen &amp;gt;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden.&lt;br /&gt;
Um die Tabellen zu sehen, kann man den folgenden Befehl eingeben: &lt;br /&gt;
&lt;br /&gt;
 MariaDB [(none)]&amp;gt;  show tables;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;&amp;lt; Ergebnis Show tables einfügen &amp;gt;&amp;gt;&lt;br /&gt;
, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Integration von DBLog in eigene Module ==&lt;br /&gt;
=== Bereitstellung der UNITS ===&lt;br /&gt;
Mit der DbLog_splitFn kann der Modulautor selbst festlegen, wie die Events des Moduls in die Bestandteile Reading/Value/Unit zerlegt werden um ein korrektes Logging per DbLog zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
Dazu muss der Modulautor in der [[DevelopmentModuleIntro#X_Initialize|Initialize-Funktion]] eine &amp;lt;code&amp;gt;DbLog_splitFn&amp;lt;/code&amp;gt; bereitstellen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	...&lt;br /&gt;
	$hash-&amp;gt;{DbLog_splitFn}      = &amp;quot;X_DbLog_splitFn&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die genaue Aufrufsyntax und Funktionweise einer DbLog_split-Funktion findet man [[DevelopmentModuleIntro#X_DbLog_split|hier]].&lt;br /&gt;
&lt;br /&gt;
== Werte auslesen ==&lt;br /&gt;
Manchmal möchte man Daten aus den Logs abrufen ohne händisch in der Datenbank herumzuwühlen (s.u.). Dies ist insb. auch dann hilfreich, wenn man eigenen Funktionen, Notifys oder spezielle Plots entwirft, bei denen man auf Logdaten zugreifen möchte.&lt;br /&gt;
&lt;br /&gt;
Grundsätzlich beschrieben ist dies in der {{Link2CmdRef|Lang=de|Anker=DbLog}} und unterscheidet sich minimal (aber entscheidend) von der Struktur bei [[FileLog#Werte_auslesen|FileLogs]].&lt;br /&gt;
&lt;br /&gt;
Hier ein paar Beispiele, was man damit anstellen kann:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01 2016-10-03 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor vom 01.10.-03.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor von 8-16 Uhr am 01.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor:temperature&amp;lt;/code&amp;gt; nur die temperature Werte&lt;br /&gt;
* &amp;lt;code&amp;gt;{ ReadingsTimestamp(&amp;quot;meinSensor&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;0&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des aktuellen state des meinSensor&lt;br /&gt;
* &amp;lt;code&amp;gt;{ OldTimestamp(&amp;quot;meinSensor&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des letzten state des FHT_3a32&lt;br /&gt;
* &amp;lt;code&amp;gt;{ time_str2num(OldTimestamp(&amp;quot;meinSensor&amp;quot;)) }&amp;lt;/code&amp;gt; Timestamp in Sekunden des letzten state des meinSensor&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
&#039;&#039;&#039;Öffnen der DB unter Linux:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(Es werden Schreibrechte benötigt,ohne kann man die DB zwar öffnen, aber nichts machen)&lt;br /&gt;
 sudo sqlite3 fhem.db&lt;br /&gt;
Dadurch öffnet sich ein SQL-Konsole, auf der alle weiteren Befehle ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schliessen der DB:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .exit&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfe anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .help&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Tabellen anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .tables&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Das Schema der DB anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(vgl. oben [[DbLog#Datenbanken]] und [[DbLog#Beispiel: Anlegen und Nutzung einer SQLite-Datenbank]])&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .schema&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Eintäge anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Die Einträge liegen alle in der Tabelle &amp;quot;History&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ganz wichtig&#039;&#039;&#039; ist immer das &amp;quot;;&amp;quot; am Ende Zeile (bei allen Kommandos, die nicht mit einem &amp;quot;.&amp;quot; anfangen). Wenn es vergessen wurde zeigt die Konsole solange neue Zeilen bis ein &amp;quot;;&amp;quot; eingegeben wird. So kann ein Befehl auch bequem über mehrere Zeilen geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY;&lt;br /&gt;
&lt;br /&gt;
Dies kann sehr lange dauern und kann ggf. mit &amp;lt;code&amp;gt;STRG-C&amp;lt;/code&amp;gt; abgebrochen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;where&amp;lt;/code&amp;gt;-Statements werden Strings in einfache Anführungsstriche gesetzt, Zahlen nicht.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LÖSCHEN aller Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Löschen kann nicht rückgängig gemacht werden!! Also IMMER erst die entsprechenden SELECT-Statements solange verfeinern bis wirklich nur die gewünschten Einträge angezeigt werden. Dann das &amp;lt;code&amp;gt;select *&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; ersetzen.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; delete from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank reparieren ==&lt;br /&gt;
Es kann immer wieder mal vorkommen, dass Datenbanken Fehler enthalten. Das muss im Alltag garnicht auffallen und auch nicht immer schlimm enden. Wenn man auf der SQL-Konsole aber bspw. eine Meldung &amp;lt;code&amp;gt;Error: database disk image is malformed&amp;lt;/code&amp;gt; erhält, sollte man ein Reparatur vornehmen.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
Die folgenden Schritte beschreiben, wie man eine SQLite-DB reparieren kann (Quelle: [http://techblog.dorogin.com/2011/05/sqliteexception-database-disk-image-is.html]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
DB öffnen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Integritäts-Check durchführen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sqlite&amp;gt; pragma integrity_check;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kommt hier ein &amp;quot;ok&amp;quot; ist die DB gesund. Ansonsten erscheint etwas wie&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
*** in database main ***&lt;br /&gt;
On tree page 118786 cell 1: Rowid 75 out of order (previous was 816660)&lt;br /&gt;
On tree page 118786 cell 4: Rowid 815704 out of order (previous was 816727)&lt;br /&gt;
Corruption detected in cell 0 on page 118786&lt;br /&gt;
Multiple uses for byte 132 of page 118786&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Datenbank-Dump erstellen (Export gesamten DB in die Datei &amp;quot;dump_all_20160516_1043.sql&amp;quot;) und DB verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .mode insert&lt;br /&gt;
sqlite&amp;gt; .output dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; .dump&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Neue Datenbank erstellen und den Dump einlesen, Integritäts-Check machen und verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem-neu.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .read dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; pragma integrity_check;&lt;br /&gt;
ok&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Spätestens jetzt FHEM stoppen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem stop&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Alte DB sichern und neue aktivieren:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo mv fhem.db fhem.db.sv_20160516&lt;br /&gt;
sudo mv fhem-neu.db fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Kontrollieren, dass die neue DB die gleichen Rechte wie die alte DB hat (und ggf. korrigieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 root root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
~/fhem$ sudo chown fhem:root fhem.db&lt;br /&gt;
&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 fhem root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
FHEM wieder starten (und natürlich kontrollieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem start&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. Diese geschieht mittels des Userreadings, welches man vorteilshafterweise mit im DbLog-device anlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;attr myDbLog userReadings DbFileSize:reduceLogState.* { (split(&#039; &#039;,`du -m fhem.db`))[0] }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels dieses Attributs wird die Grösse der .db-Datei immer nach dem Ausführen des ReduceLog in das Reading &amp;quot;DbFileSize&amp;quot; in ganzzahligen MByte abgelegt.&lt;br /&gt;
&lt;br /&gt;
Basierend auf diesem Reading können dann weitere Aktionen, beispielsweise ein Plot, erstellt werden.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist für SQLite verwendbar. Zur Ermittlung der DB-Größe andere DB-Typen (aber auch für SQLite nutzbar) kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Performance-Optimierung ==&lt;br /&gt;
Auch eine Datenbank kann mit der Zeit langsamer werden. Dies hängt von mehreren Faktoren ab:&lt;br /&gt;
* Menge der gelogten Daten (zB. &amp;gt; 4-5 GB)&lt;br /&gt;
* Eingesetzte Hardware (zB. langsame SD-Karte vs. schnelle SSD)&lt;br /&gt;
* Eingesetztes Datenbanksystem (zB. SQLite, MySQL)&lt;br /&gt;
* Komplexität der Abfragen (zB. für aufwändige Graphen oder Berechnungen)&lt;br /&gt;
&lt;br /&gt;
Diese Punkte sollen im folgenden diskutiert werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komplexität der Abfragen ===&lt;br /&gt;
Dies ist kein Problem der Datenbank, sondern rein der Abfrage. Dem entsprechend muss die Optimierung auch in der Abfrage oder im Skript gesucht werden. Dies ist nicht Ziel dieses Abschnittes und wird hier nicht weiter behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetztes Datenbanksystem ===&lt;br /&gt;
Welches Datenbanksystem eingesetzt wird (zB. SQLite oder MySQL) hat auf die Performance der Datenbank gar keinen so großen Einfluss, wie vielleicht zuerst gedacht. Selbst SQLite kann problemlos Datenbanken mit etlichen GB Größe performant verarbeiten. Der Flaschenhals ist hier viel mehr die darunter liegende Hardware (s.u.).&lt;br /&gt;
&lt;br /&gt;
Die Performance der Datenbank an sich, kann aber durch verschiedene Maßnahmen verbessert werden:&lt;br /&gt;
* Pflegemaßnahmen bzgl. der Daten&lt;br /&gt;
* &#039;&#039;&#039;Erstellung von Indizes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Menge der Daten und Pflegemaßnahmen bzgl. der Daten ====&lt;br /&gt;
Die Menge der geloggten Daten hat natürlich Einfluss auf die Geschwindigkeit von Abfragen - je mehr Daten vorhanden sind, desto mehr Daten müssen auch durchforstet werden um eine Abfrage zu bedienen. Die Reduzierung der geloggten Datenmenge hat also direkten Einfluss auf die Größe und damit auch die Geschwindigkeit der Datenbank. Die Menge der zu loggenden Daten lässt sich an zwei Stellen einschränken:&lt;br /&gt;
* bei der Definition jedes Devices (s. Kapitel oben)&lt;br /&gt;
* bei der Festlegung des fhem-weiten Log-Levels (s. [[Loglevel]])&lt;br /&gt;
&lt;br /&gt;
Die Menge der bereits geloggten Daten kann zB. mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] verringert und optimiert werden, bspw.&lt;br /&gt;
* löschen unnötiger Daten&lt;br /&gt;
* vacuum der Datenbank&lt;br /&gt;
&lt;br /&gt;
Insgesamt haben diese Maßnahmen aber nur einen eingeschränkten Effekt auf die Performance der DB. Deutlich effektiver ist die Erstellung eines Index (s.u.).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Erstellung von Indizes ====&lt;br /&gt;
Die Erstellung von Indizes hat mit Abstand den größten Einfluss auf die Performance einer Datenbank (auf unveränderter Hardware). Extrem zusammengefasst ist ein Index eine extrem optimiertes Nachschlageverzeichnis für einen bestimmten Typ Daten (ein Index wie im Buch halt). Eine wunderbare Einführung in Indizes bietet [[https://use-the-index-luke.com/de|https://use-the-index-luke.com]].&lt;br /&gt;
&lt;br /&gt;
In fhem sind Indizes sogar sehr einfach einzurichten da die Datenbank-Nutzung sehr stark vorgegeben ist. Nahezu jede Abfrage folgt dem Schema &#039;&#039;Device -&amp;gt; Reading -&amp;gt; Datum -&amp;gt; Wert&#039;&#039;. Ein Index kann genau diese Abfrage bedienen und beschleunigen. Ein Index nur über die Devices wäre ein erster Schritt, brächte aber noch keinen großen Gewinn (wie um Link oben gut beschrieben). Über die gesamten ersten drei Schritte erstellt (Device -&amp;gt; Reading -&amp;gt; Datum) bringt der Index aber sofort eine deutliche Geschwindigkeitssteigerung.&lt;br /&gt;
&lt;br /&gt;
Die Erstellung eines Index erfolgt direkt in der Datenbank (und nicht aus fhem heraus), hier am Beispiel einer SQLite-DB:&lt;br /&gt;
&lt;br /&gt;
Öffnen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; sudo sqlite3 fhem.db &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugen des Index auf der DB-Konsole (das Semikolon am Ende ist wichtig):&lt;br /&gt;
&amp;lt;pre&amp;gt; create index idx_device_reading_timestamp on history (device, reading, timestamp); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verlassen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; .exit &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzig zu berücksichtigen ist, dass dieser Index die Datenbank um bis zu 1/3 vergrößert. Er kann aber bei Bedarf auch wieder entfernt werden. Bei meiner 15 GB SQLite-Datei (auf einem Mac Mini mit SSD) hat dies ca. 15 min gedauert (den fhem hatte ich vorsichtshalber währenddessen deaktiviert).&lt;br /&gt;
&lt;br /&gt;
Sollte jemand spezielle Berechnungen oder Skripte ausführen, die nach einem anderen Abfrage-Schema arbeiten, könnte man dafür spezialisierte zusätzliche Indizes erstellen. Das sollte aber dann mit dem Wissen des obigen Links erarbeitet werden, da dann etwas mehr Hintergrundwissen sehr hilfreich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== DB-Backup ====&lt;br /&gt;
Ein anderer Aspekt, der eigentlich nichts mit der Performance der DB zu tun hat, ist der Einfluss aufs Backup. Wird bspw. ein Systembackup per RSYNC gemacht, muss bei SQLite immer die komplette ggf. riesige Datei gesynct werden - bei MySQL würden nur die veränderten DB-Elemente gesynct. Dies soll hier nicht weiter vertieft werden, sollte aber bei einer Gesamtstrategie bedacht werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetzte Hardware ===&lt;br /&gt;
fhem hat grundsätzlich sehr viele kleine Datenzugriffe, unabhängig davon ob FileLog oder DbLog eingesetzt wird. Deshalb ist der absolut größte Performance-Gewinn durch den Einsatz schneller Festplatten zu erreichen - ganz einfache Aussage: &#039;&#039;je schneller desto besser ;-)&#039;&#039;. Konkret bietet eine SSD mit mind. 250MB/s Datenzugriff eine ordentliche Basis für jedes datengestützte System, wie den fhem.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenmenge größer und die Abfragen komplexer werden, müssen natürlich irgendwann auch die Prozessorleistung und der Arbeitsspeicher mit wachsen. Aber auch an einem Raspi wird eine SSD deutlich performanter sein, als eine einfache SD-Karte oder eine klassische rotierende Festplatte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31046</id>
		<title>DbLog-MySQL</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31046"/>
		<updated>2019-07-29T08:03:10Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Beispiel: Anlegen und Nutzung einer Mysql-Datenbank */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Da bei der Inbetriebnahme und der Migration von MySQL bzw MariaDB-Datenbanken einige kleine Fallen auftreten können, gibt&#039;s hier ein paar Hinweise.&lt;br /&gt;
&lt;br /&gt;
== Server version: 10.1.40-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04 ==&lt;br /&gt;
=== db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MariaDB-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp. Bei der Verwendung von MariaDB muss die MySQL-Datei verwendet werden.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; befindet sich das Script &#039;&#039;&#039;db_create_mysql.sql&#039;&#039;&#039; , welches eine neue Datenbank samt Tabellenstruktur anlegt. Ausserdem wird ein Benutzer &amp;quot;fhemuser&amp;quot; mit einem (zu ändernden) &amp;quot;fhempasswort&amp;quot; angelegt.&lt;br /&gt;
==== Primary Keys ====&lt;br /&gt;
===== current =====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== history =====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`history` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`TIMESTAMP`,`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer MariaDB-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 apt-get update &amp;amp;&amp;amp; apt-get install mariadb-server&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der Shell-Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der MariaDB-Prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Zum Test mal mit MariaDB verbinden:&lt;br /&gt;
 # mysql -p -u root&lt;br /&gt;
 Enter password:&lt;br /&gt;
 mysql&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen (root Passwort wird abgefragt): &lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; show databases;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Integration von DBLog in eigene Module ==&lt;br /&gt;
=== Bereitstellung der UNITS ===&lt;br /&gt;
Mit der DbLog_splitFn kann der Modulautor selbst festlegen, wie die Events des Moduls in die Bestandteile Reading/Value/Unit zerlegt werden um ein korrektes Logging per DbLog zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
Dazu muss der Modulautor in der [[DevelopmentModuleIntro#X_Initialize|Initialize-Funktion]] eine &amp;lt;code&amp;gt;DbLog_splitFn&amp;lt;/code&amp;gt; bereitstellen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	...&lt;br /&gt;
	$hash-&amp;gt;{DbLog_splitFn}      = &amp;quot;X_DbLog_splitFn&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die genaue Aufrufsyntax und Funktionweise einer DbLog_split-Funktion findet man [[DevelopmentModuleIntro#X_DbLog_split|hier]].&lt;br /&gt;
&lt;br /&gt;
== Werte auslesen ==&lt;br /&gt;
Manchmal möchte man Daten aus den Logs abrufen ohne händisch in der Datenbank herumzuwühlen (s.u.). Dies ist insb. auch dann hilfreich, wenn man eigenen Funktionen, Notifys oder spezielle Plots entwirft, bei denen man auf Logdaten zugreifen möchte.&lt;br /&gt;
&lt;br /&gt;
Grundsätzlich beschrieben ist dies in der {{Link2CmdRef|Lang=de|Anker=DbLog}} und unterscheidet sich minimal (aber entscheidend) von der Struktur bei [[FileLog#Werte_auslesen|FileLogs]].&lt;br /&gt;
&lt;br /&gt;
Hier ein paar Beispiele, was man damit anstellen kann:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01 2016-10-03 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor vom 01.10.-03.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor von 8-16 Uhr am 01.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor:temperature&amp;lt;/code&amp;gt; nur die temperature Werte&lt;br /&gt;
* &amp;lt;code&amp;gt;{ ReadingsTimestamp(&amp;quot;meinSensor&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;0&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des aktuellen state des meinSensor&lt;br /&gt;
* &amp;lt;code&amp;gt;{ OldTimestamp(&amp;quot;meinSensor&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des letzten state des FHT_3a32&lt;br /&gt;
* &amp;lt;code&amp;gt;{ time_str2num(OldTimestamp(&amp;quot;meinSensor&amp;quot;)) }&amp;lt;/code&amp;gt; Timestamp in Sekunden des letzten state des meinSensor&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
&#039;&#039;&#039;Öffnen der DB unter Linux:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(Es werden Schreibrechte benötigt,ohne kann man die DB zwar öffnen, aber nichts machen)&lt;br /&gt;
 sudo sqlite3 fhem.db&lt;br /&gt;
Dadurch öffnet sich ein SQL-Konsole, auf der alle weiteren Befehle ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schliessen der DB:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .exit&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfe anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .help&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Tabellen anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .tables&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Das Schema der DB anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(vgl. oben [[DbLog#Datenbanken]] und [[DbLog#Beispiel: Anlegen und Nutzung einer SQLite-Datenbank]])&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .schema&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Eintäge anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Die Einträge liegen alle in der Tabelle &amp;quot;History&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ganz wichtig&#039;&#039;&#039; ist immer das &amp;quot;;&amp;quot; am Ende Zeile (bei allen Kommandos, die nicht mit einem &amp;quot;.&amp;quot; anfangen). Wenn es vergessen wurde zeigt die Konsole solange neue Zeilen bis ein &amp;quot;;&amp;quot; eingegeben wird. So kann ein Befehl auch bequem über mehrere Zeilen geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY;&lt;br /&gt;
&lt;br /&gt;
Dies kann sehr lange dauern und kann ggf. mit &amp;lt;code&amp;gt;STRG-C&amp;lt;/code&amp;gt; abgebrochen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;where&amp;lt;/code&amp;gt;-Statements werden Strings in einfache Anführungsstriche gesetzt, Zahlen nicht.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LÖSCHEN aller Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Löschen kann nicht rückgängig gemacht werden!! Also IMMER erst die entsprechenden SELECT-Statements solange verfeinern bis wirklich nur die gewünschten Einträge angezeigt werden. Dann das &amp;lt;code&amp;gt;select *&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; ersetzen.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; delete from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank reparieren ==&lt;br /&gt;
Es kann immer wieder mal vorkommen, dass Datenbanken Fehler enthalten. Das muss im Alltag garnicht auffallen und auch nicht immer schlimm enden. Wenn man auf der SQL-Konsole aber bspw. eine Meldung &amp;lt;code&amp;gt;Error: database disk image is malformed&amp;lt;/code&amp;gt; erhält, sollte man ein Reparatur vornehmen.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
Die folgenden Schritte beschreiben, wie man eine SQLite-DB reparieren kann (Quelle: [http://techblog.dorogin.com/2011/05/sqliteexception-database-disk-image-is.html]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
DB öffnen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Integritäts-Check durchführen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sqlite&amp;gt; pragma integrity_check;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kommt hier ein &amp;quot;ok&amp;quot; ist die DB gesund. Ansonsten erscheint etwas wie&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
*** in database main ***&lt;br /&gt;
On tree page 118786 cell 1: Rowid 75 out of order (previous was 816660)&lt;br /&gt;
On tree page 118786 cell 4: Rowid 815704 out of order (previous was 816727)&lt;br /&gt;
Corruption detected in cell 0 on page 118786&lt;br /&gt;
Multiple uses for byte 132 of page 118786&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Datenbank-Dump erstellen (Export gesamten DB in die Datei &amp;quot;dump_all_20160516_1043.sql&amp;quot;) und DB verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .mode insert&lt;br /&gt;
sqlite&amp;gt; .output dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; .dump&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Neue Datenbank erstellen und den Dump einlesen, Integritäts-Check machen und verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem-neu.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .read dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; pragma integrity_check;&lt;br /&gt;
ok&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Spätestens jetzt FHEM stoppen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem stop&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Alte DB sichern und neue aktivieren:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo mv fhem.db fhem.db.sv_20160516&lt;br /&gt;
sudo mv fhem-neu.db fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Kontrollieren, dass die neue DB die gleichen Rechte wie die alte DB hat (und ggf. korrigieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 root root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
~/fhem$ sudo chown fhem:root fhem.db&lt;br /&gt;
&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 fhem root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
FHEM wieder starten (und natürlich kontrollieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem start&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. Diese geschieht mittels des Userreadings, welches man vorteilshafterweise mit im DbLog-device anlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;attr myDbLog userReadings DbFileSize:reduceLogState.* { (split(&#039; &#039;,`du -m fhem.db`))[0] }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels dieses Attributs wird die Grösse der .db-Datei immer nach dem Ausführen des ReduceLog in das Reading &amp;quot;DbFileSize&amp;quot; in ganzzahligen MByte abgelegt.&lt;br /&gt;
&lt;br /&gt;
Basierend auf diesem Reading können dann weitere Aktionen, beispielsweise ein Plot, erstellt werden.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist für SQLite verwendbar. Zur Ermittlung der DB-Größe andere DB-Typen (aber auch für SQLite nutzbar) kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Performance-Optimierung ==&lt;br /&gt;
Auch eine Datenbank kann mit der Zeit langsamer werden. Dies hängt von mehreren Faktoren ab:&lt;br /&gt;
* Menge der gelogten Daten (zB. &amp;gt; 4-5 GB)&lt;br /&gt;
* Eingesetzte Hardware (zB. langsame SD-Karte vs. schnelle SSD)&lt;br /&gt;
* Eingesetztes Datenbanksystem (zB. SQLite, MySQL)&lt;br /&gt;
* Komplexität der Abfragen (zB. für aufwändige Graphen oder Berechnungen)&lt;br /&gt;
&lt;br /&gt;
Diese Punkte sollen im folgenden diskutiert werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komplexität der Abfragen ===&lt;br /&gt;
Dies ist kein Problem der Datenbank, sondern rein der Abfrage. Dem entsprechend muss die Optimierung auch in der Abfrage oder im Skript gesucht werden. Dies ist nicht Ziel dieses Abschnittes und wird hier nicht weiter behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetztes Datenbanksystem ===&lt;br /&gt;
Welches Datenbanksystem eingesetzt wird (zB. SQLite oder MySQL) hat auf die Performance der Datenbank gar keinen so großen Einfluss, wie vielleicht zuerst gedacht. Selbst SQLite kann problemlos Datenbanken mit etlichen GB Größe performant verarbeiten. Der Flaschenhals ist hier viel mehr die darunter liegende Hardware (s.u.).&lt;br /&gt;
&lt;br /&gt;
Die Performance der Datenbank an sich, kann aber durch verschiedene Maßnahmen verbessert werden:&lt;br /&gt;
* Pflegemaßnahmen bzgl. der Daten&lt;br /&gt;
* &#039;&#039;&#039;Erstellung von Indizes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Menge der Daten und Pflegemaßnahmen bzgl. der Daten ====&lt;br /&gt;
Die Menge der geloggten Daten hat natürlich Einfluss auf die Geschwindigkeit von Abfragen - je mehr Daten vorhanden sind, desto mehr Daten müssen auch durchforstet werden um eine Abfrage zu bedienen. Die Reduzierung der geloggten Datenmenge hat also direkten Einfluss auf die Größe und damit auch die Geschwindigkeit der Datenbank. Die Menge der zu loggenden Daten lässt sich an zwei Stellen einschränken:&lt;br /&gt;
* bei der Definition jedes Devices (s. Kapitel oben)&lt;br /&gt;
* bei der Festlegung des fhem-weiten Log-Levels (s. [[Loglevel]])&lt;br /&gt;
&lt;br /&gt;
Die Menge der bereits geloggten Daten kann zB. mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] verringert und optimiert werden, bspw.&lt;br /&gt;
* löschen unnötiger Daten&lt;br /&gt;
* vacuum der Datenbank&lt;br /&gt;
&lt;br /&gt;
Insgesamt haben diese Maßnahmen aber nur einen eingeschränkten Effekt auf die Performance der DB. Deutlich effektiver ist die Erstellung eines Index (s.u.).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Erstellung von Indizes ====&lt;br /&gt;
Die Erstellung von Indizes hat mit Abstand den größten Einfluss auf die Performance einer Datenbank (auf unveränderter Hardware). Extrem zusammengefasst ist ein Index eine extrem optimiertes Nachschlageverzeichnis für einen bestimmten Typ Daten (ein Index wie im Buch halt). Eine wunderbare Einführung in Indizes bietet [[https://use-the-index-luke.com/de|https://use-the-index-luke.com]].&lt;br /&gt;
&lt;br /&gt;
In fhem sind Indizes sogar sehr einfach einzurichten da die Datenbank-Nutzung sehr stark vorgegeben ist. Nahezu jede Abfrage folgt dem Schema &#039;&#039;Device -&amp;gt; Reading -&amp;gt; Datum -&amp;gt; Wert&#039;&#039;. Ein Index kann genau diese Abfrage bedienen und beschleunigen. Ein Index nur über die Devices wäre ein erster Schritt, brächte aber noch keinen großen Gewinn (wie um Link oben gut beschrieben). Über die gesamten ersten drei Schritte erstellt (Device -&amp;gt; Reading -&amp;gt; Datum) bringt der Index aber sofort eine deutliche Geschwindigkeitssteigerung.&lt;br /&gt;
&lt;br /&gt;
Die Erstellung eines Index erfolgt direkt in der Datenbank (und nicht aus fhem heraus), hier am Beispiel einer SQLite-DB:&lt;br /&gt;
&lt;br /&gt;
Öffnen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; sudo sqlite3 fhem.db &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugen des Index auf der DB-Konsole (das Semikolon am Ende ist wichtig):&lt;br /&gt;
&amp;lt;pre&amp;gt; create index idx_device_reading_timestamp on history (device, reading, timestamp); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verlassen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; .exit &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzig zu berücksichtigen ist, dass dieser Index die Datenbank um bis zu 1/3 vergrößert. Er kann aber bei Bedarf auch wieder entfernt werden. Bei meiner 15 GB SQLite-Datei (auf einem Mac Mini mit SSD) hat dies ca. 15 min gedauert (den fhem hatte ich vorsichtshalber währenddessen deaktiviert).&lt;br /&gt;
&lt;br /&gt;
Sollte jemand spezielle Berechnungen oder Skripte ausführen, die nach einem anderen Abfrage-Schema arbeiten, könnte man dafür spezialisierte zusätzliche Indizes erstellen. Das sollte aber dann mit dem Wissen des obigen Links erarbeitet werden, da dann etwas mehr Hintergrundwissen sehr hilfreich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== DB-Backup ====&lt;br /&gt;
Ein anderer Aspekt, der eigentlich nichts mit der Performance der DB zu tun hat, ist der Einfluss aufs Backup. Wird bspw. ein Systembackup per RSYNC gemacht, muss bei SQLite immer die komplette ggf. riesige Datei gesynct werden - bei MySQL würden nur die veränderten DB-Elemente gesynct. Dies soll hier nicht weiter vertieft werden, sollte aber bei einer Gesamtstrategie bedacht werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetzte Hardware ===&lt;br /&gt;
fhem hat grundsätzlich sehr viele kleine Datenzugriffe, unabhängig davon ob FileLog oder DbLog eingesetzt wird. Deshalb ist der absolut größte Performance-Gewinn durch den Einsatz schneller Festplatten zu erreichen - ganz einfache Aussage: &#039;&#039;je schneller desto besser ;-)&#039;&#039;. Konkret bietet eine SSD mit mind. 250MB/s Datenzugriff eine ordentliche Basis für jedes datengestützte System, wie den fhem.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenmenge größer und die Abfragen komplexer werden, müssen natürlich irgendwann auch die Prozessorleistung und der Arbeitsspeicher mit wachsen. Aber auch an einem Raspi wird eine SSD deutlich performanter sein, als eine einfache SD-Karte oder eine klassische rotierende Festplatte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31045</id>
		<title>DbLog-MySQL</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31045"/>
		<updated>2019-07-29T06:50:20Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Beispiel: Anlegen und Nutzung einer SQLite-Datenbank */  gelöscht&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Da bei der Inbetriebnahme und der Migration von MySQL bzw MariaDB-Datenbanken einige kleine Fallen auftreten können, gibt&#039;s hier ein paar Hinweise.&lt;br /&gt;
&lt;br /&gt;
== Server version: 10.1.40-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04 ==&lt;br /&gt;
=== db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MariaDB-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp. Bei der Verwendung von MariaDB muss die MySQL-Datei verwendet werden.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; befindet sich das Script &#039;&#039;&#039;db_create_mysql.sql&#039;&#039;&#039; , welches eine neue Datenbank samt Tabellenstruktur anlegt. Ausserdem wird ein Benutzer &amp;quot;fhemuser&amp;quot; mit einem (zu ändernden) &amp;quot;fhempasswort&amp;quot; angelegt.&lt;br /&gt;
==== Primary Keys ====&lt;br /&gt;
===== current =====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== history =====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`history` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`TIMESTAMP`,`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer Mysql-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 apt-get update &amp;amp;&amp;amp; apt-get install mysql-server mysql-client libdbd-mysql libdbd-mysql-perl&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Installation sollte man aus Sicherheitsgründen ein Passwort für den mysql-root vergeben, wenn man nicht sogar ganz den Login verbietet.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der normale Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Zum Test mal mit mysql verbinden:&lt;br /&gt;
 # mysql -p -u root&lt;br /&gt;
 Enter password:&lt;br /&gt;
 mysql&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen (root Passwort wird abgefragt): &lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; show databases;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Integration von DBLog in eigene Module ==&lt;br /&gt;
=== Bereitstellung der UNITS ===&lt;br /&gt;
Mit der DbLog_splitFn kann der Modulautor selbst festlegen, wie die Events des Moduls in die Bestandteile Reading/Value/Unit zerlegt werden um ein korrektes Logging per DbLog zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
Dazu muss der Modulautor in der [[DevelopmentModuleIntro#X_Initialize|Initialize-Funktion]] eine &amp;lt;code&amp;gt;DbLog_splitFn&amp;lt;/code&amp;gt; bereitstellen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	...&lt;br /&gt;
	$hash-&amp;gt;{DbLog_splitFn}      = &amp;quot;X_DbLog_splitFn&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die genaue Aufrufsyntax und Funktionweise einer DbLog_split-Funktion findet man [[DevelopmentModuleIntro#X_DbLog_split|hier]].&lt;br /&gt;
&lt;br /&gt;
== Werte auslesen ==&lt;br /&gt;
Manchmal möchte man Daten aus den Logs abrufen ohne händisch in der Datenbank herumzuwühlen (s.u.). Dies ist insb. auch dann hilfreich, wenn man eigenen Funktionen, Notifys oder spezielle Plots entwirft, bei denen man auf Logdaten zugreifen möchte.&lt;br /&gt;
&lt;br /&gt;
Grundsätzlich beschrieben ist dies in der {{Link2CmdRef|Lang=de|Anker=DbLog}} und unterscheidet sich minimal (aber entscheidend) von der Struktur bei [[FileLog#Werte_auslesen|FileLogs]].&lt;br /&gt;
&lt;br /&gt;
Hier ein paar Beispiele, was man damit anstellen kann:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01 2016-10-03 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor vom 01.10.-03.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor von 8-16 Uhr am 01.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor:temperature&amp;lt;/code&amp;gt; nur die temperature Werte&lt;br /&gt;
* &amp;lt;code&amp;gt;{ ReadingsTimestamp(&amp;quot;meinSensor&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;0&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des aktuellen state des meinSensor&lt;br /&gt;
* &amp;lt;code&amp;gt;{ OldTimestamp(&amp;quot;meinSensor&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des letzten state des FHT_3a32&lt;br /&gt;
* &amp;lt;code&amp;gt;{ time_str2num(OldTimestamp(&amp;quot;meinSensor&amp;quot;)) }&amp;lt;/code&amp;gt; Timestamp in Sekunden des letzten state des meinSensor&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
&#039;&#039;&#039;Öffnen der DB unter Linux:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(Es werden Schreibrechte benötigt,ohne kann man die DB zwar öffnen, aber nichts machen)&lt;br /&gt;
 sudo sqlite3 fhem.db&lt;br /&gt;
Dadurch öffnet sich ein SQL-Konsole, auf der alle weiteren Befehle ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schliessen der DB:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .exit&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfe anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .help&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Tabellen anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .tables&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Das Schema der DB anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(vgl. oben [[DbLog#Datenbanken]] und [[DbLog#Beispiel: Anlegen und Nutzung einer SQLite-Datenbank]])&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .schema&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Eintäge anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Die Einträge liegen alle in der Tabelle &amp;quot;History&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ganz wichtig&#039;&#039;&#039; ist immer das &amp;quot;;&amp;quot; am Ende Zeile (bei allen Kommandos, die nicht mit einem &amp;quot;.&amp;quot; anfangen). Wenn es vergessen wurde zeigt die Konsole solange neue Zeilen bis ein &amp;quot;;&amp;quot; eingegeben wird. So kann ein Befehl auch bequem über mehrere Zeilen geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY;&lt;br /&gt;
&lt;br /&gt;
Dies kann sehr lange dauern und kann ggf. mit &amp;lt;code&amp;gt;STRG-C&amp;lt;/code&amp;gt; abgebrochen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;where&amp;lt;/code&amp;gt;-Statements werden Strings in einfache Anführungsstriche gesetzt, Zahlen nicht.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LÖSCHEN aller Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Löschen kann nicht rückgängig gemacht werden!! Also IMMER erst die entsprechenden SELECT-Statements solange verfeinern bis wirklich nur die gewünschten Einträge angezeigt werden. Dann das &amp;lt;code&amp;gt;select *&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; ersetzen.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; delete from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank reparieren ==&lt;br /&gt;
Es kann immer wieder mal vorkommen, dass Datenbanken Fehler enthalten. Das muss im Alltag garnicht auffallen und auch nicht immer schlimm enden. Wenn man auf der SQL-Konsole aber bspw. eine Meldung &amp;lt;code&amp;gt;Error: database disk image is malformed&amp;lt;/code&amp;gt; erhält, sollte man ein Reparatur vornehmen.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
Die folgenden Schritte beschreiben, wie man eine SQLite-DB reparieren kann (Quelle: [http://techblog.dorogin.com/2011/05/sqliteexception-database-disk-image-is.html]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
DB öffnen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Integritäts-Check durchführen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sqlite&amp;gt; pragma integrity_check;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kommt hier ein &amp;quot;ok&amp;quot; ist die DB gesund. Ansonsten erscheint etwas wie&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
*** in database main ***&lt;br /&gt;
On tree page 118786 cell 1: Rowid 75 out of order (previous was 816660)&lt;br /&gt;
On tree page 118786 cell 4: Rowid 815704 out of order (previous was 816727)&lt;br /&gt;
Corruption detected in cell 0 on page 118786&lt;br /&gt;
Multiple uses for byte 132 of page 118786&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Datenbank-Dump erstellen (Export gesamten DB in die Datei &amp;quot;dump_all_20160516_1043.sql&amp;quot;) und DB verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .mode insert&lt;br /&gt;
sqlite&amp;gt; .output dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; .dump&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Neue Datenbank erstellen und den Dump einlesen, Integritäts-Check machen und verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem-neu.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .read dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; pragma integrity_check;&lt;br /&gt;
ok&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Spätestens jetzt FHEM stoppen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem stop&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Alte DB sichern und neue aktivieren:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo mv fhem.db fhem.db.sv_20160516&lt;br /&gt;
sudo mv fhem-neu.db fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Kontrollieren, dass die neue DB die gleichen Rechte wie die alte DB hat (und ggf. korrigieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 root root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
~/fhem$ sudo chown fhem:root fhem.db&lt;br /&gt;
&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 fhem root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
FHEM wieder starten (und natürlich kontrollieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem start&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. Diese geschieht mittels des Userreadings, welches man vorteilshafterweise mit im DbLog-device anlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;attr myDbLog userReadings DbFileSize:reduceLogState.* { (split(&#039; &#039;,`du -m fhem.db`))[0] }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels dieses Attributs wird die Grösse der .db-Datei immer nach dem Ausführen des ReduceLog in das Reading &amp;quot;DbFileSize&amp;quot; in ganzzahligen MByte abgelegt.&lt;br /&gt;
&lt;br /&gt;
Basierend auf diesem Reading können dann weitere Aktionen, beispielsweise ein Plot, erstellt werden.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist für SQLite verwendbar. Zur Ermittlung der DB-Größe andere DB-Typen (aber auch für SQLite nutzbar) kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Performance-Optimierung ==&lt;br /&gt;
Auch eine Datenbank kann mit der Zeit langsamer werden. Dies hängt von mehreren Faktoren ab:&lt;br /&gt;
* Menge der gelogten Daten (zB. &amp;gt; 4-5 GB)&lt;br /&gt;
* Eingesetzte Hardware (zB. langsame SD-Karte vs. schnelle SSD)&lt;br /&gt;
* Eingesetztes Datenbanksystem (zB. SQLite, MySQL)&lt;br /&gt;
* Komplexität der Abfragen (zB. für aufwändige Graphen oder Berechnungen)&lt;br /&gt;
&lt;br /&gt;
Diese Punkte sollen im folgenden diskutiert werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komplexität der Abfragen ===&lt;br /&gt;
Dies ist kein Problem der Datenbank, sondern rein der Abfrage. Dem entsprechend muss die Optimierung auch in der Abfrage oder im Skript gesucht werden. Dies ist nicht Ziel dieses Abschnittes und wird hier nicht weiter behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetztes Datenbanksystem ===&lt;br /&gt;
Welches Datenbanksystem eingesetzt wird (zB. SQLite oder MySQL) hat auf die Performance der Datenbank gar keinen so großen Einfluss, wie vielleicht zuerst gedacht. Selbst SQLite kann problemlos Datenbanken mit etlichen GB Größe performant verarbeiten. Der Flaschenhals ist hier viel mehr die darunter liegende Hardware (s.u.).&lt;br /&gt;
&lt;br /&gt;
Die Performance der Datenbank an sich, kann aber durch verschiedene Maßnahmen verbessert werden:&lt;br /&gt;
* Pflegemaßnahmen bzgl. der Daten&lt;br /&gt;
* &#039;&#039;&#039;Erstellung von Indizes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Menge der Daten und Pflegemaßnahmen bzgl. der Daten ====&lt;br /&gt;
Die Menge der geloggten Daten hat natürlich Einfluss auf die Geschwindigkeit von Abfragen - je mehr Daten vorhanden sind, desto mehr Daten müssen auch durchforstet werden um eine Abfrage zu bedienen. Die Reduzierung der geloggten Datenmenge hat also direkten Einfluss auf die Größe und damit auch die Geschwindigkeit der Datenbank. Die Menge der zu loggenden Daten lässt sich an zwei Stellen einschränken:&lt;br /&gt;
* bei der Definition jedes Devices (s. Kapitel oben)&lt;br /&gt;
* bei der Festlegung des fhem-weiten Log-Levels (s. [[Loglevel]])&lt;br /&gt;
&lt;br /&gt;
Die Menge der bereits geloggten Daten kann zB. mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] verringert und optimiert werden, bspw.&lt;br /&gt;
* löschen unnötiger Daten&lt;br /&gt;
* vacuum der Datenbank&lt;br /&gt;
&lt;br /&gt;
Insgesamt haben diese Maßnahmen aber nur einen eingeschränkten Effekt auf die Performance der DB. Deutlich effektiver ist die Erstellung eines Index (s.u.).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Erstellung von Indizes ====&lt;br /&gt;
Die Erstellung von Indizes hat mit Abstand den größten Einfluss auf die Performance einer Datenbank (auf unveränderter Hardware). Extrem zusammengefasst ist ein Index eine extrem optimiertes Nachschlageverzeichnis für einen bestimmten Typ Daten (ein Index wie im Buch halt). Eine wunderbare Einführung in Indizes bietet [[https://use-the-index-luke.com/de|https://use-the-index-luke.com]].&lt;br /&gt;
&lt;br /&gt;
In fhem sind Indizes sogar sehr einfach einzurichten da die Datenbank-Nutzung sehr stark vorgegeben ist. Nahezu jede Abfrage folgt dem Schema &#039;&#039;Device -&amp;gt; Reading -&amp;gt; Datum -&amp;gt; Wert&#039;&#039;. Ein Index kann genau diese Abfrage bedienen und beschleunigen. Ein Index nur über die Devices wäre ein erster Schritt, brächte aber noch keinen großen Gewinn (wie um Link oben gut beschrieben). Über die gesamten ersten drei Schritte erstellt (Device -&amp;gt; Reading -&amp;gt; Datum) bringt der Index aber sofort eine deutliche Geschwindigkeitssteigerung.&lt;br /&gt;
&lt;br /&gt;
Die Erstellung eines Index erfolgt direkt in der Datenbank (und nicht aus fhem heraus), hier am Beispiel einer SQLite-DB:&lt;br /&gt;
&lt;br /&gt;
Öffnen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; sudo sqlite3 fhem.db &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugen des Index auf der DB-Konsole (das Semikolon am Ende ist wichtig):&lt;br /&gt;
&amp;lt;pre&amp;gt; create index idx_device_reading_timestamp on history (device, reading, timestamp); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verlassen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; .exit &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzig zu berücksichtigen ist, dass dieser Index die Datenbank um bis zu 1/3 vergrößert. Er kann aber bei Bedarf auch wieder entfernt werden. Bei meiner 15 GB SQLite-Datei (auf einem Mac Mini mit SSD) hat dies ca. 15 min gedauert (den fhem hatte ich vorsichtshalber währenddessen deaktiviert).&lt;br /&gt;
&lt;br /&gt;
Sollte jemand spezielle Berechnungen oder Skripte ausführen, die nach einem anderen Abfrage-Schema arbeiten, könnte man dafür spezialisierte zusätzliche Indizes erstellen. Das sollte aber dann mit dem Wissen des obigen Links erarbeitet werden, da dann etwas mehr Hintergrundwissen sehr hilfreich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== DB-Backup ====&lt;br /&gt;
Ein anderer Aspekt, der eigentlich nichts mit der Performance der DB zu tun hat, ist der Einfluss aufs Backup. Wird bspw. ein Systembackup per RSYNC gemacht, muss bei SQLite immer die komplette ggf. riesige Datei gesynct werden - bei MySQL würden nur die veränderten DB-Elemente gesynct. Dies soll hier nicht weiter vertieft werden, sollte aber bei einer Gesamtstrategie bedacht werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetzte Hardware ===&lt;br /&gt;
fhem hat grundsätzlich sehr viele kleine Datenzugriffe, unabhängig davon ob FileLog oder DbLog eingesetzt wird. Deshalb ist der absolut größte Performance-Gewinn durch den Einsatz schneller Festplatten zu erreichen - ganz einfache Aussage: &#039;&#039;je schneller desto besser ;-)&#039;&#039;. Konkret bietet eine SSD mit mind. 250MB/s Datenzugriff eine ordentliche Basis für jedes datengestützte System, wie den fhem.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenmenge größer und die Abfragen komplexer werden, müssen natürlich irgendwann auch die Prozessorleistung und der Arbeitsspeicher mit wachsen. Aber auch an einem Raspi wird eine SSD deutlich performanter sein, als eine einfache SD-Karte oder eine klassische rotierende Festplatte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31044</id>
		<title>DbLog-MySQL</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31044"/>
		<updated>2019-07-29T06:49:55Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Anpassen der gplot-Konfigurationen */  gelöscht&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Da bei der Inbetriebnahme und der Migration von MySQL bzw MariaDB-Datenbanken einige kleine Fallen auftreten können, gibt&#039;s hier ein paar Hinweise.&lt;br /&gt;
&lt;br /&gt;
== Server version: 10.1.40-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04 ==&lt;br /&gt;
=== db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MariaDB-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp. Bei der Verwendung von MariaDB muss die MySQL-Datei verwendet werden.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; befindet sich das Script &#039;&#039;&#039;db_create_mysql.sql&#039;&#039;&#039; , welches eine neue Datenbank samt Tabellenstruktur anlegt. Ausserdem wird ein Benutzer &amp;quot;fhemuser&amp;quot; mit einem (zu ändernden) &amp;quot;fhempasswort&amp;quot; angelegt.&lt;br /&gt;
==== Primary Keys ====&lt;br /&gt;
===== current =====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== history =====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`history` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`TIMESTAMP`,`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer SQLite-Datenbank ==&lt;br /&gt;
Im folgenden wird eine lokale SQLite-Datenbank auf einen Ubuntu-System angelegt (nach Quelle: [http://www.tatsch-it.de/fhem-dblog/ http://www.tatsch-it.de/fhem-dblog/])&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Installation von SQLite:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo aptitude install sqlite3 libdbi-perl libdbd-sqlite3-perl&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anlegen der SQLite-Datenbank fhem.db&#039;&#039; (öffnet auch direkt eine SQL-Kommandozeile):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 /opt/fhem/fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der geöffneten SQL-Kommandozeile eingeben:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE &#039;history&#039; (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
CREATE TABLE &#039;current&#039; (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
CREATE INDEX Search_Idx ON `history` (DEVICE, READING, TIMESTAMP);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Kommandozeile verlässt man mit &amp;lt;code&amp;gt;.exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anpassen des Besitzers und der Rechte der Datenbank-Datei:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo chown fhem /opt/fhem/fhem.db&lt;br /&gt;
sudo chmod 666 /opt/fhem/fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Datenbank-Anbindung des FHEM konfigurieren:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo nano /opt/fhem/db.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
Inhalt:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%dbconfig= (&lt;br /&gt;
  connection =&amp;gt; &amp;quot;SQLite:dbname=/opt/fhem/fhem.db&amp;quot;,&lt;br /&gt;
  user =&amp;gt; &amp;quot;&amp;quot;,&lt;br /&gt;
  password =&amp;gt; &amp;quot;&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Logging des FHEM auf die Datenbank konfigurieren:&#039;&#039; (hier sind nur die Anpassungen aufgeführt)&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo nano /opt/fhem/fhem.cfg&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
attr global userattr DbLogExclude ...  # erlaubt es einzelne Einträge nicht zu loggen&lt;br /&gt;
...&lt;br /&gt;
define logdb DbLog ./db.conf .*:.*     # logt alle(!) auflaufenden Events aller Konfigurationen&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Da durch diese &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Definition alle auflaufenden Events gelogt werden, müssen keine weiteren Anpassungen in der Konfiguration gemacht werden. Die FileLog-Einträge können bedenkenlos bestehen bleiben - dann wird in Datenbank und FileLog gelogt und man verliert keine Daten, falls etwas nicht klappt. Wenn alles wie geplant läuft, können die FileLog-Definitionen gelöscht werden (ebenso die Log-Dateien). Ebenso können die zu loggenden Daten später eingegrenzt werden (s. [[#Finetuning des Loggings]]).&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;FHEM neu starten:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo service fhem stop&lt;br /&gt;
sudo service fhem start&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Kontrollieren, ob Logs in die Datenbank geschrieben werden:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 /opt/fhem/fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der geöffneten SQL-Kommandozeile eingeben:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
select * from history order by TIMESTAMP;       # dies gibt alle(!) Logs chronologisch aus (kann nach längerem Betrieb recht lange dauern)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Kommandozeile verlässt man mit &amp;lt;code&amp;gt;.exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anpassung der glot-Dateien:&#039;&#039; siehe [[#Anpassen der gplot-Konfigurationen]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer Mysql-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 apt-get update &amp;amp;&amp;amp; apt-get install mysql-server mysql-client libdbd-mysql libdbd-mysql-perl&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Installation sollte man aus Sicherheitsgründen ein Passwort für den mysql-root vergeben, wenn man nicht sogar ganz den Login verbietet.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der normale Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Zum Test mal mit mysql verbinden:&lt;br /&gt;
 # mysql -p -u root&lt;br /&gt;
 Enter password:&lt;br /&gt;
 mysql&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen (root Passwort wird abgefragt): &lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; show databases;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Integration von DBLog in eigene Module ==&lt;br /&gt;
=== Bereitstellung der UNITS ===&lt;br /&gt;
Mit der DbLog_splitFn kann der Modulautor selbst festlegen, wie die Events des Moduls in die Bestandteile Reading/Value/Unit zerlegt werden um ein korrektes Logging per DbLog zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
Dazu muss der Modulautor in der [[DevelopmentModuleIntro#X_Initialize|Initialize-Funktion]] eine &amp;lt;code&amp;gt;DbLog_splitFn&amp;lt;/code&amp;gt; bereitstellen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	...&lt;br /&gt;
	$hash-&amp;gt;{DbLog_splitFn}      = &amp;quot;X_DbLog_splitFn&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die genaue Aufrufsyntax und Funktionweise einer DbLog_split-Funktion findet man [[DevelopmentModuleIntro#X_DbLog_split|hier]].&lt;br /&gt;
&lt;br /&gt;
== Werte auslesen ==&lt;br /&gt;
Manchmal möchte man Daten aus den Logs abrufen ohne händisch in der Datenbank herumzuwühlen (s.u.). Dies ist insb. auch dann hilfreich, wenn man eigenen Funktionen, Notifys oder spezielle Plots entwirft, bei denen man auf Logdaten zugreifen möchte.&lt;br /&gt;
&lt;br /&gt;
Grundsätzlich beschrieben ist dies in der {{Link2CmdRef|Lang=de|Anker=DbLog}} und unterscheidet sich minimal (aber entscheidend) von der Struktur bei [[FileLog#Werte_auslesen|FileLogs]].&lt;br /&gt;
&lt;br /&gt;
Hier ein paar Beispiele, was man damit anstellen kann:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01 2016-10-03 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor vom 01.10.-03.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor von 8-16 Uhr am 01.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor:temperature&amp;lt;/code&amp;gt; nur die temperature Werte&lt;br /&gt;
* &amp;lt;code&amp;gt;{ ReadingsTimestamp(&amp;quot;meinSensor&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;0&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des aktuellen state des meinSensor&lt;br /&gt;
* &amp;lt;code&amp;gt;{ OldTimestamp(&amp;quot;meinSensor&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des letzten state des FHT_3a32&lt;br /&gt;
* &amp;lt;code&amp;gt;{ time_str2num(OldTimestamp(&amp;quot;meinSensor&amp;quot;)) }&amp;lt;/code&amp;gt; Timestamp in Sekunden des letzten state des meinSensor&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
&#039;&#039;&#039;Öffnen der DB unter Linux:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(Es werden Schreibrechte benötigt,ohne kann man die DB zwar öffnen, aber nichts machen)&lt;br /&gt;
 sudo sqlite3 fhem.db&lt;br /&gt;
Dadurch öffnet sich ein SQL-Konsole, auf der alle weiteren Befehle ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schliessen der DB:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .exit&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfe anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .help&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Tabellen anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .tables&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Das Schema der DB anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(vgl. oben [[DbLog#Datenbanken]] und [[DbLog#Beispiel: Anlegen und Nutzung einer SQLite-Datenbank]])&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .schema&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Eintäge anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Die Einträge liegen alle in der Tabelle &amp;quot;History&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ganz wichtig&#039;&#039;&#039; ist immer das &amp;quot;;&amp;quot; am Ende Zeile (bei allen Kommandos, die nicht mit einem &amp;quot;.&amp;quot; anfangen). Wenn es vergessen wurde zeigt die Konsole solange neue Zeilen bis ein &amp;quot;;&amp;quot; eingegeben wird. So kann ein Befehl auch bequem über mehrere Zeilen geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY;&lt;br /&gt;
&lt;br /&gt;
Dies kann sehr lange dauern und kann ggf. mit &amp;lt;code&amp;gt;STRG-C&amp;lt;/code&amp;gt; abgebrochen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;where&amp;lt;/code&amp;gt;-Statements werden Strings in einfache Anführungsstriche gesetzt, Zahlen nicht.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LÖSCHEN aller Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Löschen kann nicht rückgängig gemacht werden!! Also IMMER erst die entsprechenden SELECT-Statements solange verfeinern bis wirklich nur die gewünschten Einträge angezeigt werden. Dann das &amp;lt;code&amp;gt;select *&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; ersetzen.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; delete from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank reparieren ==&lt;br /&gt;
Es kann immer wieder mal vorkommen, dass Datenbanken Fehler enthalten. Das muss im Alltag garnicht auffallen und auch nicht immer schlimm enden. Wenn man auf der SQL-Konsole aber bspw. eine Meldung &amp;lt;code&amp;gt;Error: database disk image is malformed&amp;lt;/code&amp;gt; erhält, sollte man ein Reparatur vornehmen.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
Die folgenden Schritte beschreiben, wie man eine SQLite-DB reparieren kann (Quelle: [http://techblog.dorogin.com/2011/05/sqliteexception-database-disk-image-is.html]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
DB öffnen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Integritäts-Check durchführen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sqlite&amp;gt; pragma integrity_check;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kommt hier ein &amp;quot;ok&amp;quot; ist die DB gesund. Ansonsten erscheint etwas wie&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
*** in database main ***&lt;br /&gt;
On tree page 118786 cell 1: Rowid 75 out of order (previous was 816660)&lt;br /&gt;
On tree page 118786 cell 4: Rowid 815704 out of order (previous was 816727)&lt;br /&gt;
Corruption detected in cell 0 on page 118786&lt;br /&gt;
Multiple uses for byte 132 of page 118786&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Datenbank-Dump erstellen (Export gesamten DB in die Datei &amp;quot;dump_all_20160516_1043.sql&amp;quot;) und DB verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .mode insert&lt;br /&gt;
sqlite&amp;gt; .output dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; .dump&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Neue Datenbank erstellen und den Dump einlesen, Integritäts-Check machen und verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem-neu.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .read dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; pragma integrity_check;&lt;br /&gt;
ok&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Spätestens jetzt FHEM stoppen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem stop&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Alte DB sichern und neue aktivieren:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo mv fhem.db fhem.db.sv_20160516&lt;br /&gt;
sudo mv fhem-neu.db fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Kontrollieren, dass die neue DB die gleichen Rechte wie die alte DB hat (und ggf. korrigieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 root root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
~/fhem$ sudo chown fhem:root fhem.db&lt;br /&gt;
&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 fhem root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
FHEM wieder starten (und natürlich kontrollieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem start&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. Diese geschieht mittels des Userreadings, welches man vorteilshafterweise mit im DbLog-device anlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;attr myDbLog userReadings DbFileSize:reduceLogState.* { (split(&#039; &#039;,`du -m fhem.db`))[0] }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels dieses Attributs wird die Grösse der .db-Datei immer nach dem Ausführen des ReduceLog in das Reading &amp;quot;DbFileSize&amp;quot; in ganzzahligen MByte abgelegt.&lt;br /&gt;
&lt;br /&gt;
Basierend auf diesem Reading können dann weitere Aktionen, beispielsweise ein Plot, erstellt werden.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist für SQLite verwendbar. Zur Ermittlung der DB-Größe andere DB-Typen (aber auch für SQLite nutzbar) kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Performance-Optimierung ==&lt;br /&gt;
Auch eine Datenbank kann mit der Zeit langsamer werden. Dies hängt von mehreren Faktoren ab:&lt;br /&gt;
* Menge der gelogten Daten (zB. &amp;gt; 4-5 GB)&lt;br /&gt;
* Eingesetzte Hardware (zB. langsame SD-Karte vs. schnelle SSD)&lt;br /&gt;
* Eingesetztes Datenbanksystem (zB. SQLite, MySQL)&lt;br /&gt;
* Komplexität der Abfragen (zB. für aufwändige Graphen oder Berechnungen)&lt;br /&gt;
&lt;br /&gt;
Diese Punkte sollen im folgenden diskutiert werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komplexität der Abfragen ===&lt;br /&gt;
Dies ist kein Problem der Datenbank, sondern rein der Abfrage. Dem entsprechend muss die Optimierung auch in der Abfrage oder im Skript gesucht werden. Dies ist nicht Ziel dieses Abschnittes und wird hier nicht weiter behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetztes Datenbanksystem ===&lt;br /&gt;
Welches Datenbanksystem eingesetzt wird (zB. SQLite oder MySQL) hat auf die Performance der Datenbank gar keinen so großen Einfluss, wie vielleicht zuerst gedacht. Selbst SQLite kann problemlos Datenbanken mit etlichen GB Größe performant verarbeiten. Der Flaschenhals ist hier viel mehr die darunter liegende Hardware (s.u.).&lt;br /&gt;
&lt;br /&gt;
Die Performance der Datenbank an sich, kann aber durch verschiedene Maßnahmen verbessert werden:&lt;br /&gt;
* Pflegemaßnahmen bzgl. der Daten&lt;br /&gt;
* &#039;&#039;&#039;Erstellung von Indizes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Menge der Daten und Pflegemaßnahmen bzgl. der Daten ====&lt;br /&gt;
Die Menge der geloggten Daten hat natürlich Einfluss auf die Geschwindigkeit von Abfragen - je mehr Daten vorhanden sind, desto mehr Daten müssen auch durchforstet werden um eine Abfrage zu bedienen. Die Reduzierung der geloggten Datenmenge hat also direkten Einfluss auf die Größe und damit auch die Geschwindigkeit der Datenbank. Die Menge der zu loggenden Daten lässt sich an zwei Stellen einschränken:&lt;br /&gt;
* bei der Definition jedes Devices (s. Kapitel oben)&lt;br /&gt;
* bei der Festlegung des fhem-weiten Log-Levels (s. [[Loglevel]])&lt;br /&gt;
&lt;br /&gt;
Die Menge der bereits geloggten Daten kann zB. mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] verringert und optimiert werden, bspw.&lt;br /&gt;
* löschen unnötiger Daten&lt;br /&gt;
* vacuum der Datenbank&lt;br /&gt;
&lt;br /&gt;
Insgesamt haben diese Maßnahmen aber nur einen eingeschränkten Effekt auf die Performance der DB. Deutlich effektiver ist die Erstellung eines Index (s.u.).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Erstellung von Indizes ====&lt;br /&gt;
Die Erstellung von Indizes hat mit Abstand den größten Einfluss auf die Performance einer Datenbank (auf unveränderter Hardware). Extrem zusammengefasst ist ein Index eine extrem optimiertes Nachschlageverzeichnis für einen bestimmten Typ Daten (ein Index wie im Buch halt). Eine wunderbare Einführung in Indizes bietet [[https://use-the-index-luke.com/de|https://use-the-index-luke.com]].&lt;br /&gt;
&lt;br /&gt;
In fhem sind Indizes sogar sehr einfach einzurichten da die Datenbank-Nutzung sehr stark vorgegeben ist. Nahezu jede Abfrage folgt dem Schema &#039;&#039;Device -&amp;gt; Reading -&amp;gt; Datum -&amp;gt; Wert&#039;&#039;. Ein Index kann genau diese Abfrage bedienen und beschleunigen. Ein Index nur über die Devices wäre ein erster Schritt, brächte aber noch keinen großen Gewinn (wie um Link oben gut beschrieben). Über die gesamten ersten drei Schritte erstellt (Device -&amp;gt; Reading -&amp;gt; Datum) bringt der Index aber sofort eine deutliche Geschwindigkeitssteigerung.&lt;br /&gt;
&lt;br /&gt;
Die Erstellung eines Index erfolgt direkt in der Datenbank (und nicht aus fhem heraus), hier am Beispiel einer SQLite-DB:&lt;br /&gt;
&lt;br /&gt;
Öffnen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; sudo sqlite3 fhem.db &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugen des Index auf der DB-Konsole (das Semikolon am Ende ist wichtig):&lt;br /&gt;
&amp;lt;pre&amp;gt; create index idx_device_reading_timestamp on history (device, reading, timestamp); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verlassen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; .exit &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzig zu berücksichtigen ist, dass dieser Index die Datenbank um bis zu 1/3 vergrößert. Er kann aber bei Bedarf auch wieder entfernt werden. Bei meiner 15 GB SQLite-Datei (auf einem Mac Mini mit SSD) hat dies ca. 15 min gedauert (den fhem hatte ich vorsichtshalber währenddessen deaktiviert).&lt;br /&gt;
&lt;br /&gt;
Sollte jemand spezielle Berechnungen oder Skripte ausführen, die nach einem anderen Abfrage-Schema arbeiten, könnte man dafür spezialisierte zusätzliche Indizes erstellen. Das sollte aber dann mit dem Wissen des obigen Links erarbeitet werden, da dann etwas mehr Hintergrundwissen sehr hilfreich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== DB-Backup ====&lt;br /&gt;
Ein anderer Aspekt, der eigentlich nichts mit der Performance der DB zu tun hat, ist der Einfluss aufs Backup. Wird bspw. ein Systembackup per RSYNC gemacht, muss bei SQLite immer die komplette ggf. riesige Datei gesynct werden - bei MySQL würden nur die veränderten DB-Elemente gesynct. Dies soll hier nicht weiter vertieft werden, sollte aber bei einer Gesamtstrategie bedacht werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetzte Hardware ===&lt;br /&gt;
fhem hat grundsätzlich sehr viele kleine Datenzugriffe, unabhängig davon ob FileLog oder DbLog eingesetzt wird. Deshalb ist der absolut größte Performance-Gewinn durch den Einsatz schneller Festplatten zu erreichen - ganz einfache Aussage: &#039;&#039;je schneller desto besser ;-)&#039;&#039;. Konkret bietet eine SSD mit mind. 250MB/s Datenzugriff eine ordentliche Basis für jedes datengestützte System, wie den fhem.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenmenge größer und die Abfragen komplexer werden, müssen natürlich irgendwann auch die Prozessorleistung und der Arbeitsspeicher mit wachsen. Aber auch an einem Raspi wird eine SSD deutlich performanter sein, als eine einfache SD-Karte oder eine klassische rotierende Festplatte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31043</id>
		<title>DbLog-MySQL</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31043"/>
		<updated>2019-07-29T06:49:13Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Datenbank */  Anpassung auf mysql&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Da bei der Inbetriebnahme und der Migration von MySQL bzw MariaDB-Datenbanken einige kleine Fallen auftreten können, gibt&#039;s hier ein paar Hinweise.&lt;br /&gt;
&lt;br /&gt;
== Server version: 10.1.40-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04 ==&lt;br /&gt;
=== db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MariaDB-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp. Bei der Verwendung von MariaDB muss die MySQL-Datei verwendet werden.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; befindet sich das Script &#039;&#039;&#039;db_create_mysql.sql&#039;&#039;&#039; , welches eine neue Datenbank samt Tabellenstruktur anlegt. Ausserdem wird ein Benutzer &amp;quot;fhemuser&amp;quot; mit einem (zu ändernden) &amp;quot;fhempasswort&amp;quot; angelegt.&lt;br /&gt;
==== Primary Keys ====&lt;br /&gt;
===== current =====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== history =====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`history` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`TIMESTAMP`,`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anpassen der gplot-Konfigurationen ==&lt;br /&gt;
Die meisten gplot-Konfigurationen sind bisher lediglich auf FileLog-Konfigurationen ausgelegt. Deshalb müssen sie für die Verwendung mit DbLog angepasst werden. Glücklicherweise beschränkt sich dies auf die reinen FileLog-Zeilen - es müssen die DbLog-Äquivalente hinzugefügt werden. Die FileLog-Einträge müssen zwar nicht gelöscht werden, wenn man aber FileLog und DbLog parallel betreibt, sollte man getrennte gplot-Dateien für beide Logging-Typen haben um Auswertungsprobleme erkennen zu können.&lt;br /&gt;
&lt;br /&gt;
Für die fht.gplot Konfiguration sähe die Anpassung wie folgt aus (lediglich die vier DbLog-Zeilen wurden hinzugefügt):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2014-12-25 21:53: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 nomirror&lt;br /&gt;
set y2tics &lt;br /&gt;
set grid y2tics&lt;br /&gt;
set ylabel &amp;quot;Actuator/Window (%)&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Temperature in C&amp;quot;&lt;br /&gt;
set yrange 0:100&lt;br /&gt;
set y2range 5:25&lt;br /&gt;
&lt;br /&gt;
#FileLog 4:.measured-temp\x3a:0:&lt;br /&gt;
#FileLog 4:.actuator\x3a:0:int&lt;br /&gt;
#FileLog 4:.desired-temp::&lt;br /&gt;
#FileLog 4:.window\x3a::&lt;br /&gt;
&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.measured-temp:0:&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.actuator:0:int&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.desired-temp::&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.window::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Measured temperature&#039; ls l0 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Actuator (%)&#039; ls l1 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Desired Temperature&#039; ls l2 lw 1 with steps,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Window&#039; ls l3 lw 1 with steps&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Des Weiteren ist zu beachten: &lt;br /&gt;
&lt;br /&gt;
On-Off-Plots&lt;br /&gt;
&lt;br /&gt;
 EG_Bad:window:::$val=~s/(on|off)(\d*).*/$1eq&amp;quot;on&amp;quot;?1:0/eg&lt;br /&gt;
&lt;br /&gt;
unter Berücksichtigung von dim-Werten:&lt;br /&gt;
&lt;br /&gt;
 EG_WoZi_Licht:value:::$val=~s/(on|off)(\d*).*/$1eq&amp;quot;on&amp;quot;?1:($1eq&amp;quot;dim&amp;quot;?$2*0.01:0)/eg&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer SQLite-Datenbank ==&lt;br /&gt;
Im folgenden wird eine lokale SQLite-Datenbank auf einen Ubuntu-System angelegt (nach Quelle: [http://www.tatsch-it.de/fhem-dblog/ http://www.tatsch-it.de/fhem-dblog/])&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Installation von SQLite:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo aptitude install sqlite3 libdbi-perl libdbd-sqlite3-perl&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anlegen der SQLite-Datenbank fhem.db&#039;&#039; (öffnet auch direkt eine SQL-Kommandozeile):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 /opt/fhem/fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der geöffneten SQL-Kommandozeile eingeben:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE &#039;history&#039; (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
CREATE TABLE &#039;current&#039; (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
CREATE INDEX Search_Idx ON `history` (DEVICE, READING, TIMESTAMP);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Kommandozeile verlässt man mit &amp;lt;code&amp;gt;.exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anpassen des Besitzers und der Rechte der Datenbank-Datei:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo chown fhem /opt/fhem/fhem.db&lt;br /&gt;
sudo chmod 666 /opt/fhem/fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Datenbank-Anbindung des FHEM konfigurieren:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo nano /opt/fhem/db.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
Inhalt:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%dbconfig= (&lt;br /&gt;
  connection =&amp;gt; &amp;quot;SQLite:dbname=/opt/fhem/fhem.db&amp;quot;,&lt;br /&gt;
  user =&amp;gt; &amp;quot;&amp;quot;,&lt;br /&gt;
  password =&amp;gt; &amp;quot;&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Logging des FHEM auf die Datenbank konfigurieren:&#039;&#039; (hier sind nur die Anpassungen aufgeführt)&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo nano /opt/fhem/fhem.cfg&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
attr global userattr DbLogExclude ...  # erlaubt es einzelne Einträge nicht zu loggen&lt;br /&gt;
...&lt;br /&gt;
define logdb DbLog ./db.conf .*:.*     # logt alle(!) auflaufenden Events aller Konfigurationen&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Da durch diese &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Definition alle auflaufenden Events gelogt werden, müssen keine weiteren Anpassungen in der Konfiguration gemacht werden. Die FileLog-Einträge können bedenkenlos bestehen bleiben - dann wird in Datenbank und FileLog gelogt und man verliert keine Daten, falls etwas nicht klappt. Wenn alles wie geplant läuft, können die FileLog-Definitionen gelöscht werden (ebenso die Log-Dateien). Ebenso können die zu loggenden Daten später eingegrenzt werden (s. [[#Finetuning des Loggings]]).&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;FHEM neu starten:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo service fhem stop&lt;br /&gt;
sudo service fhem start&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Kontrollieren, ob Logs in die Datenbank geschrieben werden:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 /opt/fhem/fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der geöffneten SQL-Kommandozeile eingeben:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
select * from history order by TIMESTAMP;       # dies gibt alle(!) Logs chronologisch aus (kann nach längerem Betrieb recht lange dauern)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Kommandozeile verlässt man mit &amp;lt;code&amp;gt;.exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anpassung der glot-Dateien:&#039;&#039; siehe [[#Anpassen der gplot-Konfigurationen]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer Mysql-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 apt-get update &amp;amp;&amp;amp; apt-get install mysql-server mysql-client libdbd-mysql libdbd-mysql-perl&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Installation sollte man aus Sicherheitsgründen ein Passwort für den mysql-root vergeben, wenn man nicht sogar ganz den Login verbietet.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der normale Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Zum Test mal mit mysql verbinden:&lt;br /&gt;
 # mysql -p -u root&lt;br /&gt;
 Enter password:&lt;br /&gt;
 mysql&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen (root Passwort wird abgefragt): &lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; show databases;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Integration von DBLog in eigene Module ==&lt;br /&gt;
=== Bereitstellung der UNITS ===&lt;br /&gt;
Mit der DbLog_splitFn kann der Modulautor selbst festlegen, wie die Events des Moduls in die Bestandteile Reading/Value/Unit zerlegt werden um ein korrektes Logging per DbLog zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
Dazu muss der Modulautor in der [[DevelopmentModuleIntro#X_Initialize|Initialize-Funktion]] eine &amp;lt;code&amp;gt;DbLog_splitFn&amp;lt;/code&amp;gt; bereitstellen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	...&lt;br /&gt;
	$hash-&amp;gt;{DbLog_splitFn}      = &amp;quot;X_DbLog_splitFn&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die genaue Aufrufsyntax und Funktionweise einer DbLog_split-Funktion findet man [[DevelopmentModuleIntro#X_DbLog_split|hier]].&lt;br /&gt;
&lt;br /&gt;
== Werte auslesen ==&lt;br /&gt;
Manchmal möchte man Daten aus den Logs abrufen ohne händisch in der Datenbank herumzuwühlen (s.u.). Dies ist insb. auch dann hilfreich, wenn man eigenen Funktionen, Notifys oder spezielle Plots entwirft, bei denen man auf Logdaten zugreifen möchte.&lt;br /&gt;
&lt;br /&gt;
Grundsätzlich beschrieben ist dies in der {{Link2CmdRef|Lang=de|Anker=DbLog}} und unterscheidet sich minimal (aber entscheidend) von der Struktur bei [[FileLog#Werte_auslesen|FileLogs]].&lt;br /&gt;
&lt;br /&gt;
Hier ein paar Beispiele, was man damit anstellen kann:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01 2016-10-03 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor vom 01.10.-03.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor von 8-16 Uhr am 01.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor:temperature&amp;lt;/code&amp;gt; nur die temperature Werte&lt;br /&gt;
* &amp;lt;code&amp;gt;{ ReadingsTimestamp(&amp;quot;meinSensor&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;0&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des aktuellen state des meinSensor&lt;br /&gt;
* &amp;lt;code&amp;gt;{ OldTimestamp(&amp;quot;meinSensor&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des letzten state des FHT_3a32&lt;br /&gt;
* &amp;lt;code&amp;gt;{ time_str2num(OldTimestamp(&amp;quot;meinSensor&amp;quot;)) }&amp;lt;/code&amp;gt; Timestamp in Sekunden des letzten state des meinSensor&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
&#039;&#039;&#039;Öffnen der DB unter Linux:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(Es werden Schreibrechte benötigt,ohne kann man die DB zwar öffnen, aber nichts machen)&lt;br /&gt;
 sudo sqlite3 fhem.db&lt;br /&gt;
Dadurch öffnet sich ein SQL-Konsole, auf der alle weiteren Befehle ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schliessen der DB:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .exit&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfe anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .help&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Tabellen anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .tables&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Das Schema der DB anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(vgl. oben [[DbLog#Datenbanken]] und [[DbLog#Beispiel: Anlegen und Nutzung einer SQLite-Datenbank]])&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .schema&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Eintäge anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Die Einträge liegen alle in der Tabelle &amp;quot;History&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ganz wichtig&#039;&#039;&#039; ist immer das &amp;quot;;&amp;quot; am Ende Zeile (bei allen Kommandos, die nicht mit einem &amp;quot;.&amp;quot; anfangen). Wenn es vergessen wurde zeigt die Konsole solange neue Zeilen bis ein &amp;quot;;&amp;quot; eingegeben wird. So kann ein Befehl auch bequem über mehrere Zeilen geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY;&lt;br /&gt;
&lt;br /&gt;
Dies kann sehr lange dauern und kann ggf. mit &amp;lt;code&amp;gt;STRG-C&amp;lt;/code&amp;gt; abgebrochen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;where&amp;lt;/code&amp;gt;-Statements werden Strings in einfache Anführungsstriche gesetzt, Zahlen nicht.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LÖSCHEN aller Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Löschen kann nicht rückgängig gemacht werden!! Also IMMER erst die entsprechenden SELECT-Statements solange verfeinern bis wirklich nur die gewünschten Einträge angezeigt werden. Dann das &amp;lt;code&amp;gt;select *&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; ersetzen.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; delete from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank reparieren ==&lt;br /&gt;
Es kann immer wieder mal vorkommen, dass Datenbanken Fehler enthalten. Das muss im Alltag garnicht auffallen und auch nicht immer schlimm enden. Wenn man auf der SQL-Konsole aber bspw. eine Meldung &amp;lt;code&amp;gt;Error: database disk image is malformed&amp;lt;/code&amp;gt; erhält, sollte man ein Reparatur vornehmen.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
Die folgenden Schritte beschreiben, wie man eine SQLite-DB reparieren kann (Quelle: [http://techblog.dorogin.com/2011/05/sqliteexception-database-disk-image-is.html]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
DB öffnen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Integritäts-Check durchführen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sqlite&amp;gt; pragma integrity_check;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kommt hier ein &amp;quot;ok&amp;quot; ist die DB gesund. Ansonsten erscheint etwas wie&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
*** in database main ***&lt;br /&gt;
On tree page 118786 cell 1: Rowid 75 out of order (previous was 816660)&lt;br /&gt;
On tree page 118786 cell 4: Rowid 815704 out of order (previous was 816727)&lt;br /&gt;
Corruption detected in cell 0 on page 118786&lt;br /&gt;
Multiple uses for byte 132 of page 118786&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Datenbank-Dump erstellen (Export gesamten DB in die Datei &amp;quot;dump_all_20160516_1043.sql&amp;quot;) und DB verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .mode insert&lt;br /&gt;
sqlite&amp;gt; .output dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; .dump&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Neue Datenbank erstellen und den Dump einlesen, Integritäts-Check machen und verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem-neu.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .read dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; pragma integrity_check;&lt;br /&gt;
ok&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Spätestens jetzt FHEM stoppen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem stop&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Alte DB sichern und neue aktivieren:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo mv fhem.db fhem.db.sv_20160516&lt;br /&gt;
sudo mv fhem-neu.db fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Kontrollieren, dass die neue DB die gleichen Rechte wie die alte DB hat (und ggf. korrigieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 root root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
~/fhem$ sudo chown fhem:root fhem.db&lt;br /&gt;
&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 fhem root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
FHEM wieder starten (und natürlich kontrollieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem start&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. Diese geschieht mittels des Userreadings, welches man vorteilshafterweise mit im DbLog-device anlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;attr myDbLog userReadings DbFileSize:reduceLogState.* { (split(&#039; &#039;,`du -m fhem.db`))[0] }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels dieses Attributs wird die Grösse der .db-Datei immer nach dem Ausführen des ReduceLog in das Reading &amp;quot;DbFileSize&amp;quot; in ganzzahligen MByte abgelegt.&lt;br /&gt;
&lt;br /&gt;
Basierend auf diesem Reading können dann weitere Aktionen, beispielsweise ein Plot, erstellt werden.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist für SQLite verwendbar. Zur Ermittlung der DB-Größe andere DB-Typen (aber auch für SQLite nutzbar) kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Performance-Optimierung ==&lt;br /&gt;
Auch eine Datenbank kann mit der Zeit langsamer werden. Dies hängt von mehreren Faktoren ab:&lt;br /&gt;
* Menge der gelogten Daten (zB. &amp;gt; 4-5 GB)&lt;br /&gt;
* Eingesetzte Hardware (zB. langsame SD-Karte vs. schnelle SSD)&lt;br /&gt;
* Eingesetztes Datenbanksystem (zB. SQLite, MySQL)&lt;br /&gt;
* Komplexität der Abfragen (zB. für aufwändige Graphen oder Berechnungen)&lt;br /&gt;
&lt;br /&gt;
Diese Punkte sollen im folgenden diskutiert werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komplexität der Abfragen ===&lt;br /&gt;
Dies ist kein Problem der Datenbank, sondern rein der Abfrage. Dem entsprechend muss die Optimierung auch in der Abfrage oder im Skript gesucht werden. Dies ist nicht Ziel dieses Abschnittes und wird hier nicht weiter behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetztes Datenbanksystem ===&lt;br /&gt;
Welches Datenbanksystem eingesetzt wird (zB. SQLite oder MySQL) hat auf die Performance der Datenbank gar keinen so großen Einfluss, wie vielleicht zuerst gedacht. Selbst SQLite kann problemlos Datenbanken mit etlichen GB Größe performant verarbeiten. Der Flaschenhals ist hier viel mehr die darunter liegende Hardware (s.u.).&lt;br /&gt;
&lt;br /&gt;
Die Performance der Datenbank an sich, kann aber durch verschiedene Maßnahmen verbessert werden:&lt;br /&gt;
* Pflegemaßnahmen bzgl. der Daten&lt;br /&gt;
* &#039;&#039;&#039;Erstellung von Indizes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Menge der Daten und Pflegemaßnahmen bzgl. der Daten ====&lt;br /&gt;
Die Menge der geloggten Daten hat natürlich Einfluss auf die Geschwindigkeit von Abfragen - je mehr Daten vorhanden sind, desto mehr Daten müssen auch durchforstet werden um eine Abfrage zu bedienen. Die Reduzierung der geloggten Datenmenge hat also direkten Einfluss auf die Größe und damit auch die Geschwindigkeit der Datenbank. Die Menge der zu loggenden Daten lässt sich an zwei Stellen einschränken:&lt;br /&gt;
* bei der Definition jedes Devices (s. Kapitel oben)&lt;br /&gt;
* bei der Festlegung des fhem-weiten Log-Levels (s. [[Loglevel]])&lt;br /&gt;
&lt;br /&gt;
Die Menge der bereits geloggten Daten kann zB. mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] verringert und optimiert werden, bspw.&lt;br /&gt;
* löschen unnötiger Daten&lt;br /&gt;
* vacuum der Datenbank&lt;br /&gt;
&lt;br /&gt;
Insgesamt haben diese Maßnahmen aber nur einen eingeschränkten Effekt auf die Performance der DB. Deutlich effektiver ist die Erstellung eines Index (s.u.).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Erstellung von Indizes ====&lt;br /&gt;
Die Erstellung von Indizes hat mit Abstand den größten Einfluss auf die Performance einer Datenbank (auf unveränderter Hardware). Extrem zusammengefasst ist ein Index eine extrem optimiertes Nachschlageverzeichnis für einen bestimmten Typ Daten (ein Index wie im Buch halt). Eine wunderbare Einführung in Indizes bietet [[https://use-the-index-luke.com/de|https://use-the-index-luke.com]].&lt;br /&gt;
&lt;br /&gt;
In fhem sind Indizes sogar sehr einfach einzurichten da die Datenbank-Nutzung sehr stark vorgegeben ist. Nahezu jede Abfrage folgt dem Schema &#039;&#039;Device -&amp;gt; Reading -&amp;gt; Datum -&amp;gt; Wert&#039;&#039;. Ein Index kann genau diese Abfrage bedienen und beschleunigen. Ein Index nur über die Devices wäre ein erster Schritt, brächte aber noch keinen großen Gewinn (wie um Link oben gut beschrieben). Über die gesamten ersten drei Schritte erstellt (Device -&amp;gt; Reading -&amp;gt; Datum) bringt der Index aber sofort eine deutliche Geschwindigkeitssteigerung.&lt;br /&gt;
&lt;br /&gt;
Die Erstellung eines Index erfolgt direkt in der Datenbank (und nicht aus fhem heraus), hier am Beispiel einer SQLite-DB:&lt;br /&gt;
&lt;br /&gt;
Öffnen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; sudo sqlite3 fhem.db &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugen des Index auf der DB-Konsole (das Semikolon am Ende ist wichtig):&lt;br /&gt;
&amp;lt;pre&amp;gt; create index idx_device_reading_timestamp on history (device, reading, timestamp); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verlassen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; .exit &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzig zu berücksichtigen ist, dass dieser Index die Datenbank um bis zu 1/3 vergrößert. Er kann aber bei Bedarf auch wieder entfernt werden. Bei meiner 15 GB SQLite-Datei (auf einem Mac Mini mit SSD) hat dies ca. 15 min gedauert (den fhem hatte ich vorsichtshalber währenddessen deaktiviert).&lt;br /&gt;
&lt;br /&gt;
Sollte jemand spezielle Berechnungen oder Skripte ausführen, die nach einem anderen Abfrage-Schema arbeiten, könnte man dafür spezialisierte zusätzliche Indizes erstellen. Das sollte aber dann mit dem Wissen des obigen Links erarbeitet werden, da dann etwas mehr Hintergrundwissen sehr hilfreich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== DB-Backup ====&lt;br /&gt;
Ein anderer Aspekt, der eigentlich nichts mit der Performance der DB zu tun hat, ist der Einfluss aufs Backup. Wird bspw. ein Systembackup per RSYNC gemacht, muss bei SQLite immer die komplette ggf. riesige Datei gesynct werden - bei MySQL würden nur die veränderten DB-Elemente gesynct. Dies soll hier nicht weiter vertieft werden, sollte aber bei einer Gesamtstrategie bedacht werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetzte Hardware ===&lt;br /&gt;
fhem hat grundsätzlich sehr viele kleine Datenzugriffe, unabhängig davon ob FileLog oder DbLog eingesetzt wird. Deshalb ist der absolut größte Performance-Gewinn durch den Einsatz schneller Festplatten zu erreichen - ganz einfache Aussage: &#039;&#039;je schneller desto besser ;-)&#039;&#039;. Konkret bietet eine SSD mit mind. 250MB/s Datenzugriff eine ordentliche Basis für jedes datengestützte System, wie den fhem.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenmenge größer und die Abfragen komplexer werden, müssen natürlich irgendwann auch die Prozessorleistung und der Arbeitsspeicher mit wachsen. Aber auch an einem Raspi wird eine SSD deutlich performanter sein, als eine einfache SD-Karte oder eine klassische rotierende Festplatte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31042</id>
		<title>DbLog-MySQL</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31042"/>
		<updated>2019-07-29T06:44:10Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Konfiguration */ Anpassung auf MariaDB&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Da bei der Inbetriebnahme und der Migration von MySQL bzw MariaDB-Datenbanken einige kleine Fallen auftreten können, gibt&#039;s hier ein paar Hinweise.&lt;br /&gt;
&lt;br /&gt;
== Server version: 10.1.40-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04 ==&lt;br /&gt;
=== db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MariaDB-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp. Bei der Verwendung von MariaDB muss die MySQL-Datei verwendet werden.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
== Datenbank ==&lt;br /&gt;
Unterstützte Datenbanksysteme (Auswahl):&lt;br /&gt;
* Sqlite&lt;br /&gt;
* MySQL&lt;br /&gt;
* PostGreSql&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen zur Anlage von Tabellen und Indizes sind für jeden unterstützten Datenbanktyp im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation, oder hier zu finden: [https://svn.fhem.de/trac/browser/trunk/fhem/contrib/dblog/ Link]. Das MySQL-Skript (db_create_mysql.sql) legt eine neue Datenbank, das PostGres-Skript (db_create_postgresql.sql) ein neues Schema mit Namen &amp;quot;fhem&amp;quot; an.&lt;br /&gt;
&lt;br /&gt;
==== current ====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== history ====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
{{Todo|Ausbauen}}&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
== Anpassen der gplot-Konfigurationen ==&lt;br /&gt;
Die meisten gplot-Konfigurationen sind bisher lediglich auf FileLog-Konfigurationen ausgelegt. Deshalb müssen sie für die Verwendung mit DbLog angepasst werden. Glücklicherweise beschränkt sich dies auf die reinen FileLog-Zeilen - es müssen die DbLog-Äquivalente hinzugefügt werden. Die FileLog-Einträge müssen zwar nicht gelöscht werden, wenn man aber FileLog und DbLog parallel betreibt, sollte man getrennte gplot-Dateien für beide Logging-Typen haben um Auswertungsprobleme erkennen zu können.&lt;br /&gt;
&lt;br /&gt;
Für die fht.gplot Konfiguration sähe die Anpassung wie folgt aus (lediglich die vier DbLog-Zeilen wurden hinzugefügt):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2014-12-25 21:53: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 nomirror&lt;br /&gt;
set y2tics &lt;br /&gt;
set grid y2tics&lt;br /&gt;
set ylabel &amp;quot;Actuator/Window (%)&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Temperature in C&amp;quot;&lt;br /&gt;
set yrange 0:100&lt;br /&gt;
set y2range 5:25&lt;br /&gt;
&lt;br /&gt;
#FileLog 4:.measured-temp\x3a:0:&lt;br /&gt;
#FileLog 4:.actuator\x3a:0:int&lt;br /&gt;
#FileLog 4:.desired-temp::&lt;br /&gt;
#FileLog 4:.window\x3a::&lt;br /&gt;
&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.measured-temp:0:&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.actuator:0:int&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.desired-temp::&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.window::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Measured temperature&#039; ls l0 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Actuator (%)&#039; ls l1 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Desired Temperature&#039; ls l2 lw 1 with steps,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Window&#039; ls l3 lw 1 with steps&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Des Weiteren ist zu beachten: &lt;br /&gt;
&lt;br /&gt;
On-Off-Plots&lt;br /&gt;
&lt;br /&gt;
 EG_Bad:window:::$val=~s/(on|off)(\d*).*/$1eq&amp;quot;on&amp;quot;?1:0/eg&lt;br /&gt;
&lt;br /&gt;
unter Berücksichtigung von dim-Werten:&lt;br /&gt;
&lt;br /&gt;
 EG_WoZi_Licht:value:::$val=~s/(on|off)(\d*).*/$1eq&amp;quot;on&amp;quot;?1:($1eq&amp;quot;dim&amp;quot;?$2*0.01:0)/eg&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer SQLite-Datenbank ==&lt;br /&gt;
Im folgenden wird eine lokale SQLite-Datenbank auf einen Ubuntu-System angelegt (nach Quelle: [http://www.tatsch-it.de/fhem-dblog/ http://www.tatsch-it.de/fhem-dblog/])&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Installation von SQLite:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo aptitude install sqlite3 libdbi-perl libdbd-sqlite3-perl&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anlegen der SQLite-Datenbank fhem.db&#039;&#039; (öffnet auch direkt eine SQL-Kommandozeile):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 /opt/fhem/fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der geöffneten SQL-Kommandozeile eingeben:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE &#039;history&#039; (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
CREATE TABLE &#039;current&#039; (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
CREATE INDEX Search_Idx ON `history` (DEVICE, READING, TIMESTAMP);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Kommandozeile verlässt man mit &amp;lt;code&amp;gt;.exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anpassen des Besitzers und der Rechte der Datenbank-Datei:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo chown fhem /opt/fhem/fhem.db&lt;br /&gt;
sudo chmod 666 /opt/fhem/fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Datenbank-Anbindung des FHEM konfigurieren:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo nano /opt/fhem/db.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
Inhalt:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%dbconfig= (&lt;br /&gt;
  connection =&amp;gt; &amp;quot;SQLite:dbname=/opt/fhem/fhem.db&amp;quot;,&lt;br /&gt;
  user =&amp;gt; &amp;quot;&amp;quot;,&lt;br /&gt;
  password =&amp;gt; &amp;quot;&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Logging des FHEM auf die Datenbank konfigurieren:&#039;&#039; (hier sind nur die Anpassungen aufgeführt)&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo nano /opt/fhem/fhem.cfg&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
attr global userattr DbLogExclude ...  # erlaubt es einzelne Einträge nicht zu loggen&lt;br /&gt;
...&lt;br /&gt;
define logdb DbLog ./db.conf .*:.*     # logt alle(!) auflaufenden Events aller Konfigurationen&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Da durch diese &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Definition alle auflaufenden Events gelogt werden, müssen keine weiteren Anpassungen in der Konfiguration gemacht werden. Die FileLog-Einträge können bedenkenlos bestehen bleiben - dann wird in Datenbank und FileLog gelogt und man verliert keine Daten, falls etwas nicht klappt. Wenn alles wie geplant läuft, können die FileLog-Definitionen gelöscht werden (ebenso die Log-Dateien). Ebenso können die zu loggenden Daten später eingegrenzt werden (s. [[#Finetuning des Loggings]]).&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;FHEM neu starten:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo service fhem stop&lt;br /&gt;
sudo service fhem start&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Kontrollieren, ob Logs in die Datenbank geschrieben werden:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 /opt/fhem/fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der geöffneten SQL-Kommandozeile eingeben:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
select * from history order by TIMESTAMP;       # dies gibt alle(!) Logs chronologisch aus (kann nach längerem Betrieb recht lange dauern)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Kommandozeile verlässt man mit &amp;lt;code&amp;gt;.exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anpassung der glot-Dateien:&#039;&#039; siehe [[#Anpassen der gplot-Konfigurationen]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer Mysql-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 apt-get update &amp;amp;&amp;amp; apt-get install mysql-server mysql-client libdbd-mysql libdbd-mysql-perl&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Installation sollte man aus Sicherheitsgründen ein Passwort für den mysql-root vergeben, wenn man nicht sogar ganz den Login verbietet.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der normale Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Zum Test mal mit mysql verbinden:&lt;br /&gt;
 # mysql -p -u root&lt;br /&gt;
 Enter password:&lt;br /&gt;
 mysql&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen (root Passwort wird abgefragt): &lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; show databases;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Integration von DBLog in eigene Module ==&lt;br /&gt;
=== Bereitstellung der UNITS ===&lt;br /&gt;
Mit der DbLog_splitFn kann der Modulautor selbst festlegen, wie die Events des Moduls in die Bestandteile Reading/Value/Unit zerlegt werden um ein korrektes Logging per DbLog zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
Dazu muss der Modulautor in der [[DevelopmentModuleIntro#X_Initialize|Initialize-Funktion]] eine &amp;lt;code&amp;gt;DbLog_splitFn&amp;lt;/code&amp;gt; bereitstellen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	...&lt;br /&gt;
	$hash-&amp;gt;{DbLog_splitFn}      = &amp;quot;X_DbLog_splitFn&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die genaue Aufrufsyntax und Funktionweise einer DbLog_split-Funktion findet man [[DevelopmentModuleIntro#X_DbLog_split|hier]].&lt;br /&gt;
&lt;br /&gt;
== Werte auslesen ==&lt;br /&gt;
Manchmal möchte man Daten aus den Logs abrufen ohne händisch in der Datenbank herumzuwühlen (s.u.). Dies ist insb. auch dann hilfreich, wenn man eigenen Funktionen, Notifys oder spezielle Plots entwirft, bei denen man auf Logdaten zugreifen möchte.&lt;br /&gt;
&lt;br /&gt;
Grundsätzlich beschrieben ist dies in der {{Link2CmdRef|Lang=de|Anker=DbLog}} und unterscheidet sich minimal (aber entscheidend) von der Struktur bei [[FileLog#Werte_auslesen|FileLogs]].&lt;br /&gt;
&lt;br /&gt;
Hier ein paar Beispiele, was man damit anstellen kann:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01 2016-10-03 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor vom 01.10.-03.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor von 8-16 Uhr am 01.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor:temperature&amp;lt;/code&amp;gt; nur die temperature Werte&lt;br /&gt;
* &amp;lt;code&amp;gt;{ ReadingsTimestamp(&amp;quot;meinSensor&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;0&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des aktuellen state des meinSensor&lt;br /&gt;
* &amp;lt;code&amp;gt;{ OldTimestamp(&amp;quot;meinSensor&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des letzten state des FHT_3a32&lt;br /&gt;
* &amp;lt;code&amp;gt;{ time_str2num(OldTimestamp(&amp;quot;meinSensor&amp;quot;)) }&amp;lt;/code&amp;gt; Timestamp in Sekunden des letzten state des meinSensor&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
&#039;&#039;&#039;Öffnen der DB unter Linux:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(Es werden Schreibrechte benötigt,ohne kann man die DB zwar öffnen, aber nichts machen)&lt;br /&gt;
 sudo sqlite3 fhem.db&lt;br /&gt;
Dadurch öffnet sich ein SQL-Konsole, auf der alle weiteren Befehle ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schliessen der DB:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .exit&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfe anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .help&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Tabellen anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .tables&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Das Schema der DB anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(vgl. oben [[DbLog#Datenbanken]] und [[DbLog#Beispiel: Anlegen und Nutzung einer SQLite-Datenbank]])&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .schema&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Eintäge anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Die Einträge liegen alle in der Tabelle &amp;quot;History&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ganz wichtig&#039;&#039;&#039; ist immer das &amp;quot;;&amp;quot; am Ende Zeile (bei allen Kommandos, die nicht mit einem &amp;quot;.&amp;quot; anfangen). Wenn es vergessen wurde zeigt die Konsole solange neue Zeilen bis ein &amp;quot;;&amp;quot; eingegeben wird. So kann ein Befehl auch bequem über mehrere Zeilen geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY;&lt;br /&gt;
&lt;br /&gt;
Dies kann sehr lange dauern und kann ggf. mit &amp;lt;code&amp;gt;STRG-C&amp;lt;/code&amp;gt; abgebrochen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;where&amp;lt;/code&amp;gt;-Statements werden Strings in einfache Anführungsstriche gesetzt, Zahlen nicht.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LÖSCHEN aller Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Löschen kann nicht rückgängig gemacht werden!! Also IMMER erst die entsprechenden SELECT-Statements solange verfeinern bis wirklich nur die gewünschten Einträge angezeigt werden. Dann das &amp;lt;code&amp;gt;select *&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; ersetzen.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; delete from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank reparieren ==&lt;br /&gt;
Es kann immer wieder mal vorkommen, dass Datenbanken Fehler enthalten. Das muss im Alltag garnicht auffallen und auch nicht immer schlimm enden. Wenn man auf der SQL-Konsole aber bspw. eine Meldung &amp;lt;code&amp;gt;Error: database disk image is malformed&amp;lt;/code&amp;gt; erhält, sollte man ein Reparatur vornehmen.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
Die folgenden Schritte beschreiben, wie man eine SQLite-DB reparieren kann (Quelle: [http://techblog.dorogin.com/2011/05/sqliteexception-database-disk-image-is.html]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
DB öffnen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Integritäts-Check durchführen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sqlite&amp;gt; pragma integrity_check;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kommt hier ein &amp;quot;ok&amp;quot; ist die DB gesund. Ansonsten erscheint etwas wie&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
*** in database main ***&lt;br /&gt;
On tree page 118786 cell 1: Rowid 75 out of order (previous was 816660)&lt;br /&gt;
On tree page 118786 cell 4: Rowid 815704 out of order (previous was 816727)&lt;br /&gt;
Corruption detected in cell 0 on page 118786&lt;br /&gt;
Multiple uses for byte 132 of page 118786&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Datenbank-Dump erstellen (Export gesamten DB in die Datei &amp;quot;dump_all_20160516_1043.sql&amp;quot;) und DB verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .mode insert&lt;br /&gt;
sqlite&amp;gt; .output dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; .dump&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Neue Datenbank erstellen und den Dump einlesen, Integritäts-Check machen und verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem-neu.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .read dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; pragma integrity_check;&lt;br /&gt;
ok&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Spätestens jetzt FHEM stoppen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem stop&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Alte DB sichern und neue aktivieren:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo mv fhem.db fhem.db.sv_20160516&lt;br /&gt;
sudo mv fhem-neu.db fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Kontrollieren, dass die neue DB die gleichen Rechte wie die alte DB hat (und ggf. korrigieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 root root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
~/fhem$ sudo chown fhem:root fhem.db&lt;br /&gt;
&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 fhem root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
FHEM wieder starten (und natürlich kontrollieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem start&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. Diese geschieht mittels des Userreadings, welches man vorteilshafterweise mit im DbLog-device anlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;attr myDbLog userReadings DbFileSize:reduceLogState.* { (split(&#039; &#039;,`du -m fhem.db`))[0] }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels dieses Attributs wird die Grösse der .db-Datei immer nach dem Ausführen des ReduceLog in das Reading &amp;quot;DbFileSize&amp;quot; in ganzzahligen MByte abgelegt.&lt;br /&gt;
&lt;br /&gt;
Basierend auf diesem Reading können dann weitere Aktionen, beispielsweise ein Plot, erstellt werden.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist für SQLite verwendbar. Zur Ermittlung der DB-Größe andere DB-Typen (aber auch für SQLite nutzbar) kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Performance-Optimierung ==&lt;br /&gt;
Auch eine Datenbank kann mit der Zeit langsamer werden. Dies hängt von mehreren Faktoren ab:&lt;br /&gt;
* Menge der gelogten Daten (zB. &amp;gt; 4-5 GB)&lt;br /&gt;
* Eingesetzte Hardware (zB. langsame SD-Karte vs. schnelle SSD)&lt;br /&gt;
* Eingesetztes Datenbanksystem (zB. SQLite, MySQL)&lt;br /&gt;
* Komplexität der Abfragen (zB. für aufwändige Graphen oder Berechnungen)&lt;br /&gt;
&lt;br /&gt;
Diese Punkte sollen im folgenden diskutiert werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komplexität der Abfragen ===&lt;br /&gt;
Dies ist kein Problem der Datenbank, sondern rein der Abfrage. Dem entsprechend muss die Optimierung auch in der Abfrage oder im Skript gesucht werden. Dies ist nicht Ziel dieses Abschnittes und wird hier nicht weiter behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetztes Datenbanksystem ===&lt;br /&gt;
Welches Datenbanksystem eingesetzt wird (zB. SQLite oder MySQL) hat auf die Performance der Datenbank gar keinen so großen Einfluss, wie vielleicht zuerst gedacht. Selbst SQLite kann problemlos Datenbanken mit etlichen GB Größe performant verarbeiten. Der Flaschenhals ist hier viel mehr die darunter liegende Hardware (s.u.).&lt;br /&gt;
&lt;br /&gt;
Die Performance der Datenbank an sich, kann aber durch verschiedene Maßnahmen verbessert werden:&lt;br /&gt;
* Pflegemaßnahmen bzgl. der Daten&lt;br /&gt;
* &#039;&#039;&#039;Erstellung von Indizes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Menge der Daten und Pflegemaßnahmen bzgl. der Daten ====&lt;br /&gt;
Die Menge der geloggten Daten hat natürlich Einfluss auf die Geschwindigkeit von Abfragen - je mehr Daten vorhanden sind, desto mehr Daten müssen auch durchforstet werden um eine Abfrage zu bedienen. Die Reduzierung der geloggten Datenmenge hat also direkten Einfluss auf die Größe und damit auch die Geschwindigkeit der Datenbank. Die Menge der zu loggenden Daten lässt sich an zwei Stellen einschränken:&lt;br /&gt;
* bei der Definition jedes Devices (s. Kapitel oben)&lt;br /&gt;
* bei der Festlegung des fhem-weiten Log-Levels (s. [[Loglevel]])&lt;br /&gt;
&lt;br /&gt;
Die Menge der bereits geloggten Daten kann zB. mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] verringert und optimiert werden, bspw.&lt;br /&gt;
* löschen unnötiger Daten&lt;br /&gt;
* vacuum der Datenbank&lt;br /&gt;
&lt;br /&gt;
Insgesamt haben diese Maßnahmen aber nur einen eingeschränkten Effekt auf die Performance der DB. Deutlich effektiver ist die Erstellung eines Index (s.u.).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Erstellung von Indizes ====&lt;br /&gt;
Die Erstellung von Indizes hat mit Abstand den größten Einfluss auf die Performance einer Datenbank (auf unveränderter Hardware). Extrem zusammengefasst ist ein Index eine extrem optimiertes Nachschlageverzeichnis für einen bestimmten Typ Daten (ein Index wie im Buch halt). Eine wunderbare Einführung in Indizes bietet [[https://use-the-index-luke.com/de|https://use-the-index-luke.com]].&lt;br /&gt;
&lt;br /&gt;
In fhem sind Indizes sogar sehr einfach einzurichten da die Datenbank-Nutzung sehr stark vorgegeben ist. Nahezu jede Abfrage folgt dem Schema &#039;&#039;Device -&amp;gt; Reading -&amp;gt; Datum -&amp;gt; Wert&#039;&#039;. Ein Index kann genau diese Abfrage bedienen und beschleunigen. Ein Index nur über die Devices wäre ein erster Schritt, brächte aber noch keinen großen Gewinn (wie um Link oben gut beschrieben). Über die gesamten ersten drei Schritte erstellt (Device -&amp;gt; Reading -&amp;gt; Datum) bringt der Index aber sofort eine deutliche Geschwindigkeitssteigerung.&lt;br /&gt;
&lt;br /&gt;
Die Erstellung eines Index erfolgt direkt in der Datenbank (und nicht aus fhem heraus), hier am Beispiel einer SQLite-DB:&lt;br /&gt;
&lt;br /&gt;
Öffnen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; sudo sqlite3 fhem.db &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugen des Index auf der DB-Konsole (das Semikolon am Ende ist wichtig):&lt;br /&gt;
&amp;lt;pre&amp;gt; create index idx_device_reading_timestamp on history (device, reading, timestamp); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verlassen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; .exit &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzig zu berücksichtigen ist, dass dieser Index die Datenbank um bis zu 1/3 vergrößert. Er kann aber bei Bedarf auch wieder entfernt werden. Bei meiner 15 GB SQLite-Datei (auf einem Mac Mini mit SSD) hat dies ca. 15 min gedauert (den fhem hatte ich vorsichtshalber währenddessen deaktiviert).&lt;br /&gt;
&lt;br /&gt;
Sollte jemand spezielle Berechnungen oder Skripte ausführen, die nach einem anderen Abfrage-Schema arbeiten, könnte man dafür spezialisierte zusätzliche Indizes erstellen. Das sollte aber dann mit dem Wissen des obigen Links erarbeitet werden, da dann etwas mehr Hintergrundwissen sehr hilfreich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== DB-Backup ====&lt;br /&gt;
Ein anderer Aspekt, der eigentlich nichts mit der Performance der DB zu tun hat, ist der Einfluss aufs Backup. Wird bspw. ein Systembackup per RSYNC gemacht, muss bei SQLite immer die komplette ggf. riesige Datei gesynct werden - bei MySQL würden nur die veränderten DB-Elemente gesynct. Dies soll hier nicht weiter vertieft werden, sollte aber bei einer Gesamtstrategie bedacht werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetzte Hardware ===&lt;br /&gt;
fhem hat grundsätzlich sehr viele kleine Datenzugriffe, unabhängig davon ob FileLog oder DbLog eingesetzt wird. Deshalb ist der absolut größte Performance-Gewinn durch den Einsatz schneller Festplatten zu erreichen - ganz einfache Aussage: &#039;&#039;je schneller desto besser ;-)&#039;&#039;. Konkret bietet eine SSD mit mind. 250MB/s Datenzugriff eine ordentliche Basis für jedes datengestützte System, wie den fhem.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenmenge größer und die Abfragen komplexer werden, müssen natürlich irgendwann auch die Prozessorleistung und der Arbeitsspeicher mit wachsen. Aber auch an einem Raspi wird eine SSD deutlich performanter sein, als eine einfache SD-Karte oder eine klassische rotierende Festplatte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31041</id>
		<title>DbLog-MySQL</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31041"/>
		<updated>2019-07-29T06:32:27Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Einleitung */  Anpassung auf MySQL&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Da bei der Inbetriebnahme und der Migration von MySQL bzw MariaDB-Datenbanken einige kleine Fallen auftreten können, gibt&#039;s hier ein paar Hinweise.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
=== Datenbank-Anbindung mittels db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MySQL-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration als Device ===&lt;br /&gt;
Das DbLog Device wird dann definiert mit&lt;br /&gt;
:&amp;lt;code&amp;gt;define &amp;lt;name&amp;gt; DbLog &amp;lt;configfilename&amp;gt; &amp;lt;regexp&amp;gt; &amp;lt;/code&amp;gt;&lt;br /&gt;
wobei &#039;&#039;&amp;lt;configfilename&amp;gt;&#039;&#039; dem Pfad zur zuvor angelegten db.conf entspricht.&lt;br /&gt;
Ein Beispiel hierfür wäre:&lt;br /&gt;
:&amp;lt;code&amp;gt;define logdb DbLog ./db.conf .*:.* &amp;lt;/code&amp;gt;&lt;br /&gt;
Die Angabe von &amp;lt;code&amp;gt;.*:.*&amp;lt;/code&amp;gt; bedeutet, dass sämtliche DeviceMessages (Messwerte, Batteriestatus, KeepAlives, etc.) in die Datenbank geschrieben werden. Dies führt u.U. dazu, dass die Datenbank auch mit vielen teils irrelevanten Werten gefüllt wird. Man kann daher die zu loggenden Werte einschränken, indem man genau angibt welche Werte übertragen werden sollen. Dies ist in [[#Finetuning des Loggings]] beschrieben.&lt;br /&gt;
&lt;br /&gt;
Unbedingt beachten: bei Verwendung des Moduls configdb wird die Konfigurationsdatei aus der &#039;&#039;&#039;&#039;&#039;Datenbank&#039;&#039;&#039;&#039;&#039; gelesen. Deshalb ist es erforderlich, das File mittels &amp;lt;code&amp;gt;configdb fileimport db.conf &amp;lt;/code&amp;gt; vorher zu importieren !&lt;br /&gt;
&lt;br /&gt;
=== Finetuning des Loggings ===&lt;br /&gt;
Bei der Konfiguration des Log-Devices werden die zu loggenden Daten definiert - in der einfachsten Form sieht das so aus: &amp;lt;code&amp;gt;define logdb DbLog ./db.conf .*:.* &amp;lt;/code&amp;gt;. Die Angabe von &amp;lt;code&amp;gt;.*:.*&amp;lt;/code&amp;gt; bedeutet, dass sämtliche DeviceMessages (Messwerte, Batteriestatus, KeepAlives, etc.) in die Datenbank geschrieben werden. Dies führt u.U. dazu, dass die Datenbank auch mit sehr vielen und teils nicht benötigten Werten gefüllt wird und schnell wächst. Die Datenbank ist zwar deutlich leistungsfähiger, was große Datenmengen angeht, Datensparsamkeit kann aber schnell sinnvoll werden...&lt;br /&gt;
&lt;br /&gt;
Um das Log-Aufkommen einzugrenzen gibt es mehrere Ansätze:&lt;br /&gt;
* Einschränkung über den &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Eintrag&lt;br /&gt;
* Einschränkung über DbLogExclude-Einträge der jeweiligen Devices&lt;br /&gt;
* Einschränkung über DbLogInclude-Einträge des jeweiligen Devices&lt;br /&gt;
* Ausschluß von Device/Reading-Kombinationen über das Attribut &amp;quot;excludeDevs&amp;quot;. Es können [https://fhem.de/commandref_DE.html#devspec devspec] verwendet werden. &lt;br /&gt;
&lt;br /&gt;
==== Einschränkung über den zentralen &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Eintrag ====&lt;br /&gt;
Man kann die zu loggenden Werte einschränken, indem man genau angibt welche Werte übertragen werden sollen. Die erste Wildcard, also das erste &amp;lt;code&amp;gt;.*&amp;lt;/code&amp;gt;, entspricht dem in FHEM verwendeten Device-Namen. Die zweite Wildcard entspricht dem vom Device ausgegebenen zu loggenden Wert. Separiert werden beiden Angaben durch einen Doppelpunkt. &lt;br /&gt;
&lt;br /&gt;
Ein Beispiel, um zwar alle definierten Devices zu erfassen, aber nur die Werte Temperatur, Ventilposition und Luftfeuchte in die Datenbank zu schreiben wäre:&lt;br /&gt;
:&amp;lt;code&amp;gt;define myDbLog DbLog ./db.conf .*:(temperature|valveposition|humidity).* &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einschränkung über die jeweiligen Devices ====&lt;br /&gt;
Man kann die zu loggenden Werte für einzelne Devices separat einschränken, ohne dies im zentralen define-Eintrag machen zu müssen. Dies kann interessant sein, wenn beispielsweise ein Device Fehlerwerte meldet, die uninteressant sind, oder es meldet unnötig häufig Werte - beides ist z.B. bei 1-wire-Temperatursensoren gerne der Fall.&lt;br /&gt;
&lt;br /&gt;
Um das einzuschränken gibt es 2 Stellparameter, die als Attribute direkt zum jeweiligen Device konfiguriert werden:&lt;br /&gt;
* DbLogExclude - definiert Werte, die nicht geloggt werden sollen&lt;br /&gt;
* DbLogInclude - definiert Werte, die geloggt werden sollen ( siehe attr DbLogSelectionMode )&lt;br /&gt;
* event-min-interval, event-on-change-reading und event-on-update-reading beeinflussen, wie häufig Werte geloggt werden (vgl. {{Link2CmdRef|Lang=de|Anker=event-on-update-reading}})&lt;br /&gt;
&lt;br /&gt;
Eine konkrete Konfiguration für einen sehr gesprächigen 1-wire-Temperatursensor könnte wie folgt aussehen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define EG_Balkon GPIO4 BUSMASTER&lt;br /&gt;
attr EG_Balkon DbLogExclude failures,T,85     # logge keine &amp;quot;failures&amp;quot;, &amp;quot;T&amp;quot;-Werte und &amp;quot;85&amp;quot;-Werte (default-Werte, wenn keine Temperatur gelesen werden kann)&lt;br /&gt;
attr EG_Balkon event-on-change-reading state  # logge nur, wenn sich ein Wert ändert (wenn sich die Temperatur nicht ändert, logge das nicht)&lt;br /&gt;
attr EG_Balkon event-min-interval state:900   # logge spätestens alle 900sec = 15min&lt;br /&gt;
attr EG_Balkon event-on-update-reading .*     # logge alle Werte, die aktualisiert werden&lt;br /&gt;
&lt;br /&gt;
attr &amp;lt;1-Wire-Device vom Typ OWTHERM oder OWSWITCH&amp;gt; DbLogExclude data.*      # verhindert das Logging der state-Eintragungen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine in diesem {{Link2Forum|Topic=33697|Message=264127}} vorgestellte Strategie zur Vermeidung unnötigen Loggings ist, dass bei der Definition von Devices durch das nachfolgende &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; automatisch ein DbLogExclude für alle Werte (.*) des Devices zugewiesen wird und dies nur bei Interesse an geloggten Werten gelöscht bzw. angepasst wird:&lt;br /&gt;
&amp;lt;code&amp;gt;define nDbLogExclude notify global:DEFINED.* attr $EVTPART1 DbLogExclude .*&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebenso ist es mittlerweile möglich, lediglich erwünschte Werte (Positiv-Liste) zu loggen und alle anderen zu verwerfen. Hierfür wird im LogDevice das attribut DbLogSelectionMode Include verwendet. Nun kann für jedes Device mit DbLogInclude &amp;lt;Reading1&amp;gt;,&amp;lt;Reading2&amp;gt;,... angegeben werden, welche Readings geloggt werden sollen. &lt;br /&gt;
Integriert ist ebenfalls ein &amp;quot;min-interval&amp;quot;, siehe {{Link2CmdRef}}.&lt;br /&gt;
&lt;br /&gt;
== Datenbank ==&lt;br /&gt;
Unterstützte Datenbanksysteme (Auswahl):&lt;br /&gt;
* Sqlite&lt;br /&gt;
* MySQL&lt;br /&gt;
* PostGreSql&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen zur Anlage von Tabellen und Indizes sind für jeden unterstützten Datenbanktyp im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation, oder hier zu finden: [https://svn.fhem.de/trac/browser/trunk/fhem/contrib/dblog/ Link]. Das MySQL-Skript (db_create_mysql.sql) legt eine neue Datenbank, das PostGres-Skript (db_create_postgresql.sql) ein neues Schema mit Namen &amp;quot;fhem&amp;quot; an.&lt;br /&gt;
&lt;br /&gt;
==== current ====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== history ====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
{{Todo|Ausbauen}}&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
== Anpassen der gplot-Konfigurationen ==&lt;br /&gt;
Die meisten gplot-Konfigurationen sind bisher lediglich auf FileLog-Konfigurationen ausgelegt. Deshalb müssen sie für die Verwendung mit DbLog angepasst werden. Glücklicherweise beschränkt sich dies auf die reinen FileLog-Zeilen - es müssen die DbLog-Äquivalente hinzugefügt werden. Die FileLog-Einträge müssen zwar nicht gelöscht werden, wenn man aber FileLog und DbLog parallel betreibt, sollte man getrennte gplot-Dateien für beide Logging-Typen haben um Auswertungsprobleme erkennen zu können.&lt;br /&gt;
&lt;br /&gt;
Für die fht.gplot Konfiguration sähe die Anpassung wie folgt aus (lediglich die vier DbLog-Zeilen wurden hinzugefügt):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2014-12-25 21:53: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 nomirror&lt;br /&gt;
set y2tics &lt;br /&gt;
set grid y2tics&lt;br /&gt;
set ylabel &amp;quot;Actuator/Window (%)&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Temperature in C&amp;quot;&lt;br /&gt;
set yrange 0:100&lt;br /&gt;
set y2range 5:25&lt;br /&gt;
&lt;br /&gt;
#FileLog 4:.measured-temp\x3a:0:&lt;br /&gt;
#FileLog 4:.actuator\x3a:0:int&lt;br /&gt;
#FileLog 4:.desired-temp::&lt;br /&gt;
#FileLog 4:.window\x3a::&lt;br /&gt;
&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.measured-temp:0:&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.actuator:0:int&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.desired-temp::&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.window::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Measured temperature&#039; ls l0 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Actuator (%)&#039; ls l1 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Desired Temperature&#039; ls l2 lw 1 with steps,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Window&#039; ls l3 lw 1 with steps&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Des Weiteren ist zu beachten: &lt;br /&gt;
&lt;br /&gt;
On-Off-Plots&lt;br /&gt;
&lt;br /&gt;
 EG_Bad:window:::$val=~s/(on|off)(\d*).*/$1eq&amp;quot;on&amp;quot;?1:0/eg&lt;br /&gt;
&lt;br /&gt;
unter Berücksichtigung von dim-Werten:&lt;br /&gt;
&lt;br /&gt;
 EG_WoZi_Licht:value:::$val=~s/(on|off)(\d*).*/$1eq&amp;quot;on&amp;quot;?1:($1eq&amp;quot;dim&amp;quot;?$2*0.01:0)/eg&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer SQLite-Datenbank ==&lt;br /&gt;
Im folgenden wird eine lokale SQLite-Datenbank auf einen Ubuntu-System angelegt (nach Quelle: [http://www.tatsch-it.de/fhem-dblog/ http://www.tatsch-it.de/fhem-dblog/])&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Installation von SQLite:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo aptitude install sqlite3 libdbi-perl libdbd-sqlite3-perl&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anlegen der SQLite-Datenbank fhem.db&#039;&#039; (öffnet auch direkt eine SQL-Kommandozeile):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 /opt/fhem/fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der geöffneten SQL-Kommandozeile eingeben:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE &#039;history&#039; (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
CREATE TABLE &#039;current&#039; (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
CREATE INDEX Search_Idx ON `history` (DEVICE, READING, TIMESTAMP);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Kommandozeile verlässt man mit &amp;lt;code&amp;gt;.exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anpassen des Besitzers und der Rechte der Datenbank-Datei:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo chown fhem /opt/fhem/fhem.db&lt;br /&gt;
sudo chmod 666 /opt/fhem/fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Datenbank-Anbindung des FHEM konfigurieren:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo nano /opt/fhem/db.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
Inhalt:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%dbconfig= (&lt;br /&gt;
  connection =&amp;gt; &amp;quot;SQLite:dbname=/opt/fhem/fhem.db&amp;quot;,&lt;br /&gt;
  user =&amp;gt; &amp;quot;&amp;quot;,&lt;br /&gt;
  password =&amp;gt; &amp;quot;&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Logging des FHEM auf die Datenbank konfigurieren:&#039;&#039; (hier sind nur die Anpassungen aufgeführt)&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo nano /opt/fhem/fhem.cfg&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
attr global userattr DbLogExclude ...  # erlaubt es einzelne Einträge nicht zu loggen&lt;br /&gt;
...&lt;br /&gt;
define logdb DbLog ./db.conf .*:.*     # logt alle(!) auflaufenden Events aller Konfigurationen&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Da durch diese &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Definition alle auflaufenden Events gelogt werden, müssen keine weiteren Anpassungen in der Konfiguration gemacht werden. Die FileLog-Einträge können bedenkenlos bestehen bleiben - dann wird in Datenbank und FileLog gelogt und man verliert keine Daten, falls etwas nicht klappt. Wenn alles wie geplant läuft, können die FileLog-Definitionen gelöscht werden (ebenso die Log-Dateien). Ebenso können die zu loggenden Daten später eingegrenzt werden (s. [[#Finetuning des Loggings]]).&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;FHEM neu starten:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo service fhem stop&lt;br /&gt;
sudo service fhem start&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Kontrollieren, ob Logs in die Datenbank geschrieben werden:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 /opt/fhem/fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der geöffneten SQL-Kommandozeile eingeben:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
select * from history order by TIMESTAMP;       # dies gibt alle(!) Logs chronologisch aus (kann nach längerem Betrieb recht lange dauern)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Kommandozeile verlässt man mit &amp;lt;code&amp;gt;.exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anpassung der glot-Dateien:&#039;&#039; siehe [[#Anpassen der gplot-Konfigurationen]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer Mysql-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 apt-get update &amp;amp;&amp;amp; apt-get install mysql-server mysql-client libdbd-mysql libdbd-mysql-perl&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Installation sollte man aus Sicherheitsgründen ein Passwort für den mysql-root vergeben, wenn man nicht sogar ganz den Login verbietet.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der normale Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Zum Test mal mit mysql verbinden:&lt;br /&gt;
 # mysql -p -u root&lt;br /&gt;
 Enter password:&lt;br /&gt;
 mysql&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen (root Passwort wird abgefragt): &lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; show databases;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Integration von DBLog in eigene Module ==&lt;br /&gt;
=== Bereitstellung der UNITS ===&lt;br /&gt;
Mit der DbLog_splitFn kann der Modulautor selbst festlegen, wie die Events des Moduls in die Bestandteile Reading/Value/Unit zerlegt werden um ein korrektes Logging per DbLog zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
Dazu muss der Modulautor in der [[DevelopmentModuleIntro#X_Initialize|Initialize-Funktion]] eine &amp;lt;code&amp;gt;DbLog_splitFn&amp;lt;/code&amp;gt; bereitstellen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	...&lt;br /&gt;
	$hash-&amp;gt;{DbLog_splitFn}      = &amp;quot;X_DbLog_splitFn&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die genaue Aufrufsyntax und Funktionweise einer DbLog_split-Funktion findet man [[DevelopmentModuleIntro#X_DbLog_split|hier]].&lt;br /&gt;
&lt;br /&gt;
== Werte auslesen ==&lt;br /&gt;
Manchmal möchte man Daten aus den Logs abrufen ohne händisch in der Datenbank herumzuwühlen (s.u.). Dies ist insb. auch dann hilfreich, wenn man eigenen Funktionen, Notifys oder spezielle Plots entwirft, bei denen man auf Logdaten zugreifen möchte.&lt;br /&gt;
&lt;br /&gt;
Grundsätzlich beschrieben ist dies in der {{Link2CmdRef|Lang=de|Anker=DbLog}} und unterscheidet sich minimal (aber entscheidend) von der Struktur bei [[FileLog#Werte_auslesen|FileLogs]].&lt;br /&gt;
&lt;br /&gt;
Hier ein paar Beispiele, was man damit anstellen kann:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01 2016-10-03 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor vom 01.10.-03.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor von 8-16 Uhr am 01.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor:temperature&amp;lt;/code&amp;gt; nur die temperature Werte&lt;br /&gt;
* &amp;lt;code&amp;gt;{ ReadingsTimestamp(&amp;quot;meinSensor&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;0&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des aktuellen state des meinSensor&lt;br /&gt;
* &amp;lt;code&amp;gt;{ OldTimestamp(&amp;quot;meinSensor&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des letzten state des FHT_3a32&lt;br /&gt;
* &amp;lt;code&amp;gt;{ time_str2num(OldTimestamp(&amp;quot;meinSensor&amp;quot;)) }&amp;lt;/code&amp;gt; Timestamp in Sekunden des letzten state des meinSensor&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
&#039;&#039;&#039;Öffnen der DB unter Linux:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(Es werden Schreibrechte benötigt,ohne kann man die DB zwar öffnen, aber nichts machen)&lt;br /&gt;
 sudo sqlite3 fhem.db&lt;br /&gt;
Dadurch öffnet sich ein SQL-Konsole, auf der alle weiteren Befehle ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schliessen der DB:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .exit&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfe anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .help&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Tabellen anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .tables&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Das Schema der DB anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(vgl. oben [[DbLog#Datenbanken]] und [[DbLog#Beispiel: Anlegen und Nutzung einer SQLite-Datenbank]])&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .schema&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Eintäge anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Die Einträge liegen alle in der Tabelle &amp;quot;History&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ganz wichtig&#039;&#039;&#039; ist immer das &amp;quot;;&amp;quot; am Ende Zeile (bei allen Kommandos, die nicht mit einem &amp;quot;.&amp;quot; anfangen). Wenn es vergessen wurde zeigt die Konsole solange neue Zeilen bis ein &amp;quot;;&amp;quot; eingegeben wird. So kann ein Befehl auch bequem über mehrere Zeilen geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY;&lt;br /&gt;
&lt;br /&gt;
Dies kann sehr lange dauern und kann ggf. mit &amp;lt;code&amp;gt;STRG-C&amp;lt;/code&amp;gt; abgebrochen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;where&amp;lt;/code&amp;gt;-Statements werden Strings in einfache Anführungsstriche gesetzt, Zahlen nicht.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LÖSCHEN aller Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Löschen kann nicht rückgängig gemacht werden!! Also IMMER erst die entsprechenden SELECT-Statements solange verfeinern bis wirklich nur die gewünschten Einträge angezeigt werden. Dann das &amp;lt;code&amp;gt;select *&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; ersetzen.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; delete from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank reparieren ==&lt;br /&gt;
Es kann immer wieder mal vorkommen, dass Datenbanken Fehler enthalten. Das muss im Alltag garnicht auffallen und auch nicht immer schlimm enden. Wenn man auf der SQL-Konsole aber bspw. eine Meldung &amp;lt;code&amp;gt;Error: database disk image is malformed&amp;lt;/code&amp;gt; erhält, sollte man ein Reparatur vornehmen.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
Die folgenden Schritte beschreiben, wie man eine SQLite-DB reparieren kann (Quelle: [http://techblog.dorogin.com/2011/05/sqliteexception-database-disk-image-is.html]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
DB öffnen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Integritäts-Check durchführen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sqlite&amp;gt; pragma integrity_check;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kommt hier ein &amp;quot;ok&amp;quot; ist die DB gesund. Ansonsten erscheint etwas wie&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
*** in database main ***&lt;br /&gt;
On tree page 118786 cell 1: Rowid 75 out of order (previous was 816660)&lt;br /&gt;
On tree page 118786 cell 4: Rowid 815704 out of order (previous was 816727)&lt;br /&gt;
Corruption detected in cell 0 on page 118786&lt;br /&gt;
Multiple uses for byte 132 of page 118786&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Datenbank-Dump erstellen (Export gesamten DB in die Datei &amp;quot;dump_all_20160516_1043.sql&amp;quot;) und DB verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .mode insert&lt;br /&gt;
sqlite&amp;gt; .output dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; .dump&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Neue Datenbank erstellen und den Dump einlesen, Integritäts-Check machen und verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem-neu.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .read dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; pragma integrity_check;&lt;br /&gt;
ok&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Spätestens jetzt FHEM stoppen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem stop&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Alte DB sichern und neue aktivieren:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo mv fhem.db fhem.db.sv_20160516&lt;br /&gt;
sudo mv fhem-neu.db fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Kontrollieren, dass die neue DB die gleichen Rechte wie die alte DB hat (und ggf. korrigieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 root root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
~/fhem$ sudo chown fhem:root fhem.db&lt;br /&gt;
&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 fhem root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
FHEM wieder starten (und natürlich kontrollieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem start&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. Diese geschieht mittels des Userreadings, welches man vorteilshafterweise mit im DbLog-device anlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;attr myDbLog userReadings DbFileSize:reduceLogState.* { (split(&#039; &#039;,`du -m fhem.db`))[0] }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels dieses Attributs wird die Grösse der .db-Datei immer nach dem Ausführen des ReduceLog in das Reading &amp;quot;DbFileSize&amp;quot; in ganzzahligen MByte abgelegt.&lt;br /&gt;
&lt;br /&gt;
Basierend auf diesem Reading können dann weitere Aktionen, beispielsweise ein Plot, erstellt werden.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist für SQLite verwendbar. Zur Ermittlung der DB-Größe andere DB-Typen (aber auch für SQLite nutzbar) kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Performance-Optimierung ==&lt;br /&gt;
Auch eine Datenbank kann mit der Zeit langsamer werden. Dies hängt von mehreren Faktoren ab:&lt;br /&gt;
* Menge der gelogten Daten (zB. &amp;gt; 4-5 GB)&lt;br /&gt;
* Eingesetzte Hardware (zB. langsame SD-Karte vs. schnelle SSD)&lt;br /&gt;
* Eingesetztes Datenbanksystem (zB. SQLite, MySQL)&lt;br /&gt;
* Komplexität der Abfragen (zB. für aufwändige Graphen oder Berechnungen)&lt;br /&gt;
&lt;br /&gt;
Diese Punkte sollen im folgenden diskutiert werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komplexität der Abfragen ===&lt;br /&gt;
Dies ist kein Problem der Datenbank, sondern rein der Abfrage. Dem entsprechend muss die Optimierung auch in der Abfrage oder im Skript gesucht werden. Dies ist nicht Ziel dieses Abschnittes und wird hier nicht weiter behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetztes Datenbanksystem ===&lt;br /&gt;
Welches Datenbanksystem eingesetzt wird (zB. SQLite oder MySQL) hat auf die Performance der Datenbank gar keinen so großen Einfluss, wie vielleicht zuerst gedacht. Selbst SQLite kann problemlos Datenbanken mit etlichen GB Größe performant verarbeiten. Der Flaschenhals ist hier viel mehr die darunter liegende Hardware (s.u.).&lt;br /&gt;
&lt;br /&gt;
Die Performance der Datenbank an sich, kann aber durch verschiedene Maßnahmen verbessert werden:&lt;br /&gt;
* Pflegemaßnahmen bzgl. der Daten&lt;br /&gt;
* &#039;&#039;&#039;Erstellung von Indizes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Menge der Daten und Pflegemaßnahmen bzgl. der Daten ====&lt;br /&gt;
Die Menge der geloggten Daten hat natürlich Einfluss auf die Geschwindigkeit von Abfragen - je mehr Daten vorhanden sind, desto mehr Daten müssen auch durchforstet werden um eine Abfrage zu bedienen. Die Reduzierung der geloggten Datenmenge hat also direkten Einfluss auf die Größe und damit auch die Geschwindigkeit der Datenbank. Die Menge der zu loggenden Daten lässt sich an zwei Stellen einschränken:&lt;br /&gt;
* bei der Definition jedes Devices (s. Kapitel oben)&lt;br /&gt;
* bei der Festlegung des fhem-weiten Log-Levels (s. [[Loglevel]])&lt;br /&gt;
&lt;br /&gt;
Die Menge der bereits geloggten Daten kann zB. mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] verringert und optimiert werden, bspw.&lt;br /&gt;
* löschen unnötiger Daten&lt;br /&gt;
* vacuum der Datenbank&lt;br /&gt;
&lt;br /&gt;
Insgesamt haben diese Maßnahmen aber nur einen eingeschränkten Effekt auf die Performance der DB. Deutlich effektiver ist die Erstellung eines Index (s.u.).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Erstellung von Indizes ====&lt;br /&gt;
Die Erstellung von Indizes hat mit Abstand den größten Einfluss auf die Performance einer Datenbank (auf unveränderter Hardware). Extrem zusammengefasst ist ein Index eine extrem optimiertes Nachschlageverzeichnis für einen bestimmten Typ Daten (ein Index wie im Buch halt). Eine wunderbare Einführung in Indizes bietet [[https://use-the-index-luke.com/de|https://use-the-index-luke.com]].&lt;br /&gt;
&lt;br /&gt;
In fhem sind Indizes sogar sehr einfach einzurichten da die Datenbank-Nutzung sehr stark vorgegeben ist. Nahezu jede Abfrage folgt dem Schema &#039;&#039;Device -&amp;gt; Reading -&amp;gt; Datum -&amp;gt; Wert&#039;&#039;. Ein Index kann genau diese Abfrage bedienen und beschleunigen. Ein Index nur über die Devices wäre ein erster Schritt, brächte aber noch keinen großen Gewinn (wie um Link oben gut beschrieben). Über die gesamten ersten drei Schritte erstellt (Device -&amp;gt; Reading -&amp;gt; Datum) bringt der Index aber sofort eine deutliche Geschwindigkeitssteigerung.&lt;br /&gt;
&lt;br /&gt;
Die Erstellung eines Index erfolgt direkt in der Datenbank (und nicht aus fhem heraus), hier am Beispiel einer SQLite-DB:&lt;br /&gt;
&lt;br /&gt;
Öffnen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; sudo sqlite3 fhem.db &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugen des Index auf der DB-Konsole (das Semikolon am Ende ist wichtig):&lt;br /&gt;
&amp;lt;pre&amp;gt; create index idx_device_reading_timestamp on history (device, reading, timestamp); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verlassen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; .exit &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzig zu berücksichtigen ist, dass dieser Index die Datenbank um bis zu 1/3 vergrößert. Er kann aber bei Bedarf auch wieder entfernt werden. Bei meiner 15 GB SQLite-Datei (auf einem Mac Mini mit SSD) hat dies ca. 15 min gedauert (den fhem hatte ich vorsichtshalber währenddessen deaktiviert).&lt;br /&gt;
&lt;br /&gt;
Sollte jemand spezielle Berechnungen oder Skripte ausführen, die nach einem anderen Abfrage-Schema arbeiten, könnte man dafür spezialisierte zusätzliche Indizes erstellen. Das sollte aber dann mit dem Wissen des obigen Links erarbeitet werden, da dann etwas mehr Hintergrundwissen sehr hilfreich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== DB-Backup ====&lt;br /&gt;
Ein anderer Aspekt, der eigentlich nichts mit der Performance der DB zu tun hat, ist der Einfluss aufs Backup. Wird bspw. ein Systembackup per RSYNC gemacht, muss bei SQLite immer die komplette ggf. riesige Datei gesynct werden - bei MySQL würden nur die veränderten DB-Elemente gesynct. Dies soll hier nicht weiter vertieft werden, sollte aber bei einer Gesamtstrategie bedacht werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetzte Hardware ===&lt;br /&gt;
fhem hat grundsätzlich sehr viele kleine Datenzugriffe, unabhängig davon ob FileLog oder DbLog eingesetzt wird. Deshalb ist der absolut größte Performance-Gewinn durch den Einsatz schneller Festplatten zu erreichen - ganz einfache Aussage: &#039;&#039;je schneller desto besser ;-)&#039;&#039;. Konkret bietet eine SSD mit mind. 250MB/s Datenzugriff eine ordentliche Basis für jedes datengestützte System, wie den fhem.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenmenge größer und die Abfragen komplexer werden, müssen natürlich irgendwann auch die Prozessorleistung und der Arbeitsspeicher mit wachsen. Aber auch an einem Raspi wird eine SSD deutlich performanter sein, als eine einfache SD-Karte oder eine klassische rotierende Festplatte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31040</id>
		<title>DbLog-MySQL</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DbLog-MySQL&amp;diff=31040"/>
		<updated>2019-07-29T06:26:08Z</updated>

		<summary type="html">&lt;p&gt;Stephan: Kopie der DbLog-Seite&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Protokolliert Ereignisse in einer Datenbank&lt;br /&gt;
|ModType=h&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=93_DbLog.pm&lt;br /&gt;
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}}/[[Benutzer Diskussion:Tobias.faust|Wiki]])&amp;lt;br /&amp;gt;DS_Starter ({{Link2FU|16933|Forum}}/[[Benutzer Diskussion:DS_Starter|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
Mit der Zeit entstehen in FHEM recht umfangreiche Log-Daten für die verschiedensten konfigurierten Devices. Die übliche Einstiegs-[[Konfiguration]] sieht vor, dass die Logs als {{Link2CmdRef|Lang=de|Anker=FileLog|Label=FileLog}} gespeichert werden - je nach Einstellung in wenigen sehr großen oder vielen kleineren Dateien. Der Datei-basierte Zugriff ist allerdings nicht wirklick performant und kann schnell zum Flaschenhals werden (z.B. bei der Darstellung von Graphen über einen längeren Zeitraum).&lt;br /&gt;
&lt;br /&gt;
Alternativ kann FHEM die Log-Daten mittels {{Link2CmdRef|Lang=de|Anker=DbLog|Label=DbLog}} in einer Datenbank speichern. Diese kann lokal als einfache SQLite- oder als zentrale Server-Datenbank (s.u.) gestaltet sein. Schon eine lokale einfache SQLite-Datenbank ist in der Regel deutlich performanter als File-basierte Logs.&lt;br /&gt;
&lt;br /&gt;
Damit eine Datenbank-Nutzung möglich ist, müssen folgende Anpassungen gemacht werden:&lt;br /&gt;
# [[#Datenbank|Erstellen einer entsprechenden Datenbank]]&lt;br /&gt;
# [[#Datenbank-Anbindung mittels db.conf|Konfiguration der Datenbank-Anbindung in FHEM]]&lt;br /&gt;
# [[#Konfiguration als Device in fhem.cfg|Anpassen aller (oder einzelner) Konfigurationen von FileLog nach DbLog]]&lt;br /&gt;
# [[#Anpassen der gplot-Konfigurationen|Ggf. Anpassen der gplot-Konfigurationen]]&lt;br /&gt;
&lt;br /&gt;
;Hinweis:&lt;br /&gt;
Reporting und Management von DbLog-Datenbankinhalten kann mit dem Modul [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] stattfinden.&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
=== Datenbank-Anbindung mittels db.conf ===&lt;br /&gt;
DbLog wird durch 2 verschiedene Einträge aktiviert/definiert. In einer Datei namens &#039;&#039;&#039;db.conf&#039;&#039;&#039; werden die Parameter für eine Verbindung zur Datenbank (host, username, password, etc.) hinterlegt. Diese Datei kann in einem beliebigen Verzeichnis angelegt werden. Für eine MySQL-Datenbank sieht die db.conf folgendermaßen aus:&lt;br /&gt;
&lt;br /&gt;
 %dbconfig= (&lt;br /&gt;
     connection =&amp;gt; &amp;quot;mysql:database=fhem;host=db;port=3306&amp;quot;,&lt;br /&gt;
     user =&amp;gt; &amp;quot;fhemuser&amp;quot;,&lt;br /&gt;
     password =&amp;gt; &amp;quot;fhempassword&amp;quot;,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
Im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation befindet sich eine Beispielkonfiguration mit der Syntax für jeden unterstützen Datenbanktyp.&lt;br /&gt;
Es wird empfohlen diese Datei zu kopieren und erst dann entsprechend zu bearbeiten. Am Besten kopiert man diese Datei in das FHEM Home Directory /opt/fhem/ und achtet auf die entsprechenden Rechte!&lt;br /&gt;
 chown fhem:dialout /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
=== Konfiguration als Device ===&lt;br /&gt;
Das DbLog Device wird dann definiert mit&lt;br /&gt;
:&amp;lt;code&amp;gt;define &amp;lt;name&amp;gt; DbLog &amp;lt;configfilename&amp;gt; &amp;lt;regexp&amp;gt; &amp;lt;/code&amp;gt;&lt;br /&gt;
wobei &#039;&#039;&amp;lt;configfilename&amp;gt;&#039;&#039; dem Pfad zur zuvor angelegten db.conf entspricht.&lt;br /&gt;
Ein Beispiel hierfür wäre:&lt;br /&gt;
:&amp;lt;code&amp;gt;define logdb DbLog ./db.conf .*:.* &amp;lt;/code&amp;gt;&lt;br /&gt;
Die Angabe von &amp;lt;code&amp;gt;.*:.*&amp;lt;/code&amp;gt; bedeutet, dass sämtliche DeviceMessages (Messwerte, Batteriestatus, KeepAlives, etc.) in die Datenbank geschrieben werden. Dies führt u.U. dazu, dass die Datenbank auch mit vielen teils irrelevanten Werten gefüllt wird. Man kann daher die zu loggenden Werte einschränken, indem man genau angibt welche Werte übertragen werden sollen. Dies ist in [[#Finetuning des Loggings]] beschrieben.&lt;br /&gt;
&lt;br /&gt;
Unbedingt beachten: bei Verwendung des Moduls configdb wird die Konfigurationsdatei aus der &#039;&#039;&#039;&#039;&#039;Datenbank&#039;&#039;&#039;&#039;&#039; gelesen. Deshalb ist es erforderlich, das File mittels &amp;lt;code&amp;gt;configdb fileimport db.conf &amp;lt;/code&amp;gt; vorher zu importieren !&lt;br /&gt;
&lt;br /&gt;
=== Finetuning des Loggings ===&lt;br /&gt;
Bei der Konfiguration des Log-Devices werden die zu loggenden Daten definiert - in der einfachsten Form sieht das so aus: &amp;lt;code&amp;gt;define logdb DbLog ./db.conf .*:.* &amp;lt;/code&amp;gt;. Die Angabe von &amp;lt;code&amp;gt;.*:.*&amp;lt;/code&amp;gt; bedeutet, dass sämtliche DeviceMessages (Messwerte, Batteriestatus, KeepAlives, etc.) in die Datenbank geschrieben werden. Dies führt u.U. dazu, dass die Datenbank auch mit sehr vielen und teils nicht benötigten Werten gefüllt wird und schnell wächst. Die Datenbank ist zwar deutlich leistungsfähiger, was große Datenmengen angeht, Datensparsamkeit kann aber schnell sinnvoll werden...&lt;br /&gt;
&lt;br /&gt;
Um das Log-Aufkommen einzugrenzen gibt es mehrere Ansätze:&lt;br /&gt;
* Einschränkung über den &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Eintrag&lt;br /&gt;
* Einschränkung über DbLogExclude-Einträge der jeweiligen Devices&lt;br /&gt;
* Einschränkung über DbLogInclude-Einträge des jeweiligen Devices&lt;br /&gt;
* Ausschluß von Device/Reading-Kombinationen über das Attribut &amp;quot;excludeDevs&amp;quot;. Es können [https://fhem.de/commandref_DE.html#devspec devspec] verwendet werden. &lt;br /&gt;
&lt;br /&gt;
==== Einschränkung über den zentralen &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Eintrag ====&lt;br /&gt;
Man kann die zu loggenden Werte einschränken, indem man genau angibt welche Werte übertragen werden sollen. Die erste Wildcard, also das erste &amp;lt;code&amp;gt;.*&amp;lt;/code&amp;gt;, entspricht dem in FHEM verwendeten Device-Namen. Die zweite Wildcard entspricht dem vom Device ausgegebenen zu loggenden Wert. Separiert werden beiden Angaben durch einen Doppelpunkt. &lt;br /&gt;
&lt;br /&gt;
Ein Beispiel, um zwar alle definierten Devices zu erfassen, aber nur die Werte Temperatur, Ventilposition und Luftfeuchte in die Datenbank zu schreiben wäre:&lt;br /&gt;
:&amp;lt;code&amp;gt;define myDbLog DbLog ./db.conf .*:(temperature|valveposition|humidity).* &amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Einschränkung über die jeweiligen Devices ====&lt;br /&gt;
Man kann die zu loggenden Werte für einzelne Devices separat einschränken, ohne dies im zentralen define-Eintrag machen zu müssen. Dies kann interessant sein, wenn beispielsweise ein Device Fehlerwerte meldet, die uninteressant sind, oder es meldet unnötig häufig Werte - beides ist z.B. bei 1-wire-Temperatursensoren gerne der Fall.&lt;br /&gt;
&lt;br /&gt;
Um das einzuschränken gibt es 2 Stellparameter, die als Attribute direkt zum jeweiligen Device konfiguriert werden:&lt;br /&gt;
* DbLogExclude - definiert Werte, die nicht geloggt werden sollen&lt;br /&gt;
* DbLogInclude - definiert Werte, die geloggt werden sollen ( siehe attr DbLogSelectionMode )&lt;br /&gt;
* event-min-interval, event-on-change-reading und event-on-update-reading beeinflussen, wie häufig Werte geloggt werden (vgl. {{Link2CmdRef|Lang=de|Anker=event-on-update-reading}})&lt;br /&gt;
&lt;br /&gt;
Eine konkrete Konfiguration für einen sehr gesprächigen 1-wire-Temperatursensor könnte wie folgt aussehen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define EG_Balkon GPIO4 BUSMASTER&lt;br /&gt;
attr EG_Balkon DbLogExclude failures,T,85     # logge keine &amp;quot;failures&amp;quot;, &amp;quot;T&amp;quot;-Werte und &amp;quot;85&amp;quot;-Werte (default-Werte, wenn keine Temperatur gelesen werden kann)&lt;br /&gt;
attr EG_Balkon event-on-change-reading state  # logge nur, wenn sich ein Wert ändert (wenn sich die Temperatur nicht ändert, logge das nicht)&lt;br /&gt;
attr EG_Balkon event-min-interval state:900   # logge spätestens alle 900sec = 15min&lt;br /&gt;
attr EG_Balkon event-on-update-reading .*     # logge alle Werte, die aktualisiert werden&lt;br /&gt;
&lt;br /&gt;
attr &amp;lt;1-Wire-Device vom Typ OWTHERM oder OWSWITCH&amp;gt; DbLogExclude data.*      # verhindert das Logging der state-Eintragungen&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine in diesem {{Link2Forum|Topic=33697|Message=264127}} vorgestellte Strategie zur Vermeidung unnötigen Loggings ist, dass bei der Definition von Devices durch das nachfolgende &amp;lt;code&amp;gt;notify&amp;lt;/code&amp;gt; automatisch ein DbLogExclude für alle Werte (.*) des Devices zugewiesen wird und dies nur bei Interesse an geloggten Werten gelöscht bzw. angepasst wird:&lt;br /&gt;
&amp;lt;code&amp;gt;define nDbLogExclude notify global:DEFINED.* attr $EVTPART1 DbLogExclude .*&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ebenso ist es mittlerweile möglich, lediglich erwünschte Werte (Positiv-Liste) zu loggen und alle anderen zu verwerfen. Hierfür wird im LogDevice das attribut DbLogSelectionMode Include verwendet. Nun kann für jedes Device mit DbLogInclude &amp;lt;Reading1&amp;gt;,&amp;lt;Reading2&amp;gt;,... angegeben werden, welche Readings geloggt werden sollen. &lt;br /&gt;
Integriert ist ebenfalls ein &amp;quot;min-interval&amp;quot;, siehe {{Link2CmdRef}}.&lt;br /&gt;
&lt;br /&gt;
== Datenbank ==&lt;br /&gt;
Unterstützte Datenbanksysteme (Auswahl):&lt;br /&gt;
* Sqlite&lt;br /&gt;
* MySQL&lt;br /&gt;
* PostGreSql&lt;br /&gt;
&lt;br /&gt;
=== Tabellen ===&lt;br /&gt;
Die Datenbank ist relativ simpel gestaltet und besteht lediglich aus den folgenden beiden Tabellen:&lt;br /&gt;
* current&lt;br /&gt;
* history&lt;br /&gt;
&lt;br /&gt;
DbLog ist auf eine feste Tabellenstruktur angewiesen. Man muss daher in seiner Datenbank eine Tabelle mit folgenden Spalten anlegen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Spalte&lt;br /&gt;
! Beschreibung (en)&lt;br /&gt;
! Beschreibung (de)&lt;br /&gt;
! Beispiel&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TIMESTAMP&#039;&#039;&#039;&lt;br /&gt;
| timestamp of event&lt;br /&gt;
| Zeitstempel&lt;br /&gt;
| 2007-12-30 21:45:22 &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;DEVICE&#039;&#039;&#039;&lt;br /&gt;
| device name&lt;br /&gt;
| Device-Name&lt;br /&gt;
| Wetterstation&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;TYPE&#039;&#039;&#039;&lt;br /&gt;
| device type&lt;br /&gt;
| Device-Typ&lt;br /&gt;
| KS300&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;EVENT&#039;&#039;&#039;&lt;br /&gt;
| event specification as full string&lt;br /&gt;
| Eventspezifikation als Text&lt;br /&gt;
| humidity: 71 (%)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;READING&#039;&#039;&#039;&lt;br /&gt;
| name of reading extracted from event&lt;br /&gt;
| Bezeichnung des Readings&lt;br /&gt;
| humidity&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;VALUE&#039;&#039;&#039;&lt;br /&gt;
| actual reading extracted from event&lt;br /&gt;
| Wert des Readings&lt;br /&gt;
| 71&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;UNIT&#039;&#039;&#039;&lt;br /&gt;
| unit extracted from event&lt;br /&gt;
| Einheit des Readings&lt;br /&gt;
| %&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Vorlagen zur Anlage von Tabellen und Indizes sind für jeden unterstützten Datenbanktyp im Verzeichnis &#039;&#039;&#039;contrib/dblog&#039;&#039;&#039; der FHEM-Installation, oder hier zu finden: [https://svn.fhem.de/trac/browser/trunk/fhem/contrib/dblog/ Link]. Das MySQL-Skript (db_create_mysql.sql) legt eine neue Datenbank, das PostGres-Skript (db_create_postgresql.sql) ein neues Schema mit Namen &amp;quot;fhem&amp;quot; an.&lt;br /&gt;
&lt;br /&gt;
==== current ====&lt;br /&gt;
Die Tabelle current enthält für jedes zu loggende Device lediglich den letzten Wert. Falls noch kein Wert geloggt wurde, ist diese Tabelle leer. &lt;br /&gt;
Falls der Inhalt gelöscht wird, bauen sich die Daten automatisch wieder auf. Es gehen durch das löschen der Tabelle current keine Log-Informationen verloren.&lt;br /&gt;
Der Inhalt wird aber u.a. für die Dropdown-Felder beim Plot-Editor verwendet.&lt;br /&gt;
&lt;br /&gt;
Um doppelte Einträge in der Tabelle zu vermeiden, wurden die Möglichkeit geschaffen Primary Keys zu definieren. Da in der Spalte &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; u.U. bei verschiedenen Geräten gleiche Namen vorkommen können, sollte der Primary Key um den Gerätenamen erweitert werden. Der Primary Key sollte also aus &amp;lt;code&amp;gt;DEVICE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;READING&amp;lt;/code&amp;gt; bestehen. Um in der Datenbank &#039;&#039;fhem&#039;&#039; diesen PK zu setzen, kann folgender SQL Code verwendet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
ALTER TABLE `fhem`.`current` &lt;br /&gt;
CHANGE COLUMN `DEVICE` `DEVICE` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
CHANGE COLUMN `READING` `READING` VARCHAR(64) CHARACTER SET &#039;utf8&#039; COLLATE &#039;utf8_bin&#039; NOT NULL ,&lt;br /&gt;
ADD PRIMARY KEY (`DEVICE`, `READING`);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== history ====&lt;br /&gt;
Die Tabelle history enthält alle bisher geloggten Daten. Löschen in dieser Tabelle bedeutet automatisch Datenverlust (gewollt oder nicht ... )&lt;br /&gt;
Der Inhalt dieser Tabelle wird verwendet, um die Plots zu zeichnen oder Auswertungen mit [https://wiki.fhem.de/wiki/DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten DbRep] anzufertigen&lt;br /&gt;
&lt;br /&gt;
{{Todo|Ausbauen}}&lt;br /&gt;
&lt;br /&gt;
Um Problem beim Import von cacheFiles zu vermeiden, kann in der Datenbank ein PK angelegt werden, welcher Timestamp, Device und Reading umfasst. Dadurch werden doppelte Einträge wirksam verhindert.&lt;br /&gt;
&lt;br /&gt;
== Anpassen der gplot-Konfigurationen ==&lt;br /&gt;
Die meisten gplot-Konfigurationen sind bisher lediglich auf FileLog-Konfigurationen ausgelegt. Deshalb müssen sie für die Verwendung mit DbLog angepasst werden. Glücklicherweise beschränkt sich dies auf die reinen FileLog-Zeilen - es müssen die DbLog-Äquivalente hinzugefügt werden. Die FileLog-Einträge müssen zwar nicht gelöscht werden, wenn man aber FileLog und DbLog parallel betreibt, sollte man getrennte gplot-Dateien für beide Logging-Typen haben um Auswertungsprobleme erkennen zu können.&lt;br /&gt;
&lt;br /&gt;
Für die fht.gplot Konfiguration sähe die Anpassung wie folgt aus (lediglich die vier DbLog-Zeilen wurden hinzugefügt):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# Created by FHEM/98_SVG.pm, 2014-12-25 21:53: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 nomirror&lt;br /&gt;
set y2tics &lt;br /&gt;
set grid y2tics&lt;br /&gt;
set ylabel &amp;quot;Actuator/Window (%)&amp;quot;&lt;br /&gt;
set y2label &amp;quot;Temperature in C&amp;quot;&lt;br /&gt;
set yrange 0:100&lt;br /&gt;
set y2range 5:25&lt;br /&gt;
&lt;br /&gt;
#FileLog 4:.measured-temp\x3a:0:&lt;br /&gt;
#FileLog 4:.actuator\x3a:0:int&lt;br /&gt;
#FileLog 4:.desired-temp::&lt;br /&gt;
#FileLog 4:.window\x3a::&lt;br /&gt;
&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.measured-temp:0:&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.actuator:0:int&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.desired-temp::&lt;br /&gt;
#DbLog &amp;lt;SPEC1&amp;gt;:.window::&lt;br /&gt;
&lt;br /&gt;
plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Measured temperature&#039; ls l0 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Actuator (%)&#039; ls l1 lw 1 with lines,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Desired Temperature&#039; ls l2 lw 1 with steps,\&lt;br /&gt;
     &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y1 title &#039;Window&#039; ls l3 lw 1 with steps&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Des Weiteren ist zu beachten: &lt;br /&gt;
&lt;br /&gt;
On-Off-Plots&lt;br /&gt;
&lt;br /&gt;
 EG_Bad:window:::$val=~s/(on|off)(\d*).*/$1eq&amp;quot;on&amp;quot;?1:0/eg&lt;br /&gt;
&lt;br /&gt;
unter Berücksichtigung von dim-Werten:&lt;br /&gt;
&lt;br /&gt;
 EG_WoZi_Licht:value:::$val=~s/(on|off)(\d*).*/$1eq&amp;quot;on&amp;quot;?1:($1eq&amp;quot;dim&amp;quot;?$2*0.01:0)/eg&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer SQLite-Datenbank ==&lt;br /&gt;
Im folgenden wird eine lokale SQLite-Datenbank auf einen Ubuntu-System angelegt (nach Quelle: [http://www.tatsch-it.de/fhem-dblog/ http://www.tatsch-it.de/fhem-dblog/])&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Installation von SQLite:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo aptitude install sqlite3 libdbi-perl libdbd-sqlite3-perl&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anlegen der SQLite-Datenbank fhem.db&#039;&#039; (öffnet auch direkt eine SQL-Kommandozeile):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 /opt/fhem/fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der geöffneten SQL-Kommandozeile eingeben:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE &#039;history&#039; (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
CREATE TABLE &#039;current&#039; (TIMESTAMP TIMESTAMP, DEVICE varchar(64), TYPE varchar(64), EVENT varchar(512), READING varchar(64), VALUE varchar(128), UNIT varchar(32));&lt;br /&gt;
CREATE INDEX Search_Idx ON `history` (DEVICE, READING, TIMESTAMP);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Kommandozeile verlässt man mit &amp;lt;code&amp;gt;.exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anpassen des Besitzers und der Rechte der Datenbank-Datei:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo chown fhem /opt/fhem/fhem.db&lt;br /&gt;
sudo chmod 666 /opt/fhem/fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Datenbank-Anbindung des FHEM konfigurieren:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo nano /opt/fhem/db.conf&amp;lt;/pre&amp;gt;&lt;br /&gt;
Inhalt:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
%dbconfig= (&lt;br /&gt;
  connection =&amp;gt; &amp;quot;SQLite:dbname=/opt/fhem/fhem.db&amp;quot;,&lt;br /&gt;
  user =&amp;gt; &amp;quot;&amp;quot;,&lt;br /&gt;
  password =&amp;gt; &amp;quot;&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Logging des FHEM auf die Datenbank konfigurieren:&#039;&#039; (hier sind nur die Anpassungen aufgeführt)&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo nano /opt/fhem/fhem.cfg&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
attr global userattr DbLogExclude ...  # erlaubt es einzelne Einträge nicht zu loggen&lt;br /&gt;
...&lt;br /&gt;
define logdb DbLog ./db.conf .*:.*     # logt alle(!) auflaufenden Events aller Konfigurationen&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Da durch diese &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Definition alle auflaufenden Events gelogt werden, müssen keine weiteren Anpassungen in der Konfiguration gemacht werden. Die FileLog-Einträge können bedenkenlos bestehen bleiben - dann wird in Datenbank und FileLog gelogt und man verliert keine Daten, falls etwas nicht klappt. Wenn alles wie geplant läuft, können die FileLog-Definitionen gelöscht werden (ebenso die Log-Dateien). Ebenso können die zu loggenden Daten später eingegrenzt werden (s. [[#Finetuning des Loggings]]).&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;FHEM neu starten:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo service fhem stop&lt;br /&gt;
sudo service fhem start&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Kontrollieren, ob Logs in die Datenbank geschrieben werden:&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 /opt/fhem/fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der geöffneten SQL-Kommandozeile eingeben:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
select * from history order by TIMESTAMP;       # dies gibt alle(!) Logs chronologisch aus (kann nach längerem Betrieb recht lange dauern)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die Kommandozeile verlässt man mit &amp;lt;code&amp;gt;.exit&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
&#039;&#039;Anpassung der glot-Dateien:&#039;&#039; siehe [[#Anpassen der gplot-Konfigurationen]]&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Anlegen und Nutzung einer Mysql-Datenbank ==&lt;br /&gt;
Anstatt nano kann jeder andere kompatible Editor verwendet werden. Weiterhin bitte beachten, dass die hier genannten Befehle teilweise root-Rechte voraussetzen. Entweder komplett als root arbeiten, oder mittels sudo.&lt;br /&gt;
&lt;br /&gt;
Unter Ubuntu/debian: &lt;br /&gt;
 apt-get update &amp;amp;&amp;amp; apt-get install mysql-server mysql-client libdbd-mysql libdbd-mysql-perl&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Bei der Installation sollte man aus Sicherheitsgründen ein Passwort für den mysql-root vergeben, wenn man nicht sogar ganz den Login verbietet.&lt;br /&gt;
&lt;br /&gt;
Hinweis: im Folgenden ist &amp;quot;#&amp;quot; der normale Prompt und &amp;quot;mysql&amp;gt;&amp;quot; der prompt innerhalb mysql, dieser kann mit exit verlassen werden. &lt;br /&gt;
&lt;br /&gt;
Zum Test mal mit mysql verbinden:&lt;br /&gt;
 # mysql -p -u root&lt;br /&gt;
 Enter password:&lt;br /&gt;
 mysql&amp;gt; exit&lt;br /&gt;
&lt;br /&gt;
Jetzt die Tabellenstruktur anlegen. &lt;br /&gt;
Hierfür kann die Datei /opt/fhem/contrib/dblog/db_create_mysql.sql als Vorlage verwendet und das Passwort und der Benutzername geändert werden. &lt;br /&gt;
 cd /opt/fhem/contrib/dblog/&lt;br /&gt;
 nano db_create_mysql.sql&lt;br /&gt;
Dann wird die Datei eingelesen (root Passwort wird abgefragt): &lt;br /&gt;
&lt;br /&gt;
 # mysql -u root -p &amp;lt; db_create_mysql.sql&lt;br /&gt;
&lt;br /&gt;
Jetzt kann man den Zugang testen: &lt;br /&gt;
&lt;br /&gt;
 # mysql -p -u &amp;lt;fhemuser&amp;gt;&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; show databases;&lt;br /&gt;
&lt;br /&gt;
Nun müsste eine Datenbank &amp;quot;fhem&amp;quot; angezeigt werden, die die Tabellen current und history enthält.&lt;br /&gt;
&lt;br /&gt;
Nun in der Datei db.conf den mysql-Block auskommentieren und ebenfalls Benutzername, Passwort UND HOST anpassen. Leider ist hier nicht standardmäßig localhost eingestellt.&lt;br /&gt;
 nano /opt/fhem/db.conf&lt;br /&gt;
&lt;br /&gt;
Jetzt kann unter FHEM ein DbLog-Device angelegt werden (mit dem beispiel wird alles geloggt: &lt;br /&gt;
 define logdb DbLog ./db.conf .*:.*&lt;br /&gt;
Als State muss ein &amp;quot;connected&amp;quot; angezeigt werden. &lt;br /&gt;
&lt;br /&gt;
Ein rereadcfg in FHEM stellt sicher, dass die neue Konfiguration übernommen wird - ein Neustart ist nicht erforderlich.&lt;br /&gt;
&lt;br /&gt;
Nun kann die Funktion noch einmal überprüft werden: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
 # mysql -u &amp;lt;fhemuser&amp;gt; -p&lt;br /&gt;
 Enter password: &amp;lt;fhempassword&amp;gt;&lt;br /&gt;
 mysql&amp;gt; use fhem;&lt;br /&gt;
 Database changed&lt;br /&gt;
 mysql&amp;gt; show tables;&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | Tables_in_fhem |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 | current        |&lt;br /&gt;
 | history        |&lt;br /&gt;
 +----------------+&lt;br /&gt;
 2 rows in set (0,00 sec)&lt;br /&gt;
 mysql&amp;gt; select * from history; # Achtung, kann sehr groß werden .... #&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiel: Abfragescript PHP/MySQL ==&lt;br /&gt;
Um eine schnelle Übersicht zu bekommen habe ich mir dieses Script geschrieben:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&amp;lt;?php $pdo = new PDO(&#039;mysql:host=localhost;dbname=fhem&#039;, &#039;fhemuser&#039;, &#039;fhempasswort&#039;);&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle Current&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Readings&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE, GROUP_CONCAT(DISTINCT READING ORDER BY READING DESC SEPARATOR &#039;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&#039;) FROM current GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;quot; . $row[2] . &amp;quot;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
echo &#039;&amp;lt;h2&amp;gt;Tabelle History&amp;lt;/h1&amp;gt;&amp;lt;br&amp;gt;&amp;lt;table border=&amp;quot;1&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Anzahl&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
$sql = &amp;quot;SELECT COUNT(*), DEVICE FROM history GROUP BY DEVICE;&amp;quot;; foreach ($pdo-&amp;gt;query($sql) as&lt;br /&gt;
$row) {&lt;br /&gt;
  echo &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[0] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot; . $row[1] . &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bitte passt fhemuser und fhempasswort an. Das Ganze kommt dann nach &#039;&#039;/var/www/html/fhemdb.php&#039;&#039; und ist mit &#039;&#039;&amp;lt;IP&amp;gt;/fhemdb.php&#039;&#039; aufrufbar. Wenn ihr den 2. Block für die history Tabelle ausklammert oder entfernt läuft das Script viel schneller ab - klar die history Tabelle ist meist randvoll.&lt;br /&gt;
&lt;br /&gt;
== Integration von DBLog in eigene Module ==&lt;br /&gt;
=== Bereitstellung der UNITS ===&lt;br /&gt;
Mit der DbLog_splitFn kann der Modulautor selbst festlegen, wie die Events des Moduls in die Bestandteile Reading/Value/Unit zerlegt werden um ein korrektes Logging per DbLog zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
Dazu muss der Modulautor in der [[DevelopmentModuleIntro#X_Initialize|Initialize-Funktion]] eine &amp;lt;code&amp;gt;DbLog_splitFn&amp;lt;/code&amp;gt; bereitstellen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	...&lt;br /&gt;
	$hash-&amp;gt;{DbLog_splitFn}      = &amp;quot;X_DbLog_splitFn&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die genaue Aufrufsyntax und Funktionweise einer DbLog_split-Funktion findet man [[DevelopmentModuleIntro#X_DbLog_split|hier]].&lt;br /&gt;
&lt;br /&gt;
== Werte auslesen ==&lt;br /&gt;
Manchmal möchte man Daten aus den Logs abrufen ohne händisch in der Datenbank herumzuwühlen (s.u.). Dies ist insb. auch dann hilfreich, wenn man eigenen Funktionen, Notifys oder spezielle Plots entwirft, bei denen man auf Logdaten zugreifen möchte.&lt;br /&gt;
&lt;br /&gt;
Grundsätzlich beschrieben ist dies in der {{Link2CmdRef|Lang=de|Anker=DbLog}} und unterscheidet sich minimal (aber entscheidend) von der Struktur bei [[FileLog#Werte_auslesen|FileLogs]].&lt;br /&gt;
&lt;br /&gt;
Hier ein paar Beispiele, was man damit anstellen kann:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01 2016-10-03 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor vom 01.10.-03.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor&amp;lt;/code&amp;gt; alle Einträge des meinSensor von 8-16 Uhr am 01.10.2016&lt;br /&gt;
* &amp;lt;code&amp;gt;get meineDB - - 2016-10-01_08:00:00 2016-10-01_16:00:00 meinSensor:temperature&amp;lt;/code&amp;gt; nur die temperature Werte&lt;br /&gt;
* &amp;lt;code&amp;gt;{ ReadingsTimestamp(&amp;quot;meinSensor&amp;quot;,&amp;quot;state&amp;quot;,&amp;quot;0&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des aktuellen state des meinSensor&lt;br /&gt;
* &amp;lt;code&amp;gt;{ OldTimestamp(&amp;quot;meinSensor&amp;quot;) }&amp;lt;/code&amp;gt; Timestamp des letzten state des FHT_3a32&lt;br /&gt;
* &amp;lt;code&amp;gt;{ time_str2num(OldTimestamp(&amp;quot;meinSensor&amp;quot;)) }&amp;lt;/code&amp;gt; Timestamp in Sekunden des letzten state des meinSensor&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
== Bearbeitung von Datenbank-Einträgen ==&lt;br /&gt;
{{Hinweis|Dieser Abschnitt soll lediglich eine kleine Einführung in die Datenbank-Bearbeitung liefern. Für vertiefende Informationen sollte man sich grundsätzlich mit SQL beschäftigen. Eine umfassende und gut verständliche Anleitung zu SQL bietet bspw. [http://www.w3schools.com/sql/default.asp w3schools].}}&lt;br /&gt;
Irgendwann wird der Fall eintreten, dass in der Datenbank Einträge drinstehen, die geändert oder gelöscht werden sollen (zB. fehlerhafte Sensor-Rückmeldungen, umbenannte Readings). In klassischen Log-Dateien würde man diese einfach bearbeiten und löschen/anpassen (wobei man aber tunlichst zuvor FHEM stoppt, um Datenfehler zu vermeiden). Eine Datenbank kann bearbeitet werden, ohne FHEM stoppen zu müssen. &lt;br /&gt;
&lt;br /&gt;
Datenbanken kann man ohne weitere Hilfsmittel direkt von der Kommandozeile/Shell aus bearbeiten. Alternativ gibt es auch verschiedenste Tools (webbasiert oder als Applikation), die einen dabei unterstützen (Bsp. findet man u.a. [https://wiki.ubuntuusers.de/SQLite/#Grafische-Benutzeroberflaechen hier]). Für einfache Arbeiten reicht allerdings idR. Shell.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
&#039;&#039;&#039;Öffnen der DB unter Linux:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(Es werden Schreibrechte benötigt,ohne kann man die DB zwar öffnen, aber nichts machen)&lt;br /&gt;
 sudo sqlite3 fhem.db&lt;br /&gt;
Dadurch öffnet sich ein SQL-Konsole, auf der alle weiteren Befehle ausgeführt werden.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Schliessen der DB:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .exit&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hilfe anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .help&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Tabellen anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .tables&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Das Schema der DB anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
(vgl. oben [[DbLog#Datenbanken]] und [[DbLog#Beispiel: Anlegen und Nutzung einer SQLite-Datenbank]])&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; .schema&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Eintäge anzeigen:&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
Die Einträge liegen alle in der Tabelle &amp;quot;History&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ganz wichtig&#039;&#039;&#039; ist immer das &amp;quot;;&amp;quot; am Ende Zeile (bei allen Kommandos, die nicht mit einem &amp;quot;.&amp;quot; anfangen). Wenn es vergessen wurde zeigt die Konsole solange neue Zeilen bis ein &amp;quot;;&amp;quot; eingegeben wird. So kann ein Befehl auch bequem über mehrere Zeilen geschrieben werden.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY;&lt;br /&gt;
&lt;br /&gt;
Dies kann sehr lange dauern und kann ggf. mit &amp;lt;code&amp;gt;STRG-C&amp;lt;/code&amp;gt; abgebrochen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;where&amp;lt;/code&amp;gt;-Statements werden Strings in einfache Anführungsstriche gesetzt, Zahlen nicht.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039;;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Alle Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; select * from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;LÖSCHEN aller Einträge eines bestimmten Wertes eines Readings eines Geräts anzeigen:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039; Löschen kann nicht rückgängig gemacht werden!! Also IMMER erst die entsprechenden SELECT-Statements solange verfeinern bis wirklich nur die gewünschten Einträge angezeigt werden. Dann das &amp;lt;code&amp;gt;select *&amp;lt;/code&amp;gt; durch &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; ersetzen.&lt;br /&gt;
&lt;br /&gt;
 sqlite&amp;gt; delete from HISTORY where DEVICE=&#039;Pollenflug&#039; and READING=&#039;Graeser&#039; and VALUE&amp;gt;1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank reparieren ==&lt;br /&gt;
Es kann immer wieder mal vorkommen, dass Datenbanken Fehler enthalten. Das muss im Alltag garnicht auffallen und auch nicht immer schlimm enden. Wenn man auf der SQL-Konsole aber bspw. eine Meldung &amp;lt;code&amp;gt;Error: database disk image is malformed&amp;lt;/code&amp;gt; erhält, sollte man ein Reparatur vornehmen.&lt;br /&gt;
&lt;br /&gt;
=== SQLite-Datenbanken ===&lt;br /&gt;
Die folgenden Schritte beschreiben, wie man eine SQLite-DB reparieren kann (Quelle: [http://techblog.dorogin.com/2011/05/sqliteexception-database-disk-image-is.html]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
DB öffnen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Integritäts-Check durchführen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sqlite&amp;gt; pragma integrity_check;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Kommt hier ein &amp;quot;ok&amp;quot; ist die DB gesund. Ansonsten erscheint etwas wie&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
*** in database main ***&lt;br /&gt;
On tree page 118786 cell 1: Rowid 75 out of order (previous was 816660)&lt;br /&gt;
On tree page 118786 cell 4: Rowid 815704 out of order (previous was 816727)&lt;br /&gt;
Corruption detected in cell 0 on page 118786&lt;br /&gt;
Multiple uses for byte 132 of page 118786&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Datenbank-Dump erstellen (Export gesamten DB in die Datei &amp;quot;dump_all_20160516_1043.sql&amp;quot;) und DB verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .mode insert&lt;br /&gt;
sqlite&amp;gt; .output dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; .dump&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Neue Datenbank erstellen und den Dump einlesen, Integritäts-Check machen und verlassen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo sqlite3 fhem-neu.db&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sqlite&amp;gt; .read dump_all_20160516_1043.sql&lt;br /&gt;
sqlite&amp;gt; pragma integrity_check;&lt;br /&gt;
ok&lt;br /&gt;
sqlite&amp;gt; .exit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Spätestens jetzt FHEM stoppen:&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem stop&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Alte DB sichern und neue aktivieren:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sudo mv fhem.db fhem.db.sv_20160516&lt;br /&gt;
sudo mv fhem-neu.db fhem.db&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
Kontrollieren, dass die neue DB die gleichen Rechte wie die alte DB hat (und ggf. korrigieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 root root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
~/fhem$ sudo chown fhem:root fhem.db&lt;br /&gt;
&lt;br /&gt;
~/fhem$ ls -lha&lt;br /&gt;
insgesamt 6,3G&lt;br /&gt;
drwxr-xr-x 12 fhem root    4,0K Mai 16 11:07 .&lt;br /&gt;
drwxr-xr-x  4 root root    4,0K Dez 25 17:50 ..&lt;br /&gt;
...&lt;br /&gt;
-rw-r--r--  1 fhem root    1,4G Mai 16 11:04 fhem.db&lt;br /&gt;
-rw-r--r--  1 fhem root    2,6G Mai 16 10:59 fhem.db.sv_20160516&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&lt;br /&gt;
FHEM wieder starten (und natürlich kontrollieren):&lt;br /&gt;
&amp;lt;pre&amp;gt;sudo service fhem start&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Datenbank migrieren ==&lt;br /&gt;
Eine schöne Anleitung zur Migration von SQLite zu MySQL/MariaDB mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] findet sich hier: [https://demaya.de/fhem-umzug-sqlite-mysql-mariadb/].&lt;br /&gt;
&lt;br /&gt;
Hinweis: Wenn die SQLite-DB sehr groß wird, kann es sein, dass der oben beschriebene Weg nicht funktioniert (konkret war dies bei meiner 15 GB großen DB nicht möglich, der Prozess hat sich immer nach mehreren Stunden aufgehängt).&lt;br /&gt;
&lt;br /&gt;
== Nützliche Codeschnipsel ==&lt;br /&gt;
Anbei ein paar nützliche Codeschnipsel rund um DbLog&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dateigrösse mitloggen ===&lt;br /&gt;
Da die Datenbank ins Unermessliche wachsen kann, empfiehlt es sich - je nach Speicherplatz - ab einer bestimmten Grösse tätig zu werden. Dazu muss diese Grösse allerdings ermittelt werden. Diese geschieht mittels des Userreadings, welches man vorteilshafterweise mit im DbLog-device anlegt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;attr myDbLog userReadings DbFileSize:reduceLogState.* { (split(&#039; &#039;,`du -m fhem.db`))[0] }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mittels dieses Attributs wird die Grösse der .db-Datei immer nach dem Ausführen des ReduceLog in das Reading &amp;quot;DbFileSize&amp;quot; in ganzzahligen MByte abgelegt.&lt;br /&gt;
&lt;br /&gt;
Basierend auf diesem Reading können dann weitere Aktionen, beispielsweise ein Plot, erstellt werden.&lt;br /&gt;
&lt;br /&gt;
Die oben beschriebene Möglichkeit ist für SQLite verwendbar. Zur Ermittlung der DB-Größe andere DB-Typen (aber auch für SQLite nutzbar) kann wie [[DbRep_-_Reporting_und_Management_von_DbLog-Datenbankinhalten#Gr.C3.B6.C3.9Fe_der_FHEM-Datenbank_ermitteln | hier]] beschrieben vorgegangen werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Performance-Optimierung ==&lt;br /&gt;
Auch eine Datenbank kann mit der Zeit langsamer werden. Dies hängt von mehreren Faktoren ab:&lt;br /&gt;
* Menge der gelogten Daten (zB. &amp;gt; 4-5 GB)&lt;br /&gt;
* Eingesetzte Hardware (zB. langsame SD-Karte vs. schnelle SSD)&lt;br /&gt;
* Eingesetztes Datenbanksystem (zB. SQLite, MySQL)&lt;br /&gt;
* Komplexität der Abfragen (zB. für aufwändige Graphen oder Berechnungen)&lt;br /&gt;
&lt;br /&gt;
Diese Punkte sollen im folgenden diskutiert werden:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Komplexität der Abfragen ===&lt;br /&gt;
Dies ist kein Problem der Datenbank, sondern rein der Abfrage. Dem entsprechend muss die Optimierung auch in der Abfrage oder im Skript gesucht werden. Dies ist nicht Ziel dieses Abschnittes und wird hier nicht weiter behandelt.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetztes Datenbanksystem ===&lt;br /&gt;
Welches Datenbanksystem eingesetzt wird (zB. SQLite oder MySQL) hat auf die Performance der Datenbank gar keinen so großen Einfluss, wie vielleicht zuerst gedacht. Selbst SQLite kann problemlos Datenbanken mit etlichen GB Größe performant verarbeiten. Der Flaschenhals ist hier viel mehr die darunter liegende Hardware (s.u.).&lt;br /&gt;
&lt;br /&gt;
Die Performance der Datenbank an sich, kann aber durch verschiedene Maßnahmen verbessert werden:&lt;br /&gt;
* Pflegemaßnahmen bzgl. der Daten&lt;br /&gt;
* &#039;&#039;&#039;Erstellung von Indizes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Menge der Daten und Pflegemaßnahmen bzgl. der Daten ====&lt;br /&gt;
Die Menge der geloggten Daten hat natürlich Einfluss auf die Geschwindigkeit von Abfragen - je mehr Daten vorhanden sind, desto mehr Daten müssen auch durchforstet werden um eine Abfrage zu bedienen. Die Reduzierung der geloggten Datenmenge hat also direkten Einfluss auf die Größe und damit auch die Geschwindigkeit der Datenbank. Die Menge der zu loggenden Daten lässt sich an zwei Stellen einschränken:&lt;br /&gt;
* bei der Definition jedes Devices (s. Kapitel oben)&lt;br /&gt;
* bei der Festlegung des fhem-weiten Log-Levels (s. [[Loglevel]])&lt;br /&gt;
&lt;br /&gt;
Die Menge der bereits geloggten Daten kann zB. mit Hilfe von [[DbRep - Reporting und Management von DbLog-Datenbankinhalten|DbRep]] verringert und optimiert werden, bspw.&lt;br /&gt;
* löschen unnötiger Daten&lt;br /&gt;
* vacuum der Datenbank&lt;br /&gt;
&lt;br /&gt;
Insgesamt haben diese Maßnahmen aber nur einen eingeschränkten Effekt auf die Performance der DB. Deutlich effektiver ist die Erstellung eines Index (s.u.).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Erstellung von Indizes ====&lt;br /&gt;
Die Erstellung von Indizes hat mit Abstand den größten Einfluss auf die Performance einer Datenbank (auf unveränderter Hardware). Extrem zusammengefasst ist ein Index eine extrem optimiertes Nachschlageverzeichnis für einen bestimmten Typ Daten (ein Index wie im Buch halt). Eine wunderbare Einführung in Indizes bietet [[https://use-the-index-luke.com/de|https://use-the-index-luke.com]].&lt;br /&gt;
&lt;br /&gt;
In fhem sind Indizes sogar sehr einfach einzurichten da die Datenbank-Nutzung sehr stark vorgegeben ist. Nahezu jede Abfrage folgt dem Schema &#039;&#039;Device -&amp;gt; Reading -&amp;gt; Datum -&amp;gt; Wert&#039;&#039;. Ein Index kann genau diese Abfrage bedienen und beschleunigen. Ein Index nur über die Devices wäre ein erster Schritt, brächte aber noch keinen großen Gewinn (wie um Link oben gut beschrieben). Über die gesamten ersten drei Schritte erstellt (Device -&amp;gt; Reading -&amp;gt; Datum) bringt der Index aber sofort eine deutliche Geschwindigkeitssteigerung.&lt;br /&gt;
&lt;br /&gt;
Die Erstellung eines Index erfolgt direkt in der Datenbank (und nicht aus fhem heraus), hier am Beispiel einer SQLite-DB:&lt;br /&gt;
&lt;br /&gt;
Öffnen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; sudo sqlite3 fhem.db &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Erzeugen des Index auf der DB-Konsole (das Semikolon am Ende ist wichtig):&lt;br /&gt;
&amp;lt;pre&amp;gt; create index idx_device_reading_timestamp on history (device, reading, timestamp); &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Verlassen der DB:&lt;br /&gt;
&amp;lt;pre&amp;gt; .exit &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Einzig zu berücksichtigen ist, dass dieser Index die Datenbank um bis zu 1/3 vergrößert. Er kann aber bei Bedarf auch wieder entfernt werden. Bei meiner 15 GB SQLite-Datei (auf einem Mac Mini mit SSD) hat dies ca. 15 min gedauert (den fhem hatte ich vorsichtshalber währenddessen deaktiviert).&lt;br /&gt;
&lt;br /&gt;
Sollte jemand spezielle Berechnungen oder Skripte ausführen, die nach einem anderen Abfrage-Schema arbeiten, könnte man dafür spezialisierte zusätzliche Indizes erstellen. Das sollte aber dann mit dem Wissen des obigen Links erarbeitet werden, da dann etwas mehr Hintergrundwissen sehr hilfreich ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== DB-Backup ====&lt;br /&gt;
Ein anderer Aspekt, der eigentlich nichts mit der Performance der DB zu tun hat, ist der Einfluss aufs Backup. Wird bspw. ein Systembackup per RSYNC gemacht, muss bei SQLite immer die komplette ggf. riesige Datei gesynct werden - bei MySQL würden nur die veränderten DB-Elemente gesynct. Dies soll hier nicht weiter vertieft werden, sollte aber bei einer Gesamtstrategie bedacht werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Eingesetzte Hardware ===&lt;br /&gt;
fhem hat grundsätzlich sehr viele kleine Datenzugriffe, unabhängig davon ob FileLog oder DbLog eingesetzt wird. Deshalb ist der absolut größte Performance-Gewinn durch den Einsatz schneller Festplatten zu erreichen - ganz einfache Aussage: &#039;&#039;je schneller desto besser ;-)&#039;&#039;. Konkret bietet eine SSD mit mind. 250MB/s Datenzugriff eine ordentliche Basis für jedes datengestützte System, wie den fhem.&lt;br /&gt;
&lt;br /&gt;
Wenn die Datenmenge größer und die Abfragen komplexer werden, müssen natürlich irgendwann auch die Prozessorleistung und der Arbeitsspeicher mit wachsen. Aber auch an einem Raspi wird eine SSD deutlich performanter sein, als eine einfache SD-Karte oder eine klassische rotierende Festplatte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [[Heizleistung_und_Gasverbrauch|Beispiel das DbLog-Daten für SVG-Plots verwendet]]&lt;br /&gt;
* [[SVG-Plots von FileLog auf DbLog umstellen]]&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28157</id>
		<title>BOTVAC</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28157"/>
		<updated>2018-10-23T22:21:37Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Attribute */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Dieses Modul dient zur Steuerung eines NEATO oder VORWERK Saugroboters&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=BOTVAC&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=noch nicht eingcheckt&lt;br /&gt;
|ModOwner=vuffiraa&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[BOTVAC]] ist ein Modul mit welchem VORWERK und NEATO Saugroboter gesteuert werden können. &lt;br /&gt;
&lt;br /&gt;
== Allgemeines ==&lt;br /&gt;
=== Funktionsumfang  ===&lt;br /&gt;
Aktuell ist folgenden Funktionsumfang vorhanden:&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
*actionInterval&lt;br /&gt;
**Poll-Interval für den aktiven Betrieb (Busy/Paused)&lt;br /&gt;
*boundaries&lt;br /&gt;
&lt;br /&gt;
*cleaningMode&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: eco,turbo&lt;br /&gt;
*cleaningModifier&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: normal,double&lt;br /&gt;
*cleaningNavigationMode&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: normal,extra care,deep&lt;br /&gt;
**Note that navigationMode can only be set to deep, if cleaningMode is turbo&lt;br /&gt;
*cleaningSpotWidth&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: 100,200,300,400&lt;br /&gt;
*cleaningSpotHeight&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: 100,200,300,400&lt;br /&gt;
Es muss kein Quadrat sein, der Bereich ist von 100 - 400 cm, in 1 cm Schritten einstellbar. Rechtecke von z.B. 152x244 sind also möglich. &lt;br /&gt;
&lt;br /&gt;
Die Attribute werden zum Saugertyp passend angeboten. Dadurch sind die Startbefehle für die Reinigung parameterlos. Einzige Ausnahme beim D7 ist set &amp;lt;name&amp;gt; startCleaning &amp;lt;house|map&amp;gt;&lt;br /&gt;
Wenn die cleaning-Attribute nicht definiert sind, werden die Werte der letzten Reinigung aus dem Reading genommen.&lt;br /&gt;
&lt;br /&gt;
=== SET === &lt;br /&gt;
*dismissCurrentAlert&lt;br /&gt;
**Zurücksetzen einer anstehenden Warnmeldung&lt;br /&gt;
*findMe&lt;br /&gt;
**The robot will emit a sound and blink the LEDs when it receives the findMe command.&lt;br /&gt;
*manualCleaningMode&lt;br /&gt;
*nextCleaningMode&lt;br /&gt;
*nextCleaningNavigationMode&lt;br /&gt;
*nextCleaningSpotHeight&lt;br /&gt;
*nextCleaningSpotWidth&lt;br /&gt;
*password&lt;br /&gt;
*pause&lt;br /&gt;
**pausiert die Reinigung&lt;br /&gt;
*pauseToBase&lt;br /&gt;
**Beendet eine Reinigung und fährt zur Basis zurück&lt;br /&gt;
*reloadMaps&lt;br /&gt;
**Lädt die letzte Karte vom Neato-Server, die erstellt wurde. Das passiert normalerweise automatisch, ist aber z.B. beim Neustart von Fhem sinnvoll. Beim Vorwerk läuft diese Funktion (noch) ins Leere.&lt;br /&gt;
*resume&lt;br /&gt;
**Setzt Reinigung fort&lt;br /&gt;
&lt;br /&gt;
*schedule&lt;br /&gt;
**Die möglichen Argumente sind &#039;on&#039; und &#039;off&#039; zum An- und Abschalten der Zeitsteuerung.&lt;br /&gt;
*sendToBase&lt;br /&gt;
**Fährt zur Basis zurück&lt;br /&gt;
&lt;br /&gt;
*setBoundaries&lt;br /&gt;
**https://developers.neatorobotics.com/api/robot-remote-protocol/maps&lt;br /&gt;
*setRobot&lt;br /&gt;
**Legt den zu steuernden Roboter fest&lt;br /&gt;
*startCleaning&lt;br /&gt;
** Startet die Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*startSpot&lt;br /&gt;
**Startet die Spot-Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*statusRequest&lt;br /&gt;
*stop&lt;br /&gt;
** stoppt die Reinigung&lt;br /&gt;
*syncRobots&lt;br /&gt;
**Synchronisiert die Roboterdaten. Das ist nützlich, wenn man mehrere Geräte unter einem Neato- bzw. Vorwerk-Account verwaltet und dort Geräte entfernt oder hinzugefügt hat.&lt;br /&gt;
*stopCleaning&lt;br /&gt;
** Stop cleaning the house.&lt;br /&gt;
&lt;br /&gt;
=== GET ===&lt;br /&gt;
*batteryPercent&lt;br /&gt;
&lt;br /&gt;
=== readings ===&lt;br /&gt;
&lt;br /&gt;
*dockHasBeenSeen&lt;br /&gt;
**stellt sich erst eine Weile nach Beginn einer Reinigung auf &amp;quot;1&amp;quot;.Und nur in diesem Zustand klappt das Zurücksenden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== MAPS ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg548498.html#msg548498&lt;br /&gt;
http(s)://&amp;lt;ip&amp;gt;:&amp;lt;port&amp;gt;/fhem/BOTVAC/&amp;lt;device&amp;gt;/map&lt;br /&gt;
&lt;br /&gt;
Map per telegram versenden: &lt;br /&gt;
set &amp;lt;TelegramBot&amp;gt; cmdSend {ReadingsVal(&#039;&amp;lt;BOTVAC&amp;gt;&#039;, &#039;.map_cache&#039;, &#039;&#039;)}&lt;br /&gt;
&lt;br /&gt;
== Grundkonfiguration ==&lt;br /&gt;
&lt;br /&gt;
=== Definition in FHEM ===&lt;br /&gt;
 define &amp;lt;name&amp;gt; BOTVAC &amp;lt;email&amp;gt; [&amp;lt;vendor&amp;gt;] [&amp;lt;poll-interval&amp;gt;]&lt;br /&gt;
 set &amp;lt;name&amp;gt; password &amp;lt;password&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Icons ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg539375.html#msg539375&lt;br /&gt;
&lt;br /&gt;
die Datei reinkopieren, dann &amp;quot;set WEB rereadicons&amp;quot; und anschließend normal als Icon auswählen.&lt;br /&gt;
Inzwischen ist das Icon wohl im SVN eingecheckt.&lt;br /&gt;
&lt;br /&gt;
== Probleme ==&lt;br /&gt;
=== falls er Sauger mit der App neu konfiguriert werden musste ===&lt;br /&gt;
Du hast nach der Neuinstallation der App auch deinen VR/NEATO neu registriert. Fhem lädt aber noch die Daten aus der alten Registrierung. &lt;br /&gt;
Wähle mal im Gerät unter Set &#039;syncRobots&#039; aus. Wenn es danach bei Set auch eine Funktion &#039;setRobot&#039; gibt, hast du wirklich  den VR neu registriert. Dann einfach über dieses Set den VR wechseln. &lt;br /&gt;
Ansonsten bleibt noch die Registrierungsschlüssel im Fhem-Gerät zu löschen. Das geht über die Kommandozeile in Fhem:&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; accessToken&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; secretKey&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28156</id>
		<title>BOTVAC</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28156"/>
		<updated>2018-10-23T20:19:59Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Definition in FHEM */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Dieses Modul dient zur Steuerung eines NEATO oder VORWERK Saugroboters&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=BOTVAC&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=noch nicht eingcheckt&lt;br /&gt;
|ModOwner=vuffiraa&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[BOTVAC]] ist ein Modul mit welchem VORWERK und NEATO Saugroboter gesteuert werden können. &lt;br /&gt;
&lt;br /&gt;
== Allgemeines ==&lt;br /&gt;
=== Funktionsumfang  ===&lt;br /&gt;
Aktuell ist folgenden Funktionsumfang vorhanden:&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
*actionInterval&lt;br /&gt;
**Poll-Interval für den aktiven Betrieb (Busy/Paused)&lt;br /&gt;
*cleaningMode&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: eco,turbo&lt;br /&gt;
*cleaningModifier&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: normal,double&lt;br /&gt;
*cleaningNavigationMode&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: normal,extra care,deep&lt;br /&gt;
**Note that navigationMode can only be set to deep, if cleaningMode is turbo&lt;br /&gt;
*cleaningSpotWidth&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: 100,200,300,400&lt;br /&gt;
*cleaningSpotHeight&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: 100,200,300,400&lt;br /&gt;
Es muss kein Quadrat sein, der Bereich ist von 100 - 400 cm, in 1 cm Schritten einstellbar. Rechtecke von z.B. 152x244 sind also möglich. &lt;br /&gt;
&lt;br /&gt;
Die Attribute werden zum Saugertyp passend angeboten. Dadurch sind die Startbefehle für die Reinigung parameterlos. Einzige Ausnahme beim D7 ist set &amp;lt;name&amp;gt; startCleaning &amp;lt;house|map&amp;gt;&lt;br /&gt;
Wenn die cleaning-Attribute nicht definiert sind, werden die Werte der letzten Reinigung aus dem Reading genommen.&lt;br /&gt;
&lt;br /&gt;
=== SET === &lt;br /&gt;
*dismissCurrentAlert&lt;br /&gt;
**Zurücksetzen einer anstehenden Warnmeldung&lt;br /&gt;
*findMe&lt;br /&gt;
**The robot will emit a sound and blink the LEDs when it receives the findMe command.&lt;br /&gt;
*manualCleaningMode&lt;br /&gt;
*nextCleaningMode&lt;br /&gt;
*nextCleaningNavigationMode&lt;br /&gt;
*nextCleaningSpotHeight&lt;br /&gt;
*nextCleaningSpotWidth&lt;br /&gt;
*password&lt;br /&gt;
*pause&lt;br /&gt;
**pausiert die Reinigung&lt;br /&gt;
*pauseToBase&lt;br /&gt;
**Beendet eine Reinigung und fährt zur Basis zurück&lt;br /&gt;
*reloadMaps&lt;br /&gt;
**Lädt die letzte Karte vom Neato-Server, die erstellt wurde. Das passiert normalerweise automatisch, ist aber z.B. beim Neustart von Fhem sinnvoll. Beim Vorwerk läuft diese Funktion (noch) ins Leere.&lt;br /&gt;
*resume&lt;br /&gt;
**Setzt Reinigung fort&lt;br /&gt;
&lt;br /&gt;
*schedule&lt;br /&gt;
**Die möglichen Argumente sind &#039;on&#039; und &#039;off&#039; zum An- und Abschalten der Zeitsteuerung.&lt;br /&gt;
*sendToBase&lt;br /&gt;
**Fährt zur Basis zurück&lt;br /&gt;
&lt;br /&gt;
*setBoundaries&lt;br /&gt;
**https://developers.neatorobotics.com/api/robot-remote-protocol/maps&lt;br /&gt;
*setRobot&lt;br /&gt;
**Legt den zu steuernden Roboter fest&lt;br /&gt;
*startCleaning&lt;br /&gt;
** Startet die Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*startSpot&lt;br /&gt;
**Startet die Spot-Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*statusRequest&lt;br /&gt;
*stop&lt;br /&gt;
** stoppt die Reinigung&lt;br /&gt;
*syncRobots&lt;br /&gt;
**Synchronisiert die Roboterdaten. Das ist nützlich, wenn man mehrere Geräte unter einem Neato- bzw. Vorwerk-Account verwaltet und dort Geräte entfernt oder hinzugefügt hat.&lt;br /&gt;
*stopCleaning&lt;br /&gt;
** Stop cleaning the house.&lt;br /&gt;
&lt;br /&gt;
=== GET ===&lt;br /&gt;
*batteryPercent&lt;br /&gt;
&lt;br /&gt;
=== readings ===&lt;br /&gt;
&lt;br /&gt;
*dockHasBeenSeen&lt;br /&gt;
**stellt sich erst eine Weile nach Beginn einer Reinigung auf &amp;quot;1&amp;quot;.Und nur in diesem Zustand klappt das Zurücksenden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== MAPS ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg548498.html#msg548498&lt;br /&gt;
http(s)://&amp;lt;ip&amp;gt;:&amp;lt;port&amp;gt;/fhem/BOTVAC/&amp;lt;device&amp;gt;/map&lt;br /&gt;
&lt;br /&gt;
Map per telegram versenden: &lt;br /&gt;
set &amp;lt;TelegramBot&amp;gt; cmdSend {ReadingsVal(&#039;&amp;lt;BOTVAC&amp;gt;&#039;, &#039;.map_cache&#039;, &#039;&#039;)}&lt;br /&gt;
&lt;br /&gt;
== Grundkonfiguration ==&lt;br /&gt;
&lt;br /&gt;
=== Definition in FHEM ===&lt;br /&gt;
 define &amp;lt;name&amp;gt; BOTVAC &amp;lt;email&amp;gt; [&amp;lt;vendor&amp;gt;] [&amp;lt;poll-interval&amp;gt;]&lt;br /&gt;
 set &amp;lt;name&amp;gt; password &amp;lt;password&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Icons ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg539375.html#msg539375&lt;br /&gt;
&lt;br /&gt;
die Datei reinkopieren, dann &amp;quot;set WEB rereadicons&amp;quot; und anschließend normal als Icon auswählen.&lt;br /&gt;
Inzwischen ist das Icon wohl im SVN eingecheckt.&lt;br /&gt;
&lt;br /&gt;
== Probleme ==&lt;br /&gt;
=== falls er Sauger mit der App neu konfiguriert werden musste ===&lt;br /&gt;
Du hast nach der Neuinstallation der App auch deinen VR/NEATO neu registriert. Fhem lädt aber noch die Daten aus der alten Registrierung. &lt;br /&gt;
Wähle mal im Gerät unter Set &#039;syncRobots&#039; aus. Wenn es danach bei Set auch eine Funktion &#039;setRobot&#039; gibt, hast du wirklich  den VR neu registriert. Dann einfach über dieses Set den VR wechseln. &lt;br /&gt;
Ansonsten bleibt noch die Registrierungsschlüssel im Fhem-Gerät zu löschen. Das geht über die Kommandozeile in Fhem:&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; accessToken&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; secretKey&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28155</id>
		<title>BOTVAC</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28155"/>
		<updated>2018-10-23T20:19:53Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Allgemeines */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Dieses Modul dient zur Steuerung eines NEATO oder VORWERK Saugroboters&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=BOTVAC&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=noch nicht eingcheckt&lt;br /&gt;
|ModOwner=vuffiraa&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[BOTVAC]] ist ein Modul mit welchem VORWERK und NEATO Saugroboter gesteuert werden können. &lt;br /&gt;
&lt;br /&gt;
== Allgemeines ==&lt;br /&gt;
=== Funktionsumfang  ===&lt;br /&gt;
Aktuell ist folgenden Funktionsumfang vorhanden:&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
*actionInterval&lt;br /&gt;
**Poll-Interval für den aktiven Betrieb (Busy/Paused)&lt;br /&gt;
*cleaningMode&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: eco,turbo&lt;br /&gt;
*cleaningModifier&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: normal,double&lt;br /&gt;
*cleaningNavigationMode&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: normal,extra care,deep&lt;br /&gt;
**Note that navigationMode can only be set to deep, if cleaningMode is turbo&lt;br /&gt;
*cleaningSpotWidth&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: 100,200,300,400&lt;br /&gt;
*cleaningSpotHeight&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: 100,200,300,400&lt;br /&gt;
Es muss kein Quadrat sein, der Bereich ist von 100 - 400 cm, in 1 cm Schritten einstellbar. Rechtecke von z.B. 152x244 sind also möglich. &lt;br /&gt;
&lt;br /&gt;
Die Attribute werden zum Saugertyp passend angeboten. Dadurch sind die Startbefehle für die Reinigung parameterlos. Einzige Ausnahme beim D7 ist set &amp;lt;name&amp;gt; startCleaning &amp;lt;house|map&amp;gt;&lt;br /&gt;
Wenn die cleaning-Attribute nicht definiert sind, werden die Werte der letzten Reinigung aus dem Reading genommen.&lt;br /&gt;
&lt;br /&gt;
=== SET === &lt;br /&gt;
*dismissCurrentAlert&lt;br /&gt;
**Zurücksetzen einer anstehenden Warnmeldung&lt;br /&gt;
*findMe&lt;br /&gt;
**The robot will emit a sound and blink the LEDs when it receives the findMe command.&lt;br /&gt;
*manualCleaningMode&lt;br /&gt;
*nextCleaningMode&lt;br /&gt;
*nextCleaningNavigationMode&lt;br /&gt;
*nextCleaningSpotHeight&lt;br /&gt;
*nextCleaningSpotWidth&lt;br /&gt;
*password&lt;br /&gt;
*pause&lt;br /&gt;
**pausiert die Reinigung&lt;br /&gt;
*pauseToBase&lt;br /&gt;
**Beendet eine Reinigung und fährt zur Basis zurück&lt;br /&gt;
*reloadMaps&lt;br /&gt;
**Lädt die letzte Karte vom Neato-Server, die erstellt wurde. Das passiert normalerweise automatisch, ist aber z.B. beim Neustart von Fhem sinnvoll. Beim Vorwerk läuft diese Funktion (noch) ins Leere.&lt;br /&gt;
*resume&lt;br /&gt;
**Setzt Reinigung fort&lt;br /&gt;
&lt;br /&gt;
*schedule&lt;br /&gt;
**Die möglichen Argumente sind &#039;on&#039; und &#039;off&#039; zum An- und Abschalten der Zeitsteuerung.&lt;br /&gt;
*sendToBase&lt;br /&gt;
**Fährt zur Basis zurück&lt;br /&gt;
&lt;br /&gt;
*setBoundaries&lt;br /&gt;
**https://developers.neatorobotics.com/api/robot-remote-protocol/maps&lt;br /&gt;
*setRobot&lt;br /&gt;
**Legt den zu steuernden Roboter fest&lt;br /&gt;
*startCleaning&lt;br /&gt;
** Startet die Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*startSpot&lt;br /&gt;
**Startet die Spot-Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*statusRequest&lt;br /&gt;
*stop&lt;br /&gt;
** stoppt die Reinigung&lt;br /&gt;
*syncRobots&lt;br /&gt;
**Synchronisiert die Roboterdaten. Das ist nützlich, wenn man mehrere Geräte unter einem Neato- bzw. Vorwerk-Account verwaltet und dort Geräte entfernt oder hinzugefügt hat.&lt;br /&gt;
*stopCleaning&lt;br /&gt;
** Stop cleaning the house.&lt;br /&gt;
&lt;br /&gt;
=== GET ===&lt;br /&gt;
*batteryPercent&lt;br /&gt;
&lt;br /&gt;
=== readings ===&lt;br /&gt;
&lt;br /&gt;
*dockHasBeenSeen&lt;br /&gt;
**stellt sich erst eine Weile nach Beginn einer Reinigung auf &amp;quot;1&amp;quot;.Und nur in diesem Zustand klappt das Zurücksenden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== MAPS ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg548498.html#msg548498&lt;br /&gt;
http(s)://&amp;lt;ip&amp;gt;:&amp;lt;port&amp;gt;/fhem/BOTVAC/&amp;lt;device&amp;gt;/map&lt;br /&gt;
&lt;br /&gt;
Map per telegram versenden: &lt;br /&gt;
set &amp;lt;TelegramBot&amp;gt; cmdSend {ReadingsVal(&#039;&amp;lt;BOTVAC&amp;gt;&#039;, &#039;.map_cache&#039;, &#039;&#039;)}&lt;br /&gt;
&lt;br /&gt;
== Grundkonfiguration ==&lt;br /&gt;
&lt;br /&gt;
=== Definition in FHEM ===&lt;br /&gt;
=== Icons ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg539375.html#msg539375&lt;br /&gt;
&lt;br /&gt;
die Datei reinkopieren, dann &amp;quot;set WEB rereadicons&amp;quot; und anschließend normal als Icon auswählen.&lt;br /&gt;
Inzwischen ist das Icon wohl im SVN eingecheckt.&lt;br /&gt;
&lt;br /&gt;
== Probleme ==&lt;br /&gt;
=== falls er Sauger mit der App neu konfiguriert werden musste ===&lt;br /&gt;
Du hast nach der Neuinstallation der App auch deinen VR/NEATO neu registriert. Fhem lädt aber noch die Daten aus der alten Registrierung. &lt;br /&gt;
Wähle mal im Gerät unter Set &#039;syncRobots&#039; aus. Wenn es danach bei Set auch eine Funktion &#039;setRobot&#039; gibt, hast du wirklich  den VR neu registriert. Dann einfach über dieses Set den VR wechseln. &lt;br /&gt;
Ansonsten bleibt noch die Registrierungsschlüssel im Fhem-Gerät zu löschen. Das geht über die Kommandozeile in Fhem:&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; accessToken&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; secretKey&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28154</id>
		<title>BOTVAC</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28154"/>
		<updated>2018-10-23T20:19:28Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* FHEM Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Dieses Modul dient zur Steuerung eines NEATO oder VORWERK Saugroboters&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=BOTVAC&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=noch nicht eingcheckt&lt;br /&gt;
|ModOwner=vuffiraa&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[BOTVAC]] ist ein Modul mit welchem VORWERK und NEATO Saugroboter gesteuert werden können. &lt;br /&gt;
&lt;br /&gt;
== Allgemeines ==&lt;br /&gt;
=== Definition ===&lt;br /&gt;
 define &amp;lt;name&amp;gt; BOTVAC &amp;lt;email&amp;gt; [&amp;lt;vendor&amp;gt;] [&amp;lt;poll-interval&amp;gt;]&lt;br /&gt;
 set &amp;lt;name&amp;gt; password &amp;lt;password&amp;gt;&lt;br /&gt;
=== Funktionsumfang  ===&lt;br /&gt;
Aktuell ist folgenden Funktionsumfang vorhanden:&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
*actionInterval&lt;br /&gt;
**Poll-Interval für den aktiven Betrieb (Busy/Paused)&lt;br /&gt;
*cleaningMode&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: eco,turbo&lt;br /&gt;
*cleaningModifier&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: normal,double&lt;br /&gt;
*cleaningNavigationMode&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: normal,extra care,deep&lt;br /&gt;
**Note that navigationMode can only be set to deep, if cleaningMode is turbo&lt;br /&gt;
*cleaningSpotWidth&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: 100,200,300,400&lt;br /&gt;
*cleaningSpotHeight&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: 100,200,300,400&lt;br /&gt;
Es muss kein Quadrat sein, der Bereich ist von 100 - 400 cm, in 1 cm Schritten einstellbar. Rechtecke von z.B. 152x244 sind also möglich. &lt;br /&gt;
&lt;br /&gt;
Die Attribute werden zum Saugertyp passend angeboten. Dadurch sind die Startbefehle für die Reinigung parameterlos. Einzige Ausnahme beim D7 ist set &amp;lt;name&amp;gt; startCleaning &amp;lt;house|map&amp;gt;&lt;br /&gt;
Wenn die cleaning-Attribute nicht definiert sind, werden die Werte der letzten Reinigung aus dem Reading genommen.&lt;br /&gt;
&lt;br /&gt;
=== SET === &lt;br /&gt;
*dismissCurrentAlert&lt;br /&gt;
**Zurücksetzen einer anstehenden Warnmeldung&lt;br /&gt;
*findMe&lt;br /&gt;
**The robot will emit a sound and blink the LEDs when it receives the findMe command.&lt;br /&gt;
*manualCleaningMode&lt;br /&gt;
*nextCleaningMode&lt;br /&gt;
*nextCleaningNavigationMode&lt;br /&gt;
*nextCleaningSpotHeight&lt;br /&gt;
*nextCleaningSpotWidth&lt;br /&gt;
*password&lt;br /&gt;
*pause&lt;br /&gt;
**pausiert die Reinigung&lt;br /&gt;
*pauseToBase&lt;br /&gt;
**Beendet eine Reinigung und fährt zur Basis zurück&lt;br /&gt;
*reloadMaps&lt;br /&gt;
**Lädt die letzte Karte vom Neato-Server, die erstellt wurde. Das passiert normalerweise automatisch, ist aber z.B. beim Neustart von Fhem sinnvoll. Beim Vorwerk läuft diese Funktion (noch) ins Leere.&lt;br /&gt;
*resume&lt;br /&gt;
**Setzt Reinigung fort&lt;br /&gt;
&lt;br /&gt;
*schedule&lt;br /&gt;
**Die möglichen Argumente sind &#039;on&#039; und &#039;off&#039; zum An- und Abschalten der Zeitsteuerung.&lt;br /&gt;
*sendToBase&lt;br /&gt;
**Fährt zur Basis zurück&lt;br /&gt;
&lt;br /&gt;
*setBoundaries&lt;br /&gt;
**https://developers.neatorobotics.com/api/robot-remote-protocol/maps&lt;br /&gt;
*setRobot&lt;br /&gt;
**Legt den zu steuernden Roboter fest&lt;br /&gt;
*startCleaning&lt;br /&gt;
** Startet die Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*startSpot&lt;br /&gt;
**Startet die Spot-Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*statusRequest&lt;br /&gt;
*stop&lt;br /&gt;
** stoppt die Reinigung&lt;br /&gt;
*syncRobots&lt;br /&gt;
**Synchronisiert die Roboterdaten. Das ist nützlich, wenn man mehrere Geräte unter einem Neato- bzw. Vorwerk-Account verwaltet und dort Geräte entfernt oder hinzugefügt hat.&lt;br /&gt;
*stopCleaning&lt;br /&gt;
** Stop cleaning the house.&lt;br /&gt;
&lt;br /&gt;
=== GET ===&lt;br /&gt;
*batteryPercent&lt;br /&gt;
&lt;br /&gt;
=== readings ===&lt;br /&gt;
&lt;br /&gt;
*dockHasBeenSeen&lt;br /&gt;
**stellt sich erst eine Weile nach Beginn einer Reinigung auf &amp;quot;1&amp;quot;.Und nur in diesem Zustand klappt das Zurücksenden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== MAPS ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg548498.html#msg548498&lt;br /&gt;
http(s)://&amp;lt;ip&amp;gt;:&amp;lt;port&amp;gt;/fhem/BOTVAC/&amp;lt;device&amp;gt;/map&lt;br /&gt;
&lt;br /&gt;
Map per telegram versenden: &lt;br /&gt;
set &amp;lt;TelegramBot&amp;gt; cmdSend {ReadingsVal(&#039;&amp;lt;BOTVAC&amp;gt;&#039;, &#039;.map_cache&#039;, &#039;&#039;)}&lt;br /&gt;
&lt;br /&gt;
== Grundkonfiguration ==&lt;br /&gt;
&lt;br /&gt;
=== Definition in FHEM ===&lt;br /&gt;
=== Icons ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg539375.html#msg539375&lt;br /&gt;
&lt;br /&gt;
die Datei reinkopieren, dann &amp;quot;set WEB rereadicons&amp;quot; und anschließend normal als Icon auswählen.&lt;br /&gt;
Inzwischen ist das Icon wohl im SVN eingecheckt.&lt;br /&gt;
&lt;br /&gt;
== Probleme ==&lt;br /&gt;
=== falls er Sauger mit der App neu konfiguriert werden musste ===&lt;br /&gt;
Du hast nach der Neuinstallation der App auch deinen VR/NEATO neu registriert. Fhem lädt aber noch die Daten aus der alten Registrierung. &lt;br /&gt;
Wähle mal im Gerät unter Set &#039;syncRobots&#039; aus. Wenn es danach bei Set auch eine Funktion &#039;setRobot&#039; gibt, hast du wirklich  den VR neu registriert. Dann einfach über dieses Set den VR wechseln. &lt;br /&gt;
Ansonsten bleibt noch die Registrierungsschlüssel im Fhem-Gerät zu löschen. Das geht über die Kommandozeile in Fhem:&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; accessToken&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; secretKey&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28153</id>
		<title>BOTVAC</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28153"/>
		<updated>2018-10-23T20:17:50Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Attribute */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Dieses Modul dient zur Steuerung eines NEATO oder VORWERK Saugroboters&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=BOTVAC&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=noch nicht eingcheckt&lt;br /&gt;
|ModOwner=vuffiraa&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[BOTVAC]] ist ein Modul mit welchem VORWERK und NEATO Saugroboter gesteuert werden können. &lt;br /&gt;
&lt;br /&gt;
== Allgemeines ==&lt;br /&gt;
=== Definition ===&lt;br /&gt;
 define &amp;lt;name&amp;gt; BOTVAC &amp;lt;email&amp;gt; [&amp;lt;vendor&amp;gt;] [&amp;lt;poll-interval&amp;gt;]&lt;br /&gt;
 set &amp;lt;name&amp;gt; password &amp;lt;password&amp;gt;&lt;br /&gt;
=== Funktionsumfang  ===&lt;br /&gt;
Aktuell ist folgenden Funktionsumfang vorhanden:&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
*actionInterval&lt;br /&gt;
**Poll-Interval für den aktiven Betrieb (Busy/Paused)&lt;br /&gt;
*cleaningMode&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: eco,turbo&lt;br /&gt;
*cleaningModifier&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: normal,double&lt;br /&gt;
*cleaningNavigationMode&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: normal,extra care,deep&lt;br /&gt;
**Note that navigationMode can only be set to deep, if cleaningMode is turbo&lt;br /&gt;
*cleaningSpotWidth&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: 100,200,300,400&lt;br /&gt;
*cleaningSpotHeight&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: 100,200,300,400&lt;br /&gt;
Es muss kein Quadrat sein, der Bereich ist von 100 - 400 cm, in 1 cm Schritten einstellbar. Rechtecke von z.B. 152x244 sind also möglich. &lt;br /&gt;
&lt;br /&gt;
Die Attribute werden zum Saugertyp passend angeboten. Dadurch sind die Startbefehle für die Reinigung parameterlos. Einzige Ausnahme beim D7 ist set &amp;lt;name&amp;gt; startCleaning &amp;lt;house|map&amp;gt;&lt;br /&gt;
Wenn die cleaning-Attribute nicht definiert sind, werden die Werte der letzten Reinigung aus dem Reading genommen.&lt;br /&gt;
&lt;br /&gt;
=== SET === &lt;br /&gt;
*dismissCurrentAlert&lt;br /&gt;
**Zurücksetzen einer anstehenden Warnmeldung&lt;br /&gt;
*findMe&lt;br /&gt;
**The robot will emit a sound and blink the LEDs when it receives the findMe command.&lt;br /&gt;
*manualCleaningMode&lt;br /&gt;
*nextCleaningMode&lt;br /&gt;
*nextCleaningNavigationMode&lt;br /&gt;
*nextCleaningSpotHeight&lt;br /&gt;
*nextCleaningSpotWidth&lt;br /&gt;
*password&lt;br /&gt;
*pause&lt;br /&gt;
**pausiert die Reinigung&lt;br /&gt;
*pauseToBase&lt;br /&gt;
**Beendet eine Reinigung und fährt zur Basis zurück&lt;br /&gt;
*reloadMaps&lt;br /&gt;
**Lädt die letzte Karte vom Neato-Server, die erstellt wurde. Das passiert normalerweise automatisch, ist aber z.B. beim Neustart von Fhem sinnvoll. Beim Vorwerk läuft diese Funktion (noch) ins Leere.&lt;br /&gt;
*resume&lt;br /&gt;
**Setzt Reinigung fort&lt;br /&gt;
&lt;br /&gt;
*schedule&lt;br /&gt;
**Die möglichen Argumente sind &#039;on&#039; und &#039;off&#039; zum An- und Abschalten der Zeitsteuerung.&lt;br /&gt;
*sendToBase&lt;br /&gt;
**Fährt zur Basis zurück&lt;br /&gt;
&lt;br /&gt;
*setBoundaries&lt;br /&gt;
**https://developers.neatorobotics.com/api/robot-remote-protocol/maps&lt;br /&gt;
*setRobot&lt;br /&gt;
**Legt den zu steuernden Roboter fest&lt;br /&gt;
*startCleaning&lt;br /&gt;
** Startet die Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*startSpot&lt;br /&gt;
**Startet die Spot-Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*statusRequest&lt;br /&gt;
*stop&lt;br /&gt;
** stoppt die Reinigung&lt;br /&gt;
*syncRobots&lt;br /&gt;
**Synchronisiert die Roboterdaten. Das ist nützlich, wenn man mehrere Geräte unter einem Neato- bzw. Vorwerk-Account verwaltet und dort Geräte entfernt oder hinzugefügt hat.&lt;br /&gt;
*stopCleaning&lt;br /&gt;
** Stop cleaning the house.&lt;br /&gt;
&lt;br /&gt;
=== GET ===&lt;br /&gt;
*batteryPercent&lt;br /&gt;
&lt;br /&gt;
=== readings ===&lt;br /&gt;
&lt;br /&gt;
*dockHasBeenSeen&lt;br /&gt;
**stellt sich erst eine Weile nach Beginn einer Reinigung auf &amp;quot;1&amp;quot;.Und nur in diesem Zustand klappt das Zurücksenden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== MAPS ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg548498.html#msg548498&lt;br /&gt;
http(s)://&amp;lt;ip&amp;gt;:&amp;lt;port&amp;gt;/fhem/BOTVAC/&amp;lt;device&amp;gt;/map&lt;br /&gt;
&lt;br /&gt;
Map per telegram versenden: &lt;br /&gt;
set &amp;lt;TelegramBot&amp;gt; cmdSend {ReadingsVal(&#039;&amp;lt;BOTVAC&amp;gt;&#039;, &#039;.map_cache&#039;, &#039;&#039;)}&lt;br /&gt;
&lt;br /&gt;
== Grundkonfiguration ==&lt;br /&gt;
&lt;br /&gt;
=== Definition in FHEM ===&lt;br /&gt;
=== Icons ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg539375.html#msg539375&lt;br /&gt;
&lt;br /&gt;
die Datei reinkopieren, dann &amp;quot;set WEB rereadicons&amp;quot; und anschließend normal als Icon auswählen.&lt;br /&gt;
Inzwischen ist das Icon wohl im SVN eingecheckt.&lt;br /&gt;
&lt;br /&gt;
== FHEM Device ==&lt;br /&gt;
siehe https://mwinkler.jimdo.com/eigene-anwendungen/winconnect/fhem-device/&lt;br /&gt;
&lt;br /&gt;
== Probleme ==&lt;br /&gt;
=== falls er Sauger mit der App neu konfiguriert werden musste ===&lt;br /&gt;
Du hast nach der Neuinstallation der App auch deinen VR/NEATO neu registriert. Fhem lädt aber noch die Daten aus der alten Registrierung. &lt;br /&gt;
Wähle mal im Gerät unter Set &#039;syncRobots&#039; aus. Wenn es danach bei Set auch eine Funktion &#039;setRobot&#039; gibt, hast du wirklich  den VR neu registriert. Dann einfach über dieses Set den VR wechseln. &lt;br /&gt;
Ansonsten bleibt noch die Registrierungsschlüssel im Fhem-Gerät zu löschen. Das geht über die Kommandozeile in Fhem:&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; accessToken&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; secretKey&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28152</id>
		<title>BOTVAC</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28152"/>
		<updated>2018-10-23T20:09:32Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Attribute */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Dieses Modul dient zur Steuerung eines NEATO oder VORWERK Saugroboters&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=BOTVAC&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=noch nicht eingcheckt&lt;br /&gt;
|ModOwner=vuffiraa&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[BOTVAC]] ist ein Modul mit welchem VORWERK und NEATO Saugroboter gesteuert werden können. &lt;br /&gt;
&lt;br /&gt;
== Allgemeines ==&lt;br /&gt;
=== Definition ===&lt;br /&gt;
 define &amp;lt;name&amp;gt; BOTVAC &amp;lt;email&amp;gt; [&amp;lt;vendor&amp;gt;] [&amp;lt;poll-interval&amp;gt;]&lt;br /&gt;
 set &amp;lt;name&amp;gt; password &amp;lt;password&amp;gt;&lt;br /&gt;
=== Funktionsumfang  ===&lt;br /&gt;
Aktuell ist folgenden Funktionsumfang vorhanden:&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
*actionInterval&lt;br /&gt;
**Poll-Interval für den aktiven Betrieb (Busy/Paused)&lt;br /&gt;
*cleaningMode&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: eco,turbo&lt;br /&gt;
*cleaningModifier&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: normal,double&lt;br /&gt;
*cleaningNavigationMode&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: normal,extra care,deep&lt;br /&gt;
*cleaningSpotWidth&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: 100,200,300,400&lt;br /&gt;
*cleaningSpotHeight&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: 100,200,300,400&lt;br /&gt;
Es muss kein Quadrat sein, der Bereich ist von 100 - 400 cm, in 1 cm Schritten einstellbar. Rechtecke von z.B. 152x244 sind also möglich. &lt;br /&gt;
&lt;br /&gt;
Die Attribute werden zum Saugertyp passend angeboten. Dadurch sind die Startbefehle für die Reinigung parameterlos. Einzige Ausnahme beim D7 ist set &amp;lt;name&amp;gt; startCleaning &amp;lt;house|map&amp;gt;&lt;br /&gt;
Wenn die cleaning-Attribute nicht definiert sind, werden die Werte der letzten Reinigung aus dem Reading genommen.&lt;br /&gt;
&lt;br /&gt;
=== SET === &lt;br /&gt;
*dismissCurrentAlert&lt;br /&gt;
**Zurücksetzen einer anstehenden Warnmeldung&lt;br /&gt;
*findMe&lt;br /&gt;
**The robot will emit a sound and blink the LEDs when it receives the findMe command.&lt;br /&gt;
*manualCleaningMode&lt;br /&gt;
*nextCleaningMode&lt;br /&gt;
*nextCleaningNavigationMode&lt;br /&gt;
*nextCleaningSpotHeight&lt;br /&gt;
*nextCleaningSpotWidth&lt;br /&gt;
*password&lt;br /&gt;
*pause&lt;br /&gt;
**pausiert die Reinigung&lt;br /&gt;
*pauseToBase&lt;br /&gt;
**Beendet eine Reinigung und fährt zur Basis zurück&lt;br /&gt;
*reloadMaps&lt;br /&gt;
**Lädt die letzte Karte vom Neato-Server, die erstellt wurde. Das passiert normalerweise automatisch, ist aber z.B. beim Neustart von Fhem sinnvoll. Beim Vorwerk läuft diese Funktion (noch) ins Leere.&lt;br /&gt;
*resume&lt;br /&gt;
**Setzt Reinigung fort&lt;br /&gt;
&lt;br /&gt;
*schedule&lt;br /&gt;
**Die möglichen Argumente sind &#039;on&#039; und &#039;off&#039; zum An- und Abschalten der Zeitsteuerung.&lt;br /&gt;
*sendToBase&lt;br /&gt;
**Fährt zur Basis zurück&lt;br /&gt;
&lt;br /&gt;
*setBoundaries&lt;br /&gt;
**https://developers.neatorobotics.com/api/robot-remote-protocol/maps&lt;br /&gt;
*setRobot&lt;br /&gt;
**Legt den zu steuernden Roboter fest&lt;br /&gt;
*startCleaning&lt;br /&gt;
** Startet die Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*startSpot&lt;br /&gt;
**Startet die Spot-Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*statusRequest&lt;br /&gt;
*stop&lt;br /&gt;
** stoppt die Reinigung&lt;br /&gt;
*syncRobots&lt;br /&gt;
**Synchronisiert die Roboterdaten. Das ist nützlich, wenn man mehrere Geräte unter einem Neato- bzw. Vorwerk-Account verwaltet und dort Geräte entfernt oder hinzugefügt hat.&lt;br /&gt;
*stopCleaning&lt;br /&gt;
** Stop cleaning the house.&lt;br /&gt;
&lt;br /&gt;
=== GET ===&lt;br /&gt;
*batteryPercent&lt;br /&gt;
&lt;br /&gt;
=== readings ===&lt;br /&gt;
&lt;br /&gt;
*dockHasBeenSeen&lt;br /&gt;
**stellt sich erst eine Weile nach Beginn einer Reinigung auf &amp;quot;1&amp;quot;.Und nur in diesem Zustand klappt das Zurücksenden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== MAPS ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg548498.html#msg548498&lt;br /&gt;
http(s)://&amp;lt;ip&amp;gt;:&amp;lt;port&amp;gt;/fhem/BOTVAC/&amp;lt;device&amp;gt;/map&lt;br /&gt;
&lt;br /&gt;
Map per telegram versenden: &lt;br /&gt;
set &amp;lt;TelegramBot&amp;gt; cmdSend {ReadingsVal(&#039;&amp;lt;BOTVAC&amp;gt;&#039;, &#039;.map_cache&#039;, &#039;&#039;)}&lt;br /&gt;
&lt;br /&gt;
== Grundkonfiguration ==&lt;br /&gt;
&lt;br /&gt;
=== Definition in FHEM ===&lt;br /&gt;
=== Icons ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg539375.html#msg539375&lt;br /&gt;
&lt;br /&gt;
die Datei reinkopieren, dann &amp;quot;set WEB rereadicons&amp;quot; und anschließend normal als Icon auswählen.&lt;br /&gt;
Inzwischen ist das Icon wohl im SVN eingecheckt.&lt;br /&gt;
&lt;br /&gt;
== FHEM Device ==&lt;br /&gt;
siehe https://mwinkler.jimdo.com/eigene-anwendungen/winconnect/fhem-device/&lt;br /&gt;
&lt;br /&gt;
== Probleme ==&lt;br /&gt;
=== falls er Sauger mit der App neu konfiguriert werden musste ===&lt;br /&gt;
Du hast nach der Neuinstallation der App auch deinen VR/NEATO neu registriert. Fhem lädt aber noch die Daten aus der alten Registrierung. &lt;br /&gt;
Wähle mal im Gerät unter Set &#039;syncRobots&#039; aus. Wenn es danach bei Set auch eine Funktion &#039;setRobot&#039; gibt, hast du wirklich  den VR neu registriert. Dann einfach über dieses Set den VR wechseln. &lt;br /&gt;
Ansonsten bleibt noch die Registrierungsschlüssel im Fhem-Gerät zu löschen. Das geht über die Kommandozeile in Fhem:&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; accessToken&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; secretKey&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28151</id>
		<title>BOTVAC</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28151"/>
		<updated>2018-10-23T20:06:04Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Attribute */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Dieses Modul dient zur Steuerung eines NEATO oder VORWERK Saugroboters&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=BOTVAC&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=noch nicht eingcheckt&lt;br /&gt;
|ModOwner=vuffiraa&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[BOTVAC]] ist ein Modul mit welchem VORWERK und NEATO Saugroboter gesteuert werden können. &lt;br /&gt;
&lt;br /&gt;
== Allgemeines ==&lt;br /&gt;
=== Definition ===&lt;br /&gt;
 define &amp;lt;name&amp;gt; BOTVAC &amp;lt;email&amp;gt; [&amp;lt;vendor&amp;gt;] [&amp;lt;poll-interval&amp;gt;]&lt;br /&gt;
 set &amp;lt;name&amp;gt; password &amp;lt;password&amp;gt;&lt;br /&gt;
=== Funktionsumfang  ===&lt;br /&gt;
Aktuell ist folgenden Funktionsumfang vorhanden:&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
*actionInterval&lt;br /&gt;
**Poll-Interval für den aktiven Betrieb (Busy/Paused)&lt;br /&gt;
*cleaningMode&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: eco,turbo&lt;br /&gt;
*cleaningModifier&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: normal,double&lt;br /&gt;
*cleaningNavigationMode&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: normal,extra care,deep&lt;br /&gt;
*cleaningSpotWidth&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: 100,200,300,400&lt;br /&gt;
*cleaningSpotHeight&lt;br /&gt;
**wird bei Starten der nächsten Reinigung verwendet: 100,200,300,400&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die Attribute werden zum Saugertyp passend angeboten. Dadurch sind die Startbefehle für die Reinigung parameterlos. Einzige Ausnahme beim D7 ist set &amp;lt;name&amp;gt; startCleaning &amp;lt;house|map&amp;gt;&lt;br /&gt;
Wenn die cleaning-Attribute nicht definiert sind, werden die Werte der letzten Reinigung aus dem Reading genommen.&lt;br /&gt;
&lt;br /&gt;
=== SET === &lt;br /&gt;
*dismissCurrentAlert&lt;br /&gt;
**Zurücksetzen einer anstehenden Warnmeldung&lt;br /&gt;
*findMe&lt;br /&gt;
**The robot will emit a sound and blink the LEDs when it receives the findMe command.&lt;br /&gt;
*manualCleaningMode&lt;br /&gt;
*nextCleaningMode&lt;br /&gt;
*nextCleaningNavigationMode&lt;br /&gt;
*nextCleaningSpotHeight&lt;br /&gt;
*nextCleaningSpotWidth&lt;br /&gt;
*password&lt;br /&gt;
*pause&lt;br /&gt;
**pausiert die Reinigung&lt;br /&gt;
*pauseToBase&lt;br /&gt;
**Beendet eine Reinigung und fährt zur Basis zurück&lt;br /&gt;
*reloadMaps&lt;br /&gt;
**Lädt die letzte Karte vom Neato-Server, die erstellt wurde. Das passiert normalerweise automatisch, ist aber z.B. beim Neustart von Fhem sinnvoll. Beim Vorwerk läuft diese Funktion (noch) ins Leere.&lt;br /&gt;
*resume&lt;br /&gt;
**Setzt Reinigung fort&lt;br /&gt;
&lt;br /&gt;
*schedule&lt;br /&gt;
**Die möglichen Argumente sind &#039;on&#039; und &#039;off&#039; zum An- und Abschalten der Zeitsteuerung.&lt;br /&gt;
*sendToBase&lt;br /&gt;
**Fährt zur Basis zurück&lt;br /&gt;
&lt;br /&gt;
*setBoundaries&lt;br /&gt;
**https://developers.neatorobotics.com/api/robot-remote-protocol/maps&lt;br /&gt;
*setRobot&lt;br /&gt;
**Legt den zu steuernden Roboter fest&lt;br /&gt;
*startCleaning&lt;br /&gt;
** Startet die Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*startSpot&lt;br /&gt;
**Startet die Spot-Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*statusRequest&lt;br /&gt;
*stop&lt;br /&gt;
** stoppt die Reinigung&lt;br /&gt;
*syncRobots&lt;br /&gt;
**Synchronisiert die Roboterdaten. Das ist nützlich, wenn man mehrere Geräte unter einem Neato- bzw. Vorwerk-Account verwaltet und dort Geräte entfernt oder hinzugefügt hat.&lt;br /&gt;
*stopCleaning&lt;br /&gt;
** Stop cleaning the house.&lt;br /&gt;
&lt;br /&gt;
=== GET ===&lt;br /&gt;
*batteryPercent&lt;br /&gt;
&lt;br /&gt;
=== readings ===&lt;br /&gt;
&lt;br /&gt;
*dockHasBeenSeen&lt;br /&gt;
**stellt sich erst eine Weile nach Beginn einer Reinigung auf &amp;quot;1&amp;quot;.Und nur in diesem Zustand klappt das Zurücksenden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== MAPS ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg548498.html#msg548498&lt;br /&gt;
http(s)://&amp;lt;ip&amp;gt;:&amp;lt;port&amp;gt;/fhem/BOTVAC/&amp;lt;device&amp;gt;/map&lt;br /&gt;
&lt;br /&gt;
Map per telegram versenden: &lt;br /&gt;
set &amp;lt;TelegramBot&amp;gt; cmdSend {ReadingsVal(&#039;&amp;lt;BOTVAC&amp;gt;&#039;, &#039;.map_cache&#039;, &#039;&#039;)}&lt;br /&gt;
&lt;br /&gt;
== Grundkonfiguration ==&lt;br /&gt;
&lt;br /&gt;
=== Definition in FHEM ===&lt;br /&gt;
=== Icons ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg539375.html#msg539375&lt;br /&gt;
&lt;br /&gt;
die Datei reinkopieren, dann &amp;quot;set WEB rereadicons&amp;quot; und anschließend normal als Icon auswählen.&lt;br /&gt;
Inzwischen ist das Icon wohl im SVN eingecheckt.&lt;br /&gt;
&lt;br /&gt;
== FHEM Device ==&lt;br /&gt;
siehe https://mwinkler.jimdo.com/eigene-anwendungen/winconnect/fhem-device/&lt;br /&gt;
&lt;br /&gt;
== Probleme ==&lt;br /&gt;
=== falls er Sauger mit der App neu konfiguriert werden musste ===&lt;br /&gt;
Du hast nach der Neuinstallation der App auch deinen VR/NEATO neu registriert. Fhem lädt aber noch die Daten aus der alten Registrierung. &lt;br /&gt;
Wähle mal im Gerät unter Set &#039;syncRobots&#039; aus. Wenn es danach bei Set auch eine Funktion &#039;setRobot&#039; gibt, hast du wirklich  den VR neu registriert. Dann einfach über dieses Set den VR wechseln. &lt;br /&gt;
Ansonsten bleibt noch die Registrierungsschlüssel im Fhem-Gerät zu löschen. Das geht über die Kommandozeile in Fhem:&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; accessToken&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; secretKey&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28150</id>
		<title>BOTVAC</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28150"/>
		<updated>2018-10-23T20:04:30Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Allgemeines */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Dieses Modul dient zur Steuerung eines NEATO oder VORWERK Saugroboters&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=BOTVAC&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=noch nicht eingcheckt&lt;br /&gt;
|ModOwner=vuffiraa&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[BOTVAC]] ist ein Modul mit welchem VORWERK und NEATO Saugroboter gesteuert werden können. &lt;br /&gt;
&lt;br /&gt;
== Allgemeines ==&lt;br /&gt;
=== Definition ===&lt;br /&gt;
 define &amp;lt;name&amp;gt; BOTVAC &amp;lt;email&amp;gt; [&amp;lt;vendor&amp;gt;] [&amp;lt;poll-interval&amp;gt;]&lt;br /&gt;
 set &amp;lt;name&amp;gt; password &amp;lt;password&amp;gt;&lt;br /&gt;
=== Funktionsumfang  ===&lt;br /&gt;
Aktuell ist folgenden Funktionsumfang vorhanden:&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== SET === &lt;br /&gt;
*dismissCurrentAlert&lt;br /&gt;
**Zurücksetzen einer anstehenden Warnmeldung&lt;br /&gt;
*findMe&lt;br /&gt;
**The robot will emit a sound and blink the LEDs when it receives the findMe command.&lt;br /&gt;
*manualCleaningMode&lt;br /&gt;
*nextCleaningMode&lt;br /&gt;
*nextCleaningNavigationMode&lt;br /&gt;
*nextCleaningSpotHeight&lt;br /&gt;
*nextCleaningSpotWidth&lt;br /&gt;
*password&lt;br /&gt;
*pause&lt;br /&gt;
**pausiert die Reinigung&lt;br /&gt;
*pauseToBase&lt;br /&gt;
**Beendet eine Reinigung und fährt zur Basis zurück&lt;br /&gt;
*reloadMaps&lt;br /&gt;
**Lädt die letzte Karte vom Neato-Server, die erstellt wurde. Das passiert normalerweise automatisch, ist aber z.B. beim Neustart von Fhem sinnvoll. Beim Vorwerk läuft diese Funktion (noch) ins Leere.&lt;br /&gt;
*resume&lt;br /&gt;
**Setzt Reinigung fort&lt;br /&gt;
&lt;br /&gt;
*schedule&lt;br /&gt;
**Die möglichen Argumente sind &#039;on&#039; und &#039;off&#039; zum An- und Abschalten der Zeitsteuerung.&lt;br /&gt;
*sendToBase&lt;br /&gt;
**Fährt zur Basis zurück&lt;br /&gt;
&lt;br /&gt;
*setBoundaries&lt;br /&gt;
**https://developers.neatorobotics.com/api/robot-remote-protocol/maps&lt;br /&gt;
*setRobot&lt;br /&gt;
**Legt den zu steuernden Roboter fest&lt;br /&gt;
*startCleaning&lt;br /&gt;
** Startet die Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*startSpot&lt;br /&gt;
**Startet die Spot-Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*statusRequest&lt;br /&gt;
*stop&lt;br /&gt;
** stoppt die Reinigung&lt;br /&gt;
*syncRobots&lt;br /&gt;
**Synchronisiert die Roboterdaten. Das ist nützlich, wenn man mehrere Geräte unter einem Neato- bzw. Vorwerk-Account verwaltet und dort Geräte entfernt oder hinzugefügt hat.&lt;br /&gt;
*stopCleaning&lt;br /&gt;
** Stop cleaning the house.&lt;br /&gt;
&lt;br /&gt;
=== GET ===&lt;br /&gt;
*batteryPercent&lt;br /&gt;
&lt;br /&gt;
=== readings ===&lt;br /&gt;
&lt;br /&gt;
*dockHasBeenSeen&lt;br /&gt;
**stellt sich erst eine Weile nach Beginn einer Reinigung auf &amp;quot;1&amp;quot;.Und nur in diesem Zustand klappt das Zurücksenden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== MAPS ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg548498.html#msg548498&lt;br /&gt;
http(s)://&amp;lt;ip&amp;gt;:&amp;lt;port&amp;gt;/fhem/BOTVAC/&amp;lt;device&amp;gt;/map&lt;br /&gt;
&lt;br /&gt;
Map per telegram versenden: &lt;br /&gt;
set &amp;lt;TelegramBot&amp;gt; cmdSend {ReadingsVal(&#039;&amp;lt;BOTVAC&amp;gt;&#039;, &#039;.map_cache&#039;, &#039;&#039;)}&lt;br /&gt;
&lt;br /&gt;
== Grundkonfiguration ==&lt;br /&gt;
&lt;br /&gt;
=== Definition in FHEM ===&lt;br /&gt;
=== Icons ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg539375.html#msg539375&lt;br /&gt;
&lt;br /&gt;
die Datei reinkopieren, dann &amp;quot;set WEB rereadicons&amp;quot; und anschließend normal als Icon auswählen.&lt;br /&gt;
Inzwischen ist das Icon wohl im SVN eingecheckt.&lt;br /&gt;
&lt;br /&gt;
== FHEM Device ==&lt;br /&gt;
siehe https://mwinkler.jimdo.com/eigene-anwendungen/winconnect/fhem-device/&lt;br /&gt;
&lt;br /&gt;
== Probleme ==&lt;br /&gt;
=== falls er Sauger mit der App neu konfiguriert werden musste ===&lt;br /&gt;
Du hast nach der Neuinstallation der App auch deinen VR/NEATO neu registriert. Fhem lädt aber noch die Daten aus der alten Registrierung. &lt;br /&gt;
Wähle mal im Gerät unter Set &#039;syncRobots&#039; aus. Wenn es danach bei Set auch eine Funktion &#039;setRobot&#039; gibt, hast du wirklich  den VR neu registriert. Dann einfach über dieses Set den VR wechseln. &lt;br /&gt;
Ansonsten bleibt noch die Registrierungsschlüssel im Fhem-Gerät zu löschen. Das geht über die Kommandozeile in Fhem:&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; accessToken&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; secretKey&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28149</id>
		<title>BOTVAC</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28149"/>
		<updated>2018-10-23T20:04:01Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* SET */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Dieses Modul dient zur Steuerung eines NEATO oder VORWERK Saugroboters&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=BOTVAC&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=noch nicht eingcheckt&lt;br /&gt;
|ModOwner=vuffiraa&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[BOTVAC]] ist ein Modul mit welchem VORWERK und NEATO Saugroboter gesteuert werden können. &lt;br /&gt;
&lt;br /&gt;
== Allgemeines ==&lt;br /&gt;
&lt;br /&gt;
=== Funktionsumfang  ===&lt;br /&gt;
Aktuell ist folgenden Funktionsumfang vorhanden:&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== SET === &lt;br /&gt;
*dismissCurrentAlert&lt;br /&gt;
**Zurücksetzen einer anstehenden Warnmeldung&lt;br /&gt;
*findMe&lt;br /&gt;
**The robot will emit a sound and blink the LEDs when it receives the findMe command.&lt;br /&gt;
*manualCleaningMode&lt;br /&gt;
*nextCleaningMode&lt;br /&gt;
*nextCleaningNavigationMode&lt;br /&gt;
*nextCleaningSpotHeight&lt;br /&gt;
*nextCleaningSpotWidth&lt;br /&gt;
*password&lt;br /&gt;
*pause&lt;br /&gt;
**pausiert die Reinigung&lt;br /&gt;
*pauseToBase&lt;br /&gt;
**Beendet eine Reinigung und fährt zur Basis zurück&lt;br /&gt;
*reloadMaps&lt;br /&gt;
**Lädt die letzte Karte vom Neato-Server, die erstellt wurde. Das passiert normalerweise automatisch, ist aber z.B. beim Neustart von Fhem sinnvoll. Beim Vorwerk läuft diese Funktion (noch) ins Leere.&lt;br /&gt;
*resume&lt;br /&gt;
**Setzt Reinigung fort&lt;br /&gt;
&lt;br /&gt;
*schedule&lt;br /&gt;
**Die möglichen Argumente sind &#039;on&#039; und &#039;off&#039; zum An- und Abschalten der Zeitsteuerung.&lt;br /&gt;
*sendToBase&lt;br /&gt;
**Fährt zur Basis zurück&lt;br /&gt;
&lt;br /&gt;
*setBoundaries&lt;br /&gt;
**https://developers.neatorobotics.com/api/robot-remote-protocol/maps&lt;br /&gt;
*setRobot&lt;br /&gt;
**Legt den zu steuernden Roboter fest&lt;br /&gt;
*startCleaning&lt;br /&gt;
** Startet die Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*startSpot&lt;br /&gt;
**Startet die Spot-Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*statusRequest&lt;br /&gt;
*stop&lt;br /&gt;
** stoppt die Reinigung&lt;br /&gt;
*syncRobots&lt;br /&gt;
**Synchronisiert die Roboterdaten. Das ist nützlich, wenn man mehrere Geräte unter einem Neato- bzw. Vorwerk-Account verwaltet und dort Geräte entfernt oder hinzugefügt hat.&lt;br /&gt;
*stopCleaning&lt;br /&gt;
** Stop cleaning the house.&lt;br /&gt;
&lt;br /&gt;
=== GET ===&lt;br /&gt;
*batteryPercent&lt;br /&gt;
&lt;br /&gt;
=== readings ===&lt;br /&gt;
&lt;br /&gt;
*dockHasBeenSeen&lt;br /&gt;
**stellt sich erst eine Weile nach Beginn einer Reinigung auf &amp;quot;1&amp;quot;.Und nur in diesem Zustand klappt das Zurücksenden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== MAPS ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg548498.html#msg548498&lt;br /&gt;
http(s)://&amp;lt;ip&amp;gt;:&amp;lt;port&amp;gt;/fhem/BOTVAC/&amp;lt;device&amp;gt;/map&lt;br /&gt;
&lt;br /&gt;
Map per telegram versenden: &lt;br /&gt;
set &amp;lt;TelegramBot&amp;gt; cmdSend {ReadingsVal(&#039;&amp;lt;BOTVAC&amp;gt;&#039;, &#039;.map_cache&#039;, &#039;&#039;)}&lt;br /&gt;
&lt;br /&gt;
== Grundkonfiguration ==&lt;br /&gt;
&lt;br /&gt;
=== Definition in FHEM ===&lt;br /&gt;
=== Icons ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg539375.html#msg539375&lt;br /&gt;
&lt;br /&gt;
die Datei reinkopieren, dann &amp;quot;set WEB rereadicons&amp;quot; und anschließend normal als Icon auswählen.&lt;br /&gt;
Inzwischen ist das Icon wohl im SVN eingecheckt.&lt;br /&gt;
&lt;br /&gt;
== FHEM Device ==&lt;br /&gt;
siehe https://mwinkler.jimdo.com/eigene-anwendungen/winconnect/fhem-device/&lt;br /&gt;
&lt;br /&gt;
== Probleme ==&lt;br /&gt;
=== falls er Sauger mit der App neu konfiguriert werden musste ===&lt;br /&gt;
Du hast nach der Neuinstallation der App auch deinen VR/NEATO neu registriert. Fhem lädt aber noch die Daten aus der alten Registrierung. &lt;br /&gt;
Wähle mal im Gerät unter Set &#039;syncRobots&#039; aus. Wenn es danach bei Set auch eine Funktion &#039;setRobot&#039; gibt, hast du wirklich  den VR neu registriert. Dann einfach über dieses Set den VR wechseln. &lt;br /&gt;
Ansonsten bleibt noch die Registrierungsschlüssel im Fhem-Gerät zu löschen. Das geht über die Kommandozeile in Fhem:&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; accessToken&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; secretKey&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28148</id>
		<title>BOTVAC</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28148"/>
		<updated>2018-10-23T20:00:57Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* MAPS */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Dieses Modul dient zur Steuerung eines NEATO oder VORWERK Saugroboters&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=BOTVAC&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=noch nicht eingcheckt&lt;br /&gt;
|ModOwner=vuffiraa&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[BOTVAC]] ist ein Modul mit welchem VORWERK und NEATO Saugroboter gesteuert werden können. &lt;br /&gt;
&lt;br /&gt;
== Allgemeines ==&lt;br /&gt;
&lt;br /&gt;
=== Funktionsumfang  ===&lt;br /&gt;
Aktuell ist folgenden Funktionsumfang vorhanden:&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== SET === &lt;br /&gt;
*findMe&lt;br /&gt;
**The robot will emit a sound and blink the LEDs when it receives the findMe command.&lt;br /&gt;
*manualCleaningMode&lt;br /&gt;
*nextCleaningMode&lt;br /&gt;
*nextCleaningNavigationMode&lt;br /&gt;
*nextCleaningSpotHeight&lt;br /&gt;
*nextCleaningSpotWidth&lt;br /&gt;
*password&lt;br /&gt;
*pause&lt;br /&gt;
**pausiert die Reinigung&lt;br /&gt;
*pauseToBase&lt;br /&gt;
**Beendet eine Reinigung und fährt zur Basis zurück&lt;br /&gt;
*reloadMaps&lt;br /&gt;
**Lädt die letzte Karte vom Neato-Server, die erstellt wurde. Das passiert normalerweise automatisch, ist aber z.B. beim Neustart von Fhem sinnvoll. Beim Vorwerk läuft diese Funktion (noch) ins Leere.&lt;br /&gt;
*resume&lt;br /&gt;
**Setzt Reinigung fort&lt;br /&gt;
&lt;br /&gt;
*schedule&lt;br /&gt;
**Die möglichen Argumente sind &#039;on&#039; und &#039;off&#039; zum An- und Abschalten der Zeitsteuerung.&lt;br /&gt;
*sendToBase&lt;br /&gt;
**Fährt zur Basis zurück&lt;br /&gt;
&lt;br /&gt;
*setBoundaries&lt;br /&gt;
*setRobot&lt;br /&gt;
**Legt den zu steuernden Roboter fest&lt;br /&gt;
*startCleaning&lt;br /&gt;
** Startet die Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*startSpot&lt;br /&gt;
**Startet die Spot-Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*statusRequest&lt;br /&gt;
*stop&lt;br /&gt;
** stoppt die Reinigung&lt;br /&gt;
*syncRobots&lt;br /&gt;
**Synchronisiert die Roboterdaten. Das ist nützlich, wenn man mehrere Geräte unter einem Neato- bzw. Vorwerk-Account verwaltet und dort Geräte entfernt oder hinzugefügt hat.&lt;br /&gt;
*stopCleaning&lt;br /&gt;
** Stop cleaning the house.&lt;br /&gt;
&lt;br /&gt;
=== GET ===&lt;br /&gt;
*batteryPercent&lt;br /&gt;
&lt;br /&gt;
=== readings ===&lt;br /&gt;
&lt;br /&gt;
*dockHasBeenSeen&lt;br /&gt;
**stellt sich erst eine Weile nach Beginn einer Reinigung auf &amp;quot;1&amp;quot;.Und nur in diesem Zustand klappt das Zurücksenden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== MAPS ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg548498.html#msg548498&lt;br /&gt;
http(s)://&amp;lt;ip&amp;gt;:&amp;lt;port&amp;gt;/fhem/BOTVAC/&amp;lt;device&amp;gt;/map&lt;br /&gt;
&lt;br /&gt;
Map per telegram versenden: &lt;br /&gt;
set &amp;lt;TelegramBot&amp;gt; cmdSend {ReadingsVal(&#039;&amp;lt;BOTVAC&amp;gt;&#039;, &#039;.map_cache&#039;, &#039;&#039;)}&lt;br /&gt;
&lt;br /&gt;
== Grundkonfiguration ==&lt;br /&gt;
&lt;br /&gt;
=== Definition in FHEM ===&lt;br /&gt;
=== Icons ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg539375.html#msg539375&lt;br /&gt;
&lt;br /&gt;
die Datei reinkopieren, dann &amp;quot;set WEB rereadicons&amp;quot; und anschließend normal als Icon auswählen.&lt;br /&gt;
Inzwischen ist das Icon wohl im SVN eingecheckt.&lt;br /&gt;
&lt;br /&gt;
== FHEM Device ==&lt;br /&gt;
siehe https://mwinkler.jimdo.com/eigene-anwendungen/winconnect/fhem-device/&lt;br /&gt;
&lt;br /&gt;
== Probleme ==&lt;br /&gt;
=== falls er Sauger mit der App neu konfiguriert werden musste ===&lt;br /&gt;
Du hast nach der Neuinstallation der App auch deinen VR/NEATO neu registriert. Fhem lädt aber noch die Daten aus der alten Registrierung. &lt;br /&gt;
Wähle mal im Gerät unter Set &#039;syncRobots&#039; aus. Wenn es danach bei Set auch eine Funktion &#039;setRobot&#039; gibt, hast du wirklich  den VR neu registriert. Dann einfach über dieses Set den VR wechseln. &lt;br /&gt;
Ansonsten bleibt noch die Registrierungsschlüssel im Fhem-Gerät zu löschen. Das geht über die Kommandozeile in Fhem:&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; accessToken&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; secretKey&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28147</id>
		<title>BOTVAC</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28147"/>
		<updated>2018-10-23T19:53:25Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* SET */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Dieses Modul dient zur Steuerung eines NEATO oder VORWERK Saugroboters&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=BOTVAC&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=noch nicht eingcheckt&lt;br /&gt;
|ModOwner=vuffiraa&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[BOTVAC]] ist ein Modul mit welchem VORWERK und NEATO Saugroboter gesteuert werden können. &lt;br /&gt;
&lt;br /&gt;
== Allgemeines ==&lt;br /&gt;
&lt;br /&gt;
=== Funktionsumfang  ===&lt;br /&gt;
Aktuell ist folgenden Funktionsumfang vorhanden:&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== SET === &lt;br /&gt;
*findMe&lt;br /&gt;
**The robot will emit a sound and blink the LEDs when it receives the findMe command.&lt;br /&gt;
*manualCleaningMode&lt;br /&gt;
*nextCleaningMode&lt;br /&gt;
*nextCleaningNavigationMode&lt;br /&gt;
*nextCleaningSpotHeight&lt;br /&gt;
*nextCleaningSpotWidth&lt;br /&gt;
*password&lt;br /&gt;
*pause&lt;br /&gt;
**pausiert die Reinigung&lt;br /&gt;
*pauseToBase&lt;br /&gt;
**Beendet eine Reinigung und fährt zur Basis zurück&lt;br /&gt;
*reloadMaps&lt;br /&gt;
**Lädt die letzte Karte vom Neato-Server, die erstellt wurde. Das passiert normalerweise automatisch, ist aber z.B. beim Neustart von Fhem sinnvoll. Beim Vorwerk läuft diese Funktion (noch) ins Leere.&lt;br /&gt;
*resume&lt;br /&gt;
**Setzt Reinigung fort&lt;br /&gt;
&lt;br /&gt;
*schedule&lt;br /&gt;
**Die möglichen Argumente sind &#039;on&#039; und &#039;off&#039; zum An- und Abschalten der Zeitsteuerung.&lt;br /&gt;
*sendToBase&lt;br /&gt;
**Fährt zur Basis zurück&lt;br /&gt;
&lt;br /&gt;
*setBoundaries&lt;br /&gt;
*setRobot&lt;br /&gt;
**Legt den zu steuernden Roboter fest&lt;br /&gt;
*startCleaning&lt;br /&gt;
** Startet die Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*startSpot&lt;br /&gt;
**Startet die Spot-Reinigung und hat je nach Model ein weiteres Argument Eco/Turbo oder Normal/ExtraCare&lt;br /&gt;
*statusRequest&lt;br /&gt;
*stop&lt;br /&gt;
** stoppt die Reinigung&lt;br /&gt;
*syncRobots&lt;br /&gt;
**Synchronisiert die Roboterdaten. Das ist nützlich, wenn man mehrere Geräte unter einem Neato- bzw. Vorwerk-Account verwaltet und dort Geräte entfernt oder hinzugefügt hat.&lt;br /&gt;
*stopCleaning&lt;br /&gt;
** Stop cleaning the house.&lt;br /&gt;
&lt;br /&gt;
=== GET ===&lt;br /&gt;
*batteryPercent&lt;br /&gt;
&lt;br /&gt;
=== readings ===&lt;br /&gt;
&lt;br /&gt;
*dockHasBeenSeen&lt;br /&gt;
**stellt sich erst eine Weile nach Beginn einer Reinigung auf &amp;quot;1&amp;quot;.Und nur in diesem Zustand klappt das Zurücksenden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== MAPS ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg548498.html#msg548498&lt;br /&gt;
http(s)://&amp;lt;ip&amp;gt;:&amp;lt;port&amp;gt;/fhem/BOTVAC/&amp;lt;device&amp;gt;/map&lt;br /&gt;
&lt;br /&gt;
== Grundkonfiguration ==&lt;br /&gt;
&lt;br /&gt;
=== Definition in FHEM ===&lt;br /&gt;
=== Icons ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg539375.html#msg539375&lt;br /&gt;
&lt;br /&gt;
die Datei reinkopieren, dann &amp;quot;set WEB rereadicons&amp;quot; und anschließend normal als Icon auswählen.&lt;br /&gt;
Inzwischen ist das Icon wohl im SVN eingecheckt.&lt;br /&gt;
&lt;br /&gt;
== FHEM Device ==&lt;br /&gt;
siehe https://mwinkler.jimdo.com/eigene-anwendungen/winconnect/fhem-device/&lt;br /&gt;
&lt;br /&gt;
== Probleme ==&lt;br /&gt;
=== falls er Sauger mit der App neu konfiguriert werden musste ===&lt;br /&gt;
Du hast nach der Neuinstallation der App auch deinen VR/NEATO neu registriert. Fhem lädt aber noch die Daten aus der alten Registrierung. &lt;br /&gt;
Wähle mal im Gerät unter Set &#039;syncRobots&#039; aus. Wenn es danach bei Set auch eine Funktion &#039;setRobot&#039; gibt, hast du wirklich  den VR neu registriert. Dann einfach über dieses Set den VR wechseln. &lt;br /&gt;
Ansonsten bleibt noch die Registrierungsschlüssel im Fhem-Gerät zu löschen. Das geht über die Kommandozeile in Fhem:&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; accessToken&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; secretKey&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28146</id>
		<title>BOTVAC</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28146"/>
		<updated>2018-10-23T19:36:12Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* MAPS */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Dieses Modul dient zur Steuerung eines NEATO oder VORWERK Saugroboters&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=BOTVAC&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=noch nicht eingcheckt&lt;br /&gt;
|ModOwner=vuffiraa&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[BOTVAC]] ist ein Modul mit welchem VORWERK und NEATO Saugroboter gesteuert werden können. &lt;br /&gt;
&lt;br /&gt;
== Allgemeines ==&lt;br /&gt;
&lt;br /&gt;
=== Funktionsumfang  ===&lt;br /&gt;
Aktuell ist folgenden Funktionsumfang vorhanden:&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== SET === &lt;br /&gt;
*findMe&lt;br /&gt;
*manualCleaningMode&lt;br /&gt;
*nextCleaningMode&lt;br /&gt;
*nextCleaningNavigationMode&lt;br /&gt;
*nextCleaningSpotHeight&lt;br /&gt;
*nextCleaningSpotWidth&lt;br /&gt;
*password&lt;br /&gt;
*reloadMaps&lt;br /&gt;
*schedule&lt;br /&gt;
*setBoundaries&lt;br /&gt;
*setRobot&lt;br /&gt;
** nur verfügbar, wenn mehr als ein Sauger vorhanden ist. Ordnet dem Fhem-Gerät einen Sauger(name) zu.&lt;br /&gt;
*startCleaning&lt;br /&gt;
*startSpot&lt;br /&gt;
*statusRequest&lt;br /&gt;
*syncRobots&lt;br /&gt;
**synchronisiert die Sauger, die zu einem Nutzer zugeordnet sind&lt;br /&gt;
*pause&lt;br /&gt;
*stop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== GET ===&lt;br /&gt;
*batteryPercent&lt;br /&gt;
&lt;br /&gt;
=== readings ===&lt;br /&gt;
&lt;br /&gt;
*dockHasBeenSeen&lt;br /&gt;
**stellt sich erst eine Weile nach Beginn einer Reinigung auf &amp;quot;1&amp;quot;.Und nur in diesem Zustand klappt das Zurücksenden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== MAPS ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg548498.html#msg548498&lt;br /&gt;
http(s)://&amp;lt;ip&amp;gt;:&amp;lt;port&amp;gt;/fhem/BOTVAC/&amp;lt;device&amp;gt;/map&lt;br /&gt;
&lt;br /&gt;
== Grundkonfiguration ==&lt;br /&gt;
&lt;br /&gt;
=== Definition in FHEM ===&lt;br /&gt;
=== Icons ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg539375.html#msg539375&lt;br /&gt;
&lt;br /&gt;
die Datei reinkopieren, dann &amp;quot;set WEB rereadicons&amp;quot; und anschließend normal als Icon auswählen.&lt;br /&gt;
Inzwischen ist das Icon wohl im SVN eingecheckt.&lt;br /&gt;
&lt;br /&gt;
== FHEM Device ==&lt;br /&gt;
siehe https://mwinkler.jimdo.com/eigene-anwendungen/winconnect/fhem-device/&lt;br /&gt;
&lt;br /&gt;
== Probleme ==&lt;br /&gt;
=== falls er Sauger mit der App neu konfiguriert werden musste ===&lt;br /&gt;
Du hast nach der Neuinstallation der App auch deinen VR/NEATO neu registriert. Fhem lädt aber noch die Daten aus der alten Registrierung. &lt;br /&gt;
Wähle mal im Gerät unter Set &#039;syncRobots&#039; aus. Wenn es danach bei Set auch eine Funktion &#039;setRobot&#039; gibt, hast du wirklich  den VR neu registriert. Dann einfach über dieses Set den VR wechseln. &lt;br /&gt;
Ansonsten bleibt noch die Registrierungsschlüssel im Fhem-Gerät zu löschen. Das geht über die Kommandozeile in Fhem:&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; accessToken&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; secretKey&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28145</id>
		<title>BOTVAC</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28145"/>
		<updated>2018-10-23T19:34:19Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* MAPS */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Dieses Modul dient zur Steuerung eines NEATO oder VORWERK Saugroboters&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=BOTVAC&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=noch nicht eingcheckt&lt;br /&gt;
|ModOwner=vuffiraa&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[BOTVAC]] ist ein Modul mit welchem VORWERK und NEATO Saugroboter gesteuert werden können. &lt;br /&gt;
&lt;br /&gt;
== Allgemeines ==&lt;br /&gt;
&lt;br /&gt;
=== Funktionsumfang  ===&lt;br /&gt;
Aktuell ist folgenden Funktionsumfang vorhanden:&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== SET === &lt;br /&gt;
*findMe&lt;br /&gt;
*manualCleaningMode&lt;br /&gt;
*nextCleaningMode&lt;br /&gt;
*nextCleaningNavigationMode&lt;br /&gt;
*nextCleaningSpotHeight&lt;br /&gt;
*nextCleaningSpotWidth&lt;br /&gt;
*password&lt;br /&gt;
*reloadMaps&lt;br /&gt;
*schedule&lt;br /&gt;
*setBoundaries&lt;br /&gt;
*setRobot&lt;br /&gt;
** nur verfügbar, wenn mehr als ein Sauger vorhanden ist. Ordnet dem Fhem-Gerät einen Sauger(name) zu.&lt;br /&gt;
*startCleaning&lt;br /&gt;
*startSpot&lt;br /&gt;
*statusRequest&lt;br /&gt;
*syncRobots&lt;br /&gt;
**synchronisiert die Sauger, die zu einem Nutzer zugeordnet sind&lt;br /&gt;
*pause&lt;br /&gt;
*stop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== GET ===&lt;br /&gt;
*batteryPercent&lt;br /&gt;
&lt;br /&gt;
=== readings ===&lt;br /&gt;
&lt;br /&gt;
*dockHasBeenSeen&lt;br /&gt;
**stellt sich erst eine Weile nach Beginn einer Reinigung auf &amp;quot;1&amp;quot;.Und nur in diesem Zustand klappt das Zurücksenden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== MAPS ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg548498.html#msg548498&lt;br /&gt;
&lt;br /&gt;
== Grundkonfiguration ==&lt;br /&gt;
&lt;br /&gt;
=== Definition in FHEM ===&lt;br /&gt;
=== Icons ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg539375.html#msg539375&lt;br /&gt;
&lt;br /&gt;
die Datei reinkopieren, dann &amp;quot;set WEB rereadicons&amp;quot; und anschließend normal als Icon auswählen.&lt;br /&gt;
Inzwischen ist das Icon wohl im SVN eingecheckt.&lt;br /&gt;
&lt;br /&gt;
== FHEM Device ==&lt;br /&gt;
siehe https://mwinkler.jimdo.com/eigene-anwendungen/winconnect/fhem-device/&lt;br /&gt;
&lt;br /&gt;
== Probleme ==&lt;br /&gt;
=== falls er Sauger mit der App neu konfiguriert werden musste ===&lt;br /&gt;
Du hast nach der Neuinstallation der App auch deinen VR/NEATO neu registriert. Fhem lädt aber noch die Daten aus der alten Registrierung. &lt;br /&gt;
Wähle mal im Gerät unter Set &#039;syncRobots&#039; aus. Wenn es danach bei Set auch eine Funktion &#039;setRobot&#039; gibt, hast du wirklich  den VR neu registriert. Dann einfach über dieses Set den VR wechseln. &lt;br /&gt;
Ansonsten bleibt noch die Registrierungsschlüssel im Fhem-Gerät zu löschen. Das geht über die Kommandozeile in Fhem:&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; accessToken&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; secretKey&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28144</id>
		<title>BOTVAC</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=BOTVAC&amp;diff=28144"/>
		<updated>2018-10-23T19:33:52Z</updated>

		<summary type="html">&lt;p&gt;Stephan: /* Voraussetzungen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Dieses Modul dient zur Steuerung eines NEATO oder VORWERK Saugroboters&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=BOTVAC&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=noch nicht eingcheckt&lt;br /&gt;
|ModOwner=vuffiraa&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
[[BOTVAC]] ist ein Modul mit welchem VORWERK und NEATO Saugroboter gesteuert werden können. &lt;br /&gt;
&lt;br /&gt;
== Allgemeines ==&lt;br /&gt;
&lt;br /&gt;
=== Funktionsumfang  ===&lt;br /&gt;
Aktuell ist folgenden Funktionsumfang vorhanden:&lt;br /&gt;
&lt;br /&gt;
=== Attribute ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== SET === &lt;br /&gt;
*findMe&lt;br /&gt;
*manualCleaningMode&lt;br /&gt;
*nextCleaningMode&lt;br /&gt;
*nextCleaningNavigationMode&lt;br /&gt;
*nextCleaningSpotHeight&lt;br /&gt;
*nextCleaningSpotWidth&lt;br /&gt;
*password&lt;br /&gt;
*reloadMaps&lt;br /&gt;
*schedule&lt;br /&gt;
*setBoundaries&lt;br /&gt;
*setRobot&lt;br /&gt;
** nur verfügbar, wenn mehr als ein Sauger vorhanden ist. Ordnet dem Fhem-Gerät einen Sauger(name) zu.&lt;br /&gt;
*startCleaning&lt;br /&gt;
*startSpot&lt;br /&gt;
*statusRequest&lt;br /&gt;
*syncRobots&lt;br /&gt;
**synchronisiert die Sauger, die zu einem Nutzer zugeordnet sind&lt;br /&gt;
*pause&lt;br /&gt;
*stop&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== GET ===&lt;br /&gt;
*batteryPercent&lt;br /&gt;
&lt;br /&gt;
=== readings ===&lt;br /&gt;
&lt;br /&gt;
*dockHasBeenSeen&lt;br /&gt;
**stellt sich erst eine Weile nach Beginn einer Reinigung auf &amp;quot;1&amp;quot;.Und nur in diesem Zustand klappt das Zurücksenden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== MAPS ===&lt;br /&gt;
&lt;br /&gt;
== Grundkonfiguration ==&lt;br /&gt;
&lt;br /&gt;
=== Definition in FHEM ===&lt;br /&gt;
=== Icons ===&lt;br /&gt;
https://forum.fhem.de/index.php/topic,51713.msg539375.html#msg539375&lt;br /&gt;
&lt;br /&gt;
die Datei reinkopieren, dann &amp;quot;set WEB rereadicons&amp;quot; und anschließend normal als Icon auswählen.&lt;br /&gt;
Inzwischen ist das Icon wohl im SVN eingecheckt.&lt;br /&gt;
&lt;br /&gt;
== FHEM Device ==&lt;br /&gt;
siehe https://mwinkler.jimdo.com/eigene-anwendungen/winconnect/fhem-device/&lt;br /&gt;
&lt;br /&gt;
== Probleme ==&lt;br /&gt;
=== falls er Sauger mit der App neu konfiguriert werden musste ===&lt;br /&gt;
Du hast nach der Neuinstallation der App auch deinen VR/NEATO neu registriert. Fhem lädt aber noch die Daten aus der alten Registrierung. &lt;br /&gt;
Wähle mal im Gerät unter Set &#039;syncRobots&#039; aus. Wenn es danach bei Set auch eine Funktion &#039;setRobot&#039; gibt, hast du wirklich  den VR neu registriert. Dann einfach über dieses Set den VR wechseln. &lt;br /&gt;
Ansonsten bleibt noch die Registrierungsschlüssel im Fhem-Gerät zu löschen. Das geht über die Kommandozeile in Fhem:&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; accessToken&lt;br /&gt;
- deleteReading &amp;lt;device&amp;gt; secretKey&lt;/div&gt;</summary>
		<author><name>Stephan</name></author>
	</entry>
</feed>