<?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=Eisler</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=Eisler"/>
	<link rel="alternate" type="text/html" href="http://wiki.fhem.de/wiki/Spezial:Beitr%C3%A4ge/Eisler"/>
	<updated>2026-04-30T15:38:13Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=31976</id>
		<title>Proteus EcoMeter</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=31976"/>
		<updated>2019-12-14T17:35:03Z</updated>

		<summary type="html">&lt;p&gt;Eisler: Links korrigiert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Proteus_EcoMeter]] ist ein Ultraschallsensor mit Monitor zur Pegelmessung von Heizöl- oder Wassertanks&lt;br /&gt;
&lt;br /&gt;
* Sensor zum Einbau in den Tank&lt;br /&gt;
* Funkverbindung zwischen Sensor und Monitor&lt;br /&gt;
* USB-Verbindung zwischen Monitor und FHEM&lt;br /&gt;
* Für Wasser oder Öltanks&lt;br /&gt;
&lt;br /&gt;
EcoMeter für Heizöltanks&amp;lt;br&amp;gt;&lt;br /&gt;
EcoMeter S für Wassertanks&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Infobox Hardware&lt;br /&gt;
|Bild=platzHalter.png&lt;br /&gt;
|Bildbeschreibung=EcoMeter Ultraschall Füllstandsanzeige für Heizöltank&lt;br /&gt;
|HWProtocol=unknown&lt;br /&gt;
|HWType=Sensor und Sender&lt;br /&gt;
|HWCategory=Other Components&lt;br /&gt;
|HWComm=Funk, 433,92MHz, USB&lt;br /&gt;
|HWChannels=unknown&lt;br /&gt;
|HWVoltage=1,5V LR2450, 5V USB&lt;br /&gt;
|HWPowerConsumption=unknown&lt;br /&gt;
|HWPoweredBy=Battery, USB&lt;br /&gt;
|HWSize=21 x 18 x 6 cm&lt;br /&gt;
|HWDeviceFHEM=[http://fhem.de/commandref.html#TEK603 TEK603]&lt;br /&gt;
|HWManufacturer=Tekelek&lt;br /&gt;
}}&lt;br /&gt;
== Features ==&lt;br /&gt;
EcoMeter für Heizöl liefert alle 60 Minuten einen Messwert&lt;br /&gt;
&lt;br /&gt;
EcoMeter S für Zisterne, Wassertank liefert alle 30 Minuten einen Messwert &lt;br /&gt;
&lt;br /&gt;
Wenn der Sensor eine Füllstandänderung von 3cm pro Minute erkennt, geht der Sensor in den FastModus, d.h. es werden alle 2 - 3 Sekunden neue Werte an den Ecomonitor kommuniziert. Dies ist hilfreich bei einem Leck oder beim Nachtanken. Der FastModus, vergleichbar mit dem Modus nach dem Koppeln, dauert dann ca. 10 Minuten an, so dass die Batterie nicht leer gesaugt wird.&lt;br /&gt;
&lt;br /&gt;
== Hinweise zum Betrieb mit FHEM ==&lt;br /&gt;
=== Definition/Anlernvorgang ===&lt;br /&gt;
# eventuell im Linux fehlende Pakete installieren &amp;lt;code&amp;gt;sudo apt-get install libdigest-crc-perl &amp;lt;/code&amp;gt;&lt;br /&gt;
# Monitor an das FHEM Gerät anschließen&lt;br /&gt;
# Festellen, dass die Verbindung geht. Dazu auf der Kommandozeile eingeben: &amp;lt;code&amp;gt;ls -al /dev/serial/by-id &amp;lt;br&amp;gt; lrwxrwxrwx 1 root root 13 Jan  1  1970 usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 -&amp;gt; ../../ttyUSB0 &amp;lt;/code&amp;gt; &lt;br /&gt;
# ecometer definieren &amp;lt;code&amp;gt;define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 &amp;lt;/code&amp;gt;&lt;br /&gt;
# LogFile definieren &amp;lt;code&amp;gt;define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.* &amp;lt;/code&amp;gt;&lt;br /&gt;
# Gerät wie in der Betriebsanleitung installieren&lt;br /&gt;
&lt;br /&gt;
=== Identifikation ===&lt;br /&gt;
Das Proteus EcoMeter verwendet für die Anbindung der seriellen Kommunikation an die USB-Schnittstelle einen sehr weit verbreiteten Chip der Familie &amp;lt;code&amp;gt;CP210x UART Bridges&amp;lt;/code&amp;gt;. Dieser Baustein hat die USB &amp;lt;code&amp;gt;ID 10c4:ea60&amp;lt;/code&amp;gt; über die auch der Name in &amp;lt;code&amp;gt;/dev/serial/by-id&amp;lt;/code&amp;gt; abgeleitet wird. Hat man mehrere Geräte die mit diesem Chip arbeiten (z.B. den optischen Adapter von Viessmann für [[Vitotronic_200_(Viessmann_Heizungssteuerung)|VCONTROL]]) am gleichen Rechner, so werden diese unter &amp;lt;code&amp;gt;lsusb&amp;lt;/code&amp;gt; mit identischer ID gelistet, aber nur einer erhält den passenden Namen unter &amp;lt;code&amp;gt;/dev/serial/by-id&amp;lt;/code&amp;gt;. Sie sind also auf diese Weise nicht unterscheidbar. Die kurzen Gerätenamen &amp;lt;code&amp;gt;/dev/ttyUSBx&amp;lt;/code&amp;gt; werden willkürlich zugeordnet. Damit sind die Geräte auch nicht unterscheidbar.&lt;br /&gt;
&lt;br /&gt;
{{Hinweis|Eventuell können die Identifizierungsangaben ggf. im EEPROM der CP2102 abgeändert werden. Details hierzu sind im Artikel [[CP2102]] zu finden.}}&lt;br /&gt;
&lt;br /&gt;
Bleibt noch der Pfad wenn man die Geräte immer in die gleiche Buchse steckt. Diese Pfade werden unter Linux in &amp;lt;code&amp;gt;/dev/serial/by-path&amp;lt;/code&amp;gt; angelegt. Hier kann man einzelne Buchsen (auch von Hubs) identifizieren. Die Namen enthalten jedoch Doppelpunkte und FHEM verkraftet keine Gerätenamen mit Doppelpunkten. &amp;lt;small&amp;gt;&#039;&#039;Wer weiß warum bitte hier Lösung eintragen.&#039;&#039;&amp;lt;/small&amp;gt; Um diese ohnehin unhandlichen Namen kürzer und verträglicher zu bekommen, kann man Symbolische Links anlegen. Dazu erzeugt man am besten in &amp;lt;code&amp;gt;/opt/fhem&amp;lt;/code&amp;gt; ein Verzeichnis &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; und verlinkt alle für FHEM relevanten Geräte dort hin. Also in meinem Beispiel TEK603 und VCONTROL:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=bash&amp;gt;&lt;br /&gt;
cd /opt/fhem&lt;br /&gt;
mkdir dev&lt;br /&gt;
cd dev&lt;br /&gt;
ln -s /dev/serial/by-path/pci-0000:00:14.0-usb-0:4.3:1.0-port0 heizung&lt;br /&gt;
ln -s /dev/serial/by-path/pci-0000:00:14.0-usb-0:4.2:1.0-port0 oelpegel&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Solange man die beiden Geräte immer in die gleiche Buchse steckt, werden sie ab jetzt immer korrekt zugeordnet:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=text&amp;gt;&lt;br /&gt;
defmod Oelpegel TEK603 /opt/fhem/dev/oelpegel&lt;br /&gt;
defmod Heizung VCONTROL /opt/fhem/dev/heizung /opt/fhem/mycfg/vitoladens_300-C_J3Ra-24.cfg 120&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FHEM Config-Auszug ===&lt;br /&gt;
Ein exemplarischer Auszug aus der [[Konfiguration]]:&lt;br /&gt;
 define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0&lt;br /&gt;
 define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.*&lt;br /&gt;
&lt;br /&gt;
=== Eventmonitorbeispiel ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=text&amp;gt;&lt;br /&gt;
2017-03-03_13:07:11 Oelpegel CONNECTED&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Time: 13:05:21&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Temperature: 18.33&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Ullage: 100&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel RemainingUsableLevel: 3227&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel RemainingUsablePercent: 31.0&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel TotalUsableCapacity: 10414&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In den ersten 10 Minuten nach dem Anlernen, befindet sich das Gerät im Echtzeit-Modus. Das bedeutet es werden mehrere Messungen pro Sekunde übertragen. Das würde die Batterie sehr schnell lehren. Daher schaltet das Gerät nach 10 Minuten in den Normalbetrieb, in dem nur noch ganz grob alle 30 Minuten Werte übertragen werden. Die Übertragung zu FHEM findet auch nur statt, wenn über die Funkstrecke Daten vom Sensor kommen. Das führt zu der Situation, dass nach diesen 10 Minuten ein neu eingerichtetes &amp;lt;code&amp;gt;TEK603&amp;lt;/code&amp;gt; Modul sehr lange (Stunden ?) keine Daten anzeigt, obwohl das Display der Proteus Daten anzeigt. Das liegt einfach daran, dass dieses Display den zuletzt gespeicherten Messwert anzeigt egal wie lange der her ist. Übertragen wird bis zum nächsten Wert auf der Funkstrecke nichts.&lt;br /&gt;
&lt;br /&gt;
== Einsatzbeispiel ==&lt;br /&gt;
=== Anzeige des Ölstands in einem Öltank ===&lt;br /&gt;
{{todo|Beispielunterlagen erstellen}}&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
Füllstandsanzeige für Zisterne, Wassertank]&lt;br /&gt;
* Hersteller: [https://tekelek.ie/wp-content/uploads/2019/04/InstallationGuide-TEK-603-Eco-Oil-Monitor-Tekelek-Installation-Guide-A4.pdf Bedienanleitung]&lt;br /&gt;
* Forenbeitrag zur FHEM-Einbindung: {{Link2Forum|Topic=27315}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Other Components]]&lt;br /&gt;
[[Kategorie:Füllstandsmesser]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=AutoShuttersControl&amp;diff=28567</id>
		<title>AutoShuttersControl</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=AutoShuttersControl&amp;diff=28567"/>
		<updated>2018-11-26T20:54:45Z</updated>

		<summary type="html">&lt;p&gt;Eisler: fix&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Baustelle}}&lt;br /&gt;
{{Infobox Modul&lt;br /&gt;
|ModPurpose=Steuerung von Rollläden&lt;br /&gt;
|ModCategory=Automatisierung&lt;br /&gt;
|ModType=x&lt;br /&gt;
&amp;lt;!-- |ModCmdRef= ---- noch nicht Teil von FHEM --&amp;gt;&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=73_AutoShuttersControl.pm&lt;br /&gt;
|ModOwner=CoolTux}}&lt;br /&gt;
Mit [[AutoShuttersControl]] oder kurz ASC können typische Aufgabenstellungen im Zusammenhang mit Rollläden u.ä. automatisiert werden, wie zum Beispiel das Öffnen bei Sonnenaufgang, Schließen bei Sonnenuntergang oder das Anfahren von Lüftungspositionen beim Öffnen des zugehörigen Fensters. &lt;br /&gt;
&lt;br /&gt;
{{Hinweis|Das Modul befindet sich derzeit noch in der Entwicklung; der jeweils aktuelle Stand ist  diesem {{Link2Forum|Topic=90751|LinkText=&amp;quot;Thread im Forum&amp;quot;}} zu entnehmen.}}&lt;br /&gt;
&lt;br /&gt;
== Basics ==&lt;br /&gt;
Zur Nutzung des Moduls sollten folgende andere FHEM-Devices vorhanden sein:&lt;br /&gt;
* Rollläden&lt;br /&gt;
* Fensterkontakte und &lt;br /&gt;
* Bewohnerstatus auf Basis von Residents/Roomates in englisch. Ersatzweise andere Devices, z.B. Dummys, welche als &#039;&#039;state&#039;&#039; &#039;&#039;home&#039;&#039;, &#039;&#039;asleep&#039;&#039;, &#039;&#039;gotosleep&#039;&#039; und &#039;&#039;awoken&#039;&#039; setzen sowie ein Reading &#039;&#039;lastState&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Die Einrichtung des Moduls bzw. dessen Funktionalität erfolgt in mehreren Schritten:&lt;br /&gt;
* Definition des ASC-Devices&lt;br /&gt;
* Einstellung zentraler Vorgaben am ASC-Device&lt;br /&gt;
* Markieren der zukünftig zu steuernden Rollladen-Devices&lt;br /&gt;
* Einbinden der Rollladen-Devices in das ASC-Device&lt;br /&gt;
* Einstellen der individuellen Vorgaben je Rollladen (am Rollladen-Device)&lt;br /&gt;
&lt;br /&gt;
== Einrichtung ==&lt;br /&gt;
&lt;br /&gt;
=== Vorbereitung ===&lt;br /&gt;
&lt;br /&gt;
{{Hinweis|Ihr benötigt ein aktuelles FHEM. Update ab dem 04.09.2018 Voraussetzung!}}&lt;br /&gt;
&lt;br /&gt;
=== Define des ASC-Devices ===&lt;br /&gt;
Es genügt ein einfaches define ohne weitere Parameter.&lt;br /&gt;
&amp;lt;code&amp;gt;define &amp;lt;name&amp;gt; AutoShuttersControl&amp;lt;/code&amp;gt;&lt;br /&gt;
Dies bewirkt neben der Anlage des Devices selbst auch, dass als weiteres globales Attribut &#039;&#039;ASC&#039;&#039; verfügbar wird.&lt;br /&gt;
&lt;br /&gt;
=== Einstellung zentraler Vorgaben ===&lt;br /&gt;
Es stehen am ASC-Device einige Attribute zur Verfügung, über die sich das Verhalten des ASC-Devices insgesamt steuern lässt, z.B. zur Vermeidung von morgendlichen oder abendlichen Fahrten sowie bei Gefahr von Schäden an den Rollläden durch Frost oder zur Reaktion auf Fensterkontakte. Details sind der Commandref zu entnehmen.&lt;br /&gt;
Diese Attribute können auch nachträglich noch geändert werden.&lt;br /&gt;
&lt;br /&gt;
=== Markieren zu steuernder Rollladen-Devices ===&lt;br /&gt;
Um einen oder mehrere Rollläden durch das ASC-Device zu steuern, wird für jeden Rollladen jeweils das neue globale Attribut &#039;&#039;ASC&#039;&#039; gesetzt. Der Wert ist mit 1 oder 2 festzulegen, wobei &#039;&#039;&#039;1&#039;&#039;&#039; bedeutet, dass &lt;br /&gt;
* die Offen-Position kleiner ist als die Geschlossen-Position, also typischerweise 0 &#039;&#039;offen&#039;&#039; bedeutet und 100 für &#039;&#039;geschlossen&#039;&#039; steht&lt;br /&gt;
* eine bestimmte Position mit &#039;&#039;&#039;position&#039;&#039;&#039; angefahren wird, also z.B. &amp;lt;code&amp;gt;set &amp;lt;name&amp;gt; position 70&amp;lt;/code&amp;gt;.&lt;br /&gt;
Dies ist z.B. die  passende Wahl für ROLLO-Devices&lt;br /&gt;
&lt;br /&gt;
Die &#039;&#039;&#039;2&#039;&#039;&#039; steht für ein umgekehrtes Verhalten und den Befehlsteil &#039;&#039;pct&#039;&#039;, also &#039;&#039;offen&#039;&#039; für &amp;lt;code&amp;gt;set &amp;lt;name&amp;gt; pct 100&amp;lt;/code&amp;gt;. Typischer Vertreter dieses Typs sind HomeMatic (CUL_HM-) Geräte oder die Shelly2-Aktoren.&lt;br /&gt;
&lt;br /&gt;
=== Einbinden in das ASC-Device ===&lt;br /&gt;
Nachdem man das obige Attribut bei einem oder mehreren Rollladen-Devices gesetzt hat, kann mit &lt;br /&gt;
&amp;lt;code&amp;gt;set &amp;lt;name&amp;gt; scanForShutters&amp;lt;/code&amp;gt; ein Suchlauf gestartet werden, mit dem dann diese Rollläden durch das Modul gefunden und in die Steuerungslogik eingebunden werden.&lt;br /&gt;
&lt;br /&gt;
=== Einstellen der individuellen Vorgaben ===&lt;br /&gt;
Nach der Einbindung sind an den Rollladen-Devices weitere Attribute verfügbar, mit denen die für den jeweiligen Rollladen geltenden Einstellungen vorgenommen werden können. &lt;br /&gt;
Die Beschreibung der Attribute ist in der commandref enthalten.&lt;br /&gt;
&lt;br /&gt;
== Readings ==&lt;br /&gt;
&lt;br /&gt;
===Readings im ASC-Device selbst.===&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Datentyp/&amp;lt;BR/&amp;gt;Wertebereich !! Bedeutung  &lt;br /&gt;
|-&lt;br /&gt;
|..._nextAstroTimeEvent || ||Uhrzeit des nächsten Astro Events, Sonnenauf- oder Sonnenuntergang oder feste Zeit pro Rollonamen &lt;br /&gt;
|-&lt;br /&gt;
|..._lastPosValue || ||letzter abgesetzter Fahrbefehl pro Rollonamen&lt;br /&gt;
|-&lt;br /&gt;
|..._lastDelayPosValue || ||letzter abgesetzter Fahrbefehl welcher beim nächsten zulässigen Event ausgeführt wird.&lt;br /&gt;
|-&lt;br /&gt;
|partyMode ||on, off || aktiviert den globalen Partymodus. Alle Rollläden, welche das Attribut AutoShuttersControl_Partymode bei sich auf on gestellt haben, werden nicht mehr gesteuert. Der letzte Schaltbefehl, welcher durch ein Fensterevent oder Bewohnerstatus an die Rollläden gesendet wurde, wird beim off setzen durch set ASC-Device partyMode off ausgeführt&lt;br /&gt;
|-&lt;br /&gt;
|lockOut || on, off ||für das Aktivieren des Aussperrschutzes gemäß des entsprechenden Attributs AutoShuttersControl_lock-out im jeweiligen Rolladen. (siehe Beschreibung bei den Attributen für die Rolladendevices)&lt;br /&gt;
|-&lt;br /&gt;
|room_... || ||Auflistung aller Rollläden welche in den jeweiligen Rämen gefunden wurde, Bsp.: room_Schlafzimmer,Terrasse&lt;br /&gt;
|-&lt;br /&gt;
|state || ||Status des Devices active, enabled, disabled&lt;br /&gt;
|-&lt;br /&gt;
|sunriseTimeWeHoliday|| on,off ||wird das Rolladen Device Attribut Attributes AutoShuttersControl_Time_Up_WE_Holiday beachtet oder nicht&lt;br /&gt;
|-&lt;br /&gt;
|userAttrList || rolled out ||Status der UserAttribute, welche an die Rollläden gesendet werden&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Readings in den Rolllädendevices===&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Bedeutung  &lt;br /&gt;
|-&lt;br /&gt;
|ASC_Time_DriveUp ||Sonnenaufgangszeit für das Rollo&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Time_DriveDown ||Sonnenuntergangszeit für das Rollo&lt;br /&gt;
|-&lt;br /&gt;
|ASC_ShuttersLastDrive ||Grund des letzten fahrens vom Rolladen&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Settings ==&lt;br /&gt;
&lt;br /&gt;
===Set Befehle für ASC-Device===&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Datentyp/&amp;lt;BR/&amp;gt;Wertebereich !! Beschreibung  &lt;br /&gt;
|-&lt;br /&gt;
|partyMode ||on, off ||aktiviert den globalen Partymodus. Siehe Reading partyMode&lt;br /&gt;
|-&lt;br /&gt;
|lockOut ||on, off ||aktiviert den globalen Aussperrschutz. Siehe Reading partyMode&lt;br /&gt;
|-&lt;br /&gt;
|renewSetSunriseSunsetTimer || || erneuert bei allen Rollläden die Zeiten für Sunset und Sunrise und setzt die internen Timer neu.&lt;br /&gt;
|-&lt;br /&gt;
|scanForShutters || ||sucht alle FHEM Devices mit dem Attribut &amp;quot;AutoShuttersControl&amp;quot; 1, 2&lt;br /&gt;
|-&lt;br /&gt;
|sunriseTimeWeHoliday || on,off ||aktiviert/deaktiviert die Beachtung des Attributes AutoShuttersControl_Time_Up_WE_Holiday für Rollladen-Devices&lt;br /&gt;
|-&lt;br /&gt;
|createNewNotifyDev || ||Legt die interne Struktur für NOTIFYDEV neu an&lt;br /&gt;
|-&lt;br /&gt;
|selfDefence ||on, off||aktiviert/deaktiviert den Selbstschutz, wenn das Residents Device absent meldet und selfDefence aktiv ist und ein Fenster im Haus steht noch offen, wird an diesem Fenster das Rollo runtergefahren&lt;br /&gt;
|-&lt;br /&gt;
|wiggle ||||Bewegt einen Rollladen oder alle Rollläden (für Abschreckungszwecke bei der Alarmierung) um 5%, und nach 1 Minute wieder zurück zur Ursprungsposition&lt;br /&gt;
|-&lt;br /&gt;
|||||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Get ==&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| showShuttersInformations ||zeigt eine Übersicht der Autofahrzeiten&lt;br /&gt;
|-&lt;br /&gt;
| showNotifyDevsInformations ||zeigt eine Übersicht der abgelegten NOTIFYDEV Struktur. Dient zur Kontrolle&lt;br /&gt;
|}&lt;br /&gt;
== Attribute ==&lt;br /&gt;
&lt;br /&gt;
===Attribute im AutoShuttersControl Device selbst.===&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Datentyp/&amp;lt;BR/&amp;gt;Wertebereich !! Default-Wert !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|ASC_antifreezeTemp || || ||Temperatur ab welcher der Frostschutz greifen soll und das Rollo nicht mehr fährt. Der letzte Fahrbefehl wird gespeichert.&lt;br /&gt;
|-&lt;br /&gt;
|ASC_autoAstroModeEvening ||aktuell REAL, CIVIL, NAUTIC, ASTRONOMIC || || (in genau dieser Schreibweise)&lt;br /&gt;
|-&lt;br /&gt;
|ASC_autoAstroModeEveningHorizon || || || Höhe über Horizont, wenn beim Attribut AutoShuttersControl_autoAstroModeEvening HORIZON ausgewählt&lt;br /&gt;
|-&lt;br /&gt;
|ASC_autoAstroModeMorning ||aktuell REAL, CIVIL, NAUTIC, ASTRONOMIC || ||(in genau dieser Schreibweise)&lt;br /&gt;
|-&lt;br /&gt;
|ASC_autoAstroModeMorningHorizon || || ||Höhe über Horizont, wenn beim Attribut AutoShuttersControl_autoAstroModeMorning HORIZON ausgewählt&lt;br /&gt;
|-&lt;br /&gt;
|ASC_autoShuttersControlComfort ||on, off || ||schaltet die Komfortfunktion an. Bedeutet, dass ein Rollladen mit einem threestate Sensor am Fenster beim Öffnen in eine weit offen Position fährt. Die Offenposition wird beim Rollladen über das Attribut AutoShuttersControl_Pos_after_ComfortOpen eingestellt.&lt;br /&gt;
|- &lt;br /&gt;
|ASC_autoShuttersControlEvening ||on, off || ||ob Abends die Rollläden automatisch nach Zeit gesteuert werden sollen&lt;br /&gt;
|-&lt;br /&gt;
|ASC_autoShuttersControlMorning ||on, off || ||ob Morgens die Rollläden automatisch nach Zeit gesteuert werden sollen&lt;br /&gt;
|-&lt;br /&gt;
|ASC_temperatureReading || || ||Reading für die Außentemperatur&lt;br /&gt;
|-&lt;br /&gt;
|ASC_temperatureSensor || || ||Device für die Außentemperatur&lt;br /&gt;
|-&lt;br /&gt;
|ASC_timeUpHolidayDevice || || ||Device zur Urlaubserkennung oder Sonstiges / muss 0 oder 1 im Reading state beinhalten.&lt;br /&gt;
|-&lt;br /&gt;
|ASC_residentsDevice|| || ||Devicenamen des Residents-Device der obersten Ebene&lt;br /&gt;
|-&lt;br /&gt;
|ASC_residentsDeviceReading|| || ||Status Reading des Residents-Device der obersten Ebene&lt;br /&gt;
|-&lt;br /&gt;
|ASC_brightnessMinVal|| || ||minimaler Lichtwert, bei dem Schaltbedingungen noch geprüft werden sollen&lt;br /&gt;
|-&lt;br /&gt;
|ASC_brightnessMaxVal || || ||maximaler Lichtwert, bei dem Schaltbedingungen noch geprüft werden sollen&lt;br /&gt;
|-&lt;br /&gt;
|ASC_rainSensorDevice || || ||Device, welches bei Regen getriggert werden soll&lt;br /&gt;
|-&lt;br /&gt;
|ASC_rainSensorReading || || ||das ensprechende Reading zum Regendevice&lt;br /&gt;
|-&lt;br /&gt;
|ASC_rainSensorShuttersClosedPos || || ||Position in pct, welche der Rollladen bei Regen anfahren soll &lt;br /&gt;
|-&lt;br /&gt;
|ASC_shuttersDriveOffset  || || ||maximale zufällige Verzögerung in Sekunden bei der Berechnung der Fahrzeiten, 0 bedeutet keine Verzögerung&lt;br /&gt;
|-&lt;br /&gt;
|ASC_twilightDevice || || ||Device welches Informationen zum Sonnenstand liefert, wird unter anderem für die Beschattung verwendet.&lt;br /&gt;
|-&lt;br /&gt;
|ASC_expert || || ||ist der Wert 1 werden erweiterte Informationen bezüglich des NotifyDevs unter set und get angezeigt&lt;br /&gt;
|-&lt;br /&gt;
| || || ||&lt;br /&gt;
|-&lt;br /&gt;
| || || ||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Attribute in den Rolllädendevices===&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Datentyp/&amp;lt;BR/&amp;gt;Wertebereich !! Default-Wert !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|ASC || 0, 1, 2|| ||1 = &amp;quot;Inverse oder Rollo Bsp.: Rollo Oben 0,Rollo Unten 100 und der Befehl zum Prozentualen fahren ist position&amp;quot;,2 = &amp;quot;Homematic Style Bsp.: Rollo Oben 100,Rollo Unten 0 und der Befehl zum Prozentualen fahren ist pct&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Antifreeze || on, off|| ||Frostschutz an oder aus&lt;br /&gt;
|-&lt;br /&gt;
|ASC_AutoAstroModeEvening ||REAL, CIVIL, NAUTIC, ASTRONOMIC|| ||aktuell REAL,CIVIL,NAUTIC,ASTRONOMIC&lt;br /&gt;
|-&lt;br /&gt;
|ASC_AutoAstroModeEveningHorizon || || ||Höhe über Horizont wenn beim Attribut ASC_autoAstroModeEvening HORIZON ausgewählt&lt;br /&gt;
|-&lt;br /&gt;
|ASC_AutoAstroModeMorning ||REAL, CIVIL, NAUTIC, ASTRONOMIC|| ||aktuell REAL,CIVIL,NAUTIC,ASTRONOMIC&lt;br /&gt;
|-&lt;br /&gt;
|ASC_AutoAstroModeMorningHorizon || || ||Höhe über Horizont wenn beim Attribut ASC_autoAstroModeMorning HORIZON ausgewählt&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Closed_Pos || || ||in 10 Schritten von 0 bis 100,default Vorgabe ist abhängig vom Attribut ASC&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Down || astro, time, brightness || ||bei Astro wird Sonnenuntergang berechnet, bei time wird der Wert aus ASC_Time_Down_Early als Fahrzeit verwendet und bei brightness muss ASC_Time_Down_Early und ASC_Time_Down_Late korrekt gesetzt werden. Der Timer läuft dann nach ASC_Time_Down_Late Zeit,es wird aber in der Zeit zwischen ASC_Time_Down_Early und ASC_Time_Down_Late geschaut ob die als Attribut im Moduldevice hinterlegte ASC_brightnessMinVal erreicht wurde, wenn ja wird der Rolladen runter gefahren&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Mode_Down ||always, home, absent, off || ||wann darf die Automatik steuern. immer,niemals,bei abwesenheit des Roommate (ist kein Roommate und absent eingestellt wird gar nicht gesteuert)&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Mode_Up ||always, home, absent, off || ||wann darf die Automatik steuern. immer,niemals,bei abwesenheit des Roommate (ist kein Roommate und absent eingestellt wird gar nicht gesteuert)&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Drive_Offset || || ||maximale zufällige Verzögerung in Sekunden bei der Berechnung der Fahrzeiten, 0 bedeutet sofort, -1 bedeutet das das gleichwertige Attribut aus dem ASC-Device ausgewertet werden soll.&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Open_Pos || || ||in 10 Schritten von 0 bis 100,default Vorgabe ist abhängig vom Attribut ASC&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Partymode || on, off || ||schaltet den Partymodus an oder aus. Wird dann am ASC Device set ASC-DEVICE partyMode on geschalten, werden alle Fahrbefehle an den Rollläden, welche das Attribut auf on haben, zwischengespeichert und erst später ausgeführt&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Pos_Reading || || ||Name des Readings, welches die Position des Rollladen in Prozent an gibt. Wird bei unbekannten Device -ypen auch als set Befehl zum Fahren verwendet&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Pos_after_ComfortOpen || || ||in 10 Schritten von 0 bis 100, default Vorgabe ist abhängig vom Attribut ASC&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Roommate_Reading || || ||das Reading zum Roommate-Device, welches den Status wieder gibt&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Roommate_Device || || ||mit Komma getrennte Namen des/der Roommate-Device/s welche den/die Bewohner des Raumes vom Rollladen wieder gibt. Es macht nur Sinn in Schlaf- oder Kinderzimmern&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Time_Down_Early || || ||Sunset frühste Zeit zum Runterfahren&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Time_Down_Late || || ||Sunset späteste Zeit zum Runterfahren&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Time_Up_Early || || ||Sunrise frühste Zeit zum Hochfahren&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Time_Up_Late || || ||Sunrise späteste Zeit zum Hochfahren&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Time_Up_WE_Holiday || || ||Sunrise frühste Zeit zum Hochfahren am Wochenende und/oder Urlaub (we2holiday wird beachtet). Achtung! Sollte nicht größer sein als ASC_Time_Up_Late, sonst wird ASC_Time_Up_Late verwendet&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Up || astro, time, brightness || ||bei astro wird Sonnenaufgang berechnet, bei time wird der Wert aus ASC_Time_Up_Early als Fahrzeit verwendet und bei brightness müssen ASC_Time_Up_Early und ASC_Time_Up_Late korrekt gesetzt werden. Der Timer läuft dann nach ASC_Time_Up_Late Zeit, es wird aber in der Zeit zwischen ASC_Time_Up_Early und ASC_Time_Up_Late geschaut, ob die als Attribut im Moduldevice hinterlegte ASC_brightnessMinVal erreicht wurde. Wenn ja, wird der Rolladen runter gefahren&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Ventilate_Pos || || ||in 10 Schritten von 0 bis 100, default Vorgabe ist abhängig vom Attribut ASC&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Ventilate_Window_Open || || ||auf lüften, wenn das Fenster gekippt/geöffnet wird und aktuelle Position unterhalb der Lüften-Position ist&lt;br /&gt;
|-&lt;br /&gt;
|ASC_WindowRec || || ||Name des Fensterkontakts an welchen Fenster der Rollladen angebracht ist&lt;br /&gt;
|-&lt;br /&gt;
|ASC_WindowRec_subType || || ||Typ des verwendeten Fensterkontakts: twostate (optisch oder magnetisch) oder threestate (Drehgriffkontakt)&lt;br /&gt;
|-&lt;br /&gt;
|ASC_lock-out || soft, hard || ||stellt entsprechend den Aussperrschutz ein. Bei global aktiven Aussperrschutz (set ASC-Device lockOut soft) und einem Fensterkontakt open bleibt dann der Rolladen oben. Dies gilt nur bei Steuerbefehle über das ASC Modul. Stellt man global auf hard, wird bei entsprechender Möglichkeit versucht, den Rollladen hardwareseitig zu blockieren. Dann ist auch ein Fahren über die Taster nicht mehr möglich.&lt;br /&gt;
|-&lt;br /&gt;
|ASC_lock-outCmd || inhibit, blocked || ||set Befehl für das Rolladen-Device zum Hardware sperren. Der Befehl wird verwendet, wenn man &amp;quot;ASC_lock-out&amp;quot; auf hard setzt&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Self_Defense_Exclude || on, off || ||bei on Wert wird dieser Rolladen bei aktiven Self Defense und offenen Fenster nicht runter gefahren, wenn Residents absent ist.&lt;br /&gt;
|-&lt;br /&gt;
|ASC_Shading_Brightness_Sensor || || ||Sensor-Device, welches für die Lichtwerte verwendet wird. ACHTUNG! Findet auch Verwendung bei ASC_Down - brightness&lt;br /&gt;
|-&lt;br /&gt;
|ASC_BrightnessMinVal || || ||minimaler Lichtwert, bei dem Schaltbedingungen noch geprüft werden sollen / wird der Wert von -1 nicht geändert, so wird automatisch der Wert aus dem Moduldevice genommen&lt;br /&gt;
|-&lt;br /&gt;
|ASC_BrightnessMaxVal || || ||maximaler Lichtwert, bei dem Schaltbedingungen noch geprüft werden sollen / wird der Wert von -1 nicht geändert, so wird automatisch der Wert aus dem Moduldevice genommen&lt;br /&gt;
|-&lt;br /&gt;
|ASC_ShuttersPlace || window, terrace || ||wenn dieses Attribut auf terrace gesetzt ist und das Residence Device in den Status &amp;quot;done&amp;quot; geht und SelfDefence aktiv ist wird das Rollo geschlossen&lt;br /&gt;
|-  &lt;br /&gt;
|ASC_WiggleValue || || ||Wert um welchen sich die Position des Rollladens ändern soll &lt;br /&gt;
|-&lt;br /&gt;
| || || ||&lt;br /&gt;
|-  &lt;br /&gt;
| || || ||&lt;br /&gt;
|-&lt;br /&gt;
| || || ||    &lt;br /&gt;
|-  &lt;br /&gt;
| || || ||&lt;br /&gt;
|-&lt;br /&gt;
| || || ||    &lt;br /&gt;
|-  &lt;br /&gt;
| || || ||&lt;br /&gt;
|-&lt;br /&gt;
| || || ||        &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Hilfsmittel ==&lt;br /&gt;
tbd&lt;br /&gt;
&lt;br /&gt;
readingsGroup, um die diversen Level einzustellen: &lt;br /&gt;
&lt;br /&gt;
 define rg_ASC_Rollaeden_Level readingsGroup &amp;lt;Gerät&amp;gt;,&amp;lt;Closed_Pos&amp;gt;,&amp;lt;Open_Pos&amp;gt;,&amp;lt;Shading_Pos&amp;gt;,&amp;lt;Ventilate_Pos&amp;gt; (Rolladen_.*|Jalousie_.*)..:?ASC_Closed_Pos,?ASC_Open_Pos,?ASC_Shading_Pos,?ASC_Ventilate_Pos&lt;br /&gt;
 attr rg_ASC_Rollaeden_Level commands { ASC_Closed_Pos =&amp;gt; &#039;ASC_Closed_Pos:0,10,20,30,35,40,45,50,55,60,65,70,75,80,85,90,95,100&#039;,\&lt;br /&gt;
 ASC_Open_Pos =&amp;gt; &#039;ASC_Open_Pos:0,10,20,30,35,40,45,50,55,60,65,70,75,80,85,90,95,100&#039;,\&lt;br /&gt;
 ASC_Shading_Pos =&amp;gt; &#039;ASC_Shading_Pos:0,10,20,30,35,40,45,50,55,60,65,70,75,80,85,90,95,100&#039;,\&lt;br /&gt;
 ASC_Ventilate_Pos =&amp;gt; &#039;ASC_Ventilate_Pos:0,10,20,30,35,40,45,50,55,60,65,70,75,80,85,90,95,100&#039;}&lt;br /&gt;
[[Datei:ReadingsGroup ASC Level.png|600px|]]&lt;br /&gt;
readingsGroup, um die diversen Zeiten einzustellen:&lt;br /&gt;
&lt;br /&gt;
 define rg_ASC_Rollaeden_Times readingsGroup &amp;lt;Gerät&amp;gt;,&amp;lt;Stand&amp;gt;,&amp;lt;Time_Up_Early&amp;gt;,&amp;lt;Time_Up_WE&amp;gt;,&amp;lt;Time_Up_Late&amp;gt;,&amp;lt;Time_Down_Early&amp;gt;,&amp;lt;Time_Down_Late&amp;gt;,&amp;lt;Mode_Down&amp;gt;,&amp;lt;Mode_Up&amp;gt; (Rolladen_.*|Jalousie_.*)..:level,?ASC_Time_Up_Early,?ASC_Time_Up_WE_Holiday,?ASC_Time_Up_Late,?ASC_Time_Down_Early,?ASC_Time_Down_Late,?ASC_Mode_Down,?ASC_Mode_Up&lt;br /&gt;
 attr rg_ASC_Rollaeden_Times commands {level =&amp;gt; &#039;pct:0,10,20,30,35,40,45,50,55,60,65,70,75,80,85,90,95,100&#039;, \&lt;br /&gt;
 ASC_Mode_Down =&amp;gt; &#039;ASC_Mode_Down:always,absent,off&#039;,\&lt;br /&gt;
 ASC_Mode_Up =&amp;gt; &#039;ASC_Mode_Up:always,absent,off&#039;,\&lt;br /&gt;
 ASC_Time_Down_Early =&amp;gt; &#039;ASC_Time_Down_Early:15:00,15:15,15:30,15:45,16:00,16:15,16:30,16:45,17:00,17:15,17:30,17:45,18:00,18:15,18:30,18:45,19:00,19:15,19:30,19:45,20:00,20:15,20:30,20:45,21:00,21:15,21:30,21:45,22:00&#039;, \&lt;br /&gt;
 ASC_Time_Down_Late  =&amp;gt; &#039;ASC_Time_Down_Late:20:15,20:30,20:45,21:00,21:15,21:30,21:45,22:00,22:15,22:30,22:45,23:00,23:15,23:30&#039;,\&lt;br /&gt;
 ASC_Time_Up_Early =&amp;gt; &#039;ASC_Time_Up_Early:05:00,05:05,05:30,05:45,06:00,06:15,06:30,06:45,07:00,07:15,07:30,07:45,08:00,08:15,08:30,08:45,09:00,09:15,09:30,09:45,10:00&#039;, \&lt;br /&gt;
 ASC_Time_Up_Late =&amp;gt;&#039;ASC_Time_Up_Late:06:00,06:15,06:30,06:45,07:00,07:15,07:30,07:45,08:00,08:15,08:30,08:45,09:00,09:15,09:30,09:45,10:00&#039;,ASC_Time_Up_WE_Holiday =&amp;gt; &#039;ASC_Time_Up_WE_Holiday:06:00,06:15,06:30,06:45,07:00,07:15,07:30,07:45,08:00,08:15,08:30,08:45,09:00,09:15,09:30,09:45,10:00&#039;}&lt;br /&gt;
[[Datei:ReadingsGroup ASC Times.png|600px|]]&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* {{Link2Forum|Topic=92628|LinkText=&amp;quot;Thread zum Modul im Forum&amp;quot;}}&lt;br /&gt;
* {{Link2Forum|Topic=90751|LinkText=&amp;quot;Thread zur Entwicklung im Forum&amp;quot;}} &lt;br /&gt;
* {{Link2Forum|Topic=73964|LinkText=&amp;quot;Thread zu den Scripten von user cluni&amp;quot;}}, die der Entwicklung des Moduls zugrunde liegen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:Rollladensteuerung]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevIo&amp;diff=27364</id>
		<title>DevIo</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevIo&amp;diff=27364"/>
		<updated>2018-07-10T10:19:02Z</updated>

		<summary type="html">&lt;p&gt;Eisler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Dienstfunktionen für die Kommunikation per serieller Schnittstelle (USB/RS232), TCP/IP-Verbindung oder UNIX-Socket&lt;br /&gt;
|ModType=u&lt;br /&gt;
|ModForumArea=FHEM Development&lt;br /&gt;
|ModTechName=DevIo.pm&lt;br /&gt;
|ModOwner=rudolfkoenig ({{Link2FU|8|Forum}} / [[Benutzer Diskussion:Rudolfkoenig|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Das Modul [[DevIo]](.pm) ist für Modulentwickler gedacht, um Daten zwischen einem FHEM-Modul und bspw. einer seriellen Schnittstelle, einer TCP/IP-Verbindung oder einem UNIX-Socket auszutauschen. Es übernimmt dabei die gesamte Verbindungsverwaltung und Aufrechterhaltung innerhalb von FHEM und nimmt dem Modulentwickler daher die gesamte Verbindungsverwaltung (Aufbau, Initialisierung, Neu-Verbindung bei Abbruch, etc.) ab. Es berücksichtigt dabei Besonderheiten zwischen Unix-basierten Betriebssystemen und Windows.&lt;br /&gt;
&lt;br /&gt;
Es dient dabei lediglich dem Zweck einen Kommunikationskanal für eine Definition in FHEM zu etablieren und Daten darüber auszutauschen. Die Interpretation der empfangenen Daten obliegt dem Modul, welches die Verbindung via DevIo geöffnet hat. Die ausgetauschten Daten werden durch DevIo nicht verändert.&lt;br /&gt;
&lt;br /&gt;
= Allgemeine Funktionsweise =&lt;br /&gt;
DevIo hat das Ziel eine besonders einfache Möglichkeit für Modulentwickler zu schaffen, um einen dauerhaften Kommunikationskanal mit einem Hardware- oder Netzwerk-Gerät bzw. Service zu etablieren. Um eine Verbindung aufzubauen muss zunächst die Gegenstelle bekannt sein. Dazu muss vor dem Verbindungsaufbau in dem Internal &amp;lt;code&amp;gt;$hash-&amp;gt;{DeviceName}&amp;lt;/code&amp;gt; der jeweiligen Definition das Ziel hinterlegt werden. Dies kann bspw. eine serielle Schnittstelle sein (z.B. &amp;quot;/dev/ttyUSB0&amp;quot; oder &amp;quot;COM1&amp;quot; unter Windows) oder eine TCP/IP-Gegenstelle (z.B. &amp;quot;192.168.1.100:1012&amp;quot;) sein. Eine detaillierte Aufstellung der möglichen Verbindungsarten und deren Angabe in &amp;lt;code&amp;gt;$hash-&amp;gt;{DeviceName}&amp;lt;/code&amp;gt; gibt es im folgenden Kapitel [[#Unterstützte Verbindungsarten|Unterstützte Verbindungsarten]].&lt;br /&gt;
&lt;br /&gt;
Die Funktion [[#DevIo_OpenDev()|DevIo_OpenDev()]] baut dabei die entsprechende Verbindung für eine einzelne Definition in Form eines Filedeskriptors auf und registriert diesen in dem globalen Hash &amp;lt;code&amp;gt;%selectlist&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;selectlist&amp;quot;&amp;gt;[[DevelopmentModuleIntro#Wichtige_globale_Variablen_aus_fhem.pl|Development Module Introduction]] - Wichtige globale Variablen aus fhem.pl&amp;lt;/ref&amp;gt;. Sobald die Verbindung erfolgreich aufgebaut wurde, kann eine, durch den Modulautor mitgelieferte, Initialisierungs-Funktion ausgeführt werden, um die Kommunikation zu initialiseren (bspw. das Senden einer Authentifizierungs-/Loginsequenz oder aktivieren der Hardware, etc.).&lt;br /&gt;
&lt;br /&gt;
FHEM (respektive fhem.pl) prüft nun regelmäßig, ob Daten zum Lesen bereitstehen (also Daten empfangen wurden). Ist dies der Fall, so wird die [[DevelopmentModuleIntro#X_Read|X_Read()]]-Funktion des zugehörigen Moduls für die hinterlegte Definition aufgerufen. Hier können die Daten durch den Aufruf von [[#DevIo_SimpleRead()|DevIo_SimpleRead()]] nun eingelesen und verarbeitet werden. Das Senden von Daten ist durch den Aufruf von  [[#DevIo_SimpleWrite()|DevIo_SimpleWrite()]] sehr einfach möglich.&lt;br /&gt;
&lt;br /&gt;
Sollte die Verbindung zusammenbrechen (USB-Gerät abgezogen, Gerät per Netzwerk nicht mehr erreichbar, etc.), so erkennt dies DevIo und registriert die Definition in &amp;lt;code&amp;gt;%readyfnlist&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;selectlist&amp;quot; /&amp;gt;. FHEM führt nun regelmäßig die [[DevelopmentModuleIntro#X_Ready|X_Ready()]]-Funktion des zugehörigen Moduls&lt;br /&gt;
aus um zu prüfen, ob die Verbindung wieder aufgebaut werden kann. Hier wird nun durch die Ausführung von [[#DevIo_OpenDev()|DevIo_OpenDev()]] versucht die Verbindung wieder herzustellen.&lt;br /&gt;
&lt;br /&gt;
Sobald die Verbindung nicht mehr benötigt wird, oder FHEM bspw. beendet wird, kann die Verbindung via [[#DevIo_CloseDev()|DevIo_CloseDev()]] sauber geschlossen werden.&lt;br /&gt;
&lt;br /&gt;
= Unterstützte Verbindungsarten = &lt;br /&gt;
&lt;br /&gt;
Die folgenden Verbindungsarten können via DevIo realisiert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Verbindungsart !! Beispielangabe in&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;$hash-&amp;gt;{DeviceName}&amp;lt;/code&amp;gt; !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;Serielle Schnittstelle&#039;&#039;&#039;&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; |&lt;br /&gt;
* &amp;lt;code&amp;gt;/dev/ttyUSB0&amp;lt;/code&amp;gt; &lt;br /&gt;
* &amp;lt;code&amp;gt;/dev/ttyUSB0@9600&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;/dev/ttyUSB0@9600,7,E,2&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;/dev/ttyUSB0@directio&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;COM1@9600&amp;lt;/code&amp;gt;&lt;br /&gt;
|| &lt;br /&gt;
Durch Angabe eines Geräts in Form eines Gerätepfad (UNIX-basierte Betriebssysteme) oder der Schnittstellenbezeichnung aus Windows kann eine serielle Verbindung geöffnet werden. Der Gerätename kann zusätzliche Angaben zu Baudrate, Datenbits, Parität und Stoppbits enthalten um die Verbindung entsprechend zu konfiguieren. Diese Angaben sind durch ein &amp;quot;@&amp;quot; getrennt an die Gerätebezeichnung angehangen:&lt;br /&gt;
&lt;br /&gt;
Schematische Syntax: &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;&amp;lt;Gerät&amp;gt;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&#039;&#039;@&#039;&#039;&#039;&#039;&#039;&amp;lt;Baudrate&amp;gt;&#039;&#039;&#039;&#039;&#039;,&#039;&#039;&#039;&#039;&#039;&amp;lt;Datenbits&amp;gt;&#039;&#039;&#039;&#039;&#039;,&#039;&#039;&#039;&#039;&#039;&amp;lt;Parität&amp;gt;&#039;&#039;&#039;&#039;&#039;,&#039;&#039;&#039;&#039;&#039;&amp;lt;Stopbits&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Baudrate&amp;gt;&amp;lt;/code&amp;gt; - Eine gültige Taktfrequenz (Symbole pro Sekunde) mit der die Schnittstelle geöffnet werden soll (Beispiel: &amp;lt;code&amp;gt;9600&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;14400&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;115200&amp;lt;/code&amp;gt;, ...)&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Datenbits&amp;gt;&amp;lt;/code&amp;gt; - Die Anzahl an Datenbits (7 oder 8 Datenbits). Standardwert sofern nicht angegeben sind 8 Datenbits.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Parität&amp;gt;&amp;lt;/code&amp;gt; - Die zu verwendende Parität, sofern benötigt. Dabei bedeutet der Wert &amp;lt;code&amp;gt;N&amp;lt;/code&amp;gt; keine Parität, &amp;lt;code&amp;gt;O&amp;lt;/code&amp;gt; bedeutet ungleiche Parität (odd) und &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; bedeutet gleiche Parität (even) Standardwert sofern nicht angegeben ist &amp;lt;code&amp;gt;N&amp;lt;/code&amp;gt; (keine Parität).&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Stopbits&amp;gt;&amp;lt;/code&amp;gt; - Die Anzahl an zu verwendenden Stopbits (0, 1 oder 2 Stopbits). Standardwert sofern nicht angegeben sind 0 Stopbits.&lt;br /&gt;
&lt;br /&gt;
Wenn man unter Unix-basierten Betriebssystemen die Schnittstelle nicht explizit konfiguriert öffnen möchte, sondern das Gerät direkt öffnen möchte (und damit die OS-Einstellungen verwendet), kann man durch Angabe von &amp;lt;code&amp;gt;@directio&amp;lt;/code&amp;gt; die Konfiguration der Schnittstelle umgehen und das Gerät direkt öffnen:&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;/dev/ttyUSB0&amp;lt;/font&amp;gt;@directio&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unter Windows verwendet man als Gerätename die entsprechende Schnittstellenbezeichung wie bspw. &amp;lt;code&amp;gt;COM1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;COM2&amp;lt;/code&amp;gt;, usw.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;TCP/IP-Verbindung&#039;&#039;&#039;&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &lt;br /&gt;
* &amp;lt;code&amp;gt;192.168.1.2:1012&amp;lt;/code&amp;gt; &lt;br /&gt;
* &amp;lt;code&amp;gt;raspberry:5000&amp;lt;/code&amp;gt;&lt;br /&gt;
||&lt;br /&gt;
Durch Angabe eines Hostnamen oder IP-Adresse und einem Port, kann eine TCP-Verbindung aufgebaut werden. Dazu muss Hostname/IP-Adresse und Port in folgendem Schema angegeben werden:&lt;br /&gt;
&lt;br /&gt;
Schematische Syntax: &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;Hostname/IP-Adresse&amp;gt;&#039;&#039;&#039;&#039;&#039;:&#039;&#039;&#039;&#039;&#039;&amp;lt;Port&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Hostname/IP-Adresse&amp;gt;&amp;lt;/code&amp;gt; - Der Hostname oder die IP-Adresse des zu verbindenden Gerätes/Server.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Port&amp;gt;&amp;lt;/code&amp;gt; - Der Zielport zu dem sich verbunden werden soll (0-65535).&lt;br /&gt;
&lt;br /&gt;
Die Verbindung kann optional verschlüsselt via SSL/TLS aufgebaut werden. Dazu muss das Internal &amp;lt;code&amp;gt;$hash-&amp;gt;{SSL}&amp;lt;/code&amp;gt; auf 1 gesetzt werden.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;UNIX-Socket&#039;&#039;&#039;&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot;|&lt;br /&gt;
* &amp;lt;code&amp;gt;UNIX:SEQPACKET:/var/tmp/me_avm_home_external.ctl&amp;lt;/code&amp;gt; &lt;br /&gt;
* &amp;lt;code&amp;gt;UNIX:STREAM:/var/socket/con&amp;lt;/code&amp;gt;&lt;br /&gt;
|| &lt;br /&gt;
Durch Angabe eines Pfads zu einem UNIX-Domain-Socket kann eine Kommunikation mit einem anderen Prozess aufgebaut werden (Inter-Prozess-Kommunikation). Man kann den Socket dabei paketorientiert (&amp;quot;SEQPACKET&amp;quot;) oder als Stream (&amp;quot;STREAM&amp;quot;) öffnen. &lt;br /&gt;
&lt;br /&gt;
Schematische Syntax: &amp;lt;code&amp;gt;UNIX:&#039;&#039;&amp;lt;Typ&amp;gt;&#039;&#039;:&#039;&#039;&amp;lt;Pfad&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Typ&amp;gt;&amp;lt;/code&amp;gt; - Der Typ des Sockets. Für einen paketorientierten Socket ist hier &amp;lt;code&amp;gt;SEQPACKET&amp;lt;/code&amp;gt; zu verwenden, für einen streamorientierten Socket &amp;lt;code&amp;gt;STREAM&amp;lt;/code&amp;gt;. Der Typ muss immer mit angegeben werden.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Pfad&amp;gt;&amp;lt;/code&amp;gt; - Der Pfad im Dateisystem zu dem gewünschten UNIX-Socket&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;FHEM IO-Modul&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; |&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM:DEVIO:Firmata_SerielleSchnittstelle@9600&amp;lt;/code&amp;gt; &lt;br /&gt;
|| &lt;br /&gt;
Beschreibung siehe {{Link2Forum|Topic=46276}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Wichtige Internals zur Konfiguration =&lt;br /&gt;
&lt;br /&gt;
Da DevIo ausschließlich definitionsbezogen arbeitet, erfolgt eine Konfiguration von DevIo über Internals, die im übergebenen &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; gesetzt werden müssen (oder können). Hiermit lässt sich das Verhalten von DevIo entsprechend beeinflussen. &lt;br /&gt;
&lt;br /&gt;
Hier eine Auflistung von allen Internals, die DevIo beeinflussen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;min-width: 13em;&amp;quot; | Internal !!  Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &amp;lt;code&amp;gt;$hash-&amp;gt;{DeviceName}&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; |  Die Gegenstelle zu der eine Verbindung aufgebaut werden soll. Die möglichen Werte und deren Syntax ist im Kapitel [[#Unterstützte Verbindungsarten|Unterstützte Verbindungsarten]] genauer beschrieben.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &amp;lt;code&amp;gt;$hash-&amp;gt;{nextOpenDelay}&amp;lt;/code&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die Zeit in Sekunden, welche im Falle eines Verbindungsabbruchs gewartet werden soll, bevor ein erneuter Verbindungsversuch stattfindet. &lt;br /&gt;
&lt;br /&gt;
Standardwert: 60 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &amp;lt;code&amp;gt;$hash-&amp;gt;{TIMEOUT}&amp;lt;/code&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die maximale Zeit in Sekunden für den Aufbau einer TCP/IP-Verbindung. Sollte diese Zeit überschritten werden, bricht der Verbindungsaufbau mit einer Fehlermeldung ab.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG:&#039;&#039;&#039; Sollte beim Aufruf von [[#DevIo_OpenDev|DevIo_OpenDev()]] keine Callback-Funktion parametrisiert sein und die Gegenstelle antwortet beim Verbindungsaufbau nicht, so wird FHEM für die Dauer von &amp;lt;code&amp;gt;$hash-&amp;gt;{TIMEOUT}&amp;lt;/code&amp;gt; blockiert.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 3 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &amp;lt;code&amp;gt;$hash-&amp;gt;{SSL}&amp;lt;/code&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Flag (0 oder 1), ob eine TCP/IP-Verbindung verschlüsselt (via SSL/TLS) aufgebaut werden soll. Wenn dieses Flag auf 1 gesetzt ist, wird nach erfolgtem Verbindungsaufbau eine SSL-Session initiiert.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0 (keine Verschlüsselung)&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &amp;lt;code&amp;gt;$hash-&amp;gt;{devioLoglevel}&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Das Loglevel in dem &amp;lt;code&amp;gt;disconnected&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;reappeared&amp;lt;/code&amp;gt; Meldungen geloggt werden sollen. Standardmäßig werden solche Verbindungsabbrüche (&amp;lt;code&amp;gt;disconnected&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;reappeared&amp;lt;/code&amp;gt;) im Loglevel 1 geloggt. Die erfolgreiche Erstverbindung wird standardmäßig im Loglevel 3 geloggt. Durch das Setzen von &amp;lt;code&amp;gt;$hash-&amp;gt;{devioLoglevel}&amp;lt;/code&amp;gt; werden diese Meldungen allesamt in dem gesetzten Loglevel ausgegeben. Details dazu siehe {{Link2Forum|Topic=61970}}.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Die Funktionen =&lt;br /&gt;
== DevIo_OpenDev() ==&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$error = DevIo_OpenDev($hash, $reopen, $initfn);&lt;br /&gt;
$error = DevIo_OpenDev($hash, $reopen, $initfn, $callback);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_OpenDev() öffnet eine Verbindung zu dem Endpunkt der in &amp;lt;code&amp;gt;$hash-&amp;gt;{DeviceName}&amp;lt;/code&amp;gt; hinterlegt ist. Sobald die Verbindung erfolgreich hergestellt wurde, wird die Funktion &amp;lt;code&amp;gt;$initfn&amp;lt;/code&amp;gt; ausgeführt, sofern gesetzt, um die Verbindung zu initialisieren. Sofern eine TCP/IP-Verbindung hergestellt wird, kann eine optionale Callback-Funktion &amp;lt;code&amp;gt;$callback&amp;lt;/code&amp;gt; übergeben werden um den Verbindungsaufbau non-blocking durchzuführen.&lt;br /&gt;
&lt;br /&gt;
Der Rückgabewert enthält im Fehlerfall eine entsprechende Fehlermeldung. Im Erfolgsfall wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben. Sofern eine TCP/IP-Verbindung hergestellt wird und eine Callback-Funktion &amp;lt;code&amp;gt;$callback&amp;lt;/code&amp;gt; übergeben wurde, wird immer &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben, da ein evtl. Fehler an diese Callback-Funktion nach dem erfolgten Verbindungsversuch mitgeteilt wird.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | Die Hash-Referenz der Definition, für die eine Verbindung geöffnet werden soll&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$reopen&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&amp;lt;br&amp;gt;can be &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | Flag (0 oder 1), ob es sich um einen erneuten Verbindungsversuch handelt (im Rahmen der [[DevelopmentModuleIntro#X_Ready|X_Ready()]]-Funktion). Sollte es der erste Verbindungsversuch sein, so muss dieser Parameter den Wert 0 besitzen.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$initfn&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&amp;lt;br&amp;gt;can be &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt;&#039;&#039;&lt;br /&gt;
|| Der Name (als Zeichenkette) oder die Referenz auf eine Modulfunktion, welche optional nach dem erfolgreichen Aufbau/Wiederaufbau der Verbindung ausgeführt werden soll. Im Rahmen dieser Funktion kann weiterführende Kommunikation über die aufgebaute Verbindung erfolgen um zum Beispiel eine Loginsequenz oder eine Konfiguration der Gegenseite vorzunehmen, bevor die Verbindung allgemein benutzt werden kann. &lt;br /&gt;
&lt;br /&gt;
Die Funktion welche in &amp;lt;code&amp;gt;$initfn&amp;lt;/code&amp;gt; angegeben wurde, wird mit folgenden Parametern aufgerufen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$ret = MYMODULE_InitFn($hash)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn der Rückgabewert &amp;lt;code&amp;gt;$ret&amp;lt;/code&amp;gt; einen Inhalt zurückliefert, wird die Initialisierung als fehlgeschlagen bewertet und die Verbindung wieder geschlossen um einen neuen Verbindungsversuch zu einem späteren Zeitpunkt zu versuchen. Der genaue Inhalt von &amp;lt;code&amp;gt;$ret&amp;lt;/code&amp;gt; ist dabei unerheblich, da er nicht weiterverwendet wird. Die Rückgabewerte &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; signalisieren dabei eine erfolgreiche Initialisierung.&lt;br /&gt;
&lt;br /&gt;
Beispiel: &lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;MYMODULE_InitFn&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;\&amp;amp;MYMODULE_InitFn&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Der Name (als Zeichenkette) oder die Referenz auf eine Modulfunktion, welche aufgerufen werden soll um evtl. Fehlermeldungen beim Verbindungsaufbau einer TCP/IP-Verbindung an das Modul zurückzuliefern. Wenn eine Callback-Funktion gesetzt ist, erfolgt der Verbindungsaufbau non-blocking. Andernfalls wartet DevIo_OpenDev() bis die Verbindung steht bzw. ein Fehler auftritt (z.B. Timeout).&lt;br /&gt;
&lt;br /&gt;
Die Funktion welche in &amp;lt;code&amp;gt;$callback&amp;lt;/code&amp;gt; angegeben wurde, wird mit folgenden Parametern aufgerufen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;MYMODULE_ConnectCallbackFn($hash, $error)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Argument &amp;lt;code&amp;gt;$error&amp;lt;/code&amp;gt; enthält dabei eine Fehlermeldung als Zeichenkette welche die aufgetretene Fehlermeldung enthält. Der Rückgabewert der Callback-Funktion wird nicht ausgewertet und ist daher irrelevant.&lt;br /&gt;
&lt;br /&gt;
Beispiel: &lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;MYMODULE_ConnectCallbackFn&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;\&amp;amp;MYMODULE_ConnectCallbackFn&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewert:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabe!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$error &amp;lt;/code&amp;gt;&#039;&#039;&#039; || Eine Fehlermeldung als Zeichenkette, sollte der Verbindungsaufbau fehlschlagen. Im Erfolgsfall wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wenn es sich um eine TCP/IP-Verbindung handelt und eine Callback-Funktion als Parameter &amp;lt;code&amp;gt;$callback&amp;lt;/code&amp;gt; angegeben wurde, wird immer &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben, andernfalls erfolgt der Verbindungsaufbau blocking und eine evtl. Fehlermeldung wird als Rückgabewert zurückgegeben. Im Erfolgsfall wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_IsOpen() ==&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$status = DevIo_IsOpen($hash);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_IsOpen() prüft, ob eine Verbindung für &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; geöffnet ist. Falls ja, wird das zugehörige IO-Objekt zurückgegeben, andernfalls &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Diese Funktion kann dabei direkt in typischen if-Konstrukten verwendet werden um zu prüfen, ob eine Verbindung für die jeweilige Definition geöffnet oder geschlossen ist.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, deren Verbindung geprüft werden soll.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$status&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Der Status der Verbindung. Wenn eine Verbindung besteht, wird das zugehörige IO-Objekt zurückgegeben. Falls keine Verbindung besteht, wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_SimpleRead() ==&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$buf = DevIo_SimpleRead($hash);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_SimpleRead() liest anstehende Daten für die Verbindung von &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; ein und gibt diese zurück.&lt;br /&gt;
&lt;br /&gt;
Sollte beim Versuch Daten zu lesen eine geschlossene Verbindung erkannt werden, so wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben und die Verbindung geschlossen. Es erfolgt zu einem späteren Zeitpunkt (siehe Internal &amp;lt;code&amp;gt;$hash-&amp;gt;{nextOpenDelay}&amp;lt;/code&amp;gt; aus Kapitel [[#Wichtige Internals zur Konfiguration|Wichtige Internals zur Konfiguration]]) ein neuer Verbindungsversuch.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, für deren Verbindung aktuell anstehende Daten gelesen werden sollen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewert:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabe!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$buf&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die zu lesenden Daten als Zeichenkette. Im Falle eines Verbindungsabruchs wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_SimpleReadWithTimeout() ==&lt;br /&gt;
{{Randnotiz|RNTyp=Warn|RNText=&amp;lt;u&amp;gt;&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039;&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der Benutzung von DevIo_SimpleReadWithTimeout() wird FHEM für die Dauer von bis zu &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden blockiert, sollten keine Daten bis dahin zum Lesen bereitstehen.}}&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$buf = DevIo_SimpleReadWithTimeout($hash, $timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_SimpleReadWithTimeout() wartet maximal &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden bis die Verbindung von &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; Daten zum einlesen bereitstellt und gibt diese zurück. Sollte nach dem Warten von &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; noch immer keine Daten zum Lesen bereitstehen, so wird ein Leerstring zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, für deren Verbindung aktuell anstehende Daten gelesen werden sollen.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die maximale Wartezeit in Sekunden.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewert:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabe!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$buf&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die zu lesenden Daten als Zeichenkette.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_TimeoutRead() ==&lt;br /&gt;
{{Randnotiz|RNTyp=Warn|RNText=&amp;lt;u&amp;gt;&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039;&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der Benutzung von DevIo_TimeoutRead() wird FHEM für die Dauer von &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden blockiert.}}&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$buf = DevIo_TimeoutRead($hash, $timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_SimpleReadWithTimeout() wartet &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden und liest sämtliche Daten für die Verbindung von &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; ein, die in dieser Zeit eintreffen. Sollten keinerlei Daten während der Wartezeit eintreffen, so wird ein Leerstring zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, für deren Verbindung Daten eingelesen werden sollen.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Wartezeit in Sekunden.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewert:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabe!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$buf&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die eingelesenen Daten als Zeichenkette.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_SimpleWrite() ==&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;DevIo_SimpleWrite($hash, $msg, $type);&lt;br /&gt;
DevIo_SimpleWrite($hash, $msg, $type, $addnl);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_SimpleWrite() sendet den Inhalt von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; über die Verbindung von &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;. Mit den beiden Argumenten &amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$addnl&amp;lt;/code&amp;gt; kann die Formatierung von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; beeinflusst werden bevor die Daten tatsächlich gesendet werden.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, über deren Verbindung die Daten gesendet werden sollen.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die zu schreibenden Daten als Zeichenkette. Abhängig von &amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt; kann &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; Byte-Characters, HEX-Darstellungen oder normale ASCII-Zeichen enthalten.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Art des Inhalts von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt;. Abhängig von dem Inhalt von &amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt; wird &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; entsprechend geändert oder nicht. Desweiteren werden die Daten für Logausgaben evtl. leserlich gemacht.&lt;br /&gt;
&lt;br /&gt;
Folgende Werte sind möglich:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; enthält Daten in Byteform. Der Inhalt von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; wird 1:1 gesendet. Zur besseren Lesbarkeit werden die Daten bei Logausgaben in HEX-Darstellung umgewandelt.&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; enthält Binärdaten in HEX-Darstellung (0-9/A-F). Der Inhalt von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; wird vorher in Byteform umgewandelt und anschließend gesendet. In Logausgaben wird die HEX-Darstellung wie übergeben verwendet.&lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; enthält normale ASCII-Textzeichen. Der Inhalt von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; wird 1:1 gesendet. In Logausgaben wird &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; wie übergeben ausgegeben.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$addnl&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Flag (0/1) welches, sofern aktiviert, einen Zeilenumbruch (&amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt;) an &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; anfügt, bevor es gesendet wird.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_Expect() ==&lt;br /&gt;
{{Randnotiz|RNTyp=Warn|RNText=&amp;lt;u&amp;gt;&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039;&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Bei der Benutzung von DevIo_Expect() wird FHEM für die Dauer von bis zu &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden blockiert.&lt;br /&gt;
# Sollte im ersten Versuch keine Antwort auf die zuvor gesendeten Daten innerhalb von &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden eintreffen, wird die Verbindung geschlossen und neu aufgebaut. Es erfolgt dabei &#039;&#039;&#039;KEINE INITIALISIERUNG&#039;&#039;&#039; durch &amp;lt;code&amp;gt;$initfn&amp;lt;/code&amp;gt; aus [[#DevIo_OpenDev()|DevIo_OpenDev()]].&lt;br /&gt;
}}&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$buf = DevIo_Expect($hash, $msg, $timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_Expect() sendet den Inhalt von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; und wartet bis zu &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden auf eine Antwort. Sollte in dieser Zeit keine Antwort eintreffen, so wird die Verbindung einmalig geschlossen, erneut geöffnet (ohne Aufruf einer Initialisierungs-Funktion) und der Vorgang wiederholt. Die empfangene Antwort wird anschließend als Funktionsergebnis zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wenn beim ersten Versuch keine Antwort innerhalb von &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden eintrifft, wird das Event &amp;lt;code&amp;gt;FAILED&amp;lt;/code&amp;gt; generiert (siehe [[#Generierte Events|Generierte Events]]). Anschließend wird die Verbindung geschlossen, neu geöffnet (ohne Initialisierung) und &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; erneut gesendet. Sollte nun eine Antwort eintreffen, so wird das Event &amp;lt;code&amp;gt;CONNECTED&amp;lt;/code&amp;gt; generiert und die empfangene Antwort zurückgegeben. Sollte dennoch keine Antwort eintreffen, so wird das Event &amp;lt;code&amp;gt;DISCONNECTED&amp;lt;/code&amp;gt; getriggert.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, für deren Verbindung Daten gesendet und anschließen gelesen werden sollen.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die zu sendenden Daten auf die eine Antwort erwartet wird. Die Daten werden ohne Konvertierung direkt gesendet (entspricht &amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt; gleich &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; bei [[#DevIo_SimpleWrite()|DevIo_SimpleWrite]]).&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die maximale Wartezeit in Sekunden bis zum Eintreffen einer Antwort.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewert:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabe!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$buf&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die empfangene Antwort. Sollte es zu einem Fehler gekommen sein (Senden von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; fehlgeschlagen, keine Antwort innerhalb von &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden erhalten, etc.), so wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_CloseDev() ==&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;DevIo_CloseDev($hash);&lt;br /&gt;
DevIo_CloseDev($hash, $isFork);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_CloseDev() schließt eine evtl. geöffnete Verbindung für die Definition &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Optional kann man mit dem Flag &amp;lt;code&amp;gt;$isFork&amp;lt;/code&amp;gt; angeben, dass man sich aktuell in einem Fork vom Hauptprozess befindet. Dadurch wird beim Schließen von seriellen Verbindungen die Kommunikationsparameter nicht zurückgesetzt. Dies verhindert einen Ausfall der nachwievor bestehenden Verbindung im Hauptprozess.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, deren Verbindung geschlossen werden soll.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$isFork&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Flag (0 oder 1), welches angibt, dass DevIo_CloseDev() innerhalb eines geforkten Kindprozess ausgeführt wird. Dadurch werden serielle Verbindung so geschlossen, dass dabei die Kommunikationsparameter (Baudrate, Datenbits, Parität, etc.) nicht zurückgesetzt werden. Dieses Flag wird primär von dem Modul [[Blocking_Call|Blocking.pm]] verwendet um Verbindungen in einem geforkten Kindprozess zu schließen, ohne die Verbindung im Hauptprozess zu beeinträchtigen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0 (Verbindung wird im Hauptprozess geschlossen)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Generierte Events =&lt;br /&gt;
Die Funktionen von DevIo generieren verschiedene Events für die entsprechende Definition (parametrisiert durch &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;). Diese können bspw. in der [[DevelopmentModuleIntro#X_Notify|Notify]]-Funktion des entsprechenden Moduls verarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Übersicht der generierten Events:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Event !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;code&amp;gt;CONNECTED&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die Verbindung wurde nach einem Verbindungsabbruch erfolgreich wieder aufgebaut. Dieses Event wird nur nach einem erfolgreichen Reconnect von einer zuvor verlorenen Verbindung generiert. Es wird &#039;&#039;&#039;nicht&#039;&#039;&#039; nach der erfolgreichen Initialverbindung generiert.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;code&amp;gt;DISCONNECTED&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die Verbindung ist zusammengebrochen. Es wird ein neuer Verbindungsversuch nach &amp;lt;code&amp;gt;$hash-&amp;gt;{nextOpenDelay}&amp;lt;/code&amp;gt; Sekunden erfolgen (siehe [[#Wichtige Internals zur Konfiguration|Wichtige Internals zur Konfiguration]]).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;code&amp;gt;FAILED&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Dieses Event wird nur bei der Nutzung der Funktion [[#DevIo_Expect|DevIo_Expect()]] generiert, sofern es keine Antwort auf die zuvor gesendeten Daten gibt. Dies bedeutet konkret, dass Gerät hat nicht auf die eigene Anfrage geantwortet und die Verbindung wird daher neu aufgebaut. Details dazu, siehe [[#DevIo_Expect()|DevIo_Expect()]].&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Hinweis bei der Datenverarbeitung (Buffering) =&lt;br /&gt;
DevIo beschränkt sich ausschließlich auf die Verbindungsverwaltung. Sobald die Verbindung etabliert wurde, übernimmt FHEM (fhem.pl) die Verarbeitung von eingehenden Daten und informiert das entsprechende Modul durch Aufruf der [[DevelopmentModuleIntro#X_Read|Read]]-Funktion. Dabei hat weder DevIo, noch FHEM selber Kenntnis von der Struktur der Daten. Sowohl DevIo, als auch FHEM selber können nicht erkennen, ob die empfangenen Daten weder vollständig, noch valide in ihrem Aufbau sind. Dies alles obliegt dem Modul, welches die Verbindung initiiert hat. Man sollte daher immer davon ausgehen, dass beim Lesen von Daten (in [[DevelopmentModuleIntro#X_Read|X_Read()]]) diese unvollständig sein können, oder sogar mehrere Datagramme enthalten sein können. &lt;br /&gt;
&lt;br /&gt;
Dazu stellt DevIo dem Modulentwickler das Internal &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; in der jeweiligen Definition zur Verfügung um dort Daten zwischenzuspeichern, bis ein vollständiges Datagramm daraus verarbeitet werden kann. Alle eingelesenen Daten werden dazu in der [[DevelopmentModuleIntro#X_Read|Read]]-Funktion immer an &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; hinten angehangen. Sobald ein vollständiges Datagramm in &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; erkannt wurde, wird dieses herausgenommen und verarbeitet, solange bis kein vollständiges Datagramm in &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; erkennbar ist. Hierbei ist jedoch entscheidend, woran man ein abgeschlossenes Datagramm erkennen kann. Dies kann je nach Protokoll sehr unterschiedlich sein (bspw. Zeilenumbruch &amp;lt;code&amp;gt;\r\n&amp;lt;/code&amp;gt; oder ein Semikolon &amp;lt;code&amp;gt;;&amp;lt;/code&amp;gt;, etc.). &lt;br /&gt;
&lt;br /&gt;
DevIo initialisiert &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; nach dem erfolgreichen Verbindungsaufbau mit einem Leerstring (&amp;lt;code&amp;gt;&amp;quot;&amp;quot;&amp;lt;/code&amp;gt;). Sobald eine Verbindung mit [[#DevIo_CloseDev()|DevIo_CloseDev()]] geschlossen wird, wird &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; gelöscht.&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie man ein solches Buffering verwendet für ein Protokoll, welches Datagramme durch einen Zeilenumbruch &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt; (Newline) abtrennt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub MY_MODULE_Read($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
  my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
  &lt;br /&gt;
  my $data = DevIo_SimpleRead($hash);&lt;br /&gt;
  return if(!defined($data)); # connection lost&lt;br /&gt;
  &lt;br /&gt;
  my $buffer = $hash-&amp;gt;{PARTIAL};&lt;br /&gt;
  &lt;br /&gt;
  Log3 $name, 5, &amp;quot;MY_MODULE ($name) - received $data (buffer contains: $buffer)&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  # concat received data to $buffer&lt;br /&gt;
  $buffer .= $data;&lt;br /&gt;
&lt;br /&gt;
  # as long as the buffer contains newlines (complete datagramm)&lt;br /&gt;
  while($buffer =~ m/\n/)&lt;br /&gt;
  {&lt;br /&gt;
    my $msg;&lt;br /&gt;
    &lt;br /&gt;
    # extract the complete message ($msg), everything else is assigned to $buffer&lt;br /&gt;
    ($msg, $buffer) = split(&amp;quot;\n&amp;quot;, $buffer, 2);&lt;br /&gt;
    &lt;br /&gt;
    # remove trailing whitespaces&lt;br /&gt;
    chomp $msg;&lt;br /&gt;
&lt;br /&gt;
    # update $hash-&amp;gt;{PARTIAL} with the current buffer content&lt;br /&gt;
    $hash-&amp;gt;{PARTIAL} = $buffer; &lt;br /&gt;
    &lt;br /&gt;
    # parse the extracted message&lt;br /&gt;
    MY_MODULE_ParseMessage($hash, $msg);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Beispielimplementierung in einem Modul =&lt;br /&gt;
== serielle Verbindung ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
package main;&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
use DevIo; # load DevIo.pm if not already loaded&lt;br /&gt;
&lt;br /&gt;
# called upon loading the module MY_MODULE&lt;br /&gt;
sub MY_MODULE_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
&lt;br /&gt;
  $hash-&amp;gt;{DefFn}    = &amp;quot;MY_MODULE_Define&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{UndefFn}  = &amp;quot;MY_MODULE_Undef&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{SetFn}    = &amp;quot;MY_MODULE_Set&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{ReadFn}   = &amp;quot;MY_MODULE_Read&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{ReadyFn}  = &amp;quot;MY_MODULE_Ready&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when a new definition is created (by hand or from configuration read on FHEM startup)&lt;br /&gt;
sub MY_MODULE_Define($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash, $def) = @_;&lt;br /&gt;
  my @a = split(&amp;quot;[ \t]+&amp;quot;, $def);&lt;br /&gt;
&lt;br /&gt;
  my $name = $a[0];&lt;br /&gt;
  &lt;br /&gt;
  # $a[1] is always equals the module name &amp;quot;MY_MODULE&amp;quot;&lt;br /&gt;
  &lt;br /&gt;
  # first argument is a serial device (e.g. &amp;quot;/dev/ttyUSB0@9600&amp;quot;)&lt;br /&gt;
  my $dev = $a[2]; &lt;br /&gt;
&lt;br /&gt;
  return &amp;quot;no device given&amp;quot; unless($dev);&lt;br /&gt;
  &lt;br /&gt;
  # add a default baud rate (9600), if not given by user&lt;br /&gt;
  $dev .= &#039;@9600&#039; if(not $dev =~ m/\@\d+$/);&lt;br /&gt;
  &lt;br /&gt;
  $hash-&amp;gt;{DeviceName} = $dev;&lt;br /&gt;
  &lt;br /&gt;
  # close connection if maybe open (on definition modify)&lt;br /&gt;
  DevIo_CloseDev($hash) if(DevIo_IsOpen($hash));  &lt;br /&gt;
  &lt;br /&gt;
  # open connection with custom init function&lt;br /&gt;
  my $ret = DevIo_OpenDev($hash, 0, &amp;quot;MY_MODULE_Init&amp;quot;); &lt;br /&gt;
 &lt;br /&gt;
  return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when definition is undefined &lt;br /&gt;
# (config reload, shutdown or delete of definition)&lt;br /&gt;
sub MY_MODULE_Undef($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash, $name) = @_;&lt;br /&gt;
 &lt;br /&gt;
  # close the connection &lt;br /&gt;
  DevIo_CloseDev($hash);&lt;br /&gt;
  &lt;br /&gt;
  return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called repeatedly if device disappeared&lt;br /&gt;
sub MY_MODULE_Ready($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
  &lt;br /&gt;
  # try to reopen the connection in case the connection is lost&lt;br /&gt;
  return DevIo_OpenDev($hash, 1, &amp;quot;MY_MODULE_Init&amp;quot;); &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when data was received&lt;br /&gt;
sub MY_MODULE_Read($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
  my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
  &lt;br /&gt;
  # read the available data&lt;br /&gt;
  my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
  &lt;br /&gt;
  # stop processing if no data is available (device disconnected)&lt;br /&gt;
  return if(!defined($buf));&lt;br /&gt;
  &lt;br /&gt;
  Log3 $name, 5, &amp;quot;MY_MODULE ($name) - received: $buf&amp;quot;; &lt;br /&gt;
  &lt;br /&gt;
  #&lt;br /&gt;
  # do something with $buf, e.g. generate readings, send answers via DevIo_SimpleWrite(), ...&lt;br /&gt;
  #&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called if set command is executed&lt;br /&gt;
sub MY_MODULE_Set($$@)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash, $name, $cmd) = @_;&lt;br /&gt;
    my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
    &lt;br /&gt;
    my $usage = &amp;quot;unknown argument $cmd, choose one of statusRequest:noArg on:noArg off:noArg&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    if($cmd eq &amp;quot;statusRequest&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;get_status\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    elsif($cmd eq &amp;quot;on&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;on\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    elsif($cmd eq &amp;quot;off&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;off\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
        return $usage;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
# will be executed upon successful connection establishment (see DevIo_OpenDev())&lt;br /&gt;
sub MY_MODULE_Init($)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash) = @_;&lt;br /&gt;
&lt;br /&gt;
    # send a status request to the device&lt;br /&gt;
    DevIo_SimpleWrite($hash, &amp;quot;get_status\r\n&amp;quot;, 2);&lt;br /&gt;
    &lt;br /&gt;
    return undef; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== TCP/IP-Verbindung == &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
package main;&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
use DevIo; # load DevIo.pm if not already loaded&lt;br /&gt;
&lt;br /&gt;
# called upon loading the module MY_MODULE&lt;br /&gt;
sub MY_MODULE_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
&lt;br /&gt;
  $hash-&amp;gt;{DefFn}    = &amp;quot;MY_MODULE_Define&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{UndefFn}  = &amp;quot;MY_MODULE_Undef&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{SetFn}    = &amp;quot;MY_MODULE_Set&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{ReadFn}   = &amp;quot;MY_MODULE_Read&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{ReadyFn}  = &amp;quot;MY_MODULE_Ready&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when a new definition is created (by hand or from configuration read on FHEM startup)&lt;br /&gt;
sub MY_MODULE_Define($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash, $def) = @_;&lt;br /&gt;
  my @a = split(&amp;quot;[ \t]+&amp;quot;, $def);&lt;br /&gt;
&lt;br /&gt;
  my $name = $a[0];&lt;br /&gt;
  &lt;br /&gt;
  # $a[1] is always equals the module name &amp;quot;MY_MODULE&amp;quot;&lt;br /&gt;
  &lt;br /&gt;
  # first argument is the hostname or IP address of the device (e.g. &amp;quot;192.168.1.120&amp;quot;)&lt;br /&gt;
  my $dev = $a[2]; &lt;br /&gt;
&lt;br /&gt;
  return &amp;quot;no device given&amp;quot; unless($dev);&lt;br /&gt;
  &lt;br /&gt;
  # add a default port (1012), if not explicitly given by user&lt;br /&gt;
  $dev .= &#039;:1012&#039; if(not $dev =~ m/:\d+$/);&lt;br /&gt;
  &lt;br /&gt;
  $hash-&amp;gt;{DeviceName} = $dev;&lt;br /&gt;
  &lt;br /&gt;
  # close connection if maybe open (on definition modify)&lt;br /&gt;
  DevIo_CloseDev($hash) if(DevIo_IsOpen($hash));  &lt;br /&gt;
  &lt;br /&gt;
  # open connection with custom init and error callback function (non-blocking connection establishment)&lt;br /&gt;
  DevIo_OpenDev($hash, 0, &amp;quot;MY_MODULE_Init&amp;quot;, &amp;quot;MY_MODULE_Callback&amp;quot;); &lt;br /&gt;
 &lt;br /&gt;
  return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when definition is undefined &lt;br /&gt;
# (config reload, shutdown or delete of definition)&lt;br /&gt;
sub MY_MODULE_Undef($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash, $name) = @_;&lt;br /&gt;
 &lt;br /&gt;
  # close the connection &lt;br /&gt;
  DevIo_CloseDev($hash);&lt;br /&gt;
  &lt;br /&gt;
  return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called repeatedly if device disappeared&lt;br /&gt;
sub MY_MODULE_Ready($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
  &lt;br /&gt;
  # try to reopen the connection in case the connection is lost&lt;br /&gt;
  return DevIo_OpenDev($hash, 1, &amp;quot;MY_MODULE_Init&amp;quot;, &amp;quot;MY_MODULE_Callback&amp;quot;); &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when data was received&lt;br /&gt;
sub MY_MODULE_Read($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
  my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
  &lt;br /&gt;
  # read the available data&lt;br /&gt;
  my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
  &lt;br /&gt;
  # stop processing if no data is available (device disconnected)&lt;br /&gt;
  return if(!defined($buf));&lt;br /&gt;
  &lt;br /&gt;
  Log3 $name, 5, &amp;quot;MY_MODULE ($name) - received: $buf&amp;quot;; &lt;br /&gt;
  &lt;br /&gt;
  #&lt;br /&gt;
  # do something with $buf, e.g. generate readings, send answers via DevIo_SimpleWrite(), ...&lt;br /&gt;
  #&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called if set command is executed&lt;br /&gt;
sub MY_MODULE_Set($$@)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash, $name, $cmd) = @_;&lt;br /&gt;
    my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
    &lt;br /&gt;
    my $usage = &amp;quot;unknown argument $cmd, choose one of statusRequest:noArg on:noArg off:noArg&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    if($cmd eq &amp;quot;statusRequest&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;get_status\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    elsif($cmd eq &amp;quot;on&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;on\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    elsif($cmd eq &amp;quot;off&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;off\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
        return $usage;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
# will be executed upon successful connection establishment (see DevIo_OpenDev())&lt;br /&gt;
sub MY_MODULE_Init($)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash) = @_;&lt;br /&gt;
&lt;br /&gt;
    # send a status request to the device&lt;br /&gt;
    DevIo_SimpleWrite($hash, &amp;quot;get_status\r\n&amp;quot;, 2);&lt;br /&gt;
    &lt;br /&gt;
    return undef; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# will be executed if connection establishment fails (see DevIo_OpenDev())&lt;br /&gt;
sub MY_MODULE_Callback($)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash, $error) = @_;&lt;br /&gt;
    my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
&lt;br /&gt;
    # create a log emtry with the error message&lt;br /&gt;
    Log3 $name, 5, &amp;quot;MY_MODULE ($name) - error while connecting: $error&amp;quot;; &lt;br /&gt;
    &lt;br /&gt;
    return undef; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Referenzen = &lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevIo&amp;diff=27363</id>
		<title>DevIo</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevIo&amp;diff=27363"/>
		<updated>2018-07-10T10:15:49Z</updated>

		<summary type="html">&lt;p&gt;Eisler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Dienstfunktionen für die Kommunikation per serieller Schnittstelle (USB/RS232), TCP/IP-Verbindung oder UNIX-Socket&lt;br /&gt;
|ModType=u&lt;br /&gt;
|ModForumArea=FHEM Development&lt;br /&gt;
|ModTechName=DevIo.pm&lt;br /&gt;
|ModOwner=rudolfkoenig ({{Link2FU|8|Forum}} / [[Benutzer Diskussion:Rudolfkoenig|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Das Modul [[DevIo]](.pm) ist für Modulentwickler gedacht, um Daten zwischen einem FHEM-Modul und bspw. einer seriellen Schnittstelle, einer TCP/IP-Verbindung oder einem UNIX-Socket auszutauschen. Es übernimmt dabei die gesamte Verbindungsverwaltung und Aufrechterhaltung innerhalb von FHEM und nimmt dem Modulentwickler daher die gesamte Verbindungsverwaltung (Aufbau, Initialisierung, Neu-Verbindung bei Abbruch, etc.) ab. Es berücksichtigt dabei Besonderheiten zwischen Unix-basierten Betriebssystemen und Windows.&lt;br /&gt;
&lt;br /&gt;
Es dient dabei lediglich dem Zweck einen Kommunikationskanal für eine Definition in FHEM zu etablieren und Daten darüber auszutauschen. Die Interpretation der empfangenen Daten obliegt dem Modul, welches die Verbindung via DevIo geöffnet hat. Die ausgetauschten Daten werden durch DevIo nicht verändert.&lt;br /&gt;
&lt;br /&gt;
= Allgemeine Funktionsweise =&lt;br /&gt;
DevIo hat das Ziel eine besonders einfache Möglichkeit für Modulentwickler zu schaffen, um einen dauerhaften Kommunikationskanal mit einem Hardware- oder Netzwerk-Gerät bzw. Service zu etablieren. Um eine Verbindung aufzubauen muss zunächst die Gegenstelle bekannt sein. Dazu muss vor dem Verbindungsaufbau in dem Internal &amp;lt;code&amp;gt;$hash-&amp;gt;{DeviceName}&amp;lt;/code&amp;gt; der jeweiligen Definition das Ziel hinterlegt werden. Dies kann bspw. eine serielle Schnittstelle sein (z.B. &amp;quot;/dev/ttyUSB0&amp;quot; oder &amp;quot;COM1&amp;quot; unter Windows) oder eine TCP/IP-Gegenstelle (z.B. &amp;quot;192.168.1.100:1012&amp;quot;) sein. Eine detaillierte Aufstellung der möglichen Verbindungsarten und deren Angabe in &amp;lt;code&amp;gt;$hash-&amp;gt;{DeviceName}&amp;lt;/code&amp;gt; gibt es im folgenden Kapitel [[#Unterstützte Verbindungsarten|Unterstützte Verbindungsarten]].&lt;br /&gt;
&lt;br /&gt;
Die Funktion [[#DevIo_OpenDev()|DevIo_OpenDev()]] baut dabei die entsprechende Verbindung für eine einzelne Definition in Form eines Filedeskriptors auf und registriert diesen in dem globalen Hash &amp;lt;code&amp;gt;%selectlist&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;selectlist&amp;quot;&amp;gt;[[DevelopmentModuleIntro#Wichtige_globale_Variablen_aus_fhem.pl|Development Module Introduction]] - Wichtige globale Variablen aus fhem.pl&amp;lt;/ref&amp;gt;. Sobald die Verbindung erfolgreich aufgebaut wurde, kann eine, durch den Modulautor mitgelieferte, Initialisierungs-Funktion ausgeführt werden, um die Kommunikation zu initialiseren (bspw. das Senden einer Authentifizierungs-/Loginsequenz oder aktivieren der Hardware, etc.).&lt;br /&gt;
&lt;br /&gt;
FHEM (respektive fhem.pl) prüft nun regelmäßig, ob Daten zum Lesen bereitstehen (also Daten empfangen wurden). Ist dies der Fall, so wird die [[DevelopmentModuleIntro#X_Read|X_Read()]]-Funktion des zugehörigen Moduls für die hinterlegte Definition aufgerufen. Hier können die Daten durch den Aufruf von [[#DevIo_SimpleRead()|DevIo_SimpleRead()]] nun eingelesen und verarbeitet werden. Das Senden von Daten ist durch den Aufruf von  [[#DevIo_SimpleWrite()|DevIo_SimpleWrite()]] sehr einfach möglich.&lt;br /&gt;
&lt;br /&gt;
Sollte die Verbindung zusammenbrechen (USB-Gerät abgezogen, Gerät per Netzwerk nicht mehr erreichbar, etc.), so erkennt dies DevIo und registriert die Definition in &amp;lt;code&amp;gt;%readyfnlist&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;selectlist&amp;quot; /&amp;gt;. FHEM führt nun regelmäßig die [[DevelopmentModuleIntro#X_Ready|X_Ready()]]-Funktion des zugehörigen Moduls&lt;br /&gt;
aus um zu prüfen, ob die Verbindung wieder aufgebaut werden kann. Hier wird nun durch die Ausführung von [[#DevIo_OpenDev()|DevIo_OpenDev()]] versucht die Verbindung wieder herzustellen.&lt;br /&gt;
&lt;br /&gt;
Sobald die Verbindung nicht mehr benötigt wird, oder FHEM bspw. beendet wird, kann die Verbindung via [[#DevIo_CloseDev()|DevIo_CloseDev()]] sauber geschlossen werden.&lt;br /&gt;
&lt;br /&gt;
= Unterstützte Verbindungsarten = &lt;br /&gt;
&lt;br /&gt;
Die folgenden Verbindungsarten können via DevIo realisiert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Verbindungsart !! Beispielangabe in&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;$hash-&amp;gt;{DeviceName}&amp;lt;/code&amp;gt; !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;Serielle Schnittstelle&#039;&#039;&#039;&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; |&lt;br /&gt;
* &amp;lt;code&amp;gt;/dev/ttyUSB0&amp;lt;/code&amp;gt; &lt;br /&gt;
* &amp;lt;code&amp;gt;/dev/ttyUSB0@9600&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;/dev/ttyUSB0@9600,7,E,2&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;/dev/ttyUSB0@directio&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;COM1@9600&amp;lt;/code&amp;gt;&lt;br /&gt;
|| &lt;br /&gt;
Durch Angabe eines Geräts in Form eines Gerätepfad (UNIX-basierte Betriebssysteme) oder der Schnittstellenbezeichnung aus Windows kann eine serielle Verbindung geöffnet werden. Der Gerätename kann zusätzliche Angaben zu Baudrate, Datenbits, Parität und Stoppbits enthalten um die Verbindung entsprechend zu konfiguieren. Diese Angaben sind durch ein &amp;quot;@&amp;quot; getrennt an die Gerätebezeichnung angehangen:&lt;br /&gt;
&lt;br /&gt;
Schematische Syntax: &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;&amp;lt;Gerät&amp;gt;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&#039;&#039;@&#039;&#039;&#039;&#039;&#039;&amp;lt;Baudrate&amp;gt;&#039;&#039;&#039;&#039;&#039;,&#039;&#039;&#039;&#039;&#039;&amp;lt;Datenbits&amp;gt;&#039;&#039;&#039;&#039;&#039;,&#039;&#039;&#039;&#039;&#039;&amp;lt;Parität&amp;gt;&#039;&#039;&#039;&#039;&#039;,&#039;&#039;&#039;&#039;&#039;&amp;lt;Stopbits&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Baudrate&amp;gt;&amp;lt;/code&amp;gt; - Eine gültige Taktfrequenz (Symbole pro Sekunde) mit der die Schnittstelle geöffnet werden soll (Beispiel: &amp;lt;code&amp;gt;9600&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;14400&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;115200&amp;lt;/code&amp;gt;, ...)&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Datenbits&amp;gt;&amp;lt;/code&amp;gt; - Die Anzahl an Datenbits (7 oder 8 Datenbits). Standardwert sofern nicht angegeben sind 8 Datenbits.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Parität&amp;gt;&amp;lt;/code&amp;gt; - Die zu verwendende Parität, sofern benötigt. Dabei bedeutet der Wert &amp;lt;code&amp;gt;N&amp;lt;/code&amp;gt; keine Parität, &amp;lt;code&amp;gt;O&amp;lt;/code&amp;gt; bedeutet ungleiche Parität (odd) und &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; bedeutet gleiche Parität (even) Standardwert sofern nicht angegeben ist &amp;lt;code&amp;gt;N&amp;lt;/code&amp;gt; (keine Parität).&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Stopbits&amp;gt;&amp;lt;/code&amp;gt; - Die Anzahl an zu verwendenden Stopbits (0, 1 oder 2 Stopbits). Standardwert sofern nicht angegeben sind 0 Stopbits.&lt;br /&gt;
&lt;br /&gt;
Wenn man unter Unix-basierten Betriebssystemen die Schnittstelle nicht explizit konfiguriert öffnen möchte, sondern das Gerät direkt öffnen möchte (und damit die OS-Einstellungen verwendet), kann man durch Angabe von &amp;lt;code&amp;gt;@directio&amp;lt;/code&amp;gt; die Konfiguration der Schnittstelle umgehen und das Gerät direkt öffnen:&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;/dev/ttyUSB0&amp;lt;/font&amp;gt;@directio&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unter Windows verwendet man als Gerätename die entsprechende Schnittstellenbezeichung wie bspw. &amp;lt;code&amp;gt;COM1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;COM2&amp;lt;/code&amp;gt;, usw.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;TCP/IP-Verbindung&#039;&#039;&#039;&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &lt;br /&gt;
* &amp;lt;code&amp;gt;192.168.1.2:1012&amp;lt;/code&amp;gt; &lt;br /&gt;
* &amp;lt;code&amp;gt;raspberry:5000&amp;lt;/code&amp;gt;&lt;br /&gt;
||&lt;br /&gt;
Durch Angabe eines Hostnamen oder IP-Adresse und einem Port, kann eine TCP-Verbindung aufgebaut werden. Dazu muss Hostname/IP-Adresse und Port in folgendem Schema angegeben werden:&lt;br /&gt;
&lt;br /&gt;
Schematische Syntax: &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;Hostname/IP-Adresse&amp;gt;&#039;&#039;&#039;&#039;&#039;:&#039;&#039;&#039;&#039;&#039;&amp;lt;Port&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Hostname/IP-Adresse&amp;gt;&amp;lt;/code&amp;gt; - Der Hostname oder die IP-Adresse des zu verbindenden Gerätes/Server.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Port&amp;gt;&amp;lt;/code&amp;gt; - Der Zielport zu dem sich verbunden werden soll (0-65535).&lt;br /&gt;
&lt;br /&gt;
Die Verbindung kann optional verschlüsselt via SSL/TLS aufgebaut werden. Dazu muss das Internal &amp;lt;code&amp;gt;$hash-&amp;gt;{SSL}&amp;lt;/code&amp;gt; auf 1 gesetzt werden.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;UNIX-Socket&#039;&#039;&#039;&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot;|&lt;br /&gt;
* &amp;lt;code&amp;gt;UNIX:SEQPACKET:/var/tmp/me_avm_home_external.ctl&amp;lt;/code&amp;gt; &lt;br /&gt;
* &amp;lt;code&amp;gt;UNIX:STREAM:/var/socket/con&amp;lt;/code&amp;gt;&lt;br /&gt;
|| &lt;br /&gt;
Durch Angabe eines Pfads zu einem UNIX-Domain-Socket kann eine Kommunikation mit einem anderen Prozess aufgebaut werden (Inter-Prozess-Kommunikation). Man kann den Socket dabei paketorientiert (&amp;quot;SEQPACKET&amp;quot;) oder als Stream (&amp;quot;STREAM&amp;quot;) öffnen. &lt;br /&gt;
&lt;br /&gt;
Schematische Syntax: &amp;lt;code&amp;gt;UNIX:&#039;&#039;&amp;lt;Typ&amp;gt;&#039;&#039;:&#039;&#039;&amp;lt;Pfad&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Typ&amp;gt;&amp;lt;/code&amp;gt; - Der Typ des Sockets. Für einen paketorientierten Socket ist hier &amp;lt;code&amp;gt;SEQPACKET&amp;lt;/code&amp;gt; zu verwenden, für einen streamorientierten Socket &amp;lt;code&amp;gt;STREAM&amp;lt;/code&amp;gt;. Der Typ muss immer mit angegeben werden.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Pfad&amp;gt;&amp;lt;/code&amp;gt; - Der Pfad im Dateisystem zu dem gewünschten UNIX-Socket&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;FHEM IO-Modul&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; |&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM:DEVIO:Firmata_SerielleSchnittstelle@9600&amp;lt;/code&amp;gt; &lt;br /&gt;
|| &lt;br /&gt;
Beschreibung siehe {{Link2Forum|Topic=46276}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Wichtige Internals zur Konfiguration =&lt;br /&gt;
&lt;br /&gt;
Da DevIo ausschließlich definitionsbezogen arbeitet, erfolgt eine Konfiguration von DevIo über Internals, die im übergebenen &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; gesetzt werden müssen (oder können). Hiermit lässt sich das Verhalten von DevIo entsprechend beeinflussen. &lt;br /&gt;
&lt;br /&gt;
Hier eine Auflistung von allen Internals, die DevIo beeinflussen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;min-width: 13em;&amp;quot; | Internal !!  Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &amp;lt;code&amp;gt;$hash-&amp;gt;{DeviceName}&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; |  Die Gegenstelle zu der eine Verbindung aufgebaut werden soll. Die möglichen Werte und deren Syntax ist im Kapitel [[#Unterstützte Verbindungsarten|Unterstützte Verbindungsarten]] genauer beschrieben.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &amp;lt;code&amp;gt;$hash-&amp;gt;{nextOpenDelay}&amp;lt;/code&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die Zeit in Sekunden, welche im Falle eines Verbindungsabbruchs gewartet werden soll, bevor ein erneuter Verbindungsversuch stattfindet. &lt;br /&gt;
&lt;br /&gt;
Standardwert: 60 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &amp;lt;code&amp;gt;$hash-&amp;gt;{TIMEOUT}&amp;lt;/code&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die maximale Zeit in Sekunden für den Aufbau einer TCP/IP-Verbindung. Sollte diese Zeit überschritten werden, bricht der Verbindungsaufbau mit einer Fehlermeldung ab.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG:&#039;&#039;&#039; Sollte beim Aufruf von [[#DevIo_OpenDev|DevIo_OpenDev()]] keine Callback-Funktion parametrisiert sein und die Gegenstelle antwortet beim Verbindungsaufbau nicht, so wird FHEM für die Dauer von &amp;lt;code&amp;gt;$hash-&amp;gt;{TIMEOUT}&amp;lt;/code&amp;gt; blockiert.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 3 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &amp;lt;code&amp;gt;$hash-&amp;gt;{SSL}&amp;lt;/code&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Flag (0 oder 1), ob eine TCP/IP-Verbindung verschlüsselt (via SSL/TLS) aufgebaut werden soll. Wenn dieses Flag auf 1 gesetzt ist, wird nach erfolgtem Verbindungsaufbau eine SSL-Session initiiert.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0 (keine Verschlüsselung)&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &amp;lt;code&amp;gt;$hash-&amp;gt;{devioLoglevel}&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Das Loglevel in dem &amp;lt;code&amp;gt;disconnected&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;reappeared&amp;lt;/code&amp;gt; Meldungen geloggt werden sollen. Standardmäßig werden solche Verbindungsabbrüche (&amp;lt;code&amp;gt;disconnected&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;reappeared&amp;lt;/code&amp;gt;) im Loglevel 1 geloggt. Die erfolgreiche Erstverbindung wird standardmäßig im Loglevel 3 geloggt. Durch das Setzen von &amp;lt;code&amp;gt;$hash-&amp;gt;{devioLoglevel}&amp;lt;/code&amp;gt; werden diese Meldungen allesamt in dem gesetzten Loglevel ausgegeben. Details dazu siehe {{Link2Forum|Topic=61970}}.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Die Funktionen =&lt;br /&gt;
== DevIo_OpenDev() ==&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$error = DevIo_OpenDev($hash, $reopen, $initfn);&lt;br /&gt;
$error = DevIo_OpenDev($hash, $reopen, $initfn, $callback);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_OpenDev() öffnet eine Verbindung zu dem Endpunkt der in &amp;lt;code&amp;gt;$hash-&amp;gt;{DeviceName}&amp;lt;/code&amp;gt; hinterlegt ist. Sobald die Verbindung erfolgreich hergestellt wurde, wird die Funktion &amp;lt;code&amp;gt;$initfn&amp;lt;/code&amp;gt; ausgeführt, sofern gesetzt, um die Verbindung zu initialisieren. Sofern eine TCP/IP-Verbindung hergestellt wird, kann eine optionale Callback-Funktion &amp;lt;code&amp;gt;$callback&amp;lt;/code&amp;gt; übergeben werden um den Verbindungsaufbau non-blocking durchzuführen.&lt;br /&gt;
&lt;br /&gt;
Der Rückgabewert enthält im Fehlerfall eine entsprechende Fehlermeldung. Im Erfolgsfall wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben. Sofern eine TCP/IP-Verbindung hergestellt wird und eine Callback-Funktion &amp;lt;code&amp;gt;$callback&amp;lt;/code&amp;gt; übergeben wurde, wird immer &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben, da ein evtl. Fehler an diese Callback-Funktion nach dem erfolgten Verbindungsversuch mitgeteilt wird.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | Die Hash-Referenz der Definition, für die eine Verbindung geöffnet werden soll&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$reopen&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&amp;lt;br&amp;gt;can be &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | Flag (0 oder 1), ob es sich um einen erneuten Verbindungsversuch handelt (im Rahmen der [[DevelopmentModuleIntro#X_Ready|X_Ready()]]-Funktion). Sollte es der erste Verbindungsversuch sein, so muss dieser Parameter den Wert 0 besitzen.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$initfn&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&amp;lt;br&amp;gt;can be &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt;&#039;&#039;&lt;br /&gt;
|| Der Name (als Zeichenkette) oder die Referenz auf eine Modulfunktion, welche optional nach dem erfolgreichen Aufbau/Wiederaufbau der Verbindung ausgeführt werden soll. Im Rahmen dieser Funktion kann weiterführende Kommunikation über die aufgebaute Verbindung erfolgen um zum Beispiel eine Loginsequenz oder eine Konfiguration der Gegenseite vorzunehmen, bevor die Verbindung allgemein benutzt werden kann. &lt;br /&gt;
&lt;br /&gt;
Die Funktion welche in &amp;lt;code&amp;gt;$initfn&amp;lt;/code&amp;gt; angegeben wurde, wird mit folgenden Parametern aufgerufen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$ret = MYMODULE_InitFn($hash)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn der Rückgabewert &amp;lt;code&amp;gt;$ret&amp;lt;/code&amp;gt; einen Inhalt zurückliefert, wird die Initialisierung als fehlgeschlagen bewertet und die Verbindung wieder geschlossen um einen neuen Verbindungsversuch zu einem späteren Zeitpunkt zu versuchen. Der genaue Inhalt von &amp;lt;code&amp;gt;$ret&amp;lt;/code&amp;gt; ist dabei unerheblich, da er nicht weiterverwendet wird. Die Rückgabewerte &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; signalisieren dabei eine erfolgreiche Initialisierung.&lt;br /&gt;
&lt;br /&gt;
Beispiel: &lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;MYMODULE_InitFn&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;\&amp;amp;MYMODULE_InitFn&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Der Name (als Zeichenkette) oder die Referenz auf eine Modulfunktion, welche aufgerufen werden soll um evtl. Fehlermeldungen beim Verbindungsaufbau einer TCP/IP-Verbindung an das Modul zurückzuliefern. Wenn eine Callback-Funktion gesetzt ist, erfolgt der Verbindungsaufbau non-blocking. Andernfalls wartet DevIo_OpenDev() bis die Verbindung steht bzw. ein Fehler auftritt (z.B. Timeout).&lt;br /&gt;
&lt;br /&gt;
Die Funktion welche in &amp;lt;code&amp;gt;$callback&amp;lt;/code&amp;gt; angegeben wurde, wird mit folgenden Parametern aufgerufen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;MYMODULE_ConnectCallbackFn($hash, $error)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Argument &amp;lt;code&amp;gt;$error&amp;lt;/code&amp;gt; enthält dabei eine Fehlermeldung als Zeichenkette welche die aufgetretene Fehlermeldung enthält. Der Rückgabewert der Callback-Funktion wird nicht ausgewertet und ist daher irrelevant.&lt;br /&gt;
&lt;br /&gt;
Beispiel: &lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;MYMODULE_ConnectCallbackFn&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;\&amp;amp;MYMODULE_ConnectCallbackFn&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewert:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabe!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$error &amp;lt;/code&amp;gt;&#039;&#039;&#039; || Eine Fehlermeldung als Zeichenkette, sollte der Verbindungsaufbau fehlschlagen. Im Erfolgsfall wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wenn es sich um eine TCP/IP-Verbindung handelt und eine Callback-Funktion als Parameter &amp;lt;code&amp;gt;$callback&amp;lt;/code&amp;gt; angegeben wurde, wird immer &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben, andernfalls erfolgt der Verbindungsaufbau blocking und eine evtl. Fehlermeldung wird als Rückgabewert zurückgegeben. Im Erfolgsfall wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_IsOpen() ==&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$status = DevIo_IsOpen($hash);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_IsOpen() prüft, ob eine Verbindung für &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; geöffnet ist. Falls ja, wird das zugehörige IO-Objekt zurückgegeben, andernfalls &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Diese Funktion kann dabei direkt in typischen if-Konstrukten verwendet werden um zu prüfen, ob eine Verbindung für die jeweilige Definition geöffnet oder geschlossen ist.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, deren Verbindung geprüft werden soll.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$status&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Der Status der Verbindung. Wenn eine Verbindung besteht, wird das zugehörige IO-Objekt zurückgegeben. Falls keine Verbindung besteht, wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_SimpleRead() ==&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$buf = DevIo_SimpleRead($hash);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_SimpleRead() liest anstehende Daten für die Verbindung von &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; ein und gibt diese zurück.&lt;br /&gt;
&lt;br /&gt;
Sollte beim Versuch Daten zu lesen eine geschlossene Verbindung erkannt werden, so wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben und die Verbindung geschlossen. Es erfolgt zu einem späteren Zeitpunkt (siehe Internal &amp;lt;code&amp;gt;$hash-&amp;gt;{nextOpenDelay}&amp;lt;/code&amp;gt; aus Kapitel [[#Wichtige Internals zur Konfiguration|Wichtige Internals zur Konfiguration]]) ein neuer Verbindungsversuch.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, für deren Verbindung aktuell anstehende Daten gelesen werden sollen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewert:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabe!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$buf&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die zu lesenden Daten als Zeichenkette. Im Falle eines Verbindungsabruchs wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_SimpleReadWithTimeout() ==&lt;br /&gt;
{{Randnotiz|RNTyp=Warn|RNText=&amp;lt;u&amp;gt;&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039;&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der Benutzung von DevIo_SimpleReadWithTimeout() wird FHEM für die Dauer von bis zu &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden blockiert, sollten keine Daten bis dahin zum Lesen bereitstehen.}}&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$buf = DevIo_SimpleReadWithTimeout($hash, $timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_SimpleReadWithTimeout() wartet maximal &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden bis die Verbindung von &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; Daten zum einlesen bereitstellt und gibt diese zurück. Sollte nach dem Warten von &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; noch immer keine Daten zum Lesen bereitstehen, so wird ein Leerstring zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, für deren Verbindung aktuell anstehende Daten gelesen werden sollen.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die maximale Wartezeit in Sekunden.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewert:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabe!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$buf&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die zu lesenden Daten als Zeichenkette.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_TimeoutRead() ==&lt;br /&gt;
{{Randnotiz|RNTyp=Warn|RNText=&amp;lt;u&amp;gt;&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039;&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der Benutzung von DevIo_TimeoutRead() wird FHEM für die Dauer von &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden blockiert.}}&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$buf = DevIo_TimeoutRead($hash, $timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_SimpleReadWithTimeout() wartet &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden und liest sämtliche Daten für die Verbindung von &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; ein, die in dieser Zeit eintreffen. Sollten keinerlei Daten während der Wartezeit eintreffen, so wird ein Leerstring zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, für deren Verbindung Daten eingelesen werden sollen.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Wartezeit in Sekunden.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewert:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabe!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$buf&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die eingelesenen Daten als Zeichenkette.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_SimpleWrite() ==&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;DevIo_SimpleWrite($hash, $msg, $type);&lt;br /&gt;
DevIo_SimpleWrite($hash, $msg, $type, $addnl);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_SimpleWrite() sendet den Inhalt von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; über die Verbindung von &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;. Mit den beiden Argumenten &amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$addnl&amp;lt;/code&amp;gt; kann die Formatierung von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; beeinflusst werden bevor die Daten tatsächlich gesendet werden.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, über deren Verbindung die Daten gesendet werden sollen.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die zu schreibenden Daten als Zeichenkette. Abhängig von &amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt; kann &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; Byte-Characters, HEX-Darstellungen oder normale ASCII-Zeichen enthalten.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Art des Inhalts von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt;. Abhängig von dem Inhalt von &amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt; wird &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; entsprechend geändert oder nicht. Desweiteren werden die Daten für Logausgaben evtl. leserlich gemacht.&lt;br /&gt;
&lt;br /&gt;
Folgende Werte sind möglich:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; enthält Daten in Byteform. Der Inhalt von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; wird 1:1 gesendet. Zur besseren Lesbarkeit werden die Daten bei Logausgaben in HEX-Darstellung umgewandelt.&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; enthält Binärdaten in HEX-Darstellung (0-9/A-F). Der Inhalt von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; wird vorher in Byteform umgewandelt und anschließend gesendet. In Logausgaben wird die HEX-Darstellung wie übergeben verwendet.&lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; enthält normale ASCII-Textzeichen. Der Inhalt von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; wird 1:1 gesendet. In Logausgaben wird &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; wie übergeben ausgegeben.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$addnl&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Flag (0/1) welches, sofern aktiviert, einen Zeilenumbruch (&amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt;) an &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; anfügt, bevor es gesendet wird.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_Expect() ==&lt;br /&gt;
{{Randnotiz|RNTyp=Warn|RNText=&amp;lt;u&amp;gt;&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039;&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Bei der Benutzung von DevIo_Expect() wird FHEM für die Dauer von bis zu &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden blockiert.&lt;br /&gt;
# Sollte im ersten Versuch keine Antwort auf die zuvor gesendeten Daten innerhalb von &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden eintreffen, wird die Verbindung geschlossen und neu aufgebaut. Es erfolgt dabei &#039;&#039;&#039;KEINE INITIALISIERUNG&#039;&#039;&#039; durch &amp;lt;code&amp;gt;$initfn&amp;lt;/code&amp;gt; aus [[#DevIo_OpenDev()|DevIo_OpenDev()]].&lt;br /&gt;
}}&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$buf = DevIo_Expect($hash, $msg, $timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_Expect() sendet den Inhalt von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; und wartet bis zu &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden auf eine Antwort. Sollte in dieser Zeit keine Antwort eintreffen, so wird die Verbindung einmalig geschlossen, erneut geöffnet (ohne Aufruf einer Initialisierungs-Funktion) und der Vorgang wiederholt. Die empfangene Antwort wird anschließend als Funktionsergebnis zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wenn beim ersten Versuch keine Antwort innerhalb von &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden eintrifft, wird das Event &amp;lt;code&amp;gt;FAILED&amp;lt;/code&amp;gt; generiert (siehe [[#Generierte Events|Generierte Events]]). Anschließend wird die Verbindung geschlossen, neu geöffnet (ohne Initialisierung) und &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; erneut gesendet. Sollte nun eine Antwort eintreffen, so wird das Event &amp;lt;code&amp;gt;CONNECTED&amp;lt;/code&amp;gt; generiert und die empfangene Antwort zurückgegeben. Sollte dennoch keine Antwort eintreffen, so wird das Event &amp;lt;code&amp;gt;DISCONNECTED&amp;lt;/code&amp;gt; getriggert.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, für deren Verbindung Daten gesendet und anschließen gelesen werden sollen.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die zu sendenden Daten auf die eine Antwort erwartet wird. Die Daten werden ohne Konvertierung direkt gesendet (entspricht &amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt; gleich &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; bei [[#DevIo_SimpleWrite()|DevIo_SimpleWrite]]).&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die maximale Wartezeit in Sekunden bis zum Eintreffen einer Antwort.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewert:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabe!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$buf&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die empfangene Antwort. Sollte es zu einem Fehler gekommen sein (Senden von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; fehlgeschlagen, keine Antwort innerhalb von &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden erhalten, etc.), so wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_CloseDev() ==&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;DevIo_CloseDev($hash);&lt;br /&gt;
DevIo_CloseDev($hash, $isFork);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_CloseDev() schließt eine evtl. geöffnete Verbindung für die Definition &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Optional kann man mit dem Flag &amp;lt;code&amp;gt;$isFork&amp;lt;/code&amp;gt; angeben, dass man sich aktuell in einem Fork vom Hauptprozess befindet. Dadurch wird beim Schließen von seriellen Verbindungen die Kommunikationsparameter nicht zurückgesetzt. Dies verhindert einen Ausfall der nachwievor bestehenden Verbindung im Hauptprozess.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, deren Verbindung geschlossen werden soll.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$isFork&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Flag (0 oder 1), welches angibt, dass DevIo_CloseDev() innerhalb eines geforkten Kindprozess ausgeführt wird. Dadurch werden serielle Verbindung so geschlossen, dass dabei die Kommunikationsparameter (Baudrate, Datenbits, Parität, etc.) nicht zurückgesetzt werden. Dieses Flag wird primär von dem Modul [[Blocking_Call|Blocking.pm]] verwendet um Verbindungen in einem geforkten Kindprozess zu schließen, ohne die Verbindung im Hauptprozess zu beeinträchtigen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0 (Verbindung wird im Hauptprozess geschlossen)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Generierte Events =&lt;br /&gt;
Die Funktionen von DevIo generieren verschiedene Events für die entsprechende Definition (parametrisiert durch &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;). Diese können bspw. in der [[DevelopmentModuleIntro#X_Notify|Notify]]-Funktion des entsprechenden Moduls verarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Übersicht der generierten Events:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Event !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;code&amp;gt;CONNECTED&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die Verbindung wurde nach einem Verbindungsabbruch erfolgreich wieder aufgebaut. Dieses Event wird nur nach einem erfolgreichen Reconnect von einer zuvor verlorenen Verbindung generiert. Es wird &#039;&#039;&#039;nicht&#039;&#039;&#039; nach der erfolgreichen Initialverbindung generiert.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;code&amp;gt;DISCONNECTED&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die Verbindung ist zusammengebrochen. Es wird ein neuer Verbindungsversuch nach &amp;lt;code&amp;gt;$hash-&amp;gt;{nextOpenDelay}&amp;lt;/code&amp;gt; Sekunden erfolgen (siehe [[#Wichtige Internals zur Konfiguration|Wichtige Internals zur Konfiguration]]).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;code&amp;gt;FAILED&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Dieses Event wird nur bei der Nutzung der Funktion [[#DevIo_Expect|DevIo_Expect()]] generiert, sofern es keine Antwort auf die zuvor gesendeten Daten gibt. Dies bedeutet konkret, dass Gerät hat nicht auf die eigene Anfrage geantwortet und die Verbindung wird daher neu aufgebaut. Details dazu, siehe [[#DevIo_Expect()|DevIo_Expect()]].&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Hinweis bei der Datenverarbeitung (Buffering) =&lt;br /&gt;
DevIo beschränkt sich ausschließlich auf die Verbindungsverwaltung. Sobald die Verbindung etabliert wurde, übernimmt FHEM (fhem.pl) die Verarbeitung von eingehenden Daten und informiert das entsprechende Modul durch Aufruf der [[DevelopmentModuleIntro#X_Read|Read]]-Funktion. Dabei hat weder DevIo, noch FHEM selber Kenntnis von der Struktur der Daten. Sowohl DevIo, als auch FHEM selber können nicht erkennen, ob die empfangenen Daten weder vollständig, noch valide in ihrem Aufbau sind. Dies alles obliegt dem Modul, welches die Verbindung initiiert hat. Man sollte daher immer davon ausgehen, dass beim Lesen von Daten (in [[DevelopmentModuleIntro#X_Read|X_Read()]]) diese unvollständig sein können, oder sogar mehrere Datagramme enthalten sein können. &lt;br /&gt;
&lt;br /&gt;
Dazu stellt DevIo dem Modulentwickler das Internal &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; in der jeweiligen Definition zur Verfügung um dort Daten zwischenzuspeichern, bis ein vollständiges Datagramm daraus verarbeitet werden kann. Alle eingelesenen Daten werden dazu in der [[DevelopmentModuleIntro#X_Read|Read]]-Funktion immer an &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; hinten angehangen. Sobald ein vollständiges Datagramm in &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; erkannt wurde, wird dieses herausgenommen und verarbeitet, solange bis kein vollständiges Datagramm in &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; erkennbar ist. Hierbei ist jedoch entscheidend, woran man ein abgeschlossenes Datagramm erkennen kann. Dies kann je nach Protokoll sehr unterschiedlich sein (bspw. Zeilenumbruch &amp;lt;code&amp;gt;\r\n&amp;lt;/code&amp;gt; oder ein Semikolon &amp;lt;code&amp;gt;;&amp;lt;/code&amp;gt;, etc.). &lt;br /&gt;
&lt;br /&gt;
DevIo initialisiert &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; nach dem erfolgreichen Verbindungsaufbau mit einem Leerstring (&amp;lt;code&amp;gt;&amp;quot;&amp;quot;&amp;lt;/code&amp;gt;). Sobald eine Verbindung mit [[#DevIo_CloseDev()|DevIo_CloseDev()]] geschlossen wird, wird &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; gelöscht.&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie man ein solches Buffering verwendet für ein Protokoll, welches Datagramme durch einen Zeilenumbruch &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt; (Newline) abtrennt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub MY_MODULE_Read($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
  my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
  &lt;br /&gt;
  my $data = DevIo_SimpleRead($hash);&lt;br /&gt;
  return if(!defined($data)); # connection lost&lt;br /&gt;
  &lt;br /&gt;
  my $buffer = $hash-&amp;gt;{PARTIAL};&lt;br /&gt;
  &lt;br /&gt;
  Log3 $name, 5, &amp;quot;MY_MODULE ($name) - received $data (buffer contains: $buf)&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  # concat received data to $buffer&lt;br /&gt;
  $buffer .= $data;&lt;br /&gt;
&lt;br /&gt;
  # as long as the buffer contains newlines (complete datagramm)&lt;br /&gt;
  while($buffer =~ m/\n/)&lt;br /&gt;
  {&lt;br /&gt;
    my $msg;&lt;br /&gt;
    &lt;br /&gt;
    # extract the complete message ($msg), everything else is assigned to $buffer&lt;br /&gt;
    ($msg, $buffer) = split(&amp;quot;\n&amp;quot;, $buffer, 2);&lt;br /&gt;
    &lt;br /&gt;
    # remove trailing whitespaces&lt;br /&gt;
    chomp $msg;&lt;br /&gt;
&lt;br /&gt;
    # update $hash-&amp;gt;{PARTIAL} with the current buffer content&lt;br /&gt;
    $hash-&amp;gt;{PARTIAL} = $buffer; &lt;br /&gt;
    &lt;br /&gt;
    # parse the extracted message&lt;br /&gt;
    MY_MODULE_ParseMessage($hash, $msg);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Beispielimplementierung in einem Modul =&lt;br /&gt;
== serielle Verbindung ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
package main;&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
use DevIo; # load DevIo.pm if not already loaded&lt;br /&gt;
&lt;br /&gt;
# called upon loading the module MY_MODULE&lt;br /&gt;
sub MY_MODULE_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
&lt;br /&gt;
  $hash-&amp;gt;{DefFn}    = &amp;quot;MY_MODULE_Define&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{UndefFn}  = &amp;quot;MY_MODULE_Undef&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{SetFn}    = &amp;quot;MY_MODULE_Set&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{ReadFn}   = &amp;quot;MY_MODULE_Read&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{ReadyFn}  = &amp;quot;MY_MODULE_Ready&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when a new definition is created (by hand or from configuration read on FHEM startup)&lt;br /&gt;
sub MY_MODULE_Define($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash, $def) = @_;&lt;br /&gt;
  my @a = split(&amp;quot;[ \t]+&amp;quot;, $def);&lt;br /&gt;
&lt;br /&gt;
  my $name = $a[0];&lt;br /&gt;
  &lt;br /&gt;
  # $a[1] is always equals the module name &amp;quot;MY_MODULE&amp;quot;&lt;br /&gt;
  &lt;br /&gt;
  # first argument is a serial device (e.g. &amp;quot;/dev/ttyUSB0@9600&amp;quot;)&lt;br /&gt;
  my $dev = $a[2]; &lt;br /&gt;
&lt;br /&gt;
  return &amp;quot;no device given&amp;quot; unless($dev);&lt;br /&gt;
  &lt;br /&gt;
  # add a default baud rate (9600), if not given by user&lt;br /&gt;
  $dev .= &#039;@9600&#039; if(not $dev =~ m/\@\d+$/);&lt;br /&gt;
  &lt;br /&gt;
  $hash-&amp;gt;{DeviceName} = $dev;&lt;br /&gt;
  &lt;br /&gt;
  # close connection if maybe open (on definition modify)&lt;br /&gt;
  DevIo_CloseDev($hash) if(DevIo_IsOpen($hash));  &lt;br /&gt;
  &lt;br /&gt;
  # open connection with custom init function&lt;br /&gt;
  my $ret = DevIo_OpenDev($hash, 0, &amp;quot;MY_MODULE_Init&amp;quot;); &lt;br /&gt;
 &lt;br /&gt;
  return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when definition is undefined &lt;br /&gt;
# (config reload, shutdown or delete of definition)&lt;br /&gt;
sub MY_MODULE_Undef($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash, $name) = @_;&lt;br /&gt;
 &lt;br /&gt;
  # close the connection &lt;br /&gt;
  DevIo_CloseDev($hash);&lt;br /&gt;
  &lt;br /&gt;
  return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called repeatedly if device disappeared&lt;br /&gt;
sub MY_MODULE_Ready($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
  &lt;br /&gt;
  # try to reopen the connection in case the connection is lost&lt;br /&gt;
  return DevIo_OpenDev($hash, 1, &amp;quot;MY_MODULE_Init&amp;quot;); &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when data was received&lt;br /&gt;
sub MY_MODULE_Read($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
  my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
  &lt;br /&gt;
  # read the available data&lt;br /&gt;
  my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
  &lt;br /&gt;
  # stop processing if no data is available (device disconnected)&lt;br /&gt;
  return if(!defined($buf));&lt;br /&gt;
  &lt;br /&gt;
  Log3 $name, 5, &amp;quot;MY_MODULE ($name) - received: $buf&amp;quot;; &lt;br /&gt;
  &lt;br /&gt;
  #&lt;br /&gt;
  # do something with $buf, e.g. generate readings, send answers via DevIo_SimpleWrite(), ...&lt;br /&gt;
  #&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called if set command is executed&lt;br /&gt;
sub MY_MODULE_Set($$@)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash, $name, $cmd) = @_;&lt;br /&gt;
    my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
    &lt;br /&gt;
    my $usage = &amp;quot;unknown argument $cmd, choose one of statusRequest:noArg on:noArg off:noArg&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    if($cmd eq &amp;quot;statusRequest&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;get_status\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    elsif($cmd eq &amp;quot;on&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;on\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    elsif($cmd eq &amp;quot;off&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;off\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
        return $usage;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
# will be executed upon successful connection establishment (see DevIo_OpenDev())&lt;br /&gt;
sub MY_MODULE_Init($)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash) = @_;&lt;br /&gt;
&lt;br /&gt;
    # send a status request to the device&lt;br /&gt;
    DevIo_SimpleWrite($hash, &amp;quot;get_status\r\n&amp;quot;, 2);&lt;br /&gt;
    &lt;br /&gt;
    return undef; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== TCP/IP-Verbindung == &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
package main;&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
use DevIo; # load DevIo.pm if not already loaded&lt;br /&gt;
&lt;br /&gt;
# called upon loading the module MY_MODULE&lt;br /&gt;
sub MY_MODULE_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
&lt;br /&gt;
  $hash-&amp;gt;{DefFn}    = &amp;quot;MY_MODULE_Define&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{UndefFn}  = &amp;quot;MY_MODULE_Undef&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{SetFn}    = &amp;quot;MY_MODULE_Set&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{ReadFn}   = &amp;quot;MY_MODULE_Read&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{ReadyFn}  = &amp;quot;MY_MODULE_Ready&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when a new definition is created (by hand or from configuration read on FHEM startup)&lt;br /&gt;
sub MY_MODULE_Define($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash, $def) = @_;&lt;br /&gt;
  my @a = split(&amp;quot;[ \t]+&amp;quot;, $def);&lt;br /&gt;
&lt;br /&gt;
  my $name = $a[0];&lt;br /&gt;
  &lt;br /&gt;
  # $a[1] is always equals the module name &amp;quot;MY_MODULE&amp;quot;&lt;br /&gt;
  &lt;br /&gt;
  # first argument is the hostname or IP address of the device (e.g. &amp;quot;192.168.1.120&amp;quot;)&lt;br /&gt;
  my $dev = $a[2]; &lt;br /&gt;
&lt;br /&gt;
  return &amp;quot;no device given&amp;quot; unless($dev);&lt;br /&gt;
  &lt;br /&gt;
  # add a default port (1012), if not explicitly given by user&lt;br /&gt;
  $dev .= &#039;:1012&#039; if(not $dev =~ m/:\d+$/);&lt;br /&gt;
  &lt;br /&gt;
  $hash-&amp;gt;{DeviceName} = $dev;&lt;br /&gt;
  &lt;br /&gt;
  # close connection if maybe open (on definition modify)&lt;br /&gt;
  DevIo_CloseDev($hash) if(DevIo_IsOpen($hash));  &lt;br /&gt;
  &lt;br /&gt;
  # open connection with custom init and error callback function (non-blocking connection establishment)&lt;br /&gt;
  DevIo_OpenDev($hash, 0, &amp;quot;MY_MODULE_Init&amp;quot;, &amp;quot;MY_MODULE_Callback&amp;quot;); &lt;br /&gt;
 &lt;br /&gt;
  return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when definition is undefined &lt;br /&gt;
# (config reload, shutdown or delete of definition)&lt;br /&gt;
sub MY_MODULE_Undef($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash, $name) = @_;&lt;br /&gt;
 &lt;br /&gt;
  # close the connection &lt;br /&gt;
  DevIo_CloseDev($hash);&lt;br /&gt;
  &lt;br /&gt;
  return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called repeatedly if device disappeared&lt;br /&gt;
sub MY_MODULE_Ready($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
  &lt;br /&gt;
  # try to reopen the connection in case the connection is lost&lt;br /&gt;
  return DevIo_OpenDev($hash, 1, &amp;quot;MY_MODULE_Init&amp;quot;, &amp;quot;MY_MODULE_Callback&amp;quot;); &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when data was received&lt;br /&gt;
sub MY_MODULE_Read($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
  my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
  &lt;br /&gt;
  # read the available data&lt;br /&gt;
  my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
  &lt;br /&gt;
  # stop processing if no data is available (device disconnected)&lt;br /&gt;
  return if(!defined($buf));&lt;br /&gt;
  &lt;br /&gt;
  Log3 $name, 5, &amp;quot;MY_MODULE ($name) - received: $buf&amp;quot;; &lt;br /&gt;
  &lt;br /&gt;
  #&lt;br /&gt;
  # do something with $buf, e.g. generate readings, send answers via DevIo_SimpleWrite(), ...&lt;br /&gt;
  #&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called if set command is executed&lt;br /&gt;
sub MY_MODULE_Set($$@)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash, $name, $cmd) = @_;&lt;br /&gt;
    my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
    &lt;br /&gt;
    my $usage = &amp;quot;unknown argument $cmd, choose one of statusRequest:noArg on:noArg off:noArg&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    if($cmd eq &amp;quot;statusRequest&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;get_status\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    elsif($cmd eq &amp;quot;on&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;on\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    elsif($cmd eq &amp;quot;off&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;off\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
        return $usage;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
# will be executed upon successful connection establishment (see DevIo_OpenDev())&lt;br /&gt;
sub MY_MODULE_Init($)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash) = @_;&lt;br /&gt;
&lt;br /&gt;
    # send a status request to the device&lt;br /&gt;
    DevIo_SimpleWrite($hash, &amp;quot;get_status\r\n&amp;quot;, 2);&lt;br /&gt;
    &lt;br /&gt;
    return undef; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# will be executed if connection establishment fails (see DevIo_OpenDev())&lt;br /&gt;
sub MY_MODULE_Callback($)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash, $error) = @_;&lt;br /&gt;
    my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
&lt;br /&gt;
    # create a log emtry with the error message&lt;br /&gt;
    Log3 $name, 5, &amp;quot;MY_MODULE ($name) - error while connecting: $error&amp;quot;; &lt;br /&gt;
    &lt;br /&gt;
    return undef; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Referenzen = &lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=MQTT_(Modul)&amp;diff=26350</id>
		<title>MQTT (Modul)</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=MQTT_(Modul)&amp;diff=26350"/>
		<updated>2018-04-02T07:41:26Z</updated>

		<summary type="html">&lt;p&gt;Eisler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Stellt als Gateway die Verbindung zu einem MQTT-Broker her&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModForumArea=MQTT&lt;br /&gt;
|ModTechName=00_MQTT.pm&lt;br /&gt;
|ModOwner=eisler ({{Link2FU|2214|Forum}}) }}&lt;br /&gt;
Das Modul [[MQTT]] fungiert als [[Interface|Verbindung]] zwischen FHEM und einem MQTT Broker und repräsentiert ein  &#039;&#039;&#039;&#039;&#039;MQTT Gateway&#039;&#039;&#039;&#039;&#039;. Die durch die Module [[MQTT_DEVICE]] oder {{Link2CmdRef|Anker=MQTT_BRIDGE|Lang=en|Label=MQTT_BRIDGE}} repräsentierten Client-Geräte sind gesondert anzulegen.&lt;br /&gt;
&lt;br /&gt;
== Voraussetzungen ==&lt;br /&gt;
Es muss für jedes MQTT-Gateway ein Broker eingerichtet und erreichbar sein.&lt;br /&gt;
Eine Anleitung zur Einrichtung eines Mosquitto-Brokers finden Sie z.B. im Artikel [[MQTT Einführung]].&lt;br /&gt;
&lt;br /&gt;
== Begrifflichkeiten in Kürze ==&lt;br /&gt;
Ein Broker ist ein zentraler Serverdienst (irgendwo im Netz oder auf derselben Maschine, auf der auch FHEM läuft), an den Geräte ihre Informationen weitergeben (publish), und der dann dafür sorgt, dass diese Information an alle anderen Geräte weitergegeben wird, die sich dafür interessieren (subscribe). [[MQTT]] fungiert dabei als [[Systemübersicht#Interfaces| IO-Modul]] und organisiert die Kommunikation zwischen dem Broker und allen FHEM-Geräten, die MQTT bei diesem Server nutzen wollen.&lt;br /&gt;
&lt;br /&gt;
== Define ==&lt;br /&gt;
Das MQTT Gateway wird angelegt mit&lt;br /&gt;
&amp;lt;code&amp;gt;define &#039;&#039;&#039;meinMQTTGW&#039;&#039;&#039; MQTT &amp;lt;Broker-IP&amp;gt;[:&amp;lt;Broker-Port&amp;gt;]&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Anwendung ==&lt;br /&gt;
Danach können entsprechende Client-Devices angelegt werden, &lt;br /&gt;
[[MQTT_DEVICE]] wird verwendet, wenn das physikalische Device (Sensor oder Aktor) über mqtt kommuniziert, wie dieses z.B. bei vielen ESP8266-basierten Geräten der Fall ist. &lt;br /&gt;
{{Link2CmdRef|Anker=MQTT_BRIDGE|Lang=en|Label=MQTT_BRIDGE}} ist für den Fall, dass ein FHEM-Device&amp;lt;ref&amp;gt;Dies kann z.B. ein beliebiger HM- oder Z-Wave-Aktor sein, der ein Licht einschaltet.&amp;lt;/ref&amp;gt; existiert und dieses über MQTT sicht- bzw. steuerbar gemacht werden soll.&lt;br /&gt;
&lt;br /&gt;
== Ergänzende Funktionen ==&lt;br /&gt;
Manche MQTT-Geräte senden Informationen im JSON-Format. Um solche Informationen in FHEM verwenden zu können, müssen sie zunächst in ein anderes Format überführt werden. Hierfür wird das Modul {{Link2CmdRef|Anker=expandJSON|Lang=en|Label=expandJSON}} benötigt.&lt;br /&gt;
&lt;br /&gt;
== Anwendungsbeispiele ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* {{Link2Forum|Topic=27532|LinkText=Ankündigung}} der MQTT Module&lt;br /&gt;
* [[MQTT Einführung]]&lt;br /&gt;
&lt;br /&gt;
== Anmerkungen ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:IP Components|IP Komponenten]]&lt;br /&gt;
[[Kategorie:Other Components]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevelopmentModuleIntro&amp;diff=26267</id>
		<title>DevelopmentModuleIntro</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevelopmentModuleIntro&amp;diff=26267"/>
		<updated>2018-03-25T11:48:38Z</updated>

		<summary type="html">&lt;p&gt;Eisler: /* X_Set */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Hinweis|Dieser Text ist in Arbeit und muss noch an einigen Stellen ergänzt werden. }}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Um neue Geräte, Dienste, o.ä. in FHEM verfügbar zu machen, kann man ein eigenes Modul in Perl schreiben. Ein Modul wird in FHEM automatisch geladen, wenn ein entsprechendes Device in FHEM definiert wird. Das Modul ermöglicht eine spezifische Kommunikation mit einem physikalischen Gerät, stellt Ergebnisse (&amp;quot;Readings&amp;quot;) und Events innerhalb von FHEM zur Verfügung und erlaubt es, das Gerät mit &amp;quot;Set&amp;quot;-/&amp;quot;Get&amp;quot;-Befehlen zu beeinflussen. Dieser Artikel soll den Einstieg in die Entwicklung eigener Module erleichtern.&lt;br /&gt;
&lt;br /&gt;
Mit dem FHEM-Befehl &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt; werden Devices in FHEM basierend auf einem Modul definiert. Dieser Befehl sorgt dafür, dass ein neues Modul bei Bedarf geladen und initialisiert wird. Ein gutes Beispiel ist hierbei die zentrale Konfigurationsdatei &amp;quot;fhem.cfg&amp;quot; in der sämtliche Devices in Form von &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Statements gespeichert sind.&lt;br /&gt;
&lt;br /&gt;
Damit das funktioniert müssen der Name des Moduls und der Name der [[#X_Initialize|Initialisierungsfunktion]]  identisch sein. Das folgende Beispiel soll dies verdeutlichen:&lt;br /&gt;
&lt;br /&gt;
Ein Jeelink USB-Stick könnte beispielsweise mit dem Befehl &amp;lt;code&amp;gt;define JeeLink1 &#039;&#039;JeeLink&#039;&#039; /dev/ttyUSB0@57600&amp;lt;/code&amp;gt; definiert werden.&lt;br /&gt;
&lt;br /&gt;
In fhem.pl wird der define-Befehl verarbeitet und geprüft, ob ein Modul mit dem Namen &amp;quot;JeeLink&amp;quot; schon geladen ist und falls nicht ein Modul mit Namen XY_JeeLink.pm im Modulverzeichnis (z.B. /opt/fhem/FHEM) gesucht und dann geladen. &lt;br /&gt;
Danach wird die Funktion &amp;lt;code&amp;gt;&#039;&#039;JeeLink&#039;&#039;_Initialize()&amp;lt;/code&amp;gt; aufgerufen um das Modul in FHEM zu registrieren. Eine Moduldatei muss dazu eine Funktion &amp;lt;code&amp;gt;&#039;&#039;&amp;amp;lt;Modulname&amp;amp;gt;&#039;&#039;_Initialize()&amp;lt;/code&amp;gt; enthalten. Durch den Aufruf dieser Funktion wird FHEM mitgeteilt, welche Funktionalitäten dieses Modul unterstützt.&lt;br /&gt;
&lt;br /&gt;
In der Initialisierungsfunktion des Moduls werden die Namen aller weiteren Perl-Funktionen des Moduls, die von fhem.pl aus aufgerufen werden, bekannt gemacht. Dazu wird für jedes Modul ein eigener Hash (genauer &amp;quot;Modul-Hash&amp;quot;) mit entsprechenden Werten gefüllt, der in fhem.pl für jedes Modul entsprechend abgelegt wird. Dadurch weiß FHEM wie dieses Modul anzusprechen ist.&lt;br /&gt;
&lt;br /&gt;
== Grundlegender Aufbau eines Moduls ==&lt;br /&gt;
&lt;br /&gt;
=== Dateiname ===&lt;br /&gt;
&lt;br /&gt;
Ein FHEM-Modul wird als Perl-Modul mit der Dateiendung *.pm abgespeichert. Der Dateiname folgt dabei folgendem Schema:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;&#039;&#039;[&#039;&#039;&#039;Schlüsselnummer&#039;&#039;&#039;]&#039;&#039;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;_&amp;lt;/font&amp;gt;&#039;&#039;[&#039;&#039;&#039;Modulname&#039;&#039;&#039;]&#039;&#039;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;.pm&amp;lt;/font&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schlüsselnummer&#039;&#039;&#039; - Eine zweistellige Zahl zwischen 00 - 99. Die Schlüsselnummer hat aktuell keine technische Relevanz mehr. In früheren FHEM-Versionen ist sie relevant für [[#Zweistufiges_Modell_f.C3.BCr_Module|zweistufige Module]] (Reihenfolge für [[DevelopmentModuleAPI#Dispatch|Dispatch()]] um logische Module zu prüfen). Die allgemeine Empfehlung ist hierbei eine Schlüsselnummer eines Moduls zu verwenden, welches eine ähnliche Funktionalität bietet. Die Schlüsselnummer 99 hat hierbei eine besondere Bedeutung, da alle Module mit dieser Schlüsselnummer beim Start von FHEM automatisch geladen werden, selbst, wenn sie in der Konfiguration nicht verwendet werden. Daher wird für myUtils 99 als Schlüsselnummer verwendet (99_myUtils.pm). Module mit der Schlüsselnummer 99 werden im SVN nicht akzeptiert (siehe [[SVN Nutzungsregeln]])&lt;br /&gt;
* &#039;&#039;&#039;Modulname&#039;&#039;&#039; - Der Name des Moduls wie er in FHEM bei dem Anlegen einer Gerätedefinition zu verwenden ist. Der Modulname sollte nur aus den folgenden möglichen Zeichen bestehen: Groß-/Kleinbuchstaben, Zahlen sowie Unterstrich (_)&lt;br /&gt;
&lt;br /&gt;
=== Inhaltlicher Aufbau ===&lt;br /&gt;
&lt;br /&gt;
Ein Modul ist inhaltlich in folgende Abschnitte unterteilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
#&lt;br /&gt;
#  72_MYMODULE.pm &lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
package main;&lt;br /&gt;
&lt;br /&gt;
# Laden evtl. abhängiger Perl- bzw. FHEM-Module&lt;br /&gt;
use HttpUtils;&lt;br /&gt;
use [...]&lt;br /&gt;
&lt;br /&gt;
# FHEM Modulfunktionen&lt;br /&gt;
&lt;br /&gt;
sub MYMODULE_Initialize() {&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub MYMODULE_Define() {&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# Eval-Rückgabewert für erfolgreiches&lt;br /&gt;
# Laden des Moduls&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# Beginn der Commandref&lt;br /&gt;
&lt;br /&gt;
=pod&lt;br /&gt;
=item [helper|device|command]&lt;br /&gt;
=item summary Kurzbeschreibung in Englisch was MYMODULE steuert/unterstützt&lt;br /&gt;
=item summary_DE Kurzbeschreibung in Deutsch was MYMODULE steuert/unterstützt&lt;br /&gt;
&lt;br /&gt;
=begin html&lt;br /&gt;
 Englische Commandref in HTML&lt;br /&gt;
=end html&lt;br /&gt;
&lt;br /&gt;
=begin html_DE&lt;br /&gt;
 Deustche Commandref in HTML&lt;br /&gt;
=end html&lt;br /&gt;
&lt;br /&gt;
# Ende der Commandref&lt;br /&gt;
=cut&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man kann hierbei von folgender Reihenfolge sprechen:&lt;br /&gt;
&lt;br /&gt;
# Perl-Code, welcher das Modul implementiert&lt;br /&gt;
# Die Zeile &amp;lt;code&amp;gt;1;&amp;lt;/code&amp;gt; nachdem der Perl-Code abgeschlossen ist. Dies dient FHEM der Erkennung, dass das Modul erfolgreich und vollständig geladen wurde. Sollte diese Zeile nicht enthalten sein, wird FHEM beim Laden des Moduls die Fehlermeldung &amp;lt;code&amp;gt;Error:Modul 72_MYMODULE deactivated&amp;lt;/code&amp;gt; in das Logfile schreiben.&lt;br /&gt;
# Commandref zur Dokumentation des Moduls. Diese Dokumentation soll dem User die möglichen Befehle/Attribute/Readings/Events vermitteln. Weitere Informationen und Hinweise findet man in den [[Guidelines zur Dokumentation]].&lt;br /&gt;
&lt;br /&gt;
== Der Hash einer Geräteinstanz ==&lt;br /&gt;
Eine Besonderheit in Perl sind [http://de.wikipedia.org/wiki/Assoziatives_Array#Perl assoziative Arrays], (nicht ganz richtig als &amp;quot;Hash&amp;quot; bezeichnet) in denen die Adressierung nicht über eine Zählvariable erfolgt, sondern über einen beliebigen String. Die internen Abläufe bei der Adressierung führen dazu, dass die Speicherung in und der Abruf aus Hashes relativ langsam ist.&lt;br /&gt;
&lt;br /&gt;
Der zentrale Speicherort für Informationen einer Geräteinstanz bei FHEM ist ein solcher Hash, der seinerseits in fhem.pl von einem globalen Hash referenziert wird. &lt;br /&gt;
&lt;br /&gt;
In fhem.pl werden alle Gerätedefinitionen in dem globalen Hash &amp;lt;code&amp;gt;%defs&amp;lt;/code&amp;gt; abgelegt. Der Inhalt von &amp;lt;code&amp;gt;$defs{&#039;&#039;&amp;amp;lt;Name&amp;amp;gt;&#039;&#039;}&amp;lt;/code&amp;gt; in fhem.pl verweist dabei auf den Hash der Geräteinstanz in Form einer Hashreferenz. Diesen Verweis (also nur die Adresse) bekommen die Funktionen eines Moduls übergeben (i.d.R. als &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; bezeichnet), welche direkt von fhem.pl aufgerufen werden. In dem Hash stehen beispielsweise die internen Werte des Geräts, die im Frontend als &amp;quot;Internals&amp;quot; angezeigt werden, sowie die Readings des Geräts. &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
*&amp;lt;code&amp;gt;$hash-&amp;gt;{NAME}&amp;lt;/code&amp;gt; enthält den Namen der Geräteinstanz, &lt;br /&gt;
*&amp;lt;code&amp;gt;$hash-&amp;gt;{TYPE}&amp;lt;/code&amp;gt; enthält die Typbezeichnung des Geräts (Modulname)&lt;br /&gt;
&lt;br /&gt;
==Ausführung von Modulen==&lt;br /&gt;
FHEM arbeitet intern nicht parallel, sondern arbeitet alle Aufgaben seriell nacheinander kontinuierlich ab. Daher wäre es ungünstig, wenn Module Daten von einem physikalischen Gerät abfragen wollen und dabei innerhalb der selben Funktion auf die Antwort des Geräts warten. In dieser Zeit, in der FHEM auf die Antwort des Gerätes warten muss, wäre der Rest von FHEM blockiert. Da immer nur eine Aufgabe zur selben Zeit bearbeitet wird, müssen alle weiteren Aufgaben solange warten. Eine Datenkommunikation innerhalb eines Moduls sollte daher immer ohne Blockierung erfolgen. Dadurch kann FHEM die Wartezeit effizient für andere Aufgaben nutzen um bspw. anstehende Daten für andere Module zu verarbeiten. Es gibt in FHEM entsprechende Mechanismen, welche eine &amp;quot;Non-Blocking&amp;quot;-Kommunikation über verschiedene Wege (z.B. seriell, HTTP, TCP, ...) ermöglichen.&lt;br /&gt;
&lt;br /&gt;
Dafür werden in FHEM zwei zentrale Listen gepflegt, in der die Filedeskriptoren der geöffneten Kommunikatonsverbindungen gespeichert sind. Auf Linux- bzw. Unix-basierten Plattformen wird der select-Befehl des Betriebssystems verwendet um Filedeskriptoren auf lesbare Daten zu überprüfen. In FHEM gibt es dazu eine Liste (&amp;lt;code&amp;gt;%selectlist&amp;lt;/code&amp;gt;), in der die Filedeskriptoren sämtlicher Geräte (z.B. serielle Verbindung, TCP-Verbindung, etc.) gespeichert sind. &lt;br /&gt;
&lt;br /&gt;
In der zentralen Schleife (Main-Loop) von fhem.pl wird mit &amp;lt;code&amp;gt;select()&amp;lt;/code&amp;gt; überwacht, ob über eine der geöffneten Schnittstellen Daten zum Lesen anstehen. Wenn dies der Fall ist, dann wird die Lesefunktion ([[#X_Read|X_Read]]) des zuständigen Moduls aufgerufen, damit es die Daten entgegennimmt und verarbeitet. Anschließend wird die Schleife weiter ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Auf Windows-Systemen funktioniert dies anders. Hier können USB/Seriell-Geräte nicht per &amp;lt;code&amp;gt;select()&amp;lt;/code&amp;gt; überwacht werden. In FHEM unter Windows werden daher diese Schnittstellen kontinuierlich abgefragt ob Daten bereitstehen. Dafür müssen Module zusätzlich zur Lesefunktion eine Abfragefunktion ([[#X_Ready|X_Ready]]) implementieren, welche prüft, ob Daten zum Lesen anstehen. Auch auf Linux/Unix-Plattformen hat diese Funktion eine Aufgabe. Falls nämlich eine Schnittstelle ausfällt, beziehungsweise ein CUL oder USB-zu-Seriell Adapter ausgesteckt wird, dann wird über diese Funktion regelmäßig geprüft ob die Schnittstelle wieder verfügbar wird.&lt;br /&gt;
&lt;br /&gt;
Innerhalb der eigentlichen Lesefunktion (X_Read) werden dann die Daten vom zugehörigen Gerät gelesen, das nötige Protokoll implementiert um die Daten zu interpretieren und Werte in Readings geschrieben.&lt;br /&gt;
&lt;br /&gt;
Auch wenn von einem Anwender über einen Get-Befehl Daten aktiv von einem Gerät angefordert werden, sollte nicht blockierend gewartet werden. Eine asynchrone Ausgabe, sobald das Ergebnis vorliegt, ist über [[DevelopmentModuleAPI#asyncOutput|asyncOutput()]] möglich. Siehe {{Link2Forum|Topic=43771|Message=357870|LinkText=Beschreibung}} und {{Link2Forum|Topic=43771|Message=360935|LinkText=Beispiel}}. Weitere Anwendungsbeispiele finden sich im  {{Link2Forum|Topic=43052|Message=353477|LinkText=PLEX Modul}} und im überarbeiteten und nicht-blockierenden {{Link2Forum|Topic=42771|Message=348498|LinkText=SYSSTAT Modul}}.&lt;br /&gt;
&lt;br /&gt;
== Wichtige globale Variablen aus fhem.pl ==&lt;br /&gt;
&lt;br /&gt;
FHEM arbeitet mit einer Vielzahl an internen Variablen. Die nun folgenden aufgelisteten Variablen sind die wichtigsten, welche man im Rahmen der Modulprogrammierung kennen sollte:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variable !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; || Dient der Erkennung für fhem.pl sowie den Modulen, ob FHEM den Initialisierungsvorgang abgeschlossen hat. Beim Starten von FHEM ist &amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; gleich &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. Erst, wenn das Einlesen der Konfiguration, sowie des State-Files (Readings) abgeschlossen ist, wird &amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; auf &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; gesetzt.&lt;br /&gt;
Das gleiche Verfahren wird auch bei dem Befehl &amp;lt;code&amp;gt;rereadcfg&amp;lt;/code&amp;gt; angewandt. Während &amp;lt;code&amp;gt;rereadcfg&amp;lt;/code&amp;gt; ausgeführt wird (Konfiguration löschen, neu einlesen), ist &amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; gleich &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dies ist insbesondere in der [[#X_Define|Define]]-Funktion eines Moduls relevant. Durch eine Prüfung auf &amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; kann man erkennen, ob eine Definition von Hand (&amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; = 1) oder im Rahmen der Initialisierung (FHEM Start / Rereadcfg =&amp;gt; &amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; = 0) erfolgte. Während der Initialisierung stehen bspw. die gesetzten Attribute der Definition noch nicht zur Verfügung und können daher nicht ausgewertet werden (siehe . &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;%attr&amp;lt;/code&amp;gt; || In &amp;lt;code&amp;gt;%attr&amp;lt;/code&amp;gt; werden sämtliche gesetzten Attribute aller Geräte gespeichert. Diese Datenstruktur wird generell durch den &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt;-Befehl verwaltet. Hierbei wird einem Gerätenamen eine Mehrzahl an Attributnamen mit einem Wert zugeordnet. Attribut-Inhalte können über die Funktion [[DevelopmentModuleAPI#AttrVal|AttrVal()]] ausgelesen werden.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;%cmds&amp;lt;/code&amp;gt; || In &amp;lt;code&amp;gt;%cmds&amp;lt;/code&amp;gt; wird jedem in FHEM existierendem Befehl die entsprechende Funktion zugewiesen, welche diesen Befehl umsetzt. Module können durch das Eintragen eines Befehlsnamen samt Funktion in &amp;lt;code&amp;gt;%cmds&amp;lt;/code&amp;gt; über die [[#X_Initialize|Initialize]]-Funktion eines Moduls einen (oder mehrere) eigene Befehle in FHEM registrieren.&lt;br /&gt;
&lt;br /&gt;
Die Struktur ist dabei wiefolgt:&lt;br /&gt;
&lt;br /&gt;
  $cmds{&#039;&#039;&amp;amp;lt;Befehlsname&amp;amp;gt;&#039;&#039;} = {  Fn  =&amp;gt; &amp;quot;&#039;&#039;&amp;amp;lt;Funktionsname&amp;amp;gt;&#039;&#039;&amp;quot;,&lt;br /&gt;
                            Hlp =&amp;gt; &amp;quot;&#039;&#039;&amp;amp;lt;Aufrufsyntax&amp;amp;gt;&#039;&#039;&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;%data&amp;lt;/code&amp;gt;|| Der eigentliche Zweck von &amp;lt;code&amp;gt;%data&amp;lt;/code&amp;gt; ist dem Nutzer eine Möglichkeit zum Speichern von temporären Daten im globalen Kontext zu ermöglichen. Einige Module verwenden &amp;lt;code&amp;gt;%data&amp;lt;/code&amp;gt; jedoch auch um modul- &amp;amp; geräteübergreifend Daten auszutauschen.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;%defs&amp;lt;/code&amp;gt;|| In &amp;lt;code&amp;gt;%defs&amp;lt;/code&amp;gt; werden sämtliche Gerätedefinitionen, bzw. die Hash-Referenzen auf diese, gespeichert. Hier ist jedem Gerätenamen eine Hash-Referenz zugeordnet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;%modules&amp;lt;/code&amp;gt;|| In &amp;lt;code&amp;gt;%modules&amp;lt;/code&amp;gt; sind alle geladenen Module gelistet mit ihren entsprechenden Initialisierungsdaten (Funktionsnamen, Attribut-Listen, spezielle Einstellungen, ...). Hier wird für jeden Modulname der Modul-Hash aus der [[#X_Initialize|Initialize]]-Funktion gespeichert. &lt;br /&gt;
Desweiteren legen viele Module, welche nach dem [[DevelopmentModuleIntro#Zweistufiges_Modell_f.C3.BCr_Module|zweistufigen Modulkonzept]] hier eine Rückwärtszuordnung von Geräteadressen zu Geräte-Hash an.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;%readyfnlist&amp;lt;/code&amp;gt;|| In &amp;lt;code&amp;gt;%readyfnlist&amp;lt;/code&amp;gt; sind alle zu prüfenden Verbindungen mit ihrer entsprechendem Geräte-Hash gelistet. FHEM prüft alle hier gelisteten Geräte regelmäßig über eine Aufruf der entsprechenden [[#X_Ready|Ready]]-Funktion.&lt;br /&gt;
&lt;br /&gt;
Bei einer Nutzung von dem Hilfsmodul [[DevIo|DevIo.pm]] zum Aufbau einer Kommunikationsverbindung, kümmert sich DevIo selbständig um den entsprechenden Eintrag in &amp;lt;code&amp;gt;%readyfnlist&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;%selectlist&amp;lt;/code&amp;gt;|| In &amp;lt;code&amp;gt;%selectlist&amp;lt;/code&amp;gt; sind alle geöffneten Verbindungen mit ihrer entsprechendem Geräte-Hash gelistet. FHEM prüft alle hier gelisteten Geräte, ob der geöffnete Filedeskriptor unter &amp;lt;code&amp;gt;$hash-&amp;gt;{FD}&amp;lt;/code&amp;gt; Daten zum Lesen bereitgestellt hat. Ist dass der Fall, wird die entsprechende [[#X_Read|Read]]-Funktion aufgerufen, um anstehende Daten durch das Modul zu verarbeiten.&lt;br /&gt;
Bei einer Nutzung von dem Hilfsmodul [[DevIo|DevIo.pm]] zum Aufbau einer Kommunikationsverbindung, kümmert sich DevIo selbständig um den entsprechenden Eintrag in &amp;lt;code&amp;gt;%selectlist&amp;lt;/code&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Es gibt durchaus viele weitere globale Variablen, die jedoch für sehr spezielle Anwendungsfälle und z.T. nur einzelne Module gedacht sind und daher hier nicht aufgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Internals ==&lt;br /&gt;
Daten, die ein Modul im Geräte-Hash speichert nennt man Internals. Sie werden als Unterstruktur des Hashes der jeweiligen Geräteinstanz gespeichert, beispielswiese &amp;lt;code&amp;gt;$hash-&amp;gt;{NAME}&amp;lt;/code&amp;gt; für den Gerätenamen, welcher beim Define-Befehl übergeben wurde und als Internal gespeichert wird. Diese Daten spielen für FHEM eine sehr wichtige Rolle, da sämtliche gerätespezifischen Daten als Internal im Gerätehash gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
Falls Werte wie z.B. ein Intervall nicht über den Define-Befehl gesetzt werden sollen und im Betrieb einfach änderbar sein sollten, ist eine alternative Möglichkeit die Speicherung in so genannten Attributen. Dann würde man den Define-Befehl so implementieren, dass er kein Intervall übergeben bekommt und statt dessen das Interval als Attribut über den Befehl &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt; gesetzt wird.&lt;br /&gt;
&lt;br /&gt;
Generell werden alle Werte, welche direkt in der ersten Ebene von &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; (Gerätehash) gespeichert werden auf der Detail-Seite einer Definition in der FHEMWEB Oberfläche angezeigt. Es gibt jedoch Ausnahmen:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;$hash-&amp;gt;{helper}{URL}&amp;lt;/code&amp;gt; - Alle Elemente, welche als Unterelement wieder einen Hash besitzen werden nicht in FHEMWEB dargestellt. Typischerweise speichern Module Daten unter &amp;lt;code&amp;gt;$hash-&amp;gt;{helper}&amp;lt;/code&amp;gt; interne Daten zwischen, die für den User nicht relevant sind, sondern nur der internen Verarbeitung dienen.&lt;br /&gt;
* &amp;lt;code&amp;gt;$hash-&amp;gt;{&#039;&#039;&#039;.&#039;&#039;&#039;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;ELEMENT&amp;lt;/font&amp;gt;}&amp;lt;/code&amp;gt; - Alle Knoten, welche mit einem Punkt beginnen werden in der FHEMWEB Oberfläche nicht angezeigt. Man kann diese Daten jedoch beim Aufruf des [[List|list-Kommandos]] einsehen.&lt;br /&gt;
&lt;br /&gt;
Es gibt bereits vorbelegte Internals welche in FHEM dazu dienen definitionsbezogene Informationen wie bspw. Namen und Readings zu speichern. Dies sind im besonderen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;min-width: 13em;&amp;quot; | Internal !!  Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{NAME}&amp;lt;/code&amp;gt;  || Der Definitionsname, mit dem das Gerät angelegt wurde.  &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{READINGS}&amp;lt;/code&amp;gt;  || Enthält alle aktuell vorhandenen Readings. Daten unterhalb dieses Knotens sollte man nicht direkt manipulieren. Um Readings zu Erzeugen gibt es entsprechende [[DevelopmentModuleAPI#Readings_.2F_Events|Reading-Funktionen]].&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{NR}&amp;lt;/code&amp;gt;  || Die Positions-Nr. der Definition innerhalb der Konfiguration. Diese dient dazu die Konfiguration in der gleichen Reihenfolge zu speichern, wie die einzelnen Geräte angelegt wurden.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{TYPE}&amp;lt;/code&amp;gt;  || Der Modulname, mit welchem die Definition angelegt wurde.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{DEF}&amp;lt;/code&amp;gt;  || Sämtliche Argumente, welche beim &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Befehl nach dem Modulnamen übergeben wurden.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{CFGFN}&amp;lt;/code&amp;gt;  || Der Dateiname der Konfigurationsdatei in der diese Definition enthalten ist (sofern nicht in fhem.cfg). Dieser Wert ist nur gefüllt, wenn man mit mehreren Konfigurationsdateien arbeitet, welche dann in fhem.cfg via include-Befehl eingebunden werden.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{NTFY_ORDER}&amp;lt;/code&amp;gt;  || Sofern das Modul Events via [[DevelopmentModuleIntro#X_Notify|Notify-Funktion]] verarbeitet enthält jede Definition eine Notify-Order als Zeichenkette bestehend aus dem Notify Order Prefix und dem Definitionsnamen. Details zur Funktionsweise gibt es in der Beschreibung zur [[DevelopmentModuleIntro#X_Notify|Notify-Funktion]] im Abschnitt &amp;quot;Reihenfolge für den Aufruf der Notify-Funktion beeinflussen&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{NOTIFYDEV}&amp;lt;/code&amp;gt;  || Sofern das Modul Events via NotifyFn verarbeitet kann man damit die Definitionen, von denen man Events erhalten will begrenzen. Details zur Funktionsweise gibt es in der Beschreibung zur [[DevelopmentModuleIntro#X_Notify|Notify-Funktion]] im Abschnitt &amp;quot;Begrenzung der Aufrufe auf bestimmte Geräte&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{IODev}&amp;lt;/code&amp;gt;  || Hier wird das zugeordnete IO-Gerät durch [[DevelopmentModuleAPI#AssignIoPort|AssignIoPort()]] gespeichert, welches für den Datentransport und -empfang dieses logischen Gerätes zuständig ist. Dieser Wert existiert nur bei Modulen die nach dem [[DevelopmentModuleIntro#Zweistufiges_Modell_f.C3.BCr_Module|zweistufigen Modulkonzept]] arbeiten. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{CHANGED}&amp;lt;/code&amp;gt;  || Hier werden alle Events kurzzeitig gesammelt, welche für die Eventverarbeitung anstehen. Insbesondere die [[DevelopmentModuleAPI#Readings_.2F_Events|Reading-Funktionen]] speichern hier alle Events zwischen um sie nach Abschluss via [[DevelopmentModuleAPI#DoTrigger|DoTrigger()]] zu verarbeiten. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{FD}&amp;lt;/code&amp;gt;  || Wenn die Definition eine Netzwerkverbindung oder serielle Schnittstelle geöffnet hat (z.B. via [[DevIo]]), so wird der entsprechende File-Deskriptor in diesem Internal gespeichert. Damit kann FHEM alle geöffneten Filedeskriptoren der entsprechenden Definition zuordnen um bei ankommenden Daten die Definition via [[DevelopmentModuleIntro#X_Read|Read-Funktion]] damit zu versorgen.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{EXCEPT_FD}&amp;lt;/code&amp;gt;  || Ähnlich wie &amp;lt;code&amp;gt;$hash-&amp;gt;{FD}&amp;lt;/code&amp;gt;. Sofern die Definition in &amp;lt;code&amp;gt;[[#Wichtige_globale_Variablen_aus_fhem.pl|%selectlist]]&amp;lt;/code&amp;gt; eingetragen ist und ein Fildeskriptor in diesem Internal gesetzt ist, wird bei einer auftretenden Exception bzw. Interrupt die [[DevelopmentModuleIntro#X_Except|Except]]-Funktion des entsprechenden Moduls aufgerufen um darauf zu reagieren.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Generell sollte man die meisten der hier genannten systemweiten Internals nicht modifizieren, da ansonsten die korrekte Funktionsweise von FHEM nicht mehr garantiert werden kann.&lt;br /&gt;
&lt;br /&gt;
== Readings ==&lt;br /&gt;
Daten, welche von einem Gerät gelesen werden und in FHEM in einer für Menschen verständlichen Form zur Verfügung gestellt werden können, werden Readings genannt. Sie geben den Status des Gerätes wieder und erzeugen Events innerhalb von FHEM auf die andere Geräte reagieren können. Sie werden als Unterstruktur des Hashes der jeweiligen Geräteinstanz gespeichert, beispielsweise &lt;br /&gt;
*&amp;lt;code&amp;gt;$hash-&amp;gt;{READINGS}{temperature}{VAL}&amp;lt;/code&amp;gt; für die Temperatur eines Fühlers&lt;br /&gt;
*&amp;lt;code&amp;gt;$hash-&amp;gt;{READINGS}{temperature}{TIME}&amp;lt;/code&amp;gt; für den Zeitstempel der Messung&lt;br /&gt;
&lt;br /&gt;
Für den lesenden Zugriff auf Readings steht die Funktion &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#ReadingsVal|ReadingsVal()]]&amp;lt;/code&amp;gt; zur Verfügung. Ein direkter Zugriff auf die Datenstruktur sollte nicht vorgenommen werden.&lt;br /&gt;
&lt;br /&gt;
Readings werden im Statefile von FHEM automatisch auf der Festplatte zwischengespeichert, damit sie nach einem Neustart sofort wieder zur Verfügung stehen. Dadurch ist der letzte Status eines Gerätes vor einem Neustart nachvollziehbar.&lt;br /&gt;
&lt;br /&gt;
Readings, die mit einem Punkt im Namen beginnen, haben eine funktionale Besonderheit. Sie werden im FHEMWEB nicht angezeigt und können somit als &amp;quot;Permanentspeicher&amp;quot; für kleinere Daten innerhalb des Moduls genutzt werden. Um größere Datenmengen permanent zu speichern sollte man jedoch die Funktion &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#setKeyValue|setKeyValue()]]&amp;lt;/code&amp;gt; verwenden.&lt;br /&gt;
&lt;br /&gt;
Zum Setzen von Readings sollen &lt;br /&gt;
*bei Gruppen von Readings der Funktionsblock &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#readingsBeginUpdate|readingsBeginUpdate()]]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#readingsBulkUpdate|readingsBulkUpdate()]]&amp;lt;/code&amp;gt; (mehrfach wiederholt), &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#readingsEndUpdate|readingsEndUpdate()]]&amp;lt;/code&amp;gt;&lt;br /&gt;
*bei einzelnen Updates die Funktion &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#readingsSingleUpdate|readingsSingleUpdate()]]&amp;lt;/code&amp;gt; &lt;br /&gt;
aufgerufen werden. Dabei kann man auch angeben, ob dabei ein Event ausgelöst werden soll oder nicht. Events erzeugen, je nach Hardwareperformance, spürbare Last auf dem System (siehe [[DevelopmentModuleIntro#X_Notify|NotifyFn]]), das Ändern von Readings ohne dass dabei Events erzeugt werden jedoch nicht.&lt;br /&gt;
&lt;br /&gt;
Eine Sequenz zum Setzen von Readings könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
readingsBeginUpdate($hash);&lt;br /&gt;
readingsBulkUpdate($hash, $readingName1, $wert1 );&lt;br /&gt;
readingsBulkUpdate($hash, $readingName2, $wert2 );&lt;br /&gt;
readingsEndUpdate($hash, 1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Events ==&lt;br /&gt;
&lt;br /&gt;
FHEM verfügt über einen Event-Mechanismus um Änderungen verschiedenster Art an einzelne oder alle Definitionen mitzuteilen. Jedes Modul (und damit alle Definitionen dieses Moduls) können auf Events von FHEM selber (Definition &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;) oder von anderen Definitionen reagieren und dadurch selber aktiv werden. Ein Event wird innerhalb von FHEM als Zeichenkette behandelt.&lt;br /&gt;
&lt;br /&gt;
Events sind grundsätzlich immer definitionsbezogen. Das bedeutet, dass ein Event immer in Verbindung mit einem Definitionsnamen erzeugt wird. Jede Definition, welche ein Event verarbeitet, erhält den Definitions-Hash (&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;) der auslösenden Definition.&lt;br /&gt;
&lt;br /&gt;
Events werden typischerweise bei der Erstellung von Readings implizit für jedes einzelne Reading erzeugt. Es gibt jedoch auch Events die nichts mit Readings zu tun haben um anderweitige Änderungen bekannt zu geben.&lt;br /&gt;
&lt;br /&gt;
Eigene Events können in FHEM mit der Funktion [[DevelopmentModuleAPI#DoTrigger|DoTrigger()]] erzeugt werden. Um auf Events in einem Modul reagieren zu können, muss eine [[#X_Notify|Notify]]-Funktion implementiert sein. Sobald ein oder mehrere Events für eine Definition getriggert werden, prüft FHEM, welche Definitionen über Events der auslösenden Definition informiert werden möchten. Diese werden dann nacheinander in einer bestimmten Reihenfolge durch Aufruf der [[#X_Notify|Notify]]-Funktion über anstehende Events in Kenntnis gesetzt. Es obliegt dann dem jeweiligen Modul, wie es auf die Events reagiert.&lt;br /&gt;
&lt;br /&gt;
=== globale Events ===&lt;br /&gt;
&lt;br /&gt;
Als &amp;quot;globale Events&amp;quot; werden alle Events bezeichnet, die durch die Definition &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; erzeugt werden. Es handelt sich hierbei um Events die Strukturänderungen in der Konfiguration, als auch systemweite Ereignisse zu FHEM selbst signalisieren.&lt;br /&gt;
&lt;br /&gt;
Hier eine kurze Zusammenfassung, welche Events durch &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; getriggert werden können:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Allgemeine Events:&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Event-Text !! Beschreibung.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;INITIALIZED&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Der Start von FHEM ist abgeschlossen. Sämtliche Definitionen und Attribute wurden aus der Konfiguration (fhem.cfg oder configDB) eingelesen, sowie sämtliche Readings sind aus dem State-File eingelesen und stehen nun voll umfänglich zur Verfügung.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;REREADCFG&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die Konfiguration wurde erneut eingelesen. Dies bedeutet, es wurden alle Definitionen/Attribute/Readings aus FHEM entfernt und durch Einlesen der Konfiguration neu angelegt. (FHEM-Befehl: &amp;quot;rereadcfg&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;SAVE&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die laufende Konfiguration soll gespeichert werden (in fhem.cfg oder configDB). Dieses Event wird &#039;&#039;&#039;VOR&#039;&#039;&#039; dem Speichern der Konfiguration getriggert. Sobald der Trigger verarbeitet wurde, beginnt das Speichern der Konfiguration. (FHEM-Befehl: &amp;quot;save&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;SHUTDOWN&amp;lt;/code&amp;gt;&#039;&#039;&#039; || FHEM wird sich beenden. (FHEM-Befehl: &amp;quot;shutdown&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Es wurde ein Update erfolgreich installiert. (FHEM-Befehl: &amp;quot;update&amp;quot;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Definitionsbezogene Events:&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Event-Text !! Beschreibung.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;DEFINED &#039;&#039;&amp;lt;Name&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Es wurde eine neue Definition mit Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;Name&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt; angelegt. (FHEM-Befehl: &amp;quot;define&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;DELETED &#039;&#039;&amp;lt;Name&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die Definition mit dem Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;Name&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt; wurde gelöscht. (FHEM-Befehl: &amp;quot;delete&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;RENAMED &#039;&#039;&amp;lt;Alt&amp;gt;&#039;&#039; &#039;&#039;&amp;lt;Neu&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die Definition mit dem Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;Alt&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt; wurde in den Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;Neu&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt; umbenannt. (FHEM-Befehl: &amp;quot;rename&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;MODIFIED &#039;&#039;&amp;lt;Name&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die Definition mit dem Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;Name&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt; wurde modifiziert. (FHEM-Befehl: &amp;quot;modify&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;UNDEFINED &#039;&#039;&amp;lt;Name&amp;gt; &amp;lt;Modul&amp;gt; &amp;lt;Define-Parameter&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Es wurde eine Nachricht von einem physikalischen Modul (siehe [[#Zweistufiges Modell für Module|zweistufiges Modulkonzept]]) erhalten, für die keine passende logische Definition in FHEM existiert. Details dazu, siehe dazu Abschnitt [[#Automatisches Anlegen von logischen Gerätedefinitionen (autocreate)|Automatisches Anlegen von logischen Gerätedefinitionen (autocreate)]].&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Begrenzung von Events ===&lt;br /&gt;
&lt;br /&gt;
Ein Modul, welches Events verarbeitet, kann die Eventverarbeitung auf bestimmte Definitionen begrenzen. Dadurch werden nur Events an das Modul gemeldet (via [[#X_Notify|X_Notify()]]), welche von einer oder mehreren bestimmten Definitionen getriggert wurden. Dadurch werden unnötige Events nicht an das Modul gemeldet und schont somit Ressourcen.&lt;br /&gt;
&lt;br /&gt;
Standardmäßig werden sämtliche Events ohne Begrenzung an ein Modul gemeldet, welches eine [[#X_Notify|Notify]]-Funktion implementiert hat und somit Events verarbeiten kann. Details zur Begrenzung von Events findet man in der Beschreibung zur Modulfunktion [[#X_Notify|X_Notify()]].&lt;br /&gt;
&lt;br /&gt;
=== Reihenfolge der Eventverarbeitung ===&lt;br /&gt;
&lt;br /&gt;
Ein getriggertes Event wird nacheinander gegen jede Definition geprüft, deren Modul eine [[#X_Notify|Notify]]-Funktion implementiert hat. Dies bedeutet, jede Definition wird nacheinander durch Aufruf der [[#X_Notify|Notify]]-Funktion mit dem Definitionshash der auslösenden Definition aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Unter bestimmten Umständen kann es erforderlich sein, in diese Reihenfolge einzugreifen. Beispielsweise wenn das eigene Modul und deren Definitionen das Event als letztes oder erstes verarbeiten müssen. Ein Beispiel bietet hierbei das Modul [[dewpoint]], welches Events vor allen anderen Modulen verarbeiten muss.&lt;br /&gt;
&lt;br /&gt;
Details, wie man die Reihenfolge der Eventverarbeitung steuern kann, findet man in der Beschreibung zur Modulfunktion [[#X_Notify|X_Notify()]].&lt;br /&gt;
&lt;br /&gt;
== Attribute ==&lt;br /&gt;
Damit der Nutzer das Verhalten einer einzelnen Gerätedefinition zur Laufzeit individuell anpassen kann, gibt es in FHEM für jede Definition sogenannte Attribute, welche mit dem Befehl &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt; gesetzt werden können.&lt;br /&gt;
Diese stehen dann dem Modul unmittelbar zur Verfügung um das Verhalten während der Ausführung zu beeinflussen. Attribute werden zusammen mit dem &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Befehl der jeweiligen Definition beim Speichern der aktuellen Konfiguration von FHEM in die Konfigurationsdatei geschrieben. Beim Neustart werden die entsprechenden Befehle ausgeführt um alle Definition inkl. Attribute wieder anzulegen. Zur Laufzeit werden Attribute in dem globalen Hash &amp;lt;code&amp;gt;%attr&amp;lt;/code&amp;gt; mit dem Definitionsnamen als Index (&amp;lt;code&amp;gt;$attr{$name} = $value&amp;lt;/code&amp;gt;) gespeichert. Ein Attribut mit dem Namen &amp;lt;code&amp;gt;header&amp;lt;/code&amp;gt; würde beispielsweise mit &amp;lt;code&amp;gt;$attr{$name}{header}&amp;lt;/code&amp;gt; adressiert. Generell sollte &amp;lt;code&amp;gt;%attr&amp;lt;/code&amp;gt; nicht durch direkten Zugriff manipuliert/benutzt werden.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen von Attributen sollte die Funktion [[DevelopmentModuleAPI#AttrVal|AttrVal()]] verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Welche Attribute ein Modul unterstützt muss in der Funktion &amp;lt;code&amp;gt;[[#X_Initialize|X_Initialize]]&amp;lt;/code&amp;gt; durch Setzen von &amp;lt;code&amp;gt;$hash-&amp;gt;{AttrList}&amp;lt;/code&amp;gt; bekannt gemacht werden (siehe unten).&lt;br /&gt;
&lt;br /&gt;
Wenn beim Setzen von Attributen in einer Gerätedefinition entsprechende Werte geprüft werden sollen oder zusätzliche Funktionalitäten implementiert werden müssen, dann muss dies in der Funktion &amp;lt;code&amp;gt;[[#X_Attr|X_Attr]]&amp;lt;/code&amp;gt; (siehe unten) implementiert werden. Hier kann man bspw. einen Syntaxcheck für Attribut-Werte implementieren um ungültige Werte zurückzuweisen.&lt;br /&gt;
&lt;br /&gt;
== Modulfunktionen ==&lt;br /&gt;
&lt;br /&gt;
Damit fhem.pl ein Modul nutzen kann, muss dieses entsprechende Funktionen mit einer vorgegebenen Aufrufsyntax implementieren. Durch die Bekanntgabe dieser modulspezifischen Funktionen können Daten zwischen fhem.pl und einem Modul entsprechend ausgetauscht werden. Es gibt verschiedene Arten von Funktionen die ein Modul anbieten muss bzw. kann, je nach Funktionsumfang.&lt;br /&gt;
&lt;br /&gt;
=== Die wichtigsten Funktionen in einem Modul ===&lt;br /&gt;
&lt;br /&gt;
Folgende Funktion muss ein Modul mit dem beispielhaften Namen &amp;quot;X&amp;quot; mindestens bereitstellen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left&amp;quot; | Funktionsname !! style=&amp;quot;text-align:left&amp;quot; | Kurzbeschreibung&lt;br /&gt;
|-&lt;br /&gt;
|  [[#X_Initialize|X_Initialize]] || Initialisiert das Modul und gibt den Namen zusätzlicher Modulfunktionen bekannt, sowie modulspezifische Einstellungen. Wird direkt nach dem erfolgreichen Laden des Moduls durch fhem.pl aufgerufen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die folgenden Funktionen sind die wichtigsten Funktionen, welche je nach Anwendungsfall zu implementieren sind. Es handelt sich hierbei um die wichtigsten Vertreter, welche in den meisten Modulen Verwendung finden. Nicht alle Funktionen machen jedoch in jedem Modul Sinn. Generell sollte auch hier bei jeder Funktion der Modulname vorangestellt werden um ein einheitliches Namensschema zu gewährleisten. Hier die wichtigsten Modulfunktionen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left&amp;quot; | Funktionsname !! style=&amp;quot;text-align:left&amp;quot; class=&amp;quot;unsortable&amp;quot;| Kurzbeschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Define|X_Define]] || Wird im Rahmen des &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Befehls aufgerufen.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Undef|X_Undef]] || Wird im Rahmen des &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt;-Befehls, sowie &amp;lt;code&amp;gt;rereadcfg&amp;lt;/code&amp;gt;-Befehl aufgerufen. Dient zum Abbau von offenen Verbindungen, Timern, etc.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Delete|X_Delete]] || Wird im Rahmen des beim &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt;-Befehls aufgerufen wenn das Gerät endgültig gelöscht wird um weiterführende Aktionen vor dem Löschen durchzuführen.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Get|X_Get]] || Wird im Rahmen des &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt;-Befehls aufgerufen um Daten vom Gerät abzufragen&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Set|X_Set]]  || Wird im Rahmen des &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt;-Befehls aufgerufen um Daten an das Gerät zu senden.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Attr|X_Attr]]  || Wird im Rahmen des &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt;-Befehls aufgerufen um Attributwerte zu prüfen)&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Read|X_Read]]  || Wird durch FHEM aufgerufen, wenn ein gelisteter Filedeskriptor in &amp;lt;code&amp;gt;[[#Wichtige_globale_Variablen_aus_fhem.pl|%selectlist]]&amp;lt;/code&amp;gt; Daten zum Lesen bereitstellt.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Ready|X_Ready]]  || Wird unter Windows durch FHEM aufgerufen um zyklisch einen seriellen Filedeskriptor auf lesbare Daten zu prüfen. Unter Linux dient diese Funktion dem Wiederaufbau verlorener Verbindungen.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Notify|X_Notify]]  || Verarbeitet Events von anderen Geräten innerhalb von FHEM&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Rename|X_Rename]] || Wird aufgerufen, wenn ein Gerät umbenannt wird.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Shutdown|X_Shutdown]] || Wird beim Herunterfahren von FHEM ausgeführt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Funktionen werden in diesem Abschnitt genauer beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== X_Initialize ====&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;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; im Namen muss dabei auf den Namen des Moduls bzw. des definierten Gerätetyps geändert werden. Im Modul mit der Datei &amp;lt;code&amp;gt;36_JeeLink.pm&amp;lt;/code&amp;gt; beispielsweise ist der Name der Funktion &amp;lt;code&amp;gt;JeeLink_Initialize&amp;lt;/code&amp;gt;. Die Funktion wird von fhem.pl nach dem Laden des Moduls aufgerufen und bekommt eine leere Hashreferenz für den Initialisierungsvorgang übergeben. &lt;br /&gt;
&lt;br /&gt;
Dieser Hash muss nun von X_Initialize mit allen modulrelevanten Funktionsnamen gefüllt werden. Anschließend wird dieser Hash durch fhem.pl im globalen Hash &amp;lt;code&amp;gt;%modules&amp;lt;/code&amp;gt; gespeichert. &amp;lt;code&amp;gt;$modules{ModulName}&amp;lt;/code&amp;gt; wäre dabei der Hash für das Modul mit dem Namen &amp;lt;code&amp;gt;ModulName&amp;lt;/code&amp;gt;. Es handelt sich also nicht um den oben beschriebenen Hash der Geräteinstanzen sondern einen Hash, der für jedes Modul existiert und modulspezifische Daten wie bspw. die implementierten Modulfunktionen enthält. Die Initialize-Funktion setzt diese Funktionsnamen, in den Hash des Moduls wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$hash-&amp;gt;{DefFn}         = &amp;quot;X_Define&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{UndefFn}       = &amp;quot;X_Undef&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{DeleteFn}      = &amp;quot;X_Delete&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{SetFn}         = &amp;quot;X_Set&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{GetFn}         = &amp;quot;X_Get&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{AttrFn}        = &amp;quot;X_Attr&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{ReadFn}        = &amp;quot;X_Read&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{ReadyFn}       = &amp;quot;X_Ready&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{NotifyFn}      = &amp;quot;X_Notify&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{RenameFn}      = &amp;quot;X_Rename&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{ShutdownFn}    = &amp;quot;X_Shutdown&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um eine entsprechende Funktion in FHEM bekannt zu machen muss dazu der Funktionsname, wie er im Modul als &amp;lt;code&amp;gt;sub &amp;amp;lt;&#039;&#039;Funktionsname&#039;&#039;&amp;amp;gt;() { ... }&amp;lt;/code&amp;gt; definiert ist, als Zeichenkette in &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; gesetzt werden. Dabei sollten die entsprechenden Funktionsnamen immer den Modulnamen (in diesem Beispiel &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt;) als Präfix verwenden.&lt;br /&gt;
Auf diese Weise können sämtliche modulspezifisch implementierten Funktionen wie &amp;lt;code&amp;gt;X_Read&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;X_Parse&amp;lt;/code&amp;gt; etc. durch Zuweisung an &amp;lt;code&amp;gt;$hash-&amp;gt;{ReadFn}&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;$hash-&amp;gt;{ParseFn}&amp;lt;/code&amp;gt; usw. bekannt gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Darüber hinaus sollten die vom Modul unterstützten Attribute definiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$hash-&amp;gt;{AttrList} =&lt;br /&gt;
  &amp;quot;do_not_notify:1,0 &amp;quot; . &lt;br /&gt;
  &amp;quot;header &amp;quot; .&lt;br /&gt;
  $readingFnAttributes;  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Auflistung aller unterstützten modulspezifischen Attribute erfolgt in Form einer durch Leerzeichen getrennten Liste in &amp;lt;code&amp;gt;$hash-&amp;gt;{AttrList}}&amp;lt;/code&amp;gt;. Es gibt in FHEM globale Attribute, die in allen Gerätedefinitionen verfügbar sind und nur modulspezifische Attribute die jedes Modul via &amp;lt;code&amp;gt;$hash-&amp;gt;{AttrList}&amp;lt;/code&amp;gt; über die eigene Initialize-Funktion setzt.  In fhem.pl werden dann die entsprechenden Attributwerte beim Aufruf eines &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt;-Befehls in die globale Datenstruktur &amp;lt;code&amp;gt;$attr{$name}&amp;lt;/code&amp;gt;, z.B. &amp;lt;code&amp;gt;$attr{$name}{header}&amp;lt;/code&amp;gt; für das Attribut &amp;lt;code&amp;gt;header&amp;lt;/code&amp;gt; gespeichert. Falls im Modul weitere Aktionen oder Prüfungen beim Setzen eines Attributs nötig sind, dann kann wie im Beispiel oben die [[#X_Attr|Attr]]-Funktion implementiert und in der Initialize-Funktion bekannt gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Die Variable &amp;lt;code&amp;gt;$readingFnAttributes&amp;lt;/code&amp;gt;, die im obigen Beispiel an die Liste der unterstützten Attribute angefügt wird, definiert Attributnamen, die dann zusätzlich gemacht werden, wenn das Modul zum Setzen von Readings die Funktionen &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#readingsBeginUpdate|readingsBeginUpdate()]]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#readingsBulkUpdate|readingsBulkUpdate()]]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#readingsEndUpdate|readingsEndUpdate()]]&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#readingsSingleUpdate|readingsSingleUpdate()]]&amp;lt;/code&amp;gt; verwendet. In diesen Funktionen werden Attribute wie &amp;lt;code&amp;gt;event-min-interval&amp;lt;/code&amp;gt; oder auch &amp;lt;code&amp;gt;event-on-change-reading&amp;lt;/code&amp;gt; ausgewertet. Für Details hierzu siehe commandref zu {{Link2CmdRef|Anker=readingFnAttributes|Label=readingFnAttributes}}.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Nutzung von parseParams()&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt; [[DevelopmentModuleAPI#parseParams|parseParams()]]&amp;lt;/code&amp;gt; unterstützt Modulautoren beim Parsen von Übergabeparametern, welche bei &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; Kommandos an die entsprechenden Modulfunktionen übergeben werden. Dadurch lassen sich auf einfache Weise insbesondere komplexe Parameter (wie bspw. Perl-Ausdrücke) sehr einfach parsen.&lt;br /&gt;
&lt;br /&gt;
Diese Zusatzfunktion kann man in der Initialize-Funktion einfach über folgenden Parameter für [[#X_Define|Define]]-, [[#X_Get|Get]]- und [[#X_Set|Set]]-Funktion modulweit aktivieren:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$hash-&amp;gt;{parseParams} = 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Sobald es gesetzt ist wird automatisch durch fhem.pl &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#parseParams|parseParams()]]&amp;lt;/code&amp;gt; aufgerufen und die an die [[#X_Define|Define]]-, [[#X_Get|Get]]- und [[#X_Set|Set]]-Funktion übergebenen Parameter ändern sich wie weiter unten in den jeweiligen Funktionen beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== X_Define ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Define($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $def ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
 &lt;br /&gt;
	return $error;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Define-Funktion eines Moduls wird von FHEM aufgerufen wenn der Define-Befehl für ein Geräte ausgeführt wird und das Modul bereits geladen und mit der Initialize-Funktion initialisiert ist. Sie ist typischerweise dazu da, die übergebenen Parameter zu prüfen und an geeigneter Stelle zu speichern sowie einen Kommunikationsweg zum Gerät zu öffnen (z.B. TCP-Verbindung, USB-Schnittstelle o.ä.) oder einen [[#Pollen_von_Geräten|Status-Timer]] zu starten.&lt;br /&gt;
Sie beginnt typischerweise mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Define($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $def ) = @_;&lt;br /&gt;
	my @a = split( &amp;quot;[ \t][ \t]*&amp;quot;, $def );&lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als Übergabeparameter bekommt die Define-Funktion den Hash der Geräteinstanz sowie den die im &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Befehl übergebenen Parameter. Welche bzw. wie viele Parameter &lt;br /&gt;
akzeptiert werden und welcher Syntax diese entsprechen müssen ist Sache dieser Funktion. Im obigen Beispiel wird die Argumentzeile &amp;lt;code&amp;gt;$def&amp;lt;/code&amp;gt; in ein Array aufgeteilt (durch Leerzeichen/Tabulator getrennt) und so können die vom Modul bzw. der Define-Funktion erwarteten Werte über das Array der Reihe nach verarbeitet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $name   = $a[0];&lt;br /&gt;
my $module = $a[1];&lt;br /&gt;
my $url    = $a[2];&lt;br /&gt;
my $inter  = 300;&lt;br /&gt;
&lt;br /&gt;
if(int(@a) == 4) { &lt;br /&gt;
	$inter = $a[3]; &lt;br /&gt;
	if ($inter &amp;lt; 5) {&lt;br /&gt;
		return &amp;quot;interval too small, please use something &amp;gt; 5s, default is 300 seconds&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit die übergebenen Werte auch anderen Funktionen zur Verfügung stehen und an die jeweilige Geräteinstanz gebunden sind, werden die Werte typischerweise als Internals im Hash der Geräteinstanz gespeichert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$hash-&amp;gt;{url} 		= $url;&lt;br /&gt;
$hash-&amp;gt;{Interval}	= $inter;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Parameter korrekt verarbeitet wurden, wird in der Regel die erste Verbindung zum Gerät aufgebaut. Je nach Art des Geräts kann das eine permanente Datenverbindung sein (z.B. serielle Schnittstelle oder TCP-Verbindung) oder das Starten eines regelmäßigen Timers, der zyklisch den Status z.B. via [[HttpUtils|HTTP]] ausliest.&lt;br /&gt;
&lt;br /&gt;
Sollten im Rahmen der Define-Funktion Syntax-Probleme der Übergabeparameter festgestellt werden oder es kann bspw. keine Verbindung aufgebaut werden, so ist als Funktionsrückgabewert eine entsprechende Fehlermeldung zurückzugeben. Nur wenn alle Übergabeparameter akzeptiert werden, darf &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben werden. Sobald eine Define-Funktion eine Fehlermeldung zurückmeldet, wird der define-Befehl durch FHEM zurückgewiesen und der User erhält die Fehlermeldung, welche die Define-Funktion produziert hat, als Ausgabe zurück.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Verfügbarkeit von Attributen&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Während die Define-Funktion ausgeführt wird, sollte man nicht davon ausgehen, dass alle vom Nutzer konfigurierten Attribute via [[DevelopmentModuleAPI#AttrVal|AttrVal()]] verfügbar sind. Attribute stehen in der Define-Funktion nur dann zur Verfügung, wenn FHEM sich nicht in der Initialisierungsphase befindet (globale Variable &amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; ist wahr; der Nutzer hat die Gerätedefinition modifiziert). Daher sollte man weiterführende Funktion, welche auf gesetzte Attribute angewiesen sind, nur dann in der Define-Funktion starten, wenn &amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; zutrifft.&lt;br /&gt;
&lt;br /&gt;
Andernfalls sollte man den Aufruf in der Notify-Funktion durchführen sobald &amp;lt;code&amp;gt;global:INITIALIZED&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;global:REREADCFG&amp;lt;/code&amp;gt; getriggert wurde:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Define($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $def ) = @_;&lt;br /&gt;
 &lt;br /&gt;
	...&lt;br /&gt;
&lt;br /&gt;
	$hash-&amp;gt;{NOTIFYDEV} = &amp;quot;global&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
	X_FunctionWhoNeedsAttr($hash) if($init_done);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub X_Notify($$)&lt;br /&gt;
{&lt;br /&gt;
	my ($own_hash, $dev_hash) = @_;&lt;br /&gt;
	my $ownName = $own_hash-&amp;gt;{NAME}; # own name / hash&lt;br /&gt;
 &lt;br /&gt;
	return &amp;quot;&amp;quot; if(IsDisabled($ownName)); # Return without any further action if the module is disabled&lt;br /&gt;
 &lt;br /&gt;
	my $devName = $dev_hash-&amp;gt;{NAME}; # Device that created the events&lt;br /&gt;
	my $events = deviceEvents($dev_hash, 1);&lt;br /&gt;
&lt;br /&gt;
	if($devName eq &amp;quot;global&amp;quot; &amp;amp;&amp;amp; grep(m/^INITIALIZED|REREADCFG$/, @{$events}))&lt;br /&gt;
	{&lt;br /&gt;
		 X_FunctionWhoNeedsAttr($hash);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird die Modulfunktion X_FunctionWhoNeedsAttr() nach dem Start erst aufgerufen, wenn alle Attribute aus der Konfiguration geladen wurden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Nutzung von parseParams()&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Aufteilen und Parsen von &amp;lt;code&amp;gt;$def&amp;lt;/code&amp;gt; lässt sich die Funktion [[DevelopmentModuleAPI#parseParams|parseParams()]] verwenden um die einzelnen Argumente einfach zu parsen. Wenn in [[#X_Initialize|X_Initialize()]] &amp;lt;code&amp;gt;$hash-&amp;gt;{parseParams} = 1;&amp;lt;/code&amp;gt; gesetzt wurde dann wird parseParams() automatisch aufgerufen und X_Define() ändert sich wie folgt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Define($$$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $a, $h ) = @_;&lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die genauen Möglichkeiten von parseParams() sind in dem entsprechenden [[DevelopmentModuleAPI#parseParams|Artikel]] dokumentiert.&lt;br /&gt;
&lt;br /&gt;
==== X_Undef ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Undef ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $name ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
 &lt;br /&gt;
	return $error;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Undef-Funktion wird aufgerufen wenn ein Gerät mit &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; gelöscht wird oder bei der Abarbeitung des Befehls &amp;lt;code&amp;gt;rereadcfg&amp;lt;/code&amp;gt;, der ebenfalls alle Geräte löscht und danach das Konfigurationsfile neu einliest. Entsprechend müssen in der Funktion typische Aufräumarbeiten durchgeführt werden wie das saubere Schließen von Verbindungen oder das Entfernen von internen Timern, sofern diese im Modul zum Pollen verwendet wurden (siehe Abschnitt [[#Pollen_von_Geräten|Pollen von Geräten]]). &lt;br /&gt;
&lt;br /&gt;
Zugewiesene Variablen im Hash der Geräteinstanz, Internals oder Readings müssen hier nicht gelöscht werden. In fhem.pl werden die entsprechenden Strukturen beim Löschen der Geräteinstanz ohnehin vollständig gelöscht.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Undef($$)    &lt;br /&gt;
{                     &lt;br /&gt;
	my ( $hash, $name) = @_;       &lt;br /&gt;
	DevIo_CloseDev($hash);         &lt;br /&gt;
	RemoveInternalTimer($hash);    &lt;br /&gt;
	return undef;                  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollten im Rahmen der Undef-Funktion Probleme festgestellt werden, die ein Löschen nicht zulassen, so ist als Funktionsrückgabewert eine entsprechende Fehlermeldung zurückzugeben. Nur wenn die Undef-Funktion erfolgreich durchgeführt wurde, darf &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben werden. Nur dann wird eine Gerätedefinition von FHEM auch tatsächlich gelöscht bzw. neu angelegt. Sollte die Undef-Funktion jedoch eine Fehlermeldung zurückgeben, wird der entsprechende Vorgang (&amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;rereadcfg&amp;lt;/code&amp;gt;) für dieses Gerät abgebrochen. Es bleibt dann unverändert in FHEM bestehen.&lt;br /&gt;
&lt;br /&gt;
==== X_Delete ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Delete ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $name ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
 &lt;br /&gt;
	return $error;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Delete-Funktion ist das Gegenstück zur Funktion [[#X_Define|X_Define]] und wird aufgerufen wenn ein Gerät mit dem Befehl &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; gelöscht wird. &lt;br /&gt;
&lt;br /&gt;
Wenn ein Gerät in FHEM gelöscht wird, wird zuerst die Funktion [[#X_Undef|X_Undef]] aufgerufen um offene Verbindungen zu schließen, anschließend wird die Funktion X_Delete aufgerufen. Diese dient eher zum Aufräumen von dauerhaften Daten, welche durch das Modul evtl. für dieses Gerät spezifisch erstellt worden sind. Es geht hier also eher darum, alle Spuren sowohl im laufenden FHEM-Prozess, als auch dauerhafte Daten bspw. im physikalischen Gerät zu löschen die mit dieser Gerätedefinition zu tun haben.&lt;br /&gt;
&lt;br /&gt;
Dies kann z.B. folgendes sein:&lt;br /&gt;
&lt;br /&gt;
* Löschen von Dateien im Dateisystem die während der Nutzung dieses Geräts angelegt worden sind.&lt;br /&gt;
* Lösen von evtl. Pairings mit dem physikalischen Gerät &lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Delete($$)    &lt;br /&gt;
{                     &lt;br /&gt;
	my ( $hash, $name ) = @_;       &lt;br /&gt;
&lt;br /&gt;
	# Löschen von Geräte-assoziiertem Temp-File&lt;br /&gt;
	unlink($attr{global}{modpath}.&amp;quot;/FHEM/FhemUtils/$name.tmp&amp;quot;;)&lt;br /&gt;
&lt;br /&gt;
	return undef;&lt;br /&gt;
}    &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollten im Rahmen der Delete-Funktion Probleme festgestellt werden, die ein Löschen nicht zulassen, so ist als Funktionsrückgabewert eine entsprechende Fehlermeldung zurückzugeben. Nur die Delete-Funktion erfolgreich durchgeführt wurde, darf &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben werden. Nur dann wird eine Gerätedefinition von FHEM auch tatsächlich gelöscht. Sollte die Delete-Funktion eine Fehlermeldung zurückgeben, wird der Löschvorgang abgebrochen und das Gerät bleibt weiter in FHEM bestehen.&lt;br /&gt;
&lt;br /&gt;
==== X_Get ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Get ($$@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $name, $opt, @args ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
 &lt;br /&gt;
	return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Get-Funktion wird aufgerufen wenn der FHEM-Befehl &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; mit einem Gerät dieses Moduls ausgeführt wird. Mit &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; werden typischerweise Werte von einem Gerät abgefragt. In vielen Modulen wird auf diese Weise auch der Zugriff auf generierte Readings ermöglicht. Der Get-Funktion wird dabei der Geräte-Hash, der Gerätename, sowie die Aufrufparameter des get-Befehls übergeben. Als Rückgabewert wird das Ergebnis des entsprechenden Befehls in Form einer Zeichenkette zurückgegeben. Der Rückgabewert &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; hat hierbei keine besondere Bedeutung und wird behandelt wie eine leere Zeichenkette &amp;lt;code&amp;gt;&amp;quot;&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Get($$@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $name, $opt, @args ) = @_;&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;\&amp;quot;get $name\&amp;quot; needs at least one argument&amp;quot; unless(defined($opt));&lt;br /&gt;
&lt;br /&gt;
	if($opt eq &amp;quot;status&amp;quot;) &lt;br /&gt;
	{&lt;br /&gt;
	   ...&lt;br /&gt;
	}&lt;br /&gt;
	elsif($opt eq &amp;quot;power&amp;quot;)&lt;br /&gt;
	{&lt;br /&gt;
	   ...&lt;br /&gt;
	}&lt;br /&gt;
	...&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		return &amp;quot;Unknown argument $opt, choose one of status power [...]&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn eine unbekannte Option an die Get-Funktion übergeben wird, so muss als Rückgabewert der Funktion eine bestimmte Syntax einhalten um FHEM mitzuteilen, welche Optionen für einen Get-Befehl aktuell unterstützt werden. Die Rückgabe muss dabei folgender Syntax entsprechen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;&#039;&#039;unknown&#039;&#039;&#039; argument &#039;&#039;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;[Parameter]&amp;lt;/font&amp;gt;&#039;&#039; &#039;&#039;&#039;choose one of&#039;&#039;&#039; &#039;&#039;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;[Liste möglicher Optionen]&amp;lt;/font&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei sind die fett gedruckten Teile der Rückmeldung besonders wichtig. Sind diese nicht vorhanden, kann FHEM nicht die möglichen Get-Kommandos für das entsprechende Gerät ermitteln. Es muss am Anfang der Meldung das Stichwort &amp;quot;unknown&amp;quot; vorkommen gefolgt von einer frei definierbaren Fehlermeldung (i.d.R der übergebene Parameter, welcher ungültig ist). Anschließend folgt &amp;quot;choose one of&amp;quot; mit einer anschließenden Liste möglicher Optionen getrennt durch ein Leerzeichen. &lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;return &amp;quot;unknown argument $opt choose one of status temperature humidity&amp;quot;;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden als mögliche Optionen für einen Get-Befehl folgende Parameter angegeben:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;temperature&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;humidity&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies würde in folgenden, mögliche Get-Befehle für einen User resultieren:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;get &#039;&#039;&amp;amp;lt;NAME&amp;amp;gt;&#039;&#039; status&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;get &#039;&#039;&amp;amp;lt;NAME&amp;amp;gt;&#039;&#039; temperature&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;get &#039;&#039;&amp;amp;lt;NAME&amp;amp;gt;&#039;&#039; humidity&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe einer solchen Meldung ist sehr wichtig, da sie im GUI-Modul verwendet wird um die möglichen &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt;-Optionen zu ermitteln und als Auswahl anzubieten. Im weiteren Verlauf der Get-Funktion könnte man dann mit dem physischen Gerät kommunizieren und den gefragten Wert direkt abfragen und diesen als Return-Wert der Get-Funktion zurückgeben.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Nutzung von parseParams()&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn in [[#X_Initialize|X_Initialize()]] &amp;lt;code&amp;gt;$hash-&amp;gt;{parseParams} = 1;&amp;lt;/code&amp;gt; gesetzt wurde dann wird [[DevelopmentModuleAPI#parseParams|parseParams()]] automatisch aufgerufen und X_Get() ändert sich wie folgt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Get($$$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $a, $h ) = @_;&lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die genauen Möglichkeiten von parseParams() sind in dem entsprechenden [[DevelopmentModuleAPI#parseParams|Artikel]] dokumentiert.&lt;br /&gt;
&lt;br /&gt;
==== X_Set ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Set ($$@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $name, $cmd, @args ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
&lt;br /&gt;
	return $error;&lt;br /&gt;
	return ($error, $skip_trigger);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Set-Funktion ist das Gegenteil zur [[#X_Get|Get]]-Funktion. Sie ist dafür gedacht, Daten zum physischen Gerät zu schicken, bzw. entsprechende Aktionen im Gerät selber auszulösen. Ein Set-Befehl dient daher der direkten Steuerung des physikalischen Gerätes in dem es bspw. Zustände verändert (wie &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt;). Der Set-Funktion wird dabei der Geräte-Hash, der Gerätename, sowie die Aufrufparameter des set-Befehls übergeben. Als Rückgabewert kann eine Fehlermeldung in Form Zeichenkette zurückgegeben werden. Der Rückgabewert &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; bedeutet hierbei, dass der Set-Befehl erfolgreich durchgeführt wurde. Eine Set-Funktion gibt daher nur im Fehlerfall eine Rückmeldung mit einer entsprechenden Fehlermeldung. Der Wert &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; wird als &amp;quot;erfolgreich&amp;quot; interpretiert. &lt;br /&gt;
&lt;br /&gt;
Standardmäßig wird jeder Set-Befehl, welcher erfolgreich ausgeführt wurde (&amp;lt;code&amp;gt;$error&amp;lt;/code&amp;gt; ist &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt;), als Event getriggert um dies bspw. in einem FileLog festzuhalten. Dieses Verhalten kann optional unterbunden werden indem der zweite Rückgabewert &amp;lt;code&amp;gt;$skip_trigger&amp;lt;/code&amp;gt; auf &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; gesetzt wird. Damit wird das Generieren eines Events für das erfolgreich ausgeführte Set-Kommando unterbunden. Falls nicht gesetzt, wird ein Event erzeugt (&amp;lt;code&amp;gt;$cmd&amp;lt;/code&amp;gt; mit sämtlichen &amp;lt;code&amp;gt;@args&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Rückmeldungen (Fehler) von set-Befehlen sämtlicher Module, die im Rahmen eines ausgeführten [[Notify]] auftreten werden im FHEM Logfile festgehalten.&lt;br /&gt;
&lt;br /&gt;
Falls nur interne Daten, die ausschließlich für das Modul relevant sind, gesetzt werden müssen, so sollte statt Set die [[#X_Attr|Attr]]-Funktion verwendet werden. Attribute werden bei Save-Config auch in der Fhem.cfg gesichert. Set-Befehle nicht, da sie nur zur Steuerungszwecken im laufenden Betrieb von FHEM dienen.&lt;br /&gt;
 &lt;br /&gt;
Eine Set-Funktion ist ähnlich aufgebaut wie die Get-Funktion, sie bekommt jedoch in der Regel weitere zusätzliche Parameter übergeben um Zustände zu setzen. &lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Set($@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $name, $cmd, @args ) = @_;&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;\&amp;quot;set $name\&amp;quot; needs at least one argument&amp;quot; unless(defined($cmd));&lt;br /&gt;
&lt;br /&gt;
	if($cmd eq &amp;quot;status&amp;quot;)&lt;br /&gt;
	{&lt;br /&gt;
	   if($args[0] eq &amp;quot;up&amp;quot;)&lt;br /&gt;
	   {&lt;br /&gt;
	      ...&lt;br /&gt;
	   }&lt;br /&gt;
	   elsif($args[0] eq &amp;quot;down&amp;quot;)&lt;br /&gt;
	   {&lt;br /&gt;
	      ...&lt;br /&gt;
	   }&lt;br /&gt;
	   else&lt;br /&gt;
	   {&lt;br /&gt;
	      return &amp;quot;Unknown value $args[0] for $cmd, choose one of status power&amp;quot;;&lt;br /&gt;
	   }   &lt;br /&gt;
	}&lt;br /&gt;
	elsif($cmd eq &amp;quot;power&amp;quot;)&lt;br /&gt;
	{&lt;br /&gt;
	   if($args[0] eq &amp;quot;on&amp;quot;)&lt;br /&gt;
	   {&lt;br /&gt;
	      ...&lt;br /&gt;
	   }&lt;br /&gt;
	   elsif($args[0] eq &amp;quot;off&amp;quot;)&lt;br /&gt;
	   {&lt;br /&gt;
	      ...&lt;br /&gt;
	   }  &lt;br /&gt;
	   else&lt;br /&gt;
	   {&lt;br /&gt;
	      return &amp;quot;Unknown value $args[0] for $cmd, choose one of status power&amp;quot;;&lt;br /&gt;
	   }       &lt;br /&gt;
	}&lt;br /&gt;
	...&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		return &amp;quot;Unknown argument $cmd, choose one of status power&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn eine unbekannte Option an die Set-Funktion übergeben wird, so muss als Rückgabewert der Funktion eine bestimmte Syntax eingehalten werden um FHEM mitzuteilen, welche Optionen für einen Set-Befehl aktuell unterstützt werden. Die Rückgabe muss dabei folgender Syntax entsprechen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;&#039;&#039;unknown&#039;&#039;&#039; argument &#039;&#039;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;[Parameter]&amp;lt;/font&amp;gt;&#039;&#039; &#039;&#039;&#039;choose one of&#039;&#039;&#039; &#039;&#039;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;[Liste möglicher Optionen]&amp;lt;/font&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei sind die fett gedruckten Teile der Rückmeldung besonders wichtig. Sind diese nicht vorhanden, kann FHEM nicht die möglichen Set-Kommandos für das entsprechende Gerät ermitteln. Es muss am Anfang der Meldung das Stichwort &amp;quot;unknown&amp;quot; vorkommen gefolgt von einer frei definierbaren Fehlermeldung (i.d.R der übergebene Parameter, welcher ungültig ist). Anschließend folgt &amp;quot;choose one of&amp;quot; mit einer anschließenden Liste möglicher Optionen getrennt durch ein Leerzeichen. &lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;return &amp;quot;unknown argument $cmd choose one of status power&amp;quot;;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden als mögliche Optionen für einen Set-Befehl folgende Parameter angegeben:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;power&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies würde in folgenden, mögliche Set-Befehle für einen User resultieren:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set &#039;&#039;&amp;amp;lt;NAME&amp;amp;gt;&#039;&#039; status&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set &#039;&#039;&amp;amp;lt;NAME&amp;amp;gt;&#039;&#039; power&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe einer solchen Meldung ist sehr wichtig, da sie im Modul [[FHEMWEB]] verwendet wird um die möglichen &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt;-Optionen zu ermitteln und als Auswahl anzubieten.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Nutzung von SetExtensions.pm&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man dem Nutzer zusätzlich zu den Set-Befehlen &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; auch weiterführende Befehle wie &amp;lt;code&amp;gt;on-for-timer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;on-till&amp;lt;/code&amp;gt;, usw. anbieten möchte, obwohl die zu steuernde Hardware solche Kommandos nicht unterstützt, kann man dies über das Hilfsmodul SetExtensions.pm realisieren.&lt;br /&gt;
&lt;br /&gt;
Das Hilfsmodul SetExtensions.pm bietet weiterführende Set-Kommandos basierend auf den Befehlen &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; an. Dabei werden durch interne Timer bzw. eigens angelegten [[at]]-Definitionen diese Befehle durch FHEM selber umgesetzt. Je nach ausgeführtem Befehl wird der &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt;- bzw. &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt;-Befehl dann durch FHEM zum richtigen Zeitpunkt ausgeführt. Vorausgesetzt das Modul unterstützt in der Set-Funktion die Befehle &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt;, so werden durch den Einsatz von SetExtensions.pm folgende Befehle zusätzlich unterstützt:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Set-Kommando !! Beispiel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;on-for-timer &#039;&#039;&amp;amp;lt;Dauer&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;  || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;on-for-timer 120&amp;lt;/code&amp;gt; || Schaltet das Gerät sofort mit dem &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt;-Befehl ein und nach der angegebenen Dauer in Sekunden via &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; wieder aus.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;off-for-timer &#039;&#039;&amp;amp;lt;Dauer&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;  || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;off-for-timer 120&amp;lt;/code&amp;gt; || Schaltet das Gerät sofort mit dem &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt;-Befehl aus und nach der angegebenen Dauer in Sekunden via &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt; wieder ein.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;on-till &#039;&#039;&amp;amp;lt;Zeitpunkt&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;  || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;on-till 16:30&amp;lt;/code&amp;gt; || Schaltet das Gerät sofort mit dem &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt;-Befehl ein und zum angegebenen Zeitpunkt via &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; wieder aus.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;off-till &#039;&#039;&amp;amp;lt;Zeitpunkt&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;  || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;off-till 16:30&amp;lt;/code&amp;gt; || Schaltet das Gerät sofort mit dem &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt;-Befehl aus und zum angegebenen Zeitpunkt via &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt; wieder ein.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;on-till-overnight &#039;&#039;&amp;amp;lt;Zeitpunkt&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;  || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;on-till-overnight 01:00&amp;lt;/code&amp;gt; || Ähnlich wie &amp;lt;code&amp;gt;on-till&amp;lt;/code&amp;gt;. Der übergebene Zeitpunkt wird aber nicht geprüft, ob er für den heutigen Tag bereits überschritten wurde. Dadurch kann man Abends einen Zeitpunkt setzen, der erst am nächsten Tag zutrifft.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;off-till-overnight &#039;&#039;&amp;amp;lt;Zeitpunkt&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;  || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;off-till-overnight 01:00&amp;lt;/code&amp;gt; || Ähnlich wie &amp;lt;code&amp;gt;off-till&amp;lt;/code&amp;gt;. Der übergebene Zeitpunkt wird aber nicht geprüft, ob er für den heutigen Tag bereits überschritten wurde. Dadurch kann man Abends einen Zeitpunkt setzen, der erst am nächsten Tag zutrifft.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;blink &#039;&#039;&amp;amp;lt;Anzahl&amp;amp;gt; &amp;amp;lt;Interval&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;  || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;blink 3 1&amp;lt;/code&amp;gt; || Schaltet das Gerät via &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt; für &amp;lt;code&amp;gt;&#039;&#039;&amp;amp;lt;Interval&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt; Sekunden ein und anschließend via &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; wieder aus. Nach &amp;lt;code&amp;gt;&#039;&#039;&amp;amp;lt;Interval&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt; Sekunden wird das ganze wiederholt, solange bis die angegebene Anzahl erreicht ist.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;intervals &#039;&#039;&amp;amp;lt;Start&amp;amp;gt;-&amp;amp;lt;Ende&amp;amp;gt;&#039;&#039; &#039;&#039;&amp;amp;lt;Start&amp;amp;gt;-&amp;amp;lt;Ende&amp;amp;gt;&#039;&#039; ...&amp;lt;/code&amp;gt; || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;intervals 07:00-08:00 16:30-18:00&amp;lt;/code&amp;gt; || Schaltet das Gerät innerhalb der übergebenen Zeiträumen via &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt; ein. Sobald die aktuelle Zeit ausserhalb dieser Zeiträume liegt, wird das Gerät via &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; wieder ausgeschaltet. Es können dabei beliebig viele Zeiträume angegeben werden.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;toggle&amp;lt;/code&amp;gt; || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;toggle&amp;lt;/code&amp;gt;  || Sofern der aktuelle Status &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt; ist, wird das Gerät via &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; ausgeschaltet. Andernfalls wird es via &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt; eingeschaltet.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine kurze Beschreibung zu den möglichen Befehlen durch SetExtensions.pm gibt es auch in der commandref zum {{Link2CmdRef|Anker=set|Label=set-Befehl}}.&lt;br /&gt;
&lt;br /&gt;
Um SetExtensions.pm in der Set-Funktion nutzen zu können müssen folgende Aktionen durchgeführt werden:&lt;br /&gt;
&lt;br /&gt;
# Laden von SetExtensions.pm via &amp;lt;code&amp;gt;use SetExtensions;&amp;lt;/code&amp;gt; am Anfang des Moduls&lt;br /&gt;
# Aufruf und Rückgabe der Funktion [[DevelopmentModuleAPI#SetExtensions|SetExtensions()]] sofern die Set-Funktion mit dem übergebenen Befehl nichts anfangen kann.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
use SetExtensions;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
sub X_Set($@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $name, $cmd, @args ) = @_;&lt;br /&gt;
	my $cmdList = &amp;quot;on off&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;\&amp;quot;set $name\&amp;quot; needs at least one argument&amp;quot; unless(defined($cmd));&lt;br /&gt;
&lt;br /&gt;
	if($cmd eq &amp;quot;on&amp;quot;)&lt;br /&gt;
	{&lt;br /&gt;
		# Gerät einschalten...&lt;br /&gt;
	}&lt;br /&gt;
	elsif($cmd eq &amp;quot;off&amp;quot;)&lt;br /&gt;
	{&lt;br /&gt;
		# Gerät ausschalten...&lt;br /&gt;
	}&lt;br /&gt;
	...&lt;br /&gt;
	else # wenn der übergebene Befehl nicht durch X_Set() verarbeitet werden kann, Weitergabe an SetExtensions()&lt;br /&gt;
	{&lt;br /&gt;
		return SetExtensions($hash, $cmdList, $name, $cmd, @args);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollte der übergebene Set-Befehl auch für SetExtensions unbekannt sein (bspw. &amp;lt;code&amp;gt;set &#039;&#039;&amp;amp;lt;Name&amp;amp;gt;&#039;&#039; ?&amp;lt;/code&amp;gt;), so generiert SetExtensions() eine entsprechende Usage-Meldung, welche innerhalb der Set-Funktion an FHEM zurückgegeben werden muss.&lt;br /&gt;
&lt;br /&gt;
Eine ausführliche Beschreibung zu der Funktion SetExtensions() gibt es in der  [[DevelopmentModuleAPI#SetExtensions|API-Referenz]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Nutzung von parseParams()&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn in [[#X_Initialize|X_Initialize()]] &amp;lt;code&amp;gt;$hash-&amp;gt;{parseParams} = 1;&amp;lt;/code&amp;gt; gesetzt wurde dann wird [[DevelopmentModuleAPI#parseParams|parseParams()]] automatisch aufgerufen und X_Set() ändert sich wie folgt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Set($$$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $a, $h ) = @_;&lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die genauen Möglichkeiten von parseParams() sind in dem entsprechenden [[DevelopmentModuleAPI#parseParams|Artikel]] dokumentiert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Nutzung von FHEMWEB-Widgets&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das GUI-Modul [[FHEMWEB]] kann für die einzelnen Set-Optionen, die das Modul versteht, automatisch Eingabehilfen wie Drop-Down Boxen oder Slider erzeugen. In der Detailansicht der GUI kann der Anwender dann die jeweiligen Werte komfortabel auswählen. Dafür muss die Set-Funktion, wenn sie mit der Option &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; aufgerufen wird, nicht nur einen Text mit  &amp;lt;code&amp;gt;&amp;quot;Unknown ... choose one of ...&amp;quot;&amp;lt;/code&amp;gt; zurückgeben sondern den einzelnen Set-Optionen in diesem Rückgabetext nach einem Doppelpunkt entsprechende Zusatzinformationen anhängen.&lt;br /&gt;
Meist prüft man in den Modulen gar nicht auf die Option &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; sondern gibt generell bei unbekannten Optionen diesen Text zurück. Das Modul FHEMWEB ermittelt die Syntax eines Gerätes jedoch immer mit dem Befehl:&lt;br /&gt;
 set &#039;&#039;&amp;amp;lt;NAME&amp;amp;gt;&#039;&#039; ?&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
	return &amp;quot;Unknown argument $cmd, choose one of status:up,down power:on,off on:noArg off:noArg&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit Kommata getrennte Werte ergeben eine Drop-Down Liste, mit der der User die Werte auswählen kann&lt;br /&gt;
&amp;lt;pre&amp;gt;timer:30,120,300&lt;br /&gt;
mode:verbose,ultra,relaxed&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird kein Doppelpunkt zum Kommando angegeben, so wird eine Eingabezeile angezeigt, die die freie Eingabe eines Wertes erlaubt.&lt;br /&gt;
&lt;br /&gt;
Man kann jedoch die Eingabe-/Auswahlmöglichkeiten durch Widgets vereinfachen. Dazu gibt man hinter dem Doppelpunkt einen Widgetnamen und widgetspezifische Parameter an. Es existieren mehrere solcher Widgets in FHEMWEB. Die gebräuchlichsten sind:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Zusatz !! Beispiel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;noArg&#039;&#039;&#039; || &amp;lt;code&amp;gt;reset:noArg&amp;lt;/code&amp;gt;|| Es werden keine weiteren Argumente mehr benötigt. In so einem Fall wird bei der Auswahl keine Textbox oder ähnliches angezeigt, da keine weiteren Argumente für diesen Befehl notwendig sind.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;slider&#039;&#039;&#039;,&amp;lt;min&amp;gt;,&amp;lt;step&amp;gt;,&amp;lt;max&amp;gt; || &amp;lt;code&amp;gt;dim:slider,0,1,100&amp;lt;/code&amp;gt;|| Es wird ein Schieberegler angezeigt um den Parameter auszuwählen. Dabei werden als Zusatzparameter Minimum, Schrittweite und Maximum angegeben.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;colorpicker&#039;&#039;&#039; || &amp;lt;code&amp;gt;rgb:colorpicker,RGB&amp;lt;/code&amp;gt;|| Es wird ein Colorpicker angezeigt, der dem Anwender die Auswahl einer Farbe ermöglicht. Die genaue Parametersyntax kann man dem Artikel zum  [[Color#Colorpicker|Colorpicker]] entnehmen.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;multiple&#039;&#039;&#039; || &amp;lt;code&amp;gt;group:multiple,Telefon,Multimedia,Licht,Heizung&amp;lt;/code&amp;gt; || Es erscheint ein Auswahldialog, wo man verschiedene Werte durch klicken auswählen kann. Optional kann man in einem Freitext eigene Werte ergänzen. dieser Dialog wird bspw. bei der Raum-Auswahl (Attribut &amp;quot;room&amp;quot;) oder der Gruppen-Auswahl (Attribut &amp;quot;group&amp;quot;) in FHEMWEB genutzt. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;sortable&#039;&#039;&#039; || &amp;lt;code&amp;gt;command:sortable,monday,tuesday,...&amp;lt;/code&amp;gt; || Es erscheint ein Auswahldialog, wo man verschiedene Werte auswählen und sortieren kann. Man kann dabei Werte durch Klicken auswählen und durch Drag&#039;n&#039;Drop sortieren.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Es gibt noch weitere solcher Widgets. Eine genaue Auflistung dazu findet sich in der {{Link2CmdRef|Anker=widgetOverride}} unter widgetOverride zu FHEMWEB.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweise&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Damit in einer Eingabe bereits der aktuelle Wert vorbelegt bzw. in einer Auswahlliste der aktuelle Wert vorselektiert ist, muss es im Modul bzw. Gerät ein Reading mit dem gleichen Namen wie die Set-Option geben. Der Wert des gleichnamigen Readings wird dann als Vorbelegung / Vorselektion verwendet. &lt;br /&gt;
* Der User kann sich in der Raumübersicht nach wie vor via [[WebCmd|webCmd]] eine entsprechende Steuerung anlegen.&lt;br /&gt;
&lt;br /&gt;
==== X_Attr ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Attr ($$$$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $cmd, $name, $attrName, $attrValue  ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
 &lt;br /&gt;
	return $error;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Attr-Funktion dient der Prüfung von Attributen, welche über den &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt;-Befehl gesetzt werden können. Sobald versucht wird, ein Attribut für ein Gerät zu setzen, wird vorher die Attr-Funktion des entsprechenden Moduls aufgerufen um zu prüfen, ob das Attribut aus Sicht des Moduls korrekt ist.&lt;br /&gt;
Liegt ein Problem mit dem Attribut bzw. dem Wert vor, so muss die Funktion eine aussagekräftige Fehlermeldung zurückgeben, welche dem User angezeigt wird.&lt;br /&gt;
Sofern das übergebene Attribut samt Inhalt korrekt ist, gibt die Attr-Funktion den Wert &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurück. Erst dann wird das Attribut in der globalen Datenstruktur &amp;lt;code&amp;gt;%attr&amp;lt;/code&amp;gt; gespeichert und ist somit erst aktiv.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
X_Attr(@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $cmd, $name, $attrName, $attrValue ) = @_;&lt;br /&gt;
    &lt;br /&gt;
  	# $cmd  - Vorgangsart - kann die Werte &amp;quot;del&amp;quot; (löschen) oder &amp;quot;set&amp;quot; (setzen) annehmen&lt;br /&gt;
	# $name - Gerätename&lt;br /&gt;
	# $attrName/$attrValue sind Attribut-Name und Attribut-Wert&lt;br /&gt;
    &lt;br /&gt;
	if ($cmd eq &amp;quot;set&amp;quot;) {&lt;br /&gt;
		if ($aName eq &amp;quot;Regex&amp;quot;) {&lt;br /&gt;
			eval { qr/$aVal/ };&lt;br /&gt;
			if ($@) {&lt;br /&gt;
				Log3 $name, 3, &amp;quot;X ($name) - Invalid regex in attr $name $aName $aVal: $@&amp;quot;;&lt;br /&gt;
				return &amp;quot;Invalid Regex $aVal: $@&amp;quot;;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return undef;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich ist es möglich auch übergebene Attributwerte zu verändern bzw. zu korrigieren, indem man im Parameterarray &amp;lt;code&amp;gt;@_&amp;lt;/code&amp;gt; den ursprünglichen Wert anpasst. Dies erfolgt im Beispiel über die Modifikation des Wertes mit Index 3 (entspricht dem 4. Element) im Parameterarray, also &amp;lt;code&amp;gt;$_[3]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Da das Attribut zum Zeitpunkt des Aufrufs der Attr-Funktion noch nicht gespeichert ist, wird der neue Wert zu diesem Zeitpunkt noch nicht via [[DevelopmentModuleAPI#AttrVal|AttrVal()]] zurückgegeben. Erst, wenn die Attr-Funktion mit &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; beendet ist, wird der neue Wert in FHEM gespeichert und steht dann via AttrVal() zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
Die Attr-Funktion bekommt nicht den Hash der Geräteinstanz übergeben, da sie normalerweise keine Werte dort speichern muss, sondern lediglich das Attribut auf Korrektheit prüfen muss.&lt;br /&gt;
Im obigen Beispiel wird für ein Attribut mit Namen &amp;quot;Regex&amp;quot; geprüft ob der reguläre Ausdruck fehlerhaft ist. Sofern dieser OK ist, wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben und fhem.pl speichert den Wert des Attributs in &amp;lt;code&amp;gt;%attr&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Attributnamen mit Platzhaltern&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls man Attribute in der [[#X_Initialize|Initialize]]-Funktion mit Platzhaltern definiert (Wildcard-Attribute) wie z.B.:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
    $hash-&amp;gt;{AttrList} =&lt;br /&gt;
      &amp;quot;reading[0-9]*Name &amp;quot; .&lt;br /&gt;
    # usw.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann können Anwender Attribute wie reading01Name, reading02Name etc. setzen. Leider funktioniert das bisher nicht durch Klicken in der Web-Oberfläche, da FHEMWEB nicht alle denkbaren Ausprägungen in einem Dropdown anbieten kann. Der Benutzer muss solche Attribute manuell über den &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt;-Befehl eingeben.&lt;br /&gt;
&lt;br /&gt;
Man kann jedoch in der Attr-Funktion neu gesetzte Ausprägungen von Wildcard-Attributen an die gerätespezifische userattr-Variable anfügen. Dann können bereits gesetzte Attribute in FHEMWEB durch Klicken ausgewählt und geändert werden.&lt;br /&gt;
Dazu reicht ein Aufruf der Funktion [[DevelopmentModuleAPI#addToDevAttrList|addToDevAttrList()]]: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
    addToDevAttrList($name, $aName);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_Read ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Read ($)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die X_Read-Funktion wird aufgerufen, wenn ein dem Gerät zugeordneter Filedeskriptor (serielle Schnittstelle, TCP-Verbindung, ...) Daten zum Lesen bereitgestellt hat. Die Daten müssen nun eingelesen und interpretiert werden.&lt;br /&gt;
&lt;br /&gt;
Im folgenden Beispiel wird über eine serielle Schnittstelle (beziehungsweise über einen USB-To-Serial-Konverter) von einem angeschlossenen Gerät gelesen. Dazu werden die bisher verfügbaren Daten mit der Funktion [[DevIo#DevIo_SimpleRead()|DevIo_SimpleRead()]] gelesen. Da die Übertragung möglicherweise noch nicht vollständig ist, kann es sein, dass kurz darauf die X_Read-Funktion wieder aufgerufen wird und ein weiterer Teil oder der Rest der Daten gelesen werden kann.&lt;br /&gt;
Die Funktion muss daher prüfen ob schon alle erwarteten Daten angekommen sind und gegebenenfalls die bisher gelesenen Daten in einem eigenen Puffer (idealerweise in &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;) zwischenspeichern (siehe auch [[DevIo#Hinweis bei der Datenverarbeitung (Buffering)|DevIo]]). Im Beispiel ist dies &amp;lt;code&amp;gt;$hash-&amp;gt;{helper}{BUFFER}&amp;lt;/code&amp;gt; an den die aktuell gelesenen Daten angehängt werden, bis die folgende Prüfung ein für das jeweilige Protokoll vollständige Frame erkennt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Read($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
	&lt;br /&gt;
	# einlesen der bereitstehenden Daten&lt;br /&gt;
	my $buf = DevIo_SimpleRead($hash);		&lt;br /&gt;
	return &amp;quot;&amp;quot; if ( !defined($buf) );&lt;br /&gt;
	Log3 $name, 5, &amp;quot;X ($name) - received data: &amp;quot;.$buf;    &lt;br /&gt;
&lt;br /&gt;
	# Daten in Hex konvertieren und an den Puffer anhängen&lt;br /&gt;
	$hash-&amp;gt;{helper}{BUFFER} .= unpack (&#039;H*&#039;, $buf);	&lt;br /&gt;
	Log3 $name, 5, &amp;quot;X ($name) - current buffer content: &amp;quot;.$hash-&amp;gt;{helper}{BUFFER};&lt;br /&gt;
&lt;br /&gt;
	# prüfen, ob im Buffer ein vollständiger Frame zur Verarbeitung vorhanden ist.&lt;br /&gt;
	if ($hash-&amp;gt;{helper}{BUFFER} =~ &amp;quot;ff1002(.{4})(.*)1003(.{4})ff(.*)&amp;quot;) {&lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zu lesenden Nutzdaten können dann je nach Protokoll des Geräts beispielsweise an einer festgelegten Stelle im Frame (dann in &amp;lt;code&amp;gt;$hash-&amp;gt;{helper}{BUFFER}&amp;lt;/code&amp;gt;) stehen oder aus dem Kontext mit einem Regex-Match extrahiert werden und via [[DevelopmentModuleAPI#Readings_.2F_Events|Reading-Funktionen]] in Readings gespeichert werden (siehe unten).&lt;br /&gt;
&lt;br /&gt;
Der Rückgabewert der Read-Funktion wird nicht geprüft und hat daher keinerlei Bedeutung.&lt;br /&gt;
&lt;br /&gt;
==== X_Ready ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Ready ($)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
    &lt;br /&gt;
	return $success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird im Main-Loop aufgerufen falls das Modul in der globalen Liste &amp;lt;code&amp;gt;%readyfnlist&amp;lt;/code&amp;gt; existiert. Diese Funktion hat, je nachdem auf welchem OS FHEM ausgeführt wird, unterschiedliche Aufgaben:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;UNIX-artiges Betriebssystem:&#039;&#039;&#039; prüfen, ob eine Verbindung nach einem Verbindungsabbruch wieder aufgebaut werden kann. Sobald der Verbindungsaufbau erfolgreich war, muss die Funktion einen erfolgreichen Wahrheitswert zurückliefern (z.B. &amp;quot;1&amp;quot;) und den eigenen Eintrag entsprechend aus &amp;lt;code&amp;gt;%readyfnlist&amp;lt;/code&amp;gt; löschen.&lt;br /&gt;
* &#039;&#039;&#039;Windows-Betriebssystem:&#039;&#039;&#039; prüfen, ob lesbare Daten für ein serielles Device (via COM1, COM2, ...) vorliegen. Sofern lesbare Daten vorliegen, muss Funktion einen erfolgreichen Wahrheitswert zurückliefern (z.B. &amp;quot;1&amp;quot;). Zusätzlich dazu muss die Funktion, wie bei UNIX-artigen Betriebssystem, ebenfalls bei einem Verbindungsabbruch einen neuen Verbindungsversuch initiieren. Der Eintrag in &amp;lt;code&amp;gt;%readyfnlist&amp;lt;/code&amp;gt; bleibt solange erhalten, bis die Verbindung seitens FHEM beendet wird.&lt;br /&gt;
&lt;br /&gt;
Der Windows-spezifische Teil zur Datenprüfung ist dabei nur zu implementieren, wenn das Modul über eine serielle Verbindung kommuniziert.&lt;br /&gt;
&lt;br /&gt;
Bei der Nutzung des Moduls [[DevIo]] wird dem Modulentwickler der Umgang mit &amp;lt;code&amp;gt;%readyfnlist&amp;lt;/code&amp;gt; abgenommen, da DevIo sich selbst um die entsprechenden Einträge kümmert und diese selbstständig wieder entfernt.&lt;br /&gt;
&lt;br /&gt;
In der Regel sieht eine Ready-Funktion immer gleich aus.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Ready($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
      &lt;br /&gt;
	# Versuch eines Verbindungsaufbaus, sofern die Verbindung beendet ist.&lt;br /&gt;
	return DevIo_OpenDev($hash, 1, undef ) if ( $hash-&amp;gt;{STATE} eq &amp;quot;disconnected&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
	# This is relevant for Windows/USB only&lt;br /&gt;
	if(defined($hash-&amp;gt;{USBDev})) {&lt;br /&gt;
		my $po = $hash-&amp;gt;{USBDev};&lt;br /&gt;
		my ( $BlockingFlags, $InBytes, $OutBytes, $ErrorFlags ) = $po-&amp;gt;status;&lt;br /&gt;
		return ( $InBytes &amp;gt; 0 );&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_Notify ====&lt;br /&gt;
&lt;br /&gt;
Die X_Notify-Funktion wird aus der Funktion [[DevelopmentModuleAPI#DoTrigger|DoTrigger()]] in fhem.pl heraus aufgerufen sobald ein Modul Events erzeugt hat. Damit kann ein Modul auf Events anderer Module reagieren. Typische Beispiele sind dabei das [[FileLog]]-Modul oder das [[notify]]-Modul.&lt;br /&gt;
&lt;br /&gt;
Die Notify-Funktion bekommt dafür zwei Hash-Referenzen übergeben: den Hash des eigenen Geräts und den Hash des Geräts, dass die Events erzeugt hat. &lt;br /&gt;
Über den Hash des eigenen Geräts kann die Notify-Funktion beispielsweise auf die Internals oder Attribute des eigenen Geräts zugreifen.&lt;br /&gt;
Über den Hash des Gerätes und der [[DevelopmentModuleAPI#deviceEvents|deviceEvents()]]-Funktion kann man auf die generierten Events zugreifen. Über den zweiten Parameter dieser Routine lässt sich bestimmen ob für das Reading &amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt; ein &#039;normales&#039; Event (d.h. in der form &amp;lt;code&amp;gt;state: &amp;lt;wert&amp;gt;&amp;lt;/code&amp;gt;) erzeugen soll (Wert: 1) oder ob z.b. aus Gründen der Rückwärtskompatibilität ein Event ohne &amp;lt;code&amp;gt;state: &amp;lt;/code&amp;gt; erzeugt werden soll. Falls dem Anwender die Wahl des verwendeten Formats überlassen werden soll ist hierzu das {{Link2CmdRef|Anker=addStateEvent|Lang=de|Label=addStateEvent-Attribut}} vorzusehen.&lt;br /&gt;
&lt;br /&gt;
Der direkte Zugriff auf &amp;lt;code&amp;gt;$hash-&amp;gt;{CHANGED}&amp;lt;/code&amp;gt; ist nicht mehr zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Notify($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($own_hash, $dev_hash) = @_;&lt;br /&gt;
  my $ownName = $own_hash-&amp;gt;{NAME}; # own name / hash&lt;br /&gt;
&lt;br /&gt;
  return &amp;quot;&amp;quot; if(IsDisabled($ownName)); # Return without any further action if the module is disabled&lt;br /&gt;
&lt;br /&gt;
  my $devName = $dev_hash-&amp;gt;{NAME}; # Device that created the events&lt;br /&gt;
&lt;br /&gt;
  my $events = deviceEvents($dev_hash,1);&lt;br /&gt;
  return if( !$events );&lt;br /&gt;
&lt;br /&gt;
  foreach my $event (@{$events}) {&lt;br /&gt;
    $event = &amp;quot;&amp;quot; if(!defined($event));&lt;br /&gt;
&lt;br /&gt;
    # Examples:&lt;br /&gt;
    # $event = &amp;quot;readingname: value&amp;quot; &lt;br /&gt;
    # or&lt;br /&gt;
    # $event = &amp;quot;INITIALIZED&amp;quot; (for $devName equal &amp;quot;global&amp;quot;)&lt;br /&gt;
    #&lt;br /&gt;
    # processing $event with further code&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Begrenzung der Aufrufe auf bestimmte Geräte&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da die Notify-Funktion für jedes definierte Gerät mit all seinen Events aufgerufen wird, muss sie in einer Schleife jedesmal prüfen und entscheiden, ob es mit dem jeweiligen Event etwas anfangen kann. Ein Gerät, dass die Notify-Funktion implementiert, sieht dafür typischerweise einen regulären Ausdruck vor, welcher für die Filterung verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Wenn man nur gezielt von bestimmten Definitionen Events erhalten will, kann man diese auch in Form einer {{Link2CmdRef|Lang=de|Anker=devspec|Label=devspec}} in &amp;lt;code&amp;gt;$hash-&amp;gt;{NOTIFYDEV}&amp;lt;/code&amp;gt; angeben. Bspw. kann man in der Define-Funktion diesen Wert setzen. Dadurch wird die Notify-Funktion nur aufgerufen wenn eine der Definitionen, auf welche die devspec passt, ein Event erzeugt hat. Ein typischer Fall ist die Begrenzung von Events auf &amp;quot;global&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
in der Define-Funktion:&lt;br /&gt;
&lt;br /&gt;
$hash-&amp;gt;{NOTIFYDEV} = &amp;quot;global&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{NOTIFYDEV} = &amp;quot;global,Definition_A,Definition_B&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{NOTIFYDEV} = &amp;quot;global,TYPE=CUL_HM&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies schont insbesondere bei grossen Installationen Ressourcen, da die Notify-Funktion nicht sämtliche Events, sondern nur noch Events der gewünschten Definitionen erhält. Dadurch erfolgen deutlich weniger Aufrufe der Notify-Funktion, was Systemressourcen schont.&lt;br /&gt;
&lt;br /&gt;
Sofern in der [[#X_Define|Define-Funktion]] eine Regexp als Argument übergeben wird, die ähnlich wie beim Modul [[notify]] auf Events wie &amp;lt;code&amp;gt;&amp;amp;lt;Definitionsname&amp;amp;gt;&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;&amp;amp;lt;Definitionsname&amp;amp;gt;:&amp;amp;lt;Event&amp;amp;gt;&amp;lt;/code&amp;gt; reagiert, so sollte man in der Define-Funktion die Funktion [[DevelopmentModuleAPI#notifyRegexpChanged|notifyRegexpChanged()]] verwenden. Diese versucht einen passenden Eintrag für &amp;lt;code&amp;gt;$hash-&amp;gt;{NOTIFYDEV}&amp;lt;/code&amp;gt; basierend auf der übergebenen Regexp zu setzen, sofern dies möglich ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Reihenfolge für den Aufruf der Notify-Funktion beeinflussen&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald ein Event ausgelöst wurde, stellt sich FHEM eine Liste aller relevanten Geräte-Hashes zusammen, welche via Notify-Funktion prüfen müssen, ob das Event relevant ist. Dabei wird die Liste nach &amp;lt;code&amp;gt;$hash-&amp;gt;{NTFY_ORDER}&amp;lt;/code&amp;gt; sortiert. Diese enthält ein Order-Präfix in Form einer Ganzzahl, sowie den Namen der Definition (Bsp: &amp;lt;code&amp;gt;&#039;&#039;&#039;50&#039;&#039;&#039;-Lampe_Wohnzimmer&amp;lt;/code&amp;gt;). Dadurch kann man jedoch nicht sicherstellen, dass Events von bestimmten Modulen zuerst verarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
Wenn das eigene Modul bei der Eventverarbeitung gegenüber den anderen Modulen eine bestimmte Reihenfolge einhalten muss, kann man in der [[#X_Initialize|Initialize]]-Funktion durch Setzen von &amp;lt;code&amp;gt;$hash-&amp;gt;{NotifyOrderPrefix}&amp;lt;/code&amp;gt; diese Reihenfolge beeinflussen. Standardmäßig werden Module immer mit einem Order-Präfix von &amp;quot;50-&amp;quot; in FHEM registriert. Durch die Veränderung dieses Präfixes kann man das eigene Modul in der Reihenfolge gegenüber anderen Modulen bei der Eventverarbeitung beeinflussen. &lt;br /&gt;
&lt;br /&gt;
Beispiel:&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;
	...&lt;br /&gt;
	&lt;br /&gt;
	$hash-&amp;gt;{NotifyOrderPrefix} = &amp;quot;45-&amp;quot;  # Alle Definitionen des Moduls X werden bei der Eventverarbeitung zuerst geprüft&lt;br /&gt;
	&lt;br /&gt;
	# oder...&lt;br /&gt;
	&lt;br /&gt;
	$hash-&amp;gt;{NotifyOrderPrefix} = &amp;quot;55-&amp;quot;  # Alle Definitionen des Moduls X werden bei der Eventverarbeitung als letztes geprüft&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
Da dieses Präfix bei eventverarbeitenden Definitionen in &amp;lt;code&amp;gt;$hash-&amp;gt;{NTFY_ORDER}&amp;lt;/code&amp;gt; dem Definitionsnamen vorangestellt wird bewirkt es bei einer normalen aufsteigenden Sortierung nach &amp;lt;code&amp;gt;$hash-&amp;gt;{NTFY_ORDER}&amp;lt;/code&amp;gt; eine veränderte Reihenfolge. Alle Module die in der Initialize-Funktion nicht &amp;lt;code&amp;gt;$hash-&amp;gt;{NotifyOrderPrefix}&amp;lt;/code&amp;gt; explizit setzen, werden mit &amp;quot;50-&amp;quot; als Standardwert vorbelegt.&lt;br /&gt;
&lt;br /&gt;
==== X_Rename ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Rename ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $new_name, $old_name) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Rename-Funktion wird ausgeführt, nachdem ein Gerät umbenannt wurde. Auf diese Weise kann ein Modul auf eine Namensänderung reagieren, wenn das Gerät &amp;lt;code&amp;gt;$old_name&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;$new_name&amp;lt;/code&amp;gt; umbenannt wurde. Ein typischer Fall ist das Umsetzen der Namensänderungen bei Daten die mittels [[DevelopmentModuleAPI#setKeyValue|setKeyValue()]] gespeichert wurden. Hierbei müssen die Daten, welche unter dem alten Namen gespeichert sind, auf den neuen Namen geändert werden.&lt;br /&gt;
&lt;br /&gt;
Der Rename-Funktion wird lediglich der alte, sowie der neue Gerätename übergeben. Der Rückgabewert wird nicht ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Rename ($)&lt;br /&gt;
{&lt;br /&gt;
	my ( $new_name, $old_name ) = @_;&lt;br /&gt;
&lt;br /&gt;
	my $old_index = &amp;quot;Module_X_&amp;quot;.$old_name.&amp;quot;_data&amp;quot;;&lt;br /&gt;
	my $new_index = &amp;quot;Module_X_&amp;quot;.$new_name.&amp;quot;_data&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
	my ($err, $data) = getKeyValue($old_index);&lt;br /&gt;
	return undef unless(defined($old_pwd));&lt;br /&gt;
&lt;br /&gt;
	setKeyValue($new_index, $data);&lt;br /&gt;
	setKeyValue($old_index, undef);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_Shutdown ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Shutdown ($)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Mit der X_Shutdown Funktion kann ein Modul Aktionen durchführen bevor FHEM gestoppt wird. Dies kann z.B. der ordnungsgemäße Verbindungsabbau mit dem physikalischen Gerät sein (z.B. Session beenden, Logout, etc.). Als Übergabeparameter wird der Geräte-Hash bereitgestellt. Der Rückgabewert einer Shutdown-Funktion wird nicht ausgewertet und ist daher irrelevant.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Shutdown($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
&lt;br /&gt;
	# Verbindung schließen&lt;br /&gt;
	DevIo_CloseDev($hash);&lt;br /&gt;
	return undef;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Funktionen für zweistufiges Modulkonzept ===&lt;br /&gt;
&lt;br /&gt;
Für das [[#Zweistufiges_Modell_für_Module|zweistufige Modulkonzept]] gibt es weiterhin:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left&amp;quot; | Funktionsname !! style=&amp;quot;text-align:left&amp;quot; class=&amp;quot;unsortable&amp;quot; | Kurzbeschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Parse|X_Parse]] || Zustellen von Daten via [[DevelopmentModuleAPI#Dispatch|Dispatch()]] vom physischen Modul zum logischen Modul zwecks der Verarbeitung.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Write|X_Write]]|| Zustellen von Daten via [[DevelopmentModuleAPI#Dispatch|IOWrite()]] vom logischen zum physischen Modul um diese an die Hardware weiterzureichen.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Fingerprint|X_Fingerprint]] || Rückgabe eines &amp;quot;Fingerabdrucks&amp;quot; einer Nachricht. Dient der Erkennung von Duplikaten im Rahmen von [[DevelopmentModuleAPI#Dispatch|Dispatch()]]. Kann im physischen, als auch logischen Modul benutzt werden.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Für das zweistufige Modulkonzept muss in einem logischen Modul eine [[#X_Parse|Parse]]-Funktion im Modul-Hash registriert werden. In einem physikalischen Modul muss eine [[#X_Write|Write]]-Funktion registriert sein. Diese dienem dem Datenaustausch in beide Richtungen und werden von dem jeweils anderen Modul indirekt aufgerufen.&lt;br /&gt;
&lt;br /&gt;
In der [[#X_Initialize|Initialize]]-Funktion werden diese wie folgt definiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$hash-&amp;gt;{ParseFn}       = &amp;quot;X_Parse&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{WriteFn}       = &amp;quot;X_Write&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{FingerprintFn} = &amp;quot;X_Fingerprint&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktionen werden in diesem Abschnitt genauer beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== X_Parse ====&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;u&amp;gt;&#039;&#039;&#039;ACHTUNG&#039;&#039;&#039;:&amp;lt;/u&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
Dieser Abschnitt geht davon aus, dass das Modul mit dem Namen &amp;quot;X&amp;quot; ein &#039;&#039;&#039;logisches Modul&#039;&#039;&#039; im Sinne des zweistufigen Modulkonzepts ist, also Daten mit einem übergeordneten, physikalischen Modul austauscht.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Parse ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $io_hash, $message) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
	&lt;br /&gt;
	return $found;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion X_Parse wird aufgerufen, sobald von dem IO-Gerät &amp;lt;code&amp;gt;$io_hash&amp;lt;/code&amp;gt; eine Nachricht &amp;lt;code&amp;gt;$message&amp;lt;/code&amp;gt; via [[DevelopmentModuleAPI#Dispatch|Dispatch()]] zur Verarbeitung angefragt wird. Die Parse-Funktion muss dann prüfen, zu welcher Gerätedefinition diese Nachricht gehört und diese entsprechend verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Üblicherweise enthält eine Nachricht immer eine Komponente durch welche sich die Nachricht einem Gerät zuordnen lässt (z.B. Adresse, ID-Nummer, ...). Eine solche Identifikation sollte man im Rahmen der [[#X_Define|Define]]-Funktion im logischen Modul an geeigneter Stelle speichern, um in der Parse-Funktion eine einfache Zuordnung von Adresse/ID einer Nachricht zur entsprechenden Gerätedefinition zu haben. Dazu wird in der Regel im Modul-Hash im modulspezifischen Berreich eine Liste &amp;lt;code&amp;gt;defptr&amp;lt;/code&amp;gt; (Definition Pointer) geführt, welche jede eindeutige Adresse/ID dem entsprechenden Geräte-Hash zuordnet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sub X_Define ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $def) = @_;&lt;br /&gt;
	my @a = split(&amp;quot;[ \t][ \t]*&amp;quot;, $def);&lt;br /&gt;
	my $name = $a[0];&lt;br /&gt;
&lt;br /&gt;
	...&lt;br /&gt;
	&lt;br /&gt;
	# erstes Argument ist die eindeutige Geräteadresse&lt;br /&gt;
	my $address = $a[1];&lt;br /&gt;
&lt;br /&gt;
	# Adresse rückwärts dem Hash zuordnen (für ParseFn)&lt;br /&gt;
	$modules{X}{defptr}{$address} = $hash;&lt;br /&gt;
&lt;br /&gt;
	...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf Basis dieses Definition Pointers kann die Parse-Funktion nun sehr einfach prüfen, ob für die empfangene Nachricht bereits eine entsprechende Gerätedefinition existiert. Sofern diese existiert, kann die Nachricht entsprechend verarbeitet werden. Sollte jedoch keine passende Gerätedefinition zu der empfangenen Nachricht existieren, so muss die Parse-Funktion den Gerätenamen &amp;quot;UNDEFINED&amp;quot; zusammen mit den Argumenten für einen &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Befehl zurückgeben, welcher ein passendes Gerät in FHEM anlegen würde (durch [[autocreate]]).&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sub X_Parse ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $io_hash, $message) = @_;&lt;br /&gt;
	&lt;br /&gt;
	# Die Stellen 10-15 enthalten die eindeutige Identifikation des Geräts&lt;br /&gt;
	my $address = substr($message, 10, 5); &lt;br /&gt;
&lt;br /&gt;
	# wenn bereits eine Gerätedefinition existiert (via Definition Pointer aus Define-Funktion)&lt;br /&gt;
	if(my $hash = $modules{X}{defptr}{$address}) &lt;br /&gt;
	{&lt;br /&gt;
		...  # Nachricht für $hash verarbeiten&lt;br /&gt;
		&lt;br /&gt;
		# Rückgabe des Gerätenamens, für welches die Nachricht bestimmt ist.&lt;br /&gt;
		return $hash-&amp;gt;{NAME}; &lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		# Keine Gerätedefinition verfügbar&lt;br /&gt;
		# Daher Vorschlag define-Befehl: &amp;lt;NAME&amp;gt; &amp;lt;MODULNAME&amp;gt; &amp;lt;ADDRESSE&amp;gt;&lt;br /&gt;
		return &amp;quot;UNDEFINED X_&amp;quot;.$address.&amp;quot; X $address&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_Write ====&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;u&amp;gt;&#039;&#039;&#039;ACHTUNG&#039;&#039;&#039;:&amp;lt;/u&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
Dieser Abschnitt geht davon aus, dass das Modul mit dem Namen &amp;quot;X&amp;quot; ein &#039;&#039;&#039;physisches Modul&#039;&#039;&#039; im Sinne des zweistufigen Modulkonzepts ist, also Daten mit untergeordneten logischen Modulen austauscht. }}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Write ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, @arguments) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
	&lt;br /&gt;
	return $return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Write-Funktion wird durch die Funktion [[DevelopmentModuleAPI#IOWrite|IOWrite()]] aufgerufen, sobald eine logische Gerätedefinition Daten per IO-Gerät an die Hardware übertragen möchte. Dazu kümmert sich die Write-Funktion um die Übertragung der Nachricht in geeigneter Form an die verbundene Hardware. Als Argumente wird der Hash des physischen Gerätes übertragen, sowie alle weiteren Argumente, die das logische Modul beim Aufruf von IOWrite() mitgegeben hat. Im Normalfall ist das ein Skalar mit der zu sendenden Nachricht in Textform. Es kann aber auch sein, dass weitere Daten zum Versand notwendig sind (evtl. Schlüssel, Session-Key, ...). Daher ist die Parametersyntax einer zu schreibenden Nachricht via IOWrite-/Write-Funktion zwischen logischem und physikalischen Modul abzustimmen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Write ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $message, $address) = @_;&lt;br /&gt;
	&lt;br /&gt;
	DevIo_SimpleWrite($hash, $address.$message, 2);&lt;br /&gt;
&lt;br /&gt;
	return undef;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_Fingerprint ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Fingerprint($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $io_name, $msg ) = @_;&lt;br /&gt;
 &lt;br /&gt;
	...&lt;br /&gt;
 &lt;br /&gt;
	return ( $io_name, $fingerprint );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Fingerprint-Funktion dient der Erkennung von Duplikaten empfangener Nachrichten. Diese Funktion kann dabei sowohl im physischen, als auch im logischen Modul implementiert sein. Je nachdem auf welcher Ebene man für eine Nachricht einen Fingerprint bilden kann. &lt;br /&gt;
&lt;br /&gt;
Als Parameter wird der Name des IO-Geräts &amp;lt;code&amp;gt;$io_name&amp;lt;/code&amp;gt; übergeben, sowie die Nachricht &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt;, welche empfangen wurde. Nun muss aus dieser Nachricht ein eindeutiger Fingerprint gebildet werden. Dies bedeutet, dass alle variablen Inhalte, die aufgrund des Empfangs dieser Nachricht über unterschiedliche IO-Geräte enthalten sein können, entfernt werden müssen. Dies können bspw. Empfangsadressen von IO-Geräten sein oder Session-ID&#039;s die in der Nachricht enthalten sind. Alle Fingerprints sämtlicher Nachrichten, die innerhalb der letzten 500 Millisekunden (konfigurierbar via &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; Attribut &amp;lt;code&amp;gt;dupTimeout&amp;lt;/code&amp;gt;) empfangen wurden, werden gegen diesen generierten Fingerprint getestet. Sollte innerhalb dieser Zeit bereits eine Nachricht mit diesem Fingerprint verarbeitet worden sein, so wird sie als Duplikat erkannt und nicht weiter verarbeitet. In diesem Fall gibt [[DevelopmentModuleAPI#Dispatch|Dispatch()]] den Namen der Gerätedefinition zurück, welche eine Nachricht mit dem selben Fingerprint bereits verarbeitet hat. Es erfolgt dann kein Aufruf der [[#X_Parse|Parse]]-Funktion.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Fingerprint($$)&lt;br /&gt;
{&lt;br /&gt;
  my ( $io_name, $msg ) = @_;&lt;br /&gt;
&lt;br /&gt;
  substr( $msg, 2, 2, &amp;quot;--&amp;quot; ); # entferne Empfangsadresse&lt;br /&gt;
  substr( $msg, 4, 1, &amp;quot;-&amp;quot; );  # entferne Hop-Count&lt;br /&gt;
&lt;br /&gt;
  return ( $io_name, $msg );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es wird zuerst, sofern implementiert, die Fingerprint-Funktion des physischen Moduls aufgerufen. Sollte sich hierdurch kein Duplikat erkennen lassen, wird die Fingerprint-Funktion jedes möglichen geladenen logischen Moduls aufgerufen, sofern implementiert. &lt;br /&gt;
&lt;br /&gt;
Sollte sowohl im physischen, als auch im logischen Modul keine Fingerprint-Funktion implementiert sein, so wird keinerlei Duplikatserkennung durchgeführt.&lt;br /&gt;
&lt;br /&gt;
=== FHEMWEB-spezifische Funktionen ===&lt;br /&gt;
&lt;br /&gt;
FHEMWEB bietet Modulautoren die Möglichkeit an durch spezielle Funktionsaufrufe in Modulen, eigene HTML-Inhalte zu verwenden. Dadurch können in Verbindung mit zusätzlichem JavaScript komplexe Dialoge/Inhalte/Steuermöglichkeiten dargestellt werden. &lt;br /&gt;
&lt;br /&gt;
Eine genaue Auflistung aller FHEMWEB-spezifischen Funktionsaufrufe gibt es in dem separaten Artikel [[DevelopmentFHEMWEB]]&lt;br /&gt;
&lt;br /&gt;
=== sonstige Funktionen ===&lt;br /&gt;
&lt;br /&gt;
In diesem Abschnitt werden weitere Funktionen behandelt die zum Teil aus FHEM, aber auch aus anderen Modulen aufgerufen werden. Sie sind dabei nur in speziellen Anwendungsfällen relevant. Hier eine Auflistung aller sonstigen Modulfunktionen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Funktionsname !! class=&amp;quot;unsortable&amp;quot; | Kurzbeschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_DbLog_split|X_DbLog_split]] || Wird durch das Modul 93_DbLog.pm aufgerufen. Dient dem korrekten Split eines moduleigenen Events in Name/Wert/Einheit für die Nutzung einer Datenbank.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Except|X_Except]]|| Wird aufgerufen, sobald ein ein geöffneter Filedescriptor in [[#Wichtige_globale_Variablen_aus_fhem.pl|&amp;lt;code&amp;gt;%selectlist&amp;lt;/code&amp;gt;]], der unter &amp;lt;code&amp;gt;$hash-&amp;gt;{EXCEPT_FD}&amp;lt;/code&amp;gt; im Geräte-Hash gesetzt ist, einen Interrupt bzw. Exception auslöst.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Copy|X_Copy]]|| Wird durch das Modul 98_copy.pm aufgerufen im Rahmen des &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;-Befehls sobald ein Gerät kopiert wurde.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_State|X_State]]|| Wird aufgerufen im Rahmen des &amp;lt;code&amp;gt;setstate&amp;lt;/code&amp;gt;-Befehls bevor der Status einer Gerätedefinition bzw. eines zugehörigen Readings gesetzt wird.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_AsyncOutput|X_AsyncOutput]]|| Nur Relevant für Module die via [[TcpServerUtils]] eine Client-Verbindung zu FHEM ermöglichen (z.B. FHEMWEB und telnet). Ermöglicht die asynchrone Ausgabe von Daten via [[DevelopmentModuleAPI#asyncOutput|asyncOutput()]] an einen einzelnen verbundenen Client.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_ActivateInform|X_ActivateInform]]|| Nur Relevant für Module die via [[TcpServerUtils]] eine Client-Verbindung zu FHEM ermöglichen (z.B. FHEMWEB und telnet). Ermöglicht das aktivieren des inform-Mechanismus zum senden von Events für einen einzelnen verbundenen Client.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Authorize|X_Authorize]]|| Wird aufgerufen im Rahmen von [[DevelopmentModuleAPI#Authorized|Authorized()]] um eine gewünschte Vorgangs-Art zu autorisieren.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Authenticate|X_Authenticate]]||  Wird aufgerufen im Rahmen von [[DevelopmentModuleAPI#Authenticate|Authenticate()]] um eine Authentifizierung zu prüfen und ggf. zu genehmigen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der [[#X_Initialize|Initialize]]-Funktion werden diese wie folgt definiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$hash-&amp;gt;{DbLog_splitFn} = &amp;quot;X_DbLog_split&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{ExceptFn} = &amp;quot;X_Except&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{CopyFn} = &amp;quot;X_Copy&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{AsyncOutputFn} = &amp;quot;X_AsyncOutput&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{ActivateInformFn} = &amp;quot;X_ActivateInform&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{StateFn} = &amp;quot;X_State&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{AuthorizeFn} = &amp;quot;X_Authorize&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{AuthenticateFn} = &amp;quot;X_Authenticate&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Diese Funktionen werden in diesem Abschnitt genauer beschrieben.&lt;br /&gt;
==== X_DbLog_split ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_DbLog_split ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $event, $device_name ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
    &lt;br /&gt;
	return  ( $reading, $value, $unit );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die DbLog_split-Funktion wird durch das Modul [[DbLog]] aufgerufen, sofern der Nutzer DbLog benutzt. Sofern diese Funktion implementiert ist, kann der Modulautor das Auftrennen von Events in den Reading-Namen, -Wert und der Einheit selbst steuern. Andernfalls nimmt DbLog diese Auftrennung selber mittels Trennung durch Leerzeichen sowie vordefinierten Regeln zu verschiedenen Modulen vor. Je nachdem, welche Readings man in seinem Modul implementiert, passt diese standardmäßige Trennung jedoch nicht immer.&lt;br /&gt;
&lt;br /&gt;
Der Funktion werden folgende Eingangsparameter übergeben:&lt;br /&gt;
# Das generierte Event (Bsp: &amp;lt;code&amp;gt;temperature: 20.5 °C&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Der Name des Geräts, welche das Event erzeugt hat (Bsp: &amp;lt;code&amp;gt;Temperatursensor_Wohnzimmer&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Es ist nicht möglich in der DbLog_split-Funktion auf die verarbeitende DbLog-Definition zu referenzieren.&lt;br /&gt;
&lt;br /&gt;
Als Rückgabewerte muss die Funktion folgende Werte bereitstellen:&lt;br /&gt;
# Name des Readings (Bsp: &amp;lt;code&amp;gt;temperature&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Wert des Readings (Bsp: &amp;lt;code&amp;gt;20.5&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Einheit des Readings (Bsp: &amp;lt;code&amp;gt;°C&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_DbLog_splitFn($$)&lt;br /&gt;
{&lt;br /&gt;
	my ($event, $device) = @_;&lt;br /&gt;
	my ($reading, $value, $unit);&lt;br /&gt;
        my $devhash = $defs{$device}&lt;br /&gt;
&lt;br /&gt;
	if($event =~ m/temperature/) {&lt;br /&gt;
	   $reading = &#039;temperature&#039;;&lt;br /&gt;
	   $value = substr($event,12,4);&lt;br /&gt;
	   $unit = &#039;°C&#039;;&lt;br /&gt;
	}   &lt;br /&gt;
        &lt;br /&gt;
        return ($reading, $value, $unit);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_Except ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Except ($)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die X_Except-Funktion wird durch fhem.pl aufgerufen, wenn die Gerätedefinition &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;[[#Wichtige_globale_Variablen_aus_fhem.pl|%selectlist]]&amp;lt;/code&amp;gt; aufgeführt ist und der Filedeskriptor in &amp;lt;code&amp;gt;$hash-&amp;gt;{EXCEPT_FD}&amp;lt;/code&amp;gt; eine Exception bzw. Interrupt auslöst. &lt;br /&gt;
&lt;br /&gt;
Der Rückgabewert wird nicht ausgewertet und ist daher irrelevant.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
use IO::File;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
sub X_Except ($)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	# Filehandle aus Filedescriptor erstellen&lt;br /&gt;
	my $filehandle = IO::File-&amp;gt;new_from_fd($hash-&amp;gt;{EXCEPT_FD}, &#039;r&#039;);&lt;br /&gt;
	seek($filehandle,0,0);	&lt;br /&gt;
&lt;br /&gt;
	# aktuellen Inhalt auslesen&lt;br /&gt;
	my $current_value = $filehandle-&amp;gt;getline;&lt;br /&gt;
&lt;br /&gt;
	if($current_value eq &amp;quot;1&amp;quot;)&lt;br /&gt;
	{&lt;br /&gt;
		...&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		...&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_Copy ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Copy ($)&lt;br /&gt;
{&lt;br /&gt;
	my ( $old_name, $new_name ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die X_Copy-Funktion wird durch das Modul [[copy]] aufgerufen nachdem ein Nutzer eine Gerätedefinition über den Befehl &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt; kopiert hat. Dazu werden als Funktionsparameter die Definitionsnamen der alten und neuen Gerätedefinition übergeben. Es dient dazu zusätzliche Daten aus der zu kopierenden Gerätedefinition in die neue Definition zu übernehmen. Der Befehl &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt; überträgt lediglich &amp;lt;code&amp;gt;$hash-&amp;gt;{DEF}&amp;lt;/code&amp;gt; in die neue Definition sowie sämtliche gesetzte Attribute. Weitere Daten müssen dann durch die X_Copy-Funktion übertragen werden. &lt;br /&gt;
&lt;br /&gt;
Die X_Copy-Funktion wird erst nach dem erfolgtem Kopiervorgang aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Der Rückgabewert wird nicht ausgewertet und ist daher irrelevant.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sub X_Copy ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $old_name, $new_name ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	my $old_hash = $defs{$old_name};&lt;br /&gt;
	my $new_hash = $defs{$new_name};&lt;br /&gt;
&lt;br /&gt;
	# copy also temporary session key&lt;br /&gt;
	$new_hash-&amp;gt;{helper}{SESSION_KEY} = $old_hash-&amp;gt;{helper}{SESSION_KEY};&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_AsyncOutput ====&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039; Diese Funktion ist nur relevant, wenn man ein Frontend-Modul erstellt über das FHEM von einem Anwender bedient werden kann (FHEMWEB, telnet, yowsup, telegram, alexa-fhem, homebridge-fhem, tabletui, ...).}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_AsyncOutput ($)&lt;br /&gt;
{&lt;br /&gt;
	my ( $client_hash, $text ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
&lt;br /&gt;
	return $error;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion X_AsyncOutput wird durch [[DevelopmentModuleAPI#asyncOutput|asyncOutput()]] von anderen Modulen aufgerufen. Es erlaubt diesen anderen Modulen die Ausgabe von asynchronen Befehlsergebnissen &amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt; zuvor ausgeführter set-/get-Befehle an den entsprechenden Client (identifiziert durch den Client-Hash &amp;lt;code&amp;gt;$client_hash&amp;lt;/code&amp;gt; der temporären Definition) zurückzugeben. &lt;br /&gt;
&lt;br /&gt;
Wenn ein Client einen set-/get-Befehl ausführt, wird der Client-Hash bei der Ausführung dieser Befehle an die jeweiligen Module übermittelt. Sobald ein Befehl ausgeführt wird, der seine Ausgabe asynchron ausführen möchte und die Client-Verbindung des Server-Moduls dies unterstützt (&amp;lt;code&amp;gt;$client_hash-&amp;gt;{canAsyncOutput}&amp;lt;/code&amp;gt; ist gesetzt), merkt sich das befehlsausführende Modul den Client-Hash und gibt das Ergebnis des Befehls zu späterer Zeit via [[DevelopmentModuleAPI#asyncOutput|asyncOutput()]] an den ursprünglichen Client zurück. Die Funktion X_AsyncOutput des Server-Moduls kümmert sich darum das Ergebnis dem entsprechenden Client in der notwendigen Form zuzustellen.&lt;br /&gt;
&lt;br /&gt;
Der Rückgabewert von X_AsyncOutput() wird als Rückgabewert für asyncOutput() verwendet. Man kann hier im Fehlerfall eine Fehlermeldung angeben und im Erfolgsfall &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt;. Der Rückgabewert wird aber aktuell nicht ausgewertet.&lt;br /&gt;
&lt;br /&gt;
==== X_ActivateInform====&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039; Diese Funktion ist nur relevant, wenn man ein Frontend-Modul erstellt über das FHEM von einem Anwender bedient werden kann (FHEMWEB, telnet, yowsup, telegram, alexa-fhem, homebridge-fhem, tabletui, ...).}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_ActivateInform($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $client_hash, $arg ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion X_ActivateInform wird aktuell nur durch den [[update]]-Befehl aufgerufen, sofern ein Client eines Frontend-Moduls diesen Befehl aufgerufen hat um den Inform-Mechanismus (Senden von Events) zu aktivieren. Dadurch wird im Falle von [[update]] die umgehende Anzeige der Logmeldungen für den ausführenden Client aktiviert. In [[FHEMWEB]] geschieht das über den Event-Monitor, bei telnet mit der direkten Ausgabe.&lt;br /&gt;
&lt;br /&gt;
Da diese Funktion aktuell nur speziell für den update-Befehl implementiert ist, kann man aktuell keine genaue Angaben zu den möglichen Werten von &amp;lt;code&amp;gt;$arg&amp;lt;/code&amp;gt; geben. Dieser Parameter dient dazu genauer zu spezifizieren was exakt an Events an den entsprechenden Client &amp;lt;code&amp;gt;$client_hash&amp;lt;/code&amp;gt; zu senden ist. Aktuell wird dazu die Parametersyntax des inform-Befehls verwendet (on|off|log|raw|timer|status).&lt;br /&gt;
&lt;br /&gt;
==== X_State ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_State($$$$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $time, $readingName, $value ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
&lt;br /&gt;
	return $error;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die X_State-Funktion wird durch fhem.pl aufgerufen, sobald über den Befehl &amp;lt;code&amp;gt;setstate&amp;lt;/code&amp;gt; versucht wird ein Wert für ein Reading oder den Status (&amp;lt;code&amp;gt;$hash-&amp;gt;{STATE}&amp;lt;/code&amp;gt;) einer Gerätedefinition zu setzen. Dieser Befehl wird primär beim Starten von FHEM aufgerufen sobald das State-File eingelesen wird. Je nachdem, ob im gegebenen Fall ein Reading oder der Definitionsstatus gesetzt wird, haben die Übergabeparameter verschiedene Werte:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Funktionsparameter!! Wert beim Setzen eines Readings !! Wert beim Setzen eines Definitionsstatus&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; || colspan=2 align=center | Die Hashreferenz der betreffenden Gerätedefinition&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$time&amp;lt;/code&amp;gt;|| Der Zeitstempel auf welchen das Reading &amp;lt;code&amp;gt;$readingName&amp;lt;/code&amp;gt; gesetzt werden soll. Das Ergebnis entspricht dem Rückgabewert der Funktion || Der aktuelle Zeitstempel zum jetzigen Zeitpunkt.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$readingName&amp;lt;/code&amp;gt;|| Der Name des Readings, welches auf einen neuen Wert gesetzt werden soll. || Statischer Wert &amp;quot;STATE&amp;quot; um anzuzeigen, dass es sich um den Definitionsstatus handelt, welcher gesetzt werden soll (&amp;lt;code&amp;gt;$hash-&amp;gt;{STATE}&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$value&amp;lt;/code&amp;gt; || Den Wert, welchen das Reading &amp;lt;code&amp;gt;$readingName&amp;lt;/code&amp;gt; annehmen soll. || Den Wert, welchen die Gerätedefinition als Status annehmen soll.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn via &amp;lt;code&amp;gt;setstate&amp;lt;/code&amp;gt; ein Reading gesetzt wird, kann die X_State-Funktion das Setzen dieses Readings durch die Rückgabe einer aussagekräftigen Fehlermeldung unterbinden. Sofern &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben wird, wird das entsprechende Reading auf den übergebenen Status gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wenn via &amp;lt;code&amp;gt;setstate&amp;lt;/code&amp;gt; der Definitionsstatus gesetzt wird, wird die X_State-Funktion erst nach dem Setzen des Status aufgerufen. Man kann dabei zwar eine Fehlermeldung zurückgeben, der Status wird aber dennoch übernommen. Die Fehlermeldung wird lediglich dem Nutzer angezeigt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_State($$$$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $time, $readingName, $value ) = @_;&lt;br /&gt;
&lt;br /&gt;
	return undef if($readingName &amp;quot;STATE&amp;quot; || $value ne &amp;quot;inactive&amp;quot;);&lt;br /&gt;
	readingsSingleUpdate($hash, &amp;quot;state&amp;quot;, &amp;quot;inactive&amp;quot;, 1);&lt;br /&gt;
	return undef;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_Authorize ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Authorize($$$$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $client_hash, $type, $arg ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
 &lt;br /&gt;
	return $authorized;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Authorize-Funktion wird von fhem.pl aufgerufen um zu erfragen, ob ein bestimmter Client &amp;lt;code&amp;gt;$client_hash&amp;lt;/code&amp;gt; die Aktion &amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;$arg&amp;lt;/code&amp;gt; ausführen darf. Auf diese Weise können Module Einfluss nehmen, welcher User welche Funktionen in FHEM nutzen darf. Wenn ein Client eine Aktion ausführen möchte, werden alle Module, die eine Authorize-Funktion implementiert haben, gefragt, ob diese Aktion ausgeführt werden darf. Als Rückgabewert wird das Ergebnis der Überprüfung zurückgegeben, wobei &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; (unbekannt / nicht zuständig), &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; (erlaubt) oder &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; (verboten) zurückgegeben werden können.&lt;br /&gt;
&lt;br /&gt;
Es gibt aktuell folgende &amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;$arg&amp;lt;/code&amp;gt; Kombinationen, mit denen die Authorize-Funktion aufgerufen werden:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! $type !! $arg !! Überschrift&lt;br /&gt;
|- &lt;br /&gt;
|rowspan=&amp;quot;3&amp;quot; style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;&#039;&#039;&#039;$type&#039;&#039;&#039; = &amp;quot;cmd&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Befehlsausführung&#039;&#039;&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;&#039;&#039;&#039;$arg&#039;&#039;&#039; = &amp;quot;set Lampe on&amp;quot;&amp;lt;/code&amp;gt; || Jeglicher FHEM-Befehl, der ausgeführt werden soll, wird in &amp;lt;code&amp;gt;$arg&amp;lt;/code&amp;gt; hinterlegt, sodass innerhalb einer Authorize-Funktion der Befehl genauer geparst werden kann um zu entscheiden, ob dieser Befehl erlaubt ist, oder nicht.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;&#039;&#039;&#039;$arg&#039;&#039;&#039; = &amp;quot;perl&amp;quot;&amp;lt;/code&amp;gt; || Ausführen von Perl-Befehlen jeglicher Art. Der genaue Befehl wird dabei nicht an die Authorize-Funktion übergeben.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;{ReadingsVal(&amp;quot;Lampe&amp;quot;, &amp;quot;state&amp;quot;, &amp;quot;off&amp;quot;}&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;&#039;&#039;&#039;$arg&#039;&#039;&#039; = &amp;quot;shell&amp;quot;&amp;lt;/code&amp;gt; || Ausführen von Shell-Befehlen jeglicher Art. Der genaue Befehl wird dabei nicht an die Authorize-Funktion übergeben.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;&amp;quot;/opt/fhem/myScript.sh&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;&#039;&#039;&#039;$type&#039;&#039;&#039; = &amp;quot;devicename&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Sichtbarkeit von Geräten/Definitionen&#039;&#039; &lt;br /&gt;
|style=&amp;quot;white-space: nowrap;&amp;quot; |  &amp;lt;code&amp;gt;&#039;&#039;&#039;$arg&#039;&#039;&#039; = &amp;quot;Licht_Wohnzimmer&amp;quot;&amp;lt;/code&amp;gt; || Sichtbarkeit des jeweiligen Gerät/Definition in FHEM. Dies bedeutet konkret die Auffindbarkeit im &amp;lt;code&amp;gt;list&amp;lt;/code&amp;gt;-Befehl, sowie der Suche via [[DevelopmentModuleAPI#devspec2array|devspec2array()]]. Wird eine solche Anfrage durch die Authorize-Funktion abgelehnt, ist das entsprechende Gerät bzw. Definition für den jeweiligen Client nicht sichtbar.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== X_Authenticate ====&lt;br /&gt;
{{Link2Forum|Topic=72757|Message=644098}}&lt;br /&gt;
&lt;br /&gt;
== Bereitstellen eines eigenen Befehls (Befehlsmodul) ==&lt;br /&gt;
&lt;br /&gt;
Ein Modul kann primär einen neuen FHEM-Befehl bereitstellen. Man spricht in so einem Fall nicht von einem Gerätemodul, sondern einem Befehlsmodul. Ein solches Befehlsmodul stellt nur einen einzelnen Befehl bereit, der dem Modulnamen entsprechen muss. Nur, wenn der Modulname dem Befehlsname entspricht, kann FHEM das Modul beim ersten Ausführen dieses unbekannten Befehls finden und nachladen.&lt;br /&gt;
&lt;br /&gt;
Der entsprechende Befehl wird dazu in der [[#X_Initialize|Initialize]]-Funktion im globalen Hash &amp;lt;code&amp;gt;[[DevelopmentModuleIntro#Wichtige_globale_Variablen_aus_fhem.pl|%cmds]]&amp;lt;/code&amp;gt; registriert:&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;
    $cmds{X} = { Fn           =&amp;gt; &amp;quot;CommandX&amp;quot;,&lt;br /&gt;
                 Hlp          =&amp;gt; &amp;quot;&amp;lt;argument1&amp;gt; [optional_argument2], print something very useful&amp;quot;,&lt;br /&gt;
 &lt;br /&gt;
                 # optionaler Filter für Clientmodule als regulärer Ausdruck&lt;br /&gt;
                 ClientFilter =&amp;gt; &amp;quot;FHEMWEB&amp;quot;&lt;br /&gt;
                };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird der neue Befehl &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; in FHEM registriert. Die Funktion mit dem Namen &amp;lt;code&amp;gt;CommandX&amp;lt;/code&amp;gt; setzt diesen Befehl innerhalb des Moduls um. Desweiteren wird eine kurze Aufrufsyntax mitgegeben, welche beim Aufruf des &amp;lt;code&amp;gt;help&amp;lt;/code&amp;gt;-Befehls dem Nutzer angezeigt wird um als Gedankenstütze zu dienen. Optional kann man mittels &amp;lt;code&amp;gt;ClientFilter&amp;lt;/code&amp;gt; (regulärer Ausdruck für Modulnamen) die Ausführbarkeit nur auf bestimmte Client-Module (wie FHEMWEB oder telnet) beschränken. &lt;br /&gt;
&lt;br /&gt;
Nun muss noch die Funktion &amp;lt;code&amp;gt;CommandX&amp;lt;/code&amp;gt; im Rahmen des Moduls implementiert werden, welche den Befehl &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; umsetzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub CommandX($$)&lt;br /&gt;
{&lt;br /&gt;
 	my ($client_hash, $arguments) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
	&lt;br /&gt;
	return $output;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei werden der Befehlsfunktion zwei Parameter übergeben. Zuerst die Hash-Referenz des aufrufenden Clients (sofern manuell ausgeführt, ansonsten &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt;) zwecks Rechteprüfung via [[allowed|allowed-Definitionen]]. Anschließend folgen die Aufrufparameter als zusammenhängende Zeichenkette. Die Trennung der einzelnen Argumente obligt der Funktion (bspw. via [[DevelopmentModuleAPI#parseParams|parseParams()]]). Als Funktionsrückgabewert wird eine Ausgabemeldung erwartet, die dem Nutzer angezeigt werden soll.&lt;br /&gt;
&lt;br /&gt;
== Pollen von Geräten ==&lt;br /&gt;
Wenn Geräte von sich aus keine Informationen senden sondern abgefragt werden müssen, kann man im Modul die Funktion [[DevelopmentModuleAPI#InternalTimer|InternalTimer()]] verwenden um einen Funktionsaufruf zu einem späteren Zeitpunkt durchführen zu können. Man übergibt dabei den Zeitpunkt für den nächsten Aufruf, den Namen der Funktion, die aufgerufen werden soll, sowie den zu übergebenden Parameter. Als zu übergebender Parameter wird üblicherweise der Hash der betroffenen Geräteinstanz verwendet. Damit hat die aufgerufene Funktion Zugriff auf alle wichtigen Daten der Geräteinstanz. Eventuell zusätzlich benötigte Werte können einfach als weitere Internals über den Hash zugänglich gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise könnte man für das Abfragen eines Geräts in der [[#X_Define|Define]]-Funktion den Timer folgendermaßen setzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
InternalTimer(gettimeofday()+2, &amp;quot;X_GetUpdate&amp;quot;, $hash);	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man auch in der [[#X_Notify|Notify]]-Funktion auf das Event &amp;lt;code&amp;gt;global:INITIALIZED&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;global:REREADCFG&amp;lt;/code&amp;gt; reagieren und erst dort, den Timer anstoßen, sobald die Konfiguration komplett eingelesen wurde. Dies ist insbesondere notwendig, wenn man sicherstellen will, dass alle Attribute aus der Konfiguration gesetzt sind, sobald man einen Status-Update initiiert.&lt;br /&gt;
&lt;br /&gt;
In der Funktion &amp;lt;code&amp;gt;X_GetUpdate&amp;lt;/code&amp;gt; selbst wird dann der Timer neu gesetzt, so dass nach einem Intervall die Funktion erneut aufgerufen wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_GetUpdate($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
	Log3 $name, 4, &amp;quot;X: GetUpdate called ...&amp;quot;;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
	&lt;br /&gt;
	# neuen Timer starten in einem konfigurierten Interval.&lt;br /&gt;
	InternalTimer(gettimeofday()+$hash-&amp;gt;{Interval}, &amp;quot;X_GetUpdate&amp;quot;, $hash);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Innerhalb der Funktion kann man nun das Gerät abfragen und die abgefragten Werte in Readings speichern. Falls das Abfragen der Werte jedoch zu einer Verzögerung und damit zu einer Blockade von FHEM führen kann, ist es möglich, in der GetUpdate-Funktion nur die Aufforderung zum Senden bestimmter Daten an das angeschlossene Gerät zu senden und dann das Lesen über die oben beschriebene [[#X_Read|Read]]-Funktion zu implementieren.&lt;br /&gt;
&lt;br /&gt;
Eine genaue Beschreibung der Timer-Funktion gibt es [[DevelopmentModuleAPI#Timer|hier im Wiki]]&lt;br /&gt;
&lt;br /&gt;
== Logging / Debugging ==&lt;br /&gt;
Um Innerhalb eines Moduls eine Log-Meldung in die FHEM-Logdatei zu schreiben, wird die Funktion [[DevelopmentModuleAPI#Log3|Log3()]] aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
Log3 $name, 3, &amp;quot;X ($name) - Problem erkannt ...&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Eine genaue Beschreibung zu der Funktion inkl. Aufrufparameter findet man [[DevelopmentModuleAPI#Log3|hier]]. Es ist generell ratsam in der Logmeldung sowohl den Namen des eigenen Moduls zu schreiben, sowie den Namen des Geräts, welche diese Logmeldung produziert, da die Meldung, so wie sie ist, direkt in das Logfile wandert und es für User ohne diese Informationen schwierig ist, die Meldungen korrekt zuzuordnen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion Log3() verwendet den Namen der Geräteinstanz um das &amp;lt;code&amp;gt;verbose&amp;lt;/code&amp;gt;-Attribut zu prüfen. In der Regel wird bei Modulfunktionen jedoch immer nur der Gerätehash &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; übergeben. Um den Namen der Definition zu ermitteln ist es daher notwendig sich diesen aus dem Hash extrahieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um für eine einzelne Geräteinstanz das Verbose-Level zu erhöhen, ohne gleich für das gesamte FHEM den globalen Verbose-Level zu erhöhen und damit alle Meldungen zu erzeugen, kann man den Befehl &lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;NAME&amp;gt; verbose&amp;lt;/code&amp;gt; verwenden. Beispielsweise &amp;lt;code&amp;gt;attr Lichtschalter_Wohnzimmer verbose 5&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Logmeldungen sollten je nach Art und Wichtigkeit für den Nutzer in unterschiedlichen Loglevels erzeugt werden. Es gibt insgesamt 5 Stufen in denen geloggt werden kann. Standardmäßig steht der systemweite Loglevel (&amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;-Attribut &amp;lt;code&amp;gt;verbose&amp;lt;/code&amp;gt;) auf der Stufe 3. Die Bedeutung der jeweiligen Stufen ist in der {{Link2CmdRef|Lang=de|Anker=verbose}} beschrieben.&lt;br /&gt;
&lt;br /&gt;
Während der Entwicklung eines Moduls kann man für eigene Debug-Zwecke auch die Funktion [[DevelopmentModuleAPI#Debug|Debug()]] verwenden um schnell und einfach Debug-Ausgaben in das Log zu schreiben. Diese sollten in der endgültigen Fassung jedoch nicht mehr vorhanden sein. Sie dienen ausschließlich zum Debugging während der Entwicklung.&lt;br /&gt;
&lt;br /&gt;
Eine genaue Beschreibung der Log-Funktion gibt es [[DevelopmentModuleAPI#Logging|hier im Wiki]].&lt;br /&gt;
&lt;br /&gt;
== Zweistufiges Modell für Module ==&lt;br /&gt;
[[Datei:Zweistufiges Modulkonzept.jpg|mini|rechts|Schematische Darstellung am Beispiel CUL]]&lt;br /&gt;
Es gibt viele Geräte, welche die Kommunikation mit weiteren Geräten mit tlw. unterschiedlichen Protokollen ermöglichen. Das typischste Beispiel bietet hier der [[CUL]], welcher via Funk mit verschiedenen Protokollen weitere Geräte ansprechen kann (z.B. Aktoren, Sensoren, ...). Hier bildet ein Gerät eine Brücke durch die weitere Geräte in FHEM zugänglich gemacht werden können. Dabei werden über einen Kommunikationsweg (z.B. serielle Schnittstelle, TCP, ...) beliebig viele Geräte gesteuert. Typische Beispiele dazu sind:&lt;br /&gt;
&lt;br /&gt;
* [[CUL]]: stellt Geräte mit verschiedenen Kommunikationsprotokollen via Funk bereit (u.a. [[FS20]], [[HomeMatic]], [[Funk-Heizkörperregler_Kurz-Bedienungsanleitung_FHT|FHT]], [[MAX]], ...)&lt;br /&gt;
* [[HMLAN]]: stellt HomeMatic Geräte via Funk bereit&lt;br /&gt;
* [[MAX#MAXLAN|MAXLAN]]: stellt [[MAX|MAX!]] Geräte via Funk bereit&lt;br /&gt;
* [[PanStamp#panStick.2FShield|panStamp]]: stellt weitere panStamp Geräte via Funk bereit&lt;br /&gt;
&lt;br /&gt;
Dabei wird die Kommunikation in 2 Stufen unterteilt:&lt;br /&gt;
* physisches Modul - z.B. 00_CUL.pm - zuständig für die physikalische Kommunikation mit der Hardware. Empfangene Daten müssen einem logischen Modul zugeordnet werden.&lt;br /&gt;
* logische Modul(e) - z.B. 10_FS20.pm - interpretiert protokollspezifische Nachrichten. Sendet protokollspezifische Daten über das physische Modul an die Hardware.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;physisches Modul&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das physische Modul öffnet die Datenverbindung zum Gerät (z.B. CUL) und verarbeitet sämtliche Daten. Es kümmert sich um den Erhalt der Verbindung (bsp. durch Keep-Alives) und konfiguriert das Gerät so, dass eine Kommunikation mit allen weiteren Geräten möglich ist (bsp. Frequenz, Modulation, Kanal, etc.).&lt;br /&gt;
&lt;br /&gt;
Empfangene Nutzdaten werden als Zeichenkette über die Funktion [[DevelopmentModuleAPI#Dispatch|Dispatch()]] an logische Module weitergegeben.&lt;br /&gt;
&lt;br /&gt;
Das Modul stellt eine [[#Die_Match-Liste|Match-Liste]] bereit, anhand FHEM die Nachricht einem Modul zuordnen kann, sofern dieses noch nicht geladen sein sollte. Die Match-Liste enthält eine Liste von regulären Ausdrücken und ordnet diese einem Modul zu. Wenn eine Nachricht auf einen solchen regulären Ausdruck passt und das Modul noch nicht geladen ist, lädt FHEM dieses automatisch nach, zwecks Verarbeitung der Nachricht. &lt;br /&gt;
&lt;br /&gt;
Anhand einer bereitgestellten [[#Die_Client-Liste|Client-Liste]] (Auflistung von logischen Modulen) kann FHEM feststellen, welche logischen Module mit dem physischen Modul kommunizieren können. Nur die hier aufgelisteten, logischen Module werden beim Aufruf von [[DevelopmentModuleAPI#Dispatch|Dispatch()]] angesprochen.&lt;br /&gt;
&lt;br /&gt;
Das Modul stellt eine [[#X_Write|Write]]-Funktion zur Verfügung, über die logische Module Daten in beliebiger Form an die Hardware übertragen können. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;logisches Modul&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das logische Modul interpretiert die via Dispatch() übergebene Nachricht (Zeichenkette) durch eine bereitgestellte [[#X_Parse|Parse]]-Funktion und erzeugt entsprechende Readings/Events. Es stellt über &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt;-/&amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt;-Kommandos Steuerungsmöglichkeiten dem Nutzer zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
Es stellt FHEM einen [[#Der_Match-Ausdruck|Match-Ausdruck]] (regulärer Ausdruck) zur Verfügung anhand [[DevelopmentModuleAPI#Dispatch|Dispatch()]] ermitteln kann, ob die Nachricht durch das logische Modul verarbeitet werden kann. Nur Nachrichten, welche auf diesen Ausdruck passen, werden an das logische Modul weitergegeben (Aufruf [[#X_Parse|Parse]]-Funktion).&lt;br /&gt;
&lt;br /&gt;
=== Die Client-Liste ===&lt;br /&gt;
&lt;br /&gt;
Die Client-Liste ist eine Auflistung von Modulnamen (genauer: regulären Ausdrücken die auf Modulnamen passen) die in einem physischen Modul gesetzt ist. Damit wird definiert, mit welchen logischen Modulen das physikalische Modul  kommunizieren kann. &lt;br /&gt;
&lt;br /&gt;
Eine Client-Liste ist eine Zeichenkette, welche aus allen logischen Modulnamen besteht. Die einzelnen Namen werden durch einen Doppelpunkt getrennt. Anstatt kompletter Modulnamen können auch reguläre Ausdrücke verwendet werden, die auf mehrere Modulnamen passen (z.B. &amp;lt;code&amp;gt;CUL_.*&amp;lt;/code&amp;gt; um die logischen Module CUL_HM, CUL_MAX, etc. zu verwenden).&lt;br /&gt;
&lt;br /&gt;
Bsp.: Die Client-Liste von dem Modul CUL lautet daher wie folgt:&lt;br /&gt;
&lt;br /&gt;
 FS20:FHT.*:KS300:USF1000:BS:HMS:CUL_EM:CUL_WS:CUL_FHTTK:CUL_HOERMANN:ESA2000:CUL_IR:CUL_TX:Revolt:IT:UNIRoll:SOMFY:STACKABLE_CC:CUL_RFR:CUL_TCM97001:CUL_REDIRECT&lt;br /&gt;
&lt;br /&gt;
Alle hier aufgelisteten Module können über das Modul CUL Daten empfangen bzw. senden.&lt;br /&gt;
&lt;br /&gt;
Die Client-Liste hat generell folgende Funktion:&lt;br /&gt;
* Die Funktion [[DevelopmentModuleAPI#Dispatch|Dispatch()]] prüft nur Module, welche in der Client-Liste enthalten sind, ob diese die Nachricht verarbeiten können (Prüfung via [[#Der Match-Ausdruck|Match-Ausdruck]])&lt;br /&gt;
* Die Funktion [[DevelopmentModuleAPI#AssignIoPort|AssignIoPort()]] prüft anhand sämtlicher Client-Listen in FHEM, welches IO-Gerät für ein logisches Gerät nutzbar ist.&lt;br /&gt;
&lt;br /&gt;
Üblicherweise wird die Client-Liste in der [[#X_Initialize|Initialize]]-Funktion im Modul-Hash gesetzt:&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;{Clients} = &amp;quot;FS20:KS300:FHT.*&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man kann die Client-Liste jedoch auch pro physikalisches Gerät setzen. Eine gesetzte Client-Liste in einem Gerät hat immer Vorrang vor der Liste im Modul-Hash. Eine gerätespezifische Client-Liste wird dann verwendet, wenn bspw. ein Gerät je nach Konfiguration nur bestimmte logische Module bedienen kann. Bspw. kann ein CUL je nach RF-Einstellungen FS20, uvm. oder nur HomeMatic bedienen. In einem solchen Fall wird die Client-Liste im Geräte-Hash gesetzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Define($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $def ) = @_;&lt;br /&gt;
	...&lt;br /&gt;
	$hash-&amp;gt;{Clients} = &amp;quot;CUL_HM&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In vielen Modulen, welche nach dem zweistufigem Konzept arbeiten, beginnt und endet die Client-Liste mit einem Doppelpunkt. Dies ist ein historisches Überbleibsel, da der Prüfmechanismus die Client-Liste früher auf das Vorhandensein von &amp;lt;code&amp;gt;&#039;&#039;&#039;&amp;lt;u&amp;gt;&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;:&amp;lt;/font&amp;gt;&amp;lt;/u&amp;gt;&#039;&#039;&#039;&amp;amp;lt;Modulname&amp;amp;gt;&#039;&#039;&#039;&amp;lt;u&amp;gt;&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;:&amp;lt;/font&amp;gt;&amp;lt;/u&amp;gt;&#039;&#039;&#039;&amp;lt;/code&amp;gt; prüfte. Dies ist nun nicht mehr notwendig. Die einzelnen Modulnamen müssen lediglich durch einen Doppelpunkt getrennt werden.&lt;br /&gt;
&lt;br /&gt;
=== Die Match-Liste ===&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;u&amp;gt;&#039;&#039;&#039;ACHTUNG&#039;&#039;&#039;:&amp;lt;/u&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
Sämtliche regulären Ausdrücke in der Match-Liste werden &amp;quot;case insensitive&amp;quot; überprüft. Das bedeutet, dass Groß-/Kleinschreibung nicht berücksichtigt wird.&lt;br /&gt;
&lt;br /&gt;
Um dennoch in einem regulären Ausdruck auf Groß-/Kleinschreibung zu prüfen, kann man dieses mit dem Modifizierer &amp;lt;code&amp;gt;(?-i)&amp;lt;/code&amp;gt; wieder aktivieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my %matchListFHEMduino = (&lt;br /&gt;
    ....&lt;br /&gt;
    &amp;quot;5:FHEMduino_PT2262&amp;quot;   =&amp;gt; &amp;quot;^(?-i)IR.*\$&amp;quot;,&lt;br /&gt;
    ....&lt;br /&gt;
    &amp;quot;13:IT&amp;quot;                =&amp;gt; &amp;quot;^(?-i)i......\$&amp;quot;,&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe dazu Forumsbeitrag: {{Link2Forum|Topic=33422}}&lt;br /&gt;
}}&lt;br /&gt;
Die Match-Liste ordnet eine Nachrichtensyntax (regulärer Ausdruck) einem Modulnamen zu und wird in einem physikalischen Modul gesetzt. Sollte eine Nachricht vom physikalischen Gerät empfangen werden, die durch kein geladenes Modul verarbeitet werden kann ([[DevelopmentModuleAPI#Dispatch|Dispatch()]] prüft nur alle geladenen Module aus der [[#Die Client-Liste|Client-Liste), so wird über die Match-Liste geprüft, welches Modul diese Nachricht verarbeiten kann. Dieses Modul wird anschließend geladen und die Nachricht durch dieses verarbeitet. In dieser Liste findet mittels regulärem Ausdruck eine Zuordnung der Nachrichtenstruktur zum verarbeitenden logischen Modul statt.&lt;br /&gt;
&lt;br /&gt;
Diese Liste wird ausschließlich in der [[DevelopmentModuleAPI#Dispatch|Dispatch()]]-Funktion verwendet. Sollte keine passendes Modul, welches bereits geladen ist, zur Verarbeitung einer Nachricht gefunden werden, so wird mithilfe der Match-Liste aufgrund der vorliegenden Nachricht das entsprechende Modul ermittelt. Dieses Modul wird dann direkt geladen und die Nachricht wird via [[#X_Parse|Parse]]-Funktion verarbeitet.&lt;br /&gt;
&lt;br /&gt;
Die Match-Liste ist eine Zuordnung von einem Sortierpräfix + Modulname zu einem regulären Ausdruck:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;1:FS20&amp;quot;  =&amp;gt; &amp;quot;^81..(04|0c)..0101a001&amp;quot;,&lt;br /&gt;
    &amp;quot;2:KS300&amp;quot; =&amp;gt; &amp;quot;^810d04..4027a001&amp;quot;,&lt;br /&gt;
    &amp;quot;3:FHT&amp;quot;   =&amp;gt; &amp;quot;^81..(04|09|0d)..(0909a001|83098301|c409c401)..&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Sortierpräfix (&amp;lt;code&amp;gt;&amp;lt;u&amp;gt;1:&amp;lt;/u&amp;gt;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;FS20&amp;lt;/font&amp;gt;&amp;lt;/code&amp;gt;) dient als Sortierhilfe um so die Reihenfolge der Prüfung festzulegen. Bei der Prüfung wird die Match-Liste mittels sort() nach dem Schlüssel (Sortierpräfix + Modulname) sortiert und die regulären Ausdrücke werden dann nacheinander getestet. Daher sollten die präzisesten Ausdrücke immer zuerst getestet werden, sofern es weniger präzise Ausdrücke in der Match-Liste gibt. Dabei ist zu beachten, dass der Sortierpräfix nicht nach numerischen Regeln sortiert wird, sondern zeichenbasierend.&lt;br /&gt;
&lt;br /&gt;
Üblicherweise wird die Match-Liste in der [[#X_Initialize|Initialize]]-Funktion im Modul-Hash gesetzt:&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;
   ...&lt;br /&gt;
&lt;br /&gt;
   $hash-&amp;gt;{MatchList} = { &amp;quot;1:FS20&amp;quot;      =&amp;gt; &amp;quot;^81..(04|0c)..0101a001&amp;quot;,&lt;br /&gt;
                          &amp;quot;2:KS300&amp;quot;     =&amp;gt; &amp;quot;^810d04..4027a001&amp;quot;,&lt;br /&gt;
                          &amp;quot;3:FHT&amp;quot;       =&amp;gt; &amp;quot;^81..(04|09|0d)..(0909a001|83098301|c409c401)..&amp;quot; };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man kann die Match-Liste, ähnlich wie bei der Client-Liste, auch pro physikalisches Gerät setzen. Dabei hat auch hier die Match-Liste eines Gerätes immer Vorrang vor der Match-Liste aus dem Modul-Hash:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Define($$)&lt;br /&gt;
{&lt;br /&gt;
   my ($hash, $def) = @_;&lt;br /&gt;
&lt;br /&gt;
   ...&lt;br /&gt;
&lt;br /&gt;
   $hash-&amp;gt;{MatchList} = { &amp;quot;1:CUL_HM&amp;quot; =&amp;gt; &amp;quot;^A....................&amp;quot; };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Der Match-Ausdruck ===&lt;br /&gt;
&lt;br /&gt;
Ein Match-Ausdruck wird in einem logischen Modul gesetzt und dient der Prüfung, ob eine Nachricht durch das eigene Modul via [[#X_Parse|Parse]]-Funktion verarbeitet werden kann. Es handelt sich hierbei um einen einzelnen regulären Ausdruck, den FHEM innerhalb der [[DevelopmentModuleAPI#Dispatch|Dispatch()]]-Funktion prüft. Nur wenn eine Nachricht via Dispatch() auf diesen Audruck matcht, wird die Parse-Funktion des eigenen Moduls aufgerufen um die Nachricht zu verarbeiten. &lt;br /&gt;
&lt;br /&gt;
Der Hintergrund, warum man den Aufruf mit einem solchen Ausdruck vorher abprüft, liegt in der Möglichkeit, dass ein physikalisches Modul mehrere unterschiedliche logische Module ansprechen kann. So kann FHEM jedes geladene Modul durch diesen Match-Ausdruck prüfen, ob es diese Nachricht verarbeiten kann. Erst, wenn alle geladenen Module, aufgrund einer Prüfung des Ausdrucks, die Nachricht nicht verarbeiten können, wird via [[#Die_Match-Liste|Match-Liste]] ermittelt, welches Modul geladen werden muss um die Nachricht zu verarbeiten. &lt;br /&gt;
&lt;br /&gt;
Der Match-Ausdruck wird in der [[#X_Initialize|Initialize]]-Funktion zur Verfügung gestellt:&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;
	...&lt;br /&gt;
	&lt;br /&gt;
	# Dieses Modul verarbeitet FS20 Nachrichten&lt;br /&gt;
	$hash-&amp;gt;{Match} = &amp;quot;^81..(04|0c)..0101a001&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Die vollständige Implementierung ===&lt;br /&gt;
&lt;br /&gt;
Hier nun eine Zusammenfassung beim zweistufigen Modulkonzept in der jeweiligen Stufe implementiert werden muss, damit die Kommunikation funktioniert.&lt;br /&gt;
&lt;br /&gt;
==== physisches Modul ====&lt;br /&gt;
&lt;br /&gt;
Das physische Modul, welches als Kommunikationsbrücke zwischen der Hardware und logischen Modulen fungieren wird, sollte mindestens folgende Funktionen implementieren:&lt;br /&gt;
&lt;br /&gt;
* [[#X_Initialize|Initialize]]-Funktion - Zum Registrieren des Moduls in FHEM.&lt;br /&gt;
* [[#X_Define|Define]]-Funktion - Zum öffnen der Datenverbindung zur Hardware (IP-Adresse/serielle Schnittstelle/...).&lt;br /&gt;
* [[#X_Read|Read]]-Funktion - Zum Lesen von Daten, welche die Hardware übermittelt.&lt;br /&gt;
* [[#X_Read|Ready]]-Funktion - Zum Wiederaufbau der Verbindung bei Verbindungsabbruch, bzw. Prüfung auf lesbare Daten bei serieller Schnittstelle unter Windows.&lt;br /&gt;
* [[#X_Write|Write]]-Funktion - Zum Senden von Daten, welche logische Module via [[DevelopmentModuleAPI#IOWrite|IOWrite()]] an die Hardware übertragen möchten.&lt;br /&gt;
* [[#X_Undef|Undef]]-Funktion - Schließen der Verbindung zur Hardware beim Löschen via &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;rereadcfg&amp;lt;/code&amp;gt;.&lt;br /&gt;
* [[#X_Shutdown|Shutdown]]-Funktion - Schließen der Verbindung zur Hardware beim Stopp von FHEM via &amp;lt;code&amp;gt;shutdown&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Desweiteren müssen in der [[#X_Initialize|Initialize]]-Funktion folgende Daten bereitgestellt werden:&lt;br /&gt;
&lt;br /&gt;
* [[#Die_Client-Liste|Client-Liste]] - Auflistung aller logischen Module, die über dieses Modul kommunizieren können&lt;br /&gt;
* [[#Die_Match-Liste|Match-Liste]] - Zuordnung von Nachrichtensyntax zu Modul zwecks Autoload-Funktionalität.&lt;br /&gt;
&lt;br /&gt;
==== logisches Modul ====&lt;br /&gt;
&lt;br /&gt;
Das logische Modul, bildet ein einzelnes Gerät ab, über das mit einem physikalisches Modul kommuniziert werden kann. Es sollte mindestens folgende Funktionen implementieren:&lt;br /&gt;
&lt;br /&gt;
* [[#X_Initialize|Initialize]]-Funktion - Zum Registrieren des Moduls in FHEM.&lt;br /&gt;
* [[#X_Define|Define]]-Funktion - Speichern des Definition Pointers (siehe [[#X_Parse|Parse-Funktion]])&lt;br /&gt;
* [[#X_Parse|Parse]]-Funktion - Zum Lesen von Daten, welche die Hardware übermittelt.&lt;br /&gt;
* [[#X_Undef|Undef]]-Funktion - Löschen des Definition Pointers beim Löschen via &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;rereadcfg&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Desweiteren müssen in der [[#X_Initialize|Initialize]]-Funktion folgende Daten bereitgestellt werden:&lt;br /&gt;
&lt;br /&gt;
* [[#Der_Match-Ausdruck|Match-Ausdruck]] - Prüfausdruck, ob eine Nachricht durch dieses Modul verarbeitet werden kann.&lt;br /&gt;
&lt;br /&gt;
=== Kommunikation von der Hardware bis zu den logischen Modulen ===&lt;br /&gt;
&lt;br /&gt;
Die Gerätedefinition des physischen Moduls öffnet eine Verbindung zur Hardware (z.B. via [[DevIo]]). Die [[#X_Read|Read]]-Funktion wird bei anstehenden Daten aus der Hauptschleife von fhem.pl aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Die Read-Funktion stellt dabei sicher, dass die Daten&lt;br /&gt;
* komplett (in der Regel über einen internen Puffer in &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;) und&lt;br /&gt;
* korrekt (z.B. via Prüfung mittels regulärem Ausdruck)&lt;br /&gt;
sind und ruft die globale Funktion [[DevelopmentModuleAPI#Dispatch|Dispatch()]] mit einer kompletten Nachricht auf.&lt;br /&gt;
&lt;br /&gt;
Die Funktion Dispatch() prüft alle geladenen Module aus der [[#Die_Client-Liste|Client-Liste]] des physikalischen Moduls nach möglichen logischen Modulen zur Verarbeitung. Alle zum Zeitpunkt geladenen Module, die in der Client-Liste aufgeführt sind, werden über den [[#Der_Match-Ausdruck|Match-Ausdruck]] geprüft, ob sie mit der Nachricht etwas anfangen können. Sollte bei einem logischen Modul der Match-Ausdruck passen, so wird die entsprechende [[#X_Parse|Parse]]-Funktion des logischen Moduls aufgerufen. Sofern keine passendes Modul gefunden wurde, um die Nachricht zu verarbeiten, wird in der [[#Die_Match-Liste|Match-Liste]] im Geräte- bzw. Modul-Hash der physischen Gerätedefinition nach dem passenden Modul gesucht. Sollte es darin ein Modul geben, was diese Art von Nachricht verarbeiten kann, so wird versucht dieses Modul zu laden um nun die Nachricht via Parse-Funktion zu verarbeiten. Es erfolgt in diesem Fall keine Vorprüfung durch den Match-Ausdruck.&lt;br /&gt;
&lt;br /&gt;
Durch Dispatch() wird nun die [[#X_Parse|Parse]]-Funktion des gefundenen logischen Moduls aufgerufen. Diese&lt;br /&gt;
* interpretiert die übergebene Nachricht,&lt;br /&gt;
* versucht eine existierende Gerätedefinition in FHEM zu finden (z.B. mittels Definition Pointer), für welche die Nachricht addressiert ist,&lt;br /&gt;
* setzt alle [[#Readings|Readings]] für die gefundene Gerätedefinition via [[DevelopmentModuleAPI#Readings_.2F_Events|readings*update]]()-Funktionen,&lt;br /&gt;
* gibt den Namen der logischen Definition zurück, welche die Nachricht verarbeitet hat.&lt;br /&gt;
&lt;br /&gt;
Sollte keine passende Gerätedefinition für die entsprechende Nachricht existieren (Adresse/ID/Kanal/...), wird der Gerätename &amp;quot;UNDEFINED&amp;quot; inkl. einem passenden &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Statement zurückgegeben, um die Definition durch [[autocreate]] erzeugen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Es findet während der Verarbeitung einer Nachricht durch Dispatch()/Parse-Funktion keine sofortige Eventverarbeitung (via [[DevelopmentModuleAPI#Dispatch|DoTrigger()]]) statt, wenn die [[DevelopmentModuleAPI#Readings_.2F_Events|readings*update]]()-Funktionen verwendet werden.&lt;br /&gt;
(Im Gegensatz zum direkten Aufrufen der readings*update Funktionen ohne vorhergehendes Dispatch() )&lt;br /&gt;
&lt;br /&gt;
Die Funktion Dispatch() triggert das Event-Handling für das von der Parse-Funktion zurückgegebene logische Device selbstständig nach Abschluss der Parse-Funktion.&lt;br /&gt;
&lt;br /&gt;
Optional führt die Funktion Dispatch() eine Überprüfung auf Nachrichtenduplikate beim Einsatz von mehreren IO-Geräten durch. Dazu wird eine implementierte [[#X_Fingerprint|Fingerprint]]-Funktion im physischen oder logischen Modul benötigt. Sollte der Fingerprint einer Nachricht innerhalb einer bestimmten Zeit (globales Attribut &amp;lt;code&amp;gt;dupTimeout&amp;lt;/code&amp;gt;, standardmäßig 500ms) bereits empfangen worden sein, so wird die Nachricht verworfen. Dies ist insbesondere bei funkbasierter Hardware notwendig, wenn mehrere Empfänger die selbe Nachricht empfangen.&lt;br /&gt;
&lt;br /&gt;
=== Kommunikation von den logischen Modulen bis zur Hardware ===&lt;br /&gt;
&lt;br /&gt;
Um von einem logischen Modul eine Nachricht an die Hardware senden zu können, muss zunächst im logischen Gerät ein passenden IO-Gerät ausgewählt sein. Dazu muss die Funktion [[DevelopmentModuleAPI#AssignIoPort|AssignIoPort()]] ein entsprechendes IO-Gerät auswählen und in &amp;lt;code&amp;gt;$hash-&amp;gt;{IODev}&amp;lt;/code&amp;gt; setzen. Dieser Aufruf wird üblicherweise in der [[#X_Define|Define]]-Funktion des logischen Moduls ausgeführt. Erst, wenn ein IO-Gerät ausgewählt wurde, können Daten über das physikalische Gerät an die Hardware übermittelt werden.&lt;br /&gt;
&lt;br /&gt;
Zum Senden von Daten ruft das logische Modul die Funktion [[DevelopmentModuleAPI#IOWrite|IOWrite()]] samt Daten auf. Diese ruft für das entsprechende IO-Gerät die [[#X_Write|Write]]-Funktion auf und übergibt die Daten zum Schreiben an das physikalische Modul. Die Write-Funktion kümmert sich nun um die Übertragung der Daten an die Hardware. &lt;br /&gt;
&lt;br /&gt;
Keine direkten Zugriffe zwischen dem logischen und dem physischen Gerät gibt (d.h. keine direkten Aufrufe von Funktionen, kein direktes Überprüfen von &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;-Inhalten, ...), so können die Module hintereinander geschaltet werden (z.B. für Routerfunktionen wie bei der [[RFR_CUL|RFR]]-Funktionalität) oder mittels [[FHEM2FHEM]] im RAW-Modus zwei FHEM-Installationen verbunden werden und die logischen Geräte können dennoch kommunizieren.&lt;br /&gt;
&lt;br /&gt;
=== Automatisches Anlegen von logischen Gerätedefinitionen (autocreate) ===&lt;br /&gt;
&lt;br /&gt;
Das logische Modul kann im Rahmen der [[#X_Parse|Parse]]-Funktion eine neue Gerätedefinition anlegen, sofern eine passende Definition nicht existieren sollte. Die Parse-Funktion gibt generell den Namen der logischen Gerätedefinition zurück, für welche die Nachricht verarbeitet wurde. Sollte keine passende Definition gefunden werden, so muss die Parse-Funktion folgenden Rückgabewert liefern (zusammenhängende Zeichenkette):&lt;br /&gt;
&lt;br /&gt;
 UNDEFINED &#039;&#039;&amp;amp;lt;Namensvorschlag&amp;amp;gt;&#039;&#039; &#039;&#039;&amp;amp;lt;Modulname&amp;amp;gt;&#039;&#039; &#039;&#039;&amp;amp;lt;Define-Parameter&amp;amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Sollte also bspw. im Rahmen der Parse-Funktion zu Modul X eine Nachricht nicht einer existierenden Gerätedefinition zugeordnet werden können, so muss ein Namensvorschlag erstellt werden der eine eindeutige Komponente wie bspw. eine Adresse/ID/Kanal-Nr enthält. In der Regel wird hier immer der Modulname zusammen mit der eindeutigen Komponente, durch einen Unterstrich getrennt, verwendet (Bsp: &amp;lt;code&amp;gt;X_4834&amp;lt;/code&amp;gt;). Der Modulname ist in der Regel immer der, des eigenen Moduls. In besonderen Fällen kann man hier auch einen abweichenden Modulnamen angeben. Dies wird bspw. bei den [[PanStamp#FHEM-Module.2FDevice_Definition_Files|SWAP-Modulen]] eingesetzt. Als Define-Parameter müssen alle notwendigen Parameter angegeben werden, die beim Aufruf des &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Befehls notwendig sind, damit eine neu angelegte Gerätedefinition Nachrichten zu dieser eindeutigen Adresse Daten verarbeitet. Dazu muss mind. die eindeutige Adresse mitgegeben werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel für FS20:&lt;br /&gt;
&lt;br /&gt;
 UNDEFINED FS20_0ae42f8 FS20 0ae42 f8&lt;br /&gt;
&lt;br /&gt;
Sobald [[DevelopmentModuleAPI#Dispatch|Dispatch()]] einen solchen Rückgabewert von einer [[#X_Parse|Parse]]-Funktion erhält, wird diese Zeichenkette so wie sie ist via [[DevelopmentModuleAPI#DoTrigger|DoTrigger()]] als Event für die Definition &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; getriggert.&lt;br /&gt;
&lt;br /&gt;
Sofern der Nutzer das Modul [[autocreate]] verwendet (definiert hat), kümmert sich dieses nun um das Anlegen einer entsprechenden Gerätedefinition. Es lauscht dabei auf generierte Events der Definition &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; via [[#X_Notify|Notify]]-Funktion. Der Nutzer kann dabei das Verhalten von autocreate durch entsprechende Parameter beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Das Modul, für welches autocreate eine neue Definition anlegen möchte, kann das Verhalten durch entsprechende Parameter im Modul-Hash beeinflussen. Dabei gilt, dass gesetzte Attribute durch den Nutzer generell Vorrang haben. Die entsprechenden Parameter werden dabei im Rahmen der [[#X_Initialize|Initialize]]-Funktion im Modul-Hash gesetzt:&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;
	...&lt;br /&gt;
 &lt;br /&gt;
	$hash-&amp;gt;{AutoCreate} = {&amp;quot;X_.*&amp;quot;  =&amp;gt; { ATTR   =&amp;gt; &amp;quot;event-on-change-reading:.* event-min-interval:.*:300&amp;quot;,&lt;br /&gt;
	                                    FILTER =&amp;gt; &amp;quot;%NAME&amp;quot;,&lt;br /&gt;
	                                    GPLOT  =&amp;gt; &amp;quot;temp4hum4:Temp/Hum,&amp;quot;,&lt;br /&gt;
	                                    autocreateThreshold =&amp;gt; &amp;quot;2:140&amp;quot;&lt;br /&gt;
					  }&lt;br /&gt;
	                      };&lt;br /&gt;
			    &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei wird unterhalb von &amp;lt;code&amp;gt;$hash-&amp;gt;{AutoCreate}&amp;lt;/code&amp;gt; eine Liste angelegt, wo einem regulären Ausdruck für einen anzulegenden Definitionsnamen entsprechende Optionen zugeordnet werden. Sobald durch autocreate eine Gerätedefintion angelegt wird, auf den ein hier gelisteter Ausdruck matcht, so werden die zugeordneten Optionen berücksichtigt.&lt;br /&gt;
&lt;br /&gt;
Hier eine Auflistung aller möglichen Optionen und ihrer Bedeutung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Optionsname !! Beispiel !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;ATTR&amp;lt;/code&amp;gt;|| style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; |  &amp;lt;code&amp;gt;&amp;quot;event-on-change-reading:.* event-min-interval:.*:300&amp;quot;&amp;lt;/code&amp;gt; || Eine Auflistung von Attributen, die nach dem Anlegen einer Definition zusätzlich gesetzt werden. Es handelt sich hierbei um eine Leerzeichen-separierte Liste von Doppelpunkt-getrennten Tupels mit Attributname und -wert.&lt;br /&gt;
&lt;br /&gt;
Für das dargestellte Beispiel bedeutet dies, dass nach dem Anlegen der Definition folgende FHEM-Befehle zusätzlich ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
 attr &#039;&#039;&amp;amp;lt;Name&amp;amp;gt;&#039;&#039; event-on-change-reading .*&lt;br /&gt;
 attr &#039;&#039;&amp;amp;lt;Name&amp;amp;gt;&#039;&#039; event-min-interval .*:300&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;FILTER&amp;lt;/code&amp;gt; || style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; |  &amp;lt;code&amp;gt;&amp;quot;%NAME&amp;quot;&amp;lt;/code&amp;gt;|| Sofern in der autocreate-Definiton das Attribut &amp;lt;code&amp;gt;filelog&amp;lt;/code&amp;gt; entsprechend durch den Nutzer gesetzt ist, wird eine zugehörige FileLog-Definition angelegt. Diese Option setzt den dabei benutzten Filter-Regexp, der beim Anlegen der FileLog-Definition gesetzt wird. &lt;br /&gt;
&lt;br /&gt;
Dabei werden folgende Platzhalter durch die entsprechenden Werte der neu angelegten Gerätedefinition ersetzt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;%NAME&amp;lt;/code&amp;gt; - wird ersetzt durch den Definitionsnamen&lt;br /&gt;
* &amp;lt;code&amp;gt;%TYPE&amp;lt;/code&amp;gt; - wird ersetzt durch den Modulnamen&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; |  &amp;lt;code&amp;gt;GPLOT&amp;lt;/code&amp;gt; || style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;&amp;quot;temp4hum4:Temp/Hum,&amp;quot;&amp;lt;/code&amp;gt; || Sofern eine FileLog-Definition angelegt wurde, kann man weiterführend dazu eine passende SVG-Definition erzeugen um Daten aus dem erzeugten FileLog zu visualisieren. Ein typischer Fall sind hierbei Temperatursensoren, wo es sinnvoll sein kann, einen passenden SVG-Plot mit Temperatur/Luftfeuchtigkeit direkt anzulegen.&lt;br /&gt;
&lt;br /&gt;
Es handelt sich hierbei um eine kommaseparierte Auflistung von gplot-Dateinamen und optionalen Label-Texten durch einen Doppelpunkt getrennt. Im genannten Beispiel entspricht &amp;lt;code&amp;gt;temp4hum4&amp;lt;/code&amp;gt; der zu verwendenden GnuPlot-Datei und &amp;lt;code&amp;gt;Temp/Hum&amp;lt;/code&amp;gt; dem zu verwendenden Label ([[SVG]] Attribut &amp;lt;code&amp;gt;label&amp;lt;/code&amp;gt;). Das Label wird auch durch FileLog verwendet als Link-Text zum entsprechenden SVG Plot. Alternativ kann auch nur die entsprechende GnuPlot-Datei anegeben werden ohne Label. Für jede angegebene GnuPlot-Datei wird anschließend eine entsprechende SVG-Definition erzeugt mit der vorher erzeugten FileLog-Definition als Datenquelle.&lt;br /&gt;
&lt;br /&gt;
Der gesamte Inhalt der &amp;lt;code&amp;gt;GPLOT&amp;lt;/code&amp;gt;-Option wird beim Anlegen einer FileLog-Definition dem Attribut &amp;lt;code&amp;gt;logtype&amp;lt;/code&amp;gt; als Wert plus dem Text &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt; zugewiesen. Daher muss der Inhalt der Option &amp;lt;code&amp;gt;GPLOT&amp;lt;/code&amp;gt; immer mit einem Komma enden. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;autocreateThreshold&amp;lt;/code&amp;gt; || style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot;| &amp;lt;code&amp;gt;&amp;quot;2:10&amp;quot;&amp;lt;/code&amp;gt; || Definiert, wie viele Aufrufe (im Bsp: &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;) von autocreate innerhalb welcher Zeit (im Bsp: &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt; Sek.) stattfinden müssen, bevor die Gerätedefinition tatsächlich durch autocreate angelegt wird. Dadurch kann das ungewollte Anlegen von Geräten verhindert werden die tatsächlich nicht in Echt existieren. Aufgrund von Funkstörungen kann es durchaus zum ungewollten Anlegen einer Definition kommen. Diese Funktion lässt eine Definition erst zu wenn innerhalb einer vorgegeben Zeit eine Mindestzahl an Nachrichten eintrifft.&lt;br /&gt;
&lt;br /&gt;
Die erste Zahl stellt dabei die Mindestanzahl an Nachrichten dar. Die Zweite Zahl stellt die Zeit in Sekunden dar, in der die Mindestanzahl an Nachrichten erreicht werden muss um eine entsprechende Gerätedefinition anzulegen. &lt;br /&gt;
&lt;br /&gt;
Sofern diese Option nicht gesetzt ist, wird standardmäßig &amp;lt;code&amp;gt;2:60&amp;lt;/code&amp;gt; verwendet.&lt;br /&gt;
&lt;br /&gt;
Diese Option kann durch den Anwender über das Attribut &amp;lt;code&amp;gt;autocreateThreshold&amp;lt;/code&amp;gt; übersteuert werden.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;noAutocreatedFilelog&amp;lt;/code&amp;gt;|| style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; |  &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;|| Flag. Sofern gesetzt, wird keine FileLog- und ggf. SVG-Definition erzeugt. Selbst wenn der Nutzer durch entsprechende Attribute das Anlegen wünscht. Diese Option ist sinnvoll für Module bzw. Geräte die keine Readings erzeugen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Ergänzende Hinweise ==&lt;br /&gt;
Die Wahl der vorangestellten Nummer für den Dateinamen eines neuen Moduls hat keine Bedeutung mehr, es sei denn die Nummer ist 99. Module, die mit 99_ beginnen, werden von FHEM automatisch geladen. Module mit einer anderen Nummer nur wenn ein &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Befehl dafür sorgt, dass das Modul geladen wird.&lt;br /&gt;
&lt;br /&gt;
Wenn ein Modul Initialisierungsdaten benötigt, sollten diese im Modul selbst enthalten sein. Eine zusätzliche Datei oder sogar ein Unterverzeichnis mit mehreren Dateien ist bei FHEM nicht üblich und sollte bei Modulen, die mit FHEM ausgeliefert werden nur in Rücksprache mit Rudolf König angelegt werden, da sie sonst bei einem Update nicht verteilt werden.&lt;br /&gt;
&lt;br /&gt;
== Weitere Informationen ==&lt;br /&gt;
Wenn man weitere Details wissen möchte, ist ein erster sinnvoller Schritt ein Blick in die Datei fhem.pl. Dort sieht man im Perl-Code wie die Module aufgerufen werden, was vorher passiert und was danach. Am Anfang der Datei (ca. ab Zeile 130) findet man beispielsweise eine Liste der globalen Variablen, die den Modulen zur Verfügung stehen sowie Details zu den wichtigen Hashes %modules und %defs. Wer mit Perl noch nicht so gut klar kommt, dem hilft eventuell ein Blick auf die Perldoc Website[http://perldoc.perl.org/] oder in das Perl-Buch seiner Wahl. Auch die FHEM {{Link2CmdRef}} sollte nicht unterschätzt werden. Es stehen oft mehr interessante Details auch für Modulentwickler darin als man zunächst vermuten könnte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Hello World&amp;quot; Beispiel ==&lt;br /&gt;
&lt;br /&gt;
98_Hello.pm&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
package main;&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
&lt;br /&gt;
my %Hello_gets = (&lt;br /&gt;
	&amp;quot;whatyouwant&amp;quot;	=&amp;gt; &amp;quot;can&#039;t&amp;quot;,&lt;br /&gt;
	&amp;quot;whatyouneed&amp;quot;	=&amp;gt; &amp;quot;try sometimes&amp;quot;,&lt;br /&gt;
	&amp;quot;satisfaction&amp;quot;  =&amp;gt; &amp;quot;no&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
sub Hello_Initialize($) {&lt;br /&gt;
    my ($hash) = @_;&lt;br /&gt;
&lt;br /&gt;
    $hash-&amp;gt;{DefFn}      = &#039;Hello_Define&#039;;&lt;br /&gt;
    $hash-&amp;gt;{UndefFn}    = &#039;Hello_Undef&#039;;&lt;br /&gt;
    $hash-&amp;gt;{SetFn}      = &#039;Hello_Set&#039;;&lt;br /&gt;
    $hash-&amp;gt;{GetFn}      = &#039;Hello_Get&#039;;&lt;br /&gt;
    $hash-&amp;gt;{AttrFn}     = &#039;Hello_Attr&#039;;&lt;br /&gt;
    $hash-&amp;gt;{ReadFn}     = &#039;Hello_Read&#039;;&lt;br /&gt;
&lt;br /&gt;
    $hash-&amp;gt;{AttrList} =&lt;br /&gt;
          &amp;quot;formal:yes,no &amp;quot;&lt;br /&gt;
        . $readingFnAttributes;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub Hello_Define($$) {&lt;br /&gt;
    my ($hash, $def) = @_;&lt;br /&gt;
    my @param = split(&#039;[ \t]+&#039;, $def);&lt;br /&gt;
    &lt;br /&gt;
    if(int(@param) &amp;lt; 3) {&lt;br /&gt;
        return &amp;quot;too few parameters: define &amp;lt;name&amp;gt; Hello &amp;lt;greet&amp;gt;&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    my $hash-&amp;gt;{name}  = $param[0];&lt;br /&gt;
    my $hash-&amp;gt;{greet} = $param[2];&lt;br /&gt;
    &lt;br /&gt;
    return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub Hello_Undef($$) {&lt;br /&gt;
    my ($hash, $arg) = @_; &lt;br /&gt;
    # nothing to do&lt;br /&gt;
    return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub Hello_Get($@) {&lt;br /&gt;
	my ($hash, @param) = @_;&lt;br /&gt;
	&lt;br /&gt;
	return &#039;&amp;quot;get Hello&amp;quot; needs at least one argument&#039; if (int(@param) &amp;lt; 2);&lt;br /&gt;
	&lt;br /&gt;
	my $name = shift @param;&lt;br /&gt;
	my $opt = shift @param;&lt;br /&gt;
	if(!$Hello_gets{$opt}) {&lt;br /&gt;
		my @cList = keys %Hello_gets;&lt;br /&gt;
		return &amp;quot;Unknown argument $opt, choose one of &amp;quot; . join(&amp;quot; &amp;quot;, @cList);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	if($attr{$name}{formal} eq &#039;yes&#039;) {&lt;br /&gt;
	    return $Hello_gets{$opt}.&#039;, sir&#039;;&lt;br /&gt;
    }&lt;br /&gt;
	return $Hello_gets{$opt};&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub Hello_Set($@) {&lt;br /&gt;
	my ($hash, @param) = @_;&lt;br /&gt;
	&lt;br /&gt;
	return &#039;&amp;quot;set Hello&amp;quot; needs at least one argument&#039; if (int(@param) &amp;lt; 2);&lt;br /&gt;
	&lt;br /&gt;
	my $name = shift @param;&lt;br /&gt;
	my $opt = shift @param;&lt;br /&gt;
	my $value = join(&amp;quot;&amp;quot;, @param);&lt;br /&gt;
	&lt;br /&gt;
	if(!defined($Hello_gets{$opt})) {&lt;br /&gt;
		my @cList = keys %Hello_gets;&lt;br /&gt;
		return &amp;quot;Unknown argument $opt, choose one of &amp;quot; . join(&amp;quot; &amp;quot;, @cList);&lt;br /&gt;
	}&lt;br /&gt;
    $hash-&amp;gt;{STATE} = $Hello_gets{$opt} = $value;&lt;br /&gt;
    &lt;br /&gt;
	return &amp;quot;$opt set to $value. Try to get it.&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub Hello_Attr(@) {&lt;br /&gt;
	my ($cmd,$name,$attr_name,$attr_value) = @_;&lt;br /&gt;
	if($cmd eq &amp;quot;set&amp;quot;) {&lt;br /&gt;
        if($attr_name eq &amp;quot;formal&amp;quot;) {&lt;br /&gt;
			if($attr_value !~ /^yes|no$/) {&lt;br /&gt;
			    my $err = &amp;quot;Invalid argument $attr_value to $attr_name. Must be yes or no.&amp;quot;;&lt;br /&gt;
			    Log 3, &amp;quot;Hello: &amp;quot;.$err;&lt;br /&gt;
			    return $err;&lt;br /&gt;
			}&lt;br /&gt;
		} else {&lt;br /&gt;
		    return &amp;quot;Unknown attr $attr_name&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
=pod&lt;br /&gt;
=begin html&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;Hello&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;Hello&amp;lt;/h3&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
    &amp;lt;i&amp;gt;Hello&amp;lt;/i&amp;gt; implements the classical &amp;quot;Hello World&amp;quot; as a starting point for module development. &lt;br /&gt;
    You may want to copy 98_Hello.pm to start implementing a module of your very own. See &lt;br /&gt;
    &amp;lt;a href=&amp;quot;http://wiki.fhem.de/wiki/DevelopmentModuleIntro&amp;quot;&amp;gt;DevelopmentModuleIntro&amp;lt;/a&amp;gt; for an &lt;br /&gt;
    in-depth instruction to your first module.&lt;br /&gt;
    &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
    &amp;lt;a name=&amp;quot;Hellodefine&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Define&amp;lt;/b&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
        &amp;lt;code&amp;gt;define &amp;amp;lt;name&amp;amp;gt; Hello &amp;amp;lt;greet&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
        Example: &amp;lt;code&amp;gt;define HELLO Hello TurnUrRadioOn&amp;lt;/code&amp;gt;&lt;br /&gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
        The &amp;quot;greet&amp;quot; parameter has no further meaning, it just demonstrates&lt;br /&gt;
        how to set a so called &amp;quot;Internal&amp;quot; value. See &amp;lt;a href=&amp;quot;http://fhem.de/commandref.html#define&amp;quot;&amp;gt;commandref#define&amp;lt;/a&amp;gt; &lt;br /&gt;
        for more info about the define command.&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
    &amp;lt;br&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;a name=&amp;quot;Helloset&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Set&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
        &amp;lt;code&amp;gt;set &amp;amp;lt;name&amp;amp;gt; &amp;amp;lt;option&amp;amp;gt; &amp;amp;lt;value&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
        You can &amp;lt;i&amp;gt;set&amp;lt;/i&amp;gt; any value to any of the following options. They&#039;re just there to &lt;br /&gt;
        &amp;lt;i&amp;gt;get&amp;lt;/i&amp;gt; them. See &amp;lt;a href=&amp;quot;http://fhem.de/commandref.html#set&amp;quot;&amp;gt;commandref#set&amp;lt;/a&amp;gt; &lt;br /&gt;
        for more info about the set command.&lt;br /&gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
        Options:&lt;br /&gt;
        &amp;lt;ul&amp;gt;&lt;br /&gt;
              &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;satisfaction&amp;lt;/i&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
                  Defaults to &amp;quot;no&amp;quot;&amp;lt;/li&amp;gt;&lt;br /&gt;
              &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;whatyouwant&amp;lt;/i&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
                  Defaults to &amp;quot;can&#039;t&amp;quot;&amp;lt;/li&amp;gt;&lt;br /&gt;
              &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;whatyouneed&amp;lt;/i&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
                  Defaults to &amp;quot;try sometimes&amp;quot;&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;/ul&amp;gt;&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
    &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;a name=&amp;quot;Helloget&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Get&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
        &amp;lt;code&amp;gt;get &amp;amp;lt;name&amp;amp;gt; &amp;amp;lt;option&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
        You can &amp;lt;i&amp;gt;get&amp;lt;/i&amp;gt; the value of any of the options described in &lt;br /&gt;
        &amp;lt;a href=&amp;quot;#Helloset&amp;quot;&amp;gt;paragraph &amp;quot;Set&amp;quot; above&amp;lt;/a&amp;gt;. See &lt;br /&gt;
        &amp;lt;a href=&amp;quot;http://fhem.de/commandref.html#get&amp;quot;&amp;gt;commandref#get&amp;lt;/a&amp;gt; for more info about &lt;br /&gt;
        the get command.&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
    &amp;lt;br&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;a name=&amp;quot;Helloattr&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Attributes&amp;lt;/b&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
        &amp;lt;code&amp;gt;attr &amp;amp;lt;name&amp;amp;gt; &amp;amp;lt;attribute&amp;amp;gt; &amp;amp;lt;value&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
        See &amp;lt;a href=&amp;quot;http://fhem.de/commandref.html#attr&amp;quot;&amp;gt;commandref#attr&amp;lt;/a&amp;gt; for more info about &lt;br /&gt;
        the attr command.&lt;br /&gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
        Attributes:&lt;br /&gt;
        &amp;lt;ul&amp;gt;&lt;br /&gt;
            &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;formal&amp;lt;/i&amp;gt; no|yes&amp;lt;br&amp;gt;&lt;br /&gt;
                When you set formal to &amp;quot;yes&amp;quot;, all output of &amp;lt;i&amp;gt;get&amp;lt;/i&amp;gt; will be in a&lt;br /&gt;
                more formal language. Default is &amp;quot;no&amp;quot;.&lt;br /&gt;
            &amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;/ul&amp;gt;&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=end html&lt;br /&gt;
&lt;br /&gt;
=cut&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der HTML-Code zwischen den Tags &amp;lt;code&amp;gt;=pod&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;=cut&amp;lt;/code&amp;gt; dient zur Generierung der commandref.html. Der HTML-Inhalt wird automatisch beim Verteilen des Moduls im Rahmen des Update-Mechanismus aus jedem Modul extrahiert und daraus die Commandref in verschiedenen Sprachen erstellt. Eine detaillierte Beschreibung wie ein Commandref-Abschnitt in einem Modul definiert wird, siehe: [[Guidelines zur Dokumentation]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevelopmentModuleIntro&amp;diff=26266</id>
		<title>DevelopmentModuleIntro</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevelopmentModuleIntro&amp;diff=26266"/>
		<updated>2018-03-25T11:47:54Z</updated>

		<summary type="html">&lt;p&gt;Eisler: /* X_Set */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Hinweis|Dieser Text ist in Arbeit und muss noch an einigen Stellen ergänzt werden. }}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Einleitung ==&lt;br /&gt;
&lt;br /&gt;
Um neue Geräte, Dienste, o.ä. in FHEM verfügbar zu machen, kann man ein eigenes Modul in Perl schreiben. Ein Modul wird in FHEM automatisch geladen, wenn ein entsprechendes Device in FHEM definiert wird. Das Modul ermöglicht eine spezifische Kommunikation mit einem physikalischen Gerät, stellt Ergebnisse (&amp;quot;Readings&amp;quot;) und Events innerhalb von FHEM zur Verfügung und erlaubt es, das Gerät mit &amp;quot;Set&amp;quot;-/&amp;quot;Get&amp;quot;-Befehlen zu beeinflussen. Dieser Artikel soll den Einstieg in die Entwicklung eigener Module erleichtern.&lt;br /&gt;
&lt;br /&gt;
Mit dem FHEM-Befehl &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt; werden Devices in FHEM basierend auf einem Modul definiert. Dieser Befehl sorgt dafür, dass ein neues Modul bei Bedarf geladen und initialisiert wird. Ein gutes Beispiel ist hierbei die zentrale Konfigurationsdatei &amp;quot;fhem.cfg&amp;quot; in der sämtliche Devices in Form von &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Statements gespeichert sind.&lt;br /&gt;
&lt;br /&gt;
Damit das funktioniert müssen der Name des Moduls und der Name der [[#X_Initialize|Initialisierungsfunktion]]  identisch sein. Das folgende Beispiel soll dies verdeutlichen:&lt;br /&gt;
&lt;br /&gt;
Ein Jeelink USB-Stick könnte beispielsweise mit dem Befehl &amp;lt;code&amp;gt;define JeeLink1 &#039;&#039;JeeLink&#039;&#039; /dev/ttyUSB0@57600&amp;lt;/code&amp;gt; definiert werden.&lt;br /&gt;
&lt;br /&gt;
In fhem.pl wird der define-Befehl verarbeitet und geprüft, ob ein Modul mit dem Namen &amp;quot;JeeLink&amp;quot; schon geladen ist und falls nicht ein Modul mit Namen XY_JeeLink.pm im Modulverzeichnis (z.B. /opt/fhem/FHEM) gesucht und dann geladen. &lt;br /&gt;
Danach wird die Funktion &amp;lt;code&amp;gt;&#039;&#039;JeeLink&#039;&#039;_Initialize()&amp;lt;/code&amp;gt; aufgerufen um das Modul in FHEM zu registrieren. Eine Moduldatei muss dazu eine Funktion &amp;lt;code&amp;gt;&#039;&#039;&amp;amp;lt;Modulname&amp;amp;gt;&#039;&#039;_Initialize()&amp;lt;/code&amp;gt; enthalten. Durch den Aufruf dieser Funktion wird FHEM mitgeteilt, welche Funktionalitäten dieses Modul unterstützt.&lt;br /&gt;
&lt;br /&gt;
In der Initialisierungsfunktion des Moduls werden die Namen aller weiteren Perl-Funktionen des Moduls, die von fhem.pl aus aufgerufen werden, bekannt gemacht. Dazu wird für jedes Modul ein eigener Hash (genauer &amp;quot;Modul-Hash&amp;quot;) mit entsprechenden Werten gefüllt, der in fhem.pl für jedes Modul entsprechend abgelegt wird. Dadurch weiß FHEM wie dieses Modul anzusprechen ist.&lt;br /&gt;
&lt;br /&gt;
== Grundlegender Aufbau eines Moduls ==&lt;br /&gt;
&lt;br /&gt;
=== Dateiname ===&lt;br /&gt;
&lt;br /&gt;
Ein FHEM-Modul wird als Perl-Modul mit der Dateiendung *.pm abgespeichert. Der Dateiname folgt dabei folgendem Schema:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;&#039;&#039;[&#039;&#039;&#039;Schlüsselnummer&#039;&#039;&#039;]&#039;&#039;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;_&amp;lt;/font&amp;gt;&#039;&#039;[&#039;&#039;&#039;Modulname&#039;&#039;&#039;]&#039;&#039;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;.pm&amp;lt;/font&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Schlüsselnummer&#039;&#039;&#039; - Eine zweistellige Zahl zwischen 00 - 99. Die Schlüsselnummer hat aktuell keine technische Relevanz mehr. In früheren FHEM-Versionen ist sie relevant für [[#Zweistufiges_Modell_f.C3.BCr_Module|zweistufige Module]] (Reihenfolge für [[DevelopmentModuleAPI#Dispatch|Dispatch()]] um logische Module zu prüfen). Die allgemeine Empfehlung ist hierbei eine Schlüsselnummer eines Moduls zu verwenden, welches eine ähnliche Funktionalität bietet. Die Schlüsselnummer 99 hat hierbei eine besondere Bedeutung, da alle Module mit dieser Schlüsselnummer beim Start von FHEM automatisch geladen werden, selbst, wenn sie in der Konfiguration nicht verwendet werden. Daher wird für myUtils 99 als Schlüsselnummer verwendet (99_myUtils.pm). Module mit der Schlüsselnummer 99 werden im SVN nicht akzeptiert (siehe [[SVN Nutzungsregeln]])&lt;br /&gt;
* &#039;&#039;&#039;Modulname&#039;&#039;&#039; - Der Name des Moduls wie er in FHEM bei dem Anlegen einer Gerätedefinition zu verwenden ist. Der Modulname sollte nur aus den folgenden möglichen Zeichen bestehen: Groß-/Kleinbuchstaben, Zahlen sowie Unterstrich (_)&lt;br /&gt;
&lt;br /&gt;
=== Inhaltlicher Aufbau ===&lt;br /&gt;
&lt;br /&gt;
Ein Modul ist inhaltlich in folgende Abschnitte unterteilt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
#&lt;br /&gt;
#  72_MYMODULE.pm &lt;br /&gt;
#&lt;br /&gt;
&lt;br /&gt;
package main;&lt;br /&gt;
&lt;br /&gt;
# Laden evtl. abhängiger Perl- bzw. FHEM-Module&lt;br /&gt;
use HttpUtils;&lt;br /&gt;
use [...]&lt;br /&gt;
&lt;br /&gt;
# FHEM Modulfunktionen&lt;br /&gt;
&lt;br /&gt;
sub MYMODULE_Initialize() {&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub MYMODULE_Define() {&lt;br /&gt;
   ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
# Eval-Rückgabewert für erfolgreiches&lt;br /&gt;
# Laden des Moduls&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# Beginn der Commandref&lt;br /&gt;
&lt;br /&gt;
=pod&lt;br /&gt;
=item [helper|device|command]&lt;br /&gt;
=item summary Kurzbeschreibung in Englisch was MYMODULE steuert/unterstützt&lt;br /&gt;
=item summary_DE Kurzbeschreibung in Deutsch was MYMODULE steuert/unterstützt&lt;br /&gt;
&lt;br /&gt;
=begin html&lt;br /&gt;
 Englische Commandref in HTML&lt;br /&gt;
=end html&lt;br /&gt;
&lt;br /&gt;
=begin html_DE&lt;br /&gt;
 Deustche Commandref in HTML&lt;br /&gt;
=end html&lt;br /&gt;
&lt;br /&gt;
# Ende der Commandref&lt;br /&gt;
=cut&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man kann hierbei von folgender Reihenfolge sprechen:&lt;br /&gt;
&lt;br /&gt;
# Perl-Code, welcher das Modul implementiert&lt;br /&gt;
# Die Zeile &amp;lt;code&amp;gt;1;&amp;lt;/code&amp;gt; nachdem der Perl-Code abgeschlossen ist. Dies dient FHEM der Erkennung, dass das Modul erfolgreich und vollständig geladen wurde. Sollte diese Zeile nicht enthalten sein, wird FHEM beim Laden des Moduls die Fehlermeldung &amp;lt;code&amp;gt;Error:Modul 72_MYMODULE deactivated&amp;lt;/code&amp;gt; in das Logfile schreiben.&lt;br /&gt;
# Commandref zur Dokumentation des Moduls. Diese Dokumentation soll dem User die möglichen Befehle/Attribute/Readings/Events vermitteln. Weitere Informationen und Hinweise findet man in den [[Guidelines zur Dokumentation]].&lt;br /&gt;
&lt;br /&gt;
== Der Hash einer Geräteinstanz ==&lt;br /&gt;
Eine Besonderheit in Perl sind [http://de.wikipedia.org/wiki/Assoziatives_Array#Perl assoziative Arrays], (nicht ganz richtig als &amp;quot;Hash&amp;quot; bezeichnet) in denen die Adressierung nicht über eine Zählvariable erfolgt, sondern über einen beliebigen String. Die internen Abläufe bei der Adressierung führen dazu, dass die Speicherung in und der Abruf aus Hashes relativ langsam ist.&lt;br /&gt;
&lt;br /&gt;
Der zentrale Speicherort für Informationen einer Geräteinstanz bei FHEM ist ein solcher Hash, der seinerseits in fhem.pl von einem globalen Hash referenziert wird. &lt;br /&gt;
&lt;br /&gt;
In fhem.pl werden alle Gerätedefinitionen in dem globalen Hash &amp;lt;code&amp;gt;%defs&amp;lt;/code&amp;gt; abgelegt. Der Inhalt von &amp;lt;code&amp;gt;$defs{&#039;&#039;&amp;amp;lt;Name&amp;amp;gt;&#039;&#039;}&amp;lt;/code&amp;gt; in fhem.pl verweist dabei auf den Hash der Geräteinstanz in Form einer Hashreferenz. Diesen Verweis (also nur die Adresse) bekommen die Funktionen eines Moduls übergeben (i.d.R. als &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; bezeichnet), welche direkt von fhem.pl aufgerufen werden. In dem Hash stehen beispielsweise die internen Werte des Geräts, die im Frontend als &amp;quot;Internals&amp;quot; angezeigt werden, sowie die Readings des Geräts. &lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
*&amp;lt;code&amp;gt;$hash-&amp;gt;{NAME}&amp;lt;/code&amp;gt; enthält den Namen der Geräteinstanz, &lt;br /&gt;
*&amp;lt;code&amp;gt;$hash-&amp;gt;{TYPE}&amp;lt;/code&amp;gt; enthält die Typbezeichnung des Geräts (Modulname)&lt;br /&gt;
&lt;br /&gt;
==Ausführung von Modulen==&lt;br /&gt;
FHEM arbeitet intern nicht parallel, sondern arbeitet alle Aufgaben seriell nacheinander kontinuierlich ab. Daher wäre es ungünstig, wenn Module Daten von einem physikalischen Gerät abfragen wollen und dabei innerhalb der selben Funktion auf die Antwort des Geräts warten. In dieser Zeit, in der FHEM auf die Antwort des Gerätes warten muss, wäre der Rest von FHEM blockiert. Da immer nur eine Aufgabe zur selben Zeit bearbeitet wird, müssen alle weiteren Aufgaben solange warten. Eine Datenkommunikation innerhalb eines Moduls sollte daher immer ohne Blockierung erfolgen. Dadurch kann FHEM die Wartezeit effizient für andere Aufgaben nutzen um bspw. anstehende Daten für andere Module zu verarbeiten. Es gibt in FHEM entsprechende Mechanismen, welche eine &amp;quot;Non-Blocking&amp;quot;-Kommunikation über verschiedene Wege (z.B. seriell, HTTP, TCP, ...) ermöglichen.&lt;br /&gt;
&lt;br /&gt;
Dafür werden in FHEM zwei zentrale Listen gepflegt, in der die Filedeskriptoren der geöffneten Kommunikatonsverbindungen gespeichert sind. Auf Linux- bzw. Unix-basierten Plattformen wird der select-Befehl des Betriebssystems verwendet um Filedeskriptoren auf lesbare Daten zu überprüfen. In FHEM gibt es dazu eine Liste (&amp;lt;code&amp;gt;%selectlist&amp;lt;/code&amp;gt;), in der die Filedeskriptoren sämtlicher Geräte (z.B. serielle Verbindung, TCP-Verbindung, etc.) gespeichert sind. &lt;br /&gt;
&lt;br /&gt;
In der zentralen Schleife (Main-Loop) von fhem.pl wird mit &amp;lt;code&amp;gt;select()&amp;lt;/code&amp;gt; überwacht, ob über eine der geöffneten Schnittstellen Daten zum Lesen anstehen. Wenn dies der Fall ist, dann wird die Lesefunktion ([[#X_Read|X_Read]]) des zuständigen Moduls aufgerufen, damit es die Daten entgegennimmt und verarbeitet. Anschließend wird die Schleife weiter ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Auf Windows-Systemen funktioniert dies anders. Hier können USB/Seriell-Geräte nicht per &amp;lt;code&amp;gt;select()&amp;lt;/code&amp;gt; überwacht werden. In FHEM unter Windows werden daher diese Schnittstellen kontinuierlich abgefragt ob Daten bereitstehen. Dafür müssen Module zusätzlich zur Lesefunktion eine Abfragefunktion ([[#X_Ready|X_Ready]]) implementieren, welche prüft, ob Daten zum Lesen anstehen. Auch auf Linux/Unix-Plattformen hat diese Funktion eine Aufgabe. Falls nämlich eine Schnittstelle ausfällt, beziehungsweise ein CUL oder USB-zu-Seriell Adapter ausgesteckt wird, dann wird über diese Funktion regelmäßig geprüft ob die Schnittstelle wieder verfügbar wird.&lt;br /&gt;
&lt;br /&gt;
Innerhalb der eigentlichen Lesefunktion (X_Read) werden dann die Daten vom zugehörigen Gerät gelesen, das nötige Protokoll implementiert um die Daten zu interpretieren und Werte in Readings geschrieben.&lt;br /&gt;
&lt;br /&gt;
Auch wenn von einem Anwender über einen Get-Befehl Daten aktiv von einem Gerät angefordert werden, sollte nicht blockierend gewartet werden. Eine asynchrone Ausgabe, sobald das Ergebnis vorliegt, ist über [[DevelopmentModuleAPI#asyncOutput|asyncOutput()]] möglich. Siehe {{Link2Forum|Topic=43771|Message=357870|LinkText=Beschreibung}} und {{Link2Forum|Topic=43771|Message=360935|LinkText=Beispiel}}. Weitere Anwendungsbeispiele finden sich im  {{Link2Forum|Topic=43052|Message=353477|LinkText=PLEX Modul}} und im überarbeiteten und nicht-blockierenden {{Link2Forum|Topic=42771|Message=348498|LinkText=SYSSTAT Modul}}.&lt;br /&gt;
&lt;br /&gt;
== Wichtige globale Variablen aus fhem.pl ==&lt;br /&gt;
&lt;br /&gt;
FHEM arbeitet mit einer Vielzahl an internen Variablen. Die nun folgenden aufgelisteten Variablen sind die wichtigsten, welche man im Rahmen der Modulprogrammierung kennen sollte:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variable !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; || Dient der Erkennung für fhem.pl sowie den Modulen, ob FHEM den Initialisierungsvorgang abgeschlossen hat. Beim Starten von FHEM ist &amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; gleich &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. Erst, wenn das Einlesen der Konfiguration, sowie des State-Files (Readings) abgeschlossen ist, wird &amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; auf &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; gesetzt.&lt;br /&gt;
Das gleiche Verfahren wird auch bei dem Befehl &amp;lt;code&amp;gt;rereadcfg&amp;lt;/code&amp;gt; angewandt. Während &amp;lt;code&amp;gt;rereadcfg&amp;lt;/code&amp;gt; ausgeführt wird (Konfiguration löschen, neu einlesen), ist &amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; gleich &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Dies ist insbesondere in der [[#X_Define|Define]]-Funktion eines Moduls relevant. Durch eine Prüfung auf &amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; kann man erkennen, ob eine Definition von Hand (&amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; = 1) oder im Rahmen der Initialisierung (FHEM Start / Rereadcfg =&amp;gt; &amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; = 0) erfolgte. Während der Initialisierung stehen bspw. die gesetzten Attribute der Definition noch nicht zur Verfügung und können daher nicht ausgewertet werden (siehe . &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;%attr&amp;lt;/code&amp;gt; || In &amp;lt;code&amp;gt;%attr&amp;lt;/code&amp;gt; werden sämtliche gesetzten Attribute aller Geräte gespeichert. Diese Datenstruktur wird generell durch den &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt;-Befehl verwaltet. Hierbei wird einem Gerätenamen eine Mehrzahl an Attributnamen mit einem Wert zugeordnet. Attribut-Inhalte können über die Funktion [[DevelopmentModuleAPI#AttrVal|AttrVal()]] ausgelesen werden.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;%cmds&amp;lt;/code&amp;gt; || In &amp;lt;code&amp;gt;%cmds&amp;lt;/code&amp;gt; wird jedem in FHEM existierendem Befehl die entsprechende Funktion zugewiesen, welche diesen Befehl umsetzt. Module können durch das Eintragen eines Befehlsnamen samt Funktion in &amp;lt;code&amp;gt;%cmds&amp;lt;/code&amp;gt; über die [[#X_Initialize|Initialize]]-Funktion eines Moduls einen (oder mehrere) eigene Befehle in FHEM registrieren.&lt;br /&gt;
&lt;br /&gt;
Die Struktur ist dabei wiefolgt:&lt;br /&gt;
&lt;br /&gt;
  $cmds{&#039;&#039;&amp;amp;lt;Befehlsname&amp;amp;gt;&#039;&#039;} = {  Fn  =&amp;gt; &amp;quot;&#039;&#039;&amp;amp;lt;Funktionsname&amp;amp;gt;&#039;&#039;&amp;quot;,&lt;br /&gt;
                            Hlp =&amp;gt; &amp;quot;&#039;&#039;&amp;amp;lt;Aufrufsyntax&amp;amp;gt;&#039;&#039;&amp;quot;};&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;%data&amp;lt;/code&amp;gt;|| Der eigentliche Zweck von &amp;lt;code&amp;gt;%data&amp;lt;/code&amp;gt; ist dem Nutzer eine Möglichkeit zum Speichern von temporären Daten im globalen Kontext zu ermöglichen. Einige Module verwenden &amp;lt;code&amp;gt;%data&amp;lt;/code&amp;gt; jedoch auch um modul- &amp;amp; geräteübergreifend Daten auszutauschen.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;%defs&amp;lt;/code&amp;gt;|| In &amp;lt;code&amp;gt;%defs&amp;lt;/code&amp;gt; werden sämtliche Gerätedefinitionen, bzw. die Hash-Referenzen auf diese, gespeichert. Hier ist jedem Gerätenamen eine Hash-Referenz zugeordnet.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;%modules&amp;lt;/code&amp;gt;|| In &amp;lt;code&amp;gt;%modules&amp;lt;/code&amp;gt; sind alle geladenen Module gelistet mit ihren entsprechenden Initialisierungsdaten (Funktionsnamen, Attribut-Listen, spezielle Einstellungen, ...). Hier wird für jeden Modulname der Modul-Hash aus der [[#X_Initialize|Initialize]]-Funktion gespeichert. &lt;br /&gt;
Desweiteren legen viele Module, welche nach dem [[DevelopmentModuleIntro#Zweistufiges_Modell_f.C3.BCr_Module|zweistufigen Modulkonzept]] hier eine Rückwärtszuordnung von Geräteadressen zu Geräte-Hash an.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;%readyfnlist&amp;lt;/code&amp;gt;|| In &amp;lt;code&amp;gt;%readyfnlist&amp;lt;/code&amp;gt; sind alle zu prüfenden Verbindungen mit ihrer entsprechendem Geräte-Hash gelistet. FHEM prüft alle hier gelisteten Geräte regelmäßig über eine Aufruf der entsprechenden [[#X_Ready|Ready]]-Funktion.&lt;br /&gt;
&lt;br /&gt;
Bei einer Nutzung von dem Hilfsmodul [[DevIo|DevIo.pm]] zum Aufbau einer Kommunikationsverbindung, kümmert sich DevIo selbständig um den entsprechenden Eintrag in &amp;lt;code&amp;gt;%readyfnlist&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;%selectlist&amp;lt;/code&amp;gt;|| In &amp;lt;code&amp;gt;%selectlist&amp;lt;/code&amp;gt; sind alle geöffneten Verbindungen mit ihrer entsprechendem Geräte-Hash gelistet. FHEM prüft alle hier gelisteten Geräte, ob der geöffnete Filedeskriptor unter &amp;lt;code&amp;gt;$hash-&amp;gt;{FD}&amp;lt;/code&amp;gt; Daten zum Lesen bereitgestellt hat. Ist dass der Fall, wird die entsprechende [[#X_Read|Read]]-Funktion aufgerufen, um anstehende Daten durch das Modul zu verarbeiten.&lt;br /&gt;
Bei einer Nutzung von dem Hilfsmodul [[DevIo|DevIo.pm]] zum Aufbau einer Kommunikationsverbindung, kümmert sich DevIo selbständig um den entsprechenden Eintrag in &amp;lt;code&amp;gt;%selectlist&amp;lt;/code&amp;gt;.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Es gibt durchaus viele weitere globale Variablen, die jedoch für sehr spezielle Anwendungsfälle und z.T. nur einzelne Module gedacht sind und daher hier nicht aufgeführt werden.&lt;br /&gt;
&lt;br /&gt;
== Internals ==&lt;br /&gt;
Daten, die ein Modul im Geräte-Hash speichert nennt man Internals. Sie werden als Unterstruktur des Hashes der jeweiligen Geräteinstanz gespeichert, beispielswiese &amp;lt;code&amp;gt;$hash-&amp;gt;{NAME}&amp;lt;/code&amp;gt; für den Gerätenamen, welcher beim Define-Befehl übergeben wurde und als Internal gespeichert wird. Diese Daten spielen für FHEM eine sehr wichtige Rolle, da sämtliche gerätespezifischen Daten als Internal im Gerätehash gespeichert werden.&lt;br /&gt;
&lt;br /&gt;
Falls Werte wie z.B. ein Intervall nicht über den Define-Befehl gesetzt werden sollen und im Betrieb einfach änderbar sein sollten, ist eine alternative Möglichkeit die Speicherung in so genannten Attributen. Dann würde man den Define-Befehl so implementieren, dass er kein Intervall übergeben bekommt und statt dessen das Interval als Attribut über den Befehl &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt; gesetzt wird.&lt;br /&gt;
&lt;br /&gt;
Generell werden alle Werte, welche direkt in der ersten Ebene von &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; (Gerätehash) gespeichert werden auf der Detail-Seite einer Definition in der FHEMWEB Oberfläche angezeigt. Es gibt jedoch Ausnahmen:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;$hash-&amp;gt;{helper}{URL}&amp;lt;/code&amp;gt; - Alle Elemente, welche als Unterelement wieder einen Hash besitzen werden nicht in FHEMWEB dargestellt. Typischerweise speichern Module Daten unter &amp;lt;code&amp;gt;$hash-&amp;gt;{helper}&amp;lt;/code&amp;gt; interne Daten zwischen, die für den User nicht relevant sind, sondern nur der internen Verarbeitung dienen.&lt;br /&gt;
* &amp;lt;code&amp;gt;$hash-&amp;gt;{&#039;&#039;&#039;.&#039;&#039;&#039;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;ELEMENT&amp;lt;/font&amp;gt;}&amp;lt;/code&amp;gt; - Alle Knoten, welche mit einem Punkt beginnen werden in der FHEMWEB Oberfläche nicht angezeigt. Man kann diese Daten jedoch beim Aufruf des [[List|list-Kommandos]] einsehen.&lt;br /&gt;
&lt;br /&gt;
Es gibt bereits vorbelegte Internals welche in FHEM dazu dienen definitionsbezogene Informationen wie bspw. Namen und Readings zu speichern. Dies sind im besonderen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;min-width: 13em;&amp;quot; | Internal !!  Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{NAME}&amp;lt;/code&amp;gt;  || Der Definitionsname, mit dem das Gerät angelegt wurde.  &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{READINGS}&amp;lt;/code&amp;gt;  || Enthält alle aktuell vorhandenen Readings. Daten unterhalb dieses Knotens sollte man nicht direkt manipulieren. Um Readings zu Erzeugen gibt es entsprechende [[DevelopmentModuleAPI#Readings_.2F_Events|Reading-Funktionen]].&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{NR}&amp;lt;/code&amp;gt;  || Die Positions-Nr. der Definition innerhalb der Konfiguration. Diese dient dazu die Konfiguration in der gleichen Reihenfolge zu speichern, wie die einzelnen Geräte angelegt wurden.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{TYPE}&amp;lt;/code&amp;gt;  || Der Modulname, mit welchem die Definition angelegt wurde.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{DEF}&amp;lt;/code&amp;gt;  || Sämtliche Argumente, welche beim &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Befehl nach dem Modulnamen übergeben wurden.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{CFGFN}&amp;lt;/code&amp;gt;  || Der Dateiname der Konfigurationsdatei in der diese Definition enthalten ist (sofern nicht in fhem.cfg). Dieser Wert ist nur gefüllt, wenn man mit mehreren Konfigurationsdateien arbeitet, welche dann in fhem.cfg via include-Befehl eingebunden werden.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{NTFY_ORDER}&amp;lt;/code&amp;gt;  || Sofern das Modul Events via [[DevelopmentModuleIntro#X_Notify|Notify-Funktion]] verarbeitet enthält jede Definition eine Notify-Order als Zeichenkette bestehend aus dem Notify Order Prefix und dem Definitionsnamen. Details zur Funktionsweise gibt es in der Beschreibung zur [[DevelopmentModuleIntro#X_Notify|Notify-Funktion]] im Abschnitt &amp;quot;Reihenfolge für den Aufruf der Notify-Funktion beeinflussen&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{NOTIFYDEV}&amp;lt;/code&amp;gt;  || Sofern das Modul Events via NotifyFn verarbeitet kann man damit die Definitionen, von denen man Events erhalten will begrenzen. Details zur Funktionsweise gibt es in der Beschreibung zur [[DevelopmentModuleIntro#X_Notify|Notify-Funktion]] im Abschnitt &amp;quot;Begrenzung der Aufrufe auf bestimmte Geräte&amp;quot;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{IODev}&amp;lt;/code&amp;gt;  || Hier wird das zugeordnete IO-Gerät durch [[DevelopmentModuleAPI#AssignIoPort|AssignIoPort()]] gespeichert, welches für den Datentransport und -empfang dieses logischen Gerätes zuständig ist. Dieser Wert existiert nur bei Modulen die nach dem [[DevelopmentModuleIntro#Zweistufiges_Modell_f.C3.BCr_Module|zweistufigen Modulkonzept]] arbeiten. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{CHANGED}&amp;lt;/code&amp;gt;  || Hier werden alle Events kurzzeitig gesammelt, welche für die Eventverarbeitung anstehen. Insbesondere die [[DevelopmentModuleAPI#Readings_.2F_Events|Reading-Funktionen]] speichern hier alle Events zwischen um sie nach Abschluss via [[DevelopmentModuleAPI#DoTrigger|DoTrigger()]] zu verarbeiten. &lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{FD}&amp;lt;/code&amp;gt;  || Wenn die Definition eine Netzwerkverbindung oder serielle Schnittstelle geöffnet hat (z.B. via [[DevIo]]), so wird der entsprechende File-Deskriptor in diesem Internal gespeichert. Damit kann FHEM alle geöffneten Filedeskriptoren der entsprechenden Definition zuordnen um bei ankommenden Daten die Definition via [[DevelopmentModuleIntro#X_Read|Read-Funktion]] damit zu versorgen.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash-&amp;gt;{EXCEPT_FD}&amp;lt;/code&amp;gt;  || Ähnlich wie &amp;lt;code&amp;gt;$hash-&amp;gt;{FD}&amp;lt;/code&amp;gt;. Sofern die Definition in &amp;lt;code&amp;gt;[[#Wichtige_globale_Variablen_aus_fhem.pl|%selectlist]]&amp;lt;/code&amp;gt; eingetragen ist und ein Fildeskriptor in diesem Internal gesetzt ist, wird bei einer auftretenden Exception bzw. Interrupt die [[DevelopmentModuleIntro#X_Except|Except]]-Funktion des entsprechenden Moduls aufgerufen um darauf zu reagieren.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Generell sollte man die meisten der hier genannten systemweiten Internals nicht modifizieren, da ansonsten die korrekte Funktionsweise von FHEM nicht mehr garantiert werden kann.&lt;br /&gt;
&lt;br /&gt;
== Readings ==&lt;br /&gt;
Daten, welche von einem Gerät gelesen werden und in FHEM in einer für Menschen verständlichen Form zur Verfügung gestellt werden können, werden Readings genannt. Sie geben den Status des Gerätes wieder und erzeugen Events innerhalb von FHEM auf die andere Geräte reagieren können. Sie werden als Unterstruktur des Hashes der jeweiligen Geräteinstanz gespeichert, beispielsweise &lt;br /&gt;
*&amp;lt;code&amp;gt;$hash-&amp;gt;{READINGS}{temperature}{VAL}&amp;lt;/code&amp;gt; für die Temperatur eines Fühlers&lt;br /&gt;
*&amp;lt;code&amp;gt;$hash-&amp;gt;{READINGS}{temperature}{TIME}&amp;lt;/code&amp;gt; für den Zeitstempel der Messung&lt;br /&gt;
&lt;br /&gt;
Für den lesenden Zugriff auf Readings steht die Funktion &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#ReadingsVal|ReadingsVal()]]&amp;lt;/code&amp;gt; zur Verfügung. Ein direkter Zugriff auf die Datenstruktur sollte nicht vorgenommen werden.&lt;br /&gt;
&lt;br /&gt;
Readings werden im Statefile von FHEM automatisch auf der Festplatte zwischengespeichert, damit sie nach einem Neustart sofort wieder zur Verfügung stehen. Dadurch ist der letzte Status eines Gerätes vor einem Neustart nachvollziehbar.&lt;br /&gt;
&lt;br /&gt;
Readings, die mit einem Punkt im Namen beginnen, haben eine funktionale Besonderheit. Sie werden im FHEMWEB nicht angezeigt und können somit als &amp;quot;Permanentspeicher&amp;quot; für kleinere Daten innerhalb des Moduls genutzt werden. Um größere Datenmengen permanent zu speichern sollte man jedoch die Funktion &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#setKeyValue|setKeyValue()]]&amp;lt;/code&amp;gt; verwenden.&lt;br /&gt;
&lt;br /&gt;
Zum Setzen von Readings sollen &lt;br /&gt;
*bei Gruppen von Readings der Funktionsblock &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#readingsBeginUpdate|readingsBeginUpdate()]]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#readingsBulkUpdate|readingsBulkUpdate()]]&amp;lt;/code&amp;gt; (mehrfach wiederholt), &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#readingsEndUpdate|readingsEndUpdate()]]&amp;lt;/code&amp;gt;&lt;br /&gt;
*bei einzelnen Updates die Funktion &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#readingsSingleUpdate|readingsSingleUpdate()]]&amp;lt;/code&amp;gt; &lt;br /&gt;
aufgerufen werden. Dabei kann man auch angeben, ob dabei ein Event ausgelöst werden soll oder nicht. Events erzeugen, je nach Hardwareperformance, spürbare Last auf dem System (siehe [[DevelopmentModuleIntro#X_Notify|NotifyFn]]), das Ändern von Readings ohne dass dabei Events erzeugt werden jedoch nicht.&lt;br /&gt;
&lt;br /&gt;
Eine Sequenz zum Setzen von Readings könnte folgendermaßen aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
readingsBeginUpdate($hash);&lt;br /&gt;
readingsBulkUpdate($hash, $readingName1, $wert1 );&lt;br /&gt;
readingsBulkUpdate($hash, $readingName2, $wert2 );&lt;br /&gt;
readingsEndUpdate($hash, 1);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Events ==&lt;br /&gt;
&lt;br /&gt;
FHEM verfügt über einen Event-Mechanismus um Änderungen verschiedenster Art an einzelne oder alle Definitionen mitzuteilen. Jedes Modul (und damit alle Definitionen dieses Moduls) können auf Events von FHEM selber (Definition &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;) oder von anderen Definitionen reagieren und dadurch selber aktiv werden. Ein Event wird innerhalb von FHEM als Zeichenkette behandelt.&lt;br /&gt;
&lt;br /&gt;
Events sind grundsätzlich immer definitionsbezogen. Das bedeutet, dass ein Event immer in Verbindung mit einem Definitionsnamen erzeugt wird. Jede Definition, welche ein Event verarbeitet, erhält den Definitions-Hash (&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;) der auslösenden Definition.&lt;br /&gt;
&lt;br /&gt;
Events werden typischerweise bei der Erstellung von Readings implizit für jedes einzelne Reading erzeugt. Es gibt jedoch auch Events die nichts mit Readings zu tun haben um anderweitige Änderungen bekannt zu geben.&lt;br /&gt;
&lt;br /&gt;
Eigene Events können in FHEM mit der Funktion [[DevelopmentModuleAPI#DoTrigger|DoTrigger()]] erzeugt werden. Um auf Events in einem Modul reagieren zu können, muss eine [[#X_Notify|Notify]]-Funktion implementiert sein. Sobald ein oder mehrere Events für eine Definition getriggert werden, prüft FHEM, welche Definitionen über Events der auslösenden Definition informiert werden möchten. Diese werden dann nacheinander in einer bestimmten Reihenfolge durch Aufruf der [[#X_Notify|Notify]]-Funktion über anstehende Events in Kenntnis gesetzt. Es obliegt dann dem jeweiligen Modul, wie es auf die Events reagiert.&lt;br /&gt;
&lt;br /&gt;
=== globale Events ===&lt;br /&gt;
&lt;br /&gt;
Als &amp;quot;globale Events&amp;quot; werden alle Events bezeichnet, die durch die Definition &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; erzeugt werden. Es handelt sich hierbei um Events die Strukturänderungen in der Konfiguration, als auch systemweite Ereignisse zu FHEM selbst signalisieren.&lt;br /&gt;
&lt;br /&gt;
Hier eine kurze Zusammenfassung, welche Events durch &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; getriggert werden können:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Allgemeine Events:&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Event-Text !! Beschreibung.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;INITIALIZED&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Der Start von FHEM ist abgeschlossen. Sämtliche Definitionen und Attribute wurden aus der Konfiguration (fhem.cfg oder configDB) eingelesen, sowie sämtliche Readings sind aus dem State-File eingelesen und stehen nun voll umfänglich zur Verfügung.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;REREADCFG&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die Konfiguration wurde erneut eingelesen. Dies bedeutet, es wurden alle Definitionen/Attribute/Readings aus FHEM entfernt und durch Einlesen der Konfiguration neu angelegt. (FHEM-Befehl: &amp;quot;rereadcfg&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;SAVE&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die laufende Konfiguration soll gespeichert werden (in fhem.cfg oder configDB). Dieses Event wird &#039;&#039;&#039;VOR&#039;&#039;&#039; dem Speichern der Konfiguration getriggert. Sobald der Trigger verarbeitet wurde, beginnt das Speichern der Konfiguration. (FHEM-Befehl: &amp;quot;save&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;SHUTDOWN&amp;lt;/code&amp;gt;&#039;&#039;&#039; || FHEM wird sich beenden. (FHEM-Befehl: &amp;quot;shutdown&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Es wurde ein Update erfolgreich installiert. (FHEM-Befehl: &amp;quot;update&amp;quot;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Definitionsbezogene Events:&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Event-Text !! Beschreibung.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;DEFINED &#039;&#039;&amp;lt;Name&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Es wurde eine neue Definition mit Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;Name&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt; angelegt. (FHEM-Befehl: &amp;quot;define&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;DELETED &#039;&#039;&amp;lt;Name&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die Definition mit dem Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;Name&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt; wurde gelöscht. (FHEM-Befehl: &amp;quot;delete&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;RENAMED &#039;&#039;&amp;lt;Alt&amp;gt;&#039;&#039; &#039;&#039;&amp;lt;Neu&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die Definition mit dem Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;Alt&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt; wurde in den Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;Neu&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt; umbenannt. (FHEM-Befehl: &amp;quot;rename&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;MODIFIED &#039;&#039;&amp;lt;Name&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die Definition mit dem Namen &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;Name&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt; wurde modifiziert. (FHEM-Befehl: &amp;quot;modify&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;UNDEFINED &#039;&#039;&amp;lt;Name&amp;gt; &amp;lt;Modul&amp;gt; &amp;lt;Define-Parameter&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Es wurde eine Nachricht von einem physikalischen Modul (siehe [[#Zweistufiges Modell für Module|zweistufiges Modulkonzept]]) erhalten, für die keine passende logische Definition in FHEM existiert. Details dazu, siehe dazu Abschnitt [[#Automatisches Anlegen von logischen Gerätedefinitionen (autocreate)|Automatisches Anlegen von logischen Gerätedefinitionen (autocreate)]].&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Begrenzung von Events ===&lt;br /&gt;
&lt;br /&gt;
Ein Modul, welches Events verarbeitet, kann die Eventverarbeitung auf bestimmte Definitionen begrenzen. Dadurch werden nur Events an das Modul gemeldet (via [[#X_Notify|X_Notify()]]), welche von einer oder mehreren bestimmten Definitionen getriggert wurden. Dadurch werden unnötige Events nicht an das Modul gemeldet und schont somit Ressourcen.&lt;br /&gt;
&lt;br /&gt;
Standardmäßig werden sämtliche Events ohne Begrenzung an ein Modul gemeldet, welches eine [[#X_Notify|Notify]]-Funktion implementiert hat und somit Events verarbeiten kann. Details zur Begrenzung von Events findet man in der Beschreibung zur Modulfunktion [[#X_Notify|X_Notify()]].&lt;br /&gt;
&lt;br /&gt;
=== Reihenfolge der Eventverarbeitung ===&lt;br /&gt;
&lt;br /&gt;
Ein getriggertes Event wird nacheinander gegen jede Definition geprüft, deren Modul eine [[#X_Notify|Notify]]-Funktion implementiert hat. Dies bedeutet, jede Definition wird nacheinander durch Aufruf der [[#X_Notify|Notify]]-Funktion mit dem Definitionshash der auslösenden Definition aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Unter bestimmten Umständen kann es erforderlich sein, in diese Reihenfolge einzugreifen. Beispielsweise wenn das eigene Modul und deren Definitionen das Event als letztes oder erstes verarbeiten müssen. Ein Beispiel bietet hierbei das Modul [[dewpoint]], welches Events vor allen anderen Modulen verarbeiten muss.&lt;br /&gt;
&lt;br /&gt;
Details, wie man die Reihenfolge der Eventverarbeitung steuern kann, findet man in der Beschreibung zur Modulfunktion [[#X_Notify|X_Notify()]].&lt;br /&gt;
&lt;br /&gt;
== Attribute ==&lt;br /&gt;
Damit der Nutzer das Verhalten einer einzelnen Gerätedefinition zur Laufzeit individuell anpassen kann, gibt es in FHEM für jede Definition sogenannte Attribute, welche mit dem Befehl &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt; gesetzt werden können.&lt;br /&gt;
Diese stehen dann dem Modul unmittelbar zur Verfügung um das Verhalten während der Ausführung zu beeinflussen. Attribute werden zusammen mit dem &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Befehl der jeweiligen Definition beim Speichern der aktuellen Konfiguration von FHEM in die Konfigurationsdatei geschrieben. Beim Neustart werden die entsprechenden Befehle ausgeführt um alle Definition inkl. Attribute wieder anzulegen. Zur Laufzeit werden Attribute in dem globalen Hash &amp;lt;code&amp;gt;%attr&amp;lt;/code&amp;gt; mit dem Definitionsnamen als Index (&amp;lt;code&amp;gt;$attr{$name} = $value&amp;lt;/code&amp;gt;) gespeichert. Ein Attribut mit dem Namen &amp;lt;code&amp;gt;header&amp;lt;/code&amp;gt; würde beispielsweise mit &amp;lt;code&amp;gt;$attr{$name}{header}&amp;lt;/code&amp;gt; adressiert. Generell sollte &amp;lt;code&amp;gt;%attr&amp;lt;/code&amp;gt; nicht durch direkten Zugriff manipuliert/benutzt werden.&lt;br /&gt;
&lt;br /&gt;
Zum Auslesen von Attributen sollte die Funktion [[DevelopmentModuleAPI#AttrVal|AttrVal()]] verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Welche Attribute ein Modul unterstützt muss in der Funktion &amp;lt;code&amp;gt;[[#X_Initialize|X_Initialize]]&amp;lt;/code&amp;gt; durch Setzen von &amp;lt;code&amp;gt;$hash-&amp;gt;{AttrList}&amp;lt;/code&amp;gt; bekannt gemacht werden (siehe unten).&lt;br /&gt;
&lt;br /&gt;
Wenn beim Setzen von Attributen in einer Gerätedefinition entsprechende Werte geprüft werden sollen oder zusätzliche Funktionalitäten implementiert werden müssen, dann muss dies in der Funktion &amp;lt;code&amp;gt;[[#X_Attr|X_Attr]]&amp;lt;/code&amp;gt; (siehe unten) implementiert werden. Hier kann man bspw. einen Syntaxcheck für Attribut-Werte implementieren um ungültige Werte zurückzuweisen.&lt;br /&gt;
&lt;br /&gt;
== Modulfunktionen ==&lt;br /&gt;
&lt;br /&gt;
Damit fhem.pl ein Modul nutzen kann, muss dieses entsprechende Funktionen mit einer vorgegebenen Aufrufsyntax implementieren. Durch die Bekanntgabe dieser modulspezifischen Funktionen können Daten zwischen fhem.pl und einem Modul entsprechend ausgetauscht werden. Es gibt verschiedene Arten von Funktionen die ein Modul anbieten muss bzw. kann, je nach Funktionsumfang.&lt;br /&gt;
&lt;br /&gt;
=== Die wichtigsten Funktionen in einem Modul ===&lt;br /&gt;
&lt;br /&gt;
Folgende Funktion muss ein Modul mit dem beispielhaften Namen &amp;quot;X&amp;quot; mindestens bereitstellen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left&amp;quot; | Funktionsname !! style=&amp;quot;text-align:left&amp;quot; | Kurzbeschreibung&lt;br /&gt;
|-&lt;br /&gt;
|  [[#X_Initialize|X_Initialize]] || Initialisiert das Modul und gibt den Namen zusätzlicher Modulfunktionen bekannt, sowie modulspezifische Einstellungen. Wird direkt nach dem erfolgreichen Laden des Moduls durch fhem.pl aufgerufen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die folgenden Funktionen sind die wichtigsten Funktionen, welche je nach Anwendungsfall zu implementieren sind. Es handelt sich hierbei um die wichtigsten Vertreter, welche in den meisten Modulen Verwendung finden. Nicht alle Funktionen machen jedoch in jedem Modul Sinn. Generell sollte auch hier bei jeder Funktion der Modulname vorangestellt werden um ein einheitliches Namensschema zu gewährleisten. Hier die wichtigsten Modulfunktionen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left&amp;quot; | Funktionsname !! style=&amp;quot;text-align:left&amp;quot; class=&amp;quot;unsortable&amp;quot;| Kurzbeschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Define|X_Define]] || Wird im Rahmen des &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Befehls aufgerufen.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Undef|X_Undef]] || Wird im Rahmen des &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt;-Befehls, sowie &amp;lt;code&amp;gt;rereadcfg&amp;lt;/code&amp;gt;-Befehl aufgerufen. Dient zum Abbau von offenen Verbindungen, Timern, etc.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Delete|X_Delete]] || Wird im Rahmen des beim &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt;-Befehls aufgerufen wenn das Gerät endgültig gelöscht wird um weiterführende Aktionen vor dem Löschen durchzuführen.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Get|X_Get]] || Wird im Rahmen des &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt;-Befehls aufgerufen um Daten vom Gerät abzufragen&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Set|X_Set]]  || Wird im Rahmen des &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt;-Befehls aufgerufen um Daten an das Gerät zu senden.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Attr|X_Attr]]  || Wird im Rahmen des &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt;-Befehls aufgerufen um Attributwerte zu prüfen)&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Read|X_Read]]  || Wird durch FHEM aufgerufen, wenn ein gelisteter Filedeskriptor in &amp;lt;code&amp;gt;[[#Wichtige_globale_Variablen_aus_fhem.pl|%selectlist]]&amp;lt;/code&amp;gt; Daten zum Lesen bereitstellt.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Ready|X_Ready]]  || Wird unter Windows durch FHEM aufgerufen um zyklisch einen seriellen Filedeskriptor auf lesbare Daten zu prüfen. Unter Linux dient diese Funktion dem Wiederaufbau verlorener Verbindungen.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Notify|X_Notify]]  || Verarbeitet Events von anderen Geräten innerhalb von FHEM&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Rename|X_Rename]] || Wird aufgerufen, wenn ein Gerät umbenannt wird.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Shutdown|X_Shutdown]] || Wird beim Herunterfahren von FHEM ausgeführt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Diese Funktionen werden in diesem Abschnitt genauer beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== X_Initialize ====&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;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; im Namen muss dabei auf den Namen des Moduls bzw. des definierten Gerätetyps geändert werden. Im Modul mit der Datei &amp;lt;code&amp;gt;36_JeeLink.pm&amp;lt;/code&amp;gt; beispielsweise ist der Name der Funktion &amp;lt;code&amp;gt;JeeLink_Initialize&amp;lt;/code&amp;gt;. Die Funktion wird von fhem.pl nach dem Laden des Moduls aufgerufen und bekommt eine leere Hashreferenz für den Initialisierungsvorgang übergeben. &lt;br /&gt;
&lt;br /&gt;
Dieser Hash muss nun von X_Initialize mit allen modulrelevanten Funktionsnamen gefüllt werden. Anschließend wird dieser Hash durch fhem.pl im globalen Hash &amp;lt;code&amp;gt;%modules&amp;lt;/code&amp;gt; gespeichert. &amp;lt;code&amp;gt;$modules{ModulName}&amp;lt;/code&amp;gt; wäre dabei der Hash für das Modul mit dem Namen &amp;lt;code&amp;gt;ModulName&amp;lt;/code&amp;gt;. Es handelt sich also nicht um den oben beschriebenen Hash der Geräteinstanzen sondern einen Hash, der für jedes Modul existiert und modulspezifische Daten wie bspw. die implementierten Modulfunktionen enthält. Die Initialize-Funktion setzt diese Funktionsnamen, in den Hash des Moduls wie folgt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$hash-&amp;gt;{DefFn}         = &amp;quot;X_Define&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{UndefFn}       = &amp;quot;X_Undef&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{DeleteFn}      = &amp;quot;X_Delete&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{SetFn}         = &amp;quot;X_Set&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{GetFn}         = &amp;quot;X_Get&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{AttrFn}        = &amp;quot;X_Attr&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{ReadFn}        = &amp;quot;X_Read&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{ReadyFn}       = &amp;quot;X_Ready&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{NotifyFn}      = &amp;quot;X_Notify&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{RenameFn}      = &amp;quot;X_Rename&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{ShutdownFn}    = &amp;quot;X_Shutdown&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um eine entsprechende Funktion in FHEM bekannt zu machen muss dazu der Funktionsname, wie er im Modul als &amp;lt;code&amp;gt;sub &amp;amp;lt;&#039;&#039;Funktionsname&#039;&#039;&amp;amp;gt;() { ... }&amp;lt;/code&amp;gt; definiert ist, als Zeichenkette in &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; gesetzt werden. Dabei sollten die entsprechenden Funktionsnamen immer den Modulnamen (in diesem Beispiel &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt;) als Präfix verwenden.&lt;br /&gt;
Auf diese Weise können sämtliche modulspezifisch implementierten Funktionen wie &amp;lt;code&amp;gt;X_Read&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;X_Parse&amp;lt;/code&amp;gt; etc. durch Zuweisung an &amp;lt;code&amp;gt;$hash-&amp;gt;{ReadFn}&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;$hash-&amp;gt;{ParseFn}&amp;lt;/code&amp;gt; usw. bekannt gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Darüber hinaus sollten die vom Modul unterstützten Attribute definiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$hash-&amp;gt;{AttrList} =&lt;br /&gt;
  &amp;quot;do_not_notify:1,0 &amp;quot; . &lt;br /&gt;
  &amp;quot;header &amp;quot; .&lt;br /&gt;
  $readingFnAttributes;  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Auflistung aller unterstützten modulspezifischen Attribute erfolgt in Form einer durch Leerzeichen getrennten Liste in &amp;lt;code&amp;gt;$hash-&amp;gt;{AttrList}}&amp;lt;/code&amp;gt;. Es gibt in FHEM globale Attribute, die in allen Gerätedefinitionen verfügbar sind und nur modulspezifische Attribute die jedes Modul via &amp;lt;code&amp;gt;$hash-&amp;gt;{AttrList}&amp;lt;/code&amp;gt; über die eigene Initialize-Funktion setzt.  In fhem.pl werden dann die entsprechenden Attributwerte beim Aufruf eines &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt;-Befehls in die globale Datenstruktur &amp;lt;code&amp;gt;$attr{$name}&amp;lt;/code&amp;gt;, z.B. &amp;lt;code&amp;gt;$attr{$name}{header}&amp;lt;/code&amp;gt; für das Attribut &amp;lt;code&amp;gt;header&amp;lt;/code&amp;gt; gespeichert. Falls im Modul weitere Aktionen oder Prüfungen beim Setzen eines Attributs nötig sind, dann kann wie im Beispiel oben die [[#X_Attr|Attr]]-Funktion implementiert und in der Initialize-Funktion bekannt gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Die Variable &amp;lt;code&amp;gt;$readingFnAttributes&amp;lt;/code&amp;gt;, die im obigen Beispiel an die Liste der unterstützten Attribute angefügt wird, definiert Attributnamen, die dann zusätzlich gemacht werden, wenn das Modul zum Setzen von Readings die Funktionen &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#readingsBeginUpdate|readingsBeginUpdate()]]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#readingsBulkUpdate|readingsBulkUpdate()]]&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#readingsEndUpdate|readingsEndUpdate()]]&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#readingsSingleUpdate|readingsSingleUpdate()]]&amp;lt;/code&amp;gt; verwendet. In diesen Funktionen werden Attribute wie &amp;lt;code&amp;gt;event-min-interval&amp;lt;/code&amp;gt; oder auch &amp;lt;code&amp;gt;event-on-change-reading&amp;lt;/code&amp;gt; ausgewertet. Für Details hierzu siehe commandref zu {{Link2CmdRef|Anker=readingFnAttributes|Label=readingFnAttributes}}.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Nutzung von parseParams()&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt; [[DevelopmentModuleAPI#parseParams|parseParams()]]&amp;lt;/code&amp;gt; unterstützt Modulautoren beim Parsen von Übergabeparametern, welche bei &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; Kommandos an die entsprechenden Modulfunktionen übergeben werden. Dadurch lassen sich auf einfache Weise insbesondere komplexe Parameter (wie bspw. Perl-Ausdrücke) sehr einfach parsen.&lt;br /&gt;
&lt;br /&gt;
Diese Zusatzfunktion kann man in der Initialize-Funktion einfach über folgenden Parameter für [[#X_Define|Define]]-, [[#X_Get|Get]]- und [[#X_Set|Set]]-Funktion modulweit aktivieren:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$hash-&amp;gt;{parseParams} = 1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Sobald es gesetzt ist wird automatisch durch fhem.pl &amp;lt;code&amp;gt;[[DevelopmentModuleAPI#parseParams|parseParams()]]&amp;lt;/code&amp;gt; aufgerufen und die an die [[#X_Define|Define]]-, [[#X_Get|Get]]- und [[#X_Set|Set]]-Funktion übergebenen Parameter ändern sich wie weiter unten in den jeweiligen Funktionen beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== X_Define ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Define($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $def ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
 &lt;br /&gt;
	return $error;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Define-Funktion eines Moduls wird von FHEM aufgerufen wenn der Define-Befehl für ein Geräte ausgeführt wird und das Modul bereits geladen und mit der Initialize-Funktion initialisiert ist. Sie ist typischerweise dazu da, die übergebenen Parameter zu prüfen und an geeigneter Stelle zu speichern sowie einen Kommunikationsweg zum Gerät zu öffnen (z.B. TCP-Verbindung, USB-Schnittstelle o.ä.) oder einen [[#Pollen_von_Geräten|Status-Timer]] zu starten.&lt;br /&gt;
Sie beginnt typischerweise mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Define($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $def ) = @_;&lt;br /&gt;
	my @a = split( &amp;quot;[ \t][ \t]*&amp;quot;, $def );&lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als Übergabeparameter bekommt die Define-Funktion den Hash der Geräteinstanz sowie den die im &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Befehl übergebenen Parameter. Welche bzw. wie viele Parameter &lt;br /&gt;
akzeptiert werden und welcher Syntax diese entsprechen müssen ist Sache dieser Funktion. Im obigen Beispiel wird die Argumentzeile &amp;lt;code&amp;gt;$def&amp;lt;/code&amp;gt; in ein Array aufgeteilt (durch Leerzeichen/Tabulator getrennt) und so können die vom Modul bzw. der Define-Funktion erwarteten Werte über das Array der Reihe nach verarbeitet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $name   = $a[0];&lt;br /&gt;
my $module = $a[1];&lt;br /&gt;
my $url    = $a[2];&lt;br /&gt;
my $inter  = 300;&lt;br /&gt;
&lt;br /&gt;
if(int(@a) == 4) { &lt;br /&gt;
	$inter = $a[3]; &lt;br /&gt;
	if ($inter &amp;lt; 5) {&lt;br /&gt;
		return &amp;quot;interval too small, please use something &amp;gt; 5s, default is 300 seconds&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit die übergebenen Werte auch anderen Funktionen zur Verfügung stehen und an die jeweilige Geräteinstanz gebunden sind, werden die Werte typischerweise als Internals im Hash der Geräteinstanz gespeichert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$hash-&amp;gt;{url} 		= $url;&lt;br /&gt;
$hash-&amp;gt;{Interval}	= $inter;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald alle Parameter korrekt verarbeitet wurden, wird in der Regel die erste Verbindung zum Gerät aufgebaut. Je nach Art des Geräts kann das eine permanente Datenverbindung sein (z.B. serielle Schnittstelle oder TCP-Verbindung) oder das Starten eines regelmäßigen Timers, der zyklisch den Status z.B. via [[HttpUtils|HTTP]] ausliest.&lt;br /&gt;
&lt;br /&gt;
Sollten im Rahmen der Define-Funktion Syntax-Probleme der Übergabeparameter festgestellt werden oder es kann bspw. keine Verbindung aufgebaut werden, so ist als Funktionsrückgabewert eine entsprechende Fehlermeldung zurückzugeben. Nur wenn alle Übergabeparameter akzeptiert werden, darf &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben werden. Sobald eine Define-Funktion eine Fehlermeldung zurückmeldet, wird der define-Befehl durch FHEM zurückgewiesen und der User erhält die Fehlermeldung, welche die Define-Funktion produziert hat, als Ausgabe zurück.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Verfügbarkeit von Attributen&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Während die Define-Funktion ausgeführt wird, sollte man nicht davon ausgehen, dass alle vom Nutzer konfigurierten Attribute via [[DevelopmentModuleAPI#AttrVal|AttrVal()]] verfügbar sind. Attribute stehen in der Define-Funktion nur dann zur Verfügung, wenn FHEM sich nicht in der Initialisierungsphase befindet (globale Variable &amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; ist wahr; der Nutzer hat die Gerätedefinition modifiziert). Daher sollte man weiterführende Funktion, welche auf gesetzte Attribute angewiesen sind, nur dann in der Define-Funktion starten, wenn &amp;lt;code&amp;gt;$init_done&amp;lt;/code&amp;gt; zutrifft.&lt;br /&gt;
&lt;br /&gt;
Andernfalls sollte man den Aufruf in der Notify-Funktion durchführen sobald &amp;lt;code&amp;gt;global:INITIALIZED&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;global:REREADCFG&amp;lt;/code&amp;gt; getriggert wurde:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Define($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $def ) = @_;&lt;br /&gt;
 &lt;br /&gt;
	...&lt;br /&gt;
&lt;br /&gt;
	$hash-&amp;gt;{NOTIFYDEV} = &amp;quot;global&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
	X_FunctionWhoNeedsAttr($hash) if($init_done);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub X_Notify($$)&lt;br /&gt;
{&lt;br /&gt;
	my ($own_hash, $dev_hash) = @_;&lt;br /&gt;
	my $ownName = $own_hash-&amp;gt;{NAME}; # own name / hash&lt;br /&gt;
 &lt;br /&gt;
	return &amp;quot;&amp;quot; if(IsDisabled($ownName)); # Return without any further action if the module is disabled&lt;br /&gt;
 &lt;br /&gt;
	my $devName = $dev_hash-&amp;gt;{NAME}; # Device that created the events&lt;br /&gt;
	my $events = deviceEvents($dev_hash, 1);&lt;br /&gt;
&lt;br /&gt;
	if($devName eq &amp;quot;global&amp;quot; &amp;amp;&amp;amp; grep(m/^INITIALIZED|REREADCFG$/, @{$events}))&lt;br /&gt;
	{&lt;br /&gt;
		 X_FunctionWhoNeedsAttr($hash);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dadurch wird die Modulfunktion X_FunctionWhoNeedsAttr() nach dem Start erst aufgerufen, wenn alle Attribute aus der Konfiguration geladen wurden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Nutzung von parseParams()&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zum Aufteilen und Parsen von &amp;lt;code&amp;gt;$def&amp;lt;/code&amp;gt; lässt sich die Funktion [[DevelopmentModuleAPI#parseParams|parseParams()]] verwenden um die einzelnen Argumente einfach zu parsen. Wenn in [[#X_Initialize|X_Initialize()]] &amp;lt;code&amp;gt;$hash-&amp;gt;{parseParams} = 1;&amp;lt;/code&amp;gt; gesetzt wurde dann wird parseParams() automatisch aufgerufen und X_Define() ändert sich wie folgt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Define($$$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $a, $h ) = @_;&lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die genauen Möglichkeiten von parseParams() sind in dem entsprechenden [[DevelopmentModuleAPI#parseParams|Artikel]] dokumentiert.&lt;br /&gt;
&lt;br /&gt;
==== X_Undef ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Undef ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $name ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
 &lt;br /&gt;
	return $error;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Undef-Funktion wird aufgerufen wenn ein Gerät mit &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; gelöscht wird oder bei der Abarbeitung des Befehls &amp;lt;code&amp;gt;rereadcfg&amp;lt;/code&amp;gt;, der ebenfalls alle Geräte löscht und danach das Konfigurationsfile neu einliest. Entsprechend müssen in der Funktion typische Aufräumarbeiten durchgeführt werden wie das saubere Schließen von Verbindungen oder das Entfernen von internen Timern, sofern diese im Modul zum Pollen verwendet wurden (siehe Abschnitt [[#Pollen_von_Geräten|Pollen von Geräten]]). &lt;br /&gt;
&lt;br /&gt;
Zugewiesene Variablen im Hash der Geräteinstanz, Internals oder Readings müssen hier nicht gelöscht werden. In fhem.pl werden die entsprechenden Strukturen beim Löschen der Geräteinstanz ohnehin vollständig gelöscht.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Undef($$)    &lt;br /&gt;
{                     &lt;br /&gt;
	my ( $hash, $name) = @_;       &lt;br /&gt;
	DevIo_CloseDev($hash);         &lt;br /&gt;
	RemoveInternalTimer($hash);    &lt;br /&gt;
	return undef;                  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollten im Rahmen der Undef-Funktion Probleme festgestellt werden, die ein Löschen nicht zulassen, so ist als Funktionsrückgabewert eine entsprechende Fehlermeldung zurückzugeben. Nur wenn die Undef-Funktion erfolgreich durchgeführt wurde, darf &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben werden. Nur dann wird eine Gerätedefinition von FHEM auch tatsächlich gelöscht bzw. neu angelegt. Sollte die Undef-Funktion jedoch eine Fehlermeldung zurückgeben, wird der entsprechende Vorgang (&amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;rereadcfg&amp;lt;/code&amp;gt;) für dieses Gerät abgebrochen. Es bleibt dann unverändert in FHEM bestehen.&lt;br /&gt;
&lt;br /&gt;
==== X_Delete ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Delete ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $name ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
 &lt;br /&gt;
	return $error;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Delete-Funktion ist das Gegenstück zur Funktion [[#X_Define|X_Define]] und wird aufgerufen wenn ein Gerät mit dem Befehl &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; gelöscht wird. &lt;br /&gt;
&lt;br /&gt;
Wenn ein Gerät in FHEM gelöscht wird, wird zuerst die Funktion [[#X_Undef|X_Undef]] aufgerufen um offene Verbindungen zu schließen, anschließend wird die Funktion X_Delete aufgerufen. Diese dient eher zum Aufräumen von dauerhaften Daten, welche durch das Modul evtl. für dieses Gerät spezifisch erstellt worden sind. Es geht hier also eher darum, alle Spuren sowohl im laufenden FHEM-Prozess, als auch dauerhafte Daten bspw. im physikalischen Gerät zu löschen die mit dieser Gerätedefinition zu tun haben.&lt;br /&gt;
&lt;br /&gt;
Dies kann z.B. folgendes sein:&lt;br /&gt;
&lt;br /&gt;
* Löschen von Dateien im Dateisystem die während der Nutzung dieses Geräts angelegt worden sind.&lt;br /&gt;
* Lösen von evtl. Pairings mit dem physikalischen Gerät &lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Delete($$)    &lt;br /&gt;
{                     &lt;br /&gt;
	my ( $hash, $name ) = @_;       &lt;br /&gt;
&lt;br /&gt;
	# Löschen von Geräte-assoziiertem Temp-File&lt;br /&gt;
	unlink($attr{global}{modpath}.&amp;quot;/FHEM/FhemUtils/$name.tmp&amp;quot;;)&lt;br /&gt;
&lt;br /&gt;
	return undef;&lt;br /&gt;
}    &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollten im Rahmen der Delete-Funktion Probleme festgestellt werden, die ein Löschen nicht zulassen, so ist als Funktionsrückgabewert eine entsprechende Fehlermeldung zurückzugeben. Nur die Delete-Funktion erfolgreich durchgeführt wurde, darf &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben werden. Nur dann wird eine Gerätedefinition von FHEM auch tatsächlich gelöscht. Sollte die Delete-Funktion eine Fehlermeldung zurückgeben, wird der Löschvorgang abgebrochen und das Gerät bleibt weiter in FHEM bestehen.&lt;br /&gt;
&lt;br /&gt;
==== X_Get ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Get ($$@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $name, $opt, @args ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
 &lt;br /&gt;
	return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Get-Funktion wird aufgerufen wenn der FHEM-Befehl &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; mit einem Gerät dieses Moduls ausgeführt wird. Mit &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; werden typischerweise Werte von einem Gerät abgefragt. In vielen Modulen wird auf diese Weise auch der Zugriff auf generierte Readings ermöglicht. Der Get-Funktion wird dabei der Geräte-Hash, der Gerätename, sowie die Aufrufparameter des get-Befehls übergeben. Als Rückgabewert wird das Ergebnis des entsprechenden Befehls in Form einer Zeichenkette zurückgegeben. Der Rückgabewert &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; hat hierbei keine besondere Bedeutung und wird behandelt wie eine leere Zeichenkette &amp;lt;code&amp;gt;&amp;quot;&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Get($$@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $name, $opt, @args ) = @_;&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;\&amp;quot;get $name\&amp;quot; needs at least one argument&amp;quot; unless(defined($opt));&lt;br /&gt;
&lt;br /&gt;
	if($opt eq &amp;quot;status&amp;quot;) &lt;br /&gt;
	{&lt;br /&gt;
	   ...&lt;br /&gt;
	}&lt;br /&gt;
	elsif($opt eq &amp;quot;power&amp;quot;)&lt;br /&gt;
	{&lt;br /&gt;
	   ...&lt;br /&gt;
	}&lt;br /&gt;
	...&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		return &amp;quot;Unknown argument $opt, choose one of status power [...]&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn eine unbekannte Option an die Get-Funktion übergeben wird, so muss als Rückgabewert der Funktion eine bestimmte Syntax einhalten um FHEM mitzuteilen, welche Optionen für einen Get-Befehl aktuell unterstützt werden. Die Rückgabe muss dabei folgender Syntax entsprechen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;&#039;&#039;unknown&#039;&#039;&#039; argument &#039;&#039;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;[Parameter]&amp;lt;/font&amp;gt;&#039;&#039; &#039;&#039;&#039;choose one of&#039;&#039;&#039; &#039;&#039;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;[Liste möglicher Optionen]&amp;lt;/font&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei sind die fett gedruckten Teile der Rückmeldung besonders wichtig. Sind diese nicht vorhanden, kann FHEM nicht die möglichen Get-Kommandos für das entsprechende Gerät ermitteln. Es muss am Anfang der Meldung das Stichwort &amp;quot;unknown&amp;quot; vorkommen gefolgt von einer frei definierbaren Fehlermeldung (i.d.R der übergebene Parameter, welcher ungültig ist). Anschließend folgt &amp;quot;choose one of&amp;quot; mit einer anschließenden Liste möglicher Optionen getrennt durch ein Leerzeichen. &lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;return &amp;quot;unknown argument $opt choose one of status temperature humidity&amp;quot;;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden als mögliche Optionen für einen Get-Befehl folgende Parameter angegeben:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;temperature&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;humidity&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies würde in folgenden, mögliche Get-Befehle für einen User resultieren:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;get &#039;&#039;&amp;amp;lt;NAME&amp;amp;gt;&#039;&#039; status&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;get &#039;&#039;&amp;amp;lt;NAME&amp;amp;gt;&#039;&#039; temperature&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;get &#039;&#039;&amp;amp;lt;NAME&amp;amp;gt;&#039;&#039; humidity&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe einer solchen Meldung ist sehr wichtig, da sie im GUI-Modul verwendet wird um die möglichen &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt;-Optionen zu ermitteln und als Auswahl anzubieten. Im weiteren Verlauf der Get-Funktion könnte man dann mit dem physischen Gerät kommunizieren und den gefragten Wert direkt abfragen und diesen als Return-Wert der Get-Funktion zurückgeben.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Nutzung von parseParams()&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn in [[#X_Initialize|X_Initialize()]] &amp;lt;code&amp;gt;$hash-&amp;gt;{parseParams} = 1;&amp;lt;/code&amp;gt; gesetzt wurde dann wird [[DevelopmentModuleAPI#parseParams|parseParams()]] automatisch aufgerufen und X_Get() ändert sich wie folgt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Get($$$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $a, $h ) = @_;&lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die genauen Möglichkeiten von parseParams() sind in dem entsprechenden [[DevelopmentModuleAPI#parseParams|Artikel]] dokumentiert.&lt;br /&gt;
&lt;br /&gt;
==== X_Set ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Set ($$@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $name, $cmd, @args ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
&lt;br /&gt;
	return $error;&lt;br /&gt;
	return ($error, $skip_trigger);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Set-Funktion ist das Gegenteil zur [[#X_Get|Get]]-Funktion. Sie ist dafür gedacht, Daten zum physischen Gerät zu schicken, bzw. entsprechende Aktionen im Gerät selber auszulösen. Ein Set-Befehl dient daher der direkten Steuerung des physikalischen Gerätes in dem es bspw. Zustände verändert (wie &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt;). Der Set-Funktion wird dabei der Geräte-Hash, der Gerätename, sowie die Aufrufparameter des set-Befehls übergeben. Als Rückgabewert kann eine Fehlermeldung in Form Zeichenkette zurückgegeben werden. Der Rückgabewert &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; bedeutet hierbei, dass der Set-Befehl erfolgreich durchgeführt wurde. Eine Set-Funktion gibt daher nur im Fehlerfall eine Rückmeldung mit einer entsprechenden Fehlermeldung. Der Wert &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; wird als &amp;quot;erfolgreich&amp;quot; interpretiert. &lt;br /&gt;
&lt;br /&gt;
Standardmäßig wird jeder Set-Befehl, welcher erfolgreich ausgeführt wurde (&amp;lt;code&amp;gt;$error&amp;lt;/code&amp;gt; ist &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt;), als Event getriggert um dies bspw. in einem FileLog festzuhalten. Dieses Verhalten kann optional unterbunden werden indem der zweite Rückgabewert &amp;lt;code&amp;gt;$skip_trigger&amp;lt;/code&amp;gt; auf &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; gesetzt wird. Damit wird das Generieren eines Events für das erfolgreich ausgeführte Set-Kommando unterbunden. Falls nicht gesetzt, wird ein Event erzeugt (&amp;lt;code&amp;gt;$cmd&amp;lt;/code&amp;gt; mit sämtlichen &amp;lt;code&amp;gt;@args&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Rückmeldungen (Fehler) von set-Befehlen sämtlicher Module, die im Rahmen eines ausgeführten [[Notify]] auftreten werden im FHEM Logfile festgehalten.&lt;br /&gt;
&lt;br /&gt;
Falls nur interne Daten, die ausschließlich für das Modul relevant sind, gesetzt werden müssen, so sollte statt Set die [[#X_Attr|Attr]]-Funktion verwendet werden. Attribute werden bei Save-Config auch in der Fhem.cfg gesichert. Set-Befehle nicht, da sie nur zur Steuerungszwecken im laufenden Betrieb von FHEM dienen.&lt;br /&gt;
 &lt;br /&gt;
Eine Set-Funktion ist ähnlich aufgebaut wie die Get-Funktion, sie bekommt jedoch in der Regel weitere zusätzliche Parameter übergeben um Zustände zu setzen. &lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Set($@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $name, $cmd, @args ) = @_;&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;\&amp;quot;set $name\&amp;quot; needs at least one argument&amp;quot; unless(defined($cmd));&lt;br /&gt;
&lt;br /&gt;
	if($cmd eq &amp;quot;status&amp;quot;)&lt;br /&gt;
	{&lt;br /&gt;
	   if($args[0] eq &amp;quot;up&amp;quot;)&lt;br /&gt;
	   {&lt;br /&gt;
	      ...&lt;br /&gt;
	   }&lt;br /&gt;
	   elsif($args[0] eq &amp;quot;down&amp;quot;)&lt;br /&gt;
	   {&lt;br /&gt;
	      ...&lt;br /&gt;
	   }&lt;br /&gt;
	   else&lt;br /&gt;
	   {&lt;br /&gt;
	      return &amp;quot;Unknown value $args[0] for $cmd, choose one of status power&amp;quot;;&lt;br /&gt;
	   }   &lt;br /&gt;
	}&lt;br /&gt;
	elsif($cmd eq &amp;quot;power&amp;quot;)&lt;br /&gt;
	{&lt;br /&gt;
	   if($args[0] eq &amp;quot;on&amp;quot;)&lt;br /&gt;
	   {&lt;br /&gt;
	      ...&lt;br /&gt;
	   }&lt;br /&gt;
	   elsif($args[0] eq &amp;quot;off&amp;quot;)&lt;br /&gt;
	   {&lt;br /&gt;
	      ...&lt;br /&gt;
	   }  &lt;br /&gt;
	   else&lt;br /&gt;
	   {&lt;br /&gt;
	      return &amp;quot;Unknown value $args[0] for $cmd, choose one of status power&amp;quot;;&lt;br /&gt;
	   }       &lt;br /&gt;
	}&lt;br /&gt;
	...&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		return &amp;quot;Unknown argument $cmd, choose one of status power&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn eine unbekannte Option an die Set-Funktion übergeben wird, so muss als Rückgabewert der Funktion eine bestimmte Syntax eingehalten werden um FHEM mitzuteilen, welche Optionen für einen Set-Befehl aktuell unterstützt werden. Die Rückgabe muss dabei folgender Syntax entsprechen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;&#039;&#039;unknown&#039;&#039;&#039; argument &#039;&#039;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;[Parameter]&amp;lt;/font&amp;gt;&#039;&#039; &#039;&#039;&#039;choose one of&#039;&#039;&#039; &#039;&#039;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;[Liste möglicher Optionen]&amp;lt;/font&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei sind die fett gedruckten Teile der Rückmeldung besonders wichtig. Sind diese nicht vorhanden, kann FHEM nicht die möglichen Set-Kommandos für das entsprechende Gerät ermitteln. Es muss am Anfang der Meldung das Stichwort &amp;quot;unknown&amp;quot; vorkommen gefolgt von einer frei definierbaren Fehlermeldung (i.d.R der übergebene Parameter, welcher ungültig ist). Anschließend folgt &amp;quot;choose one of&amp;quot; mit einer anschließenden Liste möglicher Optionen getrennt durch ein Leerzeichen. &lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;return &amp;quot;unknown argument $cmd choose one of status power&amp;quot;;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier werden als mögliche Optionen für einen Set-Befehl folgende Parameter angegeben:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;status&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;power&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies würde in folgenden, mögliche Set-Befehle für einen User resultieren:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set &#039;&#039;&amp;amp;lt;NAME&amp;amp;gt;&#039;&#039; status&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set &#039;&#039;&amp;amp;lt;NAME&amp;amp;gt;&#039;&#039; power&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe einer solchen Meldung ist sehr wichtig, da sie im Modul [[FHEMWEB]] verwendet wird um die möglichen &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt;-Optionen zu ermitteln und als Auswahl anzubieten.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Nutzung von SetExtensions.pm&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man dem Nutzer zusätzlich zu den Set-Befehlen &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; auch weiterführende Befehle wie &amp;lt;code&amp;gt;on-for-timer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;on-till&amp;lt;/code&amp;gt;, usw. anbieten möchte, obwohl die zu steuernde Hardware solche Kommandos nicht unterstützt, kann man dies über das Hilfsmodul SetExtensions.pm realisieren.&lt;br /&gt;
&lt;br /&gt;
Das Hilfsmodul SetExtensions.pm bietet weiterführende Set-Kommandos basierend auf den Befehlen &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; an. Dabei werden durch interne Timer bzw. eigens angelegten [[at]]-Definitionen diese Befehle durch FHEM selber umgesetzt. Je nach ausgeführtem Befehl wird der &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt;- bzw. &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt;-Befehl dann durch FHEM zum richtigen Zeitpunkt ausgeführt. Vorausgesetzt das Modul unterstützt in der Set-Funktion die Befehle &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt;, so werden durch den Einsatz von SetExtensions.pm folgende Befehle zusätzlich unterstützt:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Set-Kommando !! Beispiel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;on-for-timer &#039;&#039;&amp;amp;lt;Dauer&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;  || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;on-for-timer 120&amp;lt;/code&amp;gt; || Schaltet das Gerät sofort mit dem &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt;-Befehl ein und nach der angegebenen Dauer in Sekunden via &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; wieder aus.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;off-for-timer &#039;&#039;&amp;amp;lt;Dauer&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;  || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;off-for-timer 120&amp;lt;/code&amp;gt; || Schaltet das Gerät sofort mit dem &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt;-Befehl aus und nach der angegebenen Dauer in Sekunden via &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt; wieder ein.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;on-till &#039;&#039;&amp;amp;lt;Zeitpunkt&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;  || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;on-till 16:30&amp;lt;/code&amp;gt; || Schaltet das Gerät sofort mit dem &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt;-Befehl ein und zum angegebenen Zeitpunkt via &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; wieder aus.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;off-till &#039;&#039;&amp;amp;lt;Zeitpunkt&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;  || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;off-till 16:30&amp;lt;/code&amp;gt; || Schaltet das Gerät sofort mit dem &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt;-Befehl aus und zum angegebenen Zeitpunkt via &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt; wieder ein.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;on-till-overnight &#039;&#039;&amp;amp;lt;Zeitpunkt&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;  || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;on-till-overnight 01:00&amp;lt;/code&amp;gt; || Ähnlich wie &amp;lt;code&amp;gt;on-till&amp;lt;/code&amp;gt;. Der übergebene Zeitpunkt wird aber nicht geprüft, ob er für den heutigen Tag bereits überschritten wurde. Dadurch kann man Abends einen Zeitpunkt setzen, der erst am nächsten Tag zutrifft.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;off-till-overnight &#039;&#039;&amp;amp;lt;Zeitpunkt&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;  || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;off-till-overnight 01:00&amp;lt;/code&amp;gt; || Ähnlich wie &amp;lt;code&amp;gt;off-till&amp;lt;/code&amp;gt;. Der übergebene Zeitpunkt wird aber nicht geprüft, ob er für den heutigen Tag bereits überschritten wurde. Dadurch kann man Abends einen Zeitpunkt setzen, der erst am nächsten Tag zutrifft.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;blink &#039;&#039;&amp;amp;lt;Anzahl&amp;amp;gt; &amp;amp;lt;Interval&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;  || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;blink 3 1&amp;lt;/code&amp;gt; || Schaltet das Gerät via &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt; für &amp;lt;code&amp;gt;&#039;&#039;&amp;amp;lt;Interval&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt; Sekunden ein und anschließend via &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; wieder aus. Nach &amp;lt;code&amp;gt;&#039;&#039;&amp;amp;lt;Interval&amp;amp;gt;&#039;&#039;&amp;lt;/code&amp;gt; Sekunden wird das ganze wiederholt, solange bis die angegebene Anzahl erreicht ist.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;intervals &#039;&#039;&amp;amp;lt;Start&amp;amp;gt;-&amp;amp;lt;Ende&amp;amp;gt;&#039;&#039; &#039;&#039;&amp;amp;lt;Start&amp;amp;gt;-&amp;amp;lt;Ende&amp;amp;gt;&#039;&#039; ...&amp;lt;/code&amp;gt; || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;intervals 07:00-08:00 16:30-18:00&amp;lt;/code&amp;gt; || Schaltet das Gerät innerhalb der übergebenen Zeiträumen via &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt; ein. Sobald die aktuelle Zeit ausserhalb dieser Zeiträume liegt, wird das Gerät via &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; wieder ausgeschaltet. Es können dabei beliebig viele Zeiträume angegeben werden.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;toggle&amp;lt;/code&amp;gt; || style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;toggle&amp;lt;/code&amp;gt;  || Sofern der aktuelle Status &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt; ist, wird das Gerät via &amp;lt;code&amp;gt;off&amp;lt;/code&amp;gt; ausgeschaltet. Andernfalls wird es via &amp;lt;code&amp;gt;on&amp;lt;/code&amp;gt; eingeschaltet.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Eine kurze Beschreibung zu den möglichen Befehlen durch SetExtensions.pm gibt es auch in der commandref zum {{Link2CmdRef|Anker=set|Label=set-Befehl}}.&lt;br /&gt;
&lt;br /&gt;
Um SetExtensions.pm in der Set-Funktion nutzen zu können müssen folgende Aktionen durchgeführt werden:&lt;br /&gt;
&lt;br /&gt;
# Laden von SetExtensions.pm via &amp;lt;code&amp;gt;use SetExtensions;&amp;lt;/code&amp;gt; am Anfang des Moduls&lt;br /&gt;
# Aufruf und Rückgabe der Funktion [[DevelopmentModuleAPI#SetExtensions|SetExtensions()]] sofern die Set-Funktion mit dem übergebenen Befehl nichts anfangen kann.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
use SetExtensions;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
sub X_Set($@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $name, $cmd, @args ) = @_;&lt;br /&gt;
	my $cmdList = &amp;quot;on off&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;\&amp;quot;set $name\&amp;quot; needs at least one argument&amp;quot; unless(defined($cmd)));&lt;br /&gt;
&lt;br /&gt;
	if($cmd eq &amp;quot;on&amp;quot;)&lt;br /&gt;
	{&lt;br /&gt;
		# Gerät einschalten...&lt;br /&gt;
	}&lt;br /&gt;
	elsif($cmd eq &amp;quot;off&amp;quot;)&lt;br /&gt;
	{&lt;br /&gt;
		# Gerät ausschalten...&lt;br /&gt;
	}&lt;br /&gt;
	...&lt;br /&gt;
	else # wenn der übergebene Befehl nicht durch X_Set() verarbeitet werden kann, Weitergabe an SetExtensions()&lt;br /&gt;
	{&lt;br /&gt;
		return SetExtensions($hash, $cmdList, $name, $cmd, @args);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sollte der übergebene Set-Befehl auch für SetExtensions unbekannt sein (bspw. &amp;lt;code&amp;gt;set &#039;&#039;&amp;amp;lt;Name&amp;amp;gt;&#039;&#039; ?&amp;lt;/code&amp;gt;), so generiert SetExtensions() eine entsprechende Usage-Meldung, welche innerhalb der Set-Funktion an FHEM zurückgegeben werden muss.&lt;br /&gt;
&lt;br /&gt;
Eine ausführliche Beschreibung zu der Funktion SetExtensions() gibt es in der  [[DevelopmentModuleAPI#SetExtensions|API-Referenz]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Nutzung von parseParams()&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn in [[#X_Initialize|X_Initialize()]] &amp;lt;code&amp;gt;$hash-&amp;gt;{parseParams} = 1;&amp;lt;/code&amp;gt; gesetzt wurde dann wird [[DevelopmentModuleAPI#parseParams|parseParams()]] automatisch aufgerufen und X_Set() ändert sich wie folgt:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Set($$$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $a, $h ) = @_;&lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die genauen Möglichkeiten von parseParams() sind in dem entsprechenden [[DevelopmentModuleAPI#parseParams|Artikel]] dokumentiert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Nutzung von FHEMWEB-Widgets&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das GUI-Modul [[FHEMWEB]] kann für die einzelnen Set-Optionen, die das Modul versteht, automatisch Eingabehilfen wie Drop-Down Boxen oder Slider erzeugen. In der Detailansicht der GUI kann der Anwender dann die jeweiligen Werte komfortabel auswählen. Dafür muss die Set-Funktion, wenn sie mit der Option &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; aufgerufen wird, nicht nur einen Text mit  &amp;lt;code&amp;gt;&amp;quot;Unknown ... choose one of ...&amp;quot;&amp;lt;/code&amp;gt; zurückgeben sondern den einzelnen Set-Optionen in diesem Rückgabetext nach einem Doppelpunkt entsprechende Zusatzinformationen anhängen.&lt;br /&gt;
Meist prüft man in den Modulen gar nicht auf die Option &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; sondern gibt generell bei unbekannten Optionen diesen Text zurück. Das Modul FHEMWEB ermittelt die Syntax eines Gerätes jedoch immer mit dem Befehl:&lt;br /&gt;
 set &#039;&#039;&amp;amp;lt;NAME&amp;amp;gt;&#039;&#039; ?&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
	return &amp;quot;Unknown argument $cmd, choose one of status:up,down power:on,off on:noArg off:noArg&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit Kommata getrennte Werte ergeben eine Drop-Down Liste, mit der der User die Werte auswählen kann&lt;br /&gt;
&amp;lt;pre&amp;gt;timer:30,120,300&lt;br /&gt;
mode:verbose,ultra,relaxed&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird kein Doppelpunkt zum Kommando angegeben, so wird eine Eingabezeile angezeigt, die die freie Eingabe eines Wertes erlaubt.&lt;br /&gt;
&lt;br /&gt;
Man kann jedoch die Eingabe-/Auswahlmöglichkeiten durch Widgets vereinfachen. Dazu gibt man hinter dem Doppelpunkt einen Widgetnamen und widgetspezifische Parameter an. Es existieren mehrere solcher Widgets in FHEMWEB. Die gebräuchlichsten sind:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Zusatz !! Beispiel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;noArg&#039;&#039;&#039; || &amp;lt;code&amp;gt;reset:noArg&amp;lt;/code&amp;gt;|| Es werden keine weiteren Argumente mehr benötigt. In so einem Fall wird bei der Auswahl keine Textbox oder ähnliches angezeigt, da keine weiteren Argumente für diesen Befehl notwendig sind.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;slider&#039;&#039;&#039;,&amp;lt;min&amp;gt;,&amp;lt;step&amp;gt;,&amp;lt;max&amp;gt; || &amp;lt;code&amp;gt;dim:slider,0,1,100&amp;lt;/code&amp;gt;|| Es wird ein Schieberegler angezeigt um den Parameter auszuwählen. Dabei werden als Zusatzparameter Minimum, Schrittweite und Maximum angegeben.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;colorpicker&#039;&#039;&#039; || &amp;lt;code&amp;gt;rgb:colorpicker,RGB&amp;lt;/code&amp;gt;|| Es wird ein Colorpicker angezeigt, der dem Anwender die Auswahl einer Farbe ermöglicht. Die genaue Parametersyntax kann man dem Artikel zum  [[Color#Colorpicker|Colorpicker]] entnehmen.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;multiple&#039;&#039;&#039; || &amp;lt;code&amp;gt;group:multiple,Telefon,Multimedia,Licht,Heizung&amp;lt;/code&amp;gt; || Es erscheint ein Auswahldialog, wo man verschiedene Werte durch klicken auswählen kann. Optional kann man in einem Freitext eigene Werte ergänzen. dieser Dialog wird bspw. bei der Raum-Auswahl (Attribut &amp;quot;room&amp;quot;) oder der Gruppen-Auswahl (Attribut &amp;quot;group&amp;quot;) in FHEMWEB genutzt. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;sortable&#039;&#039;&#039; || &amp;lt;code&amp;gt;command:sortable,monday,tuesday,...&amp;lt;/code&amp;gt; || Es erscheint ein Auswahldialog, wo man verschiedene Werte auswählen und sortieren kann. Man kann dabei Werte durch Klicken auswählen und durch Drag&#039;n&#039;Drop sortieren.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Es gibt noch weitere solcher Widgets. Eine genaue Auflistung dazu findet sich in der {{Link2CmdRef|Anker=widgetOverride}} unter widgetOverride zu FHEMWEB.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweise&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Damit in einer Eingabe bereits der aktuelle Wert vorbelegt bzw. in einer Auswahlliste der aktuelle Wert vorselektiert ist, muss es im Modul bzw. Gerät ein Reading mit dem gleichen Namen wie die Set-Option geben. Der Wert des gleichnamigen Readings wird dann als Vorbelegung / Vorselektion verwendet. &lt;br /&gt;
* Der User kann sich in der Raumübersicht nach wie vor via [[WebCmd|webCmd]] eine entsprechende Steuerung anlegen.&lt;br /&gt;
&lt;br /&gt;
==== X_Attr ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Attr ($$$$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $cmd, $name, $attrName, $attrValue  ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
 &lt;br /&gt;
	return $error;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Attr-Funktion dient der Prüfung von Attributen, welche über den &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt;-Befehl gesetzt werden können. Sobald versucht wird, ein Attribut für ein Gerät zu setzen, wird vorher die Attr-Funktion des entsprechenden Moduls aufgerufen um zu prüfen, ob das Attribut aus Sicht des Moduls korrekt ist.&lt;br /&gt;
Liegt ein Problem mit dem Attribut bzw. dem Wert vor, so muss die Funktion eine aussagekräftige Fehlermeldung zurückgeben, welche dem User angezeigt wird.&lt;br /&gt;
Sofern das übergebene Attribut samt Inhalt korrekt ist, gibt die Attr-Funktion den Wert &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurück. Erst dann wird das Attribut in der globalen Datenstruktur &amp;lt;code&amp;gt;%attr&amp;lt;/code&amp;gt; gespeichert und ist somit erst aktiv.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
X_Attr(@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $cmd, $name, $attrName, $attrValue ) = @_;&lt;br /&gt;
    &lt;br /&gt;
  	# $cmd  - Vorgangsart - kann die Werte &amp;quot;del&amp;quot; (löschen) oder &amp;quot;set&amp;quot; (setzen) annehmen&lt;br /&gt;
	# $name - Gerätename&lt;br /&gt;
	# $attrName/$attrValue sind Attribut-Name und Attribut-Wert&lt;br /&gt;
    &lt;br /&gt;
	if ($cmd eq &amp;quot;set&amp;quot;) {&lt;br /&gt;
		if ($aName eq &amp;quot;Regex&amp;quot;) {&lt;br /&gt;
			eval { qr/$aVal/ };&lt;br /&gt;
			if ($@) {&lt;br /&gt;
				Log3 $name, 3, &amp;quot;X ($name) - Invalid regex in attr $name $aName $aVal: $@&amp;quot;;&lt;br /&gt;
				return &amp;quot;Invalid Regex $aVal: $@&amp;quot;;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return undef;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zusätzlich ist es möglich auch übergebene Attributwerte zu verändern bzw. zu korrigieren, indem man im Parameterarray &amp;lt;code&amp;gt;@_&amp;lt;/code&amp;gt; den ursprünglichen Wert anpasst. Dies erfolgt im Beispiel über die Modifikation des Wertes mit Index 3 (entspricht dem 4. Element) im Parameterarray, also &amp;lt;code&amp;gt;$_[3]&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Da das Attribut zum Zeitpunkt des Aufrufs der Attr-Funktion noch nicht gespeichert ist, wird der neue Wert zu diesem Zeitpunkt noch nicht via [[DevelopmentModuleAPI#AttrVal|AttrVal()]] zurückgegeben. Erst, wenn die Attr-Funktion mit &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; beendet ist, wird der neue Wert in FHEM gespeichert und steht dann via AttrVal() zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
Die Attr-Funktion bekommt nicht den Hash der Geräteinstanz übergeben, da sie normalerweise keine Werte dort speichern muss, sondern lediglich das Attribut auf Korrektheit prüfen muss.&lt;br /&gt;
Im obigen Beispiel wird für ein Attribut mit Namen &amp;quot;Regex&amp;quot; geprüft ob der reguläre Ausdruck fehlerhaft ist. Sofern dieser OK ist, wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben und fhem.pl speichert den Wert des Attributs in &amp;lt;code&amp;gt;%attr&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Attributnamen mit Platzhaltern&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Falls man Attribute in der [[#X_Initialize|Initialize]]-Funktion mit Platzhaltern definiert (Wildcard-Attribute) wie z.B.:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
    $hash-&amp;gt;{AttrList} =&lt;br /&gt;
      &amp;quot;reading[0-9]*Name &amp;quot; .&lt;br /&gt;
    # usw.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann können Anwender Attribute wie reading01Name, reading02Name etc. setzen. Leider funktioniert das bisher nicht durch Klicken in der Web-Oberfläche, da FHEMWEB nicht alle denkbaren Ausprägungen in einem Dropdown anbieten kann. Der Benutzer muss solche Attribute manuell über den &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt;-Befehl eingeben.&lt;br /&gt;
&lt;br /&gt;
Man kann jedoch in der Attr-Funktion neu gesetzte Ausprägungen von Wildcard-Attributen an die gerätespezifische userattr-Variable anfügen. Dann können bereits gesetzte Attribute in FHEMWEB durch Klicken ausgewählt und geändert werden.&lt;br /&gt;
Dazu reicht ein Aufruf der Funktion [[DevelopmentModuleAPI#addToDevAttrList|addToDevAttrList()]]: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
    addToDevAttrList($name, $aName);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_Read ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Read ($)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die X_Read-Funktion wird aufgerufen, wenn ein dem Gerät zugeordneter Filedeskriptor (serielle Schnittstelle, TCP-Verbindung, ...) Daten zum Lesen bereitgestellt hat. Die Daten müssen nun eingelesen und interpretiert werden.&lt;br /&gt;
&lt;br /&gt;
Im folgenden Beispiel wird über eine serielle Schnittstelle (beziehungsweise über einen USB-To-Serial-Konverter) von einem angeschlossenen Gerät gelesen. Dazu werden die bisher verfügbaren Daten mit der Funktion [[DevIo#DevIo_SimpleRead()|DevIo_SimpleRead()]] gelesen. Da die Übertragung möglicherweise noch nicht vollständig ist, kann es sein, dass kurz darauf die X_Read-Funktion wieder aufgerufen wird und ein weiterer Teil oder der Rest der Daten gelesen werden kann.&lt;br /&gt;
Die Funktion muss daher prüfen ob schon alle erwarteten Daten angekommen sind und gegebenenfalls die bisher gelesenen Daten in einem eigenen Puffer (idealerweise in &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;) zwischenspeichern (siehe auch [[DevIo#Hinweis bei der Datenverarbeitung (Buffering)|DevIo]]). Im Beispiel ist dies &amp;lt;code&amp;gt;$hash-&amp;gt;{helper}{BUFFER}&amp;lt;/code&amp;gt; an den die aktuell gelesenen Daten angehängt werden, bis die folgende Prüfung ein für das jeweilige Protokoll vollständige Frame erkennt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Read($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
	&lt;br /&gt;
	# einlesen der bereitstehenden Daten&lt;br /&gt;
	my $buf = DevIo_SimpleRead($hash);		&lt;br /&gt;
	return &amp;quot;&amp;quot; if ( !defined($buf) );&lt;br /&gt;
	Log3 $name, 5, &amp;quot;X ($name) - received data: &amp;quot;.$buf;    &lt;br /&gt;
&lt;br /&gt;
	# Daten in Hex konvertieren und an den Puffer anhängen&lt;br /&gt;
	$hash-&amp;gt;{helper}{BUFFER} .= unpack (&#039;H*&#039;, $buf);	&lt;br /&gt;
	Log3 $name, 5, &amp;quot;X ($name) - current buffer content: &amp;quot;.$hash-&amp;gt;{helper}{BUFFER};&lt;br /&gt;
&lt;br /&gt;
	# prüfen, ob im Buffer ein vollständiger Frame zur Verarbeitung vorhanden ist.&lt;br /&gt;
	if ($hash-&amp;gt;{helper}{BUFFER} =~ &amp;quot;ff1002(.{4})(.*)1003(.{4})ff(.*)&amp;quot;) {&lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zu lesenden Nutzdaten können dann je nach Protokoll des Geräts beispielsweise an einer festgelegten Stelle im Frame (dann in &amp;lt;code&amp;gt;$hash-&amp;gt;{helper}{BUFFER}&amp;lt;/code&amp;gt;) stehen oder aus dem Kontext mit einem Regex-Match extrahiert werden und via [[DevelopmentModuleAPI#Readings_.2F_Events|Reading-Funktionen]] in Readings gespeichert werden (siehe unten).&lt;br /&gt;
&lt;br /&gt;
Der Rückgabewert der Read-Funktion wird nicht geprüft und hat daher keinerlei Bedeutung.&lt;br /&gt;
&lt;br /&gt;
==== X_Ready ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Ready ($)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
    &lt;br /&gt;
	return $success;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wird im Main-Loop aufgerufen falls das Modul in der globalen Liste &amp;lt;code&amp;gt;%readyfnlist&amp;lt;/code&amp;gt; existiert. Diese Funktion hat, je nachdem auf welchem OS FHEM ausgeführt wird, unterschiedliche Aufgaben:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;UNIX-artiges Betriebssystem:&#039;&#039;&#039; prüfen, ob eine Verbindung nach einem Verbindungsabbruch wieder aufgebaut werden kann. Sobald der Verbindungsaufbau erfolgreich war, muss die Funktion einen erfolgreichen Wahrheitswert zurückliefern (z.B. &amp;quot;1&amp;quot;) und den eigenen Eintrag entsprechend aus &amp;lt;code&amp;gt;%readyfnlist&amp;lt;/code&amp;gt; löschen.&lt;br /&gt;
* &#039;&#039;&#039;Windows-Betriebssystem:&#039;&#039;&#039; prüfen, ob lesbare Daten für ein serielles Device (via COM1, COM2, ...) vorliegen. Sofern lesbare Daten vorliegen, muss Funktion einen erfolgreichen Wahrheitswert zurückliefern (z.B. &amp;quot;1&amp;quot;). Zusätzlich dazu muss die Funktion, wie bei UNIX-artigen Betriebssystem, ebenfalls bei einem Verbindungsabbruch einen neuen Verbindungsversuch initiieren. Der Eintrag in &amp;lt;code&amp;gt;%readyfnlist&amp;lt;/code&amp;gt; bleibt solange erhalten, bis die Verbindung seitens FHEM beendet wird.&lt;br /&gt;
&lt;br /&gt;
Der Windows-spezifische Teil zur Datenprüfung ist dabei nur zu implementieren, wenn das Modul über eine serielle Verbindung kommuniziert.&lt;br /&gt;
&lt;br /&gt;
Bei der Nutzung des Moduls [[DevIo]] wird dem Modulentwickler der Umgang mit &amp;lt;code&amp;gt;%readyfnlist&amp;lt;/code&amp;gt; abgenommen, da DevIo sich selbst um die entsprechenden Einträge kümmert und diese selbstständig wieder entfernt.&lt;br /&gt;
&lt;br /&gt;
In der Regel sieht eine Ready-Funktion immer gleich aus.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Ready($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
      &lt;br /&gt;
	# Versuch eines Verbindungsaufbaus, sofern die Verbindung beendet ist.&lt;br /&gt;
	return DevIo_OpenDev($hash, 1, undef ) if ( $hash-&amp;gt;{STATE} eq &amp;quot;disconnected&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
	# This is relevant for Windows/USB only&lt;br /&gt;
	if(defined($hash-&amp;gt;{USBDev})) {&lt;br /&gt;
		my $po = $hash-&amp;gt;{USBDev};&lt;br /&gt;
		my ( $BlockingFlags, $InBytes, $OutBytes, $ErrorFlags ) = $po-&amp;gt;status;&lt;br /&gt;
		return ( $InBytes &amp;gt; 0 );&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_Notify ====&lt;br /&gt;
&lt;br /&gt;
Die X_Notify-Funktion wird aus der Funktion [[DevelopmentModuleAPI#DoTrigger|DoTrigger()]] in fhem.pl heraus aufgerufen sobald ein Modul Events erzeugt hat. Damit kann ein Modul auf Events anderer Module reagieren. Typische Beispiele sind dabei das [[FileLog]]-Modul oder das [[notify]]-Modul.&lt;br /&gt;
&lt;br /&gt;
Die Notify-Funktion bekommt dafür zwei Hash-Referenzen übergeben: den Hash des eigenen Geräts und den Hash des Geräts, dass die Events erzeugt hat. &lt;br /&gt;
Über den Hash des eigenen Geräts kann die Notify-Funktion beispielsweise auf die Internals oder Attribute des eigenen Geräts zugreifen.&lt;br /&gt;
Über den Hash des Gerätes und der [[DevelopmentModuleAPI#deviceEvents|deviceEvents()]]-Funktion kann man auf die generierten Events zugreifen. Über den zweiten Parameter dieser Routine lässt sich bestimmen ob für das Reading &amp;lt;code&amp;gt;state&amp;lt;/code&amp;gt; ein &#039;normales&#039; Event (d.h. in der form &amp;lt;code&amp;gt;state: &amp;lt;wert&amp;gt;&amp;lt;/code&amp;gt;) erzeugen soll (Wert: 1) oder ob z.b. aus Gründen der Rückwärtskompatibilität ein Event ohne &amp;lt;code&amp;gt;state: &amp;lt;/code&amp;gt; erzeugt werden soll. Falls dem Anwender die Wahl des verwendeten Formats überlassen werden soll ist hierzu das {{Link2CmdRef|Anker=addStateEvent|Lang=de|Label=addStateEvent-Attribut}} vorzusehen.&lt;br /&gt;
&lt;br /&gt;
Der direkte Zugriff auf &amp;lt;code&amp;gt;$hash-&amp;gt;{CHANGED}&amp;lt;/code&amp;gt; ist nicht mehr zu empfehlen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Notify($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($own_hash, $dev_hash) = @_;&lt;br /&gt;
  my $ownName = $own_hash-&amp;gt;{NAME}; # own name / hash&lt;br /&gt;
&lt;br /&gt;
  return &amp;quot;&amp;quot; if(IsDisabled($ownName)); # Return without any further action if the module is disabled&lt;br /&gt;
&lt;br /&gt;
  my $devName = $dev_hash-&amp;gt;{NAME}; # Device that created the events&lt;br /&gt;
&lt;br /&gt;
  my $events = deviceEvents($dev_hash,1);&lt;br /&gt;
  return if( !$events );&lt;br /&gt;
&lt;br /&gt;
  foreach my $event (@{$events}) {&lt;br /&gt;
    $event = &amp;quot;&amp;quot; if(!defined($event));&lt;br /&gt;
&lt;br /&gt;
    # Examples:&lt;br /&gt;
    # $event = &amp;quot;readingname: value&amp;quot; &lt;br /&gt;
    # or&lt;br /&gt;
    # $event = &amp;quot;INITIALIZED&amp;quot; (for $devName equal &amp;quot;global&amp;quot;)&lt;br /&gt;
    #&lt;br /&gt;
    # processing $event with further code&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Begrenzung der Aufrufe auf bestimmte Geräte&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da die Notify-Funktion für jedes definierte Gerät mit all seinen Events aufgerufen wird, muss sie in einer Schleife jedesmal prüfen und entscheiden, ob es mit dem jeweiligen Event etwas anfangen kann. Ein Gerät, dass die Notify-Funktion implementiert, sieht dafür typischerweise einen regulären Ausdruck vor, welcher für die Filterung verwendet wird.&lt;br /&gt;
&lt;br /&gt;
Wenn man nur gezielt von bestimmten Definitionen Events erhalten will, kann man diese auch in Form einer {{Link2CmdRef|Lang=de|Anker=devspec|Label=devspec}} in &amp;lt;code&amp;gt;$hash-&amp;gt;{NOTIFYDEV}&amp;lt;/code&amp;gt; angeben. Bspw. kann man in der Define-Funktion diesen Wert setzen. Dadurch wird die Notify-Funktion nur aufgerufen wenn eine der Definitionen, auf welche die devspec passt, ein Event erzeugt hat. Ein typischer Fall ist die Begrenzung von Events auf &amp;quot;global&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
in der Define-Funktion:&lt;br /&gt;
&lt;br /&gt;
$hash-&amp;gt;{NOTIFYDEV} = &amp;quot;global&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{NOTIFYDEV} = &amp;quot;global,Definition_A,Definition_B&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{NOTIFYDEV} = &amp;quot;global,TYPE=CUL_HM&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies schont insbesondere bei grossen Installationen Ressourcen, da die Notify-Funktion nicht sämtliche Events, sondern nur noch Events der gewünschten Definitionen erhält. Dadurch erfolgen deutlich weniger Aufrufe der Notify-Funktion, was Systemressourcen schont.&lt;br /&gt;
&lt;br /&gt;
Sofern in der [[#X_Define|Define-Funktion]] eine Regexp als Argument übergeben wird, die ähnlich wie beim Modul [[notify]] auf Events wie &amp;lt;code&amp;gt;&amp;amp;lt;Definitionsname&amp;amp;gt;&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;&amp;amp;lt;Definitionsname&amp;amp;gt;:&amp;amp;lt;Event&amp;amp;gt;&amp;lt;/code&amp;gt; reagiert, so sollte man in der Define-Funktion die Funktion [[DevelopmentModuleAPI#notifyRegexpChanged|notifyRegexpChanged()]] verwenden. Diese versucht einen passenden Eintrag für &amp;lt;code&amp;gt;$hash-&amp;gt;{NOTIFYDEV}&amp;lt;/code&amp;gt; basierend auf der übergebenen Regexp zu setzen, sofern dies möglich ist.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;Reihenfolge für den Aufruf der Notify-Funktion beeinflussen&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sobald ein Event ausgelöst wurde, stellt sich FHEM eine Liste aller relevanten Geräte-Hashes zusammen, welche via Notify-Funktion prüfen müssen, ob das Event relevant ist. Dabei wird die Liste nach &amp;lt;code&amp;gt;$hash-&amp;gt;{NTFY_ORDER}&amp;lt;/code&amp;gt; sortiert. Diese enthält ein Order-Präfix in Form einer Ganzzahl, sowie den Namen der Definition (Bsp: &amp;lt;code&amp;gt;&#039;&#039;&#039;50&#039;&#039;&#039;-Lampe_Wohnzimmer&amp;lt;/code&amp;gt;). Dadurch kann man jedoch nicht sicherstellen, dass Events von bestimmten Modulen zuerst verarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
Wenn das eigene Modul bei der Eventverarbeitung gegenüber den anderen Modulen eine bestimmte Reihenfolge einhalten muss, kann man in der [[#X_Initialize|Initialize]]-Funktion durch Setzen von &amp;lt;code&amp;gt;$hash-&amp;gt;{NotifyOrderPrefix}&amp;lt;/code&amp;gt; diese Reihenfolge beeinflussen. Standardmäßig werden Module immer mit einem Order-Präfix von &amp;quot;50-&amp;quot; in FHEM registriert. Durch die Veränderung dieses Präfixes kann man das eigene Modul in der Reihenfolge gegenüber anderen Modulen bei der Eventverarbeitung beeinflussen. &lt;br /&gt;
&lt;br /&gt;
Beispiel:&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;
	...&lt;br /&gt;
	&lt;br /&gt;
	$hash-&amp;gt;{NotifyOrderPrefix} = &amp;quot;45-&amp;quot;  # Alle Definitionen des Moduls X werden bei der Eventverarbeitung zuerst geprüft&lt;br /&gt;
	&lt;br /&gt;
	# oder...&lt;br /&gt;
	&lt;br /&gt;
	$hash-&amp;gt;{NotifyOrderPrefix} = &amp;quot;55-&amp;quot;  # Alle Definitionen des Moduls X werden bei der Eventverarbeitung als letztes geprüft&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
Da dieses Präfix bei eventverarbeitenden Definitionen in &amp;lt;code&amp;gt;$hash-&amp;gt;{NTFY_ORDER}&amp;lt;/code&amp;gt; dem Definitionsnamen vorangestellt wird bewirkt es bei einer normalen aufsteigenden Sortierung nach &amp;lt;code&amp;gt;$hash-&amp;gt;{NTFY_ORDER}&amp;lt;/code&amp;gt; eine veränderte Reihenfolge. Alle Module die in der Initialize-Funktion nicht &amp;lt;code&amp;gt;$hash-&amp;gt;{NotifyOrderPrefix}&amp;lt;/code&amp;gt; explizit setzen, werden mit &amp;quot;50-&amp;quot; als Standardwert vorbelegt.&lt;br /&gt;
&lt;br /&gt;
==== X_Rename ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Rename ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $new_name, $old_name) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Rename-Funktion wird ausgeführt, nachdem ein Gerät umbenannt wurde. Auf diese Weise kann ein Modul auf eine Namensänderung reagieren, wenn das Gerät &amp;lt;code&amp;gt;$old_name&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;$new_name&amp;lt;/code&amp;gt; umbenannt wurde. Ein typischer Fall ist das Umsetzen der Namensänderungen bei Daten die mittels [[DevelopmentModuleAPI#setKeyValue|setKeyValue()]] gespeichert wurden. Hierbei müssen die Daten, welche unter dem alten Namen gespeichert sind, auf den neuen Namen geändert werden.&lt;br /&gt;
&lt;br /&gt;
Der Rename-Funktion wird lediglich der alte, sowie der neue Gerätename übergeben. Der Rückgabewert wird nicht ausgewertet.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Rename ($)&lt;br /&gt;
{&lt;br /&gt;
	my ( $new_name, $old_name ) = @_;&lt;br /&gt;
&lt;br /&gt;
	my $old_index = &amp;quot;Module_X_&amp;quot;.$old_name.&amp;quot;_data&amp;quot;;&lt;br /&gt;
	my $new_index = &amp;quot;Module_X_&amp;quot;.$new_name.&amp;quot;_data&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
	my ($err, $data) = getKeyValue($old_index);&lt;br /&gt;
	return undef unless(defined($old_pwd));&lt;br /&gt;
&lt;br /&gt;
	setKeyValue($new_index, $data);&lt;br /&gt;
	setKeyValue($old_index, undef);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_Shutdown ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Shutdown ($)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Mit der X_Shutdown Funktion kann ein Modul Aktionen durchführen bevor FHEM gestoppt wird. Dies kann z.B. der ordnungsgemäße Verbindungsabbau mit dem physikalischen Gerät sein (z.B. Session beenden, Logout, etc.). Als Übergabeparameter wird der Geräte-Hash bereitgestellt. Der Rückgabewert einer Shutdown-Funktion wird nicht ausgewertet und ist daher irrelevant.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Shutdown($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
&lt;br /&gt;
	# Verbindung schließen&lt;br /&gt;
	DevIo_CloseDev($hash);&lt;br /&gt;
	return undef;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Funktionen für zweistufiges Modulkonzept ===&lt;br /&gt;
&lt;br /&gt;
Für das [[#Zweistufiges_Modell_für_Module|zweistufige Modulkonzept]] gibt es weiterhin:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;text-align:left&amp;quot; | Funktionsname !! style=&amp;quot;text-align:left&amp;quot; class=&amp;quot;unsortable&amp;quot; | Kurzbeschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Parse|X_Parse]] || Zustellen von Daten via [[DevelopmentModuleAPI#Dispatch|Dispatch()]] vom physischen Modul zum logischen Modul zwecks der Verarbeitung.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Write|X_Write]]|| Zustellen von Daten via [[DevelopmentModuleAPI#Dispatch|IOWrite()]] vom logischen zum physischen Modul um diese an die Hardware weiterzureichen.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Fingerprint|X_Fingerprint]] || Rückgabe eines &amp;quot;Fingerabdrucks&amp;quot; einer Nachricht. Dient der Erkennung von Duplikaten im Rahmen von [[DevelopmentModuleAPI#Dispatch|Dispatch()]]. Kann im physischen, als auch logischen Modul benutzt werden.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Für das zweistufige Modulkonzept muss in einem logischen Modul eine [[#X_Parse|Parse]]-Funktion im Modul-Hash registriert werden. In einem physikalischen Modul muss eine [[#X_Write|Write]]-Funktion registriert sein. Diese dienem dem Datenaustausch in beide Richtungen und werden von dem jeweils anderen Modul indirekt aufgerufen.&lt;br /&gt;
&lt;br /&gt;
In der [[#X_Initialize|Initialize]]-Funktion werden diese wie folgt definiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$hash-&amp;gt;{ParseFn}       = &amp;quot;X_Parse&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{WriteFn}       = &amp;quot;X_Write&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{FingerprintFn} = &amp;quot;X_Fingerprint&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Funktionen werden in diesem Abschnitt genauer beschrieben.&lt;br /&gt;
&lt;br /&gt;
==== X_Parse ====&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;u&amp;gt;&#039;&#039;&#039;ACHTUNG&#039;&#039;&#039;:&amp;lt;/u&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
Dieser Abschnitt geht davon aus, dass das Modul mit dem Namen &amp;quot;X&amp;quot; ein &#039;&#039;&#039;logisches Modul&#039;&#039;&#039; im Sinne des zweistufigen Modulkonzepts ist, also Daten mit einem übergeordneten, physikalischen Modul austauscht.}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Parse ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $io_hash, $message) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
	&lt;br /&gt;
	return $found;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion X_Parse wird aufgerufen, sobald von dem IO-Gerät &amp;lt;code&amp;gt;$io_hash&amp;lt;/code&amp;gt; eine Nachricht &amp;lt;code&amp;gt;$message&amp;lt;/code&amp;gt; via [[DevelopmentModuleAPI#Dispatch|Dispatch()]] zur Verarbeitung angefragt wird. Die Parse-Funktion muss dann prüfen, zu welcher Gerätedefinition diese Nachricht gehört und diese entsprechend verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Üblicherweise enthält eine Nachricht immer eine Komponente durch welche sich die Nachricht einem Gerät zuordnen lässt (z.B. Adresse, ID-Nummer, ...). Eine solche Identifikation sollte man im Rahmen der [[#X_Define|Define]]-Funktion im logischen Modul an geeigneter Stelle speichern, um in der Parse-Funktion eine einfache Zuordnung von Adresse/ID einer Nachricht zur entsprechenden Gerätedefinition zu haben. Dazu wird in der Regel im Modul-Hash im modulspezifischen Berreich eine Liste &amp;lt;code&amp;gt;defptr&amp;lt;/code&amp;gt; (Definition Pointer) geführt, welche jede eindeutige Adresse/ID dem entsprechenden Geräte-Hash zuordnet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sub X_Define ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $def) = @_;&lt;br /&gt;
	my @a = split(&amp;quot;[ \t][ \t]*&amp;quot;, $def);&lt;br /&gt;
	my $name = $a[0];&lt;br /&gt;
&lt;br /&gt;
	...&lt;br /&gt;
	&lt;br /&gt;
	# erstes Argument ist die eindeutige Geräteadresse&lt;br /&gt;
	my $address = $a[1];&lt;br /&gt;
&lt;br /&gt;
	# Adresse rückwärts dem Hash zuordnen (für ParseFn)&lt;br /&gt;
	$modules{X}{defptr}{$address} = $hash;&lt;br /&gt;
&lt;br /&gt;
	...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf Basis dieses Definition Pointers kann die Parse-Funktion nun sehr einfach prüfen, ob für die empfangene Nachricht bereits eine entsprechende Gerätedefinition existiert. Sofern diese existiert, kann die Nachricht entsprechend verarbeitet werden. Sollte jedoch keine passende Gerätedefinition zu der empfangenen Nachricht existieren, so muss die Parse-Funktion den Gerätenamen &amp;quot;UNDEFINED&amp;quot; zusammen mit den Argumenten für einen &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Befehl zurückgeben, welcher ein passendes Gerät in FHEM anlegen würde (durch [[autocreate]]).&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sub X_Parse ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $io_hash, $message) = @_;&lt;br /&gt;
	&lt;br /&gt;
	# Die Stellen 10-15 enthalten die eindeutige Identifikation des Geräts&lt;br /&gt;
	my $address = substr($message, 10, 5); &lt;br /&gt;
&lt;br /&gt;
	# wenn bereits eine Gerätedefinition existiert (via Definition Pointer aus Define-Funktion)&lt;br /&gt;
	if(my $hash = $modules{X}{defptr}{$address}) &lt;br /&gt;
	{&lt;br /&gt;
		...  # Nachricht für $hash verarbeiten&lt;br /&gt;
		&lt;br /&gt;
		# Rückgabe des Gerätenamens, für welches die Nachricht bestimmt ist.&lt;br /&gt;
		return $hash-&amp;gt;{NAME}; &lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		# Keine Gerätedefinition verfügbar&lt;br /&gt;
		# Daher Vorschlag define-Befehl: &amp;lt;NAME&amp;gt; &amp;lt;MODULNAME&amp;gt; &amp;lt;ADDRESSE&amp;gt;&lt;br /&gt;
		return &amp;quot;UNDEFINED X_&amp;quot;.$address.&amp;quot; X $address&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_Write ====&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;u&amp;gt;&#039;&#039;&#039;ACHTUNG&#039;&#039;&#039;:&amp;lt;/u&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
Dieser Abschnitt geht davon aus, dass das Modul mit dem Namen &amp;quot;X&amp;quot; ein &#039;&#039;&#039;physisches Modul&#039;&#039;&#039; im Sinne des zweistufigen Modulkonzepts ist, also Daten mit untergeordneten logischen Modulen austauscht. }}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Write ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, @arguments) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
	&lt;br /&gt;
	return $return;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Write-Funktion wird durch die Funktion [[DevelopmentModuleAPI#IOWrite|IOWrite()]] aufgerufen, sobald eine logische Gerätedefinition Daten per IO-Gerät an die Hardware übertragen möchte. Dazu kümmert sich die Write-Funktion um die Übertragung der Nachricht in geeigneter Form an die verbundene Hardware. Als Argumente wird der Hash des physischen Gerätes übertragen, sowie alle weiteren Argumente, die das logische Modul beim Aufruf von IOWrite() mitgegeben hat. Im Normalfall ist das ein Skalar mit der zu sendenden Nachricht in Textform. Es kann aber auch sein, dass weitere Daten zum Versand notwendig sind (evtl. Schlüssel, Session-Key, ...). Daher ist die Parametersyntax einer zu schreibenden Nachricht via IOWrite-/Write-Funktion zwischen logischem und physikalischen Modul abzustimmen.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Write ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $message, $address) = @_;&lt;br /&gt;
	&lt;br /&gt;
	DevIo_SimpleWrite($hash, $address.$message, 2);&lt;br /&gt;
&lt;br /&gt;
	return undef;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_Fingerprint ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Fingerprint($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $io_name, $msg ) = @_;&lt;br /&gt;
 &lt;br /&gt;
	...&lt;br /&gt;
 &lt;br /&gt;
	return ( $io_name, $fingerprint );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Fingerprint-Funktion dient der Erkennung von Duplikaten empfangener Nachrichten. Diese Funktion kann dabei sowohl im physischen, als auch im logischen Modul implementiert sein. Je nachdem auf welcher Ebene man für eine Nachricht einen Fingerprint bilden kann. &lt;br /&gt;
&lt;br /&gt;
Als Parameter wird der Name des IO-Geräts &amp;lt;code&amp;gt;$io_name&amp;lt;/code&amp;gt; übergeben, sowie die Nachricht &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt;, welche empfangen wurde. Nun muss aus dieser Nachricht ein eindeutiger Fingerprint gebildet werden. Dies bedeutet, dass alle variablen Inhalte, die aufgrund des Empfangs dieser Nachricht über unterschiedliche IO-Geräte enthalten sein können, entfernt werden müssen. Dies können bspw. Empfangsadressen von IO-Geräten sein oder Session-ID&#039;s die in der Nachricht enthalten sind. Alle Fingerprints sämtlicher Nachrichten, die innerhalb der letzten 500 Millisekunden (konfigurierbar via &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; Attribut &amp;lt;code&amp;gt;dupTimeout&amp;lt;/code&amp;gt;) empfangen wurden, werden gegen diesen generierten Fingerprint getestet. Sollte innerhalb dieser Zeit bereits eine Nachricht mit diesem Fingerprint verarbeitet worden sein, so wird sie als Duplikat erkannt und nicht weiter verarbeitet. In diesem Fall gibt [[DevelopmentModuleAPI#Dispatch|Dispatch()]] den Namen der Gerätedefinition zurück, welche eine Nachricht mit dem selben Fingerprint bereits verarbeitet hat. Es erfolgt dann kein Aufruf der [[#X_Parse|Parse]]-Funktion.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Fingerprint($$)&lt;br /&gt;
{&lt;br /&gt;
  my ( $io_name, $msg ) = @_;&lt;br /&gt;
&lt;br /&gt;
  substr( $msg, 2, 2, &amp;quot;--&amp;quot; ); # entferne Empfangsadresse&lt;br /&gt;
  substr( $msg, 4, 1, &amp;quot;-&amp;quot; );  # entferne Hop-Count&lt;br /&gt;
&lt;br /&gt;
  return ( $io_name, $msg );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es wird zuerst, sofern implementiert, die Fingerprint-Funktion des physischen Moduls aufgerufen. Sollte sich hierdurch kein Duplikat erkennen lassen, wird die Fingerprint-Funktion jedes möglichen geladenen logischen Moduls aufgerufen, sofern implementiert. &lt;br /&gt;
&lt;br /&gt;
Sollte sowohl im physischen, als auch im logischen Modul keine Fingerprint-Funktion implementiert sein, so wird keinerlei Duplikatserkennung durchgeführt.&lt;br /&gt;
&lt;br /&gt;
=== FHEMWEB-spezifische Funktionen ===&lt;br /&gt;
&lt;br /&gt;
FHEMWEB bietet Modulautoren die Möglichkeit an durch spezielle Funktionsaufrufe in Modulen, eigene HTML-Inhalte zu verwenden. Dadurch können in Verbindung mit zusätzlichem JavaScript komplexe Dialoge/Inhalte/Steuermöglichkeiten dargestellt werden. &lt;br /&gt;
&lt;br /&gt;
Eine genaue Auflistung aller FHEMWEB-spezifischen Funktionsaufrufe gibt es in dem separaten Artikel [[DevelopmentFHEMWEB]]&lt;br /&gt;
&lt;br /&gt;
=== sonstige Funktionen ===&lt;br /&gt;
&lt;br /&gt;
In diesem Abschnitt werden weitere Funktionen behandelt die zum Teil aus FHEM, aber auch aus anderen Modulen aufgerufen werden. Sie sind dabei nur in speziellen Anwendungsfällen relevant. Hier eine Auflistung aller sonstigen Modulfunktionen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Funktionsname !! class=&amp;quot;unsortable&amp;quot; | Kurzbeschreibung&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_DbLog_split|X_DbLog_split]] || Wird durch das Modul 93_DbLog.pm aufgerufen. Dient dem korrekten Split eines moduleigenen Events in Name/Wert/Einheit für die Nutzung einer Datenbank.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Except|X_Except]]|| Wird aufgerufen, sobald ein ein geöffneter Filedescriptor in [[#Wichtige_globale_Variablen_aus_fhem.pl|&amp;lt;code&amp;gt;%selectlist&amp;lt;/code&amp;gt;]], der unter &amp;lt;code&amp;gt;$hash-&amp;gt;{EXCEPT_FD}&amp;lt;/code&amp;gt; im Geräte-Hash gesetzt ist, einen Interrupt bzw. Exception auslöst.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Copy|X_Copy]]|| Wird durch das Modul 98_copy.pm aufgerufen im Rahmen des &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;-Befehls sobald ein Gerät kopiert wurde.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_State|X_State]]|| Wird aufgerufen im Rahmen des &amp;lt;code&amp;gt;setstate&amp;lt;/code&amp;gt;-Befehls bevor der Status einer Gerätedefinition bzw. eines zugehörigen Readings gesetzt wird.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_AsyncOutput|X_AsyncOutput]]|| Nur Relevant für Module die via [[TcpServerUtils]] eine Client-Verbindung zu FHEM ermöglichen (z.B. FHEMWEB und telnet). Ermöglicht die asynchrone Ausgabe von Daten via [[DevelopmentModuleAPI#asyncOutput|asyncOutput()]] an einen einzelnen verbundenen Client.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_ActivateInform|X_ActivateInform]]|| Nur Relevant für Module die via [[TcpServerUtils]] eine Client-Verbindung zu FHEM ermöglichen (z.B. FHEMWEB und telnet). Ermöglicht das aktivieren des inform-Mechanismus zum senden von Events für einen einzelnen verbundenen Client.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Authorize|X_Authorize]]|| Wird aufgerufen im Rahmen von [[DevelopmentModuleAPI#Authorized|Authorized()]] um eine gewünschte Vorgangs-Art zu autorisieren.&lt;br /&gt;
|-&lt;br /&gt;
| [[#X_Authenticate|X_Authenticate]]||  Wird aufgerufen im Rahmen von [[DevelopmentModuleAPI#Authenticate|Authenticate()]] um eine Authentifizierung zu prüfen und ggf. zu genehmigen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In der [[#X_Initialize|Initialize]]-Funktion werden diese wie folgt definiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$hash-&amp;gt;{DbLog_splitFn} = &amp;quot;X_DbLog_split&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{ExceptFn} = &amp;quot;X_Except&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{CopyFn} = &amp;quot;X_Copy&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{AsyncOutputFn} = &amp;quot;X_AsyncOutput&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{ActivateInformFn} = &amp;quot;X_ActivateInform&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{StateFn} = &amp;quot;X_State&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{AuthorizeFn} = &amp;quot;X_Authorize&amp;quot;;&lt;br /&gt;
$hash-&amp;gt;{AuthenticateFn} = &amp;quot;X_Authenticate&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Diese Funktionen werden in diesem Abschnitt genauer beschrieben.&lt;br /&gt;
==== X_DbLog_split ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_DbLog_split ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $event, $device_name ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
    &lt;br /&gt;
	return  ( $reading, $value, $unit );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die DbLog_split-Funktion wird durch das Modul [[DbLog]] aufgerufen, sofern der Nutzer DbLog benutzt. Sofern diese Funktion implementiert ist, kann der Modulautor das Auftrennen von Events in den Reading-Namen, -Wert und der Einheit selbst steuern. Andernfalls nimmt DbLog diese Auftrennung selber mittels Trennung durch Leerzeichen sowie vordefinierten Regeln zu verschiedenen Modulen vor. Je nachdem, welche Readings man in seinem Modul implementiert, passt diese standardmäßige Trennung jedoch nicht immer.&lt;br /&gt;
&lt;br /&gt;
Der Funktion werden folgende Eingangsparameter übergeben:&lt;br /&gt;
# Das generierte Event (Bsp: &amp;lt;code&amp;gt;temperature: 20.5 °C&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Der Name des Geräts, welche das Event erzeugt hat (Bsp: &amp;lt;code&amp;gt;Temperatursensor_Wohnzimmer&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Es ist nicht möglich in der DbLog_split-Funktion auf die verarbeitende DbLog-Definition zu referenzieren.&lt;br /&gt;
&lt;br /&gt;
Als Rückgabewerte muss die Funktion folgende Werte bereitstellen:&lt;br /&gt;
# Name des Readings (Bsp: &amp;lt;code&amp;gt;temperature&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Wert des Readings (Bsp: &amp;lt;code&amp;gt;20.5&amp;lt;/code&amp;gt;)&lt;br /&gt;
# Einheit des Readings (Bsp: &amp;lt;code&amp;gt;°C&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_DbLog_splitFn($$)&lt;br /&gt;
{&lt;br /&gt;
	my ($event, $device) = @_;&lt;br /&gt;
	my ($reading, $value, $unit);&lt;br /&gt;
        my $devhash = $defs{$device}&lt;br /&gt;
&lt;br /&gt;
	if($event =~ m/temperature/) {&lt;br /&gt;
	   $reading = &#039;temperature&#039;;&lt;br /&gt;
	   $value = substr($event,12,4);&lt;br /&gt;
	   $unit = &#039;°C&#039;;&lt;br /&gt;
	}   &lt;br /&gt;
        &lt;br /&gt;
        return ($reading, $value, $unit);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_Except ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Except ($)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die X_Except-Funktion wird durch fhem.pl aufgerufen, wenn die Gerätedefinition &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;[[#Wichtige_globale_Variablen_aus_fhem.pl|%selectlist]]&amp;lt;/code&amp;gt; aufgeführt ist und der Filedeskriptor in &amp;lt;code&amp;gt;$hash-&amp;gt;{EXCEPT_FD}&amp;lt;/code&amp;gt; eine Exception bzw. Interrupt auslöst. &lt;br /&gt;
&lt;br /&gt;
Der Rückgabewert wird nicht ausgewertet und ist daher irrelevant.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
use IO::File;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
sub X_Except ($)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	# Filehandle aus Filedescriptor erstellen&lt;br /&gt;
	my $filehandle = IO::File-&amp;gt;new_from_fd($hash-&amp;gt;{EXCEPT_FD}, &#039;r&#039;);&lt;br /&gt;
	seek($filehandle,0,0);	&lt;br /&gt;
&lt;br /&gt;
	# aktuellen Inhalt auslesen&lt;br /&gt;
	my $current_value = $filehandle-&amp;gt;getline;&lt;br /&gt;
&lt;br /&gt;
	if($current_value eq &amp;quot;1&amp;quot;)&lt;br /&gt;
	{&lt;br /&gt;
		...&lt;br /&gt;
	}&lt;br /&gt;
	else&lt;br /&gt;
	{&lt;br /&gt;
		...&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_Copy ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Copy ($)&lt;br /&gt;
{&lt;br /&gt;
	my ( $old_name, $new_name ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die X_Copy-Funktion wird durch das Modul [[copy]] aufgerufen nachdem ein Nutzer eine Gerätedefinition über den Befehl &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt; kopiert hat. Dazu werden als Funktionsparameter die Definitionsnamen der alten und neuen Gerätedefinition übergeben. Es dient dazu zusätzliche Daten aus der zu kopierenden Gerätedefinition in die neue Definition zu übernehmen. Der Befehl &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt; überträgt lediglich &amp;lt;code&amp;gt;$hash-&amp;gt;{DEF}&amp;lt;/code&amp;gt; in die neue Definition sowie sämtliche gesetzte Attribute. Weitere Daten müssen dann durch die X_Copy-Funktion übertragen werden. &lt;br /&gt;
&lt;br /&gt;
Die X_Copy-Funktion wird erst nach dem erfolgtem Kopiervorgang aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Der Rückgabewert wird nicht ausgewertet und ist daher irrelevant.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sub X_Copy ($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $old_name, $new_name ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	my $old_hash = $defs{$old_name};&lt;br /&gt;
	my $new_hash = $defs{$new_name};&lt;br /&gt;
&lt;br /&gt;
	# copy also temporary session key&lt;br /&gt;
	$new_hash-&amp;gt;{helper}{SESSION_KEY} = $old_hash-&amp;gt;{helper}{SESSION_KEY};&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_AsyncOutput ====&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039; Diese Funktion ist nur relevant, wenn man ein Frontend-Modul erstellt über das FHEM von einem Anwender bedient werden kann (FHEMWEB, telnet, yowsup, telegram, alexa-fhem, homebridge-fhem, tabletui, ...).}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_AsyncOutput ($)&lt;br /&gt;
{&lt;br /&gt;
	my ( $client_hash, $text ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
&lt;br /&gt;
	return $error;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion X_AsyncOutput wird durch [[DevelopmentModuleAPI#asyncOutput|asyncOutput()]] von anderen Modulen aufgerufen. Es erlaubt diesen anderen Modulen die Ausgabe von asynchronen Befehlsergebnissen &amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt; zuvor ausgeführter set-/get-Befehle an den entsprechenden Client (identifiziert durch den Client-Hash &amp;lt;code&amp;gt;$client_hash&amp;lt;/code&amp;gt; der temporären Definition) zurückzugeben. &lt;br /&gt;
&lt;br /&gt;
Wenn ein Client einen set-/get-Befehl ausführt, wird der Client-Hash bei der Ausführung dieser Befehle an die jeweiligen Module übermittelt. Sobald ein Befehl ausgeführt wird, der seine Ausgabe asynchron ausführen möchte und die Client-Verbindung des Server-Moduls dies unterstützt (&amp;lt;code&amp;gt;$client_hash-&amp;gt;{canAsyncOutput}&amp;lt;/code&amp;gt; ist gesetzt), merkt sich das befehlsausführende Modul den Client-Hash und gibt das Ergebnis des Befehls zu späterer Zeit via [[DevelopmentModuleAPI#asyncOutput|asyncOutput()]] an den ursprünglichen Client zurück. Die Funktion X_AsyncOutput des Server-Moduls kümmert sich darum das Ergebnis dem entsprechenden Client in der notwendigen Form zuzustellen.&lt;br /&gt;
&lt;br /&gt;
Der Rückgabewert von X_AsyncOutput() wird als Rückgabewert für asyncOutput() verwendet. Man kann hier im Fehlerfall eine Fehlermeldung angeben und im Erfolgsfall &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt;. Der Rückgabewert wird aber aktuell nicht ausgewertet.&lt;br /&gt;
&lt;br /&gt;
==== X_ActivateInform====&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039; Diese Funktion ist nur relevant, wenn man ein Frontend-Modul erstellt über das FHEM von einem Anwender bedient werden kann (FHEMWEB, telnet, yowsup, telegram, alexa-fhem, homebridge-fhem, tabletui, ...).}}&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_ActivateInform($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $client_hash, $arg ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion X_ActivateInform wird aktuell nur durch den [[update]]-Befehl aufgerufen, sofern ein Client eines Frontend-Moduls diesen Befehl aufgerufen hat um den Inform-Mechanismus (Senden von Events) zu aktivieren. Dadurch wird im Falle von [[update]] die umgehende Anzeige der Logmeldungen für den ausführenden Client aktiviert. In [[FHEMWEB]] geschieht das über den Event-Monitor, bei telnet mit der direkten Ausgabe.&lt;br /&gt;
&lt;br /&gt;
Da diese Funktion aktuell nur speziell für den update-Befehl implementiert ist, kann man aktuell keine genaue Angaben zu den möglichen Werten von &amp;lt;code&amp;gt;$arg&amp;lt;/code&amp;gt; geben. Dieser Parameter dient dazu genauer zu spezifizieren was exakt an Events an den entsprechenden Client &amp;lt;code&amp;gt;$client_hash&amp;lt;/code&amp;gt; zu senden ist. Aktuell wird dazu die Parametersyntax des inform-Befehls verwendet (on|off|log|raw|timer|status).&lt;br /&gt;
&lt;br /&gt;
==== X_State ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_State($$$$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $time, $readingName, $value ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
&lt;br /&gt;
	return $error;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die X_State-Funktion wird durch fhem.pl aufgerufen, sobald über den Befehl &amp;lt;code&amp;gt;setstate&amp;lt;/code&amp;gt; versucht wird ein Wert für ein Reading oder den Status (&amp;lt;code&amp;gt;$hash-&amp;gt;{STATE}&amp;lt;/code&amp;gt;) einer Gerätedefinition zu setzen. Dieser Befehl wird primär beim Starten von FHEM aufgerufen sobald das State-File eingelesen wird. Je nachdem, ob im gegebenen Fall ein Reading oder der Definitionsstatus gesetzt wird, haben die Übergabeparameter verschiedene Werte:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Funktionsparameter!! Wert beim Setzen eines Readings !! Wert beim Setzen eines Definitionsstatus&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; || colspan=2 align=center | Die Hashreferenz der betreffenden Gerätedefinition&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$time&amp;lt;/code&amp;gt;|| Der Zeitstempel auf welchen das Reading &amp;lt;code&amp;gt;$readingName&amp;lt;/code&amp;gt; gesetzt werden soll. Das Ergebnis entspricht dem Rückgabewert der Funktion || Der aktuelle Zeitstempel zum jetzigen Zeitpunkt.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$readingName&amp;lt;/code&amp;gt;|| Der Name des Readings, welches auf einen neuen Wert gesetzt werden soll. || Statischer Wert &amp;quot;STATE&amp;quot; um anzuzeigen, dass es sich um den Definitionsstatus handelt, welcher gesetzt werden soll (&amp;lt;code&amp;gt;$hash-&amp;gt;{STATE}&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;$value&amp;lt;/code&amp;gt; || Den Wert, welchen das Reading &amp;lt;code&amp;gt;$readingName&amp;lt;/code&amp;gt; annehmen soll. || Den Wert, welchen die Gerätedefinition als Status annehmen soll.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Wenn via &amp;lt;code&amp;gt;setstate&amp;lt;/code&amp;gt; ein Reading gesetzt wird, kann die X_State-Funktion das Setzen dieses Readings durch die Rückgabe einer aussagekräftigen Fehlermeldung unterbinden. Sofern &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben wird, wird das entsprechende Reading auf den übergebenen Status gesetzt.&lt;br /&gt;
&lt;br /&gt;
Wenn via &amp;lt;code&amp;gt;setstate&amp;lt;/code&amp;gt; der Definitionsstatus gesetzt wird, wird die X_State-Funktion erst nach dem Setzen des Status aufgerufen. Man kann dabei zwar eine Fehlermeldung zurückgeben, der Status wird aber dennoch übernommen. Die Fehlermeldung wird lediglich dem Nutzer angezeigt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_State($$$$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $time, $readingName, $value ) = @_;&lt;br /&gt;
&lt;br /&gt;
	return undef if($readingName &amp;quot;STATE&amp;quot; || $value ne &amp;quot;inactive&amp;quot;);&lt;br /&gt;
	readingsSingleUpdate($hash, &amp;quot;state&amp;quot;, &amp;quot;inactive&amp;quot;, 1);&lt;br /&gt;
	return undef;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== X_Authorize ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Authorize($$$$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $client_hash, $type, $arg ) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
 &lt;br /&gt;
	return $authorized;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Authorize-Funktion wird von fhem.pl aufgerufen um zu erfragen, ob ein bestimmter Client &amp;lt;code&amp;gt;$client_hash&amp;lt;/code&amp;gt; die Aktion &amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;$arg&amp;lt;/code&amp;gt; ausführen darf. Auf diese Weise können Module Einfluss nehmen, welcher User welche Funktionen in FHEM nutzen darf. Wenn ein Client eine Aktion ausführen möchte, werden alle Module, die eine Authorize-Funktion implementiert haben, gefragt, ob diese Aktion ausgeführt werden darf. Als Rückgabewert wird das Ergebnis der Überprüfung zurückgegeben, wobei &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; (unbekannt / nicht zuständig), &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; (erlaubt) oder &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; (verboten) zurückgegeben werden können.&lt;br /&gt;
&lt;br /&gt;
Es gibt aktuell folgende &amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;$arg&amp;lt;/code&amp;gt; Kombinationen, mit denen die Authorize-Funktion aufgerufen werden:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! $type !! $arg !! Überschrift&lt;br /&gt;
|- &lt;br /&gt;
|rowspan=&amp;quot;3&amp;quot; style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;&#039;&#039;&#039;$type&#039;&#039;&#039; = &amp;quot;cmd&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Befehlsausführung&#039;&#039;&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;&#039;&#039;&#039;$arg&#039;&#039;&#039; = &amp;quot;set Lampe on&amp;quot;&amp;lt;/code&amp;gt; || Jeglicher FHEM-Befehl, der ausgeführt werden soll, wird in &amp;lt;code&amp;gt;$arg&amp;lt;/code&amp;gt; hinterlegt, sodass innerhalb einer Authorize-Funktion der Befehl genauer geparst werden kann um zu entscheiden, ob dieser Befehl erlaubt ist, oder nicht.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;&#039;&#039;&#039;$arg&#039;&#039;&#039; = &amp;quot;perl&amp;quot;&amp;lt;/code&amp;gt; || Ausführen von Perl-Befehlen jeglicher Art. Der genaue Befehl wird dabei nicht an die Authorize-Funktion übergeben.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;{ReadingsVal(&amp;quot;Lampe&amp;quot;, &amp;quot;state&amp;quot;, &amp;quot;off&amp;quot;}&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;&#039;&#039;&#039;$arg&#039;&#039;&#039; = &amp;quot;shell&amp;quot;&amp;lt;/code&amp;gt; || Ausführen von Shell-Befehlen jeglicher Art. Der genaue Befehl wird dabei nicht an die Authorize-Funktion übergeben.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;&amp;quot;/opt/fhem/myScript.sh&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;&#039;&#039;&#039;$type&#039;&#039;&#039; = &amp;quot;devicename&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Sichtbarkeit von Geräten/Definitionen&#039;&#039; &lt;br /&gt;
|style=&amp;quot;white-space: nowrap;&amp;quot; |  &amp;lt;code&amp;gt;&#039;&#039;&#039;$arg&#039;&#039;&#039; = &amp;quot;Licht_Wohnzimmer&amp;quot;&amp;lt;/code&amp;gt; || Sichtbarkeit des jeweiligen Gerät/Definition in FHEM. Dies bedeutet konkret die Auffindbarkeit im &amp;lt;code&amp;gt;list&amp;lt;/code&amp;gt;-Befehl, sowie der Suche via [[DevelopmentModuleAPI#devspec2array|devspec2array()]]. Wird eine solche Anfrage durch die Authorize-Funktion abgelehnt, ist das entsprechende Gerät bzw. Definition für den jeweiligen Client nicht sichtbar.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== X_Authenticate ====&lt;br /&gt;
{{Link2Forum|Topic=72757|Message=644098}}&lt;br /&gt;
&lt;br /&gt;
== Bereitstellen eines eigenen Befehls (Befehlsmodul) ==&lt;br /&gt;
&lt;br /&gt;
Ein Modul kann primär einen neuen FHEM-Befehl bereitstellen. Man spricht in so einem Fall nicht von einem Gerätemodul, sondern einem Befehlsmodul. Ein solches Befehlsmodul stellt nur einen einzelnen Befehl bereit, der dem Modulnamen entsprechen muss. Nur, wenn der Modulname dem Befehlsname entspricht, kann FHEM das Modul beim ersten Ausführen dieses unbekannten Befehls finden und nachladen.&lt;br /&gt;
&lt;br /&gt;
Der entsprechende Befehl wird dazu in der [[#X_Initialize|Initialize]]-Funktion im globalen Hash &amp;lt;code&amp;gt;[[DevelopmentModuleIntro#Wichtige_globale_Variablen_aus_fhem.pl|%cmds]]&amp;lt;/code&amp;gt; registriert:&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;
    $cmds{X} = { Fn           =&amp;gt; &amp;quot;CommandX&amp;quot;,&lt;br /&gt;
                 Hlp          =&amp;gt; &amp;quot;&amp;lt;argument1&amp;gt; [optional_argument2], print something very useful&amp;quot;,&lt;br /&gt;
 &lt;br /&gt;
                 # optionaler Filter für Clientmodule als regulärer Ausdruck&lt;br /&gt;
                 ClientFilter =&amp;gt; &amp;quot;FHEMWEB&amp;quot;&lt;br /&gt;
                };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit wird der neue Befehl &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; in FHEM registriert. Die Funktion mit dem Namen &amp;lt;code&amp;gt;CommandX&amp;lt;/code&amp;gt; setzt diesen Befehl innerhalb des Moduls um. Desweiteren wird eine kurze Aufrufsyntax mitgegeben, welche beim Aufruf des &amp;lt;code&amp;gt;help&amp;lt;/code&amp;gt;-Befehls dem Nutzer angezeigt wird um als Gedankenstütze zu dienen. Optional kann man mittels &amp;lt;code&amp;gt;ClientFilter&amp;lt;/code&amp;gt; (regulärer Ausdruck für Modulnamen) die Ausführbarkeit nur auf bestimmte Client-Module (wie FHEMWEB oder telnet) beschränken. &lt;br /&gt;
&lt;br /&gt;
Nun muss noch die Funktion &amp;lt;code&amp;gt;CommandX&amp;lt;/code&amp;gt; im Rahmen des Moduls implementiert werden, welche den Befehl &amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; umsetzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
sub CommandX($$)&lt;br /&gt;
{&lt;br /&gt;
 	my ($client_hash, $arguments) = @_;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
	&lt;br /&gt;
	return $output;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei werden der Befehlsfunktion zwei Parameter übergeben. Zuerst die Hash-Referenz des aufrufenden Clients (sofern manuell ausgeführt, ansonsten &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt;) zwecks Rechteprüfung via [[allowed|allowed-Definitionen]]. Anschließend folgen die Aufrufparameter als zusammenhängende Zeichenkette. Die Trennung der einzelnen Argumente obligt der Funktion (bspw. via [[DevelopmentModuleAPI#parseParams|parseParams()]]). Als Funktionsrückgabewert wird eine Ausgabemeldung erwartet, die dem Nutzer angezeigt werden soll.&lt;br /&gt;
&lt;br /&gt;
== Pollen von Geräten ==&lt;br /&gt;
Wenn Geräte von sich aus keine Informationen senden sondern abgefragt werden müssen, kann man im Modul die Funktion [[DevelopmentModuleAPI#InternalTimer|InternalTimer()]] verwenden um einen Funktionsaufruf zu einem späteren Zeitpunkt durchführen zu können. Man übergibt dabei den Zeitpunkt für den nächsten Aufruf, den Namen der Funktion, die aufgerufen werden soll, sowie den zu übergebenden Parameter. Als zu übergebender Parameter wird üblicherweise der Hash der betroffenen Geräteinstanz verwendet. Damit hat die aufgerufene Funktion Zugriff auf alle wichtigen Daten der Geräteinstanz. Eventuell zusätzlich benötigte Werte können einfach als weitere Internals über den Hash zugänglich gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise könnte man für das Abfragen eines Geräts in der [[#X_Define|Define]]-Funktion den Timer folgendermaßen setzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
InternalTimer(gettimeofday()+2, &amp;quot;X_GetUpdate&amp;quot;, $hash);	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternativ kann man auch in der [[#X_Notify|Notify]]-Funktion auf das Event &amp;lt;code&amp;gt;global:INITIALIZED&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;global:REREADCFG&amp;lt;/code&amp;gt; reagieren und erst dort, den Timer anstoßen, sobald die Konfiguration komplett eingelesen wurde. Dies ist insbesondere notwendig, wenn man sicherstellen will, dass alle Attribute aus der Konfiguration gesetzt sind, sobald man einen Status-Update initiiert.&lt;br /&gt;
&lt;br /&gt;
In der Funktion &amp;lt;code&amp;gt;X_GetUpdate&amp;lt;/code&amp;gt; selbst wird dann der Timer neu gesetzt, so dass nach einem Intervall die Funktion erneut aufgerufen wird:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_GetUpdate($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
	Log3 $name, 4, &amp;quot;X: GetUpdate called ...&amp;quot;;&lt;br /&gt;
	&lt;br /&gt;
	...&lt;br /&gt;
	&lt;br /&gt;
	# neuen Timer starten in einem konfigurierten Interval.&lt;br /&gt;
	InternalTimer(gettimeofday()+$hash-&amp;gt;{Interval}, &amp;quot;X_GetUpdate&amp;quot;, $hash);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Innerhalb der Funktion kann man nun das Gerät abfragen und die abgefragten Werte in Readings speichern. Falls das Abfragen der Werte jedoch zu einer Verzögerung und damit zu einer Blockade von FHEM führen kann, ist es möglich, in der GetUpdate-Funktion nur die Aufforderung zum Senden bestimmter Daten an das angeschlossene Gerät zu senden und dann das Lesen über die oben beschriebene [[#X_Read|Read]]-Funktion zu implementieren.&lt;br /&gt;
&lt;br /&gt;
Eine genaue Beschreibung der Timer-Funktion gibt es [[DevelopmentModuleAPI#Timer|hier im Wiki]]&lt;br /&gt;
&lt;br /&gt;
== Logging / Debugging ==&lt;br /&gt;
Um Innerhalb eines Moduls eine Log-Meldung in die FHEM-Logdatei zu schreiben, wird die Funktion [[DevelopmentModuleAPI#Log3|Log3()]] aufgerufen.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
Log3 $name, 3, &amp;quot;X ($name) - Problem erkannt ...&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Eine genaue Beschreibung zu der Funktion inkl. Aufrufparameter findet man [[DevelopmentModuleAPI#Log3|hier]]. Es ist generell ratsam in der Logmeldung sowohl den Namen des eigenen Moduls zu schreiben, sowie den Namen des Geräts, welche diese Logmeldung produziert, da die Meldung, so wie sie ist, direkt in das Logfile wandert und es für User ohne diese Informationen schwierig ist, die Meldungen korrekt zuzuordnen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion Log3() verwendet den Namen der Geräteinstanz um das &amp;lt;code&amp;gt;verbose&amp;lt;/code&amp;gt;-Attribut zu prüfen. In der Regel wird bei Modulfunktionen jedoch immer nur der Gerätehash &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; übergeben. Um den Namen der Definition zu ermitteln ist es daher notwendig sich diesen aus dem Hash extrahieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um für eine einzelne Geräteinstanz das Verbose-Level zu erhöhen, ohne gleich für das gesamte FHEM den globalen Verbose-Level zu erhöhen und damit alle Meldungen zu erzeugen, kann man den Befehl &lt;br /&gt;
&amp;lt;code&amp;gt;attr &amp;lt;NAME&amp;gt; verbose&amp;lt;/code&amp;gt; verwenden. Beispielsweise &amp;lt;code&amp;gt;attr Lichtschalter_Wohnzimmer verbose 5&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Logmeldungen sollten je nach Art und Wichtigkeit für den Nutzer in unterschiedlichen Loglevels erzeugt werden. Es gibt insgesamt 5 Stufen in denen geloggt werden kann. Standardmäßig steht der systemweite Loglevel (&amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt;-Attribut &amp;lt;code&amp;gt;verbose&amp;lt;/code&amp;gt;) auf der Stufe 3. Die Bedeutung der jeweiligen Stufen ist in der {{Link2CmdRef|Lang=de|Anker=verbose}} beschrieben.&lt;br /&gt;
&lt;br /&gt;
Während der Entwicklung eines Moduls kann man für eigene Debug-Zwecke auch die Funktion [[DevelopmentModuleAPI#Debug|Debug()]] verwenden um schnell und einfach Debug-Ausgaben in das Log zu schreiben. Diese sollten in der endgültigen Fassung jedoch nicht mehr vorhanden sein. Sie dienen ausschließlich zum Debugging während der Entwicklung.&lt;br /&gt;
&lt;br /&gt;
Eine genaue Beschreibung der Log-Funktion gibt es [[DevelopmentModuleAPI#Logging|hier im Wiki]].&lt;br /&gt;
&lt;br /&gt;
== Zweistufiges Modell für Module ==&lt;br /&gt;
[[Datei:Zweistufiges Modulkonzept.jpg|mini|rechts|Schematische Darstellung am Beispiel CUL]]&lt;br /&gt;
Es gibt viele Geräte, welche die Kommunikation mit weiteren Geräten mit tlw. unterschiedlichen Protokollen ermöglichen. Das typischste Beispiel bietet hier der [[CUL]], welcher via Funk mit verschiedenen Protokollen weitere Geräte ansprechen kann (z.B. Aktoren, Sensoren, ...). Hier bildet ein Gerät eine Brücke durch die weitere Geräte in FHEM zugänglich gemacht werden können. Dabei werden über einen Kommunikationsweg (z.B. serielle Schnittstelle, TCP, ...) beliebig viele Geräte gesteuert. Typische Beispiele dazu sind:&lt;br /&gt;
&lt;br /&gt;
* [[CUL]]: stellt Geräte mit verschiedenen Kommunikationsprotokollen via Funk bereit (u.a. [[FS20]], [[HomeMatic]], [[Funk-Heizkörperregler_Kurz-Bedienungsanleitung_FHT|FHT]], [[MAX]], ...)&lt;br /&gt;
* [[HMLAN]]: stellt HomeMatic Geräte via Funk bereit&lt;br /&gt;
* [[MAX#MAXLAN|MAXLAN]]: stellt [[MAX|MAX!]] Geräte via Funk bereit&lt;br /&gt;
* [[PanStamp#panStick.2FShield|panStamp]]: stellt weitere panStamp Geräte via Funk bereit&lt;br /&gt;
&lt;br /&gt;
Dabei wird die Kommunikation in 2 Stufen unterteilt:&lt;br /&gt;
* physisches Modul - z.B. 00_CUL.pm - zuständig für die physikalische Kommunikation mit der Hardware. Empfangene Daten müssen einem logischen Modul zugeordnet werden.&lt;br /&gt;
* logische Modul(e) - z.B. 10_FS20.pm - interpretiert protokollspezifische Nachrichten. Sendet protokollspezifische Daten über das physische Modul an die Hardware.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;physisches Modul&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das physische Modul öffnet die Datenverbindung zum Gerät (z.B. CUL) und verarbeitet sämtliche Daten. Es kümmert sich um den Erhalt der Verbindung (bsp. durch Keep-Alives) und konfiguriert das Gerät so, dass eine Kommunikation mit allen weiteren Geräten möglich ist (bsp. Frequenz, Modulation, Kanal, etc.).&lt;br /&gt;
&lt;br /&gt;
Empfangene Nutzdaten werden als Zeichenkette über die Funktion [[DevelopmentModuleAPI#Dispatch|Dispatch()]] an logische Module weitergegeben.&lt;br /&gt;
&lt;br /&gt;
Das Modul stellt eine [[#Die_Match-Liste|Match-Liste]] bereit, anhand FHEM die Nachricht einem Modul zuordnen kann, sofern dieses noch nicht geladen sein sollte. Die Match-Liste enthält eine Liste von regulären Ausdrücken und ordnet diese einem Modul zu. Wenn eine Nachricht auf einen solchen regulären Ausdruck passt und das Modul noch nicht geladen ist, lädt FHEM dieses automatisch nach, zwecks Verarbeitung der Nachricht. &lt;br /&gt;
&lt;br /&gt;
Anhand einer bereitgestellten [[#Die_Client-Liste|Client-Liste]] (Auflistung von logischen Modulen) kann FHEM feststellen, welche logischen Module mit dem physischen Modul kommunizieren können. Nur die hier aufgelisteten, logischen Module werden beim Aufruf von [[DevelopmentModuleAPI#Dispatch|Dispatch()]] angesprochen.&lt;br /&gt;
&lt;br /&gt;
Das Modul stellt eine [[#X_Write|Write]]-Funktion zur Verfügung, über die logische Module Daten in beliebiger Form an die Hardware übertragen können. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;u&amp;gt;logisches Modul&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das logische Modul interpretiert die via Dispatch() übergebene Nachricht (Zeichenkette) durch eine bereitgestellte [[#X_Parse|Parse]]-Funktion und erzeugt entsprechende Readings/Events. Es stellt über &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt;-/&amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt;-Kommandos Steuerungsmöglichkeiten dem Nutzer zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
Es stellt FHEM einen [[#Der_Match-Ausdruck|Match-Ausdruck]] (regulärer Ausdruck) zur Verfügung anhand [[DevelopmentModuleAPI#Dispatch|Dispatch()]] ermitteln kann, ob die Nachricht durch das logische Modul verarbeitet werden kann. Nur Nachrichten, welche auf diesen Ausdruck passen, werden an das logische Modul weitergegeben (Aufruf [[#X_Parse|Parse]]-Funktion).&lt;br /&gt;
&lt;br /&gt;
=== Die Client-Liste ===&lt;br /&gt;
&lt;br /&gt;
Die Client-Liste ist eine Auflistung von Modulnamen (genauer: regulären Ausdrücken die auf Modulnamen passen) die in einem physischen Modul gesetzt ist. Damit wird definiert, mit welchen logischen Modulen das physikalische Modul  kommunizieren kann. &lt;br /&gt;
&lt;br /&gt;
Eine Client-Liste ist eine Zeichenkette, welche aus allen logischen Modulnamen besteht. Die einzelnen Namen werden durch einen Doppelpunkt getrennt. Anstatt kompletter Modulnamen können auch reguläre Ausdrücke verwendet werden, die auf mehrere Modulnamen passen (z.B. &amp;lt;code&amp;gt;CUL_.*&amp;lt;/code&amp;gt; um die logischen Module CUL_HM, CUL_MAX, etc. zu verwenden).&lt;br /&gt;
&lt;br /&gt;
Bsp.: Die Client-Liste von dem Modul CUL lautet daher wie folgt:&lt;br /&gt;
&lt;br /&gt;
 FS20:FHT.*:KS300:USF1000:BS:HMS:CUL_EM:CUL_WS:CUL_FHTTK:CUL_HOERMANN:ESA2000:CUL_IR:CUL_TX:Revolt:IT:UNIRoll:SOMFY:STACKABLE_CC:CUL_RFR:CUL_TCM97001:CUL_REDIRECT&lt;br /&gt;
&lt;br /&gt;
Alle hier aufgelisteten Module können über das Modul CUL Daten empfangen bzw. senden.&lt;br /&gt;
&lt;br /&gt;
Die Client-Liste hat generell folgende Funktion:&lt;br /&gt;
* Die Funktion [[DevelopmentModuleAPI#Dispatch|Dispatch()]] prüft nur Module, welche in der Client-Liste enthalten sind, ob diese die Nachricht verarbeiten können (Prüfung via [[#Der Match-Ausdruck|Match-Ausdruck]])&lt;br /&gt;
* Die Funktion [[DevelopmentModuleAPI#AssignIoPort|AssignIoPort()]] prüft anhand sämtlicher Client-Listen in FHEM, welches IO-Gerät für ein logisches Gerät nutzbar ist.&lt;br /&gt;
&lt;br /&gt;
Üblicherweise wird die Client-Liste in der [[#X_Initialize|Initialize]]-Funktion im Modul-Hash gesetzt:&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;{Clients} = &amp;quot;FS20:KS300:FHT.*&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man kann die Client-Liste jedoch auch pro physikalisches Gerät setzen. Eine gesetzte Client-Liste in einem Gerät hat immer Vorrang vor der Liste im Modul-Hash. Eine gerätespezifische Client-Liste wird dann verwendet, wenn bspw. ein Gerät je nach Konfiguration nur bestimmte logische Module bedienen kann. Bspw. kann ein CUL je nach RF-Einstellungen FS20, uvm. oder nur HomeMatic bedienen. In einem solchen Fall wird die Client-Liste im Geräte-Hash gesetzt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Define($$)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, $def ) = @_;&lt;br /&gt;
	...&lt;br /&gt;
	$hash-&amp;gt;{Clients} = &amp;quot;CUL_HM&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In vielen Modulen, welche nach dem zweistufigem Konzept arbeiten, beginnt und endet die Client-Liste mit einem Doppelpunkt. Dies ist ein historisches Überbleibsel, da der Prüfmechanismus die Client-Liste früher auf das Vorhandensein von &amp;lt;code&amp;gt;&#039;&#039;&#039;&amp;lt;u&amp;gt;&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;:&amp;lt;/font&amp;gt;&amp;lt;/u&amp;gt;&#039;&#039;&#039;&amp;amp;lt;Modulname&amp;amp;gt;&#039;&#039;&#039;&amp;lt;u&amp;gt;&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;:&amp;lt;/font&amp;gt;&amp;lt;/u&amp;gt;&#039;&#039;&#039;&amp;lt;/code&amp;gt; prüfte. Dies ist nun nicht mehr notwendig. Die einzelnen Modulnamen müssen lediglich durch einen Doppelpunkt getrennt werden.&lt;br /&gt;
&lt;br /&gt;
=== Die Match-Liste ===&lt;br /&gt;
{{Randnotiz|RNTyp=Info|RNText=&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;&amp;lt;u&amp;gt;&#039;&#039;&#039;ACHTUNG&#039;&#039;&#039;:&amp;lt;/u&amp;gt;&amp;lt;/font&amp;gt;&lt;br /&gt;
Sämtliche regulären Ausdrücke in der Match-Liste werden &amp;quot;case insensitive&amp;quot; überprüft. Das bedeutet, dass Groß-/Kleinschreibung nicht berücksichtigt wird.&lt;br /&gt;
&lt;br /&gt;
Um dennoch in einem regulären Ausdruck auf Groß-/Kleinschreibung zu prüfen, kann man dieses mit dem Modifizierer &amp;lt;code&amp;gt;(?-i)&amp;lt;/code&amp;gt; wieder aktivieren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my %matchListFHEMduino = (&lt;br /&gt;
    ....&lt;br /&gt;
    &amp;quot;5:FHEMduino_PT2262&amp;quot;   =&amp;gt; &amp;quot;^(?-i)IR.*\$&amp;quot;,&lt;br /&gt;
    ....&lt;br /&gt;
    &amp;quot;13:IT&amp;quot;                =&amp;gt; &amp;quot;^(?-i)i......\$&amp;quot;,&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe dazu Forumsbeitrag: {{Link2Forum|Topic=33422}}&lt;br /&gt;
}}&lt;br /&gt;
Die Match-Liste ordnet eine Nachrichtensyntax (regulärer Ausdruck) einem Modulnamen zu und wird in einem physikalischen Modul gesetzt. Sollte eine Nachricht vom physikalischen Gerät empfangen werden, die durch kein geladenes Modul verarbeitet werden kann ([[DevelopmentModuleAPI#Dispatch|Dispatch()]] prüft nur alle geladenen Module aus der [[#Die Client-Liste|Client-Liste), so wird über die Match-Liste geprüft, welches Modul diese Nachricht verarbeiten kann. Dieses Modul wird anschließend geladen und die Nachricht durch dieses verarbeitet. In dieser Liste findet mittels regulärem Ausdruck eine Zuordnung der Nachrichtenstruktur zum verarbeitenden logischen Modul statt.&lt;br /&gt;
&lt;br /&gt;
Diese Liste wird ausschließlich in der [[DevelopmentModuleAPI#Dispatch|Dispatch()]]-Funktion verwendet. Sollte keine passendes Modul, welches bereits geladen ist, zur Verarbeitung einer Nachricht gefunden werden, so wird mithilfe der Match-Liste aufgrund der vorliegenden Nachricht das entsprechende Modul ermittelt. Dieses Modul wird dann direkt geladen und die Nachricht wird via [[#X_Parse|Parse]]-Funktion verarbeitet.&lt;br /&gt;
&lt;br /&gt;
Die Match-Liste ist eine Zuordnung von einem Sortierpräfix + Modulname zu einem regulären Ausdruck:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;1:FS20&amp;quot;  =&amp;gt; &amp;quot;^81..(04|0c)..0101a001&amp;quot;,&lt;br /&gt;
    &amp;quot;2:KS300&amp;quot; =&amp;gt; &amp;quot;^810d04..4027a001&amp;quot;,&lt;br /&gt;
    &amp;quot;3:FHT&amp;quot;   =&amp;gt; &amp;quot;^81..(04|09|0d)..(0909a001|83098301|c409c401)..&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Sortierpräfix (&amp;lt;code&amp;gt;&amp;lt;u&amp;gt;1:&amp;lt;/u&amp;gt;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;FS20&amp;lt;/font&amp;gt;&amp;lt;/code&amp;gt;) dient als Sortierhilfe um so die Reihenfolge der Prüfung festzulegen. Bei der Prüfung wird die Match-Liste mittels sort() nach dem Schlüssel (Sortierpräfix + Modulname) sortiert und die regulären Ausdrücke werden dann nacheinander getestet. Daher sollten die präzisesten Ausdrücke immer zuerst getestet werden, sofern es weniger präzise Ausdrücke in der Match-Liste gibt. Dabei ist zu beachten, dass der Sortierpräfix nicht nach numerischen Regeln sortiert wird, sondern zeichenbasierend.&lt;br /&gt;
&lt;br /&gt;
Üblicherweise wird die Match-Liste in der [[#X_Initialize|Initialize]]-Funktion im Modul-Hash gesetzt:&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;
   ...&lt;br /&gt;
&lt;br /&gt;
   $hash-&amp;gt;{MatchList} = { &amp;quot;1:FS20&amp;quot;      =&amp;gt; &amp;quot;^81..(04|0c)..0101a001&amp;quot;,&lt;br /&gt;
                          &amp;quot;2:KS300&amp;quot;     =&amp;gt; &amp;quot;^810d04..4027a001&amp;quot;,&lt;br /&gt;
                          &amp;quot;3:FHT&amp;quot;       =&amp;gt; &amp;quot;^81..(04|09|0d)..(0909a001|83098301|c409c401)..&amp;quot; };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Man kann die Match-Liste, ähnlich wie bei der Client-Liste, auch pro physikalisches Gerät setzen. Dabei hat auch hier die Match-Liste eines Gerätes immer Vorrang vor der Match-Liste aus dem Modul-Hash:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub X_Define($$)&lt;br /&gt;
{&lt;br /&gt;
   my ($hash, $def) = @_;&lt;br /&gt;
&lt;br /&gt;
   ...&lt;br /&gt;
&lt;br /&gt;
   $hash-&amp;gt;{MatchList} = { &amp;quot;1:CUL_HM&amp;quot; =&amp;gt; &amp;quot;^A....................&amp;quot; };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Der Match-Ausdruck ===&lt;br /&gt;
&lt;br /&gt;
Ein Match-Ausdruck wird in einem logischen Modul gesetzt und dient der Prüfung, ob eine Nachricht durch das eigene Modul via [[#X_Parse|Parse]]-Funktion verarbeitet werden kann. Es handelt sich hierbei um einen einzelnen regulären Ausdruck, den FHEM innerhalb der [[DevelopmentModuleAPI#Dispatch|Dispatch()]]-Funktion prüft. Nur wenn eine Nachricht via Dispatch() auf diesen Audruck matcht, wird die Parse-Funktion des eigenen Moduls aufgerufen um die Nachricht zu verarbeiten. &lt;br /&gt;
&lt;br /&gt;
Der Hintergrund, warum man den Aufruf mit einem solchen Ausdruck vorher abprüft, liegt in der Möglichkeit, dass ein physikalisches Modul mehrere unterschiedliche logische Module ansprechen kann. So kann FHEM jedes geladene Modul durch diesen Match-Ausdruck prüfen, ob es diese Nachricht verarbeiten kann. Erst, wenn alle geladenen Module, aufgrund einer Prüfung des Ausdrucks, die Nachricht nicht verarbeiten können, wird via [[#Die_Match-Liste|Match-Liste]] ermittelt, welches Modul geladen werden muss um die Nachricht zu verarbeiten. &lt;br /&gt;
&lt;br /&gt;
Der Match-Ausdruck wird in der [[#X_Initialize|Initialize]]-Funktion zur Verfügung gestellt:&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;
	...&lt;br /&gt;
	&lt;br /&gt;
	# Dieses Modul verarbeitet FS20 Nachrichten&lt;br /&gt;
	$hash-&amp;gt;{Match} = &amp;quot;^81..(04|0c)..0101a001&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Die vollständige Implementierung ===&lt;br /&gt;
&lt;br /&gt;
Hier nun eine Zusammenfassung beim zweistufigen Modulkonzept in der jeweiligen Stufe implementiert werden muss, damit die Kommunikation funktioniert.&lt;br /&gt;
&lt;br /&gt;
==== physisches Modul ====&lt;br /&gt;
&lt;br /&gt;
Das physische Modul, welches als Kommunikationsbrücke zwischen der Hardware und logischen Modulen fungieren wird, sollte mindestens folgende Funktionen implementieren:&lt;br /&gt;
&lt;br /&gt;
* [[#X_Initialize|Initialize]]-Funktion - Zum Registrieren des Moduls in FHEM.&lt;br /&gt;
* [[#X_Define|Define]]-Funktion - Zum öffnen der Datenverbindung zur Hardware (IP-Adresse/serielle Schnittstelle/...).&lt;br /&gt;
* [[#X_Read|Read]]-Funktion - Zum Lesen von Daten, welche die Hardware übermittelt.&lt;br /&gt;
* [[#X_Read|Ready]]-Funktion - Zum Wiederaufbau der Verbindung bei Verbindungsabbruch, bzw. Prüfung auf lesbare Daten bei serieller Schnittstelle unter Windows.&lt;br /&gt;
* [[#X_Write|Write]]-Funktion - Zum Senden von Daten, welche logische Module via [[DevelopmentModuleAPI#IOWrite|IOWrite()]] an die Hardware übertragen möchten.&lt;br /&gt;
* [[#X_Undef|Undef]]-Funktion - Schließen der Verbindung zur Hardware beim Löschen via &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;rereadcfg&amp;lt;/code&amp;gt;.&lt;br /&gt;
* [[#X_Shutdown|Shutdown]]-Funktion - Schließen der Verbindung zur Hardware beim Stopp von FHEM via &amp;lt;code&amp;gt;shutdown&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Desweiteren müssen in der [[#X_Initialize|Initialize]]-Funktion folgende Daten bereitgestellt werden:&lt;br /&gt;
&lt;br /&gt;
* [[#Die_Client-Liste|Client-Liste]] - Auflistung aller logischen Module, die über dieses Modul kommunizieren können&lt;br /&gt;
* [[#Die_Match-Liste|Match-Liste]] - Zuordnung von Nachrichtensyntax zu Modul zwecks Autoload-Funktionalität.&lt;br /&gt;
&lt;br /&gt;
==== logisches Modul ====&lt;br /&gt;
&lt;br /&gt;
Das logische Modul, bildet ein einzelnes Gerät ab, über das mit einem physikalisches Modul kommuniziert werden kann. Es sollte mindestens folgende Funktionen implementieren:&lt;br /&gt;
&lt;br /&gt;
* [[#X_Initialize|Initialize]]-Funktion - Zum Registrieren des Moduls in FHEM.&lt;br /&gt;
* [[#X_Define|Define]]-Funktion - Speichern des Definition Pointers (siehe [[#X_Parse|Parse-Funktion]])&lt;br /&gt;
* [[#X_Parse|Parse]]-Funktion - Zum Lesen von Daten, welche die Hardware übermittelt.&lt;br /&gt;
* [[#X_Undef|Undef]]-Funktion - Löschen des Definition Pointers beim Löschen via &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;rereadcfg&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Desweiteren müssen in der [[#X_Initialize|Initialize]]-Funktion folgende Daten bereitgestellt werden:&lt;br /&gt;
&lt;br /&gt;
* [[#Der_Match-Ausdruck|Match-Ausdruck]] - Prüfausdruck, ob eine Nachricht durch dieses Modul verarbeitet werden kann.&lt;br /&gt;
&lt;br /&gt;
=== Kommunikation von der Hardware bis zu den logischen Modulen ===&lt;br /&gt;
&lt;br /&gt;
Die Gerätedefinition des physischen Moduls öffnet eine Verbindung zur Hardware (z.B. via [[DevIo]]). Die [[#X_Read|Read]]-Funktion wird bei anstehenden Daten aus der Hauptschleife von fhem.pl aufgerufen.&lt;br /&gt;
&lt;br /&gt;
Die Read-Funktion stellt dabei sicher, dass die Daten&lt;br /&gt;
* komplett (in der Regel über einen internen Puffer in &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;) und&lt;br /&gt;
* korrekt (z.B. via Prüfung mittels regulärem Ausdruck)&lt;br /&gt;
sind und ruft die globale Funktion [[DevelopmentModuleAPI#Dispatch|Dispatch()]] mit einer kompletten Nachricht auf.&lt;br /&gt;
&lt;br /&gt;
Die Funktion Dispatch() prüft alle geladenen Module aus der [[#Die_Client-Liste|Client-Liste]] des physikalischen Moduls nach möglichen logischen Modulen zur Verarbeitung. Alle zum Zeitpunkt geladenen Module, die in der Client-Liste aufgeführt sind, werden über den [[#Der_Match-Ausdruck|Match-Ausdruck]] geprüft, ob sie mit der Nachricht etwas anfangen können. Sollte bei einem logischen Modul der Match-Ausdruck passen, so wird die entsprechende [[#X_Parse|Parse]]-Funktion des logischen Moduls aufgerufen. Sofern keine passendes Modul gefunden wurde, um die Nachricht zu verarbeiten, wird in der [[#Die_Match-Liste|Match-Liste]] im Geräte- bzw. Modul-Hash der physischen Gerätedefinition nach dem passenden Modul gesucht. Sollte es darin ein Modul geben, was diese Art von Nachricht verarbeiten kann, so wird versucht dieses Modul zu laden um nun die Nachricht via Parse-Funktion zu verarbeiten. Es erfolgt in diesem Fall keine Vorprüfung durch den Match-Ausdruck.&lt;br /&gt;
&lt;br /&gt;
Durch Dispatch() wird nun die [[#X_Parse|Parse]]-Funktion des gefundenen logischen Moduls aufgerufen. Diese&lt;br /&gt;
* interpretiert die übergebene Nachricht,&lt;br /&gt;
* versucht eine existierende Gerätedefinition in FHEM zu finden (z.B. mittels Definition Pointer), für welche die Nachricht addressiert ist,&lt;br /&gt;
* setzt alle [[#Readings|Readings]] für die gefundene Gerätedefinition via [[DevelopmentModuleAPI#Readings_.2F_Events|readings*update]]()-Funktionen,&lt;br /&gt;
* gibt den Namen der logischen Definition zurück, welche die Nachricht verarbeitet hat.&lt;br /&gt;
&lt;br /&gt;
Sollte keine passende Gerätedefinition für die entsprechende Nachricht existieren (Adresse/ID/Kanal/...), wird der Gerätename &amp;quot;UNDEFINED&amp;quot; inkl. einem passenden &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Statement zurückgegeben, um die Definition durch [[autocreate]] erzeugen zu lassen.&lt;br /&gt;
&lt;br /&gt;
Es findet während der Verarbeitung einer Nachricht durch Dispatch()/Parse-Funktion keine sofortige Eventverarbeitung (via [[DevelopmentModuleAPI#Dispatch|DoTrigger()]]) statt, wenn die [[DevelopmentModuleAPI#Readings_.2F_Events|readings*update]]()-Funktionen verwendet werden.&lt;br /&gt;
(Im Gegensatz zum direkten Aufrufen der readings*update Funktionen ohne vorhergehendes Dispatch() )&lt;br /&gt;
&lt;br /&gt;
Die Funktion Dispatch() triggert das Event-Handling für das von der Parse-Funktion zurückgegebene logische Device selbstständig nach Abschluss der Parse-Funktion.&lt;br /&gt;
&lt;br /&gt;
Optional führt die Funktion Dispatch() eine Überprüfung auf Nachrichtenduplikate beim Einsatz von mehreren IO-Geräten durch. Dazu wird eine implementierte [[#X_Fingerprint|Fingerprint]]-Funktion im physischen oder logischen Modul benötigt. Sollte der Fingerprint einer Nachricht innerhalb einer bestimmten Zeit (globales Attribut &amp;lt;code&amp;gt;dupTimeout&amp;lt;/code&amp;gt;, standardmäßig 500ms) bereits empfangen worden sein, so wird die Nachricht verworfen. Dies ist insbesondere bei funkbasierter Hardware notwendig, wenn mehrere Empfänger die selbe Nachricht empfangen.&lt;br /&gt;
&lt;br /&gt;
=== Kommunikation von den logischen Modulen bis zur Hardware ===&lt;br /&gt;
&lt;br /&gt;
Um von einem logischen Modul eine Nachricht an die Hardware senden zu können, muss zunächst im logischen Gerät ein passenden IO-Gerät ausgewählt sein. Dazu muss die Funktion [[DevelopmentModuleAPI#AssignIoPort|AssignIoPort()]] ein entsprechendes IO-Gerät auswählen und in &amp;lt;code&amp;gt;$hash-&amp;gt;{IODev}&amp;lt;/code&amp;gt; setzen. Dieser Aufruf wird üblicherweise in der [[#X_Define|Define]]-Funktion des logischen Moduls ausgeführt. Erst, wenn ein IO-Gerät ausgewählt wurde, können Daten über das physikalische Gerät an die Hardware übermittelt werden.&lt;br /&gt;
&lt;br /&gt;
Zum Senden von Daten ruft das logische Modul die Funktion [[DevelopmentModuleAPI#IOWrite|IOWrite()]] samt Daten auf. Diese ruft für das entsprechende IO-Gerät die [[#X_Write|Write]]-Funktion auf und übergibt die Daten zum Schreiben an das physikalische Modul. Die Write-Funktion kümmert sich nun um die Übertragung der Daten an die Hardware. &lt;br /&gt;
&lt;br /&gt;
Keine direkten Zugriffe zwischen dem logischen und dem physischen Gerät gibt (d.h. keine direkten Aufrufe von Funktionen, kein direktes Überprüfen von &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;-Inhalten, ...), so können die Module hintereinander geschaltet werden (z.B. für Routerfunktionen wie bei der [[RFR_CUL|RFR]]-Funktionalität) oder mittels [[FHEM2FHEM]] im RAW-Modus zwei FHEM-Installationen verbunden werden und die logischen Geräte können dennoch kommunizieren.&lt;br /&gt;
&lt;br /&gt;
=== Automatisches Anlegen von logischen Gerätedefinitionen (autocreate) ===&lt;br /&gt;
&lt;br /&gt;
Das logische Modul kann im Rahmen der [[#X_Parse|Parse]]-Funktion eine neue Gerätedefinition anlegen, sofern eine passende Definition nicht existieren sollte. Die Parse-Funktion gibt generell den Namen der logischen Gerätedefinition zurück, für welche die Nachricht verarbeitet wurde. Sollte keine passende Definition gefunden werden, so muss die Parse-Funktion folgenden Rückgabewert liefern (zusammenhängende Zeichenkette):&lt;br /&gt;
&lt;br /&gt;
 UNDEFINED &#039;&#039;&amp;amp;lt;Namensvorschlag&amp;amp;gt;&#039;&#039; &#039;&#039;&amp;amp;lt;Modulname&amp;amp;gt;&#039;&#039; &#039;&#039;&amp;amp;lt;Define-Parameter&amp;amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Sollte also bspw. im Rahmen der Parse-Funktion zu Modul X eine Nachricht nicht einer existierenden Gerätedefinition zugeordnet werden können, so muss ein Namensvorschlag erstellt werden der eine eindeutige Komponente wie bspw. eine Adresse/ID/Kanal-Nr enthält. In der Regel wird hier immer der Modulname zusammen mit der eindeutigen Komponente, durch einen Unterstrich getrennt, verwendet (Bsp: &amp;lt;code&amp;gt;X_4834&amp;lt;/code&amp;gt;). Der Modulname ist in der Regel immer der, des eigenen Moduls. In besonderen Fällen kann man hier auch einen abweichenden Modulnamen angeben. Dies wird bspw. bei den [[PanStamp#FHEM-Module.2FDevice_Definition_Files|SWAP-Modulen]] eingesetzt. Als Define-Parameter müssen alle notwendigen Parameter angegeben werden, die beim Aufruf des &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Befehls notwendig sind, damit eine neu angelegte Gerätedefinition Nachrichten zu dieser eindeutigen Adresse Daten verarbeitet. Dazu muss mind. die eindeutige Adresse mitgegeben werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel für FS20:&lt;br /&gt;
&lt;br /&gt;
 UNDEFINED FS20_0ae42f8 FS20 0ae42 f8&lt;br /&gt;
&lt;br /&gt;
Sobald [[DevelopmentModuleAPI#Dispatch|Dispatch()]] einen solchen Rückgabewert von einer [[#X_Parse|Parse]]-Funktion erhält, wird diese Zeichenkette so wie sie ist via [[DevelopmentModuleAPI#DoTrigger|DoTrigger()]] als Event für die Definition &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; getriggert.&lt;br /&gt;
&lt;br /&gt;
Sofern der Nutzer das Modul [[autocreate]] verwendet (definiert hat), kümmert sich dieses nun um das Anlegen einer entsprechenden Gerätedefinition. Es lauscht dabei auf generierte Events der Definition &amp;lt;code&amp;gt;global&amp;lt;/code&amp;gt; via [[#X_Notify|Notify]]-Funktion. Der Nutzer kann dabei das Verhalten von autocreate durch entsprechende Parameter beeinflussen.&lt;br /&gt;
&lt;br /&gt;
Das Modul, für welches autocreate eine neue Definition anlegen möchte, kann das Verhalten durch entsprechende Parameter im Modul-Hash beeinflussen. Dabei gilt, dass gesetzte Attribute durch den Nutzer generell Vorrang haben. Die entsprechenden Parameter werden dabei im Rahmen der [[#X_Initialize|Initialize]]-Funktion im Modul-Hash gesetzt:&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;
	...&lt;br /&gt;
 &lt;br /&gt;
	$hash-&amp;gt;{AutoCreate} = {&amp;quot;X_.*&amp;quot;  =&amp;gt; { ATTR   =&amp;gt; &amp;quot;event-on-change-reading:.* event-min-interval:.*:300&amp;quot;,&lt;br /&gt;
	                                    FILTER =&amp;gt; &amp;quot;%NAME&amp;quot;,&lt;br /&gt;
	                                    GPLOT  =&amp;gt; &amp;quot;temp4hum4:Temp/Hum,&amp;quot;,&lt;br /&gt;
	                                    autocreateThreshold =&amp;gt; &amp;quot;2:140&amp;quot;&lt;br /&gt;
					  }&lt;br /&gt;
	                      };&lt;br /&gt;
			    &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierbei wird unterhalb von &amp;lt;code&amp;gt;$hash-&amp;gt;{AutoCreate}&amp;lt;/code&amp;gt; eine Liste angelegt, wo einem regulären Ausdruck für einen anzulegenden Definitionsnamen entsprechende Optionen zugeordnet werden. Sobald durch autocreate eine Gerätedefintion angelegt wird, auf den ein hier gelisteter Ausdruck matcht, so werden die zugeordneten Optionen berücksichtigt.&lt;br /&gt;
&lt;br /&gt;
Hier eine Auflistung aller möglichen Optionen und ihrer Bedeutung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Optionsname !! Beispiel !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;ATTR&amp;lt;/code&amp;gt;|| style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; |  &amp;lt;code&amp;gt;&amp;quot;event-on-change-reading:.* event-min-interval:.*:300&amp;quot;&amp;lt;/code&amp;gt; || Eine Auflistung von Attributen, die nach dem Anlegen einer Definition zusätzlich gesetzt werden. Es handelt sich hierbei um eine Leerzeichen-separierte Liste von Doppelpunkt-getrennten Tupels mit Attributname und -wert.&lt;br /&gt;
&lt;br /&gt;
Für das dargestellte Beispiel bedeutet dies, dass nach dem Anlegen der Definition folgende FHEM-Befehle zusätzlich ausgeführt werden:&lt;br /&gt;
&lt;br /&gt;
 attr &#039;&#039;&amp;amp;lt;Name&amp;amp;gt;&#039;&#039; event-on-change-reading .*&lt;br /&gt;
 attr &#039;&#039;&amp;amp;lt;Name&amp;amp;gt;&#039;&#039; event-min-interval .*:300&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;FILTER&amp;lt;/code&amp;gt; || style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; |  &amp;lt;code&amp;gt;&amp;quot;%NAME&amp;quot;&amp;lt;/code&amp;gt;|| Sofern in der autocreate-Definiton das Attribut &amp;lt;code&amp;gt;filelog&amp;lt;/code&amp;gt; entsprechend durch den Nutzer gesetzt ist, wird eine zugehörige FileLog-Definition angelegt. Diese Option setzt den dabei benutzten Filter-Regexp, der beim Anlegen der FileLog-Definition gesetzt wird. &lt;br /&gt;
&lt;br /&gt;
Dabei werden folgende Platzhalter durch die entsprechenden Werte der neu angelegten Gerätedefinition ersetzt:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;%NAME&amp;lt;/code&amp;gt; - wird ersetzt durch den Definitionsnamen&lt;br /&gt;
* &amp;lt;code&amp;gt;%TYPE&amp;lt;/code&amp;gt; - wird ersetzt durch den Modulnamen&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; |  &amp;lt;code&amp;gt;GPLOT&amp;lt;/code&amp;gt; || style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;&amp;quot;temp4hum4:Temp/Hum,&amp;quot;&amp;lt;/code&amp;gt; || Sofern eine FileLog-Definition angelegt wurde, kann man weiterführend dazu eine passende SVG-Definition erzeugen um Daten aus dem erzeugten FileLog zu visualisieren. Ein typischer Fall sind hierbei Temperatursensoren, wo es sinnvoll sein kann, einen passenden SVG-Plot mit Temperatur/Luftfeuchtigkeit direkt anzulegen.&lt;br /&gt;
&lt;br /&gt;
Es handelt sich hierbei um eine kommaseparierte Auflistung von gplot-Dateinamen und optionalen Label-Texten durch einen Doppelpunkt getrennt. Im genannten Beispiel entspricht &amp;lt;code&amp;gt;temp4hum4&amp;lt;/code&amp;gt; der zu verwendenden GnuPlot-Datei und &amp;lt;code&amp;gt;Temp/Hum&amp;lt;/code&amp;gt; dem zu verwendenden Label ([[SVG]] Attribut &amp;lt;code&amp;gt;label&amp;lt;/code&amp;gt;). Das Label wird auch durch FileLog verwendet als Link-Text zum entsprechenden SVG Plot. Alternativ kann auch nur die entsprechende GnuPlot-Datei anegeben werden ohne Label. Für jede angegebene GnuPlot-Datei wird anschließend eine entsprechende SVG-Definition erzeugt mit der vorher erzeugten FileLog-Definition als Datenquelle.&lt;br /&gt;
&lt;br /&gt;
Der gesamte Inhalt der &amp;lt;code&amp;gt;GPLOT&amp;lt;/code&amp;gt;-Option wird beim Anlegen einer FileLog-Definition dem Attribut &amp;lt;code&amp;gt;logtype&amp;lt;/code&amp;gt; als Wert plus dem Text &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt; zugewiesen. Daher muss der Inhalt der Option &amp;lt;code&amp;gt;GPLOT&amp;lt;/code&amp;gt; immer mit einem Komma enden. &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;autocreateThreshold&amp;lt;/code&amp;gt; || style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot;| &amp;lt;code&amp;gt;&amp;quot;2:10&amp;quot;&amp;lt;/code&amp;gt; || Definiert, wie viele Aufrufe (im Bsp: &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;) von autocreate innerhalb welcher Zeit (im Bsp: &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt; Sek.) stattfinden müssen, bevor die Gerätedefinition tatsächlich durch autocreate angelegt wird. Dadurch kann das ungewollte Anlegen von Geräten verhindert werden die tatsächlich nicht in Echt existieren. Aufgrund von Funkstörungen kann es durchaus zum ungewollten Anlegen einer Definition kommen. Diese Funktion lässt eine Definition erst zu wenn innerhalb einer vorgegeben Zeit eine Mindestzahl an Nachrichten eintrifft.&lt;br /&gt;
&lt;br /&gt;
Die erste Zahl stellt dabei die Mindestanzahl an Nachrichten dar. Die Zweite Zahl stellt die Zeit in Sekunden dar, in der die Mindestanzahl an Nachrichten erreicht werden muss um eine entsprechende Gerätedefinition anzulegen. &lt;br /&gt;
&lt;br /&gt;
Sofern diese Option nicht gesetzt ist, wird standardmäßig &amp;lt;code&amp;gt;2:60&amp;lt;/code&amp;gt; verwendet.&lt;br /&gt;
&lt;br /&gt;
Diese Option kann durch den Anwender über das Attribut &amp;lt;code&amp;gt;autocreateThreshold&amp;lt;/code&amp;gt; übersteuert werden.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; | &amp;lt;code&amp;gt;noAutocreatedFilelog&amp;lt;/code&amp;gt;|| style=&amp;quot;vertical-align:top; white-space: nowrap;&amp;quot; |  &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;|| Flag. Sofern gesetzt, wird keine FileLog- und ggf. SVG-Definition erzeugt. Selbst wenn der Nutzer durch entsprechende Attribute das Anlegen wünscht. Diese Option ist sinnvoll für Module bzw. Geräte die keine Readings erzeugen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Ergänzende Hinweise ==&lt;br /&gt;
Die Wahl der vorangestellten Nummer für den Dateinamen eines neuen Moduls hat keine Bedeutung mehr, es sei denn die Nummer ist 99. Module, die mit 99_ beginnen, werden von FHEM automatisch geladen. Module mit einer anderen Nummer nur wenn ein &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;-Befehl dafür sorgt, dass das Modul geladen wird.&lt;br /&gt;
&lt;br /&gt;
Wenn ein Modul Initialisierungsdaten benötigt, sollten diese im Modul selbst enthalten sein. Eine zusätzliche Datei oder sogar ein Unterverzeichnis mit mehreren Dateien ist bei FHEM nicht üblich und sollte bei Modulen, die mit FHEM ausgeliefert werden nur in Rücksprache mit Rudolf König angelegt werden, da sie sonst bei einem Update nicht verteilt werden.&lt;br /&gt;
&lt;br /&gt;
== Weitere Informationen ==&lt;br /&gt;
Wenn man weitere Details wissen möchte, ist ein erster sinnvoller Schritt ein Blick in die Datei fhem.pl. Dort sieht man im Perl-Code wie die Module aufgerufen werden, was vorher passiert und was danach. Am Anfang der Datei (ca. ab Zeile 130) findet man beispielsweise eine Liste der globalen Variablen, die den Modulen zur Verfügung stehen sowie Details zu den wichtigen Hashes %modules und %defs. Wer mit Perl noch nicht so gut klar kommt, dem hilft eventuell ein Blick auf die Perldoc Website[http://perldoc.perl.org/] oder in das Perl-Buch seiner Wahl. Auch die FHEM {{Link2CmdRef}} sollte nicht unterschätzt werden. Es stehen oft mehr interessante Details auch für Modulentwickler darin als man zunächst vermuten könnte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &amp;quot;Hello World&amp;quot; Beispiel ==&lt;br /&gt;
&lt;br /&gt;
98_Hello.pm&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
package main;&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
&lt;br /&gt;
my %Hello_gets = (&lt;br /&gt;
	&amp;quot;whatyouwant&amp;quot;	=&amp;gt; &amp;quot;can&#039;t&amp;quot;,&lt;br /&gt;
	&amp;quot;whatyouneed&amp;quot;	=&amp;gt; &amp;quot;try sometimes&amp;quot;,&lt;br /&gt;
	&amp;quot;satisfaction&amp;quot;  =&amp;gt; &amp;quot;no&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
sub Hello_Initialize($) {&lt;br /&gt;
    my ($hash) = @_;&lt;br /&gt;
&lt;br /&gt;
    $hash-&amp;gt;{DefFn}      = &#039;Hello_Define&#039;;&lt;br /&gt;
    $hash-&amp;gt;{UndefFn}    = &#039;Hello_Undef&#039;;&lt;br /&gt;
    $hash-&amp;gt;{SetFn}      = &#039;Hello_Set&#039;;&lt;br /&gt;
    $hash-&amp;gt;{GetFn}      = &#039;Hello_Get&#039;;&lt;br /&gt;
    $hash-&amp;gt;{AttrFn}     = &#039;Hello_Attr&#039;;&lt;br /&gt;
    $hash-&amp;gt;{ReadFn}     = &#039;Hello_Read&#039;;&lt;br /&gt;
&lt;br /&gt;
    $hash-&amp;gt;{AttrList} =&lt;br /&gt;
          &amp;quot;formal:yes,no &amp;quot;&lt;br /&gt;
        . $readingFnAttributes;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub Hello_Define($$) {&lt;br /&gt;
    my ($hash, $def) = @_;&lt;br /&gt;
    my @param = split(&#039;[ \t]+&#039;, $def);&lt;br /&gt;
    &lt;br /&gt;
    if(int(@param) &amp;lt; 3) {&lt;br /&gt;
        return &amp;quot;too few parameters: define &amp;lt;name&amp;gt; Hello &amp;lt;greet&amp;gt;&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    my $hash-&amp;gt;{name}  = $param[0];&lt;br /&gt;
    my $hash-&amp;gt;{greet} = $param[2];&lt;br /&gt;
    &lt;br /&gt;
    return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub Hello_Undef($$) {&lt;br /&gt;
    my ($hash, $arg) = @_; &lt;br /&gt;
    # nothing to do&lt;br /&gt;
    return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub Hello_Get($@) {&lt;br /&gt;
	my ($hash, @param) = @_;&lt;br /&gt;
	&lt;br /&gt;
	return &#039;&amp;quot;get Hello&amp;quot; needs at least one argument&#039; if (int(@param) &amp;lt; 2);&lt;br /&gt;
	&lt;br /&gt;
	my $name = shift @param;&lt;br /&gt;
	my $opt = shift @param;&lt;br /&gt;
	if(!$Hello_gets{$opt}) {&lt;br /&gt;
		my @cList = keys %Hello_gets;&lt;br /&gt;
		return &amp;quot;Unknown argument $opt, choose one of &amp;quot; . join(&amp;quot; &amp;quot;, @cList);&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	if($attr{$name}{formal} eq &#039;yes&#039;) {&lt;br /&gt;
	    return $Hello_gets{$opt}.&#039;, sir&#039;;&lt;br /&gt;
    }&lt;br /&gt;
	return $Hello_gets{$opt};&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub Hello_Set($@) {&lt;br /&gt;
	my ($hash, @param) = @_;&lt;br /&gt;
	&lt;br /&gt;
	return &#039;&amp;quot;set Hello&amp;quot; needs at least one argument&#039; if (int(@param) &amp;lt; 2);&lt;br /&gt;
	&lt;br /&gt;
	my $name = shift @param;&lt;br /&gt;
	my $opt = shift @param;&lt;br /&gt;
	my $value = join(&amp;quot;&amp;quot;, @param);&lt;br /&gt;
	&lt;br /&gt;
	if(!defined($Hello_gets{$opt})) {&lt;br /&gt;
		my @cList = keys %Hello_gets;&lt;br /&gt;
		return &amp;quot;Unknown argument $opt, choose one of &amp;quot; . join(&amp;quot; &amp;quot;, @cList);&lt;br /&gt;
	}&lt;br /&gt;
    $hash-&amp;gt;{STATE} = $Hello_gets{$opt} = $value;&lt;br /&gt;
    &lt;br /&gt;
	return &amp;quot;$opt set to $value. Try to get it.&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
sub Hello_Attr(@) {&lt;br /&gt;
	my ($cmd,$name,$attr_name,$attr_value) = @_;&lt;br /&gt;
	if($cmd eq &amp;quot;set&amp;quot;) {&lt;br /&gt;
        if($attr_name eq &amp;quot;formal&amp;quot;) {&lt;br /&gt;
			if($attr_value !~ /^yes|no$/) {&lt;br /&gt;
			    my $err = &amp;quot;Invalid argument $attr_value to $attr_name. Must be yes or no.&amp;quot;;&lt;br /&gt;
			    Log 3, &amp;quot;Hello: &amp;quot;.$err;&lt;br /&gt;
			    return $err;&lt;br /&gt;
			}&lt;br /&gt;
		} else {&lt;br /&gt;
		    return &amp;quot;Unknown attr $attr_name&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
1;&lt;br /&gt;
&lt;br /&gt;
=pod&lt;br /&gt;
=begin html&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a name=&amp;quot;Hello&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;Hello&amp;lt;/h3&amp;gt;&lt;br /&gt;
&amp;lt;ul&amp;gt;&lt;br /&gt;
    &amp;lt;i&amp;gt;Hello&amp;lt;/i&amp;gt; implements the classical &amp;quot;Hello World&amp;quot; as a starting point for module development. &lt;br /&gt;
    You may want to copy 98_Hello.pm to start implementing a module of your very own. See &lt;br /&gt;
    &amp;lt;a href=&amp;quot;http://wiki.fhem.de/wiki/DevelopmentModuleIntro&amp;quot;&amp;gt;DevelopmentModuleIntro&amp;lt;/a&amp;gt; for an &lt;br /&gt;
    in-depth instruction to your first module.&lt;br /&gt;
    &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
    &amp;lt;a name=&amp;quot;Hellodefine&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Define&amp;lt;/b&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
        &amp;lt;code&amp;gt;define &amp;amp;lt;name&amp;amp;gt; Hello &amp;amp;lt;greet&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
        Example: &amp;lt;code&amp;gt;define HELLO Hello TurnUrRadioOn&amp;lt;/code&amp;gt;&lt;br /&gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
        The &amp;quot;greet&amp;quot; parameter has no further meaning, it just demonstrates&lt;br /&gt;
        how to set a so called &amp;quot;Internal&amp;quot; value. See &amp;lt;a href=&amp;quot;http://fhem.de/commandref.html#define&amp;quot;&amp;gt;commandref#define&amp;lt;/a&amp;gt; &lt;br /&gt;
        for more info about the define command.&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
    &amp;lt;br&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;a name=&amp;quot;Helloset&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Set&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
        &amp;lt;code&amp;gt;set &amp;amp;lt;name&amp;amp;gt; &amp;amp;lt;option&amp;amp;gt; &amp;amp;lt;value&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
        You can &amp;lt;i&amp;gt;set&amp;lt;/i&amp;gt; any value to any of the following options. They&#039;re just there to &lt;br /&gt;
        &amp;lt;i&amp;gt;get&amp;lt;/i&amp;gt; them. See &amp;lt;a href=&amp;quot;http://fhem.de/commandref.html#set&amp;quot;&amp;gt;commandref#set&amp;lt;/a&amp;gt; &lt;br /&gt;
        for more info about the set command.&lt;br /&gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
        Options:&lt;br /&gt;
        &amp;lt;ul&amp;gt;&lt;br /&gt;
              &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;satisfaction&amp;lt;/i&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
                  Defaults to &amp;quot;no&amp;quot;&amp;lt;/li&amp;gt;&lt;br /&gt;
              &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;whatyouwant&amp;lt;/i&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
                  Defaults to &amp;quot;can&#039;t&amp;quot;&amp;lt;/li&amp;gt;&lt;br /&gt;
              &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;whatyouneed&amp;lt;/i&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
                  Defaults to &amp;quot;try sometimes&amp;quot;&amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;/ul&amp;gt;&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
    &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;a name=&amp;quot;Helloget&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Get&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
        &amp;lt;code&amp;gt;get &amp;amp;lt;name&amp;amp;gt; &amp;amp;lt;option&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
        You can &amp;lt;i&amp;gt;get&amp;lt;/i&amp;gt; the value of any of the options described in &lt;br /&gt;
        &amp;lt;a href=&amp;quot;#Helloset&amp;quot;&amp;gt;paragraph &amp;quot;Set&amp;quot; above&amp;lt;/a&amp;gt;. See &lt;br /&gt;
        &amp;lt;a href=&amp;quot;http://fhem.de/commandref.html#get&amp;quot;&amp;gt;commandref#get&amp;lt;/a&amp;gt; for more info about &lt;br /&gt;
        the get command.&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
    &amp;lt;br&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;a name=&amp;quot;Helloattr&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
    &amp;lt;b&amp;gt;Attributes&amp;lt;/b&amp;gt;&lt;br /&gt;
    &amp;lt;ul&amp;gt;&lt;br /&gt;
        &amp;lt;code&amp;gt;attr &amp;amp;lt;name&amp;amp;gt; &amp;amp;lt;attribute&amp;amp;gt; &amp;amp;lt;value&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
        See &amp;lt;a href=&amp;quot;http://fhem.de/commandref.html#attr&amp;quot;&amp;gt;commandref#attr&amp;lt;/a&amp;gt; for more info about &lt;br /&gt;
        the attr command.&lt;br /&gt;
        &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
        Attributes:&lt;br /&gt;
        &amp;lt;ul&amp;gt;&lt;br /&gt;
            &amp;lt;li&amp;gt;&amp;lt;i&amp;gt;formal&amp;lt;/i&amp;gt; no|yes&amp;lt;br&amp;gt;&lt;br /&gt;
                When you set formal to &amp;quot;yes&amp;quot;, all output of &amp;lt;i&amp;gt;get&amp;lt;/i&amp;gt; will be in a&lt;br /&gt;
                more formal language. Default is &amp;quot;no&amp;quot;.&lt;br /&gt;
            &amp;lt;/li&amp;gt;&lt;br /&gt;
        &amp;lt;/ul&amp;gt;&lt;br /&gt;
    &amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=end html&lt;br /&gt;
&lt;br /&gt;
=cut&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der HTML-Code zwischen den Tags &amp;lt;code&amp;gt;=pod&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;=cut&amp;lt;/code&amp;gt; dient zur Generierung der commandref.html. Der HTML-Inhalt wird automatisch beim Verteilen des Moduls im Rahmen des Update-Mechanismus aus jedem Modul extrahiert und daraus die Commandref in verschiedenen Sprachen erstellt. Eine detaillierte Beschreibung wie ein Commandref-Abschnitt in einem Modul definiert wird, siehe: [[Guidelines zur Dokumentation]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevIo&amp;diff=26254</id>
		<title>DevIo</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevIo&amp;diff=26254"/>
		<updated>2018-03-24T10:05:01Z</updated>

		<summary type="html">&lt;p&gt;Eisler: /* TCP/IP-Verbindung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Dienstfunktionen für die Kommunikation per serieller Schnittstelle (USB/RS232), TCP/IP-Verbindung oder UNIX-Socket&lt;br /&gt;
|ModType=u&lt;br /&gt;
|ModForumArea=FHEM Development&lt;br /&gt;
|ModTechName=DevIo.pm&lt;br /&gt;
|ModOwner=rudolfkoenig ({{Link2FU|8|Forum}} / [[Benutzer Diskussion:Rudolfkoenig|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Das Modul [[DevIo]](.pm) ist für Modulentwickler gedacht, um Daten zwischen einem FHEM-Modul und bspw. einer seriellen Schnittstelle, einer TCP/IP-Verbindung oder einem UNIX-Socket auszutauschen. Es übernimmt dabei die gesamte Verbindungsverwaltung und Aufrechterhaltung innerhalb von FHEM und nimmt dem Modulentwickler daher die gesamte Verbindungsverwaltung (Aufbau, Initialisierung, Neu-Verbindung bei Abbruch, etc.) ab. Es berücksichtigt dabei Besonderheiten zwischen Unix-basierten Betriebssystemen und Windows.&lt;br /&gt;
&lt;br /&gt;
Es dient dabei lediglich dem Zweck einen Kommunikationskanal für eine Definition in FHEM zu etablieren und Daten darüber auszutauschen. Die Interpretation der empfangenen Daten obliegt dem Modul, welches die Verbindung via DevIo geöffnet hat. Die ausgetauschten Daten werden durch DevIo nicht verändert.&lt;br /&gt;
&lt;br /&gt;
= Allgemeine Funktionsweise =&lt;br /&gt;
DevIo hat das Ziel eine besonders einfache Möglichkeit für Modulentwickler zu schaffen, um einen dauerhaften Kommunikationskanal mit einem Hardware- oder Netzwerk-Gerät bzw. Service zu etablieren. Um eine Verbindung aufzubauen muss zunächst die Gegenstelle bekannt sein. Dazu muss vor dem Verbindungsaufbau in dem Internal &amp;lt;code&amp;gt;$hash-&amp;gt;{DeviceName}&amp;lt;/code&amp;gt; der jeweiligen Definition das Ziel hinterlegt werden. Dies kann bspw. eine serielle Schnittstelle sein (z.B. &amp;quot;/dev/ttyUSB0&amp;quot; oder &amp;quot;COM1&amp;quot; unter Windows) oder eine TCP/IP-Gegenstelle (z.B. &amp;quot;192.168.1.100:1012&amp;quot;) sein. Eine detaillierte Aufstellung der möglichen Verbindungsarten und deren Angabe in &amp;lt;code&amp;gt;$hash-&amp;gt;{DeviceName}&amp;lt;/code&amp;gt; gibt es im folgenden Kapitel [[#Unterstützte Verbindungsarten|Unterstützte Verbindungsarten]].&lt;br /&gt;
&lt;br /&gt;
Die Funktion [[#DevIo_OpenDev()|DevIo_OpenDev()]] baut dabei die entsprechende Verbindung für eine einzelne Definition in Form eines Filedeskriptors auf und registriert diesen in dem globalen Hash &amp;lt;code&amp;gt;%selectlist&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;selectlist&amp;quot;&amp;gt;[[DevelopmentModuleIntro#Wichtige_globale_Variablen_aus_fhem.pl|Development Module Introduction]] - Wichtige globale Variablen aus fhem.pl&amp;lt;/ref&amp;gt;. Sobald die Verbindung erfolgreich aufgebaut wurde, kann eine, durch den Modulautor mitgelieferte, Initialisierungs-Funktion ausgeführt werden, um die Kommunikation zu initialiseren (bspw. das Senden einer Authentifizierungs-/Loginsequenz oder aktivieren der Hardware, etc.).&lt;br /&gt;
&lt;br /&gt;
FHEM (respektive fhem.pl) prüft nun regelmäßig, ob Daten zum Lesen bereitstehen (also Daten empfangen wurden). Ist dies der Fall, so wird die [[DevelopmentModuleIntro#X_Read|X_Read()]]-Funktion des zugehörigen Moduls für die hinterlegte Definition aufgerufen. Hier können die Daten durch den Aufruf von [[#DevIo_SimpleRead()|DevIo_SimpleRead()]] nun eingelesen und verarbeitet werden. Das Senden von Daten ist durch den Aufruf von  [[#DevIo_SimpleWrite()|DevIo_SimpleWrite()]] sehr einfach möglich.&lt;br /&gt;
&lt;br /&gt;
Sollte die Verbindung zusammenbrechen (USB-Gerät abgezogen, Gerät per Netzwerk nicht mehr erreichbar, etc.), so erkennt dies DevIo und registriert die Definition in &amp;lt;code&amp;gt;%readyfnlist&amp;lt;/code&amp;gt;&amp;lt;ref name=&amp;quot;selectlist&amp;quot; /&amp;gt;. FHEM führt nun regelmäßig die [[DevelopmentModuleIntro#X_Ready|X_Ready()]]-Funktion des zugehörigen Moduls&lt;br /&gt;
aus um zu prüfen, ob die Verbindung wieder aufgebaut werden kann. Hier wird nun durch die Ausführung von [[#DevIo_OpenDev()|DevIo_OpenDev()]] versucht die Verbindung wieder herzustellen.&lt;br /&gt;
&lt;br /&gt;
Sobald die Verbindung nicht mehr benötigt wird, oder FHEM bspw. beendet wird, kann die Verbindung via [[#DevIo_CloseDev()|DevIo_CloseDev()]] sauber geschlossen werden.&lt;br /&gt;
&lt;br /&gt;
= Unterstützte Verbindungsarten = &lt;br /&gt;
&lt;br /&gt;
Die folgenden Verbindungsarten können via DevIo realisiert werden.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Verbindungsart !! Beispielangabe in&amp;lt;br&amp;gt;&amp;lt;code&amp;gt;$hash-&amp;gt;{DeviceName}&amp;lt;/code&amp;gt; !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;Serielle Schnittstelle&#039;&#039;&#039;&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; |&lt;br /&gt;
* &amp;lt;code&amp;gt;/dev/ttyUSB0&amp;lt;/code&amp;gt; &lt;br /&gt;
* &amp;lt;code&amp;gt;/dev/ttyUSB0@9600&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;/dev/ttyUSB0@9600,7,E,2&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;/dev/ttyUSB0@directio&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;COM1@9600&amp;lt;/code&amp;gt;&lt;br /&gt;
|| &lt;br /&gt;
Durch Angabe eines Geräts in Form eines Gerätepfad (UNIX-basierte Betriebssysteme) oder der Schnittstellenbezeichnung aus Windows kann eine serielle Verbindung geöffnet werden. Der Gerätename kann zusätzliche Angaben zu Baudrate, Datenbits, Parität und Stoppbits enthalten um die Verbindung entsprechend zu konfiguieren. Diese Angaben sind durch ein &amp;quot;@&amp;quot; getrennt an die Gerätebezeichnung angehangen:&lt;br /&gt;
&lt;br /&gt;
Schematische Syntax: &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;&amp;lt;Gerät&amp;gt;&amp;lt;/font&amp;gt;&#039;&#039;&#039;&#039;&#039;@&#039;&#039;&#039;&#039;&#039;&amp;lt;Baudrate&amp;gt;&#039;&#039;&#039;&#039;&#039;,&#039;&#039;&#039;&#039;&#039;&amp;lt;Datenbits&amp;gt;&#039;&#039;&#039;&#039;&#039;,&#039;&#039;&#039;&#039;&#039;&amp;lt;Parität&amp;gt;&#039;&#039;&#039;&#039;&#039;,&#039;&#039;&#039;&#039;&#039;&amp;lt;Stopbits&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Baudrate&amp;gt;&amp;lt;/code&amp;gt; - Eine gültige Taktfrequenz (Symbole pro Sekunde) mit der die Schnittstelle geöffnet werden soll (Beispiel: &amp;lt;code&amp;gt;9600&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;14400&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;115200&amp;lt;/code&amp;gt;, ...)&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Datenbits&amp;gt;&amp;lt;/code&amp;gt; - Die Anzahl an Datenbits (7 oder 8 Datenbits). Standardwert sofern nicht angegeben sind 8 Datenbits.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Parität&amp;gt;&amp;lt;/code&amp;gt; - Die zu verwendende Parität, sofern benötigt. Dabei bedeutet der Wert &amp;lt;code&amp;gt;N&amp;lt;/code&amp;gt; keine Parität, &amp;lt;code&amp;gt;O&amp;lt;/code&amp;gt; bedeutet ungleiche Parität (odd) und &amp;lt;code&amp;gt;E&amp;lt;/code&amp;gt; bedeutet gleiche Parität (even) Standardwert sofern nicht angegeben ist &amp;lt;code&amp;gt;N&amp;lt;/code&amp;gt; (keine Parität).&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Stopbits&amp;gt;&amp;lt;/code&amp;gt; - Die Anzahl an zu verwendenden Stopbits (0, 1 oder 2 Stopbits). Standardwert sofern nicht angegeben sind 0 Stopbits.&lt;br /&gt;
&lt;br /&gt;
Wenn man unter Unix-basierten Betriebssystemen die Schnittstelle nicht explizit konfiguriert öffnen möchte, sondern das Gerät direkt öffnen möchte (und damit die OS-Einstellungen verwendet), kann man durch Angabe von &amp;lt;code&amp;gt;@directio&amp;lt;/code&amp;gt; die Konfiguration der Schnittstelle umgehen und das Gerät direkt öffnen:&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;&amp;lt;font color=&amp;quot;grey&amp;quot;&amp;gt;/dev/ttyUSB0&amp;lt;/font&amp;gt;@directio&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unter Windows verwendet man als Gerätename die entsprechende Schnittstellenbezeichung wie bspw. &amp;lt;code&amp;gt;COM1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;COM2&amp;lt;/code&amp;gt;, usw.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;TCP/IP-Verbindung&#039;&#039;&#039;&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &lt;br /&gt;
* &amp;lt;code&amp;gt;192.168.1.2:1012&amp;lt;/code&amp;gt; &lt;br /&gt;
* &amp;lt;code&amp;gt;raspberry:5000&amp;lt;/code&amp;gt;&lt;br /&gt;
||&lt;br /&gt;
Durch Angabe eines Hostnamen oder IP-Adresse und einem Port, kann eine TCP-Verbindung aufgebaut werden. Dazu muss Hostname/IP-Adresse und Port in folgendem Schema angegeben werden:&lt;br /&gt;
&lt;br /&gt;
Schematische Syntax: &amp;lt;code&amp;gt;&#039;&#039;&amp;lt;Hostname/IP-Adresse&amp;gt;&#039;&#039;&#039;&#039;&#039;:&#039;&#039;&#039;&#039;&#039;&amp;lt;Port&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Hostname/IP-Adresse&amp;gt;&amp;lt;/code&amp;gt; - Der Hostname oder die IP-Adresse des zu verbindenden Gerätes/Server.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Port&amp;gt;&amp;lt;/code&amp;gt; - Der Zielport zu dem sich verbunden werden soll (0-65535).&lt;br /&gt;
&lt;br /&gt;
Die Verbindung kann optional verschlüsselt via SSL/TLS aufgebaut werden. Dazu muss das Internal &amp;lt;code&amp;gt;$hash-&amp;gt;{SSL}&amp;lt;/code&amp;gt; auf 1 gesetzt werden.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;UNIX-Socket&#039;&#039;&#039;&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot;|&lt;br /&gt;
* &amp;lt;code&amp;gt;UNIX:SEQPACKET:/var/tmp/me_avm_home_external.ctl&amp;lt;/code&amp;gt; &lt;br /&gt;
* &amp;lt;code&amp;gt;UNIX:STREAM:/var/socket/con&amp;lt;/code&amp;gt;&lt;br /&gt;
|| &lt;br /&gt;
Durch Angabe eines Pfads zu einem UNIX-Domain-Socket kann eine Kommunikation mit einem anderen Prozess aufgebaut werden (Inter-Prozess-Kommunikation). Man kann den Socket dabei paketorientiert (&amp;quot;SEQPACKET&amp;quot;) oder als Stream (&amp;quot;STREAM&amp;quot;) öffnen. &lt;br /&gt;
&lt;br /&gt;
Schematische Syntax: &amp;lt;code&amp;gt;UNIX:&#039;&#039;&amp;lt;Typ&amp;gt;&#039;&#039;:&#039;&#039;&amp;lt;Pfad&amp;gt;&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Typ&amp;gt;&amp;lt;/code&amp;gt; - Der Typ des Sockets. Für einen paketorientierten Socket ist hier &amp;lt;code&amp;gt;SEQPACKET&amp;lt;/code&amp;gt; zu verwenden, für einen streamorientierten Socket &amp;lt;code&amp;gt;STREAM&amp;lt;/code&amp;gt;. Der Typ muss immer mit angegeben werden.&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;lt;Pfad&amp;gt;&amp;lt;/code&amp;gt; - Der Pfad im Dateisystem zu dem gewünschten UNIX-Socket&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;FHEM IO-Modul&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; |&lt;br /&gt;
* &amp;lt;code&amp;gt;FHEM:DEVIO:Firmata_SerielleSchnittstelle@9600&amp;lt;/code&amp;gt; &lt;br /&gt;
|| &lt;br /&gt;
Beschreibung siehe {{Link2Forum|Topic=46276}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Wichtige Internals zur Konfiguration =&lt;br /&gt;
&lt;br /&gt;
Da DevIo ausschließlich definitionsbezogen arbeitet, erfolgt eine Konfiguration von DevIo über Internals, die im übergebenen &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; gesetzt werden müssen (oder können). Hiermit lässt sich das Verhalten von DevIo entsprechend beeinflussen. &lt;br /&gt;
&lt;br /&gt;
Hier eine Auflistung von allen Internals, die DevIo beeinflussen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;min-width: 13em;&amp;quot; | Internal !!  Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &amp;lt;code&amp;gt;$hash-&amp;gt;{DeviceName}&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; |  Die Gegenstelle zu der eine Verbindung aufgebaut werden soll. Die möglichen Werte und deren Syntax ist im Kapitel [[#Unterstützte Verbindungsarten|Unterstützte Verbindungsarten]] genauer beschrieben.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &amp;lt;code&amp;gt;$hash-&amp;gt;{nextOpenDelay}&amp;lt;/code&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die Zeit in Sekunden, welche im Falle eines Verbindungsabbruchs gewartet werden soll, bevor ein erneuter Verbindungsversuch stattfindet. &lt;br /&gt;
&lt;br /&gt;
Standardwert: 60 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &amp;lt;code&amp;gt;$hash-&amp;gt;{TIMEOUT}&amp;lt;/code&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die maximale Zeit in Sekunden für den Aufbau einer TCP/IP-Verbindung. Sollte diese Zeit überschritten werden, bricht der Verbindungsaufbau mit einer Fehlermeldung ab.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG:&#039;&#039;&#039; Sollte beim Aufruf von [[#DevIo_OpenDev|DevIo_OpenDev()]] keine Callback-Funktion parametrisiert sein und die Gegenstelle antwortet beim Verbindungsaufbau nicht, so wird FHEM für die Dauer von &amp;lt;code&amp;gt;$hash-&amp;gt;{TIMEOUT}&amp;lt;/code&amp;gt; blockiert.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 3 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &amp;lt;code&amp;gt;$hash-&amp;gt;{SSL}&amp;lt;/code&amp;gt;  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Flag (0 oder 1), ob eine TCP/IP-Verbindung verschlüsselt (via SSL/TLS) aufgebaut werden soll. Wenn dieses Flag auf 1 gesetzt ist, wird nach erfolgtem Verbindungsaufbau eine SSL-Session initiiert.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0 (keine Verschlüsselung)&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &amp;lt;code&amp;gt;$hash-&amp;gt;{devioLoglevel}&amp;lt;/code&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Das Loglevel in dem &amp;lt;code&amp;gt;disconnected&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;reappeared&amp;lt;/code&amp;gt; Meldungen geloggt werden sollen. Standardmäßig werden solche Verbindungsabbrüche (&amp;lt;code&amp;gt;disconnected&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;reappeared&amp;lt;/code&amp;gt;) im Loglevel 1 geloggt. Die erfolgreiche Erstverbindung wird standardmäßig im Loglevel 3 geloggt. Durch das Setzen von &amp;lt;code&amp;gt;$hash-&amp;gt;{devioLoglevel}&amp;lt;/code&amp;gt; werden diese Meldungen allesamt in dem gesetzten Loglevel ausgegeben. Details dazu siehe {{Link2Forum|Topic=61970}}.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Die Funktionen =&lt;br /&gt;
== DevIo_OpenDev() ==&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$error = DevIo_OpenDev($hash, $reopen, $initfn);&lt;br /&gt;
$error = DevIo_OpenDev($hash, $reopen, $initfn, $callback);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_OpenDev() öffnet eine Verbindung zu dem Endpunkt der in &amp;lt;code&amp;gt;$hash-&amp;gt;{DeviceName}&amp;lt;/code&amp;gt; hinterlegt ist. Sobald die Verbindung erfolgreich hergestellt wurde, wird die Funktion &amp;lt;code&amp;gt;$initfn&amp;lt;/code&amp;gt; ausgeführt, sofern gesetzt, um die Verbindung zu initialisieren. Sofern eine TCP/IP-Verbindung hergestellt wird, kann eine optionale Callback-Funktion &amp;lt;code&amp;gt;$callback&amp;lt;/code&amp;gt; übergeben werden um den Verbindungsaufbau non-blocking durchzuführen.&lt;br /&gt;
&lt;br /&gt;
Der Rückgabewert enthält im Fehlerfall eine entsprechende Fehlermeldung. Im Erfolgsfall wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben. Sofern eine TCP/IP-Verbindung hergestellt wird und eine Callback-Funktion &amp;lt;code&amp;gt;$callback&amp;lt;/code&amp;gt; übergeben wurde, wird immer &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben, da ein evtl. Fehler an diese Callback-Funktion nach dem erfolgten Verbindungsversuch mitgeteilt wird.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | Die Hash-Referenz der Definition, für die eine Verbindung geöffnet werden soll&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$reopen&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&amp;lt;br&amp;gt;can be &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | Flag (0 oder 1), ob es sich um einen erneuten Verbindungsversuch handelt (im Rahmen der [[DevelopmentModuleIntro#X_Ready|X_Ready()]]-Funktion). Sollte es der erste Verbindungsversuch sein, so muss dieser Parameter den Wert 0 besitzen.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$initfn&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&amp;lt;br&amp;gt;can be &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt;&#039;&#039;&lt;br /&gt;
|| Der Name (als Zeichenkette) oder die Referenz auf eine Modulfunktion, welche optional nach dem erfolgreichen Aufbau/Wiederaufbau der Verbindung ausgeführt werden soll. Im Rahmen dieser Funktion kann weiterführende Kommunikation über die aufgebaute Verbindung erfolgen um zum Beispiel eine Loginsequenz oder eine Konfiguration der Gegenseite vorzunehmen, bevor die Verbindung allgemein benutzt werden kann. &lt;br /&gt;
&lt;br /&gt;
Die Funktion welche in &amp;lt;code&amp;gt;$initfn&amp;lt;/code&amp;gt; angegeben wurde, wird mit folgenden Parametern aufgerufen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$ret = MYMODULE_InitFn($hash)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn der Rückgabewert &amp;lt;code&amp;gt;$ret&amp;lt;/code&amp;gt; einen Inhalt zurückliefert, wird die Initialisierung als fehlgeschlagen bewertet und die Verbindung wieder geschlossen um einen neuen Verbindungsversuch zu einem späteren Zeitpunkt zu versuchen. Der genaue Inhalt von &amp;lt;code&amp;gt;$ret&amp;lt;/code&amp;gt; ist dabei unerheblich, da er nicht weiterverwendet wird. Die Rückgabewerte &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; signalisieren dabei eine erfolgreiche Initialisierung.&lt;br /&gt;
&lt;br /&gt;
Beispiel: &lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;MYMODULE_InitFn&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;\&amp;amp;MYMODULE_InitFn&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Der Name (als Zeichenkette) oder die Referenz auf eine Modulfunktion, welche aufgerufen werden soll um evtl. Fehlermeldungen beim Verbindungsaufbau einer TCP/IP-Verbindung an das Modul zurückzuliefern. Wenn eine Callback-Funktion gesetzt ist, erfolgt der Verbindungsaufbau non-blocking. Andernfalls wartet DevIo_OpenDev() bis die Verbindung steht bzw. ein Fehler auftritt (z.B. Timeout).&lt;br /&gt;
&lt;br /&gt;
Die Funktion welche in &amp;lt;code&amp;gt;$callback&amp;lt;/code&amp;gt; angegeben wurde, wird mit folgenden Parametern aufgerufen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;MYMODULE_ConnectCallbackFn($hash, $error)&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Argument &amp;lt;code&amp;gt;$error&amp;lt;/code&amp;gt; enthält dabei eine Fehlermeldung als Zeichenkette welche die aufgetretene Fehlermeldung enthält. Der Rückgabewert der Callback-Funktion wird nicht ausgewertet und ist daher irrelevant.&lt;br /&gt;
&lt;br /&gt;
Beispiel: &lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;MYMODULE_ConnectCallbackFn&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;\&amp;amp;MYMODULE_ConnectCallbackFn&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewert:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabe!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$error &amp;lt;/code&amp;gt;&#039;&#039;&#039; || Eine Fehlermeldung als Zeichenkette, sollte der Verbindungsaufbau fehlschlagen. Im Erfolgsfall wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wenn es sich um eine TCP/IP-Verbindung handelt und eine Callback-Funktion als Parameter &amp;lt;code&amp;gt;$callback&amp;lt;/code&amp;gt; angegeben wurde, wird immer &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben, andernfalls erfolgt der Verbindungsaufbau blocking und eine evtl. Fehlermeldung wird als Rückgabewert zurückgegeben. Im Erfolgsfall wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_IsOpen() ==&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$status = DevIo_IsOpen($hash);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_IsOpen() prüft, ob eine Verbindung für &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; geöffnet ist. Falls ja, wird das zugehörige IO-Objekt zurückgegeben, andernfalls &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Diese Funktion kann dabei direkt in typischen if-Konstrukten verwendet werden um zu prüfen, ob eine Verbindung für die jeweilige Definition geöffnet oder geschlossen ist.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, deren Verbindung geprüft werden soll.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$status&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Der Status der Verbindung. Wenn eine Verbindung besteht, wird das zugehörige IO-Objekt zurückgegeben. Falls keine Verbindung besteht, wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_SimpleRead() ==&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$buf = DevIo_SimpleRead($hash);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_SimpleRead() liest anstehende Daten für die Verbindung von &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; ein und gibt diese zurück.&lt;br /&gt;
&lt;br /&gt;
Sollte beim Versuch Daten zu lesen eine geschlossene Verbindung erkannt werden, so wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben und die Verbindung geschlossen. Es erfolgt zu einem späteren Zeitpunkt (siehe Internal &amp;lt;code&amp;gt;$hash-&amp;gt;{nextOpenDelay}&amp;lt;/code&amp;gt; aus Kapitel [[#Wichtige Internals zur Konfiguration|Wichtige Internals zur Konfiguration]]) ein neuer Verbindungsversuch.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, für deren Verbindung aktuell anstehende Daten gelesen werden sollen.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewert:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabe!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$buf&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die zu lesenden Daten als Zeichenkette. Im Falle eines Verbindungsabruchs wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_SimpleReadWithTimeout() ==&lt;br /&gt;
{{Randnotiz|RNTyp=Warn|RNText=&amp;lt;u&amp;gt;&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039;&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der Benutzung von DevIo_SimpleReadWithTimeout() wird FHEM für die Dauer von bis zu &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden blockiert, sollten keine Daten bis dahin zum Lesen bereitstehen.}}&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$buf = DevIo_SimpleReadWithTimeout($hash, $timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_SimpleReadWithTimeout() wartet maximal &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden bis die Verbindung von &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; Daten zum einlesen bereitstellt und gibt diese zurück. Sollte nach dem Warten von &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; noch immer keine Daten zum Lesen bereitstehen, so wird ein Leerstring zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, für deren Verbindung aktuell anstehende Daten gelesen werden sollen.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die maximale Wartezeit in Sekunden.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewert:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabe!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$buf&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die zu lesenden Daten als Zeichenkette.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_TimeoutRead() ==&lt;br /&gt;
{{Randnotiz|RNTyp=Warn|RNText=&amp;lt;u&amp;gt;&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039;&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bei der Benutzung von DevIo_TimeoutRead() wird FHEM für die Dauer von &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden blockiert.}}&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$buf = DevIo_TimeoutRead($hash, $timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_SimpleReadWithTimeout() wartet &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden und liest sämtliche Daten für die Verbindung von &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; ein, die in dieser Zeit eintreffen. Sollten keinerlei Daten während der Wartezeit eintreffen, so wird ein Leerstring zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, für deren Verbindung Daten eingelesen werden sollen.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Wartezeit in Sekunden.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewert:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabe!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$buf&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die eingelesenen Daten als Zeichenkette.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_SimpleWrite() ==&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;DevIo_SimpleWrite($hash, $msg, $type);&lt;br /&gt;
DevIo_SimpleWrite($hash, $msg, $type, $addnl);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_SimpleWrite() sendet den Inhalt von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; über die Verbindung von &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;. Mit den beiden Argumenten &amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$addnl&amp;lt;/code&amp;gt; kann die Formatierung von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; beeinflusst werden bevor die Daten tatsächlich gesendet werden.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, über deren Verbindung die Daten gesendet werden sollen.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die zu schreibenden Daten als Zeichenkette. Abhängig von &amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt; kann &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; Byte-Characters, HEX-Darstellungen oder normale ASCII-Zeichen enthalten.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Art des Inhalts von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt;. Abhängig von dem Inhalt von &amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt; wird &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; entsprechend geändert oder nicht. Desweiteren werden die Daten für Logausgaben evtl. leserlich gemacht.&lt;br /&gt;
&lt;br /&gt;
Folgende Werte sind möglich:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; enthält Daten in Byteform. Der Inhalt von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; wird 1:1 gesendet. Zur besseren Lesbarkeit werden die Daten bei Logausgaben in HEX-Darstellung umgewandelt.&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; enthält Binärdaten in HEX-Darstellung (0-9/A-F). Der Inhalt von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; wird vorher in Byteform umgewandelt und anschließend gesendet. In Logausgaben wird die HEX-Darstellung wie übergeben verwendet.&lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt; - &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; enthält normale ASCII-Textzeichen. Der Inhalt von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; wird 1:1 gesendet. In Logausgaben wird &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; wie übergeben ausgegeben.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$addnl&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Flag (0/1) welches, sofern aktiviert, einen Zeilenumbruch (&amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt;) an &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; anfügt, bevor es gesendet wird.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_Expect() ==&lt;br /&gt;
{{Randnotiz|RNTyp=Warn|RNText=&amp;lt;u&amp;gt;&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039;&amp;lt;/u&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Bei der Benutzung von DevIo_Expect() wird FHEM für die Dauer von bis zu &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden blockiert.&lt;br /&gt;
# Sollte im ersten Versuch keine Antwort auf die zuvor gesendeten Daten innerhalb von &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden eintreffen, wird die Verbindung geschlossen und neu aufgebaut. Es erfolgt dabei &#039;&#039;&#039;KEINE INITIALISIERUNG&#039;&#039;&#039; durch &amp;lt;code&amp;gt;$initfn&amp;lt;/code&amp;gt; aus [[#DevIo_OpenDev()|DevIo_OpenDev()]].&lt;br /&gt;
}}&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$buf = DevIo_Expect($hash, $msg, $timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_Expect() sendet den Inhalt von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; und wartet bis zu &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden auf eine Antwort. Sollte in dieser Zeit keine Antwort eintreffen, so wird die Verbindung einmalig geschlossen, erneut geöffnet (ohne Aufruf einer Initialisierungs-Funktion) und der Vorgang wiederholt. Die empfangene Antwort wird anschließend als Funktionsergebnis zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Wenn beim ersten Versuch keine Antwort innerhalb von &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden eintrifft, wird das Event &amp;lt;code&amp;gt;FAILED&amp;lt;/code&amp;gt; generiert (siehe [[#Generierte Events|Generierte Events]]). Anschließend wird die Verbindung geschlossen, neu geöffnet (ohne Initialisierung) und &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; erneut gesendet. Sollte nun eine Antwort eintreffen, so wird das Event &amp;lt;code&amp;gt;CONNECTED&amp;lt;/code&amp;gt; generiert und die empfangene Antwort zurückgegeben. Sollte dennoch keine Antwort eintreffen, so wird das Event &amp;lt;code&amp;gt;DISCONNECTED&amp;lt;/code&amp;gt; getriggert.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, für deren Verbindung Daten gesendet und anschließen gelesen werden sollen.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die zu sendenden Daten auf die eine Antwort erwartet wird. Die Daten werden ohne Konvertierung direkt gesendet (entspricht &amp;lt;code&amp;gt;$type&amp;lt;/code&amp;gt; gleich &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; bei [[#DevIo_SimpleWrite()|DevIo_SimpleWrite]]).&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die maximale Wartezeit in Sekunden bis zum Eintreffen einer Antwort.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewert:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabe!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$buf&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die empfangene Antwort. Sollte es zu einem Fehler gekommen sein (Senden von &amp;lt;code&amp;gt;$msg&amp;lt;/code&amp;gt; fehlgeschlagen, keine Antwort innerhalb von &amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt; Sekunden erhalten, etc.), so wird &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== DevIo_CloseDev() ==&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;DevIo_CloseDev($hash);&lt;br /&gt;
DevIo_CloseDev($hash, $isFork);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Funktion DevIo_CloseDev() schließt eine evtl. geöffnete Verbindung für die Definition &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Optional kann man mit dem Flag &amp;lt;code&amp;gt;$isFork&amp;lt;/code&amp;gt; angeben, dass man sich aktuell in einem Fork vom Hauptprozess befindet. Dadurch wird beim Schließen von seriellen Verbindungen die Kommunikationsparameter nicht zurückgesetzt. Dies verhindert einen Ausfall der nachwievor bestehenden Verbindung im Hauptprozess.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Hash-Referenz der Definition, deren Verbindung geschlossen werden soll.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$isFork&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Flag (0 oder 1), welches angibt, dass DevIo_CloseDev() innerhalb eines geforkten Kindprozess ausgeführt wird. Dadurch werden serielle Verbindung so geschlossen, dass dabei die Kommunikationsparameter (Baudrate, Datenbits, Parität, etc.) nicht zurückgesetzt werden. Dieses Flag wird primär von dem Modul [[Blocking_Call|Blocking.pm]] verwendet um Verbindungen in einem geforkten Kindprozess zu schließen, ohne die Verbindung im Hauptprozess zu beeinträchtigen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0 (Verbindung wird im Hauptprozess geschlossen)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Generierte Events =&lt;br /&gt;
Die Funktionen von DevIo generieren verschiedene Events für die entsprechende Definition (parametrisiert durch &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt;). Diese können bspw. in der [[DevelopmentModuleIntro#X_Notify|Notify]]-Funktion des entsprechenden Moduls verarbeitet werden.&lt;br /&gt;
&lt;br /&gt;
Hier eine Übersicht der generierten Events:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Event !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;code&amp;gt;CONNECTED&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die Verbindung wurde nach einem Verbindungsabbruch erfolgreich wieder aufgebaut. Dieses Event wird nur nach einem erfolgreichen Reconnect von einer zuvor verlorenen Verbindung generiert. Es wird &#039;&#039;&#039;nicht&#039;&#039;&#039; nach der erfolgreichen Initialverbindung generiert.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;code&amp;gt;DISCONNECTED&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die Verbindung ist zusammengebrochen. Es wird ein neuer Verbindungsversuch nach &amp;lt;code&amp;gt;$hash-&amp;gt;{nextOpenDelay}&amp;lt;/code&amp;gt; Sekunden erfolgen (siehe [[#Wichtige Internals zur Konfiguration|Wichtige Internals zur Konfiguration]]).&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;&amp;lt;code&amp;gt;FAILED&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Dieses Event wird nur bei der Nutzung der Funktion [[#DevIo_Expect|DevIo_Expect()]] generiert, sofern es keine Antwort auf die zuvor gesendeten Daten gibt. Dies bedeutet konkret, dass Gerät hat nicht auf die eigene Anfrage geantwortet und die Verbindung wird daher neu aufgebaut. Details dazu, siehe [[#DevIo_Expect()|DevIo_Expect()]].&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Hinweis bei der Datenverarbeitung (Buffering) =&lt;br /&gt;
DevIo beschränkt sich ausschließlich auf die Verbindungsverwaltung. Sobald die Verbindung etabliert wurde, übernimmt FHEM (fhem.pl) die Verarbeitung von eingehenden Daten und informiert das entsprechende Modul durch Aufruf der [[DevelopmentModuleIntro#X_Read|Read]]-Funktion. Dabei hat weder DevIo, noch FHEM selber Kenntnis von der Struktur der Daten. Sowohl DevIo, als auch FHEM selber können nicht erkennen, ob die empfangenen Daten weder vollständig, noch valide in ihrem Aufbau sind. Dies alles obliegt dem Modul, welches die Verbindung initiiert hat. Man sollte daher immer davon ausgehen, dass beim Lesen von Daten (in [[DevelopmentModuleIntro#X_Read|X_Read()]]) diese unvollständig sein können, oder sogar mehrere Datagramme enthalten sein können. &lt;br /&gt;
&lt;br /&gt;
Dazu stellt DevIo dem Modulentwickler das Internal &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; in der jeweiligen Definition zur Verfügung um dort Daten zwischenzuspeichern, bis ein vollständiges Datagramm daraus verarbeitet werden kann. Alle eingelesenen Daten werden dazu in der [[DevelopmentModuleIntro#X_Read|Read]]-Funktion immer an &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; hinten angehangen. Sobald ein vollständiges Datagramm in &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; erkannt wurde, wird dieses herausgenommen und verarbeitet, solange bis kein vollständiges Datagramm in &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; erkennbar ist. Hierbei ist jedoch entscheidend, woran man ein abgeschlossenes Datagramm erkennen kann. Dies kann je nach Protokoll sehr unterschiedlich sein (bspw. Zeilenumbruch &amp;lt;code&amp;gt;\r\n&amp;lt;/code&amp;gt; oder ein Semikolon &amp;lt;code&amp;gt;;&amp;lt;/code&amp;gt;, etc.). &lt;br /&gt;
&lt;br /&gt;
DevIo initialisiert &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; nach dem erfolgreichen Verbindungsaufbau mit einem Leerstring (&amp;lt;code&amp;gt;&amp;quot;&amp;quot;&amp;lt;/code&amp;gt;). Sobald eine Verbindung mit [[#DevIo_CloseDev()|DevIo_CloseDev()]] geschlossen wird, wird &amp;lt;code&amp;gt;$hash-&amp;gt;{PARTIAL}&amp;lt;/code&amp;gt; gelöscht.&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel, wie man ein solches Buffering verwendet für ein Protokoll, welches Datagramme durch einen Zeilenumbruch &amp;lt;code&amp;gt;\n&amp;lt;/code&amp;gt; (Newline) abtrennt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub MY_MODULE_Read($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
  my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
  &lt;br /&gt;
  my $data = DevIo_SimpleRead($hash);&lt;br /&gt;
  return if(!defined($buf)); # connection lost&lt;br /&gt;
  &lt;br /&gt;
  my $buffer = $hash-&amp;gt;{PARTIAL};&lt;br /&gt;
  &lt;br /&gt;
  Log3 $name, 5, &amp;quot;MY_MODULE ($name) - received $data (buffer contains: $buf)&amp;quot;;&lt;br /&gt;
  &lt;br /&gt;
  # concat received data to $buffer&lt;br /&gt;
  $buffer .= $data;&lt;br /&gt;
&lt;br /&gt;
  # as long as the buffer contains newlines (complete datagramm)&lt;br /&gt;
  while($buffer =~ m/\n/)&lt;br /&gt;
  {&lt;br /&gt;
    my $msg;&lt;br /&gt;
    &lt;br /&gt;
    # extract the complete message ($msg), everything else is assigned to $buffer&lt;br /&gt;
    ($msg, $buffer) = split(&amp;quot;\n&amp;quot;, $buffer, 2);&lt;br /&gt;
    &lt;br /&gt;
    # remove trailing whitespaces&lt;br /&gt;
    chomp $msg;&lt;br /&gt;
&lt;br /&gt;
    # update $hash-&amp;gt;{PARTIAL} with the current buffer content&lt;br /&gt;
    $hash-&amp;gt;{PARTIAL} = $buffer; &lt;br /&gt;
    &lt;br /&gt;
    # parse the extracted message&lt;br /&gt;
    MY_MODULE_ParseMessage($hash, $msg);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Beispielimplementierung in einem Modul =&lt;br /&gt;
== serielle Verbindung ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
package main;&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
use DevIo; # load DevIo.pm if not already loaded&lt;br /&gt;
&lt;br /&gt;
# called upon loading the module MY_MODULE&lt;br /&gt;
sub MY_MODULE_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
&lt;br /&gt;
  $hash-&amp;gt;{DefFn}    = &amp;quot;MY_MODULE_Define&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{UndefFn}  = &amp;quot;MY_MODULE_Undef&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{SetFn}    = &amp;quot;MY_MODULE_Set&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{ReadFn}   = &amp;quot;MY_MODULE_Read&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{ReadyFn}  = &amp;quot;MY_MODULE_Ready&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when a new definition is created (by hand or from configuration read on FHEM startup)&lt;br /&gt;
sub MY_MODULE_Define($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash, $def) = @_;&lt;br /&gt;
  my @a = split(&amp;quot;[ \t]+&amp;quot;, $def);&lt;br /&gt;
&lt;br /&gt;
  my $name = $a[0];&lt;br /&gt;
  &lt;br /&gt;
  # $a[1] is always equals the module name &amp;quot;MY_MODULE&amp;quot;&lt;br /&gt;
  &lt;br /&gt;
  # first argument is a serial device (e.g. &amp;quot;/dev/ttyUSB0@9600&amp;quot;)&lt;br /&gt;
  my $dev = $a[2]; &lt;br /&gt;
&lt;br /&gt;
  return &amp;quot;no device given&amp;quot; unless($dev);&lt;br /&gt;
  &lt;br /&gt;
  # add a default baud rate (9600), if not given by user&lt;br /&gt;
  $dev .= &#039;@9600&#039; if(not $dev =~ m/\@\d+$/);&lt;br /&gt;
  &lt;br /&gt;
  $hash-&amp;gt;{DeviceName} = $dev;&lt;br /&gt;
  &lt;br /&gt;
  # close connection if maybe open (on definition modify)&lt;br /&gt;
  DevIo_CloseDev($hash) if(DevIo_IsOpen($hash));  &lt;br /&gt;
  &lt;br /&gt;
  # open connection with custom init function&lt;br /&gt;
  my $ret = DevIo_OpenDev($hash, 0, &amp;quot;MY_MODULE_Init&amp;quot;); &lt;br /&gt;
 &lt;br /&gt;
  return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when definition is undefined &lt;br /&gt;
# (config reload, shutdown or delete of definition)&lt;br /&gt;
sub MY_MODULE_Undef($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash, $name) = @_;&lt;br /&gt;
 &lt;br /&gt;
  # close the connection &lt;br /&gt;
  DevIo_CloseDev($hash);&lt;br /&gt;
  &lt;br /&gt;
  return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called repeatedly if device disappeared&lt;br /&gt;
sub MY_MODULE_Ready($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
  &lt;br /&gt;
  # try to reopen the connection in case the connection is lost&lt;br /&gt;
  return DevIo_OpenDev($hash, 1, &amp;quot;MY_MODULE_Init&amp;quot;); &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when data was received&lt;br /&gt;
sub MY_MODULE_Read($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
  my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
  &lt;br /&gt;
  # read the available data&lt;br /&gt;
  my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
  &lt;br /&gt;
  # stop processing if no data is available (device disconnected)&lt;br /&gt;
  return if(!defined($buf));&lt;br /&gt;
  &lt;br /&gt;
  Log3 $name, 5, &amp;quot;MY_MODULE ($name) - received: $buf&amp;quot;; &lt;br /&gt;
  &lt;br /&gt;
  #&lt;br /&gt;
  # do something with $buf, e.g. generate readings, send answers via DevIo_SimpleWrite(), ...&lt;br /&gt;
  #&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called if set command is executed&lt;br /&gt;
sub MY_MODULE_Set($$@)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash, $name, $cmd) = @_;&lt;br /&gt;
    my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
    &lt;br /&gt;
    my $usage = &amp;quot;unknown argument $cmd, choose one of statusRequest:noArg on:noArg off:noArg&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    if($cmd eq &amp;quot;statusRequest&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;get_status\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    elsif($cmd eq &amp;quot;on&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;on\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    elsif($cmd eq &amp;quot;off&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;off\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
        return $usage;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
# will be executed upon successful connection establishment (see DevIo_OpenDev())&lt;br /&gt;
sub MY_MODULE_Init($)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash) = @_;&lt;br /&gt;
&lt;br /&gt;
    # send a status request to the device&lt;br /&gt;
    DevIo_SimpleWrite($hash, &amp;quot;get_status\r\n&amp;quot;, 2);&lt;br /&gt;
    &lt;br /&gt;
    return undef; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== TCP/IP-Verbindung == &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
package main;&lt;br /&gt;
&lt;br /&gt;
use strict;&lt;br /&gt;
use warnings;&lt;br /&gt;
use DevIo; # load DevIo.pm if not already loaded&lt;br /&gt;
&lt;br /&gt;
# called upon loading the module MY_MODULE&lt;br /&gt;
sub MY_MODULE_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
&lt;br /&gt;
  $hash-&amp;gt;{DefFn}    = &amp;quot;MY_MODULE_Define&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{UndefFn}  = &amp;quot;MY_MODULE_Undef&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{SetFn}    = &amp;quot;MY_MODULE_Set&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{ReadFn}   = &amp;quot;MY_MODULE_Read&amp;quot;;&lt;br /&gt;
  $hash-&amp;gt;{ReadyFn}  = &amp;quot;MY_MODULE_Ready&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when a new definition is created (by hand or from configuration read on FHEM startup)&lt;br /&gt;
sub MY_MODULE_Define($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash, $def) = @_;&lt;br /&gt;
  my @a = split(&amp;quot;[ \t]+&amp;quot;, $def);&lt;br /&gt;
&lt;br /&gt;
  my $name = $a[0];&lt;br /&gt;
  &lt;br /&gt;
  # $a[1] is always equals the module name &amp;quot;MY_MODULE&amp;quot;&lt;br /&gt;
  &lt;br /&gt;
  # first argument is the hostname or IP address of the device (e.g. &amp;quot;192.168.1.120&amp;quot;)&lt;br /&gt;
  my $dev = $a[2]; &lt;br /&gt;
&lt;br /&gt;
  return &amp;quot;no device given&amp;quot; unless($dev);&lt;br /&gt;
  &lt;br /&gt;
  # add a default port (1012), if not explicitly given by user&lt;br /&gt;
  $dev .= &#039;:1012&#039; if(not $dev =~ m/:\d+$/);&lt;br /&gt;
  &lt;br /&gt;
  $hash-&amp;gt;{DeviceName} = $dev;&lt;br /&gt;
  &lt;br /&gt;
  # close connection if maybe open (on definition modify)&lt;br /&gt;
  DevIo_CloseDev($hash) if(DevIo_IsOpen($hash));  &lt;br /&gt;
  &lt;br /&gt;
  # open connection with custom init and error callback function (non-blocking connection establishment)&lt;br /&gt;
  DevIo_OpenDev($hash, 0, &amp;quot;MY_MODULE_Init&amp;quot;, &amp;quot;MY_MODULE_Callback&amp;quot;); &lt;br /&gt;
 &lt;br /&gt;
  return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when definition is undefined &lt;br /&gt;
# (config reload, shutdown or delete of definition)&lt;br /&gt;
sub MY_MODULE_Undef($$)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash, $name) = @_;&lt;br /&gt;
 &lt;br /&gt;
  # close the connection &lt;br /&gt;
  DevIo_CloseDev($hash);&lt;br /&gt;
  &lt;br /&gt;
  return undef;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called repeatedly if device disappeared&lt;br /&gt;
sub MY_MODULE_Ready($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
  &lt;br /&gt;
  # try to reopen the connection in case the connection is lost&lt;br /&gt;
  return DevIo_OpenDev($hash, 1, &amp;quot;MY_MODULE_Init&amp;quot;, &amp;quot;MY_MODULE_Callback&amp;quot;); &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called when data was received&lt;br /&gt;
sub MY_MODULE_Read($)&lt;br /&gt;
{&lt;br /&gt;
  my ($hash) = @_;&lt;br /&gt;
  my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
  &lt;br /&gt;
  # read the available data&lt;br /&gt;
  my $buf = DevIo_SimpleRead($hash);&lt;br /&gt;
  &lt;br /&gt;
  # stop processing if no data is available (device disconnected)&lt;br /&gt;
  return if(!defined($buf));&lt;br /&gt;
  &lt;br /&gt;
  Log3 $name, 5, &amp;quot;MY_MODULE ($name) - received: $buf&amp;quot;; &lt;br /&gt;
  &lt;br /&gt;
  #&lt;br /&gt;
  # do something with $buf, e.g. generate readings, send answers via DevIo_SimpleWrite(), ...&lt;br /&gt;
  #&lt;br /&gt;
   &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# called if set command is executed&lt;br /&gt;
sub MY_MODULE_Set($$@)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash, $name, $cmd) = @_;&lt;br /&gt;
    my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
    &lt;br /&gt;
    my $usage = &amp;quot;unknown argument $cmd, choose one of statusRequest:noArg on:noArg off:noArg&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    if($cmd eq &amp;quot;statusRequest&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;get_status\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    elsif($cmd eq &amp;quot;on&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;on\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    elsif($cmd eq &amp;quot;off&amp;quot;)&lt;br /&gt;
    {&lt;br /&gt;
         DevIo_SimpleWrite($hash, &amp;quot;off\r\n&amp;quot;, 2);&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
        return $usage;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
# will be executed upon successful connection establishment (see DevIo_OpenDev())&lt;br /&gt;
sub MY_MODULE_Init($)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash) = @_;&lt;br /&gt;
&lt;br /&gt;
    # send a status request to the device&lt;br /&gt;
    DevIo_SimpleWrite($hash, &amp;quot;get_status\r\n&amp;quot;, 2);&lt;br /&gt;
    &lt;br /&gt;
    return undef; &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
# will be executed if connection establishment fails (see DevIo_OpenDev())&lt;br /&gt;
sub MY_MODULE_Callback($)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash, $error) = @_;&lt;br /&gt;
    my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
&lt;br /&gt;
    # create a log emtry with the error message&lt;br /&gt;
    Log3 $name, 5, &amp;quot;MY_MODULE ($name) - error while connecting: $error&amp;quot;; &lt;br /&gt;
    &lt;br /&gt;
    return undef; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Referenzen = &lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEM_auf_Mac_OS_X&amp;diff=26239</id>
		<title>FHEM auf Mac OS X</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEM_auf_Mac_OS_X&amp;diff=26239"/>
		<updated>2018-03-23T08:50:23Z</updated>

		<summary type="html">&lt;p&gt;Eisler: /* FHEM installieren */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== OS X vorbereiten ==&lt;br /&gt;
&lt;br /&gt;
=== Perl installieren ===&lt;br /&gt;
Anscheinend ist die [[Perl]]-Installation von OS X nicht komplett oder fehlerhaft wodurch nach der Installation zB die Icon-List nicht komplett geladen wurde. Deshalb wird Perl zur Sicherheit nochmals neu installiert. [http://learn.perl.org/installing/osx.html]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;curl -L http://xrl.us/installperlosx | bash&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Randnotiz|RNText=Optionale Pakete&lt;br /&gt;
* JSON&lt;br /&gt;
** [[Hue]]&lt;br /&gt;
** [[Volkszaehler]]&lt;br /&gt;
** [[Pushbullet]]&lt;br /&gt;
** &#039;&#039;withings&#039;&#039;&lt;br /&gt;
** [[Ubiquit mFi/mPower]]&lt;br /&gt;
** etc.&lt;br /&gt;
* Net::Telnet&lt;br /&gt;
**[[Ubiquit mFi/mPower]]&lt;br /&gt;
* Net::WebSocket::Server&lt;br /&gt;
** [[Fronthem]]&lt;br /&gt;
* XML::Simple&lt;br /&gt;
** [[Enigma2 Receiver (Dreambox, VUplus etc.) steuern|ENIGMA2 Modul]]&lt;br /&gt;
}}&lt;br /&gt;
=== Benötigte Pakete installieren ===&lt;br /&gt;
&lt;br /&gt;
In der Konsole folgende Befehle eingeben:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
sudo -i&lt;br /&gt;
cpan install Bundle::CPAN&lt;br /&gt;
cpan install Device::SerialPort &lt;br /&gt;
cpan install IO::Socket::SSL&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Optionale Pakete installieren ===&lt;br /&gt;
In der Konsole folgende Befehle eingeben:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
sudo -i&lt;br /&gt;
cpan install JSON&lt;br /&gt;
cpan install Net::Telnet&lt;br /&gt;
cpan install Net::WebSocket::Server&lt;br /&gt;
cpan install XML::Simple&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==FHEM installieren==&lt;br /&gt;
&lt;br /&gt;
=== FHEM herunterladen ===&lt;br /&gt;
Anschließend wird FHEM von der [http://fhem.de/fhem.html#Download Website] heruntergeladen und ins gewünschte Verzeichnis entpackt. &amp;lt;br /&amp;gt;&lt;br /&gt;
In dieser Anleitung wird FHEM nach &#039;&#039;/Users/Name/fhem-install&#039;&#039; entpackt&lt;br /&gt;
&lt;br /&gt;
=== Makefile bearbeiten ===&lt;br /&gt;
In der Datei &#039;&#039;/Users/Name/fhem-install/Makefile&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;BINDIR=/usr/local/bin&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Pfad&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;BINDIR=/Users/Name/fhem&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
entsprechend ändern.&lt;br /&gt;
&lt;br /&gt;
=== FHEM installieren ===&lt;br /&gt;
In der Konsole folgende Befehle eingeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
cd /Users/Name/fhem-install&lt;br /&gt;
sudo make install&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun sollte FHEM korrekt installiert und unter der URL http://IP-Adresse:8083 erreichbar sein.&lt;br /&gt;
&lt;br /&gt;
== Infos ==&lt;br /&gt;
&lt;br /&gt;
=== FHEM starten ===&lt;br /&gt;
In der Konsole folgenden Befehl eingeben:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
cd /Users/Name/fhem/ &amp;amp;&amp;amp; perl fhem.pl fhem.cfg&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FHEM stoppen ===&lt;br /&gt;
In der Konsole folgende Befehle eingeben:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
telnet IP-Adresse 7072&lt;br /&gt;
shutdown&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;br /&gt;
[[Kategorie:OSX]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEM_auf_Mac_OS_X&amp;diff=26238</id>
		<title>FHEM auf Mac OS X</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEM_auf_Mac_OS_X&amp;diff=26238"/>
		<updated>2018-03-23T08:50:02Z</updated>

		<summary type="html">&lt;p&gt;Eisler: /* Makefile bearbeiten */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== OS X vorbereiten ==&lt;br /&gt;
&lt;br /&gt;
=== Perl installieren ===&lt;br /&gt;
Anscheinend ist die [[Perl]]-Installation von OS X nicht komplett oder fehlerhaft wodurch nach der Installation zB die Icon-List nicht komplett geladen wurde. Deshalb wird Perl zur Sicherheit nochmals neu installiert. [http://learn.perl.org/installing/osx.html]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;curl -L http://xrl.us/installperlosx | bash&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Randnotiz|RNText=Optionale Pakete&lt;br /&gt;
* JSON&lt;br /&gt;
** [[Hue]]&lt;br /&gt;
** [[Volkszaehler]]&lt;br /&gt;
** [[Pushbullet]]&lt;br /&gt;
** &#039;&#039;withings&#039;&#039;&lt;br /&gt;
** [[Ubiquit mFi/mPower]]&lt;br /&gt;
** etc.&lt;br /&gt;
* Net::Telnet&lt;br /&gt;
**[[Ubiquit mFi/mPower]]&lt;br /&gt;
* Net::WebSocket::Server&lt;br /&gt;
** [[Fronthem]]&lt;br /&gt;
* XML::Simple&lt;br /&gt;
** [[Enigma2 Receiver (Dreambox, VUplus etc.) steuern|ENIGMA2 Modul]]&lt;br /&gt;
}}&lt;br /&gt;
=== Benötigte Pakete installieren ===&lt;br /&gt;
&lt;br /&gt;
In der Konsole folgende Befehle eingeben:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
sudo -i&lt;br /&gt;
cpan install Bundle::CPAN&lt;br /&gt;
cpan install Device::SerialPort &lt;br /&gt;
cpan install IO::Socket::SSL&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Optionale Pakete installieren ===&lt;br /&gt;
In der Konsole folgende Befehle eingeben:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
sudo -i&lt;br /&gt;
cpan install JSON&lt;br /&gt;
cpan install Net::Telnet&lt;br /&gt;
cpan install Net::WebSocket::Server&lt;br /&gt;
cpan install XML::Simple&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==FHEM installieren==&lt;br /&gt;
&lt;br /&gt;
=== FHEM herunterladen ===&lt;br /&gt;
Anschließend wird FHEM von der [http://fhem.de/fhem.html#Download Website] heruntergeladen und ins gewünschte Verzeichnis entpackt. &amp;lt;br /&amp;gt;&lt;br /&gt;
In dieser Anleitung wird FHEM nach &#039;&#039;/Users/Name/fhem-install&#039;&#039; entpackt&lt;br /&gt;
&lt;br /&gt;
=== Makefile bearbeiten ===&lt;br /&gt;
In der Datei &#039;&#039;/Users/Name/fhem-install/Makefile&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;BINDIR=/usr/local/bin&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Pfad&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;BINDIR=/Users/Name/fhem&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
entsprechend ändern.&lt;br /&gt;
&lt;br /&gt;
=== FHEM installieren ===&lt;br /&gt;
In der Konsole folgende Befehle eingeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
cd /Users/Name/fhem&lt;br /&gt;
sudo make install&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun sollte FHEM korrekt installiert und unter der URL http://IP-Adresse:8083 erreichbar sein.&lt;br /&gt;
&lt;br /&gt;
== Infos ==&lt;br /&gt;
&lt;br /&gt;
=== FHEM starten ===&lt;br /&gt;
In der Konsole folgenden Befehl eingeben:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
cd /Users/Name/fhem/ &amp;amp;&amp;amp; perl fhem.pl fhem.cfg&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FHEM stoppen ===&lt;br /&gt;
In der Konsole folgende Befehle eingeben:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
telnet IP-Adresse 7072&lt;br /&gt;
shutdown&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;br /&gt;
[[Kategorie:OSX]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=FHEM_auf_Mac_OS_X&amp;diff=26237</id>
		<title>FHEM auf Mac OS X</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=FHEM_auf_Mac_OS_X&amp;diff=26237"/>
		<updated>2018-03-23T08:49:31Z</updated>

		<summary type="html">&lt;p&gt;Eisler: /* FHEM herunterladen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== OS X vorbereiten ==&lt;br /&gt;
&lt;br /&gt;
=== Perl installieren ===&lt;br /&gt;
Anscheinend ist die [[Perl]]-Installation von OS X nicht komplett oder fehlerhaft wodurch nach der Installation zB die Icon-List nicht komplett geladen wurde. Deshalb wird Perl zur Sicherheit nochmals neu installiert. [http://learn.perl.org/installing/osx.html]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;curl -L http://xrl.us/installperlosx | bash&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Randnotiz|RNText=Optionale Pakete&lt;br /&gt;
* JSON&lt;br /&gt;
** [[Hue]]&lt;br /&gt;
** [[Volkszaehler]]&lt;br /&gt;
** [[Pushbullet]]&lt;br /&gt;
** &#039;&#039;withings&#039;&#039;&lt;br /&gt;
** [[Ubiquit mFi/mPower]]&lt;br /&gt;
** etc.&lt;br /&gt;
* Net::Telnet&lt;br /&gt;
**[[Ubiquit mFi/mPower]]&lt;br /&gt;
* Net::WebSocket::Server&lt;br /&gt;
** [[Fronthem]]&lt;br /&gt;
* XML::Simple&lt;br /&gt;
** [[Enigma2 Receiver (Dreambox, VUplus etc.) steuern|ENIGMA2 Modul]]&lt;br /&gt;
}}&lt;br /&gt;
=== Benötigte Pakete installieren ===&lt;br /&gt;
&lt;br /&gt;
In der Konsole folgende Befehle eingeben:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
sudo -i&lt;br /&gt;
cpan install Bundle::CPAN&lt;br /&gt;
cpan install Device::SerialPort &lt;br /&gt;
cpan install IO::Socket::SSL&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Optionale Pakete installieren ===&lt;br /&gt;
In der Konsole folgende Befehle eingeben:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
sudo -i&lt;br /&gt;
cpan install JSON&lt;br /&gt;
cpan install Net::Telnet&lt;br /&gt;
cpan install Net::WebSocket::Server&lt;br /&gt;
cpan install XML::Simple&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==FHEM installieren==&lt;br /&gt;
&lt;br /&gt;
=== FHEM herunterladen ===&lt;br /&gt;
Anschließend wird FHEM von der [http://fhem.de/fhem.html#Download Website] heruntergeladen und ins gewünschte Verzeichnis entpackt. &amp;lt;br /&amp;gt;&lt;br /&gt;
In dieser Anleitung wird FHEM nach &#039;&#039;/Users/Name/fhem-install&#039;&#039; entpackt&lt;br /&gt;
&lt;br /&gt;
=== Makefile bearbeiten ===&lt;br /&gt;
In der Datei &#039;&#039;/Users/Name/fhem/Makefile&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;BINDIR=/usr/local/bin&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
den Pfad&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;BINDIR=/Users/Name/fhem&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
entsprechend ändern.&lt;br /&gt;
&lt;br /&gt;
=== FHEM installieren ===&lt;br /&gt;
In der Konsole folgende Befehle eingeben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
cd /Users/Name/fhem&lt;br /&gt;
sudo make install&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun sollte FHEM korrekt installiert und unter der URL http://IP-Adresse:8083 erreichbar sein.&lt;br /&gt;
&lt;br /&gt;
== Infos ==&lt;br /&gt;
&lt;br /&gt;
=== FHEM starten ===&lt;br /&gt;
In der Konsole folgenden Befehl eingeben:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
cd /Users/Name/fhem/ &amp;amp;&amp;amp; perl fhem.pl fhem.cfg&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FHEM stoppen ===&lt;br /&gt;
In der Konsole folgende Befehle eingeben:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
telnet IP-Adresse 7072&lt;br /&gt;
shutdown&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;br /&gt;
[[Kategorie:OSX]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=22768</id>
		<title>Proteus EcoMeter</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=22768"/>
		<updated>2017-09-28T20:46:31Z</updated>

		<summary type="html">&lt;p&gt;Eisler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Proteus_EcoMeter]] ist ein Ultraschallsensor mit Monitor zur Pegelmessung von Heizöl- oder Wassertanks&lt;br /&gt;
&lt;br /&gt;
* Sensor zum Einbau in den Tank&lt;br /&gt;
* Funkverbindung zwischen Sensor und Monitor&lt;br /&gt;
* USB-Verbindung zwischen Monitor und FHEM&lt;br /&gt;
* Für Wasser oder Öltanks&lt;br /&gt;
&lt;br /&gt;
EcoMeter für Heizöltanks&amp;lt;br&amp;gt;&lt;br /&gt;
EcoMeter S für Wassertanks&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Infobox Hardware&lt;br /&gt;
|Bild=EcoMeter.png&lt;br /&gt;
|Bildbeschreibung=EcoMeter Ultraschall Füllstandsanzeige für Heizöltank&lt;br /&gt;
|HWProtocol=unknown&lt;br /&gt;
|HWType=Sensor und Sender&lt;br /&gt;
|HWCategory=Other Components&lt;br /&gt;
|HWComm=Funk, 433,92MHz, USB&lt;br /&gt;
|HWChannels=unknown&lt;br /&gt;
|HWVoltage=1,5V LR2450, 5V USB&lt;br /&gt;
|HWPowerConsumption=unknown&lt;br /&gt;
|HWPoweredBy=Battery, USB&lt;br /&gt;
|HWSize=21 x 18 x 6 cm&lt;br /&gt;
|HWDeviceFHEM=[http://fhem.de/commandref.html#TEK603 TEK603]&lt;br /&gt;
|HWManufacturer=Tekelek&lt;br /&gt;
}}&lt;br /&gt;
== Features ==&lt;br /&gt;
EcoMeter für Heizöl liefert alle 60 Minuten einen Messwert&lt;br /&gt;
&lt;br /&gt;
EcoMeter S für Zisterne, Wassertank liefert alle 30 Minuten einen Messwert &lt;br /&gt;
&lt;br /&gt;
Wenn der Sensor eine Füllstandänderung von 3cm pro Minute erkennt, geht der Sensor in den FastModus, d.h. es werden alle 2 - 3 Sekunden neue Werte an den Ecomonitor kommuniziert. Dies ist hilfreich bei einem Leck oder beim Nachtanken. Der FastModus, vergleichbar mit dem Modus nach dem Koppeln, dauert dann ca. 10 Minuten an, so dass die Batterie nicht leer gesaugt wird.&lt;br /&gt;
&lt;br /&gt;
== Hinweise zum Betrieb mit FHEM ==&lt;br /&gt;
=== Definition/Anlernvorgang ===&lt;br /&gt;
# eventuell im Linux fehlende Pakete installieren &amp;lt;code&amp;gt;sudo apt-get install libdigest-crc-perl &amp;lt;/code&amp;gt;&lt;br /&gt;
# Monitor an das FHEM Gerät anschließen&lt;br /&gt;
# Festellen, dass die Verbindung geht. Dazu auf der Kommandozeile eingeben: &amp;lt;code&amp;gt;ls -al /dev/serial/by-id &amp;lt;br&amp;gt; lrwxrwxrwx 1 root root 13 Jan  1  1970 usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 -&amp;gt; ../../ttyUSB0 &amp;lt;/code&amp;gt; &lt;br /&gt;
# ecometer definieren &amp;lt;code&amp;gt;define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 &amp;lt;/code&amp;gt;&lt;br /&gt;
# LogFile definieren &amp;lt;code&amp;gt;define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.* &amp;lt;/code&amp;gt;&lt;br /&gt;
# Gerät wie in der Betriebsanleitung installieren&lt;br /&gt;
&lt;br /&gt;
=== Identifikation ===&lt;br /&gt;
Das Proteus EcoMeter verwendet für die Anbindung der seriellen Kommunikation an die USB-Schnittstelle einen sehr weit verbreiteten Chip der Familie &amp;lt;code&amp;gt;CP210x UART Bridges&amp;lt;/code&amp;gt;. Dieser Baustein hat die USB &amp;lt;code&amp;gt;ID 10c4:ea60&amp;lt;/code&amp;gt; über die auch der Name in &amp;lt;code&amp;gt;/dev/serial/by-id&amp;lt;/code&amp;gt; abgeleitet wird. Hat man mehrere Geräte die mit diesem Chip arbeiten (z.B. den optischen Adapter von Viessmann für [[Vitotronic_200_(Viessmann_Heizungssteuerung)|VCONTROL]]) am gleichen Rechner, so werden diese unter &amp;lt;code&amp;gt;lsusb&amp;lt;/code&amp;gt; mit identischer ID gelistet, aber nur einer erhält den passenden Namen unter &amp;lt;code&amp;gt;/dev/serial/by-id&amp;lt;/code&amp;gt;. Sie sind also auf diese Weise nicht unterscheidbar. Die kurzen Gerätenamen &amp;lt;code&amp;gt;/dev/ttyUSBx&amp;lt;/code&amp;gt; werden willkürlich zugeordnet. Damit sind die Geräte auch nicht unterscheidbar.&lt;br /&gt;
&lt;br /&gt;
Bleibt noch der Pfad wenn man die Geräte immer in die gleiche Buchse steckt. Diese Pfade werden unter Linux in &amp;lt;code&amp;gt;/dev/serial/by-path&amp;lt;/code&amp;gt; angelegt. Hier kann man einzelne Buchsen (auch von Hubs) identifizieren. Die Namen enthalten jedoch Doppelpunkte und FHEM verkraftet keine Gerätenamen mit Doppelpunkten. &amp;lt;small&amp;gt;&#039;&#039;Wer weiß warum bitte hier Lösung eintragen.&#039;&#039;&amp;lt;/small&amp;gt; Um diese ohnehin unhandlichen Namen kürzer und verträglicher zu bekommen, kann man Symbolische Links anlegen. Dazu erzeugt man am besten in &amp;lt;code&amp;gt;/opt/fhem&amp;lt;/code&amp;gt; ein Verzeichnis &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; und verlinkt alle für FHEM relevanten Geräte dort hin. Also in meinem Beispiel TEK603 und VCONTROL:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=bash&amp;gt;&lt;br /&gt;
cd /opt/fhem&lt;br /&gt;
mkdir dev&lt;br /&gt;
cd dev&lt;br /&gt;
ln -s /dev/serial/by-path/pci-0000:00:14.0-usb-0:4.3:1.0-port0 heizung&lt;br /&gt;
ln -s /dev/serial/by-path/pci-0000:00:14.0-usb-0:4.2:1.0-port0 oelpegel&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Solange man die beiden Geräte immer in die gleiche Buchse steckt, werden sie ab jetzt immer korrekt zugeordnet:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=text&amp;gt;&lt;br /&gt;
defmod Oelpegel TEK603 /opt/fhem/dev/oelpegel&lt;br /&gt;
defmod Heizung VCONTROL /opt/fhem/dev/heizung /opt/fhem/mycfg/vitoladens_300-C_J3Ra-24.cfg 120&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FHEM Config-Auszug ===&lt;br /&gt;
Ein exemplarischer Auszug aus der [[Konfiguration]]:&lt;br /&gt;
 define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0&lt;br /&gt;
 define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.*&lt;br /&gt;
&lt;br /&gt;
=== Eventmonitorbeispiel ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=text&amp;gt;&lt;br /&gt;
2017-03-03_13:07:11 Oelpegel CONNECTED&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Time: 13:05:21&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Temperature: 18.33&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Ullage: 100&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel RemainingUsableLevel: 3227&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel RemainingUsablePercent: 31.0&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel TotalUsableCapacity: 10414&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
In den ersten 10 Minuten nach dem Anlernen, befindet sich das Gerät im Echtzeit-Modus. Das bedeutet es werden mehrere Messungen pro Sekunde übertragen. Das würde die Batterie sehr schnell lehren. Daher schaltet das Gerät nach 10 Minuten in den Normalbetrieb, in dem nur noch ganz grob alle 30 Minuten Werte übertragen werden. Die Übertragung zu FHEM findet auch nur statt, wenn über die Funkstrecke Daten vom Sensor kommen. Das führt zu der Situation, dass nach diesen 10 Minuten ein neu eingerichtetes &amp;lt;code&amp;gt;TEK603&amp;lt;/code&amp;gt; Modul sehr lange (Stunden ?) keine Daten anzeigt, obwohl das Display der Proteus Daten anzeigt. Das liegt einfach daran, dass dieses Display den zuletzt gespeicherten Messwert anzeigt egal wie lange der her ist. Übertragen wird bis zum nächsten Wert auf der Funkstrecke nichts.&lt;br /&gt;
&lt;br /&gt;
== Einsatzbeispiel ==&lt;br /&gt;
=== Anzeige des Ölstands in einem Öltank ===&lt;br /&gt;
{{todo|Beispielunterlagen erstellen}}&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Shop: [http://hausautomatisierung.co/haustechnik/ecometer-ultraschall-fuellstandsanzeige-fuer-heizoeltank/ EcoMeter Ultraschall Füllstandsanzeige für Heizöltank]&lt;br /&gt;
* Shop: [http://hausautomatisierung.co/haustechnik/ecometer-s-ultraschall-fuellstandsanzeige-fuer-zisterne-wassertank/ EcoMeter S Ultraschall Füllstandsanzeige für Zisterne, Wassertank]&lt;br /&gt;
* Hersteller: [http://tekelek.ie/?ddownload=1408 Bedienanleitung]&lt;br /&gt;
* Forenbeitrag zur FHEM-Einbindung: {{Link2Forum|Topic=27315}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Other Components]]&lt;br /&gt;
[[Kategorie:Füllstandsmesser]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Knxd&amp;diff=22359</id>
		<title>Knxd</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Knxd&amp;diff=22359"/>
		<updated>2017-08-25T10:49:08Z</updated>

		<summary type="html">&lt;p&gt;Eisler: cmake zu Jessie hinzugefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= knxd mit einem IP Gateway einrichten =&lt;br /&gt;
Damit FHEM auf den KNX Bus zugreifen kann, benötigt man ein passendes Interface&lt;br /&gt;
&lt;br /&gt;
Es gibt:&lt;br /&gt;
&lt;br /&gt;
* RS232&lt;br /&gt;
* USB&lt;br /&gt;
* IP&lt;br /&gt;
&lt;br /&gt;
Ich beschreibe die Einrichtung von knxd mit einem IP Gateway auf einen Raspberry Pi2 mit Wheezy oder Jessie.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1. als erstes müssen folgende Pakete installiert werden (Referenz Debian Jessie):&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt-get update&lt;br /&gt;
sudo apt-get install debhelper cdbs automake libtool libusb-1.0-0-dev git-core build-essential libsystemd-daemon-dev dh-systemd libev-dev cmake&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.1 als erstes müssen folgende Pakete installiert werden (Referenz Debian Stretch):&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;sudo apt-get update&lt;br /&gt;
sudo apt-get install debhelper cdbs automake libtool libusb-1.0-0-dev git-core build-essential libsystemd-dev dh-systemd libev-dev cmake&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2. knxd herunterladen und installieren&#039;&#039;&#039;&lt;br /&gt;
Achtung: Wenn Abhängigkeiten fehlen, dann installiere diese nach. Nicht einfach mittels &amp;quot;-d&amp;quot; diese übergehen!&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git clone https://github.com/knxd/knxd.git&lt;br /&gt;
cd knxd&lt;br /&gt;
git checkout stable&lt;br /&gt;
dpkg-buildpackage -b -uc&lt;br /&gt;
cd ..&lt;br /&gt;
sudo dpkg -i knxd_*.deb knxd-tools_*.deb&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Konfiguration ==&lt;br /&gt;
&#039;&#039;&#039;1. Ohne systemd&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
es muss als nächstes die Konfigurationsdatei editiert werden.&lt;br /&gt;
&lt;br /&gt;
das geht mit:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo nano /etc/default/knxd &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann folgende Einträge anpassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
DAEMON_ARGS=&amp;quot;-u /tmp/eib -u /var/run/knx -i -b ipt:192.168.188.XX&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
und&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
START_KNXD=YES&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;2. Mit systemd z. B. für Debian Jessie&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Die Konfigurationsdatei bei Jessie hat sich wegen der Nutzung von systemd geändert:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo nano /etc/knxd.conf &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
dann folgende Einträge anpassen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
KNXD_OPTS=&amp;quot;-u /tmp/eib -u /var/run/knx -b ipt:192.168.188.XX&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
und&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
START_KNXD=YES&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
== knxd Status überprüfen ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
/etc/init.d/knxd status&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== FAQ ==&lt;br /&gt;
&#039;&#039;&#039;Wie wird eibd vorher deinstalliert?&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo rm -r /usr/local/bin/{eibd,knxtool,group*} /usr/local/lib/lib{eib,pthsem}*.so* /usr/local/include/pth*&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;folgender Fehler: dpkg-buildpackage: Fehler: Fehler-Exitstatus von debian/rules build war 2&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt-get install git-core build-essential&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Fehler: dpkg-buildpackage: error: fakeroot not found, either install the fakeroot &amp;lt;....&amp;gt; &#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt-get install fakeroot dpkg-dev&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
[[Benutzer:Marthinx]]&lt;br /&gt;
&lt;br /&gt;
[https://github.com/knxd/knxd Github knxd]&lt;br /&gt;
&lt;br /&gt;
[https://knx-user-forum.de/forum/projektforen/knxd/1049547-grundlagen-zum-knxd-mit-installationsanleitung-vor-dem-schreiben-lesen Forums-Thread zu knxd. Sehr informativ.]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Examples]]&lt;br /&gt;
[[Kategorie:EIB/KNX]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=21410</id>
		<title>Proteus EcoMeter</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=21410"/>
		<updated>2017-04-29T13:26:47Z</updated>

		<summary type="html">&lt;p&gt;Eisler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Proteus_EcoMeter]] ist ein Ultraschallsensor mit Monitor zur Pegelmessung von Heizöl- oder Wassertanks&lt;br /&gt;
&lt;br /&gt;
* Sensor zum Einbau in den Tank&lt;br /&gt;
* Funkverbindung zwischen Sensor und Monitor&lt;br /&gt;
* USB-Verbindung zwischen Monitor und FHEM&lt;br /&gt;
* Für Wasser oder Öltanks&lt;br /&gt;
&lt;br /&gt;
EcoMeter für Heizöltanks&amp;lt;br&amp;gt;&lt;br /&gt;
EcoMeter S für Wassertanks&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Infobox Hardware&lt;br /&gt;
|Bild=EcoMeter.png&lt;br /&gt;
|Bildbeschreibung=EcoMeter Ultraschall Füllstandsanzeige für Heizöltank&lt;br /&gt;
|HWProtocol=unknown&lt;br /&gt;
|HWType=Sensor und Sender&lt;br /&gt;
|HWCategory=Other Components&lt;br /&gt;
|HWComm=Funk, 433,92MHz, USB&lt;br /&gt;
|HWChannels=unknown&lt;br /&gt;
|HWVoltage=1,5V LR2450, 5V USB&lt;br /&gt;
|HWPowerConsumption=unknown&lt;br /&gt;
|HWPoweredBy=Battery, USB&lt;br /&gt;
|HWSize=21 x 18 x 6 cm&lt;br /&gt;
|HWDeviceFHEM=[http://fhem.de/commandref.html#TEK603 TEK603]&lt;br /&gt;
|HWManufacturer=Tekelek&lt;br /&gt;
}}&lt;br /&gt;
== Features ==&lt;br /&gt;
EcoMeter für Heizöl liefert alle 60 Minuten einen Messwert&lt;br /&gt;
&lt;br /&gt;
EcoMeter S für Zisterne, Wassertank liefert alle 30 Minuten einen Messwert &lt;br /&gt;
&lt;br /&gt;
Wenn der Sensor eine Füllstandänderung von 3cm pro Minute erkennt, geht der Sensor in den FastModus, d.h. es werden alle 2 - 3 Sekunden neue Werte an den Ecomonitor kommuniziert. Dies ist hilfreich bei einem Leck oder beim Nachtanken. Der FastModus, vergleichbar mit dem Modus nach dem Koppeln, dauert dann ca. 10 Minuten an, so dass die Batterie nicht leer gesaugt wird.&lt;br /&gt;
&lt;br /&gt;
== Hinweise zum Betrieb mit FHEM ==&lt;br /&gt;
=== Definition/Anlernvorgang ===&lt;br /&gt;
# eventuell im Linux fehlende Pakete installieren &amp;lt;code&amp;gt;sudo apt-get install libdigest-crc-perl &amp;lt;/code&amp;gt;&lt;br /&gt;
# Monitor an das FHEM Gerät anschließen&lt;br /&gt;
# Festellen, dass die Verbindung geht. Dazu auf der Kommandozeile eingeben: &amp;lt;code&amp;gt;ls -al /dev/serial/by-id &amp;lt;br&amp;gt; lrwxrwxrwx 1 root root 13 Jan  1  1970 usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 -&amp;gt; ../../ttyUSB0 &amp;lt;/code&amp;gt; &lt;br /&gt;
# ecometer definieren &amp;lt;code&amp;gt;define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 &amp;lt;/code&amp;gt;&lt;br /&gt;
# LogFile definieren &amp;lt;code&amp;gt;define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.* &amp;lt;/code&amp;gt;&lt;br /&gt;
# Gerät wie in der Betriebsanleitung installieren&lt;br /&gt;
&lt;br /&gt;
=== Identifikation ===&lt;br /&gt;
Das Proteus EcoMeter verwendet für die Anbindung der seriellen Kommunikation an die USB-Schnittstelle einen sehr weit verbreiteten Chip der Familie &amp;lt;code&amp;gt;CP210x UART Bridges&amp;lt;/code&amp;gt;. Dieser Baustein hat die USB &amp;lt;code&amp;gt;ID 10c4:ea60&amp;lt;/code&amp;gt; über die auch der Name in &amp;lt;code&amp;gt;/dev/serial/by-id&amp;lt;/code&amp;gt; abgeleitet wird. Hat man mehrere Geräte die mit diesem Chip arbeiten (z.B. den optischen Adapter von Viessmann für [[Vitotronic_200_(Viessmann_Heizungssteuerung)|VCONTROL]]) am gleichen Rechner, so werden diese unter &amp;lt;code&amp;gt;lsusb&amp;lt;/code&amp;gt; mit identischer ID gelistet, aber nur einer erhält den passenden Namen unter &amp;lt;code&amp;gt;/dev/serial/by-id&amp;lt;/code&amp;gt;. Sie sind also auf diese Weise nicht unterscheidbar. Die kurzen Gerätenamen &amp;lt;code&amp;gt;/dev/ttyUSBx&amp;lt;/code&amp;gt; werden willkürlich zugeordnet. Damit sind die Geräte auch nicht unterscheidbar.&lt;br /&gt;
&lt;br /&gt;
Bleibt noch der Pfad wenn man die Geräte immer in die gleiche Buchse steckt. Diese Pfade werden unter Linux in &amp;lt;code&amp;gt;/dev/serial/by-path&amp;lt;/code&amp;gt; angelegt. Hier kann man einzelne Buchsen (auch von Hubs) identifizieren. Die Namen enthalten jedoch Doppelpunkte und FHEM verkraftet keine Gerätenamen mit Doppelpunkten. &amp;lt;small&amp;gt;&#039;&#039;Wer weiß warum bitte hier Lösung eintragen.&#039;&#039;&amp;lt;/small&amp;gt; Um diese ohnehin unhandlichen Namen kürzer und verträglicher zu bekommen, kann man Symbolische Links anlegen. Dazu erzeugt man am besten in &amp;lt;code&amp;gt;/opt/fhem&amp;lt;/code&amp;gt; ein Verzeichnis &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; und verlinkt alle für FHEM relevanten Geräte dort hin. Also in meinem Beispiel TEK603 und VCONTROL:&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
cd /opt/fhem&lt;br /&gt;
mkdir dev&lt;br /&gt;
cd dev&lt;br /&gt;
ln -s /dev/serial/by-path/pci-0000:00:14.0-usb-0:4.3:1.0-port0 heizung&lt;br /&gt;
ln -s /dev/serial/by-path/pci-0000:00:14.0-usb-0:4.2:1.0-port0 oelpegel&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Solange man die beiden Geräte immer in die gleiche Buchse steckt, werden sie ab jetzt immer korrekt zugeordnet:&lt;br /&gt;
&amp;lt;source lang=text&amp;gt;&lt;br /&gt;
defmod Oelpegel TEK603 /opt/fhem/dev/oelpegel&lt;br /&gt;
defmod Heizung VCONTROL /opt/fhem/dev/heizung /opt/fhem/mycfg/vitoladens_300-C_J3Ra-24.cfg 120&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FHEM Config-Auszug ===&lt;br /&gt;
Ein exemplarischer Auszug aus der [[Konfiguration]]:&lt;br /&gt;
 define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0&lt;br /&gt;
 define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.*&lt;br /&gt;
&lt;br /&gt;
=== Eventmonitorbeispiel ===&lt;br /&gt;
&amp;lt;source lang=text&amp;gt;&lt;br /&gt;
2017-03-03_13:07:11 Oelpegel CONNECTED&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Time: 13:05:21&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Temperature: 18.33&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Ullage: 100&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel RemainingUsableLevel: 3227&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel RemainingUsablePercent: 31.0&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel TotalUsableCapacity: 10414&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
In den ersten 10 Minuten nach dem Anlernen, befindet sich das Gerät im Echtzeit-Modus. Das bedeutet es werden mehrere Messungen pro Sekunde übertragen. Das würde die Batterie sehr schnell lehren. Daher schaltet das Gerät nach 10 Minuten in den Normalbetrieb, in dem nur noch ganz grob alle 30 Minuten Werte übertragen werden. Die Übertragung zu FHEM findet auch nur statt, wenn über die Funkstrecke Daten vom Sensor kommen. Das führt zu der Situation, dass nach diesen 10 Minuten ein neu eingerichtetes &amp;lt;code&amp;gt;TEK603&amp;lt;/code&amp;gt; Modul sehr lange (Stunden ?) keine Daten anzeigt, obwohl das Display der Proteus Daten anzeigt. Das liegt einfach daran, dass dieses Display den zuletzt gespeicherten Messwert anzeigt egal wie lange der her ist. Übertragen wird bis zum nächsten Wert auf der Funkstrecke nichts.&lt;br /&gt;
&lt;br /&gt;
== Einsatzbeispiel ==&lt;br /&gt;
=== Anzeige des Ölstands in einem Öltank ===&lt;br /&gt;
{{todo|Beispielunterlagen erstellen}}&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Shop: [http://hausautomatisierung.co/haustechnik/ecometer-ultraschall-fuellstandsanzeige-fuer-heizoeltank/ EcoMeter Ultraschall Füllstandsanzeige für Heizöltank]&lt;br /&gt;
* Shop: [http://hausautomatisierung.co/haustechnik/ecometer-s-ultraschall-fuellstandsanzeige-fuer-zisterne-wassertank/ EcoMeter S Ultraschall Füllstandsanzeige für Zisterne, Wassertank]&lt;br /&gt;
* Hersteller: [https://proteus-meter.com/faq/ Bedienanleitung]&lt;br /&gt;
* Forenbeitrag zur FHEM-Einbindung: {{Link2Forum|Topic=27315}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Other Components]]&lt;br /&gt;
[[Kategorie:Füllstandsmesser]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=21409</id>
		<title>Proteus EcoMeter</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=21409"/>
		<updated>2017-04-29T12:52:49Z</updated>

		<summary type="html">&lt;p&gt;Eisler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Proteus_EcoMeter]] ist ein Ultraschallsensor mit Monitor zur Pegelmessung von Heizöl- oder Wassertanks&lt;br /&gt;
&lt;br /&gt;
* Sensor zum Einbau in den Tank&lt;br /&gt;
* Funkverbindung zwischen Sensor und Monitor&lt;br /&gt;
* USB-Verbindung zwischen Monitor und FHEM&lt;br /&gt;
* Für Wasser oder Öltanks&lt;br /&gt;
&lt;br /&gt;
EcoMeter für Heizöltanks&amp;lt;br&amp;gt;&lt;br /&gt;
EcoMeter S für Wassertanks&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Infobox Hardware&lt;br /&gt;
|Bild=EcoMeter.png&lt;br /&gt;
|Bildbeschreibung=EcoMeter Ultraschall Füllstandsanzeige für Heizöltank&lt;br /&gt;
|HWProtocol=unknown&lt;br /&gt;
|HWType=Sensor und Sender&lt;br /&gt;
|HWCategory=Other Components&lt;br /&gt;
|HWComm=Funk, 433,92MHz, USB&lt;br /&gt;
|HWChannels=unknown&lt;br /&gt;
|HWVoltage=1,5V LR2450, 5V USB&lt;br /&gt;
|HWPowerConsumption=unknown&lt;br /&gt;
|HWPoweredBy=Battery, USB&lt;br /&gt;
|HWSize=21 x 18 x 6 cm&lt;br /&gt;
|HWDeviceFHEM=[http://fhem.de/commandref.html#TEK603 TEK603]&lt;br /&gt;
|HWManufacturer=Tekelek&lt;br /&gt;
}}&lt;br /&gt;
== Features ==&lt;br /&gt;
EcoMeter liefert alle 60 Minuten einen Messwert&lt;br /&gt;
EcoMeter liefert alle 30 Minuten einen Messwert und bei schnellen Änderungen&lt;br /&gt;
&lt;br /&gt;
== Hinweise zum Betrieb mit FHEM ==&lt;br /&gt;
=== Definition/Anlernvorgang ===&lt;br /&gt;
# eventuell im Linux fehlende Pakete installieren &amp;lt;code&amp;gt;sudo apt-get install libdigest-crc-perl &amp;lt;/code&amp;gt;&lt;br /&gt;
# Monitor an das FHEM Gerät anschließen&lt;br /&gt;
# Festellen, dass die Verbindung geht. Dazu auf der Kommandozeile eingeben: &amp;lt;code&amp;gt;ls -al /dev/serial/by-id &amp;lt;br&amp;gt; lrwxrwxrwx 1 root root 13 Jan  1  1970 usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 -&amp;gt; ../../ttyUSB0 &amp;lt;/code&amp;gt; &lt;br /&gt;
# ecometer definieren &amp;lt;code&amp;gt;define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 &amp;lt;/code&amp;gt;&lt;br /&gt;
# LogFile definieren &amp;lt;code&amp;gt;define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.* &amp;lt;/code&amp;gt;&lt;br /&gt;
# Gerät wie in der Betriebsanleitung installieren&lt;br /&gt;
&lt;br /&gt;
=== Identifikation ===&lt;br /&gt;
Das Proteus EcoMeter verwendet für die Anbindung der seriellen Kommunikation an die USB-Schnittstelle einen sehr weit verbreiteten Chip der Familie &amp;lt;code&amp;gt;CP210x UART Bridges&amp;lt;/code&amp;gt;. Dieser Baustein hat die USB &amp;lt;code&amp;gt;ID 10c4:ea60&amp;lt;/code&amp;gt; über die auch der Name in &amp;lt;code&amp;gt;/dev/serial/by-id&amp;lt;/code&amp;gt; abgeleitet wird. Hat man mehrere Geräte die mit diesem Chip arbeiten (z.B. den optischen Adapter von Viessmann für [[Vitotronic_200_(Viessmann_Heizungssteuerung)|VCONTROL]]) am gleichen Rechner, so werden diese unter &amp;lt;code&amp;gt;lsusb&amp;lt;/code&amp;gt; mit identischer ID gelistet, aber nur einer erhält den passenden Namen unter &amp;lt;code&amp;gt;/dev/serial/by-id&amp;lt;/code&amp;gt;. Sie sind also auf diese Weise nicht unterscheidbar. Die kurzen Gerätenamen &amp;lt;code&amp;gt;/dev/ttyUSBx&amp;lt;/code&amp;gt; werden willkürlich zugeordnet. Damit sind die Geräte auch nicht unterscheidbar.&lt;br /&gt;
&lt;br /&gt;
Bleibt noch der Pfad wenn man die Geräte immer in die gleiche Buchse steckt. Diese Pfade werden unter Linux in &amp;lt;code&amp;gt;/dev/serial/by-path&amp;lt;/code&amp;gt; angelegt. Hier kann man einzelne Buchsen (auch von Hubs) identifizieren. Die Namen enthalten jedoch Doppelpunkte und FHEM verkraftet keine Gerätenamen mit Doppelpunkten. &amp;lt;small&amp;gt;&#039;&#039;Wer weiß warum bitte hier Lösung eintragen.&#039;&#039;&amp;lt;/small&amp;gt; Um diese ohnehin unhandlichen Namen kürzer und verträglicher zu bekommen, kann man Symbolische Links anlegen. Dazu erzeugt man am besten in &amp;lt;code&amp;gt;/opt/fhem&amp;lt;/code&amp;gt; ein Verzeichnis &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; und verlinkt alle für FHEM relevanten Geräte dort hin. Also in meinem Beispiel TEK603 und VCONTROL:&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
cd /opt/fhem&lt;br /&gt;
mkdir dev&lt;br /&gt;
cd dev&lt;br /&gt;
ln -s /dev/serial/by-path/pci-0000:00:14.0-usb-0:4.3:1.0-port0 heizung&lt;br /&gt;
ln -s /dev/serial/by-path/pci-0000:00:14.0-usb-0:4.2:1.0-port0 oelpegel&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Solange man die beiden Geräte immer in die gleiche Buchse steckt, werden sie ab jetzt immer korrekt zugeordnet:&lt;br /&gt;
&amp;lt;source lang=text&amp;gt;&lt;br /&gt;
defmod Oelpegel TEK603 /opt/fhem/dev/oelpegel&lt;br /&gt;
defmod Heizung VCONTROL /opt/fhem/dev/heizung /opt/fhem/mycfg/vitoladens_300-C_J3Ra-24.cfg 120&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FHEM Config-Auszug ===&lt;br /&gt;
Ein exemplarischer Auszug aus der [[Konfiguration]]:&lt;br /&gt;
 define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0&lt;br /&gt;
 define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.*&lt;br /&gt;
&lt;br /&gt;
=== Eventmonitorbeispiel ===&lt;br /&gt;
&amp;lt;source lang=text&amp;gt;&lt;br /&gt;
2017-03-03_13:07:11 Oelpegel CONNECTED&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Time: 13:05:21&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Temperature: 18.33&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Ullage: 100&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel RemainingUsableLevel: 3227&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel RemainingUsablePercent: 31.0&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel TotalUsableCapacity: 10414&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
In den ersten 10 Minuten nach dem Anlernen, befindet sich das Gerät im Echtzeit-Modus. Das bedeutet es werden mehrere Messungen pro Sekunde übertragen. Das würde die Batterie sehr schnell lehren. Daher schaltet das Gerät nach 10 Minuten in den Normalbetrieb, in dem nur noch ganz grob alle 30 Minuten Werte übertragen werden. Die Übertragung zu FHEM findet auch nur statt, wenn über die Funkstrecke Daten vom Sensor kommen. Das führt zu der Situation, dass nach diesen 10 Minuten ein neu eingerichtetes &amp;lt;code&amp;gt;TEK603&amp;lt;/code&amp;gt; Modul sehr lange (Stunden ?) keine Daten anzeigt, obwohl das Display der Proteus Daten anzeigt. Das liegt einfach daran, dass dieses Display den zuletzt gespeicherten Messwert anzeigt egal wie lange der her ist. Übertragen wird bis zum nächsten Wert auf der Funkstrecke nichts.&lt;br /&gt;
&lt;br /&gt;
== Einsatzbeispiel ==&lt;br /&gt;
=== Anzeige des Ölstands in einem Öltank ===&lt;br /&gt;
{{todo|Beispielunterlagen erstellen}}&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Shop: [http://hausautomatisierung.co/haustechnik/ecometer-ultraschall-fuellstandsanzeige-fuer-heizoeltank/ EcoMeter Ultraschall Füllstandsanzeige für Heizöltank]&lt;br /&gt;
* Shop: [http://hausautomatisierung.co/haustechnik/ecometer-s-ultraschall-fuellstandsanzeige-fuer-zisterne-wassertank/ EcoMeter S Ultraschall Füllstandsanzeige für Zisterne, Wassertank]&lt;br /&gt;
* Hersteller: [https://proteus-meter.com/faq/ Bedienanleitung]&lt;br /&gt;
* Forenbeitrag zur FHEM-Einbindung: {{Link2Forum|Topic=27315}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Other Components]]&lt;br /&gt;
[[Kategorie:Füllstandsmesser]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=21408</id>
		<title>Proteus EcoMeter</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=21408"/>
		<updated>2017-04-29T12:52:03Z</updated>

		<summary type="html">&lt;p&gt;Eisler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Proteus_EcoMeter]] ist ein Ultraschallsensor mit Monitor zur Pegelmessung von Heizöl- oder Wassertanks&lt;br /&gt;
&lt;br /&gt;
* Sensor zum Einbau in den Tank&lt;br /&gt;
* Funkverbindung zwischen Sensor und Monitor&lt;br /&gt;
* USB-Verbindung zwischen Monitor und FHEM&lt;br /&gt;
* Für Wasser oder Öltanks&lt;br /&gt;
&lt;br /&gt;
EcoMeter für Heizöltanks&amp;lt;br&amp;gt;&lt;br /&gt;
EcoMeter S für Wassertanks&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Infobox Hardware&lt;br /&gt;
|Bild=EcoMeter.png&lt;br /&gt;
|Bildbeschreibung=EcoMeter Ultraschall Füllstandsanzeige für Heizöltank&lt;br /&gt;
|HWProtocol=unknown&lt;br /&gt;
|HWType=Sensor und Sender&lt;br /&gt;
|HWCategory=Other Components&lt;br /&gt;
|HWComm=Funk, 433,92MHz, USB&lt;br /&gt;
|HWChannels=unknown&lt;br /&gt;
|HWVoltage=1,5V LR2450, 5V USB&lt;br /&gt;
|HWPowerConsumption=unknown&lt;br /&gt;
|HWPoweredBy=Battery, USB&lt;br /&gt;
|HWSize=21 x 18 x 6 cm&lt;br /&gt;
|HWDeviceFHEM=[http://fhem.de/commandref.html#TEK603 TEK603]&lt;br /&gt;
|HWManufacturer=Tekelek&lt;br /&gt;
}}&lt;br /&gt;
== Features ==&lt;br /&gt;
EcoMeter liefert alle 60 Minuten einen Messwert&lt;br /&gt;
EcoMeter liefert alle 30 Minuten einen Messwert und bei schnellen Änderungen&lt;br /&gt;
&lt;br /&gt;
== Hinweise zum Betrieb mit FHEM ==&lt;br /&gt;
=== Definition/Anlernvorgang ===&lt;br /&gt;
# eventuell im Linux fehlende Pakete installieren &amp;lt;code&amp;gt;sudo apt-get install libdigest-crc-perl &amp;lt;/code&amp;gt;&lt;br /&gt;
# Monitor an das FHEM Gerät anschließen&lt;br /&gt;
# Festellen, dass die Verbindung geht. Dazu auf der Kommandozeile eingeben: &amp;lt;code&amp;gt;ls -al /dev/serial/by-id &amp;lt;br&amp;gt; lrwxrwxrwx 1 root root 13 Jan  1  1970 usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 -&amp;gt; ../../ttyUSB0 &amp;lt;/code&amp;gt; &lt;br /&gt;
# ecometer definieren &amp;lt;code&amp;gt;define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 &amp;lt;/code&amp;gt;&lt;br /&gt;
# LogFile definieren &amp;lt;code&amp;gt;define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.* &amp;lt;/code&amp;gt;&lt;br /&gt;
# Gerät wie in der Betriebsanleitung installieren&lt;br /&gt;
&lt;br /&gt;
=== Identifikation ===&lt;br /&gt;
Das Proteus EcoMeter verwendet für die Anbindung der seriellen Kommunikation an die USB-Schnittstelle einen sehr weit verbreiteten Chip der Familie &amp;lt;code&amp;gt;CP210x UART Bridges&amp;lt;/code&amp;gt;. Dieser Baustein hat die USB &amp;lt;code&amp;gt;ID 10c4:ea60&amp;lt;/code&amp;gt; über die auch der Name in &amp;lt;code&amp;gt;/dev/serial/by-id&amp;lt;/code&amp;gt; abgeleitet wird. Hat man mehrere Geräte die mit diesem Chip arbeiten (z.B. den optischen Adapter von Viessmann für [[Vitotronic_200_(Viessmann_Heizungssteuerung)|VCONTROL]]) am gleichen Rechner, so werden diese unter &amp;lt;code&amp;gt;lsusb&amp;lt;/code&amp;gt; mit identischer ID gelistet, aber nur einer erhält den passenden Namen unter &amp;lt;code&amp;gt;/dev/serial/by-id&amp;lt;/code&amp;gt;. Sie sind also auf diese Weise nicht unterscheidbar. Die kurzen Gerätenamen &amp;lt;code&amp;gt;/dev/ttyUSBx&amp;lt;/code&amp;gt; werden willkürlich zugeordnet. Damit sind die Geräte auch nicht unterscheidbar.&lt;br /&gt;
&lt;br /&gt;
Bleibt noch der Pfad wenn man die Geräte immer in die gleiche Buchse steckt. Diese Pfade werden unter Linux in &amp;lt;code&amp;gt;/dev/serial/by-path&amp;lt;/code&amp;gt; angelegt. Hier kann man einzelne Buchsen (auch von Hubs) identifizieren. Die Namen enthalten jedoch Doppelpunkte und FHEM verkraftet keine Gerätenamen mit Doppelpunkten. &amp;lt;small&amp;gt;&#039;&#039;Wer weiß warum bitte hier Lösung eintragen.&#039;&#039;&amp;lt;/small&amp;gt; Um diese ohnehin unhandlichen Namen kürzer und verträglicher zu bekommen, kann man Symbolische Links anlegen. Dazu erzeugt man am besten in &amp;lt;code&amp;gt;/opt/fhem&amp;lt;/code&amp;gt; ein Verzeichnis &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; und verlinkt alle für FHEM relevanten Geräte dort hin. Also in meinem Beispiel TEK603 und VCONTROL:&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
cd /opt/fhem&lt;br /&gt;
mkdir dev&lt;br /&gt;
cd dev&lt;br /&gt;
ln -s /dev/serial/by-path/pci-0000:00:14.0-usb-0:4.3:1.0-port0 heizung&lt;br /&gt;
ln -s /dev/serial/by-path/pci-0000:00:14.0-usb-0:4.2:1.0-port0 oelpegel&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Solange man die beiden Geräte immer in die gleiche Buchse steckt, werden sie ab jetzt immer korrekt zugeordnet:&lt;br /&gt;
&amp;lt;source lang=text&amp;gt;&lt;br /&gt;
defmod Oelpegel TEK603 /opt/fhem/dev/oelpegel&lt;br /&gt;
defmod Heizung VCONTROL /opt/fhem/dev/heizung /opt/fhem/mycfg/vitoladens_300-C_J3Ra-24.cfg 120&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FHEM Config-Auszug ===&lt;br /&gt;
Ein exemplarischer Auszug aus der [[Konfiguration]]:&lt;br /&gt;
 define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0&lt;br /&gt;
 define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.*&lt;br /&gt;
&lt;br /&gt;
=== Eventmonitorbeispiel ===&lt;br /&gt;
&amp;lt;source lang=text&amp;gt;&lt;br /&gt;
2017-03-03_13:07:11 Oelpegel CONNECTED&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Time: 13:05:21&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Temperature: 18.33&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Ullage: 100&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel RemainingUsableLevel: 3227&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel RemainingUsablePercent: 31.0&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel TotalUsableCapacity: 10414&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
In den ersten 10 Minuten nach dem Anlernen, befindet sich das Gerät im Echtzeit-Modus. Das bedeutet es werden mehrere Messungen pro Sekunde übertragen. Das würde die Batterie sehr schnell lehren. Daher schaltet das Gerät nach 10 Minuten in den Normalbetrieb, in dem nur noch ganz grob alle 30 Minuten Werte übertragen werden. Die Übertragung zu FHEM findet auch nur statt, wenn über die Funkstrecke Daten vom Sensor kommen. Das führt zu der Situation, dass nach diesen 10 Minuten ein neu eingerichtetes &amp;lt;code&amp;gt;TEK603&amp;lt;/code&amp;gt; Modul sehr lange (Stunden ?) keine Daten anzeigt, obwohl das Display der Proteus Daten anzeigt. Das liegt einfach daran, dass dieses Display den zuletzt gespeicherten Messwert anzeigt egal wie lange der her ist. Übertragen wird bis zum nächsten Wert auf der Funkstrecke nichts.&lt;br /&gt;
&lt;br /&gt;
== Einsatzbeispiel ==&lt;br /&gt;
=== Anzeige des Ölstands in einem Öltank ===&lt;br /&gt;
{{todo|Beispielunterlagen erstellen}}&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Hersteller: [http://hausautomatisierung.co/haustechnik/ecometer-ultraschall-fuellstandsanzeige-fuer-heizoeltank/ EcoMeter Ultraschall Füllstandsanzeige für Heizöltank]&lt;br /&gt;
* Hersteller: [http://hausautomatisierung.co/haustechnik/ecometer-s-ultraschall-fuellstandsanzeige-fuer-zisterne-wassertank/ EcoMeter S Ultraschall Füllstandsanzeige für Zisterne, Wassertank]&lt;br /&gt;
* Hersteller: [https://proteus-meter.com/faq/ Bedienanleitung]&lt;br /&gt;
* Forenbeitrag zur FHEM-Einbindung: {{Link2Forum|Topic=27315}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Other Components]]&lt;br /&gt;
[[Kategorie:Füllstandsmesser]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=21407</id>
		<title>Proteus EcoMeter</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=21407"/>
		<updated>2017-04-29T12:46:57Z</updated>

		<summary type="html">&lt;p&gt;Eisler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Proteus_EcoMeter]] ist ein Ultraschallsensor mit Monitor zur Pegelmessung von Heizöl- oder Wassertanks&lt;br /&gt;
&lt;br /&gt;
* Sensor zum Einbau in den Tank&lt;br /&gt;
* Funkverbindung zwischen Sensor und Monitor&lt;br /&gt;
* USB-Verbindung zwischen Monitor und FHEM&lt;br /&gt;
* Für Wasser oder Öltanks&lt;br /&gt;
&lt;br /&gt;
EcoMeter für Heizöltanks&amp;lt;br&amp;gt;&lt;br /&gt;
EcoMeter S für Wassertanks&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Infobox Hardware&lt;br /&gt;
|Bild=EcoMeter.png&lt;br /&gt;
|Bildbeschreibung=EcoMeter Ultraschall Füllstandsanzeige für Heizöltank&lt;br /&gt;
|HWProtocol=unknown&lt;br /&gt;
|HWType=Sensor und Sender&lt;br /&gt;
|HWCategory=Other Components&lt;br /&gt;
|HWComm=Funk, 433,92MHz, USB&lt;br /&gt;
|HWChannels=unknown&lt;br /&gt;
|HWVoltage=1,5V LR2450, 5V USB&lt;br /&gt;
|HWPowerConsumption=unknown&lt;br /&gt;
|HWPoweredBy=Battery, USB&lt;br /&gt;
|HWSize=21 x 18 x 6 cm&lt;br /&gt;
|HWDeviceFHEM=[http://fhem.de/commandref.html#TEK603 TEK603]&lt;br /&gt;
|HWManufacturer=Tekelek&lt;br /&gt;
}}&lt;br /&gt;
== Features ==&lt;br /&gt;
EcoMeter liefert alle 60 Minuten einen Messwert&lt;br /&gt;
EcoMeter liefert alle 30 Minuten einen Messwert und bei schnellen Änderungen&lt;br /&gt;
&lt;br /&gt;
== Hinweise zum Betrieb mit FHEM ==&lt;br /&gt;
=== Definition/Anlernvorgang ===&lt;br /&gt;
# eventuell im Linux fehlende Pakete installieren &amp;lt;code&amp;gt;sudo apt-get install libdigest-crc-perl &amp;lt;/code&amp;gt;&lt;br /&gt;
# Monitor an das FHEM Gerät anschließen&lt;br /&gt;
# Festellen, dass die Verbindung geht. Dazu auf der Kommandozeile eingeben: &amp;lt;code&amp;gt;ls -al /dev/serial/by-id &amp;lt;br&amp;gt; lrwxrwxrwx 1 root root 13 Jan  1  1970 usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 -&amp;gt; ../../ttyUSB0 &amp;lt;/code&amp;gt; &lt;br /&gt;
# ecometer definieren &amp;lt;code&amp;gt;define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 &amp;lt;/code&amp;gt;&lt;br /&gt;
# LogFile definieren &amp;lt;code&amp;gt;define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.* &amp;lt;/code&amp;gt;&lt;br /&gt;
# Gerät wie in der Betriebsanleitung installieren&lt;br /&gt;
&lt;br /&gt;
=== Identifikation ===&lt;br /&gt;
Das Proteus EcoMeter verwendet für die Anbindung der seriellen Kommunikation an die USB-Schnittstelle einen sehr weit verbreiteten Chip der Familie &amp;lt;code&amp;gt;CP210x UART Bridges&amp;lt;/code&amp;gt;. Dieser Baustein hat die USB &amp;lt;code&amp;gt;ID 10c4:ea60&amp;lt;/code&amp;gt; über die auch der Name in &amp;lt;code&amp;gt;/dev/serial/by-id&amp;lt;/code&amp;gt; abgeleitet wird. Hat man mehrere Geräte die mit diesem Chip arbeiten (z.B. den optischen Adapter von Viessmann für [[Vitotronic_200_(Viessmann_Heizungssteuerung)|VCONTROL]]) am gleichen Rechner, so werden diese unter &amp;lt;code&amp;gt;lsusb&amp;lt;/code&amp;gt; mit identischer ID gelistet, aber nur einer erhält den passenden Namen unter &amp;lt;code&amp;gt;/dev/serial/by-id&amp;lt;/code&amp;gt;. Sie sind also auf diese Weise nicht unterscheidbar. Die kurzen Gerätenamen &amp;lt;code&amp;gt;/dev/ttyUSBx&amp;lt;/code&amp;gt; werden willkürlich zugeordnet. Damit sind die Geräte auch nicht unterscheidbar.&lt;br /&gt;
&lt;br /&gt;
Bleibt noch der Pfad wenn man die Geräte immer in die gleiche Buchse steckt. Diese Pfade werden unter Linux in &amp;lt;code&amp;gt;/dev/serial/by-path&amp;lt;/code&amp;gt; angelegt. Hier kann man einzelne Buchsen (auch von Hubs) identifizieren. Die Namen enthalten jedoch Doppelpunkte und FHEM verkraftet keine Gerätenamen mit Doppelpunkten. &amp;lt;small&amp;gt;&#039;&#039;Wer weiß warum bitte hier Lösung eintragen.&#039;&#039;&amp;lt;/small&amp;gt; Um diese ohnehin unhandlichen Namen kürzer und verträglicher zu bekommen, kann man Symbolische Links anlegen. Dazu erzeugt man am besten in &amp;lt;code&amp;gt;/opt/fhem&amp;lt;/code&amp;gt; ein Verzeichnis &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; und verlinkt alle für FHEM relevanten Geräte dort hin. Also in meinem Beispiel TEK603 und VCONTROL:&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
cd /opt/fhem&lt;br /&gt;
mkdir dev&lt;br /&gt;
cd dev&lt;br /&gt;
ln -s /dev/serial/by-path/pci-0000:00:14.0-usb-0:4.3:1.0-port0 heizung&lt;br /&gt;
ln -s /dev/serial/by-path/pci-0000:00:14.0-usb-0:4.2:1.0-port0 oelpegel&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Solange man die beiden Geräte immer in die gleiche Buchse steckt, werden sie ab jetzt immer korrekt zugeordnet:&lt;br /&gt;
&amp;lt;source lang=text&amp;gt;&lt;br /&gt;
defmod Oelpegel TEK603 /opt/fhem/dev/oelpegel&lt;br /&gt;
defmod Heizung VCONTROL /opt/fhem/dev/heizung /opt/fhem/mycfg/vitoladens_300-C_J3Ra-24.cfg 120&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FHEM Config-Auszug ===&lt;br /&gt;
Ein exemplarischer Auszug aus der [[Konfiguration]]:&lt;br /&gt;
 define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0&lt;br /&gt;
 define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.*&lt;br /&gt;
&lt;br /&gt;
=== Eventmonitorbeispiel ===&lt;br /&gt;
&amp;lt;source lang=text&amp;gt;&lt;br /&gt;
2017-03-03_13:07:11 Oelpegel CONNECTED&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Time: 13:05:21&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Temperature: 18.33&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Ullage: 100&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel RemainingUsableLevel: 3227&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel RemainingUsablePercent: 31.0&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel TotalUsableCapacity: 10414&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
In den ersten 10 Minuten nach dem Anlernen, befindet sich das Gerät im Echtzeit-Modus. Das bedeutet es werden mehrere Messungen pro Sekunde übertragen. Das würde die Batterie sehr schnell lehren. Daher schaltet das Gerät nach 10 Minuten in den Normalbetrieb, in dem nur noch ganz grob alle 30 Minuten Werte übertragen werden. Die Übertragung zu FHEM findet auch nur statt, wenn über die Funkstrecke Daten vom Sensor kommen. Das führt zu der Situation, dass nach diesen 10 Minuten ein neu eingerichtetes &amp;lt;code&amp;gt;TEK603&amp;lt;/code&amp;gt; Modul sehr lange (Stunden ?) keine Daten anzeigt, obwohl das Display der Proteus Daten anzeigt. Das liegt einfach daran, dass dieses Display den zuletzt gespeicherten Messwert anzeigt egal wie lange der her ist. Übertragen wird bis zum nächsten Wert auf der Funkstrecke nichts.&lt;br /&gt;
&lt;br /&gt;
== Einsatzbeispiel ==&lt;br /&gt;
=== Anzeige des Ölstands in einem Öltank ===&lt;br /&gt;
{{todo|Beispielunterlagen erstellen}}&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Hersteller: [https://proteus-meter.com/produkte/proteus-ecometer/ Shop]&lt;br /&gt;
* Hersteller: [https://proteus-meter.com/faq/ Bedienanleitung]&lt;br /&gt;
* Forenbeitrag zur FHEM-Einbindung: {{Link2Forum|Topic=27315}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Other Components]]&lt;br /&gt;
[[Kategorie:Füllstandsmesser]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=21406</id>
		<title>Proteus EcoMeter</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=21406"/>
		<updated>2017-04-29T12:46:09Z</updated>

		<summary type="html">&lt;p&gt;Eisler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Proteus_EcoMeter]] ist ein Ultraschallsensor mit Monitor zur Pegelmessung von Heizöl- oder Wassertanks&lt;br /&gt;
&lt;br /&gt;
* Sensor zum Einbau in den Tank&lt;br /&gt;
* Funkverbindung zwischen Sensor und Monitor&lt;br /&gt;
* USB-Verbindung zwischen Monitor und FHEM&lt;br /&gt;
* Für Wasser oder Öltanks&lt;br /&gt;
&lt;br /&gt;
EcoMeter für Heizöltanks&amp;lt;br&amp;gt;&lt;br /&gt;
EcoMeter S für Wassertanks&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Infobox Hardware&lt;br /&gt;
|Bild=EcoMeter.png&lt;br /&gt;
|Bildbeschreibung=todo&lt;br /&gt;
|HWProtocol=unknown&lt;br /&gt;
|HWType=Sensor und Sender&lt;br /&gt;
|HWCategory=Other Components&lt;br /&gt;
|HWComm=Funk, 433,92MHz, USB&lt;br /&gt;
|HWChannels=unknown&lt;br /&gt;
|HWVoltage=1,5V LR2450, 5V USB&lt;br /&gt;
|HWPowerConsumption=unknown&lt;br /&gt;
|HWPoweredBy=Battery, USB&lt;br /&gt;
|HWSize=21 x 18 x 6 cm&lt;br /&gt;
|HWDeviceFHEM=[http://fhem.de/commandref.html#TEK603 TEK603]&lt;br /&gt;
|HWManufacturer=Tekelek&lt;br /&gt;
}}&lt;br /&gt;
== Features ==&lt;br /&gt;
EcoMeter liefert alle 60 Minuten einen Messwert&lt;br /&gt;
EcoMeter liefert alle 30 Minuten einen Messwert und bei schnellen Änderungen&lt;br /&gt;
&lt;br /&gt;
== Hinweise zum Betrieb mit FHEM ==&lt;br /&gt;
=== Definition/Anlernvorgang ===&lt;br /&gt;
# eventuell im Linux fehlende Pakete installieren &amp;lt;code&amp;gt;sudo apt-get install libdigest-crc-perl &amp;lt;/code&amp;gt;&lt;br /&gt;
# Monitor an das FHEM Gerät anschließen&lt;br /&gt;
# Festellen, dass die Verbindung geht. Dazu auf der Kommandozeile eingeben: &amp;lt;code&amp;gt;ls -al /dev/serial/by-id &amp;lt;br&amp;gt; lrwxrwxrwx 1 root root 13 Jan  1  1970 usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 -&amp;gt; ../../ttyUSB0 &amp;lt;/code&amp;gt; &lt;br /&gt;
# ecometer definieren &amp;lt;code&amp;gt;define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 &amp;lt;/code&amp;gt;&lt;br /&gt;
# LogFile definieren &amp;lt;code&amp;gt;define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.* &amp;lt;/code&amp;gt;&lt;br /&gt;
# Gerät wie in der Betriebsanleitung installieren&lt;br /&gt;
&lt;br /&gt;
=== Identifikation ===&lt;br /&gt;
Das Proteus EcoMeter verwendet für die Anbindung der seriellen Kommunikation an die USB-Schnittstelle einen sehr weit verbreiteten Chip der Familie &amp;lt;code&amp;gt;CP210x UART Bridges&amp;lt;/code&amp;gt;. Dieser Baustein hat die USB &amp;lt;code&amp;gt;ID 10c4:ea60&amp;lt;/code&amp;gt; über die auch der Name in &amp;lt;code&amp;gt;/dev/serial/by-id&amp;lt;/code&amp;gt; abgeleitet wird. Hat man mehrere Geräte die mit diesem Chip arbeiten (z.B. den optischen Adapter von Viessmann für [[Vitotronic_200_(Viessmann_Heizungssteuerung)|VCONTROL]]) am gleichen Rechner, so werden diese unter &amp;lt;code&amp;gt;lsusb&amp;lt;/code&amp;gt; mit identischer ID gelistet, aber nur einer erhält den passenden Namen unter &amp;lt;code&amp;gt;/dev/serial/by-id&amp;lt;/code&amp;gt;. Sie sind also auf diese Weise nicht unterscheidbar. Die kurzen Gerätenamen &amp;lt;code&amp;gt;/dev/ttyUSBx&amp;lt;/code&amp;gt; werden willkürlich zugeordnet. Damit sind die Geräte auch nicht unterscheidbar.&lt;br /&gt;
&lt;br /&gt;
Bleibt noch der Pfad wenn man die Geräte immer in die gleiche Buchse steckt. Diese Pfade werden unter Linux in &amp;lt;code&amp;gt;/dev/serial/by-path&amp;lt;/code&amp;gt; angelegt. Hier kann man einzelne Buchsen (auch von Hubs) identifizieren. Die Namen enthalten jedoch Doppelpunkte und FHEM verkraftet keine Gerätenamen mit Doppelpunkten. &amp;lt;small&amp;gt;&#039;&#039;Wer weiß warum bitte hier Lösung eintragen.&#039;&#039;&amp;lt;/small&amp;gt; Um diese ohnehin unhandlichen Namen kürzer und verträglicher zu bekommen, kann man Symbolische Links anlegen. Dazu erzeugt man am besten in &amp;lt;code&amp;gt;/opt/fhem&amp;lt;/code&amp;gt; ein Verzeichnis &amp;lt;code&amp;gt;dev&amp;lt;/code&amp;gt; und verlinkt alle für FHEM relevanten Geräte dort hin. Also in meinem Beispiel TEK603 und VCONTROL:&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
cd /opt/fhem&lt;br /&gt;
mkdir dev&lt;br /&gt;
cd dev&lt;br /&gt;
ln -s /dev/serial/by-path/pci-0000:00:14.0-usb-0:4.3:1.0-port0 heizung&lt;br /&gt;
ln -s /dev/serial/by-path/pci-0000:00:14.0-usb-0:4.2:1.0-port0 oelpegel&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Solange man die beiden Geräte immer in die gleiche Buchse steckt, werden sie ab jetzt immer korrekt zugeordnet:&lt;br /&gt;
&amp;lt;source lang=text&amp;gt;&lt;br /&gt;
defmod Oelpegel TEK603 /opt/fhem/dev/oelpegel&lt;br /&gt;
defmod Heizung VCONTROL /opt/fhem/dev/heizung /opt/fhem/mycfg/vitoladens_300-C_J3Ra-24.cfg 120&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FHEM Config-Auszug ===&lt;br /&gt;
Ein exemplarischer Auszug aus der [[Konfiguration]]:&lt;br /&gt;
 define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0&lt;br /&gt;
 define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.*&lt;br /&gt;
&lt;br /&gt;
=== Eventmonitorbeispiel ===&lt;br /&gt;
&amp;lt;source lang=text&amp;gt;&lt;br /&gt;
2017-03-03_13:07:11 Oelpegel CONNECTED&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Time: 13:05:21&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Temperature: 18.33&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel Ullage: 100&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel RemainingUsableLevel: 3227&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel RemainingUsablePercent: 31.0&lt;br /&gt;
2017-03-03_13:08:27 Oelpegel TotalUsableCapacity: 10414&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
In den ersten 10 Minuten nach dem Anlernen, befindet sich das Gerät im Echtzeit-Modus. Das bedeutet es werden mehrere Messungen pro Sekunde übertragen. Das würde die Batterie sehr schnell lehren. Daher schaltet das Gerät nach 10 Minuten in den Normalbetrieb, in dem nur noch ganz grob alle 30 Minuten Werte übertragen werden. Die Übertragung zu FHEM findet auch nur statt, wenn über die Funkstrecke Daten vom Sensor kommen. Das führt zu der Situation, dass nach diesen 10 Minuten ein neu eingerichtetes &amp;lt;code&amp;gt;TEK603&amp;lt;/code&amp;gt; Modul sehr lange (Stunden ?) keine Daten anzeigt, obwohl das Display der Proteus Daten anzeigt. Das liegt einfach daran, dass dieses Display den zuletzt gespeicherten Messwert anzeigt egal wie lange der her ist. Übertragen wird bis zum nächsten Wert auf der Funkstrecke nichts.&lt;br /&gt;
&lt;br /&gt;
== Einsatzbeispiel ==&lt;br /&gt;
=== Anzeige des Ölstands in einem Öltank ===&lt;br /&gt;
{{todo|Beispielunterlagen erstellen}}&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Hersteller: [https://proteus-meter.com/produkte/proteus-ecometer/ Shop]&lt;br /&gt;
* Hersteller: [https://proteus-meter.com/faq/ Bedienanleitung]&lt;br /&gt;
* Forenbeitrag zur FHEM-Einbindung: {{Link2Forum|Topic=27315}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Other Components]]&lt;br /&gt;
[[Kategorie:Füllstandsmesser]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=MQTT_Einf%C3%BChrung_Teil_3&amp;diff=21116</id>
		<title>MQTT Einführung Teil 3</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=MQTT_Einf%C3%BChrung_Teil_3&amp;diff=21116"/>
		<updated>2017-04-02T07:40:16Z</updated>

		<summary type="html">&lt;p&gt;Eisler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= MQTT Einführung Teil 3 =&lt;br /&gt;
Zeit sich die Hände schmutzig zu machen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NOCH NICHT GANZ FERTIG&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Übersicht ==&lt;br /&gt;
Im folgenden basteln wir einen einfachen Temperatursensor.&lt;br /&gt;
&lt;br /&gt;
Das ist nicht aufregend. Jedoch ist der Code im Originalzustand sehr gut überschaubar. Man kann daher gut die Änderungen sehen.&lt;br /&gt;
&lt;br /&gt;
== Originales Projekt ==&lt;br /&gt;
[http://fluuux.de/2012/10/arduino-temperatur-und-luftfeuchtigkeit-mit-dem-dht22-prufen/ hier bitte nachlesen]&lt;br /&gt;
&lt;br /&gt;
Mit freundlicher Genehmigung des Autors hier der originale Quellcode:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;arduino&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;quot;DHT.h&amp;quot;&lt;br /&gt;
 &lt;br /&gt;
#define DHTPIN 9     &lt;br /&gt;
#define DHTTYPE DHT22 //DHT11, DHT21, DHT22&lt;br /&gt;
 &lt;br /&gt;
DHT dht(DHTPIN, DHTTYPE);&lt;br /&gt;
 &lt;br /&gt;
void setup() &lt;br /&gt;
{&lt;br /&gt;
  Serial.begin(9600); &lt;br /&gt;
  Serial.println(&amp;quot;DHT22 - Test!&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
  dht.begin();&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void loop() &lt;br /&gt;
{&lt;br /&gt;
  float h = dht.readHumidity();     //Luftfeuchte auslesen&lt;br /&gt;
  float t = dht.readTemperature();  //Temperatur auslesen&lt;br /&gt;
 &lt;br /&gt;
  // Prüfen ob eine gültige Zahl zurückgegeben wird. Wenn NaN (not a number) zurückgegeben wird, dann Fehler ausgeben.&lt;br /&gt;
  if (isnan(t) || isnan(h)) &lt;br /&gt;
  {&lt;br /&gt;
    Serial.println(&amp;quot;DHT22 konnte nicht ausgelesen werden&amp;quot;);&lt;br /&gt;
  } &lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    Serial.print(&amp;quot;Luftfeuchte: &amp;quot;); &lt;br /&gt;
    Serial.print(h);&lt;br /&gt;
    Serial.print(&amp;quot; %\t&amp;quot;);&lt;br /&gt;
    Serial.print(&amp;quot;Temperatur: &amp;quot;); &lt;br /&gt;
    Serial.print(t);&lt;br /&gt;
    Serial.println(&amp;quot; C&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Benötigte Hardware ===&lt;br /&gt;
* Arduino&lt;br /&gt;
* DHT-22 Sensor (nicht der Beste, aber seine Verwendung hält den Code einfach)&lt;br /&gt;
* 10 kOhm Widerstand&lt;br /&gt;
* Steckbrett&lt;br /&gt;
* Drähte&lt;br /&gt;
&lt;br /&gt;
Zusätzlich&lt;br /&gt;
* Ethernetshield mit W5100 Chip&lt;br /&gt;
* MQTT Broker (Testweise: 37.187.106.16 =&amp;gt; IP von test.mosquitto.org)&lt;br /&gt;
&lt;br /&gt;
== Kurze Beschreibung was der originale Code tut ==&lt;br /&gt;
* Zeilen 1-6: Library einbinden, PIN festlegen, initialisieren.&lt;br /&gt;
* Zeilen 8-14: Serielle Schnittstelle konfigurieren, netten Text schreiben...&lt;br /&gt;
* Zeilen 18-19: Sensorwerte auslesen&lt;br /&gt;
* Zeilen 21-25: Fehlerbehandlung&lt;br /&gt;
* Zeilen 26-34: Sensorwerte mit Beschreibung an die serielle Schnittstelle senden&lt;br /&gt;
&lt;br /&gt;
=== Bibliotheken die zu installieren sind ===&lt;br /&gt;
Anmerkung:&lt;br /&gt;
&lt;br /&gt;
Die Arduino IDE bietet viele Möglichkeiten Bibliotheken einzubinden. Bevorzugt verwendet man den eingebauten Library-Manager. Wer das nicht will, kann auch die Bibliotheken als ZIP Datei herunterladen, und dann mit &amp;quot;Bibliothek aus ZIP Datei hinzufügen&amp;quot; einbinden.&lt;br /&gt;
&lt;br /&gt;
Im folgenden die Links zu den ZIP Dateien&lt;br /&gt;
Für den originalen Sketch&lt;br /&gt;
* [https://github.com/adafruit/DHT-sensor-library Adafruit DHT Library]&lt;br /&gt;
* [https://github.com/adafruit/Adafruit_Sensor Adafruit Sensor Library]&lt;br /&gt;
In der Adruino IDE gibt es eine Warnung, &#039;&#039;WARNUNG: Unberechtigter Ordner .github in der Bibliothek &#039;DHT sensor library&#039;&#039;&#039;; diese kann ignoriert werden. Da die Meldung aber immer kommt, löschen wir einfach besagten Ordner &amp;quot;.github&amp;quot; im Ordner der DHT Sensor Library.&lt;br /&gt;
&lt;br /&gt;
Für MQTT brauchen wir drei weitere Libraries:&lt;br /&gt;
* [https://github.com/knolleary/pubsubclient PubSubClient Library, MQTT]&lt;br /&gt;
* Ethernet, ist in der Arduino IDE schon mit dabei&lt;br /&gt;
* SPI, für das Netzwerkshield, ist auch schon dabei&lt;br /&gt;
&lt;br /&gt;
== Notwendige Änderungen ==&lt;br /&gt;
&lt;br /&gt;
Im folgenden bauen wir den Sketch um. Zunächst prüfen wir, was am Sketch an sich geändert werden muss, anschließend fügen wir den MQTT Code hinzu. Zuletzt nutzen wir noch einige MQTT Features für weitere Optimierungen.&lt;br /&gt;
&lt;br /&gt;
=== Änderungen am Skript selbst ===&lt;br /&gt;
Die einfachste Möglichkeit ein Skript auf MQTT umzustellen, ist das Ändern der Ausgabe von Serial.print in ein mqtt.publish. Hier ergibt sich im Sketch ein Problem: Pubsubclient kann keine Werte vom Typ &amp;quot;float&amp;quot; als Payload versenden. Daher müssen wir zwei Konvertierungen hinzufügen:&lt;br /&gt;
* dtostrf(h,6, 1, humidity);&lt;br /&gt;
* dtostrf(t,6, 1, temperature);&lt;br /&gt;
machen den Trick: Nehme float h, benutze insgesamt 6 Stellen, 1 Nachkommastelle und schreibe das Ergebnis in humidity.&lt;br /&gt;
&lt;br /&gt;
Weiterhin ist es angenehmer, Variablen etc. am Anfang eines Sketches zu definieren, das machen wir auch noch.&lt;br /&gt;
&lt;br /&gt;
=== Änderungen für MQTT ===&lt;br /&gt;
Am einfachsten ist, wir nehmen uns einen der mitgelieferten MQTT Beispielsketche: mqtt_reconnect_nonblocking. Das müssen wir eigentlich nur an den jeweiligen Stellen in den originalen Sketch kopieren.&lt;br /&gt;
Dabei sind die IP Adressen anzupassen und eine MAC Adresse zu vergeben.&lt;br /&gt;
&lt;br /&gt;
Statt oder zusätzlich zu unserer Ausgabe über die serielle Schnittstelle fügen wir noch einen client.publish ein, so dass die Werte auf dem Broker landen.&lt;br /&gt;
&lt;br /&gt;
Der Vollständigkeit halber, hier der Sketch für das nicht - blockierende MQTT reconnect:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;arduino&amp;quot;&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 Reconnecting MQTT example - non-blocking&lt;br /&gt;
&lt;br /&gt;
 This sketch demonstrates how to keep the client connected&lt;br /&gt;
 using a non-blocking reconnect function. If the client loses&lt;br /&gt;
 its connection, it attempts to reconnect every 5 seconds&lt;br /&gt;
 without blocking the main loop.&lt;br /&gt;
&lt;br /&gt;
*/&lt;br /&gt;
&lt;br /&gt;
#include &amp;lt;SPI.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Ethernet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Update these with values suitable for your hardware/network.&lt;br /&gt;
byte mac[]    = {  0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED };&lt;br /&gt;
IPAddress ip(172, 16, 0, 100);&lt;br /&gt;
IPAddress server(172, 16, 0, 2);&lt;br /&gt;
&lt;br /&gt;
void callback(char* topic, byte* payload, unsigned int length) {&lt;br /&gt;
  // handle message arrived&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
EthernetClient ethClient;&lt;br /&gt;
PubSubClient client(ethClient);&lt;br /&gt;
&lt;br /&gt;
long lastReconnectAttempt = 0;&lt;br /&gt;
&lt;br /&gt;
boolean reconnect() {&lt;br /&gt;
  if (client.connect(&amp;quot;arduinoClient&amp;quot;)) {&lt;br /&gt;
    // Once connected, publish an announcement...&lt;br /&gt;
    client.publish(&amp;quot;outTopic&amp;quot;,&amp;quot;hello world&amp;quot;);&lt;br /&gt;
    // ... and resubscribe&lt;br /&gt;
    client.subscribe(&amp;quot;inTopic&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  return client.connected();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void setup()&lt;br /&gt;
{&lt;br /&gt;
  client.setServer(server, 1883);&lt;br /&gt;
  client.setCallback(callback);&lt;br /&gt;
&lt;br /&gt;
  Ethernet.begin(mac, ip);&lt;br /&gt;
  delay(1500);&lt;br /&gt;
  lastReconnectAttempt = 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void loop()&lt;br /&gt;
{&lt;br /&gt;
  if (!client.connected()) {&lt;br /&gt;
    long now = millis();&lt;br /&gt;
    if (now - lastReconnectAttempt &amp;gt; 5000) {&lt;br /&gt;
      lastReconnectAttempt = now;&lt;br /&gt;
      // Attempt to reconnect&lt;br /&gt;
      if (reconnect()) {&lt;br /&gt;
        lastReconnectAttempt = 0;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  } else {&lt;br /&gt;
    // Client connected&lt;br /&gt;
&lt;br /&gt;
    client.loop();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Optimierungsmöglichkeiten === &lt;br /&gt;
&lt;br /&gt;
==== Unfreiwilliger Netzwerktest ====&lt;br /&gt;
Jedesmal wenn die Schleife durchgelaufen wird, bekämen wir eine Nachricht mit der Temperatur und eine mit der Luftfeuchtigkeit. Das ist nicht so wirklich schlau. Hier bieten sich 3 Alternativen an:&lt;br /&gt;
* Delay =&amp;gt; dumme Idee, blockiert den Arduino, nicht benutzen&lt;br /&gt;
* eine nicht-blockierende Methode für den Arduino nehmen =&amp;gt; kann man machen&lt;br /&gt;
* Senden nur bei Werteänderung =&amp;gt; vermutlich das Ressourcenschonendste&lt;br /&gt;
&lt;br /&gt;
Ich verwende Variante 3.&lt;br /&gt;
&lt;br /&gt;
Wir benötigen dazu noch zwei weitere Variablen (h_alt und t_alt); jedesmal wenn die Sendeschleife durchlaufen wird, speichern wir h und t in diese Variablen. Zusätzlich benötigt unsere Schleife nach der Fehlerbehandlung noch einen if Zweig, der dafür sorgt, dass nichts getan wird wenn beide Werte sich nicht geändert haben.&lt;br /&gt;
&lt;br /&gt;
Jetzt können wir MQTT anweisen, die letzten Messwerte retained zu senden. Sonst würden wir erst dann Werte bekommen, wenn sich entweder die Temperatur oder die Luftfeuchte verändert haben.&lt;br /&gt;
* client.publish(&amp;quot;zuHause/Arduino_1/Kueche/Kuehlschrank/Luftfeuchte&amp;quot;,humidity);&lt;br /&gt;
wird zu&lt;br /&gt;
* client.publish(&amp;quot;zuHause/Arduino_1/Kueche/Kuehlschrank/Luftfeuchte&amp;quot;,humidity, true);&lt;br /&gt;
* das true dahinter ist das Flag für retained Messages&lt;br /&gt;
&lt;br /&gt;
==== Nachtrag zum Thema Pausen ====&lt;br /&gt;
Mit den Codeänderungen oben haben wir schon mal ein dauerhaftes publishen der gleichen Daten verhindert. Jedoch gibt es natürlich noch einige technische Besonderheiten, die wir ebenfalls berücksichtigen sollten:&lt;br /&gt;
&lt;br /&gt;
Bis jetzt führt unser Sketch bei jedem Durchlauf durch void.loop() eine Messung durch. Das hat zwei Folgen:&lt;br /&gt;
* der Sensor ist im Dauerbetrieb&lt;br /&gt;
* kleine Schwankungen führen natürlich zu einem erneuten publishen.&lt;br /&gt;
Wenn wir den Code, sagen wir, nur alle 2 Minuten ausführen würden, würderd der Sensor weniger belastet. Beim dem Verwendeten spielt es nicht wirklich eine große Rolle, aber es gibt Sensoren die auf einen derart häufigen Zugriff durchaus empfindlich reagieren können.&lt;br /&gt;
&lt;br /&gt;
Gleichzeitig bekommen wir viele kleine Schwankungen  (ich nenne sie mal Messungenauigkeiten) übermittelt, die uns eigentlich nicht interessieren.&lt;br /&gt;
&lt;br /&gt;
Die einfache Variante wäre wieder ein Delay. Dieses würde recht schnell zu einem Fehler führen: Ein Delay blockiert. Für MQTT bedeutet es, dass keine Keep-alive Pakete mehr gesendet werden. Hätten wir eingehende Nachrichten, dann würden wir diese auch verpassen.&lt;br /&gt;
Wir merken uns: nutze niemals Delays.&lt;br /&gt;
&lt;br /&gt;
Die oben schon angesprochene Lösung, eine nicht blockierende Methode verwenden, ist sehr gut geeignet.&lt;br /&gt;
&lt;br /&gt;
Wie geht das?&lt;br /&gt;
&lt;br /&gt;
Zunächst zählt der Arduino seine Laufzeit in Millisekunden. Wir schreiben unseren Temperaturausleseteil in eine kleine Schleife.&lt;br /&gt;
* zwei weitere Variablen&lt;br /&gt;
** unsigned long previousMillis = 0; //Zählervariable, zählt Millisekunden seit dem letzten Funktionsaufruf nach oben&lt;br /&gt;
** const long interval = 120000; //120000 Millisekunden aka 120 Sekunden, das Interval wie oft der Sensor überhaupt benutzt wird&lt;br /&gt;
* Drei Zeilen Programmcode mehr:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;arduino&amp;quot;&amp;gt;&lt;br /&gt;
unsigned long currentMillis = millis();&lt;br /&gt;
&lt;br /&gt;
  if (currentMillis - previousMillis &amp;gt;= interval) {&lt;br /&gt;
    previousMillis = currentMillis;&lt;br /&gt;
    &lt;br /&gt;
    h = dht.readHumidity();     //Luftfeuchte auslesen&lt;br /&gt;
    t = dht.readTemperature();  //Temperatur auslesen&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Last Will &amp;amp; Testament ====&lt;br /&gt;
Retten wir auch die Statusmeldungen des Skripts nach MQTT. Dazu ergänzen wir den client.connect Aufruf:&lt;br /&gt;
* if (client.connect(&amp;quot;Arduino_1&amp;quot;)) {&lt;br /&gt;
&lt;br /&gt;
um folgendes:&lt;br /&gt;
* if (client.connect(&amp;quot;Arduino_1&amp;quot;, &amp;quot;zuHause/Arduino_1&amp;quot;, 0, true, &amp;quot;offline&amp;quot;)) { &lt;br /&gt;
** Arduino_1 nennt sich unser Sensor&lt;br /&gt;
** &amp;quot;zuHause/Arduino_1&amp;quot; ist das Topic für dessen Statusmeldungen&lt;br /&gt;
** 0 ist die QoS&lt;br /&gt;
** true bedeutet, Nachricht retained senden&lt;br /&gt;
** &amp;quot;offline&amp;quot; ist das, was auf dem Topic geschrieben werden soll, wenn die Verbindung abbricht&lt;br /&gt;
** Anmerkung:&lt;br /&gt;
** Unsauberes disconnect wird verursacht durch:&lt;br /&gt;
** Abbruch der Netzwerkverbindung&lt;br /&gt;
** Protokollfehler (z.B. ungültige Topics, fehlerhafte Payload)&lt;br /&gt;
&lt;br /&gt;
Warum machen wir das?&lt;br /&gt;
* beim Start des Sensors steht nun &amp;quot;online&amp;quot; im Status&lt;br /&gt;
* bei einem Sensorfehler werden wir auf den Sensorfehler aufmerksam gemacht &amp;quot;Sensorfehler&amp;quot;&lt;br /&gt;
* bricht die Netzwerkverbindung ab, steht automatisch &amp;quot;offline&amp;quot; in dem Topic&lt;br /&gt;
* bei einer erfolgreichen Messung mit Datenübertragung steht wieder &amp;quot;online&amp;quot; im Topic&lt;br /&gt;
Auf diese Art und Weise haben wir eine aussagekräftige Zustandsmeldung in unserem Topic. Diese können wir bequem mit FHEM überwachen, um bei einem Fehler weitere Maßnahmen einzuleiten. Ein Vergleich des Alters der Sensorwerte wird überflüssig.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== MQTT an sich ===&lt;br /&gt;
Wer keinen eigenen Broker hat und (für diesen einen Test) keinen aufsetzen will, kann testweise auch einen öffentlichen Broker benutzen: test.mosquitto.org stellt so einen bereit. Bitte beachten, dass dieser Broker wirklich öffentlich ist. Mit &#039;&#039;ping test.mosquitto.org&#039;&#039; bekommt man die IP Adresse, die man, ich möchte es nochmal betonen, zu Testzwecken, in seinen Sketch schreiben kann.&lt;br /&gt;
&lt;br /&gt;
Wir benötigen für MQTT entsprechende Topics.&lt;br /&gt;
* &amp;quot;zuHause/Arduino_1/Kueche/Kuehlschrank/Luftfeuchte&amp;quot;&lt;br /&gt;
* &amp;quot;zuHause/Arduino_1/Kueche/Kuehlschrank/Temperatur&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== Daus ergibt sich letztlich folgender Sketch ==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;arduino&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;DHT.h&amp;gt;&lt;br /&gt;
#include &amp;lt;SPI.h&amp;gt;&lt;br /&gt;
#include &amp;lt;Ethernet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;PubSubClient.h&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
#define DHTPIN 9     &lt;br /&gt;
#define DHTTYPE DHT22 //DHT11, DHT21, DHT22&lt;br /&gt;
 &lt;br /&gt;
DHT dht(DHTPIN, DHTTYPE);&lt;br /&gt;
&lt;br /&gt;
byte mac[]    = {  0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED }; //eine MAC Adresse wählen, darf im eigenen Netz nur 1x vorkommen&lt;br /&gt;
IPAddress ip(192, 168, 5, 220); //eine gültige IP Adresse für das eigene Netz&lt;br /&gt;
IPAddress server(192, 168, 5, 2); //die Adresse wo der eigene MQTT Broker drauf läuft&lt;br /&gt;
&lt;br /&gt;
void callback(char* topic, byte* payload, unsigned int length) {&lt;br /&gt;
  // handle message arrived&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
EthernetClient ethClient;&lt;br /&gt;
PubSubClient client(ethClient);&lt;br /&gt;
&lt;br /&gt;
long lastReconnectAttempt = 0;&lt;br /&gt;
&lt;br /&gt;
boolean reconnect() {&lt;br /&gt;
  if (client.connect(&amp;quot;Arduino_1&amp;quot;, &amp;quot;zuHause/Arduino_1&amp;quot;, 0, true, &amp;quot;offline&amp;quot;)) {&lt;br /&gt;
    // Once connected, publish an announcement...&lt;br /&gt;
    client.publish(&amp;quot;zuHause/Arduino_1&amp;quot;,&amp;quot;online&amp;quot;, true);&lt;br /&gt;
    // ... and resubscribe&lt;br /&gt;
    client.subscribe(&amp;quot;inTopic&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
  return client.connected();&lt;br /&gt;
}&lt;br /&gt;
static char humidity[15]; //Speicherbereich reservieren um die Fechtigkeit zu speichern&lt;br /&gt;
static char temperature[15];&lt;br /&gt;
float h = 0.0;&lt;br /&gt;
float h_alt = 0.0;&lt;br /&gt;
float t = 0.0;&lt;br /&gt;
float t_alt = 0.0;&lt;br /&gt;
unsigned long previousMillis = 0; //Zählervariable, zählt Millisekunden seit dem letzten Funktionsaufruf nach oben&lt;br /&gt;
const long interval = 120000; //120000 Millisekunden aka 120 Sekunden, das Interval wie oft der Sensor überhaupt benutzt wird&lt;br /&gt;
&lt;br /&gt;
void setup() &lt;br /&gt;
{&lt;br /&gt;
  client.setServer(server, 1883);&lt;br /&gt;
  client.setCallback(callback);&lt;br /&gt;
&lt;br /&gt;
  Ethernet.begin(mac, ip);&lt;br /&gt;
  delay(1500);&lt;br /&gt;
  lastReconnectAttempt = 0;&lt;br /&gt;
  &lt;br /&gt;
  Serial.begin(9600); &lt;br /&gt;
  Serial.println(&amp;quot;DHT22 - Test!&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
  dht.begin();&lt;br /&gt;
}&lt;br /&gt;
 &lt;br /&gt;
void loop() &lt;br /&gt;
{&lt;br /&gt;
  if (!client.connected()) {&lt;br /&gt;
    long now = millis();&lt;br /&gt;
    if (now - lastReconnectAttempt &amp;gt; 5000) {&lt;br /&gt;
      lastReconnectAttempt = now;&lt;br /&gt;
      // Attempt to reconnect&lt;br /&gt;
      if (reconnect()) {&lt;br /&gt;
        lastReconnectAttempt = 0;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  } else {&lt;br /&gt;
    // Client connected&lt;br /&gt;
&lt;br /&gt;
    client.loop();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
unsigned long currentMillis = millis();&lt;br /&gt;
&lt;br /&gt;
  if (currentMillis - previousMillis &amp;gt;= interval) {&lt;br /&gt;
    previousMillis = currentMillis;&lt;br /&gt;
    &lt;br /&gt;
    h = dht.readHumidity();     //Luftfeuchte auslesen&lt;br /&gt;
    t = dht.readTemperature();  //Temperatur auslesen&lt;br /&gt;
  }&lt;br /&gt;
    &lt;br /&gt;
  // Prüfen ob eine gültige Zahl zurückgegeben wird. Wenn NaN (not a number) zurückgegeben wird, dann Fehler ausgeben.&lt;br /&gt;
  if (isnan(t) || isnan(h)) &lt;br /&gt;
  {&lt;br /&gt;
    Serial.println(&amp;quot;DHT22 konnte nicht ausgelesen werden&amp;quot;);&lt;br /&gt;
    client.publish(&amp;quot;zuHause/Arduino_1&amp;quot;,&amp;quot;Sensorfehler&amp;quot;,true); //true sendet die Nachricht retained, d.h. die Nachricht bleibt solange auf dem Broker, bis etwas neues kommt&lt;br /&gt;
  }&lt;br /&gt;
  else if (h == h_alt &amp;amp;&amp;amp; t == t_alt)&lt;br /&gt;
  {&lt;br /&gt;
    //nix machen&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    client.publish(&amp;quot;zuHause/Arduino_1&amp;quot;,&amp;quot;online&amp;quot;, true);&lt;br /&gt;
    dtostrf(h,6, 1, humidity);&lt;br /&gt;
    dtostrf(t,6, 1, temperature);&lt;br /&gt;
    client.publish(&amp;quot;zuHause/Arduino_1/Kueche/Kuehlschrank/Luftfeuchte&amp;quot;,humidity, true);&lt;br /&gt;
    client.publish(&amp;quot;zuHause/Arduino_1/Kueche/Kuehlschrank/Temperatur&amp;quot;,temperature, true);&lt;br /&gt;
    h_alt = h; //den alten Messwert aufheben&lt;br /&gt;
    t_alt = t; //um nur bei Veränderung zu reagieren&lt;br /&gt;
    Serial.print(&amp;quot;Luftfeuchte: &amp;quot;); &lt;br /&gt;
    Serial.print(h);&lt;br /&gt;
    Serial.print(&amp;quot; %\t&amp;quot;);&lt;br /&gt;
    Serial.print(&amp;quot;Temperatur: &amp;quot;); &lt;br /&gt;
    Serial.print(t);&lt;br /&gt;
    Serial.println(&amp;quot; C&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;
== Zusammenfassung ==&lt;br /&gt;
Um aus einem &amp;quot;normalen&amp;quot; Sketch einen MQTT Sketch zu machen muss man also:&lt;br /&gt;
* Netzwerk konfigurieren&lt;br /&gt;
* MQTT einrichten&lt;br /&gt;
Im wesentlichen kann man einfach den MQTT Beispielsketch &amp;quot;mqtt reconnect nonblocking&amp;quot; in seinen Sketch einbauen.&lt;br /&gt;
&lt;br /&gt;
Geringe Änderungen im vorhandenen Code sind nötig, um diversen Typen von Variablen als MQTT Payload verwenden zu können&lt;br /&gt;
&lt;br /&gt;
Als Bonus bekommen wir &amp;quot;vernünftige&amp;quot; Statuswerte für unseren Sensor, die sich einfach in FHEM abfangen und auswerten lassen. Als weiteren Bonus betrachte ich, dass die Netzwerklast sinkt. Zum Vergleich möchte ich hier die Jeelink Sensoren anführen, aber auch viele 433 MHz Sensoren, die einfach im &amp;quot;wenige Sekunden Takt&amp;quot; ihre Werte in der Gegend rumfunken. Von da her kann durch den Einsatz von MQTT-Techniken die Funkbelastung der Umgebung reduziert werden. Einerseits trägt man also weniger zur &amp;quot;Sensorischen Luftverschmutzung&amp;quot; bei, andererseits entlastet man auch sein FHEM vor der Aufgabe, unnötige Werte erst mal zu verwerfen.&lt;br /&gt;
&lt;br /&gt;
== Danke ==&lt;br /&gt;
Hier möchte ich mal Danke sagen. An Beta-User, der seit Teil 1 das Geschreibsel vorab durchliest und immer wertvolle Tipps und Änderungsvorschläge parat hat.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
[http://fluuux.de/2012/10/arduino-temperatur-und-luftfeuchtigkeit-mit-dem-dht22-prufen/ Originales Projekt]&lt;br /&gt;
&lt;br /&gt;
[https://wiki.fhem.de/wiki/MQTT_Einf%C3%BChrung Teil 1 der MQTT Einführung]&lt;br /&gt;
&lt;br /&gt;
[https://wiki.fhem.de/wiki/MQTT_Einf%C3%BChrung_Teil_2 Teil 2 der MQTT Einführung]&lt;br /&gt;
&lt;br /&gt;
[https://forum.fhem.de/index.php/topic,69954.0.html Diskussionsthread im Forum]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=MQTT_Einf%C3%BChrung&amp;diff=20776</id>
		<title>MQTT Einführung</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=MQTT_Einf%C3%BChrung&amp;diff=20776"/>
		<updated>2017-03-18T21:14:40Z</updated>

		<summary type="html">&lt;p&gt;Eisler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
= MQTT =&lt;br /&gt;
&lt;br /&gt;
Message Queue Telemetry Transport&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Was ist das? ==&lt;br /&gt;
&lt;br /&gt;
Es handelt sich um ein Protokoll um Daten unter verschiedenen Geräten zu übermitteln. MQTT funktioniert z.B. über eine TCP/IP Verbindung. &lt;br /&gt;
(Für die, die etwas verbal posen wollen: Man nennt das M2M, Machine to Machine; im Übrigen handelt es sich bei MQTT um ein Transportprotokoll.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Grundlegender Aufbau ==&lt;br /&gt;
&lt;br /&gt;
Herzstück des eigenen MQTT Netzes ist ein Broker. Die mit dem Broker verbundenen Geräte nennen wir der Einfachheit halber mal Clients. (stimmt nicht ganz, aber später lernen wir das genauer kennen)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== So tickt MQTT ==&lt;br /&gt;
&lt;br /&gt;
Wir schreiben/schicken einfach eine Nachricht an den Broker. Wer wann die Nachricht abholt und liest, interessiert an dieser Stelle überhaupt nicht. Das ist einer der großen Vorteile.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ordnung in das Chaos ==&lt;br /&gt;
&lt;br /&gt;
Worin liegt nun der Vorteil? Und wie kann nun jemand eine Nachricht lesen?&lt;br /&gt;
Hier beginnt das raffinierte Konzept von MQTT:&lt;br /&gt;
Wir stellen nicht einfach nur Daten dem Broker zur Verfügung. Wir organisieren sie auch selbst. Das Zauberwort hierfür nennt sich &amp;quot;Topic&amp;quot;. &lt;br /&gt;
Kann man sich so ähnlich vorstellen wie einen Verzeichnisbaum unter Windows, oder wie eine Datei auf einem Webserver.&lt;br /&gt;
Ein anderes Bild für einen &amp;quot;Topic&amp;quot; wäre eine Art schwarzes Brett.&lt;br /&gt;
&lt;br /&gt;
Ein Topic könnte so lauten:&lt;br /&gt;
&#039;&#039;&#039;zuHause/1OG/Kueche/Licht/state&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Dieses Topic böte sich geradezu an, den Zustand des Küchenlichts im 1. Stock reinzuschreiben.&lt;br /&gt;
&lt;br /&gt;
Unser kleiner schlauer Lichtschalter könnte in diesem Topic immer den aktuellen Zustand des Lichts notieren. In &amp;quot;MQTT-Sprechweise&amp;quot; würde man sagen, unser Client ist ein Publisher. Er veröffentlicht Informationen.&lt;br /&gt;
&lt;br /&gt;
Nehmen wir im folgenden einen Sensor:&lt;br /&gt;
&#039;&#039;&#039;zuHause/1OG/Kueche/Kuehlschrank/Temperatur/state&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Das ergäbe ein hübsches Topic für einen im Kühlschrank befindlichen Temperatursensor.&lt;br /&gt;
Auch hier hätten wir es mit einem Publisher zu tun.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist hierbei, dass wir uns nicht um die Technik kümmern müssen. Auch bei der Organisation der Topics haben wir ziemlich freie Hand.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Was kann man jetzt damit anfangen? ==&lt;br /&gt;
&lt;br /&gt;
Nun wollen wir die Kühlschranktemperatur auf einem Display sehen. Vielleicht lagern wir ja empfindliche Medikamente im Kühlschrank und haben Kinder, die gerne die Kühlschranktür offen lassen?&lt;br /&gt;
&lt;br /&gt;
Wir nehmen also ein Display und klemmen das auch an unseren Broker. Jetzt möchten wir aber nicht die Temperatur publishen, das macht ja der Sensor. Wir wollen sie lesen. Also melden wir uns mit dem Display beim Broker an und &amp;quot;subscriben&amp;quot; das Topic. Wir teilen dem Broker mit, dass uns diese Information interessiert.&lt;br /&gt;
&lt;br /&gt;
Unser Display abonniert also die Temperatur. Zukünftig wird der Broker immer versuchen, unserem Display die Temperatur zu übermitteln.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Ah ja. Und was ist daran toll? ==&lt;br /&gt;
&lt;br /&gt;
Nun, wie wir das Thermometer programmiert haben, wussten wir nichts von dem Display. Wir müssen auch am Thermometer gar nichts mehr verändern. Das bleibt so wie es ist.&lt;br /&gt;
&lt;br /&gt;
Wenn wir nun ein weiteres Display haben wollen, bauen wir uns eben ein zweites Display und subscriben das Topic ebenfalls beim Broker.&lt;br /&gt;
Zukünftig wird er an beide Displays die Information schicken.&lt;br /&gt;
&lt;br /&gt;
Wenn wir eine grafische Auswertung des Temperaturverlaufs wollen, dann könnten wir das Topic beim Broker zusätzlich mit FHEM subscriben. Temperatur bekommen, in Logfile schreiben, Plot, fertig.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Immer noch: warum ist das so toll? ==&lt;br /&gt;
&lt;br /&gt;
Nun, bei der Konzeption von MQTT hat man Wert darauf gelegt, dass es ohne große Ressourcen auskommt. Das bedeutet für uns, dass es für viele &amp;quot;Bastelhardware&amp;quot; (Arduino etc.) fertige Libraries gibt. Ich zeige das später. Auch wer gerne mit Javascript, Java, Perl, Python programmiert, findet gut funktionierende Bibliotheken. &lt;br /&gt;
Für den &amp;quot;ambitionierten Bastler&amp;quot; bedeutet dies:&lt;br /&gt;
Wir können Geräte bauen und programmieren, die sich selbstständig miteinander unterhalten. Ein Taster der Licht an einem entfernten Aktor einschaltet, ist kein Problem. Und zwar ohne FHEM hiermit zu belasten oder es auch nur zu brauchen.&lt;br /&gt;
Wir können also sehr schnell unsere Kühlschranktemperatur mit diversen Programmiersprachen auf einer Unzahl von Geräten lesen und auswerten.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cool, was braucht man? ==&lt;br /&gt;
&lt;br /&gt;
Einen MQTT Broker; z.B. Mosquitto.&lt;br /&gt;
Für den Mikrocontroller der eigenen Wahl eine passende Library. Für Arduinos böte sich der PubSubClient an&lt;br /&gt;
Ggfs. ein Analyse-Tool wie MQTT.fx&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Nachteile ==&lt;br /&gt;
&lt;br /&gt;
=== Single Point of Failure ===&lt;br /&gt;
&lt;br /&gt;
Wo Licht ist, ist bekanntermaßen auch Schatten. Für uns, da wir FHEM zur Haussteuerung einsetzen, stellt sich die erste Frage, warum man seine Geräte nicht einfach gleich an FHEM anbindet, sondern erst noch einen MQTT Broker (letztlich noch ein Stück Software) extra davor setzt.&lt;br /&gt;
Möglicher Grund:&lt;br /&gt;
MQTT ist besser ausgereift als jedes selbst gestricke Protokoll. Es ist sogar ISO Standard. Es gibt viele Tools und Hilfestellungen dafür; eigene Intelligenz den Geräten beizubringen ist auch recht einfach.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Sicherheit ===&lt;br /&gt;
&lt;br /&gt;
Prinzipiell ist MQTT ebenso sicher wie eine Postkarte. Solange man es nicht extra absichert, kann jeder der im eigenen LAN ist (und die Adresse vom Broker kennt) alle Topics mitlesen.&lt;br /&gt;
meinHaus/Flur/Haustuer:open / close&lt;br /&gt;
Ist da nicht wirklich schlau! &lt;br /&gt;
Abhilfe:&lt;br /&gt;
==== Username / Passwort ====&lt;br /&gt;
Zunächst kann man erst mal einen Username / Passwort vergeben. Da ist zwar auch noch lange nicht sicher, aber zumindest steigert es den Aufwand schon mal. Jetzt muss man zumindest schon mal Pakete sniffen und verstehen, um unbefugt zu lesen oder gar zu publishen.&lt;br /&gt;
&lt;br /&gt;
==== TLS ====&lt;br /&gt;
Um wirklich sicher zu werden, führt kein Weg an TLS vorbei. Leider kann z.B. ein Arduino das schlicht nicht mehr. Irgendwo machen sich der Speicher und die Rechenleistung dann doch bemerkbar.&lt;br /&gt;
&lt;br /&gt;
=== Änderung der Topic Hierarchie ===&lt;br /&gt;
Ja, das ist ein Problem. Wenn man die Hierarchie der Topics ändern will, wird man in den seltensten Fällen drum rum kommen, seinen Programmcode (Arduino..,) anzupassen, neu zu kompilieren und wieder hoch zu laden.&lt;br /&gt;
Gleiches gilt, wenn sich z.B. die IP Adresse des Brokers ändert.&lt;br /&gt;
&lt;br /&gt;
=== Netzwerktraffic ===&lt;br /&gt;
Eine weitere Frage, die man sich stellen kann ist eine grundsätzliche:&lt;br /&gt;
Welchen Sinn hat es, sein Netzwerk (insbesondere WLAN) mit so Dingen wie Sensordaten zuzukleistern. Das ist nicht völlig unberechtigt. MySensors ist für viele kleine batteriebetriebenen Sensoren vermutlich eine deutlich bessere Wahl. Sensoren mit Akku in ein WLAN einzubinden, ist technisch eine echte Herausforderung. De große Vorteil ist aber, dass man sein WLAN im Fall des Reichweitenendes einfach erweitern kann. WLAN-Repeater, Sache gut.&lt;br /&gt;
&lt;br /&gt;
== Ausblick ==&lt;br /&gt;
Hier endet erst mal der kurze Überblick. Wer neugierig geworden ist, darf sich auf viele weitere spannenden Themen freuen:&lt;br /&gt;
&lt;br /&gt;
=== QoS ===&lt;br /&gt;
Ein weiteres Feature sind die QoS Level von MQTT.&lt;br /&gt;
 * QoS Level 0: ist eine Art fire and forget. Ob die Daten ankommen, wird nicht weiter geprüft&lt;br /&gt;
 * QoS Level 1: garantiert die Zustellung der Nachricht; es kann aber auch passieren, dass eine Nachricht öfter zugestellt und empfangen wird&lt;br /&gt;
 * QoS Level 3: Hier wird garantiert, dass jede Nachricht &#039;&#039;&#039;genau 1x&#039;&#039;&#039; bei den Empfängern ankommt&lt;br /&gt;
&lt;br /&gt;
=== Retained Messages ===&lt;br /&gt;
Auch eine lustige Sache, lernen wir später kennen. Aber ein Beispiel möchte ich dennoch schon schildern: Wir basteln uns später den oben angesprochenen Temperatursensor und das zugehörige Display. Das erweitern wir um eine blinkende LED, wenn eine bestimmte Temperatur überschritten wird. Quizfrage: woher kennt das Display den gewünschten Schwellenwert? Freilich könnte man ihn im Programmcode hinterlegen. Aber dann ist er fest verdrahtet. Wenn wir ihn einfach nur normal publishen würden, würde das Display ihn erst kennen, wenn der Schwellwert gepublished würde nachdem das Display mit dem Broker verbunden war... Hier kommen retained Messages. Diese werden auch beim Verbinden eines Subscribers gesendet, obwohl es da keine Änderung gab.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
* [http://mqtt.org Offizielle Homepage von MQTT, englisch]&lt;br /&gt;
* [http://www.hivemq.com/blog/mqtt-essentials-part-1-introducing-mqtt Sehr gute Einführung, englisch, sind 5 lesenswerte Teile]&lt;br /&gt;
* [https://www.heise.de/developer/artikel/MQTT-Protokoll-fuer-das-Internet-der-Dinge-2168152.html Ein Exkurs von Heise mit Beispielen, deusch, sehr lesenswert]&lt;br /&gt;
* [https://forum.fhem.de/index.php/topic,69230.0.html Diskussionsthread im Forum]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Alexa-Fhem&amp;diff=19177</id>
		<title>Alexa-Fhem</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Alexa-Fhem&amp;diff=19177"/>
		<updated>2017-01-28T19:28:43Z</updated>

		<summary type="html">&lt;p&gt;Eisler: /* Arbeitsweise und Datenfluss */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;Alexa-Fhem&#039;&#039;&#039; ist eine in JavaScript und auf NodeJS basierende Software, welche es ermöglicht, der digitalen Amazon Assistentin Alexa zusätzliche Skills für die Heimautomatisierung via FHEM beizubringen. Eine erste funktionierende Version wurde von [https://forum.fhem.de/index.php?action=profile;u=430 justme1968] im [https://forum.fhem.de/index.php/topic,60244.0.html Forum] veröffentlicht.&lt;br /&gt;
Das ist eine erste Version der Dokumentation zur Installation und Einrichtung, eine Erweiterung wird sicherlich in nächster Zeit noch folgen.&lt;br /&gt;
{{Infobox Modul&lt;br /&gt;
|ModPurpose=Anbindung von FHEM an Amazon Assistent Alexa&lt;br /&gt;
|ModType=x&lt;br /&gt;
|ModTechName=&lt;br /&gt;
|ModForumArea=Sonstige Systeme&lt;br /&gt;
|ModOwner=justme1968&lt;br /&gt;
}} &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Einführung==&lt;br /&gt;
&lt;br /&gt;
===Arbeitsweise und Datenfluss===&lt;br /&gt;
[[Datei:2gpXyLN.jpg|200px|thumb|right|Grafische Darstellung der beteiligten Komponenten]]&lt;br /&gt;
Echo -&amp;gt; AVS -&amp;gt; AWS Lambda -&amp;gt; alexa-fhem -&amp;gt; AWS Lambda -&amp;gt; AVS -&amp;gt; Echo&lt;br /&gt;
 &lt;br /&gt;
*Der Echo (oder ein anderes Alexa/AVS fähiges device)  nimmt Audiodaten auf und schickt diese an AVS  (Amazon Voice Service) zur Erkennung&lt;br /&gt;
*AVS führt die Spracherkennung durch und erzeugt ein Event mit Informationen zu den Erkannten Daten&lt;br /&gt;
:*Beim Custom Skill kommen die dazu nötgen Informationen aus dem &#039;&#039;Interaction Model&#039;&#039; der Alexa Skills Konfiguration&lt;br /&gt;
:*Beim Home Automation Skill sind die möglichen Sätze fest vorgegeben &lt;br /&gt;
*Das Event wird an den unter &#039;&#039;Configruation&#039;&#039; in der Alexa Skills Konfiguration hinterlegten Endpoint geschickt&lt;br /&gt;
:*Beim Home Automation Skill ist das zwingend eine AWS Lambda Routine&lt;br /&gt;
:*Beim Custom Skill kann das im Prinzip auch ein eigener Web Service sein&lt;br /&gt;
*Das Event wird vom &amp;lt;code&amp;gt;lambda.js&amp;lt;/code&amp;gt; code an alexa-fhem weitergeleitet&lt;br /&gt;
*alexa-fhem steuert FHEM und sendet ein Antwort Event zurück&lt;br /&gt;
*&amp;lt;code&amp;gt;lambda.js&amp;lt;/code&amp;gt; nimmt diese Antwort entgegen und gibt sie an AVS zurück&lt;br /&gt;
*AVS sogt dafür das der echo &#039;antwortet&#039; und das die Card in der Alexa App erscheint&lt;br /&gt;
&lt;br /&gt;
===Anmerkungen===&lt;br /&gt;
*Ein Skill hat keinen zugriff auf die Audiodaten&lt;br /&gt;
*Mit dem Skill API kann ein Skill zu zu keiner Zeit von sich aus aktiv werden und &#039;einfach&#039; Daten an den Echo schicken oder ihn dazu bringen irgendetwas zu tun.&lt;br /&gt;
*Es ist ein kleines Wunder wie schnell die Reaktion auf einen gesprochenen Satz erfolgt wenn man berücksichtigt welchen Weg die Daten insgesamt gehen.&lt;br /&gt;
&lt;br /&gt;
=== Abgrenzung des &#039;&#039;&#039;Alexa Smart Home Skills&#039;&#039;&#039; und des &#039;&#039;&#039;Alexa Custom Skills&#039;&#039;&#039; ===&lt;br /&gt;
&lt;br /&gt;
Der [[Alexa-Fhem#Smart_Home|Alexa Smart Home Skill]] ist ein Amazon-Alexa-Standard-Skill, der wesentliche Basisfunktionalitäten bereitstellt. Zu diesen gehört im Wesentlichen die Funktionalität, durch Alexa-FHEM bereitgestellte Devices im Alexa-Account des Benutzers anzulegen. Der Alexa Smart Home Skill reagiert auf gesprochene Interaktion in einem beschränkten Umfang. Beispielsweise genügt ein &amp;quot;Alexa, schalte die Wohnzimmerlampe an&amp;quot; um eine Interaktion zwischen Alexa Smart Home Skill und FHEM-Alexa auszulösen. Nach erfolgreicher Einrichtung wird dieser Skill in der Alexa-App bzw. im Web in der Rubrik &amp;quot;Smart Home&amp;quot; als Skill angezeigt.&lt;br /&gt;
&lt;br /&gt;
Der [[Alexa-Fhem#Custom|Alexa Custom Skill]] ist kein Standard-Smart-Home-Skill, sondern ein individuell entwickelter Skill, so wie alle anderen Skills auch. Er wird daher auch nicht in der Alexa-App unter der Rubrik &amp;quot;Smart Home&amp;quot; angezeigt. Gesprochene Interaktion mit diesem Skill erfolgt dadurch, dass entweder der Skill explizit gestartet wird (z.B. &amp;quot;Alexa, starte [Name des Skills]&amp;quot;) oder direkt angesprochen wird (z.B. &amp;quot;Alexa, frage [Name des Skills] wie ist der Status von [Device] &amp;quot;). Der Alexa Custom Skill befindet sich in Entwicklung und wird hinsichtlich seiner Funktionalitäten laufend weiterentwickelt. Die Einrichtung dieses Skills ist grundsätzlich optional, jedoch werden anspruchsvollere Steuerungsmöglichkeiten nur mit diesem realisiert werden können.&lt;br /&gt;
&lt;br /&gt;
==Installation==&lt;br /&gt;
&lt;br /&gt;
Grundvoraussetzung für alle folgenden Schritte ist, dass ein Amazon-Account vorhanden ist. Es wird davon ausgegangen, dass bereits für alle folgenden Amazon-Dienste vorab das Konto jeweils eingerichtet wurde, die Einrichtung dieser ist nicht Bestandteil dieser Anleitungen.&lt;br /&gt;
&lt;br /&gt;
Es sei an dieser Stelle darauf hingewiesen, dass für die Nutzung der Amazon AWS-Dienste zwingend die Daten einer Kreditkarte angegeben werden müssen. Nach gegenwärtigem Kenntnisstand sollen jedoch keine Kosten für die Nutzung der im Rahmen dieses How To beschriebenen Dienste anfallen, sofern diese in einem Rahmen genutzt werden, der selbst eine intensive private Nutzung nicht überschreitet. Der Benutzer sei an dieser Stelle auf die von Amazon veröffentlichten Preislisten verwiesen.&lt;br /&gt;
&lt;br /&gt;
{{Randnotiz|RNTyp=[g|Info]|RNText=Da die einzelnen Schritte der Anleitung an verschiedenen Stellen unterbrochen und später fortgesetzt werden müssen, empfiehlt es sich, die Anleitung einmal vollständig gelesen zu haben. Dies vermeidet Fehler, die ansonsten geschehen können. Während der Konfiguration bietet es sich an diese Schritte dann parallel in gleichzeitig geöffneten Browserfenster durchzuführen. }}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Voraussetzungen ===&lt;br /&gt;
Folgende grundlegende Voraussetzungen sollten erfüllt sein, bevor du mit der Einrichtung beginnst:&lt;br /&gt;
* Amazon Echo oder Amazon Dot&lt;br /&gt;
* node.js (vermutlich ab Version 0.12.7, getestet mit 4.2.6. Kann in Debian z.B. mit &amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;sudo apt-get install nodejs-legacy&amp;lt;/source&amp;gt; installiert werden)&lt;br /&gt;
* Weiterleitung von Port 3000 vom Router aus auf den Rechner, auf dem Alexa-Fhem läuft&lt;br /&gt;
* Es empfiehlt sich ein alexa Gerät in FHEM anzulegen.&lt;br /&gt;
&lt;br /&gt;
=== Alexa-Fhem installieren ===&lt;br /&gt;
# Die tgz-Datei unter Linux entpacken (Windows macht die Rechte kaputt!) (Quelle: https://forum.fhem.de/index.php/topic,60244.0.html) (neuere Version verügbar, bitte Hinweise unter Custom Skill beachten:https://forum.fhem.de/index.php/topic,60244.msg540117.html#msg540117) &amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;tar -xzf alexa-fhem-0.1.3.tgz&amp;lt;/source&amp;gt;&lt;br /&gt;
# Verzeichnis &#039;&#039;package&#039;&#039; in &#039;&#039;alexa-fhem&#039;&#039; umbenennen &amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;mv package alexa-fhem&amp;lt;/source&amp;gt;&lt;br /&gt;
# Durch &amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;cd alexa-fhem&amp;lt;/source&amp;gt; in das Verzeichnis wechseln&lt;br /&gt;
# Mit (kein sudo!) &amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;npm install&amp;lt;/source&amp;gt; alle Abhängigkeiten installieren&lt;br /&gt;
# SSL Zertifikat erzeugen durch Aufruf von (kein sudo!)&amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;./createKey.sh&amp;lt;/source&amp;gt;. Hierbei beachten, dass ein Kennwort vergeben werden soll, das mindestens aus 4 Zeichen besteht.&lt;br /&gt;
# Die Datei &#039;&#039;config-sample.json&#039;&#039; nach &#039;&#039;~/.alexa/config.json&#039;&#039; kopieren (~/.alexa gegebenenfalls vorher anlegen) und die Werte auf die eigene Umgebung hin anpassen. Der Filter funktioniert hierbei wie bei [[Homebridge_einrichten#Einstellungen_f.C3.BCr_homebridge|homebridge-fhem]],die folgenden Zeilen sind anzupassen oder zu löschen:&amp;lt;br&amp;gt;&amp;quot;nat-pmp&amp;quot;: -&amp;gt; wenn nat-pmp verwendet werden soll: die ip des eigenen routers,&amp;lt;br&amp;gt;&amp;quot;nat-upnp&amp;quot;: -&amp;gt; wenn nat-upnp verwendet werden soll: &#039;&#039;true&#039;&#039;,&amp;lt;br&amp;gt;&amp;quot;applicationId&amp;quot;: -&amp;gt; SkillID aus &amp;quot;Alexa Custom Skill anlegen&amp;quot; Punkt 7 (s.u.),&amp;lt;br&amp;gt;&amp;quot;oauthClientID&amp;quot;: -&amp;gt; &#039;&#039;Client Id&#039;&#039; aus Punkt 1. &#039;&#039;&#039;Login with Amazon&#039;&#039;&#039;&lt;br /&gt;
# Durch Aufruf von &amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;./bin/alexa&amp;lt;/source&amp;gt; den Dienst starten (kein sudo!)&lt;br /&gt;
&lt;br /&gt;
Der Start des Alexa-Dienstes auf der Console ist immer dann zu empfehlen, wenn man auf die Ausgaben des Dienstes angewiesen ist und beispielsweise sehen möchte, welche Devices durch den Dienst bereitgestellt werden oder ob Fehler auftreten. Beendet man die Console-Session wird auch der Dienst wieder beendet. Insofern ist die vorgenannte Vorgehensweise nur für ein Debugging zu empfehlen und nicht im Regelbetrieb. Nachfolgend ist beschrieben, wie man den Alexa-Dienst aus FHEM heraus starten / stoppen und neu starten kann.&lt;br /&gt;
&lt;br /&gt;
=== Alexa-Fhem aus FHEM heraus starten ===&lt;br /&gt;
Zunächst das Start-up-Skript aus diesem Post herunterladen [https://forum.fhem.de/index.php/topic,60244.msg517271.html#msg517271 https://forum.fhem.de/index.php/topic,60244.msg517271.html#msg517271] und unter /etc/init.d/alexa speichern.&lt;br /&gt;
&lt;br /&gt;
Das Script geht davon aus, das der alexa-fhem script unter /opt/fhem/alexa-fhem liegt, und die logfiles später unter /opt/fhem/log. Sollte das nicht der Fall sein, muss das Skript angepasst werden.&lt;br /&gt;
&lt;br /&gt;
Nun folgende Kommandos ausführen:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;sudo chmod 755 /etc/init.d/alexa&lt;br /&gt;
sudo update-rc.d alexa defaults&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der Datei &amp;lt;code&amp;gt;/etc/sudoers&amp;lt;/code&amp;gt; den User fhem für die Nutzung von sudo zulassen (&amp;lt;code&amp;gt;sudo nano /etc/sudoers&amp;lt;/code&amp;gt;), z.B. durch Anfügen der nachfolgenden Zeile:&lt;br /&gt;
&amp;lt;code&amp;gt;fhem ALL=(ALL) NOPASSWD: ALL&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nun folgende Devices anlegen (ggf. einem Raum zuordnen, z.B. AlexaControl):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:75%;&amp;quot;&amp;gt;define FHEM.Alexa.Status dummy&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:75%;&amp;quot;&amp;gt;define FHEM.Alexa dummy&lt;br /&gt;
attr FHEM.Alexa event-on-change-reading state&lt;br /&gt;
attr FHEM.Alexa webCmd status:start:stop:restart&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:75%;&amp;quot;&amp;gt;define FHEM.Alexa.DOIF DOIF ([FHEM.Alexa] eq &amp;quot;start&amp;quot;) &lt;br /&gt;
(set FHEM.Alexa on, {system (&amp;quot;sudo /etc/init.d/alexa start &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;quot;)}) &lt;br /&gt;
DOELSEIF ([FHEM.Alexa] eq &amp;quot;stop&amp;quot;) &lt;br /&gt;
(set FHEM.Alexa on, {system (&amp;quot;sudo /etc/init.d/alexa stop &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;quot;)}) &lt;br /&gt;
DOELSEIF ([FHEM.Alexa] eq &amp;quot;restart&amp;quot;) &lt;br /&gt;
(set FHEM.Alexa on, {system (&amp;quot;sudo /etc/init.d/alexa restart &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;quot;)}) &lt;br /&gt;
DOELSEIF ([FHEM.Alexa] eq &amp;quot;status&amp;quot;) &lt;br /&gt;
(set FHEM.Alexa on, {system (&amp;quot;sudo /etc/init.d/alexa status &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&amp;quot;)})&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Alexa Device mit dem Modul 39_alexa.pm anlegen ===&lt;br /&gt;
&lt;br /&gt;
Das Modul 39_alexa.pm stellt innerhalb von FHEM verschiedene Attribute z.B. alexaName oder alexaRoom zur Verfügung. Manche dieser Attribute (wie z.b. alexaName) werden in beiden Skills verwendet, andere werden ausschließlich bei einer Nutzung des Alexa Custom Skill verwendet.&lt;br /&gt;
&lt;br /&gt;
Die Einrichtung des Alexa Device geschieht durch die nachfolgende Definition:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:70%;&amp;quot;&amp;gt;define MyAlexa alexa&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Alexa Skills anlegen ===&lt;br /&gt;
&lt;br /&gt;
==== Smart Home ====&lt;br /&gt;
Für folgende Schritte muss man unter der Adresse http://developer.amazon.com angemeldet sein&lt;br /&gt;
# Anmeldung auswählen&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-01-login2.png|200px]]&lt;br /&gt;
# Anmeldedaten eingeben&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-02-userpass2.png|200px]]&lt;br /&gt;
&lt;br /&gt;
===== Apps &amp;amp; Services =====&lt;br /&gt;
# Klicke nach der Anmeldung auf &#039;&#039;APPS &amp;amp; SERVICES&#039;&#039;&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-03-apps_and_services.png|200px]]&lt;br /&gt;
# Klicke anschließend auf &#039;&#039;Security Profiles&#039;&#039;&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-05-apps_and_services_-_security_profiles.png|200px]]&lt;br /&gt;
# Wähle anschließend &#039;&#039;Create a New Security Profile&#039;&#039; aus&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-06-apps_and_services_-_create_a_new_security_profile.png|200px]]&lt;br /&gt;
# Gebe dann ein Namen und eine Beschreibung für das Profil ein und bestätige die Eingaben durch anklicken von &#039;&#039;Save&#039;&#039;&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-07-apps_and_services_-_security_profile_management.png|200px]]&lt;br /&gt;
&lt;br /&gt;
===== Login with Amazon =====&lt;br /&gt;
{{Randnotiz|RNTyp=[g|Info]|RNText=Hier wird beschrieben, wo &#039;&#039;Client Id&#039;&#039; und &#039;&#039;Client Secret&#039;&#039; zu finden ist}}&lt;br /&gt;
# Nachdem du die vorherigen Schritte befolgt hast, siehst du im Browser das Profil dass du angelegt hast. Klicke jetzt oben rechts auf &#039;&#039;Login with Amazon&#039;&#039;&amp;lt;br/&amp;gt;[[Datei:Developer.amazon.com-08-login_with_amazon.png|200px]]&lt;br /&gt;
# Auf der neu geladenen Seite klickst du dann auf &#039;&#039;Sign up&#039;&#039;&amp;lt;br/&amp;gt;[[Datei:Developer.amazon.com-09-login_with_amazon_-_sign_up.png|200px]]&lt;br /&gt;
# Wähle anschließend im Dropdown Menü das vorher angelegte Profil aus und bestätige das anschließend mit &#039;&#039;Confirm&#039;&#039;&amp;lt;br/&amp;gt;[[Datei:Developer.amazon.com-10-login_with_amazon_-_create_new_profile.png|200px]] [[Datei:Developer.amazon.com-11-login_with_amazon_-_create_new_profile2.png|200px]]&lt;br /&gt;
# Im folgenden Fenster gibst du dann die Adresse [https://www.amazon.com/gp/help/customer/display.html?nodeId=468496 https://www.amazon.com/gp/help/customer/display.html?nodeId=468496] ein und bestätigst die Eingabe mit &#039;&#039;Save&#039;&#039;&amp;lt;br/&amp;gt;[[Datei:Developer.amazon.com-12-login_with_amazon_-_enter_consent_screen_information.png|200px]]&lt;br /&gt;
# Klicke dann bei dem neu angelegten Eintrag auf der rechten Seite auf das Zahnrad und wähle &#039;&#039;Web Settings&#039;&#039; aus&amp;lt;br/&amp;gt;[[Datei:Developer.amazon.com-13-login_with_amazon_-_web_settings.png|200px]]&lt;br /&gt;
# Im neu geladenen Fenster klickst du dann auf &#039;&#039;Edit&#039;&#039;&amp;lt;br/&amp;gt;[[Datei:Developer.amazon.com-14-login_with_amazon_-_edit.png|200px]]&lt;br /&gt;
# Füge zuletzt dann bei &#039;&#039;Allowed Return URLs&#039;&#039; die Adresse [https://layla.amazon.co.uk/api/skill/link/xxx https://layla.amazon.co.uk/api/skill/link/xxx], [https://pitangui.amazon.com/api/skill/link/xxx https://pitangui.amazon.com/api/skill/link/xxx], und [https://layla.amazon.com/api/skill/link/xxx https://layla.amazon.com/api/skill/link/xxx] hinzu. xxx muss hierbei durch den Wert ersetzt werden, der bei Punkt 6 &#039;&#039;&#039;Skill Kit einrichten&#039;&#039;&#039; unter &#039;&#039;&#039;Redirect Urls&#039;&#039;&#039; am Ende der URLs angezeigt wird.&amp;lt;br/&amp;gt;[[Datei:Developer.amazon.com-15-login_with_amazon_-_allowed_return_urls.png|200px]]&lt;br /&gt;
&lt;br /&gt;
===== Skill Kit einrichten =====&lt;br /&gt;
# Wähle im Menü den Punkt &#039;&#039;Alexa&#039;&#039; aus&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-03-apps_and_services.png|200px]]&lt;br /&gt;
# Klicke dann &#039;&#039;Get started&#039;&#039; beim Punkt &#039;&#039;Alexa Skills Kit&#039;&#039; an&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-17-alexa_-_alex_skills_kit_-_get_started.png|200px]]&lt;br /&gt;
# Wähle dann auf der neu geladenen Seite oben rechts &#039;&#039;Add a New Skill&#039;&#039; aus&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-18-alexa_-_alex_skills_kit_-_add_a_new_skill.png|200px]]&lt;br /&gt;
# Auf der folgenden Seite wählst du &#039;&#039;Smart Home Skill API&#039;&#039; sowie &#039;&#039;German&#039;&#039; als Sprache aus und gibst ein Name für den Skill ein was du dann mit &#039;&#039;Next&#039;&#039; bestätigst.&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-19-alexa_-_alex_skills_kit_-_skill_information.png|200px]]&lt;br /&gt;
# Die folgende Seite klickst du mit &#039;&#039;Next&#039;&#039; dann weiter&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-20-alexa_-_alex_skills_kit_-_interaction_model.png|200px]]&lt;br /&gt;
# Auf der Seite Configuration gibst du dann unter &#039;&#039;&#039;Authorization URL&#039;&#039;&#039; die Adresse: &amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:70%;&amp;quot;&amp;gt;https://www.amazon.com/ap/oa&amp;lt;/source&amp;gt;an, bei &#039;&#039;&#039;Scope&#039;&#039;&#039;: &amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:70%;&amp;quot;&amp;gt;profile:user_id&amp;lt;/source&amp;gt; und bei &#039;&#039;&#039;Access Token URI&#039;&#039;&#039;: &amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:70%;&amp;quot;&amp;gt;https://api.amazon.com/auth/o2/token&amp;lt;/source&amp;gt;sowie bei &#039;&#039;&#039;Privacy Policy URL&#039;&#039;&#039; die Adresse: &amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:70%;&amp;quot;&amp;gt;https://www.amazon.com/gp/help/customer/display.html?nodeId=468496&amp;lt;/source&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;Folgende Felder müssen individuell ausgefüllt werden:&amp;lt;br /&amp;gt;* &#039;&#039;Service Endpoint Type&#039;&#039; -&amp;gt; Europe auswählen und im Textfeld den Wert aus Punkt 12 &#039;&#039;&#039;AWS Lambda Funktion anlegen&#039;&#039;&#039; eintragen&amp;lt;br /&amp;gt;* &#039;&#039;Client Id&#039;&#039; -&amp;gt; &#039;&#039;Client Id&#039;&#039; aus Punkt 1. &#039;&#039;&#039;Login with Amazon&#039;&#039;&#039;&amp;lt;br /&amp;gt;* &#039;&#039;Client Secret&#039;&#039; -&amp;gt; &#039;&#039;Client Secret&#039;&#039; aus Punkt 1. &#039;&#039;&#039;Login with Amazon&#039;&#039;&#039;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-21-alexa_-_alex_skills_kit_-_configuration.png|200px]]&lt;br /&gt;
# &amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-22-alexa_-_alex_skills_kit_-_test.png|200px]]&lt;br /&gt;
&lt;br /&gt;
{{Randnotiz|RNTyp=[g|Info]|RNText=Hier wird beschrieben, wo die &#039;&#039;Alexa Skill Id&#039;&#039; zu finden ist}}&lt;br /&gt;
Zur späteren Verwendung unter Punkt 7 &#039;&#039;&#039;AWS Lambda Funktion anlegen&#039;&#039;&#039; wird noch die Application Id benötigt. An diese kommt man wie folgt:&lt;br /&gt;
# Wähle im Menü den Punkt &#039;&#039;Alexa&#039;&#039; aus&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-03-apps_and_services.png|200px]]&lt;br /&gt;
# Klicke dann &#039;&#039;Get started&#039;&#039; beim Punkt &#039;&#039;Alexa Skills Kit&#039;&#039; an&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-17-alexa_-_alex_skills_kit_-_get_started.png|200px]]&lt;br /&gt;
# Beim vorher angelegten Eintrag auf &#039;&#039;Edit&#039;&#039; klicken&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-23-alexa_-_alex_skills_kit_-_overview.png|200px]]&lt;br /&gt;
# Die Id, die nun oben angezeigt wird, ist die gesuchte &amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-06-configure_triggers2.png|200px]]&lt;br /&gt;
&lt;br /&gt;
==== Custom ====&lt;br /&gt;
&#039;&#039;&#039;ACHTUNG:&#039;&#039;&#039; die daten unter &#039;interaction model&#039; unten gelten nur für skill version 0.1.3. ab version 0.1.4 werden die daten im alexa device erzeugt. wie das geht steht hier   {{Link2Forum|Topic=60244|Message=540117|LinkText=im Forum}}.&lt;br /&gt;
&lt;br /&gt;
Für folgende Schritte muss man unter der Adresse http://developer.amazon.com angemeldet sein{{Randnotiz|RNTyp=[g|Info]|RNText=Bei Punkt 6 wird die Skill ID angezeigt (siehe Screenshot), die später als zusätzlicher Trigger für AWS Lambda verwendet wird. Bitte per Copy / Paste speichern.}}&lt;br /&gt;
# Anmeldung auswählen&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-01-login2.png|200px]]&lt;br /&gt;
# Anmeldedaten eingeben&amp;lt;br /&amp;gt;[[Datei:Developer.amazon.com-02-userpass2.png|200px]]&lt;br /&gt;
# Wähle im Menü den Punkt &#039;&#039;Alexa&#039;&#039; aus und klicke dann &#039;&#039;Get started&#039;&#039; beim Punkt &#039;&#039;Alexa Skills Kit&#039;&#039;&amp;lt;br /&amp;gt;[[Datei:CustomSkill_1.PNG|400px]]&lt;br /&gt;
# Klicke dann oben rechts auf &#039;&#039;Add a new skill&#039;&#039;&lt;br /&gt;
# Auf der Seite &#039;&#039;&#039;Create a new Alexa Skill&#039;&#039;&#039; werden folgende Optionen gewählt / Informationen eingetragen:&lt;br /&gt;
#:* Skill Type = Custom Interaction Model&lt;br /&gt;
#:* Language = German&lt;br /&gt;
#:* Name: Hier dem Skill einen beliebigen Namen geben. Dieser wird in der Alexa App unter &amp;quot;Meine Skills&amp;quot; angezeigt.&lt;br /&gt;
#:* Invocation Name: Name des Skills, unter dem dieser später gestartet wird. Z.B. &amp;quot;Alexa, starte James&amp;quot;&amp;lt;br /&amp;gt;[[Datei:CustomSkill_2.PNG|400px]]&lt;br /&gt;
# Mit &#039;&#039;Next&#039;&#039; die Seite verlassen.&lt;br /&gt;
# Auf der Seite &#039;&#039;&#039;Interaction Model&#039;&#039;&#039; folgende Eingaben tätigen:&lt;br /&gt;
#* In die Box &#039;&#039;&#039;Intent Schema&#039;&#039;&#039; den Programmcode aus folgendem Post einfügen: [https://forum.fhem.de/index.php/topic,60244.msg528230.html#msg528230 https://forum.fhem.de/index.php/topic,60244.msg528230.html#msg528230]&amp;lt;br /&amp;gt;[[Datei:CustomSkill_3.PNG|400px]]&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
#* Nun auf &#039;&#039;Add Slot Type&#039;&#039; klicken und die Custom Slot Types (siehe [https://forum.fhem.de/index.php/topic,60244.msg528230.html#msg528230 https://forum.fhem.de/index.php/topic,60244.msg528230.html#msg528230]) einrichten. Bitte beachten, dass das Pipeline-Zeichen einen Zeilenumbruch darstellen soll. Im Screenshot ist sichtbar, wie das aussehen muss.&amp;lt;br /&amp;gt; Für FHEM_DEVICE und FHEM_ROOM bitte die eigenen in FHEM definierten Devices und Räume benennen (siehe oben alexaName und alexaRoom).&amp;lt;br /&amp;gt;[[Datei:CustomSkill_4.PNG|400px]]&amp;lt;br /&amp;gt;Der Screenshot zeigt die Einrichtung für FHEM_DEVICE. Nach der Einrichtung von FHEM_DEVICE &#039;&#039;SAVE&#039;&#039; klicken und die weiteren Slot Types (FHEM_ROOM, FHEM_SWITCH_ACTION und FHEM_artikel) jeweils durch klicken auf &#039;&#039;Add Slot Type&#039;&#039; einrichten.&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
#* Unter Sample Utterances bitte den Text aus [https://forum.fhem.de/index.php/topic,60244.msg528230.html#msg528230 https://forum.fhem.de/index.php/topic,60244.msg528230.html#msg528230] reinkopieren&amp;lt;br /&amp;gt;[[Datei:CustomSkill_5.PNG|400px]]&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
#* Eingaben mit &#039;&#039;Next&#039;&#039; abschließen.&lt;br /&gt;
# Auf der Folgeseite &#039;&#039;Configuration&#039;&#039; bitte folgende Angaben machen:&lt;br /&gt;
#* Service Endpoint Type = AWS Lambda&lt;br /&gt;
#* Geographical Region = Europe&lt;br /&gt;
#* Im Feld &#039;&#039;Europe&#039;&#039; wird die ARN eingetragen, die man erst in nachfolgenden Abschnitt erhält! Es muss hier also erst mal pausiert werden! Den Browser Tab geöffnet lassen!&lt;br /&gt;
#* Sobald man die ARN hat geht es hier weiter...&lt;br /&gt;
#* Do you allow users to create an account or link to an existing account with you? = Yes&lt;br /&gt;
#* Authorization URL = https://www.amazon.com/ap/oa&lt;br /&gt;
#* Client ID = Client ID von oben aus &#039;&#039;&#039;Login with Amazon&#039;&#039;&#039;&lt;br /&gt;
#* Domain List - nichts eingeben&lt;br /&gt;
#* Scope = profile:user_id&lt;br /&gt;
#* Redirect URLs - sollten vorbelegt sein&lt;br /&gt;
#* Authorization Grant Type - Auth Code Grant auswählen&lt;br /&gt;
#* Access Token URI = https://api.amazon.com/auth/o2/token&lt;br /&gt;
#* Client Secret = siehe oben bei &#039;&#039;&#039;Login with Amazon&#039;&#039;&#039;&lt;br /&gt;
#* Client Authentication Scheme = HTTP Basic&lt;br /&gt;
#* Privacy Policy URL = https://www.amazon.com/gp/help/customer/display.html?nodeId=468496&lt;br /&gt;
#* Das sollte jetzt alles wie folgt aussehen:&amp;lt;br /&amp;gt;[[Datei:CustomSkill_6.PNG|400px]]&amp;lt;br /&amp;gt;[[Datei:CustomSkill_7.PNG|400px]]&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit ist die Einrichtung des Custom Skill abgeschlossen. Auf der Folgeseite lässt sich der Skill dann testen. In der Alexa App sollte der Skill nun auch sichtbar sein. Er musshier noch aktiviert werden und die Kontoverknüpfung eingerichtet werden, was selbsterklärend ist.&lt;br /&gt;
&lt;br /&gt;
=== AWS Lambda Funktion anlegen ===&lt;br /&gt;
Für folgende Schritte muss man unter der Adresse http://aws.amazon.com angemeldet sein&lt;br /&gt;
# Anmeldung auswählen&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-01-site.png|200px]]&lt;br /&gt;
# Anmeldedaten eingeben&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-02-login.png|200px]]&lt;br /&gt;
# Den Punkt &#039;&#039;Lambda&#039;&#039; links auf der Startseite auswählen&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-03-lambda.png|200px]]&lt;br /&gt;
# Anschließend den Punkt &#039;&#039;Get Started Now&#039;&#039; auswählen&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-04-get_started_now.png|200px]]&lt;br /&gt;
# Den Blueprint &#039;&#039;Blank function&#039;&#039; auswählen&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-05-select_blueprint.png|200px]]&lt;br /&gt;
# Im neuen Fenster dann auf den gestrichelten Kasten klicken und &#039;&#039;Alexa Smart Home&#039;&#039; auswählen und mit &#039;&#039;Next&#039;&#039; bestätigen&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-06-configure_triggers1.png|200px]]&amp;lt;br /&amp;gt;&#039;&#039;Achtung! Es ist möglich, dass ihr hier &#039;&#039;&#039;Alexa Smart Home&#039;&#039;&#039; überhaupt nicht auswählen könnt. Dann solltet ihr ganz rechts oben in der Ecke mal schauen, welche Region bzw. welches Land ausgewählt ist. Ich empfehle hier &#039;&#039;&#039;Ireland&#039;&#039;&#039; auszuwählen. Dann erscheint bei den Funktionen auch &#039;&#039;&#039;Alexa Smart Home&#039;&#039;&#039;.&#039;&#039;&lt;br /&gt;
# Bei &#039;&#039;Application Id&#039;&#039; den Wert eintragen, dessen Ermittlung unter &#039;&#039;&#039;Skill Kit einrichten&#039;&#039;&#039; in der zusätzlichen Beschreibung beschrieben wird, den Haken bei &#039;&#039;Enable trigger&#039;&#039; setzen und mit &#039;&#039;Next&#039;&#039; bestätigen &amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-06-configure_triggers3.png|200px]]&lt;br /&gt;
# Auf der Konfigurationsseite bei &#039;&#039;Name&#039;&#039; den Wert FHEM eingeben, bei &#039;&#039;Runtime&#039;&#039; Node.js 4.3. Bei &#039;&#039;Role&#039;&#039; den Wert &#039;&#039;Choose an existing role&#039;&#039; wählen (wenn es noch keine existing role gibt: zuerst Create a custom role -&amp;gt; in dem Popup dann lambda_basic_execution und auf Allow)  und bei &#039;&#039;Existing role&#039;&#039; dann &#039;&#039;x&#039;&#039; wählen. Der Quellcode der Datei lambda.js aus dem Quellpaket wird dann an die Stelle des großen Textfeldes vollständig eingefügt. Dann noch den Hostname im Quellcode an den eigenen anpassen. Anschließend alles mit &#039;&#039;Next&#039;&#039; bestätigen.&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-07-configure_function.png|200px]]&lt;br /&gt;
# Auf der Übersichtsseite dann &#039;&#039;Create function&#039;&#039; anklicken&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-08-review.png|200px]]&lt;br /&gt;
# Klicke dann auf der Übersichtsseite oben links auf &#039;&#039;Functions&#039;&#039;&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-09-go_overview.png|200px]]&lt;br /&gt;
# Wähle dann die angelegte Funktion aus und klicke dann im Menü &#039;&#039;Action&#039;&#039; auf den Punkt &#039;&#039;Show ARN&#039;&#039;&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-10-1-show_arn.png|200px]]&lt;br /&gt;
# Es wird nun eine ARN Adresse angezeigt, die für den Punkt 6 bei &#039;&#039;&#039;Skill Kit einrichten&#039;&#039;&#039; benötigt wird&amp;lt;br /&amp;gt;[[Datei:Aws.amazon.com-10-2-arn.png|200px]]&lt;br /&gt;
&lt;br /&gt;
== Einrichtung ==&lt;br /&gt;
Nachdem die Alexa Skills installiert bzw. definiert wurden, müssen diese noch in deiner Alexa eingerichtet werden.&lt;br /&gt;
&lt;br /&gt;
=== Alexa Skill ===&lt;br /&gt;
# Auf http://alexa.amazon.de anmelden (Per Desktop-Browser, nicht die App unter iOS oder Android verwenden. Diese hat Probleme mit der OAuth Verknüpfung.)&lt;br /&gt;
# Auf &#039;&#039;Skills&#039;&#039; klicken&amp;lt;br /&amp;gt;[[Datei:Alexa.amazon.de-01-startseite.png|200px]]&lt;br /&gt;
# Oben rechts &#039;&#039;Meine Skills&#039;&#039;  auswählen&amp;lt;br /&amp;gt;[[Datei:Alexa.amazon.de-03-meine_skills.png|200px]]&lt;br /&gt;
# In der Liste der Skills sollte das angelegte FHEM Skill angezeigt werden. Dieses anklicken&amp;lt;br /&amp;gt;[[Datei:Alexa.amazon.de-02-liste_skills.png|200px]]&lt;br /&gt;
# Oben Rechts in den Details des Skills auf &#039;&#039;Skill aktivieren&#039;&#039; klicken&amp;lt;br /&amp;gt;[[Datei:Alexa.amazon.de-04-skill_details.png|200px]]&lt;br /&gt;
# In dem neu geöffneten Fenster die Authorisierung bestätigen&amp;lt;br /&amp;gt;[[Datei:Alexa.amazon.de-05-amazon_auth.png|200px]]&lt;br /&gt;
# Anschließend sollte die Verbindung erfolgreich aufgebaut worden sein &amp;lt;br /&amp;gt;[[Datei:Alexa.amazon.de-06-success.png|200px]]&lt;br /&gt;
&lt;br /&gt;
=== Geräte ===&lt;br /&gt;
# Auf http://alexa.amazon.de anmelden&lt;br /&gt;
# Auf &#039;&#039;Smart Home&#039;&#039; klicken&amp;lt;br /&amp;gt;[[Datei:Alexa.amazon.de-01-startseite.png|200px]]&lt;br /&gt;
# Anschließend den Punkt &#039;&#039;Geräte suchen&#039;&#039; anklicken&amp;lt;br /&amp;gt;[[Datei:Alexa.amazon.de-07-Gerätesuche.png|200px]]&lt;br /&gt;
# Wurde soweit alles korrekt eingerichtet, werden nun die gefundenen Geräte angezeigt.&lt;br /&gt;
&lt;br /&gt;
Tip: Es macht Sinn, unter &#039;&#039;Meine Gruppen&#039;&#039; Gruppen benannt nach den Räumen einzurichten. Hierdurch kann Alexa die Geräte besser auseinander halten, vor allem wenn die den gleichen Alias (z.B. &amp;quot;Licht&amp;quot;) haben.&lt;br /&gt;
== Nutzung ==&lt;br /&gt;
Um den Namen zu bestimmen unter dem ein Gerät mit Alexa angesprochen wird verwendet Alexa-Fhem mit absteigender Priorität:&lt;br /&gt;
* das alexaName Attribut&lt;br /&gt;
* das alias Attribut&lt;br /&gt;
* das NAME Internal&lt;br /&gt;
&lt;br /&gt;
Damit Alexa ein Gerät eindeutig identifizieren kann sollten eindeutige Bezeichnungen möglichst aus einem Wort und ohne Ziffern verwendet werden. Wenn Alexa einen Namen nicht versteht sollte man unter [http://alexa.amazon.de/spa/index.html] nachsehen was tatsächlich verstanden wurde und die Benennung anpassen.&lt;br /&gt;
&lt;br /&gt;
=== Smart Home Skill ===&lt;br /&gt;
Gruppen (Räume) müssen in der Alexa App konfiguriert werden. Über das API lassen sich nur der Name und die Schalteigenschaften übergeben.&lt;br /&gt;
&lt;br /&gt;
==== Sprachkommandos ====&lt;br /&gt;
Nach erfolgreicher Einrichtung des Smart Home Skills sollte Alexa mit den folgenden Befehlen nutzbar sein:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
“alexa, schalte &amp;lt;gerät/gruppe&amp;gt; ein”&lt;br /&gt;
“alexa, schalte &amp;lt;gerät/gruppe&amp;gt; aus”&lt;br /&gt;
“alexa, stelle &amp;lt;gerät/gruppe&amp;gt; auf &amp;lt;wert&amp;gt; prozent”&lt;br /&gt;
“alexa, stelle &amp;lt;gerät/gruppe&amp;gt; auf &amp;lt;anzahl&amp;gt; grad”&lt;br /&gt;
“alexa, erhöhe &amp;lt;gerät/gruppe&amp;gt; um &amp;lt;anzahl&amp;gt; prozent”&lt;br /&gt;
“alexa, reduziere &amp;lt;gerät/gruppe&amp;gt; um &amp;lt;anzahl&amp;gt; prozent”&lt;br /&gt;
“alexa, erhöhe &amp;lt;gerät/gruppe&amp;gt; um &amp;lt;anzahl&amp;gt; grad”&lt;br /&gt;
“alexa, reduziere &amp;lt;gerät/gruppe&amp;gt; um &amp;lt;anzahl&amp;gt; grad”&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Custom Skill ===&lt;br /&gt;
Der Custom Skill erlaubt im Gegensatz zum Smart Home Skill eine weitreichende Konfiguration der möglichen Kommandos.&lt;br /&gt;
&lt;br /&gt;
Das Prinzip der Kommandokonfiguration ist {{Link2Forum|Topic=60244|Message=532513|LinkText=im Forum}} beschrieben.&lt;br /&gt;
&lt;br /&gt;
TODO: Abfragen, Attribute (alexaMapping, alexaTypes, fhemIntents, articles, prepositions)&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
====Allgemeiner Hinweis====&lt;br /&gt;
Besonders wichtig ist, dass man sich sehr genau an diese Anleitung hält. Ein häufiger Fehler ist, dass die einfachen Anführungszeichen in der Anleitung unter &#039;&#039;&#039;AWS Lambda Funktion anlegen&#039;&#039;&#039; Punkt 8 einfach weggelassen werden. Diese sind zwingend notwendig. Es darf auch nur der reine Hostname eingetragen werden. Also kein &#039;&#039;http://&#039;&#039; davor. Entweder eine feste IP Adresse oder den Hostnamen, um den Rechner zu erreichen, den ihr über den Port 3000 freigegeben habt. Das sollte dann so aussehen:&lt;br /&gt;
&amp;lt;pre style=&amp;quot;width:50%;&amp;quot;&amp;gt;&lt;br /&gt;
const PORT=3000;&lt;br /&gt;
const HOST=&#039;mein.host.name&#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Freigabe von Port 3000====&lt;br /&gt;
{{Randnotiz|RNTyp=Fehl|RNText=Derzeit müsst ihr über einen echten IPv4 Anschluss verfügen, damit der Amazon Lambda-Server euch erreichen kann. DS-Lite Anschlüsse wie die von &amp;lt;b&amp;gt;UnityMedia&amp;lt;/b&amp;gt; z.B. funktionieren derzeit leider nicht. Eine möglicher &amp;quot;Workaround&amp;quot; wird hier beschrieben: https://forum.fhem.de/index.php/topic,60244.msg518276.html#msg518276}}&lt;br /&gt;
&lt;br /&gt;
Auf dem Router muss der Port 3000 Protokoll TCP freigegeben werden. D.h. von außen muss man wenn man den Port 3000 aufruft, auf dem intern laufenden node.js Alexa-Dienst zugreifen können.&lt;br /&gt;
Je nach Router gestaltet sich das Portforwarding bzw. die Portumleitung etwas schwieriger.&lt;br /&gt;
&lt;br /&gt;
Bei einem Speedport Router der Telekom beispielsweise, muss der Router komplett neu gestartet werden, wenn die Portfreigabe eingerichtet wurde. &lt;br /&gt;
&lt;br /&gt;
Bei der Fritz!Box ist das nicht nötig, bei dieser finden die Freigabe unter &#039;&#039;Internet -&amp;gt; Freigaben -&amp;gt; Portfreigaben&#039;&#039; statt. Dort wählt man dann den Rechner aus und richtet eine neue Freigabe ein. Wichtig hierbei ist, dass man Portfreigabe auswählt und nicht MyFRITZ!-Freigabe. Bei Port von bis trägt man 3000 ein, bei Port extern ebenfalls.&lt;br /&gt;
&lt;br /&gt;
Um die Portweiterleitung zu testen, solltet ihr euch auch nicht im gleichen Netz befinden. Viele Router blockieren den Netzaufruf aus dem gleichen Netz. Am besten testet ihr es, wenn ihr an eurem Mobiltelefon W-LAN deaktiviert und im Browser folgende Seite aufruft: &#039;&#039;https://mein.hostname:3000&#039;&#039;. Wenn ihr im Browser dann einen Quellcode von Alexa seht, funktioniert die Portumleitung.&lt;br /&gt;
&lt;br /&gt;
Wenn bis hier alles funktioniert und es läuft dennoch nicht rund, liegt das Problem woanders. Kommt z.B. bei der Gerätesuche kein Request rein (sichtbar auf dem Bildschirm, wenn bin/alexa gestartet wurden), kann evtl. der Lambda-Dienst falsch konfiguriert sein.&lt;br /&gt;
&lt;br /&gt;
====Probleme mit node.js - npm install====&lt;br /&gt;
&lt;br /&gt;
Falls eine Fehlermeldung auftritt, dass &amp;quot;npm&amp;quot; nicht gefunden werden kann, bitte NodeJS entsprechend der Anleitung im Homebridge-Artikel vorgehen: [[Homebridge_einrichten#NodeJS_installieren|NodeJS installieren]] sowie [[Homebridge_einrichten#Python.2C_g.2B.2B.2C_MDNS_installieren|Python, g++, MDNS installieren]], siehe auch folgenden Abschnitt.&lt;br /&gt;
&lt;br /&gt;
====Es kommen diverse Fehlermeldungen beim Starten von alexa-fhem und es beendet sich====&lt;br /&gt;
Wenn man auf der Konsole angemeldet ist, den Befehl&amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:50%;&amp;quot;&amp;gt;node -v&amp;lt;/source&amp;gt;eingeben. Ist die Version niedriger als die geforderte 0.12, muss eine neuere installiert werden. Hier darf man dann im Wiki unter [[Homebridge_einrichten#NodeJS_installieren]] nachschauen. NodeJS V4 sollte hierbei schon ausreichen. Solange die node.js Version nicht passt, gar nicht groß rum experimentieren! Bitte beachtet, dass alle Voraussetzungen unter [[Alexa-Fhem#Voraussetzungen]] erfüllt sind! Keine Experimente mit Versionen die darunter liegen.&lt;br /&gt;
&lt;br /&gt;
====Fehlermeldung &#039;&#039;NAT-PMP failed: Error: timeout&#039;&#039; Fehler angezeigt beim Start von alexa-fhem====&lt;br /&gt;
Wenn ihr dann alexa-fhem über die Konsole startet und bekommt folgenden Fehler: &#039;&#039;NAT-PMP failed: Error: timeout&#039;&#039;, lasst euch davon nicht irritieren. Das bedeutet lediglich, dass der Port nicht automatich freigegeben wurde über uPNP. Alternativ prüft, ob die Funktion der Portfreigabe via uPNP auf eurem Router aktiviert ist.&lt;br /&gt;
&lt;br /&gt;
====Nach Start auf der Console beendet sich Alexa-FHEM sofort wieder====&lt;br /&gt;
Unmittelbar nach dem Start von Alexa-FHEM werden ein paar UPNP Fehlermeldungen ausgegeben. Unmittelbar danach beendet sich Alexa-FHEM wieder. &lt;br /&gt;
&lt;br /&gt;
Viele scheinen ein Problem mit UPNP auf dem Raspberry Pi zu haben. Wenn dieses Problem auftritt einfach in der &amp;lt;code&amp;gt;~/.alexa/config.json&amp;lt;/code&amp;gt; die folgenden Zeilen rauslöschen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;quot;nat-pmp&amp;quot;: &amp;quot;10.0.1.1&amp;quot;,&lt;br /&gt;
        &amp;quot;nat-upnp&amp;quot;: true,&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Jetzt erneut Alexa-FHEM starten. Sollte nun laufen.&lt;br /&gt;
&lt;br /&gt;
====Was ist zu tun, wenn alexa-fhem keine Geräte findet?====&lt;br /&gt;
Zunächst müssen die Geräte, die angesprochen werden wollen, unter FHEM ein neues Attribut zugewiesen bekommen. Dazu das Gerät in FHEM öffnen und das Attribut &#039;&#039;genericDeviceType switch&#039;&#039; hinzufügen, wenn es ein Schalter mit der Funktiona AN/AUS sein soll. Wenn man will, kann man dem Gerät jetzt noch über das Attribut &#039;&#039;alias&#039;&#039; eine besseren Namen geben, mit dem Alexa das Gerät dann auch finden kann.&lt;br /&gt;
Anschließend muss alexa-fhem neu gestartet werden und die definierten Geräte sollten nun gefunden werden.&lt;br /&gt;
&lt;br /&gt;
====Was ist zu tun, wenn Alexa zwar Geräte findet, diese aber nicht angesprochen werden können?====&lt;br /&gt;
Zuerst die Informationen zum Datenfluss ganz oben ansehen. Dann am besten von hinten nach vorne vorgehen:&lt;br /&gt;
* wenn nichts bei alexa-fhem ankommt: port forwarding prüfen&lt;br /&gt;
* wenn lambda.js nichts los wird: im cloudwatch log nachsehen&lt;br /&gt;
* wenn bei lambda.js nichts ankommt: den trigger prüfen&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Zunächst sollte man sich unter &#039;&#039;http://aws.amazon.com&#039;&#039; das Logfile seiner erstellten Funktion anschauen. Ist überhaupt ein Logfile vorhanden? Falls nicht, liegt es vermutlich am Trigger.&lt;br /&gt;
Den solltet ihr überprüfen. Scheinbar kommt es hin und wieder vor, dass dieser nicht gesetzt ist. Dazu einfach auf &#039;&#039;Triggers&#039;&#039; klicken und mit &#039;&#039;Add trigger&#039;&#039; erneut einen anlegen. Hier muss, wie in der Anleitung unter &#039;&#039;&#039;AWS Lambda Funktion anlegen&#039;&#039;&#039; Punkt 7, die &#039;&#039;Application Id&#039;&#039; stehen und der Haken bei &#039;&#039;Enable trigger&#039;&#039; gesetzt sein. Dann alexa-fhem neu starten.&lt;br /&gt;
Wenn ihr Änderugen gemacht habt und den alexa-fhem Dienst noch nicht neu gestartet habt, wäre jetzt der richtige Zeitpunkt.&lt;br /&gt;
&lt;br /&gt;
====Was ist zu tun, wenn sich der Alexa-Service nicht starten lässt?====&lt;br /&gt;
Schaut bitte in das Unterverzeichnis [alexa-fhem (also dort, wo Ihr Alexa-FHEM instelliert habt]/bin. Die dort befindliche Datei &#039;&#039;alexa&#039;&#039; muss ausführbar sein. Also z.B. so:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:70%;&amp;quot;&amp;gt;2755327 4 -rwxr-xr-x 1 pi pi  339 Nov 26 23:20 alexa&amp;lt;/source&amp;gt;&lt;br /&gt;
Sollte dies nicht der Fall sein bitte mit:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;bash&amp;quot; style=&amp;quot;width:70%;&amp;quot;&amp;gt;chmod +x alexa&amp;lt;/source&amp;gt;&lt;br /&gt;
die Datei ausführbar machen. Sofern der User &amp;quot;pi&amp;quot; Eigentümer ist, ist kein sudo erforderlich.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Wie kann ich via Alexa-FHEM auf FHEM zugreifen, wenn der Port mit Benutzername/Kennwort geschützt ist?====&lt;br /&gt;
&lt;br /&gt;
Hierzu muss die Datei &amp;lt;code&amp;gt;~/.alexa/config.json&amp;lt;/code&amp;gt; geöffnet werden und der Abschnitt &amp;quot;connections&amp;quot; um folgende Zeile ergänzt werden:&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;quot;auth&amp;quot;: {&amp;quot;user&amp;quot;: &amp;quot;fhem&amp;quot;, &amp;quot;pass&amp;quot;: &amp;quot;fhempassword&amp;quot;},&amp;lt;/pre&amp;gt;&lt;br /&gt;
Bei Verwendung von SSL bei FHEM muss auch noch &amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;quot;ssl&amp;quot;: true,&amp;lt;/pre&amp;gt; hinzugefügt werden&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=17694</id>
		<title>Proteus EcoMeter</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=17694"/>
		<updated>2016-12-07T14:41:18Z</updated>

		<summary type="html">&lt;p&gt;Eisler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Proteus_EcoMeter]] ist ein Ultraschallsensor mit Monitor zur Pegelmessung von Heizöl- oder Wassertanks&lt;br /&gt;
&lt;br /&gt;
* Sensor zum Einbau in den Tank&lt;br /&gt;
* Funkverbindung zwischen Sensor und Monitor&lt;br /&gt;
* USB-Verbindung zwischen Monitor und FHEM&lt;br /&gt;
* Für Wasser oder Öltanks&lt;br /&gt;
&lt;br /&gt;
EcoMeter für Heizöltanks&amp;lt;br&amp;gt;&lt;br /&gt;
EcoMeter S für Wassertanks&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Infobox Hardware&lt;br /&gt;
|Bild=platzHalter.png&lt;br /&gt;
|Bildbeschreibung=todo&lt;br /&gt;
|HWProtocol=unknown&lt;br /&gt;
|HWType=Sensor und Sender&lt;br /&gt;
|HWCategory=Other Components&lt;br /&gt;
|HWComm=Funk, 433,92MHz, USB&lt;br /&gt;
|HWChannels=unknown&lt;br /&gt;
|HWVoltage=1,5V LR2450, 5V USB&lt;br /&gt;
|HWPowerConsumption=unknown&lt;br /&gt;
|HWPoweredBy=Battery, USB&lt;br /&gt;
|HWSize=21 x 18 x 6 cm&lt;br /&gt;
|HWDeviceFHEM=[http://fhem.de/commandref.html#TEK603 TEK603]&lt;br /&gt;
|HWManufacturer=Tekelek&lt;br /&gt;
}}&lt;br /&gt;
== Features ==&lt;br /&gt;
EcoMeter liefert alle 60 Minuten einen Messwert&lt;br /&gt;
EcoMeter liefert alle 30 Minuten einen Messwert und bei schnellen Änderungen&lt;br /&gt;
&lt;br /&gt;
== Hinweise zum Betrieb mit Fhem ==&lt;br /&gt;
=== Definition/Anlernvorgang ===&lt;br /&gt;
# eventuell im Linux fehlende Pakete installieren &amp;lt;code&amp;gt;sudo apt-get install libdigest-crc-perl &amp;lt;/code&amp;gt;&lt;br /&gt;
# Monitor an das FHEM Gerät anschließen&lt;br /&gt;
# Festellen, dass die Verbindung geht. Dazu auf der Kommandozeile eingeben: &amp;lt;code&amp;gt;ls -al /dev/serial/by-id &amp;lt;br&amp;gt; lrwxrwxrwx 1 root root 13 Jan  1  1970 usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 -&amp;gt; ../../ttyUSB0 &amp;lt;/code&amp;gt; &lt;br /&gt;
# ecometer definieren &amp;lt;code&amp;gt;define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 &amp;lt;/code&amp;gt;&lt;br /&gt;
# LogFile definieren &amp;lt;code&amp;gt;define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.* &amp;lt;/code&amp;gt;&lt;br /&gt;
# Gerät wie in der Betriebsanleitung installieren&lt;br /&gt;
&lt;br /&gt;
=== FHEM Config-Auszug ===&lt;br /&gt;
Ein exemplarischer Auszug aus der [[Konfiguration]]:&lt;br /&gt;
 define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0&lt;br /&gt;
 define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.*&lt;br /&gt;
&lt;br /&gt;
=== Eventmonitorbeispiel ===&lt;br /&gt;
{{todo| Eventmonitor einfügen}}&lt;br /&gt;
&lt;br /&gt;
== Einsatzbeispiel ==&lt;br /&gt;
=== Anzeige des Ölstands in einem Öltank ===&lt;br /&gt;
{{todo|Beispielunterlagen erstellen}}&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Hersteller: [https://proteus-meter.com/produkte/proteus-ecometer/ Shop]&lt;br /&gt;
* Hersteller: [https://proteus-meter.com/faq/ Bedienanleitung]&lt;br /&gt;
* Forenbeitrag zur FHEM-Einbindung: {{Link2Forum|Topic=27315}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Other Components]]&lt;br /&gt;
[[Kategorie:Füllstandsmesser]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=17692</id>
		<title>Proteus EcoMeter</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=17692"/>
		<updated>2016-12-07T14:38:24Z</updated>

		<summary type="html">&lt;p&gt;Eisler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Proteus_EcoMeter]] ist ein Ultraschallsensor mit Monitor zur Pegelmessung von Heizöl- oder Wassertanks&lt;br /&gt;
&lt;br /&gt;
* Sensor zum Einbau in den Tank&lt;br /&gt;
* Funkverbindung zwischen Sensor und Monitor&lt;br /&gt;
* USB-Verbindung zwischen Monitor und FHEM&lt;br /&gt;
* Für Wasser oder Öltanks&lt;br /&gt;
&lt;br /&gt;
EcoMeter für Heizöltanks&amp;lt;br&amp;gt;&lt;br /&gt;
EcoMeter S für Wassertanks&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Infobox Hardware&lt;br /&gt;
|Bild=platzHalter.png&lt;br /&gt;
|Bildbeschreibung=todo&lt;br /&gt;
|HWProtocol=unknown&lt;br /&gt;
|HWType=Sensor und Sender&lt;br /&gt;
|HWCategory=Other Components&lt;br /&gt;
|HWComm=Funk, 868Mhz??, USB&lt;br /&gt;
|HWChannels=unknown&lt;br /&gt;
|HWVoltage=1,5V LR2450, 5V USB&lt;br /&gt;
|HWPowerConsumption=unknown&lt;br /&gt;
|HWPoweredBy=Battery, USB&lt;br /&gt;
|HWSize=21 x 18 x 6 cm&lt;br /&gt;
|HWDeviceFHEM=[http://fhem.de/commandref.html#TEK603 TEK603]&lt;br /&gt;
|HWManufacturer=Tekelek&lt;br /&gt;
}}&lt;br /&gt;
== Features ==&lt;br /&gt;
EcoMeter liefert alle 60 Minuten einen Messwert&lt;br /&gt;
EcoMeter liefert alle 30 Minuten einen Messwert und bei schnellen Änderungen&lt;br /&gt;
&lt;br /&gt;
== Hinweise zum Betrieb mit Fhem ==&lt;br /&gt;
=== Definition/Anlernvorgang ===&lt;br /&gt;
# eventuell im Linux fehlende Pakete installieren &amp;lt;code&amp;gt;sudo apt-get install libdigest-crc-perl &amp;lt;/code&amp;gt;&lt;br /&gt;
# Monitor an das FHEM Gerät anschließen&lt;br /&gt;
# Festellen, dass die Verbindung geht. Dazu auf der Kommandozeile eingeben: &amp;lt;code&amp;gt;ls -al /dev/serial/by-id &amp;lt;br&amp;gt; lrwxrwxrwx 1 root root 13 Jan  1  1970 usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 -&amp;gt; ../../ttyUSB0 &amp;lt;/code&amp;gt; &lt;br /&gt;
# ecometer definieren &amp;lt;code&amp;gt;define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 &amp;lt;/code&amp;gt;&lt;br /&gt;
# LogFile definieren &amp;lt;code&amp;gt;define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.* &amp;lt;/code&amp;gt;&lt;br /&gt;
# Gerät wie in der Betriebsanleitung installieren&lt;br /&gt;
&lt;br /&gt;
=== FHEM Config-Auszug ===&lt;br /&gt;
Ein exemplarischer Auszug aus der [[Konfiguration]]:&lt;br /&gt;
 define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0&lt;br /&gt;
 define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.*&lt;br /&gt;
&lt;br /&gt;
=== Eventmonitorbeispiel ===&lt;br /&gt;
{{todo| Eventmonitor einfügen}}&lt;br /&gt;
&lt;br /&gt;
== Einsatzbeispiel ==&lt;br /&gt;
=== Anzeige des Ölstands in einem Öltank ===&lt;br /&gt;
{{todo|Beispielunterlagen erstellen}}&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Hersteller: [https://proteus-meter.com/produkte/proteus-ecometer/ Shop]&lt;br /&gt;
* Hersteller: [https://proteus-meter.com/faq/ Bedienanleitung]&lt;br /&gt;
* Forenbeitrag zur FHEM-Einbindung: {{Link2Forum|Topic=27315}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Other Components]]&lt;br /&gt;
[[Kategorie:Füllstandsmesser]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=17520</id>
		<title>Proteus EcoMeter</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Proteus_EcoMeter&amp;diff=17520"/>
		<updated>2016-11-27T12:16:18Z</updated>

		<summary type="html">&lt;p&gt;Eisler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Proteus_EcoMeter]] ist ein Ultraschallsensor mit Monitor zum Pegelmessung von Heizöl- oder Wassertanks&lt;br /&gt;
&lt;br /&gt;
{{Baustelle}}&lt;br /&gt;
&lt;br /&gt;
* Sensor zum Einbau in den Tank&lt;br /&gt;
* Funkverbindung zwischen Sensor und Monitor&lt;br /&gt;
* USB-Verbindung zwischen Monitor und FHEM&lt;br /&gt;
* Für Wasser oder Öltanks&lt;br /&gt;
&lt;br /&gt;
EcoMeter für Heizöltanks&amp;lt;br&amp;gt;&lt;br /&gt;
EcoMeter S für Wassertanks&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Infobox Hardware&lt;br /&gt;
|Bild=platzHalter.png&lt;br /&gt;
|Bildbeschreibung=todo&lt;br /&gt;
|HWProtocol=unknown&lt;br /&gt;
|HWType=Sensor und Sender&lt;br /&gt;
|HWCategory=Other Components&lt;br /&gt;
|HWComm=Funk, 868Mhz??, USB&lt;br /&gt;
|HWChannels=unknown&lt;br /&gt;
|HWVoltage=1,5V LR2450, 5V USB&lt;br /&gt;
|HWPowerConsumption=unknown&lt;br /&gt;
|HWPoweredBy=Battery, USB&lt;br /&gt;
|HWSize=21 x 18 x 6 cm&lt;br /&gt;
|HWDeviceFHEM=[http://fhem.de/commandref.html#TEK603 TEK603]&lt;br /&gt;
|HWManufacturer=Proteus&lt;br /&gt;
}}&lt;br /&gt;
== Features ==&lt;br /&gt;
EcoMeter liefert alle 60 Minuten einen Messwert&lt;br /&gt;
EcoMeter liefert alle 30 Minuten einen Messwert und bei schnellen Änderungen&lt;br /&gt;
&lt;br /&gt;
== Hinweise zum Betrieb mit Fhem ==&lt;br /&gt;
=== Definition/Anlernvorgang ===&lt;br /&gt;
# eventuell im Linux fehlende Pakete installieren &amp;lt;code&amp;gt;sudo apt-get install libdigest-crc-perl &amp;lt;/code&amp;gt;&lt;br /&gt;
# Monitor an das FHEM Gerät anschließen&lt;br /&gt;
# Festellen, dass die Verbindung geht. Dazu auf der Kommandozeile eingeben: &amp;lt;code&amp;gt;ls -al /dev/serial/by-id &amp;lt;br&amp;gt; lrwxrwxrwx 1 root root 13 Jan  1  1970 usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 -&amp;gt; ../../ttyUSB0 &amp;lt;/code&amp;gt; &lt;br /&gt;
# ecometer definieren &amp;lt;code&amp;gt;define TEK603 TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0 &amp;lt;/code&amp;gt;&lt;br /&gt;
# LogFile definieren &amp;lt;code&amp;gt;define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.* &amp;lt;/code&amp;gt;&lt;br /&gt;
# Gerät wie in der Betriebsanleitung installieren&lt;br /&gt;
&lt;br /&gt;
=== FHEM Config-Auszug ===&lt;br /&gt;
Ein exemplarischer Auszug aus der [[Konfiguration]]:&lt;br /&gt;
 define ecometer TEK603 /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0&lt;br /&gt;
 define FileLog_ecometer FileLog %Lecometer-%Y.log ecometer.*&lt;br /&gt;
&lt;br /&gt;
=== Eventmonitorbeispiel ===&lt;br /&gt;
{{todo| Eventmonitor einfügen}}&lt;br /&gt;
&lt;br /&gt;
== Einsatzbeispiel ==&lt;br /&gt;
=== Anzeige des Ölstands in einem Öltank ===&lt;br /&gt;
{{todo|Beispielunterlagen erstellen}}&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Hersteller: [https://proteus-meter.com/produkte/proteus-ecometer/ Shop]&lt;br /&gt;
* Hersteller: [https://proteus-meter.com/faq/ Bedienanleitung]&lt;br /&gt;
* Forenbeitrag zur FHEM-Einbindung: {{Link2Forum|Topic=27315}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Other Components]]&lt;br /&gt;
[[Kategorie:Füllstandsmesser]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=HM-ES-TX-WM_Z%C3%A4hlersensor_f%C3%BCr_Strom-_und_Gasz%C3%A4hler&amp;diff=10062</id>
		<title>HM-ES-TX-WM Zählersensor für Strom- und Gaszähler</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=HM-ES-TX-WM_Z%C3%A4hlersensor_f%C3%BCr_Strom-_und_Gasz%C3%A4hler&amp;diff=10062"/>
		<updated>2015-02-13T14:58:40Z</updated>

		<summary type="html">&lt;p&gt;Eisler: fix&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Hardware&lt;br /&gt;
|Bild=HM-EM-TX-WM.jpg&lt;br /&gt;
|Bildbeschreibung=HomeMatic Zählersensor&lt;br /&gt;
|HWProtocol=HomeMatic&lt;br /&gt;
|HWType=Powersensor&lt;br /&gt;
|HWCategory=HomeMatic&lt;br /&gt;
|HWComm=868&amp;amp;nbsp;MHz&lt;br /&gt;
|HWChannels=1&lt;br /&gt;
|HWVoltage=6 V&lt;br /&gt;
|HWPowerConsumption=30 mA&lt;br /&gt;
|HWPoweredBy=4 Batterien LR6/AA/Mignon&lt;br /&gt;
|HWSize=68x105x30mm&lt;br /&gt;
|HWDeviceFHEM=[http://fhem.de/commandref.html#CUL_HM CUL_HM]&lt;br /&gt;
|HWManufacturer=ELV / eQ-3 &lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Dieses Device ist seit Anfang Feb. 2015 als Bausatz auf dem Markt - dieser Artikel ist im Aufbau - bitte Verständnis für Unvollständigkeiten und fehlende Bereiche &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Features ==&lt;br /&gt;
Der Zählersensor ist ein Datenerfassungssystem, das den Energieverbräuche (Strom, Gas) direkt am Zähler erfasst und in FHEM als laufender Saldo und als Momentanwerte verfügbar macht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Technische Daten:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Gewicht: 195 g (inkl. 4 LR6/Mignon/AA-Batterien)&lt;br /&gt;
&lt;br /&gt;
== Anwendungsszenarien ==&lt;br /&gt;
Mit den erfassten und gesammelten Daten lassen sich Aktionen beim Erreichen bestimmter Schwellwerte auslösen, Verbrauchs- und Kostenstatistiken ableiten sowie beliebige weitere Abhängigkeiten bilden. Durch die Funkdatenübertragung sowie den Batteriebetrieb (1 bis 2 Jahre Betriebsdauer verspricht der Hersteller mit einem Satz) ist die Installation denkbar einfach und erfordert lediglich beim Anbringen der Leseeinheit Genauigkeit.&lt;br /&gt;
&lt;br /&gt;
Aktuell (Feb. 2015) hat die Auslieferung der Geräte als (einfacher) Bausatz begonnen, zu dem es einen Ferraris-Sensor (für Stromzähler mit den markanten Laufscheiben), LED-Sensor (für neue Zähler mit S0-Schnittstelle) sowie BK-G4-Gaszähler von Elster-Kromschröder, die an einer Ziffernrolle einen kleinen Magneten haben, der über einen Sensor mit Reedkontakt erfasst wird.&lt;br /&gt;
&lt;br /&gt;
Die Zähler werden mit rückstandslos entfernbaren Klebepads angebracht.&lt;br /&gt;
&lt;br /&gt;
== Parameter ==&lt;br /&gt;
 &#039;&#039;&#039;list:        register | range              | peer     | description&#039;&#039;&#039; &lt;br /&gt;
   0: pairCentral      |   0 to 16777215    |          | pairing to central &lt;br /&gt;
   1: mtrConstGas      |   0 to 655.36      |          | constant gas &lt;br /&gt;
   1: mtrConstIr       |   0 to 65536       |          | constant IR &lt;br /&gt;
   1: mtrConstLed      |   0 to 65536       |          | constant led &lt;br /&gt;
   1: mtrSensIr        | -99 to 99          |          | sensiblity IR &lt;br /&gt;
   1: mtrType          |     literal        |          | type of measurement options:LED,gas,unknown,IR &lt;br /&gt;
&lt;br /&gt;
== Probleme ==&lt;br /&gt;
&amp;lt;ggfls. ergänzen&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Betrieb mit FHEM ==&lt;br /&gt;
Der Sensor wird mit einem FHEM im Update-Stand vom (mindestens) 7.2.2015 funktionsfähig eingebunden (getestet in der Variante mit Feraris-Fühler). Es werden der Fühlertyp und die Fühlerkonstante richtig ausgelesen und im Reading Energy die errechnete Energiemenge ausgegeben (bei Strom: Wattstunden, Wh).&lt;br /&gt;
&lt;br /&gt;
=== Readings ===&lt;br /&gt;
 &#039;&#039;&#039;Reading:       |Beispiel&#039;&#039;&#039;   &lt;br /&gt;
 D-firmware     |1.0&lt;br /&gt;
 D-serialNr     |MEQ0025452 &lt;br /&gt;
 boot           |off &lt;br /&gt;
 current        |0 &lt;br /&gt;
 eState         |E: 6199.9 P: 1713 I: 0 U: 0 f: 50 &lt;br /&gt;
 energy         |6199.9 &lt;br /&gt;
 frequency      |50 &lt;br /&gt;
 mpower         |1713 &lt;br /&gt;
 voltage        |0&lt;br /&gt;
&lt;br /&gt;
=== Events Auszug ===&lt;br /&gt;
 &#039;&#039;&#039;Event     |Beispielswert&#039;&#039;&#039;&lt;br /&gt;
 energy    |10186.6&lt;br /&gt;
 power     |283&lt;br /&gt;
 current   |0&lt;br /&gt;
 voltage   |0&lt;br /&gt;
 frequency |50&lt;br /&gt;
 eState    |E: 10186.6 P: 283 I: 0 U: 0 f: 50&lt;br /&gt;
 boot      |off&lt;br /&gt;
&lt;br /&gt;
Mit einem Userreading lässt sich die Angabe aus Energy zum Beispiel für einen Stromzähler (Wh) in die auf dem Zähler übliche Angabe kWh umrechnen und zugleich mit dem Aufaddieren des Unterschieds beim Start (das Zählermodul beginnt ja mit 0 Wh, während der Zähler meist schon einige Jahre auf der &amp;quot;Rolle&amp;quot; hat) die Angaben gleichziehen:&lt;br /&gt;
&lt;br /&gt;
 attr &amp;lt;DeviceName&amp;gt; userReadings kWh {sprintf(&amp;quot;%.1f&amp;quot;,ReadingsVal(&amp;quot;CUL_HM_HM_ES_TX_WM_353594&amp;quot;,&amp;quot;energy&amp;quot;,&amp;quot;???&amp;quot;)/1000+72031.5639)}&lt;br /&gt;
&lt;br /&gt;
=== fhem.log Auszug ===&lt;br /&gt;
&amp;lt;Bitte ergänzen&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== [[Konfiguration]] ===&lt;br /&gt;
Nach dem Erkennen durch [[autocreate]] wird der Zählersensor mit diesen Angaben eingebunden:&lt;br /&gt;
&lt;br /&gt;
 define CUL_HM_HM_ES_TX_WM_353594 CUL_HM 353594&lt;br /&gt;
 attr CUL_HM_HM_ES_TX_WM_353594 IODev CUL_0&lt;br /&gt;
 attr CUL_HM_HM_ES_TX_WM_353594 actCycle 000:10&lt;br /&gt;
 attr CUL_HM_HM_ES_TX_WM_353594 actStatus alive&lt;br /&gt;
 attr CUL_HM_HM_ES_TX_WM_353594 expert 2_full&lt;br /&gt;
 attr CUL_HM_HM_ES_TX_WM_353594 firmware 1.0&lt;br /&gt;
 attr CUL_HM_HM_ES_TX_WM_353594 model HM-ES-TX-WM&lt;br /&gt;
 attr CUL_HM_HM_ES_TX_WM_353594 subType powerSensor&lt;br /&gt;
&lt;br /&gt;
== Logging/Graph. Darstellung ==&lt;br /&gt;
[[Datei:PlotStromverbrauchProStunde.png|mini|400px|rechts|Beispiel der graph. Darstellung]]&lt;br /&gt;
Der Zähler wird ja fortlaufend aufaddiert. Mit der delta-Funktion von SVG-Plot können die Verbräuche sehr gut visualisiert werden. &lt;br /&gt;
Beispiel Stromzähler:&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;Stündlicher Stromverbrauch | aktuell: $data{currval1} ($data{min1} / $data{avg1} / $data{max1}) kWh&#039;&lt;br /&gt;
 set ytics &lt;br /&gt;
 set y2tics &lt;br /&gt;
 set grid ytics&lt;br /&gt;
 set ylabel &amp;quot;kWh/h&amp;quot;&lt;br /&gt;
 set y2label &amp;quot;kWh/h&amp;quot;&lt;br /&gt;
 #FileLog 4:CUL_HM_HM_ES_TX_WM_353594.kWh\x3a:0:delta-h&lt;br /&gt;
 plot &amp;quot;&amp;lt;IN&amp;gt;&amp;quot; using 1:2 axes x1y2 title &#039;Line 1&#039; ls l0 lw 2 with bars&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Handbuch: &lt;br /&gt;
&lt;br /&gt;
{{Todo|Artikel vervollständigen}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:HomeMatic Components]]&lt;br /&gt;
[[Kategorie:Energieverbrauchsmessung]]&lt;br /&gt;
[[Kategorie:Energieerzeugungsmessung]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Harmony&amp;diff=8284</id>
		<title>Harmony</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Harmony&amp;diff=8284"/>
		<updated>2014-10-31T13:11:01Z</updated>

		<summary type="html">&lt;p&gt;Eisler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{SEITENTITEL:harmoony}}&lt;br /&gt;
{{Infobox Modul&lt;br /&gt;
|ModPurpose=Anbindung Logitech Harmony Hub basierter Fernsbedienungen&lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=harmony&lt;br /&gt;
|ModForumArea=Multimedia&lt;br /&gt;
|ModTechName=37_harmony.pm&lt;br /&gt;
|ModOwner=Andre ([http://forum.fhem.de/index.php?action=profile;u=430 Forum] / [[Benutzer Diskussion:justme|Wiki]])}}&lt;br /&gt;
&lt;br /&gt;
Das Fhem-[[:Kategorie:Gerätemodul|Gerätemodul]] [[harmony]] bietet die Möglichkeit Logitech Harmony Hub basierte Fernbedienungen an Fhem anzubinden und so von Fhem aus Aktivitäten zu starten und zu stoppen, in Fhem auf das starten und stoppen von Aktivitäten über eine der mit dem Hub verbundenen Fernbedienungen zu reagieren oder auf Geräteebene jedes im Hub konfigurierte Gerät über IR, Bluetooth und/oder einen Smart Keyboard USB Dongle zu steuern.&lt;br /&gt;
&lt;br /&gt;
Unterstütz werden zur Zeit die Modelle Ultumate Hub, Ultimate Smart Control, Ultimate, Smart Keyboard sowie alle darauf basierenden Kombinationen mit Smart Control und Smart Keyboard Add-On.&lt;br /&gt;
&lt;br /&gt;
=== Define ===&lt;br /&gt;
  define &amp;lt;hub&amp;gt; harmony &amp;lt;ip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Allgemeines===&lt;br /&gt;
Aktivitäten und Geräte lassen sich an allen stellen entweder als ID oder als Name angeben. Wenn der Name angegeben wird müßen hierbei Leerzeichen durch einen Punkt ersetzt werden. Dies kann auch für eventuell andre im Namen vorhandene Sonderzeichen gelten.&lt;br /&gt;
&lt;br /&gt;
=== Aktivität ===&lt;br /&gt;
Das Reading currentActivity enthält die gerade laufende Aktivität. Beim Wechsel zwischen Aktivitäten erzeugt es Events auf die in Fhem über notify reagiert werden kann. Das Reading previousActivity enthält die davor laufende Aktivität sofern diese bekannt ist. Diese Reading erzeugt keine Events.&lt;br /&gt;
&lt;br /&gt;
In den Inernal currentActivityID und previousActivityID stehen die dazu gehörenden IDs der Aktivitäten.&lt;br /&gt;
&lt;br /&gt;
Innerhalb einer laufenden Aktivität kann mit&lt;br /&gt;
  set &amp;lt;hub&amp;gt; command &amp;lt;command&amp;gt;&lt;br /&gt;
ein IR Kommando an eines der Beteiligten Geräte gesendet werden.&lt;br /&gt;
&lt;br /&gt;
=== Geräteebene ===&lt;br /&gt;
Innerhalb und außerhalb einer laufenden Aktivität kann mit&lt;br /&gt;
  set &amp;lt;hub&amp;gt; command &amp;lt;id|name&amp;gt; &amp;lt;command&amp;gt;&lt;br /&gt;
ein IR Kommando an eines der im Hub bekannten Geräte gesendet werden. Hierbei ist darauf zu achten das innerhalb einer Aktivität keine Kommandos verwendet werden sollten die den Smart-State betreffen.&lt;br /&gt;
&lt;br /&gt;
==== Fhem Devices auf Geräteebene ====&lt;br /&gt;
&lt;br /&gt;
Es ist möglich sich für einzelne oder alle im Hub konfigurierten gerät ein zugehöriges Fhem device anlegen zu lassen:&lt;br /&gt;
  set &amp;lt;hub&amp;gt; autocreate [&amp;lt;id|name&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
Beim umschalten zwischen Aktivitäten wird in diesen Fhem devices wird im Reading power den in der Aktivität konfigurierten Einschaltzustand: on,off oder manual. Mit einem notify auf power Events lässt sich in Fhem auf einzelne Geräte reagieren.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Diese Fhem devices spiegeln nicht den tatsächlichen Gerätezustand wieder sondern den innerhalb einer Aktivität beabsichtigen Zustand. Der tatsächliche zustand kann z.b. auf Grund von Empfangsproblemen abweichen.&lt;br /&gt;
&lt;br /&gt;
Das get commands sowie die set command, hidDevice, text, cursor und special Kommandos auf Hub ebene stehen hier direkt und ohne Angabe von Device ID oder Name zur verfügung.&lt;br /&gt;
&lt;br /&gt;
=== Smart Keyboard ===&lt;br /&gt;
Der Harmony Hub kann über Bluetooth oder die zum Smart Keyboard gehörenden USB Dongle mit einem Rechner,Media PC oder sonstigem Gerät das Tastatureingabe unterstütz verbunden werden. Fhem kann diese Verbindung nutzen um beliebige Tastendrücke an ein solches gerät zu senden. Das können Texte sein, Bursorbewegungen oder die Power-, Multimedia oder sonstigen Funktionstasten die das Gerät unterstütz.&lt;br /&gt;
&lt;br /&gt;
Alle gesendeten Tastendrücke beziehen sich normalerweise auf das zur gerade laufenden Aktivität gehörende Tastatureingabegerät. Mit dem hidDevice kommando lässt sich die Tastatureingabe auf jedes im Hub dafür konfigurierte Gerät umschalten.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Dieses Umschalten kann einige Sekunden dauern da zum die bestehende Bluetooth Verbindung getrennt und eine neue aufgebaut wird.&lt;br /&gt;
&lt;br /&gt;
Es stehen die kommandos text, cursor und special zur verfügung.&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
&lt;br /&gt;
Gehe in PLEX auf die Library Musik und spiele das erste item in der OnDeck liste:&lt;br /&gt;
  set &amp;lt;hub&amp;gt; text M&lt;br /&gt;
  set &amp;lt;hub&amp;gt; cursor right&lt;br /&gt;
  set &amp;lt;hub&amp;gt; text p&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [http://myharmony.com myHarmony] Logitech Harmony Hersteller&lt;br /&gt;
* [http://www.harmony-remote-forum.de/portal.php Harmony Remote Forum] deutsches Harmony Forum&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Unterhaltungselektronik]]&lt;br /&gt;
[[Kategorie:IP Components]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=EnergyCam&amp;diff=8172</id>
		<title>EnergyCam</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=EnergyCam&amp;diff=8172"/>
		<updated>2014-10-21T18:52:37Z</updated>

		<summary type="html">&lt;p&gt;Eisler: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Hardware&lt;br /&gt;
|Bild=EnergyCam.jpg&lt;br /&gt;
|Bildbeschreibung=EnergyCam mit externer Batterie und externer Antenne montiert auf einem Ferraris-Zähler&lt;br /&gt;
|HWProtocol=Wireless M-Bus &lt;br /&gt;
|HWType=Sensor&lt;br /&gt;
|HWCategory=Energieverbrauchsmessung&lt;br /&gt;
|HWComm=Funk 868MHz, ModBus&lt;br /&gt;
|HWChannels=N/A&lt;br /&gt;
|HWVoltage=3,0 - 3,6V&lt;br /&gt;
|HWPowerConsumption=&amp;lt;0,2W&lt;br /&gt;
|HWPoweredBy=interne CR2032 oder externe Batterie, USB&lt;br /&gt;
|HWSize=B x H x T 48,00 x 55,50 x 15,50 mm&lt;br /&gt;
|HWDeviceFHEM=WMBUS&lt;br /&gt;
|HWManufacturer=Fast Forward AG&lt;br /&gt;
}}&lt;br /&gt;
Die [[EnergyCam]] ist ein Zähleraufsatz der den Zählerstand erfasst und dann drahtlos (Wireless M-Bus) oder drahtgebunden (Modbus) weiterleitet.&lt;br /&gt;
== Features ==&lt;br /&gt;
Eine EnergyCam erfasst optisch den Zählerstand eines herkömmlichen Zählers für Elektrizität, Gas oder Wasser und wandelt diesen per optischer Zeichenerkennung (OCR) in einen Zahlenwert um. Dieser Wert wird dann zyklisch versandt und kann vom Empfänger ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
Die EnergyCam wird dazu auf den Zähler aufgeklebt. Das Gerät unterstützt dabei durch eine Anzeige die Montage in der richtigen Position. Für die Verwendung mit Gas- und Wasserzählern sind noch zusätzliche Adapter erforderlich.&lt;br /&gt;
Es gibt die EnergyCam sowohl mit interner als auch externer Antenne sowie mit interner und externer Energieversorgung.&lt;br /&gt;
Der aktuelle Zählerstand kann bei Bedarf auch direkt am Gerät abgelesen werden und wird auf dem internen Display dargestellt. &lt;br /&gt;
&lt;br /&gt;
== Hinweise zum Betrieb mit FHEM ==&lt;br /&gt;
Die EnergyCam im Wireless M-Bus Modus wird vom Modul [[WMBUS]] vollständig unterstützt. Das Herstellerkürzel ist FFD.&lt;br /&gt;
&lt;br /&gt;
== Bekannte Probleme ==&lt;br /&gt;
Die Erfassung der Zählerstands erfolgt von schräg unterhalb des eigentlichen Zählwerks. Daher darf dieser Bereich nicht durch Aufdruck oder ähnliches die freie Sicht auf das Zählwerk behindern, sonst ist keine fehlerfreie Erfassung möglich.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Herstellerseite [http://www.fastforward.ag/]&lt;br /&gt;
[[Kategorie:Energieverbrauchsmessung]]&lt;br /&gt;
[[Kategorie:Other Components]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=HM-Sec-SFA-SM_Funk-Sirenensteuerung&amp;diff=6742</id>
		<title>HM-Sec-SFA-SM Funk-Sirenensteuerung</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=HM-Sec-SFA-SM_Funk-Sirenensteuerung&amp;diff=6742"/>
		<updated>2014-07-04T12:18:27Z</updated>

		<summary type="html">&lt;p&gt;Eisler: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;Homematic HM-SEC-SFA-SM Funk-Sirenensteuerung&#039;&#039;&#039;&lt;br /&gt;
== Features ==&lt;br /&gt;
&amp;amp;lt;Bitte ergänzen&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hinweise zum Betrieb mit FHEM ==&lt;br /&gt;
&#039;&#039;&#039;Achtung:&#039;&#039;&#039;Die meisten &#039;&#039;&#039;Aktoren&#039;&#039;&#039; von HomeMatic, die den Namensbestandteil &amp;quot;SEC&amp;quot; haben, sind nicht mit [[CUL]] oder CUNO nutzbar, sondern erfordern [[HMLAN Konfigurator]], da bei ihnen der [[AES Encryption]] genannte Siginingrequest nicht abschaltbar ist.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Das Pairing sollte wie in [[HomeMatic Devices pairen]] beschrieben durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
=== FHEM Config-Auszug ===&lt;br /&gt;
Per &#039;&#039;autocreate&#039;&#039;&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;define CUL_HM_switch_124DEF CUL_HM 124DEF&lt;br /&gt;
attr CUL_HM_switch_124DEF devInfo 820100&lt;br /&gt;
attr CUL_HM_switch_124DEF firmware 1.0&lt;br /&gt;
attr CUL_HM_switch_124DEF hmClass receiver&lt;br /&gt;
attr CUL_HM_switch_124DEF model HM-SEC-SFA-SM&lt;br /&gt;
attr CUL_HM_switch_124DEF room CUL_HM&lt;br /&gt;
attr CUL_HM_switch_124DEF serialNr GEQ0173622&lt;br /&gt;
attr CUL_HM_switch_124DEF subType switch&lt;br /&gt;
define FileLog_CUL_HM_switch_124DEF FileLog /var/log/fhem/CUL_HM_switch_124DEF-%Y.log CUL_HM_switch_124DEF&lt;br /&gt;
attr FileLog_CUL_HM_switch_124DEF logtype text&lt;br /&gt;
attr FileLog_CUL_HM_switch_124DEF room CUL_HM&lt;br /&gt;
define CUL_HM_switch_124DEF_Siren CUL_HM 124DEF01&lt;br /&gt;
attr CUL_HM_switch_124DEF_Siren model HM-SEC-SFA-SM&lt;br /&gt;
attr CUL_HM_switch_124DEF_Siren room CUL_HM&lt;br /&gt;
define FileLog_CUL_HM_switch_124DEF_Siren FileLog /var/log/fhem/CUL_HM_switch_124DEF_Siren-%Y.log CUL_HM_switch_124DEF_Siren&lt;br /&gt;
attr FileLog_CUL_HM_switch_124DEF_Siren logtype text&lt;br /&gt;
attr FileLog_CUL_HM_switch_124DEF_Siren room CUL_HM&lt;br /&gt;
define CUL_HM_switch_124DEF_Flash CUL_HM 124DEF02&lt;br /&gt;
attr CUL_HM_switch_124DEF_Flash model HM-SEC-SFA-SM&lt;br /&gt;
attr CUL_HM_switch_124DEF_Flash room CUL_HM&lt;br /&gt;
define FileLog_CUL_HM_switch_124DEF_Flash FileLog /var/log/fhem/CUL_HM_switch_124DEF_Flash-%Y.log CUL_HM_switch_124DEF_Flash&lt;br /&gt;
attr FileLog_CUL_HM_switch_124DEF_Flash logtype text&lt;br /&gt;
attr FileLog_CUL_HM_switch_124DEF_Flash room CUL_HM&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
=== Log-Auszüge ===&lt;br /&gt;
&amp;amp;lt;Bitte ergänzen&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
[http://forum.fhem.de/index.php?t=msg&amp;amp;amp;th=10220&amp;amp;amp;start=0&amp;amp;amp;rid=417 Foren-Thread zum Gerät]&lt;br /&gt;
&lt;br /&gt;
Anleitung [http://www.eq-3.de/Downloads/eq3/pdf_produkte/HM-Sec-SFA-SM_UM_GE_091209.pdf PDF]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:HomeMatic Components]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=HM-WDS40-TH-I_Funk-Temperatur-/Feuchtesensor_innen_(IT)&amp;diff=6569</id>
		<title>HM-WDS40-TH-I Funk-Temperatur-/Feuchtesensor innen (IT)</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=HM-WDS40-TH-I_Funk-Temperatur-/Feuchtesensor_innen_(IT)&amp;diff=6569"/>
		<updated>2014-06-15T07:47:25Z</updated>

		<summary type="html">&lt;p&gt;Eisler: WDS40 übermittelt den Batteriestatus mit Firmware 1.3&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;HomeMatic HM-WDS40-TH-I Funk-Innensensor ITH&#039;&#039;&#039;&lt;br /&gt;
Homematic Innensensor für Temperatur und relative Luftfeuchte.&lt;br /&gt;
&lt;br /&gt;
== Features ==&lt;br /&gt;
* Batteriebetrieb (2 x AA)&lt;br /&gt;
* Wandaufhängung möglich&lt;br /&gt;
* Für Temp. von -20 °C bis +80 °C&lt;br /&gt;
* Datenübermittlung alle 120 bis 180 Sekunden&lt;br /&gt;
* Übermittlung des Batteriestatus&lt;br /&gt;
* Aktuelle Firmware: 1.3&lt;br /&gt;
&lt;br /&gt;
== Hinweise zum Betrieb mit FHEM ==&lt;br /&gt;
Das Pairing sollte wie unter [[HomeMatic Devices pairen]] beschrieben durchgeführt werden. Hierfür muss die von außen erreichbare Anlerntaste betätigt werden.&lt;br /&gt;
&lt;br /&gt;
=== Probleme ===&lt;br /&gt;
Bisher keine.&lt;br /&gt;
&lt;br /&gt;
=== FHEM Config-Auszug ===&lt;br /&gt;
 define TEMPERATUR_SENSOR CUL_HM xxxxxx&lt;br /&gt;
 attr TEMPERATUR_SENSOR .devInfo 030100&lt;br /&gt;
 attr TEMPERATUR_SENSOR .stc 70&lt;br /&gt;
 attr TEMPERATUR_SENSOR actCycle 000:10&lt;br /&gt;
 attr TEMPERATUR_SENSOR actStatus alive&lt;br /&gt;
 attr TEMPERATUR_SENSOR autoReadReg 4_reqStatus&lt;br /&gt;
 attr TEMPERATUR_SENSOR expert 2_full&lt;br /&gt;
 attr TEMPERATUR_SENSOR firmware 1.3&lt;br /&gt;
 attr TEMPERATUR_SENSOR model HM-WDS40-TH-I&lt;br /&gt;
 attr TEMPERATUR_SENSOR peerIDs 00000000,xxxxxxxx,&lt;br /&gt;
 attr TEMPERATUR_SENSOR room xxx&lt;br /&gt;
 attr TEMPERATUR_SENSOR serialNr KEQxxxxxxx&lt;br /&gt;
 attr TEMPERATUR_SENSOR subType THSensor&lt;br /&gt;
 define FileLog_TEMPERATUR_SENSOR FileLog ./log/TEMPERATUR_SENSOR-%Y.log TEMPERATUR_SENSOR&lt;br /&gt;
 attr FileLog_TEMPERATUR_SENSOR logtype text&lt;br /&gt;
 attr FileLog_TEMPERATUR_SENSOR room xxx&lt;br /&gt;
 define SVG_FileLog_TEMPERATUR_SENSOR_1 SVG FileLog_TEMPERATUR_SENSOR:SVG_FileLog_TEMPERATUR_SENSOR_1:CURRENT&lt;br /&gt;
 attr SVG_FileLog_TEMPERATUR_SENSOR_1 room xxx&lt;br /&gt;
&lt;br /&gt;
=== FHEM Log-Auszug ===&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&amp;amp;lt;Datum&amp;amp;gt;_&amp;amp;lt;Zeit&amp;amp;gt; CUL_HM_THSensor_1ABCDE T: 21.8 H: 56&lt;br /&gt;
&amp;amp;lt;Datum&amp;amp;gt;_&amp;amp;lt;Zeit&amp;amp;gt; CUL_HM_THSensor_1ABCDE T: 21.8 H: 56&lt;br /&gt;
&amp;amp;lt;Datum&amp;amp;gt;_&amp;amp;lt;Zeit&amp;amp;gt; CUL_HM_THSensor_1ABCDE T: 21.8 H: 56&lt;br /&gt;
&amp;amp;lt;Datum&amp;amp;gt;_&amp;amp;lt;Zeit&amp;amp;gt; CUL_HM_THSensor_1ABCDE T: 21.8 H: 56&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Device-Log ===&lt;br /&gt;
 &amp;lt;Datum&amp;gt;_&amp;lt;Zeit&amp;gt; CUL_HM_THSensor_1ABCDE temperature: 21.6&lt;br /&gt;
 &amp;lt;Datum&amp;gt;_&amp;lt;Zeit&amp;gt; CUL_HM_THSensor_1ABCDE humidity: 60&lt;br /&gt;
 &amp;lt;Datum&amp;gt;_&amp;lt;Zeit&amp;gt; CUL_HM_THSensor_1ABCDE battery: ok&lt;br /&gt;
 &amp;lt;Datum&amp;gt;_&amp;lt;Zeit&amp;gt; CUL_HM_THSensor_1ABCDE T: 21.6 H: 60&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
Anleitung [http://www.elv-downloads.de/service/manuals/76998_HM_GE_WDS40_TH_I_GE_V1_1.pdf PDF]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:HomeMatic Components]]&lt;br /&gt;
[[Kategorie:Feuchtesensoren]]&lt;br /&gt;
[[Kategorie:Temperatursensoren]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Umsetzung&amp;diff=5840</id>
		<title>Umsetzung</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Umsetzung&amp;diff=5840"/>
		<updated>2014-04-10T09:21:42Z</updated>

		<summary type="html">&lt;p&gt;Eisler: /* Ereignissteuerung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Die &#039;&#039;&#039;Umsetzung&#039;&#039;&#039; (Implementierung) eines Fhem-Projekts hat zumindest die Teilaspekte der Installation der Geräte und der Entwicklung des Interfaces mit den darüber ansprechbaren Funktionen. Für den Start dieser Phase sollte die [[Planung]] fortgeschritten sein, nach Abschluss der Implementierung kann der (Regel-)[[Betrieb]] aufgenommen werden.&lt;br /&gt;
&lt;br /&gt;
== Installation der Hardware ==&lt;br /&gt;
=== Sonsoren, Aktoren, Verkabelung ===&lt;br /&gt;
Sensoren und Aktoren müssen installiert und ggf. (bei kabelgebundenen Systemen) mit der Zentrale verbunden werden.&lt;br /&gt;
&lt;br /&gt;
=== Fhem Server und Interfaces ===&lt;br /&gt;
Die Hardware, auf der Fhem läuft, muss in Betrieb genommen und Fhem installiert werden.&lt;br /&gt;
&lt;br /&gt;
== Implementierung der Funktionalität ==&lt;br /&gt;
=== Definition und Beschreibung der Geräte ===&lt;br /&gt;
Alle Geräte müssen in Fhem definiert und ggf. mit zusätzlichen Attributen versehen werden. Die Basisdefinitionen von einigen Geräten wird schon durch die &#039;&#039;autocreate&#039;&#039; Funktion durchgeführt. Es ist sinnvoll, für die (Um-)Benennung von Geräten ein Namensschema festzulegen und zu benutzen.&lt;br /&gt;
&lt;br /&gt;
=== Bedienelemente ===&lt;br /&gt;
&#039;&#039;Bedienelemente&#039;&#039; umfasst alle Icons, Buttons, Texte etc., die auf der Bedienoberfläche angezeigt werden.&lt;br /&gt;
&lt;br /&gt;
=== Automatikfunktionen ===&lt;br /&gt;
Automatikfunktionen müssen definiert und getestet werden:&lt;br /&gt;
&lt;br /&gt;
==== Zeitsteuerung ====&lt;br /&gt;
Um Aktionen zeitgesteuert auszuführen, wird in der Regel das &#039;&#039;&#039;[http://fhem.de/commandref.html#at at]&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
==== Ereignissteuerung ====&lt;br /&gt;
Ereignisse werden mittels &#039;&#039;&#039;[http://fhem.de/commandref.html#notify notify]&#039;&#039;&#039; verarbeitet.&lt;br /&gt;
&lt;br /&gt;
=== Test, Probebetrieb ===&lt;br /&gt;
Für Test und Probebetrieb sowie zur Problembestimmung/-behebung stellt Fhem die folgenden Hilfsmittel zur Verfügung (Auswahl):&lt;br /&gt;
* Details über [[HomeMatic]](-Geräte): [http://fhem.de/commandref.html#HMinfo HMinfo]&lt;br /&gt;
* Performance-Analyse: [http://fhem.de/commandref.html#apptime apptime]&lt;br /&gt;
* Fhem-Systeminformationen: [http://fhem.de/commandref.html#fheminfo fheminfo]&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
== Sonstiges ==&lt;br /&gt;
Die [[Systemübersicht]] kann auch in dieser Phase als Orientierung dienen, bei der Realisierung der Funktionalität stellen [[Anwendungsszenarien]], [[:Kategorie:Examples|Beispiele]], [[:Kategorie:Code Snippets|Code-Schnipsel]] und letztendlich die [http://fhem.de/commandref.htm Befehlsreferenz (commandref)] die Informationsbasis dar.&lt;br /&gt;
&lt;br /&gt;
{{Todo|Diese Aufstellung sollte noch erweitert und mit sinnvollen Querverweisen an andere Stellen in diesem Wiki versehen werden}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:FHEM]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Umsetzung&amp;diff=5839</id>
		<title>Umsetzung</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Umsetzung&amp;diff=5839"/>
		<updated>2014-04-10T09:20:52Z</updated>

		<summary type="html">&lt;p&gt;Eisler: /* Zeitsteuerung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Die &#039;&#039;&#039;Umsetzung&#039;&#039;&#039; (Implementierung) eines Fhem-Projekts hat zumindest die Teilaspekte der Installation der Geräte und der Entwicklung des Interfaces mit den darüber ansprechbaren Funktionen. Für den Start dieser Phase sollte die [[Planung]] fortgeschritten sein, nach Abschluss der Implementierung kann der (Regel-)[[Betrieb]] aufgenommen werden.&lt;br /&gt;
&lt;br /&gt;
== Installation der Hardware ==&lt;br /&gt;
=== Sonsoren, Aktoren, Verkabelung ===&lt;br /&gt;
Sensoren und Aktoren müssen installiert und ggf. (bei kabelgebundenen Systemen) mit der Zentrale verbunden werden.&lt;br /&gt;
&lt;br /&gt;
=== Fhem Server und Interfaces ===&lt;br /&gt;
Die Hardware, auf der Fhem läuft, muss in Betrieb genommen und Fhem installiert werden.&lt;br /&gt;
&lt;br /&gt;
== Implementierung der Funktionalität ==&lt;br /&gt;
=== Definition und Beschreibung der Geräte ===&lt;br /&gt;
Alle Geräte müssen in Fhem definiert und ggf. mit zusätzlichen Attributen versehen werden. Die Basisdefinitionen von einigen Geräten wird schon durch die &#039;&#039;autocreate&#039;&#039; Funktion durchgeführt. Es ist sinnvoll, für die (Um-)Benennung von Geräten ein Namensschema festzulegen und zu benutzen.&lt;br /&gt;
&lt;br /&gt;
=== Bedienelemente ===&lt;br /&gt;
&#039;&#039;Bedienelemente&#039;&#039; umfasst alle Icons, Buttons, Texte etc., die auf der Bedienoberfläche angezeigt werden.&lt;br /&gt;
&lt;br /&gt;
=== Automatikfunktionen ===&lt;br /&gt;
Automatikfunktionen müssen definiert und getestet werden:&lt;br /&gt;
&lt;br /&gt;
==== Zeitsteuerung ====&lt;br /&gt;
Um Aktionen zeitgesteuert auszuführen, wird in der Regel das &#039;&#039;&#039;[http://fhem.de/commandref.html#at at]&#039;&#039;&#039; verwendet.&lt;br /&gt;
&lt;br /&gt;
==== Ereignissteuerung ====&lt;br /&gt;
Ereignisse werden mittels &#039;&#039;&#039;[http://fhem.de/commandref.htm#notify notify]&#039;&#039;&#039; verarbeitet.&lt;br /&gt;
&lt;br /&gt;
=== Test, Probebetrieb ===&lt;br /&gt;
Für Test und Probebetrieb sowie zur Problembestimmung/-behebung stellt Fhem die folgenden Hilfsmittel zur Verfügung (Auswahl):&lt;br /&gt;
* Details über [[HomeMatic]](-Geräte): [http://fhem.de/commandref.html#HMinfo HMinfo]&lt;br /&gt;
* Performance-Analyse: [http://fhem.de/commandref.html#apptime apptime]&lt;br /&gt;
* Fhem-Systeminformationen: [http://fhem.de/commandref.html#fheminfo fheminfo]&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
== Sonstiges ==&lt;br /&gt;
Die [[Systemübersicht]] kann auch in dieser Phase als Orientierung dienen, bei der Realisierung der Funktionalität stellen [[Anwendungsszenarien]], [[:Kategorie:Examples|Beispiele]], [[:Kategorie:Code Snippets|Code-Schnipsel]] und letztendlich die [http://fhem.de/commandref.htm Befehlsreferenz (commandref)] die Informationsbasis dar.&lt;br /&gt;
&lt;br /&gt;
{{Todo|Diese Aufstellung sollte noch erweitert und mit sinnvollen Querverweisen an andere Stellen in diesem Wiki versehen werden}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:FHEM]]&lt;/div&gt;</summary>
		<author><name>Eisler</name></author>
	</entry>
</feed>