<?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=Fhemrocks</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=Fhemrocks"/>
	<link rel="alternate" type="text/html" href="http://wiki.fhem.de/wiki/Spezial:Beitr%C3%A4ge/Fhemrocks"/>
	<updated>2026-05-07T13:03:32Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevelopmentGuidelinesAV&amp;diff=4848</id>
		<title>DevelopmentGuidelinesAV</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevelopmentGuidelinesAV&amp;diff=4848"/>
		<updated>2014-02-11T09:07:09Z</updated>

		<summary type="html">&lt;p&gt;Fhemrocks: /* Implementation in Modulen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
Auf dieser Seite sollen Richtlinien für AV-Module gesammelt werden damit es einfacher wird diese zusammen mit anderen Modulen wie remotecontroll oder LightScene zu verwenden. Auch Benachrichtigungen wie Sprachdurchsagen oder Einblendungen lassen sich universeller verwenden wenn sie bei allen Geräten die dies unterstützen gleich angesprochen werden.&lt;br /&gt;
&lt;br /&gt;
Der Text aus dem ursprünglichen Forumsthread war folgender:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::gerade ist ja mit den tv und verstärker modulen sowie dem remotecontrol modul ziemlich schwung in den bereich audio und video geräte gekommen. zusätzlich gibt es noch eine ganze reihe älterer module wie sonor und xbmc und zwei arten itunes anzusprechen und neue module wie für die web radios oder das raspberry multiroom stehen vor der tür.&lt;br /&gt;
&lt;br /&gt;
::wie wäre es sich rechtzeitig auf ein möglichst einheitiches kommando set zu verständigen damit grundlegende dinge wie play/pause/volume/next bei allen geräten einheitlich, in gleicher schreibweise und mit möglichst ähnlichen parametern funktionieren?&lt;br /&gt;
&lt;br /&gt;
::das würde module wie die remotecontrol aber auch structure und lightscene deutlich einfacher und nützlicher machen und auch alternative frontends erleichtern wenn bestimmte features wie audio,video oder cover über ein einheitliches schema markiert würden.&lt;br /&gt;
&lt;br /&gt;
::mein vorschlag wäre sich an das sonos modul anzulehnen weil es mir in dieser hinsicht am fortgeschrittensten erscheint und auch weitergehende features wie cover oder durchsagen anbietet.&lt;br /&gt;
&lt;br /&gt;
::zu vereinheitlichen wäre dann u.a.:&lt;br /&gt;
::- welche kommandos zu welchem zweck&lt;br /&gt;
::- kommandos sollten einheitlich geschrieben werden. also z.b. immer klein oder immer mixed case.&lt;br /&gt;
::- parameter sollten so weit möglich den gleichen wertebereich haben. also z.b. volume immer von 0-100.&lt;br /&gt;
::- wenn es aus irgendeinem grund noch ein gerätespezifischer wertebereich nötig ist sollte der zusätzlich vorhanden sein.&lt;br /&gt;
::- cover sollten immer auf die gleiche art gelesen werden können&lt;br /&gt;
::- ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Thread ist [http://forum.fhem.de/index.php?t=msg&amp;amp;th=13784&amp;amp;start=0&amp;amp;rid=430 hier] zu finden.&lt;br /&gt;
&lt;br /&gt;
== Bezeichner ==&lt;br /&gt;
siehe [[DevelopmentGuidelines#Bezeichnungen|Bezeichnungen allgmein]]&lt;br /&gt;
&lt;br /&gt;
== Kommandos ==&lt;br /&gt;
&lt;br /&gt;
Diese Tabelle soll eine einheitliche Definition von set-Befehlen aufzeigen. Dazu zählen auch ggf. Parameter und deren Bedeutung.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;on&#039;&#039;&#039; ||  || Schaltet das Gerät ein, so dass es benutzt werden kann.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;off&#039;&#039;&#039; ||  || Schaltet das Gerät aus.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;play&#039;&#039;&#039; ||  || Startet das Abspielen von Medien (Video, mp3, Radio stream etc.)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;pause&#039;&#039;&#039; ||  || Pausiert das Abspielen von Medien, stoppt es aber nicht.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;stop&#039;&#039;&#039; ||  || Stoppt (beendet) das Abspielen von Medien.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volume&#039;&#039;&#039; || 0 - 100 || Setzt die Lautstärke auf einen prozentualen Wert. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeStraight&#039;&#039;&#039; || X-Y || Setzt die Lautstärke auf den absoluten Wert, so wie er vom Gerät tatsächlich verwendet wird (z.B. dB)&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;&#039;Nur zu Verwenden, wenn das Gerät einen anderen Wertebereich als 0-100% intern verwendet.&#039;&#039; &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeUp&#039;&#039;&#039; || [0 - 100]  || Erhöht die Lautstärke um einen gerätespezifischen Schritt (z.b. 5%). Wenn die Schrittweite konfigurierbar ist soll das über das Attribut volumeStep erfolgen. Optional kann das Inkrement als optionaler Parameter mit angegeben werden.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeDown&#039;&#039;&#039; || [0 - 100] || Verringert die Lautstärke um einen gerätespezifischen Schritt (z.b. 5%). Wenn die Schrittweite konfigurierbar ist soll das über das Attribut volumeStep erfolgen. Optional kann das Dekrement als optionaler Parameter mit angegeben werden.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;mute&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on | off | toggle&amp;lt;/nowiki&amp;gt;  || Aktiviert, deaktiviert oder schaltet die Mutefunktion = Lautstärke der aktuellen Ausgabe wird verringert bzw. abgschaltet (Gerätespezifisch)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;repeat&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;one | all | off&amp;lt;/nowiki&amp;gt;  || Aktivieren der Repeatfunktion = wiederholen des aktuellen Titels/Albums&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;shuffle&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on | off&amp;lt;/nowiki&amp;gt;  || Aktiviert oder deaktiviert die Shufflefunktion = zufällige Wiedergabe.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channel&#039;&#039;&#039; || 0 - 9999 || Schaltet auf einen absoluten Sender- oder Programmspeicherplatz (Nicht zu verwechseln mit input)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channelUp&#039;&#039;&#039; ||  || Springt zum nächsten Sender- oder Programmspeicherplatz.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channelDown&#039;&#039;&#039; ||  || Springt zum vorherigen Sender- oder Programmspeicherplatz.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;remoteControl&#039;&#039;&#039; || # || Parameter ist Hersteller- und Gerätespezifisch. Funktion bei Geräten, die es erlauben die mitgelieferte Hardware Fernbedienung zu simulieren. Parameter in lowerCamelCaps (ggf. muss im Modul selber auf die Herstellerspezifische Schreibweise umcodiert werden; Alles groß- oder klein geschrieben)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;input&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;z.B. hdmi[1-n] | av[1-n] | usb | airplay | server [1-n]&amp;lt;/nowiki&amp;gt; || Auswahl von Hardwarekanälen (HDMI, DVB-S, PAL, DVI) oder Softwarekanälen (Airplay, IPTV, IP-Radiostream) die das zu steuernde Gerät aktiv schalten soll (zur Darstellung auf TV, Kanalwahl bei AV-Receivern etc.). Da diese Kanäle je nach Gerät und Modellreihen unterschiedliche Bezeichnungen besitzen, sollten alle möglichen Bezeichnungen in Form von lowerCamelCaps angeboten werden.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;output&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;z.B. Airtunes1,Airtunes2,Intern&amp;lt;/nowiki&amp;gt; || Auswahl des/der Ausgabegerätes.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;statusRequest&#039;&#039;&#039; || || Den aktuellen Status des Gerätes abfragen&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;3&amp;quot;| ... &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Readings ==&lt;br /&gt;
&lt;br /&gt;
Diese Tabelle soll eine einheitliche Wertedefinition von Readings. Dazu zählen auch die verwendeten Werte und deren Bedeutung.&lt;br /&gt;
&lt;br /&gt;
Wenn es zwischen reading und zugehörigen set eine 1:1 Beziehung gibt sollte sich der Inhalt des readings als Argument des set verwenden lassen um genau diesen dargestellten Zustand zu erreichen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name !! mögliche Werte !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;power&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on|off&amp;lt;/nowiki&amp;gt; || Ist das Gerät an oder aus?&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;presence&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;present|absent&amp;lt;/nowiki&amp;gt; || Ist das Gerät aktuell ansprechbar? Sollte das Gerät aufgrund abgeschalteten Stromzufuhr, o.ä. aktuell nicht steuerbar sein, so sollte dies mit dem Wert &amp;quot;absent&amp;quot; verdeutlicht werden. In solch einem Fall sollte ein set-Befehl eine entsprechende Fehlermeldung bringen. Evtl. StatusUpdate-Timer sollten ensprechende Fehlermeldungen nur einmal im Log, etc. festhalten und Fehlermeldungen beim nächsten Status-Update entsprechend unterdrücken um so die Logfiles nicht unnötig vollzuschreiben. Auch das presence reading sollte nur dann aktualisiert werden wenn sich der Status geändert hat um am timestamp sehen zu können wann das war. event-on-change-reading ist hierzu nicht ausreichend weil nur das Event unterdrückt wird der Timestamp sich aber trotzdem ändert. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volume&#039;&#039;&#039; || 0-100 || Der prozentuale Lautstärkepegel gemessen der maximal möglichen tatsächlichen Werte. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeStraight&#039;&#039;&#039; || X-Y || Der tatsächliche Lautstärkepegel des Gerätes, so wie er am Gerät angezeigt/verwendet wird. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;&#039;Nur zu Verwenden, wenn das Gerät einen anderen Wertebereich als 0-100% intern verwendet.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;mute&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on | off&amp;lt;/nowiki&amp;gt; || Ist das Gerät aktuell stumm geschaltet?&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;repeat&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;one | all | off&amp;lt;/nowiki&amp;gt; || Ist das Gerät im repeat modus?&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;shuffle&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on | off&amp;lt;/nowiki&amp;gt; || Ist das Gerät im shuffle modus?&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;input&#039;&#039;&#039; ||  &#039;&#039;aktuell gewählter Eingangskanal&#039;&#039; ||  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;output&#039;&#039;&#039; ||  &#039;&#039;aktuell gewählte(s) Ausgabegerät(e)&#039;&#039; ||  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channel&#039;&#039;&#039; || &#039;&#039;aktuell gewählter Eingangskanal entsprechend dem Gerät&#039;&#039;|| &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentArtist&#039;&#039;&#039; || &#039;&#039;aktueller Interpret&#039;&#039; || &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentAlbum&#039;&#039;&#039; || &#039;&#039;aktuelles Album&#039;&#039; ||  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentTitle&#039;&#039;&#039; || &#039;&#039;aktueller Titelname&#039;&#039; ||  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentMedia&#039;&#039;&#039; || &#039;&#039;&amp;quot;Name&amp;quot; der Wiedergabe&amp;quot;datei&amp;quot;&#039;&#039; ||  kann alles sein: Datei vom Filesystem, Stream aus dem Internet, m3u-URL oder was auch immer&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;playStatus&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;playing | paused | stopped&amp;lt;/nowiki&amp;gt; ||  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;state&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on | off | absent&amp;lt;/nowiki&amp;gt; || Sofern das Gerät empfangsbereit ist, soll der Schaltzustand des Gerätes (Reading: power, sofern anwendbar) zurückgegeben werden. Ansonsten ist die Abwesenheit des Gerätes durch den Wert &amp;quot;absent&amp;quot; anzuzeigen.&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;3&amp;quot;| ... &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Implementation in Modulen ==&lt;br /&gt;
{{yes}} = Implementiert {{planed}} = Implementation geplant/in Arbeit {{no}} = Nicht implementiert/Keine Implementation geplant&lt;br /&gt;
 &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! set-Befehl !!  LGTV !! LISTENLIVE !! STV !! VIERA !! YAMAHA_AVR !! iTunes !! ENIGMA2 !! ONKYO_AVR !! Squeezebox&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;on&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{yes-c}} || {{no-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;off&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} || {{yes-c}} || {{no-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;play&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{no-c}} || {{yes-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;pause&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{no-c}} || {{yes-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;stop&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{no-c}} || {{no-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volume&#039;&#039;&#039; ||  ||  || {{yes-c}} || {{yes-c}} ||   {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeStraight&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||   {{yes-c}} || {{no-c}} || {{no-c}} || {{planed-c}}|| {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeUp&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||   {{yes-c}} || {{planed-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeDown&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{planed-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;mute&#039;&#039;&#039; ||  ||  || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;repeat&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{no-c}} || {{yes-c}} || {{no-c}} || {{no-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;shuffle&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{no-c}}  || {{yes-c}} || {{no-c}} || {{no-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channel&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{no-c}}  || {{no-c}} || {{yes-c}} || {{planed-c}} || {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channelUp&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} || {{no-c}}    || {{no-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channelDown&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{no-c}}   || {{no-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;remoteControl&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;input&#039;&#039;&#039; || {{yes-c}} ||  ||  || {{no-c}} || {{yes-c}} || {{no-c}} || {{yes-c}} || {{yes-c}}|| {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;output&#039;&#039;&#039; || ||  ||  || || {{no-c}} || {{yes-c}} || {{no-c}} || {{no-c}} || {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;statusRequest&#039;&#039;&#039; ||  || {{yes-c}} ||  || {{yes-c}} ||  {{yes-c}} || {{planed-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
! Reading !!  LGTV !! LISTENLIVE !! STV !! VIERA !! YAMAHA_AVR !! iTunes !! ENIGMA2 !! ONKYO_AVR !! Squeezebox&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;power&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{planed-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;presence&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{planed-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volume&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||   {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeStraight&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{yes-c}} || {{no-c}} || {{no-c}} || {{planed-c}}|| {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;mute&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;repeat&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{no-c}} || {{yes-c}} || {{no-c}} || {{no-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;shuffle&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{no-c}} || {{yes-c}} || {{no-c}} || {{no-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;input&#039;&#039;&#039; ||  ||  ||  || || {{yes-c}} || {{no-c}} || {{yes-c}} || {{yes-c}} || {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;output&#039;&#039;&#039; || ||  ||  || || {{no-c}} || {{yes-c}} || {{no-c}} || {{no-c}} || {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channel&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{no-c}}  || {{no-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentArtist&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{yes-c}} || {{yes-c}} || {{no-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentAlbum&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{yes-c}} || {{yes-c}} || {{no-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentTitle&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{yes-c}} || {{yes-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentMedia&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{no-c}} || {{planed-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;playStatus&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{yes-c}}  || {{yes-c}} || {{planed-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;state&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Sonstiges ==&lt;br /&gt;
=== Wie funktioniert Cover Art? ===&lt;br /&gt;
Reading mit URL-Link oder als Gerätestatus???&lt;br /&gt;
&lt;br /&gt;
=== Wie funktionieren Sprachdurchsagen oder Text Einblendungen? ===&lt;br /&gt;
&lt;br /&gt;
Obwohl dies sehr stark davon abhängt, ob das jeweilige Gerät eine solche Funktion unterstützt, oder ob sie anderweitig (z.B. durch Google Service) umgesetzt werden können, so sollen dennoch solche Features über die folgenden Set-Kommandos angeboten werden.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Kommando !! Beispiel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;sayText&#039;&#039;&#039;|| &amp;lt;pre&amp;gt;set &#039;&#039;&amp;lt;device&amp;gt;&#039;&#039; sayText &amp;quot;Anruf in Abwesenheit&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
 || Dieses Kommando soll den übergebenen Text auf dem jeweiligen Device in Audio-Form wieder ausgeben. Hierbei kann auf einen Synthese-Anbieter im Internet zurückgegriffen werden, der ein solches Audiosample erzeugt, oder geräteinterne Kommandos verwendet werden, je nach dem was vorhanden und unterstützt wird.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;showText&#039;&#039;&#039; || &amp;lt;pre&amp;gt;set &#039;&#039;&amp;lt;device&amp;gt;&#039;&#039; showText &amp;quot;Anruf in Abwesenheit&amp;quot;&amp;lt;/pre&amp;gt;|| Dieses Kommando soll einen Text z.B. auf dem Display des Gerätes (oder Fernseher) in geeigneter Form anzeigen, sofern das Gerät eine solche Funktion unterstützt. &lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot;| Alternativvorschlag&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;message&#039;&#039;&#039; || &amp;lt;pre&amp;gt;set &#039;&#039;&amp;lt;device&amp;gt;&#039;&#039; message &amp;quot;Anruf in Abwesenheit&amp;quot; [audio|video]&amp;lt;/pre&amp;gt;|| Dieses Kommando soll eine Nachricht mit dem Gerät anzeigen (video) oder abspielen (audio), sofern das Gerät eine solche Funktion unterstützt. Ohne die optionale Wiedergabe-Art oder wenn die angegebene nicht unterstützt ist über die von diesem Gerät bevorzugte Methode. &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Wie funktionieren Playlisten? ===&lt;br /&gt;
&lt;br /&gt;
Sofern anwendbar, sollten diese mit dem Kommando &amp;lt;code&amp;gt;set &amp;lt;device&amp;gt; playlist XY&amp;lt;/code&amp;gt; aufrufbar sein. Dabei sollten die zur Verfügung stehenden Playlists bereits bekannt sein und als möglicher Kommando-Parameter angeboten werden. &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Fhemrocks</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevelopmentGuidelinesAV&amp;diff=4572</id>
		<title>DevelopmentGuidelinesAV</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevelopmentGuidelinesAV&amp;diff=4572"/>
		<updated>2014-01-23T13:16:51Z</updated>

		<summary type="html">&lt;p&gt;Fhemrocks: /* Implementation in Modulen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
Auf dieser Seite sollen Richtlinien für AV-Module gesammelt werden damit es einfacher wird diese zusammen mit anderen Modulen wie remotecontroll oder LightScene zu verwenden. Auch Benachrichtigungen wie Sprachdurchsagen oder Einblendungen lassen sich universeller verwenden wenn sie bei allen Geräten die dies unterstützen gleich angesprochen werden.&lt;br /&gt;
&lt;br /&gt;
Der Text aus dem ursprünglichen Forumsthread war folgender:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::gerade ist ja mit den tv und verstärker modulen sowie dem remotecontrol modul ziemlich schwung in den bereich audio und video geräte gekommen. zusätzlich gibt es noch eine ganze reihe älterer module wie sonor und xbmc und zwei arten itunes anzusprechen und neue module wie für die web radios oder das raspberry multiroom stehen vor der tür.&lt;br /&gt;
&lt;br /&gt;
::wie wäre es sich rechtzeitig auf ein möglichst einheitiches kommando set zu verständigen damit grundlegende dinge wie play/pause/volume/next bei allen geräten einheitlich, in gleicher schreibweise und mit möglichst ähnlichen parametern funktionieren?&lt;br /&gt;
&lt;br /&gt;
::das würde module wie die remotecontrol aber auch structure und lightscene deutlich einfacher und nützlicher machen und auch alternative frontends erleichtern wenn bestimmte features wie audio,video oder cover über ein einheitliches schema markiert würden.&lt;br /&gt;
&lt;br /&gt;
::mein vorschlag wäre sich an das sonos modul anzulehnen weil es mir in dieser hinsicht am fortgeschrittensten erscheint und auch weitergehende features wie cover oder durchsagen anbietet.&lt;br /&gt;
&lt;br /&gt;
::zu vereinheitlichen wäre dann u.a.:&lt;br /&gt;
::- welche kommandos zu welchem zweck&lt;br /&gt;
::- kommandos sollten einheitlich geschrieben werden. also z.b. immer klein oder immer mixed case.&lt;br /&gt;
::- parameter sollten so weit möglich den gleichen wertebereich haben. also z.b. volume immer von 0-100.&lt;br /&gt;
::- wenn es aus irgendeinem grund noch ein gerätespezifischer wertebereich nötig ist sollte der zusätzlich vorhanden sein.&lt;br /&gt;
::- cover sollten immer auf die gleiche art gelesen werden können&lt;br /&gt;
::- ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Thread ist [http://forum.fhem.de/index.php?t=msg&amp;amp;th=13784&amp;amp;start=0&amp;amp;rid=430 hier] zu finden.&lt;br /&gt;
&lt;br /&gt;
== Bezeichner ==&lt;br /&gt;
siehe [[DevelopmentGuidelines#Bezeichnungen|Bezeichnungen allgmein]]&lt;br /&gt;
&lt;br /&gt;
== Kommandos ==&lt;br /&gt;
&lt;br /&gt;
Diese Tabelle soll eine einheitliche Definition von set-Befehlen aufzeigen. Dazu zählen auch ggf. Parameter und deren Bedeutung.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;on&#039;&#039;&#039; ||  || Schaltet das Gerät ein, so dass es benutzt werden kann.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;off&#039;&#039;&#039; ||  || Schaltet das Gerät aus, so dass es nicht mehr benutzt werden kann.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;play&#039;&#039;&#039; ||  || Startet das Abspielen von Medien (Video, mp3, Radio stream etc.)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;pause&#039;&#039;&#039; ||  || Pausiert das Abspielen von Medien, stoppt es aber nicht.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;stop&#039;&#039;&#039; ||  || Stoppt (beendet) das Abspielen von Medien.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volume&#039;&#039;&#039; || 0 - 100 || Setzt die Lautstärke auf einen prozentualen Wert. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeStraight&#039;&#039;&#039; || X-Y || Setzt die Lautstärke auf den absoluten Wert, so wie er vom Gerät tatsächlich verwendet wird (z.B. dB)&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;&#039;Nur zu Verwenden, wenn das Gerät einen anderen Wertebereich als 0-100% intern verwendet.&#039;&#039; &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeUp&#039;&#039;&#039; || [0 - 100]  || Erhöht die Lautstärke um einen gerätespezifischen Schritt (z.b. 5%). Wenn die Schrittweite konfigurierbar ist soll das über das Attribut volumeStep erfolgen. Optional kann das Inkrement als optionaler Parameter mit angegeben werden.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeDown&#039;&#039;&#039; || [0 - 100] || Verringert die Lautstärke um einen gerätespezifischen Schritt (z.b. 5%). Wenn die Schrittweite konfigurierbar ist soll das über das Attribut volumeStep erfolgen. Optional kann das Dekrement als optionaler Parameter mit angegeben werden.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;mute&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on | off | toggle&amp;lt;/nowiki&amp;gt;  || Aktiviert, deaktiviert oder schaltet die Mutefunktion = Lautstärke der aktuellen Ausgabe wird verringert bzw. abgschaltet (Gerätespezifisch)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;repeat&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;one | all | off&amp;lt;/nowiki&amp;gt;  || Aktivieren der Repeatfunktion = wiederholen des aktuellen Titels/Albums&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;shuffle&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on | off&amp;lt;/nowiki&amp;gt;  || Aktiviert oder deaktiviert die Shufflefunktion = zufällige Wiedergabe.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channel&#039;&#039;&#039; || 0 - 9999 || ??? Schaltet auf einen absoluten Fernsehkanal (Nicht zu verwechseln mit input)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channelUp&#039;&#039;&#039; ||  || Erhöht den Fernsehkanal.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channelDown&#039;&#039;&#039; ||  || Verringert den Fernsehkanal.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;remoteControl&#039;&#039;&#039; || # || Parameter ist Hersteller- und Gerätespezifisch. Funktion bei Geräten, die es erlauben die mitgelieferte Hardware Fernbedienung zu simulieren. Parameter in lowerCamelCaps (ggf. muss im Modul selber auf die Herstellerspezifische Schreibweise umcodiert werden; Alles groß- oder klein geschrieben)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;input&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;z.B. hdmi[1-n] | av[1-n] | usb | airplay | server [1-n]&amp;lt;/nowiki&amp;gt; || Auswahl von Hardwarekanälen (HDMI, DVB-S, PAL, DVI) oder Softwarekanälen (Airplay, IPTV, IP-Radiostream) die das zu steuernde Gerät aktiv schalten soll (zur Darstellung auf TV, Kanalwahl bei AV-Receivern etc.). Da diese Kanäle je nach Gerät und Modellreihen unterschiedliche Bezeichnungen besitzen, sollten alle möglichen Bezeichnungen in Form von lowerCamelCaps angeboten werden.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;output&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;z.B. Airtunes1,Airtunes2,Intern&amp;lt;/nowiki&amp;gt; || Auswahl des/der Ausgabegerätes.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;statusRequest&#039;&#039;&#039; || || Den aktuellen Status des Gerätes abfragen&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;3&amp;quot;| ... &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Readings ==&lt;br /&gt;
&lt;br /&gt;
Diese Tabelle soll eine einheitliche Wertedefinition von Readings. Dazu zählen auch die verwendeten Werte und deren Bedeutung.&lt;br /&gt;
&lt;br /&gt;
Wenn es zwischen reading und zugehörigen set eine 1:1 Beziehung gibt sollte sich der Inhalt des readings als Argument des set verwenden lassen um genau diesen dargestellten Zustand zu erreichen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name !! mögliche Werte !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;power&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on|off&amp;lt;/nowiki&amp;gt; || Ist das Gerät an oder aus?&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;presence&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;present|absent&amp;lt;/nowiki&amp;gt; || Ist das Gerät aktuell ansprechbar? Sollte das Gerät aufgrund abgeschalteten Stromzufuhr, o.ä. aktuell nicht steuerbar sein, so sollte dies mit dem Wert &amp;quot;absent&amp;quot; verdeutlicht werden. In solch einem Fall sollte ein set-Befehl eine entsprechende Fehlermeldung bringen. Evtl. StatusUpdate-Timer sollten ensprechende Fehlermeldungen nur einmal im Log, etc. festhalten und Fehlermeldungen beim nächsten Status-Update entsprechend unterdrücken um so die Logfiles nicht unnötig vollzuschreiben. Auch das presence reading sollte nur dann aktualisiert werden wenn sich der Status geändert hat um am timestamp sehen zu können wann das war. event-on-change-reading ist hierzu nicht ausreichend weil nur das Event unterdrückt wird der Timestamp sich aber trotzdem ändert. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volume&#039;&#039;&#039; || 0-100 || Der prozentuale Lautstärkepegel gemessen der maximal möglichen tatsächlichen Werte. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeStraight&#039;&#039;&#039; || X-Y || Der tatsächliche Lautstärkepegel des Gerätes, so wie er am Gerät angezeigt/verwendet wird. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;&#039;Nur zu Verwenden, wenn das Gerät einen anderen Wertebereich als 0-100% intern verwendet.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;mute&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on | off&amp;lt;/nowiki&amp;gt; || Ist das Gerät aktuell stumm geschaltet?&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;repeat&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;one | all | off&amp;lt;/nowiki&amp;gt; || Ist das Gerät im repeat modus?&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;shuffle&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on | off&amp;lt;/nowiki&amp;gt; || Ist das Gerät im shuffle modus?&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;input&#039;&#039;&#039; ||  &#039;&#039;aktuell gewählter Eingangskanal&#039;&#039; ||  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;output&#039;&#039;&#039; ||  &#039;&#039;aktuell gewählte(s) Ausgabegerät(e)&#039;&#039; ||  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channel&#039;&#039;&#039; || &#039;&#039;aktuell gewählter Eingangskanal entsprechend dem Gerät&#039;&#039;|| &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentArtist&#039;&#039;&#039; || &#039;&#039;aktueller Interpret&#039;&#039; || &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentAlbum&#039;&#039;&#039; || &#039;&#039;aktuelles Album&#039;&#039; ||  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentTitle&#039;&#039;&#039; || &#039;&#039;aktueller Titelname&#039;&#039; ||  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentMedia&#039;&#039;&#039; || &#039;&#039;&amp;quot;Name&amp;quot; der Wiedergabe&amp;quot;datei&amp;quot;&#039;&#039; ||  kann alles sein: Datei vom Filesystem, Stream aus dem Internet, m3u-URL oder was auch immer&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;playStatus&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;playing | paused | stopped&amp;lt;/nowiki&amp;gt; ||  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;state&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on | off | absent&amp;lt;/nowiki&amp;gt; || Sofern das Gerät empfangsbereit ist, soll der Schaltzustand des Gerätes (Reading: power, sofern anwendbar) zurückgegeben werden. Ansonsten ist die Abwesenheit des Gerätes durch den Wert &amp;quot;absent&amp;quot; anzuzeigen.&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;3&amp;quot;| ... &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Implementation in Modulen ==&lt;br /&gt;
{{yes}} = Implementiert {{planed}} = Implementation geplant/in Arbeit {{no}} = Nicht implementiert/Keine Implementation geplant&lt;br /&gt;
 &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! set-Befehl !!  LGTV !! LISTENLIVE !! STV !! VIERA !! YAMAHA_AVR !! iTunes !! ENIGMA2 !! ONKYO_AVR !! Squeezebox&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;on&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{yes-c}} || {{no-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;off&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} || {{yes-c}} || {{no-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;play&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{no-c}} || {{yes-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;pause&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{no-c}} || {{yes-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;stop&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{no-c}} || {{no-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volume&#039;&#039;&#039; ||  ||  || {{yes-c}} || {{yes-c}} ||   {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeStraight&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||   {{yes-c}} || {{no-c}} || {{no-c}} || {{planed-c}}|| {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeUp&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||   {{yes-c}} || {{planed-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeDown&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{planed-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;mute&#039;&#039;&#039; ||  ||  || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;repeat&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{no-c}} || {{yes-c}} || {{no-c}} || {{no-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;shuffle&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{no-c}}  || {{yes-c}} || {{no-c}} || {{no-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channel&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{no-c}}  || {{no-c}} || {{yes-c}} || {{planed-c}} || {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channelUp&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} || {{no-c}}    || {{no-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channelDown&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{no-c}}   || {{no-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;remoteControl&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;input&#039;&#039;&#039; || {{yes-c}} ||  ||  || {{no-c}} || {{yes-c}} || {{no-c}} || {{yes-c}} || {{yes-c}}|| {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;output&#039;&#039;&#039; || ||  ||  || || {{no-c}} || {{yes-c}} || {{no-c}} || {{no-c}} || {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;statusRequest&#039;&#039;&#039; ||  || {{yes-c}} ||  || {{yes-c}} ||  {{yes-c}} || {{planed-c}} || {{yes-c}} || {{yes-c}} || {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
! Reading !!  LGTV !! LISTENLIVE !! STV !! VIERA !! YAMAHA_AVR !! iTunes !! ENIGMA2 !! ONKYO_AVR !! Squeezebox&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;power&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{planed-c}} || {{yes-c}} || {{yes-c}} || {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;presence&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{planed-c}} || {{yes-c}} || {{yes-c}} || {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volume&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||   {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeStraight&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{yes-c}} || {{no-c}} || {{no-c}} || {{planed-c}}|| {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;mute&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;repeat&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{no-c}} || {{yes-c}} || {{no-c}} || {{no-c}} || {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;shuffle&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{no-c}} || {{yes-c}} || {{no-c}} || {{no-c}} || {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;input&#039;&#039;&#039; ||  ||  ||  || || {{yes-c}} || {{no-c}} || {{yes-c}} || {{yes-c}} || {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;output&#039;&#039;&#039; || ||  ||  || || {{no-c}} || {{yes-c}} || {{no-c}} || {{no-c}} || {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channel&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{no-c}}  || {{no-c}} || {{yes-c}} || {{planed-c}} || {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentArtist&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{yes-c}} || {{yes-c}} || {{no-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentAlbum&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{yes-c}} || {{yes-c}} || {{no-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentTitle&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{yes-c}} || {{yes-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentMedia&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{no-c}} || {{planed-c}} || {{yes-c}} || {{planed-c}} || {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;playStatus&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{yes-c}}  || {{yes-c}} || {{planed-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;state&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Sonstiges ==&lt;br /&gt;
=== Wie funktioniert Cover Art? ===&lt;br /&gt;
Reading mit URL-Link oder als Gerätestatus???&lt;br /&gt;
&lt;br /&gt;
=== Wie funktionieren Sprachdurchsagen oder Text Einblendungen? ===&lt;br /&gt;
&lt;br /&gt;
Obwohl dies sehr stark davon abhängt, ob das jeweilige Gerät eine solche Funktion unterstützt, oder ob sie anderweitig (z.B. durch Google Service) umgesetzt werden können, so sollen dennoch solche Features über die folgenden Set-Kommandos angeboten werden.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Kommando !! Beispiel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;sayText&#039;&#039;&#039;|| &amp;lt;pre&amp;gt;set &#039;&#039;&amp;lt;device&amp;gt;&#039;&#039; sayText &amp;quot;Anruf in Abwesenheit&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
 || Dieses Kommando soll den übergebenen Text auf dem jeweiligen Device in Audio-Form wieder ausgeben. Hierbei kann auf einen Synthese-Anbieter im Internet zurückgegriffen werden, der ein solches Audiosample erzeugt, oder geräteinterne Kommandos verwendet werden, je nach dem was vorhanden und unterstützt wird.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;showText&#039;&#039;&#039; || &amp;lt;pre&amp;gt;set &#039;&#039;&amp;lt;device&amp;gt;&#039;&#039; showText &amp;quot;Anruf in Abwesenheit&amp;quot;&amp;lt;/pre&amp;gt;|| Dieses Kommando soll einen Text z.B. auf dem Display des Gerätes (oder Fernseher) in geeigneter Form anzeigen, sofern das Gerät eine solche Funktion unterstützt. &lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot;| Alternativvorschlag&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;message&#039;&#039;&#039; || &amp;lt;pre&amp;gt;set &#039;&#039;&amp;lt;device&amp;gt;&#039;&#039; message &amp;quot;Anruf in Abwesenheit&amp;quot; [audio|video]&amp;lt;/pre&amp;gt;|| Dieses Kommando soll eine Nachricht mit dem Gerät anzeigen (video) oder abspielen (audio), sofern das Gerät eine solche Funktion unterstützt. Ohne die optionale Wiedergabe-Art oder wenn die angegebene nicht unterstützt ist über die von diesem Gerät bevorzugte Methode. &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Wie funktionieren Playlisten? ===&lt;br /&gt;
&lt;br /&gt;
Sofern anwendbar, sollten diese mit dem Kommando &amp;lt;code&amp;gt;set &amp;lt;device&amp;gt; playlist XY&amp;lt;/code&amp;gt; aufrufbar sein. Dabei sollten die zur Verfügung stehenden Playlists bereits bekannt sein und als möglicher Kommando-Parameter angeboten werden. &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Fhemrocks</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevelopmentGuidelinesAV&amp;diff=4481</id>
		<title>DevelopmentGuidelinesAV</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevelopmentGuidelinesAV&amp;diff=4481"/>
		<updated>2014-01-20T16:18:11Z</updated>

		<summary type="html">&lt;p&gt;Fhemrocks: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
Auf dieser Seite sollen Richtlinien für AV-Module gesammelt werden damit es einfacher wird diese zusammen mit anderen Modulen wie remotecontroll oder LightScene zu verwenden. Auch Benachrichtigungen wie Sprachdurchsagen oder Einblendungen lassen sich universeller verwenden wenn sie bei allen Geräten die dies unterstützen gleich angesprochen werden.&lt;br /&gt;
&lt;br /&gt;
Der Text aus dem ursprünglichen Forumsthread war folgender:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
::gerade ist ja mit den tv und verstärker modulen sowie dem remotecontrol modul ziemlich schwung in den bereich audio und video geräte gekommen. zusätzlich gibt es noch eine ganze reihe älterer module wie sonor und xbmc und zwei arten itunes anzusprechen und neue module wie für die web radios oder das raspberry multiroom stehen vor der tür.&lt;br /&gt;
&lt;br /&gt;
::wie wäre es sich rechtzeitig auf ein möglichst einheitiches kommando set zu verständigen damit grundlegende dinge wie play/pause/volume/next bei allen geräten einheitlich, in gleicher schreibweise und mit möglichst ähnlichen parametern funktionieren?&lt;br /&gt;
&lt;br /&gt;
::das würde module wie die remotecontrol aber auch structure und lightscene deutlich einfacher und nützlicher machen und auch alternative frontends erleichtern wenn bestimmte features wie audio,video oder cover über ein einheitliches schema markiert würden.&lt;br /&gt;
&lt;br /&gt;
::mein vorschlag wäre sich an das sonos modul anzulehnen weil es mir in dieser hinsicht am fortgeschrittensten erscheint und auch weitergehende features wie cover oder durchsagen anbietet.&lt;br /&gt;
&lt;br /&gt;
::zu vereinheitlichen wäre dann u.a.:&lt;br /&gt;
::- welche kommandos zu welchem zweck&lt;br /&gt;
::- kommandos sollten einheitlich geschrieben werden. also z.b. immer klein oder immer mixed case.&lt;br /&gt;
::- parameter sollten so weit möglich den gleichen wertebereich haben. also z.b. volume immer von 0-100.&lt;br /&gt;
::- wenn es aus irgendeinem grund noch ein gerätespezifischer wertebereich nötig ist sollte der zusätzlich vorhanden sein.&lt;br /&gt;
::- cover sollten immer auf die gleiche art gelesen werden können&lt;br /&gt;
::- ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Der Thread ist [http://forum.fhem.de/index.php?t=msg&amp;amp;th=13784&amp;amp;start=0&amp;amp;rid=430 hier] zu finden.&lt;br /&gt;
&lt;br /&gt;
== Bezeichner ==&lt;br /&gt;
siehe [[DevelopmentGuidelines#Bezeichnungen|Bezeichnungen allgmein]]&lt;br /&gt;
&lt;br /&gt;
== Kommandos ==&lt;br /&gt;
&lt;br /&gt;
Diese Tabelle soll eine einheitliche Definition von set-Befehlen aufzeigen. Dazu zählen auch ggf. Parameter und deren Bedeutung.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name !! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;on&#039;&#039;&#039; ||  || Schaltet das Gerät ein, so dass es benutzt werden kann.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;off&#039;&#039;&#039; ||  || Schaltet das Gerät aus, so dass es nicht mehr benutzt werden kann.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;play&#039;&#039;&#039; ||  || Startet das Abspielen von Medien (Video, mp3, Radio stream etc.)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;pause&#039;&#039;&#039; ||  || Pausiert das Abspielen von Medien, stoppt es aber nicht.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;stop&#039;&#039;&#039; ||  || Stoppt (beendet) das Abspielen von Medien.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volume&#039;&#039;&#039; || 0 - 100 || Setzt die Lautstärke auf einen prozentualen Wert. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeStraight&#039;&#039;&#039; || X-Y || Setzt die Lautstärke auf den absoluten Wert, so wie er vom Gerät tatsächlich verwendet wird (z.B. dB)&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;&#039;Nur zu Verwenden, wenn das Gerät einen anderen Wertebereich als 0-100% intern verwendet.&#039;&#039; &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeUp&#039;&#039;&#039; || [0 - 100]  || Erhöht die Lautstärke um einen gerätespezifischen Schritt (z.b. 5%). Wenn die Schrittweite konfigurierbar ist soll das über das Attribut volumeStep erfolgen. Optional kann das Inkrement als optionaler Parameter mit angegeben werden.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeDown&#039;&#039;&#039; || [0 - 100] || Verringert die Lautstärke um einen gerätespezifischen Schritt (z.b. 5%). Wenn die Schrittweite konfigurierbar ist soll das über das Attribut volumeStep erfolgen. Optional kann das Dekrement als optionaler Parameter mit angegeben werden.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;mute&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on | off | toggle&amp;lt;/nowiki&amp;gt;  || Aktiviert, deaktiviert oder schaltet die Mutefunktion = Lautstärke der aktuellen Ausgabe wird verringert bzw. abgschaltet (Gerätespezifisch)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;repeat&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;one | all | off&amp;lt;/nowiki&amp;gt;  || Aktivieren der Repeatfunktion = wiederholen des aktuellen Titels/Albums&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;shuffle&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on | off&amp;lt;/nowiki&amp;gt;  || Aktiviert oder deaktiviert die Shufflefunktion = zufällige Wiedergabe.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channel&#039;&#039;&#039; || 0 - 9999 || ??? Schaltet auf einen absoluten Fernsehkanal (Nicht zu verwechseln mit input)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channelUp&#039;&#039;&#039; ||  || Erhöht den Fernsehkanal.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channelDown&#039;&#039;&#039; ||  || Verringert den Fernsehkanal.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;remoteControl&#039;&#039;&#039; || # || Parameter ist Hersteller- und Gerätespezifisch. Funktion bei Geräten, die es erlauben die mitgelieferte Hardware Fernbedienung zu simulieren. Parameter in lowerCamelCaps (ggf. muss im Modul selber auf die Herstellerspezifische Schreibweise umcodiert werden; Alles groß- oder klein geschrieben)&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;input&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;z.B. hdmi[1-n] | av[1-n] | usb | airplay | server [1-n]&amp;lt;/nowiki&amp;gt; || Auswahl von Hardwarekanälen (HDMI, DVB-S, PAL, DVI) oder Softwarekanälen (Airplay, IPTV, IP-Radiostream) die das zu steuernde Gerät aktiv schalten soll (zur Darstellung auf TV, Kanalwahl bei AV-Receivern etc.). Da diese Kanäle je nach Gerät und Modellreihen unterschiedliche Bezeichnungen besitzen, sollten alle möglichen Bezeichnungen in Form von lowerCamelCaps angeboten werden.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;output&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;z.B. Airtunes1,Airtunes2,Intern&amp;lt;/nowiki&amp;gt; || Auswahl des/der Ausgabegerätes.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;statusRequest&#039;&#039;&#039; || || Den aktuellen Status des Gerätes abfragen&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;3&amp;quot;| ... &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Readings ==&lt;br /&gt;
&lt;br /&gt;
Diese Tabelle soll eine einheitliche Wertedefinition von Readings. Dazu zählen auch die verwendeten Werte und deren Bedeutung.&lt;br /&gt;
&lt;br /&gt;
Wenn es zwischen reading und zugehörigen set eine 1:1 Beziehung gibt sollte sich der Inhalt des readings als Argument des set verwenden lassen um genau diesen dargestellten Zustand zu erreichen.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name !! mögliche Werte !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;power&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on|off&amp;lt;/nowiki&amp;gt; || Ist das Gerät an oder aus?&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;presence&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;present|absent&amp;lt;/nowiki&amp;gt; || Ist das Gerät aktuell ansprechbar? Sollte das Gerät aufgrund abgeschalteten Stromzufuhr, o.ä. aktuell nicht steuerbar sein, so sollte dies mit dem Wert &amp;quot;absent&amp;quot; verdeutlicht werden. In solch einem Fall sollte ein set-Befehl eine entsprechende Fehlermeldung bringen. Evtl. StatusUpdate-Timer sollten ensprechende Fehlermeldungen nur einmal im Log, etc. festhalten und Fehlermeldungen beim nächsten Status-Update entsprechend unterdrücken um so die Logfiles nicht unnötig vollzuschreiben. Auch das presence reading sollte nur dann aktualisiert werden wenn sich der Status geändert hat um am timestamp sehen zu können wann das war. event-on-change-reading ist hierzu nicht ausreichend weil nur das Event unterdrückt wird der Timestamp sich aber trotzdem ändert. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volume&#039;&#039;&#039; || 0-100 || Der prozentuale Lautstärkepegel gemessen der maximal möglichen tatsächlichen Werte. &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeStraight&#039;&#039;&#039; || X-Y || Der tatsächliche Lautstärkepegel des Gerätes, so wie er am Gerät angezeigt/verwendet wird. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&#039;&#039;Nur zu Verwenden, wenn das Gerät einen anderen Wertebereich als 0-100% intern verwendet.&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;mute&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on | off&amp;lt;/nowiki&amp;gt; || Ist das Gerät aktuell stumm geschaltet?&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;repeat&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;one | all | off&amp;lt;/nowiki&amp;gt; || Ist das Gerät im repeat modus?&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;shuffle&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on | off&amp;lt;/nowiki&amp;gt; || Ist das Gerät im shuffle modus?&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;input&#039;&#039;&#039; ||  &#039;&#039;aktuell gewählter Eingangskanal&#039;&#039; ||  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;output&#039;&#039;&#039; ||  &#039;&#039;aktuell gewählte(s) Ausgabegerät(e)&#039;&#039; ||  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channel&#039;&#039;&#039; || &#039;&#039;aktuell gewählter Eingangskanal entsprechend dem Gerät&#039;&#039;|| &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentArtist&#039;&#039;&#039; || &#039;&#039;aktueller Interpret&#039;&#039; || &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentAlbum&#039;&#039;&#039; || &#039;&#039;aktuelles Album&#039;&#039; ||  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentTitle&#039;&#039;&#039; || &#039;&#039;aktueller Titelname&#039;&#039; ||  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentMedia&#039;&#039;&#039; || &#039;&#039;&amp;quot;Name&amp;quot; der Wiedergabe&amp;quot;datei&amp;quot;&#039;&#039; ||  kann alles sein: Datei vom Filesystem, Stream aus dem Internet, m3u-URL oder was auch immer&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;playStatus&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;playing | paused | stopped&amp;lt;/nowiki&amp;gt; ||  &lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;state&#039;&#039;&#039; || &amp;lt;nowiki&amp;gt;on | off | absent&amp;lt;/nowiki&amp;gt; || Sofern das Gerät empfangsbereit ist, soll der Schaltzustand des Gerätes (Reading: power, sofern anwendbar) zurückgegeben werden. Ansonsten ist die Abwesenheit des Gerätes durch den Wert &amp;quot;absent&amp;quot; anzuzeigen.&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;3&amp;quot;| ... &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Implementation in Modulen ==&lt;br /&gt;
{{yes}} = Implementiert {{planed}} = Implementation geplant/in Arbeit {{no}} = Nicht implementiert/Keine Implementation geplant&lt;br /&gt;
 &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! set-Befehl !!  LGTV !! LISTENLIVE !! STV !! VIERA !! YAMAHA_AVR !! iTunes !! ENIGMA2 !! ONKYO_AVR !! Squeezebox&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;on&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{yes-c}} || {{no-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;off&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} || {{yes-c}} || {{no-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;play&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{no-c}} || {{yes-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;pause&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{no-c}} || {{yes-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;stop&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{no-c}} || {{no-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volume&#039;&#039;&#039; ||  ||  || {{yes-c}} || {{yes-c}} ||   {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeStraight&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||   {{yes-c}} || {{no-c}} || {{no-c}} || {{planed-c}}|| {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeUp&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||   {{yes-c}} || {{planed-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeDown&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{planed-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;mute&#039;&#039;&#039; ||  ||  || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;repeat&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{no-c}} || {{yes-c}} || {{no-c}} || {{no-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;shuffle&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{no-c}}  || {{yes-c}} || {{no-c}} || {{no-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channel&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{no-c}}  || {{no-c}} || {{yes-c}} || {{planed-c}} || {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channelUp&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} || {{no-c}}    || {{no-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channelDown&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{no-c}}   || {{no-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;remoteControl&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;input&#039;&#039;&#039; || {{yes-c}} ||  ||  || {{no-c}} || {{yes-c}} || {{no-c}} || {{yes-c}} || {{yes-c}}|| {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;output&#039;&#039;&#039; || ||  ||  || || {{no-c}} || {{yes-c}} || {{no-c}} || {{no-c}} || {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;statusRequest&#039;&#039;&#039; ||  || {{yes-c}} ||  || {{yes-c}} ||  {{yes-c}} || {{planed-c}} || {{yes-c}} || {{yes-c}} || {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
! Reading !!  LGTV !! LISTENLIVE !! STV !! VIERA !! YAMAHA_AVR !! iTunes !! ENIGMA2 !! ONKYO_AVR !! Squeezebox&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;power&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{planed-c}} || {{yes-c}} || {{yes-c}} || {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;presence&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{planed-c}} || {{yes-c}} || {{yes-c}} || {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volume&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||   {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;volumeStraight&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{yes-c}} || {{no-c}} || {{no-c}} || {{planed-c}}|| {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;mute&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;repeat&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{no-c}} || {{yes-c}} || {{no-c}} || {{no-c}} || {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;shuffle&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{no-c}} || {{yes-c}} || {{no-c}} || {{no-c}} || {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;input&#039;&#039;&#039; ||  ||  ||  || || {{yes-c}} || {{no-c}} || {{yes-c}} || {{yes-c}} || {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;output&#039;&#039;&#039; || ||  ||  || || {{no-c}} || {{yes-c}} || {{no-c}} || {{no-c}} || {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;channel&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{no-c}}  || {{no-c}} || {{yes-c}} || {{planed-c}} || {{planed-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentArtist&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{yes-c}} || {{yes-c}} || {{no-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentAlbum&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{yes-c}} || {{yes-c}} || {{no-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentTitle&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{yes-c}} || {{yes-c}} || {{yes-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;currentMedia&#039;&#039;&#039; ||  ||  ||  || {{no-c}} ||  {{no-c}} || {{planed-c}} || {{yes-c}} || {{planed-c}} || {{no-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;playStatus&#039;&#039;&#039; ||  ||  ||  || {{no-c}} || {{yes-c}}  || {{yes-c}} || {{planed-c}} || {{planed-c}} || {{yes-c}}&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;state&#039;&#039;&#039; ||  ||  ||  || {{yes-c}} ||  {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}} || {{yes-c}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Sonstiges ==&lt;br /&gt;
=== Wie funktioniert Cover Art? ===&lt;br /&gt;
Reading mit URL-Link oder als Gerätestatus???&lt;br /&gt;
&lt;br /&gt;
=== Wie funktionieren Sprachdurchsagen oder Text Einblendungen? ===&lt;br /&gt;
&lt;br /&gt;
Obwohl dies sehr stark davon abhängt, ob das jeweilige Gerät eine solche Funktion unterstützt, oder ob sie anderweitig (z.B. durch Google Service) umgesetzt werden können, so sollen dennoch solche Features über die folgenden Set-Kommandos angeboten werden.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Kommando !! Beispiel !! Beschreibung&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;sayText&#039;&#039;&#039;|| &amp;lt;pre&amp;gt;set &#039;&#039;&amp;lt;device&amp;gt;&#039;&#039; sayText &amp;quot;Anruf in Abwesenheit&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
 || Dieses Kommando soll den übergebenen Text auf dem jeweiligen Device in Audio-Form wieder ausgeben. Hierbei kann auf einen Synthese-Anbieter im Internet zurückgegriffen werden, der ein solches Audiosample erzeugt, oder geräteinterne Kommandos verwendet werden, je nach dem was vorhanden und unterstützt wird.&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;showText&#039;&#039;&#039; || &amp;lt;pre&amp;gt;set &#039;&#039;&amp;lt;device&amp;gt;&#039;&#039; showText &amp;quot;Anruf in Abwesenheit&amp;quot;&amp;lt;/pre&amp;gt;|| Dieses Kommando soll einen Text z.B. auf dem Display des Gerätes (oder Fernseher) in geeigneter Form anzeigen, sofern das Gerät eine solche Funktion unterstützt. &lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot;| Alternativvorschlag&lt;br /&gt;
|-&lt;br /&gt;
| &#039;&#039;&#039;message&#039;&#039;&#039; || &amp;lt;pre&amp;gt;set &#039;&#039;&amp;lt;device&amp;gt;&#039;&#039; message &amp;quot;Anruf in Abwesenheit&amp;quot; [audio|video]&amp;lt;/pre&amp;gt;|| Dieses Kommando soll eine Nachricht mit dem Gerät anzeigen (video) oder abspielen (audio), sofern das Gerät eine solche Funktion unterstützt. Ohne die optionale Wiedergabe-Art oder wenn die angegebene nicht unterstützt ist über die von diesem Gerät bevorzugte Methode. &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Wie funktionieren Playlisten? ===&lt;br /&gt;
&lt;br /&gt;
Sofern anwendbar, sollten diese mit dem Kommando &amp;lt;code&amp;gt;set &amp;lt;device&amp;gt; playlist XY&amp;lt;/code&amp;gt; aufrufbar sein. Dabei sollten die zur Verfügung stehenden Playlists bereits bekannt sein und als möglicher Kommando-Parameter angeboten werden. &lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Fhemrocks</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevelopmentModuleIntro&amp;diff=4335</id>
		<title>DevelopmentModuleIntro</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevelopmentModuleIntro&amp;diff=4335"/>
		<updated>2014-01-14T13:20:33Z</updated>

		<summary type="html">&lt;p&gt;Fhemrocks: /* Noch zu beschreiben */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
