<?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=Torxgewinde</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=Torxgewinde"/>
	<link rel="alternate" type="text/html" href="http://wiki.fhem.de/wiki/Spezial:Beitr%C3%A4ge/Torxgewinde"/>
	<updated>2026-05-20T03:58:08Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Ntfy&amp;diff=40750</id>
		<title>Ntfy</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Ntfy&amp;diff=40750"/>
		<updated>2026-01-22T17:19:56Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Weitere Quelle von Byterazor gefunden, Hinweis entfernt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==📬 NTFY.sh: Push Nachrichten an iOS, Android, PC, Command-Line, E-Mail &amp;amp; Telefon==&lt;br /&gt;
[[Datei:NTFY Screenshot mit einem Bild.png|alternativtext=NTFY Screenshot mit einem Bild|mini|300x300px|NTFY Screenshot mit einem Bild]]&lt;br /&gt;
[https://NTFY.sh NTFY.sh] bietet Push-Dienste auf iOS, Android, Console, E-Mail, ja sogar als Anruf auf ein Telefon an. Es ist damit ähnlich zu dem, was man mit PushOver, PushBullet oder auch Gotify anstellen kann, wobei NTFY auch Kriterien wie &amp;quot;Self-hosted&amp;quot; und echte &amp;quot;iOS-Pushnachrichten&amp;quot; erfüllt. Man kann damit von FHEM aus alle diese Kanäle mit einer Push-Nachricht erreichen. Die Nutzung ist extrem einfach, man setzt einen HTTP-Aufruf ab, um eine Push-Nachricht zu versenden. NTFY.sh betreibt sogar einen Gratis-Server, den man nutzen darf. Man kann den Server auch selber hosten, wenn man will.&lt;br /&gt;
&lt;br /&gt;
Öffnet testweise diesen Link, dort tauchen die Nachrichten auch in der PWA (Webapp) auf: https://ntfy.sh/FreundlichenGruesseAnAlleFHEMNutzer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;color:#ffff00; background:#ff0000; border: solid 1px black&amp;quot;&amp;gt;&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Nutzt man eine öffentliche Instanz ist das Topic so zu wählen, dass es wie ein Passwort funktioniert und schwer zu erraten ist. Wählt man ein einfaches Topic, können andere mitlesen und auch Nachrichten senden. Dies kann zu ungewollten Effekten führen!&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== NTFY mit anderen Systemen nutzen/verbinden ===&lt;br /&gt;
Auch ganz ohne FHEM, also aus anderen Systemen, kann man an NTFY Nachrichten senden und empfangen. So kann man sich aus iOS zum Beispiel per Webhook etwas an NTFY senden, oder auch von IP-Kameras Infos an den NTFY Server senden und dann von den diversen Clients empfangen und verarbeiten.&lt;br /&gt;
&lt;br /&gt;
==== Apps (iOS, Android und PWA) ====&lt;br /&gt;
Ganz wichtig für die meisten Nutzer ist natürlich der Empfang von NTFY Nachrichten mit:&lt;br /&gt;
&lt;br /&gt;
* der Android-App ([https://play.google.com/store/apps/details?id=io.heckel.ntfy&amp;amp;pli=1 Playstore], [https://f-droid.org/en/packages/io.heckel.ntfy/ F-Droid]),&lt;br /&gt;
* der [https://apps.apple.com/us/app/ntfy/id1625396347 iOS-App] *, oder&lt;br /&gt;
* der [https://ntfy.sh/app PWA] (Progressive-Web-App)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis zur nativen iOS-App:&#039;&#039;&#039; [https://github.com/binwiederhier/ntfy/issues/1226 Zur Zeit] funktioniert die Anzeige von Anhängen unter iOS, iPadOS, MacOS &#039;&#039;&#039;nur&#039;&#039;&#039; über die Web-App (zu finden unter https://ntfy.sh/app). Um die PWA unter iOS/MacOS fast wie eine gewöhnliche App zu nutzen, kann man die Web-Seite an den &amp;quot;Home-Screen&amp;quot; (iOS) senden oder &amp;quot;Zum Dock hinzufügen&amp;quot; (MacOS). Anschließend noch &amp;quot;Benachrichtigungen erlauben&amp;quot; aktivieren.&lt;br /&gt;
&lt;br /&gt;
==== CLI, Commandline, aus Skripten heraus Nachrichten senden ====&lt;br /&gt;
Zum Beispiel kann man mit einem CURL-Befehl etwas senden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
curl -d &amp;quot;Your message content here&amp;quot; \&lt;br /&gt;
    -H &amp;quot;Title: Bla&amp;quot; \&lt;br /&gt;
    https://ntfy.sh/FreundlichenGruesseAnAlleFHEMNutzer&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder auch mit WGET:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wget --method=POST \&lt;br /&gt;
    --body-data=&amp;quot;Your message content here&amp;quot; \&lt;br /&gt;
    --header=&amp;quot;Title: Bla&amp;quot; \&lt;br /&gt;
    https://ntfy.sh/FreundlichenGruesseAnAlleFHEMNutzer -q -O -&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Per Webhook Nachrichten senden ====&lt;br /&gt;
Via HTTP-GET als [https://ntfy.sh/FreundlichenGruesseAnAlleFHEMNutzer/publish?message=Your%20message%20content%20here&amp;amp;title=Bla Webhook] kann man ebenfalls Nachrichten via NTFY senden. Diese Option eignet sich besonders für weitere Geräte wie Shelly, Alexa oder auch Apple-Shortcuts und viele weitere Geräte dieser Art:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
https://ntfy.sh/FreundlichenGruesseAnAlleFHEMNutzer/publish?message=Your%20message%20content%20here&amp;amp;title=Bla&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Per E-Mail Nachrichten senden ====&lt;br /&gt;
Der NTFY Server kann auch per E-Mail erreicht werden, sofern dies so eingestellt wurde. Dies ist sehr praktisch, wenn ein System wie zum Beispiel eine IP-Kamera E-Mails bei Bewegung versenden kann, oder wenn ein CI-Build E-Mails verschickt. Die Doku dazu ist unter: https://docs.ntfy.sh/config/?h=mail#e-mail-publishing&lt;br /&gt;
&lt;br /&gt;
Alternativ, kann man ein E-Mail Postfach überwachen und bei neuen E-Mails diese in eine NTFY Nachricht umwandeln und sich dann an die NTFY-Apps senden: [https://forum.fhem.de/index.php?topic=139868.0 📧 Fetchmail, Email von IP-Cam ohne Verzögerung an NTFY/FHEM senden]&lt;br /&gt;
&lt;br /&gt;
===Anbindung in FHEM per Modul/Device &amp;quot;NTFY_CLIENT&amp;quot;===&lt;br /&gt;
Es gibt ein FHEM-Device, mit dem man NTFY nutzen kann: [https://rm.byterazor.de/projects/fhem-ntfy fhem-ntfy]&lt;br /&gt;
&lt;br /&gt;
====&#039;&#039;&#039;Schritte um NTFY_CLIENT einzurichten:&#039;&#039;&#039;====&lt;br /&gt;
Den Quelltext für das NTFY_CLIENT-Device kann man von Extern wie folgt in FHEM laden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
update add https://rm.byterazor.de/upd-fhem-ntfy/controls_byterazor-fhem-ntfy.txt&lt;br /&gt;
update&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fall die Adresse nicht erreichbar ist kann man alternativ das Modul herunterladen von:&lt;br /&gt;
&lt;br /&gt;
https://gitea.federationhq.de/byterazor/FHEM-NTFY/src/branch/main/FHEM&lt;br /&gt;
&lt;br /&gt;
Nachdem man den Quelltext in FHEM hat, sollte FHEM einmal neugestartet werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
shutdown restart&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Danach kann man ein NTFY_CLIENT-Device anlegen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod NTFY0 NTFY_CLIENT https://ntfy.sh&lt;br /&gt;
attr NTFY0 defaultPriority default&lt;br /&gt;
attr NTFY0 defaultTopic FreundlichenGruesseAnAlleFHEMNutzer&lt;br /&gt;
attr NTFY0 room Experimente&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Senden von NTFY Nachrichten aus FHEM heraus: ====&lt;br /&gt;
Senden kann man Nachrichten mit:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set NTFY0 publish @FreundlichenGruesseAnAlleFHEMNutzer Testnachricht!&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Die genaue Nutzung sind in dem Hilfetext zum Device zu finden. Das &amp;quot;@&amp;quot; setzt zum Beispiel das Topic für NTFY.&lt;br /&gt;
&lt;br /&gt;
==== Empfangen und reagieren auf Nachrichten ====&lt;br /&gt;
Im Log wird es Meldungen in der folgenden Art geben wenn Nachrichten empfangen werden:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2024-10-29 19:01:16.714 NTFY_TOPIC NTFY0_FreundlichenGruesseAnAlleFHEMNutzer nrReceivedMessages: 7&lt;br /&gt;
2024-10-29 19:01:16.715 NTFY_CLIENT NTFY0 subscriptions: FreundlichenGruesseAnAlleFHEMNutzer&lt;br /&gt;
2024-10-29 19:01:16.715 NTFY_CLIENT NTFY0 nrReceivedMessages: 7&lt;br /&gt;
2024-10-29 19:01:16.715 NTFY_CLIENT NTFY0 lastReceivedTitle: Bla&lt;br /&gt;
2024-10-29 19:01:16.715 NTFY_CLIENT NTFY0 lastReceivedData: Your message content here&lt;br /&gt;
2024-10-29 19:01:16.715 NTFY_CLIENT NTFY0 lastReceivedRawMessage: {&amp;quot;id&amp;quot;:&amp;quot;oU5iqrdSFoeY&amp;quot;,&amp;quot;time&amp;quot;:1730224876,&amp;quot;expires&amp;quot;:1730268076,&amp;quot;event&amp;quot;:&amp;quot;message&amp;quot;,&amp;quot;topic&amp;quot;:&amp;quot;FreundlichenGruesseAnAlleFHEMNutzer&amp;quot;,&amp;quot;title&amp;quot;:&amp;quot;Bla&amp;quot;,&amp;quot;message&amp;quot;:&amp;quot;Your message content here&amp;quot;}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hier ein Beispiel mit einem notify-Device, dass auf die Nachrichten &amp;quot;Garage auf&amp;quot; oder &amp;quot;Garage zu&amp;quot; reagieren soll:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod GarageNotify notify NTFY0:lastReceivedData:.* {\&lt;br /&gt;
	Log(1, &amp;quot;$NAME: $EVTPART1, $EVTPART2, $EVENT&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	if (&amp;quot;$EVTPART1 $EVTPART2&amp;quot; eq &amp;quot;Garage zu&amp;quot;) {\&lt;br /&gt;
		Log(1, &amp;quot;Garage soll zu gemacht werden&amp;quot;);;\&lt;br /&gt;
		fhem(&amp;quot;set NTFY0 publish Ich werde die Garage nun schliessen&amp;quot;);;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	if (&amp;quot;$EVTPART1 $EVTPART2&amp;quot; eq &amp;quot;Garage auf&amp;quot;) {\&lt;br /&gt;
		Log(1, &amp;quot;Garage soll auf gemacht werden&amp;quot;);;\&lt;br /&gt;
		fhem(&amp;quot;set NTFY0 publish Ich werde die Garage nun oeffnen&amp;quot;);;\&lt;br /&gt;
	}\&lt;br /&gt;
}&lt;br /&gt;
attr GarageNotify room Experimente&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die zugehörigen Webhooks für obiges Beispiel-notify-Device wären dann:&lt;br /&gt;
&lt;br /&gt;
* https://ntfy.sh/FreundlichenGruesseAnAlleFHEMNutzer/publish?message=Garage%20auf&lt;br /&gt;
* https://ntfy.sh/FreundlichenGruesseAnAlleFHEMNutzer/publish?message=Garage%20zu&lt;br /&gt;
&lt;br /&gt;
Testen kann man direkt gegen die bekannteste und vom NTFY-Autor geschaffene NTFY-Installation: https://ntfy.sh/FreundlichenGruesseAnAlleFHEMNutzer&lt;br /&gt;
&lt;br /&gt;
===Anbindung per HTTPMOD und Websocket===&lt;br /&gt;
Neben der Option per speziellem NTFY Device kann man auch das HTTPMOD-Device nutzen um Nachrichten oder Attachments/Dateien zu senden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod NTFY HTTPMOD none 0&lt;br /&gt;
attr NTFY userattr Filename Markdown NtfyServer Priority Title Topic password username&lt;br /&gt;
attr NTFY Filename test.png&lt;br /&gt;
attr NTFY Markdown true&lt;br /&gt;
attr NTFY NtfyServer ntfy.sh&lt;br /&gt;
attr NTFY Priority high&lt;br /&gt;
attr NTFY Title Titel aus UserAttr heraus&lt;br /&gt;
attr NTFY Topic FreundlichenGruesseAnAlleFHEMNutzer&lt;br /&gt;
attr NTFY comment # for HTTP Basic authentication use this as &amp;quot;setUrl&amp;quot;:\&lt;br /&gt;
# https://[$name:username]:[$name:password]@[$name:NtfyServer]/%%path%%&lt;br /&gt;
attr NTFY replacement01Mode text&lt;br /&gt;
attr NTFY replacement01Regex %%path%%&lt;br /&gt;
attr NTFY replacement02Mode expression&lt;br /&gt;
attr NTFY replacement02Regex \[([^:]+):([^\]]+)\]&lt;br /&gt;
attr NTFY replacement02Value my $device = $name if ($1 eq &amp;quot;\$name&amp;quot;) // $1;;\&lt;br /&gt;
ReadingsVal($device, $2, undef) or AttrVal($device, $2, &amp;quot;???&amp;quot;);;&lt;br /&gt;
attr NTFY replacement03Mode expression&lt;br /&gt;
attr NTFY replacement03Regex %%title%%&lt;br /&gt;
attr NTFY replacement04Mode expression&lt;br /&gt;
attr NTFY replacement04Regex %%message%%&lt;br /&gt;
attr NTFY replacement05Mode expression&lt;br /&gt;
attr NTFY replacement05Regex %%file%%&lt;br /&gt;
attr NTFY room Global&lt;br /&gt;
attr NTFY set1Data %%message%%&lt;br /&gt;
attr NTFY set1HeaderIcon Icon: https://fhem.de/www/images/default/fhemicon.png&lt;br /&gt;
attr NTFY set1HeaderMarkdown Markdown: [$name:Markdown]&lt;br /&gt;
attr NTFY set1HeaderPrio Priority: [$name:Priority]&lt;br /&gt;
attr NTFY set1HeaderTitle Title: %%title%%&lt;br /&gt;
attr NTFY set1Method POST&lt;br /&gt;
attr NTFY set1Name message&lt;br /&gt;
attr NTFY set1Replacement01Value [$name:Topic]&lt;br /&gt;
attr NTFY set1Replacement03Value # get the value as passed to the set command:\&lt;br /&gt;
my $value = InternalVal($name, &amp;quot;value&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
# find first occurence of pattern Title=&amp;quot;.*&amp;quot; and use that as result:\&lt;br /&gt;
my ($result) = $value =~ /Title=&amp;quot;(.*?)&amp;quot;/;;\&lt;br /&gt;
\&lt;br /&gt;
# assign value from userAttr if $result is emtpy:\&lt;br /&gt;
$result //= AttrVal($name, &amp;quot;Title&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
return $result;;&lt;br /&gt;
attr NTFY set1Replacement04Value # get the value as passed to the set command:\&lt;br /&gt;
my $value = InternalVal($name, &amp;quot;value&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
# remove everything matching pattern Title=&amp;quot;.*&amp;quot;\&lt;br /&gt;
$value =~ s/Title=&amp;quot;.*?&amp;quot;//;;\&lt;br /&gt;
\&lt;br /&gt;
#replace the literal character sequence\&lt;br /&gt;
# \n with a real linefeed\&lt;br /&gt;
$value =~ s/\\n/\n/g;;\&lt;br /&gt;
\&lt;br /&gt;
return $value;;&lt;br /&gt;
attr NTFY set1TextArg 1&lt;br /&gt;
attr NTFY set2Data %%file%%&lt;br /&gt;
attr NTFY set2HeaderFilename Filename: [$name:Filename]&lt;br /&gt;
attr NTFY set2HeaderPrio Priority: [$name:Priority]&lt;br /&gt;
attr NTFY set2HeaderTitle Title: [$name:Title]&lt;br /&gt;
attr NTFY set2Method PUT&lt;br /&gt;
attr NTFY set2Name attach&lt;br /&gt;
attr NTFY set2Replacement01Value [$name:Topic]&lt;br /&gt;
attr NTFY set2Replacement05Value my $value = InternalVal($name, &amp;quot;value&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
open(my $fh, &#039;&amp;lt;&#039;, $value) or return &amp;quot;ERROR: Cannot open file&amp;quot;;;\&lt;br /&gt;
binmode($fh);; \&lt;br /&gt;
my $result = do { local $/;; &amp;lt;$fh&amp;gt; };; \&lt;br /&gt;
close($fh);; \&lt;br /&gt;
\&lt;br /&gt;
return $result;;&lt;br /&gt;
attr NTFY set2TextArg 1&lt;br /&gt;
attr NTFY setURL https://[$name:NtfyServer]/%%path%%&lt;br /&gt;
attr NTFY widgetOverride Priority:select,max,high,default,low,min Markdown:select,true,false&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Senden mit HTTPMOD====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set NTFY message Title=&amp;quot;mein Titel aus dem Set heraus&amp;quot; Meine Nachricht&lt;br /&gt;
set NTFY message Title=&amp;quot;&amp;quot; Meine Nachricht ohne Titel&lt;br /&gt;
set NTFY message Title=&amp;quot;✅ Emojis 🚀&amp;quot; 📝 Meine Nachricht mit Emojis 👾&lt;br /&gt;
set NTFY attach /opt/fhem/www/images/default/fhemicon.png&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man Dateien versendet, wird der Dateiname für den Empfänger per Reading oder Attribut definiert. Das Reading hat Vorrang:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
setreading NTFY Filename meinDateiName.png&lt;br /&gt;
attr NTFY Filename meinDateiName.png&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Empfangen mit Websocket und Dummydevice====&lt;br /&gt;
Alternativ zum NTFY_CLIENT Modul kann man auch mit folgenden Snippet NTFY Nachrichten empfangen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod NTFY_RECEIVE dummy&lt;br /&gt;
attr NTFY_RECEIVE userattr URL last_seen_max_age password username&lt;br /&gt;
attr NTFY_RECEIVE URL wss:ntfy.sh:443/FreundlichenGruesseAnAlleFHEMNutzer/ws&lt;br /&gt;
attr NTFY_RECEIVE alias NTFY_RECEIVE&lt;br /&gt;
attr NTFY_RECEIVE devStateIcon opened:general_ok@green:stop disconnected:rc_STOP@red:start&lt;br /&gt;
attr NTFY_RECEIVE eventMap /cmd connect:start/cmd disconnect:stop/&lt;br /&gt;
attr NTFY_RECEIVE group Experimente&lt;br /&gt;
attr NTFY_RECEIVE icon hue_filled_plug&lt;br /&gt;
&lt;br /&gt;
attr NTFY_RECEIVE password superGeheimesPasswort&lt;br /&gt;
attr NTFY_RECEIVE readingList cmd&lt;br /&gt;
&lt;br /&gt;
attr NTFY_RECEIVE setList cmd&lt;br /&gt;
attr NTFY_RECEIVE userReadings connect:cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;URL&amp;quot;, &amp;quot;wss:ntfy.sh:443/FreundlichenGruesseAnAlleFHEMNutzer/ws&amp;quot;);;\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} =~ m,^(ws:|wss:)?([^/:]+):([0-9]+)(.*?)$,;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = $2;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;User-Agent&#039;} = &#039;FHEM&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    my $user = AttrVal($name, &amp;quot;username&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $pwd  = AttrVal($name, &amp;quot;password&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    if ($user ne &amp;quot;???&amp;quot; &amp;amp;&amp;amp; $pwd ne &amp;quot;???&amp;quot;) {\&lt;br /&gt;
        my $encoded_auth = MIME::Base64::encode_base64(&amp;quot;$user:$pwd&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
        $hash-&amp;gt;{header}{&#039;Authorization&#039;} = &amp;quot;Basic $encoded_auth&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        readingsBeginUpdate($hash);;\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # track activity, emtpy buffer normally is from ping/pongs\&lt;br /&gt;
        readingsBulkUpdate($hash, &amp;quot;last_seen&amp;quot;, int(time()*1000));;\&lt;br /&gt;
        RemoveInternalTimer($name.&#039;Timeout&#039;);;\&lt;br /&gt;
        my $timeoutFunction = sub() {\&lt;br /&gt;
            my ($arg) = @_;;\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            my $myCmd = ReadingsVal($name, &amp;quot;cmd&amp;quot;, &amp;quot;disconnect&amp;quot;);;\&lt;br /&gt;
            return if ($myCmd =~ /disconnect|stop/);;\&lt;br /&gt;
            \&lt;br /&gt;
            Log3($name, 3, &amp;quot;$name: Timeout occured, restarting websocket...&amp;quot;);;\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;state&amp;quot;, &amp;quot;disconnected&amp;quot;);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
        };;\&lt;br /&gt;
        InternalTimer(gettimeofday() + 120, $timeoutFunction, $name.&#039;Timeout&#039;);;\&lt;br /&gt;
        \&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            #readingsBulkUpdate($hash, &amp;quot;last_seen&amp;quot;, 0);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and looks like it contains a message\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
            $buf =~ /^{.*&amp;quot;event&amp;quot;:&amp;quot;message&amp;quot;.*}$/) { ## check if buffer looks like JSON with msg\&lt;br /&gt;
            \&lt;br /&gt;
            # delete all our readings that begin with &amp;quot;ntfy_&amp;quot;\&lt;br /&gt;
            foreach my $reading (grep { $_ =~ /^ntfy_.*/ } keys %{$hash-&amp;gt;{READINGS}}) {\&lt;br /&gt;
                readingsDelete($hash, $reading);;\&lt;br /&gt;
            }\&lt;br /&gt;
            \&lt;br /&gt;
            # parse as JSON, do not trust the input fully, thus sanitize buffer\&lt;br /&gt;
            my %res = %{json2nameValue($buf)};; #(https://wiki.fhem.de/wiki/MQTT2_DEVICE_-_Schritt_f%C3%BCr_Schritt#json2nameValue.28.29)\&lt;br /&gt;
            foreach my $k (sort keys %res) {\&lt;br /&gt;
                # only keep ASCII and a German Characters like Umlaute, sharp-S...\&lt;br /&gt;
                my $sanitizedValue = $res{$k} =~ s/[^[:ascii:]äöüÖÄÜß]/_/rg;; # &#039;r&#039; flag prevents modifying the input string\&lt;br /&gt;
                readingsBulkUpdate($hash, &amp;quot;ntfy_&amp;quot;.makeReadingName($k), $sanitizedValue);;\&lt;br /&gt;
            }\&lt;br /&gt;
        }\&lt;br /&gt;
        #readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($buf ne &amp;quot;&amp;quot;);;\&lt;br /&gt;
        Log3($name, 3, &amp;quot;$name: Rx: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;) if ($buf ne &amp;quot;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        readingsEndUpdate($hash, 1);;\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    DevIo_OpenDev($hash,\&lt;br /&gt;
        0,      ## reopen flag\&lt;br /&gt;
        undef,  ## initFn, on success\&lt;br /&gt;
        sub() { ## callbackFn, on verdict, req. to make it a non-blocking call\&lt;br /&gt;
            my ($hash, $error) = @_;;\&lt;br /&gt;
            if ($error) {\&lt;br /&gt;
                Log(3, &amp;quot;$name: DevIo_OpenDev Callback: connection failed: $error&amp;quot;);;\&lt;br /&gt;
                \&lt;br /&gt;
                my $timerFunction = sub() {\&lt;br /&gt;
                    my ($arg) = @_;;\&lt;br /&gt;
                    my $hash = $defs{$name};;\&lt;br /&gt;
                    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
                    readingsSingleUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
                };;\&lt;br /&gt;
                \&lt;br /&gt;
                RemoveInternalTimer($name.&#039;Timer&#039;);;\&lt;br /&gt;
                my $rwait = int(rand(20)) + 10;;\&lt;br /&gt;
                InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.&#039;Timer&#039;);;\&lt;br /&gt;
                readingsSingleUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;, 1);;\&lt;br /&gt;
            }\&lt;br /&gt;
        }\&lt;br /&gt;
    );;\&lt;br /&gt;
    \&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;state&amp;quot;, &amp;quot;connecting...&amp;quot;);;\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
disconnect:cmd:.(disconnect|reconnect) {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $myCmd = ReadingsVal($name, &amp;quot;cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    RemoveInternalTimer($name.&#039;Timer&#039;);;\&lt;br /&gt;
    RemoveInternalTimer($name.&#039;Timeout&#039;);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;state&amp;quot;, &amp;quot;disconnected&amp;quot;) if (!defined(DevIo_IsOpen($hash)));;\&lt;br /&gt;
    \&lt;br /&gt;
    if ($myCmd =~ /reconnect/) {\&lt;br /&gt;
        my $timerFunction = sub() {\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            readingsSingleUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
        };;\&lt;br /&gt;
    \&lt;br /&gt;
        RemoveInternalTimer(&amp;quot;${name}_${reading}_timer&amp;quot;);;\&lt;br /&gt;
        InternalTimer(gettimeofday()+1, $timerFunction, &amp;quot;${name}_${reading}_timer&amp;quot;);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
        RemoveInternalTimer(&amp;quot;${name}_watchdog_timer&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
onDisconnect { ## check on each update if the connection is unintentionally broken...\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myCmd = ReadingsVal($name, &amp;quot;cmd&amp;quot;, &amp;quot;disconnect&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    return if ($myCmd =~ /disconnect|stop/);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $timerFunction = sub() {\&lt;br /&gt;
        my ($arg) = @_;;\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
        readingsSingleUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    RemoveInternalTimer($name.&#039;Timer&#039;);;\&lt;br /&gt;
    my $rwait = int(rand(20)) + 10;;\&lt;br /&gt;
    InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.&#039;Timer&#039;);;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
watchdog:last_seen:.* {\&lt;br /&gt;
    my $ls = ReadingsVal($name, &amp;quot;last_seen&amp;quot;, 0);;\&lt;br /&gt;
        \&lt;br /&gt;
    my $timerFunction = sub() {\&lt;br /&gt;
        ##fhem(&amp;quot;set FHEMMeldung.ntfy message $name $reading wurde ausgelöst (last_seen: $ls)&amp;quot;);;\&lt;br /&gt;
        readingsSingleUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;reconnect&amp;quot;, 1);;\&lt;br /&gt;
        readingsSingleUpdate($hash, &amp;quot;last_seen&amp;quot;, 0, 1);;\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    RemoveInternalTimer(&amp;quot;${name}_${reading}_timer&amp;quot;);;\&lt;br /&gt;
    InternalTimer(gettimeofday()+240, $timerFunction, &amp;quot;${name}_${reading}_timer&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;, localtime(time()));;\&lt;br /&gt;
}&lt;br /&gt;
attr NTFY_RECEIVE username Torxgewinde&lt;br /&gt;
attr NTFY_RECEIVE verbose 1&lt;br /&gt;
attr NTFY_RECEIVE webCmd start:stop&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Weitere Links===&lt;br /&gt;
Forumthread: {{Link2Forum|Topic=137036|LinkText=NTFY.sh: Push Nachrichten an iOS, Android, PC, Command-Line, E-Mail &amp;amp; Telefon}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:Messenger]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Ntfy&amp;diff=40749</id>
		<title>Ntfy</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Ntfy&amp;diff=40749"/>
		<updated>2026-01-22T17:14:03Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Externes Repo nicht mehr erreichbar&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==📬 NTFY.sh: Push Nachrichten an iOS, Android, PC, Command-Line, E-Mail &amp;amp; Telefon==&lt;br /&gt;
[[Datei:NTFY Screenshot mit einem Bild.png|alternativtext=NTFY Screenshot mit einem Bild|mini|300x300px|NTFY Screenshot mit einem Bild]]&lt;br /&gt;
[https://NTFY.sh NTFY.sh] bietet Push-Dienste auf iOS, Android, Console, E-Mail, ja sogar als Anruf auf ein Telefon an. Es ist damit ähnlich zu dem, was man mit PushOver, PushBullet oder auch Gotify anstellen kann, wobei NTFY auch Kriterien wie &amp;quot;Self-hosted&amp;quot; und echte &amp;quot;iOS-Pushnachrichten&amp;quot; erfüllt. Man kann damit von FHEM aus alle diese Kanäle mit einer Push-Nachricht erreichen. Die Nutzung ist extrem einfach, man setzt einen HTTP-Aufruf ab, um eine Push-Nachricht zu versenden. NTFY.sh betreibt sogar einen Gratis-Server, den man nutzen darf. Man kann den Server auch selber hosten, wenn man will.&lt;br /&gt;
&lt;br /&gt;
Öffnet testweise diesen Link, dort tauchen die Nachrichten auch in der PWA (Webapp) auf: https://ntfy.sh/FreundlichenGruesseAnAlleFHEMNutzer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;color:#ffff00; background:#ff0000; border: solid 1px black&amp;quot;&amp;gt;&#039;&#039;&#039;Wichtig:&#039;&#039;&#039; Nutzt man eine öffentliche Instanz ist das Topic so zu wählen, dass es wie ein Passwort funktioniert und schwer zu erraten ist. Wählt man ein einfaches Topic, können andere mitlesen und auch Nachrichten senden. Dies kann zu ungewollten Effekten führen!&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== NTFY mit anderen Systemen nutzen/verbinden ===&lt;br /&gt;
Auch ganz ohne FHEM, also aus anderen Systemen, kann man an NTFY Nachrichten senden und empfangen. So kann man sich aus iOS zum Beispiel per Webhook etwas an NTFY senden, oder auch von IP-Kameras Infos an den NTFY Server senden und dann von den diversen Clients empfangen und verarbeiten.&lt;br /&gt;
&lt;br /&gt;
==== Apps (iOS, Android und PWA) ====&lt;br /&gt;
Ganz wichtig für die meisten Nutzer ist natürlich der Empfang von NTFY Nachrichten mit:&lt;br /&gt;
&lt;br /&gt;
* der Android-App ([https://play.google.com/store/apps/details?id=io.heckel.ntfy&amp;amp;pli=1 Playstore], [https://f-droid.org/en/packages/io.heckel.ntfy/ F-Droid]),&lt;br /&gt;
* der [https://apps.apple.com/us/app/ntfy/id1625396347 iOS-App] *, oder&lt;br /&gt;
* der [https://ntfy.sh/app PWA] (Progressive-Web-App)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis zur nativen iOS-App:&#039;&#039;&#039; [https://github.com/binwiederhier/ntfy/issues/1226 Zur Zeit] funktioniert die Anzeige von Anhängen unter iOS, iPadOS, MacOS &#039;&#039;&#039;nur&#039;&#039;&#039; über die Web-App (zu finden unter https://ntfy.sh/app). Um die PWA unter iOS/MacOS fast wie eine gewöhnliche App zu nutzen, kann man die Web-Seite an den &amp;quot;Home-Screen&amp;quot; (iOS) senden oder &amp;quot;Zum Dock hinzufügen&amp;quot; (MacOS). Anschließend noch &amp;quot;Benachrichtigungen erlauben&amp;quot; aktivieren.&lt;br /&gt;
&lt;br /&gt;
==== CLI, Commandline, aus Skripten heraus Nachrichten senden ====&lt;br /&gt;
Zum Beispiel kann man mit einem CURL-Befehl etwas senden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
curl -d &amp;quot;Your message content here&amp;quot; \&lt;br /&gt;
    -H &amp;quot;Title: Bla&amp;quot; \&lt;br /&gt;
    https://ntfy.sh/FreundlichenGruesseAnAlleFHEMNutzer&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
oder auch mit WGET:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
wget --method=POST \&lt;br /&gt;
    --body-data=&amp;quot;Your message content here&amp;quot; \&lt;br /&gt;
    --header=&amp;quot;Title: Bla&amp;quot; \&lt;br /&gt;
    https://ntfy.sh/FreundlichenGruesseAnAlleFHEMNutzer -q -O -&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Per Webhook Nachrichten senden ====&lt;br /&gt;
Via HTTP-GET als [https://ntfy.sh/FreundlichenGruesseAnAlleFHEMNutzer/publish?message=Your%20message%20content%20here&amp;amp;title=Bla Webhook] kann man ebenfalls Nachrichten via NTFY senden. Diese Option eignet sich besonders für weitere Geräte wie Shelly, Alexa oder auch Apple-Shortcuts und viele weitere Geräte dieser Art:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
https://ntfy.sh/FreundlichenGruesseAnAlleFHEMNutzer/publish?message=Your%20message%20content%20here&amp;amp;title=Bla&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Per E-Mail Nachrichten senden ====&lt;br /&gt;
Der NTFY Server kann auch per E-Mail erreicht werden, sofern dies so eingestellt wurde. Dies ist sehr praktisch, wenn ein System wie zum Beispiel eine IP-Kamera E-Mails bei Bewegung versenden kann, oder wenn ein CI-Build E-Mails verschickt. Die Doku dazu ist unter: https://docs.ntfy.sh/config/?h=mail#e-mail-publishing&lt;br /&gt;
&lt;br /&gt;
Alternativ, kann man ein E-Mail Postfach überwachen und bei neuen E-Mails diese in eine NTFY Nachricht umwandeln und sich dann an die NTFY-Apps senden: [https://forum.fhem.de/index.php?topic=139868.0 📧 Fetchmail, Email von IP-Cam ohne Verzögerung an NTFY/FHEM senden]&lt;br /&gt;
&lt;br /&gt;
===Anbindung in FHEM per Modul/Device &amp;quot;NTFY_CLIENT&amp;quot;===&lt;br /&gt;
Hinweis: Das extern gehostete Repository ist zur Zeit offline, deswegen ist der folgende Abschnitt vorerst noch sichtbar und sollte ca. 03.2026 entfernt werden, falls das Repo weiterhin nicht erreichbar ist :-(&lt;br /&gt;
&lt;br /&gt;
&amp;lt;s&amp;gt;Es gibt ein FHEM-Device, mit dem man NTFY nutzen kann: [https://rm.byterazor.de/projects/fhem-ntfy fhem-ntfy]&amp;lt;/s&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====&#039;&#039;&#039;&amp;lt;s&amp;gt;Schritte um NTFY_CLIENT einzurichten:&amp;lt;/s&amp;gt;&#039;&#039;&#039;====&lt;br /&gt;
&amp;lt;s&amp;gt;Den Quelltext für das NTFY_CLIENT-Device kann man von Extern wie folgt in FHEM laden:&amp;lt;/s&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
update add https://rm.byterazor.de/upd-fhem-ntfy/controls_byterazor-fhem-ntfy.txt&lt;br /&gt;
update&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;s&amp;gt;Nachdem man den Quelltext in FHEM hat, sollte FHEM einmal neugestartet werden:&amp;lt;/s&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
shutdown restart&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;s&amp;gt;Danach kann man ein NTFY_CLIENT-Device anlegen:&amp;lt;/s&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod NTFY0 NTFY_CLIENT https://ntfy.sh&lt;br /&gt;
attr NTFY0 defaultPriority default&lt;br /&gt;
attr NTFY0 defaultTopic FreundlichenGruesseAnAlleFHEMNutzer&lt;br /&gt;
attr NTFY0 room Experimente&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;s&amp;gt;Senden von NTFY Nachrichten aus FHEM heraus:&amp;lt;/s&amp;gt; ====&lt;br /&gt;
&amp;lt;s&amp;gt;Senden kann man Nachrichten mit:&amp;lt;/s&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set NTFY0 publish @FreundlichenGruesseAnAlleFHEMNutzer Testnachricht!&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;s&amp;gt;Die genaue Nutzung sind in dem Hilfetext zum Device zu finden. Das &amp;quot;@&amp;quot; setzt zum Beispiel das Topic für NTFY.&amp;lt;/s&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== &amp;lt;s&amp;gt;Empfangen und reagieren auf Nachrichten&amp;lt;/s&amp;gt; ====&lt;br /&gt;
&amp;lt;s&amp;gt;Im Log wird es Meldungen in der folgenden Art geben wenn Nachrichten empfangen werden:&amp;lt;/s&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2024-10-29 19:01:16.714 NTFY_TOPIC NTFY0_FreundlichenGruesseAnAlleFHEMNutzer nrReceivedMessages: 7&lt;br /&gt;
2024-10-29 19:01:16.715 NTFY_CLIENT NTFY0 subscriptions: FreundlichenGruesseAnAlleFHEMNutzer&lt;br /&gt;
2024-10-29 19:01:16.715 NTFY_CLIENT NTFY0 nrReceivedMessages: 7&lt;br /&gt;
2024-10-29 19:01:16.715 NTFY_CLIENT NTFY0 lastReceivedTitle: Bla&lt;br /&gt;
2024-10-29 19:01:16.715 NTFY_CLIENT NTFY0 lastReceivedData: Your message content here&lt;br /&gt;
2024-10-29 19:01:16.715 NTFY_CLIENT NTFY0 lastReceivedRawMessage: {&amp;quot;id&amp;quot;:&amp;quot;oU5iqrdSFoeY&amp;quot;,&amp;quot;time&amp;quot;:1730224876,&amp;quot;expires&amp;quot;:1730268076,&amp;quot;event&amp;quot;:&amp;quot;message&amp;quot;,&amp;quot;topic&amp;quot;:&amp;quot;FreundlichenGruesseAnAlleFHEMNutzer&amp;quot;,&amp;quot;title&amp;quot;:&amp;quot;Bla&amp;quot;,&amp;quot;message&amp;quot;:&amp;quot;Your message content here&amp;quot;}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;s&amp;gt;Hier ein Beispiel mit einem notify-Device, dass auf die Nachrichten &amp;quot;Garage auf&amp;quot; oder &amp;quot;Garage zu&amp;quot; reagieren soll:&amp;lt;/s&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod GarageNotify notify NTFY0:lastReceivedData:.* {\&lt;br /&gt;
	Log(1, &amp;quot;$NAME: $EVTPART1, $EVTPART2, $EVENT&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	if (&amp;quot;$EVTPART1 $EVTPART2&amp;quot; eq &amp;quot;Garage zu&amp;quot;) {\&lt;br /&gt;
		Log(1, &amp;quot;Garage soll zu gemacht werden&amp;quot;);;\&lt;br /&gt;
		fhem(&amp;quot;set NTFY0 publish Ich werde die Garage nun schliessen&amp;quot;);;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	if (&amp;quot;$EVTPART1 $EVTPART2&amp;quot; eq &amp;quot;Garage auf&amp;quot;) {\&lt;br /&gt;
		Log(1, &amp;quot;Garage soll auf gemacht werden&amp;quot;);;\&lt;br /&gt;
		fhem(&amp;quot;set NTFY0 publish Ich werde die Garage nun oeffnen&amp;quot;);;\&lt;br /&gt;
	}\&lt;br /&gt;
}&lt;br /&gt;
attr GarageNotify room Experimente&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;s&amp;gt;Die zugehörigen Webhooks für obiges Beispiel-notify-Device wären dann:&amp;lt;/s&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;s&amp;gt;https://ntfy.sh/FreundlichenGruesseAnAlleFHEMNutzer/publish?message=Garage%20auf&amp;lt;/s&amp;gt;&lt;br /&gt;
* &amp;lt;s&amp;gt;https://ntfy.sh/FreundlichenGruesseAnAlleFHEMNutzer/publish?message=Garage%20zu&amp;lt;/s&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;s&amp;gt;Testen kann man direkt gegen die bekannteste und vom NTFY-Autor geschaffene NTFY-Installation: https://ntfy.sh/FreundlichenGruesseAnAlleFHEMNutzer&amp;lt;/s&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Anbindung per HTTPMOD und Websocket===&lt;br /&gt;
Neben der Option per speziellem NTFY Device kann man auch das HTTPMOD-Device nutzen um Nachrichten oder Attachments/Dateien zu senden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod NTFY HTTPMOD none 0&lt;br /&gt;
attr NTFY userattr Filename Markdown NtfyServer Priority Title Topic password username&lt;br /&gt;
attr NTFY Filename test.png&lt;br /&gt;
attr NTFY Markdown true&lt;br /&gt;
attr NTFY NtfyServer ntfy.sh&lt;br /&gt;
attr NTFY Priority high&lt;br /&gt;
attr NTFY Title Titel aus UserAttr heraus&lt;br /&gt;
attr NTFY Topic FreundlichenGruesseAnAlleFHEMNutzer&lt;br /&gt;
attr NTFY comment # for HTTP Basic authentication use this as &amp;quot;setUrl&amp;quot;:\&lt;br /&gt;
# https://[$name:username]:[$name:password]@[$name:NtfyServer]/%%path%%&lt;br /&gt;
attr NTFY replacement01Mode text&lt;br /&gt;
attr NTFY replacement01Regex %%path%%&lt;br /&gt;
attr NTFY replacement02Mode expression&lt;br /&gt;
attr NTFY replacement02Regex \[([^:]+):([^\]]+)\]&lt;br /&gt;
attr NTFY replacement02Value my $device = $name if ($1 eq &amp;quot;\$name&amp;quot;) // $1;;\&lt;br /&gt;
ReadingsVal($device, $2, undef) or AttrVal($device, $2, &amp;quot;???&amp;quot;);;&lt;br /&gt;
attr NTFY replacement03Mode expression&lt;br /&gt;
attr NTFY replacement03Regex %%title%%&lt;br /&gt;
attr NTFY replacement04Mode expression&lt;br /&gt;
attr NTFY replacement04Regex %%message%%&lt;br /&gt;
attr NTFY replacement05Mode expression&lt;br /&gt;
attr NTFY replacement05Regex %%file%%&lt;br /&gt;
attr NTFY room Global&lt;br /&gt;
attr NTFY set1Data %%message%%&lt;br /&gt;
attr NTFY set1HeaderIcon Icon: https://fhem.de/www/images/default/fhemicon.png&lt;br /&gt;
attr NTFY set1HeaderMarkdown Markdown: [$name:Markdown]&lt;br /&gt;
attr NTFY set1HeaderPrio Priority: [$name:Priority]&lt;br /&gt;
attr NTFY set1HeaderTitle Title: %%title%%&lt;br /&gt;
attr NTFY set1Method POST&lt;br /&gt;
attr NTFY set1Name message&lt;br /&gt;
attr NTFY set1Replacement01Value [$name:Topic]&lt;br /&gt;
attr NTFY set1Replacement03Value # get the value as passed to the set command:\&lt;br /&gt;
my $value = InternalVal($name, &amp;quot;value&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
# find first occurence of pattern Title=&amp;quot;.*&amp;quot; and use that as result:\&lt;br /&gt;
my ($result) = $value =~ /Title=&amp;quot;(.*?)&amp;quot;/;;\&lt;br /&gt;
\&lt;br /&gt;
# assign value from userAttr if $result is emtpy:\&lt;br /&gt;
$result //= AttrVal($name, &amp;quot;Title&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
return $result;;&lt;br /&gt;
attr NTFY set1Replacement04Value # get the value as passed to the set command:\&lt;br /&gt;
my $value = InternalVal($name, &amp;quot;value&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
# remove everything matching pattern Title=&amp;quot;.*&amp;quot;\&lt;br /&gt;
$value =~ s/Title=&amp;quot;.*?&amp;quot;//;;\&lt;br /&gt;
\&lt;br /&gt;
#replace the literal character sequence\&lt;br /&gt;
# \n with a real linefeed\&lt;br /&gt;
$value =~ s/\\n/\n/g;;\&lt;br /&gt;
\&lt;br /&gt;
return $value;;&lt;br /&gt;
attr NTFY set1TextArg 1&lt;br /&gt;
attr NTFY set2Data %%file%%&lt;br /&gt;
attr NTFY set2HeaderFilename Filename: [$name:Filename]&lt;br /&gt;
attr NTFY set2HeaderPrio Priority: [$name:Priority]&lt;br /&gt;
attr NTFY set2HeaderTitle Title: [$name:Title]&lt;br /&gt;
attr NTFY set2Method PUT&lt;br /&gt;
attr NTFY set2Name attach&lt;br /&gt;
attr NTFY set2Replacement01Value [$name:Topic]&lt;br /&gt;
attr NTFY set2Replacement05Value my $value = InternalVal($name, &amp;quot;value&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
\&lt;br /&gt;
open(my $fh, &#039;&amp;lt;&#039;, $value) or return &amp;quot;ERROR: Cannot open file&amp;quot;;;\&lt;br /&gt;
binmode($fh);; \&lt;br /&gt;
my $result = do { local $/;; &amp;lt;$fh&amp;gt; };; \&lt;br /&gt;
close($fh);; \&lt;br /&gt;
\&lt;br /&gt;
return $result;;&lt;br /&gt;
attr NTFY set2TextArg 1&lt;br /&gt;
attr NTFY setURL https://[$name:NtfyServer]/%%path%%&lt;br /&gt;
attr NTFY widgetOverride Priority:select,max,high,default,low,min Markdown:select,true,false&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Senden mit HTTPMOD====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
set NTFY message Title=&amp;quot;mein Titel aus dem Set heraus&amp;quot; Meine Nachricht&lt;br /&gt;
set NTFY message Title=&amp;quot;&amp;quot; Meine Nachricht ohne Titel&lt;br /&gt;
set NTFY message Title=&amp;quot;✅ Emojis 🚀&amp;quot; 📝 Meine Nachricht mit Emojis 👾&lt;br /&gt;
set NTFY attach /opt/fhem/www/images/default/fhemicon.png&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wenn man Dateien versendet, wird der Dateiname für den Empfänger per Reading oder Attribut definiert. Das Reading hat Vorrang:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
setreading NTFY Filename meinDateiName.png&lt;br /&gt;
attr NTFY Filename meinDateiName.png&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Empfangen mit Websocket und Dummydevice====&lt;br /&gt;
Alternativ zum NTFY_CLIENT Modul kann man auch mit folgenden Snippet NTFY Nachrichten empfangen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
defmod NTFY_RECEIVE dummy&lt;br /&gt;
attr NTFY_RECEIVE userattr URL last_seen_max_age password username&lt;br /&gt;
attr NTFY_RECEIVE URL wss:ntfy.sh:443/FreundlichenGruesseAnAlleFHEMNutzer/ws&lt;br /&gt;
attr NTFY_RECEIVE alias NTFY_RECEIVE&lt;br /&gt;
attr NTFY_RECEIVE devStateIcon opened:general_ok@green:stop disconnected:rc_STOP@red:start&lt;br /&gt;
attr NTFY_RECEIVE eventMap /cmd connect:start/cmd disconnect:stop/&lt;br /&gt;
attr NTFY_RECEIVE group Experimente&lt;br /&gt;
attr NTFY_RECEIVE icon hue_filled_plug&lt;br /&gt;
&lt;br /&gt;
attr NTFY_RECEIVE password superGeheimesPasswort&lt;br /&gt;
attr NTFY_RECEIVE readingList cmd&lt;br /&gt;
&lt;br /&gt;
attr NTFY_RECEIVE setList cmd&lt;br /&gt;
attr NTFY_RECEIVE userReadings connect:cmd:.connect {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
    return &amp;quot;Device already open&amp;quot; if (defined($devState));;\&lt;br /&gt;
    \&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} = AttrVal($name, &amp;quot;URL&amp;quot;, &amp;quot;wss:ntfy.sh:443/FreundlichenGruesseAnAlleFHEMNutzer/ws&amp;quot;);;\&lt;br /&gt;
    $hash-&amp;gt;{DeviceName} =~ m,^(ws:|wss:)?([^/:]+):([0-9]+)(.*?)$,;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;Host&#039;} = $2;;\&lt;br /&gt;
    $hash-&amp;gt;{header}{&#039;User-Agent&#039;} = &#039;FHEM&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    my $user = AttrVal($name, &amp;quot;username&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $pwd  = AttrVal($name, &amp;quot;password&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    if ($user ne &amp;quot;???&amp;quot; &amp;amp;&amp;amp; $pwd ne &amp;quot;???&amp;quot;) {\&lt;br /&gt;
        my $encoded_auth = MIME::Base64::encode_base64(&amp;quot;$user:$pwd&amp;quot;, &amp;quot;&amp;quot;);;\&lt;br /&gt;
        $hash-&amp;gt;{header}{&#039;Authorization&#039;} = &amp;quot;Basic $encoded_auth&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    $hash-&amp;gt;{directReadFn} = sub () {\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        readingsBeginUpdate($hash);;\&lt;br /&gt;
        my $buf = DevIo_SimpleRead($hash);;\&lt;br /&gt;
        \&lt;br /&gt;
        # track activity, emtpy buffer normally is from ping/pongs\&lt;br /&gt;
        readingsBulkUpdate($hash, &amp;quot;last_seen&amp;quot;, int(time()*1000));;\&lt;br /&gt;
        RemoveInternalTimer($name.&#039;Timeout&#039;);;\&lt;br /&gt;
        my $timeoutFunction = sub() {\&lt;br /&gt;
            my ($arg) = @_;;\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            my $myCmd = ReadingsVal($name, &amp;quot;cmd&amp;quot;, &amp;quot;disconnect&amp;quot;);;\&lt;br /&gt;
            return if ($myCmd =~ /disconnect|stop/);;\&lt;br /&gt;
            \&lt;br /&gt;
            Log3($name, 3, &amp;quot;$name: Timeout occured, restarting websocket...&amp;quot;);;\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            readingsBeginUpdate($hash);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;state&amp;quot;, &amp;quot;disconnected&amp;quot;);;\&lt;br /&gt;
            readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
            readingsEndUpdate($hash, 1);;\&lt;br /&gt;
        };;\&lt;br /&gt;
        InternalTimer(gettimeofday() + 120, $timeoutFunction, $name.&#039;Timeout&#039;);;\&lt;br /&gt;
        \&lt;br /&gt;
        if(!defined($buf)) {\&lt;br /&gt;
            DevIo_CloseDev($hash);;\&lt;br /&gt;
            #readingsBulkUpdate($hash, &amp;quot;last_seen&amp;quot;, 0);;\&lt;br /&gt;
            $buf = &amp;quot;not_connected&amp;quot;;;\&lt;br /&gt;
        }\&lt;br /&gt;
        \&lt;br /&gt;
        # only update our reading if buffer is not empty and looks like it contains a message\&lt;br /&gt;
        if ($buf ne &amp;quot;&amp;quot; &amp;amp;&amp;amp; \&lt;br /&gt;
            $buf =~ /^{.*&amp;quot;event&amp;quot;:&amp;quot;message&amp;quot;.*}$/) { ## check if buffer looks like JSON with msg\&lt;br /&gt;
            \&lt;br /&gt;
            # delete all our readings that begin with &amp;quot;ntfy_&amp;quot;\&lt;br /&gt;
            foreach my $reading (grep { $_ =~ /^ntfy_.*/ } keys %{$hash-&amp;gt;{READINGS}}) {\&lt;br /&gt;
                readingsDelete($hash, $reading);;\&lt;br /&gt;
            }\&lt;br /&gt;
            \&lt;br /&gt;
            # parse as JSON, do not trust the input fully, thus sanitize buffer\&lt;br /&gt;
            my %res = %{json2nameValue($buf)};; #(https://wiki.fhem.de/wiki/MQTT2_DEVICE_-_Schritt_f%C3%BCr_Schritt#json2nameValue.28.29)\&lt;br /&gt;
            foreach my $k (sort keys %res) {\&lt;br /&gt;
                # only keep ASCII and a German Characters like Umlaute, sharp-S...\&lt;br /&gt;
                my $sanitizedValue = $res{$k} =~ s/[^[:ascii:]äöüÖÄÜß]/_/rg;; # &#039;r&#039; flag prevents modifying the input string\&lt;br /&gt;
                readingsBulkUpdate($hash, &amp;quot;ntfy_&amp;quot;.makeReadingName($k), $sanitizedValue);;\&lt;br /&gt;
            }\&lt;br /&gt;
        }\&lt;br /&gt;
        #readingsBulkUpdate($hash, &amp;quot;websocketData&amp;quot;, &amp;quot;$buf&amp;quot;) if ($buf ne &amp;quot;&amp;quot;);;\&lt;br /&gt;
        Log3($name, 3, &amp;quot;$name: Rx: &amp;gt;&amp;gt;&amp;gt;$buf&amp;lt;&amp;lt;&amp;lt;&amp;quot;) if ($buf ne &amp;quot;&amp;quot;);;\&lt;br /&gt;
        \&lt;br /&gt;
        readingsEndUpdate($hash, 1);;\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    DevIo_OpenDev($hash,\&lt;br /&gt;
        0,      ## reopen flag\&lt;br /&gt;
        undef,  ## initFn, on success\&lt;br /&gt;
        sub() { ## callbackFn, on verdict, req. to make it a non-blocking call\&lt;br /&gt;
            my ($hash, $error) = @_;;\&lt;br /&gt;
            if ($error) {\&lt;br /&gt;
                Log(3, &amp;quot;$name: DevIo_OpenDev Callback: connection failed: $error&amp;quot;);;\&lt;br /&gt;
                \&lt;br /&gt;
                my $timerFunction = sub() {\&lt;br /&gt;
                    my ($arg) = @_;;\&lt;br /&gt;
                    my $hash = $defs{$name};;\&lt;br /&gt;
                    my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
                    readingsSingleUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
                };;\&lt;br /&gt;
                \&lt;br /&gt;
                RemoveInternalTimer($name.&#039;Timer&#039;);;\&lt;br /&gt;
                my $rwait = int(rand(20)) + 10;;\&lt;br /&gt;
                InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.&#039;Timer&#039;);;\&lt;br /&gt;
                readingsSingleUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;, 1);;\&lt;br /&gt;
            }\&lt;br /&gt;
        }\&lt;br /&gt;
    );;\&lt;br /&gt;
    \&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;state&amp;quot;, &amp;quot;connecting...&amp;quot;);;\&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
disconnect:cmd:.(disconnect|reconnect) {\&lt;br /&gt;
    my $hash = $defs{$name};;\&lt;br /&gt;
    my $myCmd = ReadingsVal($name, &amp;quot;cmd&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    RemoveInternalTimer($name.&#039;Timer&#039;);;\&lt;br /&gt;
    RemoveInternalTimer($name.&#039;Timeout&#039;);;\&lt;br /&gt;
    DevIo_CloseDev($hash);;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;state&amp;quot;, &amp;quot;disconnected&amp;quot;) if (!defined(DevIo_IsOpen($hash)));;\&lt;br /&gt;
    \&lt;br /&gt;
    if ($myCmd =~ /reconnect/) {\&lt;br /&gt;
        my $timerFunction = sub() {\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            readingsSingleUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1);;\&lt;br /&gt;
        };;\&lt;br /&gt;
    \&lt;br /&gt;
        RemoveInternalTimer(&amp;quot;${name}_${reading}_timer&amp;quot;);;\&lt;br /&gt;
        InternalTimer(gettimeofday()+1, $timerFunction, &amp;quot;${name}_${reading}_timer&amp;quot;);;\&lt;br /&gt;
    } else {\&lt;br /&gt;
        RemoveInternalTimer(&amp;quot;${name}_watchdog_timer&amp;quot;);;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
onDisconnect { ## check on each update if the connection is unintentionally broken...\&lt;br /&gt;
    my $myState = ReadingsVal($name, &amp;quot;state&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myData = ReadingsVal($name, &amp;quot;websocketData&amp;quot;, &amp;quot;???&amp;quot;);;\&lt;br /&gt;
    my $myCmd = ReadingsVal($name, &amp;quot;cmd&amp;quot;, &amp;quot;disconnect&amp;quot;);;\&lt;br /&gt;
    return if ($myState ne &amp;quot;disconnected&amp;quot; and $myData ne &amp;quot;not_connected&amp;quot;);;\&lt;br /&gt;
    return if ($myCmd =~ /disconnect|stop/);;\&lt;br /&gt;
    \&lt;br /&gt;
    my $timerFunction = sub() {\&lt;br /&gt;
        my ($arg) = @_;;\&lt;br /&gt;
        my $hash = $defs{$name};;\&lt;br /&gt;
        my $devState = DevIo_IsOpen($hash);;\&lt;br /&gt;
        readingsSingleUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;connect&amp;quot;, 1) if (!defined($devState));;\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    RemoveInternalTimer($name.&#039;Timer&#039;);;\&lt;br /&gt;
    my $rwait = int(rand(20)) + 10;;\&lt;br /&gt;
    InternalTimer(gettimeofday() + $rwait, $timerFunction, $name.&#039;Timer&#039;);;\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;reconnect attempt in $rwait seconds&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;,localtime(time()));;\&lt;br /&gt;
},\&lt;br /&gt;
watchdog:last_seen:.* {\&lt;br /&gt;
    my $ls = ReadingsVal($name, &amp;quot;last_seen&amp;quot;, 0);;\&lt;br /&gt;
        \&lt;br /&gt;
    my $timerFunction = sub() {\&lt;br /&gt;
        ##fhem(&amp;quot;set FHEMMeldung.ntfy message $name $reading wurde ausgelöst (last_seen: $ls)&amp;quot;);;\&lt;br /&gt;
        readingsSingleUpdate($hash, &amp;quot;cmd&amp;quot;, &amp;quot;reconnect&amp;quot;, 1);;\&lt;br /&gt;
        readingsSingleUpdate($hash, &amp;quot;last_seen&amp;quot;, 0, 1);;\&lt;br /&gt;
    };;\&lt;br /&gt;
    \&lt;br /&gt;
    RemoveInternalTimer(&amp;quot;${name}_${reading}_timer&amp;quot;);;\&lt;br /&gt;
    InternalTimer(gettimeofday()+240, $timerFunction, &amp;quot;${name}_${reading}_timer&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return POSIX::strftime(&amp;quot;%H:%M:%S&amp;quot;, localtime(time()));;\&lt;br /&gt;
}&lt;br /&gt;
attr NTFY_RECEIVE username Torxgewinde&lt;br /&gt;
attr NTFY_RECEIVE verbose 1&lt;br /&gt;
attr NTFY_RECEIVE webCmd start:stop&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Weitere Links===&lt;br /&gt;
Forumthread: {{Link2Forum|Topic=137036|LinkText=NTFY.sh: Push Nachrichten an iOS, Android, PC, Command-Line, E-Mail &amp;amp; Telefon}}&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:Messenger]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40557</id>
		<title>E-Mail senden</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40557"/>
		<updated>2025-12-21T18:40:06Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Code musste noch angepasst werden damit man From mit Klarnamen setzen kann&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Um aus FHEM heraus E-Mails senden zu können, gibt es unterschiedliche Methoden, abhängig von der verwendeten Plattform.&lt;br /&gt;
&lt;br /&gt;
Lange Zeit war &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; sehr beliebt. Da aber die Homepage des Projekts nicht mehr online ist und Probleme nur noch via erhöhtem Supportaufwand gelöst werden, sollte diese Option als veraltet betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Eine Liste mit ressourcensparenden E-Mails Clients findet man bei dem OpenWRT Projekt: https://openwrt.org/docs/guide-user/services/email/smtp.client&lt;br /&gt;
&lt;br /&gt;
MSGMail ist ein FHEM eigenes Modul zum Versenden, die Einrichtung ist allerdings nicht gänzlich einfach. Anfängern sei die cURL Methode empfohlen.&lt;br /&gt;
&lt;br /&gt;
=== MSGMail (Linux, Windows) ===&lt;br /&gt;
FHEM bringt ein eigenes Device zum Senden von E-Mails mit, welches in Perl implementiert ist: [https://fhem.de/commandref.html#MSGMail MSGMail, https://fhem.de/commandref.html#MSGMail]&lt;br /&gt;
&lt;br /&gt;
=== cURL (Linux, Windows) ===&lt;br /&gt;
[https://curl.se/ cURL] kann auf praktisch [https://curl.se/download.html jedem Betriebssystem] genutzt werden, [https://curl.se/windows/ selbst unter Windows]. Es kann direkt mit einem SMTP Server E-Mails versenden und stellt damit eine moderne, portable und sichere Option zum Versenden von E-Mails dar.&lt;br /&gt;
&lt;br /&gt;
In FHEM richtet man sich ein Device ein und passt die Details (&amp;lt;code&amp;gt;$emailTo, $emailFrom, $emailServer&amp;lt;/code&amp;gt;) an. Das Programm cURL muss installiert sein (z.B. mit &amp;lt;code&amp;gt;apt install curl&amp;lt;/code&amp;gt;, oder durch Herunterladen und Installieren von der [https://curl.se cURL-Webseite]).&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod sendMail dummy&lt;br /&gt;
attr sendMail readingList message password&lt;br /&gt;
attr sendMail setList message:textField-long password&lt;br /&gt;
attr sendMail userReadings state:message:[\s\S]* {\&lt;br /&gt;
    my $emailTo   = &#039;du@example.com&#039;;;\&lt;br /&gt;
    my $emailFrom = &#039;fhem@example.de&#039;;;\&lt;br /&gt;
	#my $emailFrom = &#039;FHEM Server &amp;lt;fhem@example.de&amp;gt;&#039;;;\&lt;br /&gt;
	my $emailServer = &#039;mail.your-server.de&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #erzeuge Datumsangabe konform zu RFC-5322:\&lt;br /&gt;
    my @emailDateArray = split(&#039; &#039;, strftime(&amp;quot;%w %d %m %Y %H:%M:%S %z&amp;quot;, localtime));;\&lt;br /&gt;
    $emailDateArray[0] = (qw[Sun Mon Tue Wed Thu Fri Sat])[$emailDateArray[0]] . &#039;,&#039;;;\&lt;br /&gt;
    $emailDateArray[2] = (qw[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec])[$emailDateArray[2]-1];;\&lt;br /&gt;
    my $emailDate = join(&#039; &#039;, @emailDateArray);;\&lt;br /&gt;
    \&lt;br /&gt;
    # Passwort aus getKeyValue abrufen\&lt;br /&gt;
    my ($err, $emailPass) = getKeyValue(&amp;quot;${name}_password&amp;quot;);;\&lt;br /&gt;
    if ($err || !defined $emailPass) {\&lt;br /&gt;
        return &amp;quot;Error retrieving password: $err&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
	#extrahiere die E-Mail Adresse aus dem From für den Login:\&lt;br /&gt;
	my ($emailUser) = $emailFrom =~ /([A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,})/i;;\&lt;br /&gt;
    \&lt;br /&gt;
    my $message = ReadingsVal($name, &#039;message&#039;, &#039;???&#039;);;\&lt;br /&gt;
    my $subject = &amp;quot;$name: FHEM Nachricht&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # Betreff extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
    if ($message =~ s/(?:Subject|Betreff)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        $subject = $1;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    # Empfänger extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
    if ($message =~ s/(?:To|An)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        $emailTo = $1;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #Dateianhänge bestimmen\&lt;br /&gt;
    my @attachments;;\&lt;br /&gt;
    while ($message =~ s/(?:Attachment|Anhang)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        my $file = $1;;\&lt;br /&gt;
        return &amp;quot;&amp;gt;&amp;gt;$file&amp;lt;&amp;lt; not found, not sending email&amp;quot; unless (-e $file);;\&lt;br /&gt;
        push (@attachments, $file);;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #der curl Befehl wird hier aufgebaut:\&lt;br /&gt;
    my $cmd = &amp;quot;curl -m 19 --noproxy &#039;*&#039; --no-progress-meter --ssl-reqd smtps://$emailServer:465 &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;--user &#039;$emailUser:$emailPass&#039; --mail-from &#039;$emailUser&#039; --mail-rcpt &#039;$emailTo&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;--write-out &#039;Email sent with status %{http_code}&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;Subject: $subject&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;From: $emailFrom&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;Date: $emailDate&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #multipart/alternative für Text und HTML EMail Body\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;=(;;type=multipart/alternative&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Anfang entfernen:\&lt;br /&gt;
    $message =~ s/^(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+//;;\&lt;br /&gt;
    # Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Ende entfernen:\&lt;br /&gt;
    $message =~ s/(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+$//;;\&lt;br /&gt;
    \&lt;br /&gt;
    #Plaintext E-Mail Inhalt an cURL:\&lt;br /&gt;
    $cmd .= &amp;quot;-F \&amp;quot;=$message;;type=text/plain;; charset=UTF-8\&amp;quot; &amp;quot;;;\&lt;br /&gt;
    #ersetze \n bzw. \r\n durch ein HTML-tag &amp;lt;br /&amp;gt;:\&lt;br /&gt;
    $message =~ s/\r?\n/&amp;lt;br \/&amp;gt;/g;;\&lt;br /&gt;
    \&lt;br /&gt;
    #HTML E-Mail Inhalt an cURL:\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;= &amp;lt;body&amp;gt;$message&amp;lt;/body&amp;gt;;;type=text/html;; charset=UTF-8&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;=)&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #jetzt alle Attachments (0..N) anhängen:\&lt;br /&gt;
    foreach my $file (@attachments) {\&lt;br /&gt;
        $cmd .= &amp;quot;-F &#039;=@&amp;quot;.$file.&amp;quot;;;encoder=base64&#039; &amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #stderr auch mit in den output sammeln:\&lt;br /&gt;
    $cmd .= &amp;quot; 2&amp;gt;&amp;amp;1&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc1) {\&lt;br /&gt;
        *sendMailfunc1 = sub ($) {\&lt;br /&gt;
            my ($param) = @_;;\&lt;br /&gt;
            my $result;;\&lt;br /&gt;
            $result = qx/$param/;;\&lt;br /&gt;
            $result = MIME::Base64::encode_base64($result);;\&lt;br /&gt;
            $result =~ s/\n//g;;\&lt;br /&gt;
            return $result;;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #Rückgabe über diese Funktion,\&lt;br /&gt;
    #anzeigen in dem reading dieses UserReadings:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc2) {\&lt;br /&gt;
        *sendMailfunc2 = sub ($) {\&lt;br /&gt;
            my ($result) = @_;;\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            $result = MIME::Base64::decode_base64($result);;\&lt;br /&gt;
            readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #funktion falls BlockingCall abgebrochen wird:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc3) {\&lt;br /&gt;
        *sendMailfunc3 = sub ($) {\&lt;br /&gt;
            my ($result) = @_;;\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    BlockingCall(&amp;quot;sendMailfunc1&amp;quot;, $cmd, &amp;quot;sendMailfunc2&amp;quot;, 20, &amp;quot;sendMailfunc3&amp;quot;, &amp;quot;Error: timeout&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return &amp;quot;started cURL command...&amp;quot;;;\&lt;br /&gt;
},\&lt;br /&gt;
state:password:.* {\&lt;br /&gt;
    # Passwort speichern\&lt;br /&gt;
    my $ret = setKeyValue(&amp;quot;${name}_password&amp;quot;, ReadingsVal($name, &#039;password&#039;, undef)) // &amp;quot;password stored&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\&lt;br /&gt;
    #\&lt;br /&gt;
    # Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\&lt;br /&gt;
    # Alternativ: { setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;password&amp;quot;, &amp;quot;****&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return &amp;quot;$ret&amp;quot;;;\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Das Passwort setzt man mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail password geheimesPasswort&amp;lt;/code&amp;gt;&lt;br /&gt;
* Alternativ, damit das Passwort nicht im Log auftaucht: &amp;lt;code&amp;gt;{ setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
E-Mails kann man dann Senden mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Mein Text&amp;lt;/code&amp;gt; (es wird der Standardbetreff und Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;(es wird der Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Subject=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;✅ Dies ist ein Test&amp;quot; Meldung mit Emoji ⚽ geht auch&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eine Datei mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/bag.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zwei Dateien mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;./www/images/fhemSVG/bag.svg&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/batterie.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;, für die anzuhängenden Dateien können relative und absolute Pfade verwendet werden. Beim Vorbereiten der E-Mail wird geprüft ob die Dateien existieren, da dies sonst eine typische Fehlerquelle ist. Es wird nichts versendet wenn eine Datei nicht gefunden werden kann.&lt;br /&gt;
&lt;br /&gt;
==== Klarname als Absenderadresse ====&lt;br /&gt;
Will man als Absenderadresse einen Klarnamen verwenden, versteht CURL das auch, wenn man folgendes ändert ([https://forum.fhem.de/index.php?topic=140814.msg1353962#msg1353962 Hinweis aus dem Forum]):&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
#my $emailFrom = &#039;fhem@example.de&#039;;;\&lt;br /&gt;
&lt;br /&gt;
my $emailFrom = &#039;&amp;quot;Dein FHEM Server&amp;quot; &amp;lt;fhem@example.de&amp;gt;&#039;;;\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fehlersuche ====&lt;br /&gt;
&lt;br /&gt;
===== Fehlende Protokolloption in cURL =====&lt;br /&gt;
Sollte cURL zwar installiert sein, aber keine E-Mails versenden, kann es sein, dass das Protokoll nicht einkompiliert wurde. Dies ist bei OpenWRT zum Beispiel der Fall.&lt;br /&gt;
&lt;br /&gt;
So sollte es aussehen (hier ein Ubuntu), relevant ist für diese Nutzung das Protokoll smtp und smtps, das braucht man:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.7&lt;br /&gt;
Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.6&lt;br /&gt;
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp&lt;br /&gt;
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So sollte es nicht aussehen (hier ein OpenWRT):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl: (1) Protocol &amp;quot;smtps&amp;quot; not supported&lt;br /&gt;
&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.10.1 (arm-openwrt-linux-gnu) libcurl/8.10.1 mbedTLS/3.6.2 nghttp2/1.63.0&lt;br /&gt;
Release-Date: 2024-09-18&lt;br /&gt;
Protocols: file ftp ftps http https ipfs ipns mqtt&lt;br /&gt;
Features: alt-svc HSTS HTTP2 HTTPS-proxy IPv6 Largefile SSL threadsafe UnixSockets&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Hat man nur ein cURL ohne das Protokoll, so muss man sich das Paket leider anderweitig beschaffen (chroot, selbst kompilieren, statisch gelinkte Version, ...).&lt;br /&gt;
&lt;br /&gt;
===== Falscher Port, login options =====&lt;br /&gt;
Einige Provider nehmen ihre TLS verschlüsselten E-Mails über einen anderen Port an, häufig Port 587 (siehe auch {{Link2Forum|Topic= 140814|Message=1340366|LinkText=diesen Forenbeitrag}}). Ggf. muss man auch mit einigen Loginoptionen experimentieren bis cURL  wie gewünscht funktioniert (Stichwort: &#039;&#039;[https://curl.se/docs/manpage.html#--login-options --login-options]&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
===== Alte cURL Version =====&lt;br /&gt;
Sollte eine sehr alte cURL Version kleiner 7.67.0 genutzt werden, fehlt der Schalter &amp;lt;code&amp;gt;--no-progress-meter.&amp;lt;/code&amp;gt; In solchen Fällen sollte man cURL aktualisieren. Falls dies nicht möglich ist, kann man den Schalter ersetzen durch &amp;lt;code&amp;gt;--silent --show-error&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Providerspezifisches=====&lt;br /&gt;
Eine lose Liste mit providerspezifischen Erfolgen und Misserfolgen:&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|+Liste mit Rückmeldungen zu E-Mail Anbietern&lt;br /&gt;
!Provider&lt;br /&gt;
!Funktioniert&lt;br /&gt;
!Details&lt;br /&gt;
|-&lt;br /&gt;
|Hetzner&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;mail.your-server.de&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailServer:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Vodafone&lt;br /&gt;
|Ja&lt;br /&gt;
|Folgende Schalter sind hilfreich:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
--ssl-reqd --sasl-ir smtps://$emailserver:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Freenet&lt;br /&gt;
|Nein (nicht zuverlässig aufgrund des Spamfilters)&lt;br /&gt;
|Der Spamfilter lässt teilweise Nachrichten passieren, teilweise wird die Verbindung abgelehnt:&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;&#039;550 Spam message rejected&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
https://forum.fhem.de/index.php?topic=140814.msg1341872#msg1341872&lt;br /&gt;
|-&lt;br /&gt;
|T-Online&lt;br /&gt;
|Ja?&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;securesmtp.t-online.de&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailServer:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;https://forum.fhem.de/index.php?topic=140814.msg1340258#msg1340258&lt;br /&gt;
|-&lt;br /&gt;
|GMX&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;mail.gmx.net&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailserver:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;https://forum.fhem.de/index.php?topic=140814.msg1341891#msg1341891&lt;br /&gt;
|-&lt;br /&gt;
|Google -&amp;gt; GMail&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;smtp.gmail.com&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailserver:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;https://forum.fhem.de/index.php?topic=140814.msg1341891#msg1341891&lt;br /&gt;
|-&lt;br /&gt;
|Strato&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;smtp.strato.de&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailserver:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;https://forum.fhem.de/index.php?topic=140814.msg1342201#msg1342201&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Siehe auch dazu den {{Link2Forum|Message=1341182|Topic=140814|LinkText=Forumspost}}&lt;br /&gt;
===EXIM4 (Debian)===&lt;br /&gt;
Eine Anleitung zum Versenden von E-Mails mit Exim ist als PDF verfügbar: [[Medium:Anleitung Exim4 Debian GMX.pdf|PDF]]&lt;br /&gt;
&lt;br /&gt;
===SSMTP (OpenWRT, embedded Distros)===&lt;br /&gt;
&lt;br /&gt;
====Installation====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install ssmtp_2.64-4_mpc85xx.ipk  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Damit &amp;lt;code&amp;gt;ssmtp&amp;lt;/code&amp;gt; funktioniert müssen die Dateien &amp;lt;code&amp;gt;/etc/ssmtp/ssmtp.conf&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;/etc/ssmtp/revaliases&amp;lt;/code&amp;gt; angepasst werden.&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/ssmtp.conf &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root=arnold@gmx.net&lt;br /&gt;
 mailhub=mail.gmx.net:465&lt;br /&gt;
 rewriteDomain=gmx.net&lt;br /&gt;
 hostname=gmx.net&lt;br /&gt;
 FromLineOverride=YES&lt;br /&gt;
 UseTLS=YES&lt;br /&gt;
 #UseSTARTTLS=YES&lt;br /&gt;
 AuthUser=arnold@gmx.net&lt;br /&gt;
 AuthPass=Passwort_von_arnold@gmx.net&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/revaliases&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root:arnold@gmx.net:mail.gmx.net:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der [[99_myUtils_anlegen|99_myUtils]] folgende Unterroutine einfügen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub OpenWRTMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $sender = &amp;quot;dockstar\@heye-tammo.de&amp;quot;; &lt;br /&gt;
 Log 1, &amp;quot;sendEmail RCP: $rcpt, Subject: $subject, Text: $text&amp;quot;;&lt;br /&gt;
 $ret .= qx(echo -e &#039;to:$rcpt\n from:$sender\nsubject:$subject\n$text\n&#039; | ssmtp $rcpt);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendEmail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===PHP Mail Funktion (Synology DiskStation)===&lt;br /&gt;
Beim DSM 3.2 funktioniert der &amp;lt;code&amp;gt;php-mail&amp;lt;/code&amp;gt;-Befehl, so dass man mittels folgendem Modul E-Mails versenden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub sendmail($$$) {&lt;br /&gt;
 my($empf, $subj, $nachricht) = @_;&lt;br /&gt;
 system(&amp;quot;php -r &#039;mail(\&amp;quot;$empf\&amp;quot;,\&amp;quot;$subj\&amp;quot;,\&amp;quot;$nachricht\&amp;quot;);&#039;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sendemail (Veraltet, Debian-basierende Distros)===&lt;br /&gt;
Es handelt sich um ein Perl Programm, dass man aus den Paketquellen installieren kann.&lt;br /&gt;
&lt;br /&gt;
Es gibt Probleme: &lt;br /&gt;
&lt;br /&gt;
*mit der TLS bzw STARTTLS Verschlüsselung (https://github.com/fhem/fhem-docker/issues/254, https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1915977.html) ,&lt;br /&gt;
*Mit der Webseite des Projekts (https://web.archive.org/web/20190906140125/http://caspian.dotconf.net/menu/Software/SendEmail); diese ist nicht mehr verfügbar und nur eine archivierte Kopie kann eingesehen werden,&lt;br /&gt;
&lt;br /&gt;
weswegen diese Lösung als technisch überholt gelten sollte. Will man trotzdem &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; verwenden folgenden hier die Informationen dazu:&lt;br /&gt;
&lt;br /&gt;
Auf dem Server muss &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; installiert werden:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt-get update&lt;br /&gt;
sudo apt-get install sendemail libio-socket-ssl-perl libnet-ssleay-perl perl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Man kann die korrekte Funktion im Terminal (ohne &amp;lt;code&amp;gt;{qx()}&amp;lt;/code&amp;gt; ) oder auch in der FHEM Kommandozeile testen (Hinweis: manche Provider (z.B. gmail) lassen diesen Zugang erst nach Freischaltung zu!).&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Bei Gmail muss seit dem 30.6.2022 ein separates Passwort erzeugt werden, die alte Einstellung über &amp;quot;Zugriff durch weniger sichere Apps&amp;quot; funktioniert nicht mehr. Siehe Support Seite Google: &amp;lt;nowiki&amp;gt;https://support.google.com/accounts/answer/185833&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Beispiel mit Gmail über Linux Terminalfenster:&#039;&#039;&lt;br /&gt;
 sendemail -f &#039;Der.Absender@gmail.com&#039; -t &#039;Der.Empfaenger@gmail.com&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt; -s &#039;smtp.gmail.com:587&#039; -xu &#039;Der.Absender&#039; -xp DASSUPERGEHEIMEPASSW0RT&lt;br /&gt;
In der [[99 myUtils anlegen|99_myUtils]] folgende Unterroutine einfügen:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
######## DebianMail  Mail auf dem RPi versenden ############ &lt;br /&gt;
# $provider für SMTP Server anpassen. &lt;br /&gt;
# Einmal in der FHEM Kommandozeile, user und password anpassen:&lt;br /&gt;
# {setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&lt;br /&gt;
sub DebianMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $attach = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $error;&lt;br /&gt;
 my $konto = getKeyValue(&amp;quot;myEmailKonto&amp;quot;); &lt;br /&gt;
 my $passwrd = getKeyValue(&amp;quot;myEmailPasswrd&amp;quot;); &lt;br /&gt;
 my $from = $konto; # or use different KeyValue if konto is not the from email address&lt;br /&gt;
 my $provider = &amp;quot;smtp.1und1.de&amp;quot;; # smtp.domain.tld:port see provider documentation&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail RCP: $rcpt&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Subject: $subject&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Text: $text&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Anhang: $attach&amp;quot;;&lt;br /&gt;
 if (not defined($attach)){$attach=&#039;&#039;}&lt;br /&gt;
 $ret .= qx(sendemail -f &#039;$from&#039; -t &#039;$rcpt&#039; -u &#039;$subject&#039; -m &#039;$text&#039; -a $attach -s &#039;$provider&#039; -xu &#039;$konto&#039; -xp &#039;$passwrd&#039; -o tls=auto -o message-charset=utf-8);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendemail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
###############################################################################&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Die Zugangsdaten für den Email-Account müssen dabei FHEM-intern gespeichert werden. Dazu gibt man einmalig über die Kommandozeile folgendes ein (user@domain und password müssen auf den Email Account geändert werden):&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;{setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die TLS Verschlüsselung (ehem. SSL) nicht zu nutzen, muss in der viertletzten Zeile tls=no verwendet und der Port des Mailproviders auf 25 eigetragen werden. Sollte anschließend keine Mail verschickt werden, siehe Probleme.&lt;br /&gt;
&lt;br /&gt;
Falls der Body-Text in einem (Android-)Mailer auf dem Handy nicht gezeigt wird, kann der Parameter &#039;&#039;&#039;-o message-content-type=html&#039;&#039;&#039; helfen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion kann beispielsweise folgendermaßen aufgerufen werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{qx(sendemail -f &#039;Ab\@send.er&#039; -t &#039;Emp\@faeng.er&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &#039;DateinameOderLeer&#039; -s &#039;smtpFQDN:port&#039; -xu &#039;MailUser&#039; -xp &#039;PasswortOhneKlammeraffen&#039;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bei allen Feldern können generell doppelte &amp;quot; oder einfache &#039; Anführungszeichen verwendet werden. Innerhalb von &amp;quot;&amp;quot; müssen Sonderzeichen wie @ aber maskiert werden, da sie sonst als Steuerzeichen interpretiert werden:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;quot;email\@email.domain&amp;quot; oder &#039;email@email.domain&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direkt in der FHEM Kommandozeile (ohne Anhang)&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;{DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ordnungsgemäße Ausführung wird mit 71 quittiert.&lt;br /&gt;
&lt;br /&gt;
Beispiele mehrere Anhänge (Es ist wichtig Dateinamen mit Leerzeichen in separate Anführungszeichen zu setzen)&lt;br /&gt;
 {DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;Anhang1 Anhang2 Anhang3&amp;quot;)}&lt;br /&gt;
 {DebianMail(&#039;email@email.domain&#039;,&#039;Subject&#039;,&#039;Text&#039;,&amp;quot;&#039;with spaces Anhang1&#039; &#039;with spaces Anhang2&#039;&amp;quot;)}&lt;br /&gt;
oder (mehr als einen Empfänger, diese können durch Leerzeichen, Semikolon oder Komma getrennt sein)&lt;br /&gt;
 define Sonstiges at *01:00:00 {\&lt;br /&gt;
 DebianMail(&#039;email@email.domain,email2@email.domain&#039;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;&#039;Anhang1&#039; &#039;Anhang2&#039; ...&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Die archivierte Doku zu &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; befindet sich in der Änderungshistorie: https://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;amp;oldid=37524&lt;br /&gt;
[[Kategorie:E-Mail]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40556</id>
		<title>E-Mail senden</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40556"/>
		<updated>2025-12-21T18:21:59Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Hinweis zum Klarnamen und E-Mail in der Absenderadresse, danke @mabula&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Um aus FHEM heraus E-Mails senden zu können, gibt es unterschiedliche Methoden, abhängig von der verwendeten Plattform.&lt;br /&gt;
&lt;br /&gt;
Lange Zeit war &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; sehr beliebt. Da aber die Homepage des Projekts nicht mehr online ist und Probleme nur noch via erhöhtem Supportaufwand gelöst werden, sollte diese Option als veraltet betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Eine Liste mit ressourcensparenden E-Mails Clients findet man bei dem OpenWRT Projekt: https://openwrt.org/docs/guide-user/services/email/smtp.client&lt;br /&gt;
&lt;br /&gt;
MSGMail ist ein FHEM eigenes Modul zum Versenden, die Einrichtung ist allerdings nicht gänzlich einfach. Anfängern sei die cURL Methode empfohlen.&lt;br /&gt;
&lt;br /&gt;
=== MSGMail (Linux, Windows) ===&lt;br /&gt;
FHEM bringt ein eigenes Device zum Senden von E-Mails mit, welches in Perl implementiert ist: [https://fhem.de/commandref.html#MSGMail MSGMail, https://fhem.de/commandref.html#MSGMail]&lt;br /&gt;
&lt;br /&gt;
=== cURL (Linux, Windows) ===&lt;br /&gt;
[https://curl.se/ cURL] kann auf praktisch [https://curl.se/download.html jedem Betriebssystem] genutzt werden, [https://curl.se/windows/ selbst unter Windows]. Es kann direkt mit einem SMTP Server E-Mails versenden und stellt damit eine moderne, portable und sichere Option zum Versenden von E-Mails dar.&lt;br /&gt;
&lt;br /&gt;
In FHEM richtet man sich ein Device ein und passt die Details (&amp;lt;code&amp;gt;$emailTo, $emailFrom, $emailServer&amp;lt;/code&amp;gt;) an. Das Programm cURL muss installiert sein (z.B. mit &amp;lt;code&amp;gt;apt install curl&amp;lt;/code&amp;gt;, oder durch Herunterladen und Installieren von der [https://curl.se cURL-Webseite]).&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod sendMail dummy&lt;br /&gt;
attr sendMail readingList message password&lt;br /&gt;
attr sendMail setList message:textField-long password&lt;br /&gt;
attr sendMail userReadings state:message:[\s\S]* {\&lt;br /&gt;
    my $emailTo   = &#039;du@example.com&#039;;;\&lt;br /&gt;
    my $emailFrom = &#039;fhem@example.de&#039;;;\&lt;br /&gt;
    my $emailServer = &#039;mail.your-server.de&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #erzeuge Datumsangabe konform zu RFC-5322:\&lt;br /&gt;
    my @emailDateArray = split(&#039; &#039;, strftime(&amp;quot;%w %d %m %Y %H:%M:%S %z&amp;quot;, localtime));;\&lt;br /&gt;
    $emailDateArray[0] = (qw[Sun Mon Tue Wed Thu Fri Sat])[$emailDateArray[0]] . &#039;,&#039;;;\&lt;br /&gt;
    $emailDateArray[2] = (qw[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec])[$emailDateArray[2]-1];;\&lt;br /&gt;
    my $emailDate = join(&#039; &#039;, @emailDateArray);;\&lt;br /&gt;
    \&lt;br /&gt;
    # Passwort aus getKeyValue abrufen\&lt;br /&gt;
    my ($err, $emailPass) = getKeyValue(&amp;quot;${name}_password&amp;quot;);;\&lt;br /&gt;
    if ($err || !defined $emailPass) {\&lt;br /&gt;
        return &amp;quot;Error retrieving password: $err&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    my $message = ReadingsVal($name, &#039;message&#039;, &#039;???&#039;);;\&lt;br /&gt;
    my $subject = &amp;quot;$name: FHEM Nachricht&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # Betreff extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
    if ($message =~ s/(?:Subject|Betreff)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        $subject = $1;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    # Empfänger extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
    if ($message =~ s/(?:To|An)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        $emailTo = $1;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #Dateianhänge bestimmen\&lt;br /&gt;
    my @attachments;;\&lt;br /&gt;
    while ($message =~ s/(?:Attachment|Anhang)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        my $file = $1;;\&lt;br /&gt;
        return &amp;quot;&amp;gt;&amp;gt;$file&amp;lt;&amp;lt; not found, not sending email&amp;quot; unless (-e $file);;\&lt;br /&gt;
        push (@attachments, $file);;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #der curl Befehl wird hier aufgebaut:\&lt;br /&gt;
    my $cmd = &amp;quot;curl -m 19 --noproxy &#039;*&#039; --no-progress-meter --ssl-reqd smtps://$emailServer:465 &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;--user &#039;$emailFrom:$emailPass&#039; --mail-from &#039;$emailFrom&#039; --mail-rcpt &#039;$emailTo&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;--write-out &#039;Email sent with status %{http_code}&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;Subject: $subject&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;From: $emailFrom&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;Date: $emailDate&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #multipart/alternative für Text und HTML EMail Body\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;=(;;type=multipart/alternative&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Anfang entfernen:\&lt;br /&gt;
    $message =~ s/^(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+//;;\&lt;br /&gt;
    # Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Ende entfernen:\&lt;br /&gt;
    $message =~ s/(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+$//;;\&lt;br /&gt;
    \&lt;br /&gt;
    #Plaintext E-Mail Inhalt an cURL:\&lt;br /&gt;
    $cmd .= &amp;quot;-F \&amp;quot;=$message;;type=text/plain;; charset=UTF-8\&amp;quot; &amp;quot;;;\&lt;br /&gt;
    #ersetze \n bzw. \r\n durch ein HTML-tag &amp;lt;br /&amp;gt;:\&lt;br /&gt;
    $message =~ s/\r?\n/&amp;lt;br \/&amp;gt;/g;;\&lt;br /&gt;
    \&lt;br /&gt;
    #HTML E-Mail Inhalt an cURL:\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;= &amp;lt;body&amp;gt;$message&amp;lt;/body&amp;gt;;;type=text/html;; charset=UTF-8&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;=)&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #jetzt alle Attachments (0..N) anhängen:\&lt;br /&gt;
    foreach my $file (@attachments) {\&lt;br /&gt;
        $cmd .= &amp;quot;-F &#039;=@&amp;quot;.$file.&amp;quot;;;encoder=base64&#039; &amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #stderr auch mit in den output sammeln:\&lt;br /&gt;
    $cmd .= &amp;quot; 2&amp;gt;&amp;amp;1&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc1) {\&lt;br /&gt;
        *sendMailfunc1 = sub ($) {\&lt;br /&gt;
            my ($param) = @_;;\&lt;br /&gt;
            my $result;;\&lt;br /&gt;
            $result = qx/$param/;;\&lt;br /&gt;
            $result = MIME::Base64::encode_base64($result);;\&lt;br /&gt;
            $result =~ s/\n//g;;\&lt;br /&gt;
            return $result;;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #Rückgabe über diese Funktion,\&lt;br /&gt;
    #anzeigen in dem reading dieses UserReadings:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc2) {\&lt;br /&gt;
        *sendMailfunc2 = sub ($) {\&lt;br /&gt;
            my ($result) = @_;;\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            $result = MIME::Base64::decode_base64($result);;\&lt;br /&gt;
            readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #funktion falls BlockingCall abgebrochen wird:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc3) {\&lt;br /&gt;
        *sendMailfunc3 = sub ($) {\&lt;br /&gt;
            my ($result) = @_;;\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    BlockingCall(&amp;quot;sendMailfunc1&amp;quot;, $cmd, &amp;quot;sendMailfunc2&amp;quot;, 20, &amp;quot;sendMailfunc3&amp;quot;, &amp;quot;Error: timeout&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return &amp;quot;started cURL command...&amp;quot;;;\&lt;br /&gt;
},\&lt;br /&gt;
state:password:.* {\&lt;br /&gt;
    # Passwort speichern\&lt;br /&gt;
    my $ret = setKeyValue(&amp;quot;${name}_password&amp;quot;, ReadingsVal($name, &#039;password&#039;, undef)) // &amp;quot;password stored&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\&lt;br /&gt;
    #\&lt;br /&gt;
    # Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\&lt;br /&gt;
    # Alternativ: { setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;password&amp;quot;, &amp;quot;****&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return &amp;quot;$ret&amp;quot;;;\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Das Passwort setzt man mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail password geheimesPasswort&amp;lt;/code&amp;gt;&lt;br /&gt;
* Alternativ, damit das Passwort nicht im Log auftaucht: &amp;lt;code&amp;gt;{ setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
E-Mails kann man dann Senden mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Mein Text&amp;lt;/code&amp;gt; (es wird der Standardbetreff und Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;(es wird der Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Subject=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;✅ Dies ist ein Test&amp;quot; Meldung mit Emoji ⚽ geht auch&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eine Datei mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/bag.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zwei Dateien mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;./www/images/fhemSVG/bag.svg&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/batterie.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;, für die anzuhängenden Dateien können relative und absolute Pfade verwendet werden. Beim Vorbereiten der E-Mail wird geprüft ob die Dateien existieren, da dies sonst eine typische Fehlerquelle ist. Es wird nichts versendet wenn eine Datei nicht gefunden werden kann.&lt;br /&gt;
&lt;br /&gt;
==== Klarname als Absenderadresse ====&lt;br /&gt;
Will man als Absenderadresse einen Klarnamen verwenden, versteht CURL das auch, wenn man folgendes ändert ([https://forum.fhem.de/index.php?topic=140814.msg1353962#msg1353962 Hinweis aus dem Forum]):&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
#my $emailFrom = &#039;fhem@example.de&#039;;;\&lt;br /&gt;
&lt;br /&gt;
my $emailFrom = &#039;&amp;quot;Dein FHEM Server&amp;quot; &amp;lt;fhem@example.de&amp;gt;&#039;;;\&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fehlersuche ====&lt;br /&gt;
&lt;br /&gt;
===== Fehlende Protokolloption in cURL =====&lt;br /&gt;
Sollte cURL zwar installiert sein, aber keine E-Mails versenden, kann es sein, dass das Protokoll nicht einkompiliert wurde. Dies ist bei OpenWRT zum Beispiel der Fall.&lt;br /&gt;
&lt;br /&gt;
So sollte es aussehen (hier ein Ubuntu), relevant ist für diese Nutzung das Protokoll smtp und smtps, das braucht man:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.7&lt;br /&gt;
Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.6&lt;br /&gt;
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp&lt;br /&gt;
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So sollte es nicht aussehen (hier ein OpenWRT):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl: (1) Protocol &amp;quot;smtps&amp;quot; not supported&lt;br /&gt;
&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.10.1 (arm-openwrt-linux-gnu) libcurl/8.10.1 mbedTLS/3.6.2 nghttp2/1.63.0&lt;br /&gt;
Release-Date: 2024-09-18&lt;br /&gt;
Protocols: file ftp ftps http https ipfs ipns mqtt&lt;br /&gt;
Features: alt-svc HSTS HTTP2 HTTPS-proxy IPv6 Largefile SSL threadsafe UnixSockets&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Hat man nur ein cURL ohne das Protokoll, so muss man sich das Paket leider anderweitig beschaffen (chroot, selbst kompilieren, statisch gelinkte Version, ...).&lt;br /&gt;
&lt;br /&gt;
===== Falscher Port, login options =====&lt;br /&gt;
Einige Provider nehmen ihre TLS verschlüsselten E-Mails über einen anderen Port an, häufig Port 587 (siehe auch {{Link2Forum|Topic= 140814|Message=1340366|LinkText=diesen Forenbeitrag}}). Ggf. muss man auch mit einigen Loginoptionen experimentieren bis cURL  wie gewünscht funktioniert (Stichwort: &#039;&#039;[https://curl.se/docs/manpage.html#--login-options --login-options]&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
===== Alte cURL Version =====&lt;br /&gt;
Sollte eine sehr alte cURL Version kleiner 7.67.0 genutzt werden, fehlt der Schalter &amp;lt;code&amp;gt;--no-progress-meter.&amp;lt;/code&amp;gt; In solchen Fällen sollte man cURL aktualisieren. Falls dies nicht möglich ist, kann man den Schalter ersetzen durch &amp;lt;code&amp;gt;--silent --show-error&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Providerspezifisches=====&lt;br /&gt;
Eine lose Liste mit providerspezifischen Erfolgen und Misserfolgen:&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|+Liste mit Rückmeldungen zu E-Mail Anbietern&lt;br /&gt;
!Provider&lt;br /&gt;
!Funktioniert&lt;br /&gt;
!Details&lt;br /&gt;
|-&lt;br /&gt;
|Hetzner&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;mail.your-server.de&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailServer:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Vodafone&lt;br /&gt;
|Ja&lt;br /&gt;
|Folgende Schalter sind hilfreich:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
--ssl-reqd --sasl-ir smtps://$emailserver:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Freenet&lt;br /&gt;
|Nein (nicht zuverlässig aufgrund des Spamfilters)&lt;br /&gt;
|Der Spamfilter lässt teilweise Nachrichten passieren, teilweise wird die Verbindung abgelehnt:&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;&#039;550 Spam message rejected&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
https://forum.fhem.de/index.php?topic=140814.msg1341872#msg1341872&lt;br /&gt;
|-&lt;br /&gt;
|T-Online&lt;br /&gt;
|Ja?&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;securesmtp.t-online.de&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailServer:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;https://forum.fhem.de/index.php?topic=140814.msg1340258#msg1340258&lt;br /&gt;
|-&lt;br /&gt;
|GMX&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;mail.gmx.net&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailserver:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;https://forum.fhem.de/index.php?topic=140814.msg1341891#msg1341891&lt;br /&gt;
|-&lt;br /&gt;
|Google -&amp;gt; GMail&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;smtp.gmail.com&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailserver:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;https://forum.fhem.de/index.php?topic=140814.msg1341891#msg1341891&lt;br /&gt;
|-&lt;br /&gt;
|Strato&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;smtp.strato.de&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailserver:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;https://forum.fhem.de/index.php?topic=140814.msg1342201#msg1342201&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Siehe auch dazu den {{Link2Forum|Message=1341182|Topic=140814|LinkText=Forumspost}}&lt;br /&gt;
===EXIM4 (Debian)===&lt;br /&gt;
Eine Anleitung zum Versenden von E-Mails mit Exim ist als PDF verfügbar: [[Medium:Anleitung Exim4 Debian GMX.pdf|PDF]]&lt;br /&gt;
&lt;br /&gt;
===SSMTP (OpenWRT, embedded Distros)===&lt;br /&gt;
&lt;br /&gt;
====Installation====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install ssmtp_2.64-4_mpc85xx.ipk  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Damit &amp;lt;code&amp;gt;ssmtp&amp;lt;/code&amp;gt; funktioniert müssen die Dateien &amp;lt;code&amp;gt;/etc/ssmtp/ssmtp.conf&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;/etc/ssmtp/revaliases&amp;lt;/code&amp;gt; angepasst werden.&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/ssmtp.conf &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root=arnold@gmx.net&lt;br /&gt;
 mailhub=mail.gmx.net:465&lt;br /&gt;
 rewriteDomain=gmx.net&lt;br /&gt;
 hostname=gmx.net&lt;br /&gt;
 FromLineOverride=YES&lt;br /&gt;
 UseTLS=YES&lt;br /&gt;
 #UseSTARTTLS=YES&lt;br /&gt;
 AuthUser=arnold@gmx.net&lt;br /&gt;
 AuthPass=Passwort_von_arnold@gmx.net&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/revaliases&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root:arnold@gmx.net:mail.gmx.net:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der [[99_myUtils_anlegen|99_myUtils]] folgende Unterroutine einfügen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub OpenWRTMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $sender = &amp;quot;dockstar\@heye-tammo.de&amp;quot;; &lt;br /&gt;
 Log 1, &amp;quot;sendEmail RCP: $rcpt, Subject: $subject, Text: $text&amp;quot;;&lt;br /&gt;
 $ret .= qx(echo -e &#039;to:$rcpt\n from:$sender\nsubject:$subject\n$text\n&#039; | ssmtp $rcpt);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendEmail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===PHP Mail Funktion (Synology DiskStation)===&lt;br /&gt;
Beim DSM 3.2 funktioniert der &amp;lt;code&amp;gt;php-mail&amp;lt;/code&amp;gt;-Befehl, so dass man mittels folgendem Modul E-Mails versenden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub sendmail($$$) {&lt;br /&gt;
 my($empf, $subj, $nachricht) = @_;&lt;br /&gt;
 system(&amp;quot;php -r &#039;mail(\&amp;quot;$empf\&amp;quot;,\&amp;quot;$subj\&amp;quot;,\&amp;quot;$nachricht\&amp;quot;);&#039;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sendemail (Veraltet, Debian-basierende Distros)===&lt;br /&gt;
Es handelt sich um ein Perl Programm, dass man aus den Paketquellen installieren kann.&lt;br /&gt;
&lt;br /&gt;
Es gibt Probleme: &lt;br /&gt;
&lt;br /&gt;
*mit der TLS bzw STARTTLS Verschlüsselung (https://github.com/fhem/fhem-docker/issues/254, https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1915977.html) ,&lt;br /&gt;
*Mit der Webseite des Projekts (https://web.archive.org/web/20190906140125/http://caspian.dotconf.net/menu/Software/SendEmail); diese ist nicht mehr verfügbar und nur eine archivierte Kopie kann eingesehen werden,&lt;br /&gt;
&lt;br /&gt;
weswegen diese Lösung als technisch überholt gelten sollte. Will man trotzdem &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; verwenden folgenden hier die Informationen dazu:&lt;br /&gt;
&lt;br /&gt;
Auf dem Server muss &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; installiert werden:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt-get update&lt;br /&gt;
sudo apt-get install sendemail libio-socket-ssl-perl libnet-ssleay-perl perl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Man kann die korrekte Funktion im Terminal (ohne &amp;lt;code&amp;gt;{qx()}&amp;lt;/code&amp;gt; ) oder auch in der FHEM Kommandozeile testen (Hinweis: manche Provider (z.B. gmail) lassen diesen Zugang erst nach Freischaltung zu!).&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Bei Gmail muss seit dem 30.6.2022 ein separates Passwort erzeugt werden, die alte Einstellung über &amp;quot;Zugriff durch weniger sichere Apps&amp;quot; funktioniert nicht mehr. Siehe Support Seite Google: &amp;lt;nowiki&amp;gt;https://support.google.com/accounts/answer/185833&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Beispiel mit Gmail über Linux Terminalfenster:&#039;&#039;&lt;br /&gt;
 sendemail -f &#039;Der.Absender@gmail.com&#039; -t &#039;Der.Empfaenger@gmail.com&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt; -s &#039;smtp.gmail.com:587&#039; -xu &#039;Der.Absender&#039; -xp DASSUPERGEHEIMEPASSW0RT&lt;br /&gt;
In der [[99 myUtils anlegen|99_myUtils]] folgende Unterroutine einfügen:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
######## DebianMail  Mail auf dem RPi versenden ############ &lt;br /&gt;
# $provider für SMTP Server anpassen. &lt;br /&gt;
# Einmal in der FHEM Kommandozeile, user und password anpassen:&lt;br /&gt;
# {setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&lt;br /&gt;
sub DebianMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $attach = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $error;&lt;br /&gt;
 my $konto = getKeyValue(&amp;quot;myEmailKonto&amp;quot;); &lt;br /&gt;
 my $passwrd = getKeyValue(&amp;quot;myEmailPasswrd&amp;quot;); &lt;br /&gt;
 my $from = $konto; # or use different KeyValue if konto is not the from email address&lt;br /&gt;
 my $provider = &amp;quot;smtp.1und1.de&amp;quot;; # smtp.domain.tld:port see provider documentation&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail RCP: $rcpt&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Subject: $subject&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Text: $text&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Anhang: $attach&amp;quot;;&lt;br /&gt;
 if (not defined($attach)){$attach=&#039;&#039;}&lt;br /&gt;
 $ret .= qx(sendemail -f &#039;$from&#039; -t &#039;$rcpt&#039; -u &#039;$subject&#039; -m &#039;$text&#039; -a $attach -s &#039;$provider&#039; -xu &#039;$konto&#039; -xp &#039;$passwrd&#039; -o tls=auto -o message-charset=utf-8);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendemail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
###############################################################################&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Die Zugangsdaten für den Email-Account müssen dabei FHEM-intern gespeichert werden. Dazu gibt man einmalig über die Kommandozeile folgendes ein (user@domain und password müssen auf den Email Account geändert werden):&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;{setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die TLS Verschlüsselung (ehem. SSL) nicht zu nutzen, muss in der viertletzten Zeile tls=no verwendet und der Port des Mailproviders auf 25 eigetragen werden. Sollte anschließend keine Mail verschickt werden, siehe Probleme.&lt;br /&gt;
&lt;br /&gt;
Falls der Body-Text in einem (Android-)Mailer auf dem Handy nicht gezeigt wird, kann der Parameter &#039;&#039;&#039;-o message-content-type=html&#039;&#039;&#039; helfen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion kann beispielsweise folgendermaßen aufgerufen werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{qx(sendemail -f &#039;Ab\@send.er&#039; -t &#039;Emp\@faeng.er&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &#039;DateinameOderLeer&#039; -s &#039;smtpFQDN:port&#039; -xu &#039;MailUser&#039; -xp &#039;PasswortOhneKlammeraffen&#039;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bei allen Feldern können generell doppelte &amp;quot; oder einfache &#039; Anführungszeichen verwendet werden. Innerhalb von &amp;quot;&amp;quot; müssen Sonderzeichen wie @ aber maskiert werden, da sie sonst als Steuerzeichen interpretiert werden:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;quot;email\@email.domain&amp;quot; oder &#039;email@email.domain&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direkt in der FHEM Kommandozeile (ohne Anhang)&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;{DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ordnungsgemäße Ausführung wird mit 71 quittiert.&lt;br /&gt;
&lt;br /&gt;
Beispiele mehrere Anhänge (Es ist wichtig Dateinamen mit Leerzeichen in separate Anführungszeichen zu setzen)&lt;br /&gt;
 {DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;Anhang1 Anhang2 Anhang3&amp;quot;)}&lt;br /&gt;
 {DebianMail(&#039;email@email.domain&#039;,&#039;Subject&#039;,&#039;Text&#039;,&amp;quot;&#039;with spaces Anhang1&#039; &#039;with spaces Anhang2&#039;&amp;quot;)}&lt;br /&gt;
oder (mehr als einen Empfänger, diese können durch Leerzeichen, Semikolon oder Komma getrennt sein)&lt;br /&gt;
 define Sonstiges at *01:00:00 {\&lt;br /&gt;
 DebianMail(&#039;email@email.domain,email2@email.domain&#039;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;&#039;Anhang1&#039; &#039;Anhang2&#039; ...&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Die archivierte Doku zu &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; befindet sich in der Änderungshistorie: https://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;amp;oldid=37524&lt;br /&gt;
[[Kategorie:E-Mail]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40500</id>
		<title>DevelopmentFHEMWEB-API</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40500"/>
		<updated>2025-11-22T19:11:49Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Beispiel ergänzt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Randnotiz|RNTyp=r|RNText=Bitte beachten: Diese Auflistung von Funktionen wird nicht aktiv durch alle Entwickler gepflegt. Es kann daher keine Garantie auf Vollständigkeit und Korrektheit gegeben werden, entscheidend ist immer der aktuelle Programm-Code.&lt;br /&gt;
&amp;lt;hr /&amp;gt;&lt;br /&gt;
Please note: This list of functions is not official maintained by all developers. So there is no guarantee for completeness and correctness, at least the program code itself is relevant.&lt;br /&gt;
}}&lt;br /&gt;
Dieses Seite soll die verfügbaren Funktionen zugänglicher machen, um das Beisteuern von Funktionalität zu erleichtern.&lt;br /&gt;
&lt;br /&gt;
Natürlich hat diese Seite keinen Anspruch auf Vollständigkeit und jeder ist aufgefordert mitzuarbeiten, sobald er beim Stöbern über eine Funktion stolpert, die auch andere interessieren könnte.&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Server (pgm2) Perl-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_ME&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| URL-Pfad unter dem der FHEMWEB-Server via HTTP erreichbar ist.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;/fhem&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_dir&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| Das Basisverzeichnis auf dem lokalen Filesystem des FHEMWEB-Servers.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;./www&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_CSRF&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält das aktuell gültige CSRF-Token als HTTP URL-Parameter für die aktuelle Verbindung oder bleibt leer.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;&amp;amp;fwcsrf=csrf_165435377333711&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_detail&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuell erzeugten Detailansicht. &lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Licht_Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_room&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Raumnamen der aktuell erzeugten Raumansicht.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_wname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen FHEMWEB-Instanz.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_cname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen Verbindung (Connection-Name).&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB_192.168.179.65_54406&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_userAgent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den User-Agent, welcher für den aktuellen Aufruf übermittelt wurde.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
Will man mit den Variablen zum Beispiel mittels eines Perl-Codes einen Link aufbauen, dann kann dieser wie folgt zusammengesetzt werden: &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
my $cmd = &amp;quot;list&amp;quot;;&lt;br /&gt;
my $link = &amp;quot;&amp;lt;a href=\&amp;quot;${FW_ME}?XHR=1${FW_CSRF}&amp;amp;cmd=${cmd}\&amp;quot;&amp;gt;Klick mich&amp;lt;/a&amp;gt;&amp;quot;;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_makeImage ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage($icon, $text, $class);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_makeImage gibt HTML-Code zurück, der ein Icon aus dem dem FHEMWEB-Iconpool beschreibt.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, mit optionaler Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname&amp;gt;[@fillcolor&amp;gt;[@strokecolor&amp;gt;]]&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
Die Farbangaben ersetzen bei SVG Dateien nur die Farbwerte für stroke und fill, sofern diese in der SVG Datei &amp;lt;code&amp;gt;#000000&amp;lt;/code&amp;gt; sind. Wird also eine Angabe &amp;lt;code&amp;gt;fill:#000000&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fill=#000000&amp;lt;/code&amp;gt; im Quelltext des SVG gefunden, wird dies ersetzt durch &amp;lt;code&amp;gt;fill:fillcolor&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;fill=fillcolor&amp;lt;/code&amp;gt;, je nachdem ob ein Doppelpunkt oder Gleichheitszeichen vorhanden war.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine SVG-Datei verweist, dann wird &amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt; dem svg-HTML-Tag hinzugefügt. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;svg ... data-txt=&amp;quot;$text&amp;quot; ... &amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine Datei verweist die nicht auf &#039;&#039;.svg&#039;&#039; endet, dann wird der Pfad von der Datei hinter &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; als &amp;lt;code&amp;gt;&amp;lt;img&amp;gt;&amp;lt;/code&amp;gt; eingebunden. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;img ... src=&amp;quot;...&amp;quot; alt=&amp;quot;$text&amp;quot; title=&amp;quot;$text&amp;quot;&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$class&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine CSS-Klasse angibt.&lt;br /&gt;
Wird der Parameter angegeben, wird dem &amp;lt;img&amp;gt; oder &amp;lt;svg&amp;gt; &amp;lt;code&amp;gt;class=&amp;quot;$class&amp;quot;&amp;lt;/code&amp;gt; hinzugefügt. Ist der Parameter &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; oder nicht übergeben, so wird kein &amp;lt;code&amp;gt;class&amp;lt;/code&amp;gt;-Attribut hinzugefügt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$html&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
|| Wenn &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039; mit einem Iconnamen aus dem FHEMWEB-Iconpool übereinstimmt, wird der HTML-Code zum Einbinden des Icons zurückgegeben.&lt;br /&gt;
Mit nachgestellter Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname@color&amp;gt;&#039;&amp;lt;/code&amp;gt;, wird das Icon entsprechend eingefärbt.&lt;br /&gt;
Entspricht die Zeichenkette keinem Iconnamen, wird die Zeichenkette zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage(&#039;fts_shutter_10@red&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_directNotify ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify($filter, $fhemweb_instance, $javascript, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_directNotify führt Javascript auf einer longpoll FHEMWEB-Instanz aus, ohne Events zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$filter&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die mit &amp;quot;FILTER=&amp;quot; beginnt &amp;lt;code&amp;gt;&amp;quot;FILTER=&amp;lt;filter expression&amp;gt;&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$fhemweb_instance&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine longpoll FHEMWEB-Instanz &amp;lt;code&amp;gt;&amp;quot;#FHEMWEB:&amp;lt;FHEMWEB devicename&amp;gt;&amp;quot;&amp;lt;/code&amp;gt; angibt.&amp;lt;br&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$javascript&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die ein Javascript angibt.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;&amp;quot;&amp;quot;&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, leer oder (noch zu präzisieren).&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;location.reload(&#039;true&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* siehe auch {{Link2Forum|Topic=48736|Message=404497|LinkText=diesen Forumbeitrag}} und {{Link2Forum|Topic=134208|LinkText=dieses Forumthema}}&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Client (pgm2) Javascript-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_root&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| FHEM-Ursprungsverzeichnis, steht auch im head-Element als Attribut &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_version&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Ein assoziatives Array, das die Versionszeichenketten der geladenen Javascript-Dateien enthält.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== FW_cmd ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(arg, callback);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_cmd führt einen Befehl auf dem FHEMWEB-Server aus.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;arg&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die einem Befehl an den Server entspricht.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn der auf dem FHEM-Server ausgeführte Befehl einen Rückgabewert enthält, der weiter verarbeitet werden soll.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(FW_root+&#039;?cmd={FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;amp;XHR=1&#039;, function(data){FW_okDialog(data)});&amp;lt;/syntaxhighlight&amp;gt; Das Beispiel holt sich vom Server den HTML-Code des Icon &amp;quot;fts_shutter_10&amp;quot; und zeigt das Icon in einem Popup-Fenster an. Der vom Server auszuführende Befehl lautet &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; inline&amp;gt;{FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;lt;/syntaxhighlight&amp;gt;. An die Callback-Funktion &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; inline&amp;gt;function(data){FW_okDialog(data)}&amp;lt;/syntaxhighlight&amp;gt; wird der Rückgabewert vom Server als Parameter übergeben und steht als Variable &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
* Will man mehrere Perl-Kommandos senden, kann es hilfreich sein, die Daten vor der Übertragung BASE64 zu codieren. Im Kern nutzt man eine Wrapperfunktion für FW_cmd:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function FW_cmd_perlcode(arg, callback, rep) {&lt;br /&gt;
  let perlcode = &amp;quot;eval(MIME::Base64::decode_base64(&#039;&amp;quot; +btoa(arg)+ &amp;quot;&#039;))&amp;quot;;&lt;br /&gt;
  return FW_cmd(FW_root + &#039;?cmd={&#039; + perlcode + &#039;}&amp;amp;XHR=1&#039;, callback, rep);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Weitere Details findet man in {{Link2Forum|Topic=141230|diesem Forenthema}}.&lt;br /&gt;
&lt;br /&gt;
=== FW_errmsg ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(text, timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_errmsg zeigt eine Meldung im FHEMWEB-Client (Browser) an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Anzeigedauer der Zeichenkette in Millisekunden.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_okDialog ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(txt, parent, removeFn);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_okDialog öffnet im FHEMWEB-Client (Browser) ein modales Popup-Fenster.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die im Popup-Fenster angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;parent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Objekt zur Verankerung des Popup-Fensters, default ist das body-Element. Eine FHEM-Widget-Schnittstelle wird interpretiert.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;returnFn&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die beim Schliessen des Popup-Fensters ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;#!&amp;quot; onclick=&amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;&amp;gt;Hello&amp;lt;/a&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll das Popup-Fenster aus einem Hyperlink heraus aufgerufen werden, dann ist darauf zu achten, dass ein ungültiger Anchor Tag verwendet wird. In diesem Beispiel &amp;quot;!&amp;quot;. Dadurch wird ein unkontrolliertes Scrollen der Webseite effektiv verhindert und der Dialog erscheint genau dort wo man ihn haben möchte.&lt;br /&gt;
&lt;br /&gt;
=== loadScript ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(scriptname, callback, force);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
loadScript läd im FHEMWEB-Client (Browser) ein Javascript nach.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;scriptname&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die den Sriptnamen enthält.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn das Script geladen wurde.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;force&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Flag, wenn gesetzt, wird das Laden erzwungen.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(&amp;quot;pgm2/jquery.knob.min.js&amp;quot;, function(){&amp;lt;Javascript-Code&amp;gt;});&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== log ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(text);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
log zeigt einen Logeintrag in der Konsole des Browsers an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(&#039;Hello world!&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
* [[DevelopmentFHEMWEB|Development FHEMWEB (englischsprachig)]]&lt;br /&gt;
* [[DevelopmentModuleAPI]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=HttpUtils&amp;diff=40499</id>
		<title>HttpUtils</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=HttpUtils&amp;diff=40499"/>
		<updated>2025-11-22T15:45:17Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* Beispiel #2 eines Snippets um eine Datei herunterzuladen und lokal zu speichern */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Hilfsfunktionen für HTTP-Zugriffe&lt;br /&gt;
|ModType=u&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=HttpUtils.pm&lt;br /&gt;
|ModOwner=rudolfkoenig ({{Link2FU|8|Forum}} / [[Benutzer Diskussion:Rudolfkoenig|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Das Modul [[HttpUtils]](.pm) ist sowohl für Modulentwickler als auch für Endanwender gedacht, um Daten via HTTP auszutauschen. Es stellt dabei eine Reihe von Funktionen zur Verfügung und wird beispielsweise vom Modul [[HTTPMOD]] intensiv genutzt.&lt;br /&gt;
&lt;br /&gt;
== Primäre Funktionen (HTTP) ==&lt;br /&gt;
Es ist zu beachten, dass bei den Funktionen&lt;br /&gt;
* &amp;lt;code&amp;gt;GetHttpFile()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;GetFileFromURL&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;GetFileFromURLQuiet&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;HttpUtils_BlockingGet&amp;lt;/code&amp;gt;&lt;br /&gt;
ein sogenannter &amp;quot;blockierender&amp;quot; Aufruf durchgeführt wird. Das bedeutet, dass FHEM bei einem Aufruf einer dieser Funktionen solange wartet und dabei absolut nichts macht, bis die Antwort vom HTTP-Server eintrifft und die Funktion damit beendet ist. Das kann bei Verbindungsproblemen evtl. dazu führen, dass FHEM für die gesamte Wartezeit (Timeout) steht und nichts verarbeitet. Problematisch ist das gerade bei Anwendungen oder Hardware, die eine zeitnahe Reaktion von FHEM erwarten (z.B. HomeMatic-Geräte). In der Zeit, in der auf eine HTTP Antwort gewartet wird, steht FHEM dabei komplett. &lt;br /&gt;
&lt;br /&gt;
Es wird daher empfohlen, die Funktionen so sparsam wie möglich zu verwenden und die Timeouts so niedrig wie möglich zu halten, um ein längeres Einfrieren von FHEM möglichst zu vermeiden.&lt;br /&gt;
&lt;br /&gt;
Um eine Blockierung zu vermeiden wird generell die Verwendung von&lt;br /&gt;
:&amp;lt;code&amp;gt;HttpUtils_NonblockingGet&amp;lt;/code&amp;gt;&lt;br /&gt;
empfohlen. Diese führt den HTTP-Request asynchron durch, wodurch ein Blockieren von FHEM verhindert wird. Wie das genau funktioniert, wird in dem entsprechenden Kapitel beschrieben.&lt;br /&gt;
&lt;br /&gt;
=== GetHttpFile ===&lt;br /&gt;
Die Funktion GetHttpFile ist die denkbar einfachste Variante um eine URL aufzurufen. &lt;br /&gt;
:&amp;lt;code&amp;gt;GetHttpFile($server, $file)&amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$server&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Der DNS-Name oder die IP-Adresse des HTTP-Servers&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;www.myhost.com&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;192.168.0.10&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$file&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Datei, welche auf dem HTTP-Server aufgerufen werden soll.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &#039;&#039;/&#039;&#039;&lt;br /&gt;
* &#039;&#039;/index.html&#039;&#039;&lt;br /&gt;
* &#039;&#039;/directory/image.jpg&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Funktionsergebnis ist der Inhalt der aufgerufenen Seite in Form einer Zeichenkette.&lt;br /&gt;
&lt;br /&gt;
=== GetFileFromURL ===&lt;br /&gt;
Die Funktion GetFileFromURL ruft die HTTP-URL auf und gibt als Funktionsergebnis den Seiteninhalt zurück. Im Gegensatz zu GetHttpFile beinhaltet GetFileFromURL einige Zusatzoptionen in Form von Funktionsparametern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;GetFileFromURL($url, &#039;&#039;[$timeout], [$data], [$noshutdown], [$loglevel]&#039;&#039;)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$url&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die HTTP-URL, welche aufgerufen werden soll. Diese kann optional Usernamen, Passwort und einen Port enthalten. Sowohl HTTP als auch HTTPS wird hierbei unterstützt.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com/directory/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;https://www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com:8080/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://user:password@www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG:&#039;&#039;&#039; Falls ein Username sowie Passwort übergeben werden, so müssen diese vorher jeweils mittels [[#urlEncode|urlEncode()]] in URL-kompatible Form umgewandelt werden um Probleme mit evtl. enthaltenen Sonderzeichen zu vermeiden. &lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die maximale Dauer in Sekunden für die HTTP-Anfrage&lt;br /&gt;
&lt;br /&gt;
Beispiel: 5 &#039;&#039;(Sekunden)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn man Daten via HTTP-POST übertragen möchte, so kann man die Nutzdaten über $data übergeben. Die Daten werden dabei als Formulardaten übertragen. Wenn man den Content-Type beeinflussen oder mehrere Formular-Felder senden möchte, sollte man zur Funktion HttpUtils_BlockingGet oder HttpUtils_NonblockingGet greifen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$noshutdown&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn $noshutdown auf 1 gesetzt ist, wird dem HTTP-Server nicht implizit mitgeteilt, dass die Verbindung nach dem Request geschlossen werden soll. Viele Webserver schließen in solch einem Fall die Verbindung, bevor sie die Antwort senden. Bei 0 wird dem Webserver mitgeteilt, dass der Sendevorgang beendet ist und nun die Antwort abgewartet wird.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$loglevel&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Das [[verbose|Loglevel]], in dem sämtliche Logmeldungen zu dieser HTTP-Abfrage erzeugt werden sollen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Funktionsergebnis ist der Inhalt der aufgerufenen Seite in Form eines Strings.&lt;br /&gt;
&lt;br /&gt;
=== GetFileFromURLQuiet ===&lt;br /&gt;
Diese Funktion funktioniert ähnlich wie GetFileFromURL. Allerdings wird die tatsächliche URL in allen erzeugten Log-Meldungen unkenntlich gemacht um z.B. Zugangsdaten nicht preiszugeben. Die aufgerufene Seite wird ebenfalls als Funktionsergebnis zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;GetFileFromURLQuiet($url, &#039;&#039;[$timeout], [$data], [$noshutdown], [$loglevel]&#039;&#039;)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$url&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die HTTP-URL, welche aufgerufen werden soll. Diese kann optional Usernamen, Passwort und einen Port enthalten. Sowohl HTTP als auch HTTPS wird hierbei unterstützt.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com/directory/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;https://www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com:8080/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://user:password@www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG:&#039;&#039;&#039; Falls ein Username sowie Passwort übergeben werden, so müssen diese vorher jeweils mittels [[#urlEncode|urlEncode()]] in URL-kompatible Form umgewandelt werden um Probleme mit evtl. enthaltenen Sonderzeichen zu vermeiden. &lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die maximale Dauer in Sekunden für die HTTP-Anfrage&lt;br /&gt;
&lt;br /&gt;
Beispiel: 5 &#039;&#039;(Sekunden)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn man Daten via HTTP-POST übertragen möchte, so kann man die Nutzdaten über &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; übergeben. Die Daten werden dabei als Formulardaten übertragen. Wenn man den Content-Type beeinflussen möchte, oder mehrere Formular-Felder senden möchte, sollte man zur Funktion HttpUtils_BlockingGet oder HttpUtils_NonblockingGet greifen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$noshutdown&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn $noshutdown auf 1 gesetzt ist, wird dem HTTP-Server nicht implizit mitgeteilt, dass die Verbindung nach dem Request geschlossen werden soll. Viele Webserver schließen in solch einem Fall die Verbindung bevor, sie die Antwort senden. Bei 0 wird dem Webserver mitgeteilt, dass der Sendevorgang beendet ist und nun die Antwort abgewartet wird.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$loglevel&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Das Loglevel, in dem sämtliche Logmeldungen zu dieser HTTP-Abfrage erzeugt werden sollen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Funktionsergebnis ist der Inhalt der aufgerufenen Seite in Form eines Strings.&lt;br /&gt;
&lt;br /&gt;
=== HttpUtils_BlockingGet ===&lt;br /&gt;
Wenn die bisher genannten Funktionen nicht ausreichen um die gewünschte Abfrage durchzuführen, so kann man diese Funktion verwenden. Aufgrund zahlreicher Parameter ermöglicht sie viele Anpassungsmöglichkeiten. Diese Funktion hat dabei nicht wie üblich eine Liste an Funktionsparametern, sondern lediglich einen Parameter, welcher eine Hashreferenz mit allen Funktionsparametern darstellt. Dieser Hash enthält sämtliche Parameter inkl. Werten. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;HttpUtils_BlockingGet($param)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Parameter $param ist eine Referenz auf eine Hash-Struktur, welche die einzelnen Parameter enthält. Der Hash $param kann folgende Optionen beinhalten:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:175px&amp;quot; | Parameter !! style=&amp;quot;width:auto&amp;quot; | Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{url}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
 || Die HTTP-URL, welche aufgerufen werden soll. Diese kann optional Usernamen, Passwort und einen Port enthalten. Sowohl HTTP als auch HTTPS wird hierbei unterstützt.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com/directory/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;https://www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com:8080/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://user:password@www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG:&#039;&#039;&#039; Alternativ können die Zugangsdaten auch in &amp;lt;code&amp;gt;$param-&amp;gt;{user}&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$param-&amp;gt;{pwd}&amp;lt;/code&amp;gt; übergeben werden. Falls  Username und Passwort in der URL übergeben werden, so müssen diese vorher jeweils mittels [[#urlEncode|urlEncode()]] in URL-kompatible Form umgewandelt werden um Probleme mit evtl. enthaltenen Sonderzeichen (z.B. &amp;quot;:&amp;quot; und &amp;quot;@&amp;quot;) zu vermeiden, da sonst die URL nicht mehr einer validen Syntax entspricht. &lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; |&#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{timeout}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die maximale Dauer in Sekunden bis der Server eine Antwort liefern muss. Andernfalls wird der Request mit einer Fehlermeldung abgebrochen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: 5 &#039;&#039;(Sekunden)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; |&#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{data}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn man Daten via HTTP-POST übertragen möchte, so kann man die Nutzdaten über &amp;lt;code&amp;gt;$param-&amp;gt;{data}&amp;lt;/code&amp;gt; übergeben. Die Daten werden dabei als Formulardaten übertragen. Die Daten können dabei auf zwei Arten übergeben werden:&lt;br /&gt;
&lt;br /&gt;
1. Daten als Zeichenkette:&lt;br /&gt;
:* Die Daten werden komplett als gesamte Zeichenkette in &amp;lt;code&amp;gt;$param-&amp;gt;{data}&amp;lt;/code&amp;gt; abgelegt (z.B.: &amp;lt;code&amp;gt;$param-&amp;gt;{data} = &amp;quot;Jede Menge tolle Daten&amp;quot;&amp;lt;/code&amp;gt;).&lt;br /&gt;
2. Daten als Hash-Struktur:&lt;br /&gt;
:* Die Daten werden als Hash mit Key-Value-Pairs übergeben (z.B.: &amp;lt;code&amp;gt;$param-&amp;gt;{data}{field1} = &amp;quot;value1&amp;quot;, $param{data}{field2} = &amp;quot;value2&amp;quot;, ...&amp;lt;/code&amp;gt; ). Die Daten werden dann als Formulardaten mit mehrfachen Datenfeldern übertragen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{user}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Im Falle einer notwendigen HTTP-Authentifizierung, der zu benutzende Username um sich gegenüber dem Server zu identifizieren. Alternativ kann der Username und das Passwort auch direkt in der URL mitgegeben werden (siehe jedoch &amp;lt;code&amp;gt;$param-&amp;gt;{hideurl}&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{pwd}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Im Falle einer notwendigen HTTP-Authentifizierung, das zu benutzende Passwort um sich gegenüber dem Server zu identifizieren. Alternativ kann der Username und das Passwort auch direkt in der URL mitgegeben werden (siehe jedoch &amp;lt;code&amp;gt;$param-&amp;gt;{hideurl}&amp;lt;/code&amp;gt;).&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{noshutdown}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Wenn &amp;lt;code&amp;gt;$param-&amp;gt;{noshutdown}&amp;lt;/code&amp;gt; auf 1 gesetzt ist, wird dem HTTP-Server nicht implizit mitgeteilt, dass die Verbindung nach dem Request geschlossen werden soll. Viele Webserver schließen in solch einem Fall die Verbindung, bevor sie die Antwort senden. Bei 0 wird dem Webserver mitgeteilt, dass der Sendevorgang beendet ist und nun die Antwort abgewartet wird.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{loglevel}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Das Loglevel, in dem sämtliche Logmeldungen zu dieser HTTP-Abfrage erzeugt werden sollen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{hideurl}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn dieser Parameter den Wert 1 trägt, wird die gesamte URL in sämtlichen Log-Ausgaben unkenntlich gemacht. Dies notwendig, wenn z.B. Zugangsdaten direkt in der URL angegeben wurden, oder die URL ein geheimes Token oder andere schützenswerte Informationen enthält.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{ignoreredirects}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn dieser Parameter den Wert 1 trägt, werden Umleitungen durch den Server ignoriert und der Request beendet. Dies kann erforderlich sein um evtl. Cookies aus der Antwort, welche eine Umleitung enthält aus dem HTTP Header zu extrahieren um diese im nächsten Request weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{method}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Die HTTP-Methode, welche zur Abfrage verwendet werden soll. Sofern keine Daten übertragen werden ist dies standardmäßig &amp;quot;GET&amp;quot;, ansonsten &amp;quot;POST&amp;quot;. Es können aber auch andere Methoden verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &amp;quot;GET&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{keepalive}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Wenn dieser Parameter auf 1 gesetzt ist, wird dem Server der Wunsch mitgeteilt, die Verbindung offen zu lassen für weitere Anfragen. Sobald die Antwort auf den jeweiligen Request eintrifft, bleibt die TCP-Verbindung bestehen (sofern der Server dies unterstützt). Anschließend kann man den Parameter-Hash mit einer neuen URL und Optionen füllen. Der Parameter &amp;quot;keepalive&amp;quot; sollte dabei weiterhin gesetzt bleiben, sofern die Verbindung auch weiterhin möglichst erhalten bleiben soll. &lt;br /&gt;
&lt;br /&gt;
Um eine offene Verbindung endgültig zu schließen, muss die Funktion [[HttpUtils#HttpUtils_Close|HttpUtils_Close]] aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{header}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eigene HTTP-Header-Zeilen können über diesen Parameter eingebracht werden. Er kann dazu genutzt werden um z.B. den Content-Type festzulegen, oder einfach nur zusätzliche Header-Felder zu setzen. Es gibt zwei Möglichkeiten, diesen Parameter zu befüllen.&lt;br /&gt;
&lt;br /&gt;
# Als &#039;&#039;&#039;Zeichenkette&#039;&#039;&#039;. Mehrere Header-Zeilen müssen dabei mit &amp;quot;\r\n&amp;quot; getrennt werden.&lt;br /&gt;
# Als &#039;&#039;&#039;Hash-Struktur&#039;&#039;&#039;. Hierbei findet eine Zuordnung von Header-Bezeichnung zum Wert als Key-Value-Pair statt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;User-Agent: Mozilla/1.22&#039;&#039;&#039;&amp;lt;u&amp;gt;\r\n&amp;lt;/u&amp;gt;&#039;&#039;&#039;Content-Type: application/xml&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;Content-Type: application/xml&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;{ &amp;quot;User-Agent&amp;quot; =&amp;gt;  &amp;quot;Mozilla/1.22&amp;quot;, &amp;quot;Content-Type&amp;quot; =&amp;gt; &amp;quot;application/xml&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;{ &amp;quot;Content-Type&amp;quot; =&amp;gt; &amp;quot;application/xml&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{sslargs}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eigene SSL-Optionen können über diesen Parameter eingebracht werden. Er kann dazu genutzt werden um z.B. die SSL-Zertifikats Verifikation abzuschalten. Die SSL-Optionen müssen als eigene Hash-Referenz übergeben werden. Eine Liste aller möglichen Optionen findet man in der Perl-Dokumentation zu [http://search.cpan.org/~sullr/IO-Socket-SSL-2.016/lib/IO/Socket/SSL.pod#Description_Of_Methods IO::Socket::SSL].&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{sslargs} = { SSL_verify_mode =&amp;gt; 0, sslOpt2 =&amp;gt; &#039;sslVal2&#039; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;{ }&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{httpversion}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die HTTP-Version, welche zur Abfrage verwendet werden soll. Standardmäßig werden alle Abfragen mit HTTP/1.0 durchgeführt. Falls es jedoch notwendig ist HTTP/1.1 zu verwenden, so sollte &amp;lt;code&amp;gt;$param-&amp;gt;{httpversion}&amp;lt;/code&amp;gt; auf &amp;quot;1.1&amp;quot; gesetzt werden. Bei Version 1.1 wird automatisch der Header &amp;quot;&amp;lt;code&amp;gt;Connection: close&amp;lt;/code&amp;gt;&amp;quot; implizit mitgesendet.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &amp;quot;1.0&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{digest}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn dieser Parameter den Wert 1 trägt, wird bei vorhandenen Authentifizierungsdaten (Username+Passwort) eine Authentifizierung via Digest-Verfahren nach RFC 2617&amp;lt;ref name=&amp;quot;rfc2617&amp;quot;&amp;gt;RFC 2617 - HTTP Authentication: Basic and Digest Access Authentication&amp;lt;/ref&amp;gt; erwartet. Die Anmeldedaten werden dann nur verwendet, wenn dieser explizit eine HTTP Digest Authentifizierung einleitet. Ist dieser Parameter nicht gesetzt (Wert: 0), wird bei vorhandenen Authentifizierungsdaten immer eine HTTP Basic&amp;lt;ref name=&amp;quot;rfc2617&amp;quot; /&amp;gt; Authentifizierung im Request mitgeschickt.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{compress}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn dieser Parameter den Wert 1 trägt, wird dem Server die Verwendung von Komprimierung in Form GZIP oder alternativ Deflate&amp;lt;ref&amp;gt;RFC 1951 - DEFLATE Compressed Data Format Specification version 1.3&amp;lt;/ref&amp;gt; zur Übertragung der Antwort ermöglicht. Die komprimierte Antwort wird dabei direkt durch HttpUtils.pm wieder dekomprimiert. Wird dieser Parameter auf den Wert 0 gesetzt, so wird keine Komrpimierung ermöglicht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG&#039;&#039;&#039;: Die Verwendung von Komprimierung in HttpUtils.pm kann mit dem globalen Attribut &amp;quot;httpcompress&amp;quot; (Standardwert: 1) durch setzen auf 0 für die gesamte FHEM Installation durch den Nutzer deaktiviert werden. In diesem Fall ist der Parameter &amp;lt;code&amp;gt;$param-&amp;gt;{compress} = 1&amp;lt;/code&amp;gt; wirkungslos.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Als Rückgabewert von HttpUtils_BlockingGet wird ein Array mit zwei Rückgabewerten zurückgegeben:&lt;br /&gt;
:&amp;lt;code&amp;gt;($err, $data) = HttpUtils_BlockingGet( … )&amp;lt;/code&amp;gt;&lt;br /&gt;
Diese zwei Rückgabewerte haben folgende Bedeutung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$err&amp;lt;/code&amp;gt;&#039;&#039;&#039;|| Falls beim Aufruf der URL ein Fehler aufgetreten ist (z.B. Server nicht erreichbar oder Verbindungstimeout), dann ist dieser Wert mit einer Fehlermeldung gefüllt. &lt;br /&gt;
&lt;br /&gt;
Wenn kein Fehler aufgetreten ist, ist dieser Wert mit einem Leerstring gefüllt (&amp;lt;code&amp;gt;$err = &amp;quot;&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt;&#039;&#039;&#039;|| Die Ergebnisdaten, welche der HTTP-Server zurückgeliefert hat. Die Daten werden als Klartext in Form eines gesamten Strings zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Falls ein Fehler aufgetreten ist, ist dieser Wert mit einem Leersting gefüllt (&amp;lt;code&amp;gt;$data = &amp;quot;&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== HttpUtils_NonblockingGet ===&lt;br /&gt;
{{Randnotiz|RNText=Die Funktion HttpUtils_NonblockingGet ist nicht komplett durchgehend &amp;quot;non-blocking&amp;quot;. DNS-Abfragen sind weiterhin blockierend. Insbesondere wenn der DNS-Name nicht aufgelöst werden kann.}}&lt;br /&gt;
Diese Funktion arbeitet ähnlich wie HttpUtils_BlockingGet. Allerdings wird das Ergebnis nicht als Funktionsergebnis zurückgegeben. Die Funktion HttpUtils_NonblockingGet initiiert den Verbindungsaufbau und übergibt alles weitere an FHEM interne Routinen. Sobald eine Antwort vom HTTP-Server eintrifft, wird eine Callback-Funktion mit verschiedenen Parametern (unter anderem auch das Ergebnis) aufgerufen, um die Antwort entgegenzunehmen und weiter zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Der Aufruf ist daher ähnlich zu HttpUtils_BlockingGet mit nur einem Parameter-Hash:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;HttpUtils_NonblockingGet($param)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:175px&amp;quot; | Parameter !! style=&amp;quot;width:auto&amp;quot; | Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | &#039;&#039;&#039;&#039;&#039;&amp;lt;br&amp;gt;Alle Hash-Parameter, welche für HttpUtils_BlockingGet gelten, sind auch für HttpUtils_NonblockingGet gültig&#039;&#039;&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{callback}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Funktion (oder eine Referenz auf eine Funktion), welche die Ergebnisdaten entgegennimmt und die Antwort entsprechend weiterverarbeitet. Die Callback-Funktion muss dabei 3 Parameter erwarten. Die Funktionsparameter der Callback-Funktion werden im nachfolgenden Abschnitt näher erläutert.&lt;br /&gt;
&lt;br /&gt;
Beispiel: &lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{callback} = \&amp;amp;MyCallbackFn&amp;lt;/code&amp;gt; — &#039;&#039;(Referenzzeiger auf Funktionsname)&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{callback} = sub(){ … }&amp;lt;/code&amp;gt; —  &#039;&#039;(direkte Funktionsdefinition)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top;white-space:nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{incrementalTimeout}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Sofern gesetzt (Wert: &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;), wird der Timeout nach jedem Empfang von Teil-Daten wieder zurückgesetzt. So können größere Datenmengen von langsamen Sendern empfangen werden, ohne den Parameter &amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt; zu erhöhen und dadurch tatsächliche Verbindungsprobleme erst spät zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;Benutzerdefinierte Parameter&#039;&#039; &lt;br /&gt;
|| Es können im Hash weitere benutzerdefinierte Parameter gesetzt werden, welche evtl. in der Callback-Funktion benötigt werden, um die Antwort korrekt zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Zum Beispiel im Rahmen der Modul-Programmierung wäre das &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; der aktuellen Definition. Alle gesetzten Parameter sind in der Callback-Funktion wieder über &amp;lt;code&amp;gt;$param&amp;lt;/code&amp;gt; direkt abrufbar und können ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: &lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{hash} = $hash&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{command} = &amp;quot;on&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
Ein Funktionsrückgabewert von HttpUtils_NonblockingGet existiert nicht, da die eigentliche Rückgabe der Daten über die Callback-Funktion erfolgt. Die Callback-Funktion wird aufgerufen, sobdald der HTTP-Request abgeschlossen ist, oder ein Fehler aufgetreten ist. Der Funktionsaufruf erfolgt mit den folgenden Parametern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt; &#039;&#039;MyCallbackFn&#039;&#039; ( $param, $err, $data )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese 3 Parameter haben dabei folgende Bedeutung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Der Parameter-Hash, mit allen Argumenten die beim Aufruf der Funktion übergeben worden sind.&lt;br /&gt;
&lt;br /&gt;
Es ist möglich, dass der Parameter-Hash nach erfolgter Abfrage zusätzliche oder veränderte Elemente enthält:&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{redirects}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Die Anzahl an Umleitungen die erfolgte, bis die Anfrage beantwortet wurde.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{url}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Die URL, die nach erfolgter Umleitung die Anfrage beantwortet hat.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{protocol}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Das verwendete Protokoll, welches zur Abfrage verwendet wurde (&amp;quot;http&amp;quot; oder &amp;quot;https&amp;quot;).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{path}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Der Pfad, welcher auf dem HTTP-Server angefragt wurde.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{host}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Der Name oder die IP-Adresse des HTTP-Servers.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{httpheader}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Der gesamte HTTP Header, welcher der Server bei der letzten Antwort zurücklieferte.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{code}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Der HTTP-Statuscode, mit dem die Anfrage vom Server beantwortet wurde.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{addr}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Die HTTP-URL ohne Pfad und evtl. Authentifizerungsinformationen des HTTP-Servers (z.B. &amp;quot;&amp;lt;nowiki&amp;gt;http://myserver.com:8080&amp;lt;/nowiki&amp;gt;&amp;quot;).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{auth}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Ein Flag (0/1) ob Zugangsdaten für den Request zur Verfügung standen (durch Angabe in der URL oder direkt in &amp;lt;code&amp;gt;$param&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$err&amp;lt;/code&amp;gt;&#039;&#039;&#039;|| Falls beim Aufruf der URL ein Fehler aufgetreten ist (z.B. Server nicht erreichbar oder Verbindungstimeout), dann ist dieser Wert mit einer Fehlermeldung gefüllt. &lt;br /&gt;
&lt;br /&gt;
Wenn kein Fehler aufgetreten ist, ist dieser Wert mit einem Leerstring gefüllt (&amp;lt;code&amp;gt;$err = &amp;quot;&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt;&#039;&#039;&#039;|| Die Ergebnisdaten, welche der HTTP-Server zurückgeliefert hat. Die Daten werden als Klartext in Form eines gesamten Strings zurückgegeben. Es handelt sich hierbei ausschließlich um den HTTP-Body. &lt;br /&gt;
&lt;br /&gt;
Falls ein Fehler aufgetreten ist, ist dieser Wert mit einem Leersting gefüllt (&amp;lt;code&amp;gt;$data = &amp;quot;&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Callback-Funktion kann nun die Daten aus der HTTP-Antwort verarbeiten oder bei Fehlern entsprechende Log-Meldungen ausgeben.&lt;br /&gt;
&lt;br /&gt;
==== Beispiel #1 für HttpUtils_NonblockingGet() für Modulprogrammierer ====&lt;br /&gt;
Das folgende Beispiel soll eine Hilfestellung für eigene Anwendungen geben&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;use HttpUtils;&lt;br /&gt;
sub X_PerformHttpRequest($)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash, $def) = @_;&lt;br /&gt;
    my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
    my $param = {&lt;br /&gt;
                    url        =&amp;gt; &amp;quot;http://www.foo.de/&amp;quot;,&lt;br /&gt;
                    timeout    =&amp;gt; 5,&lt;br /&gt;
                    hash       =&amp;gt; $hash,                                                                                 # Muss gesetzt werden, damit die Callback funktion wieder $hash hat&lt;br /&gt;
                    method     =&amp;gt; &amp;quot;GET&amp;quot;,                                                                                 # Lesen von Inhalten&lt;br /&gt;
                    header     =&amp;gt; &amp;quot;User-Agent: TeleHeater/2.2.3\r\nAccept: application/json&amp;quot;,                            # Den Header gemäß abzufragender Daten ändern&lt;br /&gt;
                    callback   =&amp;gt; \&amp;amp;X_ParseHttpResponse                                                                  # Diese Funktion soll das Ergebnis dieser HTTP Anfrage bearbeiten&lt;br /&gt;
                };&lt;br /&gt;
&lt;br /&gt;
    HttpUtils_NonblockingGet($param);                                                                                    # Starten der HTTP Abfrage. Es gibt keinen Return-Code. &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub X_ParseHttpResponse($)&lt;br /&gt;
{&lt;br /&gt;
    my ($param, $err, $data) = @_;&lt;br /&gt;
    my $hash = $param-&amp;gt;{hash};&lt;br /&gt;
    my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
&lt;br /&gt;
    if($err ne &amp;quot;&amp;quot;)                                                                                                      # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist&lt;br /&gt;
    {&lt;br /&gt;
        Log3 $name, 3, &amp;quot;error while requesting &amp;quot;.$param-&amp;gt;{url}.&amp;quot; - $err&amp;quot;;                                               # Eintrag fürs Log&lt;br /&gt;
        readingsSingleUpdate($hash, &amp;quot;fullResponse&amp;quot;, &amp;quot;ERROR&amp;quot;, 0);                                                        # Readings erzeugen&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    elsif($data ne &amp;quot;&amp;quot;)                                                                                                  # wenn die Abfrage erfolgreich war ($data enthält die Ergebnisdaten des HTTP Aufrufes)&lt;br /&gt;
    {&lt;br /&gt;
        Log3 $name, 3, &amp;quot;url &amp;quot;.$param-&amp;gt;{url}.&amp;quot; returned: $data&amp;quot;;                                                         # Eintrag fürs Log&lt;br /&gt;
&lt;br /&gt;
        # An dieser Stelle die Antwort parsen / verarbeiten mit $data&lt;br /&gt;
&lt;br /&gt;
        readingsSingleUpdate($hash, &amp;quot;fullResponse&amp;quot;, $data, 0);                                                          # Readings erzeugen&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    # Damit ist die Abfrage zuende.&lt;br /&gt;
    # Evtl. einen InternalTimer neu schedulen&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Beispiel #2 eines Snippets um eine Datei herunterzuladen und lokal zu speichern ====&lt;br /&gt;
Dieses vollständige Beispiel eines Device verwendet &amp;lt;code&amp;gt;HttpUtils_NonblockingGet()&amp;lt;/code&amp;gt; um eine Datei herunterzuladen und im lokalem Dateisystem mit den Rechten des FHEM Nutzers zu speichern.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod MyDownloader dummy&lt;br /&gt;
attr MyDownloader userattr FilePath&lt;br /&gt;
attr MyDownloader comment &#039;\&lt;br /&gt;
Beispiel:\&lt;br /&gt;
set MyDownloader job Filepath=&amp;quot;/opt/fhem/www/Dateiname.bin&amp;quot; https://example.com/datei.bin\&lt;br /&gt;
&#039;&lt;br /&gt;
attr MyDownloader readingList job&lt;br /&gt;
attr MyDownloader setList job&lt;br /&gt;
attr MyDownloader userReadings result:job:.* {\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	my $job = ReadingsVal($name, &amp;quot;job&amp;quot;, &#039;https://fhem.de/www/images/default/fhemicon.png&#039;);;\&lt;br /&gt;
	my $path = AttrVal($name, &amp;quot;Filepath&amp;quot;, &amp;quot;/opt/fhem/www/download.bin&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	if ($job =~ s/(?:Filepath|Dateipfad)=&amp;quot;(.*?)&amp;quot;//) {\&lt;br /&gt;
		$path = $1;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	my $fnFct = sub {\&lt;br /&gt;
		my ($param, $err, $data) = @_;;\&lt;br /&gt;
		\&lt;br /&gt;
		my $len = $data ? length($data) : 0;;\&lt;br /&gt;
		my $error = $err ? $err : FileWrite($path, $data);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsBeginUpdate($hash);;\&lt;br /&gt;
		readingsBulkUpdate($hash, &amp;quot;busy&amp;quot;, 0, 1);;\&lt;br /&gt;
		readingsBulkUpdate($hash, &amp;quot;result&amp;quot;, &amp;quot;saved $len Bytes&amp;quot;, 1) if !$error;;\&lt;br /&gt;
		readingsBulkUpdate($hash, &amp;quot;result&amp;quot;, &amp;quot;error ($error, $err)&amp;quot;, 1) if $error;;\&lt;br /&gt;
		readingsEndUpdate($hash, 1);;\&lt;br /&gt;
	};;\&lt;br /&gt;
	\&lt;br /&gt;
	if ( ReadingsVal($name, &amp;quot;busy&amp;quot;, 0) == 0 ) {\&lt;br /&gt;
		readingsBulkUpdate($hash, &amp;quot;busy&amp;quot;, 1, 1);;\&lt;br /&gt;
		$job =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
		my $param = {\&lt;br /&gt;
                    url        =&amp;gt; $job,\&lt;br /&gt;
                    timeout    =&amp;gt; 10,\&lt;br /&gt;
                    hash       =&amp;gt; $hash,\&lt;br /&gt;
                    method     =&amp;gt; &amp;quot;GET&amp;quot;,\&lt;br /&gt;
                    callback   =&amp;gt; $fnFct\&lt;br /&gt;
                };;\&lt;br /&gt;
		HttpUtils_NonblockingGet($param);;\&lt;br /&gt;
		return &amp;quot;download job started&amp;quot;;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	return;;\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Zugehöriger Forumspost: [https://forum.fhem.de/index.php?topic=143117.msg1352221#msg1352221 📂 Datei herunterladen, Downloader, HttpUtils_NonblockingGet]&lt;br /&gt;
&lt;br /&gt;
=== HttpUtils_Close ===&lt;br /&gt;
&lt;br /&gt;
Für den Abbruch von offenen Verbindungen und noch laufenden NonBockingGet-Aufrufen gibt es die Funktion HttpUtils_Close. Diese kann z.B. beim Löschen eines Devices oder Herunterfahren des Servers aufgerufen werden, um bestehende Verbindungen zu schliessen.&lt;br /&gt;
&lt;br /&gt;
Wenn man den Parameter &amp;quot;keepalive&amp;quot; beim Aufruf von HttpUtils_NonBlockingGet/HttpUtils_BlockingGet gesetzt hat, muss man HttpUtils_Close aufrufen um die Verbindung tatsächlich zu schließen, da diese noch durch den Server offen gehalten werden kann.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;HttpUtils_Close($param)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:175px&amp;quot; | Parameter !! style=&amp;quot;width:auto&amp;quot; | Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Der Hash, der beim vorherigen Aufruf an HTTPUtils Funktionen (HttpUtils_NonblockingGet oder HttpUtils_BlockingGet) übergeben wurde&lt;br /&gt;
&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
== Hilfsfunktionen ==&lt;br /&gt;
&lt;br /&gt;
=== urlEncode ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$encoded = urlEncode($string);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Funktion urlEncode() wandelt die übergebene Zeichenkette &amp;lt;code&amp;gt;$string&amp;lt;/code&amp;gt; in eine URL-kompatible&amp;lt;ref name=&amp;quot;rfc3986-2.1&amp;quot;&amp;gt;[https://tools.ietf.org/html/rfc3986#section-2.1 RFC 3989] - Abschnitt 2.1 &amp;quot;Percent-Encoding&amp;quot;&amp;lt;/ref&amp;gt; Zeichenkette um. Der Rückgabewert &amp;lt;code&amp;gt;$encoded&amp;lt;/code&amp;gt; ist eine Zeichenkette welche innerhalb einer URL verwendet werden kann, bspw. als Wert eines GET-Parameters.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$string&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Zeichenkette, welche in eine URL-kompatible Form umgewandelt werden soll.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$encoded &amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die URL-kompatible Version der übergebenen Zeichenkette. Problematische Zeichen sind in diesem Wert durch ein Prozentzeichen gefolgt von der Hexadezimaldarstellung des Zeichens dargestellt.&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
* &amp;quot;a: b&amp;quot; =&amp;gt; &amp;quot;a%3a%20b&amp;quot;&lt;br /&gt;
* &amp;quot;a/b&amp;quot; =&amp;gt; &amp;quot;a%2fb&amp;quot;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== urlDecode ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$string = urlDecode($encoded);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Funktion urlDecode () normalisiert die übergebene Zeichenkette &amp;lt;code&amp;gt;$encoded&amp;lt;/code&amp;gt; und ersetzt dabei sämtliche Hexadezimaldarstellungen&amp;lt;ref name=&amp;quot;rfc3986-2.1&amp;quot; /&amp;gt; (eingeleitet mit einem Prozentzeichen) durch die entsprechenden Zeichen. Der Rückgabewert &amp;lt;code&amp;gt;$string&amp;lt;/code&amp;gt; ist eine Zeichenkette welche keinerlei Hexadezimaldarstellungen mehr aufweist.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$encoded&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, welche URL-kombatible Hexadezimaldarstellungen enthält.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$string&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die normale Version der übergebenen Zeichenkette. Sämtliche Hexadezimalddarstellungen sind in ihre entsprechenden Zeichen umgewandelt.&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
* &amp;quot;a%3a%20b&amp;quot; =&amp;gt; &amp;quot;a: b&amp;quot;&lt;br /&gt;
* &amp;quot;a%2fb&amp;quot; =&amp;gt; &amp;quot;a/b&amp;quot;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Referenzen ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=HttpUtils&amp;diff=40498</id>
		<title>HttpUtils</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=HttpUtils&amp;diff=40498"/>
		<updated>2025-11-22T15:42:12Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Weiteres Beispiel hinugefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Hilfsfunktionen für HTTP-Zugriffe&lt;br /&gt;
|ModType=u&lt;br /&gt;
|ModForumArea=Automatisierung&lt;br /&gt;
|ModTechName=HttpUtils.pm&lt;br /&gt;
|ModOwner=rudolfkoenig ({{Link2FU|8|Forum}} / [[Benutzer Diskussion:Rudolfkoenig|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Das Modul [[HttpUtils]](.pm) ist sowohl für Modulentwickler als auch für Endanwender gedacht, um Daten via HTTP auszutauschen. Es stellt dabei eine Reihe von Funktionen zur Verfügung und wird beispielsweise vom Modul [[HTTPMOD]] intensiv genutzt.&lt;br /&gt;
&lt;br /&gt;
== Primäre Funktionen (HTTP) ==&lt;br /&gt;
Es ist zu beachten, dass bei den Funktionen&lt;br /&gt;
* &amp;lt;code&amp;gt;GetHttpFile()&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;GetFileFromURL&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;GetFileFromURLQuiet&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;HttpUtils_BlockingGet&amp;lt;/code&amp;gt;&lt;br /&gt;
ein sogenannter &amp;quot;blockierender&amp;quot; Aufruf durchgeführt wird. Das bedeutet, dass FHEM bei einem Aufruf einer dieser Funktionen solange wartet und dabei absolut nichts macht, bis die Antwort vom HTTP-Server eintrifft und die Funktion damit beendet ist. Das kann bei Verbindungsproblemen evtl. dazu führen, dass FHEM für die gesamte Wartezeit (Timeout) steht und nichts verarbeitet. Problematisch ist das gerade bei Anwendungen oder Hardware, die eine zeitnahe Reaktion von FHEM erwarten (z.B. HomeMatic-Geräte). In der Zeit, in der auf eine HTTP Antwort gewartet wird, steht FHEM dabei komplett. &lt;br /&gt;
&lt;br /&gt;
Es wird daher empfohlen, die Funktionen so sparsam wie möglich zu verwenden und die Timeouts so niedrig wie möglich zu halten, um ein längeres Einfrieren von FHEM möglichst zu vermeiden.&lt;br /&gt;
&lt;br /&gt;
Um eine Blockierung zu vermeiden wird generell die Verwendung von&lt;br /&gt;
:&amp;lt;code&amp;gt;HttpUtils_NonblockingGet&amp;lt;/code&amp;gt;&lt;br /&gt;
empfohlen. Diese führt den HTTP-Request asynchron durch, wodurch ein Blockieren von FHEM verhindert wird. Wie das genau funktioniert, wird in dem entsprechenden Kapitel beschrieben.&lt;br /&gt;
&lt;br /&gt;
=== GetHttpFile ===&lt;br /&gt;
Die Funktion GetHttpFile ist die denkbar einfachste Variante um eine URL aufzurufen. &lt;br /&gt;
:&amp;lt;code&amp;gt;GetHttpFile($server, $file)&amp;lt;/code&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$server&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Der DNS-Name oder die IP-Adresse des HTTP-Servers&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;www.myhost.com&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;192.168.0.10&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$file&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Datei, welche auf dem HTTP-Server aufgerufen werden soll.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &#039;&#039;/&#039;&#039;&lt;br /&gt;
* &#039;&#039;/index.html&#039;&#039;&lt;br /&gt;
* &#039;&#039;/directory/image.jpg&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Funktionsergebnis ist der Inhalt der aufgerufenen Seite in Form einer Zeichenkette.&lt;br /&gt;
&lt;br /&gt;
=== GetFileFromURL ===&lt;br /&gt;
Die Funktion GetFileFromURL ruft die HTTP-URL auf und gibt als Funktionsergebnis den Seiteninhalt zurück. Im Gegensatz zu GetHttpFile beinhaltet GetFileFromURL einige Zusatzoptionen in Form von Funktionsparametern.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;GetFileFromURL($url, &#039;&#039;[$timeout], [$data], [$noshutdown], [$loglevel]&#039;&#039;)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$url&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die HTTP-URL, welche aufgerufen werden soll. Diese kann optional Usernamen, Passwort und einen Port enthalten. Sowohl HTTP als auch HTTPS wird hierbei unterstützt.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com/directory/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;https://www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com:8080/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://user:password@www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG:&#039;&#039;&#039; Falls ein Username sowie Passwort übergeben werden, so müssen diese vorher jeweils mittels [[#urlEncode|urlEncode()]] in URL-kompatible Form umgewandelt werden um Probleme mit evtl. enthaltenen Sonderzeichen zu vermeiden. &lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die maximale Dauer in Sekunden für die HTTP-Anfrage&lt;br /&gt;
&lt;br /&gt;
Beispiel: 5 &#039;&#039;(Sekunden)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn man Daten via HTTP-POST übertragen möchte, so kann man die Nutzdaten über $data übergeben. Die Daten werden dabei als Formulardaten übertragen. Wenn man den Content-Type beeinflussen oder mehrere Formular-Felder senden möchte, sollte man zur Funktion HttpUtils_BlockingGet oder HttpUtils_NonblockingGet greifen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$noshutdown&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn $noshutdown auf 1 gesetzt ist, wird dem HTTP-Server nicht implizit mitgeteilt, dass die Verbindung nach dem Request geschlossen werden soll. Viele Webserver schließen in solch einem Fall die Verbindung, bevor sie die Antwort senden. Bei 0 wird dem Webserver mitgeteilt, dass der Sendevorgang beendet ist und nun die Antwort abgewartet wird.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$loglevel&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Das [[verbose|Loglevel]], in dem sämtliche Logmeldungen zu dieser HTTP-Abfrage erzeugt werden sollen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Funktionsergebnis ist der Inhalt der aufgerufenen Seite in Form eines Strings.&lt;br /&gt;
&lt;br /&gt;
=== GetFileFromURLQuiet ===&lt;br /&gt;
Diese Funktion funktioniert ähnlich wie GetFileFromURL. Allerdings wird die tatsächliche URL in allen erzeugten Log-Meldungen unkenntlich gemacht um z.B. Zugangsdaten nicht preiszugeben. Die aufgerufene Seite wird ebenfalls als Funktionsergebnis zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;GetFileFromURLQuiet($url, &#039;&#039;[$timeout], [$data], [$noshutdown], [$loglevel]&#039;&#039;)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$url&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die HTTP-URL, welche aufgerufen werden soll. Diese kann optional Usernamen, Passwort und einen Port enthalten. Sowohl HTTP als auch HTTPS wird hierbei unterstützt.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com/directory/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;https://www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com:8080/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://user:password@www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG:&#039;&#039;&#039; Falls ein Username sowie Passwort übergeben werden, so müssen diese vorher jeweils mittels [[#urlEncode|urlEncode()]] in URL-kompatible Form umgewandelt werden um Probleme mit evtl. enthaltenen Sonderzeichen zu vermeiden. &lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die maximale Dauer in Sekunden für die HTTP-Anfrage&lt;br /&gt;
&lt;br /&gt;
Beispiel: 5 &#039;&#039;(Sekunden)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn man Daten via HTTP-POST übertragen möchte, so kann man die Nutzdaten über &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; übergeben. Die Daten werden dabei als Formulardaten übertragen. Wenn man den Content-Type beeinflussen möchte, oder mehrere Formular-Felder senden möchte, sollte man zur Funktion HttpUtils_BlockingGet oder HttpUtils_NonblockingGet greifen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$noshutdown&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn $noshutdown auf 1 gesetzt ist, wird dem HTTP-Server nicht implizit mitgeteilt, dass die Verbindung nach dem Request geschlossen werden soll. Viele Webserver schließen in solch einem Fall die Verbindung bevor, sie die Antwort senden. Bei 0 wird dem Webserver mitgeteilt, dass der Sendevorgang beendet ist und nun die Antwort abgewartet wird.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$loglevel&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Das Loglevel, in dem sämtliche Logmeldungen zu dieser HTTP-Abfrage erzeugt werden sollen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Funktionsergebnis ist der Inhalt der aufgerufenen Seite in Form eines Strings.&lt;br /&gt;
&lt;br /&gt;
=== HttpUtils_BlockingGet ===&lt;br /&gt;
Wenn die bisher genannten Funktionen nicht ausreichen um die gewünschte Abfrage durchzuführen, so kann man diese Funktion verwenden. Aufgrund zahlreicher Parameter ermöglicht sie viele Anpassungsmöglichkeiten. Diese Funktion hat dabei nicht wie üblich eine Liste an Funktionsparametern, sondern lediglich einen Parameter, welcher eine Hashreferenz mit allen Funktionsparametern darstellt. Dieser Hash enthält sämtliche Parameter inkl. Werten. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;HttpUtils_BlockingGet($param)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Der Parameter $param ist eine Referenz auf eine Hash-Struktur, welche die einzelnen Parameter enthält. Der Hash $param kann folgende Optionen beinhalten:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:175px&amp;quot; | Parameter !! style=&amp;quot;width:auto&amp;quot; | Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{url}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
 || Die HTTP-URL, welche aufgerufen werden soll. Diese kann optional Usernamen, Passwort und einen Port enthalten. Sowohl HTTP als auch HTTPS wird hierbei unterstützt.&amp;lt;br&amp;gt;&lt;br /&gt;
Beispiel:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com/directory/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;https://www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://www.myhost.com:8080/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&amp;lt;nowiki&amp;gt;http://user:password@www.myhost.com/&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG:&#039;&#039;&#039; Alternativ können die Zugangsdaten auch in &amp;lt;code&amp;gt;$param-&amp;gt;{user}&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$param-&amp;gt;{pwd}&amp;lt;/code&amp;gt; übergeben werden. Falls  Username und Passwort in der URL übergeben werden, so müssen diese vorher jeweils mittels [[#urlEncode|urlEncode()]] in URL-kompatible Form umgewandelt werden um Probleme mit evtl. enthaltenen Sonderzeichen (z.B. &amp;quot;:&amp;quot; und &amp;quot;@&amp;quot;) zu vermeiden, da sonst die URL nicht mehr einer validen Syntax entspricht. &lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; |&#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{timeout}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die maximale Dauer in Sekunden bis der Server eine Antwort liefern muss. Andernfalls wird der Request mit einer Fehlermeldung abgebrochen.&lt;br /&gt;
&lt;br /&gt;
Beispiel: 5 &#039;&#039;(Sekunden)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4 Sekunden&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; |&#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{data}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn man Daten via HTTP-POST übertragen möchte, so kann man die Nutzdaten über &amp;lt;code&amp;gt;$param-&amp;gt;{data}&amp;lt;/code&amp;gt; übergeben. Die Daten werden dabei als Formulardaten übertragen. Die Daten können dabei auf zwei Arten übergeben werden:&lt;br /&gt;
&lt;br /&gt;
1. Daten als Zeichenkette:&lt;br /&gt;
:* Die Daten werden komplett als gesamte Zeichenkette in &amp;lt;code&amp;gt;$param-&amp;gt;{data}&amp;lt;/code&amp;gt; abgelegt (z.B.: &amp;lt;code&amp;gt;$param-&amp;gt;{data} = &amp;quot;Jede Menge tolle Daten&amp;quot;&amp;lt;/code&amp;gt;).&lt;br /&gt;
2. Daten als Hash-Struktur:&lt;br /&gt;
:* Die Daten werden als Hash mit Key-Value-Pairs übergeben (z.B.: &amp;lt;code&amp;gt;$param-&amp;gt;{data}{field1} = &amp;quot;value1&amp;quot;, $param{data}{field2} = &amp;quot;value2&amp;quot;, ...&amp;lt;/code&amp;gt; ). Die Daten werden dann als Formulardaten mit mehrfachen Datenfeldern übertragen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{user}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Im Falle einer notwendigen HTTP-Authentifizierung, der zu benutzende Username um sich gegenüber dem Server zu identifizieren. Alternativ kann der Username und das Passwort auch direkt in der URL mitgegeben werden (siehe jedoch &amp;lt;code&amp;gt;$param-&amp;gt;{hideurl}&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{pwd}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Im Falle einer notwendigen HTTP-Authentifizierung, das zu benutzende Passwort um sich gegenüber dem Server zu identifizieren. Alternativ kann der Username und das Passwort auch direkt in der URL mitgegeben werden (siehe jedoch &amp;lt;code&amp;gt;$param-&amp;gt;{hideurl}&amp;lt;/code&amp;gt;).&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{noshutdown}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Wenn &amp;lt;code&amp;gt;$param-&amp;gt;{noshutdown}&amp;lt;/code&amp;gt; auf 1 gesetzt ist, wird dem HTTP-Server nicht implizit mitgeteilt, dass die Verbindung nach dem Request geschlossen werden soll. Viele Webserver schließen in solch einem Fall die Verbindung, bevor sie die Antwort senden. Bei 0 wird dem Webserver mitgeteilt, dass der Sendevorgang beendet ist und nun die Antwort abgewartet wird.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{loglevel}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Das Loglevel, in dem sämtliche Logmeldungen zu dieser HTTP-Abfrage erzeugt werden sollen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 4&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{hideurl}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn dieser Parameter den Wert 1 trägt, wird die gesamte URL in sämtlichen Log-Ausgaben unkenntlich gemacht. Dies notwendig, wenn z.B. Zugangsdaten direkt in der URL angegeben wurden, oder die URL ein geheimes Token oder andere schützenswerte Informationen enthält.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{ignoreredirects}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn dieser Parameter den Wert 1 trägt, werden Umleitungen durch den Server ignoriert und der Request beendet. Dies kann erforderlich sein um evtl. Cookies aus der Antwort, welche eine Umleitung enthält aus dem HTTP Header zu extrahieren um diese im nächsten Request weiterzuverwenden.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{method}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Die HTTP-Methode, welche zur Abfrage verwendet werden soll. Sofern keine Daten übertragen werden ist dies standardmäßig &amp;quot;GET&amp;quot;, ansonsten &amp;quot;POST&amp;quot;. Es können aber auch andere Methoden verwendet werden.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &amp;quot;GET&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{keepalive}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
||Wenn dieser Parameter auf 1 gesetzt ist, wird dem Server der Wunsch mitgeteilt, die Verbindung offen zu lassen für weitere Anfragen. Sobald die Antwort auf den jeweiligen Request eintrifft, bleibt die TCP-Verbindung bestehen (sofern der Server dies unterstützt). Anschließend kann man den Parameter-Hash mit einer neuen URL und Optionen füllen. Der Parameter &amp;quot;keepalive&amp;quot; sollte dabei weiterhin gesetzt bleiben, sofern die Verbindung auch weiterhin möglichst erhalten bleiben soll. &lt;br /&gt;
&lt;br /&gt;
Um eine offene Verbindung endgültig zu schließen, muss die Funktion [[HttpUtils#HttpUtils_Close|HttpUtils_Close]] aufgerufen werden.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{header}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eigene HTTP-Header-Zeilen können über diesen Parameter eingebracht werden. Er kann dazu genutzt werden um z.B. den Content-Type festzulegen, oder einfach nur zusätzliche Header-Felder zu setzen. Es gibt zwei Möglichkeiten, diesen Parameter zu befüllen.&lt;br /&gt;
&lt;br /&gt;
# Als &#039;&#039;&#039;Zeichenkette&#039;&#039;&#039;. Mehrere Header-Zeilen müssen dabei mit &amp;quot;\r\n&amp;quot; getrennt werden.&lt;br /&gt;
# Als &#039;&#039;&#039;Hash-Struktur&#039;&#039;&#039;. Hierbei findet eine Zuordnung von Header-Bezeichnung zum Wert als Key-Value-Pair statt.&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;User-Agent: Mozilla/1.22&#039;&#039;&#039;&amp;lt;u&amp;gt;\r\n&amp;lt;/u&amp;gt;&#039;&#039;&#039;Content-Type: application/xml&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;Content-Type: application/xml&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;{ &amp;quot;User-Agent&amp;quot; =&amp;gt;  &amp;quot;Mozilla/1.22&amp;quot;, &amp;quot;Content-Type&amp;quot; =&amp;gt; &amp;quot;application/xml&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;{ &amp;quot;Content-Type&amp;quot; =&amp;gt; &amp;quot;application/xml&amp;quot; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;[leer]&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{sslargs}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eigene SSL-Optionen können über diesen Parameter eingebracht werden. Er kann dazu genutzt werden um z.B. die SSL-Zertifikats Verifikation abzuschalten. Die SSL-Optionen müssen als eigene Hash-Referenz übergeben werden. Eine Liste aller möglichen Optionen findet man in der Perl-Dokumentation zu [http://search.cpan.org/~sullr/IO-Socket-SSL-2.016/lib/IO/Socket/SSL.pod#Description_Of_Methods IO::Socket::SSL].&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{sslargs} = { SSL_verify_mode =&amp;gt; 0, sslOpt2 =&amp;gt; &#039;sslVal2&#039; }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Standardwert: &#039;&#039;{ }&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{httpversion}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Die HTTP-Version, welche zur Abfrage verwendet werden soll. Standardmäßig werden alle Abfragen mit HTTP/1.0 durchgeführt. Falls es jedoch notwendig ist HTTP/1.1 zu verwenden, so sollte &amp;lt;code&amp;gt;$param-&amp;gt;{httpversion}&amp;lt;/code&amp;gt; auf &amp;quot;1.1&amp;quot; gesetzt werden. Bei Version 1.1 wird automatisch der Header &amp;quot;&amp;lt;code&amp;gt;Connection: close&amp;lt;/code&amp;gt;&amp;quot; implizit mitgesendet.&lt;br /&gt;
&lt;br /&gt;
Standardwert: &amp;quot;1.0&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{digest}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn dieser Parameter den Wert 1 trägt, wird bei vorhandenen Authentifizierungsdaten (Username+Passwort) eine Authentifizierung via Digest-Verfahren nach RFC 2617&amp;lt;ref name=&amp;quot;rfc2617&amp;quot;&amp;gt;RFC 2617 - HTTP Authentication: Basic and Digest Access Authentication&amp;lt;/ref&amp;gt; erwartet. Die Anmeldedaten werden dann nur verwendet, wenn dieser explizit eine HTTP Digest Authentifizierung einleitet. Ist dieser Parameter nicht gesetzt (Wert: 0), wird bei vorhandenen Authentifizierungsdaten immer eine HTTP Basic&amp;lt;ref name=&amp;quot;rfc2617&amp;quot; /&amp;gt; Authentifizierung im Request mitgeschickt.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{compress}&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn dieser Parameter den Wert 1 trägt, wird dem Server die Verwendung von Komprimierung in Form GZIP oder alternativ Deflate&amp;lt;ref&amp;gt;RFC 1951 - DEFLATE Compressed Data Format Specification version 1.3&amp;lt;/ref&amp;gt; zur Übertragung der Antwort ermöglicht. Die komprimierte Antwort wird dabei direkt durch HttpUtils.pm wieder dekomprimiert. Wird dieser Parameter auf den Wert 0 gesetzt, so wird keine Komrpimierung ermöglicht.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WICHTIG&#039;&#039;&#039;: Die Verwendung von Komprimierung in HttpUtils.pm kann mit dem globalen Attribut &amp;quot;httpcompress&amp;quot; (Standardwert: 1) durch setzen auf 0 für die gesamte FHEM Installation durch den Nutzer deaktiviert werden. In diesem Fall ist der Parameter &amp;lt;code&amp;gt;$param-&amp;gt;{compress} = 1&amp;lt;/code&amp;gt; wirkungslos.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Als Rückgabewert von HttpUtils_BlockingGet wird ein Array mit zwei Rückgabewerten zurückgegeben:&lt;br /&gt;
:&amp;lt;code&amp;gt;($err, $data) = HttpUtils_BlockingGet( … )&amp;lt;/code&amp;gt;&lt;br /&gt;
Diese zwei Rückgabewerte haben folgende Bedeutung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$err&amp;lt;/code&amp;gt;&#039;&#039;&#039;|| Falls beim Aufruf der URL ein Fehler aufgetreten ist (z.B. Server nicht erreichbar oder Verbindungstimeout), dann ist dieser Wert mit einer Fehlermeldung gefüllt. &lt;br /&gt;
&lt;br /&gt;
Wenn kein Fehler aufgetreten ist, ist dieser Wert mit einem Leerstring gefüllt (&amp;lt;code&amp;gt;$err = &amp;quot;&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt;&#039;&#039;&#039;|| Die Ergebnisdaten, welche der HTTP-Server zurückgeliefert hat. Die Daten werden als Klartext in Form eines gesamten Strings zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Falls ein Fehler aufgetreten ist, ist dieser Wert mit einem Leersting gefüllt (&amp;lt;code&amp;gt;$data = &amp;quot;&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== HttpUtils_NonblockingGet ===&lt;br /&gt;
{{Randnotiz|RNText=Die Funktion HttpUtils_NonblockingGet ist nicht komplett durchgehend &amp;quot;non-blocking&amp;quot;. DNS-Abfragen sind weiterhin blockierend. Insbesondere wenn der DNS-Name nicht aufgelöst werden kann.}}&lt;br /&gt;
Diese Funktion arbeitet ähnlich wie HttpUtils_BlockingGet. Allerdings wird das Ergebnis nicht als Funktionsergebnis zurückgegeben. Die Funktion HttpUtils_NonblockingGet initiiert den Verbindungsaufbau und übergibt alles weitere an FHEM interne Routinen. Sobald eine Antwort vom HTTP-Server eintrifft, wird eine Callback-Funktion mit verschiedenen Parametern (unter anderem auch das Ergebnis) aufgerufen, um die Antwort entgegenzunehmen und weiter zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Der Aufruf ist daher ähnlich zu HttpUtils_BlockingGet mit nur einem Parameter-Hash:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;HttpUtils_NonblockingGet($param)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:175px&amp;quot; | Parameter !! style=&amp;quot;width:auto&amp;quot; | Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; style=&amp;quot;text-align:center&amp;quot; | &#039;&#039;&#039;&#039;&#039;&amp;lt;br&amp;gt;Alle Hash-Parameter, welche für HttpUtils_BlockingGet gelten, sind auch für HttpUtils_NonblockingGet gültig&#039;&#039;&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{callback}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Funktion (oder eine Referenz auf eine Funktion), welche die Ergebnisdaten entgegennimmt und die Antwort entsprechend weiterverarbeitet. Die Callback-Funktion muss dabei 3 Parameter erwarten. Die Funktionsparameter der Callback-Funktion werden im nachfolgenden Abschnitt näher erläutert.&lt;br /&gt;
&lt;br /&gt;
Beispiel: &lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{callback} = \&amp;amp;MyCallbackFn&amp;lt;/code&amp;gt; — &#039;&#039;(Referenzzeiger auf Funktionsname)&#039;&#039;&lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{callback} = sub(){ … }&amp;lt;/code&amp;gt; —  &#039;&#039;(direkte Funktionsdefinition)&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top;white-space:nowrap;&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{incrementalTimeout}&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Sofern gesetzt (Wert: &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;), wird der Timeout nach jedem Empfang von Teil-Daten wieder zurückgesetzt. So können größere Datenmengen von langsamen Sendern empfangen werden, ohne den Parameter &amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt; zu erhöhen und dadurch tatsächliche Verbindungsprobleme erst spät zu erkennen.&lt;br /&gt;
&lt;br /&gt;
Standardwert: 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;Benutzerdefinierte Parameter&#039;&#039; &lt;br /&gt;
|| Es können im Hash weitere benutzerdefinierte Parameter gesetzt werden, welche evtl. in der Callback-Funktion benötigt werden, um die Antwort korrekt zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Zum Beispiel im Rahmen der Modul-Programmierung wäre das &amp;lt;code&amp;gt;$hash&amp;lt;/code&amp;gt; der aktuellen Definition. Alle gesetzten Parameter sind in der Callback-Funktion wieder über &amp;lt;code&amp;gt;$param&amp;lt;/code&amp;gt; direkt abrufbar und können ausgewertet werden.&lt;br /&gt;
&lt;br /&gt;
Beispiel: &lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{hash} = $hash&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;$param-&amp;gt;{command} = &amp;quot;on&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
Ein Funktionsrückgabewert von HttpUtils_NonblockingGet existiert nicht, da die eigentliche Rückgabe der Daten über die Callback-Funktion erfolgt. Die Callback-Funktion wird aufgerufen, sobdald der HTTP-Request abgeschlossen ist, oder ein Fehler aufgetreten ist. Der Funktionsaufruf erfolgt mit den folgenden Parametern:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt; &#039;&#039;MyCallbackFn&#039;&#039; ( $param, $err, $data )&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese 3 Parameter haben dabei folgende Bedeutung:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Der Parameter-Hash, mit allen Argumenten die beim Aufruf der Funktion übergeben worden sind.&lt;br /&gt;
&lt;br /&gt;
Es ist möglich, dass der Parameter-Hash nach erfolgter Abfrage zusätzliche oder veränderte Elemente enthält:&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{redirects}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Die Anzahl an Umleitungen die erfolgte, bis die Anfrage beantwortet wurde.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{url}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Die URL, die nach erfolgter Umleitung die Anfrage beantwortet hat.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{protocol}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Das verwendete Protokoll, welches zur Abfrage verwendet wurde (&amp;quot;http&amp;quot; oder &amp;quot;https&amp;quot;).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{path}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Der Pfad, welcher auf dem HTTP-Server angefragt wurde.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{host}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Der Name oder die IP-Adresse des HTTP-Servers.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{httpheader}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Der gesamte HTTP Header, welcher der Server bei der letzten Antwort zurücklieferte.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{code}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Der HTTP-Statuscode, mit dem die Anfrage vom Server beantwortet wurde.&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{addr}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Die HTTP-URL ohne Pfad und evtl. Authentifizerungsinformationen des HTTP-Servers (z.B. &amp;quot;&amp;lt;nowiki&amp;gt;http://myserver.com:8080&amp;lt;/nowiki&amp;gt;&amp;quot;).&lt;br /&gt;
* &#039;&#039;&#039;&amp;lt;code&amp;gt;$param-&amp;gt;{auth}&amp;lt;/code&amp;gt;&#039;&#039;&#039; - Ein Flag (0/1) ob Zugangsdaten für den Request zur Verfügung standen (durch Angabe in der URL oder direkt in &amp;lt;code&amp;gt;$param&amp;lt;/code&amp;gt;).&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$err&amp;lt;/code&amp;gt;&#039;&#039;&#039;|| Falls beim Aufruf der URL ein Fehler aufgetreten ist (z.B. Server nicht erreichbar oder Verbindungstimeout), dann ist dieser Wert mit einer Fehlermeldung gefüllt. &lt;br /&gt;
&lt;br /&gt;
Wenn kein Fehler aufgetreten ist, ist dieser Wert mit einem Leerstring gefüllt (&amp;lt;code&amp;gt;$err = &amp;quot;&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt;&#039;&#039;&#039;|| Die Ergebnisdaten, welche der HTTP-Server zurückgeliefert hat. Die Daten werden als Klartext in Form eines gesamten Strings zurückgegeben. Es handelt sich hierbei ausschließlich um den HTTP-Body. &lt;br /&gt;
&lt;br /&gt;
Falls ein Fehler aufgetreten ist, ist dieser Wert mit einem Leersting gefüllt (&amp;lt;code&amp;gt;$data = &amp;quot;&amp;quot;&amp;lt;/code&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Die Callback-Funktion kann nun die Daten aus der HTTP-Antwort verarbeiten oder bei Fehlern entsprechende Log-Meldungen ausgeben.&lt;br /&gt;
&lt;br /&gt;
==== Beispiel #1 für HttpUtils_NonblockingGet() für Modulprogrammierer ====&lt;br /&gt;
Das folgende Beispiel soll eine Hilfestellung für eigene Anwendungen geben&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;use HttpUtils;&lt;br /&gt;
sub X_PerformHttpRequest($)&lt;br /&gt;
{&lt;br /&gt;
    my ($hash, $def) = @_;&lt;br /&gt;
    my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
    my $param = {&lt;br /&gt;
                    url        =&amp;gt; &amp;quot;http://www.foo.de/&amp;quot;,&lt;br /&gt;
                    timeout    =&amp;gt; 5,&lt;br /&gt;
                    hash       =&amp;gt; $hash,                                                                                 # Muss gesetzt werden, damit die Callback funktion wieder $hash hat&lt;br /&gt;
                    method     =&amp;gt; &amp;quot;GET&amp;quot;,                                                                                 # Lesen von Inhalten&lt;br /&gt;
                    header     =&amp;gt; &amp;quot;User-Agent: TeleHeater/2.2.3\r\nAccept: application/json&amp;quot;,                            # Den Header gemäß abzufragender Daten ändern&lt;br /&gt;
                    callback   =&amp;gt; \&amp;amp;X_ParseHttpResponse                                                                  # Diese Funktion soll das Ergebnis dieser HTTP Anfrage bearbeiten&lt;br /&gt;
                };&lt;br /&gt;
&lt;br /&gt;
    HttpUtils_NonblockingGet($param);                                                                                    # Starten der HTTP Abfrage. Es gibt keinen Return-Code. &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
sub X_ParseHttpResponse($)&lt;br /&gt;
{&lt;br /&gt;
    my ($param, $err, $data) = @_;&lt;br /&gt;
    my $hash = $param-&amp;gt;{hash};&lt;br /&gt;
    my $name = $hash-&amp;gt;{NAME};&lt;br /&gt;
&lt;br /&gt;
    if($err ne &amp;quot;&amp;quot;)                                                                                                      # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist&lt;br /&gt;
    {&lt;br /&gt;
        Log3 $name, 3, &amp;quot;error while requesting &amp;quot;.$param-&amp;gt;{url}.&amp;quot; - $err&amp;quot;;                                               # Eintrag fürs Log&lt;br /&gt;
        readingsSingleUpdate($hash, &amp;quot;fullResponse&amp;quot;, &amp;quot;ERROR&amp;quot;, 0);                                                        # Readings erzeugen&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    elsif($data ne &amp;quot;&amp;quot;)                                                                                                  # wenn die Abfrage erfolgreich war ($data enthält die Ergebnisdaten des HTTP Aufrufes)&lt;br /&gt;
    {&lt;br /&gt;
        Log3 $name, 3, &amp;quot;url &amp;quot;.$param-&amp;gt;{url}.&amp;quot; returned: $data&amp;quot;;                                                         # Eintrag fürs Log&lt;br /&gt;
&lt;br /&gt;
        # An dieser Stelle die Antwort parsen / verarbeiten mit $data&lt;br /&gt;
&lt;br /&gt;
        readingsSingleUpdate($hash, &amp;quot;fullResponse&amp;quot;, $data, 0);                                                          # Readings erzeugen&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    # Damit ist die Abfrage zuende.&lt;br /&gt;
    # Evtl. einen InternalTimer neu schedulen&lt;br /&gt;
}&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Beispiel #2 eines Snippets um eine Datei herunterzuladen und lokal zu speichern ====&lt;br /&gt;
Dieses vollständige Beispiel eines Device verwendet &amp;lt;code&amp;gt;HttpUtils_NonblockingGet()&amp;lt;/code&amp;gt; um eine Datei herunterzuladen und im lokalem Dateisystem mit den Rechten des FHEM Nutzers zu speichern.&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod MyDownloader dummy&lt;br /&gt;
attr MyDownloader userattr FilePath&lt;br /&gt;
attr MyDownloader FilePath /opt/fhem/www/webhook/tasmota.bin.gz&lt;br /&gt;
attr MyDownloader comment &#039;\&lt;br /&gt;
set MyDownloader job Filepath=&amp;quot;/opt/fhem/fhemicon.png&amp;quot;https://fhem.de/www/images/default/fhemicon.png\&lt;br /&gt;
set MyDownloader job https://fhem.de/www/images/default/fhemicon.png\&lt;br /&gt;
\&lt;br /&gt;
set MyDownloader job Filepath=&amp;quot;/opt/fhem/www/webhook/tasmota.bin&amp;quot; https://ota.tasmota.com/tasmota/release/tasmota.bin\&lt;br /&gt;
set MyDownloader job Filepath=&amp;quot;/opt/fhem/www/webhook/tasmota.bin.gz&amp;quot; https://ota.tasmota.com/tasmota/release/tasmota.bin.gz\&lt;br /&gt;
set MyDownloader job Filepath=&amp;quot;/opt/fhem/www/webhook/tasmota-minimal.bin.gz&amp;quot; https://ota.tasmota.com/tasmota/release/tasmota-minimal.bin.gz\&lt;br /&gt;
set MyDownloader job Filepath=&amp;quot;/opt/fhem/www/webhook/tasmota32-zbbrdgpro.bin&amp;quot; https://ota.tasmota.com/tasmota32/release/tasmota32-zbbrdgpro.bin\&lt;br /&gt;
\&lt;br /&gt;
set MyDownloader job Filepath=&amp;quot;/opt/fhem/www/webhook/tasmota.bin&amp;quot; https://ota.tasmota.com/tasmota/release/tasmota.bin;; sleep 5;; set MyDownloader job Filepath=&amp;quot;/opt/fhem/www/webhook/tasmota.bin.gz&amp;quot; https://ota.tasmota.com/tasmota/release/tasmota.bin.gz;; sleep 5;; set MyDownloader job Filepath=&amp;quot;/opt/fhem/www/webhook/tasmota-minimal.bin.gz&amp;quot; https://ota.tasmota.com/tasmota/release/tasmota-minimal.bin.gz;; sleep 5;; set MyDownloader job Filepath=&amp;quot;/opt/fhem/www/webhook/tasmota32-zbbrdgpro.bin&amp;quot; https://ota.tasmota.com/tasmota32/release/tasmota32-zbbrdgpro.bin\&lt;br /&gt;
&#039;&lt;br /&gt;
attr MyDownloader readingList job&lt;br /&gt;
attr MyDownloader setList job&lt;br /&gt;
attr MyDownloader userReadings result:job:.* {\&lt;br /&gt;
	my $hash = $defs{$name};;\&lt;br /&gt;
	my $job = ReadingsVal($name, &amp;quot;job&amp;quot;, &#039;https://fhem.de/www/images/default/fhemicon.png&#039;);;\&lt;br /&gt;
	my $path = AttrVal($name, &amp;quot;Filepath&amp;quot;, &amp;quot;/opt/fhem/www/download.bin&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	if ($job =~ s/(?:Filepath|Dateipfad)=&amp;quot;(.*?)&amp;quot;//) {\&lt;br /&gt;
		$path = $1;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	my $fnFct = sub {\&lt;br /&gt;
		my ($param, $err, $data) = @_;;\&lt;br /&gt;
		\&lt;br /&gt;
		my $len = $data ? length($data) : 0;;\&lt;br /&gt;
		my $error = $err ? $err : FileWrite($path, $data);;\&lt;br /&gt;
		\&lt;br /&gt;
		readingsBeginUpdate($hash);;\&lt;br /&gt;
		readingsBulkUpdate($hash, &amp;quot;busy&amp;quot;, 0, 1);;\&lt;br /&gt;
		readingsBulkUpdate($hash, &amp;quot;result&amp;quot;, &amp;quot;saved $len Bytes&amp;quot;, 1) if !$error;;\&lt;br /&gt;
		readingsBulkUpdate($hash, &amp;quot;result&amp;quot;, &amp;quot;error ($error, $err)&amp;quot;, 1) if $error;;\&lt;br /&gt;
		readingsEndUpdate($hash, 1);;\&lt;br /&gt;
	};;\&lt;br /&gt;
	\&lt;br /&gt;
	if ( ReadingsVal($name, &amp;quot;busy&amp;quot;, 0) == 0 ) {\&lt;br /&gt;
		readingsBulkUpdate($hash, &amp;quot;busy&amp;quot;, 1, 1);;\&lt;br /&gt;
		$job =~ s/^\s+|\s+$//g;;\&lt;br /&gt;
		my $param = {\&lt;br /&gt;
                    url        =&amp;gt; $job,\&lt;br /&gt;
                    timeout    =&amp;gt; 10,\&lt;br /&gt;
                    hash       =&amp;gt; $hash,\&lt;br /&gt;
                    method     =&amp;gt; &amp;quot;GET&amp;quot;,\&lt;br /&gt;
                    callback   =&amp;gt; $fnFct\&lt;br /&gt;
                };;\&lt;br /&gt;
		HttpUtils_NonblockingGet($param);;\&lt;br /&gt;
		return &amp;quot;download job started&amp;quot;;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	return;;\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Zugehöriger Forumspost: [https://forum.fhem.de/index.php?topic=143117.msg1352221#msg1352221 📂 Datei herunterladen, Downloader, HttpUtils_NonblockingGet]&lt;br /&gt;
&lt;br /&gt;
=== HttpUtils_Close ===&lt;br /&gt;
&lt;br /&gt;
Für den Abbruch von offenen Verbindungen und noch laufenden NonBockingGet-Aufrufen gibt es die Funktion HttpUtils_Close. Diese kann z.B. beim Löschen eines Devices oder Herunterfahren des Servers aufgerufen werden, um bestehende Verbindungen zu schliessen.&lt;br /&gt;
&lt;br /&gt;
Wenn man den Parameter &amp;quot;keepalive&amp;quot; beim Aufruf von HttpUtils_NonBlockingGet/HttpUtils_BlockingGet gesetzt hat, muss man HttpUtils_Close aufrufen um die Verbindung tatsächlich zu schließen, da diese noch durch den Server offen gehalten werden kann.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Aufruf: &amp;lt;code&amp;gt;HttpUtils_Close($param)&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! style=&amp;quot;width:175px&amp;quot; | Parameter !! style=&amp;quot;width:auto&amp;quot; | Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$param&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Der Hash, der beim vorherigen Aufruf an HTTPUtils Funktionen (HttpUtils_NonblockingGet oder HttpUtils_BlockingGet) übergeben wurde&lt;br /&gt;
&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
== Hilfsfunktionen ==&lt;br /&gt;
&lt;br /&gt;
=== urlEncode ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$encoded = urlEncode($string);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Funktion urlEncode() wandelt die übergebene Zeichenkette &amp;lt;code&amp;gt;$string&amp;lt;/code&amp;gt; in eine URL-kompatible&amp;lt;ref name=&amp;quot;rfc3986-2.1&amp;quot;&amp;gt;[https://tools.ietf.org/html/rfc3986#section-2.1 RFC 3989] - Abschnitt 2.1 &amp;quot;Percent-Encoding&amp;quot;&amp;lt;/ref&amp;gt; Zeichenkette um. Der Rückgabewert &amp;lt;code&amp;gt;$encoded&amp;lt;/code&amp;gt; ist eine Zeichenkette welche innerhalb einer URL verwendet werden kann, bspw. als Wert eines GET-Parameters.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$string&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Die Zeichenkette, welche in eine URL-kompatible Form umgewandelt werden soll.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$encoded &amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die URL-kompatible Version der übergebenen Zeichenkette. Problematische Zeichen sind in diesem Wert durch ein Prozentzeichen gefolgt von der Hexadezimaldarstellung des Zeichens dargestellt.&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
* &amp;quot;a: b&amp;quot; =&amp;gt; &amp;quot;a%3a%20b&amp;quot;&lt;br /&gt;
* &amp;quot;a/b&amp;quot; =&amp;gt; &amp;quot;a%2fb&amp;quot;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== urlDecode ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
$string = urlDecode($encoded);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Funktion urlDecode () normalisiert die übergebene Zeichenkette &amp;lt;code&amp;gt;$encoded&amp;lt;/code&amp;gt; und ersetzt dabei sämtliche Hexadezimaldarstellungen&amp;lt;ref name=&amp;quot;rfc3986-2.1&amp;quot; /&amp;gt; (eingeleitet mit einem Prozentzeichen) durch die entsprechenden Zeichen. Der Rückgabewert &amp;lt;code&amp;gt;$string&amp;lt;/code&amp;gt; ist eine Zeichenkette welche keinerlei Hexadezimaldarstellungen mehr aufweist.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$encoded&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, welche URL-kombatible Hexadezimaldarstellungen enthält.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert !! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$string&amp;lt;/code&amp;gt;&#039;&#039;&#039; || Die normale Version der übergebenen Zeichenkette. Sämtliche Hexadezimalddarstellungen sind in ihre entsprechenden Zeichen umgewandelt.&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
* &amp;quot;a%3a%20b&amp;quot; =&amp;gt; &amp;quot;a: b&amp;quot;&lt;br /&gt;
* &amp;quot;a%2fb&amp;quot; =&amp;gt; &amp;quot;a/b&amp;quot;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Referenzen ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Gleitende_Mittelwerte_berechnen_und_loggen&amp;diff=40389</id>
		<title>Gleitende Mittelwerte berechnen und loggen</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Gleitende_Mittelwerte_berechnen_und_loggen&amp;diff=40389"/>
		<updated>2025-09-21T10:16:29Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: SVG Plot von Temperaturen und dem Mittelwert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Datei:SVG Plot von Temperaturen und dem Mittelwert.png|alternativtext=SVG Plot von Temperaturen und dem Mittelwert|ohne|mini|SVG Plot von Temperaturen und dem Mittelwert]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Für verschiedene Wettersensoren ist es oftmals wünschenswert, gleitende Mittelwerte (fälschlich auch als &amp;quot;relative&amp;quot; Mittelwerte bezeichnet) über ein bestimmtes Zeitfenster auszugeben oder in den Logs zu speichern und zu plotten. Beispiele:&lt;br /&gt;
* Die momentanen Winddaten eines KS300 sind oft stark schwankend. Hier wäre ein Mittelwert z.B. der letzten 2-3 Stunden interessant. &lt;br /&gt;
* Für die Außentemperatur ist anhand eines Mittelwertes der letzten 24 Stunden evtl. ein gewisser Trend der generellen Entwicklung der Temperatur ableitbar.&lt;br /&gt;
&lt;br /&gt;
Mit Modulen wie &#039;&#039;rain&#039;&#039; oder &#039;&#039;average&#039;&#039; ist konzeptbedingt nur ein Mittelwert von fixen Zeiträumen (aktueller Tag, Monat, etc.) möglich. &lt;br /&gt;
&lt;br /&gt;
Im Nachfolgenden werden deshalb zwei Programme vorgestellt, die solche gleitenden Mittelwerte bereitstellen. Beide Programme werden in die Datei [[99 myUtils anlegen|99_MyUtils.pm]] eingetragen und dann von FHEM aufgerufen. Das erste Programm macht dies für beliebige Readings beliebiger Devices, das zweite Programm greift auf beliebige [[FileLog|File-]] oder [[DbLog|DbLogs]] zurück.&lt;br /&gt;
&lt;br /&gt;
== Gleitender Mittelwert für beliebige Readings ==&lt;br /&gt;
=== Subroutine movingAverage in 99_MyUtils anlegen ===&lt;br /&gt;
Es werden die Funktionen &#039;&#039;movingAverage&#039;&#039; und &#039;&#039;movingAverageT&#039;&#039; in die 99_MyUtils Datei übernommen werden. Wenn noch nicht vorhanden, sollte die Datei dann so aussehen, der übliche Speicherort ist &#039;&#039;/opt/fhem/FHEM/99_MyUtils.pm&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;package main;&lt;br /&gt;
  use strict;&lt;br /&gt;
  use warnings;&lt;br /&gt;
  sub&lt;br /&gt;
  MyUtils_Initialize($$)&lt;br /&gt;
  {&lt;br /&gt;
   my ($hash) = @_;&lt;br /&gt;
  }&lt;br /&gt;
  ###############################################################################&lt;br /&gt;
  #&lt;br /&gt;
  #  Moving average &lt;br /&gt;
  #&lt;br /&gt;
  #  Aufruf: movingAverage(devicename,readingname,zeitspanne in s)&lt;br /&gt;
  #&lt;br /&gt;
  ###############################################################################&lt;br /&gt;
  &lt;br /&gt;
  sub movingAverage($$$){&lt;br /&gt;
     my ($name,$reading,$avtime) = @_;&lt;br /&gt;
     my $hash = $defs{$name};&lt;br /&gt;
     my @new = my ($val,$time) = ($hash-&amp;gt;{READINGS}{$reading}{VAL},$hash-&amp;gt;{READINGS}{$reading}{TIME});&lt;br /&gt;
     my ($cyear, $cmonth, $cday, $chour, $cmin, $csec) = $time =~ /(\d+)-(\d+)-(\d+)\s(\d+):(\d+):(\d+)/;&lt;br /&gt;
     my $ctime = $csec+60*$cmin+3600*$chour;&lt;br /&gt;
     my $num;&lt;br /&gt;
     my $arr;&lt;br /&gt;
     #-- initialize if requested&lt;br /&gt;
     if( ($avtime eq &amp;quot;-1&amp;quot;) ){&lt;br /&gt;
       $hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}=undef;&lt;br /&gt;
     }&lt;br /&gt;
     #-- test for existence&lt;br /&gt;
     if( !$hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}){&lt;br /&gt;
        #Log 1,&amp;quot;ARRAY CREATED&amp;quot;;&lt;br /&gt;
        push(@{$hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}},\@new);&lt;br /&gt;
        $num = 1;&lt;br /&gt;
        $arr=\@{$hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}};&lt;br /&gt;
     } else {&lt;br /&gt;
        $num = int(@{$hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}});&lt;br /&gt;
        $arr=\@{$hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}};&lt;br /&gt;
        my $starttime = $arr-&amp;gt;[0][1];&lt;br /&gt;
        my ($syear, $smonth, $sday, $shour, $smin, $ssec) = $starttime =~ /(\d+)-(\d+)-(\d+)\s(\d+):(\d+):(\d+)/;&lt;br /&gt;
        my $stime = $ssec+60*$smin+3600*$shour;&lt;br /&gt;
        #-- correct for daybreak&lt;br /&gt;
        $stime-=86400 &lt;br /&gt;
          if( $stime &amp;gt; $ctime);&lt;br /&gt;
        if( ($num &amp;lt; 25)&amp;amp;&amp;amp;( ($ctime-$stime)&amp;lt;$avtime) ){&lt;br /&gt;
          #Log 1,&amp;quot;ARRAY has $num elements, adding another one&amp;quot;;&lt;br /&gt;
          push(@{$hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}},\@new);&lt;br /&gt;
        }else{&lt;br /&gt;
          shift(@{$hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}});&lt;br /&gt;
          push(@{$hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}},\@new);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      #-- output and average&lt;br /&gt;
      my $average = 0;&lt;br /&gt;
      for(my $i=0;$i&amp;lt;$num;$i++){&lt;br /&gt;
        $average+=$arr-&amp;gt;[$i][0];&lt;br /&gt;
        Log 4,&amp;quot;[$name moving average] Value = &amp;quot;.$arr-&amp;gt;[$i][0].&amp;quot; Time = &amp;quot;.$arr-&amp;gt;[$i][1]; &lt;br /&gt;
      }&lt;br /&gt;
      $average=sprintf( &amp;quot;%5.3f&amp;quot;, $average/$num);&lt;br /&gt;
      #--average&lt;br /&gt;
      Log 4,&amp;quot;[$name moving average] calculated over $num values is $average&amp;quot;;  &lt;br /&gt;
      return $average;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
    ###############################################################################&lt;br /&gt;
    #&lt;br /&gt;
    #  Moving average of last X seconds, weighted by time&lt;br /&gt;
    #&lt;br /&gt;
    #  movingAverageT(Devicename, Readingname, HistoryLength)&lt;br /&gt;
    #&lt;br /&gt;
    ###############################################################################&lt;br /&gt;
    sub movingAverageT($$$) {&lt;br /&gt;
    my ($name, $reading, $hSeconds) = @_;&lt;br /&gt;
    my $hash = $defs{$name};&lt;br /&gt;
&lt;br /&gt;
    # aktuelles Value + Timestamp holen&lt;br /&gt;
    my ($val, $time) = (&lt;br /&gt;
        $hash-&amp;gt;{READINGS}{$reading}{VAL},&lt;br /&gt;
        $hash-&amp;gt;{READINGS}{$reading}{TIME}&lt;br /&gt;
    );&lt;br /&gt;
&lt;br /&gt;
    # Gültigkeit prüfen (nur Zahlen erlauben)&lt;br /&gt;
    return undef unless defined $val &amp;amp;&amp;amp; $val =~ /^-?\d+(?:\.\d+)?$/ &amp;amp;&amp;amp; defined $time;&lt;br /&gt;
&lt;br /&gt;
    my @new = ($val, $time);&lt;br /&gt;
    my $histKey = &amp;quot;history.T.$hSeconds&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    # Wenn kein Zeitraum → direkt aktuellen Wert zurückgeben&lt;br /&gt;
    return sprintf(&amp;quot;%5.3f&amp;quot;, $val) if $hSeconds == 0;&lt;br /&gt;
&lt;br /&gt;
    # History initialisieren, falls nicht vorhanden oder kein Array&lt;br /&gt;
    if (!exists $hash-&amp;gt;{READINGS}{$reading}{$histKey} || ref $hash-&amp;gt;{READINGS}{$reading}{$histKey} ne &#039;ARRAY&#039;) {&lt;br /&gt;
        $hash-&amp;gt;{READINGS}{$reading}{$histKey} = [ \@new ];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    my $arr = $hash-&amp;gt;{READINGS}{$reading}{$histKey};&lt;br /&gt;
&lt;br /&gt;
    # nur hinzufügen, wenn leer oder neuer Zeitstempel&lt;br /&gt;
    if (!@$arr || $time ne $arr-&amp;gt;[-1]-&amp;gt;[1]) {&lt;br /&gt;
        push @$arr, \@new;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # alte Einträge entfernen&lt;br /&gt;
    my $now = time();&lt;br /&gt;
    while (@$arr &amp;amp;&amp;amp; $now - time_str2num($arr-&amp;gt;[0][1]) &amp;gt; $hSeconds) {&lt;br /&gt;
        shift @$arr;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    my $aLength = scalar @$arr;&lt;br /&gt;
    return sprintf(&amp;quot;%5.3f&amp;quot;, $val) if $aLength &amp;lt;= 1;&lt;br /&gt;
&lt;br /&gt;
    # Zeiten vorab umrechnen&lt;br /&gt;
    my @times = map { time_str2num($_-&amp;gt;[1]) } @$arr;&lt;br /&gt;
    my $oldest   = $times[0];&lt;br /&gt;
    my $timespan = $now - $oldest;&lt;br /&gt;
    return sprintf(&amp;quot;%5.3f&amp;quot;, $val) if $timespan &amp;lt;= 0;&lt;br /&gt;
&lt;br /&gt;
    # Werte zeitgewichtet mitteln (inkl. letztem Segment bis jetzt)&lt;br /&gt;
    my $average = 0;&lt;br /&gt;
    for (my $i = 0; $i &amp;lt; $aLength; $i++) {&lt;br /&gt;
        my $segmentEnd = ($i &amp;lt; $aLength - 1) ? $times[$i+1] : $now;&lt;br /&gt;
        my $diffTime   = $segmentEnd - $times[$i];&lt;br /&gt;
        next if $diffTime &amp;lt;= 0;&lt;br /&gt;
        my $fraction = $diffTime / $timespan;&lt;br /&gt;
        $average += $arr-&amp;gt;[$i][0] * $fraction;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return sprintf(&amp;quot;%5.3f&amp;quot;, $average);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
  1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Funktion movingAverage() aufrufen und nutzen ===&lt;br /&gt;
Bei jedem Aufruf schiebt die obige Funktion den gegenwärtigen Wert sowie den Zeitpunkt eines Readings (zweiter Parameter) ans Ende eines Arrays (maximal 25 Einträge) und wirft dafür den ersten Eintrag heraus. Die Anzahl der Einträge im Array wird so gewählt, dass die Zeitpunkte der Readings größer oder gleich der gewünschten Zeitspanne für die Mittelung sind. Über diese Werte wird dann gemittelt und der Wert zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Zur Anwendung empfiehlt sich, dem gewünschten Device ein userReading zu geben:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr devicename userReadings readingname.av {movingAverage(&amp;quot;devicename&amp;quot;,&amp;quot;readingname&amp;quot;,zeitspanne)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieses wird dann automatisch bei jedem neuen Wert des Readings mit dem neuen gleitenden Mittelwert über die vergangene Zeitspanne gefüllt.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Das Reading &amp;quot;pressure&amp;quot; des Devices &amp;quot;A.OWB&amp;quot; wird alle 5 Minuten aktualisiert. Das Device bekommt nun ein userReading:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr A.OWB userReadings pressure.av {movingAverage(&amp;quot;A.OWB&amp;quot;,&amp;quot;pressure&amp;quot;,1800)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieses schreibt bei jeder Aktualisierung von &amp;quot;pressure&amp;quot; einen neuen Eintrag in sein Array - allerdings maximal sechs Werte, dann wird immer der älteste Eintrag gelöscht. Damit ist sichergestellt, dass diese mindestens 1800 Sekunden abdecken - also sechs Einträge.&lt;br /&gt;
&lt;br /&gt;
=== Funktion movingAverageT() aufrufen und nutzen ===&lt;br /&gt;
Bei jedem Aufruf schiebt die Funktion &#039;&#039;movingAverageT&#039;&#039; den gegenwärtigen Wert sowie den Zeitpunkt eines Readings (zweiter Parameter) ans Ende eines Arrays und verwirft Einträge die älter als das Zeitlimit aus dem dritten Parameter sind. Die Anzahl der Einträge im Array ist nicht limitiert und es wird nur anhand des Zeitstempels entschieden, ob ein Wert im Array verbleibt. Über die Werte im Array wird dann gemittelt und der Wert zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Zur Anwendung empfiehlt sich, dem gewünschten Device ein userReading zu geben:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr devicename userReadings readingname.av {movingAverageT(&amp;quot;devicename&amp;quot;, &amp;quot;readingname&amp;quot;, Zeitspanne_in_Sekunden)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieses wird dann automatisch bei jedem neuen Wert des Readings mit dem neuen gleitenden Mittelwert über die vergangene Zeitspanne gefüllt.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Das Reading &amp;quot;pressure&amp;quot; des Devices &amp;quot;A.OWB&amp;quot; wird alle 5 Minuten aktualisiert. Das Device bekommt nun ein userReading:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr A.OWB userReadings pressure.av {movingAverageT(&amp;quot;A.OWB&amp;quot;, &amp;quot;pressure&amp;quot;, 1800)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieses schreibt bei jeder Aktualisierung von &amp;quot;pressure&amp;quot; einen neuen Eintrag an das ende seines Array und löscht zu alte Werte am Anfang des Array wieder heraus.&lt;br /&gt;
&lt;br /&gt;
== Gleitender Mittelwert aus Log-Dateien ==&lt;br /&gt;
=== Subroutine myAverage() in 99_MyUtils anlegen ===&lt;br /&gt;
Zuerst muss die Subroutine myAverage in die 99_MyUtils übernommen werden. Wenn noch nicht vorhanden, sollte die Datei dann so aussehen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
 package main;&lt;br /&gt;
  use strict;&lt;br /&gt;
  use warnings;&lt;br /&gt;
  sub&lt;br /&gt;
  MyUtils_Initialize($$)&lt;br /&gt;
  {&lt;br /&gt;
   my ($hash) = @_;&lt;br /&gt;
  }&lt;br /&gt;
  ##########################################################&lt;br /&gt;
  # myAverage&lt;br /&gt;
  # berechnet den Mittelwert aus LogFiles über einen beliebigen Zeitraum&lt;br /&gt;
  # Args: $offset = Offset in Sekunden&lt;br /&gt;
  #		$logfile = LogFile, z.B. DbLog Device&lt;br /&gt;
  #     $cspec = column-spec, z.B. 4:temp:: (4.Spalte, Zeile enthält temp)&lt;br /&gt;
sub myAverage($$$) {&lt;br /&gt;
   my ($offset,$logfile,$cspec) = @_;&lt;br /&gt;
   my $period_s = strftime &amp;quot;%Y-%m-%d\x5f%H:%M:%S&amp;quot;, localtime(time-$offset);&lt;br /&gt;
   my $period_e = strftime &amp;quot;%Y-%m-%d\x5f%H:%M:%S&amp;quot;, localtime;&lt;br /&gt;
   my $oll = $attr{global}{verbose};&lt;br /&gt;
   $attr{global}{verbose} = 0; &lt;br /&gt;
   my @logdata = split(&amp;quot;\n&amp;quot;, fhem(&amp;quot;get $logfile - - $period_s $period_e $cspec&amp;quot;));&lt;br /&gt;
   $attr{global}{verbose} = $oll; &lt;br /&gt;
   my ($cnt, $cum, $avg) = (0)x3;&lt;br /&gt;
   foreach (@logdata){&lt;br /&gt;
    my @line = split(&amp;quot; &amp;quot;, $_);&lt;br /&gt;
    if(defined $line[1] &amp;amp;&amp;amp; $line[1] ne &amp;quot;&amp;quot;){&lt;br /&gt;
     $cnt += 1;&lt;br /&gt;
     $cum += $line[1];&lt;br /&gt;
    }&lt;br /&gt;
   }&lt;br /&gt;
   if(&amp;quot;$cnt&amp;quot; &amp;gt; 0){$avg = sprintf(&amp;quot;%0.1f&amp;quot;, $cum/$cnt)};&lt;br /&gt;
   Log 4, (&amp;quot;myAverage: File: $logfile, Field: $cspec, Period: $period_s bis $period_e, Count: $cnt, Cum: $cum, Average: $avg&amp;quot;);&lt;br /&gt;
   return $avg;&lt;br /&gt;
  }&lt;br /&gt;
  ##########################################################&lt;br /&gt;
  1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Funktion myAverage() aufrufen und nutzen ===&lt;br /&gt;
Jetzt kann die Funktion &amp;lt;code&amp;gt;myAverage(&amp;amp;lt;offset&amp;amp;gt;, &amp;amp;lt;logfile&amp;amp;gt;, &amp;amp;lt;column-spec&amp;amp;gt;)&amp;lt;/code&amp;gt; ganz normal auf der FHEM-Konsole (telnet localhost 7072) genutzt werden. Hier z.B. die Ausgabe des Mittelwertes des Windsensors, eines KS300, über die letzten drei Stunden (Angabe in 3*3600s) oder der Temperaturmittelwert der letzten 24 Stunden.&lt;br /&gt;
&lt;br /&gt;
Mit FileLog:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;fhem&amp;amp;gt; {myAverage(&amp;amp;quot;10800&amp;amp;quot;, &amp;amp;quot;FileLog_KS300&amp;amp;quot;, &amp;amp;quot;8:::&amp;amp;quot;)}&lt;br /&gt;
  6.3&lt;br /&gt;
  fhem&amp;amp;gt; {myAverage(&amp;amp;quot;86400&amp;amp;quot;, &amp;amp;quot;FileLog_KS300&amp;amp;quot;, &amp;amp;quot;4:::&amp;amp;quot;)}&lt;br /&gt;
  -2.7&lt;br /&gt;
  fhem&amp;amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit DbLog:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;fhem&amp;amp;gt; {myAverage(&amp;amp;quot;10800&amp;amp;quot;, &amp;amp;quot;myDbLog&amp;amp;quot;, &amp;amp;quot;KS300:wind::&amp;amp;quot;)}&lt;br /&gt;
  5.9&lt;br /&gt;
  fhem&amp;amp;gt; {myAverage(&amp;amp;quot;86400&amp;amp;quot;, &amp;amp;quot;myDbLog&amp;amp;quot;, &amp;amp;quot;KS300:temperature::&amp;amp;quot;)}&lt;br /&gt;
  -2.7&lt;br /&gt;
  fhem&amp;amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ähnlich ist die Verwendung in perl-Code in der [[Konfiguration|fhem.cfg]]:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;my $avg = myAverage(&amp;amp;quot;10800&amp;amp;quot;, &amp;amp;quot;FileLog_KS300&amp;amp;quot;, &amp;amp;quot;8:::&amp;amp;quot;);;\&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
Ein [[notify]], welches bei jedem neuen Sensorwert die Mitteltemperatur der letzten 24 Stunden als Event &amp;quot;average-temp xy&amp;quot; ins Logfile schreibt, sieht dann wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;define KS300_T_notify notify KS300:temperature.* {\&lt;br /&gt;
   fhem(&#039;trigger KS300 average-temp: &#039;.myAverage(&amp;amp;quot;86400&amp;amp;quot;, &amp;amp;quot;FileLog_KS300&amp;amp;quot;, &amp;amp;quot;4:::&amp;amp;quot;));;\&lt;br /&gt;
  }&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&amp;lt;!-- korrigiert lt. http://forum.fhem.de/index.php/topic,11622.msg170331.html#msg170331 --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analog dazu wäre mit den Winddaten zu verfahren.&lt;br /&gt;
&lt;br /&gt;
== Gleitender Mittelwert und Minimum und Maximum aus Log-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Manchmal interessieren einem auch Minimum und Maximum Werte über einen bestimmten Zeitraum, die Funktion &#039;&#039;myAverage&#039;&#039; kann einfach entsprechend abgeändert werden:&lt;br /&gt;
&lt;br /&gt;
=== Subroutine avgMinMaxAusLog() in 99_MyUtils anlegen ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
  # Berechnet den Mittelwert, Minimum und Maximum aus LogFiles über einen beliebigen Zeitraum&lt;br /&gt;
  # Args: $offset = Offset in Sekunden&lt;br /&gt;
  #		$logfile = LogFile, z.B. DbLog Device&lt;br /&gt;
  #		$cspec = column-spec, siehe https://fhem.de/commandref_DE.html#DbLog -&amp;gt; get&lt;br /&gt;
  sub avgMinMaxAusLog($$$) {&lt;br /&gt;
  	my($offset, $logfile, $cspec) = @_;&lt;br /&gt;
  	my $period_s = strftime &amp;quot;%Y-%m-%d\x5f%H:%M:%S&amp;quot;, localtime(time - $offset);&lt;br /&gt;
  	my $period_e = strftime &amp;quot;%Y-%m-%d\x5f%H:%M:%S&amp;quot;, localtime;&lt;br /&gt;
  	my $oll = $attr { global } { verbose };&lt;br /&gt;
  	$attr { global } { verbose } = 0;&lt;br /&gt;
  	my @logdata = split(&amp;quot;\n&amp;quot;, fhem(&amp;quot;get $logfile - - $period_s $period_e $cspec&amp;quot;));&lt;br /&gt;
  	$attr { global } { verbose } = $oll;&lt;br /&gt;
  	my ($cnt, $cum, $avg, $min, $max) = (0)x5;&lt;br /&gt;
  	foreach(@logdata) {&lt;br /&gt;
  		my @line = split(&amp;quot; &amp;quot;, $_);&lt;br /&gt;
  		if (defined $line[1] &amp;amp;&amp;amp; $line[1] ne &amp;quot;&amp;quot;) {&lt;br /&gt;
  			$cnt += 1;&lt;br /&gt;
  			$cum += $line[1];&lt;br /&gt;
  			# Beim ersten Eintrag min/max setzen&lt;br /&gt;
  			if ($cnt == 1) {&lt;br /&gt;
  				$min = $line[1];&lt;br /&gt;
  				$max = $line[1];&lt;br /&gt;
  			# Bei den darauffolgenden Einträgen $min/max ermitteln&lt;br /&gt;
  			} else {&lt;br /&gt;
  				if ($line[1] &amp;lt; $min) { $min = $line[1]; }&lt;br /&gt;
  				if ($line[1] &amp;gt; $max) { $max = $line[1]; }&lt;br /&gt;
  			}&lt;br /&gt;
  		}&lt;br /&gt;
  	}&lt;br /&gt;
  	if (&amp;quot;$cnt&amp;quot; &amp;gt; 0) {&lt;br /&gt;
  		$avg = sprintf(&amp;quot;%0.1f&amp;quot;, $cum / $cnt)&lt;br /&gt;
  	};&lt;br /&gt;
  	Log 4, (&amp;quot;avgMinMaxAusLog: File: $logfile, Field: $cspec, Period: $period_s bis $period_e, Count: $cnt, Cum: $cum, Average: $avg, Minimum: $min, Maximum: $max&amp;quot;);&lt;br /&gt;
  	return ($avg, $min, $max);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Anwendung wie oben. Statt nur dem Mittelwert wird eine Liste zurückgeliefert mit Mittelwert, Minimum und Maximum. Verwendung z.B. wie folgt:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;my ($avg, $min, $max) = myAverage(&amp;amp;quot;10800&amp;amp;quot;, &amp;amp;quot;FileLog_KS300&amp;amp;quot;, &amp;amp;quot;8:::&amp;amp;quot;);;\&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Eigenes Filelog und Plot ==&lt;br /&gt;
==== Definitionen ====&lt;br /&gt;
Ich schreibe ein komplett eigenes Logfile und lasse über eine spezielle gplot-Definition die Temperatur- und Winddaten loggen und plotten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
define KS300_W dummy&lt;br /&gt;
  define KS300_W_notify notify KS300:wind.* {\&lt;br /&gt;
   my $avg = myAverage(&amp;amp;quot;10800&amp;amp;quot;, &amp;amp;quot;FileLog_KS300&amp;amp;quot;, &amp;amp;quot;8:::&amp;amp;quot;);;\&lt;br /&gt;
   my $event = sprintf(&amp;amp;quot;Wc:&amp;amp;#160;%0.1f Wd:&amp;amp;#160;%0.1f Wm:&amp;amp;#160;%0.1f Wlh:&amp;amp;#160;%0.1f&amp;amp;quot;, ReadingsVal(&amp;amp;quot;KS300&amp;amp;quot;,&amp;amp;quot;wind&amp;amp;quot;,&amp;amp;quot;0&amp;amp;quot;), ReadingsVal(&amp;amp;quot;KS300&amp;amp;quot;,&amp;amp;quot;W_avg_day&amp;amp;quot;,&amp;amp;quot;0&amp;amp;quot;), ReadingsVal(&amp;amp;quot;KS300&amp;amp;quot;,&amp;amp;quot;W_avg_month&amp;amp;quot;,&amp;amp;quot;0&amp;amp;quot;), $avg);;\&lt;br /&gt;
   Log 2, (&amp;amp;quot;K300_W: Event: $event&amp;amp;quot;);;\&lt;br /&gt;
   fhem(&amp;amp;quot;trigger KS300_W data: $event&amp;amp;quot;);;\&lt;br /&gt;
   fhem(&amp;amp;quot;trigger KS300 average-wind: $avg&amp;amp;quot;);;\&lt;br /&gt;
  }&lt;br /&gt;
  define FileLog_KS300_W FileLog ./log/KS300_W-%Y.log KS300_W.*&lt;br /&gt;
  attr FileLog_KS300_W logtype text&lt;br /&gt;
  attr FileLog_KS300_W room Logs&lt;br /&gt;
  define KS300_TH dummy&lt;br /&gt;
  define KS300_TH_notify notify KS300:temperature.* {\&lt;br /&gt;
   my $avg = myAverage(&amp;amp;quot;86400&amp;amp;quot;, &amp;amp;quot;FileLog_KS300&amp;amp;quot;, &amp;amp;quot;4:::&amp;amp;quot;);;\&lt;br /&gt;
   my $event = sprintf(&amp;amp;quot;Tc:&amp;amp;#160;%0.1f Hc:&amp;amp;#160;%0.1f Tavg:&amp;amp;#160;%0.1f&amp;amp;quot;, ReadingsVal(&amp;amp;quot;KS300&amp;amp;quot;,&amp;amp;quot;temperature&amp;amp;quot;,&amp;amp;quot;0&amp;amp;quot;), ReadingsVal(&amp;amp;quot;KS300&amp;amp;quot;,&amp;amp;quot;humidity&amp;amp;quot;,&amp;amp;quot;0&amp;amp;quot;), $avg);;\&lt;br /&gt;
   Log 2, (&amp;amp;quot;K300_TH: Event: $event&amp;amp;quot;);;\&lt;br /&gt;
   fhem(&amp;amp;quot;trigger KS300_TH data: $event&amp;amp;quot;);;\&lt;br /&gt;
   fhem(&#039;trigger KS300 average-temp: &#039;.$avg);;\&lt;br /&gt;
  }&lt;br /&gt;
  define FileLog_KS300_TH FileLog ./log/KS300_TH-%Y.log KS300_TH.*&lt;br /&gt;
  attr FileLog_KS300_TH logtype text&lt;br /&gt;
  attr FileLog_KS300_TH room Logs&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
Die Definitionen für die Plots schauen so aus:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;define weblink_KS300_W weblink fileplot FileLog_KS300_W:ks300_wind:CURRENT&lt;br /&gt;
  attr weblink_KS300_W label &amp;amp;quot;Wind - Min: $data{min1}, Max: $data{max1}, Last: $data{currval1}&amp;amp;quot;&lt;br /&gt;
  attr weblink_KS300_W room Dachterasse&lt;br /&gt;
  define weblink_KS300_TH weblink fileplot FileLog_KS300_TH:ks300_temphum:CURRENT&lt;br /&gt;
  attr weblink_KS300_TH label &amp;amp;quot;Temperatur/Feuchte - Min: $data{min1}, Max: $data{max1}, Last: $data{currval1}&amp;amp;quot;&lt;br /&gt;
  attr weblink_KS300_TH room Dachterasse&lt;br /&gt;
  define weblink_KS300_R weblink fileplot FileLog_KS300:ks300_rain10:CURRENT&lt;br /&gt;
  attr weblink_KS300_R label &amp;amp;quot;Regen - Min: $data{min1}, Max: $data{max1}, Last: $data{currval1}&amp;amp;quot;&lt;br /&gt;
  attr weblink_KS300_R room Dachterasse&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Angepasste gplot-Dateien ====&lt;br /&gt;
Nun noch die speziellen gplot-Dateien.&lt;br /&gt;
ks300_wind.gplot&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
  ############################&lt;br /&gt;
  # Display the wind values of a KS300.&lt;br /&gt;
  # Corresponding FileLog definition:&lt;br /&gt;
  # define &amp;amp;lt;filelogname&amp;amp;gt; FileLog ./log/KS300_W-%Y.log KS300_W.*&lt;br /&gt;
  set terminal png transparent size &amp;amp;lt;SIZE&amp;amp;gt; crop&lt;br /&gt;
  set output &#039;&amp;amp;lt;OUT&amp;amp;gt;.png&#039;&lt;br /&gt;
  set xdata time&lt;br /&gt;
  set timefmt &amp;amp;quot;%Y-%m-%d_%H:%M:%S&amp;amp;quot;&lt;br /&gt;
  set xlabel &amp;amp;quot; &amp;amp;quot;&lt;br /&gt;
  set ytics nomirror&lt;br /&gt;
  #set y2tics&lt;br /&gt;
  set ytics&lt;br /&gt;
  set title &#039;&amp;amp;lt;L1&amp;amp;gt;&#039;&lt;br /&gt;
  set grid xtics ytics&lt;br /&gt;
  set yrange [0:]&lt;br /&gt;
  set y2range [0:]&lt;br /&gt;
  set ylabel &amp;amp;quot;Wind (aktuell) [km/h]&amp;amp;quot;&lt;br /&gt;
  set y2label &amp;amp;quot;Wind (gemittelt) [km/h]&amp;amp;quot;&lt;br /&gt;
  set format y2 &amp;amp;quot;%0.1f&amp;amp;quot;&lt;br /&gt;
  #FileLog 5:Wc\x3a:0:&lt;br /&gt;
  #FileLog 11:Wlh\x3a:0:&lt;br /&gt;
  plot &amp;amp;quot;&amp;amp;lt;IN&amp;amp;gt;&amp;amp;quot; using 1:5 ls l7fill axes x1y1 title &#039;Wind (aktuell)&#039; with lines,\&lt;br /&gt;
     &amp;amp;quot;&amp;amp;lt;IN&amp;amp;gt;&amp;amp;quot; using 1:11 ls l5 axes x1y2 title &#039;Wind (3h-Mittel)&#039; with steps,\&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ks300_temphum.gplot&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
  ############################&lt;br /&gt;
  # Display the Temperature und Humidity values of a KS300.&lt;br /&gt;
  # Corresponding FileLog definition:&lt;br /&gt;
  # define &amp;amp;lt;filelogname&amp;amp;gt; FileLog ./log/KS300_TH-%Y.log KS300_TH.*&lt;br /&gt;
  set terminal png transparent size &amp;amp;lt;SIZE&amp;amp;gt; crop&lt;br /&gt;
  set output &#039;&amp;amp;lt;OUT&amp;amp;gt;.png&#039;&lt;br /&gt;
  set xdata time&lt;br /&gt;
  set timefmt &amp;amp;quot;%Y-%m-%d_%H:%M:%S&amp;amp;quot;&lt;br /&gt;
  set xlabel &amp;amp;quot; &amp;amp;quot;&lt;br /&gt;
  set ytics nomirror&lt;br /&gt;
  set y2tics&lt;br /&gt;
  #set ytics&lt;br /&gt;
  set title &#039;&amp;amp;lt;L1&amp;amp;gt;&#039;&lt;br /&gt;
  set grid xtics y2tics&lt;br /&gt;
  set y2label &amp;amp;quot;Temperatur [°C]&amp;amp;quot;&lt;br /&gt;
  set ylabel &amp;amp;quot;Luftfeuchte [%]&amp;amp;quot;&lt;br /&gt;
  #FileLog 5:Tc\x3a:0:&lt;br /&gt;
  #FileLog 9:Tavg\x3a:0:&lt;br /&gt;
  #FileLog 7:Hc\x3a:0:&lt;br /&gt;
  plot &amp;amp;quot;&amp;amp;lt;IN&amp;amp;gt;&amp;amp;quot; using 1:5 axes x1y2 ls l0 title &#039;Temperatur&#039; with lines,\&lt;br /&gt;
     &amp;amp;quot;&amp;amp;lt;IN&amp;amp;gt;&amp;amp;quot; using 1:9 axes x1y2 ls l10 title &#039;Temperatur (24h-Mittel)&#039; with lines,\&lt;br /&gt;
     &amp;amp;quot;&amp;amp;lt;IN&amp;amp;gt;&amp;amp;quot; using 1:7 axes x1y1 ls l2fill title &#039;Luftfeuchte&#039; with lines&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Den Plot für den Regen habe ich noch um Punkt-Plots für den israining-Status erweitert, so dass Punkte bei &amp;quot;yes&amp;quot; geplottet werden.&lt;br /&gt;
&lt;br /&gt;
ks300_rain10.gplot&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
  ############################&lt;br /&gt;
  # Display the Rain values of a KS300.&lt;br /&gt;
  # Corresponding FileLog definition:&lt;br /&gt;
  # define &amp;amp;lt;filelogname&amp;amp;gt; FileLog ./log/KS300-%Y.log KS300:T:.*&lt;br /&gt;
  set terminal png transparent size &amp;amp;lt;SIZE&amp;amp;gt; crop&lt;br /&gt;
  set output &#039;&amp;amp;lt;OUT&amp;amp;gt;.png&#039;&lt;br /&gt;
  set xdata time&lt;br /&gt;
  set timefmt &amp;amp;quot;%Y-%m-%d_%H:%M:%S&amp;amp;quot;&lt;br /&gt;
  set xlabel &amp;amp;quot; &amp;amp;quot;&lt;br /&gt;
  set ytics nomirror&lt;br /&gt;
  #set y2tics&lt;br /&gt;
  set ytics&lt;br /&gt;
  set title &#039;&amp;amp;lt;L1&amp;amp;gt;&#039;&lt;br /&gt;
  set grid xtics ytics&lt;br /&gt;
  set y2range [0.5:1.5]&lt;br /&gt;
  set y2tics (&amp;amp;quot;&amp;amp;quot; 0, &amp;amp;quot;Regen&amp;amp;quot; 1)&lt;br /&gt;
  set pointsize 7&lt;br /&gt;
  set ylabel &amp;amp;quot;Regen (l/m²)&amp;amp;quot;&lt;br /&gt;
  set yrange [0:]&lt;br /&gt;
  # Computing Rain/h and Rain/d values by accumulating the changes.&lt;br /&gt;
  #FileLog 10:IR\x3a:0:delta-h&lt;br /&gt;
  #FileLog 10:IR\x3a:0:delta-d&lt;br /&gt;
  #FileLog 12:IR:0:$fld[11]=~&amp;amp;quot;yes&amp;amp;quot;?1:0&lt;br /&gt;
  plot &amp;amp;quot;&amp;amp;lt;grep -v avg_ &amp;amp;lt;IN&amp;amp;gt; | perl -ane &#039;\&lt;br /&gt;
      @a = split(\&amp;amp;quot;[_:]\&amp;amp;quot;, $F[0]);\&lt;br /&gt;
      if(defined($lh) &amp;amp;amp;&amp;amp;amp; $lh ne $a[1])\&lt;br /&gt;
       { printf(\&amp;amp;quot;${ld}_$lh:30:00&amp;amp;#160;%f\n\&amp;amp;quot;, $hv); $hv = 0; }\&lt;br /&gt;
      if($lv) { $hv += ($F[9]-$lv); }\&lt;br /&gt;
      $lh = $a[1]; $ld = $a[0]; $lv = $F[9];\&lt;br /&gt;
      END { printf(\&amp;amp;quot;${ld}_$lh:30:00&amp;amp;#160;%f\n\&amp;amp;quot;, $hv) }&#039;&amp;amp;quot;\&lt;br /&gt;
     using 1:2 axes x1y1 ls l1fill title &#039;Regen pro Stunde&#039; with histeps,\&lt;br /&gt;
     &amp;amp;quot;&amp;amp;lt;grep -v avg_ &amp;amp;lt;IN&amp;amp;gt; | perl -ane &#039;\&lt;br /&gt;
      @a = split(\&amp;amp;quot;[_]\&amp;amp;quot;, $F[0]);\&lt;br /&gt;
      if(defined($ld) &amp;amp;amp;&amp;amp;amp; $ld ne $a[0]) {\&lt;br /&gt;
       printf(\&amp;amp;quot;${ld}_12:00:00&amp;amp;#160;%f\n\&amp;amp;quot;, $dv); $dv = 0; }\&lt;br /&gt;
       if($lv) { $dv += ($F[9]-$lv); }\&lt;br /&gt;
       $ld = $a[0]; $lv = $F[9];\&lt;br /&gt;
       END {printf(\&amp;amp;quot;${ld}_12:00:00&amp;amp;#160;%f\n\&amp;amp;quot;, $dv)}&#039;&amp;amp;quot;\&lt;br /&gt;
     using 1:2 axes x1y1 ls l7 title &#039;Regen pro Tag&#039; with histeps&lt;br /&gt;
     &amp;amp;quot;&amp;amp;lt;grep -v avg_ &amp;amp;lt;IN&amp;amp;gt; | awk &#039;{print $1, $12==\&amp;amp;quot;yes\&amp;amp;quot;? 1&amp;amp;#160;: 0; }&#039;&amp;amp;quot;\&lt;br /&gt;
     using 1:2 axes x1y2 ls l11fill title &#039;Regen aktuell&#039; with points&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Eventuell müssen in den gplots noch die Linienstile/Farben den eigenen Bedürfnissen angepasst werden. Ich verwende hier eigene Farben und Füllungen.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:Logging]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Datei:SVG_Plot_von_Temperaturen_und_dem_Mittelwert.png&amp;diff=40388</id>
		<title>Datei:SVG Plot von Temperaturen und dem Mittelwert.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Datei:SVG_Plot_von_Temperaturen_und_dem_Mittelwert.png&amp;diff=40388"/>
		<updated>2025-09-21T10:15:48Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;SVG Plot von Temperaturen und dem Mittelwert&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Gleitende_Mittelwerte_berechnen_und_loggen&amp;diff=40387</id>
		<title>Gleitende Mittelwerte berechnen und loggen</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Gleitende_Mittelwerte_berechnen_und_loggen&amp;diff=40387"/>
		<updated>2025-09-21T10:13:50Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Aktualisierte Version der Funktion AverageMovingT&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Für verschiedene Wettersensoren ist es oftmals wünschenswert, gleitende Mittelwerte (fälschlich auch als &amp;quot;relative&amp;quot; Mittelwerte bezeichnet) über ein bestimmtes Zeitfenster auszugeben oder in den Logs zu speichern und zu plotten. Beispiele:&lt;br /&gt;
* Die momentanen Winddaten eines KS300 sind oft stark schwankend. Hier wäre ein Mittelwert z.B. der letzten 2-3 Stunden interessant. &lt;br /&gt;
* Für die Außentemperatur ist anhand eines Mittelwertes der letzten 24 Stunden evtl. ein gewisser Trend der generellen Entwicklung der Temperatur ableitbar.&lt;br /&gt;
&lt;br /&gt;
Mit Modulen wie &#039;&#039;rain&#039;&#039; oder &#039;&#039;average&#039;&#039; ist konzeptbedingt nur ein Mittelwert von fixen Zeiträumen (aktueller Tag, Monat, etc.) möglich. &lt;br /&gt;
&lt;br /&gt;
Im Nachfolgenden werden deshalb zwei Programme vorgestellt, die solche gleitenden Mittelwerte bereitstellen. Beide Programme werden in die Datei [[99 myUtils anlegen|99_MyUtils.pm]] eingetragen und dann von FHEM aufgerufen. Das erste Programm macht dies für beliebige Readings beliebiger Devices, das zweite Programm greift auf beliebige [[FileLog|File-]] oder [[DbLog|DbLogs]] zurück.&lt;br /&gt;
&lt;br /&gt;
== Gleitender Mittelwert für beliebige Readings ==&lt;br /&gt;
=== Subroutine movingAverage in 99_MyUtils anlegen ===&lt;br /&gt;
Es werden die Funktionen &#039;&#039;movingAverage&#039;&#039; und &#039;&#039;movingAverageT&#039;&#039; in die 99_MyUtils Datei übernommen werden. Wenn noch nicht vorhanden, sollte die Datei dann so aussehen, der übliche Speicherort ist &#039;&#039;/opt/fhem/FHEM/99_MyUtils.pm&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;package main;&lt;br /&gt;
  use strict;&lt;br /&gt;
  use warnings;&lt;br /&gt;
  sub&lt;br /&gt;
  MyUtils_Initialize($$)&lt;br /&gt;
  {&lt;br /&gt;
   my ($hash) = @_;&lt;br /&gt;
  }&lt;br /&gt;
  ###############################################################################&lt;br /&gt;
  #&lt;br /&gt;
  #  Moving average &lt;br /&gt;
  #&lt;br /&gt;
  #  Aufruf: movingAverage(devicename,readingname,zeitspanne in s)&lt;br /&gt;
  #&lt;br /&gt;
  ###############################################################################&lt;br /&gt;
  &lt;br /&gt;
  sub movingAverage($$$){&lt;br /&gt;
     my ($name,$reading,$avtime) = @_;&lt;br /&gt;
     my $hash = $defs{$name};&lt;br /&gt;
     my @new = my ($val,$time) = ($hash-&amp;gt;{READINGS}{$reading}{VAL},$hash-&amp;gt;{READINGS}{$reading}{TIME});&lt;br /&gt;
     my ($cyear, $cmonth, $cday, $chour, $cmin, $csec) = $time =~ /(\d+)-(\d+)-(\d+)\s(\d+):(\d+):(\d+)/;&lt;br /&gt;
     my $ctime = $csec+60*$cmin+3600*$chour;&lt;br /&gt;
     my $num;&lt;br /&gt;
     my $arr;&lt;br /&gt;
     #-- initialize if requested&lt;br /&gt;
     if( ($avtime eq &amp;quot;-1&amp;quot;) ){&lt;br /&gt;
       $hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}=undef;&lt;br /&gt;
     }&lt;br /&gt;
     #-- test for existence&lt;br /&gt;
     if( !$hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}){&lt;br /&gt;
        #Log 1,&amp;quot;ARRAY CREATED&amp;quot;;&lt;br /&gt;
        push(@{$hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}},\@new);&lt;br /&gt;
        $num = 1;&lt;br /&gt;
        $arr=\@{$hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}};&lt;br /&gt;
     } else {&lt;br /&gt;
        $num = int(@{$hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}});&lt;br /&gt;
        $arr=\@{$hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}};&lt;br /&gt;
        my $starttime = $arr-&amp;gt;[0][1];&lt;br /&gt;
        my ($syear, $smonth, $sday, $shour, $smin, $ssec) = $starttime =~ /(\d+)-(\d+)-(\d+)\s(\d+):(\d+):(\d+)/;&lt;br /&gt;
        my $stime = $ssec+60*$smin+3600*$shour;&lt;br /&gt;
        #-- correct for daybreak&lt;br /&gt;
        $stime-=86400 &lt;br /&gt;
          if( $stime &amp;gt; $ctime);&lt;br /&gt;
        if( ($num &amp;lt; 25)&amp;amp;&amp;amp;( ($ctime-$stime)&amp;lt;$avtime) ){&lt;br /&gt;
          #Log 1,&amp;quot;ARRAY has $num elements, adding another one&amp;quot;;&lt;br /&gt;
          push(@{$hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}},\@new);&lt;br /&gt;
        }else{&lt;br /&gt;
          shift(@{$hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}});&lt;br /&gt;
          push(@{$hash-&amp;gt;{READINGS}{$reading}{&amp;quot;history&amp;quot;}},\@new);&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      #-- output and average&lt;br /&gt;
      my $average = 0;&lt;br /&gt;
      for(my $i=0;$i&amp;lt;$num;$i++){&lt;br /&gt;
        $average+=$arr-&amp;gt;[$i][0];&lt;br /&gt;
        Log 4,&amp;quot;[$name moving average] Value = &amp;quot;.$arr-&amp;gt;[$i][0].&amp;quot; Time = &amp;quot;.$arr-&amp;gt;[$i][1]; &lt;br /&gt;
      }&lt;br /&gt;
      $average=sprintf( &amp;quot;%5.3f&amp;quot;, $average/$num);&lt;br /&gt;
      #--average&lt;br /&gt;
      Log 4,&amp;quot;[$name moving average] calculated over $num values is $average&amp;quot;;  &lt;br /&gt;
      return $average;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
    ###############################################################################&lt;br /&gt;
    #&lt;br /&gt;
    #  Moving average of last X seconds, weighted by time&lt;br /&gt;
    #&lt;br /&gt;
    #  movingAverageT(Devicename, Readingname, HistoryLength)&lt;br /&gt;
    #&lt;br /&gt;
    ###############################################################################&lt;br /&gt;
    sub movingAverageT($$$) {&lt;br /&gt;
    my ($name, $reading, $hSeconds) = @_;&lt;br /&gt;
    my $hash = $defs{$name};&lt;br /&gt;
&lt;br /&gt;
    # aktuelles Value + Timestamp holen&lt;br /&gt;
    my ($val, $time) = (&lt;br /&gt;
        $hash-&amp;gt;{READINGS}{$reading}{VAL},&lt;br /&gt;
        $hash-&amp;gt;{READINGS}{$reading}{TIME}&lt;br /&gt;
    );&lt;br /&gt;
&lt;br /&gt;
    # Gültigkeit prüfen (nur Zahlen erlauben)&lt;br /&gt;
    return undef unless defined $val &amp;amp;&amp;amp; $val =~ /^-?\d+(?:\.\d+)?$/ &amp;amp;&amp;amp; defined $time;&lt;br /&gt;
&lt;br /&gt;
    my @new = ($val, $time);&lt;br /&gt;
    my $histKey = &amp;quot;history.T.$hSeconds&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
    # Wenn kein Zeitraum → direkt aktuellen Wert zurückgeben&lt;br /&gt;
    return sprintf(&amp;quot;%5.3f&amp;quot;, $val) if $hSeconds == 0;&lt;br /&gt;
&lt;br /&gt;
    # History initialisieren, falls nicht vorhanden oder kein Array&lt;br /&gt;
    if (!exists $hash-&amp;gt;{READINGS}{$reading}{$histKey} || ref $hash-&amp;gt;{READINGS}{$reading}{$histKey} ne &#039;ARRAY&#039;) {&lt;br /&gt;
        $hash-&amp;gt;{READINGS}{$reading}{$histKey} = [ \@new ];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    my $arr = $hash-&amp;gt;{READINGS}{$reading}{$histKey};&lt;br /&gt;
&lt;br /&gt;
    # nur hinzufügen, wenn leer oder neuer Zeitstempel&lt;br /&gt;
    if (!@$arr || $time ne $arr-&amp;gt;[-1]-&amp;gt;[1]) {&lt;br /&gt;
        push @$arr, \@new;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    # alte Einträge entfernen&lt;br /&gt;
    my $now = time();&lt;br /&gt;
    while (@$arr &amp;amp;&amp;amp; $now - time_str2num($arr-&amp;gt;[0][1]) &amp;gt; $hSeconds) {&lt;br /&gt;
        shift @$arr;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    my $aLength = scalar @$arr;&lt;br /&gt;
    return sprintf(&amp;quot;%5.3f&amp;quot;, $val) if $aLength &amp;lt;= 1;&lt;br /&gt;
&lt;br /&gt;
    # Zeiten vorab umrechnen&lt;br /&gt;
    my @times = map { time_str2num($_-&amp;gt;[1]) } @$arr;&lt;br /&gt;
    my $oldest   = $times[0];&lt;br /&gt;
    my $timespan = $now - $oldest;&lt;br /&gt;
    return sprintf(&amp;quot;%5.3f&amp;quot;, $val) if $timespan &amp;lt;= 0;&lt;br /&gt;
&lt;br /&gt;
    # Werte zeitgewichtet mitteln (inkl. letztem Segment bis jetzt)&lt;br /&gt;
    my $average = 0;&lt;br /&gt;
    for (my $i = 0; $i &amp;lt; $aLength; $i++) {&lt;br /&gt;
        my $segmentEnd = ($i &amp;lt; $aLength - 1) ? $times[$i+1] : $now;&lt;br /&gt;
        my $diffTime   = $segmentEnd - $times[$i];&lt;br /&gt;
        next if $diffTime &amp;lt;= 0;&lt;br /&gt;
        my $fraction = $diffTime / $timespan;&lt;br /&gt;
        $average += $arr-&amp;gt;[$i][0] * $fraction;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return sprintf(&amp;quot;%5.3f&amp;quot;, $average);&lt;br /&gt;
}&lt;br /&gt;
    &lt;br /&gt;
  1;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Funktion movingAverage() aufrufen und nutzen ===&lt;br /&gt;
Bei jedem Aufruf schiebt die obige Funktion den gegenwärtigen Wert sowie den Zeitpunkt eines Readings (zweiter Parameter) ans Ende eines Arrays (maximal 25 Einträge) und wirft dafür den ersten Eintrag heraus. Die Anzahl der Einträge im Array wird so gewählt, dass die Zeitpunkte der Readings größer oder gleich der gewünschten Zeitspanne für die Mittelung sind. Über diese Werte wird dann gemittelt und der Wert zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Zur Anwendung empfiehlt sich, dem gewünschten Device ein userReading zu geben:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr devicename userReadings readingname.av {movingAverage(&amp;quot;devicename&amp;quot;,&amp;quot;readingname&amp;quot;,zeitspanne)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieses wird dann automatisch bei jedem neuen Wert des Readings mit dem neuen gleitenden Mittelwert über die vergangene Zeitspanne gefüllt.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Das Reading &amp;quot;pressure&amp;quot; des Devices &amp;quot;A.OWB&amp;quot; wird alle 5 Minuten aktualisiert. Das Device bekommt nun ein userReading:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr A.OWB userReadings pressure.av {movingAverage(&amp;quot;A.OWB&amp;quot;,&amp;quot;pressure&amp;quot;,1800)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieses schreibt bei jeder Aktualisierung von &amp;quot;pressure&amp;quot; einen neuen Eintrag in sein Array - allerdings maximal sechs Werte, dann wird immer der älteste Eintrag gelöscht. Damit ist sichergestellt, dass diese mindestens 1800 Sekunden abdecken - also sechs Einträge.&lt;br /&gt;
&lt;br /&gt;
=== Funktion movingAverageT() aufrufen und nutzen ===&lt;br /&gt;
Bei jedem Aufruf schiebt die Funktion &#039;&#039;movingAverageT&#039;&#039; den gegenwärtigen Wert sowie den Zeitpunkt eines Readings (zweiter Parameter) ans Ende eines Arrays und verwirft Einträge die älter als das Zeitlimit aus dem dritten Parameter sind. Die Anzahl der Einträge im Array ist nicht limitiert und es wird nur anhand des Zeitstempels entschieden, ob ein Wert im Array verbleibt. Über die Werte im Array wird dann gemittelt und der Wert zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
Zur Anwendung empfiehlt sich, dem gewünschten Device ein userReading zu geben:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr devicename userReadings readingname.av {movingAverageT(&amp;quot;devicename&amp;quot;, &amp;quot;readingname&amp;quot;, Zeitspanne_in_Sekunden)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieses wird dann automatisch bei jedem neuen Wert des Readings mit dem neuen gleitenden Mittelwert über die vergangene Zeitspanne gefüllt.&lt;br /&gt;
&lt;br /&gt;
Beispiel: Das Reading &amp;quot;pressure&amp;quot; des Devices &amp;quot;A.OWB&amp;quot; wird alle 5 Minuten aktualisiert. Das Device bekommt nun ein userReading:&lt;br /&gt;
:&amp;lt;code&amp;gt;attr A.OWB userReadings pressure.av {movingAverageT(&amp;quot;A.OWB&amp;quot;, &amp;quot;pressure&amp;quot;, 1800)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dieses schreibt bei jeder Aktualisierung von &amp;quot;pressure&amp;quot; einen neuen Eintrag an das ende seines Array und löscht zu alte Werte am Anfang des Array wieder heraus.&lt;br /&gt;
&lt;br /&gt;
== Gleitender Mittelwert aus Log-Dateien ==&lt;br /&gt;
=== Subroutine myAverage() in 99_MyUtils anlegen ===&lt;br /&gt;
Zuerst muss die Subroutine myAverage in die 99_MyUtils übernommen werden. Wenn noch nicht vorhanden, sollte die Datei dann so aussehen:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
 package main;&lt;br /&gt;
  use strict;&lt;br /&gt;
  use warnings;&lt;br /&gt;
  sub&lt;br /&gt;
  MyUtils_Initialize($$)&lt;br /&gt;
  {&lt;br /&gt;
   my ($hash) = @_;&lt;br /&gt;
  }&lt;br /&gt;
  ##########################################################&lt;br /&gt;
  # myAverage&lt;br /&gt;
  # berechnet den Mittelwert aus LogFiles über einen beliebigen Zeitraum&lt;br /&gt;
  # Args: $offset = Offset in Sekunden&lt;br /&gt;
  #		$logfile = LogFile, z.B. DbLog Device&lt;br /&gt;
  #     $cspec = column-spec, z.B. 4:temp:: (4.Spalte, Zeile enthält temp)&lt;br /&gt;
sub myAverage($$$) {&lt;br /&gt;
   my ($offset,$logfile,$cspec) = @_;&lt;br /&gt;
   my $period_s = strftime &amp;quot;%Y-%m-%d\x5f%H:%M:%S&amp;quot;, localtime(time-$offset);&lt;br /&gt;
   my $period_e = strftime &amp;quot;%Y-%m-%d\x5f%H:%M:%S&amp;quot;, localtime;&lt;br /&gt;
   my $oll = $attr{global}{verbose};&lt;br /&gt;
   $attr{global}{verbose} = 0; &lt;br /&gt;
   my @logdata = split(&amp;quot;\n&amp;quot;, fhem(&amp;quot;get $logfile - - $period_s $period_e $cspec&amp;quot;));&lt;br /&gt;
   $attr{global}{verbose} = $oll; &lt;br /&gt;
   my ($cnt, $cum, $avg) = (0)x3;&lt;br /&gt;
   foreach (@logdata){&lt;br /&gt;
    my @line = split(&amp;quot; &amp;quot;, $_);&lt;br /&gt;
    if(defined $line[1] &amp;amp;&amp;amp; $line[1] ne &amp;quot;&amp;quot;){&lt;br /&gt;
     $cnt += 1;&lt;br /&gt;
     $cum += $line[1];&lt;br /&gt;
    }&lt;br /&gt;
   }&lt;br /&gt;
   if(&amp;quot;$cnt&amp;quot; &amp;gt; 0){$avg = sprintf(&amp;quot;%0.1f&amp;quot;, $cum/$cnt)};&lt;br /&gt;
   Log 4, (&amp;quot;myAverage: File: $logfile, Field: $cspec, Period: $period_s bis $period_e, Count: $cnt, Cum: $cum, Average: $avg&amp;quot;);&lt;br /&gt;
   return $avg;&lt;br /&gt;
  }&lt;br /&gt;
  ##########################################################&lt;br /&gt;
  1;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Funktion myAverage() aufrufen und nutzen ===&lt;br /&gt;
Jetzt kann die Funktion &amp;lt;code&amp;gt;myAverage(&amp;amp;lt;offset&amp;amp;gt;, &amp;amp;lt;logfile&amp;amp;gt;, &amp;amp;lt;column-spec&amp;amp;gt;)&amp;lt;/code&amp;gt; ganz normal auf der FHEM-Konsole (telnet localhost 7072) genutzt werden. Hier z.B. die Ausgabe des Mittelwertes des Windsensors, eines KS300, über die letzten drei Stunden (Angabe in 3*3600s) oder der Temperaturmittelwert der letzten 24 Stunden.&lt;br /&gt;
&lt;br /&gt;
Mit FileLog:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;fhem&amp;amp;gt; {myAverage(&amp;amp;quot;10800&amp;amp;quot;, &amp;amp;quot;FileLog_KS300&amp;amp;quot;, &amp;amp;quot;8:::&amp;amp;quot;)}&lt;br /&gt;
  6.3&lt;br /&gt;
  fhem&amp;amp;gt; {myAverage(&amp;amp;quot;86400&amp;amp;quot;, &amp;amp;quot;FileLog_KS300&amp;amp;quot;, &amp;amp;quot;4:::&amp;amp;quot;)}&lt;br /&gt;
  -2.7&lt;br /&gt;
  fhem&amp;amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Mit DbLog:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;fhem&amp;amp;gt; {myAverage(&amp;amp;quot;10800&amp;amp;quot;, &amp;amp;quot;myDbLog&amp;amp;quot;, &amp;amp;quot;KS300:wind::&amp;amp;quot;)}&lt;br /&gt;
  5.9&lt;br /&gt;
  fhem&amp;amp;gt; {myAverage(&amp;amp;quot;86400&amp;amp;quot;, &amp;amp;quot;myDbLog&amp;amp;quot;, &amp;amp;quot;KS300:temperature::&amp;amp;quot;)}&lt;br /&gt;
  -2.7&lt;br /&gt;
  fhem&amp;amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ähnlich ist die Verwendung in perl-Code in der [[Konfiguration|fhem.cfg]]:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;my $avg = myAverage(&amp;amp;quot;10800&amp;amp;quot;, &amp;amp;quot;FileLog_KS300&amp;amp;quot;, &amp;amp;quot;8:::&amp;amp;quot;);;\&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
Ein [[notify]], welches bei jedem neuen Sensorwert die Mitteltemperatur der letzten 24 Stunden als Event &amp;quot;average-temp xy&amp;quot; ins Logfile schreibt, sieht dann wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;define KS300_T_notify notify KS300:temperature.* {\&lt;br /&gt;
   fhem(&#039;trigger KS300 average-temp: &#039;.myAverage(&amp;amp;quot;86400&amp;amp;quot;, &amp;amp;quot;FileLog_KS300&amp;amp;quot;, &amp;amp;quot;4:::&amp;amp;quot;));;\&lt;br /&gt;
  }&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&amp;lt;!-- korrigiert lt. http://forum.fhem.de/index.php/topic,11622.msg170331.html#msg170331 --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analog dazu wäre mit den Winddaten zu verfahren.&lt;br /&gt;
&lt;br /&gt;
== Gleitender Mittelwert und Minimum und Maximum aus Log-Dateien ==&lt;br /&gt;
&lt;br /&gt;
Manchmal interessieren einem auch Minimum und Maximum Werte über einen bestimmten Zeitraum, die Funktion &#039;&#039;myAverage&#039;&#039; kann einfach entsprechend abgeändert werden:&lt;br /&gt;
&lt;br /&gt;
=== Subroutine avgMinMaxAusLog() in 99_MyUtils anlegen ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
  # Berechnet den Mittelwert, Minimum und Maximum aus LogFiles über einen beliebigen Zeitraum&lt;br /&gt;
  # Args: $offset = Offset in Sekunden&lt;br /&gt;
  #		$logfile = LogFile, z.B. DbLog Device&lt;br /&gt;
  #		$cspec = column-spec, siehe https://fhem.de/commandref_DE.html#DbLog -&amp;gt; get&lt;br /&gt;
  sub avgMinMaxAusLog($$$) {&lt;br /&gt;
  	my($offset, $logfile, $cspec) = @_;&lt;br /&gt;
  	my $period_s = strftime &amp;quot;%Y-%m-%d\x5f%H:%M:%S&amp;quot;, localtime(time - $offset);&lt;br /&gt;
  	my $period_e = strftime &amp;quot;%Y-%m-%d\x5f%H:%M:%S&amp;quot;, localtime;&lt;br /&gt;
  	my $oll = $attr { global } { verbose };&lt;br /&gt;
  	$attr { global } { verbose } = 0;&lt;br /&gt;
  	my @logdata = split(&amp;quot;\n&amp;quot;, fhem(&amp;quot;get $logfile - - $period_s $period_e $cspec&amp;quot;));&lt;br /&gt;
  	$attr { global } { verbose } = $oll;&lt;br /&gt;
  	my ($cnt, $cum, $avg, $min, $max) = (0)x5;&lt;br /&gt;
  	foreach(@logdata) {&lt;br /&gt;
  		my @line = split(&amp;quot; &amp;quot;, $_);&lt;br /&gt;
  		if (defined $line[1] &amp;amp;&amp;amp; $line[1] ne &amp;quot;&amp;quot;) {&lt;br /&gt;
  			$cnt += 1;&lt;br /&gt;
  			$cum += $line[1];&lt;br /&gt;
  			# Beim ersten Eintrag min/max setzen&lt;br /&gt;
  			if ($cnt == 1) {&lt;br /&gt;
  				$min = $line[1];&lt;br /&gt;
  				$max = $line[1];&lt;br /&gt;
  			# Bei den darauffolgenden Einträgen $min/max ermitteln&lt;br /&gt;
  			} else {&lt;br /&gt;
  				if ($line[1] &amp;lt; $min) { $min = $line[1]; }&lt;br /&gt;
  				if ($line[1] &amp;gt; $max) { $max = $line[1]; }&lt;br /&gt;
  			}&lt;br /&gt;
  		}&lt;br /&gt;
  	}&lt;br /&gt;
  	if (&amp;quot;$cnt&amp;quot; &amp;gt; 0) {&lt;br /&gt;
  		$avg = sprintf(&amp;quot;%0.1f&amp;quot;, $cum / $cnt)&lt;br /&gt;
  	};&lt;br /&gt;
  	Log 4, (&amp;quot;avgMinMaxAusLog: File: $logfile, Field: $cspec, Period: $period_s bis $period_e, Count: $cnt, Cum: $cum, Average: $avg, Minimum: $min, Maximum: $max&amp;quot;);&lt;br /&gt;
  	return ($avg, $min, $max);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Anwendung wie oben. Statt nur dem Mittelwert wird eine Liste zurückgeliefert mit Mittelwert, Minimum und Maximum. Verwendung z.B. wie folgt:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;my ($avg, $min, $max) = myAverage(&amp;amp;quot;10800&amp;amp;quot;, &amp;amp;quot;FileLog_KS300&amp;amp;quot;, &amp;amp;quot;8:::&amp;amp;quot;);;\&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Eigenes Filelog und Plot ==&lt;br /&gt;
==== Definitionen ====&lt;br /&gt;
Ich schreibe ein komplett eigenes Logfile und lasse über eine spezielle gplot-Definition die Temperatur- und Winddaten loggen und plotten.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
define KS300_W dummy&lt;br /&gt;
  define KS300_W_notify notify KS300:wind.* {\&lt;br /&gt;
   my $avg = myAverage(&amp;amp;quot;10800&amp;amp;quot;, &amp;amp;quot;FileLog_KS300&amp;amp;quot;, &amp;amp;quot;8:::&amp;amp;quot;);;\&lt;br /&gt;
   my $event = sprintf(&amp;amp;quot;Wc:&amp;amp;#160;%0.1f Wd:&amp;amp;#160;%0.1f Wm:&amp;amp;#160;%0.1f Wlh:&amp;amp;#160;%0.1f&amp;amp;quot;, ReadingsVal(&amp;amp;quot;KS300&amp;amp;quot;,&amp;amp;quot;wind&amp;amp;quot;,&amp;amp;quot;0&amp;amp;quot;), ReadingsVal(&amp;amp;quot;KS300&amp;amp;quot;,&amp;amp;quot;W_avg_day&amp;amp;quot;,&amp;amp;quot;0&amp;amp;quot;), ReadingsVal(&amp;amp;quot;KS300&amp;amp;quot;,&amp;amp;quot;W_avg_month&amp;amp;quot;,&amp;amp;quot;0&amp;amp;quot;), $avg);;\&lt;br /&gt;
   Log 2, (&amp;amp;quot;K300_W: Event: $event&amp;amp;quot;);;\&lt;br /&gt;
   fhem(&amp;amp;quot;trigger KS300_W data: $event&amp;amp;quot;);;\&lt;br /&gt;
   fhem(&amp;amp;quot;trigger KS300 average-wind: $avg&amp;amp;quot;);;\&lt;br /&gt;
  }&lt;br /&gt;
  define FileLog_KS300_W FileLog ./log/KS300_W-%Y.log KS300_W.*&lt;br /&gt;
  attr FileLog_KS300_W logtype text&lt;br /&gt;
  attr FileLog_KS300_W room Logs&lt;br /&gt;
  define KS300_TH dummy&lt;br /&gt;
  define KS300_TH_notify notify KS300:temperature.* {\&lt;br /&gt;
   my $avg = myAverage(&amp;amp;quot;86400&amp;amp;quot;, &amp;amp;quot;FileLog_KS300&amp;amp;quot;, &amp;amp;quot;4:::&amp;amp;quot;);;\&lt;br /&gt;
   my $event = sprintf(&amp;amp;quot;Tc:&amp;amp;#160;%0.1f Hc:&amp;amp;#160;%0.1f Tavg:&amp;amp;#160;%0.1f&amp;amp;quot;, ReadingsVal(&amp;amp;quot;KS300&amp;amp;quot;,&amp;amp;quot;temperature&amp;amp;quot;,&amp;amp;quot;0&amp;amp;quot;), ReadingsVal(&amp;amp;quot;KS300&amp;amp;quot;,&amp;amp;quot;humidity&amp;amp;quot;,&amp;amp;quot;0&amp;amp;quot;), $avg);;\&lt;br /&gt;
   Log 2, (&amp;amp;quot;K300_TH: Event: $event&amp;amp;quot;);;\&lt;br /&gt;
   fhem(&amp;amp;quot;trigger KS300_TH data: $event&amp;amp;quot;);;\&lt;br /&gt;
   fhem(&#039;trigger KS300 average-temp: &#039;.$avg);;\&lt;br /&gt;
  }&lt;br /&gt;
  define FileLog_KS300_TH FileLog ./log/KS300_TH-%Y.log KS300_TH.*&lt;br /&gt;
  attr FileLog_KS300_TH logtype text&lt;br /&gt;
  attr FileLog_KS300_TH room Logs&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
Die Definitionen für die Plots schauen so aus:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;define weblink_KS300_W weblink fileplot FileLog_KS300_W:ks300_wind:CURRENT&lt;br /&gt;
  attr weblink_KS300_W label &amp;amp;quot;Wind - Min: $data{min1}, Max: $data{max1}, Last: $data{currval1}&amp;amp;quot;&lt;br /&gt;
  attr weblink_KS300_W room Dachterasse&lt;br /&gt;
  define weblink_KS300_TH weblink fileplot FileLog_KS300_TH:ks300_temphum:CURRENT&lt;br /&gt;
  attr weblink_KS300_TH label &amp;amp;quot;Temperatur/Feuchte - Min: $data{min1}, Max: $data{max1}, Last: $data{currval1}&amp;amp;quot;&lt;br /&gt;
  attr weblink_KS300_TH room Dachterasse&lt;br /&gt;
  define weblink_KS300_R weblink fileplot FileLog_KS300:ks300_rain10:CURRENT&lt;br /&gt;
  attr weblink_KS300_R label &amp;amp;quot;Regen - Min: $data{min1}, Max: $data{max1}, Last: $data{currval1}&amp;amp;quot;&lt;br /&gt;
  attr weblink_KS300_R room Dachterasse&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Angepasste gplot-Dateien ====&lt;br /&gt;
Nun noch die speziellen gplot-Dateien.&lt;br /&gt;
ks300_wind.gplot&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
  ############################&lt;br /&gt;
  # Display the wind values of a KS300.&lt;br /&gt;
  # Corresponding FileLog definition:&lt;br /&gt;
  # define &amp;amp;lt;filelogname&amp;amp;gt; FileLog ./log/KS300_W-%Y.log KS300_W.*&lt;br /&gt;
  set terminal png transparent size &amp;amp;lt;SIZE&amp;amp;gt; crop&lt;br /&gt;
  set output &#039;&amp;amp;lt;OUT&amp;amp;gt;.png&#039;&lt;br /&gt;
  set xdata time&lt;br /&gt;
  set timefmt &amp;amp;quot;%Y-%m-%d_%H:%M:%S&amp;amp;quot;&lt;br /&gt;
  set xlabel &amp;amp;quot; &amp;amp;quot;&lt;br /&gt;
  set ytics nomirror&lt;br /&gt;
  #set y2tics&lt;br /&gt;
  set ytics&lt;br /&gt;
  set title &#039;&amp;amp;lt;L1&amp;amp;gt;&#039;&lt;br /&gt;
  set grid xtics ytics&lt;br /&gt;
  set yrange [0:]&lt;br /&gt;
  set y2range [0:]&lt;br /&gt;
  set ylabel &amp;amp;quot;Wind (aktuell) [km/h]&amp;amp;quot;&lt;br /&gt;
  set y2label &amp;amp;quot;Wind (gemittelt) [km/h]&amp;amp;quot;&lt;br /&gt;
  set format y2 &amp;amp;quot;%0.1f&amp;amp;quot;&lt;br /&gt;
  #FileLog 5:Wc\x3a:0:&lt;br /&gt;
  #FileLog 11:Wlh\x3a:0:&lt;br /&gt;
  plot &amp;amp;quot;&amp;amp;lt;IN&amp;amp;gt;&amp;amp;quot; using 1:5 ls l7fill axes x1y1 title &#039;Wind (aktuell)&#039; with lines,\&lt;br /&gt;
     &amp;amp;quot;&amp;amp;lt;IN&amp;amp;gt;&amp;amp;quot; using 1:11 ls l5 axes x1y2 title &#039;Wind (3h-Mittel)&#039; with steps,\&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ks300_temphum.gplot&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
  ############################&lt;br /&gt;
  # Display the Temperature und Humidity values of a KS300.&lt;br /&gt;
  # Corresponding FileLog definition:&lt;br /&gt;
  # define &amp;amp;lt;filelogname&amp;amp;gt; FileLog ./log/KS300_TH-%Y.log KS300_TH.*&lt;br /&gt;
  set terminal png transparent size &amp;amp;lt;SIZE&amp;amp;gt; crop&lt;br /&gt;
  set output &#039;&amp;amp;lt;OUT&amp;amp;gt;.png&#039;&lt;br /&gt;
  set xdata time&lt;br /&gt;
  set timefmt &amp;amp;quot;%Y-%m-%d_%H:%M:%S&amp;amp;quot;&lt;br /&gt;
  set xlabel &amp;amp;quot; &amp;amp;quot;&lt;br /&gt;
  set ytics nomirror&lt;br /&gt;
  set y2tics&lt;br /&gt;
  #set ytics&lt;br /&gt;
  set title &#039;&amp;amp;lt;L1&amp;amp;gt;&#039;&lt;br /&gt;
  set grid xtics y2tics&lt;br /&gt;
  set y2label &amp;amp;quot;Temperatur [°C]&amp;amp;quot;&lt;br /&gt;
  set ylabel &amp;amp;quot;Luftfeuchte [%]&amp;amp;quot;&lt;br /&gt;
  #FileLog 5:Tc\x3a:0:&lt;br /&gt;
  #FileLog 9:Tavg\x3a:0:&lt;br /&gt;
  #FileLog 7:Hc\x3a:0:&lt;br /&gt;
  plot &amp;amp;quot;&amp;amp;lt;IN&amp;amp;gt;&amp;amp;quot; using 1:5 axes x1y2 ls l0 title &#039;Temperatur&#039; with lines,\&lt;br /&gt;
     &amp;amp;quot;&amp;amp;lt;IN&amp;amp;gt;&amp;amp;quot; using 1:9 axes x1y2 ls l10 title &#039;Temperatur (24h-Mittel)&#039; with lines,\&lt;br /&gt;
     &amp;amp;quot;&amp;amp;lt;IN&amp;amp;gt;&amp;amp;quot; using 1:7 axes x1y1 ls l2fill title &#039;Luftfeuchte&#039; with lines&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Den Plot für den Regen habe ich noch um Punkt-Plots für den israining-Status erweitert, so dass Punkte bei &amp;quot;yes&amp;quot; geplottet werden.&lt;br /&gt;
&lt;br /&gt;
ks300_rain10.gplot&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
  ############################&lt;br /&gt;
  # Display the Rain values of a KS300.&lt;br /&gt;
  # Corresponding FileLog definition:&lt;br /&gt;
  # define &amp;amp;lt;filelogname&amp;amp;gt; FileLog ./log/KS300-%Y.log KS300:T:.*&lt;br /&gt;
  set terminal png transparent size &amp;amp;lt;SIZE&amp;amp;gt; crop&lt;br /&gt;
  set output &#039;&amp;amp;lt;OUT&amp;amp;gt;.png&#039;&lt;br /&gt;
  set xdata time&lt;br /&gt;
  set timefmt &amp;amp;quot;%Y-%m-%d_%H:%M:%S&amp;amp;quot;&lt;br /&gt;
  set xlabel &amp;amp;quot; &amp;amp;quot;&lt;br /&gt;
  set ytics nomirror&lt;br /&gt;
  #set y2tics&lt;br /&gt;
  set ytics&lt;br /&gt;
  set title &#039;&amp;amp;lt;L1&amp;amp;gt;&#039;&lt;br /&gt;
  set grid xtics ytics&lt;br /&gt;
  set y2range [0.5:1.5]&lt;br /&gt;
  set y2tics (&amp;amp;quot;&amp;amp;quot; 0, &amp;amp;quot;Regen&amp;amp;quot; 1)&lt;br /&gt;
  set pointsize 7&lt;br /&gt;
  set ylabel &amp;amp;quot;Regen (l/m²)&amp;amp;quot;&lt;br /&gt;
  set yrange [0:]&lt;br /&gt;
  # Computing Rain/h and Rain/d values by accumulating the changes.&lt;br /&gt;
  #FileLog 10:IR\x3a:0:delta-h&lt;br /&gt;
  #FileLog 10:IR\x3a:0:delta-d&lt;br /&gt;
  #FileLog 12:IR:0:$fld[11]=~&amp;amp;quot;yes&amp;amp;quot;?1:0&lt;br /&gt;
  plot &amp;amp;quot;&amp;amp;lt;grep -v avg_ &amp;amp;lt;IN&amp;amp;gt; | perl -ane &#039;\&lt;br /&gt;
      @a = split(\&amp;amp;quot;[_:]\&amp;amp;quot;, $F[0]);\&lt;br /&gt;
      if(defined($lh) &amp;amp;amp;&amp;amp;amp; $lh ne $a[1])\&lt;br /&gt;
       { printf(\&amp;amp;quot;${ld}_$lh:30:00&amp;amp;#160;%f\n\&amp;amp;quot;, $hv); $hv = 0; }\&lt;br /&gt;
      if($lv) { $hv += ($F[9]-$lv); }\&lt;br /&gt;
      $lh = $a[1]; $ld = $a[0]; $lv = $F[9];\&lt;br /&gt;
      END { printf(\&amp;amp;quot;${ld}_$lh:30:00&amp;amp;#160;%f\n\&amp;amp;quot;, $hv) }&#039;&amp;amp;quot;\&lt;br /&gt;
     using 1:2 axes x1y1 ls l1fill title &#039;Regen pro Stunde&#039; with histeps,\&lt;br /&gt;
     &amp;amp;quot;&amp;amp;lt;grep -v avg_ &amp;amp;lt;IN&amp;amp;gt; | perl -ane &#039;\&lt;br /&gt;
      @a = split(\&amp;amp;quot;[_]\&amp;amp;quot;, $F[0]);\&lt;br /&gt;
      if(defined($ld) &amp;amp;amp;&amp;amp;amp; $ld ne $a[0]) {\&lt;br /&gt;
       printf(\&amp;amp;quot;${ld}_12:00:00&amp;amp;#160;%f\n\&amp;amp;quot;, $dv); $dv = 0; }\&lt;br /&gt;
       if($lv) { $dv += ($F[9]-$lv); }\&lt;br /&gt;
       $ld = $a[0]; $lv = $F[9];\&lt;br /&gt;
       END {printf(\&amp;amp;quot;${ld}_12:00:00&amp;amp;#160;%f\n\&amp;amp;quot;, $dv)}&#039;&amp;amp;quot;\&lt;br /&gt;
     using 1:2 axes x1y1 ls l7 title &#039;Regen pro Tag&#039; with histeps&lt;br /&gt;
     &amp;amp;quot;&amp;amp;lt;grep -v avg_ &amp;amp;lt;IN&amp;amp;gt; | awk &#039;{print $1, $12==\&amp;amp;quot;yes\&amp;amp;quot;? 1&amp;amp;#160;: 0; }&#039;&amp;amp;quot;\&lt;br /&gt;
     using 1:2 axes x1y2 ls l11fill title &#039;Regen aktuell&#039; with points&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Eventuell müssen in den gplots noch die Linienstile/Farben den eigenen Bedürfnissen angepasst werden. Ich verwende hier eigene Farben und Füllungen.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;br /&gt;
[[Kategorie:Code Snippets]]&lt;br /&gt;
[[Kategorie:Logging]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40278</id>
		<title>Öffentliche IP-Adresse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40278"/>
		<updated>2025-06-29T09:16:48Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Hinweise von @passibe aus dem Forum ergänzt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
In FHEM kann die aktuell verwendete öffentliche IP-Adresse (IPv4/IPv6) auf verschiedene Arten ermittelt werden. Eine Methode ist der Abruf über externe Webdienste. Es kann auch auf lokale Router-Informationen (z. B. Fritzbox) oder systemnahe Module (z.B. STUN) zurückgegriffen werden.&lt;br /&gt;
&lt;br /&gt;
== HTTPMOD-Definition zum ermitteln der öffentlichen IPv4 und IPv6 via HTTPS-Webdienst ==&lt;br /&gt;
Die öffentliche IP kann man mit Webdiensten ermitteln, die einem die IPv4 und/oder IPv6 anzeigen mit der die Seite abgerufen wird:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod PublicIP HTTPMOD https://ipwho.de/json/ 600&lt;br /&gt;
attr PublicIP extractAllJSON 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Ein Abfrageintervall von 300 bis 600 Sekunden (5 bis 10 Minuten) wird empfohlen – häufigere Abfragen belasten die externen Dienste unnötig.&lt;br /&gt;
&lt;br /&gt;
=== Weitere Dienste ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Getestete Dienste zum ermitteln der öffentlichen IP via HTTPS&lt;br /&gt;
!Anbieter&lt;br /&gt;
!Adresse&lt;br /&gt;
!Zuletzt erfolgreich getestet im Jahr&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipify.org&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api64.ipify.org/?format=json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipwho.de&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipwho.de/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipinfo.io&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipinfo.io/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;myip.com&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api.myip.com/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ident.me&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://a.ident.me/json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Fritzbox nutzen ==&lt;br /&gt;
&lt;br /&gt;
=== Reguläres Device nutzen ===&lt;br /&gt;
&lt;br /&gt;
Wird eine Fritzbox als Router in das Internet verwendet, kann man die öffentliche IP sehr einfach vom Router abfragen. Der Vorteil dieser Lösung besteht darin, dass kein externer Dienst abgefragt werden muss:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Fritzbox FRITZBOX 192.168.123.123&lt;br /&gt;
attr Fritzbox INTERVAL 300&lt;br /&gt;
attr Fritzbox boxUser &amp;lt;dein Fritzbox User&amp;gt;&lt;br /&gt;
attr Fritzbox nonblockingTimeOut 100&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hierbei sind die Attribute wie die IP Adresse und Nutzername auf der Fritzbox anzupassen.&lt;br /&gt;
&lt;br /&gt;
Die IPv4 und IPv6 der Fritzbox, als auch das IPv6 Prefix des lokalen Netzes sind als Readings direkt abfragbar:&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv4_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Prefix&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn diese Readings nicht verfügbar sind, sollte überprüft werden, ob in den Einstellungen der Fritzbox unter Heimnetz -&amp;gt; Netzwerk -&amp;gt; Netzwerkeinstellungen die Option &amp;quot;Statusinformationen über UPnP übertragen&amp;quot; aktiviert ist. Zum Abrufen der IP-Adressen muss diese Option aktiviert sein (siehe auch: https://forum.fhem.de/index.php?topic=141872.msg1343693#msg1343693).&lt;br /&gt;
&lt;br /&gt;
=== Spezielle Abfrage der Fritzbox für Sonderfälle ===&lt;br /&gt;
In einigen Fällen stellt das Fritzbox Device nicht die passenden Readings bereit (zum Beispiel bei einer FB 6660 cable). Da in solchen Fällen CGNAT/DS-Lite eine IPv4 Abfrage vom Internet aus auf FHEM verhindert ist nur die öffentlich erreichbare IPv6 Adresse von Interesse (=GUA).&lt;br /&gt;
Es steht zur Zeit alternativ ein BASH Skript bereit ({{Link2Forum|Topic=141872|Message=1343341|LinkText=Quelle}}):&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
FRITZ_USER=&amp;quot;&amp;lt;USER&amp;gt;&amp;quot;&lt;br /&gt;
FRITZ_PASS=&amp;quot;&amp;lt;PASS&amp;gt;&amp;quot;&lt;br /&gt;
FRITZ_HOSTNAME=&amp;quot;&amp;lt;HOST&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
FRITZ_IPV6_GUA=$(curl -s --max-time 10 \&lt;br /&gt;
    --anyauth --user &amp;quot;${FRITZ_USER}:${FRITZ_PASS}&amp;quot; \&lt;br /&gt;
    http://${FRITZ_HOSTNAME}:49000/upnp/control/wanipconnection1 \&lt;br /&gt;
    -H &amp;quot;Content-Type: text/xml; charset=&amp;quot;utf-8&amp;quot;&amp;quot; \&lt;br /&gt;
    -H &amp;quot;SoapAction:urn:dslforum-org:service:WANIPConnection:1#GetExternalIPAddress&amp;quot; \&lt;br /&gt;
    -d &amp;quot;&amp;lt;?xml version=&#039;1.0&#039; encoding=&#039;utf-8&#039;?&amp;gt; &amp;lt;s:Envelope s:encodingStyle=&#039;http://schemas.xmlsoap.org/soap/encoding/&#039; xmlns:s=&#039;http://schemas.xmlsoap.org/soap/envelope/&#039;&amp;gt; &amp;lt;s:Body&amp;gt; &amp;lt;u:GetExternalIPAddress xmlns:u=&#039;urn:dslforum-org:service:WANIPConnection:1&#039;&amp;gt;&amp;lt;/u:GetExternalIPAddress&amp;gt; &amp;lt;/s:Body&amp;gt; &amp;lt;/s:Envelope&amp;gt;&amp;quot; | egrep -o &amp;quot;(::)?[0-9a-fA-F]{1,4}(::?[0-9a-fA-F]{1,4}){1,7}(::)?&amp;quot;)&lt;br /&gt;
if [ -z &amp;quot;$FRITZ_IPV6_GUA&amp;quot; ]; then&lt;br /&gt;
    echo &amp;quot;Error! Can&#039;t get IPv6 GUA from FRITZ!Box&amp;quot;&lt;br /&gt;
    exit 0&lt;br /&gt;
else&lt;br /&gt;
    echo &amp;quot;FRITZ!Box IPv6 GUA is: $FRITZ_IPV6_GUA&amp;quot;&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== STUN Server nutzen ==&lt;br /&gt;
Alternativ kann ein STUN-Server zur Ermittlung der externen IP genutzt werden. Beispiel-Snippet (Perl):&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub myip {&lt;br /&gt;
  use STUN::Client;&lt;br /&gt;
  my $stun_client = STUN::Client-&amp;gt;new;&lt;br /&gt;
  $stun_client-&amp;gt;stun_server(&#039;stun.ekiga.net&#039;);&lt;br /&gt;
  my (undef, $result) = $stun_client-&amp;gt;get;&lt;br /&gt;
  return $result-&amp;gt;{&#039;attributes&#039;}-&amp;gt;{&#039;0001&#039;}-&amp;gt;{&#039;address&#039;};&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hinweis: Das Perl-Modul &amp;lt;code&amp;gt;STUN::Client&amp;lt;/code&amp;gt; ist ggf. nicht in jeder Distribution verfügbar und muss über CPAN installiert werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Details und Diskussion: https://forum.fhem.de/index.php?topic=141872.msg1343225#msg1343225&lt;br /&gt;
&lt;br /&gt;
== IPs des FHEM Servers ermitteln (ohne Link-Local, Docker, etc) ==&lt;br /&gt;
Sofern der FHEM Server bereits eine öffentliche IP hat, kann man diese von den lokalen Netzwerkinterfaces abfragen. Mit wenigen Abhängigkeiten geht dies unter typischen Linux-Distributionen wie Debian Buster, Bullseye, Bookworm oder aktueller mit:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define LocalIPs at +*00:01:00 {&lt;br /&gt;
	my $hash = $defs{$SELF};&lt;br /&gt;
	my (@ipv4, @ipv6);&lt;br /&gt;
	&lt;br /&gt;
	#Abfragen der IP Adressen mittels CLI:&lt;br /&gt;
	my $json = qx(ip -j addr show scope global);&lt;br /&gt;
	my $data = decode_json($json);&lt;br /&gt;
    &lt;br /&gt;
	for my $iface (@$data) {&lt;br /&gt;
		for my $addr (@{$iface-&amp;gt;{addr_info} || []}) {&lt;br /&gt;
			next unless $addr-&amp;gt;{local};&lt;br /&gt;
			if ($addr-&amp;gt;{family} eq &#039;inet&#039;) {&lt;br /&gt;
				push @ipv4, $addr-&amp;gt;{local};&lt;br /&gt;
			} elsif ($addr-&amp;gt;{family} eq &#039;inet6&#039;) {&lt;br /&gt;
				push @ipv6, $addr-&amp;gt;{local};&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	readingsBeginUpdate($hash);&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;allIPv4&amp;quot;, join(&#039;,&#039;, @ipv4));&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;allIPv6&amp;quot;, join(&#039;,&#039;, @ipv6));&lt;br /&gt;
	readingsEndUpdate($hash, 1);&lt;br /&gt;
}&lt;br /&gt;
attr LocalIPs alignTime 00:00:00&lt;br /&gt;
attr LocalIPs event-on-change-reading .*&lt;br /&gt;
attr LocalIPs stateFormat IPv4: allIPv4&amp;lt;br /&amp;gt;IPv6: allIPv6&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40260</id>
		<title>Öffentliche IP-Adresse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40260"/>
		<updated>2025-06-17T05:27:53Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* Spezielle Abfrage der Fritzbox für Sonderfälle */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
In FHEM kann die aktuell verwendete öffentliche IP-Adresse (IPv4/IPv6) auf verschiedene Arten ermittelt werden. Eine Methode ist der Abruf über externe Webdienste. Es kann auch auf lokale Router-Informationen (z. B. Fritzbox) oder systemnahe Module (z.B. STUN) zurückgegriffen werden.&lt;br /&gt;
&lt;br /&gt;
== HTTPMOD-Definition zum ermitteln der öffentlichen IPv4 und IPv6 via HTTPS-Webdienst ==&lt;br /&gt;
Die öffentliche IP kann man mit Webdiensten ermitteln, die einem die IPv4 und/oder IPv6 anzeigen mit der die Seite abgerufen wird:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod PublicIP HTTPMOD https://ipwho.de/json/ 600&lt;br /&gt;
attr PublicIP extractAllJSON 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Ein Abfrageintervall von 300 bis 600 Sekunden (5 bis 10 Minuten) wird empfohlen – häufigere Abfragen belasten die externen Dienste unnötig.&lt;br /&gt;
&lt;br /&gt;
=== Weitere Dienste ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Getestete Dienste zum ermitteln der öffentlichen IP via HTTPS&lt;br /&gt;
!Anbieter&lt;br /&gt;
!Adresse&lt;br /&gt;
!Zuletzt erfolgreich getestet im Jahr&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipify.org&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api64.ipify.org/?format=json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipwho.de&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipwho.de/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipinfo.io&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipinfo.io/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;myip.com&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api.myip.com/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ident.me&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://a.ident.me/json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Fritzbox nutzen ==&lt;br /&gt;
&lt;br /&gt;
=== Reguläres Device nutzen ===&lt;br /&gt;
&lt;br /&gt;
Wird eine Fritzbox als Router in das Internet verwendet, kann man die öffentliche IP sehr einfach vom Router abfragen. Der Vorteil dieser Lösung besteht darin, dass kein externer Dienst abgefragt werden muss:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Fritzbox FRITZBOX 192.168.123.123&lt;br /&gt;
attr Fritzbox INTERVAL 300&lt;br /&gt;
attr Fritzbox boxUser &amp;lt;dein Fritzbox User&amp;gt;&lt;br /&gt;
attr Fritzbox nonblockingTimeOut 100&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hierbei sind die Attribute wie die IP Adresse und Nutzername auf der Fritzbox anzupassen.&lt;br /&gt;
&lt;br /&gt;
Die IPv4 und IPv6 der Fritzbox, als auch das IPv6 Prefix des lokalen Netzes sind als Readings direkt abfragbar:&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv4_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Prefix&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Spezielle Abfrage der Fritzbox für Sonderfälle ===&lt;br /&gt;
In einigen Fällen stellt das Fritzbox Device nicht die passenden Readings bereit (zum Beispiel bei einer FB 6660 cable). Da in solchen Fällen CGNAT/DS-Lite eine IPv4 Abfrage vom Internet aus auf FHEM verhindert ist nur die öffentlich erreichbare IPv6 Adresse von Interesse (=GUA).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Es steht zur Zeit nur ein BASH Skript bereit ({{Link2Forum|Topic=141872|Message=1343341|LinkText=Quelle}}):&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
FRITZ_USER=&amp;quot;&amp;lt;USER&amp;gt;&amp;quot;&lt;br /&gt;
FRITZ_PASS=&amp;quot;&amp;lt;PASS&amp;gt;&amp;quot;&lt;br /&gt;
FRITZ_HOSTNAME=&amp;quot;&amp;lt;HOST&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
FRITZ_IPV6_GUA=$(curl -s --max-time 10 \&lt;br /&gt;
    --anyauth --user &amp;quot;${FRITZ_USER}:${FRITZ_PASS}&amp;quot; \&lt;br /&gt;
    http://${FRITZ_HOSTNAME}:49000/upnp/control/wanipconnection1 \&lt;br /&gt;
    -H &amp;quot;Content-Type: text/xml; charset=&amp;quot;utf-8&amp;quot;&amp;quot; \&lt;br /&gt;
    -H &amp;quot;SoapAction:urn:dslforum-org:service:WANIPConnection:1#GetExternalIPAddress&amp;quot; \&lt;br /&gt;
    -d &amp;quot;&amp;lt;?xml version=&#039;1.0&#039; encoding=&#039;utf-8&#039;?&amp;gt; &amp;lt;s:Envelope s:encodingStyle=&#039;http://schemas.xmlsoap.org/soap/encoding/&#039; xmlns:s=&#039;http://schemas.xmlsoap.org/soap/envelope/&#039;&amp;gt; &amp;lt;s:Body&amp;gt; &amp;lt;u:GetExternalIPAddress xmlns:u=&#039;urn:dslforum-org:service:WANIPConnection:1&#039;&amp;gt;&amp;lt;/u:GetExternalIPAddress&amp;gt; &amp;lt;/s:Body&amp;gt; &amp;lt;/s:Envelope&amp;gt;&amp;quot; | egrep -o &amp;quot;(::)?[0-9a-fA-F]{1,4}(::?[0-9a-fA-F]{1,4}){1,7}(::)?&amp;quot;)&lt;br /&gt;
if [ -z &amp;quot;$FRITZ_IPV6_GUA&amp;quot; ]; then&lt;br /&gt;
    echo &amp;quot;Error! Can&#039;t get IPv6 GUA from FRITZ!Box&amp;quot;&lt;br /&gt;
    exit 0&lt;br /&gt;
else&lt;br /&gt;
    echo &amp;quot;FRITZ!Box IPv6 GUA is: $FRITZ_IPV6_GUA&amp;quot;&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== STUN Server nutzen ==&lt;br /&gt;
Alternativ kann ein STUN-Server zur Ermittlung der externen IP genutzt werden. Beispiel-Snippet (Perl):&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub myip {&lt;br /&gt;
  use STUN::Client;&lt;br /&gt;
  my $stun_client = STUN::Client-&amp;gt;new;&lt;br /&gt;
  $stun_client-&amp;gt;stun_server(&#039;stun.ekiga.net&#039;);&lt;br /&gt;
  my (undef, $result) = $stun_client-&amp;gt;get;&lt;br /&gt;
  return $result-&amp;gt;{&#039;attributes&#039;}-&amp;gt;{&#039;0001&#039;}-&amp;gt;{&#039;address&#039;};&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hinweis: Das Perl-Modul &amp;lt;code&amp;gt;STUN::Client&amp;lt;/code&amp;gt; ist ggf. nicht in jeder Distribution verfügbar und muss über CPAN installiert werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Details und Diskussion: https://forum.fhem.de/index.php?topic=141872.msg1343225#msg1343225&lt;br /&gt;
&lt;br /&gt;
== IPs des FHEM Servers ermitteln (ohne Link-Local, Docker, etc) ==&lt;br /&gt;
Sofern der FHEM Server bereits eine öffentliche IP hat, kann man diese von den lokalen Netzwerkinterfaces abfragen. Mit wenigen Abhängigkeiten geht dies unter typischen Linux-Distributionen wie Debian Buster, Bullseye, Bookworm oder aktueller mit:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define LocalIPs at +*00:01:00 {&lt;br /&gt;
	my $hash = $defs{$SELF};&lt;br /&gt;
	my (@ipv4, @ipv6);&lt;br /&gt;
	&lt;br /&gt;
	#Abfragen der IP Adressen mittels CLI:&lt;br /&gt;
	my $json = qx(ip -j addr show scope global);&lt;br /&gt;
	my $data = decode_json($json);&lt;br /&gt;
    &lt;br /&gt;
	for my $iface (@$data) {&lt;br /&gt;
		for my $addr (@{$iface-&amp;gt;{addr_info} || []}) {&lt;br /&gt;
			next unless $addr-&amp;gt;{local};&lt;br /&gt;
			if ($addr-&amp;gt;{family} eq &#039;inet&#039;) {&lt;br /&gt;
				push @ipv4, $addr-&amp;gt;{local};&lt;br /&gt;
			} elsif ($addr-&amp;gt;{family} eq &#039;inet6&#039;) {&lt;br /&gt;
				push @ipv6, $addr-&amp;gt;{local};&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	readingsBeginUpdate($hash);&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;allIPv4&amp;quot;, join(&#039;,&#039;, @ipv4));&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;allIPv6&amp;quot;, join(&#039;,&#039;, @ipv6));&lt;br /&gt;
	readingsEndUpdate($hash, 1);&lt;br /&gt;
}&lt;br /&gt;
attr LocalIPs alignTime 00:00:00&lt;br /&gt;
attr LocalIPs event-on-change-reading .*&lt;br /&gt;
attr LocalIPs stateFormat IPv4: allIPv4&amp;lt;br /&amp;gt;IPv6: allIPv6&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40259</id>
		<title>Öffentliche IP-Adresse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40259"/>
		<updated>2025-06-17T05:27:09Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* Spezielle Abfrage der Fritzbox für Sonderfälle */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
In FHEM kann die aktuell verwendete öffentliche IP-Adresse (IPv4/IPv6) auf verschiedene Arten ermittelt werden. Eine Methode ist der Abruf über externe Webdienste. Es kann auch auf lokale Router-Informationen (z. B. Fritzbox) oder systemnahe Module (z.B. STUN) zurückgegriffen werden.&lt;br /&gt;
&lt;br /&gt;
== HTTPMOD-Definition zum ermitteln der öffentlichen IPv4 und IPv6 via HTTPS-Webdienst ==&lt;br /&gt;
Die öffentliche IP kann man mit Webdiensten ermitteln, die einem die IPv4 und/oder IPv6 anzeigen mit der die Seite abgerufen wird:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod PublicIP HTTPMOD https://ipwho.de/json/ 600&lt;br /&gt;
attr PublicIP extractAllJSON 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Ein Abfrageintervall von 300 bis 600 Sekunden (5 bis 10 Minuten) wird empfohlen – häufigere Abfragen belasten die externen Dienste unnötig.&lt;br /&gt;
&lt;br /&gt;
=== Weitere Dienste ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Getestete Dienste zum ermitteln der öffentlichen IP via HTTPS&lt;br /&gt;
!Anbieter&lt;br /&gt;
!Adresse&lt;br /&gt;
!Zuletzt erfolgreich getestet im Jahr&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipify.org&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api64.ipify.org/?format=json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipwho.de&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipwho.de/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipinfo.io&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipinfo.io/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;myip.com&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api.myip.com/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ident.me&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://a.ident.me/json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Fritzbox nutzen ==&lt;br /&gt;
&lt;br /&gt;
=== Reguläres Device nutzen ===&lt;br /&gt;
&lt;br /&gt;
Wird eine Fritzbox als Router in das Internet verwendet, kann man die öffentliche IP sehr einfach vom Router abfragen. Der Vorteil dieser Lösung besteht darin, dass kein externer Dienst abgefragt werden muss:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Fritzbox FRITZBOX 192.168.123.123&lt;br /&gt;
attr Fritzbox INTERVAL 300&lt;br /&gt;
attr Fritzbox boxUser &amp;lt;dein Fritzbox User&amp;gt;&lt;br /&gt;
attr Fritzbox nonblockingTimeOut 100&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hierbei sind die Attribute wie die IP Adresse und Nutzername auf der Fritzbox anzupassen.&lt;br /&gt;
&lt;br /&gt;
Die IPv4 und IPv6 der Fritzbox, als auch das IPv6 Prefix des lokalen Netzes sind als Readings direkt abfragbar:&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv4_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Prefix&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Spezielle Abfrage der Fritzbox für Sonderfälle ===&lt;br /&gt;
In einigen Fällen stellt das Fritzbox Device nicht die passenden Readings bereit (zum Beispiel bei einer FB 6660 cable). Da in solchen Fällen CGNAT/DS-Lite eine IPv4 Abfrage vom Internet aus auf FHEM verhindert ist nur die öffentlich erreichbare IPv6 Adresse von Interesse (=GUA).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Es steht zur Zeit nur ein BASH Skript bereit ({{Link2Forum|Topic=141872|Message=1343332|LinkText=Quelle}}):&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
FRITZ_USER=&amp;quot;&amp;lt;USER&amp;gt;&amp;quot;&lt;br /&gt;
FRITZ_PASS=&amp;quot;&amp;lt;PASS&amp;gt;&amp;quot;&lt;br /&gt;
FRITZ_HOSTNAME=&amp;quot;&amp;lt;HOST&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
FRITZ_IPV6_GUA=$(curl -s --max-time 10 \&lt;br /&gt;
    --anyauth --user &amp;quot;${FRITZ_USER}:${FRITZ_PASS}&amp;quot; \&lt;br /&gt;
    http://${FRITZ_HOSTNAME}:49000/upnp/control/wanipconnection1 \&lt;br /&gt;
    -H &amp;quot;Content-Type: text/xml; charset=&amp;quot;utf-8&amp;quot;&amp;quot; \&lt;br /&gt;
    -H &amp;quot;SoapAction:urn:dslforum-org:service:WANIPConnection:1#GetExternalIPAddress&amp;quot; \&lt;br /&gt;
    -d &amp;quot;&amp;lt;?xml version=&#039;1.0&#039; encoding=&#039;utf-8&#039;?&amp;gt; &amp;lt;s:Envelope s:encodingStyle=&#039;http://schemas.xmlsoap.org/soap/encoding/&#039; xmlns:s=&#039;http://schemas.xmlsoap.org/soap/envelope/&#039;&amp;gt; &amp;lt;s:Body&amp;gt; &amp;lt;u:GetExternalIPAddress xmlns:u=&#039;urn:dslforum-org:service:WANIPConnection:1&#039;&amp;gt;&amp;lt;/u:GetExternalIPAddress&amp;gt; &amp;lt;/s:Body&amp;gt; &amp;lt;/s:Envelope&amp;gt;&amp;quot; | egrep -o &amp;quot;(::)?[0-9a-fA-F]{1,4}(::?[0-9a-fA-F]{1,4}){1,7}(::)?&amp;quot;)&lt;br /&gt;
if [ -z &amp;quot;$FRITZ_IPV6_GUA&amp;quot; ]; then&lt;br /&gt;
    echo &amp;quot;Error! Can&#039;t get IPv6 GUA from FRITZ!Box&amp;quot;&lt;br /&gt;
    exit 0&lt;br /&gt;
else&lt;br /&gt;
    echo &amp;quot;FRITZ!Box IPv6 GUA is: $FRITZ_IPV6_GUA&amp;quot;&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== STUN Server nutzen ==&lt;br /&gt;
Alternativ kann ein STUN-Server zur Ermittlung der externen IP genutzt werden. Beispiel-Snippet (Perl):&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub myip {&lt;br /&gt;
  use STUN::Client;&lt;br /&gt;
  my $stun_client = STUN::Client-&amp;gt;new;&lt;br /&gt;
  $stun_client-&amp;gt;stun_server(&#039;stun.ekiga.net&#039;);&lt;br /&gt;
  my (undef, $result) = $stun_client-&amp;gt;get;&lt;br /&gt;
  return $result-&amp;gt;{&#039;attributes&#039;}-&amp;gt;{&#039;0001&#039;}-&amp;gt;{&#039;address&#039;};&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hinweis: Das Perl-Modul &amp;lt;code&amp;gt;STUN::Client&amp;lt;/code&amp;gt; ist ggf. nicht in jeder Distribution verfügbar und muss über CPAN installiert werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Details und Diskussion: https://forum.fhem.de/index.php?topic=141872.msg1343225#msg1343225&lt;br /&gt;
&lt;br /&gt;
== IPs des FHEM Servers ermitteln (ohne Link-Local, Docker, etc) ==&lt;br /&gt;
Sofern der FHEM Server bereits eine öffentliche IP hat, kann man diese von den lokalen Netzwerkinterfaces abfragen. Mit wenigen Abhängigkeiten geht dies unter typischen Linux-Distributionen wie Debian Buster, Bullseye, Bookworm oder aktueller mit:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define LocalIPs at +*00:01:00 {&lt;br /&gt;
	my $hash = $defs{$SELF};&lt;br /&gt;
	my (@ipv4, @ipv6);&lt;br /&gt;
	&lt;br /&gt;
	#Abfragen der IP Adressen mittels CLI:&lt;br /&gt;
	my $json = qx(ip -j addr show scope global);&lt;br /&gt;
	my $data = decode_json($json);&lt;br /&gt;
    &lt;br /&gt;
	for my $iface (@$data) {&lt;br /&gt;
		for my $addr (@{$iface-&amp;gt;{addr_info} || []}) {&lt;br /&gt;
			next unless $addr-&amp;gt;{local};&lt;br /&gt;
			if ($addr-&amp;gt;{family} eq &#039;inet&#039;) {&lt;br /&gt;
				push @ipv4, $addr-&amp;gt;{local};&lt;br /&gt;
			} elsif ($addr-&amp;gt;{family} eq &#039;inet6&#039;) {&lt;br /&gt;
				push @ipv6, $addr-&amp;gt;{local};&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	readingsBeginUpdate($hash);&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;allIPv4&amp;quot;, join(&#039;,&#039;, @ipv4));&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;allIPv6&amp;quot;, join(&#039;,&#039;, @ipv6));&lt;br /&gt;
	readingsEndUpdate($hash, 1);&lt;br /&gt;
}&lt;br /&gt;
attr LocalIPs alignTime 00:00:00&lt;br /&gt;
attr LocalIPs event-on-change-reading .*&lt;br /&gt;
attr LocalIPs stateFormat IPv4: allIPv4&amp;lt;br /&amp;gt;IPv6: allIPv6&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40258</id>
		<title>Öffentliche IP-Adresse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40258"/>
		<updated>2025-06-17T05:26:26Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Hinweise von @passibe wie man die GUA abfragen kann&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
In FHEM kann die aktuell verwendete öffentliche IP-Adresse (IPv4/IPv6) auf verschiedene Arten ermittelt werden. Eine Methode ist der Abruf über externe Webdienste. Es kann auch auf lokale Router-Informationen (z. B. Fritzbox) oder systemnahe Module (z.B. STUN) zurückgegriffen werden.&lt;br /&gt;
&lt;br /&gt;
== HTTPMOD-Definition zum ermitteln der öffentlichen IPv4 und IPv6 via HTTPS-Webdienst ==&lt;br /&gt;
Die öffentliche IP kann man mit Webdiensten ermitteln, die einem die IPv4 und/oder IPv6 anzeigen mit der die Seite abgerufen wird:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod PublicIP HTTPMOD https://ipwho.de/json/ 600&lt;br /&gt;
attr PublicIP extractAllJSON 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Ein Abfrageintervall von 300 bis 600 Sekunden (5 bis 10 Minuten) wird empfohlen – häufigere Abfragen belasten die externen Dienste unnötig.&lt;br /&gt;
&lt;br /&gt;
=== Weitere Dienste ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Getestete Dienste zum ermitteln der öffentlichen IP via HTTPS&lt;br /&gt;
!Anbieter&lt;br /&gt;
!Adresse&lt;br /&gt;
!Zuletzt erfolgreich getestet im Jahr&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipify.org&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api64.ipify.org/?format=json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipwho.de&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipwho.de/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipinfo.io&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipinfo.io/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;myip.com&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api.myip.com/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ident.me&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://a.ident.me/json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Fritzbox nutzen ==&lt;br /&gt;
&lt;br /&gt;
=== Reguläres Device nutzen ===&lt;br /&gt;
&lt;br /&gt;
Wird eine Fritzbox als Router in das Internet verwendet, kann man die öffentliche IP sehr einfach vom Router abfragen. Der Vorteil dieser Lösung besteht darin, dass kein externer Dienst abgefragt werden muss:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Fritzbox FRITZBOX 192.168.123.123&lt;br /&gt;
attr Fritzbox INTERVAL 300&lt;br /&gt;
attr Fritzbox boxUser &amp;lt;dein Fritzbox User&amp;gt;&lt;br /&gt;
attr Fritzbox nonblockingTimeOut 100&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hierbei sind die Attribute wie die IP Adresse und Nutzername auf der Fritzbox anzupassen.&lt;br /&gt;
&lt;br /&gt;
Die IPv4 und IPv6 der Fritzbox, als auch das IPv6 Prefix des lokalen Netzes sind als Readings direkt abfragbar:&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv4_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Prefix&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Spezielle Abfrage der Fritzbox für Sonderfälle ===&lt;br /&gt;
In einigen Fällen stellt das Fritzbox Device nicht die passenden Readings bereit (zum Beispiel bei einer FB 6660 cable). Da in solchen Fällen CGNAT/DS-Lite eine IPv4 Abfrage vom Internet aus auf FHEM verhindert ist nur die öffentlich erreichbare IPv6 Adresse von Interesse (=GUA).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Es steht zur Zeit nur ein BASH Skript bereit ({{Link2Forum|Topic=141872.msg1343332|Message=msg1343332|LinkText=Quelle}}):&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
&lt;br /&gt;
FRITZ_USER=&amp;quot;&amp;lt;USER&amp;gt;&amp;quot;&lt;br /&gt;
FRITZ_PASS=&amp;quot;&amp;lt;PASS&amp;gt;&amp;quot;&lt;br /&gt;
FRITZ_HOSTNAME=&amp;quot;&amp;lt;HOST&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
FRITZ_IPV6_GUA=$(curl -s --max-time 10 \&lt;br /&gt;
    --anyauth --user &amp;quot;${FRITZ_USER}:${FRITZ_PASS}&amp;quot; \&lt;br /&gt;
    http://${FRITZ_HOSTNAME}:49000/upnp/control/wanipconnection1 \&lt;br /&gt;
    -H &amp;quot;Content-Type: text/xml; charset=&amp;quot;utf-8&amp;quot;&amp;quot; \&lt;br /&gt;
    -H &amp;quot;SoapAction:urn:dslforum-org:service:WANIPConnection:1#GetExternalIPAddress&amp;quot; \&lt;br /&gt;
    -d &amp;quot;&amp;lt;?xml version=&#039;1.0&#039; encoding=&#039;utf-8&#039;?&amp;gt; &amp;lt;s:Envelope s:encodingStyle=&#039;http://schemas.xmlsoap.org/soap/encoding/&#039; xmlns:s=&#039;http://schemas.xmlsoap.org/soap/envelope/&#039;&amp;gt; &amp;lt;s:Body&amp;gt; &amp;lt;u:GetExternalIPAddress xmlns:u=&#039;urn:dslforum-org:service:WANIPConnection:1&#039;&amp;gt;&amp;lt;/u:GetExternalIPAddress&amp;gt; &amp;lt;/s:Body&amp;gt; &amp;lt;/s:Envelope&amp;gt;&amp;quot; | egrep -o &amp;quot;(::)?[0-9a-fA-F]{1,4}(::?[0-9a-fA-F]{1,4}){1,7}(::)?&amp;quot;)&lt;br /&gt;
if [ -z &amp;quot;$FRITZ_IPV6_GUA&amp;quot; ]; then&lt;br /&gt;
    echo &amp;quot;Error! Can&#039;t get IPv6 GUA from FRITZ!Box&amp;quot;&lt;br /&gt;
    exit 0&lt;br /&gt;
else&lt;br /&gt;
    echo &amp;quot;FRITZ!Box IPv6 GUA is: $FRITZ_IPV6_GUA&amp;quot;&lt;br /&gt;
fi&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== STUN Server nutzen ==&lt;br /&gt;
Alternativ kann ein STUN-Server zur Ermittlung der externen IP genutzt werden. Beispiel-Snippet (Perl):&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub myip {&lt;br /&gt;
  use STUN::Client;&lt;br /&gt;
  my $stun_client = STUN::Client-&amp;gt;new;&lt;br /&gt;
  $stun_client-&amp;gt;stun_server(&#039;stun.ekiga.net&#039;);&lt;br /&gt;
  my (undef, $result) = $stun_client-&amp;gt;get;&lt;br /&gt;
  return $result-&amp;gt;{&#039;attributes&#039;}-&amp;gt;{&#039;0001&#039;}-&amp;gt;{&#039;address&#039;};&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hinweis: Das Perl-Modul &amp;lt;code&amp;gt;STUN::Client&amp;lt;/code&amp;gt; ist ggf. nicht in jeder Distribution verfügbar und muss über CPAN installiert werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Details und Diskussion: https://forum.fhem.de/index.php?topic=141872.msg1343225#msg1343225&lt;br /&gt;
&lt;br /&gt;
== IPs des FHEM Servers ermitteln (ohne Link-Local, Docker, etc) ==&lt;br /&gt;
Sofern der FHEM Server bereits eine öffentliche IP hat, kann man diese von den lokalen Netzwerkinterfaces abfragen. Mit wenigen Abhängigkeiten geht dies unter typischen Linux-Distributionen wie Debian Buster, Bullseye, Bookworm oder aktueller mit:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define LocalIPs at +*00:01:00 {&lt;br /&gt;
	my $hash = $defs{$SELF};&lt;br /&gt;
	my (@ipv4, @ipv6);&lt;br /&gt;
	&lt;br /&gt;
	#Abfragen der IP Adressen mittels CLI:&lt;br /&gt;
	my $json = qx(ip -j addr show scope global);&lt;br /&gt;
	my $data = decode_json($json);&lt;br /&gt;
    &lt;br /&gt;
	for my $iface (@$data) {&lt;br /&gt;
		for my $addr (@{$iface-&amp;gt;{addr_info} || []}) {&lt;br /&gt;
			next unless $addr-&amp;gt;{local};&lt;br /&gt;
			if ($addr-&amp;gt;{family} eq &#039;inet&#039;) {&lt;br /&gt;
				push @ipv4, $addr-&amp;gt;{local};&lt;br /&gt;
			} elsif ($addr-&amp;gt;{family} eq &#039;inet6&#039;) {&lt;br /&gt;
				push @ipv6, $addr-&amp;gt;{local};&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	readingsBeginUpdate($hash);&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;allIPv4&amp;quot;, join(&#039;,&#039;, @ipv4));&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;allIPv6&amp;quot;, join(&#039;,&#039;, @ipv6));&lt;br /&gt;
	readingsEndUpdate($hash, 1);&lt;br /&gt;
}&lt;br /&gt;
attr LocalIPs alignTime 00:00:00&lt;br /&gt;
attr LocalIPs event-on-change-reading .*&lt;br /&gt;
attr LocalIPs stateFormat IPv4: allIPv4&amp;lt;br /&amp;gt;IPv6: allIPv6&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40257</id>
		<title>Öffentliche IP-Adresse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40257"/>
		<updated>2025-06-16T20:11:09Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* IPs des FHEM Servers ermitteln (ohne Link-Local, Docker, etc) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
In FHEM kann die aktuell verwendete öffentliche IP-Adresse (IPv4/IPv6) auf verschiedene Arten ermittelt werden. Eine Methode ist der Abruf über externe Webdienste. Es kann auch auf lokale Router-Informationen (z. B. Fritzbox) oder systemnahe Module (z.B. STUN) zurückgegriffen werden.&lt;br /&gt;
&lt;br /&gt;
== HTTPMOD-Definition zum ermitteln der öffentlichen IPv4 und IPv6 via HTTPS-Webdienst ==&lt;br /&gt;
Die öffentliche IP kann man mit Webdiensten ermitteln, die einem die IPv4 und/oder IPv6 anzeigen mit der die Seite abgerufen wird:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod PublicIP HTTPMOD https://ipwho.de/json/ 600&lt;br /&gt;
attr PublicIP extractAllJSON 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Ein Abfrageintervall von 300 bis 600 Sekunden (5 bis 10 Minuten) wird empfohlen – häufigere Abfragen belasten die externen Dienste unnötig.&lt;br /&gt;
&lt;br /&gt;
=== Weitere Dienste ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Getestete Dienste zum ermitteln der öffentlichen IP via HTTPS&lt;br /&gt;
!Anbieter&lt;br /&gt;
!Adresse&lt;br /&gt;
!Zuletzt erfolgreich getestet im Jahr&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipify.org&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api64.ipify.org/?format=json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipwho.de&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipwho.de/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipinfo.io&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipinfo.io/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;myip.com&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api.myip.com/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ident.me&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://a.ident.me/json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Fritzbox nutzen ==&lt;br /&gt;
Wird eine Fritzbox als Router in das Internet verwendet, kann man die öffentliche IP sehr einfach vom Router abfragen. Der Vorteil dieser Lösung besteht darin, dass kein externer Dienst abgefragt werden muss:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Fritzbox FRITZBOX 192.168.123.123&lt;br /&gt;
attr Fritzbox INTERVAL 300&lt;br /&gt;
attr Fritzbox boxUser &amp;lt;dein Fritzbox User&amp;gt;&lt;br /&gt;
attr Fritzbox nonblockingTimeOut 100&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hierbei sind die Attribute wie die IP Adresse und Nutzername auf der Fritzbox anzupassen.&lt;br /&gt;
&lt;br /&gt;
Die IPv4 und IPv6 der Fritzbox, als auch das IPv6 Prefix des lokalen Netzes sind als Readings direkt abfragbar:&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv4_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Prefix&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== STUN Server nutzen ==&lt;br /&gt;
Alternativ kann ein STUN-Server zur Ermittlung der externen IP genutzt werden. Beispiel-Snippet (Perl):&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub myip {&lt;br /&gt;
  use STUN::Client;&lt;br /&gt;
  my $stun_client = STUN::Client-&amp;gt;new;&lt;br /&gt;
  $stun_client-&amp;gt;stun_server(&#039;stun.ekiga.net&#039;);&lt;br /&gt;
  my (undef, $result) = $stun_client-&amp;gt;get;&lt;br /&gt;
  return $result-&amp;gt;{&#039;attributes&#039;}-&amp;gt;{&#039;0001&#039;}-&amp;gt;{&#039;address&#039;};&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hinweis: Das Perl-Modul &amp;lt;code&amp;gt;STUN::Client&amp;lt;/code&amp;gt; ist ggf. nicht in jeder Distribution verfügbar und muss über CPAN installiert werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Details und Diskussion: https://forum.fhem.de/index.php?topic=141872.msg1343225#msg1343225&lt;br /&gt;
&lt;br /&gt;
== IPs des FHEM Servers ermitteln (ohne Link-Local, Docker, etc) ==&lt;br /&gt;
Sofern der FHEM Server bereits eine öffentliche IP hat, kann man diese von den lokalen Netzwerkinterfaces abfragen. Mit wenigen Abhängigkeiten geht dies unter typischen Linux-Distributionen wie Debian Buster, Bullseye, Bookworm oder aktueller mit:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define LocalIPs at +*00:01:00 {&lt;br /&gt;
	my $hash = $defs{$SELF};&lt;br /&gt;
	my (@ipv4, @ipv6);&lt;br /&gt;
	&lt;br /&gt;
	#Abfragen der IP Adressen mittels CLI:&lt;br /&gt;
	my $json = qx(ip -j addr show scope global);&lt;br /&gt;
	my $data = decode_json($json);&lt;br /&gt;
    &lt;br /&gt;
	for my $iface (@$data) {&lt;br /&gt;
		for my $addr (@{$iface-&amp;gt;{addr_info} || []}) {&lt;br /&gt;
			next unless $addr-&amp;gt;{local};&lt;br /&gt;
			if ($addr-&amp;gt;{family} eq &#039;inet&#039;) {&lt;br /&gt;
				push @ipv4, $addr-&amp;gt;{local};&lt;br /&gt;
			} elsif ($addr-&amp;gt;{family} eq &#039;inet6&#039;) {&lt;br /&gt;
				push @ipv6, $addr-&amp;gt;{local};&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	readingsBeginUpdate($hash);&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;allIPv4&amp;quot;, join(&#039;,&#039;, @ipv4));&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;allIPv6&amp;quot;, join(&#039;,&#039;, @ipv6));&lt;br /&gt;
	readingsEndUpdate($hash, 1);&lt;br /&gt;
}&lt;br /&gt;
attr LocalIPs alignTime 00:00:00&lt;br /&gt;
attr LocalIPs event-on-change-reading .*&lt;br /&gt;
attr LocalIPs stateFormat IPv4: allIPv4&amp;lt;br /&amp;gt;IPv6: allIPv6&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40256</id>
		<title>Öffentliche IP-Adresse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40256"/>
		<updated>2025-06-16T20:10:24Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Falls man die IP Adressen des Servers direkt nutzen kann, ein at device ergänzt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
In FHEM kann die aktuell verwendete öffentliche IP-Adresse (IPv4/IPv6) auf verschiedene Arten ermittelt werden. Eine Methode ist der Abruf über externe Webdienste. Es kann auch auf lokale Router-Informationen (z. B. Fritzbox) oder systemnahe Module (z.B. STUN) zurückgegriffen werden.&lt;br /&gt;
&lt;br /&gt;
== HTTPMOD-Definition zum ermitteln der öffentlichen IPv4 und IPv6 via HTTPS-Webdienst ==&lt;br /&gt;
Die öffentliche IP kann man mit Webdiensten ermitteln, die einem die IPv4 und/oder IPv6 anzeigen mit der die Seite abgerufen wird:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod PublicIP HTTPMOD https://ipwho.de/json/ 600&lt;br /&gt;
attr PublicIP extractAllJSON 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Ein Abfrageintervall von 300 bis 600 Sekunden (5 bis 10 Minuten) wird empfohlen – häufigere Abfragen belasten die externen Dienste unnötig.&lt;br /&gt;
&lt;br /&gt;
=== Weitere Dienste ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Getestete Dienste zum ermitteln der öffentlichen IP via HTTPS&lt;br /&gt;
!Anbieter&lt;br /&gt;
!Adresse&lt;br /&gt;
!Zuletzt erfolgreich getestet im Jahr&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipify.org&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api64.ipify.org/?format=json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipwho.de&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipwho.de/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipinfo.io&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipinfo.io/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;myip.com&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api.myip.com/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ident.me&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://a.ident.me/json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Fritzbox nutzen ==&lt;br /&gt;
Wird eine Fritzbox als Router in das Internet verwendet, kann man die öffentliche IP sehr einfach vom Router abfragen. Der Vorteil dieser Lösung besteht darin, dass kein externer Dienst abgefragt werden muss:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Fritzbox FRITZBOX 192.168.123.123&lt;br /&gt;
attr Fritzbox INTERVAL 300&lt;br /&gt;
attr Fritzbox boxUser &amp;lt;dein Fritzbox User&amp;gt;&lt;br /&gt;
attr Fritzbox nonblockingTimeOut 100&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hierbei sind die Attribute wie die IP Adresse und Nutzername auf der Fritzbox anzupassen.&lt;br /&gt;
&lt;br /&gt;
Die IPv4 und IPv6 der Fritzbox, als auch das IPv6 Prefix des lokalen Netzes sind als Readings direkt abfragbar:&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv4_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Prefix&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== STUN Server nutzen ==&lt;br /&gt;
Alternativ kann ein STUN-Server zur Ermittlung der externen IP genutzt werden. Beispiel-Snippet (Perl):&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub myip {&lt;br /&gt;
  use STUN::Client;&lt;br /&gt;
  my $stun_client = STUN::Client-&amp;gt;new;&lt;br /&gt;
  $stun_client-&amp;gt;stun_server(&#039;stun.ekiga.net&#039;);&lt;br /&gt;
  my (undef, $result) = $stun_client-&amp;gt;get;&lt;br /&gt;
  return $result-&amp;gt;{&#039;attributes&#039;}-&amp;gt;{&#039;0001&#039;}-&amp;gt;{&#039;address&#039;};&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hinweis: Das Perl-Modul &amp;lt;code&amp;gt;STUN::Client&amp;lt;/code&amp;gt; ist ggf. nicht in jeder Distribution verfügbar und muss über CPAN installiert werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Details und Diskussion: https://forum.fhem.de/index.php?topic=141872.msg1343225#msg1343225&lt;br /&gt;
&lt;br /&gt;
== IPs des FHEM Servers ermitteln (ohne Link-Local, Docker, etc) ==&lt;br /&gt;
Sofern der FHEM Server bereits eine öffentliche IP hat, kann man diese von den lokalen Netzwerkinterfaces abfragen. Mit wenigen Abhängigkeiten geht dies unter typischen Linux-Distributionen wie Debian Buster, Bullseye, Bookworm oder aktueller mit:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define LocalIPs at +*00:01:00 {&lt;br /&gt;
	my $hash = $defs{$SELF};&lt;br /&gt;
	my (@ipv4, @ipv6);&lt;br /&gt;
	&lt;br /&gt;
	#Abfragen der IP Adressen mittels CLI:&lt;br /&gt;
	my $json = qx(ip -j addr show scope global);&lt;br /&gt;
	my $data = decode_json($json);&lt;br /&gt;
    &lt;br /&gt;
	for my $iface (@$data) {&lt;br /&gt;
		for my $addr (@{$iface-&amp;gt;{addr_info} || []}) {&lt;br /&gt;
			next unless $addr-&amp;gt;{local};&lt;br /&gt;
			if ($addr-&amp;gt;{family} eq &#039;inet&#039;) {&lt;br /&gt;
				push @ipv4, $addr-&amp;gt;{local};&lt;br /&gt;
			} elsif ($addr-&amp;gt;{family} eq &#039;inet6&#039;) {&lt;br /&gt;
				push @ipv6, $addr-&amp;gt;{local};&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	readingsBeginUpdate($hash);&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;allIPv4&amp;quot;, join(&#039;,&#039;, @ipv4));&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;allIPv6&amp;quot;, join(&#039;,&#039;, @ipv6));&lt;br /&gt;
	readingsEndUpdate($hash, 1);&lt;br /&gt;
}&lt;br /&gt;
attr LocalIPs alignTime 00:00:00&lt;br /&gt;
attr LocalIPs event-on-change-reading .*&lt;br /&gt;
attr LocalIPs stateFormat IPv4: allIPv4&amp;lt;br /&amp;gt;&lt;br /&gt;
IPv6: allIPv6&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40255</id>
		<title>Öffentliche IP-Adresse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40255"/>
		<updated>2025-06-16T16:30:12Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* HTTPMOD-Definition zum ermitteln der öffentlichen IPv4 und IPv6 via HTTPS-Webdienst */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
In FHEM kann die aktuell verwendete öffentliche IP-Adresse (IPv4/IPv6) auf verschiedene Arten ermittelt werden. Eine Methode ist der Abruf über externe Webdienste. Es kann auch auf lokale Router-Informationen (z. B. Fritzbox) oder systemnahe Module (z.B. STUN) zurückgegriffen werden.&lt;br /&gt;
&lt;br /&gt;
== HTTPMOD-Definition zum ermitteln der öffentlichen IPv4 und IPv6 via HTTPS-Webdienst ==&lt;br /&gt;
Die öffentliche IP kann man mit Webdiensten ermitteln, die einem die IPv4 und/oder IPv6 anzeigen mit der die Seite abgerufen wird:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod PublicIP HTTPMOD https://ipwho.de/json/ 600&lt;br /&gt;
attr PublicIP extractAllJSON 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Ein Abfrageintervall von 300 bis 600 Sekunden (5 bis 10 Minuten) wird empfohlen – häufigere Abfragen belasten die externen Dienste unnötig.&lt;br /&gt;
&lt;br /&gt;
=== Weitere Dienste ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Getestete Dienste zum ermitteln der öffentlichen IP via HTTPS&lt;br /&gt;
!Anbieter&lt;br /&gt;
!Adresse&lt;br /&gt;
!Zuletzt erfolgreich getestet im Jahr&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipify.org&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api64.ipify.org/?format=json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipwho.de&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipwho.de/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipinfo.io&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipinfo.io/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;myip.com&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api.myip.com/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ident.me&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://a.ident.me/json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Fritzbox nutzen ==&lt;br /&gt;
Wird eine Fritzbox als Router in das Internet verwendet, kann man die öffentliche IP sehr einfach vom Router abfragen. Der Vorteil dieser Lösung besteht darin, dass kein externer Dienst abgefragt werden muss:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Fritzbox FRITZBOX 192.168.123.123&lt;br /&gt;
attr Fritzbox INTERVAL 300&lt;br /&gt;
attr Fritzbox boxUser &amp;lt;dein Fritzbox User&amp;gt;&lt;br /&gt;
attr Fritzbox nonblockingTimeOut 100&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hierbei sind die Attribute wie die IP Adresse und Nutzername auf der Fritzbox anzupassen.&lt;br /&gt;
&lt;br /&gt;
Die IPv4 und IPv6 der Fritzbox, als auch das IPv6 Prefix des lokalen Netzes sind als Readings direkt abfragbar:&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv4_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Prefix&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== STUN Server nutzen ==&lt;br /&gt;
Alternativ kann ein STUN-Server zur Ermittlung der externen IP genutzt werden. Beispiel-Snippet (Perl):&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub myip {&lt;br /&gt;
  use STUN::Client;&lt;br /&gt;
  my $stun_client = STUN::Client-&amp;gt;new;&lt;br /&gt;
  $stun_client-&amp;gt;stun_server(&#039;stun.ekiga.net&#039;);&lt;br /&gt;
  my (undef, $result) = $stun_client-&amp;gt;get;&lt;br /&gt;
  return $result-&amp;gt;{&#039;attributes&#039;}-&amp;gt;{&#039;0001&#039;}-&amp;gt;{&#039;address&#039;};&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hinweis: Das Perl-Modul &amp;lt;code&amp;gt;STUN::Client&amp;lt;/code&amp;gt; ist ggf. nicht in jeder Distribution verfügbar und muss über CPAN installiert werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Details und Diskussion: https://forum.fhem.de/index.php?topic=141872.msg1343225#msg1343225&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40254</id>
		<title>Öffentliche IP-Adresse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40254"/>
		<updated>2025-06-16T16:29:44Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* STUN Server nutzen */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
In FHEM kann die aktuell verwendete öffentliche IP-Adresse (IPv4/IPv6) auf verschiedene Arten ermittelt werden. Eine Methode ist der Abruf über externe Webdienste. Es kann auch auf lokale Router-Informationen (z. B. Fritzbox) oder systemnahe Module (z.B. STUN) zurückgegriffen werden.&lt;br /&gt;
&lt;br /&gt;
== HTTPMOD-Definition zum ermitteln der öffentlichen IPv4 und IPv6 via HTTPS-Webdienst ==&lt;br /&gt;
Die öffentliche IP kann man mit Webdiensten ermitteln, die einem die IPv4 und/oder IPv6 anzeigen mit der die Seite abgerufen wird:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod PublicIP HTTPMOD https://ipwho.de/json/ 600&lt;br /&gt;
attr PublicIP extractAllJSON 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Ein Abfrageintervall von 300 bis 600 Sekunden (5 bis 10 Minuten) wird empfohlen – häufigere Abfragen belastend die externen Dienste unnötig.&lt;br /&gt;
&lt;br /&gt;
=== Weitere Dienste ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Getestete Dienste zum ermitteln der öffentlichen IP via HTTPS&lt;br /&gt;
!Anbieter&lt;br /&gt;
!Adresse&lt;br /&gt;
!Zuletzt erfolgreich getestet im Jahr&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipify.org&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api64.ipify.org/?format=json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipwho.de&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipwho.de/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipinfo.io&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipinfo.io/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;myip.com&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api.myip.com/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ident.me&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://a.ident.me/json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Fritzbox nutzen ==&lt;br /&gt;
Wird eine Fritzbox als Router in das Internet verwendet, kann man die öffentliche IP sehr einfach vom Router abfragen. Der Vorteil dieser Lösung besteht darin, dass kein externer Dienst abgefragt werden muss:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Fritzbox FRITZBOX 192.168.123.123&lt;br /&gt;
attr Fritzbox INTERVAL 300&lt;br /&gt;
attr Fritzbox boxUser &amp;lt;dein Fritzbox User&amp;gt;&lt;br /&gt;
attr Fritzbox nonblockingTimeOut 100&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hierbei sind die Attribute wie die IP Adresse und Nutzername auf der Fritzbox anzupassen.&lt;br /&gt;
&lt;br /&gt;
Die IPv4 und IPv6 der Fritzbox, als auch das IPv6 Prefix des lokalen Netzes sind als Readings direkt abfragbar:&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv4_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Prefix&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== STUN Server nutzen ==&lt;br /&gt;
Alternativ kann ein STUN-Server zur Ermittlung der externen IP genutzt werden. Beispiel-Snippet (Perl):&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub myip {&lt;br /&gt;
  use STUN::Client;&lt;br /&gt;
  my $stun_client = STUN::Client-&amp;gt;new;&lt;br /&gt;
  $stun_client-&amp;gt;stun_server(&#039;stun.ekiga.net&#039;);&lt;br /&gt;
  my (undef, $result) = $stun_client-&amp;gt;get;&lt;br /&gt;
  return $result-&amp;gt;{&#039;attributes&#039;}-&amp;gt;{&#039;0001&#039;}-&amp;gt;{&#039;address&#039;};&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hinweis: Das Perl-Modul &amp;lt;code&amp;gt;STUN::Client&amp;lt;/code&amp;gt; ist ggf. nicht in jeder Distribution verfügbar und muss über CPAN installiert werden.&lt;br /&gt;
&lt;br /&gt;
Weitere Details und Diskussion: https://forum.fhem.de/index.php?topic=141872.msg1343225#msg1343225&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40253</id>
		<title>Öffentliche IP-Adresse</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=%C3%96ffentliche_IP-Adresse&amp;diff=40253"/>
		<updated>2025-06-16T15:01:03Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Öffentliche IPv4 und IPv6 ermitteln, Wie in Snippet https://forum.fhem.de/index.php?topic=141872.0 besprochen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
In FHEM kann die aktuell verwendete öffentliche IP-Adresse (IPv4/IPv6) auf verschiedene Arten ermittelt werden. Eine Methode ist der Abruf über externe Webdienste. Es kann auch auf lokale Router-Informationen (z. B. Fritzbox) oder systemnahe Module (z.B. STUN) zurückgegriffen werden.&lt;br /&gt;
&lt;br /&gt;
== HTTPMOD-Definition zum ermitteln der öffentlichen IPv4 und IPv6 via HTTPS-Webdienst ==&lt;br /&gt;
Die öffentliche IP kann man mit Webdiensten ermitteln, die einem die IPv4 und/oder IPv6 anzeigen mit der die Seite abgerufen wird:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod PublicIP HTTPMOD https://ipwho.de/json/ 600&lt;br /&gt;
attr PublicIP extractAllJSON 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Ein Abfrageintervall von 300 bis 600 Sekunden (5 bis 10 Minuten) wird empfohlen – häufigere Abfragen belastend die externen Dienste unnötig.&lt;br /&gt;
&lt;br /&gt;
=== Weitere Dienste ===&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Getestete Dienste zum ermitteln der öffentlichen IP via HTTPS&lt;br /&gt;
!Anbieter&lt;br /&gt;
!Adresse&lt;br /&gt;
!Zuletzt erfolgreich getestet im Jahr&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipify.org&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api64.ipify.org/?format=json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipwho.de&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipwho.de/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ipinfo.io&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://ipinfo.io/json/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;myip.com&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://api.myip.com/&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;ident.me&amp;lt;/code&amp;gt;&lt;br /&gt;
|https://a.ident.me/json&lt;br /&gt;
|2025&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Fritzbox nutzen ==&lt;br /&gt;
Wird eine Fritzbox als Router in das Internet verwendet, kann man die öffentliche IP sehr einfach vom Router abfragen. Der Vorteil dieser Lösung besteht darin, dass kein externer Dienst abgefragt werden muss:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Fritzbox FRITZBOX 192.168.123.123&lt;br /&gt;
attr Fritzbox INTERVAL 300&lt;br /&gt;
attr Fritzbox boxUser &amp;lt;dein Fritzbox User&amp;gt;&lt;br /&gt;
attr Fritzbox nonblockingTimeOut 100&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hierbei sind die Attribute wie die IP Adresse und Nutzername auf der Fritzbox anzupassen.&lt;br /&gt;
&lt;br /&gt;
Die IPv4 und IPv6 der Fritzbox, als auch das IPv6 Prefix des lokalen Netzes sind als Readings direkt abfragbar:&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv4_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Extern&amp;lt;/code&amp;gt;&lt;br /&gt;
 &amp;lt;code&amp;gt;box_ipv6_Prefix&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== STUN Server nutzen ==&lt;br /&gt;
STUN Server ermöglichen es die externe IP Adresse zu ermitteln. Es gibt zur Zeit kein Standarddevice in FHEM und die Perl-Pakete sind nicht in jeder Distribution enthalten und es muss ggf. auf CPAN zurückgegriffen werden. Eine Snippet gibt es unter https://forum.fhem.de/index.php?topic=141872.msg1343225#msg1343225&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40182</id>
		<title>E-Mail senden</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40182"/>
		<updated>2025-05-26T05:11:07Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Info von @Remstäler zu Strato hinzugefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Um aus FHEM heraus E-Mails senden zu können, gibt es unterschiedliche Methoden, abhängig von der verwendeten Plattform.&lt;br /&gt;
&lt;br /&gt;
Lange Zeit war &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; sehr beliebt. Da aber die Homepage des Projekts nicht mehr online ist und Probleme nur noch via erhöhtem Supportaufwand gelöst werden, sollte diese Option als veraltet betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Eine Liste mit ressourcensparenden E-Mails Clients findet man bei dem OpenWRT Projekt: https://openwrt.org/docs/guide-user/services/email/smtp.client&lt;br /&gt;
&lt;br /&gt;
MSGMail ist ein FHEM eigenes Modul zum Versenden, die Einrichtung ist allerdings nicht gänzlich einfach. Anfängern sei die cURL Methode empfohlen.&lt;br /&gt;
&lt;br /&gt;
=== MSGMail (Linux, Windows) ===&lt;br /&gt;
FHEM bringt ein eigenes Device zum Senden von E-Mails mit, welches in Perl implementiert ist: [https://fhem.de/commandref.html#MSGMail MSGMail, https://fhem.de/commandref.html#MSGMail]&lt;br /&gt;
&lt;br /&gt;
=== cURL (Linux, Windows) ===&lt;br /&gt;
[https://curl.se/ cURL] kann auf praktisch [https://curl.se/download.html jedem Betriebssystem] genutzt werden, [https://curl.se/windows/ selbst unter Windows]. Es kann direkt mit einem SMTP Server E-Mails versenden und stellt damit eine moderne, portable und sichere Option zum Versenden von E-Mails dar.&lt;br /&gt;
&lt;br /&gt;
In FHEM richtet man sich ein Device ein und passt die Details (&amp;lt;code&amp;gt;$emailTo, $emailFrom, $emailServer&amp;lt;/code&amp;gt;) an. Das Programm cURL muss installiert sein (z.B. mit &amp;lt;code&amp;gt;apt install curl&amp;lt;/code&amp;gt;, oder durch Herunterladen und Installieren von der [https://curl.se cURL-Webseite]).&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod sendMail dummy&lt;br /&gt;
attr sendMail readingList message password&lt;br /&gt;
attr sendMail setList message:textField-long password&lt;br /&gt;
attr sendMail userReadings state:message:[\s\S]* {\&lt;br /&gt;
    my $emailTo   = &#039;du@example.com&#039;;;\&lt;br /&gt;
    my $emailFrom = &#039;fhem@example.de&#039;;;\&lt;br /&gt;
    my $emailServer = &#039;mail.your-server.de&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #erzeuge Datumsangabe konform zu RFC-5322:\&lt;br /&gt;
    my @emailDateArray = split(&#039; &#039;, strftime(&amp;quot;%w %d %m %Y %H:%M:%S %z&amp;quot;, localtime));;\&lt;br /&gt;
    $emailDateArray[0] = (qw[Sun Mon Tue Wed Thu Fri Sat])[$emailDateArray[0]] . &#039;,&#039;;;\&lt;br /&gt;
    $emailDateArray[2] = (qw[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec])[$emailDateArray[2]-1];;\&lt;br /&gt;
    my $emailDate = join(&#039; &#039;, @emailDateArray);;\&lt;br /&gt;
    \&lt;br /&gt;
    # Passwort aus getKeyValue abrufen\&lt;br /&gt;
    my ($err, $emailPass) = getKeyValue(&amp;quot;${name}_password&amp;quot;);;\&lt;br /&gt;
    if ($err || !defined $emailPass) {\&lt;br /&gt;
        return &amp;quot;Error retrieving password: $err&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    my $message = ReadingsVal($name, &#039;message&#039;, &#039;???&#039;);;\&lt;br /&gt;
    my $subject = &amp;quot;$name: FHEM Nachricht&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # Betreff extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
    if ($message =~ s/(?:Subject|Betreff)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        $subject = $1;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    # Empfänger extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
    if ($message =~ s/(?:To|An)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        $emailTo = $1;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #Dateianhänge bestimmen\&lt;br /&gt;
    my @attachments;;\&lt;br /&gt;
    while ($message =~ s/(?:Attachment|Anhang)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        my $file = $1;;\&lt;br /&gt;
        return &amp;quot;&amp;gt;&amp;gt;$file&amp;lt;&amp;lt; not found, not sending email&amp;quot; unless (-e $file);;\&lt;br /&gt;
        push (@attachments, $file);;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #der curl Befehl wird hier aufgebaut:\&lt;br /&gt;
    my $cmd = &amp;quot;curl -m 19 --noproxy &#039;*&#039; --no-progress-meter --ssl-reqd smtps://$emailServer:465 &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;--user &#039;$emailFrom:$emailPass&#039; --mail-from &#039;$emailFrom&#039; --mail-rcpt &#039;$emailTo&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;--write-out &#039;Email sent with status %{http_code}&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;Subject: $subject&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;From: $emailFrom&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;Date: $emailDate&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #multipart/alternative für Text und HTML EMail Body\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;=(;;type=multipart/alternative&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Anfang entfernen:\&lt;br /&gt;
    $message =~ s/^(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+//;;\&lt;br /&gt;
    # Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Ende entfernen:\&lt;br /&gt;
    $message =~ s/(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+$//;;\&lt;br /&gt;
    \&lt;br /&gt;
    #Plaintext E-Mail Inhalt an cURL:\&lt;br /&gt;
    $cmd .= &amp;quot;-F \&amp;quot;=$message;;type=text/plain;; charset=UTF-8\&amp;quot; &amp;quot;;;\&lt;br /&gt;
    #ersetze \n bzw. \r\n durch ein HTML-tag &amp;lt;br /&amp;gt;:\&lt;br /&gt;
    $message =~ s/\r?\n/&amp;lt;br \/&amp;gt;/g;;\&lt;br /&gt;
    \&lt;br /&gt;
    #HTML E-Mail Inhalt an cURL:\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;= &amp;lt;body&amp;gt;$message&amp;lt;/body&amp;gt;;;type=text/html;; charset=UTF-8&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;=)&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #jetzt alle Attachments (0..N) anhängen:\&lt;br /&gt;
    foreach my $file (@attachments) {\&lt;br /&gt;
        $cmd .= &amp;quot;-F &#039;=@&amp;quot;.$file.&amp;quot;;;encoder=base64&#039; &amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #stderr auch mit in den output sammeln:\&lt;br /&gt;
    $cmd .= &amp;quot; 2&amp;gt;&amp;amp;1&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc1) {\&lt;br /&gt;
        *sendMailfunc1 = sub ($) {\&lt;br /&gt;
            my ($param) = @_;;\&lt;br /&gt;
            my $result;;\&lt;br /&gt;
            $result = qx/$param/;;\&lt;br /&gt;
            $result = MIME::Base64::encode_base64($result);;\&lt;br /&gt;
            $result =~ s/\n//g;;\&lt;br /&gt;
            return $result;;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #Rückgabe über diese Funktion,\&lt;br /&gt;
    #anzeigen in dem reading dieses UserReadings:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc2) {\&lt;br /&gt;
        *sendMailfunc2 = sub ($) {\&lt;br /&gt;
            my ($result) = @_;;\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            $result = MIME::Base64::decode_base64($result);;\&lt;br /&gt;
            readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #funktion falls BlockingCall abgebrochen wird:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc3) {\&lt;br /&gt;
        *sendMailfunc3 = sub ($) {\&lt;br /&gt;
            my ($result) = @_;;\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    BlockingCall(&amp;quot;sendMailfunc1&amp;quot;, $cmd, &amp;quot;sendMailfunc2&amp;quot;, 20, &amp;quot;sendMailfunc3&amp;quot;, &amp;quot;Error: timeout&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return &amp;quot;started cURL command...&amp;quot;;;\&lt;br /&gt;
},\&lt;br /&gt;
state:password:.* {\&lt;br /&gt;
    # Passwort speichern\&lt;br /&gt;
    my $ret = setKeyValue(&amp;quot;${name}_password&amp;quot;, ReadingsVal($name, &#039;password&#039;, undef)) // &amp;quot;password stored&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\&lt;br /&gt;
    #\&lt;br /&gt;
    # Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\&lt;br /&gt;
    # Alternativ: { setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;password&amp;quot;, &amp;quot;****&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return &amp;quot;$ret&amp;quot;;;\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Das Passwort setzt man mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail password geheimesPasswort&amp;lt;/code&amp;gt;&lt;br /&gt;
* Alternativ, damit das Passwort nicht im Log auftaucht: &amp;lt;code&amp;gt;{ setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
E-Mails kann man dann Senden mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Mein Text&amp;lt;/code&amp;gt; (es wird der Standardbetreff und Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;(es wird der Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Subject=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;✅ Dies ist ein Test&amp;quot; Meldung mit Emoji ⚽ geht auch&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eine Datei mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/bag.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zwei Dateien mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;./www/images/fhemSVG/bag.svg&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/batterie.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;, für die anzuhängenden Dateien können relative und absolute Pfade verwendet werden. Beim Vorbereiten der E-Mail wird geprüft ob die Dateien existieren, da dies sonst eine typische Fehlerquelle ist. Es wird nichts versendet wenn eine Datei nicht gefunden werden kann.&lt;br /&gt;
&lt;br /&gt;
==== Fehlersuche ====&lt;br /&gt;
&lt;br /&gt;
===== Fehlende Protokolloption in cURL =====&lt;br /&gt;
Sollte cURL zwar installiert sein, aber keine E-Mails versenden, kann es sein, dass das Protokoll nicht einkompiliert wurde. Dies ist bei OpenWRT zum Beispiel der Fall.&lt;br /&gt;
&lt;br /&gt;
So sollte es aussehen (hier ein Ubuntu), relevant ist für diese Nutzung das Protokoll smtp und smtps, das braucht man:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.7&lt;br /&gt;
Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.6&lt;br /&gt;
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp&lt;br /&gt;
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So sollte es nicht aussehen (hier ein OpenWRT):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl: (1) Protocol &amp;quot;smtps&amp;quot; not supported&lt;br /&gt;
&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.10.1 (arm-openwrt-linux-gnu) libcurl/8.10.1 mbedTLS/3.6.2 nghttp2/1.63.0&lt;br /&gt;
Release-Date: 2024-09-18&lt;br /&gt;
Protocols: file ftp ftps http https ipfs ipns mqtt&lt;br /&gt;
Features: alt-svc HSTS HTTP2 HTTPS-proxy IPv6 Largefile SSL threadsafe UnixSockets&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Hat man nur ein cURL ohne das Protokoll, so muss man sich das Paket leider anderweitig beschaffen (chroot, selbst kompilieren, statisch gelinkte Version, ...).&lt;br /&gt;
&lt;br /&gt;
===== Falscher Port, login options =====&lt;br /&gt;
Einige Provider nehmen ihre TLS verschlüsselten E-Mails über einen anderen Port an, häufig Port 587 (siehe auch {{Link2Forum|Topic= 140814|Message=1340366|LinkText=diesen Forenbeitrag}}). Ggf. muss man auch mit einigen Loginoptionen experimentieren bis cURL  wie gewünscht funktioniert (Stichwort: &#039;&#039;[https://curl.se/docs/manpage.html#--login-options --login-options]&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
===== Alte cURL Version =====&lt;br /&gt;
Sollte eine sehr alte cURL Version kleiner 7.67.0 genutzt werden, fehlt der Schalter &amp;lt;code&amp;gt;--no-progress-meter.&amp;lt;/code&amp;gt; In solchen Fällen sollte man cURL aktualisieren. Falls dies nicht möglich ist, kann man den Schalter ersetzen durch &amp;lt;code&amp;gt;--silent --show-error&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Providerspezifisches=====&lt;br /&gt;
Eine lose Liste mit providerspezifischen Erfolgen und Misserfolgen:&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|+Liste mit Rückmeldungen zu E-Mail Anbietern&lt;br /&gt;
!Provider&lt;br /&gt;
!Funktioniert&lt;br /&gt;
!Details&lt;br /&gt;
|-&lt;br /&gt;
|Hetzner&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;mail.your-server.de&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailServer:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Vodafone&lt;br /&gt;
|Ja&lt;br /&gt;
|Folgende Schalter sind hilfreich:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
--ssl-reqd --sasl-ir smtps://$emailserver:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Freenet&lt;br /&gt;
|Nein (nicht zuverlässig aufgrund des Spamfilters)&lt;br /&gt;
|Der Spamfilter lässt teilweise Nachrichten passieren, teilweise wird die Verbindung abgelehnt:&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;&#039;550 Spam message rejected&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
https://forum.fhem.de/index.php?topic=140814.msg1341872#msg1341872&lt;br /&gt;
|-&lt;br /&gt;
|T-Online&lt;br /&gt;
|Ja?&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;securesmtp.t-online.de&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailServer:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;https://forum.fhem.de/index.php?topic=140814.msg1340258#msg1340258&lt;br /&gt;
|-&lt;br /&gt;
|GMX&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;mail.gmx.net&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailserver:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;https://forum.fhem.de/index.php?topic=140814.msg1341891#msg1341891&lt;br /&gt;
|-&lt;br /&gt;
|Google -&amp;gt; GMail&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;smtp.gmail.com&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailserver:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;https://forum.fhem.de/index.php?topic=140814.msg1341891#msg1341891&lt;br /&gt;
|-&lt;br /&gt;
|Strato&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;smtp.strato.de&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailserver:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;https://forum.fhem.de/index.php?topic=140814.msg1342201#msg1342201&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Siehe auch dazu den {{Link2Forum|Message=1341182|Topic=140814|LinkText=Forumspost}}&lt;br /&gt;
===EXIM4 (Debian)===&lt;br /&gt;
Eine Anleitung zum Versenden von E-Mails mit Exim ist als PDF verfügbar: [[Medium:Anleitung Exim4 Debian GMX.pdf|PDF]]&lt;br /&gt;
&lt;br /&gt;
===SSMTP (OpenWRT, embedded Distros)===&lt;br /&gt;
&lt;br /&gt;
====Installation====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install ssmtp_2.64-4_mpc85xx.ipk  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Damit &amp;lt;code&amp;gt;ssmtp&amp;lt;/code&amp;gt; funktioniert müssen die Dateien &amp;lt;code&amp;gt;/etc/ssmtp/ssmtp.conf&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;/etc/ssmtp/revaliases&amp;lt;/code&amp;gt; angepasst werden.&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/ssmtp.conf &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root=arnold@gmx.net&lt;br /&gt;
 mailhub=mail.gmx.net:465&lt;br /&gt;
 rewriteDomain=gmx.net&lt;br /&gt;
 hostname=gmx.net&lt;br /&gt;
 FromLineOverride=YES&lt;br /&gt;
 UseTLS=YES&lt;br /&gt;
 #UseSTARTTLS=YES&lt;br /&gt;
 AuthUser=arnold@gmx.net&lt;br /&gt;
 AuthPass=Passwort_von_arnold@gmx.net&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/revaliases&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root:arnold@gmx.net:mail.gmx.net:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der [[99_myUtils_anlegen|99_myUtils]] folgende Unterroutine einfügen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub OpenWRTMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $sender = &amp;quot;dockstar\@heye-tammo.de&amp;quot;; &lt;br /&gt;
 Log 1, &amp;quot;sendEmail RCP: $rcpt, Subject: $subject, Text: $text&amp;quot;;&lt;br /&gt;
 $ret .= qx(echo -e &#039;to:$rcpt\n from:$sender\nsubject:$subject\n$text\n&#039; | ssmtp $rcpt);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendEmail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===PHP Mail Funktion (Synology DiskStation)===&lt;br /&gt;
Beim DSM 3.2 funktioniert der &amp;lt;code&amp;gt;php-mail&amp;lt;/code&amp;gt;-Befehl, so dass man mittels folgendem Modul E-Mails versenden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub sendmail($$$) {&lt;br /&gt;
 my($empf, $subj, $nachricht) = @_;&lt;br /&gt;
 system(&amp;quot;php -r &#039;mail(\&amp;quot;$empf\&amp;quot;,\&amp;quot;$subj\&amp;quot;,\&amp;quot;$nachricht\&amp;quot;);&#039;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sendemail (Veraltet, Debian-basierende Distros)===&lt;br /&gt;
Es handelt sich um ein Perl Programm, dass man aus den Paketquellen installieren kann.&lt;br /&gt;
&lt;br /&gt;
Es gibt Probleme: &lt;br /&gt;
&lt;br /&gt;
*mit der TLS bzw STARTTLS Verschlüsselung (https://github.com/fhem/fhem-docker/issues/254, https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1915977.html) ,&lt;br /&gt;
*Mit der Webseite des Projekts (https://web.archive.org/web/20190906140125/http://caspian.dotconf.net/menu/Software/SendEmail); diese ist nicht mehr verfügbar und nur eine archivierte Kopie kann eingesehen werden,&lt;br /&gt;
&lt;br /&gt;
weswegen diese Lösung als technisch überholt gelten sollte. Will man trotzdem &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; verwenden folgenden hier die Informationen dazu:&lt;br /&gt;
&lt;br /&gt;
Auf dem Server muss &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; installiert werden:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt-get update&lt;br /&gt;
sudo apt-get install sendemail libio-socket-ssl-perl libnet-ssleay-perl perl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Man kann die korrekte Funktion im Terminal (ohne &amp;lt;code&amp;gt;{qx()}&amp;lt;/code&amp;gt; ) oder auch in der FHEM Kommandozeile testen (Hinweis: manche Provider (z.B. gmail) lassen diesen Zugang erst nach Freischaltung zu!).&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Bei Gmail muss seit dem 30.6.2022 ein separates Passwort erzeugt werden, die alte Einstellung über &amp;quot;Zugriff durch weniger sichere Apps&amp;quot; funktioniert nicht mehr. Siehe Support Seite Google: &amp;lt;nowiki&amp;gt;https://support.google.com/accounts/answer/185833&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Beispiel mit Gmail über Linux Terminalfenster:&#039;&#039;&lt;br /&gt;
 sendemail -f &#039;Der.Absender@gmail.com&#039; -t &#039;Der.Empfaenger@gmail.com&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt; -s &#039;smtp.gmail.com:587&#039; -xu &#039;Der.Absender&#039; -xp DASSUPERGEHEIMEPASSW0RT&lt;br /&gt;
In der [[99 myUtils anlegen|99_myUtils]] folgende Unterroutine einfügen:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
######## DebianMail  Mail auf dem RPi versenden ############ &lt;br /&gt;
# $provider für SMTP Server anpassen. &lt;br /&gt;
# Einmal in der FHEM Kommandozeile, user und password anpassen:&lt;br /&gt;
# {setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&lt;br /&gt;
sub DebianMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $attach = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $error;&lt;br /&gt;
 my $konto = getKeyValue(&amp;quot;myEmailKonto&amp;quot;); &lt;br /&gt;
 my $passwrd = getKeyValue(&amp;quot;myEmailPasswrd&amp;quot;); &lt;br /&gt;
 my $from = $konto; # or use different KeyValue if konto is not the from email address&lt;br /&gt;
 my $provider = &amp;quot;smtp.1und1.de&amp;quot;; # smtp.domain.tld:port see provider documentation&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail RCP: $rcpt&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Subject: $subject&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Text: $text&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Anhang: $attach&amp;quot;;&lt;br /&gt;
 if (not defined($attach)){$attach=&#039;&#039;}&lt;br /&gt;
 $ret .= qx(sendemail -f &#039;$from&#039; -t &#039;$rcpt&#039; -u &#039;$subject&#039; -m &#039;$text&#039; -a $attach -s &#039;$provider&#039; -xu &#039;$konto&#039; -xp &#039;$passwrd&#039; -o tls=auto -o message-charset=utf-8);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendemail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
###############################################################################&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Die Zugangsdaten für den Email-Account müssen dabei FHEM-intern gespeichert werden. Dazu gibt man einmalig über die Kommandozeile folgendes ein (user@domain und password müssen auf den Email Account geändert werden):&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;{setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die TLS Verschlüsselung (ehem. SSL) nicht zu nutzen, muss in der viertletzten Zeile tls=no verwendet und der Port des Mailproviders auf 25 eigetragen werden. Sollte anschließend keine Mail verschickt werden, siehe Probleme.&lt;br /&gt;
&lt;br /&gt;
Falls der Body-Text in einem (Android-)Mailer auf dem Handy nicht gezeigt wird, kann der Parameter &#039;&#039;&#039;-o message-content-type=html&#039;&#039;&#039; helfen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion kann beispielsweise folgendermaßen aufgerufen werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{qx(sendemail -f &#039;Ab\@send.er&#039; -t &#039;Emp\@faeng.er&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &#039;DateinameOderLeer&#039; -s &#039;smtpFQDN:port&#039; -xu &#039;MailUser&#039; -xp &#039;PasswortOhneKlammeraffen&#039;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bei allen Feldern können generell doppelte &amp;quot; oder einfache &#039; Anführungszeichen verwendet werden. Innerhalb von &amp;quot;&amp;quot; müssen Sonderzeichen wie @ aber maskiert werden, da sie sonst als Steuerzeichen interpretiert werden:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;quot;email\@email.domain&amp;quot; oder &#039;email@email.domain&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direkt in der FHEM Kommandozeile (ohne Anhang)&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;{DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ordnungsgemäße Ausführung wird mit 71 quittiert.&lt;br /&gt;
&lt;br /&gt;
Beispiele mehrere Anhänge (Es ist wichtig Dateinamen mit Leerzeichen in separate Anführungszeichen zu setzen)&lt;br /&gt;
 {DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;Anhang1 Anhang2 Anhang3&amp;quot;)}&lt;br /&gt;
 {DebianMail(&#039;email@email.domain&#039;,&#039;Subject&#039;,&#039;Text&#039;,&amp;quot;&#039;with spaces Anhang1&#039; &#039;with spaces Anhang2&#039;&amp;quot;)}&lt;br /&gt;
oder (mehr als einen Empfänger, diese können durch Leerzeichen, Semikolon oder Komma getrennt sein)&lt;br /&gt;
 define Sonstiges at *01:00:00 {\&lt;br /&gt;
 DebianMail(&#039;email@email.domain,email2@email.domain&#039;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;&#039;Anhang1&#039; &#039;Anhang2&#039; ...&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Die archivierte Doku zu &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; befindet sich in der Änderungshistorie: https://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;amp;oldid=37524&lt;br /&gt;
[[Kategorie:E-Mail]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Matrix&amp;diff=40180</id>
		<title>Matrix</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Matrix&amp;diff=40180"/>
		<updated>2025-05-24T17:07:31Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* Matrix per Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Allgemeines ==&lt;br /&gt;
&#039;&#039;&#039;Matrix&#039;&#039;&#039; ist ein offenes Kommunikationsprotokoll für dezentrale Echtzeitkommunikation. Es wird von zahlreichen Clients unterstützt, darunter &#039;&#039;&#039;Element&#039;&#039;&#039;, &#039;&#039;&#039;Element-X&#039;&#039;&#039;, &#039;&#039;&#039;SchildiChat Next&#039;&#039;&#039;, &#039;&#039;&#039;FluffyChat&#039;&#039;&#039; und viele weitere.&lt;br /&gt;
&lt;br /&gt;
Viele Open-Source-Projekte betreiben eigene Chaträume auf föderierten Matrix-Servern. Ein Beispiel dafür ist der &#039;&#039;&#039;Gadgetbridge-Chat&#039;&#039;&#039;, einer der größeren und aktiveren Räume im Matrix-Ökosystem.&lt;br /&gt;
&lt;br /&gt;
== Matrix und FHEM ==&lt;br /&gt;
Um mit &#039;&#039;&#039;FHEM&#039;&#039;&#039; Nachrichten über das Matrix-Protokoll zu senden und zu empfangen, stehen mehrere Möglichkeiten zur Verfügung. Eine einfache und pragmatische Lösung ist die Nutzung eines &#039;&#039;&#039;HTTPMOD-Devices&#039;&#039;&#039;. Damit lassen sich Nachrichten über die Matrix-API versenden oder empfangen.&lt;br /&gt;
&lt;br /&gt;
=== Matrix per Device ===&lt;br /&gt;
Es gibt ein Device für Matrix unter https://git.cooltux.net/FHEM/mod-matrix bzw https://github.com/Man-fred/matrix&lt;br /&gt;
&lt;br /&gt;
==== Einrichten ====&lt;br /&gt;
Das Device ist nicht im FHEM Umfang enthalten und wird deswegen mit den folgenden Befehlen hinzugefügt:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;update add &amp;lt;nowiki&amp;gt;https://git.cooltux.net/FHEM/mod-matrix/raw/branch/dev/controls_Matrix.txt&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;update&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;shutdown restart&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Weiteres ====&lt;br /&gt;
https://forum.fhem.de/index.php?topic=129978.0&lt;br /&gt;
&lt;br /&gt;
=== Matrix per HTTPMOD Device ===&lt;br /&gt;
[[Datei:Screenshot des Matrix HTTPMOD Device.png|alternativtext=Screenshot des Matrix HTTPMOD Device|ohne|mini|Screenshot des Matrix HTTPMOD Device]]&lt;br /&gt;
Folgendes Device kann Matrix Nachrichten senden und empfangen, es hat wenige Abhängigkeiten und sollte in den meisten FHEM Installationen unkompliziert eingerichtet sein. Es wird die Datenübertragung von FHEM zum Server mit HTTPS verschlüsselt, E2EE wird nicht verwendet.&lt;br /&gt;
&lt;br /&gt;
==== Einrichtung ====&lt;br /&gt;
Es sind die Attribute MatrixServer und MatrixUser zu setzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixServer Dein-Servername&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixUser FHEM-Matrix-Username&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Passwort des Matrix-FHEM-Users setzt man mit dem Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot storeKeyValue MatrixPassword yourPassword123&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann richtet man einen, &#039;&#039;&#039;nicht E2E verschlüsselten Raum&#039;&#039;&#039; ein in dem der Matrix-FHEM-Account eingeladen ist. Von dem Raum benötigt man die MatrixRoomID, die man mit Element in den Raumdetails finden kann (Room Settings --&amp;gt; Advanced --&amp;gt; Internal room ID).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixRoomID !12345678901234:example.com&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Verwendung ====&lt;br /&gt;
Nachrichten senden geht mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot sendText Bla Bla Bla&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Longpoll von Nachrichten starten und stoppen mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot longpollCmd startTimer&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot longpollCmd stopTimer&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Weiteres ====&lt;br /&gt;
&lt;br /&gt;
===== Forumlink =====&lt;br /&gt;
https://forum.fhem.de/index.php?topic=120834.msg1339487#msg1339487&lt;br /&gt;
&lt;br /&gt;
===== Demo-Device =====&lt;br /&gt;
Das folgende DOIF-Device lauscht auf eine eingehende Nachricht die als Inhalt &amp;quot;ping&amp;quot; oder &amp;quot;Ping&amp;quot; ist und antwortet mit &amp;quot;Pong!&amp;quot;. Es dient als Beispiel und kann natürlich um weitere Befehle ergänzt/ersetzt werden um FHEM damit zu steuern:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod MatrixPing.doif DOIF ([&amp;quot;MatrixBot:msg: .*: @.*: [Pp]ing$&amp;quot;])\&lt;br /&gt;
  (set MatrixBot sendText Pong!)&lt;br /&gt;
attr MatrixPing.doif alias Matrix Ping/Pong&lt;br /&gt;
attr MatrixPing.doif do always&lt;br /&gt;
attr MatrixPing.doif group Meldungen&lt;br /&gt;
attr MatrixPing.doif icon homeConnect&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Device-Definition ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod MatrixBot HTTPMOD none 0&lt;br /&gt;
attr MatrixBot userattr MatrixRoomID MatrixServer MatrixUser&lt;br /&gt;
attr MatrixBot MatrixRoomID !12345678901234:nope.chat&lt;br /&gt;
attr MatrixBot MatrixServer nope.chat&lt;br /&gt;
attr MatrixBot MatrixUser DeinUsername&lt;br /&gt;
attr MatrixBot bodyDecode utf-8&lt;br /&gt;
attr MatrixBot bodyEncode utf-8&lt;br /&gt;
attr MatrixBot comment &amp;quot;\&lt;br /&gt;
Create a room for FHEM.\&lt;br /&gt;
\&lt;br /&gt;
The room must not use encryption, a room that has encryption\&lt;br /&gt;
enabled, cannot be converted to a non-encrypted room anymore \&lt;br /&gt;
\&lt;br /&gt;
The room-id can be found in Element-Web at:\&lt;br /&gt;
Room Settings --&amp;gt; Advanced --&amp;gt; Internal room ID\&lt;br /&gt;
\&lt;br /&gt;
To store the password in FHEM in obfuscated way:\&lt;br /&gt;
set MatrixBot storeKeyValue MatrixPassword yourPassword123\&lt;br /&gt;
\&lt;br /&gt;
###\&lt;br /&gt;
To send a text:\&lt;br /&gt;
set MatrixBot sendText Bla Bla Bla\&lt;br /&gt;
\&lt;br /&gt;
\&lt;br /&gt;
###\&lt;br /&gt;
To longpoll for messages once:\&lt;br /&gt;
# 1. send special filter to Matrix:\&lt;br /&gt;
set MatrixBot sendFilter\&lt;br /&gt;
\&lt;br /&gt;
# 2. start one longPoll (waits up to 60 seconds\&lt;br /&gt;
#                        or until data is available)\&lt;br /&gt;
get MatrixBot longpoll\&lt;br /&gt;
\&lt;br /&gt;
#alternatively, to keep on longPolling (this also sets filter):\&lt;br /&gt;
set MatrixBot longpollCmd startTimer\&lt;br /&gt;
#to stop the timers:\&lt;br /&gt;
set MatrixBot longpollCmd stopTimer\&lt;br /&gt;
\&lt;br /&gt;
#############################################################\&lt;br /&gt;
https://spec.matrix.org/v1.14/client-server-api/#syncing\&lt;br /&gt;
&amp;quot;&lt;br /&gt;
attr MatrixBot get02AlwaysNum 0&lt;br /&gt;
attr MatrixBot get02HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot get02Name longpoll&lt;br /&gt;
attr MatrixBot get02Regex \&amp;quot;next_batch\&amp;quot;:\s*\&amp;quot;(?&amp;lt;next_batch&amp;gt;[^\&amp;quot;]+)\&amp;quot;(?:.*?\&amp;quot;timeline\&amp;quot;:\s*{\s*\&amp;quot;events\&amp;quot;:\s*(?&amp;lt;messages&amp;gt;\[.*?\])\s*)?&lt;br /&gt;
attr MatrixBot get02TextArg 0&lt;br /&gt;
attr MatrixBot get02URL https://[$name:MatrixServer]/_matrix/client/v3/sync?timeout=60000&amp;amp;filter=[$name:filter_id]%%next_batch_param%%&lt;br /&gt;
attr MatrixBot icon message_info&lt;br /&gt;
attr MatrixBot parseFunction1 handleAuthErrors&lt;br /&gt;
attr MatrixBot reAuthAlways 0&lt;br /&gt;
attr MatrixBot reAuthRegex M_UNKNOWN_TOKEN&lt;br /&gt;
attr MatrixBot replacement01Mode expression&lt;br /&gt;
attr MatrixBot replacement01Regex \[([^:\s\[\&amp;quot;\&#039;]+):([^\]\s]+)\]&lt;br /&gt;
attr MatrixBot replacement01Value my $device = $name if ($1 eq &amp;quot;\$name&amp;quot;) // $1;;\&lt;br /&gt;
ReadingsVal($device, $2, undef) or AttrVal($device, $2, &amp;quot;???&amp;quot;);;&lt;br /&gt;
attr MatrixBot replacement02Mode expression&lt;br /&gt;
attr MatrixBot replacement02Regex %%uuid%%&lt;br /&gt;
attr MatrixBot replacement02Value join(&amp;quot;-&amp;quot;, unpack(&amp;quot;A8 A4 A4 A4 A12&amp;quot;, unpack(&amp;quot;H*&amp;quot;, join(&amp;quot;&amp;quot;, map { chr(int rand 256) } 0..15))))&lt;br /&gt;
attr MatrixBot replacement03Mode key&lt;br /&gt;
attr MatrixBot replacement03Regex %%MatrixPassword%%&lt;br /&gt;
attr MatrixBot replacement03Value MatrixPassword&lt;br /&gt;
attr MatrixBot replacement04Mode expression&lt;br /&gt;
attr MatrixBot replacement04Regex %%next_batch_param%%&lt;br /&gt;
attr MatrixBot replacement04Value #is there a reading &#039;next_batch&#039;?\&lt;br /&gt;
my $val = ReadingsVal($name, &#039;next_batch&#039;, &#039;???&#039;);;\&lt;br /&gt;
\&lt;br /&gt;
#return the GET parameter &#039;sync=value&#039; for /sync Endpoint\&lt;br /&gt;
return &amp;quot;&amp;amp;since=$val&amp;quot; if ($val ne &#039;???&#039;);;\&lt;br /&gt;
\&lt;br /&gt;
#return neither since-key nor value for it:\&lt;br /&gt;
return &amp;quot;&amp;quot;;;&lt;br /&gt;
attr MatrixBot set01Data {\&lt;br /&gt;
  &amp;quot;msgtype&amp;quot;: &amp;quot;m.text&amp;quot;,\&lt;br /&gt;
  &amp;quot;body&amp;quot;: &amp;quot;$val&amp;quot;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot set01HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot set01HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot set01IExpr #cancel an active longpoll\&lt;br /&gt;
if ($hash-&amp;gt;{BUSY}\&lt;br /&gt;
    &amp;amp;&amp;amp; $hash-&amp;gt;{HttpUtils}\&lt;br /&gt;
    &amp;amp;&amp;amp; $hash-&amp;gt;{HttpUtils}-&amp;gt;{url} =~ m|^https://[^/]+/_matrix/client/v3/sync\?timeout=\d+&amp;amp;filter=| ) {\&lt;br /&gt;
    Log(3, &amp;quot;$name: longpoll active, cutting it off now&amp;quot;);;\&lt;br /&gt;
    HttpUtils_Close($hash-&amp;gt;{HttpUtils});;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
#just return $val unteraltered\&lt;br /&gt;
$val&lt;br /&gt;
attr MatrixBot set01Method POST&lt;br /&gt;
attr MatrixBot set01Name sendText&lt;br /&gt;
attr MatrixBot set01ParseResponse 1&lt;br /&gt;
attr MatrixBot set01Regex {\&amp;quot;event_id\&amp;quot;:\&amp;quot;(.*)\&amp;quot;}&lt;br /&gt;
attr MatrixBot set01TextArg 1&lt;br /&gt;
attr MatrixBot set01URL https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%&lt;br /&gt;
attr MatrixBot set02Data {\&lt;br /&gt;
  &amp;quot;room&amp;quot;: {\&lt;br /&gt;
    &amp;quot;rooms&amp;quot;: [&amp;quot;[$name:MatrixRoomID]&amp;quot;],\&lt;br /&gt;
    &amp;quot;timeline&amp;quot;: {\&lt;br /&gt;
      &amp;quot;limit&amp;quot;: 10,\&lt;br /&gt;
      &amp;quot;types&amp;quot;: [&amp;quot;m.room.message&amp;quot;]\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;include_leave&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_join&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_account_data&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_state&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;state&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;ephemeral&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;account_data&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    }\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;event_fields&amp;quot;: [\&lt;br /&gt;
    &amp;quot;content.body&amp;quot;,\&lt;br /&gt;
    &amp;quot;sender&amp;quot;,\&lt;br /&gt;
    &amp;quot;origin_server_ts&amp;quot;\&lt;br /&gt;
  ],\&lt;br /&gt;
  &amp;quot;event_format&amp;quot;: &amp;quot;client&amp;quot;,\&lt;br /&gt;
  &amp;quot;presence&amp;quot;: {\&lt;br /&gt;
    &amp;quot;types&amp;quot;: [],\&lt;br /&gt;
    &amp;quot;not_types&amp;quot;: [&amp;quot;*&amp;quot;]\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;account_data&amp;quot;: {\&lt;br /&gt;
    &amp;quot;types&amp;quot;: [],\&lt;br /&gt;
    &amp;quot;not_types&amp;quot;: [&amp;quot;*&amp;quot;]\&lt;br /&gt;
  }\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot set02HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot set02HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot set02Method POST&lt;br /&gt;
attr MatrixBot set02Name sendFilter&lt;br /&gt;
attr MatrixBot set02NoArg 1&lt;br /&gt;
attr MatrixBot set02ParseResponse 1&lt;br /&gt;
attr MatrixBot set02Regex \&amp;quot;filter_id\&amp;quot;:\s*\&amp;quot;(?&amp;lt;filter_id&amp;gt;\d+)\&amp;quot;&lt;br /&gt;
attr MatrixBot set02URL https://[$name:MatrixServer]/_matrix/client/v3/user/@[$name:MatrixUser]:[$name:MatrixServer]/filter&lt;br /&gt;
attr MatrixBot set03Local 1&lt;br /&gt;
attr MatrixBot set03Name longpollCmd&lt;br /&gt;
attr MatrixBot set03TextArg 1&lt;br /&gt;
attr MatrixBot showError 1&lt;br /&gt;
attr MatrixBot sid01Data {\&lt;br /&gt;
  &amp;quot;type&amp;quot;: &amp;quot;m.login.password&amp;quot;,\&lt;br /&gt;
  &amp;quot;identifier&amp;quot;: {\&lt;br /&gt;
    &amp;quot;type&amp;quot;: &amp;quot;m.id.user&amp;quot;,\&lt;br /&gt;
    &amp;quot;user&amp;quot;: &amp;quot;[$name:MatrixUser]&amp;quot;\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;password&amp;quot;: &amp;quot;%%MatrixPassword%%&amp;quot;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot sid01HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot sid01IdRegex &amp;quot;access_token&amp;quot;\s*:\s*&amp;quot;([^&amp;quot;]+)&amp;quot;&lt;br /&gt;
attr MatrixBot sid01URL https://[$name:MatrixServer]/_matrix/client/v3/login&lt;br /&gt;
attr MatrixBot timeout 65&lt;br /&gt;
attr MatrixBot userReadings longpollTimer:(next_batch|longpollCmd|LAST_ERROR|sendText):.* {\&lt;br /&gt;
  my $longpollCmd = ReadingsVal($name, &#039;longpollCmd&#039;, &#039;???&#039;);;\&lt;br /&gt;
  my $delay = 1;;\&lt;br /&gt;
  my $timeout = AttrVal($name, &#039;timeout&#039;, 61) + $delay + 5;;\&lt;br /&gt;
  \&lt;br /&gt;
  # stop our timers:\&lt;br /&gt;
  if ($longpollCmd eq &amp;quot;stopTimer&amp;quot;) {\&lt;br /&gt;
    fhem(&amp;quot;cancel ${name}_longpollTimer quiet&amp;quot;);;\&lt;br /&gt;
    fhem(&amp;quot;cancel ${name}_longpollTimer2 quiet&amp;quot;);;\&lt;br /&gt;
    return &amp;quot;stopped&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  if ($longpollCmd ne &amp;quot;startTimer&amp;quot;) {\&lt;br /&gt;
    return &amp;quot;no timer set, longpollCmd is not set to &#039;startTimer&#039;&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #if startTimer cmd was given now, set filter as well:\&lt;br /&gt;
  if (ReadingsAge($name, &#039;longpollCmd&#039;, 0) &amp;lt;= 1) {\&lt;br /&gt;
    $delay = 5;; #delay to allow for sendFilter to be answered\&lt;br /&gt;
    #Log(1, &amp;quot;🪲 $name: &amp;gt;&amp;gt;&amp;quot;. InternalVal($name, &#039;httpbody&#039;, &#039;???&#039;) .&amp;quot;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    fhem(&amp;quot;sleep 0.1 quiet;; set $name sendFilter&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #we handle an error reported by http-utils:\&lt;br /&gt;
  if (ReadingsAge($name, &#039;LAST_ERROR&#039;, 0) &amp;lt;= 1) {\&lt;br /&gt;
    my $last_error = ReadingsVal($name, &#039;LAST_ERROR&#039;, &#039;???&#039;);;\&lt;br /&gt;
    $delay = 10;; #delay to allow for error reasons to improve\&lt;br /&gt;
    #Log(1, &amp;quot;🪲 $name: Dealing with error: &amp;gt;&amp;gt;$last_error&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  # for testing this: { $defs{MatrixBot}{sid} = &#039;bla&#039; }\&lt;br /&gt;
  if (!defined &amp;amp;HTTPMOD::handleAuthErrors) {\&lt;br /&gt;
    *HTTPMOD::handleAuthErrors = sub {\&lt;br /&gt;
      my ($hash, $header, $body, $request) = @_;;\&lt;br /&gt;
      my $name = $hash-&amp;gt;{NAME};;\&lt;br /&gt;
      my $status;;\&lt;br /&gt;
      \&lt;br /&gt;
      if ($header =~ m{^HTTP/\d\.\d\s+(\d+)}m) {\&lt;br /&gt;
        $status = $1;;\&lt;br /&gt;
      }\&lt;br /&gt;
      \&lt;br /&gt;
      Log3($name, 4, &amp;quot;$name: HTTP status code is $status&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
      if ( $status == 401 || $status == 403 || $status == 500 ) {\&lt;br /&gt;
        Log3($name, 3, &amp;quot;$name: auth-error or servererror ($status), calling doAuth()&amp;quot;);;\&lt;br /&gt;
        HTTPMOD::DoAuth($hash);;\&lt;br /&gt;
      }\&lt;br /&gt;
    };;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #set timers, one regular and one fallback:\&lt;br /&gt;
  fhem(&amp;quot;sleep $delay ${name}_longpollTimer quiet;; get $name longpoll&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;sleep $timeout ${name}_longpollTimer2 quiet;; set $name longpollCmd startTimer&amp;quot;);;\&lt;br /&gt;
  \&lt;br /&gt;
  return strftime(&amp;quot;next longpoll at %H:%M:%S&amp;quot;, localtime( time()+$delay ));;\&lt;br /&gt;
},\&lt;br /&gt;
messages_list:messages:.* {\&lt;br /&gt;
  my $this = ReadingsVal($name, $reading, &#039;&#039;);;\&lt;br /&gt;
  my @timestampArray = split(&amp;quot;\n&amp;quot;, $this);;\&lt;br /&gt;
  my $messages_ref = decode_json(ReadingsVal($name, &#039;messages&#039;, &#039;&#039;));;\&lt;br /&gt;
  my $length = 20;;\&lt;br /&gt;
  \&lt;br /&gt;
  my %seen_messages = map { $_ =&amp;gt; 1 } @timestampArray;;\&lt;br /&gt;
  \&lt;br /&gt;
  foreach my $msg (@$messages_ref) {\&lt;br /&gt;
    my $val = $msg-&amp;gt;{content}{body};;\&lt;br /&gt;
    $val = Encode::decode(&#039;utf-8&#039;, $val) unless Encode::is_utf8($val);;\&lt;br /&gt;
    $val =~ s/\n/ /g;; #replace newlines with spaces\&lt;br /&gt;
    \&lt;br /&gt;
    my $sender = $msg-&amp;gt;{sender};;\&lt;br /&gt;
    my $ts = strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;, localtime($msg-&amp;gt;{origin_server_ts} / 1000));;\&lt;br /&gt;
    my $new_entry = &amp;quot;$ts: $sender: $val&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    next if $seen_messages{$new_entry};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $inserted = 0;;\&lt;br /&gt;
    for (my $i = 0;; $i &amp;lt; @timestampArray;; $i++) {\&lt;br /&gt;
      my ($existing_ts) = $timestampArray[$i] =~ /^([^:]+):/;;\&lt;br /&gt;
      if ($ts lt $existing_ts) {\&lt;br /&gt;
        splice(@timestampArray, $i, 0, $new_entry);;\&lt;br /&gt;
        $inserted = 1;;\&lt;br /&gt;
        last;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
    push(@timestampArray, encode(&#039;utf-8&#039;, $new_entry)) unless $inserted;;\&lt;br /&gt;
    shift(@timestampArray) while @timestampArray &amp;gt; $length;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  return join(&amp;quot;\n&amp;quot;, @timestampArray);;\&lt;br /&gt;
},\&lt;br /&gt;
process_messages:messages:.* {\&lt;br /&gt;
  my $val = ReadingsVal($name, &#039;messages_list&#039;, &#039;&#039;);;\&lt;br /&gt;
  my $this = ReadingsVal($name, $reading, &#039;&#039;);;\&lt;br /&gt;
  my $latest_ts = $this;;\&lt;br /&gt;
  \&lt;br /&gt;
  foreach my $line (split(/\n/, $val)) {\&lt;br /&gt;
    if ($line =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}):\s*(.*)$/) {\&lt;br /&gt;
      my ($ts, $msg) = ($1, $2);;\&lt;br /&gt;
      \&lt;br /&gt;
      if ($ts gt $this) {\&lt;br /&gt;
        fhem(&amp;quot;trigger $name msg: $line&amp;quot;);;\&lt;br /&gt;
        $latest_ts = $ts if ($ts gt $latest_ts);;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  return $latest_ts;;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot verbose 3&lt;br /&gt;
attr MatrixBot widgetOverride longpollCmd:uzsuSelectRadio,startTimer,stopTimer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Matrix&amp;diff=40179</id>
		<title>Matrix</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Matrix&amp;diff=40179"/>
		<updated>2025-05-24T16:57:42Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* Matrix per Device */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Allgemeines ==&lt;br /&gt;
&#039;&#039;&#039;Matrix&#039;&#039;&#039; ist ein offenes Kommunikationsprotokoll für dezentrale Echtzeitkommunikation. Es wird von zahlreichen Clients unterstützt, darunter &#039;&#039;&#039;Element&#039;&#039;&#039;, &#039;&#039;&#039;Element-X&#039;&#039;&#039;, &#039;&#039;&#039;SchildiChat Next&#039;&#039;&#039;, &#039;&#039;&#039;FluffyChat&#039;&#039;&#039; und viele weitere.&lt;br /&gt;
&lt;br /&gt;
Viele Open-Source-Projekte betreiben eigene Chaträume auf föderierten Matrix-Servern. Ein Beispiel dafür ist der &#039;&#039;&#039;Gadgetbridge-Chat&#039;&#039;&#039;, einer der größeren und aktiveren Räume im Matrix-Ökosystem.&lt;br /&gt;
&lt;br /&gt;
== Matrix und FHEM ==&lt;br /&gt;
Um mit &#039;&#039;&#039;FHEM&#039;&#039;&#039; Nachrichten über das Matrix-Protokoll zu senden und zu empfangen, stehen mehrere Möglichkeiten zur Verfügung. Eine einfache und pragmatische Lösung ist die Nutzung eines &#039;&#039;&#039;HTTPMOD-Devices&#039;&#039;&#039;. Damit lassen sich Nachrichten über die Matrix-API versenden oder empfangen.&lt;br /&gt;
&lt;br /&gt;
=== Matrix per Device ===&lt;br /&gt;
Es gibt ein Device für Matrix unter https://git.cooltux.net/FHEM/mod-matrix bzw https://github.com/Man-fred/matrix&lt;br /&gt;
&lt;br /&gt;
=== Matrix per HTTPMOD Device ===&lt;br /&gt;
[[Datei:Screenshot des Matrix HTTPMOD Device.png|alternativtext=Screenshot des Matrix HTTPMOD Device|ohne|mini|Screenshot des Matrix HTTPMOD Device]]&lt;br /&gt;
Folgendes Device kann Matrix Nachrichten senden und empfangen, es hat wenige Abhängigkeiten und sollte in den meisten FHEM Installationen unkompliziert eingerichtet sein. Es wird die Datenübertragung von FHEM zum Server mit HTTPS verschlüsselt, E2EE wird nicht verwendet.&lt;br /&gt;
&lt;br /&gt;
==== Einrichtung ====&lt;br /&gt;
Es sind die Attribute MatrixServer und MatrixUser zu setzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixServer Dein-Servername&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixUser FHEM-Matrix-Username&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Passwort des Matrix-FHEM-Users setzt man mit dem Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot storeKeyValue MatrixPassword yourPassword123&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann richtet man einen, &#039;&#039;&#039;nicht E2E verschlüsselten Raum&#039;&#039;&#039; ein in dem der Matrix-FHEM-Account eingeladen ist. Von dem Raum benötigt man die MatrixRoomID, die man mit Element in den Raumdetails finden kann (Room Settings --&amp;gt; Advanced --&amp;gt; Internal room ID).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixRoomID !12345678901234:example.com&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Verwendung ====&lt;br /&gt;
Nachrichten senden geht mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot sendText Bla Bla Bla&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Longpoll von Nachrichten starten und stoppen mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot longpollCmd startTimer&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot longpollCmd stopTimer&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Weiteres ====&lt;br /&gt;
&lt;br /&gt;
===== Forumlink =====&lt;br /&gt;
https://forum.fhem.de/index.php?topic=120834.msg1339487#msg1339487&lt;br /&gt;
&lt;br /&gt;
===== Demo-Device =====&lt;br /&gt;
Das folgende DOIF-Device lauscht auf eine eingehende Nachricht die als Inhalt &amp;quot;ping&amp;quot; oder &amp;quot;Ping&amp;quot; ist und antwortet mit &amp;quot;Pong!&amp;quot;. Es dient als Beispiel und kann natürlich um weitere Befehle ergänzt/ersetzt werden um FHEM damit zu steuern:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod MatrixPing.doif DOIF ([&amp;quot;MatrixBot:msg: .*: @.*: [Pp]ing$&amp;quot;])\&lt;br /&gt;
  (set MatrixBot sendText Pong!)&lt;br /&gt;
attr MatrixPing.doif alias Matrix Ping/Pong&lt;br /&gt;
attr MatrixPing.doif do always&lt;br /&gt;
attr MatrixPing.doif group Meldungen&lt;br /&gt;
attr MatrixPing.doif icon homeConnect&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Device-Definition ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod MatrixBot HTTPMOD none 0&lt;br /&gt;
attr MatrixBot userattr MatrixRoomID MatrixServer MatrixUser&lt;br /&gt;
attr MatrixBot MatrixRoomID !12345678901234:nope.chat&lt;br /&gt;
attr MatrixBot MatrixServer nope.chat&lt;br /&gt;
attr MatrixBot MatrixUser DeinUsername&lt;br /&gt;
attr MatrixBot bodyDecode utf-8&lt;br /&gt;
attr MatrixBot bodyEncode utf-8&lt;br /&gt;
attr MatrixBot comment &amp;quot;\&lt;br /&gt;
Create a room for FHEM.\&lt;br /&gt;
\&lt;br /&gt;
The room must not use encryption, a room that has encryption\&lt;br /&gt;
enabled, cannot be converted to a non-encrypted room anymore \&lt;br /&gt;
\&lt;br /&gt;
The room-id can be found in Element-Web at:\&lt;br /&gt;
Room Settings --&amp;gt; Advanced --&amp;gt; Internal room ID\&lt;br /&gt;
\&lt;br /&gt;
To store the password in FHEM in obfuscated way:\&lt;br /&gt;
set MatrixBot storeKeyValue MatrixPassword yourPassword123\&lt;br /&gt;
\&lt;br /&gt;
###\&lt;br /&gt;
To send a text:\&lt;br /&gt;
set MatrixBot sendText Bla Bla Bla\&lt;br /&gt;
\&lt;br /&gt;
\&lt;br /&gt;
###\&lt;br /&gt;
To longpoll for messages once:\&lt;br /&gt;
# 1. send special filter to Matrix:\&lt;br /&gt;
set MatrixBot sendFilter\&lt;br /&gt;
\&lt;br /&gt;
# 2. start one longPoll (waits up to 60 seconds\&lt;br /&gt;
#                        or until data is available)\&lt;br /&gt;
get MatrixBot longpoll\&lt;br /&gt;
\&lt;br /&gt;
#alternatively, to keep on longPolling (this also sets filter):\&lt;br /&gt;
set MatrixBot longpollCmd startTimer\&lt;br /&gt;
#to stop the timers:\&lt;br /&gt;
set MatrixBot longpollCmd stopTimer\&lt;br /&gt;
\&lt;br /&gt;
#############################################################\&lt;br /&gt;
https://spec.matrix.org/v1.14/client-server-api/#syncing\&lt;br /&gt;
&amp;quot;&lt;br /&gt;
attr MatrixBot get02AlwaysNum 0&lt;br /&gt;
attr MatrixBot get02HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot get02Name longpoll&lt;br /&gt;
attr MatrixBot get02Regex \&amp;quot;next_batch\&amp;quot;:\s*\&amp;quot;(?&amp;lt;next_batch&amp;gt;[^\&amp;quot;]+)\&amp;quot;(?:.*?\&amp;quot;timeline\&amp;quot;:\s*{\s*\&amp;quot;events\&amp;quot;:\s*(?&amp;lt;messages&amp;gt;\[.*?\])\s*)?&lt;br /&gt;
attr MatrixBot get02TextArg 0&lt;br /&gt;
attr MatrixBot get02URL https://[$name:MatrixServer]/_matrix/client/v3/sync?timeout=60000&amp;amp;filter=[$name:filter_id]%%next_batch_param%%&lt;br /&gt;
attr MatrixBot icon message_info&lt;br /&gt;
attr MatrixBot parseFunction1 handleAuthErrors&lt;br /&gt;
attr MatrixBot reAuthAlways 0&lt;br /&gt;
attr MatrixBot reAuthRegex M_UNKNOWN_TOKEN&lt;br /&gt;
attr MatrixBot replacement01Mode expression&lt;br /&gt;
attr MatrixBot replacement01Regex \[([^:\s\[\&amp;quot;\&#039;]+):([^\]\s]+)\]&lt;br /&gt;
attr MatrixBot replacement01Value my $device = $name if ($1 eq &amp;quot;\$name&amp;quot;) // $1;;\&lt;br /&gt;
ReadingsVal($device, $2, undef) or AttrVal($device, $2, &amp;quot;???&amp;quot;);;&lt;br /&gt;
attr MatrixBot replacement02Mode expression&lt;br /&gt;
attr MatrixBot replacement02Regex %%uuid%%&lt;br /&gt;
attr MatrixBot replacement02Value join(&amp;quot;-&amp;quot;, unpack(&amp;quot;A8 A4 A4 A4 A12&amp;quot;, unpack(&amp;quot;H*&amp;quot;, join(&amp;quot;&amp;quot;, map { chr(int rand 256) } 0..15))))&lt;br /&gt;
attr MatrixBot replacement03Mode key&lt;br /&gt;
attr MatrixBot replacement03Regex %%MatrixPassword%%&lt;br /&gt;
attr MatrixBot replacement03Value MatrixPassword&lt;br /&gt;
attr MatrixBot replacement04Mode expression&lt;br /&gt;
attr MatrixBot replacement04Regex %%next_batch_param%%&lt;br /&gt;
attr MatrixBot replacement04Value #is there a reading &#039;next_batch&#039;?\&lt;br /&gt;
my $val = ReadingsVal($name, &#039;next_batch&#039;, &#039;???&#039;);;\&lt;br /&gt;
\&lt;br /&gt;
#return the GET parameter &#039;sync=value&#039; for /sync Endpoint\&lt;br /&gt;
return &amp;quot;&amp;amp;since=$val&amp;quot; if ($val ne &#039;???&#039;);;\&lt;br /&gt;
\&lt;br /&gt;
#return neither since-key nor value for it:\&lt;br /&gt;
return &amp;quot;&amp;quot;;;&lt;br /&gt;
attr MatrixBot set01Data {\&lt;br /&gt;
  &amp;quot;msgtype&amp;quot;: &amp;quot;m.text&amp;quot;,\&lt;br /&gt;
  &amp;quot;body&amp;quot;: &amp;quot;$val&amp;quot;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot set01HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot set01HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot set01IExpr #cancel an active longpoll\&lt;br /&gt;
if ($hash-&amp;gt;{BUSY}\&lt;br /&gt;
    &amp;amp;&amp;amp; $hash-&amp;gt;{HttpUtils}\&lt;br /&gt;
    &amp;amp;&amp;amp; $hash-&amp;gt;{HttpUtils}-&amp;gt;{url} =~ m|^https://[^/]+/_matrix/client/v3/sync\?timeout=\d+&amp;amp;filter=| ) {\&lt;br /&gt;
    Log(3, &amp;quot;$name: longpoll active, cutting it off now&amp;quot;);;\&lt;br /&gt;
    HttpUtils_Close($hash-&amp;gt;{HttpUtils});;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
#just return $val unteraltered\&lt;br /&gt;
$val&lt;br /&gt;
attr MatrixBot set01Method POST&lt;br /&gt;
attr MatrixBot set01Name sendText&lt;br /&gt;
attr MatrixBot set01ParseResponse 1&lt;br /&gt;
attr MatrixBot set01Regex {\&amp;quot;event_id\&amp;quot;:\&amp;quot;(.*)\&amp;quot;}&lt;br /&gt;
attr MatrixBot set01TextArg 1&lt;br /&gt;
attr MatrixBot set01URL https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%&lt;br /&gt;
attr MatrixBot set02Data {\&lt;br /&gt;
  &amp;quot;room&amp;quot;: {\&lt;br /&gt;
    &amp;quot;rooms&amp;quot;: [&amp;quot;[$name:MatrixRoomID]&amp;quot;],\&lt;br /&gt;
    &amp;quot;timeline&amp;quot;: {\&lt;br /&gt;
      &amp;quot;limit&amp;quot;: 10,\&lt;br /&gt;
      &amp;quot;types&amp;quot;: [&amp;quot;m.room.message&amp;quot;]\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;include_leave&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_join&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_account_data&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_state&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;state&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;ephemeral&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;account_data&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    }\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;event_fields&amp;quot;: [\&lt;br /&gt;
    &amp;quot;content.body&amp;quot;,\&lt;br /&gt;
    &amp;quot;sender&amp;quot;,\&lt;br /&gt;
    &amp;quot;origin_server_ts&amp;quot;\&lt;br /&gt;
  ],\&lt;br /&gt;
  &amp;quot;event_format&amp;quot;: &amp;quot;client&amp;quot;,\&lt;br /&gt;
  &amp;quot;presence&amp;quot;: {\&lt;br /&gt;
    &amp;quot;types&amp;quot;: [],\&lt;br /&gt;
    &amp;quot;not_types&amp;quot;: [&amp;quot;*&amp;quot;]\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;account_data&amp;quot;: {\&lt;br /&gt;
    &amp;quot;types&amp;quot;: [],\&lt;br /&gt;
    &amp;quot;not_types&amp;quot;: [&amp;quot;*&amp;quot;]\&lt;br /&gt;
  }\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot set02HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot set02HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot set02Method POST&lt;br /&gt;
attr MatrixBot set02Name sendFilter&lt;br /&gt;
attr MatrixBot set02NoArg 1&lt;br /&gt;
attr MatrixBot set02ParseResponse 1&lt;br /&gt;
attr MatrixBot set02Regex \&amp;quot;filter_id\&amp;quot;:\s*\&amp;quot;(?&amp;lt;filter_id&amp;gt;\d+)\&amp;quot;&lt;br /&gt;
attr MatrixBot set02URL https://[$name:MatrixServer]/_matrix/client/v3/user/@[$name:MatrixUser]:[$name:MatrixServer]/filter&lt;br /&gt;
attr MatrixBot set03Local 1&lt;br /&gt;
attr MatrixBot set03Name longpollCmd&lt;br /&gt;
attr MatrixBot set03TextArg 1&lt;br /&gt;
attr MatrixBot showError 1&lt;br /&gt;
attr MatrixBot sid01Data {\&lt;br /&gt;
  &amp;quot;type&amp;quot;: &amp;quot;m.login.password&amp;quot;,\&lt;br /&gt;
  &amp;quot;identifier&amp;quot;: {\&lt;br /&gt;
    &amp;quot;type&amp;quot;: &amp;quot;m.id.user&amp;quot;,\&lt;br /&gt;
    &amp;quot;user&amp;quot;: &amp;quot;[$name:MatrixUser]&amp;quot;\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;password&amp;quot;: &amp;quot;%%MatrixPassword%%&amp;quot;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot sid01HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot sid01IdRegex &amp;quot;access_token&amp;quot;\s*:\s*&amp;quot;([^&amp;quot;]+)&amp;quot;&lt;br /&gt;
attr MatrixBot sid01URL https://[$name:MatrixServer]/_matrix/client/v3/login&lt;br /&gt;
attr MatrixBot timeout 65&lt;br /&gt;
attr MatrixBot userReadings longpollTimer:(next_batch|longpollCmd|LAST_ERROR|sendText):.* {\&lt;br /&gt;
  my $longpollCmd = ReadingsVal($name, &#039;longpollCmd&#039;, &#039;???&#039;);;\&lt;br /&gt;
  my $delay = 1;;\&lt;br /&gt;
  my $timeout = AttrVal($name, &#039;timeout&#039;, 61) + $delay + 5;;\&lt;br /&gt;
  \&lt;br /&gt;
  # stop our timers:\&lt;br /&gt;
  if ($longpollCmd eq &amp;quot;stopTimer&amp;quot;) {\&lt;br /&gt;
    fhem(&amp;quot;cancel ${name}_longpollTimer quiet&amp;quot;);;\&lt;br /&gt;
    fhem(&amp;quot;cancel ${name}_longpollTimer2 quiet&amp;quot;);;\&lt;br /&gt;
    return &amp;quot;stopped&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  if ($longpollCmd ne &amp;quot;startTimer&amp;quot;) {\&lt;br /&gt;
    return &amp;quot;no timer set, longpollCmd is not set to &#039;startTimer&#039;&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #if startTimer cmd was given now, set filter as well:\&lt;br /&gt;
  if (ReadingsAge($name, &#039;longpollCmd&#039;, 0) &amp;lt;= 1) {\&lt;br /&gt;
    $delay = 5;; #delay to allow for sendFilter to be answered\&lt;br /&gt;
    #Log(1, &amp;quot;🪲 $name: &amp;gt;&amp;gt;&amp;quot;. InternalVal($name, &#039;httpbody&#039;, &#039;???&#039;) .&amp;quot;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    fhem(&amp;quot;sleep 0.1 quiet;; set $name sendFilter&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #we handle an error reported by http-utils:\&lt;br /&gt;
  if (ReadingsAge($name, &#039;LAST_ERROR&#039;, 0) &amp;lt;= 1) {\&lt;br /&gt;
    my $last_error = ReadingsVal($name, &#039;LAST_ERROR&#039;, &#039;???&#039;);;\&lt;br /&gt;
    $delay = 10;; #delay to allow for error reasons to improve\&lt;br /&gt;
    #Log(1, &amp;quot;🪲 $name: Dealing with error: &amp;gt;&amp;gt;$last_error&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  # for testing this: { $defs{MatrixBot}{sid} = &#039;bla&#039; }\&lt;br /&gt;
  if (!defined &amp;amp;HTTPMOD::handleAuthErrors) {\&lt;br /&gt;
    *HTTPMOD::handleAuthErrors = sub {\&lt;br /&gt;
      my ($hash, $header, $body, $request) = @_;;\&lt;br /&gt;
      my $name = $hash-&amp;gt;{NAME};;\&lt;br /&gt;
      my $status;;\&lt;br /&gt;
      \&lt;br /&gt;
      if ($header =~ m{^HTTP/\d\.\d\s+(\d+)}m) {\&lt;br /&gt;
        $status = $1;;\&lt;br /&gt;
      }\&lt;br /&gt;
      \&lt;br /&gt;
      Log3($name, 4, &amp;quot;$name: HTTP status code is $status&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
      if ( $status == 401 || $status == 403 || $status == 500 ) {\&lt;br /&gt;
        Log3($name, 3, &amp;quot;$name: auth-error or servererror ($status), calling doAuth()&amp;quot;);;\&lt;br /&gt;
        HTTPMOD::DoAuth($hash);;\&lt;br /&gt;
      }\&lt;br /&gt;
    };;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #set timers, one regular and one fallback:\&lt;br /&gt;
  fhem(&amp;quot;sleep $delay ${name}_longpollTimer quiet;; get $name longpoll&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;sleep $timeout ${name}_longpollTimer2 quiet;; set $name longpollCmd startTimer&amp;quot;);;\&lt;br /&gt;
  \&lt;br /&gt;
  return strftime(&amp;quot;next longpoll at %H:%M:%S&amp;quot;, localtime( time()+$delay ));;\&lt;br /&gt;
},\&lt;br /&gt;
messages_list:messages:.* {\&lt;br /&gt;
  my $this = ReadingsVal($name, $reading, &#039;&#039;);;\&lt;br /&gt;
  my @timestampArray = split(&amp;quot;\n&amp;quot;, $this);;\&lt;br /&gt;
  my $messages_ref = decode_json(ReadingsVal($name, &#039;messages&#039;, &#039;&#039;));;\&lt;br /&gt;
  my $length = 20;;\&lt;br /&gt;
  \&lt;br /&gt;
  my %seen_messages = map { $_ =&amp;gt; 1 } @timestampArray;;\&lt;br /&gt;
  \&lt;br /&gt;
  foreach my $msg (@$messages_ref) {\&lt;br /&gt;
    my $val = $msg-&amp;gt;{content}{body};;\&lt;br /&gt;
    $val = Encode::decode(&#039;utf-8&#039;, $val) unless Encode::is_utf8($val);;\&lt;br /&gt;
    $val =~ s/\n/ /g;; #replace newlines with spaces\&lt;br /&gt;
    \&lt;br /&gt;
    my $sender = $msg-&amp;gt;{sender};;\&lt;br /&gt;
    my $ts = strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;, localtime($msg-&amp;gt;{origin_server_ts} / 1000));;\&lt;br /&gt;
    my $new_entry = &amp;quot;$ts: $sender: $val&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    next if $seen_messages{$new_entry};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $inserted = 0;;\&lt;br /&gt;
    for (my $i = 0;; $i &amp;lt; @timestampArray;; $i++) {\&lt;br /&gt;
      my ($existing_ts) = $timestampArray[$i] =~ /^([^:]+):/;;\&lt;br /&gt;
      if ($ts lt $existing_ts) {\&lt;br /&gt;
        splice(@timestampArray, $i, 0, $new_entry);;\&lt;br /&gt;
        $inserted = 1;;\&lt;br /&gt;
        last;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
    push(@timestampArray, encode(&#039;utf-8&#039;, $new_entry)) unless $inserted;;\&lt;br /&gt;
    shift(@timestampArray) while @timestampArray &amp;gt; $length;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  return join(&amp;quot;\n&amp;quot;, @timestampArray);;\&lt;br /&gt;
},\&lt;br /&gt;
process_messages:messages:.* {\&lt;br /&gt;
  my $val = ReadingsVal($name, &#039;messages_list&#039;, &#039;&#039;);;\&lt;br /&gt;
  my $this = ReadingsVal($name, $reading, &#039;&#039;);;\&lt;br /&gt;
  my $latest_ts = $this;;\&lt;br /&gt;
  \&lt;br /&gt;
  foreach my $line (split(/\n/, $val)) {\&lt;br /&gt;
    if ($line =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}):\s*(.*)$/) {\&lt;br /&gt;
      my ($ts, $msg) = ($1, $2);;\&lt;br /&gt;
      \&lt;br /&gt;
      if ($ts gt $this) {\&lt;br /&gt;
        fhem(&amp;quot;trigger $name msg: $line&amp;quot;);;\&lt;br /&gt;
        $latest_ts = $ts if ($ts gt $latest_ts);;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  return $latest_ts;;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot verbose 3&lt;br /&gt;
attr MatrixBot widgetOverride longpollCmd:uzsuSelectRadio,startTimer,stopTimer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Matrix&amp;diff=40176</id>
		<title>Matrix</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Matrix&amp;diff=40176"/>
		<updated>2025-05-24T09:32:15Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Cooltux Device auch mit aufgeführt, Screenshot ergänzt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Allgemeines ==&lt;br /&gt;
&#039;&#039;&#039;Matrix&#039;&#039;&#039; ist ein offenes Kommunikationsprotokoll für dezentrale Echtzeitkommunikation. Es wird von zahlreichen Clients unterstützt, darunter &#039;&#039;&#039;Element&#039;&#039;&#039;, &#039;&#039;&#039;Element-X&#039;&#039;&#039;, &#039;&#039;&#039;SchildiChat Next&#039;&#039;&#039;, &#039;&#039;&#039;FluffyChat&#039;&#039;&#039; und viele weitere.&lt;br /&gt;
&lt;br /&gt;
Viele Open-Source-Projekte betreiben eigene Chaträume auf föderierten Matrix-Servern. Ein Beispiel dafür ist der &#039;&#039;&#039;Gadgetbridge-Chat&#039;&#039;&#039;, einer der größeren und aktiveren Räume im Matrix-Ökosystem.&lt;br /&gt;
&lt;br /&gt;
== Matrix und FHEM ==&lt;br /&gt;
Um mit &#039;&#039;&#039;FHEM&#039;&#039;&#039; Nachrichten über das Matrix-Protokoll zu senden und zu empfangen, stehen mehrere Möglichkeiten zur Verfügung. Eine einfache und pragmatische Lösung ist die Nutzung eines &#039;&#039;&#039;HTTPMOD-Devices&#039;&#039;&#039;. Damit lassen sich Nachrichten über die Matrix-API versenden oder empfangen.&lt;br /&gt;
&lt;br /&gt;
=== Matrix per Device ===&lt;br /&gt;
Es gibt ein Device für Matrix unter https://git.cooltux.net/FHEM/mod-matrix&lt;br /&gt;
&lt;br /&gt;
=== Matrix per HTTPMOD Device ===&lt;br /&gt;
[[Datei:Screenshot des Matrix HTTPMOD Device.png|alternativtext=Screenshot des Matrix HTTPMOD Device|ohne|mini|Screenshot des Matrix HTTPMOD Device]]&lt;br /&gt;
Folgendes Device kann Matrix Nachrichten senden und empfangen, es hat wenige Abhängigkeiten und sollte in den meisten FHEM Installationen unkompliziert eingerichtet sein. Es wird die Datenübertragung von FHEM zum Server mit HTTPS verschlüsselt, E2EE wird nicht verwendet.&lt;br /&gt;
&lt;br /&gt;
==== Einrichtung ====&lt;br /&gt;
Es sind die Attribute MatrixServer und MatrixUser zu setzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixServer Dein-Servername&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixUser FHEM-Matrix-Username&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Passwort des Matrix-FHEM-Users setzt man mit dem Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot storeKeyValue MatrixPassword yourPassword123&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann richtet man einen, &#039;&#039;&#039;nicht E2E verschlüsselten Raum&#039;&#039;&#039; ein in dem der Matrix-FHEM-Account eingeladen ist. Von dem Raum benötigt man die MatrixRoomID, die man mit Element in den Raumdetails finden kann (Room Settings --&amp;gt; Advanced --&amp;gt; Internal room ID).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixRoomID !12345678901234:example.com&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Verwendung ====&lt;br /&gt;
Nachrichten senden geht mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot sendText Bla Bla Bla&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Longpoll von Nachrichten starten und stoppen mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot longpollCmd startTimer&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot longpollCmd stopTimer&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Weiteres ====&lt;br /&gt;
&lt;br /&gt;
===== Forumlink =====&lt;br /&gt;
https://forum.fhem.de/index.php?topic=120834.msg1339487#msg1339487&lt;br /&gt;
&lt;br /&gt;
===== Demo-Device =====&lt;br /&gt;
Das folgende DOIF-Device lauscht auf eine eingehende Nachricht die als Inhalt &amp;quot;ping&amp;quot; oder &amp;quot;Ping&amp;quot; ist und antwortet mit &amp;quot;Pong!&amp;quot;. Es dient als Beispiel und kann natürlich um weitere Befehle ergänzt/ersetzt werden um FHEM damit zu steuern:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod MatrixPing.doif DOIF ([&amp;quot;MatrixBot:msg: .*: @.*: [Pp]ing$&amp;quot;])\&lt;br /&gt;
  (set MatrixBot sendText Pong!)&lt;br /&gt;
attr MatrixPing.doif alias Matrix Ping/Pong&lt;br /&gt;
attr MatrixPing.doif do always&lt;br /&gt;
attr MatrixPing.doif group Meldungen&lt;br /&gt;
attr MatrixPing.doif icon homeConnect&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Device-Definition ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod MatrixBot HTTPMOD none 0&lt;br /&gt;
attr MatrixBot userattr MatrixRoomID MatrixServer MatrixUser&lt;br /&gt;
attr MatrixBot MatrixRoomID !12345678901234:nope.chat&lt;br /&gt;
attr MatrixBot MatrixServer nope.chat&lt;br /&gt;
attr MatrixBot MatrixUser DeinUsername&lt;br /&gt;
attr MatrixBot bodyDecode utf-8&lt;br /&gt;
attr MatrixBot bodyEncode utf-8&lt;br /&gt;
attr MatrixBot comment &amp;quot;\&lt;br /&gt;
Create a room for FHEM.\&lt;br /&gt;
\&lt;br /&gt;
The room must not use encryption, a room that has encryption\&lt;br /&gt;
enabled, cannot be converted to a non-encrypted room anymore \&lt;br /&gt;
\&lt;br /&gt;
The room-id can be found in Element-Web at:\&lt;br /&gt;
Room Settings --&amp;gt; Advanced --&amp;gt; Internal room ID\&lt;br /&gt;
\&lt;br /&gt;
To store the password in FHEM in obfuscated way:\&lt;br /&gt;
set MatrixBot storeKeyValue MatrixPassword yourPassword123\&lt;br /&gt;
\&lt;br /&gt;
###\&lt;br /&gt;
To send a text:\&lt;br /&gt;
set MatrixBot sendText Bla Bla Bla\&lt;br /&gt;
\&lt;br /&gt;
\&lt;br /&gt;
###\&lt;br /&gt;
To longpoll for messages once:\&lt;br /&gt;
# 1. send special filter to Matrix:\&lt;br /&gt;
set MatrixBot sendFilter\&lt;br /&gt;
\&lt;br /&gt;
# 2. start one longPoll (waits up to 60 seconds\&lt;br /&gt;
#                        or until data is available)\&lt;br /&gt;
get MatrixBot longpoll\&lt;br /&gt;
\&lt;br /&gt;
#alternatively, to keep on longPolling (this also sets filter):\&lt;br /&gt;
set MatrixBot longpollCmd startTimer\&lt;br /&gt;
#to stop the timers:\&lt;br /&gt;
set MatrixBot longpollCmd stopTimer\&lt;br /&gt;
\&lt;br /&gt;
#############################################################\&lt;br /&gt;
https://spec.matrix.org/v1.14/client-server-api/#syncing\&lt;br /&gt;
&amp;quot;&lt;br /&gt;
attr MatrixBot get02AlwaysNum 0&lt;br /&gt;
attr MatrixBot get02HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot get02Name longpoll&lt;br /&gt;
attr MatrixBot get02Regex \&amp;quot;next_batch\&amp;quot;:\s*\&amp;quot;(?&amp;lt;next_batch&amp;gt;[^\&amp;quot;]+)\&amp;quot;(?:.*?\&amp;quot;timeline\&amp;quot;:\s*{\s*\&amp;quot;events\&amp;quot;:\s*(?&amp;lt;messages&amp;gt;\[.*?\])\s*)?&lt;br /&gt;
attr MatrixBot get02TextArg 0&lt;br /&gt;
attr MatrixBot get02URL https://[$name:MatrixServer]/_matrix/client/v3/sync?timeout=60000&amp;amp;filter=[$name:filter_id]%%next_batch_param%%&lt;br /&gt;
attr MatrixBot icon message_info&lt;br /&gt;
attr MatrixBot parseFunction1 handleAuthErrors&lt;br /&gt;
attr MatrixBot reAuthAlways 0&lt;br /&gt;
attr MatrixBot reAuthRegex M_UNKNOWN_TOKEN&lt;br /&gt;
attr MatrixBot replacement01Mode expression&lt;br /&gt;
attr MatrixBot replacement01Regex \[([^:\s\[\&amp;quot;\&#039;]+):([^\]\s]+)\]&lt;br /&gt;
attr MatrixBot replacement01Value my $device = $name if ($1 eq &amp;quot;\$name&amp;quot;) // $1;;\&lt;br /&gt;
ReadingsVal($device, $2, undef) or AttrVal($device, $2, &amp;quot;???&amp;quot;);;&lt;br /&gt;
attr MatrixBot replacement02Mode expression&lt;br /&gt;
attr MatrixBot replacement02Regex %%uuid%%&lt;br /&gt;
attr MatrixBot replacement02Value join(&amp;quot;-&amp;quot;, unpack(&amp;quot;A8 A4 A4 A4 A12&amp;quot;, unpack(&amp;quot;H*&amp;quot;, join(&amp;quot;&amp;quot;, map { chr(int rand 256) } 0..15))))&lt;br /&gt;
attr MatrixBot replacement03Mode key&lt;br /&gt;
attr MatrixBot replacement03Regex %%MatrixPassword%%&lt;br /&gt;
attr MatrixBot replacement03Value MatrixPassword&lt;br /&gt;
attr MatrixBot replacement04Mode expression&lt;br /&gt;
attr MatrixBot replacement04Regex %%next_batch_param%%&lt;br /&gt;
attr MatrixBot replacement04Value #is there a reading &#039;next_batch&#039;?\&lt;br /&gt;
my $val = ReadingsVal($name, &#039;next_batch&#039;, &#039;???&#039;);;\&lt;br /&gt;
\&lt;br /&gt;
#return the GET parameter &#039;sync=value&#039; for /sync Endpoint\&lt;br /&gt;
return &amp;quot;&amp;amp;since=$val&amp;quot; if ($val ne &#039;???&#039;);;\&lt;br /&gt;
\&lt;br /&gt;
#return neither since-key nor value for it:\&lt;br /&gt;
return &amp;quot;&amp;quot;;;&lt;br /&gt;
attr MatrixBot set01Data {\&lt;br /&gt;
  &amp;quot;msgtype&amp;quot;: &amp;quot;m.text&amp;quot;,\&lt;br /&gt;
  &amp;quot;body&amp;quot;: &amp;quot;$val&amp;quot;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot set01HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot set01HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot set01IExpr #cancel an active longpoll\&lt;br /&gt;
if ($hash-&amp;gt;{BUSY}\&lt;br /&gt;
    &amp;amp;&amp;amp; $hash-&amp;gt;{HttpUtils}\&lt;br /&gt;
    &amp;amp;&amp;amp; $hash-&amp;gt;{HttpUtils}-&amp;gt;{url} =~ m|^https://[^/]+/_matrix/client/v3/sync\?timeout=\d+&amp;amp;filter=| ) {\&lt;br /&gt;
    Log(3, &amp;quot;$name: longpoll active, cutting it off now&amp;quot;);;\&lt;br /&gt;
    HttpUtils_Close($hash-&amp;gt;{HttpUtils});;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
#just return $val unteraltered\&lt;br /&gt;
$val&lt;br /&gt;
attr MatrixBot set01Method POST&lt;br /&gt;
attr MatrixBot set01Name sendText&lt;br /&gt;
attr MatrixBot set01ParseResponse 1&lt;br /&gt;
attr MatrixBot set01Regex {\&amp;quot;event_id\&amp;quot;:\&amp;quot;(.*)\&amp;quot;}&lt;br /&gt;
attr MatrixBot set01TextArg 1&lt;br /&gt;
attr MatrixBot set01URL https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%&lt;br /&gt;
attr MatrixBot set02Data {\&lt;br /&gt;
  &amp;quot;room&amp;quot;: {\&lt;br /&gt;
    &amp;quot;rooms&amp;quot;: [&amp;quot;[$name:MatrixRoomID]&amp;quot;],\&lt;br /&gt;
    &amp;quot;timeline&amp;quot;: {\&lt;br /&gt;
      &amp;quot;limit&amp;quot;: 10,\&lt;br /&gt;
      &amp;quot;types&amp;quot;: [&amp;quot;m.room.message&amp;quot;]\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;include_leave&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_join&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_account_data&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_state&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;state&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;ephemeral&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;account_data&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    }\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;event_fields&amp;quot;: [\&lt;br /&gt;
    &amp;quot;content.body&amp;quot;,\&lt;br /&gt;
    &amp;quot;sender&amp;quot;,\&lt;br /&gt;
    &amp;quot;origin_server_ts&amp;quot;\&lt;br /&gt;
  ],\&lt;br /&gt;
  &amp;quot;event_format&amp;quot;: &amp;quot;client&amp;quot;,\&lt;br /&gt;
  &amp;quot;presence&amp;quot;: {\&lt;br /&gt;
    &amp;quot;types&amp;quot;: [],\&lt;br /&gt;
    &amp;quot;not_types&amp;quot;: [&amp;quot;*&amp;quot;]\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;account_data&amp;quot;: {\&lt;br /&gt;
    &amp;quot;types&amp;quot;: [],\&lt;br /&gt;
    &amp;quot;not_types&amp;quot;: [&amp;quot;*&amp;quot;]\&lt;br /&gt;
  }\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot set02HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot set02HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot set02Method POST&lt;br /&gt;
attr MatrixBot set02Name sendFilter&lt;br /&gt;
attr MatrixBot set02NoArg 1&lt;br /&gt;
attr MatrixBot set02ParseResponse 1&lt;br /&gt;
attr MatrixBot set02Regex \&amp;quot;filter_id\&amp;quot;:\s*\&amp;quot;(?&amp;lt;filter_id&amp;gt;\d+)\&amp;quot;&lt;br /&gt;
attr MatrixBot set02URL https://[$name:MatrixServer]/_matrix/client/v3/user/@[$name:MatrixUser]:[$name:MatrixServer]/filter&lt;br /&gt;
attr MatrixBot set03Local 1&lt;br /&gt;
attr MatrixBot set03Name longpollCmd&lt;br /&gt;
attr MatrixBot set03TextArg 1&lt;br /&gt;
attr MatrixBot showError 1&lt;br /&gt;
attr MatrixBot sid01Data {\&lt;br /&gt;
  &amp;quot;type&amp;quot;: &amp;quot;m.login.password&amp;quot;,\&lt;br /&gt;
  &amp;quot;identifier&amp;quot;: {\&lt;br /&gt;
    &amp;quot;type&amp;quot;: &amp;quot;m.id.user&amp;quot;,\&lt;br /&gt;
    &amp;quot;user&amp;quot;: &amp;quot;[$name:MatrixUser]&amp;quot;\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;password&amp;quot;: &amp;quot;%%MatrixPassword%%&amp;quot;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot sid01HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot sid01IdRegex &amp;quot;access_token&amp;quot;\s*:\s*&amp;quot;([^&amp;quot;]+)&amp;quot;&lt;br /&gt;
attr MatrixBot sid01URL https://[$name:MatrixServer]/_matrix/client/v3/login&lt;br /&gt;
attr MatrixBot timeout 65&lt;br /&gt;
attr MatrixBot userReadings longpollTimer:(next_batch|longpollCmd|LAST_ERROR|sendText):.* {\&lt;br /&gt;
  my $longpollCmd = ReadingsVal($name, &#039;longpollCmd&#039;, &#039;???&#039;);;\&lt;br /&gt;
  my $delay = 1;;\&lt;br /&gt;
  my $timeout = AttrVal($name, &#039;timeout&#039;, 61) + $delay + 5;;\&lt;br /&gt;
  \&lt;br /&gt;
  # stop our timers:\&lt;br /&gt;
  if ($longpollCmd eq &amp;quot;stopTimer&amp;quot;) {\&lt;br /&gt;
    fhem(&amp;quot;cancel ${name}_longpollTimer quiet&amp;quot;);;\&lt;br /&gt;
    fhem(&amp;quot;cancel ${name}_longpollTimer2 quiet&amp;quot;);;\&lt;br /&gt;
    return &amp;quot;stopped&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  if ($longpollCmd ne &amp;quot;startTimer&amp;quot;) {\&lt;br /&gt;
    return &amp;quot;no timer set, longpollCmd is not set to &#039;startTimer&#039;&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #if startTimer cmd was given now, set filter as well:\&lt;br /&gt;
  if (ReadingsAge($name, &#039;longpollCmd&#039;, 0) &amp;lt;= 1) {\&lt;br /&gt;
    $delay = 5;; #delay to allow for sendFilter to be answered\&lt;br /&gt;
    #Log(1, &amp;quot;🪲 $name: &amp;gt;&amp;gt;&amp;quot;. InternalVal($name, &#039;httpbody&#039;, &#039;???&#039;) .&amp;quot;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    fhem(&amp;quot;sleep 0.1 quiet;; set $name sendFilter&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #we handle an error reported by http-utils:\&lt;br /&gt;
  if (ReadingsAge($name, &#039;LAST_ERROR&#039;, 0) &amp;lt;= 1) {\&lt;br /&gt;
    my $last_error = ReadingsVal($name, &#039;LAST_ERROR&#039;, &#039;???&#039;);;\&lt;br /&gt;
    $delay = 10;; #delay to allow for error reasons to improve\&lt;br /&gt;
    #Log(1, &amp;quot;🪲 $name: Dealing with error: &amp;gt;&amp;gt;$last_error&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  # for testing this: { $defs{MatrixBot}{sid} = &#039;bla&#039; }\&lt;br /&gt;
  if (!defined &amp;amp;HTTPMOD::handleAuthErrors) {\&lt;br /&gt;
    *HTTPMOD::handleAuthErrors = sub {\&lt;br /&gt;
      my ($hash, $header, $body, $request) = @_;;\&lt;br /&gt;
      my $name = $hash-&amp;gt;{NAME};;\&lt;br /&gt;
      my $status;;\&lt;br /&gt;
      \&lt;br /&gt;
      if ($header =~ m{^HTTP/\d\.\d\s+(\d+)}m) {\&lt;br /&gt;
        $status = $1;;\&lt;br /&gt;
      }\&lt;br /&gt;
      \&lt;br /&gt;
      Log3($name, 4, &amp;quot;$name: HTTP status code is $status&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
      if ( $status == 401 || $status == 403 || $status == 500 ) {\&lt;br /&gt;
        Log3($name, 3, &amp;quot;$name: auth-error or servererror ($status), calling doAuth()&amp;quot;);;\&lt;br /&gt;
        HTTPMOD::DoAuth($hash);;\&lt;br /&gt;
      }\&lt;br /&gt;
    };;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #set timers, one regular and one fallback:\&lt;br /&gt;
  fhem(&amp;quot;sleep $delay ${name}_longpollTimer quiet;; get $name longpoll&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;sleep $timeout ${name}_longpollTimer2 quiet;; set $name longpollCmd startTimer&amp;quot;);;\&lt;br /&gt;
  \&lt;br /&gt;
  return strftime(&amp;quot;next longpoll at %H:%M:%S&amp;quot;, localtime( time()+$delay ));;\&lt;br /&gt;
},\&lt;br /&gt;
messages_list:messages:.* {\&lt;br /&gt;
  my $this = ReadingsVal($name, $reading, &#039;&#039;);;\&lt;br /&gt;
  my @timestampArray = split(&amp;quot;\n&amp;quot;, $this);;\&lt;br /&gt;
  my $messages_ref = decode_json(ReadingsVal($name, &#039;messages&#039;, &#039;&#039;));;\&lt;br /&gt;
  my $length = 20;;\&lt;br /&gt;
  \&lt;br /&gt;
  my %seen_messages = map { $_ =&amp;gt; 1 } @timestampArray;;\&lt;br /&gt;
  \&lt;br /&gt;
  foreach my $msg (@$messages_ref) {\&lt;br /&gt;
    my $val = $msg-&amp;gt;{content}{body};;\&lt;br /&gt;
    $val = Encode::decode(&#039;utf-8&#039;, $val) unless Encode::is_utf8($val);;\&lt;br /&gt;
    $val =~ s/\n/ /g;; #replace newlines with spaces\&lt;br /&gt;
    \&lt;br /&gt;
    my $sender = $msg-&amp;gt;{sender};;\&lt;br /&gt;
    my $ts = strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;, localtime($msg-&amp;gt;{origin_server_ts} / 1000));;\&lt;br /&gt;
    my $new_entry = &amp;quot;$ts: $sender: $val&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    next if $seen_messages{$new_entry};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $inserted = 0;;\&lt;br /&gt;
    for (my $i = 0;; $i &amp;lt; @timestampArray;; $i++) {\&lt;br /&gt;
      my ($existing_ts) = $timestampArray[$i] =~ /^([^:]+):/;;\&lt;br /&gt;
      if ($ts lt $existing_ts) {\&lt;br /&gt;
        splice(@timestampArray, $i, 0, $new_entry);;\&lt;br /&gt;
        $inserted = 1;;\&lt;br /&gt;
        last;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
    push(@timestampArray, encode(&#039;utf-8&#039;, $new_entry)) unless $inserted;;\&lt;br /&gt;
    shift(@timestampArray) while @timestampArray &amp;gt; $length;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  return join(&amp;quot;\n&amp;quot;, @timestampArray);;\&lt;br /&gt;
},\&lt;br /&gt;
process_messages:messages:.* {\&lt;br /&gt;
  my $val = ReadingsVal($name, &#039;messages_list&#039;, &#039;&#039;);;\&lt;br /&gt;
  my $this = ReadingsVal($name, $reading, &#039;&#039;);;\&lt;br /&gt;
  my $latest_ts = $this;;\&lt;br /&gt;
  \&lt;br /&gt;
  foreach my $line (split(/\n/, $val)) {\&lt;br /&gt;
    if ($line =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}):\s*(.*)$/) {\&lt;br /&gt;
      my ($ts, $msg) = ($1, $2);;\&lt;br /&gt;
      \&lt;br /&gt;
      if ($ts gt $this) {\&lt;br /&gt;
        fhem(&amp;quot;trigger $name msg: $line&amp;quot;);;\&lt;br /&gt;
        $latest_ts = $ts if ($ts gt $latest_ts);;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  return $latest_ts;;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot verbose 3&lt;br /&gt;
attr MatrixBot widgetOverride longpollCmd:uzsuSelectRadio,startTimer,stopTimer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Datei:Screenshot_des_Matrix_HTTPMOD_Device.png&amp;diff=40175</id>
		<title>Datei:Screenshot des Matrix HTTPMOD Device.png</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Datei:Screenshot_des_Matrix_HTTPMOD_Device.png&amp;diff=40175"/>
		<updated>2025-05-24T09:31:22Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Screenshot des Matrix HTTPMOD Device&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Matrix&amp;diff=40174</id>
		<title>Matrix</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Matrix&amp;diff=40174"/>
		<updated>2025-05-24T08:46:02Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* Einrichtung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Allgemeines ==&lt;br /&gt;
&#039;&#039;&#039;Matrix&#039;&#039;&#039; ist ein offenes Kommunikationsprotokoll für dezentrale Echtzeitkommunikation. Es wird von zahlreichen Clients unterstützt, darunter &#039;&#039;&#039;Element&#039;&#039;&#039;, &#039;&#039;&#039;Element-X&#039;&#039;&#039;, &#039;&#039;&#039;SchildiChat Next&#039;&#039;&#039;, &#039;&#039;&#039;FluffyChat&#039;&#039;&#039; und viele weitere.&lt;br /&gt;
&lt;br /&gt;
Viele Open-Source-Projekte betreiben eigene Chaträume auf föderierten Matrix-Servern. Ein Beispiel dafür ist der &#039;&#039;&#039;Gadgetbridge-Chat&#039;&#039;&#039;, einer der größeren und aktiveren Räume im Matrix-Ökosystem.&lt;br /&gt;
&lt;br /&gt;
== Matrix und FHEM ==&lt;br /&gt;
Um mit &#039;&#039;&#039;FHEM&#039;&#039;&#039; Nachrichten über das Matrix-Protokoll zu senden und zu empfangen, stehen mehrere Möglichkeiten zur Verfügung. Eine einfache und pragmatische Lösung ist die Nutzung eines &#039;&#039;&#039;HTTPMOD-Devices&#039;&#039;&#039;. Damit lassen sich Nachrichten über die Matrix-API versenden oder empfangen.&lt;br /&gt;
&lt;br /&gt;
=== Matrix per HTTPMOD Device ===&lt;br /&gt;
Folgendes Device kann Matrix Nachrichten senden und empfangen, es hat wenige Abhängigkeiten und sollte in den meisten FHEM Installationen unkompliziert eingerichtet sein. Es wird die Datenübertragung von FHEM zum Server mit HTTPS verschlüsselt, E2EE wird nicht verwendet.&lt;br /&gt;
&lt;br /&gt;
==== Einrichtung ====&lt;br /&gt;
Es sind die Attribute MatrixServer und MatrixUser zu setzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixServer Dein-Servername&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixUser FHEM-Matrix-Username&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Passwort des Matrix-FHEM-Users setzt man mit dem Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot storeKeyValue MatrixPassword yourPassword123&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann richtet man einen, &#039;&#039;&#039;nicht E2E verschlüsselten Raum&#039;&#039;&#039; ein in dem der Matrix-FHEM-Account eingeladen ist. Von dem Raum benötigt man die MatrixRoomID, die man mit Element in den Raumdetails finden kann (Room Settings --&amp;gt; Advanced --&amp;gt; Internal room ID).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixRoomID !12345678901234:example.com&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Verwendung ====&lt;br /&gt;
Nachrichten senden geht mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot sendText Bla Bla Bla&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Longpoll von Nachrichten starten und stoppen mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot longpollCmd startTimer&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot longpollCmd stopTimer&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Weiteres ====&lt;br /&gt;
&lt;br /&gt;
===== Forumlink =====&lt;br /&gt;
https://forum.fhem.de/index.php?topic=120834.msg1339487#msg1339487&lt;br /&gt;
&lt;br /&gt;
===== Demo-Device =====&lt;br /&gt;
Das folgende DOIF-Device lauscht auf eine eingehende Nachricht die als Inhalt &amp;quot;ping&amp;quot; oder &amp;quot;Ping&amp;quot; ist und antwortet mit &amp;quot;Pong!&amp;quot;. Es dient als Beispiel und kann natürlich um weitere Befehle ergänzt/ersetzt werden um FHEM damit zu steuern:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod MatrixPing.doif DOIF ([&amp;quot;MatrixBot:msg: .*: @.*: [Pp]ing$&amp;quot;])\&lt;br /&gt;
  (set MatrixBot sendText Pong!)&lt;br /&gt;
attr MatrixPing.doif alias Matrix Ping/Pong&lt;br /&gt;
attr MatrixPing.doif do always&lt;br /&gt;
attr MatrixPing.doif group Meldungen&lt;br /&gt;
attr MatrixPing.doif icon homeConnect&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Device-Definition ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod MatrixBot HTTPMOD none 0&lt;br /&gt;
attr MatrixBot userattr MatrixRoomID MatrixServer MatrixUser&lt;br /&gt;
attr MatrixBot MatrixRoomID !12345678901234:nope.chat&lt;br /&gt;
attr MatrixBot MatrixServer nope.chat&lt;br /&gt;
attr MatrixBot MatrixUser DeinUsername&lt;br /&gt;
attr MatrixBot bodyDecode utf-8&lt;br /&gt;
attr MatrixBot bodyEncode utf-8&lt;br /&gt;
attr MatrixBot comment &amp;quot;\&lt;br /&gt;
Create a room for FHEM.\&lt;br /&gt;
\&lt;br /&gt;
The room must not use encryption, a room that has encryption\&lt;br /&gt;
enabled, cannot be converted to a non-encrypted room anymore \&lt;br /&gt;
\&lt;br /&gt;
The room-id can be found in Element-Web at:\&lt;br /&gt;
Room Settings --&amp;gt; Advanced --&amp;gt; Internal room ID\&lt;br /&gt;
\&lt;br /&gt;
To store the password in FHEM in obfuscated way:\&lt;br /&gt;
set MatrixBot storeKeyValue MatrixPassword yourPassword123\&lt;br /&gt;
\&lt;br /&gt;
###\&lt;br /&gt;
To send a text:\&lt;br /&gt;
set MatrixBot sendText Bla Bla Bla\&lt;br /&gt;
\&lt;br /&gt;
\&lt;br /&gt;
###\&lt;br /&gt;
To longpoll for messages once:\&lt;br /&gt;
# 1. send special filter to Matrix:\&lt;br /&gt;
set MatrixBot sendFilter\&lt;br /&gt;
\&lt;br /&gt;
# 2. start one longPoll (waits up to 60 seconds\&lt;br /&gt;
#                        or until data is available)\&lt;br /&gt;
get MatrixBot longpoll\&lt;br /&gt;
\&lt;br /&gt;
#alternatively, to keep on longPolling (this also sets filter):\&lt;br /&gt;
set MatrixBot longpollCmd startTimer\&lt;br /&gt;
#to stop the timers:\&lt;br /&gt;
set MatrixBot longpollCmd stopTimer\&lt;br /&gt;
\&lt;br /&gt;
#############################################################\&lt;br /&gt;
https://spec.matrix.org/v1.14/client-server-api/#syncing\&lt;br /&gt;
&amp;quot;&lt;br /&gt;
attr MatrixBot get02AlwaysNum 0&lt;br /&gt;
attr MatrixBot get02HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot get02Name longpoll&lt;br /&gt;
attr MatrixBot get02Regex \&amp;quot;next_batch\&amp;quot;:\s*\&amp;quot;(?&amp;lt;next_batch&amp;gt;[^\&amp;quot;]+)\&amp;quot;(?:.*?\&amp;quot;timeline\&amp;quot;:\s*{\s*\&amp;quot;events\&amp;quot;:\s*(?&amp;lt;messages&amp;gt;\[.*?\])\s*)?&lt;br /&gt;
attr MatrixBot get02TextArg 0&lt;br /&gt;
attr MatrixBot get02URL https://[$name:MatrixServer]/_matrix/client/v3/sync?timeout=60000&amp;amp;filter=[$name:filter_id]%%next_batch_param%%&lt;br /&gt;
attr MatrixBot icon message_info&lt;br /&gt;
attr MatrixBot parseFunction1 handleAuthErrors&lt;br /&gt;
attr MatrixBot reAuthAlways 0&lt;br /&gt;
attr MatrixBot reAuthRegex M_UNKNOWN_TOKEN&lt;br /&gt;
attr MatrixBot replacement01Mode expression&lt;br /&gt;
attr MatrixBot replacement01Regex \[([^:\s\[\&amp;quot;\&#039;]+):([^\]\s]+)\]&lt;br /&gt;
attr MatrixBot replacement01Value my $device = $name if ($1 eq &amp;quot;\$name&amp;quot;) // $1;;\&lt;br /&gt;
ReadingsVal($device, $2, undef) or AttrVal($device, $2, &amp;quot;???&amp;quot;);;&lt;br /&gt;
attr MatrixBot replacement02Mode expression&lt;br /&gt;
attr MatrixBot replacement02Regex %%uuid%%&lt;br /&gt;
attr MatrixBot replacement02Value join(&amp;quot;-&amp;quot;, unpack(&amp;quot;A8 A4 A4 A4 A12&amp;quot;, unpack(&amp;quot;H*&amp;quot;, join(&amp;quot;&amp;quot;, map { chr(int rand 256) } 0..15))))&lt;br /&gt;
attr MatrixBot replacement03Mode key&lt;br /&gt;
attr MatrixBot replacement03Regex %%MatrixPassword%%&lt;br /&gt;
attr MatrixBot replacement03Value MatrixPassword&lt;br /&gt;
attr MatrixBot replacement04Mode expression&lt;br /&gt;
attr MatrixBot replacement04Regex %%next_batch_param%%&lt;br /&gt;
attr MatrixBot replacement04Value #is there a reading &#039;next_batch&#039;?\&lt;br /&gt;
my $val = ReadingsVal($name, &#039;next_batch&#039;, &#039;???&#039;);;\&lt;br /&gt;
\&lt;br /&gt;
#return the GET parameter &#039;sync=value&#039; for /sync Endpoint\&lt;br /&gt;
return &amp;quot;&amp;amp;since=$val&amp;quot; if ($val ne &#039;???&#039;);;\&lt;br /&gt;
\&lt;br /&gt;
#return neither since-key nor value for it:\&lt;br /&gt;
return &amp;quot;&amp;quot;;;&lt;br /&gt;
attr MatrixBot set01Data {\&lt;br /&gt;
  &amp;quot;msgtype&amp;quot;: &amp;quot;m.text&amp;quot;,\&lt;br /&gt;
  &amp;quot;body&amp;quot;: &amp;quot;$val&amp;quot;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot set01HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot set01HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot set01IExpr #cancel an active longpoll\&lt;br /&gt;
if ($hash-&amp;gt;{BUSY}\&lt;br /&gt;
    &amp;amp;&amp;amp; $hash-&amp;gt;{HttpUtils}\&lt;br /&gt;
    &amp;amp;&amp;amp; $hash-&amp;gt;{HttpUtils}-&amp;gt;{url} =~ m|^https://[^/]+/_matrix/client/v3/sync\?timeout=\d+&amp;amp;filter=| ) {\&lt;br /&gt;
    Log(3, &amp;quot;$name: longpoll active, cutting it off now&amp;quot;);;\&lt;br /&gt;
    HttpUtils_Close($hash-&amp;gt;{HttpUtils});;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
#just return $val unteraltered\&lt;br /&gt;
$val&lt;br /&gt;
attr MatrixBot set01Method POST&lt;br /&gt;
attr MatrixBot set01Name sendText&lt;br /&gt;
attr MatrixBot set01ParseResponse 1&lt;br /&gt;
attr MatrixBot set01Regex {\&amp;quot;event_id\&amp;quot;:\&amp;quot;(.*)\&amp;quot;}&lt;br /&gt;
attr MatrixBot set01TextArg 1&lt;br /&gt;
attr MatrixBot set01URL https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%&lt;br /&gt;
attr MatrixBot set02Data {\&lt;br /&gt;
  &amp;quot;room&amp;quot;: {\&lt;br /&gt;
    &amp;quot;rooms&amp;quot;: [&amp;quot;[$name:MatrixRoomID]&amp;quot;],\&lt;br /&gt;
    &amp;quot;timeline&amp;quot;: {\&lt;br /&gt;
      &amp;quot;limit&amp;quot;: 10,\&lt;br /&gt;
      &amp;quot;types&amp;quot;: [&amp;quot;m.room.message&amp;quot;]\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;include_leave&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_join&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_account_data&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_state&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;state&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;ephemeral&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;account_data&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    }\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;event_fields&amp;quot;: [\&lt;br /&gt;
    &amp;quot;content.body&amp;quot;,\&lt;br /&gt;
    &amp;quot;sender&amp;quot;,\&lt;br /&gt;
    &amp;quot;origin_server_ts&amp;quot;\&lt;br /&gt;
  ],\&lt;br /&gt;
  &amp;quot;event_format&amp;quot;: &amp;quot;client&amp;quot;,\&lt;br /&gt;
  &amp;quot;presence&amp;quot;: {\&lt;br /&gt;
    &amp;quot;types&amp;quot;: [],\&lt;br /&gt;
    &amp;quot;not_types&amp;quot;: [&amp;quot;*&amp;quot;]\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;account_data&amp;quot;: {\&lt;br /&gt;
    &amp;quot;types&amp;quot;: [],\&lt;br /&gt;
    &amp;quot;not_types&amp;quot;: [&amp;quot;*&amp;quot;]\&lt;br /&gt;
  }\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot set02HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot set02HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot set02Method POST&lt;br /&gt;
attr MatrixBot set02Name sendFilter&lt;br /&gt;
attr MatrixBot set02NoArg 1&lt;br /&gt;
attr MatrixBot set02ParseResponse 1&lt;br /&gt;
attr MatrixBot set02Regex \&amp;quot;filter_id\&amp;quot;:\s*\&amp;quot;(?&amp;lt;filter_id&amp;gt;\d+)\&amp;quot;&lt;br /&gt;
attr MatrixBot set02URL https://[$name:MatrixServer]/_matrix/client/v3/user/@[$name:MatrixUser]:[$name:MatrixServer]/filter&lt;br /&gt;
attr MatrixBot set03Local 1&lt;br /&gt;
attr MatrixBot set03Name longpollCmd&lt;br /&gt;
attr MatrixBot set03TextArg 1&lt;br /&gt;
attr MatrixBot showError 1&lt;br /&gt;
attr MatrixBot sid01Data {\&lt;br /&gt;
  &amp;quot;type&amp;quot;: &amp;quot;m.login.password&amp;quot;,\&lt;br /&gt;
  &amp;quot;identifier&amp;quot;: {\&lt;br /&gt;
    &amp;quot;type&amp;quot;: &amp;quot;m.id.user&amp;quot;,\&lt;br /&gt;
    &amp;quot;user&amp;quot;: &amp;quot;[$name:MatrixUser]&amp;quot;\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;password&amp;quot;: &amp;quot;%%MatrixPassword%%&amp;quot;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot sid01HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot sid01IdRegex &amp;quot;access_token&amp;quot;\s*:\s*&amp;quot;([^&amp;quot;]+)&amp;quot;&lt;br /&gt;
attr MatrixBot sid01URL https://[$name:MatrixServer]/_matrix/client/v3/login&lt;br /&gt;
attr MatrixBot timeout 65&lt;br /&gt;
attr MatrixBot userReadings longpollTimer:(next_batch|longpollCmd|LAST_ERROR|sendText):.* {\&lt;br /&gt;
  my $longpollCmd = ReadingsVal($name, &#039;longpollCmd&#039;, &#039;???&#039;);;\&lt;br /&gt;
  my $delay = 1;;\&lt;br /&gt;
  my $timeout = AttrVal($name, &#039;timeout&#039;, 61) + $delay + 5;;\&lt;br /&gt;
  \&lt;br /&gt;
  # stop our timers:\&lt;br /&gt;
  if ($longpollCmd eq &amp;quot;stopTimer&amp;quot;) {\&lt;br /&gt;
    fhem(&amp;quot;cancel ${name}_longpollTimer quiet&amp;quot;);;\&lt;br /&gt;
    fhem(&amp;quot;cancel ${name}_longpollTimer2 quiet&amp;quot;);;\&lt;br /&gt;
    return &amp;quot;stopped&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  if ($longpollCmd ne &amp;quot;startTimer&amp;quot;) {\&lt;br /&gt;
    return &amp;quot;no timer set, longpollCmd is not set to &#039;startTimer&#039;&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #if startTimer cmd was given now, set filter as well:\&lt;br /&gt;
  if (ReadingsAge($name, &#039;longpollCmd&#039;, 0) &amp;lt;= 1) {\&lt;br /&gt;
    $delay = 5;; #delay to allow for sendFilter to be answered\&lt;br /&gt;
    #Log(1, &amp;quot;🪲 $name: &amp;gt;&amp;gt;&amp;quot;. InternalVal($name, &#039;httpbody&#039;, &#039;???&#039;) .&amp;quot;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    fhem(&amp;quot;sleep 0.1 quiet;; set $name sendFilter&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #we handle an error reported by http-utils:\&lt;br /&gt;
  if (ReadingsAge($name, &#039;LAST_ERROR&#039;, 0) &amp;lt;= 1) {\&lt;br /&gt;
    my $last_error = ReadingsVal($name, &#039;LAST_ERROR&#039;, &#039;???&#039;);;\&lt;br /&gt;
    $delay = 10;; #delay to allow for error reasons to improve\&lt;br /&gt;
    #Log(1, &amp;quot;🪲 $name: Dealing with error: &amp;gt;&amp;gt;$last_error&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  # for testing this: { $defs{MatrixBot}{sid} = &#039;bla&#039; }\&lt;br /&gt;
  if (!defined &amp;amp;HTTPMOD::handleAuthErrors) {\&lt;br /&gt;
    *HTTPMOD::handleAuthErrors = sub {\&lt;br /&gt;
      my ($hash, $header, $body, $request) = @_;;\&lt;br /&gt;
      my $name = $hash-&amp;gt;{NAME};;\&lt;br /&gt;
      my $status;;\&lt;br /&gt;
      \&lt;br /&gt;
      if ($header =~ m{^HTTP/\d\.\d\s+(\d+)}m) {\&lt;br /&gt;
        $status = $1;;\&lt;br /&gt;
      }\&lt;br /&gt;
      \&lt;br /&gt;
      Log3($name, 4, &amp;quot;$name: HTTP status code is $status&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
      if ( $status == 401 || $status == 403 || $status == 500 ) {\&lt;br /&gt;
        Log3($name, 3, &amp;quot;$name: auth-error or servererror ($status), calling doAuth()&amp;quot;);;\&lt;br /&gt;
        HTTPMOD::DoAuth($hash);;\&lt;br /&gt;
      }\&lt;br /&gt;
    };;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #set timers, one regular and one fallback:\&lt;br /&gt;
  fhem(&amp;quot;sleep $delay ${name}_longpollTimer quiet;; get $name longpoll&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;sleep $timeout ${name}_longpollTimer2 quiet;; set $name longpollCmd startTimer&amp;quot;);;\&lt;br /&gt;
  \&lt;br /&gt;
  return strftime(&amp;quot;next longpoll at %H:%M:%S&amp;quot;, localtime( time()+$delay ));;\&lt;br /&gt;
},\&lt;br /&gt;
messages_list:messages:.* {\&lt;br /&gt;
  my $this = ReadingsVal($name, $reading, &#039;&#039;);;\&lt;br /&gt;
  my @timestampArray = split(&amp;quot;\n&amp;quot;, $this);;\&lt;br /&gt;
  my $messages_ref = decode_json(ReadingsVal($name, &#039;messages&#039;, &#039;&#039;));;\&lt;br /&gt;
  my $length = 20;;\&lt;br /&gt;
  \&lt;br /&gt;
  my %seen_messages = map { $_ =&amp;gt; 1 } @timestampArray;;\&lt;br /&gt;
  \&lt;br /&gt;
  foreach my $msg (@$messages_ref) {\&lt;br /&gt;
    my $val = $msg-&amp;gt;{content}{body};;\&lt;br /&gt;
    $val = Encode::decode(&#039;utf-8&#039;, $val) unless Encode::is_utf8($val);;\&lt;br /&gt;
    $val =~ s/\n/ /g;; #replace newlines with spaces\&lt;br /&gt;
    \&lt;br /&gt;
    my $sender = $msg-&amp;gt;{sender};;\&lt;br /&gt;
    my $ts = strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;, localtime($msg-&amp;gt;{origin_server_ts} / 1000));;\&lt;br /&gt;
    my $new_entry = &amp;quot;$ts: $sender: $val&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    next if $seen_messages{$new_entry};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $inserted = 0;;\&lt;br /&gt;
    for (my $i = 0;; $i &amp;lt; @timestampArray;; $i++) {\&lt;br /&gt;
      my ($existing_ts) = $timestampArray[$i] =~ /^([^:]+):/;;\&lt;br /&gt;
      if ($ts lt $existing_ts) {\&lt;br /&gt;
        splice(@timestampArray, $i, 0, $new_entry);;\&lt;br /&gt;
        $inserted = 1;;\&lt;br /&gt;
        last;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
    push(@timestampArray, encode(&#039;utf-8&#039;, $new_entry)) unless $inserted;;\&lt;br /&gt;
    shift(@timestampArray) while @timestampArray &amp;gt; $length;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  return join(&amp;quot;\n&amp;quot;, @timestampArray);;\&lt;br /&gt;
},\&lt;br /&gt;
process_messages:messages:.* {\&lt;br /&gt;
  my $val = ReadingsVal($name, &#039;messages_list&#039;, &#039;&#039;);;\&lt;br /&gt;
  my $this = ReadingsVal($name, $reading, &#039;&#039;);;\&lt;br /&gt;
  my $latest_ts = $this;;\&lt;br /&gt;
  \&lt;br /&gt;
  foreach my $line (split(/\n/, $val)) {\&lt;br /&gt;
    if ($line =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}):\s*(.*)$/) {\&lt;br /&gt;
      my ($ts, $msg) = ($1, $2);;\&lt;br /&gt;
      \&lt;br /&gt;
      if ($ts gt $this) {\&lt;br /&gt;
        fhem(&amp;quot;trigger $name msg: $line&amp;quot;);;\&lt;br /&gt;
        $latest_ts = $ts if ($ts gt $latest_ts);;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  return $latest_ts;;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot verbose 3&lt;br /&gt;
attr MatrixBot widgetOverride longpollCmd:uzsuSelectRadio,startTimer,stopTimer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Matrix&amp;diff=40173</id>
		<title>Matrix</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Matrix&amp;diff=40173"/>
		<updated>2025-05-24T08:44:39Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* Einrichtung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Allgemeines ==&lt;br /&gt;
&#039;&#039;&#039;Matrix&#039;&#039;&#039; ist ein offenes Kommunikationsprotokoll für dezentrale Echtzeitkommunikation. Es wird von zahlreichen Clients unterstützt, darunter &#039;&#039;&#039;Element&#039;&#039;&#039;, &#039;&#039;&#039;Element-X&#039;&#039;&#039;, &#039;&#039;&#039;SchildiChat Next&#039;&#039;&#039;, &#039;&#039;&#039;FluffyChat&#039;&#039;&#039; und viele weitere.&lt;br /&gt;
&lt;br /&gt;
Viele Open-Source-Projekte betreiben eigene Chaträume auf föderierten Matrix-Servern. Ein Beispiel dafür ist der &#039;&#039;&#039;Gadgetbridge-Chat&#039;&#039;&#039;, einer der größeren und aktiveren Räume im Matrix-Ökosystem.&lt;br /&gt;
&lt;br /&gt;
== Matrix und FHEM ==&lt;br /&gt;
Um mit &#039;&#039;&#039;FHEM&#039;&#039;&#039; Nachrichten über das Matrix-Protokoll zu senden und zu empfangen, stehen mehrere Möglichkeiten zur Verfügung. Eine einfache und pragmatische Lösung ist die Nutzung eines &#039;&#039;&#039;HTTPMOD-Devices&#039;&#039;&#039;. Damit lassen sich Nachrichten über die Matrix-API versenden oder empfangen.&lt;br /&gt;
&lt;br /&gt;
=== Matrix per HTTPMOD Device ===&lt;br /&gt;
Folgendes Device kann Matrix Nachrichten senden und empfangen, es hat wenige Abhängigkeiten und sollte in den meisten FHEM Installationen unkompliziert eingerichtet sein. Es wird die Datenübertragung von FHEM zum Server mit HTTPS verschlüsselt, E2EE wird nicht verwendet.&lt;br /&gt;
&lt;br /&gt;
==== Einrichtung ====&lt;br /&gt;
Es sind die Attribute MatrixServer und MatrixUser zu setzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixServer Dein-Servername&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixUser FHEM-Matrix-Username&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Passwort des Matrix-FHEM-Users setzt man mit dem Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot storeKeyValue MatrixPassword yourPassword123&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann richtet man einen nicht E2E verschlüsselten Raum ein in dem der Matrix-FHEM-Account eingeladen ist. Von dem Raum benötigt man die MatrixRoomID, die man mit Element in den Raumdetails finden kann (Room Settings --&amp;gt; Advanced --&amp;gt; Internal room ID).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixRoomID !12345678901234:example.com&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Verwendung ====&lt;br /&gt;
Nachrichten senden geht mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot sendText Bla Bla Bla&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Longpoll von Nachrichten starten und stoppen mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot longpollCmd startTimer&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot longpollCmd stopTimer&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Weiteres ====&lt;br /&gt;
&lt;br /&gt;
===== Forumlink =====&lt;br /&gt;
https://forum.fhem.de/index.php?topic=120834.msg1339487#msg1339487&lt;br /&gt;
&lt;br /&gt;
===== Demo-Device =====&lt;br /&gt;
Das folgende DOIF-Device lauscht auf eine eingehende Nachricht die als Inhalt &amp;quot;ping&amp;quot; oder &amp;quot;Ping&amp;quot; ist und antwortet mit &amp;quot;Pong!&amp;quot;. Es dient als Beispiel und kann natürlich um weitere Befehle ergänzt/ersetzt werden um FHEM damit zu steuern:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod MatrixPing.doif DOIF ([&amp;quot;MatrixBot:msg: .*: @.*: [Pp]ing$&amp;quot;])\&lt;br /&gt;
  (set MatrixBot sendText Pong!)&lt;br /&gt;
attr MatrixPing.doif alias Matrix Ping/Pong&lt;br /&gt;
attr MatrixPing.doif do always&lt;br /&gt;
attr MatrixPing.doif group Meldungen&lt;br /&gt;
attr MatrixPing.doif icon homeConnect&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Device-Definition ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod MatrixBot HTTPMOD none 0&lt;br /&gt;
attr MatrixBot userattr MatrixRoomID MatrixServer MatrixUser&lt;br /&gt;
attr MatrixBot MatrixRoomID !12345678901234:nope.chat&lt;br /&gt;
attr MatrixBot MatrixServer nope.chat&lt;br /&gt;
attr MatrixBot MatrixUser DeinUsername&lt;br /&gt;
attr MatrixBot bodyDecode utf-8&lt;br /&gt;
attr MatrixBot bodyEncode utf-8&lt;br /&gt;
attr MatrixBot comment &amp;quot;\&lt;br /&gt;
Create a room for FHEM.\&lt;br /&gt;
\&lt;br /&gt;
The room must not use encryption, a room that has encryption\&lt;br /&gt;
enabled, cannot be converted to a non-encrypted room anymore \&lt;br /&gt;
\&lt;br /&gt;
The room-id can be found in Element-Web at:\&lt;br /&gt;
Room Settings --&amp;gt; Advanced --&amp;gt; Internal room ID\&lt;br /&gt;
\&lt;br /&gt;
To store the password in FHEM in obfuscated way:\&lt;br /&gt;
set MatrixBot storeKeyValue MatrixPassword yourPassword123\&lt;br /&gt;
\&lt;br /&gt;
###\&lt;br /&gt;
To send a text:\&lt;br /&gt;
set MatrixBot sendText Bla Bla Bla\&lt;br /&gt;
\&lt;br /&gt;
\&lt;br /&gt;
###\&lt;br /&gt;
To longpoll for messages once:\&lt;br /&gt;
# 1. send special filter to Matrix:\&lt;br /&gt;
set MatrixBot sendFilter\&lt;br /&gt;
\&lt;br /&gt;
# 2. start one longPoll (waits up to 60 seconds\&lt;br /&gt;
#                        or until data is available)\&lt;br /&gt;
get MatrixBot longpoll\&lt;br /&gt;
\&lt;br /&gt;
#alternatively, to keep on longPolling (this also sets filter):\&lt;br /&gt;
set MatrixBot longpollCmd startTimer\&lt;br /&gt;
#to stop the timers:\&lt;br /&gt;
set MatrixBot longpollCmd stopTimer\&lt;br /&gt;
\&lt;br /&gt;
#############################################################\&lt;br /&gt;
https://spec.matrix.org/v1.14/client-server-api/#syncing\&lt;br /&gt;
&amp;quot;&lt;br /&gt;
attr MatrixBot get02AlwaysNum 0&lt;br /&gt;
attr MatrixBot get02HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot get02Name longpoll&lt;br /&gt;
attr MatrixBot get02Regex \&amp;quot;next_batch\&amp;quot;:\s*\&amp;quot;(?&amp;lt;next_batch&amp;gt;[^\&amp;quot;]+)\&amp;quot;(?:.*?\&amp;quot;timeline\&amp;quot;:\s*{\s*\&amp;quot;events\&amp;quot;:\s*(?&amp;lt;messages&amp;gt;\[.*?\])\s*)?&lt;br /&gt;
attr MatrixBot get02TextArg 0&lt;br /&gt;
attr MatrixBot get02URL https://[$name:MatrixServer]/_matrix/client/v3/sync?timeout=60000&amp;amp;filter=[$name:filter_id]%%next_batch_param%%&lt;br /&gt;
attr MatrixBot icon message_info&lt;br /&gt;
attr MatrixBot parseFunction1 handleAuthErrors&lt;br /&gt;
attr MatrixBot reAuthAlways 0&lt;br /&gt;
attr MatrixBot reAuthRegex M_UNKNOWN_TOKEN&lt;br /&gt;
attr MatrixBot replacement01Mode expression&lt;br /&gt;
attr MatrixBot replacement01Regex \[([^:\s\[\&amp;quot;\&#039;]+):([^\]\s]+)\]&lt;br /&gt;
attr MatrixBot replacement01Value my $device = $name if ($1 eq &amp;quot;\$name&amp;quot;) // $1;;\&lt;br /&gt;
ReadingsVal($device, $2, undef) or AttrVal($device, $2, &amp;quot;???&amp;quot;);;&lt;br /&gt;
attr MatrixBot replacement02Mode expression&lt;br /&gt;
attr MatrixBot replacement02Regex %%uuid%%&lt;br /&gt;
attr MatrixBot replacement02Value join(&amp;quot;-&amp;quot;, unpack(&amp;quot;A8 A4 A4 A4 A12&amp;quot;, unpack(&amp;quot;H*&amp;quot;, join(&amp;quot;&amp;quot;, map { chr(int rand 256) } 0..15))))&lt;br /&gt;
attr MatrixBot replacement03Mode key&lt;br /&gt;
attr MatrixBot replacement03Regex %%MatrixPassword%%&lt;br /&gt;
attr MatrixBot replacement03Value MatrixPassword&lt;br /&gt;
attr MatrixBot replacement04Mode expression&lt;br /&gt;
attr MatrixBot replacement04Regex %%next_batch_param%%&lt;br /&gt;
attr MatrixBot replacement04Value #is there a reading &#039;next_batch&#039;?\&lt;br /&gt;
my $val = ReadingsVal($name, &#039;next_batch&#039;, &#039;???&#039;);;\&lt;br /&gt;
\&lt;br /&gt;
#return the GET parameter &#039;sync=value&#039; for /sync Endpoint\&lt;br /&gt;
return &amp;quot;&amp;amp;since=$val&amp;quot; if ($val ne &#039;???&#039;);;\&lt;br /&gt;
\&lt;br /&gt;
#return neither since-key nor value for it:\&lt;br /&gt;
return &amp;quot;&amp;quot;;;&lt;br /&gt;
attr MatrixBot set01Data {\&lt;br /&gt;
  &amp;quot;msgtype&amp;quot;: &amp;quot;m.text&amp;quot;,\&lt;br /&gt;
  &amp;quot;body&amp;quot;: &amp;quot;$val&amp;quot;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot set01HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot set01HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot set01IExpr #cancel an active longpoll\&lt;br /&gt;
if ($hash-&amp;gt;{BUSY}\&lt;br /&gt;
    &amp;amp;&amp;amp; $hash-&amp;gt;{HttpUtils}\&lt;br /&gt;
    &amp;amp;&amp;amp; $hash-&amp;gt;{HttpUtils}-&amp;gt;{url} =~ m|^https://[^/]+/_matrix/client/v3/sync\?timeout=\d+&amp;amp;filter=| ) {\&lt;br /&gt;
    Log(3, &amp;quot;$name: longpoll active, cutting it off now&amp;quot;);;\&lt;br /&gt;
    HttpUtils_Close($hash-&amp;gt;{HttpUtils});;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
#just return $val unteraltered\&lt;br /&gt;
$val&lt;br /&gt;
attr MatrixBot set01Method POST&lt;br /&gt;
attr MatrixBot set01Name sendText&lt;br /&gt;
attr MatrixBot set01ParseResponse 1&lt;br /&gt;
attr MatrixBot set01Regex {\&amp;quot;event_id\&amp;quot;:\&amp;quot;(.*)\&amp;quot;}&lt;br /&gt;
attr MatrixBot set01TextArg 1&lt;br /&gt;
attr MatrixBot set01URL https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%&lt;br /&gt;
attr MatrixBot set02Data {\&lt;br /&gt;
  &amp;quot;room&amp;quot;: {\&lt;br /&gt;
    &amp;quot;rooms&amp;quot;: [&amp;quot;[$name:MatrixRoomID]&amp;quot;],\&lt;br /&gt;
    &amp;quot;timeline&amp;quot;: {\&lt;br /&gt;
      &amp;quot;limit&amp;quot;: 10,\&lt;br /&gt;
      &amp;quot;types&amp;quot;: [&amp;quot;m.room.message&amp;quot;]\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;include_leave&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_join&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_account_data&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_state&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;state&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;ephemeral&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;account_data&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    }\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;event_fields&amp;quot;: [\&lt;br /&gt;
    &amp;quot;content.body&amp;quot;,\&lt;br /&gt;
    &amp;quot;sender&amp;quot;,\&lt;br /&gt;
    &amp;quot;origin_server_ts&amp;quot;\&lt;br /&gt;
  ],\&lt;br /&gt;
  &amp;quot;event_format&amp;quot;: &amp;quot;client&amp;quot;,\&lt;br /&gt;
  &amp;quot;presence&amp;quot;: {\&lt;br /&gt;
    &amp;quot;types&amp;quot;: [],\&lt;br /&gt;
    &amp;quot;not_types&amp;quot;: [&amp;quot;*&amp;quot;]\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;account_data&amp;quot;: {\&lt;br /&gt;
    &amp;quot;types&amp;quot;: [],\&lt;br /&gt;
    &amp;quot;not_types&amp;quot;: [&amp;quot;*&amp;quot;]\&lt;br /&gt;
  }\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot set02HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot set02HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot set02Method POST&lt;br /&gt;
attr MatrixBot set02Name sendFilter&lt;br /&gt;
attr MatrixBot set02NoArg 1&lt;br /&gt;
attr MatrixBot set02ParseResponse 1&lt;br /&gt;
attr MatrixBot set02Regex \&amp;quot;filter_id\&amp;quot;:\s*\&amp;quot;(?&amp;lt;filter_id&amp;gt;\d+)\&amp;quot;&lt;br /&gt;
attr MatrixBot set02URL https://[$name:MatrixServer]/_matrix/client/v3/user/@[$name:MatrixUser]:[$name:MatrixServer]/filter&lt;br /&gt;
attr MatrixBot set03Local 1&lt;br /&gt;
attr MatrixBot set03Name longpollCmd&lt;br /&gt;
attr MatrixBot set03TextArg 1&lt;br /&gt;
attr MatrixBot showError 1&lt;br /&gt;
attr MatrixBot sid01Data {\&lt;br /&gt;
  &amp;quot;type&amp;quot;: &amp;quot;m.login.password&amp;quot;,\&lt;br /&gt;
  &amp;quot;identifier&amp;quot;: {\&lt;br /&gt;
    &amp;quot;type&amp;quot;: &amp;quot;m.id.user&amp;quot;,\&lt;br /&gt;
    &amp;quot;user&amp;quot;: &amp;quot;[$name:MatrixUser]&amp;quot;\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;password&amp;quot;: &amp;quot;%%MatrixPassword%%&amp;quot;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot sid01HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot sid01IdRegex &amp;quot;access_token&amp;quot;\s*:\s*&amp;quot;([^&amp;quot;]+)&amp;quot;&lt;br /&gt;
attr MatrixBot sid01URL https://[$name:MatrixServer]/_matrix/client/v3/login&lt;br /&gt;
attr MatrixBot timeout 65&lt;br /&gt;
attr MatrixBot userReadings longpollTimer:(next_batch|longpollCmd|LAST_ERROR|sendText):.* {\&lt;br /&gt;
  my $longpollCmd = ReadingsVal($name, &#039;longpollCmd&#039;, &#039;???&#039;);;\&lt;br /&gt;
  my $delay = 1;;\&lt;br /&gt;
  my $timeout = AttrVal($name, &#039;timeout&#039;, 61) + $delay + 5;;\&lt;br /&gt;
  \&lt;br /&gt;
  # stop our timers:\&lt;br /&gt;
  if ($longpollCmd eq &amp;quot;stopTimer&amp;quot;) {\&lt;br /&gt;
    fhem(&amp;quot;cancel ${name}_longpollTimer quiet&amp;quot;);;\&lt;br /&gt;
    fhem(&amp;quot;cancel ${name}_longpollTimer2 quiet&amp;quot;);;\&lt;br /&gt;
    return &amp;quot;stopped&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  if ($longpollCmd ne &amp;quot;startTimer&amp;quot;) {\&lt;br /&gt;
    return &amp;quot;no timer set, longpollCmd is not set to &#039;startTimer&#039;&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #if startTimer cmd was given now, set filter as well:\&lt;br /&gt;
  if (ReadingsAge($name, &#039;longpollCmd&#039;, 0) &amp;lt;= 1) {\&lt;br /&gt;
    $delay = 5;; #delay to allow for sendFilter to be answered\&lt;br /&gt;
    #Log(1, &amp;quot;🪲 $name: &amp;gt;&amp;gt;&amp;quot;. InternalVal($name, &#039;httpbody&#039;, &#039;???&#039;) .&amp;quot;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    fhem(&amp;quot;sleep 0.1 quiet;; set $name sendFilter&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #we handle an error reported by http-utils:\&lt;br /&gt;
  if (ReadingsAge($name, &#039;LAST_ERROR&#039;, 0) &amp;lt;= 1) {\&lt;br /&gt;
    my $last_error = ReadingsVal($name, &#039;LAST_ERROR&#039;, &#039;???&#039;);;\&lt;br /&gt;
    $delay = 10;; #delay to allow for error reasons to improve\&lt;br /&gt;
    #Log(1, &amp;quot;🪲 $name: Dealing with error: &amp;gt;&amp;gt;$last_error&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  # for testing this: { $defs{MatrixBot}{sid} = &#039;bla&#039; }\&lt;br /&gt;
  if (!defined &amp;amp;HTTPMOD::handleAuthErrors) {\&lt;br /&gt;
    *HTTPMOD::handleAuthErrors = sub {\&lt;br /&gt;
      my ($hash, $header, $body, $request) = @_;;\&lt;br /&gt;
      my $name = $hash-&amp;gt;{NAME};;\&lt;br /&gt;
      my $status;;\&lt;br /&gt;
      \&lt;br /&gt;
      if ($header =~ m{^HTTP/\d\.\d\s+(\d+)}m) {\&lt;br /&gt;
        $status = $1;;\&lt;br /&gt;
      }\&lt;br /&gt;
      \&lt;br /&gt;
      Log3($name, 4, &amp;quot;$name: HTTP status code is $status&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
      if ( $status == 401 || $status == 403 || $status == 500 ) {\&lt;br /&gt;
        Log3($name, 3, &amp;quot;$name: auth-error or servererror ($status), calling doAuth()&amp;quot;);;\&lt;br /&gt;
        HTTPMOD::DoAuth($hash);;\&lt;br /&gt;
      }\&lt;br /&gt;
    };;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #set timers, one regular and one fallback:\&lt;br /&gt;
  fhem(&amp;quot;sleep $delay ${name}_longpollTimer quiet;; get $name longpoll&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;sleep $timeout ${name}_longpollTimer2 quiet;; set $name longpollCmd startTimer&amp;quot;);;\&lt;br /&gt;
  \&lt;br /&gt;
  return strftime(&amp;quot;next longpoll at %H:%M:%S&amp;quot;, localtime( time()+$delay ));;\&lt;br /&gt;
},\&lt;br /&gt;
messages_list:messages:.* {\&lt;br /&gt;
  my $this = ReadingsVal($name, $reading, &#039;&#039;);;\&lt;br /&gt;
  my @timestampArray = split(&amp;quot;\n&amp;quot;, $this);;\&lt;br /&gt;
  my $messages_ref = decode_json(ReadingsVal($name, &#039;messages&#039;, &#039;&#039;));;\&lt;br /&gt;
  my $length = 20;;\&lt;br /&gt;
  \&lt;br /&gt;
  my %seen_messages = map { $_ =&amp;gt; 1 } @timestampArray;;\&lt;br /&gt;
  \&lt;br /&gt;
  foreach my $msg (@$messages_ref) {\&lt;br /&gt;
    my $val = $msg-&amp;gt;{content}{body};;\&lt;br /&gt;
    $val = Encode::decode(&#039;utf-8&#039;, $val) unless Encode::is_utf8($val);;\&lt;br /&gt;
    $val =~ s/\n/ /g;; #replace newlines with spaces\&lt;br /&gt;
    \&lt;br /&gt;
    my $sender = $msg-&amp;gt;{sender};;\&lt;br /&gt;
    my $ts = strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;, localtime($msg-&amp;gt;{origin_server_ts} / 1000));;\&lt;br /&gt;
    my $new_entry = &amp;quot;$ts: $sender: $val&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    next if $seen_messages{$new_entry};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $inserted = 0;;\&lt;br /&gt;
    for (my $i = 0;; $i &amp;lt; @timestampArray;; $i++) {\&lt;br /&gt;
      my ($existing_ts) = $timestampArray[$i] =~ /^([^:]+):/;;\&lt;br /&gt;
      if ($ts lt $existing_ts) {\&lt;br /&gt;
        splice(@timestampArray, $i, 0, $new_entry);;\&lt;br /&gt;
        $inserted = 1;;\&lt;br /&gt;
        last;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
    push(@timestampArray, encode(&#039;utf-8&#039;, $new_entry)) unless $inserted;;\&lt;br /&gt;
    shift(@timestampArray) while @timestampArray &amp;gt; $length;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  return join(&amp;quot;\n&amp;quot;, @timestampArray);;\&lt;br /&gt;
},\&lt;br /&gt;
process_messages:messages:.* {\&lt;br /&gt;
  my $val = ReadingsVal($name, &#039;messages_list&#039;, &#039;&#039;);;\&lt;br /&gt;
  my $this = ReadingsVal($name, $reading, &#039;&#039;);;\&lt;br /&gt;
  my $latest_ts = $this;;\&lt;br /&gt;
  \&lt;br /&gt;
  foreach my $line (split(/\n/, $val)) {\&lt;br /&gt;
    if ($line =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}):\s*(.*)$/) {\&lt;br /&gt;
      my ($ts, $msg) = ($1, $2);;\&lt;br /&gt;
      \&lt;br /&gt;
      if ($ts gt $this) {\&lt;br /&gt;
        fhem(&amp;quot;trigger $name msg: $line&amp;quot;);;\&lt;br /&gt;
        $latest_ts = $ts if ($ts gt $latest_ts);;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  return $latest_ts;;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot verbose 3&lt;br /&gt;
attr MatrixBot widgetOverride longpollCmd:uzsuSelectRadio,startTimer,stopTimer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Matrix&amp;diff=40172</id>
		<title>Matrix</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Matrix&amp;diff=40172"/>
		<updated>2025-05-24T08:37:09Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* Weiteres */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Allgemeines ==&lt;br /&gt;
&#039;&#039;&#039;Matrix&#039;&#039;&#039; ist ein offenes Kommunikationsprotokoll für dezentrale Echtzeitkommunikation. Es wird von zahlreichen Clients unterstützt, darunter &#039;&#039;&#039;Element&#039;&#039;&#039;, &#039;&#039;&#039;Element-X&#039;&#039;&#039;, &#039;&#039;&#039;SchildiChat Next&#039;&#039;&#039;, &#039;&#039;&#039;FluffyChat&#039;&#039;&#039; und viele weitere.&lt;br /&gt;
&lt;br /&gt;
Viele Open-Source-Projekte betreiben eigene Chaträume auf föderierten Matrix-Servern. Ein Beispiel dafür ist der &#039;&#039;&#039;Gadgetbridge-Chat&#039;&#039;&#039;, einer der größeren und aktiveren Räume im Matrix-Ökosystem.&lt;br /&gt;
&lt;br /&gt;
== Matrix und FHEM ==&lt;br /&gt;
Um mit &#039;&#039;&#039;FHEM&#039;&#039;&#039; Nachrichten über das Matrix-Protokoll zu senden und zu empfangen, stehen mehrere Möglichkeiten zur Verfügung. Eine einfache und pragmatische Lösung ist die Nutzung eines &#039;&#039;&#039;HTTPMOD-Devices&#039;&#039;&#039;. Damit lassen sich Nachrichten über die Matrix-API versenden oder empfangen.&lt;br /&gt;
&lt;br /&gt;
=== Matrix per HTTPMOD Device ===&lt;br /&gt;
Folgendes Device kann Matrix Nachrichten senden und empfangen, es hat wenige Abhängigkeiten und sollte in den meisten FHEM Installationen unkompliziert eingerichtet sein. Es wird die Datenübertragung von FHEM zum Server mit HTTPS verschlüsselt, E2EE wird nicht verwendet.&lt;br /&gt;
&lt;br /&gt;
==== Einrichtung ====&lt;br /&gt;
Es sind die Attribute MatrixServer und MatrixUser zu setzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixServer Dein-Servername&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixUser FHEM-Matrix-Username&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann richtet man einen nicht E2E verschlüsselten Raum ein in dem der Matrix-FHEM-Account eingeladen ist. Von dem Raum benötigt man die MatrixRoomID, die man mit Element in den Raumdetails finden kann (Room Settings --&amp;gt; Advanced --&amp;gt; Internal room ID).&lt;br /&gt;
&lt;br /&gt;
Das Passwort des Matrix-FHEM-Users setzt man mit dem Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot storeKeyValue MatrixPassword yourPassword123&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Verwendung ====&lt;br /&gt;
Nachrichten senden geht mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot sendText Bla Bla Bla&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Longpoll von Nachrichten starten und stoppen mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot longpollCmd startTimer&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot longpollCmd stopTimer&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Weiteres ====&lt;br /&gt;
&lt;br /&gt;
===== Forumlink =====&lt;br /&gt;
https://forum.fhem.de/index.php?topic=120834.msg1339487#msg1339487&lt;br /&gt;
&lt;br /&gt;
===== Demo-Device =====&lt;br /&gt;
Das folgende DOIF-Device lauscht auf eine eingehende Nachricht die als Inhalt &amp;quot;ping&amp;quot; oder &amp;quot;Ping&amp;quot; ist und antwortet mit &amp;quot;Pong!&amp;quot;. Es dient als Beispiel und kann natürlich um weitere Befehle ergänzt/ersetzt werden um FHEM damit zu steuern:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod MatrixPing.doif DOIF ([&amp;quot;MatrixBot:msg: .*: @.*: [Pp]ing$&amp;quot;])\&lt;br /&gt;
  (set MatrixBot sendText Pong!)&lt;br /&gt;
attr MatrixPing.doif alias Matrix Ping/Pong&lt;br /&gt;
attr MatrixPing.doif do always&lt;br /&gt;
attr MatrixPing.doif group Meldungen&lt;br /&gt;
attr MatrixPing.doif icon homeConnect&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Device-Definition ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod MatrixBot HTTPMOD none 0&lt;br /&gt;
attr MatrixBot userattr MatrixRoomID MatrixServer MatrixUser&lt;br /&gt;
attr MatrixBot MatrixRoomID !12345678901234:nope.chat&lt;br /&gt;
attr MatrixBot MatrixServer nope.chat&lt;br /&gt;
attr MatrixBot MatrixUser DeinUsername&lt;br /&gt;
attr MatrixBot bodyDecode utf-8&lt;br /&gt;
attr MatrixBot bodyEncode utf-8&lt;br /&gt;
attr MatrixBot comment &amp;quot;\&lt;br /&gt;
Create a room for FHEM.\&lt;br /&gt;
\&lt;br /&gt;
The room must not use encryption, a room that has encryption\&lt;br /&gt;
enabled, cannot be converted to a non-encrypted room anymore \&lt;br /&gt;
\&lt;br /&gt;
The room-id can be found in Element-Web at:\&lt;br /&gt;
Room Settings --&amp;gt; Advanced --&amp;gt; Internal room ID\&lt;br /&gt;
\&lt;br /&gt;
To store the password in FHEM in obfuscated way:\&lt;br /&gt;
set MatrixBot storeKeyValue MatrixPassword yourPassword123\&lt;br /&gt;
\&lt;br /&gt;
###\&lt;br /&gt;
To send a text:\&lt;br /&gt;
set MatrixBot sendText Bla Bla Bla\&lt;br /&gt;
\&lt;br /&gt;
\&lt;br /&gt;
###\&lt;br /&gt;
To longpoll for messages once:\&lt;br /&gt;
# 1. send special filter to Matrix:\&lt;br /&gt;
set MatrixBot sendFilter\&lt;br /&gt;
\&lt;br /&gt;
# 2. start one longPoll (waits up to 60 seconds\&lt;br /&gt;
#                        or until data is available)\&lt;br /&gt;
get MatrixBot longpoll\&lt;br /&gt;
\&lt;br /&gt;
#alternatively, to keep on longPolling (this also sets filter):\&lt;br /&gt;
set MatrixBot longpollCmd startTimer\&lt;br /&gt;
#to stop the timers:\&lt;br /&gt;
set MatrixBot longpollCmd stopTimer\&lt;br /&gt;
\&lt;br /&gt;
#############################################################\&lt;br /&gt;
https://spec.matrix.org/v1.14/client-server-api/#syncing\&lt;br /&gt;
&amp;quot;&lt;br /&gt;
attr MatrixBot get02AlwaysNum 0&lt;br /&gt;
attr MatrixBot get02HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot get02Name longpoll&lt;br /&gt;
attr MatrixBot get02Regex \&amp;quot;next_batch\&amp;quot;:\s*\&amp;quot;(?&amp;lt;next_batch&amp;gt;[^\&amp;quot;]+)\&amp;quot;(?:.*?\&amp;quot;timeline\&amp;quot;:\s*{\s*\&amp;quot;events\&amp;quot;:\s*(?&amp;lt;messages&amp;gt;\[.*?\])\s*)?&lt;br /&gt;
attr MatrixBot get02TextArg 0&lt;br /&gt;
attr MatrixBot get02URL https://[$name:MatrixServer]/_matrix/client/v3/sync?timeout=60000&amp;amp;filter=[$name:filter_id]%%next_batch_param%%&lt;br /&gt;
attr MatrixBot icon message_info&lt;br /&gt;
attr MatrixBot parseFunction1 handleAuthErrors&lt;br /&gt;
attr MatrixBot reAuthAlways 0&lt;br /&gt;
attr MatrixBot reAuthRegex M_UNKNOWN_TOKEN&lt;br /&gt;
attr MatrixBot replacement01Mode expression&lt;br /&gt;
attr MatrixBot replacement01Regex \[([^:\s\[\&amp;quot;\&#039;]+):([^\]\s]+)\]&lt;br /&gt;
attr MatrixBot replacement01Value my $device = $name if ($1 eq &amp;quot;\$name&amp;quot;) // $1;;\&lt;br /&gt;
ReadingsVal($device, $2, undef) or AttrVal($device, $2, &amp;quot;???&amp;quot;);;&lt;br /&gt;
attr MatrixBot replacement02Mode expression&lt;br /&gt;
attr MatrixBot replacement02Regex %%uuid%%&lt;br /&gt;
attr MatrixBot replacement02Value join(&amp;quot;-&amp;quot;, unpack(&amp;quot;A8 A4 A4 A4 A12&amp;quot;, unpack(&amp;quot;H*&amp;quot;, join(&amp;quot;&amp;quot;, map { chr(int rand 256) } 0..15))))&lt;br /&gt;
attr MatrixBot replacement03Mode key&lt;br /&gt;
attr MatrixBot replacement03Regex %%MatrixPassword%%&lt;br /&gt;
attr MatrixBot replacement03Value MatrixPassword&lt;br /&gt;
attr MatrixBot replacement04Mode expression&lt;br /&gt;
attr MatrixBot replacement04Regex %%next_batch_param%%&lt;br /&gt;
attr MatrixBot replacement04Value #is there a reading &#039;next_batch&#039;?\&lt;br /&gt;
my $val = ReadingsVal($name, &#039;next_batch&#039;, &#039;???&#039;);;\&lt;br /&gt;
\&lt;br /&gt;
#return the GET parameter &#039;sync=value&#039; for /sync Endpoint\&lt;br /&gt;
return &amp;quot;&amp;amp;since=$val&amp;quot; if ($val ne &#039;???&#039;);;\&lt;br /&gt;
\&lt;br /&gt;
#return neither since-key nor value for it:\&lt;br /&gt;
return &amp;quot;&amp;quot;;;&lt;br /&gt;
attr MatrixBot set01Data {\&lt;br /&gt;
  &amp;quot;msgtype&amp;quot;: &amp;quot;m.text&amp;quot;,\&lt;br /&gt;
  &amp;quot;body&amp;quot;: &amp;quot;$val&amp;quot;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot set01HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot set01HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot set01IExpr #cancel an active longpoll\&lt;br /&gt;
if ($hash-&amp;gt;{BUSY}\&lt;br /&gt;
    &amp;amp;&amp;amp; $hash-&amp;gt;{HttpUtils}\&lt;br /&gt;
    &amp;amp;&amp;amp; $hash-&amp;gt;{HttpUtils}-&amp;gt;{url} =~ m|^https://[^/]+/_matrix/client/v3/sync\?timeout=\d+&amp;amp;filter=| ) {\&lt;br /&gt;
    Log(3, &amp;quot;$name: longpoll active, cutting it off now&amp;quot;);;\&lt;br /&gt;
    HttpUtils_Close($hash-&amp;gt;{HttpUtils});;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
#just return $val unteraltered\&lt;br /&gt;
$val&lt;br /&gt;
attr MatrixBot set01Method POST&lt;br /&gt;
attr MatrixBot set01Name sendText&lt;br /&gt;
attr MatrixBot set01ParseResponse 1&lt;br /&gt;
attr MatrixBot set01Regex {\&amp;quot;event_id\&amp;quot;:\&amp;quot;(.*)\&amp;quot;}&lt;br /&gt;
attr MatrixBot set01TextArg 1&lt;br /&gt;
attr MatrixBot set01URL https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%&lt;br /&gt;
attr MatrixBot set02Data {\&lt;br /&gt;
  &amp;quot;room&amp;quot;: {\&lt;br /&gt;
    &amp;quot;rooms&amp;quot;: [&amp;quot;[$name:MatrixRoomID]&amp;quot;],\&lt;br /&gt;
    &amp;quot;timeline&amp;quot;: {\&lt;br /&gt;
      &amp;quot;limit&amp;quot;: 10,\&lt;br /&gt;
      &amp;quot;types&amp;quot;: [&amp;quot;m.room.message&amp;quot;]\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;include_leave&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_join&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_account_data&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_state&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;state&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;ephemeral&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;account_data&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    }\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;event_fields&amp;quot;: [\&lt;br /&gt;
    &amp;quot;content.body&amp;quot;,\&lt;br /&gt;
    &amp;quot;sender&amp;quot;,\&lt;br /&gt;
    &amp;quot;origin_server_ts&amp;quot;\&lt;br /&gt;
  ],\&lt;br /&gt;
  &amp;quot;event_format&amp;quot;: &amp;quot;client&amp;quot;,\&lt;br /&gt;
  &amp;quot;presence&amp;quot;: {\&lt;br /&gt;
    &amp;quot;types&amp;quot;: [],\&lt;br /&gt;
    &amp;quot;not_types&amp;quot;: [&amp;quot;*&amp;quot;]\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;account_data&amp;quot;: {\&lt;br /&gt;
    &amp;quot;types&amp;quot;: [],\&lt;br /&gt;
    &amp;quot;not_types&amp;quot;: [&amp;quot;*&amp;quot;]\&lt;br /&gt;
  }\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot set02HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot set02HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot set02Method POST&lt;br /&gt;
attr MatrixBot set02Name sendFilter&lt;br /&gt;
attr MatrixBot set02NoArg 1&lt;br /&gt;
attr MatrixBot set02ParseResponse 1&lt;br /&gt;
attr MatrixBot set02Regex \&amp;quot;filter_id\&amp;quot;:\s*\&amp;quot;(?&amp;lt;filter_id&amp;gt;\d+)\&amp;quot;&lt;br /&gt;
attr MatrixBot set02URL https://[$name:MatrixServer]/_matrix/client/v3/user/@[$name:MatrixUser]:[$name:MatrixServer]/filter&lt;br /&gt;
attr MatrixBot set03Local 1&lt;br /&gt;
attr MatrixBot set03Name longpollCmd&lt;br /&gt;
attr MatrixBot set03TextArg 1&lt;br /&gt;
attr MatrixBot showError 1&lt;br /&gt;
attr MatrixBot sid01Data {\&lt;br /&gt;
  &amp;quot;type&amp;quot;: &amp;quot;m.login.password&amp;quot;,\&lt;br /&gt;
  &amp;quot;identifier&amp;quot;: {\&lt;br /&gt;
    &amp;quot;type&amp;quot;: &amp;quot;m.id.user&amp;quot;,\&lt;br /&gt;
    &amp;quot;user&amp;quot;: &amp;quot;[$name:MatrixUser]&amp;quot;\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;password&amp;quot;: &amp;quot;%%MatrixPassword%%&amp;quot;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot sid01HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot sid01IdRegex &amp;quot;access_token&amp;quot;\s*:\s*&amp;quot;([^&amp;quot;]+)&amp;quot;&lt;br /&gt;
attr MatrixBot sid01URL https://[$name:MatrixServer]/_matrix/client/v3/login&lt;br /&gt;
attr MatrixBot timeout 65&lt;br /&gt;
attr MatrixBot userReadings longpollTimer:(next_batch|longpollCmd|LAST_ERROR|sendText):.* {\&lt;br /&gt;
  my $longpollCmd = ReadingsVal($name, &#039;longpollCmd&#039;, &#039;???&#039;);;\&lt;br /&gt;
  my $delay = 1;;\&lt;br /&gt;
  my $timeout = AttrVal($name, &#039;timeout&#039;, 61) + $delay + 5;;\&lt;br /&gt;
  \&lt;br /&gt;
  # stop our timers:\&lt;br /&gt;
  if ($longpollCmd eq &amp;quot;stopTimer&amp;quot;) {\&lt;br /&gt;
    fhem(&amp;quot;cancel ${name}_longpollTimer quiet&amp;quot;);;\&lt;br /&gt;
    fhem(&amp;quot;cancel ${name}_longpollTimer2 quiet&amp;quot;);;\&lt;br /&gt;
    return &amp;quot;stopped&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  if ($longpollCmd ne &amp;quot;startTimer&amp;quot;) {\&lt;br /&gt;
    return &amp;quot;no timer set, longpollCmd is not set to &#039;startTimer&#039;&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #if startTimer cmd was given now, set filter as well:\&lt;br /&gt;
  if (ReadingsAge($name, &#039;longpollCmd&#039;, 0) &amp;lt;= 1) {\&lt;br /&gt;
    $delay = 5;; #delay to allow for sendFilter to be answered\&lt;br /&gt;
    #Log(1, &amp;quot;🪲 $name: &amp;gt;&amp;gt;&amp;quot;. InternalVal($name, &#039;httpbody&#039;, &#039;???&#039;) .&amp;quot;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    fhem(&amp;quot;sleep 0.1 quiet;; set $name sendFilter&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #we handle an error reported by http-utils:\&lt;br /&gt;
  if (ReadingsAge($name, &#039;LAST_ERROR&#039;, 0) &amp;lt;= 1) {\&lt;br /&gt;
    my $last_error = ReadingsVal($name, &#039;LAST_ERROR&#039;, &#039;???&#039;);;\&lt;br /&gt;
    $delay = 10;; #delay to allow for error reasons to improve\&lt;br /&gt;
    #Log(1, &amp;quot;🪲 $name: Dealing with error: &amp;gt;&amp;gt;$last_error&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  # for testing this: { $defs{MatrixBot}{sid} = &#039;bla&#039; }\&lt;br /&gt;
  if (!defined &amp;amp;HTTPMOD::handleAuthErrors) {\&lt;br /&gt;
    *HTTPMOD::handleAuthErrors = sub {\&lt;br /&gt;
      my ($hash, $header, $body, $request) = @_;;\&lt;br /&gt;
      my $name = $hash-&amp;gt;{NAME};;\&lt;br /&gt;
      my $status;;\&lt;br /&gt;
      \&lt;br /&gt;
      if ($header =~ m{^HTTP/\d\.\d\s+(\d+)}m) {\&lt;br /&gt;
        $status = $1;;\&lt;br /&gt;
      }\&lt;br /&gt;
      \&lt;br /&gt;
      Log3($name, 4, &amp;quot;$name: HTTP status code is $status&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
      if ( $status == 401 || $status == 403 || $status == 500 ) {\&lt;br /&gt;
        Log3($name, 3, &amp;quot;$name: auth-error or servererror ($status), calling doAuth()&amp;quot;);;\&lt;br /&gt;
        HTTPMOD::DoAuth($hash);;\&lt;br /&gt;
      }\&lt;br /&gt;
    };;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #set timers, one regular and one fallback:\&lt;br /&gt;
  fhem(&amp;quot;sleep $delay ${name}_longpollTimer quiet;; get $name longpoll&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;sleep $timeout ${name}_longpollTimer2 quiet;; set $name longpollCmd startTimer&amp;quot;);;\&lt;br /&gt;
  \&lt;br /&gt;
  return strftime(&amp;quot;next longpoll at %H:%M:%S&amp;quot;, localtime( time()+$delay ));;\&lt;br /&gt;
},\&lt;br /&gt;
messages_list:messages:.* {\&lt;br /&gt;
  my $this = ReadingsVal($name, $reading, &#039;&#039;);;\&lt;br /&gt;
  my @timestampArray = split(&amp;quot;\n&amp;quot;, $this);;\&lt;br /&gt;
  my $messages_ref = decode_json(ReadingsVal($name, &#039;messages&#039;, &#039;&#039;));;\&lt;br /&gt;
  my $length = 20;;\&lt;br /&gt;
  \&lt;br /&gt;
  my %seen_messages = map { $_ =&amp;gt; 1 } @timestampArray;;\&lt;br /&gt;
  \&lt;br /&gt;
  foreach my $msg (@$messages_ref) {\&lt;br /&gt;
    my $val = $msg-&amp;gt;{content}{body};;\&lt;br /&gt;
    $val = Encode::decode(&#039;utf-8&#039;, $val) unless Encode::is_utf8($val);;\&lt;br /&gt;
    $val =~ s/\n/ /g;; #replace newlines with spaces\&lt;br /&gt;
    \&lt;br /&gt;
    my $sender = $msg-&amp;gt;{sender};;\&lt;br /&gt;
    my $ts = strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;, localtime($msg-&amp;gt;{origin_server_ts} / 1000));;\&lt;br /&gt;
    my $new_entry = &amp;quot;$ts: $sender: $val&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    next if $seen_messages{$new_entry};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $inserted = 0;;\&lt;br /&gt;
    for (my $i = 0;; $i &amp;lt; @timestampArray;; $i++) {\&lt;br /&gt;
      my ($existing_ts) = $timestampArray[$i] =~ /^([^:]+):/;;\&lt;br /&gt;
      if ($ts lt $existing_ts) {\&lt;br /&gt;
        splice(@timestampArray, $i, 0, $new_entry);;\&lt;br /&gt;
        $inserted = 1;;\&lt;br /&gt;
        last;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
    push(@timestampArray, encode(&#039;utf-8&#039;, $new_entry)) unless $inserted;;\&lt;br /&gt;
    shift(@timestampArray) while @timestampArray &amp;gt; $length;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  return join(&amp;quot;\n&amp;quot;, @timestampArray);;\&lt;br /&gt;
},\&lt;br /&gt;
process_messages:messages:.* {\&lt;br /&gt;
  my $val = ReadingsVal($name, &#039;messages_list&#039;, &#039;&#039;);;\&lt;br /&gt;
  my $this = ReadingsVal($name, $reading, &#039;&#039;);;\&lt;br /&gt;
  my $latest_ts = $this;;\&lt;br /&gt;
  \&lt;br /&gt;
  foreach my $line (split(/\n/, $val)) {\&lt;br /&gt;
    if ($line =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}):\s*(.*)$/) {\&lt;br /&gt;
      my ($ts, $msg) = ($1, $2);;\&lt;br /&gt;
      \&lt;br /&gt;
      if ($ts gt $this) {\&lt;br /&gt;
        fhem(&amp;quot;trigger $name msg: $line&amp;quot;);;\&lt;br /&gt;
        $latest_ts = $ts if ($ts gt $latest_ts);;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  return $latest_ts;;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot verbose 3&lt;br /&gt;
attr MatrixBot widgetOverride longpollCmd:uzsuSelectRadio,startTimer,stopTimer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Matrix&amp;diff=40171</id>
		<title>Matrix</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Matrix&amp;diff=40171"/>
		<updated>2025-05-24T08:31:54Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Matrix mit FHEM nutzen, erste Version der Seite&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Allgemeines ==&lt;br /&gt;
&#039;&#039;&#039;Matrix&#039;&#039;&#039; ist ein offenes Kommunikationsprotokoll für dezentrale Echtzeitkommunikation. Es wird von zahlreichen Clients unterstützt, darunter &#039;&#039;&#039;Element&#039;&#039;&#039;, &#039;&#039;&#039;Element-X&#039;&#039;&#039;, &#039;&#039;&#039;SchildiChat Next&#039;&#039;&#039;, &#039;&#039;&#039;FluffyChat&#039;&#039;&#039; und viele weitere.&lt;br /&gt;
&lt;br /&gt;
Viele Open-Source-Projekte betreiben eigene Chaträume auf föderierten Matrix-Servern. Ein Beispiel dafür ist der &#039;&#039;&#039;Gadgetbridge-Chat&#039;&#039;&#039;, einer der größeren und aktiveren Räume im Matrix-Ökosystem.&lt;br /&gt;
&lt;br /&gt;
== Matrix und FHEM ==&lt;br /&gt;
Um mit &#039;&#039;&#039;FHEM&#039;&#039;&#039; Nachrichten über das Matrix-Protokoll zu senden und zu empfangen, stehen mehrere Möglichkeiten zur Verfügung. Eine einfache und pragmatische Lösung ist die Nutzung eines &#039;&#039;&#039;HTTPMOD-Devices&#039;&#039;&#039;. Damit lassen sich Nachrichten über die Matrix-API versenden oder empfangen.&lt;br /&gt;
&lt;br /&gt;
=== Matrix per HTTPMOD Device ===&lt;br /&gt;
Folgendes Device kann Matrix Nachrichten senden und empfangen, es hat wenige Abhängigkeiten und sollte in den meisten FHEM Installationen unkompliziert eingerichtet sein. Es wird die Datenübertragung von FHEM zum Server mit HTTPS verschlüsselt, E2EE wird nicht verwendet.&lt;br /&gt;
&lt;br /&gt;
==== Einrichtung ====&lt;br /&gt;
Es sind die Attribute MatrixServer und MatrixUser zu setzen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixServer Dein-Servername&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;attr MatrixBot MatrixUser FHEM-Matrix-Username&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dann richtet man einen nicht E2E verschlüsselten Raum ein in dem der Matrix-FHEM-Account eingeladen ist. Von dem Raum benötigt man die MatrixRoomID, die man mit Element in den Raumdetails finden kann (Room Settings --&amp;gt; Advanced --&amp;gt; Internal room ID).&lt;br /&gt;
&lt;br /&gt;
Das Passwort des Matrix-FHEM-Users setzt man mit dem Befehl:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot storeKeyValue MatrixPassword yourPassword123&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Verwendung ====&lt;br /&gt;
Nachrichten senden geht mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot sendText Bla Bla Bla&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Longpoll von Nachrichten starten und stoppen mit:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot longpollCmd startTimer&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;set MatrixBot longpollCmd stopTimer&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Weiteres ====&lt;br /&gt;
https://forum.fhem.de/index.php?topic=120834.msg1339487#msg1339487&lt;br /&gt;
&lt;br /&gt;
==== Device-Definition ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod MatrixBot HTTPMOD none 0&lt;br /&gt;
attr MatrixBot userattr MatrixRoomID MatrixServer MatrixUser&lt;br /&gt;
attr MatrixBot MatrixRoomID !12345678901234:nope.chat&lt;br /&gt;
attr MatrixBot MatrixServer nope.chat&lt;br /&gt;
attr MatrixBot MatrixUser DeinUsername&lt;br /&gt;
attr MatrixBot bodyDecode utf-8&lt;br /&gt;
attr MatrixBot bodyEncode utf-8&lt;br /&gt;
attr MatrixBot comment &amp;quot;\&lt;br /&gt;
Create a room for FHEM.\&lt;br /&gt;
\&lt;br /&gt;
The room must not use encryption, a room that has encryption\&lt;br /&gt;
enabled, cannot be converted to a non-encrypted room anymore \&lt;br /&gt;
\&lt;br /&gt;
The room-id can be found in Element-Web at:\&lt;br /&gt;
Room Settings --&amp;gt; Advanced --&amp;gt; Internal room ID\&lt;br /&gt;
\&lt;br /&gt;
To store the password in FHEM in obfuscated way:\&lt;br /&gt;
set MatrixBot storeKeyValue MatrixPassword yourPassword123\&lt;br /&gt;
\&lt;br /&gt;
###\&lt;br /&gt;
To send a text:\&lt;br /&gt;
set MatrixBot sendText Bla Bla Bla\&lt;br /&gt;
\&lt;br /&gt;
\&lt;br /&gt;
###\&lt;br /&gt;
To longpoll for messages once:\&lt;br /&gt;
# 1. send special filter to Matrix:\&lt;br /&gt;
set MatrixBot sendFilter\&lt;br /&gt;
\&lt;br /&gt;
# 2. start one longPoll (waits up to 60 seconds\&lt;br /&gt;
#                        or until data is available)\&lt;br /&gt;
get MatrixBot longpoll\&lt;br /&gt;
\&lt;br /&gt;
#alternatively, to keep on longPolling (this also sets filter):\&lt;br /&gt;
set MatrixBot longpollCmd startTimer\&lt;br /&gt;
#to stop the timers:\&lt;br /&gt;
set MatrixBot longpollCmd stopTimer\&lt;br /&gt;
\&lt;br /&gt;
#############################################################\&lt;br /&gt;
https://spec.matrix.org/v1.14/client-server-api/#syncing\&lt;br /&gt;
&amp;quot;&lt;br /&gt;
attr MatrixBot get02AlwaysNum 0&lt;br /&gt;
attr MatrixBot get02HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot get02Name longpoll&lt;br /&gt;
attr MatrixBot get02Regex \&amp;quot;next_batch\&amp;quot;:\s*\&amp;quot;(?&amp;lt;next_batch&amp;gt;[^\&amp;quot;]+)\&amp;quot;(?:.*?\&amp;quot;timeline\&amp;quot;:\s*{\s*\&amp;quot;events\&amp;quot;:\s*(?&amp;lt;messages&amp;gt;\[.*?\])\s*)?&lt;br /&gt;
attr MatrixBot get02TextArg 0&lt;br /&gt;
attr MatrixBot get02URL https://[$name:MatrixServer]/_matrix/client/v3/sync?timeout=60000&amp;amp;filter=[$name:filter_id]%%next_batch_param%%&lt;br /&gt;
attr MatrixBot icon message_info&lt;br /&gt;
attr MatrixBot parseFunction1 handleAuthErrors&lt;br /&gt;
attr MatrixBot reAuthAlways 0&lt;br /&gt;
attr MatrixBot reAuthRegex M_UNKNOWN_TOKEN&lt;br /&gt;
attr MatrixBot replacement01Mode expression&lt;br /&gt;
attr MatrixBot replacement01Regex \[([^:\s\[\&amp;quot;\&#039;]+):([^\]\s]+)\]&lt;br /&gt;
attr MatrixBot replacement01Value my $device = $name if ($1 eq &amp;quot;\$name&amp;quot;) // $1;;\&lt;br /&gt;
ReadingsVal($device, $2, undef) or AttrVal($device, $2, &amp;quot;???&amp;quot;);;&lt;br /&gt;
attr MatrixBot replacement02Mode expression&lt;br /&gt;
attr MatrixBot replacement02Regex %%uuid%%&lt;br /&gt;
attr MatrixBot replacement02Value join(&amp;quot;-&amp;quot;, unpack(&amp;quot;A8 A4 A4 A4 A12&amp;quot;, unpack(&amp;quot;H*&amp;quot;, join(&amp;quot;&amp;quot;, map { chr(int rand 256) } 0..15))))&lt;br /&gt;
attr MatrixBot replacement03Mode key&lt;br /&gt;
attr MatrixBot replacement03Regex %%MatrixPassword%%&lt;br /&gt;
attr MatrixBot replacement03Value MatrixPassword&lt;br /&gt;
attr MatrixBot replacement04Mode expression&lt;br /&gt;
attr MatrixBot replacement04Regex %%next_batch_param%%&lt;br /&gt;
attr MatrixBot replacement04Value #is there a reading &#039;next_batch&#039;?\&lt;br /&gt;
my $val = ReadingsVal($name, &#039;next_batch&#039;, &#039;???&#039;);;\&lt;br /&gt;
\&lt;br /&gt;
#return the GET parameter &#039;sync=value&#039; for /sync Endpoint\&lt;br /&gt;
return &amp;quot;&amp;amp;since=$val&amp;quot; if ($val ne &#039;???&#039;);;\&lt;br /&gt;
\&lt;br /&gt;
#return neither since-key nor value for it:\&lt;br /&gt;
return &amp;quot;&amp;quot;;;&lt;br /&gt;
attr MatrixBot set01Data {\&lt;br /&gt;
  &amp;quot;msgtype&amp;quot;: &amp;quot;m.text&amp;quot;,\&lt;br /&gt;
  &amp;quot;body&amp;quot;: &amp;quot;$val&amp;quot;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot set01HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot set01HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot set01IExpr #cancel an active longpoll\&lt;br /&gt;
if ($hash-&amp;gt;{BUSY}\&lt;br /&gt;
    &amp;amp;&amp;amp; $hash-&amp;gt;{HttpUtils}\&lt;br /&gt;
    &amp;amp;&amp;amp; $hash-&amp;gt;{HttpUtils}-&amp;gt;{url} =~ m|^https://[^/]+/_matrix/client/v3/sync\?timeout=\d+&amp;amp;filter=| ) {\&lt;br /&gt;
    Log(3, &amp;quot;$name: longpoll active, cutting it off now&amp;quot;);;\&lt;br /&gt;
    HttpUtils_Close($hash-&amp;gt;{HttpUtils});;\&lt;br /&gt;
}\&lt;br /&gt;
\&lt;br /&gt;
#just return $val unteraltered\&lt;br /&gt;
$val&lt;br /&gt;
attr MatrixBot set01Method POST&lt;br /&gt;
attr MatrixBot set01Name sendText&lt;br /&gt;
attr MatrixBot set01ParseResponse 1&lt;br /&gt;
attr MatrixBot set01Regex {\&amp;quot;event_id\&amp;quot;:\&amp;quot;(.*)\&amp;quot;}&lt;br /&gt;
attr MatrixBot set01TextArg 1&lt;br /&gt;
attr MatrixBot set01URL https://[$name:MatrixServer]/_matrix/client/v3/rooms/[$name:MatrixRoomID]/send/m.room.message?txnId=%%uuid%%&lt;br /&gt;
attr MatrixBot set02Data {\&lt;br /&gt;
  &amp;quot;room&amp;quot;: {\&lt;br /&gt;
    &amp;quot;rooms&amp;quot;: [&amp;quot;[$name:MatrixRoomID]&amp;quot;],\&lt;br /&gt;
    &amp;quot;timeline&amp;quot;: {\&lt;br /&gt;
      &amp;quot;limit&amp;quot;: 10,\&lt;br /&gt;
      &amp;quot;types&amp;quot;: [&amp;quot;m.room.message&amp;quot;]\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;include_leave&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_join&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_account_data&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;include_state&amp;quot;: false,\&lt;br /&gt;
    &amp;quot;state&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;ephemeral&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    },\&lt;br /&gt;
    &amp;quot;account_data&amp;quot;: {\&lt;br /&gt;
      &amp;quot;types&amp;quot;: []\&lt;br /&gt;
    }\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;event_fields&amp;quot;: [\&lt;br /&gt;
    &amp;quot;content.body&amp;quot;,\&lt;br /&gt;
    &amp;quot;sender&amp;quot;,\&lt;br /&gt;
    &amp;quot;origin_server_ts&amp;quot;\&lt;br /&gt;
  ],\&lt;br /&gt;
  &amp;quot;event_format&amp;quot;: &amp;quot;client&amp;quot;,\&lt;br /&gt;
  &amp;quot;presence&amp;quot;: {\&lt;br /&gt;
    &amp;quot;types&amp;quot;: [],\&lt;br /&gt;
    &amp;quot;not_types&amp;quot;: [&amp;quot;*&amp;quot;]\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;account_data&amp;quot;: {\&lt;br /&gt;
    &amp;quot;types&amp;quot;: [],\&lt;br /&gt;
    &amp;quot;not_types&amp;quot;: [&amp;quot;*&amp;quot;]\&lt;br /&gt;
  }\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot set02HeaderAuthorization Authorization: Bearer $sid&lt;br /&gt;
attr MatrixBot set02HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot set02Method POST&lt;br /&gt;
attr MatrixBot set02Name sendFilter&lt;br /&gt;
attr MatrixBot set02NoArg 1&lt;br /&gt;
attr MatrixBot set02ParseResponse 1&lt;br /&gt;
attr MatrixBot set02Regex \&amp;quot;filter_id\&amp;quot;:\s*\&amp;quot;(?&amp;lt;filter_id&amp;gt;\d+)\&amp;quot;&lt;br /&gt;
attr MatrixBot set02URL https://[$name:MatrixServer]/_matrix/client/v3/user/@[$name:MatrixUser]:[$name:MatrixServer]/filter&lt;br /&gt;
attr MatrixBot set03Local 1&lt;br /&gt;
attr MatrixBot set03Name longpollCmd&lt;br /&gt;
attr MatrixBot set03TextArg 1&lt;br /&gt;
attr MatrixBot showError 1&lt;br /&gt;
attr MatrixBot sid01Data {\&lt;br /&gt;
  &amp;quot;type&amp;quot;: &amp;quot;m.login.password&amp;quot;,\&lt;br /&gt;
  &amp;quot;identifier&amp;quot;: {\&lt;br /&gt;
    &amp;quot;type&amp;quot;: &amp;quot;m.id.user&amp;quot;,\&lt;br /&gt;
    &amp;quot;user&amp;quot;: &amp;quot;[$name:MatrixUser]&amp;quot;\&lt;br /&gt;
  },\&lt;br /&gt;
  &amp;quot;password&amp;quot;: &amp;quot;%%MatrixPassword%%&amp;quot;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot sid01HeaderContent-Type application/json&lt;br /&gt;
attr MatrixBot sid01IdRegex &amp;quot;access_token&amp;quot;\s*:\s*&amp;quot;([^&amp;quot;]+)&amp;quot;&lt;br /&gt;
attr MatrixBot sid01URL https://[$name:MatrixServer]/_matrix/client/v3/login&lt;br /&gt;
attr MatrixBot timeout 65&lt;br /&gt;
attr MatrixBot userReadings longpollTimer:(next_batch|longpollCmd|LAST_ERROR|sendText):.* {\&lt;br /&gt;
  my $longpollCmd = ReadingsVal($name, &#039;longpollCmd&#039;, &#039;???&#039;);;\&lt;br /&gt;
  my $delay = 1;;\&lt;br /&gt;
  my $timeout = AttrVal($name, &#039;timeout&#039;, 61) + $delay + 5;;\&lt;br /&gt;
  \&lt;br /&gt;
  # stop our timers:\&lt;br /&gt;
  if ($longpollCmd eq &amp;quot;stopTimer&amp;quot;) {\&lt;br /&gt;
    fhem(&amp;quot;cancel ${name}_longpollTimer quiet&amp;quot;);;\&lt;br /&gt;
    fhem(&amp;quot;cancel ${name}_longpollTimer2 quiet&amp;quot;);;\&lt;br /&gt;
    return &amp;quot;stopped&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  if ($longpollCmd ne &amp;quot;startTimer&amp;quot;) {\&lt;br /&gt;
    return &amp;quot;no timer set, longpollCmd is not set to &#039;startTimer&#039;&amp;quot;;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #if startTimer cmd was given now, set filter as well:\&lt;br /&gt;
  if (ReadingsAge($name, &#039;longpollCmd&#039;, 0) &amp;lt;= 1) {\&lt;br /&gt;
    $delay = 5;; #delay to allow for sendFilter to be answered\&lt;br /&gt;
    #Log(1, &amp;quot;🪲 $name: &amp;gt;&amp;gt;&amp;quot;. InternalVal($name, &#039;httpbody&#039;, &#039;???&#039;) .&amp;quot;&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
    fhem(&amp;quot;sleep 0.1 quiet;; set $name sendFilter&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #we handle an error reported by http-utils:\&lt;br /&gt;
  if (ReadingsAge($name, &#039;LAST_ERROR&#039;, 0) &amp;lt;= 1) {\&lt;br /&gt;
    my $last_error = ReadingsVal($name, &#039;LAST_ERROR&#039;, &#039;???&#039;);;\&lt;br /&gt;
    $delay = 10;; #delay to allow for error reasons to improve\&lt;br /&gt;
    #Log(1, &amp;quot;🪲 $name: Dealing with error: &amp;gt;&amp;gt;$last_error&amp;lt;&amp;lt;&amp;quot;);;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  # for testing this: { $defs{MatrixBot}{sid} = &#039;bla&#039; }\&lt;br /&gt;
  if (!defined &amp;amp;HTTPMOD::handleAuthErrors) {\&lt;br /&gt;
    *HTTPMOD::handleAuthErrors = sub {\&lt;br /&gt;
      my ($hash, $header, $body, $request) = @_;;\&lt;br /&gt;
      my $name = $hash-&amp;gt;{NAME};;\&lt;br /&gt;
      my $status;;\&lt;br /&gt;
      \&lt;br /&gt;
      if ($header =~ m{^HTTP/\d\.\d\s+(\d+)}m) {\&lt;br /&gt;
        $status = $1;;\&lt;br /&gt;
      }\&lt;br /&gt;
      \&lt;br /&gt;
      Log3($name, 4, &amp;quot;$name: HTTP status code is $status&amp;quot;);;\&lt;br /&gt;
      \&lt;br /&gt;
      if ( $status == 401 || $status == 403 || $status == 500 ) {\&lt;br /&gt;
        Log3($name, 3, &amp;quot;$name: auth-error or servererror ($status), calling doAuth()&amp;quot;);;\&lt;br /&gt;
        HTTPMOD::DoAuth($hash);;\&lt;br /&gt;
      }\&lt;br /&gt;
    };;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  #set timers, one regular and one fallback:\&lt;br /&gt;
  fhem(&amp;quot;sleep $delay ${name}_longpollTimer quiet;; get $name longpoll&amp;quot;);;\&lt;br /&gt;
  fhem(&amp;quot;sleep $timeout ${name}_longpollTimer2 quiet;; set $name longpollCmd startTimer&amp;quot;);;\&lt;br /&gt;
  \&lt;br /&gt;
  return strftime(&amp;quot;next longpoll at %H:%M:%S&amp;quot;, localtime( time()+$delay ));;\&lt;br /&gt;
},\&lt;br /&gt;
messages_list:messages:.* {\&lt;br /&gt;
  my $this = ReadingsVal($name, $reading, &#039;&#039;);;\&lt;br /&gt;
  my @timestampArray = split(&amp;quot;\n&amp;quot;, $this);;\&lt;br /&gt;
  my $messages_ref = decode_json(ReadingsVal($name, &#039;messages&#039;, &#039;&#039;));;\&lt;br /&gt;
  my $length = 20;;\&lt;br /&gt;
  \&lt;br /&gt;
  my %seen_messages = map { $_ =&amp;gt; 1 } @timestampArray;;\&lt;br /&gt;
  \&lt;br /&gt;
  foreach my $msg (@$messages_ref) {\&lt;br /&gt;
    my $val = $msg-&amp;gt;{content}{body};;\&lt;br /&gt;
    $val = Encode::decode(&#039;utf-8&#039;, $val) unless Encode::is_utf8($val);;\&lt;br /&gt;
    $val =~ s/\n/ /g;; #replace newlines with spaces\&lt;br /&gt;
    \&lt;br /&gt;
    my $sender = $msg-&amp;gt;{sender};;\&lt;br /&gt;
    my $ts = strftime(&amp;quot;%Y-%m-%d %H:%M:%S&amp;quot;, localtime($msg-&amp;gt;{origin_server_ts} / 1000));;\&lt;br /&gt;
    my $new_entry = &amp;quot;$ts: $sender: $val&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    next if $seen_messages{$new_entry};;\&lt;br /&gt;
    \&lt;br /&gt;
    my $inserted = 0;;\&lt;br /&gt;
    for (my $i = 0;; $i &amp;lt; @timestampArray;; $i++) {\&lt;br /&gt;
      my ($existing_ts) = $timestampArray[$i] =~ /^([^:]+):/;;\&lt;br /&gt;
      if ($ts lt $existing_ts) {\&lt;br /&gt;
        splice(@timestampArray, $i, 0, $new_entry);;\&lt;br /&gt;
        $inserted = 1;;\&lt;br /&gt;
        last;;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
    push(@timestampArray, encode(&#039;utf-8&#039;, $new_entry)) unless $inserted;;\&lt;br /&gt;
    shift(@timestampArray) while @timestampArray &amp;gt; $length;;\&lt;br /&gt;
  }\&lt;br /&gt;
  \&lt;br /&gt;
  return join(&amp;quot;\n&amp;quot;, @timestampArray);;\&lt;br /&gt;
},\&lt;br /&gt;
process_messages:messages:.* {\&lt;br /&gt;
  my $val = ReadingsVal($name, &#039;messages_list&#039;, &#039;&#039;);;\&lt;br /&gt;
  my $this = ReadingsVal($name, $reading, &#039;&#039;);;\&lt;br /&gt;
  my $latest_ts = $this;;\&lt;br /&gt;
  \&lt;br /&gt;
  foreach my $line (split(/\n/, $val)) {\&lt;br /&gt;
    if ($line =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}):\s*(.*)$/) {\&lt;br /&gt;
      my ($ts, $msg) = ($1, $2);;\&lt;br /&gt;
      \&lt;br /&gt;
      if ($ts gt $this) {\&lt;br /&gt;
        fhem(&amp;quot;trigger $name msg: $line&amp;quot;);;\&lt;br /&gt;
        $latest_ts = $ts if ($ts gt $latest_ts);;\&lt;br /&gt;
      }\&lt;br /&gt;
    }\&lt;br /&gt;
  }\&lt;br /&gt;
  return $latest_ts;;\&lt;br /&gt;
}&lt;br /&gt;
attr MatrixBot verbose 3&lt;br /&gt;
attr MatrixBot widgetOverride longpollCmd:uzsuSelectRadio,startTimer,stopTimer&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40170</id>
		<title>E-Mail senden</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40170"/>
		<updated>2025-05-24T08:10:25Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* Providerspezifisches */ Noch ein paar mehr Rückmeldungen eingefügt&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Um aus FHEM heraus E-Mails senden zu können, gibt es unterschiedliche Methoden, abhängig von der verwendeten Plattform.&lt;br /&gt;
&lt;br /&gt;
Lange Zeit war &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; sehr beliebt. Da aber die Homepage des Projekts nicht mehr online ist und Probleme nur noch via erhöhtem Supportaufwand gelöst werden, sollte diese Option als veraltet betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Eine Liste mit ressourcensparenden E-Mails Clients findet man bei dem OpenWRT Projekt: https://openwrt.org/docs/guide-user/services/email/smtp.client&lt;br /&gt;
&lt;br /&gt;
MSGMail ist ein FHEM eigenes Modul zum Versenden, die Einrichtung ist allerdings nicht gänzlich einfach. Anfängern sei die cURL Methode empfohlen.&lt;br /&gt;
&lt;br /&gt;
=== MSGMail (Linux, Windows) ===&lt;br /&gt;
FHEM bringt ein eigenes Device zum Senden von E-Mails mit, welches in Perl implementiert ist: [https://fhem.de/commandref.html#MSGMail MSGMail, https://fhem.de/commandref.html#MSGMail]&lt;br /&gt;
&lt;br /&gt;
=== cURL (Linux, Windows) ===&lt;br /&gt;
[https://curl.se/ cURL] kann auf praktisch [https://curl.se/download.html jedem Betriebssystem] genutzt werden, [https://curl.se/windows/ selbst unter Windows]. Es kann direkt mit einem SMTP Server E-Mails versenden und stellt damit eine moderne, portable und sichere Option zum Versenden von E-Mails dar.&lt;br /&gt;
&lt;br /&gt;
In FHEM richtet man sich ein Device ein und passt die Details (&amp;lt;code&amp;gt;$emailTo, $emailFrom, $emailServer&amp;lt;/code&amp;gt;) an. Das Programm cURL muss installiert sein (z.B. mit &amp;lt;code&amp;gt;apt install curl&amp;lt;/code&amp;gt;, oder durch Herunterladen und Installieren von der [https://curl.se cURL-Webseite]).&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod sendMail dummy&lt;br /&gt;
attr sendMail readingList message password&lt;br /&gt;
attr sendMail setList message:textField-long password&lt;br /&gt;
attr sendMail userReadings state:message:[\s\S]* {\&lt;br /&gt;
    my $emailTo   = &#039;du@example.com&#039;;;\&lt;br /&gt;
    my $emailFrom = &#039;fhem@example.de&#039;;;\&lt;br /&gt;
    my $emailServer = &#039;mail.your-server.de&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #erzeuge Datumsangabe konform zu RFC-5322:\&lt;br /&gt;
    my @emailDateArray = split(&#039; &#039;, strftime(&amp;quot;%w %d %m %Y %H:%M:%S %z&amp;quot;, localtime));;\&lt;br /&gt;
    $emailDateArray[0] = (qw[Sun Mon Tue Wed Thu Fri Sat])[$emailDateArray[0]] . &#039;,&#039;;;\&lt;br /&gt;
    $emailDateArray[2] = (qw[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec])[$emailDateArray[2]-1];;\&lt;br /&gt;
    my $emailDate = join(&#039; &#039;, @emailDateArray);;\&lt;br /&gt;
    \&lt;br /&gt;
    # Passwort aus getKeyValue abrufen\&lt;br /&gt;
    my ($err, $emailPass) = getKeyValue(&amp;quot;${name}_password&amp;quot;);;\&lt;br /&gt;
    if ($err || !defined $emailPass) {\&lt;br /&gt;
        return &amp;quot;Error retrieving password: $err&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    my $message = ReadingsVal($name, &#039;message&#039;, &#039;???&#039;);;\&lt;br /&gt;
    my $subject = &amp;quot;$name: FHEM Nachricht&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # Betreff extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
    if ($message =~ s/(?:Subject|Betreff)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        $subject = $1;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    # Empfänger extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
    if ($message =~ s/(?:To|An)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        $emailTo = $1;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #Dateianhänge bestimmen\&lt;br /&gt;
    my @attachments;;\&lt;br /&gt;
    while ($message =~ s/(?:Attachment|Anhang)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        my $file = $1;;\&lt;br /&gt;
        return &amp;quot;&amp;gt;&amp;gt;$file&amp;lt;&amp;lt; not found, not sending email&amp;quot; unless (-e $file);;\&lt;br /&gt;
        push (@attachments, $file);;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #der curl Befehl wird hier aufgebaut:\&lt;br /&gt;
    my $cmd = &amp;quot;curl -m 19 --noproxy &#039;*&#039; --no-progress-meter --ssl-reqd smtps://$emailServer:465 &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;--user &#039;$emailFrom:$emailPass&#039; --mail-from &#039;$emailFrom&#039; --mail-rcpt &#039;$emailTo&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;--write-out &#039;Email sent with status %{http_code}&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;Subject: $subject&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;From: $emailFrom&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;Date: $emailDate&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #multipart/alternative für Text und HTML EMail Body\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;=(;;type=multipart/alternative&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Anfang entfernen:\&lt;br /&gt;
    $message =~ s/^(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+//;;\&lt;br /&gt;
    # Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Ende entfernen:\&lt;br /&gt;
    $message =~ s/(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+$//;;\&lt;br /&gt;
    \&lt;br /&gt;
    #Plaintext E-Mail Inhalt an cURL:\&lt;br /&gt;
    $cmd .= &amp;quot;-F \&amp;quot;=$message;;type=text/plain;; charset=UTF-8\&amp;quot; &amp;quot;;;\&lt;br /&gt;
    #ersetze \n bzw. \r\n durch ein HTML-tag &amp;lt;br /&amp;gt;:\&lt;br /&gt;
    $message =~ s/\r?\n/&amp;lt;br \/&amp;gt;/g;;\&lt;br /&gt;
    \&lt;br /&gt;
    #HTML E-Mail Inhalt an cURL:\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;= &amp;lt;body&amp;gt;$message&amp;lt;/body&amp;gt;;;type=text/html;; charset=UTF-8&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;=)&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #jetzt alle Attachments (0..N) anhängen:\&lt;br /&gt;
    foreach my $file (@attachments) {\&lt;br /&gt;
        $cmd .= &amp;quot;-F &#039;=@&amp;quot;.$file.&amp;quot;;;encoder=base64&#039; &amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #stderr auch mit in den output sammeln:\&lt;br /&gt;
    $cmd .= &amp;quot; 2&amp;gt;&amp;amp;1&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc1) {\&lt;br /&gt;
        *sendMailfunc1 = sub ($) {\&lt;br /&gt;
            my ($param) = @_;;\&lt;br /&gt;
            my $result;;\&lt;br /&gt;
            $result = qx/$param/;;\&lt;br /&gt;
            $result = MIME::Base64::encode_base64($result);;\&lt;br /&gt;
            $result =~ s/\n//g;;\&lt;br /&gt;
            return $result;;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #Rückgabe über diese Funktion,\&lt;br /&gt;
    #anzeigen in dem reading dieses UserReadings:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc2) {\&lt;br /&gt;
        *sendMailfunc2 = sub ($) {\&lt;br /&gt;
            my ($result) = @_;;\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            $result = MIME::Base64::decode_base64($result);;\&lt;br /&gt;
            readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #funktion falls BlockingCall abgebrochen wird:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc3) {\&lt;br /&gt;
        *sendMailfunc3 = sub ($) {\&lt;br /&gt;
            my ($result) = @_;;\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    BlockingCall(&amp;quot;sendMailfunc1&amp;quot;, $cmd, &amp;quot;sendMailfunc2&amp;quot;, 20, &amp;quot;sendMailfunc3&amp;quot;, &amp;quot;Error: timeout&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return &amp;quot;started cURL command...&amp;quot;;;\&lt;br /&gt;
},\&lt;br /&gt;
state:password:.* {\&lt;br /&gt;
    # Passwort speichern\&lt;br /&gt;
    my $ret = setKeyValue(&amp;quot;${name}_password&amp;quot;, ReadingsVal($name, &#039;password&#039;, undef)) // &amp;quot;password stored&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\&lt;br /&gt;
    #\&lt;br /&gt;
    # Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\&lt;br /&gt;
    # Alternativ: { setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;password&amp;quot;, &amp;quot;****&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return &amp;quot;$ret&amp;quot;;;\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Das Passwort setzt man mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail password geheimesPasswort&amp;lt;/code&amp;gt;&lt;br /&gt;
* Alternativ, damit das Passwort nicht im Log auftaucht: &amp;lt;code&amp;gt;{ setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
E-Mails kann man dann Senden mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Mein Text&amp;lt;/code&amp;gt; (es wird der Standardbetreff und Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;(es wird der Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Subject=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;✅ Dies ist ein Test&amp;quot; Meldung mit Emoji ⚽ geht auch&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eine Datei mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/bag.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zwei Dateien mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;./www/images/fhemSVG/bag.svg&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/batterie.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;, für die anzuhängenden Dateien können relative und absolute Pfade verwendet werden. Beim Vorbereiten der E-Mail wird geprüft ob die Dateien existieren, da dies sonst eine typische Fehlerquelle ist. Es wird nichts versendet wenn eine Datei nicht gefunden werden kann.&lt;br /&gt;
&lt;br /&gt;
==== Fehlersuche ====&lt;br /&gt;
&lt;br /&gt;
===== Fehlende Protokolloption in cURL =====&lt;br /&gt;
Sollte cURL zwar installiert sein, aber keine E-Mails versenden, kann es sein, dass das Protokoll nicht einkompiliert wurde. Dies ist bei OpenWRT zum Beispiel der Fall.&lt;br /&gt;
&lt;br /&gt;
So sollte es aussehen (hier ein Ubuntu), relevant ist für diese Nutzung das Protokoll smtp und smtps, das braucht man:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.7&lt;br /&gt;
Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.6&lt;br /&gt;
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp&lt;br /&gt;
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So sollte es nicht aussehen (hier ein OpenWRT):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl: (1) Protocol &amp;quot;smtps&amp;quot; not supported&lt;br /&gt;
&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.10.1 (arm-openwrt-linux-gnu) libcurl/8.10.1 mbedTLS/3.6.2 nghttp2/1.63.0&lt;br /&gt;
Release-Date: 2024-09-18&lt;br /&gt;
Protocols: file ftp ftps http https ipfs ipns mqtt&lt;br /&gt;
Features: alt-svc HSTS HTTP2 HTTPS-proxy IPv6 Largefile SSL threadsafe UnixSockets&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Hat man nur ein cURL ohne das Protokoll, so muss man sich das Paket leider anderweitig beschaffen (chroot, selbst kompilieren, statisch gelinkte Version, ...).&lt;br /&gt;
&lt;br /&gt;
===== Falscher Port, login options =====&lt;br /&gt;
Einige Provider nehmen ihre TLS verschlüsselten E-Mails über einen anderen Port an, häufig Port 587 (siehe auch {{Link2Forum|Topic= 140814|Message=1340366|LinkText=diesen Forenbeitrag}}). Ggf. muss man auch mit einigen Loginoptionen experimentieren bis cURL  wie gewünscht funktioniert (Stichwort: &#039;&#039;[https://curl.se/docs/manpage.html#--login-options --login-options]&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
===== Alte cURL Version =====&lt;br /&gt;
Sollte eine sehr alte cURL Version kleiner 7.67.0 genutzt werden, fehlt der Schalter &amp;lt;code&amp;gt;--no-progress-meter.&amp;lt;/code&amp;gt; In solchen Fällen sollte man cURL aktualisieren. Falls dies nicht möglich ist, kann man den Schalter ersetzen durch &amp;lt;code&amp;gt;--silent --show-error&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Providerspezifisches=====&lt;br /&gt;
Eine lose Liste mit providerspezifischen Erfolgen und Misserfolgen:&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|+Liste mit Rückmeldungen zu E-Mail Anbietern&lt;br /&gt;
!Provider&lt;br /&gt;
!Funktioniert&lt;br /&gt;
!Details&lt;br /&gt;
|-&lt;br /&gt;
|Hetzner&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;mail.your-server.de&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailServer:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Vodafone&lt;br /&gt;
|Ja&lt;br /&gt;
|Folgende Schalter sind hilfreich:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
--ssl-reqd --sasl-ir smtps://$emailserver:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Freenet&lt;br /&gt;
|Nein (nicht zuverlässig aufgrund des Spamfilters)&lt;br /&gt;
|Der Spamfilter lässt teilweise Nachrichten passieren, teilweise wird die Verbindung abgelehnt:&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;&#039;550 Spam message rejected&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
https://forum.fhem.de/index.php?topic=140814.msg1341872#msg1341872&lt;br /&gt;
|-&lt;br /&gt;
|T-Online&lt;br /&gt;
|Ja?&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;securesmtp.t-online.de&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailServer:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;https://forum.fhem.de/index.php?topic=140814.msg1340258#msg1340258&lt;br /&gt;
|-&lt;br /&gt;
|GMX&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;mail.gmx.net&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailserver:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;https://forum.fhem.de/index.php?topic=140814.msg1341891#msg1341891&lt;br /&gt;
|-&lt;br /&gt;
|Google -&amp;gt; GMail&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
my $emailServer = &#039;smtp.gmail.com&#039;;&lt;br /&gt;
...&lt;br /&gt;
smtps://$emailserver:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;https://forum.fhem.de/index.php?topic=140814.msg1341891#msg1341891&lt;br /&gt;
|-&lt;br /&gt;
|Strato&lt;br /&gt;
|Ja&lt;br /&gt;
|https://forum.fhem.de/index.php?topic=140814.msg1342201#msg1342201&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Siehe auch dazu den {{Link2Forum|Message=1341182|Topic=140814|LinkText=Forumspost}}&lt;br /&gt;
===EXIM4 (Debian)===&lt;br /&gt;
Eine Anleitung zum Versenden von E-Mails mit Exim ist als PDF verfügbar: [[Medium:Anleitung Exim4 Debian GMX.pdf|PDF]]&lt;br /&gt;
&lt;br /&gt;
===SSMTP (OpenWRT, embedded Distros)===&lt;br /&gt;
&lt;br /&gt;
====Installation====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install ssmtp_2.64-4_mpc85xx.ipk  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Damit &amp;lt;code&amp;gt;ssmtp&amp;lt;/code&amp;gt; funktioniert müssen die Dateien &amp;lt;code&amp;gt;/etc/ssmtp/ssmtp.conf&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;/etc/ssmtp/revaliases&amp;lt;/code&amp;gt; angepasst werden.&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/ssmtp.conf &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root=arnold@gmx.net&lt;br /&gt;
 mailhub=mail.gmx.net:465&lt;br /&gt;
 rewriteDomain=gmx.net&lt;br /&gt;
 hostname=gmx.net&lt;br /&gt;
 FromLineOverride=YES&lt;br /&gt;
 UseTLS=YES&lt;br /&gt;
 #UseSTARTTLS=YES&lt;br /&gt;
 AuthUser=arnold@gmx.net&lt;br /&gt;
 AuthPass=Passwort_von_arnold@gmx.net&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/revaliases&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root:arnold@gmx.net:mail.gmx.net:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der [[99_myUtils_anlegen|99_myUtils]] folgende Unterroutine einfügen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub OpenWRTMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $sender = &amp;quot;dockstar\@heye-tammo.de&amp;quot;; &lt;br /&gt;
 Log 1, &amp;quot;sendEmail RCP: $rcpt, Subject: $subject, Text: $text&amp;quot;;&lt;br /&gt;
 $ret .= qx(echo -e &#039;to:$rcpt\n from:$sender\nsubject:$subject\n$text\n&#039; | ssmtp $rcpt);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendEmail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===PHP Mail Funktion (Synology DiskStation)===&lt;br /&gt;
Beim DSM 3.2 funktioniert der &amp;lt;code&amp;gt;php-mail&amp;lt;/code&amp;gt;-Befehl, so dass man mittels folgendem Modul E-Mails versenden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub sendmail($$$) {&lt;br /&gt;
 my($empf, $subj, $nachricht) = @_;&lt;br /&gt;
 system(&amp;quot;php -r &#039;mail(\&amp;quot;$empf\&amp;quot;,\&amp;quot;$subj\&amp;quot;,\&amp;quot;$nachricht\&amp;quot;);&#039;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sendemail (Veraltet, Debian-basierende Distros)===&lt;br /&gt;
Es handelt sich um ein Perl Programm, dass man aus den Paketquellen installieren kann.&lt;br /&gt;
&lt;br /&gt;
Es gibt Probleme: &lt;br /&gt;
&lt;br /&gt;
*mit der TLS bzw STARTTLS Verschlüsselung (https://github.com/fhem/fhem-docker/issues/254, https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1915977.html) ,&lt;br /&gt;
*Mit der Webseite des Projekts (https://web.archive.org/web/20190906140125/http://caspian.dotconf.net/menu/Software/SendEmail); diese ist nicht mehr verfügbar und nur eine archivierte Kopie kann eingesehen werden,&lt;br /&gt;
&lt;br /&gt;
weswegen diese Lösung als technisch überholt gelten sollte. Will man trotzdem &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; verwenden folgenden hier die Informationen dazu:&lt;br /&gt;
&lt;br /&gt;
Auf dem Server muss &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; installiert werden:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt-get update&lt;br /&gt;
sudo apt-get install sendemail libio-socket-ssl-perl libnet-ssleay-perl perl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Man kann die korrekte Funktion im Terminal (ohne &amp;lt;code&amp;gt;{qx()}&amp;lt;/code&amp;gt; ) oder auch in der FHEM Kommandozeile testen (Hinweis: manche Provider (z.B. gmail) lassen diesen Zugang erst nach Freischaltung zu!).&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Bei Gmail muss seit dem 30.6.2022 ein separates Passwort erzeugt werden, die alte Einstellung über &amp;quot;Zugriff durch weniger sichere Apps&amp;quot; funktioniert nicht mehr. Siehe Support Seite Google: &amp;lt;nowiki&amp;gt;https://support.google.com/accounts/answer/185833&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Beispiel mit Gmail über Linux Terminalfenster:&#039;&#039;&lt;br /&gt;
 sendemail -f &#039;Der.Absender@gmail.com&#039; -t &#039;Der.Empfaenger@gmail.com&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt; -s &#039;smtp.gmail.com:587&#039; -xu &#039;Der.Absender&#039; -xp DASSUPERGEHEIMEPASSW0RT&lt;br /&gt;
In der [[99 myUtils anlegen|99_myUtils]] folgende Unterroutine einfügen:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
######## DebianMail  Mail auf dem RPi versenden ############ &lt;br /&gt;
# $provider für SMTP Server anpassen. &lt;br /&gt;
# Einmal in der FHEM Kommandozeile, user und password anpassen:&lt;br /&gt;
# {setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&lt;br /&gt;
sub DebianMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $attach = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $error;&lt;br /&gt;
 my $konto = getKeyValue(&amp;quot;myEmailKonto&amp;quot;); &lt;br /&gt;
 my $passwrd = getKeyValue(&amp;quot;myEmailPasswrd&amp;quot;); &lt;br /&gt;
 my $from = $konto; # or use different KeyValue if konto is not the from email address&lt;br /&gt;
 my $provider = &amp;quot;smtp.1und1.de&amp;quot;; # smtp.domain.tld:port see provider documentation&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail RCP: $rcpt&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Subject: $subject&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Text: $text&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Anhang: $attach&amp;quot;;&lt;br /&gt;
 if (not defined($attach)){$attach=&#039;&#039;}&lt;br /&gt;
 $ret .= qx(sendemail -f &#039;$from&#039; -t &#039;$rcpt&#039; -u &#039;$subject&#039; -m &#039;$text&#039; -a $attach -s &#039;$provider&#039; -xu &#039;$konto&#039; -xp &#039;$passwrd&#039; -o tls=auto -o message-charset=utf-8);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendemail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
###############################################################################&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Die Zugangsdaten für den Email-Account müssen dabei FHEM-intern gespeichert werden. Dazu gibt man einmalig über die Kommandozeile folgendes ein (user@domain und password müssen auf den Email Account geändert werden):&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;{setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die TLS Verschlüsselung (ehem. SSL) nicht zu nutzen, muss in der viertletzten Zeile tls=no verwendet und der Port des Mailproviders auf 25 eigetragen werden. Sollte anschließend keine Mail verschickt werden, siehe Probleme.&lt;br /&gt;
&lt;br /&gt;
Falls der Body-Text in einem (Android-)Mailer auf dem Handy nicht gezeigt wird, kann der Parameter &#039;&#039;&#039;-o message-content-type=html&#039;&#039;&#039; helfen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion kann beispielsweise folgendermaßen aufgerufen werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{qx(sendemail -f &#039;Ab\@send.er&#039; -t &#039;Emp\@faeng.er&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &#039;DateinameOderLeer&#039; -s &#039;smtpFQDN:port&#039; -xu &#039;MailUser&#039; -xp &#039;PasswortOhneKlammeraffen&#039;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bei allen Feldern können generell doppelte &amp;quot; oder einfache &#039; Anführungszeichen verwendet werden. Innerhalb von &amp;quot;&amp;quot; müssen Sonderzeichen wie @ aber maskiert werden, da sie sonst als Steuerzeichen interpretiert werden:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;quot;email\@email.domain&amp;quot; oder &#039;email@email.domain&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direkt in der FHEM Kommandozeile (ohne Anhang)&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;{DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ordnungsgemäße Ausführung wird mit 71 quittiert.&lt;br /&gt;
&lt;br /&gt;
Beispiele mehrere Anhänge (Es ist wichtig Dateinamen mit Leerzeichen in separate Anführungszeichen zu setzen)&lt;br /&gt;
 {DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;Anhang1 Anhang2 Anhang3&amp;quot;)}&lt;br /&gt;
 {DebianMail(&#039;email@email.domain&#039;,&#039;Subject&#039;,&#039;Text&#039;,&amp;quot;&#039;with spaces Anhang1&#039; &#039;with spaces Anhang2&#039;&amp;quot;)}&lt;br /&gt;
oder (mehr als einen Empfänger, diese können durch Leerzeichen, Semikolon oder Komma getrennt sein)&lt;br /&gt;
 define Sonstiges at *01:00:00 {\&lt;br /&gt;
 DebianMail(&#039;email@email.domain,email2@email.domain&#039;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;&#039;Anhang1&#039; &#039;Anhang2&#039; ...&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Die archivierte Doku zu &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; befindet sich in der Änderungshistorie: https://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;amp;oldid=37524&lt;br /&gt;
[[Kategorie:E-Mail]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40168</id>
		<title>E-Mail senden</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40168"/>
		<updated>2025-05-23T05:43:08Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Hinweise von @bertl zu GMX und GMail&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Um aus FHEM heraus E-Mails senden zu können, gibt es unterschiedliche Methoden, abhängig von der verwendeten Plattform.&lt;br /&gt;
&lt;br /&gt;
Lange Zeit war &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; sehr beliebt. Da aber die Homepage des Projekts nicht mehr online ist und Probleme nur noch via erhöhtem Supportaufwand gelöst werden, sollte diese Option als veraltet betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Eine Liste mit ressourcensparenden E-Mails Clients findet man bei dem OpenWRT Projekt: https://openwrt.org/docs/guide-user/services/email/smtp.client&lt;br /&gt;
&lt;br /&gt;
MSGMail ist ein FHEM eigenes Modul zum Versenden, die Einrichtung ist allerdings nicht gänzlich einfach. Anfängern sei die cURL Methode empfohlen.&lt;br /&gt;
&lt;br /&gt;
=== MSGMail (Linux, Windows) ===&lt;br /&gt;
FHEM bringt ein eigenes Device zum Senden von E-Mails mit, welches in Perl implementiert ist: [https://fhem.de/commandref.html#MSGMail MSGMail, https://fhem.de/commandref.html#MSGMail]&lt;br /&gt;
&lt;br /&gt;
=== cURL (Linux, Windows) ===&lt;br /&gt;
[https://curl.se/ cURL] kann auf praktisch [https://curl.se/download.html jedem Betriebssystem] genutzt werden, [https://curl.se/windows/ selbst unter Windows]. Es kann direkt mit einem SMTP Server E-Mails versenden und stellt damit eine moderne, portable und sichere Option zum Versenden von E-Mails dar.&lt;br /&gt;
&lt;br /&gt;
In FHEM richtet man sich ein Device ein und passt die Details (&amp;lt;code&amp;gt;$emailTo, $emailFrom, $emailServer&amp;lt;/code&amp;gt;) an. Das Programm cURL muss installiert sein (z.B. mit &amp;lt;code&amp;gt;apt install curl&amp;lt;/code&amp;gt;, oder durch Herunterladen und Installieren von der [https://curl.se cURL-Webseite]).&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod sendMail dummy&lt;br /&gt;
attr sendMail readingList message password&lt;br /&gt;
attr sendMail setList message:textField-long password&lt;br /&gt;
attr sendMail userReadings state:message:[\s\S]* {\&lt;br /&gt;
    my $emailTo   = &#039;du@example.com&#039;;;\&lt;br /&gt;
    my $emailFrom = &#039;fhem@example.de&#039;;;\&lt;br /&gt;
    my $emailServer = &#039;mail.your-server.de&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #erzeuge Datumsangabe konform zu RFC-5322:\&lt;br /&gt;
    my @emailDateArray = split(&#039; &#039;, strftime(&amp;quot;%w %d %m %Y %H:%M:%S %z&amp;quot;, localtime));;\&lt;br /&gt;
    $emailDateArray[0] = (qw[Sun Mon Tue Wed Thu Fri Sat])[$emailDateArray[0]] . &#039;,&#039;;;\&lt;br /&gt;
    $emailDateArray[2] = (qw[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec])[$emailDateArray[2]-1];;\&lt;br /&gt;
    my $emailDate = join(&#039; &#039;, @emailDateArray);;\&lt;br /&gt;
    \&lt;br /&gt;
    # Passwort aus getKeyValue abrufen\&lt;br /&gt;
    my ($err, $emailPass) = getKeyValue(&amp;quot;${name}_password&amp;quot;);;\&lt;br /&gt;
    if ($err || !defined $emailPass) {\&lt;br /&gt;
        return &amp;quot;Error retrieving password: $err&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    my $message = ReadingsVal($name, &#039;message&#039;, &#039;???&#039;);;\&lt;br /&gt;
    my $subject = &amp;quot;$name: FHEM Nachricht&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # Betreff extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
    if ($message =~ s/(?:Subject|Betreff)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        $subject = $1;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    # Empfänger extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
    if ($message =~ s/(?:To|An)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        $emailTo = $1;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #Dateianhänge bestimmen\&lt;br /&gt;
    my @attachments;;\&lt;br /&gt;
    while ($message =~ s/(?:Attachment|Anhang)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        my $file = $1;;\&lt;br /&gt;
        return &amp;quot;&amp;gt;&amp;gt;$file&amp;lt;&amp;lt; not found, not sending email&amp;quot; unless (-e $file);;\&lt;br /&gt;
        push (@attachments, $file);;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #der curl Befehl wird hier aufgebaut:\&lt;br /&gt;
    my $cmd = &amp;quot;curl -m 19 --noproxy &#039;*&#039; --no-progress-meter --ssl-reqd smtps://$emailServer:465 &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;--user &#039;$emailFrom:$emailPass&#039; --mail-from &#039;$emailFrom&#039; --mail-rcpt &#039;$emailTo&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;--write-out &#039;Email sent with status %{http_code}&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;Subject: $subject&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;From: $emailFrom&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;Date: $emailDate&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #multipart/alternative für Text und HTML EMail Body\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;=(;;type=multipart/alternative&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Anfang entfernen:\&lt;br /&gt;
    $message =~ s/^(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+//;;\&lt;br /&gt;
    # Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Ende entfernen:\&lt;br /&gt;
    $message =~ s/(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+$//;;\&lt;br /&gt;
    \&lt;br /&gt;
    #Plaintext E-Mail Inhalt an cURL:\&lt;br /&gt;
    $cmd .= &amp;quot;-F \&amp;quot;=$message;;type=text/plain;; charset=UTF-8\&amp;quot; &amp;quot;;;\&lt;br /&gt;
    #ersetze \n bzw. \r\n durch ein HTML-tag &amp;lt;br /&amp;gt;:\&lt;br /&gt;
    $message =~ s/\r?\n/&amp;lt;br \/&amp;gt;/g;;\&lt;br /&gt;
    \&lt;br /&gt;
    #HTML E-Mail Inhalt an cURL:\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;= &amp;lt;body&amp;gt;$message&amp;lt;/body&amp;gt;;;type=text/html;; charset=UTF-8&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;=)&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #jetzt alle Attachments (0..N) anhängen:\&lt;br /&gt;
    foreach my $file (@attachments) {\&lt;br /&gt;
        $cmd .= &amp;quot;-F &#039;=@&amp;quot;.$file.&amp;quot;;;encoder=base64&#039; &amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #stderr auch mit in den output sammeln:\&lt;br /&gt;
    $cmd .= &amp;quot; 2&amp;gt;&amp;amp;1&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc1) {\&lt;br /&gt;
        *sendMailfunc1 = sub ($) {\&lt;br /&gt;
            my ($param) = @_;;\&lt;br /&gt;
            my $result;;\&lt;br /&gt;
            $result = qx/$param/;;\&lt;br /&gt;
            $result = MIME::Base64::encode_base64($result);;\&lt;br /&gt;
            $result =~ s/\n//g;;\&lt;br /&gt;
            return $result;;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #Rückgabe über diese Funktion,\&lt;br /&gt;
    #anzeigen in dem reading dieses UserReadings:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc2) {\&lt;br /&gt;
        *sendMailfunc2 = sub ($) {\&lt;br /&gt;
            my ($result) = @_;;\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            $result = MIME::Base64::decode_base64($result);;\&lt;br /&gt;
            readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #funktion falls BlockingCall abgebrochen wird:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc3) {\&lt;br /&gt;
        *sendMailfunc3 = sub ($) {\&lt;br /&gt;
            my ($result) = @_;;\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    BlockingCall(&amp;quot;sendMailfunc1&amp;quot;, $cmd, &amp;quot;sendMailfunc2&amp;quot;, 20, &amp;quot;sendMailfunc3&amp;quot;, &amp;quot;Error: timeout&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return &amp;quot;started cURL command...&amp;quot;;;\&lt;br /&gt;
},\&lt;br /&gt;
state:password:.* {\&lt;br /&gt;
    # Passwort speichern\&lt;br /&gt;
    my $ret = setKeyValue(&amp;quot;${name}_password&amp;quot;, ReadingsVal($name, &#039;password&#039;, undef)) // &amp;quot;password stored&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\&lt;br /&gt;
    #\&lt;br /&gt;
    # Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\&lt;br /&gt;
    # Alternativ: { setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;password&amp;quot;, &amp;quot;****&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return &amp;quot;$ret&amp;quot;;;\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Das Passwort setzt man mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail password geheimesPasswort&amp;lt;/code&amp;gt;&lt;br /&gt;
* Alternativ, damit das Passwort nicht im Log auftaucht: &amp;lt;code&amp;gt;{ setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
E-Mails kann man dann Senden mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Mein Text&amp;lt;/code&amp;gt; (es wird der Standardbetreff und Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;(es wird der Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Subject=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;✅ Dies ist ein Test&amp;quot; Meldung mit Emoji ⚽ geht auch&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eine Datei mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/bag.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zwei Dateien mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;./www/images/fhemSVG/bag.svg&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/batterie.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;, für die anzuhängenden Dateien können relative und absolute Pfade verwendet werden. Beim Vorbereiten der E-Mail wird geprüft ob die Dateien existieren, da dies sonst eine typische Fehlerquelle ist. Es wird nichts versendet wenn eine Datei nicht gefunden werden kann.&lt;br /&gt;
&lt;br /&gt;
==== Fehlersuche ====&lt;br /&gt;
&lt;br /&gt;
===== Fehlende Protokolloption in cURL =====&lt;br /&gt;
Sollte cURL zwar installiert sein, aber keine E-Mails versenden, kann es sein, dass das Protokoll nicht einkompiliert wurde. Dies ist bei OpenWRT zum Beispiel der Fall.&lt;br /&gt;
&lt;br /&gt;
So sollte es aussehen (hier ein Ubuntu), relevant ist für diese Nutzung das Protokoll smtp und smtps, das braucht man:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.7&lt;br /&gt;
Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.6&lt;br /&gt;
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp&lt;br /&gt;
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So sollte es nicht aussehen (hier ein OpenWRT):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl: (1) Protocol &amp;quot;smtps&amp;quot; not supported&lt;br /&gt;
&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.10.1 (arm-openwrt-linux-gnu) libcurl/8.10.1 mbedTLS/3.6.2 nghttp2/1.63.0&lt;br /&gt;
Release-Date: 2024-09-18&lt;br /&gt;
Protocols: file ftp ftps http https ipfs ipns mqtt&lt;br /&gt;
Features: alt-svc HSTS HTTP2 HTTPS-proxy IPv6 Largefile SSL threadsafe UnixSockets&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Hat man nur ein cURL ohne das Protokoll, so muss man sich das Paket leider anderweitig beschaffen (chroot, selbst kompilieren, statisch gelinkte Version, ...).&lt;br /&gt;
&lt;br /&gt;
===== Falscher Port, login options =====&lt;br /&gt;
Einige Provider nehmen ihre TLS verschlüsselten E-Mails über einen anderen Port an, häufig Port 587 (siehe auch {{Link2Forum|Topic= 140814|Message=1340366|LinkText=diesen Forenbeitrag}}). Ggf. muss man auch mit einigen Loginoptionen experimentieren bis cURL  wie gewünscht funktioniert (Stichwort: &#039;&#039;[https://curl.se/docs/manpage.html#--login-options --login-options]&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
===== Alte cURL Version =====&lt;br /&gt;
Sollte eine sehr alte cURL Version kleiner 7.67.0 genutzt werden, fehlt der Schalter &amp;lt;code&amp;gt;--no-progress-meter.&amp;lt;/code&amp;gt; In solchen Fällen sollte man cURL aktualisieren. Falls dies nicht möglich ist, kann man den Schalter ersetzen durch &amp;lt;code&amp;gt;--silent --show-error&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Providerspezifisches=====&lt;br /&gt;
Eine lose Liste mit providerspezifischen Erfolgen und Misserfolgen:&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|+Liste mit Rückmeldungen zu E-Mail Anbietern&lt;br /&gt;
!Provider&lt;br /&gt;
!Funktioniert&lt;br /&gt;
!Details&lt;br /&gt;
|-&lt;br /&gt;
|Hetzner&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;code&amp;gt;my $emailServer = &#039;mail.your-server.de&#039;;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;...&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;smtps://$emailServer:465&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Vodafone&lt;br /&gt;
|Ja&lt;br /&gt;
|Folgende Schalter sind hilfreich:&lt;br /&gt;
&amp;lt;code&amp;gt;--ssl-reqd --sasl-ir smtps://$emailserver:465&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Freenet&lt;br /&gt;
|Nein (nicht zuverlässig aufgrund des Spamfilters)&lt;br /&gt;
|Der Spamfilter lässt teilweise Nachrichten passieren, teilweise wird die Verbindung abgelehnt:&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;&#039;550 Spam message rejected&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
https://forum.fhem.de/index.php?topic=140814.msg1341872#msg1341872&lt;br /&gt;
|-&lt;br /&gt;
|T-Online&lt;br /&gt;
|Ja?&lt;br /&gt;
|&amp;lt;code&amp;gt;my $emailServer = &#039;securesmtp.t-online.de&#039;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;...&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;smtps://$emailServer:465&amp;lt;/code&amp;gt;https://forum.fhem.de/index.php?topic=140814.msg1340258#msg1340258&lt;br /&gt;
|-&lt;br /&gt;
|GMX&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;code&amp;gt;my $emailServer = &#039;mail.gmx.net&#039;;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;...&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;smtps://$emailserver:465&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Google -&amp;gt; GMail&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;code&amp;gt;my $emailServer = &#039;smtp.gmail.com&#039;;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;...&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;smtps://$emailserver:465&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Siehe auch dazu den {{Link2Forum|Message=1341182|Topic=140814|LinkText=Forumspost}}&lt;br /&gt;
===EXIM4 (Debian)===&lt;br /&gt;
Eine Anleitung zum Versenden von E-Mails mit Exim ist als PDF verfügbar: [[Medium:Anleitung Exim4 Debian GMX.pdf|PDF]]&lt;br /&gt;
&lt;br /&gt;
===SSMTP (OpenWRT, embedded Distros)===&lt;br /&gt;
&lt;br /&gt;
====Installation====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install ssmtp_2.64-4_mpc85xx.ipk  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Damit &amp;lt;code&amp;gt;ssmtp&amp;lt;/code&amp;gt; funktioniert müssen die Dateien &amp;lt;code&amp;gt;/etc/ssmtp/ssmtp.conf&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;/etc/ssmtp/revaliases&amp;lt;/code&amp;gt; angepasst werden.&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/ssmtp.conf &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root=arnold@gmx.net&lt;br /&gt;
 mailhub=mail.gmx.net:465&lt;br /&gt;
 rewriteDomain=gmx.net&lt;br /&gt;
 hostname=gmx.net&lt;br /&gt;
 FromLineOverride=YES&lt;br /&gt;
 UseTLS=YES&lt;br /&gt;
 #UseSTARTTLS=YES&lt;br /&gt;
 AuthUser=arnold@gmx.net&lt;br /&gt;
 AuthPass=Passwort_von_arnold@gmx.net&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/revaliases&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root:arnold@gmx.net:mail.gmx.net:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der [[99_myUtils_anlegen|99_myUtils]] folgende Unterroutine einfügen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub OpenWRTMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $sender = &amp;quot;dockstar\@heye-tammo.de&amp;quot;; &lt;br /&gt;
 Log 1, &amp;quot;sendEmail RCP: $rcpt, Subject: $subject, Text: $text&amp;quot;;&lt;br /&gt;
 $ret .= qx(echo -e &#039;to:$rcpt\n from:$sender\nsubject:$subject\n$text\n&#039; | ssmtp $rcpt);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendEmail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===PHP Mail Funktion (Synology DiskStation)===&lt;br /&gt;
Beim DSM 3.2 funktioniert der &amp;lt;code&amp;gt;php-mail&amp;lt;/code&amp;gt;-Befehl, so dass man mittels folgendem Modul E-Mails versenden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub sendmail($$$) {&lt;br /&gt;
 my($empf, $subj, $nachricht) = @_;&lt;br /&gt;
 system(&amp;quot;php -r &#039;mail(\&amp;quot;$empf\&amp;quot;,\&amp;quot;$subj\&amp;quot;,\&amp;quot;$nachricht\&amp;quot;);&#039;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sendemail (Veraltet, Debian-basierende Distros)===&lt;br /&gt;
Es handelt sich um ein Perl Programm, dass man aus den Paketquellen installieren kann.&lt;br /&gt;
&lt;br /&gt;
Es gibt Probleme: &lt;br /&gt;
&lt;br /&gt;
*mit der TLS bzw STARTTLS Verschlüsselung (https://github.com/fhem/fhem-docker/issues/254, https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1915977.html) ,&lt;br /&gt;
*Mit der Webseite des Projekts (https://web.archive.org/web/20190906140125/http://caspian.dotconf.net/menu/Software/SendEmail); diese ist nicht mehr verfügbar und nur eine archivierte Kopie kann eingesehen werden,&lt;br /&gt;
&lt;br /&gt;
weswegen diese Lösung als technisch überholt gelten sollte. Will man trotzdem &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; verwenden folgenden hier die Informationen dazu:&lt;br /&gt;
&lt;br /&gt;
Auf dem Server muss &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; installiert werden:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt-get update&lt;br /&gt;
sudo apt-get install sendemail libio-socket-ssl-perl libnet-ssleay-perl perl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Man kann die korrekte Funktion im Terminal (ohne &amp;lt;code&amp;gt;{qx()}&amp;lt;/code&amp;gt; ) oder auch in der FHEM Kommandozeile testen (Hinweis: manche Provider (z.B. gmail) lassen diesen Zugang erst nach Freischaltung zu!).&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Bei Gmail muss seit dem 30.6.2022 ein separates Passwort erzeugt werden, die alte Einstellung über &amp;quot;Zugriff durch weniger sichere Apps&amp;quot; funktioniert nicht mehr. Siehe Support Seite Google: &amp;lt;nowiki&amp;gt;https://support.google.com/accounts/answer/185833&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Beispiel mit Gmail über Linux Terminalfenster:&#039;&#039;&lt;br /&gt;
 sendemail -f &#039;Der.Absender@gmail.com&#039; -t &#039;Der.Empfaenger@gmail.com&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt; -s &#039;smtp.gmail.com:587&#039; -xu &#039;Der.Absender&#039; -xp DASSUPERGEHEIMEPASSW0RT&lt;br /&gt;
In der [[99 myUtils anlegen|99_myUtils]] folgende Unterroutine einfügen:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
######## DebianMail  Mail auf dem RPi versenden ############ &lt;br /&gt;
# $provider für SMTP Server anpassen. &lt;br /&gt;
# Einmal in der FHEM Kommandozeile, user und password anpassen:&lt;br /&gt;
# {setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&lt;br /&gt;
sub DebianMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $attach = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $error;&lt;br /&gt;
 my $konto = getKeyValue(&amp;quot;myEmailKonto&amp;quot;); &lt;br /&gt;
 my $passwrd = getKeyValue(&amp;quot;myEmailPasswrd&amp;quot;); &lt;br /&gt;
 my $from = $konto; # or use different KeyValue if konto is not the from email address&lt;br /&gt;
 my $provider = &amp;quot;smtp.1und1.de&amp;quot;; # smtp.domain.tld:port see provider documentation&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail RCP: $rcpt&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Subject: $subject&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Text: $text&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Anhang: $attach&amp;quot;;&lt;br /&gt;
 if (not defined($attach)){$attach=&#039;&#039;}&lt;br /&gt;
 $ret .= qx(sendemail -f &#039;$from&#039; -t &#039;$rcpt&#039; -u &#039;$subject&#039; -m &#039;$text&#039; -a $attach -s &#039;$provider&#039; -xu &#039;$konto&#039; -xp &#039;$passwrd&#039; -o tls=auto -o message-charset=utf-8);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendemail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
###############################################################################&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Die Zugangsdaten für den Email-Account müssen dabei FHEM-intern gespeichert werden. Dazu gibt man einmalig über die Kommandozeile folgendes ein (user@domain und password müssen auf den Email Account geändert werden):&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;{setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die TLS Verschlüsselung (ehem. SSL) nicht zu nutzen, muss in der viertletzten Zeile tls=no verwendet und der Port des Mailproviders auf 25 eigetragen werden. Sollte anschließend keine Mail verschickt werden, siehe Probleme.&lt;br /&gt;
&lt;br /&gt;
Falls der Body-Text in einem (Android-)Mailer auf dem Handy nicht gezeigt wird, kann der Parameter &#039;&#039;&#039;-o message-content-type=html&#039;&#039;&#039; helfen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion kann beispielsweise folgendermaßen aufgerufen werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{qx(sendemail -f &#039;Ab\@send.er&#039; -t &#039;Emp\@faeng.er&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &#039;DateinameOderLeer&#039; -s &#039;smtpFQDN:port&#039; -xu &#039;MailUser&#039; -xp &#039;PasswortOhneKlammeraffen&#039;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bei allen Feldern können generell doppelte &amp;quot; oder einfache &#039; Anführungszeichen verwendet werden. Innerhalb von &amp;quot;&amp;quot; müssen Sonderzeichen wie @ aber maskiert werden, da sie sonst als Steuerzeichen interpretiert werden:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;quot;email\@email.domain&amp;quot; oder &#039;email@email.domain&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direkt in der FHEM Kommandozeile (ohne Anhang)&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;{DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ordnungsgemäße Ausführung wird mit 71 quittiert.&lt;br /&gt;
&lt;br /&gt;
Beispiele mehrere Anhänge (Es ist wichtig Dateinamen mit Leerzeichen in separate Anführungszeichen zu setzen)&lt;br /&gt;
 {DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;Anhang1 Anhang2 Anhang3&amp;quot;)}&lt;br /&gt;
 {DebianMail(&#039;email@email.domain&#039;,&#039;Subject&#039;,&#039;Text&#039;,&amp;quot;&#039;with spaces Anhang1&#039; &#039;with spaces Anhang2&#039;&amp;quot;)}&lt;br /&gt;
oder (mehr als einen Empfänger, diese können durch Leerzeichen, Semikolon oder Komma getrennt sein)&lt;br /&gt;
 define Sonstiges at *01:00:00 {\&lt;br /&gt;
 DebianMail(&#039;email@email.domain,email2@email.domain&#039;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;&#039;Anhang1&#039; &#039;Anhang2&#039; ...&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Die archivierte Doku zu &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; befindet sich in der Änderungshistorie: https://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;amp;oldid=37524&lt;br /&gt;
[[Kategorie:E-Mail]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40156</id>
		<title>E-Mail senden</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40156"/>
		<updated>2025-05-19T17:38:45Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Liste mit E-Mail Anbietern angefangen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Um aus FHEM heraus E-Mails senden zu können, gibt es unterschiedliche Methoden, abhängig von der verwendeten Plattform.&lt;br /&gt;
&lt;br /&gt;
Lange Zeit war &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; sehr beliebt. Da aber die Homepage des Projekts nicht mehr online ist und Probleme nur noch via erhöhtem Supportaufwand gelöst werden, sollte diese Option als veraltet betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Eine Liste mit ressourcensparenden E-Mails Clients findet man bei dem OpenWRT Projekt: https://openwrt.org/docs/guide-user/services/email/smtp.client&lt;br /&gt;
&lt;br /&gt;
MSGMail ist ein FHEM eigenes Modul zum Versenden, die Einrichtung ist allerdings nicht gänzlich einfach. Anfängern sei die cURL Methode empfohlen.&lt;br /&gt;
&lt;br /&gt;
=== MSGMail (Linux, Windows) ===&lt;br /&gt;
FHEM bringt ein eigenes Device zum Senden von E-Mails mit, welches in Perl implementiert ist: [https://fhem.de/commandref.html#MSGMail MSGMail, https://fhem.de/commandref.html#MSGMail]&lt;br /&gt;
&lt;br /&gt;
=== cURL (Linux, Windows) ===&lt;br /&gt;
[https://curl.se/ cURL] kann auf praktisch [https://curl.se/download.html jedem Betriebssystem] genutzt werden, [https://curl.se/windows/ selbst unter Windows]. Es kann direkt mit einem SMTP Server E-Mails versenden und stellt damit eine moderne, portable und sichere Option zum Versenden von E-Mails dar.&lt;br /&gt;
&lt;br /&gt;
In FHEM richtet man sich ein Device ein und passt die Details (&amp;lt;code&amp;gt;$emailTo, $emailFrom, $emailServer&amp;lt;/code&amp;gt;) an. Das Programm cURL muss installiert sein (z.B. mit &amp;lt;code&amp;gt;apt install curl&amp;lt;/code&amp;gt;, oder durch Herunterladen und Installieren von der [https://curl.se cURL-Webseite]).&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod sendMail dummy&lt;br /&gt;
attr sendMail readingList message password&lt;br /&gt;
attr sendMail setList message:textField-long password&lt;br /&gt;
attr sendMail userReadings state:message:[\s\S]* {\&lt;br /&gt;
    my $emailTo   = &#039;du@example.com&#039;;;\&lt;br /&gt;
    my $emailFrom = &#039;fhem@example.de&#039;;;\&lt;br /&gt;
    my $emailServer = &#039;mail.your-server.de&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #erzeuge Datumsangabe konform zu RFC-5322:\&lt;br /&gt;
    my @emailDateArray = split(&#039; &#039;, strftime(&amp;quot;%w %d %m %Y %H:%M:%S %z&amp;quot;, localtime));;\&lt;br /&gt;
    $emailDateArray[0] = (qw[Sun Mon Tue Wed Thu Fri Sat])[$emailDateArray[0]] . &#039;,&#039;;;\&lt;br /&gt;
    $emailDateArray[2] = (qw[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec])[$emailDateArray[2]-1];;\&lt;br /&gt;
    my $emailDate = join(&#039; &#039;, @emailDateArray);;\&lt;br /&gt;
    \&lt;br /&gt;
    # Passwort aus getKeyValue abrufen\&lt;br /&gt;
    my ($err, $emailPass) = getKeyValue(&amp;quot;${name}_password&amp;quot;);;\&lt;br /&gt;
    if ($err || !defined $emailPass) {\&lt;br /&gt;
        return &amp;quot;Error retrieving password: $err&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    my $message = ReadingsVal($name, &#039;message&#039;, &#039;???&#039;);;\&lt;br /&gt;
    my $subject = &amp;quot;$name: FHEM Nachricht&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # Betreff extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
    if ($message =~ s/(?:Subject|Betreff)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        $subject = $1;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    # Empfänger extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
    if ($message =~ s/(?:To|An)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        $emailTo = $1;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #Dateianhänge bestimmen\&lt;br /&gt;
    my @attachments;;\&lt;br /&gt;
    while ($message =~ s/(?:Attachment|Anhang)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        my $file = $1;;\&lt;br /&gt;
        return &amp;quot;&amp;gt;&amp;gt;$file&amp;lt;&amp;lt; not found, not sending email&amp;quot; unless (-e $file);;\&lt;br /&gt;
        push (@attachments, $file);;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #der curl Befehl wird hier aufgebaut:\&lt;br /&gt;
    my $cmd = &amp;quot;curl -m 19 --noproxy &#039;*&#039; --no-progress-meter --ssl-reqd smtps://$emailServer:465 &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;--user &#039;$emailFrom:$emailPass&#039; --mail-from &#039;$emailFrom&#039; --mail-rcpt &#039;$emailTo&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;--write-out &#039;Email sent with status %{http_code}&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;Subject: $subject&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;From: $emailFrom&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;Date: $emailDate&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #multipart/alternative für Text und HTML EMail Body\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;=(;;type=multipart/alternative&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Anfang entfernen:\&lt;br /&gt;
    $message =~ s/^(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+//;;\&lt;br /&gt;
    # Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Ende entfernen:\&lt;br /&gt;
    $message =~ s/(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+$//;;\&lt;br /&gt;
    \&lt;br /&gt;
    #Plaintext E-Mail Inhalt an cURL:\&lt;br /&gt;
    $cmd .= &amp;quot;-F \&amp;quot;=$message;;type=text/plain;; charset=UTF-8\&amp;quot; &amp;quot;;;\&lt;br /&gt;
    #ersetze \n bzw. \r\n durch ein HTML-tag &amp;lt;br /&amp;gt;:\&lt;br /&gt;
    $message =~ s/\r?\n/&amp;lt;br \/&amp;gt;/g;;\&lt;br /&gt;
    \&lt;br /&gt;
    #HTML E-Mail Inhalt an cURL:\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;= &amp;lt;body&amp;gt;$message&amp;lt;/body&amp;gt;;;type=text/html;; charset=UTF-8&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;=)&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #jetzt alle Attachments (0..N) anhängen:\&lt;br /&gt;
    foreach my $file (@attachments) {\&lt;br /&gt;
        $cmd .= &amp;quot;-F &#039;=@&amp;quot;.$file.&amp;quot;;;encoder=base64&#039; &amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #stderr auch mit in den output sammeln:\&lt;br /&gt;
    $cmd .= &amp;quot; 2&amp;gt;&amp;amp;1&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc1) {\&lt;br /&gt;
        *sendMailfunc1 = sub ($) {\&lt;br /&gt;
            my ($param) = @_;;\&lt;br /&gt;
            my $result;;\&lt;br /&gt;
            $result = qx/$param/;;\&lt;br /&gt;
            $result = MIME::Base64::encode_base64($result);;\&lt;br /&gt;
            $result =~ s/\n//g;;\&lt;br /&gt;
            return $result;;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #Rückgabe über diese Funktion,\&lt;br /&gt;
    #anzeigen in dem reading dieses UserReadings:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc2) {\&lt;br /&gt;
        *sendMailfunc2 = sub ($) {\&lt;br /&gt;
            my ($result) = @_;;\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            $result = MIME::Base64::decode_base64($result);;\&lt;br /&gt;
            readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #funktion falls BlockingCall abgebrochen wird:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc3) {\&lt;br /&gt;
        *sendMailfunc3 = sub ($) {\&lt;br /&gt;
            my ($result) = @_;;\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    BlockingCall(&amp;quot;sendMailfunc1&amp;quot;, $cmd, &amp;quot;sendMailfunc2&amp;quot;, 20, &amp;quot;sendMailfunc3&amp;quot;, &amp;quot;Error: timeout&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return &amp;quot;started cURL command...&amp;quot;;;\&lt;br /&gt;
},\&lt;br /&gt;
state:password:.* {\&lt;br /&gt;
    # Passwort speichern\&lt;br /&gt;
    my $ret = setKeyValue(&amp;quot;${name}_password&amp;quot;, ReadingsVal($name, &#039;password&#039;, undef)) // &amp;quot;password stored&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\&lt;br /&gt;
    #\&lt;br /&gt;
    # Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\&lt;br /&gt;
    # Alternativ: { setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;password&amp;quot;, &amp;quot;****&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return &amp;quot;$ret&amp;quot;;;\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Das Passwort setzt man mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail password geheimesPasswort&amp;lt;/code&amp;gt;&lt;br /&gt;
* Alternativ, damit das Passwort nicht im Log auftaucht: &amp;lt;code&amp;gt;{ setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
E-Mails kann man dann Senden mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Mein Text&amp;lt;/code&amp;gt; (es wird der Standardbetreff und Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;(es wird der Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Subject=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;✅ Dies ist ein Test&amp;quot; Meldung mit Emoji ⚽ geht auch&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eine Datei mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/bag.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zwei Dateien mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;./www/images/fhemSVG/bag.svg&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/batterie.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;, für die anzuhängenden Dateien können relative und absolute Pfade verwendet werden. Beim Vorbereiten der E-Mail wird geprüft ob die Dateien existieren, da dies sonst eine typische Fehlerquelle ist. Es wird nichts versendet wenn eine Datei nicht gefunden werden kann.&lt;br /&gt;
&lt;br /&gt;
==== Fehlersuche ====&lt;br /&gt;
&lt;br /&gt;
===== Fehlende Protokolloption in cURL =====&lt;br /&gt;
Sollte cURL zwar installiert sein, aber keine E-Mails versenden, kann es sein, dass das Protokoll nicht einkompiliert wurde. Dies ist bei OpenWRT zum Beispiel der Fall.&lt;br /&gt;
&lt;br /&gt;
So sollte es aussehen (hier ein Ubuntu), relevant ist für diese Nutzung das Protokoll smtp und smtps, das braucht man:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.7&lt;br /&gt;
Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.6&lt;br /&gt;
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp&lt;br /&gt;
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So sollte es nicht aussehen (hier ein OpenWRT):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl: (1) Protocol &amp;quot;smtps&amp;quot; not supported&lt;br /&gt;
&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.10.1 (arm-openwrt-linux-gnu) libcurl/8.10.1 mbedTLS/3.6.2 nghttp2/1.63.0&lt;br /&gt;
Release-Date: 2024-09-18&lt;br /&gt;
Protocols: file ftp ftps http https ipfs ipns mqtt&lt;br /&gt;
Features: alt-svc HSTS HTTP2 HTTPS-proxy IPv6 Largefile SSL threadsafe UnixSockets&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Hat man nur ein cURL ohne das Protokoll, so muss man sich das Paket leider anderweitig beschaffen (chroot, selbst kompilieren, statisch gelinkte Version, ...).&lt;br /&gt;
&lt;br /&gt;
===== Falscher Port, login options =====&lt;br /&gt;
Einige Provider nehmen ihre TLS verschlüsselten E-Mails über einen anderen Port an, häufig Port 587 (siehe auch {{Link2Forum|Topic= 140814|Message=1340366|LinkText=diesen Forenbeitrag}}). Ggf. muss man auch mit einigen Loginoptionen experimentieren bis cURL  wie gewünscht funktioniert (Stichwort: &#039;&#039;[https://curl.se/docs/manpage.html#--login-options --login-options]&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
===== Alte cURL Version =====&lt;br /&gt;
Sollte eine sehr alte cURL Version kleiner 7.67.0 genutzt werden, fehlt der Schalter &amp;lt;code&amp;gt;--no-progress-meter.&amp;lt;/code&amp;gt; In solchen Fällen sollte man cURL aktualisieren. Falls dies nicht möglich ist, kann man den Schalter ersetzen durch &amp;lt;code&amp;gt;--silent --show-error&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Providerspezifisches=====&lt;br /&gt;
Eine lose Liste mit providerspezifischen Erfolgen und Misserfolgen:&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
|+Liste mit Rückmeldungen zu E-Mail Anbietern&lt;br /&gt;
!Provider&lt;br /&gt;
!Funktioniert&lt;br /&gt;
!Details&lt;br /&gt;
|-&lt;br /&gt;
|Hetzner&lt;br /&gt;
|Ja&lt;br /&gt;
|&amp;lt;code&amp;gt;my $emailServer = &#039;mail.your-server.de&#039;;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;...&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;smtps://$emailServer:465&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Vodafone&lt;br /&gt;
|Ja&lt;br /&gt;
|Folgende Schalter sind hilfreich:&lt;br /&gt;
&amp;lt;code&amp;gt;--ssl-reqd --sasl-ir smtps://$emailserver:465&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|Freenet&lt;br /&gt;
|Nein (nicht zuverlässig aufgrund des Spamfilters)&lt;br /&gt;
|Der Spamfilter lässt teilweise Nachrichten passieren, teilweise wird die Verbindung abgelehnt:&lt;br /&gt;
&amp;lt;code&amp;gt;&#039;&#039;550 Spam message rejected&#039;&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
https://forum.fhem.de/index.php?topic=140814.msg1341872#msg1341872&lt;br /&gt;
|-&lt;br /&gt;
|T-Online&lt;br /&gt;
|Ja?&lt;br /&gt;
|&amp;lt;code&amp;gt;my $emailServer = &#039;securesmtp.t-online.de&#039;&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;...&amp;lt;/code&amp;gt;&amp;lt;code&amp;gt;smtps://$emailServer:465&amp;lt;/code&amp;gt;https://forum.fhem.de/index.php?topic=140814.msg1340258#msg1340258&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Siehe auch dazu den {{Link2Forum|Message=1341182|Topic=140814|LinkText=Forumspost}}&lt;br /&gt;
===EXIM4 (Debian)===&lt;br /&gt;
Eine Anleitung zum Versenden von E-Mails mit Exim ist als PDF verfügbar: [[Medium:Anleitung Exim4 Debian GMX.pdf|PDF]]&lt;br /&gt;
&lt;br /&gt;
===SSMTP (OpenWRT, embedded Distros)===&lt;br /&gt;
&lt;br /&gt;
====Installation====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install ssmtp_2.64-4_mpc85xx.ipk  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Damit &amp;lt;code&amp;gt;ssmtp&amp;lt;/code&amp;gt; funktioniert müssen die Dateien &amp;lt;code&amp;gt;/etc/ssmtp/ssmtp.conf&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;/etc/ssmtp/revaliases&amp;lt;/code&amp;gt; angepasst werden.&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/ssmtp.conf &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root=arnold@gmx.net&lt;br /&gt;
 mailhub=mail.gmx.net:465&lt;br /&gt;
 rewriteDomain=gmx.net&lt;br /&gt;
 hostname=gmx.net&lt;br /&gt;
 FromLineOverride=YES&lt;br /&gt;
 UseTLS=YES&lt;br /&gt;
 #UseSTARTTLS=YES&lt;br /&gt;
 AuthUser=arnold@gmx.net&lt;br /&gt;
 AuthPass=Passwort_von_arnold@gmx.net&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/revaliases&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root:arnold@gmx.net:mail.gmx.net:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der [[99_myUtils_anlegen|99_myUtils]] folgende Unterroutine einfügen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub OpenWRTMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $sender = &amp;quot;dockstar\@heye-tammo.de&amp;quot;; &lt;br /&gt;
 Log 1, &amp;quot;sendEmail RCP: $rcpt, Subject: $subject, Text: $text&amp;quot;;&lt;br /&gt;
 $ret .= qx(echo -e &#039;to:$rcpt\n from:$sender\nsubject:$subject\n$text\n&#039; | ssmtp $rcpt);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendEmail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===PHP Mail Funktion (Synology DiskStation)===&lt;br /&gt;
Beim DSM 3.2 funktioniert der &amp;lt;code&amp;gt;php-mail&amp;lt;/code&amp;gt;-Befehl, so dass man mittels folgendem Modul E-Mails versenden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub sendmail($$$) {&lt;br /&gt;
 my($empf, $subj, $nachricht) = @_;&lt;br /&gt;
 system(&amp;quot;php -r &#039;mail(\&amp;quot;$empf\&amp;quot;,\&amp;quot;$subj\&amp;quot;,\&amp;quot;$nachricht\&amp;quot;);&#039;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Sendemail (Veraltet, Debian-basierende Distros)===&lt;br /&gt;
Es handelt sich um ein Perl Programm, dass man aus den Paketquellen installieren kann.&lt;br /&gt;
&lt;br /&gt;
Es gibt Probleme: &lt;br /&gt;
&lt;br /&gt;
*mit der TLS bzw STARTTLS Verschlüsselung (https://github.com/fhem/fhem-docker/issues/254, https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1915977.html) ,&lt;br /&gt;
*Mit der Webseite des Projekts (https://web.archive.org/web/20190906140125/http://caspian.dotconf.net/menu/Software/SendEmail); diese ist nicht mehr verfügbar und nur eine archivierte Kopie kann eingesehen werden,&lt;br /&gt;
&lt;br /&gt;
weswegen diese Lösung als technisch überholt gelten sollte. Will man trotzdem &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; verwenden folgenden hier die Informationen dazu:&lt;br /&gt;
&lt;br /&gt;
Auf dem Server muss &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; installiert werden:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt-get update&lt;br /&gt;
sudo apt-get install sendemail libio-socket-ssl-perl libnet-ssleay-perl perl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Man kann die korrekte Funktion im Terminal (ohne &amp;lt;code&amp;gt;{qx()}&amp;lt;/code&amp;gt; ) oder auch in der FHEM Kommandozeile testen (Hinweis: manche Provider (z.B. gmail) lassen diesen Zugang erst nach Freischaltung zu!).&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Bei Gmail muss seit dem 30.6.2022 ein separates Passwort erzeugt werden, die alte Einstellung über &amp;quot;Zugriff durch weniger sichere Apps&amp;quot; funktioniert nicht mehr. Siehe Support Seite Google: &amp;lt;nowiki&amp;gt;https://support.google.com/accounts/answer/185833&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Beispiel mit Gmail über Linux Terminalfenster:&#039;&#039;&lt;br /&gt;
 sendemail -f &#039;Der.Absender@gmail.com&#039; -t &#039;Der.Empfaenger@gmail.com&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt; -s &#039;smtp.gmail.com:587&#039; -xu &#039;Der.Absender&#039; -xp DASSUPERGEHEIMEPASSW0RT&lt;br /&gt;
In der [[99 myUtils anlegen|99_myUtils]] folgende Unterroutine einfügen:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
######## DebianMail  Mail auf dem RPi versenden ############ &lt;br /&gt;
# $provider für SMTP Server anpassen. &lt;br /&gt;
# Einmal in der FHEM Kommandozeile, user und password anpassen:&lt;br /&gt;
# {setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&lt;br /&gt;
sub DebianMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $attach = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $error;&lt;br /&gt;
 my $konto = getKeyValue(&amp;quot;myEmailKonto&amp;quot;); &lt;br /&gt;
 my $passwrd = getKeyValue(&amp;quot;myEmailPasswrd&amp;quot;); &lt;br /&gt;
 my $from = $konto; # or use different KeyValue if konto is not the from email address&lt;br /&gt;
 my $provider = &amp;quot;smtp.1und1.de&amp;quot;; # smtp.domain.tld:port see provider documentation&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail RCP: $rcpt&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Subject: $subject&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Text: $text&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Anhang: $attach&amp;quot;;&lt;br /&gt;
 if (not defined($attach)){$attach=&#039;&#039;}&lt;br /&gt;
 $ret .= qx(sendemail -f &#039;$from&#039; -t &#039;$rcpt&#039; -u &#039;$subject&#039; -m &#039;$text&#039; -a $attach -s &#039;$provider&#039; -xu &#039;$konto&#039; -xp &#039;$passwrd&#039; -o tls=auto -o message-charset=utf-8);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendemail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
###############################################################################&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Die Zugangsdaten für den Email-Account müssen dabei FHEM-intern gespeichert werden. Dazu gibt man einmalig über die Kommandozeile folgendes ein (user@domain und password müssen auf den Email Account geändert werden):&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;{setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die TLS Verschlüsselung (ehem. SSL) nicht zu nutzen, muss in der viertletzten Zeile tls=no verwendet und der Port des Mailproviders auf 25 eigetragen werden. Sollte anschließend keine Mail verschickt werden, siehe Probleme.&lt;br /&gt;
&lt;br /&gt;
Falls der Body-Text in einem (Android-)Mailer auf dem Handy nicht gezeigt wird, kann der Parameter &#039;&#039;&#039;-o message-content-type=html&#039;&#039;&#039; helfen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion kann beispielsweise folgendermaßen aufgerufen werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{qx(sendemail -f &#039;Ab\@send.er&#039; -t &#039;Emp\@faeng.er&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &#039;DateinameOderLeer&#039; -s &#039;smtpFQDN:port&#039; -xu &#039;MailUser&#039; -xp &#039;PasswortOhneKlammeraffen&#039;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bei allen Feldern können generell doppelte &amp;quot; oder einfache &#039; Anführungszeichen verwendet werden. Innerhalb von &amp;quot;&amp;quot; müssen Sonderzeichen wie @ aber maskiert werden, da sie sonst als Steuerzeichen interpretiert werden:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;&amp;quot;email\@email.domain&amp;quot; oder &#039;email@email.domain&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direkt in der FHEM Kommandozeile (ohne Anhang)&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;code&amp;gt;{DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ordnungsgemäße Ausführung wird mit 71 quittiert.&lt;br /&gt;
&lt;br /&gt;
Beispiele mehrere Anhänge (Es ist wichtig Dateinamen mit Leerzeichen in separate Anführungszeichen zu setzen)&lt;br /&gt;
 {DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;Anhang1 Anhang2 Anhang3&amp;quot;)}&lt;br /&gt;
 {DebianMail(&#039;email@email.domain&#039;,&#039;Subject&#039;,&#039;Text&#039;,&amp;quot;&#039;with spaces Anhang1&#039; &#039;with spaces Anhang2&#039;&amp;quot;)}&lt;br /&gt;
oder (mehr als einen Empfänger, diese können durch Leerzeichen, Semikolon oder Komma getrennt sein)&lt;br /&gt;
 define Sonstiges at *01:00:00 {\&lt;br /&gt;
 DebianMail(&#039;email@email.domain,email2@email.domain&#039;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;&#039;Anhang1&#039; &#039;Anhang2&#039; ...&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Die archivierte Doku zu &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; befindet sich in der Änderungshistorie: https://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;amp;oldid=37524&lt;br /&gt;
[[Kategorie:E-Mail]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40138</id>
		<title>E-Mail senden</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40138"/>
		<updated>2025-05-09T17:22:14Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* cURL (Linux, Windows) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Um aus FHEM heraus E-Mails senden zu können, gibt es unterschiedliche Methoden, abhängig von der verwendeten Plattform.&lt;br /&gt;
&lt;br /&gt;
Lange Zeit war &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; sehr beliebt. Da aber die Homepage des Projekts nicht mehr online ist und Probleme nur noch via erhöhtem Supportaufwand gelöst werden, sollte diese Option als veraltet betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Eine Liste mit ressourcensparenden E-Mails Clients findet man bei dem OpenWRT Projekt: https://openwrt.org/docs/guide-user/services/email/smtp.client&lt;br /&gt;
&lt;br /&gt;
MSGMail ist ein FHEM eigenes Modul zum Versenden, die Einrichtung ist allerdings nicht gänzlich einfach. Anfängern sei die cURL Methode empfohlen.&lt;br /&gt;
&lt;br /&gt;
=== MSGMail (Linux, Windows) ===&lt;br /&gt;
FHEM bringt ein eigenes Device zum Senden von E-Mails mit, welches in Perl implementiert ist: [https://fhem.de/commandref.html#MSGMail MSGMail, https://fhem.de/commandref.html#MSGMail]&lt;br /&gt;
&lt;br /&gt;
=== cURL (Linux, Windows) ===&lt;br /&gt;
[https://curl.se/ cURL] kann auf praktisch [https://curl.se/download.html jedem Betriebssystem] genutzt werden, [https://curl.se/windows/ selbst unter Windows]. Es kann direkt mit einem SMTP Server E-Mails versenden und stellt damit eine moderne, portable und sichere Option zum Versenden von E-Mails dar.&lt;br /&gt;
&lt;br /&gt;
In FHEM richtet man sich ein Device ein und passt die Details (&amp;lt;code&amp;gt;$emailTo, $emailFrom, $emailServer&amp;lt;/code&amp;gt;) an. Das Programm cURL muss installiert sein (z.B. mit &amp;lt;code&amp;gt;apt install curl&amp;lt;/code&amp;gt;, oder durch Herunterladen und Installieren von der [https://curl.se cURL-Webseite]).&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod sendMail dummy&lt;br /&gt;
attr sendMail readingList message password&lt;br /&gt;
attr sendMail setList message:textField-long password&lt;br /&gt;
attr sendMail userReadings state:message:[\s\S]* {\&lt;br /&gt;
    my $emailTo   = &#039;du@example.com&#039;;;\&lt;br /&gt;
    my $emailFrom = &#039;fhem@example.de&#039;;;\&lt;br /&gt;
    my $emailServer = &#039;mail.your-server.de&#039;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #erzeuge Datumsangabe konform zu RFC-5322:\&lt;br /&gt;
    my @emailDateArray = split(&#039; &#039;, strftime(&amp;quot;%w %d %m %Y %H:%M:%S %z&amp;quot;, localtime));;\&lt;br /&gt;
    $emailDateArray[0] = (qw[Sun Mon Tue Wed Thu Fri Sat])[$emailDateArray[0]] . &#039;,&#039;;;\&lt;br /&gt;
    $emailDateArray[2] = (qw[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec])[$emailDateArray[2]-1];;\&lt;br /&gt;
    my $emailDate = join(&#039; &#039;, @emailDateArray);;\&lt;br /&gt;
    \&lt;br /&gt;
    # Passwort aus getKeyValue abrufen\&lt;br /&gt;
    my ($err, $emailPass) = getKeyValue(&amp;quot;${name}_password&amp;quot;);;\&lt;br /&gt;
    if ($err || !defined $emailPass) {\&lt;br /&gt;
        return &amp;quot;Error retrieving password: $err&amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    my $message = ReadingsVal($name, &#039;message&#039;, &#039;???&#039;);;\&lt;br /&gt;
    my $subject = &amp;quot;$name: FHEM Nachricht&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # Betreff extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
    if ($message =~ s/(?:Subject|Betreff)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        $subject = $1;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    # Empfänger extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
    if ($message =~ s/(?:To|An)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        $emailTo = $1;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #Dateianhänge bestimmen\&lt;br /&gt;
    my @attachments;;\&lt;br /&gt;
    while ($message =~ s/(?:Attachment|Anhang)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
        my $file = $1;;\&lt;br /&gt;
        return &amp;quot;&amp;gt;&amp;gt;$file&amp;lt;&amp;lt; not found, not sending email&amp;quot; unless (-e $file);;\&lt;br /&gt;
        push (@attachments, $file);;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #der curl Befehl wird hier aufgebaut:\&lt;br /&gt;
    my $cmd = &amp;quot;curl -m 19 --noproxy &#039;*&#039; --no-progress-meter --ssl-reqd smtps://$emailServer:465 &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;--user &#039;$emailFrom:$emailPass&#039; --mail-from &#039;$emailFrom&#039; --mail-rcpt &#039;$emailTo&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;--write-out &#039;Email sent with status %{http_code}&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;Subject: $subject&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;From: $emailFrom&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-H &#039;Date: $emailDate&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #multipart/alternative für Text und HTML EMail Body\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;=(;;type=multipart/alternative&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    # Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Anfang entfernen:\&lt;br /&gt;
    $message =~ s/^(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+//;;\&lt;br /&gt;
    # Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Ende entfernen:\&lt;br /&gt;
    $message =~ s/(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+$//;;\&lt;br /&gt;
    \&lt;br /&gt;
    #Plaintext E-Mail Inhalt an cURL:\&lt;br /&gt;
    $cmd .= &amp;quot;-F \&amp;quot;=$message;;type=text/plain;; charset=UTF-8\&amp;quot; &amp;quot;;;\&lt;br /&gt;
    #ersetze \n bzw. \r\n durch ein HTML-tag &amp;lt;br /&amp;gt;:\&lt;br /&gt;
    $message =~ s/\r?\n/&amp;lt;br \/&amp;gt;/g;;\&lt;br /&gt;
    \&lt;br /&gt;
    #HTML E-Mail Inhalt an cURL:\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;= &amp;lt;body&amp;gt;$message&amp;lt;/body&amp;gt;;;type=text/html;; charset=UTF-8&#039; &amp;quot;;;\&lt;br /&gt;
    $cmd .= &amp;quot;-F &#039;=)&#039; &amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #jetzt alle Attachments (0..N) anhängen:\&lt;br /&gt;
    foreach my $file (@attachments) {\&lt;br /&gt;
        $cmd .= &amp;quot;-F &#039;=@&amp;quot;.$file.&amp;quot;;;encoder=base64&#039; &amp;quot;;;\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #stderr auch mit in den output sammeln:\&lt;br /&gt;
    $cmd .= &amp;quot; 2&amp;gt;&amp;amp;1&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc1) {\&lt;br /&gt;
        *sendMailfunc1 = sub ($) {\&lt;br /&gt;
            my ($param) = @_;;\&lt;br /&gt;
            my $result;;\&lt;br /&gt;
            $result = qx/$param/;;\&lt;br /&gt;
            $result = MIME::Base64::encode_base64($result);;\&lt;br /&gt;
            $result =~ s/\n//g;;\&lt;br /&gt;
            return $result;;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #Rückgabe über diese Funktion,\&lt;br /&gt;
    #anzeigen in dem reading dieses UserReadings:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc2) {\&lt;br /&gt;
        *sendMailfunc2 = sub ($) {\&lt;br /&gt;
            my ($result) = @_;;\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            $result = MIME::Base64::decode_base64($result);;\&lt;br /&gt;
            readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    #funktion falls BlockingCall abgebrochen wird:\&lt;br /&gt;
    if (!defined &amp;amp;sendMailfunc3) {\&lt;br /&gt;
        *sendMailfunc3 = sub ($) {\&lt;br /&gt;
            my ($result) = @_;;\&lt;br /&gt;
            my $hash = $defs{$name};;\&lt;br /&gt;
            readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
        }\&lt;br /&gt;
    }\&lt;br /&gt;
    \&lt;br /&gt;
    BlockingCall(&amp;quot;sendMailfunc1&amp;quot;, $cmd, &amp;quot;sendMailfunc2&amp;quot;, 20, &amp;quot;sendMailfunc3&amp;quot;, &amp;quot;Error: timeout&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return &amp;quot;started cURL command...&amp;quot;;;\&lt;br /&gt;
},\&lt;br /&gt;
state:password:.* {\&lt;br /&gt;
    # Passwort speichern\&lt;br /&gt;
    my $ret = setKeyValue(&amp;quot;${name}_password&amp;quot;, ReadingsVal($name, &#039;password&#039;, undef)) // &amp;quot;password stored&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
    #password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\&lt;br /&gt;
    #\&lt;br /&gt;
    # Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\&lt;br /&gt;
    # Alternativ: { setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }\&lt;br /&gt;
    readingsBulkUpdate($hash, &amp;quot;password&amp;quot;, &amp;quot;****&amp;quot;);;\&lt;br /&gt;
    \&lt;br /&gt;
    return &amp;quot;$ret&amp;quot;;;\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Das Passwort setzt man mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail password geheimesPasswort&amp;lt;/code&amp;gt;&lt;br /&gt;
* Alternativ, damit das Passwort nicht im Log auftaucht: &amp;lt;code&amp;gt;{ setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
E-Mails kann man dann Senden mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Mein Text&amp;lt;/code&amp;gt; (es wird der Standardbetreff und Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;(es wird der Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Subject=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;✅ Dies ist ein Test&amp;quot; Meldung mit Emoji ⚽ geht auch&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eine Datei mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/bag.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zwei Dateien mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;./www/images/fhemSVG/bag.svg&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/batterie.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;, für die anzuhängenden Dateien können relative und absolute Pfade verwendet werden. Beim Vorbereiten der E-Mail wird geprüft ob die Dateien existieren, da dies sonst eine typische Fehlerquelle ist. Es wird nichts versendet wenn eine Datei nicht gefunden werden kann.&lt;br /&gt;
&lt;br /&gt;
==== Fehlersuche ====&lt;br /&gt;
&lt;br /&gt;
===== Fehlende Protokolloption in cURL =====&lt;br /&gt;
Sollte cURL zwar installiert sein, aber keine E-Mails versenden, kann es sein, dass das Protokoll nicht einkompiliert wurde. Dies ist bei OpenWRT zum Beispiel der Fall.&lt;br /&gt;
&lt;br /&gt;
So sollte es aussehen (hier ein Ubuntu), relevant ist für diese Nutzung das Protokoll smtp und smtps, das braucht man:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.7&lt;br /&gt;
Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.6&lt;br /&gt;
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp&lt;br /&gt;
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So sollte es nicht aussehen (hier ein OpenWRT):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl: (1) Protocol &amp;quot;smtps&amp;quot; not supported&lt;br /&gt;
&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.10.1 (arm-openwrt-linux-gnu) libcurl/8.10.1 mbedTLS/3.6.2 nghttp2/1.63.0&lt;br /&gt;
Release-Date: 2024-09-18&lt;br /&gt;
Protocols: file ftp ftps http https ipfs ipns mqtt&lt;br /&gt;
Features: alt-svc HSTS HTTP2 HTTPS-proxy IPv6 Largefile SSL threadsafe UnixSockets&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Hat man nur ein cURL ohne das Protokoll, so muss man sich das Paket leider anderweitig beschaffen (chroot, selbst kompilieren, statisch gelinkte Version, ...).&lt;br /&gt;
&lt;br /&gt;
===== Falscher Port, login options =====&lt;br /&gt;
Einige Provider nehmen ihre TLS verschlüsselten E-Mails über einen anderen Port an, häufig Port 587 (siehe auch {{Link2Forum|Topic= 140814|Message=1340366|LinkText=diesen Forenbeitrag}}). Ggf. muss man auch mit einigen Loginoptionen experimentieren bis cURL  wie gewünscht funktioniert (Stichwort: &#039;&#039;[https://curl.se/docs/manpage.html#--login-options --login-options]&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
===== Alte cURL Version =====&lt;br /&gt;
Sollte eine sehr alte cURL Version kleiner 7.67.0 genutzt werden, fehlt der Schalter &amp;lt;code&amp;gt;--no-progress-meter.&amp;lt;/code&amp;gt; In solchen Fällen sollte man cURL aktualisieren. Falls dies nicht möglich ist, kann man den Schalter ersetzen durch &amp;lt;code&amp;gt;--silent --show-error&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Siehe auch dazu den {{Link2Forum|Message=1341182|Topic=140814|LinkText=Forumspost}}&lt;br /&gt;
&lt;br /&gt;
=== EXIM4 (Debian) ===&lt;br /&gt;
Eine Anleitung zum Versenden von E-Mails mit Exim ist als PDF verfügbar: [[Medium:Anleitung Exim4 Debian GMX.pdf|PDF]]&lt;br /&gt;
&lt;br /&gt;
=== SSMTP (OpenWRT, embedded Distros) ===&lt;br /&gt;
&lt;br /&gt;
==== Installation ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install ssmtp_2.64-4_mpc85xx.ipk  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Damit &amp;lt;code&amp;gt;ssmtp&amp;lt;/code&amp;gt; funktioniert müssen die Dateien &amp;lt;code&amp;gt;/etc/ssmtp/ssmtp.conf&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;/etc/ssmtp/revaliases&amp;lt;/code&amp;gt; angepasst werden.&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/ssmtp.conf &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root=arnold@gmx.net&lt;br /&gt;
 mailhub=mail.gmx.net:465&lt;br /&gt;
 rewriteDomain=gmx.net&lt;br /&gt;
 hostname=gmx.net&lt;br /&gt;
 FromLineOverride=YES&lt;br /&gt;
 UseTLS=YES&lt;br /&gt;
 #UseSTARTTLS=YES&lt;br /&gt;
 AuthUser=arnold@gmx.net&lt;br /&gt;
 AuthPass=Passwort_von_arnold@gmx.net&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/revaliases&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root:arnold@gmx.net:mail.gmx.net:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der [[99_myUtils_anlegen|99_myUtils]] folgende Unterroutine einfügen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub OpenWRTMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $sender = &amp;quot;dockstar\@heye-tammo.de&amp;quot;; &lt;br /&gt;
 Log 1, &amp;quot;sendEmail RCP: $rcpt, Subject: $subject, Text: $text&amp;quot;;&lt;br /&gt;
 $ret .= qx(echo -e &#039;to:$rcpt\n from:$sender\nsubject:$subject\n$text\n&#039; | ssmtp $rcpt);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendEmail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== PHP Mail Funktion (Synology DiskStation) ===&lt;br /&gt;
Beim DSM 3.2 funktioniert der &amp;lt;code&amp;gt;php-mail&amp;lt;/code&amp;gt;-Befehl, so dass man mittels folgendem Modul E-Mails versenden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub sendmail($$$) {&lt;br /&gt;
 my($empf, $subj, $nachricht) = @_;&lt;br /&gt;
 system(&amp;quot;php -r &#039;mail(\&amp;quot;$empf\&amp;quot;,\&amp;quot;$subj\&amp;quot;,\&amp;quot;$nachricht\&amp;quot;);&#039;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sendemail (Veraltet, Debian-basierende Distros) ===&lt;br /&gt;
Es handelt sich um ein Perl Programm, dass man aus den Paketquellen installieren kann.&lt;br /&gt;
&lt;br /&gt;
Es gibt Probleme:&lt;br /&gt;
&lt;br /&gt;
* mit der TLS bzw STARTTLS Verschlüsselung (https://github.com/fhem/fhem-docker/issues/254, https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1915977.html) ,&lt;br /&gt;
* Mit der Webseite des Projekts (https://web.archive.org/web/20190906140125/http://caspian.dotconf.net/menu/Software/SendEmail); diese ist nicht mehr verfügbar und nur eine archivierte Kopie kann eingesehen werden,&lt;br /&gt;
&lt;br /&gt;
weswegen diese Lösung als technisch überholt gelten sollte. Will man trotzdem &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; verwenden folgenden hier die Informationen dazu:&lt;br /&gt;
&lt;br /&gt;
Auf dem Server muss &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; installiert werden:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt-get update&lt;br /&gt;
sudo apt-get install sendemail libio-socket-ssl-perl libnet-ssleay-perl perl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Man kann die korrekte Funktion im Terminal (ohne &amp;lt;code&amp;gt;{qx()}&amp;lt;/code&amp;gt; ) oder auch in der FHEM Kommandozeile testen (Hinweis: manche Provider (z.B. gmail) lassen diesen Zugang erst nach Freischaltung zu!).&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Bei Gmail muss seit dem 30.6.2022 ein separates Passwort erzeugt werden, die alte Einstellung über &amp;quot;Zugriff durch weniger sichere Apps&amp;quot; funktioniert nicht mehr. Siehe Support Seite Google: &amp;lt;nowiki&amp;gt;https://support.google.com/accounts/answer/185833&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Beispiel mit Gmail über Linux Terminalfenster:&#039;&#039;&lt;br /&gt;
 sendemail -f &#039;Der.Absender@gmail.com&#039; -t &#039;Der.Empfaenger@gmail.com&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt; -s &#039;smtp.gmail.com:587&#039; -xu &#039;Der.Absender&#039; -xp DASSUPERGEHEIMEPASSW0RT&lt;br /&gt;
In der [[99 myUtils anlegen|99_myUtils]] folgende Unterroutine einfügen:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
######## DebianMail  Mail auf dem RPi versenden ############ &lt;br /&gt;
# $provider für SMTP Server anpassen. &lt;br /&gt;
# Einmal in der FHEM Kommandozeile, user und password anpassen:&lt;br /&gt;
# {setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&lt;br /&gt;
sub DebianMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $attach = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $error;&lt;br /&gt;
 my $konto = getKeyValue(&amp;quot;myEmailKonto&amp;quot;); &lt;br /&gt;
 my $passwrd = getKeyValue(&amp;quot;myEmailPasswrd&amp;quot;); &lt;br /&gt;
 my $from = $konto; # or use different KeyValue if konto is not the from email address&lt;br /&gt;
 my $provider = &amp;quot;smtp.1und1.de&amp;quot;; # smtp.domain.tld:port see provider documentation&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail RCP: $rcpt&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Subject: $subject&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Text: $text&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Anhang: $attach&amp;quot;;&lt;br /&gt;
 if (not defined($attach)){$attach=&#039;&#039;}&lt;br /&gt;
 $ret .= qx(sendemail -f &#039;$from&#039; -t &#039;$rcpt&#039; -u &#039;$subject&#039; -m &#039;$text&#039; -a $attach -s &#039;$provider&#039; -xu &#039;$konto&#039; -xp &#039;$passwrd&#039; -o tls=auto -o message-charset=utf-8);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendemail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
###############################################################################&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Die Zugangsdaten für den Email-Account müssen dabei FHEM-intern gespeichert werden. Dazu gibt man einmalig über die Kommandozeile folgendes ein (user@domain und password müssen auf den Email Account geändert werden):&lt;br /&gt;
&lt;br /&gt;
:  &amp;lt;code&amp;gt;{setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die TLS Verschlüsselung (ehem. SSL) nicht zu nutzen, muss in der viertletzten Zeile tls=no verwendet und der Port des Mailproviders auf 25 eigetragen werden. Sollte anschließend keine Mail verschickt werden, siehe Probleme.&lt;br /&gt;
&lt;br /&gt;
Falls der Body-Text in einem (Android-)Mailer auf dem Handy nicht gezeigt wird, kann der Parameter &#039;&#039;&#039;-o message-content-type=html&#039;&#039;&#039; helfen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion kann beispielsweise folgendermaßen aufgerufen werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{qx(sendemail -f &#039;Ab\@send.er&#039; -t &#039;Emp\@faeng.er&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &#039;DateinameOderLeer&#039; -s &#039;smtpFQDN:port&#039; -xu &#039;MailUser&#039; -xp &#039;PasswortOhneKlammeraffen&#039;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bei allen Feldern können generell doppelte &amp;quot; oder einfache &#039; Anführungszeichen verwendet werden. Innerhalb von &amp;quot;&amp;quot; müssen Sonderzeichen wie @ aber maskiert werden, da sie sonst als Steuerzeichen interpretiert werden:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;&amp;quot;email\@email.domain&amp;quot; oder &#039;email@email.domain&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direkt in der FHEM Kommandozeile (ohne Anhang)&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;{DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ordnungsgemäße Ausführung wird mit 71 quittiert.&lt;br /&gt;
&lt;br /&gt;
Beispiele mehrere Anhänge (Es ist wichtig Dateinamen mit Leerzeichen in separate Anführungszeichen zu setzen)&lt;br /&gt;
 {DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;Anhang1 Anhang2 Anhang3&amp;quot;)}&lt;br /&gt;
 {DebianMail(&#039;email@email.domain&#039;,&#039;Subject&#039;,&#039;Text&#039;,&amp;quot;&#039;with spaces Anhang1&#039; &#039;with spaces Anhang2&#039;&amp;quot;)}&lt;br /&gt;
oder (mehr als einen Empfänger, diese können durch Leerzeichen, Semikolon oder Komma getrennt sein)&lt;br /&gt;
 define Sonstiges at *01:00:00 {\&lt;br /&gt;
 DebianMail(&#039;email@email.domain,email2@email.domain&#039;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;&#039;Anhang1&#039; &#039;Anhang2&#039; ...&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Die archivierte Doku zu &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; befindet sich in der Änderungshistorie: https://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;amp;oldid=37524&lt;br /&gt;
[[Kategorie:E-Mail]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40132</id>
		<title>E-Mail senden</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40132"/>
		<updated>2025-05-04T16:56:53Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: strftime machte Probleme, Hinweis von @moskito in https://forum.fhem.de/index.php?topic=140814.msg1340788#msg1340788&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Um aus FHEM heraus E-Mails senden zu können, gibt es unterschiedliche Methoden, abhängig von der verwendeten Plattform.&lt;br /&gt;
&lt;br /&gt;
Lange Zeit war &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; sehr beliebt. Da aber die Homepage des Projekts nicht mehr online ist und Probleme nur noch via erhöhtem Supportaufwand gelöst werden, sollte diese Option als veraltet betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Eine Liste mit ressourcensparenden E-Mails Clients findet man bei dem OpenWRT Projekt: https://openwrt.org/docs/guide-user/services/email/smtp.client&lt;br /&gt;
&lt;br /&gt;
MSGMail ist ein FHEM eigenes Modul zum Versenden, die Einrichtung ist allerdings nicht gänzlich einfach. Anfängern sei die cURL Methode empfohlen.&lt;br /&gt;
&lt;br /&gt;
=== MSGMail (Linux, Windows) ===&lt;br /&gt;
FHEM bringt ein eigenes Device zum Senden von E-Mails mit, welches in Perl implementiert ist: [https://fhem.de/commandref.html#MSGMail MSGMail, https://fhem.de/commandref.html#MSGMail]&lt;br /&gt;
&lt;br /&gt;
=== cURL (Linux, Windows) ===&lt;br /&gt;
[https://curl.se/ cURL] kann auf praktisch [https://curl.se/download.html jedem Betriebssystem] genutzt werden, [https://curl.se/windows/ selbst unter Windows]. Es kann direkt mit einem SMTP Server E-Mails versenden und stellt damit eine moderne, portable und sichere Option zum Versenden von E-Mails dar.&lt;br /&gt;
&lt;br /&gt;
In FHEM richtet man sich ein Device ein und passt die Details (&amp;lt;code&amp;gt;$emailTo, $emailFrom, $emailServer&amp;lt;/code&amp;gt;) an. Das Programm cURL muss installiert sein (z.B. mit &amp;lt;code&amp;gt;apt install curl&amp;lt;/code&amp;gt;, oder durch Herunterladen und Installieren von der [https://curl.se cURL-Webseite]).&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod sendMail dummy&lt;br /&gt;
attr sendMail readingList message password&lt;br /&gt;
attr sendMail setList message:textField-long password&lt;br /&gt;
attr sendMail userReadings state:message:[\s\S]* {\&lt;br /&gt;
	my $emailTo   = &#039;du@example.com&#039;;;\&lt;br /&gt;
	my $emailFrom = &#039;fhem@example.de&#039;;;\&lt;br /&gt;
	my $emailServer = &#039;mail.your-server.de&#039;;;\&lt;br /&gt;
	\&lt;br /&gt;
	setlocale(LC_TIME, &amp;quot;C&amp;quot;);;\&lt;br /&gt;
	my $emailDate = strftime(&amp;quot;%a, %d %b %Y %H:%M:%S %z&amp;quot;, localtime(time()));;\&lt;br /&gt;
	\&lt;br /&gt;
	# Passwort aus getKeyValue abrufen\&lt;br /&gt;
	my ($err, $emailPass) = getKeyValue(&amp;quot;${name}_password&amp;quot;);;\&lt;br /&gt;
	if ($err || !defined $emailPass) {\&lt;br /&gt;
		return &amp;quot;Error retrieving password: $err&amp;quot;;;\&lt;br /&gt;
	}\&lt;br /&gt;
    \&lt;br /&gt;
	my $message = ReadingsVal($name, &#039;message&#039;, &#039;???&#039;);;\&lt;br /&gt;
	my $subject = &amp;quot;$name: FHEM Nachricht&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
	# Betreff extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
	if ($message =~ s/(?:Subject|Betreff)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
		$subject = $1;;\&lt;br /&gt;
	}\&lt;br /&gt;
    \&lt;br /&gt;
	# Empfänger extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
	if ($message =~ s/(?:To|An)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
		$emailTo = $1;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	#Dateianhänge bestimmen\&lt;br /&gt;
	my @attachments;;\&lt;br /&gt;
	while ($message =~ s/(?:Attachment|Anhang)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
		my $file = $1;;\&lt;br /&gt;
		return &amp;quot;&amp;gt;&amp;gt;$file&amp;lt;&amp;lt; not found, not sending email&amp;quot; unless (-e $file);;\&lt;br /&gt;
    	push (@attachments, $file);;\&lt;br /&gt;
	}\&lt;br /&gt;
    \&lt;br /&gt;
	#der curl Befehl wird hier aufgebaut:\&lt;br /&gt;
	my $cmd = &amp;quot;curl -m 19 --noproxy &#039;*&#039; --no-progress-meter --ssl-reqd smtps://$emailServer:465 &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;--user &#039;$emailFrom:$emailPass&#039; --mail-from &#039;$emailFrom&#039; --mail-rcpt &#039;$emailTo&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;--write-out &#039;Email sent with status %{http_code}\n&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-H &#039;Subject: $subject&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-H &#039;From: $emailFrom&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-H &#039;Date: $emailDate&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-H &#039;To: $emailTo&#039; &amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	#multipart/alternative für Text und HTML EMail Body\&lt;br /&gt;
	$cmd .= &amp;quot;-F &#039;=(;;type=multipart/alternative&#039; &amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	# Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Anfang entfernen:\&lt;br /&gt;
	$message =~ s/^(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+//;;\&lt;br /&gt;
	# Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Ende entfernen:\&lt;br /&gt;
	$message =~ s/(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+$//;;\&lt;br /&gt;
	\&lt;br /&gt;
	#Plaintext E-Mail Inhalt an cURL:\&lt;br /&gt;
	$cmd .= &amp;quot;-F \&amp;quot;=$message;;type=text/plain;; charset=UTF-8\&amp;quot; &amp;quot;;;\&lt;br /&gt;
	#ersetze \n bzw. \r\n durch ein HTML-tag &amp;lt;br /&amp;gt;:\&lt;br /&gt;
	$message =~ s/\r?\n/&amp;lt;br \/&amp;gt;/g;;\&lt;br /&gt;
	\&lt;br /&gt;
	#HTML E-Mail Inhalt an cURL:\&lt;br /&gt;
	$cmd .= &amp;quot;-F &#039;= &amp;lt;body&amp;gt;$message&amp;lt;/body&amp;gt;;;type=text/html;; charset=UTF-8&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-F &#039;=)&#039; &amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	#jetzt alle Attachments (0..N) anhängen:\&lt;br /&gt;
	foreach my $file (@attachments) {\&lt;br /&gt;
    	$cmd .= &amp;quot;-F &#039;=@&amp;quot;.$file.&amp;quot;;;encoder=base64&#039; &amp;quot;;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	#stderr auch mit in den output sammeln:\&lt;br /&gt;
	$cmd .= &amp;quot; 2&amp;gt;&amp;amp;1&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
	#Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:\&lt;br /&gt;
	if (!defined &amp;amp;sendMailfunc1) {\&lt;br /&gt;
		*sendMailfunc1 = sub ($) {\&lt;br /&gt;
			my ($param) = @_;;\&lt;br /&gt;
			my $result;;\&lt;br /&gt;
			$result = qx/$param/;;\&lt;br /&gt;
			$result = MIME::Base64::encode_base64($result);;\&lt;br /&gt;
			$result =~ s/\n//g;;\&lt;br /&gt;
			return $result;;\&lt;br /&gt;
		}\&lt;br /&gt;
	}\&lt;br /&gt;
    \&lt;br /&gt;
	#Rückgabe über diese Funktion,\&lt;br /&gt;
	#anzeigen in dem reading dieses UserReadings:\&lt;br /&gt;
	if (!defined &amp;amp;sendMailfunc2) {\&lt;br /&gt;
		*sendMailfunc2 = sub ($) {\&lt;br /&gt;
			my ($result) = @_;;\&lt;br /&gt;
			my $hash = $defs{$name};;\&lt;br /&gt;
			$result = MIME::Base64::decode_base64($result);;\&lt;br /&gt;
			readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
		}\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	#funktion falls BlockingCall abgebrochen wird:\&lt;br /&gt;
	if (!defined &amp;amp;sendMailfunc3) {\&lt;br /&gt;
		*sendMailfunc3 = sub ($) {\&lt;br /&gt;
			my ($result) = @_;;\&lt;br /&gt;
			my $hash = $defs{$name};;\&lt;br /&gt;
			readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
		}\&lt;br /&gt;
	}\&lt;br /&gt;
    \&lt;br /&gt;
	BlockingCall(&amp;quot;sendMailfunc1&amp;quot;, $cmd, &amp;quot;sendMailfunc2&amp;quot;, 20, &amp;quot;sendMailfunc3&amp;quot;, &amp;quot;Error: timeout&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return &amp;quot;started cURL command...&amp;quot;;;\&lt;br /&gt;
},\&lt;br /&gt;
state:password:.* {\&lt;br /&gt;
	# Passwort speichern\&lt;br /&gt;
	my $ret = setKeyValue(&amp;quot;${name}_password&amp;quot;, ReadingsVal($name, &#039;password&#039;, undef)) // &amp;quot;password stored&amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	#password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\&lt;br /&gt;
	#\&lt;br /&gt;
	# Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\&lt;br /&gt;
	# Alternativ: { setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;password&amp;quot;, &amp;quot;****&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return &amp;quot;$ret&amp;quot;;;\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Das Passwort setzt man mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail password geheimesPasswort&amp;lt;/code&amp;gt;&lt;br /&gt;
* Alternativ, damit das Passwort nicht im Log auftaucht: &amp;lt;code&amp;gt;{ setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
E-Mails kann man dann Senden mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Mein Text&amp;lt;/code&amp;gt; (es wird der Standardbetreff und Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;(es wird der Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Subject=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;✅ Dies ist ein Test&amp;quot; Meldung mit Emoji ⚽ geht auch&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eine Datei mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/bag.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zwei Dateien mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;./www/images/fhemSVG/bag.svg&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/batterie.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;, für die anzuhängenden Dateien können relative und absolute Pfade verwendet werden. Beim Vorbereiten der E-Mail wird geprüft ob die Dateien existieren, da dies sonst eine typische Fehlerquelle ist. Es wird nichts versendet wenn eine Datei nicht gefunden werden kann.&lt;br /&gt;
&lt;br /&gt;
==== Fehlersuche ====&lt;br /&gt;
&lt;br /&gt;
===== Fehlende Protokolloption in cURL =====&lt;br /&gt;
Sollte cURL zwar installiert sein, aber keine E-Mails versenden, kann es sein, dass das Protokoll nicht einkompiliert wurde. Dies ist bei OpenWRT zum Beispiel der Fall.&lt;br /&gt;
&lt;br /&gt;
So sollte es aussehen (hier ein Ubuntu), relevant ist für diese Nutzung das Protokoll smtp und smtps, das braucht man:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.7&lt;br /&gt;
Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.6&lt;br /&gt;
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp&lt;br /&gt;
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
So sollte es nicht aussehen (hier ein OpenWRT):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl: (1) Protocol &amp;quot;smtps&amp;quot; not supported&lt;br /&gt;
&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.10.1 (arm-openwrt-linux-gnu) libcurl/8.10.1 mbedTLS/3.6.2 nghttp2/1.63.0&lt;br /&gt;
Release-Date: 2024-09-18&lt;br /&gt;
Protocols: file ftp ftps http https ipfs ipns mqtt&lt;br /&gt;
Features: alt-svc HSTS HTTP2 HTTPS-proxy IPv6 Largefile SSL threadsafe UnixSockets&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Hat man nur ein cURL ohne das Protokoll, so muss man sich das Paket leider anderweitig beschaffen (chroot, selbst kompilieren, statisch gelinkte Version, ...).&lt;br /&gt;
&lt;br /&gt;
===== Falscher Port, login options =====&lt;br /&gt;
Einige Provider nehmen ihre TLS verschlüsselten E-Mails über einen anderen Port an, häufig Port 587 (siehe auch {{Link2Forum|Topic= 140814|Message=1340366|LinkText=diesen Forenbeitrag}}). Ggf. muss man auch mit einigen Loginoptionen experimentieren bis cURL  wie gewünscht funktioniert (Stichwort: &#039;&#039;[https://curl.se/docs/manpage.html#--login-options --login-options]&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
=== EXIM4 (Debian) ===&lt;br /&gt;
Eine Anleitung zum Versenden von E-Mails mit Exim ist als PDF verfügbar: [[Medium:Anleitung Exim4 Debian GMX.pdf|PDF]]&lt;br /&gt;
&lt;br /&gt;
=== SSMTP (OpenWRT, embedded Distros) ===&lt;br /&gt;
&lt;br /&gt;
==== Installation ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install ssmtp_2.64-4_mpc85xx.ipk  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Damit &amp;lt;code&amp;gt;ssmtp&amp;lt;/code&amp;gt; funktioniert müssen die Dateien &amp;lt;code&amp;gt;/etc/ssmtp/ssmtp.conf&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;/etc/ssmtp/revaliases&amp;lt;/code&amp;gt; angepasst werden.&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/ssmtp.conf &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root=arnold@gmx.net&lt;br /&gt;
 mailhub=mail.gmx.net:465&lt;br /&gt;
 rewriteDomain=gmx.net&lt;br /&gt;
 hostname=gmx.net&lt;br /&gt;
 FromLineOverride=YES&lt;br /&gt;
 UseTLS=YES&lt;br /&gt;
 #UseSTARTTLS=YES&lt;br /&gt;
 AuthUser=arnold@gmx.net&lt;br /&gt;
 AuthPass=Passwort_von_arnold@gmx.net&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/revaliases&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root:arnold@gmx.net:mail.gmx.net:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der [[99_myUtils_anlegen|99_myUtils]] folgende Unterroutine einfügen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub OpenWRTMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $sender = &amp;quot;dockstar\@heye-tammo.de&amp;quot;; &lt;br /&gt;
 Log 1, &amp;quot;sendEmail RCP: $rcpt, Subject: $subject, Text: $text&amp;quot;;&lt;br /&gt;
 $ret .= qx(echo -e &#039;to:$rcpt\n from:$sender\nsubject:$subject\n$text\n&#039; | ssmtp $rcpt);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendEmail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== PHP Mail Funktion (Synology DiskStation) ===&lt;br /&gt;
Beim DSM 3.2 funktioniert der &amp;lt;code&amp;gt;php-mail&amp;lt;/code&amp;gt;-Befehl, so dass man mittels folgendem Modul E-Mails versenden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub sendmail($$$) {&lt;br /&gt;
 my($empf, $subj, $nachricht) = @_;&lt;br /&gt;
 system(&amp;quot;php -r &#039;mail(\&amp;quot;$empf\&amp;quot;,\&amp;quot;$subj\&amp;quot;,\&amp;quot;$nachricht\&amp;quot;);&#039;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sendemail (Veraltet, Debian-basierende Distros) ===&lt;br /&gt;
Es handelt sich um ein Perl Programm, dass man aus den Paketquellen installieren kann.&lt;br /&gt;
&lt;br /&gt;
Es gibt Probleme:&lt;br /&gt;
&lt;br /&gt;
* mit der TLS bzw STARTTLS Verschlüsselung (https://github.com/fhem/fhem-docker/issues/254, https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1915977.html) ,&lt;br /&gt;
* Mit der Webseite des Projekts (https://web.archive.org/web/20190906140125/http://caspian.dotconf.net/menu/Software/SendEmail); diese ist nicht mehr verfügbar und nur eine archivierte Kopie kann eingesehen werden,&lt;br /&gt;
&lt;br /&gt;
weswegen diese Lösung als technisch überholt gelten sollte. Will man trotzdem &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; verwenden folgenden hier die Informationen dazu:&lt;br /&gt;
&lt;br /&gt;
Auf dem Server muss &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; installiert werden:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt-get update&lt;br /&gt;
sudo apt-get install sendemail libio-socket-ssl-perl libnet-ssleay-perl perl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Man kann die korrekte Funktion im Terminal (ohne &amp;lt;code&amp;gt;{qx()}&amp;lt;/code&amp;gt; ) oder auch in der FHEM Kommandozeile testen (Hinweis: manche Provider (z.B. gmail) lassen diesen Zugang erst nach Freischaltung zu!).&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Bei Gmail muss seit dem 30.6.2022 ein separates Passwort erzeugt werden, die alte Einstellung über &amp;quot;Zugriff durch weniger sichere Apps&amp;quot; funktioniert nicht mehr. Siehe Support Seite Google: &amp;lt;nowiki&amp;gt;https://support.google.com/accounts/answer/185833&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Beispiel mit Gmail über Linux Terminalfenster:&#039;&#039;&lt;br /&gt;
 sendemail -f &#039;Der.Absender@gmail.com&#039; -t &#039;Der.Empfaenger@gmail.com&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt; -s &#039;smtp.gmail.com:587&#039; -xu &#039;Der.Absender&#039; -xp DASSUPERGEHEIMEPASSW0RT&lt;br /&gt;
In der [[99 myUtils anlegen|99_myUtils]] folgende Unterroutine einfügen:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
######## DebianMail  Mail auf dem RPi versenden ############ &lt;br /&gt;
# $provider für SMTP Server anpassen. &lt;br /&gt;
# Einmal in der FHEM Kommandozeile, user und password anpassen:&lt;br /&gt;
# {setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&lt;br /&gt;
sub DebianMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $attach = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $error;&lt;br /&gt;
 my $konto = getKeyValue(&amp;quot;myEmailKonto&amp;quot;); &lt;br /&gt;
 my $passwrd = getKeyValue(&amp;quot;myEmailPasswrd&amp;quot;); &lt;br /&gt;
 my $from = $konto; # or use different KeyValue if konto is not the from email address&lt;br /&gt;
 my $provider = &amp;quot;smtp.1und1.de&amp;quot;; # smtp.domain.tld:port see provider documentation&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail RCP: $rcpt&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Subject: $subject&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Text: $text&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Anhang: $attach&amp;quot;;&lt;br /&gt;
 if (not defined($attach)){$attach=&#039;&#039;}&lt;br /&gt;
 $ret .= qx(sendemail -f &#039;$from&#039; -t &#039;$rcpt&#039; -u &#039;$subject&#039; -m &#039;$text&#039; -a $attach -s &#039;$provider&#039; -xu &#039;$konto&#039; -xp &#039;$passwrd&#039; -o tls=auto -o message-charset=utf-8);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendemail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
###############################################################################&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Die Zugangsdaten für den Email-Account müssen dabei FHEM-intern gespeichert werden. Dazu gibt man einmalig über die Kommandozeile folgendes ein (user@domain und password müssen auf den Email Account geändert werden):&lt;br /&gt;
&lt;br /&gt;
:  &amp;lt;code&amp;gt;{setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die TLS Verschlüsselung (ehem. SSL) nicht zu nutzen, muss in der viertletzten Zeile tls=no verwendet und der Port des Mailproviders auf 25 eigetragen werden. Sollte anschließend keine Mail verschickt werden, siehe Probleme.&lt;br /&gt;
&lt;br /&gt;
Falls der Body-Text in einem (Android-)Mailer auf dem Handy nicht gezeigt wird, kann der Parameter &#039;&#039;&#039;-o message-content-type=html&#039;&#039;&#039; helfen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion kann beispielsweise folgendermaßen aufgerufen werden:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{qx(sendemail -f &#039;Ab\@send.er&#039; -t &#039;Emp\@faeng.er&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &#039;DateinameOderLeer&#039; -s &#039;smtpFQDN:port&#039; -xu &#039;MailUser&#039; -xp &#039;PasswortOhneKlammeraffen&#039;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Bei allen Feldern können generell doppelte &amp;quot; oder einfache &#039; Anführungszeichen verwendet werden. Innerhalb von &amp;quot;&amp;quot; müssen Sonderzeichen wie @ aber maskiert werden, da sie sonst als Steuerzeichen interpretiert werden:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;&amp;quot;email\@email.domain&amp;quot; oder &#039;email@email.domain&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direkt in der FHEM Kommandozeile (ohne Anhang)&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;{DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ordnungsgemäße Ausführung wird mit 71 quittiert.&lt;br /&gt;
&lt;br /&gt;
Beispiele mehrere Anhänge (Es ist wichtig Dateinamen mit Leerzeichen in separate Anführungszeichen zu setzen)&lt;br /&gt;
 {DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;Anhang1 Anhang2 Anhang3&amp;quot;)}&lt;br /&gt;
 {DebianMail(&#039;email@email.domain&#039;,&#039;Subject&#039;,&#039;Text&#039;,&amp;quot;&#039;with spaces Anhang1&#039; &#039;with spaces Anhang2&#039;&amp;quot;)}&lt;br /&gt;
oder (mehr als einen Empfänger, diese können durch Leerzeichen, Semikolon oder Komma getrennt sein)&lt;br /&gt;
 define Sonstiges at *01:00:00 {\&lt;br /&gt;
 DebianMail(&#039;email@email.domain,email2@email.domain&#039;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;&#039;Anhang1&#039; &#039;Anhang2&#039; ...&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Die archivierte Doku zu &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; befindet sich in der Änderungshistorie: https://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;amp;oldid=37524&lt;br /&gt;
[[Kategorie:E-Mail]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40126</id>
		<title>E-Mail senden</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40126"/>
		<updated>2025-04-29T19:46:42Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Hinweise aus https://forum.fhem.de/index.php?topic=140814.msg1340366#msg1340366&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Um aus FHEM heraus E-Mails senden zu können, gibt es unterschiedliche Methoden, abhängig von der verwendeten Plattform.&lt;br /&gt;
&lt;br /&gt;
Lange Zeit war &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; sehr beliebt. Da aber die Homepage des Projekts nicht mehr online ist und Probleme nur noch via erhöhtem Supportaufwand gelöst werden, sollte diese Option als veraltet betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Eine Liste mit ressourcensparenden E-Mails Clients findet man bei dem OpenWRT Projekt: https://openwrt.org/docs/guide-user/services/email/smtp.client&lt;br /&gt;
&lt;br /&gt;
MSGMail ist ein FHEM eigenes Modul zum Versenden, die Einrichtung ist allerdings nicht gänzlich einfach. Anfängern sei die cURL Methode empfohlen.&lt;br /&gt;
&lt;br /&gt;
=== MSGMail (Linux, Windows) ===&lt;br /&gt;
FHEM bringt ein eigenes Device zum Senden von E-Mails mit, welches in Perl implementiert ist: [https://fhem.de/commandref.html#MSGMail MSGMail, https://fhem.de/commandref.html#MSGMail]&lt;br /&gt;
&lt;br /&gt;
=== cURL (Linux, Windows) ===&lt;br /&gt;
[https://curl.se/ cURL] kann auf praktisch [https://curl.se/download.html jedem Betriebssystem] genutzt werden, [https://curl.se/windows/ selbst unter Windows]. Es kann direkt mit einem SMTP Server E-Mails versenden und stellt damit eine moderne, portable und sichere Option zum Versenden von E-Mails dar.&lt;br /&gt;
&lt;br /&gt;
In FHEM richtet man sich ein Device ein und passt die Details (&amp;lt;code&amp;gt;$emailTo, $emailFrom, $emailServer&amp;lt;/code&amp;gt;) an. Das Programm cURL muss installiert sein (z.B. mit &amp;lt;code&amp;gt;apt install curl&amp;lt;/code&amp;gt;, oder durch Herunterladen und Installieren von der [https://curl.se cURL-Webseite]).&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod sendMail dummy&lt;br /&gt;
attr sendMail readingList message password&lt;br /&gt;
attr sendMail setList message:textField-long password&lt;br /&gt;
attr sendMail userReadings state:message:[\s\S]* {\&lt;br /&gt;
	my $emailTo   = &#039;du@example.com&#039;;;\&lt;br /&gt;
	my $emailFrom = &#039;fhem@example.de&#039;;;\&lt;br /&gt;
	my $emailServer = &#039;mail.your-server.de&#039;;;\&lt;br /&gt;
	my $emailDate = strftime(&amp;quot;%a, %d %b %Y %H:%M:%S %z&amp;quot;, localtime(time()));;\&lt;br /&gt;
	\&lt;br /&gt;
	# Passwort aus getKeyValue abrufen\&lt;br /&gt;
	my ($err, $emailPass) = getKeyValue(&amp;quot;${name}_password&amp;quot;);;\&lt;br /&gt;
	if ($err || !defined $emailPass) {\&lt;br /&gt;
		return &amp;quot;Error retrieving password: $err&amp;quot;;;\&lt;br /&gt;
	}\&lt;br /&gt;
    \&lt;br /&gt;
	my $message = ReadingsVal($name, &#039;message&#039;, &#039;???&#039;);;\&lt;br /&gt;
	my $subject = &amp;quot;$name: FHEM Nachricht&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
	# Betreff extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
	if ($message =~ s/(?:Subject|Betreff)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
		$subject = $1;;\&lt;br /&gt;
	}\&lt;br /&gt;
    \&lt;br /&gt;
	# Empfänger extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
	if ($message =~ s/(?:To|An)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
		$emailTo = $1;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	#Dateianhänge bestimmen\&lt;br /&gt;
	my @attachments;;\&lt;br /&gt;
	while ($message =~ s/(?:Attachment|Anhang)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
		my $file = $1;;\&lt;br /&gt;
		return &amp;quot;&amp;gt;&amp;gt;$file&amp;lt;&amp;lt; not found, not sending email&amp;quot; unless (-e $file);;\&lt;br /&gt;
    	push (@attachments, $file);;\&lt;br /&gt;
	}\&lt;br /&gt;
    \&lt;br /&gt;
	#der curl Befehl wird hier aufgebaut:\&lt;br /&gt;
	my $cmd = &amp;quot;curl -m 19 --noproxy &#039;*&#039; --no-progress-meter --ssl-reqd smtps://$emailServer:465 &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;--user &#039;$emailFrom:$emailPass&#039; --mail-from &#039;$emailFrom&#039; --mail-rcpt &#039;$emailTo&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;--write-out &#039;Email sent with status %{http_code}\n&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-H &#039;Subject: $subject&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-H &#039;From: $emailFrom&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-H &#039;Date: $emailDate&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-H &#039;To: $emailTo&#039; &amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	#multipart/alternative für Text und HTML EMail Body\&lt;br /&gt;
	$cmd .= &amp;quot;-F &#039;=(;;type=multipart/alternative&#039; &amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	# Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Anfang entfernen:\&lt;br /&gt;
	$message =~ s/^(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+//;;\&lt;br /&gt;
	# Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Ende entfernen:\&lt;br /&gt;
	$message =~ s/(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+$//;;\&lt;br /&gt;
	\&lt;br /&gt;
	#Plaintext E-Mail Inhalt an cURL:\&lt;br /&gt;
	$cmd .= &amp;quot;-F \&amp;quot;=$message;;type=text/plain;; charset=UTF-8\&amp;quot; &amp;quot;;;\&lt;br /&gt;
	#ersetze \n bzw. \r\n durch ein HTML-tag &amp;lt;br /&amp;gt;:\&lt;br /&gt;
	$message =~ s/\r?\n/&amp;lt;br \/&amp;gt;/g;;\&lt;br /&gt;
	\&lt;br /&gt;
	#HTML E-Mail Inhalt an cURL:\&lt;br /&gt;
	$cmd .= &amp;quot;-F &#039;= &amp;lt;body&amp;gt;$message&amp;lt;/body&amp;gt;;;type=text/html;; charset=UTF-8&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-F &#039;=)&#039; &amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	#jetzt alle Attachments (0..N) anhängen:\&lt;br /&gt;
	foreach my $file (@attachments) {\&lt;br /&gt;
    	$cmd .= &amp;quot;-F &#039;=@&amp;quot;.$file.&amp;quot;;;encoder=base64&#039; &amp;quot;;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	#stderr auch mit in den output sammeln:\&lt;br /&gt;
	$cmd .= &amp;quot; 2&amp;gt;&amp;amp;1&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
	#Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:\&lt;br /&gt;
	if (!defined &amp;amp;sendMailfunc1) {\&lt;br /&gt;
		*sendMailfunc1 = sub ($) {\&lt;br /&gt;
			my ($param) = @_;;\&lt;br /&gt;
			my $result;;\&lt;br /&gt;
			$result = qx/$param/;;\&lt;br /&gt;
			$result = MIME::Base64::encode_base64($result);;\&lt;br /&gt;
			$result =~ s/\n//g;;\&lt;br /&gt;
			return $result;;\&lt;br /&gt;
		}\&lt;br /&gt;
	}\&lt;br /&gt;
    \&lt;br /&gt;
	#Rückgabe über diese Funktion,\&lt;br /&gt;
	#anzeigen in dem reading dieses UserReadings:\&lt;br /&gt;
	if (!defined &amp;amp;sendMailfunc2) {\&lt;br /&gt;
		*sendMailfunc2 = sub ($) {\&lt;br /&gt;
			my ($result) = @_;;\&lt;br /&gt;
			my $hash = $defs{$name};;\&lt;br /&gt;
			$result = MIME::Base64::decode_base64($result);;\&lt;br /&gt;
			readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
		}\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	#funktion falls BlockingCall abgebrochen wird:\&lt;br /&gt;
	if (!defined &amp;amp;sendMailfunc3) {\&lt;br /&gt;
		*sendMailfunc3 = sub ($) {\&lt;br /&gt;
			my ($result) = @_;;\&lt;br /&gt;
			my $hash = $defs{$name};;\&lt;br /&gt;
			readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
		}\&lt;br /&gt;
	}\&lt;br /&gt;
    \&lt;br /&gt;
	BlockingCall(&amp;quot;sendMailfunc1&amp;quot;, $cmd, &amp;quot;sendMailfunc2&amp;quot;, 20, &amp;quot;sendMailfunc3&amp;quot;, &amp;quot;Error: timeout&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return &amp;quot;started cURL command...&amp;quot;;;\&lt;br /&gt;
},\&lt;br /&gt;
state:password:.* {\&lt;br /&gt;
	# Passwort speichern\&lt;br /&gt;
	my $ret = setKeyValue(&amp;quot;${name}_password&amp;quot;, ReadingsVal($name, &#039;password&#039;, undef)) // &amp;quot;password stored&amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	#password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\&lt;br /&gt;
	#\&lt;br /&gt;
	# Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\&lt;br /&gt;
	# Alternativ: { setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;password&amp;quot;, &amp;quot;****&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return &amp;quot;$ret&amp;quot;;;\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Das Passwort setzt man mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail password geheimesPasswort&amp;lt;/code&amp;gt;&lt;br /&gt;
* Alternativ, damit das Passwort nicht im Log auftaucht: &amp;lt;code&amp;gt;{ setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
E-Mails kann man dann Senden mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Mein Text&amp;lt;/code&amp;gt; (es wird der Standardbetreff und Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;(es wird der Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Subject=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;✅ Dies ist ein Test&amp;quot; Meldung mit Emoji ⚽ geht auch&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eine Datei mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/bag.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zwei Dateien mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;./www/images/fhemSVG/bag.svg&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/batterie.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;, für die anzuhängenden Dateien können relative und absolute Pfade verwendet werden. Beim Vorbereiten der E-Mail wird geprüft ob die Dateien existieren, da dies sonst eine typische Fehlerquelle ist. Es wird nichts versendet wenn eine Datei nicht gefunden werden kann.&lt;br /&gt;
&lt;br /&gt;
==== Fehlersuche ====&lt;br /&gt;
&lt;br /&gt;
===== Fehlende Protokolloption in cURL =====&lt;br /&gt;
Sollte cURL zwar installiert sein, aber keine E-Mails versenden, kann es sein, dass das Protokoll nicht einkompiliert wurde. Dies ist bei OpenWRT zum Beispiel der Fall.&lt;br /&gt;
&lt;br /&gt;
So sollte es aussehen (hier ein Ubuntu), relevant ist für diese Nutzung das Protokoll smtp und smtps, das braucht man:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.7&lt;br /&gt;
Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.6&lt;br /&gt;
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp&lt;br /&gt;
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;So sollte es nicht aussehen (hier ein OpenWRT):&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl: (1) Protocol &amp;quot;smtps&amp;quot; not supported&lt;br /&gt;
&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.10.1 (arm-openwrt-linux-gnu) libcurl/8.10.1 mbedTLS/3.6.2 nghttp2/1.63.0&lt;br /&gt;
Release-Date: 2024-09-18&lt;br /&gt;
Protocols: file ftp ftps http https ipfs ipns mqtt&lt;br /&gt;
Features: alt-svc HSTS HTTP2 HTTPS-proxy IPv6 Largefile SSL threadsafe UnixSockets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hat man nur ein cURL ohne das Protokoll, so muss man sich das Paket leider anderweitig beschaffen (chroot, selbst kompilieren, statisch gelinkte Version, ...).&lt;br /&gt;
&lt;br /&gt;
===== Falscher Port, login options =====&lt;br /&gt;
Einige Provider nehmen ihre TLS verschlüsselten E-Mails über einen anderen Port an, häufig Port 587 (siehe auch https://forum.fhem.de/index.php?topic=140814.msg1340366#msg1340366). Ggf. muss man auch mit einigen Loginoptionen experimentieren bis cURL  wie gewünscht funktioniert (Stichwort: &#039;&#039;[https://curl.se/docs/manpage.html#--login-options --login-options]&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
==== Forumlink ====&lt;br /&gt;
https://forum.fhem.de/index.php?topic=140814.0&lt;br /&gt;
&lt;br /&gt;
=== EXIM4 (Debian) ===&lt;br /&gt;
Eine Anleitung zum Versenden von E-Mails mit Exim ist als PDF verfügbar: [[Medium:Anleitung Exim4 Debian GMX.pdf|PDF]]&lt;br /&gt;
&lt;br /&gt;
=== SSMTP (OpenWRT, embedded Distros) ===&lt;br /&gt;
&lt;br /&gt;
==== Installation ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install ssmtp_2.64-4_mpc85xx.ipk  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Damit &amp;lt;code&amp;gt;ssmtp&amp;lt;/code&amp;gt; funktioniert müssen die Dateien &amp;lt;code&amp;gt;/etc/ssmtp/ssmtp.conf&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;/etc/ssmtp/revaliases&amp;lt;/code&amp;gt; angepasst werden.&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/ssmtp.conf &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root=arnold@gmx.net&lt;br /&gt;
 mailhub=mail.gmx.net:465&lt;br /&gt;
 rewriteDomain=gmx.net&lt;br /&gt;
 hostname=gmx.net&lt;br /&gt;
 FromLineOverride=YES&lt;br /&gt;
 UseTLS=YES&lt;br /&gt;
 #UseSTARTTLS=YES&lt;br /&gt;
 AuthUser=arnold@gmx.net&lt;br /&gt;
 AuthPass=Passwort_von_arnold@gmx.net&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/revaliases&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root:arnold@gmx.net:mail.gmx.net:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der [[99_myUtils_anlegen|99_myUtils]] folgende Unterroutine einfügen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub OpenWRTMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $sender = &amp;quot;dockstar\@heye-tammo.de&amp;quot;; &lt;br /&gt;
 Log 1, &amp;quot;sendEmail RCP: $rcpt, Subject: $subject, Text: $text&amp;quot;;&lt;br /&gt;
 $ret .= qx(echo -e &#039;to:$rcpt\n from:$sender\nsubject:$subject\n$text\n&#039; | ssmtp $rcpt);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendEmail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== PHP Mail Funktion (Synology DiskStation) ===&lt;br /&gt;
Beim DSM 3.2 funktioniert der &amp;lt;code&amp;gt;php-mail&amp;lt;/code&amp;gt;-Befehl, so dass man mittels folgendem Modul E-Mails versenden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub sendmail($$$) {&lt;br /&gt;
 my($empf, $subj, $nachricht) = @_;&lt;br /&gt;
 system(&amp;quot;php -r &#039;mail(\&amp;quot;$empf\&amp;quot;,\&amp;quot;$subj\&amp;quot;,\&amp;quot;$nachricht\&amp;quot;);&#039;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sendemail (Veraltet, Debian-basierende Distros) ===&lt;br /&gt;
Es handelt sich um ein Perl Programm, dass man aus den Paketquellen installieren kann.&lt;br /&gt;
&lt;br /&gt;
Es gibt Probleme:&lt;br /&gt;
&lt;br /&gt;
* mit der TLS bzw STARTTLS Verschlüsselung (https://github.com/fhem/fhem-docker/issues/254, https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1915977.html) ,&lt;br /&gt;
* Mit der Webseite des Projekts (https://web.archive.org/web/20190906140125/http://caspian.dotconf.net/menu/Software/SendEmail); diese ist nicht mehr verfügbar und nur eine archivierte Kopie kann eingesehen werden,&lt;br /&gt;
&lt;br /&gt;
weswegen diese Lösung als technisch überholt gelten sollte. Will man trotzdem &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; verwenden folgenden hier die Informationen dazu:&lt;br /&gt;
&lt;br /&gt;
Auf dem Server muss &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; installiert werden:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt-get update&lt;br /&gt;
sudo apt-get install sendemail libio-socket-ssl-perl libnet-ssleay-perl perl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Man kann die korrekte Funktion im Terminal (ohne &amp;lt;code&amp;gt;{qx()}&amp;lt;/code&amp;gt; ) oder auch in der FHEM Kommandozeile testen (Hinweis: manche Provider (z.B. gmail) lassen diesen Zugang erst nach Freischaltung zu!).&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Bei Gmail muss seit dem 30.6.2022 ein separates Passwort erzeugt werden, die alte Einstellung über &amp;quot;Zugriff durch weniger sichere Apps&amp;quot; funktioniert nicht mehr. Siehe Support Seite Google: &amp;lt;nowiki&amp;gt;https://support.google.com/accounts/answer/185833&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Beispiel mit Gmail über Linux Terminalfenster:&#039;&#039;&lt;br /&gt;
 sendemail -f &#039;Der.Absender@gmail.com&#039; -t &#039;Der.Empfaenger@gmail.com&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt; -s &#039;smtp.gmail.com:587&#039; -xu &#039;Der.Absender&#039; -xp DASSUPERGEHEIMEPASSW0RT&lt;br /&gt;
In der [[99 myUtils anlegen|99_myUtils]] folgende Unterroutine einfügen:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
######## DebianMail  Mail auf dem RPi versenden ############ &lt;br /&gt;
# $provider für SMTP Server anpassen. &lt;br /&gt;
# Einmal in der FHEM Kommandozeile, user und password anpassen:&lt;br /&gt;
# {setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&lt;br /&gt;
sub DebianMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $attach = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $error;&lt;br /&gt;
 my $konto = getKeyValue(&amp;quot;myEmailKonto&amp;quot;); &lt;br /&gt;
 my $passwrd = getKeyValue(&amp;quot;myEmailPasswrd&amp;quot;); &lt;br /&gt;
 my $from = $konto; # or use different KeyValue if konto is not the from email address&lt;br /&gt;
 my $provider = &amp;quot;smtp.1und1.de&amp;quot;; # smtp.domain.tld:port see provider documentation&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail RCP: $rcpt&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Subject: $subject&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Text: $text&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Anhang: $attach&amp;quot;;&lt;br /&gt;
 if (not defined($attach)){$attach=&#039;&#039;}&lt;br /&gt;
 $ret .= qx(sendemail -f &#039;$from&#039; -t &#039;$rcpt&#039; -u &#039;$subject&#039; -m &#039;$text&#039; -a $attach -s &#039;$provider&#039; -xu &#039;$konto&#039; -xp &#039;$passwrd&#039; -o tls=auto -o message-charset=utf-8);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendemail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
###############################################################################&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Die Zugangsdaten für den Email-Account müssen dabei FHEM-intern gespeichert werden. Dazu gibt man einmalig über die Kommandozeile folgendes ein (user@domain und password müssen auf den Email Account geändert werden):&lt;br /&gt;
&lt;br /&gt;
:  &amp;lt;code&amp;gt;{setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die TLS Verschlüsselung (ehem. SSL) nicht zu nutzen, muss in der viertletzten Zeile tls=no verwendet und der Port des Mailproviders auf 25 eigetragen werden. Sollte anschließend keine Mail verschickt werden, siehe Probleme.&lt;br /&gt;
&lt;br /&gt;
Falls der Body-Text in einem (Android-)Mailer auf dem Handy nicht gezeigt wird, kann der Parameter &#039;&#039;&#039;-o message-content-type=html&#039;&#039;&#039; helfen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion kann beispielsweise folgendermaßen aufgerufen werden:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{qx(sendemail -f &#039;Ab\@send.er&#039; -t &#039;Emp\@faeng.er&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &#039;DateinameOderLeer&#039; -s &#039;smtpFQDN:port&#039; -xu &#039;MailUser&#039; -xp &#039;PasswortOhneKlammeraffen&#039;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Bei allen Feldern können generell doppelte &amp;quot; oder einfache &#039; Anführungszeichen verwendet werden. Innerhalb von &amp;quot;&amp;quot; müssen Sonderzeichen wie @ aber maskiert werden, da sie sonst als Steuerzeichen interpretiert werden:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;&amp;quot;email\@email.domain&amp;quot; oder &#039;email@email.domain&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direkt in der FHEM Kommandozeile (ohne Anhang)&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;{DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ordnungsgemäße Ausführung wird mit 71 quittiert.&lt;br /&gt;
&lt;br /&gt;
Beispiele mehrere Anhänge (Es ist wichtig Dateinamen mit Leerzeichen in separate Anführungszeichen zu setzen)&lt;br /&gt;
 {DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;Anhang1 Anhang2 Anhang3&amp;quot;)}&lt;br /&gt;
 {DebianMail(&#039;email@email.domain&#039;,&#039;Subject&#039;,&#039;Text&#039;,&amp;quot;&#039;with spaces Anhang1&#039; &#039;with spaces Anhang2&#039;&amp;quot;)}&lt;br /&gt;
oder (mehr als einen Empfänger, diese können durch Leerzeichen, Semikolon oder Komma getrennt sein)&lt;br /&gt;
 define Sonstiges at *01:00:00 {\&lt;br /&gt;
 DebianMail(&#039;email@email.domain,email2@email.domain&#039;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;&#039;Anhang1&#039; &#039;Anhang2&#039; ...&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Die archivierte Doku zu &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; befindet sich in der Änderungshistorie: https://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;amp;oldid=37524&lt;br /&gt;
[[Kategorie:E-Mail]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40125</id>
		<title>E-Mail senden</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40125"/>
		<updated>2025-04-29T19:38:22Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Hinweise von User bertl aus https://forum.fhem.de/index.php?topic=140814.msg1340245#msg1340245&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Um aus FHEM heraus E-Mails senden zu können, gibt es unterschiedliche Methoden, abhängig von der verwendeten Plattform.&lt;br /&gt;
&lt;br /&gt;
Lange Zeit war &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; sehr beliebt. Da aber die Homepage des Projekts nicht mehr online ist und Probleme nur noch via erhöhtem Supportaufwand gelöst werden, sollte diese Option als veraltet betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Eine Liste mit ressourcensparenden E-Mails Clients findet man bei dem OpenWRT Projekt: https://openwrt.org/docs/guide-user/services/email/smtp.client&lt;br /&gt;
&lt;br /&gt;
MSGMail ist ein FHEM eigenes Modul zum Versenden, die Einrichtung ist allerdings nicht gänzlich einfach. Anfängern sei die cURL Methode empfohlen.&lt;br /&gt;
&lt;br /&gt;
=== MSGMail (Linux, Windows) ===&lt;br /&gt;
FHEM bringt ein eigenes Device zum Senden von E-Mails mit, welches in Perl implementiert ist: [https://fhem.de/commandref.html#MSGMail MSGMail, https://fhem.de/commandref.html#MSGMail]&lt;br /&gt;
&lt;br /&gt;
=== cURL (Linux, Windows) ===&lt;br /&gt;
[https://curl.se/ cURL] kann auf praktisch [https://curl.se/download.html jedem Betriebssystem] genutzt werden, [https://curl.se/windows/ selbst unter Windows]. Es kann direkt mit einem SMTP Server E-Mails versenden und stellt damit eine moderne, portable und sichere Option zum Versenden von E-Mails dar.&lt;br /&gt;
&lt;br /&gt;
In FHEM richtet man sich ein Device ein und passt die Details (&amp;lt;code&amp;gt;$emailTo, $emailFrom, $emailServer&amp;lt;/code&amp;gt;) an. Das Programm cURL muss installiert sein (z.B. mit &amp;lt;code&amp;gt;apt install curl&amp;lt;/code&amp;gt;, oder durch Herunterladen und Installieren von der [https://curl.se cURL-Webseite]).&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod sendMail dummy&lt;br /&gt;
attr sendMail readingList message password&lt;br /&gt;
attr sendMail setList message:textField-long password&lt;br /&gt;
attr sendMail userReadings state:message:[\s\S]* {\&lt;br /&gt;
	my $emailTo   = &#039;du@example.com&#039;;;\&lt;br /&gt;
	my $emailFrom = &#039;fhem@example.de&#039;;;\&lt;br /&gt;
	my $emailServer = &#039;mail.your-server.de&#039;;;\&lt;br /&gt;
	my $emailDate = strftime(&amp;quot;%a, %d %b %Y %H:%M:%S %z&amp;quot;, localtime(time()));;\&lt;br /&gt;
	\&lt;br /&gt;
	# Passwort aus getKeyValue abrufen\&lt;br /&gt;
	my ($err, $emailPass) = getKeyValue(&amp;quot;${name}_password&amp;quot;);;\&lt;br /&gt;
	if ($err || !defined $emailPass) {\&lt;br /&gt;
		return &amp;quot;Error retrieving password: $err&amp;quot;;;\&lt;br /&gt;
	}\&lt;br /&gt;
    \&lt;br /&gt;
	my $message = ReadingsVal($name, &#039;message&#039;, &#039;???&#039;);;\&lt;br /&gt;
	my $subject = &amp;quot;$name: FHEM Nachricht&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
	# Betreff extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
	if ($message =~ s/(?:Subject|Betreff)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
		$subject = $1;;\&lt;br /&gt;
	}\&lt;br /&gt;
    \&lt;br /&gt;
	# Empfänger extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
	if ($message =~ s/(?:To|An)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
		$emailTo = $1;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	#Dateianhänge bestimmen\&lt;br /&gt;
	my @attachments;;\&lt;br /&gt;
	while ($message =~ s/(?:Attachment|Anhang)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
		my $file = $1;;\&lt;br /&gt;
		return &amp;quot;&amp;gt;&amp;gt;$file&amp;lt;&amp;lt; not found, not sending email&amp;quot; unless (-e $file);;\&lt;br /&gt;
    	push (@attachments, $file);;\&lt;br /&gt;
	}\&lt;br /&gt;
    \&lt;br /&gt;
	#der curl Befehl wird hier aufgebaut:\&lt;br /&gt;
	my $cmd = &amp;quot;curl -m 19 --noproxy &#039;*&#039; --no-progress-meter --ssl-reqd smtps://$emailServer:465 &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;--user &#039;$emailFrom:$emailPass&#039; --mail-from &#039;$emailFrom&#039; --mail-rcpt &#039;$emailTo&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;--write-out &#039;Email sent with status %{http_code}\n&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-H &#039;Subject: $subject&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-H &#039;From: $emailFrom&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-H &#039;Date: $emailDate&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-H &#039;To: $emailTo&#039; &amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	#multipart/alternative für Text und HTML EMail Body\&lt;br /&gt;
	$cmd .= &amp;quot;-F &#039;=(;;type=multipart/alternative&#039; &amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	# Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Anfang entfernen:\&lt;br /&gt;
	$message =~ s/^(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+//;;\&lt;br /&gt;
	# Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Ende entfernen:\&lt;br /&gt;
	$message =~ s/(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+$//;;\&lt;br /&gt;
	\&lt;br /&gt;
	#Plaintext E-Mail Inhalt an cURL:\&lt;br /&gt;
	$cmd .= &amp;quot;-F \&amp;quot;=$message;;type=text/plain;; charset=UTF-8\&amp;quot; &amp;quot;;;\&lt;br /&gt;
	#ersetze \n bzw. \r\n durch ein HTML-tag &amp;lt;br /&amp;gt;:\&lt;br /&gt;
	$message =~ s/\r?\n/&amp;lt;br \/&amp;gt;/g;;\&lt;br /&gt;
	\&lt;br /&gt;
	#HTML E-Mail Inhalt an cURL:\&lt;br /&gt;
	$cmd .= &amp;quot;-F &#039;= &amp;lt;body&amp;gt;$message&amp;lt;/body&amp;gt;;;type=text/html;; charset=UTF-8&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-F &#039;=)&#039; &amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	#jetzt alle Attachments (0..N) anhängen:\&lt;br /&gt;
	foreach my $file (@attachments) {\&lt;br /&gt;
    	$cmd .= &amp;quot;-F &#039;=@&amp;quot;.$file.&amp;quot;;;encoder=base64&#039; &amp;quot;;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	#stderr auch mit in den output sammeln:\&lt;br /&gt;
	$cmd .= &amp;quot; 2&amp;gt;&amp;amp;1&amp;quot;;;\&lt;br /&gt;
    \&lt;br /&gt;
	#Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:\&lt;br /&gt;
	if (!defined &amp;amp;sendMailfunc1) {\&lt;br /&gt;
		*sendMailfunc1 = sub ($) {\&lt;br /&gt;
			my ($param) = @_;;\&lt;br /&gt;
			my $result;;\&lt;br /&gt;
			$result = qx/$param/;;\&lt;br /&gt;
			$result = MIME::Base64::encode_base64($result);;\&lt;br /&gt;
			$result =~ s/\n//g;;\&lt;br /&gt;
			return $result;;\&lt;br /&gt;
		}\&lt;br /&gt;
	}\&lt;br /&gt;
    \&lt;br /&gt;
	#Rückgabe über diese Funktion,\&lt;br /&gt;
	#anzeigen in dem reading dieses UserReadings:\&lt;br /&gt;
	if (!defined &amp;amp;sendMailfunc2) {\&lt;br /&gt;
		*sendMailfunc2 = sub ($) {\&lt;br /&gt;
			my ($result) = @_;;\&lt;br /&gt;
			my $hash = $defs{$name};;\&lt;br /&gt;
			$result = MIME::Base64::decode_base64($result);;\&lt;br /&gt;
			readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
		}\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	#funktion falls BlockingCall abgebrochen wird:\&lt;br /&gt;
	if (!defined &amp;amp;sendMailfunc3) {\&lt;br /&gt;
		*sendMailfunc3 = sub ($) {\&lt;br /&gt;
			my ($result) = @_;;\&lt;br /&gt;
			my $hash = $defs{$name};;\&lt;br /&gt;
			readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
		}\&lt;br /&gt;
	}\&lt;br /&gt;
    \&lt;br /&gt;
	BlockingCall(&amp;quot;sendMailfunc1&amp;quot;, $cmd, &amp;quot;sendMailfunc2&amp;quot;, 20, &amp;quot;sendMailfunc3&amp;quot;, &amp;quot;Error: timeout&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return &amp;quot;started cURL command...&amp;quot;;;\&lt;br /&gt;
},\&lt;br /&gt;
state:password:.* {\&lt;br /&gt;
	# Passwort speichern\&lt;br /&gt;
	my $ret = setKeyValue(&amp;quot;${name}_password&amp;quot;, ReadingsVal($name, &#039;password&#039;, undef)) // &amp;quot;password stored&amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	#password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\&lt;br /&gt;
	#\&lt;br /&gt;
	# Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\&lt;br /&gt;
	# Alternativ: { setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;password&amp;quot;, &amp;quot;****&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return &amp;quot;$ret&amp;quot;;;\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Das Passwort setzt man mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail password geheimesPasswort&amp;lt;/code&amp;gt;&lt;br /&gt;
* Alternativ, damit das Passwort nicht im Log auftaucht: &amp;lt;code&amp;gt;{ setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
E-Mails kann man dann Senden mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Mein Text&amp;lt;/code&amp;gt; (es wird der Standardbetreff und Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;(es wird der Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Subject=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;✅ Dies ist ein Test&amp;quot; Meldung mit Emoji ⚽ geht auch&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eine Datei mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/bag.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zwei Dateien mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;./www/images/fhemSVG/bag.svg&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/batterie.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;, für die anzuhängenden Dateien können relative und absolute Pfade verwendet werden. Beim Vorbereiten der E-Mail wird geprüft ob die Dateien existieren, da dies sonst eine typische Fehlerquelle ist. Es wird nichts versendet wenn eine Datei nicht gefunden werden kann.&lt;br /&gt;
&lt;br /&gt;
==== Fehlersuche ====&lt;br /&gt;
Sollte cURL zwar installiert sein, aber keine E-Mails versenden, kann es sein, dass das Protokoll nicht einkompiliert wurde. Dies ist bei OpenWRT zum Beispiel der Fall.&lt;br /&gt;
&lt;br /&gt;
So sollte es aussehen (hier ein Ubuntu), relevant ist für diese Nutzung das Protokoll smtp und smtps, das braucht man:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.7&lt;br /&gt;
Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.6&lt;br /&gt;
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp&lt;br /&gt;
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;So sollte es nicht aussehen (hier ein OpenWRT):&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl: (1) Protocol &amp;quot;smtps&amp;quot; not supported&lt;br /&gt;
&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.10.1 (arm-openwrt-linux-gnu) libcurl/8.10.1 mbedTLS/3.6.2 nghttp2/1.63.0&lt;br /&gt;
Release-Date: 2024-09-18&lt;br /&gt;
Protocols: file ftp ftps http https ipfs ipns mqtt&lt;br /&gt;
Features: alt-svc HSTS HTTP2 HTTPS-proxy IPv6 Largefile SSL threadsafe UnixSockets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hat man nur ein cURL ohne das Protokoll, so muss man sich das Paket leider anderweitig beschaffen (chroot, selbst kompilieren, statisch gelinkte Version, ...).&lt;br /&gt;
&lt;br /&gt;
==== Forumlink ====&lt;br /&gt;
https://forum.fhem.de/index.php?topic=140814.0&lt;br /&gt;
&lt;br /&gt;
=== EXIM4 (Debian) ===&lt;br /&gt;
Eine Anleitung zum Versenden von E-Mails mit Exim ist als PDF verfügbar: [[Medium:Anleitung Exim4 Debian GMX.pdf|PDF]]&lt;br /&gt;
&lt;br /&gt;
=== SSMTP (OpenWRT, embedded Distros) ===&lt;br /&gt;
&lt;br /&gt;
==== Installation ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install ssmtp_2.64-4_mpc85xx.ipk  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Damit &amp;lt;code&amp;gt;ssmtp&amp;lt;/code&amp;gt; funktioniert müssen die Dateien &amp;lt;code&amp;gt;/etc/ssmtp/ssmtp.conf&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;/etc/ssmtp/revaliases&amp;lt;/code&amp;gt; angepasst werden.&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/ssmtp.conf &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root=arnold@gmx.net&lt;br /&gt;
 mailhub=mail.gmx.net:465&lt;br /&gt;
 rewriteDomain=gmx.net&lt;br /&gt;
 hostname=gmx.net&lt;br /&gt;
 FromLineOverride=YES&lt;br /&gt;
 UseTLS=YES&lt;br /&gt;
 #UseSTARTTLS=YES&lt;br /&gt;
 AuthUser=arnold@gmx.net&lt;br /&gt;
 AuthPass=Passwort_von_arnold@gmx.net&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/revaliases&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root:arnold@gmx.net:mail.gmx.net:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der [[99_myUtils_anlegen|99_myUtils]] folgende Unterroutine einfügen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub OpenWRTMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $sender = &amp;quot;dockstar\@heye-tammo.de&amp;quot;; &lt;br /&gt;
 Log 1, &amp;quot;sendEmail RCP: $rcpt, Subject: $subject, Text: $text&amp;quot;;&lt;br /&gt;
 $ret .= qx(echo -e &#039;to:$rcpt\n from:$sender\nsubject:$subject\n$text\n&#039; | ssmtp $rcpt);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendEmail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== PHP Mail Funktion (Synology DiskStation) ===&lt;br /&gt;
Beim DSM 3.2 funktioniert der &amp;lt;code&amp;gt;php-mail&amp;lt;/code&amp;gt;-Befehl, so dass man mittels folgendem Modul E-Mails versenden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub sendmail($$$) {&lt;br /&gt;
 my($empf, $subj, $nachricht) = @_;&lt;br /&gt;
 system(&amp;quot;php -r &#039;mail(\&amp;quot;$empf\&amp;quot;,\&amp;quot;$subj\&amp;quot;,\&amp;quot;$nachricht\&amp;quot;);&#039;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sendemail (Veraltet, Debian-basierende Distros) ===&lt;br /&gt;
Es handelt sich um ein Perl Programm, dass man aus den Paketquellen installieren kann.&lt;br /&gt;
&lt;br /&gt;
Es gibt Probleme:&lt;br /&gt;
&lt;br /&gt;
* mit der TLS bzw STARTTLS Verschlüsselung (https://github.com/fhem/fhem-docker/issues/254, https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1915977.html) ,&lt;br /&gt;
* Mit der Webseite des Projekts (https://web.archive.org/web/20190906140125/http://caspian.dotconf.net/menu/Software/SendEmail); diese ist nicht mehr verfügbar und nur eine archivierte Kopie kann eingesehen werden,&lt;br /&gt;
&lt;br /&gt;
weswegen diese Lösung als technisch überholt gelten sollte. Will man trotzdem &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; verwenden folgenden hier die Informationen dazu:&lt;br /&gt;
&lt;br /&gt;
Auf dem Server muss &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; installiert werden:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt-get update&lt;br /&gt;
sudo apt-get install sendemail libio-socket-ssl-perl libnet-ssleay-perl perl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Man kann die korrekte Funktion im Terminal (ohne &amp;lt;code&amp;gt;{qx()}&amp;lt;/code&amp;gt; ) oder auch in der FHEM Kommandozeile testen (Hinweis: manche Provider (z.B. gmail) lassen diesen Zugang erst nach Freischaltung zu!).&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Bei Gmail muss seit dem 30.6.2022 ein separates Passwort erzeugt werden, die alte Einstellung über &amp;quot;Zugriff durch weniger sichere Apps&amp;quot; funktioniert nicht mehr. Siehe Support Seite Google: &amp;lt;nowiki&amp;gt;https://support.google.com/accounts/answer/185833&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Beispiel mit Gmail über Linux Terminalfenster:&#039;&#039;&lt;br /&gt;
 sendemail -f &#039;Der.Absender@gmail.com&#039; -t &#039;Der.Empfaenger@gmail.com&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt; -s &#039;smtp.gmail.com:587&#039; -xu &#039;Der.Absender&#039; -xp DASSUPERGEHEIMEPASSW0RT&lt;br /&gt;
In der [[99 myUtils anlegen|99_myUtils]] folgende Unterroutine einfügen:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
######## DebianMail  Mail auf dem RPi versenden ############ &lt;br /&gt;
# $provider für SMTP Server anpassen. &lt;br /&gt;
# Einmal in der FHEM Kommandozeile, user und password anpassen:&lt;br /&gt;
# {setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&lt;br /&gt;
sub DebianMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $attach = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $error;&lt;br /&gt;
 my $konto = getKeyValue(&amp;quot;myEmailKonto&amp;quot;); &lt;br /&gt;
 my $passwrd = getKeyValue(&amp;quot;myEmailPasswrd&amp;quot;); &lt;br /&gt;
 my $from = $konto; # or use different KeyValue if konto is not the from email address&lt;br /&gt;
 my $provider = &amp;quot;smtp.1und1.de&amp;quot;; # smtp.domain.tld:port see provider documentation&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail RCP: $rcpt&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Subject: $subject&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Text: $text&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Anhang: $attach&amp;quot;;&lt;br /&gt;
 if (not defined($attach)){$attach=&#039;&#039;}&lt;br /&gt;
 $ret .= qx(sendemail -f &#039;$from&#039; -t &#039;$rcpt&#039; -u &#039;$subject&#039; -m &#039;$text&#039; -a $attach -s &#039;$provider&#039; -xu &#039;$konto&#039; -xp &#039;$passwrd&#039; -o tls=auto -o message-charset=utf-8);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendemail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
###############################################################################&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Die Zugangsdaten für den Email-Account müssen dabei FHEM-intern gespeichert werden. Dazu gibt man einmalig über die Kommandozeile folgendes ein (user@domain und password müssen auf den Email Account geändert werden):&lt;br /&gt;
&lt;br /&gt;
:  &amp;lt;code&amp;gt;{setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die TLS Verschlüsselung (ehem. SSL) nicht zu nutzen, muss in der viertletzten Zeile tls=no verwendet und der Port des Mailproviders auf 25 eigetragen werden. Sollte anschließend keine Mail verschickt werden, siehe Probleme.&lt;br /&gt;
&lt;br /&gt;
Falls der Body-Text in einem (Android-)Mailer auf dem Handy nicht gezeigt wird, kann der Parameter &#039;&#039;&#039;-o message-content-type=html&#039;&#039;&#039; helfen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion kann beispielsweise folgendermaßen aufgerufen werden:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{qx(sendemail -f &#039;Ab\@send.er&#039; -t &#039;Emp\@faeng.er&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &#039;DateinameOderLeer&#039; -s &#039;smtpFQDN:port&#039; -xu &#039;MailUser&#039; -xp &#039;PasswortOhneKlammeraffen&#039;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Bei allen Feldern können generell doppelte &amp;quot; oder einfache &#039; Anführungszeichen verwendet werden. Innerhalb von &amp;quot;&amp;quot; müssen Sonderzeichen wie @ aber maskiert werden, da sie sonst als Steuerzeichen interpretiert werden:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;&amp;quot;email\@email.domain&amp;quot; oder &#039;email@email.domain&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direkt in der FHEM Kommandozeile (ohne Anhang)&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;{DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ordnungsgemäße Ausführung wird mit 71 quittiert.&lt;br /&gt;
&lt;br /&gt;
Beispiele mehrere Anhänge (Es ist wichtig Dateinamen mit Leerzeichen in separate Anführungszeichen zu setzen)&lt;br /&gt;
 {DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;Anhang1 Anhang2 Anhang3&amp;quot;)}&lt;br /&gt;
 {DebianMail(&#039;email@email.domain&#039;,&#039;Subject&#039;,&#039;Text&#039;,&amp;quot;&#039;with spaces Anhang1&#039; &#039;with spaces Anhang2&#039;&amp;quot;)}&lt;br /&gt;
oder (mehr als einen Empfänger, diese können durch Leerzeichen, Semikolon oder Komma getrennt sein)&lt;br /&gt;
 define Sonstiges at *01:00:00 {\&lt;br /&gt;
 DebianMail(&#039;email@email.domain,email2@email.domain&#039;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;&#039;Anhang1&#039; &#039;Anhang2&#039; ...&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Die archivierte Doku zu &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; befindet sich in der Änderungshistorie: https://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;amp;oldid=37524&lt;br /&gt;
[[Kategorie:E-Mail]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40105</id>
		<title>E-Mail senden</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40105"/>
		<updated>2025-04-18T17:09:26Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Absendedatum wird vom Server gesetzt, Hinweis von Nutzer moskito im Forum&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Um aus FHEM heraus E-Mails senden zu können, gibt es unterschiedliche Methoden, abhängig von der verwendeten Plattform.&lt;br /&gt;
&lt;br /&gt;
Lange Zeit war &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; sehr beliebt. Da aber die Homepage des Projekts nicht mehr online ist und Probleme nur noch via erhöhtem Supportaufwand gelöst werden, sollte diese Option als veraltet betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Eine Liste mit ressourcensparenden E-Mails Clients findet man bei dem OpenWRT Projekt: https://openwrt.org/docs/guide-user/services/email/smtp.client&lt;br /&gt;
&lt;br /&gt;
MSGMail ist ein FHEM eigenes Modul zum Versenden, die Einrichtung ist allerdings nicht gänzlich einfach. Anfängern sei die cURL Methode empfohlen.&lt;br /&gt;
&lt;br /&gt;
=== MSGMail (Linux, Windows) ===&lt;br /&gt;
FHEM bringt ein eigenes Device zum Senden von E-Mails mit, welches in Perl implementiert ist: [https://fhem.de/commandref.html#MSGMail MSGMail, https://fhem.de/commandref.html#MSGMail]&lt;br /&gt;
&lt;br /&gt;
=== cURL (Linux, Windows) ===&lt;br /&gt;
[https://curl.se/ cURL] kann auf praktisch [https://curl.se/download.html jedem Betriebssystem] genutzt werden, [https://curl.se/windows/ selbst unter Windows]. Es kann direkt mit einem SMTP Server E-Mails versenden und stellt damit eine moderne, portable und sichere Option zum Versenden von E-Mails dar.&lt;br /&gt;
&lt;br /&gt;
In FHEM richtet man sich ein Device ein und passt die Details (&amp;lt;code&amp;gt;$emailTo, $emailFrom, $emailServer&amp;lt;/code&amp;gt;) an. Das Programm cURL muss installiert sein (z.B. mit &amp;lt;code&amp;gt;apt install curl&amp;lt;/code&amp;gt;, oder durch Herunterladen und Installieren von der [https://curl.se cURL-Webseite]).&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod sendMail dummy&lt;br /&gt;
attr sendMail readingList message password&lt;br /&gt;
attr sendMail setList message:textField-long password&lt;br /&gt;
attr sendMail userReadings state:message:[\s\S]* {\&lt;br /&gt;
	my $emailTo   = &#039;du@example.com&#039;;;\&lt;br /&gt;
	my $emailFrom = &#039;fhem@example.de&#039;;;\&lt;br /&gt;
	my $emailServer = &#039;mail.your-server.de&#039;;;\&lt;br /&gt;
	my $emailDate = strftime(&amp;quot;%a, %d %b %Y %H:%M:%S %z&amp;quot;, localtime(time()));;\&lt;br /&gt;
	\&lt;br /&gt;
	# Passwort aus getKeyValue abrufen\&lt;br /&gt;
	my ($err, $emailPass) = getKeyValue(&amp;quot;${name}_password&amp;quot;);;\&lt;br /&gt;
	if ($err || !defined $emailPass) {\&lt;br /&gt;
		return &amp;quot;Error retrieving password: $err&amp;quot;;;\&lt;br /&gt;
	}\&lt;br /&gt;
\&lt;br /&gt;
	my $message = ReadingsVal($name, &#039;message&#039;, &#039;???&#039;);;\&lt;br /&gt;
	my $subject = &amp;quot;$name: FHEM Nachricht&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
	# Betreff extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
	if ($message =~ s/(?:Subject|Betreff)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
		$subject = $1;;\&lt;br /&gt;
	}\&lt;br /&gt;
\&lt;br /&gt;
	# Empfänger extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
	if ($message =~ s/(?:To|An)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
		$emailTo = $1;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	#Dateianhänge bestimmen\&lt;br /&gt;
	my @attachments;;\&lt;br /&gt;
	while ($message =~ s/(?:Attachment|Anhang)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
		my $file = $1;;\&lt;br /&gt;
		return &amp;quot;&amp;gt;&amp;gt;$file&amp;lt;&amp;lt; not found, not sending email&amp;quot; unless (-e $file);;\&lt;br /&gt;
    	push (@attachments, $file);;\&lt;br /&gt;
	}\&lt;br /&gt;
\&lt;br /&gt;
	#der curl Befehl wird hier aufgebaut:\&lt;br /&gt;
	my $cmd = &amp;quot;curl -m 19 --noproxy &#039;*&#039; --no-progress-meter --ssl-reqd smtps://$emailServer:465 &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;--user &#039;$emailFrom:$emailPass&#039; --mail-from &#039;$emailFrom&#039; --mail-rcpt &#039;$emailTo&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;--write-out &#039;Email sent with status %{http_code}\n&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-H &#039;Subject: $subject&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-H &#039;From: $emailFrom&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-H &#039;Date: $emailDate&#039; &amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	#multipart/alternative für Text und HTML EMail Body\&lt;br /&gt;
	$cmd .= &amp;quot;-F &#039;=(;;type=multipart/alternative&#039; &amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	# Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Anfang entfernen:\&lt;br /&gt;
	$message =~ s/^(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+//;;\&lt;br /&gt;
	# Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Ende entfernen:\&lt;br /&gt;
	$message =~ s/(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+$//;;\&lt;br /&gt;
	\&lt;br /&gt;
	#Plaintext E-Mail Inhalt an cURL:\&lt;br /&gt;
	$cmd .= &amp;quot;-F \&amp;quot;=$message;;type=text/plain;; charset=UTF-8\&amp;quot; &amp;quot;;;\&lt;br /&gt;
	#ersetze \n bzw. \r\n durch ein HTML-tag &amp;lt;br /&amp;gt;:\&lt;br /&gt;
	$message =~ s/\r?\n/&amp;lt;br \/&amp;gt;/g;;\&lt;br /&gt;
	\&lt;br /&gt;
	#HTML E-Mail Inhalt an cURL:\&lt;br /&gt;
	$cmd .= &amp;quot;-F &#039;= &amp;lt;body&amp;gt;$message&amp;lt;/body&amp;gt;;;type=text/html;; charset=UTF-8&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-F &#039;=)&#039; &amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	#jetzt alle Attachments (0..N) anhängen:\&lt;br /&gt;
	foreach my $file (@attachments) {\&lt;br /&gt;
    	$cmd .= &amp;quot;-F &#039;=@&amp;quot;.$file.&amp;quot;;;encoder=base64&#039; &amp;quot;;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	#stderr auch mit in den output sammeln:\&lt;br /&gt;
	$cmd .= &amp;quot; 2&amp;gt;&amp;amp;1&amp;quot;;;\&lt;br /&gt;
\&lt;br /&gt;
	#Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:\&lt;br /&gt;
	if (!defined &amp;amp;sendMailfunc1) {\&lt;br /&gt;
		*sendMailfunc1 = sub ($) {\&lt;br /&gt;
			my ($param) = @_;;\&lt;br /&gt;
			my $result;;\&lt;br /&gt;
			$result = qx/$param/;;\&lt;br /&gt;
			$result = MIME::Base64::encode_base64($result);;\&lt;br /&gt;
			$result =~ s/\n//g;;\&lt;br /&gt;
			return $result;;\&lt;br /&gt;
		}\&lt;br /&gt;
	}\&lt;br /&gt;
\&lt;br /&gt;
	#Rückgabe über diese Funktion,\&lt;br /&gt;
	#anzeigen in dem reading dieses UserReadings:\&lt;br /&gt;
	if (!defined &amp;amp;sendMailfunc2) {\&lt;br /&gt;
		*sendMailfunc2 = sub ($) {\&lt;br /&gt;
			my ($result) = @_;;\&lt;br /&gt;
			my $hash = $defs{$name};;\&lt;br /&gt;
			$result = MIME::Base64::decode_base64($result);;\&lt;br /&gt;
			readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
		}\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	#funktion falls BlockingCall abgebrochen wird:\&lt;br /&gt;
	if (!defined &amp;amp;sendMailfunc3) {\&lt;br /&gt;
		*sendMailfunc3 = sub ($) {\&lt;br /&gt;
			my ($result) = @_;;\&lt;br /&gt;
			my $hash = $defs{$name};;\&lt;br /&gt;
			readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
		}\&lt;br /&gt;
	}\&lt;br /&gt;
\&lt;br /&gt;
	BlockingCall(&amp;quot;sendMailfunc1&amp;quot;, $cmd, &amp;quot;sendMailfunc2&amp;quot;, 20, &amp;quot;sendMailfunc3&amp;quot;, &amp;quot;Error: timeout&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return &amp;quot;started cURL command...&amp;quot;;;\&lt;br /&gt;
},\&lt;br /&gt;
state:password:.* {\&lt;br /&gt;
	# Passwort speichern\&lt;br /&gt;
	my $ret = setKeyValue(&amp;quot;${name}_password&amp;quot;, ReadingsVal($name, &#039;password&#039;, undef)) // &amp;quot;password stored&amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	#password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\&lt;br /&gt;
	#\&lt;br /&gt;
	# Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\&lt;br /&gt;
	# Alternativ: { setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;password&amp;quot;, &amp;quot;****&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return &amp;quot;$ret&amp;quot;;;\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Das Passwort setzt man mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail password geheimesPasswort&amp;lt;/code&amp;gt;&lt;br /&gt;
* Alternativ, damit das Passwort nicht im Log auftaucht: &amp;lt;code&amp;gt;{ setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
E-Mails kann man dann Senden mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Mein Text&amp;lt;/code&amp;gt; (es wird der Standardbetreff und Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;(es wird der Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Subject=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;✅ Dies ist ein Test&amp;quot; Meldung mit Emoji ⚽ geht auch&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eine Datei mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/bag.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zwei Dateien mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;./www/images/fhemSVG/bag.svg&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/batterie.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;, für die anzuhängenden Dateien können relative und absolute Pfade verwendet werden. Beim Vorbereiten der E-Mail wird geprüft ob die Dateien existieren, da dies sonst eine typische Fehlerquelle ist. Es wird nichts versendet wenn eine Datei nicht gefunden werden kann.&lt;br /&gt;
&lt;br /&gt;
==== Fehlersuche ====&lt;br /&gt;
Sollte cURL zwar installiert sein, aber keine E-Mails versenden, kann es sein, dass das Protokoll nicht einkompiliert wurde. Dies ist bei OpenWRT zum Beispiel der Fall.&lt;br /&gt;
&lt;br /&gt;
So sollte es aussehen (hier ein Ubuntu), relevant ist für diese Nutzung das Protokoll smtp und smtps, das braucht man:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.7&lt;br /&gt;
Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.6&lt;br /&gt;
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp&lt;br /&gt;
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;So sollte es nicht aussehen (hier ein OpenWRT):&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl: (1) Protocol &amp;quot;smtps&amp;quot; not supported&lt;br /&gt;
&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.10.1 (arm-openwrt-linux-gnu) libcurl/8.10.1 mbedTLS/3.6.2 nghttp2/1.63.0&lt;br /&gt;
Release-Date: 2024-09-18&lt;br /&gt;
Protocols: file ftp ftps http https ipfs ipns mqtt&lt;br /&gt;
Features: alt-svc HSTS HTTP2 HTTPS-proxy IPv6 Largefile SSL threadsafe UnixSockets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hat man nur ein cURL ohne das Protokoll, so muss man sich das Paket leider anderweitig beschaffen (chroot, selbst kompilieren, statisch gelinkte Version, ...).&lt;br /&gt;
&lt;br /&gt;
==== Forumlink ====&lt;br /&gt;
https://forum.fhem.de/index.php?topic=140814.0&lt;br /&gt;
&lt;br /&gt;
=== EXIM4 (Debian) ===&lt;br /&gt;
Eine Anleitung zum Versenden von E-Mails mit Exim ist als PDF verfügbar: [[Medium:Anleitung Exim4 Debian GMX.pdf|PDF]]&lt;br /&gt;
&lt;br /&gt;
=== SSMTP (OpenWRT, embedded Distros) ===&lt;br /&gt;
&lt;br /&gt;
==== Installation ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install ssmtp_2.64-4_mpc85xx.ipk  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Damit &amp;lt;code&amp;gt;ssmtp&amp;lt;/code&amp;gt; funktioniert müssen die Dateien &amp;lt;code&amp;gt;/etc/ssmtp/ssmtp.conf&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;/etc/ssmtp/revaliases&amp;lt;/code&amp;gt; angepasst werden.&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/ssmtp.conf &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root=arnold@gmx.net&lt;br /&gt;
 mailhub=mail.gmx.net:465&lt;br /&gt;
 rewriteDomain=gmx.net&lt;br /&gt;
 hostname=gmx.net&lt;br /&gt;
 FromLineOverride=YES&lt;br /&gt;
 UseTLS=YES&lt;br /&gt;
 #UseSTARTTLS=YES&lt;br /&gt;
 AuthUser=arnold@gmx.net&lt;br /&gt;
 AuthPass=Passwort_von_arnold@gmx.net&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/revaliases&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root:arnold@gmx.net:mail.gmx.net:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der [[99_myUtils_anlegen|99_myUtils]] folgende Unterroutine einfügen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub OpenWRTMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $sender = &amp;quot;dockstar\@heye-tammo.de&amp;quot;; &lt;br /&gt;
 Log 1, &amp;quot;sendEmail RCP: $rcpt, Subject: $subject, Text: $text&amp;quot;;&lt;br /&gt;
 $ret .= qx(echo -e &#039;to:$rcpt\n from:$sender\nsubject:$subject\n$text\n&#039; | ssmtp $rcpt);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendEmail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== PHP Mail Funktion (Synology DiskStation) ===&lt;br /&gt;
Beim DSM 3.2 funktioniert der &amp;lt;code&amp;gt;php-mail&amp;lt;/code&amp;gt;-Befehl, so dass man mittels folgendem Modul E-Mails versenden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub sendmail($$$) {&lt;br /&gt;
 my($empf, $subj, $nachricht) = @_;&lt;br /&gt;
 system(&amp;quot;php -r &#039;mail(\&amp;quot;$empf\&amp;quot;,\&amp;quot;$subj\&amp;quot;,\&amp;quot;$nachricht\&amp;quot;);&#039;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sendemail (Veraltet, Debian-basierende Distros) ===&lt;br /&gt;
Es handelt sich um ein Perl Programm, dass man aus den Paketquellen installieren kann.&lt;br /&gt;
&lt;br /&gt;
Es gibt Probleme:&lt;br /&gt;
&lt;br /&gt;
* mit der TLS bzw STARTTLS Verschlüsselung (https://github.com/fhem/fhem-docker/issues/254, https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1915977.html) ,&lt;br /&gt;
* Mit der Webseite des Projekts (https://web.archive.org/web/20190906140125/http://caspian.dotconf.net/menu/Software/SendEmail); diese ist nicht mehr verfügbar und nur eine archivierte Kopie kann eingesehen werden,&lt;br /&gt;
&lt;br /&gt;
weswegen diese Lösung als technisch überholt gelten sollte. Will man trotzdem &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; verwenden folgenden hier die Informationen dazu:&lt;br /&gt;
&lt;br /&gt;
Auf dem Server muss &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; installiert werden:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt-get update&lt;br /&gt;
sudo apt-get install sendemail libio-socket-ssl-perl libnet-ssleay-perl perl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Man kann die korrekte Funktion im Terminal (ohne &amp;lt;code&amp;gt;{qx()}&amp;lt;/code&amp;gt; ) oder auch in der FHEM Kommandozeile testen (Hinweis: manche Provider (z.B. gmail) lassen diesen Zugang erst nach Freischaltung zu!).&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Bei Gmail muss seit dem 30.6.2022 ein separates Passwort erzeugt werden, die alte Einstellung über &amp;quot;Zugriff durch weniger sichere Apps&amp;quot; funktioniert nicht mehr. Siehe Support Seite Google: &amp;lt;nowiki&amp;gt;https://support.google.com/accounts/answer/185833&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Beispiel mit Gmail über Linux Terminalfenster:&#039;&#039;&lt;br /&gt;
 sendemail -f &#039;Der.Absender@gmail.com&#039; -t &#039;Der.Empfaenger@gmail.com&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt; -s &#039;smtp.gmail.com:587&#039; -xu &#039;Der.Absender&#039; -xp DASSUPERGEHEIMEPASSW0RT&lt;br /&gt;
In der [[99 myUtils anlegen|99_myUtils]] folgende Unterroutine einfügen:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
######## DebianMail  Mail auf dem RPi versenden ############ &lt;br /&gt;
# $provider für SMTP Server anpassen. &lt;br /&gt;
# Einmal in der FHEM Kommandozeile, user und password anpassen:&lt;br /&gt;
# {setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&lt;br /&gt;
sub DebianMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $attach = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $error;&lt;br /&gt;
 my $konto = getKeyValue(&amp;quot;myEmailKonto&amp;quot;); &lt;br /&gt;
 my $passwrd = getKeyValue(&amp;quot;myEmailPasswrd&amp;quot;); &lt;br /&gt;
 my $from = $konto; # or use different KeyValue if konto is not the from email address&lt;br /&gt;
 my $provider = &amp;quot;smtp.1und1.de&amp;quot;; # smtp.domain.tld:port see provider documentation&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail RCP: $rcpt&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Subject: $subject&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Text: $text&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Anhang: $attach&amp;quot;;&lt;br /&gt;
 if (not defined($attach)){$attach=&#039;&#039;}&lt;br /&gt;
 $ret .= qx(sendemail -f &#039;$from&#039; -t &#039;$rcpt&#039; -u &#039;$subject&#039; -m &#039;$text&#039; -a $attach -s &#039;$provider&#039; -xu &#039;$konto&#039; -xp &#039;$passwrd&#039; -o tls=auto -o message-charset=utf-8);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendemail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
###############################################################################&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Die Zugangsdaten für den Email-Account müssen dabei FHEM-intern gespeichert werden. Dazu gibt man einmalig über die Kommandozeile folgendes ein (user@domain und password müssen auf den Email Account geändert werden):&lt;br /&gt;
&lt;br /&gt;
:  &amp;lt;code&amp;gt;{setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die TLS Verschlüsselung (ehem. SSL) nicht zu nutzen, muss in der viertletzten Zeile tls=no verwendet und der Port des Mailproviders auf 25 eigetragen werden. Sollte anschließend keine Mail verschickt werden, siehe Probleme.&lt;br /&gt;
&lt;br /&gt;
Falls der Body-Text in einem (Android-)Mailer auf dem Handy nicht gezeigt wird, kann der Parameter &#039;&#039;&#039;-o message-content-type=html&#039;&#039;&#039; helfen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion kann beispielsweise folgendermaßen aufgerufen werden:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{qx(sendemail -f &#039;Ab\@send.er&#039; -t &#039;Emp\@faeng.er&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &#039;DateinameOderLeer&#039; -s &#039;smtpFQDN:port&#039; -xu &#039;MailUser&#039; -xp &#039;PasswortOhneKlammeraffen&#039;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Bei allen Feldern können generell doppelte &amp;quot; oder einfache &#039; Anführungszeichen verwendet werden. Innerhalb von &amp;quot;&amp;quot; müssen Sonderzeichen wie @ aber maskiert werden, da sie sonst als Steuerzeichen interpretiert werden:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;&amp;quot;email\@email.domain&amp;quot; oder &#039;email@email.domain&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direkt in der FHEM Kommandozeile (ohne Anhang)&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;{DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ordnungsgemäße Ausführung wird mit 71 quittiert.&lt;br /&gt;
&lt;br /&gt;
Beispiele mehrere Anhänge (Es ist wichtig Dateinamen mit Leerzeichen in separate Anführungszeichen zu setzen)&lt;br /&gt;
 {DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;Anhang1 Anhang2 Anhang3&amp;quot;)}&lt;br /&gt;
 {DebianMail(&#039;email@email.domain&#039;,&#039;Subject&#039;,&#039;Text&#039;,&amp;quot;&#039;with spaces Anhang1&#039; &#039;with spaces Anhang2&#039;&amp;quot;)}&lt;br /&gt;
oder (mehr als einen Empfänger, diese können durch Leerzeichen, Semikolon oder Komma getrennt sein)&lt;br /&gt;
 define Sonstiges at *01:00:00 {\&lt;br /&gt;
 DebianMail(&#039;email@email.domain,email2@email.domain&#039;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;&#039;Anhang1&#039; &#039;Anhang2&#039; ...&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Die archivierte Doku zu &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; befindet sich in der Änderungshistorie: https://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;amp;oldid=37524&lt;br /&gt;
[[Kategorie:E-Mail]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=HTTPMOD&amp;diff=40103</id>
		<title>HTTPMOD</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=HTTPMOD&amp;diff=40103"/>
		<updated>2025-04-14T19:32:42Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Moved the code to more suitable location&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Extract information from devices with an HTTP interface (or, more generic, from any URL) or send information to such devices &lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=HTTPMOD&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=98_HTTPMOD.pm&lt;br /&gt;
|ModOwner=StefanStrobel ({{Link2FU|3960|Forum}} / [[Benutzer:StefanStrobel|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
[[HTTPMOD]] provides a generic way to retrieve information from devices with an HTTP Interface and store them in Readings or send information to such devices. It queries a given URL with Headers and data defined by attributes. &lt;br /&gt;
&lt;br /&gt;
From the HTTP response it extracts readings named in attributes using Regexes, JSON or XPath parsing also defined by attributes.&lt;br /&gt;
&lt;br /&gt;
In an advanced [[Konfiguration|configuration]] the module can also send information to devices. To do this, a generic &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; option can be configured using attributes. &lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
With the help of HTTPMOD you can automatically access websites, retrieve information and send data. In the simplest case, an http call is started regularly and the content of a page, for example, is read and processed further. The URL and the time interval in which the call is to be made must then be specified when the call is made. In order to read information from the called web pages, readings are required. In this readings one specifies Regex, which reads the necessary information. Here is an example.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define PM HTTPMOD  http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/-1/free.html 20000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a device that accesses the http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/-1/free.html website with the TV program every 20000 seconds. If neither readings nor other attributes are defined, then only the call of the website takes place and FHEM receives no further data from the web access.&lt;br /&gt;
&lt;br /&gt;
But now a television program is returned when you call the website, which you might want to read out. In order to save the contents in FHEM, which are related to the ARD, you need a reading. This reading must contain a name (so that FHEM knows under which name the information is stored) and a regex (so that you know where the information is stored on the website). Since usually not one, but many readings are defined, these readings are numbered consecutively. This is done by numbers that are given immediately after the word reading. In our example this would be for example&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM reading1Name ARD &lt;br /&gt;
attr PM reading1Regex &amp;lt;span class=&amp;quot;stationName&amp;quot;&amp;gt;ARD&amp;lt;\/span&amp;gt;[\w\W]*?&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;\s*&amp;lt;a[\w\W]*?&amp;gt;\s*(.*?)\s*&amp;lt;\/a&amp;gt; &lt;br /&gt;
attr PM reading2Name ARD_time &lt;br /&gt;
attr PM reading2Regex &amp;lt;span class=&amp;quot;stationName&amp;quot;&amp;gt;ARD&amp;lt;\/span&amp;gt;[\w\W]*?&amp;lt;td class=&amp;quot;time&amp;quot;&amp;gt;\s*(.*?)\s*&amp;lt; &lt;br /&gt;
attr PM reading3Name ARD_details &lt;br /&gt;
attr PM reading3Regex &amp;lt;span class=&amp;quot;stationName&amp;quot;&amp;gt;ARD&amp;lt;\/span&amp;gt;[\w\W]*?&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;\s*&amp;lt;a[\w\W]*?&amp;gt;[\w\W]*?&amp;lt;\/a&amp;gt;\s*&amp;lt;br\/&amp;gt;\s*(.*?)(&amp;lt;img|&amp;lt;\/div) &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reading1 contains the programme, reading2 the time and reading3 further details.  The Regex result from the structure of the website and may have to be designed in painstaking detail.&lt;br /&gt;
&lt;br /&gt;
There are cases where this is not enough. For example, it could be the case that the information from the web access is not automated, but manually received. Then a 0 must first be entered at the top of the duration. But how does HTTPMOD then know when the data should be retrieved? This is done with get-commands. If a get-command is executed, HTTPMOD accesses the website, fetches the information and fills the corresponding readings. &lt;br /&gt;
&lt;br /&gt;
Again, it is possible to create not one but several get-commands. Therefore, get is always followed by (at least) one number with which the get commands are numbered.  Here is another example. The TV program should be called manually and not time-controlled. To do this we first change the definition to &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define PM HTTPMOD  http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/-1/free.html 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
and add the following to the attributes&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM get1Name ARDHolen &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
With the command &amp;quot;get PM ARDHolen&amp;quot; the above readings are filled. In order for FHEM to know that the other reading2, reading3, etc. are to be read as well as reading1, the (user) attribute  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM get1CheckAllReadings 1 &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
needs to be defined.&lt;br /&gt;
&lt;br /&gt;
If the URL is not sufficient when the web page is called, but a special header is to be sent with it, this can be done with the get1Header attribute. If the header is multiline, the individual lines can be entered with get1Header1, get1Header2, and so on.    &lt;br /&gt;
&lt;br /&gt;
If you want to evaluate or control different URLs, you can define several get commands (get1, get2, etc.) and provide them with your own URLs. get1 usually reads only the reading1, get2 reads only the reading2 and so on. With CheckAllReadings all readings are evaluated simultaneously.&lt;br /&gt;
&lt;br /&gt;
If only data is to be sent on the web page, but not read, the set command is recommended. It has the same structure as the get-command.&lt;br /&gt;
&lt;br /&gt;
It becomes a bit more complex if the website requires a login check. The sid attributes are used for this purpose.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Availability, prerequisites and definition ==&lt;br /&gt;
This module is part of the regular FHEM distribution and uses the non blocking HTTP function &amp;lt;code&amp;gt;HttpUtils_NonblockingGet&amp;lt;/code&amp;gt; provided by FHEM&#039;s [[HttpUtils]] in a new version published in December 2013.&lt;br /&gt;
If not already installed in your environment, please [[update]] FHEM or install it manually using appropriate commands from your environment.&lt;br /&gt;
Please also note that FHEM HttpUtils need the global attribute dnsServer to be set in order to work really non blocking even when dns requests can not be answered.&lt;br /&gt;
&lt;br /&gt;
The device is defined as follows&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define &amp;lt;name&amp;gt; HTTPMOD &amp;lt;URL&amp;gt; &amp;lt;Interval&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The module connects to the given &amp;lt;code&amp;gt;URL&amp;lt;/code&amp;gt; every &amp;lt;code&amp;gt;Interval&amp;lt;/code&amp;gt; seconds, sends optional headers and data and then parses the response with regular expressions, xpath or json to set readings.&lt;br /&gt;
&lt;br /&gt;
URL can be &amp;quot;none&amp;quot; and Interval can be 0 if you prefer to only query data with a get command and not in a defined interval.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define PM HTTPMOD http://MyPoolManager/cgi-bin/webgui.fcgi 60&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Set-Commands ==&lt;br /&gt;
can be defined using attributes, see advanced configuration&lt;br /&gt;
&lt;br /&gt;
If you set the attribute enableControlSet to 1, the following additional built in set commands are available:&lt;br /&gt;
;interval&lt;br /&gt;
:set new interval time in seconds and restart the timer&lt;br /&gt;
;reread&lt;br /&gt;
:request the defined URL and try to parse it just like the automatic update would do it every Interval seconds without modifying the running timer.&lt;br /&gt;
;stop&lt;br /&gt;
:stop interval timer.&lt;br /&gt;
;start&lt;br /&gt;
:restart interval timer to call GetUpdate after interval seconds&lt;br /&gt;
;upgradeAttributes&lt;br /&gt;
:convert outdated attributes for older HTTPMOD-Versions that are still defined for this device from the old syntax to the new one.&lt;br /&gt;
:attributes with the description &amp;quot;this attribute should not be used anymore&amp;quot; or similar will be translated to the new syntax, e.g. readingsName1 to reading01Name.&lt;br /&gt;
;storeKeyValue&lt;br /&gt;
:stores a key value pair in an obfuscated form in the file system. Such values can then be used in replacements where the mode is &amp;quot;key&amp;quot; e.g. to avoid storing passwords in the configuration in clear text&lt;br /&gt;
&lt;br /&gt;
== Get-Commands ==&lt;br /&gt;
can be defined using attributes, see advanced configuration&lt;br /&gt;
&lt;br /&gt;
== simple Attributes ==&lt;br /&gt;
;enableControlSet&lt;br /&gt;
:enables the built in set commands &#039;&#039;interval&#039;&#039;, &#039;&#039;stop&#039;&#039;, &#039;&#039;start&#039;&#039;, &#039;&#039;reread&#039;&#039;, &#039;&#039;upgradeAttributes&#039;&#039;, &#039;&#039;storeKeyValue&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;enableCookies&lt;br /&gt;
:enables the cookie handling inside HTTPMOD. It is advisable to always set this attribute and allow HTTPMOD to track the state of cookies and set them for following HTTP-requests&lt;br /&gt;
&lt;br /&gt;
;enforceGoodReadingNames&lt;br /&gt;
:makes sure that HTTPMOD only creates readings that are allowd for Fhem (especially if reading names are dynamically created from JSON object names with extractAllJSON. It is advisable to always set this attribute.&lt;br /&gt;
&lt;br /&gt;
;handleRedirects&lt;br /&gt;
:enables the redirect handling inside HTTPMOD which should be used together with the cookie handling of HTTPMOD. HTTPMOD will then automatically follow redirects from a web server and keep track of cookies at the same time. It is advisable to always set this attribute.&lt;br /&gt;
&lt;br /&gt;
;requestHeader.* &lt;br /&gt;
:Define an additional HTTP Header to set in the HTTP request&lt;br /&gt;
&lt;br /&gt;
;requestData&lt;br /&gt;
:POST Data to be sent in the request. If not defined, it will be a GET request as defined in HttpUtils used by this module&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+(-[0-9]+)?Name&lt;br /&gt;
:the name of a reading to extract with the corresponding readingRegex&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?OExpr&lt;br /&gt;
:defines an expression that is used in an eval to compute the readings value. The raw value will be in the variable $val.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?OMap&lt;br /&gt;
:Output Map. Defines a mapping from raw to visible values like &amp;quot;0:mittig, 1:oberhalb, 2:unterhalb&amp;quot;. If specified as readingOMap then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*OMap.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?Format&lt;br /&gt;
:Defines a format string that will be used in sprintf to format a reading value. If specified as readingFormat then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*Format.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?Decode&lt;br /&gt;
:defines an encoding to be used in a call to the perl function decode to convert the raw data string read from the device to a reading. This can be used if the device delivers strings in an encoding like cp850 instead of utf8.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?Encode&lt;br /&gt;
:defines an encoding to be used in a call to the perl function encode to convert the raw data string read from the device to a reading. This can be used if the device delivers strings in an encoding like cp850 and after decoding it you want to reencode it to e.g. utf8.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+Regex&lt;br /&gt;
:defines the regex to be used for extracting the reading. The value to extract should be in a capture group / sub expression &lt;br /&gt;
:e.g. ([\d\.]+) in the above example. Multiple capture groups will create multiple readings (see explanation above)&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+XPath&lt;br /&gt;
:defines an xpath to one or more readings when parsing HTML data (see examples below)&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+XPath-Strict&lt;br /&gt;
:defines an xpath to one or more readings when parsing XML data (see examples below)&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+JSON&lt;br /&gt;
:defines a path to the JSON object wanted by concatenating the object names with an underscore as delimiter (see the example below)&lt;br /&gt;
&lt;br /&gt;
;noShutdown&lt;br /&gt;
:pass the noshutdown flag to HTTPUtils for webservers that need it (some embedded webservers only deliver empty pages otherwise)&lt;br /&gt;
&lt;br /&gt;
;disable&lt;br /&gt;
:stop doing automatic HTTP requests while this attribute is set to 1&lt;br /&gt;
&lt;br /&gt;
;timeout&lt;br /&gt;
:time in seconds to wait for an answer. Default value is 2&lt;br /&gt;
&lt;br /&gt;
;do_not_notify&lt;br /&gt;
&lt;br /&gt;
;readingFnAttributes&lt;br /&gt;
&lt;br /&gt;
== Simple Configuration of HTTP Devices ==&lt;br /&gt;
If your device expects special HTTP-headers then specify them as &amp;lt;code&amp;gt;attr requestHeader1&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;attr requestHeaderX&amp;lt;/code&amp;gt;.&lt;br /&gt;
If your Device expects an HTTP POST instead of HTTP GET then the POST-data can be specified as &amp;lt;code&amp;gt;attr requestData&amp;lt;/code&amp;gt;.&lt;br /&gt;
To get the readings, specify pairs of &amp;lt;code&amp;gt;attr readingXName&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;attr readingXRegex&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;attr readingXXPath&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;attr readingXXPath-Strict&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;attr readingXJSON&amp;lt;/code&amp;gt; to define which readings you want to extract from the HTTP response and how to extract them. (The old syntax &amp;lt;code&amp;gt;attr readingsNameX&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;attr readingsRegexX&amp;lt;/code&amp;gt; is still supported but the new one with &amp;lt;code&amp;gt;attr readingXName&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;attr readingXRegex&amp;lt;/code&amp;gt; should be preferred. The actual values to be extracted have to be sub expressions within () in the regex (see example below)&lt;br /&gt;
&lt;br /&gt;
=== Example for a PoolManager 5: ===&lt;br /&gt;
The PoolManager Web GUI can be queried with HTTP POST Requests like this one:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
POST /cgi-bin/webgui.fcgi HTTP/1.1&lt;br /&gt;
Host: 192.168.70.90&lt;br /&gt;
Accept: */*&lt;br /&gt;
Content-Type: application/json;charset=UTF-8&lt;br /&gt;
Content-Length: 60&lt;br /&gt;
&lt;br /&gt;
{&amp;quot;get&amp;quot; :[&amp;quot;34.4001.value&amp;quot; ,&amp;quot;34.4008.value&amp;quot; ,&amp;quot;34.4033.value&amp;quot;]}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The resulting HTTP Response would look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
HTTP/1.1 200 OK&lt;br /&gt;
Content-type: application/json; charset=UTF-8&lt;br /&gt;
Expires: 0&lt;br /&gt;
Cache-Control: no-cache&lt;br /&gt;
Date: Sun, 12 Jan 2014 12:23:11 GMT&lt;br /&gt;
Server: lighttpd/1.4.26&lt;br /&gt;
Content-Length: 179&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;data&amp;quot;:	{&lt;br /&gt;
		&amp;quot;34.4001.value&amp;quot;:	&amp;quot;7.00&amp;quot;,&lt;br /&gt;
		&amp;quot;34.4008.value&amp;quot;:	&amp;quot;0.52&amp;quot;,&lt;br /&gt;
		&amp;quot;34.4033.value&amp;quot;:	&amp;quot;24.8&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;status&amp;quot;:	{&lt;br /&gt;
		&amp;quot;code&amp;quot;:	0&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;event&amp;quot;:	{&lt;br /&gt;
		&amp;quot;type&amp;quot;:	1,&lt;br /&gt;
		&amp;quot;data&amp;quot;:	&amp;quot;48.30000.0&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To configure HTTPMOD for a PoolManager one would first define a PoolManager device with e.g. the name PM, the URL and an interval of e.g. 60 seconds. &lt;br /&gt;
&lt;br /&gt;
Then the data to be sent in the request needs to be defined because in this example the device expects a POST request so the query is not contained in the URL but in the request data.&lt;br /&gt;
&lt;br /&gt;
Also as seen above the device expects special HTTP headers in the request so these headers also need to be defined as &amp;lt;code&amp;gt;attr PM requestHeader1&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;attr PM requestHeader2&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then the names of the readings to be extracted would be set with attributes&lt;br /&gt;
&lt;br /&gt;
Then for each reading value to be extracted a regular expression needs to be set that will match the value in question within ().&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define PM HTTPMOD http://MyPoolManager/cgi-bin/webgui.fcgi 60&lt;br /&gt;
&lt;br /&gt;
attr PM enableControlSet 1&lt;br /&gt;
attr PM enableCookies 1&lt;br /&gt;
attr PM enforceGoodReadingNames 1&lt;br /&gt;
attr PM handleRedirects 1&lt;br /&gt;
&lt;br /&gt;
attr PM reading01Name PH&lt;br /&gt;
attr PM reading01Regex 34.4001.value&amp;quot;:[ \t]+&amp;quot;([\d\.]+)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attr PM reading02Name CL&lt;br /&gt;
attr PM reading02Regex 34.4008.value&amp;quot;:[ \t]+&amp;quot;([\d\.]+)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attr PM reading03Name3TEMP&lt;br /&gt;
attr PM reading03Regex 34.4033.value&amp;quot;:[ \t]+&amp;quot;([\d\.]+)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attr PM requestData {&amp;quot;get&amp;quot; :[&amp;quot;34.4001.value&amp;quot; ,&amp;quot;34.4008.value&amp;quot; ,&amp;quot;34.4033.value&amp;quot;, &amp;quot;14.16601.value&amp;quot;, &amp;quot;14.16602.value&amp;quot;]}&lt;br /&gt;
attr PM requestHeader1 Content-Type: application/json&lt;br /&gt;
attr PM requestHeader2 Accept: */*&lt;br /&gt;
attr PM stateFormat {sprintf(&amp;quot;%.1f Grad, PH %.1f, %.1f mg/l Chlor&amp;quot;, ReadingsVal($name,&amp;quot;TEMP&amp;quot;,0), ReadingsVal($name,&amp;quot;PH&amp;quot;,0), ReadingsVal($name,&amp;quot;CL&amp;quot;,0))}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Example for AmbientMonitor ===&lt;br /&gt;
AmbientMonitor is a webbased visualisation for sensors connected to an Arduino device. Its web interface can also be queried with HTTMOD to grab the data into readings.&lt;br /&gt;
&lt;br /&gt;
This example was provided by locutus. The hardware configuration is an Arduino + Ethercard with ENC28J60 Controller + DHT22 Sensor and software can be downloaded from https://github.com/lucadentella/AmbientMonitor&lt;br /&gt;
&lt;br /&gt;
In this example an HTTP GET is sufficent, so no &amp;lt;code&amp;gt;requestData&amp;lt;/code&amp;gt; is needed. The device provides temperature and humidity readings in an HTTP response that looks like:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
HTTP/1.0 200 OK &lt;br /&gt;
Content-Type: text/html &lt;br /&gt;
&lt;br /&gt;
myCB({&#039;temperature&#039;:22.00,&#039;humidity&#039;:46.00})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the definition could be:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define AmbientMonitor HTTPMOD http://192.168.1.221/?callback=? 300&lt;br /&gt;
&lt;br /&gt;
attr AmbientMonitor enableControlSet 1&lt;br /&gt;
attr AmbientMonitor enableCookies 1&lt;br /&gt;
attr AmbientMonitor enforceGoodReadingNames 1&lt;br /&gt;
attr AmbientMonitor handleRedirects 1&lt;br /&gt;
&lt;br /&gt;
attr AmbientMonitor requestHeader Content-Type: application/json&lt;br /&gt;
attr AmbientMonitor reading1Name Temperatur&lt;br /&gt;
attr AmbientMonitor reading1Regex temperature&#039;:([\d\.]+)&lt;br /&gt;
attr AmbientMonitor reading2Name Feuchtigkeit&lt;br /&gt;
attr AmbientMonitor reading2Regex humidity&#039;:([\d\.]+)&lt;br /&gt;
attr AmbientMonitor stateFormat {sprintf(&amp;quot;Temperatur %.1f C, Feuchtigkeit %.1f %&amp;quot;, ReadingsVal($name,&amp;quot;Temperatur&amp;quot;,0), ReadingsVal($name,&amp;quot;Feuchtigkeit&amp;quot;,0))}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== formatting and manipulating values / readings ==&lt;br /&gt;
Values that are parsed from an HTTP response can be further treated or formatted with the following attributes:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?OExpr&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?OMap&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?Format&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?Decode&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?Encode&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
They can all be specified for an individual reading, for all readings in one match (e.g. if a regular expression has several capture groups) or for all readings in a get command (defined by getXX) or for all readings in the main reading list (defined by readingXX):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading01Format %.1f&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will format the reading with the name specified by the attribute reading01Name to be numerical with one digit after the decimal point.&lt;br /&gt;
If the attribute reading01Regex is used and contains several capture groups then the format will be applied to all readings parsed by this regex unless these readings have their own format specified by reading01-1Format, reading01-2Format and so on.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading01-2Format %.1f&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be used in cases where a regular expression specified as reading01regex contains several capture groups or an xpath specified as reading01XPath creates several readings. &lt;br /&gt;
In this case reading01-2Format specifies the format to be applied to the second match.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
readingFormat %.1f&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
applies to all readings defined by a reading-Attribute that have no more specific format.&lt;br /&gt;
&lt;br /&gt;
If you need to do some calculation on a raw value before it is used as a reading, you can define the attribute &amp;lt;code&amp;gt;readingOExpr&amp;lt;/code&amp;gt;.&lt;br /&gt;
It defines a Perl expression that is used in an eval to compute the readings value. The raw value will be in the variable $val.&lt;br /&gt;
&lt;br /&gt;
Example for an expression:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM reading03OExpr $val * 10&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Just like in the above example of the readingFormat attributes, readingOExpr and the other following attributes can be applied on several levels.&lt;br /&gt;
&lt;br /&gt;
To map a numerical value to a name, you can use the readingOMap attribute. &lt;br /&gt;
It defines a mapping from raw to visible values like &amp;quot;0:mittig, 1:oberhalb, 2:unterhalb&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Example for a map:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM reading02-3OMap 0:kalt, 1:warm, 2:sehr warm&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To convert character sets, the module can first decode a string read from the device and then encode it again. For example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM getDecode UTF-8&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This applies to all readings defined for Get-Commands.&lt;br /&gt;
&lt;br /&gt;
== Some help with Regular Expressions ==&lt;br /&gt;
{{Randnotiz|RNTyp=y|RNText=Starting with version &#039;&#039;2018-02-10&#039;&#039; the internal that holds the HTTP response is no longer called &#039;&#039;&#039;&#039;&#039;buf&#039;&#039;&#039;&#039;&#039; but rather &#039;&#039;&#039;&#039;&#039;httpbody&#039;&#039;&#039;&#039;&#039;, and it is only displayed when attribute &#039;&#039;&#039;&#039;&#039;showBody&#039;&#039;&#039;&#039;&#039; is set to &amp;quot;1&amp;quot;.}}&lt;br /&gt;
If HTTPMOD seems not to work and the FHEM Logfile contains a message like  &lt;br /&gt;
:&amp;lt;code&amp;gt;HTTPMOD: Response didn&#039;t match Reading ...&amp;lt;/code&amp;gt;&lt;br /&gt;
then you should check if the value you want to extract is read into the internal with the name buf. Internals are visible when you click on the defined HTTPMOD Device. buf is an internal variable that contains the HTTP Response read. If the value is there and you get the mentioned message then probably something is wrong with your regular expression. Please note that buf might contain special characters like newlines but they are not shown in fhemweb. If you are new to regular expressions then the introduction at http://perldoc.perl.org/perlretut.html might be helpful. &lt;br /&gt;
&lt;br /&gt;
For a typical HTTPMOD use case where you want to extract a number out of a HTTP-Response you can use something like &amp;lt;code&amp;gt;[\d\.]+&amp;lt;/code&amp;gt; to match the number itself. The expression matches the number characters (&amp;lt;code&amp;gt;\d&amp;lt;/code&amp;gt;) or a &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; if one of these characters occurs at least once. &lt;br /&gt;
&lt;br /&gt;
To tell HTTPMOD that the number is what you want to use for the reading, you have to put the expression in between &amp;lt;code&amp;gt;()&amp;lt;/code&amp;gt;. A &amp;lt;code&amp;gt;([\d\.]+)&amp;lt;/code&amp;gt; alone would match the longest number in the HTTP Response which is very likely not the number you are looking for so you need to add something to the expression to give it a context and define how to find the number that you are looking for.&lt;br /&gt;
&lt;br /&gt;
If there is a title text before the number or a special text after the number you can put this in the regex. In one of the examples above &amp;lt;code&amp;gt;humidity&#039;:([\d\.]+)&amp;lt;/code&amp;gt; is looking for the number that immediately follows the text &amp;lt;code&amp;gt;humidity&#039;:&amp;lt;/code&amp;gt; without any blanks in between.&lt;br /&gt;
Be careful if the text you are getting from your device contains special characters like newline. You don&#039;t see such special characters in the fhem webinterface as contents of the internal buf but they might cause your regular expression to fail. &lt;br /&gt;
&lt;br /&gt;
If you have trouble defining a regular expression that matches a certain name, then many complicated characters and then a number, it might be helpful to use a negation in matching like &amp;lt;code&amp;gt;temp[^\d]+([\d\.]).*&amp;lt;/code&amp;gt;. In this examle &amp;lt;code&amp;gt;[^\d]+&amp;lt;/code&amp;gt; means any character that is not a numerical digit, more than once.&lt;br /&gt;
&lt;br /&gt;
=== Regular Expressions with multiple capture Groups ===&lt;br /&gt;
The regular expressions used in the above example for a Poolmanager will take the value that matches one capture group. This is the part of the regular expression inside (). In the above example &amp;quot;([\d\.]+)&amp;quot; refers to numerical digits or points between double quotation marks. Only the string consiting of digits and points will match inside (). This piece is assigned to the reading.&lt;br /&gt;
        &lt;br /&gt;
You can also use regular expressions that have several capture groups which might be helpful when parsing tables. In this case an attribute like &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading02Regex something[ \t]+([\d\.]+)[ \t]+([\d\.]+)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
could match two numbers. When you specify only one reading02Name like &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading02Name Temp&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the name Temp will be used with the extension -1 and -2 thus giving a reading Temp-1 for the first number and Temp-2 for the second. You can also specify individual names for several readings that get parsed from one regular expression with several capture groups by defining attributes &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading02-1Name&lt;br /&gt;
reading02-2Name&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The same notation can be used for formatting attributes like readingXOMap, readingXFormat and so on.&lt;br /&gt;
&lt;br /&gt;
The usual way to define readings is however to have an individual regular expression with just one capture group per reading as shown in the above example.&lt;br /&gt;
&lt;br /&gt;
== Parsing JSON ==&lt;br /&gt;
    &lt;br /&gt;
If a webservice delivers data in JSON format, HTTPMOD can directly parse JSON which might be easier in this case than definig regular expressions.&lt;br /&gt;
The next example shows the data that can be requested from a Poolmanager with the following partial configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define test2 HTTPMOD none 0&lt;br /&gt;
attr test2 get01Name Chlor&lt;br /&gt;
attr test2 getURL http://192.168.70.90/cgi-bin/webgui.fcgi&lt;br /&gt;
attr test2 getHeader1 Content-Type: application/json&lt;br /&gt;
attr test2 getHeader2 Accept: */*&lt;br /&gt;
attr test2 getData {&amp;quot;get&amp;quot; :[&amp;quot;34.4008.value&amp;quot;]}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The data in the HTTP response looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;data&amp;quot;: {&lt;br /&gt;
			&amp;quot;34.4008.value&amp;quot;: &amp;quot;0.25&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;status&amp;quot;:       {&lt;br /&gt;
			&amp;quot;code&amp;quot;: 0&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;event&amp;quot;:        {&lt;br /&gt;
			&amp;quot;type&amp;quot;: 1,&lt;br /&gt;
			&amp;quot;data&amp;quot;: &amp;quot;48.30000.0&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the classic way to extract the value 0.25 into a reading with the name Chlor with a regex would have been&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr test2 get01Regex 34.4008.value&amp;quot;:[ \t]+&amp;quot;([\d\.]+)&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
with JSON you can write &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr test2 get01JSON data_34.4008.value &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
which will create a reading with the Name &amp;quot;Chlor&amp;quot; (as shown above) and take the value 0.25 from the JSON string.&lt;br /&gt;
&lt;br /&gt;
or if you don&#039;t care about the naming of your readings, you can simply extract all JSON data with &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr test2 extractAllJSON&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which would apply to all data read from this device and create the following readings out of the HTTP response shown above:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| data_34.4008.value || 0.25&lt;br /&gt;
|-&lt;br /&gt;
| event_data || 48.30000.0&lt;br /&gt;
|-&lt;br /&gt;
| event_type || 1&lt;br /&gt;
|-&lt;br /&gt;
| status_code || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
or you can specify&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr test2 get01ExtractAllJSON&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
which would only apply to all data read as response to the get command defined as get01.        &lt;br /&gt;
&lt;br /&gt;
It might seem very simple at first sight to use extractAllJSON but if you prefer readings with a meaningful name you should instead define these readings with readingXXName and readingXXJSON or getXXName and getXXJSON individually. Of Course it would be possible to create additional user readings outside HTTPMOD but doing calculations, naming and formatting inside HTTPMOD is more efficient.&lt;br /&gt;
&lt;br /&gt;
=== JSON Lists ===&lt;br /&gt;
&lt;br /&gt;
imagine the HTTP Response contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{ &amp;quot;power&amp;quot;:&amp;quot;0&amp;quot;,&lt;br /&gt;
  &amp;quot;modes&amp;quot;:[&amp;quot;Off&amp;quot;,&amp;quot;SimpleColor&amp;quot;,&amp;quot;RainbowChase&amp;quot;],&lt;br /&gt;
  &amp;quot;code1&amp;quot;:3,&lt;br /&gt;
  &amp;quot;code2&amp;quot;:4&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
then a configuration like &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr device reading01JSON modes &lt;br /&gt;
attr device reading01Name Mode &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will create a list of Subreadings just like a regex with multiple matches can create multiple subreadings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Mode-1 || Off&lt;br /&gt;
|-&lt;br /&gt;
| Mode-2  || SimpleColor&lt;br /&gt;
|-&lt;br /&gt;
| Mode-3 || RainbowChase &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
if you don&#039;t want several subreadings but one reading that contains the list of modes, you can specify a recombine expression:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr device reading01Name Modes &lt;br /&gt;
attr device reading01RecombineExpr join &amp;quot;,&amp;quot;, @matchlist &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which will create one reading containing a list:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Modes || Off,SimpleColor,RainbowChase&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
JSON parsing specifications also don&#039;t Need to match exactly. If there is no exact match for a defined reading, the HTTPMOD will try to Interpret the specification as a regex and look for json object paths that match the specification as a regex. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr device reading01Name CodeElem&lt;br /&gt;
attr device reading01JSON code&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which will create a list of readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| CodeElem-1|| 3&lt;br /&gt;
|-&lt;br /&gt;
| CodeElem-2  || 4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
and of course they could also be recombined into one reading with a RecombineExpr Attribute.&lt;br /&gt;
&lt;br /&gt;
== Parsing http / XML using xpath ==&lt;br /&gt;
Another alternative to regex parsing is the use of XPath to extract values from HTTP responses.&lt;br /&gt;
The following example shows how XML data can be parsed with XPath-Strict or HTML Data can be parsed with XPath.&lt;br /&gt;
Both work similar and the example uses XML Data parsed with the XPath-Strict option:&lt;br /&gt;
&lt;br /&gt;
If The XML data in the HTTP response looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;root xmlns:foo=&amp;quot;http://www.foo.org/&amp;quot; xmlns:bar=&amp;quot;http://www.bar.org&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;actors&amp;gt;&lt;br /&gt;
		&amp;lt;actor id=&amp;quot;1&amp;quot;&amp;gt;Peter X&amp;lt;/actor&amp;gt;&lt;br /&gt;
		&amp;lt;actor id=&amp;quot;2&amp;quot;&amp;gt;Charles Y&amp;lt;/actor&amp;gt;&lt;br /&gt;
		&amp;lt;actor id=&amp;quot;3&amp;quot;&amp;gt;John Doe&amp;lt;/actor&amp;gt;&lt;br /&gt;
	&amp;lt;/actor&amp;gt;&lt;br /&gt;
&amp;lt;/root&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
with XPath you can write        &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr htest reading01Name Actor&lt;br /&gt;
attr htest reading01XPath-Strict //actor[2]/text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will create a reading with the Name &amp;quot;Actor&amp;quot; and the value &amp;quot;Charles Y&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Since XPath specifications can define several values / matches, HTTPMOD can also interpret these and store them in multiple readings:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr htest reading01Name Actor&lt;br /&gt;
attr htest reading01XPath-Strict //actor/text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will create the readings &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Actor-1 || Peter X&lt;br /&gt;
|-&lt;br /&gt;
| Actor-2 || Charles Y&lt;br /&gt;
|-&lt;br /&gt;
| Actor-3 || John Doe&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Further replacements of URL, header or post data ==&lt;br /&gt;
sometimes it might be helpful to dynamically change parts of a URL, HTTP header or post data depending on existing readings, internals or &lt;br /&gt;
perl expressions at runtime. This might be needed to pass further variables to a server, a current date or other things. &lt;br /&gt;
&lt;br /&gt;
To support this HTTPMOD offers generic replacements that are applied to a request before it is sent to the server. A replacement can be defined with the attributes &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;replacement[0-9]*Regex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;[gs]et[0-9]*Replacement[0-9]*Value&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
a replacement always replaces a match of a regular expression. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;replacement[0-9]*Mode:&#039;&#039;&#039;&lt;br /&gt;
The way the replacement value is defined can be specified with the replacement mode.&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;reading&amp;lt;/code&amp;gt;, then the corresponding &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt; is interpreted as the name of a &#039;&#039;reading&#039;&#039; of the same device or as &#039;&#039;device:reading&#039;&#039; to refer to another device.&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;internal&amp;lt;/code&amp;gt;, then the corresponding &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt; is interpreted as the name of an &#039;&#039;internal&#039;&#039; of the same device or as &#039;&#039;device:internal&#039;&#039; to refer to another device.&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;, then the corresponding &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt; is interpreted as a static text&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;expression&amp;lt;/code&amp;gt;, then the corresponding &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt; is evaluated as a perl expression to compute the replacement. Inside such a replacement expression it is possible to refer to capture groups of the replacement regex.&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;key&amp;lt;/code&amp;gt;, then the module will use a value from a key / value pair that is stored in an obfuscated form in the file system with the set storeKeyValue command. This might be useful for storing passwords.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
attr mydevice getData {&amp;quot;get&amp;quot; :[&amp;quot;%%value%%.value&amp;quot;]}&lt;br /&gt;
attr mydevice replacement01Mode text&lt;br /&gt;
attr mydevice replacement01Regex %%value%%&lt;br /&gt;
&lt;br /&gt;
attr mydevice get01Name Chlor&lt;br /&gt;
attr mydevice get01Replacement01Value 34.4008&lt;br /&gt;
&lt;br /&gt;
attr mydevice get02Name Something&lt;br /&gt;
attr mydevice get02Replacement01Value 31.4024&lt;br /&gt;
&lt;br /&gt;
attr mydevice get05Name profile&lt;br /&gt;
attr mydevice get05URL http://www.mydevice.local/getprofile?password=%%password%%&lt;br /&gt;
attr mydevice replacement02Mode key&lt;br /&gt;
attr mydevice replacement02Regex %%password%%&lt;br /&gt;
attr mydevice replacement02Value password&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;        &lt;br /&gt;
&lt;br /&gt;
defines that &amp;lt;code&amp;gt;%%value%%&amp;lt;/code&amp;gt; will be replaced by a static text.&lt;br /&gt;
&lt;br /&gt;
All Get commands will be HTTP post requests of a similar form. Only the &amp;lt;code&amp;gt;%%value%%&amp;lt;/code&amp;gt; will be different from get to get.&lt;br /&gt;
The first get will set the reading named Chlor and for the request it will take the generic getData and replace %%value%% with 34.4008.&lt;br /&gt;
&lt;br /&gt;
A second get will look the same except a different name and replacement value.&lt;br /&gt;
&lt;br /&gt;
With the command &amp;lt;code&amp;gt;set mydevice storeKeyValue password geheim&amp;lt;/code&amp;gt; you can store the password geheim in an obfuscated form in the file system. &lt;br /&gt;
To use this password and send it in a request you can use the above replacement with mode key. The value password will then refer to the ofuscated string stored with the key password.&lt;br /&gt;
&lt;br /&gt;
The mode &amp;lt;code&amp;gt;expression&amp;lt;/code&amp;gt; allows you to define your own replacement syntax:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;        &lt;br /&gt;
attr mydevice replacement01Mode expression&lt;br /&gt;
attr mydevice replacement01Regex {{([^}]+)}}&lt;br /&gt;
attr mydevice replacement01Value ReadingsVal(&amp;quot;mydevice&amp;quot;, $1, &amp;quot;&amp;quot;)&lt;br /&gt;
attr mydevice getData {&amp;quot;get&amp;quot; :[&amp;quot;{{temp}}.value&amp;quot;]}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;      &lt;br /&gt;
&lt;br /&gt;
In this example any &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{name}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; in a URL, header or post data will be passed on to the perl function ReadingsVal &lt;br /&gt;
which uses the string between &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; as second parameter. This way one defined replacement can be used for many different&lt;br /&gt;
readings.&lt;br /&gt;
&lt;br /&gt;
HTTPMOD has two built in replacements: One for session Ids and another one for the input value in a set command.&lt;br /&gt;
The placeholder $sid is always replaced with the internal &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt; which contains the session id after it is extracted from a previous HTTP response. &lt;br /&gt;
If you don&#039;t like to use the placeholder $sid then you can define your own replacement for example like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr mydevice replacement01Mode internal&lt;br /&gt;
attr mydevice replacement01Regex %session%&lt;br /&gt;
attr mydevice replacement01Value sid&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;      &lt;br /&gt;
&lt;br /&gt;
Now the internal &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt; will be used as a replacement for the placeholder %session%.&lt;br /&gt;
&lt;br /&gt;
In the same way a value that is passed to a set-command can be put into a request with a user defined replacement. &lt;br /&gt;
In this case the internal &amp;lt;code&amp;gt;$hash-&amp;gt;{value}&amp;lt;/code&amp;gt; will contain the value passed to the set command. &lt;br /&gt;
&amp;lt;code&amp;gt;$hash-&amp;gt;{value}&amp;lt;/code&amp;gt; might even be a string containing several values that could be put into several different positions in a request by using user defined replacements.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example: set-magic in HTTPMOD&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
To achieve the same just like [[set magic]] the following can be added to a HTTPMOD device:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr Owntone.device replacement02Mode expression&lt;br /&gt;
attr Owntone.device replacement02Regex \[([^:]+):([^\]]+)\]&lt;br /&gt;
attr Owntone.device replacement02Value my $device = $name if ($1 eq &amp;quot;\$name&amp;quot;) // $1;;\&lt;br /&gt;
ReadingsVal($device, $2, undef) or AttrVal($device, $2, &amp;quot;???&amp;quot;);;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;With that definition you can just write &amp;lt;code&amp;gt;[$name:state]&amp;lt;/code&amp;gt; to get the state-reading of your own HTTPMOD device there. It also accepts other devicenames and first searches for a reading and if no reading is found it searches for an attrbibute with a matching name. This neat trick is used throughout in the Owntone-HTTPMOD-device (see links section below) to keep it easily adoptable to different server names without editing many places in the listing.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Other example : steering a pellet stove from Rika&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The stove API of Rika on https://www.rika-firenet.com/web/ delivers a JSON string with all settings and values, and can be piloted with a data string containing all the &amp;quot;set&amp;quot; values at once.&lt;br /&gt;
&lt;br /&gt;
Delivered JSON on get https://www.rika-firenet.com/api/client/xxxxxxxx/status : (xxxxxxxx must be replaced with the unique stove ID)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Vorzimmer&amp;quot;,&lt;br /&gt;
    &amp;quot;stoveID&amp;quot;: &amp;quot;xxxxxxxxx&amp;quot;,&lt;br /&gt;
    &amp;quot;lastSeenMinutes&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;lastConfirmedRevision&amp;quot;: 1504385700,&lt;br /&gt;
    &amp;quot;controls&amp;quot;: {&lt;br /&gt;
        &amp;quot;revision&amp;quot;: 1504385700,&lt;br /&gt;
        &amp;quot;onOff&amp;quot;: true,&lt;br /&gt;
        &amp;quot;operatingMode&amp;quot;: 2,&lt;br /&gt;
        &amp;quot;heatingPower&amp;quot;: 65,&lt;br /&gt;
        &amp;quot;targetTemperature&amp;quot;: 24,&lt;br /&gt;
        &amp;quot;heatingTimesActive&amp;quot;: false,&lt;br /&gt;
        &amp;quot;heatingTimesActiveForComfort&amp;quot;: true,&lt;br /&gt;
        &amp;quot;setBackTemperature&amp;quot;: 18,&lt;br /&gt;
        &amp;quot;convectionFan1Active&amp;quot;: false,&lt;br /&gt;
        &amp;quot;convectionFan1Level&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;convectionFan1Area&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;convectionFan2Active&amp;quot;: false,&lt;br /&gt;
        &amp;quot;convectionFan2Level&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;convectionFan2Area&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;frostProtectionActive&amp;quot;: false,&lt;br /&gt;
        &amp;quot;frostProtectionTemperature&amp;quot;: 5&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;sensors&amp;quot;: {&lt;br /&gt;
        &amp;quot;statusError&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;statusWarning&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;statusService&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;statusMainState&amp;quot;: 1,&lt;br /&gt;
        &amp;quot;statusSubState&amp;quot;: 3,&lt;br /&gt;
        &amp;quot;statusFrostStarted&amp;quot;: false,&lt;br /&gt;
        &amp;quot;inputFlameTemperature&amp;quot;: 21,&lt;br /&gt;
        &amp;quot;inputRoomTemperature&amp;quot;: 21,&lt;br /&gt;
        &amp;quot;inputExternalRequest&amp;quot;: true,&lt;br /&gt;
        &amp;quot;outputDischargeMotor&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;outputInsertionMotor&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;outputIDFan&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;outputAirFlaps&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;outputIgnition&amp;quot;: false,&lt;br /&gt;
        &amp;quot;parameterStoveTypeNumber&amp;quot;: 13,&lt;br /&gt;
        &amp;quot;parameterVersionMainBoard&amp;quot;: 223,&lt;br /&gt;
        &amp;quot;parameterVersionTFT&amp;quot;: 223,&lt;br /&gt;
        &amp;quot;parameterRuntimePellets&amp;quot;: 11,&lt;br /&gt;
        &amp;quot;parameterRuntimeLogs&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;parameterFeedRateTotal&amp;quot;: 17,&lt;br /&gt;
        &amp;quot;parameterFeedRateService&amp;quot;: 683,&lt;br /&gt;
        &amp;quot;parameterOnOffCycles&amp;quot;: 2&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;stoveType&amp;quot;: &amp;quot;DOMO MultiAir&amp;quot;,&lt;br /&gt;
    &amp;quot;stoveFeatures&amp;quot;: {&lt;br /&gt;
        &amp;quot;multiAir1&amp;quot;: true,&lt;br /&gt;
        &amp;quot;multiAir2&amp;quot;: true,&lt;br /&gt;
        &amp;quot;insertionMotor&amp;quot;: false,&lt;br /&gt;
        &amp;quot;airFlaps&amp;quot;: false,&lt;br /&gt;
        &amp;quot;logRuntime&amp;quot;: false&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Data string to send to https://www.rika-firenet.com/api/client/xxxxxxxx/controls in order to set values:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
heatingTimesActiveForComfort=true&amp;amp;frostProtectionTemperature=3&amp;amp;setBackTemperature=18&amp;amp;targetTemperature=24&amp;amp;convectionFan2Level=0&amp;amp;convectionFan2Active=false&amp;amp;convectionFan1Level=0&amp;amp;onOff=true&amp;amp;convectionFan1Active=false&amp;amp;convectionFan2Area=0&amp;amp;revision=1505550101&amp;amp;heatingTimesActive=false&amp;amp;convectionFan1Area=0&amp;amp;frostProtectionActive=false&amp;amp;operatingMode=2&amp;amp;heatingPower=65&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Code in 99_myUtils.pm:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
use JSON;&lt;br /&gt;
...&lt;br /&gt;
sub&lt;br /&gt;
replaceJSON ($$) {&lt;br /&gt;
   my ($valToReplace, $value) = @_;&lt;br /&gt;
&lt;br /&gt;
   #$value in the parameters is a default value&lt;br /&gt;
   #It has to be replaced through the real value nnn passed in the set command &amp;quot;set &amp;lt;device&amp;gt; valToset nnn&amp;quot;&lt;br /&gt;
   $value = InternalVal(&amp;quot;Ofen&amp;quot;, &amp;quot;value&amp;quot;, $value);&lt;br /&gt;
   Log3 (&amp;quot;Ofen&amp;quot;, 3, &amp;quot;replaceJSON Internalvalue: $value&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   #Force an update to avoid outdated revision number&lt;br /&gt;
   fhem (&amp;quot;get Ofen revision&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   #Get all the controls as json&lt;br /&gt;
   my $json = ReadingsVal(&amp;quot;Ofen&amp;quot;, &amp;quot;controlsJSON&amp;quot;,&amp;quot;&amp;quot;);&lt;br /&gt;
   Log3 (&amp;quot;Ofen&amp;quot;, 3, &amp;quot;replaceJSON configsJSON: $json&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   # When starting FHEM or rereading config, the reading controlsJSON is empty&lt;br /&gt;
   return if ($json eq &amp;quot;&amp;quot;); &lt;br /&gt;
&lt;br /&gt;
   my $decoded = decode_json($json);&lt;br /&gt;
   my $result;&lt;br /&gt;
   for my $key ( keys %$decoded ) {&lt;br /&gt;
      $result .= &amp;quot;$key=&amp;quot;;&lt;br /&gt;
      if ($key eq $valToReplace) {&lt;br /&gt;
         $result .= $value.&amp;quot;&amp;amp;&amp;quot;;&lt;br /&gt;
      } else {&lt;br /&gt;
         $result .= $decoded-&amp;gt;{$key}.&amp;quot;&amp;amp;&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   chop($result); #remove last &amp;amp;&lt;br /&gt;
   Log3(&amp;quot;Ofen&amp;quot;, 3, &amp;quot;replaceJSON Result: $result&amp;quot;);&lt;br /&gt;
   return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Define stove in fhem:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Ofen HTTPMOD https://www.rika-firenet.com/api/client/xxxxxxxx/status 60&lt;br /&gt;
&lt;br /&gt;
attr Ofen enableCookies 1&lt;br /&gt;
attr Ofen reAuthRegex id=&amp;quot;login&amp;quot;|Unauthorized&lt;br /&gt;
attr Ofen sid01Data email=xx@xx&amp;amp;password=xx&lt;br /&gt;
attr Ofen sid01URL https://www.rika-firenet.com/web/login&lt;br /&gt;
&lt;br /&gt;
attr Ofen reading01JSON sensors_inputRoomTemperature&lt;br /&gt;
attr Ofen reading01Name RaumTemp&lt;br /&gt;
attr Ofen reading02JSON controls_setBackTemperature&lt;br /&gt;
attr Ofen reading02Name Absenkung&lt;br /&gt;
attr Ofen reading03JSON controls_frostProtectionTemperature&lt;br /&gt;
attr Ofen reading03Name Frostschutz&lt;br /&gt;
attr Ofen reading10Name controlsJSON&lt;br /&gt;
attr Ofen reading10Regex (?s)controls.*?({.*?})&lt;br /&gt;
&lt;br /&gt;
attr Ofen get09Name revision&lt;br /&gt;
attr Ofen get09URL https://www.rika-firenet.com/api/client/xxxxxxxx/status&lt;br /&gt;
&lt;br /&gt;
attr Ofen setURL https://www.rika-firenet.com/api/client/xxxxxxxx/controls&lt;br /&gt;
attr Ofen setData {{data}}&lt;br /&gt;
attr Ofen replacement01Mode expression&lt;br /&gt;
attr Ofen replacement01Regex {{data}}&lt;br /&gt;
&lt;br /&gt;
attr Ofen set11Name frostProtectionTemperature&lt;br /&gt;
attr Ofen set11Replacement01Value replaceJSON(&amp;quot;frostProtectionTemperature&amp;quot;, 2)&lt;br /&gt;
&lt;br /&gt;
attr Ofen set12Name targetTemperature&lt;br /&gt;
attr Ofen set12Replacement01Value replaceJSON(&amp;quot;targetTemperature&amp;quot;, 24)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A detailed explanation (in german) of the login process can be found here: [https://forum.fhem.de/index.php/topic,76220.msg682514.html#msg682514]&lt;br /&gt;
and the explanation of the other parameters here: [https://forum.fhem.de/index.php/topic,76220.msg685710.html#msg685710]&lt;br /&gt;
&lt;br /&gt;
== replacing reading values when they have not been updated / the device did not respond ==&lt;br /&gt;
If a device does not respond then the values stored in readings will keep the same and only their timestamp shows that they are outdated. &lt;br /&gt;
If you want to modify reading values that have not been updated for a number of seconds, you can use the attributes&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?MaxAge&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?MaxAgeReplacementMode&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?MaxAgeReplacement&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Every time the module tries to read from a device, it will also check if readings have not been updated &lt;br /&gt;
for longer than the &amp;lt;code&amp;gt;MaxAge&amp;lt;/code&amp;gt; attributes allow. If readings are outdated, the &amp;lt;code&amp;gt;MaxAgeReplacementMode&amp;lt;/code&amp;gt; defines how the affected&lt;br /&gt;
reading values should be replaced. &amp;lt;code&amp;gt;MaxAgeReplacementMode&amp;lt;/code&amp;gt; can be &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;reading&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;internal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;expression&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;MaxAge&amp;lt;/code&amp;gt; specifies the number of seconds that a reading should remain untouched before it is replaced. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;MaxAgeReplacement&amp;lt;/code&amp;gt; contains either a static text that is used as replacement value or a Perl expression that is evaluated to &lt;br /&gt;
give the replacement value. This can be used for example to replace a temperature that has not bee updated for more than 5 minutes &lt;br /&gt;
with the string &amp;quot;outdated - was 12&amp;quot;:        &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM readingMaxAge 300&lt;br /&gt;
attr PM readingMaxAgeReplacement &amp;quot;outdated - was &amp;quot; . $val&lt;br /&gt;
attr PM readingMaxAgeReplacementMode expression&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The variable &amp;lt;code&amp;gt;$val&amp;lt;/code&amp;gt; contains the value of the reading before it became outdated.&lt;br /&gt;
&lt;br /&gt;
Or to show that a device was offline:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr MyLight reading01Name color&lt;br /&gt;
attr MyLight reading01JSON result_02_color&lt;br /&gt;
attr MyLight reading01MaxAge 300&lt;br /&gt;
attr MyLight reading01MaxAgeReplacement &amp;quot;offline&amp;quot;&lt;br /&gt;
attr MyLight reading01MaxAgeReplacementMode text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Note on determining how to send requests to a special device ==&lt;br /&gt;
If you don&#039;t know which URLs, headers or POST data your web GUI uses, you might try a local proxy like BurpSuite [http://portswigger.net/burp/ BurpSuite] to track requests and responses. This is a tedious task but probably the best way to achieve a successful result. &lt;br /&gt;
&lt;br /&gt;
Let us consider an example. The Telekom Speedport W724V has a login-site that is famous for being cumbersome. Burp allows to monitor each step in the login procedure. In the case of a speedport the following steps occur:&lt;br /&gt;
&lt;br /&gt;
First burp shows that a get command is issued&lt;br /&gt;
 ################################################################################################## &lt;br /&gt;
 GET / HTTP/1.1&lt;br /&gt;
 Host: speedport.ip&lt;br /&gt;
 Cache-Control: max-age=0&lt;br /&gt;
 Upgrade-Insecure-Requests: 1&lt;br /&gt;
 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36&lt;br /&gt;
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8&lt;br /&gt;
 Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4&lt;br /&gt;
 Cookie: lang=de&lt;br /&gt;
 Connection: close  &lt;br /&gt;
In order to mimic the behavior of a real person calling the website HTTPMOD should copy all necessary steps. Host, Cookie and the GET-command are usually necessary. The same cannot be said of the User-Agent because the website can be called from any mobile or desktop computer. &lt;br /&gt;
&lt;br /&gt;
Then, the speedport will answer with a command that consists of several lines. By going through every line for every step in the login procedure one will finally arrive at the information that is necessary to successfully enter the login of the speedport (in case of the W724V, for example, it is necessary to copy a token called _httoken and to include the referer).&lt;br /&gt;
&lt;br /&gt;
== Advanced configuration to define a &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; command and send data to a device ==&lt;br /&gt;
       &lt;br /&gt;
When a set option is defined by attributes, the module will use the value given to the set command and integrate it into an HTTP-Request that sends the value to the device. The definitions for URL, headers and post data can contain the placeholder $val which will be replaced by the (numerical) value given to the set command.&lt;br /&gt;
&lt;br /&gt;
This can be as simple as:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
# No cyclic requests and no main URL needed in this example&lt;br /&gt;
define MyDevice HTTPMOD none 0&lt;br /&gt;
&lt;br /&gt;
attr MyDevice set01Name Licht&lt;br /&gt;
attr MyDevice set01URL http://192.168.1.22/switch=$val&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A user command &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
set MyDevice Licht 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will be translated into the http GET request&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
http://192.168.1.22/switch=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this example a map would also be helpful, that translates on / off to 0 or 1 and allows the user to select on/of in fhemweb:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr MyDevive set01IMap 0:off, 1:on&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This also provides input validation to make sure that only on and off can be used with the set command.&lt;br /&gt;
&lt;br /&gt;
In more complex Scenarios you might need to login before sending a command and the Login might create a session id that has to be part of further requests either in the URL, in headers or in the post data.&lt;br /&gt;
&lt;br /&gt;
Extension to the above example for a PoolManager 5 where a set needs a session id in the URL and the values have to be passed in JSON strings as post data:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM set01Name HeizungSoll&lt;br /&gt;
attr PM set01URL http://MyPoolManager/cgi-bin/webgui.fcgi?sid=$sid&lt;br /&gt;
attr PM set01Hint 6,10,20,30&lt;br /&gt;
attr PM set01Min 6&lt;br /&gt;
attr PM set01Max 30&lt;br /&gt;
attr PM setHeader1 Content-Type: application/json&lt;br /&gt;
attr PM set01Data {&amp;quot;set&amp;quot; :{&amp;quot;34.3118.value&amp;quot; :&amp;quot;$val&amp;quot; }}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example defines a set option with the name HeizungSoll.&lt;br /&gt;
By issuing &amp;lt;code&amp;gt;set PM HeizungSoll 10&amp;lt;/code&amp;gt; in FHEM, the value 10 will be sent in the defined HTTP&lt;br /&gt;
Post to URL &amp;lt;code&amp;gt;http://MyPoolManager/cgi-bin/webgui.fcgi&amp;lt;/code&amp;gt; in the Post Data as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;set&amp;quot; :{&amp;quot;34.3118.value&amp;quot; :&amp;quot;10&amp;quot; }}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The optional attributes set01Min and set01Max define input validations that will be checked in the set function. &lt;br /&gt;
The optional attribute set01Hint will define a selection list for the FHEMweb GUI.&lt;br /&gt;
&lt;br /&gt;
The HTTP response to such a request will be ignored unless you specify the attribute &amp;lt;code&amp;gt;setParseResponse&amp;lt;/code&amp;gt; &lt;br /&gt;
for all set commands or &amp;lt;code&amp;gt;set01ParseResponse&amp;lt;/code&amp;gt; for the set command with number 01.&lt;br /&gt;
If the HTTP response to a set command is parsed then this is done like the parsing of responses to get commands and you can use the attributes ending e.g. on Format, Encode, Decode, OMap and OExpr to manipulate / format the values read.&lt;br /&gt;
&lt;br /&gt;
If a parameter to a set command is not numeric but should be passed on to the device as text, then you can specify the attribute setTextArg. For example: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM set01TextArg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If a set command should not require a parameter at all, then you can specify the attribute NoArg. For example: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM set03Name On&lt;br /&gt;
attr PM set03NoArg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example defines the set of parameters for Wiesemann&amp;amp;Theiss Web-IO Analog 4.0 #57761. The current limit is set to 16mA (setxxMax)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod WuT_Analog1 HTTPMOD none 30&lt;br /&gt;
attr WuT_Analog1 room W&amp;amp;T&lt;br /&gt;
attr WuT_Analog1 set01Max 16&lt;br /&gt;
attr WuT_Analog1 set01Min 0&lt;br /&gt;
attr WuT_Analog1 set01Name Output1&lt;br /&gt;
attr WuT_Analog1 set01URL http://192.xx.My_IP/outputaccess1?PW=MyPassword&amp;amp;State=$val&amp;amp;&lt;br /&gt;
attr WuT_Analog1 set02Max 16&lt;br /&gt;
attr WuT_Analog1 set02Min 0&lt;br /&gt;
attr WuT_Analog1 set02Name Output2&lt;br /&gt;
attr WuT_Analog1 set02URL http://192.xx.My_IP/outputaccess2?PW=MyPassword&amp;amp;State=$val&amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Advanced configuration to create a valid session id that might be necessary ==&lt;br /&gt;
In simple cases logging in works with basic authentication. In the case HTTPMOD accepts a username and password as part of the URL in the form &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
http://User:Password@192.168.1.18/something&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
However basic auth is seldom used. If you need to fill in a username and password in a HTML form and the session is then managed by a session id, here is how to configure this:&lt;br /&gt;
&lt;br /&gt;
when sending data to an HTTP-Device in a set, HTTPMOD will replace any &amp;lt;code&amp;gt;$sid&amp;lt;/code&amp;gt; in the URL, Headers and Post data with the internal &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt;. To authenticate towards the device and give this internal a value, you can use an optional multi step login procedure defined by the following attributes: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;sid[0-9]*URL&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;sid[0-9]*Data.*&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;sid[0-9]*Header.*&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;sid[0-9]*IgnoreRedirects&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;idRegex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;idJSON&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;idXPath&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;idXPath-Strict&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set|sid)[0-9]*IdRegex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set|sid)[0-9]*IdJSON&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set|sid)[0-9]*IdXPath&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set|sid)[0-9]*IdXPath-Strict&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each step can have a URL, Headers, Post Data pieces and a Regex to extract a resulting Session ID into &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt;.&lt;br /&gt;
HTTPMOD will create a sorted list of steps (the numbers between sid and URL / Data / Header) and the loop through these steps and send the corresponding requests to the device. For each step a $sid in a Header or Post Data will be replaced with the current content of &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Using this feature, HTTPMOD can perform a forms based authentication and send user name, password or other necessary data to the device and save the session id for further requests.&lt;br /&gt;
        &lt;br /&gt;
To determine when this login procedure is necessary, HTTPMOD will first try to send a request without &lt;br /&gt;
doing the login procedure. If the result contains an error that authentication is necessary, then a login is performed. &lt;br /&gt;
To detect such an error in the HTTP response, you can again use a regular expression, JSON or XPath, this time with the attributes &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;reAuthRegex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;reAuthJSON&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;reAuthXPath&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;reAuthXPath-Strict&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set)[0-9]*ReAuthRegex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set)[0-9]*ReAuthJSON&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set)[0-9]*ReAuthXPath&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set)[0-9]*ReAuthXPath-Strict&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
reAuthJSON or reAuthXPath typically only extract one piece of data from a response. &lt;br /&gt;
If the existence of the specified piece of data is sufficent to start a login procedure, then nothing more needs to be defined to detect this situation. &lt;br /&gt;
If however the indicator is a status code that contains different values depending on a successful request and a failed request if a new authentication is needed, &lt;br /&gt;
then you can combine things like reAuthJSON with reAuthRegex. In this case the regex is only matched to the data extracted by JSON (or XPath). &lt;br /&gt;
This way you can easily extract the status code using JSON parsing and then specify the code that means &amp;quot;authentication needed&amp;quot; as a regular expression.&lt;br /&gt;
        &lt;br /&gt;
If for one step not all of the URL, Data or Header Attributes are set, then HTTPMOD tries to use a &lt;br /&gt;
&amp;lt;code&amp;gt;sidURL&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sidData.*&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;sidHeader.*&amp;lt;/code&amp;gt; Attribute (without the step number after sid). This way parts that are the same for all steps don&#039;t need to be defined redundantly.&lt;br /&gt;
&lt;br /&gt;
=== Example for a multi step login procedure: ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM reAuthRegex /html/dummy_login.htm &lt;br /&gt;
attr PM sidURL http://192.168.70.90/cgi-bin/webgui.fcgi?sid=$sid&lt;br /&gt;
attr PM sidHeader1 Content-Type: application/json&lt;br /&gt;
attr PM sid1IDRegex wui.init\(&#039;([^&#039;]+)&#039;&lt;br /&gt;
attr PM sid2Data {&amp;quot;set&amp;quot; :{&amp;quot;9.17401.user&amp;quot; :&amp;quot;fhem&amp;quot; ,&amp;quot;9.17401.pass&amp;quot; :&amp;quot;password&amp;quot; }}&lt;br /&gt;
attr PM sid3Data {&amp;quot;set&amp;quot; :{&amp;quot;35.5062.value&amp;quot; :&amp;quot;128&amp;quot; }}&lt;br /&gt;
attr PM sid4Data {&amp;quot;set&amp;quot; :{&amp;quot;42.8026.code&amp;quot; :&amp;quot;pincode&amp;quot; }}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case HTTPMOD detects that a login is necessary by looking for the pattern /html/dummy_login.htm in the HTTP response. &lt;br /&gt;
If it matches, it starts a login sequence. In the above example all steps request the same URL. In step 1 only the defined Header is sent in an HTTP get request. The response will contain a session id that is extraced with the regex wui.init\(&#039;([^&#039;]+)&#039;.&lt;br /&gt;
&lt;br /&gt;
In the next step this session id is sent in a post request to the same URL where tha post data contains a username and password. The a third and a fourth request follow that set a value and a code. The result will be a valid and authorized session id that can be used in other requests where $sid is part of a URL, header or post data and will be replaced with the session id extracted above.&lt;br /&gt;
&lt;br /&gt;
===Another Practical Example reading the SOC from a Renault Zoe===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define myZoe HTTPMOD https://www.services.renault-ze.com/api/vehicle/VINVINVIN/battery 7000&lt;br /&gt;
attr myZoe userattr reading01JSON reading01Name reading02JSON reading02Name reading03JSON reading03Nam\&lt;br /&gt;
e reading04JSON reading04Name reading05JSON reading05Name requestHeader02 sid1Data sid1Header01 sid1Id\&lt;br /&gt;
JSON sid1IgnoreRedirects:0,1 sid1URL&lt;br /&gt;
attr myZoe reAuthJSON message&lt;br /&gt;
attr myZoe reading01JSON charge_level&lt;br /&gt;
attr myZoe reading01Name SOC&lt;br /&gt;
attr myZoe reading02JSON charging&lt;br /&gt;
attr myZoe reading02Name charging&lt;br /&gt;
attr myZoe reading03JSON plugged&lt;br /&gt;
attr myZoe reading03Name plugged&lt;br /&gt;
attr myZoe reading04JSON remaining_range&lt;br /&gt;
attr myZoe reading04Name remaining_range&lt;br /&gt;
attr myZoe reading05JSON last_update&lt;br /&gt;
attr myZoe reading05Name last_update&lt;br /&gt;
attr myZoe requestHeader02 Authorization: Bearer $sid&lt;br /&gt;
attr myZoe sid1Data {&amp;quot;username&amp;quot;:&amp;quot;ZEUSERNAME&amp;quot;,&amp;quot;password&amp;quot;:&amp;quot;ZEPASSWORD&amp;quot;}&lt;br /&gt;
attr myZoe sid1Header01 Content-Type: application/json&lt;br /&gt;
attr myZoe sid1IdJSON token&lt;br /&gt;
attr myZoe sid1IgnoreRedirects 1&lt;br /&gt;
attr myZoe sid1URL https://www.services.renault-ze.com/api/user/login&lt;br /&gt;
attr myZoe timeout 10&lt;br /&gt;
#attr myZoe verbose 7&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The $sid is read by logging in with username and password from JSON {&amp;quot;token&amp;quot;: ..} after detecting an error {&amp;quot;message&amp;quot;: ..}  from a failed request. This example offers no solution for the reading of the VIN from the login request, which in turn is part of the reading URL. It should be possible to read more than just the $sid from the login sequence.&lt;br /&gt;
&lt;br /&gt;
In the special case where a session id is set as a HTTP-Cookie (with the header Set-cookie: in the HTTP response) HTTPMOD offers an even simpler way. With the attribute enableCookies a very basic cookie handling mechanism is activated that stores all cookies that the server sends to the HTTPMOD device and puts them back as cookie headers in the following requests.&lt;br /&gt;
&lt;br /&gt;
For such cases no sidIdRegex and no $sid in a user defined header is necessary.&lt;br /&gt;
&lt;br /&gt;
== Advanced configuration to define a &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; and request additional data with its own request from a device ==&lt;br /&gt;
&lt;br /&gt;
The normal automatic HTTP request that is done repeatedly after the defined interval has elapsed works well in cases where all required readings can be requested in one common HTTP request. If however a device needs individual requests with different URLs or different POST data for each value, then another method is necessary. &lt;br /&gt;
For such cases a &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; option can be defined and the user can either issue FHEM &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; commands each time he needs the reading or the user can set an attribute to request the reading automatically together with the normal iteration.&lt;br /&gt;
For each &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; option attributes define an individual URL, optional headers, and post data as well as individual regular expressions and formatting options. &lt;br /&gt;
&lt;br /&gt;
When a get option is defined by attributes, the module allows querying additional values from the device that require individual HTTP-Requests or special parameters to be sent&lt;br /&gt;
&lt;br /&gt;
Extension to the above example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM get01Name MyGetValue &lt;br /&gt;
attr PM get01URL http://MyPoolManager/cgi-bin/directory/webgui.fcgi?special=1?sid=$sid &lt;br /&gt;
attr PM getHeader1 Content-Type: application/json &lt;br /&gt;
attr PM get01Data {&amp;quot;get&amp;quot; :{&amp;quot;30.1234.value&amp;quot;}} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example defines a get option with the name MyGetValue.&lt;br /&gt;
By issuing &amp;lt;code&amp;gt;get PM MyGetValue&amp;lt;/code&amp;gt; in FHEM, the defined HTTP request is sent to the device.&lt;br /&gt;
The HTTP response is then parsed using the same readingXXName and readingXXRegex attributes as above so&lt;br /&gt;
additional pairs will probably be needed there for additional values.&lt;br /&gt;
&lt;br /&gt;
if you prefer to define the parsing and formatting of readings individually per get command, you can use &lt;br /&gt;
attributes like get01Regex, get01XPath, get01Format, get01OMap and so on just like for reading01...&lt;br /&gt;
&lt;br /&gt;
You can also include parameters / values that are passed to the get command in the request just like for set commands.&lt;br /&gt;
The placeholder $val will be replaced with the value given to the get command or you can specify your own replacement as described above.&lt;br /&gt;
&lt;br /&gt;
If the new get parameter should also be queried regularly, you can define the following optional attributes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM get01Poll 1&lt;br /&gt;
attr PM get01PollDelay 300&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first attribute includes this reading in the automatic update cycle and the second defines an alternative lower update frequency. When the interval defined initially in the define is over and the normal readings are read from the device, the update function will check for additional get parameters that should be included in the update cycle.&lt;br /&gt;
&lt;br /&gt;
If a PollDelay is specified for a get parameter, the update function also checks if the time passed since it has last read this value is more than the given PollDelay. If not, this reading is skipped and it will be rechecked in the next cycle when interval is over again. So the effective PollDelay will always be a multiple of the interval specified in the initial define.&lt;br /&gt;
&lt;br /&gt;
Please note that each defined get command that is included in the regular update cycle will create its own HTTP request. So if you want to extract several values from the same request, it is much more efficient to do this by defining readingXXName and readingXXRegex, XPath or JSON attributes and to specify an interval and a URL in the define of the HTTPMOD device. &lt;br /&gt;
&lt;br /&gt;
Example for a Siemens webserver provided by Lanhydrock:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define ozw672 HTTPMOD https://192.168.178.8/api/auth/login.json?user=test&amp;amp;pwd=test 300&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get1Name tempAussen&lt;br /&gt;
attr ozw672 get1URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1960&lt;br /&gt;
attr ozw672 get1Poll 1&lt;br /&gt;
attr ozw672 get1PollDelay 1800&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get2Name tempAussenGemischt&lt;br /&gt;
attr ozw672 get2URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1964&lt;br /&gt;
attr ozw672 get2Poll 1&lt;br /&gt;
attr ozw672 get2PollDelay 1800&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get3Name tempTWW&lt;br /&gt;
attr ozw672 get3URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1996&lt;br /&gt;
attr ozw672 get3Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get4Name tempKesselSoll&lt;br /&gt;
attr ozw672 get4URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1910&lt;br /&gt;
attr ozw672 get4Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get5Name tempKesselRuecklauf&lt;br /&gt;
attr ozw672 get5URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1915&lt;br /&gt;
attr ozw672 get5Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get6Name tempKesselRuecklaufSoll&lt;br /&gt;
attr ozw672 get6URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1916&lt;br /&gt;
attr ozw672 get6Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get7Name anzahlStartsBrenner&lt;br /&gt;
attr ozw672 get7URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1927&lt;br /&gt;
attr ozw672 get7PollDelay 1800&lt;br /&gt;
attr ozw672 get7Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get8Name statusKessel&lt;br /&gt;
attr ozw672 get8URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1898&lt;br /&gt;
attr ozw672 get8Poll 1&lt;br /&gt;
attr ozw672 get8Regex Value&amp;quot;: &amp;quot;([a-zA-Zü ]*)&amp;quot;&lt;br /&gt;
attr ozw672 get8OMap Aus:0, Nachlauf aktiv:5, Freigegeben für TWW:10, Freigegeben für HK:20, In Teillastbetrieb für TWW:40, In Teillastbetrieb für HK:50, In Betrieb für Trinkwasser:90, In Betrieb für Heizkreis:100&lt;br /&gt;
&lt;br /&gt;
attr ozw672 getRegex Value&amp;quot;: &amp;quot;[ ]*([-.0-9]*)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attr ozw672 reAuthRegex .*session not valid.*&lt;br /&gt;
attr ozw672 sid1IDRegex .*&amp;quot;(.*-.*-.*-[0-9a-z]*).*&lt;br /&gt;
attr ozw672 sid1URL https://192.168.178.8/api/auth/login.json?user=test&amp;amp;pwd=test&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== All attributes ==&lt;br /&gt;
;reading[0-9]+Name&lt;br /&gt;
:the name of a reading to extract with the corresponding readingRegex, readingJSON, readingXPath or readingXPath-Strict&lt;br /&gt;
:Please note that the old syntax &amp;lt;b&amp;gt;readingsName.*&amp;lt;/b&amp;gt; does not work with all features of HTTPMOD and should be avoided. It might go away in a future version of HTTPMOD.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]+Name&lt;br /&gt;
:Name of a get or set command&lt;br /&gt;
:If the HTTP response that is received after the command is parsed with an individual parse option then this name is also used as a reading name. Please note that no individual parsing needs to be defined for a get or set. If no regex, XPath or JSON is specified for the command, then HTTPMOD will try to parse the response using all the defined readingRegex, reading XPath or readingJSON attributes.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+Regex&lt;br /&gt;
:If this attribute is specified, the Regex defined here is used to extract the value from the HTTP Response and assign it to a Reading with the name defined in the (get|set|reading)[0-9]+Name attribute.&lt;br /&gt;
:If this attribute is not specified for an individual Reading or get or set but without the numbers in the middle, e.g. as getRegex or readingRegex, then it applies to all the other readings / get / set commands where no specific Regex is defined.&amp;lt;br&amp;gt;&lt;br /&gt;
:The value to extract should be in a capture group / sub expression e.g. ([\d\.]+) in the above example. Multiple capture groups will create multiple readings (see explanation above)&lt;br /&gt;
:Using this attribute for a set command (setXXRegex) only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
:Please note that the old syntax &amp;lt;code&amp;gt;readingsRegex.*&amp;lt;/code&amp;gt; does not work with all features of HTTPMOD and should be avoided. It might go away in a future version of HTTPMOD.&lt;br /&gt;
:If for get or set commands neither a generic Regex attribute without numbers nor a specific (get|set)[0-9]+Regex attribute is specified and also no XPath or JSON parsing specification is given for the get or set command, then HTTPMOD tries to use the parsing definitions for general readings defined in reading[0-9]+Name, reading[0-9]+Regex or XPath or JSON attributes and assigns the Readings that match here.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+RegOpt&lt;br /&gt;
:Lets the user specify regular expression modifiers. For example if the same regular expression should be matched as often as possible in the HTTP response, then you can specify RegOpt g which will case the matching to be done as /regex/g&lt;br /&gt;
:The results will be trated the same way as multiple capture groups so the reading name will be extended with -number. &lt;br /&gt;
:For other possible regular expression modifiers see http://perldoc.perl.org/perlre.html#Modifiers&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+XPath&lt;br /&gt;
:defines an xpath to one or more values when parsing HTML data (see examples above)&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;get|set|reading[0-9]+XPath-Strict&lt;br /&gt;
:defines an xpath to one or more values when parsing XML data (see examples above)&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+AutoNumLen&lt;br /&gt;
:In cases where a regular expression or an XPath results in multiple results and these results are stored in a common reading name with extension -number, then you can modify the format of this number to have a fixed length with leading zeros. AutoNumLen 3 for example will lead to reading names ending with -001 -002 and so on.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+AlwaysNum&lt;br /&gt;
:if set to 1 this attributes forces reading names to end with a -1, -01 (depending on the above described AutoNumLen) even if just one value is parsed.&lt;br /&gt;
&lt;br /&gt;
;get|set|reading[0-9]+JSON&lt;br /&gt;
:defines a path to the JSON object wanted by concatenating the object names. See the above example.&lt;br /&gt;
:If you don&#039;t know the paths, then start by using extractAllJSON and the use the names of the readings as values for the JSON attribute.&amp;lt;br&amp;gt;&lt;br /&gt;
:Please don&#039;t forget to also specify a name for a reading, get or set. &lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]*RecombineExpr&lt;br /&gt;
:defines an expression that is used in an eval to compute one reading value out of the list of matches.&lt;br /&gt;
:This is supposed to be used for regexes or xpath specifications that produce multiple results if only one result that combines them is wanted. The list of matches will be in the variable @matchlist.&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;get[0-9]*CheckAllReadings&lt;br /&gt;
:this attribute modifies the behavior of HTTPMOD when the HTTP Response of a get command is parsed. &amp;lt;br&amp;gt;&lt;br /&gt;
:If this attribute is set to 1, then additionally to the matching of the corresponding get specific regex (get[0-9]*Regex), XPath or JSON attribute also all the reading names and parse definitions defined in Reading[0-9]+Name and Reading[0-9]+Regex, XPath or JSON attributes are checked and if they match, the coresponding Readings are assigned as well.&lt;br /&gt;
:Please note that this does not mean that get01CheckAllReadings will cause a get02Regex to be used. Only the corresponding get01Regex will be used but additionally all the readingXYRegex attributes.&lt;br /&gt;
:This is automatically done if a get or set command is defined without its own parse attributes.&lt;br /&gt;
&lt;br /&gt;
;(get|reading)[0-9]*OExpr&lt;br /&gt;
:defines an optional expression that is used in an eval to compute / format a readings value after parsing an HTTP response&lt;br /&gt;
:The raw value from the parsing will be in the variable $val.&lt;br /&gt;
:If specified as readingOExpr then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*Expr.&lt;br /&gt;
:Please note that the old syntax &amp;lt;b&amp;gt;readingsExpr.*&amp;lt;/b&amp;gt; does not work with all features of HTTPMOD and should be avoided. It might go away in a future version of HTTPMOD.&lt;br /&gt;
&lt;br /&gt;
;(get|reading)[0-9]*Expr&lt;br /&gt;
:This is the old syntax for (get|reading)[0-9]*OExpr. It should be replaced by (get|reading)[0-9]*OExpr. The set command upgradeAttributes which becomes visible when the attribute enableControlSet is set to 1, can do this renaming automatically.&lt;br /&gt;
&lt;br /&gt;
;(get|reading)[0-9]*OMap&lt;br /&gt;
:Map that defines a mapping from raw value parsed to visible values like &amp;quot;0:mittig, 1:oberhalb, 2:unterhalb&amp;quot;.&lt;br /&gt;
:If specified as readingOMap then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*Map.&amp;lt;br&amp;gt;&lt;br /&gt;
:The individual options in a map are separated by a komma and an optional space. Spaces are allowed to appear in a visible value however kommas are not possible.&lt;br /&gt;
&lt;br /&gt;
;(get|reading)[0-9]*Map&lt;br /&gt;
:This is the old syntax for (get|reading)[0-9]*OMap. It should be replaced by (get|reading)[0-9]*OMap. The set command upgradeAttributes which becomes visible when the attribute enableControlSet is set to 1, can do this renaming automatically.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]*Format&lt;br /&gt;
:Defines a format string that will be used in sprintf to format a reading value.&lt;br /&gt;
:If specified without the numbers in the middle e.g. as readingFormat then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*Format.&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]*Decode&lt;br /&gt;
:defines an encoding to be used in a call to the perl function decode to convert the raw data string read from the device to a reading. &lt;br /&gt;
:This can be used if the device delivers strings in an encoding like cp850 instead of utf8.&lt;br /&gt;
:If your reading values contain Umlauts and they are shown as strange looking icons then you probably need to use this feature.&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]*Encode&lt;br /&gt;
:defines an encoding to be used in a call to the perl function encode to convert the raw data string read from the device to a reading. &lt;br /&gt;
:This can be used if the device delivers strings in an encoding like cp850 and after decoding it you want to reencode it to e.g. utf8.&lt;br /&gt;
:If your reading values contain Umlauts and they are shown as strange looking icons then you probably need to use this feature.&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*URL&lt;br /&gt;
:URL to be requested for the set or get command. If this option is missing, the URL specified during define will be used.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*Data&lt;br /&gt;
:Data to be sent to the device as POST data when the get or set command is executed. if this attribute is not specified, an HTTP GET method will be used instead of an HTTP POST&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*NoData&lt;br /&gt;
:can be used to override a more generic attribute that specifies POST data for all get or set commands. With NoData no data is sent and therefor the request will be an HTTP GET.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*Header.*&lt;br /&gt;
:HTTP Headers to be sent to the device when the set is executed&lt;br /&gt;
&lt;br /&gt;
;requestHeader.*&lt;br /&gt;
:Define an optional additional HTTP Header to set in the HTTP request of the main loop&lt;br /&gt;
&lt;br /&gt;
;requestData&lt;br /&gt;
:optional POST Data to be sent in the request of the main loop. If not defined, it will be an HTTP GET request as defined in HttpUtils which is used by this module&lt;br /&gt;
&lt;br /&gt;
;get[0-9]+Poll&lt;br /&gt;
:if set to 1 the get is executed automatically during the normal update cycle (after the interval provided in the define command has elapsed)&lt;br /&gt;
&lt;br /&gt;
;get[0-9]+PollDelay&lt;br /&gt;
:if the value should not be read in each iteration (after the interval given to the define command), then a minimum delay can be specified with this attribute. This has only an effect if the above Poll attribute has also been set. Every time the update function is called, it checks if since this get has been read the last time, the defined delay has elapsed. If not, then it is skipped this time.&lt;br /&gt;
:PollDelay can be specified as seconds or as x[0-9]+ which means a multiple of the interval in the define command.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*TextArg&lt;br /&gt;
:For a get command this defines that the command accepts a text value after the option name. By default a get command doesn&#039;t accept optional values after the command name. &lt;br /&gt;
:If TextArg is specified and a value is passed after the get name then this value can then be used in a request URL, header or data as replacement for $val or in a user defined replacement that uses the internal &amp;quot;value&amp;quot; ($hash-&amp;gt;{value}).&lt;br /&gt;
:If used for a set command then it defines that the value to be set doesn&#039;t require any validation / conversion. &lt;br /&gt;
:The raw value is passed on as text to the device. By default a set command expects a numerical value or a text value that is converted to a numeric value using a map.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Min&lt;br /&gt;
:Minimum value for input validation. &lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Max&lt;br /&gt;
:Maximum value for input validation. &lt;br /&gt;
&lt;br /&gt;
;set[0-9]+IExpr&lt;br /&gt;
:Perl Expression to compute the raw value to be sent to the device from the input value passed to the set.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Expr&lt;br /&gt;
:This is the old syntax for (get|reading)[0-9]*IExpr. It should be replaced by (get|reading)[0-9]*IExpr. The set command upgradeAttributes which becomes visible when the attribute enableControlSet is set to 1, can do this renaming automatically.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+IMap&lt;br /&gt;
:Map that defines a mapping from raw to input values like &amp;quot;0:mittig, 1:oberhalb, 2:unterhalb&amp;quot;. This attribute atomatically creates a hint for FHEMWEB so the user can choose one of the input values.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Map&lt;br /&gt;
:This is the old syntax for (get|reading)[0-9]*IMap. It should be replaced by (get|reading)[0-9]*IMap. The set command upgradeAttributes which becomes visible when the attribute enableControlSet is set to 1, can do this renaming automatically.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Hint&lt;br /&gt;
:Explicit hint for fhemWEB that will be returned when set ? is seen. Can be used to get a slider or a list of values to choose from.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]*NoArg&lt;br /&gt;
:Defines that this set option doesn&#039;t require arguments. It allows sets like &amp;quot;on&amp;quot; or &amp;quot;off&amp;quot; without further values.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]*ParseResponse&lt;br /&gt;
:defines that the HTTP response to the set will be parsed as if it was the response to a get command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*URLExpr&lt;br /&gt;
:Defines a Perl expression to specify the HTTP Headers for this request. This overwrites any other header specification and should be used carefully only if needed. The original Header is availabe as $old. Typically this feature is not needed and it might go away in future versions of HTTPMOD. Please use the &amp;quot;replacement&amp;quot; attributes if you want to pass additional variable data to a web service. &lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*DatExpr&lt;br /&gt;
:Defines a Perl expression to specify the HTTP Post data for this request. This overwrites any other post data specification and should be used carefully only if needed. The original Data is availabe as $old. Typically this feature is not needed and it might go away in future versions of HTTPMOD. Please use the &amp;quot;replacement&amp;quot; attributes if you want to pass additional variable data to a web service. &lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*HdrExpr&lt;br /&gt;
:Defines a Perl expression to specify the URL for this request. This overwrites any other URL specification and should be used carefully only if needed. The original URL is availabe as $old. Typically this feature is not needed and it might go away in future versions of HTTPMOD. Please use the &amp;quot;replacement&amp;quot; attributes if you want to pass additional variable data to a web service.           &lt;br /&gt;
&lt;br /&gt;
;ReAuthRegex&lt;br /&gt;
:regular Expression to match an error page indicating that a session has expired and a new authentication for read access needs to be done. &lt;br /&gt;
:This attribute only makes sense if you need a forms based authentication for reading data and if you specify a multi step login procedure based on the sid.. attributes.&lt;br /&gt;
:This attribute is used for all requests. For set and get operations you can however specify individual reAuthRegexes with the (get|set)[0-9]*ReAuthRegex attributes.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*ReAuthRegex&lt;br /&gt;
:Regex that will detect when a session has expired during a set operation and a new login needs to be performed.&lt;br /&gt;
:It works like the global reAuthRegex but is used for set operations.&lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*URL&lt;br /&gt;
:different URLs or one common URL to be used for each step of an optional login procedure. &lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*IDRegex&lt;br /&gt;
:different Regexes per login procedure step or one common Regex for all steps to extract the session ID from the HTTP response&lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*Data.*&lt;br /&gt;
:data part for each step to be sent as POST data to the corresponding URL&lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*Header.*&lt;br /&gt;
:HTTP Headers to be sent to the URL for the corresponding step&lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*IgnoreRedirects&lt;br /&gt;
:Tells the HttpUtils to not follow HTTP Redirects for this Request. Might be needed for some devices that set a session cookie within a 303 Redirect.&lt;br /&gt;
&lt;br /&gt;
;clearSIdBeforeAuth&lt;br /&gt;
:will set the session id to &amp;quot;&amp;quot; before doing the authentication steps&lt;br /&gt;
&lt;br /&gt;
;authRetries&lt;br /&gt;
:number of retries for authentication procedure - defaults to 1&lt;br /&gt;
&lt;br /&gt;
;replacement[0-9]*Regex&lt;br /&gt;
:Defines a replacement to be applied to an HTTP request header, data or URL before it is sent. This allows any part of the request to be modified based on a reading, an internal or an expression.&lt;br /&gt;
:The regex defines which part of a header, data or URL should be replaced. The replacement is defined with the following attributes:&lt;br /&gt;
&lt;br /&gt;
;replacement[0-9]*Mode&lt;br /&gt;
:Defines how the replacement should be done and what replacementValue means. Valid options are text, reading, internal and expression.&lt;br /&gt;
&lt;br /&gt;
;replacement[0-9]*Value&lt;br /&gt;
:Defines the replacement. If the corresponding replacementMode is &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;, then value is a static text that is used as the replacement.&lt;br /&gt;
:If replacementMode is &amp;lt;code&amp;gt;reading&amp;lt;/code&amp;gt; then Value can be the name of a reading of this device or it can be a reading of a different device referred to by devicename:reading.&lt;br /&gt;
:If replacementMode is &amp;lt;code&amp;gt;internal&amp;lt;/code&amp;gt; the Value can be the name of an internal of this device or it can be an internal of a different device referred to by devicename:internal.&lt;br /&gt;
:If replacementMode is &amp;lt;code&amp;gt;expression&amp;lt;/code&amp;gt; the the Value is treated as a Perl expression that computes the replacement value. The expression can use $1, $2 and so on to refer to capture groups of the corresponding regex that is matched against the original URL, header or post data.&lt;br /&gt;
:If replacementMode is &amp;lt;code&amp;gt;key&amp;lt;/code&amp;gt; then the module will use a value from a key / value pair that is stored in an obfuscated form in the file system with the set storeKeyValue command. This might be useful for storing passwords.&lt;br /&gt;
&lt;br /&gt;
;[gs]et[0-9]*Replacement[0-9]*Value&lt;br /&gt;
:This attribute can be used to override the replacement value for a specific get or set.&lt;br /&gt;
&lt;br /&gt;
;get|reading[0-9]*MaxAge&lt;br /&gt;
:Defines how long a reading is valid before it is automatically overwritten with a replacement when the read function is called the next time.&lt;br /&gt;
&lt;br /&gt;
;get|reading[0-9]*MaxAgeReplacement&lt;br /&gt;
:specifies the replacement for MaxAge - either as a static text or as a perl expression.&lt;br /&gt;
&lt;br /&gt;
;get|reading[0-9]*MaxAgeReplacementMode&lt;br /&gt;
:specifies how the replacement is interpreted: can be text, expression and delete.&lt;br /&gt;
&lt;br /&gt;
;get|reading[0-9]*DeleteIfUnmatched&lt;br /&gt;
:If set to 1 this attribute causes certain readings to be deleted when the parsing of the website does not match the specified reading. Internally HTTPMOD remembers which kind of operation created a reading (update, Get01, Get02 and so on). Specified readings will only be deleted if the same operation does not parse this reading again. This is especially useful for parsing that creates several matches / readings and the number of matches can vary from request to request. For example if reading01Regex creates 4 readings in one update cycle and in the next cycle it only matches two times then the readings containing the remaining values from the last round will be deleted.&lt;br /&gt;
:Please note that this mechanism will not work in all cases after a restart. Especially when a get definition does not contain its own parsing definition but ExtractAllJSON or relies on HTTPMOD to use all defined reading.* attributes to parse the responsee to a get command, old readings might not be deleted after a restart of fhem.&lt;br /&gt;
;get|reading[0-9]*DeleteOnError&lt;br /&gt;
:If set to 1 this attribute causes certain readings to be deleted when the website can not be reached and the HTTP request returns an error. Internally HTTPMOD remembers which kind of operation created a reading (update, Get01, Get02 and so on). Specified readings will only be deleted if the same operation returns an error.&lt;br /&gt;
The same restrictions as for DeleteIfUnmatched apply regarding a fhem restart.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;httpVersion&lt;br /&gt;
:defines the HTTP-Version to be sent to the server. This defaults to 1.0.&lt;br /&gt;
&lt;br /&gt;
;sslVersion&lt;br /&gt;
:defines the SSL Version for the negotiation with the server. The attribute is evaluated by HttpUtils. If it is not specified, HttpUtils assumes SSLv23:!SSLv3:!SSLv2&lt;br /&gt;
&lt;br /&gt;
;sslArgs&lt;br /&gt;
:defines a list that is converted to a key / value hash and gets passed to HttpUtils. To avoid certificate validation for broken servers you can for example specify &lt;br /&gt;
:&amp;lt;code&amp;gt;attr myDevice sslArgs SSL_verify_mode,SSL_VERIFY_NONE&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;alignTime&lt;br /&gt;
:Aligns each periodic read request for the defined interval to this base time. This is typcally something like 00:00 (see the FHEM at command)&lt;br /&gt;
&lt;br /&gt;
;noShutdown&lt;br /&gt;
:pass the noshutdown flag to HTTPUtils for webservers that need it (some embedded webservers only deliver empty pages otherwise)&lt;br /&gt;
&lt;br /&gt;
;disable&lt;br /&gt;
:stop doing automatic HTTP requests while this attribute is set to 1&lt;br /&gt;
&lt;br /&gt;
;enableControlSet&lt;br /&gt;
:enables the built in set commands interval, stop, start, reread, upgradeAttributes, storeKeyValue.&lt;br /&gt;
&lt;br /&gt;
;enableCookies&lt;br /&gt;
:enables the built in cookie handling if set to 1. With cookie handling each HTTPMOD device will remember cookies that the server sets and send them back to the server in the following requests. &lt;br /&gt;
:This simplifies session magamenet in cases where the server uses a session ID in a cookie. In such cases enabling cookies should be sufficient and no sidRegex and no manual definition of a cookie header should be necessary.&lt;br /&gt;
&lt;br /&gt;
;showMatched&lt;br /&gt;
:if set to 1 then HTTPMOD will create a reading with the name MATCHED_READINGS that contains the names of all readings that could be matched in the last request as well as UNMATCHED_READINGS and LAST_REQUEST.&lt;br /&gt;
&lt;br /&gt;
;showError&lt;br /&gt;
:if set to 1 then HTTPMOD will create a reading and event with the Name LAST_ERROR that contains the error message of the last error returned from HttpUtils. &lt;br /&gt;
&lt;br /&gt;
;removeBuf&lt;br /&gt;
:if set to 1 then HTTPMOD removes the internal named buf when a HTTP-response has been received. &lt;br /&gt;
:$hash-&amp;gt;{buf} is used internally be Fhem httpUtils and in some use cases it is desireable to remove this internal after reception &lt;br /&gt;
:because it contains a very long response which looks ugly in Fhemweb.&lt;br /&gt;
&lt;br /&gt;
;timeout&lt;br /&gt;
:time in seconds to wait for an answer. Default value is 2&lt;br /&gt;
&lt;br /&gt;
;queueDelay&lt;br /&gt;
:HTTP Requests will be sent from a queue in order to avoid blocking when several Requests have to be sent in sequence. This attribute defines the delay between calls to the function that handles the send queue. It defaults to one second.&lt;br /&gt;
&lt;br /&gt;
;queueMax&lt;br /&gt;
:Defines the maximum size of the send queue. If it is reached then further HTTP Requests will be dropped and not be added to the queue&lt;br /&gt;
&lt;br /&gt;
;minSendDelay&lt;br /&gt;
:Defines the minimum time between two HTTP Requests.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Example: This example uses many features of HTTPMOD, universally useful is the access to other readings just like what [[Set magic|set-magic]] (&amp;lt;code&amp;gt;[device:reading]&amp;lt;/code&amp;gt;) does [https://forum.fhem.de/index.php?topic=135666.0 🎸 Owntone (Musik-Player) mit FHEM steuern]&lt;br /&gt;
* Example: [https://www.goingelectric.de/forum/viewtopic.php?f=57&amp;amp;t=21462|Abfrage Akkustand einer Zoe und mehr von Renault ZE Services]&lt;br /&gt;
* Example: [[Wetter_und_Wettervorhersagen#Wetter_von_Weather_Underground|Extract weather information from WeatherUnderground]]&lt;br /&gt;
* Example: [[Pollenflug|Pollen count]]&lt;br /&gt;
* Example: [[HTTPMOD Beispielkonfiguration zur Anbindung einer Daikin Klimaanlage mit WLAN-Modul|Connect Daikin aircondition to FHEM]]&lt;br /&gt;
* Example: [[Go-eCharger|Extract information from go-eCharger]]&lt;br /&gt;
* Example: [[Sonnenspeicher|Integration of energy supplies from sonnen (https://sonnenbatterie.de/en/start)]]&lt;br /&gt;
* Example: [https://forum.fhem.de/index.php/topic,95989.msg915870.html#msg915870 Miele 3rd party API für Miele Smarthome Geräte]&lt;br /&gt;
* Example: [https://forum.fhem.de/index.php/topic,84215.msg918662.html#msg918662 Honeywell Evohome Totalconnect]&lt;br /&gt;
* Example: [https://forum.fhem.de/index.php/topic,78613.msg889015.html#msg889015 Tigo Energy Integration]&lt;br /&gt;
* Example: [https://forum.fhem.de/index.php/topic,78613.msg708518.html#msg708518 Ecowater]&lt;br /&gt;
* {{Link2Forum|Topic=17804|LinkText=Thread}} in FHEM Forum that discusses the first version of this module &lt;br /&gt;
* {{Link2Forum|Topic=29471|LinkText=Thread}} in FHEM Forum that discusses the second major version of this module &lt;br /&gt;
* {{Link2Forum|Topic=45176|LinkText=Thread}} in FHEM Forum that discusses the third major version of this module &lt;br /&gt;
* [http://perldoc.perl.org/perlretut.html Introduction to regular expressions]&lt;br /&gt;
* [http://portswigger.net/burp/ BurpSuite]: Tool (local proxy) to help analyze http traffic&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:IP Components]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=HTTPMOD&amp;diff=40087</id>
		<title>HTTPMOD</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=HTTPMOD&amp;diff=40087"/>
		<updated>2025-04-05T09:20:51Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* Some help with Regular Expressions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Extract information from devices with an HTTP interface (or, more generic, from any URL) or send information to such devices &lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=HTTPMOD&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=98_HTTPMOD.pm&lt;br /&gt;
|ModOwner=StefanStrobel ({{Link2FU|3960|Forum}} / [[Benutzer:StefanStrobel|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
[[HTTPMOD]] provides a generic way to retrieve information from devices with an HTTP Interface and store them in Readings or send information to such devices. It queries a given URL with Headers and data defined by attributes. &lt;br /&gt;
&lt;br /&gt;
From the HTTP response it extracts readings named in attributes using Regexes, JSON or XPath parsing also defined by attributes.&lt;br /&gt;
&lt;br /&gt;
In an advanced [[Konfiguration|configuration]] the module can also send information to devices. To do this, a generic &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; option can be configured using attributes. &lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
With the help of HTTPMOD you can automatically access websites, retrieve information and send data. In the simplest case, an http call is started regularly and the content of a page, for example, is read and processed further. The URL and the time interval in which the call is to be made must then be specified when the call is made. In order to read information from the called web pages, readings are required. In this readings one specifies Regex, which reads the necessary information. Here is an example.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define PM HTTPMOD  http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/-1/free.html 20000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a device that accesses the http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/-1/free.html website with the TV program every 20000 seconds. If neither readings nor other attributes are defined, then only the call of the website takes place and FHEM receives no further data from the web access.&lt;br /&gt;
&lt;br /&gt;
But now a television program is returned when you call the website, which you might want to read out. In order to save the contents in FHEM, which are related to the ARD, you need a reading. This reading must contain a name (so that FHEM knows under which name the information is stored) and a regex (so that you know where the information is stored on the website). Since usually not one, but many readings are defined, these readings are numbered consecutively. This is done by numbers that are given immediately after the word reading. In our example this would be for example&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM reading1Name ARD &lt;br /&gt;
attr PM reading1Regex &amp;lt;span class=&amp;quot;stationName&amp;quot;&amp;gt;ARD&amp;lt;\/span&amp;gt;[\w\W]*?&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;\s*&amp;lt;a[\w\W]*?&amp;gt;\s*(.*?)\s*&amp;lt;\/a&amp;gt; &lt;br /&gt;
attr PM reading2Name ARD_time &lt;br /&gt;
attr PM reading2Regex &amp;lt;span class=&amp;quot;stationName&amp;quot;&amp;gt;ARD&amp;lt;\/span&amp;gt;[\w\W]*?&amp;lt;td class=&amp;quot;time&amp;quot;&amp;gt;\s*(.*?)\s*&amp;lt; &lt;br /&gt;
attr PM reading3Name ARD_details &lt;br /&gt;
attr PM reading3Regex &amp;lt;span class=&amp;quot;stationName&amp;quot;&amp;gt;ARD&amp;lt;\/span&amp;gt;[\w\W]*?&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;\s*&amp;lt;a[\w\W]*?&amp;gt;[\w\W]*?&amp;lt;\/a&amp;gt;\s*&amp;lt;br\/&amp;gt;\s*(.*?)(&amp;lt;img|&amp;lt;\/div) &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reading1 contains the programme, reading2 the time and reading3 further details.  The Regex result from the structure of the website and may have to be designed in painstaking detail.&lt;br /&gt;
&lt;br /&gt;
There are cases where this is not enough. For example, it could be the case that the information from the web access is not automated, but manually received. Then a 0 must first be entered at the top of the duration. But how does HTTPMOD then know when the data should be retrieved? This is done with get-commands. If a get-command is executed, HTTPMOD accesses the website, fetches the information and fills the corresponding readings. &lt;br /&gt;
&lt;br /&gt;
Again, it is possible to create not one but several get-commands. Therefore, get is always followed by (at least) one number with which the get commands are numbered.  Here is another example. The TV program should be called manually and not time-controlled. To do this we first change the definition to &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define PM HTTPMOD  http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/-1/free.html 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
and add the following to the attributes&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM get1Name ARDHolen &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
With the command &amp;quot;get PM ARDHolen&amp;quot; the above readings are filled. In order for FHEM to know that the other reading2, reading3, etc. are to be read as well as reading1, the (user) attribute  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM get1CheckAllReadings 1 &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
needs to be defined.&lt;br /&gt;
&lt;br /&gt;
If the URL is not sufficient when the web page is called, but a special header is to be sent with it, this can be done with the get1Header attribute. If the header is multiline, the individual lines can be entered with get1Header1, get1Header2, and so on.    &lt;br /&gt;
&lt;br /&gt;
If you want to evaluate or control different URLs, you can define several get commands (get1, get2, etc.) and provide them with your own URLs. get1 usually reads only the reading1, get2 reads only the reading2 and so on. With CheckAllReadings all readings are evaluated simultaneously.&lt;br /&gt;
&lt;br /&gt;
If only data is to be sent on the web page, but not read, the set command is recommended. It has the same structure as the get-command.&lt;br /&gt;
&lt;br /&gt;
It becomes a bit more complex if the website requires a login check. The sid attributes are used for this purpose.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Availability, prerequisites and definition ==&lt;br /&gt;
This module is part of the regular FHEM distribution and uses the non blocking HTTP function &amp;lt;code&amp;gt;HttpUtils_NonblockingGet&amp;lt;/code&amp;gt; provided by FHEM&#039;s [[HttpUtils]] in a new version published in December 2013.&lt;br /&gt;
If not already installed in your environment, please [[update]] FHEM or install it manually using appropriate commands from your environment.&lt;br /&gt;
Please also note that FHEM HttpUtils need the global attribute dnsServer to be set in order to work really non blocking even when dns requests can not be answered.&lt;br /&gt;
&lt;br /&gt;
The device is defined as follows&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define &amp;lt;name&amp;gt; HTTPMOD &amp;lt;URL&amp;gt; &amp;lt;Interval&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The module connects to the given &amp;lt;code&amp;gt;URL&amp;lt;/code&amp;gt; every &amp;lt;code&amp;gt;Interval&amp;lt;/code&amp;gt; seconds, sends optional headers and data and then parses the response with regular expressions, xpath or json to set readings.&lt;br /&gt;
&lt;br /&gt;
URL can be &amp;quot;none&amp;quot; and Interval can be 0 if you prefer to only query data with a get command and not in a defined interval.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define PM HTTPMOD http://MyPoolManager/cgi-bin/webgui.fcgi 60&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Set-Commands ==&lt;br /&gt;
can be defined using attributes, see advanced configuration&lt;br /&gt;
&lt;br /&gt;
If you set the attribute enableControlSet to 1, the following additional built in set commands are available:&lt;br /&gt;
;interval&lt;br /&gt;
:set new interval time in seconds and restart the timer&lt;br /&gt;
;reread&lt;br /&gt;
:request the defined URL and try to parse it just like the automatic update would do it every Interval seconds without modifying the running timer.&lt;br /&gt;
;stop&lt;br /&gt;
:stop interval timer.&lt;br /&gt;
;start&lt;br /&gt;
:restart interval timer to call GetUpdate after interval seconds&lt;br /&gt;
;upgradeAttributes&lt;br /&gt;
:convert outdated attributes for older HTTPMOD-Versions that are still defined for this device from the old syntax to the new one.&lt;br /&gt;
:attributes with the description &amp;quot;this attribute should not be used anymore&amp;quot; or similar will be translated to the new syntax, e.g. readingsName1 to reading01Name.&lt;br /&gt;
;storeKeyValue&lt;br /&gt;
:stores a key value pair in an obfuscated form in the file system. Such values can then be used in replacements where the mode is &amp;quot;key&amp;quot; e.g. to avoid storing passwords in the configuration in clear text&lt;br /&gt;
&lt;br /&gt;
== Get-Commands ==&lt;br /&gt;
can be defined using attributes, see advanced configuration&lt;br /&gt;
&lt;br /&gt;
== simple Attributes ==&lt;br /&gt;
;enableControlSet&lt;br /&gt;
:enables the built in set commands &#039;&#039;interval&#039;&#039;, &#039;&#039;stop&#039;&#039;, &#039;&#039;start&#039;&#039;, &#039;&#039;reread&#039;&#039;, &#039;&#039;upgradeAttributes&#039;&#039;, &#039;&#039;storeKeyValue&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;enableCookies&lt;br /&gt;
:enables the cookie handling inside HTTPMOD. It is advisable to always set this attribute and allow HTTPMOD to track the state of cookies and set them for following HTTP-requests&lt;br /&gt;
&lt;br /&gt;
;enforceGoodReadingNames&lt;br /&gt;
:makes sure that HTTPMOD only creates readings that are allowd for Fhem (especially if reading names are dynamically created from JSON object names with extractAllJSON. It is advisable to always set this attribute.&lt;br /&gt;
&lt;br /&gt;
;handleRedirects&lt;br /&gt;
:enables the redirect handling inside HTTPMOD which should be used together with the cookie handling of HTTPMOD. HTTPMOD will then automatically follow redirects from a web server and keep track of cookies at the same time. It is advisable to always set this attribute.&lt;br /&gt;
&lt;br /&gt;
;requestHeader.* &lt;br /&gt;
:Define an additional HTTP Header to set in the HTTP request&lt;br /&gt;
&lt;br /&gt;
;requestData&lt;br /&gt;
:POST Data to be sent in the request. If not defined, it will be a GET request as defined in HttpUtils used by this module&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+(-[0-9]+)?Name&lt;br /&gt;
:the name of a reading to extract with the corresponding readingRegex&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?OExpr&lt;br /&gt;
:defines an expression that is used in an eval to compute the readings value. The raw value will be in the variable $val.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?OMap&lt;br /&gt;
:Output Map. Defines a mapping from raw to visible values like &amp;quot;0:mittig, 1:oberhalb, 2:unterhalb&amp;quot;. If specified as readingOMap then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*OMap.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?Format&lt;br /&gt;
:Defines a format string that will be used in sprintf to format a reading value. If specified as readingFormat then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*Format.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?Decode&lt;br /&gt;
:defines an encoding to be used in a call to the perl function decode to convert the raw data string read from the device to a reading. This can be used if the device delivers strings in an encoding like cp850 instead of utf8.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?Encode&lt;br /&gt;
:defines an encoding to be used in a call to the perl function encode to convert the raw data string read from the device to a reading. This can be used if the device delivers strings in an encoding like cp850 and after decoding it you want to reencode it to e.g. utf8.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+Regex&lt;br /&gt;
:defines the regex to be used for extracting the reading. The value to extract should be in a capture group / sub expression &lt;br /&gt;
:e.g. ([\d\.]+) in the above example. Multiple capture groups will create multiple readings (see explanation above)&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+XPath&lt;br /&gt;
:defines an xpath to one or more readings when parsing HTML data (see examples below)&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+XPath-Strict&lt;br /&gt;
:defines an xpath to one or more readings when parsing XML data (see examples below)&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+JSON&lt;br /&gt;
:defines a path to the JSON object wanted by concatenating the object names with an underscore as delimiter (see the example below)&lt;br /&gt;
&lt;br /&gt;
;noShutdown&lt;br /&gt;
:pass the noshutdown flag to HTTPUtils for webservers that need it (some embedded webservers only deliver empty pages otherwise)&lt;br /&gt;
&lt;br /&gt;
;disable&lt;br /&gt;
:stop doing automatic HTTP requests while this attribute is set to 1&lt;br /&gt;
&lt;br /&gt;
;timeout&lt;br /&gt;
:time in seconds to wait for an answer. Default value is 2&lt;br /&gt;
&lt;br /&gt;
;do_not_notify&lt;br /&gt;
&lt;br /&gt;
;readingFnAttributes&lt;br /&gt;
&lt;br /&gt;
== Simple Configuration of HTTP Devices ==&lt;br /&gt;
If your device expects special HTTP-headers then specify them as &amp;lt;code&amp;gt;attr requestHeader1&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;attr requestHeaderX&amp;lt;/code&amp;gt;.&lt;br /&gt;
If your Device expects an HTTP POST instead of HTTP GET then the POST-data can be specified as &amp;lt;code&amp;gt;attr requestData&amp;lt;/code&amp;gt;.&lt;br /&gt;
To get the readings, specify pairs of &amp;lt;code&amp;gt;attr readingXName&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;attr readingXRegex&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;attr readingXXPath&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;attr readingXXPath-Strict&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;attr readingXJSON&amp;lt;/code&amp;gt; to define which readings you want to extract from the HTTP response and how to extract them. (The old syntax &amp;lt;code&amp;gt;attr readingsNameX&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;attr readingsRegexX&amp;lt;/code&amp;gt; is still supported but the new one with &amp;lt;code&amp;gt;attr readingXName&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;attr readingXRegex&amp;lt;/code&amp;gt; should be preferred. The actual values to be extracted have to be sub expressions within () in the regex (see example below)&lt;br /&gt;
&lt;br /&gt;
=== Example for a PoolManager 5: ===&lt;br /&gt;
The PoolManager Web GUI can be queried with HTTP POST Requests like this one:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
POST /cgi-bin/webgui.fcgi HTTP/1.1&lt;br /&gt;
Host: 192.168.70.90&lt;br /&gt;
Accept: */*&lt;br /&gt;
Content-Type: application/json;charset=UTF-8&lt;br /&gt;
Content-Length: 60&lt;br /&gt;
&lt;br /&gt;
{&amp;quot;get&amp;quot; :[&amp;quot;34.4001.value&amp;quot; ,&amp;quot;34.4008.value&amp;quot; ,&amp;quot;34.4033.value&amp;quot;]}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The resulting HTTP Response would look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
HTTP/1.1 200 OK&lt;br /&gt;
Content-type: application/json; charset=UTF-8&lt;br /&gt;
Expires: 0&lt;br /&gt;
Cache-Control: no-cache&lt;br /&gt;
Date: Sun, 12 Jan 2014 12:23:11 GMT&lt;br /&gt;
Server: lighttpd/1.4.26&lt;br /&gt;
Content-Length: 179&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;data&amp;quot;:	{&lt;br /&gt;
		&amp;quot;34.4001.value&amp;quot;:	&amp;quot;7.00&amp;quot;,&lt;br /&gt;
		&amp;quot;34.4008.value&amp;quot;:	&amp;quot;0.52&amp;quot;,&lt;br /&gt;
		&amp;quot;34.4033.value&amp;quot;:	&amp;quot;24.8&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;status&amp;quot;:	{&lt;br /&gt;
		&amp;quot;code&amp;quot;:	0&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;event&amp;quot;:	{&lt;br /&gt;
		&amp;quot;type&amp;quot;:	1,&lt;br /&gt;
		&amp;quot;data&amp;quot;:	&amp;quot;48.30000.0&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To configure HTTPMOD for a PoolManager one would first define a PoolManager device with e.g. the name PM, the URL and an interval of e.g. 60 seconds. &lt;br /&gt;
&lt;br /&gt;
Then the data to be sent in the request needs to be defined because in this example the device expects a POST request so the query is not contained in the URL but in the request data.&lt;br /&gt;
&lt;br /&gt;
Also as seen above the device expects special HTTP headers in the request so these headers also need to be defined as &amp;lt;code&amp;gt;attr PM requestHeader1&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;attr PM requestHeader2&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then the names of the readings to be extracted would be set with attributes&lt;br /&gt;
&lt;br /&gt;
Then for each reading value to be extracted a regular expression needs to be set that will match the value in question within ().&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define PM HTTPMOD http://MyPoolManager/cgi-bin/webgui.fcgi 60&lt;br /&gt;
&lt;br /&gt;
attr PM enableControlSet 1&lt;br /&gt;
attr PM enableCookies 1&lt;br /&gt;
attr PM enforceGoodReadingNames 1&lt;br /&gt;
attr PM handleRedirects 1&lt;br /&gt;
&lt;br /&gt;
attr PM reading01Name PH&lt;br /&gt;
attr PM reading01Regex 34.4001.value&amp;quot;:[ \t]+&amp;quot;([\d\.]+)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attr PM reading02Name CL&lt;br /&gt;
attr PM reading02Regex 34.4008.value&amp;quot;:[ \t]+&amp;quot;([\d\.]+)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attr PM reading03Name3TEMP&lt;br /&gt;
attr PM reading03Regex 34.4033.value&amp;quot;:[ \t]+&amp;quot;([\d\.]+)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attr PM requestData {&amp;quot;get&amp;quot; :[&amp;quot;34.4001.value&amp;quot; ,&amp;quot;34.4008.value&amp;quot; ,&amp;quot;34.4033.value&amp;quot;, &amp;quot;14.16601.value&amp;quot;, &amp;quot;14.16602.value&amp;quot;]}&lt;br /&gt;
attr PM requestHeader1 Content-Type: application/json&lt;br /&gt;
attr PM requestHeader2 Accept: */*&lt;br /&gt;
attr PM stateFormat {sprintf(&amp;quot;%.1f Grad, PH %.1f, %.1f mg/l Chlor&amp;quot;, ReadingsVal($name,&amp;quot;TEMP&amp;quot;,0), ReadingsVal($name,&amp;quot;PH&amp;quot;,0), ReadingsVal($name,&amp;quot;CL&amp;quot;,0))}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Example for AmbientMonitor ===&lt;br /&gt;
AmbientMonitor is a webbased visualisation for sensors connected to an Arduino device. Its web interface can also be queried with HTTMOD to grab the data into readings.&lt;br /&gt;
&lt;br /&gt;
This example was provided by locutus. The hardware configuration is an Arduino + Ethercard with ENC28J60 Controller + DHT22 Sensor and software can be downloaded from https://github.com/lucadentella/AmbientMonitor&lt;br /&gt;
&lt;br /&gt;
In this example an HTTP GET is sufficent, so no &amp;lt;code&amp;gt;requestData&amp;lt;/code&amp;gt; is needed. The device provides temperature and humidity readings in an HTTP response that looks like:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
HTTP/1.0 200 OK &lt;br /&gt;
Content-Type: text/html &lt;br /&gt;
&lt;br /&gt;
myCB({&#039;temperature&#039;:22.00,&#039;humidity&#039;:46.00})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the definition could be:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define AmbientMonitor HTTPMOD http://192.168.1.221/?callback=? 300&lt;br /&gt;
&lt;br /&gt;
attr AmbientMonitor enableControlSet 1&lt;br /&gt;
attr AmbientMonitor enableCookies 1&lt;br /&gt;
attr AmbientMonitor enforceGoodReadingNames 1&lt;br /&gt;
attr AmbientMonitor handleRedirects 1&lt;br /&gt;
&lt;br /&gt;
attr AmbientMonitor requestHeader Content-Type: application/json&lt;br /&gt;
attr AmbientMonitor reading1Name Temperatur&lt;br /&gt;
attr AmbientMonitor reading1Regex temperature&#039;:([\d\.]+)&lt;br /&gt;
attr AmbientMonitor reading2Name Feuchtigkeit&lt;br /&gt;
attr AmbientMonitor reading2Regex humidity&#039;:([\d\.]+)&lt;br /&gt;
attr AmbientMonitor stateFormat {sprintf(&amp;quot;Temperatur %.1f C, Feuchtigkeit %.1f %&amp;quot;, ReadingsVal($name,&amp;quot;Temperatur&amp;quot;,0), ReadingsVal($name,&amp;quot;Feuchtigkeit&amp;quot;,0))}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== formatting and manipulating values / readings ==&lt;br /&gt;
Values that are parsed from an HTTP response can be further treated or formatted with the following attributes:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?OExpr&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?OMap&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?Format&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?Decode&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?Encode&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
They can all be specified for an individual reading, for all readings in one match (e.g. if a regular expression has several capture groups) or for all readings in a get command (defined by getXX) or for all readings in the main reading list (defined by readingXX):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading01Format %.1f&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will format the reading with the name specified by the attribute reading01Name to be numerical with one digit after the decimal point.&lt;br /&gt;
If the attribute reading01Regex is used and contains several capture groups then the format will be applied to all readings parsed by this regex unless these readings have their own format specified by reading01-1Format, reading01-2Format and so on.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading01-2Format %.1f&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be used in cases where a regular expression specified as reading01regex contains several capture groups or an xpath specified as reading01XPath creates several readings. &lt;br /&gt;
In this case reading01-2Format specifies the format to be applied to the second match.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
readingFormat %.1f&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
applies to all readings defined by a reading-Attribute that have no more specific format.&lt;br /&gt;
&lt;br /&gt;
If you need to do some calculation on a raw value before it is used as a reading, you can define the attribute &amp;lt;code&amp;gt;readingOExpr&amp;lt;/code&amp;gt;.&lt;br /&gt;
It defines a Perl expression that is used in an eval to compute the readings value. The raw value will be in the variable $val.&lt;br /&gt;
&lt;br /&gt;
Example for an expression:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM reading03OExpr $val * 10&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Just like in the above example of the readingFormat attributes, readingOExpr and the other following attributes can be applied on several levels.&lt;br /&gt;
&lt;br /&gt;
To map a numerical value to a name, you can use the readingOMap attribute. &lt;br /&gt;
It defines a mapping from raw to visible values like &amp;quot;0:mittig, 1:oberhalb, 2:unterhalb&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Example for a map:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM reading02-3OMap 0:kalt, 1:warm, 2:sehr warm&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To convert character sets, the module can first decode a string read from the device and then encode it again. For example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM getDecode UTF-8&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This applies to all readings defined for Get-Commands.&lt;br /&gt;
&lt;br /&gt;
== Some help with Regular Expressions ==&lt;br /&gt;
{{Randnotiz|RNTyp=y|RNText=Starting with version &#039;&#039;2018-02-10&#039;&#039; the internal that holds the HTTP response is no longer called &#039;&#039;&#039;&#039;&#039;buf&#039;&#039;&#039;&#039;&#039; but rather &#039;&#039;&#039;&#039;&#039;httpbody&#039;&#039;&#039;&#039;&#039;, and it is only displayed when attribute &#039;&#039;&#039;&#039;&#039;showBody&#039;&#039;&#039;&#039;&#039; is set to &amp;quot;1&amp;quot;.}}&lt;br /&gt;
If HTTPMOD seems not to work and the FHEM Logfile contains a message like  &lt;br /&gt;
:&amp;lt;code&amp;gt;HTTPMOD: Response didn&#039;t match Reading ...&amp;lt;/code&amp;gt;&lt;br /&gt;
then you should check if the value you want to extract is read into the internal with the name buf. Internals are visible when you click on the defined HTTPMOD Device. buf is an internal variable that contains the HTTP Response read. If the value is there and you get the mentioned message then probably something is wrong with your regular expression. Please note that buf might contain special characters like newlines but they are not shown in fhemweb. If you are new to regular expressions then the introduction at http://perldoc.perl.org/perlretut.html might be helpful. &lt;br /&gt;
&lt;br /&gt;
For a typical HTTPMOD use case where you want to extract a number out of a HTTP-Response you can use something like &amp;lt;code&amp;gt;[\d\.]+&amp;lt;/code&amp;gt; to match the number itself. The expression matches the number characters (&amp;lt;code&amp;gt;\d&amp;lt;/code&amp;gt;) or a &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; if one of these characters occurs at least once. &lt;br /&gt;
&lt;br /&gt;
To tell HTTPMOD that the number is what you want to use for the reading, you have to put the expression in between &amp;lt;code&amp;gt;()&amp;lt;/code&amp;gt;. A &amp;lt;code&amp;gt;([\d\.]+)&amp;lt;/code&amp;gt; alone would match the longest number in the HTTP Response which is very likely not the number you are looking for so you need to add something to the expression to give it a context and define how to find the number that you are looking for.&lt;br /&gt;
&lt;br /&gt;
If there is a title text before the number or a special text after the number you can put this in the regex. In one of the examples above &amp;lt;code&amp;gt;humidity&#039;:([\d\.]+)&amp;lt;/code&amp;gt; is looking for the number that immediately follows the text &amp;lt;code&amp;gt;humidity&#039;:&amp;lt;/code&amp;gt; without any blanks in between.&lt;br /&gt;
Be careful if the text you are getting from your device contains special characters like newline. You don&#039;t see such special characters in the fhem webinterface as contents of the internal buf but they might cause your regular expression to fail. &lt;br /&gt;
&lt;br /&gt;
If you have trouble defining a regular expression that matches a certain name, then many complicated characters and then a number, it might be helpful to use a negation in matching like &amp;lt;code&amp;gt;temp[^\d]+([\d\.]).*&amp;lt;/code&amp;gt;. In this examle &amp;lt;code&amp;gt;[^\d]+&amp;lt;/code&amp;gt; means any character that is not a numerical digit, more than once.&lt;br /&gt;
&lt;br /&gt;
=== Regular Expressions with multiple capture Groups ===&lt;br /&gt;
The regular expressions used in the above example for a Poolmanager will take the value that matches one capture group. This is the part of the regular expression inside (). In the above example &amp;quot;([\d\.]+)&amp;quot; refers to numerical digits or points between double quotation marks. Only the string consiting of digits and points will match inside (). This piece is assigned to the reading.&lt;br /&gt;
        &lt;br /&gt;
You can also use regular expressions that have several capture groups which might be helpful when parsing tables. In this case an attribute like &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading02Regex something[ \t]+([\d\.]+)[ \t]+([\d\.]+)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
could match two numbers. When you specify only one reading02Name like &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading02Name Temp&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the name Temp will be used with the extension -1 and -2 thus giving a reading Temp-1 for the first number and Temp-2 for the second. You can also specify individual names for several readings that get parsed from one regular expression with several capture groups by defining attributes &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading02-1Name&lt;br /&gt;
reading02-2Name&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The same notation can be used for formatting attributes like readingXOMap, readingXFormat and so on.&lt;br /&gt;
&lt;br /&gt;
The usual way to define readings is however to have an individual regular expression with just one capture group per reading as shown in the above example.&lt;br /&gt;
&lt;br /&gt;
=== Example: set-magic in HTTPMOD ===&lt;br /&gt;
To achieve the same just like [[set magic]] the following can be added to a HTTPMOD device:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr Owntone.device replacement02Mode expression&lt;br /&gt;
attr Owntone.device replacement02Regex \[([^:]+):([^\]]+)\]&lt;br /&gt;
attr Owntone.device replacement02Value my $device = $name if ($1 eq &amp;quot;\$name&amp;quot;) // $1;;\&lt;br /&gt;
ReadingsVal($device, $2, undef) or AttrVal($device, $2, &amp;quot;???&amp;quot;);;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;With that definition you can just write &amp;lt;code&amp;gt;[$name:state]&amp;lt;/code&amp;gt; to get the state-reading of your own HTTPMOD device there. It also accepts other devicenames and first searches for a reading and if no reading is found it searches for an attrbibute with a matching name. This neat trick is used throughout in the Owntone-HTTPMOD-device (see links section below) to keep it easily adoptable to different server names without editing many places in the listing.&lt;br /&gt;
&lt;br /&gt;
== Parsing JSON ==&lt;br /&gt;
    &lt;br /&gt;
If a webservice delivers data in JSON format, HTTPMOD can directly parse JSON which might be easier in this case than definig regular expressions.&lt;br /&gt;
The next example shows the data that can be requested from a Poolmanager with the following partial configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define test2 HTTPMOD none 0&lt;br /&gt;
attr test2 get01Name Chlor&lt;br /&gt;
attr test2 getURL http://192.168.70.90/cgi-bin/webgui.fcgi&lt;br /&gt;
attr test2 getHeader1 Content-Type: application/json&lt;br /&gt;
attr test2 getHeader2 Accept: */*&lt;br /&gt;
attr test2 getData {&amp;quot;get&amp;quot; :[&amp;quot;34.4008.value&amp;quot;]}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The data in the HTTP response looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;data&amp;quot;: {&lt;br /&gt;
			&amp;quot;34.4008.value&amp;quot;: &amp;quot;0.25&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;status&amp;quot;:       {&lt;br /&gt;
			&amp;quot;code&amp;quot;: 0&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;event&amp;quot;:        {&lt;br /&gt;
			&amp;quot;type&amp;quot;: 1,&lt;br /&gt;
			&amp;quot;data&amp;quot;: &amp;quot;48.30000.0&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the classic way to extract the value 0.25 into a reading with the name Chlor with a regex would have been&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr test2 get01Regex 34.4008.value&amp;quot;:[ \t]+&amp;quot;([\d\.]+)&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
with JSON you can write &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr test2 get01JSON data_34.4008.value &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
which will create a reading with the Name &amp;quot;Chlor&amp;quot; (as shown above) and take the value 0.25 from the JSON string.&lt;br /&gt;
&lt;br /&gt;
or if you don&#039;t care about the naming of your readings, you can simply extract all JSON data with &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr test2 extractAllJSON&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which would apply to all data read from this device and create the following readings out of the HTTP response shown above:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| data_34.4008.value || 0.25&lt;br /&gt;
|-&lt;br /&gt;
| event_data || 48.30000.0&lt;br /&gt;
|-&lt;br /&gt;
| event_type || 1&lt;br /&gt;
|-&lt;br /&gt;
| status_code || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
or you can specify&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr test2 get01ExtractAllJSON&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
which would only apply to all data read as response to the get command defined as get01.        &lt;br /&gt;
&lt;br /&gt;
It might seem very simple at first sight to use extractAllJSON but if you prefer readings with a meaningful name you should instead define these readings with readingXXName and readingXXJSON or getXXName and getXXJSON individually. Of Course it would be possible to create additional user readings outside HTTPMOD but doing calculations, naming and formatting inside HTTPMOD is more efficient.&lt;br /&gt;
&lt;br /&gt;
=== JSON Lists ===&lt;br /&gt;
&lt;br /&gt;
imagine the HTTP Response contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{ &amp;quot;power&amp;quot;:&amp;quot;0&amp;quot;,&lt;br /&gt;
  &amp;quot;modes&amp;quot;:[&amp;quot;Off&amp;quot;,&amp;quot;SimpleColor&amp;quot;,&amp;quot;RainbowChase&amp;quot;],&lt;br /&gt;
  &amp;quot;code1&amp;quot;:3,&lt;br /&gt;
  &amp;quot;code2&amp;quot;:4&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
then a configuration like &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr device reading01JSON modes &lt;br /&gt;
attr device reading01Name Mode &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will create a list of Subreadings just like a regex with multiple matches can create multiple subreadings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Mode-1 || Off&lt;br /&gt;
|-&lt;br /&gt;
| Mode-2  || SimpleColor&lt;br /&gt;
|-&lt;br /&gt;
| Mode-3 || RainbowChase &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
if you don&#039;t want several subreadings but one reading that contains the list of modes, you can specify a recombine expression:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr device reading01Name Modes &lt;br /&gt;
attr device reading01RecombineExpr join &amp;quot;,&amp;quot;, @matchlist &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which will create one reading containing a list:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Modes || Off,SimpleColor,RainbowChase&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
JSON parsing specifications also don&#039;t Need to match exactly. If there is no exact match for a defined reading, the HTTPMOD will try to Interpret the specification as a regex and look for json object paths that match the specification as a regex. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr device reading01Name CodeElem&lt;br /&gt;
attr device reading01JSON code&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which will create a list of readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| CodeElem-1|| 3&lt;br /&gt;
|-&lt;br /&gt;
| CodeElem-2  || 4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
and of course they could also be recombined into one reading with a RecombineExpr Attribute.&lt;br /&gt;
&lt;br /&gt;
== Parsing http / XML using xpath ==&lt;br /&gt;
Another alternative to regex parsing is the use of XPath to extract values from HTTP responses.&lt;br /&gt;
The following example shows how XML data can be parsed with XPath-Strict or HTML Data can be parsed with XPath.&lt;br /&gt;
Both work similar and the example uses XML Data parsed with the XPath-Strict option:&lt;br /&gt;
&lt;br /&gt;
If The XML data in the HTTP response looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;root xmlns:foo=&amp;quot;http://www.foo.org/&amp;quot; xmlns:bar=&amp;quot;http://www.bar.org&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;actors&amp;gt;&lt;br /&gt;
		&amp;lt;actor id=&amp;quot;1&amp;quot;&amp;gt;Peter X&amp;lt;/actor&amp;gt;&lt;br /&gt;
		&amp;lt;actor id=&amp;quot;2&amp;quot;&amp;gt;Charles Y&amp;lt;/actor&amp;gt;&lt;br /&gt;
		&amp;lt;actor id=&amp;quot;3&amp;quot;&amp;gt;John Doe&amp;lt;/actor&amp;gt;&lt;br /&gt;
	&amp;lt;/actor&amp;gt;&lt;br /&gt;
&amp;lt;/root&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
with XPath you can write        &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr htest reading01Name Actor&lt;br /&gt;
attr htest reading01XPath-Strict //actor[2]/text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will create a reading with the Name &amp;quot;Actor&amp;quot; and the value &amp;quot;Charles Y&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Since XPath specifications can define several values / matches, HTTPMOD can also interpret these and store them in multiple readings:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr htest reading01Name Actor&lt;br /&gt;
attr htest reading01XPath-Strict //actor/text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will create the readings &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Actor-1 || Peter X&lt;br /&gt;
|-&lt;br /&gt;
| Actor-2 || Charles Y&lt;br /&gt;
|-&lt;br /&gt;
| Actor-3 || John Doe&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Further replacements of URL, header or post data ==&lt;br /&gt;
sometimes it might be helpful to dynamically change parts of a URL, HTTP header or post data depending on existing readings, internals or &lt;br /&gt;
perl expressions at runtime. This might be needed to pass further variables to a server, a current date or other things. &lt;br /&gt;
&lt;br /&gt;
To support this HTTPMOD offers generic replacements that are applied to a request before it is sent to the server. A replacement can be defined with the attributes &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;replacement[0-9]*Regex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;[gs]et[0-9]*Replacement[0-9]*Value&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
a replacement always replaces a match of a regular expression. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;replacement[0-9]*Mode:&#039;&#039;&#039;&lt;br /&gt;
The way the replacement value is defined can be specified with the replacement mode.&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;reading&amp;lt;/code&amp;gt;, then the corresponding &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt; is interpreted as the name of a &#039;&#039;reading&#039;&#039; of the same device or as &#039;&#039;device:reading&#039;&#039; to refer to another device.&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;internal&amp;lt;/code&amp;gt;, then the corresponding &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt; is interpreted as the name of an &#039;&#039;internal&#039;&#039; of the same device or as &#039;&#039;device:internal&#039;&#039; to refer to another device.&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;, then the corresponding &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt; is interpreted as a static text&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;expression&amp;lt;/code&amp;gt;, then the corresponding &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt; is evaluated as a perl expression to compute the replacement. Inside such a replacement expression it is possible to refer to capture groups of the replacement regex.&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;key&amp;lt;/code&amp;gt;, then the module will use a value from a key / value pair that is stored in an obfuscated form in the file system with the set storeKeyValue command. This might be useful for storing passwords.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
attr mydevice getData {&amp;quot;get&amp;quot; :[&amp;quot;%%value%%.value&amp;quot;]}&lt;br /&gt;
attr mydevice replacement01Mode text&lt;br /&gt;
attr mydevice replacement01Regex %%value%%&lt;br /&gt;
&lt;br /&gt;
attr mydevice get01Name Chlor&lt;br /&gt;
attr mydevice get01Replacement01Value 34.4008&lt;br /&gt;
&lt;br /&gt;
attr mydevice get02Name Something&lt;br /&gt;
attr mydevice get02Replacement01Value 31.4024&lt;br /&gt;
&lt;br /&gt;
attr mydevice get05Name profile&lt;br /&gt;
attr mydevice get05URL http://www.mydevice.local/getprofile?password=%%password%%&lt;br /&gt;
attr mydevice replacement02Mode key&lt;br /&gt;
attr mydevice replacement02Regex %%password%%&lt;br /&gt;
attr mydevice replacement02Value password&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;        &lt;br /&gt;
&lt;br /&gt;
defines that &amp;lt;code&amp;gt;%%value%%&amp;lt;/code&amp;gt; will be replaced by a static text.&lt;br /&gt;
&lt;br /&gt;
All Get commands will be HTTP post requests of a similar form. Only the &amp;lt;code&amp;gt;%%value%%&amp;lt;/code&amp;gt; will be different from get to get.&lt;br /&gt;
The first get will set the reading named Chlor and for the request it will take the generic getData and replace %%value%% with 34.4008.&lt;br /&gt;
&lt;br /&gt;
A second get will look the same except a different name and replacement value.&lt;br /&gt;
&lt;br /&gt;
With the command &amp;lt;code&amp;gt;set mydevice storeKeyValue password geheim&amp;lt;/code&amp;gt; you can store the password geheim in an obfuscated form in the file system. &lt;br /&gt;
To use this password and send it in a request you can use the above replacement with mode key. The value password will then refer to the ofuscated string stored with the key password.&lt;br /&gt;
&lt;br /&gt;
The mode &amp;lt;code&amp;gt;expression&amp;lt;/code&amp;gt; allows you to define your own replacement syntax:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;        &lt;br /&gt;
attr mydevice replacement01Mode expression&lt;br /&gt;
attr mydevice replacement01Regex {{([^}]+)}}&lt;br /&gt;
attr mydevice replacement01Value ReadingsVal(&amp;quot;mydevice&amp;quot;, $1, &amp;quot;&amp;quot;)&lt;br /&gt;
attr mydevice getData {&amp;quot;get&amp;quot; :[&amp;quot;{{temp}}.value&amp;quot;]}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;      &lt;br /&gt;
&lt;br /&gt;
In this example any &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{name}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; in a URL, header or post data will be passed on to the perl function ReadingsVal &lt;br /&gt;
which uses the string between &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; as second parameter. This way one defined replacement can be used for many different&lt;br /&gt;
readings.&lt;br /&gt;
&lt;br /&gt;
HTTPMOD has two built in replacements: One for session Ids and another one for the input value in a set command.&lt;br /&gt;
The placeholder $sid is always replaced with the internal &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt; which contains the session id after it is extracted from a previous HTTP response. &lt;br /&gt;
If you don&#039;t like to use the placeholder $sid then you can define your own replacement for example like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr mydevice replacement01Mode internal&lt;br /&gt;
attr mydevice replacement01Regex %session%&lt;br /&gt;
attr mydevice replacement01Value sid&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;      &lt;br /&gt;
&lt;br /&gt;
Now the internal &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt; will be used as a replacement for the placeholder %session%.&lt;br /&gt;
&lt;br /&gt;
In the same way a value that is passed to a set-command can be put into a request with a user defined replacement. &lt;br /&gt;
In this case the internal &amp;lt;code&amp;gt;$hash-&amp;gt;{value}&amp;lt;/code&amp;gt; will contain the value passed to the set command. &lt;br /&gt;
&amp;lt;code&amp;gt;$hash-&amp;gt;{value}&amp;lt;/code&amp;gt; might even be a string containing several values that could be put into several different positions in a request by using user defined replacements.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Other example : steering a pellet stove from Rika&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The stove API of Rika on https://www.rika-firenet.com/web/ delivers a JSON string with all settings and values, and can be piloted with a data string containing all the &amp;quot;set&amp;quot; values at once.&lt;br /&gt;
&lt;br /&gt;
Delivered JSON on get https://www.rika-firenet.com/api/client/xxxxxxxx/status : (xxxxxxxx must be replaced with the unique stove ID)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Vorzimmer&amp;quot;,&lt;br /&gt;
    &amp;quot;stoveID&amp;quot;: &amp;quot;xxxxxxxxx&amp;quot;,&lt;br /&gt;
    &amp;quot;lastSeenMinutes&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;lastConfirmedRevision&amp;quot;: 1504385700,&lt;br /&gt;
    &amp;quot;controls&amp;quot;: {&lt;br /&gt;
        &amp;quot;revision&amp;quot;: 1504385700,&lt;br /&gt;
        &amp;quot;onOff&amp;quot;: true,&lt;br /&gt;
        &amp;quot;operatingMode&amp;quot;: 2,&lt;br /&gt;
        &amp;quot;heatingPower&amp;quot;: 65,&lt;br /&gt;
        &amp;quot;targetTemperature&amp;quot;: 24,&lt;br /&gt;
        &amp;quot;heatingTimesActive&amp;quot;: false,&lt;br /&gt;
        &amp;quot;heatingTimesActiveForComfort&amp;quot;: true,&lt;br /&gt;
        &amp;quot;setBackTemperature&amp;quot;: 18,&lt;br /&gt;
        &amp;quot;convectionFan1Active&amp;quot;: false,&lt;br /&gt;
        &amp;quot;convectionFan1Level&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;convectionFan1Area&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;convectionFan2Active&amp;quot;: false,&lt;br /&gt;
        &amp;quot;convectionFan2Level&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;convectionFan2Area&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;frostProtectionActive&amp;quot;: false,&lt;br /&gt;
        &amp;quot;frostProtectionTemperature&amp;quot;: 5&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;sensors&amp;quot;: {&lt;br /&gt;
        &amp;quot;statusError&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;statusWarning&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;statusService&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;statusMainState&amp;quot;: 1,&lt;br /&gt;
        &amp;quot;statusSubState&amp;quot;: 3,&lt;br /&gt;
        &amp;quot;statusFrostStarted&amp;quot;: false,&lt;br /&gt;
        &amp;quot;inputFlameTemperature&amp;quot;: 21,&lt;br /&gt;
        &amp;quot;inputRoomTemperature&amp;quot;: 21,&lt;br /&gt;
        &amp;quot;inputExternalRequest&amp;quot;: true,&lt;br /&gt;
        &amp;quot;outputDischargeMotor&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;outputInsertionMotor&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;outputIDFan&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;outputAirFlaps&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;outputIgnition&amp;quot;: false,&lt;br /&gt;
        &amp;quot;parameterStoveTypeNumber&amp;quot;: 13,&lt;br /&gt;
        &amp;quot;parameterVersionMainBoard&amp;quot;: 223,&lt;br /&gt;
        &amp;quot;parameterVersionTFT&amp;quot;: 223,&lt;br /&gt;
        &amp;quot;parameterRuntimePellets&amp;quot;: 11,&lt;br /&gt;
        &amp;quot;parameterRuntimeLogs&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;parameterFeedRateTotal&amp;quot;: 17,&lt;br /&gt;
        &amp;quot;parameterFeedRateService&amp;quot;: 683,&lt;br /&gt;
        &amp;quot;parameterOnOffCycles&amp;quot;: 2&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;stoveType&amp;quot;: &amp;quot;DOMO MultiAir&amp;quot;,&lt;br /&gt;
    &amp;quot;stoveFeatures&amp;quot;: {&lt;br /&gt;
        &amp;quot;multiAir1&amp;quot;: true,&lt;br /&gt;
        &amp;quot;multiAir2&amp;quot;: true,&lt;br /&gt;
        &amp;quot;insertionMotor&amp;quot;: false,&lt;br /&gt;
        &amp;quot;airFlaps&amp;quot;: false,&lt;br /&gt;
        &amp;quot;logRuntime&amp;quot;: false&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Data string to send to https://www.rika-firenet.com/api/client/xxxxxxxx/controls in order to set values:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
heatingTimesActiveForComfort=true&amp;amp;frostProtectionTemperature=3&amp;amp;setBackTemperature=18&amp;amp;targetTemperature=24&amp;amp;convectionFan2Level=0&amp;amp;convectionFan2Active=false&amp;amp;convectionFan1Level=0&amp;amp;onOff=true&amp;amp;convectionFan1Active=false&amp;amp;convectionFan2Area=0&amp;amp;revision=1505550101&amp;amp;heatingTimesActive=false&amp;amp;convectionFan1Area=0&amp;amp;frostProtectionActive=false&amp;amp;operatingMode=2&amp;amp;heatingPower=65&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Code in 99_myUtils.pm:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
use JSON;&lt;br /&gt;
...&lt;br /&gt;
sub&lt;br /&gt;
replaceJSON ($$) {&lt;br /&gt;
   my ($valToReplace, $value) = @_;&lt;br /&gt;
&lt;br /&gt;
   #$value in the parameters is a default value&lt;br /&gt;
   #It has to be replaced through the real value nnn passed in the set command &amp;quot;set &amp;lt;device&amp;gt; valToset nnn&amp;quot;&lt;br /&gt;
   $value = InternalVal(&amp;quot;Ofen&amp;quot;, &amp;quot;value&amp;quot;, $value);&lt;br /&gt;
   Log3 (&amp;quot;Ofen&amp;quot;, 3, &amp;quot;replaceJSON Internalvalue: $value&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   #Force an update to avoid outdated revision number&lt;br /&gt;
   fhem (&amp;quot;get Ofen revision&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   #Get all the controls as json&lt;br /&gt;
   my $json = ReadingsVal(&amp;quot;Ofen&amp;quot;, &amp;quot;controlsJSON&amp;quot;,&amp;quot;&amp;quot;);&lt;br /&gt;
   Log3 (&amp;quot;Ofen&amp;quot;, 3, &amp;quot;replaceJSON configsJSON: $json&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   # When starting FHEM or rereading config, the reading controlsJSON is empty&lt;br /&gt;
   return if ($json eq &amp;quot;&amp;quot;); &lt;br /&gt;
&lt;br /&gt;
   my $decoded = decode_json($json);&lt;br /&gt;
   my $result;&lt;br /&gt;
   for my $key ( keys %$decoded ) {&lt;br /&gt;
      $result .= &amp;quot;$key=&amp;quot;;&lt;br /&gt;
      if ($key eq $valToReplace) {&lt;br /&gt;
         $result .= $value.&amp;quot;&amp;amp;&amp;quot;;&lt;br /&gt;
      } else {&lt;br /&gt;
         $result .= $decoded-&amp;gt;{$key}.&amp;quot;&amp;amp;&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   chop($result); #remove last &amp;amp;&lt;br /&gt;
   Log3(&amp;quot;Ofen&amp;quot;, 3, &amp;quot;replaceJSON Result: $result&amp;quot;);&lt;br /&gt;
   return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Define stove in fhem:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Ofen HTTPMOD https://www.rika-firenet.com/api/client/xxxxxxxx/status 60&lt;br /&gt;
&lt;br /&gt;
attr Ofen enableCookies 1&lt;br /&gt;
attr Ofen reAuthRegex id=&amp;quot;login&amp;quot;|Unauthorized&lt;br /&gt;
attr Ofen sid01Data email=xx@xx&amp;amp;password=xx&lt;br /&gt;
attr Ofen sid01URL https://www.rika-firenet.com/web/login&lt;br /&gt;
&lt;br /&gt;
attr Ofen reading01JSON sensors_inputRoomTemperature&lt;br /&gt;
attr Ofen reading01Name RaumTemp&lt;br /&gt;
attr Ofen reading02JSON controls_setBackTemperature&lt;br /&gt;
attr Ofen reading02Name Absenkung&lt;br /&gt;
attr Ofen reading03JSON controls_frostProtectionTemperature&lt;br /&gt;
attr Ofen reading03Name Frostschutz&lt;br /&gt;
attr Ofen reading10Name controlsJSON&lt;br /&gt;
attr Ofen reading10Regex (?s)controls.*?({.*?})&lt;br /&gt;
&lt;br /&gt;
attr Ofen get09Name revision&lt;br /&gt;
attr Ofen get09URL https://www.rika-firenet.com/api/client/xxxxxxxx/status&lt;br /&gt;
&lt;br /&gt;
attr Ofen setURL https://www.rika-firenet.com/api/client/xxxxxxxx/controls&lt;br /&gt;
attr Ofen setData {{data}}&lt;br /&gt;
attr Ofen replacement01Mode expression&lt;br /&gt;
attr Ofen replacement01Regex {{data}}&lt;br /&gt;
&lt;br /&gt;
attr Ofen set11Name frostProtectionTemperature&lt;br /&gt;
attr Ofen set11Replacement01Value replaceJSON(&amp;quot;frostProtectionTemperature&amp;quot;, 2)&lt;br /&gt;
&lt;br /&gt;
attr Ofen set12Name targetTemperature&lt;br /&gt;
attr Ofen set12Replacement01Value replaceJSON(&amp;quot;targetTemperature&amp;quot;, 24)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A detailed explanation (in german) of the login process can be found here: [https://forum.fhem.de/index.php/topic,76220.msg682514.html#msg682514]&lt;br /&gt;
and the explanation of the other parameters here: [https://forum.fhem.de/index.php/topic,76220.msg685710.html#msg685710]&lt;br /&gt;
&lt;br /&gt;
== replacing reading values when they have not been updated / the device did not respond ==&lt;br /&gt;
If a device does not respond then the values stored in readings will keep the same and only their timestamp shows that they are outdated. &lt;br /&gt;
If you want to modify reading values that have not been updated for a number of seconds, you can use the attributes&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?MaxAge&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?MaxAgeReplacementMode&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?MaxAgeReplacement&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Every time the module tries to read from a device, it will also check if readings have not been updated &lt;br /&gt;
for longer than the &amp;lt;code&amp;gt;MaxAge&amp;lt;/code&amp;gt; attributes allow. If readings are outdated, the &amp;lt;code&amp;gt;MaxAgeReplacementMode&amp;lt;/code&amp;gt; defines how the affected&lt;br /&gt;
reading values should be replaced. &amp;lt;code&amp;gt;MaxAgeReplacementMode&amp;lt;/code&amp;gt; can be &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;reading&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;internal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;expression&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;MaxAge&amp;lt;/code&amp;gt; specifies the number of seconds that a reading should remain untouched before it is replaced. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;MaxAgeReplacement&amp;lt;/code&amp;gt; contains either a static text that is used as replacement value or a Perl expression that is evaluated to &lt;br /&gt;
give the replacement value. This can be used for example to replace a temperature that has not bee updated for more than 5 minutes &lt;br /&gt;
with the string &amp;quot;outdated - was 12&amp;quot;:        &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM readingMaxAge 300&lt;br /&gt;
attr PM readingMaxAgeReplacement &amp;quot;outdated - was &amp;quot; . $val&lt;br /&gt;
attr PM readingMaxAgeReplacementMode expression&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The variable &amp;lt;code&amp;gt;$val&amp;lt;/code&amp;gt; contains the value of the reading before it became outdated.&lt;br /&gt;
&lt;br /&gt;
Or to show that a device was offline:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr MyLight reading01Name color&lt;br /&gt;
attr MyLight reading01JSON result_02_color&lt;br /&gt;
attr MyLight reading01MaxAge 300&lt;br /&gt;
attr MyLight reading01MaxAgeReplacement &amp;quot;offline&amp;quot;&lt;br /&gt;
attr MyLight reading01MaxAgeReplacementMode text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Note on determining how to send requests to a special device ==&lt;br /&gt;
If you don&#039;t know which URLs, headers or POST data your web GUI uses, you might try a local proxy like BurpSuite [http://portswigger.net/burp/ BurpSuite] to track requests and responses. This is a tedious task but probably the best way to achieve a successful result. &lt;br /&gt;
&lt;br /&gt;
Let us consider an example. The Telekom Speedport W724V has a login-site that is famous for being cumbersome. Burp allows to monitor each step in the login procedure. In the case of a speedport the following steps occur:&lt;br /&gt;
&lt;br /&gt;
First burp shows that a get command is issued&lt;br /&gt;
 ################################################################################################## &lt;br /&gt;
 GET / HTTP/1.1&lt;br /&gt;
 Host: speedport.ip&lt;br /&gt;
 Cache-Control: max-age=0&lt;br /&gt;
 Upgrade-Insecure-Requests: 1&lt;br /&gt;
 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36&lt;br /&gt;
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8&lt;br /&gt;
 Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4&lt;br /&gt;
 Cookie: lang=de&lt;br /&gt;
 Connection: close  &lt;br /&gt;
In order to mimic the behavior of a real person calling the website HTTPMOD should copy all necessary steps. Host, Cookie and the GET-command are usually necessary. The same cannot be said of the User-Agent because the website can be called from any mobile or desktop computer. &lt;br /&gt;
&lt;br /&gt;
Then, the speedport will answer with a command that consists of several lines. By going through every line for every step in the login procedure one will finally arrive at the information that is necessary to successfully enter the login of the speedport (in case of the W724V, for example, it is necessary to copy a token called _httoken and to include the referer).&lt;br /&gt;
&lt;br /&gt;
== Advanced configuration to define a &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; command and send data to a device ==&lt;br /&gt;
       &lt;br /&gt;
When a set option is defined by attributes, the module will use the value given to the set command and integrate it into an HTTP-Request that sends the value to the device. The definitions for URL, headers and post data can contain the placeholder $val which will be replaced by the (numerical) value given to the set command.&lt;br /&gt;
&lt;br /&gt;
This can be as simple as:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
# No cyclic requests and no main URL needed in this example&lt;br /&gt;
define MyDevice HTTPMOD none 0&lt;br /&gt;
&lt;br /&gt;
attr MyDevice set01Name Licht&lt;br /&gt;
attr MyDevice set01URL http://192.168.1.22/switch=$val&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A user command &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
set MyDevice Licht 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will be translated into the http GET request&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
http://192.168.1.22/switch=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this example a map would also be helpful, that translates on / off to 0 or 1 and allows the user to select on/of in fhemweb:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr MyDevive set01IMap 0:off, 1:on&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This also provides input validation to make sure that only on and off can be used with the set command.&lt;br /&gt;
&lt;br /&gt;
In more complex Scenarios you might need to login before sending a command and the Login might create a session id that has to be part of further requests either in the URL, in headers or in the post data.&lt;br /&gt;
&lt;br /&gt;
Extension to the above example for a PoolManager 5 where a set needs a session id in the URL and the values have to be passed in JSON strings as post data:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM set01Name HeizungSoll&lt;br /&gt;
attr PM set01URL http://MyPoolManager/cgi-bin/webgui.fcgi?sid=$sid&lt;br /&gt;
attr PM set01Hint 6,10,20,30&lt;br /&gt;
attr PM set01Min 6&lt;br /&gt;
attr PM set01Max 30&lt;br /&gt;
attr PM setHeader1 Content-Type: application/json&lt;br /&gt;
attr PM set01Data {&amp;quot;set&amp;quot; :{&amp;quot;34.3118.value&amp;quot; :&amp;quot;$val&amp;quot; }}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example defines a set option with the name HeizungSoll.&lt;br /&gt;
By issuing &amp;lt;code&amp;gt;set PM HeizungSoll 10&amp;lt;/code&amp;gt; in FHEM, the value 10 will be sent in the defined HTTP&lt;br /&gt;
Post to URL &amp;lt;code&amp;gt;http://MyPoolManager/cgi-bin/webgui.fcgi&amp;lt;/code&amp;gt; in the Post Data as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;set&amp;quot; :{&amp;quot;34.3118.value&amp;quot; :&amp;quot;10&amp;quot; }}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The optional attributes set01Min and set01Max define input validations that will be checked in the set function. &lt;br /&gt;
The optional attribute set01Hint will define a selection list for the FHEMweb GUI.&lt;br /&gt;
&lt;br /&gt;
The HTTP response to such a request will be ignored unless you specify the attribute &amp;lt;code&amp;gt;setParseResponse&amp;lt;/code&amp;gt; &lt;br /&gt;
for all set commands or &amp;lt;code&amp;gt;set01ParseResponse&amp;lt;/code&amp;gt; for the set command with number 01.&lt;br /&gt;
If the HTTP response to a set command is parsed then this is done like the parsing of responses to get commands and you can use the attributes ending e.g. on Format, Encode, Decode, OMap and OExpr to manipulate / format the values read.&lt;br /&gt;
&lt;br /&gt;
If a parameter to a set command is not numeric but should be passed on to the device as text, then you can specify the attribute setTextArg. For example: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM set01TextArg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If a set command should not require a parameter at all, then you can specify the attribute NoArg. For example: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM set03Name On&lt;br /&gt;
attr PM set03NoArg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example defines the set of parameters for Wiesemann&amp;amp;Theiss Web-IO Analog 4.0 #57761. The current limit is set to 16mA (setxxMax)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod WuT_Analog1 HTTPMOD none 30&lt;br /&gt;
attr WuT_Analog1 room W&amp;amp;T&lt;br /&gt;
attr WuT_Analog1 set01Max 16&lt;br /&gt;
attr WuT_Analog1 set01Min 0&lt;br /&gt;
attr WuT_Analog1 set01Name Output1&lt;br /&gt;
attr WuT_Analog1 set01URL http://192.xx.My_IP/outputaccess1?PW=MyPassword&amp;amp;State=$val&amp;amp;&lt;br /&gt;
attr WuT_Analog1 set02Max 16&lt;br /&gt;
attr WuT_Analog1 set02Min 0&lt;br /&gt;
attr WuT_Analog1 set02Name Output2&lt;br /&gt;
attr WuT_Analog1 set02URL http://192.xx.My_IP/outputaccess2?PW=MyPassword&amp;amp;State=$val&amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Advanced configuration to create a valid session id that might be necessary ==&lt;br /&gt;
In simple cases logging in works with basic authentication. In the case HTTPMOD accepts a username and password as part of the URL in the form &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
http://User:Password@192.168.1.18/something&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
However basic auth is seldom used. If you need to fill in a username and password in a HTML form and the session is then managed by a session id, here is how to configure this:&lt;br /&gt;
&lt;br /&gt;
when sending data to an HTTP-Device in a set, HTTPMOD will replace any &amp;lt;code&amp;gt;$sid&amp;lt;/code&amp;gt; in the URL, Headers and Post data with the internal &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt;. To authenticate towards the device and give this internal a value, you can use an optional multi step login procedure defined by the following attributes: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;sid[0-9]*URL&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;sid[0-9]*Data.*&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;sid[0-9]*Header.*&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;sid[0-9]*IgnoreRedirects&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;idRegex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;idJSON&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;idXPath&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;idXPath-Strict&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set|sid)[0-9]*IdRegex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set|sid)[0-9]*IdJSON&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set|sid)[0-9]*IdXPath&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set|sid)[0-9]*IdXPath-Strict&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each step can have a URL, Headers, Post Data pieces and a Regex to extract a resulting Session ID into &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt;.&lt;br /&gt;
HTTPMOD will create a sorted list of steps (the numbers between sid and URL / Data / Header) and the loop through these steps and send the corresponding requests to the device. For each step a $sid in a Header or Post Data will be replaced with the current content of &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Using this feature, HTTPMOD can perform a forms based authentication and send user name, password or other necessary data to the device and save the session id for further requests.&lt;br /&gt;
        &lt;br /&gt;
To determine when this login procedure is necessary, HTTPMOD will first try to send a request without &lt;br /&gt;
doing the login procedure. If the result contains an error that authentication is necessary, then a login is performed. &lt;br /&gt;
To detect such an error in the HTTP response, you can again use a regular expression, JSON or XPath, this time with the attributes &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;reAuthRegex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;reAuthJSON&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;reAuthXPath&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;reAuthXPath-Strict&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set)[0-9]*ReAuthRegex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set)[0-9]*ReAuthJSON&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set)[0-9]*ReAuthXPath&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set)[0-9]*ReAuthXPath-Strict&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
reAuthJSON or reAuthXPath typically only extract one piece of data from a response. &lt;br /&gt;
If the existence of the specified piece of data is sufficent to start a login procedure, then nothing more needs to be defined to detect this situation. &lt;br /&gt;
If however the indicator is a status code that contains different values depending on a successful request and a failed request if a new authentication is needed, &lt;br /&gt;
then you can combine things like reAuthJSON with reAuthRegex. In this case the regex is only matched to the data extracted by JSON (or XPath). &lt;br /&gt;
This way you can easily extract the status code using JSON parsing and then specify the code that means &amp;quot;authentication needed&amp;quot; as a regular expression.&lt;br /&gt;
        &lt;br /&gt;
If for one step not all of the URL, Data or Header Attributes are set, then HTTPMOD tries to use a &lt;br /&gt;
&amp;lt;code&amp;gt;sidURL&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sidData.*&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;sidHeader.*&amp;lt;/code&amp;gt; Attribute (without the step number after sid). This way parts that are the same for all steps don&#039;t need to be defined redundantly.&lt;br /&gt;
&lt;br /&gt;
=== Example for a multi step login procedure: ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM reAuthRegex /html/dummy_login.htm &lt;br /&gt;
attr PM sidURL http://192.168.70.90/cgi-bin/webgui.fcgi?sid=$sid&lt;br /&gt;
attr PM sidHeader1 Content-Type: application/json&lt;br /&gt;
attr PM sid1IDRegex wui.init\(&#039;([^&#039;]+)&#039;&lt;br /&gt;
attr PM sid2Data {&amp;quot;set&amp;quot; :{&amp;quot;9.17401.user&amp;quot; :&amp;quot;fhem&amp;quot; ,&amp;quot;9.17401.pass&amp;quot; :&amp;quot;password&amp;quot; }}&lt;br /&gt;
attr PM sid3Data {&amp;quot;set&amp;quot; :{&amp;quot;35.5062.value&amp;quot; :&amp;quot;128&amp;quot; }}&lt;br /&gt;
attr PM sid4Data {&amp;quot;set&amp;quot; :{&amp;quot;42.8026.code&amp;quot; :&amp;quot;pincode&amp;quot; }}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case HTTPMOD detects that a login is necessary by looking for the pattern /html/dummy_login.htm in the HTTP response. &lt;br /&gt;
If it matches, it starts a login sequence. In the above example all steps request the same URL. In step 1 only the defined Header is sent in an HTTP get request. The response will contain a session id that is extraced with the regex wui.init\(&#039;([^&#039;]+)&#039;.&lt;br /&gt;
&lt;br /&gt;
In the next step this session id is sent in a post request to the same URL where tha post data contains a username and password. The a third and a fourth request follow that set a value and a code. The result will be a valid and authorized session id that can be used in other requests where $sid is part of a URL, header or post data and will be replaced with the session id extracted above.&lt;br /&gt;
&lt;br /&gt;
===Another Practical Example reading the SOC from a Renault Zoe===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define myZoe HTTPMOD https://www.services.renault-ze.com/api/vehicle/VINVINVIN/battery 7000&lt;br /&gt;
attr myZoe userattr reading01JSON reading01Name reading02JSON reading02Name reading03JSON reading03Nam\&lt;br /&gt;
e reading04JSON reading04Name reading05JSON reading05Name requestHeader02 sid1Data sid1Header01 sid1Id\&lt;br /&gt;
JSON sid1IgnoreRedirects:0,1 sid1URL&lt;br /&gt;
attr myZoe reAuthJSON message&lt;br /&gt;
attr myZoe reading01JSON charge_level&lt;br /&gt;
attr myZoe reading01Name SOC&lt;br /&gt;
attr myZoe reading02JSON charging&lt;br /&gt;
attr myZoe reading02Name charging&lt;br /&gt;
attr myZoe reading03JSON plugged&lt;br /&gt;
attr myZoe reading03Name plugged&lt;br /&gt;
attr myZoe reading04JSON remaining_range&lt;br /&gt;
attr myZoe reading04Name remaining_range&lt;br /&gt;
attr myZoe reading05JSON last_update&lt;br /&gt;
attr myZoe reading05Name last_update&lt;br /&gt;
attr myZoe requestHeader02 Authorization: Bearer $sid&lt;br /&gt;
attr myZoe sid1Data {&amp;quot;username&amp;quot;:&amp;quot;ZEUSERNAME&amp;quot;,&amp;quot;password&amp;quot;:&amp;quot;ZEPASSWORD&amp;quot;}&lt;br /&gt;
attr myZoe sid1Header01 Content-Type: application/json&lt;br /&gt;
attr myZoe sid1IdJSON token&lt;br /&gt;
attr myZoe sid1IgnoreRedirects 1&lt;br /&gt;
attr myZoe sid1URL https://www.services.renault-ze.com/api/user/login&lt;br /&gt;
attr myZoe timeout 10&lt;br /&gt;
#attr myZoe verbose 7&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The $sid is read by logging in with username and password from JSON {&amp;quot;token&amp;quot;: ..} after detecting an error {&amp;quot;message&amp;quot;: ..}  from a failed request. This example offers no solution for the reading of the VIN from the login request, which in turn is part of the reading URL. It should be possible to read more than just the $sid from the login sequence.&lt;br /&gt;
&lt;br /&gt;
In the special case where a session id is set as a HTTP-Cookie (with the header Set-cookie: in the HTTP response) HTTPMOD offers an even simpler way. With the attribute enableCookies a very basic cookie handling mechanism is activated that stores all cookies that the server sends to the HTTPMOD device and puts them back as cookie headers in the following requests.&lt;br /&gt;
&lt;br /&gt;
For such cases no sidIdRegex and no $sid in a user defined header is necessary.&lt;br /&gt;
&lt;br /&gt;
== Advanced configuration to define a &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; and request additional data with its own request from a device ==&lt;br /&gt;
&lt;br /&gt;
The normal automatic HTTP request that is done repeatedly after the defined interval has elapsed works well in cases where all required readings can be requested in one common HTTP request. If however a device needs individual requests with different URLs or different POST data for each value, then another method is necessary. &lt;br /&gt;
For such cases a &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; option can be defined and the user can either issue FHEM &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; commands each time he needs the reading or the user can set an attribute to request the reading automatically together with the normal iteration.&lt;br /&gt;
For each &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; option attributes define an individual URL, optional headers, and post data as well as individual regular expressions and formatting options. &lt;br /&gt;
&lt;br /&gt;
When a get option is defined by attributes, the module allows querying additional values from the device that require individual HTTP-Requests or special parameters to be sent&lt;br /&gt;
&lt;br /&gt;
Extension to the above example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM get01Name MyGetValue &lt;br /&gt;
attr PM get01URL http://MyPoolManager/cgi-bin/directory/webgui.fcgi?special=1?sid=$sid &lt;br /&gt;
attr PM getHeader1 Content-Type: application/json &lt;br /&gt;
attr PM get01Data {&amp;quot;get&amp;quot; :{&amp;quot;30.1234.value&amp;quot;}} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example defines a get option with the name MyGetValue.&lt;br /&gt;
By issuing &amp;lt;code&amp;gt;get PM MyGetValue&amp;lt;/code&amp;gt; in FHEM, the defined HTTP request is sent to the device.&lt;br /&gt;
The HTTP response is then parsed using the same readingXXName and readingXXRegex attributes as above so&lt;br /&gt;
additional pairs will probably be needed there for additional values.&lt;br /&gt;
&lt;br /&gt;
if you prefer to define the parsing and formatting of readings individually per get command, you can use &lt;br /&gt;
attributes like get01Regex, get01XPath, get01Format, get01OMap and so on just like for reading01...&lt;br /&gt;
&lt;br /&gt;
You can also include parameters / values that are passed to the get command in the request just like for set commands.&lt;br /&gt;
The placeholder $val will be replaced with the value given to the get command or you can specify your own replacement as described above.&lt;br /&gt;
&lt;br /&gt;
If the new get parameter should also be queried regularly, you can define the following optional attributes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM get01Poll 1&lt;br /&gt;
attr PM get01PollDelay 300&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first attribute includes this reading in the automatic update cycle and the second defines an alternative lower update frequency. When the interval defined initially in the define is over and the normal readings are read from the device, the update function will check for additional get parameters that should be included in the update cycle.&lt;br /&gt;
&lt;br /&gt;
If a PollDelay is specified for a get parameter, the update function also checks if the time passed since it has last read this value is more than the given PollDelay. If not, this reading is skipped and it will be rechecked in the next cycle when interval is over again. So the effective PollDelay will always be a multiple of the interval specified in the initial define.&lt;br /&gt;
&lt;br /&gt;
Please note that each defined get command that is included in the regular update cycle will create its own HTTP request. So if you want to extract several values from the same request, it is much more efficient to do this by defining readingXXName and readingXXRegex, XPath or JSON attributes and to specify an interval and a URL in the define of the HTTPMOD device. &lt;br /&gt;
&lt;br /&gt;
Example for a Siemens webserver provided by Lanhydrock:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define ozw672 HTTPMOD https://192.168.178.8/api/auth/login.json?user=test&amp;amp;pwd=test 300&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get1Name tempAussen&lt;br /&gt;
attr ozw672 get1URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1960&lt;br /&gt;
attr ozw672 get1Poll 1&lt;br /&gt;
attr ozw672 get1PollDelay 1800&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get2Name tempAussenGemischt&lt;br /&gt;
attr ozw672 get2URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1964&lt;br /&gt;
attr ozw672 get2Poll 1&lt;br /&gt;
attr ozw672 get2PollDelay 1800&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get3Name tempTWW&lt;br /&gt;
attr ozw672 get3URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1996&lt;br /&gt;
attr ozw672 get3Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get4Name tempKesselSoll&lt;br /&gt;
attr ozw672 get4URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1910&lt;br /&gt;
attr ozw672 get4Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get5Name tempKesselRuecklauf&lt;br /&gt;
attr ozw672 get5URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1915&lt;br /&gt;
attr ozw672 get5Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get6Name tempKesselRuecklaufSoll&lt;br /&gt;
attr ozw672 get6URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1916&lt;br /&gt;
attr ozw672 get6Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get7Name anzahlStartsBrenner&lt;br /&gt;
attr ozw672 get7URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1927&lt;br /&gt;
attr ozw672 get7PollDelay 1800&lt;br /&gt;
attr ozw672 get7Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get8Name statusKessel&lt;br /&gt;
attr ozw672 get8URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1898&lt;br /&gt;
attr ozw672 get8Poll 1&lt;br /&gt;
attr ozw672 get8Regex Value&amp;quot;: &amp;quot;([a-zA-Zü ]*)&amp;quot;&lt;br /&gt;
attr ozw672 get8OMap Aus:0, Nachlauf aktiv:5, Freigegeben für TWW:10, Freigegeben für HK:20, In Teillastbetrieb für TWW:40, In Teillastbetrieb für HK:50, In Betrieb für Trinkwasser:90, In Betrieb für Heizkreis:100&lt;br /&gt;
&lt;br /&gt;
attr ozw672 getRegex Value&amp;quot;: &amp;quot;[ ]*([-.0-9]*)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attr ozw672 reAuthRegex .*session not valid.*&lt;br /&gt;
attr ozw672 sid1IDRegex .*&amp;quot;(.*-.*-.*-[0-9a-z]*).*&lt;br /&gt;
attr ozw672 sid1URL https://192.168.178.8/api/auth/login.json?user=test&amp;amp;pwd=test&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== All attributes ==&lt;br /&gt;
;reading[0-9]+Name&lt;br /&gt;
:the name of a reading to extract with the corresponding readingRegex, readingJSON, readingXPath or readingXPath-Strict&lt;br /&gt;
:Please note that the old syntax &amp;lt;b&amp;gt;readingsName.*&amp;lt;/b&amp;gt; does not work with all features of HTTPMOD and should be avoided. It might go away in a future version of HTTPMOD.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]+Name&lt;br /&gt;
:Name of a get or set command&lt;br /&gt;
:If the HTTP response that is received after the command is parsed with an individual parse option then this name is also used as a reading name. Please note that no individual parsing needs to be defined for a get or set. If no regex, XPath or JSON is specified for the command, then HTTPMOD will try to parse the response using all the defined readingRegex, reading XPath or readingJSON attributes.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+Regex&lt;br /&gt;
:If this attribute is specified, the Regex defined here is used to extract the value from the HTTP Response and assign it to a Reading with the name defined in the (get|set|reading)[0-9]+Name attribute.&lt;br /&gt;
:If this attribute is not specified for an individual Reading or get or set but without the numbers in the middle, e.g. as getRegex or readingRegex, then it applies to all the other readings / get / set commands where no specific Regex is defined.&amp;lt;br&amp;gt;&lt;br /&gt;
:The value to extract should be in a capture group / sub expression e.g. ([\d\.]+) in the above example. Multiple capture groups will create multiple readings (see explanation above)&lt;br /&gt;
:Using this attribute for a set command (setXXRegex) only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
:Please note that the old syntax &amp;lt;code&amp;gt;readingsRegex.*&amp;lt;/code&amp;gt; does not work with all features of HTTPMOD and should be avoided. It might go away in a future version of HTTPMOD.&lt;br /&gt;
:If for get or set commands neither a generic Regex attribute without numbers nor a specific (get|set)[0-9]+Regex attribute is specified and also no XPath or JSON parsing specification is given for the get or set command, then HTTPMOD tries to use the parsing definitions for general readings defined in reading[0-9]+Name, reading[0-9]+Regex or XPath or JSON attributes and assigns the Readings that match here.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+RegOpt&lt;br /&gt;
:Lets the user specify regular expression modifiers. For example if the same regular expression should be matched as often as possible in the HTTP response, then you can specify RegOpt g which will case the matching to be done as /regex/g&lt;br /&gt;
:The results will be trated the same way as multiple capture groups so the reading name will be extended with -number. &lt;br /&gt;
:For other possible regular expression modifiers see http://perldoc.perl.org/perlre.html#Modifiers&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+XPath&lt;br /&gt;
:defines an xpath to one or more values when parsing HTML data (see examples above)&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;get|set|reading[0-9]+XPath-Strict&lt;br /&gt;
:defines an xpath to one or more values when parsing XML data (see examples above)&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+AutoNumLen&lt;br /&gt;
:In cases where a regular expression or an XPath results in multiple results and these results are stored in a common reading name with extension -number, then you can modify the format of this number to have a fixed length with leading zeros. AutoNumLen 3 for example will lead to reading names ending with -001 -002 and so on.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+AlwaysNum&lt;br /&gt;
:if set to 1 this attributes forces reading names to end with a -1, -01 (depending on the above described AutoNumLen) even if just one value is parsed.&lt;br /&gt;
&lt;br /&gt;
;get|set|reading[0-9]+JSON&lt;br /&gt;
:defines a path to the JSON object wanted by concatenating the object names. See the above example.&lt;br /&gt;
:If you don&#039;t know the paths, then start by using extractAllJSON and the use the names of the readings as values for the JSON attribute.&amp;lt;br&amp;gt;&lt;br /&gt;
:Please don&#039;t forget to also specify a name for a reading, get or set. &lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]*RecombineExpr&lt;br /&gt;
:defines an expression that is used in an eval to compute one reading value out of the list of matches.&lt;br /&gt;
:This is supposed to be used for regexes or xpath specifications that produce multiple results if only one result that combines them is wanted. The list of matches will be in the variable @matchlist.&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;get[0-9]*CheckAllReadings&lt;br /&gt;
:this attribute modifies the behavior of HTTPMOD when the HTTP Response of a get command is parsed. &amp;lt;br&amp;gt;&lt;br /&gt;
:If this attribute is set to 1, then additionally to the matching of the corresponding get specific regex (get[0-9]*Regex), XPath or JSON attribute also all the reading names and parse definitions defined in Reading[0-9]+Name and Reading[0-9]+Regex, XPath or JSON attributes are checked and if they match, the coresponding Readings are assigned as well.&lt;br /&gt;
:Please note that this does not mean that get01CheckAllReadings will cause a get02Regex to be used. Only the corresponding get01Regex will be used but additionally all the readingXYRegex attributes.&lt;br /&gt;
:This is automatically done if a get or set command is defined without its own parse attributes.&lt;br /&gt;
&lt;br /&gt;
;(get|reading)[0-9]*OExpr&lt;br /&gt;
:defines an optional expression that is used in an eval to compute / format a readings value after parsing an HTTP response&lt;br /&gt;
:The raw value from the parsing will be in the variable $val.&lt;br /&gt;
:If specified as readingOExpr then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*Expr.&lt;br /&gt;
:Please note that the old syntax &amp;lt;b&amp;gt;readingsExpr.*&amp;lt;/b&amp;gt; does not work with all features of HTTPMOD and should be avoided. It might go away in a future version of HTTPMOD.&lt;br /&gt;
&lt;br /&gt;
;(get|reading)[0-9]*Expr&lt;br /&gt;
:This is the old syntax for (get|reading)[0-9]*OExpr. It should be replaced by (get|reading)[0-9]*OExpr. The set command upgradeAttributes which becomes visible when the attribute enableControlSet is set to 1, can do this renaming automatically.&lt;br /&gt;
&lt;br /&gt;
;(get|reading)[0-9]*OMap&lt;br /&gt;
:Map that defines a mapping from raw value parsed to visible values like &amp;quot;0:mittig, 1:oberhalb, 2:unterhalb&amp;quot;.&lt;br /&gt;
:If specified as readingOMap then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*Map.&amp;lt;br&amp;gt;&lt;br /&gt;
:The individual options in a map are separated by a komma and an optional space. Spaces are allowed to appear in a visible value however kommas are not possible.&lt;br /&gt;
&lt;br /&gt;
;(get|reading)[0-9]*Map&lt;br /&gt;
:This is the old syntax for (get|reading)[0-9]*OMap. It should be replaced by (get|reading)[0-9]*OMap. The set command upgradeAttributes which becomes visible when the attribute enableControlSet is set to 1, can do this renaming automatically.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]*Format&lt;br /&gt;
:Defines a format string that will be used in sprintf to format a reading value.&lt;br /&gt;
:If specified without the numbers in the middle e.g. as readingFormat then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*Format.&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]*Decode&lt;br /&gt;
:defines an encoding to be used in a call to the perl function decode to convert the raw data string read from the device to a reading. &lt;br /&gt;
:This can be used if the device delivers strings in an encoding like cp850 instead of utf8.&lt;br /&gt;
:If your reading values contain Umlauts and they are shown as strange looking icons then you probably need to use this feature.&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]*Encode&lt;br /&gt;
:defines an encoding to be used in a call to the perl function encode to convert the raw data string read from the device to a reading. &lt;br /&gt;
:This can be used if the device delivers strings in an encoding like cp850 and after decoding it you want to reencode it to e.g. utf8.&lt;br /&gt;
:If your reading values contain Umlauts and they are shown as strange looking icons then you probably need to use this feature.&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*URL&lt;br /&gt;
:URL to be requested for the set or get command. If this option is missing, the URL specified during define will be used.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*Data&lt;br /&gt;
:Data to be sent to the device as POST data when the get or set command is executed. if this attribute is not specified, an HTTP GET method will be used instead of an HTTP POST&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*NoData&lt;br /&gt;
:can be used to override a more generic attribute that specifies POST data for all get or set commands. With NoData no data is sent and therefor the request will be an HTTP GET.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*Header.*&lt;br /&gt;
:HTTP Headers to be sent to the device when the set is executed&lt;br /&gt;
&lt;br /&gt;
;requestHeader.*&lt;br /&gt;
:Define an optional additional HTTP Header to set in the HTTP request of the main loop&lt;br /&gt;
&lt;br /&gt;
;requestData&lt;br /&gt;
:optional POST Data to be sent in the request of the main loop. If not defined, it will be an HTTP GET request as defined in HttpUtils which is used by this module&lt;br /&gt;
&lt;br /&gt;
;get[0-9]+Poll&lt;br /&gt;
:if set to 1 the get is executed automatically during the normal update cycle (after the interval provided in the define command has elapsed)&lt;br /&gt;
&lt;br /&gt;
;get[0-9]+PollDelay&lt;br /&gt;
:if the value should not be read in each iteration (after the interval given to the define command), then a minimum delay can be specified with this attribute. This has only an effect if the above Poll attribute has also been set. Every time the update function is called, it checks if since this get has been read the last time, the defined delay has elapsed. If not, then it is skipped this time.&lt;br /&gt;
:PollDelay can be specified as seconds or as x[0-9]+ which means a multiple of the interval in the define command.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*TextArg&lt;br /&gt;
:For a get command this defines that the command accepts a text value after the option name. By default a get command doesn&#039;t accept optional values after the command name. &lt;br /&gt;
:If TextArg is specified and a value is passed after the get name then this value can then be used in a request URL, header or data as replacement for $val or in a user defined replacement that uses the internal &amp;quot;value&amp;quot; ($hash-&amp;gt;{value}).&lt;br /&gt;
:If used for a set command then it defines that the value to be set doesn&#039;t require any validation / conversion. &lt;br /&gt;
:The raw value is passed on as text to the device. By default a set command expects a numerical value or a text value that is converted to a numeric value using a map.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Min&lt;br /&gt;
:Minimum value for input validation. &lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Max&lt;br /&gt;
:Maximum value for input validation. &lt;br /&gt;
&lt;br /&gt;
;set[0-9]+IExpr&lt;br /&gt;
:Perl Expression to compute the raw value to be sent to the device from the input value passed to the set.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Expr&lt;br /&gt;
:This is the old syntax for (get|reading)[0-9]*IExpr. It should be replaced by (get|reading)[0-9]*IExpr. The set command upgradeAttributes which becomes visible when the attribute enableControlSet is set to 1, can do this renaming automatically.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+IMap&lt;br /&gt;
:Map that defines a mapping from raw to input values like &amp;quot;0:mittig, 1:oberhalb, 2:unterhalb&amp;quot;. This attribute atomatically creates a hint for FHEMWEB so the user can choose one of the input values.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Map&lt;br /&gt;
:This is the old syntax for (get|reading)[0-9]*IMap. It should be replaced by (get|reading)[0-9]*IMap. The set command upgradeAttributes which becomes visible when the attribute enableControlSet is set to 1, can do this renaming automatically.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Hint&lt;br /&gt;
:Explicit hint for fhemWEB that will be returned when set ? is seen. Can be used to get a slider or a list of values to choose from.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]*NoArg&lt;br /&gt;
:Defines that this set option doesn&#039;t require arguments. It allows sets like &amp;quot;on&amp;quot; or &amp;quot;off&amp;quot; without further values.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]*ParseResponse&lt;br /&gt;
:defines that the HTTP response to the set will be parsed as if it was the response to a get command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*URLExpr&lt;br /&gt;
:Defines a Perl expression to specify the HTTP Headers for this request. This overwrites any other header specification and should be used carefully only if needed. The original Header is availabe as $old. Typically this feature is not needed and it might go away in future versions of HTTPMOD. Please use the &amp;quot;replacement&amp;quot; attributes if you want to pass additional variable data to a web service. &lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*DatExpr&lt;br /&gt;
:Defines a Perl expression to specify the HTTP Post data for this request. This overwrites any other post data specification and should be used carefully only if needed. The original Data is availabe as $old. Typically this feature is not needed and it might go away in future versions of HTTPMOD. Please use the &amp;quot;replacement&amp;quot; attributes if you want to pass additional variable data to a web service. &lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*HdrExpr&lt;br /&gt;
:Defines a Perl expression to specify the URL for this request. This overwrites any other URL specification and should be used carefully only if needed. The original URL is availabe as $old. Typically this feature is not needed and it might go away in future versions of HTTPMOD. Please use the &amp;quot;replacement&amp;quot; attributes if you want to pass additional variable data to a web service.           &lt;br /&gt;
&lt;br /&gt;
;ReAuthRegex&lt;br /&gt;
:regular Expression to match an error page indicating that a session has expired and a new authentication for read access needs to be done. &lt;br /&gt;
:This attribute only makes sense if you need a forms based authentication for reading data and if you specify a multi step login procedure based on the sid.. attributes.&lt;br /&gt;
:This attribute is used for all requests. For set and get operations you can however specify individual reAuthRegexes with the (get|set)[0-9]*ReAuthRegex attributes.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*ReAuthRegex&lt;br /&gt;
:Regex that will detect when a session has expired during a set operation and a new login needs to be performed.&lt;br /&gt;
:It works like the global reAuthRegex but is used for set operations.&lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*URL&lt;br /&gt;
:different URLs or one common URL to be used for each step of an optional login procedure. &lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*IDRegex&lt;br /&gt;
:different Regexes per login procedure step or one common Regex for all steps to extract the session ID from the HTTP response&lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*Data.*&lt;br /&gt;
:data part for each step to be sent as POST data to the corresponding URL&lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*Header.*&lt;br /&gt;
:HTTP Headers to be sent to the URL for the corresponding step&lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*IgnoreRedirects&lt;br /&gt;
:Tells the HttpUtils to not follow HTTP Redirects for this Request. Might be needed for some devices that set a session cookie within a 303 Redirect.&lt;br /&gt;
&lt;br /&gt;
;clearSIdBeforeAuth&lt;br /&gt;
:will set the session id to &amp;quot;&amp;quot; before doing the authentication steps&lt;br /&gt;
&lt;br /&gt;
;authRetries&lt;br /&gt;
:number of retries for authentication procedure - defaults to 1&lt;br /&gt;
&lt;br /&gt;
;replacement[0-9]*Regex&lt;br /&gt;
:Defines a replacement to be applied to an HTTP request header, data or URL before it is sent. This allows any part of the request to be modified based on a reading, an internal or an expression.&lt;br /&gt;
:The regex defines which part of a header, data or URL should be replaced. The replacement is defined with the following attributes:&lt;br /&gt;
&lt;br /&gt;
;replacement[0-9]*Mode&lt;br /&gt;
:Defines how the replacement should be done and what replacementValue means. Valid options are text, reading, internal and expression.&lt;br /&gt;
&lt;br /&gt;
;replacement[0-9]*Value&lt;br /&gt;
:Defines the replacement. If the corresponding replacementMode is &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;, then value is a static text that is used as the replacement.&lt;br /&gt;
:If replacementMode is &amp;lt;code&amp;gt;reading&amp;lt;/code&amp;gt; then Value can be the name of a reading of this device or it can be a reading of a different device referred to by devicename:reading.&lt;br /&gt;
:If replacementMode is &amp;lt;code&amp;gt;internal&amp;lt;/code&amp;gt; the Value can be the name of an internal of this device or it can be an internal of a different device referred to by devicename:internal.&lt;br /&gt;
:If replacementMode is &amp;lt;code&amp;gt;expression&amp;lt;/code&amp;gt; the the Value is treated as a Perl expression that computes the replacement value. The expression can use $1, $2 and so on to refer to capture groups of the corresponding regex that is matched against the original URL, header or post data.&lt;br /&gt;
:If replacementMode is &amp;lt;code&amp;gt;key&amp;lt;/code&amp;gt; then the module will use a value from a key / value pair that is stored in an obfuscated form in the file system with the set storeKeyValue command. This might be useful for storing passwords.&lt;br /&gt;
&lt;br /&gt;
;[gs]et[0-9]*Replacement[0-9]*Value&lt;br /&gt;
:This attribute can be used to override the replacement value for a specific get or set.&lt;br /&gt;
&lt;br /&gt;
;get|reading[0-9]*MaxAge&lt;br /&gt;
:Defines how long a reading is valid before it is automatically overwritten with a replacement when the read function is called the next time.&lt;br /&gt;
&lt;br /&gt;
;get|reading[0-9]*MaxAgeReplacement&lt;br /&gt;
:specifies the replacement for MaxAge - either as a static text or as a perl expression.&lt;br /&gt;
&lt;br /&gt;
;get|reading[0-9]*MaxAgeReplacementMode&lt;br /&gt;
:specifies how the replacement is interpreted: can be text, expression and delete.&lt;br /&gt;
&lt;br /&gt;
;get|reading[0-9]*DeleteIfUnmatched&lt;br /&gt;
:If set to 1 this attribute causes certain readings to be deleted when the parsing of the website does not match the specified reading. Internally HTTPMOD remembers which kind of operation created a reading (update, Get01, Get02 and so on). Specified readings will only be deleted if the same operation does not parse this reading again. This is especially useful for parsing that creates several matches / readings and the number of matches can vary from request to request. For example if reading01Regex creates 4 readings in one update cycle and in the next cycle it only matches two times then the readings containing the remaining values from the last round will be deleted.&lt;br /&gt;
:Please note that this mechanism will not work in all cases after a restart. Especially when a get definition does not contain its own parsing definition but ExtractAllJSON or relies on HTTPMOD to use all defined reading.* attributes to parse the responsee to a get command, old readings might not be deleted after a restart of fhem.&lt;br /&gt;
;get|reading[0-9]*DeleteOnError&lt;br /&gt;
:If set to 1 this attribute causes certain readings to be deleted when the website can not be reached and the HTTP request returns an error. Internally HTTPMOD remembers which kind of operation created a reading (update, Get01, Get02 and so on). Specified readings will only be deleted if the same operation returns an error.&lt;br /&gt;
The same restrictions as for DeleteIfUnmatched apply regarding a fhem restart.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;httpVersion&lt;br /&gt;
:defines the HTTP-Version to be sent to the server. This defaults to 1.0.&lt;br /&gt;
&lt;br /&gt;
;sslVersion&lt;br /&gt;
:defines the SSL Version for the negotiation with the server. The attribute is evaluated by HttpUtils. If it is not specified, HttpUtils assumes SSLv23:!SSLv3:!SSLv2&lt;br /&gt;
&lt;br /&gt;
;sslArgs&lt;br /&gt;
:defines a list that is converted to a key / value hash and gets passed to HttpUtils. To avoid certificate validation for broken servers you can for example specify &lt;br /&gt;
:&amp;lt;code&amp;gt;attr myDevice sslArgs SSL_verify_mode,SSL_VERIFY_NONE&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;alignTime&lt;br /&gt;
:Aligns each periodic read request for the defined interval to this base time. This is typcally something like 00:00 (see the FHEM at command)&lt;br /&gt;
&lt;br /&gt;
;noShutdown&lt;br /&gt;
:pass the noshutdown flag to HTTPUtils for webservers that need it (some embedded webservers only deliver empty pages otherwise)&lt;br /&gt;
&lt;br /&gt;
;disable&lt;br /&gt;
:stop doing automatic HTTP requests while this attribute is set to 1&lt;br /&gt;
&lt;br /&gt;
;enableControlSet&lt;br /&gt;
:enables the built in set commands interval, stop, start, reread, upgradeAttributes, storeKeyValue.&lt;br /&gt;
&lt;br /&gt;
;enableCookies&lt;br /&gt;
:enables the built in cookie handling if set to 1. With cookie handling each HTTPMOD device will remember cookies that the server sets and send them back to the server in the following requests. &lt;br /&gt;
:This simplifies session magamenet in cases where the server uses a session ID in a cookie. In such cases enabling cookies should be sufficient and no sidRegex and no manual definition of a cookie header should be necessary.&lt;br /&gt;
&lt;br /&gt;
;showMatched&lt;br /&gt;
:if set to 1 then HTTPMOD will create a reading with the name MATCHED_READINGS that contains the names of all readings that could be matched in the last request as well as UNMATCHED_READINGS and LAST_REQUEST.&lt;br /&gt;
&lt;br /&gt;
;showError&lt;br /&gt;
:if set to 1 then HTTPMOD will create a reading and event with the Name LAST_ERROR that contains the error message of the last error returned from HttpUtils. &lt;br /&gt;
&lt;br /&gt;
;removeBuf&lt;br /&gt;
:if set to 1 then HTTPMOD removes the internal named buf when a HTTP-response has been received. &lt;br /&gt;
:$hash-&amp;gt;{buf} is used internally be Fhem httpUtils and in some use cases it is desireable to remove this internal after reception &lt;br /&gt;
:because it contains a very long response which looks ugly in Fhemweb.&lt;br /&gt;
&lt;br /&gt;
;timeout&lt;br /&gt;
:time in seconds to wait for an answer. Default value is 2&lt;br /&gt;
&lt;br /&gt;
;queueDelay&lt;br /&gt;
:HTTP Requests will be sent from a queue in order to avoid blocking when several Requests have to be sent in sequence. This attribute defines the delay between calls to the function that handles the send queue. It defaults to one second.&lt;br /&gt;
&lt;br /&gt;
;queueMax&lt;br /&gt;
:Defines the maximum size of the send queue. If it is reached then further HTTP Requests will be dropped and not be added to the queue&lt;br /&gt;
&lt;br /&gt;
;minSendDelay&lt;br /&gt;
:Defines the minimum time between two HTTP Requests.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Example: This example uses many features of HTTPMOD, universally useful is the access to other readings just like what [[Set magic|set-magic]] (&amp;lt;code&amp;gt;[device:reading]&amp;lt;/code&amp;gt;) does [https://forum.fhem.de/index.php?topic=135666.0 🎸 Owntone (Musik-Player) mit FHEM steuern]&lt;br /&gt;
* Example: [https://www.goingelectric.de/forum/viewtopic.php?f=57&amp;amp;t=21462|Abfrage Akkustand einer Zoe und mehr von Renault ZE Services]&lt;br /&gt;
* Example: [[Wetter_und_Wettervorhersagen#Wetter_von_Weather_Underground|Extract weather information from WeatherUnderground]]&lt;br /&gt;
* Example: [[Pollenflug|Pollen count]]&lt;br /&gt;
* Example: [[HTTPMOD Beispielkonfiguration zur Anbindung einer Daikin Klimaanlage mit WLAN-Modul|Connect Daikin aircondition to FHEM]]&lt;br /&gt;
* Example: [[Go-eCharger|Extract information from go-eCharger]]&lt;br /&gt;
* Example: [[Sonnenspeicher|Integration of energy supplies from sonnen (https://sonnenbatterie.de/en/start)]]&lt;br /&gt;
* Example: [https://forum.fhem.de/index.php/topic,95989.msg915870.html#msg915870 Miele 3rd party API für Miele Smarthome Geräte]&lt;br /&gt;
* Example: [https://forum.fhem.de/index.php/topic,84215.msg918662.html#msg918662 Honeywell Evohome Totalconnect]&lt;br /&gt;
* Example: [https://forum.fhem.de/index.php/topic,78613.msg889015.html#msg889015 Tigo Energy Integration]&lt;br /&gt;
* Example: [https://forum.fhem.de/index.php/topic,78613.msg708518.html#msg708518 Ecowater]&lt;br /&gt;
* {{Link2Forum|Topic=17804|LinkText=Thread}} in FHEM Forum that discusses the first version of this module &lt;br /&gt;
* {{Link2Forum|Topic=29471|LinkText=Thread}} in FHEM Forum that discusses the second major version of this module &lt;br /&gt;
* {{Link2Forum|Topic=45176|LinkText=Thread}} in FHEM Forum that discusses the third major version of this module &lt;br /&gt;
* [http://perldoc.perl.org/perlretut.html Introduction to regular expressions]&lt;br /&gt;
* [http://portswigger.net/burp/ BurpSuite]: Tool (local proxy) to help analyze http traffic&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:IP Components]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=HTTPMOD&amp;diff=40086</id>
		<title>HTTPMOD</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=HTTPMOD&amp;diff=40086"/>
		<updated>2025-04-05T08:57:16Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Extract information from devices with an HTTP interface (or, more generic, from any URL) or send information to such devices &lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=HTTPMOD&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=98_HTTPMOD.pm&lt;br /&gt;
|ModOwner=StefanStrobel ({{Link2FU|3960|Forum}} / [[Benutzer:StefanStrobel|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
[[HTTPMOD]] provides a generic way to retrieve information from devices with an HTTP Interface and store them in Readings or send information to such devices. It queries a given URL with Headers and data defined by attributes. &lt;br /&gt;
&lt;br /&gt;
From the HTTP response it extracts readings named in attributes using Regexes, JSON or XPath parsing also defined by attributes.&lt;br /&gt;
&lt;br /&gt;
In an advanced [[Konfiguration|configuration]] the module can also send information to devices. To do this, a generic &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; option can be configured using attributes. &lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
With the help of HTTPMOD you can automatically access websites, retrieve information and send data. In the simplest case, an http call is started regularly and the content of a page, for example, is read and processed further. The URL and the time interval in which the call is to be made must then be specified when the call is made. In order to read information from the called web pages, readings are required. In this readings one specifies Regex, which reads the necessary information. Here is an example.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define PM HTTPMOD  http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/-1/free.html 20000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a device that accesses the http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/-1/free.html website with the TV program every 20000 seconds. If neither readings nor other attributes are defined, then only the call of the website takes place and FHEM receives no further data from the web access.&lt;br /&gt;
&lt;br /&gt;
But now a television program is returned when you call the website, which you might want to read out. In order to save the contents in FHEM, which are related to the ARD, you need a reading. This reading must contain a name (so that FHEM knows under which name the information is stored) and a regex (so that you know where the information is stored on the website). Since usually not one, but many readings are defined, these readings are numbered consecutively. This is done by numbers that are given immediately after the word reading. In our example this would be for example&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM reading1Name ARD &lt;br /&gt;
attr PM reading1Regex &amp;lt;span class=&amp;quot;stationName&amp;quot;&amp;gt;ARD&amp;lt;\/span&amp;gt;[\w\W]*?&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;\s*&amp;lt;a[\w\W]*?&amp;gt;\s*(.*?)\s*&amp;lt;\/a&amp;gt; &lt;br /&gt;
attr PM reading2Name ARD_time &lt;br /&gt;
attr PM reading2Regex &amp;lt;span class=&amp;quot;stationName&amp;quot;&amp;gt;ARD&amp;lt;\/span&amp;gt;[\w\W]*?&amp;lt;td class=&amp;quot;time&amp;quot;&amp;gt;\s*(.*?)\s*&amp;lt; &lt;br /&gt;
attr PM reading3Name ARD_details &lt;br /&gt;
attr PM reading3Regex &amp;lt;span class=&amp;quot;stationName&amp;quot;&amp;gt;ARD&amp;lt;\/span&amp;gt;[\w\W]*?&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;\s*&amp;lt;a[\w\W]*?&amp;gt;[\w\W]*?&amp;lt;\/a&amp;gt;\s*&amp;lt;br\/&amp;gt;\s*(.*?)(&amp;lt;img|&amp;lt;\/div) &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reading1 contains the programme, reading2 the time and reading3 further details.  The Regex result from the structure of the website and may have to be designed in painstaking detail.&lt;br /&gt;
&lt;br /&gt;
There are cases where this is not enough. For example, it could be the case that the information from the web access is not automated, but manually received. Then a 0 must first be entered at the top of the duration. But how does HTTPMOD then know when the data should be retrieved? This is done with get-commands. If a get-command is executed, HTTPMOD accesses the website, fetches the information and fills the corresponding readings. &lt;br /&gt;
&lt;br /&gt;
Again, it is possible to create not one but several get-commands. Therefore, get is always followed by (at least) one number with which the get commands are numbered.  Here is another example. The TV program should be called manually and not time-controlled. To do this we first change the definition to &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define PM HTTPMOD  http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/-1/free.html 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
and add the following to the attributes&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM get1Name ARDHolen &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
With the command &amp;quot;get PM ARDHolen&amp;quot; the above readings are filled. In order for FHEM to know that the other reading2, reading3, etc. are to be read as well as reading1, the (user) attribute  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM get1CheckAllReadings 1 &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
needs to be defined.&lt;br /&gt;
&lt;br /&gt;
If the URL is not sufficient when the web page is called, but a special header is to be sent with it, this can be done with the get1Header attribute. If the header is multiline, the individual lines can be entered with get1Header1, get1Header2, and so on.    &lt;br /&gt;
&lt;br /&gt;
If you want to evaluate or control different URLs, you can define several get commands (get1, get2, etc.) and provide them with your own URLs. get1 usually reads only the reading1, get2 reads only the reading2 and so on. With CheckAllReadings all readings are evaluated simultaneously.&lt;br /&gt;
&lt;br /&gt;
If only data is to be sent on the web page, but not read, the set command is recommended. It has the same structure as the get-command.&lt;br /&gt;
&lt;br /&gt;
It becomes a bit more complex if the website requires a login check. The sid attributes are used for this purpose.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Availability, prerequisites and definition ==&lt;br /&gt;
This module is part of the regular FHEM distribution and uses the non blocking HTTP function &amp;lt;code&amp;gt;HttpUtils_NonblockingGet&amp;lt;/code&amp;gt; provided by FHEM&#039;s [[HttpUtils]] in a new version published in December 2013.&lt;br /&gt;
If not already installed in your environment, please [[update]] FHEM or install it manually using appropriate commands from your environment.&lt;br /&gt;
Please also note that FHEM HttpUtils need the global attribute dnsServer to be set in order to work really non blocking even when dns requests can not be answered.&lt;br /&gt;
&lt;br /&gt;
The device is defined as follows&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define &amp;lt;name&amp;gt; HTTPMOD &amp;lt;URL&amp;gt; &amp;lt;Interval&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The module connects to the given &amp;lt;code&amp;gt;URL&amp;lt;/code&amp;gt; every &amp;lt;code&amp;gt;Interval&amp;lt;/code&amp;gt; seconds, sends optional headers and data and then parses the response with regular expressions, xpath or json to set readings.&lt;br /&gt;
&lt;br /&gt;
URL can be &amp;quot;none&amp;quot; and Interval can be 0 if you prefer to only query data with a get command and not in a defined interval.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define PM HTTPMOD http://MyPoolManager/cgi-bin/webgui.fcgi 60&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Set-Commands ==&lt;br /&gt;
can be defined using attributes, see advanced configuration&lt;br /&gt;
&lt;br /&gt;
If you set the attribute enableControlSet to 1, the following additional built in set commands are available:&lt;br /&gt;
;interval&lt;br /&gt;
:set new interval time in seconds and restart the timer&lt;br /&gt;
;reread&lt;br /&gt;
:request the defined URL and try to parse it just like the automatic update would do it every Interval seconds without modifying the running timer.&lt;br /&gt;
;stop&lt;br /&gt;
:stop interval timer.&lt;br /&gt;
;start&lt;br /&gt;
:restart interval timer to call GetUpdate after interval seconds&lt;br /&gt;
;upgradeAttributes&lt;br /&gt;
:convert outdated attributes for older HTTPMOD-Versions that are still defined for this device from the old syntax to the new one.&lt;br /&gt;
:attributes with the description &amp;quot;this attribute should not be used anymore&amp;quot; or similar will be translated to the new syntax, e.g. readingsName1 to reading01Name.&lt;br /&gt;
;storeKeyValue&lt;br /&gt;
:stores a key value pair in an obfuscated form in the file system. Such values can then be used in replacements where the mode is &amp;quot;key&amp;quot; e.g. to avoid storing passwords in the configuration in clear text&lt;br /&gt;
&lt;br /&gt;
== Get-Commands ==&lt;br /&gt;
can be defined using attributes, see advanced configuration&lt;br /&gt;
&lt;br /&gt;
== simple Attributes ==&lt;br /&gt;
;enableControlSet&lt;br /&gt;
:enables the built in set commands &#039;&#039;interval&#039;&#039;, &#039;&#039;stop&#039;&#039;, &#039;&#039;start&#039;&#039;, &#039;&#039;reread&#039;&#039;, &#039;&#039;upgradeAttributes&#039;&#039;, &#039;&#039;storeKeyValue&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;enableCookies&lt;br /&gt;
:enables the cookie handling inside HTTPMOD. It is advisable to always set this attribute and allow HTTPMOD to track the state of cookies and set them for following HTTP-requests&lt;br /&gt;
&lt;br /&gt;
;enforceGoodReadingNames&lt;br /&gt;
:makes sure that HTTPMOD only creates readings that are allowd for Fhem (especially if reading names are dynamically created from JSON object names with extractAllJSON. It is advisable to always set this attribute.&lt;br /&gt;
&lt;br /&gt;
;handleRedirects&lt;br /&gt;
:enables the redirect handling inside HTTPMOD which should be used together with the cookie handling of HTTPMOD. HTTPMOD will then automatically follow redirects from a web server and keep track of cookies at the same time. It is advisable to always set this attribute.&lt;br /&gt;
&lt;br /&gt;
;requestHeader.* &lt;br /&gt;
:Define an additional HTTP Header to set in the HTTP request&lt;br /&gt;
&lt;br /&gt;
;requestData&lt;br /&gt;
:POST Data to be sent in the request. If not defined, it will be a GET request as defined in HttpUtils used by this module&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+(-[0-9]+)?Name&lt;br /&gt;
:the name of a reading to extract with the corresponding readingRegex&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?OExpr&lt;br /&gt;
:defines an expression that is used in an eval to compute the readings value. The raw value will be in the variable $val.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?OMap&lt;br /&gt;
:Output Map. Defines a mapping from raw to visible values like &amp;quot;0:mittig, 1:oberhalb, 2:unterhalb&amp;quot;. If specified as readingOMap then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*OMap.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?Format&lt;br /&gt;
:Defines a format string that will be used in sprintf to format a reading value. If specified as readingFormat then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*Format.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?Decode&lt;br /&gt;
:defines an encoding to be used in a call to the perl function decode to convert the raw data string read from the device to a reading. This can be used if the device delivers strings in an encoding like cp850 instead of utf8.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?Encode&lt;br /&gt;
:defines an encoding to be used in a call to the perl function encode to convert the raw data string read from the device to a reading. This can be used if the device delivers strings in an encoding like cp850 and after decoding it you want to reencode it to e.g. utf8.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+Regex&lt;br /&gt;
:defines the regex to be used for extracting the reading. The value to extract should be in a capture group / sub expression &lt;br /&gt;
:e.g. ([\d\.]+) in the above example. Multiple capture groups will create multiple readings (see explanation above)&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+XPath&lt;br /&gt;
:defines an xpath to one or more readings when parsing HTML data (see examples below)&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+XPath-Strict&lt;br /&gt;
:defines an xpath to one or more readings when parsing XML data (see examples below)&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+JSON&lt;br /&gt;
:defines a path to the JSON object wanted by concatenating the object names with an underscore as delimiter (see the example below)&lt;br /&gt;
&lt;br /&gt;
;noShutdown&lt;br /&gt;
:pass the noshutdown flag to HTTPUtils for webservers that need it (some embedded webservers only deliver empty pages otherwise)&lt;br /&gt;
&lt;br /&gt;
;disable&lt;br /&gt;
:stop doing automatic HTTP requests while this attribute is set to 1&lt;br /&gt;
&lt;br /&gt;
;timeout&lt;br /&gt;
:time in seconds to wait for an answer. Default value is 2&lt;br /&gt;
&lt;br /&gt;
;do_not_notify&lt;br /&gt;
&lt;br /&gt;
;readingFnAttributes&lt;br /&gt;
&lt;br /&gt;
== Simple Configuration of HTTP Devices ==&lt;br /&gt;
If your device expects special HTTP-headers then specify them as &amp;lt;code&amp;gt;attr requestHeader1&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;attr requestHeaderX&amp;lt;/code&amp;gt;.&lt;br /&gt;
If your Device expects an HTTP POST instead of HTTP GET then the POST-data can be specified as &amp;lt;code&amp;gt;attr requestData&amp;lt;/code&amp;gt;.&lt;br /&gt;
To get the readings, specify pairs of &amp;lt;code&amp;gt;attr readingXName&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;attr readingXRegex&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;attr readingXXPath&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;attr readingXXPath-Strict&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;attr readingXJSON&amp;lt;/code&amp;gt; to define which readings you want to extract from the HTTP response and how to extract them. (The old syntax &amp;lt;code&amp;gt;attr readingsNameX&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;attr readingsRegexX&amp;lt;/code&amp;gt; is still supported but the new one with &amp;lt;code&amp;gt;attr readingXName&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;attr readingXRegex&amp;lt;/code&amp;gt; should be preferred. The actual values to be extracted have to be sub expressions within () in the regex (see example below)&lt;br /&gt;
&lt;br /&gt;
=== Example for a PoolManager 5: ===&lt;br /&gt;
The PoolManager Web GUI can be queried with HTTP POST Requests like this one:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
POST /cgi-bin/webgui.fcgi HTTP/1.1&lt;br /&gt;
Host: 192.168.70.90&lt;br /&gt;
Accept: */*&lt;br /&gt;
Content-Type: application/json;charset=UTF-8&lt;br /&gt;
Content-Length: 60&lt;br /&gt;
&lt;br /&gt;
{&amp;quot;get&amp;quot; :[&amp;quot;34.4001.value&amp;quot; ,&amp;quot;34.4008.value&amp;quot; ,&amp;quot;34.4033.value&amp;quot;]}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The resulting HTTP Response would look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
HTTP/1.1 200 OK&lt;br /&gt;
Content-type: application/json; charset=UTF-8&lt;br /&gt;
Expires: 0&lt;br /&gt;
Cache-Control: no-cache&lt;br /&gt;
Date: Sun, 12 Jan 2014 12:23:11 GMT&lt;br /&gt;
Server: lighttpd/1.4.26&lt;br /&gt;
Content-Length: 179&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;data&amp;quot;:	{&lt;br /&gt;
		&amp;quot;34.4001.value&amp;quot;:	&amp;quot;7.00&amp;quot;,&lt;br /&gt;
		&amp;quot;34.4008.value&amp;quot;:	&amp;quot;0.52&amp;quot;,&lt;br /&gt;
		&amp;quot;34.4033.value&amp;quot;:	&amp;quot;24.8&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;status&amp;quot;:	{&lt;br /&gt;
		&amp;quot;code&amp;quot;:	0&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;event&amp;quot;:	{&lt;br /&gt;
		&amp;quot;type&amp;quot;:	1,&lt;br /&gt;
		&amp;quot;data&amp;quot;:	&amp;quot;48.30000.0&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To configure HTTPMOD for a PoolManager one would first define a PoolManager device with e.g. the name PM, the URL and an interval of e.g. 60 seconds. &lt;br /&gt;
&lt;br /&gt;
Then the data to be sent in the request needs to be defined because in this example the device expects a POST request so the query is not contained in the URL but in the request data.&lt;br /&gt;
&lt;br /&gt;
Also as seen above the device expects special HTTP headers in the request so these headers also need to be defined as &amp;lt;code&amp;gt;attr PM requestHeader1&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;attr PM requestHeader2&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then the names of the readings to be extracted would be set with attributes&lt;br /&gt;
&lt;br /&gt;
Then for each reading value to be extracted a regular expression needs to be set that will match the value in question within ().&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define PM HTTPMOD http://MyPoolManager/cgi-bin/webgui.fcgi 60&lt;br /&gt;
&lt;br /&gt;
attr PM enableControlSet 1&lt;br /&gt;
attr PM enableCookies 1&lt;br /&gt;
attr PM enforceGoodReadingNames 1&lt;br /&gt;
attr PM handleRedirects 1&lt;br /&gt;
&lt;br /&gt;
attr PM reading01Name PH&lt;br /&gt;
attr PM reading01Regex 34.4001.value&amp;quot;:[ \t]+&amp;quot;([\d\.]+)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attr PM reading02Name CL&lt;br /&gt;
attr PM reading02Regex 34.4008.value&amp;quot;:[ \t]+&amp;quot;([\d\.]+)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attr PM reading03Name3TEMP&lt;br /&gt;
attr PM reading03Regex 34.4033.value&amp;quot;:[ \t]+&amp;quot;([\d\.]+)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attr PM requestData {&amp;quot;get&amp;quot; :[&amp;quot;34.4001.value&amp;quot; ,&amp;quot;34.4008.value&amp;quot; ,&amp;quot;34.4033.value&amp;quot;, &amp;quot;14.16601.value&amp;quot;, &amp;quot;14.16602.value&amp;quot;]}&lt;br /&gt;
attr PM requestHeader1 Content-Type: application/json&lt;br /&gt;
attr PM requestHeader2 Accept: */*&lt;br /&gt;
attr PM stateFormat {sprintf(&amp;quot;%.1f Grad, PH %.1f, %.1f mg/l Chlor&amp;quot;, ReadingsVal($name,&amp;quot;TEMP&amp;quot;,0), ReadingsVal($name,&amp;quot;PH&amp;quot;,0), ReadingsVal($name,&amp;quot;CL&amp;quot;,0))}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Example for AmbientMonitor ===&lt;br /&gt;
AmbientMonitor is a webbased visualisation for sensors connected to an Arduino device. Its web interface can also be queried with HTTMOD to grab the data into readings.&lt;br /&gt;
&lt;br /&gt;
This example was provided by locutus. The hardware configuration is an Arduino + Ethercard with ENC28J60 Controller + DHT22 Sensor and software can be downloaded from https://github.com/lucadentella/AmbientMonitor&lt;br /&gt;
&lt;br /&gt;
In this example an HTTP GET is sufficent, so no &amp;lt;code&amp;gt;requestData&amp;lt;/code&amp;gt; is needed. The device provides temperature and humidity readings in an HTTP response that looks like:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
HTTP/1.0 200 OK &lt;br /&gt;
Content-Type: text/html &lt;br /&gt;
&lt;br /&gt;
myCB({&#039;temperature&#039;:22.00,&#039;humidity&#039;:46.00})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the definition could be:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define AmbientMonitor HTTPMOD http://192.168.1.221/?callback=? 300&lt;br /&gt;
&lt;br /&gt;
attr AmbientMonitor enableControlSet 1&lt;br /&gt;
attr AmbientMonitor enableCookies 1&lt;br /&gt;
attr AmbientMonitor enforceGoodReadingNames 1&lt;br /&gt;
attr AmbientMonitor handleRedirects 1&lt;br /&gt;
&lt;br /&gt;
attr AmbientMonitor requestHeader Content-Type: application/json&lt;br /&gt;
attr AmbientMonitor reading1Name Temperatur&lt;br /&gt;
attr AmbientMonitor reading1Regex temperature&#039;:([\d\.]+)&lt;br /&gt;
attr AmbientMonitor reading2Name Feuchtigkeit&lt;br /&gt;
attr AmbientMonitor reading2Regex humidity&#039;:([\d\.]+)&lt;br /&gt;
attr AmbientMonitor stateFormat {sprintf(&amp;quot;Temperatur %.1f C, Feuchtigkeit %.1f %&amp;quot;, ReadingsVal($name,&amp;quot;Temperatur&amp;quot;,0), ReadingsVal($name,&amp;quot;Feuchtigkeit&amp;quot;,0))}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== formatting and manipulating values / readings ==&lt;br /&gt;
Values that are parsed from an HTTP response can be further treated or formatted with the following attributes:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?OExpr&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?OMap&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?Format&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?Decode&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?Encode&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
They can all be specified for an individual reading, for all readings in one match (e.g. if a regular expression has several capture groups) or for all readings in a get command (defined by getXX) or for all readings in the main reading list (defined by readingXX):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading01Format %.1f&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will format the reading with the name specified by the attribute reading01Name to be numerical with one digit after the decimal point.&lt;br /&gt;
If the attribute reading01Regex is used and contains several capture groups then the format will be applied to all readings parsed by this regex unless these readings have their own format specified by reading01-1Format, reading01-2Format and so on.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading01-2Format %.1f&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be used in cases where a regular expression specified as reading01regex contains several capture groups or an xpath specified as reading01XPath creates several readings. &lt;br /&gt;
In this case reading01-2Format specifies the format to be applied to the second match.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
readingFormat %.1f&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
applies to all readings defined by a reading-Attribute that have no more specific format.&lt;br /&gt;
&lt;br /&gt;
If you need to do some calculation on a raw value before it is used as a reading, you can define the attribute &amp;lt;code&amp;gt;readingOExpr&amp;lt;/code&amp;gt;.&lt;br /&gt;
It defines a Perl expression that is used in an eval to compute the readings value. The raw value will be in the variable $val.&lt;br /&gt;
&lt;br /&gt;
Example for an expression:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM reading03OExpr $val * 10&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Just like in the above example of the readingFormat attributes, readingOExpr and the other following attributes can be applied on several levels.&lt;br /&gt;
&lt;br /&gt;
To map a numerical value to a name, you can use the readingOMap attribute. &lt;br /&gt;
It defines a mapping from raw to visible values like &amp;quot;0:mittig, 1:oberhalb, 2:unterhalb&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Example for a map:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM reading02-3OMap 0:kalt, 1:warm, 2:sehr warm&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To convert character sets, the module can first decode a string read from the device and then encode it again. For example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM getDecode UTF-8&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This applies to all readings defined for Get-Commands.&lt;br /&gt;
&lt;br /&gt;
== Some help with Regular Expressions ==&lt;br /&gt;
{{Randnotiz|RNTyp=y|RNText=Starting with version &#039;&#039;2018-02-10&#039;&#039; the internal that holds the HTTP response is no longer called &#039;&#039;&#039;&#039;&#039;buf&#039;&#039;&#039;&#039;&#039; but rather &#039;&#039;&#039;&#039;&#039;httpbody&#039;&#039;&#039;&#039;&#039;, and it is only displayed when attribute &#039;&#039;&#039;&#039;&#039;showBody&#039;&#039;&#039;&#039;&#039; is set to &amp;quot;1&amp;quot;.}}&lt;br /&gt;
If HTTPMOD seems not to work and the FHEM Logfile contains a message like  &lt;br /&gt;
:&amp;lt;code&amp;gt;HTTPMOD: Response didn&#039;t match Reading ...&amp;lt;/code&amp;gt;&lt;br /&gt;
then you should check if the value you want to extract is read into the internal with the name buf. Internals are visible when you click on the defined HTTPMOD Device. buf is an internal variable that contains the HTTP Response read. If the value is there and you get the mentioned message then probably something is wrong with your regular expression. Please note that buf might contain special characters like newlines but they are not shown in fhemweb. If you are new to regular expressions then the introduction at http://perldoc.perl.org/perlretut.html might be helpful. &lt;br /&gt;
&lt;br /&gt;
For a typical HTTPMOD use case where you want to extract a number out of a HTTP-Response you can use something like &amp;lt;code&amp;gt;[\d\.]+&amp;lt;/code&amp;gt; to match the number itself. The expression matches the number characters (&amp;lt;code&amp;gt;\d&amp;lt;/code&amp;gt;) or a &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; if one of these characters occurs at least once. &lt;br /&gt;
&lt;br /&gt;
To tell HTTPMOD that the number is what you want to use for the reading, you have to put the expression in between &amp;lt;code&amp;gt;()&amp;lt;/code&amp;gt;. A &amp;lt;code&amp;gt;([\d\.]+)&amp;lt;/code&amp;gt; alone would match the longest number in the HTTP Response which is very likely not the number you are looking for so you need to add something to the expression to give it a context and define how to find the number that you are looking for.&lt;br /&gt;
&lt;br /&gt;
If there is a title text before the number or a special text after the number you can put this in the regex. In one of the examples above &amp;lt;code&amp;gt;humidity&#039;:([\d\.]+)&amp;lt;/code&amp;gt; is looking for the number that immediately follows the text &amp;lt;code&amp;gt;humidity&#039;:&amp;lt;/code&amp;gt; without any blanks in between.&lt;br /&gt;
Be careful if the text you are getting from your device contains special characters like newline. You don&#039;t see such special characters in the fhem webinterface as contents of the internal buf but they might cause your regular expression to fail. &lt;br /&gt;
&lt;br /&gt;
If you have trouble defining a regular expression that matches a certain name, then many complicated characters and then a number, it might be helpful to use a negation in matching like &amp;lt;code&amp;gt;temp[^\d]+([\d\.]).*&amp;lt;/code&amp;gt;. In this examle &amp;lt;code&amp;gt;[^\d]+&amp;lt;/code&amp;gt; means any character that is not a numerical digit, more than once.&lt;br /&gt;
&lt;br /&gt;
=== Regular Expressions with multiple capture Groups ===&lt;br /&gt;
The regular expressions used in the above example for a Poolmanager will take the value that matches one capture group. This is the part of the regular expression inside (). In the above example &amp;quot;([\d\.]+)&amp;quot; refers to numerical digits or points between double quotation marks. Only the string consiting of digits and points will match inside (). This piece is assigned to the reading.&lt;br /&gt;
        &lt;br /&gt;
You can also use regular expressions that have several capture groups which might be helpful when parsing tables. In this case an attribute like &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading02Regex something[ \t]+([\d\.]+)[ \t]+([\d\.]+)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
could match two numbers. When you specify only one reading02Name like &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading02Name Temp&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the name Temp will be used with the extension -1 and -2 thus giving a reading Temp-1 for the first number and Temp-2 for the second. You can also specify individual names for several readings that get parsed from one regular expression with several capture groups by defining attributes &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading02-1Name&lt;br /&gt;
reading02-2Name&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The same notation can be used for formatting attributes like readingXOMap, readingXFormat and so on.&lt;br /&gt;
&lt;br /&gt;
The usual way to define readings is however to have an individual regular expression with just one capture group per reading as shown in the above example.&lt;br /&gt;
&lt;br /&gt;
== Parsing JSON ==&lt;br /&gt;
    &lt;br /&gt;
If a webservice delivers data in JSON format, HTTPMOD can directly parse JSON which might be easier in this case than definig regular expressions.&lt;br /&gt;
The next example shows the data that can be requested from a Poolmanager with the following partial configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define test2 HTTPMOD none 0&lt;br /&gt;
attr test2 get01Name Chlor&lt;br /&gt;
attr test2 getURL http://192.168.70.90/cgi-bin/webgui.fcgi&lt;br /&gt;
attr test2 getHeader1 Content-Type: application/json&lt;br /&gt;
attr test2 getHeader2 Accept: */*&lt;br /&gt;
attr test2 getData {&amp;quot;get&amp;quot; :[&amp;quot;34.4008.value&amp;quot;]}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The data in the HTTP response looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;data&amp;quot;: {&lt;br /&gt;
			&amp;quot;34.4008.value&amp;quot;: &amp;quot;0.25&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;status&amp;quot;:       {&lt;br /&gt;
			&amp;quot;code&amp;quot;: 0&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;event&amp;quot;:        {&lt;br /&gt;
			&amp;quot;type&amp;quot;: 1,&lt;br /&gt;
			&amp;quot;data&amp;quot;: &amp;quot;48.30000.0&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the classic way to extract the value 0.25 into a reading with the name Chlor with a regex would have been&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr test2 get01Regex 34.4008.value&amp;quot;:[ \t]+&amp;quot;([\d\.]+)&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
with JSON you can write &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr test2 get01JSON data_34.4008.value &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
which will create a reading with the Name &amp;quot;Chlor&amp;quot; (as shown above) and take the value 0.25 from the JSON string.&lt;br /&gt;
&lt;br /&gt;
or if you don&#039;t care about the naming of your readings, you can simply extract all JSON data with &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr test2 extractAllJSON&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which would apply to all data read from this device and create the following readings out of the HTTP response shown above:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| data_34.4008.value || 0.25&lt;br /&gt;
|-&lt;br /&gt;
| event_data || 48.30000.0&lt;br /&gt;
|-&lt;br /&gt;
| event_type || 1&lt;br /&gt;
|-&lt;br /&gt;
| status_code || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
or you can specify&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr test2 get01ExtractAllJSON&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
which would only apply to all data read as response to the get command defined as get01.        &lt;br /&gt;
&lt;br /&gt;
It might seem very simple at first sight to use extractAllJSON but if you prefer readings with a meaningful name you should instead define these readings with readingXXName and readingXXJSON or getXXName and getXXJSON individually. Of Course it would be possible to create additional user readings outside HTTPMOD but doing calculations, naming and formatting inside HTTPMOD is more efficient.&lt;br /&gt;
&lt;br /&gt;
=== JSON Lists ===&lt;br /&gt;
&lt;br /&gt;
imagine the HTTP Response contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{ &amp;quot;power&amp;quot;:&amp;quot;0&amp;quot;,&lt;br /&gt;
  &amp;quot;modes&amp;quot;:[&amp;quot;Off&amp;quot;,&amp;quot;SimpleColor&amp;quot;,&amp;quot;RainbowChase&amp;quot;],&lt;br /&gt;
  &amp;quot;code1&amp;quot;:3,&lt;br /&gt;
  &amp;quot;code2&amp;quot;:4&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
then a configuration like &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr device reading01JSON modes &lt;br /&gt;
attr device reading01Name Mode &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will create a list of Subreadings just like a regex with multiple matches can create multiple subreadings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Mode-1 || Off&lt;br /&gt;
|-&lt;br /&gt;
| Mode-2  || SimpleColor&lt;br /&gt;
|-&lt;br /&gt;
| Mode-3 || RainbowChase &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
if you don&#039;t want several subreadings but one reading that contains the list of modes, you can specify a recombine expression:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr device reading01Name Modes &lt;br /&gt;
attr device reading01RecombineExpr join &amp;quot;,&amp;quot;, @matchlist &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which will create one reading containing a list:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Modes || Off,SimpleColor,RainbowChase&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
JSON parsing specifications also don&#039;t Need to match exactly. If there is no exact match for a defined reading, the HTTPMOD will try to Interpret the specification as a regex and look for json object paths that match the specification as a regex. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr device reading01Name CodeElem&lt;br /&gt;
attr device reading01JSON code&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which will create a list of readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| CodeElem-1|| 3&lt;br /&gt;
|-&lt;br /&gt;
| CodeElem-2  || 4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
and of course they could also be recombined into one reading with a RecombineExpr Attribute.&lt;br /&gt;
&lt;br /&gt;
== Parsing http / XML using xpath ==&lt;br /&gt;
Another alternative to regex parsing is the use of XPath to extract values from HTTP responses.&lt;br /&gt;
The following example shows how XML data can be parsed with XPath-Strict or HTML Data can be parsed with XPath.&lt;br /&gt;
Both work similar and the example uses XML Data parsed with the XPath-Strict option:&lt;br /&gt;
&lt;br /&gt;
If The XML data in the HTTP response looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;root xmlns:foo=&amp;quot;http://www.foo.org/&amp;quot; xmlns:bar=&amp;quot;http://www.bar.org&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;actors&amp;gt;&lt;br /&gt;
		&amp;lt;actor id=&amp;quot;1&amp;quot;&amp;gt;Peter X&amp;lt;/actor&amp;gt;&lt;br /&gt;
		&amp;lt;actor id=&amp;quot;2&amp;quot;&amp;gt;Charles Y&amp;lt;/actor&amp;gt;&lt;br /&gt;
		&amp;lt;actor id=&amp;quot;3&amp;quot;&amp;gt;John Doe&amp;lt;/actor&amp;gt;&lt;br /&gt;
	&amp;lt;/actor&amp;gt;&lt;br /&gt;
&amp;lt;/root&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
with XPath you can write        &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr htest reading01Name Actor&lt;br /&gt;
attr htest reading01XPath-Strict //actor[2]/text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will create a reading with the Name &amp;quot;Actor&amp;quot; and the value &amp;quot;Charles Y&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Since XPath specifications can define several values / matches, HTTPMOD can also interpret these and store them in multiple readings:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr htest reading01Name Actor&lt;br /&gt;
attr htest reading01XPath-Strict //actor/text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will create the readings &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Actor-1 || Peter X&lt;br /&gt;
|-&lt;br /&gt;
| Actor-2 || Charles Y&lt;br /&gt;
|-&lt;br /&gt;
| Actor-3 || John Doe&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Further replacements of URL, header or post data ==&lt;br /&gt;
sometimes it might be helpful to dynamically change parts of a URL, HTTP header or post data depending on existing readings, internals or &lt;br /&gt;
perl expressions at runtime. This might be needed to pass further variables to a server, a current date or other things. &lt;br /&gt;
&lt;br /&gt;
To support this HTTPMOD offers generic replacements that are applied to a request before it is sent to the server. A replacement can be defined with the attributes &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;replacement[0-9]*Regex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;[gs]et[0-9]*Replacement[0-9]*Value&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
a replacement always replaces a match of a regular expression. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;replacement[0-9]*Mode:&#039;&#039;&#039;&lt;br /&gt;
The way the replacement value is defined can be specified with the replacement mode.&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;reading&amp;lt;/code&amp;gt;, then the corresponding &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt; is interpreted as the name of a &#039;&#039;reading&#039;&#039; of the same device or as &#039;&#039;device:reading&#039;&#039; to refer to another device.&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;internal&amp;lt;/code&amp;gt;, then the corresponding &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt; is interpreted as the name of an &#039;&#039;internal&#039;&#039; of the same device or as &#039;&#039;device:internal&#039;&#039; to refer to another device.&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;, then the corresponding &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt; is interpreted as a static text&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;expression&amp;lt;/code&amp;gt;, then the corresponding &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt; is evaluated as a perl expression to compute the replacement. Inside such a replacement expression it is possible to refer to capture groups of the replacement regex.&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;key&amp;lt;/code&amp;gt;, then the module will use a value from a key / value pair that is stored in an obfuscated form in the file system with the set storeKeyValue command. This might be useful for storing passwords.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
attr mydevice getData {&amp;quot;get&amp;quot; :[&amp;quot;%%value%%.value&amp;quot;]}&lt;br /&gt;
attr mydevice replacement01Mode text&lt;br /&gt;
attr mydevice replacement01Regex %%value%%&lt;br /&gt;
&lt;br /&gt;
attr mydevice get01Name Chlor&lt;br /&gt;
attr mydevice get01Replacement01Value 34.4008&lt;br /&gt;
&lt;br /&gt;
attr mydevice get02Name Something&lt;br /&gt;
attr mydevice get02Replacement01Value 31.4024&lt;br /&gt;
&lt;br /&gt;
attr mydevice get05Name profile&lt;br /&gt;
attr mydevice get05URL http://www.mydevice.local/getprofile?password=%%password%%&lt;br /&gt;
attr mydevice replacement02Mode key&lt;br /&gt;
attr mydevice replacement02Regex %%password%%&lt;br /&gt;
attr mydevice replacement02Value password&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;        &lt;br /&gt;
&lt;br /&gt;
defines that &amp;lt;code&amp;gt;%%value%%&amp;lt;/code&amp;gt; will be replaced by a static text.&lt;br /&gt;
&lt;br /&gt;
All Get commands will be HTTP post requests of a similar form. Only the &amp;lt;code&amp;gt;%%value%%&amp;lt;/code&amp;gt; will be different from get to get.&lt;br /&gt;
The first get will set the reading named Chlor and for the request it will take the generic getData and replace %%value%% with 34.4008.&lt;br /&gt;
&lt;br /&gt;
A second get will look the same except a different name and replacement value.&lt;br /&gt;
&lt;br /&gt;
With the command &amp;lt;code&amp;gt;set mydevice storeKeyValue password geheim&amp;lt;/code&amp;gt; you can store the password geheim in an obfuscated form in the file system. &lt;br /&gt;
To use this password and send it in a request you can use the above replacement with mode key. The value password will then refer to the ofuscated string stored with the key password.&lt;br /&gt;
&lt;br /&gt;
The mode &amp;lt;code&amp;gt;expression&amp;lt;/code&amp;gt; allows you to define your own replacement syntax:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;        &lt;br /&gt;
attr mydevice replacement01Mode expression&lt;br /&gt;
attr mydevice replacement01Regex {{([^}]+)}}&lt;br /&gt;
attr mydevice replacement01Value ReadingsVal(&amp;quot;mydevice&amp;quot;, $1, &amp;quot;&amp;quot;)&lt;br /&gt;
attr mydevice getData {&amp;quot;get&amp;quot; :[&amp;quot;{{temp}}.value&amp;quot;]}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;      &lt;br /&gt;
&lt;br /&gt;
In this example any &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{name}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; in a URL, header or post data will be passed on to the perl function ReadingsVal &lt;br /&gt;
which uses the string between &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; as second parameter. This way one defined replacement can be used for many different&lt;br /&gt;
readings.&lt;br /&gt;
&lt;br /&gt;
HTTPMOD has two built in replacements: One for session Ids and another one for the input value in a set command.&lt;br /&gt;
The placeholder $sid is always replaced with the internal &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt; which contains the session id after it is extracted from a previous HTTP response. &lt;br /&gt;
If you don&#039;t like to use the placeholder $sid then you can define your own replacement for example like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr mydevice replacement01Mode internal&lt;br /&gt;
attr mydevice replacement01Regex %session%&lt;br /&gt;
attr mydevice replacement01Value sid&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;      &lt;br /&gt;
&lt;br /&gt;
Now the internal &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt; will be used as a replacement for the placeholder %session%.&lt;br /&gt;
&lt;br /&gt;
In the same way a value that is passed to a set-command can be put into a request with a user defined replacement. &lt;br /&gt;
In this case the internal &amp;lt;code&amp;gt;$hash-&amp;gt;{value}&amp;lt;/code&amp;gt; will contain the value passed to the set command. &lt;br /&gt;
&amp;lt;code&amp;gt;$hash-&amp;gt;{value}&amp;lt;/code&amp;gt; might even be a string containing several values that could be put into several different positions in a request by using user defined replacements.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Other example : steering a pellet stove from Rika&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The stove API of Rika on https://www.rika-firenet.com/web/ delivers a JSON string with all settings and values, and can be piloted with a data string containing all the &amp;quot;set&amp;quot; values at once.&lt;br /&gt;
&lt;br /&gt;
Delivered JSON on get https://www.rika-firenet.com/api/client/xxxxxxxx/status : (xxxxxxxx must be replaced with the unique stove ID)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Vorzimmer&amp;quot;,&lt;br /&gt;
    &amp;quot;stoveID&amp;quot;: &amp;quot;xxxxxxxxx&amp;quot;,&lt;br /&gt;
    &amp;quot;lastSeenMinutes&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;lastConfirmedRevision&amp;quot;: 1504385700,&lt;br /&gt;
    &amp;quot;controls&amp;quot;: {&lt;br /&gt;
        &amp;quot;revision&amp;quot;: 1504385700,&lt;br /&gt;
        &amp;quot;onOff&amp;quot;: true,&lt;br /&gt;
        &amp;quot;operatingMode&amp;quot;: 2,&lt;br /&gt;
        &amp;quot;heatingPower&amp;quot;: 65,&lt;br /&gt;
        &amp;quot;targetTemperature&amp;quot;: 24,&lt;br /&gt;
        &amp;quot;heatingTimesActive&amp;quot;: false,&lt;br /&gt;
        &amp;quot;heatingTimesActiveForComfort&amp;quot;: true,&lt;br /&gt;
        &amp;quot;setBackTemperature&amp;quot;: 18,&lt;br /&gt;
        &amp;quot;convectionFan1Active&amp;quot;: false,&lt;br /&gt;
        &amp;quot;convectionFan1Level&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;convectionFan1Area&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;convectionFan2Active&amp;quot;: false,&lt;br /&gt;
        &amp;quot;convectionFan2Level&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;convectionFan2Area&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;frostProtectionActive&amp;quot;: false,&lt;br /&gt;
        &amp;quot;frostProtectionTemperature&amp;quot;: 5&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;sensors&amp;quot;: {&lt;br /&gt;
        &amp;quot;statusError&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;statusWarning&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;statusService&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;statusMainState&amp;quot;: 1,&lt;br /&gt;
        &amp;quot;statusSubState&amp;quot;: 3,&lt;br /&gt;
        &amp;quot;statusFrostStarted&amp;quot;: false,&lt;br /&gt;
        &amp;quot;inputFlameTemperature&amp;quot;: 21,&lt;br /&gt;
        &amp;quot;inputRoomTemperature&amp;quot;: 21,&lt;br /&gt;
        &amp;quot;inputExternalRequest&amp;quot;: true,&lt;br /&gt;
        &amp;quot;outputDischargeMotor&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;outputInsertionMotor&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;outputIDFan&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;outputAirFlaps&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;outputIgnition&amp;quot;: false,&lt;br /&gt;
        &amp;quot;parameterStoveTypeNumber&amp;quot;: 13,&lt;br /&gt;
        &amp;quot;parameterVersionMainBoard&amp;quot;: 223,&lt;br /&gt;
        &amp;quot;parameterVersionTFT&amp;quot;: 223,&lt;br /&gt;
        &amp;quot;parameterRuntimePellets&amp;quot;: 11,&lt;br /&gt;
        &amp;quot;parameterRuntimeLogs&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;parameterFeedRateTotal&amp;quot;: 17,&lt;br /&gt;
        &amp;quot;parameterFeedRateService&amp;quot;: 683,&lt;br /&gt;
        &amp;quot;parameterOnOffCycles&amp;quot;: 2&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;stoveType&amp;quot;: &amp;quot;DOMO MultiAir&amp;quot;,&lt;br /&gt;
    &amp;quot;stoveFeatures&amp;quot;: {&lt;br /&gt;
        &amp;quot;multiAir1&amp;quot;: true,&lt;br /&gt;
        &amp;quot;multiAir2&amp;quot;: true,&lt;br /&gt;
        &amp;quot;insertionMotor&amp;quot;: false,&lt;br /&gt;
        &amp;quot;airFlaps&amp;quot;: false,&lt;br /&gt;
        &amp;quot;logRuntime&amp;quot;: false&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Data string to send to https://www.rika-firenet.com/api/client/xxxxxxxx/controls in order to set values:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
heatingTimesActiveForComfort=true&amp;amp;frostProtectionTemperature=3&amp;amp;setBackTemperature=18&amp;amp;targetTemperature=24&amp;amp;convectionFan2Level=0&amp;amp;convectionFan2Active=false&amp;amp;convectionFan1Level=0&amp;amp;onOff=true&amp;amp;convectionFan1Active=false&amp;amp;convectionFan2Area=0&amp;amp;revision=1505550101&amp;amp;heatingTimesActive=false&amp;amp;convectionFan1Area=0&amp;amp;frostProtectionActive=false&amp;amp;operatingMode=2&amp;amp;heatingPower=65&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Code in 99_myUtils.pm:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
use JSON;&lt;br /&gt;
...&lt;br /&gt;
sub&lt;br /&gt;
replaceJSON ($$) {&lt;br /&gt;
   my ($valToReplace, $value) = @_;&lt;br /&gt;
&lt;br /&gt;
   #$value in the parameters is a default value&lt;br /&gt;
   #It has to be replaced through the real value nnn passed in the set command &amp;quot;set &amp;lt;device&amp;gt; valToset nnn&amp;quot;&lt;br /&gt;
   $value = InternalVal(&amp;quot;Ofen&amp;quot;, &amp;quot;value&amp;quot;, $value);&lt;br /&gt;
   Log3 (&amp;quot;Ofen&amp;quot;, 3, &amp;quot;replaceJSON Internalvalue: $value&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   #Force an update to avoid outdated revision number&lt;br /&gt;
   fhem (&amp;quot;get Ofen revision&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   #Get all the controls as json&lt;br /&gt;
   my $json = ReadingsVal(&amp;quot;Ofen&amp;quot;, &amp;quot;controlsJSON&amp;quot;,&amp;quot;&amp;quot;);&lt;br /&gt;
   Log3 (&amp;quot;Ofen&amp;quot;, 3, &amp;quot;replaceJSON configsJSON: $json&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   # When starting FHEM or rereading config, the reading controlsJSON is empty&lt;br /&gt;
   return if ($json eq &amp;quot;&amp;quot;); &lt;br /&gt;
&lt;br /&gt;
   my $decoded = decode_json($json);&lt;br /&gt;
   my $result;&lt;br /&gt;
   for my $key ( keys %$decoded ) {&lt;br /&gt;
      $result .= &amp;quot;$key=&amp;quot;;&lt;br /&gt;
      if ($key eq $valToReplace) {&lt;br /&gt;
         $result .= $value.&amp;quot;&amp;amp;&amp;quot;;&lt;br /&gt;
      } else {&lt;br /&gt;
         $result .= $decoded-&amp;gt;{$key}.&amp;quot;&amp;amp;&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   chop($result); #remove last &amp;amp;&lt;br /&gt;
   Log3(&amp;quot;Ofen&amp;quot;, 3, &amp;quot;replaceJSON Result: $result&amp;quot;);&lt;br /&gt;
   return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Define stove in fhem:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Ofen HTTPMOD https://www.rika-firenet.com/api/client/xxxxxxxx/status 60&lt;br /&gt;
&lt;br /&gt;
attr Ofen enableCookies 1&lt;br /&gt;
attr Ofen reAuthRegex id=&amp;quot;login&amp;quot;|Unauthorized&lt;br /&gt;
attr Ofen sid01Data email=xx@xx&amp;amp;password=xx&lt;br /&gt;
attr Ofen sid01URL https://www.rika-firenet.com/web/login&lt;br /&gt;
&lt;br /&gt;
attr Ofen reading01JSON sensors_inputRoomTemperature&lt;br /&gt;
attr Ofen reading01Name RaumTemp&lt;br /&gt;
attr Ofen reading02JSON controls_setBackTemperature&lt;br /&gt;
attr Ofen reading02Name Absenkung&lt;br /&gt;
attr Ofen reading03JSON controls_frostProtectionTemperature&lt;br /&gt;
attr Ofen reading03Name Frostschutz&lt;br /&gt;
attr Ofen reading10Name controlsJSON&lt;br /&gt;
attr Ofen reading10Regex (?s)controls.*?({.*?})&lt;br /&gt;
&lt;br /&gt;
attr Ofen get09Name revision&lt;br /&gt;
attr Ofen get09URL https://www.rika-firenet.com/api/client/xxxxxxxx/status&lt;br /&gt;
&lt;br /&gt;
attr Ofen setURL https://www.rika-firenet.com/api/client/xxxxxxxx/controls&lt;br /&gt;
attr Ofen setData {{data}}&lt;br /&gt;
attr Ofen replacement01Mode expression&lt;br /&gt;
attr Ofen replacement01Regex {{data}}&lt;br /&gt;
&lt;br /&gt;
attr Ofen set11Name frostProtectionTemperature&lt;br /&gt;
attr Ofen set11Replacement01Value replaceJSON(&amp;quot;frostProtectionTemperature&amp;quot;, 2)&lt;br /&gt;
&lt;br /&gt;
attr Ofen set12Name targetTemperature&lt;br /&gt;
attr Ofen set12Replacement01Value replaceJSON(&amp;quot;targetTemperature&amp;quot;, 24)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A detailed explanation (in german) of the login process can be found here: [https://forum.fhem.de/index.php/topic,76220.msg682514.html#msg682514]&lt;br /&gt;
and the explanation of the other parameters here: [https://forum.fhem.de/index.php/topic,76220.msg685710.html#msg685710]&lt;br /&gt;
&lt;br /&gt;
== replacing reading values when they have not been updated / the device did not respond ==&lt;br /&gt;
If a device does not respond then the values stored in readings will keep the same and only their timestamp shows that they are outdated. &lt;br /&gt;
If you want to modify reading values that have not been updated for a number of seconds, you can use the attributes&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?MaxAge&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?MaxAgeReplacementMode&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?MaxAgeReplacement&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Every time the module tries to read from a device, it will also check if readings have not been updated &lt;br /&gt;
for longer than the &amp;lt;code&amp;gt;MaxAge&amp;lt;/code&amp;gt; attributes allow. If readings are outdated, the &amp;lt;code&amp;gt;MaxAgeReplacementMode&amp;lt;/code&amp;gt; defines how the affected&lt;br /&gt;
reading values should be replaced. &amp;lt;code&amp;gt;MaxAgeReplacementMode&amp;lt;/code&amp;gt; can be &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;reading&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;internal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;expression&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;MaxAge&amp;lt;/code&amp;gt; specifies the number of seconds that a reading should remain untouched before it is replaced. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;MaxAgeReplacement&amp;lt;/code&amp;gt; contains either a static text that is used as replacement value or a Perl expression that is evaluated to &lt;br /&gt;
give the replacement value. This can be used for example to replace a temperature that has not bee updated for more than 5 minutes &lt;br /&gt;
with the string &amp;quot;outdated - was 12&amp;quot;:        &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM readingMaxAge 300&lt;br /&gt;
attr PM readingMaxAgeReplacement &amp;quot;outdated - was &amp;quot; . $val&lt;br /&gt;
attr PM readingMaxAgeReplacementMode expression&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The variable &amp;lt;code&amp;gt;$val&amp;lt;/code&amp;gt; contains the value of the reading before it became outdated.&lt;br /&gt;
&lt;br /&gt;
Or to show that a device was offline:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr MyLight reading01Name color&lt;br /&gt;
attr MyLight reading01JSON result_02_color&lt;br /&gt;
attr MyLight reading01MaxAge 300&lt;br /&gt;
attr MyLight reading01MaxAgeReplacement &amp;quot;offline&amp;quot;&lt;br /&gt;
attr MyLight reading01MaxAgeReplacementMode text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Note on determining how to send requests to a special device ==&lt;br /&gt;
If you don&#039;t know which URLs, headers or POST data your web GUI uses, you might try a local proxy like BurpSuite [http://portswigger.net/burp/ BurpSuite] to track requests and responses. This is a tedious task but probably the best way to achieve a successful result. &lt;br /&gt;
&lt;br /&gt;
Let us consider an example. The Telekom Speedport W724V has a login-site that is famous for being cumbersome. Burp allows to monitor each step in the login procedure. In the case of a speedport the following steps occur:&lt;br /&gt;
&lt;br /&gt;
First burp shows that a get command is issued&lt;br /&gt;
 ################################################################################################## &lt;br /&gt;
 GET / HTTP/1.1&lt;br /&gt;
 Host: speedport.ip&lt;br /&gt;
 Cache-Control: max-age=0&lt;br /&gt;
 Upgrade-Insecure-Requests: 1&lt;br /&gt;
 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36&lt;br /&gt;
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8&lt;br /&gt;
 Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4&lt;br /&gt;
 Cookie: lang=de&lt;br /&gt;
 Connection: close  &lt;br /&gt;
In order to mimic the behavior of a real person calling the website HTTPMOD should copy all necessary steps. Host, Cookie and the GET-command are usually necessary. The same cannot be said of the User-Agent because the website can be called from any mobile or desktop computer. &lt;br /&gt;
&lt;br /&gt;
Then, the speedport will answer with a command that consists of several lines. By going through every line for every step in the login procedure one will finally arrive at the information that is necessary to successfully enter the login of the speedport (in case of the W724V, for example, it is necessary to copy a token called _httoken and to include the referer).&lt;br /&gt;
&lt;br /&gt;
== Advanced configuration to define a &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; command and send data to a device ==&lt;br /&gt;
       &lt;br /&gt;
When a set option is defined by attributes, the module will use the value given to the set command and integrate it into an HTTP-Request that sends the value to the device. The definitions for URL, headers and post data can contain the placeholder $val which will be replaced by the (numerical) value given to the set command.&lt;br /&gt;
&lt;br /&gt;
This can be as simple as:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
# No cyclic requests and no main URL needed in this example&lt;br /&gt;
define MyDevice HTTPMOD none 0&lt;br /&gt;
&lt;br /&gt;
attr MyDevice set01Name Licht&lt;br /&gt;
attr MyDevice set01URL http://192.168.1.22/switch=$val&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A user command &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
set MyDevice Licht 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will be translated into the http GET request&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
http://192.168.1.22/switch=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this example a map would also be helpful, that translates on / off to 0 or 1 and allows the user to select on/of in fhemweb:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr MyDevive set01IMap 0:off, 1:on&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This also provides input validation to make sure that only on and off can be used with the set command.&lt;br /&gt;
&lt;br /&gt;
In more complex Scenarios you might need to login before sending a command and the Login might create a session id that has to be part of further requests either in the URL, in headers or in the post data.&lt;br /&gt;
&lt;br /&gt;
Extension to the above example for a PoolManager 5 where a set needs a session id in the URL and the values have to be passed in JSON strings as post data:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM set01Name HeizungSoll&lt;br /&gt;
attr PM set01URL http://MyPoolManager/cgi-bin/webgui.fcgi?sid=$sid&lt;br /&gt;
attr PM set01Hint 6,10,20,30&lt;br /&gt;
attr PM set01Min 6&lt;br /&gt;
attr PM set01Max 30&lt;br /&gt;
attr PM setHeader1 Content-Type: application/json&lt;br /&gt;
attr PM set01Data {&amp;quot;set&amp;quot; :{&amp;quot;34.3118.value&amp;quot; :&amp;quot;$val&amp;quot; }}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example defines a set option with the name HeizungSoll.&lt;br /&gt;
By issuing &amp;lt;code&amp;gt;set PM HeizungSoll 10&amp;lt;/code&amp;gt; in FHEM, the value 10 will be sent in the defined HTTP&lt;br /&gt;
Post to URL &amp;lt;code&amp;gt;http://MyPoolManager/cgi-bin/webgui.fcgi&amp;lt;/code&amp;gt; in the Post Data as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;set&amp;quot; :{&amp;quot;34.3118.value&amp;quot; :&amp;quot;10&amp;quot; }}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The optional attributes set01Min and set01Max define input validations that will be checked in the set function. &lt;br /&gt;
The optional attribute set01Hint will define a selection list for the FHEMweb GUI.&lt;br /&gt;
&lt;br /&gt;
The HTTP response to such a request will be ignored unless you specify the attribute &amp;lt;code&amp;gt;setParseResponse&amp;lt;/code&amp;gt; &lt;br /&gt;
for all set commands or &amp;lt;code&amp;gt;set01ParseResponse&amp;lt;/code&amp;gt; for the set command with number 01.&lt;br /&gt;
If the HTTP response to a set command is parsed then this is done like the parsing of responses to get commands and you can use the attributes ending e.g. on Format, Encode, Decode, OMap and OExpr to manipulate / format the values read.&lt;br /&gt;
&lt;br /&gt;
If a parameter to a set command is not numeric but should be passed on to the device as text, then you can specify the attribute setTextArg. For example: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM set01TextArg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If a set command should not require a parameter at all, then you can specify the attribute NoArg. For example: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM set03Name On&lt;br /&gt;
attr PM set03NoArg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example defines the set of parameters for Wiesemann&amp;amp;Theiss Web-IO Analog 4.0 #57761. The current limit is set to 16mA (setxxMax)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod WuT_Analog1 HTTPMOD none 30&lt;br /&gt;
attr WuT_Analog1 room W&amp;amp;T&lt;br /&gt;
attr WuT_Analog1 set01Max 16&lt;br /&gt;
attr WuT_Analog1 set01Min 0&lt;br /&gt;
attr WuT_Analog1 set01Name Output1&lt;br /&gt;
attr WuT_Analog1 set01URL http://192.xx.My_IP/outputaccess1?PW=MyPassword&amp;amp;State=$val&amp;amp;&lt;br /&gt;
attr WuT_Analog1 set02Max 16&lt;br /&gt;
attr WuT_Analog1 set02Min 0&lt;br /&gt;
attr WuT_Analog1 set02Name Output2&lt;br /&gt;
attr WuT_Analog1 set02URL http://192.xx.My_IP/outputaccess2?PW=MyPassword&amp;amp;State=$val&amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Advanced configuration to create a valid session id that might be necessary ==&lt;br /&gt;
In simple cases logging in works with basic authentication. In the case HTTPMOD accepts a username and password as part of the URL in the form &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
http://User:Password@192.168.1.18/something&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
However basic auth is seldom used. If you need to fill in a username and password in a HTML form and the session is then managed by a session id, here is how to configure this:&lt;br /&gt;
&lt;br /&gt;
when sending data to an HTTP-Device in a set, HTTPMOD will replace any &amp;lt;code&amp;gt;$sid&amp;lt;/code&amp;gt; in the URL, Headers and Post data with the internal &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt;. To authenticate towards the device and give this internal a value, you can use an optional multi step login procedure defined by the following attributes: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;sid[0-9]*URL&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;sid[0-9]*Data.*&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;sid[0-9]*Header.*&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;sid[0-9]*IgnoreRedirects&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;idRegex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;idJSON&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;idXPath&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;idXPath-Strict&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set|sid)[0-9]*IdRegex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set|sid)[0-9]*IdJSON&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set|sid)[0-9]*IdXPath&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set|sid)[0-9]*IdXPath-Strict&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each step can have a URL, Headers, Post Data pieces and a Regex to extract a resulting Session ID into &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt;.&lt;br /&gt;
HTTPMOD will create a sorted list of steps (the numbers between sid and URL / Data / Header) and the loop through these steps and send the corresponding requests to the device. For each step a $sid in a Header or Post Data will be replaced with the current content of &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Using this feature, HTTPMOD can perform a forms based authentication and send user name, password or other necessary data to the device and save the session id for further requests.&lt;br /&gt;
        &lt;br /&gt;
To determine when this login procedure is necessary, HTTPMOD will first try to send a request without &lt;br /&gt;
doing the login procedure. If the result contains an error that authentication is necessary, then a login is performed. &lt;br /&gt;
To detect such an error in the HTTP response, you can again use a regular expression, JSON or XPath, this time with the attributes &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;reAuthRegex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;reAuthJSON&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;reAuthXPath&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;reAuthXPath-Strict&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set)[0-9]*ReAuthRegex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set)[0-9]*ReAuthJSON&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set)[0-9]*ReAuthXPath&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set)[0-9]*ReAuthXPath-Strict&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
reAuthJSON or reAuthXPath typically only extract one piece of data from a response. &lt;br /&gt;
If the existence of the specified piece of data is sufficent to start a login procedure, then nothing more needs to be defined to detect this situation. &lt;br /&gt;
If however the indicator is a status code that contains different values depending on a successful request and a failed request if a new authentication is needed, &lt;br /&gt;
then you can combine things like reAuthJSON with reAuthRegex. In this case the regex is only matched to the data extracted by JSON (or XPath). &lt;br /&gt;
This way you can easily extract the status code using JSON parsing and then specify the code that means &amp;quot;authentication needed&amp;quot; as a regular expression.&lt;br /&gt;
        &lt;br /&gt;
If for one step not all of the URL, Data or Header Attributes are set, then HTTPMOD tries to use a &lt;br /&gt;
&amp;lt;code&amp;gt;sidURL&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sidData.*&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;sidHeader.*&amp;lt;/code&amp;gt; Attribute (without the step number after sid). This way parts that are the same for all steps don&#039;t need to be defined redundantly.&lt;br /&gt;
&lt;br /&gt;
=== Example for a multi step login procedure: ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM reAuthRegex /html/dummy_login.htm &lt;br /&gt;
attr PM sidURL http://192.168.70.90/cgi-bin/webgui.fcgi?sid=$sid&lt;br /&gt;
attr PM sidHeader1 Content-Type: application/json&lt;br /&gt;
attr PM sid1IDRegex wui.init\(&#039;([^&#039;]+)&#039;&lt;br /&gt;
attr PM sid2Data {&amp;quot;set&amp;quot; :{&amp;quot;9.17401.user&amp;quot; :&amp;quot;fhem&amp;quot; ,&amp;quot;9.17401.pass&amp;quot; :&amp;quot;password&amp;quot; }}&lt;br /&gt;
attr PM sid3Data {&amp;quot;set&amp;quot; :{&amp;quot;35.5062.value&amp;quot; :&amp;quot;128&amp;quot; }}&lt;br /&gt;
attr PM sid4Data {&amp;quot;set&amp;quot; :{&amp;quot;42.8026.code&amp;quot; :&amp;quot;pincode&amp;quot; }}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case HTTPMOD detects that a login is necessary by looking for the pattern /html/dummy_login.htm in the HTTP response. &lt;br /&gt;
If it matches, it starts a login sequence. In the above example all steps request the same URL. In step 1 only the defined Header is sent in an HTTP get request. The response will contain a session id that is extraced with the regex wui.init\(&#039;([^&#039;]+)&#039;.&lt;br /&gt;
&lt;br /&gt;
In the next step this session id is sent in a post request to the same URL where tha post data contains a username and password. The a third and a fourth request follow that set a value and a code. The result will be a valid and authorized session id that can be used in other requests where $sid is part of a URL, header or post data and will be replaced with the session id extracted above.&lt;br /&gt;
&lt;br /&gt;
===Another Practical Example reading the SOC from a Renault Zoe===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define myZoe HTTPMOD https://www.services.renault-ze.com/api/vehicle/VINVINVIN/battery 7000&lt;br /&gt;
attr myZoe userattr reading01JSON reading01Name reading02JSON reading02Name reading03JSON reading03Nam\&lt;br /&gt;
e reading04JSON reading04Name reading05JSON reading05Name requestHeader02 sid1Data sid1Header01 sid1Id\&lt;br /&gt;
JSON sid1IgnoreRedirects:0,1 sid1URL&lt;br /&gt;
attr myZoe reAuthJSON message&lt;br /&gt;
attr myZoe reading01JSON charge_level&lt;br /&gt;
attr myZoe reading01Name SOC&lt;br /&gt;
attr myZoe reading02JSON charging&lt;br /&gt;
attr myZoe reading02Name charging&lt;br /&gt;
attr myZoe reading03JSON plugged&lt;br /&gt;
attr myZoe reading03Name plugged&lt;br /&gt;
attr myZoe reading04JSON remaining_range&lt;br /&gt;
attr myZoe reading04Name remaining_range&lt;br /&gt;
attr myZoe reading05JSON last_update&lt;br /&gt;
attr myZoe reading05Name last_update&lt;br /&gt;
attr myZoe requestHeader02 Authorization: Bearer $sid&lt;br /&gt;
attr myZoe sid1Data {&amp;quot;username&amp;quot;:&amp;quot;ZEUSERNAME&amp;quot;,&amp;quot;password&amp;quot;:&amp;quot;ZEPASSWORD&amp;quot;}&lt;br /&gt;
attr myZoe sid1Header01 Content-Type: application/json&lt;br /&gt;
attr myZoe sid1IdJSON token&lt;br /&gt;
attr myZoe sid1IgnoreRedirects 1&lt;br /&gt;
attr myZoe sid1URL https://www.services.renault-ze.com/api/user/login&lt;br /&gt;
attr myZoe timeout 10&lt;br /&gt;
#attr myZoe verbose 7&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The $sid is read by logging in with username and password from JSON {&amp;quot;token&amp;quot;: ..} after detecting an error {&amp;quot;message&amp;quot;: ..}  from a failed request. This example offers no solution for the reading of the VIN from the login request, which in turn is part of the reading URL. It should be possible to read more than just the $sid from the login sequence.&lt;br /&gt;
&lt;br /&gt;
In the special case where a session id is set as a HTTP-Cookie (with the header Set-cookie: in the HTTP response) HTTPMOD offers an even simpler way. With the attribute enableCookies a very basic cookie handling mechanism is activated that stores all cookies that the server sends to the HTTPMOD device and puts them back as cookie headers in the following requests.&lt;br /&gt;
&lt;br /&gt;
For such cases no sidIdRegex and no $sid in a user defined header is necessary.&lt;br /&gt;
&lt;br /&gt;
== Advanced configuration to define a &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; and request additional data with its own request from a device ==&lt;br /&gt;
&lt;br /&gt;
The normal automatic HTTP request that is done repeatedly after the defined interval has elapsed works well in cases where all required readings can be requested in one common HTTP request. If however a device needs individual requests with different URLs or different POST data for each value, then another method is necessary. &lt;br /&gt;
For such cases a &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; option can be defined and the user can either issue FHEM &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; commands each time he needs the reading or the user can set an attribute to request the reading automatically together with the normal iteration.&lt;br /&gt;
For each &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; option attributes define an individual URL, optional headers, and post data as well as individual regular expressions and formatting options. &lt;br /&gt;
&lt;br /&gt;
When a get option is defined by attributes, the module allows querying additional values from the device that require individual HTTP-Requests or special parameters to be sent&lt;br /&gt;
&lt;br /&gt;
Extension to the above example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM get01Name MyGetValue &lt;br /&gt;
attr PM get01URL http://MyPoolManager/cgi-bin/directory/webgui.fcgi?special=1?sid=$sid &lt;br /&gt;
attr PM getHeader1 Content-Type: application/json &lt;br /&gt;
attr PM get01Data {&amp;quot;get&amp;quot; :{&amp;quot;30.1234.value&amp;quot;}} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example defines a get option with the name MyGetValue.&lt;br /&gt;
By issuing &amp;lt;code&amp;gt;get PM MyGetValue&amp;lt;/code&amp;gt; in FHEM, the defined HTTP request is sent to the device.&lt;br /&gt;
The HTTP response is then parsed using the same readingXXName and readingXXRegex attributes as above so&lt;br /&gt;
additional pairs will probably be needed there for additional values.&lt;br /&gt;
&lt;br /&gt;
if you prefer to define the parsing and formatting of readings individually per get command, you can use &lt;br /&gt;
attributes like get01Regex, get01XPath, get01Format, get01OMap and so on just like for reading01...&lt;br /&gt;
&lt;br /&gt;
You can also include parameters / values that are passed to the get command in the request just like for set commands.&lt;br /&gt;
The placeholder $val will be replaced with the value given to the get command or you can specify your own replacement as described above.&lt;br /&gt;
&lt;br /&gt;
If the new get parameter should also be queried regularly, you can define the following optional attributes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM get01Poll 1&lt;br /&gt;
attr PM get01PollDelay 300&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first attribute includes this reading in the automatic update cycle and the second defines an alternative lower update frequency. When the interval defined initially in the define is over and the normal readings are read from the device, the update function will check for additional get parameters that should be included in the update cycle.&lt;br /&gt;
&lt;br /&gt;
If a PollDelay is specified for a get parameter, the update function also checks if the time passed since it has last read this value is more than the given PollDelay. If not, this reading is skipped and it will be rechecked in the next cycle when interval is over again. So the effective PollDelay will always be a multiple of the interval specified in the initial define.&lt;br /&gt;
&lt;br /&gt;
Please note that each defined get command that is included in the regular update cycle will create its own HTTP request. So if you want to extract several values from the same request, it is much more efficient to do this by defining readingXXName and readingXXRegex, XPath or JSON attributes and to specify an interval and a URL in the define of the HTTPMOD device. &lt;br /&gt;
&lt;br /&gt;
Example for a Siemens webserver provided by Lanhydrock:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define ozw672 HTTPMOD https://192.168.178.8/api/auth/login.json?user=test&amp;amp;pwd=test 300&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get1Name tempAussen&lt;br /&gt;
attr ozw672 get1URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1960&lt;br /&gt;
attr ozw672 get1Poll 1&lt;br /&gt;
attr ozw672 get1PollDelay 1800&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get2Name tempAussenGemischt&lt;br /&gt;
attr ozw672 get2URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1964&lt;br /&gt;
attr ozw672 get2Poll 1&lt;br /&gt;
attr ozw672 get2PollDelay 1800&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get3Name tempTWW&lt;br /&gt;
attr ozw672 get3URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1996&lt;br /&gt;
attr ozw672 get3Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get4Name tempKesselSoll&lt;br /&gt;
attr ozw672 get4URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1910&lt;br /&gt;
attr ozw672 get4Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get5Name tempKesselRuecklauf&lt;br /&gt;
attr ozw672 get5URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1915&lt;br /&gt;
attr ozw672 get5Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get6Name tempKesselRuecklaufSoll&lt;br /&gt;
attr ozw672 get6URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1916&lt;br /&gt;
attr ozw672 get6Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get7Name anzahlStartsBrenner&lt;br /&gt;
attr ozw672 get7URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1927&lt;br /&gt;
attr ozw672 get7PollDelay 1800&lt;br /&gt;
attr ozw672 get7Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get8Name statusKessel&lt;br /&gt;
attr ozw672 get8URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1898&lt;br /&gt;
attr ozw672 get8Poll 1&lt;br /&gt;
attr ozw672 get8Regex Value&amp;quot;: &amp;quot;([a-zA-Zü ]*)&amp;quot;&lt;br /&gt;
attr ozw672 get8OMap Aus:0, Nachlauf aktiv:5, Freigegeben für TWW:10, Freigegeben für HK:20, In Teillastbetrieb für TWW:40, In Teillastbetrieb für HK:50, In Betrieb für Trinkwasser:90, In Betrieb für Heizkreis:100&lt;br /&gt;
&lt;br /&gt;
attr ozw672 getRegex Value&amp;quot;: &amp;quot;[ ]*([-.0-9]*)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attr ozw672 reAuthRegex .*session not valid.*&lt;br /&gt;
attr ozw672 sid1IDRegex .*&amp;quot;(.*-.*-.*-[0-9a-z]*).*&lt;br /&gt;
attr ozw672 sid1URL https://192.168.178.8/api/auth/login.json?user=test&amp;amp;pwd=test&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== All attributes ==&lt;br /&gt;
;reading[0-9]+Name&lt;br /&gt;
:the name of a reading to extract with the corresponding readingRegex, readingJSON, readingXPath or readingXPath-Strict&lt;br /&gt;
:Please note that the old syntax &amp;lt;b&amp;gt;readingsName.*&amp;lt;/b&amp;gt; does not work with all features of HTTPMOD and should be avoided. It might go away in a future version of HTTPMOD.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]+Name&lt;br /&gt;
:Name of a get or set command&lt;br /&gt;
:If the HTTP response that is received after the command is parsed with an individual parse option then this name is also used as a reading name. Please note that no individual parsing needs to be defined for a get or set. If no regex, XPath or JSON is specified for the command, then HTTPMOD will try to parse the response using all the defined readingRegex, reading XPath or readingJSON attributes.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+Regex&lt;br /&gt;
:If this attribute is specified, the Regex defined here is used to extract the value from the HTTP Response and assign it to a Reading with the name defined in the (get|set|reading)[0-9]+Name attribute.&lt;br /&gt;
:If this attribute is not specified for an individual Reading or get or set but without the numbers in the middle, e.g. as getRegex or readingRegex, then it applies to all the other readings / get / set commands where no specific Regex is defined.&amp;lt;br&amp;gt;&lt;br /&gt;
:The value to extract should be in a capture group / sub expression e.g. ([\d\.]+) in the above example. Multiple capture groups will create multiple readings (see explanation above)&lt;br /&gt;
:Using this attribute for a set command (setXXRegex) only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
:Please note that the old syntax &amp;lt;code&amp;gt;readingsRegex.*&amp;lt;/code&amp;gt; does not work with all features of HTTPMOD and should be avoided. It might go away in a future version of HTTPMOD.&lt;br /&gt;
:If for get or set commands neither a generic Regex attribute without numbers nor a specific (get|set)[0-9]+Regex attribute is specified and also no XPath or JSON parsing specification is given for the get or set command, then HTTPMOD tries to use the parsing definitions for general readings defined in reading[0-9]+Name, reading[0-9]+Regex or XPath or JSON attributes and assigns the Readings that match here.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+RegOpt&lt;br /&gt;
:Lets the user specify regular expression modifiers. For example if the same regular expression should be matched as often as possible in the HTTP response, then you can specify RegOpt g which will case the matching to be done as /regex/g&lt;br /&gt;
:The results will be trated the same way as multiple capture groups so the reading name will be extended with -number. &lt;br /&gt;
:For other possible regular expression modifiers see http://perldoc.perl.org/perlre.html#Modifiers&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+XPath&lt;br /&gt;
:defines an xpath to one or more values when parsing HTML data (see examples above)&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;get|set|reading[0-9]+XPath-Strict&lt;br /&gt;
:defines an xpath to one or more values when parsing XML data (see examples above)&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+AutoNumLen&lt;br /&gt;
:In cases where a regular expression or an XPath results in multiple results and these results are stored in a common reading name with extension -number, then you can modify the format of this number to have a fixed length with leading zeros. AutoNumLen 3 for example will lead to reading names ending with -001 -002 and so on.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+AlwaysNum&lt;br /&gt;
:if set to 1 this attributes forces reading names to end with a -1, -01 (depending on the above described AutoNumLen) even if just one value is parsed.&lt;br /&gt;
&lt;br /&gt;
;get|set|reading[0-9]+JSON&lt;br /&gt;
:defines a path to the JSON object wanted by concatenating the object names. See the above example.&lt;br /&gt;
:If you don&#039;t know the paths, then start by using extractAllJSON and the use the names of the readings as values for the JSON attribute.&amp;lt;br&amp;gt;&lt;br /&gt;
:Please don&#039;t forget to also specify a name for a reading, get or set. &lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]*RecombineExpr&lt;br /&gt;
:defines an expression that is used in an eval to compute one reading value out of the list of matches.&lt;br /&gt;
:This is supposed to be used for regexes or xpath specifications that produce multiple results if only one result that combines them is wanted. The list of matches will be in the variable @matchlist.&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;get[0-9]*CheckAllReadings&lt;br /&gt;
:this attribute modifies the behavior of HTTPMOD when the HTTP Response of a get command is parsed. &amp;lt;br&amp;gt;&lt;br /&gt;
:If this attribute is set to 1, then additionally to the matching of the corresponding get specific regex (get[0-9]*Regex), XPath or JSON attribute also all the reading names and parse definitions defined in Reading[0-9]+Name and Reading[0-9]+Regex, XPath or JSON attributes are checked and if they match, the coresponding Readings are assigned as well.&lt;br /&gt;
:Please note that this does not mean that get01CheckAllReadings will cause a get02Regex to be used. Only the corresponding get01Regex will be used but additionally all the readingXYRegex attributes.&lt;br /&gt;
:This is automatically done if a get or set command is defined without its own parse attributes.&lt;br /&gt;
&lt;br /&gt;
;(get|reading)[0-9]*OExpr&lt;br /&gt;
:defines an optional expression that is used in an eval to compute / format a readings value after parsing an HTTP response&lt;br /&gt;
:The raw value from the parsing will be in the variable $val.&lt;br /&gt;
:If specified as readingOExpr then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*Expr.&lt;br /&gt;
:Please note that the old syntax &amp;lt;b&amp;gt;readingsExpr.*&amp;lt;/b&amp;gt; does not work with all features of HTTPMOD and should be avoided. It might go away in a future version of HTTPMOD.&lt;br /&gt;
&lt;br /&gt;
;(get|reading)[0-9]*Expr&lt;br /&gt;
:This is the old syntax for (get|reading)[0-9]*OExpr. It should be replaced by (get|reading)[0-9]*OExpr. The set command upgradeAttributes which becomes visible when the attribute enableControlSet is set to 1, can do this renaming automatically.&lt;br /&gt;
&lt;br /&gt;
;(get|reading)[0-9]*OMap&lt;br /&gt;
:Map that defines a mapping from raw value parsed to visible values like &amp;quot;0:mittig, 1:oberhalb, 2:unterhalb&amp;quot;.&lt;br /&gt;
:If specified as readingOMap then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*Map.&amp;lt;br&amp;gt;&lt;br /&gt;
:The individual options in a map are separated by a komma and an optional space. Spaces are allowed to appear in a visible value however kommas are not possible.&lt;br /&gt;
&lt;br /&gt;
;(get|reading)[0-9]*Map&lt;br /&gt;
:This is the old syntax for (get|reading)[0-9]*OMap. It should be replaced by (get|reading)[0-9]*OMap. The set command upgradeAttributes which becomes visible when the attribute enableControlSet is set to 1, can do this renaming automatically.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]*Format&lt;br /&gt;
:Defines a format string that will be used in sprintf to format a reading value.&lt;br /&gt;
:If specified without the numbers in the middle e.g. as readingFormat then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*Format.&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]*Decode&lt;br /&gt;
:defines an encoding to be used in a call to the perl function decode to convert the raw data string read from the device to a reading. &lt;br /&gt;
:This can be used if the device delivers strings in an encoding like cp850 instead of utf8.&lt;br /&gt;
:If your reading values contain Umlauts and they are shown as strange looking icons then you probably need to use this feature.&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]*Encode&lt;br /&gt;
:defines an encoding to be used in a call to the perl function encode to convert the raw data string read from the device to a reading. &lt;br /&gt;
:This can be used if the device delivers strings in an encoding like cp850 and after decoding it you want to reencode it to e.g. utf8.&lt;br /&gt;
:If your reading values contain Umlauts and they are shown as strange looking icons then you probably need to use this feature.&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*URL&lt;br /&gt;
:URL to be requested for the set or get command. If this option is missing, the URL specified during define will be used.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*Data&lt;br /&gt;
:Data to be sent to the device as POST data when the get or set command is executed. if this attribute is not specified, an HTTP GET method will be used instead of an HTTP POST&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*NoData&lt;br /&gt;
:can be used to override a more generic attribute that specifies POST data for all get or set commands. With NoData no data is sent and therefor the request will be an HTTP GET.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*Header.*&lt;br /&gt;
:HTTP Headers to be sent to the device when the set is executed&lt;br /&gt;
&lt;br /&gt;
;requestHeader.*&lt;br /&gt;
:Define an optional additional HTTP Header to set in the HTTP request of the main loop&lt;br /&gt;
&lt;br /&gt;
;requestData&lt;br /&gt;
:optional POST Data to be sent in the request of the main loop. If not defined, it will be an HTTP GET request as defined in HttpUtils which is used by this module&lt;br /&gt;
&lt;br /&gt;
;get[0-9]+Poll&lt;br /&gt;
:if set to 1 the get is executed automatically during the normal update cycle (after the interval provided in the define command has elapsed)&lt;br /&gt;
&lt;br /&gt;
;get[0-9]+PollDelay&lt;br /&gt;
:if the value should not be read in each iteration (after the interval given to the define command), then a minimum delay can be specified with this attribute. This has only an effect if the above Poll attribute has also been set. Every time the update function is called, it checks if since this get has been read the last time, the defined delay has elapsed. If not, then it is skipped this time.&lt;br /&gt;
:PollDelay can be specified as seconds or as x[0-9]+ which means a multiple of the interval in the define command.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*TextArg&lt;br /&gt;
:For a get command this defines that the command accepts a text value after the option name. By default a get command doesn&#039;t accept optional values after the command name. &lt;br /&gt;
:If TextArg is specified and a value is passed after the get name then this value can then be used in a request URL, header or data as replacement for $val or in a user defined replacement that uses the internal &amp;quot;value&amp;quot; ($hash-&amp;gt;{value}).&lt;br /&gt;
:If used for a set command then it defines that the value to be set doesn&#039;t require any validation / conversion. &lt;br /&gt;
:The raw value is passed on as text to the device. By default a set command expects a numerical value or a text value that is converted to a numeric value using a map.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Min&lt;br /&gt;
:Minimum value for input validation. &lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Max&lt;br /&gt;
:Maximum value for input validation. &lt;br /&gt;
&lt;br /&gt;
;set[0-9]+IExpr&lt;br /&gt;
:Perl Expression to compute the raw value to be sent to the device from the input value passed to the set.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Expr&lt;br /&gt;
:This is the old syntax for (get|reading)[0-9]*IExpr. It should be replaced by (get|reading)[0-9]*IExpr. The set command upgradeAttributes which becomes visible when the attribute enableControlSet is set to 1, can do this renaming automatically.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+IMap&lt;br /&gt;
:Map that defines a mapping from raw to input values like &amp;quot;0:mittig, 1:oberhalb, 2:unterhalb&amp;quot;. This attribute atomatically creates a hint for FHEMWEB so the user can choose one of the input values.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Map&lt;br /&gt;
:This is the old syntax for (get|reading)[0-9]*IMap. It should be replaced by (get|reading)[0-9]*IMap. The set command upgradeAttributes which becomes visible when the attribute enableControlSet is set to 1, can do this renaming automatically.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Hint&lt;br /&gt;
:Explicit hint for fhemWEB that will be returned when set ? is seen. Can be used to get a slider or a list of values to choose from.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]*NoArg&lt;br /&gt;
:Defines that this set option doesn&#039;t require arguments. It allows sets like &amp;quot;on&amp;quot; or &amp;quot;off&amp;quot; without further values.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]*ParseResponse&lt;br /&gt;
:defines that the HTTP response to the set will be parsed as if it was the response to a get command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*URLExpr&lt;br /&gt;
:Defines a Perl expression to specify the HTTP Headers for this request. This overwrites any other header specification and should be used carefully only if needed. The original Header is availabe as $old. Typically this feature is not needed and it might go away in future versions of HTTPMOD. Please use the &amp;quot;replacement&amp;quot; attributes if you want to pass additional variable data to a web service. &lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*DatExpr&lt;br /&gt;
:Defines a Perl expression to specify the HTTP Post data for this request. This overwrites any other post data specification and should be used carefully only if needed. The original Data is availabe as $old. Typically this feature is not needed and it might go away in future versions of HTTPMOD. Please use the &amp;quot;replacement&amp;quot; attributes if you want to pass additional variable data to a web service. &lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*HdrExpr&lt;br /&gt;
:Defines a Perl expression to specify the URL for this request. This overwrites any other URL specification and should be used carefully only if needed. The original URL is availabe as $old. Typically this feature is not needed and it might go away in future versions of HTTPMOD. Please use the &amp;quot;replacement&amp;quot; attributes if you want to pass additional variable data to a web service.           &lt;br /&gt;
&lt;br /&gt;
;ReAuthRegex&lt;br /&gt;
:regular Expression to match an error page indicating that a session has expired and a new authentication for read access needs to be done. &lt;br /&gt;
:This attribute only makes sense if you need a forms based authentication for reading data and if you specify a multi step login procedure based on the sid.. attributes.&lt;br /&gt;
:This attribute is used for all requests. For set and get operations you can however specify individual reAuthRegexes with the (get|set)[0-9]*ReAuthRegex attributes.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*ReAuthRegex&lt;br /&gt;
:Regex that will detect when a session has expired during a set operation and a new login needs to be performed.&lt;br /&gt;
:It works like the global reAuthRegex but is used for set operations.&lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*URL&lt;br /&gt;
:different URLs or one common URL to be used for each step of an optional login procedure. &lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*IDRegex&lt;br /&gt;
:different Regexes per login procedure step or one common Regex for all steps to extract the session ID from the HTTP response&lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*Data.*&lt;br /&gt;
:data part for each step to be sent as POST data to the corresponding URL&lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*Header.*&lt;br /&gt;
:HTTP Headers to be sent to the URL for the corresponding step&lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*IgnoreRedirects&lt;br /&gt;
:Tells the HttpUtils to not follow HTTP Redirects for this Request. Might be needed for some devices that set a session cookie within a 303 Redirect.&lt;br /&gt;
&lt;br /&gt;
;clearSIdBeforeAuth&lt;br /&gt;
:will set the session id to &amp;quot;&amp;quot; before doing the authentication steps&lt;br /&gt;
&lt;br /&gt;
;authRetries&lt;br /&gt;
:number of retries for authentication procedure - defaults to 1&lt;br /&gt;
&lt;br /&gt;
;replacement[0-9]*Regex&lt;br /&gt;
:Defines a replacement to be applied to an HTTP request header, data or URL before it is sent. This allows any part of the request to be modified based on a reading, an internal or an expression.&lt;br /&gt;
:The regex defines which part of a header, data or URL should be replaced. The replacement is defined with the following attributes:&lt;br /&gt;
&lt;br /&gt;
;replacement[0-9]*Mode&lt;br /&gt;
:Defines how the replacement should be done and what replacementValue means. Valid options are text, reading, internal and expression.&lt;br /&gt;
&lt;br /&gt;
;replacement[0-9]*Value&lt;br /&gt;
:Defines the replacement. If the corresponding replacementMode is &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;, then value is a static text that is used as the replacement.&lt;br /&gt;
:If replacementMode is &amp;lt;code&amp;gt;reading&amp;lt;/code&amp;gt; then Value can be the name of a reading of this device or it can be a reading of a different device referred to by devicename:reading.&lt;br /&gt;
:If replacementMode is &amp;lt;code&amp;gt;internal&amp;lt;/code&amp;gt; the Value can be the name of an internal of this device or it can be an internal of a different device referred to by devicename:internal.&lt;br /&gt;
:If replacementMode is &amp;lt;code&amp;gt;expression&amp;lt;/code&amp;gt; the the Value is treated as a Perl expression that computes the replacement value. The expression can use $1, $2 and so on to refer to capture groups of the corresponding regex that is matched against the original URL, header or post data.&lt;br /&gt;
:If replacementMode is &amp;lt;code&amp;gt;key&amp;lt;/code&amp;gt; then the module will use a value from a key / value pair that is stored in an obfuscated form in the file system with the set storeKeyValue command. This might be useful for storing passwords.&lt;br /&gt;
&lt;br /&gt;
;[gs]et[0-9]*Replacement[0-9]*Value&lt;br /&gt;
:This attribute can be used to override the replacement value for a specific get or set.&lt;br /&gt;
&lt;br /&gt;
;get|reading[0-9]*MaxAge&lt;br /&gt;
:Defines how long a reading is valid before it is automatically overwritten with a replacement when the read function is called the next time.&lt;br /&gt;
&lt;br /&gt;
;get|reading[0-9]*MaxAgeReplacement&lt;br /&gt;
:specifies the replacement for MaxAge - either as a static text or as a perl expression.&lt;br /&gt;
&lt;br /&gt;
;get|reading[0-9]*MaxAgeReplacementMode&lt;br /&gt;
:specifies how the replacement is interpreted: can be text, expression and delete.&lt;br /&gt;
&lt;br /&gt;
;get|reading[0-9]*DeleteIfUnmatched&lt;br /&gt;
:If set to 1 this attribute causes certain readings to be deleted when the parsing of the website does not match the specified reading. Internally HTTPMOD remembers which kind of operation created a reading (update, Get01, Get02 and so on). Specified readings will only be deleted if the same operation does not parse this reading again. This is especially useful for parsing that creates several matches / readings and the number of matches can vary from request to request. For example if reading01Regex creates 4 readings in one update cycle and in the next cycle it only matches two times then the readings containing the remaining values from the last round will be deleted.&lt;br /&gt;
:Please note that this mechanism will not work in all cases after a restart. Especially when a get definition does not contain its own parsing definition but ExtractAllJSON or relies on HTTPMOD to use all defined reading.* attributes to parse the responsee to a get command, old readings might not be deleted after a restart of fhem.&lt;br /&gt;
;get|reading[0-9]*DeleteOnError&lt;br /&gt;
:If set to 1 this attribute causes certain readings to be deleted when the website can not be reached and the HTTP request returns an error. Internally HTTPMOD remembers which kind of operation created a reading (update, Get01, Get02 and so on). Specified readings will only be deleted if the same operation returns an error.&lt;br /&gt;
The same restrictions as for DeleteIfUnmatched apply regarding a fhem restart.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;httpVersion&lt;br /&gt;
:defines the HTTP-Version to be sent to the server. This defaults to 1.0.&lt;br /&gt;
&lt;br /&gt;
;sslVersion&lt;br /&gt;
:defines the SSL Version for the negotiation with the server. The attribute is evaluated by HttpUtils. If it is not specified, HttpUtils assumes SSLv23:!SSLv3:!SSLv2&lt;br /&gt;
&lt;br /&gt;
;sslArgs&lt;br /&gt;
:defines a list that is converted to a key / value hash and gets passed to HttpUtils. To avoid certificate validation for broken servers you can for example specify &lt;br /&gt;
:&amp;lt;code&amp;gt;attr myDevice sslArgs SSL_verify_mode,SSL_VERIFY_NONE&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;alignTime&lt;br /&gt;
:Aligns each periodic read request for the defined interval to this base time. This is typcally something like 00:00 (see the FHEM at command)&lt;br /&gt;
&lt;br /&gt;
;noShutdown&lt;br /&gt;
:pass the noshutdown flag to HTTPUtils for webservers that need it (some embedded webservers only deliver empty pages otherwise)&lt;br /&gt;
&lt;br /&gt;
;disable&lt;br /&gt;
:stop doing automatic HTTP requests while this attribute is set to 1&lt;br /&gt;
&lt;br /&gt;
;enableControlSet&lt;br /&gt;
:enables the built in set commands interval, stop, start, reread, upgradeAttributes, storeKeyValue.&lt;br /&gt;
&lt;br /&gt;
;enableCookies&lt;br /&gt;
:enables the built in cookie handling if set to 1. With cookie handling each HTTPMOD device will remember cookies that the server sets and send them back to the server in the following requests. &lt;br /&gt;
:This simplifies session magamenet in cases where the server uses a session ID in a cookie. In such cases enabling cookies should be sufficient and no sidRegex and no manual definition of a cookie header should be necessary.&lt;br /&gt;
&lt;br /&gt;
;showMatched&lt;br /&gt;
:if set to 1 then HTTPMOD will create a reading with the name MATCHED_READINGS that contains the names of all readings that could be matched in the last request as well as UNMATCHED_READINGS and LAST_REQUEST.&lt;br /&gt;
&lt;br /&gt;
;showError&lt;br /&gt;
:if set to 1 then HTTPMOD will create a reading and event with the Name LAST_ERROR that contains the error message of the last error returned from HttpUtils. &lt;br /&gt;
&lt;br /&gt;
;removeBuf&lt;br /&gt;
:if set to 1 then HTTPMOD removes the internal named buf when a HTTP-response has been received. &lt;br /&gt;
:$hash-&amp;gt;{buf} is used internally be Fhem httpUtils and in some use cases it is desireable to remove this internal after reception &lt;br /&gt;
:because it contains a very long response which looks ugly in Fhemweb.&lt;br /&gt;
&lt;br /&gt;
;timeout&lt;br /&gt;
:time in seconds to wait for an answer. Default value is 2&lt;br /&gt;
&lt;br /&gt;
;queueDelay&lt;br /&gt;
:HTTP Requests will be sent from a queue in order to avoid blocking when several Requests have to be sent in sequence. This attribute defines the delay between calls to the function that handles the send queue. It defaults to one second.&lt;br /&gt;
&lt;br /&gt;
;queueMax&lt;br /&gt;
:Defines the maximum size of the send queue. If it is reached then further HTTP Requests will be dropped and not be added to the queue&lt;br /&gt;
&lt;br /&gt;
;minSendDelay&lt;br /&gt;
:Defines the minimum time between two HTTP Requests.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Example: This example uses many features of HTTPMOD, universally useful is the access to other readings just like what [[Set magic|set-magic]] (&amp;lt;code&amp;gt;[device:reading]&amp;lt;/code&amp;gt;) does [https://forum.fhem.de/index.php?topic=135666.0 🎸 Owntone (Musik-Player) mit FHEM steuern]&lt;br /&gt;
* Example: [https://www.goingelectric.de/forum/viewtopic.php?f=57&amp;amp;t=21462|Abfrage Akkustand einer Zoe und mehr von Renault ZE Services]&lt;br /&gt;
* Example: [[Wetter_und_Wettervorhersagen#Wetter_von_Weather_Underground|Extract weather information from WeatherUnderground]]&lt;br /&gt;
* Example: [[Pollenflug|Pollen count]]&lt;br /&gt;
* Example: [[HTTPMOD Beispielkonfiguration zur Anbindung einer Daikin Klimaanlage mit WLAN-Modul|Connect Daikin aircondition to FHEM]]&lt;br /&gt;
* Example: [[Go-eCharger|Extract information from go-eCharger]]&lt;br /&gt;
* Example: [[Sonnenspeicher|Integration of energy supplies from sonnen (https://sonnenbatterie.de/en/start)]]&lt;br /&gt;
* Example: [https://forum.fhem.de/index.php/topic,95989.msg915870.html#msg915870 Miele 3rd party API für Miele Smarthome Geräte]&lt;br /&gt;
* Example: [https://forum.fhem.de/index.php/topic,84215.msg918662.html#msg918662 Honeywell Evohome Totalconnect]&lt;br /&gt;
* Example: [https://forum.fhem.de/index.php/topic,78613.msg889015.html#msg889015 Tigo Energy Integration]&lt;br /&gt;
* Example: [https://forum.fhem.de/index.php/topic,78613.msg708518.html#msg708518 Ecowater]&lt;br /&gt;
* {{Link2Forum|Topic=17804|LinkText=Thread}} in FHEM Forum that discusses the first version of this module &lt;br /&gt;
* {{Link2Forum|Topic=29471|LinkText=Thread}} in FHEM Forum that discusses the second major version of this module &lt;br /&gt;
* {{Link2Forum|Topic=45176|LinkText=Thread}} in FHEM Forum that discusses the third major version of this module &lt;br /&gt;
* [http://perldoc.perl.org/perlretut.html Introduction to regular expressions]&lt;br /&gt;
* [http://portswigger.net/burp/ BurpSuite]: Tool (local proxy) to help analyze http traffic&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:IP Components]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=HTTPMOD&amp;diff=40085</id>
		<title>HTTPMOD</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=HTTPMOD&amp;diff=40085"/>
		<updated>2025-04-05T08:53:56Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Modul&lt;br /&gt;
|ModPurpose=Extract information from devices with an HTTP interface (or, more generic, from any URL) or send information to such devices &lt;br /&gt;
|ModType=d&lt;br /&gt;
|ModCmdRef=HTTPMOD&lt;br /&gt;
|ModForumArea=Sonstiges&lt;br /&gt;
|ModTechName=98_HTTPMOD.pm&lt;br /&gt;
|ModOwner=StefanStrobel ({{Link2FU|3960|Forum}} / [[Benutzer:StefanStrobel|Wiki]])&lt;br /&gt;
}}&lt;br /&gt;
[[HTTPMOD]] provides a generic way to retrieve information from devices with an HTTP Interface and store them in Readings or send information to such devices. It queries a given URL with Headers and data defined by attributes. &lt;br /&gt;
&lt;br /&gt;
From the HTTP response it extracts readings named in attributes using Regexes, JSON or XPath parsing also defined by attributes.&lt;br /&gt;
&lt;br /&gt;
In an advanced [[Konfiguration|configuration]] the module can also send information to devices. To do this, a generic &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; option can be configured using attributes. &lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
With the help of HTTPMOD you can automatically access websites, retrieve information and send data. In the simplest case, an http call is started regularly and the content of a page, for example, is read and processed further. The URL and the time interval in which the call is to be made must then be specified when the call is made. In order to read information from the called web pages, readings are required. In this readings one specifies Regex, which reads the necessary information. Here is an example.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define PM HTTPMOD  http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/-1/free.html 20000&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This creates a device that accesses the http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/-1/free.html website with the TV program every 20000 seconds. If neither readings nor other attributes are defined, then only the call of the website takes place and FHEM receives no further data from the web access.&lt;br /&gt;
&lt;br /&gt;
But now a television program is returned when you call the website, which you might want to read out. In order to save the contents in FHEM, which are related to the ARD, you need a reading. This reading must contain a name (so that FHEM knows under which name the information is stored) and a regex (so that you know where the information is stored on the website). Since usually not one, but many readings are defined, these readings are numbered consecutively. This is done by numbers that are given immediately after the word reading. In our example this would be for example&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM reading1Name ARD &lt;br /&gt;
attr PM reading1Regex &amp;lt;span class=&amp;quot;stationName&amp;quot;&amp;gt;ARD&amp;lt;\/span&amp;gt;[\w\W]*?&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;\s*&amp;lt;a[\w\W]*?&amp;gt;\s*(.*?)\s*&amp;lt;\/a&amp;gt; &lt;br /&gt;
attr PM reading2Name ARD_time &lt;br /&gt;
attr PM reading2Regex &amp;lt;span class=&amp;quot;stationName&amp;quot;&amp;gt;ARD&amp;lt;\/span&amp;gt;[\w\W]*?&amp;lt;td class=&amp;quot;time&amp;quot;&amp;gt;\s*(.*?)\s*&amp;lt; &lt;br /&gt;
attr PM reading3Name ARD_details &lt;br /&gt;
attr PM reading3Regex &amp;lt;span class=&amp;quot;stationName&amp;quot;&amp;gt;ARD&amp;lt;\/span&amp;gt;[\w\W]*?&amp;lt;div class=&amp;quot;content&amp;quot;&amp;gt;\s*&amp;lt;a[\w\W]*?&amp;gt;[\w\W]*?&amp;lt;\/a&amp;gt;\s*&amp;lt;br\/&amp;gt;\s*(.*?)(&amp;lt;img|&amp;lt;\/div) &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reading1 contains the programme, reading2 the time and reading3 further details.  The Regex result from the structure of the website and may have to be designed in painstaking detail.&lt;br /&gt;
&lt;br /&gt;
There are cases where this is not enough. For example, it could be the case that the information from the web access is not automated, but manually received. Then a 0 must first be entered at the top of the duration. But how does HTTPMOD then know when the data should be retrieved? This is done with get-commands. If a get-command is executed, HTTPMOD accesses the website, fetches the information and fills the corresponding readings. &lt;br /&gt;
&lt;br /&gt;
Again, it is possible to create not one but several get-commands. Therefore, get is always followed by (at least) one number with which the get commands are numbered.  Here is another example. The TV program should be called manually and not time-controlled. To do this we first change the definition to &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define PM HTTPMOD  http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/-1/free.html 0&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
and add the following to the attributes&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM get1Name ARDHolen &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
With the command &amp;quot;get PM ARDHolen&amp;quot; the above readings are filled. In order for FHEM to know that the other reading2, reading3, etc. are to be read as well as reading1, the (user) attribute  &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM get1CheckAllReadings 1 &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
needs to be defined.&lt;br /&gt;
&lt;br /&gt;
If the URL is not sufficient when the web page is called, but a special header is to be sent with it, this can be done with the get1Header attribute. If the header is multiline, the individual lines can be entered with get1Header1, get1Header2, and so on.    &lt;br /&gt;
&lt;br /&gt;
If you want to evaluate or control different URLs, you can define several get commands (get1, get2, etc.) and provide them with your own URLs. get1 usually reads only the reading1, get2 reads only the reading2 and so on. With CheckAllReadings all readings are evaluated simultaneously.&lt;br /&gt;
&lt;br /&gt;
If only data is to be sent on the web page, but not read, the set command is recommended. It has the same structure as the get-command.&lt;br /&gt;
&lt;br /&gt;
It becomes a bit more complex if the website requires a login check. The sid attributes are used for this purpose.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Availability, prerequisites and definition ==&lt;br /&gt;
This module is part of the regular FHEM distribution and uses the non blocking HTTP function &amp;lt;code&amp;gt;HttpUtils_NonblockingGet&amp;lt;/code&amp;gt; provided by FHEM&#039;s [[HttpUtils]] in a new version published in December 2013.&lt;br /&gt;
If not already installed in your environment, please [[update]] FHEM or install it manually using appropriate commands from your environment.&lt;br /&gt;
Please also note that FHEM HttpUtils need the global attribute dnsServer to be set in order to work really non blocking even when dns requests can not be answered.&lt;br /&gt;
&lt;br /&gt;
The device is defined as follows&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define &amp;lt;name&amp;gt; HTTPMOD &amp;lt;URL&amp;gt; &amp;lt;Interval&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The module connects to the given &amp;lt;code&amp;gt;URL&amp;lt;/code&amp;gt; every &amp;lt;code&amp;gt;Interval&amp;lt;/code&amp;gt; seconds, sends optional headers and data and then parses the response with regular expressions, xpath or json to set readings.&lt;br /&gt;
&lt;br /&gt;
URL can be &amp;quot;none&amp;quot; and Interval can be 0 if you prefer to only query data with a get command and not in a defined interval.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define PM HTTPMOD http://MyPoolManager/cgi-bin/webgui.fcgi 60&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Set-Commands ==&lt;br /&gt;
can be defined using attributes, see advanced configuration&lt;br /&gt;
&lt;br /&gt;
If you set the attribute enableControlSet to 1, the following additional built in set commands are available:&lt;br /&gt;
;interval&lt;br /&gt;
:set new interval time in seconds and restart the timer&lt;br /&gt;
;reread&lt;br /&gt;
:request the defined URL and try to parse it just like the automatic update would do it every Interval seconds without modifying the running timer.&lt;br /&gt;
;stop&lt;br /&gt;
:stop interval timer.&lt;br /&gt;
;start&lt;br /&gt;
:restart interval timer to call GetUpdate after interval seconds&lt;br /&gt;
;upgradeAttributes&lt;br /&gt;
:convert outdated attributes for older HTTPMOD-Versions that are still defined for this device from the old syntax to the new one.&lt;br /&gt;
:attributes with the description &amp;quot;this attribute should not be used anymore&amp;quot; or similar will be translated to the new syntax, e.g. readingsName1 to reading01Name.&lt;br /&gt;
;storeKeyValue&lt;br /&gt;
:stores a key value pair in an obfuscated form in the file system. Such values can then be used in replacements where the mode is &amp;quot;key&amp;quot; e.g. to avoid storing passwords in the configuration in clear text&lt;br /&gt;
&lt;br /&gt;
== Get-Commands ==&lt;br /&gt;
can be defined using attributes, see advanced configuration&lt;br /&gt;
&lt;br /&gt;
== simple Attributes ==&lt;br /&gt;
;enableControlSet&lt;br /&gt;
:enables the built in set commands &#039;&#039;interval&#039;&#039;, &#039;&#039;stop&#039;&#039;, &#039;&#039;start&#039;&#039;, &#039;&#039;reread&#039;&#039;, &#039;&#039;upgradeAttributes&#039;&#039;, &#039;&#039;storeKeyValue&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
;enableCookies&lt;br /&gt;
:enables the cookie handling inside HTTPMOD. It is advisable to always set this attribute and allow HTTPMOD to track the state of cookies and set them for following HTTP-requests&lt;br /&gt;
&lt;br /&gt;
;enforceGoodReadingNames&lt;br /&gt;
:makes sure that HTTPMOD only creates readings that are allowd for Fhem (especially if reading names are dynamically created from JSON object names with extractAllJSON. It is advisable to always set this attribute.&lt;br /&gt;
&lt;br /&gt;
;handleRedirects&lt;br /&gt;
:enables the redirect handling inside HTTPMOD which should be used together with the cookie handling of HTTPMOD. HTTPMOD will then automatically follow redirects from a web server and keep track of cookies at the same time. It is advisable to always set this attribute.&lt;br /&gt;
&lt;br /&gt;
;requestHeader.* &lt;br /&gt;
:Define an additional HTTP Header to set in the HTTP request&lt;br /&gt;
&lt;br /&gt;
;requestData&lt;br /&gt;
:POST Data to be sent in the request. If not defined, it will be a GET request as defined in HttpUtils used by this module&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+(-[0-9]+)?Name&lt;br /&gt;
:the name of a reading to extract with the corresponding readingRegex&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?OExpr&lt;br /&gt;
:defines an expression that is used in an eval to compute the readings value. The raw value will be in the variable $val.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?OMap&lt;br /&gt;
:Output Map. Defines a mapping from raw to visible values like &amp;quot;0:mittig, 1:oberhalb, 2:unterhalb&amp;quot;. If specified as readingOMap then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*OMap.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?Format&lt;br /&gt;
:Defines a format string that will be used in sprintf to format a reading value. If specified as readingFormat then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*Format.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?Decode&lt;br /&gt;
:defines an encoding to be used in a call to the perl function decode to convert the raw data string read from the device to a reading. This can be used if the device delivers strings in an encoding like cp850 instead of utf8.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]*(-[0-9]+)?Encode&lt;br /&gt;
:defines an encoding to be used in a call to the perl function encode to convert the raw data string read from the device to a reading. This can be used if the device delivers strings in an encoding like cp850 and after decoding it you want to reencode it to e.g. utf8.&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+Regex&lt;br /&gt;
:defines the regex to be used for extracting the reading. The value to extract should be in a capture group / sub expression &lt;br /&gt;
:e.g. ([\d\.]+) in the above example. Multiple capture groups will create multiple readings (see explanation above)&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+XPath&lt;br /&gt;
:defines an xpath to one or more readings when parsing HTML data (see examples below)&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+XPath-Strict&lt;br /&gt;
:defines an xpath to one or more readings when parsing XML data (see examples below)&lt;br /&gt;
&lt;br /&gt;
;reading[0-9]+JSON&lt;br /&gt;
:defines a path to the JSON object wanted by concatenating the object names with an underscore as delimiter (see the example below)&lt;br /&gt;
&lt;br /&gt;
;noShutdown&lt;br /&gt;
:pass the noshutdown flag to HTTPUtils for webservers that need it (some embedded webservers only deliver empty pages otherwise)&lt;br /&gt;
&lt;br /&gt;
;disable&lt;br /&gt;
:stop doing automatic HTTP requests while this attribute is set to 1&lt;br /&gt;
&lt;br /&gt;
;timeout&lt;br /&gt;
:time in seconds to wait for an answer. Default value is 2&lt;br /&gt;
&lt;br /&gt;
;do_not_notify&lt;br /&gt;
&lt;br /&gt;
;readingFnAttributes&lt;br /&gt;
&lt;br /&gt;
== Simple Configuration of HTTP Devices ==&lt;br /&gt;
If your device expects special HTTP-headers then specify them as &amp;lt;code&amp;gt;attr requestHeader1&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;attr requestHeaderX&amp;lt;/code&amp;gt;.&lt;br /&gt;
If your Device expects an HTTP POST instead of HTTP GET then the POST-data can be specified as &amp;lt;code&amp;gt;attr requestData&amp;lt;/code&amp;gt;.&lt;br /&gt;
To get the readings, specify pairs of &amp;lt;code&amp;gt;attr readingXName&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;attr readingXRegex&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;attr readingXXPath&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;attr readingXXPath-Strict&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;attr readingXJSON&amp;lt;/code&amp;gt; to define which readings you want to extract from the HTTP response and how to extract them. (The old syntax &amp;lt;code&amp;gt;attr readingsNameX&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;attr readingsRegexX&amp;lt;/code&amp;gt; is still supported but the new one with &amp;lt;code&amp;gt;attr readingXName&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;attr readingXRegex&amp;lt;/code&amp;gt; should be preferred. The actual values to be extracted have to be sub expressions within () in the regex (see example below)&lt;br /&gt;
&lt;br /&gt;
=== Example for a PoolManager 5: ===&lt;br /&gt;
The PoolManager Web GUI can be queried with HTTP POST Requests like this one:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
POST /cgi-bin/webgui.fcgi HTTP/1.1&lt;br /&gt;
Host: 192.168.70.90&lt;br /&gt;
Accept: */*&lt;br /&gt;
Content-Type: application/json;charset=UTF-8&lt;br /&gt;
Content-Length: 60&lt;br /&gt;
&lt;br /&gt;
{&amp;quot;get&amp;quot; :[&amp;quot;34.4001.value&amp;quot; ,&amp;quot;34.4008.value&amp;quot; ,&amp;quot;34.4033.value&amp;quot;]}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The resulting HTTP Response would look like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
HTTP/1.1 200 OK&lt;br /&gt;
Content-type: application/json; charset=UTF-8&lt;br /&gt;
Expires: 0&lt;br /&gt;
Cache-Control: no-cache&lt;br /&gt;
Date: Sun, 12 Jan 2014 12:23:11 GMT&lt;br /&gt;
Server: lighttpd/1.4.26&lt;br /&gt;
Content-Length: 179&lt;br /&gt;
&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;data&amp;quot;:	{&lt;br /&gt;
		&amp;quot;34.4001.value&amp;quot;:	&amp;quot;7.00&amp;quot;,&lt;br /&gt;
		&amp;quot;34.4008.value&amp;quot;:	&amp;quot;0.52&amp;quot;,&lt;br /&gt;
		&amp;quot;34.4033.value&amp;quot;:	&amp;quot;24.8&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;status&amp;quot;:	{&lt;br /&gt;
		&amp;quot;code&amp;quot;:	0&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;event&amp;quot;:	{&lt;br /&gt;
		&amp;quot;type&amp;quot;:	1,&lt;br /&gt;
		&amp;quot;data&amp;quot;:	&amp;quot;48.30000.0&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To configure HTTPMOD for a PoolManager one would first define a PoolManager device with e.g. the name PM, the URL and an interval of e.g. 60 seconds. &lt;br /&gt;
&lt;br /&gt;
Then the data to be sent in the request needs to be defined because in this example the device expects a POST request so the query is not contained in the URL but in the request data.&lt;br /&gt;
&lt;br /&gt;
Also as seen above the device expects special HTTP headers in the request so these headers also need to be defined as &amp;lt;code&amp;gt;attr PM requestHeader1&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;attr PM requestHeader2&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then the names of the readings to be extracted would be set with attributes&lt;br /&gt;
&lt;br /&gt;
Then for each reading value to be extracted a regular expression needs to be set that will match the value in question within ().&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define PM HTTPMOD http://MyPoolManager/cgi-bin/webgui.fcgi 60&lt;br /&gt;
&lt;br /&gt;
attr PM enableControlSet 1&lt;br /&gt;
attr PM enableCookies 1&lt;br /&gt;
attr PM enforceGoodReadingNames 1&lt;br /&gt;
attr PM handleRedirects 1&lt;br /&gt;
&lt;br /&gt;
attr PM reading01Name PH&lt;br /&gt;
attr PM reading01Regex 34.4001.value&amp;quot;:[ \t]+&amp;quot;([\d\.]+)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attr PM reading02Name CL&lt;br /&gt;
attr PM reading02Regex 34.4008.value&amp;quot;:[ \t]+&amp;quot;([\d\.]+)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attr PM reading03Name3TEMP&lt;br /&gt;
attr PM reading03Regex 34.4033.value&amp;quot;:[ \t]+&amp;quot;([\d\.]+)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attr PM requestData {&amp;quot;get&amp;quot; :[&amp;quot;34.4001.value&amp;quot; ,&amp;quot;34.4008.value&amp;quot; ,&amp;quot;34.4033.value&amp;quot;, &amp;quot;14.16601.value&amp;quot;, &amp;quot;14.16602.value&amp;quot;]}&lt;br /&gt;
attr PM requestHeader1 Content-Type: application/json&lt;br /&gt;
attr PM requestHeader2 Accept: */*&lt;br /&gt;
attr PM stateFormat {sprintf(&amp;quot;%.1f Grad, PH %.1f, %.1f mg/l Chlor&amp;quot;, ReadingsVal($name,&amp;quot;TEMP&amp;quot;,0), ReadingsVal($name,&amp;quot;PH&amp;quot;,0), ReadingsVal($name,&amp;quot;CL&amp;quot;,0))}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Example for AmbientMonitor ===&lt;br /&gt;
AmbientMonitor is a webbased visualisation for sensors connected to an Arduino device. Its web interface can also be queried with HTTMOD to grab the data into readings.&lt;br /&gt;
&lt;br /&gt;
This example was provided by locutus. The hardware configuration is an Arduino + Ethercard with ENC28J60 Controller + DHT22 Sensor and software can be downloaded from https://github.com/lucadentella/AmbientMonitor&lt;br /&gt;
&lt;br /&gt;
In this example an HTTP GET is sufficent, so no &amp;lt;code&amp;gt;requestData&amp;lt;/code&amp;gt; is needed. The device provides temperature and humidity readings in an HTTP response that looks like:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
HTTP/1.0 200 OK &lt;br /&gt;
Content-Type: text/html &lt;br /&gt;
&lt;br /&gt;
myCB({&#039;temperature&#039;:22.00,&#039;humidity&#039;:46.00})&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the definition could be:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define AmbientMonitor HTTPMOD http://192.168.1.221/?callback=? 300&lt;br /&gt;
&lt;br /&gt;
attr AmbientMonitor enableControlSet 1&lt;br /&gt;
attr AmbientMonitor enableCookies 1&lt;br /&gt;
attr AmbientMonitor enforceGoodReadingNames 1&lt;br /&gt;
attr AmbientMonitor handleRedirects 1&lt;br /&gt;
&lt;br /&gt;
attr AmbientMonitor requestHeader Content-Type: application/json&lt;br /&gt;
attr AmbientMonitor reading1Name Temperatur&lt;br /&gt;
attr AmbientMonitor reading1Regex temperature&#039;:([\d\.]+)&lt;br /&gt;
attr AmbientMonitor reading2Name Feuchtigkeit&lt;br /&gt;
attr AmbientMonitor reading2Regex humidity&#039;:([\d\.]+)&lt;br /&gt;
attr AmbientMonitor stateFormat {sprintf(&amp;quot;Temperatur %.1f C, Feuchtigkeit %.1f %&amp;quot;, ReadingsVal($name,&amp;quot;Temperatur&amp;quot;,0), ReadingsVal($name,&amp;quot;Feuchtigkeit&amp;quot;,0))}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== formatting and manipulating values / readings ==&lt;br /&gt;
Values that are parsed from an HTTP response can be further treated or formatted with the following attributes:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?OExpr&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?OMap&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?Format&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?Decode&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?Encode&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
They can all be specified for an individual reading, for all readings in one match (e.g. if a regular expression has several capture groups) or for all readings in a get command (defined by getXX) or for all readings in the main reading list (defined by readingXX):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading01Format %.1f&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will format the reading with the name specified by the attribute reading01Name to be numerical with one digit after the decimal point.&lt;br /&gt;
If the attribute reading01Regex is used and contains several capture groups then the format will be applied to all readings parsed by this regex unless these readings have their own format specified by reading01-1Format, reading01-2Format and so on.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading01-2Format %.1f&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Can be used in cases where a regular expression specified as reading01regex contains several capture groups or an xpath specified as reading01XPath creates several readings. &lt;br /&gt;
In this case reading01-2Format specifies the format to be applied to the second match.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
readingFormat %.1f&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
applies to all readings defined by a reading-Attribute that have no more specific format.&lt;br /&gt;
&lt;br /&gt;
If you need to do some calculation on a raw value before it is used as a reading, you can define the attribute &amp;lt;code&amp;gt;readingOExpr&amp;lt;/code&amp;gt;.&lt;br /&gt;
It defines a Perl expression that is used in an eval to compute the readings value. The raw value will be in the variable $val.&lt;br /&gt;
&lt;br /&gt;
Example for an expression:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM reading03OExpr $val * 10&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Just like in the above example of the readingFormat attributes, readingOExpr and the other following attributes can be applied on several levels.&lt;br /&gt;
&lt;br /&gt;
To map a numerical value to a name, you can use the readingOMap attribute. &lt;br /&gt;
It defines a mapping from raw to visible values like &amp;quot;0:mittig, 1:oberhalb, 2:unterhalb&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Example for a map:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM reading02-3OMap 0:kalt, 1:warm, 2:sehr warm&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To convert character sets, the module can first decode a string read from the device and then encode it again. For example:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM getDecode UTF-8&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This applies to all readings defined for Get-Commands.&lt;br /&gt;
&lt;br /&gt;
== Some help with Regular Expressions ==&lt;br /&gt;
{{Randnotiz|RNTyp=y|RNText=Starting with version &#039;&#039;2018-02-10&#039;&#039; the internal that holds the HTTP response is no longer called &#039;&#039;&#039;&#039;&#039;buf&#039;&#039;&#039;&#039;&#039; but rather &#039;&#039;&#039;&#039;&#039;httpbody&#039;&#039;&#039;&#039;&#039;, and it is only displayed when attribute &#039;&#039;&#039;&#039;&#039;showBody&#039;&#039;&#039;&#039;&#039; is set to &amp;quot;1&amp;quot;.}}&lt;br /&gt;
If HTTPMOD seems not to work and the FHEM Logfile contains a message like  &lt;br /&gt;
:&amp;lt;code&amp;gt;HTTPMOD: Response didn&#039;t match Reading ...&amp;lt;/code&amp;gt;&lt;br /&gt;
then you should check if the value you want to extract is read into the internal with the name buf. Internals are visible when you click on the defined HTTPMOD Device. buf is an internal variable that contains the HTTP Response read. If the value is there and you get the mentioned message then probably something is wrong with your regular expression. Please note that buf might contain special characters like newlines but they are not shown in fhemweb. If you are new to regular expressions then the introduction at http://perldoc.perl.org/perlretut.html might be helpful. &lt;br /&gt;
&lt;br /&gt;
For a typical HTTPMOD use case where you want to extract a number out of a HTTP-Response you can use something like &amp;lt;code&amp;gt;[\d\.]+&amp;lt;/code&amp;gt; to match the number itself. The expression matches the number characters (&amp;lt;code&amp;gt;\d&amp;lt;/code&amp;gt;) or a &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt; if one of these characters occurs at least once. &lt;br /&gt;
&lt;br /&gt;
To tell HTTPMOD that the number is what you want to use for the reading, you have to put the expression in between &amp;lt;code&amp;gt;()&amp;lt;/code&amp;gt;. A &amp;lt;code&amp;gt;([\d\.]+)&amp;lt;/code&amp;gt; alone would match the longest number in the HTTP Response which is very likely not the number you are looking for so you need to add something to the expression to give it a context and define how to find the number that you are looking for.&lt;br /&gt;
&lt;br /&gt;
If there is a title text before the number or a special text after the number you can put this in the regex. In one of the examples above &amp;lt;code&amp;gt;humidity&#039;:([\d\.]+)&amp;lt;/code&amp;gt; is looking for the number that immediately follows the text &amp;lt;code&amp;gt;humidity&#039;:&amp;lt;/code&amp;gt; without any blanks in between.&lt;br /&gt;
Be careful if the text you are getting from your device contains special characters like newline. You don&#039;t see such special characters in the fhem webinterface as contents of the internal buf but they might cause your regular expression to fail. &lt;br /&gt;
&lt;br /&gt;
If you have trouble defining a regular expression that matches a certain name, then many complicated characters and then a number, it might be helpful to use a negation in matching like &amp;lt;code&amp;gt;temp[^\d]+([\d\.]).*&amp;lt;/code&amp;gt;. In this examle &amp;lt;code&amp;gt;[^\d]+&amp;lt;/code&amp;gt; means any character that is not a numerical digit, more than once.&lt;br /&gt;
&lt;br /&gt;
=== Regular Expressions with multiple capture Groups ===&lt;br /&gt;
The regular expressions used in the above example for a Poolmanager will take the value that matches one capture group. This is the part of the regular expression inside (). In the above example &amp;quot;([\d\.]+)&amp;quot; refers to numerical digits or points between double quotation marks. Only the string consiting of digits and points will match inside (). This piece is assigned to the reading.&lt;br /&gt;
        &lt;br /&gt;
You can also use regular expressions that have several capture groups which might be helpful when parsing tables. In this case an attribute like &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading02Regex something[ \t]+([\d\.]+)[ \t]+([\d\.]+)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
could match two numbers. When you specify only one reading02Name like &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading02Name Temp&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the name Temp will be used with the extension -1 and -2 thus giving a reading Temp-1 for the first number and Temp-2 for the second. You can also specify individual names for several readings that get parsed from one regular expression with several capture groups by defining attributes &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
reading02-1Name&lt;br /&gt;
reading02-2Name&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The same notation can be used for formatting attributes like readingXOMap, readingXFormat and so on.&lt;br /&gt;
&lt;br /&gt;
The usual way to define readings is however to have an individual regular expression with just one capture group per reading as shown in the above example.&lt;br /&gt;
&lt;br /&gt;
== Parsing JSON ==&lt;br /&gt;
    &lt;br /&gt;
If a webservice delivers data in JSON format, HTTPMOD can directly parse JSON which might be easier in this case than definig regular expressions.&lt;br /&gt;
The next example shows the data that can be requested from a Poolmanager with the following partial configuration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define test2 HTTPMOD none 0&lt;br /&gt;
attr test2 get01Name Chlor&lt;br /&gt;
attr test2 getURL http://192.168.70.90/cgi-bin/webgui.fcgi&lt;br /&gt;
attr test2 getHeader1 Content-Type: application/json&lt;br /&gt;
attr test2 getHeader2 Accept: */*&lt;br /&gt;
attr test2 getData {&amp;quot;get&amp;quot; :[&amp;quot;34.4008.value&amp;quot;]}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The data in the HTTP response looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;data&amp;quot;: {&lt;br /&gt;
			&amp;quot;34.4008.value&amp;quot;: &amp;quot;0.25&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;status&amp;quot;:       {&lt;br /&gt;
			&amp;quot;code&amp;quot;: 0&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;event&amp;quot;:        {&lt;br /&gt;
			&amp;quot;type&amp;quot;: 1,&lt;br /&gt;
			&amp;quot;data&amp;quot;: &amp;quot;48.30000.0&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
the classic way to extract the value 0.25 into a reading with the name Chlor with a regex would have been&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr test2 get01Regex 34.4008.value&amp;quot;:[ \t]+&amp;quot;([\d\.]+)&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
with JSON you can write &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr test2 get01JSON data_34.4008.value &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
which will create a reading with the Name &amp;quot;Chlor&amp;quot; (as shown above) and take the value 0.25 from the JSON string.&lt;br /&gt;
&lt;br /&gt;
or if you don&#039;t care about the naming of your readings, you can simply extract all JSON data with &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr test2 extractAllJSON&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which would apply to all data read from this device and create the following readings out of the HTTP response shown above:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| data_34.4008.value || 0.25&lt;br /&gt;
|-&lt;br /&gt;
| event_data || 48.30000.0&lt;br /&gt;
|-&lt;br /&gt;
| event_type || 1&lt;br /&gt;
|-&lt;br /&gt;
| status_code || 0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
or you can specify&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr test2 get01ExtractAllJSON&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
which would only apply to all data read as response to the get command defined as get01.        &lt;br /&gt;
&lt;br /&gt;
It might seem very simple at first sight to use extractAllJSON but if you prefer readings with a meaningful name you should instead define these readings with readingXXName and readingXXJSON or getXXName and getXXJSON individually. Of Course it would be possible to create additional user readings outside HTTPMOD but doing calculations, naming and formatting inside HTTPMOD is more efficient.&lt;br /&gt;
&lt;br /&gt;
=== JSON Lists ===&lt;br /&gt;
&lt;br /&gt;
imagine the HTTP Response contains:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{ &amp;quot;power&amp;quot;:&amp;quot;0&amp;quot;,&lt;br /&gt;
  &amp;quot;modes&amp;quot;:[&amp;quot;Off&amp;quot;,&amp;quot;SimpleColor&amp;quot;,&amp;quot;RainbowChase&amp;quot;],&lt;br /&gt;
  &amp;quot;code1&amp;quot;:3,&lt;br /&gt;
  &amp;quot;code2&amp;quot;:4&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
then a configuration like &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr device reading01JSON modes &lt;br /&gt;
attr device reading01Name Mode &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will create a list of Subreadings just like a regex with multiple matches can create multiple subreadings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Mode-1 || Off&lt;br /&gt;
|-&lt;br /&gt;
| Mode-2  || SimpleColor&lt;br /&gt;
|-&lt;br /&gt;
| Mode-3 || RainbowChase &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
if you don&#039;t want several subreadings but one reading that contains the list of modes, you can specify a recombine expression:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr device reading01Name Modes &lt;br /&gt;
attr device reading01RecombineExpr join &amp;quot;,&amp;quot;, @matchlist &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which will create one reading containing a list:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Modes || Off,SimpleColor,RainbowChase&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
JSON parsing specifications also don&#039;t Need to match exactly. If there is no exact match for a defined reading, the HTTPMOD will try to Interpret the specification as a regex and look for json object paths that match the specification as a regex. For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr device reading01Name CodeElem&lt;br /&gt;
attr device reading01JSON code&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
which will create a list of readings:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| CodeElem-1|| 3&lt;br /&gt;
|-&lt;br /&gt;
| CodeElem-2  || 4&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
and of course they could also be recombined into one reading with a RecombineExpr Attribute.&lt;br /&gt;
&lt;br /&gt;
== Parsing http / XML using xpath ==&lt;br /&gt;
Another alternative to regex parsing is the use of XPath to extract values from HTTP responses.&lt;br /&gt;
The following example shows how XML data can be parsed with XPath-Strict or HTML Data can be parsed with XPath.&lt;br /&gt;
Both work similar and the example uses XML Data parsed with the XPath-Strict option:&lt;br /&gt;
&lt;br /&gt;
If The XML data in the HTTP response looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;root xmlns:foo=&amp;quot;http://www.foo.org/&amp;quot; xmlns:bar=&amp;quot;http://www.bar.org&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;actors&amp;gt;&lt;br /&gt;
		&amp;lt;actor id=&amp;quot;1&amp;quot;&amp;gt;Peter X&amp;lt;/actor&amp;gt;&lt;br /&gt;
		&amp;lt;actor id=&amp;quot;2&amp;quot;&amp;gt;Charles Y&amp;lt;/actor&amp;gt;&lt;br /&gt;
		&amp;lt;actor id=&amp;quot;3&amp;quot;&amp;gt;John Doe&amp;lt;/actor&amp;gt;&lt;br /&gt;
	&amp;lt;/actor&amp;gt;&lt;br /&gt;
&amp;lt;/root&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
with XPath you can write        &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr htest reading01Name Actor&lt;br /&gt;
attr htest reading01XPath-Strict //actor[2]/text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will create a reading with the Name &amp;quot;Actor&amp;quot; and the value &amp;quot;Charles Y&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Since XPath specifications can define several values / matches, HTTPMOD can also interpret these and store them in multiple readings:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr htest reading01Name Actor&lt;br /&gt;
attr htest reading01XPath-Strict //actor/text()&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will create the readings &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
| Actor-1 || Peter X&lt;br /&gt;
|-&lt;br /&gt;
| Actor-2 || Charles Y&lt;br /&gt;
|-&lt;br /&gt;
| Actor-3 || John Doe&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Further replacements of URL, header or post data ==&lt;br /&gt;
sometimes it might be helpful to dynamically change parts of a URL, HTTP header or post data depending on existing readings, internals or &lt;br /&gt;
perl expressions at runtime. This might be needed to pass further variables to a server, a current date or other things. &lt;br /&gt;
&lt;br /&gt;
To support this HTTPMOD offers generic replacements that are applied to a request before it is sent to the server. A replacement can be defined with the attributes &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;replacement[0-9]*Regex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;[gs]et[0-9]*Replacement[0-9]*Value&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
a replacement always replaces a match of a regular expression. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;replacement[0-9]*Mode:&#039;&#039;&#039;&lt;br /&gt;
The way the replacement value is defined can be specified with the replacement mode.&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;reading&amp;lt;/code&amp;gt;, then the corresponding &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt; is interpreted as the name of a &#039;&#039;reading&#039;&#039; of the same device or as &#039;&#039;device:reading&#039;&#039; to refer to another device.&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;internal&amp;lt;/code&amp;gt;, then the corresponding &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt; is interpreted as the name of an &#039;&#039;internal&#039;&#039; of the same device or as &#039;&#039;device:internal&#039;&#039; to refer to another device.&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;, then the corresponding &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt; is interpreted as a static text&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;expression&amp;lt;/code&amp;gt;, then the corresponding &amp;lt;code&amp;gt;replacement[0-9]*Value&amp;lt;/code&amp;gt; is evaluated as a perl expression to compute the replacement. Inside such a replacement expression it is possible to refer to capture groups of the replacement regex.&lt;br /&gt;
* If the &amp;lt;code&amp;gt;replacement[0-9]*Mode&amp;lt;/code&amp;gt; is &amp;lt;code&amp;gt;key&amp;lt;/code&amp;gt;, then the module will use a value from a key / value pair that is stored in an obfuscated form in the file system with the set storeKeyValue command. This might be useful for storing passwords.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
attr mydevice getData {&amp;quot;get&amp;quot; :[&amp;quot;%%value%%.value&amp;quot;]}&lt;br /&gt;
attr mydevice replacement01Mode text&lt;br /&gt;
attr mydevice replacement01Regex %%value%%&lt;br /&gt;
&lt;br /&gt;
attr mydevice get01Name Chlor&lt;br /&gt;
attr mydevice get01Replacement01Value 34.4008&lt;br /&gt;
&lt;br /&gt;
attr mydevice get02Name Something&lt;br /&gt;
attr mydevice get02Replacement01Value 31.4024&lt;br /&gt;
&lt;br /&gt;
attr mydevice get05Name profile&lt;br /&gt;
attr mydevice get05URL http://www.mydevice.local/getprofile?password=%%password%%&lt;br /&gt;
attr mydevice replacement02Mode key&lt;br /&gt;
attr mydevice replacement02Regex %%password%%&lt;br /&gt;
attr mydevice replacement02Value password&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;        &lt;br /&gt;
&lt;br /&gt;
defines that &amp;lt;code&amp;gt;%%value%%&amp;lt;/code&amp;gt; will be replaced by a static text.&lt;br /&gt;
&lt;br /&gt;
All Get commands will be HTTP post requests of a similar form. Only the &amp;lt;code&amp;gt;%%value%%&amp;lt;/code&amp;gt; will be different from get to get.&lt;br /&gt;
The first get will set the reading named Chlor and for the request it will take the generic getData and replace %%value%% with 34.4008.&lt;br /&gt;
&lt;br /&gt;
A second get will look the same except a different name and replacement value.&lt;br /&gt;
&lt;br /&gt;
With the command &amp;lt;code&amp;gt;set mydevice storeKeyValue password geheim&amp;lt;/code&amp;gt; you can store the password geheim in an obfuscated form in the file system. &lt;br /&gt;
To use this password and send it in a request you can use the above replacement with mode key. The value password will then refer to the ofuscated string stored with the key password.&lt;br /&gt;
&lt;br /&gt;
The mode &amp;lt;code&amp;gt;expression&amp;lt;/code&amp;gt; allows you to define your own replacement syntax:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;        &lt;br /&gt;
attr mydevice replacement01Mode expression&lt;br /&gt;
attr mydevice replacement01Regex {{([^}]+)}}&lt;br /&gt;
attr mydevice replacement01Value ReadingsVal(&amp;quot;mydevice&amp;quot;, $1, &amp;quot;&amp;quot;)&lt;br /&gt;
attr mydevice getData {&amp;quot;get&amp;quot; :[&amp;quot;{{temp}}.value&amp;quot;]}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;      &lt;br /&gt;
&lt;br /&gt;
In this example any &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{name}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; in a URL, header or post data will be passed on to the perl function ReadingsVal &lt;br /&gt;
which uses the string between &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; as second parameter. This way one defined replacement can be used for many different&lt;br /&gt;
readings.&lt;br /&gt;
&lt;br /&gt;
HTTPMOD has two built in replacements: One for session Ids and another one for the input value in a set command.&lt;br /&gt;
The placeholder $sid is always replaced with the internal &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt; which contains the session id after it is extracted from a previous HTTP response. &lt;br /&gt;
If you don&#039;t like to use the placeholder $sid then you can define your own replacement for example like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr mydevice replacement01Mode internal&lt;br /&gt;
attr mydevice replacement01Regex %session%&lt;br /&gt;
attr mydevice replacement01Value sid&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;      &lt;br /&gt;
&lt;br /&gt;
Now the internal &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt; will be used as a replacement for the placeholder %session%.&lt;br /&gt;
&lt;br /&gt;
In the same way a value that is passed to a set-command can be put into a request with a user defined replacement. &lt;br /&gt;
In this case the internal &amp;lt;code&amp;gt;$hash-&amp;gt;{value}&amp;lt;/code&amp;gt; will contain the value passed to the set command. &lt;br /&gt;
&amp;lt;code&amp;gt;$hash-&amp;gt;{value}&amp;lt;/code&amp;gt; might even be a string containing several values that could be put into several different positions in a request by using user defined replacements.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Other example : steering a pellet stove from Rika&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The stove API of Rika on https://www.rika-firenet.com/web/ delivers a JSON string with all settings and values, and can be piloted with a data string containing all the &amp;quot;set&amp;quot; values at once.&lt;br /&gt;
&lt;br /&gt;
Delivered JSON on get https://www.rika-firenet.com/api/client/xxxxxxxx/status : (xxxxxxxx must be replaced with the unique stove ID)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Vorzimmer&amp;quot;,&lt;br /&gt;
    &amp;quot;stoveID&amp;quot;: &amp;quot;xxxxxxxxx&amp;quot;,&lt;br /&gt;
    &amp;quot;lastSeenMinutes&amp;quot;: 1,&lt;br /&gt;
    &amp;quot;lastConfirmedRevision&amp;quot;: 1504385700,&lt;br /&gt;
    &amp;quot;controls&amp;quot;: {&lt;br /&gt;
        &amp;quot;revision&amp;quot;: 1504385700,&lt;br /&gt;
        &amp;quot;onOff&amp;quot;: true,&lt;br /&gt;
        &amp;quot;operatingMode&amp;quot;: 2,&lt;br /&gt;
        &amp;quot;heatingPower&amp;quot;: 65,&lt;br /&gt;
        &amp;quot;targetTemperature&amp;quot;: 24,&lt;br /&gt;
        &amp;quot;heatingTimesActive&amp;quot;: false,&lt;br /&gt;
        &amp;quot;heatingTimesActiveForComfort&amp;quot;: true,&lt;br /&gt;
        &amp;quot;setBackTemperature&amp;quot;: 18,&lt;br /&gt;
        &amp;quot;convectionFan1Active&amp;quot;: false,&lt;br /&gt;
        &amp;quot;convectionFan1Level&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;convectionFan1Area&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;convectionFan2Active&amp;quot;: false,&lt;br /&gt;
        &amp;quot;convectionFan2Level&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;convectionFan2Area&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;frostProtectionActive&amp;quot;: false,&lt;br /&gt;
        &amp;quot;frostProtectionTemperature&amp;quot;: 5&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;sensors&amp;quot;: {&lt;br /&gt;
        &amp;quot;statusError&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;statusWarning&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;statusService&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;statusMainState&amp;quot;: 1,&lt;br /&gt;
        &amp;quot;statusSubState&amp;quot;: 3,&lt;br /&gt;
        &amp;quot;statusFrostStarted&amp;quot;: false,&lt;br /&gt;
        &amp;quot;inputFlameTemperature&amp;quot;: 21,&lt;br /&gt;
        &amp;quot;inputRoomTemperature&amp;quot;: 21,&lt;br /&gt;
        &amp;quot;inputExternalRequest&amp;quot;: true,&lt;br /&gt;
        &amp;quot;outputDischargeMotor&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;outputInsertionMotor&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;outputIDFan&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;outputAirFlaps&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;outputIgnition&amp;quot;: false,&lt;br /&gt;
        &amp;quot;parameterStoveTypeNumber&amp;quot;: 13,&lt;br /&gt;
        &amp;quot;parameterVersionMainBoard&amp;quot;: 223,&lt;br /&gt;
        &amp;quot;parameterVersionTFT&amp;quot;: 223,&lt;br /&gt;
        &amp;quot;parameterRuntimePellets&amp;quot;: 11,&lt;br /&gt;
        &amp;quot;parameterRuntimeLogs&amp;quot;: 0,&lt;br /&gt;
        &amp;quot;parameterFeedRateTotal&amp;quot;: 17,&lt;br /&gt;
        &amp;quot;parameterFeedRateService&amp;quot;: 683,&lt;br /&gt;
        &amp;quot;parameterOnOffCycles&amp;quot;: 2&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;stoveType&amp;quot;: &amp;quot;DOMO MultiAir&amp;quot;,&lt;br /&gt;
    &amp;quot;stoveFeatures&amp;quot;: {&lt;br /&gt;
        &amp;quot;multiAir1&amp;quot;: true,&lt;br /&gt;
        &amp;quot;multiAir2&amp;quot;: true,&lt;br /&gt;
        &amp;quot;insertionMotor&amp;quot;: false,&lt;br /&gt;
        &amp;quot;airFlaps&amp;quot;: false,&lt;br /&gt;
        &amp;quot;logRuntime&amp;quot;: false&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Data string to send to https://www.rika-firenet.com/api/client/xxxxxxxx/controls in order to set values:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
heatingTimesActiveForComfort=true&amp;amp;frostProtectionTemperature=3&amp;amp;setBackTemperature=18&amp;amp;targetTemperature=24&amp;amp;convectionFan2Level=0&amp;amp;convectionFan2Active=false&amp;amp;convectionFan1Level=0&amp;amp;onOff=true&amp;amp;convectionFan1Active=false&amp;amp;convectionFan2Area=0&amp;amp;revision=1505550101&amp;amp;heatingTimesActive=false&amp;amp;convectionFan1Area=0&amp;amp;frostProtectionActive=false&amp;amp;operatingMode=2&amp;amp;heatingPower=65&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Code in 99_myUtils.pm:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
use JSON;&lt;br /&gt;
...&lt;br /&gt;
sub&lt;br /&gt;
replaceJSON ($$) {&lt;br /&gt;
   my ($valToReplace, $value) = @_;&lt;br /&gt;
&lt;br /&gt;
   #$value in the parameters is a default value&lt;br /&gt;
   #It has to be replaced through the real value nnn passed in the set command &amp;quot;set &amp;lt;device&amp;gt; valToset nnn&amp;quot;&lt;br /&gt;
   $value = InternalVal(&amp;quot;Ofen&amp;quot;, &amp;quot;value&amp;quot;, $value);&lt;br /&gt;
   Log3 (&amp;quot;Ofen&amp;quot;, 3, &amp;quot;replaceJSON Internalvalue: $value&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   #Force an update to avoid outdated revision number&lt;br /&gt;
   fhem (&amp;quot;get Ofen revision&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   #Get all the controls as json&lt;br /&gt;
   my $json = ReadingsVal(&amp;quot;Ofen&amp;quot;, &amp;quot;controlsJSON&amp;quot;,&amp;quot;&amp;quot;);&lt;br /&gt;
   Log3 (&amp;quot;Ofen&amp;quot;, 3, &amp;quot;replaceJSON configsJSON: $json&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   # When starting FHEM or rereading config, the reading controlsJSON is empty&lt;br /&gt;
   return if ($json eq &amp;quot;&amp;quot;); &lt;br /&gt;
&lt;br /&gt;
   my $decoded = decode_json($json);&lt;br /&gt;
   my $result;&lt;br /&gt;
   for my $key ( keys %$decoded ) {&lt;br /&gt;
      $result .= &amp;quot;$key=&amp;quot;;&lt;br /&gt;
      if ($key eq $valToReplace) {&lt;br /&gt;
         $result .= $value.&amp;quot;&amp;amp;&amp;quot;;&lt;br /&gt;
      } else {&lt;br /&gt;
         $result .= $decoded-&amp;gt;{$key}.&amp;quot;&amp;amp;&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   chop($result); #remove last &amp;amp;&lt;br /&gt;
   Log3(&amp;quot;Ofen&amp;quot;, 3, &amp;quot;replaceJSON Result: $result&amp;quot;);&lt;br /&gt;
   return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Define stove in fhem:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod Ofen HTTPMOD https://www.rika-firenet.com/api/client/xxxxxxxx/status 60&lt;br /&gt;
&lt;br /&gt;
attr Ofen enableCookies 1&lt;br /&gt;
attr Ofen reAuthRegex id=&amp;quot;login&amp;quot;|Unauthorized&lt;br /&gt;
attr Ofen sid01Data email=xx@xx&amp;amp;password=xx&lt;br /&gt;
attr Ofen sid01URL https://www.rika-firenet.com/web/login&lt;br /&gt;
&lt;br /&gt;
attr Ofen reading01JSON sensors_inputRoomTemperature&lt;br /&gt;
attr Ofen reading01Name RaumTemp&lt;br /&gt;
attr Ofen reading02JSON controls_setBackTemperature&lt;br /&gt;
attr Ofen reading02Name Absenkung&lt;br /&gt;
attr Ofen reading03JSON controls_frostProtectionTemperature&lt;br /&gt;
attr Ofen reading03Name Frostschutz&lt;br /&gt;
attr Ofen reading10Name controlsJSON&lt;br /&gt;
attr Ofen reading10Regex (?s)controls.*?({.*?})&lt;br /&gt;
&lt;br /&gt;
attr Ofen get09Name revision&lt;br /&gt;
attr Ofen get09URL https://www.rika-firenet.com/api/client/xxxxxxxx/status&lt;br /&gt;
&lt;br /&gt;
attr Ofen setURL https://www.rika-firenet.com/api/client/xxxxxxxx/controls&lt;br /&gt;
attr Ofen setData {{data}}&lt;br /&gt;
attr Ofen replacement01Mode expression&lt;br /&gt;
attr Ofen replacement01Regex {{data}}&lt;br /&gt;
&lt;br /&gt;
attr Ofen set11Name frostProtectionTemperature&lt;br /&gt;
attr Ofen set11Replacement01Value replaceJSON(&amp;quot;frostProtectionTemperature&amp;quot;, 2)&lt;br /&gt;
&lt;br /&gt;
attr Ofen set12Name targetTemperature&lt;br /&gt;
attr Ofen set12Replacement01Value replaceJSON(&amp;quot;targetTemperature&amp;quot;, 24)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A detailed explanation (in german) of the login process can be found here: [https://forum.fhem.de/index.php/topic,76220.msg682514.html#msg682514]&lt;br /&gt;
and the explanation of the other parameters here: [https://forum.fhem.de/index.php/topic,76220.msg685710.html#msg685710]&lt;br /&gt;
&lt;br /&gt;
== replacing reading values when they have not been updated / the device did not respond ==&lt;br /&gt;
If a device does not respond then the values stored in readings will keep the same and only their timestamp shows that they are outdated. &lt;br /&gt;
If you want to modify reading values that have not been updated for a number of seconds, you can use the attributes&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?MaxAge&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?MaxAgeReplacementMode&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(reading|get)[0-9]*(-[0-9]+)?MaxAgeReplacement&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Every time the module tries to read from a device, it will also check if readings have not been updated &lt;br /&gt;
for longer than the &amp;lt;code&amp;gt;MaxAge&amp;lt;/code&amp;gt; attributes allow. If readings are outdated, the &amp;lt;code&amp;gt;MaxAgeReplacementMode&amp;lt;/code&amp;gt; defines how the affected&lt;br /&gt;
reading values should be replaced. &amp;lt;code&amp;gt;MaxAgeReplacementMode&amp;lt;/code&amp;gt; can be &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;reading&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;internal&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;expression&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;delete&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;MaxAge&amp;lt;/code&amp;gt; specifies the number of seconds that a reading should remain untouched before it is replaced. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;MaxAgeReplacement&amp;lt;/code&amp;gt; contains either a static text that is used as replacement value or a Perl expression that is evaluated to &lt;br /&gt;
give the replacement value. This can be used for example to replace a temperature that has not bee updated for more than 5 minutes &lt;br /&gt;
with the string &amp;quot;outdated - was 12&amp;quot;:        &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM readingMaxAge 300&lt;br /&gt;
attr PM readingMaxAgeReplacement &amp;quot;outdated - was &amp;quot; . $val&lt;br /&gt;
attr PM readingMaxAgeReplacementMode expression&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
The variable &amp;lt;code&amp;gt;$val&amp;lt;/code&amp;gt; contains the value of the reading before it became outdated.&lt;br /&gt;
&lt;br /&gt;
Or to show that a device was offline:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr MyLight reading01Name color&lt;br /&gt;
attr MyLight reading01JSON result_02_color&lt;br /&gt;
attr MyLight reading01MaxAge 300&lt;br /&gt;
attr MyLight reading01MaxAgeReplacement &amp;quot;offline&amp;quot;&lt;br /&gt;
attr MyLight reading01MaxAgeReplacementMode text&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Note on determining how to send requests to a special device ==&lt;br /&gt;
If you don&#039;t know which URLs, headers or POST data your web GUI uses, you might try a local proxy like BurpSuite [http://portswigger.net/burp/ BurpSuite] to track requests and responses. This is a tedious task but probably the best way to achieve a successful result. &lt;br /&gt;
&lt;br /&gt;
Let us consider an example. The Telekom Speedport W724V has a login-site that is famous for being cumbersome. Burp allows to monitor each step in the login procedure. In the case of a speedport the following steps occur:&lt;br /&gt;
&lt;br /&gt;
First burp shows that a get command is issued&lt;br /&gt;
 ################################################################################################## &lt;br /&gt;
 GET / HTTP/1.1&lt;br /&gt;
 Host: speedport.ip&lt;br /&gt;
 Cache-Control: max-age=0&lt;br /&gt;
 Upgrade-Insecure-Requests: 1&lt;br /&gt;
 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36&lt;br /&gt;
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8&lt;br /&gt;
 Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4&lt;br /&gt;
 Cookie: lang=de&lt;br /&gt;
 Connection: close  &lt;br /&gt;
In order to mimic the behavior of a real person calling the website HTTPMOD should copy all necessary steps. Host, Cookie and the GET-command are usually necessary. The same cannot be said of the User-Agent because the website can be called from any mobile or desktop computer. &lt;br /&gt;
&lt;br /&gt;
Then, the speedport will answer with a command that consists of several lines. By going through every line for every step in the login procedure one will finally arrive at the information that is necessary to successfully enter the login of the speedport (in case of the W724V, for example, it is necessary to copy a token called _httoken and to include the referer).&lt;br /&gt;
&lt;br /&gt;
== Advanced configuration to define a &amp;lt;code&amp;gt;set&amp;lt;/code&amp;gt; command and send data to a device ==&lt;br /&gt;
       &lt;br /&gt;
When a set option is defined by attributes, the module will use the value given to the set command and integrate it into an HTTP-Request that sends the value to the device. The definitions for URL, headers and post data can contain the placeholder $val which will be replaced by the (numerical) value given to the set command.&lt;br /&gt;
&lt;br /&gt;
This can be as simple as:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
# No cyclic requests and no main URL needed in this example&lt;br /&gt;
define MyDevice HTTPMOD none 0&lt;br /&gt;
&lt;br /&gt;
attr MyDevice set01Name Licht&lt;br /&gt;
attr MyDevice set01URL http://192.168.1.22/switch=$val&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A user command &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
set MyDevice Licht 1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
will be translated into the http GET request&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
http://192.168.1.22/switch=1&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this example a map would also be helpful, that translates on / off to 0 or 1 and allows the user to select on/of in fhemweb:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr MyDevive set01IMap 0:off, 1:on&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This also provides input validation to make sure that only on and off can be used with the set command.&lt;br /&gt;
&lt;br /&gt;
In more complex Scenarios you might need to login before sending a command and the Login might create a session id that has to be part of further requests either in the URL, in headers or in the post data.&lt;br /&gt;
&lt;br /&gt;
Extension to the above example for a PoolManager 5 where a set needs a session id in the URL and the values have to be passed in JSON strings as post data:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM set01Name HeizungSoll&lt;br /&gt;
attr PM set01URL http://MyPoolManager/cgi-bin/webgui.fcgi?sid=$sid&lt;br /&gt;
attr PM set01Hint 6,10,20,30&lt;br /&gt;
attr PM set01Min 6&lt;br /&gt;
attr PM set01Max 30&lt;br /&gt;
attr PM setHeader1 Content-Type: application/json&lt;br /&gt;
attr PM set01Data {&amp;quot;set&amp;quot; :{&amp;quot;34.3118.value&amp;quot; :&amp;quot;$val&amp;quot; }}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example defines a set option with the name HeizungSoll.&lt;br /&gt;
By issuing &amp;lt;code&amp;gt;set PM HeizungSoll 10&amp;lt;/code&amp;gt; in FHEM, the value 10 will be sent in the defined HTTP&lt;br /&gt;
Post to URL &amp;lt;code&amp;gt;http://MyPoolManager/cgi-bin/webgui.fcgi&amp;lt;/code&amp;gt; in the Post Data as&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;html&amp;quot;&amp;gt;&lt;br /&gt;
{&amp;quot;set&amp;quot; :{&amp;quot;34.3118.value&amp;quot; :&amp;quot;10&amp;quot; }}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The optional attributes set01Min and set01Max define input validations that will be checked in the set function. &lt;br /&gt;
The optional attribute set01Hint will define a selection list for the FHEMweb GUI.&lt;br /&gt;
&lt;br /&gt;
The HTTP response to such a request will be ignored unless you specify the attribute &amp;lt;code&amp;gt;setParseResponse&amp;lt;/code&amp;gt; &lt;br /&gt;
for all set commands or &amp;lt;code&amp;gt;set01ParseResponse&amp;lt;/code&amp;gt; for the set command with number 01.&lt;br /&gt;
If the HTTP response to a set command is parsed then this is done like the parsing of responses to get commands and you can use the attributes ending e.g. on Format, Encode, Decode, OMap and OExpr to manipulate / format the values read.&lt;br /&gt;
&lt;br /&gt;
If a parameter to a set command is not numeric but should be passed on to the device as text, then you can specify the attribute setTextArg. For example: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM set01TextArg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If a set command should not require a parameter at all, then you can specify the attribute NoArg. For example: &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM set03Name On&lt;br /&gt;
attr PM set03NoArg&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example defines the set of parameters for Wiesemann&amp;amp;Theiss Web-IO Analog 4.0 #57761. The current limit is set to 16mA (setxxMax)&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Perl&amp;quot;&amp;gt;&lt;br /&gt;
defmod WuT_Analog1 HTTPMOD none 30&lt;br /&gt;
attr WuT_Analog1 room W&amp;amp;T&lt;br /&gt;
attr WuT_Analog1 set01Max 16&lt;br /&gt;
attr WuT_Analog1 set01Min 0&lt;br /&gt;
attr WuT_Analog1 set01Name Output1&lt;br /&gt;
attr WuT_Analog1 set01URL http://192.xx.My_IP/outputaccess1?PW=MyPassword&amp;amp;State=$val&amp;amp;&lt;br /&gt;
attr WuT_Analog1 set02Max 16&lt;br /&gt;
attr WuT_Analog1 set02Min 0&lt;br /&gt;
attr WuT_Analog1 set02Name Output2&lt;br /&gt;
attr WuT_Analog1 set02URL http://192.xx.My_IP/outputaccess2?PW=MyPassword&amp;amp;State=$val&amp;amp;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Advanced configuration to create a valid session id that might be necessary ==&lt;br /&gt;
In simple cases logging in works with basic authentication. In the case HTTPMOD accepts a username and password as part of the URL in the form &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
http://User:Password@192.168.1.18/something&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
However basic auth is seldom used. If you need to fill in a username and password in a HTML form and the session is then managed by a session id, here is how to configure this:&lt;br /&gt;
&lt;br /&gt;
when sending data to an HTTP-Device in a set, HTTPMOD will replace any &amp;lt;code&amp;gt;$sid&amp;lt;/code&amp;gt; in the URL, Headers and Post data with the internal &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt;. To authenticate towards the device and give this internal a value, you can use an optional multi step login procedure defined by the following attributes: &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;sid[0-9]*URL&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;sid[0-9]*Data.*&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;sid[0-9]*Header.*&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;sid[0-9]*IgnoreRedirects&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;idRegex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;idJSON&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;idXPath&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;idXPath-Strict&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set|sid)[0-9]*IdRegex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set|sid)[0-9]*IdJSON&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set|sid)[0-9]*IdXPath&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set|sid)[0-9]*IdXPath-Strict&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each step can have a URL, Headers, Post Data pieces and a Regex to extract a resulting Session ID into &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt;.&lt;br /&gt;
HTTPMOD will create a sorted list of steps (the numbers between sid and URL / Data / Header) and the loop through these steps and send the corresponding requests to the device. For each step a $sid in a Header or Post Data will be replaced with the current content of &amp;lt;code&amp;gt;$hash-&amp;gt;{sid}&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Using this feature, HTTPMOD can perform a forms based authentication and send user name, password or other necessary data to the device and save the session id for further requests.&lt;br /&gt;
        &lt;br /&gt;
To determine when this login procedure is necessary, HTTPMOD will first try to send a request without &lt;br /&gt;
doing the login procedure. If the result contains an error that authentication is necessary, then a login is performed. &lt;br /&gt;
To detect such an error in the HTTP response, you can again use a regular expression, JSON or XPath, this time with the attributes &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;reAuthRegex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;reAuthJSON&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;reAuthXPath&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;reAuthXPath-Strict&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set)[0-9]*ReAuthRegex&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set)[0-9]*ReAuthJSON&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set)[0-9]*ReAuthXPath&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;(get|set)[0-9]*ReAuthXPath-Strict&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
reAuthJSON or reAuthXPath typically only extract one piece of data from a response. &lt;br /&gt;
If the existence of the specified piece of data is sufficent to start a login procedure, then nothing more needs to be defined to detect this situation. &lt;br /&gt;
If however the indicator is a status code that contains different values depending on a successful request and a failed request if a new authentication is needed, &lt;br /&gt;
then you can combine things like reAuthJSON with reAuthRegex. In this case the regex is only matched to the data extracted by JSON (or XPath). &lt;br /&gt;
This way you can easily extract the status code using JSON parsing and then specify the code that means &amp;quot;authentication needed&amp;quot; as a regular expression.&lt;br /&gt;
        &lt;br /&gt;
If for one step not all of the URL, Data or Header Attributes are set, then HTTPMOD tries to use a &lt;br /&gt;
&amp;lt;code&amp;gt;sidURL&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sidData.*&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;sidHeader.*&amp;lt;/code&amp;gt; Attribute (without the step number after sid). This way parts that are the same for all steps don&#039;t need to be defined redundantly.&lt;br /&gt;
&lt;br /&gt;
=== Example for a multi step login procedure: ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM reAuthRegex /html/dummy_login.htm &lt;br /&gt;
attr PM sidURL http://192.168.70.90/cgi-bin/webgui.fcgi?sid=$sid&lt;br /&gt;
attr PM sidHeader1 Content-Type: application/json&lt;br /&gt;
attr PM sid1IDRegex wui.init\(&#039;([^&#039;]+)&#039;&lt;br /&gt;
attr PM sid2Data {&amp;quot;set&amp;quot; :{&amp;quot;9.17401.user&amp;quot; :&amp;quot;fhem&amp;quot; ,&amp;quot;9.17401.pass&amp;quot; :&amp;quot;password&amp;quot; }}&lt;br /&gt;
attr PM sid3Data {&amp;quot;set&amp;quot; :{&amp;quot;35.5062.value&amp;quot; :&amp;quot;128&amp;quot; }}&lt;br /&gt;
attr PM sid4Data {&amp;quot;set&amp;quot; :{&amp;quot;42.8026.code&amp;quot; :&amp;quot;pincode&amp;quot; }}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this case HTTPMOD detects that a login is necessary by looking for the pattern /html/dummy_login.htm in the HTTP response. &lt;br /&gt;
If it matches, it starts a login sequence. In the above example all steps request the same URL. In step 1 only the defined Header is sent in an HTTP get request. The response will contain a session id that is extraced with the regex wui.init\(&#039;([^&#039;]+)&#039;.&lt;br /&gt;
&lt;br /&gt;
In the next step this session id is sent in a post request to the same URL where tha post data contains a username and password. The a third and a fourth request follow that set a value and a code. The result will be a valid and authorized session id that can be used in other requests where $sid is part of a URL, header or post data and will be replaced with the session id extracted above.&lt;br /&gt;
&lt;br /&gt;
===Another Practical Example reading the SOC from a Renault Zoe===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define myZoe HTTPMOD https://www.services.renault-ze.com/api/vehicle/VINVINVIN/battery 7000&lt;br /&gt;
attr myZoe userattr reading01JSON reading01Name reading02JSON reading02Name reading03JSON reading03Nam\&lt;br /&gt;
e reading04JSON reading04Name reading05JSON reading05Name requestHeader02 sid1Data sid1Header01 sid1Id\&lt;br /&gt;
JSON sid1IgnoreRedirects:0,1 sid1URL&lt;br /&gt;
attr myZoe reAuthJSON message&lt;br /&gt;
attr myZoe reading01JSON charge_level&lt;br /&gt;
attr myZoe reading01Name SOC&lt;br /&gt;
attr myZoe reading02JSON charging&lt;br /&gt;
attr myZoe reading02Name charging&lt;br /&gt;
attr myZoe reading03JSON plugged&lt;br /&gt;
attr myZoe reading03Name plugged&lt;br /&gt;
attr myZoe reading04JSON remaining_range&lt;br /&gt;
attr myZoe reading04Name remaining_range&lt;br /&gt;
attr myZoe reading05JSON last_update&lt;br /&gt;
attr myZoe reading05Name last_update&lt;br /&gt;
attr myZoe requestHeader02 Authorization: Bearer $sid&lt;br /&gt;
attr myZoe sid1Data {&amp;quot;username&amp;quot;:&amp;quot;ZEUSERNAME&amp;quot;,&amp;quot;password&amp;quot;:&amp;quot;ZEPASSWORD&amp;quot;}&lt;br /&gt;
attr myZoe sid1Header01 Content-Type: application/json&lt;br /&gt;
attr myZoe sid1IdJSON token&lt;br /&gt;
attr myZoe sid1IgnoreRedirects 1&lt;br /&gt;
attr myZoe sid1URL https://www.services.renault-ze.com/api/user/login&lt;br /&gt;
attr myZoe timeout 10&lt;br /&gt;
#attr myZoe verbose 7&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The $sid is read by logging in with username and password from JSON {&amp;quot;token&amp;quot;: ..} after detecting an error {&amp;quot;message&amp;quot;: ..}  from a failed request. This example offers no solution for the reading of the VIN from the login request, which in turn is part of the reading URL. It should be possible to read more than just the $sid from the login sequence.&lt;br /&gt;
&lt;br /&gt;
In the special case where a session id is set as a HTTP-Cookie (with the header Set-cookie: in the HTTP response) HTTPMOD offers an even simpler way. With the attribute enableCookies a very basic cookie handling mechanism is activated that stores all cookies that the server sends to the HTTPMOD device and puts them back as cookie headers in the following requests.&lt;br /&gt;
&lt;br /&gt;
For such cases no sidIdRegex and no $sid in a user defined header is necessary.&lt;br /&gt;
&lt;br /&gt;
== Advanced configuration to define a &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; and request additional data with its own request from a device ==&lt;br /&gt;
&lt;br /&gt;
The normal automatic HTTP request that is done repeatedly after the defined interval has elapsed works well in cases where all required readings can be requested in one common HTTP request. If however a device needs individual requests with different URLs or different POST data for each value, then another method is necessary. &lt;br /&gt;
For such cases a &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; option can be defined and the user can either issue FHEM &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; commands each time he needs the reading or the user can set an attribute to request the reading automatically together with the normal iteration.&lt;br /&gt;
For each &amp;lt;code&amp;gt;get&amp;lt;/code&amp;gt; option attributes define an individual URL, optional headers, and post data as well as individual regular expressions and formatting options. &lt;br /&gt;
&lt;br /&gt;
When a get option is defined by attributes, the module allows querying additional values from the device that require individual HTTP-Requests or special parameters to be sent&lt;br /&gt;
&lt;br /&gt;
Extension to the above example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM get01Name MyGetValue &lt;br /&gt;
attr PM get01URL http://MyPoolManager/cgi-bin/directory/webgui.fcgi?special=1?sid=$sid &lt;br /&gt;
attr PM getHeader1 Content-Type: application/json &lt;br /&gt;
attr PM get01Data {&amp;quot;get&amp;quot; :{&amp;quot;30.1234.value&amp;quot;}} &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example defines a get option with the name MyGetValue.&lt;br /&gt;
By issuing &amp;lt;code&amp;gt;get PM MyGetValue&amp;lt;/code&amp;gt; in FHEM, the defined HTTP request is sent to the device.&lt;br /&gt;
The HTTP response is then parsed using the same readingXXName and readingXXRegex attributes as above so&lt;br /&gt;
additional pairs will probably be needed there for additional values.&lt;br /&gt;
&lt;br /&gt;
if you prefer to define the parsing and formatting of readings individually per get command, you can use &lt;br /&gt;
attributes like get01Regex, get01XPath, get01Format, get01OMap and so on just like for reading01...&lt;br /&gt;
&lt;br /&gt;
You can also include parameters / values that are passed to the get command in the request just like for set commands.&lt;br /&gt;
The placeholder $val will be replaced with the value given to the get command or you can specify your own replacement as described above.&lt;br /&gt;
&lt;br /&gt;
If the new get parameter should also be queried regularly, you can define the following optional attributes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
attr PM get01Poll 1&lt;br /&gt;
attr PM get01PollDelay 300&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first attribute includes this reading in the automatic update cycle and the second defines an alternative lower update frequency. When the interval defined initially in the define is over and the normal readings are read from the device, the update function will check for additional get parameters that should be included in the update cycle.&lt;br /&gt;
&lt;br /&gt;
If a PollDelay is specified for a get parameter, the update function also checks if the time passed since it has last read this value is more than the given PollDelay. If not, this reading is skipped and it will be rechecked in the next cycle when interval is over again. So the effective PollDelay will always be a multiple of the interval specified in the initial define.&lt;br /&gt;
&lt;br /&gt;
Please note that each defined get command that is included in the regular update cycle will create its own HTTP request. So if you want to extract several values from the same request, it is much more efficient to do this by defining readingXXName and readingXXRegex, XPath or JSON attributes and to specify an interval and a URL in the define of the HTTPMOD device. &lt;br /&gt;
&lt;br /&gt;
Example for a Siemens webserver provided by Lanhydrock:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
define ozw672 HTTPMOD https://192.168.178.8/api/auth/login.json?user=test&amp;amp;pwd=test 300&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get1Name tempAussen&lt;br /&gt;
attr ozw672 get1URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1960&lt;br /&gt;
attr ozw672 get1Poll 1&lt;br /&gt;
attr ozw672 get1PollDelay 1800&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get2Name tempAussenGemischt&lt;br /&gt;
attr ozw672 get2URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1964&lt;br /&gt;
attr ozw672 get2Poll 1&lt;br /&gt;
attr ozw672 get2PollDelay 1800&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get3Name tempTWW&lt;br /&gt;
attr ozw672 get3URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1996&lt;br /&gt;
attr ozw672 get3Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get4Name tempKesselSoll&lt;br /&gt;
attr ozw672 get4URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1910&lt;br /&gt;
attr ozw672 get4Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get5Name tempKesselRuecklauf&lt;br /&gt;
attr ozw672 get5URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1915&lt;br /&gt;
attr ozw672 get5Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get6Name tempKesselRuecklaufSoll&lt;br /&gt;
attr ozw672 get6URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1916&lt;br /&gt;
attr ozw672 get6Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get7Name anzahlStartsBrenner&lt;br /&gt;
attr ozw672 get7URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1927&lt;br /&gt;
attr ozw672 get7PollDelay 1800&lt;br /&gt;
attr ozw672 get7Poll 1&lt;br /&gt;
&lt;br /&gt;
attr ozw672 get8Name statusKessel&lt;br /&gt;
attr ozw672 get8URL https://192.168.178.8/api/menutree/read_datapoint.json?SessionId=$sid&amp;amp;Id=1898&lt;br /&gt;
attr ozw672 get8Poll 1&lt;br /&gt;
attr ozw672 get8Regex Value&amp;quot;: &amp;quot;([a-zA-Zü ]*)&amp;quot;&lt;br /&gt;
attr ozw672 get8OMap Aus:0, Nachlauf aktiv:5, Freigegeben für TWW:10, Freigegeben für HK:20, In Teillastbetrieb für TWW:40, In Teillastbetrieb für HK:50, In Betrieb für Trinkwasser:90, In Betrieb für Heizkreis:100&lt;br /&gt;
&lt;br /&gt;
attr ozw672 getRegex Value&amp;quot;: &amp;quot;[ ]*([-.0-9]*)&amp;quot;&lt;br /&gt;
&lt;br /&gt;
attr ozw672 reAuthRegex .*session not valid.*&lt;br /&gt;
attr ozw672 sid1IDRegex .*&amp;quot;(.*-.*-.*-[0-9a-z]*).*&lt;br /&gt;
attr ozw672 sid1URL https://192.168.178.8/api/auth/login.json?user=test&amp;amp;pwd=test&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== All attributes ==&lt;br /&gt;
;reading[0-9]+Name&lt;br /&gt;
:the name of a reading to extract with the corresponding readingRegex, readingJSON, readingXPath or readingXPath-Strict&lt;br /&gt;
:Please note that the old syntax &amp;lt;b&amp;gt;readingsName.*&amp;lt;/b&amp;gt; does not work with all features of HTTPMOD and should be avoided. It might go away in a future version of HTTPMOD.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]+Name&lt;br /&gt;
:Name of a get or set command&lt;br /&gt;
:If the HTTP response that is received after the command is parsed with an individual parse option then this name is also used as a reading name. Please note that no individual parsing needs to be defined for a get or set. If no regex, XPath or JSON is specified for the command, then HTTPMOD will try to parse the response using all the defined readingRegex, reading XPath or readingJSON attributes.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+Regex&lt;br /&gt;
:If this attribute is specified, the Regex defined here is used to extract the value from the HTTP Response and assign it to a Reading with the name defined in the (get|set|reading)[0-9]+Name attribute.&lt;br /&gt;
:If this attribute is not specified for an individual Reading or get or set but without the numbers in the middle, e.g. as getRegex or readingRegex, then it applies to all the other readings / get / set commands where no specific Regex is defined.&amp;lt;br&amp;gt;&lt;br /&gt;
:The value to extract should be in a capture group / sub expression e.g. ([\d\.]+) in the above example. Multiple capture groups will create multiple readings (see explanation above)&lt;br /&gt;
:Using this attribute for a set command (setXXRegex) only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
:Please note that the old syntax &amp;lt;code&amp;gt;readingsRegex.*&amp;lt;/code&amp;gt; does not work with all features of HTTPMOD and should be avoided. It might go away in a future version of HTTPMOD.&lt;br /&gt;
:If for get or set commands neither a generic Regex attribute without numbers nor a specific (get|set)[0-9]+Regex attribute is specified and also no XPath or JSON parsing specification is given for the get or set command, then HTTPMOD tries to use the parsing definitions for general readings defined in reading[0-9]+Name, reading[0-9]+Regex or XPath or JSON attributes and assigns the Readings that match here.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+RegOpt&lt;br /&gt;
:Lets the user specify regular expression modifiers. For example if the same regular expression should be matched as often as possible in the HTTP response, then you can specify RegOpt g which will case the matching to be done as /regex/g&lt;br /&gt;
:The results will be trated the same way as multiple capture groups so the reading name will be extended with -number. &lt;br /&gt;
:For other possible regular expression modifiers see http://perldoc.perl.org/perlre.html#Modifiers&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+XPath&lt;br /&gt;
:defines an xpath to one or more values when parsing HTML data (see examples above)&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;get|set|reading[0-9]+XPath-Strict&lt;br /&gt;
:defines an xpath to one or more values when parsing XML data (see examples above)&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+AutoNumLen&lt;br /&gt;
:In cases where a regular expression or an XPath results in multiple results and these results are stored in a common reading name with extension -number, then you can modify the format of this number to have a fixed length with leading zeros. AutoNumLen 3 for example will lead to reading names ending with -001 -002 and so on.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]+AlwaysNum&lt;br /&gt;
:if set to 1 this attributes forces reading names to end with a -1, -01 (depending on the above described AutoNumLen) even if just one value is parsed.&lt;br /&gt;
&lt;br /&gt;
;get|set|reading[0-9]+JSON&lt;br /&gt;
:defines a path to the JSON object wanted by concatenating the object names. See the above example.&lt;br /&gt;
:If you don&#039;t know the paths, then start by using extractAllJSON and the use the names of the readings as values for the JSON attribute.&amp;lt;br&amp;gt;&lt;br /&gt;
:Please don&#039;t forget to also specify a name for a reading, get or set. &lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]*RecombineExpr&lt;br /&gt;
:defines an expression that is used in an eval to compute one reading value out of the list of matches.&lt;br /&gt;
:This is supposed to be used for regexes or xpath specifications that produce multiple results if only one result that combines them is wanted. The list of matches will be in the variable @matchlist.&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;get[0-9]*CheckAllReadings&lt;br /&gt;
:this attribute modifies the behavior of HTTPMOD when the HTTP Response of a get command is parsed. &amp;lt;br&amp;gt;&lt;br /&gt;
:If this attribute is set to 1, then additionally to the matching of the corresponding get specific regex (get[0-9]*Regex), XPath or JSON attribute also all the reading names and parse definitions defined in Reading[0-9]+Name and Reading[0-9]+Regex, XPath or JSON attributes are checked and if they match, the coresponding Readings are assigned as well.&lt;br /&gt;
:Please note that this does not mean that get01CheckAllReadings will cause a get02Regex to be used. Only the corresponding get01Regex will be used but additionally all the readingXYRegex attributes.&lt;br /&gt;
:This is automatically done if a get or set command is defined without its own parse attributes.&lt;br /&gt;
&lt;br /&gt;
;(get|reading)[0-9]*OExpr&lt;br /&gt;
:defines an optional expression that is used in an eval to compute / format a readings value after parsing an HTTP response&lt;br /&gt;
:The raw value from the parsing will be in the variable $val.&lt;br /&gt;
:If specified as readingOExpr then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*Expr.&lt;br /&gt;
:Please note that the old syntax &amp;lt;b&amp;gt;readingsExpr.*&amp;lt;/b&amp;gt; does not work with all features of HTTPMOD and should be avoided. It might go away in a future version of HTTPMOD.&lt;br /&gt;
&lt;br /&gt;
;(get|reading)[0-9]*Expr&lt;br /&gt;
:This is the old syntax for (get|reading)[0-9]*OExpr. It should be replaced by (get|reading)[0-9]*OExpr. The set command upgradeAttributes which becomes visible when the attribute enableControlSet is set to 1, can do this renaming automatically.&lt;br /&gt;
&lt;br /&gt;
;(get|reading)[0-9]*OMap&lt;br /&gt;
:Map that defines a mapping from raw value parsed to visible values like &amp;quot;0:mittig, 1:oberhalb, 2:unterhalb&amp;quot;.&lt;br /&gt;
:If specified as readingOMap then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*Map.&amp;lt;br&amp;gt;&lt;br /&gt;
:The individual options in a map are separated by a komma and an optional space. Spaces are allowed to appear in a visible value however kommas are not possible.&lt;br /&gt;
&lt;br /&gt;
;(get|reading)[0-9]*Map&lt;br /&gt;
:This is the old syntax for (get|reading)[0-9]*OMap. It should be replaced by (get|reading)[0-9]*OMap. The set command upgradeAttributes which becomes visible when the attribute enableControlSet is set to 1, can do this renaming automatically.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]*Format&lt;br /&gt;
:Defines a format string that will be used in sprintf to format a reading value.&lt;br /&gt;
:If specified without the numbers in the middle e.g. as readingFormat then the attribute value is a default for all other readings that don&#039;t specify an explicit reading[0-9]*Format.&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]*Decode&lt;br /&gt;
:defines an encoding to be used in a call to the perl function decode to convert the raw data string read from the device to a reading. &lt;br /&gt;
:This can be used if the device delivers strings in an encoding like cp850 instead of utf8.&lt;br /&gt;
:If your reading values contain Umlauts and they are shown as strange looking icons then you probably need to use this feature.&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;(get|set|reading)[0-9]*Encode&lt;br /&gt;
:defines an encoding to be used in a call to the perl function encode to convert the raw data string read from the device to a reading. &lt;br /&gt;
:This can be used if the device delivers strings in an encoding like cp850 and after decoding it you want to reencode it to e.g. utf8.&lt;br /&gt;
:If your reading values contain Umlauts and they are shown as strange looking icons then you probably need to use this feature.&lt;br /&gt;
:Using this attribute for a set command only makes sense if you want to parse the HTTP response to the HTTP request that the set command sent by defining the attribute setXXParseResponse.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*URL&lt;br /&gt;
:URL to be requested for the set or get command. If this option is missing, the URL specified during define will be used.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*Data&lt;br /&gt;
:Data to be sent to the device as POST data when the get or set command is executed. if this attribute is not specified, an HTTP GET method will be used instead of an HTTP POST&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*NoData&lt;br /&gt;
:can be used to override a more generic attribute that specifies POST data for all get or set commands. With NoData no data is sent and therefor the request will be an HTTP GET.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*Header.*&lt;br /&gt;
:HTTP Headers to be sent to the device when the set is executed&lt;br /&gt;
&lt;br /&gt;
;requestHeader.*&lt;br /&gt;
:Define an optional additional HTTP Header to set in the HTTP request of the main loop&lt;br /&gt;
&lt;br /&gt;
;requestData&lt;br /&gt;
:optional POST Data to be sent in the request of the main loop. If not defined, it will be an HTTP GET request as defined in HttpUtils which is used by this module&lt;br /&gt;
&lt;br /&gt;
;get[0-9]+Poll&lt;br /&gt;
:if set to 1 the get is executed automatically during the normal update cycle (after the interval provided in the define command has elapsed)&lt;br /&gt;
&lt;br /&gt;
;get[0-9]+PollDelay&lt;br /&gt;
:if the value should not be read in each iteration (after the interval given to the define command), then a minimum delay can be specified with this attribute. This has only an effect if the above Poll attribute has also been set. Every time the update function is called, it checks if since this get has been read the last time, the defined delay has elapsed. If not, then it is skipped this time.&lt;br /&gt;
:PollDelay can be specified as seconds or as x[0-9]+ which means a multiple of the interval in the define command.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*TextArg&lt;br /&gt;
:For a get command this defines that the command accepts a text value after the option name. By default a get command doesn&#039;t accept optional values after the command name. &lt;br /&gt;
:If TextArg is specified and a value is passed after the get name then this value can then be used in a request URL, header or data as replacement for $val or in a user defined replacement that uses the internal &amp;quot;value&amp;quot; ($hash-&amp;gt;{value}).&lt;br /&gt;
:If used for a set command then it defines that the value to be set doesn&#039;t require any validation / conversion. &lt;br /&gt;
:The raw value is passed on as text to the device. By default a set command expects a numerical value or a text value that is converted to a numeric value using a map.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Min&lt;br /&gt;
:Minimum value for input validation. &lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Max&lt;br /&gt;
:Maximum value for input validation. &lt;br /&gt;
&lt;br /&gt;
;set[0-9]+IExpr&lt;br /&gt;
:Perl Expression to compute the raw value to be sent to the device from the input value passed to the set.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Expr&lt;br /&gt;
:This is the old syntax for (get|reading)[0-9]*IExpr. It should be replaced by (get|reading)[0-9]*IExpr. The set command upgradeAttributes which becomes visible when the attribute enableControlSet is set to 1, can do this renaming automatically.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+IMap&lt;br /&gt;
:Map that defines a mapping from raw to input values like &amp;quot;0:mittig, 1:oberhalb, 2:unterhalb&amp;quot;. This attribute atomatically creates a hint for FHEMWEB so the user can choose one of the input values.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Map&lt;br /&gt;
:This is the old syntax for (get|reading)[0-9]*IMap. It should be replaced by (get|reading)[0-9]*IMap. The set command upgradeAttributes which becomes visible when the attribute enableControlSet is set to 1, can do this renaming automatically.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]+Hint&lt;br /&gt;
:Explicit hint for fhemWEB that will be returned when set ? is seen. Can be used to get a slider or a list of values to choose from.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]*NoArg&lt;br /&gt;
:Defines that this set option doesn&#039;t require arguments. It allows sets like &amp;quot;on&amp;quot; or &amp;quot;off&amp;quot; without further values.&lt;br /&gt;
&lt;br /&gt;
;set[0-9]*ParseResponse&lt;br /&gt;
:defines that the HTTP response to the set will be parsed as if it was the response to a get command.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*URLExpr&lt;br /&gt;
:Defines a Perl expression to specify the HTTP Headers for this request. This overwrites any other header specification and should be used carefully only if needed. The original Header is availabe as $old. Typically this feature is not needed and it might go away in future versions of HTTPMOD. Please use the &amp;quot;replacement&amp;quot; attributes if you want to pass additional variable data to a web service. &lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*DatExpr&lt;br /&gt;
:Defines a Perl expression to specify the HTTP Post data for this request. This overwrites any other post data specification and should be used carefully only if needed. The original Data is availabe as $old. Typically this feature is not needed and it might go away in future versions of HTTPMOD. Please use the &amp;quot;replacement&amp;quot; attributes if you want to pass additional variable data to a web service. &lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*HdrExpr&lt;br /&gt;
:Defines a Perl expression to specify the URL for this request. This overwrites any other URL specification and should be used carefully only if needed. The original URL is availabe as $old. Typically this feature is not needed and it might go away in future versions of HTTPMOD. Please use the &amp;quot;replacement&amp;quot; attributes if you want to pass additional variable data to a web service.           &lt;br /&gt;
&lt;br /&gt;
;ReAuthRegex&lt;br /&gt;
:regular Expression to match an error page indicating that a session has expired and a new authentication for read access needs to be done. &lt;br /&gt;
:This attribute only makes sense if you need a forms based authentication for reading data and if you specify a multi step login procedure based on the sid.. attributes.&lt;br /&gt;
:This attribute is used for all requests. For set and get operations you can however specify individual reAuthRegexes with the (get|set)[0-9]*ReAuthRegex attributes.&lt;br /&gt;
&lt;br /&gt;
;(get|set)[0-9]*ReAuthRegex&lt;br /&gt;
:Regex that will detect when a session has expired during a set operation and a new login needs to be performed.&lt;br /&gt;
:It works like the global reAuthRegex but is used for set operations.&lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*URL&lt;br /&gt;
:different URLs or one common URL to be used for each step of an optional login procedure. &lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*IDRegex&lt;br /&gt;
:different Regexes per login procedure step or one common Regex for all steps to extract the session ID from the HTTP response&lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*Data.*&lt;br /&gt;
:data part for each step to be sent as POST data to the corresponding URL&lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*Header.*&lt;br /&gt;
:HTTP Headers to be sent to the URL for the corresponding step&lt;br /&gt;
&lt;br /&gt;
;sid[0-9]*IgnoreRedirects&lt;br /&gt;
:Tells the HttpUtils to not follow HTTP Redirects for this Request. Might be needed for some devices that set a session cookie within a 303 Redirect.&lt;br /&gt;
&lt;br /&gt;
;clearSIdBeforeAuth&lt;br /&gt;
:will set the session id to &amp;quot;&amp;quot; before doing the authentication steps&lt;br /&gt;
&lt;br /&gt;
;authRetries&lt;br /&gt;
:number of retries for authentication procedure - defaults to 1&lt;br /&gt;
&lt;br /&gt;
;replacement[0-9]*Regex&lt;br /&gt;
:Defines a replacement to be applied to an HTTP request header, data or URL before it is sent. This allows any part of the request to be modified based on a reading, an internal or an expression.&lt;br /&gt;
:The regex defines which part of a header, data or URL should be replaced. The replacement is defined with the following attributes:&lt;br /&gt;
&lt;br /&gt;
;replacement[0-9]*Mode&lt;br /&gt;
:Defines how the replacement should be done and what replacementValue means. Valid options are text, reading, internal and expression.&lt;br /&gt;
&lt;br /&gt;
;replacement[0-9]*Value&lt;br /&gt;
:Defines the replacement. If the corresponding replacementMode is &amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;, then value is a static text that is used as the replacement.&lt;br /&gt;
:If replacementMode is &amp;lt;code&amp;gt;reading&amp;lt;/code&amp;gt; then Value can be the name of a reading of this device or it can be a reading of a different device referred to by devicename:reading.&lt;br /&gt;
:If replacementMode is &amp;lt;code&amp;gt;internal&amp;lt;/code&amp;gt; the Value can be the name of an internal of this device or it can be an internal of a different device referred to by devicename:internal.&lt;br /&gt;
:If replacementMode is &amp;lt;code&amp;gt;expression&amp;lt;/code&amp;gt; the the Value is treated as a Perl expression that computes the replacement value. The expression can use $1, $2 and so on to refer to capture groups of the corresponding regex that is matched against the original URL, header or post data.&lt;br /&gt;
:If replacementMode is &amp;lt;code&amp;gt;key&amp;lt;/code&amp;gt; then the module will use a value from a key / value pair that is stored in an obfuscated form in the file system with the set storeKeyValue command. This might be useful for storing passwords.&lt;br /&gt;
&lt;br /&gt;
;[gs]et[0-9]*Replacement[0-9]*Value&lt;br /&gt;
:This attribute can be used to override the replacement value for a specific get or set.&lt;br /&gt;
&lt;br /&gt;
;get|reading[0-9]*MaxAge&lt;br /&gt;
:Defines how long a reading is valid before it is automatically overwritten with a replacement when the read function is called the next time.&lt;br /&gt;
&lt;br /&gt;
;get|reading[0-9]*MaxAgeReplacement&lt;br /&gt;
:specifies the replacement for MaxAge - either as a static text or as a perl expression.&lt;br /&gt;
&lt;br /&gt;
;get|reading[0-9]*MaxAgeReplacementMode&lt;br /&gt;
:specifies how the replacement is interpreted: can be text, expression and delete.&lt;br /&gt;
&lt;br /&gt;
;get|reading[0-9]*DeleteIfUnmatched&lt;br /&gt;
:If set to 1 this attribute causes certain readings to be deleted when the parsing of the website does not match the specified reading. Internally HTTPMOD remembers which kind of operation created a reading (update, Get01, Get02 and so on). Specified readings will only be deleted if the same operation does not parse this reading again. This is especially useful for parsing that creates several matches / readings and the number of matches can vary from request to request. For example if reading01Regex creates 4 readings in one update cycle and in the next cycle it only matches two times then the readings containing the remaining values from the last round will be deleted.&lt;br /&gt;
:Please note that this mechanism will not work in all cases after a restart. Especially when a get definition does not contain its own parsing definition but ExtractAllJSON or relies on HTTPMOD to use all defined reading.* attributes to parse the responsee to a get command, old readings might not be deleted after a restart of fhem.&lt;br /&gt;
;get|reading[0-9]*DeleteOnError&lt;br /&gt;
:If set to 1 this attribute causes certain readings to be deleted when the website can not be reached and the HTTP request returns an error. Internally HTTPMOD remembers which kind of operation created a reading (update, Get01, Get02 and so on). Specified readings will only be deleted if the same operation returns an error.&lt;br /&gt;
The same restrictions as for DeleteIfUnmatched apply regarding a fhem restart.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;httpVersion&lt;br /&gt;
:defines the HTTP-Version to be sent to the server. This defaults to 1.0.&lt;br /&gt;
&lt;br /&gt;
;sslVersion&lt;br /&gt;
:defines the SSL Version for the negotiation with the server. The attribute is evaluated by HttpUtils. If it is not specified, HttpUtils assumes SSLv23:!SSLv3:!SSLv2&lt;br /&gt;
&lt;br /&gt;
;sslArgs&lt;br /&gt;
:defines a list that is converted to a key / value hash and gets passed to HttpUtils. To avoid certificate validation for broken servers you can for example specify &lt;br /&gt;
:&amp;lt;code&amp;gt;attr myDevice sslArgs SSL_verify_mode,SSL_VERIFY_NONE&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;alignTime&lt;br /&gt;
:Aligns each periodic read request for the defined interval to this base time. This is typcally something like 00:00 (see the FHEM at command)&lt;br /&gt;
&lt;br /&gt;
;noShutdown&lt;br /&gt;
:pass the noshutdown flag to HTTPUtils for webservers that need it (some embedded webservers only deliver empty pages otherwise)&lt;br /&gt;
&lt;br /&gt;
;disable&lt;br /&gt;
:stop doing automatic HTTP requests while this attribute is set to 1&lt;br /&gt;
&lt;br /&gt;
;enableControlSet&lt;br /&gt;
:enables the built in set commands interval, stop, start, reread, upgradeAttributes, storeKeyValue.&lt;br /&gt;
&lt;br /&gt;
;enableCookies&lt;br /&gt;
:enables the built in cookie handling if set to 1. With cookie handling each HTTPMOD device will remember cookies that the server sets and send them back to the server in the following requests. &lt;br /&gt;
:This simplifies session magamenet in cases where the server uses a session ID in a cookie. In such cases enabling cookies should be sufficient and no sidRegex and no manual definition of a cookie header should be necessary.&lt;br /&gt;
&lt;br /&gt;
;showMatched&lt;br /&gt;
:if set to 1 then HTTPMOD will create a reading with the name MATCHED_READINGS that contains the names of all readings that could be matched in the last request as well as UNMATCHED_READINGS and LAST_REQUEST.&lt;br /&gt;
&lt;br /&gt;
;showError&lt;br /&gt;
:if set to 1 then HTTPMOD will create a reading and event with the Name LAST_ERROR that contains the error message of the last error returned from HttpUtils. &lt;br /&gt;
&lt;br /&gt;
;removeBuf&lt;br /&gt;
:if set to 1 then HTTPMOD removes the internal named buf when a HTTP-response has been received. &lt;br /&gt;
:$hash-&amp;gt;{buf} is used internally be Fhem httpUtils and in some use cases it is desireable to remove this internal after reception &lt;br /&gt;
:because it contains a very long response which looks ugly in Fhemweb.&lt;br /&gt;
&lt;br /&gt;
;timeout&lt;br /&gt;
:time in seconds to wait for an answer. Default value is 2&lt;br /&gt;
&lt;br /&gt;
;queueDelay&lt;br /&gt;
:HTTP Requests will be sent from a queue in order to avoid blocking when several Requests have to be sent in sequence. This attribute defines the delay between calls to the function that handles the send queue. It defaults to one second.&lt;br /&gt;
&lt;br /&gt;
;queueMax&lt;br /&gt;
:Defines the maximum size of the send queue. If it is reached then further HTTP Requests will be dropped and not be added to the queue&lt;br /&gt;
&lt;br /&gt;
;minSendDelay&lt;br /&gt;
:Defines the minimum time between two HTTP Requests.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* Example: This example uses many features of HTTPMOD [https://forum.fhem.de/index.php?topic=135666.0 🎸 Owntone (Musik-Player) mit FHEM steuern]&lt;br /&gt;
* Example: [https://www.goingelectric.de/forum/viewtopic.php?f=57&amp;amp;t=21462|Abfrage Akkustand einer Zoe und mehr von Renault ZE Services]&lt;br /&gt;
* Example: [[Wetter_und_Wettervorhersagen#Wetter_von_Weather_Underground|Extract weather information from WeatherUnderground]]&lt;br /&gt;
* Example: [[Pollenflug|Pollen count]]&lt;br /&gt;
* Example: [[HTTPMOD Beispielkonfiguration zur Anbindung einer Daikin Klimaanlage mit WLAN-Modul|Connect Daikin aircondition to FHEM]]&lt;br /&gt;
* Example: [[Go-eCharger|Extract information from go-eCharger]]&lt;br /&gt;
* Example: [[Sonnenspeicher|Integration of energy supplies from sonnen (https://sonnenbatterie.de/en/start)]]&lt;br /&gt;
* Example: [https://forum.fhem.de/index.php/topic,95989.msg915870.html#msg915870 Miele 3rd party API für Miele Smarthome Geräte]&lt;br /&gt;
* Example: [https://forum.fhem.de/index.php/topic,84215.msg918662.html#msg918662 Honeywell Evohome Totalconnect]&lt;br /&gt;
* Example: [https://forum.fhem.de/index.php/topic,78613.msg889015.html#msg889015 Tigo Energy Integration]&lt;br /&gt;
* Example: [https://forum.fhem.de/index.php/topic,78613.msg708518.html#msg708518 Ecowater]&lt;br /&gt;
* {{Link2Forum|Topic=17804|LinkText=Thread}} in FHEM Forum that discusses the first version of this module &lt;br /&gt;
* {{Link2Forum|Topic=29471|LinkText=Thread}} in FHEM Forum that discusses the second major version of this module &lt;br /&gt;
* {{Link2Forum|Topic=45176|LinkText=Thread}} in FHEM Forum that discusses the third major version of this module &lt;br /&gt;
* [http://perldoc.perl.org/perlretut.html Introduction to regular expressions]&lt;br /&gt;
* [http://portswigger.net/burp/ BurpSuite]: Tool (local proxy) to help analyze http traffic&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:IP Components]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40077</id>
		<title>DevelopmentFHEMWEB-API</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40077"/>
		<updated>2025-03-27T13:47:11Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* FW_directNotify */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Randnotiz|RNTyp=r|RNText=Bitte beachten: Diese Auflistung von Funktionen wird nicht aktiv durch alle Entwickler gepflegt. Es kann daher keine Garantie auf Vollständigkeit und Korrektheit gegeben werden, entscheidend ist immer der aktuelle Programm-Code.&lt;br /&gt;
&amp;lt;hr /&amp;gt;&lt;br /&gt;
Please note: This list of functions is not official maintained by all developers. So there is no guarantee for completeness and correctness, at least the program code itself is relevant.&lt;br /&gt;
}}&lt;br /&gt;
Dieses Seite soll die verfügbaren Funktionen zugänglicher machen, um das Beisteuern von Funktionalität zu erleichtern.&lt;br /&gt;
&lt;br /&gt;
Natürlich hat diese Seite keinen Anspruch auf Vollständigkeit und jeder ist aufgefordert mitzuarbeiten, sobald er beim Stöbern über eine Funktion stolpert, die auch andere interessieren könnte.&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Server (pgm2) Perl-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_ME&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| URL-Pfad unter dem der FHEMWEB-Server via HTTP erreichbar ist.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;/fhem&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_dir&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| Das Basisverzeichnis auf dem lokalen Filesystem des FHEMWEB-Servers.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;./www&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_CSRF&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält das aktuell gültige CSRF-Token als HTTP URL-Parameter für die aktuelle Verbindung oder bleibt leer.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;&amp;amp;fwcsrf=csrf_165435377333711&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_detail&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuell erzeugten Detailansicht. &lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Licht_Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_room&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Raumnamen der aktuell erzeugten Raumansicht.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_wname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen FHEMWEB-Instanz.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_cname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen Verbindung (Connection-Name).&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB_192.168.179.65_54406&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_userAgent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den User-Agent, welcher für den aktuellen Aufruf übermittelt wurde.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== FW_makeImage ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage($icon, $text, $class);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_makeImage gibt HTML-Code zurück, der ein Icon aus dem dem FHEMWEB-Iconpool beschreibt.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, mit optionaler Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname&amp;gt;[@fillcolor&amp;gt;[@strokecolor&amp;gt;]]&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
Die Farbangaben ersetzen bei SVG Dateien nur die Farbwerte für stroke und fill, sofern diese in der SVG Datei &amp;lt;code&amp;gt;#000000&amp;lt;/code&amp;gt; sind. Wird also eine Angabe &amp;lt;code&amp;gt;fill:#000000&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fill=#000000&amp;lt;/code&amp;gt; im Quelltext des SVG gefunden, wird dies ersetzt durch &amp;lt;code&amp;gt;fill:fillcolor&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;fill=fillcolor&amp;lt;/code&amp;gt;, je nachdem ob ein Doppelpunkt oder Gleichheitszeichen vorhanden war.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine SVG-Datei verweist, dann wird &amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt; dem svg-HTML-Tag hinzugefügt. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;svg ... data-txt=&amp;quot;$text&amp;quot; ... &amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine Datei verweist die nicht auf &#039;&#039;.svg&#039;&#039; endet, dann wird der Pfad von der Datei hinter &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; als &amp;lt;code&amp;gt;&amp;lt;img&amp;gt;&amp;lt;/code&amp;gt; eingebunden. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;img ... src=&amp;quot;...&amp;quot; alt=&amp;quot;$text&amp;quot; title=&amp;quot;$text&amp;quot;&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$class&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine CSS-Klasse angibt.&lt;br /&gt;
Wird der Parameter angegeben, wird dem &amp;lt;img&amp;gt; oder &amp;lt;svg&amp;gt; &amp;lt;code&amp;gt;class=&amp;quot;$class&amp;quot;&amp;lt;/code&amp;gt; hinzugefügt. Ist der Parameter &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; oder nicht übergeben, so wird kein &amp;lt;code&amp;gt;class&amp;lt;/code&amp;gt;-Attribut hinzugefügt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$html&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
|| Wenn &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039; mit einem Iconnamen aus dem FHEMWEB-Iconpool übereinstimmt, wird der HTML-Code zum Einbinden des Icons zurückgegeben.&lt;br /&gt;
Mit nachgestellter Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname@color&amp;gt;&#039;&amp;lt;/code&amp;gt;, wird das Icon entsprechend eingefärbt.&lt;br /&gt;
Entspricht die Zeichenkette keinem Iconnamen, wird die Zeichenkette zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage(&#039;fts_shutter_10@red&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_directNotify ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify($filter, $fhemweb_instance, $javascript, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_directNotify führt Javascript auf einer longpoll FHEMWEB-Instanz aus, ohne Events zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$filter&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die mit &amp;quot;FILTER=&amp;quot; beginnt &amp;lt;code&amp;gt;&amp;quot;FILTER=&amp;lt;filter expression&amp;gt;&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$fhemweb_instance&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine longpoll FHEMWEB-Instanz &amp;lt;code&amp;gt;&amp;quot;#FHEMWEB:&amp;lt;FHEMWEB devicename&amp;gt;&amp;quot;&amp;lt;/code&amp;gt; angibt.&amp;lt;br&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$javascript&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die ein Javascript angibt.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;&amp;quot;&amp;quot;&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, leer oder (noch zu präzisieren).&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;location.reload(&#039;true&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* siehe auch {{Link2Forum|Topic=48736|Message=404497|LinkText=Forenthread}} und https://forum.fhem.de/index.php?topic=134208.0&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Client (pgm2) Javascript-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_root&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| FHEM-Ursprungsverzeichnis, steht auch im head-Element als Attribut &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_version&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Ein assoziatives Array, das die Versionszeichenketten der geladenen Javascript-Dateien enthält.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== FW_cmd ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(arg, callback);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_cmd führt einen Befehl auf dem FHEMWEB-Server aus.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;arg&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die einem Befehl an den Server entspricht.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn der auf dem FHEM-Server ausgeführte Befehl einen Rückgabewert enthält, der weiter verarbeitet werden soll.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(FW_root+&#039;?cmd={FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;amp;XHR=1&#039;, function(data){FW_okDialog(data)});&amp;lt;/syntaxhighlight&amp;gt; Das Beispiel holt sich vom Server den HTML-Code des Icon &amp;quot;fts_shutter_10&amp;quot; und zeigt das Icon in einem Popup-Fenster an. Der vom Server auszuführende Befehl lautet &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; inline&amp;gt;{FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;lt;/syntaxhighlight&amp;gt;. An die Callback-Funktion &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; inline&amp;gt;function(data){FW_okDialog(data)}&amp;lt;/syntaxhighlight&amp;gt; wird der Rückgabewert vom Server als Parameter übergeben und steht als Variable &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
* Will man mehrere Perl-Kommandos senden, kann es hilfreich sein die Daten vor der Übertragung BASE64 zu codieren. Im Kern nutzt man eine Wrapperfunktion für FW_cmd:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function FW_cmd_perlcode(arg, callback, rep) {&lt;br /&gt;
  let perlcode = &amp;quot;eval(MIME::Base64::decode_base64(&#039;&amp;quot; +btoa(arg)+ &amp;quot;&#039;))&amp;quot;;&lt;br /&gt;
  return FW_cmd(FW_root + &#039;?cmd={&#039; + perlcode + &#039;}&amp;amp;XHR=1&#039;, callback, rep);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Weitere Details findet man im Forumspost: https://forum.fhem.de/index.php?topic=141230.0&lt;br /&gt;
&lt;br /&gt;
=== FW_errmsg ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(text, timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_errmsg zeigt eine Meldung im FHEMWEB-Client (Browser) an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Anzeigedauer der Zeichenkette in Millisekunden.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_okDialog ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(txt, parent, removeFn);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_okDialog öffnet im FHEMWEB-Client (Browser) ein modales Popup-Fenster.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die im Popup-Fenster angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;parent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Objekt zur Verankerung des Popup-Fensters, default ist das body-Element. Eine FHEM-Widget-Schnittstelle wird interpretiert.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;returnFn&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die beim Schliessen des Popup-Fensters ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;#!&amp;quot; onclick=&amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;&amp;gt;Hello&amp;lt;/a&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll das Popup-Fenster aus einem Hyperlink heraus aufgerufen werden, dann ist darauf zu achten, dass ein ungültiger Anchor Tag verwendet wird. In diesem Beispiel &amp;quot;!&amp;quot;. Dadurch wird ein unkontrolliertes Scrollen der Webseite effektiv verhindert und der Dialog erscheint genau dort wo man ihn haben möchte.&lt;br /&gt;
&lt;br /&gt;
=== loadScript ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(scriptname, callback, force);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
loadScript läd im FHEMWEB-Client (Browser) ein Javascript nach.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;scriptname&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die den Sriptnamen enthält.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn das Script geladen wurde.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;force&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Flag, wenn gesetzt, wird das Laden erzwungen.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(&amp;quot;pgm2/jquery.knob.min.js&amp;quot;, function(){&amp;lt;Javascript-Code&amp;gt;});&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== log ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(text);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
log zeigt einen Logeintrag in der Konsole des Browsers an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(&#039;Hello world!&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
* [[DevelopmentFHEMWEB|Development FHEMWEB (englischsprachig)]]&lt;br /&gt;
* [[DevelopmentModuleAPI]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40076</id>
		<title>DevelopmentFHEMWEB-API</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40076"/>
		<updated>2025-03-27T09:22:24Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* FW_cmd */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Randnotiz|RNTyp=r|RNText=Bitte beachten: Diese Auflistung von Funktionen wird nicht aktiv durch alle Entwickler gepflegt. Es kann daher keine Garantie auf Vollständigkeit und Korrektheit gegeben werden, entscheidend ist immer der aktuelle Programm-Code.&lt;br /&gt;
&amp;lt;hr /&amp;gt;&lt;br /&gt;
Please note: This list of functions is not official maintained by all developers. So there is no guarantee for completeness and correctness, at least the program code itself is relevant.&lt;br /&gt;
}}&lt;br /&gt;
Dieses Seite soll die verfügbaren Funktionen zugänglicher machen, um das Beisteuern von Funktionalität zu erleichtern.&lt;br /&gt;
&lt;br /&gt;
Natürlich hat diese Seite keinen Anspruch auf Vollständigkeit und jeder ist aufgefordert mitzuarbeiten, sobald er beim Stöbern über eine Funktion stolpert, die auch andere interessieren könnte.&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Server (pgm2) Perl-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_ME&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| URL-Pfad unter dem der FHEMWEB-Server via HTTP erreichbar ist.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;/fhem&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_dir&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| Das Basisverzeichnis auf dem lokalen Filesystem des FHEMWEB-Servers.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;./www&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_CSRF&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält das aktuell gültige CSRF-Token als HTTP URL-Parameter für die aktuelle Verbindung oder bleibt leer.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;&amp;amp;fwcsrf=csrf_165435377333711&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_detail&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuell erzeugten Detailansicht. &lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Licht_Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_room&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Raumnamen der aktuell erzeugten Raumansicht.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_wname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen FHEMWEB-Instanz.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_cname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen Verbindung (Connection-Name).&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB_192.168.179.65_54406&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_userAgent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den User-Agent, welcher für den aktuellen Aufruf übermittelt wurde.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== FW_makeImage ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage($icon, $text, $class);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_makeImage gibt HTML-Code zurück, der ein Icon aus dem dem FHEMWEB-Iconpool beschreibt.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, mit optionaler Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname&amp;gt;[@fillcolor&amp;gt;[@strokecolor&amp;gt;]]&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
Die Farbangaben ersetzen bei SVG Dateien nur die Farbwerte für stroke und fill, sofern diese in der SVG Datei &amp;lt;code&amp;gt;#000000&amp;lt;/code&amp;gt; sind. Wird also eine Angabe &amp;lt;code&amp;gt;fill:#000000&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fill=#000000&amp;lt;/code&amp;gt; im Quelltext des SVG gefunden, wird dies ersetzt durch &amp;lt;code&amp;gt;fill:fillcolor&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;fill=fillcolor&amp;lt;/code&amp;gt;, je nachdem ob ein Doppelpunkt oder Gleichheitszeichen vorhanden war.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine SVG-Datei verweist, dann wird &amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt; dem svg-HTML-Tag hinzugefügt. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;svg ... data-txt=&amp;quot;$text&amp;quot; ... &amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine Datei verweist die nicht auf &#039;&#039;.svg&#039;&#039; endet, dann wird der Pfad von der Datei hinter &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; als &amp;lt;code&amp;gt;&amp;lt;img&amp;gt;&amp;lt;/code&amp;gt; eingebunden. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;img ... src=&amp;quot;...&amp;quot; alt=&amp;quot;$text&amp;quot; title=&amp;quot;$text&amp;quot;&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$class&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine CSS-Klasse angibt.&lt;br /&gt;
Wird der Parameter angegeben, wird dem &amp;lt;img&amp;gt; oder &amp;lt;svg&amp;gt; &amp;lt;code&amp;gt;class=&amp;quot;$class&amp;quot;&amp;lt;/code&amp;gt; hinzugefügt. Ist der Parameter &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; oder nicht übergeben, so wird kein &amp;lt;code&amp;gt;class&amp;lt;/code&amp;gt;-Attribut hinzugefügt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$html&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
|| Wenn &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039; mit einem Iconnamen aus dem FHEMWEB-Iconpool übereinstimmt, wird der HTML-Code zum Einbinden des Icons zurückgegeben.&lt;br /&gt;
Mit nachgestellter Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname@color&amp;gt;&#039;&amp;lt;/code&amp;gt;, wird das Icon entsprechend eingefärbt.&lt;br /&gt;
Entspricht die Zeichenkette keinem Iconnamen, wird die Zeichenkette zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage(&#039;fts_shutter_10@red&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_directNotify ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify($filter, $fhemweb_instance, $javascript, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_directNotify führt Javascript auf einer longpoll FHEMWEB-Instanz aus, ohne Events zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$filter&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die mit &amp;quot;FILTER=&amp;quot; beginnt &amp;lt;code&amp;gt;&amp;quot;FILTER=&amp;lt;filter expression&amp;gt;&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$fhemweb_instance&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine longpoll FHEMWEB-Instanz &amp;lt;code&amp;gt;&amp;quot;#FHEMWEB:&amp;lt;FHEMWEB devicename&amp;gt;&amp;quot;&amp;lt;/code&amp;gt; angibt.&amp;lt;br&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$javascript&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die ein Javascript angibt.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;&amp;quot;&amp;quot;&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, leer oder (noch zu präzisieren).&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;location.reload(&#039;true&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* siehe auch {{Link2Forum|Topic=48736|Message=404497|LinkText=Forenthread}}&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Client (pgm2) Javascript-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_root&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| FHEM-Ursprungsverzeichnis, steht auch im head-Element als Attribut &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_version&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Ein assoziatives Array, das die Versionszeichenketten der geladenen Javascript-Dateien enthält.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== FW_cmd ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(arg, callback);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_cmd führt einen Befehl auf dem FHEMWEB-Server aus.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;arg&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die einem Befehl an den Server entspricht.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn der auf dem FHEM-Server ausgeführte Befehl einen Rückgabewert enthält, der weiter verarbeitet werden soll.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(FW_root+&#039;?cmd={FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;amp;XHR=1&#039;, function(data){FW_okDialog(data)});&amp;lt;/syntaxhighlight&amp;gt; Das Beispiel holt sich vom Server den HTML-Code des Icon &amp;quot;fts_shutter_10&amp;quot; und zeigt das Icon in einem Popup-Fenster an. Der vom Server auszuführende Befehl lautet &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; inline&amp;gt;{FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;lt;/syntaxhighlight&amp;gt;. An die Callback-Funktion &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; inline&amp;gt;function(data){FW_okDialog(data)}&amp;lt;/syntaxhighlight&amp;gt; wird der Rückgabewert vom Server als Parameter übergeben und steht als Variable &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
* Will man mehrere Perl-Kommandos senden, kann es hilfreich sein die Daten vor der Übertragung BASE64 zu codieren. Im Kern nutzt man eine Wrapperfunktion für FW_cmd:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&lt;br /&gt;
function FW_cmd_perlcode(arg, callback, rep) {&lt;br /&gt;
  let perlcode = &amp;quot;eval(MIME::Base64::decode_base64(&#039;&amp;quot; +btoa(arg)+ &amp;quot;&#039;))&amp;quot;;&lt;br /&gt;
  return FW_cmd(FW_root + &#039;?cmd={&#039; + perlcode + &#039;}&amp;amp;XHR=1&#039;, callback, rep);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Weitere Details findet man im Forumspost: https://forum.fhem.de/index.php?topic=141230.0&lt;br /&gt;
&lt;br /&gt;
=== FW_errmsg ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(text, timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_errmsg zeigt eine Meldung im FHEMWEB-Client (Browser) an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Anzeigedauer der Zeichenkette in Millisekunden.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_okDialog ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(txt, parent, removeFn);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_okDialog öffnet im FHEMWEB-Client (Browser) ein modales Popup-Fenster.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die im Popup-Fenster angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;parent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Objekt zur Verankerung des Popup-Fensters, default ist das body-Element. Eine FHEM-Widget-Schnittstelle wird interpretiert.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;returnFn&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die beim Schliessen des Popup-Fensters ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;#!&amp;quot; onclick=&amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;&amp;gt;Hello&amp;lt;/a&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll das Popup-Fenster aus einem Hyperlink heraus aufgerufen werden, dann ist darauf zu achten, dass ein ungültiger Anchor Tag verwendet wird. In diesem Beispiel &amp;quot;!&amp;quot;. Dadurch wird ein unkontrolliertes Scrollen der Webseite effektiv verhindert und der Dialog erscheint genau dort wo man ihn haben möchte.&lt;br /&gt;
&lt;br /&gt;
=== loadScript ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(scriptname, callback, force);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
loadScript läd im FHEMWEB-Client (Browser) ein Javascript nach.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;scriptname&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die den Sriptnamen enthält.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn das Script geladen wurde.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;force&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Flag, wenn gesetzt, wird das Laden erzwungen.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(&amp;quot;pgm2/jquery.knob.min.js&amp;quot;, function(){&amp;lt;Javascript-Code&amp;gt;});&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== log ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(text);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
log zeigt einen Logeintrag in der Konsole des Browsers an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(&#039;Hello world!&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
* [[DevelopmentFHEMWEB|Development FHEMWEB (englischsprachig)]]&lt;br /&gt;
* [[DevelopmentModuleAPI]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40075</id>
		<title>DevelopmentFHEMWEB-API</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40075"/>
		<updated>2025-03-27T09:19:37Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* FW_cmd */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Randnotiz|RNTyp=r|RNText=Bitte beachten: Diese Auflistung von Funktionen wird nicht aktiv durch alle Entwickler gepflegt. Es kann daher keine Garantie auf Vollständigkeit und Korrektheit gegeben werden, entscheidend ist immer der aktuelle Programm-Code.&lt;br /&gt;
&amp;lt;hr /&amp;gt;&lt;br /&gt;
Please note: This list of functions is not official maintained by all developers. So there is no guarantee for completeness and correctness, at least the program code itself is relevant.&lt;br /&gt;
}}&lt;br /&gt;
Dieses Seite soll die verfügbaren Funktionen zugänglicher machen, um das Beisteuern von Funktionalität zu erleichtern.&lt;br /&gt;
&lt;br /&gt;
Natürlich hat diese Seite keinen Anspruch auf Vollständigkeit und jeder ist aufgefordert mitzuarbeiten, sobald er beim Stöbern über eine Funktion stolpert, die auch andere interessieren könnte.&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Server (pgm2) Perl-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_ME&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| URL-Pfad unter dem der FHEMWEB-Server via HTTP erreichbar ist.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;/fhem&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_dir&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| Das Basisverzeichnis auf dem lokalen Filesystem des FHEMWEB-Servers.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;./www&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_CSRF&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält das aktuell gültige CSRF-Token als HTTP URL-Parameter für die aktuelle Verbindung oder bleibt leer.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;&amp;amp;fwcsrf=csrf_165435377333711&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_detail&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuell erzeugten Detailansicht. &lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Licht_Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_room&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Raumnamen der aktuell erzeugten Raumansicht.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_wname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen FHEMWEB-Instanz.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_cname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen Verbindung (Connection-Name).&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB_192.168.179.65_54406&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_userAgent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den User-Agent, welcher für den aktuellen Aufruf übermittelt wurde.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== FW_makeImage ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage($icon, $text, $class);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_makeImage gibt HTML-Code zurück, der ein Icon aus dem dem FHEMWEB-Iconpool beschreibt.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, mit optionaler Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname&amp;gt;[@fillcolor&amp;gt;[@strokecolor&amp;gt;]]&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
Die Farbangaben ersetzen bei SVG Dateien nur die Farbwerte für stroke und fill, sofern diese in der SVG Datei &amp;lt;code&amp;gt;#000000&amp;lt;/code&amp;gt; sind. Wird also eine Angabe &amp;lt;code&amp;gt;fill:#000000&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fill=#000000&amp;lt;/code&amp;gt; im Quelltext des SVG gefunden, wird dies ersetzt durch &amp;lt;code&amp;gt;fill:fillcolor&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;fill=fillcolor&amp;lt;/code&amp;gt;, je nachdem ob ein Doppelpunkt oder Gleichheitszeichen vorhanden war.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine SVG-Datei verweist, dann wird &amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt; dem svg-HTML-Tag hinzugefügt. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;svg ... data-txt=&amp;quot;$text&amp;quot; ... &amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine Datei verweist die nicht auf &#039;&#039;.svg&#039;&#039; endet, dann wird der Pfad von der Datei hinter &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; als &amp;lt;code&amp;gt;&amp;lt;img&amp;gt;&amp;lt;/code&amp;gt; eingebunden. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;img ... src=&amp;quot;...&amp;quot; alt=&amp;quot;$text&amp;quot; title=&amp;quot;$text&amp;quot;&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$class&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine CSS-Klasse angibt.&lt;br /&gt;
Wird der Parameter angegeben, wird dem &amp;lt;img&amp;gt; oder &amp;lt;svg&amp;gt; &amp;lt;code&amp;gt;class=&amp;quot;$class&amp;quot;&amp;lt;/code&amp;gt; hinzugefügt. Ist der Parameter &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; oder nicht übergeben, so wird kein &amp;lt;code&amp;gt;class&amp;lt;/code&amp;gt;-Attribut hinzugefügt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$html&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
|| Wenn &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039; mit einem Iconnamen aus dem FHEMWEB-Iconpool übereinstimmt, wird der HTML-Code zum Einbinden des Icons zurückgegeben.&lt;br /&gt;
Mit nachgestellter Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname@color&amp;gt;&#039;&amp;lt;/code&amp;gt;, wird das Icon entsprechend eingefärbt.&lt;br /&gt;
Entspricht die Zeichenkette keinem Iconnamen, wird die Zeichenkette zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage(&#039;fts_shutter_10@red&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_directNotify ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify($filter, $fhemweb_instance, $javascript, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_directNotify führt Javascript auf einer longpoll FHEMWEB-Instanz aus, ohne Events zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$filter&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die mit &amp;quot;FILTER=&amp;quot; beginnt &amp;lt;code&amp;gt;&amp;quot;FILTER=&amp;lt;filter expression&amp;gt;&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$fhemweb_instance&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine longpoll FHEMWEB-Instanz &amp;lt;code&amp;gt;&amp;quot;#FHEMWEB:&amp;lt;FHEMWEB devicename&amp;gt;&amp;quot;&amp;lt;/code&amp;gt; angibt.&amp;lt;br&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$javascript&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die ein Javascript angibt.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;&amp;quot;&amp;quot;&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, leer oder (noch zu präzisieren).&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;location.reload(&#039;true&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* siehe auch {{Link2Forum|Topic=48736|Message=404497|LinkText=Forenthread}}&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Client (pgm2) Javascript-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_root&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| FHEM-Ursprungsverzeichnis, steht auch im head-Element als Attribut &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_version&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Ein assoziatives Array, das die Versionszeichenketten der geladenen Javascript-Dateien enthält.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== FW_cmd ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(arg, callback);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_cmd führt einen Befehl auf dem FHEMWEB-Server aus.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;arg&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die einem Befehl an den Server entspricht.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn der auf dem FHEM-Server ausgeführte Befehl einen Rückgabewert enthält, der weiter verarbeitet werden soll.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(FW_root+&#039;?cmd={FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;amp;XHR=1&#039;, function(data){FW_okDialog(data)});&amp;lt;/syntaxhighlight&amp;gt; Das Beispiel holt sich vom Server den HTML-Code des Icon &amp;quot;fts_shutter_10&amp;quot; und zeigt das Icon in einem Popup-Fenster an. Der vom Server auszuführende Befehl lautet &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; inline&amp;gt;{FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;lt;/syntaxhighlight&amp;gt;. An die Callback-Funktion &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; inline&amp;gt;function(data){FW_okDialog(data)}&amp;lt;/syntaxhighlight&amp;gt; wird der Rückgabewert vom Server als Parameter übergeben und steht als Variable &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
* Will man mehrere Perl-Kommandos senden, kann es hilfreich sein die Daten vor der Übertragung BASE64 zu codieren. Wie man dies nutzt behandelt ein Forumspost: https://forum.fhem.de/index.php?topic=141230.0&lt;br /&gt;
&lt;br /&gt;
=== FW_errmsg ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(text, timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_errmsg zeigt eine Meldung im FHEMWEB-Client (Browser) an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Anzeigedauer der Zeichenkette in Millisekunden.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_okDialog ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(txt, parent, removeFn);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_okDialog öffnet im FHEMWEB-Client (Browser) ein modales Popup-Fenster.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die im Popup-Fenster angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;parent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Objekt zur Verankerung des Popup-Fensters, default ist das body-Element. Eine FHEM-Widget-Schnittstelle wird interpretiert.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;returnFn&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die beim Schliessen des Popup-Fensters ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;#!&amp;quot; onclick=&amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;&amp;gt;Hello&amp;lt;/a&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll das Popup-Fenster aus einem Hyperlink heraus aufgerufen werden, dann ist darauf zu achten, dass ein ungültiger Anchor Tag verwendet wird. In diesem Beispiel &amp;quot;!&amp;quot;. Dadurch wird ein unkontrolliertes Scrollen der Webseite effektiv verhindert und der Dialog erscheint genau dort wo man ihn haben möchte.&lt;br /&gt;
&lt;br /&gt;
=== loadScript ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(scriptname, callback, force);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
loadScript läd im FHEMWEB-Client (Browser) ein Javascript nach.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;scriptname&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die den Sriptnamen enthält.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn das Script geladen wurde.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;force&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Flag, wenn gesetzt, wird das Laden erzwungen.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(&amp;quot;pgm2/jquery.knob.min.js&amp;quot;, function(){&amp;lt;Javascript-Code&amp;gt;});&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== log ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(text);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
log zeigt einen Logeintrag in der Konsole des Browsers an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(&#039;Hello world!&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
* [[DevelopmentFHEMWEB|Development FHEMWEB (englischsprachig)]]&lt;br /&gt;
* [[DevelopmentModuleAPI]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40070</id>
		<title>DevelopmentFHEMWEB-API</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40070"/>
		<updated>2025-03-24T15:58:18Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* FW_makeImage */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Randnotiz|RNTyp=r|RNText=Bitte beachten: Diese Auflistung von Funktionen wird nicht aktiv durch alle Entwickler gepflegt. Es kann daher keine Garantie auf Vollständigkeit und Korrektheit gegeben werden, entscheidend ist immer der aktuelle Programm-Code.&lt;br /&gt;
&amp;lt;hr /&amp;gt;&lt;br /&gt;
Please note: This list of functions is not official maintained by all developers. So there is no guarantee for completeness and correctness, at least the program code itself is relevant.&lt;br /&gt;
}}&lt;br /&gt;
Dieses Seite soll die verfügbaren Funktionen zugänglicher machen, um das Beisteuern von Funktionalität zu erleichtern.&lt;br /&gt;
&lt;br /&gt;
Natürlich hat diese Seite keinen Anspruch auf Vollständigkeit und jeder ist aufgefordert mitzuarbeiten, sobald er beim Stöbern über eine Funktion stolpert, die auch andere interessieren könnte.&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Server (pgm2) Perl-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_ME&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| URL-Pfad unter dem der FHEMWEB-Server via HTTP erreichbar ist.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;/fhem&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_dir&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| Das Basisverzeichnis auf dem lokalen Filesystem des FHEMWEB-Servers.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;./www&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_CSRF&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält das aktuell gültige CSRF-Token als HTTP URL-Parameter für die aktuelle Verbindung oder bleibt leer.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;&amp;amp;fwcsrf=csrf_165435377333711&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_detail&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuell erzeugten Detailansicht. &lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Licht_Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_room&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Raumnamen der aktuell erzeugten Raumansicht.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_wname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen FHEMWEB-Instanz.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_cname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen Verbindung (Connection-Name).&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB_192.168.179.65_54406&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_userAgent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den User-Agent, welcher für den aktuellen Aufruf übermittelt wurde.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== FW_makeImage ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage($icon, $text, $class);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_makeImage gibt HTML-Code zurück, der ein Icon aus dem dem FHEMWEB-Iconpool beschreibt.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, mit optionaler Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname&amp;gt;[@fillcolor&amp;gt;[@strokecolor&amp;gt;]]&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
Die Farbangaben ersetzen bei SVG Dateien nur die Farbwerte für stroke und fill, sofern diese in der SVG Datei &amp;lt;code&amp;gt;#000000&amp;lt;/code&amp;gt; sind. Wird also eine Angabe &amp;lt;code&amp;gt;fill:#000000&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fill=#000000&amp;lt;/code&amp;gt; im Quelltext des SVG gefunden, wird dies ersetzt durch &amp;lt;code&amp;gt;fill:fillcolor&amp;lt;/code&amp;gt; bzw. &amp;lt;code&amp;gt;fill=fillcolor&amp;lt;/code&amp;gt;, je nachdem ob ein Doppelpunkt oder Gleichheitszeichen vorhanden war.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine SVG-Datei verweist, dann wird &amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt; dem svg-HTML-Tag hinzugefügt. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;svg ... data-txt=&amp;quot;$text&amp;quot; ... &amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine Datei verweist die nicht auf &#039;&#039;.svg&#039;&#039; endet, dann wird der Pfad von der Datei hinter &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; als &amp;lt;code&amp;gt;&amp;lt;img&amp;gt;&amp;lt;/code&amp;gt; eingebunden. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;img ... src=&amp;quot;...&amp;quot; alt=&amp;quot;$text&amp;quot; title=&amp;quot;$text&amp;quot;&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$class&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine CSS-Klasse angibt.&lt;br /&gt;
Wird der Parameter angegeben, wird dem &amp;lt;img&amp;gt; oder &amp;lt;svg&amp;gt; &amp;lt;code&amp;gt;class=&amp;quot;$class&amp;quot;&amp;lt;/code&amp;gt; hinzugefügt. Ist der Parameter &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; oder nicht übergeben, so wird kein &amp;lt;code&amp;gt;class&amp;lt;/code&amp;gt;-Attribut hinzugefügt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$html&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
|| Wenn &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039; mit einem Iconnamen aus dem FHEMWEB-Iconpool übereinstimmt, wird der HTML-Code zum Einbinden des Icons zurückgegeben.&lt;br /&gt;
Mit nachgestellter Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname@color&amp;gt;&#039;&amp;lt;/code&amp;gt;, wird das Icon entsprechend eingefärbt.&lt;br /&gt;
Entspricht die Zeichenkette keinem Iconnamen, wird die Zeichenkette zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage(&#039;fts_shutter_10@red&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_directNotify ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify($filter, $fhemweb_instance, $javascript, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_directNotify führt Javascript auf einer longpoll FHEMWEB-Instanz aus, ohne Events zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$filter&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die mit &amp;quot;FILTER=&amp;quot; beginnt &amp;lt;code&amp;gt;&amp;quot;FILTER=&amp;lt;filter expression&amp;gt;&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$fhemweb_instance&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine longpoll FHEMWEB-Instanz &amp;lt;code&amp;gt;&amp;quot;#FHEMWEB:&amp;lt;FHEMWEB devicename&amp;gt;&amp;quot;&amp;lt;/code&amp;gt; angibt.&amp;lt;br&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$javascript&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die ein Javascript angibt.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;&amp;quot;&amp;quot;&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, leer oder (noch zu präzisieren).&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;location.reload(&#039;true&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* siehe auch {{Link2Forum|Topic=48736|Message=404497|LinkText=Forenthread}}&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Client (pgm2) Javascript-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_root&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| FHEM-Ursprungsverzeichnis, steht auch im head-Element als Attribut &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_version&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Ein assoziatives Array, das die Versionszeichenketten der geladenen Javascript-Dateien enthält.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== FW_cmd ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(arg, callback);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_cmd führt einen Befehl auf dem FHEMWEB-Server aus.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;arg&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die einem Befehl an den Server entspricht.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn der auf dem FHEM-Server ausgeführte Befehl einen Rückgabewert enthält, der weiter verarbeitet werden soll.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(FW_root+&#039;?cmd={FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;amp;XHR=1&#039;, function(data){FW_okDialog(data)});&amp;lt;/syntaxhighlight&amp;gt; Das Beispiel holt sich vom Server den HTML-Code des Icon &amp;quot;fts_shutter_10&amp;quot; und zeigt das Icon in einem Popup-Fenster an. Der vom Server auszuführende Befehl lautet &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; inline&amp;gt;{FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;lt;/syntaxhighlight&amp;gt;. An die Callback-Funktion &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; inline&amp;gt;function(data){FW_okDialog(data)}&amp;lt;/syntaxhighlight&amp;gt; wird der Rückgabewert vom Server als Parameter übergeben und steht als Variable &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
=== FW_errmsg ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(text, timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_errmsg zeigt eine Meldung im FHEMWEB-Client (Browser) an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Anzeigedauer der Zeichenkette in Millisekunden.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_okDialog ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(txt, parent, removeFn);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_okDialog öffnet im FHEMWEB-Client (Browser) ein modales Popup-Fenster.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die im Popup-Fenster angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;parent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Objekt zur Verankerung des Popup-Fensters, default ist das body-Element. Eine FHEM-Widget-Schnittstelle wird interpretiert.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;returnFn&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die beim Schliessen des Popup-Fensters ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;#!&amp;quot; onclick=&amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;&amp;gt;Hello&amp;lt;/a&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll das Popup-Fenster aus einem Hyperlink heraus aufgerufen werden, dann ist darauf zu achten, dass ein ungültiger Anchor Tag verwendet wird. In diesem Beispiel &amp;quot;!&amp;quot;. Dadurch wird ein unkontrolliertes Scrollen der Webseite effektiv verhindert und der Dialog erscheint genau dort wo man ihn haben möchte.&lt;br /&gt;
&lt;br /&gt;
=== loadScript ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(scriptname, callback, force);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
loadScript läd im FHEMWEB-Client (Browser) ein Javascript nach.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;scriptname&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die den Sriptnamen enthält.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn das Script geladen wurde.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;force&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Flag, wenn gesetzt, wird das Laden erzwungen.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(&amp;quot;pgm2/jquery.knob.min.js&amp;quot;, function(){&amp;lt;Javascript-Code&amp;gt;});&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== log ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(text);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
log zeigt einen Logeintrag in der Konsole des Browsers an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(&#039;Hello world!&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
* [[DevelopmentFHEMWEB|Development FHEMWEB (englischsprachig)]]&lt;br /&gt;
* [[DevelopmentModuleAPI]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40069</id>
		<title>DevelopmentFHEMWEB-API</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40069"/>
		<updated>2025-03-24T15:53:20Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* FW_makeImage */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Randnotiz|RNTyp=r|RNText=Bitte beachten: Diese Auflistung von Funktionen wird nicht aktiv durch alle Entwickler gepflegt. Es kann daher keine Garantie auf Vollständigkeit und Korrektheit gegeben werden, entscheidend ist immer der aktuelle Programm-Code.&lt;br /&gt;
&amp;lt;hr /&amp;gt;&lt;br /&gt;
Please note: This list of functions is not official maintained by all developers. So there is no guarantee for completeness and correctness, at least the program code itself is relevant.&lt;br /&gt;
}}&lt;br /&gt;
Dieses Seite soll die verfügbaren Funktionen zugänglicher machen, um das Beisteuern von Funktionalität zu erleichtern.&lt;br /&gt;
&lt;br /&gt;
Natürlich hat diese Seite keinen Anspruch auf Vollständigkeit und jeder ist aufgefordert mitzuarbeiten, sobald er beim Stöbern über eine Funktion stolpert, die auch andere interessieren könnte.&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Server (pgm2) Perl-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_ME&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| URL-Pfad unter dem der FHEMWEB-Server via HTTP erreichbar ist.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;/fhem&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_dir&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| Das Basisverzeichnis auf dem lokalen Filesystem des FHEMWEB-Servers.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;./www&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_CSRF&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält das aktuell gültige CSRF-Token als HTTP URL-Parameter für die aktuelle Verbindung oder bleibt leer.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;&amp;amp;fwcsrf=csrf_165435377333711&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_detail&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuell erzeugten Detailansicht. &lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Licht_Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_room&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Raumnamen der aktuell erzeugten Raumansicht.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_wname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen FHEMWEB-Instanz.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_cname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen Verbindung (Connection-Name).&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB_192.168.179.65_54406&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_userAgent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den User-Agent, welcher für den aktuellen Aufruf übermittelt wurde.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== FW_makeImage ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage($icon, $text, $class);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_makeImage gibt HTML-Code zurück, der ein Icon aus dem dem FHEMWEB-Iconpool beschreibt.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, mit optionaler Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname&amp;gt;[@color&amp;gt;]&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine SVG-Datei verweist, dann wird &amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt; dem svg-HTML-Tag hinzugefügt. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;svg ... data-txt=&amp;quot;$text&amp;quot; ... &amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine Datei verweist die nicht auf &#039;&#039;.svg&#039;&#039; endet, dann wird der Pfad von der Datei hinter &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; als &amp;lt;code&amp;gt;&amp;lt;img&amp;gt;&amp;lt;/code&amp;gt; eingebunden. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;img ... src=&amp;quot;...&amp;quot; alt=&amp;quot;$text&amp;quot; title=&amp;quot;$text&amp;quot;&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$class&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine CSS-Klasse angibt.&lt;br /&gt;
Wird der Parameter angegeben, wird dem &amp;lt;img&amp;gt; oder &amp;lt;svg&amp;gt; &amp;lt;code&amp;gt;class=&amp;quot;$class&amp;quot;&amp;lt;/code&amp;gt; hinzugefügt. Ist der Parameter &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; oder nicht übergeben, so wird kein &amp;lt;code&amp;gt;class&amp;lt;/code&amp;gt;-Attribut hinzugefügt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$html&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
|| Wenn &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039; mit einem Iconnamen aus dem FHEMWEB-Iconpool übereinstimmt, wird der HTML-Code zum Einbinden des Icons zurückgegeben.&lt;br /&gt;
Mit nachgestellter Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname@color&amp;gt;&#039;&amp;lt;/code&amp;gt;, wird das Icon entsprechend eingefärbt.&lt;br /&gt;
Entspricht die Zeichenkette keinem Iconnamen, wird die Zeichenkette zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage(&#039;fts_shutter_10@red&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_directNotify ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify($filter, $fhemweb_instance, $javascript, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_directNotify führt Javascript auf einer longpoll FHEMWEB-Instanz aus, ohne Events zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$filter&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die mit &amp;quot;FILTER=&amp;quot; beginnt &amp;lt;code&amp;gt;&amp;quot;FILTER=&amp;lt;filter expression&amp;gt;&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$fhemweb_instance&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine longpoll FHEMWEB-Instanz &amp;lt;code&amp;gt;&amp;quot;#FHEMWEB:&amp;lt;FHEMWEB devicename&amp;gt;&amp;quot;&amp;lt;/code&amp;gt; angibt.&amp;lt;br&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$javascript&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die ein Javascript angibt.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;&amp;quot;&amp;quot;&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, leer oder (noch zu präzisieren).&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;location.reload(&#039;true&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* siehe auch {{Link2Forum|Topic=48736|Message=404497|LinkText=Forenthread}}&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Client (pgm2) Javascript-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_root&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| FHEM-Ursprungsverzeichnis, steht auch im head-Element als Attribut &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_version&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Ein assoziatives Array, das die Versionszeichenketten der geladenen Javascript-Dateien enthält.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== FW_cmd ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(arg, callback);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_cmd führt einen Befehl auf dem FHEMWEB-Server aus.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;arg&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die einem Befehl an den Server entspricht.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn der auf dem FHEM-Server ausgeführte Befehl einen Rückgabewert enthält, der weiter verarbeitet werden soll.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(FW_root+&#039;?cmd={FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;amp;XHR=1&#039;, function(data){FW_okDialog(data)});&amp;lt;/syntaxhighlight&amp;gt; Das Beispiel holt sich vom Server den HTML-Code des Icon &amp;quot;fts_shutter_10&amp;quot; und zeigt das Icon in einem Popup-Fenster an. Der vom Server auszuführende Befehl lautet &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; inline&amp;gt;{FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;lt;/syntaxhighlight&amp;gt;. An die Callback-Funktion &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; inline&amp;gt;function(data){FW_okDialog(data)}&amp;lt;/syntaxhighlight&amp;gt; wird der Rückgabewert vom Server als Parameter übergeben und steht als Variable &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
=== FW_errmsg ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(text, timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_errmsg zeigt eine Meldung im FHEMWEB-Client (Browser) an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Anzeigedauer der Zeichenkette in Millisekunden.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_okDialog ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(txt, parent, removeFn);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_okDialog öffnet im FHEMWEB-Client (Browser) ein modales Popup-Fenster.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die im Popup-Fenster angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;parent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Objekt zur Verankerung des Popup-Fensters, default ist das body-Element. Eine FHEM-Widget-Schnittstelle wird interpretiert.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;returnFn&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die beim Schliessen des Popup-Fensters ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;#!&amp;quot; onclick=&amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;&amp;gt;Hello&amp;lt;/a&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll das Popup-Fenster aus einem Hyperlink heraus aufgerufen werden, dann ist darauf zu achten, dass ein ungültiger Anchor Tag verwendet wird. In diesem Beispiel &amp;quot;!&amp;quot;. Dadurch wird ein unkontrolliertes Scrollen der Webseite effektiv verhindert und der Dialog erscheint genau dort wo man ihn haben möchte.&lt;br /&gt;
&lt;br /&gt;
=== loadScript ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(scriptname, callback, force);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
loadScript läd im FHEMWEB-Client (Browser) ein Javascript nach.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;scriptname&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die den Sriptnamen enthält.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn das Script geladen wurde.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;force&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Flag, wenn gesetzt, wird das Laden erzwungen.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(&amp;quot;pgm2/jquery.knob.min.js&amp;quot;, function(){&amp;lt;Javascript-Code&amp;gt;});&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== log ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(text);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
log zeigt einen Logeintrag in der Konsole des Browsers an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(&#039;Hello world!&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
* [[DevelopmentFHEMWEB|Development FHEMWEB (englischsprachig)]]&lt;br /&gt;
* [[DevelopmentModuleAPI]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40068</id>
		<title>DevelopmentFHEMWEB-API</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40068"/>
		<updated>2025-03-24T15:52:56Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* FW_makeImage */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Randnotiz|RNTyp=r|RNText=Bitte beachten: Diese Auflistung von Funktionen wird nicht aktiv durch alle Entwickler gepflegt. Es kann daher keine Garantie auf Vollständigkeit und Korrektheit gegeben werden, entscheidend ist immer der aktuelle Programm-Code.&lt;br /&gt;
&amp;lt;hr /&amp;gt;&lt;br /&gt;
Please note: This list of functions is not official maintained by all developers. So there is no guarantee for completeness and correctness, at least the program code itself is relevant.&lt;br /&gt;
}}&lt;br /&gt;
Dieses Seite soll die verfügbaren Funktionen zugänglicher machen, um das Beisteuern von Funktionalität zu erleichtern.&lt;br /&gt;
&lt;br /&gt;
Natürlich hat diese Seite keinen Anspruch auf Vollständigkeit und jeder ist aufgefordert mitzuarbeiten, sobald er beim Stöbern über eine Funktion stolpert, die auch andere interessieren könnte.&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Server (pgm2) Perl-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_ME&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| URL-Pfad unter dem der FHEMWEB-Server via HTTP erreichbar ist.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;/fhem&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_dir&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| Das Basisverzeichnis auf dem lokalen Filesystem des FHEMWEB-Servers.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;./www&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_CSRF&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält das aktuell gültige CSRF-Token als HTTP URL-Parameter für die aktuelle Verbindung oder bleibt leer.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;&amp;amp;fwcsrf=csrf_165435377333711&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_detail&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuell erzeugten Detailansicht. &lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Licht_Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_room&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Raumnamen der aktuell erzeugten Raumansicht.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_wname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen FHEMWEB-Instanz.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_cname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen Verbindung (Connection-Name).&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB_192.168.179.65_54406&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_userAgent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den User-Agent, welcher für den aktuellen Aufruf übermittelt wurde.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== FW_makeImage ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage($icon, $text, $class);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_makeImage gibt HTML-Code zurück, der ein Icon aus dem dem FHEMWEB-Iconpool beschreibt.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, mit optionaler Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname&amp;gt;[@color&amp;gt;]&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine SVG-Datei verweist, dann wird &amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt; dem svg-HTML-Tag hinzugefügt. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;svg ... data-txt=&amp;quot;$text&amp;quot; ... &amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine Datei verweist die nicht auf &#039;&#039;.svg&#039;&#039; endet, dann wird der Pfad von der Datei hinter &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; als &amp;lt;code&amp;gt;&amp;lt;img&amp;gt;&amp;lt;/code&amp;gt; eingebunden. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;img ... src=&amp;quot;...&amp;quot; alt=&amp;quot;$text&amp;quot; title=&amp;quot;$text&amp;quot;&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$class&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine CSS-Klasse angibt.&lt;br /&gt;
Wir der Parameter angegeben, wird dem &amp;lt;img&amp;gt; oder &amp;lt;svg&amp;gt; &amp;lt;code&amp;gt;class=&amp;quot;$class&amp;quot;&amp;lt;/code&amp;gt; hinzugefügt. Ist der Parameter &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; oder nicht übergeben, so wird kein &amp;lt;code&amp;gt;class&amp;lt;/code&amp;gt;-Attribut hinzugefügt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$html&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
|| Wenn &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039; mit einem Iconnamen aus dem FHEMWEB-Iconpool übereinstimmt, wird der HTML-Code zum Einbinden des Icons zurückgegeben.&lt;br /&gt;
Mit nachgestellter Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname@color&amp;gt;&#039;&amp;lt;/code&amp;gt;, wird das Icon entsprechend eingefärbt.&lt;br /&gt;
Entspricht die Zeichenkette keinem Iconnamen, wird die Zeichenkette zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage(&#039;fts_shutter_10@red&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_directNotify ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify($filter, $fhemweb_instance, $javascript, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_directNotify führt Javascript auf einer longpoll FHEMWEB-Instanz aus, ohne Events zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$filter&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die mit &amp;quot;FILTER=&amp;quot; beginnt &amp;lt;code&amp;gt;&amp;quot;FILTER=&amp;lt;filter expression&amp;gt;&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$fhemweb_instance&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine longpoll FHEMWEB-Instanz &amp;lt;code&amp;gt;&amp;quot;#FHEMWEB:&amp;lt;FHEMWEB devicename&amp;gt;&amp;quot;&amp;lt;/code&amp;gt; angibt.&amp;lt;br&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$javascript&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die ein Javascript angibt.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;&amp;quot;&amp;quot;&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, leer oder (noch zu präzisieren).&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;location.reload(&#039;true&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* siehe auch {{Link2Forum|Topic=48736|Message=404497|LinkText=Forenthread}}&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Client (pgm2) Javascript-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_root&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| FHEM-Ursprungsverzeichnis, steht auch im head-Element als Attribut &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_version&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Ein assoziatives Array, das die Versionszeichenketten der geladenen Javascript-Dateien enthält.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== FW_cmd ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(arg, callback);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_cmd führt einen Befehl auf dem FHEMWEB-Server aus.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;arg&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die einem Befehl an den Server entspricht.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn der auf dem FHEM-Server ausgeführte Befehl einen Rückgabewert enthält, der weiter verarbeitet werden soll.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(FW_root+&#039;?cmd={FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;amp;XHR=1&#039;, function(data){FW_okDialog(data)});&amp;lt;/syntaxhighlight&amp;gt; Das Beispiel holt sich vom Server den HTML-Code des Icon &amp;quot;fts_shutter_10&amp;quot; und zeigt das Icon in einem Popup-Fenster an. Der vom Server auszuführende Befehl lautet &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; inline&amp;gt;{FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;lt;/syntaxhighlight&amp;gt;. An die Callback-Funktion &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; inline&amp;gt;function(data){FW_okDialog(data)}&amp;lt;/syntaxhighlight&amp;gt; wird der Rückgabewert vom Server als Parameter übergeben und steht als Variable &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
=== FW_errmsg ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(text, timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_errmsg zeigt eine Meldung im FHEMWEB-Client (Browser) an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Anzeigedauer der Zeichenkette in Millisekunden.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_okDialog ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(txt, parent, removeFn);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_okDialog öffnet im FHEMWEB-Client (Browser) ein modales Popup-Fenster.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die im Popup-Fenster angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;parent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Objekt zur Verankerung des Popup-Fensters, default ist das body-Element. Eine FHEM-Widget-Schnittstelle wird interpretiert.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;returnFn&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die beim Schliessen des Popup-Fensters ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;#!&amp;quot; onclick=&amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;&amp;gt;Hello&amp;lt;/a&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll das Popup-Fenster aus einem Hyperlink heraus aufgerufen werden, dann ist darauf zu achten, dass ein ungültiger Anchor Tag verwendet wird. In diesem Beispiel &amp;quot;!&amp;quot;. Dadurch wird ein unkontrolliertes Scrollen der Webseite effektiv verhindert und der Dialog erscheint genau dort wo man ihn haben möchte.&lt;br /&gt;
&lt;br /&gt;
=== loadScript ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(scriptname, callback, force);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
loadScript läd im FHEMWEB-Client (Browser) ein Javascript nach.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;scriptname&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die den Sriptnamen enthält.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn das Script geladen wurde.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;force&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Flag, wenn gesetzt, wird das Laden erzwungen.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(&amp;quot;pgm2/jquery.knob.min.js&amp;quot;, function(){&amp;lt;Javascript-Code&amp;gt;});&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== log ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(text);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
log zeigt einen Logeintrag in der Konsole des Browsers an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(&#039;Hello world!&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
* [[DevelopmentFHEMWEB|Development FHEMWEB (englischsprachig)]]&lt;br /&gt;
* [[DevelopmentModuleAPI]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40067</id>
		<title>DevelopmentFHEMWEB-API</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40067"/>
		<updated>2025-03-24T15:52:26Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: /* FW_makeImage */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Randnotiz|RNTyp=r|RNText=Bitte beachten: Diese Auflistung von Funktionen wird nicht aktiv durch alle Entwickler gepflegt. Es kann daher keine Garantie auf Vollständigkeit und Korrektheit gegeben werden, entscheidend ist immer der aktuelle Programm-Code.&lt;br /&gt;
&amp;lt;hr /&amp;gt;&lt;br /&gt;
Please note: This list of functions is not official maintained by all developers. So there is no guarantee for completeness and correctness, at least the program code itself is relevant.&lt;br /&gt;
}}&lt;br /&gt;
Dieses Seite soll die verfügbaren Funktionen zugänglicher machen, um das Beisteuern von Funktionalität zu erleichtern.&lt;br /&gt;
&lt;br /&gt;
Natürlich hat diese Seite keinen Anspruch auf Vollständigkeit und jeder ist aufgefordert mitzuarbeiten, sobald er beim Stöbern über eine Funktion stolpert, die auch andere interessieren könnte.&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Server (pgm2) Perl-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_ME&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| URL-Pfad unter dem der FHEMWEB-Server via HTTP erreichbar ist.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;/fhem&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_dir&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| Das Basisverzeichnis auf dem lokalen Filesystem des FHEMWEB-Servers.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;./www&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_CSRF&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält das aktuell gültige CSRF-Token als HTTP URL-Parameter für die aktuelle Verbindung oder bleibt leer.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;&amp;amp;fwcsrf=csrf_165435377333711&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_detail&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuell erzeugten Detailansicht. &lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Licht_Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_room&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Raumnamen der aktuell erzeugten Raumansicht.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_wname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen FHEMWEB-Instanz.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_cname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen Verbindung (Connection-Name).&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB_192.168.179.65_54406&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_userAgent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den User-Agent, welcher für den aktuellen Aufruf übermittelt wurde.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== FW_makeImage ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage($icon, $text, $class);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_makeImage gibt HTML-Code zurück, der ein Icon aus dem dem FHEMWEB-Iconpool beschreibt.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, mit optionaler Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname&amp;gt;[@color&amp;gt;]&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine SVG-Datei verweist, dann wird &amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt; dem svg-HTML-Tag hinzugefügt. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;svg ... data-txt=&amp;quot;$text&amp;quot; ... &amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine Datei verweist die nicht auf &#039;&#039;.svg&#039;&#039; endet, dann wird der Pfad von der Datei hinter &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; als &amp;lt;code&amp;gt;&amp;lt;img&amp;gt;&amp;lt;/code&amp;gt; eingebunden. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;img ... src=&amp;quot;...&amp;quot; alt=&amp;quot;$text&amp;quot; title=&amp;quot;$text&amp;quot;&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$class&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine CSS-Klasse angibt.&lt;br /&gt;
Wir der Parameter angegeben, wird dem &amp;lt;img&amp;gt; oder &amp;lt;svg&amp;gt; class=&amp;quot;$class&amp;quot; hinzugefügt. Ist der Parameter &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; oder nicht übergeben, so wird kein &amp;lt;code&amp;gt;class&amp;lt;/code&amp;gt;-Attribut hinzugefügt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$html&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
|| Wenn &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039; mit einem Iconnamen aus dem FHEMWEB-Iconpool übereinstimmt, wird der HTML-Code zum Einbinden des Icons zurückgegeben.&lt;br /&gt;
Mit nachgestellter Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname@color&amp;gt;&#039;&amp;lt;/code&amp;gt;, wird das Icon entsprechend eingefärbt.&lt;br /&gt;
Entspricht die Zeichenkette keinem Iconnamen, wird die Zeichenkette zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage(&#039;fts_shutter_10@red&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_directNotify ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify($filter, $fhemweb_instance, $javascript, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_directNotify führt Javascript auf einer longpoll FHEMWEB-Instanz aus, ohne Events zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$filter&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die mit &amp;quot;FILTER=&amp;quot; beginnt &amp;lt;code&amp;gt;&amp;quot;FILTER=&amp;lt;filter expression&amp;gt;&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$fhemweb_instance&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine longpoll FHEMWEB-Instanz &amp;lt;code&amp;gt;&amp;quot;#FHEMWEB:&amp;lt;FHEMWEB devicename&amp;gt;&amp;quot;&amp;lt;/code&amp;gt; angibt.&amp;lt;br&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$javascript&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die ein Javascript angibt.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;&amp;quot;&amp;quot;&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, leer oder (noch zu präzisieren).&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;location.reload(&#039;true&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* siehe auch {{Link2Forum|Topic=48736|Message=404497|LinkText=Forenthread}}&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Client (pgm2) Javascript-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_root&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| FHEM-Ursprungsverzeichnis, steht auch im head-Element als Attribut &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_version&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Ein assoziatives Array, das die Versionszeichenketten der geladenen Javascript-Dateien enthält.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== FW_cmd ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(arg, callback);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_cmd führt einen Befehl auf dem FHEMWEB-Server aus.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;arg&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die einem Befehl an den Server entspricht.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn der auf dem FHEM-Server ausgeführte Befehl einen Rückgabewert enthält, der weiter verarbeitet werden soll.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(FW_root+&#039;?cmd={FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;amp;XHR=1&#039;, function(data){FW_okDialog(data)});&amp;lt;/syntaxhighlight&amp;gt; Das Beispiel holt sich vom Server den HTML-Code des Icon &amp;quot;fts_shutter_10&amp;quot; und zeigt das Icon in einem Popup-Fenster an. Der vom Server auszuführende Befehl lautet &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; inline&amp;gt;{FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;lt;/syntaxhighlight&amp;gt;. An die Callback-Funktion &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; inline&amp;gt;function(data){FW_okDialog(data)}&amp;lt;/syntaxhighlight&amp;gt; wird der Rückgabewert vom Server als Parameter übergeben und steht als Variable &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
=== FW_errmsg ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(text, timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_errmsg zeigt eine Meldung im FHEMWEB-Client (Browser) an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Anzeigedauer der Zeichenkette in Millisekunden.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_okDialog ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(txt, parent, removeFn);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_okDialog öffnet im FHEMWEB-Client (Browser) ein modales Popup-Fenster.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die im Popup-Fenster angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;parent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Objekt zur Verankerung des Popup-Fensters, default ist das body-Element. Eine FHEM-Widget-Schnittstelle wird interpretiert.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;returnFn&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die beim Schliessen des Popup-Fensters ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;#!&amp;quot; onclick=&amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;&amp;gt;Hello&amp;lt;/a&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll das Popup-Fenster aus einem Hyperlink heraus aufgerufen werden, dann ist darauf zu achten, dass ein ungültiger Anchor Tag verwendet wird. In diesem Beispiel &amp;quot;!&amp;quot;. Dadurch wird ein unkontrolliertes Scrollen der Webseite effektiv verhindert und der Dialog erscheint genau dort wo man ihn haben möchte.&lt;br /&gt;
&lt;br /&gt;
=== loadScript ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(scriptname, callback, force);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
loadScript läd im FHEMWEB-Client (Browser) ein Javascript nach.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;scriptname&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die den Sriptnamen enthält.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn das Script geladen wurde.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;force&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Flag, wenn gesetzt, wird das Laden erzwungen.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(&amp;quot;pgm2/jquery.knob.min.js&amp;quot;, function(){&amp;lt;Javascript-Code&amp;gt;});&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== log ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(text);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
log zeigt einen Logeintrag in der Konsole des Browsers an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(&#039;Hello world!&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
* [[DevelopmentFHEMWEB|Development FHEMWEB (englischsprachig)]]&lt;br /&gt;
* [[DevelopmentModuleAPI]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40066</id>
		<title>DevelopmentFHEMWEB-API</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&amp;diff=40066"/>
		<updated>2025-03-24T15:51:29Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: FW_makeImage  Paremeter ein wenig genauer beschrieben&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Randnotiz|RNTyp=r|RNText=Bitte beachten: Diese Auflistung von Funktionen wird nicht aktiv durch alle Entwickler gepflegt. Es kann daher keine Garantie auf Vollständigkeit und Korrektheit gegeben werden, entscheidend ist immer der aktuelle Programm-Code.&lt;br /&gt;
&amp;lt;hr /&amp;gt;&lt;br /&gt;
Please note: This list of functions is not official maintained by all developers. So there is no guarantee for completeness and correctness, at least the program code itself is relevant.&lt;br /&gt;
}}&lt;br /&gt;
Dieses Seite soll die verfügbaren Funktionen zugänglicher machen, um das Beisteuern von Funktionalität zu erleichtern.&lt;br /&gt;
&lt;br /&gt;
Natürlich hat diese Seite keinen Anspruch auf Vollständigkeit und jeder ist aufgefordert mitzuarbeiten, sobald er beim Stöbern über eine Funktion stolpert, die auch andere interessieren könnte.&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Server (pgm2) Perl-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_ME&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| URL-Pfad unter dem der FHEMWEB-Server via HTTP erreichbar ist.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;/fhem&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_dir&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| Das Basisverzeichnis auf dem lokalen Filesystem des FHEMWEB-Servers.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;./www&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_CSRF&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält das aktuell gültige CSRF-Token als HTTP URL-Parameter für die aktuelle Verbindung oder bleibt leer.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;&amp;amp;fwcsrf=csrf_165435377333711&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_detail&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuell erzeugten Detailansicht. &lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Licht_Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_room&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Raumnamen der aktuell erzeugten Raumansicht.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Wohnzimmer&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_wname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen FHEMWEB-Instanz.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_cname&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den Definitionsnamen der aktuellen Verbindung (Connection-Name).&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;WEB_192.168.179.65_54406&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$FW_userAgent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Enthält den User-Agent, welcher für den aktuellen Aufruf übermittelt wurde.&lt;br /&gt;
&lt;br /&gt;
Bsp: &amp;lt;code&amp;gt;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== FW_makeImage ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage($icon, $text, $class);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_makeImage gibt HTML-Code zurück, der ein Icon aus dem dem FHEMWEB-Iconpool beschreibt.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, mit optionaler Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname&amp;gt;[@color&amp;gt;]&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine SVG-Datei verweist, dann wird &amp;lt;code&amp;gt;$text&amp;lt;/code&amp;gt; dem svg-HTML-Tag hinzugefügt. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;svg class=&amp;quot;$class&amp;quot; data-txt=&amp;quot;$text&amp;quot; ... &amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn hier ein Text angegeben wird und &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; auf eine Datei verweist die nicht auf &#039;&#039;.svg&#039;&#039; endet, dann wird der Pfad von der Datei hinter &amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt; als &amp;lt;code&amp;gt;&amp;lt;img&amp;gt;&amp;lt;/code&amp;gt; eingebunden. Das Resultat im HTML-Quelltext ist dann in der Art &amp;lt;code&amp;gt;&amp;lt;img ... src=&amp;quot;...&amp;quot; alt=\&amp;quot;$text\&amp;quot; title=\&amp;quot;$text\&amp;quot;&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$class&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine CSS-Klasse angibt.&lt;br /&gt;
Wir der Parameter angegeben, wird dem &amp;lt;img&amp;gt; oder &amp;lt;svg&amp;gt; class=&amp;quot;$class&amp;quot; hinzugefügt. Ist der Parameter &amp;lt;code&amp;gt;undef&amp;lt;/code&amp;gt; oder nicht übergeben, so wird kein &amp;lt;code&amp;gt;class&amp;lt;/code&amp;gt;-Attribut hinzugefügt.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Rückgabewerte:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Rückgabewert!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$html&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
|| Wenn &#039;&#039;&#039;&amp;lt;code&amp;gt;$icon&amp;lt;/code&amp;gt;&#039;&#039;&#039; mit einem Iconnamen aus dem FHEMWEB-Iconpool übereinstimmt, wird der HTML-Code zum Einbinden des Icons zurückgegeben.&lt;br /&gt;
Mit nachgestellter Farbangabe &amp;lt;code&amp;gt;&#039;&amp;lt;iconname@color&amp;gt;&#039;&amp;lt;/code&amp;gt;, wird das Icon entsprechend eingefärbt.&lt;br /&gt;
Entspricht die Zeichenkette keinem Iconnamen, wird die Zeichenkette zurückgegeben.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;$html = FW_makeImage(&#039;fts_shutter_10@red&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_directNotify ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify($filter, $fhemweb_instance, $javascript, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_directNotify führt Javascript auf einer longpoll FHEMWEB-Instanz aus, ohne Events zu erzeugen.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$filter&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die mit &amp;quot;FILTER=&amp;quot; beginnt &amp;lt;code&amp;gt;&amp;quot;FILTER=&amp;lt;filter expression&amp;gt;&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$fhemweb_instance&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die eine longpoll FHEMWEB-Instanz &amp;lt;code&amp;gt;&amp;quot;#FHEMWEB:&amp;lt;FHEMWEB devicename&amp;gt;&amp;quot;&amp;lt;/code&amp;gt; angibt.&amp;lt;br&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;$javascript&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die ein Javascript angibt.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;&amp;quot;&amp;quot;&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, leer oder (noch zu präzisieren).&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiele:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;FW_directNotify(&amp;quot;FILTER=room=XXX&amp;quot;, &amp;quot;#FHEMWEB:WEB&amp;quot;, &amp;quot;location.reload(&#039;true&#039;)&amp;quot;, &amp;quot;&amp;quot;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* siehe auch {{Link2Forum|Topic=48736|Message=404497|LinkText=Forenthread}}&lt;br /&gt;
&lt;br /&gt;
== FHEMWEB-Client (pgm2) Javascript-Funktionen ==&lt;br /&gt;
&lt;br /&gt;
=== Globale Variablen ===&lt;br /&gt;
&lt;br /&gt;
Globale Variablen:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Variablenname!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_root&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
|| FHEM-Ursprungsverzeichnis, steht auch im head-Element als Attribut &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;FW_version&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|| Ein assoziatives Array, das die Versionszeichenketten der geladenen Javascript-Dateien enthält.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== FW_cmd ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(arg, callback);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_cmd führt einen Befehl auf dem FHEMWEB-Server aus.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;arg&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die einem Befehl an den Server entspricht.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn der auf dem FHEM-Server ausgeführte Befehl einen Rückgabewert enthält, der weiter verarbeitet werden soll.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_cmd(FW_root+&#039;?cmd={FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;amp;XHR=1&#039;, function(data){FW_okDialog(data)});&amp;lt;/syntaxhighlight&amp;gt; Das Beispiel holt sich vom Server den HTML-Code des Icon &amp;quot;fts_shutter_10&amp;quot; und zeigt das Icon in einem Popup-Fenster an. Der vom Server auszuführende Befehl lautet &amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; inline&amp;gt;{FW_makeImage(&amp;quot;fts_shutter_10&amp;quot;)}&amp;lt;/syntaxhighlight&amp;gt;. An die Callback-Funktion &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot; inline&amp;gt;function(data){FW_okDialog(data)}&amp;lt;/syntaxhighlight&amp;gt; wird der Rückgabewert vom Server als Parameter übergeben und steht als Variable &amp;lt;code&amp;gt;data&amp;lt;/code&amp;gt; zur Verfügung.&lt;br /&gt;
&lt;br /&gt;
=== FW_errmsg ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(text, timeout);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_errmsg zeigt eine Meldung im FHEMWEB-Client (Browser) an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;timeout&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Anzeigedauer der Zeichenkette in Millisekunden.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_errmsg(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== FW_okDialog ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(txt, parent, removeFn);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
FW_okDialog öffnet im FHEMWEB-Client (Browser) ein modales Popup-Fenster.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die im Popup-Fenster angezeigt wird. HTML-Code wird interpretiert.&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;parent&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Objekt zur Verankerung des Popup-Fensters, default ist das body-Element. Eine FHEM-Widget-Schnittstelle wird interpretiert.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;returnFn&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die beim Schliessen des Popup-Fensters ausgeführt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;FW_okDialog(&#039;Hello world!&#039;,10000);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;#!&amp;quot; onclick=&amp;quot;FW_okDialog(&#039;Hello world!&#039;)&amp;quot;&amp;gt;Hello&amp;lt;/a&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Soll das Popup-Fenster aus einem Hyperlink heraus aufgerufen werden, dann ist darauf zu achten, dass ein ungültiger Anchor Tag verwendet wird. In diesem Beispiel &amp;quot;!&amp;quot;. Dadurch wird ein unkontrolliertes Scrollen der Webseite effektiv verhindert und der Dialog erscheint genau dort wo man ihn haben möchte.&lt;br /&gt;
&lt;br /&gt;
=== loadScript ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(scriptname, callback, force);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
loadScript läd im FHEMWEB-Client (Browser) ein Javascript nach.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;scriptname&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die den Sriptnamen enthält.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Eine Javascript-Funktion, die ausgeführt wird, wenn das Script geladen wurde.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;force&amp;lt;/code&amp;gt;&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;optional&#039;&#039;&lt;br /&gt;
|| Ein Flag, wenn gesetzt, wird das Laden erzwungen.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;loadScript(&amp;quot;pgm2/jquery.knob.min.js&amp;quot;, function(){&amp;lt;Javascript-Code&amp;gt;});&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== log ===&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(text);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
log zeigt einen Logeintrag in der Konsole des Browsers an.&lt;br /&gt;
&lt;br /&gt;
Parameter:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter!! Bedeutung&lt;br /&gt;
|-&lt;br /&gt;
|  style=&amp;quot;vertical-align:top&amp;quot; | &#039;&#039;&#039;&amp;lt;code&amp;gt;text&amp;lt;/code&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;mandatory&#039;&#039;&lt;br /&gt;
|| Eine Zeichenkette, die angezeigt wird.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Beispiel:&lt;br /&gt;
* &amp;lt;syntaxhighlight lang=&amp;quot;javascript&amp;quot;&amp;gt;log(&#039;Hello world!&#039;);&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
* [[DevelopmentFHEMWEB|Development FHEMWEB (englischsprachig)]]&lt;br /&gt;
* [[DevelopmentModuleAPI]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Development]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=Icons&amp;diff=40064</id>
		<title>Icons</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=Icons&amp;diff=40064"/>
		<updated>2025-03-23T17:55:37Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Icons tauchen erst nach Neustart oder set WEB rereadicons auf&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Icons - alles rund um die Icons in FHEM&lt;br /&gt;
= FHEM mit Icons verschönern =&lt;br /&gt;
== Geräten Icons zuweisen ==&lt;br /&gt;
Im [[FHEMWEB]] auf Detail-Ansicht des Gerätes gehen, danach in der attr Zeile per dropdown&lt;br /&gt;
# Icon auswählen, um das Icon zu wählen, das standardmäßig links vom Gerät gezeigt wird; alternativ ganz unten auf &#039;&#039;&#039;Select Icon&#039;&#039;&#039; klicken, dann bekommt ihr alle Bilder angezeigt&lt;br /&gt;
# &#039;&#039;&#039;[[devStateIcon]] auswählen&#039;&#039;&#039;, um unterschiedliche Schaltzustände mit unterschiedlichen Icons zu versehen; alternativ ganz unten auf &#039;&#039;&#039;Extend devStateIcon&#039;&#039;&#039; klicken, dann bekommt ihr alle Bilder angezeigt und könnt sie dem jeweiligen Schaltzustand zuordnen.&lt;br /&gt;
&lt;br /&gt;
Dabei gilt folgendes:&lt;br /&gt;
* Icon-Dateiname OHNE Dateiendung eingeben&lt;br /&gt;
* wenn es sich bei dem Icon um eine svg-Datei handelt, kann mit @farbe die Farbe beeinflusst werden, z.B. &amp;lt;br /&amp;gt; &amp;lt;code&amp;gt;attr Geraet devStateIcon an:general_an@green aus:general_aus@red&amp;lt;/code&amp;gt;, &amp;lt;br /&amp;gt; wobei hier die beiden Dateien general_an.svg und general_aus.svg heißen.&lt;br /&gt;
* statt des Standardfarbnamens, kann übrigens auch der 6-stellige Hex-Code übergeben werden (ohne # am Anfang). Den Code könnt ihr z.B. hier bei [http://www.colorpicker.com Colorpicker] generieren&lt;br /&gt;
* Möchte man dass bei Klick auf das Icon auch eine Aktion ausgelöst wird, kann man dies als dritten Parameter mit angeben. Beispiel für einen Wechsel zwischen &amp;quot;around&amp;quot; und &amp;quot;away&amp;quot;: &amp;lt;code&amp;gt;around:status_available:away away:status_away_2:around&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Wenn ihr die Änderungen wie oben beschrieben durchführt, könnt ihr das Ergebnis gleich betrachten. Wenn alles passt, dann noch &#039;&#039;&#039;Save config&#039;&#039;&#039; drücken, dann wird alles fest abgespeichert.&lt;br /&gt;
&lt;br /&gt;
== Größe der Icons verändern ==&lt;br /&gt;
Auch das kein Problem. Öffnet einfach die für Eurer Template (wie sieht fhem bei Euch aus: rechts unten -&amp;gt; Select Style -&amp;gt; das was ihr dort gewählt habt ist Euer Template) zuständige css-Datei. Diese findet ihr im fhem-Verzeichnis unter www/pgm2. Dort fügt ihr einfach folgende Zeile ein und ändert ggf. die Größenangaben.&lt;br /&gt;
&amp;lt;pre&amp;gt;svg { height:32px; width:32px; }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mehr Icons zur Auswahl ==&lt;br /&gt;
Grundsätzlich sind bei aktuellen fhem-Installationen die meisten Icons standardmäßig aktiviert. Navigiert doch einfach mal im fhem Verzeichnis in den www Ordner und dort in images. Die Ordner default, openautomation und fhemSVG sind standardmäßig aktiviert, d.h., Icons aus diesen Ordnern werden in fhem angezeigt. Wenn ihr zusätzlich die Icons auch aus den anderen Ordnern (z.B. icons_small) verwenden wollt, dann ergänzt einfach die entsprechende Zeile &amp;quot;WEB iconPath&amp;quot; in der fhem.cfg:&lt;br /&gt;
&amp;lt;pre&amp;gt;attr WEB iconPath fhemSVG:openautomation:default:icons_small&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das gleiche gilt, wenn ihr eine ältere fhem-Installation habt und noch nicht die Bilder z.B. aus fhemSVG angezeigt bekommt.&lt;br /&gt;
&lt;br /&gt;
Damit neu hinzugefügte Icons erkannt werden ist es notwendig FHEM neuzustarten, oder dem FHEMWEB device einen Befehl zum einlesen der Icons zu senden:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
set WEB rereadicons&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Weitere Iconsätze ===&lt;br /&gt;
&lt;br /&gt;
Im Ordner /contrib der FHEM-Installation oder des SVN befinden sich weitere Iconsätze in Form von SVG-Dateien.&lt;br /&gt;
&lt;br /&gt;
* FontAwesome in IconFontAwesome.zip, s. [http://fontawesome.io/icons/ http://fontawesome.io/icons/]&lt;br /&gt;
* IcoMoon in IcoMoon.zip, s. [https://icomoon.io/#preview-free https://icomoon.io/#preview-free]&lt;br /&gt;
&lt;br /&gt;
== Icons Farben zuweisen ==&lt;br /&gt;
Man kann Icons beliebige Farben zuweisen, indem man den Icon-Namen per &amp;quot;@&amp;quot; mit einem Farbnamen oder einer Farbcodierung versieht.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rc_dot@red&lt;br /&gt;
rc_dot@#a1d490&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Hilfe zur Bestimmung der Farbcodierung kann man auf den folgenden Seiten finden:&lt;br /&gt;
* [http://www.colorpicker.com www.colorpicker.com]&lt;br /&gt;
* [http://www.html-color-names.com www.html-color-names.com]&lt;br /&gt;
&lt;br /&gt;
== Weitere Tipps ==&lt;br /&gt;
Weitere Tipps zu Icons finden sich auch auf der Seite [[Slider für HM-Rolladensteuerung anzeigen]].&lt;br /&gt;
&lt;br /&gt;
= Eigene Icons erstellen =&lt;br /&gt;
Falls ihr eigene Icons basteln wollt - sehr gerne. Umso besser, wenn ihr diese dann mit uns teilt. Der passende {{Link2Forum|Topic=12605|LinkText=Thread im Forum}}.&lt;br /&gt;
&lt;br /&gt;
== SVG-Icons ==&lt;br /&gt;
Hier einige Tipps zum Erstellen von SVG-Icons. SVGs bieten einige Vorteile:&lt;br /&gt;
* SVGs sind vektorbasiert und somit frei skalierbar&lt;br /&gt;
* SVGs sind, wenn man sie genauer betrachtet, gut lesbarer und veränderbarer Quellcode und können deswegen leicht über css manipuliert werden&lt;br /&gt;
&lt;br /&gt;
Wie geht es nun:&lt;br /&gt;
* [http://inkscape.org/download/?lang=de Inkscape] besorgen (Open Source)&lt;br /&gt;
* Loszeichnen&lt;br /&gt;
&lt;br /&gt;
Jetzt wird es wichtig - fhem ist etwas sensibel, was svgs anbelangt und will diese zudem farblich umgestalten. Daher folgendes unbedingt beachten:&lt;br /&gt;
&lt;br /&gt;
* fhem ersetzt bei allen fill:#000000 und fill=&amp;quot;#000000&amp;quot; die sechs Nullen mit einem Farbcode, wenn dieser mit übergeben wurde (sh. FHEM mit Icons verschönern weiter oben) - das solltet ihr beim zeichnen beachten&lt;br /&gt;
* daher arbeite ich immer so, dass ich beispielsweise Rechtecke/Kreise zeichne und diese dann mit &amp;quot;Kontur in Pfad umwandeln&amp;quot; umwandle -&amp;gt; dann ist das Rechteck transparent, die Linie schwarz (und wird von fhem erstetzt) und alles klappt&lt;br /&gt;
* alle Texte müssen in Pfade umgewandelt werden&lt;br /&gt;
* das gesamte Bild muss als &amp;quot;Normales SVG&amp;quot; gespeichert werden (Standardeinstellung bei Inkscape ist &amp;quot;Inkscape SVG&amp;quot;)&lt;br /&gt;
* Wenn die Grafik nicht angezeigt wird sondern nur Text: In einem Editor den Inhalt mit einer funktionierenden SVG-Datei vergleichen und anpassen.&lt;br /&gt;
* letzter wichtiger Tipp: Eine Zeile darf im SVG nicht fehlen, sonst zeigt fhem es nicht an. Wenn ihr Euer SVG in einem Texteditor öffnet (ich empfehle notepad++), schaut in in der &amp;lt;svg&amp;gt;-Definition, ob die Zeile mit viewBox dabei ist. Wenn nicht, dann nach unterem Format ergänzen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;XML&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&lt;br /&gt;
&amp;lt;!-- Created with Inkscape (http://www.inkscape.org/) --&amp;gt;&lt;br /&gt;
&amp;lt;svg&lt;br /&gt;
   version=&amp;quot;1.0&amp;quot;&lt;br /&gt;
   width=&amp;quot;585&amp;quot;&lt;br /&gt;
   height=&amp;quot;585&amp;quot;&lt;br /&gt;
   viewBox=&amp;quot;0 0 585 585&amp;quot;&lt;br /&gt;
   id=&amp;quot;svg2421&amp;quot;&lt;br /&gt;
   sodipodi:docname=&amp;quot;usb-stick.svg&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== SVG-Icons für das Modul remotecontrol: Schriftvorlage ==&lt;br /&gt;
Wer beispielsweise für das Modul remotecontrol noch andere Beschriftungen braucht, der kann sich diese ganz fix selbst machen. Im Ordner fhem\www\images\fhemSVG findet ihr die Datei rc_templatebutton.svg. Mit der macht ihr folgendes:&lt;br /&gt;
Ganz einfach inkscape runterladen (http://inkscape.org/download/?lang=de), Datei öffnen, auf Text klicken, dann oben in der Leiste auf Text -&amp;gt; Schrift und Text -&amp;gt; Reiter Text, Text ändern. Ggf. Größe/Position anpassen, dann Pfad -&amp;gt; Objekt in Pfad umwandeln, dann Datei -&amp;gt; Speichern unter -&amp;gt; euer name und &amp;quot;Normales SVG&amp;quot; =&amp;gt; FERTIG&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:HOWTOS]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
	<entry>
		<id>http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40033</id>
		<title>E-Mail senden</title>
		<link rel="alternate" type="text/html" href="http://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;diff=40033"/>
		<updated>2025-02-28T06:31:31Z</updated>

		<summary type="html">&lt;p&gt;Torxgewinde: Snippet für cUrl mit Feedback aus Forum verbessert für Nachrichten mit Zeilenumbrüchen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Um aus FHEM heraus E-Mails senden zu können, gibt es unterschiedliche Methoden, abhängig von der verwendeten Plattform.&lt;br /&gt;
&lt;br /&gt;
Lange Zeit war &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; sehr beliebt. Da aber die Homepage des Projekts nicht mehr online ist und Probleme nur noch via erhöhtem Supportaufwand gelöst werden, sollte diese Option als veraltet betrachtet werden.&lt;br /&gt;
&lt;br /&gt;
Eine Liste mit ressourcensparenden E-Mails Clients findet man bei dem OpenWRT Projekt: https://openwrt.org/docs/guide-user/services/email/smtp.client&lt;br /&gt;
&lt;br /&gt;
MSGMail ist ein FHEM eigenes Modul zum Versenden, die Einrichtung ist allerdings nicht gänzlich einfach. Anfängern sei die cURL Methode empfohlen.&lt;br /&gt;
&lt;br /&gt;
=== MSGMail (Linux, Windows) ===&lt;br /&gt;
FHEM bringt ein eigenes Device zum Senden von E-Mails mit, welches in Perl implementiert ist: [https://fhem.de/commandref.html#MSGMail MSGMail, https://fhem.de/commandref.html#MSGMail]&lt;br /&gt;
&lt;br /&gt;
=== cURL (Linux, Windows) ===&lt;br /&gt;
[https://curl.se/ cURL] kann auf praktisch [https://curl.se/download.html jedem Betriebssystem] genutzt werden, [https://curl.se/windows/ selbst unter Windows]. Es kann direkt mit einem SMTP Server E-Mails versenden und stellt damit eine moderne, portable und sichere Option zum Versenden von E-Mails dar.&lt;br /&gt;
&lt;br /&gt;
In FHEM richtet man sich ein Device ein und passt die Details (&amp;lt;code&amp;gt;$emailTo, $emailFrom, $emailServer&amp;lt;/code&amp;gt;) an. Das Programm cURL muss installiert sein (z.B. mit &amp;lt;code&amp;gt;apt install curl&amp;lt;/code&amp;gt;, oder durch Herunterladen und Installieren von der [https://curl.se cURL-Webseite]).&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
defmod sendMail dummy&lt;br /&gt;
attr sendMail readingList message password&lt;br /&gt;
attr sendMail setList message:textField-long password&lt;br /&gt;
attr sendMail userReadings state:message:[\s\S]* {\&lt;br /&gt;
	my $emailTo   = &#039;you@example.com&#039;;;\&lt;br /&gt;
	my $emailFrom = &#039;fhem@example.org&#039;;;\&lt;br /&gt;
	my $emailServer = &#039;mail.smtpserver.de&#039;;;\&lt;br /&gt;
	\&lt;br /&gt;
	# Passwort aus getKeyValue abrufen\&lt;br /&gt;
	my ($err, $emailPass) = getKeyValue(&amp;quot;${name}_password&amp;quot;);;\&lt;br /&gt;
	if ($err || !defined $emailPass) {\&lt;br /&gt;
		return &amp;quot;Error retrieving password: $err&amp;quot;;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	my $message = ReadingsVal($name, &#039;message&#039;, &#039;???&#039;);;\&lt;br /&gt;
	my $subject = &amp;quot;$name: FHEM Nachricht&amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	# Betreff extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
	if ($message =~ s/(?:Subject|Betreff)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
		$subject = $1;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	# Empfänger extrahieren und aus der Nachricht entfernen\&lt;br /&gt;
	if ($message =~ s/(?:To|An)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
		$emailTo = $1;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	#Dateianhänge bestimmen\&lt;br /&gt;
	my @attachments;;\&lt;br /&gt;
	while ($message =~ s/(?:Attachment|Anhang)=[&amp;quot;&#039;](.*?)[&amp;quot;&#039;]//) {\&lt;br /&gt;
		my $file = $1;;\&lt;br /&gt;
		return &amp;quot;&amp;gt;&amp;gt;$file&amp;lt;&amp;lt; not found, not sending email&amp;quot; unless (-e $file);;\&lt;br /&gt;
		push (@attachments, $file);;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	#der curl Befehl wird hier aufgebaut:\&lt;br /&gt;
	my $cmd = &amp;quot;curl -m 19 --noproxy &#039;*&#039; --no-progress-meter --ssl-reqd smtps://$emailServer:465 &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;--user &#039;$emailFrom:$emailPass&#039; --mail-from &#039;$emailFrom&#039; --mail-rcpt &#039;$emailTo&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;--write-out &#039;Email sent with status %{http_code}\n&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-H &#039;Subject: $subject&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-H &#039;From: $emailFrom&#039; &amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	#multipart/alternative für Text und HTML EMail Body\&lt;br /&gt;
	$cmd .= &amp;quot;-F &#039;=(;;type=multipart/alternative&#039; &amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	# Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Anfang entfernen:\&lt;br /&gt;
	$message =~ s/^(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+//;;\&lt;br /&gt;
	# Leerzeichen und &amp;lt;br&amp;gt; bzw &amp;lt;br \&amp;gt; am Ende entfernen:\&lt;br /&gt;
	$message =~ s/(\s|&amp;lt;br(?:[ \/]+)&amp;gt;)+$//;;\&lt;br /&gt;
	\&lt;br /&gt;
	#Plaintext E-Mail Inhalt an cURL:\&lt;br /&gt;
	$cmd .= &amp;quot;-F \&amp;quot;=$message;;type=text/plain;; charset=UTF-8\&amp;quot; &amp;quot;;;\&lt;br /&gt;
	#ersetze \n bzw. \r\n durch ein HTML-tag &amp;lt;br /&amp;gt;:\&lt;br /&gt;
	$message =~ s/\r?\n/&amp;lt;br \/&amp;gt;/g;;\&lt;br /&gt;
	\&lt;br /&gt;
	#HTML E-Mail Inhalt an cURL:\&lt;br /&gt;
	$cmd .= &amp;quot;-F &#039;= &amp;lt;body&amp;gt;$message&amp;lt;/body&amp;gt;;;type=text/html;; charset=UTF-8&#039; &amp;quot;;;\&lt;br /&gt;
	$cmd .= &amp;quot;-F &#039;=)&#039; &amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	#jetzt alle Attachments (0..N) anhängen:\&lt;br /&gt;
	foreach my $file (@attachments) {\&lt;br /&gt;
		$cmd .= &amp;quot;-F &#039;=@&amp;quot;.$file.&amp;quot;;;encoder=base64&#039; &amp;quot;;;\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	#stderr auch mit in den output sammeln:\&lt;br /&gt;
	$cmd .= &amp;quot; 2&amp;gt;&amp;amp;1&amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	#Blockierender Aufruf von cURL in dieser Funktion via BlockingCall:\&lt;br /&gt;
	if (!defined &amp;amp;sendMailfunc1) {\&lt;br /&gt;
		*sendMailfunc1 = sub ($) {\&lt;br /&gt;
			my ($param) = @_;;\&lt;br /&gt;
			my $result;;\&lt;br /&gt;
			$result = qx/$param/;;\&lt;br /&gt;
			$result = MIME::Base64::encode_base64($result);;\&lt;br /&gt;
			$result =~ s/\n//g;;\&lt;br /&gt;
			return $result;;\&lt;br /&gt;
		}\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	#Rückgabe über diese Funktion,\&lt;br /&gt;
	#anzeigen in dem reading dieses UserReadings:\&lt;br /&gt;
	if (!defined &amp;amp;sendMailfunc2) {\&lt;br /&gt;
		*sendMailfunc2 = sub ($) {\&lt;br /&gt;
			my ($result) = @_;;\&lt;br /&gt;
			my $hash = $defs{$name};;\&lt;br /&gt;
			$result = MIME::Base64::decode_base64($result);;\&lt;br /&gt;
			readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
		}\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	#funktion falls BlockingCall abgebrochen wird:\&lt;br /&gt;
	if (!defined &amp;amp;sendMailfunc3) {\&lt;br /&gt;
		*sendMailfunc3 = sub ($) {\&lt;br /&gt;
			my ($result) = @_;;\&lt;br /&gt;
			my $hash = $defs{$name};;\&lt;br /&gt;
			readingsSingleUpdate($hash, $reading, $result, 1);;\&lt;br /&gt;
		}\&lt;br /&gt;
	}\&lt;br /&gt;
	\&lt;br /&gt;
	BlockingCall(&amp;quot;sendMailfunc1&amp;quot;, $cmd, &amp;quot;sendMailfunc2&amp;quot;, 20, &amp;quot;sendMailfunc3&amp;quot;, &amp;quot;Error: timeout&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return &amp;quot;started cURL command...&amp;quot;;;\&lt;br /&gt;
},\&lt;br /&gt;
state:password:.* {\&lt;br /&gt;
	# Passwort speichern\&lt;br /&gt;
	my $ret = setKeyValue(&amp;quot;${name}_password&amp;quot;, ReadingsVal($name, &#039;password&#039;, undef)) // &amp;quot;password stored&amp;quot;;;\&lt;br /&gt;
	\&lt;br /&gt;
	#password wieder aus der Variablen rausnehmen, soll nicht sichtbar bleiben:\&lt;br /&gt;
	#\&lt;br /&gt;
	# Hinweis: das Password taucht beim Setzen hierüber im Event-Log auf!\&lt;br /&gt;
	# Alternativ: { setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }\&lt;br /&gt;
	readingsBulkUpdate($hash, &amp;quot;password&amp;quot;, &amp;quot;****&amp;quot;);;\&lt;br /&gt;
	\&lt;br /&gt;
	return &amp;quot;$ret&amp;quot;;;\&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Das Passwort setzt man mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail password geheimesPasswort&amp;lt;/code&amp;gt;&lt;br /&gt;
* Alternativ, damit das Passwort nicht im Log auftaucht: &amp;lt;code&amp;gt;{ setKeyValue(&amp;quot;sendMail_password&amp;quot;, &amp;quot;geheimesPasswort&amp;quot;) }&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
E-Mails kann man dann Senden mit:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Mein Text&amp;lt;/code&amp;gt; (es wird der Standardbetreff und Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;(es wird der Empfänger aus dem Snippet genutzt)&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message An=&amp;quot;jemand@example.com&amp;quot; Betreff=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message To=&amp;quot;jemand@example.com&amp;quot; Subject=&amp;quot;Mein Betreff&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;✅ Dies ist ein Test&amp;quot; Meldung mit Emoji ⚽ geht auch&amp;lt;/code&amp;gt;&lt;br /&gt;
* Eine Datei mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/bag.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;&lt;br /&gt;
* Zwei Dateien mit anhängen: &amp;lt;code&amp;gt;set sendMail message Subject=&amp;quot;Dies ist ein Test&amp;quot; Anhang=&amp;quot;./www/images/fhemSVG/bag.svg&amp;quot; Anhang=&amp;quot;www/images/fhemSVG/batterie.svg&amp;quot; Mein Text&amp;lt;/code&amp;gt;, für die anzuhängenden Dateien können relative und absolute Pfade verwendet werden. Beim Vorbereiten der E-Mail wird geprüft ob die Dateien existieren, da dies sonst eine typische Fehlerquelle ist. Es wird nichts versendet wenn eine Datei nicht gefunden werden kann.&lt;br /&gt;
&lt;br /&gt;
==== Fehlersuche ====&lt;br /&gt;
Sollte cURL zwar installiert sein, aber keine E-Mails versenden, kann es sein, dass das Protokoll nicht einkompiliert wurde. Dies ist bei OpenWRT zum Beispiel der Fall.&lt;br /&gt;
&lt;br /&gt;
So sollte es aussehen (hier ein Ubuntu), relevant ist für diese Nutzung das Protokoll smtp und smtps, das braucht man:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.7&lt;br /&gt;
Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.6&lt;br /&gt;
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp&lt;br /&gt;
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;So sollte es nicht aussehen (hier ein OpenWRT):&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
curl: (1) Protocol &amp;quot;smtps&amp;quot; not supported&lt;br /&gt;
&lt;br /&gt;
curl -V&lt;br /&gt;
curl 8.10.1 (arm-openwrt-linux-gnu) libcurl/8.10.1 mbedTLS/3.6.2 nghttp2/1.63.0&lt;br /&gt;
Release-Date: 2024-09-18&lt;br /&gt;
Protocols: file ftp ftps http https ipfs ipns mqtt&lt;br /&gt;
Features: alt-svc HSTS HTTP2 HTTPS-proxy IPv6 Largefile SSL threadsafe UnixSockets&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Hat man nur ein cURL ohne das Protokoll, so muss man sich das Paket leider anderweitig beschaffen (chroot, selbst kompilieren, statisch gelinkte Version, ...).&lt;br /&gt;
&lt;br /&gt;
==== Forumlink ====&lt;br /&gt;
https://forum.fhem.de/index.php?topic=140814.0&lt;br /&gt;
&lt;br /&gt;
=== EXIM4 (Debian) ===&lt;br /&gt;
Eine Anleitung zum Versenden von E-Mails mit Exim ist als PDF verfügbar: [[Medium:Anleitung Exim4 Debian GMX.pdf|PDF]]&lt;br /&gt;
&lt;br /&gt;
=== SSMTP (OpenWRT, embedded Distros) ===&lt;br /&gt;
&lt;br /&gt;
==== Installation ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
opkg update&lt;br /&gt;
opkg install ssmtp_2.64-4_mpc85xx.ipk  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Damit &amp;lt;code&amp;gt;ssmtp&amp;lt;/code&amp;gt; funktioniert müssen die Dateien &amp;lt;code&amp;gt;/etc/ssmtp/ssmtp.conf&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;/etc/ssmtp/revaliases&amp;lt;/code&amp;gt; angepasst werden.&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/ssmtp.conf &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root=arnold@gmx.net&lt;br /&gt;
 mailhub=mail.gmx.net:465&lt;br /&gt;
 rewriteDomain=gmx.net&lt;br /&gt;
 hostname=gmx.net&lt;br /&gt;
 FromLineOverride=YES&lt;br /&gt;
 UseTLS=YES&lt;br /&gt;
 #UseSTARTTLS=YES&lt;br /&gt;
 AuthUser=arnold@gmx.net&lt;br /&gt;
 AuthPass=Passwort_von_arnold@gmx.net&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/etc/ssmtp/revaliases&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
 root:arnold@gmx.net:mail.gmx.net:465&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In der [[99_myUtils_anlegen|99_myUtils]] folgende Unterroutine einfügen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub OpenWRTMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $sender = &amp;quot;dockstar\@heye-tammo.de&amp;quot;; &lt;br /&gt;
 Log 1, &amp;quot;sendEmail RCP: $rcpt, Subject: $subject, Text: $text&amp;quot;;&lt;br /&gt;
 $ret .= qx(echo -e &#039;to:$rcpt\n from:$sender\nsubject:$subject\n$text\n&#039; | ssmtp $rcpt);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendEmail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== PHP Mail Funktion (Synology DiskStation) ===&lt;br /&gt;
Beim DSM 3.2 funktioniert der &amp;lt;code&amp;gt;php-mail&amp;lt;/code&amp;gt;-Befehl, so dass man mittels folgendem Modul E-Mails versenden kann:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
sub sendmail($$$) {&lt;br /&gt;
 my($empf, $subj, $nachricht) = @_;&lt;br /&gt;
 system(&amp;quot;php -r &#039;mail(\&amp;quot;$empf\&amp;quot;,\&amp;quot;$subj\&amp;quot;,\&amp;quot;$nachricht\&amp;quot;);&#039;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Sendemail (Veraltet, Debian-basierende Distros) ===&lt;br /&gt;
Es handelt sich um ein Perl Programm, dass man aus den Paketquellen installieren kann.&lt;br /&gt;
&lt;br /&gt;
Es gibt Probleme:&lt;br /&gt;
&lt;br /&gt;
* mit der TLS bzw STARTTLS Verschlüsselung (https://github.com/fhem/fhem-docker/issues/254, https://www.mail-archive.com/debian-bugs-dist@lists.debian.org/msg1915977.html) ,&lt;br /&gt;
* Mit der Webseite des Projekts (https://web.archive.org/web/20190906140125/http://caspian.dotconf.net/menu/Software/SendEmail); diese ist nicht mehr verfügbar und nur eine archivierte Kopie kann eingesehen werden,&lt;br /&gt;
&lt;br /&gt;
weswegen diese Lösung als technisch überholt gelten sollte. Will man trotzdem &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; verwenden folgenden hier die Informationen dazu:&lt;br /&gt;
&lt;br /&gt;
Auf dem Server muss &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; installiert werden:&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
sudo apt-get update&lt;br /&gt;
sudo apt-get install sendemail libio-socket-ssl-perl libnet-ssleay-perl perl&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Man kann die korrekte Funktion im Terminal (ohne &amp;lt;code&amp;gt;{qx()}&amp;lt;/code&amp;gt; ) oder auch in der FHEM Kommandozeile testen (Hinweis: manche Provider (z.B. gmail) lassen diesen Zugang erst nach Freischaltung zu!).&lt;br /&gt;
 &lt;br /&gt;
&#039;&#039;Bei Gmail muss seit dem 30.6.2022 ein separates Passwort erzeugt werden, die alte Einstellung über &amp;quot;Zugriff durch weniger sichere Apps&amp;quot; funktioniert nicht mehr. Siehe Support Seite Google: &amp;lt;nowiki&amp;gt;https://support.google.com/accounts/answer/185833&amp;lt;/nowiki&amp;gt;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Beispiel mit Gmail über Linux Terminalfenster:&#039;&#039;&lt;br /&gt;
 sendemail -f &#039;Der.Absender@gmail.com&#039; -t &#039;Der.Empfaenger@gmail.com&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt; -s &#039;smtp.gmail.com:587&#039; -xu &#039;Der.Absender&#039; -xp DASSUPERGEHEIMEPASSW0RT&lt;br /&gt;
In der [[99 myUtils anlegen|99_myUtils]] folgende Unterroutine einfügen:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
######## DebianMail  Mail auf dem RPi versenden ############ &lt;br /&gt;
# $provider für SMTP Server anpassen. &lt;br /&gt;
# Einmal in der FHEM Kommandozeile, user und password anpassen:&lt;br /&gt;
# {setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&lt;br /&gt;
sub DebianMail &lt;br /&gt;
{ &lt;br /&gt;
 my $rcpt = shift;&lt;br /&gt;
 my $subject = shift; &lt;br /&gt;
 my $text = shift; &lt;br /&gt;
 my $attach = shift; &lt;br /&gt;
 my $ret = &amp;quot;&amp;quot;;&lt;br /&gt;
 my $error;&lt;br /&gt;
 my $konto = getKeyValue(&amp;quot;myEmailKonto&amp;quot;); &lt;br /&gt;
 my $passwrd = getKeyValue(&amp;quot;myEmailPasswrd&amp;quot;); &lt;br /&gt;
 my $from = $konto; # or use different KeyValue if konto is not the from email address&lt;br /&gt;
 my $provider = &amp;quot;smtp.1und1.de&amp;quot;; # smtp.domain.tld:port see provider documentation&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail RCP: $rcpt&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Subject: $subject&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Text: $text&amp;quot;;&lt;br /&gt;
 #Log 1, &amp;quot;sendEmail Anhang: $attach&amp;quot;;&lt;br /&gt;
 if (not defined($attach)){$attach=&#039;&#039;}&lt;br /&gt;
 $ret .= qx(sendemail -f &#039;$from&#039; -t &#039;$rcpt&#039; -u &#039;$subject&#039; -m &#039;$text&#039; -a $attach -s &#039;$provider&#039; -xu &#039;$konto&#039; -xp &#039;$passwrd&#039; -o tls=auto -o message-charset=utf-8);&lt;br /&gt;
 $ret =~ s,[\r\n]*,,g;    # remove CR from return-string &lt;br /&gt;
 Log 1, &amp;quot;sendemail returned: $ret&amp;quot;; &lt;br /&gt;
}&lt;br /&gt;
###############################################################################&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Die Zugangsdaten für den Email-Account müssen dabei FHEM-intern gespeichert werden. Dazu gibt man einmalig über die Kommandozeile folgendes ein (user@domain und password müssen auf den Email Account geändert werden):&lt;br /&gt;
&lt;br /&gt;
:  &amp;lt;code&amp;gt;{setKeyValue(&amp;quot;myEmailKonto&amp;quot;,&#039;user@domain&#039;);;setKeyValue(&amp;quot;myEmailPasswrd&amp;quot;,&#039;password&#039;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um die TLS Verschlüsselung (ehem. SSL) nicht zu nutzen, muss in der viertletzten Zeile tls=no verwendet und der Port des Mailproviders auf 25 eigetragen werden. Sollte anschließend keine Mail verschickt werden, siehe Probleme.&lt;br /&gt;
&lt;br /&gt;
Falls der Body-Text in einem (Android-)Mailer auf dem Handy nicht gezeigt wird, kann der Parameter &#039;&#039;&#039;-o message-content-type=html&#039;&#039;&#039; helfen.&lt;br /&gt;
&lt;br /&gt;
Die Funktion kann beispielsweise folgendermaßen aufgerufen werden:&amp;lt;syntaxhighlight lang=&amp;quot;perl&amp;quot;&amp;gt;&lt;br /&gt;
{qx(sendemail -f &#039;Ab\@send.er&#039; -t &#039;Emp\@faeng.er&#039; -u &#039;subject&#039; -m &#039;text&#039; -a &#039;DateinameOderLeer&#039; -s &#039;smtpFQDN:port&#039; -xu &#039;MailUser&#039; -xp &#039;PasswortOhneKlammeraffen&#039;)}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Bei allen Feldern können generell doppelte &amp;quot; oder einfache &#039; Anführungszeichen verwendet werden. Innerhalb von &amp;quot;&amp;quot; müssen Sonderzeichen wie @ aber maskiert werden, da sie sonst als Steuerzeichen interpretiert werden:&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;&amp;quot;email\@email.domain&amp;quot; oder &#039;email@email.domain&#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Direkt in der FHEM Kommandozeile (ohne Anhang)&lt;br /&gt;
&lt;br /&gt;
: &amp;lt;code&amp;gt;{DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;)}&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die ordnungsgemäße Ausführung wird mit 71 quittiert.&lt;br /&gt;
&lt;br /&gt;
Beispiele mehrere Anhänge (Es ist wichtig Dateinamen mit Leerzeichen in separate Anführungszeichen zu setzen)&lt;br /&gt;
 {DebianMail(&amp;quot;email\@email.domain&amp;quot;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;Anhang1 Anhang2 Anhang3&amp;quot;)}&lt;br /&gt;
 {DebianMail(&#039;email@email.domain&#039;,&#039;Subject&#039;,&#039;Text&#039;,&amp;quot;&#039;with spaces Anhang1&#039; &#039;with spaces Anhang2&#039;&amp;quot;)}&lt;br /&gt;
oder (mehr als einen Empfänger, diese können durch Leerzeichen, Semikolon oder Komma getrennt sein)&lt;br /&gt;
 define Sonstiges at *01:00:00 {\&lt;br /&gt;
 DebianMail(&#039;email@email.domain,email2@email.domain&#039;,&amp;quot;Subject&amp;quot;,&amp;quot;Text&amp;quot;,&amp;quot;&#039;Anhang1&#039; &#039;Anhang2&#039; ...&amp;quot;);;\&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Die archivierte Doku zu &amp;lt;code&amp;gt;sendemail&amp;lt;/code&amp;gt; befindet sich in der Änderungshistorie: https://wiki.fhem.de/w/index.php?title=E-Mail_senden&amp;amp;oldid=37524&lt;br /&gt;
[[Kategorie:E-Mail]]&lt;/div&gt;</summary>
		<author><name>Torxgewinde</name></author>
	</entry>
</feed>