Browse Source

Update20180313

hmetzner 7 years ago
parent
commit
6080787db7
100 changed files with 13616 additions and 7670 deletions
  1. 495 196
      fhem/core/CHANGED
  2. 3 1
      fhem/core/FHEM/00_CM11.pm
  3. 3 3
      fhem/core/FHEM/00_DFPlayerMini.pm
  4. 4 4
      fhem/core/FHEM/00_FBAHA.pm
  5. 20 11
      fhem/core/FHEM/00_FBAHAHTTP.pm
  6. 17 12
      fhem/core/FHEM/00_HMUARTLGW.pm
  7. 3 1
      fhem/core/FHEM/00_HXB.pm
  8. 127 45
      fhem/core/FHEM/00_MQTT.pm
  9. 26 32
      fhem/core/FHEM/00_OWX.pm
  10. 2 2
      fhem/core/FHEM/00_SIGNALduino.pm
  11. 473 479
      fhem/core/FHEM/00_SONOS.pm
  12. 443 227
      fhem/core/FHEM/00_THZ.pm
  13. 82 37
      fhem/core/FHEM/00_TUL.pm
  14. 7 7
      fhem/core/FHEM/00_ZWDongle.pm
  15. 197 139
      fhem/core/FHEM/01_FHEMWEB.pm
  16. 3 1
      fhem/core/FHEM/09_BS.pm
  17. 3 1
      fhem/core/FHEM/09_USF1000.pm
  18. 793 535
      fhem/core/FHEM/10_CUL_HM.pm
  19. 4 1
      fhem/core/FHEM/10_EIB.pm
  20. 38 12
      fhem/core/FHEM/10_EQ3BT.pm
  21. 147 41
      fhem/core/FHEM/10_EnOcean.pm
  22. 27 9
      fhem/core/FHEM/10_FBDECT.pm
  23. 1447 409
      fhem/core/FHEM/10_FRM.pm
  24. 3 1
      fhem/core/FHEM/10_HXBDevice.pm
  25. 35 11
      fhem/core/FHEM/10_KNX.pm
  26. 31 3
      fhem/core/FHEM/10_MQTT_DEVICE.pm
  27. 2 2
      fhem/core/FHEM/10_MYSENSORS_DEVICE.pm
  28. 34 12
      fhem/core/FHEM/10_OWServer.pm
  29. 792 611
      fhem/core/FHEM/10_SOMFY.pm
  30. 5 6
      fhem/core/FHEM/10_ZWave.pm
  31. 8 2
      fhem/core/FHEM/10_pilight_ctrl.pm
  32. 2 2
      fhem/core/FHEM/11_FHT.pm
  33. 42 5
      fhem/core/FHEM/11_OWX_CCC.pm
  34. 107 31
      fhem/core/FHEM/11_OWX_FRM.pm
  35. 53 10
      fhem/core/FHEM/11_OWX_SER.pm
  36. 50 4
      fhem/core/FHEM/11_OWX_TCP.pm
  37. 4 3
      fhem/core/FHEM/13_KS300.pm
  38. 185 145
      fhem/core/FHEM/14_CUL_TCM97001.pm
  39. 18 9
      fhem/core/FHEM/14_CUL_WS.pm
  40. 2 2
      fhem/core/FHEM/15_CUL_EM.pm
  41. 2 2
      fhem/core/FHEM/18_CUL_HOERMANN.pm
  42. 104 37
      fhem/core/FHEM/20_FRM_AD.pm
  43. 116 31
      fhem/core/FHEM/20_FRM_IN.pm
  44. 155 30
      fhem/core/FHEM/20_FRM_OUT.pm
  45. 123 50
      fhem/core/FHEM/20_FRM_PWM.pm
  46. 4 19
      fhem/core/FHEM/20_N4HBUS.pm
  47. 3 1
      fhem/core/FHEM/20_X10.pm
  48. 8 3
      fhem/core/FHEM/21_HEOSGroup.pm
  49. 15 6
      fhem/core/FHEM/21_HEOSMaster.pm
  50. 17 3
      fhem/core/FHEM/21_HEOSPlayer.pm
  51. 502 84
      fhem/core/FHEM/21_N4HMODULE.pm
  52. 32 17
      fhem/core/FHEM/21_SONOSPLAYER.pm
  53. 1335 572
      fhem/core/FHEM/22_HOMEMODE.pm
  54. 322 182
      fhem/core/FHEM/23_LUXTRONIK2.pm
  55. 35 21
      fhem/core/FHEM/24_TPLinkHS110.pm
  56. 2041 1908
      fhem/core/FHEM/26_KM273.pm
  57. 406 549
      fhem/core/FHEM/30_DUOFERN.pm
  58. 386 74
      fhem/core/FHEM/30_HUEBridge.pm
  59. 6 11
      fhem/core/FHEM/30_MilightBridge.pm
  60. 65 39
      fhem/core/FHEM/31_Aurora.pm
  61. 45 25
      fhem/core/FHEM/31_HUEDevice.pm
  62. 29 24
      fhem/core/FHEM/31_LightScene.pm
  63. 3 1
      fhem/core/FHEM/31_MilightDevice.pm
  64. 4 3
      fhem/core/FHEM/31_PLAYBULB.pm
  65. 80 81
      fhem/core/FHEM/32_TechemHKV.pm
  66. 4 11
      fhem/core/FHEM/32_WifiLight.pm
  67. 2 2
      fhem/core/FHEM/32_mailcheck.pm
  68. 37 14
      fhem/core/FHEM/32_withings.pm
  69. 4 3
      fhem/core/FHEM/33_readingsChange.pm
  70. 249 4
      fhem/core/FHEM/33_readingsGroup.pm
  71. 2 2
      fhem/core/FHEM/33_readingsHistory.pm
  72. 2 2
      fhem/core/FHEM/33_readingsProxy.pm
  73. 59 15
      fhem/core/FHEM/34_ESPEasy.pm
  74. 51 13
      fhem/core/FHEM/36_LaCrosse.pm
  75. 9 6
      fhem/core/FHEM/36_LaCrosseGateway.pm
  76. 2 2
      fhem/core/FHEM/36_WMBUS.pm
  77. 10 8
      fhem/core/FHEM/37_fakeRoku.pm
  78. 13 11
      fhem/core/FHEM/37_harmony.pm
  79. 199 9
      fhem/core/FHEM/38_Broadlink.pm
  80. 507 103
      fhem/core/FHEM/38_netatmo.pm
  81. 2 2
      fhem/core/FHEM/39_alexa.pm
  82. 8 3
      fhem/core/FHEM/42_SYSMON.pm
  83. 6 6
      fhem/core/FHEM/44_S7_ARead.pm
  84. 5 5
      fhem/core/FHEM/44_S7_AWrite.pm
  85. 5 5
      fhem/core/FHEM/44_S7_DRead.pm
  86. 5 5
      fhem/core/FHEM/44_S7_DWrite.pm
  87. 7 6
      fhem/core/FHEM/44_S7_S5Client.pm
  88. 2 2
      fhem/core/FHEM/44_S7_S7Client.pm
  89. 72 51
      fhem/core/FHEM/46_SmartPi.pm
  90. 10 4
      fhem/core/FHEM/46_TeslaPowerwall2AC.pm
  91. 67 65
      fhem/core/FHEM/47_OBIS.pm
  92. 277 277
      fhem/core/FHEM/49_SSCam.pm
  93. 84 51
      fhem/core/FHEM/49_TBot_List.pm
  94. 188 93
      fhem/core/FHEM/50_TelegramBot.pm
  95. 17 8
      fhem/core/FHEM/51_I2C_TSL2561.pm
  96. 19 5
      fhem/core/FHEM/51_RPI_GPIO.pm
  97. 9 5
      fhem/core/FHEM/52_I2C_LM75A.pm
  98. 100 34
      fhem/core/FHEM/53_GHoma.pm
  99. 8 1
      fhem/core/FHEM/55_InfoPanel.pm
  100. 0 0
      fhem/core/FHEM/57_CALVIEW.pm

File diff suppressed because it is too large
+ 495 - 196
fhem/core/CHANGED


+ 3 - 1
fhem/core/FHEM/00_CM11.pm

@@ -23,7 +23,7 @@
 #
 ################################################################
 
-# $Id: 00_CM11.pm 9805 2015-11-07 06:38:08Z borisneubert $
+# $Id: 00_CM11.pm 16375 2018-03-10 15:40:02Z neubert $
 
 package main;
 
@@ -740,6 +740,8 @@ CM11_Ready($$)
 1;
 
 =pod
+=item summary    CM11 PC interface to receive and send X10 messages
+=item summary_DE CM11 PC Interface zum Empfangen und Senden von X10
 =begin html
 
 <a name="CM11"></a>

+ 3 - 3
fhem/core/FHEM/00_DFPlayerMini.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 00_DFPlayerMini.pm 14229 2017-05-09 18:25:06Z kaihs $
+# $Id: 00_DFPlayerMini.pm 16321 2018-03-03 20:25:45Z kaihs $
 # 
 # Support for the "FN-M16P Embedded MP3 Audio Module" aka DFPlayer Mini 
 # (http://www.flyrontech.com/eproducts/84.html)
@@ -1720,7 +1720,7 @@ sub DFPlayerMini_Notify($$)
   <li>
   tts &lt;text to translate to speech&gt;<br>
   <code>TTSDev</code> is used to calculate the MD5 hash of &lt;text to translate to speech&gt;. It then tries to play the file with this hash value.
-  If no reading for such a file exists and if the attribute <code>rememberMissingTTS</code> is set, a new reading Missing_MD5<md5> with <text to translate to speech> as its
+  If no reading for such a file exists and if the attribute <code>rememberMissingTTS</code> is set, a new reading Missing_MD5&lt;md5&gt; with &lt;text to translate to speech&gt; as its
   value is created.
   <br>Prerequisites:<br>
   This only works if this text had been translated earlier and the resulting mp3 file was stored in the cache directory of TTSDev. 
@@ -1953,7 +1953,7 @@ sub DFPlayerMini_Notify($$)
   <li>
   tts &lt;Text der in Sprache &uuml;bersetzt werden soll&gt;<br>
   <code>TTSDev</code> wird verwendet um den MD5 Hash von &lt;Text der in Sprache &uuml;bersetzt werden soll&gt; zu berechnen. Anschlie&szlig;end wird versucht die Datei mit diesem Hash abzuspielen.
-  Wenn kein Reading f&uuml;r diesen Hash existiert und das wenn das Attribute <code>rememberMissingTTS</code> gesetzt ist dann wird ein neues Reading Missing_MD5<md5> 
+  Wenn kein Reading f&uuml;r diesen Hash existiert und das wenn das Attribute <code>rememberMissingTTS</code> gesetzt ist dann wird ein neues Reading Missing_MD5&lt;md5&gt; 
   mit dem Wert &lt;Text der in Sprache &uuml;bersetzt werden soll&gt; angelegt.
   <br>Voraussetzungen:<br>
   Das funktioniert nur, wenn vorher der zu &uuml;bersetzende Text bereits einmal &uuml;bersetzt wurde und die daraus resultierende MP3 Datei im cache Verzeichnis

+ 4 - 4
fhem/core/FHEM/00_FBAHA.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 00_FBAHA.pm 15437 2017-11-16 08:48:48Z rudolfkoenig $
+# $Id: 00_FBAHA.pm 16293 2018-02-28 21:33:57Z rudolfkoenig $
 package main;
 
 use strict;
@@ -411,7 +411,7 @@ FBAHA_Ready($)
 <h3>FBAHA</h3>
 <ul>
   <br>Note: Fritz!OS 6.90 and later does not offer the AHA service needed by
-  this module. Use the successor FBAHAHTTP instead of this module.</b><br>
+  this module. Use the successor FBAHAHTTP instead of this module.<br>
 
   This module connects to the AHA server (AVM Home Automation) on a FRITZ!Box.
   It serves as the "physical" counterpart to the <a href="#FBDECT">FBDECT</a>
@@ -496,7 +496,7 @@ FBAHA_Ready($)
 <h3>FBAHA</h3>
 <ul>
   <br>Achtung: ab Fritz!OS 6.90 ist der ben&ouml;tigte Dienst deaktiviert,
-  bitte den Nachfolger FBAHAHTTP verwenden.</b><br>
+  bitte den Nachfolger FBAHAHTTP verwenden.<br>
 
   Dieses Modul verbindet sich mit dem AHA (AVM Home Automation) Server auf
   einem FRITZ!Box. Es dient als "physikalisches" Gegenst&uuml;ck zum <a
@@ -536,7 +536,7 @@ FBAHA_Ready($)
     Eintrag, siehe auch "get devList".
     </li>
   <li>reopen<br>
-    Schlie&szlig;t und &oulm;ffnet die Verbindung zum AHA Server. Nur f&uuml;r
+    Schlie&szlig;t und &ouml;ffnet die Verbindung zum AHA Server. Nur f&uuml;r
     debugging.
     </li>
   <li>reregister<br>

+ 20 - 11
fhem/core/FHEM/00_FBAHAHTTP.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 00_FBAHAHTTP.pm 15437 2017-11-16 08:48:48Z rudolfkoenig $
+# $Id: 00_FBAHAHTTP.pm 16344 2018-03-06 21:06:34Z rudolfkoenig $
 package main;
 
 # Documentation: AHA-HTTP-Interface.pdf, AVM_Technical_Note_-_Session_ID.pdf
@@ -21,7 +21,7 @@ FBAHAHTTP_Initialize($)
   $hash->{RenameFn} = "FBAHAHTTP_RenameFn";
   $hash->{DeleteFn} = "FBAHAHTTP_Delete";
   $hash->{AttrList} = "dummy:1,0 fritzbox-user polltime async_delay ".
-                      "disable:0,1 disabledForIntervals";
+                      "disable:0,1 disabledForIntervals fbTimeout";
 }
 
 
@@ -74,6 +74,7 @@ FBAHAHTTP_connect($)
   my $dr = sub {
     $hash->{STATE} = $_[0];
     Log 2, $hash->{STATE};
+    $hash->{CmdStack} = ();
     return $hash->{STATE};
   };
 
@@ -127,11 +128,13 @@ FBAHAHTTP_Poll($)
     return $ret if($ret);
   }
   my $sid = $hash->{".SID"};
+  my $host = ($hash->{DEF} =~ m/^http/i ? $hash->{DEF} : "http://$hash->{DEF}");
 
   HttpUtils_NonblockingGet({
-    url=>"http://$hash->{DEF}/webservices/homeautoswitch.lua?sid=$sid".
+    url=>"$host/webservices/homeautoswitch.lua?sid=$sid".
          "&switchcmd=getdevicelistinfos",
     loglevel => AttrVal($name, "verbose", 4),
+    timeout => AttrVal($name, "fbTimeout", 4),
     callback => sub {
       if($_[1]) {
         Log3 $name, 3, "$name: $_[1]";
@@ -219,9 +222,11 @@ FBAHAHTTP_ProcessStack($)
   my ($hash) = @_;
   my $name = $hash->{NAME};
   my $msg = $hash->{CmdStack}->[0];
+  my $host = ($hash->{DEF} =~ m/^http/i ? $hash->{DEF} : "http://$hash->{DEF}");
   HttpUtils_NonblockingGet({
-    url=>"http://$hash->{DEF}/webservices/homeautoswitch.lua?$msg",
+    url=>"$host/webservices/homeautoswitch.lua?$msg",
     loglevel => AttrVal($name, "verbose", 4),
+    timeout => AttrVal($name, "fbTimeout", 4),
     callback => sub {
       if($_[1]) {
         Log3 $name, 3, "$name: $_[1]";
@@ -308,6 +313,9 @@ FBAHAHTTP_Write($$$)
     <ul>
       <code>define fb1 FBAHAHTTP fritz.box</code><br>
     </ul>
+    Note: to specify HTTPS for the connection use https://fritz.box as
+    hostname. To explicitly specify the port, postfix the hostname with :port,
+    as in https://fritz.box:443
   </ul>
   <br>
 
@@ -332,6 +340,12 @@ FBAHAHTTP_Write($$$)
   <a name="FBAHAHTTPattr"></a>
   <b>Attributes</b>
   <ul>
+    <li><a href="#async_delay">async_delay</a><br>
+      additional delay inserted, when switching more than one device, default
+      is 0.2 seconds. Note: even with async_delay 0 there will be a delay, as
+      FHEM avoids sending commands in parallel, to avoid malfunctioning of the
+      Fritz!BOX AHA server).
+      </li>
     <li><a href="#disable">disable</a></li>
     <li><a href="#disabledForIntervals">disabledForIntervals</a></li>
     <li><a href="#dummy">dummy</a></li>
@@ -339,14 +353,9 @@ FBAHAHTTP_Write($$$)
     <li><a name="polltime">polltime</a><br>
       measured in seconds, default is 300 i.e. 5 minutes
       </li>
-
-    <li><a href="#async_delay">async_delay</a><br>
-      additional delay inserted, when switching more than one device, default
-      is 0.2 seconds. Note: even with async_delay 0 there will be a delay, as
-      FHEM avoids sending commands in parallel, to avoid malfunctioning of the
-      Fritz!BOX AHA server).
+    <li><a name="fbTimeout">fbTimeout</a><br>
+      timeout for getting answer from the Fritz!BOX. Default is 4 (seconds).
       </li>
-
   </ul>
   <br>
 </ul>

+ 17 - 12
fhem/core/FHEM/00_HMUARTLGW.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 00_HMUARTLGW.pm 15383 2017-11-03 08:11:29Z mgernoth $
+# $Id: 00_HMUARTLGW.pm 16166 2018-02-13 19:52:08Z mgernoth $
 #
 # HMUARTLGW provides support for the eQ-3 HomeMatic Wireless LAN Gateway
 # (HM-LGW-O-TW-W-EU) and the eQ-3 HomeMatic UART module (HM-MOD-UART), which
@@ -149,11 +149,6 @@ sub HMUARTLGW_Initialize($)
 	$hash->{RenameFn}  = "HMUARTLGW_Rename";
 	$hash->{ShutdownFn}= "HMUARTLGW_Shutdown";
 
-
-	$hash->{Clients} = ":CUL_HM:";
-	my %ml = ( "1:CUL_HM" => "^A......................" );
-	$hash->{MatchList} = \%ml;
-
 	$hash->{AttrList}= "hmId " .
 	                   "lgwPw " .
 	                   "hmKey hmKey2 hmKey3 " .
@@ -218,7 +213,7 @@ sub HMUARTLGW_DoInit($)
 			TEMPORARY => 1,
 			directReadFn => \&HMUARTLGW_Read,
 			DevType => "LGW-KeepAlive",
-			lgwHash => $hash,
+			'.lgwHash' => $hash,
 		};
 
 		$attr{$keepAlive->{NAME}}{room} = "hidden";
@@ -277,6 +272,10 @@ sub HMUARTLGW_Define($$)
 
 	$hash->{DeviceName} = $dev;
 
+	$hash->{Clients} = ":CUL_HM:";
+	my %ml = ( "1:CUL_HM" => "^A......................" );
+	$hash->{MatchList} = \%ml;
+
 	if (defined(AttrVal($name, "dummy", undef))) {
 		readingsSingleUpdate($hash, "state", "dummy", 1);
 		HMUARTLGW_updateCondition($hash);
@@ -313,7 +312,7 @@ sub HMUARTLGW_Undefine($$;$)
 sub HMUARTLGW_Reopen($;$)
 {
 	my ($hash, $noclose) = @_;
-	$hash = $hash->{lgwHash} if ($hash->{lgwHash});
+	$hash = $hash->{'.lgwHash'} if ($hash->{'.lgwHash'});
 	my $name = $hash->{NAME};
 
 	Log3($hash, 4, "HMUARTLGW ${name} Reopen");
@@ -331,7 +330,7 @@ sub HMUARTLGW_Ready($)
 
 	Log3($hash, 4, "HMUARTLGW ${name} ready: ${state}");
 
-	if ((!$hash->{lgwHash}) && $state eq "disconnected") {
+	if ((!$hash->{'.lgwHash'}) && $state eq "disconnected") {
 		#don't immediately reconnect when we just connected, delay
 		#for 5s because remote closed the connection on us
 		if (defined($hash->{LastOpen}) &&
@@ -411,7 +410,7 @@ sub HMUARTLGW_LGW_Init($)
 			$hash->{CNT} = hex($1);
 
 			my $lgwName = $name;
-			$lgwName = $hash->{lgwHash}->{NAME} if ($hash->{lgwHash});
+			$lgwName = $hash->{'.lgwHash'}->{NAME} if ($hash->{'.lgwHash'});
 
 			my $lgwPw = AttrVal($lgwName, "lgwPw", undef);
 
@@ -1752,6 +1751,8 @@ sub HMUARTLGW_Get($@)
 	my ( $hash, $name, $cmd, @args ) = @_;
 	my $ret = "";
 
+	return "Unknown argument ${cmd}, choose one of " if ($hash->{DevType} eq "LGW-KeepAlive");
+
 	if ($cmd eq "assignIDs") {
 		foreach my $peer (keys(%{$hash->{Peers}})) {
 			next if ($hash->{Peers}{$peer} !~ m/^\+/);
@@ -1785,6 +1786,8 @@ sub HMUARTLGW_Set($@)
 
 	return "\"set\" needs at least one parameter" if (!$cmd);
 
+	return "Unknown argument ${cmd}, choose one of " if ($hash->{DevType} eq "LGW-KeepAlive");
+
 	if ($cmd eq "hmPairForSec") {
 		$arg = 60 if(!$arg || $arg !~ m/^\d+$/);
 		HMUARTLGW_RemoveHMPair("hmPairForSec:$name");
@@ -1844,6 +1847,8 @@ sub HMUARTLGW_Attr(@)
 
 	Log3($hash, 5, "HMUARTLGW ${name} Attr ${cmd} ${aName} ".(($aVal)?$aVal:""));
 
+	return "Attribute ${cmd} not supported on keepAlive-subdevice" if ($hash->{DevType} eq "LGW-KeepAlive");
+
 	if ($aName eq "hmId") {
 		if ($cmd eq "set") {
 			my $owner_ccu = InternalVal($name, "owner_CCU", undef);
@@ -1875,7 +1880,7 @@ sub HMUARTLGW_Attr(@)
 			$attr{$name}{$aName} = "$no:".
 				(($val =~ m /^[0-9A-Fa-f]{32}$/ )
 				 ? $val
-				 : unpack('H*', md5($val)));
+				 : unpack('H*', Digest::MD5::md5($val)));
 			$retVal = "$aName set to $attr{$name}{$aName}"
 				if($aVal ne $attr{$name}{$aName});
 		} else {
@@ -2393,7 +2398,7 @@ sub HMUARTLGW_updateCoPro($$) {
 sub HMUARTLGW_getVerbLvl($$$$) {
 	my ($hash, $src, $dst, $def) = @_;
 
-	$hash = $hash->{lgwHash} if (defined($hash->{lgwHash}));
+	$hash = $hash->{'.lgwHash'} if (defined($hash->{'.lgwHash'}));
 
 	#Lookup IDs on change
 	if (defined($hash->{Helper}{Log}{Resolve}) && $init_done) {

+ 3 - 1
fhem/core/FHEM/00_HXB.pm

@@ -1,4 +1,4 @@
-# $Id: 00_HXB.pm 7911 2015-02-07 21:11:31Z borisneubert $
+# $Id: 00_HXB.pm 16375 2018-03-10 15:40:02Z neubert $
 ################################################################
 #
 #  Copyright notice
@@ -126,6 +126,8 @@ sub HXB_Read($)
 
 
 =pod
+=item summary    receive multicast messages from Hexabus devices
+=item summary_DE empfange Multicast-Nachrichten von Hexabus-Ger&auml;ten
 =begin html
 
 <a name="HXB"></a>

+ 127 - 45
fhem/core/FHEM/00_MQTT.pm

@@ -20,7 +20,7 @@
 #     You should have received a copy of the GNU General Public License
 #     along with fhem.  If not, see <http://www.gnu.org/licenses/>.
 #
-# $Id: 00_MQTT.pm 15126 2017-09-24 07:43:17Z eisler $
+# $Id: 00_MQTT.pm 16252 2018-02-23 21:32:59Z eisler $
 #
 ##############################################
 
@@ -59,14 +59,14 @@ sub MQTT_Initialize($) {
   $hash->{NotifyFn}   = "MQTT::Notify";
   $hash->{AttrFn}     = "MQTT::Attr";
 
-  $hash->{AttrList} = "keep-alive "."last-will "."on-connect on-disconnect on-timeout ".$main::readingFnAttributes;
+  $hash->{AttrList} = "keep-alive "."last-will client-id "."on-connect on-disconnect on-timeout ".$main::readingFnAttributes;
 }
 
 package MQTT;
 
 use Exporter ('import');
 @EXPORT = ();
-@EXPORT_OK = qw(send_publish send_subscribe send_unsubscribe client_attr client_subscribe_topic client_unsubscribe_topic topic_to_regexp);
+@EXPORT_OK = qw(send_publish send_subscribe send_unsubscribe client_attr client_subscribe_topic client_unsubscribe_topic topic_to_regexp parseParams);
 %EXPORT_TAGS = (all => [@EXPORT_OK]);
 
 use strict;
@@ -242,6 +242,82 @@ sub Set($@) {
   };
 }
 
+sub parseParams($;$$) {
+
+    my ( $cmd, $separator, $joiner ) = @_;
+    $separator = ' '        if ( !$separator );
+    $joiner    = $separator if ( !$joiner );   # needed if separator is a regexp
+    my ( @a, %h );
+
+    my @params;
+    if ( ref($cmd) eq 'ARRAY' ) {
+        @params = @{$cmd};
+    }
+    else {
+        @params = split( $separator, $cmd );
+    }
+
+    while (@params) {
+        my $param = shift(@params);
+        next if ( $param eq "" );
+        my ( $key, $value ) = split( ':', $param, 2 );
+
+        if ( !defined($value) ) {
+            $value = $key;
+            $key   = undef;
+
+        # the key can not start with a { -> it must be a perl expression # vim:}
+        }
+        elsif ( $key =~ m/^\s*{/ ) {    # for vim: }
+            $value = $param;
+            $key   = undef;
+        }
+
+        #collect all parts until the closing ' or "
+        while ( $param && $value =~ m/^('|")/ && $value !~ m/$1$/ ) {
+            my $next = shift(@params);
+            last if ( !defined($next) );
+            $value .= $joiner . $next;
+        }
+
+        #remove matching ' or " from the start and end
+        if ( $value =~ m/^('|")/ && $value =~ m/$1$/ ) {
+            $value =~ s/^.(.*).$/$1/;
+        }
+
+        #collect all parts until opening { and closing } are matched
+        if ( $value =~ m/^\s*{/ ) {    # } for match
+            my $count = 0;
+            for my $i ( 0 .. length($value) - 1 ) {
+                my $c = substr( $value, $i, 1 );
+                ++$count if ( $c eq '{' );
+                --$count if ( $c eq '}' );
+            }
+
+            while ( $param && $count != 0 ) {
+                my $next = shift(@params);
+                last if ( !defined($next) );
+                $value .= $joiner . $next;
+
+                for my $i ( 0 .. length($next) - 1 ) {
+                    my $c = substr( $next, $i, 1 );
+                    ++$count if ( $c eq '{' );
+                    --$count if ( $c eq '}' );
+                }
+            }
+        }
+
+        if ( defined($key) ) {
+            $h{$key} = $value;
+        }
+        else {
+            push @a, $value;
+        }
+
+    }
+    return ( \@a, \%h );
+}
+
 sub parsePublishCmdStr($) {
   my ($str) = @_;
 
@@ -257,51 +333,46 @@ sub parsePublishCmdStr($) {
   return undef;
 }
 
-sub parsePublishCmd(@) {
-  my @a = @_;
-  # [qos:?] [retain:?] topic value
-  
-  return undef if(!@a);
-  return undef if(scalar(@a)<1);
-  
-  my $qos = 0;
-  my $retain = 0;
-  my $topic = undef;
-  my $value = "\0";
-  my $expression = undef;
-  
-  while (scalar(@a)>0) {
-    my $av = shift(@a);
-    if($av =~ /\{.*\}/) {
-      $expression = $av;
-      next;
+sub parsePublishCmd(@) { 
+    my @a = @_;
+    my ( $aa, $bb ) = parseParams(\@a);
+
+    my $qos        = 0;
+    my $retain     = 0;
+    my $topic      = undef;
+    my $value      = "\0";
+    my $expression = undef;
+
+    if ( exists( $bb->{'qos'} ) ) {
+        $qos = $bb->{'qos'};
     }
-    my ($pn,$pv) = split(":",$av);
-    if(defined($pv)) {
-      if($pn eq "qos") {
-        if($pv >=0 && $pv <=2) {
-          $qos = $pv;
+
+    if ( exists $bb->{'retain'} ) {
+        $retain = $bb->{'retain'};
+    }
+
+    my @aaa = ();
+    my @xaa = @{$aa};
+
+    while ( scalar(@xaa) > 0 ) {
+        my $av = shift @xaa;
+        if ( $av =~ /\{.*\}/ ) {
+            $expression = $av;
+            next;
         }
-      } elsif($pn eq "retain") {
-        if($pv >=0 && $pv <=1) {
-          $retain = $pv;
+        else {
+            push @aaa, $av;
         }
-      } else {
-        # ignore
-        next;
-      }
-    } else {
-      $topic = $av;
-      last;
     }
-  }
-  
-  if(scalar(@a)>0) {
-    $value = join(" ", @a);
-  }
-  
-  return undef unless $topic || $expression;  
-  return ($qos, $retain,$topic, $value, $expression);
+
+    $topic = shift(@aaa);
+
+    if ( scalar(@aaa) > 0 ) {
+        $value = join( " ", @aaa );
+    }
+    return undef unless $topic || $expression;
+    return ( $qos, $retain, $topic, $value, $expression );
+
 }
 
 sub Notify($$) {
@@ -564,9 +635,10 @@ sub send_connect($) {
   my $pass = getKeyValue($name."_pass");
   
   my $lw = AttrVal($name,"last-will",undef);
+  my $clientId = AttrVal($name,"client-id",undef);
   my ($willqos, $willretain,$willtopic, $willmessage) = parsePublishCmdStr($lw);
   
-  return send_message($hash, message_type => MQTT_CONNECT, keep_alive_timer => $hash->{timeout}, user_name => $user, password => $pass, will_topic => $willtopic,  will_message => $willmessage, will_retain => $willretain, will_qos => $willqos);
+  return send_message($hash, message_type => MQTT_CONNECT, keep_alive_timer => $hash->{timeout}, user_name => $user, password => $pass, client_id=>$clientId, will_topic => $willtopic,  will_message => $willmessage, will_retain => $willretain, will_qos => $willqos);
 };
 
 sub send_publish($@) {
@@ -814,6 +886,8 @@ sub client_start($) {
     $client->{message_ids}->{$msgid}++;
     readingsSingleUpdate($client,"transmission-state","subscribe sent",1);
   }
+  
+  return undef;
 };
 
 sub client_stop($) {
@@ -879,6 +953,14 @@ sub client_stop($) {
       </p>
     </li>
     <li>
+      <p><code>attr &lt;name&gt; client-id client id</code><br/>
+         redefines client id
+      </p>
+      <p>example:<br/>
+      <code>attr mqtt client-id fhem1234567</code>
+      </p>
+    </li>
+    <li>
       <p>on-connect, on-disconnect<br/>
       <code>attr &lt;name&gt; on-connect {Perl-expression} &lt;topic&gt; &lt;message&gt;</code><br/>
          Publish the specified message to a topic at connect / disconnect (counterpart to lastwill) and / or evaluation of Perl expression<br/>

+ 26 - 32
fhem/core/FHEM/00_OWX.pm

@@ -9,7 +9,7 @@
 #
 # Prof. Dr. Peter A. Henning
 #
-# $Id: 00_OWX.pm 15392 2017-11-05 06:46:46Z phenning $
+# $Id: 00_OWX.pm 16217 2018-02-18 16:25:30Z phenning $
 #
 ########################################################################################
 ########################################################################################
@@ -85,11 +85,11 @@ use vars qw{%owg_family %gets %sets $owx_version $owx_debug};
    "qstatus" => "P"
 );
 
-#-- These occur in a pulldown menu as settable values for the bus master
+#-- These occur in a pulldown menu as settable values for the bus master 
+#   (expert mode: all, standard mode: only reopen)
 %sets = (
    "close"        => "c", 
-   "open"         => "o", 
-   "closeopen"    => "co" ,
+   "open"         => "O", 
    "reopen"       => "R",
    "discover"     => "C",
    "detect"       => "T",
@@ -98,7 +98,7 @@ use vars qw{%owg_family %gets %sets $owx_version $owx_debug};
 );
 
 #-- some globals needed for the 1-Wire module
-$owx_version="7.05";
+$owx_version="7.08";
 
 #-- debugging now verbosity, this is just for backward compatibility
 $owx_debug=0;
@@ -130,6 +130,7 @@ sub OWX_Initialize ($) {
   $hash->{AttrFn}  = "OWX_Attr";
   $hash->{AttrList}= "asynchronous:0,1 dokick:0,1 ".
                      "interval timeout opendelay expert:0_def,1_detail ".
+                     "IODev ".
                      $readingFnAttributes;    
 }
 
@@ -189,9 +190,9 @@ sub OWX_Define ($$) {
     $hwdevice = OWX_I2C->new($hash);
     
   #-- check if we have a COC/CUNO interface attached  
-  }elsif( (defined( $defs{$dev}->{VERSION} ) ? $defs{$dev}->{VERSION} : "") =~ m/CSM|CUNO/ ){
-    require "$attr{global}{modpath}/FHEM/11_OWX_CCC.pm";
-    $hwdevice = OWX_CCC->new($hash);
+  }elsif( (defined( $defs{$dev}->{VERSION} ) ? $defs{$dev}->{VERSION} : "") =~ m/CSM|CUNO|MapleCUN...(4|5|6|7|C|D|E|F)/ ){
+     require "$attr{global}{modpath}/FHEM/11_OWX_CCC.pm";
+     $hwdevice = OWX_CCC->new($hash);
     
   #-- check if we are connecting to Arduino (via FRM):
   } elsif ($dev =~ /.*\:\d{1,2}$/) {
@@ -1045,41 +1046,30 @@ sub OWX_Set($@) {
   my $name = shift @a;
   my $res  = 0;
 
-  #-- for the selector: which values are possible
+  #-- for the selector: all values are possible for expert, otherwise only reopen
   return ( (AttrVal($name,"expert","") eq "1_detail") ? join(":noArg ", sort keys %sets).":noArg" : "reopen:noArg")
     if(!defined($sets{$a[0]}));
   return "OWX_Set: With unknown argument $a[0], choose one of " .
      ( (AttrVal($name,"expert","") eq "1_detail") ? join(" ", sort keys %sets) : "reopen")
     if(!defined($sets{$a[0]}));
-  
-  #-- Set reopen
-  if( $a[0] eq "reopen" ){
-    DevIo_OpenDev($hash, 1, undef);
+
+  my $owx   = $hash->{OWX};
+
+  #-- Set open
+  if( $a[0] eq "open" ){
+    $owx->Open();
     $res = 0;
-  }
-  
-  #-- expert mode
-  #-- Set closedev
+  } 
+
+  #-- Set close
   if( $a[0] eq "close" ){
-    OWX_WDBGL($name,1,"====> CLOSING DEVICE",main::DevIo_CloseDev($hash));
-    $res = 0;
-  }
-  
-  #-- Set opendev
-  if( $a[0] eq "open" ){
-    OWX_WDBGL($name,1,"====> OPENING DEVICE",main::DevIo_OpenDev($hash,0,undef));
+    $owx->Close();
     $res = 0;
   }
   
-  #-- Set closeopendev
-  if( $a[0] eq "closeopen" ){
-    OWX_WDBGL($name,1,"====> CLOSING DEVICE",main::DevIo_CloseDev($hash));
-    OWX_WDBGL($name,1,"      OPENING DEVICE",main::DevIo_OpenDev($hash, 0, undef));  
-  }
-  
   #-- Set reopen
   if( $a[0] eq "reopen" ){
-    OWX_WDBGL($name,1,"====> REOPENING DEVICE",main::DevIo_OpenDev($hash, 1, undef));
+    $owx->Reopen();
     $res = 0;
   }
   
@@ -1667,6 +1657,7 @@ sub OWX_WDBGL($$$$) {
 
 <a name="OWX"></a>
         <h3>OWX</h3>
+        <ul>
         <p> Backend module to commmunicate with 1-Wire bus devices</p>
         <ul>
             <li>via an active DS2480/DS9097U bus master interface attached to an USB
@@ -1696,7 +1687,7 @@ sub OWX_WDBGL($$$$) {
             <li><a name="owx_reopen">
                     <code>set &lt;name&gt; reopen</code>
                 </a>
-                <br />re-opens the interface ans re-initializes the 1-Wire bus.
+                <br />re-opens the interface and re-initializes the 1-Wire bus.
             </li>
         </ul>
         <br />
@@ -1734,11 +1725,14 @@ sub OWX_WDBGL($$$$) {
                 <br />time interval in seconds for kicking temperature sensors and checking for alarms, default 300 s</li>
             <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
         </ul>
+        </ul>
 =end html
 =begin html_DE
 
 <a name="OWX"></a>
 <h3>OWX</h3>
+<ul>
 <a href="http://fhemwiki.de/wiki/Interfaces_f%C3%BCr_1-Wire">Deutsche Dokumentation im Wiki</a> vorhanden, die englische Version gibt es hier: <a href="/fhem/docs/commandref.html#OWX">OWX</a> 
+</ul>
 =end html_DE
 =cut

+ 2 - 2
fhem/core/FHEM/00_SIGNALduino.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 00_SIGNALduino.pm 15471 2017-11-21 23:34:50Z Sidey $
+# $Id: 00_SIGNALduino.pm 16390 2018-03-11 22:22:56Z Sidey $
 #
 # v3.3.2 (stable release 3.3)
 # The module is inspired by the FHEMduino project and modified in serval ways for processing the incomming messages
@@ -2782,7 +2782,7 @@ sub SIGNALduino_ParseHttpResponse
     	{
 	    	my $filename;
 	    	
-	    	if ($param->{httpheader} =~ /Content-Disposition: attachment;filename=\"?([-+.\w]+)?\"/)
+	    	if ($param->{httpheader} =~ /Content-Disposition: attachment;.?filename=\"?([-+.\w]+)?\"?/)
 			{ 
 				$filename = $1;
 			} else {  # Filename via path if not specifyied via Content-Disposition

File diff suppressed because it is too large
+ 473 - 479
fhem/core/FHEM/00_SONOS.pm


File diff suppressed because it is too large
+ 443 - 227
fhem/core/FHEM/00_THZ.pm


+ 82 - 37
fhem/core/FHEM/00_TUL.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 00_TUL.pm 15260 2017-10-14 18:48:39Z andi291 $
+# $Id: 00_TUL.pm 15613 2017-12-15 18:39:59Z andi291 $
 # ABU 20150916 removed print: simpleWriteDate, cleaned init
 # ABU 20150918 fixed deprecated warning, fixed warning related to hex-conversion in simple-write
 # ABU 20151123 added error-label in getGroup. Responsible for error-handling, if knxd is not accesible
@@ -17,6 +17,7 @@
 # ABU 20170427 cleaned logs
 # ABU 20171006 deactivated default-log-entry
 # ABU 20171006 EIB requires different handling of extended GAD --> added
+# docM 20171106 fixed problem when OBD-IP adapter is offline during FHEM startup  
 
 
 package main;
@@ -138,7 +139,7 @@ TUL_Undef($$)
 		if(defined($defs{$d}) && defined($defs{$d}{IODev}) && $defs{$d}{IODev} == $hash)
 		{
 			my $lev = ($reread_active ? 4 : 2);
-			Log(GetLogLevel($name,$lev), "deleting port for $d");
+			Log (GetLogLevel($name,$lev), "deleting port for $d");
 			delete $defs{$d}{IODev};
 		}
 	}
@@ -183,8 +184,12 @@ TUL_DoInit($)
 	TUL_Clear($hash);
 
 	# send any initializing request if needed
-	# TODO move to device init 
-	return 1 unless openGroupSocket($hash);
+	# TODO move to device init
+
+# docM 2017-11-05
+# moved openGroupSocket() to TUL_OpenDev. 
+#	return 1 unless openGroupSocket($hash);
+# /docM
   
 	# reset buffer
 	purgeReceiverBuf($hash);
@@ -206,6 +211,11 @@ TUL_Write($$$)
 	
 	return if(!defined($fn));
 	
+# docm 2017-11-05
+# Discard message if TUL is disconnected
+	return if($hash->{STATE} eq "disconnected");
+# /docm
+	
 	#Discard message, if not set to backward-compatibility
 	if (($useEIB =~ m/0/) and ($fn =~ m/\^B/))
 	{
@@ -415,7 +425,7 @@ TUL_SimpleRead($)
 	$buf .= $dst;
 	$buf .= $data;
   
-	Log(4,"SimpleRead: $buf\n");
+	Log (4,"SimpleRead: $buf\n");
   
 	return $buf;
 }
@@ -495,6 +505,25 @@ TUL_OpenDev($$)
 		$hash->{DevType} = 'EIBD';
 		$hash->{TCPDev} = $conn;
 		$hash->{FD} = $conn->fileno();
+# docM 2017-11-05
+# Call openGroupSocket() here, as it is part of device initialization.
+		if (openGroupSocket($hash))
+		{
+			Log (3, "OpenDev: OBD response from $dev") if($reopen);
+		}
+		else
+		{
+# failed to connect to OBD. Close socket and start polling		
+			Log (3, "OpenDev: No OBD response from $dev") if(!$reopen);
+			TUL_CloseDev($hash);
+			$readyfnlist{"$name.$dev"} = $hash;
+			$hash->{STATE} = "disconnected";
+			$hash->{NEXT_OPEN} = time()+60;
+			return "";
+		}
+
+# /docM
+
 		delete($readyfnlist{"$name.$dev"});
 		$selectlist{"$name.$dev"} = $hash;
 	}
@@ -590,7 +619,7 @@ TUL_OpenDev($$)
 	if($ret) 
 	{
 		TUL_CloseDev($hash);
-		Log (1, "Cannot init $dev, ignoring it");
+		Log (1, "OpenDev: Cannot init $dev, ignoring it");
 	}
 
 	DoTrigger($name, "CONNECTED") if($reopen);
@@ -669,7 +698,7 @@ sub tul_hex2addr
     }
     else
     {
-		Log(3,"Bad EIB address string: \'$str\'\n");
+		Log (3,"hex2addr: Bad KNX address string: \'$str\'\n");
 		return;
     }
 }
@@ -782,7 +811,7 @@ sub decode_eibd($)
 
     @data = unpack ("C" . length($bytes), $bytes);
     my $datalen = @data;
-    Log (5, "decode_eibd byte len: " . length($bytes) . " array size: $datalen");
+    Log (5, "decode_eibd: byte len: " . length($bytes) . " array size: $datalen");
     
     # in case of data len > 1, the first byte (the one with apci) seems not to be used
     # and only the following byte are of interest.
@@ -806,7 +835,7 @@ sub encode_eibd($)
     $APCI = $apcivalues{$mref->{'type'}};
     if (!(defined $APCI)) 
 	{
-		Log(3,"Bad EIB message type $mref->{'type'}\n");
+		Log (3,"encode_eibd: Bad KNX message type $mref->{'type'}\n");
 		return;
     }
     @data = @{$mref->{'data'}};
@@ -814,7 +843,7 @@ sub encode_eibd($)
     @data = (0x0) if(!@data || !defined($data[0])); #make sure data has at least one element
 	#@data = (0x0) if(!(defined @data) || !(defined $data[0])); #make sure data has at least one element
     my $datalen = @data;
-    Log (5,"encode_eibd dst: $mref->{'dst'} apci: $APCI datalen: $datalen data: @data");
+    Log (5,"encode_eibd: dst: $mref->{'dst'} apci: $APCI datalen: $datalen data: @data");
     @msg = (
 	    tul_hex2addr( $mref->{'dst'}), 	# Destination address
 	    0x0 | ($APCI >> 2), 	# TPDU type, Sequence no, APCI (msb)
@@ -859,11 +888,11 @@ sub decode_tpuart($)
 	#if(($ctrl & 0xB0)!=0xB0)
     if(($ctrl & 0x90)!=0x90)
     {
-		Log (3,"Control Byte " . sprintf("0x%02x",$ctrl) . " does not match expected mask 2x1001nnnn");
+		Log (3,"decode_tpuart: Control Byte " . sprintf("0x%02x",$ctrl) . " does not match expected mask 2x1001nnnn");
     	return undef;
     }
 
-   Log (5,"msg cmd: " . sprintf("0x%02x",$cmd) ." datalen: $len");
+   Log (5,"decode_tpuart: msg cmd: " . sprintf("0x%02x",$cmd) ." datalen: $len");
    
    my $apci = ($cmd >> 6) & 0x0F;
    if($len == 2) 
@@ -871,7 +900,7 @@ sub decode_tpuart($)
    	$bytes = pack("C",$cmd & 0x3F);
    }
 
-   Log (5,"msg cmd: " . sprintf("0x%02x",$cmd) ." datalen: $len apci: $apci");
+   Log (5,"decode_tpuart: msg cmd: " . sprintf("0x%02x",$cmd) ." datalen: $len apci: $apci");
    
     my %msg;
     my @data;
@@ -889,7 +918,7 @@ sub decode_tpuart($)
 
     @data = unpack ("C" . length($bytes), $bytes);
     my $datalen = @data;
-    Log (5, "decode_tpuart byte len: " . length($bytes) . " array size: $datalen");
+    Log (5, "decode_tpuart: decode_tpuart byte len: " . length($bytes) . " array size: $datalen");
     
     $msg{'data'} = \@data;
     return \%msg;
@@ -906,18 +935,18 @@ sub encode_tpuart($)
     $APCI = $apcivalues{$mref->{'type'}};
     if (!(defined $APCI)) 
 	{
-		Log (3,"Bad EIB message type $mref->{'type'}\n");
+		Log (3,"encode_tpuart: Bad KNX message type $mref->{'type'}\n");
 		return;
     }
     @data = @{$mref->{'data'}};
     my $datalen = @data;
     if($datalen > 14)
     {
-  		Log (3,"Bad EIB message length $datalen\n");
+  		Log (3,"encode_tpuart: Bad KNX message length $datalen\n");
 		return;
    	
     }
-    Log (5,"encode_tpuart dst: $mref->{'dst'} apci: $APCI datalen: $datalen data: @data");
+    Log (5,"encode_tpuart: dst: $mref->{'dst'} apci: $APCI datalen: $datalen data: @data");
     @msg = (
     	0xBC, # EIB ctrl byte
 		tul_hex2addr($mref->{'src'}), # src address    	
@@ -970,6 +999,10 @@ sub openGroupSocket($)
 	{
 	    my @msg = (0x0026,0x0000,0x00);			# EIB_OPEN_GROUPCON
 	    sendRequest ($hash, pack "nnC" ,@msg);
+# docM 2017-11-06
+		use IO::Select;
+		goto error unless (IO::Select->new($hash->{TCPDev})->can_read(10)); 
+# /docM		
 	    goto error unless my $answer = getRequest($hash);
 	    my $head = unpack ("n", $answer);
 	    goto error unless $head == 0x0026;
@@ -978,7 +1011,14 @@ sub openGroupSocket($)
 	return 1;
 
   error:
-    print "openGroupSocket failed\n";
+  
+  Log (0,"openGroupSocket: failed\n");
+ 
+# docM 2017-11-05
+# removed print 
+#    print "openGroupSocket failed\n";
+# /docM	
+
     return undef;
 }
 
@@ -1019,12 +1059,12 @@ sub purgeReceiverBuf($)
 	my ($hash) = @_;
 	if($hash->{DevType} eq 'TPUART')
 	{
-	  Log (5,"purging receiver buffer ");
+	  Log (5,"purgeReceiverBuf: purging...");
 	  my $data = undef;
 	  do
 	  {
 	    my(undef,$data) =  $hash->{USBDev}->read(100);
-	    Log (5,"purging packet: ". unpack("H*",$data) . "\n") if(defined($data) and length($data)>0);
+	    Log (5,"purgeReceiverBuf: purging packet: ". unpack("H*",$data) . "\n") if(defined($data) and length($data)>0);
 	  } while(defined($data) and length($data)>0)
 	}
 }
@@ -1035,13 +1075,13 @@ sub getRequestFixLength($$)
 	
 	if($hash->{DevType} eq 'TPUART')
 	{
-		Log (5,"waiting to receive $len bytes ...");
+		Log (5,"getRequestFixLength: waiting to receive $len bytes ...");
 		my $buf = "";
 		while(length($buf)<$len)
 		{
 			#select(undef,undef,undef,0.5);
 			my (undef,$data) =  $hash->{USBDev}->read($len-length($buf));
-	    	Log (5,"Received fixlen packet: ". unpack("H*",$data) . "\n") if(defined($data) and length($data)>0);
+	    	Log (5,"getRequestFixLength: Received fixlen packet: ". unpack("H*",$data) . "\n") if(defined($data) and length($data)>0);
 				
 			$buf .= $data if(defined($data));
 			#Log (5,"buf len: " . length($buf) . " expected: $len");
@@ -1056,10 +1096,10 @@ sub getRequestFixLength($$)
 			$hash->{PARTIAL} .= $remainpart;
 			$buf = substr($buf,0,$len);
 			
-			Log (5,"we got too much.. buf(" .unpack("H*",$buf).") remainingpart(" .unpack("H*",$remainpart).")");
+			Log (5,"getRequestFiLength: we got too much.. buf(" .unpack("H*",$buf).") remainingpart(" .unpack("H*",$remainpart).")");
 		}
 		
-		Log (5,"getRequest len: $len packet: ". unpack("H*",$buf) . "\n");
+		Log (5,"getRequestFixLength: len: $len packet: ". unpack("H*",$buf) . "\n");
 		return $buf;
 	}
 	
@@ -1093,7 +1133,7 @@ sub getGroup($)
 	    	my $data = getRequestFixLength($hash,$reqlen-length($buf)) if($reqlen>length($buf));
 	    	if(length($buf)==0 && (!defined($data)||length($data)==0))
 	    	{
-	    		Log (5,"read fix length delivered no data.");
+	    		Log (5,"getGroup: read fix length delivered no data.");
 	    		return undef;
 	    	}
 	    	$buf .= $data if(defined($data));
@@ -1104,7 +1144,7 @@ sub getGroup($)
 	    	{
 	    		$buf = substr($buf,1);
 	    		$hash->{PARTIAL} = $buf;
-	    		Log (5,"TPUART RSP " . sprintf("0x%02x",$ctrl) ." ignored.");
+	    		Log (5,"getGroup: TPUART RSP " . sprintf("0x%02x",$ctrl) ." ignored.");
 	    		return undef;
 	    	}
 	    	
@@ -1112,7 +1152,7 @@ sub getGroup($)
 	    	{
 	    		my $routingcnt = unpack("xxxxxC", $buf);
 	    		$reqlen = ($routingcnt & 0x0F)+8;
-	    		Log (5,"receiving telegram with len: $reqlen");
+	    		Log (5,"getGroup: receiving telegram with len: $reqlen");
 	    	}
 			
 			
@@ -1124,8 +1164,8 @@ sub getGroup($)
 		}
 		while(!defined($telegram));
 	    
-	    Log (5, "Telegram: (".length($telegram)."): " . unpack("H*",$telegram));
-	    Log (5, "Buf: (".length($buf)."): " . unpack("H*",$buf));
+	    Log (5, "getGroup: Telegram: (".length($telegram)."): " . unpack("H*",$telegram));
+	    Log (5, "getGroup: Buf: (".length($buf)."): " . unpack("H*",$buf));
 	    
 	    $hash->{PARTIAL} = $buf;	
 	    my $msg = decode_tpuart($telegram);
@@ -1144,11 +1184,12 @@ sub getGroup($)
 	    return $msg;
 	}
 	
-	Log (2,"DevType $hash->{DevType} not supported for getGroup\n");
+	Log (2,"GetGroup: DevType $hash->{DevType} not supported for getGroup\n");
 	return undef;
 	
 	error:
-    print "seems like eibd not connected\n";
+	
+	Log (2,"GetGroup: seems like knxd not connected\n");
     return undef;  	    
 }
 
@@ -1164,21 +1205,25 @@ sub getRequest($)
 	    goto error unless sysread($hash->{TCPDev}, $data, 2);
 	    my $size = unpack ("n", $data);
 	    goto error unless sysread($hash->{TCPDev}, $data, $size);
-	    Log (5,"Received packet: ". unpack("H*",$data) . "\n");
+	    Log (5,"getRequest: Received packet: ". unpack("H*",$data) . "\n");
 	    return $data;
 	}
 	elsif($hash->{USBDev}) {
     	my $data = $hash->{USBDev}->input();
-	    Log (5,"Received packet: ". unpack("H*",$data) . "\n") if(defined($data) and length($data)>0);
+	    Log (5,"getRequest: Received packet: ". unpack("H*",$data) . "\n") if(defined($data) and length($data)>0);
     	return $data;
 	}
 	
-	Log (1,"TUL $hash->{NAME}: can not select a source for reading data.");
+	Log (1,"getRequest: TUL $hash->{NAME}: can not select a source for reading data.");
 	return undef;
 	
   error:
-    printf "eibd communication failed\n";
-    return undef;
+# docM 2017-11-05 remove print  
+#    printf "eibd communication failed\n";
+# /docM
+
+  Log (2,"getRequest: communication to knxd failed\n");
+  return undef;
 	
 }
 
@@ -1203,7 +1248,7 @@ sub sendRequest($$)
 	}
 	else
 	{
-		Log (2,"TUL $hash->{NAME}: No known physical protocoll defined.");
+		Log (2,"sendRequest: TUL $hash->{NAME}: No known physical protocoll defined.");
 		return undef;
 	}
 	return 1;

+ 7 - 7
fhem/core/FHEM/00_ZWDongle.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 00_ZWDongle.pm 15491 2017-11-23 21:47:11Z rudolfkoenig $
+# $Id: 00_ZWDongle.pm 16293 2018-02-28 21:33:57Z rudolfkoenig $
 package main;
 
 use strict;
@@ -145,7 +145,7 @@ ZWDongle_Define($$)
     return undef;
 
   } elsif($dev !~ m/@/ && $dev !~ m/:/) {
-    $def .= "\@115200";  # default baudrate
+    $dev .= "\@115200";  # default baudrate
 
   }
 
@@ -209,7 +209,7 @@ ZWDongle_nlData($)
 
     my %line = (
       pos       => '['.AttrVal($e, "neighborListPos", "").']',
-      class     => '"zwBox"',
+      class     => '"zwBox col_link col_oddrow"',
       neighbors => '['.$nl.']'
     );
 
@@ -229,7 +229,7 @@ ZWDongle_nlData($)
   my $pos = AttrVal($d, "neighborListPos", "");
   my $nl = (@dn ? '"'.join('","',@dn).'"' : '');
   push @ret, "\"$d\":{\"txt\":\"$d\", \"pos\":[$pos],".
-                     "\"class\":\"zwDongle\",\"neighbors\":[$nl] }";
+             "\"class\":\"zwDongle col_oddrow col_link\",\"neighbors\":[$nl] }";
   return "{ \"saveFn\":\"attr {1} neighborListPos {2}\",".
            "\"firstObj\":\"$d\",".
            "\"el\":{".join(",",@ret)."} }";
@@ -569,7 +569,7 @@ ZWDongle_Get($@)
     my $maxlen = (length($msg) >= 10 ? 10 : length($msg));
     for(my $off=4; $off<$maxlen; $off+=2) {
         my $dec = hex(substr($msg, $off, 2));
-        if($dec == 127 || $dec == 0) {
+        if($dec == 127) {
           push @list, ("ch".($i+1).":N/A");
         } elsif($dec == 126) {
           push @list, ("ch".($i+1).":aboveMaxPower");
@@ -1167,13 +1167,13 @@ ZWDongle_Ready($)
 
   <li>routeFor &lt;device&gt; &lt;hop1&gt; &lt;hop2&gt; &lt;hop3&gt;
                &lt;hop4&gt; &lt;speed&gt;<br>
-    set priority routing for &ltdevice&gt. &ltdevice&gt and &lt;hopN&gt are
+    set priority routing for &lt;device&gt;. &lt;device&gt; and &lt;hopN&gt are
     either device name or decimal nodeId or 0 for unused.<br>
     &lt;speed&gt;: 1=9,6kbps; 2=40kbps; 3=100kbps
     </li>
 
   <li>sendNIF &lt;device&gt;<br>
-    Send NIF to the specified &lt;device&g.
+    Send NIF to the specified &lt;device&gt;.
     &lt;device&gt; is either device name or decimal nodeId.
     </li>
 

+ 197 - 139
fhem/core/FHEM/01_FHEMWEB.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 01_FHEMWEB.pm 15328 2017-10-27 10:51:17Z rudolfkoenig $
+# $Id: 01_FHEMWEB.pm 16350 2018-03-07 21:30:37Z rudolfkoenig $
 package main;
 
 use strict;
@@ -97,6 +97,7 @@ my $FW_lastWebName = "";  # Name of last FHEMWEB instance, for caching
 my $FW_lastHashUpdate = 0;
 my $FW_httpRetCode = "";
 my %FW_csrfTokenCache;
+my %FW_id2inform;
 
 #########################
 # As we are _not_ multithreaded, it is safe to use global variables.
@@ -132,7 +133,7 @@ FHEMWEB_Initialize($)
   $hash->{AttrFn}  = "FW_Attr";
   $hash->{DefFn}   = "FW_Define";
   $hash->{UndefFn} = "FW_Undef";
-  $hash->{NotifyFn}= ($init_done ? "FW_Notify" : "FW_SecurityCheck");
+  $hash->{NotifyFn}= "FW_Notify";
   $hash->{AsyncOutputFn} = "FW_AsyncOutput";
   $hash->{ActivateInformFn} = "FW_ActivateInform";
   no warnings 'qw';
@@ -140,6 +141,7 @@ FHEMWEB_Initialize($)
     CORS:0,1
     HTTPS:1,0
     CssFiles
+    Css:textField-long
     JavaScripts
     SVGcache:1,0
     addHtmlTitle:1,0
@@ -147,6 +149,7 @@ FHEMWEB_Initialize($)
     csrfToken
     csrfTokenHTTPHeader:0,1
     alarmTimeout
+    allowedHttpMethods
     allowedCommands
     allowfrom
     basicAuth
@@ -189,6 +192,7 @@ FHEMWEB_Initialize($)
     smallscreen:unused
     smallscreenCommands:0,1
     stylesheetPrefix
+    styleData:textField-long
     title
     touchpad:unused
     viewport
@@ -213,7 +217,6 @@ FHEMWEB_Initialize($)
     $FW_fhemwebjs = join(",", map { $_ = ~m/^fhemweb_(.*).js$/; $1 }
                               grep { /fhemweb_(.*).js$/ }
                               readdir(DH));
-    @FW_fhemwebjs = ("fhemweb.js");
     closedir(DH);
   }
 
@@ -224,37 +227,14 @@ FHEMWEB_Initialize($)
       FW_readIcons($pe);
     }
   }
-}
 
-#####################################
-sub
-FW_SecurityCheck($$)
-{
-  my ($ntfy, $dev) = @_;
-  return if($dev->{NAME} ne "global" ||
-            !grep(m/^INITIALIZED$/, @{$dev->{CHANGED}}));
-  my $motd = AttrVal("global", "motd", "");
-  if($motd =~ "^SecurityCheck") {
-    my @list1 = devspec2array("TYPE=FHEMWEB");
-    my @list2 = devspec2array("TYPE=allowed");
-    my @list3;
-    for my $l (@list1) { # This is a hack, as hardcoded to basicAuth
-      next if(!$defs{$l});
-      my $fnd = 0;
-      for my $a (@list2) {
-        next if(!$defs{$a});
-        my $vf = AttrVal($a, "validFor","");
-        $fnd = 1 if($vf && ($vf =~ m/\b$l\b/) && AttrVal($a, "basicAuth",""));
-      }
-      push @list3, $l if(!$fnd);
-    }
-    $motd .= (join(",", sort @list3).
-              " has no associated allowed device with basicAuth.\n")
-        if(@list3);
-    $attr{global}{motd} = $motd;
+  eval { require Digest::SHA; };
+  if($@) {
+    Log 4, $@;
+    Log 3, "FHEMWEB: Can't load Digest::SHA, ".
+           "longpoll via websocket is not available";
   }
-  $modules{FHEMWEB}{NotifyFn}= "FW_Notify";
-  return;
+  $FW_use_sha = 1;
 }
 
 #####################################
@@ -264,7 +244,9 @@ FW_Define($$)
   my ($hash, $def) = @_;
   my ($name, $type, $port, $global) = split("[ \t]+", $def);
   return "Usage: define <name> FHEMWEB [IPV6:]<tcp-portnr> [global]"
-        if($port !~ m/^(IPV6:)?\d+$/ || ($global && $global ne "global"));
+        if($port !~ m/^(IPV6:)?\d+$/);
+
+  FW_Undef($hash, undef) if($hash->{OLDDEF}); # modify
 
   foreach my $pe ("fhemSVG", "openautomation", "default") {
     FW_readIcons($pe);
@@ -299,6 +281,7 @@ FW_Undef($$)
   my ($hash, $arg) = @_;
   my $ret = TcpServer_Close($hash);
   if($hash->{inform}) {
+    delete $FW_id2inform{$hash->{FW_ID}} if($hash->{FW_ID});
     %FW_visibleDeviceHash = FW_visibleDevices();
     delete($logInform{$hash->{NAME}});
   }
@@ -366,7 +349,7 @@ FW_Read($$)
     }
   }
 
-  if($hash->{websocket}) { # Work in Progress (Forum #59713)
+  if($hash->{websocket}) { # 59713
     my $fin  = (ord(substr($hash->{BUF},0,1)) & 0x80)?1:0;
     my $op   = (ord(substr($hash->{BUF},0,1)) & 0x0F);
     my $mask = (ord(substr($hash->{BUF},1,1)) & 0x80)?1:0;
@@ -391,6 +374,7 @@ FW_Read($$)
     #  substr( $data, $i, 1, substr( $data, $i, 1, ) ^ substr($mask, $i% , 1) );
     #}
     #Log 1, "Received via websocket: ".unpack("H*",$data);
+    $hash->{BUF} = "";
     return;
   }
 
@@ -437,7 +421,8 @@ FW_Read($$)
   my ($method, $arg, $httpvers) = split(" ", $FW_httpheader[0], 3)
         if($FW_httpheader[0]);
   $method = "" if(!$method);
-  if($method !~ m/^(GET|POST)$/i){
+  my $ahm = AttrVal($FW_wname, "allowedHttpMethods", "GET|POST");
+  if($method !~ m/^($ahm)$/i){
     my $retCode = ($method eq "OPTIONS") ? "200 OK" : "405 Method Not Allowed";
     TcpServer_WriteBlocking($FW_chash,
       "HTTP/1.1 $retCode\r\n" .
@@ -486,6 +471,12 @@ FW_Read($$)
   delete $hash->{CONTENT_LENGTH};
   $hash->{LASTACCESS} = $now;
 
+  $FW_userAgent = $FW_httpheader{"User-Agent"};
+  $FW_userAgent = "" if(!defined($FW_userAgent));
+
+  $FW_ME = "/" . AttrVal($FW_wname, "webname", "fhem");
+  $FW_CSRF = (defined($defs{$FW_wname}{CSRFTOKEN}) ?
+                "&fwcsrf=".$defs{$FW_wname}{CSRFTOKEN} : "");
      
   if($FW_use_sha && $method eq 'GET' &&
      $FW_httpheader{Connection} && $FW_httpheader{Connection} =~ /Upgrade/i) {
@@ -511,10 +502,8 @@ FW_Read($$)
     return -1;
   }
 
-  $FW_userAgent = $FW_httpheader{"User-Agent"};
   $arg = "" if(!defined($arg));
   Log3 $FW_wname, 4, "$name $method $arg; BUFLEN:".length($hash->{BUF});
-  $FW_ME = "/" . AttrVal($FW_wname, "webname", "fhem");
   my $pf = AttrVal($FW_wname, "plotfork", undef);
   if($pf) {   # 0 disables
     # Process SVG rendering as a parallel process
@@ -600,7 +589,10 @@ FW_initInform($$)
     $me->{inform}{type}   = ($FW_room ? "status" : "raw");
     $me->{inform}{filter} = ($FW_room ? $FW_room : ".*");
   }
+  $FW_id2inform{$FW_id} = $me if($FW_id);
+
   my $filter = $me->{inform}{filter};
+  $filter =~ s/([[\]().+?])/\\$1/g if($filter =~ m/room=/); # Forum #80390
   $filter = "NAME=.*" if($filter eq "room=all");
   $filter = "room!=.+" if($filter eq "room=Unsorted");
 
@@ -636,6 +628,7 @@ FW_initInform($$)
       delete $defs{$FW_wname}{asyncOutput}{$FW_id};
     }
   }
+
   if($me->{inform}{withLog}) {
     $logInform{$me->{NAME}} = "FW_logInform";
   } else {
@@ -657,7 +650,8 @@ FW_addToWritebuffer($$@)
       if ( $len < 65536 ) {
         $txt = chr(0x81) . chr(0x7E) . pack('n', $len) . $txt;
       } else {
-        $txt = chr(0x81) . chr(0x7F) . chr(0x00) . chr(0x00) . chr(0x00) . chr(0x00) . pack('N', $len) . $txt;
+        $txt = chr(0x81) . chr(0x7F) . chr(0x00) . chr(0x00) .
+               chr(0x00) . chr(0x00) . pack('N', $len) . $txt;
       }
     }
   }
@@ -674,31 +668,28 @@ FW_AsyncOutput($$)
     $ret = $1;
 
   } else {
-    $ret =~ s/&/&amp;/g;
-    $ret =~ s/'/&apos;/g;
-    $ret =~ s/</&lt;/g;
-    $ret =~ s/>/&gt;/g;
+    $ret = FW_htmlEscape($ret);
     $ret = "<pre>$ret</pre>" if($ret =~ m/\n/ );
     $ret =~ s/\n/<br>/g;
   }
 
+  my $data = FW_longpollInfo('JSON',
+                             "#FHEMWEB:$FW_wname","FW_okDialog('$ret')","");
+
   # find the longpoll connection with the same fw_id as the page that was the
   # origin of the get command
-  my $found = 0;
-  my $data = FW_longpollInfo('JSON',
-                                "#FHEMWEB:$FW_wname","FW_okDialog('$ret')","");
-  foreach my $d (keys %defs ) {
-    my $chash = $defs{$d};
-    next if( $chash->{TYPE} ne 'FHEMWEB' );
-    next if( !$chash->{inform} );
-    next if( !$chash->{FW_ID} || $chash->{FW_ID} ne $hash->{FW_ID} );
-    FW_addToWritebuffer($chash, $data."\n");
-    $found = 1;
-    last;
+  my $fwid = $hash->{FW_ID};
+  if(!$fwid) {
+    Log3 $hash->{SNAME}, 4, "AsyncOutput from $hash->{NAME} without FW_ID";
+    return;
+  }
+  Log3 $hash->{SNAME}, 4, "AsyncOutput from $hash->{NAME}";
+  $hash = $FW_id2inform{$fwid};
+  if($hash) {
+    FW_addToWritebuffer($hash, $data."\n");
+  } else {
+    $defs{$FW_wname}{asyncOutput}{$fwid} = $data;
   }
-
-  $defs{$FW_wname}{asyncOutput}{$hash->{FW_ID}} = $data if( !$found );
-
   return undef;
 }
 
@@ -708,7 +699,7 @@ FW_closeConn($)
   my ($hash) = @_;
   if(!$hash->{inform} && !$hash->{BUF}) { # Forum #41125
     my $cc = AttrVal($hash->{SNAME}, "closeConn",
-                        $FW_userAgent && $FW_userAgent=~m/(iPhone|iPad|iPod)/);
+                     $FW_userAgent =~ m/(iPhone|iPad|iPod)/);
     if(!$FW_httpheader{Connection} || $cc) {
       TcpServer_Close($hash, 1);
     }
@@ -739,8 +730,6 @@ FW_answerCall($)
 
   $FW_RET = "";
   $FW_RETTYPE = "text/html; charset=$FW_encoding";
-  $FW_CSRF = (defined($defs{$FW_wname}{CSRFTOKEN}) ?
-                "&fwcsrf=".$defs{$FW_wname}{CSRFTOKEN} : "");
 
   $MW_dir = "$attr{global}{modpath}/FHEM";
   $FW_sp = AttrVal($FW_wname, "stylesheetPrefix", "");
@@ -748,6 +737,9 @@ FW_answerCall($)
   $FW_tp = ($FW_sp =~ m/smallscreen|touchpad/);
   @FW_iconDirs = grep { $_ } split(":", AttrVal($FW_wname, "iconPath",
                                 "$FW_sp:default:fhemSVG:openautomation"));
+  @FW_fhemwebjs = ("fhemweb.js");
+  push(@FW_fhemwebjs, "$FW_sp.js") if(-r "$FW_dir/pgm2/$FW_sp.js");
+
   if($arg =~ m,$FW_ME/floorplan/([a-z0-9.:_]+),i) { # FLOORPLAN: special icondir
     unshift @FW_iconDirs, $1;
     FW_readIcons($1);
@@ -830,12 +822,12 @@ FW_answerCall($)
   $FW_plotsize = AttrVal($FW_wname, "plotsize", $FW_ss ? "480,160" :
                                                 $FW_tp ? "640,160" : "800,160");
   my ($cmd, $cmddev) = FW_digestCgi($arg);
-  if($cmd && $FW_CSRF) {
+  if($cmd && $FW_CSRF && $cmd !~ m/style (list|select|eventMonitor)/) {
     my $supplied = defined($FW_webArgs{fwcsrf}) ? $FW_webArgs{fwcsrf} : "";
     my $want = $defs{$FW_wname}{CSRFTOKEN};
     if($supplied ne $want) {
       Log3 $FW_wname, 3, "FHEMWEB $FW_wname CSRF error: $supplied ne $want ".
-                         "for client $FW_chash->{NAME}. ".
+                         "for client $FW_chash->{NAME} / command $cmd. ".
                          "For details see the csrfToken FHEMWEB attribute.";
       $FW_httpRetCode = "400 Bad Request";
       return 0;
@@ -978,6 +970,26 @@ FW_answerCall($)
   map { FW_pO sprintf($cssTemplate, $_); }
                         split(" ", AttrVal($FW_wname, "CssFiles", ""));
 
+  my $sd = AttrVal($FW_wname, "styleData", ""); # Avoid flicker in f18
+  if($sd && $sd =~ m/"$FW_sp":/s) {
+    my $bg;
+    $bg = $1 if($FW_room && $sd =~ m/"Room\.$FW_room\.cols.bg": "([^"]*)"/s);
+    $bg = $1 if(!defined($bg) && $sd =~ m/"cols.bg": "([^"]*)"/s);
+
+    my $bgImg;
+    $bgImg = $1 if($FW_room && $sd =~ m/"Room\.$FW_room\.bgImg": "([^"]*)"/s);
+    $bgImg = $1 if(!defined($bgImg) && $sd =~ m/"bgImg": "([^"]*)"/s);
+
+    FW_pO "<style id='style_css'>";
+    FW_pO "body { background-color:#$bg; }" if($bg);
+    FW_pO "body { background-image:url($FW_ME/images/background/$bgImg); }"
+        if($bgImg);
+    FW_pO "</style>";
+  }
+
+  my $css = AttrVal($FW_wname, "Css", "");
+  FW_pO "<style id='fhemweb_css'>$css</style>\n" if($css);
+
   ########################
   # JavaScripts
   my $jsTemplate =
@@ -1012,7 +1024,8 @@ FW_answerCall($)
 
   my $csrf= ($FW_CSRF ? "fwcsrf='$defs{$FW_wname}{CSRFTOKEN}'" : "");
   my $gen = 'generated="'.(time()-1).'"';
-  my $lp = 'longpoll="'.AttrVal($FW_wname,"longpoll",1).'"';
+  my $lp = 'longpoll="'.AttrVal($FW_wname,"longpoll",
+                 $FW_use_sha && $FW_userAgent=~m/Chrome/ ? "websocket": 1).'"';
   $FW_id = $FW_chash->{NR} if( !$FW_id );
 
   my $dataAttr = FW_dataAttr();
@@ -1069,8 +1082,7 @@ FW_answerCall($)
     } else {
       my $motd = AttrVal("global","motd","none");
       if($motd ne "none") {
-        $motd =~ s/\n/<br>/g;
-        FW_addContent(">$motd</div");
+        FW_addContent("><pre class='motd'>$motd</pre></div");
       }
     }
   }
@@ -1081,10 +1093,21 @@ FW_answerCall($)
 sub
 FW_dataAttr()
 {
+  sub
+  addParam($$)
+  {
+    my ($p, $default) = @_;
+    my $val = AttrVal($FW_wname,$p, $default);
+    $val =~ s/&/&amp;/g;
+    $val =~ s/'/&quot;/g;
+    return "data-$p='$val' ";
+  }
+
   return
-    "data-confirmDelete='" .AttrVal($FW_wname,"confirmDelete", 1)."' ".
-    "data-confirmJSError='".AttrVal($FW_wname,"confirmJSError",1)."' ".
-    "data-addHtmlTitle='"  .AttrVal($FW_wname,"addHtmlTitle",  1)."' ".
+    addParam("confirmDelete", 1).
+    addParam("confirmJSError", 1).
+    addParam("addHtmlTitle", 1).
+    addParam("styleData", "").
     "data-availableJs='$FW_fhemwebjs' ".
     "data-webName='$FW_wname '";
 }
@@ -1099,18 +1122,11 @@ FW_addContent(;$)
 sub
 FW_addLinks($)
 {
-  return undef if(!defined($_[0]));
-  my @lines = split( /\n/, $_[0]); # Adding links
-  my $ret = "";
-  foreach my $line (@lines) {
-    $ret .= "\n" if( $ret );
-    foreach my $word ( split( / /, $line ) ) {
-      $word = "<a href=\"$FW_ME$FW_subdir?detail=$word\">$word</a>"
-            if( $defs{$word} );
-      $ret .= "$word ";
-    }
-  }
-  return $ret;
+  my ($txt) = @_;
+  return undef if(!defined($txt));
+  $txt =~ s,\b([a-z0-9._]+)\b,
+            $defs{$1} ? "<a href='$FW_ME$FW_subdir?detail=$1'>$1</a>" : $1,gei;
+  return $txt;
 }
 
 
@@ -1221,11 +1237,12 @@ FW_makeTable($$$@)
   my $class = lc($title);
   $class =~ s/[^A-Za-z]/_/g;
   FW_pO "<div class='makeTable wide ".lc($title)."'>";
-  FW_pO $title;
+  FW_pO "<span class='mkTitle'>$title</span>";
   FW_pO "<table class=\"block wide $class\">";
   my $si = AttrVal("global", "showInternalValues", 0);
 
   my $row = 1;
+  my $prefix = ($title eq "Attributes" ? "a-" : "");
   foreach my $n (sort keys %{$hash}) {
     next if(!$si && $n =~ m/^\./);      # Skip "hidden" Values
     my $val = $hash->{$n};
@@ -1259,12 +1276,12 @@ FW_makeTable($$$@)
           FW_pO "<td><div class=\"dval\">$v$t</div></td>";
         } else {
           $t = "" if(!$t);
-          FW_pO "<td><div class=\"dval\" informId=\"$name-$n\">$v</div></td>";
-          FW_pO "<td><div informId=\"$name-$n-ts\">$t</div></td>";
+          FW_pO "<td><div class=\"dval\" informId=\"$name-$prefix$n\">$v</div></td>";
+          FW_pO "<td><div informId=\"$name-$prefix$n-ts\">$t</div></td>";
         }
       } else {
         $val = FW_htmlEscape($val);
-        my $tattr = "informId=\"$name-$n\" class=\"dval\"";
+        my $tattr = "informId=\"$name-$prefix$n\" class=\"dval\"";
 
         # if possible provide some links
         if ($n eq "room"){
@@ -1384,7 +1401,7 @@ FW_doDetail($)
         }
 
         FW_pO "<div $style id=\"ddtable\" class='makeTable wide'>";
-        FW_pO "DeviceOverview";
+        FW_pO "<span class='mkTitle'>DeviceOverview</span>";
         FW_pO "<table class=\"block wide\">";
         FW_makeDeviceLine($d,1,\%extPage,$nameDisplay,\%usuallyAtEnd);
         FW_pO "</table></div>";
@@ -1399,8 +1416,10 @@ FW_doDetail($)
   }
 
 
-  FW_pO FW_detailSelect($d, "set", FW_widgetOverride($d, getAllSets($d)));
-  FW_pO FW_detailSelect($d, "get", FW_widgetOverride($d, getAllGets($d)));
+  FW_pO FW_detailSelect($d, "set",
+                        FW_widgetOverride($d, getAllSets($d, $FW_chash)));
+  FW_pO FW_detailSelect($d, "get",
+                        FW_widgetOverride($d, getAllGets($d, $FW_chash)));
 
   FW_makeTable("Internals", $d, $h);
   FW_makeTable("Readings", $d, $h->{READINGS});
@@ -1445,7 +1464,7 @@ FW_makeTableFromArray($$@) {
   if (@obj>0) {
     my $row=1;
     FW_pO "<div class='makeTable wide'>";
-    FW_pO "$txt";
+    FW_pO "<span class='mkTitle'>$txt</span>";
     FW_pO "<table class=\"block wide $class\">";
     foreach (sort @obj) {
       FW_pF "<tr class=\"%s\"><td>", (($row++)&1)?"odd":"even";
@@ -1544,10 +1563,7 @@ FW_roomOverview($)
     next if($r eq "hidden" || $FW_hiddenroom{$r});
     $FW_room = AttrVal($FW_wname, "defaultRoom", $r)
         if(!$FW_room && $FW_ss);
-    my $lr = $r;
-    $lr =~ s/</&lt;/g;
-    $lr =~ s/>/&gt;/g;
-    push @list1, $lr;
+    push @list1, FW_htmlEscape($r);
     push @list2, "$FW_ME?room=".urlEncode($r);
   }
   my $sfx = AttrVal("global", "language", "EN");
@@ -1589,8 +1605,7 @@ FW_roomOverview($)
     foreach(my $idx = 0; $idx < @list1; $idx++) {
       next if(!$list1[$idx]);
       my $sel = ($list1[$idx] eq $FW_room ? " selected=\"selected\""  : "");
-      my $csrf = ($list2[$idx] =~ m/cmd=/ ? $FW_CSRF : '');
-      FW_pO "<option value='$list2[$idx]$csrf'$sel>$list1[$idx]</option>";
+      FW_pO "<option value='$list2[$idx]'$sel>$list1[$idx]</option>";
     }
     FW_pO "</select></td>";
     FW_pO "</tr>";
@@ -1598,6 +1613,7 @@ FW_roomOverview($)
   } else {
 
     my $tblnr = 1;
+    my $roomEscaped = FW_htmlEscape($FW_room);
     foreach(my $idx = 0; $idx < @list1; $idx++) {
       my ($l1, $l2) = ($list1[$idx], $list2[$idx]);
       if(!$l1) {
@@ -1608,7 +1624,7 @@ FW_roomOverview($)
         }
 
       } else {
-        FW_pF "<tr%s>", $l1 eq $FW_room ? " class=\"sel\"" : "";
+        FW_pF "<tr%s>", $l1 eq $roomEscaped ? " class=\"sel\"" : "";
 
         my $class = "menu_$l1";
         $class =~ s/[^A-Z0-9]/_/gi;
@@ -1630,9 +1646,10 @@ FW_roomOverview($)
         $target = 'target="_blank"' if($l2 =~ s/^$FW_ME\/\+/$FW_ME\//);
         $target = 'target="_blank"' if($l2 =~ m/commandref|fhem.de.fhem.html/);
         if($l2 =~ m/.html$/ || $l2 =~ m/^(http|javascript)/ || length($target)){
-           FW_pO "<td><div><a href=\"$l2\" $target >$icon$l1</a></div></td>";
+           FW_pO "<td><div><a href='$l2' $target>$icon<span>$l1</span></a>".
+                 "</div></td>";
         } else {
-          FW_pH $l2, "$icon$l1", 1, $class;
+          FW_pH $l2, "$icon<span>$l1</span>", 1, $class;
         }
 
         FW_pO "</tr>";
@@ -1846,7 +1863,7 @@ FW_showRoom()
 
       #################
       # Check if there is a device of this type in the room
-      FW_pO "\n<tr><td><div class=\"devType\">$g</div></td></tr>";
+      FW_pO "<tr class='devTypeTr'><td><div class='devType'>$g</div></td></tr>";
       FW_pO "<tr><td>";
       FW_pO "<table class=\"block wide\" id=\"TYPE_$g\">";
 
@@ -2115,7 +2132,7 @@ FW_displayFileList($@)
   my $hid = lc($heading);
   $hid =~ s/[^A-Za-z]/_/g;
   FW_pO "<div class=\"fileList $hid\">$heading</div>";
-  FW_pO "<table class=\"block fileList\">";
+  FW_pO "<table class=\"block wide fileList\">";
   my $cfgDB = "";
   my $row = 0;
   foreach my $f (@files) {
@@ -2179,7 +2196,7 @@ FW_style($$)
       "Own modules and helper files:\$MW_dir:^(.*sh|[0-9][0-9].*Util.*pm|".
                         ".*cfg|.*\.holiday|myUtilsTemplate.pm|.*layout)\$\n".
       "Gplot files:\$FW_gplotdir:^.*gplot\$\n".
-      "Styles:\$FW_cssdir:^.*(css|svg)\$");
+      "Style files:\$FW_cssdir:^.*(css|svg)\$");
     foreach my $l (split(/[\r\n]/, $efl)) {
       my ($t, $v, $re) = split(":", $l, 3);
       $v = eval $v;
@@ -2188,6 +2205,8 @@ FW_style($$)
         @fList = defInfo('TYPE=SVG','GPLOTFILE');
         @fList = map { "$_.gplot" } @fList;
         @fList = map { "$_.configDB" } @fList if configDBUsed();
+        my %fListUnique = map { $_, 1 } @fList;
+        @fList = sort keys %fListUnique;
       } else {
         @fList = FW_fileList("$v/$re");
       }
@@ -2199,7 +2218,8 @@ FW_style($$)
     my @fl = grep { $_ !~ m/(floorplan|dashboard)/ }
                         FW_fileList("$FW_cssdir/.*style.css");
     FW_addContent($start);
-    FW_pO "<table class=\"block fileList\">";
+    FW_pO "<div class='fileList styles'>Styles</div>";
+    FW_pO "<table class='block wide fileList'>";
     my $row = 0;
     foreach my $file (@fl) {
       next if($file =~ m/svg_/);
@@ -2291,8 +2311,7 @@ FW_style($$)
     $ret = FW_fC("reload $fileName") if($fileName =~ m,\.pm$,);
     $ret = FW_Set("","","rereadicons") if($isImg);
     DoTrigger("global", "FILEWRITE $filePath", 1) if(!$ret); # Forum #32592
-    $ret = ($ret ? "<h3>ERROR:</h3><b>$ret</b>" :
-                "Saved the file $fileName to $forceType");
+    $ret = ($ret ? "<h3>ERROR:</h3><b>$ret</b>" : "Saved $fileName");
     FW_style("style list", $ret);
     $ret = "";
 
@@ -2380,7 +2399,8 @@ FW_pH(@)
   my ($link, $txt, $td, $class, $doRet,$nonl) = @_;
   my $ret;
 
-  $link .= $FW_CSRF if($link =~ m/cmd/);
+  $link .= $FW_CSRF if($link =~ m/cmd/ &&
+                       $link !~m/cmd=style%20(list|select|eventMonitor)/);
   $link = ($link =~ m,^/,) ? $link : "$FW_ME$FW_subdir?$link";
   
   # Using onclick, as href starts safari in a webapp.
@@ -2458,11 +2478,9 @@ FW_makeImage(@)
         $col = "#$col" if($col =~ m/^([A-F0-9]{6})$/);
         $data =~ s/fill="#000000"/fill="$col"/g;
         $data =~ s/fill:#000000/fill:$col/g;
-        $data =~ s/FHEM_COLOR/$col/g;
       } else {
         $data =~ s/fill="#000000"//g;
         $data =~ s/fill:#000000//g;
-        $data =~ s/FHEM_COLOR//g;
       }
       return $data;
     } else {
@@ -2585,13 +2603,8 @@ FW_Attr(@)
   }
 
   if($attrName eq "longpoll" && $type eq "set" && $param[0] eq "websocket") {
-    eval { require Digest::SHA; };
-    if($@) {
-      Log3 $FW_wname, 1, $@;
-      return "$devName: Can't load Digest::SHA, no websocket";
-      return -1;
-    }
-    $FW_use_sha = 1;
+    return "$devName: Could not load Digest::SHA on startup, no websocket"
+        if(!$FW_use_sha);
   }
 
   return $retMsg;
@@ -2846,7 +2859,7 @@ FW_Notify($$)
       $dn = $1 if($dev->{CHANGED}->[0] =~ m/^MODIFIED (.*)$/);
       if($dev->{CHANGED}->[0] =~ m/^ATTR ([^ ]+) ([^ ]+) (.*)$/s) {
         $dn = $1;
-        my @a = ("$2: $3");
+        my @a = ("a-$2: $3");
         $events = \@a;
       }
     }
@@ -2987,7 +3000,7 @@ FW_devState($$@)
   my ($hasOnOff, $link);
 
   my $cmdList = AttrVal($d, "webCmd", "");
-  my $allSets = FW_widgetOverride($d, getAllSets($d));
+  my $allSets = FW_widgetOverride($d, getAllSets($d, $FW_chash));
   my $state = $defs{$d}{STATE};
   $state = "" if(!defined($state));
 
@@ -3159,7 +3172,7 @@ FW_closeInactiveClients()
             !$defs{$dev}{LASTACCESS} || $defs{$dev}{inform} ||
             ($now - $defs{$dev}{LASTACCESS}) < 60);
     Log3 $FW_wname, 4, "Closing inactive connection $dev";
-    FW_Undef($defs{$dev}, "");
+    FW_Undef($defs{$dev}, undef);
     delete $defs{$dev};
     delete $attr{$dev};
   }
@@ -3173,6 +3186,7 @@ FW_htmlEscape($)
   $txt =~ s/&/&amp;/g;
   $txt =~ s/</&lt;/g;
   $txt =~ s/>/&gt;/g;
+  $txt =~ s/'/&apos;/g;
 #  $txt =~ s/\n/<br>/g;
   return $txt;
 }
@@ -3277,11 +3291,11 @@ FW_widgetOverride($$)
   <a name="FHEMWEBdefine"></a>
   <b>Define</b>
   <ul>
-    <code>define &lt;name&gt; FHEMWEB &lt;tcp-portnr&gt; [global]</code>
+    <code>define &lt;name&gt; FHEMWEB &lt;tcp-portnr&gt; [global|IP]</code>
     <br><br>
     Enable the webfrontend on port &lt;tcp-portnr&gt;. If global is specified,
     then requests from all interfaces (not only localhost / 127.0.0.1) are
-    serviced.<br>
+    serviced. If IP is specified, then FHEMWEB will only listen on this IP.<br>
     To enable listening on IPV6 see the comments <a href="#telnet">here</a>.
     <br>
   </ul>
@@ -3350,6 +3364,15 @@ FW_widgetOverride($$)
         instance from now on.
     </li><br>
 
+    <li>allowedHttpMethods<br>
+      FHEMWEB implements the GET, POST and OPTIONS HTTP methods. Some external
+      devices require the HEAD method, which is not implemented correctly in
+      FHEMWEB, as FHEMWEB always returns a body, which, according to the spec,
+      is wrong. As in some cases this not a problem, enabling GET may work.
+      To do this, set this attribute to GET|POST|HEAD, default ist GET|POST.
+      OPTIONS is always enabled.
+      </li><br>
+
     <a name="closeConn"></a>
     <li>closeConn<br>
       If set, a TCP Connection will only serve one HTTP request. Seems to
@@ -3420,6 +3443,11 @@ FW_widgetOverride($$)
        </code></ul>
        </li><br>
 
+    <a name="Css"></a>
+    <li>Css<br>
+       CSS included in the header after the CssFiles section.
+       </li><br>
+
     <a name="cmdIcon"></a>
     <li>cmdIcon<br>
         Space separated list of cmd:iconName pairs. If set, the webCmd text is
@@ -3600,7 +3628,7 @@ FW_widgetOverride($$)
         Set the icon for a device in the room overview. There is an
         icon-chooser in FHEMWEB to ease this task.  Setting icons for the room
         itself is indirect: there must exist an icon with the name
-        ico<ROOMNAME>.png in the iconPath.
+        ico&lt;ROOMNAME&gt;.png in the iconPath.
         </li>
         <br>
 
@@ -3627,12 +3655,12 @@ FW_widgetOverride($$)
        </li><br>
 
     <a name="longpoll"></a>
-    <li>longpoll<br>
-        Affects devices states in the room overview only.<br>
-        In this mode status update is refreshed more or less instantaneously,
-        and state change (on/off only) is done without requesting a complete
-        refresh from the server.
-        Default is on.
+    <li>longpoll [0|1|websocket]<br>
+        If activated, the browser is notifed when device states, readings or
+        attributes are changed, a reload of the page is not necessary.
+        Default is 1 (on), use 0 to deactivate it.<br>
+        If websocket is specified, then this API is used to notify the browser,
+        else HTTP longpoll. Note: some older browser do not implement websocket.
         </li>
         <br>
 
@@ -3824,6 +3852,11 @@ FW_widgetOverride($$)
        See the global attribute sslVersion.
        </li><br>
 
+    <a name="styleData"></a>
+    <li>styleData<br>
+      data-storage used by dynamic styles like f18
+      </li><br>
+
     <a name="stylesheetPrefix"></a>
     <li>stylesheetPrefix<br>
       prefix for the files style.css, svg_style.css and svg_defs.svg. If the
@@ -3954,11 +3987,12 @@ FW_widgetOverride($$)
   <a name="FHEMWEBdefine"></a>
   <b>Define</b>
   <ul>
-    <code>define &lt;name&gt; FHEMWEB &lt;tcp-portnr&gt; [global]</code>
+    <code>define &lt;name&gt; FHEMWEB &lt;tcp-portnr&gt; [global|IP]</code>
     <br><br>
     Aktiviert das Webfrontend auf dem Port &lt;tcp-portnr&gt;. Mit dem
     Parameter global werden Anfragen von allen Netzwerkschnittstellen
-    akzeptiert (nicht nur vom localhost / 127.0.0.1) .  <br>
+    akzeptiert (nicht nur vom localhost / 127.0.0.1). Falls IP angegeben wurde,
+    dann werden nur Anfragen an diese IP Adresse akzeptiert.  <br>
 
     Informationen f&uuml;r den Betrieb mit IPv6 finden Sie <a
     href="#telnet">hier</a>.<br>
@@ -4029,6 +4063,17 @@ FW_widgetOverride($$)
         f&uuml;r eine FHEMWEB Instanz unerw&uuml;nscht.
     </li><br>
 
+    <li>allowedHttpMethods</br>
+      FHEMWEB implementiert die HTTP Methoden GET, POST und OPTIONS. Manche
+      externe Ger&auml;te ben&ouml;tigen HEAD, das ist aber in FHEMWEB nicht
+      korrekt implementiert, da FHEMWEB immer ein body zur&uuml;ckliefert, was
+      laut Spec falsch ist. Da ein body in manchen F&auml;llen kein Problem
+      ist, kann man HEAD durch setzen dieses Attributes auf GET|POST|HEAD
+      aktivieren, die Voreinstellung ist GET|POST. OPTIONS ist immer
+      aktiviert.
+      </li><br>
+
+
      <a name="closeConn"></a>
      <li>closeConn<br>
         Falls gesetzt, wird pro TCP Verbindung nur ein HTTP Request
@@ -4115,6 +4160,11 @@ FW_widgetOverride($$)
         </code></ul>
         </li><br>
 
+    <a name="Css"></a>
+    <li>Css<br>
+       CSS, was nach dem CssFiles Abschnitt im Header eingefuegt wird.
+       </li><br>
+
     <a name="defaultRoom"></a>
     <li>defaultRoom<br>
         Zeigt den angegebenen Raum an falls kein Raum explizit ausgew&auml;hlt
@@ -4313,11 +4363,14 @@ FW_widgetOverride($$)
        </li><br>
 
     <a name="longpoll"></a>
-    <li>longpoll<br>
-        Dies betrifft die Aktualisierung der Ger&auml;testati in der
-        Weboberfl&auml;che. Ist longpoll aktiviert, werden
-        Status&auml;nderungen sofort im Browser dargestellt. ohne die Seite
-        manuell neu laden zu m&uuml;ssen. Standard ist aktiviert.
+    <li>longpoll [0|1|websocket]<br>
+        Falls gesetzt, FHEMWEB benachrichtigt den Browser, wenn
+        Ger&auml;testatuus, Readings or Attribute sich &auml;ndern, ein
+        Neuladen der Seite ist nicht notwendig. Zum deaktivieren 0 verwenden.
+        <br>
+        Falls websocket spezifiziert ist, l&auml;uft die Benachrichtigung des
+        Browsers &uuml;ber dieses Verfahren sonst &uuml;ber HTTP longpoll.
+        Achtung: &auml;ltere Browser haben keine websocket Implementierung.
         </li><br>
 
 
@@ -4495,15 +4548,20 @@ FW_widgetOverride($$)
           attr WEB sortRooms DG OG EG Keller
         </li><br>
 
-     <a name="smallscreenCommands"></a>
-     <li>smallscreenCommands<br>
-        Falls auf 1 gesetzt werden Kommandos, Slider und Dropdown Men&uuml;s im
-        Smallscreen Landscape Modus angezeigt.
-        </li><br>
+    <a name="smallscreenCommands"></a>
+    <li>smallscreenCommands<br>
+      Falls auf 1 gesetzt werden Kommandos, Slider und Dropdown Men&uuml;s im
+      Smallscreen Landscape Modus angezeigt.
+      </li><br>
 
-     <li>sslVersion<br>
-        Siehe das global Attribut sslVersion.
-        </li><br>
+    <li>sslVersion<br>
+      Siehe das global Attribut sslVersion.
+      </li><br>
+
+    <a name="styleData"></a>
+    <li>styleData<br>
+      wird von dynamischen styles wie f18 werwendet
+      </li><br>
 
     <a name="stylesheetPrefix"></a>
     <li>stylesheetPrefix<br>

+ 3 - 1
fhem/core/FHEM/09_BS.pm

@@ -5,7 +5,7 @@
 # e-mail: omega at online dot de
 #
 ##############################################
-# $Id: 09_BS.pm 3830 2013-08-31 17:09:10Z borisneubert $
+# $Id: 09_BS.pm 16375 2018-03-10 15:40:02Z neubert $
 package main;
 
 use strict;
@@ -127,6 +127,8 @@ BS_Parse($$)
 1;
 
 =pod
+=item summary    BS brightness sensor communicating over FHZ
+=item summary_DE BS Helligkeitssenor angebunden &uuml;ber FHZ
 =begin html
 
 <a name="BS"></a>

+ 3 - 1
fhem/core/FHEM/09_USF1000.pm

@@ -5,7 +5,7 @@
 # e-mail: omega at online dot de
 #
 ##############################################
-# $Id: 09_USF1000.pm 3830 2013-08-31 17:09:10Z borisneubert $
+# $Id: 09_USF1000.pm 16375 2018-03-10 15:40:02Z neubert $
 package main;
 
 use strict;
@@ -175,6 +175,8 @@ USF1000_Parse($$)
 1;
 
 =pod
+=item summary    USF1000S ultrasonic level transmitter communicating over FHZ
+=item summary_DE USF1000S Ultraschall-F&uuml;llstandsmesser angebunden &uuml;ber FHZ
 =begin html
 
 <a name="USF1000"></a>

File diff suppressed because it is too large
+ 793 - 535
fhem/core/FHEM/10_CUL_HM.pm


+ 4 - 1
fhem/core/FHEM/10_EIB.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 10_EIB.pm 11307 2016-04-25 08:02:06Z rudolfkoenig $
+# $Id: 10_EIB.pm 16389 2018-03-11 20:50:59Z andi291 $
 # MH various changes/fixes from forum e.g dpt9, 0 is not necessarily off, ...
 # MH 20140313 changed Log to Log3, verbose instead of loglevel
 # MH 20140313 testing setstate....
@@ -24,6 +24,7 @@
 # ABU 20160116 fixed motd-error due to debug-mode
 # ABU 20160122 fixed doku, changed return value for EIB_Set from undef to "", reintegrated multiple group sending
 # ABU 20160123 fixed issue for sending with additional groups
+# ABU 20180311 added summary in description
 
 package main;
 
@@ -1247,6 +1248,8 @@ eib_name2hex($)
 1;
 
 =pod
+=item summary    Communicates to EIB via TUL (deprecated, use KNX)
+=item summary_DE Kommuniziert mit EIB über TUL (veraltet, nutze KNX)
 =begin html
 
 <a name="EIB"></a>

+ 38 - 12
fhem/core/FHEM/10_EQ3BT.pm

@@ -1,15 +1,21 @@
 #############################################################
 #
-# EQ3BT.pm (c) by Dominik Karall, 2016-2017
+# EQ3BT.pm (c) by Dominik Karall, 2016-2018
 # dominik karall at gmail dot com
-# $Id: 10_EQ3BT.pm 15447 2017-11-18 19:48:43Z dominik $
+# $Id: 10_EQ3BT.pm 16254 2018-02-24 14:59:23Z dominik $
 #
 # FHEM module to communicate with EQ-3 Bluetooth thermostats
 #
-# Version: 2.0.2
-#
 #############################################################
 #
+# v2.0.4 - 20180224
+# - FEATURE: support childlock
+#
+# v2.0.3 - 20171218
+# - FEATURE: support maxRetries and timeout attribute
+#            maxRetries...number of tries before error is counted
+#            timeout...timeout for the command
+#
 # v2.0.2 - 20171118
 # - FEATURE: support remote bluetooth interfaces via SSH (thx@Cooltux!)
 #
@@ -150,7 +156,7 @@ sub EQ3BT_Initialize($) {
     $hash->{GetFn}    = 'EQ3BT_Get';
     $hash->{SetFn}    = 'EQ3BT_Set';
     $hash->{AttrFn}   = 'EQ3BT_Attribute';
-    $hash->{AttrList}  = 'sshHost '.
+    $hash->{AttrList}  = 'sshHost maxRetries timeout '.
                             $readingFnAttributes;
     
     return undef;
@@ -165,7 +171,7 @@ sub EQ3BT_Define($$) {
     my $sshHost;
     
     $hash->{STATE} = "initialized";
-    $hash->{VERSION} = "2.0.2";
+    $hash->{VERSION} = "2.0.4";
     Log3 $hash, 3, "EQ3BT: EQ-3 Bluetooth Thermostat ".$hash->{VERSION};
     
     if (int(@a) > 4) {
@@ -249,7 +255,7 @@ sub EQ3BT_Set($@) {
     my ($hash, $name, @params) = @_;
     my $workType = shift(@params);
     my $list = "desiredTemperature:slider,4.5,0.5,29.5,1 updateStatus:noArg boost:on,off mode:manual,automatic eco:noArg comfort:noArg ".
-               "resetErrorCounters:noArg resetConsumption:noArg";
+               "resetErrorCounters:noArg resetConsumption:noArg childlock:on,off";
 
     # check parameters for set function
     if($workType eq "?") {
@@ -528,7 +534,7 @@ sub EQ3BT_execGatttool($) {
         }
         
         if(defined($listen) && $listen eq "listen") {
-            $cmd = "timeout 15 ".$cmd." --listen";
+            $cmd = "timeout ".AttrVal($name, "timeout", 15)." ".$cmd." --listen";
         }
         
         #redirect stderr to stdout
@@ -606,7 +612,7 @@ sub EQ3BT_processGatttoolResult($) {
         $hash->{helper}{"retryCounter$workType"} = 0 if(!defined($hash->{helper}{"retryCounter$workType"}));
         $hash->{helper}{"retryCounter$workType"}++;
         Log3 $hash, 4, "EQ3BT ($name): $workType failed ($handle, $value, $notification)";
-        if ($hash->{helper}{"retryCounter$workType"} > 20) {
+        if ($hash->{helper}{"retryCounter$workType"} > AttrVal($name, "maxRetries", 20)) {
             my $errorCount = ReadingsVal($hash->{NAME}, "errorCount-$workType", 0);
             readingsSingleUpdate($hash, "errorCount-$workType", $errorCount+1, 1);
             Log3 $hash, 3, "EQ3BT ($name): $workType, $handle, $value failed 20 times.";
@@ -668,8 +674,8 @@ sub EQ3BT_processNotification {
         my $isBoost = (hex($vals[2]) & 4) >> 2;
         my $dst  = (hex($vals[2]) & 8) >> 3;
         my $wndOpen = (hex($vals[2]) & 16) >> 4;
-        my $unknown = (hex($vals[2]) & 32) >> 5;
-        $unknown = (hex($vals[2]) & 64) >> 6;
+        my $locked = (hex($vals[2]) & 32) >> 5;
+        my $unknown = (hex($vals[2]) & 64) >> 6;
         my $isLowBattery = (hex($vals[2]) & 128) >> 7;
         my $batteryStr = "ok";
         if($isLowBattery > 0) {
@@ -704,6 +710,7 @@ sub EQ3BT_processNotification {
         readingsSingleUpdate($hash, "valvePosition", $pct, 1);
         #changes below this line will set lastchangeby
         EQ3BT_readingsSingleUpdateIfChanged($hash, "windowOpen", $wndOpen, 1);
+        EQ3BT_readingsSingleUpdateIfChanged($hash, "childlock", $locked, 1);
         EQ3BT_readingsSingleUpdateIfChanged($hash, "ecoMode", $eco, 1);
         EQ3BT_readingsSingleUpdateIfChanged($hash, "battery", $batteryStr, 1);
         EQ3BT_readingsSingleUpdateIfChanged($hash, "boost", $isBoost, 1);
@@ -739,7 +746,26 @@ sub EQ3BT_setNightmode($) {
 }
 
 sub EQ3BT_setChildlock($$) {
-    my ($hash, $desiredState) = @_;
+    my ($hash, $onoff) = @_;
+    my $name = $hash->{NAME};
+    my $data = "01";
+    $data = "00" if($onoff eq "off");
+    
+    $hash->{helper}{RUNNING_PID} = BlockingCall("EQ3BT_execGatttool", $name."|".$hash->{MAC}."|setChildlock|0x0411|80".$data, "EQ3BT_processGatttoolResult", 60, "EQ3BT_killGatttool", $hash);
+    return undef;
+}
+
+sub EQ3BT_setChildlockSuccessful {
+    my ($hash, $handle, $value) = @_;
+    my $val = (hex($value) - 0x8000);
+    readingsSingleUpdate($hash, "childlock", $val, 1);
+    return undef;
+}
+
+sub EQ3BT_setChildlockRetry {
+    my ($hash) = @_;
+    EQ3BT_retryGatttool($hash, "setChildlock");
+    return undef;
 }
 
 sub EQ3BT_setHolidaymode($$) {

+ 147 - 41
fhem/core/FHEM/10_EnOcean.pm

@@ -1,10 +1,14 @@
 ##############################################
-# $Id: 10_EnOcean.pm 13812 2017-03-27 04:39:32Z klaus.schauer $
+# $Id: 10_EnOcean.pm 16029 2018-01-28 18:43:46Z klaus.schauer $
+
 package main;
 
 use strict;
 use warnings;
 my $cryptFunc;
+my $xmlFunc;
+my $xml;
+
 eval "use Crypt::Rijndael";
 if ($@) {
   $cryptFunc = 0;
@@ -18,6 +22,21 @@ if ($@) {
   $cryptFunc = $cryptFunc == 1 ? 1 : 0;
 }
 
+eval "use XML::Simple";
+if ($@) {
+  $xmlFunc = 0;
+} else {
+  $xmlFunc = 1;
+  $xml = new XML::Simple;
+}
+
+eval "use Data::Dumper";
+if ($@) {
+  $xmlFunc = 0;
+} else {
+  $xmlFunc = $xmlFunc == 1 ? 1 : 0;
+}
+
 use SetExtensions;
 
 sub EnOcean_Define($$);
@@ -366,11 +385,11 @@ my %EnO_eepConfig = (
   "G5.07.01" => {attr => {subType => "occupSensor.01", eep => "A5-07-01", manufID => "00D", model => 'tracker'}, GPLOT => "EnO_motion:Motion,EnO_voltage4current4:Voltage/Current,"},
   "G5.10.12" => {attr => {subType => "roomSensorControl.01", eep => "A5-10-12", manufID => "00D", scaleMax => 40, scaleMin => 0, scaleDecimals => 1}, GPLOT => "EnO_temp4humi6:Temp/Humi,"},
   "G5.38.08" => {attr => {subType => "gateway", eep => "A5-38-08", gwCmd => "dimming", manufID => "00D", webCmd => "on:off:dim"}, GPLOT => "EnO_dim4:Dim,"},
-  "H5.38.08" => {attr => {subType => "gateway", comMode => "confirm", eep => "A5-38-08", gwCmd => "dimming", manufID => "00D", model => "TF", teachMethod => "confirm", webCmd => "on:off:dim"}, GPLOT => "EnO_dim4:Dim,"},
+  "H5.38.08" => {attr => {subType => "gateway", comMode => "confirm", eep => "A5-38-08", gwCmd => "dimming", manufID => "00D", model => "Eltako_TF", teachMethod => "confirm", webCmd => "on:off:dim"}, GPLOT => "EnO_dim4:Dim,"},
   "G5.3F.7F" => {attr => {subType => "manufProfile", eep => "A5-3F-7F", manufID => "00D", webCmd => "opens:stop:closes"}},
-  "H5.3F.7F" => {attr => {subType => "manufProfile", comMode => "confirm", eep => "A5-3F-7F", manufID => "00D", model => "TF", sensorMode => 'pushbutton', settingAccuracy => "high", teachMethod => "confirm", webCmd => "opens:stop:closes"}},
+  "H5.3F.7F" => {attr => {subType => "manufProfile", comMode => "confirm", eep => "A5-3F-7F", manufID => "00D", model => "Eltako_TF", sensorMode => 'pushbutton', settingAccuracy => "high", teachMethod => "confirm", webCmd => "opens:stop:closes"}},
   "M5.38.08" => {attr => {subType => "gateway", eep => "A5-38-08", gwCmd => "switching", manufID => "00D", webCmd => "on:off"}},
-  "N5.38.08" => {attr => {subType => "gateway", comMode => "confirm", eep => "A5-38-08", gwCmd => "switching", manufID => "00D", model => "TF", teachMethod => "confirm", webCmd => "on:off"}},
+  "N5.38.08" => {attr => {subType => "gateway", comMode => "confirm", eep => "A5-38-08", gwCmd => "switching", manufID => "00D", model => "Eltako_TF", teachMethod => "confirm", webCmd => "on:off"}},
   "G5.ZZ.ZZ" => {attr => {subType => "PM101", manufID => "005"}, GPLOT => "EnO_motion:Motion,EnO_brightness4:Brightness,"},
   "L6.02.01" => {attr => {subType => "smokeDetector.02", eep => "F6-05-02", manufID => "00D"}},
   "ZZ.ZZ.ZZ" => {attr => {subType => "raw"}},
@@ -398,16 +417,23 @@ my %EnO_extendedRemoteFunctionCode = (
   0x252 => "remoteRepeaterFilter" # set
 );
 
-my @EnO_models = qw (
-  other
-  FAE14 FHK14 FHK61
-  FSA12 FSB14 FSB61 FSB70
-  FSM12 FSM61
-  FT55
-  FTS12
-  TF
-  OEM
-  tracker
+my %EnO_models = (
+  "Eltako_FAE14" => {attr => {manufID => "00D"}},
+  "Eltako_FHK14" => {attr => {manufID => "00D"}},
+  "Eltako_FHK61" => {attr => {manufID => "00D"}},
+  "Eltako_FSA12" => {attr => {manufID => "00D"}},
+  "Eltako_FSB14" => {attr => {manufID => "00D"}},
+  "Eltako_FSB61" => {attr => {manufID => "00D"}},
+  "Eltako_FSB70" => {attr => {manufID => "00D"}},
+  "Eltako_FSM12" => {attr => {manufID => "00D"}},
+  "Eltako_FSM61" => {attr => {manufID => "00D"}},
+  "Eltako_FT55" => {attr => {manufID => "00D"}},
+  "Eltako_FTS12" => {attr => {manufID => "00D"}},
+  "Eltako_TF"=> {attr => {manufID => "00D"}},
+  "Holter_OEM" => {attr => {pidCtrl => "off"}},
+  "Micropelt_MVA004" => {attr => {remoteCode => "FFFFFFFE", remoteEEP => "A5-20-01", remoteID => "getNextID", remoteManagement => "manager"}, xml => {productID => "0x004900000000", xmlDescrLocation => "/FHEM/lib/EnO_ReCom_Device_Descr.xml"}},
+  other => {},
+  tracker => {}
 );
 
 my @EnO_defaultChannel = ("all", "input", 0..29);
@@ -670,7 +696,7 @@ EnOcean_Initialize($)
                       "eep gpDef gwCmd:" . join(",", sort @EnO_gwCmd) . " humitity humidityRefDev " .
                       "keyRcv keySnd macAlgo:no,3,4 measurementCtrl:disable,enable " .
                       "manufID:" . join(",", sort keys %EnO_manuf) . " " .
-                      "model:" . join(",", @EnO_models) . " " .
+                      "model:" . join(",", sort keys %EnO_models) . " " .
                       "observe:on,off observeCmdRepetition:1,2,3,4,5 observeErrorAction observeInterval observeLogic:and,or " .
                       #observeCmds observeExeptions
                       "observeRefDev pidActorErrorAction:errorPos,freeze pidActorCallBeforeSetting pidActorErrorPos " .
@@ -704,6 +730,11 @@ EnOcean_Initialize($)
   } else {
     Log3 undef, 2, "EnOcean Cryptographic functions are not available.";
   }
+  if ($xmlFunc == 1){
+    Log3 undef, 2, "EnOcean XML functions available.";
+  } else {
+    Log3 undef, 2, "EnOcean XML functions are not available.";
+  }
   return undef;
 }
 
@@ -722,7 +753,7 @@ sub EnOcean_Define($$) {
     while (($autocreateName, $autocreateHash) = each(%defs)) {
       last if ($defs{$autocreateName}{TYPE} eq "autocreate");
     }
-    $autocreateDeviceRoom = AttrVal($autocreateName, "device_room", $autocreateDeviceRoom);
+    $autocreateDeviceRoom = AttrVal($autocreateName, "device_room", $autocreateDeviceRoom) if (defined $autocreateName);
     $autocreateDeviceRoom = 'EnOcean' if ($autocreateDeviceRoom eq '%TYPE');
     $autocreateDeviceRoom = $name if ($autocreateDeviceRoom eq '%NAME');
     $autocreateDeviceRoom = AttrVal($name, "room", $autocreateDeviceRoom);
@@ -1004,6 +1035,7 @@ sub EnOcean_Define($$) {
     $hash->{helper}{stopped} = 0;
     #$hash->{helper}{adjust} = '';
   }
+
   # all notifys needed
   #$hash->{NOTIFYDEV} = "global";
   Log3 $name, 5, "EnOcean_define for device $name executed.";
@@ -2741,8 +2773,8 @@ sub EnOcean_Set($@)
         $updateState = 2;
 
       } else {
-        $cmdList .= "setpointTemp:slider,10,1,30 " if (AttrVal($name, "pidCtrl", 'on') eq 'on' || AttrVal($name, "model", '') eq 'OEM');
-        $cmdList .= "setpoint:slider,0,5,100 " if (AttrVal($name, "pidCtrl", 'on') eq 'off' && AttrVal($name, "model", '') ne 'OEM');
+        $cmdList .= "setpointTemp:slider,10,1,30 " if (AttrVal($name, "pidCtrl", 'on') eq 'on' || AttrVal($name, "model", '') eq 'Holter_OEM');
+        $cmdList .= "setpoint:slider,0,5,100 " if (AttrVal($name, "pidCtrl", 'on') eq 'off' && AttrVal($name, "model", '') ne 'Holter_OEM');
         $cmdList .= "runInit:noArg valveCloses:noArg valveOpens:noArg";
         return "Unknown command " . $cmd . ", choose one of " . $cmdList;
       }
@@ -3018,7 +3050,7 @@ sub EnOcean_Set($@)
         if($cmd eq "teach") {
           # teach-in EEP A5-38-08, Manufacturer "Multi user Manufacturer ID"
           #$data = sprintf "%02X000000", $gwCmdID;
-          if ($model eq 'TF') {
+          if ($model =~ m/TF$/) {
             $data = "E0400D80";
           } else {
             $data = "E047FF80";
@@ -3045,7 +3077,7 @@ sub EnOcean_Set($@)
           SetExtensionsCancel($hash);
           $data = sprintf "%02X%04X%02X", $gwCmdID, $time, $setCmd;
         } elsif ($cmd eq "off") {
-          if ($model eq "FSA12") {
+          if ($model =~ m/FSA12$/) {
             $setCmd = 0x0E;
           } else {
             $setCmd = 8;
@@ -3103,7 +3135,7 @@ sub EnOcean_Set($@)
           # teach-in EEP A5-38-08, Manufacturer "Multi user Manufacturer ID"
           #$data = "E047FF80";
           # teach-in Eltako
-          if ($model eq 'TF') {
+          if ($model =~ m/TF$/) {
             $data = "E0400D80";
           } else {
             $data = "02000000";
@@ -5883,8 +5915,8 @@ sub EnOcean_Set($@)
       my $humidityThreshold = ReadingsVal($name, "humidityThreshold", 'default');
       $humidityThreshold = $humidityThreshold eq 'default' ? 127 : $humidityThreshold;
       my $startTimerMode = 0;
-      my $tempThreshold = ReadingsVal($name, "roomTempSet", 'default');;
-      $tempThreshold = $tempThreshold eq 'default' ? 127 : $tempThreshold;
+      my $tempThreshold = ReadingsVal($name, "roomTempSet", 'default');
+      $tempThreshold = $tempThreshold eq 'default' ? 0 : $tempThreshold;
       my $ventilation = 15;
       if ($cmd eq "ventilation") {
         $ventilation = $a[1];
@@ -5973,11 +6005,12 @@ sub EnOcean_Set($@)
         $tempThreshold = $a[1];
         if (defined $tempThreshold) {
           if ($tempThreshold eq 'default') {
-            $tempThreshold = 127;
+            $tempThreshold = 0;
             readingsSingleUpdate($hash, "roomTempSet", 'default', 1);
           } elsif ($tempThreshold =~ m/^[+-]?\d+$/ && $tempThreshold >= - 63 && $tempThreshold <= 63) {
             readingsSingleUpdate($hash, "roomTempSet", $tempThreshold, 1);
-            $tempThreshold = abs($tempThreshold) | 0x40 if ($tempThreshold < 0);
+            $tempThreshold += 64;
+            #$tempThreshold = abs($tempThreshold) | 0x40 if ($tempThreshold < 0);
           } else {
             return "Usage: $cmd variable is wrong, choose default|-63 ... 63." ;
           }
@@ -7075,7 +7108,7 @@ sub EnOcean_Parse($$)
       RemoveInternalTimer($hash->{helper}{alarmTimer});
       InternalTimer(gettimeofday() + 1320, 'EnOcean_readingsSingleUpdate', $hash->{helper}{alarmTimer}, 0);
 
-    } elsif ($model eq "FAE14" || $model eq "FHK14" || $model eq "FHK61") {
+    } elsif ($model =~ m/FAE14|FHK14|FHK61$/) {
       # heating/cooling relay FAE14, FHK14, untested
       $event = "controllerMode";
       if ($db[0] == 0x30) {
@@ -7200,13 +7233,10 @@ sub EnOcean_Parse($$)
       }
       # released events are disturbing when using a remote, since it overwrites
       # the "real" state immediately. In the case of an Eltako FSB14, FSB61 ...
-      # the state should remain released. (by Thomas)
+      # the state should remain released.
       if ($msg =~ m/released$/ &&
           AttrVal($name, "sensorMode", "switch") ne "pushbutton" &&
-          $model ne "FT55" && $model ne "FSB14" &&
-          $model ne "FSB61" && $model ne "FSB70" &&
-          $model ne "FSM12" && $model ne "FSM61" &&
-          $model ne "FTS12") {
+          $model !~ m/(FT55|FSB14|FSB61|FSB70|FSM12|FSM61|FTS12)$/) {
         $event = "buttons";
         $msg = "released";
       } else {
@@ -10978,16 +11008,20 @@ sub EnOcean_Parse($$)
           CommandDeleteReading(undef, "$name airQuality2");
         }
         my $outdoorTemp = ($db[8] & 0xFE) >> 1;
-        $outdoorTemp -= $outdoorTemp if ($outdoorTemp & 0x40);
+        #$outdoorTemp -= $outdoorTemp if ($outdoorTemp & 0x40);
+        $outdoorTemp -= 64;
         push @event, "3:outdoorTemp:$outdoorTemp";
         my $supplyTemp = ($db[8] & 1) << 6 | ($db[7] & 0xFC) >> 2;
-        $supplyTemp -= $supplyTemp if ($supplyTemp & 0x40);
+        #$supplyTemp -= $supplyTemp if ($supplyTemp & 0x40);
+        $supplyTemp -= 64;
         push @event, "3:supplyTemp:$supplyTemp";
         my $roomTemp = ($db[7] & 3) << 5 | ($db[6] & 0xF8) >> 3;
-        $roomTemp -= $roomTemp if ($roomTemp & 0x40);
+        #$roomTemp -= $roomTemp if ($roomTemp & 0x40);
+        $roomTemp -= 64;
         push @event, "3:roomTemp:$roomTemp";
         my $exhaustTemp = ($db[6] & 7) << 4 | ($db[5] & 0xF0) >> 4;
-        $exhaustTemp -= $exhaustTemp if ($exhaustTemp & 0x40);
+        #$exhaustTemp -= $exhaustTemp if ($exhaustTemp & 0x40);
+        $exhaustTemp -= 64;
         push @event, "3:exhaustTemp:$exhaustTemp";
         push @event, "3:supplyAirFlow:". (($db[5] & 0x0F) << 2 | ($db[4] & 0xFC) >> 2);
         push @event, "3:exhaustAirFlow:" . (($db[4] & 3) << 8 | $db[3]);
@@ -12304,6 +12338,42 @@ sub EnOcean_Attr(@)
       $err = "attribute-value [$attrName] = $attrVal wrong";
     }
 
+  } elsif ($attrName eq "model") {
+    if (!defined $attrVal){
+
+    } else {
+      # set model specific attributes
+      foreach my $attrCntr (keys %{$EnO_models{$attrVal}{attr}}) {
+        if ($attrCntr eq "remoteID") {
+          if (exists $hash->{DEF}) {
+            $attr{$name}{$attrCntr} = $hash->{DEF};
+          } else {
+            $attr{$name}{$attrCntr} = EnOcean_CheckSenderID($EnO_models{$attrVal}{attr}{$attrCntr}, $hash->{IODev}{NAME}, "00000000");
+          }
+        } else {
+          $attr{$name}{$attrCntr} = $EnO_models{$attrVal}{attr}{$attrCntr};
+        }
+      }
+      if (exists $EnO_models{$attrVal}{xml}) {
+        # read xml device description to $hash->{helper}
+        if ($xmlFunc == 1) {
+          my $xmlFile = $attr{global}{modpath} . $EnO_models{$attrVal}{xml}{xmlDescrLocation};
+          if (-e -f -r $xmlFile) {
+            $hash->{helper} = $xml->XMLin($xmlFile);
+            if (exists $hash->{helper}{Device}) {
+
+            } else {
+              Log3 $name, 2, "EnOcean $name <attr> Device Description not defined";
+            }
+          } else {
+            Log3 $name, 2, "EnOcean $name <attr> Device Description file $xmlFile not exists";
+          }
+        } else {
+          Log3 $name, 2, "EnOcean $name <attr> XML functions are not available";
+        }
+      }
+    }
+
   } elsif ($attrName eq "gpDef") {
     if (!defined $attrVal){
 
@@ -12977,6 +13047,7 @@ sub EnOcean_Notify(@)
         my @getCmd = ($name, 'state');
         EnOcean_Get($hash, @getCmd);
       }
+      EnOcean_ReadDevDesc(undef, $hash);
       #Log3($name, 2, "EnOcean $name <notify> INITIALIZED");
 
     } elsif ($devName eq "global" && $s =~ m/^REREADCFG$/) {
@@ -12996,6 +13067,7 @@ sub EnOcean_Notify(@)
         }
       }
 
+      EnOcean_ReadDevDesc(undef, $hash);
       #Log3($name, 2, "EnOcean $name <notify> REREADCFG");
 
     } elsif ($devName eq "global" && $s =~ m/^ATTR ([^ ]*) ([^ ]*) ([^ ]*)$/) {
@@ -15162,6 +15234,40 @@ sub EnOcean_setTeachConfirmWaitHash($) {
 }
 
 #
+sub EnOcean_ReadDevDesc($$) {
+  # read xml device description to $hash->{helper}
+  my ($ctrl, $hash) = @_;
+  my $name = $hash->{NAME};
+  if ($xmlFunc == 0) {
+    Log3 $name, 2, "EnOcean $name XML functions are not available";
+    return;
+  }
+  if (exists($hash->{TYPE}) && $hash->{TYPE} eq 'EnOcean' && exists($attr{$name}{model})) {
+    if (exists $EnO_models{$attr{$name}{model}}) {
+      if (exists $EnO_models{$attr{$name}{model}}{xml}{xmlDescrLocation}) {
+        my $xmlFile = $attr{global}{modpath} . $EnO_models{$attr{$name}{model}}{xml}{xmlDescrLocation};
+        if (-e -f -r $xmlFile) {
+          my $xmlData = $xml->XMLin($xmlFile);
+          $hash->{helper} = $xmlData;
+          if (exists $xmlData->{Device}) {
+            Log3 $name, 5, "EnOcean $name Beginn Device Description";
+            Log3 $name, 5, "###";
+            Log3 $name, 5, Dumper($xmlData);
+            Log3 $name, 5, "###";
+            Log3 $name, 5, "EnOcean $name End Device Description";
+          } else {
+            Log3 $name, 2, "EnOcean $name Device Description not defined";
+          }
+        } else {
+          Log3 $name, 2, "EnOcean $name Device Description file $xmlFile not exists";
+        }
+      }
+    }
+  }
+  return;
+}
+
+#
 sub EnOcean_helperClear($) {
   my ($functionHash) = @_;
   my $function = $functionHash->{function};
@@ -17006,7 +17112,7 @@ EnOcean_Delete($$)
     created by autocreate. To control the device, it must be bidirectional paired,
     see <a href="#EnOcean_teach-in">Teach-In / Teach-Out</a>.<br>
     The OEM version of the Holter SmartDrive MX has an internal PID controller. This function is activated by
-    attr <device> model OEM and attr <device> pidCtrl off.<br>
+    attr <device> model Holter_OEM and attr <device> pidCtrl off.<br>
     The command is not sent until the device wakes up and sends a message, usually
     every 5 minutes.
     </li>
@@ -17129,7 +17235,7 @@ EnOcean_Delete($$)
         The attr subType must be gateway and gwCmd must be switching. This is done if the device was
         created by autocreate.<br>
         For Eltako devices attributes must be set manually. For Eltako FSA12 attribute model must be set
-        to FSA12.
+        to Eltako_FSA12.
      </li>
      <br><br>
 
@@ -17392,7 +17498,7 @@ EnOcean_Delete($$)
       <a href="#shutTime">shutTime</a> and <a href="#shutTimeCloses">shutTimeCloses</a>,
       are set correctly.
       If <a href="#EnOcean_settingAccuracy">settingAccuracy</a> is set to high, the run-time is sent in 1/10 increments.<br>
-      Set attr subType to manufProfile, manufID to 00D and attr model to FSB14|FSB61|FSB70|TF manually.<br>
+      Set attr subType to manufProfile, manufID to 00D and attr model to Eltako_FSB14|FSB61|FSB70|TF manually.<br>
       Use the sensor type "Szenentaster/PC" for Eltako devices.
     </li>
     <br><br>
@@ -18647,7 +18753,7 @@ EnOcean_Delete($$)
          <li>state: &lt;BtnX&gt;[,&lt;BtnY&gt;] [released]</li>
      </ul><br>
          The status of the device may become "released", this is not the case for a normal switch.<br>
-         Set attr model to FT55|FSM12|FSM61|FTS12 or attr sensorMode to pushbutton manually.
+         Set attr model to Eltako_FT55|FSM12|FSM61|FTS12 or attr sensorMode to pushbutton manually.
      </li>
      <br><br>
 
@@ -18701,7 +18807,7 @@ EnOcean_Delete($$)
          <li>energyHoldOff: normal|holdoff</li>
          <li>buttons: pressed|released</li>
      </ul><br>
-        Set attr subType to switch and model to FAE14|FHK14 manually. In addition
+        Set attr subType to switch and model to Eltako_FAE14|FHK14 manually. In addition
         every telegram received from a teached-in temperature sensor (e.g. FTR55H)
         is repeated as a confirmation telegram from the Heating/Cooling Relay
         FAE14, FHK14. In this case set attr subType to e. g. roomSensorControl.05
@@ -20030,7 +20136,7 @@ EnOcean_Delete($$)
         if the command position is sent or the reading state was changed
         manually to open or closed.<br>
         Set attr subType file, attr manufID to 00D and attr model to
-        FSB14|FSB61|FSB70 manually.
+        Eltako_FSB14|FSB61|FSB70 manually.
      </li>
      <br><br>
 

+ 27 - 9
fhem/core/FHEM/10_FBDECT.pm

@@ -1,7 +1,8 @@
 ##############################################
-# $Id: 10_FBDECT.pm 15295 2017-10-20 07:03:57Z rudolfkoenig $
+# $Id: 10_FBDECT.pm 16293 2018-02-28 21:33:57Z rudolfkoenig $
 package main;
 
+# See also https://avm.de/fileadmin/user_upload/Global/Service/Schnittstellen/AHA-HTTP-Interface.pdf
 use strict;
 use warnings;
 use SetExtensions;
@@ -13,7 +14,7 @@ sub FBDECT_Cmd($$@);
 
 sub FBDECT_decodePayload($$$);
 
-my @fbdect_models = qw(Powerline546E Dect200 CometDECT);
+my @fbdect_models = qw(Powerline546E Dect200 CometDECT HAN-FUN);
 
 my %fbdect_payload = (
    7 => { n=>"connected" },
@@ -176,9 +177,12 @@ FBDECT_Get($@)
   my $cmd = ($a[1] ? $a[1] : "");
   my %gets = ("devInfo"=>1);
 
-  my $cmdList = ($hash->{IODev} && $hash->{IODev}{TYPE} eq "FBAHA") ? 
-                  join(" ", sort keys %gets) : "";
-  return "Unknown argument $cmd, choose one of $cmdList" if(!$gets{$cmd});
+  if($hash->{IODev} && $hash->{IODev}{TYPE} eq "FBAHA") {
+    return "Unknown argument $cmd, choose one of ".join(" ",sort keys %gets)
+        if(!$gets{$cmd});
+  } else {
+    return "Unknown argument $cmd, choose one of ";
+  }
 
   if($cmd eq "devInfo") {
     my @answ = FBAHA_getDevList($hash->{IODev}, $hash->{id});
@@ -230,6 +234,8 @@ my %fbhttp_readings = (
 #  tist => 'sprintf("temperature:%.1f C (measured)", $val/2)', # Forum #57644
    tsoll           => 'sprintf("desired-temp:%s", $val)',
    members         => '"members:$val"',
+   devicelock      => '"devicelock:".($val ? "yes":"no")',
+   errorcode       => '"errorcode:".($ecTxt{$val} ? $ecTxt{$val} : ">$val<")',
 );
 
 sub
@@ -245,8 +251,20 @@ FBDECT_ParseHttp($$$)
   my $ain = $h{identifier};
   $ain =~ s/[-: ]/_/g;
 
-  my %ll = (6=>"actuator", 7=>"powerMeter", 8=>"tempSensor",
-            9=>"switch", 10=>"repeater");
+  my %ll = (4=>"alarmSensor",
+            6=>"actuator",
+            7=>"powerMeter",
+            8=>"tempSensor",
+            9=>"switch",
+           10=>"repeater");
+  my %ecTxt = (0 => "noError (0)",
+               1 => "notMounted (1)",
+               2 => "valveShortOrBatteryEmpty (2)",
+               3 => "valveStuck (3)",
+               4 => "installationPreparation (4)",
+               5 => "installationInProgress (5)",
+               6 => "installationIsAdapting (6)");
+
   my $lsn = int($h{functionbitmask});
   my @fb;
   map { push @fb, $ll{$_} if((1<<$_) & $lsn) } sort keys %ll;
@@ -601,7 +619,7 @@ FBDECT_Undef($$)
 <ul>
   Dieses Modul wird verwendet, um AVM FRITZ!DECT Ger&auml;te via FHEM zu
   steuern, siehe auch das <a href="#FBAHA">FBAHA</a> oder <a
-  href="#FBAHAHTTP">FBAHAHTTP</a> Modul f&uumlr die Anbindung an das FRITZ!Box.
+  href="#FBAHAHTTP">FBAHAHTTP</a> Modul f&uuml;r die Anbindung an das FRITZ!Box.
   <br><br>
   <a name="FBDECTdefine"></a>
   <b>Define</b>
@@ -626,7 +644,7 @@ FBDECT_Undef($$)
   <ul>
   <li>on/off<br>
     Ger&auml;t einschalten bzw. ausschalten.</li>
-  <li>desired-temp &lt;value&/gt;<br>
+  <li>desired-temp &lt;value&gt;<br>
     Gew&uuml;nschte Temperatur beim Comet DECT setzen (nur mit FBAHAHTTP als
     IODev).
     </li>

File diff suppressed because it is too large
+ 1447 - 409
fhem/core/FHEM/10_FRM.pm


+ 3 - 1
fhem/core/FHEM/10_HXBDevice.pm

@@ -1,4 +1,4 @@
-# $Id: 10_HXBDevice.pm 7686 2015-01-24 11:54:59Z borisneubert $
+# $Id: 10_HXBDevice.pm 16375 2018-03-10 15:40:02Z neubert $
 ##############################################################################
 #
 #     10_HXBDevice.pm
@@ -280,6 +280,8 @@ HXBDevice_Parse($$)
 #############################
 
 =pod
+=item summary    receive multicast messages from a Hexabus device
+=item summary_DE empfange Multicast-Nachrichten von einem Hexabus-Ger&auml;
 =begin html
 
 <a name="HXBDevice"></a>

+ 35 - 11
fhem/core/FHEM/10_KNX.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 10_KNX.pm 15259 2017-10-14 18:48:05Z andi291 $
+# $Id: 10_KNX.pm 15906 2018-01-16 18:33:07Z andi291 $
 # ABU 20160307 First release
 # ABU 20160309 Fixed issue for sending group-indexed with dpt1. Added debug-information. Fixed issue for indexed get. Fixed regex-replace-issue.
 # ABU 20160312 Fixed error while receiving numeric DPT with value 0. Added factor for dpt 08.010.
@@ -37,6 +37,10 @@
 # ABU 20170622 finetuned doku
 # ABU 20171006 added sub-dpt1
 # ABU 20171006 added dpt19
+# ABU 20171212 added dpt14.057
+# ABU 20171212 finetuned doku
+# ABU 20171215 added fix for newline in def
+# docm 20180109 fixed problem with dpt16 reading-set
 
 package main;
 
@@ -181,6 +185,7 @@ my %dpttypes = (
 	"dpt14.019"		=> {CODE=>"dpt14", UNIT=>"A", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/, MIN=>undef, MAX=>undef},
 	"dpt14.027"		=> {CODE=>"dpt14", UNIT=>"V", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/, MIN=>undef, MAX=>undef},    
 	"dpt14.056"		=> {CODE=>"dpt14", UNIT=>"W", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/, MIN=>undef, MAX=>undef},
+	"dpt14.057"		=> {CODE=>"dpt14", UNIT=>"cos &Phi;", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/, MIN=>undef, MAX=>undef},  	
 	"dpt14.068"		=> {CODE=>"dpt14", UNIT=>"&deg;C", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/, MIN=>undef, MAX=>undef},  
 	"dpt14.076"		=> {CODE=>"dpt14", UNIT=>"m&sup3;", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/, MIN=>undef, MAX=>undef},  
   
@@ -232,6 +237,7 @@ KNX_Initialize($) {
 sub
 KNX_Define($$) {
 	my ($hash, $def) = @_;
+	$def =~ s/\n/ /g;
 	my @a = split("[ \t][ \t]*", $def);
 	#device name
 	my $name = $a[0];
@@ -580,12 +586,21 @@ KNX_Set($@) {
 	{
 		return "\"string\" only allowed for dpt16" if (not($code eq "dpt16"));
 		return "no data for cmd $cmd" if ($lastArg < 2);
-		
+
 		#join string
-		for (my $i=2; $i<=$lastArg; $i++)
+		#docm 180109 removed
+		#		for (my $i=2; $i<=$lastArg; $i++)
+		#		{
+		#		  $value.= $a[$i]." ";		  
+		#		}
+
+		#docm 180109 inserted
+		$value = $a[2];
+		for (my $i=3; $i<=$lastArg; $i++)
 		{
-		  $value.= $a[$i]." ";		  
-		}				
+		  $value.= " ".$a[$i];		  
+		}
+		#docm 180109 changes end
 	} 	
 	#set RGB <RRGGBB>
 	elsif ($cmd =~ m/$RGB/)
@@ -1572,6 +1587,11 @@ KNX_decodeByDpt ($$$) {
 		$numval = 0;
 		$state  = "";
 		
+		#docm 180109 inserted
+		$value =~ /^\s*(00)?(\S+)/;
+		$value = $2;
+		#docm 180109 changes end				
+		
 		for (my $i = 0; $i < 14; $i++) 
 		{
 			my $c = hex(substr($value, $i * 2, 2));
@@ -1894,9 +1914,9 @@ sub KNX_getCmdList ($$$)
 	dpt1.017 trigger, trigger<br>
 	dpt1.018 not occupied, occupied<br>
 	dpt1.019 closed, open<br>
-	dpt1.020 logical or, logical and<br>
-	dpt1.021 scene A, scene B<br>
-	dpt1.022 move up/down, move and step mode<br>
+	dpt1.021 logical or, logical and<br>
+	dpt1.022 scene A, scene B<br>
+	dpt1.023 move up/down, move and step mode<br>
 	dpt2 value on, value off, value forceOn, value forceOff<br>
 	dpt3 -100..+100<br>
 	dpt5 0..255<br>
@@ -1939,11 +1959,13 @@ sub KNX_getCmdList ($$$)
 	dpt14.019 -Inf.0..+Inf.0 A<br>
 	dpt14.027 -Inf.0..+Inf.0 V<br>
 	dpt14.056 -Inf.0..+Inf.0 W<br>
+	dpt14.057 -Inf.0..+Inf.0 cos&Phi;<br>		
 	dpt14.068 -Inf.0..+Inf.0 &degC;<br>
 	dpt14.076 -Inf.0..+Inf.0 m&sup3;<br>
 	dpt16 String;<br>
 	dpt16.000 ASCII-String;<br>
 	dpt16.001 ISO-8859-1-String (Latin1);<br>
+	dpt19 01.12.2010_01:00:00<br>
 	dpt232 RGB-Value RRGGBB<br>
   </ul>		
 </ul>
@@ -2176,9 +2198,9 @@ sub KNX_getCmdList ($$$)
 	dpt1.017 trigger, trigger<br>
 	dpt1.018 not occupied, occupied<br>
 	dpt1.019 closed, open<br>
-	dpt1.020 logical or, logical and<br>
-	dpt1.021 scene A, scene B<br>
-	dpt1.022 move up/down, move and step mode<br>
+	dpt1.021 logical or, logical and<br>
+	dpt1.022 scene A, scene B<br>
+	dpt1.023 move up/down, move and step mode<br>
 	dpt2 value on, value off, value forceOn, value forceOff<br>
 	dpt3 -100..+100<br>
 	dpt5 0..255<br>
@@ -2221,11 +2243,13 @@ sub KNX_getCmdList ($$$)
 	dpt14.019 -Inf.0..+Inf.0 A<br>
 	dpt14.027 -Inf.0..+Inf.0 V<br>
 	dpt14.056 -Inf.0..+Inf.0 W<br>
+	dpt14.057 -Inf.0..+Inf.0 cos&Phi;<br>		
 	dpt14.068 -Inf.0..+Inf.0 &degC;<br>
 	dpt14.076 -Inf.0..+Inf.0 m&sup3;<br>
 	dpt16 String;<br>
 	dpt16.000 ASCII-String;<br>
 	dpt16.001 ISO-8859-1-String (Latin1);<br>
+	dpt19 01.12.2010_01:00:00<br>
 	dpt232 RGB-Value RRGGBB<br>
   </ul>
 </ul>

+ 31 - 3
fhem/core/FHEM/10_MQTT_DEVICE.pm

@@ -20,7 +20,7 @@
 #     You should have received a copy of the GNU General Public License
 #     along with fhem.  If not, see <http://www.gnu.org/licenses/>.
 #
-# $Id: 10_MQTT_DEVICE.pm 15202 2017-10-05 20:35:33Z eisler $
+# $Id: 10_MQTT_DEVICE.pm 16252 2018-02-23 21:32:59Z eisler $
 #
 ##############################################
 
@@ -52,6 +52,7 @@ sub MQTT_DEVICE_Initialize($) {
     "publishSet_.* ".
     "subscribeReading_.* ".
     "autoSubscribeReadings ".
+    "useSetExtensions:1,0 ".
     $main::readingFnAttributes;
     
     main::LoadModule("MQTT");
@@ -64,6 +65,7 @@ use warnings;
 use GPUtils qw(:all);
 
 use Net::MQTT::Constants;
+use SetExtensions qw/ :all /;
 
 BEGIN {
   MQTT->import(qw(:all));
@@ -73,6 +75,8 @@ BEGIN {
     CommandAttr
     readingsSingleUpdate
     Log3
+    SetExtensions
+    SetExtensionsCancel
     fhem
     defs
     AttrVal
@@ -91,6 +95,14 @@ sub Set($$$@) {
   return "Need at least one parameters" unless defined $command;
   my $msgid;
   my $mark=0;
+
+  if (AttrVal($name,"useSetExtensions",undef)) {
+    if ($command =~ m/^(blink|intervals|(off-|on-)(for-timer|till(-overnight)?))(.+)?|toggle$/) {
+      Log3($hash->{NAME},5,"calling SetExtensions(...) for $command");
+      return SetExtensions($hash, join(" ", map {$hash->{sets}->{$_} eq "" ? $_ : "$_:".$hash->{sets}->{$_}} sort keys %{$hash->{sets}}), $name, $command, @values);
+    }
+  }
+
   if($command ne '?') {
     if(defined($hash->{publishSets}->{$command})) {
       my $value = join " ",@values;
@@ -115,8 +127,13 @@ sub Set($$$@) {
     }
   }
   if(!$mark) {
-    return "Unknown argument $command, choose one of " . join(" ", map {$hash->{sets}->{$_} eq "" ? $_ : "$_:".$hash->{sets}->{$_}} sort keys %{$hash->{sets}})
+    if(AttrVal($name,"useSetExtensions",undef)) {
+      return SetExtensions($hash, join(" ", map {$hash->{sets}->{$_} eq "" ? $_ : "$_:".$hash->{sets}->{$_}} sort keys %{$hash->{sets}}), $name, $command, @values);
+    } else {
+      return "Unknown argument $command, choose one of " . join(" ", map {$hash->{sets}->{$_} eq "" ? $_ : "$_:".$hash->{sets}->{$_}} sort keys %{$hash->{sets}})
+    }
   }
+  SetExtensionsCancel($hash);
   $hash->{message_ids}->{$msgid}++ if defined $msgid;
   readingsSingleUpdate($hash,"transmission-state","outgoing publish sent",1);
   return undef;
@@ -171,7 +188,8 @@ sub Attr($$$$) {
     };
     $attribute =~ /^publishSet(_?)(.*)/ and do {
       if ($command eq "set") {
-        my @values = split ("[ \t]+",$value);
+        my ( $aa, $bb ) = parseParams($value);
+        my @values = @{$aa};
         my $topic = pop @values;
         $hash->{publishSets}->{$2} = {
           'values' => \@values,
@@ -264,6 +282,12 @@ sub onmessage($$$) {
       <p><code>set &lt;name&gt; &lt;reading&gt; &lt;value&gt;</code><br/>
          sets reading &lt;reading&gt; and publishes the command to topic configured via attr publishSet_&lt;reading&gt;</p>
     </li>
+    <li>
+      <p>The <a href="#setExtensions">set extensions</a> are supported with useSetExtensions attribute.<br/>
+      Set eventMap if your publishSet commands are not on/off.</p>
+      <p>example for true/false:<br/>
+      <code>attr mqttest eventMap { dev=>{ 'true'=>'on', 'false'=>'off' }, usr=>{ '^on$'=>'true', '^off$'=>'false' }, fw=>{ '^on$'=>'on', '^off$'=>'off' } }</code></p>
+    </li>
   </ul>
   <a name="MQTT_DEVICEattr"></a>
   <p><b>Attributes</b></p>
@@ -314,6 +338,10 @@ sub onmessage($$$) {
          defines QOS 0 for all readings/topics except the reading 'test'. Retain for 'test' is 1<br>
        </p>
     </li>
+    <li>
+      <p><code>attr &lt;name&gt; useSetExtensions &lt;flags&gt;</code><br/>
+         If set to 1, then the <a href="#setExtensions">set extensions</a> are supported.</p>
+    </li>
   </ul>
 </ul>
 

+ 2 - 2
fhem/core/FHEM/10_MYSENSORS_DEVICE.pm

@@ -19,7 +19,7 @@
 #     You should have received a copy of the GNU General Public License
 #     along with fhem.  If not, see <http://www.gnu.org/licenses/>.
 #
-# $Id: 10_MYSENSORS_DEVICE.pm 15225 2017-10-10 13:31:10Z Hauswart $
+# $Id: 10_MYSENSORS_DEVICE.pm 15790 2018-01-05 12:04:05Z Hauswart $
 #
 ##############################################
 
@@ -99,7 +99,7 @@ my %static_types = (
   S_POWER                 => { receives => [V_VAR1], sends => [V_WATT,V_KWH,V_VAR,V_VA,V_POWER_FACTOR,V_VAR1] }, # Power measuring device, like power meters
   S_HEATER                => { receives => [], sends => [V_HVAC_SETPOINT_HEAT,V_HVAC_FLOW_STATE,V_TEMP,V_STATUS] }, # Heater device
   S_DISTANCE              => { receives => [], sends => [V_DISTANCE,V_UNIT_PREFIX] }, # Distance sensor
-  S_LIGHT_LEVEL           => { receives => [], sends => [V_LIGHT_LEVEL] }, # Light sensor
+  S_LIGHT_LEVEL           => { receives => [], sends => [V_LIGHT_LEVEL,V_LEVEL] }, # Light sensor
   S_ARDUINO_NODE          => { receives => [], sends => [] }, # Arduino node device
   S_ARDUINO_REPEATER_NODE => { receives => [], sends => [] }, # Arduino repeating node device
   S_LOCK                  => { receives => [V_LOCK_STATUS], sends => [V_LOCK_STATUS] }, # Lock device

+ 34 - 12
fhem/core/FHEM/10_OWServer.pm

@@ -1,4 +1,4 @@
-# $Id: 10_OWServer.pm 14523 2017-06-16 05:15:56Z neubert $
+# $Id: 10_OWServer.pm 15589 2017-12-11 11:58:29Z neubert $
 ################################################################
 #
 #  Copyright notice
@@ -75,6 +75,7 @@ use vars qw(%gets %sets);
   "/settings/timeout/w1"               => "",
   "/settings/units/pressure_scale"     => "",
   "/settings/units/temperature_scale"  => "",
+  "/uncached/alarm"                    => "",
 );
 
 %sets = (
@@ -135,9 +136,9 @@ OWServer_Define($$)
   my $protocol = $a[2];
 
   $hash->{fhem}{protocol}= $protocol;
-  
+
   $hash->{NOTIFYDEV} = "global";
- 
+
 
   if( $init_done ) {
     OWServer_OpenDev($hash);
@@ -267,7 +268,7 @@ OWServer_Read($@)
       my $r= defined($ret) ? $ret : "<undefined>";
       Log3 $hash, 5, "OWServer child read $path: $r";
       delete $hash->{".path"};
-      print WRITER $ret if(defined($ret)); 
+      print WRITER $ret if(defined($ret));
       close WRITER;
       # see http://forum.fhem.de/index.php?t=tree&goto=94670
       # changed from
@@ -388,7 +389,7 @@ OWServer_Autocreate($)
     return undef if(AttrVal($acdname,"disable",undef));
   }
   return undef unless($acdname ne "");
-  
+
   my $owserver= $hash->{fhem}{owserver};
 
   my @dir= split(",", $owserver->dir("/"));
@@ -398,7 +399,7 @@ OWServer_Autocreate($)
   foreach my $d (keys %defs) {
     next if($defs{$d}{TYPE} !~ /^OW(Device|AD|ID|MULTI|COUNT|LCD|SWITCH|THERM)$/);
     if(defined($defs{$d}{fhem}) && defined($defs{$d}{fhem}{address})) {
-      $defined{$defs{$d}{fhem}{address}} = $d; 
+      $defined{$defs{$d}{fhem}{address}} = $d;
     } elsif(defined($defs{$d}{OW_ID}) and defined($defs{$d}{OW_FAMILY})) {
       $defined{"$defs{$d}{OW_FAMILY}.$defs{$d}{OW_ID}"} = $d;
     }
@@ -486,6 +487,21 @@ OWServer_Get($@)
           $ret .= sprintf("%-*s %d\n",$wide,$str,($stat) ? $stat : 0);
         }
         return $ret;
+  } elsif($cmd eq "/uncached/alarm") {
+        # Oliver Vallant, 2017-04-17
+        my $path= $cmd;
+        my @devices= split(",", $owserver->dir($path));
+        my $ret;
+        for my $device (@devices) {
+          my $name= "";
+          my $address= substr($device, rindex($device, "/")+1);
+          my $type= $owserver->read($device . "/type");
+          foreach my $p (keys %defs) {
+             $name= concatc(", ", $name, $p) if($defs{$p}{TYPE} eq "OWDevice" and $defs{$p}{fhem}{address} eq $address);
+          }
+          $ret .= sprintf("%s %10s %s\n", $address, $type, $name);
+        }
+        return $ret;
   } elsif(defined($gets{$cmd})) {
         my $ret;
         my $value= $owserver->read($cmd);
@@ -555,10 +571,12 @@ OWServer_Set($@)
     You need <a href="http://owfs.cvs.sourceforge.net/viewvc/owfs/owfs/module/ownet/perl5/OWNet/lib/OWNet.pm">OWNet.pm from owfs.org on Sourceforge</a>, which is normally deployed with FHEM. As at 2012-12-23 the OWNet module
     on CPAN has an issue which renders it useless for remote connections.
     <p>
-    The ow* version 2.9 packages provided with Debian Jessie in combination with OWNet.pm as deployed with FHEM have issues. 
-    For Debian Jessie please either unzip 
+    The ow* version 2.9 packages provided with Debian Jessie in combination with OWNet.pm as deployed with FHEM have issues.
+    For Debian Jessie please either unzip
     <a href="http://forum.fhem.de/index.php?action=dlattach;topic=12219.0;attach=2463">owfs_2.8p17-1_all.zip</a> and install
-    owserver, dependencies and what else you require with <code>dpkg -i &lt;package&gt;.deb</code> or use the latest OWNet.pm from Sourceforge. 
+    owserver, dependencies and what else you require with <code>dpkg -i &lt;package&gt;.deb</code> or use the latest OWNet.pm from Sourceforge.
+    <p>
+    The ow* version 3.1 packages provided with Debian Stretch are fine.
     <p>
     A typical working configuration file <code>/etc/owfs.conf</code> looks as follows:<p>
     <code>
@@ -654,6 +672,7 @@ OWServer_Set($@)
         <li><code>/settings/timeout/w1</code></li>
         <li><code>/settings/units/pressure_scale</code></li>
         <li><code>/settings/units/temperature_scale</code></li>
+        <li><code>/uncached/alarm</code></li>
       </ul>
     </li>
     For further informations have look on <a href="http://owfs.org/uploads/owserver.1.html#sect41">owserver manual</a>).
@@ -693,11 +712,11 @@ OWServer_Set($@)
     <br><br>
 
     Definiert eine logische OWServer- Instanz. OWServer ist die Serverkomponente des
-    <a href="http://owfs.org">1-Wire Dateisystems</a>. Sie ermöglicht den Zugriff auf 
+    <a href="http://owfs.org">1-Wire Dateisystems</a>. Sie ermöglicht den Zugriff auf
     alle 1-Wire- Busteilnehmer eines Systems.<br><br>
         &lt;protocol&gt; hat das Format &lt;hostname&gt;:&lt;port&gt;  Nähere Informationen dazu gibt es in der <a href="http://owfs.org/index.php?page=owserver_protocol">owserver Dokumentation</a>.
         <br><br>
-    Voraussetzung innerhalb von FHEM ist das Modul <a href="http://owfs.cvs.sourceforge.net/viewvc/owfs/owfs/module/ownet/perl5/OWNet/lib/OWNet.pm">OWNet.pm von owfs.org</a>, welches bereits mit FHEM ausgeliefert wird. 
+    Voraussetzung innerhalb von FHEM ist das Modul <a href="http://owfs.cvs.sourceforge.net/viewvc/owfs/owfs/module/ownet/perl5/OWNet/lib/OWNet.pm">OWNet.pm von owfs.org</a>, welches bereits mit FHEM ausgeliefert wird.
         Das auf CPAN erhältliche OWNet- Modul beinhaltet seit dem 23.12.2012 einen Fehler, der es für Fernzugriffe unbrauchbar macht.<p>
         Auf dem Computer, an dem der 1-Wire- Bus angeschlossen ist, muss die Software "owserver" installiert sein. Zusätzlich sollte auf diesem Rechner die Konfigurationsdatei "owfs.conf" eingesehen bzw. angepasst werden. <a href="http://www.fhemwiki.de/wiki/OWServer_%26_OWDevice#Tipps_und_Tricks"> Einen WIKI- Artikel dazu gibt es hier.</a>
     <br><br>
@@ -705,6 +724,8 @@ OWServer_Set($@)
     <a href="http://forum.fhem.de/index.php?action=dlattach;topic=12219.0;attach=2463">owfs_2.8p17-1_all.zip</a> und installiere
     owserver, Abh&auml;ngigkeiten und was Du sonst noch brauchst mit <code>dpkg -i &lt;package&gt;.deb</code>, oder benutze die neueste Version von OWNet.pm von Sourceforge.
     <p>
+    Die ow*-Pakete in der Version 3.1 von Debian Stretch sind in Ordnung.
+    <p>
     Eine typische funktionierende Konfigurationsdatei <code>/etc/owfs.conf</code> sieht so aus:<p>
     <code>
       # server uses device /dev/onewire<br>
@@ -798,6 +819,7 @@ OWServer_Set($@)
         <li><code>/settings/timeout/w1</code></li>
         <li><code>/settings/units/pressure_scale</code></li>
         <li><code>/settings/units/temperature_scale</code></li>
+        <li><code>/uncached/alarm</code></li>
       </ul>
     </li>
     Nähere Informationen zu diesen Einstellungen gibt es im <a href="http://owfs.org/uploads/owserver.1.html#sect41">owserver- Manual</a>.
@@ -818,7 +840,7 @@ OWServer_Set($@)
     <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
   </ul>
   <br>
-  Hinweis: Falls in FHEM trotzdem ungewöhnliche Stillstände auftreten, sollte das Attribut <code>nonblocking</code> wieder deaktiviert werden.<br>  
+  Hinweis: Falls in FHEM trotzdem ungewöhnliche Stillstände auftreten, sollte das Attribut <code>nonblocking</code> wieder deaktiviert werden.<br>
 
 </ul>
 

File diff suppressed because it is too large
+ 792 - 611
fhem/core/FHEM/10_SOMFY.pm


+ 5 - 6
fhem/core/FHEM/10_ZWave.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 10_ZWave.pm 15445 2017-11-18 10:29:25Z rudolfkoenig $
+# $Id: 10_ZWave.pm 16266 2018-02-25 18:22:51Z rudolfkoenig $
 # See ZWDongle.pm for inspiration
 package main;
 
@@ -5142,7 +5142,7 @@ ZWave_fhemwebFn($$$$)
   my $iodev = $defs{$d}{IODev}{NAME};
   my $hs = AttrVal($iodev, "helpSites", $zwave_activeHelpSites);
   for my $n (split(",", $hs)) {
-    my $link = $zwave_link{$n}{$model};
+    my $link = $zwave_link{$n}{lc($model)};
     next if(!$link);
     $pl .= "<div class='detLink ZWPepper'>";
     my $url = ($n eq "alliance" ?
@@ -5152,7 +5152,7 @@ ZWave_fhemwebFn($$$$)
     $pl .= "</div>";
   }
 
-  my $img = ZWave_getPic($iodev, $model);
+  my $img = ZWave_getPic($iodev, lc($model));
   if($img && !$FW_ss) {
     $pl .= "<div class='img'".($FW_tp?"":" style='float:right'").">";
     $pl .= "<img style='max-width:96;max-height:96px;' src='$img'>";
@@ -5162,7 +5162,7 @@ ZWave_fhemwebFn($$$$)
   return
   "<div id='ZWHelp' class='makeTable help'></div>$pl".
   '<script type="text/javascript">'.
-   "var d='$d', FW_tp='$FW_tp';" . <<'JSEND'
+   "var zwaveDevice='$d', FW_tp='$FW_tp';" . <<'JSEND'
     $(document).ready(function() {
       $("div#ZWHelp").insertBefore("div.makeTable.internals"); // Move
       $("div.detLink.ZWPepper").insertAfter("div.detLink.devSpecHelp");
@@ -5172,7 +5172,7 @@ ZWave_fhemwebFn($$$$)
           $("div#ZWHelp").html(val);
         }
         $(this).change(function(){
-          FW_queryValue('{ZWave_helpFn("'+d+'","'+$(this).val()+'")}',
+          FW_queryValue('{ZWave_helpFn("'+zwaveDevice+'","'+$(this).val()+'")}',
                         $(this).get(0));
         });
       });
@@ -5843,7 +5843,6 @@ s2Hex($)
   <li>battery<br>
     return the charge of the battery in %, as battery:value % or battery:low
     </li>
-    </li>
 
   <br><br><b>CLASS DOOR_LOCK_LOGGING, V1 (deprecated)</b>
   <li>doorLockLoggingRecordsSupported<br>

+ 8 - 2
fhem/core/FHEM/10_pilight_ctrl.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 10_pilight_ctrl.pm 14997 2017-09-03 17:27:39Z Risiko $
+# $Id: 10_pilight_ctrl.pm 16028 2018-01-28 18:34:25Z Risiko $
 #
 # Usage
 # 
@@ -51,6 +51,7 @@
 # V 1.24 2017-04-22 - FIX: GS-iwds07 support
 # V 1.25 2017-04-23 - FIX: react only of global::INITIALIZED m/^INITIALIZED$/
 # V 1.26 2017-09-03 - FIX: heitech support
+# V 1.27 2018-01-28 - NEW: handle bh1750 illuminance sensor as weather station
 ############################################## 
 package main;
 
@@ -430,7 +431,7 @@ sub pilight_ctrl_Write($@)
           case m/mumbi/         {$code .= "\"systemcode\":$id,\"unitcode\":$unit,";}
           case m/brennenstuhl/  {$code .= "\"systemcode\":$id,\"unitcode\":$unit,";}
           case m/pollin/        {$code .= "\"systemcode\":$id,\"unitcode\":$unit,";}
-          case m/heitech/		{$code .= "\"systemcode\":$id,\"unitcode\":$unit,";}
+          case m/heitech/		    {$code .= "\"systemcode\":$id,\"unitcode\":$unit,";}
           case m/impuls/        {$code .= "\"systemcode\":$id,\"programcode\":$unit,";}
           case m/rsl366/        {$code .= "\"systemcode\":$id,\"programcode\":$unit,";}
           case m/daycom/        { if (!defined($syscode)) {
@@ -859,6 +860,9 @@ sub pilight_ctrl_Parse($$)
     case m/teknihall/   {$protoID = 4;}
     case m/oregon_21/   {$protoID = 4;}
     
+    #handle illuminance sensor as weather station - workaround
+    case m/bh1750/      {$protoID = 4;}
+    
     #gpio temperature, humidity sensors
     case m/dht11/       {$protoID = 4;}
     case m/dht22/       {$protoID = 4;}
@@ -915,6 +919,8 @@ sub pilight_ctrl_Parse($$)
         $piTempData .= ",windavg:$data->{$s}{windavg}"          if (defined($data->{$s}{windavg}));
         $piTempData .= ",winddir:$data->{$s}{winddir}"          if (defined($data->{$s}{winddir}));
         $piTempData .= ",windgust:$data->{$s}{windgust}"        if (defined($data->{$s}{windgust}));
+        #workaround illuminance sensor
+        $piTempData .= ",illuminance:$data->{$s}{illuminance}"  if (defined($data->{$s}{illuminance}));
         
         my $msg = "PITEMP,$proto,$id$piTempData";
         Log3 $me, 4, "$me(Dispatch): $msg";

+ 2 - 2
fhem/core/FHEM/11_FHT.pm

@@ -1,4 +1,4 @@
-# $Id: 11_FHT.pm 14888 2017-08-13 12:07:12Z rudolfkoenig $
+# $Id: 11_FHT.pm 16293 2018-02-28 21:33:57Z rudolfkoenig $
 ##############################################################################
 #
 #     11_FHT.pm
@@ -1193,7 +1193,7 @@ FHT_State($$$$)
 
       <li>date setzt Jahr, Monat und Tag auf lokale Zeit</li><br>
 
-      <li>refreshvalues ist ein Alias f&uumlr report1 255 report2 255</li><br>
+      <li>refreshvalues ist ein Alias f&uuml;r report1 255 report2 255</li><br>
 
       <li>Alle *-temp Werte brauchen eine Temperatur als Argument welche auf
           0.5&deg;C gerundet wird.<br> Temperatur Werte m&uuml;ssen zwischen

+ 42 - 5
fhem/core/FHEM/11_OWX_CCC.pm

@@ -6,7 +6,7 @@
 #
 # Prof. Dr. Peter A. Henning
 #
-# $Id: 11_OWX_CCC.pm 15392 2017-11-05 06:46:46Z phenning $
+# $Id: 11_OWX_CCC.pm 16362 2018-03-09 17:18:43Z phenning $
 #
 ########################################################################################
 #
@@ -17,6 +17,9 @@
 # Alarms
 # Complex
 # Discover
+# Open
+# Close
+# Reopen
 # Init
 # Read
 # ReadLow
@@ -51,7 +54,7 @@ sub new($) {
 	return bless {
 		hash => $hash,
 	    #-- module version
-		version => "7.05"
+		version => "7.08"
 	}, $class;
 }
 
@@ -81,7 +84,6 @@ sub Define($) {
     my $dev  = $a[2];
     $hash->{DeviceName} = $dev;
     
-    
     #-- Second step in case of CUNO: See if we can open it
     my $msg = "OWX_CCC::Define COC/CUNO device $dev";
     #-- hash des COC/CUNO
@@ -109,8 +111,6 @@ sub Define($) {
     #-- reset the 1-Wire system in COC/CUNO
     main::CUL_SimpleWrite($hwdevice, "Oi");
       
-    #-- module version
-	$hash->{version}      = "7.0beta2";
     main::Log3 $name,1,"OWX_CCC::Define warning: version ".$hash->{version}." not identical to OWX version ".$main::owx_version
       if( $hash->{version} ne $main::owx_version);
       
@@ -297,6 +297,39 @@ sub Discover () {
 }
 
 ########################################################################################
+#
+# Open - Open Device
+#
+########################################################################################
+
+sub Open () {
+  my ($self) = @_;
+  my $hash = $self->{hash};
+}
+
+########################################################################################
+#
+# Close - Close Device
+#
+########################################################################################
+
+sub Close () {
+  my ($self) = @_;
+  my $hash = $self->{hash};
+ 
+}
+
+########################################################################################
+#
+# Reopen - Reopen Device
+#
+########################################################################################
+
+sub Reopen () {
+  main::Log 1,"[OWX_CCC] Warning: ->Reopen currently not defined
+}
+
+########################################################################################
 # 
 # Init - Low Level Init of the 1-wire device
 #
@@ -598,11 +631,15 @@ sub Write(@) {
 
 <a name="OWX_CCC"></a>
 <h3>OWX_CCC</h3>
+<ul>
 See <a href="/fhem/docs/commandref.html#OWX">OWX</a>
+</ul>
 =end html
 =begin html_DE
 
 <a name="OWX_CCC"></a>
 <h3>OWX_CCC</h3>
+<ul>
 <a href="http://fhemwiki.de/wiki/Interfaces_f%C3%BCr_1-Wire">Deutsche Dokumentation im Wiki</a> vorhanden, die englische Version gibt es hier: <a href="/fhem/docs/commandref.html#OWX">OWX</a> 
+</ul>
 =end html_DE

+ 107 - 31
fhem/core/FHEM/11_OWX_FRM.pm

@@ -6,7 +6,7 @@
 #
 # Prof. Dr. Peter A. Henning
 #
-# $Id: 11_OWX_FRM.pm 15392 2017-11-05 06:46:46Z phenning $
+# $Id: 11_OWX_FRM.pm 16362 2018-03-09 17:18:43Z phenning $
 #
 ########################################################################################
 #
@@ -17,6 +17,9 @@
 # Alarms
 # Complex
 # Discover
+# Open
+# Close
+# Reopen
 # Read
 # Ready
 # Verify
@@ -41,6 +44,7 @@ use strict;
 use warnings;
 
 use Device::Firmata::Constants qw/ :all /;
+use GPUtils qw(:all);
 
 ########################################################################################
 # 
@@ -52,7 +56,9 @@ sub new($) {
 	my ($class,$hash) = @_;
 
 	return bless {
-		hash => $hash
+		hash => $hash,
+	    #-- module version
+        version => "7.08";
 	}, $class;
 }
 
@@ -97,13 +103,13 @@ sub Define($) {
     $hash->{PIN}          = $pin;
     $hash->{ASYNCHRONOUS} = 0;  
   
-    #-- module version
-	$hash->{version}      = "7.05";
     main::Log3 $hash->{NAME},1,"OWX_FRM::Define warning: version ".$hash->{version}." not identical to OWX version ".$main::owx_version
       if( $hash->{version} ne $main::owx_version);
    
-    #-- call low level init function for the device
-    main::InternalTimer(time()+55, "OWX_FRM::Init", $self,0);
+    #-- register IODev InitFn to be called by FRM after connection to Firmata device is initialized
+    $hash->{IODev} = $main::defs{$hash->{HWDEVICE}};
+    $main::modules{$main::defs{$hash->{NAME}}{TYPE}}->{InitFn} = "OWX_FRM::Init";
+
     return undef;
 }
 
@@ -154,7 +160,10 @@ sub Alarms() {
 	my $pin     = $hash->{PIN};
 	return 0 unless ( defined $firmata and defined $pin );
 	$hash->{ALARMDEVS} = undef;			
-	$firmata->onewire_search_alarms($hash->{PIN});
+	eval {
+		$firmata->onewire_search_alarms($hash->{PIN});
+	};
+	return 0 if ($@);
 	my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
 	for (my $i=0;$i<$times;$i++) {
 		if (main::FRM_poll($hash->{IODev})) {
@@ -170,6 +179,16 @@ sub Alarms() {
 }
 
 ########################################################################################
+#
+# Reopen - Reopen Device
+#
+########################################################################################
+
+sub Reopen () {
+  main::Log 1,"[OWX_FRM] Warning: ->Reopen currently not defined
+}
+
+########################################################################################
 # 
 # Init - Initialize the 1-wire device
 #
@@ -182,6 +201,12 @@ sub Alarms() {
 
 sub Init() {
   my ($self) = @_;
+  
+  if (defined($self->{OWX})) {
+    # class method called with parent hash instead of class hash as 1st parameter, fix
+    $self = $self->{OWX};
+  }
+  
   my $hash   = $self->{hash};
   my $dev    = $hash->{DeviceName};
   my $name   = $hash->{NAME};
@@ -193,24 +218,32 @@ sub Init() {
   
   my @args = ($pin);  
   $hash->{IODev} = $main::defs{$hash->{HWDEVICE}};
+  
+  #-- 10_FRM.pm is broken
+  #-- possible workaround
+  $main::attr{$name}{IODev} = $hash->{IODev}->{NAME};
   my $ret = main::FRM_Init_Pin_Client($hash,\@args,PIN_ONEWIRE);
+  
   if (defined $ret){
     $msg = "Error ".$ret;
     main::Log3 $name,1,"OWX_FRM::Init ".$msg;
     return $msg;
   }
   
-  my $firmata = main::FRM_Client_FirmataDevice($hash);
-		
-  $hash->{FRM_OWX_CORRELATIONID} = 0;
-  $firmata->observe_onewire($pin,\&observer,$hash);
-		
-  $hash->{FRM_OWX_REPLIES} = {};
-  $hash->{DEVS} = [];
-  if ( main::AttrVal($hash->{NAME},"buspower","") eq "parasitic" ) {
-	$firmata->onewire_config($pin,1);
-  }
-	 
+  eval {
+    my $firmata = main::FRM_Client_FirmataDevice($hash);
+    
+    $hash->{FRM_OWX_CORRELATIONID} = 0;
+    $firmata->observe_onewire($pin,\&observer,$hash);
+    
+    $hash->{FRM_OWX_REPLIES} = {};
+    $hash->{DEVS} = [];
+    if ( main::AttrVal($hash->{NAME},"buspower","") eq "parasitic" ) {
+      $firmata->onewire_config($pin,1);
+    }
+  };
+  return GP_Catch($@) if ($@);
+  
   $hash->{STATE}="Initialized";
   main::InternalTimer(main::gettimeofday()+10, "OWX_Discover", $hash,0);
   return undef;
@@ -264,7 +297,7 @@ sub Complex ($$$$) {
 	}
 	
 	#-- has receive part
-    if ( $numread > 0 ) {
+	if ( $numread > 0 ) {
 	  $ow_command->{"read"} = $numread;
 	  #Firmata sends 0-address on read after skip
 	  $owx_dev = '00.000000000000.00' unless defined $owx_dev;
@@ -278,15 +311,19 @@ sub Complex ($$$$) {
 		$hash->{FRM_OWX_CORRELATIONID} = ($id + 1) & 0xFFFF;
 	}
 	
-	$firmata->onewire_command_series( $pin, $ow_command );
-		
+	eval {
+		$firmata->onewire_command_series( $pin, $ow_command );
+	};
+	return 0 if ($@);
+	
+	my $oldResLength = length($res);
 	if ($numread) {
 	  my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
 	  for (my $i=0;$i<$times;$i++) {
 	    if (main::FRM_poll($hash->{IODev})) {
 	  	  if (defined $hash->{FRM_OWX_REPLIES}->{$owx_dev}) {
 		    $res .= $hash->{FRM_OWX_REPLIES}->{$owx_dev};
-		    main::OWX_WDBGL($name,5,"OWX_FRM::Complex receiving inside loop no. $i ",$res);
+		    main::OWX_WDBGL($name,5,"OWX_FRM::Complex receiving inside loop no. $i (" . (length($res) - $oldResLength) . " bytes received) ",$res);
 		    return $res;
 		  }
 		} else {
@@ -295,7 +332,7 @@ sub Complex ($$$$) {
 	  }
 	}
 
-	main::OWX_WDBGL($name,5,"OWX_FRM::Complex receiving outside loop ",$res);
+	main::OWX_WDBGL($name,5,"OWX_FRM::Complex receiving outside loop (" . (length($res) - $oldResLength) . " bytes received) ", $res);
 	return $res;
 }
 
@@ -325,10 +362,13 @@ sub Discover ($) {
 	return 0 unless ( defined $firmata and defined $pin );
 	my $old_devices = $hash->{DEVS};
 	$hash->{DEVS} = undef;			
-	my $res = $firmata->onewire_search($hash->{PIN});
-	#main::Log 1,"=============> result from search is $res, iodev is ".$hash->{IODev};
+	eval {
+		my $res = $firmata->onewire_search($hash->{PIN});
+		#main::Log 1,"=============> result from search is $res, iodev is ".$hash->{IODev};
+	};
+	return 0 if ($@);
 	my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
-    #main::Log 1,"===========> olddevices = $old_devices, tries =$times";
+	#main::Log 1,"===========> olddevices = $old_devices, tries =$times";
 	for (my $i=0;$i<$times;$i++) {
 		if (main::FRM_poll($hash->{IODev})) {
 			if (defined $hash->{DEVS}) {
@@ -343,6 +383,31 @@ sub Discover ($) {
 	return 1;
 }
 
+
+########################################################################################
+#
+# Open - Open Device
+#
+########################################################################################
+
+sub Open () {
+  my ($self) = @_;
+  my $hash = $self->{hash};
+}
+
+########################################################################################
+#
+# Close - Close Device
+#
+########################################################################################
+
+sub Close () {
+  my ($self) = @_;
+  my $hash = $self->{hash};
+ 
+}
+
+
 #######################################################################################
 #
 # Read - Implement the Read function
@@ -409,9 +474,11 @@ sub Reset() {
 	my $firmata = $frm->{FirmataDevice};
 	my $pin     = $hash->{PIN};
 	return undef unless ( defined $firmata and defined $pin );
-
-	$firmata->onewire_reset($pin);
-	
+	eval {
+		$firmata->onewire_reset($pin);
+	};
+	return 0 if ($@);
+  
 	return 1;
 }
 
@@ -537,8 +604,13 @@ sub Write(@) {
 		$hash->{FRM_OWX_CORRELATIONID} = ($id + 1) & 0xFFFF;
 	#}
 	
-	$firmata->onewire_command_series( $pin, $ow_command );
-	
+	eval {
+		$firmata->onewire_command_series( $pin, $ow_command );
+	};
+	if ($@) { 
+		main::Log3 $name,1,"OWX_FRM::Write device $name exception " . GP_Catch($@);
+		return 0 
+	}
 }
 
 #######################################################################################
@@ -630,11 +702,15 @@ sub firmata_to_device
 
 <a name="OWX_FRM"></a>
 <h3>OWX_FRM</h3>
+<ul>
 See <a href="/fhem/docs/commandref.html#OWX">OWX</a>
+</ul>
 =end html
 =begin html_DE
 
 <a name="OWX_FRM"></a>
 <h3>OWX_FRM</h3>
+<ul>
 <a href="http://fhemwiki.de/wiki/Interfaces_f%C3%BCr_1-Wire">Deutsche Dokumentation im Wiki</a> vorhanden, die englische Version gibt es hier: <a href="/fhem/docs/commandref.html#OWX">OWX</a> 
+</ul>
 =end html_DE

+ 53 - 10
fhem/core/FHEM/11_OWX_SER.pm

@@ -6,7 +6,7 @@
 #
 # Prof. Dr. Peter A. Henning
 #
-# $Id: 11_OWX_SER.pm 15392 2017-11-05 06:46:46Z phenning $
+# $Id: 11_OWX_SER.pm 16362 2018-03-09 17:18:43Z phenning $
 #
 ########################################################################################
 #
@@ -17,6 +17,9 @@
 # Alarms
 # Complex
 # Discover
+# Open
+# Close
+# Reopen
 # Init
 # Read
 # Ready
@@ -55,6 +58,8 @@ sub new($) {
 	return bless {
 		#-- OWX device
 		hash => $hash,
+		#-- module version
+	    version => "7.08",
 		#-- baud rate serial interface
 		baud => 9600,
 		#-- 16 byte search string
@@ -91,9 +96,7 @@ sub Define ($) {
     $hash->{DeviceName}   = $dev;
     $hash->{ASYNCHRONOUS} = 0;  
   
-    #-- module version
-	$hash->{version}      = "7.05";
-    main::Log3 $hash->{NAME},1,"OWX_SER::Define warning: version ".$hash->{version}." not identical to OWX version ".$main::owx_version
+    main::Log3 $hash->{NAME},1,"OWX_SER::Define warning: version ".$self->{version}." not identical to OWX version ".$main::owx_version
       if( $hash->{version} ne $main::owx_version);
       
     #-- call low level init function for the device
@@ -283,6 +286,47 @@ sub Discover () {
 }
 
 ########################################################################################
+#
+# Open - Open Device
+#
+########################################################################################
+
+sub Open () {
+  my ($self) = @_;
+  my $hash = $self->{hash};
+  #return main::DevIo_OpenDev($hash,1,"main::OWX_Init")
+  
+  return main::DevIo_OpenDev($hash,1,undef)
+}
+
+########################################################################################
+#
+# Close - Close Device
+#
+########################################################################################
+
+sub Close () {
+  my ($self) = @_;
+  my $hash = $self->{hash};
+  
+  return main::DevIo_CloseDev($hash);
+}
+
+########################################################################################
+#
+# Reopen - Reopen Device
+#
+########################################################################################
+
+sub Reopen () {
+  my ($self) = @_;
+  my $hash = $self->{hash};
+  
+  main::DevIo_CloseDev($hash);
+  return main::DevIo_OpenDev($hash,1,undef)
+}
+
+########################################################################################
 # 
 # Init - Initialize the 1-wire device
 #
@@ -297,12 +341,7 @@ sub Init() {
   my $name     = $hash->{NAME};
   
   main::Log3 $name,5,"OWX_SER::Init called on device $dev for bus $name, state is ".$hash->{STATE};
-  
-  #if($hash->{STATE} ne "opened"){
-  #XXX
-    #main::DevIo_CloseDev($hash);
-    main::DevIo_OpenDev($hash,0,undef);
-  #}
+  main::DevIo_OpenDev($hash,0,undef);
   my $hwdevice = $hash->{USBDev};
     
   if( !($hwdevice)){
@@ -876,11 +915,15 @@ sub SearchLow ($) {
 
 <a name="OWX_SER"></a>
 <h3>OWX_SER</h3>
+<ul>
 See <a href="/fhem/docs/commandref.html#OWX">OWX</a>
+</ul>
 =end html
 =begin html_DE
 
 <a name="OWX_SER"></a>
 <h3>OWX_SER</h3>
+<ul>
 <a href="http://fhemwiki.de/wiki/Interfaces_f%C3%BCr_1-Wire">Deutsche Dokumentation im Wiki</a> vorhanden, die englische Version gibt es hier: <a href="/fhem/docs/commandref.html#OWX">OWX</a> 
+</ul>
 =end html_DE

+ 50 - 4
fhem/core/FHEM/11_OWX_TCP.pm

@@ -7,7 +7,7 @@
 #
 # Prof. Dr. Peter A. Henning
 #
-# $Id: 11_OWX_TCP.pm 15392 2017-11-05 06:46:46Z phenning $
+# $Id: 11_OWX_TCP.pm 16362 2018-03-09 17:18:43Z phenning $
 #
 ########################################################################################
 #
@@ -18,6 +18,9 @@
 # Alarms
 # Complex
 # Discover
+# Open
+# Close
+# Reopen
 # Init
 # Read
 # Ready
@@ -56,6 +59,8 @@ sub new($) {
 	return bless {
 		#-- OWX device
 		hash => $hash,
+		#-- module version
+	    version => "7.08",
 		#-- 16 byte search string
 		search => [0,0,0,0 ,0,0,0,0, 0,0,0,0, 0,0,0,0],
 		ROM_ID => [0,0,0,0 ,0,0,0,0],
@@ -91,8 +96,6 @@ sub Define ($) {
     $hash->{DeviceName}   = $ip.":".$port;
     $hash->{ASYNCHRONOUS} = 0;
     
-    #-- module version
-	$hash->{version}      = "7.05";
     main::Log3 $hash->{NAME},1,"OWX_TCP::Define warning: version ".$hash->{version}." not identical to OWX version "..$main::owx_version
       if( $hash->{version} ne $main::owx_version );
       
@@ -280,6 +283,46 @@ sub Discover () {
 }
 
 ########################################################################################
+#
+# Open - Open Device
+#
+########################################################################################
+
+sub Open () {
+  my ($self) = @_;
+  my $hash = $self->{hash};
+  
+  return main::DevIo_OpenDev($hash,0,undef);
+}
+
+########################################################################################
+#
+# Close - Close Device
+#
+########################################################################################
+
+sub Close () {
+  my ($self) = @_;
+  my $hash = $self->{hash};
+  
+  return main::DevIo_CloseDev($hash);
+}
+
+########################################################################################
+#
+# Reopen - Reopen Device
+#
+########################################################################################
+
+sub Reopen () {
+  my ($self) = @_;
+  my $hash = $self->{hash};
+  
+  main::DevIo_CloseDev($hash);
+  return main::DevIo_OpenDev($hash,0,undef);
+}
+
+########################################################################################
 # 
 # Init - Implement the Init function. Only reopens the device
 #
@@ -294,7 +337,6 @@ sub Init() {
   my $name = $hash->{NAME};
   
   main::Log3 $name, 5,"OWX_TCP::Init called on device $dev for bus $name, state is ".$hash->{STATE};
-
   main::DevIo_OpenDev($hash,0,undef);
 
   return undef; 
@@ -843,11 +885,15 @@ sub SearchLow ($) {
 
 <a name="OWX_TCP"></a>
 <h3>OWX_TCP</h3>
+<ul>
 See <a href="/fhem/docs/commandref.html#OWX">OWX</a>
+</ul>
 =end html
 =begin html_DE
 
 <a name="OWX_TCP"></a>
 <h3>OWX_TCP</h3>
+<ul>
 <a href="http://fhemwiki.de/wiki/Interfaces_f%C3%BCr_1-Wire">Deutsche Dokumentation im Wiki</a> vorhanden, die englische Version gibt es hier: <a href="/fhem/docs/commandref.html#OWX">OWX</a> 
+</ul>
 =end html_DE

+ 4 - 3
fhem/core/FHEM/13_KS300.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 13_KS300.pm 14888 2017-08-13 12:07:12Z rudolfkoenig $
+# $Id: 13_KS300.pm 15627 2017-12-17 11:00:46Z rudolfkoenig $
 #
 # modified: 2014-02-16 - betateilchen
 #           - added new reading for windIndex (bft)
@@ -191,9 +191,10 @@ KS300_Parse($$)
     $haverain = 1 if($rain_raw_adj != $rain_raw_adj_prev);
 
     $v[1] = sprintf("%0.1f", $rain_raw_adj * $def->{RAINUNIT} / 1000);
-    $v[2] = sprintf("%0.1f", ("$a[25]$a[24].$a[23]"+0) * $def->{WINDUNIT});
+    $v[2] = sprintf("%0.1f", ("$a[25]$a[24].$a[23]"+(hex($a[17])&0x4?100:0)) *
+                              $def->{WINDUNIT});
     $v[3] = "$a[22]$a[21]" + 0;
-    $v[4] = "$a[20]$a[19].$a[18]" + 0; $v[4] = "-$v[4]" if($a[17] eq "7");
+    $v[4] = "$a[20]$a[19].$a[18]" + 0;
     $v[4] = sprintf("%0.1f", $v[4]);
     $v[5] = ((hex($a[17]) & 0x2) || $haverain) ? "yes" : "no";
     $v[6] = $a[29];

+ 185 - 145
fhem/core/FHEM/14_CUL_TCM97001.pm

@@ -31,7 +31,7 @@
 # Free Software Foundation, Inc., 
 # 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
 #
-# $Id: 14_CUL_TCM97001.pm 15367 2017-10-31 17:44:02Z bjoernh $
+# $Id: 14_CUL_TCM97001.pm 16274 2018-02-25 20:42:39Z bjoernh $
 #
 #
 # 14.06.2017 W155(TCM21...) wind/rain    pejonp
@@ -82,6 +82,7 @@ CUL_TCM97001_Initialize($)
   $hash->{ParseFn}   = "CUL_TCM97001_Parse";
   $hash->{AttrList}  = "IODev do_not_notify:1,0 ignore:0,1 showtime:1,0 " .
                         "$readingFnAttributes " .
+                        "max-deviation-temp:1,2,3,4,5,6,7,8,9,10,15,20,25,30,35,40,45,50 ".
                         "model:".join(",", sort keys %models);
 
   $hash->{AutoCreate}=
@@ -143,7 +144,7 @@ CUL_TCM97001_Undef($$)
 # n8 = ( 0x7 + n0 + n1 + n2 + n3 + n4 + n5 + n6 + n7 ) & 0xf
 sub checksum_W174 {
   my $msg = shift;
-  Log3 "CUL_TCM97001: ", 4 , "CUL_TCM97001: W174 checksum calc for: $msg";
+  Log3 "CUL_TCM97001 ", 4 , "CUL_TCM97001 W174 checksum calc for: $msg";
   my @a = split("", $msg);
   my $bitReverse = undef;
   my $x = undef;
@@ -155,7 +156,7 @@ sub checksum_W174 {
   my @aReverse = split("", $hexReverse);                      # Split reversed a again
   my $CRC = (7 + hex($aReverse[0])+hex($aReverse[1])+hex($aReverse[2])+hex($aReverse[3])+hex($aReverse[4])+hex($aReverse[5])+hex($aReverse[6])+hex($aReverse[7])) & 15;
   if ($CRC == hex($aReverse[8])) {
-      Log3 "CUL_TCM97001: ", 4 , "CUL_TCM97001: W174 checksum ok $CRC == ".hex($aReverse[8]);
+      Log3 "CUL_TCM97001 ", 4 , "CUL_TCM97001 W174 checksum ok $CRC == ".hex($aReverse[8]);
       return TRUE;
   } else {
       return FALSE;
@@ -194,17 +195,17 @@ sub checkCRC4 {
   my $msg = shift;
   my @a = split("", $msg);
   if(scalar(@a)<9){
-    Log3 "checkCRC4", 5, "CUL_TCM97001 failed for msg=($msg) length<9";
+    Log3 "checkCRC4", 5, "CUL_TCM97001 checkCRC4 failed for msg=($msg) length<9";
     return FALSE;
   }
   # xor nibbles 0 to 7 and compare to n8, if more nibble they might have been added to fill gap
   my $CRC = ( (hex($a[0])) ^ (hex($a[1])) ^ (hex($a[2])) ^ (hex($a[3])) ^ 
              (hex($a[4])) ^ (hex($a[5])) ^ (hex($a[6])) ^ (hex($a[7])) );
   if ($CRC ==  (hex($a[8]))) {
-    Log3 "checkCRC4", 5, "CUL_TCM97001 OK for msg=($msg)";
+    Log3 "checkCRC4", 5, "CUL_TCM97001 checkCRC4 OK for msg=($msg)";
     return TRUE;
   }
-  Log3 "checkCRC4", 5, "CUL_TCM97001 FAILED for msg=($msg)";
+  Log3 "checkCRC4", 5, "CUL_TCM97001 checkCRC4 FAILED for msg=($msg)";
   return FALSE;
 }
 #
@@ -216,16 +217,16 @@ sub isRain {
   my $msg = shift;
   my @a = split("", $msg);
   if(scalar(@a)<9){
-    Log3 "isRain", 5, "isRain: CUL_TCM97001 failed for msg=($msg) length<9";
+    Log3 "isRain", 5, "CUL_TCM97001 isRain failed for msg=($msg) length<9";
     return FALSE;
   }
   # if bit 0 of nibble 2 is 1 then this is no rain data
   my $isRainData = ( (hex($a[2]) & 1) );
   if ($isRainData == 1) {
-    Log3 "isRain", 5, "isRain: CUL_TCM97001 for msg=($msg) = FALSE";
+    Log3 "isRain", 5, "CUL_TCM97001 isRain for msg=($msg) = FALSE";
     return FALSE;
   }
-  Log3 "isRain", 5, "isRain: CUL_TCM97001 for msg=($msg) = TRUE";
+  Log3 "isRain", 5, "CUL_TCM97001 isRain for msg=($msg) = TRUE";
   return TRUE;
 }
 
@@ -234,7 +235,7 @@ sub isRain {
 #
 sub checkCRCKW9010 {
   my $msg = shift;
-  Log3 "CUL_TCM97001", 5 , "crc calc for: $msg";
+  Log3 "CUL_TCM97001", 5 , "CUL_TCM97001 checkCRCKW9010 crc calc for: $msg";
   my @a = split("", $msg);
   my $bitReverse = undef;
   my $x = undef;
@@ -249,8 +250,8 @@ sub checkCRCKW9010 {
 
   my $CRC = (hex($aReverse[0])+hex($aReverse[1])+hex($aReverse[2])+hex($aReverse[3])
             +hex($aReverse[4])+hex($aReverse[5])+hex($aReverse[6])+hex($aReverse[7])) & 15;
-  Log3 "CUL_TCM97001", 5 , "calc crc is: $CRC";
-  Log3 "CUL_TCM97001", 5 , "ref crc is :".hex($aReverse[8]);
+  Log3 "CUL_TCM97001", 5 , "CUL_TCM97001 checkCRCKW9010 calc crc is: $CRC";
+  Log3 "CUL_TCM97001", 5 , "CUL_TCM97001 checkCRCKW9010 ref crc is :".hex($aReverse[8]);
   if ($CRC == hex($aReverse[8])) {
       return TRUE;
   }
@@ -282,8 +283,6 @@ sub checkCRC_GTWT02 {
   my @a = split("", $msg);
   my $CRC = (hex($a[0])+hex($a[1])+hex($a[2])+hex($a[3])
             +hex($a[4])+hex($a[5])+hex($a[6])+(hex($a[7]) & 0xE));
-#  my $CRC = (hex($a[0])+hex($a[1])+hex($a[2])+hex($a[3])
-#            +hex($a[4])+hex($a[5])+hex($a[6])+hex($a[7])) -1;
   my $CRCCHECKVAL= (hex($a[7].$a[8].$a[9]) & 0x1F8) >> 3; 
   if ($CRC  % 64 == $CRCCHECKVAL) {
       return TRUE;
@@ -324,11 +323,11 @@ sub checkValues {
 sub
 CUL_TCM97001_Parse($$)
 {
-
   my $enableLongIDs = TRUE; # Disable short ID support, enable longIDs
   my ($hash, $msg) = @_;
   $msg = substr($msg, 1);
   my @a = split("", $msg);
+  my $iodev = $hash->{NAME};
 
   my $id3 = hex($a[0] . $a[1]);
   #my $id4 = hex($a[0] . $a[1] . $a[2] . (hex($a[3]) & 0x3));
@@ -368,9 +367,9 @@ CUL_TCM97001_Parse($$)
   {
 	$rssi = hex($rssi);
     $rssi = ($rssi>=128 ? (($rssi-256)/2-74) : ($rssi/2-74)) if defined($rssi);
-    Log3 $name, 4, "CUL_TCM97001 $name $id3 ($msg) length: $l RSSI: $rssi";
+    Log3 $name, 4, "$iodev: CUL_TCM97001 $name $id3 ($msg) length: $l RSSI: $rssi";
   } else {
-    Log3 $name, 4, "CUL_TCM97001 $name $id3 ($msg) length: $l"; 
+    Log3 $name, 4, "$iodev: CUL_TCM97001 $name $id3 ($msg) length: $l"; 
   }
 
   my ($msgtype, $msgtypeH);
@@ -405,7 +404,7 @@ CUL_TCM97001_Parse($$)
   
   
 
-  my $longids = AttrVal($hash->{NAME},'longids',1);
+  my $longids = AttrVal($iodev,'longids',1);
 
   if (length($msg) == 8) {
     # Only tmp TCM device
@@ -418,45 +417,53 @@ CUL_TCM97001_Parse($$)
     }
     $readedModel = AttrVal($name, "model", "Unknown");
     
-    if ($readedModel eq "Unknown" || $readedModel eq "TCM97...") {
-
-      $temp    = (hex($a[3].$a[4].$a[5]) >> 2) & 0xFFFF;  
-      my $negative    = (hex($a[2]) >> 0) & 0x3; 
+    if ($readedModel eq "Unknown" || $readedModel eq "ABS700") {
 
-      if ($negative == 0x3) {
-        $temp = (~$temp & 0x03FF) + 1;
+      $temp = (hex($a[2].$a[3]) & 0x7F)+(hex($a[5])/10);
+      if ((hex($a[2]) & 0x8) == 0x8) {
         $temp = -$temp;
       }
-
-      $temp = $temp / 10;
-
-
+      
+		# Sanity check temperature
+		if($def) {
+			my $timeSinceLastUpdate = ReadingsAge($iodev, "state", 0);
+			if ($timeSinceLastUpdate < 0) {
+				$timeSinceLastUpdate *= -1;
+				}
+				if (defined($hash->{READINGS}{temperature}{VAL})) {
+				my $diffTemp = 0;
+				my $oldTemp = $hash->{READINGS}{temperature}{VAL};
+				my $maxdeviation = AttrVal($name, "max-deviation-temp", 1);				# default 1 K
+				if ($temp > $oldTemp) {
+				$diffTemp = ($temp - $oldTemp);
+				} else {
+				$diffTemp = ($oldTemp - $temp);
+				}
+				$diffTemp = sprintf("%.1f", $diffTemp);				
+				Log3 $name, 4, "$iodev: $name old temp $oldTemp, age $timeSinceLastUpdate, new temp $temp, diff temp $diffTemp";
+				my $maxDiffTemp = $timeSinceLastUpdate / 60 + $maxdeviation; 			# maxdeviation + 1.0 Kelvin/Minute
+				$maxDiffTemp = sprintf("%.1f", $maxDiffTemp + 0.05);					# round 0.1
+				Log3 $name, 4, "$iodev:  $name max difference temperature $maxDiffTemp K";
+				if ($diffTemp > $maxDiffTemp) {
+				Log3 $name, 3, "$iodev:  $name ERROR - Temp diff too large (old $oldTemp, new $temp, diff $diffTemp)";
+				return "";
+				}
+			}
+		}
       if (checkValues($temp, 50)) {
-      	$model="TCM97...";
-         # I think bit 3 on byte 3 is battery warning
-      	$batbit    = (hex($a[2]) >> 0) & 0x4; 
-      	$batbit = ~$batbit & 0x1; # Bat bit umdrehen
-      	$mode    = (hex($a[5]) >> 0) & 0x1; 
-      	my $unknown    = (hex($a[4]) >> 0) & 0x2; 
-	    if ($mode) {
-    	  Log3 $name, 5, "CUL_TCM97001 Mode: manual triggert";
-      	} else {
-       	  Log3 $name, 5, "CUL_TCM97001 Mode: auto triggert";
-      	}
-     	if ($unknown) {
-          Log3 $name, 5, "CUL_TCM97001 Unknown Bit: $unknown";
-      	}
-        
+        $model="ABS700";
+        $batbit = ((hex($a[4]) & 0x8) != 0x8);
+        $mode = (hex($a[4]) & 0x4) >> 2;
+      
         my $deviceCode;
-        
         if (!defined($modules{CUL_TCM97001}{defptr}{$tcm97id}))
         {
             if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
           	{
 		         $deviceCode="CUL_TCM97001_".$tcm97id;
-		         Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
+		         Log3 $hash,4, "$iodev: CUL_TCM97001 using longid: $longids model: $model";
            	} else {
-		         $deviceCode="CUL_TCM97001_" . $model;
+		         $deviceCode="$iodev: CUL_TCM97001_" . $model;
            	}
         } else {
         	$deviceCode=$tcm97id;
@@ -465,40 +472,56 @@ CUL_TCM97001_Parse($$)
       	if($def) {
        	  $name = $def->{NAME};
       	} 
-      	
-      	
-      	
         if(!$def) {
-          Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
+          Log3 $name, 2, "$iodev: CUL_TCM97001 Unknown device $deviceCode, please define it";
           return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode"; 
-        }        
-        $packageOK = TRUE;
+        }
         $hasbatcheck = TRUE;
+        $packageOK = TRUE;
         $hasmode = TRUE;
+        
         $readedModel=$model;
       }
     }
-    if ($readedModel eq "Unknown" || $readedModel eq "ABS700") {
+    
+	if ($readedModel eq "Unknown" || $readedModel eq "TCM97...") {
 
-      $temp = (hex($a[2].$a[3]) & 0x7F)+(hex($a[5])/10);
-      if ((hex($a[2]) & 0x8) == 0x8) {
+      $temp    = (hex($a[3].$a[4].$a[5]) >> 2) & 0xFFFF;  
+      my $negative    = (hex($a[2]) >> 0) & 0x3; 
+
+      if ($negative == 0x3) {
+        $temp = (~$temp & 0x03FF) + 1;
         $temp = -$temp;
       }
-      
+
+      $temp = $temp / 10;
+
       if (checkValues($temp, 50)) {
-        $model="ABS700";
-        $batbit = ((hex($a[4]) & 0x8) != 0x8);
-        $mode = (hex($a[4]) & 0x4) >> 2;
-      
+      	$model="TCM97...";
+         # I think bit 3 on byte 3 is battery warning
+      	$batbit    = (hex($a[2]) >> 0) & 0x4; 
+      	$batbit = ~$batbit & 0x1; # Bat bit umdrehen
+      	$mode    = (hex($a[5]) >> 0) & 0x1; 
+      	my $unknown    = (hex($a[4]) >> 0) & 0x2; 
+	    if ($mode) {
+    	  Log3 $name, 5, "$iodev: CUL_TCM97001 Mode: manual triggert";
+      	} else {
+       	  Log3 $name, 5, "$iodev: CUL_TCM97001 Mode: auto triggert";
+      	}
+     	if ($unknown) {
+          Log3 $name, 5, "$iodev: CUL_TCM97001 Unknown Bit: $unknown";
+      	}
+        
         my $deviceCode;
+        
         if (!defined($modules{CUL_TCM97001}{defptr}{$tcm97id}))
         {
             if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
           	{
 		         $deviceCode="CUL_TCM97001_".$tcm97id;
-		         Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
+		         Log3 $hash,4, "$iodev: CUL_TCM97001 using longid: $longids model: $model";
            	} else {
-		         $deviceCode="CUL_TCM97001_" . $model;
+		         $deviceCode="$iodev: CUL_TCM97001_" . $model;
            	}
         } else {
         	$deviceCode=$tcm97id;
@@ -507,17 +530,19 @@ CUL_TCM97001_Parse($$)
       	if($def) {
        	  $name = $def->{NAME};
       	} 
+      	
+      	
+      	
         if(!$def) {
-          Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
+          Log3 $name, 2, "$iodev: CUL_TCM97001 Unknown device $deviceCode, please define it";
           return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode"; 
-        }
-        $hasbatcheck = TRUE;
+        }        
         $packageOK = TRUE;
+        $hasbatcheck = TRUE;
         $hasmode = TRUE;
-        
         $readedModel=$model;
       }
-    } 
+    }	
   } elsif (length($msg) == 10) {
   	#Log3 $name, 2, "CUL_TCM97001 10er msg: " . $msg;
     my $idType2 = hex($a[1] . $a[2]);
@@ -566,7 +591,7 @@ CUL_TCM97001_Parse($$)
 	          	if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
 	          	{
 			         $deviceCode="CUL_TCM97001_".$idType2;
-			         Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
+			         Log3 $hash,4, "$iodev: CUL_TCM97001 using longid: $longids model: $model";
 	           	} else {
 			         $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
 	           	}
@@ -579,7 +604,7 @@ CUL_TCM97001_Parse($$)
               $name = $def->{NAME};
             } 
             if(!$def) {
-                Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
+                Log3 $name, 2, "$iodev: CUL_TCM97001 Unknown device $deviceCode, please define it";
                 return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode"; 
             }
             $packageOK = TRUE;
@@ -588,7 +613,7 @@ CUL_TCM97001_Parse($$)
             $hasmode = TRUE;
             $hasbatcheck = TRUE;
             $haschannel = TRUE;
-            $id3 = $idType2
+            $id3 = $idType2;
         } else {
             $name = "Unknown";
         }
@@ -666,7 +691,7 @@ CUL_TCM97001_Parse($$)
 			} 
 					
 			if(!$def) {
-			  Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
+			  Log3 $name, 2, "$iodev: CUL_TCM97001 Unknown device $deviceCode, please define it";
 			  return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode"; 
 			}
 
@@ -707,7 +732,7 @@ CUL_TCM97001_Parse($$)
     #  $name = $def->{NAME};
     }
     $readedModel = AttrVal($name, "model", "Unknown");
-    Log3 $name, 4, "CUL_TCM97001 Parse Name: $name , devicecode: $deviceCode , Model defined: $readedModel";
+    Log3 $name, 4, "$iodev: CUL_TCM97001 Parse Name: $name , devicecode: $deviceCode , Model defined: $readedModel";
     
     if (($readedModel eq "Eurochron" || (hex($a[6]) == 0xF && $readedModel eq "Unknown") && $syncBit[1] < 5000)) {
       # EAS 800 
@@ -736,7 +761,7 @@ CUL_TCM97001_Parse($$)
 		my $dmsg = "P7#" . substr($msg, 0, $l-2, 2);
 		$hash->{RAWMSG} = $msg;
 		my %addvals = (RAWMSG => $msg, DMSG => $dmsg);
-		Log3 $name, 5, "CUL_TCM97001 Dispatch $dmsg to other modul";
+		Log3 $name, 5, "$iodev: CUL_TCM97001 Dispatch $dmsg to other modul";
 		Dispatch($hash, $dmsg, \%addvals);  ## Dispatch to other Modules 
 		return "";
       }
@@ -763,7 +788,7 @@ CUL_TCM97001_Parse($$)
      	{	
      	    if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
           	{
-		         Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
+		         Log3 $hash,4, "$iodev: CUL_TCM97001 using longid: $longids model: $model";
            	} else {
 		         $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
            	}
@@ -774,7 +799,7 @@ CUL_TCM97001_Parse($$)
           $name = $def->{NAME};
         }         
         if(!$def) {
-            Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
+            Log3 $name, 2, "$iodev: CUL_TCM97001 Unknown device $deviceCode, please define it";
             return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode"; 
         }
         if (defined($humidity)) {
@@ -821,18 +846,18 @@ CUL_TCM97001_Parse($$)
          }
          my $hexReverse = unpack("H*", pack ("B*", $bitReverse));
          my @aReverse = split("", $hexReverse);							# Split reversed a again
-         Log3 $hash,5, "CUL_TCM97001: $name original-msg: $msg , reversed nibbles: $hexReverse";
-         Log3 $hash,5, "CUL_TCM97001: $name nibble 2: $aReverse[2] , nibble 3: $aReverse[3]";
+         Log3 $hash,5, "$iodev: CUL_TCM97001 $name original-msg: $msg , reversed nibbles: $hexReverse";
+         Log3 $hash,5, "$iodev: CUL_TCM97001 $name nibble 2: $aReverse[2] , nibble 3: $aReverse[3]";
          # Nibble 2 must be x110 for rain gauge 
          # Nibble 3 must be 0x03 for rain gauge
          if ((hex($aReverse[2]) >> 1) == 3 && $aReverse[3] == 0x03) {
-            Log3 $hash,4, "CUL_TCM97001: $name detected rain gauge message ok";
+            Log3 $hash,4, "$iodev: CUL_TCM97001 $name detected rain gauge message ok";
             $batbit = $aReverse[2] & 0b0001;									# Bat bit normal=0, low=1
-            Log3 $hash,4, "CUL_TCM97001: $name battery bit: $batbit";
+            Log3 $hash,4, "$iodev: CUL_TCM97001 $name battery bit: $batbit";
             $batbit = ~$batbit & 0x1; 												# Bat bit negation
             $hasbatcheck = TRUE;
             my $rainticks = hex($aReverse[4]) + hex($aReverse[5]) * 16 + hex($aReverse[6]) * 256 + hex($aReverse[7]) * 4096;
-            Log3 $hash,5, "CUL_TCM97001: $name rain gauge swing count: $rainticks";
+            Log3 $hash,5, "$iodev: CUL_TCM97001 $name rain gauge swing count: $rainticks";
             $rain = ($rainticks + ($rainticks & 1)) / 4;			# 1 tick = 0,5 l/qm
             $model="W174";
             $hasrain = TRUE;
@@ -840,7 +865,10 @@ CUL_TCM97001_Parse($$)
             if($def) {
 			   $def = $modules{CUL_TCM97001}{defptr}{$deviceCode};
 			   my $hash = $def;
-               my $timeSinceLastUpdate = ReadingsAge($hash->{NAME}, "state", 0);
+               my $timeSinceLastUpdate = ReadingsAge($iodev, "state", 0);
+			   if ($timeSinceLastUpdate < 0) {
+					$timeSinceLastUpdate *= -1;
+					}
                $name = $def->{NAME};
                if (defined($hash->{READINGS}{rain}{VAL})) {
                   my $diffRain = 0;
@@ -851,20 +879,20 @@ CUL_TCM97001_Parse($$)
                      $diffRain = ($oldRain - $rain);
                   }
                   $diffRain = sprintf("%.1f", $diffRain);				
-                  Log3 $hash, 4, "CUL_TCM97001: $name old rain $oldRain, age $timeSinceLastUpdate, new rain $rain, diff rain $diffRain";
+                  Log3 $hash, 4, "$iodev: CUL_TCM97001 $name old rain $oldRain, age $timeSinceLastUpdate, new rain $rain, diff rain $diffRain";
                   my $maxDiffRain = $timeSinceLastUpdate / 60 + 1; 							# 1.0 Liter/Minute + 1.0 
                   $maxDiffRain = sprintf("%.1f", $maxDiffRain + 0.05);						# round 0.1
-                  Log3 $hash, 4, "CUL_TCM97001: $name max difference rain $maxDiffRain l";
+                  Log3 $hash, 4, "$iodev: CUL_TCM97001 $name max difference rain $maxDiffRain l";
                   if ($diffRain > $maxDiffRain) {
-                     Log3 $hash, 3, "CUL_TCM97001: $name ERROR - Rain diff too large (old $oldRain, new $rain, diff $diffRain)";
+                     Log3 $hash, 3, "$iodev: CUL_TCM97001 $name ERROR - Rain diff too large (old $oldRain, new $rain, diff $diffRain)";
                      return "";
                   }
                }
             } else {
-               Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
+               Log3 $name, 2, "$iodev: CUL_TCM97001 Unknown device $deviceCode, please define it";
                return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode"; 
             }
-            Log3 $hash,4, "CUL_TCM97001: $name rain total: $rain l/qm";
+            Log3 $hash,4, "$iodev: CUL_TCM97001 $name rain total: $rain l/qm";
             $readedModel=$model;
 				$packageOK = TRUE;
 			}
@@ -941,19 +969,19 @@ CUL_TCM97001_Parse($$)
 
         #Split reversed a again
         my @aReverse = split("", $hexReverse);
-        Log3 $hash,4, "CUL_TCM97001 hex:$hexReverse  msg:$msg aR:@aReverse ";
+        Log3 $hash,4, "$iodev: CUL_TCM97001 hex:$hexReverse  msg:$msg aR:@aReverse ";
         
         #  Message type (xyyz = temp/humidity if yy <> '11') else wind/rain sensor
         #  Message type (xyyx = NON temp/humidity data if yy = '11')
         $msgtype = substr(sprintf( "%04b", hex( substr( $msg,2,1))),1,2);
-        Log3 $hash,4, "CUL_TCM97001 nib2:$msgtype aRev: $aReverse[2]";
+        Log3 $hash,4, "$iodev: CUL_TCM97001 nib2:$msgtype aRev: $aReverse[2]";
         
         $msgtype = substr(sprintf( "%04b", hex( $aReverse[2])),1,2);
-        Log3 $hash,4, "CUL_TCM97001 nib2R:$msgtype hexR: $hexReverse";
+        Log3 $hash,4, "$iodev: CUL_TCM97001 nib2R:$msgtype hexR: $hexReverse";
 
 
         if ( $msgtype ne "11") {
-        Log3 $hash,4, "CUL_TCM97001 Temp/Hum Msgype: $msgtype nib3:$aReverse[3] ";
+        Log3 $hash,4, "$iodev: CUL_TCM97001 Temp/Hum Msgype: $msgtype nib3:$aReverse[3] ";
 
         if (hex($aReverse[5]) > 3) {
            # negative temp
@@ -970,12 +998,12 @@ CUL_TCM97001_Parse($$)
          
         } else {
         # Wind/Rain/Guest
-        Log3 $hash,4, "CUL_TCM97001 Wind/Rain/Guest Msgype: $msgtype nib3:$aReverse[3] ";
+        Log3 $hash,4, "$iodev: CUL_TCM97001 Wind/Rain/Guest Msgype: $msgtype nib3:$aReverse[3] ";
           #   C = Fixed to 1000 0000 0000  Reverse 0001 0000 0000
           if ((hex($aReverse[3])== 0x1) && (hex($aReverse[4])== 0x0)) { # Windspeed
               $windSpeed = hex($aReverse[6]) + hex($aReverse[7]);
               $haswindspeed = TRUE;
-              Log3 $hash,4, "CUL_TCM97001 windSpeed: $windSpeed ";
+              Log3 $hash,4, "$iodev: CUL_TCM97001 windSpeed: $windSpeed ";
              }
              
           if ((hex($aReverse[3])== 0xF)) { # Windguest    Reverse 
@@ -983,18 +1011,18 @@ CUL_TCM97001_Parse($$)
               $windDirection =  hex($aReverse[4]) + hex($aReverse[5]) ;
               $windDirectionText = $winddir_name[$windDirection];
               $haswind = TRUE;
-              Log3 $hash,4, "CUL_TCM97001 windGuest: $windGuest ";
+              Log3 $hash,4, "$iodev: CUL_TCM97001 windGuest: $windGuest ";
              }
           if ((hex($aReverse[3])== 0x3)) { # Rain
               $rain = (hex($aReverse[4]) + hex($aReverse[5]) + hex($aReverse[6]) + hex($aReverse[7])) * 0.25;
               $hasrain = TRUE;
-              Log3 $hash,4, "CUL_TCM97001 rain: $rain ";
+              Log3 $hash,4, "$iodev: CUL_TCM97001 rain: $rain ";
              }      
         }
 
        
-        #if (checkValues($temp, $humidity)) {
-        if (1) {
+        ### edited by @HomeAutoUser
+		if (checkValues($temp, $humidity) == TRUE) {				# unplausibel Werte sonst teilweise
             $batbit = (hex($a[2]) & 0x8) >> 3;
             #$mode = (hex($a[2]) & 0x4) >> 2;
             $model="TCM21....";
@@ -1002,9 +1030,9 @@ CUL_TCM97001_Parse($$)
          	{	
          	    if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
               	{
-		             Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
+		             Log3 $hash,4, "$iodev: CUL_TCM97001 using longid: $longids model: $model";
                	} else {
-		             $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
+		             $deviceCode="$iodev: CUL_TCM97001_" . $model . "_" . $channel;
                	}
          	}     
           
@@ -1013,7 +1041,7 @@ CUL_TCM97001_Parse($$)
               $name = $def->{NAME};
             }         
             if(!$def) {
-                Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
+                Log3 $name, 2, "$iodev: CUL_TCM97001 Unknown device $deviceCode, please define it";
                 return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode"; 
             }
             $packageOK = TRUE;
@@ -1074,9 +1102,9 @@ CUL_TCM97001_Parse($$)
      	{	
 	      	if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
           	{
-	             Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
+	             Log3 $hash,4, "$iodev: CUL_TCM97001 using longid: $longids model: $model";
            	} else {
-	             $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
+	             $deviceCode="$iodev: CUL_TCM97001_" . $model . "_" . $channel;
            	}
      	}     
       
@@ -1085,7 +1113,7 @@ CUL_TCM97001_Parse($$)
           $name = $def->{NAME};
         }         
         if(!$def) {
-            Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
+            Log3 $name, 2, "$iodev: CUL_TCM97001 Unknown device $deviceCode, please define it";
             return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode"; 
         }
         $hashumidity = TRUE;
@@ -1142,7 +1170,7 @@ CUL_TCM97001_Parse($$)
          	{	
 		      	if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
               	{
-		             Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
+		             Log3 $hash,4, "$iodev: CUL_TCM97001 using longid: $longids model: $model";
                	} else {
 		             $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
                	}
@@ -1153,7 +1181,7 @@ CUL_TCM97001_Parse($$)
               $name = $def->{NAME};
             }         
             if(!$def) {
-                Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
+                Log3 $name, 2, "$iodev: CUL_TCM97001 Unknown device $deviceCode, please define it";
                 return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode"; 
             }
             if (defined($humidity)) {
@@ -1210,7 +1238,7 @@ CUL_TCM97001_Parse($$)
      	{	
 		  	if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
           	{
-	             Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
+	             Log3 $hash,4, "$iodev: CUL_TCM97001 using longid: $longids model: $model";
            	} else {
 	             $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
            	}
@@ -1222,7 +1250,7 @@ CUL_TCM97001_Parse($$)
       	} 
       	      	
         if(!$def) {
-          Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
+          Log3 $name, 2, "$iodev: CUL_TCM97001 Unknown device $deviceCode, please define it";
           return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode"; 
         }
         $hashumidity = TRUE;
@@ -1264,7 +1292,7 @@ CUL_TCM97001_Parse($$)
      	{	
 		  	if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
           	{
-	             Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
+	             Log3 $hash,4, "$iodev: CUL_TCM97001 using longid: $longids model: $model";
            	} else {
 	             $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
            	}
@@ -1276,7 +1304,7 @@ CUL_TCM97001_Parse($$)
       	} 
       	      	
         if(!$def) {
-          Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
+          Log3 $name, 2, "$iodev: CUL_TCM97001 Unknown device $deviceCode, please define it";
           return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode"; 
         }
 
@@ -1313,13 +1341,13 @@ CUL_TCM97001_Parse($$)
         $temp = -$temp;
       }
       $temp = $temp / 10;
-      Log3 $name, 5, "CUL_TCM97001: PFR_130 Temp=$temp";
+      Log3 $name, 5, "$iodev: CUL_TCM97001 PFR_130 Temp=$temp";
         
       # rain values Pollin PFR_130      
       $rainticks = (hex($a[2].$a[6].$a[7])) & 0x3FF; #mask n2 n6 n7 for rain ticks
-      Log3 $name, 5, "CUL_TCM97001: PFR_130 rainticks=$rainticks";
+      Log3 $name, 5, "$iodev: CUL_TCM97001 PFR_130 rainticks=$rainticks";
       $rainMM = $rainticks / 25 * .5; # rain height in mm/qm, verified against sensor receiver display
-      Log3 $name, 5, "CUL_TCM97001: PFR_130 rain mm=$rainMM";
+      Log3 $name, 5, "$iodev: CUL_TCM97001 PFR_130 rain mm=$rainMM";
       
       if (checkValues($temp, 50)) {
         $batbit = (hex($a[2]) & 0x8) >> 3; # in auriol_protocol_v20.pdf bat bit is n2 & 0x08, same
@@ -1335,7 +1363,7 @@ CUL_TCM97001_Parse($$)
           if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
           	{
 	             $deviceCode="CUL_TCM97001_".$idType1;
-	             Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
+	             Log3 $hash,4, "$iodev: CUL_TCM97001 using longid: $longids model: $model";
            	} else {
 	             $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
            	}
@@ -1349,7 +1377,7 @@ CUL_TCM97001_Parse($$)
       	} 
       	      	
         if(!$def) {
-          Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
+          Log3 $name, 2, "$iodev: CUL_TCM97001 Unknown device $deviceCode, please define it";
           return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode"; 
         }
 
@@ -1403,7 +1431,7 @@ CUL_TCM97001_Parse($$)
 		  	if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
           	{
 	             $deviceCode="CUL_TCM97001_".$idType1;
-	             Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
+	             Log3 $hash,4, "$iodev: CUL_TCM97001 using longid: $longids model: $model";
            	} else {
 	             $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
            	}
@@ -1417,7 +1445,7 @@ CUL_TCM97001_Parse($$)
       	} 
       	      	
         if(!$def) {
-          Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
+          Log3 $name, 2, "$iodev: CUL_TCM97001 Unknown device $deviceCode, please define it";
           return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode"; 
         }
 
@@ -1461,10 +1489,10 @@ CUL_TCM97001_Parse($$)
         foreach $x (@a) {
 		    $bitReverse = $bitReverse . reverse(sprintf("%04b",hex($x))); 
         }
-        Log3 $hash, 5 , "KW9010 CRC Matched: ($bitReverse)";
+        Log3 $hash, 5 , "$iodev: KW9010 CRC Matched: ($bitReverse)";
         
         my $hexReverse = unpack("H*", pack ("B*", $bitReverse));
-        Log3 $hash, 5 , "KW9010 CRC Hex Matched: $hexReverse";
+        Log3 $hash, 5 , "$iodev: KW9010 CRC Hex Matched: $hexReverse";
 
         #Split reversed a again
         my @aReverse = split("", $hexReverse);
@@ -1480,10 +1508,10 @@ CUL_TCM97001_Parse($$)
         }
         $humidity = hex($aReverse[7].$aReverse[6]) - 156;
         
+		### edited by @HomeAutoUser				
+		if (checkValues($temp, $humidity) == 1) {				# unplausibel Werte sonst teilweise
 
-        #if (checkValues($temp, $humidity)) {
-        if (1) {
-            Log3 $hash, 5 , "KW9010 values are matching";
+            Log3 $hash, 5 , "$iodev: KW9010 values are matching";
             
             $batbit = (hex($a[2]) & 0x8) >> 3;
             #$mode = (hex($a[2]) & 0x4) >> 2; 
@@ -1497,7 +1525,7 @@ CUL_TCM97001_Parse($$)
          	{	
 		      	if ( $enableLongIDs == TRUE || (($longids != "0") && ($longids eq "1" || $longids eq "ALL" || (",$longids," =~ m/,$model,/))))
               	{
-		             Log3 $hash,4, "CUL_TCM97001 using longid: $longids model: $model";
+		             Log3 $hash,4, "$iodev: CUL_TCM97001 using longid: $longids model: $model";
                	} else {
 		             $deviceCode="CUL_TCM97001_" . $model . "_" . $channel;
                	}
@@ -1508,7 +1536,7 @@ CUL_TCM97001_Parse($$)
               $name = $def->{NAME};
             }         
             if(!$def) {
-                Log3 $name, 2, "CUL_TCM97001 Unknown device $deviceCode, please define it";
+                Log3 $name, 2, "$iodev: CUL_TCM97001 Unknown device $deviceCode, please define it";
                 return "UNDEFINED $model" . substr($deviceCode, rindex($deviceCode,"_")) . " CUL_TCM97001 $deviceCode"; 
             }
             $hashumidity = TRUE;    
@@ -1519,7 +1547,7 @@ CUL_TCM97001_Parse($$)
             
             $readedModel=$model;
         } else {
-       		Log3 $hash, 5 , "KW9010 t:$temp / h:$humidity mismatch";
+       		Log3 $hash, 5 , "$iodev: KW9010 t:$temp / h:$humidity mismatch";
        		
             $name = "Unknown";
         }
@@ -1527,6 +1555,10 @@ CUL_TCM97001_Parse($$)
      
   }
   
+  	# Ignoriere dieses Gerät. Das Gerät wird keine FileLogs/notifys triggern, empfangene Befehle
+	# werden stillschweigend ignoriert. Das Gerät wird weder in der Device-List angezeigt,
+	# noch wird es in Befehlen mit "Wildcard"-Namenspezifikation (siehe devspec) erscheinen.
+	return "" if(IsIgnored($name));	# wenn Attribut "ignore" gesetzt ist, werden alle Ausgaben ignoriert																								 
   
   if ($packageOK == TRUE) {
     # save lastT, calc rainMM sum for day and hour
@@ -1567,7 +1599,7 @@ CUL_TCM97001_Parse($$)
       $msgtype = "temperature";
       $val = sprintf("%2.1f", ($temp) );
       $state="T: $val";
-      Log3 $name, 5, "CUL_TCM97001 1. $lastDay : $lastHour : $rainSumDay : $rainSumHour";
+      Log3 $name, 5, "$iodev: CUL_TCM97001 1. $lastDay : $lastHour : $rainSumDay : $rainSumHour";
       #rain Pollin PFR-130
       if($mday==$lastDay){
          #same day add rainMM
@@ -1587,9 +1619,9 @@ CUL_TCM97001_Parse($$)
       readingsBulkUpdate($def, "RainD", $rainSumDay );
       readingsBulkUpdate($def, "RainH", $rainSumHour );
       
-      Log3 $name, 5, "CUL_TCM97001 2. $lastDay : $lastHour : $rainSumDay : $rainSumHour";
+      Log3 $name, 5, "$iodev: CUL_TCM97001 2. $lastDay : $lastHour : $rainSumDay : $rainSumHour";
       $state="$state RainH: $rainSumHour RainD: $rainSumDay R: $rainticks Rmm: $rainMM";
-      Log3 $name, 5, "CUL_TCM97001 $msgtype $name $id3 state: $state"; 
+      Log3 $name, 5, "$iodev: CUL_TCM97001 $msgtype $name $id3 state: $state"; 
 
    } else {
 		### edited by elektron-bbs
@@ -1649,10 +1681,12 @@ CUL_TCM97001_Parse($$)
       $msgtypeH = "humidity";
       $valH = $humidity;
       $state="$state H: $valH";
-      Log3 $name, 4, "CUL_TCM97001 $msgtype $name $id3 T: $val H: $valH"; 
+      Log3 $name, 4, "$iodev: CUL_TCM97001 $msgtype $name $id3 T: $val H: $valH"; 
     } else {
-      $msgtype = "other";
-      Log3 $name, 4, "CUL_TCM97001 $msgtype $name $id3";
+      if ($model eq "W174") {
+        $msgtype = "other";
+      }
+      Log3 $name, 4, "$iodev: CUL_TCM97001 $msgtype $name $id3";
       #Log3 $name, 4, "CUL_TCM97001 $msgtype $name $id3 ";
     }
 
@@ -1719,11 +1753,11 @@ CUL_TCM97001_Parse($$)
     my $defUnknown = $modules{CUL_TCM97001}{defptr}{"CUL_TCM97001_Unknown"};
     
     if (!$defUnknown) {
-      Log3 "Unknown", 2, "CUL_TCM97001 Unknown device Unknown, please define it";
+      Log3 "Unknown", 2, "$iodev: CUL_TCM97001 Unknown device Unknown, please define it";
       return "UNDEFINED Unknown CUL_TCM97001 CUL_TCM97001_Unknown"; 
     } 
     $name = $defUnknown->{NAME};
-    Log3 $name, 4, "CUL_TCM97001 Device not implemented yet name Unknown msg $msg";
+    Log3 $name, 4, "$iodev: CUL_TCM97001 Device not implemented yet name Unknown msg $msg";
 
       my $rawlen = length($msg);
       my $rawVal = substr($msg, 0, $rawlen-2);
@@ -1773,16 +1807,19 @@ CUL_TCM97001_Parse($$)
   <br>
   <b>Supported models:</b>
   <ul>
-    <li>TCM97...</li>
     <li>ABS700</li>
-    <li>TCM21....</li>
-    <li>Prologue</li>
-    <li>Rubicson</li>
-    <li>NC_WS</li>
-    <li>GT_WT_02</li>
     <li>AURIOL</li>
     <li>Eurochron</li>
+	<li>GT_WT_02</li>
     <li>KW9010</li>
+	<li>NC_WS</li>
+	<li>TCM21....</li>
+    <li>TCM97...</li>
+	<li>PFR-130 (rain)</li>
+	<li>Prologue</li>
+    <li>Rubicson</li>
+    <li>W155 (wind/rain)</li>
+    <li>W174 (rain)</li>
   </ul>
   <br>
   New received device packages are add in fhem category CUL_TCM97001 with autocreate.
@@ -1814,7 +1851,8 @@ CUL_TCM97001_Parse($$)
       </li>
     <li><a href="#do_not_notify">do_not_notify</a></li>
     <li><a href="#ignore">ignore</a></li>
-    <li><a href="#model">model</a> (TCM97..., ABS700, TCM21...., Prologue, Rubicson, NC_WS, GT_WT_02, AURIOL, KW9010, Unknown)</li>
+    <li><a href="#model">model</a> (ABS700, AURIOL, GT_WT_02, KW9010, NC_WS, PFR-130, Prologue, Rubicson, TCM21...., TCM97…, Unknown, W174)</li>
+    <li>max-deviation-temp: (default:1, allowed values: 1,2,3,4,5,6,7,8,9,10,15,20,25,30,35,40,45,50)</li>																			
     <li><a href="#showtime">showtime</a></li>
     <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
   </ul>
@@ -1833,18 +1871,18 @@ CUL_TCM97001_Parse($$)
   <br>
   <b>Unterstütze Modelle:</b>
   <ul>
-    <li>TCM97...</li>
     <li>ABS700</li>
-    <li>TCM21....</li>
-    <li>Prologue</li>
-    <li>Rubicson</li>
-    <li>NC_WS</li>
-    <li>GT_WT_02</li>
     <li>AURIOL</li>
     <li>Eurochron</li>
+	<li>GT_WT_02</li>
     <li>KW9010</li>
+	<li>NC_WS</li>
+	<li>TCM21....</li>
+    <li>TCM97...</li>
+	<li>PFR-130 (rain)</li>
+	<li>Prologue</li>
+    <li>Rubicson</li>
     <li>W155 (wind/rain)</li>
-    <li>PFR-130 (rain)</li>
     <li>W174 (rain)</li>
   </ul>
   <br>
@@ -1878,6 +1916,8 @@ CUL_TCM97001_Parse($$)
     <li><a href="#do_not_notify">do_not_notify</a></li>
     <li><a href="#ignore">ignore</a></li>
     <li><a href="#model">model</a> (ABS700, AURIOL, GT_WT_02, KW9010, NC_WS, PFR-130, Prologue, Rubicson, TCM21...., TCM97…, Unknown, W174)</li>
+    <li>max-deviation-temp: (Default:1, erlaubte Werte: 1,2,3,4,5,6,7,8,9,10,15,20,25,30,35,40,45,50)<br>
+		Maximal erlaubte Abweichung der gemessenen Temperatur zum vorhergehenden Wert in Kelvin.<br></li>																										 																										 
     <li><a href="#showtime">showtime</a></li>
     <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
   </ul>

+ 18 - 9
fhem/core/FHEM/14_CUL_WS.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 14_CUL_WS.pm 14888 2017-08-13 12:07:12Z rudolfkoenig $
+# $Id: 14_CUL_WS.pm 15603 2017-12-13 20:53:47Z rudolfkoenig $
 package main;
 
 use strict;
@@ -291,23 +291,32 @@ CUL_WS_Parse($$)
   Log3 $name, 4, "CUL_WS $devtype $name: $val";
 
   # Sanity checks
-  if($NotifyTemperature &&
-     $hash->{READINGS}{temperature} &&
-     $hash->{READINGS}{temperature}{VAL}) {
-    my $tval = $hash->{READINGS}{strangetemp} ? 
-               $hash->{READINGS}{strangetemp}{VAL} : 
-               $hash->{READINGS}{temperature}{VAL};
+  if($NotifyTemperature && ReadingsVal($name, "temperature", undef)) {
+    my $tval = ReadingsVal($name, "strangetemp",
+                           ReadingsVal($name, "temperature", undef));
     my $diff = ($NotifyTemperature - $tval)+0;
     if($diff < -15.0 || $diff > 15.0) {
       Log3 $name, 2,
         "$name: Temp difference ($diff) too large: $val, skipping it";
-      $hash->{READINGS}{strangetemp}{VAL} = $NotifyTemperature;
-      $hash->{READINGS}{strangetemp}{TIME} = TimeNow();
+      readingsSingleUpdate($hash, "strangetemp", $NotifyTemperature, 0);
       return "";
     }
   }
   delete $hash->{READINGS}{strangetemp} if($hash->{READINGS});
 
+  if($NotifyPressure && ReadingsVal($name, "pressure", undef)) {
+    my $tval = ReadingsVal($name, "strangepress",
+                           ReadingsVal($name, "pressure", undef));
+    my $diff = ($NotifyPressure - $tval)+0;
+    if($diff < -10.0 || $diff > 10.0) {
+      Log3 $name, 2,
+        "$name: Pressure difference ($diff) too large: $val, skipping it";
+      readingsSingleUpdate($hash, "strangepress", $NotifyPressure, 0);
+      return "";
+    }
+  }
+  delete $hash->{READINGS}{strangepress} if($hash->{READINGS});
+
   if(defined($hum) && ($hum < 0 || $hum > 100)) {
     Log3 $name, 1, "BOGUS: $name reading: $val, skipping it";
     return "";

+ 2 - 2
fhem/core/FHEM/15_CUL_EM.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 15_CUL_EM.pm 14888 2017-08-13 12:07:12Z rudolfkoenig $
+# $Id: 15_CUL_EM.pm 16293 2018-02-28 21:33:57Z rudolfkoenig $
 package main;
 
 use strict;
@@ -443,7 +443,7 @@ CUL_EM_Parse($$)
     <li><a href="#eventMap">eventMap</a></li><br>
     <li><a href="#readingFnAttributes">readingFnAttributes</a></li><br>
     <li><a name="maxPeak">maxPeak</a> &lt;number&gt;<br>
-      Gibt den maximal m&ouml;glichen Spitzenwert f&uumlr das EM-Meter an 
+      Gibt den maximal m&ouml;glichen Spitzenwert f&uuml;r das EM-Meter an 
       ("TOP:"-Wert in Logdatei). Spitzenwerte gr&ouml;&szlig;er als dieser 
       Wert gelten als EM-Lesefehler und werden ignoriert.
       Wenn es z.B. nicht m&ouml;glich ist mehr zu 40kW Leistung 

+ 2 - 2
fhem/core/FHEM/18_CUL_HOERMANN.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 18_CUL_HOERMANN.pm 14888 2017-08-13 12:07:12Z rudolfkoenig $
+# $Id: 18_CUL_HOERMANN.pm 15510 2017-11-27 16:52:44Z rudolfkoenig $
 package main;
 
 use strict;
@@ -10,7 +10,7 @@ CUL_HOERMANN_Initialize($)
 {
   my ($hash) = @_;
 
-  $hash->{Match}     = "^R..........";
+  $hash->{Match}     = "^R..........\$";
   $hash->{DefFn}     = "CUL_HOERMANN_Define";
   $hash->{ParseFn}   = "CUL_HOERMANN_Parse";
   $hash->{SetFn}     = "CUL_HOERMANN_Set";

+ 104 - 37
fhem/core/FHEM/20_FRM_AD.pm

@@ -1,6 +1,37 @@
-##############################################
-# $Id: 20_FRM_AD.pm 5927 2014-05-21 21:56:37Z ntruchsess $
-##############################################
+########################################################################################
+#
+# $Id: 20_FRM_AD.pm 15932 2018-01-19 21:19:00Z jensb $
+#
+# FHEM module for one Firmata analog input pin
+#
+########################################################################################
+#
+#  LICENSE AND COPYRIGHT
+#
+#  Copyright (C) 2013 ntruchess
+#  Copyright (C) 2016 jensb
+#
+#  All rights reserved
+#
+#  This script is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  The GNU General Public License can be found at
+#  http://www.gnu.org/copyleft/gpl.html.
+#  A copy is found in the textfile GPL.txt and important notices to the license
+#  from the author is found in LICENSE.txt distributed with these scripts.
+#
+#  This script is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#  GNU General Public License for more details.
+#
+#  This copyright notice MUST APPEAR in all copies of the script!
+#
+########################################################################################
+
 package main;
 
 use strict;
@@ -48,8 +79,16 @@ FRM_AD_Init($$)
 	return $ret if (defined $ret);
 	my $firmata = $hash->{IODev}->{FirmataDevice};
 	my $name = $hash->{NAME};
-	$firmata->observe_analog($hash->{PIN},\&FRM_AD_observer,$hash);
-	$main::defs{$name}{resolution}=$firmata->{metadata}{analog_resolutions}{$hash->{PIN}} if (defined $firmata->{metadata}{analog_resolutions});
+	my $resolution = 10;
+	if (defined $firmata->{metadata}{analog_resolutions}) {
+		$resolution = $firmata->{metadata}{analog_resolutions}{$hash->{PIN}} 
+	}
+	$hash->{resolution} = $resolution;
+	$hash->{".max"} = defined $resolution ? (1<<$resolution)-1 : 1024;
+	eval {
+		$firmata->observe_analog($hash->{PIN},\&FRM_AD_observer,$hash);
+	};
+	return FRM_Catch($@) if $@;
 	if (! (defined AttrVal($name,"stateFormat",undef))) {
 		$main::attr{$name}{"stateFormat"} = "reading";
 	}
@@ -65,28 +104,28 @@ FRM_AD_observer
 {
 	my ($pin,$old,$new,$hash) = @_;
 	my $name = $hash->{NAME};
-	Log3 $name,6,"onAnalogMessage for pin ".$pin.", old: ".(defined $old ? $old : "--").", new: ".(defined $new ? $new : "--");
+	Log3 $name,5,"onAnalogMessage for pin ".$pin.", old: ".(defined $old ? $old : "--").", new: ".(defined $new ? $new : "--");
 	main::readingsBeginUpdate($hash);
 	main::readingsBulkUpdate($hash,"reading",$new,1);
 	my $upperthresholdalarm = ReadingsVal($name,"alarm-upper-threshold","off");
-    if ( $new < AttrVal($name,"upper-threshold",1024) ) {
-      if ( $upperthresholdalarm eq "on" ) {
-    	main::readingsBulkUpdate($hash,"alarm-upper-threshold","off",1);
-      }
-      my $lowerthresholdalarm = ReadingsVal($name,"alarm-lower-threshold","off"); 
-      if ( $new > AttrVal($name,"lower-threshold",-1) ) {
-        if ( $lowerthresholdalarm eq "on" ) {
-          main::readingsBulkUpdate($hash,"alarm-lower-threshold","off",1);
-        }
-      } else {
-      	if ( $lowerthresholdalarm eq "off" ) {
-          main::readingsBulkUpdate($hash,"alarm-lower-threshold","on",1);
-      	}
-      }
-    } else {
-      if ( $upperthresholdalarm eq "off" ) {
-    	main::readingsBulkUpdate($hash,"alarm-upper-threshold","on",1);
-      }
+	if ( $new < AttrVal($name,"upper-threshold",$hash->{".max"}) ) {
+		if ( $upperthresholdalarm eq "on" ) {
+			main::readingsBulkUpdate($hash,"alarm-upper-threshold","off",1);
+		}
+		my $lowerthresholdalarm = ReadingsVal($name,"alarm-lower-threshold","off"); 
+		if ( $new > AttrVal($name,"lower-threshold",-1) ) {
+			if ( $lowerthresholdalarm eq "on" ) {
+				main::readingsBulkUpdate($hash,"alarm-lower-threshold","off",1);
+			}
+		} else {
+			if ( $lowerthresholdalarm eq "off" ) {
+				main::readingsBulkUpdate($hash,"alarm-lower-threshold","on",1);
+			}
+		}
+	} else {
+		if ( $upperthresholdalarm eq "off" ) {
+			main::readingsBulkUpdate($hash,"alarm-upper-threshold","on",1);
+		}
 	};
 	main::readingsEndUpdate($hash,1);
 }
@@ -139,34 +178,53 @@ FRM_AD_Attr($$$$) {
 1;
 
 =pod
+
+  CHANGES
+
+  2016 jensb
+    o modified sub FRM_AD_Init to catch exceptions and return error message
+    
+  19.01.2018 jensb
+    o support analog resolution depending on device capability
+
+=cut
+
+=pod
+=item device
+=item summary Firmata: analog input
+=item summary_DE Firmata: analog Eingang
 =begin html
 
 <a name="FRM_AD"></a>
 <h3>FRM_AD</h3>
 <ul>
-  represents a pin of an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a>
-  configured for analog input.<br>
-  The value read is stored in reading 'state'. Range is from 0 to 1023 (10 Bit)<br>
-  Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br> 
+  This module represents a pin of a <a href="http://www.firmata.org">Firmata device</a> 
+  that should be configured as an analog input.<br><br>
+  
+  Requires a defined <a href="#FRM">FRM</a> device to work. The pin must be listed in the internal reading "<a href="#FRMinternals">analog_pins</a>"<br>
+  of the FRM device (after connecting to the Firmata device) to be used as analog input.<br><br> 
   
   <a name="FRM_ADdefine"></a>
   <b>Define</b>
   <ul>
-  <code>define &lt;name&gt; FRM_AD &lt;pin&gt;</code> <br>
-  Defines the FRM_AD device. &lt;pin&gt; is the arduino-pin to use.
-  </ul>
+      <code>define &lt;name&gt; FRM_AD &lt;pin&gt;</code><br><br>
+  
+      Defines the FRM_AD device. &lt;pin&gt; is the arduino-pin to use.
+  </ul><br>
   
-  <br>
   <a name="FRM_ADset"></a>
   <b>Set</b><br>
   <ul>
     N/A<br>
   </ul><br>
+  
   <a name="FRM_ADget"></a>
   <b>Get</b><br>
   <ul>
     <li>reading<br>
-    returns the voltage-level read on the arduino-pin. Values range from 0 to 1023.</li>
+    returns the voltage-level equivalent at the arduino-pin. The min value is zero and the max value<br>
+    depends on the Firmata device (see internal reading "<a href="#FRMinternals">analog_resolutions</a>" of the FRM device.<br>
+    For 10 bits resolution the range is 0 to 1023 (also see <a href="http://arduino.cc/en/Reference/AnalogRead">analogRead()</a> for details)<br></li>
     <li>alarm-upper-threshold<br>
     returns the current state of 'alarm-upper-threshold'. Values are 'on' and 'off' (Defaults to 'off')<br>
     'alarm-upper-threshold' turns 'on' whenever the 'reading' is higher than the attribute 'upper-threshold'<br>
@@ -178,13 +236,14 @@ FRM_AD_Attr($$$$) {
     <li>state<br>
     returns the 'state' reading</li>
   </ul><br>
+  
   <a name="FRM_ADattr"></a>
   <b>Attributes</b><br>
   <ul>
       <li>upper-threshold<br>
       sets the 'upper-threshold'. Whenever the 'reading' exceeds this value 'alarm-upper-threshold' is set to 'on'<br>
       As soon 'reading' falls below the 'upper-threshold' 'alarm-upper-threshold' turns 'off' again<br>
-      Defaults to 1024.</li>
+      Defaults to the max pin resolution plus one.</li>
       <li>lower-threshold<br>
       sets the 'lower-threshold'. Whenever the 'reading' falls below this value 'alarm-lower-threshold' is set to 'on'<br>
       As soon 'reading' rises above the 'lower-threshold' 'alarm-lower-threshold' turns 'off' again<br>
@@ -195,9 +254,17 @@ FRM_AD_Attr($$$$) {
       </li>
       <li><a href="#eventMap">eventMap</a><br></li>
       <li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
-    </ul>
-  </ul>
-<br>
+  </ul><br>
+  
+  <a name="FRM_ADnotes"></a>
+  <b>Notes</b><br>
+  <ul>
+      <li>attribute <i>stateFormat</i><br>
+      In most cases it is a good idea to assign "reading" to the attribute <i>stateFormat</i>. This will show the 
+      current value of the pin in the web interface.
+      </li>
+  </ul>  
+</ul><br>
 
 =end html
 =cut

+ 116 - 31
fhem/core/FHEM/20_FRM_IN.pm

@@ -1,12 +1,43 @@
-##############################################
-# $Id: 20_FRM_IN.pm 5927 2014-05-21 21:56:37Z ntruchsess $
-##############################################
+########################################################################################
+#
+# $Id: 20_FRM_IN.pm 16012 2018-01-27 20:11:34Z jensb $
+#
+# FHEM module for one Firmata digial input pin
+#
+########################################################################################
+#
+#  LICENSE AND COPYRIGHT
+#
+#  Copyright (C) 2013 ntruchess
+#  Copyright (C) 2018 jensb
+#
+#  All rights reserved
+#
+#  This script is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  The GNU General Public License can be found at
+#  http://www.gnu.org/copyleft/gpl.html.
+#  A copy is found in the textfile GPL.txt and important notices to the license
+#  from the author is found in LICENSE.txt distributed with these scripts.
+#
+#  This script is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#  GNU General Public License for more details.
+#
+#  This copyright notice MUST APPEAR in all copies of the script!
+#
+########################################################################################
+
 package main;
 
 use strict;
 use warnings;
 
-#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
+#add FHEM/lib to @INC if it's not already included. Should rather be in fhem.pl than here though...
 BEGIN {
 	if (!grep(/FHEM\/lib$/,@INC)) {
 		foreach my $inc (grep(/FHEM$/,@INC)) {
@@ -42,26 +73,48 @@ FRM_IN_Initialize($)
   $hash->{DefFn}     = "FRM_Client_Define";
   $hash->{InitFn}    = "FRM_IN_Init";
   $hash->{UndefFn}   = "FRM_Client_Undef";
-  
+
   $hash->{AttrList}  = "IODev count-mode:none,rising,falling,both count-threshold reset-on-threshold-reached:yes,no internal-pullup:on,off activeLow:yes,no $main::readingFnAttributes";
   main::LoadModule("FRM");
 }
 
 sub
+FRM_IN_PinModePullupSupported($)
+{
+  my ($hash) = @_;
+  my $iodev = $hash->{IODev};
+  my $pullupPins = defined($iodev)? $iodev->{pullup_pins} : undef;
+
+  return defined($pullupPins);
+}
+
+sub
 FRM_IN_Init($$)
 {
 	my ($hash,$args) = @_;
-	my $ret = FRM_Init_Pin_Client($hash,$args,PIN_INPUT);
-	return $ret if (defined $ret);
-	eval {
-      my $firmata = FRM_Client_FirmataDevice($hash);
-      my $pin = $hash->{PIN};
-      if (defined (my $pullup = AttrVal($hash->{NAME},"internal-pullup",undef))) {
-        $firmata->digital_write($pin,$pullup eq "on" ? 1 : 0);
-      }
-      $firmata->observe_digital($pin,\&FRM_IN_observer,$hash);
-	};
-	return FRM_Catch($@) if $@;
+	if (FRM_IN_PinModePullupSupported($hash)) {
+		my $pullup = AttrVal($hash->{NAME},"internal-pullup","off");
+		my $ret = FRM_Init_Pin_Client($hash,$args,defined($pullup) && ($pullup eq "on")? PIN_PULLUP : PIN_INPUT);
+		return $ret if (defined $ret);
+		eval {
+			my $firmata = FRM_Client_FirmataDevice($hash);
+			my $pin = $hash->{PIN};
+			$firmata->observe_digital($pin,\&FRM_IN_observer,$hash);
+		};
+		return FRM_Catch($@) if $@;
+	} else {
+		my $ret = FRM_Init_Pin_Client($hash,$args,PIN_INPUT);
+		return $ret if (defined $ret);
+		eval {
+			my $firmata = FRM_Client_FirmataDevice($hash);
+			my $pin = $hash->{PIN};
+			if (defined (my $pullup = AttrVal($hash->{NAME},"internal-pullup",undef))) {
+				$firmata->digital_write($pin,$pullup eq "on" ? 1 : 0);
+			}
+			$firmata->observe_digital($pin,\&FRM_IN_observer,$hash);
+		};
+		return FRM_Catch($@) if $@;
+	}
 	if (! (defined AttrVal($hash->{NAME},"stateFormat",undef))) {
 		$main::attr{$hash->{NAME}}{"stateFormat"} = "reading";
 	}
@@ -99,7 +152,7 @@ FRM_IN_observer
   	    		}
   	    	}
   	    	main::readingsBulkUpdate($hash,"count",$count,1);
-  	    } 
+  	    }
   	};
 	}
 	main::readingsBulkUpdate($hash,"reading",$new == PIN_HIGH ? "on" : "off", $changed);
@@ -171,7 +224,7 @@ FRM_IN_Attr($$$$) {
             main::readingsSingleUpdate($main::defs{$name},"count",$sets{count},1);
           }
           last;
-        }; 
+        };
         $attribute eq "reset-on-threshold-reached" and do {
           if ($value eq "yes"
           and defined (my $threshold = main::AttrVal($name,"count-threshold",undef))) {
@@ -197,8 +250,12 @@ FRM_IN_Attr($$$$) {
         $attribute eq "internal-pullup" and do {
           if ($main::init_done) {
             my $firmata = FRM_Client_FirmataDevice($hash);
-            $firmata->digital_write($pin,$value eq "on" ? 1 : 0);
-            #ignore any errors here, the attribute-value will be applied next time FRM_IN_init() is called.
+            if (FRM_IN_PinModePullupSupported($hash)) {
+              $firmata->pin_mode($pin,$value eq "on"? PIN_PULLUP : PIN_INPUT);
+            } else {
+              $firmata->digital_write($pin,$value eq "on" ? 1 : 0);
+              #ignore any errors here, the attribute-value will be applied next time FRM_IN_init() is called.
+            }
           }
           last;
         };
@@ -218,7 +275,11 @@ FRM_IN_Attr($$$$) {
       ARGUMENT_HANDLER: {
         $attribute eq "internal-pullup" and do {
           my $firmata = FRM_Client_FirmataDevice($hash);
-          $firmata->digital_write($pin,0);
+          if (FRM_IN_PinModePullupSupported($hash)) {
+            $firmata->pin_mode($pin,PIN_INPUT);
+          } else {
+            $firmata->digital_write($pin,0);
+          }
           last;
         };
         $attribute eq "activeLow" and do {
@@ -241,23 +302,37 @@ FRM_IN_Attr($$$$) {
 1;
 
 =pod
+
+  CHANGES
+
+  03.01.2018 jensb
+    o implemented Firmata 2.5 feature PIN_MODE_PULLUP (requires perl-firmata 0.64 or higher)
+
+=cut
+
+=pod
+=item device
+=item summary Firmata: digital input
+=item summary_DE Firmata: digitaler Eingang
 =begin html
 
 <a name="FRM_IN"></a>
 <h3>FRM_IN</h3>
 <ul>
-  represents a pin of an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a>
-  configured for digital input.<br>
-  The current state of the arduino-pin is stored in reading 'state'. Values are 'on' and 'off'.<br>
-  Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br> 
-  
+  This module represents a pin of a <a href="http://www.firmata.org">Firmata device</a>
+  that should be configured as a digital input.<br><br>
+
+  Requires a defined <a href="#FRM">FRM</a> device to work. The pin must be listed in
+  the internal reading <a href="#FRMinternals">"input_pins" or "pullup_pins"</a><br>
+  of the FRM device (after connecting to the Firmata device) to be used as digital input with or without pullup.<br><br>
+
   <a name="FRM_INdefine"></a>
   <b>Define</b>
   <ul>
   <code>define &lt;name&gt; FRM_IN &lt;pin&gt;</code> <br>
   Defines the FRM_IN device. &lt;pin&gt> is the arduino-pin to use.
   </ul>
-  
+
   <br>
   <a name="FRM_INset"></a>
   <b>Set</b><br>
@@ -265,7 +340,8 @@ FRM_IN_Attr($$$$) {
     <li>alarm on|off<br>
     set the alarm to on or off. Used to clear the alarm.<br>
     The alarm is set to 'on' whenever the count reaches the threshold and doesn't clear itself.</li>
-  </ul>
+  </ul><br>
+
   <a name="FRM_INget"></a>
   <b>Get</b>
   <ul>
@@ -276,10 +352,11 @@ FRM_IN_Attr($$$$) {
     Depending on the attribute 'count-mode' every rising or falling edge (or both) is counted.</li>
     <li>alarm<br>
     returns the current state of 'alarm'. Values are 'on' and 'off' (Defaults to 'off')<br>
-    'alarm' doesn't clear itself, has to be set to 'off' eplicitly.</li>
+    'alarm' doesn't clear itself, has to be set to 'off' explicitly.</li>
     <li>state<br>
     returns the 'state' reading</li>
   </ul><br>
+
   <a name="FRM_INattr"></a>
   <b>Attributes</b><br>
   <ul>
@@ -302,9 +379,17 @@ FRM_IN_Attr($$$$) {
       </li>
       <li><a href="#eventMap">eventMap</a><br></li>
       <li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
-    </ul>
+  </ul><br>
+
+  <a name="FRM_INnotes"></a>
+  <b>Notes</b><br>
+  <ul>
+      <li>attribute <i>stateFormat</i><br>
+      In most cases it is a good idea to assign "reading" to the attribute <i>stateFormat</i>. This will show the state
+      of the pin in the web interface.
+      </li>
   </ul>
-<br>
+</ul><br>
 
 =end html
 =cut

+ 155 - 30
fhem/core/FHEM/20_FRM_OUT.pm

@@ -1,12 +1,43 @@
-##############################################
-# $Id: 20_FRM_OUT.pm 5927 2014-05-21 21:56:37Z ntruchsess $
-##############################################
+########################################################################################
+#
+# $Id: 20_FRM_OUT.pm 15928 2018-01-19 21:07:42Z jensb $
+#
+# FHEM module for one Firmata digial output pin
+#
+########################################################################################
+#
+#  LICENSE AND COPYRIGHT
+#
+#  Copyright (C) 2013 ntruchess
+#  Copyright (C) 2016 jensb
+#
+#  All rights reserved
+#
+#  This script is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  The GNU General Public License can be found at
+#  http://www.gnu.org/copyleft/gpl.html.
+#  A copy is found in the textfile GPL.txt and important notices to the license
+#  from the author is found in LICENSE.txt distributed with these scripts.
+#
+#  This script is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#  GNU General Public License for more details.
+#
+#  This copyright notice MUST APPEAR in all copies of the script!
+#
+########################################################################################
+
 package main;
 
 use strict;
 use warnings;
 
-#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
+#add FHEM/lib to @INC if it's not already included. Should rather be in fhem.pl than here though...
 BEGIN {
 	if (!grep(/FHEM\/lib$/,@INC)) {
 		foreach my $inc (grep(/FHEM$/,@INC)) {
@@ -31,7 +62,7 @@ FRM_OUT_Initialize($)
   $hash->{AttrFn}    = "FRM_OUT_Attr";
   $hash->{StateFn}   = "FRM_OUT_State";
   
-  $hash->{AttrList}  = "restoreOnReconnect:on,off restoreOnStartup:on,off activeLow:yes,no IODev $main::readingFnAttributes";
+  $hash->{AttrList}  = "restoreOnReconnect:on,off restoreOnStartup:on,off activeLow:yes,no IODev valueMode:send,receive,bidirectional $main::readingFnAttributes";
   main::LoadModule("FRM");
 }
 
@@ -41,12 +72,20 @@ FRM_OUT_Init($$)
 	my ($hash,$args) = @_;
 	my $ret = FRM_Init_Pin_Client($hash,$args,PIN_OUTPUT);
 	return $ret if (defined $ret);
+	eval {
+      my $firmata = FRM_Client_FirmataDevice($hash);
+      my $pin = $hash->{PIN};
+      $firmata->observe_digital($pin,\&FRM_OUT_observer,$hash);
+	};  
 	my $name = $hash->{NAME};
 	if (! (defined AttrVal($name,"stateFormat",undef))) {
 		$main::attr{$name}{"stateFormat"} = "value";
 	}
 	my $value = ReadingsVal($name,"value",undef);
-	if (defined $value and AttrVal($hash->{NAME},"restoreOnReconnect","on") eq "on") {
+	if (!defined($value)) {
+		readingsSingleUpdate($hash,"value","off",0);
+	}  
+	if (AttrVal($hash->{NAME},"restoreOnReconnect", "on") eq "on") {
 		FRM_OUT_Set($hash,$name,$value);
 	}
 	main::readingsSingleUpdate($hash,"state","Initialized",1);
@@ -54,24 +93,46 @@ FRM_OUT_Init($$)
 }
 
 sub
+FRM_OUT_observer($$$$)
+{
+  my ($pin,$old,$new,$hash) = @_;
+  my $name = $hash->{NAME};
+  Log3 $name, 5, "onDigitalMessage for pin ".$pin.", old: ".(defined $old? $old : "--").", new: ".(defined $new? $new : "--");
+  if (AttrVal($hash->{NAME}, "activeLow", "no") eq "yes") {
+    $old = $old == PIN_LOW ? PIN_HIGH : PIN_LOW if (defined $old);
+    $new = $new == PIN_LOW ? PIN_HIGH : PIN_LOW;
+  }
+  my $changed = !defined($old) || ($old != $new);
+  if ($changed && (AttrVal($hash->{NAME}, "valueMode", "send") ne "send")) {
+    main::readingsSingleUpdate($hash, "value", $new == PIN_HIGH? "on" : "off", 1);
+  }
+}
+
+sub
 FRM_OUT_Set($$$)
 {
   my ($hash, $name, $cmd, @a) = @_;
   my $value;
-  my $invert = AttrVal($hash->{NAME},"activeLow","no");
-  if ($cmd eq "on") {
-  	$value = $invert eq "yes" ? PIN_LOW : PIN_HIGH;
-  } elsif ($cmd eq "off") {
-  	$value = $invert eq "yes" ? PIN_HIGH : PIN_LOW;
+  my $invert = AttrVal($hash->{NAME},"activeLow", "no");
+  if (defined ($cmd)) {
+    if ($cmd eq "on") {
+      $value = $invert eq "yes" ? PIN_LOW : PIN_HIGH;
+    } elsif ($cmd eq "off") {
+      $value = $invert eq "yes" ? PIN_HIGH : PIN_LOW;
+    } else {
+      my $list = "on off";
+      return SetExtensions($hash, $list, $name, $cmd, @a);
+    }
+    eval {
+      FRM_Client_FirmataDevice($hash)->digital_write($hash->{PIN},$value);
+      if (AttrVal($hash->{NAME}, "valueMode", "send") ne "receive") {
+        main::readingsSingleUpdate($hash,"value",$cmd, 1);
+      }
+    };
+    return $@;
   } else {
-  	my $list = "on off";
-    return SetExtensions($hash, $list, $name, $cmd, @a);
+    return "no command specified";
   }
-  eval {
-    FRM_Client_FirmataDevice($hash)->digital_write($hash->{PIN},$value);
-    main::readingsSingleUpdate($hash,"value",$cmd, 1);
-  };
-  return $@;
 }
 
 sub FRM_OUT_State($$$$)
@@ -80,7 +141,7 @@ sub FRM_OUT_State($$$$)
 	
 STATEHANDLER: {
 		$sname eq "value" and do {
-			if (AttrVal($hash->{NAME},"restoreOnStartup","on") eq "on") { 
+			if (AttrVal($hash->{NAME},"restoreOnStartup", "on") eq "on") { 
 				FRM_OUT_Set($hash,$hash->{NAME},$sval);
 			}
 			last;
@@ -102,6 +163,18 @@ FRM_OUT_Attr($$$$) {
           }
           last;
         };
+        $attribute eq "activeLow" and do {
+          my $oldval = AttrVal($hash->{NAME},"activeLow", "no");
+          if ($oldval ne $value) {
+            # toggle output with attribute change
+            $main::attr{$hash->{NAME}}{activeLow} = $value;
+            if ($main::init_done) {
+              my $value = ReadingsVal($name,"value",undef);
+              FRM_OUT_Set($hash,$hash->{NAME},$value);
+            }
+          };
+          last;
+        };
       }
     }
   };
@@ -115,23 +188,45 @@ FRM_OUT_Attr($$$$) {
 1;
 
 =pod
+
+  CHANGES
+
+  2016 jensb
+    o new sub FRM_OUT_observer, modified sub FRM_OUT_Init
+      to receive output state from Firmata device
+    o support attribute "activeLow"
+  01.01.2018 jensb
+    o create reading "value" in FRM_OUT_Init if missing
+  02.01.2018 jensb
+    o new attribute "valueMode" to control how "value" reading is updated
+  14.01.2018 jensb
+    o fix "uninitialised" when calling FRM_OUT_Set without command
+
+=cut
+
+=pod
+=item device
+=item summary Firmata: digital output
+=item summary_DE Firmata: digitaler Ausang
 =begin html
 
 <a name="FRM_OUT"></a>
 <h3>FRM_OUT</h3>
 <ul>
-  represents a pin of an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a>
-  configured for digital output.<br>
-  Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br> 
+  This module represents a pin of a <a href="http://www.firmata.org">Firmata device</a> 
+  that should be configured as a digital output.<br><br>
+  
+  Requires a defined <a href="#FRM">FRM</a> device to work. The pin must be listed in
+  the internal reading "<a href="#FRMinternals">output_pins</a>"<br>
+  of the FRM device (after connecting to the Firmata device) to be used as digital output.<br><br> 
   
   <a name="FRM_OUTdefine"></a>
   <b>Define</b>
   <ul>
   <code>define &lt;name&gt; FRM_OUT &lt;pin&gt;</code> <br>
   Defines the FRM_OUT device. &lt;pin&gt> is the arduino-pin to use.
-  </ul>
+  </ul><br>
   
-  <br>
   <a name="FRM_OUTset"></a>
   <b>Set</b><br>
   <ul>
@@ -139,27 +234,57 @@ FRM_OUT_Attr($$$$) {
   </ul>
   <ul>
   <a href="#setExtensions">set extensions</a> are supported<br>
-  </ul>
+  </ul><br>
+  
   <a name="FRM_OUTget"></a>
   <b>Get</b><br>
   <ul>
   N/A
   </ul><br>
+  
   <a name="FRM_OUTattr"></a>
   <b>Attributes</b><br>
   <ul>
-      <li>restoreOnStartup &lt;on|off&gt;</li>
-      <li>restoreOnReconnect &lt;on|off&gt;</li>
-      <li>activeLow &lt;yes|no&gt;</li>
+      <li>restoreOnStartup &lt;on|off&gt;, default: on<br>
+      Set output value in Firmata device on FHEM startup (if device is already connected) and
+      whenever the <em>setstate</em> command is used.
+      </li>
+      <li>restoreOnReconnect &lt;on|off&gt;, default: on<br>
+      Set output value in Firmata device after IODev is initialized.
+      </li>
+      <li>activeLow &lt;yes|no&gt;, default: no</li>
       <li><a href="#IODev">IODev</a><br>
       Specify which <a href="#FRM">FRM</a> to use. (Optional, only required if there is more
       than one FRM-device defined.)
       </li>
+      <li>valueMode &lt;send|receive|bidirectional&gt;, default: send<br>
+      Define how the reading <em>value</em> is updated:<br>
+      <ul>
+        <li>send - after sending</li>
+        <li>receive - after receiving</li>
+        <li>bidirectional - after sending and receiving</li>
+      </ul>
+      </li>
       <li><a href="#eventMap">eventMap</a><br></li>
       <li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
-    </ul>
+  </ul><br>
+  
+  <a name="FRM_OUTnotes"></a>
+  <b>Notes</b><br>
+  <ul>
+      <li>attribute <i>stateFormat</i><br>
+      In most cases it is a good idea to assign "value" to the attribute <i>stateFormat</i>. This will show the state
+      of the pin in the web interface.
+      </li>
+      <li>attribute <i>valueMode</i><br>
+      For modes "receive<" and "bidirectional" to work the default Firmata application code must 
+      be modified in function "<code>setPinModeCallback</code>":<br>
+      add "<ins> || mode == OUTPUT</ins>" to the if condition for "<code>portConfigInputs[pin / 8] |= (1 << (pin & 7));</code>" to enable<br>
+      reporting the output state (as if the pin were an input). This is of interest if you have custom code in your Firmata device that can change<br>
+      the state of an output or you want a feedback from the Firmata device after the output state was changed.
+      </li>
   </ul>
-<br>
+</ul><br>
 
 =end html
 =cut

+ 123 - 50
fhem/core/FHEM/20_FRM_PWM.pm

@@ -1,6 +1,37 @@
-##############################################
-# $Id: 20_FRM_PWM.pm 5927 2014-05-21 21:56:37Z ntruchsess $
-##############################################
+########################################################################################
+#
+# $Id: 20_FRM_PWM.pm 15929 2018-01-19 21:11:06Z jensb $
+#
+# FHEM module for one Firmata PWM output pin
+#
+########################################################################################
+#
+#  LICENSE AND COPYRIGHT
+#
+#  Copyright (C) 2013 ntruchess
+#  Copyright (C) 2016 jensb
+#
+#  All rights reserved
+#
+#  This script is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  The GNU General Public License can be found at
+#  http://www.gnu.org/copyleft/gpl.html.
+#  A copy is found in the textfile GPL.txt and important notices to the license
+#  from the author is found in LICENSE.txt distributed with these scripts.
+#
+#  This script is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#  GNU General Public License for more details.
+#
+#  This copyright notice MUST APPEAR in all copies of the script!
+#
+########################################################################################
+
 package main;
 
 use strict;
@@ -62,7 +93,11 @@ FRM_PWM_Init($$)
 	return $ret if (defined $ret);
 	my $firmata = $hash->{IODev}->{FirmataDevice};
 	my $name = $hash->{NAME};
-	my $resolution = $firmata->{metadata}{pwm_resolutions}{$hash->{PIN}} if (defined $firmata->{metadata}{pwm_resolutions});
+	my $resolution = 8;
+	if (defined $firmata->{metadata}{pwm_resolutions}) {
+		$resolution = $firmata->{metadata}{pwm_resolutions}{$hash->{PIN}} 
+	}
+	$hash->{resolution} = $resolution;
 	$hash->{".max"} = defined $resolution ? (1<<$resolution)-1 : 255;
 	$hash->{".dim"} = 0;
 	$hash->{".toggle"} = "off"; 
@@ -71,7 +106,7 @@ FRM_PWM_Init($$)
 	}
 	my $value = ReadingsVal($name,"value",undef);
 	if (defined $value and AttrVal($hash->{NAME},"restoreOnReconnect","on") eq "on") {
-		FRM_PWM_Set($hash,$name,$value);
+		FRM_PWM_Set($hash,$name,"value",$value);
 	}
 	main::readingsSingleUpdate($hash,"state","Initialized",1);
 	return undef;
@@ -239,9 +274,21 @@ sub
 FRM_PWM_State($$$$)
 {
   my ($hash, $tim, $sname, $sval) = @_;
+  my $name = $hash->{NAME};
   if ($sname eq "value") {
-    FRM_PWM_Set($hash,$hash->{NAME},$sname,$sval);
+    # depending on the FHEM startup timing and the Arduino connection type, FHEM statefile restore and Arduino connect take place in arbitrary order
+    if (AttrVal($name, "restoreOnStartup", "on") eq "on") {
+      $hash->{READINGS}{$sname}{VAL} = $sval;
+      $hash->{READINGS}{$sname}{TIME} = $tim;
+      if (defined($hash->{IODev}) && defined($hash->{IODev}->{FirmataDevice} && $hash->{IODev}->{FirmataDevice}->{state} eq "Initialized")) {
+        FRM_PWM_Set($hash, $name, "value", $sval);
+      }
+    } else {
+      $hash->{READINGS}{$sname}{VAL} = undef;
+      $hash->{READINGS}{$sname}{TIME} = gettimeofday();
+    }
   }
+  return 0; # default processing by fhem.pl
 }
 
 sub
@@ -272,64 +319,82 @@ FRM_PWM_Attr($$$$)
 1;
 
 =pod
+
+  CHANGES
+
+  2016 jensb
+    o modified subs FRM_PWM_Init and FRM_PWM_State to support attribute "restoreOnStartup"
+
+=cut
+
+=pod
+=item device
+=item summary Firmata: PWM output
+=item summary_DE Firmata: PWM Ausgang
 =begin html
 
 <a name="FRM_PWM"></a>
 <h3>FRM_PWM</h3>
 <ul>
-  represents a pin of an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a>
-  configured for analog output.<br>
-  The value set will be output by the specified pin as a pulse-width-modulated signal.<br> 
-  Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br> 
+  This module represents a pin of a <a href="http://www.firmata.org">Firmata device</a> 
+  that should be configured as a pulse width modulated output (PWM).<br><br>
+  
+  Requires a defined <a href="#FRM">FRM</a> device to work. The pin must be listed in the internal reading "<a href="#FRMinternals">pwm_pins</a>"<br>
+  of the FRM device (after connecting to the Firmata device) to be used as PWM output.<br><br> 
   
   <a name="FRM_PWMdefine"></a>
   <b>Define</b>
   <ul>
-  <code>define &lt;name&gt; FRM_PWM &lt;pin&gt;</code> <br>
-  Defines the FRM_PWM device. &lt;pin&gt> is the arduino-pin to use.
-  </ul>
-  
-  <br>
+      <code>define &lt;name&gt; FRM_PWM &lt;pin&gt;</code><br><br>
+      
+      Defines the FRM_PWM device. &lt;pin&gt> is the arduino-pin to use.
+  </ul><br>
+ 
   <a name="FRM_PWMset"></a>
   <b>Set</b><br>
   <ul>
-  <code>set &lt;name&gt; on</code><br>
-  sets the pulse-width to 100%<br>
-  </ul>
-  <ul>
-  <code>set &lt;name&gt; off</code><br>
-  sets the pulse-width to 0%<br>
-  </ul>
-  <ul>
-  <a href="#setExtensions">set extensions</a> are supported<br>
-  </ul>
-  <ul>
-  <code>set &lt;name&gt; toggle</code><br>
-  toggles the pulse-width in between to the last value set by 'value' or 'dim' and 0 respectivly 100%<br>
-  </ul>
-  <ul>
-  <code>set &lt;name&gt; value &lt;value&gt;</code><br>
-  sets the pulse-width to the value specified<br>
-  Range is from 0 to 255 (for 8-bit resolution) (see <a href="http://arduino.cc/en/Reference/AnalogWrite">analogWrite()</a> for details)<br>
-  </ul>
-  <ul>
-  <code>set &lt;name&gt; dim &lt;value&gt;</code><br>
-  sets the pulse-width to the value specified in percent<br>
-  Range is from 0 to 100<br>
-  </ul>
-  <ul>
-  <code>set &lt;name&gt; dimUp</code><br>
-  increases the pulse-width by 10%<br>
-  </ul>
-  <ul>
-  <code>set &lt;name&gt; dimDown</code><br>
-  decreases the pulse-width by 10%<br>
-  </ul>
+      <li><code>set &lt;name&gt; on</code><br>
+      sets the pulse-width to 100%<br>
+      </li>
+      <li>
+      <code>set &lt;name&gt; off</code><br>
+      sets the pulse-width to 0%<br>
+      </li>
+      <li>
+      <a href="#setExtensions">set extensions</a> are supported<br>
+      </li>
+      <li>
+      <code>set &lt;name&gt; toggle</code><br>
+      toggles the pulse-width in between to the last value set by 'value' or 'dim' and 0 respectivly 100%<br>
+      </li>
+      <li>
+      <code>set &lt;name&gt; value &lt;value&gt;</code><br>
+      sets the pulse-width to the value specified<br>
+      The min value is zero and the max value depends on the Firmata device (see internal reading<br>
+      "<a href="#FRMinternals">pwm_resolutions</a>" of the FRM device). For 8 bits resolution the range
+      is 0 to 255 (also see <a href="http://arduino.cc/en/Reference/AnalogWrite">analogWrite()</a> for details)<br>
+      </li>
+      <li>
+      <code>set &lt;name&gt; dim &lt;value&gt;</code><br>
+      sets the pulse-width to the value specified in percent<br>
+      Range is from 0 to 100<br>
+      </li>
+      <li>
+      <code>set &lt;name&gt; dimUp</code><br>
+      increases the pulse-width by 10%<br>
+      </li>
+      <li>
+      <code>set &lt;name&gt; dimDown</code><br>
+      decreases the pulse-width by 10%<br>
+      </li>
+  </ul><br>
+  
   <a name="FRM_PWMget"></a>
   <b>Get</b><br>
   <ul>
   N/A
   </ul><br>
+  
   <a name="FRM_PWMattr"></a>
   <b>Attributes</b><br>
   <ul>
@@ -341,9 +406,17 @@ FRM_PWM_Attr($$$$)
       </li>
       <li><a href="#eventMap">eventMap</a><br></li>
       <li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
-    </ul>
-  </ul>
-<br>
+  </ul><br>
+  
+  <a name="FRM_PWMnotes"></a>
+  <b>Notes</b><br>
+  <ul>
+      <li>attribute <i>stateFormat</i><br>
+      In most cases it is a good idea to assign "value" to the attribute <i>stateFormat</i>. This will show the 
+      current value of the pin in the web interface.
+      </li>
+  </ul>  
+</ul><br>
 
 =end html
 =cut

+ 4 - 19
fhem/core/FHEM/20_N4HBUS.pm

@@ -4,23 +4,9 @@
 #
 # net4home Busconnector Device
 #
-# (c) 2014-2016 Oliver Koerber <koerber@net4home.de>
+# (c) 2014-2018 Oliver Koerber <koerber@net4home.de>
 #
-#
-# Fhem is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-#
-# Fhem is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with fhem.  If not, see <http://www.gnu.org/licenses/>.
-#
-# $Id: 20_N4HBUS.pm 12459 2016-10-28 19:00:08Z okoerber $
+# $Id: 20_N4HBUS.pm 15729 2017-12-30 20:38:14Z oliverk $
 #
 ##############################################################################
 
@@ -31,7 +17,7 @@ use warnings;
 use POSIX;
 use Data::Dumper;
 
-my $n4hbus_Version = "1.0.1.2 - 27.10.2016";
+my $n4hbus_Version = "1.0.2.0 - 30.12.2017";
 
 sub N4HBUS_Read($@);
 sub N4HBUS_Write($$$$);
@@ -456,7 +442,7 @@ sub N4HBUS_Ready($) {
   </ul>
   <br />  
    The device can also be connected to the busconnector on the same machine. <br />
-   Default Port for communication is 3478. In case you need to change this please change also the Port in the conf of busconnector service.
+   Default Port for communication is 3478. In case you need to change this please change also the Port in the conf of Busconnector service.
   </ul>
   <br />
   <a name="N4HBUS_Readings"></a>
@@ -500,7 +486,6 @@ sub N4HBUS_Ready($) {
   in der Konfiguration des Services durchgef&uuml;hrt werden.<br />
   </ul>
   <br /> 
-  
   <a name="N4HBUS_Readings"></a>
   <b>Readings</b>
   <ul>

+ 3 - 1
fhem/core/FHEM/20_X10.pm

@@ -22,7 +22,7 @@
 #  This copyright notice MUST APPEAR in all copies of the script!
 #
 ################################################################
-# $Id: 20_X10.pm 10401 2016-01-07 19:26:18Z borisneubert $
+# $Id: 20_X10.pm 16375 2018-03-10 15:40:02Z neubert $
 
 
 #
@@ -533,6 +533,8 @@ X10_Parse($$)
 1;
 
 =pod
+=item summary    devices communicating via the X10 protocol
+=item summary_DE Anbindung von X10-Ger&auml;ten
 =begin html
 
 <a name="X10"></a>

+ 8 - 3
fhem/core/FHEM/21_HEOSGroup.pm

@@ -26,7 +26,7 @@
 #  GNU General Public License for more details.
 #
 #
-# $Id: 21_HEOSGroup.pm 14952 2017-08-24 04:42:36Z CoolTux $
+# $Id: 21_HEOSGroup.pm 16288 2018-02-28 09:11:34Z CoolTux $
 #
 ###############################################################################
 
@@ -38,7 +38,7 @@ use JSON qw(decode_json);
 use Encode qw(encode_utf8);
 
 
-my $version = "1.0.0";
+my $version = "1.0.3";
 
 
 
@@ -433,8 +433,13 @@ sub HEOSGroup_Parse($$) {
     my $decode_json;
     my $code;
 
+
+    $decode_json = eval{decode_json(encode_utf8($json))};
+    if($@){
+        Log3 $name, 3, "HEOSGroup ($name) - JSON error while request: $@";
+        return;
+    }
     
-    $decode_json    = decode_json(encode_utf8($json));
     Log3 $name, 4, "HEOSGroup ($name) - ParseFn wurde aufgerufen";
 
     if( defined($decode_json->{gid}) ) {

+ 15 - 6
fhem/core/FHEM/21_HEOSMaster.pm

@@ -26,7 +26,7 @@
 #  GNU General Public License for more details.
 #
 #
-# $Id: 21_HEOSMaster.pm 15271 2017-10-17 09:06:20Z CoolTux $
+# $Id: 21_HEOSMaster.pm 16288 2018-02-28 09:11:34Z CoolTux $
 #
 ###############################################################################
 
@@ -65,7 +65,7 @@ eval "use Encode;1" or $missingModul .= "Encode ";
 
 
 
-my $version = "1.0.2";
+my $version = "1.0.3";
 
 my %heosCmds = (
     'enableChangeEvents'        => 'system/register_for_change_events?enable=',
@@ -94,7 +94,7 @@ my %heosCmds = (
     'setMute'                   => 'player/set_mute?',
     'setGroupMute'              => 'group/set_mute?',
     'playNext'                  => 'player/play_next?',
-    'playPrev'                  => 'player/play_prev?',
+    'playPrev'                  => 'player/play_previous?',
     'playPresetStation'         => 'browse/play_preset?',
     'playInput'                 => 'browse/play_input?',
     'playStream'                => 'browse/play_stream?',
@@ -228,7 +228,7 @@ sub HEOSMaster_Undef($$) {
     HEOSMaster_Close($hash);
     delete $modules{HEOSMaster}{defptr}{$hash->{HOST}};
     
-    Log3 $name, 3, "HEOSPlayer ($name) - device $name deleted";
+    Log3 $name, 3, "HEOSMaster ($name) - device $name deleted";
     return undef;
 }
 
@@ -514,7 +514,12 @@ sub HEOSMaster_ProcessRead($$) {
     
         $hash->{LAST_RECV} = time();
         Log3 $name, 5, "HEOSMaster ($name) - Decoding JSON message. Length: " . length($json) . " Content: " . $json;
-        my $obj = decode_json($json);
+
+        my $obj = eval{decode_json($json)};
+        if($@){
+            Log3 $name, 3, "HEOSMaster ($name) - JSON error while request: $@";
+            return;
+        }
         
         if(defined($obj->{heos})) {
         
@@ -548,7 +553,11 @@ sub HEOSMaster_ResponseProcessing($$) {
     unless( defined($json));
 
     Log3 $name, 4, "HEOSMaster ($name) - JSON detected!";
-    $decode_json = decode_json(encode_utf8($json));
+    $decode_json = eval{decode_json(encode_utf8($json))};
+    if($@){
+        Log3 $name, 3, "HEOSMaster ($name) - JSON error while request: $@";
+        return;
+    }
 
     return Log3 $name, 3, "HEOSMaster ($name) - decode_json has no Hash"
     unless(ref($decode_json) eq "HASH");

+ 17 - 3
fhem/core/FHEM/21_HEOSPlayer.pm

@@ -26,7 +26,7 @@
 #  GNU General Public License for more details.
 #
 #
-# $Id: 21_HEOSPlayer.pm 14959 2017-08-25 19:36:55Z CoolTux $
+# $Id: 21_HEOSPlayer.pm 16288 2018-02-28 09:11:34Z CoolTux $
 #
 ###############################################################################
 
@@ -39,7 +39,7 @@ use Encode qw(encode_utf8);
 use URI::Escape;
 #use Data::Dumper;
 
-my $version = "1.0.1";
+my $version = "1.0.3";
 
 
 
@@ -712,6 +712,15 @@ sub HEOSPlayer_Set($$@) {
             $heosCmd = 'playQueueItem';
             $action  = "qid=$cid";
 
+        } elsif ( $sid eq "url" ) {
+        
+            #URL abspielen
+            $heosCmd = 'playStream';
+            #$action  = "url=".substr($param,4);
+            $action  = "url=$cid";
+            
+            #getestet mit "set HEOSPlayer_Name input url,http://sender.eldoradio.de:8000/128.mp3"  ich wollte [cid] nicht nutzen da in einer url ja durchaus mehrere Kommata vorkommen können ob das mit dem substr() so toll ich kann ich leider nicht beurteilen. Auch würde ich bei der $sid ein lc($sid) drum machen aber da es nirgendwo ist :-)
+            
         } else {
             if ( $sid > 0 && $sid < 1024 ) {
                 return "usage: $cmd sid,cid,mid" unless( defined($cid) && defined($mid) );
@@ -781,8 +790,13 @@ sub HEOSPlayer_Parse($$) {
     my $decode_json;
     my $code;
 
+
+    $decode_json = eval{decode_json(encode_utf8($json))};
+    if($@){
+        Log3 $name, 3, "HEOSPlayer ($name) - JSON error while request: $@";
+        return;
+    }
     
-    $decode_json    = decode_json(encode_utf8($json));
     Log3 $name, 4, "HEOSPlayer - ParseFn wurde aufgerufen";
     if( defined($decode_json->{pid}) ) {
     

+ 502 - 84
fhem/core/FHEM/21_N4HMODULE.pm

@@ -4,23 +4,9 @@
 #
 # net4home Busconnector Device
 #
-# (c) 2014-2016 Oliver Koerber <koerber@net4home.de>
+# (c) 2014-2018 Oliver Koerber <koerber@net4home.de>
 #
-#
-# Fhem is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-#
-# Fhem is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with fhem.  If not, see <http://www.gnu.org/licenses/>.
-#
-# $Id: 21_N4HMODULE.pm 12460 2016-10-28 19:00:30Z okoerber $
+# $Id: 21_N4HMODULE.pm 15730 2017-12-30 20:40:07Z oliverk $
 #
 ##############################################################################
 
@@ -32,10 +18,11 @@ use POSIX;
 use SetExtensions;
 
 sub N4HMODULE_Set($@);
+sub N4HMODULE_Get ($$@);
 sub N4HMODULE_Update($@);
 sub N4HMODULE_DbLog_splitFn($$);
 
-my $n4hmodule_Version = "1.0.1.3 - 27.10.2016";
+my $n4hmodule_Version = "1.0.2.0 - 30.12.2017";
 
 my %OT_devices = (
 	"1" 	=> {"name" => "leer", "OTcanSet" => "false", "OTcanReq" => "false", "fields" => [] },
@@ -77,7 +64,20 @@ my %OT_devices = (
 	
 	"26" 	=> {"name" => "Messwert,Feuchte", "OTcanSet" => "false", "OTcanReq" 	=> "true", "fields" => [] },
 	
+	"34" 	=> {"name" => "TLH-Regler H,Sollwert,Temperatur", "OTcanSet" => "true", "OTcanReq" => "true", "fields" => [
+					{ "cmd" =>  "3B0000", "ID" => "0", "Text" => "AUS", 					"Type" => "Button" , "set" => "off:noArg" },
+					{ "cmd" =>  "3B0100", "ID" => "1", "Text" => "Heizen", 					"Type" => "Button" , "set" => "heat:noArg" },
+					{ "cmd" =>  "3B0200", "ID" => "2", "Text" => "Kühlen", 					"Type" => "Button" , "set" => "cool:noArg" },
+					{ "cmd" =>  "3B0300", "ID" => "3", "Text" => "Auto", 					"Type" => "Button" , "set" => "auto:noArg" },
+					{ "cmd" =>  "3200",   "ID" => "4", "Text" => "Sollwert", 		  		"Type" => "Button" , "set" => "desired-temperature:slider,4,0.5,30,1" },
+					{ "cmd" =>  "3B1400", "ID" => "5", "Text" => "Tagwert aktivieren", 		"Type" => "Button" , "set" => "comfort:noArg" },
+					{ "cmd" =>  "3B1E00", "ID" => "6", "Text" => "Nachtwert aktivieren", 	"Type" => "Button" , "set" => "eco:noArg" },
+					{ "cmd" =>  "3B1500", "ID" => "7", "Text" => "Sollwert -&gt; Tagwert",	"Type" => "Button" , "set" => "setdesired2comfort:noArg" },
+					{ "cmd" =>  "3B1F00", "ID" => "8", "Text" => "Sollwert -&gt; Nachtwert","Type" => "Button" , "set" => "setdesired2eco:noArg" },
+                        ]},
+
 	"95" 	=> {"name" => "Ausgang, Jal, Motor AJ3", "OTcanSet" => "true", "OTcanReq" => "true", "fields" => [
+					{ "cmd" =>  "350000", "ID" => "0", "text" => "Weiterschalten", 			"Type" => "Button" , "set" => "toggle:noArg" },
 					{ "cmd" =>  "320000", "ID" => "1", "text" => "STOP", 					"Type" => "Button" , "set" => "stop:noArg" },
 					{ "cmd" =>  "320300", "ID" => "2", "text" => "AUF",					 	"Type" => "Button" , "set" => "up:noArg" },
 					{ "cmd" =>  "320100", "ID" => "3", "text" => "AB", 						"Type" => "Button" , "set" => "down:noArg" },
@@ -86,6 +86,13 @@ my %OT_devices = (
 					{ "cmd" =>  "420000", "ID" => "6", "text" => "Sperre freigeben", 		"Type" => "Button" , "set" => "unlock:noArg" },
                         ]},
 
+	"100" 	=> {"name" => "Safety Basis", "OTcanSet" => "true", "OTcanReq" => "true", "fields" => [
+					{ "cmd" =>  "320000", "ID" => "0", "text" => "AUS", 					"Type" => "Button" , "set" => "off:noArg" },
+					{ "cmd" =>  "320100", "ID" => "1", "text" => "Intern scharf 1",			"Type" => "Button" , "set" => "intern1:noArg" },
+					{ "cmd" =>  "320200", "ID" => "2", "text" => "Intern scharf 1",		 	"Type" => "Button" , "set" => "intern2:noArg" },
+					{ "cmd" =>  "320300", "ID" => "3", "text" => "Extern scharf", 			"Type" => "Button" , "set" => "extern:noArg" },
+                        ]},
+
 	"210" 	=> {"name" => "UP-RF Absender", "OTcanSet" => "false", "OTcanReq" 	=> "true", "fields" => [] },
 
 	"240" 	=> {"name" => "Messwert,Wind", 		 "OTcanSet" => "false", "OTcanReq" 	=> "true", "fields" => [] },
@@ -120,6 +127,7 @@ sub N4HMODULE_Initialize($) {
 	$hash->{UndefFn}	   = "N4HMODULE_Undefine";
 	$hash->{ParseFn}	   = "N4HMODULE_Parse";
 	$hash->{SetFn}		   = "N4HMODULE_Set";
+	$hash->{GetFn}		   = "N4HMODULE_Get";
 	$hash->{AttrFn}  	   = "N4HMODULE_Attr";
 	$hash->{AttrList}	   = "IoDev dummy:1,0 Interval sendack:on,off setList ".
 						     "$readingFnAttributes ";
@@ -144,6 +152,8 @@ sub N4HMODULE_Define($$) {
 	}
 
 	$hash->{VERSION}	= $n4hmodule_Version;
+	$hash->{Hardware}  = "undefined";
+	$hash->{Software}   = "undefined";
 	$hash->{STATE} 		= "Initializing";
 	$hash->{NOTIFYDEV}  = "global";
 	$hash->{IODev} 		= $n4hbus;
@@ -152,7 +162,8 @@ sub N4HMODULE_Define($$) {
 	$hash->{DESC}		= $OT_devices{$ot}{name};
 	$hash->{OTcanSet}	= $OT_devices{$ot}{OTcanSet};
 	$hash->{OTcanReq}	= $OT_devices{$ot}{OTcanReq};
-
+	$hash->{Interval}   = 0;
+	
 	$modules{N4HMODULE}{defptr}{$objadr} = $hash;
 	
 	AssignIoPort($hash, $n4hbus);
@@ -162,6 +173,8 @@ sub N4HMODULE_Define($$) {
 	$hash->{helper}{value}		= '';
 	$hash->{helper}{cmd}		= '';
 	$hash->{helper}{ddata}		= '';
+	$hash->{helper}{pct}		= 0;
+	
 
 	if ($hash->{OTcanSet}eq"true") {
 		$hash->{helper}{state}	= 'undefined';
@@ -181,7 +194,7 @@ sub N4HMODULE_Define($$) {
 
         my $state_format;
 		
-        if( $ot == 24 ) { #Temperatur
+        if( $ot == 24 ) { 
           $state_format .= " " if( $state_format );
           $state_format .= "T: temperature";
         } elsif( $ot == 25 ) {
@@ -213,6 +226,12 @@ sub N4HMODULE_Define($$) {
 		# Timer Zeitversetzt starten, damit nicht alles auf den Bus gleichzeit kommt 30 Sekunden + x
 		Log3 $hash, 3, "N4HMODULE_Define (set timer) -> $name ($ot)";
 		InternalTimer( gettimeofday() + 30 + int(rand(15)) , "N4HMODULE_Start", $hash, 0 );
+	} elsif (( $ot ==  34 ) or # TLH
+			(  $ot ==   3 ) or # Aktor, Relais
+			(  $ot ==   5)) {  # Aktor, Dimmer
+		# get initial value from bus after first start
+		Log3 $hash, 3, "N4HMODULE_Define (get) -> $name ($ot)";
+		InternalTimer( gettimeofday() + int(rand(10)) , "N4HMODULE_Start", $hash, 0 );
 	} 
    return undef;
 }
@@ -221,22 +240,46 @@ sub N4HMODULE_Define($$) {
 sub N4HMODULE_Start($)
 ##################################################################################
 {
-	my ($hash) = @_;
-	my $name   = $hash->{NAME};
-	my $interval = $hash->{Interval}; 
-
+	my ($hash) 		= @_;
+	my $name        = $hash->{NAME};
+	my $interval    = $hash->{Interval}; 
+	my $ot			= $hash->{OT};
+	my $webCmd;
+    $webCmd  		= AttrVal($name,"webCmd",undef);
+	
     $interval = $attr{$name}{Interval}  if( defined($attr{$name}{Interval}) );
      
 	Log3 $hash, 5, "N4HMODULE (start): ($name)-> ".$interval." Sekunden";
 
-	if (($interval >= 30) and ($interval <= 86400)) {
     # reset timer if interval is defined
+	if (($interval >= 30) and ($interval <= 86400)) {
 	  Log3 $hash, 5, "N4HMODULE (restart timer): ($name)-> ".$interval." Sekunden";
       RemoveInternalTimer( $hash );
       InternalTimer(gettimeofday() + $interval, "N4HMODULE_Start", $hash, 1 );
 	  N4HMODULE_Update( $hash );
+   } else {
+   
+	# get initial values/state after start
+	if ( $ot == 3 ) {
+		# ot 3 = "Ausgang, Binär, Relais"
+	    if(!defined $webCmd){ $webCmd="toggle:on:off";}
+		Log3 $hash, 5, "N4HMODULE (first timer): ($name)-> status";
+		N4HMODULE_Get($hash, $hash->{NAME}, "status");
+	} elsif ( $ot == 5 ) {
+		# ot 5 = "Ausgang, Dimmer"
+	    if(!defined $webCmd){ $webCmd="pct:toggle:on:off";}
+		Log3 $hash, 5, "N4HMODULE (first timer): ($name)-> status";
+		N4HMODULE_Get($hash, $hash->{NAME}, "status");
+	} elsif ( $ot == 34 ) {
+		# ot 34 = "TLH-Regler H,Sollwert,Temperatur"
+	    if(!defined $webCmd){ $webCmd="off:heat:cool:auto";}
+		Log3 $hash, 5, "N4HMODULE (first timer): ($name)-> desired-temperature";
+		N4HMODULE_Get($hash, $hash->{NAME}, "desired-temperature");
+	}
+	
    }
-}
+   
+} 
 
 ##################################################################################
 sub N4HMODULE_Undefine($$) {
@@ -344,83 +387,314 @@ sub N4HMODULE_ParsePayload($@) {
 
 	my ($hash, $devtype, $ipsrc, $objsrc, $ddata) = @_;
 	my $name = $hash->{NAME};
+	my $ot 	 = $hash->{OT};
 	my $dev_funcion = hex(substr($ddata,0,2)); 
-	my $newState="";
-	my $myval="";
+	my $newState	= "";
+	my $myval		= "";
+	my $objadr 		= $hash->{OBJADR};
 
 	readingsBeginUpdate($hash);
 	
-	if( defined($objsrc))	{readingsBulkUpdate($hash,"from", $objsrc);}
-	readingsBulkUpdate($hash,"ddata", $ddata);
 	
+#	+++++++ D0_GET_TYP
+	if ($dev_funcion == hex("03")) {
+
+		if (hex(substr($ddata,2,2)) == "1F") {
+			$hash->{Hardware} = "UP-S4";
+		} elsif (hex(substr($ddata,2,2)) == "29") {
+			$hash->{Hardware} = "HS-AD3";
+		}
+	}
+
 #	+++++++ D0_SET
 	if ($dev_funcion == hex("32")) {
-		readingsBulkUpdate($hash,"cmd", "D0_SET");
 
-		if (hex(substr($ddata,2,2))== hex("00")) {
-			readingsBulkUpdate($hash,"state", "off");
-			$hash->{STATE} = "off";
+		Log3 $hash, 5, "N4HMODULE -> D0_SET ($name) (".$hash->{OT}." OBJ->$objsrc - IP->$ipsrc - ".$hash->{OBJADR}.")";
+	
+		if ( $ot == 34 && $objsrc != $objadr) {
+		# ot 34 = "TLH-Regler H,Sollwert,Temperatur"
+		
+			my ($lastval) = sprintf "%.1f", hex(substr($ddata,4,2))/10;
+			readingsBulkUpdate($hash, "desired-temperature", $lastval);
+			if( defined($objsrc))	{readingsBulkUpdate($hash,"from", $objsrc);}
+			readingsBulkUpdate($hash,"ddata", $ddata);
+			
+		} elsif ( $ot == 5 ) {
+			# ot 5 = "Ausgang, Dimmer"
+
+			if( defined($objsrc))	{readingsBulkUpdate($hash,"from", $objsrc);}
+			readingsBulkUpdate($hash,"ddata", $ddata);
+			readingsBulkUpdate($hash,"cmd", "D0_SET");
+			
+			my ($lastval) = sprintf "%.0f", hex(substr($ddata,2,2));
+			
+			if ( $lastval == 0 ) {
+				readingsBulkUpdate($hash, "state", "off");
+				$hash->{helper}{pct} = $hash->{READINGS}{pct}{VAL};	
+				readingsBulkUpdate($hash, "pct", 0);
+			} elsif ( $lastval == 101 ) {
+				readingsBulkUpdate($hash, "state", "on");
+				readingsBulkUpdate($hash, "pct", $hash->{helper}{pct});			
+			} else {
+				readingsBulkUpdate($hash, "state", "on");
+				$hash->{helper}{pct} = $lastval;
+				readingsBulkUpdate($hash, "pct", $lastval);				
+			}
+			
+			
+		} elsif ( $ot == 34 ) {
+			# do nothing - cmd only for ATe
+		} elsif ( $ot == 95 ) {
+
+			if( defined($objsrc))	{readingsBulkUpdate($hash,"from", $objsrc);}
+			readingsBulkUpdate($hash,"ddata", $ddata);
+			readingsBulkUpdate($hash,"cmd", "D0_SET");
+
+			if (hex(substr($ddata,2,2))== hex("00")) {
+				readingsBulkUpdate($hash,"state", "stop");
+				$hash->{STATE} = "stop";
+				readingsBulkUpdate($hash, "position", 50);
+			} elsif (hex(substr($ddata,2,2))== hex("01")) {
+				readingsBulkUpdate($hash,"state", "down");
+				readingsBulkUpdate($hash, "position", 0);
+				$hash->{STATE} = "down";
+			} elsif (hex(substr($ddata,2,2))== hex("03")) {
+				readingsBulkUpdate($hash,"state", "up");
+				readingsBulkUpdate($hash, "position", 100);
+				$hash->{STATE} = "up";
+			} 
+
 		} else {
-			readingsBulkUpdate($hash,"state", "on");
-			$hash->{STATE} = "on";
-		}
+
+			if( defined($objsrc))	{readingsBulkUpdate($hash,"from", $objsrc);}
+			readingsBulkUpdate($hash,"ddata", $ddata);
+			readingsBulkUpdate($hash,"cmd", "D0_SET");
+
+			if (hex(substr($ddata,2,2))== hex("00")) {
+				readingsBulkUpdate($hash,"state", "off");
+				$hash->{STATE} = "off";
+			} else {
+				readingsBulkUpdate($hash,"state", "on");
+				$hash->{STATE} = "on";
+			}
+		}	
 	}
 
 #	+++++++ D0_INC
 	if ($dev_funcion == hex("33")) {
-		readingsBulkUpdate($hash,"cmd", "D0_INC");
+
+		if ( $ot == 5 ) {
+			# ot 5 = "Ausgang, Dimmer"
+			readingsBulkUpdate($hash, "state", "on");
+			$hash->{helper}{pct} = $hash->{helper}{pct}+2;
+			readingsBulkUpdate($hash, "pct", $hash->{helper}{pct});
+			if( defined($objsrc))	{readingsBulkUpdate($hash,"from", $objsrc);}
+			readingsBulkUpdate($hash,"ddata", $ddata);
+			readingsBulkUpdate($hash,"cmd", "D0_INC");
+		} else {		
+			if( defined($objsrc))	{readingsBulkUpdate($hash,"from", $objsrc);}
+			readingsBulkUpdate($hash,"ddata", $ddata);
+			readingsBulkUpdate($hash,"cmd", "D0_INC");
+		}
+	}
+#	+++++++ D0_DEC
+	if ($dev_funcion == hex("34")) {
+
+		if ( $ot == 5 ) {
+			# ot 5 = "Ausgang, Dimmer"
+			readingsBulkUpdate($hash, "state", "on");
+			$hash->{helper}{pct} = $hash->{helper}{pct}-2;
+			readingsBulkUpdate($hash, "pct", $hash->{helper}{pct});
+			if( defined($objsrc))	{readingsBulkUpdate($hash,"from", $objsrc);}
+			readingsBulkUpdate($hash,"ddata", $ddata);
+			readingsBulkUpdate($hash,"cmd", "D0_DEC");
+		} else {		
+			if( defined($objsrc))	{readingsBulkUpdate($hash,"from", $objsrc);}
+			readingsBulkUpdate($hash,"ddata", $ddata);
+			readingsBulkUpdate($hash,"cmd", "D0_DEC");
+		}
 	}
-	
 #	+++++++ D0_TOGGLE
 	if ($dev_funcion == hex("35")) {
+		if( defined($objsrc))	{readingsBulkUpdate($hash,"from", $objsrc);}
+		readingsBulkUpdate($hash,"ddata", $ddata);
 		readingsBulkUpdate($hash,"cmd", "D0_TOGGLE");
 
-		if (ReadingsVal($name, "state", "") eq "on") {
-			readingsBulkUpdate($hash,"state", "off");
-			$hash->{STATE} = "off";
-		} elsif (ReadingsVal($name, "state", "") eq "off") {
-			readingsBulkUpdate($hash,"state", "on");
-			$hash->{STATE} = "on";
-		}
+		if ( $ot == 5 ) {
+			# ot 5 = "Ausgang, Dimmer"
+			if (ReadingsVal($name, "state", "") eq "on") {
+				readingsBulkUpdate($hash,"state", "off");
+				$hash->{helper}{pct} = $hash->{READINGS}{pct}{VAL};	
+				readingsBulkUpdate($hash, "pct", 0);
+				$hash->{STATE} = "off";
+			} elsif (ReadingsVal($name, "state", "") eq "off") {
+				readingsBulkUpdate($hash,"state", "on");
+				readingsBulkUpdate($hash, "pct", $hash->{helper}{pct});			
+				$hash->{STATE} = "on";
+			}
+		} else {		
+			if (ReadingsVal($name, "state", "") eq "on") {
+				readingsBulkUpdate($hash,"state", "off");
+				$hash->{STATE} = "off";
+			} elsif (ReadingsVal($name, "state", "") eq "off") {
+				readingsBulkUpdate($hash,"state", "on");
+				$hash->{STATE} = "on";
+			}
+		}	
 	}
 
+#	+++++++ D0_REQ
+	if ($dev_funcion == hex("36")) {
+
+		if ( $ot == 3 ) {
+		# ot 3 = "Ausgang, Binär, Relais"
+		} elsif ( $ot == 34 ) {
+		# ot 34 = "TLH-Regler H,Sollwert,Temperatur"
+		} else	{
+			if( defined($objsrc))	{readingsBulkUpdate($hash,"from", $objsrc);}
+			readingsBulkUpdate($hash,"ddata", $ddata);
+			readingsBulkUpdate($hash,"cmd", "D0_REQ");
+			Log3 $hash, 5, "N4HMODULE -> D0_REQ ($name) (".$hash->{OT}.")";
+			N4HMODULE_Update( $hash );
+		}	
+	} 
+
 #	+++++++ D0_ACTOR_ACK
 	if ($dev_funcion == hex("37")) {
+		if( defined($objsrc))	{readingsBulkUpdate($hash,"from", $objsrc);}
+		readingsBulkUpdate($hash,"ddata", $ddata);
 		readingsBulkUpdate($hash,"cmd", "D0_ACTOR_ACK");
+		Log3 $hash, 5, "N4HMODULE -> D0_ACTOR_ACK ($name) (".$hash->{OT}.")";
+		
+		if ( $ot == 3 ) {
+		# ot 3 = "Ausgang, Binär, Relais"
+			if ( (hex(substr($ddata,4,2))) == 0) {
+				readingsBulkUpdate($hash, "state", "off");
+				$hash->{STATE} = "off";
+			} elsif ( (hex(substr($ddata,4,2))) == 1) {
+				readingsBulkUpdate($hash, "state", "on");
+				$hash->{STATE} = "on";
+			}
+		} elsif ( $ot == 5 ) {
+		# ot 5 = "Ausgang, Dimmer"
+			
+			if ( hex(substr($ddata,4,2)) > 128) {	
+			
+				my ($lastval) = sprintf "%.0f", hex(substr($ddata,4,2));
+				readingsBulkUpdate($hash, "pct", $lastval-128);
+				$hash->{helper}{pct} = $lastval-128;
+				readingsBulkUpdate($hash, "state", "on");
+				$hash->{STATE} = "on";
+			} else {
+				my ($lastval) = sprintf "%.0f", hex(substr($ddata,4,2));
+				$hash->{helper}{pct} = $lastval;
+				readingsBulkUpdate($hash, "state", "off");
+				$hash->{STATE} = "off";
+			}	
+			
+		} elsif ( $ot == 34 ) {
+		# ot 34 = "TLH-Regler H,Sollwert,Temperatur"
+			my ($lastval) = sprintf "%.1f", hex(substr($ddata,4,2))/10;
+			readingsBulkUpdate($hash, "desired-temperature", $lastval);
+
+			if ( (hex(substr($ddata,6,2))) == 0) {
+				readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "0");
+				readingsBulkUpdate($hash, "state", "off");
+				$hash->{STATE} = "off";
+			} elsif ( (hex(substr($ddata,6,2))) == 1) {
+				readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "1");
+				readingsBulkUpdate($hash, "state", "heat");
+				$hash->{STATE} = "heat";
+			} elsif ( (hex(substr($ddata,6,2))) == 2) {
+				readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "2");
+				readingsBulkUpdate($hash, "state", "cool");
+				$hash->{STATE} = "cool";
+			} elsif ( (hex(substr($ddata,6,2))) == 3) {
+				readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "3");
+				readingsBulkUpdate($hash, "state", "auto");
+				$hash->{STATE} = "auto";
+			};
+		};
+		
+	
 	}
-
-#	+++++++ D0_REQ
-	if ($dev_funcion == hex("36")) {
-		readingsBulkUpdate($hash,"cmd", "D0_REQ");
-		Log3 $hash, 5, "N4MODULE -> D0_REQ ($name) (".$hash->{OT}.")";
-		N4HMODULE_Update( $hash );
-	} 
 	
 #	+++++++ D0_SENSOR_ACK
 	if ($dev_funcion == hex("41")) {
+		if( defined($objsrc))	{readingsBulkUpdate($hash,"from", $objsrc);}
+		readingsBulkUpdate($hash,"ddata", $ddata);
 		readingsBulkUpdate($hash,"cmd", "D0_SENSOR_ACK");
-		Log3 $hash, 5, "N4MODULE -> D0_SENSOR_ACK ($name) (".$hash->{OT}.")";
+		Log3 $hash, 5, "N4HMODULE -> D0_SENSOR_ACK ($name) (".$hash->{OT}.")";
 		N4HMODULE_Update( $hash );
 	} 
+
+#	+++++++ D0_LOCK_STATE_ACK
+	if ($dev_funcion == hex("44")) {
+		if( defined($objsrc))	{readingsBulkUpdate($hash,"from", $objsrc);}
+		readingsBulkUpdate($hash,"ddata", $ddata);
+		readingsBulkUpdate($hash,"cmd", "D0_LOCK_STATE_ACK");
+		Log3 $hash, 5, "N4HMODULE -> D0_LOCK_STATE_ACK ($name) (".$hash->{OT}.")";
+	} 
 	
 #	+++++++ D0_VALUE_ACK (101)
 	if ($dev_funcion == hex("65")) {
+		if( defined($objsrc))	{readingsBulkUpdate($hash,"from", $objsrc);}
+		readingsBulkUpdate($hash,"ddata", $ddata);
 		readingsBulkUpdate($hash,"cmd", "D0_VALUE_ACK");
-		my ($valtype, $lastval) = N4HMODULE_paramToText($hash, $ddata);
-		if( defined($lastval))	{	readingsBulkUpdate($hash, $valtype, $lastval);	}
-		
+
+			if ( (hex(substr($ddata,4,2))) == 0) {
+				readingsBulkUpdate($hash, "state", "off");
+				$hash->{STATE} = "off";
+			} elsif ( (hex(substr($ddata,4,2))) == 1) {
+				readingsBulkUpdate($hash, "state", "on");
+				$hash->{STATE} = "on";
+			}
+
+			my ($valtype, $lastval) = N4HMODULE_paramToText($hash, $ddata);
+			readingsBulkUpdate($hash, $valtype, $lastval);	
+			readingsBulkUpdate($hash, "state", $lastval);
+
 	}
 
 #	+++++++ D0_VALUE_REQ
 	if ($dev_funcion == hex("66")) {
+		if( defined($objsrc))	{readingsBulkUpdate($hash,"from", $objsrc);}
+		readingsBulkUpdate($hash,"ddata", $ddata);
 		readingsBulkUpdate($hash,"cmd", "D0_VALUE_REQ");
-		Log3 $hash, 5, "N4MODULE -> D0_VALUE_REQ ($name) (".$hash->{OT}.")";
+		Log3 $hash, 5, "N4HMODULE -> D0_VALUE_REQ ($name) (".$hash->{OT}.")";
 		N4HMODULE_Update( $hash );
 	} 
 
-	#	+++++++ LCD-Text
+	#	+++++++ SetN
 	if ($dev_funcion == hex("3b")) {
-		readingsBulkUpdate($hash,"cmd", "LCD-Text");
+
+		if( defined($objsrc))	{readingsBulkUpdate($hash,"from", $objsrc);}
+		readingsBulkUpdate($hash,"ddata", $ddata);
+		# ot 34 = "TLH-Regler H,Sollwert,Temperatur"
+		if ($ot == 34) {
+			readingsBulkUpdate($hash,"cmd", "SetN");
+			Log3 $hash, 5, "N4HMODULE -> SetN ($name) (".$hash->{OT}.")";
+		
+			if ( (hex(substr($ddata,2,2))) == 0) {
+				readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "0");
+				readingsBulkUpdate($hash, "state", "off");
+				$hash->{STATE} = "off";
+			} elsif ( (hex(substr($ddata,2,2))) == 1) {
+				readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "1");
+				readingsBulkUpdate($hash, "state", "heat");
+				$hash->{STATE} = "heat";
+			} elsif ( (hex(substr($ddata,2,2))) == 2) {
+				readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "2");
+				readingsBulkUpdate($hash, "state", "cool");
+				$hash->{STATE} = "cool";
+			} elsif ( (hex(substr($ddata,2,2))) == 3) {
+				readingsBulkUpdate($hash, "CurrentHeatingCoolingState", "3");
+				readingsBulkUpdate($hash, "state", "auto");
+				$hash->{STATE} = "auto";
+			};
+		} else {
+			readingsBulkUpdate($hash,"cmd", "SetN");
+		}
 	}
 
 	readingsEndUpdate( $hash , 1);
@@ -441,7 +715,7 @@ sub N4HMODULE_paramToText($@) {
 	
 # 	+++++++++++++++++++ Licht analog - IN_HW_NR_IS_LICHT_ANALOG
 	if (hex(substr($ddata,2,2)) == 5 ){
-		$rettype = "brightness";
+		$rettype = "Brightness";
 	}	
 
 # 	+++++++++++++++++++ Uhrzeit - IN_HW_NR_IS_CLOCK
@@ -549,9 +823,9 @@ sub N4HMODULE_Set($@) {
 	return "\"set $a[0]\" needs at least two parameters" if(@a < 2);
 	my $name = shift(@a);
     my $cmd  = shift(@a);
-	my $ext  =  shift(@a);
+	my $ext  = shift(@a);
 	
-	Log3 $hash, 5, "N4MODULE (set): ($name) ($cmd)";
+#	Log3 $hash, 5, "N4HMODULE (set): (Name: $name) (CMD: $cmd) (ext: $ext)";
 
 	my $ot 			= $hash->{OT};
 	my $ipdst		= $hash->{OBJADR};
@@ -564,7 +838,7 @@ sub N4HMODULE_Set($@) {
 	my $devtype = $OT_devices{$ot};
 	  
 
-	if ($ot == 3 || $ot == 4 || $ot == 5 || $ot == 95) {
+	if ($ot == 3 || $ot == 4 || $ot == 5 || $ot == 95 || $ot == 100) {
 
 	for my $field (@{$devtype->{fields}}) {
 	
@@ -586,24 +860,67 @@ sub N4HMODULE_Set($@) {
 		if ($fieldcmd ne "" && $cmd ne "?")  {
 	
 			if (defined($ext)) {
-				
-				
+			
 				if ($cmd eq "pct") {
 					$fieldcmd = "$fieldcmd".sprintf ("%02x", ($ext))."00";
 					$ddata = sprintf ("%02x%s", ((length($fieldcmd))/2), $fieldcmd);
 					readingsSingleUpdate($hash, "pct", "$ext", 1);
-					Log3 $hash, 5, "N4MODULE (set): $name to $cmd ($ext%) ($ddata)";
+					Log3 $hash, 5, "N4HMODULE (set): $name to $cmd ($ext%) ($ddata)";
 				} else {
 					$ddata = sprintf ("%02x%s", (length($fieldcmd)/2), $fieldcmd);
-					Log3 $hash, 5, "N4MODULE (set): $name to $cmd/$ext ($cmd-".join(" ", @sets)."-$devtype-$fieldcmd-$ot)";
+					Log3 $hash, 5, "N4HMODULE (set): $name to $cmd/$ext (".join(" ", @sets).") - $devtype - $fieldcmd - $ot)";
 				}
 				
 			} else {
-				Log3 $hash, 5, "N4MODULE (set): $name to $cmd ($cmd-".join(" ", @sets)."-$devtype-$fieldcmd-$ot)";
-				$ddata = sprintf ("%02x%s", (length($fieldcmd)/2), $fieldcmd);
+			
+				if ($cmd eq "toggle") {
+					$ddata = sprintf ("%02x%s", (length($fieldcmd)/2), $fieldcmd);
+					Log3 $hash, 5, "N4HMODULE (set): $name $cmd ($ddata)";
+				} else {			
+					Log3 $hash, 5, "N4HMODULE (set): $name to $cmd (".join(" ", @sets).") - $devtype - $fieldcmd - $ot)";
+					$ddata = sprintf ("%02x%s", (length($fieldcmd)/2), $fieldcmd);
+					readingsSingleUpdate($hash, "state", "$cmd", 1);
+				}
 			}
 			
-			readingsSingleUpdate($hash, "state", "$cmd", 1);
+			IOWrite($hash, $ipdst, $ddata, 0);
+			return undef;
+		}
+		else {
+			return SetExtensions($hash, join(" ", @sets), $name, $cmd, @a);
+		}
+		
+	} elsif ($ot == 34) {
+	
+		for my $field (@{$devtype->{fields}}) {
+	
+			$setfield  = $field->{set};
+			if (defined($setfield)) {
+		
+				push(@sets,$field->{set});
+			
+				$setfield = ( split /:/, $setfield, 2 )[0];
+
+				if ($setfield eq $cmd) {
+					$fieldname = $field->{text};
+					$fieldcmd  = $field->{cmd};
+					$fieldset  = $field->{set};
+				}
+			}
+		 }	
+		
+		if ($fieldcmd ne "" && $cmd ne "?")  {
+	
+			Log3 $hash, 5, "N4HMODULE (set): $name to $cmd (".join(" ", @sets).") - $devtype - $fieldcmd - $ot)";
+			
+			if ($cmd eq "desired-temperature") {
+				$fieldcmd = "$fieldcmd".sprintf ("%02x", ($ext*10))."00";
+				$ddata = sprintf ("%02x%s", ((length($fieldcmd))/2), $fieldcmd);
+				readingsSingleUpdate($hash, "desired-temperature", "$ext", 1);
+			} else {
+				$ddata = sprintf ("%02x%s", ((length($fieldcmd))/2), $fieldcmd);
+			}
+
 			IOWrite($hash, $ipdst, $ddata, 0);
 			return undef;
 		}
@@ -615,15 +932,16 @@ sub N4HMODULE_Set($@) {
 	elsif ($ot == 24 || $ot == 25 || $ot == 26 || $ot == 240 || $ot == 242 || $ot == 245) {
 
 		if ($cmd ne "?") {
-			Log3 $hash, 5, "N4MODULE (set): $name to $cmd";
+			Log3 $hash, 5, "N4HMODULE (set): $name to $cmd";
 			N4HMODULE_Update($hash, $cmd);
 		}	
 		return undef;
 	}
+	
 	elsif ($ot == 999) {
 
 		if ($cmd ne "?") {
-			Log3 $hash, 5, "N4MODULE (set n56): $name to $cmd";
+			Log3 $hash, 5, "N4HMODULE (set n56): $name to $cmd";
 			N4HMODULE_Update($hash, $cmd);
 		}	
 		return undef;
@@ -633,7 +951,51 @@ sub N4HMODULE_Set($@) {
 	
 }	
 
+##################################################################################
+sub N4HMODULE_Get($$@) {
+##################################################################################
+
+	my ( $hash, $name, $opt, @args ) = @_;
+	
+	my $ot 			= $hash->{OT};
+	my $ipdst		= $hash->{OBJADR};
+	my $ddata 		= "";
+	my $fieldcmd	= "";
+	my $list = "";
+
+	my $list = "desired-temperature:noArg";
 
+	return "\"get $name\" needs at least one argument" unless(defined($opt));
+
+	if (( $ot == 3 ) or
+        ( $ot == 5)) {
+		if ($opt eq "status") {
+			$fieldcmd = "360000";
+			$ddata = sprintf ("%02x%s", ((length($fieldcmd))/2), $fieldcmd);
+			Log3 $hash, 5, "N4HMODULE (get): $opt from $name ($ddata)";
+			IOWrite($hash, $ipdst, $ddata, 0);
+	
+		} else {
+			return "unknown argument $opt, choose one of gettype:noArg status:noArg";
+		}
+	} elsif ( $ot == 34 ) {
+		if ($opt eq "desired-temperature") {
+			$fieldcmd = "360000";
+			$ddata = sprintf ("%02x%s", ((length($fieldcmd))/2), $fieldcmd);
+			Log3 $hash, 5, "N4HMODULE (get): $opt from $name ($ddata)";
+			IOWrite($hash, $ipdst, $ddata, 0);
+	
+		} else {
+			return "unknown argument $opt, choose one of gettype:noArg desired-temperature:noArg";
+		}
+	} else {
+		return "unknown argument, choose one of gettype:noArg";
+	}	
+	return undef;
+		
+ }
+ 
+ 
 ##################################################################################
 sub N4HMODULE_Update($@) {
 ##################################################################################
@@ -642,9 +1004,13 @@ sub N4HMODULE_Update($@) {
 	my $value = shift(@a);
 
 	my $name 		= $hash->{NAME};
-    return unless (defined($hash->{NAME}) and defined $value );
+    return unless (defined($hash->{NAME}) );
 
-	Log3 $hash, 5, "N4MODULE (update): ($name) ($value)";
+	if (defined $value) {
+		Log3 $hash, 5, "N4HMODULE (update): ($name) ($value)";
+	} else {
+		Log3 $hash, 5, "N4HMODULE (update): ($name)";
+	}
 
 	my $ot 			= $hash->{OT};
 	my $ipdst		= $hash->{OBJADR};
@@ -652,7 +1018,19 @@ sub N4HMODULE_Update($@) {
 	my $cs			= "";
 	my $cmd         = "";
 
-	if ($ot == 24) {
+	if ($ot == 3) {
+	
+#	+++++++ 69 D0_STATUS_INFO (105)
+
+			if ( (hex(substr($ddata,6,2))) == 0) {
+				readingsBulkUpdate($hash, "state", "off");
+				$hash->{STATE} = "off";
+			} elsif ( (hex(substr($ddata,6,2))) == 1) {
+				readingsBulkUpdate($hash, "state", "on");
+				$hash->{STATE} = "on";
+			}
+
+	} elsif ($ot == 24) {
 
 #	+++++++ 65 D0_VALUE_ACK (101)
 #	+++++++ 09 Temperatur
@@ -681,7 +1059,7 @@ sub N4HMODULE_Update($@) {
 			$ddata1 = $ddata1.sprintf ("%02X", ( ($cs>>0) & 255 ) );	
 			$ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1);
 
-			Log3 $hash, 5, "N4MODULE (set temperature): $name to $cmd - $ddata, $ddata1, $ipdst";
+			Log3 $hash, 5, "N4HMODULE (set temperature): $name to $cmd - $ddata, $ddata1, $ipdst";
 			IOWrite($hash, 32767, $ddata, $ipdst);
 		}
 		return undef;
@@ -696,10 +1074,10 @@ sub N4HMODULE_Update($@) {
 
 		if (defined $value) {
 		 $cmd = $value; 
-		 readingsSingleUpdate($hash, "brightness", "$cmd", 1);
+		 readingsSingleUpdate($hash, "Brightness", "$cmd", 1);
 		}
 		else {
-  		 ($cmd, undef) = split(/ /, ReadingsVal($name , "brightness","")); }
+  		 ($cmd, undef) = split(/ /, ReadingsVal($name , "Brightness","")); }
 
 		if (defined $cmd) {
 			my $cs = $cmd;
@@ -707,7 +1085,7 @@ sub N4HMODULE_Update($@) {
 			$ddata1 = $ddata1.sprintf ("%02X", ( ($cs) ) );	
 			$ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1);
 
-			Log3 $hash, 5, "N4MODULE (set brightness): $name to $cmd - $ddata, $ddata1, $ipdst";
+			Log3 $hash, 5, "N4HMODULE (set brightness): $name to $cmd - $ddata, $ddata1, $ipdst";
 			IOWrite($hash, 32767, $ddata, $ipdst);
 		}
 		return undef;
@@ -733,7 +1111,41 @@ sub N4HMODULE_Update($@) {
 			$ddata1 = $ddata1.sprintf ("%02X", ( ($cs) ) );	
 			$ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1);
 
-			Log3 $hash, 5, "N4MODULE (set humidity): $name to $cmd - $ddata, $ddata1, $ipdst";
+			Log3 $hash, 5, "N4HMODULE (set humidity): $name to $cmd - $ddata, $ddata1, $ipdst";
+			IOWrite($hash, 32767, $ddata, $ipdst);
+		}
+		return undef;
+	}
+	elsif ($ot == 34) { # TLH-Regler H,Sollwert,Temperatur
+
+#	+++++++ 37 D0_ACTOR_ACK (101)
+
+		Log3 $hash, 5, "N4HMODULE (xxx): $name to $cmd - $ddata, $ipdst";
+		
+		my $ddata1 = "3700";
+		
+		if (defined $value) {
+		 $cmd = $value; 
+		 readingsSingleUpdate($hash, "desired-temp", "$cmd", 1);
+		}
+		else {
+		 ($cmd, undef) = split(/ /, ReadingsVal($name , "desired-temp", "")); } 
+		 
+		if (defined $cmd) {
+
+			if ($cmd >= 0) {
+			$cs = $cmd*16;
+			$ddata1 = $ddata1.sprintf ("%02X", ($cs>>8) );	
+			}
+			elsif ($cmd < 0) { 
+			$cs = 0xff+($cmd*16);
+			$ddata1 = $ddata1.sprintf ("%02X", 0xff );	
+			}
+		
+			$ddata1 = $ddata1.sprintf ("%02X", ( ($cs>>0) & 255 ) );	
+			$ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1);
+
+			Log3 $hash, 5, "N4HMODULE (set desired-temp): $name to $cmd - $ddata, $ddata1, $ipdst";
 			IOWrite($hash, 32767, $ddata, $ipdst);
 		}
 		return undef;
@@ -760,7 +1172,7 @@ sub N4HMODULE_Update($@) {
 			$ddata1 = $ddata1.sprintf ("%02X", ( $cs & 0xff ) );	
 			$ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1);
 
-			Log3 $hash, 5, "N4MODULE (set wind): $name to $cmd - $ddata, $ddata1, $ipdst";
+			Log3 $hash, 5, "N4HMODULE (set wind): $name to $cmd - $ddata, $ddata1, $ipdst";
 			IOWrite($hash, 32767, $ddata, $ipdst);
 		}
 		return undef;
@@ -787,7 +1199,7 @@ sub N4HMODULE_Update($@) {
 			$ddata1 = $ddata1.sprintf ("%02X", ( $cs & 0xff ) );	
 			$ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1);
 
-			Log3 $hash, 5, "N4MODULE (set pressure): $name to $cmd - $ddata, $ddata1, $ipdst";
+			Log3 $hash, 5, "N4HMODULE (set pressure): $name to $cmd - $ddata, $ddata1, $ipdst";
 			IOWrite($hash, 32767, $ddata, $ipdst);
 		}
 		return undef;
@@ -814,7 +1226,7 @@ sub N4HMODULE_Update($@) {
 			$ddata1 = $ddata1.sprintf ("%02X", ( $cs & 0xff ) );	
 			$ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1);
 
-			Log3 $hash, 5, "N4MODULE (set rain): $name to $cmd - $ddata, $ddata1, $ipdst";
+			Log3 $hash, 5, "N4HMODULE (set rain): $name to $cmd - $ddata, $ddata1, $ipdst";
 			IOWrite($hash, 32767, $ddata, $ipdst);
 		}
 		return undef;
@@ -838,7 +1250,7 @@ sub N4HMODULE_Update($@) {
 			$ddata = sprintf ("%02x%s", (length($ddata1)/2), $ddata1);
 
 			$ipdst = 1;
-			Log3 $hash, 5, "N4MODULE (set N56): $name to $cmd - $ddata, $ddata1, $ipdst, $n56";
+			Log3 $hash, 5, "N4HMODULE (set N56): $name to $cmd - $ddata, $ddata1, $ipdst, $n56";
 			IOWrite($hash, 1, $ddata, $ipdst);
 		
 		return undef;
@@ -954,6 +1366,9 @@ sub N4HMODULE_ParseN56($) {
 	 <li> 24 - Measurement,Temperature</li>
 	 <li> 25 - Measurement,Brightness</li>
 	 <li> 26 - Measurement,Humidity</li>
+	 <li> 34 - TLH-Regler H,Sollwert,Temperatur</li>
+	 <li> 95 - Ausgang, Jal, Motor AJ3</li>
+	 <li>210 - RF Reader</li>
 	 <li>240 - Measurement,Wind</li>
 	 <li>242 - Measurement,Pressure</li>
 	 <li>245 - Measurement,Rain</li>
@@ -1005,6 +1420,9 @@ sub N4HMODULE_ParseN56($) {
 	 <li> 24 - Messwert,Temperatur</li>
 	 <li> 25 - Messwert,Helligkeit</li>
 	 <li> 26 - Messwert,Feuchte</li>
+	 <li> 34 - TLH-Regler H,Sollwert,Temperatur</li>
+	 <li> 95 - Ausgang, Jal, Motor AJ3</li>
+	 <li>210 - RF Reader</li>
 	 <li>240 - Messwert,Wind</li>
 	 <li>242 - Messwert,Luftdruck</li>
 	 <li>245 - Messwert,Regenmenge</li>

+ 32 - 17
fhem/core/FHEM/21_SONOSPLAYER.pm

@@ -1,9 +1,9 @@
 ########################################################################################
 #
-# SONOSPLAYER.pm (c) by Reiner Leins, July 2017
+# SONOSPLAYER.pm (c) by Reiner Leins, March 2018
 # rleins at lmsoft dot de
 #
-# $Id: 21_SONOSPLAYER.pm 14715 2017-07-14 10:39:57Z Reinerlein $
+# $Id: 21_SONOSPLAYER.pm 16376 2018-03-10 16:21:04Z Reinerlein $
 #
 # FHEM module to work with Sonos-Zoneplayers
 #
@@ -82,6 +82,7 @@ my %gets = (
 	'QueueWithCovers' => '',
 	'Alarm' => 'ID',
 	'EthernetPortStatus' => 'PortNum(0..3)',
+	'WifiPortStatus' => '',
 	'SupportLinks' => '',
 	'PossibleRoomIcons' => '',
 	'SearchlistCategories' => ''
@@ -209,7 +210,7 @@ sub SONOSPLAYER_Define ($$) {
 	my ($hash, $def) = @_;
 	
 	# Check if we just want a modify...
-	if ($hash->{NAME}) {
+	if (defined($hash->{OLDDEF})) {
 		SONOS_Log undef, 1, 'Modify SonosPlayer-Device: '.$hash->{NAME};
 		
 		# Alle Timer entfernen...
@@ -258,7 +259,7 @@ sub SONOSPLAYER_Detail($$$;$) {
 	return '' if (!ReadingsVal($d, 'IsMaster', 0) || (ReadingsVal($d, 'playerType', '') eq 'ZB100'));
 	
 	# Open incl. Inform-Div
-	my $html .= '<html><div informId="'.$d.'-display_covertitle">';
+	my $html .= '<html><div informid="'.$d.'-display_covertitle">';
 	
 	# Cover-/TitleView
 	$html .= '<div style="border: 1px solid gray; border-radius: 10px; padding: 5px;">';
@@ -296,29 +297,26 @@ sub SONOSPLAYER_Detail($$$;$) {
 ########################################################################################
 sub SONOSPLAYER_Attribute($$$@) {
 	my ($mode, $devName, $attrName, $attrValue) = @_;
+	my $hash = SONOS_getSonosPlayerByName($devName);
 	
 	if ($mode eq 'set') {
 		if ($attrName =~ m/^(min|max)Volume(|Headphone)$/) {
-			my $hash = SONOS_getSonosPlayerByName($devName);
-			
 			SONOS_DoWork($hash->{UDN}, 'setMinMaxVolumes', $attrName, $attrValue);
+		} elsif ($attrName eq 'disable' && $attrValue == 0) {
+			SONOS_DoWork($hash->{UDN}, 'setAttribute', $attrName, $attrValue);
+			SONOS_DoWork('SONOS', 'rescanNetwork');
 		} elsif ($attrName =~ m/^(getTitleInfoFromMaster|stopSleeptimerInAction|saveSleeptimerInAction)$/) {
-			my $hash = SONOS_getSonosPlayerByName($devName);
-			
 			SONOS_DoWork($hash->{UDN}, 'setAttribute', $attrName, $attrValue);
 		}
 	} elsif ($mode eq 'del') {
 		if ($attrName =~ m/^minVolume(|Headphone)$/) {
-			my $hash = SONOS_getSonosPlayerByName($devName);
-			
 			SONOS_DoWork($hash->{UDN}, 'setMinMaxVolumes', $attrName, 0);
 		} elsif ($attrName =~ m/^maxVolume(|Headphone)$/) {
-			my $hash = SONOS_getSonosPlayerByName($devName);
-			
 			SONOS_DoWork($hash->{UDN}, 'setMinMaxVolumes', $attrName, 100);
+		} elsif ($attrName eq 'disable') {
+			SONOS_DoWork($hash->{UDN}, 'deleteAttribute', $attrName);
+			SONOS_DoWork('SONOS', 'rescanNetwork');
 		} elsif ($attrName =~ m/^(getTitleInfoFromMaster|stopSleeptimerInAction|saveSleeptimerInAction)$/) {
-			my $hash = SONOS_getSonosPlayerByName($devName);
-			
 			SONOS_DoWork($hash->{UDN}, 'deleteAttribute', $attrName);
 		}
 	}
@@ -421,7 +419,7 @@ sub SONOSPLAYER_SimulateCurrentTrackPosition() {
 	
 	my $trackPositionSec = 0;
 	if (ReadingsVal($hash->{NAME}, 'transportState', 'STOPPED') eq 'PLAYING') {
-		$trackPositionSec = time - SONOS_GetTimeFromString(ReadingsTimestamp($hash->{NAME}, 'currentTrackPositionSec', 0)) + ReadingsVal($hash->{NAME}, 'currentTrackPositionSec', 0);
+		$trackPositionSec = sprintf("%.0f", time - SONOS_GetTimeFromString(ReadingsTimestamp($hash->{NAME}, 'currentTrackPositionSec', 0)) + ReadingsVal($hash->{NAME}, 'currentTrackPositionSec', 0));
 	} else {
 		$trackPositionSec = ReadingsVal($hash->{NAME}, 'currentTrackPositionSec', 0);
 	}
@@ -508,7 +506,7 @@ sub SONOSPLAYER_Get($@) {
 	} elsif (lc($reading) eq 'ethernetportstatus') {
 		my $portNum = $a[2];
 		
-		SONOS_readingsSingleUpdate($hash, 'LastActionResult', 'Portstatus properly returned', 1);
+		SONOS_readingsSingleUpdate($hash, 'LastActionResult', 'Ethernet-Portstatus properly returned', 1);
 	
 		my $url = ReadingsVal($name, 'location', '');
 		$url =~ s/(^http:\/\/.*?)\/.*/$1\/status\/enetports/;
@@ -516,6 +514,17 @@ sub SONOSPLAYER_Get($@) {
 		my $statusPage = GetFileFromURL($url);
 		return (($1 == 0) ? 'Inactive' : 'Active') if ($statusPage =~ m/<Port port='$portNum'><Link>(\d+)<\/Link><Speed>.*?<\/Speed><\/Port>/i);
 		return 'Inactive';
+	} elsif (lc($reading) eq 'wifiportstatus') {
+		my $portNum = $a[2];
+		
+		SONOS_readingsSingleUpdate($hash, 'LastActionResult', 'Wifi-Portstatus properly returned', 1);
+	
+		my $url = ReadingsVal($name, 'location', '');
+		$url =~ s/(^http:\/\/.*?)\/.*/$1\/status\/ifconfig/;
+		
+		my $statusPage = GetFileFromURL($url);
+		return 'Active' if ($statusPage =~ m/(ath0 +?Link encap:Ethernet)/i);
+		return 'Inactive';
 	} elsif (lc($reading) eq 'supportlinks') {
 		my $playerurl = ReadingsVal($name, 'location', '');
 		$playerurl =~ s/(^http:\/\/.*?)\/.*/$1/;
@@ -902,7 +911,7 @@ sub SONOSPLAYER_Set($@) {
 				my $udnShort = $1 if ($dHash->{UDN} =~ m/(.*)_MR/); 
 				
 				# Wenn dieses Quell-Device eine Playbar ist, dann den optischen Eingang als Quelle wählen...
-				if (ReadingsVal($dHash->{NAME}, 'playerType', '') eq 'S9') {
+				if ((ReadingsVal($dHash->{NAME}, 'playerType', '') eq 'S9') || (ReadingsVal($dHash->{NAME}, 'playerType', '') eq 'S11')) {
 					# Das ganze geht nur bei dem eigenen Eingang, ansonsten eine Gruppenwiedergabe starten
 					if ($dHash->{NAME} eq $hash->{NAME}) {
 						$value = 'x-sonos-htastream:'.$udnShort.':spdif';
@@ -1556,6 +1565,9 @@ sub SONOSPLAYER_Log($$$) {
 <li><a name="SONOSPLAYER_getter_SupportLinks">
 <b><code>SupportLinks</code></b></a>
 <br /> Shows a list with direct links to the player-support-sites.</li>
+<li><a name="SONOSPLAYER_getter_WifiPortStatus">
+<b><code>WifiPortStatus</code></b></a>
+<br /> Gets the Wifi-Portstatus. Can be 'Active' or 'Inactive'.</li>
 </ul></li>
 <li><b>Lists</b><ul>
 <li><a name="SONOSPLAYER_getter_Favourites">
@@ -1937,6 +1949,9 @@ Here an event is defined, where in time of 2 seconds the Mute-Button has to be p
 <li><a name="SONOSPLAYER_getter_SupportLinks">
 <b><code>SupportLinks</code></b></a>
 <br /> Ausnahmefall. Diese Get-Anweisung liefert eine Liste mit passenden Links zu den Supportseiten des Player.</li>
+<li><a name="SONOSPLAYER_getter_WifiPortStatus">
+<b><code>WifiPortStatus</code></b></a>
+<br /> Liefert den Wifi-Portstatus. Kann 'Active' oder 'Inactive' liefern.</li>
 </ul></li>
 <li><b>Listen</b><ul>
 <li><a name="SONOSPLAYER_getter_Favourites">

File diff suppressed because it is too large
+ 1335 - 572
fhem/core/FHEM/22_HOMEMODE.pm


+ 322 - 182
fhem/core/FHEM/23_LUXTRONIK2.pm

@@ -1,5 +1,5 @@
 ###############################################################
-# $Id: 23_LUXTRONIK2.pm 15291 2017-10-19 19:29:29Z tupol $Date: $
+# $Id: 23_LUXTRONIK2.pm 16339 2018-03-06 09:55:10Z tupol $Date: $
 #
 #  23_LUXTRONIK2.pm 
 #
@@ -33,12 +33,14 @@
 ##############################################
 package main;
 
+my $missingModul = "";
+
 use strict;
 use warnings;
 use Blocking;
 use IO::Socket; 
 use Time::HiRes qw/ time /;
-use Net::Telnet;
+eval "use Net::Telnet;1" or $missingModul .= "Net::Telnet ";
 
 sub LUXTRONIK2_doStatisticThermalPower ($$$$$$$$$);
 sub LUXTRONIK2_doStatisticMinMax ($$$);
@@ -46,11 +48,12 @@ sub LUXTRONIK2_doStatisticMinMaxSingle ($$$$);
 sub LUXTRONIK2_storeReadings ($$$$$$);
 sub LUXTRONIK2_doStatisticDelta ($$$$$) ;
 sub LUXTRONIK2_doStatisticDeltaSingle ($$$$$$$);
+sub LUXTRONIK2_readData ($);
 
 
 #List of firmware versions that are known to be compatible with this modul
-my $testedFirmware = "#V1.51#V1.54C#V1.60#V1.61#V1.64#V1.69#V1.70#V1.73#V1.77#V1.80#";
-my $compatibleFirmware = "#V1.51#V1.54C#V1.60#V1.61#V1.64#V1.69#V1.70#V1.73#V1.77#V1.80#";
+my $testedFirmware = "#V1.51#V1.54C#V1.60#V1.61#V1.64#V1.69#V1.70#V1.73#V1.77#V1.80#V1.81#";
+my $compatibleFirmware = "#V1.51#V1.54C#V1.60#V1.61#V1.64#V1.69#V1.70#V1.73#V1.77#V1.80#V1.81#";
 
 sub ##########################################
 LUXTRONIK2_Log($$$)
@@ -81,13 +84,15 @@ LUXTRONIK2_Initialize($)
                  "allowSetParameter:0,1 ".
                  "autoSynchClock:slider,10,5,300 ".
                  "boilerVolumn ".
+                 "compressor2ElectricalPowerWatt ".
+                 "doStatistics:0,1 ".
                  "heatPumpElectricalPowerFactor ".
                  "heatPumpElectricalPowerWatt ".
                  "heatRodElectricalPowerWatt ".
-                 "compressor2ElectricalPowerWatt ".
-                 "doStatistics:0,1 ".
                  "ignoreFirmwareCheck:0,1 ".
                  "statusHTML ".
+                 "userHeatpumpParameters ".
+                 "userHeatpumpValues ".
                 $readingFnAttributes;
 }
 
@@ -139,7 +144,7 @@ LUXTRONIK2_Define($$)
   $hash->{fhem}{heatingPumpLastStop} = time();
   $hash->{fhem}{heatingPumpLastRun} = time();
  
-  $hash->{fhem}{modulVersion} = '$Date: 2017-10-19 21:29:29 +0200 (Thu, 19 Oct 2017) $';
+  $hash->{fhem}{modulVersion} = '$Date: 2018-03-06 10:55:10 +0100 (Tue, 06 Mar 2018) $';
        
   return undef;
 }
@@ -301,6 +306,8 @@ LUXTRONIK2_Set($$@)
           ." boostHotWater"
           ." heatingCurveEndPoint"
           ." heatingCurveOffset"
+          ." heatSourceDefrostAirEnd"
+          ." heatSourceDefrostAirThreshold"
           ." hotWaterCircPumpDeaerate:on,off"
           ." hotWaterTemperatureTarget "
           ." resetStatistics:all,statBoilerGradientCoolDownMin,statAmbientTemp...,statElectricity...,statHours...,statHeatQ..."
@@ -343,9 +350,33 @@ LUXTRONIK2_Get($$@)
          return LUXTRONIK2_getHeatingCurveReturnTemperature ( $hash, $val[0], $heatingCurveEndPoint, $heatingCurveOffset);
       }
    }
+   elsif( $cmd eq 'rawData') {
+		my ($state, $msg , $heatpump_values, $heatpump_parameters, $heatpump_visibility) = LUXTRONIK2_ReadData ($name);
+		if ($state != 1) {
+		  return $msg;
+		}
+		else {
+		  my $index = 0;
+		  $resultStr ="Heatpump Values (".scalar(@$heatpump_values).")\n";
+		  foreach (@$heatpump_values) {
+			$resultStr .= sprintf ("|%4s:%10s ", $index, $_);
+			$index++;
+			$resultStr .= "|\n"		if $index % 10 == 0;
+		  }
+		  $index = 0;
+		  $resultStr .="\n\nHeatpump Parameters (".scalar(@$heatpump_parameters).")\n";
+		  foreach (@$heatpump_parameters) {
+			$resultStr .= sprintf ("|%4s:%10s ", $index, $_);
+			$index++;
+			$resultStr .= "|\n"		if $index % 10 == 0;
+		  }
+		  return $resultStr;
+		}
+   }
 
    my $list = "heatingCurveParameter "
-            . "heatingCurveReturnTemperature ";
+            . "heatingCurveReturnTemperature "
+            . "rawData ";
    
           
   return "Unknown argument $cmd, choose one of $list";
@@ -373,171 +404,21 @@ sub LUXTRONIK2_DoUpdate($)
   my $hash = $defs{$name};
   my $host = $hash->{HOST};
   my $port = $hash->{PORT};
-  my @heatpump_values;
-  my @heatpump_parameters;
-  my @heatpump_visibility;
+  
   my $count=0;
   my $result="";
-  my $readingStartTime = time();
-  
-  LUXTRONIK2_Log $name, 5, "Opening connection to $host:$port";
-  my $socket = new IO::Socket::INET (  
-                  PeerAddr => $host, 
-                  PeerPort => $port,
-                   #   Type = SOCK_STREAM, # probably needed on some systems
-                   Proto => 'tcp'
-      );
-  if (!$socket) {
-      LUXTRONIK2_Log $name, 1, "Could not open connection to host $host:$port";
-      return "$name|0|Can't connect to $host:$port";
-  }
-  $socket->autoflush(1);
-  
-############################ 
-#Fetch operational values (FOV)
-############################ 
-  LUXTRONIK2_Log $name, 5, "Ask host for operational values";
-  $socket->send( pack( "N2", (3004,0) ) );
-
-  LUXTRONIK2_Log $name, 5, "Start to receive operational values";
- #(FOV) read first 4 bytes of response -> should be request_echo = 3004
-  $socket->recv( $result,4, MSG_WAITALL );
-  $count = unpack("N", $result);
-  if($count != 3004) {
-      LUXTRONIK2_Log $name, 2, "Fetching operational values - wrong echo of request 3004: ".length($result)." -> ".$count;
-       $socket->close();
-      return "$name|0|3004 != $count";
-  }
- 
- #(FOV) read next 4 bytes of response -> should be status = 0
-  $socket->recv($result,4, MSG_WAITALL );
-  $count = unpack("N", $result);
-  if($count > 0) {
-      LUXTRONIK2_Log $name, 4, "Parameter on target changed, restart parameter reading after 5 seconds";
-     $socket->close();
-      return "$name|2|Status = $count - parameter on target changed, restart device reading after 5 seconds";
-  }
-  
- #(FOV) read next 4 bytes of response -> should be count_calc_values > 0
-  $socket->recv($result,4, MSG_WAITALL );
-  my $count_calc_values = unpack("N", $result);
-  if($count_calc_values == 0) {
-      LUXTRONIK2_Log $name, 2, "Fetching operational values - 0 values announced: ".length($result)." -> ".$count_calc_values;
-     $socket->close();
-      return "$name|0|0 values read";
-  }
-  
- #(FOV) read remaining response -> should be previous number of parameters
-  $socket->recv( $result, $count_calc_values*4, MSG_WAITALL ); 
-  if( length($result) != $count_calc_values*4 ) {
-      LUXTRONIK2_Log $name, 1, "Operational values length check: ".length($result)." should have been ". $count_calc_values * 4;
-      $socket->close();
-      return "$name|0|Number of values read mismatch ( $!)\n";
-  }
-  
- #(FOV) unpack response in array
-  @heatpump_values = unpack("N$count_calc_values", $result);
-  if(scalar(@heatpump_values) != $count_calc_values) {
-      LUXTRONIK2_Log $name, 2, "Unpacking problem by operation values: ".scalar(@heatpump_values)." instead of ".$count_calc_values;
-      $socket->close();
-      return "$name|0|Unpacking problem of operational values";
-  
-  }
-
-  LUXTRONIK2_Log $name, 5, "$count_calc_values operational values received";
- 
-############################ 
-#Fetch set parameters (FSP)
-############################ 
-  LUXTRONIK2_Log $name, 5, "Ask host for set parameters";
-  $socket->send( pack( "N2", (3003,0) ) );
-
-  LUXTRONIK2_Log $name, 5, "Start to receive set parameters";
- #(FSP) read first 4 bytes of response -> should be request_echo=3003
-  $socket->recv($result,4, MSG_WAITALL );
-  $count = unpack("N", $result);
-  if($count != 3003) {
-      LUXTRONIK2_Log $name, 2, "Wrong echo of request 3003: ".length($result)." -> ".$count;
-      $socket->close();
-      return "$name|0|3003 != 3003";
-  }
+  my $firstLoop;
   
- #(FSP) read next 4 bytes of response -> should be number_of_parameters > 0
-  $socket->recv($result,4, MSG_WAITALL );
-  my $count_set_parameter = unpack("N", $result);
-  if($count_set_parameter == 0) {
-      LUXTRONIK2_Log $name, 2, "0 parameter read: ".length($result)." -> ".$count_set_parameter;
-     $socket->close();
-      return "$name|0|0 parameter read";
-  }
-  
- #(FSP) read remaining response -> should be previous number of parameters
-   $socket->recv( $result, $count_set_parameter*4, MSG_WAITALL ); 
-
-  if( length($result) != $count_set_parameter*4 ) {
-     LUXTRONIK2_Log $name, 1, "Parameter length check: ".length($result)." should have been ". ($count_set_parameter * 4);
-     $socket->close();
-      return "$name|0|Number of parameters read mismatch ( $!)\n";
-  }
-
-  @heatpump_parameters = unpack("N$count_set_parameter", $result);
-  if(scalar(@heatpump_parameters) != $count_set_parameter) {
-      LUXTRONIK2_Log $name, 2, "Unpacking problem by set parameter: ".scalar(@heatpump_parameters)." instead of ".$count_set_parameter;
-     $socket->close();
-      return "$name|0|Unpacking problem of set parameters";
-  }
-
-  LUXTRONIK2_Log $name, 5, "$count_set_parameter set values received";
-
-############################ 
-#Fetch Visibility Attributes (FVA)
-############################ 
-  LUXTRONIK2_Log $name, 5, "Ask host for visibility attributes";
-  $socket->send( pack( "N2", (3005,0) ) );
-
-  LUXTRONIK2_Log $name, 5, "Start to receive visibility attributes";
- #(FVA) read first 4 bytes of response -> should be request_echo=3005
-  $socket->recv($result,4, MSG_WAITALL );
-  $count = unpack("N", $result);
-  if($count != 3005) {
-      LUXTRONIK2_Log $name, 2, "Wrong echo of request 3005: ".length($result)." -> ".$count;
-      $socket->close();
-      return "$name|0|3005 != $count";
-  }
+  # Read raw data and handle error return
+  my $readingStartTime = time();
+  my ($state, $msg , $retValues, $retParameters, $retVisibility) = LUXTRONIK2_ReadData ($name);
+  my $readingEndTime = time();
   
- #(FVA) read next 4 bytes of response -> should be number_of_Visibility_Attributes > 0
-  $socket->recv($result,4, MSG_WAITALL );
-  my $countVisibAttr = unpack("N", $result);
-  if($countVisibAttr == 0) {
-      LUXTRONIK2_Log $name, 2, "0 visibility attributes announced: ".length($result)." -> ".$countVisibAttr;
-      $socket->close();
-      return "$name|0|0 visibility attributes announced";
-  }
+  return ("$name|$state|$msg")      if $state != 1 ;
+  my @heatpump_values = @$retValues;
+  my @heatpump_parameters = @$retParameters;
+  my @heatpump_visibility = @$retVisibility;
   
- #(FVA) read remaining response bytewise -> should be previous number of parameters
-  $socket->recv( $result, $countVisibAttr, MSG_WAITALL ); 
-   if( length( $result ) != $countVisibAttr ) {
-      LUXTRONIK2_Log $name, 1, "Visibility attributes length check: ".length($result)." should have been ". $countVisibAttr;
-      $socket->close();
-      return "$name|0|Number of Visibility attributes read mismatch ( $!)\n";
-   }
-
-  @heatpump_visibility = unpack("C$countVisibAttr", $result);
-  if(scalar(@heatpump_visibility) != $countVisibAttr) {
-      LUXTRONIK2_Log $name, 2, "Unpacking problem by visibility attributes: ".scalar(@heatpump_visibility)." instead of ".$countVisibAttr;
-      $socket->close();
-      return "$name|0|Unpacking problem of visibility attributes";
-  }
-
-  LUXTRONIK2_Log $name, 5, "$countVisibAttr visibility attributs received";
-
-####################################  
-
-  LUXTRONIK2_Log $name, 5, "Closing connection to host $host";
-  $socket->close();
-
-  my $readingEndTime = time();
-
 #return certain readings for further processing
   # 0 - name
   my $return_str="$name";
@@ -632,9 +513,9 @@ sub LUXTRONIK2_DoUpdate($)
   # 43 - bivalentLevel
   $return_str .= "|".$heatpump_values[79];
   # 44 - Number of calculated values
-  $return_str .= "|".$count_calc_values;
+  $return_str .= "|".scalar(@heatpump_values);
   # 45 - Number of set parameters
-  $return_str .= "|".$count_set_parameter;
+  $return_str .= "|".scalar(@heatpump_parameters);
   # 46 - opStateHeating
   $return_str .= "|".$heatpump_values[125];
   # 47 - deltaHeatingReduction
@@ -650,7 +531,7 @@ sub LUXTRONIK2_DoUpdate($)
   # 52 - counterHoursSolar
   $return_str .= "|". ($heatpump_visibility[248]==1 ? $heatpump_values[161] : "no");
   # 53 - Number of visibility attributes
-  $return_str .= "|".$countVisibAttr;
+  $return_str .= "|".scalar(@heatpump_visibility);
   # 54 - returnTemperatureSetBack
   $return_str .= "|".$heatpump_parameters[1];
   # 55 - mixer1FlowTemperature
@@ -698,7 +579,26 @@ sub LUXTRONIK2_DoUpdate($)
    $return_str .= "|". ($heatpump_visibility[63]==1 ? $heatpump_values[52] : "no");
   # 75 - 2ndHeatSource1
     $return_str .= "|". ($heatpump_visibility[59]==1 ? $heatpump_values[48] : "no");
- 
+  # 76 userReadings (attributs: userHeatpumpParameters and userHeatpumpValues)
+    $return_str .= "|";
+    $firstLoop = 1;
+    my @userReadings = split /,/, AttrVal( $name, "userHeatpumpParameters", "" );
+    foreach (@userReadings) {
+      $return_str .= ","     unless $firstLoop;
+      $firstLoop = 0;
+      my ($rIndex, $rName) = split / /, trim($_);
+      $rName = "userParameter$rIndex"  if $rName eq "";
+      $return_str .= $rName." ".$heatpump_parameters[$rIndex];
+    }
+    @userReadings = split /,/, AttrVal( $name, "userHeatpumpValues", "" );
+    foreach (@userReadings) {
+      $return_str .= ","     unless $firstLoop;
+      $firstLoop = 0;
+      my ($rIndex, $rName) = split / /, trim($_);
+      $rName = "userValue$rIndex"  if $rName eq "";
+      $return_str .= $rName." ".$heatpump_values[$rIndex];
+    }
+
    return $return_str;
 }
 
@@ -721,7 +621,7 @@ LUXTRONIK2_UpdateDone($)
 
   my $cop = 0;
 
-  LUXTRONIK2_Log $hash, 5, $string;
+  LUXTRONIK2_Log $hash, 4, $string;
 
   #Define Status Messages
   my %wpOpStat1 = ( 0 => "Waermepumpe laeuft",
@@ -1120,6 +1020,7 @@ LUXTRONIK2_UpdateDone($)
      $value = $wpType{$a[31]};
      $value = "unbekannt (".$a[31].")" unless $value;
      readingsBulkUpdate($hash,"typeHeatpump",$value);
+     $hash->{MODEL} = $value;
      readingsBulkUpdate($hash,"typeSerial",$a[65])       if $a[65] ne "";
 
    # Solar
@@ -1168,7 +1069,16 @@ LUXTRONIK2_UpdateDone($)
          readingsBulkUpdate($hash, "heatingCycle", "discontinued"); 
       }
          
-      readingsEndUpdate($hash,1);
+      # 76 userHeatpumpParameters
+      if (defined $a[76]) {
+        my @userReadings = split /,/, $a[76];
+        foreach (@userReadings) {
+          my( $rName, $rValue) = split / /, $_;
+          readingsBulkUpdate($hash, $rName, $rValue);
+        }
+      }
+          
+     readingsEndUpdate($hash,1);
      
      $hash->{helper}{fetched_calc_values} = $a[44];
      $hash->{helper}{fetched_parameters} = $a[45];
@@ -1216,6 +1126,182 @@ LUXTRONIK2_UpdateAborted($)
   LUXTRONIK2_Log $hash, 1, "Timeout when connecting to host $host";
 }
 
+########################################
+sub LUXTRONIK2_ReadData($)
+{
+  my ($name) = @_;
+  my $hash = $defs{$name};
+  my $host = $hash->{HOST};
+  my $port = $hash->{PORT};
+  my @heatpump_values;
+  my @heatpump_parameters;
+  my @heatpump_visibility;
+  my $count=0;
+  my $result="";
+  my $readingStartTime = time();
+  
+  LUXTRONIK2_Log $name, 5, "Opening connection to $host:$port";
+  my $socket = new IO::Socket::INET (  
+                  PeerAddr => $host, 
+                  PeerPort => $port,
+                   #   Type = SOCK_STREAM, # probably needed on some systems
+                   Proto => 'tcp'
+      );
+  if (!$socket) {
+      LUXTRONIK2_Log $name, 1, "Could not open connection to host $host:$port";
+      return (0, "Can't connect to $host:$port");
+  }
+  $socket->autoflush(1);
+  
+############################ 
+#Fetch operational values (FOV)
+############################ 
+  LUXTRONIK2_Log $name, 5, "Ask host for operational values";
+  $socket->send( pack( "N2", (3004,0) ) );
+
+  LUXTRONIK2_Log $name, 5, "Start to receive operational values";
+ #(FOV) read first 4 bytes of response -> should be request_echo = 3004
+  $socket->recv( $result,4, MSG_WAITALL );
+  $count = unpack("N", $result);
+  if($count != 3004) {
+      LUXTRONIK2_Log $name, 2, "Fetching operational values - wrong echo of request 3004: ".length($result)." -> ".$count;
+       $socket->close();
+      return (0, "3004 != $count");
+  }
+ 
+ #(FOV) read next 4 bytes of response -> should be status = 0
+  $socket->recv($result,4, MSG_WAITALL );
+  $count = unpack("N", $result);
+  if($count > 0) {
+      LUXTRONIK2_Log $name, 4, "Parameter on target changed, restart parameter reading after 5 seconds";
+     $socket->close();
+      return (2, "Status = $count - parameter on target changed, restart device reading after 5 seconds");
+  }
+  
+ #(FOV) read next 4 bytes of response -> should be count_calc_values > 0
+  $socket->recv($result,4, MSG_WAITALL );
+  my $count_calc_values = unpack("N", $result);
+  if($count_calc_values == 0) {
+      LUXTRONIK2_Log $name, 2, "Fetching operational values - 0 values announced: ".length($result)." -> ".$count_calc_values;
+     $socket->close();
+      return (0, "0 values read");
+  }
+  
+ #(FOV) read remaining response -> should be previous number of parameters
+  $socket->recv( $result, $count_calc_values*4, MSG_WAITALL ); 
+  if( length($result) != $count_calc_values*4 ) {
+      LUXTRONIK2_Log $name, 1, "Operational values length check: ".length($result)." should have been ". $count_calc_values * 4;
+      $socket->close();
+      return (0, "Number of values read mismatch ( $!)\n");
+  }
+  
+ #(FOV) unpack response in array
+  @heatpump_values = unpack("N$count_calc_values", $result);
+  if(scalar(@heatpump_values) != $count_calc_values) {
+      LUXTRONIK2_Log $name, 2, "Unpacking problem by operation values: ".scalar(@heatpump_values)." instead of ".$count_calc_values;
+      $socket->close();
+      return (0, "Unpacking problem of operational values");
+  }
+
+  LUXTRONIK2_Log $name, 5, "$count_calc_values operational values received";
+ 
+############################ 
+#Fetch set parameters (FSP)
+############################ 
+  LUXTRONIK2_Log $name, 5, "Ask host for set parameters";
+  $socket->send( pack( "N2", (3003,0) ) );
+
+  LUXTRONIK2_Log $name, 5, "Start to receive set parameters";
+ #(FSP) read first 4 bytes of response -> should be request_echo=3003
+  $socket->recv($result,4, MSG_WAITALL );
+  $count = unpack("N", $result);
+  if($count != 3003) {
+      LUXTRONIK2_Log $name, 2, "Wrong echo of request 3003: ".length($result)." -> ".$count;
+      $socket->close();
+      return (0, "3003 != 3003");
+  }
+  
+ #(FSP) read next 4 bytes of response -> should be number_of_parameters > 0
+  $socket->recv($result,4, MSG_WAITALL );
+  my $count_set_parameter = unpack("N", $result);
+  if($count_set_parameter == 0) {
+      LUXTRONIK2_Log $name, 2, "0 parameter read: ".length($result)." -> ".$count_set_parameter;
+     $socket->close();
+      return (0, "0 parameter read");
+  }
+  
+ #(FSP) read remaining response -> should be previous number of parameters
+   $socket->recv( $result, $count_set_parameter*4, MSG_WAITALL ); 
+
+  if( length($result) != $count_set_parameter*4 ) {
+     LUXTRONIK2_Log $name, 1, "Parameter length check: ".length($result)." should have been ". ($count_set_parameter * 4);
+     $socket->close();
+      return (0, "Number of parameters read mismatch ( $!)\n");
+  }
+
+  @heatpump_parameters = unpack("N$count_set_parameter", $result);
+  if(scalar(@heatpump_parameters) != $count_set_parameter) {
+      LUXTRONIK2_Log $name, 2, "Unpacking problem by set parameter: ".scalar(@heatpump_parameters)." instead of ".$count_set_parameter;
+     $socket->close();
+      return (0, "Unpacking problem of set parameters");
+  }
+
+  LUXTRONIK2_Log $name, 5, "$count_set_parameter set values received";
+
+############################ 
+#Fetch Visibility Attributes (FVA)
+############################ 
+  LUXTRONIK2_Log $name, 5, "Ask host for visibility attributes";
+  $socket->send( pack( "N2", (3005,0) ) );
+
+  LUXTRONIK2_Log $name, 5, "Start to receive visibility attributes";
+ #(FVA) read first 4 bytes of response -> should be request_echo=3005
+  $socket->recv($result,4, MSG_WAITALL );
+  $count = unpack("N", $result);
+  if($count != 3005) {
+      LUXTRONIK2_Log $name, 2, "Wrong echo of request 3005: ".length($result)." -> ".$count;
+      $socket->close();
+      return (0, "3005 != $count");
+  }
+  
+ #(FVA) read next 4 bytes of response -> should be number_of_Visibility_Attributes > 0
+  $socket->recv($result,4, MSG_WAITALL );
+  my $countVisibAttr = unpack("N", $result);
+  if($countVisibAttr == 0) {
+      LUXTRONIK2_Log $name, 2, "0 visibility attributes announced: ".length($result)." -> ".$countVisibAttr;
+      $socket->close();
+      return (0, "0 visibility attributes announced");
+  }
+  
+ #(FVA) read remaining response bytewise -> should be previous number of parameters
+  $socket->recv( $result, $countVisibAttr, MSG_WAITALL ); 
+   if( length( $result ) != $countVisibAttr ) {
+      LUXTRONIK2_Log $name, 1, "Visibility attributes length check: ".length($result)." should have been ". $countVisibAttr;
+      $socket->close();
+      return (0, "Number of Visibility attributes read mismatch ( $!)\n");
+   }
+
+  @heatpump_visibility = unpack("C$countVisibAttr", $result);
+  if(scalar(@heatpump_visibility) != $countVisibAttr) {
+      LUXTRONIK2_Log $name, 2, "Unpacking problem by visibility attributes: ".scalar(@heatpump_visibility)." instead of ".$countVisibAttr;
+      $socket->close();
+      return (0, "Unpacking problem of visibility attributes");
+  }
+
+  LUXTRONIK2_Log $name, 5, "$countVisibAttr visibility attributs received";
+
+####################################  
+
+  LUXTRONIK2_Log $name, 5, "Closing connection to host $host";
+  $socket->close();
+
+  my $readingEndTime = time();
+
+#return all readings for further processing
+  return ( 1, "OK", \@heatpump_values, \@heatpump_parameters, \@heatpump_visibility);
+ 
+}
+
 sub ########################################
 LUXTRONIK2_CalcTemp($)
 {
@@ -2089,7 +2175,7 @@ LUXTRONIK2_doStatisticDeltaSingle ($$$$$$$)
   <br>
   It has a built-in ethernet port, so it can be directly integrated into a local area network (LAN).
   <br>
-  <i>The modul is reported to work with firmware: V1.51, V1.54C, V1.60, V1.64, V1.69, V1.70, V1.73, V1.77.</i>
+  <i>The modul is reported to work with firmware: V1.51, V1.54C, V1.60, V1.64, V1.69, V1.70, V1.73, V1.77, V1.80, V1.81.</i>
   <br>
   More Info on the particular <a href="http://www.fhemwiki.de/wiki/Luxtronik_2.0">page of FHEM-Wiki</a> (in German).
   <br>
@@ -2120,6 +2206,12 @@ LUXTRONIK2_doStatisticDeltaSingle ($$$$$$$)
        <li><code>INTERVAL &lt;polling interval&gt;</code><br>
          Polling interval in seconds
        </li><br>
+      <li><code>heatingCurveEndPoint &lt;Temperarature&gt;</code><br>
+         Sets the heating curve parameter. Adjustable in 0.1 steps.
+      </li><br>
+      <li><code>heatingCurveOffset &lt;Temperarature&gt;</code><br>
+         Sets the heating curve parameter. Adjustable in 0.1 steps.
+      </li><br>
       <li><code>hotWaterTemperatureTarget &lt;temperature&gt;</code><br>
          Target temperature of domestic hot water boiler in &deg;C
          </li><br>
@@ -2137,7 +2229,7 @@ LUXTRONIK2_doStatisticDeltaSingle ($$$$$$$)
          </li><br>
      <li><code>returnTemperatureHyst &lt;Temperature&gt;</code>
          <br>
-         Hysteresis of the returnTemperatureTarget of the heating controller . 0.5 K till 3 K. Adjustable in 0.1 steps.
+         Hysteresis of the returnTemperatureTarget of the heating controller. 0.5 K till 3 K. Adjustable in 0.1 steps.
          </li><br>
      <li><code>returnTemperatureSetBack &lt;Temperature&gt;</code>
          <br>
@@ -2153,9 +2245,19 @@ LUXTRONIK2_doStatisticDeltaSingle ($$$$$$$)
    
    <a name="LUXTRONIK2get"></a>
    <b>Get</b>
-   <ul>
-      No get implemented yet ...
-   </ul>
+  <ul>
+      <li><code>heatingCurveParameter &lt;OutsideTemp1 SetTemp1 OutsideTemp2 SetTemp2&gt;</code>
+      <br>
+      Determines based on two points on the heating curve the respective heat curve parameter <i>heatingCurveEndPoint</i> and <i>heatingCurveOffset</i>.<br>
+      These parameter can be set via the respective set commands.
+      </li>
+      <br>
+      <li><code>rawData</code>
+      <br>
+      Shows a table with all parameter and operational values returned by the controller.<br>
+      They can be assigned to device readings via the attributes <i>userHeatpumpParameters</i> und <i>userHeatpumpValues</i>.
+      </li><br>
+  </ul>
    <br>
   
    <a name="LUXTRONIK2attr"></a>
@@ -2203,6 +2305,17 @@ LUXTRONIK2_doStatisticDeltaSingle ($$$$$$$)
          <br>
          Currently, if the value of this attribute is not NULL, the corresponding reading consists of the current status of the heat pump and the temperature of the water.
          </li><br>
+       <li><code>userHeatpumpParameters &lt;Index [Name][,Index2 [Name2],Index3 [Name3] ...]&gt;</code>
+         <br>
+         Allows to continuousely read the value of certian controler parameters. The index number of the parameter can be determined with the get command <i>rawData</i><br> 
+         In the attribute definition, a name can be writen behind the index number separated by a space. The respective parameter value will either be shown with the prefix "userParameter..." or under the given name. <br>
+         Multiple indexes are separated by a comma.<br>
+         If the readings are not used anymore the can be deleted with the FHEM command <a href="#deletereading">deleteReading</a>.
+         </li><br>
+       <li><code>userHeatpumpValues &lt;Index Name[,Index2 Name2,Index3 Name3 ...]&gt;</code>
+         <br> 
+         Allows to read out specific operational values. Proceed as with <i>userHeatpumpParameters</i>.
+         </li><br>
       <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
    </ul>
 </ul>
@@ -2220,7 +2333,7 @@ LUXTRONIK2_doStatisticDeltaSingle ($$$$$$$)
   Siemens Novelan (WPR NET), Roth (ThermoAura®, ThermoTerra), Elco und Wolf Heiztechnik (BWL/BWS) verbaut ist.
   Sie besitzt einen Ethernet Anschluss, so dass sie direkt in lokale Netzwerke (LAN) integriert werden kann.
   <br>
-  <i>Das Modul wurde bisher mit folgender Steuerungs-Firmware getestet: V1.51, V1.54C, V1.60, V1.64, V1.69, V1.70, V1.73, V1.77.</i>
+  <i>Das Modul wurde bisher mit folgender Steuerungs-Firmware getestet: V1.51, V1.54C, V1.60, V1.64, V1.69, V1.70, V1.73, V1.77, V1.80, V1.81.</i>
   <br>
   Mehr Infos im entsprechenden <u><a href="http://www.fhemwiki.de/wiki/Luxtronik_2.0">Artikel der FHEM-Wiki</a></u>.
   <br>&nbsp;
@@ -2250,6 +2363,12 @@ LUXTRONIK2_doStatisticDeltaSingle ($$$$$$$)
          Dieser Wert muss entsprechend des vorhandenen oder geplanten Tarifes zum jeweiligen Zeitpunkt z.B. durch den FHEM-Befehl "at" gesetzt werden.<br>
          0 = tariflos 
       </li><br>
+      <li><code>heatingCurveEndPoint &lt;Temperaratur&gt;</code><br>
+         Einstellung des Heizkurvenparameters. In 0.1er Schritten einstellbar.
+      </li><br>
+      <li><code>heatingCurveOffset &lt;Temperaratur&gt;</code><br>
+         Einstellung des Heizkurvenparameters. In 0.1er Schritten einstellbar.
+      </li><br>
       <li><code>hotWaterCircPumpDeaerate &lt;on | off&gt;</code><br>
          Schaltet die externe Warmwasser-Zirkulationspumpe an oder aus. Durch die Zirkulation wird das Abk&uuml;hlen des Warmwassers in den Hausleitungen verhindert. Der W&auml;rmeverbrauch steigt jedoch drastisch.
          <br>
@@ -2292,7 +2411,17 @@ LUXTRONIK2_doStatisticDeltaSingle ($$$$$$$)
   <a name="LUXTRONIK2get"></a>
   <b>Get</b>
   <ul>
-      Es wurde noch kein "get" implementiert ...
+      <li><code>heatingCurveParameter &lt;Aussentemp1 Solltemp1 Aussentemp2 Solltemp2&gt;</code>
+      <br>
+      Ermittelt rekursiv anhand zweier Punkte auf der Heizkurve die entsprechenden Heizkurvenparameter <i>heatingCurveEndPoint</i> und <i>heatingCurveOffset</i>.<br>
+      Diese k&ouml;nnen dann &uuml;ber die entsprechenden set-Befehl einstellt werden.
+      </li>
+      <br>
+      <li><code>rawData</code>
+      <br>
+      Zeigt alle von der Steuerung auslesbaren Parameter und Betriebswerte an.<br>
+      Diese k&ouml;nnen dann mit den Attributen <i>userHeatpumpParameters</i> und <i>userHeatpumpValues</i> einem Ger&auml;tewert zugeordnet werden.
+      </li><br>
   </ul>
   <br>
   
@@ -2344,11 +2473,22 @@ LUXTRONIK2_doStatisticDeltaSingle ($$$$$$$)
       </li><br>
     <li><code>statusHTML</code>
       <br>
-      wenn gesetzt, dann wird ein HTML-formatierter Wert "floorplanHTML" erzeugt, 
+      Wenn gesetzt, dann wird ein HTML-formatierter Wert "floorplanHTML" erzeugt, 
       welcher vom Modul <a href="#FLOORPLAN">FLOORPLAN</a> genutzt werden kann.<br>
       Momentan wird nur gepr&uuml;ft, ob der Wert dieses Attributes ungleich NULL ist, 
       der entsprechende Ger&auml;tewerte besteht aus dem aktuellen W&auml;rmepumpenstatus und der Heizwassertemperatur.
       </li><br>
+    <li><code>userHeatpumpParameters &lt;Index [Name][,Index2 [Name2],Index3 [Name3] ...]&gt;</code>
+      <br>
+      Erlaubt das Auslesen der Werte benutzerspezifischer Parameter. Die Indizes der verf&uml;gbaren Parameterwerte k&ouml;nnen mit dem get-Befehl <i>rawData</i> ermittelt werden.<br> 
+      In der Attributdefinition kann der Name hinter den Index getrennt durch ein Leerzeichen geschrieben werden. Der jeweilige Parameter-Wert wird entweder mit dem Präfix "userParameter..." oder unter dem angegebenen Namen angezeigt. <br>
+      Mehrere Indizes werden durch Kommas getrennt.<br>
+      Nicht mehr ben&ouml;tigte Ger&auml;tewerte k&ouml;nnen mit dem FHEM-Befehl <a href="#deletereading">deleteReading</a> gel&ouml;scht werden.
+      </li><br>
+    <li><code>userHeatpumpValues &lt;Index Name[,Index2 Name2,Index3 Name3 ...]&gt;</code>
+      <br> 
+      Erlaubt das Auslesen benutzerspezifische Betriebswerte. Vorgehen wie bei <i>userHeatpumpParameters</i>.
+      </li><br>
     <li><a href="#readingFnAttributes">readingFnAttributes</a>
     </li><br>
   </ul>

+ 35 - 21
fhem/core/FHEM/24_TPLinkHS110.pm

@@ -1,5 +1,7 @@
 ################################################################
-# $Id: 24_TPLinkHS110.pm 14381 2017-05-26 07:14:53Z vk $
+# $Id: 24_TPLinkHS110.pm 16060 2018-02-01 13:02:36Z vk $
+#
+#  Release 2018-11-01 SetExtension
 #
 #  Copyright notice
 #
@@ -34,6 +36,7 @@ use warnings;
 use IO::Socket::INET;
 use IO::Socket::Timeout;
 use JSON;
+use SetExtensions;
 
 #####################################
 sub TPLinkHS110_Initialize($)
@@ -47,10 +50,10 @@ sub TPLinkHS110_Initialize($)
   $hash->{DeleteFn}   = "TPLinkHS110_Delete";
   $hash->{AttrFn}     = "TPLinkHS110_Attr";
   $hash->{AttrList}   = "interval ".
-			"disable:0,1 " .
-			"nightmode:on,off " .
-  			"timeout " .
-                        "$readingFnAttributes";
+	  "disable:0,1 " .
+	  "nightmode:on,off " .
+	  "timeout " .
+	  "$readingFnAttributes";
 }
 
 #####################################
@@ -110,6 +113,7 @@ sub TPLinkHS110_Get($$)
 		$json = decode_json($data);
 	} or do {
 		Log3 $hash, 2, "TPLinkHS110: $name json-decoding failed. Problem decoding getting statistical data";
+                Log3 $hash, 5, "TPLinkHS110: $name json-raw: $data";
 		return;
 	};
 
@@ -125,7 +129,7 @@ sub TPLinkHS110_Get($$)
 		readingsBulkUpdate($hash, "state", "on");
 	}
 	# If the device is a HS110, get realtime data:
-	if ($json->{'system'}->{'get_sysinfo'}->{'model'} eq "HS110(EU)") {
+	if ($json->{'system'}->{'get_sysinfo'}->{'model'} eq "HS110(EU)" or $json->{'system'}->{'get_sysinfo'}->{'model'} eq "HS110(UK)") {
 		my $realtimejcommand='{"emeter":{"get_realtime":{}}}';
 		my $rc = encrypt($realtimejcommand);
 		my $socket = IO::Socket::INET->new(PeerAddr => $remote_host,
@@ -148,7 +152,9 @@ sub TPLinkHS110_Get($$)
 		eval {
 			$realtimejson = decode_json($rdata);
 		} or do {
-			Log3 $hash, 2, "TPLinkHS110: $name json-decoding failed. Problem decoding getting statistical data";
+			Log3 $hash, 2, "TPLinkHS110: $name json-decoding failed. Problem decoding getting realtime data";
+                        Log3 $hash, 5, "TPLinkHS110: $name json-raw: $rdata";
+                        readingsEndUpdate($hash, 1);
 			return;
 		};
 		foreach my $key2 (sort keys %{$realtimejson->{'emeter'}->{'get_realtime'}}) {
@@ -187,7 +193,8 @@ sub TPLinkHS110_Get($$)
 			if ($count) { readingsBulkUpdate($hash, "daily_average", $total/$count)};
 			1;
 		} or do {
-			Log3 $hash, 2, "TPLinkHS110: $name json-decoding failed. Problem decoding getting statistical data";
+			Log3 $hash, 2, "TPLinkHS110: $name json-decoding failed. Problem decoding getting daily stat data";
+                        readingsEndUpdate($hash, 1);
 			return;
 		};
 	}
@@ -199,19 +206,26 @@ sub TPLinkHS110_Get($$)
 #####################################
 sub TPLinkHS110_Set($$)
 {
-	my ( $hash, @a ) = @_;
-  	my $name= $hash->{NAME};
+	my ( $hash, $name, $cmd, @args ) = @_;
+	my $cmdList = "on off";
+	return "\"set $name\" needs at least one argument" unless(defined($cmd));
 	return "Device disabled in config" if ($attr{$name}{"disable"} eq "1");
-   	Log3 $hash, 3, "TPLinkHS110: $name Set <". $a[1] ."> called";
-	return "Unknown argument $a[1], choose one of on off " if($a[1] ne "on" & $a[1] ne "off");
-
-	my $command;
-	if($a[1] eq "on") {
-		$command = '{"system":{"set_relay_state":{"state":1}}}';
-	}
-	if($a[1] eq "off") {
-		$command = '{"system":{"set_relay_state":{"state":0}}}';
-	}
+   	Log3 $hash, 3, "TPLinkHS110: $name Set <". $cmd ."> called";
+		
+	my $command="";
+	if($cmd eq "on")
+		{
+			$command = '{"system":{"set_relay_state":{"state":1}}}';
+		}
+		elsif($cmd eq "off")
+		{
+			$command = '{"system":{"set_relay_state":{"state":0}}}';
+		}
+		else # wenn der übergebene Befehl nicht durch X_Set() verarbeitet werden kann, Weitergabe an SetExtensions
+		{
+			return SetExtensions($hash, $cmdList, $name, $cmd, @args);
+		}	
+	
 	my $remote_host = $hash->{HOST};
 	my $remote_port = 9999;
 	my $c = encrypt($command);
@@ -235,7 +249,7 @@ sub TPLinkHS110_Set($$)
 		return;
 	};
 
-        if ($json->{'system'}->{'set_relay_state'}->{'err_code'} eq "0") {
+	if ($json->{'system'}->{'set_relay_state'}->{'err_code'} eq "0") {
 		TPLinkHS110_Get($hash,"");
 		
 	} else {

File diff suppressed because it is too large
+ 2041 - 1908
fhem/core/FHEM/26_KM273.pm


File diff suppressed because it is too large
+ 406 - 549
fhem/core/FHEM/30_DUOFERN.pm


+ 386 - 74
fhem/core/FHEM/30_HUEBridge.pm

@@ -1,5 +1,5 @@
 
-# $Id: 30_HUEBridge.pm 15123 2017-09-23 17:20:38Z justme1968 $
+# $Id: 30_HUEBridge.pm 16310 2018-03-02 10:43:36Z justme1968 $
 
 # "Hue Personal Wireless Lighting" is a trademark owned by Koninklijke Philips Electronics N.V.,
 # see www.meethue.com for more information.
@@ -15,13 +15,15 @@ use Data::Dumper;
 
 use HttpUtils;
 
+use IO::Socket::INET;
+
 sub HUEBridge_Initialize($)
 {
   my ($hash) = @_;
 
   # Provider
   $hash->{ReadFn}  = "HUEBridge_Read";
-  $hash->{WriteFn} = "HUEBridge_Read";
+  $hash->{WriteFn} = "HUEBridge_Write";
   $hash->{Clients} = ":HUEDevice:";
 
   #Consumer
@@ -31,11 +33,127 @@ sub HUEBridge_Initialize($)
   $hash->{GetFn}    = "HUEBridge_Get";
   $hash->{AttrFn}   = "HUEBridge_Attr";
   $hash->{UndefFn}  = "HUEBridge_Undefine";
-  $hash->{AttrList} = "key disable:1 disabledForIntervals httpUtils:1,0 noshutdown:1,0 pollDevices:1,2,0 queryAfterSet:1,0 $readingFnAttributes";
+  $hash->{AttrList} = "key disable:1 disabledForIntervals createGroupReadings:1,0 httpUtils:1,0 noshutdown:1,0 pollDevices:1,2,0 queryAfterSet:1,0 $readingFnAttributes";
+}
+
+sub
+HUEBridge_Read($)
+{
+  my ($hash) = @_;
+  my $name = $hash->{NAME};
+
+  my $buf;
+  my $len = sysread($hash->{CD}, $buf, 10240);
+  my $peerhost = $hash->{CD}->peerhost;
+  my $peerport = $hash->{CD}->peerport;
+
+  my $close = 0;
+  if( !defined($len) || !$len ) {
+    $close = 1;
+
+  } elsif( $hash->{websocket} ) {
+    $hash->{buf} .= $buf;
+
+    do {
+      my $fin = (ord(substr($hash->{buf},0,1)) & 0x80)?1:0;
+      my $op = (ord(substr($hash->{buf},0,1)) & 0x0F);
+      my $mask = (ord(substr($hash->{buf},1,1)) & 0x80)?1:0;
+      my $len = (ord(substr($hash->{buf},1,1)) & 0x7F);
+      my $i = 2;
+      if( $len == 126 ) {
+        $len = unpack( 'n', substr($hash->{buf},$i,2) );
+        $i += 2;
+      } elsif( $len == 127 ) {
+        $len = unpack( 'q', substr($hash->{buf},$i,8) );
+        $i += 8;
+      }
+      if( $mask ) {
+        $mask = substr($hash->{buf},$i,4);
+        $i += 4;
+      }
+      #FIXME: hande !$fin
+      return if( $len > length($hash->{buf})-$i );
+
+      my $data = substr($hash->{buf}, $i, $len);
+      $hash->{buf} = substr($hash->{buf},$i+$len);
+#Log 1, ">>>$data<<<";
+
+      if( $data eq '?' ) {
+        #ignore keepalive
+
+      } elsif( $op == 0x01 ) {
+        my $obj = eval { decode_json($data) };
+
+        if( $obj ) {
+          Log3 $name, 5, "$name: websocket data: ". Dumper $obj;
+        } else {
+          Log3 $name, 2, "$name: unhandled websocket text $data";
+
+        }
+
+        if( $obj->{t} eq 'event' && $obj->{e} eq 'changed' ) {
+          my $code;
+          my $id = $obj->{id};
+          $code = $name ."-". $id if( $obj->{r} eq 'lights' );
+          $code = $name ."-S". $id if( $obj->{r} eq 'sensors' );
+          $code = $name ."-G". $id if( $obj->{r} eq 'groups' );
+          if( !$code ) {
+            Log3 $name, 5, "$name: ignoring event: $code";
+            return;
+          }
+
+          my $chash = $modules{HUEDevice}{defptr}{$code};
+          if( defined($chash) ) {
+            HUEDevice_Parse($chash,$obj);
+            HUEBridge_updateGroups($hash, $chash->{ID}) if( !$chash->{helper}{devtype} );
+          } else {
+            Log3 $name, 4, "$name: message for unknow device received: $code";
+          }
+
+        } elsif( $obj->{t} eq 'event' && $obj->{e} eq 'scene-called' ) {
+          Log3 $name, 5, "$name: todo: handle websocket scene-called $data";
+          # trigger scene event ?
+
+        } elsif( $obj->{t} eq 'event' && $obj->{e} eq 'added' ) {
+          Log3 $name, 5, "$name: websocket add: $data";
+          HUEBridge_Autocreate($hash);
+
+        } elsif( $obj->{t} eq 'event' && $obj->{e} eq 'deleted' ) {
+          Log3 $name, 5, "$name: todo: handle websocket delete $data";
+          # do what ?
+
+        } else {
+          Log3 $name, 5, "$name: unknown websocket data: $data";
+        }
+
+      } else {
+        Log3 $name, 2, "$name: unhandled websocket data: $data";
+
+      }
+    } while( $hash->{buf} && !$close );
+
+  } elsif( $buf =~ m'^HTTP/1.1 101 Switching Protocols'i )  {
+    $hash->{websocket} = 1;
+    #my $buf = plex_msg2hash($buf, 1);
+#Log 1, $buf;
+
+    Log3 $name, 3, "$name: websocket: Switching Protocols ok";
+
+  } else {
+#Log 1, $buf;
+    $close = 1;
+    Log3 $name, 2, "$name: websocket: Switching Protocols failed";
+  }
+
+  if( $close ) {
+    HUEBridge_closeWebsocket($hash);
+
+    Log3 $name, 2, "$name: websocket closed";
+  }
 }
 
 sub
-HUEBridge_Read($@)
+HUEBridge_Write($@)
 {
   my ($hash,$chash,$name,$id,$obj)= @_;
 
@@ -80,7 +198,7 @@ HUEBridge_Detect($)
   }
 
   Log3 $name, 3, "HUEBridge_Detect: ${host}";
-  $hash->{Host} = $host;
+  $hash->{host} = $host;
 
   return $host;
 }
@@ -108,7 +226,7 @@ HUEBridge_Define($$)
 
   readingsSingleUpdate($hash, 'state', 'initialized', 1 );
 
-  $hash->{Host} = $host;
+  $hash->{host} = $host;
   $hash->{INTERVAL} = $interval;
 
   $attr{$name}{"key"} = join "",map { unpack "H*", chr(rand(256)) } 1..16 unless defined( AttrVal($name, "key", undef) );
@@ -156,6 +274,79 @@ sub HUEBridge_Undefine($$)
   return undef;
 }
 
+sub
+HUEBridge_hash2header($)
+{
+  my ($hash) = @_;
+
+  return $hash if( ref($hash) ne 'HASH' );
+
+  my $header;
+  foreach my $key (keys %{$hash}) {
+    #$header .= "\r\n" if( $header );
+    $header .= "$key: $hash->{$key}\r\n";
+  }
+
+  return $header;
+}
+sub HUEBridge_closeWebsocket($)
+{
+  my ($hash) = @_;
+  my $name = $hash->{NAME};
+
+  delete $hash->{buf};
+  delete $hash->{websocket};
+
+  close($hash->{CD}) if( defined($hash->{CD}) );
+  delete($hash->{CD});
+
+  delete($selectlist{$name});
+  delete($hash->{FD});
+
+  delete($hash->{PORT});
+}
+sub HUEBridge_openWebsocket($)
+{
+  my ($hash) = @_;
+  my $name = $hash->{NAME};
+
+  return if( !defined($hash->{websocketport}) );
+
+  HUEBridge_closeWebsocket($hash);
+
+  my ($host,undef) = split(':',$hash->{host},2);
+  if( my $socket = IO::Socket::INET->new(PeerAddr=>"$host:$hash->{websocketport}", Timeout=>2, Blocking=>1, ReuseAddr=>1) ) {
+    $hash->{CD}    = $socket;
+    $hash->{FD}    = $socket->fileno();
+
+    $hash->{PORT}  = $socket->sockport if( $socket->sockport );
+
+    $selectlist{$name} = $hash;
+
+    Log3 $name, 3, "$name: websocket opened to $host:$hash->{websocketport}";
+
+
+    my $ret = "GET ws://$host:$hash->{websocketport} HTTP/1.1\r\n";
+    $ret .= HUEBridge_hash2header( {                  'Host' => "$host:$hash->{websocketport}",
+                                                   'Upgrade' => 'websocket',
+                                                'Connection' => 'Upgrade',
+                                                    'Pragma' => 'no-cache',
+                                             'Cache-Control' => 'no-cache',
+                                         'Sec-WebSocket-Key' => 'RkhFTQ==',
+                                     'Sec-WebSocket-Version' => '13',
+                                   } );
+
+    $ret .= "\r\n";
+#Log 1, $ret;
+
+    syswrite($hash->{CD}, $ret );
+
+  } else {
+    Log3 $name, 2, "$name: failed to open websocket";
+
+  }
+}
+
 sub HUEBridge_fillBridgeInfo($$)
 {
   my ($hash,$config) = @_;
@@ -166,6 +357,11 @@ sub HUEBridge_fillBridgeInfo($$)
   $hash->{swversion} = $config->{swversion};
   $hash->{apiversion} = $config->{apiversion};
 
+  if( defined($config->{websocketport}) ) {
+    $hash->{websocketport} = $config->{websocketport};
+    HUEBridge_openWebsocket($hash);
+  }
+
   if( $hash->{apiversion} ) {
     my @l = split( '\.', $config->{apiversion} );
     $hash->{helper}{apiversion} = ($l[0] << 16) + ($l[1] << 8) + $l[2];
@@ -176,8 +372,6 @@ sub HUEBridge_fillBridgeInfo($$)
     $attr{$name}{icon} = 'hue_filled_bridge_v1' if( $hash->{modelid} && $hash->{modelid} eq 'BSB001' );
     $attr{$name}{icon} = 'hue_filled_bridge_v2' if( $hash->{modelid} && $hash->{modelid} eq 'BSB002' );
   }
-
-  $hash->{noshutdown} = AttrVal($name,'noshutdown', $hash->{name} =~ /deCONZ-GW|RaspBee/i)?1:0 if( !defined($hash->{noshutdown} ) );
 }
 
 sub
@@ -189,7 +383,7 @@ HUEBridge_OpenDev($)
   HUEBridge_Detect($hash) if( defined($hash->{NUPNP}) );
 
   my ($err,$ret) = HttpUtils_BlockingGet({
-    url => "http://$hash->{Host}/description.xml",
+    url => "http://$hash->{host}/description.xml",
     method => "GET",
     timeout => 3,
   });
@@ -202,10 +396,6 @@ HUEBridge_OpenDev($)
     $hash->{modelName} = $1;
     $ret =~ m/<manufacturer>([^<]*)/;
     $hash->{manufacturer} = $1;
-
-    if( $hash->{manufacturer} =~ /dresden elektronik/i ) {
-      $hash->{noshutdown} = AttrVal($name, 'noshutdown', 1)?1:0;
-    }
   }
 
   my $result = HUEBridge_Call($hash, undef, 'config', undef);
@@ -443,7 +633,12 @@ HUEBridge_Set($@)
       HUEDevice_SetParam(undef, \%obj, $cmd, $value, $value2);
     }
 
-    my $result = HUEBridge_Call($hash, undef, "scenes/$arg/lights/$light/state", \%obj, 'PUT');
+    my $result;
+    if( $hash->{helper}{apiversion} && $hash->{helper}{apiversion} >= (1<<16) + (11<<8) ) {
+      $result = HUEBridge_Call($hash, undef, "scenes/$arg/lightstates/$light", \%obj, 'PUT');
+    } else {
+      $result = HUEBridge_Call($hash, undef, "scenes/$arg/lights/$light/state", \%obj, 'PUT');
+    }
     return $result->{error}{description} if( $result->{error} );
 
     return undef;
@@ -564,7 +759,7 @@ HUEBridge_Set($@)
 
     return undef;
 
-  } elsif($cmd eq 'configsensor' || $cmd eq 'setsensor') {
+  } elsif($cmd eq 'configsensor' || $cmd eq 'setsensor' || $cmd eq 'updatesensor') {
     return "usage: $cmd <id> <json>" if( @args < 2 );
 
     if( defined $defs{$arg} && $defs{$arg}{TYPE} eq 'HUEDevice' ) {
@@ -582,7 +777,11 @@ HUEBridge_Set($@)
     }
     $json = $decoded;
 
-    my $result = HUEBridge_Call($hash, undef, "sensors/$arg/".($cmd eq 'configsensor'?'config':'state'), $json, 'PUT');
+    my $endpoint = '';
+    $endpoint = 'state' if( $cmd eq 'setsensor' );
+    $endpoint = 'config' if( $cmd eq 'configsensor' );
+
+    my $result = HUEBridge_Call($hash, undef, "sensors/$arg/$endpoint", $json, 'PUT');
     return $result->{error}{description} if( $result->{error} );
 
     my $code = $name ."-S". $arg;
@@ -636,7 +835,7 @@ HUEBridge_Set($@)
     return undef;
 
   } else {
-    my $list = "active inactive delete creategroup deletegroup savescene deletescene modifyscene scene createrule updaterule deleterule createsensor deletesensor configsensor setsensor deletewhitelist touchlink:noArg checkforupdate:noArg autodetect:noArg autocreate:noArg statusRequest:noArg";
+    my $list = "active inactive delete creategroup deletegroup savescene deletescene modifyscene scene createrule updaterule deleterule createsensor deletesensor configsensor setsensor updatesensor deletewhitelist touchlink:noArg checkforupdate:noArg autodetect:noArg autocreate:noArg statusRequest:noArg";
     $list .= " swupdate:noArg" if( defined($hash->{updatestate}) && $hash->{updatestate} =~ '^2' );
     return "Unknown argument $cmd, choose one of $list";
   }
@@ -775,6 +974,10 @@ HUEBridge_GetUpdate($)
     InternalTimer(gettimeofday()+$hash->{INTERVAL}, "HUEBridge_GetUpdate", $hash, 0);
   }
 
+  if( $hash->{websocketport} && !$hash->{PORT} ) {
+    HUEBridge_openWebsocket($hash);
+  }
+
   my $type;
   my $result;
   my $poll_devices = AttrVal($name, "pollDevices", 1);
@@ -801,6 +1004,112 @@ HUEBridge_GetUpdate($)
   return undef;
 }
 
+my %dim_values = (
+   0 => "dim06%",
+   1 => "dim12%",
+   2 => "dim18%",
+   3 => "dim25%",
+   4 => "dim31%",
+   5 => "dim37%",
+   6 => "dim43%",
+   7 => "dim50%",
+   8 => "dim56%",
+   9 => "dim62%",
+  10 => "dim68%",
+  11 => "dim75%",
+  12 => "dim81%",
+  13 => "dim87%",
+  14 => "dim93%",
+);
+sub
+HUEBridge_updateGroups($$)
+{
+  my($hash,$lights) = @_;
+  my $name = $hash->{NAME};
+  my $createGroupReadings = AttrVal($hash->{NAME},"createGroupReadings",undef);
+  return if( !defined($createGroupReadings) );
+  $createGroupReadings = ($createGroupReadings eq "1");
+
+  my $groups = {};
+  foreach my $light ( split(',', $lights) ) {
+    foreach my $chash ( values %{$modules{HUEDevice}{defptr}} ) {
+      next if( !$chash->{IODev} );
+      next if( !$chash->{lights} );
+      next if( $chash->{IODev}{NAME} ne $name );
+      next if( $chash->{helper}{devtype} ne 'G' );
+      next if( ",$chash->{lights}," !~ m/,$light,/ );
+      next if( $createGroupReadings && !AttrVal($chash->{NAME},"createGroupReadings", 1) );
+      next if( !$createGroupReadings && !AttrVal($chash->{NAME},"createGroupReadings", undef) );
+
+      $groups->{$chash->{ID}} = $chash;
+    }
+  }
+
+  foreach my $chash ( values %{$groups} ) {
+    my $count = 0;
+    my %readings;
+    foreach my $light ( split(',', $chash->{lights}) ) {
+      next if( !$light );
+      my $current = $modules{HUEDevice}{defptr}{"$name-$light"}{helper};
+
+      $readings{ct} += $current->{ct};
+      $readings{bri} += $current->{bri};
+      $readings{pct} += $current->{pct};
+      $readings{sat} += $current->{sat};
+
+      $readings{on} |= ($current->{on}?'1':'0');
+      $readings{reachable} |= ($current->{reachable}?'1':'0');
+
+      if( !defined($readings{alert}) ) {
+        $readings{alert} = $current->{alert};
+      } elsif( $readings{alert} ne $current->{alert} ) {
+        $readings{alert} = "nonuniform";
+      }
+      if( !defined($readings{colormode}) ) {
+        $readings{colormode} = $current->{colormode};
+      } elsif( $readings{colormode} ne $current->{colormode} ) {
+        $readings{colormode} = "nonuniform";
+      }
+      if( !defined($readings{effect}) ) {
+        $readings{effect} = $current->{effect};
+      } elsif( $readings{effect} ne $current->{effect} ) {
+        $readings{effect} = "nonuniform";
+      }
+
+      ++$count;
+    }
+    $readings{ct} = int($readings{ct} / $count + 0.5);
+    $readings{bri} = int($readings{bri} / $count + 0.5);
+    $readings{pct} = int($readings{pct} / $count + 0.5);
+    $readings{sat} = int($readings{sat} / $count + 0.5);
+
+    if( $readings{on} ) {
+      if( $readings{pct} > 0
+          && $readings{pct} < 100  ) {
+        $readings{state} = $dim_values{int($readings{pct}/7)};
+      }
+      $readings{state} = 'off' if( $readings{pct} == 0 );
+      $readings{state} = 'on' if( $readings{pct} == 100 );
+
+    } else {
+      $readings{pct} = 0;
+      $readings{state} = 'off';
+    }
+    $readings{onoff} =  $readings{on};
+    delete $readings{on};
+
+    readingsBeginUpdate($chash);
+      foreach my $key ( keys %readings ) {
+        if( defined($readings{$key}) ) {
+          readingsBulkUpdate($chash, $key, $readings{$key}, 1) if( !defined($chash->{helper}{$key}) || $chash->{helper}{$key} ne $readings{$key} );
+          $chash->{helper}{$key} = $readings{$key};
+        }
+      }
+    readingsEndUpdate($chash,1);
+  }
+
+}
+
 sub
 HUEBridge_Parse($$)
 {
@@ -944,7 +1253,8 @@ HUEBridge_Autocreate($;$)
   return "created $autocreated devices";
 }
 
-sub HUEBridge_ProcessResponse($$)
+sub
+HUEBridge_ProcessResponse($$)
 {
   my ($hash,$obj) = @_;
   my $name = $hash->{NAME};
@@ -952,14 +1262,12 @@ sub HUEBridge_ProcessResponse($$)
   #Log3 $name, 3, ref($obj);
   #Log3 $name, 3, "Receiving: " . Dumper $obj;
 
-  if( ref($obj) eq 'ARRAY' )
-    {
-      if( defined($obj->[0]->{error}))
-        {
-          my $error = $obj->[0]->{error}->{'description'};
+  if( ref($obj) eq 'ARRAY' ) {
+    if( defined($obj->[0]->{error})) {
+      my $error = $obj->[0]->{error}->{'description'};
 
-          readingsSingleUpdate($hash, 'lastError', $error, 1 );
-        }
+      readingsSingleUpdate($hash, 'lastError', $error, 1 );
+    }
 
     if( !AttrVal( $name,'queryAfterSet', 1 ) ) {
       my $successes;
@@ -984,7 +1292,6 @@ sub HUEBridge_ProcessResponse($$)
                   $successes++;
                 }
               }
-
             }
           }
 
@@ -995,23 +1302,26 @@ sub HUEBridge_ProcessResponse($$)
         }
       }
 
+      my $changed = "";
       foreach my $id ( keys %json ) {
         my $code = $name ."-". $id;
         if( my $chash = $modules{HUEDevice}{defptr}{$code} ) {
           #$json{$id}->{state}->{reachable} = 1;
-          HUEDevice_Parse( $chash, $json{$id} );
+          if( HUEDevice_Parse( $chash, $json{$id} ) ) {
+            $changed .= "," if( $changed );
+            $changed .= $chash->{ID};
+          }
         }
       }
+      HUEBridge_updateGroups($hash, $changed) if( $changed );
     }
 
-      #return undef if( !$errors && $successes );
+    #return undef if( !$errors && $successes );
 
-      return ($obj->[0]);
-    }
-  elsif( ref($obj) eq 'HASH' )
-    {
-      return $obj;
-    }
+    return ($obj->[0]);
+  } elsif( ref($obj) eq 'HASH' ) {
+    return $obj;
+  }
 
   return undef;
 }
@@ -1079,7 +1389,7 @@ HUEBridge_HTTP_Call($$$;$)
 
   #return { state => {reachable => 0 } } if($attr{$name} && $attr{$name}{disable});
 
-  my $uri = "http://" . $hash->{Host} . "/api";
+  my $uri = "http://" . $hash->{host} . "/api";
   if( defined($obj) ) {
     $method = 'PUT' if( !$method );
 
@@ -1097,7 +1407,7 @@ HUEBridge_HTTP_Call($$$;$)
   }
   #Log3 $name, 3, "Url: " . $uri;
   Log3 $name, 4, "using HUEBridge_HTTP_Request: $method ". ($path?$path:'');
-  my $ret = HUEBridge_HTTP_Request(0,$uri,$method,undef,$obj,$hash->{noshutdown});
+  my $ret = HUEBridge_HTTP_Request(0,$uri,$method,undef,$obj,AttrVal($name,'noshutdown', 1));
   #Log3 $name, 3, Dumper $ret;
   if( !defined($ret) ) {
     return undef;
@@ -1133,7 +1443,7 @@ HUEBridge_HTTP_Call2($$$$;$)
 
   #return { state => {reachable => 0 } } if($attr{$name} && $attr{$name}{disable});
 
-  my $url = "http://" . $hash->{Host} . "/api";
+  my $url = "http://" . $hash->{host} . "/api";
   my $blocking = $attr{$name}{httpUtils} < 1;
   $blocking = 1 if( !defined($chash) );
   if( defined($obj) ) {
@@ -1163,7 +1473,7 @@ HUEBridge_HTTP_Call2($$$$;$)
       url => $url,
       timeout => 4,
       method => $method,
-      noshutdown => $hash->{noshutdown},
+      noshutdown => AttrVal($name,'noshutdown', 1),
       header => "Content-Type: application/json",
       data => $obj,
     });
@@ -1193,7 +1503,7 @@ HUEBridge_HTTP_Call2($$$$;$)
       url => $url,
       timeout => 10,
       method => $method,
-      noshutdown => $hash->{noshutdown},
+      noshutdown => AttrVal($name,'noshutdown', 1),
       header => "Content-Type: application/json",
       data => $obj,
       hash => $hash,
@@ -1236,21 +1546,20 @@ HUEBridge_dispatch($$$;$)
 
     my $type = $param->{type};
 
-    if( ref($json) eq 'ARRAY' )
-      {
-        HUEBridge_ProcessResponse($hash,$json) if( !$queryAfterSet );
+    if( ref($json) eq 'ARRAY' ) {
+      HUEBridge_ProcessResponse($hash,$json) if( !$queryAfterSet );
 
-        if( defined($json->[0]->{error}))
-          {
-            my $error = $json->[0]->{error}->{'description'};
+      if( defined($json->[0]->{error}))
+        {
+          my $error = $json->[0]->{error}->{'description'};
 
-            readingsSingleUpdate($hash, 'lastError', $error, 1 );
+          readingsSingleUpdate($hash, 'lastError', $error, 1 );
 
-            Log3 $name, 3, $error;
-          }
+          Log3 $name, 3, $error;
+        }
 
-        #return ($json->[0]);
-      }
+      #return ($json->[0]);
+    }
 
     if( $hash == $param->{chash} ) {
       if( !defined($type) ) {
@@ -1289,17 +1598,22 @@ HUEBridge_dispatch($$$;$)
       }
 
       if( $type eq 'lights' ) {
+        my $changed = "";
         my $lights = $json;
         foreach my $id ( keys %{$lights} ) {
           my $code = $name ."-". $id;
           my $chash = $modules{HUEDevice}{defptr}{$code};
 
           if( defined($chash) ) {
-            HUEDevice_Parse($chash,$lights->{$id});
+            if( HUEDevice_Parse($chash,$lights->{$id}) ) {
+              $changed .= "," if( $changed );
+              $changed .= $chash->{ID};
+            }
           } else {
             Log3 $name, 2, "$name: message for unknow device received: $code";
           }
         }
+        HUEBridge_updateGroups($hash, $changed) if( $changed );
 
       } elsif( $type =~ m/^config$/ ) {
         HUEBridge_Parse($hash,$json);
@@ -1311,7 +1625,9 @@ HUEBridge_dispatch($$$;$)
       }
 
     } elsif( $type =~ m/^lights\/(\d*)$/ ) {
-      HUEDevice_Parse($param->{chash},$json);
+      if( HUEDevice_Parse($param->{chash},$json) ) {
+        HUEBridge_updateGroups($hash, $param->{chash}{ID});
+      }
 
     } elsif( $type =~ m/^groups\/(\d*)$/ ) {
       HUEDevice_Parse($param->{chash},$json);
@@ -1464,19 +1780,6 @@ HUEBridge_Attr($$$)
 
     readingsSingleUpdate($hash, 'state', IsDisabled($name)?'disabled':'active', 1 );
     HUEBridge_OpenDev($hash) if( !IsDisabled($name) );
-  } elsif( $attrName eq "noshutdown" ) {
-    my $hash = $defs{$name};
-
-    if( $cmd eq 'set' ) {
-      $hash->{noshutdown} = $attrVal;
-
-    } else {
-      if( defined($hash->{manufacturer}) ) {
-        $hash->{noshutdown} = ($hash->{manufacturer} =~ /dresden elektronik/i)?1:0;
-      } else {
-        $hash->{noshutdown} = ($hash->{name} =~ /deCONZ-GW|RaspBee/i)?1:0;
-      }
-    }
 
   }
 
@@ -1562,8 +1865,8 @@ HUEBridge_Attr($$$)
       Create fhem devices for all bridge devices.</li>
     <li>autodetect<br>
       Initiate the detection of new ZigBee devices. After aproximately one minute any newly detected
-      devices can be listed with <code>get <bridge> devices</code> and the corresponding fhem devices
-      can be created by <code>set <bridge> autocreate</code>.</li>
+      devices can be listed with <code>get &lt;bridge&gt; devices</code> and the corresponding fhem devices
+      can be created by <code>set &lt;bridge&gt; autocreate</code>.</li>
     <li>delete &lt;name&gt;|&lt;id&gt;<br>
       Deletes the given device in the bridge and deletes the associated fhem device.</li>
     <li>creategroup &lt;name&gt; &lt;lights&gt;<br>
@@ -1590,6 +1893,8 @@ HUEBridge_Attr($$$)
       Write sensor config data.</li>
     <li>setsensor &lt;id&gt; &lt;json&gt;<br>
       Write CLIP sensor status data.</li>
+    <li>updatesensor &lt;id&gt; &lt;json&gt;<br>
+      Write sensor toplevel data.</li>
     <li>deletewhitelist &lt;key&gt;<br>
       Deletes the given key from the whitelist in the bridge.</li>
     <li>touchlink<br>
@@ -1603,14 +1908,14 @@ HUEBridge_Attr($$$)
       available (indicated by updatestate with a value of 2. The version and release date is shown in the reading swupdate.<br>
       A notify of the form <code>define HUEUpdate notify bridge:swupdate.* {...}</code>
       can be used to be informed about available firmware updates.<br></li>
-    <li>inactive<br>                           
-      inactivates the current device. note the slight difference to the 
+    <li>inactive<br>
+      inactivates the current device. note the slight difference to the
       disable attribute: using set inactive the state is automatically saved
       to the statefile on shutdown, there is no explicit save necesary.<br>
       this command is intended to be used by scripts to temporarily
-      deactivate the harmony device.<br> 
+      deactivate the harmony device.<br>
       the concurrent setting of the disable attribute is not recommended.</li>
-    <li>active<br>                             
+    <li>active<br>
       activates the current device (see inactive).</li>
   </ul><br>
 
@@ -1619,17 +1924,24 @@ HUEBridge_Attr($$$)
   <ul>
     <li><a href="#disable">disable</a></li>
     <li><a href="#disabledForIntervals">disabledForIntervals</a></li>
+    <li>httpUtils<br>
+      0 -> use HttpUtils_BlockingGet<br>
+      1 -> use HttpUtils_NonblockingGet<br>
+      not set -> use old module specific implementation</li>
     <li>pollDevices<br>
       1 -> the bridge will poll all lights in one go instead of each device polling itself independently<br>
       2 -> the bridge will poll all devices in one go instead of each device polling itself independently<br>
       default is 1.</li>
+    <li>createGroupReadings<br>
+      create 'artificial' readings for group devices.</li>
+      0 -> create readings only for group devices where createGroupReadings ist set to 1
+      1 -> create readings for all group devices where createGroupReadings ist not set or set to 1
+      undef -> do nothing
     <li>queryAfterSet<br>
       the bridge will request the real device state after a set command. default is 1.</li>
     <li>noshutdown<br>
       Some bridge devcies require a different type of connection handling. raspbee/deconz only works if the connection
-      is not immediately closed, the phillips hue bridge works better if the connection is immediately closed. This module
-      tries to autodetect the bridge device type. the type of connection handling can be seen in the noshutdown internal
-      and can be overwritten with the noshutdown attribute.</li>
+      is not immediately closed, the phillips hue bridge now shows the same behavior. so this is now the default.  </li>
   </ul><br>
 </ul><br>
 

+ 6 - 11
fhem/core/FHEM/30_MilightBridge.pm

@@ -1,4 +1,4 @@
-# $Id: 30_MilightBridge.pm 11710 2016-06-24 09:07:17Z mattwire $
+# $Id: 30_MilightBridge.pm 16085 2018-02-04 18:35:23Z mattwire $
 ##############################################
 #
 #     30_MilightBridge.pm (Use with 31_MilightDevice.pm)
@@ -63,13 +63,13 @@ sub MilightBridge_Define($$)
 
   return "Usage: define <name> MilightBridge <host/ip:port>"  if(@args < 3);
 
-  my ($name, $type, $host) = @args;
+  my ($name, $type, $hostandport) = @args;
 
   $hash->{Clients} = ":MilightDevice:";
   my %matchList = ( "1:MilightDevice" => ".*" );
   $hash->{MatchList} = \%matchList;
 
-  my ($host, $port) = split(":", $host);
+  my ($host, $port) = split(":", $hostandport);
   # Parameters
   $hash->{HOST} = $host;
   # Set Port (Default 8899, old bridge (V2) uses 50000
@@ -415,14 +415,7 @@ sub MilightBridge_CmdQueue_Send(@)
     #Log3 ($hash, 5, "$hash->{NAME}_cmdQueue_Send: cmdLastSent: $hash->{cmdLastSent}; Next: ".(gettimeofday()+($hash->{INTERVAL}/1000)));
 
     # Remove any existing timers and trigger a new one
-    foreach my $args (keys %intAt)
-    {
-      if (($intAt{$args}{ARG} eq $hash) && ($intAt{$args}{FN} eq 'MilightBridge_CmdQueue_Send'))
-      {
-        Log3 ($hash, 5, "$hash->{NAME}_CmdQueue_Send: Remove timer at: ".$intAt{$args}{TRIGGERTIME});
-        delete($intAt{$args});
-      }
-    }
+    RemoveInternalTimer($hash, 'MilightBridge_CmdQueue_Send');
     InternalTimer(gettimeofday()+($hash->{INTERVAL}/1000), "MilightBridge_CmdQueue_Send", $hash, 0);
   }
 
@@ -433,6 +426,8 @@ sub MilightBridge_CmdQueue_Send(@)
 1;
 
 =pod
+=item device
+=item summary Interface to a Milight Bridge connected to the network using a Wifi connection
 =begin html
 
 <a name="MilightBridge"></a>

+ 65 - 39
fhem/core/FHEM/31_Aurora.pm

@@ -1,4 +1,4 @@
-# $Id: 31_Aurora.pm 14962 2017-08-26 18:10:59Z justme1968 $
+# $Id: 31_Aurora.pm 15915 2018-01-17 16:44:46Z justme1968 $
 
 package main;
 
@@ -43,6 +43,7 @@ Aurora_Initialize($)
 
   #Consumer
   $hash->{DefFn}    = "Aurora_Define";
+  $hash->{NotifyFn} = "Aurora_Notify";
   $hash->{UndefFn}  = "Aurora_Undefine";
   $hash->{SetFn}    = "Aurora_Set";
   $hash->{GetFn}    = "Aurora_Get";
@@ -127,6 +128,8 @@ Aurora_Define($$)
   $interval = 60 if( defined($interval) && $interval < 10 );
   $hash->{INTERVAL} = $interval;
 
+  $hash->{helper}{last_config_timestamp} = 0;
+
   $hash->{helper}{on} = -1;
   $hash->{helper}{colormode} = '';
   $hash->{helper}{ct} = -1;
@@ -143,16 +146,37 @@ Aurora_Define($$)
   my $icon_path = AttrVal("WEB", "iconPath", "default:fhemSVG:openautomation" );
   $attr{$name}{'color-icons'} = 2 if( !defined( $attr{$name}{'color-icons'} ) && $icon_path =~ m/openautomation/ );
 
+  $hash->{NOTIFYDEV} = "global";
+
   RemoveInternalTimer($hash);
   if( $init_done ) {
-    Aurora_OpenDev($hash);
-  } else {
-    #InternalTimer(gettimeofday()+10, "Aurora_GetUpdate", $hash, 0);
+    Aurora_OpenDev($hash) if( !IsDisabled($name) );
+  }
+
+  return undef;
+}
+
+sub
+Aurora_Notify($$)
+{
+  my ($hash,$dev) = @_;
+  my $name  = $hash->{NAME};
+  my $type  = $hash->{TYPE};
+
+  return if($dev->{NAME} ne "global");
+  return if(!grep(m/^INITIALIZED|REREADCFG$/, @{$dev->{CHANGED}}));
+
+  if( IsDisabled($name) > 0 ) {
+    readingsSingleUpdate($hash, 'state', 'inactive', 1 ) if( ReadingsVal($name,'inactive','' ) ne 'disabled' );
+    return undef;
   }
 
+  Aurora_OpenDev($hash);
+
   return undef;
 }
 
+
 sub
 Aurora_Undefine($$)
 {
@@ -188,7 +212,7 @@ Aurora_OpenDev($)
     Log3 $name, 2, "Aurora_OpenDev: got empty config";
     return undef;
   }
-  Log3 $name, 5, "Aurora_OpenDev: got config " . Dumper $result;
+  Log3 $name, 5, "Aurora_OpenDev: got config " . Dumper $result if($Aurora_hasDataDumper);
 
   if( !defined($result->{'linkbutton'}) || !AttrVal($name, 'key', undef) )
     {
@@ -282,12 +306,6 @@ Aurora_dispatch($$$;$)
       RemoveInternalTimer($hash);
       InternalTimer(gettimeofday()+1, "Aurora_GetUpdate", $hash, 0);
     }
-  } elsif( $param->{type} eq 'effects' ) {
-    $hash->{helper}{effects} = $json->{effectsList};
-    if( my $effect = $json->{select} ) {
-      if( $effect ne $hash->{helper}{effect} ) { readingsSingleUpdate($hash, 'effect', $effect, 1 ) };
-      $hash->{helper}{effect} = $effect;
-    }
   }
 }
 
@@ -703,26 +721,32 @@ Aurora_GetUpdate($)
 
   return undef if(IsDisabled($name));
 
-  my($err,$data) = HttpUtils_NonblockingGet({
-    url => "http://$hash->{IP}:16021/api/v1/$attr{$name}{token}/state",
-    timeout => 2,
-    method => 'GET',
-    noshutdown => $hash->{noshutdown},
-    hash => $hash,
-    type => 'state',
-    callback => \&Aurora_dispatch,
-  });
+  my ($now) = gettimeofday();
+  if( $hash->{LOCAL} || $now - $hash->{helper}{last_config_timestamp} > 300 ) {
+    my($err,$data) = HttpUtils_NonblockingGet({
+      url => "http://$hash->{IP}:16021/api/v1/$attr{$name}{token}",
+      timeout => 2,
+      method => 'GET',
+      noshutdown => $hash->{noshutdown},
+      hash => $hash,
+      type => 'state',
+      callback => \&Aurora_dispatch,
+    });
 
-  ($err,$data) = HttpUtils_NonblockingGet({
-    url => "http://$hash->{IP}:16021/api/v1/$attr{$name}{token}/effects",
-    timeout => 2,
-    method => 'GET',
-    noshutdown => $hash->{noshutdown},
-    hash => $hash,
-    type => 'effects',
-    callback => \&Aurora_dispatch,
-  });
+    $hash->{helper}{last_config_timestamp} = $now;
 
+  } else {
+    my($err,$data) = HttpUtils_NonblockingGet({
+      url => "http://$hash->{IP}:16021/api/v1/$attr{$name}{token}/state",
+      timeout => 2,
+      method => 'GET',
+      noshutdown => $hash->{noshutdown},
+      hash => $hash,
+      type => 'state',
+      callback => \&Aurora_dispatch,
+    });
+
+  }
 
   return undef;
 }
@@ -757,18 +781,19 @@ Aurora_Parse($$)
   Log3 $name, 5, Dumper $result if($Aurora_hasDataDumper);
 
   $hash->{name} = $result->{name} if( defined($result->{name}) );
-  $hash->{type} = $result->{type} if( defined($result->{type}) );
-  $hash->{class} = $result->{class} if( defined($result->{class}) );
-  $hash->{uniqueid} = $result->{uniqueid} if( defined($result->{uniqueid}) );
+  $hash->{serialNo} = $result->{serialNo} if( defined($result->{serialNo}) );
+  $hash->{manufacturer} = $result->{manufacturer} if( defined($result->{manufacturer}) );
+  $hash->{model} = $result->{model} if( defined($result->{model}) );
+  $hash->{firmwareVersion} = $result->{firmwareVersion} if( defined($result->{firmwareVersion}) );
 
-  $hash->{modelid} = $result->{modelid} if( defined($result->{modelid}) );
-  $hash->{productid} = $result->{productid} if( defined($result->{productid}) );
-  $hash->{swversion} = $result->{swversion} if( defined($result->{swversion}) );
-  $hash->{swconfigid} = $result->{swconfigid} if( defined($result->{swconfigid}) );
-  $hash->{manufacturername} = $result->{manufacturername} if( defined($result->{manufacturername}) );
-  $hash->{luminaireuniqueid} = $result->{luminaireuniqueid} if( defined($result->{luminaireuniqueid}) );
+  if( my $effects = $result->{effects} ) {
+    $hash->{helper}{effects} = $effects->{effectsList} if( defined($effects->{effectsList}) );
 
-  $attr{$name}{model} = $result->{modelid} if( !defined($attr{$name}{model}) && $result->{modelid} );
+    if( my $effect = $effects->{select} ) {
+      if( $effect ne $hash->{helper}{effect} ) { readingsSingleUpdate($hash, 'effect', $effect, 1 ) };
+      $hash->{helper}{effect} = $effect;
+    }
+  }
 
   $attr{$name}{devStateIcon} = '{(Aurora_devStateIcon($name),"toggle")}' if( !defined( $attr{$name}{devStateIcon} ) );
 
@@ -783,6 +808,7 @@ Aurora_Parse($$)
   readingsBeginUpdate($hash);
 
   my $state = $result;
+  $state = $state->{'state'} if( defined($state->{'state'}) );
 
   my $on        = $state->{on}{value};
      $on = $hash->{helper}{on} if( !defined($on) );

+ 45 - 25
fhem/core/FHEM/31_HUEDevice.pm

@@ -1,5 +1,5 @@
 
-# $Id: 31_HUEDevice.pm 15247 2017-10-13 19:18:21Z justme1968 $
+# $Id: 31_HUEDevice.pm 16352 2018-03-08 07:42:40Z justme1968 $
 
 # "Hue Personal Wireless Lighting" is a trademark owned by Koninklijke Philips Electronics N.V.,
 # see www.meethue.com for more information.
@@ -165,7 +165,6 @@ sub HUEDevice_Initialize($)
   $hash->{GetFn}    = "HUEDevice_Get";
   $hash->{AttrFn}   = "HUEDevice_Attr";
   $hash->{AttrList} = "IODev ".
-                      "createActionReadings:1,0 ".
                       "delayedUpdate:1 ".
                       "ignoreReachable:1,0 ".
                       "realtimePicker:1,0 ".
@@ -194,6 +193,24 @@ HUEDevice_devStateIcon($)
   my $name = $hash->{NAME};
 
   if( $hash->{helper}->{devtype} && $hash->{helper}->{devtype} eq 'G' ) {
+    if( $hash->{IODev} ) {
+      my $createGroupReadings = AttrVal($hash->{IODev}{NAME},"createGroupReadings",undef);
+      if( defined($createGroupReadings) ) {
+        return undef if( $createGroupReadings && !AttrVal($hash->{NAME},"createGroupReadings", 1) );
+        return undef if( !$createGroupReadings && !AttrVal($hash->{NAME},"createGroupReadings", undef) );
+
+        return ".*:light_question:toggle" if( !$hash->{helper}{reachable} );
+
+        return ".*:off:toggle" if( ReadingsVal($name,"onoff","0") eq "0" );
+
+        my $pct = ReadingsVal($name,"pct","100");
+        my $s = $dim_values{int($pct/7)};
+        $s="on" if( $pct eq "100" );
+
+        return ".*:$s:toggle";
+      }
+    }
+
     #return ".*:off:toggle" if( !ReadingsVal($name,'any_on',0) );
     #return ".*:on:toggle" if( ReadingsVal($name,'any_on',0) );
 
@@ -337,6 +354,9 @@ sub HUEDevice_Define($$)
     my $icon_path = AttrVal("WEB", "iconPath", "default:fhemSVG:openautomation" );
     $attr{$name}{'color-icons'} = 2 if( !defined( $attr{$name}{'color-icons'} ) && $icon_path =~ m/openautomation/ );
 
+    addToDevAttrList($name, "createActionReadings:1,0");
+    addToDevAttrList($name, "createGroupReadings:1,0");
+
   } elsif( $hash->{helper}->{devtype} eq 'S' ) {
     $hash->{DEF} = "sensor $id $args[3] IODev=$iodev" if( $iodev );
 
@@ -721,6 +741,7 @@ HUEDevice_Set($@)
   }
 
   if( (my $joined = join(" ", @aa)) =~ /:/ ) {
+    $joined =~ s/on-till\s+[^\s]+//g; #bad workaround for: https://forum.fhem.de/index.php/topic,61636.msg728557.html#msg728557
     my @cmds = split(":", $joined);
     for( my $i = 0; $i <= $#cmds; ++$i ) {
       HUEDevice_SetParam($name, \%obj, split(" ", $cmds[$i]) );
@@ -982,8 +1003,10 @@ sub
 HUEDevice_ReadFromServer($@)
 {
   my ($hash,@a) = @_;
-
   my $name = $hash->{NAME};
+
+  #return if(IsDummy($name) || IsIgnored($name));
+
   no strict "refs";
   my $ret;
   unshift(@a,$name);
@@ -991,22 +1014,6 @@ HUEDevice_ReadFromServer($@)
   $ret = IOWrite($hash,$hash,@a);
   use strict "refs";
   return $ret;
-  return if(IsDummy($name) || IsIgnored($name));
-  my $iohash = $hash->{IODev};
-  if(!$iohash ||
-     !$iohash->{TYPE} ||
-     !$modules{$iohash->{TYPE}} ||
-     !$modules{$iohash->{TYPE}}{ReadFn}) {
-    Log3 $name, 5, "No I/O device or ReadFn found for $name";
-    return;
-  }
-
-  no strict "refs";
-  #my $ret;
-  unshift(@a,$name);
-  $ret = &{$modules{$iohash->{TYPE}}{ReadFn}}($iohash, @a);
-  use strict "refs";
-  return $ret;
 }
 
 sub
@@ -1041,7 +1048,7 @@ HUEDevice_GetUpdate($)
   my $result = HUEDevice_ReadFromServer($hash,$hash->{ID});
   if( !defined($result) ) {
     $hash->{helper}{reachable} = 0;
-    $hash->{STATE} = "unknown";
+    #$hash->{STATE} = "unknown";
     return;
   } elsif( $result->{'error'} ) {
     $hash->{helper}{reachable} = 0;
@@ -1050,6 +1057,7 @@ HUEDevice_GetUpdate($)
   }
 
   HUEDevice_Parse($hash,$result);
+  HUEBridge_updateGroups($hash->{IODev}, $hash->{ID}) if( $hash->{IODev}{TYPE} eq 'HUEBridge' );
 }
 
 sub
@@ -1103,7 +1111,7 @@ HUEDevice_Parse($$)
 
   if( $hash->{helper}->{devtype} eq 'G' ) {
     if( $result->{lights} ) {
-      $hash->{lights} = join( ",", @{$result->{lights}} );
+      $hash->{lights} = join( ",", sort { $a <=> $b } @{$result->{lights}} );
     } else {
       $hash->{lights} = '';
     }
@@ -1204,6 +1212,9 @@ HUEDevice_Parse($$)
       $hash->{sunriseoffset} = $config->{sunriseoffset} if( defined($config->{sunriseoffset}) );
       $hash->{sunsetoffset} = $config->{sunsetoffset} if( defined($config->{sunsetoffset}) );
 
+      $hash->{tholddark} = $config->{tholddark} if( defined($config->{tholddark}) );
+      $hash->{sensitivity} = $config->{sensitivity} if( defined($config->{sensitivity}) );
+
       $readings{battery} = $config->{battery} if( defined($config->{battery}) );
       $readings{reachable} = $config->{reachable} if( defined($config->{reachable}) );
     }
@@ -1241,7 +1252,7 @@ HUEDevice_Parse($$)
       $readings{state} = $state->{status} if( defined($state->{status}) );
       $readings{state} = $state->{flag}?'1':'0' if( defined($state->{flag}) );
       $readings{state} = $state->{open}?'open':'closed' if( defined($state->{open}) );
-      $readings{state} = $state->{lightlevel} if( defined($state->{lightlevel}) );
+      $readings{state} = $state->{lightlevel} if( defined($state->{lightlevel}) && !defined($state->{lux}) );
       $readings{state} = $state->{buttonevent} if( defined($state->{buttonevent}) );
       $readings{state} = $state->{presence}?'motion':'nomotion' if( defined($state->{presence}) );
 
@@ -1249,6 +1260,13 @@ HUEDevice_Parse($$)
       $readings{humidity} = $state->{humidity} * 0.01 if( defined($state->{humidity}) );
       $readings{daylight} = $state->{daylight}?'1':'0' if( defined($state->{daylight}) );
       $readings{temperature} = $state->{temperature} * 0.01 if( defined($state->{temperature}) );
+      $readings{pressure} = $state->{pressure} if( defined($state->{pressure}) );
+      $readings{lightlevel} = $state->{lightlevel} if( defined($state->{lightlevel}) );
+      $readings{lux} = $state->{lux} if( defined($state->{lux}) );
+      $readings{power} = $state->{power} if( defined($state->{power}) );
+      $readings{voltage} = $state->{voltage} if( defined($state->{voltage}) );
+      $readings{current} = $state->{current} if( defined($state->{current}) );
+      $readings{consumption} = $state->{consumption} if( defined($state->{consumption}) );
     }
 
     if( scalar keys %readings ) {
@@ -1559,13 +1577,13 @@ HUEDevice_Attr($$$;$)
       <li>dimUp [delta]</li>
       <li>dimDown [delta]</li>
       <li>ct &lt;value&gt; [&lt;ramp-time&gt;]<br>
-        set colortemperature to &lt;value&gt; in mireds (range is 154-500) or kelvin (rankge is 2000-6493).</li>
+        set colortemperature to &lt;value&gt; in mireds (range is 154-500) or kelvin (range is 2000-6493).</li>
       <li>ctUp [delta]</li>
       <li>ctDown [delta]</li>
       <li>hue &lt;value&gt; [&lt;ramp-time&gt;]<br>
         set hue to &lt;value&gt;; range is 0-65535.</li>
-      <li>humUp [delta]</li>
-      <li>humDown [delta]</li>
+      <li>hueUp [delta]</li>
+      <li>hueDown [delta]</li>
       <li>sat &lt;value&gt; [&lt;ramp-time&gt;]<br>
         set saturation to &lt;value&gt;; range is 0-254.</li>
       <li>satUp [delta]</li>
@@ -1621,6 +1639,8 @@ HUEDevice_Attr($$$;$)
       2 -> use lamp color scaled to full brightness as icon color and dim state as icon shape</li>
     <li>createActionReadings<br>
       create readings for the last action in group devices</li>
+    <li>createGroupReadings<br>
+      create 'artificial' readings for group devices. default depends on the createGroupReadings setting in the bridge device.</li>
     <li>ignoreReachable<br>
       ignore the reachable state that is reported by the hue bridge. assume the device is allways reachable.</li>
     <li>setList<br>

+ 29 - 24
fhem/core/FHEM/31_LightScene.pm

@@ -1,5 +1,5 @@
 
-# $Id: 31_LightScene.pm 15146 2017-09-28 06:28:07Z justme1968 $
+# $Id: 31_LightScene.pm 16181 2018-02-14 20:20:12Z justme1968 $
 
 package main;
 
@@ -942,46 +942,51 @@ LightScene_editTable($) {
   $dd.=FW_hidden("detail",$hash->{NAME}) . "\n";
   $dd.="</form>\n";
   # make table
+  my @devices;
+  if( $scn && defined($hash->{SCENES}{$scn}) ) {
+    if( defined($hash->{switchingOrder}) && defined($hash->{switchingOrder}{$scn}) ) {
+      @devices = @{$hash->{switchingOrder}{$scn}};
+    } else {
+      @devices = @{$hash->{devices}};
+    }
+  } else {
+    $scn = '';
+  }
+
   if ($scn eq "Choose scene" || $scn eq '') {
     $html.="<table><tr><td>Edit scene</td><td>$dd</td></tr>";
   } else {
-	$html.="<table><tr><td>Edit scene</td><td>$dd</td></tr></table>";
+    $html.="<table><tr><td>Edit scene</td><td>$dd</td></tr></table>";
     $html .= '<table class="block wide">';
     $html .= '<tr><th>Device</th><th>Command</th></tr>'."\n";
     my $row=0;
-	my @devices;
-    if( $scn && defined($hash->{switchingOrder}) && defined($hash->{switchingOrder}{$scn}) ) {
-      @devices = @{$hash->{switchingOrder}{$scn}};
-    } else {
-      @devices = @{$hash->{devices}};
-    }
     #table rows
-	my @cmds    = qw(set setcmd);
+    my @cmds    = qw(set setcmd);
     my $set     = "set $hash->{NAME} set $scn";
     my $setcmd  = '';
     foreach my $dev (@devices) {
       $row+=1;
       $html .= "<tr class=\"".(($row&1)?"odd":"even")."\">";
-	  $html .= "<td>$dev</td>";
-	  my $default = $hash->{SCENES}{$scn}{$dev};
+      $html .= "<td>$dev</td>";
+      my $default = $hash->{SCENES}{$scn}{$dev};
 
       if ($hash->{SCENES}{$scn}{$dev} =~ m/^;/) {
-	    $default =~ s/^;//;
-		$setcmd='setcmd';
+        $default =~ s/^;//;
+        $setcmd='setcmd';
       } else {
-		$setcmd='set';
-	  }
-	  $default = $default->{state} if( ref($default) eq 'HASH' );
-	  $html.="<td><form method=\"get\" action=\"" . $FW_ME . "/LightScene\">\n";
+        $setcmd='set';
+      }
+      $default = $default->{state} if( ref($default) eq 'HASH' );
+      $html.="<td><form method=\"get\" action=\"" . $FW_ME . "/LightScene\">\n";
       $html.=FW_select('',"cmd1", \@cmds, $setcmd, 'select')."\n";
-	  $html.=FW_textfieldv("val.$dev", 50, 'class',$default)."\n";
-	  $html.=FW_hidden("dev.$dev", $dev) . "\n";
-	  $html.=FW_hidden("cmd.$dev", $set) . "\n";
+      $html.=FW_textfieldv("val.$dev", 50, 'class',$default)."\n";
+      $html.=FW_hidden("dev.$dev", $dev) . "\n";
+      $html.=FW_hidden("cmd.$dev", $set) . "\n";
       $html.=FW_submit("lse", 'saveline');
-  	  $html.=FW_hidden("scn", $scn) . "\n";
-	  $html.=FW_hidden("detail",$hash->{NAME}) . "\n";
-	  $html .= "</form></td>\n";
-	}
+      $html.=FW_hidden("scn", $scn) . "\n";
+      $html.=FW_hidden("detail",$hash->{NAME}) . "\n";
+      $html .= "</form></td>\n";
+    }
   }
   #table end
   $html .= "</table><br>\n";

+ 3 - 1
fhem/core/FHEM/31_MilightDevice.pm

@@ -1,4 +1,4 @@
-# $Id: 31_MilightDevice.pm 11177 2016-04-03 11:40:34Z markus-m $
+# $Id: 31_MilightDevice.pm 16085 2018-02-04 18:35:23Z mattwire $
 ##############################################
 #
 #     31_MilightDevice.pm (Based on 32_WifiLight.pm by hermannj)
@@ -2354,6 +2354,8 @@ sub MilightDevice_roundfunc($) {
 1;
 
 =pod
+=item device
+=item summary This module represents a Milight LED Bulb or LED strip controller
 =begin html
 
 <a name="MilightDevice"></a>

+ 4 - 3
fhem/core/FHEM/31_PLAYBULB.pm

@@ -25,7 +25,7 @@
 #  GNU General Public License for more details.
 #
 #
-# $Id: 31_PLAYBULB.pm 15107 2017-09-20 19:12:40Z CoolTux $
+# $Id: 31_PLAYBULB.pm 15650 2017-12-20 05:41:22Z CoolTux $
 #
 ###############################################################################
 
@@ -41,7 +41,7 @@ use Blocking;
 use SetExtensions;
 
 
-my $version = "1.2.8";
+my $version = "1.4.0";
 
 
 
@@ -56,6 +56,7 @@ my %playbulbModels = (
         BTL400M_v18     => {'aColor' => '0x23'  ,'aEffect' => '0x21'    ,'aBattery' => '0x2e'   ,'aDevicename' => '0x7'},   # Garden
         BTL400M_v37     => {'aColor' => '0x1b'  ,'aEffect' => '0x19'    ,'aBattery' => '0x24'   ,'aDevicename' => '0x7'},   # Garden neue Version
         BTL100C_v10     => {'aColor' => '0x1b'  ,'aEffect' => '0x19'    ,'aBattery' => 'none'   ,'aDevicename' => 'none'},  # Color LED
+        BTL301W         => {'aColor' => '0x29'  ,'aEffect' => '0x27'    ,'aBattery' => '0x34'   ,'aDevicename' => '0x3'},   # Sphere Model BTL 301 W
     );
 
 my %effects = ( 
@@ -107,7 +108,7 @@ sub PLAYBULB_Initialize($) {
     $hash->{DefFn}      = "PLAYBULB_Define";
     $hash->{UndefFn}    = "PLAYBULB_Undef";
     $hash->{AttrFn}     = "PLAYBULB_Attr";
-    $hash->{AttrList}   = "model:BTL300_v5,BTL300_v6,BTL201_v2,BTL201M_V16,BTL505_v1,BTL400M_v18,BTL400M_v37,BTL100C_v10,BTL305_v14 ".
+    $hash->{AttrList}   = "model:BTL300_v5,BTL300_v6,BTL201_v2,BTL201M_V16,BTL505_v1,BTL400M_v18,BTL400M_v37,BTL100C_v10,BTL305_v14,BTL301W ".
                             "sshHost ".
                             $readingFnAttributes;
 

+ 80 - 81
fhem/core/FHEM/32_TechemHKV.pm

@@ -1,5 +1,5 @@
 ###############################################################################
-# $Id: 32_TechemHKV.pm 10660 2016-01-29 20:43:54Z herrmannj $
+# $Id: 32_TechemHKV.pm 15731 2017-12-30 21:10:18Z herrmannj $
 #
 # this module is part of fhem under the same license
 # copyright 2015, joerg herrmann
@@ -16,7 +16,7 @@ use warnings;
 use Time::HiRes qw(time);
 
 my %typeText = (
-  '80' => 'Funkheizkostenverteiler data IIl'
+  '80' => 'Funkheizkostenverteiler data III'
 );
 
 sub 
@@ -27,7 +27,7 @@ TechemHKV_Initialize(@) {
 
   # TECHEM HKV
   # 61, 64 without T1 and T2
-  $hash->{Match}      = "^b..446850[\\d]{8}(61|64|69)80....A0.*";
+  $hash->{Match}      = "^b..446850[\\d]{8}(61|64|69|94)80.*";
 
   $hash->{DefFn}      = "TechemHKV_Define";
   $hash->{UndefFn}    = "TechemHKV_Undef";
@@ -49,18 +49,17 @@ TechemHKV_Define(@) {
 
   return "ID must have 4 or 8 digits" if ($id !~ /^\d{4}(?:\d{4})?$/);
 
-  my $lid = (length($id) == 8)?$id:undef;
-  $id = (length($id) == 8)?substr($id,-4):$id;
-
   $modules{TechemHKV}{defptr}{$id} = $hash;
+
   $hash->{FRIENDLY} = $def if (defined($def));
-  $hash->{LID} = $id if (length($id) == 8);
+  $hash->{LONGID} = $id if (length($id) == 8);
   
   # create crc table if required
   $data{WMBUS}{crc_table_13757} = TechemHKV_createCrcTable() unless (exists($data{WMBUS}{crc_table_13757}));
 
   # subscribe broadcast channels 
   # TechemHKV_subscribe($hash, 'foo');
+  # TechemHKV_Parse($hash, 'b334468500180560094804C3AA20F9F211202B038E80411FD0B81104E6D6265006554261A1B000000000000000001DCBC1706085875BCFADDBEC0F25480');
   TechemHKV_Run($hash) if $init_done;
   return undef;
 }
@@ -69,7 +68,7 @@ sub
 TechemHKV_Undef(@) {
   my ($hash) = @_;
   return undef;
-}
+};
 
 sub
 TechemHKV_Set(@) {
@@ -77,19 +76,19 @@ TechemHKV_Set(@) {
   my $cnt = @args;
 
   return undef;
-}
+};
 
 sub
 TechemHKV_Get(@) {
   my ($hash) = @_;
   
   return undef;
-}
+};
 
 sub 
 TechemHKV_Notify (@) {
   my ($hash, $ntfyDev) = @_;
-  return unless (($ntfyDev->{TYPE} eq 'CUL') || ($ntfyDev->{TYPE} eq 'Global'));
+  return unless (($ntfyDev->{TYPE} =~ /CUL|STACKABLE/) || ($ntfyDev->{TYPE} eq 'Global'));
   foreach my $event (@{$ntfyDev->{CHANGED}}) {
     my @e = split(' ', $event);
     next unless defined($e[0]);
@@ -103,16 +102,16 @@ TechemHKV_Notify (@) {
       readingsBulkUpdate($hash, "temp1", "--.--") if exists($hash->{READINGS}->{'temp1'}); # exlude versions without t1,t2
       readingsBulkUpdate($hash, "temp2", "--.--") if exists($hash->{READINGS}->{'temp2'});
       readingsEndUpdate($hash, 1);
-    }
-  }
+    };
+  };
   return undef;
-}
+};
 
 sub
 TechemHKV_Receive(@) {
   my ($hash, $msg) = @_;
 	
-  $hash->{longID} = $msg->{long} unless defined($hash->{longID});
+  $hash->{LONGID} = $msg->{long} unless defined($hash->{LONGID});
   # TODO log collision if any ...	
 
   my @t = localtime(time);
@@ -122,12 +121,12 @@ TechemHKV_Receive(@) {
   $hash->{METER} = $typeText{$msg->{type}};
   delete $hash->{CHANGETIME}; # clean up, workaround for fhem prior http://forum.fhem.de/index.php/topic,47474.msg391964.html#msg391964
   
-  if (($msg->{version} || '') eq '69') {
+  if (($msg->{version} || '') =~ /69|94/) {
     readingsBeginUpdate($hash);
     readingsBulkUpdate($hash, "temp1", $msg->{temp1});
     readingsBulkUpdate($hash, "temp2", $msg->{temp2});
     readingsEndUpdate($hash, 1);
-  }
+  };
 
   # day period changed
   $ats = ReadingsTimestamp($hash->{NAME},"current_period", "0");
@@ -140,7 +139,7 @@ TechemHKV_Receive(@) {
     readingsBulkUpdate($hash, "current_period", $msg->{actualVal});
     $hash->{CHANGETIME}->[$#{ $hash->{CHANGED} }] = $ts if ($#{ $hash->{CHANGED} } != $i ); # only add ts if there is a event to
     readingsEndUpdate($hash, 1);
-  }
+  };
 
   # billing period changed
   $ats = ReadingsTimestamp($hash->{NAME},"previous_period", "0");
@@ -153,10 +152,9 @@ TechemHKV_Receive(@) {
     readingsBulkUpdate($hash, "previous_period", $msg->{lastVal});
     $hash->{CHANGETIME}->[$#{ $hash->{CHANGED} }] = $ts if ($#{ $hash->{CHANGED} } != $i ); # only add ts if there is a event to
     readingsEndUpdate($hash, 1);
-  }
-
+  };
   return undef;
-}
+};
 
 sub 
 TechemHKV_Run(@) {
@@ -164,7 +162,7 @@ TechemHKV_Run(@) {
   # find a CUL
   foreach my $d (keys %defs) {
     # live patch CUL.pm
-    TechemHKV_IOPatch($hash, $d) if ($defs{$d}{TYPE} eq "CUL");
+    TechemHKV_IOPatch($hash, $d) if ($defs{$d}{TYPE} =~ /CUL|STACKABLE/);
   }
   return undef;
 }
@@ -191,22 +189,64 @@ TechemHKV_Parse(@) {
   $msg = TechemHKV_SanityCheck($msg);
   return ('') unless $msg;
   
-  my @m = ($msg =~ m/../g);
-
-  # parse
-  ($message->{long}, $message->{short}) = TechemHKV_ParseID(@m);
-  $message->{type} = TechemHKV_ParseSubType(@m);
-  $message->{version} = TechemHKV_ParseSubVersion(@m);
-  $message->{lastVal} = TechemHKV_ParseLastPeriod(@m);
-  $message->{actualVal} = TechemHKV_ParseActualPeriod(@m);
-  $message->{temp1} = TechemHKV_ParseT1(@m);
-  $message->{temp2} = TechemHKV_ParseT2(@m);
-  ($message->{actual}->{year}, $message->{actual}->{month}, $message->{actual}->{day}) = TechemHKV_ParseActualDate(@m);
-  ($message->{last}->{year}, $message->{last}->{month}, $message->{last}->{day}) = TechemHKV_ParseLastDate(@m);
+  $message->{long} = join '', reverse split /(..)/, substr $msg, 6, 8;
+  $message->{short} = substr $message->{long}, 4, 4;
+  $message->{version} = substr $msg, 14, 2;
+  $message->{type} = substr $msg, 16, 2;
+  
+  # last_date
+  #if ($message->{version} eq '94') {
+  #  ($message->{last}->{year}, $message->{last}->{month}, $message->{last}->{day}) 
+  #    = TechemHKV_ParseLastDate(join '', reverse split /(..)/, substr $msg, 24, 4);
+  #} else {
+    ($message->{last}->{year}, $message->{last}->{month}, $message->{last}->{day}) 
+      = TechemHKV_ParseLastDate(join '', reverse split /(..)/, substr $msg, 22, 4);
+  #}  
+  # previous_period
+  #if ($message->{version} eq '94') {
+  #  $message->{lastVal} = hex(join '', reverse split /(..)/, substr $msg, 28, 4);
+  #} else {
+    $message->{lastVal} = hex(join '', reverse split /(..)/, substr $msg, 26, 4);
+  #}
+  
+  # actual_date
+  #if ($message->{version} eq '94') {
+  #  ($message->{actual}->{year}, $message->{actual}->{month}, $message->{actual}->{day}) 
+  #    = TechemHKV_ParseActualDate(join '', reverse split /(..)/, substr $msg, 32, 4);
+  #} else {
+    ($message->{actual}->{year}, $message->{actual}->{month}, $message->{actual}->{day}) 
+      = TechemHKV_ParseActualDate(join '', reverse split /(..)/, substr $msg, 30, 4);
+  #}
+  # actual_period
+  #if ($message->{version} eq '94') {
+  #  $message->{actualVal} = hex(join '', reverse split /(..)/, substr $msg, 36, 4);
+  #} else {
+    $message->{actualVal} = hex(join '', reverse split /(..)/, substr $msg, 34, 4);
+  #}
+  
+  # temp sensor 1
+  if ($message->{version} eq '94') {
+    $message->{temp1} = sprintf "%.2f", (hex(join '', reverse split /(..)/, substr $msg, 40, 4) / 100);
+  } elsif ($message->{version} eq '69') {
+    $message->{temp1} = sprintf "%.2f", (hex(join '', reverse split /(..)/, substr $msg, 38, 4) / 100);
+  }
 
+  # temp sensor 2
+  if ($message->{version} eq '94') {
+    $message->{temp2} = sprintf "%.2f", (hex(join '', reverse split /(..)/, substr $msg, 44, 4) / 100);
+  } elsif ($message->{version} eq '69') {
+    $message->{temp2} = sprintf "%.2f", (hex(join '', reverse split /(..)/, substr $msg, 42, 4) / 100);
+  }
+  
   # dispatch
-  if (exists($modules{TechemHKV}{defptr}{$message->{short}})) {
+  if (exists($modules{TechemHKV}{defptr}{$message->{long}})) {
+    my $deviceHash = $modules{TechemHKV}{defptr}{$message->{long}};
+    TechemHKV_Receive($deviceHash, $message);
+    return ($deviceHash->{NAME});
+  } elsif (exists($modules{TechemHKV}{defptr}{$message->{short}})) {
     my $deviceHash = $modules{TechemHKV}{defptr}{$message->{short}};
+    $modules{TechemHKV}{defptr}{$message->{long}} = $deviceHash;
+    delete($modules{TechemHKV}{defptr}{$message->{short}});
     TechemHKV_Receive($deviceHash, $message);
     return ($deviceHash->{NAME});
   }
@@ -267,56 +307,14 @@ TechemHKV_SanityCheck(@) {
       $t .= substr $msg, 25 + ($fb * 36), ($rb * 2);
     }
   }
+  Log3 ("TechemHKV", $dbg, "ok $t");
   return $t;
 }
 
 sub
-TechemHKV_ParseID(@) {
-  my @m = @_;
-  return ("$m[6]$m[5]$m[4]$m[3]", "$m[4]$m[3]");
-}
-
-sub
-TechemHKV_ParseSubType(@) {
-  my @m = @_;
-  return "$m[8]"; 
-}
-
-sub
-TechemHKV_ParseSubVersion(@) {
-  my @m = @_;
-  return "$m[7]"; 
-}
-
-sub
-TechemHKV_ParseLastPeriod(@) {
-  my @m = @_;
-  return hex("$m[14]$m[13]"); 
-}
-
-sub
-TechemHKV_ParseActualPeriod(@) {
-  my @m = @_;
-  return hex("$m[18]$m[17]"); 
-}
-
-sub
-TechemHKV_ParseT1(@) {
-  my @m = @_;
-  return sprintf "%.2f", (hex("$m[20]$m[19]") / 100); 
-}
-
-sub
-TechemHKV_ParseT2(@) {
-  my @m = @_;
-  return sprintf "%.2f", (hex("$m[22]$m[21]") / 100); 
-}
-
-sub
 TechemHKV_ParseActualDate(@) {
-  my @m = @_;
+  my $b = hex($_[0]);
   my @t = localtime(time);
-  my $b = hex("$m[16]$m[15]");
   my $d = ($b >> 4) & 0x1F;
   my $m = ($b >> 9) & 0x0F;
   my $y = $t[5] + 1900;
@@ -325,8 +323,7 @@ TechemHKV_ParseActualDate(@) {
 
 sub
 TechemHKV_ParseLastDate(@) {
-  my @m = @_;
-  my $b = hex("$m[12]$m[11]");
+  my $b = hex($_[0]);
   my $d = ($b >> 0) & 0x1F;
   my $m = ($b >> 5) & 0x0F;
   my $y = ($b >> 9) & 0x3F;
@@ -397,6 +394,8 @@ TechemHKV_crc16_13757(@) {
 1;
 
 =pod
+=item summary    read techem data meter for heating device.
+=item summary_DE Anbindung von Techem Heizkostenverteilern.
 =begin html
 
 <a name="TechemHKV"></a>

+ 4 - 11
fhem/core/FHEM/32_WifiLight.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 32_WifiLight.pm 14607 2017-06-30 21:16:09Z herrmannj $
+# $Id: 32_WifiLight.pm 15907 2018-01-16 20:58:44Z herrmannj $
 
 # TODO
 
@@ -159,7 +159,7 @@ WifiLight_Define($$)
     # if so, we need a shared buffer (llCmdQueue), shared socket and we need to check if the requied slot is free
     foreach $key (keys %defs) 
     {
-      if (($defs{$key}{TYPE} eq 'WifiLight') && ($defs{$key}{IP} eq $hash->{IP}) && ($key ne $name))
+      if (($defs{$key}{TYPE} eq 'WifiLight') && ($defs{$key}{IP} eq $hash->{IP}) && ($defs{$key}{PORT} eq $hash->{PORT}) && ($key ne $name))
       {
         #bridge is in use
         Log3 (undef, 3, "WifiLight: requested bridge $hash->{CONNECTION} at $hash->{IP} already in use by $key, copy llCmdQueue");
@@ -3826,15 +3826,8 @@ sub
 WifiLight_HighLevelCmdQueue_Clear(@)
 {
   my ($ledDevice) = @_;
-  foreach my $a (keys %intAt) 
-  {
-    if (($intAt{$a}{ARG} eq $ledDevice) && ($intAt{$a}{FN} eq 'WifiLight_HighLevelCmdQueue_Exec'))
-    {
-
-      Log3 ($ledDevice, 4, "$ledDevice->{NAME} high level cmd queue clear, remove timer at ".$intAt{$a}{TRIGGERTIME} );
-      delete($intAt{$a}) ;
-    }
-  }
+  Log3 ($ledDevice, 4, "$ledDevice->{NAME} high level cmd queue clear");
+  RemoveInternalTimer($ledDevice, 'WifiLight_HighLevelCmdQueue_Exec');
   $ledDevice->{helper}->{hlCmdQueue} = [];
 }
 

+ 2 - 2
fhem/core/FHEM/32_mailcheck.pm

@@ -1,5 +1,5 @@
 
-# $Id: 32_mailcheck.pm 15207 2017-10-06 17:24:43Z justme1968 $
+# $Id: 32_mailcheck.pm 16299 2018-03-01 08:06:55Z justme1968 $
 
 # basic idea from https://github.com/justinribeiro/idlemailcheck
 
@@ -520,7 +520,7 @@ mailcheck_Read($)
   <ul>
     <code>define &lt;name&gt; mailcheck &lt;host&gt; &lt;user&gt; &lt;password&gt; [&lt;folder&gt;]</code><br>
     <br>
-    &lt;user&gt; and <lt>&lt;password&gt; can be of the form {perl-code}. no spaces are allowed. for both evals $NAME and $HOST is set to the name and host of the mailcheck device and $USER is set to the user in the password eval.
+    &lt;user&gt; and &lt;password&gt; can be of the form {perl-code}. no spaces are allowed. for both evals $NAME and $HOST is set to the name and host of the mailcheck device and $USER is set to the user in the password eval.
     <br>
 
     Defines a mailcheck device.<br><br>

+ 37 - 14
fhem/core/FHEM/32_withings.pm

@@ -1,5 +1,5 @@
 ##############################################################################
-# $Id: 32_withings.pm 14576 2017-06-25 22:46:20Z moises $
+# $Id: 32_withings.pm 16296 2018-02-28 22:12:56Z moises $
 #
 #  32_withings.pm
 #
@@ -10,7 +10,7 @@
 #
 #
 ##############################################################################
-# Release 04 / 2017-06-25
+# Release 05 / 2018-03-01
 
 package main;
 
@@ -677,7 +677,7 @@ sub withings_connect($) {
   foreach my $d (keys %defs) {
     next if(!defined($defs{$d}));
     next if($defs{$d}{TYPE} ne "autocreate");
-    return undef if(AttrVal($defs{$d}{NAME},"disable",undef));
+    return undef if(IsDisabled($defs{$d}{NAME}));
   }
 
   my $autocreated = 0;
@@ -717,6 +717,12 @@ sub withings_connect($) {
       my $d = $modules{$hash->{TYPE}}{defptr}{"D$device->{deviceid}"};
       $d->{association} = $device->{association} if($device->{association});
 
+      #get user from association
+      if(defined($device->{deviceproperties})){
+        $d->{User} = $device->{deviceproperties}{linkuserid} if(defined($device->{deviceproperties}{linkuserid}));
+        $d->{color} = $device->{deviceproperties}{product_color} if(defined($device->{deviceproperties}{product_color}));
+      }
+      
       Log3 $name, 2, "$name: device '$device->{deviceid}' already defined";
       next;
     }
@@ -795,6 +801,12 @@ sub withings_autocreate($) {
       my $d = $modules{$hash->{TYPE}}{defptr}{"D$device->{deviceid}"};
       $d->{association} = $device->{association} if($device->{association});
 
+      #get user from association
+      if(defined($device->{deviceproperties})){
+        $d->{User} = $device->{deviceproperties}{linkuserid} if(defined($device->{deviceproperties}{linkuserid}));
+        $d->{color} = $device->{deviceproperties}{product_color} if(defined($device->{deviceproperties}{product_color}));
+      }
+
       Log3 $name, 2, "$name: device '$device->{deviceid}' already defined";
       next;
     }
@@ -1449,7 +1461,7 @@ sub withings_poll($;$) {
 
   RemoveInternalTimer($hash);
 
-  return undef if(AttrVal($name,"disable",0) eq "1");
+  return undef if(IsDisabled($name));
 
 
   my $resolve = inet_aton("healthmate.withings.com");
@@ -1576,6 +1588,17 @@ sub withings_getUserReadingsDaily($) {
       callback => \&withings_Dispatch,
   });
 
+#  HttpUtils_NonblockingGet({
+#    url => "https://scalews.health.nokia.com/cgi-bin/v2/activity",
+#    timeout => 60,
+#    noshutdown => 1,
+#    data => {sessionid => $hash->{IODev}->{SessionKey},  userid=> $hash->{User}, startdateymd => $startdateymd, enddateymd => $enddateymd, appname => 'hmw', appliver => $hash->{IODev}->{helper}{appliver}, apppfm => 'web', action => 'getbyuserid'},
+#      hash => $hash,
+#      type => 'userDailyActivity',
+#      enddate => int($enddate),
+#      callback => \&withings_Dispatch,
+#  });
+
   my ($seconds) = gettimeofday();
   $hash->{LAST_POLL} = FmtDateTime( $seconds );
   readingsSingleUpdate( $hash, ".pollDaily", $seconds, 0 );
@@ -1760,8 +1783,8 @@ sub withings_parseMeasureGroups($$) {
   my $name = $hash->{NAME};
   #parse
   Log3 "withings", 5, "$name: parsemeasuregroups";
-  my ($now) = time;
-  my $lastupdate = ReadingsVal( $name, ".lastData", 0 );
+  my ($now) = int(time);
+  my $lastupdate = ReadingsVal( $name, ".lastData", ($now-21*24*60*60) );
   my $newlastupdate = $lastupdate;
 
     $hash->{status} = $json->{status};
@@ -1828,7 +1851,7 @@ sub withings_parseMeasurements($$) {
   #parse
   Log3 "withings", 5, "$name: parsemeasurements";
   my ($now) = time;
-  my $lastupdate = ReadingsVal( $name, ".lastData", 0 );
+  my $lastupdate = ReadingsVal( $name, ".lastData", ($now-21*24*60*60) );
   my $newlastupdate = $lastupdate;
   my $i = 0;
 
@@ -1917,7 +1940,7 @@ sub withings_parseAggregate($$) {
 
   #return undef;
   my ($now) = time;
-  my $lastupdate = ReadingsVal( $name, ".lastAggregate", 0 );
+  my $lastupdate = ReadingsVal( $name, ".lastAggregate", ($now-21*24*60*60) );
   my $newlastupdate = $lastupdate;
   my $i = 0;
   my $unfinished;
@@ -2031,7 +2054,7 @@ sub withings_parseActivity($$) {
   Log3 "withings", 5, "$name: parseactivity";
 
   my ($now) = time;
-  my $lastupdate = ReadingsVal( $name, ".lastActivity", 0 );
+  my $lastupdate = ReadingsVal( $name, ".lastActivity", ($now-21*24*60*60) );
   my $newlastupdate = $lastupdate;
   my $i = 0;
   my $unfinished;
@@ -2140,8 +2163,8 @@ sub withings_parseVasistas($$;$) {
   Log3 "withings", 5, "$name: parsevasistas";
 
   my ($now) = time;
-  my $lastupdate = ReadingsVal( $name, ".lastData", 0 );
-  $lastupdate = ReadingsVal( $name, ".lastDebug", 0 ) if($datatype =~ /Debug/);
+  my $lastupdate = ReadingsVal( $name, ".lastData", ($now-21*24*60*60) );
+  $lastupdate = ReadingsVal( $name, ".lastDebug", ($now-21*24*60*60) ) if($datatype =~ /Debug/);
 
   if( $json ) {
     $hash->{status} = $json->{status};
@@ -2254,7 +2277,7 @@ sub withings_parseTimeline($$) {
   Log3 "withings", 5, "$name: parsemetimeline ";
 
   my ($now) = time;
-  my $lastupdate = ReadingsVal( $name, ".lastAlert", 0 );
+  my $lastupdate = ReadingsVal( $name, ".lastAlert", ($now-21*24*60*60) );
   my $newlastupdate = $lastupdate;
 
     $hash->{status} = $json->{status};
@@ -2344,8 +2367,8 @@ sub withings_parseEvents($$) {
   #parse
   Log3 "withings", 5, "$name: parseevents";
   my ($now) = time;
-  my $lastupdate = ReadingsVal( $name, ".lastData", 0 );
-  my $lastalertupdate = ReadingsVal( $name, ".lastAlert", 0 );
+  my $lastupdate = ReadingsVal( $name, ".lastData", ($now-21*24*60*60) );
+  my $lastalertupdate = ReadingsVal( $name, ".lastAlert", ($now-21*24*60*60) );
   my $newlastupdate = $lastupdate;
 
     $hash->{status} = $json->{status};

+ 4 - 3
fhem/core/FHEM/33_readingsChange.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 33_readingsChange.pm 14888 2017-08-13 12:07:12Z rudolfkoenig $
+# $Id: 33_readingsChange.pm 15609 2017-12-14 22:19:18Z rudolfkoenig $
 package main;
 
 use strict;
@@ -14,7 +14,7 @@ readingsChange_Initialize($)
 
   $hash->{DefFn} = "readingsChangeDefine";
   $hash->{NotifyFn} = "readingsChangeExec";
-  $hash->{AttrList} ="disable:1,0 disabledForIntervals";
+  $hash->{AttrList} ="disable:1,0 disabledForIntervals addStateEvent:1,0";
   $hash->{NotifyOrderPrefix} = "00-"; # be the first
 }
 
@@ -65,7 +65,7 @@ readingsChangeExec($$)
   my $NAME = $dev->{NAME};
   return if($NAME !~ m/$re->[0]/ || !$dev->{READINGS});
 
-  my $events = $dev->{CHANGED};
+  my $events = deviceEvents($dev, AttrVal($SELF, "addStateEvent", 0));
   return if(!$events);
   my $max = int(@{$events});
 
@@ -154,6 +154,7 @@ readingsChangeExec($$)
   <ul>
     <li><a href="#disable">disable</a></li>
     <li><a href="#disabledForIntervals">disabledForIntervals</a></li>
+    <li><a href="#addStateEvent">addStateEvent</a></li>
   </ul>
   <br>
 

+ 249 - 4
fhem/core/FHEM/33_readingsGroup.pm

@@ -1,4 +1,4 @@
-# $Id: 33_readingsGroup.pm 15100 2017-09-19 21:21:27Z justme1968 $
+# $Id: 33_readingsGroup.pm 16299 2018-03-01 08:06:55Z justme1968 $
 ##############################################################################
 #
 #     This file is part of fhem.
@@ -1700,7 +1700,7 @@ readingsGroup_Attr($$$;$)
 =pod
 =item helper
 =item summary    display a formated collection of readings from devices
-=item summary_DE Stellt eine formatierte Readings von Ger&auml;te dar
+=item summary_DE Stellt eine formatierte Darstellung aus Readings von Ger&auml;te bereit
 =begin html
 
 <a name="readingsGroup"></a>
@@ -1718,7 +1718,7 @@ readingsGroup_Attr($$$;$)
     Notes:
     <ul>
       <li>&lt;device&gt; can be of the form INTERNAL=VALUE where INTERNAL is the name of an internal value and VALUE is a regex.</li>
-      <li>&lt;device&gt; can be of the form ATTRIBUTE&VALUE where ATTRIBUTE is the name of an attribute and VALUE is a regex.</li>
+      <li>&lt;device&gt; can be of the form ATTRIBUTE&amp;VALUE where ATTRIBUTE is the name of an attribute and VALUE is a regex.</li>
       <li>&lt;device&gt; can be of the form &lt;STRING&gt; or &lt;{perl}&gt; where STRING or the string returned by perl is
           inserted as a line in the readings list. skipped if STRING is undef.</li>
       <li>&lt;device&gt; can be a devspec (see <a href="#devspec">devspec</a>) with at least one FILTER expression.</li>
@@ -1732,7 +1732,7 @@ readingsGroup_Attr($$$;$)
            <li>You can use an i:, r: or a: prefix instead of + and ? analogue to the devspec filtering.</li>
            <li>The suffix :d retrieves the first number.</li>
            <li>The suffix :i retrieves the integer part of the first number.</li>
-           <li>The suffix :r&lt;n&gt; retrieves the first number and rounds it to &lt;n&gt; decimal places. If <n> is missing, then rounds it to one decimal place.</li>
+           <li>The suffix :r&lt;n&gt; retrieves the first number and rounds it to &lt;n&gt; decimal places. If &lt;n&gt; is missing, then rounds it to one decimal place.</li>
            <li>The suffix :t returns the timestamp (works only for readings).</li>
            <li>The suffix :sec returns the number of seconds since the reading was set. probably not realy usefull with readingsGroups.</li>
          </ul></li>
@@ -1964,4 +1964,249 @@ readingsGroup_Attr($$$;$)
 </ul>
 
 =end html
+=begin html_DE
+
+<a name="readingsGroup"></a>
+<h3>readingsGroup</h3>
+<ul>
+  Zeigt eine Sammlung von Messwerten von einem oder mehreren Ger&auml;ten an.
+
+  <br><br>
+  <a name="readingsGroup_Define"></a>
+  <b>Define</b>
+  <ul>
+    <code>define &lt;name&gt; readingsGroup &lt;device&gt;[:regex] [&lt;device-2&gt;[:regex-2]] ... [&lt;device-n&gt;[:regex-n]]</code><br>
+    <br>
+
+    Anmerkungen:
+    <ul>
+	  <li>&lt;device&gt; kann die Form INTERNAL=VALUE haben, wobei INTERNAL der Name eines internen Wertes ist und VALUE ein Regex.</li>
+      <li>&lt;device&gt; kann die Form ATTRIBUTE&VALUE haben, wobei ATTRIBUTE der Name eines Attributs ist und VALUE ein Regex.</li>
+	  <li>&lt;device&gt; kann die Form &lt;STRING&gt; oder &lt;{perl}&gt; haben, wobei STRING oder die von Perl zur&uuml;ckgegebene Zeichenfolge als Zeile in die Readings List eingef&uuml;gt wird. Wird &uuml;bersprungen, wenn STRING undef ist.</li>
+      <li>&lt;device&gt; kann ein devspec sein (siehe <a href="#devspec">devspec</a>) mit mindestens einem FILTER-Ausdruck sein.</li>
+	  <li>Wenn Regex eine Komma separarierte Liste ist, werden die Reading-Values in einer einzelnen Zeile angezeigt.</li>
+      <li>Wenn Regex mit einem "+" beginnt, wird es mit den internen Werten (Internals) des Ger&auml;ts anstelle der Readings verglichen.</li>
+	  <li>Wenn Regex mit einem '?' beginnt, wird es mit den Attributen des Ger&auml;ts verglichen und nicht mit den Werten (Readings) verglichen.</li>
+	  <li>Wenn Regex mit einem '!' beginnt, wird die Anzeige des Wertes erzwungen, auch wenn kein Reading mit diesem Namen verf&uuml;gbar ist.</li>
+	  <li>Wenn Regex mit einem '$' beginnt, ist die Berechnung mit Wert-Spalten und Zeilen m&ouml;glich.</li>
+	  <li>Die folgenden <a href="#set">"set magic"</a> Pr&auml;fixe und Suffixe k&ouml;nnen mit Regex verwendet werden:
+         <ul>
+           <li>Sie k&ouml;nnen anstelle von + und ? ein Pr&auml;fix i :, r: oder a: verwenden. Analog zur devspec-Filterung.</li>
+           <li>Der Suffix :d ruft die erste Nummer ab.</li>
+           <li>Der Suffix :i ruft den ganzzahligen Teil der ersten Zahl ab.</li>
+           <li>Der Suffix :r&lt;n&gt; ruft die erste Zahl ab und rundet sie auf &lt;n&gt; Nachkommastellen ab. Wenn &lt;n&gt; fehlt, wird es auf eine Dezimalstelle gerundet.</li>
+		   <li>Der Suffix :t gibt den Zeitstempel zur&uuml;ck (funktioniert nur mit Readings).</li>
+           <li>Der Suffix :sec gibt die Anzahl der Sekunden seit dem das Reading gesetzt wurde zur&uuml;ck. Wahrscheinlich nicht n&uuml;tzlich mit readingsGroups.</li>
+		 </ul></li>
+      <li>Regex kann von der Form &lt;regex&gt;@device sein, um Readings von einem anderen Ger&auml;t zu verwenden.<br>
+		  Wenn der Ger&auml;tename mit einem '!' beginnt, wird die Anzeige deaktiviert. Verwenden Sie in Verbindung mit ! den Reading-Name.</li>
+      <li>Regex kann die Form &lt;regex&gt;@{perl} haben, um Readings von einem anderen Ger&auml;t zu verwenden.</li>
+      <li>Regex kann von der Form &lt;STRING&gt; oder &lt;{perl}[@readings]&gt; sein, wobei STRING oder die von Perl zur&uuml;ckgegebene Zeichenfolge als Reading eingef&uuml;gt wird, oder:
+          <ul><li>das Element wird &uuml;bersprungen, wenn STRING undef ist</li>
+              <li>wenn STRING br ist, wird eine neue Zeile gestartet</li>
+              <li>wenn STRING hr ist, wird eine horizontale Linie eingef&uuml;gt</li>
+              <li>wenn STRING tfoot ist, wird der Tabellenfu&szlig; gestartet</li>
+              <li>wenn STRING die Form hat, %ICON[%CMD] ICON wird als Name eines Symbols anstelle von Text und CMD als der Befehl verwendet, der ausgef&uuml;hrt werden soll, wenn auf das Symbol geklickt wird. Siehe auch die Befehlsattribute.</li></ul>
+          Wenn Readings aktualisiert werden, wird der Perl-Ausdruck bei Longpoll-Aktualisierungen erneut ausgewertet.</li>
+      <li>Wenn der erste Regex '@&lt;index&gt;' ist, gibt es den Index der folgenden Regex an, mit dem die Messwerte gruppiert werden sollen. Wenn Erfassungsgruppen verwendet werden, k&ouml;nnen sie durch #&lt;number&gt; refferenziert werden. z.Bsp:<br><ul>
+          <code>&lt;IP-Adress&gt;&lt;Hostname&gt;&lt;MAC&gt;&lt;Vendor&gt;<br>
+          nmap:@2,<#1>,(.*)_hostname,#1_macAddress,#1_macVendor</code></ul></li>
+      <li>F&uuml;r interne Werte (Internals) und Attribute ist longpoll update nicht m&ouml;glich. Aktualisieren Sie die Seite, um die Werte zu aktualisieren.</li>
+      <li>Der Ausdruck &lt;{perl}&gt; ist auf Ausdr&uuml;cke ohne Leerzeichen beschr&auml;nkt. Es ist am besten, eine kleine Sub in 99_myUtils.pm aufzurufen, anstatt einen complexen Ausdruck im Define zu haben.</li>
+    </ul><br>
+
+    Beispiele:
+    <ul>
+      <code>
+        define batteries readingsGroup .*:battery</code><br>
+      <br>
+        <code>define temperatures readingsGroup s300th.*:temperature</code><br>
+        <code>define temperatures readingsGroup TYPE=CUL_WS:temperature</code><br>
+      <br>
+        <code>define culRSSI readingsGroup cul_RSSI=.*:+cul_RSSI</code><br>
+      <br>
+        <code>define heizung readingsGroup t1:temperature t2:temperature t3:temperature<br>
+        attr heizung notime 1<br>
+        attr heizung mapping {'t1.temperature' => 'Vorlauf', 't2.temperature' => 'R&amp;uuml;cklauf', 't3.temperature' => 'Zirkulation'}<br>
+        attr heizung style style="font-size:20px"<br>
+      <br>
+        define systemStatus readingsGroup sysstat<br>
+        attr systemStatus notime 1<br>
+        attr systemStatus nostate 1<br>
+        attr systemStatus mapping {'load' => 'Systemauslastung', 'temperature' => 'Systemtemperatur in &amp;deg;C'}<br>
+      <br>
+        define Verbrauch readingsGroup TYPE=PCA301:state,power,consumption<br>
+        attr Verbrauch mapping %ALIAS<br>
+        attr Verbrauch nameStyle style="font-weight:bold"<br>
+        attr Verbrauch style style="font-size:20px"<br>
+        attr Verbrauch valueFormat {power => "%.1f W", consumption => "%.2f kWh"}<br>
+        attr Verbrauch valueIcon { state => '%devStateIcon' }<br>
+        attr Verbrauch valueStyle {($READING eq "power" && $VALUE > 150)?'style="color:red"':'style="color:green"'}<br>
+      <br>
+        define rg_battery readingsGroup TYPE=LaCrosse:[Bb]attery<br>
+        attr rg_battery alias Batteriestatus<br>
+        attr rg_battery commands { "battery.low" => "set %DEVICE replaceBatteryForSec 60" }<br>
+        attr rg_battery valueIcon {'battery.ok' => 'batterie', 'battery.low' => 'batterie@red'}<br>
+      <br>
+        define rgMediaPlayer readingsGroup myMediaPlayer:currentTitle,<>,totaltime,<br>,currentAlbum,<>,currentArtist,<br>,volume,<{if(ReadingsVal($DEVICE,"playStatus","")eq"paused"){"%rc_PLAY%set+$DEVICE+play"}else{"%rc_PAUSE%set+$DEVICE+pause"}}@playStatus>,playStatus<br>
+        attr rgMediaPlayer commands { "playStatus.paused" => "set %DEVICE play", "playStatus.playing" => "set %DEVICE pause" }<br>
+        attr rgMediaPlayer mapping &nbsp;<br>
+        attr rgMediaPlayer notime 1<br>
+        attr rgMediaPlayer valueFormat { "volume" => "Volume: %i" }<br>
+        #attr rgMediaPlayer valueIcon { "playStatus.paused" => "rc_PLAY", "playStatus.playing" => "rc_PAUSE" }<br>
+      </code><br>
+    </ul>
+  </ul><br>
+
+  <a name="readingsGroup_Set"></a>
+    <b>Set</b>
+    <ul>
+      <li>hide<br>
+      Alle sichtbaren Instanzen dieser ReadingsGroup werden ausgeblendet</li>
+      <li>show<br>
+      Zeigt alle sichtbaren Instanzen dieser ReadingsGroup an</li>
+      <li>toggle<br>
+      Schaltet den versteckten / angezeigten Zustand aller sichtbaren Instanzen dieser ReadingsGroup an.</li>
+      <li>toggle2<br>
+      schaltet den erweiterten / kollabierten Zustand aller sichtbaren Instanzen dieser ReadingsGroup an.</li>
+    </ul><br>
+
+  <a name="readingsGroup_Get"></a>
+    <b>Get</b>
+    <ul>
+    </ul><br>
+
+  <a name="readingsGroup_Attr"></a>
+    <b>Attribute</b>
+    <ul>
+      <li>alwaysTrigger<br>
+        1 -> alwaysTrigger Ereignisse aktualisieren auch wenn nicht sichtbar.<br>
+        2 -> trigger Ereignisse f&uuml;r berechnete Werte.</li><br>
+      <li>disable<br>
+        1 -> Deaktivieren der Benachrichtigung Verarbeitung und Longpoll-Updates. Hinweis: Dadurch wird auch die Umbenennung und L&ouml;schbehandlung deaktiviert.<br>
+        2 -> Deaktivieren der HTML-Tabellenerstellung<br>
+        3 -> Deaktivieren der HTML-Erstellung vollst&auml;ndig</li><br>
+      <li>sortDevices<br>
+        1 -> Sortieren der Ger&auml;teliste alphabetisch. Verwenden Sie das erste von sortby oder alias oder name, das f&uuml;r jedes Ger&auml;t definiert ist.</li>
+      <li>noheading<br>
+        Wenn sie auf 1 gesetzt ist, hat die Readings-Tabelle keine &Uuml;berschrift.</li><br>
+      <li>nolinks<br>
+        Deaktiviert die HTML-Links von der &Uuml;berschrift und den Readings-Namen.</li><br>
+      <li>nostate<br>
+        Wenn der Wert 1 ist, wird der Status nicht ber&uuml;cksichtigt.</li><br>
+      <li>nonames<br>
+        Wenn der Wert auf 1 gesetzt ist, wird der Readings-Name / Zeilentitel nicht angezeigt.</li><br>
+      <li>notime<br>
+        Wenn der Wert auf 1 gesetzt, wird der Readings-Timestamp nicht angezeigt.</li><br>
+      <li>mapping<br>
+        Kann ein einfacher String oder ein in {} eingeschlossener Perl-Ausdruck sein, der einen Hash zur&uuml;ckgibt, der den Reading-Name dem angezeigten Namen zuordnet. 
+		Der Schl&uuml;ssel kann entweder der Name des Readings oder &lt;device&gt;.&lt;reading&gt; oder &lt;reading&gt;.&lt;value&gt; oder &lt;device&gt;.&lt;reading&gt;.&lt;value&gt; sein.
+        %DEVICE, %ALIAS, %ROOM, %GROUP, %ROW und %READING werden durch den Ger&auml;tenamen, Ger&auml;tealias, Raumattribut ersetzt. Sie k&ouml;nnen diesen Keywords auch ein Pr&auml;fix voranstellen $ anstatt von %. Beispiele:<br>
+          <code>attr temperatures mapping $DEVICE-$READING</code><br>
+          <code>attr temperatures mapping {temperature => "%DEVICE Temperatur"}</code>
+        </li><br>
+      <li>separator<br>
+        Das zu verwendende Trennzeichen zwischen dem Ger&auml;tealias und dem Reading-Namen, wenn keine Zuordnung angegeben ist, standardgem&auml;&szlig; ':' 
+        Ein Leerzeichen wird so dargestellt <code>&amp;nbsp;</code></li><br>
+      <li>setList<br>
+        Eine durch Leerzeichen getrennte Liste von Befehlen, die zur&uuml;ckgegeben werden "set name ?",
+        Das FHEMWEB-Frontend kann also ein Dropdown-Men&uuml; erstellen und An / Aus-Schalter anbieten.
+        Set-Befehle, die nicht in dieser Liste enthalten sind, werden zur&uuml;ckgewiesen.</li><br>
+      <li>setFn<br>
+        Perl-Ausdruck, der f&uuml;r die Befehle aus der setList ausgef&uuml;hrt wird. Es hat Zugriff auf $CMD und $ARGS.</li><br>
+      <li>style<br>
+        Geben Sie einen HTML-Stil f&uuml;r die Readings-Tabelle an, z.Bsp:<br>
+          <code>attr temperatures style style="font-size:20px"</code></li><br>
+      <li>cellStyle<br>
+        Geben Sie einen HTML-Stil f&uuml;r eine Zelle der Readings-Tabelle an. Normale Zeilen und Spalten werden gez&auml;hlt beginnend mit 1,
+        Die Zeilen&uuml;berschriften beginnt mit der Spaltennummer 0. Perl-Code hat Zugriff auf $ROW und $COLUMN. Schl&uuml;ssel f&uuml;r Hash-Lookup k&ouml;nnen sein: 
+        r:#, c:# oder r:#,c:# , z.Bsp:<br>
+          <code>attr temperatures cellStyle { "c:0" => 'style="text-align:right"' }</code></li><br>
+      <li>nameStyle<br>
+        Geben Sie einen HTML-Stil f&uuml;r die Readings-Namen an, z.Bsp:<br>
+          <code>attr temperatures nameStyle style="font-weight:bold"</code></li><br>
+      <li>valueStyle<br>
+        Geben Sie einen HTML-Stil f&uuml;r die Readings-Werte an, z.Bsp:<br>
+          <code>attr temperatures valueStyle style="text-align:right"</code></li><br>
+      <li>valueColumn<br>
+        Geben Sie die Mindestspalte an, in der ein Messwert angezeigt werden soll. z.Bsp:<br>
+          <code>attr temperatures valueColumn { temperature => 2 }</code></li><br>
+      <li>valueColumns<br>
+        Geben Sie einen HTML-Colspan f&uuml;r die Readings-Werte an, z.Bsp:<br>
+          <code>attr wzReceiverRG valueColumns { eventdescription => 'colspan="4"' }</code></li><br>
+      <li>valueFormat<br>
+        Geben Sie eine Sprintf-Stilformat-Zeichenfolge an, die zum Anzeigen der Readings-Werte verwendet wird. Wenn die Formatzeichenfolge undef ist 
+        wird dieser Messwert &uuml;bersprungen. Es kann als String angegeben werden, ein Perl-Ausdruck, der einen Hash- oder Perl-Ausdruck zur&uuml;ckgibt, der einen String zur&uuml;ckgibt, z.Bsp:<br>
+          <code>attr temperatures valueFormat %.1f &deg;C</code><br>
+          <code>attr temperatures valueFormat { temperature => "%.1f &deg;C", humidity => "%i %" }</code><br>
+          <code>attr temperatures valueFormat { ($READING eq 'temperature')?"%.1f &deg;C":undef }</code></li><br>
+      <li>valuePrefix<br>
+        Text, der dem Readings-Wert vorangestellt wird</li><br>
+      <li>valueSuffix<br>
+        Text, der nach dem Readings-Wert angeh&auml;ngt wird<br>
+          <code>attr temperatures valueFormat { temperature => "%.1f", humidity => "%i" }</code><br>
+          <code>attr temperatures valueSuffix { temperature => "&deg;C", humidity => " %" }</code></li><br>
+      <li>nameIcon<br>
+        Geben Sie das Symbol an, das anstelle des Readings-Name verwendet werden soll. Es kann ein einfacher String oder ein in {} eingeschlossener Perl-Ausdruck sein, der einen Hash zur&uuml;ckgibt, der dem Readings-Name den Icon-Namen zuordnet. z.Bsp:<br>
+          <code>attr devices nameIcon $DEVICE</code></li><br>
+      <li>valueIcon<br>
+        Geben Sie ein Symbol an, das anstelle des Readings-Wert verwendet werden soll. Es kann ein einfacher String oder ein in {} eingeschlossener Perl-Ausdruck sein, der einen Hash zur&uuml;ckgibt, der dem Readings-Wert dem Symbolnamen zuordnet. z.Bsp:<br>
+          <code>attr devices valueIcon $VALUE</code><br>
+          <code>attr devices valueIcon {state => '%VALUE'}</code><br>
+          <code>attr devices valueIcon {state => '%devStateIcon'}</code><br>
+          <code>attr rgMediaPlayer valueIcon { "playStatus.paused" => "rc_PLAY", "playStatus.playing" => "rc_PAUSE" }</code></li><br>
+      <li>commands<br>
+        Kann auf verschiedene Arten verwendet werden:
+        <ul>
+        <li>Um ein Reading oder ein Symbol anklickbar zu machen, indem Sie direkt den Befehl angeben, der ausgef&uuml;hrt werden soll. z.Bsp:<br>
+        <code>attr rgMediaPlayer commands { "playStatus.paused" => "set %DEVICE play", "playStatus.playing" => "set %DEVICE pause" }</code></li>
+        <li>Wenn der zugeordnete Befehl die Form &lt;command&gt;:[&lt;modifier&gt;] hat, wird das normale <a href="#FHEMWEB">FHEMWEB</a> webCmd-Widget f&uuml;r <Modifikator> f&uuml;r diesen commands verwendet. z.Bsp:<br>
+        <code>attr rgMediaPlayer commands { volume => "volume:slider,0,1,100" }</code><br>
+        <code>attr lights commands { pct => "pct:", dim => "dim:" }</code></li>
+        <li>commands k&ouml;nnen f&uuml;r Attribute verwendet werden. z.Bsp:<br>
+        <code>attr <rg> commands { disable => "disable:" }</code></li>
+        </ul></li><br>
+      <li>visibility<br>
+        Wenn sie auf hidden oder hideable eingestellt ist, wird eine kleine Schaltfl&auml;che links neben dem Namen der Readings-Group angezeigt, um den Inhalt der Readings-Group zu erweitern / auszublenden. Wenn eine Readings-Group erweitert wird, werden alle anderen Gruppen derselben Gruppe ausgeblendet.<br>
+        <ul>
+        hidden -> Standardm&auml;&szlig;ig ist hidden aktiv, kann jedoch erweitert werden.<br>
+        hideable -> Standardm&auml;&szlig;ig ist hideable aktiv, kann jedoch ausgeblendet werden.<br><br>
+        </ul>
+        Wenn diese Option auf "collapsed" oder "collapsible" eingestellt ist, erkennt readingsGroup die Specials &lt;-&gt;,&lt;+&gt; und &lt;+-&gt; als die ersten Elemente von
+        eine Linie, um dieser Linie ein + oder - Symbol hinzuzuf&uuml;gen. Durch Klicken auf das + oder - Symbol wird zwischen erweitertem und reduziertem Zustand umgeschaltet. Wenn eine Readings-Group erweitert wird, werden alle anderen Gruppen in der gleichen Gruppe ausgeblendet.
+        <ul>
+        - -> Die Linie wird im expandierten Zustand sichtbar sein.<br>
+        + -> Die Linie wird im zusammengefalteten Zustand angezeigt.<br>
+        +- -> Die Linie wird in beiden Zust&auml;nden sichtbar sein.<br>
+        <br>
+        collapsed -> Der Standardstatus ist reduziert, kann jedoch erweitert werden.<br>
+        collapsible -> Der Standardstatus ist sichtbar, kann jedoch minimiert werden.<br><br></li>
+        </ul>
+        <li>headerRows<br><br>
+        </li>
+        <li>sortColumn<br>
+          &gt; 0 -> sortiert die Tabelle automatisch nach dem Laden der Seite nach dieser Spalte<br>
+          0 -> sortiert Sie nicht automatisch, sondern durch Klicken auf eine Spalten&uuml;berschrift<br>
+          &lt; 0 -> sortiert die Tabelle automatisch nach dem Laden der Seite nach dieser Spalte
+        </li>
+        <br><li><a href="#perlSyntaxCheck">perlSyntaxCheck</a></li>
+    </ul><br>
+
+      F&uuml;r die Hash-Version aller Zuordnungsattribute kann ein Standardwert angegeben werden mit <code>{ '' => &lt;default&gt; }</code>.<br><br>
+
+      Die Stilattribute k&ouml;nnen auch einen in {} eingeschlossenen Perl-Ausdruck enthalten, der die zu verwendende Stilzeichenfolge zur&uuml;ckgibt. F&uuml;r nameStyle und valueStyle, kann der Perl-Code $DEVICE,$READING,$VALUE und $NUM verwendet werden. z.Bsp:<br>
+      <ul>
+          <code>attr batteries valueStyle {($VALUE ne "ok")?'style="color:red"':'style="color:green"'}</code><br>
+          <code>attr temperatures valueStyle {($DEVICE =~ m/aussen/)?'style="color:green"':'style="color:red"'}</code>
+      </ul><br>
+
+      Hinweis: Nur valueStyle, valueFomat, valueIcon und <{...}@reading> werden bei Longpoll-Updates ausgewertet und valueStyle muss f&uuml;r jeden m&ouml;glichen Wert einen nicht leeren Stil zur&uuml;ckgeben. Alle anderen Perl-Ausdr&uuml;cke werden nur einmal w&auml;hrend der HTML-Erstellung ausgewertet und geben keine Wertupdates mit longpoll wieder. 
+      Aktualisieren Sie die Seite, um den dynamischen Stil zu aktualisieren. F&uuml;r nameStyle funktioniert das Farbattribut momentan nicht, die font -... und background Attribute funktionieren.<br><br>
+
+      Berechnung: Bitte sehen Sie sich daf&uuml;r diese <a href="http://www.fhemwiki.de/wiki/ReadingsGroup#Berechnungen">Beschreibung</a> an in der Wiki.<br>
+      z.Bsp: <code>define rg readingsGroup .*:temperature rg:$avg</code>
+      
+</ul>
+
+=end html_DE
 =cut

+ 2 - 2
fhem/core/FHEM/33_readingsHistory.pm

@@ -1,4 +1,4 @@
-# $Id: 33_readingsHistory.pm 15100 2017-09-19 21:21:27Z justme1968 $
+# $Id: 33_readingsHistory.pm 16299 2018-03-01 08:06:55Z justme1968 $
 ##############################################################################
 #
 #     This file is part of fhem.
@@ -740,7 +740,7 @@ readingsHistory_Attr($$$)
     <ul>
       <li>&lt;device&gt; can be of the form INTERNAL=VALUE where INTERNAL is the name of an internal value and VALUE is a regex.</li>
       <li>If regex is a comma separatet list it will be used as an enumeration of allowed readings.</li>
-      <li>if no device/reading argument is given only lines with 'set <device> add ...' are displayed.</li>
+      <li>if no device/reading argument is given only lines with 'set &lt;device&gt; add ...' are displayed.</li>
     </ul><br>
 
     Examples:

+ 2 - 2
fhem/core/FHEM/33_readingsProxy.pm

@@ -1,4 +1,4 @@
-# $Id: 33_readingsProxy.pm 14044 2017-04-20 07:48:44Z justme1968 $
+# $Id: 33_readingsProxy.pm 16299 2018-03-01 08:06:55Z justme1968 $
 ##############################################################################
 #
 #     This file is part of fhem.
@@ -410,7 +410,7 @@ readingsProxy_Attr($$$;$)
         has access to $DEVICE, $READING, $CMD and $ARGS.<br>
         undef -> do nothing<br>
         ""    -> pass through<br>
-        (<value>,1) -> directly return <value>, don't call parent getFn<br>
+        (&lt;value&gt;,1) -> directly return &lt;value&gt;, don't call parent getFn<br>
         everything else -> use this instead</li>
       <li>setFn<br>
         perl expresion that will return the set command forwarded to the parent device.

+ 59 - 15
fhem/core/FHEM/34_ESPEasy.pm

@@ -1,4 +1,4 @@
-# $Id: 34_ESPEasy.pm 14936 2017-08-21 04:12:53Z dev0 $
+# $Id: 34_ESPEasy.pm 16307 2018-03-02 06:45:27Z dev0 $
 ################################################################################
 #
 #  34_ESPEasy.pm is a FHEM Perl module to control ESP8266 /w ESPEasy
@@ -36,7 +36,7 @@ use Color;
 # ------------------------------------------------------------------------------
 # global/default values
 # ------------------------------------------------------------------------------
-my $module_version    = "1.33";     # Version of this module
+my $module_version    = "1.38";     # Version of this module
 my $minEEBuild        = 128;        # informational
 my $minJsonVersion    = 1.02;       # checked in received data
 
@@ -71,10 +71,13 @@ my %ESPEasy_setCmds = (
   "pwmfade"        => "3",
   "pulse"          => "3",
   "longpulse"      => "3",
+  "longpulse_ms"   => "3",
   "servo"          => "3",
   "lcd"            => "3",
   "lcdcmd"         => "1",
   "mcpgpio"        => "2",
+  "mcppulse"       => "3", # forum 82174
+  "mcplongpulse"   => "3", # forum 82174
   "oled"           => "3",
   "oledcmd"        => "1",
   "pcapwm"         => "2",
@@ -122,10 +125,13 @@ my %ESPEasy_setCmdsUsage = (
   "pwm"            => "pwm <pin> <level>",
   "pulse"          => "pulse <pin> <0|1|off|on> <duration>",
   "longpulse"      => "longpulse <pin> <0|1|off|on> <duration>",
+  "longpulse_ms"   => "longpulse_ms <pin> <0|1|off|on> <duration>",
   "servo"          => "servo <servoNo> <pin> <position>",
   "lcd"            => "lcd <row> <col> <text>",
   "lcdcmd"         => "lcdcmd <on|off|clear>",
-  "mcpgpio"        => "mcpgpio <pin> <0|1|off|on>",
+  "mcpgpio"        => "mcpgpio <port> <0|1|off|on>",
+  "mcppulse"       => "mcppulse <port> <0|1|off|on> <duration>",     # forum 82174
+  "mcplongpulse"   => "mcplongpulse <port> <0|1|off|on> <duration>", # forum 82174
   "oled"           => "oled <row> <col> <text>",
   "oledcmd"        => "oledcmd <on|off|clear>",
   "pcapwm"         => "pcapwm <pin> <Level>",
@@ -598,6 +604,9 @@ sub ESPEasy_Set($$@)
       elsif ($params[$pp-1] =~ m/^on$/i) {
         $state = 1;
       }
+      elsif ($params[$pp-1] =~ m/^toggle$/i) {
+        $state = "toggle";
+      }
       else {
         Log3 $name, 2, "$type $name: $cmd ".join(" ",@params)." => unknown argument: '$params[$pp-1]'";
         return undef;
@@ -607,9 +616,10 @@ sub ESPEasy_Set($$@)
     }
 
     if ($cmd eq "help") {
-      my $usage = $ESPEasy_setCmdsUsage{$params[0]};
-      $usage     =~ s/Note:/\nNote:/g;
-      return "Usage: set $name $usage";
+      my $eecmd = $params[0];
+      my $usage = $ESPEasy_setCmdsUsage{$eecmd};
+      $usage     =~ s/Note:/\nNote:/g if $usage;
+      return $usage ? "Usage: set $name $usage" : "unknown command or help not available for '$eecmd'";
     }
 
     if ($cmd eq "statusrequest") {
@@ -760,10 +770,20 @@ sub ESPEasy_Read($) {
       return;
     }
 
-    my $ident = ESPEasy_isCombineDevices($peer,$espName,AttrVal($bname,"combineDevices",0))
+    my $cd = ESPEasy_isCombineDevices($peer,$espName,AttrVal($bname,"combineDevices",0));
+    my $ident = $cd
       ? $espName ne "" ? $espName : $peer
       : $espName.($espName ne "" && $espDevName ne "" ? "_" : "").$espDevName;
 
+    my $d0;
+    Log3 $bname, 4, "$btype $name: Src:'$json->{data}{ESP}{name}'/'"
+                  . "$json->{data}{SENSOR}{0}{deviceName}' => ident:$ident "
+                  . "dev:"
+                  . ( ($d0=(devspec2array("i:IDENT=$ident:FILTER=i:TYPE=$btype"))[0])
+                       ? $d0 
+                       : "undef" )
+                  . " combinedDevice:".$cd;
+
     # push internals in @values
     my @values;
     my @intVals = qw(unit sleep build build_git build_notes version node_type_id);
@@ -1155,7 +1175,7 @@ sub ESPEasy_dispatch($$$@) #called by bridge -> send to logical devices
   my $ac = (AttrVal($bname,"autocreate",AttrVal("global","autoload_undefined_devices",1))) ? 1 : 0;
   my $msg = $ident."::".$host."::".$ac."::".$as."::".$ui."::".join("|||",@values);
 
-  Log3 $bname, 5, "$type $name: Dispatch: $msg";
+#  Log3 $bname, 5, "$type $name: Dispatch: $msg";
   Dispatch($bhash, $msg, undef);
 
   return undef;
@@ -2764,7 +2784,7 @@ sub ESPEasy_removeGit($)
     </li>
   </ul>
 
-  <h3>ESPEasy Bridge</h3>
+  <h4>ESPEasy Bridge</h4>
 
   <a name="ESPEasy_bridge_define"></a>
   <b>Define </b>(bridge)<br><br>
@@ -2973,7 +2993,7 @@ sub ESPEasy_removeGit($)
       </li><br>
   </ul>
 
-  <h3>ESPEasy Device</h3>
+  <h4>ESPEasy Device</h4>
 
   <a name="ESPEasy_device_define"></a>
   <b>Define </b>(logical device)<br><br>
@@ -3142,13 +3162,21 @@ sub ESPEasy_removeGit($)
     </li><br>
 
     <li><a name="ESPEasy_device_set_logpulse">LongPulse</a><br>
-      Direct pulse control of output pins<br>
+      Direct pulse control of output pins (duration in s)<br>
       <ul>
         <li>arguments: <code>&lt;pin&gt; &lt;0|1|off|on&gt; &lt;duration&gt;</code></li>
         <li>example: <code>set &lt;esp&gt; longpulse 14 on 10</code></li>
       </ul>
     </li><br>
 
+    <li><a name="ESPEasy_device_set_logpulse_ms">LongPulse_ms</a><br>
+      Direct pulse control of output pins (duration in ms)<br>
+      <ul>
+        <li>arguments: <code>&lt;pin&gt; &lt;0|1|off|on&gt; &lt;duration&gt;</code></li>
+        <li>example: <code>set &lt;esp&gt; longpulse_ms 14 on 10000</code></li>
+      </ul>
+    </li><br>
+
     <li><a name="ESPEasy_device_set_pcfgpio">PCFGpio</a><br>
       Control PCF8574 (8-bit I/O expander for I2C-bus)<br>
       <ul>
@@ -3182,17 +3210,33 @@ sub ESPEasy_removeGit($)
 			ESPEasy Wiki PCF8574</a>
     </li><br>
 
-    <li><a name="ESPEasy_device_set_mcpgpio">mcpgpio</a><br>
+    <li><a name="ESPEasy_device_set_mcpgpio">MCPGPIO</a><br>
       Control MCP23017 output pins (16-Bit I/O Expander with Serial Interface)<br>
 			<ul>
         <li>arguments: <code>&lt;port&gt; &lt;0|1|off|on&gt;</code></li>
-        <li>example: <code>set &lt;esp&gt; mcpgpio 48 on</code></li>
+        <li>example: <code>set &lt;esp&gt; MCPGPIO 48 on</code></li>
       </ul>
 			Port numbering see:
 			<a href="https://www.letscontrolit.com/wiki/index.php/MCP23017#Input">
 			ESPEasy Wiki MCP23017</a>
     </li><br>
 
+    <li><a name="ESPEasy_device_set_mcppulse">MCPPulse</a><br>
+      Pulse control on MCP23017 output pins (duration in ms)<br>
+			<ul>
+        <li>arguments: <code>&lt;port&gt; &lt;0|1|off|on&gt; &lt;duration&gt;</code></li>
+        <li>example: <code>set &lt;esp&gt; MCPPulse 48 on 100</code></li>
+      </ul>
+    </li><br>
+
+    <li><a name="ESPEasy_device_set_mcplongpulse">MCPLongPulse</a><br>
+      Longpulse control on MCP23017 output pins (duration in s)<br>
+			<ul>
+        <li>arguments: <code>&lt;port&gt; &lt;0|1|off|on&gt; &lt;duration&gt;</code></li>
+        <li>example: <code>set &lt;esp&gt; MCPLongPulse 48 on 2</code></li>
+      </ul>
+    </li><br>
+
     <li><a name="ESPEasy_device_set_pcapwm">pcapwm</a><br>
       Control PCA9685 (16-channel / 12-bit PWM I2C-bus controller)<br>
       <ul>
@@ -3417,10 +3461,10 @@ sub ESPEasy_removeGit($)
       Supported protocols are: NEC, JVC, RC5, RC6, SAMSUNG, SONY, PANASONIC at
       the moment. As long as official documentation is missing you can find
       some details here:
-      <a href="http://www.letscontrolit.com/forum/viewtopic.php?f=5&t=328" target="_NEW">
+      <a href="http://www.letscontrolit.com/forum/viewtopic.php?f=5&amp;t=328" target="_NEW">
       IR Transmitter thread #1</a> and  
       <a 
-      href="https://www.letscontrolit.com/forum/viewtopic.php?t=328&start=61" target="_NEW">
+      href="https://www.letscontrolit.com/forum/viewtopic.php?t=328&amp;start=61" target="_NEW">
       IR Transmitter thread #61</a>.<br>
       <ul>
         <li>

+ 51 - 13
fhem/core/FHEM/36_LaCrosse.pm

@@ -1,5 +1,5 @@
 
-# $Id: 36_LaCrosse.pm 15483 2017-11-23 20:03:23Z HCS $
+# $Id: 36_LaCrosse.pm 16168 2018-02-13 21:01:41Z HCS $
 
 
 package main;
@@ -168,7 +168,7 @@ sub LaCrosse_Parse($$) {
   my ($hash, $msg) = @_;
   my $name = $hash->{NAME};
 
-  my( @bytes, $addr, $typeNumber, $typeName, $battery_new, $battery_low, $error, $type, $channel, $temperature, $humidity, $windDirection, $windSpeed, $windGust, $rain, $pressure, $gas, $debug );
+  my( @bytes, $addr, $typeNumber, $typeName, $battery_new, $battery_low, $error, $type, $channel, $temperature, $humidity, $windDirection, $windSpeed, $windGust, $rain, $pressure, $gas1, $gas2, $lux, $version, $voltage, $debug );
   $temperature = 0xFFFF;
   $humidity = 0xFF;
   $windDirection = 0xFFFF;
@@ -176,7 +176,11 @@ sub LaCrosse_Parse($$) {
   $windGust = 0xFFFF;
   $rain = 0xFFFF;
   $pressure = 0xFFFF;
-  $gas = 0xFFFFFF;
+  $gas1 = 0xFFFFFF;
+  $gas2 = 0xFFFFFF;
+  $lux = 0xFFFFFF;
+  $version = 0xFF;
+  $voltage = 0xFF;
   $debug = 0xFFFFFF;
   $error = 0;
 
@@ -247,7 +251,7 @@ sub LaCrosse_Parse($$) {
     #                                 |---------- Low battery
 
     @bytes = split( ' ', substr($msg, 5) );
-
+    
     return "" if(@bytes < 14);
 
     $addr = sprintf( "%02X", $bytes[0] );
@@ -265,6 +269,9 @@ sub LaCrosse_Parse($$) {
     elsif($typeNumber == 4) {
       $typeName = "LaCrosseGateway";
     }
+    elsif($typeNumber == 5) {
+      $typeName = "UniversalSensor";
+    }
     else {
       $typeName = "unknown";
     }
@@ -319,14 +326,29 @@ sub LaCrosse_Parse($$) {
     }
   
     if(@bytes > 18 && $bytes[16] != 0xFF) {
-      $gas = $bytes[16] * 65536 + $bytes[17] * 256 + $bytes[18];
+      $gas1 = $bytes[16] * 65536 + $bytes[17] * 256 + $bytes[18];
     }
-  
+   
     if(@bytes > 21 && $bytes[19] != 0xFF) {
-      $debug = $bytes[19] * 65536 + $bytes[20] * 256 + $bytes[21];
-      readingsBulkUpdate($hash, "debug", $debug);
+      $gas2 = $bytes[19] * 65536 + $bytes[20] * 256 + $bytes[21];
     }
-
+  
+    if(@bytes > 24 && $bytes[22] != 0xFF) {
+      $lux = $bytes[22] * 65536 + $bytes[23] * 256 + $bytes[24];
+    }
+  
+    if(@bytes > 25 && $bytes[25] != 0xFF) {
+      $version = $bytes[25] / 10;
+    }
+  
+    if(@bytes > 26 && $bytes[26] != 0xFF) {
+      $voltage = $bytes[26] / 10;
+    }
+    
+    if(@bytes > 29 && $bytes[27] != 0xFF) {
+      $debug = $bytes[27] * 65536 + $bytes[28] * 256 + $bytes[29];
+    }
+  
   }
   else {
     DoTrigger($name, "UNKNOWNCODE $msg");
@@ -539,15 +561,31 @@ sub LaCrosse_Parse($$) {
       readingsBulkUpdate($rhash, "windDirectionText", $windDirectionText );
     }
 
-    if ($typeNumber > 0 && $pressure != 0xFFFF) {
+    if ($typeNumber > 1 && $pressure != 0xFFFF) {
       readingsBulkUpdate($rhash, "pressure", $pressure );
     }
   
-    if ($typeNumber > 0 && $gas != 0xFFFFFF) {
-      readingsBulkUpdate($rhash, "gas", $gas );
+    if ($typeNumber > 1  && $gas1 != 0xFFFFFF) {
+      readingsBulkUpdate($rhash, "gas1", $gas1 );
     }
   
-    if ($typeNumber > 0 && $debug != 0xFFFFFF) {
+    if ($typeNumber > 1 && $gas2 != 0xFFFFFF) {
+      readingsBulkUpdate($rhash, "gas2", $gas2 );
+    }
+  
+    if ($typeNumber == 5 && $lux != 0xFFFFFF) {
+      readingsBulkUpdate($rhash, "lux", $lux );
+    }
+  
+    if ($typeNumber == 5 && $version != 0xFF) {
+      readingsBulkUpdate($rhash, "version", $version );
+    }
+  
+    if ($typeNumber = 5 && $voltage != 0xFF) {
+      readingsBulkUpdate($rhash, "voltage", $voltage );
+    }
+    
+    if ($typeNumber > 1 && $debug != 0xFFFFFF) {
       readingsBulkUpdate($rhash, "debug", $debug );
     }
 

+ 9 - 6
fhem/core/FHEM/36_LaCrosseGateway.pm

@@ -1,4 +1,4 @@
-# $Id: 36_LaCrosseGateway.pm 15483 2017-11-23 20:03:23Z HCS $
+# $Id: 36_LaCrosseGateway.pm 15527 2017-11-30 14:25:16Z HCS $
 
 package main;
 
@@ -491,6 +491,9 @@ sub LaCrosseGateway_HandleKVP($$) {
   if($kvp =~ m/UpTimeText=(.*?)(\,|\ ,)/) {
     readingsBulkUpdate($hash, "UpTime", $1);
   }
+  if($kvp =~ m/UpTimeSeconds=(.*?)(\,|\ ,)/) {
+    readingsBulkUpdate($hash, "UpTimeSeconds", $1);
+  }
   if($kvp =~ m/RSSI=(.*?)(\,|\ ,)/) {
     readingsBulkUpdate($hash, "RSSI", $1);
   }
@@ -579,11 +582,11 @@ sub LaCrosseGateway_HandleOwnSensors($$) {
 
   readingsEndUpdate($hash, 1);
 
-  delete $hash->{READINGS}{"temperature"} if $temperature == undef;
-  delete $hash->{READINGS}{"humidity"} if $humidity == undef;
-  delete $hash->{READINGS}{"pressure"} if $pressure == undef;
-  delete $hash->{READINGS}{"gas"} if $gas == undef;
-  delete $hash->{READINGS}{"debug"} if $debug == undef;
+  delete $hash->{READINGS}{"temperature"} if(!defined($temperature));
+  delete $hash->{READINGS}{"humidity"} if(!defined($humidity));
+  delete $hash->{READINGS}{"pressure"} if(!defined($pressure));
+  delete $hash->{READINGS}{"gas"} if(!defined($gas));
+  delete $hash->{READINGS}{"debug"} if(!defined($debug));
 }
 
 #=======================================================================================

+ 2 - 2
fhem/core/FHEM/36_WMBUS.pm

@@ -1,7 +1,7 @@
 #
 #	kaihs@FHEM_Forum (forum.fhem.de)
 #
-# $Id: 36_WMBUS.pm 15410 2017-11-08 19:27:50Z kaihs $
+# $Id: 36_WMBUS.pm 16320 2018-03-03 20:25:22Z kaihs $
 #
 # 
 
@@ -583,7 +583,7 @@ WMBUS_Attr(@)
   <a name="WMBUSdefine"></a>
   <b>Define</b>
   <ul>
-    <code>define &lt;name&gt; WMBUS [&lt;manufacturer id&gt; &lt;identification number&gt; &lt;version&gt; &lt;type&gt; [&lt:MessageEncoding&gt;]]|&lt;bHexCode&gt;</code> <br>
+    <code>define &lt;name&gt; WMBUS [&lt;manufacturer id&gt; &lt;identification number&gt; &lt;version&gt; &lt;type&gt; [&lt;MessageEncoding&gt;]]|&lt;bHexCode&gt;</code> <br>
     <br>
     Normalerweise wird ein WMBus Device nicht manuell angelegt. Dies geschieht automatisch bem Empfang der ersten Nachrichten eines Ger&auml;tes &uuml;ber den 
     fhem <a href="#autocreate">autocreate</a> Mechanismus.

+ 10 - 8
fhem/core/FHEM/37_fakeRoku.pm

@@ -1,4 +1,4 @@
-# $Id: 37_fakeRoku.pm 13780 2017-03-23 13:25:41Z justme1968 $
+# $Id: 37_fakeRoku.pm 16381 2018-03-11 09:08:02Z justme1968 $
 
 package main;
 
@@ -39,7 +39,7 @@ fakeRoku_Initialize($)
   #$hash->{SetFn}    = "fakeRoku_Set";
   #$hash->{GetFn}    = "fakeRoku_Get";
   $hash->{AttrFn}   = "fakeRoku_Attr";
-  $hash->{AttrList} = "disable:1,0 favourites fhemIP httpPort reusePort:1,0";
+  $hash->{AttrList} = "disable:1,0 favourites fhemIP httpPort reusePort:1,0 serial";
 }
 
 #####################################
@@ -76,7 +76,6 @@ fakeRoku_Define($$)
 
   my $name = $a[0];
   my $id = $a[2];
-  $id = undef;
 
   $hash->{NAME} = $name;
   $hash->{ID} = $id?$id:'';
@@ -89,8 +88,10 @@ fakeRoku_Define($$)
   return "install IO::Socket::Multicast to use autodiscovery" if(!$fakeRoku_hasMulticast);
   $hash->{"HAS_IO::Socket::Multicast"} = $fakeRoku_hasMulticast;
 
-  $hash->{serial} = md5_hex(getUniqueId());
-  $hash->{serial} .= ":$hash->{ID}" if( $hash->{ID} );
+  $hash->{helper}{serial} = md5_hex(getUniqueId());
+  $hash->{helper}{serial} .= "-$hash->{ID}" if( $hash->{ID} );
+
+  $attr{$name}{serial} = $hash->{helper}{serial} if( !defined($attr{$name}{serial}) );
 
   $hash->{fhemHostname} = hostname();
   $hash->{fhemIP} = fakeRoku_getLocalIP();
@@ -471,7 +472,7 @@ fakeRoku_Parse($$;$$$)
         $msg .= fakeRoku_hash2header( { 'Cache-Control' => 'max-age=300',
                                                      'ST' => 'roku:ecp',
                                                'Location' => "http://$hash->{fhemIP}:$hash->{helper}{listener}{PORT}/",
-                                                    'USN' => "uuid:roku:ecp:$hash->{serial}", } );
+                                                    'USN' => "uuid:roku:ecp:". AttrVal($name,'serial',$hash->{helper}{serial}), } );
         $msg .= "\r\n";
 
         my $sin = sockaddr_in($peerport, inet_aton($peerhost));
@@ -520,8 +521,8 @@ fakeRoku_Parse($$;$$$)
                                                       modelName => ['FHEM'],
                                                     modelNumber => ['4200X'],
                                                        modelURL => ['http://www.fhem.de/'],
-                                                   serialNumber => [$hash->{serial}],
-                                                            UDN => ["uuid:roku:ecp:$hash->{serial}"],
+                                                   serialNumber => [AttrVal($name,'serial',$hash->{helper}{serial})],
+                                                            UDN => ["uuid:roku:ecp:".AttrVal($name,'serial',$hash->{helper}{serial})],
                                                     serviceList => [ { service => [ { serviceType => ['urn:roku-com:service:ecp:1'],
                                                                                         serviceId => ['urn:roku-com:serviceId:ecp1-0'],
                                                                                        controlURL => [''],
@@ -849,6 +850,7 @@ Log 1, "!!!!!!!!!!";
       not set -> set ReusePort on multicast socket if SO_REUSEPORT flag ist known. should work in most cases.<br>
       0 -> don't set ReusePort on multicast socket<br>
       1 -> set ReusePort on multicast socket</li>
+    <li>serial</li>
   </ul>
 
 </ul><br>

+ 13 - 11
fhem/core/FHEM/37_harmony.pm

@@ -1,5 +1,5 @@
 
-# $Id: 37_harmony.pm 15393 2017-11-05 11:06:17Z justme1968 $
+# $Id: 37_harmony.pm 16299 2018-03-01 08:06:55Z justme1968 $
 
 package main;
 
@@ -1019,6 +1019,7 @@ harmony_Read($)
             if( defined($decoded->{sleepTimerId}) ) {
               if( $decoded->{sleepTimerId} == -1 ) {
                 delete $hash->{sleeptimer};
+                DoTrigger( $name, "sleeptimer: expired" );
               } else {
                 harmony_sendEngineGet($hash, "gettimerinterval", "timerId=$decoded->{sleepTimerId}");
               }
@@ -1104,6 +1105,7 @@ harmony_Read($)
 
           } elsif( $content =~ m/engine\?gettimerinterval/ && $decoded ) {
             $hash->{sleeptimer} = FmtDateTime( gettimeofday() + $decoded->{interval} );
+            DoTrigger( $name, "sleeptimer: $hash->{sleeptimer}" );
 
           } elsif( $content =~ m/firmware\?/ && $decoded ) {
             Log3 $name, 4, "$name: firmware: $cdata";
@@ -1863,11 +1865,11 @@ harmony_decrypt($)
   <a name="harmony_Set"></a>
   <b>Set</b>
   <ul>
-    <li>activity &lt;id&gt;|&ltname&gt; [&lt;channel&gt;]<br>
+    <li>activity &lt;id&gt;|&lt;name&gt; [&lt;channel&gt;]<br>
       switch to this activit and optionally switch to &lt;channel&gt;</li>
     <li>channel &lt;channel&gt;<br>
       switch to &lt;channel&gt; in the current activity</li>
-    <li>command [&lt;id&gt;|&ltname&gt;] &lt;command&gt; [duration=&lt;duration&gt;]<br>
+    <li>command [&lt;id&gt;|&lt;name&gt;] &lt;command&gt; [duration=&lt;duration&gt;]<br>
       send the given ir command for the current activity or for the given device</li>
     <li>getConfig<br>
       request the configuration from the hub</li>
@@ -1883,7 +1885,7 @@ harmony_decrypt($)
       default -> 60 minutes</li>
     <li>sync<br>
       syncs the hub to the myHarmony config</li>
-    <li>hidDevice [&lt;id&gt;|&ltname&gt;]<br>
+    <li>hidDevice [&lt;id&gt;|&lt;name&gt;]<br>
       sets the target device for keyboard commands, if no device is given -> set the target to the
       default device for the current activity.</li>
     <li>text &lt;text&gt;<br>
@@ -1892,7 +1894,7 @@ harmony_decrypt($)
       moves the cursor by bluetooth/smart keaboard dongle. &lt;direction&gt; can be one of: up, down, left, right, pageUp, pageDown, home, end.</li>
     <li>special &lt;key&gt;<br>
       sends special key by bluetooth/smart keaboard dongle. &lt;key&gt; can be one of: previousTrack, nextTrack, stop, playPause, volumeUp, volumeDown, mute.</li>
-    <li>autocreate [&lt;id&gt;|&ltname&gt;]<br>
+    <li>autocreate [&lt;id&gt;|&lt;name&gt;]<br>
       creates a fhem device for a single/all device(s) in the harmony hub. if activities are startet the state
       of these devices will be updatet with the power state defined in these activites.</li>
     <li>update<br>
@@ -1907,7 +1909,7 @@ harmony_decrypt($)
     <li>active<br>
       activates the current device (see inactive).</li>
   </ul>
-  The command, hidDevice, text, cursor and special commmands are also available for the autocreated devices. The &lt;id&gt;|&ltname&gt; paramter hast to be omitted.<br><br>
+  The command, hidDevice, text, cursor and special commmands are also available for the autocreated devices. The &lt;id&gt;|&lt;name&gt; paramter hast to be omitted.<br><br>
 
   <a name="harmony_Get"></a>
   <b>Get</b>
@@ -1917,19 +1919,19 @@ harmony_decrypt($)
       parm = power -> list power state for each device in activity</li>
     <li>devices [&lt;param&gt;]<br>
       lists all devices</li>
-    <li>commands [&lt;id&gt;|&ltname&gt;]<br>
+    <li>commands [&lt;id&gt;|&lt;name&gt;]<br>
       lists the commands for the specified activity or for all activities</li>
-    <li>deviceCommands [&lt;id&gt;|&ltname&gt;]<br>
+    <li>deviceCommands [&lt;id&gt;|&lt;name&gt;]<br>
       lists the commands for the specified device or for all devices</li>
-    <li>activityDetail [&lt;id&gt;|&ltname&gt;]</li>
-    <li>deviceDetail [&lt;id&gt;|&ltname&gt;]</li>
+    <li>activityDetail [&lt;id&gt;|&lt;name&gt;]</li>
+    <li>deviceDetail [&lt;id&gt;|&lt;name&gt;]</li>
     <li>configDetail</li>
     <li>currentActivity<br>
       returns the current activity name</li>
     <li>showAccount<br>
       display obfuscated user and password in cleartext</li>
   </ul>
-  The commands commmand is also available for the autocreated devices. The &lt;id&gt;|&ltname&gt; paramter hast to be omitted.<br><br>
+  The commands commmand is also available for the autocreated devices. The &lt;id&gt;|&lt;name&gt; paramter hast to be omitted.<br><br>
 
 
   <a name="harmony_Attr"></a>

+ 199 - 9
fhem/core/FHEM/38_Broadlink.pm

@@ -1,10 +1,9 @@
 #original script by  https://github.com/mjg59/python-broadlink
 #some parts by 31_LightScene.pm
-# $Id: 38_Broadlink.pm 14362 2017-05-25 10:34:26Z daniel2311 $
+# $Id: 38_Broadlink.pm 15578 2017-12-09 11:44:57Z daniel2311 $
 package main;
 use strict;
 use warnings;
-use integer;
 use Time::Local;
 use IO::Socket::INET;
 use IO::Select;
@@ -66,7 +65,7 @@ sub Broadlink_Define($$) {
 	$hash->{'.id'} = pack('C*', 0, 0, 0, 0);
 	$hash->{counter} = 1;
 	$hash->{isAuthenticated} = 0;
-	if ($hash->{devtype} eq 'sp3') { #steckdose
+	if ($hash->{devtype} eq 'sp3' or $hash->{devtype} eq 'sp3s') { #steckdose
 		Broadlink_auth($hash);
 		if ($hash->{isAuthenticated} != 0) {
 			Broadlink_sp3_getStatus($hash, 1);
@@ -93,7 +92,7 @@ sub Broadlink_Get($@) {
 
 sub Broadlink_Set(@) {
 	my ($hash, $name, $cmd, @args) = @_;
-	if ($hash->{devtype} eq 'sp3') { #steckdose
+	if ($hash->{devtype} eq 'sp3' or $hash->{devtype} eq 'sp3s') { #steckdose
 		if ($cmd eq 'on') {
 			Broadlink_auth($hash);
 			if ($hash->{isAuthenticated} != 0) {
@@ -125,8 +124,18 @@ sub Broadlink_Set(@) {
 				Broadlink_sp3_getStatus($hash);
 			}
 			return undef;
+		} elsif ($cmd eq 'getEnergy' and $hash->{devtype} eq 'sp3s') {
+			Broadlink_auth($hash);
+			if ($hash->{isAuthenticated} != 0) {
+				Broadlink_sp3s_getEnergy($hash);
+			}
+			return undef;
 		} else {
-			return "Unknown argument $cmd, choose one of on off toggle getStatus";
+			if ($hash->{devtype} eq 'sp3s') {
+				return "Unknown argument $cmd, choose one of on off toggle getStatus getEnergy";
+			} else {
+				return "Unknown argument $cmd, choose one of on off toggle getStatus";
+			}
 		}    
 		return "$cmd. Try to get it.";
 	} else { #rmpro rmmini etc.
@@ -186,6 +195,12 @@ sub Broadlink_Set(@) {
 			delete($hash->{commandList}{$cmdname});
 			Broadlink_Save($hash);
 			return undef;
+		} elsif ($cmd eq 'getTemperature' and $hash->{devtype} eq 'rmpro') {
+			Broadlink_auth($hash);
+			if ($hash->{isAuthenticated} != 0) {
+				Broadlink_getTemperature($hash);
+			}
+			return undef;
 		} else {
 			#sort with ignore case
 			my $commandList = join(",", sort {
@@ -193,7 +208,11 @@ sub Broadlink_Set(@) {
 						|| $a cmp $b
 				} keys %{$hash->{commandList}});			
 			#return "Unknown argument $cmd, choose one of learnNewCommand sendCommand sendCommandBase64 sendCommandHex createCommandBase64 createCommandHex";
-			return "Unknown argument $cmd, choose one of recordNewCommand rename remove:" . $commandList . " commandSend:". $commandList;
+			if ($hash->{devtype} eq 'rmpro') {
+				return "Unknown argument $cmd, choose one of recordNewCommand rename getTemperature remove:" . $commandList . " commandSend:". $commandList;
+			} else {
+				return "Unknown argument $cmd, choose one of recordNewCommand rename remove:" . $commandList . " commandSend:". $commandList;
+			}
 		}    
 		return "$cmd. Try to get it.";
 	}
@@ -267,6 +286,41 @@ sub Broadlink_check_data(@) {
 	}	
 }
 
+sub Broadlink_getTemperature(@) {
+	my ($hash) = @_;
+	my @broadlink_payload = ((0) x  16);
+	$broadlink_payload[0] = 1;
+	
+	my $msg = "sp3_energy request";
+	Log3 $hash->{NAME}, 5, $hash->{NAME} . ": " . $msg;
+	my $data = Broadlink_send_packet($hash, 0x6a, @broadlink_payload);
+	#length must be bigger than 0x38, if not, cant get substring with data
+	if (length($data) > 0x38 && $data ne "xxx") {
+		my $err = unpack("C*", substr($data, 0x22, 1)) | (unpack("C*", substr($data, 0x23, 1)) << 8);
+		if ($err == 0) {
+			my $msg = "sp3 receiving temperature - data length: " . length($data);
+			Log3 $hash->{NAME}, 1, $hash->{NAME} . ": " . $msg;
+			my $enc_payload = substr($data, 0x38);
+			my $cipher =  Broadlink_getCipher($hash);         
+			my $decodedData = $cipher->decrypt($enc_payload);
+			my $temperature = 0.0;
+			if (unpack("C*", substr($decodedData, 4, 1)) =~ /^\d+?$/) { #isint
+				$temperature = (unpack("C*", substr($decodedData, 4, 1)) * 10 + unpack("C*", substr($decodedData, 5, 1))) / 10.0;
+			} else {
+				$temperature = (ord(unpack("C*", substr($decodedData, 4, 1))) * 10 + ord(unpack("C*", substr($decodedData, 5, 1)))) / 10.0;
+			}
+			readingsSingleUpdate ( $hash, "currentTemperature", $temperature, 1 );
+		} else {
+			my $msg = "Error receiving temperature";
+			Log3 $hash->{NAME}, 4, $hash->{NAME} . ": " . $msg;
+			readingsSingleUpdate ( $hash, "connectionErrorOn", "geTemperatureWithData", 1 );
+		}
+	} else {
+		my $msg = "no new temperature data found - data length: " . length($data);
+		Log3 $hash->{NAME}, 4, $hash->{NAME} . ": " . $msg;
+		readingsSingleUpdate ( $hash, "connectionErrorOn", "getTemperature", 1 );
+	}
+}
 
 sub Broadlink_sp3_getStatus(@) {
 	my ($hash) = @_;
@@ -296,7 +350,7 @@ sub Broadlink_sp3_getStatus(@) {
 			readingsSingleUpdate ( $hash, "connectionErrorOn", "geStatusWithData", 1 );
 		}
 	} else {
-		my $msg = "no new command data found - data length: " . length($data);
+		my $msg = "no new status data found - data length: " . length($data);
 		Log3 $hash->{NAME}, 4, $hash->{NAME} . ": " . $msg;
 		$hash->{STATE} = "unknown";
 		readingsSingleUpdate ( $hash, "connectionErrorOn", "geStatus", 1 );
@@ -330,6 +384,42 @@ sub Broadlink_sp3_setPower(@) {
 	}
 }
 
+sub Broadlink_sp3s_getEnergy(@) {
+	my ($hash) = @_;
+	my @broadlink_payload = ((0) x  16);
+	$broadlink_payload[0] = 8;
+	$broadlink_payload[2] = 254;
+	$broadlink_payload[3] = 1;
+	$broadlink_payload[4] = 5;
+	$broadlink_payload[5] = 1;
+	$broadlink_payload[9] = 45;
+	#my @broadlink_payload = pack('C*', 8, 0, 254, 1, 5, 1, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0);
+	
+	my $msg = "sp3_energy request";
+	Log3 $hash->{NAME}, 5, $hash->{NAME} . ": " . $msg;
+	my $data = Broadlink_send_packet($hash, 0x6a, @broadlink_payload);
+	#length must be bigger than 0x38, if not, cant get substring with data
+	if (length($data) > 0x38 && $data ne "xxx") {
+		my $err = unpack("C*", substr($data, 0x22, 1)) | (unpack("C*", substr($data, 0x23, 1)) << 8);
+		if ($err == 0) {
+			my $msg = "sp3 receiving energy - data length: " . length($data);
+			Log3 $hash->{NAME}, 1, $hash->{NAME} . ": " . $msg;
+			my $enc_payload = substr($data, 0x38);
+			my $cipher =  Broadlink_getCipher($hash);         
+			my $decodedData = $cipher->decrypt($enc_payload);
+			readingsSingleUpdate ( $hash, "currentPowerComsuption", sprintf("%.2f", (sprintf("%X", unpack("C*", substr($decodedData, 7, 1)) * 256 + unpack("C*", substr($decodedData, 6, 1))) + sprintf("%.2f", sprintf("%X", unpack("C*", substr($decodedData, 5, 1))) / 100.0))), 1 );
+		} else {
+			my $msg = "Error receiving energy";
+			Log3 $hash->{NAME}, 4, $hash->{NAME} . ": " . $msg;
+			readingsSingleUpdate ( $hash, "connectionErrorOn", "geEnergyWithData", 1 );
+		}
+	} else {
+		my $msg = "no new ernergy data found - data length: " . length($data);
+		Log3 $hash->{NAME}, 4, $hash->{NAME} . ": " . $msg;
+		readingsSingleUpdate ( $hash, "connectionErrorOn", "getEnergy", 1 );
+	}
+}
+
 sub Broadlink_enterLearning(@) {
 	my ($hash, $cmdname) = @_;
 	my @broadlink_payload = ((0) x 16);
@@ -603,6 +693,7 @@ sub Broadlink_Load(@) {
 <h3>Broadlink</h3>
 <ul>
     <i>Broadlink</i> implements a connection to Broadlink devices - currently tested with Broadlink RM Pro, which is able to send IR and 433MHz commands. It is also able to record this commands.
+	It can also control <i>rmmini</i> devices and sp3 or sp3s plugs.
 	<br>
 	It requires AES encryption please install on Windows:<br>
 	<code>ppm install Crypt-CBC</code><br>
@@ -615,7 +706,7 @@ sub Broadlink_Load(@) {
     <a name="Broadlinkdefine"></a>
     <b>Define</b>
     <ul>
-        <code>define &lt;name&gt; Broadlink &lt;ip/host&gt; &lt;mac&gt; &lt;type=rmpro or sp3&gt;</code>
+        <code>define &lt;name&gt; Broadlink &lt;ip/host&gt; &lt;mac&gt; &lt;type=rmpro or rmmini or sp3 or sp3s&gt;</code>
         <br><br>
         Example: <code>define broadlinkWZ Broadlink 10.23.11.85 34:EA:34:F4:77:7B rmpro</code>
         <br><br>
@@ -645,6 +736,31 @@ sub Broadlink_Load(@) {
         <br><br>
 		Renames a recored command.
         </li>
+		<li><code>set &lt;name&gt; getTemperature</code>
+        <br><br>
+		Get the device current enviroment Temperature
+        </li>
+    </ul>
+	<b>Set for rmmini</b><br>
+    <ul>
+        <li><code>set &lt;name&gt; &lt;commandSend&gt; &lt;command name&gt;</code>
+        <br><br>
+		Send a previous recorded command.
+        </li>
+		<li><code>set &lt;name&gt; recordNewCommand &lt;command name&gt;</code>
+        <br><br>
+		Records a new command. You have to specify a commandname
+        </li>
+		<li>
+		<code>set &lt;name&gt; remove &lt;command name&gt;</code>
+        <br><br>
+		Removes a recored command.
+        </li>
+		<li>
+		<code>set &lt;name&gt; rename &lt;old command name&gt; &lt;new command name&gt;</code>
+        <br><br>
+		Renames a recored command.
+        </li>
     </ul>
     <br>
 	    <b>Set for sp3</b><br>
@@ -666,6 +782,29 @@ sub Broadlink_Load(@) {
 		Get the device on/off status
         </li>
     </ul>
+	    <b>Set for sp3s</b><br>
+    <ul>
+        <li><code>set &lt;name&gt; on</code>
+        <br><br>
+		Set the device on
+        </li>
+		<li><code>set &lt;name&gt; off</code>
+        <br><br>
+		Set the device off
+        </li>
+		<li><code>set &lt;name&gt; toggle</code>
+        <br><br>
+		Toggle the device on and off
+        </li>
+		<li><code>set &lt;name&gt; getStatus</code>
+        <br><br>
+		Get the device on/off status
+        </li>
+		<li><code>set &lt;name&gt; getEnergy</code>
+        <br><br>
+		Get the device current energy consumption
+        </li>
+    </ul>
     <br>
 	<a name="Broadlinkattr"></a>
     <b>Attributes for all Broadlink Devices</b><br>
@@ -685,6 +824,7 @@ sub Broadlink_Load(@) {
 <h3>Broadlink</h3>
 <ul>
     <i>Broadlink</i> implementiert die Verbindung zu Broadlink Geräten - aktuell mit Broadlink RM Pro, welcher sowohl Infrarot als auch 433MHz aufnehmen und anschließend versenden kann.
+	Zusätzlich werden RMMinis und die Wlan Steckdosen SP3 und SP3S unterstützt
 	<br>
 	Das Modul benötigt AES-Verschlüsslung.<br>
 	In Windows installiert man die Untersützung mit:<br>
@@ -698,7 +838,7 @@ sub Broadlink_Load(@) {
     <a name="Broadlinkdefine"></a>
     <b>Define</b>
     <ul>
-        <code>define &lt;name&gt; Broadlink &lt;ip/host&gt; &lt;mac&gt; &lt;type=rmpro or sp3&gt;</code>
+        <code>define &lt;name&gt; Broadlink &lt;ip/host&gt; &lt;mac&gt; &lt;type=rmpro or rmmini or sp3 or sp3s&gt;</code>
         <br><br>
         Beispiel: <code>define broadlinkWZ Broadlink 10.23.11.85 34:EA:34:F4:77:7B rmpro</code>
         <br><br>
@@ -728,6 +868,32 @@ sub Broadlink_Load(@) {
         <br><br>
 		Benennt einen vorher aufgezeichneten Befehl um.
         </li>
+		<li><code>set &lt;name&gt; getTemperature</code>
+        <br><br>
+		Ermittelt die aktuelle Temperatur die am Gerät gemessen wird.
+        </li>
+    </ul>
+	<br>
+	<b>Set f&uuml;r rmmini</b><br>
+    <ul>
+        <li><code>set &lt;name&gt; &lt;commandSend&gt; &lt;command name&gt;</code>
+        <br><br>
+		Sendet ein vorher aufgenommenen Befehl
+        </li>
+		<li><code>set &lt;name&gt; recordNewCommand &lt;command name&gt;</code>
+        <br><br>
+		Nimmt ein neuen Befehl auf. Man muss einen Befehlnamen angeben.
+        </li>
+		<li>
+		<code>set &lt;name&gt; remove &lt;command name&gt;</code>
+        <br><br>
+		Löscht einen vorher aufgezeichneten Befehl.
+        </li>
+		<li>
+		<code>set &lt;name&gt; rename &lt;old command name&gt; &lt;new command name&gt;</code>
+        <br><br>
+		Benennt einen vorher aufgezeichneten Befehl um.
+        </li>
     </ul>
     <br>
 	    <b>Set f&uuml;r sp3</b><br>
@@ -750,6 +916,30 @@ sub Broadlink_Load(@) {
         </li>
     </ul>
     <br>
+	    <b>Set f&uuml;r sp3s</b><br>
+    <ul>
+        <li><code>set &lt;name&gt; on</code>
+        <br><br>
+		Schaltet das Gerät an.
+        </li>
+		<li><code>set &lt;name&gt; off</code>
+        <br><br>
+		Schaltet das Gerät aus.
+        </li>
+		<li><code>set &lt;name&gt; toggle</code>
+        <br><br>
+		Schaltet das Gerät entweder ein oder aus.
+        </li>
+		<li><code>set &lt;name&gt; getStatus</code>
+        <br><br>
+		Ermittelt den aktuellen Status des Gerätes.
+        </li>
+		<li><code>set &lt;name&gt; getEnergy</code>
+        <br><br>
+		Ermittelt den aktuellen Stromverbrauch des angeschlossenen Gerätes.
+        </li>
+    </ul>
+    <br>
 	<a name="Broadlinkattr"></a>
     <b>Attribute f&uuml;r alle Broadlink Gräte</b><br>
     <ul>

File diff suppressed because it is too large
+ 507 - 103
fhem/core/FHEM/38_netatmo.pm


+ 2 - 2
fhem/core/FHEM/39_alexa.pm

@@ -1,5 +1,5 @@
 
-# $Id: 39_alexa.pm 15047 2017-09-10 18:05:05Z justme1968 $
+# $Id: 39_alexa.pm 16299 2018-03-01 08:06:55Z justme1968 $
 
 package main;
 
@@ -714,7 +714,7 @@ alexa_Attr($$$)
   <b>Set</b>
   <ul>
     <li>reload [name]<br>
-      Reloads the device <it>name</it> or all devices in alexa-fhem. Subsequently you have to start a device discovery
+      Reloads the device <code>name</code> or all devices in alexa-fhem. Subsequently you have to start a device discovery
       for the home automation skill in the amazon alexa app.</li>
   </ul>
 

+ 8 - 3
fhem/core/FHEM/42_SYSMON.pm

@@ -27,7 +27,7 @@
 #
 ################################################################
 
-# $Id: 42_SYSMON.pm 15378 2017-11-01 20:36:57Z hexenmeister $
+# $Id: 42_SYSMON.pm 15910 2018-01-16 23:07:56Z hexenmeister $
 
 package main;
 
@@ -2263,7 +2263,11 @@ sub SYSMON_getRamAndSwap($$) {
   if($hash->{helper}->{excludes}{'ramswap'}) {return $map;}
 
   #my @speicher = qx(free -m);
-  my @speicher = SYSMON_execute($hash, "LANG=en free");
+  #my @speicher = SYSMON_execute($hash, "LANG=en free");
+  my $free_version = SYSMON_execute($hash, 'free -V');
+  $free_version =~ s/\D//g;
+  my @speicher = SYSMON_execute($hash, 'LANG=en ' . ($free_version > 339 ? 'free -w' : 'free'));
+
 
   if(!@speicher) {
     return $map;
@@ -2312,7 +2316,8 @@ sub SYSMON_getRamAndSwap($$) {
     }
     #$used_clean = $used - $buffers - $cached;
     #$ram = sprintf("Total: %.2f MB, Used: %.2f MB, %.2f %%, Free: %.2f MB", $total, $used_clean, ($used_clean / $total * 100), ($free + $buffers + $cached));
-    if ($total > 2048) {
+    #if ($total > 2048) {
+    if ($free_version > 339) {
        $used_clean = $used;
        $ram = sprintf("Total: %.2f MB, Used: %.2f MB, %.2f %%, Free: %.2f MB", $total, $used_clean, ($used_clean / $total * 100), ($free));
      } else {

+ 6 - 6
fhem/core/FHEM/44_S7_ARead.pm

@@ -1,4 +1,4 @@
-# $Id: 44_S7_ARead.pm 14965 2017-08-27 05:25:39Z charlie71 $
+# $Id: 44_S7_ARead.pm 15539 2017-12-01 21:52:13Z charlie71 $
 ##############################################
 package main;
 
@@ -306,7 +306,7 @@ sub S7_ARead_Parse($$) {
 				}
 				else {
 					Log3 $name, 3,
-					  "$name S7_ARead: Parse unknown type : ("
+					  "$n S7_ARead: Parse unknown type : ("
 					  . $h->{DATATYPE} . ")";
 				}
 
@@ -333,7 +333,7 @@ sub S7_ARead_Parse($$) {
 				my @a;
 				if($attreocr) {
 					@a = split(/,/,$attreocr);
-					$hash->{".attreocr"} = \@a;
+					$h->{".attreocr"} = \@a;
 				}
 				# determine whether the reading is listed in any of the attributes
 				my @eocrv;
@@ -350,13 +350,13 @@ sub S7_ARead_Parse($$) {
 
 				  if($myI =~ m/([\d\.\-eE]+)/ && looks_like_number($1)) { #41083, #62190
 					my $mv = $1;
-					my $last_value = $hash->{".attreocr-threshold$reading"};
+					my $last_value = $h->{".attreocr-threshold$reading"};
 					if( !defined($last_value) ) {
-					  $h->{".attreocr-threshold$reading"} = $mv;
+					  # $h->{".attreocr-threshold$reading"} = $mv;
 					} elsif( abs($mv - $last_value) < $threshold ) {
 					  $eocr = 0;
 					} else {
-					  $h->{".attreocr-threshold$reading"} = $mv;
+					  # $h->{".attreocr-threshold$reading"} = $mv;
 					}
 				  }
 				}

+ 5 - 5
fhem/core/FHEM/44_S7_AWrite.pm

@@ -1,4 +1,4 @@
-# $Id: 44_S7_AWrite.pm 14965 2017-08-27 05:25:39Z charlie71 $
+# $Id: 44_S7_AWrite.pm 15539 2017-12-01 21:52:13Z charlie71 $
 ##############################################
 package main;
 
@@ -475,7 +475,7 @@ sub S7_AWrite_Parse($$) {
 				my @a;
 				if($attreocr) {
 					@a = split(/,/,$attreocr);
-					$hash->{".attreocr"} = \@a;
+					$h->{".attreocr"} = \@a;
 				}
 				# determine whether the reading is listed in any of the attributes
 				my @eocrv;
@@ -492,13 +492,13 @@ sub S7_AWrite_Parse($$) {
 
 				  if($myI =~ m/([\d\.\-eE]+)/ && looks_like_number($1)) { #41083, #62190
 					my $mv = $1;
-					my $last_value = $hash->{".attreocr-threshold$reading"};
+					my $last_value = $h->{".attreocr-threshold$reading"};
 					if( !defined($last_value) ) {
-					  $h->{".attreocr-threshold$reading"} = $mv;
+					 # $h->{".attreocr-threshold$reading"} = $mv;
 					} elsif( abs($mv - $last_value) < $threshold ) {
 					  $eocr = 0;
 					} else {
-					  $h->{".attreocr-threshold$reading"} = $mv;
+					 # $h->{".attreocr-threshold$reading"} = $mv;
 					}
 				  }
 				}

+ 5 - 5
fhem/core/FHEM/44_S7_DRead.pm

@@ -1,4 +1,4 @@
-# $Id: 44_S7_DRead.pm 14965 2017-08-27 05:25:39Z charlie71 $
+# $Id: 44_S7_DRead.pm 15539 2017-12-01 21:52:13Z charlie71 $
 ##############################################
 package main;
 
@@ -402,7 +402,7 @@ sub S7_DRead_Parse($$) {
 				my @a;
 				if($attreocr) {
 					@a = split(/,/,$attreocr);
-					$hash->{".attreocr"} = \@a;
+					$h->{".attreocr"} = \@a;
 				}
 				# determine whether the reading is listed in any of the attributes
 				my @eocrv;
@@ -419,13 +419,13 @@ sub S7_DRead_Parse($$) {
 
 				  if($valueText =~ m/([\d\.\-eE]+)/ && looks_like_number($1)) { #41083, #62190
 					my $mv = $1;
-					my $last_value = $hash->{".attreocr-threshold$reading"};
+					my $last_value = $h->{".attreocr-threshold$reading"};
 					if( !defined($last_value) ) {
-					  $h->{".attreocr-threshold$reading"} = $mv;
+					  # $h->{".attreocr-threshold$reading"} = $mv;
 					} elsif( abs($mv - $last_value) < $threshold ) {
 					  $eocr = 0;
 					} else {
-					  $h->{".attreocr-threshold$reading"} = $mv;
+					  # $h->{".attreocr-threshold$reading"} = $mv;
 					}
 				  }
 				}

+ 5 - 5
fhem/core/FHEM/44_S7_DWrite.pm

@@ -1,4 +1,4 @@
-# $Id: 44_S7_DWrite.pm 14965 2017-08-27 05:25:39Z charlie71 $
+# $Id: 44_S7_DWrite.pm 15539 2017-12-01 21:52:13Z charlie71 $
 ##############################################
 package main;
 
@@ -478,7 +478,7 @@ sub S7_DWrite_setABit($$) {
 					my @a;
 					if($attreocr) {
 						@a = split(/,/,$attreocr);
-						$hash->{".attreocr"} = \@a;
+						$h->{".attreocr"} = \@a;
 					}
 					# determine whether the reading is listed in any of the attributes
 					my @eocrv;
@@ -495,13 +495,13 @@ sub S7_DWrite_setABit($$) {
 
 					  if($valueText =~ m/([\d\.\-eE]+)/ && looks_like_number($1)) { #41083, #62190
 						my $mv = $1;
-						my $last_value = $hash->{".attreocr-threshold$reading"};
+						my $last_value = $h->{".attreocr-threshold$reading"};
 						if( !defined($last_value) ) {
-						  $h->{".attreocr-threshold$reading"} = $mv;
+						  # $h->{".attreocr-threshold$reading"} = $mv;
 						} elsif( abs($mv - $last_value) < $threshold ) {
 						  $eocr = 0;
 						} else {
-						  $h->{".attreocr-threshold$reading"} = $mv;
+						  # $h->{".attreocr-threshold$reading"} = $mv;
 						}
 					  }
 					}

+ 7 - 6
fhem/core/FHEM/44_S7_S5Client.pm

@@ -1,4 +1,4 @@
-# $Id: 44_S7_S5Client.pm 14939 2017-08-21 07:50:47Z charlie71 $
+# $Id: 44_S7_S5Client.pm 15518 2017-11-28 21:17:47Z charlie71 $
 ##############################################
 
 use strict;
@@ -477,14 +477,15 @@ sub S5ConnectPLCAS511($$) {
 	my $b1 = "";
 	my $ttyPort;
 
-	if ($^O=~/Win/) {
-		eval ("use Win32::SerialPort;");
+	if($^O =~ m/Win/) {
+		require Win32::SerialPort;
+		#eval ("use Win32::SerialPort;");
 		$self->{serial} = new Win32::SerialPort ($portName);
 	}else{
-		eval ("use Device::SerialPort;");
+		#eval ("use Device::SerialPort;");
+		require Device::SerialPort;
 		$self->{serial} = new Device::SerialPort ($portName);
-	}
-
+    }
 
 	main::Log3( undef, 3, "Can't open serial port $portName" )
 	  unless ( $self->{serial} );

+ 2 - 2
fhem/core/FHEM/44_S7_S7Client.pm

@@ -1,4 +1,4 @@
-# $Id: 44_S7_S7Client.pm 12776 2016-12-14 18:09:08Z charlie71born $
+# $Id: 44_S7_S7Client.pm 15511 2017-11-27 21:13:16Z charlie71 $
 ##############################################
 
 use strict;
@@ -334,7 +334,7 @@ sub new {
 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 );
 	$self->{PDU}->{DATA} = "";
-
+	$self->{TCPClient} = undef;
 	return bless $self, $class;
 }
 

File diff suppressed because it is too large
+ 72 - 51
fhem/core/FHEM/46_SmartPi.pm


+ 10 - 4
fhem/core/FHEM/46_TeslaPowerwall2AC.pm

@@ -21,7 +21,7 @@
 #  GNU General Public License for more details.
 #
 #
-# $Id: 46_TeslaPowerwall2AC.pm 15311 2017-10-23 19:53:19Z CoolTux $
+# $Id: 46_TeslaPowerwall2AC.pm 15976 2018-01-24 04:58:51Z CoolTux $
 #
 ###############################################################################
 ##
@@ -66,7 +66,7 @@ use HttpUtils;
 eval "use JSON;1" or $missingModul .= "JSON ";
 
 
-my $version = "0.2.3";
+my $version = "0.4.0";
 
 
 
@@ -231,6 +231,7 @@ sub TeslaPowerwall2AC_Notify($$) {
 
     TeslaPowerwall2AC_Timer_GetData($hash) if( grep /^INITIALIZED$/,@{$events}
                                                 or grep /^DELETEATTR.$name.disable$/,@{$events}
+                                                or grep /^DELETEATTR.$name.interval$/,@{$events}
                                                 or (grep /^DEFINED.$name$/,@{$events} and $init_done) );
     return;
 }
@@ -489,15 +490,20 @@ sub TeslaPowerwall2AC_ReadingsProcessing_Powerwalls($$) {
     
     
     if( ref($decode_json->{powerwalls}) eq "ARRAY" and scalar(@{$decode_json->{powerwalls}}) > 0 ) {
-    
+        my $i = 0;
+        
         foreach my $powerwall (@{$decode_json->{powerwalls}}) {
             if( ref($powerwall) eq "HASH" ) {
             
                 while( my ($r,$v) = each %{$powerwall} ) {
-                    $readings{$r}   = $v;
+                    $readings{'wall_'.$i.'_'.$r}   = $v;
                 }
+                
+                $i++;
             }
         }
+        
+        $readings{'numberOfWalls'}   = $i;
 
     } else {
         $readings{'error'} = 'aggregates response is not a Array';

+ 67 - 65
fhem/core/FHEM/47_OBIS.pm

@@ -5,7 +5,11 @@
 # Thanks to matzefizi for letting me merge this with 70_SMLUSB.pm and for testing
 # Tanks to immi for testing and supporting help and tips
 # 
-# $Id: 47_OBIS.pm 14235 2017-05-09 19:24:14Z Icinger $
+# $Id: 47_OBIS.pm 16167 2018-02-13 20:36:00Z Icinger $
+
+# Removed: PERL WARNING: Hexadecimal number > 0xffffffff non-portable at
+# Added:   attr ExtChannels -> set History-Readings
+
 
 package main;
 use strict;
@@ -13,6 +17,8 @@ use warnings;
 use Time::HiRes qw(gettimeofday usleep);
 use Scalar::Util qw(looks_like_number);
 use POSIX qw{strftime};
+no warnings 'portable';  # Support for 64-bit ints required
+#use Math::BigInt ':constant';
 
 my %OBIS_channels = ( "21"	=>"power_L1",
 	                  "41"	=>"power_L2",
@@ -46,7 +52,7 @@ my %OBIS_codes = (	"Serial" 		=> qr{^0-0:96.1.255(?:.\d+)?\((.*?)\).*},
 					"Channel_sum" 	=> qr{^(?:1.0.)?(\d+).1.7(?:.0|.255)?(?:\(.*?\))?\((<|>)?([-+]?\d+\.?\d*)\*?(.*)\).*},
 					"Channels"		=> qr{^(?:\d.0.)?(\d+).7\.\d+(?:.0|.255)?(?:\(.*?\))?\((<|>)?([-+]?\d+\.?\d*)\*?(.*)\).*},
 					"Channels2"		=> qr{^(?:0.1.)?(\d+).2\.\d+(?:.0|.255)?(?:\(.*?\))?\((<|>)?(-?\d+\.?\d*)\*?(.*)\).*},
-					"Counter"		=> qr{^(?:1.\d.)?(\d).(8)\.(\d)(?:.\d+)?(?:\(.*?\))?\((<|>)?(-?\d+\.?\d*)\*?(.*)\).*},
+					"Counter"		=> qr{^(?:1.\d.)?(\d).(8)\.(\d).(\d+)?(?:\(.*?\))?\((<|>)?(-?\d+\.?\d*)\*?(.*)\).*},    #^(?:1.\d.)?(\d).(8)\.(\d)(?:.\d+)?(?:\(.*?\))?\((<|>)?(-?\d+\.?\d*)\*?(.*)\).*
 					"ManufID"		=> qr{^129-129:199\.130\.3(?:.\d+)?\((.*?)\).*},
 					"PublicKey"		=> qr{^129-129:199\.130\.5(?:.\d+)?\((.*?)\).*},
 				);
@@ -76,11 +82,10 @@ sub OBIS_Initialize($)
   $hash->{ReadyFn}  = "OBIS_Ready";
   $hash->{DefFn}   = "OBIS_Define";
   $hash->{ParseFn}   = "OBIS_Parse";
-#  $hash->{SetFn} = "OBIS_Set";
     $hash->{GetFn} = "OBIS_Get";
   $hash->{UndefFn} = "OBIS_Undef";
   $hash->{AttrFn}	= "OBIS_Attr";
-  $hash->{AttrList}= "do_not_notify:1,0 interval offset_feed offset_energy IODev channels directions alignTime pollingMode:on,off unitReadings:on,off ignoreUnknown:on,off valueBracket:first,second,both ".
+  $hash->{AttrList}= "do_not_notify:1,0 interval offset_feed offset_energy IODev channels directions alignTime pollingMode:on,off extChannels:on,off unitReadings:on,off ignoreUnknown:on,off valueBracket:first,second,both createPreValues:on,off ".
   					  $readingFnAttributes;
 }
 
@@ -144,6 +149,7 @@ sub OBIS_Define($$)
     "VSM102"	=> 	["/?!".chr(13).chr(10),    600,    chr(6)."0".$hash->{helper}{SPEED}."0".chr(13).chr(10)],
     "E110"		=>  ["/?!".chr(13).chr(10),    600,    chr(6)."0".$hash->{helper}{SPEED}."0".chr(13).chr(10)],
     "E350USB"	=>  ["/?!".chr(13).chr(10),    600,    chr(6)."0".$hash->{helper}{SPEED}."0".chr(13).chr(10)],
+    "AS1440"	=> 	["/2!".chr(13).chr(10),    600,    chr(6)."0".$hash->{helper}{SPEED}."0".chr(13).chr(10)]
     );
     if (!$devs{$type}) {return 'unknown meterType. Must be one of <nothing>, SML, Standard, VSM102, E110'};
     $devs{$type}[1] = $hash->{helper}{DEVICES}[1] // $devs{$type}[1];
@@ -316,12 +322,9 @@ sub OBIS_trySMLdecode($$)
 			my $OBISid=$msg=~m/7701([0-9A-F]*?)01/g;
 			Log3 $hash,5,"OBIS: Full message-> $msg";
 			(undef,undef,$OBISid,undef)=OBIS_decodeTL($1);
-#			Log3 $hash,5,"OBIS: ObisID-> $1";
 			while ($msg =~ m/(7707)([0-9A-F]*)/g) {
-#	    		my $telegramm = $&;
       			my @list=$&=~/(7707)([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]*)/g;
       			Log3 $hash, 5,"OBIS: Telegram=$msg";
-#      			Log 3,Dumper(@list);
       			if (!@list) {Log3 $hash,3,"OBIS - Empty datagram: .$msg"};
 	    		my $line=hex($list[1])."-".hex($list[2]).":".hex($list[3]).".".hex($list[4]).".".hex($list[5])."*255(";
 	    		if ($line eq '255-255:255.255.255*255(') {
@@ -329,31 +332,22 @@ sub OBIS_trySMLdecode($$)
 	    			$msg=$1;
 	    		} else
 	    		{
-#		    		Log3 $hash,5,"Line: $line";
-#		    		Log3 $hash,5,"Before decoding: $list[7]";
 					my ($status,$statusL,$statusT,$valTime,$valTimeL,$valTimeT,$unit,$unitL,$unitT,$scaler,$scalerL,$scalerT,$data,$dataL,$dataT,$other);		   
 		    		($statusL,$statusT,$status,$other)=OBIS_decodeTL($list[7]);
-#		    		Log3 $hash,5,"After status: $other";
 		    		($valTimeL,$valTimeT,$valTime,$other)=OBIS_decodeTL($other);
-#		    		Log3 $hash,5,"After Time: $other";
 		    		($unitL,$unitT,$unit,$other)=OBIS_decodeTL($other);
-#		    		Log3 $hash,5,"After Unit: $other";
 		    		($scalerL,$scalerT,$scaler,$other)=OBIS_decodeTL($other);
-#		    		Log3 $hash,5,"After Scaler: $other";
 		    		($dataL,$dataT,$data,$msg)=OBIS_decodeTL($other);
-#		    		Log3 $hash,5,"After Data: $msg";
-		    		
-	# Type String
-					my $line2=""; 
+		    		my $line2=""; 
 		    		if ($dataT ==0 ) {				
-	$line2=$data;
+						$line2=$data;
 		    			if($line=~$SML_specialities{"HEX4"}[0]) {
-	#	    				
 	#	    				$line2=$SML_specialities{"HEX4"}[1]->($data)
 		    			} elsif($line=~$SML_specialities{"HEX2"}[0]) {
 	#    					$line2=$SML_specialities{"HEX2"}[1]->($data)
 		    			} else {
 		    				$data=~s/([A-F0-9]{2})/chr(hex($1))/eg;
+		    				$data=~s/[^!-~\s]//g;
 		    				$line2="$data";
 	    				}
 	    				
@@ -381,7 +375,6 @@ sub OBIS_trySMLdecode($$)
 						if ($dataT & 0b00100000 || $val>=0) {
 							$val=hex($data);
 						}
-	#					$line2.=($val*$scaler).($unit eq "" ? "" : "*$unit")  if($dataT ==80);
 						$line2.=($val*$scaler).($unit eq "" ? "" : "*$unit"); # if($dataT ==96);					
 					} elsif ($dataT & 0b01000000) {		# Type Boolean - no Idea, where this is used
 						$line2=OBIS_hex2int($data);			# 0=false, everything else is true
@@ -396,7 +389,6 @@ sub OBIS_trySMLdecode($$)
 					}
 					$initstr.="$line2\\" if ($line=~$SML_specialities{"INFO"}[0]);
 					$newMsg.=$line.$line2.")\r\n";
-	#				Log 3,"$line$line2)";
 	###### TypeLength-Test ends here
 				}
 			}
@@ -405,7 +397,6 @@ sub OBIS_trySMLdecode($$)
 			$newMsg.="!".chr(13).chr(10);
 			Log3 $hash,4,"MSG IS: \r\n$newMsg";
 		} else {
-#			Log 3,"Illegal CRC";
 			$hash->{CRC_Errors}+=1;
 		}
 	}
@@ -438,7 +429,11 @@ sub OBIS_Parse($$)
 	my $remainingSML;
 	($buffer,$remainingSML) = OBIS_trySMLdecode($hash,$buffer) if ($hash->{MeterType}=~/SML|Ext|Unknown/);
 	my $type= $hash->{MeterType};
-	my $name = $hash->{NAME};  
+	my $name = $hash->{NAME};
+			$buf='/'.$buf;  
+	    	$buf =~ /!((?!\/).*)$/gmsi;
+			$buf=$1;
+	
 	if(index($buffer,chr(13).chr(10)) ne -1){
 		readingsBeginUpdate($hash);
 		
@@ -455,6 +450,7 @@ sub OBIS_Parse($$)
 						$channel=~s/:/\./;
 						$channel=~s/-/\./;
 						$channel=~s/\*/\./;
+#						Log 3,"Channel would be: $channel";
 					}
 					if ($hash->{MeterType} eq "Unknown") {$hash->{MeterType}="Standard"}
 #					if($rmsg=~/^([23456789]+)-.*/) {
@@ -462,7 +458,7 @@ sub OBIS_Parse($$)
 #					}
 			
 			# End of Message
-					if ($rmsg=~/!.*/) {
+					if ($rmsg=~/^!.*/) {
 						$hash->{helper}{EoM}+=1 if ($hash->{helper}{DEVICES}[1]>0);
 					}
 			#Version
@@ -503,13 +499,14 @@ sub OBIS_Parse($$)
 											$rmsg =~ $OBIS_codes{$code};
 											my $L=$hash->{helper}{Channels}{$channel} //$hash->{helper}{Channels}{$1.".".$2} // $OBIS_channels{$1.".".$2} // $channel;
 											my $chan=$3+0 > 0 ? "_Ch$3" : "";
+											if (AttrVal($name,"extChannels","off") eq "on") {$chan.=".$4" if $4;}
     										if (AttrVal($name,"ignoreUnknown","off") eq "off" || $L ne $channel) {
 												if($1==1) {
-    								Log3($hash,4,"Set ".$L.$chan." to ".((looks_like_number($3) ? $5+0 : $5) +AttrVal($name,"offset_energy",0)));
+    												Log3($hash,4,"Set ".$L.$chan." to ".((looks_like_number($3) ? $6+0 : $5) +AttrVal($name,"offset_energy",0)));
 
-													readingsBulkUpdate($hash, $L.$chan  ,(looks_like_number($3) ? $5+0 : $5) +AttrVal($name,"offset_energy",0).(AttrVal($name,"unitReadings","off") eq "off"?"":" $6")); 
+													readingsBulkUpdate($hash, $L.$chan  ,(looks_like_number($3) ? $6+0 : $5) +AttrVal($name,"offset_energy",0).(AttrVal($name,"unitReadings","off") eq "off"?"":" $7")); 
 												} elsif ($1==2) {
-													readingsBulkUpdate($hash, $L.$chan  ,(looks_like_number($3) ? $5+0 : $5) +AttrVal($name,"offset_feed",0).(AttrVal($name,"unitReadings","off") eq "off"?"":" $6")); 				
+													readingsBulkUpdate($hash, $L.$chan  ,(looks_like_number($3) ? $6+0 : $5) +AttrVal($name,"offset_feed",0).(AttrVal($name,"unitReadings","off") eq "off"?"":" $7")); 				
 												}
 							  					readingsBulkUpdate($hash, "dir_$L",$hash->{helper}{directions}{$4} // $dir{$4}) if (length $4);
     										}											
@@ -542,6 +539,7 @@ sub OBIS_Parse($$)
 					     		}
 				  			}
 				     		if ($found==0) {
+#				     			Log 3,"Found a Channel-Attr";
 				     			$rmsg=~/^((?:\d{1,3}-\d{1,3}:)?(?:\d{1,3}|[CF]).\d{1,3}(?:.\d{1,3})?(?:\*\d{1,3})?)(?:\((.*?)\))?\((.*?)\)/;
     							my $chan=$hash->{helper}{Channels}{$channel} //$hash->{helper}{Channels}{$1} //  $OBIS_channels{$1} //$channel;
     							my $chan1=$chan;
@@ -631,6 +629,7 @@ sub OBIS_Attr(@)
 				$hash->{helper}{Channels}=undef;
 			}
 		}
+		
 		if ($aName eq "directions") {
 	      $hash->{helper}{directions}=eval $aVal;
 			if ($@) {
@@ -651,7 +650,7 @@ sub OBIS_Attr(@)
 			}
 		}
 		if ($aName eq "alignTime") {
-			 if ($hash->{helper}{DEVICES}[1]>0) {
+			 if ($hash->{helper}{DEVICES}[1]>0 || !$init_done) {
 			 	if ($aVal=~/\d+/) {
 			  		RemoveInternalTimer($hash);
 				    $hash->{helper}{TRIGGERTIME}=gettimeofday();
@@ -660,7 +659,9 @@ sub OBIS_Attr(@)
 					InternalTimer($t, "GetUpdate", $hash, 0);
 			 	} else {return "OBIS ($name): attr alignTime must be a Value >0"}
 			 } else {
- 				return "OBIS ($name): attr alignTime is useless, if no interval is specified";
+			 	if ($init_done) {
+ 					return "OBIS ($name): attr alignTime is useless, if no interval is specified";
+			 	}
 			 }			
 		}
 		if ($aName eq "pollingMode")
@@ -809,16 +810,18 @@ sub OBIS_decodeTL($){
 
 <a name="OBIS"></a>
 <h3>OBIS</h3>
+<ul>
   This module is for SmartMeters, that report their data in OBIS-Standard. It dosen't matter, wether the data comes as PlainText or SML-encoded.
-  <br>
+  <br><br>
   <b>Define</b>
+  <ul>
     <code>define &lt;name&gt; OBIS device|none [MeterType] </code><br>
     <br>
       &lt;device&gt; specifies the serial port to communicate with the smartmeter.
       Normally on Linux the device will be named /dev/ttyUSBx, where x is a number.
       For example /dev/ttyUSB0. You may specify the baudrate used after the @ char.<br>
-      <br><br>
-      Optional:MeterType can be of
+      <br>
+      Optional: MeterType can be of
       <ul><li>VSM102 -&gt; Voltcraft VSM102</li>
       <li>E110 -&gt; Landis&&;Gyr E110</li>
       <li>E350USB -&gt; Landis&&;Gyr E350 USB-Version</li>
@@ -829,46 +832,45 @@ sub OBIS_decodeTL($){
     <code>define myPowerMeter OBIS /dev/ttyPlugwise@@9600,7,E,1 VSM102</code>
       <br>
     <br>
- 
+  </ul>
   <b>Attributes</b>
   <ul><li>
     <code>offset_feed <br>offset_energy</code><br>
       If your smartmeter is BEHIND the meter of your powersupplier, then you can hereby adjust
       the total-reading of your SM to that of your official one.
-      <br><br>
-      </li>
-      <li>
+      </li><li>
    <code>channels</code><br>
-      With this, you can rename the reported channels.
+      With this, you can rename the reported channels.<BR>e.g.: 
       <code>attr myOBIS channels {"1.0.96.5.5.255"=>"Status","1.0.0.0.0.255"=>"Info","16.7"=>"Verbrauch"}></code>
-      <br><br></li>
-      <li><code>directions</code><br>
+      </li><li>
+   <code>directions</code><br>
       Some Meters report feeding/comnsuming of power in a statusword.
-      If this is set, you get an extra reading dir_total_consumption which defaults to "in" and "out"
-      Here, you can change this text with:
+      If this is set, you get an extra reading dir_total_consumption which defaults to "in" and "out".<BR>
+      Here, you can change this text with, e.g.: 
       <code>attr myOBIS directions {">" => "pwr consuming", "<"=>"pwr feeding"}</code>
-      </li>
-      <li>
+      </li><li>
    <code>interval</code><br>
       The polling-interval in seconds. (Only useful in Polling-Mode)
-      </li>
-      <li>
+      </li><li>
    <code>alignTime</code><br>
       Aligns the intervals to a given time. Each interval is repeatedly calculated.
-      So if alignTime=00:00 and interval=600 aligns the interval to xx:00:00, xx:10:00, xx:20:00 etc....  
+      So if alignTime=00:00 and interval=600 aligns the interval to xx:00:00, xx:10:00, xx:20:00 etc....
+      </li><li>  
    <code>pollingMode</code><br>
       Changes from direct-read to polling-mode.
       Useful with meters, that send a continous datastream. 
       Reduces CPU-load.  
+      </li><li>
    <code>unitReadings</code><br>
-      Adds the units to the readings like w, wH, A etc.  
+      Adds the units to the readings like w, wH, A etc.
+      </li><li>  
    <code>valueBracket</code><br>
       Sets, weather to use the value from the first or the second bracket, if applicable.
       Standard is "second"
       </li>
       
   <br>
-</ul>
+</ul></ul>
 
 =end html
 
@@ -876,14 +878,16 @@ sub OBIS_decodeTL($){
 
 <a name="OBIS"></a>
 <h3>OBIS</h3>
+<ul>
   Modul für Smartmeter, die ihre Daten im OBIS-Standard senden. Hierbei ist es egal, ob die Daten als reiner Text oder aber SML-kodiert kommen.
-  <br>
+  <br><br>
   <b>Define</b>
+  <ul>
     <code>define &lt;name&gt; OBIS device|none [MeterType] </code><br>
     <br>
       &lt;device&gt; gibt den seriellen Port an.
       <br><br>
-      Optional:MeterType kann sein:
+      Optional: MeterType kann sein:
       <ul><li>VSM102 -&gt; Voltcraft VSM102</li>
       <li>E110 -&gt; Landis&&;Gyr E110</li>
       <li>E350USB -&gt; Landis&&;Gyr E350 USB-Version</li>
@@ -894,25 +898,21 @@ sub OBIS_decodeTL($){
     <code>define myPowerMeter OBIS /dev/ttyPlugwise@@9600,7,E,1 VSM102</code>
       <br>
     <br>
- 
+  </ul>
   <b>Attribute</b>
   <ul><li>
     <code>offset_feed <br>offset_energy</code><br>
       Wenn das Smartmeter hinter einem Zähler des EVU's sitzt, kann hiermit der Zähler des
       Smartmeters an den des EVU's angepasst werden.
-      <br><br>
-      </li>
-      <li>
+      </li><li>
    <code>channels</code><br>
-      Hiermit können die einzelnen Kanal-Readings mittels RegExes umbenannt werden.
-      Beispiel:
-      <code>attr myOBIS channels {"1.0.96.5.5.255"=>"Status","1.0.0.0.0.255"=>"Info","16.7"=>"Verbrauch"}></code>
-      <br><br>
-      </li>
-      <li><code>directions</code><br>
+      Hiermit können die einzelnen Kanal-Readings mittels RegExes umbenannt werden.<BR>
+      Beispiel: <code>attr myOBIS channels {"1.0.96.5.5.255"=>"Status","1.0.0.0.0.255"=>"Info","16.7"=>"Verbrauch"}></code>
+      </li><li>
+   <code>directions</code><br>
       Manche SmartMeter senden im Statusbyte die Stromrichtung.
-      In diesem Fall gibt es ein extra Reading "dir_total_consumption" welches standardmäßig "in" and "out" beinhaltet
-      Hiermit kann dieser Text geändert werden:
+      In diesem Fall gibt es ein extra Reading "dir_total_consumption" welches standardmäßig "in" and "out" beinhaltet<BR>
+      Hiermit kann dieser Text geändert werden, z.B.:
       <code>attr myOBIS directions {">" => "pwr consuming", "<"=>"pwr feeding"}</code>
       </li><li>
    <code>interval</code><br>
@@ -924,15 +924,17 @@ sub OBIS_decodeTL($){
    <code>pollingMode</code><br>
       Hiermit wird von Direktbenachrichtigung auf Polling umgestellt.
       Bei Smartmetern, welche von selbst im Sekundentakt senden,
-      kann das zu einer spürbaren Senkung der Prozessorleistung führen.  
+      kann das zu einer spürbaren Senkung der Prozessorleistung führen.
+      </li><li>  
    <code>unitReadings</code><br>
-      Hängt bei den Readings auch die Einheiten an, zB w, wH, A usw.  
+      Hängt bei den Readings auch die Einheiten an, zB w, wH, A usw.
+      </li><li>  
    <code>valueBracket</code><br>
       Legt fest, ob der Wert aus dem ersten oder zweiten Klammernpaar genommen wird. 
       Standard ist "second"
       </li>
   <br>
-</ul>
+</ul></ul>
 
 =end html_DE
 

File diff suppressed because it is too large
+ 277 - 277
fhem/core/FHEM/49_SSCam.pm


+ 84 - 51
fhem/core/FHEM/49_TBot_List.pm

@@ -26,7 +26,7 @@
 #
 # Discussed in FHEM Forum: https://forum.fhem.de/index.php/topic,67976.0.html
 #
-# $Id: 49_TBot_List.pm 14726 2017-07-16 13:34:42Z viegener $
+# $Id: 49_TBot_List.pm 16382 2018-03-11 13:20:55Z viegener $
 #
 ##############################################################################
 # 0.0 2017-01-15 Started
@@ -78,6 +78,15 @@
 #   confirm add unsolicited configurable as attribute confirmUnsolicited 
 # 0.6 2017-07-16   confirmDelete & confirmUnsolicited
 #   
+#   added list getter for simple text list with \n and empty string if no entries
+#   switched from fhem( calls to AnalyzeCommandChain
+#   added count getter for count of list entries
+#   FIX: Some log entries / issues with inline commands
+#   new attribute deleteOnly to have deleteonly lists / no changes or adds
+#   document deleteOnly
+# 0.7 2018-03-11   deleteonly lists / internal changes
+  
+#   
 ##############################################################################
 # TASKS 
 #   
@@ -147,6 +156,7 @@ sub TBot_List_Initialize($) {
           "handleUnsolicited:0,1 ".
           "confirmDelete:0,1 ".
           "confirmUnsolicited:0,1 ".
+          "deleteOnly:0,1 ".
           "allowedPeers:textField ".
           $readingFnAttributes;           
 }
@@ -234,17 +244,13 @@ sub TBot_List_Set($@)
 
   my $addArg = ($args[0] ? join(" ", @args ) : undef);
 
-  Log3 $name, 4, "TBot_List_Set $name: Processing TBot_List_Set( $cmd ) - args :".(defined($addArg)?$addArg:"<undef>").":";
-
   # check cmd / handle ?
   my $ret = TBot_List_CheckSetGet( $hash, $cmd, $hash->{setoptions} );
+  return $ret if ( $ret );
+  
+  Log3 $name, 4, "TBot_List_Set $name: Processing TBot_List_Set( $cmd ) - args :".(defined($addArg)?$addArg:"<undef>").":";
 
-  if ( $ret ) {
-
-    # This is wrong arg / ? --> just return without log
-    return $ret;
-
-  } elsif ($cmd eq 'start')  {
+  if ($cmd eq 'start')  {
     Log3 $name, 4, "TBot_List_Set $name: start of dialog requested ";
     $ret = "start requires a telegrambot and optionally a peer" if ( ( $numberOfArgs < 2 ) && ( $numberOfArgs > 3 ) );
     
@@ -265,7 +271,7 @@ sub TBot_List_Set($@)
         $tchat = ReadingsVal( $tbot, "msgChatId", undef );
         $tpeer = ReadingsVal( $tbot, "msgPeerId", "" );
       } else {
-        $tpeer = fhem( "get $tbot peerId ".$args[1] );
+        $tpeer = AnalyzeCommandChain( $hash, "get $tbot peerId ".$args[1] );
       }
       $ret = "No peer found or specified :$tbot: ".(( $numberOfArgs == 2 )?"":$args[1]) if ( ! $tpeer );
     }  
@@ -299,7 +305,7 @@ sub TBot_List_Set($@)
     }  
     
     if ( ! $ret ) {
-      $tpeer = fhem( "get $tbot peerId ".$args[1], 1 );
+      $tpeer = AnalyzeCommandChain( $hash, "get $tbot peerId ".$args[1], 1 );
       $ret = "No peer found or specified :$tbot: ".$args[1] if ( ! $tpeer );
     }  
   
@@ -331,18 +337,24 @@ sub TBot_List_Get($@)
   my $cmd = $args[0];
   my $arg = $args[1];
 
-  Log3 $name, 5, "TBot_List_Get $name: Processing TBot_List_Get( $cmd )";
-
   # check cmd / handle ?
   my $ret = TBot_List_CheckSetGet( $hash, $cmd, $hash->{getoptions} );
+  return $ret if ( $ret );
 
-  if ( $ret ) {
-    # This is wrong arg / ? --> just return without log
-    return $ret;
+  Log3 $name, 5, "TBot_List_Get $name: Processing TBot_List_Get( $cmd )";
 
-  } elsif($cmd eq "textList") {
+  if($cmd eq "textList") {
     $ret = TBot_List_getTextList($hash);
-  
+    
+  } elsif($cmd eq "list") {
+    my @list = TBot_List_getList( $hash );
+    $ret = "";
+    $ret = join("\n", @list ) if ( scalar( @list ) != 0 );
+    
+  } elsif($cmd eq "count") {
+    my @list = TBot_List_getList( $hash );
+    $ret = scalar( @list );
+    
   } elsif($cmd eq 'queryAnswer') {
     # parameters cmd - queryAnswer <tbot> <peer> <querydata> 
     if ( $numberOfArgs != 4 ) {
@@ -366,7 +378,7 @@ sub TBot_List_Get($@)
           $ret = "No telegramBot specified :$tbot:" if ( ! TBot_List_isTBot( $hash, $tbot ) );
         }
         if ( ! $ret ) {
-          $tpeer = fhem( "get $tbot peerId ".$args[2] );
+          $tpeer = AnalyzeCommandChain( $hash, "get $tbot peerId ".$args[2] );
           $ret = "No peer specified :$tbot: ".$args[2] if ( ! $tpeer );
         }
         
@@ -409,7 +421,7 @@ sub TBot_List_Attr(@) {
     if ( ($aName eq 'optionDouble') ) {
       $aVal = ($aVal eq "1")? "1": "0";
 
-    } elsif ( ($aName eq "confirmDelete" ) ||  ($aName eq "confirmUnsolicited" ) ) {
+    } elsif ( ($aName eq "confirmDelete" ) ||  ($aName eq "confirmUnsolicited" )  ||  ($aName eq "deleteOnly" )  ) {
       $aVal = ($aVal eq "1")? "1": "0";
 
     } elsif ($aName eq 'allowedPeers') {
@@ -723,13 +735,15 @@ sub TBot_List_handler($$$$;$)
 
   my $ret;
 
-  Log3 $name, 4, "JVLISTMGR_handler: $name - $tbot  peer :$peer:   cmd :$cmd:  ".(defined($arg)?"arg :$arg:":"");
+  Log3 $name, 4, "TBot_List_handler: $name - $tbot  peer :$peer:   cmd :$cmd:  ".(defined($arg)?"arg :$arg:":"");
 
   my $lname = TBot_List_getConfigListname($hash);
   my $msgId;
   my $chatId;
   my @list;
   
+  my $donly = AttrVal($name,'deleteOnly',0);
+  
   # in start case from group chat both ids will be given and need to be allowed
   ($peer, $chatId) = split( / /, $peer );
   
@@ -746,7 +760,7 @@ sub TBot_List_handler($$$$;$)
 
     @list = TBot_List_getList( $hash );
   }
-  Log3 $name, 4, "JVLISTMGR_handler: $name - after prefetch peer :$peer:  chatId :$chatId:   msgId :".($msgId?$msgId:"<undef>").": ";
+  Log3 $name, 4, "TBot_List_handler: $name - after prefetch peer :$peer:  chatId :$chatId:   msgId :".($msgId?$msgId:"<undef>").": ";
   
   #####################  
   if ( $ret ) {
@@ -774,7 +788,7 @@ sub TBot_List_handler($$$$;$)
 
     if ( defined($msgId ) ) {
       # show final list 
-      fhem( "set ".$tbot." queryEditInline $msgId ".'@'.$chatId." $inline $textmsg" );
+      AnalyzeCommandChain( $hash, "set ".$tbot." queryEditInline $msgId ".'@'.$chatId." $inline $textmsg" );
       TBot_List_setMsgId( $hash, $tbot, $chatId );
       TBot_List_setMsgId( $hash, $tbot, $peer, undef, "chat" );
     } else {
@@ -808,9 +822,15 @@ sub TBot_List_handler($$$$;$)
     }
     
     $inline .= ") " if ( $double == 2 );
-
-    $inline .= "(".TBot_List_inlinekey( $hash, "ok", "list_ok" )."|".TBot_List_inlinekey( $hash, "ändern", "list_menu" )."|".
+    
+    $inline .= "(".TBot_List_inlinekey( $hash, "ok", "list_ok" );
+    
+    if ( $donly ) {
+      $inline .=  "|".TBot_List_inlinekey( $hash, "Leeren", "list_askclr" ).")";
+    } else {
+      $inline .=  "|".TBot_List_inlinekey( $hash, "ändern", "list_menu" )."|".
                   TBot_List_inlinekey( $hash, "hinzu", "list_askadd" ).")";
+    }
     
     my $textmsg = "Liste ".$lname;
     $textmsg .= " ist leer " if ( scalar(@list) == 0 );
@@ -835,12 +855,12 @@ sub TBot_List_handler($$$$;$)
       TBot_List_setMsgId( $hash, $tbot, $peer, $chatId, "chat" );
       
       # send msg and keys
-      fhem( "set ".$tbot." queryInline ".'@'.$chatId." $inline $textmsg" );
+      AnalyzeCommandChain( $hash, "set ".$tbot." queryInline ".'@'.$chatId." $inline $textmsg" );
       
     } else {
       if ( defined($msgId ) ) {
         # show new list 
-        fhem( "set ".$tbot." queryEditInline $msgId ".'@'.$chatId." $inline $textmsg" );
+        AnalyzeCommandChain( $hash, "set ".$tbot." queryEditInline $msgId ".'@'.$chatId." $inline $textmsg" );
       } else {
         $ret = "TBot_List_handler: $name - $tbot  ERROR no msgId known for peer :$peer: chat :$chatId:  cmd :$cmd:  ".(defined($arg)?"arg :$arg:":"");
       }
@@ -859,10 +879,15 @@ sub TBot_List_handler($$$$;$)
         my $textmsg = "Liste ".$lname."\nEintrag ".($no+1)." (".$list[$no].") ?";
         # show ask msgs (depending on attr)
         my $indata = ( AttrVal($name,'confirmDelete',1) ? "list_rem-$no" : "list_remyes-$no" );
-        my $inline = "(".TBot_List_inlinekey( $hash, "Entfernen", $indata )."|".
-                   TBot_List_inlinekey( $hash, "Aendern", "list_askchg-$no" )."|".
-                   TBot_List_inlinekey( $hash, "Nach Oben", "list_totop-$no" )."|".TBot_List_inlinekey( $hash, "Zurueck", "list_edit" ).")";
-        fhem( "set ".$tbot." queryEditInline $msgId ".'@'.$chatId." $inline $textmsg" );
+        my $inline = "(".TBot_List_inlinekey( $hash, "Entfernen", $indata );
+        
+        if ( ! $donly ) {
+          $inline .= "|".TBot_List_inlinekey( $hash, "Aendern", "list_askchg-$no" )."|".
+                        TBot_List_inlinekey( $hash, "Nach Oben", "list_totop-$no" );
+        }
+        $inline .= "|".TBot_List_inlinekey( $hash, "Zurueck", "list_edit" ).")";
+        
+        AnalyzeCommandChain( $hash, "set ".$tbot." queryEditInline $msgId ".'@'.$chatId." $inline $textmsg" );
       } else {
         $ret = "TBot_List_handler: $name - $tbot  ERROR no msgId known for peer :$peer: chat :$chatId:  cmd :$cmd:  ".(defined($arg)?"arg :$arg:":"");
       }
@@ -886,8 +911,8 @@ sub TBot_List_handler($$$$;$)
 
       my $text = join(",", @list );
       
-      fhem( "set ".TBot_List_getConfigPostMe($hash)." clear $lname " );
-      fhem( "set ".TBot_List_getConfigPostMe($hash)." add $lname $text" );
+      AnalyzeCommandChain( $hash, "set ".TBot_List_getConfigPostMe($hash)." clear $lname " );
+      AnalyzeCommandChain( $hash, "set ".TBot_List_getConfigPostMe($hash)." add $lname $text" );
     
       # show updated list -> call recursively
       TBot_List_handler( $hash,  "list_edit", $tbot, $peer, " Nach oben gesetzt" );
@@ -906,7 +931,7 @@ sub TBot_List_handler($$$$;$)
         my $textmsg = "Liste ".$lname."\nSoll der Eintrag ".($no+1)." (".$list[$no].") entfernt werden?";
         # show ask msg 
         my $inline = "(".TBot_List_inlinekey( $hash, "Ja", "list_remyes-$no" )."|".TBot_List_inlinekey( $hash, "Nein", "list_edit" ).")";
-        fhem( "set ".$tbot." queryEditInline $msgId ".'@'.$chatId." $inline $textmsg" );
+        AnalyzeCommandChain( $hash, "set ".$tbot." queryEditInline $msgId ".'@'.$chatId." $inline $textmsg" );
       } else {
         $ret = "TBot_List_handler: $name - $tbot  ERROR no msgId known for peer :$peer: chat :$chatId:  cmd :$cmd:  ".(defined($arg)?"arg :$arg:":"");
       }
@@ -924,8 +949,8 @@ sub TBot_List_handler($$$$;$)
 
       my $text = join(",", @list );
       
-      fhem( "set ".TBot_List_getConfigPostMe($hash)." clear $lname " );
-      fhem( "set ".TBot_List_getConfigPostMe($hash)." add $lname $text" );
+      AnalyzeCommandChain( $hash, "set ".TBot_List_getConfigPostMe($hash)." clear $lname " );
+      AnalyzeCommandChain( $hash, "set ".TBot_List_getConfigPostMe($hash)." add $lname $text" );
       
       # show updated list -> call recursively
       TBot_List_handler( $hash,  "list_edit", $tbot, $peer, " Eintrag geloescht" );
@@ -941,7 +966,7 @@ sub TBot_List_handler($$$$;$)
       # show menu msg 
       my $inline = "(".TBot_List_inlinekey( $hash, "Sortieren", "list_asksrt" )."|".TBot_List_inlinekey( $hash, "Leeren", "list_askclr" )."|".
                 TBot_List_inlinekey( $hash, "Zurück", "list_edit" ).")";
-      fhem( "set ".$tbot." queryEditInline $msgId ".'@'.$chatId." $inline $textmsg" );
+      AnalyzeCommandChain( $hash, "set ".$tbot." queryEditInline $msgId ".'@'.$chatId." $inline $textmsg" );
     } else {
       $ret = "TBot_List_handler: $name - $tbot  ERROR no msgId known for peer :$peer: chat :$chatId:  cmd :$cmd:  ".(defined($arg)?"arg :$arg:":"");
     }
@@ -955,7 +980,7 @@ sub TBot_List_handler($$$$;$)
       # show ask msg 
       my $inline = "(".TBot_List_inlinekey( $hash, "Ja - von A-Z", "list_srtyes1" )."|".TBot_List_inlinekey( $hash, "Ja - von Z-A", "list_srtyes2" )."|".
                 TBot_List_inlinekey( $hash, "Nein", "list_edit" ).")";
-      fhem( "set ".$tbot." queryEditInline $msgId ".'@'.$chatId." $inline $textmsg" );
+      AnalyzeCommandChain( $hash, "set ".$tbot." queryEditInline $msgId ".'@'.$chatId." $inline $textmsg" );
     } else {
       $ret = "TBot_List_handler: $name - $tbot  ERROR no msgId known for peer :$peer: chat :$chatId:  cmd :$cmd:  ".(defined($arg)?"arg :$arg:":"");
     }
@@ -973,8 +998,8 @@ sub TBot_List_handler($$$$;$)
         @list = sort {$b cmp $a} @list;
       }
       my $text = join( ",", @list );
-      fhem( "set ".TBot_List_getConfigPostMe($hash)." clear $lname " );
-      fhem( "set ".TBot_List_getConfigPostMe($hash)." add $lname $text" );     
+      AnalyzeCommandChain( $hash, "set ".TBot_List_getConfigPostMe($hash)." clear $lname " );
+      AnalyzeCommandChain( $hash, "set ".TBot_List_getConfigPostMe($hash)." add $lname $text" );     
     }
     
     # show updated list -> call recursively
@@ -989,7 +1014,7 @@ sub TBot_List_handler($$$$;$)
       my $textmsg = "Liste ".$lname."\nSoll die gesamte Liste ".scalar(@list)." Einträge gelöscht werden?";
       # show ask msg 
       my $inline = "(".TBot_List_inlinekey( $hash, "Ja - Liste löschen", "list_clryes" )."|".TBot_List_inlinekey( $hash, "Nein", "list_edit" ).")";
-      fhem( "set ".$tbot." queryEditInline $msgId ".'@'.$chatId." $inline $textmsg" );
+      AnalyzeCommandChain( $hash, "set ".$tbot." queryEditInline $msgId ".'@'.$chatId." $inline $textmsg" );
     } else {
       $ret = "TBot_List_handler: $name - $tbot  ERROR no msgId known for peer :$peer: chat :$chatId:  cmd :$cmd:  ".(defined($arg)?"arg :$arg:":"");
     }
@@ -997,7 +1022,7 @@ sub TBot_List_handler($$$$;$)
   #####################  
   } elsif ( $cmd eq "list_clryes" ) {
     # means remove all entries - now it is confirmed
-    fhem( "set ".TBot_List_getConfigPostMe($hash)." clear $lname " );
+    AnalyzeCommandChain( $hash, "set ".TBot_List_getConfigPostMe($hash)." clear $lname " );
 
     # show updated list -> call recursively
     TBot_List_handler( $hash,  "list_edit", $tbot, $peer, " Liste geloescht" );
@@ -1013,7 +1038,7 @@ sub TBot_List_handler($$$$;$)
     TBot_List_setMsgId( $hash, $tbot, $chatId, $textmsg, "textmsg" );
     
     # means ask for an entry to be added to the list
-    fhem( "set ".$tbot." msgForceReply ".'@'.$chatId." $textmsg" );
+    AnalyzeCommandChain( $hash, "set ".$tbot." msgForceReply ".'@'.$chatId." $textmsg" );
 
   #####################  
   } elsif ( $cmd eq "list_add" ) {
@@ -1028,11 +1053,11 @@ sub TBot_List_handler($$$$;$)
         $text .= ",".$entry ;
       }
        
-      fhem( "set ".TBot_List_getConfigPostMe($hash)." clear $lname " );
-      fhem( "set ".TBot_List_getConfigPostMe($hash)." add $lname $text" );
+      AnalyzeCommandChain( $hash, "set ".TBot_List_getConfigPostMe($hash)." clear $lname " );
+      AnalyzeCommandChain( $hash, "set ".TBot_List_getConfigPostMe($hash)." add $lname $text" );
     
     } else {
-      fhem( "set ".TBot_List_getConfigPostMe($hash)." add $lname ".$arg );
+      AnalyzeCommandChain( $hash, "set ".TBot_List_getConfigPostMe($hash)." add $lname ".$arg );
     }
     
     if ( defined($msgId ) ) {
@@ -1062,7 +1087,7 @@ sub TBot_List_handler($$$$;$)
       TBot_List_setMsgId( $hash, $tbot, $chatId, $textmsg, "textmsg" );
 
       # means ask for an entry to be added to the list
-      fhem( "set ".$tbot." msgForceReply ".'@'.$chatId." $textmsg" );
+      AnalyzeCommandChain( $hash, "set ".$tbot." msgForceReply ".'@'.$chatId." $textmsg" );
       
     }
 
@@ -1084,8 +1109,8 @@ sub TBot_List_handler($$$$;$)
         }
         $nre++;
       }
-      fhem( "set ".TBot_List_getConfigPostMe($hash)." clear $lname " );
-      fhem( "set ".TBot_List_getConfigPostMe($hash)." add $lname $text" );
+      AnalyzeCommandChain( $hash, "set ".TBot_List_getConfigPostMe($hash)." clear $lname " );
+      AnalyzeCommandChain( $hash, "set ".TBot_List_getConfigPostMe($hash)." add $lname $text" );
     }
     
     if ( defined($msgId ) ) {
@@ -1112,7 +1137,7 @@ sub TBot_List_handler($$$$;$)
       
       if ( AttrVal($name,'confirmUnsolicited',1) ) {
         my $inline = "(".TBot_List_inlinekey( $hash, "Ja", "list_expaddyes" )."|".TBot_List_inlinekey( $hash, "Nein", "list_edit" ).")";
-        fhem( "set ".$tbot." queryEditInline $msgId ".'@'.$chatId." $inline $textmsg" );
+        AnalyzeCommandChain( $hash, "set ".$tbot." queryEditInline $msgId ".'@'.$chatId." $inline $textmsg" );
       } else {
         # directly add entry  --> call recursively
         TBot_List_handler( $hash,  "list_expaddyes", $tbot, $peer );  
@@ -1128,7 +1153,7 @@ sub TBot_List_handler($$$$;$)
     my $addentry =  TBot_List_getMsgId( $hash, $tbot, $chatId, "expadd" );
 
     if ( defined($addentry ) ) {
-      fhem( "set ".TBot_List_getConfigPostMe($hash)." add $lname ".$addentry );
+      AnalyzeCommandChain( $hash, "set ".TBot_List_getConfigPostMe($hash)." add $lname ".$addentry );
       # show list again -> call recursively
       if ( defined($msgId ) ) {
         TBot_List_handler( $hash,  "list_edit", $tbot, $peer, " Eintrag hinzugefuegt" );
@@ -1257,6 +1282,8 @@ sub TBot_List_Setup($) {
   my %gets = (
     "queryAnswer" => undef,
     "textList" => undef,
+    "list" => undef,
+    "count" => undef,
 
   );
 
@@ -1344,6 +1371,9 @@ sub TBot_List_Setup($) {
     <li><code>textList</code><br>Returns a multiline string containing the list elements or <Leer>  
     </li>
     
+    <li><code>list</code><br>Returns a multiline string containing the list elements or an empty String  
+    </li>
+    
   </ul>
 
   <br><br>
@@ -1367,6 +1397,9 @@ sub TBot_List_Setup($) {
     <li><code>confirmDelete &lt;1 or 0&gt;</code><br>If set to 1 the bot will ask for a confirmation if an entry should be deleted. This is the default. With a value of 0 the additional confirmation will not be requested.
     </li> 
     
+    <li><code>deleteOnly &lt;1 or 0&gt;</code><br>If set to 1 the bot will only allow deletion of entries or the complete list (no new entries or entry text can be changed - neither sorting or similar will be possible). Default is 0 (all changes allowed).
+    </li> 
+    
   </ul>
 
   <br><br>

+ 188 - 93
fhem/core/FHEM/50_TelegramBot.pm

@@ -27,7 +27,7 @@
 #
 # Discussed in FHEM Forum: https://forum.fhem.de/index.php/topic,38328.0.html
 #
-# $Id: 50_TelegramBot.pm 15421 2017-11-11 16:02:47Z viegener $
+# $Id: 50_TelegramBot.pm 16382 2018-03-11 13:20:55Z viegener $
 #
 ##############################################################################
 # 0.0 2015-09-16 Started
@@ -138,24 +138,44 @@
 
 #   Fix minusdesc undefined issue
 #   Cleanup old code
+#   add favoritesMenu to send favorites 
+#   doc favoritesMenu
+#   correct favoritesMenu to allow parameter
+#   FIX: allow_nonref / eval also for makekeyboard #msg732757
+#   new set cmd silentmsg for disable_notification - syntax as in msg
+#   INT: change forceReply to options for sendit
+# 2.7 2017-12-20  set command silentmsg 
+
+#   new set cmd silentImage for disable_notification - syntax as in sendImage
+#   FIX: allow queryAsnwer also with defaultpeer not set #msg757339
+#   General. set commands not requiring a peer - internally set peers to 0 for sendit
+#   FIX: Doc missing end code tag
+#   Change log to not write _Set/_Get on ? parameter
+#   attr to handle set / del types for polling/allowedCmds/favorites
+#   silentInline added
+#   cmdSendSilent added and documented
+#   tests and fixes on handling of peers/chats for tbot_list and replies
+#   FIX: peer names not numeric in send commands
+#   FIX: disable also sending messages
+#   FIX: have disable attribute with dropdown
+#   Allow caption in sendImage also with \n\t
+# 2.8 2018-03-11  more silent cmds, caption formatting, several fixes 
 
 #   
 ##############################################################################
 # TASKS 
 #   
-#   \n in inline keyboards
+#   additional silent commands
 #   
 #   queryDialogStart / queryDialogEnd - keep msg id 
 #   
-#   
-#   
 #   remove keyboard after favorite confirm
 #   
 #   cleanup encodings
 #   
 #   replyKeyboardRemove - #msg592808
 #   
-#   add an option to send silent messages - msg556631
+#   \n in inline keyboards - not possible currently
 #   
 ##############################################################################
 
@@ -207,6 +227,10 @@ my %sets = (
   "message" => "textField",
   "msg" => "textField",
   "send" => "textField",
+  
+  "silentmsg" => "textField",
+  "silentImage" => "textField",
+  "silentInline" => "textField",
 
   "msgDelete" => "textField",
 
@@ -223,10 +247,13 @@ my %sets = (
   "sendDocument" => "textField",
   "sendMedia" => "textField",
   "sendVoice" => "textField",
-
+  
   "sendLocation" => "textField",
 
+  "favoritesMenu" => "textField",
+  
   "cmdSend" => "textField",
+  "cmdSendSilent" => "textField",
 
   "replaceContacts" => "textField",
   "reset" => undef,
@@ -283,7 +310,7 @@ sub TelegramBot_Initialize($) {
   $hash->{SetFn}      = "TelegramBot_Set";
   $hash->{AttrFn}     = "TelegramBot_Attr";
   $hash->{AttrList}   = "defaultPeer defaultPeerCopy:0,1 cmdKeyword cmdSentCommands favorites:textField-long favoritesInline:0,1 cmdFavorites cmdRestrictedPeer ". "cmdTriggerOnly:0,1 saveStateOnContactChange:1,0 maxFileSize maxReturnSize cmdReturnEmptyResult:1,0 pollingVerbose:1_Digest,2_Log,0_None ".
-  "cmdTimeout pollingTimeout disable queryAnswerText:textField cmdRespondChat:0,1 ".
+  "cmdTimeout pollingTimeout disable:1,0 queryAnswerText:textField cmdRespondChat:0,1 ".
   "allowUnknownContacts:1,0 textResponseConfirm:textField textResponseCommands:textField allowedCommands filenameUrlEscape:1,0 ". 
   "textResponseFavorites:textField textResponseResult:textField textResponseUnauthorized:textField ".
   "parseModeSend:0_None,1_Markdown,2_HTML,3_InMsg webPagePreview:1,0 utf8Special:1,0 favorites2Col:0,1 ".
@@ -436,7 +463,7 @@ sub TelegramBot_Set($@)
 {
   my ( $hash, $name, @args ) = @_;
   
-  Log3 $name, 4, "TelegramBot_Set $name: called ";
+  Log3 $name, 5, "TelegramBot_Set $name: called "; 
 
   ### Check Args
   my $numberOfArgs  = int(@args);
@@ -444,8 +471,6 @@ sub TelegramBot_Set($@)
 
   my $cmd = shift @args;
 
-  Log3 $name, 4, "TelegramBot_Set $name: Processing TelegramBot_Set( $cmd )";
-
   if (!exists($sets{$cmd}))  {
     my @cList;
     foreach my $k (keys %sets) {
@@ -462,29 +487,39 @@ sub TelegramBot_Set($@)
     return "TelegramBot_Set: Unknown argument $cmd, choose one of " . join(" ", @cList);
   } # error unknown cmd handling
 
+  Log3 $name, 4, "TelegramBot_Set $name: Processing TelegramBot_Set( $cmd )";
+
   my $ret = undef;
   
-  if( ($cmd eq 'message') || ($cmd eq 'queryInline') || ($cmd eq 'queryEditInline') || ($cmd eq 'queryAnswer') || ($cmd eq 'msg') || ($cmd eq '_msg') || ($cmd eq 'reply') || ($cmd eq 'msgEdit') || ($cmd eq 'msgForceReply') || ($cmd =~ /^send.*/ ) ) {
+  if( ($cmd eq 'message') || ($cmd eq 'queryInline') || ($cmd eq 'queryEditInline') || ($cmd eq 'queryAnswer') || 
+      ($cmd eq 'msg') || ($cmd eq '_msg') || ($cmd eq 'reply') || ($cmd eq 'msgEdit') || ($cmd eq 'msgForceReply') || 
+      ($cmd eq 'silentmsg') || ($cmd eq 'silentImage')  || ($cmd eq 'silentInline') || ($cmd =~ /^send.*/ ) ) {
 
     my $msgid;
     my $msg;
     my $addPar;
     my $sendType = 0;
+    my $options = "";
     my $peers;
     my $inline = 0;
+    my $needspeer = 1;
     
     if ( ($cmd eq 'reply') || ($cmd eq 'msgEdit' ) || ($cmd eq 'queryEditInline' ) ) {
-      return "TelegramBot_Set: Command $cmd, no peer, msgid and no text/file specified" if ( $numberOfArgs < 3 );
+      return "TelegramBot_Set: Command $cmd, no msgid and no text/file specified" if ( $numberOfArgs < 3 );
       $msgid = shift @args; 
       return "TelegramBot_Set: Command $cmd, msgId must be given as first parameter before peer" if ( $msgid =~ /^@/ );
       $numberOfArgs--;
-      $inline = 1 if ($cmd eq 'queryEditInline');
-    } elsif ($cmd eq 'msgForceReply')  {
-      $addPar = "{\"force_reply\":true}";
-    } elsif ($cmd eq 'queryInline')  {
-      $inline = 1;
+      # all three messages need also a peer/chat_id
+    } elsif ($cmd eq 'queryAnswer')  {
+      $needspeer = 0;
     }
     
+    # special options
+    $inline = 1 if ( ($cmd eq 'queryInline') || ($cmd eq 'queryEditInline') || ($cmd eq 'silentInline') );
+    $options .= " -force_reply- " if ($cmd eq 'msgForceReply');
+    $options .= " -silent- " if ( ($cmd eq 'silentmsg') || ($cmd eq 'silentImage') || ($cmd eq 'silentInline') ) ;
+    
+    
     return "TelegramBot_Set: Command $cmd, no peers and no text/file specified" if ( $numberOfArgs < 2 );
     # numberOfArgs might not be correct beyond this point
 
@@ -498,15 +533,17 @@ sub TelegramBot_Set($@)
       shift @args;
       last if ( int(@args) == 0 );
     }
-    
+      
     return "TelegramBot_Set: Command $cmd, no msg content specified" if ( int(@args) < 1 );
 
-    
-    if ( ! defined( $peers ) ) {
+
+    if ( ($needspeer ) && ( ! defined( $peers ) ) ) {
       $peers = AttrVal($name,'defaultPeer',undef);
       return "TelegramBot_Set: Command $cmd, without explicit peer requires defaultPeer being set" if ( ! defined($peers) );
+    } elsif ( ! defined( $peers ) ) {
+      $peers = 0;
     }
-    if ( ($cmd eq 'sendPhoto') || ($cmd eq 'sendImage') || ($cmd eq 'image') ) {
+    if ( ($cmd eq 'sendPhoto') || ($cmd eq 'sendImage') || ($cmd eq 'image') || ($cmd eq 'silentImage')  ) {
       $sendType = 1;
     } elsif ($cmd eq 'sendVoice')  {
       $sendType = 2;
@@ -587,18 +624,44 @@ sub TelegramBot_Set($@)
     }
       
     Log3 $name, 5, "TelegramBot_Set $name: start send for cmd :$cmd: and sendType :$sendType:";
-    $ret = TelegramBot_SendIt( $hash, $peers, $msg, $addPar, $sendType, $msgid );
+    $ret = TelegramBot_SendIt( $hash, $peers, $msg, $addPar, $sendType, $msgid, $options );
 
 
-  } elsif($cmd eq 'cmdSend') {
+  } elsif($cmd eq 'favoritesMenu') {
+
+    my $peers;
+    if ( int(@args) > 0 ) {
+      while ( $args[0] =~ /^@(..+)$/ ) {
+        my $ppart = $1;
+        return "TelegramBot_Set: Command $cmd, need exactly one peer" if ( ( defined( $peers ) ) );
+        $peers = (defined($peers)?$peers." ":"").$ppart;
+        shift @args;
+        last if ( int(@args) == 0 );
+      }   
+      return "TelegramBot_Set: Command $cmd, addiitonal parameter specified" if ( int(@args) >= 1 );
+    }
+    
+    if ( ! defined( $peers ) ) {
+      $peers = AttrVal($name,'defaultPeer',undef);
+      return "TelegramBot_Set: Command $cmd, without explicit peer requires defaultPeer being set" if ( ! defined($peers) );
+    }
+    
+    return "TelegramBot_Set: Command $cmd, no favorites defined" if ( ! defined( AttrVal($name,'favorites',undef) ) );
+
+    TelegramBot_SendFavorites($hash, $peers, undef, "", undef, undef, 0);
+  
+  } elsif($cmd =~ 'cmdSend(Silent)?') {
 
     return "TelegramBot_Set: Command $cmd, no peers and no text/file specified" if ( $numberOfArgs < 2 );
     # numberOfArgs might not be correct beyond this point
+    
+    my $options = "";
+    $options .= " -silent- " if ( ($cmd eq 'cmdSendSilent') ) ;
 
     my $peers;
     while ( $args[0] =~ /^@(..+)$/ ) {
       my $ppart = $1;
-      return "TelegramBot_Set: Command $cmd, need exactly one peer" if ( ($cmd eq 'reply') && ( defined( $peers ) ) );
+      return "TelegramBot_Set: Command $cmd, need exactly one peer" if ( ( defined( $peers ) ) );
       $peers .= " " if ( defined( $peers ) );
       $peers = "" if ( ! defined( $peers ) );
       $peers .= $ppart;
@@ -636,7 +699,7 @@ sub TelegramBot_Set($@)
     ( $isMediaStream ) = TelegramBot_IdentifyStream( $hash, $msg ) if ( defined( $msg ) );
       
     Log3 $name, 5, "TelegramBot_Set $name: start send for cmd :$cmd: and isMediaStream :$isMediaStream:";
-    $ret = TelegramBot_SendIt( $hash, $peers, $msg, undef, $isMediaStream, undef );
+    $ret = TelegramBot_SendIt( $hash, $peers, $msg, undef, $isMediaStream, undef, $options );
     
   } elsif($cmd eq 'msgDelete') {
 
@@ -703,7 +766,7 @@ sub TelegramBot_Set($@)
     Log3 $name, 5, "TelegramBot_Set $name: contacts newly set ";
 
   }
-
+  
   if ( ! defined( $ret ) ) {
     Log3 $name, 5, "TelegramBot_Set $name: $cmd done succesful: ";
   } else {
@@ -727,8 +790,6 @@ sub TelegramBot_Get($@)
   my $cmd = $args[0];
   my $arg = ($args[1] ? $args[1] : "");
 
-  Log3 $name, 5, "TelegramBot_Get $name: Processing TelegramBot_Get( $cmd )";
-
   if(!exists($gets{$cmd})) {
     my @cList;
     foreach my $k (sort keys %gets) {
@@ -745,6 +806,7 @@ sub TelegramBot_Get($@)
     return "TelegramBot_Get: Unknown argument $cmd, choose one of " . join(" ", @cList);
   } # error unknown cmd handling
 
+  Log3 $name, 4, "TelegramBot_Get $name: Processing TelegramBot_Get( $cmd )";
   
   my $ret = undef;
   
@@ -809,21 +871,22 @@ sub TelegramBot_Attr(@) {
   } else {
     Log3 $name, 5, "TelegramBot_Attr $name: $cmd  on $aName to <undef>";
   }
+  
   # $cmd can be "del" or "set"
   # $name is device name
   # aName and aVal are Attribute name and value
-  if ($cmd eq "set") {
-    if ($aName eq 'favorites') {
-      # Empty current alias list in hash
-      if ( defined( $hash->{AliasCmds} ) ) {
-        foreach my $key (keys %{$hash->{AliasCmds}} )
-            {
-                delete $hash->{AliasCmds}{$key};
-            }
-      } else {
-        $hash->{AliasCmds} = {};
-      }
+  if ($aName eq 'favorites') {
+    # Empty current alias list in hash
+    if ( defined( $hash->{AliasCmds} ) ) {
+      foreach my $key (keys %{$hash->{AliasCmds}} )
+          {
+              delete $hash->{AliasCmds}{$key};
+          }
+    } else {
+      $hash->{AliasCmds} = {};
+    }
 
+    if ($cmd eq "set") {
       # keep double ; for inside commands
       $aVal =~ s/;;/SeMiCoLoN/g; 
       my @clist = split( /;/, $aVal);
@@ -853,8 +916,42 @@ sub TelegramBot_Attr(@) {
       # set attribute value to newly combined commands
       $attr{$name}{'favorites'} = $newVal;
       $aVal = $newVal;
+    }
+  
+  } elsif ($aName eq 'allowedCommands') {
+    my $allowedName = "allowed_$name";
+    my $exists = ($defs{$allowedName} ? 1 : 0); 
+    my $alcmd = (($cmd eq "set")?$aVal:"<none>");
+    AnalyzeCommand(undef, "defmod $allowedName allowed");
+    AnalyzeCommand(undef, "attr $allowedName validFor $name");
+    AnalyzeCommand(undef, "attr $allowedName $aName ".$alcmd);
+    Log3 $name, 3, "TelegramBot_Attr $name: ".($exists ? "modified":"created")." $allowedName with commands :$alcmd:";
+    # allowedCommands only set on the corresponding allowed_device
+    return "\"TelegramBot_Attr: \" $aName ".($exists ? "modified":"created")." $allowedName with commands :$alcmd:"
+      
+  } elsif ($aName eq 'pollingTimeout') {
+    return "\"TelegramBot_Attr: \" $aName needs to be given in digits only" if ( ($cmd eq "set") && ( $aVal !~ /^[[:digit:]]+$/ ) );
+    # let all existing methods run into block
+    RemoveInternalTimer($hash);
+    $hash->{POLLING} = -1;
+    
+    # wait some time before next polling is starting
+    TelegramBot_ResetPolling( $hash );
+
+  } elsif ($aName eq 'disable') {
+    return "\"TelegramBot_Attr: \" $aName needs to be 1 or 0" if ( ($cmd eq "set") && ( $aVal !~ /^(1|0)$/ ) );
+    # let all existing methods run into block
+    RemoveInternalTimer($hash);
+    $hash->{POLLING} = -1;
+    
+    # wait some time before next polling is starting
+    TelegramBot_ResetPolling( $hash );
+
+    
+  # attributes where only the set is relevant for syntax check  
+  } elsif ($cmd eq "set") {
 
-    } elsif ($aName eq 'cmdRestrictedPeer') {
+    if ($aName eq 'cmdRestrictedPeer') {
       $aVal =~ s/^\s+|\s+$//g;
       
     } elsif ( ($aName eq 'defaultPeerCopy') ||
@@ -869,53 +966,13 @@ sub TelegramBot_Attr(@) {
               ($aName eq 'maxRetries') ) {
       return "\"TelegramBot_Attr: \" $aName needs to be given in digits only" if ( $aVal !~ /^[[:digit:]]+$/ );
 
-    } elsif ($aName eq 'pollingTimeout') {
-      return "\"TelegramBot_Attr: \" $aName needs to be given in digits only" if ( $aVal !~ /^[[:digit:]]+$/ );
-      # let all existing methods run into block
-      RemoveInternalTimer($hash);
-      $hash->{POLLING} = -1;
-      
-      # wait some time before next polling is starting
-      TelegramBot_ResetPolling( $hash );
-
-    } elsif ($aName eq 'disable') {
-      if ( $aVal =~ /^(1|0)$/ ) {
-        # let all existing methods run into block
-        RemoveInternalTimer($hash);
-        $hash->{POLLING} = -1;
-        
-        # wait some time before next polling is starting
-        TelegramBot_ResetPolling( $hash );
-      } else {
-        return "\"TelegramBot_Attr: \" $aName needs to be 1 or 0";
-      }
 
     } elsif ($aName eq 'pollingVerbose') {
       return "\"TelegramBot_Attr: \" Incorrect value given for pollingVerbose" if ( $aVal !~ /^((1_Digest)|(2_Log)|(0_None))$/ );
-
-    } elsif ($aName eq 'allowedCommands') {
-      my $allowedName = "allowed_$name";
-      my $exists = ($defs{$allowedName} ? 1 : 0); 
-      AnalyzeCommand(undef, "defmod $allowedName allowed");
-      AnalyzeCommand(undef, "attr $allowedName validFor $name");
-      AnalyzeCommand(undef, "attr $allowedName $aName ".$aVal);
-      Log3 $name, 3, "TelegramBot_Attr $name: ".($exists ? "modified":"created")." $allowedName with commands :$aVal:";
-      # allowedCommands only set on the corresponding allowed_device
-      return "\"TelegramBot_Attr: \" $aName ".($exists ? "modified":"created")." $allowedName with commands :$aVal:"
-
     }
 
     $_[3] = $aVal;
   
-  } elsif ($cmd eq "set") {
-    if ( ($aName eq 'pollingTimeout') || ($aName eq 'disable') ) {
-      # let all existing methods run into block
-      RemoveInternalTimer($hash);
-      $hash->{POLLING} = -1;
-      
-      # wait some time before next polling is starting
-      TelegramBot_ResetPolling( $hash );
-    }
   }
 
   return undef;
@@ -1052,6 +1109,7 @@ sub TelegramBot_SendFavorites($$$$$;$$) {
   my $name = $hash->{NAME};
   
   $aliasExec = 0 if ( ! $aliasExec );
+  $iscallback = 0 if ( ! $iscallback );
 
   my $ret;
   
@@ -1300,7 +1358,7 @@ sub TelegramBot_ReadHandleCommand($$$$$) {
 
   my $ret;
   
-  Log3 $name, 3, "TelegramBot_ReadHandleCommand $name: cmd found :".$cmd.": ";
+  Log3 $name, 4, "TelegramBot_ReadHandleCommand $name: cmd found :".$cmd.": ";
   
   Log3 $name, 5, "TelegramBot_ReadHandleCommand cmd correct peer ";
   # Either no peer defined or cmdpeer matches peer for message -> good to execute
@@ -1359,6 +1417,7 @@ sub TelegramBot_ExecuteCommand($$$$;$$) {
     # Check for image/doc/audio stream in return (-1 image
     ( $isMediaStream ) = TelegramBot_IdentifyStream( $hash, $ret ) if ( defined( $ret ) );
     
+    Log3 $name, 3, "TelegramBot_ExecuteCommand $name: cmd executed :".$cmd.": -->    :".TelegramBot_MsgForLog($ret, $isMediaStream ).":" if ( $ret );
   }
 
   Log3 $name, 4, "TelegramBot_ExecuteCommand result for analyze :".TelegramBot_MsgForLog($ret, $isMediaStream ).": ";
@@ -1609,6 +1668,9 @@ sub TelegramBot_SendIt($$$$$;$$$)
   $args[$TelegramBot_arg_retrycnt] = $retryCount+1;
   
   Log3 $name, 5, "TelegramBot_SendIt $name: called ";
+  
+  # ignore all sends if disabled
+  return if ( AttrVal($name,'disable',0) );
 
   # ensure sentQueue exists
   $hash->{sentQueue} = [] if ( ! defined( $hash->{sentQueue} ) );
@@ -1641,10 +1703,15 @@ sub TelegramBot_SendIt($$$$$;$$$)
   }
   
   Log3 $name, 5, "TelegramBot_SendIt $name: try to send message to :$peer: -:".
-      TelegramBot_MsgForLog($msg, ($isMedia<0) ).": - :".(defined($addPar)?$addPar:"<undef>").":";
+      TelegramBot_MsgForLog($msg, ($isMedia<0) ).": - add :".(defined($addPar)?$addPar:"<undef>").
+      ": - replyid :".(defined($replyid)?$replyid:"<undef>").
+      ":".":    options :".$options.":";
 
     # trim and convert spaces in peer to underline 
-  my $peer2 = TelegramBot_GetIdForPeer( $hash, $peer );
+  $peer = 0 if ( ! $peer ); # ensure peer is defined
+  my $peer2 = (! $peer )?$peer:TelegramBot_GetIdForPeer( $hash, $peer );
+  
+#  Debug "peer :$peer:    peer2 :$peer2:";
 
   if ( ! defined( $peer2 ) ) {
     $ret = "FAILED peer not found :$peer:";
@@ -1674,7 +1741,7 @@ sub TelegramBot_SendIt($$$$$;$$$)
   if ( ! defined( $ret ) ) {
 
     # add chat / user id (no file) --> this will also do init
-    $ret = TelegramBot_AddMultipart($hash, $hash->{HU_DO_PARAMS}, "chat_id", undef, $peer2, 0 );
+    $ret = TelegramBot_AddMultipart($hash, $hash->{HU_DO_PARAMS}, "chat_id", undef, $peer2, 0 ) if ( $peer );
 
     if ( ( $isMedia == 0 ) || ( $isMedia == 10 ) || ( $isMedia == 20 ) ) {
       if ( $isMedia == 0 ) {
@@ -1750,6 +1817,7 @@ sub TelegramBot_SendIt($$$$$;$$$)
       $hash->{HU_DO_PARAMS}->{url} = TelegramBot_getBaseURL($hash)."answerCallbackQuery";
       
       $ret = TelegramBot_AddMultipart($hash, $hash->{HU_DO_PARAMS}, "callback_query_id", undef, $addPar, 0 ) if ( ! defined( $ret ) );
+      $addPar = undef;
 
       $ret = TelegramBot_AddMultipart($hash, $hash->{HU_DO_PARAMS}, "text", undef, $msg, 0 ) if ( ( ! defined( $ret ) ) && ( $msg ) );
       
@@ -1762,6 +1830,9 @@ sub TelegramBot_SendIt($$$$$;$$$)
 
       # add caption
       if ( defined( $addPar ) ) {
+        $addPar =~ s/(?<![\\])\\n/\x0A/g;
+        $addPar =~ s/(?<![\\])\\t/\x09/g;
+
         $ret = TelegramBot_AddMultipart($hash, $hash->{HU_DO_PARAMS}, "caption", undef, $addPar, 0 ) if ( ! defined( $ret ) );
         $addPar = undef;
       }
@@ -1798,7 +1869,14 @@ sub TelegramBot_SendIt($$$$$;$$$)
 
     if ( defined( $addPar ) ) {
       $ret = TelegramBot_AddMultipart($hash, $hash->{HU_DO_PARAMS}, "reply_markup", undef, $addPar, 0 ) if ( ! defined( $ret ) );
+    } elsif ( $options =~ /-force_reply-/ ) {
+      $ret = TelegramBot_AddMultipart($hash, $hash->{HU_DO_PARAMS}, "reply_markup", undef, "{\"force_reply\":true}", 0 ) if ( ! defined( $ret ) );
     }
+    
+    if ( $options =~ /-silent-/ ) {
+      $ret = TelegramBot_AddMultipart($hash, $hash->{HU_DO_PARAMS}, "disable_notification", undef, "true", 0 ) if ( ! defined( $ret ) );
+    }
+
 
     # finalize multipart 
     $ret = TelegramBot_AddMultipart($hash, $hash->{HU_DO_PARAMS}, undef, undef, undef, 0 ) if ( ! defined( $ret ) );
@@ -1949,14 +2027,21 @@ sub TelegramBot_MakeKeyboard($$$@)
   }
   
   my $refkb = \%par;
-  
-  my $json        = JSON->new->utf8;
-  $ret = $json->utf8(0)->encode( $refkb );
-  Log3 $name, 4, "TelegramBot_MakeKeyboard $name: json :$ret: is utf8? ".(utf8::is_utf8($ret)?"yes":"no");
 
-  if ( utf8::is_utf8($ret) ) {
-    utf8::downgrade($ret); 
-    Log3 $name, 4, "TelegramBot_MakeKeyboard $name: json downgraded :$ret: is utf8? ".(utf8::is_utf8($ret)?"yes":"no");
+  # encode keyboard with JSON
+  my $json        = JSON->new->utf8->allow_nonref;
+  eval {
+    $ret = $json->utf8(0)->encode( $refkb );
+  };
+  Log3 $name, 2, "JSON encode() did fail with: ".(( $@ )?$@:"<unknown error>") if ( ! $ret );
+
+  if ( $ret ) {
+    Log3 $name, 4, "TelegramBot_MakeKeyboard $name: json :$ret: is utf8? ".(utf8::is_utf8($ret)?"yes":"no");
+
+    if ( utf8::is_utf8($ret) ) {
+      utf8::downgrade($ret); 
+      Log3 $name, 4, "TelegramBot_MakeKeyboard $name: json downgraded :$ret: is utf8? ".(utf8::is_utf8($ret)?"yes":"no");
+    }
   }
   
   return $ret;
@@ -3337,7 +3422,7 @@ sub TelegramBot_BinaryFileWrite($$$) {
 1;
 
 =pod
-=item summary    send and receive of messages through telegram instant messaging
+=item summary   send and receive of messages through telegram instant messaging
 =item summary_DE senden und empfangen von Nachrichten durch telegram IM
 =begin html
 
@@ -3427,6 +3512,9 @@ sub TelegramBot_BinaryFileWrite($$$) {
       <dl>
     </li>
     
+    <li><code>silentmsg, silentImage, silentInline ...</code><br>Sends the given message silently (with disabled_notifications) to the recipients. Syntax and parameters are the same as in the corresponding send/message command.
+    </li>
+    
     <li><code>msgForceReply [ @&lt;peer1&gt; ... @&lt;peerN&gt; ] &lt;text&gt;</code><br>Sends the given message to the recipient(s) and requests (forces) a reply. Handling of peers is equal to the message command. Adding reply keyboards is currently not supported by telegram.
     </li>
     <li><code>reply &lt;msgid&gt; [ @&lt;peer1&gt; ] &lt;text&gt;</code><br>Sends the given message as a reply to the msgid (number) given to the given peer or if peer is ommitted to the defined default peer user. Only a single peer can be specified. Beside the handling of the message as a reply to a message received earlier, the peer and message handling is otherwise identical to the msg command. 
@@ -3438,7 +3526,10 @@ sub TelegramBot_BinaryFileWrite($$$) {
     <li><code>msgDelete &lt;msgid&gt; [ @&lt;peer1&gt; ] </code><br>Deletes the given message on the recipients clients. The msgid of the message to be changed must match a valid msgId and the peers need to match the original recipient, so only a single peer can be given or if peer is ommitted the defined default peer user is used. Restrictions apply for deleting messages in the Bot API as currently specified here (<a href=https://core.telegram.org/bots/api#deletemessage>deleteMessage</a>)
     </li>
 
-    <li><code>cmdSend [ @&lt;peer1&gt; ... @&lt;peerN&gt; ] &lt;fhem command&gt;</code><br>Executes the given fhem command and then sends the result to the given peers or the default peer.<br>
+    <li><code>favoritesMenu [ @&lt;peer&gt; ] </code><br>send the favorites menu to the corresponding peer if defined</code>
+    </li>
+
+    <li><code>cmdSend|cmdSendSilent [ @&lt;peer1&gt; ... @&lt;peerN&gt; ] &lt;fhem command&gt;</code><br>Executes the given fhem command and then sends the result to the given peers or the default peer (cmdSendSilent does the same as silent message).<br>
     Example: The following command would sent the resulting SVG picture to the default peer: <br>
       <code>set tbot cmdSend { plotAsPng('SVG_FileLog_Aussen') }</code>
     </li>
@@ -3465,6 +3556,10 @@ sub TelegramBot_BinaryFileWrite($$$) {
     </li>
     <li><code>sendVoice [ @&lt;peer1&gt; ... @&lt;peerN&gt;] &lt;file&gt;</code><br>Sends a voice message for playing directly in the browser to the given peer(s) or if ommitted to the default peer. Handling for files and peers is as specified above.
     </li>
+    
+    <li><code>silentImage ...</code><br>Sends the given image silently (with disabled_notifications) to the recipients. Syntax and parameters are the same as in the sendImage command.
+    </li>
+    
 
   <br>
     <li><code>sendLocation [ @&lt;peer1&gt; ... @&lt;peerN&gt;] &lt;latitude&gt; &lt;longitude&gt;</code><br>Sends a location as pair of coordinates latitude and longitude as floating point numbers 

+ 17 - 8
fhem/core/FHEM/51_I2C_TSL2561.pm

@@ -1,3 +1,4 @@
+# $Id: 51_I2C_TSL2561.pm 15723 2017-12-29 21:08:17Z jensb $
 =head1
   51_I2C_TSL2561.pm
 
@@ -6,8 +7,6 @@
   connected to the Raspberry Pi.
 
   contributed by Kai Stuke 2014
-  
-  $Id: 51_I2C_TSL2561.pm 10443 2016-01-10 14:50:08Z jnsbyr $
 
 =head1 DESCRIPTION
   51_I2C_TSL2561.pm reads the illumination of the the ambient light sensor TSL2561
@@ -87,6 +86,8 @@
     changing gain/integrationTime attributes will no longer write to device but will be used at next poll
   26.12.2015 kaihs
     CalculateLux float arithmetics formula fix
+  02.01.2017 jensb
+    inverted check of I2C IO result (not "Ok" instead of "error")
     
     
 =head1 TODO
@@ -709,7 +710,7 @@ sub I2C_TSL2561_I2CRcvTiming ($$) {
   
   $hash->{tsl2561IntegrationTime} = $timing & 0x03;
   $hash->{tsl2561Gain}            = $timing & 0x10;          
-  Log3 $name, 5, "I2C_TSL2561_I2CRcvTiming: time $hash->{tsl2561IntegrationTime}, gain $hash->{tsl2561Gain}";
+  Log3 $name, 4, "I2C_TSL2561_I2CRcvTiming: time $hash->{tsl2561IntegrationTime}, gain $hash->{tsl2561Gain}";
   
   $hash->{acquiState} = TSL2561_ACQUI_STATE_IDLE;
   if (!$hash->{blockingIO}) {
@@ -726,7 +727,7 @@ sub I2C_TSL2561_I2CRcvChan0 ($$) {
   my $name = $hash->{NAME};
     
   $hash->{broadband} = $broadband;
-  Log3 $name, 5, 'I2C_TSL2561_I2CRcvChan0 ' . $broadband; 
+  Log3 $name, 4, 'I2C_TSL2561_I2CRcvChan0 ' . $broadband; 
   
   $hash->{acquiState} = TSL2561_ACQUI_STATE_DATA_CH0_RECEIVED;
   return undef;
@@ -740,7 +741,7 @@ sub I2C_TSL2561_I2CRcvChan1 ($$) {
   my $name = $hash->{NAME};
   
   $hash->{ir} = $ir;
-  Log3 $name, 5, 'I2C_TSL2561_I2CRcvChan1 ' . $ir;   
+  Log3 $name, 4, 'I2C_TSL2561_I2CRcvChan1 ' . $ir;   
 
   $hash->{acquiState} = TSL2561_ACQUI_STATE_DATA_CH1_RECEIVED;
   if (!$hash->{blockingIO}) {
@@ -961,7 +962,7 @@ sub I2C_TSL2561_SetTimingRegister($) {
       my $attrVal = AttrVal($name, 'integrationTime', 13);  
       $hash->{tsl2561IntegrationTime} = $attrVal == 402? TSL2561_INTEGRATIONTIME_402MS : $attrVal == 101? TSL2561_INTEGRATIONTIME_101MS : TSL2561_INTEGRATIONTIME_13MS;
     }
-    Log3 $name, 5, "I2C_TSL2561_SetTimingRegister: time $hash->{tsl2561IntegrationTime}, gain $hash->{tsl2561Gain}";
+    Log3 $name, 4, "I2C_TSL2561_SetTimingRegister: time $hash->{tsl2561IntegrationTime}, gain $hash->{tsl2561Gain}";
     if (I2C_TSL2561_i2cwrite($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, $hash->{tsl2561IntegrationTime} | $hash->{tsl2561Gain})) {
       if (I2C_TSL2561_i2cread($hash,  TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, 1)) {
         $success = 1;
@@ -989,6 +990,9 @@ sub I2C_TSL2561_SetIntegrationTime($$) {
  
   # store the value even if $hash->{tsl2561Package} is not set (yet). That happens
   # during fhem startup.
+  if (defined($hash->{tsl2561IntegrationTime}) && $hash->{tsl2561IntegrationTime} != $time) {
+    Log3 $name, 4, "I2C_TSL2561_SetIntegrationTime: $hash->{tsl2561IntegrationTime} -> $time";
+  }
   $hash->{tsl2561IntegrationTime} = $time;  
   $hash->{timingModified} = 1;  
   
@@ -1029,6 +1033,9 @@ sub I2C_TSL2561_SetGain($$) {
 
   # store the value even if $hash->{tsl2561Package} is not set (yet). That happens
   # during fhem startup.
+  if (defined($hash->{tsl2561Gain}) && $hash->{tsl2561Gain} != $gain) {
+    Log3 $name, 4, "I2C_TSL2561_SetGain: $hash->{tsl2561Gain} -> $gain";
+  }
   $hash->{tsl2561Gain} = $gain;
   $hash->{timingModified} = 1;  
 
@@ -1441,7 +1448,7 @@ sub I2C_TSL2561_i2cread($$$) {
       });
     };
     my $sendStat = $hash->{$iodev->{NAME}.'_SENDSTAT'};
-    if (defined($sendStat) && $sendStat eq 'error') {
+    if (defined($sendStat) && $sendStat ne 'Ok') {
       readingsSingleUpdate($hash, 'state', TSL2561_STATE_I2C_ERROR, 1);
       Log3 ($hash, 5, $hash->{NAME} . ": i2cread on $iodev->{NAME} failed");
       $success = 0;
@@ -1479,7 +1486,7 @@ sub I2C_TSL2561_i2cwrite($$$) {
       });
     };
     my $sendStat = $hash->{$iodev->{NAME}.'_SENDSTAT'};
-    if (defined($sendStat) && $sendStat eq 'error') {
+    if (defined($sendStat) && $sendStat ne 'Ok') {
       readingsSingleUpdate($hash, 'state', TSL2561_STATE_I2C_ERROR, 1);
       Log3 ($hash, 5, $hash->{NAME} . ": i2cwrite on $iodev->{NAME} failed");
       $success = 0;
@@ -1495,6 +1502,8 @@ sub I2C_TSL2561_i2cwrite($$$) {
 1;
 
 =pod
+=item summary TSL2561 luminosity sensor
+=item summary_DE TSL2561 Helligkeitssensor
 =begin html
 
 <a name="I2C_TSL2561"></a>

+ 19 - 5
fhem/core/FHEM/51_RPI_GPIO.pm

@@ -1,5 +1,5 @@
 ##############################################################################
-# $Id: 51_RPI_GPIO.pm 15350 2017-10-29 21:58:26Z klausw $
+# $Id: 51_RPI_GPIO.pm 15821 2018-01-07 21:36:43Z klausw $
 # 51_RPI_GPIO.pm
 #
 ##############################################################################
@@ -442,7 +442,7 @@ sub RPI_GPIO_Undef($$) {
 	}
 	if ( ( AttrVal($hash->{NAME}, "interrupt", "none") ) ne ( "none" ) ) {
 		delete $selectlist{$hash->{NAME}};
-		close($hash->{filehandle});
+		close($hash->{filehandle}) if defined $hash->{filehandle};
 	}
 	# to have a chance to externaly setup the GPIOs -
 	# leave GPIOs untouched if attr unexportpin is set to "no"
@@ -639,10 +639,24 @@ sub RPI_GPIO_exuexpin($$) {			#export, unexport, direction, pud_resistor via GPI
 			$sw = "-g mode";
 		} else {
 			$sw = "export";
-			$dir = "out" if ( $dir eq "high" || $dir eq "low" );		#auf out zurueck, da gpio tool dies nicht unterst?tzt
+			#$dir = "out" if ( $dir eq "high" || $dir eq "low" );		#auf out zurueck, da gpio tool dies nicht unterst?tzt
+		}
+		#my $exp = $gpioutility.' '.$sw.' '.$hash->{GPIO_Nr}. (defined $dir ? " " . $dir : "");
+		#$exp = `$exp`;
+		my $exp = "$gpioutility $sw $hash->{GPIO_Nr} $dir";
+		my $exp_result = `$exp 2>&1`;
+
+		if ($exp_result =~ /export: Invalid mode/) {
+			#gpio tool in neueren versionen (>= 2.25, feb 2015) unterstützt beim export high/low argumente. 
+			#das verhindert kurzes flickern beim restart von fhem.
+			#fallback auf alte syntax wenn utility "Invalid mode" retourniert
+			
+			Log3 $hash, 2, "$hash->{NAME}: WiringPi alte version erkannt. '$exp' $exp_result";
+
+			$exp = "$gpioutility $sw $hash->{GPIO_Nr} out"; 			#fallback auf out in alter version
+			$exp_result = `$exp 2>&1`
 		}
-		my $exp = $gpioutility.' '.$sw.' '.$hash->{GPIO_Nr}. (defined $dir ? " " . $dir : "");
-		$exp = `$exp`;
+		Log3 $hash, 4, "$hash->{NAME}: WiringPi executed: '$exp' $exp_result";
 	} else {
 		my $ret = "WiringPi gpio utility not (correct) installed";
 		Log3 $hash, 1, "$hash->{NAME}: $ret";

+ 9 - 5
fhem/core/FHEM/52_I2C_LM75A.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 52_I2C_LM75A.pm 15048 2017-09-10 21:45:38Z klausw $
+# $Id: 52_I2C_LM75A.pm 16302 2018-03-01 18:21:42Z klausw $
 #
 # adapted from 52_I2C_SHT3x.pm by stefan@clumsy.ch
 #
@@ -193,14 +193,18 @@ sub I2C_LM75A_GetTemp ($$) {
 	my @raw = split(" ",$rawdata);
 
 	my $temperature = 0;
-	if(($raw[0] & 0x80) > 0) {
-		$temperature = 0xffffff00;
-	}
+#	if(($raw[0] & 0x80) > 0) {
+#		$temperature = 0xffffff00;
+#	}
 #	$temperature |= ($raw[0] & 0x7f) << 1;
 #	$temperature |= (($raw[1] >> 7) & 1);
 
-  	my $temperature_11_bit = ($raw[0]<<8 | $raw[1]) >> 5; # Compute 11-bit temperature output value  
+  	my $temperature_11_bit = ($raw[0] << 8 | $raw[1]) >> 5; # Compute 11-bit temperature output value  
 	$temperature = ($temperature_11_bit) * 0.125; # Compute temperature in °C  
+	if(($raw[0] & 0x80) > 0) { # check for negative value
+#		$temperature *= -1;
+		$temperature -= 256.000;
+	}
 
 #	$temperature = $temperature / 2;
     	Log3 $hash, 5, "temperature: $temperature";    

+ 100 - 34
fhem/core/FHEM/53_GHoma.pm

@@ -1,52 +1,70 @@
-	##############################################
-# $Id: 53_GHoma.pm 14991 2017-09-02 17:36:55Z klausw $
+##############################################
+# $Id: 53_GHoma.pm 16234 2018-02-20 20:52:53Z klausw $
+#
+# 
+# modifikation fuer Energiemessung von martin-s
+#
+# Todo:
+# - unbekannte Strings mit Log1 speichern
+# - unbekannte Steckermodelle mit Log1 speichern
 #
 # Protokoll:
 # Prefix (5a a5), Anzahl Nutzbytes (2 Byte), Payload, Checksumme (FF - LowByte der Summe aller Payloadbytes), Postfix (5b b5)
 # Antwort von Dose hat immer die letzen 3 Bloecke der MAC vom 11-13 Byte
 #
 # Payload immer in "|"
-# Init1 (vom Server):
+#
+#Init1 (vom Server):
 # 5a a5 00 07|02 05 0d 07 05 07 12|c6 5b b5
 #                ** ** ** ** ** **													** scheinen zufaellig zu sein
 # 5a a5 00 01|02|fd 5b b5
-# Antwort auf Init1 von Dose:
+#Antwort auf Init1 von Dose:
 # 5A A5 00 0B|03 01 0A C0 32 23 62 8A 7E 01 C2|AF 5B B5
 #                               MM MM MM    **										MM: letzte 3 Stellen der MAC, ** scheinbar eine Checksumme basierend auf den 6 zufaelligen Bytes von Init1
-# Init2 (vom Server):
+#                         ?? ??                                                     ??: Unterschiedlich bei verschiedenen Steckermodellen
+#Init2 (vom Server):
 # 5a a5 00 02|05 01|f9 5b b5
-# Antwort auf Init2 von Dose:
+#Antwort auf Init2 von Dose:
 # 5A A5 00 12|07 01 0A C0 32 23 62 8A 7E 00 01 06 AC CF 23 62 8A 7E|5F 5B B5
 #                               MM MM MM											MM: letzte 3 Stellen der MAC
 #                                                 MM MM MM MM MM MM					MM: komplette MAC
+#                         ?? ??                                                     ??: Unterschiedlich bei verschiedenen Steckermodellen
 # 5A A5 00 12|07 01 0A C0 32 23 62 8A 7E 00 02 05 00 01 01 08 11|4C 5B B5    			Anzahl Bytes stimmt nicht! ist aber immer so
+#                                                       FF FF FF					FF: Firmware Version
 # 5A A5 00 15|90 01 0A E0 32 23 62 8A 7E 00 00 00 81 11 00 00 01 00 00 00 00|32 5B B5		Status der Dose (wird auch immer bei Zustandsaenderung geschickt)
 #                               MM MM MM											MM: letzte 3 Stellen der MAC
 #                                                 qq								qq: Schaltquelle 	81=lokal geschaltet, 11=remote geschaltet
 #                                                                         oo 		oo: Schaltzustand	ff=an, 00=aus
+#                         ?? ??                                                     ??: Unterschiedlich bei verschiedenen Steckermodellen
 # Danach kommt alle x Sekunden ein Heartbeat von der Dose:
 # 5A A5 00 09|04 01 0A C0 32 23 62 8A 7E|71 5B B5
 #                               MM MM MM
+#                         ?? ??                                                     ??: Unterschiedlich bei verschiedenen Steckermodellen
 # Antwort vom Server (wenn die nicht kommt blinkt Dose wieder und muss neu initialisiert werden):
 # 5a a5 00 01|06|f9 5b b5
 #---------------------------------------------------------------------------------------------------------
 # Einschalten der Dose:
 # 5a a5 00 17|10 01 01 0a e0 32 23 62 8a 7e ff fe 00 00 10 11 00 00 01 00 00 00 ff|26 5b b5
 #                                  MM MM MM
+#                            ?? ??                                                     ??: Unterschiedlich bei verschiedenen Steckermodellen
 # Ausschalten der Dose
 # 5a a5 00 17|10 01 01 0a e0 32 23 62 8a 7e ff fe 00 00 10 11 00 00 01 00 00 00 00|25 5b b5
 #                                  MM MM MM
+#                            ?? ??                                                     ??: Unterschiedlich bei verschiedenen Steckermodellen
 # beides wird quittiert (ebenso wird auch bei lokaler betaetigung quittiert) -> siehe 3. Antwort auf Init 2
+#---------------------------------------------------------------------------------------------------------
+# Bei Dosen mit Verbrauchsdaten:
+# 5A A5 00 16|90 01 0a e0 35 23 d3 48 d4 ff fe 01 81 39 00 00 01 03 20 00 56 9b|70 5b b5	Verbrauchsdaten
+#                         ?? ?? MM MM MM
+#																 id					id: Art der Daten 01 = Leistung, 02 = Energie, 03 = Spannung, 04 = Strom, 05 = Frequenz, 07 = maxpower, 08 = Cosphi
+#																	   VV VV VV     VV: Verbrauchswerte (muss durch 100 geteilt werden)
+
 package main;
 use strict;
 use warnings;
 use SetExtensions;
 use TcpServerUtils;
 
-use constant  { PREFIX    => pack('C*', (0x5a,0xa5)),
-                POSTFIX   => pack('C*', (0x5b,0xb5)),
-                INIT1A    => pack('C*', (0x02,0x05,0x0d,0x07,0x05,0x07,0x12)), };
-
 my $prefix =  pack('C*', (0x5a,0xa5));
 my $postfix = pack('C*', (0x5b,0xb5));
 
@@ -54,13 +72,25 @@ my $init1a =  pack('C*', (0x02,0x05,0x0d,0x07,0x05,0x07,0x12));
 my $init1b =  pack('C*', (0x02));
 my $init2 =   pack('C*', (0x05,0x01));
 my $hbeat =   pack('C*', (0x06));
-my $switch1 = pack('C*', (0x10,0x01,0x01,0x0a,0xe0,0x32,0x23));
+my $switch1 = pack('C*', (0x10,0x01,0x01,0x0a,0xe0));
 my $switch2 = pack('C*', (0xff,0xfe,0x00,0x00,0x10,0x11,0x00,0x00,0x01,0x00,0x00,0x00));
 
-my $dosehb =  pack('C*', (0x00,0x09,0x04,0x01,0x0A,0xC0,0x32,0x23));
-my $cinit1 =  pack('C*', (0x03,0x01,0x0a,0xc0,0x32,0x23));
-my $cmac =    pack('C*', (0x07,0x01,0x0a,0xc0,0x32,0x23));
-my $cswitch = pack('C*', (0x90,0x01,0x0a,0xe0,0x32,0x23));
+my $dosehb =  pack('C*', (0x00,0x09,0x04,0x01,0x0a,0xc0));
+my $cinit1 =  pack('C*', (0x03,0x01,0x0a,0xc0));
+my $cmac =    pack('C*', (0x07,0x01,0x0a,0xc0));
+my $cswitch = pack('C*', (0x90,0x01,0x0a,0xe0));
+
+my $measure = pack('C*', (0xff,0xfe,0x01,0x81,0x39,0x00,0x00,0x01));
+
+my %values = (
+	'01' => 'power',
+	'02' => 'energy',
+	'03' => 'voltage',
+	'04' => 'current',
+	'05' => 'frequency',
+	'07' => 'maxpower',
+	'08' => 'cosphi',
+);
 
 my $timeout = 60;
 
@@ -75,9 +105,10 @@ sub GHoma_Initialize($) {			#
   $hash->{AttrFn}   = "GHoma_Attr";
   $hash->{StateFn}  = "GHoma_State";
   $hash->{AttrList} = "restoreOnStartup:last,on,off restoreOnReinit:last,on,off blocklocal:yes,no ".
-                      "allowfrom connectTimeout connectInterval";
+                      "allowfrom connectTimeout connectInterval $readingFnAttributes";
   $hash->{noAutocreatedFilelog} = 1;		# kein Filelog bei Autocreate anlegen
-  $hash->{ShutdownFn} = "GHoma_Shutdown";
+  $hash->{ShutdownFn}	 = "GHoma_Shutdown";
+  $hash->{DbLog_splitFn} = "GHoma_DbLog_splitFn";
 }
 #####################################
 sub GHoma_ClientConnect($) {		# im Mom unnuetz
@@ -247,11 +278,11 @@ sub GHoma_Read($) {					# wird von der globalen loop aufgerufen (ueber $hash->{F
     return;
   }
   
-  if ( substr($buf,0,10) eq ($prefix . $dosehb )) {     									# Heartbeat (Dosen Id wird nicht ueberprueft)
-    #DevIo_SimpleWrite($hash, GHoma_BuildString($hbeat) , undef);
+  if ( substr($buf,0,8) eq ($prefix . $dosehb )) {     									# Heartbeat (Dosen Id wird nicht ueberprueft)
+	#DevIo_SimpleWrite($hash, GHoma_BuildString($hbeat) , undef);
 	RemoveInternalTimer($hash);
 	$buf =~ s/(.|\n)/sprintf("%.2X ",ord($1))/eg;		#empfangene Zeichen in Hexwerte wandeln
-	Log3 $name, 5, "$name empfangen: $buf";
+	Log3 $name, 5, "$name Heartbeatanfrage empfangen: $buf";
     syswrite( $hash->{CD}, GHoma_BuildString($hbeat) );
     Log3 $hash, 5, "$hash->{NAME} Heartbeat gesendet";
     InternalTimer(gettimeofday()+ $timeout, "GHoma_Timer", $hash,0);
@@ -272,10 +303,11 @@ sub GHoma_Read($) {					# wird von der globalen loop aufgerufen (ueber $hash->{F
 		}
 		
 		(my $smsg = $_) =~ s/(.|\n)/sprintf("%.2X ",ord($1))/eg;							# empfangene Zeichen in Hexwerte wandeln
-		Log3 $hash, 5, "$hash->{NAME} RX: 5A A5 $smsg";										# ...und ins Log schreiben
+		Log3 $hash, 4, "$hash->{NAME} RX: 5A A5 $smsg";										# ...und ins Log schreiben
+		
+		$hash->{Pattern} = unpack('H*', substr($_,6,2) ) unless defined $hash->{Pattern};
 		
-		if ( substr($_,2,6) eq ($cinit1)) {  												# Antwort auf erstes Init
-			#$hash->{Id} = substr($_,8,3);
+		if ( substr($_,2,4) eq ($cinit1)) {  												# Antwort auf erstes Init
 			$hash->{Id} = unpack('H*', substr($_,8,3) );
 			unless ($hash->{isClient}) {
 				# fuer Server Loesung bei erster Antwort von Dose nach bestehendem Device mit gleicher Id suchen und Verbindung auf dieses Modul uebertragen
@@ -289,10 +321,6 @@ sub GHoma_Read($) {					# wird von der globalen loop aufgerufen (ueber $hash->{F
 					}
 				}
 				unless ( defined $clientdefined) {							# ...ein Neues anlegen, falls keins existiert
-					#my $id = unpack('H*', $hash->{Id} );
-					#Log3 $name, 4, "GHoma Unknown device $id, please define it";
-					#DoTrigger("global", "UNDEFINED GHoma_$id GHoma $id");
-					#GHoma_moveclient($hash, $defs{"GHoma_$id"}) if ($defs{"GHoma_$id"});
 					Log3 $name, 4, "GHoma Unknown device $hash->{Id}, please define it";
 					DoTrigger("global", "UNDEFINED GHoma_$hash->{Id} GHoma $hash->{Id}");
 					GHoma_moveclient($hash, $defs{"GHoma_$hash->{Id}"}) if ($defs{"GHoma_$hash->{Id}"});
@@ -303,7 +331,7 @@ sub GHoma_Read($) {					# wird von der globalen loop aufgerufen (ueber $hash->{F
 				RemoveInternalTimer($hash);
 				InternalTimer(gettimeofday()+ $timeout, "GHoma_Timer", $hash,0);
 			}
-		} elsif ( substr($_,2,6) eq $cmac && substr($_,8,3) eq substr($_,17,3) ) {			# Nachricht mit MAC (kommt unter Anderem als Antwort auf Init2)
+		} elsif ( substr($_,2,4) eq $cmac && substr($_,8,3) eq substr($_,17,3) ) {			# Nachricht mit MAC (kommt unter Anderem als Antwort auf Init2)
 			my $mac;
 			for my $i (0...5) {			# MAC formattieren
 				$mac .= sprintf("%.2X",ord( substr($_,14+$i,1) ));
@@ -311,7 +339,10 @@ sub GHoma_Read($) {					# wird von der globalen loop aufgerufen (ueber $hash->{F
 				$mac .= ":";
 			}
 			$hash->{MAC} = $mac;
-		} elsif ( substr($_,2,6) eq $cswitch && (( length($_) - 5 ) == 0x15 ) ) {			# An oder Aus
+		} elsif ( substr($_,2,4) eq $cmac && (( length($_) - 5 ) == 0x11 ) ) {				# Nachricht mit Firmware Version
+			my ($high,$mid,$low)=unpack('CCC',substr($_,16,3));
+			$hash->{FWVERSION} = $high.".".$mid.".".$low;
+		} elsif ( substr($_,2,4) eq $cswitch && (( length($_) - 5 ) == 0x15 ) ) {			# An oder Aus
 			my $id = unpack('H*', substr($_,8,3) );
 			my $rstate = hex(unpack('H*', substr($_,22,1))) == 0xFF ? "on" : "off";
             my $src    = hex(unpack('H*', substr($_,14,1))) == 0x81 ? "local" : "remote";
@@ -339,11 +370,27 @@ sub GHoma_Read($) {					# wird von der globalen loop aufgerufen (ueber $hash->{F
 			
 			readingsBeginUpdate($hash);
 			readingsBulkUpdate($hash, 'state', $rstate);
-			readingsBulkUpdate($hash, 'source', $src);
+  			readingsBulkUpdate($hash, 'source', $src);
+  			readingsEndUpdate($hash, 1);
+		} elsif ( substr($_,2,4) eq $cswitch && substr($_,11,8) eq $measure && ( length($_) - 5 ) == 0x16) {	# Botschaft mit Verbrauchsdaten
+			readingsBeginUpdate($hash);
+			my $value=$values{unpack('H*',substr($_,19,1))};
+			if (defined($value)) {
+				my ($high,$mid,$low)=unpack('CCC',substr($_,21,3));
+				readingsBulkUpdate($hash, $value, ($high*65536+$mid*256+$low)/($value eq 'energy' ? 1000 : 100));
+			} else {
+				# readingsBulkUpdate($hash, 'message_'.unpack('H*',substr($_,19,1)), unpack('H*',substr($_,20)));
+				Log3 $name, 3, "$name unknown control message Id: " . unpack('H*',substr($_,19,1)) . " value: " . unpack('H*',substr($_,20));
+			}
 			readingsEndUpdate($hash, 1);
-		} 
+		} else {
+			# readingsBeginUpdate($hash);
+			# readingsBulkUpdate($hash, 'message_unkn', unpack('H*',$_));
+			# readingsEndUpdate($hash, 1);
+			Log3 $name, 3, "$name unknown message: " . unpack('H*',$_);
+		}
+      }
     }
-  }
   #Log3 $name, 5, "$name empfangen: $buf";
   return
 }
@@ -396,7 +443,8 @@ sub GHoma_Set($@) {					#
 	return SetExtensions($hash, $slist, @a);
   }
   if (defined $hash->{CD}) {
-	syswrite( $hash->{CD}, GHoma_BuildString($switch1 . pack('C*', ( hex(substr($hash->{Id},0,2)), hex(substr($hash->{Id},2,2)), hex(substr($hash->{Id},4,2)) ) ) . $switch2 . $type) );
+  	Log3 $hash, 2, "$hash->{NAME}: Pattern noch nicht empfangen" unless defined $hash->{Pattern};
+	syswrite( $hash->{CD}, GHoma_BuildString($switch1  . pack('C*', ( hex(substr($hash->{Pattern},0,2)), hex(substr($hash->{Pattern},2,2)) ) ) . pack('C*', ( hex(substr($hash->{Id},0,2)), hex(substr($hash->{Id},2,2)), hex(substr($hash->{Id},4,2)) ) ) . $switch2 . $type) );
   }
   return undef;
 }
@@ -456,6 +504,24 @@ sub GHoma_udpbroad {
 
 }
 
+sub GHoma_DbLog_splitFn($) {  			# Einheiten
+    my ($event) = @_;
+    Log3 undef, 5, "in DbLog_splitFn empfangen: $event"; 
+    my ($reading, $value, $unit) = "";
+
+    my @parts = split(/ /,$event);
+    $reading = shift @parts;
+    $reading =~ tr/://d;
+    $value = $parts[0];
+    $unit = "W" 	if(lc($reading) =~ m/power/);
+    $unit = "kWh" 	if(lc($reading) =~ m/energy/);
+    $unit = "V" 	if(lc($reading) =~ m/voltage/);
+    $unit = "A" 	if(lc($reading) =~ m/current/);
+    $unit = "Hz" 	if(lc($reading) =~ m/frequency/);
+    $unit = "W" 	if(lc($reading) =~ m/maxpower/);
+    return ($reading, $value, $unit);
+}
+
 1;
 
 =pod
@@ -648,4 +714,4 @@ sub GHoma_udpbroad {
 
 =end html_DE
 
-=cut 
+=cut

+ 8 - 1
fhem/core/FHEM/55_InfoPanel.pm

@@ -1,4 +1,4 @@
-# $Id: 55_InfoPanel.pm 14101 2017-04-25 10:19:12Z betateilchen $
+# $Id: 55_InfoPanel.pm 15708 2017-12-27 15:01:42Z betateilchen $
 
 =for comment
 ##############################################
@@ -260,8 +260,15 @@ sub btIP_readLayout {
 
   my ($err, @layoutfile) = FileRead($filename);
   if($err) {
+#    Log 1, "InfoPanel $name: $err";
+#    $hash->{fhem}{layout} = "text ERROR 50 50 \"Error on reading layout!\"";
     Log 1, "InfoPanel $name: $err";
     $hash->{fhem}{layout} = "text ERROR 50 50 \"Error on reading layout!\"";
+    my ($e,@layout) = FileRead('./FHEM/template.layout');
+    unless ($e){
+       FileWrite($filename,@layout);
+       $hash->{fhem}{layout} = "text ERROR 50 50 \"Please edit layoutfile now.\"";
+    }
   } else {
     $hash->{fhem}{layout} = join("\n", @layoutfile);
     while($hash->{fhem}{layout} =~ m/\@include/ && $level < 1000) {

+ 0 - 0
fhem/core/FHEM/57_CALVIEW.pm


Some files were not shown because too many files changed in this diff