Dieser Text ist in Arbeit und muss noch an einigen Stellen ergänzt werden. &lt;br /&gt;
Insbesondere beschreibt der Text derzeit nur einstufige Module. Die Abgrenzung zu zweistufigen Modulen und deren Eigenschaften sollte noch ergänzt werden.&lt;br /&gt;
&lt;br /&gt;
Um neue Geräte in FHEM verfügbar zu machen, kann man ein eigenes Modul in Perl schreiben, das automatisch von FHEM geladen wird, wenn ein passendes Gerät in FHEM definiert wird. Das Modul definiert dann wie mit dem Gerät kommuniziert wird, stellt Werte (&amp;quot;Readings&amp;quot;) innerhalb von FHEM zur Verfügung oder erlaubt es das Gerät mit &amp;quot;Set&amp;quot;-Befehlen zu beeinflussen. Dieser Text soll den Einstieg in die Entwicklung eigener Module erleichtern.&lt;br /&gt;
&lt;br /&gt;
Mit dem FHEM-Befehl &amp;quot;define&amp;quot;, der typischerweise in die zentrale Konfigurationsdatei fhem.cfg  eingetragen wird, werden Geräte in FHEM definiert. Der Befehl sorgt dafür dass ein neues Modul bei Bedarf geladen wird und die Initialisierungsfunktion des Moduls aufgerufen wird. &lt;br /&gt;
&lt;br /&gt;
Damit das funktioniert müssen der Name des Geräts, der Name des Moduls und der Name der Initialisierungsfunktion zueinander passen. Das folgende Beispiel soll dies verdeutlichen:&lt;br /&gt;
&lt;br /&gt;
Ein Jeelink USB-Stick in einer Fritz-Box könnte beispielsweise mit dem Befehl &amp;lt;code&amp;gt;define JeeLink1 JeeLink /dev/ttyUSB0@57600&amp;lt;/code&amp;gt; definiert werden.&lt;br /&gt;
&lt;br /&gt;
In der fhem.pl wird der define-Befehl verarbeitet, geprüft, ob ein Modul mit Namen JeeLink schon geladen ist und falls nicht ein Modul mit Namen XY_JeeLink.pm im Modulverzeichnis (bei einer FritzBox z.B. /var/media/ftp/fhem/FHEM) gesucht und dann geladen. &lt;br /&gt;
Danach wird die Funktion JeeLink_Initialize aufgerufen.&lt;br /&gt;
Die Moduldatei muss also nach dem Namen des Geräts benannt werden und eine Funktion mit dem Namen des Geräts und einer _initialize Funktion enthalten.&lt;br /&gt;
In der Initialisierungsfunktion des Moduls werden dann die Namen der aller weiteren Funktionen des Moduls, die von fhem.pl aus aufgerufen werden, bekannt gemacht. Dazu wird der Hash - das ist die zentrale Datenstruktur für jede Instanz eines Gerätes - mit entsprechenden Werten gefüllt.&lt;br /&gt;
&lt;br /&gt;
== Der Hash einer Geräteinstanz ==&lt;br /&gt;
Der zentrale Speicherort für Informationen einer Geräteinstanz ist ein Hash, der seinerseits in fhem.pl von einem globalen Hash referenziert wird. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;$defs{$d}&amp;lt;/code&amp;gt; in fhem.pl verweist auf den Hash der Geräteinstanz mit Namen &amp;lt;code&amp;gt;$d&amp;lt;/code&amp;gt;. Diesen Hash bekommen die Funktionen eines Moduls oft von fhem.pl übergeben. In ihm stehen beispielsweise die internen Werte des Geräts, die im GUI als &amp;quot;Internals&amp;quot; angezeigt werden oder die Readings des Geräts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;$hash{NAME}&amp;lt;/code&amp;gt; enthält beispielsweise den Namen der Geräteinstanz, &amp;lt;code&amp;gt;$hash{TYPE}&amp;lt;/code&amp;gt;  enthält den Namen des Typs des Geräts, der ja auch für den Namen des Moduls verwendet wird. &lt;br /&gt;
&lt;br /&gt;
Ein Abfrageintervall, das beim define eines Geräts an die define-Funktion im Modul übergeben wird, würde typischerweise als &amp;quot;Internal&amp;quot; in &amp;lt;code&amp;gt;$hash-&amp;gt;{INTERVAL}&amp;lt;/code&amp;gt; abgelegt.&lt;br /&gt;
&lt;br /&gt;
Die Readings sind hier als weitere Unterstruktur gespeichert, beispielsweise &amp;lt;code&amp;gt;$hash{READINGS}{$ReadingName}{VAL}&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$hash{READINGS}{$ReadingName}{TIME}&amp;lt;/code&amp;gt;. Innerhalb von fhem.pl könnten die selben Readings beispielsweise über &amp;lt;code&amp;gt;$defs{$d}{READINGS}{$ReadingName}{VAL}&amp;lt;/code&amp;gt; adressiert werden, da &amp;lt;code&amp;gt;$defs{$d}&amp;lt;/code&amp;gt; ja auf den gleichen Hash verweist, der dann den Modul-Funktionen übergeben wird und innerhalb der Funktionen typischerweise als &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; verwendet wird.&lt;br /&gt;
&lt;br /&gt;
== Readings ==&lt;br /&gt;
Werte, die von einem Gerät gelesen werden und in FHEM zur Verfügung stehen werden Readings genannt. Sie werden wie oben beschrieben als Unterstruktur des Hashes der jeweiligen Geräteinstanz gespeichert, beispielsweise &amp;lt;code&amp;gt;$hash{READINGS}{$Temp}{VAL}&amp;lt;/code&amp;gt; für die Temperatur eines Fühlers, die als Reading gespeichert wurde. &lt;br /&gt;
Zum Setzen von Readings (siehe auch unten) ist es empfehlenswert die Funktionen &amp;lt;code&amp;gt;readingsBeginUpdate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingsBulkUpdate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingsEndUpdate&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;readingsSingleUpdate&amp;lt;/code&amp;gt; aufzurufen. Dabei kann man auch angeben, ob dabei ein Event ausgelöst werden soll oder nicht.  Events erzeugen spürbare Last auf dem System (siehe NotifyFn), das Ändern von Readings ohne dass dabei Events erzeugt werden jedoch nicht.&lt;br /&gt;
&lt;br /&gt;
für den lesenden Zugriff auf Readings steht die Funktion ReadingsVal($$$) zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
Readings werden im statefile von FHEM automatisch zwischengespeichert, damit sie nach einem Neustart sofort wieder zur Verfügung stehen, auch bevor sie vom Modul neu gesetzt oder aktualisiert werden. &lt;br /&gt;
&lt;br /&gt;
== Internals ==&lt;br /&gt;
Werte, die das Modul intern als Teil des Hashes speichert, die aber keine Readings sind, nennt man Internals. Sie werden abenfalls als Unterstruktur des Hashes der jeweiligen Geräteinstanz gespeichert, beispielswiese &amp;lt;code&amp;gt;$hash-&amp;gt;{INTERVAL}&amp;lt;/code&amp;gt; für ein Abfrageintervall, das beim Define-Befehl übergeben wurde und als Internal gespeichert wird. Internals werden jedoch im Gegensatz zu Readings nicht im statefile zwischengespeichert.&lt;br /&gt;
&lt;br /&gt;
== Attribute ==&lt;br /&gt;
Parameter eines Moduls können als so genannte Attribute gesetzt und damit dem Modul zur Verfügung gestellt werden. Attribute werden zusammen mit der Definition der Geräte beim Speichern der aktuellen Konfiguration von FHEM in die Konfigurationsdate geschrieben. Zur Laufzeit werden sie in der globalen Datenstruktur &amp;lt;code&amp;gt;$attr{$name}&amp;lt;/code&amp;gt; gespeichert. Ein Attribut mit dem Namen &amp;lt;code&amp;gt;header&amp;lt;/code&amp;gt; würde beispielswiese mit &amp;lt;code&amp;gt;$attr{$name}{header}&amp;lt;/code&amp;gt; adressiert (&amp;lt;code&amp;gt;$attr{$name}-&amp;gt;{&#039;header&#039;}&amp;lt;/code&amp;gt; wäre eine alternative aber unübliche Schreibweise für die selber Variable). &lt;br /&gt;
&lt;br /&gt;
Zum Auslesen solcher Attribute sollte die Funktion &amp;lt;code&amp;gt;AttrVal($$$)&amp;lt;/code&amp;gt; verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Welche Attribute ein Modul unterstützt sollte in der Funktion &amp;lt;code&amp;gt;X_Initialize&amp;lt;/code&amp;gt; durch Setzen der Variable &amp;lt;code&amp;gt;$hash-&amp;gt;{AttrList}&amp;lt;/code&amp;gt; bekannt gemacht werden (siehe unten). Wenn beim Setzen von Attributen die Werte geprüft werden sollen oder zusätzliche Funktionalität implementiert werden muss, dann kann dies in der Funktion &amp;lt;code&amp;gt;X_Attr&amp;lt;/code&amp;gt; (siehe unten) implementiert werden.&lt;br /&gt;
&lt;br /&gt;
== Die wichtigsten Funktionen in einem Modul ==&lt;br /&gt;
Eine typische Grundfunktion eines einfachen Moduls ist das Auslesen von Werten von einem physischen Gerät und Bereitstellen dieser Werte innerhalb von FHEM als Readings. Das Geräte könnte beispielsweise an einem USB-Port angeschlossen sein. Folgende Funktionen könnte man beispielsweise in einem Modul mit Namen X implementieren:&lt;br /&gt;
* X_Initialize (initialisiert das Modul und gibt de Namen der zusätzlichen Funktionen bekannt)&lt;br /&gt;
* X_Define (wird beim &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt; aufgerufen&lt;br /&gt;
* X_Undef (wird beim Löschen einer Geräteinstanz aufgerufen - Gegenteil zu &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;)&lt;br /&gt;
* X_Set (wird beim Befehl &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; aufgerufen um Daten an das Gerät zu senden)&lt;br /&gt;
* X_Get (wird beim Befehl &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; aufgerufen um Daten vom Gerät abzufragen)&lt;br /&gt;
* X_Attr (wird beim Befehl &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt; aufgerufen um beispielsweise Werte zu prüfen)&lt;br /&gt;
* X_Read (wird vom globalen select aufgerufen, falls Daten zur Verfuegung stehen)&lt;br /&gt;
* X_Parse (wird bei zweistufigen Modulen vom Dispatch aufgerufen und muss hier noch beschrieben werden)&lt;br /&gt;
* X_Ready (wird unter windows als ReadFn-Erstatz benoetigt bzw. um zu pruefen, ob ein Geraet wieder eingesteckt ist)&lt;br /&gt;
* X_Notify (falls man benachrichtigt werden will)&lt;br /&gt;
* X_Rename (falls ein Gerät umbenannt wird)&lt;br /&gt;
&lt;br /&gt;
Die Funktionen werden im folgenden beschrieben (soweit diese Seite inzwischen vollständig ist):&lt;br /&gt;
&lt;br /&gt;
=== X_Initialize ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub X_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/pre&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 einen Hash für das Modul als zentrale Datenstruktur übergeben. &lt;br /&gt;
Die Initialize-Funktion setzt dann weitere Funktionsnamen, die im Modul implementiert sind, in diesen Hash:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&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;{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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; ist wieder durch den Modulnamen ohne die vorangestellte Zahl zu ersetzen. &lt;br /&gt;
Entsprechend können auch die Funktionen &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; etc. bekannt gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Darüber hinaus sollten die vom Modul unterstützen Attribute definiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Fhem.pl werden dann die entsprechenden Werte 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 Funktion &amp;lt;code&amp;gt;X_Attr&amp;lt;/code&amp;gt; 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 verfügbar werden wenn das Modul zum Setzen von Readings die Funktionen readingsBeginUpdate, readingsBulkUpdate, readingsEndUpdate oder readingsSingleUpdate 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.&lt;br /&gt;
&lt;br /&gt;
=== X_Define ===&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-Fubktion 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.ä.)&lt;br /&gt;
Sie beginnt typischerweise mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub HTTPMOD_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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als Übergabeparameter bekommt die Define-Funktion den Hash der Geräteinstanz sowie den Rest der Parameter, die im Befehl angegeben wurden. Welche und wie viele Parameter &lt;br /&gt;
akzeptiert werden ist Sache dieser Funktion. Im obigen Beispiel wird alles nach dem übergebenen Hash in ein Array aufgeteilt 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;pre&amp;gt;&lt;br /&gt;
my $name 	= $a[0];&lt;br /&gt;
my $url 	= $a[2];&lt;br /&gt;
my $inter	= 300;&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; 5, default is 300&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&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 typischerwiese als Internals im Hash der Geräteinstanz gespeichert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$hash-&amp;gt;{url} 		= $url;&lt;br /&gt;
$hash-&amp;gt;{Interval}	= $inter;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn eine physische Schnittstelle geöffnet werden soll und dann bei verfügbaren Eingabedaten eine Lese-Funktion von Fhem aufgerufen werden soll, dann kann man in der Define-Funktion die Funktion DevIo_OpenDev aufrufen, die sich um alles weitere kümmert. Sie öffnet die Schnittstelle und fügt den Filedeskriptor an die globale Liste offener Verbindungen (selectlist / readyfnlist) an. Damit kann Fhem in seiner Hauptschleife erkennen, von welchem Gerät Daten bereit stehen und die zuständigen Funktionen aufrufen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $ret = DevIo_OpenDev( $hash, 0, &amp;quot;X_DevInit&amp;quot; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die optionale Funktion &amp;lt;code&amp;gt;X_DevIni&amp;lt;/code&amp;gt;t wird zur weiteren Initialisierung der Verbindung von &amp;lt;code&amp;gt;DevIo_OpenDev&amp;lt;/code&amp;gt; aufgerufen. Der zweite Übergabeparameter an &amp;lt;code&amp;gt;DevIo_OpenDev&amp;lt;/code&amp;gt; (hier &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;) steht für reopen und wird benötigt, da die Funktion auch aufgerufen wird, wenn ein USB-Geräte beispielsweise im Betrieb aus- und wieder eingesteckt wird. In diesem Fall wird die Funktion mit &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; aufgerufen.&lt;br /&gt;
&lt;br /&gt;
=== X_Undef ===&lt;br /&gt;
&lt;br /&gt;
Die &amp;lt;code&amp;gt;Undef&amp;lt;/code&amp;gt;-Funktion ist das Gegenstück zur &amp;lt;code&amp;gt;Define&amp;lt;/code&amp;gt;-Funktion und wird aufgerufen wenn ein Gerät mit &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; gelöscht wird. 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 später).&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub WKRCD4_Undef($$)    &lt;br /&gt;
{                     &lt;br /&gt;
	my ( $hash, $arg ) = @_;       &lt;br /&gt;
	DevIo_CloseDev($hash);         &lt;br /&gt;
	RemoveInternalTimer($hash);    &lt;br /&gt;
	return undef;                  &lt;br /&gt;
}    &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== X_Get ===&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. Einige Module verwenden für diese Funktion einen Hash im Modul, der die möglichen &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt;-Optionen mit zusätzlichen Werten definiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my X_gets = (&lt;br /&gt;
	&amp;quot;TempSoll&amp;quot;	=&amp;gt; &amp;quot;XY&amp;quot;,&lt;br /&gt;
	&amp;quot;Steilheit&amp;quot;	=&amp;gt; &amp;quot;Z&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der Get-Funktion selbst werden dann die übergebenen Parameter gegen diesen Hash geprüft.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub X_Get($@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, @a ) = @_;&lt;br /&gt;
	return &amp;quot;\&amp;quot;get X\&amp;quot; needs at least one argument&amp;quot; if ( @a &amp;lt; 2 );&lt;br /&gt;
	my $name = shift @a;&lt;br /&gt;
	my $opt = shift @a;&lt;br /&gt;
	if(!$X_gets{$opt}) {&lt;br /&gt;
		my @cList = keys %X_gets;&lt;br /&gt;
		return &amp;quot;Unknown argument $attr, choose one of &amp;quot; . join(&amp;quot; &amp;quot;, @cList);&lt;br /&gt;
	}&lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe der Meldung mit &amp;lt;code&amp;gt;... choose one of ...&amp;lt;/code&amp;gt; ist dabei 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 abfragen und diesen als Return-Wert der Get-Funktion zurückgeben.&lt;br /&gt;
&lt;br /&gt;
=== X_Set ===&lt;br /&gt;
Die Set-Funktion ist das Gegenteil zur Get-Funktion. Sie ist dafür gedacht, Werte zum physischen Gerät zu schicken. Falls nur interne Werte im Modul gesetzt werden sollen, so sollte statt Set die Attr-Funktion verwendet werden. Attribute werden bei Save-Config auch in der Fhem.cfg gesichert. Set-Befehle nicht.&lt;br /&gt;
 &lt;br /&gt;
Eine Set-Funktion ist ähnlich aufgebaut wie die Get-Funktion, sie bekommt jedoch nach dem Namen der Option auch den zu setzenden Wert übergeben.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub X_Set($@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, @a ) = @_;&lt;br /&gt;
	return &amp;quot;\&amp;quot;set X\&amp;quot; needs at least an argument&amp;quot; if ( @a &amp;lt; 2 );&lt;br /&gt;
	my $name = shift @a;&lt;br /&gt;
	my $opt = shift @a;&lt;br /&gt;
	my $arg = join(&amp;quot;&amp;quot;, @a);&lt;br /&gt;
	&lt;br /&gt;
	if(!defined($X_sets{$attr})) {&lt;br /&gt;
		my @cList = keys %X_sets;&lt;br /&gt;
		return &amp;quot;Unknown argument $attr, choose one of &amp;quot; . join(&amp;quot; &amp;quot;, @cList);&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Den einzelnen Befehlen, die das Modul versteht und die via set &amp;lt;name&amp;gt; &amp;lt;cmd&amp;gt; &amp;lt;arg1&amp;gt; übergeben werden kann man bei der Rückgabe auch zulässige Auswahlwerte für Kommando Argumente mitgeben. Aus diesen Auswahlwerten erzeugt FHEM-Web dann z.B. Drop-Down Boxen in der Detailansicht, mit denen der Anwender die jeweiligen Argumente auswählen kann. Dabei ist jeweils vor dem Doppelpunkt der Name des Kommandoes anzugeben und nach dem Doppelpunkt der jeweilige Auswahlwert.&lt;br /&gt;
Es gibt dabei folgende Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;noArg&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
es werden keine weiteren Argumente mehr benötigt&lt;br /&gt;
&amp;lt;pre&amp;gt;on:noArg&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;slider&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
es wird ein Schieberegler für den Wert angezeigt. Dabei Minimum, Schrittweite und Maximum angeben&lt;br /&gt;
&amp;lt;pre&amp;gt;dim:slider,0,1,100&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RGB&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
es wird ein Colorpicker angezeigt, der dem Anwender die Auswahl einer Farbe ermöglicht. Bitte dazu auch den Wiki Artikel zum Colorpicker lesen, da im Modul noch weiterer Code eingefügt werden muß.&lt;br /&gt;
&amp;lt;pre&amp;gt;rgb:colorpicker,RGB&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Liste&#039;&#039;&#039;&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;loglevel:1,2,3,4,5&lt;br /&gt;
timer:30,120,300&lt;br /&gt;
mode:verbose,ultra,relaxed&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Leer&#039;&#039;&#039;&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;
&lt;br /&gt;
&lt;br /&gt;
Damit die jeweiligen Auswahllisten bereits mit dem korrekten Wert, der aktuell im Modul gesetzt ist, befüllt wird, muß dass Get Kommando im Modul implementiert sein und den jeweiligen Wert zurück liefern.&lt;br /&gt;
Am Beispiel des Sliders oben:&lt;br /&gt;
der Wert heisst dim und soll per Slider dargestellt sein. Damit der aktuelle Wert des dim im Slider in der Oberfläche bereits richtig voreingestellt ist, sollte die Get Funktion auf Anfrage nach dim auch den aktuellen Wert von dim zurück liefern.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweise&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
- bei den üblichen Kommandos wie on off sollte man auf noArg verzichten, da diese durch FHEMWeb automatisch in der Raumübersicht angezeigt werden. Wenn man hier noArg spezifiziert, so werden diese nicht neben dem Modul in der Raumübersicht angezeigt und der User muß sich diese vie webCmd dann erst selbst definieren, was natürlich unschön ist&lt;br /&gt;
&lt;br /&gt;
- der User kann sich in der Raumübersicht nach wie vor via webCmd eine entsprechende Steuerung anlegen.&lt;br /&gt;
&lt;br /&gt;
=== X_Attr ===&lt;br /&gt;
Die Attr-Funktion implementiert Prüfungen der bei einem &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt; übergebenen Werte und eventuell zusätzliche Aktionen wenn ein Attribut gesetzt wird. Die Liste der möglichen Attribute wird in der &amp;lt;code&amp;gt;X_Initialize-Funktion&amp;lt;/code&amp;gt; definiert (siehe oben). Fhem ruft bei einem Attr-Befehl die zuständige &amp;lt;code&amp;gt;X-Attr-Funktion&amp;lt;/code&amp;gt; auf und wenn diese keine Fehlermelhung sondern &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgibt, dann schreibt fhem.pl die bei &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt; angegebenen Werte in die jeweilige Datenstruktur &amp;lt;code&amp;gt;$attr{$name}-&amp;gt; ...&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
X_Attr(@)&lt;br /&gt;
{&lt;br /&gt;
	my ($cmd,$name,$aName,$aVal) = @_;&lt;br /&gt;
  	# $cmd can be &amp;quot;del&amp;quot; or &amp;quot;set&amp;quot;&lt;br /&gt;
	# $name is device name&lt;br /&gt;
	# aName and aVal are Attribute name and value&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: 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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Attr-Funktion bekommt nicht den Hash der Geräteinstanz übergeben, da sie ja auch keine Werte dort speichern muss sondern den Befehl &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;del&amp;lt;/code&amp;gt; je nachdem ob ein Attribut gesetzt oder gelöscht wird, den Namen der Geräteinstanz sowie den Namen des Attributs und seinen Wert.&lt;br /&gt;
Im obigen Beispiel wird für ein Attribut mit Namen Regex geprüft ob die Regex fehlerhaft ist. Falls sie ok ist, wird undef zurück gegegen und fhem.pl speichert den Wert des Attributs.&lt;br /&gt;
&lt;br /&gt;
=== X_Read ===&lt;br /&gt;
&lt;br /&gt;
Die X_Read-Funktion wird aus der Hauptschleife von FHEM aus aufgerufen wenn das Gerät, für das das Modul zuständig ist, Daten bereit gestellt hat, die gelesen werden können. Im folgenden Beispiel wird über eine serielle Schnittstelle (beziehungsweise über einen USB-To-Seriell-Konverter) von einem angeschlossenen Gerät gelesen. Dazu werden die bisher verfügbaren Daten mit der Funktion &amp;lt;code&amp;gt;DevIo_SimpleRead&amp;lt;/code&amp;gt; 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 zwischenspeichern. Es bietet sich an, dies im Hash der Geräteinstanz zu tun. Im Beispiel ist dies &amp;lt;code&amp;gt;$hash-&amp;gt;{buffer}&amp;lt;/code&amp;gt; an den die jeweils gelesenen Daten angehängt werden bis die folgende Prüfung ein für das jeweilige Protokoll passendes Frame identifiziert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&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;
	# read from serial device&lt;br /&gt;
	my $buf = DevIo_SimpleRead($hash);		&lt;br /&gt;
	return &amp;quot;&amp;quot; if ( !defined($buf) );&lt;br /&gt;
&lt;br /&gt;
	# convert to hex string to make parsing with regex easier&lt;br /&gt;
	$hash-&amp;gt;{buffer} .= unpack (&#039;H*&#039;, $buf);	&lt;br /&gt;
	Log3 $name, 5, &amp;quot;Current buffer content: &amp;quot; . $hash-&amp;gt;{buffer};&lt;br /&gt;
&lt;br /&gt;
	# did we already get a full frame?&lt;br /&gt;
	if ($hash-&amp;gt;{buffer} =~ &amp;quot;ff1002(.{4})(.*)1003(.{4})ff(.*)&amp;quot;) &lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/pre&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;{buffer}&amp;lt;/code&amp;gt;) stehen oder aus dem Kontext mit einem Regex-Match extrahiert werden und in Readings gespeichert werden (siehe unten).&lt;br /&gt;
&lt;br /&gt;
=== X_Ready ===&lt;br /&gt;
&lt;br /&gt;
muss noch beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub X_Ready($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	return DevIo_OpenDev($hash, 1, undef )&lt;br /&gt;
	  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;
	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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Lesen von Geräte und Speichern der Werte in Readings ==&lt;br /&gt;
&lt;br /&gt;
FHEM führt Module normalerweise nicht parallel aus. Daher wäre es ungünstig wenn Module Werte von einem Gerät abfragen und dann auf die Antwort des Geräts warten. In dieser Zeit wäre der Rest von FHEM blockiert. Die Ein- und Ausgabe sollte ohne Blockieren erfolgen und die Verarbeitung mehrerer Ein- und Ausgabekanäle quasi parallel 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 sein können. Auf Linux- bzw. Unix-basierten Plattformen wird der select-Befehl des Betriebssystems verwendet und entsprechend gibt es in FHEM eine selectlist, in der die Filedeskriptoren der Geräedateien (z.B. /dev/ttyUSBx etc.) gespeichert sind. &lt;br /&gt;
&lt;br /&gt;
In der zentralen Schleife von fhem.pl wird vereinfacht gesagt mit select überwacht, ob über eine der geöffneten Schnittstellen Daten zum Lesen anstehen. Wenn dies der Fall ist, dann wird die Lesefunktion (X_Read) des zuständigen Moduls aufgerufen, damit es die Daten entgegennimmt und die Schleife wird weiter ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Auf Windows-Systemen funktioniert dies anders. Hier können USB/Seriell-Geräte nicht per select ü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) implementieren, die 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 und Werte in Readings geschrieben.&lt;br /&gt;
&lt;br /&gt;
Zum Setzen von Readings ist es wie oben schon erwähnt empfehlenswert die Funktionen &amp;lt;code&amp;gt;readingsBeginUpdate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingsBulkUpdate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingsEndUpdate&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;readingsSingleUpdate&amp;lt;/code&amp;gt; aufzurufen. Dabei kann man auch angeben, ob dabei ein Event ausgelöst werden soll oder nicht.  Events erzeugen spürbare Last auf dem System (siehe 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;pre&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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; beim Aufruf von &amp;lt;code&amp;gt;readingsEndUpdate&amp;lt;/code&amp;gt; gibt an, dass Events ausgelöst werden, die auf Veränderungen der Readings reagieren.&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 &amp;lt;code&amp;gt;InternalTimer&amp;lt;code&amp;gt; verwenden. Man übergibt ihr den Zeitpunkt für den nächsten Aufruf, den Namen der Funktion, die aufgerufen werden soll, die zu übergebenden Parameter und ein Flag ob der erste Aufruf verzögert werden soll falls die Initialiserung des Geräts noch nicht abgeschlossen ist.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise könnte man für das Abfragen eines Geräts in der Define-Funktion den Timer folgendermassen setzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# initial request after 2 secs, there timer is set to interval for further update&lt;br /&gt;
InternalTimer(gettimeofday()+2, &amp;quot;X_GetUpdate&amp;quot;, $hash, 0);	&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;pre&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;
	InternalTimer(gettimeofday()+$hash-&amp;gt;{Interval}, &amp;quot;X_GetUpdate&amp;quot;, $hash, 1);&lt;br /&gt;
	Log3 $name, 4, &amp;quot;X: GetUpdate called ...&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf der Funktion könnte man dann 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 Read-Funktion zu implementieren, die beim Anstehen von Daten aufgerufen wird.&lt;br /&gt;
&lt;br /&gt;
== Logging / Debugging ==&lt;br /&gt;
Um Innerhalb eines Moduls eine Protokollmeldung in die Fhem-Logdatei zu schreiben, wird die Funktion Log3 aufgerufen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Log3 $name, 3, &amp;quot;X: Problem erkannt ...&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Parameter der Funktion Log3 sind der Name oder der Hash der Geräteinstanz, das Verbose-Level, in dem die Meldung sichtbar sein soll und die Meldung selbst.&lt;br /&gt;
Den Namen der Geräteinstanz kann man in den Funktionen, die den Hash übergeben bekommen einfach aus diesem Hash nehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um für ein neues Modul das Verbose-Level zu erhöhen, ohne gleich für das Gesamte FHEM alle Meldungen zu erzeugen kann man den Befehl &lt;br /&gt;
&amp;lt;code&amp;gt;attr gerätename verbose&amp;lt;/code&amp;gt; verwenden. Beispielsweise &amp;lt;code&amp;gt;attr PM verbose 5&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit bietet es sich an im Modul Meldungen, die im normalen Betrieb nicht benötigt werden, beim Aufruf von Log3 mit dem Level 4 oder 5 anzugeben. Wenn man dann bei der Fehlersuche mehr Meldungen sehen möchte, erhöht man mit attr X verbose das Level für das betroffene Gerät.&lt;br /&gt;
&lt;br /&gt;
== Noch zu beschreiben ==&lt;br /&gt;
* Zweistufiges Modell für Module&lt;br /&gt;
* Funktion X_Ready ...&lt;br /&gt;
* FW_summaryFn (wird von FHEMWEB aufgerufen fuer Raum-Uebersicht)&lt;br /&gt;
* FW_detailFn (wird von FHEMWEB aufgerufen fuer Detail-Ansicht)&lt;br /&gt;
* Bei der Beschreibung der von SetFn/GetFn zurueckgelieferten Meldung: Modifier fuer FHEMWEB wie :noArg, :slider, etc. --&amp;gt;2014-01-14: done. Bitte um Review der Beschreibung&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Fhemrocks</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevelopmentModuleIntro&amp;diff=4334</id>
		<title>DevelopmentModuleIntro</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevelopmentModuleIntro&amp;diff=4334"/>
		<updated>2014-01-14T13:20:14Z</updated>

		<summary type="html">&lt;p&gt;Fhemrocks: /* Noch zu beschreiben */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
Dieser Text ist in Arbeit und muss noch an einigen Stellen ergänzt werden. &lt;br /&gt;
Insbesondere beschreibt der Text derzeit nur einstufige Module. Die Abgrenzung zu zweistufigen Modulen und deren Eigenschaften sollte noch ergänzt werden.&lt;br /&gt;
&lt;br /&gt;
Um neue Geräte in FHEM verfügbar zu machen, kann man ein eigenes Modul in Perl schreiben, das automatisch von FHEM geladen wird, wenn ein passendes Gerät in FHEM definiert wird. Das Modul definiert dann wie mit dem Gerät kommuniziert wird, stellt Werte (&amp;quot;Readings&amp;quot;) innerhalb von FHEM zur Verfügung oder erlaubt es das Gerät mit &amp;quot;Set&amp;quot;-Befehlen zu beeinflussen. Dieser Text soll den Einstieg in die Entwicklung eigener Module erleichtern.&lt;br /&gt;
&lt;br /&gt;
Mit dem FHEM-Befehl &amp;quot;define&amp;quot;, der typischerweise in die zentrale Konfigurationsdatei fhem.cfg  eingetragen wird, werden Geräte in FHEM definiert. Der Befehl sorgt dafür dass ein neues Modul bei Bedarf geladen wird und die Initialisierungsfunktion des Moduls aufgerufen wird. &lt;br /&gt;
&lt;br /&gt;
Damit das funktioniert müssen der Name des Geräts, der Name des Moduls und der Name der Initialisierungsfunktion zueinander passen. Das folgende Beispiel soll dies verdeutlichen:&lt;br /&gt;
&lt;br /&gt;
Ein Jeelink USB-Stick in einer Fritz-Box könnte beispielsweise mit dem Befehl &amp;lt;code&amp;gt;define JeeLink1 JeeLink /dev/ttyUSB0@57600&amp;lt;/code&amp;gt; definiert werden.&lt;br /&gt;
&lt;br /&gt;
In der fhem.pl wird der define-Befehl verarbeitet, geprüft, ob ein Modul mit Namen JeeLink schon geladen ist und falls nicht ein Modul mit Namen XY_JeeLink.pm im Modulverzeichnis (bei einer FritzBox z.B. /var/media/ftp/fhem/FHEM) gesucht und dann geladen. &lt;br /&gt;
Danach wird die Funktion JeeLink_Initialize aufgerufen.&lt;br /&gt;
Die Moduldatei muss also nach dem Namen des Geräts benannt werden und eine Funktion mit dem Namen des Geräts und einer _initialize Funktion enthalten.&lt;br /&gt;
In der Initialisierungsfunktion des Moduls werden dann die Namen der aller weiteren Funktionen des Moduls, die von fhem.pl aus aufgerufen werden, bekannt gemacht. Dazu wird der Hash - das ist die zentrale Datenstruktur für jede Instanz eines Gerätes - mit entsprechenden Werten gefüllt.&lt;br /&gt;
&lt;br /&gt;
== Der Hash einer Geräteinstanz ==&lt;br /&gt;
Der zentrale Speicherort für Informationen einer Geräteinstanz ist ein Hash, der seinerseits in fhem.pl von einem globalen Hash referenziert wird. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;$defs{$d}&amp;lt;/code&amp;gt; in fhem.pl verweist auf den Hash der Geräteinstanz mit Namen &amp;lt;code&amp;gt;$d&amp;lt;/code&amp;gt;. Diesen Hash bekommen die Funktionen eines Moduls oft von fhem.pl übergeben. In ihm stehen beispielsweise die internen Werte des Geräts, die im GUI als &amp;quot;Internals&amp;quot; angezeigt werden oder die Readings des Geräts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;$hash{NAME}&amp;lt;/code&amp;gt; enthält beispielsweise den Namen der Geräteinstanz, &amp;lt;code&amp;gt;$hash{TYPE}&amp;lt;/code&amp;gt;  enthält den Namen des Typs des Geräts, der ja auch für den Namen des Moduls verwendet wird. &lt;br /&gt;
&lt;br /&gt;
Ein Abfrageintervall, das beim define eines Geräts an die define-Funktion im Modul übergeben wird, würde typischerweise als &amp;quot;Internal&amp;quot; in &amp;lt;code&amp;gt;$hash-&amp;gt;{INTERVAL}&amp;lt;/code&amp;gt; abgelegt.&lt;br /&gt;
&lt;br /&gt;
Die Readings sind hier als weitere Unterstruktur gespeichert, beispielsweise &amp;lt;code&amp;gt;$hash{READINGS}{$ReadingName}{VAL}&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$hash{READINGS}{$ReadingName}{TIME}&amp;lt;/code&amp;gt;. Innerhalb von fhem.pl könnten die selben Readings beispielsweise über &amp;lt;code&amp;gt;$defs{$d}{READINGS}{$ReadingName}{VAL}&amp;lt;/code&amp;gt; adressiert werden, da &amp;lt;code&amp;gt;$defs{$d}&amp;lt;/code&amp;gt; ja auf den gleichen Hash verweist, der dann den Modul-Funktionen übergeben wird und innerhalb der Funktionen typischerweise als &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; verwendet wird.&lt;br /&gt;
&lt;br /&gt;
== Readings ==&lt;br /&gt;
Werte, die von einem Gerät gelesen werden und in FHEM zur Verfügung stehen werden Readings genannt. Sie werden wie oben beschrieben als Unterstruktur des Hashes der jeweiligen Geräteinstanz gespeichert, beispielsweise &amp;lt;code&amp;gt;$hash{READINGS}{$Temp}{VAL}&amp;lt;/code&amp;gt; für die Temperatur eines Fühlers, die als Reading gespeichert wurde. &lt;br /&gt;
Zum Setzen von Readings (siehe auch unten) ist es empfehlenswert die Funktionen &amp;lt;code&amp;gt;readingsBeginUpdate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingsBulkUpdate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingsEndUpdate&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;readingsSingleUpdate&amp;lt;/code&amp;gt; aufzurufen. Dabei kann man auch angeben, ob dabei ein Event ausgelöst werden soll oder nicht.  Events erzeugen spürbare Last auf dem System (siehe NotifyFn), das Ändern von Readings ohne dass dabei Events erzeugt werden jedoch nicht.&lt;br /&gt;
&lt;br /&gt;
für den lesenden Zugriff auf Readings steht die Funktion ReadingsVal($$$) zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
Readings werden im statefile von FHEM automatisch zwischengespeichert, damit sie nach einem Neustart sofort wieder zur Verfügung stehen, auch bevor sie vom Modul neu gesetzt oder aktualisiert werden. &lt;br /&gt;
&lt;br /&gt;
== Internals ==&lt;br /&gt;
Werte, die das Modul intern als Teil des Hashes speichert, die aber keine Readings sind, nennt man Internals. Sie werden abenfalls als Unterstruktur des Hashes der jeweiligen Geräteinstanz gespeichert, beispielswiese &amp;lt;code&amp;gt;$hash-&amp;gt;{INTERVAL}&amp;lt;/code&amp;gt; für ein Abfrageintervall, das beim Define-Befehl übergeben wurde und als Internal gespeichert wird. Internals werden jedoch im Gegensatz zu Readings nicht im statefile zwischengespeichert.&lt;br /&gt;
&lt;br /&gt;
== Attribute ==&lt;br /&gt;
Parameter eines Moduls können als so genannte Attribute gesetzt und damit dem Modul zur Verfügung gestellt werden. Attribute werden zusammen mit der Definition der Geräte beim Speichern der aktuellen Konfiguration von FHEM in die Konfigurationsdate geschrieben. Zur Laufzeit werden sie in der globalen Datenstruktur &amp;lt;code&amp;gt;$attr{$name}&amp;lt;/code&amp;gt; gespeichert. Ein Attribut mit dem Namen &amp;lt;code&amp;gt;header&amp;lt;/code&amp;gt; würde beispielswiese mit &amp;lt;code&amp;gt;$attr{$name}{header}&amp;lt;/code&amp;gt; adressiert (&amp;lt;code&amp;gt;$attr{$name}-&amp;gt;{&#039;header&#039;}&amp;lt;/code&amp;gt; wäre eine alternative aber unübliche Schreibweise für die selber Variable). &lt;br /&gt;
&lt;br /&gt;
Zum Auslesen solcher Attribute sollte die Funktion &amp;lt;code&amp;gt;AttrVal($$$)&amp;lt;/code&amp;gt; verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Welche Attribute ein Modul unterstützt sollte in der Funktion &amp;lt;code&amp;gt;X_Initialize&amp;lt;/code&amp;gt; durch Setzen der Variable &amp;lt;code&amp;gt;$hash-&amp;gt;{AttrList}&amp;lt;/code&amp;gt; bekannt gemacht werden (siehe unten). Wenn beim Setzen von Attributen die Werte geprüft werden sollen oder zusätzliche Funktionalität implementiert werden muss, dann kann dies in der Funktion &amp;lt;code&amp;gt;X_Attr&amp;lt;/code&amp;gt; (siehe unten) implementiert werden.&lt;br /&gt;
&lt;br /&gt;
== Die wichtigsten Funktionen in einem Modul ==&lt;br /&gt;
Eine typische Grundfunktion eines einfachen Moduls ist das Auslesen von Werten von einem physischen Gerät und Bereitstellen dieser Werte innerhalb von FHEM als Readings. Das Geräte könnte beispielsweise an einem USB-Port angeschlossen sein. Folgende Funktionen könnte man beispielsweise in einem Modul mit Namen X implementieren:&lt;br /&gt;
* X_Initialize (initialisiert das Modul und gibt de Namen der zusätzlichen Funktionen bekannt)&lt;br /&gt;
* X_Define (wird beim &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt; aufgerufen&lt;br /&gt;
* X_Undef (wird beim Löschen einer Geräteinstanz aufgerufen - Gegenteil zu &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;)&lt;br /&gt;
* X_Set (wird beim Befehl &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; aufgerufen um Daten an das Gerät zu senden)&lt;br /&gt;
* X_Get (wird beim Befehl &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; aufgerufen um Daten vom Gerät abzufragen)&lt;br /&gt;
* X_Attr (wird beim Befehl &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt; aufgerufen um beispielsweise Werte zu prüfen)&lt;br /&gt;
* X_Read (wird vom globalen select aufgerufen, falls Daten zur Verfuegung stehen)&lt;br /&gt;
* X_Parse (wird bei zweistufigen Modulen vom Dispatch aufgerufen und muss hier noch beschrieben werden)&lt;br /&gt;
* X_Ready (wird unter windows als ReadFn-Erstatz benoetigt bzw. um zu pruefen, ob ein Geraet wieder eingesteckt ist)&lt;br /&gt;
* X_Notify (falls man benachrichtigt werden will)&lt;br /&gt;
* X_Rename (falls ein Gerät umbenannt wird)&lt;br /&gt;
&lt;br /&gt;
Die Funktionen werden im folgenden beschrieben (soweit diese Seite inzwischen vollständig ist):&lt;br /&gt;
&lt;br /&gt;
=== X_Initialize ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub X_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/pre&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 einen Hash für das Modul als zentrale Datenstruktur übergeben. &lt;br /&gt;
Die Initialize-Funktion setzt dann weitere Funktionsnamen, die im Modul implementiert sind, in diesen Hash:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&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;{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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; ist wieder durch den Modulnamen ohne die vorangestellte Zahl zu ersetzen. &lt;br /&gt;
Entsprechend können auch die Funktionen &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; etc. bekannt gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Darüber hinaus sollten die vom Modul unterstützen Attribute definiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Fhem.pl werden dann die entsprechenden Werte 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 Funktion &amp;lt;code&amp;gt;X_Attr&amp;lt;/code&amp;gt; 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 verfügbar werden wenn das Modul zum Setzen von Readings die Funktionen readingsBeginUpdate, readingsBulkUpdate, readingsEndUpdate oder readingsSingleUpdate 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.&lt;br /&gt;
&lt;br /&gt;
=== X_Define ===&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-Fubktion 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.ä.)&lt;br /&gt;
Sie beginnt typischerweise mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub HTTPMOD_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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als Übergabeparameter bekommt die Define-Funktion den Hash der Geräteinstanz sowie den Rest der Parameter, die im Befehl angegeben wurden. Welche und wie viele Parameter &lt;br /&gt;
akzeptiert werden ist Sache dieser Funktion. Im obigen Beispiel wird alles nach dem übergebenen Hash in ein Array aufgeteilt 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;pre&amp;gt;&lt;br /&gt;
my $name 	= $a[0];&lt;br /&gt;
my $url 	= $a[2];&lt;br /&gt;
my $inter	= 300;&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; 5, default is 300&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&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 typischerwiese als Internals im Hash der Geräteinstanz gespeichert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$hash-&amp;gt;{url} 		= $url;&lt;br /&gt;
$hash-&amp;gt;{Interval}	= $inter;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn eine physische Schnittstelle geöffnet werden soll und dann bei verfügbaren Eingabedaten eine Lese-Funktion von Fhem aufgerufen werden soll, dann kann man in der Define-Funktion die Funktion DevIo_OpenDev aufrufen, die sich um alles weitere kümmert. Sie öffnet die Schnittstelle und fügt den Filedeskriptor an die globale Liste offener Verbindungen (selectlist / readyfnlist) an. Damit kann Fhem in seiner Hauptschleife erkennen, von welchem Gerät Daten bereit stehen und die zuständigen Funktionen aufrufen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $ret = DevIo_OpenDev( $hash, 0, &amp;quot;X_DevInit&amp;quot; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die optionale Funktion &amp;lt;code&amp;gt;X_DevIni&amp;lt;/code&amp;gt;t wird zur weiteren Initialisierung der Verbindung von &amp;lt;code&amp;gt;DevIo_OpenDev&amp;lt;/code&amp;gt; aufgerufen. Der zweite Übergabeparameter an &amp;lt;code&amp;gt;DevIo_OpenDev&amp;lt;/code&amp;gt; (hier &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;) steht für reopen und wird benötigt, da die Funktion auch aufgerufen wird, wenn ein USB-Geräte beispielsweise im Betrieb aus- und wieder eingesteckt wird. In diesem Fall wird die Funktion mit &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; aufgerufen.&lt;br /&gt;
&lt;br /&gt;
=== X_Undef ===&lt;br /&gt;
&lt;br /&gt;
Die &amp;lt;code&amp;gt;Undef&amp;lt;/code&amp;gt;-Funktion ist das Gegenstück zur &amp;lt;code&amp;gt;Define&amp;lt;/code&amp;gt;-Funktion und wird aufgerufen wenn ein Gerät mit &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; gelöscht wird. 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 später).&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub WKRCD4_Undef($$)    &lt;br /&gt;
{                     &lt;br /&gt;
	my ( $hash, $arg ) = @_;       &lt;br /&gt;
	DevIo_CloseDev($hash);         &lt;br /&gt;
	RemoveInternalTimer($hash);    &lt;br /&gt;
	return undef;                  &lt;br /&gt;
}    &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== X_Get ===&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. Einige Module verwenden für diese Funktion einen Hash im Modul, der die möglichen &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt;-Optionen mit zusätzlichen Werten definiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my X_gets = (&lt;br /&gt;
	&amp;quot;TempSoll&amp;quot;	=&amp;gt; &amp;quot;XY&amp;quot;,&lt;br /&gt;
	&amp;quot;Steilheit&amp;quot;	=&amp;gt; &amp;quot;Z&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der Get-Funktion selbst werden dann die übergebenen Parameter gegen diesen Hash geprüft.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub X_Get($@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, @a ) = @_;&lt;br /&gt;
	return &amp;quot;\&amp;quot;get X\&amp;quot; needs at least one argument&amp;quot; if ( @a &amp;lt; 2 );&lt;br /&gt;
	my $name = shift @a;&lt;br /&gt;
	my $opt = shift @a;&lt;br /&gt;
	if(!$X_gets{$opt}) {&lt;br /&gt;
		my @cList = keys %X_gets;&lt;br /&gt;
		return &amp;quot;Unknown argument $attr, choose one of &amp;quot; . join(&amp;quot; &amp;quot;, @cList);&lt;br /&gt;
	}&lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe der Meldung mit &amp;lt;code&amp;gt;... choose one of ...&amp;lt;/code&amp;gt; ist dabei 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 abfragen und diesen als Return-Wert der Get-Funktion zurückgeben.&lt;br /&gt;
&lt;br /&gt;
=== X_Set ===&lt;br /&gt;
Die Set-Funktion ist das Gegenteil zur Get-Funktion. Sie ist dafür gedacht, Werte zum physischen Gerät zu schicken. Falls nur interne Werte im Modul gesetzt werden sollen, so sollte statt Set die Attr-Funktion verwendet werden. Attribute werden bei Save-Config auch in der Fhem.cfg gesichert. Set-Befehle nicht.&lt;br /&gt;
 &lt;br /&gt;
Eine Set-Funktion ist ähnlich aufgebaut wie die Get-Funktion, sie bekommt jedoch nach dem Namen der Option auch den zu setzenden Wert übergeben.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub X_Set($@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, @a ) = @_;&lt;br /&gt;
	return &amp;quot;\&amp;quot;set X\&amp;quot; needs at least an argument&amp;quot; if ( @a &amp;lt; 2 );&lt;br /&gt;
	my $name = shift @a;&lt;br /&gt;
	my $opt = shift @a;&lt;br /&gt;
	my $arg = join(&amp;quot;&amp;quot;, @a);&lt;br /&gt;
	&lt;br /&gt;
	if(!defined($X_sets{$attr})) {&lt;br /&gt;
		my @cList = keys %X_sets;&lt;br /&gt;
		return &amp;quot;Unknown argument $attr, choose one of &amp;quot; . join(&amp;quot; &amp;quot;, @cList);&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Den einzelnen Befehlen, die das Modul versteht und die via set &amp;lt;name&amp;gt; &amp;lt;cmd&amp;gt; &amp;lt;arg1&amp;gt; übergeben werden kann man bei der Rückgabe auch zulässige Auswahlwerte für Kommando Argumente mitgeben. Aus diesen Auswahlwerten erzeugt FHEM-Web dann z.B. Drop-Down Boxen in der Detailansicht, mit denen der Anwender die jeweiligen Argumente auswählen kann. Dabei ist jeweils vor dem Doppelpunkt der Name des Kommandoes anzugeben und nach dem Doppelpunkt der jeweilige Auswahlwert.&lt;br /&gt;
Es gibt dabei folgende Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;noArg&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
es werden keine weiteren Argumente mehr benötigt&lt;br /&gt;
&amp;lt;pre&amp;gt;on:noArg&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;slider&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
es wird ein Schieberegler für den Wert angezeigt. Dabei Minimum, Schrittweite und Maximum angeben&lt;br /&gt;
&amp;lt;pre&amp;gt;dim:slider,0,1,100&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RGB&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
es wird ein Colorpicker angezeigt, der dem Anwender die Auswahl einer Farbe ermöglicht. Bitte dazu auch den Wiki Artikel zum Colorpicker lesen, da im Modul noch weiterer Code eingefügt werden muß.&lt;br /&gt;
&amp;lt;pre&amp;gt;rgb:colorpicker,RGB&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Liste&#039;&#039;&#039;&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;loglevel:1,2,3,4,5&lt;br /&gt;
timer:30,120,300&lt;br /&gt;
mode:verbose,ultra,relaxed&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Leer&#039;&#039;&#039;&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;
&lt;br /&gt;
&lt;br /&gt;
Damit die jeweiligen Auswahllisten bereits mit dem korrekten Wert, der aktuell im Modul gesetzt ist, befüllt wird, muß dass Get Kommando im Modul implementiert sein und den jeweiligen Wert zurück liefern.&lt;br /&gt;
Am Beispiel des Sliders oben:&lt;br /&gt;
der Wert heisst dim und soll per Slider dargestellt sein. Damit der aktuelle Wert des dim im Slider in der Oberfläche bereits richtig voreingestellt ist, sollte die Get Funktion auf Anfrage nach dim auch den aktuellen Wert von dim zurück liefern.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweise&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
- bei den üblichen Kommandos wie on off sollte man auf noArg verzichten, da diese durch FHEMWeb automatisch in der Raumübersicht angezeigt werden. Wenn man hier noArg spezifiziert, so werden diese nicht neben dem Modul in der Raumübersicht angezeigt und der User muß sich diese vie webCmd dann erst selbst definieren, was natürlich unschön ist&lt;br /&gt;
&lt;br /&gt;
- der User kann sich in der Raumübersicht nach wie vor via webCmd eine entsprechende Steuerung anlegen.&lt;br /&gt;
&lt;br /&gt;
=== X_Attr ===&lt;br /&gt;
Die Attr-Funktion implementiert Prüfungen der bei einem &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt; übergebenen Werte und eventuell zusätzliche Aktionen wenn ein Attribut gesetzt wird. Die Liste der möglichen Attribute wird in der &amp;lt;code&amp;gt;X_Initialize-Funktion&amp;lt;/code&amp;gt; definiert (siehe oben). Fhem ruft bei einem Attr-Befehl die zuständige &amp;lt;code&amp;gt;X-Attr-Funktion&amp;lt;/code&amp;gt; auf und wenn diese keine Fehlermelhung sondern &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgibt, dann schreibt fhem.pl die bei &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt; angegebenen Werte in die jeweilige Datenstruktur &amp;lt;code&amp;gt;$attr{$name}-&amp;gt; ...&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
X_Attr(@)&lt;br /&gt;
{&lt;br /&gt;
	my ($cmd,$name,$aName,$aVal) = @_;&lt;br /&gt;
  	# $cmd can be &amp;quot;del&amp;quot; or &amp;quot;set&amp;quot;&lt;br /&gt;
	# $name is device name&lt;br /&gt;
	# aName and aVal are Attribute name and value&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: 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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Attr-Funktion bekommt nicht den Hash der Geräteinstanz übergeben, da sie ja auch keine Werte dort speichern muss sondern den Befehl &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;del&amp;lt;/code&amp;gt; je nachdem ob ein Attribut gesetzt oder gelöscht wird, den Namen der Geräteinstanz sowie den Namen des Attributs und seinen Wert.&lt;br /&gt;
Im obigen Beispiel wird für ein Attribut mit Namen Regex geprüft ob die Regex fehlerhaft ist. Falls sie ok ist, wird undef zurück gegegen und fhem.pl speichert den Wert des Attributs.&lt;br /&gt;
&lt;br /&gt;
=== X_Read ===&lt;br /&gt;
&lt;br /&gt;
Die X_Read-Funktion wird aus der Hauptschleife von FHEM aus aufgerufen wenn das Gerät, für das das Modul zuständig ist, Daten bereit gestellt hat, die gelesen werden können. Im folgenden Beispiel wird über eine serielle Schnittstelle (beziehungsweise über einen USB-To-Seriell-Konverter) von einem angeschlossenen Gerät gelesen. Dazu werden die bisher verfügbaren Daten mit der Funktion &amp;lt;code&amp;gt;DevIo_SimpleRead&amp;lt;/code&amp;gt; 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 zwischenspeichern. Es bietet sich an, dies im Hash der Geräteinstanz zu tun. Im Beispiel ist dies &amp;lt;code&amp;gt;$hash-&amp;gt;{buffer}&amp;lt;/code&amp;gt; an den die jeweils gelesenen Daten angehängt werden bis die folgende Prüfung ein für das jeweilige Protokoll passendes Frame identifiziert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&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;
	# read from serial device&lt;br /&gt;
	my $buf = DevIo_SimpleRead($hash);		&lt;br /&gt;
	return &amp;quot;&amp;quot; if ( !defined($buf) );&lt;br /&gt;
&lt;br /&gt;
	# convert to hex string to make parsing with regex easier&lt;br /&gt;
	$hash-&amp;gt;{buffer} .= unpack (&#039;H*&#039;, $buf);	&lt;br /&gt;
	Log3 $name, 5, &amp;quot;Current buffer content: &amp;quot; . $hash-&amp;gt;{buffer};&lt;br /&gt;
&lt;br /&gt;
	# did we already get a full frame?&lt;br /&gt;
	if ($hash-&amp;gt;{buffer} =~ &amp;quot;ff1002(.{4})(.*)1003(.{4})ff(.*)&amp;quot;) &lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/pre&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;{buffer}&amp;lt;/code&amp;gt;) stehen oder aus dem Kontext mit einem Regex-Match extrahiert werden und in Readings gespeichert werden (siehe unten).&lt;br /&gt;
&lt;br /&gt;
=== X_Ready ===&lt;br /&gt;
&lt;br /&gt;
muss noch beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub X_Ready($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	return DevIo_OpenDev($hash, 1, undef )&lt;br /&gt;
	  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;
	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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Lesen von Geräte und Speichern der Werte in Readings ==&lt;br /&gt;
&lt;br /&gt;
FHEM führt Module normalerweise nicht parallel aus. Daher wäre es ungünstig wenn Module Werte von einem Gerät abfragen und dann auf die Antwort des Geräts warten. In dieser Zeit wäre der Rest von FHEM blockiert. Die Ein- und Ausgabe sollte ohne Blockieren erfolgen und die Verarbeitung mehrerer Ein- und Ausgabekanäle quasi parallel 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 sein können. Auf Linux- bzw. Unix-basierten Plattformen wird der select-Befehl des Betriebssystems verwendet und entsprechend gibt es in FHEM eine selectlist, in der die Filedeskriptoren der Geräedateien (z.B. /dev/ttyUSBx etc.) gespeichert sind. &lt;br /&gt;
&lt;br /&gt;
In der zentralen Schleife von fhem.pl wird vereinfacht gesagt mit select überwacht, ob über eine der geöffneten Schnittstellen Daten zum Lesen anstehen. Wenn dies der Fall ist, dann wird die Lesefunktion (X_Read) des zuständigen Moduls aufgerufen, damit es die Daten entgegennimmt und die Schleife wird weiter ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Auf Windows-Systemen funktioniert dies anders. Hier können USB/Seriell-Geräte nicht per select ü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) implementieren, die 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 und Werte in Readings geschrieben.&lt;br /&gt;
&lt;br /&gt;
Zum Setzen von Readings ist es wie oben schon erwähnt empfehlenswert die Funktionen &amp;lt;code&amp;gt;readingsBeginUpdate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingsBulkUpdate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingsEndUpdate&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;readingsSingleUpdate&amp;lt;/code&amp;gt; aufzurufen. Dabei kann man auch angeben, ob dabei ein Event ausgelöst werden soll oder nicht.  Events erzeugen spürbare Last auf dem System (siehe 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;pre&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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; beim Aufruf von &amp;lt;code&amp;gt;readingsEndUpdate&amp;lt;/code&amp;gt; gibt an, dass Events ausgelöst werden, die auf Veränderungen der Readings reagieren.&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 &amp;lt;code&amp;gt;InternalTimer&amp;lt;code&amp;gt; verwenden. Man übergibt ihr den Zeitpunkt für den nächsten Aufruf, den Namen der Funktion, die aufgerufen werden soll, die zu übergebenden Parameter und ein Flag ob der erste Aufruf verzögert werden soll falls die Initialiserung des Geräts noch nicht abgeschlossen ist.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise könnte man für das Abfragen eines Geräts in der Define-Funktion den Timer folgendermassen setzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# initial request after 2 secs, there timer is set to interval for further update&lt;br /&gt;
InternalTimer(gettimeofday()+2, &amp;quot;X_GetUpdate&amp;quot;, $hash, 0);	&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;pre&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;
	InternalTimer(gettimeofday()+$hash-&amp;gt;{Interval}, &amp;quot;X_GetUpdate&amp;quot;, $hash, 1);&lt;br /&gt;
	Log3 $name, 4, &amp;quot;X: GetUpdate called ...&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf der Funktion könnte man dann 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 Read-Funktion zu implementieren, die beim Anstehen von Daten aufgerufen wird.&lt;br /&gt;
&lt;br /&gt;
== Logging / Debugging ==&lt;br /&gt;
Um Innerhalb eines Moduls eine Protokollmeldung in die Fhem-Logdatei zu schreiben, wird die Funktion Log3 aufgerufen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Log3 $name, 3, &amp;quot;X: Problem erkannt ...&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Parameter der Funktion Log3 sind der Name oder der Hash der Geräteinstanz, das Verbose-Level, in dem die Meldung sichtbar sein soll und die Meldung selbst.&lt;br /&gt;
Den Namen der Geräteinstanz kann man in den Funktionen, die den Hash übergeben bekommen einfach aus diesem Hash nehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um für ein neues Modul das Verbose-Level zu erhöhen, ohne gleich für das Gesamte FHEM alle Meldungen zu erzeugen kann man den Befehl &lt;br /&gt;
&amp;lt;code&amp;gt;attr gerätename verbose&amp;lt;/code&amp;gt; verwenden. Beispielsweise &amp;lt;code&amp;gt;attr PM verbose 5&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit bietet es sich an im Modul Meldungen, die im normalen Betrieb nicht benötigt werden, beim Aufruf von Log3 mit dem Level 4 oder 5 anzugeben. Wenn man dann bei der Fehlersuche mehr Meldungen sehen möchte, erhöht man mit attr X verbose das Level für das betroffene Gerät.&lt;br /&gt;
&lt;br /&gt;
== Noch zu beschreiben ==&lt;br /&gt;
* Zweistufiges Modell für Module&lt;br /&gt;
* Funktion X_Ready ...&lt;br /&gt;
* FW_summaryFn (wird von FHEMWEB aufgerufen fuer Raum-Uebersicht)&lt;br /&gt;
* FW_detailFn (wird von FHEMWEB aufgerufen fuer Detail-Ansicht)&lt;br /&gt;
* Bei der Beschreibung der von SetFn/GetFn zurueckgelieferten Meldung: Modifier fuer FHEMWEB wie :noArg, :slider, etc.&lt;br /&gt;
--&amp;gt;2014-01-14: done. Bitte um Review der Beschreibung&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Fhemrocks</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevelopmentModuleIntro&amp;diff=4333</id>
		<title>DevelopmentModuleIntro</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevelopmentModuleIntro&amp;diff=4333"/>
		<updated>2014-01-14T13:17:30Z</updated>

		<summary type="html">&lt;p&gt;Fhemrocks: /* X_Set */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
Dieser Text ist in Arbeit und muss noch an einigen Stellen ergänzt werden. &lt;br /&gt;
Insbesondere beschreibt der Text derzeit nur einstufige Module. Die Abgrenzung zu zweistufigen Modulen und deren Eigenschaften sollte noch ergänzt werden.&lt;br /&gt;
&lt;br /&gt;
Um neue Geräte in FHEM verfügbar zu machen, kann man ein eigenes Modul in Perl schreiben, das automatisch von FHEM geladen wird, wenn ein passendes Gerät in FHEM definiert wird. Das Modul definiert dann wie mit dem Gerät kommuniziert wird, stellt Werte (&amp;quot;Readings&amp;quot;) innerhalb von FHEM zur Verfügung oder erlaubt es das Gerät mit &amp;quot;Set&amp;quot;-Befehlen zu beeinflussen. Dieser Text soll den Einstieg in die Entwicklung eigener Module erleichtern.&lt;br /&gt;
&lt;br /&gt;
Mit dem FHEM-Befehl &amp;quot;define&amp;quot;, der typischerweise in die zentrale Konfigurationsdatei fhem.cfg  eingetragen wird, werden Geräte in FHEM definiert. Der Befehl sorgt dafür dass ein neues Modul bei Bedarf geladen wird und die Initialisierungsfunktion des Moduls aufgerufen wird. &lt;br /&gt;
&lt;br /&gt;
Damit das funktioniert müssen der Name des Geräts, der Name des Moduls und der Name der Initialisierungsfunktion zueinander passen. Das folgende Beispiel soll dies verdeutlichen:&lt;br /&gt;
&lt;br /&gt;
Ein Jeelink USB-Stick in einer Fritz-Box könnte beispielsweise mit dem Befehl &amp;lt;code&amp;gt;define JeeLink1 JeeLink /dev/ttyUSB0@57600&amp;lt;/code&amp;gt; definiert werden.&lt;br /&gt;
&lt;br /&gt;
In der fhem.pl wird der define-Befehl verarbeitet, geprüft, ob ein Modul mit Namen JeeLink schon geladen ist und falls nicht ein Modul mit Namen XY_JeeLink.pm im Modulverzeichnis (bei einer FritzBox z.B. /var/media/ftp/fhem/FHEM) gesucht und dann geladen. &lt;br /&gt;
Danach wird die Funktion JeeLink_Initialize aufgerufen.&lt;br /&gt;
Die Moduldatei muss also nach dem Namen des Geräts benannt werden und eine Funktion mit dem Namen des Geräts und einer _initialize Funktion enthalten.&lt;br /&gt;
In der Initialisierungsfunktion des Moduls werden dann die Namen der aller weiteren Funktionen des Moduls, die von fhem.pl aus aufgerufen werden, bekannt gemacht. Dazu wird der Hash - das ist die zentrale Datenstruktur für jede Instanz eines Gerätes - mit entsprechenden Werten gefüllt.&lt;br /&gt;
&lt;br /&gt;
== Der Hash einer Geräteinstanz ==&lt;br /&gt;
Der zentrale Speicherort für Informationen einer Geräteinstanz ist ein Hash, der seinerseits in fhem.pl von einem globalen Hash referenziert wird. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;$defs{$d}&amp;lt;/code&amp;gt; in fhem.pl verweist auf den Hash der Geräteinstanz mit Namen &amp;lt;code&amp;gt;$d&amp;lt;/code&amp;gt;. Diesen Hash bekommen die Funktionen eines Moduls oft von fhem.pl übergeben. In ihm stehen beispielsweise die internen Werte des Geräts, die im GUI als &amp;quot;Internals&amp;quot; angezeigt werden oder die Readings des Geräts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;$hash{NAME}&amp;lt;/code&amp;gt; enthält beispielsweise den Namen der Geräteinstanz, &amp;lt;code&amp;gt;$hash{TYPE}&amp;lt;/code&amp;gt;  enthält den Namen des Typs des Geräts, der ja auch für den Namen des Moduls verwendet wird. &lt;br /&gt;
&lt;br /&gt;
Ein Abfrageintervall, das beim define eines Geräts an die define-Funktion im Modul übergeben wird, würde typischerweise als &amp;quot;Internal&amp;quot; in &amp;lt;code&amp;gt;$hash-&amp;gt;{INTERVAL}&amp;lt;/code&amp;gt; abgelegt.&lt;br /&gt;
&lt;br /&gt;
Die Readings sind hier als weitere Unterstruktur gespeichert, beispielsweise &amp;lt;code&amp;gt;$hash{READINGS}{$ReadingName}{VAL}&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$hash{READINGS}{$ReadingName}{TIME}&amp;lt;/code&amp;gt;. Innerhalb von fhem.pl könnten die selben Readings beispielsweise über &amp;lt;code&amp;gt;$defs{$d}{READINGS}{$ReadingName}{VAL}&amp;lt;/code&amp;gt; adressiert werden, da &amp;lt;code&amp;gt;$defs{$d}&amp;lt;/code&amp;gt; ja auf den gleichen Hash verweist, der dann den Modul-Funktionen übergeben wird und innerhalb der Funktionen typischerweise als &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; verwendet wird.&lt;br /&gt;
&lt;br /&gt;
== Readings ==&lt;br /&gt;
Werte, die von einem Gerät gelesen werden und in FHEM zur Verfügung stehen werden Readings genannt. Sie werden wie oben beschrieben als Unterstruktur des Hashes der jeweiligen Geräteinstanz gespeichert, beispielsweise &amp;lt;code&amp;gt;$hash{READINGS}{$Temp}{VAL}&amp;lt;/code&amp;gt; für die Temperatur eines Fühlers, die als Reading gespeichert wurde. &lt;br /&gt;
Zum Setzen von Readings (siehe auch unten) ist es empfehlenswert die Funktionen &amp;lt;code&amp;gt;readingsBeginUpdate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingsBulkUpdate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingsEndUpdate&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;readingsSingleUpdate&amp;lt;/code&amp;gt; aufzurufen. Dabei kann man auch angeben, ob dabei ein Event ausgelöst werden soll oder nicht.  Events erzeugen spürbare Last auf dem System (siehe NotifyFn), das Ändern von Readings ohne dass dabei Events erzeugt werden jedoch nicht.&lt;br /&gt;
&lt;br /&gt;
für den lesenden Zugriff auf Readings steht die Funktion ReadingsVal($$$) zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
Readings werden im statefile von FHEM automatisch zwischengespeichert, damit sie nach einem Neustart sofort wieder zur Verfügung stehen, auch bevor sie vom Modul neu gesetzt oder aktualisiert werden. &lt;br /&gt;
&lt;br /&gt;
== Internals ==&lt;br /&gt;
Werte, die das Modul intern als Teil des Hashes speichert, die aber keine Readings sind, nennt man Internals. Sie werden abenfalls als Unterstruktur des Hashes der jeweiligen Geräteinstanz gespeichert, beispielswiese &amp;lt;code&amp;gt;$hash-&amp;gt;{INTERVAL}&amp;lt;/code&amp;gt; für ein Abfrageintervall, das beim Define-Befehl übergeben wurde und als Internal gespeichert wird. Internals werden jedoch im Gegensatz zu Readings nicht im statefile zwischengespeichert.&lt;br /&gt;
&lt;br /&gt;
== Attribute ==&lt;br /&gt;
Parameter eines Moduls können als so genannte Attribute gesetzt und damit dem Modul zur Verfügung gestellt werden. Attribute werden zusammen mit der Definition der Geräte beim Speichern der aktuellen Konfiguration von FHEM in die Konfigurationsdate geschrieben. Zur Laufzeit werden sie in der globalen Datenstruktur &amp;lt;code&amp;gt;$attr{$name}&amp;lt;/code&amp;gt; gespeichert. Ein Attribut mit dem Namen &amp;lt;code&amp;gt;header&amp;lt;/code&amp;gt; würde beispielswiese mit &amp;lt;code&amp;gt;$attr{$name}{header}&amp;lt;/code&amp;gt; adressiert (&amp;lt;code&amp;gt;$attr{$name}-&amp;gt;{&#039;header&#039;}&amp;lt;/code&amp;gt; wäre eine alternative aber unübliche Schreibweise für die selber Variable). &lt;br /&gt;
&lt;br /&gt;
Zum Auslesen solcher Attribute sollte die Funktion &amp;lt;code&amp;gt;AttrVal($$$)&amp;lt;/code&amp;gt; verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Welche Attribute ein Modul unterstützt sollte in der Funktion &amp;lt;code&amp;gt;X_Initialize&amp;lt;/code&amp;gt; durch Setzen der Variable &amp;lt;code&amp;gt;$hash-&amp;gt;{AttrList}&amp;lt;/code&amp;gt; bekannt gemacht werden (siehe unten). Wenn beim Setzen von Attributen die Werte geprüft werden sollen oder zusätzliche Funktionalität implementiert werden muss, dann kann dies in der Funktion &amp;lt;code&amp;gt;X_Attr&amp;lt;/code&amp;gt; (siehe unten) implementiert werden.&lt;br /&gt;
&lt;br /&gt;
== Die wichtigsten Funktionen in einem Modul ==&lt;br /&gt;
Eine typische Grundfunktion eines einfachen Moduls ist das Auslesen von Werten von einem physischen Gerät und Bereitstellen dieser Werte innerhalb von FHEM als Readings. Das Geräte könnte beispielsweise an einem USB-Port angeschlossen sein. Folgende Funktionen könnte man beispielsweise in einem Modul mit Namen X implementieren:&lt;br /&gt;
* X_Initialize (initialisiert das Modul und gibt de Namen der zusätzlichen Funktionen bekannt)&lt;br /&gt;
* X_Define (wird beim &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt; aufgerufen&lt;br /&gt;
* X_Undef (wird beim Löschen einer Geräteinstanz aufgerufen - Gegenteil zu &amp;lt;code&amp;gt;define&amp;lt;/code&amp;gt;)&lt;br /&gt;
* X_Set (wird beim Befehl &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; aufgerufen um Daten an das Gerät zu senden)&lt;br /&gt;
* X_Get (wird beim Befehl &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; aufgerufen um Daten vom Gerät abzufragen)&lt;br /&gt;
* X_Attr (wird beim Befehl &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt; aufgerufen um beispielsweise Werte zu prüfen)&lt;br /&gt;
* X_Read (wird vom globalen select aufgerufen, falls Daten zur Verfuegung stehen)&lt;br /&gt;
* X_Parse (wird bei zweistufigen Modulen vom Dispatch aufgerufen und muss hier noch beschrieben werden)&lt;br /&gt;
* X_Ready (wird unter windows als ReadFn-Erstatz benoetigt bzw. um zu pruefen, ob ein Geraet wieder eingesteckt ist)&lt;br /&gt;
* X_Notify (falls man benachrichtigt werden will)&lt;br /&gt;
* X_Rename (falls ein Gerät umbenannt wird)&lt;br /&gt;
&lt;br /&gt;
Die Funktionen werden im folgenden beschrieben (soweit diese Seite inzwischen vollständig ist):&lt;br /&gt;
&lt;br /&gt;
=== X_Initialize ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub X_Initialize($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/pre&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 einen Hash für das Modul als zentrale Datenstruktur übergeben. &lt;br /&gt;
Die Initialize-Funktion setzt dann weitere Funktionsnamen, die im Modul implementiert sind, in diesen Hash:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&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;{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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;X&amp;lt;/code&amp;gt; ist wieder durch den Modulnamen ohne die vorangestellte Zahl zu ersetzen. &lt;br /&gt;
Entsprechend können auch die Funktionen &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; etc. bekannt gemacht werden.&lt;br /&gt;
&lt;br /&gt;
Darüber hinaus sollten die vom Modul unterstützen Attribute definiert werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In Fhem.pl werden dann die entsprechenden Werte 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 Funktion &amp;lt;code&amp;gt;X_Attr&amp;lt;/code&amp;gt; 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 verfügbar werden wenn das Modul zum Setzen von Readings die Funktionen readingsBeginUpdate, readingsBulkUpdate, readingsEndUpdate oder readingsSingleUpdate 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.&lt;br /&gt;
&lt;br /&gt;
=== X_Define ===&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-Fubktion 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.ä.)&lt;br /&gt;
Sie beginnt typischerweise mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub HTTPMOD_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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Als Übergabeparameter bekommt die Define-Funktion den Hash der Geräteinstanz sowie den Rest der Parameter, die im Befehl angegeben wurden. Welche und wie viele Parameter &lt;br /&gt;
akzeptiert werden ist Sache dieser Funktion. Im obigen Beispiel wird alles nach dem übergebenen Hash in ein Array aufgeteilt 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;pre&amp;gt;&lt;br /&gt;
my $name 	= $a[0];&lt;br /&gt;
my $url 	= $a[2];&lt;br /&gt;
my $inter	= 300;&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; 5, default is 300&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&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 typischerwiese als Internals im Hash der Geräteinstanz gespeichert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$hash-&amp;gt;{url} 		= $url;&lt;br /&gt;
$hash-&amp;gt;{Interval}	= $inter;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn eine physische Schnittstelle geöffnet werden soll und dann bei verfügbaren Eingabedaten eine Lese-Funktion von Fhem aufgerufen werden soll, dann kann man in der Define-Funktion die Funktion DevIo_OpenDev aufrufen, die sich um alles weitere kümmert. Sie öffnet die Schnittstelle und fügt den Filedeskriptor an die globale Liste offener Verbindungen (selectlist / readyfnlist) an. Damit kann Fhem in seiner Hauptschleife erkennen, von welchem Gerät Daten bereit stehen und die zuständigen Funktionen aufrufen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $ret = DevIo_OpenDev( $hash, 0, &amp;quot;X_DevInit&amp;quot; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die optionale Funktion &amp;lt;code&amp;gt;X_DevIni&amp;lt;/code&amp;gt;t wird zur weiteren Initialisierung der Verbindung von &amp;lt;code&amp;gt;DevIo_OpenDev&amp;lt;/code&amp;gt; aufgerufen. Der zweite Übergabeparameter an &amp;lt;code&amp;gt;DevIo_OpenDev&amp;lt;/code&amp;gt; (hier &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;) steht für reopen und wird benötigt, da die Funktion auch aufgerufen wird, wenn ein USB-Geräte beispielsweise im Betrieb aus- und wieder eingesteckt wird. In diesem Fall wird die Funktion mit &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; aufgerufen.&lt;br /&gt;
&lt;br /&gt;
=== X_Undef ===&lt;br /&gt;
&lt;br /&gt;
Die &amp;lt;code&amp;gt;Undef&amp;lt;/code&amp;gt;-Funktion ist das Gegenstück zur &amp;lt;code&amp;gt;Define&amp;lt;/code&amp;gt;-Funktion und wird aufgerufen wenn ein Gerät mit &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt; gelöscht wird. 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 später).&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub WKRCD4_Undef($$)    &lt;br /&gt;
{                     &lt;br /&gt;
	my ( $hash, $arg ) = @_;       &lt;br /&gt;
	DevIo_CloseDev($hash);         &lt;br /&gt;
	RemoveInternalTimer($hash);    &lt;br /&gt;
	return undef;                  &lt;br /&gt;
}    &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== X_Get ===&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. Einige Module verwenden für diese Funktion einen Hash im Modul, der die möglichen &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt;-Optionen mit zusätzlichen Werten definiert:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my X_gets = (&lt;br /&gt;
	&amp;quot;TempSoll&amp;quot;	=&amp;gt; &amp;quot;XY&amp;quot;,&lt;br /&gt;
	&amp;quot;Steilheit&amp;quot;	=&amp;gt; &amp;quot;Z&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In der Get-Funktion selbst werden dann die übergebenen Parameter gegen diesen Hash geprüft.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub X_Get($@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, @a ) = @_;&lt;br /&gt;
	return &amp;quot;\&amp;quot;get X\&amp;quot; needs at least one argument&amp;quot; if ( @a &amp;lt; 2 );&lt;br /&gt;
	my $name = shift @a;&lt;br /&gt;
	my $opt = shift @a;&lt;br /&gt;
	if(!$X_gets{$opt}) {&lt;br /&gt;
		my @cList = keys %X_gets;&lt;br /&gt;
		return &amp;quot;Unknown argument $attr, choose one of &amp;quot; . join(&amp;quot; &amp;quot;, @cList);&lt;br /&gt;
	}&lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Ausgabe der Meldung mit &amp;lt;code&amp;gt;... choose one of ...&amp;lt;/code&amp;gt; ist dabei 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 abfragen und diesen als Return-Wert der Get-Funktion zurückgeben.&lt;br /&gt;
&lt;br /&gt;
=== X_Set ===&lt;br /&gt;
Die Set-Funktion ist das Gegenteil zur Get-Funktion. Sie ist dafür gedacht, Werte zum physischen Gerät zu schicken. Falls nur interne Werte im Modul gesetzt werden sollen, so sollte statt Set die Attr-Funktion verwendet werden. Attribute werden bei Save-Config auch in der Fhem.cfg gesichert. Set-Befehle nicht.&lt;br /&gt;
 &lt;br /&gt;
Eine Set-Funktion ist ähnlich aufgebaut wie die Get-Funktion, sie bekommt jedoch nach dem Namen der Option auch den zu setzenden Wert übergeben.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub X_Set($@)&lt;br /&gt;
{&lt;br /&gt;
	my ( $hash, @a ) = @_;&lt;br /&gt;
	return &amp;quot;\&amp;quot;set X\&amp;quot; needs at least an argument&amp;quot; if ( @a &amp;lt; 2 );&lt;br /&gt;
	my $name = shift @a;&lt;br /&gt;
	my $opt = shift @a;&lt;br /&gt;
	my $arg = join(&amp;quot;&amp;quot;, @a);&lt;br /&gt;
	&lt;br /&gt;
	if(!defined($X_sets{$attr})) {&lt;br /&gt;
		my @cList = keys %X_sets;&lt;br /&gt;
		return &amp;quot;Unknown argument $attr, choose one of &amp;quot; . join(&amp;quot; &amp;quot;, @cList);&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Den einzelnen Befehlen, die das Modul versteht und die via set &amp;lt;name&amp;gt; &amp;lt;cmd&amp;gt; &amp;lt;arg1&amp;gt; übergeben werden kann man bei der Rückgabe auch zulässige Auswahlwerte für Kommando Argumente mitgeben. Aus diesen Auswahlwerten erzeugt FHEM-Web dann z.B. Drop-Down Boxen in der Detailansicht, mit denen der Anwender die jeweiligen Argumente auswählen kann. Dabei ist jeweils vor dem Doppelpunkt der Name des Kommandoes anzugeben und nach dem Doppelpunkt der jeweilige Auswahlwert.&lt;br /&gt;
Es gibt dabei folgende Möglichkeiten:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;noArg&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
es werden keine weiteren Argumente mehr benötigt&lt;br /&gt;
&amp;lt;pre&amp;gt;on:noArg&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;slider&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
es wird ein Schieberegler für den Wert angezeigt. Dabei Minimum, Schrittweite und Maximum angeben&lt;br /&gt;
&amp;lt;pre&amp;gt;dim:slider,0,1,100&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RGB&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
es wird ein Colorpicker angezeigt, der dem Anwender die Auswahl einer Farbe ermöglicht. Bitte dazu auch den Wiki Artikel zum Colorpicker lesen, da im Modul noch weiterer Code eingefügt werden muß.&lt;br /&gt;
&amp;lt;pre&amp;gt;rgb:colorpicker,RGB&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Liste&#039;&#039;&#039;&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;loglevel:1,2,3,4,5&lt;br /&gt;
timer:30,120,300&lt;br /&gt;
mode:verbose,ultra,relaxed&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Leer&#039;&#039;&#039;&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;
&lt;br /&gt;
&lt;br /&gt;
Damit die jeweiligen Auswahllisten bereits mit dem korrekten Wert, der aktuell im Modul gesetzt ist, befüllt wird, muß dass Get Kommando im Modul implementiert sein und den jeweiligen Wert zurück liefern.&lt;br /&gt;
Am Beispiel des Sliders oben:&lt;br /&gt;
der Wert heisst dim und soll per Slider dargestellt sein. Damit der aktuelle Wert des dim im Slider in der Oberfläche bereits richtig voreingestellt ist, sollte die Get Funktion auf Anfrage nach dim auch den aktuellen Wert von dim zurück liefern.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweise&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
- bei den üblichen Kommandos wie on off sollte man auf noArg verzichten, da diese durch FHEMWeb automatisch in der Raumübersicht angezeigt werden. Wenn man hier noArg spezifiziert, so werden diese nicht neben dem Modul in der Raumübersicht angezeigt und der User muß sich diese vie webCmd dann erst selbst definieren, was natürlich unschön ist&lt;br /&gt;
&lt;br /&gt;
- der User kann sich in der Raumübersicht nach wie vor via webCmd eine entsprechende Steuerung anlegen.&lt;br /&gt;
&lt;br /&gt;
=== X_Attr ===&lt;br /&gt;
Die Attr-Funktion implementiert Prüfungen der bei einem &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt; übergebenen Werte und eventuell zusätzliche Aktionen wenn ein Attribut gesetzt wird. Die Liste der möglichen Attribute wird in der &amp;lt;code&amp;gt;X_Initialize-Funktion&amp;lt;/code&amp;gt; definiert (siehe oben). Fhem ruft bei einem Attr-Befehl die zuständige &amp;lt;code&amp;gt;X-Attr-Funktion&amp;lt;/code&amp;gt; auf und wenn diese keine Fehlermelhung sondern &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; zurückgibt, dann schreibt fhem.pl die bei &amp;lt;code&amp;gt;attr&amp;lt;/code&amp;gt; angegebenen Werte in die jeweilige Datenstruktur &amp;lt;code&amp;gt;$attr{$name}-&amp;gt; ...&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
X_Attr(@)&lt;br /&gt;
{&lt;br /&gt;
	my ($cmd,$name,$aName,$aVal) = @_;&lt;br /&gt;
  	# $cmd can be &amp;quot;del&amp;quot; or &amp;quot;set&amp;quot;&lt;br /&gt;
	# $name is device name&lt;br /&gt;
	# aName and aVal are Attribute name and value&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: 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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Attr-Funktion bekommt nicht den Hash der Geräteinstanz übergeben, da sie ja auch keine Werte dort speichern muss sondern den Befehl &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;del&amp;lt;/code&amp;gt; je nachdem ob ein Attribut gesetzt oder gelöscht wird, den Namen der Geräteinstanz sowie den Namen des Attributs und seinen Wert.&lt;br /&gt;
Im obigen Beispiel wird für ein Attribut mit Namen Regex geprüft ob die Regex fehlerhaft ist. Falls sie ok ist, wird undef zurück gegegen und fhem.pl speichert den Wert des Attributs.&lt;br /&gt;
&lt;br /&gt;
=== X_Read ===&lt;br /&gt;
&lt;br /&gt;
Die X_Read-Funktion wird aus der Hauptschleife von FHEM aus aufgerufen wenn das Gerät, für das das Modul zuständig ist, Daten bereit gestellt hat, die gelesen werden können. Im folgenden Beispiel wird über eine serielle Schnittstelle (beziehungsweise über einen USB-To-Seriell-Konverter) von einem angeschlossenen Gerät gelesen. Dazu werden die bisher verfügbaren Daten mit der Funktion &amp;lt;code&amp;gt;DevIo_SimpleRead&amp;lt;/code&amp;gt; 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 zwischenspeichern. Es bietet sich an, dies im Hash der Geräteinstanz zu tun. Im Beispiel ist dies &amp;lt;code&amp;gt;$hash-&amp;gt;{buffer}&amp;lt;/code&amp;gt; an den die jeweils gelesenen Daten angehängt werden bis die folgende Prüfung ein für das jeweilige Protokoll passendes Frame identifiziert.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&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;
	# read from serial device&lt;br /&gt;
	my $buf = DevIo_SimpleRead($hash);		&lt;br /&gt;
	return &amp;quot;&amp;quot; if ( !defined($buf) );&lt;br /&gt;
&lt;br /&gt;
	# convert to hex string to make parsing with regex easier&lt;br /&gt;
	$hash-&amp;gt;{buffer} .= unpack (&#039;H*&#039;, $buf);	&lt;br /&gt;
	Log3 $name, 5, &amp;quot;Current buffer content: &amp;quot; . $hash-&amp;gt;{buffer};&lt;br /&gt;
&lt;br /&gt;
	# did we already get a full frame?&lt;br /&gt;
	if ($hash-&amp;gt;{buffer} =~ &amp;quot;ff1002(.{4})(.*)1003(.{4})ff(.*)&amp;quot;) &lt;br /&gt;
	...&lt;br /&gt;
&amp;lt;/pre&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;{buffer}&amp;lt;/code&amp;gt;) stehen oder aus dem Kontext mit einem Regex-Match extrahiert werden und in Readings gespeichert werden (siehe unten).&lt;br /&gt;
&lt;br /&gt;
=== X_Ready ===&lt;br /&gt;
&lt;br /&gt;
muss noch beschrieben werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sub X_Ready($)&lt;br /&gt;
{&lt;br /&gt;
	my ($hash) = @_;&lt;br /&gt;
	return DevIo_OpenDev($hash, 1, undef )&lt;br /&gt;
	  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;
	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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Lesen von Geräte und Speichern der Werte in Readings ==&lt;br /&gt;
&lt;br /&gt;
FHEM führt Module normalerweise nicht parallel aus. Daher wäre es ungünstig wenn Module Werte von einem Gerät abfragen und dann auf die Antwort des Geräts warten. In dieser Zeit wäre der Rest von FHEM blockiert. Die Ein- und Ausgabe sollte ohne Blockieren erfolgen und die Verarbeitung mehrerer Ein- und Ausgabekanäle quasi parallel 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 sein können. Auf Linux- bzw. Unix-basierten Plattformen wird der select-Befehl des Betriebssystems verwendet und entsprechend gibt es in FHEM eine selectlist, in der die Filedeskriptoren der Geräedateien (z.B. /dev/ttyUSBx etc.) gespeichert sind. &lt;br /&gt;
&lt;br /&gt;
In der zentralen Schleife von fhem.pl wird vereinfacht gesagt mit select überwacht, ob über eine der geöffneten Schnittstellen Daten zum Lesen anstehen. Wenn dies der Fall ist, dann wird die Lesefunktion (X_Read) des zuständigen Moduls aufgerufen, damit es die Daten entgegennimmt und die Schleife wird weiter ausgeführt.&lt;br /&gt;
&lt;br /&gt;
Auf Windows-Systemen funktioniert dies anders. Hier können USB/Seriell-Geräte nicht per select ü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) implementieren, die 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 und Werte in Readings geschrieben.&lt;br /&gt;
&lt;br /&gt;
Zum Setzen von Readings ist es wie oben schon erwähnt empfehlenswert die Funktionen &amp;lt;code&amp;gt;readingsBeginUpdate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingsBulkUpdate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;readingsEndUpdate&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;readingsSingleUpdate&amp;lt;/code&amp;gt; aufzurufen. Dabei kann man auch angeben, ob dabei ein Event ausgelöst werden soll oder nicht.  Events erzeugen spürbare Last auf dem System (siehe 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;pre&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;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt; beim Aufruf von &amp;lt;code&amp;gt;readingsEndUpdate&amp;lt;/code&amp;gt; gibt an, dass Events ausgelöst werden, die auf Veränderungen der Readings reagieren.&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 &amp;lt;code&amp;gt;InternalTimer&amp;lt;code&amp;gt; verwenden. Man übergibt ihr den Zeitpunkt für den nächsten Aufruf, den Namen der Funktion, die aufgerufen werden soll, die zu übergebenden Parameter und ein Flag ob der erste Aufruf verzögert werden soll falls die Initialiserung des Geräts noch nicht abgeschlossen ist.&lt;br /&gt;
&lt;br /&gt;
Beispielsweise könnte man für das Abfragen eines Geräts in der Define-Funktion den Timer folgendermassen setzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# initial request after 2 secs, there timer is set to interval for further update&lt;br /&gt;
InternalTimer(gettimeofday()+2, &amp;quot;X_GetUpdate&amp;quot;, $hash, 0);	&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;pre&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;
	InternalTimer(gettimeofday()+$hash-&amp;gt;{Interval}, &amp;quot;X_GetUpdate&amp;quot;, $hash, 1);&lt;br /&gt;
	Log3 $name, 4, &amp;quot;X: GetUpdate called ...&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Im weiteren Verlauf der Funktion könnte man dann 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 Read-Funktion zu implementieren, die beim Anstehen von Daten aufgerufen wird.&lt;br /&gt;
&lt;br /&gt;
== Logging / Debugging ==&lt;br /&gt;
Um Innerhalb eines Moduls eine Protokollmeldung in die Fhem-Logdatei zu schreiben, wird die Funktion Log3 aufgerufen:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Log3 $name, 3, &amp;quot;X: Problem erkannt ...&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Parameter der Funktion Log3 sind der Name oder der Hash der Geräteinstanz, das Verbose-Level, in dem die Meldung sichtbar sein soll und die Meldung selbst.&lt;br /&gt;
Den Namen der Geräteinstanz kann man in den Funktionen, die den Hash übergeben bekommen einfach aus diesem Hash nehmen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um für ein neues Modul das Verbose-Level zu erhöhen, ohne gleich für das Gesamte FHEM alle Meldungen zu erzeugen kann man den Befehl &lt;br /&gt;
&amp;lt;code&amp;gt;attr gerätename verbose&amp;lt;/code&amp;gt; verwenden. Beispielsweise &amp;lt;code&amp;gt;attr PM verbose 5&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Damit bietet es sich an im Modul Meldungen, die im normalen Betrieb nicht benötigt werden, beim Aufruf von Log3 mit dem Level 4 oder 5 anzugeben. Wenn man dann bei der Fehlersuche mehr Meldungen sehen möchte, erhöht man mit attr X verbose das Level für das betroffene Gerät.&lt;br /&gt;
&lt;br /&gt;
== Noch zu beschreiben ==&lt;br /&gt;
* Zweistufiges Modell für Module&lt;br /&gt;
* Funktion X_Ready ...&lt;br /&gt;
* FW_summaryFn (wird von FHEMWEB aufgerufen fuer Raum-Uebersicht)&lt;br /&gt;
* FW_detailFn (wird von FHEMWEB aufgerufen fuer Detail-Ansicht)&lt;br /&gt;
* Bei der Beschreibung der von SetFn/GetFn zurueckgelieferten Fehlermeldung: Modifier fuer FHEMWEB wie :noArg, :slider, etc.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Fhemrocks</name></author>
	</entry>
</feed>