Explorar o código

Create and implment Docker for JTradfri
Include FHEM Updates from 20181017

hmetzner %!s(int64=7) %!d(string=hai) anos
pai
achega
ab3fbd4b23
Modificáronse 100 ficheiros con 17315 adicións e 6760 borrados
  1. 27 14
      fhem-compose.yml
  2. 62 16
      fhem/core/CHANGED
  3. 2 1
      fhem/core/FHEM/00_MQTT.pm
  4. 26 8
      fhem/core/FHEM/00_MQTT2_SERVER.pm
  5. 26 13
      fhem/core/FHEM/01_FHEMWEB.pm
  6. 22 16
      fhem/core/FHEM/10_CUL_HM.pm
  7. 57 29
      fhem/core/FHEM/10_EQ3BT.pm
  8. 57 1
      fhem/core/FHEM/10_EnOcean.pm
  9. 6 4
      fhem/core/FHEM/10_IT.pm
  10. 45 20
      fhem/core/FHEM/10_KNX.pm
  11. 67 43
      fhem/core/FHEM/10_MQTT2_DEVICE.pm
  12. 2 1
      fhem/core/FHEM/10_MQTT_BRIDGE.pm
  13. 2 1
      fhem/core/FHEM/10_MQTT_DEVICE.pm
  14. 1068 252
      fhem/core/FHEM/10_MQTT_GENERIC_BRIDGE.pm
  15. 15 11
      fhem/core/FHEM/12_HProtocolGateway.pm
  16. 2 2
      fhem/core/FHEM/12_HProtocolTank.pm
  17. 117 58
      fhem/core/FHEM/19_Revolt.pm
  18. 10 7
      fhem/core/FHEM/20_X10.pm
  19. 233 0
      fhem/core/FHEM/24_Iluminize.pm
  20. 38 25
      fhem/core/FHEM/24_TPLinkHS110.pm
  21. 10 11
      fhem/core/FHEM/30_DUOFERN.pm
  22. 339 0
      fhem/core/FHEM/30_TradfriGateway.pm
  23. 266 0
      fhem/core/FHEM/31_TradfriDevice.pm
  24. 259 0
      fhem/core/FHEM/31_TradfriGroup.pm
  25. 143 115
      fhem/core/FHEM/32_withings.pm
  26. 1459 950
      fhem/core/FHEM/34_ESPEasy.pm
  27. 1091 0
      fhem/core/FHEM/36_Shelly.pm
  28. 87 63
      fhem/core/FHEM/38_netatmo.pm
  29. 20 20
      fhem/core/FHEM/50_HP1000.pm
  30. 7 4
      fhem/core/FHEM/51_MOBILEALERTS.pm
  31. 986 740
      fhem/core/FHEM/55_DWD_OpenData.pm
  32. 87 19
      fhem/core/FHEM/57_Calendar.pm
  33. 7 5
      fhem/core/FHEM/59_HCS.pm
  34. 22 1
      fhem/core/FHEM/59_LuftdatenInfo.pm
  35. 717 0
      fhem/core/FHEM/70_ZoneMinder.pm
  36. 27 12
      fhem/core/FHEM/71_YAMAHA_AVR.pm
  37. 504 0
      fhem/core/FHEM/71_ZM_Monitor.pm
  38. 21 14
      fhem/core/FHEM/72_FRITZBOX.pm
  39. 78 21
      fhem/core/FHEM/72_XiaomiDevice.pm
  40. 741 550
      fhem/core/FHEM/73_GardenaSmartBridge.pm
  41. 632 427
      fhem/core/FHEM/74_GardenaSmartDevice.pm
  42. 214 35
      fhem/core/FHEM/88_HMCCU.pm
  43. 5 3
      fhem/core/FHEM/88_HMCCUCHN.pm
  44. 108 62
      fhem/core/FHEM/88_HMCCUDEV.pm
  45. 3 3
      fhem/core/FHEM/90_at.pm
  46. 76 46
      fhem/core/FHEM/93_DbLog.pm
  47. 683 453
      fhem/core/FHEM/93_DbRep.pm
  48. 31 6
      fhem/core/FHEM/93_FHEM2FHEM.pm
  49. 922 333
      fhem/core/FHEM/93_Log2Syslog.pm
  50. 74 43
      fhem/core/FHEM/95_Alarm.pm
  51. 5 6
      fhem/core/FHEM/95_Astro.pm
  52. 198 137
      fhem/core/FHEM/95_YAAHM.pm
  53. 4 5
      fhem/core/FHEM/96_allowed.pm
  54. 696 366
      fhem/core/FHEM/98_DOIF.pm
  55. 2 3
      fhem/core/FHEM/98_HMinfo.pm
  56. 1878 865
      fhem/core/FHEM/98_MSwitch.pm
  57. 2 2
      fhem/core/FHEM/98_SVG.pm
  58. 29 7
      fhem/core/FHEM/98_telnet.pm
  59. 79 6
      fhem/core/FHEM/HMCCUConf.pm
  60. 5 3
      fhem/core/FHEM/TcpServerUtils.pm
  61. 244 0
      fhem/core/FHEM/TradfriUtils.pm
  62. 1 0
      fhem/core/FHEM/controls.txt
  63. 76 72
      fhem/core/FHEM/controls_fhem.txt
  64. 44 43
      fhem/core/FHEM/controls_fhemtabletui.txt
  65. 23 23
      fhem/core/FHEM/controls_ha_theme.txt
  66. 4 0
      fhem/core/FHEM/controls_tradfri.txt
  67. BIN=BIN
      fhem/core/FHEM/lib/fhem_zwave_deviceconfig.xml.gz
  68. 3 2
      fhem/core/FHEM/lib/openzwave_manufacturer_specific.xml
  69. 4 2
      fhem/core/MAINTAINER.txt
  70. 0 1
      fhem/core/demolog/fhem.save
  71. 1398 322
      fhem/core/docs/commandref.html
  72. 844 335
      fhem/core/docs/commandref_DE.html
  73. 5 8
      fhem/core/docs/commandref_frame.html
  74. 5 8
      fhem/core/docs/commandref_frame_DE.html
  75. 15 8
      fhem/core/fhem.cfg
  76. 0 13
      fhem/core/fhem.cfg.demo
  77. 15 12
      fhem/core/fhem.pl
  78. 7 1
      fhem/core/www/hausautomatisierung-com/custom.js
  79. 4 2
      fhem/core/www/pgm2/alarm.js
  80. 15 0
      fhem/core/www/pgm2/darksmallscreenstyle.css
  81. 3 0
      fhem/core/www/pgm2/darkstyle.css
  82. 20 9
      fhem/core/www/pgm2/f18.js
  83. 28 1
      fhem/core/www/pgm2/f18style.css
  84. 13 3
      fhem/core/www/pgm2/fhemweb.js
  85. 1 1
      fhem/core/www/pgm2/hausautomatisierung_comfloorplanstyle.css
  86. 11 10
      fhem/core/www/pgm2/hausautomatisierung_comstyle.css
  87. 16 0
      fhem/core/www/pgm2/smallscreenstyle.css
  88. 68 7
      fhem/core/www/tablet/css/fhem-tablet-ui.css
  89. 1 1
      fhem/core/www/tablet/css/fhem-tablet-ui.min.css
  90. 10 0
      fhem/core/www/tablet/css/ftui_chart.css
  91. 9 18
      fhem/core/www/tablet/fonts/FTUI-icons.svg
  92. BIN=BIN
      fhem/core/www/tablet/fonts/FTUI-icons.ttf
  93. BIN=BIN
      fhem/core/www/tablet/fonts/FTUI-icons.woff
  94. BIN=BIN
      fhem/core/www/tablet/fonts/fa-brands-400.eot
  95. 27 0
      fhem/core/www/tablet/fonts/fa-brands-400.svg
  96. BIN=BIN
      fhem/core/www/tablet/fonts/fa-brands-400.ttf
  97. BIN=BIN
      fhem/core/www/tablet/fonts/fa-brands-400.woff
  98. BIN=BIN
      fhem/core/www/tablet/fonts/fa-brands-400.woff2
  99. BIN=BIN
      fhem/core/www/tablet/fonts/fa-regular-400.eot
  100. 0 0
      fhem/core/www/tablet/fonts/fa-regular-400.svg

+ 27 - 14
fhem-compose.yml

@@ -39,6 +39,19 @@ services:
 #        build: habridge
 #        network_mode: host
 
+    tradfri:
+        restart: unless-stopped
+        container_name: fhem-jtradfri
+        expose:
+            - "1505"
+        ports:
+            - "1505:1505"
+        build: tradfribridge
+        volumes:
+            - ./tradfribridge/jtradfri.conf:/opt/jtradfri/jtradfri.conf
+        networks:
+            - fhem-network
+
     mysql:
         restart: unless-stopped
         container_name: fhem-mysql
@@ -74,20 +87,20 @@ services:
             - ./mqtt/log/:/mqtt/log/
             - ./mqtt/data/:/mqtt/data/
 
-    nodered:
-        restart: unless-stopped
-        container_name: fhem-nodered
-        expose:
-            - "1880"
-        ports:
-            - "1880:1880"
-        image: nodered/node-red-docker:0.18.4
-        volumes:
-            - ./nodered/data/:/data/
-        networks:
-            - fhem-network
-        depends_on:
-            - "mqtt"
+#    nodered:
+#        restart: unless-stopped
+#        container_name: fhem-nodered
+#        expose:
+#            - "1880"
+#        ports:
+#            - "1880:1880"
+#        image: nodered/node-red-docker:0.18.4
+#        volumes:
+#            - ./nodered/data/:/data/
+#        networks:
+#            - fhem-network
+#        depends_on:
+#            - "mqtt"
 
 networks:
     fhem-network:

+ 62 - 16
fhem/core/CHANGED

@@ -1,11 +1,57 @@
 # Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
 # Do not insert empty lines here, update check depends on it.
+  - change:  93_DbLog: Log output of recuceLogNbl & use of charFilter enhanced
+  - feature: 93_Log2Syslog: attribute sslCertPrefix added (Forum:#92030)
+  - bugfix:  10_IT fix usage with multible io devices
+  - bugfix:  73_GardenaSmartBridge/Device fix typo oK to ok, change to
+                package System
+  - change:  93_DbRep: V8.2.3, check availability of DbLog-device at 
+                       definition time of DbRep-device
+  - change:  93_Log2Syslog: send BSD-format changed, commandref revised
+  - bugfix:  93_DbRep: fix don't get the real min timestamp in special cases
+  - bugfix:  51_MOBILEALERTS: Fix temperature for MA10320PRO
+  - change:  93_DbLog, 93_DbRep: minor fix time until DB is closed
+  - change:  38_netatmo: changed weathermap calls to new endpoints
+  - feature: 5.9 released
+
+- 2018-10-07 (5.9)
+  - change:  93_DbRep: direct help for attributes
+  - bugfix:  72_XiaomiDevice: set incorrect fan speed to 0
+  - bugfix:  38_netatmo: increased timeouts to counter server issues, logging
+  - bugfix:  88_HMCCU: Detection of non standard addresses
+  - feature: 93_Log2Syslog: TCP-Server in Collector-mode / SSL-support and a 
+                            lot more, pls. see new get versionNotes command 
+                            for further Information
+  - feature: 93_DbRep: 8.1.0, new get versionNotes command
+  - bugfix:  88_HMCCU: Temporary CCU programs are ignored now.
+  - change:  19_Revolt: allow adjustment of energy value, filtering of 
+             implausible values
+  - bugfix:  72_XiaomiDevice: better handling of definition w/ missing token
+  - change:  32_withings: add in_bed for sleep trackers, ignore inactive users
+  - change:  57_Calendar: new attribute quirks with ignoreDtStamp value.
+  - change:  57_Calendar: cutoffOlderThan also removes recurring events when
+             the series has ended.
+  - change:  55_DWD_OpenData: KML based forecast, readings have changed,
+             attr forecastProperties must be updated (forum #83097)
+  - bugfix:  12_HProtocolGateway: Filllevel conversion corrected
+  - bugfix:  57_Calendar.pm: make ical file work again
+  - change:  34_ESPEasy: get/set cmd handling rewritten, more supported ESP
+             Easy cmds, user defined cmds/mappings, nfx plugin handling
+             changed (Forum #91353)
+  - change:  93_DbRep: an invalid timestamp will put into state if found on
+                       startup
+  - bugfix:  38_netatmo: fixed changed values and deprecated API calls
+  - bugfix:  93_DbLog: 3.12.1, crash if SVG called (forum:#91285)
+  - change:  32_withings: API endpoint changed back to Withings servers
+  - bugfix:  93_DbLog: 3.12.0, SVG-select corrected
+             (forum.fhem.de/index.php/topic,65860.msg815640.html#msg815640)
+  - bugfix:  88_HMCCU: Fixed device definition bug.
   - bugfix:  88_HMCCU: Fixed shutdown bug.
   - bugfix:  32_withings: API endpoint change to Nokia servers
   - feature: 93_DbRep: V8.0.0, restoreMySQL for clientSide dumps implemented
   - bugfix:  74_XiaomiBTLESens: fix bug in disabledForInterval (Forum #835350)
   - change:  88_HMCCU: New release
-  - feature: 93_DbLog: 3.11.0, function reduceLog[Nbl] syntax extended to limit 
+  - feature: 93_DbLog: 3.11.0, function reduceLog[Nbl] syntax extended to limit
                                days to reduce (pls. see commandref for details)
   - change:  01_FHEMWEB.pm: change default style to f18
   - change:  01_FHEMWEB.pm: change iconPath: to fhemSVG:openautomation:default
@@ -13,27 +59,27 @@
   - change:  98_DOIFtools: improved direct help contetn
   - change:  38_netatmo: removed rain sum readings
   - feature: 00_MYSENSORS: FOTA (thanks Beta-User), gateway fix (thanks Sidey)
-  - new:     12_HProtocolGateway / 12_HProtocolTank 
+  - new:     12_HProtocolGateway / 12_HProtocolTank
   - bugfix:  72_XiaomiDevice: remove unused battery readings for new fans
-  - feature: 49_SSCam: activate/deactivate cam internal PIR-sensor 
-  - new:     10_MQTT_GENERIC_BRIDGE an MQTT bridge, which simultaneously 
-             collects data from several FHEM devices and passes 
-             their readings via MQTT or set readings from 
-             the incoming MQTT messages or executes them 
+  - feature: 49_SSCam: activate/deactivate cam internal PIR-sensor
+  - new:     10_MQTT_GENERIC_BRIDGE an MQTT bridge, which simultaneously
+             collects data from several FHEM devices and passes
+             their readings via MQTT or set readings from
+             the incoming MQTT messages or executes them
              as a 'set' command on the configured FHEM device.
   - feature: 72_XiaomiDevice: vacuum events, resume zoned cleanup
   - feature: 51_MOBILEALERTS: Added MA10120PRO
   - feature: 51_MOBILEALERTS: Added TFA30.3060.01.IT
   - change:  89_FULLY: Set start URL and bug fixes
-  - bugfix:  49_SSCam: V7.0.1, enable/disable issue 
+  - bugfix:  49_SSCam: V7.0.1, enable/disable issue
              (forum.fhem.de/index.php/topic,45671.msg830869.html#msg830869)
   - change:  98_DOIFtools: add help labels in commandref for attr, set and get
                 add MODEL to statistics report, check DOIF excludes Perl mode
   - new:     98_MSwitch.pm: Multi Switch Modul
-  - feature: 93_DbRep: V7.19.0, attribute "valueFilter" to filter datasets in 
+  - feature: 93_DbRep: V7.19.0, attribute "valueFilter" to filter datasets in
                                 fetchrows
   - feature: 49_SSCam: V7.0.0, compatibility to SVS 8.2.0
-  - feature: 93_Log2Syslog: V4.8.5, new Syslog-Server Mode and some other 
+  - feature: 93_Log2Syslog: V4.8.5, new Syslog-Server Mode and some other
                             improvements, version is moved from contrib
   - bugfix:  73_GardenaSmartBridge fix get humidity bug, add rename Fn
   - change:  71_YAMAHA_AVR: renamed attributes:
@@ -43,17 +89,17 @@
              already configured values will be converted by FHEM automatically
   - feature: 71_YAMAHA_AVR: add new set command tunerFrequencyBand to change
              between FM and DAB band on DAB-based models
-  - bugfix:  71_YAMAHA_AVR: fix set commands preset/presetUp/presetDown for 
+  - bugfix:  71_YAMAHA_AVR: fix set commands preset/presetUp/presetDown for
              input tuner on DAB based models
   - bugfix:  71_YAMAHA_AVR: fix set commands remoteControl tunerPresetUp,
-             tunerPresetDown and tunerFrequency for DAB based models 
+             tunerPresetDown and tunerFrequency for DAB based models
   - added:   98_systemd_watchdog: tested and moved from contrib
   - bugfix:  36_Vallox: Initialization bug fixed
-  - added:   00_MQTT: ability for client notifications: on connect, 
+  - added:   00_MQTT: ability for client notifications: on connect,
                       on disconnect, on timeout
-  - fixed:   00_MQTT: failure on handling with mqtt-devices without 
+  - fixed:   00_MQTT: failure on handling with mqtt-devices without
                       or with wrong devio attributes
-  - change:  maintainer change for 00_MQTT, 10_MQTT_DEVICE, 10_MQTT_BRIDGE 
+  - change:  maintainer change for 00_MQTT, 10_MQTT_DEVICE, 10_MQTT_BRIDGE
              (eisler => hexenmeister)
   - feature: 36_Vallox: Added DF Method, Added CO2SetPoint Handling, Optimized
   - feature: 59_WUup: added attribute unit_solarradiation
@@ -63,7 +109,7 @@
   - bugfix:  93_DbRep: fix in fetchrow function (forum:#89886),fix highlighting
   - bugfix:  82_LGTV_WebOS: fix set cmd for AmazonLovefilm
   - feature: 51_MOBILEALERTS: added feature to adjust values in define
-  - changed: 09_CUL_FHTTK: adapted battery reading to new common reading 
+  - changed: 09_CUL_FHTTK: adapted battery reading to new common reading
                 Forum #87575
   - bugfix:  82_LGTV_WebOS: fix uninitialized value
   - change:  98_dewpoint: adjust log level for FAN/ALARM on/off to 4

+ 2 - 1
fhem/core/FHEM/00_MQTT.pm

@@ -2,6 +2,7 @@
 #
 # fhem bridge to mqtt (see http://mqtt.org)
 #
+# Copyright (C) 2018 Alexander Schulz
 # Copyright (C) 2017 Stephan Eisler
 # Copyright (C) 2014 - 2016 Norbert Truchsess
 #
@@ -20,7 +21,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 17166 2018-08-18 19:40:56Z hexenmeister $
+# $Id: 00_MQTT.pm 17362 2018-09-17 12:57:29Z hexenmeister $
 #
 ##############################################
 

+ 26 - 8
fhem/core/FHEM/00_MQTT2_SERVER.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 00_MQTT2_SERVER.pm 17287 2018-09-06 08:24:44Z rudolfkoenig $
+# $Id: 00_MQTT2_SERVER.pm 17539 2018-10-15 18:47:53Z rudolfkoenig $
 package main;
 
 # TODO: test SSL
@@ -36,11 +36,13 @@ MQTT2_SERVER_Initialize($)
 
   no warnings 'qw';
   my @attrList = qw(
+    SSL:0,1
+    autocreate
     disable:0,1
     disabledForIntervals
-    autocreate
     rawEvents
-    SSL:0,1
+    sslVersion
+    sslCertPrefix
   );
   use warnings 'qw';
   $hash->{AttrList} = join(" ", @attrList);
@@ -319,6 +321,9 @@ MQTT2_SERVER_Read($@)
     MQTT2_SERVER_doPublish($hash, $defs{$sname}, $tp, $val, $cf & 0x01);
 
   ####################################
+  } elsif($cpt eq "PUBACK") { # ignore it
+
+  ####################################
   } elsif($cpt eq "SUBSCRIBE") {
     Log3 $sname, 4, "$cname $hash->{cid} $cpt";
     my $pid = unpack('n', substr($pl, 0, 2));
@@ -339,7 +344,7 @@ MQTT2_SERVER_Read($@)
         delete($hash->{answerScheduled});
         my $r = $defs{$sname}{retain};
         foreach my $tp (sort { $r->{$a}{ts} <=> $r->{$b}{ts} } keys %{$r}) {
-          MQTT2_SERVER_sendto($hash, $tp, $r->{$tp}{val});
+          MQTT2_SERVER_sendto($defs{$sname}, $hash, $tp, $r->{$tp}{val});
         }
       }, undef, 0);
     }
@@ -357,14 +362,17 @@ MQTT2_SERVER_Read($@)
     }
     addToWritebuffer($hash, pack("CCn", 0xb0, 2, $pid)); # UNSUBACK
 
+  ####################################
   } elsif($cpt eq "PINGREQ") {
     Log3 $sname, 4, "$cname $hash->{cid} $cpt";
     addToWritebuffer($hash, pack("C*", 0xd0, 0)); # pingresp
 
+  ####################################
   } elsif($cpt eq "DISCONNECT") {
     Log3 $sname, 4, "$cname $hash->{cid} $cpt";
     CommandDelete(undef, $cname);
 
+  ####################################
   } else {
     Log 1, "M2: Unhandled packet $cpt, disconneting $cname";
     CommandDelete(undef, $cname);
@@ -394,7 +402,8 @@ MQTT2_SERVER_doPublish($$$$;$)
   }
 
   foreach my $clName (keys %{$tgt->{clients}}) {
-    MQTT2_SERVER_sendto($defs{$clName}, $tp, $val) if($src->{NAME} ne $clName);
+    MQTT2_SERVER_sendto($tgt, $defs{$clName}, $tp, $val)
+        if($src->{NAME} ne $clName);
   }
 
   if(defined($src->{cid})) { # "real" MQTT client
@@ -410,9 +419,9 @@ MQTT2_SERVER_doPublish($$$$;$)
 ######################################
 # send topic to client if its subscription matches the topic
 sub
-MQTT2_SERVER_sendto($$$)
+MQTT2_SERVER_sendto($$$$)
 {
-  my ($hash, $topic, $val) = @_;
+  my ($shash, $hash, $topic, $val) = @_;
   return if(IsDisabled($hash->{NAME}));
   $val = "" if(!defined($val));
   foreach my $s (keys %{$hash->{subscriptions}}) {
@@ -420,6 +429,7 @@ MQTT2_SERVER_sendto($$$)
     $re =~ s,/?#,\\b.*,g;
     $re =~ s,\+,\\b[^/]+\\b,g;
     if($topic =~ m/^$re$/) {
+      Log3 $shash, 5, "$hash->{NAME} $hash->{cid} => $topic:$val";
       addToWritebuffer($hash,
         pack("C",0x30).
         MQTT2_SERVER_calcRemainingLength(2+length($topic)+length($val)).
@@ -519,7 +529,6 @@ MQTT2_SERVER_getStr($$)
     <ul>
     <li>to set user/password use an allowed instance and its basicAuth
       feature (set/attr)</li>
-    <li>retained messages are lost after a FHEM restart</li>
     <li>the retain flag is not propagated by publish</li>
     <li>only QOS 0 and 1 is implemented</li>
     </ul>
@@ -572,6 +581,15 @@ MQTT2_SERVER_getStr($$)
       Enable SSL (i.e. TLS)
       </li><br>
 
+    <li>sslVersion<br>
+       See the global attribute sslVersion.
+       </li><br>
+
+    <li>sslCertPrefix<br>
+       Set the prefix for the SSL certificate, default is certs/server-, see
+       also the SSL attribute.
+       </li><br>
+
     <a name="autocreate"></a>
     <li>autocreate<br>
       If set, MQTT2_DEVICES will be automatically created upon receiving an

+ 26 - 13
fhem/core/FHEM/01_FHEMWEB.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 01_FHEMWEB.pm 17307 2018-09-09 13:42:47Z rudolfkoenig $
+# $Id: 01_FHEMWEB.pm 17529 2018-10-14 12:57:06Z rudolfkoenig $
 package main;
 
 use strict;
@@ -187,9 +187,10 @@ FHEMWEB_Initialize($)
     refresh
     reverseLogs:0,1
     roomIcons
-    sortRooms
     showUsedFiles:0,1
+    sortRooms
     sslVersion
+    sslCertPrefix
     smallscreen:unused
     smallscreenCommands:0,1
     stylesheetPrefix
@@ -1276,6 +1277,7 @@ FW_makeTable($$$@)
           $v = $1;
         } else {
           $v = FW_htmlEscape($v);
+          $v = "<pre>$v</pre>" if($v =~ m/\n/);
         }
 
         my $ifid = "class='dval' informId='$name-$prefix$n'";
@@ -1305,7 +1307,7 @@ FW_makeTable($$$@)
           FW_pH "cmd=list%20TYPE=$val", $val,1;
 
         } else {
-           $val = "<pre>$val</pre>" if($val =~ m/\n/ && $title eq "Attributes");
+           $val = "<pre>$val</pre>" if($val =~ m/\n/);
            FW_pO "<td><div $tattr>".
                    join(",", map { ($_ ne $name && $defs{$_}) ?
                      FW_pH( "detail=$_", $_ ,0,"",1,1) : $_ } split(",",$val)).
@@ -1646,8 +1648,9 @@ FW_roomOverview($)
                         FW_makeImage($icoName,$icoName,"icon")."&nbsp;" : "";
 
         if($l1 eq "Save config") {
-          $l1 .= '</a> <a id="saveCheck" class="changed" style="visibility:'.
-                      (int(@structChangeHist) ? 'visible' : 'hidden').'">?';
+          $l1 .= '</span></a> '.
+                  '<a id="saveCheck" class="changed" style="visibility:'.
+                  (int(@structChangeHist) ? 'visible' : 'hidden').'"><span>?';
         }
 
         # Force external browser if FHEMWEB is installed as an offline app.
@@ -1911,6 +1914,7 @@ FW_showRoom()
   my %res;
   my ($idx,$svgIdx) = (1,1);
   @atEnds =  sort { $sortIndex{$a} cmp $sortIndex{$b} } @atEnds;
+  $FW_svgData{$FW_cname} = { FW_RET=>$FW_RET, RES=>\%res, ATENDS=>\@atEnds };
   foreach my $d (@atEnds) {
     no strict "refs";
     my $fn = $modules{$defs{$d}{TYPE}}{FW_summaryFn};
@@ -1934,11 +1938,7 @@ sub
 FW_svgDone($$$)
 {
   my ($res, $atEnds, $delayedReturn) = @_;
-
-  if(int(keys %{$res}) != int(@{$atEnds})) {
-    $FW_svgData{$FW_cname} = { FW_RET=>$FW_RET, RES=>$res, ATENDS=>$atEnds };
-    return -2 ;
-  }
+  return -2 if(int(keys %{$res}) != int(@{$atEnds}));
 
   foreach my $d (@{$atEnds}) {
     FW_pO $res->{$d};
@@ -2046,7 +2046,6 @@ sub
 FW_returnFileAsStream($$$$$)
 {
   my ($path, $suffix, $type, $doEsc, $cacheable) = @_;
-
   my $etag;
 
   if($cacheable) {
@@ -3740,7 +3739,8 @@ FW_widgetOverride($$)
         only the deviceName or have the form deviceName.event or deviceName.*.
         This is always the case when using the <a href="#plotEditor">Plot
         editor</a>. The SVG will be reloaded for <b>any</b> event triggered by
-        this deviceName. Default is off.
+        this deviceName. Default is off. Note: the plotEmbed attribute must be
+        set.
         </li>
         <br>
 
@@ -3912,6 +3912,12 @@ FW_widgetOverride($$)
        See the global attribute sslVersion.
        </li><br>
 
+    <a name="sslCertPrefix"></a>
+    <li>sslCertPrefix<br>
+       Set the prefix for the SSL certificate, default is certs/server-, see
+       also the HTTPS attribute.
+       </li><br>
+
     <a name="styleData"></a>
     <li>styleData<br>
       data-storage used by dynamic styles like f18
@@ -4444,7 +4450,8 @@ FW_widgetOverride($$)
         bzw. deviceName.*. Wenn man den <a href="#plotEditor">Plot Editor</a>
         benutzt, ist das &uuml;brigens immer der Fall. Die SVG Datei wird bei
         <b>jedem</b> ausl&ouml;senden Event dieses Ger&auml;tes neu geladen.
-        Die Voreinstellung ist aus.
+        Die Voreinstellung ist aus. Achtung: das plotEmbed Attribute muss
+        gesetzt sein.
         </li><br>
 
     <a name="mainInputLength"></a>
@@ -4613,6 +4620,12 @@ FW_widgetOverride($$)
       Siehe das global Attribut sslVersion.
       </li><br>
 
+    <a name="sslCertPrefix"></a>
+    <li>sslCertPrefix<br>
+       Setzt das Pr&auml;fix der SSL-Zertifikate, die Voreinstellung ist
+       certs/server-, siehe auch das HTTP Attribut.
+       </li><br>
+
     <a name="styleData"></a>
     <li>styleData<br>
       wird von dynamischen styles wie f18 werwendet

+ 22 - 16
fhem/core/FHEM/10_CUL_HM.pm

@@ -1,7 +1,7 @@
 ##############################################
 ##############################################
 # CUL HomeMatic handler
-# $Id: 10_CUL_HM.pm 17146 2018-08-15 15:02:59Z martinp876 $
+# $Id: 10_CUL_HM.pm 17532 2018-10-14 17:50:45Z martinp876 $
 
 package main;
 
@@ -3245,7 +3245,6 @@ sub CUL_HM_parseCommon(@){#####################################################
         $paired = 1;
       }
     }
-
     if($paired == 0 && CUL_HM_getRxType($mhp->{devH}) & 0x14){#no pair -send config?
       CUL_HM_appFromQ($mhp->{devN},"cf");   # stack cmds if waiting
       my $ioId = CUL_HM_h2IoId($mhp->{devH}{IODev});
@@ -3283,9 +3282,9 @@ sub CUL_HM_parseCommon(@){#####################################################
           (undef,@peers) = unpack 'A2(A8)*',$mhp->{p};
         }
 
-        $_ = '00000000' foreach (grep /^000000/,@peers);#correct bad term(6 chars) from rain sens)
-        $_ .= '0x' foreach (grep /^......$/,@peers);    #if channel is unknown we assume at least a device
-        $chnhash->{helper}{peerIDsRaw}.= ",".join",",@peers;
+        $_ = '00000000' foreach (grep /^000000/ ,@peers);#correct bad term(6 chars) from rain sens)
+        $_ .= '0x'      foreach (grep /^......$/,@peers);#if channel is unknown we assume at least a device
+        $chnhash->{helper}{peerIDsRaw} .= ",".join(",",@peers);
 
         CUL_HM_ID2PeerList ($chnName,$_,1) foreach (@peers);
         if (grep /00000000/,@peers) {# last entry, peerList is complete
@@ -3331,7 +3330,7 @@ sub CUL_HM_parseCommon(@){#####################################################
         ($format,$data) = ($1,$2) if ($mhp->{p} =~ m/^(..)(.*)/);
         my $list = $rspWait->{forList};
         $list = "00" if (!$list); #use the default
-        if ($format eq "02"){ # list 2: format aa:dd aa:dd ...
+        if    ($format eq "02"){ # list 2: format aa:dd aa:dd ...
           $data =~ s/(..)(..)/ $1:$2/g;
         }
         elsif ($format eq "03"){ # list 3: format aa:dddd
@@ -3363,13 +3362,14 @@ sub CUL_HM_parseCommon(@){#####################################################
         }
 
         if ($data =~ m/00:00$/){ # this was the last message in the block
+          my $peerId = CUL_HM_peerChId($peer,$mhp->{devH}{DEF});
           if($list eq "00"){
             push @evtEt,[$mhp->{devH},0,"PairedTo:".CUL_HM_getRegFromStore($mhp->{devN},"pairCentral",0,"")];
           }
           CUL_HM_respPendRm($mhp->{devH});
           delete $mhp->{cHash}{helper}{shadowReg}{$regLNp};   #rm shadow
           # peerChannel name from/for user entry. <IDorName> <deviceID> <ioID>
-          CUL_HM_updtRegDisp($mhp->{cHash},$list,$peer);
+          CUL_HM_updtRegDisp($mhp->{cHash},$list,$peerId);
         }
         else{
           CUL_HM_respPendToutProlong($mhp->{devH});#wasn't last - reschedule timer
@@ -3389,7 +3389,7 @@ sub CUL_HM_parseCommon(@){#####################################################
       my $peer = ($peerID ne "00000000") ? CUL_HM_peerChName($peerID,"000000") : "";
       
       if($data eq "00"){#update finished for mStp 05. Now update display
-        CUL_HM_updtRegDisp($fHash,$list,$peer);
+        CUL_HM_updtRegDisp($fHash,$list,$peerID);
       }
       else{
         my $regLNp = "RegL_".$list.".".$peer;
@@ -3422,7 +3422,7 @@ sub CUL_HM_parseCommon(@){#####################################################
             $shdwReg =~ s/ $a:..// if ($shdwReg);# confirmed: remove from shadow
           }
           CUL_HM_UpdtReadSingle($fHash,$regLN,$rCur,0);
-          CUL_HM_updtRegDisp($fHash,$list,$peer) if ($mhp->{mStp} eq "04");
+          CUL_HM_updtRegDisp($fHash,$list,$peerID) if ($mhp->{mStp} eq "04");
         }
       }
       $ret= "parsed"; # send ACK 
@@ -4143,7 +4143,7 @@ sub CUL_HM_Set($@) {#+++++++++++++++++ set command+++++++++++++++++++++++++++++
     if ($sectIn eq "all") {
       @sectL = ("rssi","msgEvents","readings","attack");#readings is last - it schedules a reread possible
     }
-    elsif($sectIn =~ m/(rssi|trigger|msgEvents|readings|oldRegs|register|unknownDev|attack)/){
+    elsif($sectIn =~ m/(rssi|trigger|msgEvents|msgErrors|readings|oldRegs|register|unknownDev|attack)/){
       @sectL = ($sectIn);
     }
     else{
@@ -4206,6 +4206,13 @@ sub CUL_HM_Set($@) {#+++++++++++++++++ set command+++++++++++++++++++++++++++++
         CUL_HM_unQEntity($name,"qReqStat");
         CUL_HM_protState($hash,"Info_Cleared");
       }
+      elsif($sect eq "msgErrors"){
+        delete $hash->{protResndFail};
+        delete $hash->{protResnd};
+        delete $hash->{protCmdDel};
+        delete $hash->{protNACK};
+        delete $hash->{protIOerr};
+      }
       elsif($sect eq "rssi"){
         delete $defs{$name}{helper}{rssi};
         delete ($hash->{$_}) foreach (grep(/^rssi/,keys %{$hash}))
@@ -8030,7 +8037,7 @@ sub CUL_HM_updtRegDisp($$$) {
   
   my $regLN = ($hash->{helper}{expert}{raw}?"":".")
               .sprintf("RegL_%02X.",$listNo)
-              .($peerId?CUL_HM_peerChName($peerId,$devId):"");
+              .($peerId ? CUL_HM_peerChName($peerId,$devId) : "");
   if (($md eq "HM-MOD-Re-8") && $listNo == 0){#handle Fw bug 
     CUL_HM_ModRe8($hash,$regLN);
   }
@@ -8721,7 +8728,6 @@ sub CUL_HM_ActCheck($) {# perform supervision
   my @event;
   my ($cntUnkn,$cntAliv,$cntDead,$cnt_Off) =(0,0,0,0);
   my $autoTry = CUL_HM_getAttrInt($actName,"actAutoTry",0);
-  
   foreach my $devId (split(",",$peerIDs)){
     next if (!$devId);
     my $devName = CUL_HM_id2Name($devId);
@@ -8751,7 +8757,7 @@ sub CUL_HM_ActCheck($) {# perform supervision
           || $tSince gt $tLast){   #no message received in window
         if ($actHash->{helper}{$devId}{start} lt $tSince){  
           if($autoTry) { #try to send a statusRequest?
-            if (!$actHash->{helper}{$devId}{try} || $actHash->{helper}{$devId}{try} < 2){
+            if (!$actHash->{helper}{$devId}{try} || $actHash->{helper}{$devId}{try} < 4){
               $actHash->{helper}{$devId}{try} = $actHash->{helper}{$devId}{try}
                                                  ? ($actHash->{helper}{$devId}{try} + 1)
                                                  : 1;
@@ -9465,7 +9471,7 @@ sub CUL_HM_reglUsed($) {# provide data for HMinfo
 
   my @pNames;
   push @pNames,CUL_HM_peerChName($_,$devId)
-             foreach (grep !/00000000/,split(",",AttrVal($name,"peerIDs","")));
+             foreach (grep !/(00000000|x)/,split(",",AttrVal($name,"peerIDs","")));#dont check 'x' peers
 
   my @lsNo;
   my $mId = CUL_HM_getMId($hash);
@@ -9991,7 +9997,7 @@ sub CUL_HM_tempListTmpl(@) { ##################################################
             set myChannel peerBulk 12345601 unset # remove peer 123456 channel 01<br>
           </code></ul>
         </li>
-        <li><B>regBulk  &lt;reg List&gt;:&lt;peer&gt; &lt;addr1:data1&gt; &lt;addr2:data2&gt;...</B><a name="CUL_HMregBulk"></a><br>
+        <li><B>regBulk  &lt;reg List&gt;.&lt;peer&gt; &lt;addr1:data1&gt; &lt;addr2:data2&gt;...</B><a name="CUL_HMregBulk"></a><br>
           This command will replace the former regRaw. It allows to set register
           in raw format. Its main purpose is to restore a complete register list
           to values secured before. <br>
@@ -11415,7 +11421,7 @@ sub CUL_HM_tempListTmpl(@) { ##################################################
             set myChannel peerBulk 12345601 unset # entferne Peer 123456 Kanal 01<br>
           </code></ul>
         </li>
-        <li><B>regBulk &lt;reg List&gt;:&lt;peer&gt; &lt;addr1:data1&gt; &lt;addr2:data2&gt;...</B><a name="CUL_HMregBulk"></a><br>
+        <li><B>regBulk &lt;reg List&gt;.&lt;peer&gt; &lt;addr1:data1&gt; &lt;addr2:data2&gt;...</B><a name="CUL_HMregBulk"></a><br>
           Dieser Befehl ersetzt das bisherige regRaw. Er erlaubt Register mit Rohdaten zu
           beschreiben. Hauptzweck ist das komplette Wiederherstellen eines zuvor gesicherten
           Registers. <br>

+ 57 - 29
fhem/core/FHEM/10_EQ3BT.pm

@@ -2,12 +2,15 @@
 #
 # EQ3BT.pm (c) by Dominik Karall, 2016-2018
 # dominik karall at gmail dot com
-# $Id: 10_EQ3BT.pm 16466 2018-03-22 04:18:29Z CoolTux $
+# $Id: 10_EQ3BT.pm 17484 2018-10-07 18:54:14Z dominik $
 #
 # FHEM module to communicate with EQ-3 Bluetooth thermostats
 #
 #############################################################
 #
+# v2.0.5 - 20181007
+# - BUGFIX:  ssh bugfixes by CoolTux
+#
 # v2.0.4 - 20180224
 # - FEATURE: support childlock
 #
@@ -156,7 +159,7 @@ sub EQ3BT_Initialize($) {
     $hash->{GetFn}    = 'EQ3BT_Get';
     $hash->{SetFn}    = 'EQ3BT_Set';
     $hash->{AttrFn}   = 'EQ3BT_Attribute';
-    $hash->{AttrList}  = 'sshHost maxRetries timeout '.
+    $hash->{AttrList}  = 'sshHost maxRetries timeout blockingCallLoglevel '.
                             $readingFnAttributes;
     
     return undef;
@@ -171,7 +174,8 @@ sub EQ3BT_Define($$) {
     my $sshHost;
     
     $hash->{STATE} = "initialized";
-    $hash->{VERSION} = "2.0.4";
+    $hash->{VERSION} = "2.0.5";
+    $hash->{loglevel} = 4;
     Log3 $hash, 3, "EQ3BT: EQ-3 Bluetooth Thermostat ".$hash->{VERSION};
     
     if (int(@a) > 4) {
@@ -235,12 +239,20 @@ sub EQ3BT_pairDevice {
 }
 
 sub EQ3BT_Attribute($$$$) {
-    my ($mode, $devName, $attrName, $attrValue) = @_;
+    my ( $cmd, $name, $attrName, $attrVal ) = @_;
+    my $hash                                = $defs{$name};
     
-    if($mode eq "set") {
-        
-    } elsif($mode eq "del") {
-        
+    if($cmd eq "set") {
+        if( $attrName eq "blockingCallLoglevel" ) {
+            $hash->{loglevel} = $attrVal;
+            Log3 $name, 3, "EQ3BT ($name) - set blockingCallLoglevel to $attrVal";
+        }
+    
+    } elsif($cmd eq "del") {
+        if( $attrName eq "blockingCallLoglevel" ) {
+            $hash->{loglevel} = 4;
+            Log3 $name, 3, "EQ3BT ($name) - set blockingCallLoglevel to $attrVal";
+        }
     }
     
     return undef;
@@ -499,15 +511,19 @@ sub EQ3BT_execGatttool($) {
     my ($name, $mac, $workType, $handle, $value, $listen) = split("\\|", $string);
     my $wait = 1;
     my $hash = $main::defs{$name};
+    my $sshHost     = AttrVal($name,"sshHost","none");
+    my $gatttool;   # = qx(which gatttool);
     
-    my $gatttool = qx(which gatttool);
+    $gatttool                               = qx(which gatttool) if($sshHost eq 'none');
+    $gatttool                               = qx(ssh $sshHost 'which gatttool') if($sshHost ne 'none');
     chomp $gatttool;
     
-    if(-x $gatttool) {
+    #if(-x $gatttool) {
+    if(defined($gatttool) and ($gatttool)) {
         my $gtResult;
         my $cmd;
-        my $sshHost     = AttrVal($name,"sshHost","none");
-
+        my $hciDevice = "hci".$hash->{helper}{hcidevices}[$hash->{helper}{currenthcidevice}];
+    
         while($wait) {
             my $grepGatttool = qx(ps ax| grep -E \'gatttool -b $mac\' | grep -v grep);
             if(not $grepGatttool =~ /^\s*$/) {
@@ -518,6 +534,18 @@ sub EQ3BT_execGatttool($) {
                 $wait = 0;
             }
         }
+        
+        
+        $cmd .= "ssh $sshHost '" if($sshHost ne 'none');
+        $cmd .= "timeout " . AttrVal($name, "timeout", 15) . " " if($listen);
+        $cmd .= "gatttool -i $hciDevice -b $mac ";
+        $cmd .= "--char-write-req -a $handle -n $value";
+        $cmd .= " --listen" if($listen);
+        $cmd .= " 2>&1 /dev/null";
+        $cmd .= "'" if($sshHost ne 'none');
+        
+        
+        
 
         if($value eq "03") {
             my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
@@ -525,24 +553,24 @@ sub EQ3BT_execGatttool($) {
             $value .= $currentDate;
         }
 
-        my $hciDevice = "hci".$hash->{helper}{hcidevices}[$hash->{helper}{currenthcidevice}];
-        #my $cmd = "gatttool -b $mac -i $hciDevice --char-write-req --handle=$handle --value=$value";
-        if( $sshHost ne 'none' ) {
-            $cmd = "ssh $sshHost 'gatttool -b $mac -i $hciDevice --char-write-req --handle=$handle --value=$value";
-        } else {
-            $cmd = "gatttool -b $mac -i $hciDevice --char-write-req --handle=$handle --value=$value";
-        }
-        
-        if(defined($listen) && $listen eq "listen") {
-            $cmd = "timeout ".AttrVal($name, "timeout", 15)." ".$cmd." --listen";
-        }
         
-        #redirect stderr to stdout
-        if( $sshHost ne 'none' ) {
-            $cmd .= " 2>&1'";
-        } else {
-            $cmd .= " 2>&1";
-        }
+        #my $cmd = "gatttool -b $mac -i $hciDevice --char-write-req --handle=$handle --value=$value";
+#         if( $sshHost ne 'none' ) {
+#             $cmd = "ssh $sshHost 'gatttool -b $mac -i $hciDevice --char-write-req --handle=$handle --value=$value";
+#         } else {
+#             $cmd = "gatttool -b $mac -i $hciDevice --char-write-req --handle=$handle --value=$value";
+#         }
+#         
+#         if(defined($listen) && $listen eq "listen") {
+#             $cmd = "timeout ".AttrVal($name, "timeout", 15)." ".$cmd." --listen";
+#         }
+#         
+#         #redirect stderr to stdout
+#         if( $sshHost ne 'none' ) {
+#             $cmd .= " 2>&1'";
+#         } else {
+#             $cmd .= " 2>&1";
+#         }
 
         Log3 $name, 5, "EQ3BT ($name): $cmd";
         $gtResult = qx($cmd);

+ 57 - 1
fhem/core/FHEM/10_EnOcean.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 10_EnOcean.pm 17260 2018-09-03 16:23:07Z klaus.schauer $
+# $Id: 10_EnOcean.pm 17353 2018-09-15 18:02:31Z klaus.schauer $
 
 package main;
 
@@ -353,6 +353,7 @@ my %EnO_eepConfig = (
   "D2.11.06" => {attr => {subType => "roomCtrlPanel.01", comMode => "biDir", webCmd => "setpointTemp"}, GPLOT => "EnO_D2-10-xx:Temp/SPT/Humi,"},
   "D2.11.07" => {attr => {subType => "roomCtrlPanel.01", comMode => "biDir", webCmd => "setpointTemp"}, GPLOT => "EnO_D2-10-xx:Temp/SPT/Humi,"},
   "D2.11.08" => {attr => {subType => "roomCtrlPanel.01", comMode => "biDir", webCmd => "setpointTemp"}, GPLOT => "EnO_D2-10-xx:Temp/SPT/Humi,"},
+  "D2.14.30" => {attr => {subType => "multiFuncSensor.30"}, GPLOT => "EnO_temp4humi4:Temp/Humi,"},
   "D2.20.00" => {attr => {subType => "fanCtrl.00", webCmd => "fanSpeed"}, GPLOT => "EnO_fanSpeed4humi4:FanSpeed/Humi,"},
   "D2.32.00" => {attr => {subType => "currentClamp.00"}, GPLOT => "EnO_D2-32-xx:Current,"},
   "D2.32.01" => {attr => {subType => "currentClamp.01"}, GPLOT => "EnO_D2-32-xx:Current,"},
@@ -10825,6 +10826,37 @@ sub EnOcean_Parse($$)
       }
       CommandDeleteReading(undef, "$name waitingCmds");
 
+    } elsif ($st eq "multiFuncSensor.30") {
+      # Sensor for Smoke, Air quality, Hygrothermal comfort, Temperature and Humidity
+      # (D2-14-30)
+      my @airQuality = ('optimal', 'air_dry', 'humidity_high', 'temperature_humidity_high', '', '', 'error');
+      my @comfort = ('good', 'medium', 'bad', 'error');
+      my @energyStorage = ('ok', 'medium', 'low', 'critical');
+      my $alarm = ($db[5] & 0x80) == 0 ? 'off' : 'smoke-alarm';
+      my $battery = $energyStorage[($db[4] & 6) >> 1];
+      if (!exists($hash->{helper}{lastAlarm}) || $hash->{helper}{lastAlarm} ne $alarm || ReadingsVal($name, 'alarm', '') eq 'dead_sensor') {
+        push @event, "3:alarm:" . $alarm;
+      }
+      if (!exists($hash->{helper}{lastBattery}) || $hash->{helper}{lastBattery} ne $battery) {
+        push @event, "3:battery:" . $battery;
+      }
+      push @event, "3:sensorFaultMode:" . (($db[5] & 0x40) == 0 ? 'off' : 'on');
+      push @event, "3:smokeAlarmMaintenance:" . (($db[5] & 0x20) == 0 ? 'ok' : 'not_done');
+      push @event, "3:smokeAlarmHumidity:" . (($db[5] & 0x10) == 0 ? 'ok' : 'not_ok');
+      push @event, "3:smokeAlarmTemperature:" . (($db[5] & 8) == 0 ? 'ok' : 'not_ok');
+      push @event, "3:maintenanceLast:" . (($db[5] & 7) << 5 | $db[4] >> 3);
+      push @event, "3:endOffLife:" . (($db[4] & 1) << 7 | $db[3] >> 1);
+      push @event, "3:temperature:" . sprintf "%0.1f", (($db[3] & 1) << 7 | $db[2] >> 1) / 5;
+      push @event, "3:humidity:" . sprintf "%0.1f", (($db[2] & 1) << 7 | $db[1] >> 1) / 2;
+      push @event, "3:hygrothermalComfort:" . $comfort[(($db[1] & 1) << 1 | $db[0] >> 7)];
+      push @event, "3:airQuality:" . $airQuality[($db[0] & 0x70) >> 4];
+      push @event, "3:state:" . $alarm;
+      $hash->{helper}{lastAlarm} = $alarm;
+      $hash->{helper}{lastBattery} = $battery;
+      RemoveInternalTimer($hash->{helper}{timer}{alarm}) if (exists $hash->{helper}{timer}{alarm});
+      @{$hash->{helper}{timer}{alarm}} = ($hash, 'alarm', 'dead_sensor', 1, 5);
+      InternalTimer(gettimeofday() + 1440, 'EnOcean_readingsSingleUpdate', $hash->{helper}{timer}{alarm}, 0);
+
     } elsif ($st eq "fanCtrl.00") {
       # Fan Control
       # (D2-20-00 - D2-20-02)
@@ -20466,6 +20498,30 @@ EnOcean_Delete($$)
      </li>
      <br><br>
 
+     <li>Sensor for Smoke, Air quality, Hygrothermal comfort, Temperature and Humidity (D2-14-30)<br>
+        [INSAFE+ Origin I870EO untested]<br>
+     <ul>
+       <li>off|smoke-alarm</li>
+       <li>airQuality: optimal|air_dry|humidity_high|teperature_humidity_high|error</li>
+       <li>alarm: off|smoke-alarm|dead_sensor</li>
+       <li>battery: ok|medium|low|critical</li>
+       <li>endOffLife: t/month (Range t = 0...120 month</li>
+       <li>humidity: rH/%</li>
+       <li>hygrothermalComfort: good|medium|bad|error</li>
+       <li>maintenanceLast: t/week (Range t = 0...250 week</li>
+       <li>sensorFaultMode: off|on</li>
+       <li>smokeAlarmHumidity: ok|not_ok</li>
+       <li>smokeAlarmMaintenance: ok|not_done</li>
+       <li>smokeAlarmTemperature: ok|not_ok</li>
+       <li>teach: &lt;result of teach procedure&gt;</li>
+       <li>temperature: t/&#176C (Sensor Range: t = 0 &#176C ... 50 &#176C)</li>
+       <li>state: off|smoke-alarm</li>
+     </ul><br>
+       The attr subType must be multiFuncSensor.30. This is done if the device was
+       created by autocreate.
+     </li>
+     <br><br>
+
      <li>Fan Control (D2-20-00 - D2-20-02)<br>
         [Maico ECA x RC/RCH, ER 100 RC, untested]<br>
      <ul>

+ 6 - 4
fhem/core/FHEM/10_IT.pm

@@ -6,7 +6,7 @@
 # 
 # Published under GNU GPL License
 #
-# $Id: 10_IT.pm 14852 2017-08-06 08:48:24Z bjoernh $
+# $Id: 10_IT.pm 17540 2018-10-15 19:00:42Z bjoernh $
 #
 ######################################################
 package main;
@@ -659,6 +659,7 @@ IT_Define($$)
 {
   my ($hash, $def) = @_;
   my @a = split("[ \t][ \t]*", $def);
+  my $ioname = $modules{IT}{defptr}{ioname};
 
   # calculate transmit code from IT A-P rotary switches
   if($a[2] =~ /^([A-O])(([0]{0,1}[1-9])|(1[0-6]))$/i) {
@@ -848,7 +849,7 @@ IT_Define($$)
   $hash->{CODE}{$ncode++} = $code;
   $modules{IT}{defptr}{$code}{$name}   = $hash;
   
-  AssignIoPort($hash);
+  AssignIoPort($hash, $ioname);
 }
 
 #############################
@@ -885,6 +886,7 @@ IT_Parse($$)
   my $def;
   my $newstate;
   my @list;
+  $modules{IT}{defptr}{ioname} = $ioname;
   if ((substr($msg, 0, 1)) ne 'i') {
     Log3 $hash,4,"$ioname IT: message not supported by IT \"$msg\"!";
     return undef;
@@ -1529,7 +1531,7 @@ Examples:
       - at the CUL can the ITclock found out from the raw messages (X31).
     </li><br>
     
-    <a name="ITfrequency"></a> </li>
+    <a name="ITfrequency"></a>
     <li>ITfrequency<br>
       Sets the frequency of the sender.
     </li><br>
@@ -1816,7 +1818,7 @@ Beispiele:
       - Beim CUL kann die ITclock aus den raw Daten (X31) ermittelt werden.
     </li><br>
       
-    <a name="ITfrequency"></a> </li>
+    <a name="ITfrequency"></a>
     <li>ITfrequency<br>
       Setzt die Sendefrequenz.
     </li><br>

+ 45 - 20
fhem/core/FHEM/10_KNX.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 10_KNX.pm 17174 2018-08-19 12:45:19Z andi291 $
+# $Id: 10_KNX.pm 17471 2018-10-07 06:17:05Z andi291 $
 # ABU 20180218 restructuring, removed older documentation
 # ABU 20180317 setExtensions reingebaut, set funktion
 # ABU 20180319 repaired "reply"-function
@@ -31,6 +31,11 @@
 # ABU 20180706 changed eval, removed stateCopy
 # ABU 20180706 fixed doku: changed readonly in listenonly
 # ABU 20180815 updated link in doku, changed (dpt16$) to dpt16 in set, tried to fix öast-sender (replaced bulk by single in decoding loop)
+# ABU 20180829 added dpt9.0020, tried workaround in putCmd, remove non printable chars
+# ABU 20180925 added dpt3.007, added last-sender "fhem"
+# ABU 20180926 fixed KNX_Eval in line 1291 (replaced hash by deviceHash), fixed decoding dpt3
+# ABU 20181007 fixed dpt19
+
 
 package main;
 
@@ -133,6 +138,7 @@ my %dpttypes = (
 	  
 	#Step value (four-bit)
 	"dpt3" 			=> {CODE=>"dpt3", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/i, MIN=>-100, MAX=>100},
+	"dpt3.007" 		=> {CODE=>"dpt3", UNIT=>"%", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/i, MIN=>-100, MAX=>100},	
 
 	# 1-Octet unsigned value
 	"dpt5" 			=> {CODE=>"dpt5", UNIT=>"", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/i, MIN=>0, MAX=>255},
@@ -169,6 +175,7 @@ my %dpttypes = (
 	"dpt9.008"	 	=> {CODE=>"dpt9", UNIT=>"ppm", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},	
 	"dpt9.009"	 	=> {CODE=>"dpt9", UNIT=>"m&sup3/h", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},	
 	"dpt9.010"	 	=> {CODE=>"dpt9", UNIT=>"s", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},	
+	"dpt9.020"		=> {CODE=>"dpt9", UNIT=>"mV", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},
 	"dpt9.021"	 	=> {CODE=>"dpt9", UNIT=>"mA", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},		
 	"dpt9.024"	 	=> {CODE=>"dpt9", UNIT=>"kW", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},	
 	"dpt9.025"	 	=> {CODE=>"dpt9", UNIT=>"l/h", FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/i, MIN=>-670760, MAX=>670760},	
@@ -996,6 +1003,7 @@ KNX_Set($@) {
 		}
 		
 		readingsBulkUpdate($hash, "state", $state);
+		readingsBulkUpdate($hash, "last-sender", "fhem");
 		readingsEndUpdate($hash, 1);
 	}							
 	
@@ -1282,9 +1290,11 @@ KNX_Parse($$) {
 			{			
 				my $orgValue = $value;
 
-				$value = KNX_eval ($hash, $gadName, $value, $cmdAttr);
+				$value = KNX_eval ($deviceHash, $gadName, $value, $cmdAttr);
 				
-				if ($orgValue ne $value)
+				#if ($orgValue ne $value)
+				#try to fix: answer only, if eval was successful
+				if (($orgValue ne $value) and ($value !~ m/ERROR/i))
 				{
 					Log3 ($deviceName, 5, "parse device hash (r): $deviceHash name: $deviceName - put replaced via command $cmdAttr - value: $value");
 					readingsSingleUpdate($deviceHash, $putString, $value,1);								
@@ -1804,12 +1814,13 @@ KNX_encodeByDpt ($$$) {
 			my $hoffset;
 			
 			#add offsets
+			#$year+=1900;
 			$mon++;	
 			# calculate offset for weekday
 			$wday = 7 if ($wday eq "0");
 			
 			$hexval = 0;
-			$hexval = sprintf ("00%.8x", (($secs<<16) + ($mins<<24) + ($hours<<32) + ($wday<<37) + ($mday<<40) + ($mon<<48) + ($year<<56)));
+			$hexval = sprintf ("00%.16x", (($secs<<16) + ($mins<<24) + ($hours<<32) + ($mday<<40) + ($mon<<48) + ($year<<56)));
 			
 		} else
 		{
@@ -1822,7 +1833,7 @@ KNX_encodeByDpt ($$$) {
 			my $wday = 0;
 			
 			$hexval = 0;
-			$hexval = sprintf ("00%.8x", (($ss<<16) + ($mi<<24) + ($hh<<32) + ($wday<<37) + ($dd<<40) + ($mm<<48) + ($yyyy<<56)));
+			$hexval = sprintf ("00%.16x", (($ss<<16) + ($mi<<24) + ($hh<<32) + ($dd<<40) + ($mm<<48) + ($yyyy<<56)));
 		}
 		$numval = 0;
 	}	
@@ -1892,16 +1903,23 @@ KNX_decodeByDpt ($$$) {
 		#get numeric value
 		$numval = hex ($value);
 
-		$state = 1 if ($numval & 7);
-		$state = 3 if ($numval & 6);
-		$state = 6 if ($numval & 5);
-		$state = 12 if ($numval & 4);
-		$state = 25 if ($numval & 3);
-		$state = 50 if ($numval & 2);
-		$state = 100 if ($numval & 1);
-				
 		#get dim-direction
-		$state = 0 - $state if (not ($numval & 8));
+		my $sign = 1;
+		if ($numval >= 8)
+		{
+			$sign = 0;
+			$numval -= 8;
+		}
+
+		$state = 100 if ($numval >= 1);
+		$state = 50 if ($numval >= 2);
+		$state = 25 if ($numval >= 3);
+		$state = 12 if ($numval >= 4);
+		$state = 6 if ($numval >= 5);
+		$state = 3 if ($numval >= 6);
+		$state = 1 if ($numval >= 7);
+				
+		$state = 0 - $state if ($sign == 1);
 		
 		$state = sprintf ("%.0f", $state);
 	}
@@ -2024,20 +2042,26 @@ KNX_decodeByDpt ($$$) {
 		}
 
 		#convert to latin-1
-		$state = encode ("utf8", $state) if ($model =~ m/16.001/);		
+		$state = encode ("utf8", $state) if ($model =~ m/16.001/);
+
+		#remove non printable chars
+		$state =~ s/[\x00-\x1F]+//g;	
 	}
 	#DateTime
 	elsif ($code eq "dpt19")
 	{
 		$numval = $value;
-		my $time = hex (substr ($value, 6, 6));
-		my $date = hex (substr ($value, 0, 6));
+		my $time = hex (substr ($numval, 8, 6));
+		my $date = hex (substr ($numval, 2, 6));
 		my $secs  = ($time & 0x3F) >> 0;
 		my $mins  = ($time & 0x3F00) >> 8;
 		my $hours = ($time & 0x1F0000) >> 16;
-		my $day   = ($date & 0x1F) >> 0;
-		my $month = ($date & 0x0F00) >> 8;
-		my $year  = ($date & 0xFFFF0000) >> 16;		
+		#my $day   = ($date & 0x1F) >> 0;
+		#my $month = ($date & 0x0F00) >> 8;
+		#my $year  = ($date & 0xFFFF0000) >> 16;		
+		my $day   = ($date & 0xFF) >> 0;
+		my $month = ($date & 0xFF00) >> 8;
+		my $year  = ($date & 0xFF0000) >> 16;		
 		
 		$year += 1900;
 		$state = sprintf("%02d.%02d.%04d_%02d:%02d:%02d", $day, $month, $year, $hours, $mins, $secs);	
@@ -2205,6 +2229,7 @@ The answer from the bus-device is not shown in the toolbox, but is treated like
 <ul>dpt1.023 move up/down, move and step mode</ul>
 <ul>dpt2 on, off, forceOn, forceOff</ul>
 <ul>dpt3 -100..+100</ul>
+<ul>dpt3.007 -100..+100 %</ul>
 <ul>dpt5 0..255</ul>
 <ul>dpt5.001 0..100 %</ul>
 <ul>dpt5.003 0..360 &deg;</ul>

+ 67 - 43
fhem/core/FHEM/10_MQTT2_DEVICE.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 10_MQTT2_DEVICE.pm 17289 2018-09-06 08:26:05Z rudolfkoenig $
+# $Id: 10_MQTT2_DEVICE.pm 17487 2018-10-08 07:38:31Z rudolfkoenig $
 package main;
 
 use strict;
@@ -153,12 +153,59 @@ MQTT2_DEVICE_Parse($$)
   return keys %fnd;
 }
 
+# compatibility: the first version was implemented as MQTT2_JSON and published.
 sub
 MQTT2_JSON($;$)
 {
   return json2nameValue($_[0], $_[1]);
 }
 
+sub
+MQTT2_getCmdHash($)
+{
+  my ($list) = @_;
+  my (%h, @cmd);
+  map { 
+    my ($k,$v) = split(" ",$_,2);
+    push @cmd, $k;
+    $k =~ s/:.*//; # potential arguments
+    $h{$k} = $v;
+  }
+  grep /./,
+  split("\n", $list);
+  return (\%h, join(" ",@cmd));
+}
+
+#############################
+# replace {} and $EVENT. Used both in set and get
+sub
+MQTT2_buildCmd($$$)
+{
+  my ($hash, $a, $cmd) = @_;
+
+  shift @{$a};
+  if($cmd =~ m/^{.*}$/) {
+    $cmd = EvalSpecials($cmd, ("%EVENT"=>join(" ",@{$a}), "%NAME"=>$hash->{NAME}));
+    $cmd = AnalyzeCommandChain($hash->{CL}, $cmd);
+    return if(!$cmd);
+
+  } else {
+    if($cmd =~ m/\$EV/) {       # replace EVENT & $EVTPART
+      my $event = join(" ",@{$a});
+      $cmd =~ s/\$EVENT/$event/g;
+      for(my $i=0; $i<@{$a}; $i++) {
+        my $n = "\\\$EVTPART$i";
+        $cmd =~ s/$n/$a->[$i]/ge;
+      }
+    } else {
+      shift @{$a};
+      $cmd .= " ".join(" ",@{$a}) if(@{$a});
+    }
+  }
+
+  $cmd =~ s/\$DEVICETOPIC/$hash->{DEVICETOPIC}/g;
+  return $cmd;
+}
 
 #############################
 sub
@@ -167,15 +214,11 @@ MQTT2_DEVICE_Get($@)
   my ($hash, @a) = @_;
   return "Not enough arguments for get" if(!defined($a[1]));
 
-  my %gets;
-  map {  my ($k,$v) = split(" ",$_,2); $gets{$k} = $v; }
-        grep /./,
-        split("\n", AttrVal($hash->{NAME}, "getList", ""));
-  return "Unknown argument $a[1], choose one of ".join(" ",sort keys %gets)
-        if(!$gets{$a[1]});
+  my ($gets,$cmdList) = MQTT2_getCmdHash(AttrVal($hash->{NAME}, "getList", ""));
+  return "Unknown argument $a[1], choose one of $cmdList" if(!$gets->{$a[1]});
   return undef if(IsDisabled($hash->{NAME}));
 
-  my ($getReading, $cmd) = split(" ",$gets{$a[1]},2);
+  my ($getReading, $cmd) = split(" ",$gets->{$a[1]},2);
   if($hash->{CL}) {
     my $tHash = { hash=>$hash, CL=>$hash->{CL}, reading=>$getReading };
     $hash->{asyncGet} = $tHash;
@@ -185,18 +228,10 @@ MQTT2_DEVICE_Get($@)
     }, $tHash, 0);
   }
 
-  shift @a;
-  if($cmd =~ m/^{.*}$/) {
-    $cmd = EvalSpecials($cmd, ("%EVENT"=>join(" ",@a), "%NAME"=>$hash->{NAME}));
-    $cmd = AnalyzeCommandChain($hash->{CL}, $cmd);
-    return if(!$cmd);
-  } else {
-    shift @a;
-    $cmd .= " ".join(" ",@a) if(@a);
-  }
-
-  $cmd =~ s/\$DEVICETOPIC/$hash->{DEVICETOPIC}/g;
+  $cmd = MQTT2_buildCmd($hash, \@a, $cmd);
+  return if(!$cmd);
   IOWrite($hash, split(" ",$cmd,2));
+
   return undef;
 }
 
@@ -207,27 +242,14 @@ MQTT2_DEVICE_Set($@)
   my ($hash, @a) = @_;
   return "Not enough arguments for set" if(!defined($a[1]));
 
-  my %sets;
-  map {  my ($k,$v) = split(" ",$_,2); $sets{$k} = $v; }
-        grep /./,
-        split("\n", AttrVal($hash->{NAME}, "setList", ""));
+  my ($sets,$cmdList) = MQTT2_getCmdHash(AttrVal($hash->{NAME}, "setList", ""));
   my $cmdName = $a[1];
-  my $cmd = $sets{$cmdName};
-  return SetExtensions($hash, join(" ", sort keys %sets), @a) if(!$cmd);
+  my $cmd = $sets->{$cmdName};
+  return SetExtensions($hash, $cmdList, @a) if(!$cmd);
   return undef if(IsDisabled($hash->{NAME}));
 
-  shift @a;
-  if($cmd =~ m/^{.*}$/) {
-    my $NAME = $hash->{NAME};
-    $cmd = EvalSpecials($cmd, ("%EVENT"=>join(" ",@a), "%NAME"=>$hash->{NAME}));
-    $cmd = AnalyzeCommandChain($hash->{CL}, $cmd);
-    return if(!$cmd);
-  } else {
-    shift @a;
-    $cmd .= " ".join(" ",@a) if(@a);
-  }
-
-  $cmd =~ s/\$DEVICETOPIC/$hash->{DEVICETOPIC}/g;
+  $cmd = MQTT2_buildCmd($hash, \@a, $cmd);
+  return if(!$cmd);
   IOWrite($hash, split(" ",$cmd,2));
   readingsSingleUpdate($hash, "state", $cmdName, 1);
   return undef;
@@ -430,9 +452,10 @@ MQTT2_DEVICE_Undef($$)
       <ul>
         <li>arguments to the set command will be appended to the message
           published (not for the perl expression)</li>
-        <li>if using a perl expressions, the command arguments are available as
-          $EVENT, $EVTPART0, etc. The perl expression must return a string
-          containing the topic and the message separated by a space.</li>
+        <li>the command arguments are available as $EVENT, $EVTPART0, etc.,
+          bot in the perl expression and the "normal" topic variant.</li>
+        <li>the perl expression must return a string containing the topic and
+          the message separated by a space.</li>
         <li>SetExtensions is activated</li>
         <li>if the topic name ends with :r, then the retain flag is set</li>
       </ul>
@@ -460,9 +483,10 @@ MQTT2_DEVICE_Undef($$)
           Use a set and a notify/DOIF/etc definition for such a purpose</li>
         <li>arguments to the get command will be appended to the message
           published (not for the perl expression)</li>
-        <li>if using a perl expressions, the command arguments are available as
-          $EVENT, $EVTPART0, etc. The perl expression must return a string
-          containing the topic and the message separated by a space.</li>
+        <li>the command arguments are available as $EVENT, $EVTPART0, etc.
+          </li>
+        <li>the perl expression must return a string containing the topic and
+          the message separated by a space.</li>
       </ul>
       </li><br>
 

+ 2 - 1
fhem/core/FHEM/10_MQTT_BRIDGE.pm

@@ -2,6 +2,7 @@
 #
 # fhem bridge to mqtt (see http://mqtt.org)
 #
+# Copyright (C) 2018 Alexander Schulz
 # Copyright (C) 2017 Stephan Eisler
 # Copyright (C) 2014 - 2016 Norbert Truchsess
 #
@@ -20,7 +21,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_BRIDGE.pm 16674 2018-04-29 12:53:05Z eisler $
+# $Id: 10_MQTT_BRIDGE.pm 17362 2018-09-17 12:57:29Z hexenmeister $
 #
 ##############################################
 

+ 2 - 1
fhem/core/FHEM/10_MQTT_DEVICE.pm

@@ -2,6 +2,7 @@
 #
 # fhem bridge to mqtt (see http://mqtt.org)
 #
+# Copyright (C) 2018 Alexander Schulz
 # Copyright (C) 2017 Stephan Eisler
 # Copyright (C) 2014 - 2016 Norbert Truchsess
 #
@@ -20,7 +21,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 16674 2018-04-29 12:53:05Z eisler $
+# $Id: 10_MQTT_DEVICE.pm 17362 2018-09-17 12:57:29Z hexenmeister $
 #
 ##############################################
 

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1068 - 252
fhem/core/FHEM/10_MQTT_GENERIC_BRIDGE.pm


+ 15 - 11
fhem/core/FHEM/12_HProtocolGateway.pm

@@ -1,4 +1,4 @@
-# $Id: 12_HProtocolGateway.pm 17337 2018-09-13 15:15:11Z eisler $
+# $Id: 12_HProtocolGateway.pm 17509 2018-10-11 13:53:45Z eisler $
 ####################################################################################################
 #
 #	12_HProtocolGateway.pm
@@ -28,6 +28,7 @@ package main;
 use strict;
 use warnings;
 use DevIo;
+use Math::Round qw/round/;
 
 my @tankList = undef;
 
@@ -48,7 +49,7 @@ sub HProtocolGateway_Initialize($) {
                       "baudrate:300,600,1200,2400,4800,9600 " .
                       "parityBit:N,E,O " .
                       "databitsLength:5,6,7,8 " .
-                      "stopBit:0,1" .
+                      "stopBit:0,1 " .
                       "pollIntervalMins " .
                       "path";
 }
@@ -192,8 +193,8 @@ sub HProtocolGateway_ParseMessage($$) {
     # convert to HEX
     $check = sprintf '%02X', $check;
     
-    # Unitronics Vision130
-    if ($water == 0 && $temperature == 0 && $probe_offset == 0 && $version == 0 && $error == 0 && $checksum == 0 ) {
+    # Unitronics
+    if ($version == 0 && $error == 0 && $checksum == 0) {
       $check = 0;
     }
 
@@ -203,7 +204,7 @@ sub HProtocolGateway_ParseMessage($$) {
     my $mode = AttrVal($tankHash->{NAME},"mode","");
 
     if ($mode eq "FillLevel") {
-      $filllevel = $tankdata;
+      $filllevel = $tankdata/100;
       $volume = HProtocolGateway_Tank($hash,$tankHash,$filllevel);
     } elsif ($mode eq "Volume") {
       $volume = $tankdata;
@@ -221,8 +222,8 @@ sub HProtocolGateway_ParseMessage($$) {
     if ($sign eq "-") { $probe_offset = int($probe_offset) * -1 };
   
     my $volume_15C = $volume * (1 + 0.00084 * ( 15 - $temperature ));
-    $volume_15C = int($volume_15C); 
-
+    $volume_15C = Math::Round::nearest('0.01',$volume_15C);
+    
     # Update all received readings
     HProtocolGateway_UpdateTankDevice($hash, $tankHash->{NAME}, "ullage", $ullage);
     HProtocolGateway_UpdateTankDevice($hash, $tankHash->{NAME}, "filllevel", $filllevel);
@@ -363,14 +364,17 @@ sub HProtocolGateway_Tank($$$) {
   }
   close $fh;
 
-  my $messwert = $filllevel/10;
   my $volume = 0;
-
+  my $volume1 = 0;
+  my $level1 = 0;
   foreach my $level (sort keys %TankChartHash) {
-    if ($level ne "level" && $messwert <= $level) {
-	    $volume = $TankChartHash{$level};
+    if ($level ne "level" && $filllevel <= $level) {
+      $volume = $volume1 + ($TankChartHash{$level} - $volume1) / ($level - $level1) * ($filllevel-$level1);
+      $volume = Math::Round::nearest('0.01',$volume);
       last;
     }
+    $level1 = $level;
+    $volume1 = $TankChartHash{$level};
   }
   return $volume;
 }

+ 2 - 2
fhem/core/FHEM/12_HProtocolTank.pm

@@ -1,4 +1,4 @@
-# $Id: 12_HProtocolTank.pm 17337 2018-09-13 15:15:11Z eisler $
+# $Id: 12_HProtocolTank.pm 17508 2018-10-11 12:41:53Z eisler $
 ####################################################################################################
 #
 #	12_HProtocolTank.pm
@@ -149,7 +149,7 @@ sub HProtocolTank_Attr (@) {
     <li>waterlevel<br />
     0..9999 Water level in mm</li>
     <li>probe_offset<br />
-    -9999 - +9999 Probe offset in mm)</li>
+    -9999 - +9999 Probe offset in mm</li>
     <li>version<br />
     00..999 Software version</li>
     <li>error<br />

+ 117 - 58
fhem/core/FHEM/19_Revolt.pm

@@ -1,8 +1,9 @@
-##############################################
-#                                            #
-# Written by Martin Paulat, 2013             #
-#                                            #
-##############################################
+#                                            
+# Written by Martin Paulat, 2013             
+# mainted by Gernot Hillier (yoda_gh), 2018- 
+#            <gernot@hillier.de>             
+#                                           
+# $Id: 19_Revolt.pm 17447 2018-10-01 19:13:48Z yoda_gh $
 
 package main;
 
@@ -13,53 +14,52 @@ use Date::Parse;
 
 
 #####################################
-sub
-Revolt_Initialize($)
+sub Revolt_Initialize($)
 {
   my ($hash) = @_;
 
-#                        r00C5E100303203C85921FF
   $hash->{Match}     = "^r......................\$";
   $hash->{DefFn}     = "Revolt_Define";
   $hash->{UndefFn}   = "Revolt_Undef";
   $hash->{ParseFn}   = "Revolt_Parse";
   $hash->{AttrList}  = "IODev ".
+                       "EnergyAdjustValue ".
                        $readingFnAttributes;
 }
 
 #####################################
-sub
-Revolt_Define($$)
+sub Revolt_Define($$)
 {
   my ($hash, $def) = @_;
   my @a = split("[ \t][ \t]*", $def);
 
   return "wrong syntax: define <name> Revolt <id>" if(int(@a) != 3);
   $a[2] = lc($a[2]);
-  return "Define $a[0]: wrong <id> format: specify a 4 digit hex value"
-  		if($a[2] !~ m/^[a-f0-9][a-f0-9][a-f0-9][a-f0-9]$/);
+  return "Define $a[0]: wrong <id> format: specify a 4 digit hex value"	if($a[2] !~ m/^[a-f0-9][a-f0-9][a-f0-9][a-f0-9]$/);
 
   $hash->{ID} = $a[2];
   #$hash->{STATE} = "Initialized";
   $modules{REVOLT}{defptr}{$a[2]} = $hash;
   AssignIoPort($hash);
+  
+  my $name = $a[0]; 
+  #$attr{$name}{"event-aggregator"} = "power::none:median:120,energy::none:median:120" if(!defined($attr{$name}{"event-aggregator"}));
+  $attr{$name}{"stateFormat"} = "P: power E: energy V: voltage C: current Pf: pf" if(!defined($attr{$name}{"stateFormat"}));
+  
   return undef;
 }
 
 #####################################
-sub
-Revolt_Undef($$)
+sub Revolt_Undef($$)
 {
   my ($hash, $name) = @_;
-  delete($modules{REVOLT}{defptr}{$hash->{ID}})
-        if(defined($hash->{ID}) &&
-           defined($modules{REVOLT}{defptr}{$hash->{ID}}));
+  delete($modules{REVOLT}{defptr}{$hash->{ID}}) if(defined($hash->{ID}) &&
+                                                   defined($modules{REVOLT}{defptr}{$hash->{ID}}));
   return undef;
 }
 
 #####################################
-sub
-Revolt_Parse($$)
+sub Revolt_Parse($$)
 {
   my ($hash, $msg) = @_;
 
@@ -70,15 +70,14 @@ Revolt_Parse($$)
   my $val = substr($msg, 11, 22);
   my $id       = substr($msg, 1, 4);
   my $voltage  = hex(substr($msg, 5, 2));
-  my $current  = hex(substr($msg, 7, 4))*0.01;
+  my $current  = hex(substr($msg, 7, 4)) * 0.01;
   my $freq     = hex(substr($msg, 11, 2));
-  my $power    = hex(substr($msg, 13, 4))*0.1;
-  my $pf       = hex(substr($msg, 17, 2))*0.01;
-  my $energy   = hex(substr($msg, 19, 4))*0.01;
-  my $lastval;
-  my $avg;
-  
+  my $power    = hex(substr($msg, 13, 4)) * 0.1;
+  my $pf       = hex(substr($msg, 17, 2)) * 0.01;
+  my $energy   = hex(substr($msg, 19, 4)) * 0.01;
+  my $lastval  = 0.0;
   my $type = "";
+  my $energyAdj = $energy;
   
   if(!defined($modules{REVOLT}{defptr}{$id})) {
     Log3 undef,3, "Unknown Revolt device $id, please define it";
@@ -90,38 +89,63 @@ Revolt_Parse($$)
   my $name = $def->{NAME};
   return "" if(IsIgnored($name));
   
-  my $state;
-  $state="P: ".sprintf("%5.1f",$power)." E: ".sprintf("%6.2f",$energy)." V: ".sprintf("%3d",$voltage)." C: ".sprintf("%6.2f",$current)." F: $freq Pf: ".sprintf("%4.2f",$pf);
+  # check if data is invalid
+  if (defined($def->{READINGS}{".lastenergy"})) {
+    $lastval = $def->{READINGS}{".lastenergy"}{VAL};
+  } 
+  else {
+    readingsSingleUpdate($def,".lastenergy", $energy, 1);
+  }
   
-  readingsBeginUpdate($def);
+  # adjust energy value
+  $energy -= AttrVal($name, "EnergyAdjustValue", 0);
   
-  if (defined($def->{READINGS}{".lastenergy"})) {
-    $lastval=$def->{READINGS}{".lastenergy"}{VAL};
-    if ($lastval != $energy) {
-      $avg=(($lastval-$energy)*1000.0*3600.0)/(str2time($def->{READINGS}{".lastenergy"}{TIME})-gettimeofday());
-      readingsBulkUpdate($def,".lastenergy", $energy,1);
-      readingsBulkUpdate($def,"avgpower", sprintf("%.2f",$avg),1);
-    }
-  } else {
-    readingsBulkUpdate($def,".lastenergy", $energy,1);
+  my $isInvalid = 0;
+  #my $energydiff = 0;
+  #my $maxenergy = 0;
+
+  #TODO: This plausability check will stop accepting values forever after 
+  #receiving a very small outlier once. We could use abs(...), but what about
+  #changing energyadjustvalue etc.? Do we really need this at all?
+  #
+  #if (defined($def->{READINGS}{"energy"})) {
+  # my $timediff = gettimeofday() - str2time($def->{READINGS}{"energy"}{TIME});
+  #  $energydiff = $energy - $def->{READINGS}{"energy"}{VAL};
+  #  $maxenergy = 3.65 * ($timediff / 3600.0);
+  #}
+  #if ($energydiff > $maxenergy) {
+  #  $isInvalid = 1;
+  #}
+
+  if (0 == $pf) {
+    $pf = 0.0001;
+  }
+  # plausability check partly taken from http://www.sknorrell.de/blog/energiemesssung-mit-revolt-nc-5462/
+  if (($voltage < 80) || ($freq > 65) || ($power > 3650) || ($current > 16) ||
+      ((($power / $voltage / $pf) > 0.00999) && (0 == $current))) {
+    $isInvalid = 1;
   }
 
-  readingsBulkUpdate($def,"state", $state,1);
-  Log3  $name,4, "$name: $state";
-  readingsBulkUpdate($def,"voltage", $voltage,1);
-  #Log3  $def,3, "$name:voltage $voltage";
-  readingsBulkUpdate($def,"current", $current,1);
-  #Log3  $def,3, "$name:current $current";
-  readingsBulkUpdate($def,"frequency", $freq,1);
-  #Log3  $def,3, "$name:frequency $freq";
-  readingsBulkUpdate($def,"power", $power,1);
-  #Log3  $def,3, "$name:power $power";
-  readingsBulkUpdate($def,"pf", $pf,1);
-  #Log3  $def,3, "$name:Pf $pf";
-  readingsBulkUpdate($def,"energy", $energy,1);
-  #Log3  $def,3, "$name:energy $energy";
-  
-  readingsEndUpdate($def, 1);
+  if (0 == $isInvalid) {
+    #my $state = "P: ".sprintf("%5.1f", $power)." E: ".sprintf("%6.2f", $energy)." V: ".sprintf("%3d", $voltage)." C: ".sprintf("%6.2f", $current)." F: $freq Pf: ".sprintf("%4.2f", $pf);
+
+    readingsBeginUpdate($def);
+
+    my $timediff = gettimeofday() - str2time($def->{READINGS}{".lastenergy"}{TIME});
+    if (($lastval != $energy) && (($energy - $lastval) < (3.65 * ($timediff / 3600.0)))) {
+        readingsBulkUpdate($def, ".lastenergy", $energy, 1);
+    }
+
+    readingsBulkUpdate($def, "state", "active",  0);
+    readingsBulkUpdate($def, "voltage", $voltage, 1);
+    readingsBulkUpdate($def, "current", $current, 1);
+    readingsBulkUpdate($def, "frequency", $freq, 1);
+    readingsBulkUpdate($def, "power", $power, 1);
+    readingsBulkUpdate($def, "pf", $pf, 1);
+    readingsBulkUpdate($def, "energy", $energy, 1);
+
+    readingsEndUpdate($def, 1);
+  }
 
   return $name;
 }
@@ -129,12 +153,26 @@ Revolt_Parse($$)
 1;
 
 =pod
+=item device
+=item summary Receive power, energy, voltage etc. from Revolt NC-5462 433MHz devices
+=item summary_DE Energieverbrauch von Revolt NC-5462 über 433MHz empfangen
 =begin html
 
 <a name="Revolt"></a>
 <h3>Revolt NC-5462</h3>
 <ul>
-  Provides voltage, current, frequency, power, pf, energy readings for Revolt NC-5462 devices via CUL.
+  Provides energy consumption readings for Revolt NC-5462 devices via CUL or
+  SIGNALduino (433MHz).
+  <br><br>
+  These devices send telegrams with measured values every few seconds. Many 
+  users saw problems with cheap 433MHz receivers - and even with good 
+  receivers, wrong values are received quite often, leading to outliers
+  in plots and sporadic autocreation of devices with wrong IDs.
+  <br><br>
+  This module now ignores implausible telegrams (e.g. voltage below 80),
+  but to further reduce outliers and event frequency, you should use the common
+  attributes described below. You also might want to disable autocreation of
+  Revolt devices once all of yours are detected.
   <br><br>
 
   <a name="RevoltDefine"></a>
@@ -146,17 +184,38 @@ Revolt_Parse($$)
     Note: devices are autocreated on reception of the first message.<br>
   </ul>
   <br>
+  <a name="RevoltAttributes"></a>
+  <b>Attributes</b>
+  <ul>
+    <li>EnergyAdjustValue: adjust the energy reading (energy = energy - EnergyAdjustValue)</li>
+  </ul>
+  <br>
+  <b>Common attributes</b>
+  <ul>
+    <li><a href="#event-aggregator">event-aggregator</a>: To reduce the high sending frequency and filter reception errors, you can use FHEM's event-aggregator. Common examples:
+      <ul>
+        <li><tt>power:60:linear:mean,energy:36000:none:v</tt> reduce sampling frequency to one power event per minute and one energy value per 10 hours, other events can be disabled via <tt>event-on-change-reading</tt> or <tt>event-on-update-reading</tt>.</li>
+        <li><tt>power::none:median:120,energy::none:median:120</tt> keep sampling frequency untouched, but filter outliers using statistical median</li>
+      </ul>
+    </li>
+    <li><a href="#event-min-interval">event-min-interval</a></li>
+    <li><a href="#event-on-change-reading">event-on-change-reading</a>: can be used to disable events for readings you don't need. See documentation for details.</li>
+    <li><a href="#event-on-update-reading">event-on-update-reading</a>: can be used to disable events for readings you don't need. See documentatino for details.</li> 
+  </ul>
   <a name="RevoltReadings"></a>
   <b>Readings</b>
   <ul>
-    <li>energy    [kWh]</li>
+    <li>energy    [kWh]: Total energy consumption summed up in the NC-5462
+        devices. It seems you can't reset this counter and it will wrap around
+        to 0 at 655.35 kWh. The EnergyAdjustValue attribute (see above) allows
+        to adapt the reading, e.g. when you connect a new device.</li>
     <li>power     [W]</li>
     <li>voltage   [V]</li>
     <li>current   [A]</li>
     <li>frequency [Hz]</li>
-    <li>Pf</li>
+    <li>Pf: Power factor</li>
   </ul>
 
 </ul>
 =end html
-=cut
+=cut

+ 10 - 7
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 16375 2018-03-10 15:40:02Z neubert $
+# $Id: 20_X10.pm 17421 2018-09-28 19:18:37Z neubert $
 
 
 #
@@ -89,16 +89,19 @@ my %unitcodes_snd  = qw( 1 0110   2 1110   3 0010   4 1010
                         13 0000  14 1000  15 0100  16 1100);
 
 
-my %functions_set = ( "on"      => 0,
-                      "off"     => 0,
-                      "dimup"   => 1,
+my %functions_set = ( "on" => 0,
+                      "off" => 0,
+                      "dimup" => 1,
                       "dimdown" => 1,
-		      "dimto"   => 1,
+		                  "dimto" => 1,
                       "on-till" => 1,
-		      "on-for-timer" => 1,
+            		      "on-for-timer" => 1,
+                      "all_units_off" => 0,
+                      "all_units_on" => 0,
+                      "all_lights_off" => 0,
+                      "all_lights_on" => 0,
                     );
 
-
 my %models = (
     lm12	=> 'dimmer',
     lm15        => 'switch',

+ 233 - 0
fhem/core/FHEM/24_Iluminize.pm

@@ -0,0 +1,233 @@
+################################################################################################
+#
+# $Id: 24_Iluminize.pm 17537 2018-10-15 16:08:23Z vk $
+#
+#
+#  Copyright notice
+#
+#  Release 2018-10 First version
+#
+#  (c) 2018 Copyright: Volker Kettenbach
+#  e-mail: volker at kettenbach minus it dot de
+#
+#  Description:
+#  This is an FHEM-Module for the Iluinize LED Stripes,
+#
+#  Requirements:
+#  This module requires:
+#  	Perl Module: IO::Socket::INET
+#  	Perl Module: IO::Socket::Timeout
+#  On a Debian (based) system, these requirements can be fullfilled by:
+#   apt-get install install libio-socket-inet6-perl libio-socket-timeout-perl
+#
+#  Origin:
+#  https://gitlab.com/volkerkettenbach/FHEM-Iluminize
+#
+#  Support: https://forum.fhem.de/index.php/topic,92007.0.html
+#
+#
+#################################################################################################
+
+package main;
+
+use strict;
+use warnings;
+
+sub Iluminize_Initialize($)
+{
+    my ($hash) = @_;
+
+    $hash->{DefFn}      = "Iluminize::Define";
+    $hash->{ReadFn}     = "Iluminize::Get";
+    $hash->{SetFn}      = "Iluminize::Set";
+    $hash->{UndefFn}    = "Iluminize::Undefine";
+    $hash->{DeleteFn}   = "Iluminize::Delete";
+    $hash->{AttrFn}     = "Iluminize::Attr";
+    $hash->{AttrList}   = "$readingFnAttributes";
+}
+
+
+
+package Iluminize;
+
+use strict;
+use warnings;
+use POSIX;
+use IO::Socket::INET;
+use IO::Socket::Timeout;
+use SetExtensions;
+use GPUtils qw(:all);  # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt
+
+## Import der FHEM Funktionen
+BEGIN {
+    GP_Import(qw(
+        readingsSingleUpdate
+        readingsBulkUpdate
+        readingsBulkUpdateIfChanged
+        readingsBeginUpdate
+        readingsEndUpdate
+        Log3
+        CommandAttr
+        AttrVal
+        ReadingsVal
+        CommandDefMod
+        modules
+        setKeyValue
+        getKeyValue
+        getUniqueId
+        RemoveInternalTimer
+        InternalTimer
+        defs
+        init_done
+        IsDisabled
+        deviceEvents
+        HttpUtils_NonblockingGet
+        gettimeofday
+        Dispatch
+        SetExtensions
+    ))
+};
+
+my $on =
+    chr(hex("55")) .
+    chr(hex("99")) .
+    chr(hex("5e")) .
+    chr(hex("bb")) .
+    chr(hex("01")) .
+    chr(hex("02")) .
+    chr(hex("02")) .
+    chr(hex("12")) .
+    chr(hex("ab")) .
+    chr(hex("c2")) .
+    chr(hex("aa")) .
+    chr(hex("aa"));
+
+my $off=
+    chr(hex("55")) .
+    chr(hex("99")) .
+    chr(hex("5e")) .
+    chr(hex("bb")) .
+    chr(hex("01")) .
+    chr(hex("02")) .
+    chr(hex("02")) .
+    chr(hex("12")) .
+    chr(hex("a9")) .
+    chr(hex("c0")) .
+    chr(hex("aa")) .
+    chr(hex("aa"));
+
+
+sub Define($$)
+{
+    my ($hash, $def) = @_;
+    my $name= $hash->{NAME};
+
+    my @a = split( "[ \t][ \t]*", $def );
+    return "Wrong syntax: use define <name> Iluminize <hostname/ip> " if (int(@a) != 3);
+
+    $hash->{HOST}=$a[2];
+
+    Log3 $hash, 0, "Iluminize: $name defined.";
+
+    return undef;
+}
+
+# No get so far
+sub Get($$) {}
+
+
+sub Set($$)
+{
+    my ($hash, $name, $cmd, @args) = @_;
+    my $cmdList = "on off";
+
+    my $remote_host = $hash->{HOST};
+    my $remote_port = 8899;
+
+    Log3 $hash, 3, "Iluminize: $name Set <". $cmd ."> called" if ($cmd !~ /\?/);
+
+    return "\"set $name\" needs at least one argument" unless(defined($cmd));
+
+    my $command;
+    if ($cmd eq "on") {
+        $command = $on;
+        readingsSingleUpdate($hash, "state", "on", 1);
+    } elsif($cmd eq "off") {
+        $command = $off;
+        readingsSingleUpdate($hash, "state", "off", 1);
+    } else {
+        return SetExtensions($hash, $cmdList, $name, $cmd, @args);
+    }
+
+    my $socket = IO::Socket::INET->new(PeerAddr => $remote_host,
+        PeerPort => $remote_port,
+        Proto    => 'tcp',
+        Type     => SOCK_STREAM,
+        Timeout  => 2 )
+        or return "Couldn't connect to $remote_host:$remote_port: $@\n";
+
+    $socket->write($command);
+
+    return undef;
+}
+
+
+sub Undefine($$)
+{
+    my ($hash, $arg) = @_;
+    my $name= $hash->{NAME};
+    Log3 $hash, 4, "Iluminize: $name undefined.";
+    return;
+}
+
+
+
+sub Delete($$) {
+    my ($hash, $arg) = @_;
+    my $name= $hash->{NAME};
+    Log3 $hash, 0, "Ilumnize: $name deleted.";
+    return undef;
+}
+
+
+
+sub Attr($$$$) {
+    my ($cmd, $name, $aName, $aVal) = @_;
+    my $hash = $defs{$name};
+
+    return undef;
+}
+
+1;
+
+
+=pod
+=item summary Support for Iluminize wifi controlled led stripe
+=item summary_DE Support für die Iluminize wlan LED-Produkte
+
+=begin html
+
+<a name="Iluminize"></a>
+<h3>Iluminize</h3>
+  <br>
+    <a name="Iluminize"></a>
+    <b>Define</b>
+    <code>define &lt;name&gt; Iluminize &lt;ip/hostname&gt;</code><br>
+    	<br>
+    	Defines a wifi controlled Iluminize LED light
+
+=end html
+
+=begin html_DE
+
+<a name="Iluminize"></a>
+<h3>Iluminize</h3>
+  <br>
+    <a name="Iluminize"></a>
+    <b>Define</b>
+    <code>define &lt;name&gt; Iluminize &lt;ip/hostname&gt;</code><br>
+    	<br>
+    	Definiert ein Iluminize LED Wifi-Licht
+
+=end html_DE
+

+ 38 - 25
fhem/core/FHEM/24_TPLinkHS110.pm

@@ -1,5 +1,5 @@
 ################################################################
-# $Id: 24_TPLinkHS110.pm 16933 2018-07-02 16:02:15Z vk $
+# $Id: 24_TPLinkHS110.pm 17468 2018-10-06 16:32:28Z vk $
 #
 #  Release 2018-11-01 SetExtension
 #
@@ -105,11 +105,12 @@ sub TPLinkHS110_Get($$)
 	        Type     => SOCK_STREAM,
        		Timeout  => $hash->{TIMEOUT} )
 	        or return "Couldn't connect to $remote_host:$remote_port: $@\n";
-	$socket->send($c);
-	my $data;
-	my $retval = $socket->recv($data,8192);
+	$socket->write($c);
+    IO::Socket::Timeout->enable_timeouts_on($socket);
+    $socket->read_timeout(.5);
+    my $data;
+    $data = <$socket>;
 	$socket->close();
-	unless( defined $retval) { return undef; }
 	
     readingsBeginUpdate($hash);
     $data = decrypt(substr($data,4));
@@ -153,11 +154,11 @@ sub TPLinkHS110_Get($$)
 		        Type     => SOCK_STREAM,
 	       		Timeout  => $hash->{TIMEOUT} )
 		        or return "Couldn't connect to $remote_host:$remote_port: $@\n";
-		$socket->send($rc);
-		my $rdata;
-		$retval = $socket->recv($rdata,8192);
-		$socket->close();
-		unless( defined $retval) { return undef; }
+		$socket->write($rc);
+        IO::Socket::Timeout->enable_timeouts_on($socket);
+        $socket->read_timeout(.5);
+        my $rdata;
+        $rdata = <$socket>;
 		$rdata = decrypt(substr($rdata,4));
 
 		if (length($rdata)==0) {
@@ -169,7 +170,9 @@ sub TPLinkHS110_Get($$)
         if(!$success) {
             readingsEndUpdate($hash, 1);
 			return; 
-		}
+		} else {
+            Log3 $hash, 2, "TPLinkHS110: $name Realtime data updated";
+        }
 		
 		my %emeterReadings = ();
 
@@ -202,13 +205,16 @@ sub TPLinkHS110_Get($$)
 		        Type     => SOCK_STREAM,
 	       		Timeout  => $hash->{TIMEOUT} )
 		        or return "Couldn't connect to $remote_host:$remote_port: $@\n";
-		$socket->send($c);
-		my $data;
-		$retval = $socket->recv($data,8192);
+		$socket->write($c);
+        IO::Socket::Timeout->enable_timeouts_on($socket);
+        $socket->read_timeout(.5);
+        my $data;
+        $data = <$socket>;
 		$socket->close();
-		unless( defined $retval) { return undef; }
 		$data = decrypt(substr($data,4));
-        
+
+        Log3 $hash, 3, "TPLinkHS110: $name Updating daystat. Data: " . $data;
+
         ($success,$json) = TPLinkHS110__evaljson($name,$data);
         if($success && $json) {
 			my $total=0;
@@ -232,11 +238,16 @@ sub TPLinkHS110_Get($$)
 			$count = @{$json->{'emeter'}->{'get_daystat'}->{'day_list'}};
 			readingsBulkUpdate($hash, "monthly_total", $total);
 			if ($count) { readingsBulkUpdate($hash, "daily_average", $total/$count)};
+            Log3 $hash, 2, "TPLinkHS110: $name Daystat updated";
         } else {
+            Log3 $hash, 1, "TPLinkHS110: $name Error updating daystat. Success: " . $success . ", json: " . $json;
+            Log3 $hash, 3, "TPLinkHS110: $name Updating readings";
             readingsEndUpdate($hash, 1);
-			return;       
+            Log3 $hash, 3, "TPLinkHS110: $name Get end";
+			return;
         }
 	}
+    Log3 $hash, 3, "TPLinkHS110: $name Updating readings";
 	readingsEndUpdate($hash, 1);
 	Log3 $hash, 3, "TPLinkHS110: $name Get end";
 }
@@ -275,11 +286,12 @@ sub TPLinkHS110_Set($$)
 	        Type     => SOCK_STREAM,
        		Timeout  => $hash->{TIMEOUT})
 	        or return "Couldn't connect to $remote_host:$remote_port: $@\n";
-	$socket->send($c);
-	my $data;
-	my $retval = $socket->recv($data,8192);
+	$socket->write($c);
+    IO::Socket::Timeout->enable_timeouts_on($socket);
+    $socket->read_timeout(.5);
+    my $data;
+    $data = <$socket>;
 	$socket->close();
-	unless( defined $retval) { return undef; }
     
     readingsBeginUpdate($hash);
 	$data = decrypt(substr($data,4));
@@ -365,11 +377,12 @@ sub TPLinkHS110_Attr {
 		        Type     => SOCK_STREAM,
 	       		Timeout  => $hash->{TIMEOUT} )
 		        or return "Couldn't connect to $remote_host:$remote_port: $@\n";
-		$socket->send($c);
-		my $data;
-		my $retval = $socket->recv($data,8192);
+        $socket->write($c);
+        IO::Socket::Timeout->enable_timeouts_on($socket);
+        $socket->read_timeout(.5);
+        my $data;
+        $data = <$socket>;
 		$socket->close();
-		unless( defined $retval) { return undef; }
 		$data = decrypt(substr($data,4));
 		my $json;
 		eval {

+ 10 - 11
fhem/core/FHEM/30_DUOFERN.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 30_DUOFERN.pm 16783 2018-05-27 10:01:34Z Telekatz $
+# $Id: 30_DUOFERN.pm 17477 2018-10-07 14:15:26Z Telekatz $
 
 package main;
 
@@ -1054,7 +1054,8 @@ sub
 DUOFERN_Parse($$)
 {
   my ($hash,$msg) = @_;
-
+  my @retval = ();
+  
   my $code = substr($msg,30,6);
   $code = substr($msg,36,6) if ($msg =~ m/81.{42}/);
 
@@ -1063,7 +1064,6 @@ DUOFERN_Parse($$)
   my $def = $modules{DUOFERN}{defptr}{$code};
    
   my $def01;
-  my $def02;
   
   if(!$def) {
     DoTrigger("global","UNDEFINED DUOFERN_$code DUOFERN $code");
@@ -1165,7 +1165,7 @@ DUOFERN_Parse($$)
           foreach my $reading (@readingsBlindMode){
             delete($hash->{READINGS}{$reading});
             delete($statusValue{$reading});
-            Log3 $hash, 1, "DUOFERN blinds mode ".$reading;
+            #Log3 $hash, 1, "DUOFERN blinds mode ".$reading;
           }
         }
         
@@ -1249,8 +1249,8 @@ DUOFERN_Parse($$)
           readingsBulkUpdate($hashA, $key,  $statusValue{$key},     1);  
         }
         readingsEndUpdate($hashA, 1); # Notify is done by Dispatch
-         
-        DoTrigger($hashA->{NAME}, undef);
+        
+        push (@retval, $hashA->{NAME}) if ($hashA->{NAME} ne $name);
       }
 
     } else {
@@ -1308,7 +1308,6 @@ DUOFERN_Parse($$)
         }
         
         readingsSingleUpdate($hash, "event", $sensorMsg{$id}{name}.$chan, 1);
-        DoTrigger($hash->{NAME},$sensorMsg{$id}{name}.$chan);
       }        
     }
   
@@ -1417,10 +1416,10 @@ DUOFERN_Parse($$)
     Log3 $hash, 3, "DUOFERN unknown msg: $msg";
   }
   
-  DoTrigger($def01->{NAME}, undef) if ($def01);
-  DoTrigger($def02->{NAME}, undef) if ($def02);
-  
-  return $name;
+  push (@retval, $def01->{NAME}) if ($def01); 
+  push (@retval, $name);
+      
+  return @retval;
 }
 
 #####################################

+ 339 - 0
fhem/core/FHEM/30_TradfriGateway.pm

@@ -0,0 +1,339 @@
+# @author Peter Kappelt
+# @version 1.16.dev-cf.9
+
+package main;
+use strict;
+use warnings;
+
+require 'DevIo.pm';
+
+my %TradfriGateway_sets = (
+	#'reopen'	=> ' ',
+);
+
+my %TradfriGateway_gets = (
+	'deviceList'	=> ' ',
+	'groupList'		=> ' ',
+);
+
+sub TradfriGateway_Initialize($) {
+	my ($hash) = @_;
+
+	$hash->{DefFn}      = 'TradfriGateway_Define';
+	$hash->{UndefFn}    = 'TradfriGateway_Undef';
+	$hash->{SetFn}      = 'TradfriGateway_Set';
+	$hash->{GetFn}      = 'TradfriGateway_Get';
+	$hash->{AttrFn}     = 'TradfriGateway_Attr';
+	$hash->{ReadFn}     = 'TradfriGateway_Read';
+	$hash->{WriteFn}	= 'TradfriGateway_Write';
+	$hash->{ReadyFn}	= 'TradfriGateway_Ready';
+
+	$hash->{Clients}	= "TradfriDevice:TradfriGroup";
+	$hash->{MatchList} = {
+			"1:TradfriDevice" => '^subscribedDeviceUpdate::',
+			"2:TradfriGroup" => '(^subscribedGroupUpdate::)|(^moodList::)' ,
+			};
+}
+
+sub TradfriGateway_Define($$) {
+	my ($hash, $def) = @_;
+	my @param = split('[ \t]+', $def);
+
+	if(int(@param) < 4) {
+		return "too few parameters: define <name> TradfriGateway <gateway-ip> <gateway-secret>";
+	}
+
+	#close connection to socket, if open
+	DevIo_CloseDev($hash);
+
+	$hash->{name}  = $param[0];
+
+	$hash->{gatewayAddress} = $param[2];
+	$hash->{gatewaySecret} = $param[3];
+
+	# @todo make user settable
+	$hash->{DeviceName} = "192.168.1.146:1505";
+
+	if(int(@param) > 4){
+		#there was a fifth parameter
+		#it is the path to the coap client, add it to the environments path
+		#Edit: it is obsolete now! Give advice to the user
+		Log(0, "[TradfriGateway] The parameter \"coap-client-directory\" in the gateway's definition is obsolete now! Please remove it as soon as possible!")
+	}
+
+	#open the socket connection
+	#@todo react to return code
+	DevIo_OpenDev($hash, 0, "TradfriGateway_DeviceInit");
+
+	return undef;
+}
+
+sub TradfriGateway_Undef($$) {
+	my ($hash, $arg) = @_;
+	# nothing to do
+	return undef;
+}
+
+sub TradfriGateway_DeviceInit($){
+	my $hash = shift;
+
+	#subscribe to all devices and groups, update the moodlist of the group
+	#@todo check, whether we this instance is the IODev of the device/ group
+	foreach my $deviceID ( keys %{$modules{'TradfriDevice'}{defptr}}){
+		TradfriGateway_Write($hash, 0, 'subscribe', $deviceID);
+	}
+
+	foreach my $groupID ( keys %{$modules{'TradfriGroup'}{defptr}}){
+		TradfriGateway_Write($hash, 1, 'subscribe', $groupID);
+		TradfriGateway_Write($hash, 1, 'moodlist', $groupID);
+	}
+}
+
+
+# a write command, that is dispatch from the logical module to here via IOWrite requires at least two arguments:
+# - 1. Scope: 				Group (1) or Device (0)
+# - 2. Action	: 			A command:
+#								* list -> sets the readings groups/ devices
+#								* moodlist (groups only) -> get all moods that are defined for this group
+#								* subscribe -> subscribe to updated of that specific device
+#								* set -> write a specific value to the group/ device
+# - 3. ID:					ID of the group or device
+# - 4. attribute::value		only for command set, attribute can be onoff, dimvalue, mood (groups only), color (devices only) or name
+sub TradfriGateway_Write ($@){
+	my ( $hash, $groupOrDevice, $action, $id, $attrValue) = @_;
+
+	if(!defined($groupOrDevice) && !defined($action)){
+		Log(1, "[TradfriGateway] Not enough arguments for IOWrite!");
+		return "Not enough arguments for IOWrite!";
+	}
+
+	my $command = '';
+
+	#for cmd-buildup: decide on group/ device
+	if($groupOrDevice){
+		$command .= 'group::';
+	}else{
+		$command .= 'device::';
+	}
+
+	if($action eq 'list'){
+		$command .= 'list';
+	}elsif($action eq 'moodlist'){
+		$command .= "moodlist::${id}";
+	}elsif($action eq 'subscribe'){
+		#silently return if connection is open.
+		#at startup, every device/ group runs subscribe. If the connection isn't open, we do it later.
+		return if($hash->{STATE} ne 'opened');
+		$command .= "subscribe::${id}";
+	}elsif($action eq 'set'){
+		$command .= "set::${id}::${attrValue}";
+	}else{
+		return "Unknown command: " . $command;
+	}
+
+	#@todo better check, if opened
+	if($hash->{STATE} ne 'opened'){
+		Log(1, "[TradfriGateway] Can't write, connection is not opened!");
+		return "Can't write, connection is not opened!";
+	}
+
+	DevIo_SimpleWrite($hash, $command . "\n", 2, 0);
+
+	return undef;
+}
+
+#data was received on the socket
+sub TradfriGateway_Read ($){
+	my ( $hash ) = @_;
+
+	my $msg = DevIo_SimpleRead($hash);
+
+	if(!defined($msg)){
+		return undef;
+	}
+
+	my $msgReadableWhitespace = $msg;
+	$msgReadableWhitespace =~ s/\r/\\r/g;
+	$msgReadableWhitespace =~ s/\n/\\n/g;
+	Log(4, "[TradfriGateway] Received message on socket: \"" . $msgReadableWhitespace . "\"");
+
+	#there might be multiple messages at once, they are split by newline. Iterate through each of them
+	my @messagesSingle = split(/\n/, $msg);
+	foreach my $message(@messagesSingle){
+		#if there is whitespace left, remove it.
+		$message =~ s/\r//g;
+		$message =~ s/\n//g;
+
+		#devices and groups
+		#@todo not as JSON array
+		if(($message ne '') && ((split(/::/, $message))[0] =~ /(?:group|device)List/)){
+			if((split(/::/, $message))[0] eq 'deviceList'){
+				readingsSingleUpdate($hash, 'devices', (split(/::/, $message))[1], 1);
+			}
+			if((split(/::/, $message))[0] eq 'groupList'){
+				readingsSingleUpdate($hash, 'groups', (split(/::/, $message))[1], 1);
+			}
+		}
+
+		#dispatch the message if it isn't empty, only dispatch messages that come from an observe
+		if(($message ne '') && ((split(/::/, $message))[0] =~ /(?:subscribed(?:Group|Device)Update)|(?:moodList)/)){
+			Dispatch($hash, $message, undef);
+		}
+	}
+}
+
+sub TradfriGateway_Ready($){
+	my ($hash) = @_;
+	return DevIo_OpenDev($hash, 1, "TradfriGateway_DeviceInit") if($hash->{STATE} eq "disconnected");
+}
+
+
+sub TradfriGateway_Get($@) {
+	my ($hash, @param) = @_;
+
+	return '"get TradfriGateway" needs at least one argument' if (int(@param) < 2);
+
+	my $name = shift @param;
+	my $opt = shift @param;
+	if(!$TradfriGateway_gets{$opt}) {
+		my @cList = keys %TradfriGateway_gets;
+		return "Unknown argument $opt, choose one of " . join(" ", @cList);
+	}
+
+	if($opt eq 'deviceList'){
+		TradfriGateway_Write($hash, 0, 'list');
+	}elsif($opt eq 'groupList'){
+		TradfriGateway_Write($hash, 1, 'list');
+	}
+
+	return $TradfriGateway_gets{$opt};
+}
+
+sub TradfriGateway_Set($@) {
+	my ($hash, @param) = @_;
+
+	return '"set TradfriGateway" needs at least one argument' if (int(@param) < 2);
+
+	my $name = shift @param;
+	my $opt = shift @param;
+	my $value = join("", @param);
+
+	if(!defined($TradfriGateway_sets{$opt})) {
+		my @cList = keys %TradfriGateway_sets;
+		return "Unknown argument $opt, choose one of " . join(" ", @cList);
+	}
+
+	if($opt eq "reopen"){
+		#close connection to socket, if open
+		DevIo_CloseDev($hash);
+		#@todo react to return code
+		my $ret = DevIo_OpenDev($hash, 0, "TradfriGateway_DeviceInit");
+	}
+
+	return undef;
+}
+
+
+sub TradfriGateway_Attr(@) {
+	my ($cmd,$name,$attr_name,$attr_value) = @_;
+	if($cmd eq "set") {
+		#if($attr_name eq "formal") {
+		#	if($attr_value !~ /^yes|no$/) {
+		#		my $err = "Invalid argument $attr_value to $attr_name. Must be yes or no.";
+		#		Log 3, "TradfriGateway: ".$err;
+		#		return $err;
+		#	}
+		#} else {
+		#	return "Unknown attr $attr_name";
+		#}
+	}
+	return undef;
+}
+
+1;
+
+=pod
+
+=item device
+=item summary connects with an IKEA Trådfri gateway
+=item summary_DE stellt die Verbindung mit einem IKEA Trådfri Gateway her
+
+=begin html
+
+<a name="TradfriGateway"></a>
+<h3>TradfriGateway</h3>
+<ul>
+    <i>TradfriGateway</i> stores the connection data for an IKEA Trådfri gateway. It is necessary for TradfriDevice and TradfriGroup
+    <br><br>
+    <a name="TradfriGatewaydefine"></a>
+    <b>Define</b>
+    <ul>
+        <code>define &lt;name&gt; TradfriGateway &lt;gateway-ip&gt; &lt;gateway-secret&gt;</code>
+        <br><br>
+        Example: <code>define trGateway TradfriGateway TradfriGW.int.kappelt.net vBkxxxxxxxxxx7hz</code>
+        <br><br>
+        The IP can either be a "normal" IP-Address, like 192.168.2.60, or a DNS name (like shown above).<br>
+        You can find the secret on a label on the bottom side of the gateway.
+        The parameter "coap-client-path" is isn't used anymore and thus not shown here anymore. Please remove it as soon as possible, if you are still using it.<br>
+		You need to run kCoAPSocket running in background, that acts like a translator between FHEM and the Trådfri Gateway.
+    </ul>
+    <br>
+
+    <a name="TradfriGatewayset"></a>
+    <b>Set</b><br>
+    <ul>
+        <code>set &lt;name&gt; &lt;option&gt; [&lt;value&gt;]</code>
+        <br><br>
+        You can set the following options. See <a href="http://fhem.de/commandref.html#set">commandref#set</a>
+        for more info about the set command.
+        <br><br>
+        Options:
+        <ul>
+              <li><i>reopen</i><br>
+                  Re-open the connection to the Java TCP socket, that acts like a "translator" between FHEM and the Tradfri-CoAP-Infrastructure.<br>
+                  If the connection is already opened, it'll be closed and opened.<br>
+                  If the connection isn't open yet, a try to open it will be executed.<br>
+                  <b>Caution: </b>Running this command seems to trigger some issues! Do <i>not</i> run it before a update is available!</li>
+        </ul>
+    </ul>
+    <br>
+
+    <a name="TradfriGatewayget"></a>
+    <b>Get</b><br>
+    <ul>
+        <code>get &lt;name&gt; &lt;option&gt;</code>
+        <br><br>
+        You can get the following information about the device. See
+        <a href="http://fhem.de/commandref.html#get">commandref#get</a> for more info about
+        the get command.
+		<br><br>
+        Options:
+        <ul>
+              <li><i>deviceList</i><br>
+                  Sets the reading "devices" to a JSON-formatted string of all device IDs and their names.</li>
+              <li><i>groupList</i><br>
+                  Sets the reading "devices" to a JSON-formatted string of all group IDs and their names.</li>
+        </ul>
+    </ul>
+    <br>
+
+    <a name="TradfriGatewayattr"></a>
+    <b>Attributes</b>
+    <ul>
+        <code>attr &lt;name&gt; &lt;attribute&gt; &lt;value&gt;</code>
+        <br><br>
+        See <a href="http://fhem.de/commandref.html#attr">commandref#attr</a> for more info about
+        the attr command.
+        <br><br>
+        Attributes:
+        <ul>
+            <li><i></i><br>
+            	There are no custom attributes implemented
+            </li>
+        </ul>
+    </ul>
+</ul>
+
+=end html
+
+=cut

+ 266 - 0
fhem/core/FHEM/31_TradfriDevice.pm

@@ -0,0 +1,266 @@
+# @author Peter Kappelt
+# @author Clemens Bergmann
+# @version 1.16.dev-cf.9
+
+package main;
+use strict;
+use warnings;
+
+use Data::Dumper;
+use JSON;
+
+use TradfriUtils;
+
+sub TradfriDevice_Initialize($) {
+	my ($hash) = @_;
+
+	$hash->{DefFn}      = 'Tradfri_Define';
+	$hash->{UndefFn}    = 'Tradfri_Undef';
+	$hash->{SetFn}      = 'Tradfri_Set';
+	$hash->{GetFn}      = 'Tradfri_Get';
+	$hash->{AttrFn}     = 'Tradfri_Attr';
+	$hash->{ReadFn}     = 'Tradfri_Read';
+	$hash->{ParseFn}	= 'TradfriDevice_Parse';
+
+	$hash->{Match} = '^subscribedDeviceUpdate::';
+
+	$hash->{AttrList} =
+		"usePercentDimming:1,0 "
+		. $readingFnAttributes;
+}
+
+#messages look like this: (without newlines)
+# subscribedDeviceUpdate::device-id::{
+#    "lastSeenAt":1501407261,
+#    "createdAt":1492280964,
+#    "reachabilityState":1,
+#    "name":"Fenster Links",
+#    "dimvalue":200,
+#    "type":"TRADFRI bulb E27 opal 1000lm",
+#    "deviceid":65537,
+#    "version":"1.1.1.0-5.7.2.0",
+#    "manufacturer":"IKEA of Sweden",
+#    "onoff":0
+# }
+sub TradfriDevice_Parse ($$){
+	my ($io_hash, $message) = @_;
+
+	my @parts = split('::', $message);
+
+	if(int(@parts) < 3){
+		#expecting at least three parts
+		return undef;
+	}
+	
+	my $messageID = $parts[1];
+
+	#check if device with the id exists
+	if(my $hash = $modules{'TradfriDevice'}{defptr}{$messageID}) 
+	{
+		# the path returned "Not Found" -> unknown resource, but this message still suits for this device
+		if($parts[2] eq "Not Found"){
+			$hash->{STATE} = "NotFound";
+			return $hash->{NAME};
+		}
+
+		#parse the JSON data
+		my $jsonData = eval{ JSON->new->utf8->decode($parts[2]) };
+		if($@){
+			return undef; #the string was probably not valid JSON
+		}
+
+		my $manufacturer = $jsonData->{'manufacturer'} || '';
+		my $type = $jsonData->{'type'} || '';
+		
+		#dimvalue is in range 0 - 254
+		my $dimvalue = $jsonData->{'dimvalue'} || '0';
+		#dimpercent is always in range 0 - 100
+		my $dimpercent = int($dimvalue / 2.54 + 0.5);
+		$dimpercent = 1 if($dimvalue == 1);
+		$dimvalue = $dimpercent if (AttrVal($hash->{NAME}, 'usePercentDimming', 0) == 1);
+		
+		my $state = 'off';
+		if($jsonData->{'onoff'} eq '0'){
+			$dimpercent = 0;
+		}else{
+        	$state = Tradfri_stateString($dimpercent);
+        }
+		
+        my $onoff = ($jsonData->{'onoff'} || '0') ? 'on':'off';
+		my $name = $jsonData->{'name'} || '';
+		my $createdAt = FmtDateTimeRFC1123($jsonData->{'createdAt'} || '');
+		my $reachableState = $jsonData->{'reachabilityState'} || '';
+		my $lastSeen = FmtDateTimeRFC1123($jsonData->{'lastSeenAt'} || '');
+		my $color = $jsonData->{'color'} || '';
+		my $version = $jsonData->{'version'} || '';
+
+		readingsBeginUpdate($hash);
+		readingsBulkUpdateIfChanged($hash, 'dimvalue', $dimvalue, 1);
+		readingsBulkUpdateIfChanged($hash, 'pct', $dimpercent, 1);
+		readingsBulkUpdateIfChanged($hash, 'state', $state, 1);
+		readingsBulkUpdateIfChanged($hash, 'name', $name, 1);
+		readingsBulkUpdateIfChanged($hash, 'createdAt', $createdAt, 1);
+		readingsBulkUpdateIfChanged($hash, 'softwareVersion', $version, 1);
+		readingsBulkUpdateIfChanged($hash, 'type', $type, 1);
+		readingsBulkUpdateIfChanged($hash, 'manufacturer', $manufacturer, 1);
+		readingsBulkUpdateIfChanged($hash, 'reachableState', $reachableState, 1);
+		readingsBulkUpdateIfChanged($hash, 'color', $color, 1);
+		readingsBulkUpdateIfChanged($hash, 'lastSeen', $lastSeen, 1);
+		readingsBulkUpdateIfChanged($hash, 'onoff', $onoff, 1);
+		readingsEndUpdate($hash, 1);
+	
+		#$attr{$hash->{NAME}}{webCmd} = 'pct:toggle:on:off';
+		#$attr{$hash->{NAME}}{devStateIcon} = '{(Tradfri_devStateIcon($name),"toggle")}' if( !defined( $attr{$hash->{NAME}}{devStateIcon} ) );
+		
+		#return the appropriate device's name
+		return $hash->{NAME}; 
+	}
+	
+	return undef;
+}
+
+
+1;
+
+=pod
+
+=item device
+=item summary controls an IKEA Trådfri device (bulb, panel)
+=item summary_DE steuert ein IKEA Trådfri Gerät
+
+=begin html
+
+<a name="TradfriDevice"></a>
+<h3>TradfriDevice</h3>
+<ul>
+    <i>TradfriDevice</i> is a module for controlling a single IKEA Trådfri device. You currently need a gateway for the connection.
+    See TradfriGateway.
+    <br><br>
+    <a name="TradfriDevicedefine"></a>
+    <b>Define</b>
+    <ul>
+        <code>define &lt;name&gt; TradfriDevice &lt;device-address&gt;</code>
+        <br><br>
+        Example: <code>define trDeviceOne TradfriDevice 65538</code>
+        <br><br>
+        You can get the ID of the devices by calling "get TradfriGW deviceList" on the gateway device
+    </ul>
+    <br>
+    
+    <a name="TradfriDeviceset"></a>
+    <b>Set</b><br>
+    <ul>
+        <code>set &lt;name&gt; &lt;option&gt; [&lt;value&gt;]</code>
+        <br><br>
+        You can set the following options. See <a href="http://fhem.de/commandref.html#set">commandref#set</a> 
+        for more info about the set command.
+        <br><br>
+        Options:
+        <ul>
+              <li><i>on</i><br>
+                  Turns the device on.<br>The brightness is the one, before the device was turned off</li>
+              <li><i>off</i><br>
+                  Turn the device off.</li>
+              <li><i>dimvalue</i><br>
+                  Set the brightness of the device.<br>
+                  You need to specify the brightness value as an integer between 0 and 100/254.<br>
+                  The largest value depends on the attribute "usePercentDimming".<br>
+                  If this attribute is set, the largest value will be 100.<br>
+                  By default, it isn't set, so the largest value is 254.<br>
+                  A brightness value of 0 turns the device off.<br>
+                  If the device is off, and you set a value greater than 0, it will turn on.</li>
+              <li><i>color</i>
+                  Set the color temperature of a bubl<br>
+                  Of course, that only works with bulbs, that can change their color temperature<br>
+                  You may pass "warm", "cold", "standard" or a RGB code in the format "RRGGBB"<br>
+                  Any RGB code can be set, though the bulb only is able to switch to certain colors.<br>
+                  IKEA uses the following RGB codes for their colors. Bulbs I've tested could only be set to those.<br>
+				  <ul>
+					<li>F1E0B5 for standard</li>
+					<li>F5FAF6 for cold</li>
+					<li>EFD275 for warm</li>
+				  </ul>
+				  Other RGB codes than listed here do not work with the current bulb (they only set their color to those three values).
+                  </li>
+        </ul>
+    </ul>
+    <br>
+
+    <a name="TradfriDeviceget"></a>
+    <b>Get</b><br>
+    <ul>
+        <code>get &lt;name&gt; &lt;option&gt;</code>
+        <br><br>
+        You can get the following information about the device. See 
+        <a href="http://fhem.de/commandref.html#get">commandref#get</a> for more info about 
+        the get command.
+		<br><br>
+        Options:
+        <ul>
+              <li><i></i><br>
+                  There are no gets implemented</li>
+        </ul>
+    </ul>
+    <br>
+
+    <a name="TradfriDevicereadings"></a>
+    <b>Readings</b><br>
+    <ul>
+        The following readings are displayed for a device. Once there is a change and the connection to the gateway is made, they get updated automatically.
+		<br><br>
+        Readings:
+        <ul>
+              <li><i>color</i><br>
+                  The color that is set for this bulb. Its value is a hexadecimal code in the format "RRGGBB".<br>
+                  If the device doesn't support the change of colors the reading's value will be "0"</li>
+              <li><i>createdAt</i><br>
+                  A timestamp string, like "Sat, 15 Apr 2017 18:29:24 GMT", that indicates, when the device was paired with the gateway.</li>
+              <li><i>dimvalue</i><br>
+                  The brightness that is set for this device. It is a integer in the range of 0 to 100/ 254.<br>
+                  The greatest dimvalue depends on the attribute "usePercentDimming", see below.</li>
+              <li><i>pct</i><br>
+                  The brightness that is set for this device in percent.</li>
+              <li><i>lastSeen</i><br>
+                  A timestamp string, like "Wed, 12 Jul 2017 14:32:06 GMT". I haven't understand the mechanism behind it yet.<br>
+                  Communication with the device won't update the lastSeen-timestamp - so I don't get the point of it. This needs some more investigation.</li>
+              <li><i>manufacturer</i><br>
+                  The device's manufacturer. Since there are only devices from IKEA available yet, it'll surely be "IKEA of Sweden".</li>
+              <li><i>name</i><br>
+                  The name of the device that you've set in the app.</li>
+              <li><i>onoff</i><br>
+                  Indicates whether the device is on or off, can be the strings 'on' or 'off'</li>
+              <li><i>reachableState</i><br>
+                  Indicates whether the gateway is able to connect to the device. This reading's value is either "0" or "1", where "1" indicates reachability.</li>
+              <li><i>softwareVersion</i><br>
+                  The version of the software that is running on the device.</li>
+              <li><i>state</i><br>
+                  Indicates, whether the device is on or off. Thus, the reading's value is either "on" or "off", too.</li>
+              <li><i>type</i><br>
+                  The product type of the device. Is a string like "TRADFRI bulb E27 opal 1000lm"</li>
+        </ul>
+    </ul>
+    <br>
+    
+    <a name="TradfriDeviceattr"></a>
+    <b>Attributes</b>
+    <ul>
+        <code>attr &lt;name&gt; &lt;attribute&gt; &lt;value&gt;</code>
+        <br><br>
+        See <a href="http://fhem.de/commandref.html#attr">commandref#attr</a> for more info about 
+        the attr command.
+        <br><br>
+        Attributes:
+        <ul>
+            <li><i>usePercentDimming</i> 0/1<br>
+            	If this attribute is one, the largest value for "set dimvalue" will be 100.<br>
+            	Otherwise, the largest value is 254.<br>
+            	This attribute is useful, if you need to control the brightness in percent (0-100%)<br>
+            	For backward compatibility, it is disabled by default, so the largest dimvalue is 254 by default.
+            </li>
+        </ul>
+    </ul>
+</ul>
+
+=end html
+
+=cut

+ 259 - 0
fhem/core/FHEM/31_TradfriGroup.pm

@@ -0,0 +1,259 @@
+# @author Peter Kappelt
+# @author Clemens Bergmann
+# @version 1.16.dev-cf.9
+
+package main;
+use strict;
+use warnings;
+
+use Data::Dumper;
+use JSON;
+
+use TradfriUtils;
+
+sub TradfriGroup_Initialize($) {
+	my ($hash) = @_;
+
+	$hash->{DefFn}      = 'Tradfri_Define';
+	$hash->{UndefFn}    = 'Tradfri_Undef';
+	$hash->{SetFn}      = 'Tradfri_Set';
+	$hash->{GetFn}      = 'Tradfri_Get';
+	$hash->{AttrFn}     = 'Tradfri_Attr';
+	$hash->{ReadFn}     = 'Tradfri_Read';
+	$hash->{ParseFn}	= 'TradfriGroup_Parse';
+
+	$hash->{Match} = '(^subscribedGroupUpdate::)|(^moodList::)';
+
+	$hash->{AttrList} =
+		"usePercentDimming:1,0 "
+		. $readingFnAttributes;
+}
+
+
+#messages look like this: (without newlines)
+# subscribedGroupUpdate::group-id::{
+#    "createdAt":1494088484,
+#    "mood":198884,
+#    "groupid":173540,
+#    "members":[
+#       {
+#          "name":"Fenster Links",
+#          "deviceid":65537
+#       },
+#       {
+#          "deviceid":65536
+#       },
+#       {
+#          "name":"Fenster Rechts",
+#          "deviceid":65538
+#       }
+#    ],
+#    "name":"Wohnzimmer",
+#    "dimvalue":200,
+#    "onoff":0
+# }
+sub TradfriGroup_Parse($$){
+	my ($io_hash, $message) = @_;
+	
+	my @parts = split('::', $message);
+
+	if(int(@parts) < 3){
+		#expecting at least three parts
+		return undef;
+	}
+
+	my $messageID = $parts[1];
+
+	#check if group with the id exists
+	if(my $hash = $modules{'TradfriGroup'}{defptr}{$messageID}) 
+	{
+		#parse the JSON data
+		my $jsonData = eval{ JSON->new->utf8->decode($parts[2]) };
+		if($@){
+			return undef; #the string was probably not valid JSON
+		}
+
+		if('subscribedGroupUpdate' eq $parts[0]){
+			my $createdAt = FmtDateTimeRFC1123($jsonData->{'createdAt'} || '');
+			my $name = $jsonData->{'name'} || '';
+			my $members = JSON->new->pretty->encode($jsonData->{'members'});
+			
+			#dimvalue is in range 0 - 254
+			my $dimvalue = $jsonData->{'dimvalue'} || '0';
+			#dimpercent is always in range 0 - 100
+			my $dimpercent = int($dimvalue / 2.54 + 0.5);
+			$dimpercent = 1 if($dimvalue == 1);
+			$dimvalue = $dimpercent if (AttrVal($hash->{name}, 'usePercentDimming', 0) == 1);
+			
+			my $state = 'off';
+            if($jsonData->{'onoff'} eq '0'){
+				$dimpercent = 0;
+			}else{
+               	$state = Tradfri_stateString($dimpercent);
+            }
+
+            my $onoff = ($jsonData->{'onoff'} || '0') ? 'on':'off';
+
+			readingsBeginUpdate($hash);
+			readingsBulkUpdateIfChanged($hash, 'createdAt', $createdAt, 1);
+			readingsBulkUpdateIfChanged($hash, 'name', $name, 1);
+			readingsBulkUpdateIfChanged($hash, 'members', $members, 1);
+			readingsBulkUpdateIfChanged($hash, 'dimvalue', $dimvalue, 1);
+			readingsBulkUpdateIfChanged($hash, 'pct', $dimpercent, 1);
+			readingsBulkUpdateIfChanged($hash, 'onoff', $onoff, 1) ;
+			readingsBulkUpdateIfChanged($hash, 'state', $state, 1);
+			readingsEndUpdate($hash, 1);
+		}elsif('moodList' eq $parts[0]){
+			#update of mood list
+			readingsSingleUpdate($hash, 'moods', JSON->new->pretty->encode($jsonData), 1);
+
+			$hash->{helper}{moods} = undef;
+			foreach (@{$jsonData}){
+				$hash->{helper}{moods}->{$_->{name}} = $_;
+			}
+		}
+
+		#$attr{$hash->{NAME}}{webCmd} = 'pct:toggle:on:off';
+        #$attr{$hash->{NAME}}{devStateIcon} = '{(Tradfri_devStateIcon($name),"toggle")}' if( !defined( $attr{$hash->{name}}{devStateIcon} ) );
+		
+		#return the appropriate group's name
+		return $hash->{NAME}; 
+	}
+
+	return undef;
+}
+
+1;
+
+=pod
+
+=item device
+=item summary controls an IKEA Trådfri lighting group
+=item summary_DE steuert eine IKEA Trådfri Beleuchtungsgruppe
+
+=begin html
+
+<a name="TradfriGroup"></a>
+<h3>TradfriGroup</h3>
+<ul>
+    <i>TradfriGroup</i> is a module for controlling an IKEA Trådfri lighting group. You currently need a gateway for the connection.
+    See TradfriGateway.
+    <br><br>
+    <a name="TradfriGroupdefine"></a>
+    <b>Define</b>
+    <ul>
+        <code>define &lt;name&gt; TradfriGroup &lt;group-address&gt;</code>
+        <br><br>
+        Example: <code>define trGroupOne TradfriGroup 193768</code>
+        <br><br>
+        You can get the ID of the lighting groups by calling "get TradfriGW groupList" on the gateway device
+    </ul>
+    <br>
+    
+    <a name="TradfriGroupset"></a>
+    <b>Set</b><br>
+    <ul>
+        <code>set &lt;name&gt; &lt;option&gt; [&lt;value&gt;]</code>
+        <br><br>
+        You can set the following options. See <a href="http://fhem.de/commandref.html#set">commandref#set</a> 
+        for more info about the set command.
+        <br><br>
+        Options:
+        <ul>
+              <li><i>on</i><br>
+                  Turns all devices in the group on.<br>The brightness is the one, before the devices were turned off</li>
+              <li><i>off</i><br>
+                  Turn all devices in the group off.</li>
+              <li><i>dimvalue</i><br>
+                  Set the brightness of all devices in the group.<br>
+                  You need to specify the brightness value as an integer between 0 and 100/254.<br>
+                  The largest value depends on the attribute "usePercentDimming".<br>
+                  If this attribute is set, the largest value will be 100.<br>
+                  By default, it isn't set, so the largest value is 254.<br>
+                  A brightness value of 0 turns the devices off.<br>
+                  If the devices are off, and you set a value greater than 0, they'll turn on.</li>
+              <li><i>mood</i><br>
+                  Set the mood of the group.<br>
+                  Moods are preconfigured color temperatures, brightnesses and states for each device of the group<br>
+                  In order to set the mood, you need a mood ID or the mood's name.<br>
+                  You can list the moods that are available for this group by running "get moods".<br>
+                  Note, that the mood's name isn't necessarily the same that you've defined in the IKEA app.
+                  This module is currently unable to handle whitespaces in mood names, so whitespaces get removed internally.
+                  Check the reading "moods" after running "get moods" in order to get the names, that you may use with this module.<br>
+                  Mood names are case-sensitive. Mood names, that are only made out of numbers are not supported.</li>
+        </ul>
+    </ul>
+    <br>
+
+    <a name="TradfriGroupget"></a>
+    <b>Get</b><br>
+    <ul>
+        <code>get &lt;name&gt; &lt;option&gt;</code>
+        <br><br>
+        You can get the following information about the group. See 
+        <a href="http://fhem.de/commandref.html#get">commandref#get</a> for more info about 
+        the get command.
+		<br><br>
+        Options:
+        <ul>
+               <li><i>moods</i><br>
+                  Get all moods (their name and their ID) that are configured for this group<br>
+                  The JSON-formatted result is stored in the Reading "moods"</br>
+                  Please note, that the mood IDs may differ between different groups (though they are the same moods) -> check them for each group</li>
+        </ul>
+    </ul>
+    <br>
+    
+	<a name="TradfriGroupreadings"></a>
+    <b>Readings</b><br>
+    <ul>
+        The following readings are displayed for a group. Once there is a change and the connection to the gateway is made, they get updated automatically.
+		<br><br>
+        Readings:
+        <ul>
+              <li><i>createdAt</i><br>
+                  A timestamp string, like "Sat, 15 Apr 2017 18:29:24 GMT", that indicates, when the group was created in the gateway.</li>
+              <li><i>dimvalue</i><br>
+                  The brightness that is set for this group. It is a integer in the range of 0 to 100/ 254.<br>
+                  The greatest dimvalue depends on the attribute "usePercentDimming", see below.</li>
+              <li><i>pct</i><br>
+                  The brightness that is set for this device in percent.</li>
+              <li><i>members</i><br>
+                  JSON-String that contains all member-IDs and their names.</li>
+              <li><i>moods</i><br>
+                  JSON info of all moods and their names, e.g.:<br>
+                  [ { "groupid" : 173540, "moodid" : 198884, "name" : "EVERYDAY" }, { "moodid" : 213983, "name" : "RELAX", "groupid" : 173540 }, { "groupid" : 173540, "name" : "FOCUS", "moodid" : 206399 } ]<br>
+                  This reading isn't updated automatically, you've to call "get moods" in order to refresh them.</li>    
+              <li><i>name</i><br>
+                  The name of the group that you've set in the app.</li>
+              <li><i>onoff</i><br>
+                  Indicates whether the device is on or off, can be the strings 'on' or 'off'</li>
+              <li><i>state</i><br>
+                  Indicates, whether the group is on or off. Thus, the reading's value is either "on" or "off", too.</li>
+        </ul>
+    </ul>
+    <br>
+
+    <a name="TradfriGroupattr"></a>
+    <b>Attributes</b>
+    <ul>
+        <code>attr &lt;name&gt; &lt;attribute&gt; &lt;value&gt;</code>
+        <br><br>
+        See <a href="http://fhem.de/commandref.html#attr">commandref#attr</a> for more info about 
+        the attr command.
+        <br><br>
+        Attributes:
+        <ul>
+            <li><i>usePercentDimming</i> 0/1<br>
+            	If this attribute is one, the largest value for "set dimvalue" will be 100.<br>
+            	Otherwise, the largest value is 254.<br>
+            	This attribute is useful, if you need to control the brightness in percent (0-100%)<br>
+            	For backward compatibility, it is disabled by default, so the largest dimvalue is 254 by default.
+            </li>
+        </ul>
+    </ul>
+</ul>
+
+=end html
+
+=cut

+ 143 - 115
fhem/core/FHEM/32_withings.pm

@@ -1,16 +1,16 @@
 ##############################################################################
-# $Id: 32_withings.pm 17341 2018-09-13 22:23:46Z moises $
+# $Id: 32_withings.pm 17431 2018-09-29 20:49:21Z moises $
 #
 #  32_withings.pm
 #
-#  2017 Markus M.
+#  2018 Markus M.
 #  Based on original code by justme1968
 #
 #  https://forum.fhem.de/index.php/topic,64944.0.html
 #
 #
 ##############################################################################
-# Release 06 / 2018-09-13
+# Release 08 / 2018-09-28
 
 package main;
 
@@ -341,14 +341,18 @@ sub withings_Define($$) {
 
     CommandAttr(undef,"$name IODev $a[4]");
 
-  } elsif( @a == 4 && $a[2] =~ m/^\d+$/ && $a[3] =~ m/^[\w-]+$/i  ) {
+  } elsif( @a == 4 && $a[2] =~ m/^\d+$/ && $a[3] =~ m/^[\w:-]+$/i ) {
     $subtype = "USER";
 
     my $user = $a[2];
     my $key = $a[3];
 
+    my $accesskey = withings_encrypt($key);
+    Log3 $name, 3, "$name: encrypt $key to $accesskey" if($key ne $accesskey);
+    $hash->{DEF} = "$user $accesskey";
+
     $hash->{User} = $user;
-    $hash->{Key} = $key;
+    #$hash->{Key} = $accesskey; #not needed
 
     my $d = $modules{$hash->{TYPE}}{defptr}{"U$user"};
     return "device $user already defined as $d->{NAME}" if( defined($d) && $d->{NAME} ne $name );
@@ -380,13 +384,13 @@ sub withings_Define($$) {
   }
 
   $hash->{NAME} = $name;
-  $hash->{SUBTYPE} = $subtype;
+  $hash->{SUBTYPE} = $subtype if(defined($subtype));
 
 
   #CommandAttr(undef,"$name DbLogExclude .*");
 
 
-  my $resolve = inet_aton("scalews.health.nokia.com");
+  my $resolve = inet_aton("scalews.withings.com");
   if(!defined($resolve))
   {
     $hash->{STATE} = "DNS error";
@@ -418,7 +422,7 @@ sub withings_InitWait($) {
 
   RemoveInternalTimer($hash);
 
-  my $resolve = inet_aton("scalews.health.nokia.com");
+  my $resolve = inet_aton("scalews.withings.com");
   if(!defined($resolve))
   {
     $hash->{STATE} = "DNS error";
@@ -448,7 +452,7 @@ sub withings_Notify($$) {
   return if(!grep(m/^INITIALIZED|REREADCFG$/, @{$dev->{CHANGED}}));
   Log3 "withings", 5, "withings: notify";
 
-  my $resolve = inet_aton("scalews.health.nokia.com");
+  my $resolve = inet_aton("scalews.withings.com");
   if(!defined($resolve))
   {
     $hash->{STATE} = "DNS error";
@@ -523,11 +527,11 @@ sub withings_getSessionKey($) {
 
   return if( $hash->{SessionKey} && $hash->{SessionTimestamp} && gettimeofday() - $hash->{SessionTimestamp} < (60*60*24*7-3600) );
 
-  my $resolve = inet_aton("account.health.nokia.com");
+  my $resolve = inet_aton("account.withings.com");
   if(!defined($resolve))
   {
     $hash->{SessionTimestamp} = 0;
-    Log3 "withings", 1, "$name: DNS error on getSessionData";
+    Log3 $name, 1, "$name: DNS error on getSessionData";
     return undef;
   }
 
@@ -537,24 +541,24 @@ sub withings_getSessionKey($) {
   # if( !defined($hash->{helper}{appliver}) || !defined($hash->{helper}{csrf_token}) || !defined($hash->{SessionTimestamp}) || gettimeofday() - $hash->{SessionTimestamp} > (30*60) )#!defined($hash->{helper}{appliver}) || !defined($hash->{helper}{csrf_token}))
   # {
   #   my($err0,$data0) = HttpUtils_BlockingGet({
-  #     url => $hash->{'.https'}."://scalews.health.nokia.com/",
+  #     url => $hash->{'.https'}."://account.withings.com/",
   #     timeout => 10,
   #     noshutdown => 1,
   #   });
   #   if($err0 || !defined($data0))
   #   {
-  #     Log3 "withings", 1, "$name: appliver call failed! ".$err0;
+  #     Log3 $name, 1, "$name: appliver call failed! ".$err0;
   #     return undef;
   #   }
   #   $data1 = $data0;
   #   $data0 =~ /appliver=([^.*]+)\&/;
   #   $hash->{helper}{appliver} = $1;
   #   if(!defined($hash->{helper}{appliver})) {
-  #     Log3 "withings", 1, "$name: APPLIVER ERROR ";
+  #     Log3 $name, 1, "$name: APPLIVER ERROR ";
   #     $hash->{STATE} = "APPLIVER error";
   #     return undef;
   #   }
-  #   Log3 "withings", 4, "$name: appliver ".$hash->{helper}{appliver};
+  #   Log3 $name, 4, "$name: appliver ".$hash->{helper}{appliver};
   # #}
   # 
   # 
@@ -564,11 +568,11 @@ sub withings_getSessionKey($) {
   #   $hash->{helper}{csrf_token} = $1;
   #   
   #   if(!defined($hash->{helper}{csrf_token})) {
-  #     Log3 "withings", 1, "$name: CSRF ERROR ";
+  #     Log3 $name, 1, "$name: CSRF ERROR ";
   #     $hash->{STATE} = "CSRF error";
   #     return undef;
   #   }
-  #   Log3 "withings", 4, "$name: csrf_token ".$hash->{helper}{csrf_token};
+  #   Log3 $name, 4, "$name: csrf_token ".$hash->{helper}{csrf_token};
   #}
 
     #my $ua = LWP::UserAgent->new;
@@ -577,26 +581,26 @@ sub withings_getSessionKey($) {
     #$request->content($get_data);
     #my $response = $ua->request($request);
 
-    # $resolve = inet_aton("account.health.nokia.com");
+    # $resolve = inet_aton("account.withings.com");
     # if(!defined($resolve))
     # {
-    #   Log3 "withings", 1, "$name: DNS error on getSessionKey.";
+    #   Log3 $name, 1, "$name: DNS error on getSessionKey.";
     #   return undef;
     # }
 
   my $datahash = {
-      url => $hash->{'.https'}."://account.health.nokia.com/connectionwou/account_login?r=https://dashboard.health.nokia.com/",
+      url => $hash->{'.https'}."://account.withings.com/connectionwou/account_login?r=https://healthmate.withings.com/",
     timeout => 10,
     noshutdown => 1,
     ignoreredirects => 1,
-      data => { email=> withings_decrypt($hash->{helper}{username}), password => withings_decrypt($hash->{helper}{password}), is_admin => '' },
+      data => { email=> withings_decrypt($hash->{helper}{username}), password => withings_decrypt($hash->{helper}{password}), is_admin => 'f' },
   };
 
   my($err,$data) = HttpUtils_BlockingGet($datahash);
 
   if ($err || !defined($data) || $data =~ /Authentification failed/ || $data =~ /not a valid/)
   {
-    Log3 "withings", 1, "$name: LOGIN ERROR ";
+    Log3 $name, 1, "$name: LOGIN ERROR ";
     $hash->{STATE} = "Login error";
     return undef;
   }
@@ -608,12 +612,12 @@ sub withings_getSessionKey($) {
       $hash->{SessionTimestamp} = (gettimeofday())[0] if( $hash->{SessionKey} );
       $hash->{STATE} = "Connected" if( $hash->{SessionKey} );
       $hash->{STATE} = "Session error" if( !$hash->{SessionKey} );
-      Log3 "withings", 4, "$name: sessionkey ".$hash->{SessionKey};
+      Log3 $name, 4, "$name: sessionkey ".$hash->{SessionKey};
     }
     else
     {
       $hash->{STATE} = "Cookie error";
-      Log3 "withings", 1, "$name: COOKIE ERROR ";
+      Log3 $name, 1, "$name: COOKIE ERROR ";
       $hash->{helper}{appliver} = '9855c478';
       $hash->{helper}{csrf_token} = '9855c478';
       return undef;
@@ -623,10 +627,10 @@ sub withings_getSessionKey($) {
   if( !$hash->{AccountID} || length($hash->{AccountID} < 2 ) ) {
 
     ($err,$data) = HttpUtils_BlockingGet({
-      url => $hash->{'.https'}."://scalews.withings.com/index/service/account",
+      url => $hash->{'.https'}."://scalews.withings.com/cgi-bin/account",
       timeout => 10,
       noshutdown => 1,
-      data => {sessionid => $hash->{SessionKey}, appname => 'my2', appliver=> $hash->{helper}{appliver}, apppfm => 'web', action => 'get'},
+      data => {sessionid => $hash->{SessionKey}, appname => 'my2', appliver=> $hash->{helper}{appliver}, apppfm => 'web', action => 'get', enrich => 't'},
     });
     return undef if(!defined($data));
 
@@ -648,15 +652,15 @@ sub withings_getSessionKey($) {
           }
           else
           {
-            Log3 "withings", 4, "$name: account email: ".$account->{email};
+            Log3 $name, 4, "$name: account email: ".$account->{email};
           }
       }
-      Log3 "withings", 4, "$name: accountid ".$hash->{AccountID};
+      Log3 $name, 4, "$name: accountid ".$hash->{AccountID};
     }
     else
     {
       $hash->{STATE} = "Account error";
-      Log3 "withings", 1, "$name: ACCOUNT ERROR ";
+      Log3 $name, 1, "$name: ACCOUNT ERROR ";
       return undef;
     }
   }
@@ -667,16 +671,15 @@ sub withings_getSessionKey($) {
 sub withings_connect($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: connect";
+  Log3 $name, 5, "$name: connect";
 
   $hash->{'.https'} = "https";
   $hash->{'.https'} = "http" if( AttrVal($name, "nossl", 0) );
 
-  #my $cmdret= CommandAttr(undef,"$name room WithingsTest");
-  #$cmdret= CommandAttr(undef,"$name verbose 5");
-
   withings_getSessionKey( $hash );
 
+  return undef; #no more autocreate on start
+
   foreach my $d (keys %defs) {
     next if(!defined($defs{$d}));
     next if($defs{$d}{TYPE} ne "autocreate");
@@ -691,11 +694,12 @@ sub withings_connect($) {
       Log3 $name, 2, "$name: user '$user->{id}' already defined";
       next;
     }
-    next if($user->{firstname} eq "Repository-User");
+    next if($user->{usertype} ne "1" || $user->{status} ne "0");
 
     my $id = $user->{id};
     my $devname = "withings_U". $id;
-    my $define= "$devname withings $id $user->{publickey}";
+    my $publickey = withings_encrypt($user->{publickey});
+    my $define= "$devname withings $id $publickey";
 
     Log3 $name, 2, "$name: create new device '$devname' for user '$id'";
 
@@ -761,7 +765,7 @@ sub withings_connect($) {
 sub withings_autocreate($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: autocreate";
+  Log3 $name, 5, "$name: autocreate";
 
   $hash->{'.https'} = "https";
   $hash->{'.https'} = "http" if( AttrVal($name, "nossl", 0) );
@@ -777,11 +781,12 @@ sub withings_autocreate($) {
       Log3 $name, 2, "$name: user '$user->{id}' already defined";
       next;
     }
-    next if($user->{firstname} eq "Repository-User");
+    next if($user->{usertype} ne "1" || $user->{status} ne "0");
 
     my $id = $user->{id};
     my $devname = "withings_U". $id;
-    my $define= "$devname withings $id $user->{publickey}";
+    my $publickey = withings_encrypt($user->{publickey});
+    my $define= "$devname withings $id $publickey";
 
     Log3 $name, 2, "$name: create new device '$devname' for user '$id'";
 
@@ -844,7 +849,7 @@ sub withings_autocreate($) {
 sub withings_initDevice($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: initdevice ".$hash->{Device};
+  Log3 $name, 5, "$name: initdevice ".$hash->{Device};
 
   AssignIoPort($hash);
   if(defined($hash->{IODev}->{NAME})) {
@@ -904,7 +909,7 @@ sub withings_initDevice($) {
 sub withings_initUser($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: inituser ".$hash->{User};
+  Log3 $name, 5, "$name: inituser ".$hash->{User};
 
   AssignIoPort($hash);
   if(defined($hash->{IODev}->{NAME})) {
@@ -936,12 +941,12 @@ sub withings_initUser($) {
 sub withings_getUsers($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: getusers";
+  Log3 $name, 5, "$name: getusers";
 
   withings_getSessionKey($hash);
 
   my ($err,$data) = HttpUtils_BlockingGet({
-    url => $hash->{'.https'}."://scalews.health.nokia.com/index/service/account",
+    url => $hash->{'.https'}."://scalews.withings.com/cgi-bin/account",
     timeout => 10,
     noshutdown => 1,
     data => {sessionid => $hash->{SessionKey}, accountid => $hash->{AccountID} , recurse_use => '1', recurse_devtype => '1', listmask => '5', allusers => 't' , appname => 'my2', appliver=> $hash->{helper}{appliver}, apppfm => 'web', action => 'getuserslist'},
@@ -977,7 +982,7 @@ sub withings_getUsers($) {
 sub withings_getDevices($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: getdevices";
+  Log3 $name, 5, "$name: getdevices";
 
   withings_getSessionKey($hash);
 
@@ -989,7 +994,7 @@ sub withings_getDevices($) {
   });
 
   #my $ua = LWP::UserAgent->new;
-  #my $request = HTTP::Request->new(POST => $hash->{'.https'}.'://healthmate.withings.com/index/service/association');
+  #my $request = HTTP::Request->new(POST => $hash->{'.https'}.'://scalews.withings.com/cgi-bin/association');
   #my $get_data = 'sessionid='.$hash->{SessionKey}.'&accountid='.$hash->{AccountID}.'&type=-1&enrich=t&appname=my2&appliver='.$hash->{helper}{appliver}.'&apppfm=web&action=getbyaccountid';
   #$request->content($get_data);
   #my $response = $ua->request($request);
@@ -1002,7 +1007,7 @@ sub withings_getDevices($) {
     return undef;
   }
   Log3 $name, 1, "withings: getDevices json error ".$json->{error} if(defined($json->{error}));
-  Log3 "withings", 5, "$name: getdevices ".Dumper($json);
+  Log3 $name, 5, "$name: getdevices ".Dumper($json);
 
   my @devices = ();
   foreach my $association (@{$json->{body}{associations}}) {
@@ -1018,19 +1023,19 @@ sub withings_getDevices($) {
 sub withings_getDeviceDetail($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: getdevicedetail ".$hash->{Device};
+  Log3 $name, 5, "$name: getdevicedetail ".$hash->{Device};
 
   return undef if( !defined($hash->{IODev}) );
   withings_getSessionKey( $hash->{IODev} );
 
   my ($err,$data) = HttpUtils_BlockingGet({
-    url => $hash->{'.https'}."://scalews.health.nokia.com/index/service/device",
+    url => $hash->{'.https'}."://scalews.withings.com/cgi-bin/device",
     timeout => 10,
     noshutdown => 1,
     data => {sessionid => $hash->{IODev}->{SessionKey}, deviceid => $hash->{Device} , appname => 'my2', appliver=> $hash->{IODev}->{helper}{appliver}, apppfm => 'web', action => 'getproperties'},
   });
 
-  #Log3 "withings", 5, "$name: getdevicedetaildata ".Dumper($data);
+  #Log3 $name, 5, "$name: getdevicedetaildata ".Dumper($data);
   return undef if(!defined($data));
 
   my $json = eval { JSON->new->utf8(0)->decode($data) };
@@ -1066,13 +1071,13 @@ sub withings_getDeviceDetail($) {
 sub withings_getDeviceLink($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: getdevicelink ".$hash->{Device};
+  Log3 $name, 5, "$name: getdevicelink ".$hash->{Device};
 
   return undef if( !defined($hash->{IODev}) );
   withings_getSessionKey( $hash->{IODev} );
 
   my ($err,$data) = HttpUtils_BlockingGet({
-    url => $hash->{'.https'}."://scalews.health.nokia.com/cgi-bin/association",
+    url => $hash->{'.https'}."://scalews.withings.com/cgi-bin/association",
     timeout => 10,
     noshutdown => 1,
     data => {sessionid => $hash->{IODev}->{SessionKey}, appname => 'hmw', appliver=> $hash->{IODev}->{helper}{appliver}, enrich => 't', action => 'getbyaccountid'},
@@ -1110,14 +1115,14 @@ sub withings_getDeviceProperties($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
 
-  Log3 "withings", 5, "$name: getdeviceproperties ".$hash->{Device};
+  Log3 $name, 5, "$name: getdeviceproperties ".$hash->{Device};
   return undef if( !defined($hash->{Device}) );
 
   return undef if( !defined($hash->{IODev}) );
   withings_getSessionKey( $hash->{IODev} );
 
   HttpUtils_NonblockingGet({
-    url => "https://scalews.health.nokia.com/index/service/device",
+    url => "https://scalews.withings.com/cgi-bin/device",
     timeout => 30,
     noshutdown => 1,
     data => {sessionid => $hash->{IODev}->{SessionKey}, deviceid=> $hash->{Device}, appname => 'my2', appliver => $hash->{IODev}->{helper}{appliver}, apppfm => 'web', action => 'getproperties'},
@@ -1140,7 +1145,7 @@ sub withings_getDeviceProperties($) {
 sub withings_getDeviceReadingsScale($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: getscalereadings ".$hash->{Device};
+  Log3 $name, 5, "$name: getscalereadings ".$hash->{Device};
   return undef if( !defined($hash->{Device}) );
 
   return undef if( !defined($hash->{IODev}) );
@@ -1153,7 +1158,7 @@ sub withings_getDeviceReadingsScale($) {
   $enddate = $now if ($enddate > $now);
 
   HttpUtils_NonblockingGet({
-    url => "https://scalews.health.nokia.com/cgi-bin/v2/measure",
+    url => "https://scalews.withings.com/cgi-bin/v2/measure",
     timeout => 30,
     noshutdown => 1,
     data => {sessionid => $hash->{IODev}->{SessionKey}, deviceid=> $hash->{Device}, meastype => '12,35', startdate => int($lastupdate), enddate => int($enddate), devicetype => '16', appname => 'my2', appliver => $hash->{IODev}->{helper}{appliver}, apppfm => 'web', action => 'getmeashf'},
@@ -1175,7 +1180,7 @@ sub withings_getDeviceReadingsScale($) {
 sub withings_getDeviceReadingsBedside($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: getaurareadings ".$hash->{Device};
+  Log3 $name, 5, "$name: getaurareadings ".$hash->{Device};
   return undef if( !defined($hash->{Device}) );
 
   return undef if( !defined($hash->{IODev}) );
@@ -1188,7 +1193,7 @@ sub withings_getDeviceReadingsBedside($) {
   $enddate = $now if ($enddate > $now);
 
   HttpUtils_NonblockingGet({
-    url => "https://scalews.health.nokia.com/cgi-bin/v2/measure",
+    url => "https://scalews.withings.com/cgi-bin/v2/measure",
     timeout => 30,
     noshutdown => 1,
     data => {sessionid => $hash->{IODev}->{SessionKey}, deviceid=> $hash->{Device}, meastype => '12,13,14,15,56', startdate => int($lastupdate), enddate => int($enddate), devicetype => '16', appname => 'my2', appliver => $hash->{IODev}->{helper}{appliver}, apppfm => 'web', action => 'getmeashf'},
@@ -1210,7 +1215,7 @@ sub withings_getDeviceReadingsBedside($) {
 sub withings_getDeviceReadingsHome($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: gethomereadings ".$hash->{Device};
+  Log3 $name, 5, "$name: gethomereadings ".$hash->{Device};
   return undef if( !defined($hash->{Device}) );
 
   return undef if( !defined($hash->{IODev}) );
@@ -1223,7 +1228,7 @@ sub withings_getDeviceReadingsHome($) {
   $enddate = $now if ($enddate > $now);
 
   HttpUtils_NonblockingGet({
-    url => "https://scalews.health.nokia.com/cgi-bin/v2/measure",
+    url => "https://scalews.withings.com/cgi-bin/v2/measure",
     timeout => 30,
     noshutdown => 1,
     data => {sessionid => $hash->{IODev}->{SessionKey}, deviceid=> $hash->{Device}, meastype => '12,13,14,15,58', startdate => int($lastupdate), enddate => int($enddate), devicetype => '16', appname => 'my2', appliver => $hash->{IODev}->{helper}{appliver}, apppfm => 'web', action => 'getmeashf'},
@@ -1245,7 +1250,7 @@ sub withings_getDeviceReadingsHome($) {
 sub withings_getDeviceEventsBaby($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: getbabyevents ".$hash->{Device};
+  Log3 $name, 5, "$name: getbabyevents ".$hash->{Device};
   return undef if( !defined($hash->{Device}) );
 
   return undef if( !defined($hash->{IODev}) );
@@ -1257,7 +1262,7 @@ sub withings_getDeviceEventsBaby($) {
 
 
   HttpUtils_NonblockingGet({
-    url => "https://scalews.health.nokia.com/index/service/event",
+    url => "https://scalews.withings.com/index/service/event",
     timeout => 30,
     noshutdown => 1,
     data => {activated => '0', action => 'get', sessionid => $hash->{IODev}->{SessionKey}, deviceid=> $hash->{Device}, type => '10,11,12,13,14,15,20', begindate => int($lastupdate)},
@@ -1280,7 +1285,7 @@ sub withings_getDeviceEventsBaby($) {
 sub withings_getDeviceAlertsHome($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: gethomealerts ".$hash->{Device};
+  Log3 $name, 5, "$name: gethomealerts ".$hash->{Device};
   return undef if( !defined($hash->{Device}) );
 
   return undef if( !defined($hash->{IODev}) );
@@ -1291,7 +1296,7 @@ sub withings_getDeviceAlertsHome($) {
   $lastupdate = $hash->{lastsessiondate} if(defined($hash->{lastsessiondate}) and $hash->{lastsessiondate} < $lastupdate);
 
   HttpUtils_NonblockingGet({
-    url => "https://scalews.withings.net/cgi-bin/v2/timeline",
+    url => "https://scalews.withings.com/cgi-bin/v2/timeline",
     timeout => 30,
     noshutdown => 1,
     data => {type => '1', callctx => 'foreground', action => 'getbydeviceid', appname => 'HomeMonitor', apppfm => 'ios', appliver => '20000', sessionid => $hash->{IODev}->{SessionKey}, deviceid=> $hash->{Device}, lastupdate => int($lastupdate) },
@@ -1313,7 +1318,7 @@ sub withings_getDeviceAlertsHome($) {
 sub withings_getDeviceAlertsBaby($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: getbabyevents ".$hash->{Device};
+  Log3 $name, 5, "$name: getbabyevents ".$hash->{Device};
   return undef if( !defined($hash->{Device}) );
 
   return undef if( !defined($hash->{IODev}) );
@@ -1324,7 +1329,7 @@ sub withings_getDeviceAlertsBaby($) {
   $lastupdate = $hash->{lastsessiondate} if(defined($hash->{lastsessiondate}) and $hash->{lastsessiondate} < $lastupdate);
 
   HttpUtils_NonblockingGet({
-    url => "https://scalews.health.nokia.com/index/service/event",
+    url => "https://scalews.withings.com/index/service/event",
     timeout => 30,
     noshutdown => 1,
     data => {activated => '1', action => 'get', sessionid => $hash->{IODev}->{SessionKey}, deviceid=> $hash->{Device}, type => '10,11,12,13,14,15,20', begindate => int($lastupdate)},
@@ -1345,7 +1350,7 @@ sub withings_getDeviceAlertsBaby($) {
 sub withings_getVideoLink($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: getbabyvideo ".$hash->{Device};
+  Log3 $name, 5, "$name: getbabyvideo ".$hash->{Device};
   return undef if( !defined($hash->{Device}) );
 
   return undef if( !defined($hash->{IODev}) );
@@ -1384,12 +1389,12 @@ sub withings_getS3Credentials($) {
   return undef if( $hash->{sts_expiretime} && $hash->{sts_expiretime} > time - 3600 ); # min 1h
 
   return undef if( !defined($hash->{IODev}) );
-  Log3 "withings", 5, "$name: gets3credentials ".$hash->{Device};
+  Log3 $name, 5, "$name: gets3credentials ".$hash->{Device};
 
   withings_getSessionKey( $hash->{IODev} );
 
   my ($err,$data) = HttpUtils_BlockingGet({
-    url => $hash->{'.https'}."://scalews.withings.net/cgi-bin/v2/device",
+    url => $hash->{'.https'}."://scalews.withings.com/cgi-bin/v2/device",
     timeout => 10,
     noshutdown => 1,
     data => {callctx => 'foreground', action => 'getsts', deviceid => $hash->{Device}, appname => 'HomeMonitor', apppfm => 'ios' , appliver => '20000', sessionid => $hash->{IODev}->{SessionKey}},
@@ -1442,7 +1447,7 @@ sub withings_signS3Link($$$;$) {
 sub withings_getUserDetail($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: getuserdetails ".$hash->{User};
+  Log3 $name, 5, "$name: getuserdetails ".$hash->{User};
   return undef if( !defined($hash->{User}) );
 
   return undef if( $hash->{SUBTYPE} ne "USER" );
@@ -1451,7 +1456,7 @@ sub withings_getUserDetail($) {
   withings_getSessionKey( $hash->{IODev} );
 
   my ($err,$data) = HttpUtils_BlockingGet({
-    url => $hash->{'.https'}."://scalews.health.nokia.com/index/service/user",
+    url => $hash->{'.https'}."://scalews.withings.com/index/service/user",
     timeout => 10,
     noshutdown => 1,
     data => {sessionid => $hash->{IODev}->{SessionKey}, userid => $hash->{User} , appname => 'my2', appliver => $hash->{IODev}->{helper}{appliver}, apppfm => 'web', action => 'getbyuserid'},
@@ -1481,7 +1486,7 @@ sub withings_poll($;$) {
   return undef if(IsDisabled($name));
 
 
-  #my $resolve = inet_aton("scalews.health.nokia.com");
+  #my $resolve = inet_aton("scalews.withings.com");
   #if(!defined($resolve))
   #{
   #  $hash->{STATE} = "DNS error";
@@ -1495,14 +1500,14 @@ sub withings_poll($;$) {
 
   if( $hash->{SUBTYPE} eq "DEVICE" ) {
     my $intervalData = AttrVal($name,"intervalData",900);
-    my $intervalDebug = AttrVal($name,"intervalDebug",900);
+    my $intervalDebug = AttrVal($name,"intervalDebug",AttrVal($name,"intervalData",900));
+    my $intervalProperties = AttrVal($name,"intervalProperties",AttrVal($name,"intervalData",900));
     my $lastData = ReadingsVal( $name, ".pollData", 0 );
     my $lastDebug = ReadingsVal( $name, ".pollDebug", 0 );
-    my $intervalProperties = AttrVal($name,"intervalProperties",43200);
     my $lastProperties = ReadingsVal( $name, ".pollProperties", 0 );
 
     if(defined($hash->{modelID}) && $hash->{modelID} eq '4') {
-      withings_getDeviceProperties($hash) if($force > 1 || $lastData <= ($now - $intervalData));
+      withings_getDeviceProperties($hash) if($force > 1 || $lastProperties <= ($now - $intervalProperties));
       withings_getDeviceReadingsScale($hash) if($force || $lastData <= ($now - $intervalData));
     }
     elsif(defined($hash->{modelID}) && $hash->{modelID} eq '21') {
@@ -1563,7 +1568,7 @@ sub withings_poll($;$) {
 sub withings_getUserReadingsDaily($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: getuserdailystats ".$hash->{User};
+  Log3 $name, 5, "$name: getuserdailystats ".$hash->{User};
 
   return undef if( !defined($hash->{IODev}) );
   withings_getSessionKey( $hash->{IODev} );
@@ -1577,7 +1582,7 @@ sub withings_getUserReadingsDaily($) {
   my $enddateymd = strftime("%Y-%m-%d", localtime($enddate));
 
   HttpUtils_NonblockingGet({
-    url => "https://scalews.health.nokia.com/cgi-bin/v2/aggregate",
+    url => "https://scalews.withings.com/cgi-bin/v2/aggregate",
     timeout => 60,
     noshutdown => 1,
     data => {sessionid => $hash->{IODev}->{SessionKey},  userid=> $hash->{User}, range => '1', meastype => '36,37,38,40,41,49,50,51,52,53,87', startdateymd => $startdateymd, enddateymd => $enddateymd, appname => 'my2', appliver => $hash->{IODev}->{helper}{appliver}, apppfm => 'web', action => 'getbyuserid'},
@@ -1595,7 +1600,7 @@ sub withings_getUserReadingsDaily($) {
   $enddateymd = strftime("%Y-%m-%d", localtime($enddate));
 
   HttpUtils_NonblockingGet({
-    url => "https://scalews.health.nokia.com/cgi-bin/v2/activity",
+    url => "https://scalews.withings.com/cgi-bin/v2/activity",
     timeout => 60,
     noshutdown => 1,
     data => {sessionid => $hash->{IODev}->{SessionKey},  userid=> $hash->{User}, subcategory => '37', startdateymd => $startdateymd, enddateymd => $enddateymd, appname => 'my2', appliver => $hash->{IODev}->{helper}{appliver}, apppfm => 'web', action => 'getbyuserid'},
@@ -1606,7 +1611,7 @@ sub withings_getUserReadingsDaily($) {
   });
 
 #  HttpUtils_NonblockingGet({
-#    url => "https://scalews.health.nokia.com/cgi-bin/v2/activity",
+#    url => "https://scalews.withings.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'},
@@ -1630,7 +1635,7 @@ sub withings_getUserReadingsDaily($) {
 sub withings_getUserReadingsCommon($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: getuserreadings ".$hash->{User};
+  Log3 $name, 5, "$name: getuserreadings ".$hash->{User};
 
   return undef if( !defined($hash->{IODev}) );
   withings_getSessionKey( $hash->{IODev} );
@@ -1641,7 +1646,7 @@ sub withings_getUserReadingsCommon($) {
   $enddate = $now if ($enddate > $now);
 
   HttpUtils_NonblockingGet({
-    url => "https://scalews.health.nokia.com/index/service/measure",
+    url => "https://scalews.withings.com/cgi-bin/measure",
     timeout => 60,
     noshutdown => 1,
     data => {sessionid => $hash->{IODev}->{SessionKey}, category => '1',  userid=> $hash->{User}, offset => '0', limit => '400', startdate => int($lastupdate), enddate => int($enddate), appname => 'my2', appliver => $hash->{IODev}->{helper}{appliver}, apppfm => 'web', action => 'getmeas'},
@@ -1666,7 +1671,7 @@ sub withings_getUserReadingsCommon($) {
 sub withings_getUserReadingsSleep($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: getsleepreadings ".$hash->{User};
+  Log3 $name, 5, "$name: getsleepreadings ".$hash->{User};
 
   return undef if( !defined($hash->{IODev}) );
   withings_getSessionKey( $hash->{IODev} );
@@ -1679,7 +1684,7 @@ sub withings_getUserReadingsSleep($) {
   #    data => {sessionid => $hash->{IODev}->{SessionKey}, userid=> $hash->{User}, meastype => '43,44,11,57,59,60,61,62,63,64,65,66,67,68,69,70', startdate => int($lastupdate), enddate => int($enddate), devicetype => '32', appname => 'my2', appliver => $hash->{IODev}->{helper}{appliver}, apppfm => 'web', action => 'getvasistas'},
 
   HttpUtils_NonblockingGet({
-    url => "https://scalews.health.nokia.com/cgi-bin/v2/measure",
+    url => "https://scalews.withings.com/cgi-bin/v2/measure",
     timeout => 60,
     noshutdown => 1,
     data => {sessionid => $hash->{IODev}->{SessionKey}, userid=> $hash->{User}, meastype => '11,39,41,43,44,57,59,87', startdate => int($lastupdate), enddate => int($enddate), devicetype => '32', appname => 'my2', appliver => $hash->{IODev}->{helper}{appliver}, apppfm => 'web', action => 'getvasistas'},
@@ -1701,7 +1706,7 @@ sub withings_getUserReadingsSleep($) {
 sub withings_getUserReadingsSleepDebug($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
-  Log3 "withings", 5, "$name: getsleepreadingsdebug ".$hash->{User};
+  Log3 $name, 5, "$name: getsleepreadingsdebug ".$hash->{User};
 
   return undef if( !defined($hash->{IODev}) );
   withings_getSessionKey( $hash->{IODev} );
@@ -1713,7 +1718,7 @@ sub withings_getUserReadingsSleepDebug($) {
   $enddate = $now if ($enddate > $now);
 
   HttpUtils_NonblockingGet({
-    url => "https://scalews.health.nokia.com/cgi-bin/v2/measure",
+    url => "https://scalews.withings.com/cgi-bin/v2/measure",
     timeout => 60,
     noshutdown => 1,
     data => {sessionid => $hash->{IODev}->{SessionKey}, userid=> $hash->{User}, meastype => '60,61,62,63,64,65,66,67,68,69,70', startdate => int($lastupdate), enddate => int($enddate), devicetype => '32', appname => 'my2', appliver => $hash->{IODev}->{helper}{appliver}, apppfm => 'web', action => 'getvasistas'},
@@ -1737,7 +1742,7 @@ sub withings_getUserReadingsActivity($) {
   my ($hash) = @_;
   my $name = $hash->{NAME};
 
-  Log3 "withings", 5, "$name: getactivityreadings ".$hash->{User};
+  Log3 $name, 5, "$name: getactivityreadings ".$hash->{User};
 
   return undef if( !defined($hash->{IODev}) );
   withings_getSessionKey( $hash->{IODev} );
@@ -1748,10 +1753,10 @@ sub withings_getUserReadingsActivity($) {
   my $enddate = ($lastupdate+(8*60*60));
   $enddate = $now if ($enddate > $now);
 
- Log3 "withings", 5, "$name: getactivityreadings ".$lastupdate." to ".$enddate;
+ Log3 $name, 5, "$name: getactivityreadings ".$lastupdate." to ".$enddate;
 
   HttpUtils_NonblockingGet({
-    url => "https://scalews.health.nokia.com/cgi-bin/v2/measure",
+    url => "https://scalews.withings.com/cgi-bin/v2/measure",
     timeout => 60,
     noshutdown => 1,
     data => {sessionid => $hash->{IODev}->{SessionKey}, userid=> $hash->{User}, meastype => '36,37,38,39,40,41,42,43,44,59,70,87,90', startdate => int($lastupdate), enddate => int($enddate), devicetype => '16', appname => 'my2', appliver => $hash->{IODev}->{helper}{appliver}, apppfm => 'web', action => 'getvasistas'},
@@ -1776,7 +1781,7 @@ sub withings_parseProperties($$) {
   my ($hash,$json) = @_;
   my $name = $hash->{NAME};
 
-  Log3 "withings", 5, "$name: parsedevice";
+  Log3 $name, 5, "$name: parsedevice";
 
   #parse
   my $detail = $json->{body};
@@ -1799,7 +1804,7 @@ sub withings_parseMeasureGroups($$) {
   my ($hash, $json) = @_;
   my $name = $hash->{NAME};
   #parse
-  Log3 "withings", 5, "$name: parsemeasuregroups";
+  Log3 $name, 5, "$name: parsemeasuregroups";
   my ($now) = int(time);
   my $lastupdate = ReadingsVal( $name, ".lastData", ($now-21*24*60*60) );
   my $newlastupdate = $lastupdate;
@@ -1831,6 +1836,7 @@ sub withings_parseMeasureGroups($$) {
           readingsBulkUpdate( $hash, $reading, $value, 1 );
           $hash->{CHANGETIME}[0] = FmtDateTime($measuregrp->{date});
           readingsEndUpdate($hash,1);
+          $i++;
         }
       }
 
@@ -1857,7 +1863,7 @@ sub withings_parseMeasureGroups($$) {
 
 
      delete $hash->{CHANGETIME};
-     Log3 $name, 3, "$name: got ".$i.' entries from MeasureGroups (latest: '.FmtDateTime($newlastupdate).')';
+     Log3 $name, (($i>0)?3:4), "$name: got ".$i.' entries from MeasureGroups (latest: '.FmtDateTime($newlastupdate).')';
 
     }
 
@@ -1867,7 +1873,7 @@ sub withings_parseMeasurements($$) {
   my ($hash, $json) = @_;
   my $name = $hash->{NAME};
   #parse
-  Log3 "withings", 4, "$name: parsemeasurements";
+  Log3 $name, 4, "$name: parsemeasurements";
   my ($now) = time;
   my $lastupdate = ReadingsVal( $name, ".lastData", ($now-21*24*60*60) );
   my $newlastupdate = $lastupdate;
@@ -1910,7 +1916,7 @@ sub withings_parseMeasurements($$) {
           readingsBulkUpdate( $hash, $reading->[1], $reading->[2], 1 );
           $hash->{CHANGETIME}[0] = FmtDateTime($reading->[0]);;
           readingsEndUpdate($hash,1);
-
+          $i++;
         }
 
 
@@ -1939,7 +1945,7 @@ sub withings_parseMeasurements($$) {
 
 
       delete $hash->{CHANGETIME};
-      Log3 $name, 3, "$name: got ".$i.' entries from Measurements (latest: '.FmtDateTime($newlastupdate).')';
+      Log3 $name, (($i>0)?3:4), "$name: got ".$i.' entries from Measurements (latest: '.FmtDateTime($newlastupdate).')';
 
 
     }
@@ -1955,7 +1961,7 @@ sub withings_parseAggregate($$) {
   my ($hash, $json) = @_;
   my $name = $hash->{NAME};
   #parse
-  Log3 "withings", 5, "$name: parseaggregate";
+  Log3 $name, 5, "$name: parseaggregate";
 
   #return undef;
   my ($now) = time;
@@ -2032,7 +2038,7 @@ sub withings_parseAggregate($$) {
           readingsBulkUpdate( $hash, $reading->[1], $reading->[2], 1 );
           $hash->{CHANGETIME}[0] = FmtDateTime($reading->[0]);
           readingsEndUpdate($hash,1);
-
+          $i++;
         }
 
       }
@@ -2055,7 +2061,7 @@ sub withings_parseAggregate($$) {
 
 
       delete $hash->{CHANGETIME};
-      Log3 $name, 4, "$name: got ".$i.' entries from Aggregate (latest: '.FmtDateTime($newlastupdate).')';
+      Log3 $name, (($i>0)?3:4), "$name: got ".$i.' entries from Aggregate (latest: '.FmtDateTime($newlastupdate).')';
 
 
     }
@@ -2070,7 +2076,7 @@ sub withings_parseActivity($$) {
   my ($hash, $json) = @_;
   my $name = $hash->{NAME};
   #parse
-  Log3 "withings", 5, "$name: parseactivity";
+  Log3 $name, 5, "$name: parseactivity";
 
   my ($now) = time;
   my $lastupdate = ReadingsVal( $name, ".lastActivity", ($now-21*24*60*60) );
@@ -2129,7 +2135,7 @@ sub withings_parseActivity($$) {
           readingsBulkUpdate( $hash, $reading->[1], $reading->[2], 1 );
           $hash->{CHANGETIME}[0] = FmtDateTime($reading->[0]);
           readingsEndUpdate($hash,1);
-
+          $i++;
         }
 
       }
@@ -2151,7 +2157,7 @@ sub withings_parseActivity($$) {
 
 
       delete $hash->{CHANGETIME};
-      Log3 $name, 4, "$name: got ".$i.' entries from Activity (latest: '.FmtDateTime($newlastupdate).')';
+      Log3 $name, (($i>0)?3:4), "$name: got ".$i.' entries from Activity (latest: '.FmtDateTime($newlastupdate).')';
 
 
     }
@@ -2166,7 +2172,7 @@ sub withings_parseWorkouts($$) {
   my ($hash, $json) = @_;
   my $name = $hash->{NAME};
   #parse
-  Log3 "withings", 1, "$name: parseworkouts\n".Dumper($json);
+  Log3 $name, 1, "$name: parseworkouts\n".Dumper($json);
 
 return undef;
 
@@ -2179,7 +2185,7 @@ sub withings_parseVasistas($$;$) {
   my ($hash, $json, $datatype) = @_;
   my $name = $hash->{NAME};
   #parse
-  Log3 "withings", 5, "$name: parsevasistas";
+  Log3 $name, 5, "$name: parsevasistas";
 
   my ($now) = time;
   my $lastupdate = ReadingsVal( $name, ".lastData", ($now-21*24*60*60) );
@@ -2195,7 +2201,7 @@ sub withings_parseVasistas($$;$) {
     my $readingsdate;
     my $newlastupdate = $lastupdate;
 
-
+    my $iscurrent = 0;
 
     foreach my $series ( @{$json->{body}{series}}) {
       $j=0;
@@ -2249,8 +2255,21 @@ sub withings_parseVasistas($$;$) {
 
           if($updatetype ne "unknown") {
             $newlastupdate = $readingsdate if($readingsdate > $newlastupdate);
+            $i++;
           }
-
+#start in-bed detection
+          if($iscurrent == 0 && $datatype =~ /Sleep/){
+            if($i>40 && $readingsdate > time()-3600){
+              $iscurrent = 1;
+              #Log3 $name, 1, "$name: in-bed: ".FmtDateTime($readingsdate) if($i>0);
+              readingsBeginUpdate($hash);
+              $hash->{".updateTimestamp"} = FmtDateTime($readingsdate);
+              readingsBulkUpdate( $hash, "in_bed", 1, 1 );
+              $hash->{CHANGETIME}[0] = FmtDateTime($readingsdate);
+              readingsEndUpdate($hash,1);
+            }
+          }
+#end in-bed detection
         }
       }
     }
@@ -2261,6 +2280,15 @@ sub withings_parseVasistas($$;$) {
       $newlastupdate = $device->{lastsessiondate} if($device->{lastsessiondate} and $device->{lastsessiondate} < $newlastupdate);
       $newlastupdate = $device->{lastweighindate} if($device->{lastweighindate} and $device->{lastweighindate} < $newlastupdate);
 
+      #start in-bed detection
+      if($datatype =~ /Sleep/ && $iscurrent == 0){
+        if($device->{lastweighindate} > (time()-1800)){
+          readingsSingleUpdate( $hash, "in_bed", 1, 1 );
+        } else {
+          readingsSingleUpdate( $hash, "in_bed", 0, 1 );
+        }
+      }
+      #end in-bed detection
     }
     $newlastupdate = $now if($newlastupdate > $now);
     if($newlastupdate < ($lastupdate-1))
@@ -2282,7 +2310,7 @@ sub withings_parseVasistas($$;$) {
      readingsSingleUpdate( $hash, ".lastData", $newlastupdate, 0 );
    }
 
-    Log3 $name, 4, "$name: got ".$i.' entries from Vasistas (latest: '.FmtDateTime($newlastupdate).')';
+    Log3 $name, (($i>0)?3:4), "$name: got ".$i.' entries from Vasistas (latest: '.FmtDateTime($newlastupdate).')';
 
     }
   }
@@ -2295,7 +2323,7 @@ sub withings_parseTimeline($$) {
   my ($hash, $json) = @_;
   my $name = $hash->{NAME};
   #parse
-  Log3 "withings", 5, "$name: parsemetimeline ";
+  Log3 $name, 5, "$name: parsemetimeline ";
 
   my ($now) = time;
   my $lastupdate = ReadingsVal( $name, ".lastAlert", ($now-21*24*60*60) );
@@ -2319,12 +2347,12 @@ sub withings_parseTimeline($$) {
         }
         elsif($event->{class} eq 'deleted')
         {
-          Log3 "withings", 5, "withings: event " . FmtDateTime($event->{epoch})." Event was deleted";
+          Log3 $name, 5, "withings: event " . FmtDateTime($event->{epoch})." Event was deleted";
           next;
         }
         elsif($event->{class} ne 'noise_detected' && $event->{class} ne 'movement_detected' && $event->{class} ne 'alert_environment' && $event->{class} ne 'offline' && $event->{class} ne 'online' && $event->{class} ne 'snapshot')
         {
-          Log3 "withings", 2, "withings: alert class unknown " . $event->{class};
+          Log3 $name, 2, "withings: alert class unknown " . $event->{class};
           next;
         }
 
@@ -2344,7 +2372,7 @@ sub withings_parseTimeline($$) {
           readingsBulkUpdate( $hash, $reading, $value, 1 );
           $hash->{CHANGETIME}[0] = FmtDateTime($event->{epoch});
           readingsEndUpdate($hash,1);
-
+          $i++;
         }
         if(AttrVal($name,"videoLinkEvents",0) eq "1")
         {
@@ -2375,7 +2403,7 @@ sub withings_parseTimeline($$) {
 
 
      delete $hash->{CHANGETIME};
-     Log3 $name, 4, "$name: got ".$i.' entries from Timeline (latest: '.FmtDateTime($newlastupdate).')';
+     Log3 $name, (($i>0)?3:4), "$name: got ".$i.' entries from Timeline (latest: '.FmtDateTime($newlastupdate).')';
 
     }
 
@@ -2386,7 +2414,7 @@ sub withings_parseEvents($$) {
   my ($hash, $json) = @_;
   my $name = $hash->{NAME};
   #parse
-  Log3 "withings", 5, "$name: parseevents";
+  Log3 $name, 5, "$name: parseevents";
   my ($now) = time;
   my $lastupdate = ReadingsVal( $name, ".lastData", ($now-21*24*60*60) );
   my $lastalertupdate = ReadingsVal( $name, ".lastAlert", ($now-21*24*60*60) );
@@ -2408,7 +2436,7 @@ sub withings_parseEvents($$) {
         $hash->{".updateTimestamp"} = FmtDateTime($event->{date});
         my $changeindex = 0;
 
-        #Log3 "withings", 5, "withings: event " . FmtDateTime($event->{date})." ".$event->{type}." ".$event->{activated}."/".$event->{measure}{value};
+        #Log3 $name, 5, "withings: event " . FmtDateTime($event->{date})." ".$event->{type}." ".$event->{activated}."/".$event->{measure}{value};
 
         my $reading = $event_types{$event->{type}}->{reading};
         my $value = "notice";
@@ -2449,7 +2477,7 @@ sub withings_parseEvents($$) {
 
 
         readingsEndUpdate($hash,1);
-
+        $i++;
       }
 
       if($newlastupdate == $lastupdate and $i == 0)
@@ -2480,7 +2508,7 @@ sub withings_parseEvents($$) {
 
 
       delete $hash->{CHANGETIME};
-      Log3 $name, 4, "$name: got ".$i.' entries from Events (latest: '.FmtDateTime($newlastupdate).')';
+      Log3 $name, (($i>0)?3:4), "$name: got ".$i.' entries from Events (latest: '.FmtDateTime($newlastupdate).')';
 
       }
 
@@ -2548,10 +2576,10 @@ sub withings_Get($$@) {
       my $users = withings_getUsers($hash);
       my $ret;
       foreach my $user (@{$users}) {
-        $ret .= "$user->{id}\t\[$user->{shortname}\]\t$user->{publickey}\t$user->{firstname} $user->{lastname}\n";
+        $ret .= "$user->{id}\t\[$user->{shortname}\]\t$user->{publickey}   \t$user->{usertype}/$user->{status}\t$user->{firstname} $user->{lastname}\n";
       }
 
-      $ret = "id\tshort\tpublickey\t\tname\n" . $ret if( $ret );;
+      $ret = "id\tshort\tpublickey\tusertype/status\tname\n" . $ret if( $ret );;
       $ret = "no users found" if( !$ret );
       return $ret;
     }
@@ -3170,7 +3198,7 @@ sub withings_Dispatch($$$) {
       Log3 $name, 2, "$name: json evaluation error on dispatch type ".$param->{type}." ".$@;
       return undef;
     }
-    Log3 $name, 1, "withings: Dispatch ".$param->{type}." json error ".$json->{error} if(defined($json->{error}));
+    Log3 $name, 1, "$name: Dispatch ".$param->{type}." json error ".$json->{error} if(defined($json->{error}));
 
     Log3 $name, 5, "$name: json returned: ".Dumper($json);
 

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1459 - 950
fhem/core/FHEM/34_ESPEasy.pm


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1091 - 0
fhem/core/FHEM/36_Shelly.pm


+ 87 - 63
fhem/core/FHEM/38_netatmo.pm

@@ -1,5 +1,5 @@
 ##############################################################################
-# $Id: 38_netatmo.pm 17294 2018-09-07 22:07:24Z moises $
+# $Id: 38_netatmo.pm 17485 2018-10-07 19:05:13Z moises $
 #
 #  38_netatmo.pm
 #
@@ -11,7 +11,7 @@
 #
 #
 ##############################################################################
-# Release 20 / 2018-09-09
+# Release 21 / 2018-10-06
 
 package main;
 
@@ -78,6 +78,8 @@ netatmo_Define($$)
   my $subtype;
   if($a[2] eq "WEBHOOK") {
     $subtype = "WEBHOOK";
+    $hash->{model} = "WEBHOOK";
+
     my $d = $modules{$hash->{TYPE}}{defptr}{"WEBHOOK"};
     return "Netatmo webkook already defined as $d->{NAME}" if( defined($d) && $d->{NAME} ne $name );
 
@@ -116,6 +118,7 @@ netatmo_Define($$)
 
     if( $a[3] && $a[3] =~ m/[\da-f]{2}(:[\da-f]{2}){5}/ )
     {
+      $hash->{model} = "PUBLIC";
 
       my $device = $a[3];
       $hash->{Device} = $device;
@@ -200,6 +203,8 @@ netatmo_Define($$)
       $hash->{Rad} = $rad;
 
       $subtype = "PUBLIC";
+      $hash->{model} = "WEATHERMAP";
+
       $modules{$hash->{TYPE}}{defptr}{$hash->{Lat}.$hash->{Lon}.$hash->{Rad}} = $hash;
 
       my $account = $modules{$hash->{TYPE}}{defptr}{"account"};
@@ -233,6 +238,7 @@ netatmo_Define($$)
 
   } elsif( ($a[2] eq "FORECAST" && @a == 4 ) ) {
     $subtype = "FORECAST";
+    $hash->{model} = "FORECAST";
 
     my $device = $a[3];
 
@@ -291,6 +297,7 @@ netatmo_Define($$)
 
   } elsif( ($a[2] eq "HEATINGHOME" && @a == 4 ) ) {
     $subtype = "HEATINGHOME";
+    $hash->{model} = "HEATINGHOME";
 
     my $home = $a[@a-1];
 
@@ -307,6 +314,7 @@ netatmo_Define($$)
 
   } elsif( ($a[2] eq "HEATINGROOM" && @a == 5 ) ) {
     $subtype = "HEATINGROOM";
+    $hash->{model} = "HEATINGROOM";
 
     my $room = $a[@a-1];
     my $home = $a[@a-2];
@@ -325,6 +333,7 @@ netatmo_Define($$)
 
   } elsif( ($a[2] eq "HOME" && @a == 4 ) ) {
     $subtype = "HOME";
+    $hash->{model} = "HOME";
 
     my $home = $a[@a-1];
 
@@ -341,6 +350,7 @@ netatmo_Define($$)
 
   } elsif( ($a[2] eq "PERSON" && @a == 5 ) ) {
     $subtype = "PERSON";
+    $hash->{model} = "PERSON";
 
     my $home = $a[@a-2];
     my $person = $a[@a-1];
@@ -389,6 +399,7 @@ netatmo_Define($$)
 
   } elsif( @a == 6  || ($a[2] eq "ACCOUNT" && @a == 7 ) ) {
     $subtype = "ACCOUNT";
+    $hash->{model} = "ACCOUNT";
     $hash->{network} = "ok";
 
     delete($hash->{access_token});
@@ -786,7 +797,7 @@ netatmo_refreshToken($;$)
   if( $nonblocking ) {
     HttpUtils_NonblockingGet({
       url => "https://".$hash->{helper}{apiserver}."/oauth2/token",
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       data => {grant_type => 'refresh_token', client_id => $hash->{helper}{client_id},  client_secret=> $hash->{helper}{client_secret}, refresh_token => $hash->{refresh_token}},
         hash => $hash,
@@ -836,7 +847,7 @@ netatmo_refreshAppToken($;$)
   if( $nonblocking ) {
     HttpUtils_NonblockingGet({
       url => "https://app.netatmo.net/oauth2/token",
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       header => "$auth",
       data => {grant_type => 'refresh_token', refresh_token => $hash->{refresh_token_app}},
@@ -891,14 +902,19 @@ netatmo_checkConnection($)
   my $name = $hash->{NAME};
 
   return undef if($hash->{network} eq "ok");
+  return undef if(!defined($hash->{access_token}));
 
   Log3 $name, 3, "$name: refreshing connection information";
 
+  my $json = '{"limit":2,"divider":3,"zoom":18,"lat_ne":0.1,"lon_ne":-0.1,"lat_sw":0.1,"lon_sw":-0.1,"date_end":"last","quality":1}';
 
   HttpUtils_NonblockingGet({
-    url => "https://".$hash->{helper}{apiserver}."/api/readtimeline",
-    timeout => 5,
+    url => "https://".$hash->{helper}{apiserver}."/api/getpublicmeasures",
+    method => "POST",
+    timeout => 30,
+    header => "Content-Type: application/json\r\nAuthorization: Bearer ".$hash->{access_token},
     hash => $hash,
+    data => $json,
     callback => \&netatmo_parseConnection,
   });
   return undef;
@@ -945,7 +961,12 @@ netatmo_parseConnection($$$)
         Log3 $name, 2, "$name: invalid json evaluation on connection check ".$@;
         return undef;
       }
-      $hash->{network} = "ok" if($json->{status} eq "ok");
+      if(!defined($json->{status})) {
+        Log3 $name, 2, "$name: invalid json data on connection check: \n".$data;
+      } else {
+        Log3 $name, 4, "$name: connection check: \n".$data;
+        $hash->{network} = "ok" if($json->{status} eq "ok");
+      }
     }
   return undef;
 }
@@ -998,7 +1019,7 @@ netatmo_parseUnban($$$)
 
   HttpUtils_NonblockingGet({
     url => "https://auth.netatmo.com/en-US/access/login?next_url=https://dev.netatmo.com/dev/myaccount",
-    timeout => 5,
+    timeout => 30,
     hash => $hash,
     ignoreredirects => 1,
     type => 'unban',
@@ -1048,7 +1069,7 @@ netatmo_parseUnban2($$$)
 
   HttpUtils_NonblockingGet({
     url => "https://dev.netatmo.com/api/unbanapp",
-    timeout => 5,
+    timeout => 30,
     hash => $hash,
     type => 'unban',
     header => "Referer: https://dev.netatmo.com/dev/myaccount\r\nAuthorization: Bearer ".$accesstoken."\r\nContent-Type: application/json;charset=utf-8\r\nCookie: netatmocomci_csrf_cookie_na=".$csrf_token."; netatmocomlocale=en-US; netatmocomacces_token=".$accesstoken,
@@ -1089,7 +1110,8 @@ netatmo_initDevice($)
 
   if(IsDisabled($name) || !defined($name)) {
     RemoveInternalTimer($hash);
-    $hash->{STATE} = "Disabled";
+    #$hash->{STATE} = "Disabled";
+    readingsSingleUpdate($hash, "active", "disabled", 1);
     return undef;
   }
 
@@ -1166,7 +1188,8 @@ netatmo_initDevice($)
 
   if(IsDisabled($name) || !defined($name)) {
     RemoveInternalTimer($hash);
-    $hash->{STATE} = "Disabled";
+    #$hash->{STATE} = "Disabled";
+    readingsSingleUpdate($hash, "active", "disabled", 1);
     return undef;
   }
 
@@ -1199,7 +1222,7 @@ netatmo_getDevices($;$)
   } else {
     HttpUtils_NonblockingGet({
       url => "https://".$hash->{helper}{apiserver}."/api/getstationsdata",
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       data => { access_token => $hash->{access_token}, },
       hash => $hash,
@@ -1234,7 +1257,7 @@ netatmo_getHomes($;$)
   } else {
     HttpUtils_NonblockingGet({
       url => "https://".$hash->{helper}{apiserver}."/api/gethomedata",
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       data => { access_token => $hash->{access_token}, },
       hash => $hash,
@@ -1269,7 +1292,7 @@ netatmo_getThermostats($;$)
   } else {
     HttpUtils_NonblockingGet({
       url => "https://".$hash->{helper}{apiserver}."/api/gethomesdata",
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       data => { access_token => $hash->{access_token}, },
       hash => $hash,
@@ -1306,7 +1329,7 @@ netatmo_getHomecoachs($;$)
   } else {
     HttpUtils_NonblockingGet({
       url => "https://".$hash->{helper}{apiserver}."/api/gethomecoachsdata",
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       data => { access_token => $hash->{access_token}, },
       hash => $hash,
@@ -1352,7 +1375,7 @@ netatmo_pingCamera($;$)
   } else {
     HttpUtils_NonblockingGet({
       url => $pingurl,
-      timeout => 10,
+      timeout => 30,
       sslargs => { SSL_hostname => '', },
       data => { access_token => $iohash->{access_token}, },
       hash => $hash,
@@ -1514,7 +1537,7 @@ netatmo_getEvents($)
 
   HttpUtils_NonblockingGet({
     url => "https://".$iohash->{helper}{apiserver}."/api/getnextevents",
-    timeout => 20,
+    timeout => 60,
     noshutdown => 1,
     data => { access_token => $iohash->{access_token}, home_id => $hash->{Home}, event_id => $hash->{lastevent}, },
     hash => $hash,
@@ -1568,19 +1591,19 @@ netatmo_getPublicDevices($$;$$$$)
 
   if( $blocking ) {
     my($err,$data) = HttpUtils_BlockingGet({
-      url => "https://".$iohash->{helper}{apiserver}."/api/getpublicdata",
-      timeout => 5,
+      url => "https://".$iohash->{helper}{apiserver}."/api/getpublicmeasures",
+      timeout => 10,
       noshutdown => 1,
-      data => { access_token => $iohash->{access_token}, lat_ne => $lat_ne, lon_ne => $lon_ne, lat_sw => $lat_sw, lon_sw => $lon_sw },
+      data => { access_token => $iohash->{access_token}, lat_ne => $lat_ne, lon_ne => $lon_ne, lat_sw => $lat_sw, lon_sw => $lon_sw, quality => 1, divider => 8, limit => 4, zoom => 10, date_end => 'last' },
     });
 
       return netatmo_dispatch( {hash=>$hash,type=>'publicdata'},$err,$data );
   } else {
     HttpUtils_NonblockingGet({
-      url => "https://".$iohash->{helper}{apiserver}."/api/getpublicdata",
-      timeout => 20,
+      url => "https://".$iohash->{helper}{apiserver}."/api/getpublicmeasures",
+      timeout => 60,
       noshutdown => 1,
-      data => { access_token => $iohash->{access_token}, lat_ne => $lat_ne, lon_ne => $lon_ne, lat_sw => $lat_sw, lon_sw => $lon_sw, filter => 'true' },
+      data => { access_token => $iohash->{access_token}, lat_ne => $lat_ne, lon_ne => $lon_ne, lat_sw => $lat_sw, lon_sw => $lon_sw, quality => 1, divider => 8, limit => 4, zoom => 10, date_end => 'last' },
       hash => $hash,
       type => 'publicdata',
       callback => \&netatmo_dispatch,
@@ -1697,7 +1720,7 @@ netatmo_requestDeviceReadings($@)
 
   HttpUtils_NonblockingGet({
     url => "https://".$iohash->{helper}{apiserver}."/api/getmeasure",
-    timeout => 20,
+    timeout => 60,
     noshutdown => 1,
     data => \%data,
     hash => $hash,
@@ -1730,7 +1753,7 @@ netatmo_initHome($@)
 #    data => \%data,
   HttpUtils_NonblockingGet({
     url => "https://app.netatmo.net/api/gethomesdata",
-    timeout => 20,
+    timeout => 30,
     noshutdown => 1,
     header => "Content-Type: application/json\r\nAuthorization: Bearer ".$iohash->{access_token_app},
     hash => $hash,
@@ -1766,7 +1789,7 @@ netatmo_requestHomeReadings($@)
 #    data => \%data,
   HttpUtils_NonblockingGet({
     url => "https://app.netatmo.net/api/gethomesdata",
-    timeout => 20,
+    timeout => 60,
     noshutdown => 1,
     header => "Content-Type: application/json\r\nAuthorization: Bearer ".$iohash->{access_token_app},
     hash => $hash,
@@ -1797,7 +1820,7 @@ netatmo_requestThermostatReadings($@)
 
   HttpUtils_NonblockingGet({
     url => "https://".$iohash->{helper}{apiserver}."/api/getthermostatsdata",
-    timeout => 20,
+    timeout => 60,
     noshutdown => 1,
     data => \%data,
     hash => $hash,
@@ -1832,7 +1855,7 @@ netatmo_initHeatingHome($@)
 #    data => \%data,
   HttpUtils_NonblockingGet({
     url => "https://app.netatmo.net/api/gethomesdata",
-    timeout => 20,
+    timeout => 30,
     noshutdown => 1,
     header => "Content-Type: application/json\r\nAuthorization: Bearer ".$iohash->{access_token_app},
     data => \%data,
@@ -1874,7 +1897,7 @@ netatmo_pollHeatingHome($@)
 #    data => \%data,
   HttpUtils_NonblockingGet({
     url => "https://my.netatmo.com/syncapi/v1/gethomestatus",
-    timeout => 20,
+    timeout => 60,
     noshutdown => 1,
     header => "Content-Type: application/json;charset=utf-8\r\nAuthorization: Bearer ".$iohash->{access_token},
     data => $json,
@@ -1928,7 +1951,7 @@ netatmo_pollHeatingRoom($@)
 #    data => \%data,
   HttpUtils_NonblockingGet({
     url => "https://app.netatmo.net/api/getroommeasure",
-    timeout => 20,
+    timeout => 60,
     noshutdown => 1,
     header => "Content-Type: application/json\r\nAuthorization: Bearer ".$iohash->{access_token_app},
     data => $json,
@@ -1980,7 +2003,7 @@ netatmo_setRoomMode($$;$)
 
   HttpUtils_NonblockingGet({
       url => "https://".$iohash->{helper}{apiserver}."/syncapi/v1/setthermpoint",
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       header => "Content-Type: application/json\r\nAuthorization: Bearer ".$iohash->{access_token},
       data => $json,
@@ -2018,7 +2041,7 @@ netatmo_setRoomTemp($$;$)
 
   HttpUtils_NonblockingGet({
       url => "https://".$iohash->{helper}{apiserver}."/syncapi/v1/setthermpoint",
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       header => "Content-Type: application/json\r\nAuthorization: Bearer ".$iohash->{access_token},
       data => $json,
@@ -2053,7 +2076,7 @@ netatmo_requestPersonReadings($)
 
   HttpUtils_NonblockingGet({
     url => "https://".$iohash->{helper}{apiserver}."/api/getlasteventof",
-    timeout => 20,
+    timeout => 60,
     noshutdown => 1,
     data => \%data,
     hash => $hash,
@@ -2099,7 +2122,7 @@ netatmo_setPresence($$)
 
   HttpUtils_NonblockingGet({
     url => "https://app.netatmo.net/api/setpersons".$urlstatus,
-    timeout => 20,
+    timeout => 30,
     noshutdown => 1,
     method => "POST",
     header => "Content-Type: application/json\r\nAuthorization: Bearer ".$iohash->{access_token_app},
@@ -2185,7 +2208,7 @@ netatmo_setNotifications($$$)
 
   HttpUtils_NonblockingGet({
     url => "https://app.netatmo.net/api/updatehome",
-    timeout => 20,
+    timeout => 30,
     noshutdown => 1,
     method => "POST",
     header => "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\nAuthorization: Bearer ".$iohash->{access_token_app},
@@ -2214,7 +2237,7 @@ netatmo_setCamera($$$)
 
   HttpUtils_NonblockingGet({
       url => $commandurl,
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       verify_hostname => 0,
       hash => $hash,
@@ -2242,7 +2265,7 @@ netatmo_setCameraSetting($$$)
 
   HttpUtils_NonblockingGet({
       url => $commandurl,
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       verify_hostname => 0,
       hash => $hash,
@@ -2270,7 +2293,7 @@ netatmo_setFloodlight($$)
 
   HttpUtils_NonblockingGet({
       url => $commandurl,
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       verify_hostname => 0,
       hash => $hash,
@@ -2299,7 +2322,7 @@ netatmo_setIntensity($$)
 
   HttpUtils_NonblockingGet({
       url => $commandurl,
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       verify_hostname => 0,
       hash => $hash,
@@ -2329,7 +2352,7 @@ netatmo_setPresenceConfig($$)
 
   HttpUtils_NonblockingGet({
       url => $commandurl,
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       verify_hostname => 0,
       hash => $hash,
@@ -2358,7 +2381,7 @@ netatmo_getPresenceConfig($)
 
   HttpUtils_NonblockingGet({
       url => $commandurl,
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       verify_hostname => 0,
       hash => $hash,
@@ -2394,7 +2417,7 @@ netatmo_setTagCalibration($$)
 
   HttpUtils_NonblockingGet({
       url => $commandurl,
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       verify_hostname => 0,
       hash => $hash,
@@ -2433,7 +2456,7 @@ netatmo_setThermostatMode($$;$)
 
   HttpUtils_NonblockingGet({
       url => "https://".$iohash->{helper}{apiserver}."/api/setthermpoint",
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       data => \%data,
       hash => $hash,
@@ -2466,7 +2489,7 @@ netatmo_setThermostatTemp($$;$$)
 
   HttpUtils_NonblockingGet({
       url => "https://".$iohash->{helper}{apiserver}."/api/setthermpoint",
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       data => \%data,
       hash => $hash,
@@ -2502,7 +2525,7 @@ netatmo_setThermostatProgram($$)
 
   HttpUtils_NonblockingGet({
       url => "https://".$iohash->{helper}{apiserver}."/api/switchschedule",
-      timeout => 20,
+      timeout => 30,
       noshutdown => 1,
       data => \%data,
       hash => $hash,
@@ -2522,7 +2545,8 @@ netatmo_poll($)
 
   if(IsDisabled($name) || !defined($name)) {
     RemoveInternalTimer($hash);
-    $hash->{STATE} = "Disabled";
+    #$hash->{STATE} = "Disabled";
+    readingsSingleUpdate($hash, "active", "disabled", 1);
     return undef;
   }
 
@@ -2635,7 +2659,7 @@ netatmo_dispatch($$$)
   $hash->{openRequests} -= 1 if( $param->{type} eq 'getmeasure' );
 
   if( $err ) {
-    Log3 $name, 2, "$name: http request failed: $err";
+    Log3 $name, 2, "$name: ".$param->{type}." request failed: $err";
     if($err =~ /refused/ ){
       RemoveInternalTimer($hash);
       InternalTimer(gettimeofday()+3600, "netatmo_poll", $hash);
@@ -3105,7 +3129,7 @@ netatmo_autocreatehomecoach($;$)
     if($cmdret) {
       Log3 $name, 1, "$name: Autocreate: An error occurred while creating device for id '$id': $cmdret";
     } else {
-      $cmdret= CommandAttr(undef,"$devname alias ".encode_utf8($device->{name})) if( defined($device->{name}) );
+      $cmdret= CommandAttr(undef,"$devname alias ".encode_utf8($device->{station_name})) if( defined($device->{station_name}) );
       $cmdret= CommandAttr(undef,"$devname room netatmo");
       $cmdret= CommandAttr(undef,"$devname IODev $name");
       $cmdret= CommandAttr(undef,"$devname devStateIcon .*:no-icon");
@@ -3466,7 +3490,7 @@ netatmo_parseReadings($$;$)
                 $hash->{helper}{NEXT_POLL} = $nextdata;
                 Log3 $name, 3, "$name: next extended dynamic update from device ($requested) at ".FmtDateTime($nextdata);
               } else {
-              Log3 $name, 2, "$name: invalid time for dynamic update from device ($requested): ".FmtDateTime($nextdata);
+                Log3 $name, 3, "$name: invalid time for dynamic update from device ($requested): ".FmtDateTime($nextdata);
             }
           }
         }
@@ -3714,14 +3738,14 @@ netatmo_parseGlobal($$)
             }
             if(defined($moduledata->{dashboard_data}{sum_rain_24}))
             {
-              my $rain_day = ReadingsVal($module->{NAME},"rain_day",0);
-              if($moduledata->{dashboard_data}{sum_rain_24} < $rain_day)
-              {
-                my $rain_total = ReadingsVal($module->{NAME},"rain_total",0);
-                $rain_total += $rain_day;
-                readingsSingleUpdate($module,"rain_total",$rain_total,1);
-                Log3 $name, 1, $module->{NAME}.":_added rain ".$rain_day." (to ".$rain_total.")";
-              }
+              # my $rain_day = ReadingsVal($module->{NAME},"rain_day",0);
+              # if($moduledata->{dashboard_data}{sum_rain_24} < $rain_day)
+              # {
+              #   my $rain_total = ReadingsVal($module->{NAME},"rain_total",0);
+              #   $rain_total += $rain_day;
+              #   readingsSingleUpdate($module,"rain_total",$rain_total,1);
+              #   Log3 $name, 1, $module->{NAME}.":_added rain ".$rain_day." (to ".$rain_total.")";
+              # }
               readingsBeginUpdate($module);
               $module->{".updateTimestamp"} = FmtDateTime($moduledata->{dashboard_data}{time_utc});
               readingsBulkUpdate( $module, "rain_day", $moduledata->{dashboard_data}{sum_rain_24}, 1 );
@@ -5558,7 +5582,7 @@ netatmo_pollGlobal($)
 
   HttpUtils_NonblockingGet({
       url => "https://".$hash->{helper}{apiserver}."/api/getstationsdata",
-      timeout => 20,
+      timeout => 60,
       noshutdown => 1,
       data => { access_token => $hash->{access_token}, },
       hash => $hash,
@@ -5582,7 +5606,7 @@ netatmo_pollGlobalHealth($)
 
   HttpUtils_NonblockingGet({
       url => "https://".$hash->{helper}{apiserver}."/api/gethomecoachsdata",
-      timeout => 20,
+      timeout => 60,
       noshutdown => 1,
       data => { access_token => $hash->{access_token}, },
       hash => $hash,
@@ -5621,7 +5645,7 @@ netatmo_pollForecast($)
 
   HttpUtils_NonblockingGet({
       url => "https://app.netatmo.net/api/simplifiedfuturemeasure",
-      timeout => 20,
+      timeout => 60,
       noshutdown => 1,
       data => { device_id => $hash->{Station}, },
       header => "Authorization: Bearer ".$iohash->{access_token_app},
@@ -5824,7 +5848,7 @@ netatmo_Get($$@)
       my $ret;
       foreach my $homecoach (@{$homecoachs}) {
         $ret .= "\n" if( $ret );
-        $ret .= "$homecoach->{_id}\t$homecoach->{firmware}\t$homecoach->{type}\t$homecoach->{name}";
+        $ret .= "$homecoach->{_id}\t$homecoach->{firmware}\t$homecoach->{type}\t$homecoach->{station_name}";
       }
 
       $ret = "id\t\t\tfw\ttype\t name\n" . $ret if( $ret );
@@ -6049,7 +6073,7 @@ netatmo_registerWebhook($)
   
   HttpUtils_NonblockingGet({
     url => "https://".$iohash->{helper}{apiserver}."/api/addwebhook",
-    timeout => 20,
+    timeout => 30,
     noshutdown => 1,
     data => { access_token => $iohash->{access_token}, url => $webhookurl, app_type => 'app_security', },
     hash => $hash,
@@ -6074,7 +6098,7 @@ netatmo_dropWebhook($)
   
   HttpUtils_NonblockingGet({
     url => "https://".$iohash->{helper}{apiserver}."/api/dropwebhook",
-    timeout => 20,
+    timeout => 30,
     noshutdown => 1,
     data => { access_token => $iohash->{access_token}, app_type => 'app_security', },
     hash => $hash,

+ 20 - 20
fhem/core/FHEM/50_HP1000.pm

@@ -1,5 +1,5 @@
 ###############################################################################
-# $Id: 50_HP1000.pm 17074 2018-08-01 19:00:53Z loredo $
+# $Id: 50_HP1000.pm 17527 2018-10-14 09:38:12Z loredo $
 package main;
 use strict;
 use warnings;
@@ -285,14 +285,12 @@ sub HP1000_Define($$$) {
       . " (there can only be one instance as per restriction of the weather station itself)"
       if ( defined( $modules{HP1000}{defptr} ) && !defined( $hash->{OLDDEF} ) );
 
-    # check FHEMWEB instance
+    # check FHEMWEB instance when user first defines the device
     if ( $init_done && !defined( $hash->{OLDDEF} ) ) {
         my $FWports;
         foreach ( devspec2array('TYPE=FHEMWEB:FILTER=TEMPORARY!=1') ) {
             $hash->{FW} = $_
               if ( AttrVal( $_, "webname", "fhem" ) eq "weatherstation" );
-            push( @{$FWports}, $defs{$_}->{PORT} )
-              if ( defined( $defs{$_}->{PORT} ) );
         }
 
         if ( !defined( $hash->{FW} ) ) {
@@ -315,8 +313,6 @@ sub HP1000_Define($$$) {
             }
         }
 
-        $hash->{FW_PORT} = $defs{ $hash->{FW} }{PORT};
-
         fhem 'attr ' . $name . ' stateReadings temperature humidity';
         fhem 'attr ' . $name . ' stateReadingsFormat 1';
     }
@@ -423,16 +419,31 @@ sub HP1000_CGI() {
         # get device name
         $name = $data{FWEXT}{"/updateweatherstation"}{deviceName}
           if ( defined( $data{FWEXT}{"/updateweatherstation"} ) );
+        $hash = $defs{$name};
 
         # return error if no such device
         return ( "text/plain; charset=utf-8",
             "No HP1000 device for webhook /updateweatherstation" )
           unless ( IsDevice( $name, 'HP1000' ) );
 
-        # incorrect FHEMWEB instance used
-        my @webhookFWinstances = split( ",",
-            AttrVal( $name, "webhookFWinstances", "WEBweatherstation" ) );
+        # Only allow data via explicitly named FHEMWEB instances,
+        # e.g. the user is taking care about correct incoming data
+        # routing to that instance via reverse proxy
+        $hash->{FW} = AttrVal( $name, "webhookFWinstances", "" );
+
+        # Otherwise, only allow data via FHEMWEB instance with
+        # webname 'weatherstation' as hardcoded in weatherstation firmware
+        if ( $hash->{FW} eq "" ) {
+            foreach ( devspec2array('TYPE=FHEMWEB:FILTER=TEMPORARY!=1') ) {
+                if ( AttrVal( $_, "webname", "fhem" ) eq "weatherstation" ) {
+                    $hash->{FW} = $_;
+                    last;
+                }
+            }
+        }
 
+        # incorrect FHEMWEB instance used
+        my @webhookFWinstances = split( ",", $hash->{FW} );
         return ( "text/plain; charset=utf-8",
             "incorrect FHEMWEB instance to receive data" )
           unless ( grep ( /^$FW_wname$/, @webhookFWinstances ) );
@@ -487,7 +498,6 @@ sub HP1000_CGI() {
         return ( "text/plain; charset=utf-8", "Missing data" );
     }
 
-    $hash = $defs{$name};
     my $uptime = time() - $fhem_started;
 
     delete $hash->{FORECASTDEV} if ( $hash->{FORECASTDEV} );
@@ -508,20 +518,10 @@ sub HP1000_CGI() {
         : 0
     );
     $hash->{SYSTEMTIME_UTC} = $webArgs->{dateutc};
-    $hash->{FW}             = "";
-    $hash->{FW_PORT}        = "";
     $hash->{UPLOAD_TYPE}    = "default";
     $hash->{UPLOAD_TYPE}    = "customize"
       if ( defined( $webArgs->{solarradiation} ) );
 
-    foreach ( devspec2array('TYPE=FHEMWEB:FILTER=TEMPORARY!=1') ) {
-        if ( AttrVal( $_, "webname", "fhem" ) eq "weatherstation" ) {
-            $hash->{FW}      = $_;
-            $hash->{FW_PORT} = $defs{$_}{PORT};
-            last;
-        }
-    }
-
     Log3 $name, 5,
       "HP1000: received data (uptime=$uptime):\n" . Dumper($webArgs);
 

+ 7 - 4
fhem/core/FHEM/51_MOBILEALERTS.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 51_MOBILEALERTS.pm 17226 2018-08-29 15:23:49Z MarkusF $
+# $Id: 51_MOBILEALERTS.pm 17494 2018-10-08 18:07:46Z MarkusF $
 # Written by Markus Feist, 2017
 package main;
 
@@ -1015,12 +1015,15 @@ sub MOBILEALERTS_decodeTemperature($) {
     #Illegal value
     return -9999 if ( ( $temperature & 0x1000 ) == 0x1000 );
 
+    #Clear flags
+    $temperature &= 0x7ff;
+
     #Negativ Values
-    return ( 0x800 - ( $temperature & 0x7ff ) ) * -0.1
-      if ( ( $temperature & 0x400 ) == 0x400 );
+    return ( 0x800 - $temperature ) * -0.1
+      if ( $temperature > 1100 );
 
     #Positiv Values
-    return ( $temperature & 0x7ff ) * 0.1;
+    return $temperature * 0.1;
 }
 
 sub MOBILEALERTS_temperatureToString($) {

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 986 - 740
fhem/core/FHEM/55_DWD_OpenData.pm


+ 87 - 19
fhem/core/FHEM/57_Calendar.pm

@@ -1,4 +1,4 @@
-# $Id: 57_Calendar.pm 16742 2018-05-15 19:20:16Z neubert $
+# $Id: 57_Calendar.pm 17531 2018-10-14 16:19:52Z neubert $
 ##############################################################################
 #
 #     57_Calendar.pm
@@ -1685,7 +1685,7 @@ sub Calendar_Initialize($) {
   $hash->{NotifyFn}= "Calendar_Notify";
   $hash->{AttrList}=  "update:sync,async,none removevcalendar:0,1 " .
   "cutoffOlderThan hideOlderThan hideLaterThan onCreateEvent " .
-  "ignoreCancelled:0,1 " .
+  "ignoreCancelled:0,1 quirks " .
   "SSLVerify:0,1 defaultFormat defaultTimeFormat " .
   $readingFnAttributes;
 }
@@ -2525,6 +2525,7 @@ sub Calendar_ProcessUpdate($$$) {
   my $name = $hash->{NAME};
   my $removeall = $param->{removeall};
   my $t= $param->{t};
+  my $type= $hash->{".fhem"}{type};
 
   if(exists($hash->{".fhem"}{subprocess})) {
       Log3 $hash, 2, "Calendar $name: update in progress, process aborted.";
@@ -2537,20 +2538,39 @@ sub Calendar_ProcessUpdate($$$) {
 
   delete($hash->{".fhem"}{iCalendar});
 
+  my $httpresponsecode= $param->{code};
+
   if($errmsg) {
     Log3 $name, 1, "Calendar $name: retrieval failed with error message $errmsg";
     readingsSingleUpdate($hash, "state", "error ($errmsg)", 1);
   } else {
-    readingsSingleUpdate($hash, "state", "retrieved", 1);
+    if($type eq "url") {
+      if($httpresponsecode != 200) {
+        $errmsg= "retrieval failed with HTTP response code $httpresponsecode";
+        Log3 $name, 1, "Calendar $name: $errmsg";
+        readingsSingleUpdate($hash, "state", "error ($errmsg)", 1);
+        Log3 $name, 5, "Calendar $name: HTTP response header:\n" .
+          $param->{httpheader};
+      } else {
+        Log3 $name, 5, "Calendar $name: HTTP response code $httpresponsecode";
+        readingsSingleUpdate($hash, "state", "retrieved", 1);
+      }
+    } elsif($type eq "file") {
+      Log3 $name, 5, "Calendar $name: file retrieval successful";
+      readingsSingleUpdate($hash, "state", "retrieved", 1);
+    } else {
+      # this case never happens by virtue of _Define, so just
+      die "Software Error";
+    }
   }
 
+  $hash->{".fhem"}{t}= $t;
   if($errmsg or !defined($ics) or ("$ics" eq "") ) {
     Log3 $hash, 1, "Calendar $name: retrieved no or empty data";
     readingsSingleUpdate($hash, "state", "error (no or empty data)", 1);
     Calendar_CheckAndRearm($hash);
   } else {
     $hash->{".fhem"}{iCalendar}= $ics; # the plain text iCalendar
-    $hash->{".fhem"}{t}= $t;
     $hash->{".fhem"}{removeall}= $removeall;
     if(AttrVal($name, "update", "sync") eq "async") {
       Calendar_AsynchronousUpdateCalendar($hash);
@@ -2699,11 +2719,15 @@ sub Calendar_UpdateCalendar($$) {
 
   my ($hash, $ical)= @_;
 
+  my $name= $hash->{NAME};
+
+  my @quirks= split(",", AttrVal($name, "quirks", ""));
+  my $nodtstamp= "ignoreDtStamp" ~~ @quirks;
+
   # *******************************
   # *** Step 1 Digest Parser Result
   # *******************************
 
-  my $name= $hash->{NAME};
   my $error= $ical->{error};
   my $state= $ical->{state};
 
@@ -2791,17 +2815,30 @@ sub Calendar_UpdateCalendar($$) {
         $cutoff= $t- $cutoffT;
     }
 
+
   foreach my $v (grep { $_->{type} eq "VEVENT" } @{$root->{entries}}) {
 
         # totally skip outdated calendar entries
-	next if(
-          defined($cutoffOlderThan) &&
-          $v->hasKey("DTEND") &&
-          $v->tm($v->value("DTEND")) < $cutoff &&
-          !$v->hasKey("RRULE")
-        );
-
-	#main::Debug "Merging " . $v->asString();
+        if($cutoffOlderThan) {
+          if(!$v->isRecurring()) {
+            # non recurring event
+            next if(
+              defined($cutoffOlderThan) &&
+              $v->hasKey("DTEND") &&
+              $v->tm($v->value("DTEND")) < $cutoff
+              );
+          } else {
+            # recurring event, inspect
+            my $rrule= $v->value("RRULE");
+            my @rrparts= split(";", $rrule);
+            my %r= map { split("=", $_); } @rrparts;
+            if(exists($r{"UNTIL"})) {
+              next if($v->tm($r{"UNTIL"}) < $cutoff)
+            }
+          }
+        }
+
+	      #main::Debug "Merging " . $v->asString();
         my $found= 0;
         my $added= 0; # flag to prevent multiple additions
         $n++;
@@ -2824,7 +2861,8 @@ sub Calendar_UpdateCalendar($$) {
                 #
                 # and same SEQUENCE
                 #
-                if($v0->sameValue($v, "LAST-MODIFIED")) {
+                if($v0->sameValue($v, "LAST-MODIFIED") &&
+                   ($nodtstamp || $v0->sameValue($v, "DTSTAMP"))) {
                     #
                     # is not modified
                     #
@@ -3154,6 +3192,10 @@ sub CalendarEventsAsHtml($;$) {
     replace it by <code>http://</code> if and only if there is no redirection to the <code>https://</code> URL.
     Check with your browser first if unsure.<br><br>
 
+    Note for users of Netxtcloud Calendar: you can use an URL of the form
+    <code>https://admin:admin@demo.nextcloud.com/wid0ohgh/remote.php/dav/calendars/admin/personal/?export</code>.
+    <p>
+
     The optional parameter <code>interval</code> is the time between subsequent updates
     in seconds. It defaults to 3600 (1 hour).<br><br>
 
@@ -3472,10 +3514,10 @@ sub CalendarEventsAsHtml($;$) {
         <p>
 
     <li><code>cutoffOlderThan &lt;timespec&gt;</code><br>
-        This attribute cuts off all non-recurring calendar events that ended a timespan cutoffOlderThan
+        This attribute cuts off all calendar events that ended a timespan cutoffOlderThan
         before the last update of the calendar. The purpose of setting this attribute is to save memory.
         Such calendar events cannot be accessed at all from FHEM. Calendar events are not cut off if
-        they are recurring or if they have no end time (DTEND).
+        they are recurring with no end of series (UNTIL) or if they have no end time (DTEND).
     </li><p>
 
     <li><code>onCreateEvent &lt;perl-code&gt;</code><br>
@@ -3498,6 +3540,15 @@ sub CalendarEventsAsHtml($;$) {
         although they are cancelled.
     </li><p>
 
+    <li><code>quirks &lt;values&gt;</code><br>
+        Parameters to handle special situations. <code>&lt;values&gt;</code> is
+        a comma-separated list of the following keywords:
+        <ul>
+          <li><code>ignoreDtStamp</code>: if present, a modified DTSTAMP attribute of a calendar event
+          does not signify that the calendar event was modified.</li>
+        </ul>
+    </li><p>
+
 
     <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
   </ul>
@@ -3622,7 +3673,7 @@ sub CalendarEventsAsHtml($;$) {
   <br>The double semicolon masks the semicolon. <a href="#perl">Perl specials</a> cannot be used.<br>
   <br>
   To add a missing end time, the following plug-in can be used:<br><br>
-  <code>attr MyCalendar onCreateEvent { $e->{end}= $e->{start}+86400 unless(defined($e->{summary})) }</code><br>
+  <code>attr MyCalendar onCreateEvent { $e->{end}= $e->{start}+86400 unless(defined($e->{end})) }</code><br>
   </ul>
   <br><br>
 
@@ -3782,6 +3833,11 @@ sub CalendarEventsAsHtml($;$) {
 	kannst Du in der URL  <code>https://</code> durch <code>http://</code> ersetzen, falls keine automatische Umleitung auf die <code>https://</code> URL erfolgt.
     Solltest Du unsicher sein, ob dies der Fall ist, &uuml;berpr&uuml;fe es bitte zuerst mit Deinem Browser.<br><br>
 
+    Hinweis f&uuml;r Nutzer des Nextcloud-Kalenders: Du kannst eine URL der folgenden Form benutzen:
+    <code>https://admin:admin@demo.nextcloud.com/wid0ohgh/remote.php/dav/calendars/admin/personal/?export</code>.<p>
+
+
+
     Der optionale Parameter <code>interval</code> bestimmt die Zeit in Sekunden zwischen den Updates. Default-Wert ist 3600 (1 Stunde).<br><br>
 
     Beispiele:
@@ -3931,9 +3987,10 @@ sub CalendarEventsAsHtml($;$) {
         <p>
 
     <li><code>cutoffOlderThan &lt;timespec&gt;</code><br>
-        Dieses Attribut schneidet alle nicht wiederkehrenden Termine weg, die eine Zeitspanne cutoffOlderThan
+        Dieses Attribut schneidet alle Termine weg, die eine Zeitspanne cutoffOlderThan
         vor der letzten Aktualisierung des Kalenders endeten. Der Zweck dieses Attributs ist es Speicher zu
-        sparen. Auf solche Termine kann gar nicht mehr aus FHEM heraus zugegriffen werden. Serientermine und
+        sparen. Auf solche Termine kann gar nicht mehr aus FHEM heraus zugegriffen
+        werden. Serientermine ohne Ende (UNTIL) und
         Termine ohne Endezeitpunkt (DTEND) werden nicht weggeschnitten.
     </li><p>
 
@@ -3959,6 +4016,17 @@ sub CalendarEventsAsHtml($;$) {
         Serie zur&uuml;ckgegeben werden, die gel&ouml;scht sind.
     </li><p>
 
+    <li><code>quirks &lt;values&gt;</code><br>
+        Parameter f&uuml;r spezielle Situationen. <code>&lt;values&gt;</code> ist
+        eine mit Kommas getrennte Liste der folgenden Schl&uuml;sselw&ouml;rter:
+        <ul>
+          <li><code>ignoreDtStamp</code>: wenn gesetzt, dann zeigt
+          ein ver&auml;ndertes DTSTAMP Attribut eines Termins nicht an, dass
+          der Termin ver&auml;ndert wurde.</li>
+        </ul>
+    </li><p>
+
+
 <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
   </ul>
   <br>

+ 7 - 5
fhem/core/FHEM/59_HCS.pm

@@ -1,5 +1,5 @@
 ################################################################
-# $Id: 59_HCS.pm 17162 2018-08-18 14:12:22Z hjr $
+# $Id: 59_HCS.pm 17435 2018-09-30 11:46:23Z hjr $
 # vim: ts=2:et
 #
 #  (c) 2012 Copyright: Martin Fischer (m_fischer at gmx dot de)
@@ -459,16 +459,16 @@ HCS_getValues($$) {
   foreach my $d (sort keys %defs) {
     my $t = $defs{$d}{TYPE};
     # skipping unneeded devices
-    next if($t ne "FHT" && $t ne "CUL_HM" && $t ne "MAX" && $t ne "ZWave");
+    next if($t ne "FHT" && $t ne "CUL_HM" && $t ne "HMCCUDEV" && $t ne "MAX" && $t ne "ZWave");
     next if($t eq "MAX" && !$defs{$d}{type});
     next if($t eq "MAX" && $defs{$d}{type} !~ m/HeatingThermostat/);
 
-    next if($t eq "CUL_HM" && ( !$attr{$d}{model}
-                || !( ($attr{$d}{model} eq "HM-CC-TC" && !$defs{$d}{device})
+    next if( ($t eq "CUL_HM" || $t eq "HMCCUDEV") && ( !$attr{$d}{model}
+                || !(  ($attr{$d}{model} eq "HM-CC-TC" && !$defs{$d}{device})
                     || ($attr{$d}{model} eq "HM-TC-IT-WM-W-EU" && !$defs{$d}{device})
                     || ($attr{$d}{model} eq "HmIP-WTH-2" && !$defs{$d}{device})
+                    || ($attr{$d}{model} eq "HmIP-eTRV" && !$defs{$d}{device}) 
                     || ($attr{$d}{model} eq "HM-CC-RT-DN" && !$defs{$d}{device}) )) );
-    
     next if($t eq "ZWave" && $attr{$d}{classes} !~ m/THERMOSTAT_SETPOINT/);
 
     $devs{$d}{actuator}     = ReadingsVal($d,"valveposition","n/a") if($t =~ m/(MAX)/);
@@ -484,9 +484,11 @@ HCS_getValues($$) {
     $devs{$d}{ignored}      = ($attr{$d}{ignore} && $attr{$d}{ignore} == 1) ? 1 : 0;
 
     $devs{$d}{tempDesired}  = ReadingsVal($d,"desired-temp","n/a")       if($t =~ m/(FHT|CUL_HM)/);
+    $devs{$d}{tempDesired}  = ReadingsVal($d,"1.SET_POINT_TEMPERATURE","n/a")   if($t =~ m/(HMCCUDEV)/);
     $devs{$d}{tempDesired}  = ReadingsVal($d,"desiredTemperature","n/a") if($t =~ m/(MAX)/);
     $devs{$d}{tempDesired}  = ReadingsNum($d,"setpointTemp","n/a",1)     if($t =~ m/(ZWave)/);
     $devs{$d}{tempMeasured} = ReadingsVal($d,"measured-temp","n/a")      if($t =~ m/(FHT|CUL_HM)/);
+    $devs{$d}{tempMeasured} = ReadingsVal($d,"1.ACTUAL_TEMPERATURE","n/a")      if($t =~ m/(HMCCUDEV)/);
     $devs{$d}{tempMeasured} = ReadingsNum($d,"temperature","n/a",1)      if($t =~ m/(MAX|ZWave)/);
 
     $devs{$d}{tempDesired}  = ($t =~ m/(FHT)/) ? 5.5 : 4.5               if($devs{$d}{tempDesired} eq "off");

+ 22 - 1
fhem/core/FHEM/59_LuftdatenInfo.pm

@@ -1,5 +1,5 @@
 # Id ##########################################################################
-# $Id: 59_LuftdatenInfo.pm 15273 2017-10-18 03:43:26Z igami $
+# $Id: 59_LuftdatenInfo.pm 17548 2018-10-16 19:33:15Z igami $
 
 # copyright ###################################################################
 #
@@ -437,6 +437,9 @@ sub LuftdatenInfo_ParseHttpResponse($) {
         elsif($_->{value_type} =~ /P2$/){
           $_->{value_type} = "PM2.5";
         }
+        elsif($_->{value_type} =~ /_air_quality$/){
+          $_->{value_type} = "airQuality";
+        }
         elsif($_->{value_type} =~ /_height$/){
           $_->{value_type} = "altitude";
         }
@@ -619,6 +622,15 @@ sub LuftdatenInfo_statusRequest($) {
     <b>Readings</b><br>
     <ul>
       <li>
+        <code>airQuality</code>
+        1 => good<br>
+        2 => moderate<br>
+        3 => unhealthy for sensitive groups<br>
+        4 => unhealthy<br>
+        5 => very unhealthy<br>
+        6 => hazardous<br>
+      </li>
+      <li>
         <code>altitude</code>
       </li>
       <li>
@@ -790,6 +802,15 @@ sub LuftdatenInfo_statusRequest($) {
     <b>Readings</b><br>
     <ul>
       <li>
+        <code>airQuality</code>
+        1 => gut<br>
+        2 => mittelmä&suml;ig<br>
+        3 => ungesund für empfindliche Menschen<br>
+        4 => ungesund<br>
+        5 => sehr ungesund<br>
+        6 => katastrophal<br>
+      </li>
+      <li>
         <code>altitude</code><br>
         Höhe über NN
       </li>

+ 717 - 0
fhem/core/FHEM/70_ZoneMinder.pm

@@ -0,0 +1,717 @@
+##############################################################################
+#
+#     70_ZoneMinder.pm
+#
+#     This file is part of Fhem.
+#
+#     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/>.
+#
+##############################################################################
+#  
+# ZoneMinder (c) Martin Gutenbrunner / https://github.com/delmar43/FHEM
+#
+# This module enables FHEM to interact with ZoneMinder surveillance system (see https://zoneminder.com)
+#
+# Discussed in FHEM Forum: https://forum.fhem.de/index.php/topic,91847.0.html
+#
+# $Id: 70_ZoneMinder.pm 17549 2018-10-17 02:55:08Z delmar $
+#
+##############################################################################
+
+package main;
+
+use strict;
+use warnings;
+use HttpUtils;
+use Crypt::MySQL qw(password41);
+use DevIo;
+use Digest::MD5 qw(md5 md5_hex md5_base64);
+
+sub ZoneMinder_Initialize {
+  my ($hash) = @_;
+  $hash->{NotifyOrderPrefix} = "70-";
+  $hash->{Clients} = "ZM_Monitor";
+
+  $hash->{GetFn}     = "ZoneMinder_Get";
+  $hash->{SetFn}     = "ZoneMinder_Set";
+  $hash->{DefFn}     = "ZoneMinder_Define";
+  $hash->{UndefFn}   = "ZoneMinder_Undef";
+  $hash->{ReadFn}    = "ZoneMinder_Read";
+  $hash->{ShutdownFn}= "ZoneMinder_Shutdown";
+  $hash->{FW_detailFn} = "ZoneMinder_DetailFn";
+  $hash->{WriteFn}   = "ZoneMinder_Write";
+  $hash->{ReadyFn}   = "ZoneMinder_Ready";
+
+  $hash->{AttrList} = "interval publicAddress webConsoleContext " . $readingFnAttributes;
+  $hash->{MatchList} = { "1:ZM_Monitor" => "^.*" };
+
+  Log3 '', 3, "ZoneMinder - Initialize done ...";
+}
+
+sub ZoneMinder_Define {
+  my ( $hash, $def ) = @_;
+  my @a = split( "[ \t][ \t]*", $def );
+  $hash->{NOTIFYDEV} = "global";
+
+  my $name   = $a[0];
+  $hash->{NAME} = $name;
+ 
+  my $nrArgs = scalar @a;
+  if ($nrArgs < 3) {
+    my $msg = "ZoneMinder ($name) - Wrong syntax: define <name> ZoneMinder <ZM_URL>";
+    Log3 $name, 2, $msg;
+    return $msg;
+  }
+
+  my $module = $a[1];
+  my $zmHost = $a[2];
+  $hash->{helper}{ZM_HOST} = $zmHost;
+  $zmHost .= ':6802' if (not $zmHost =~ m/:\d+$/);
+  $hash->{DeviceName} = $zmHost;
+
+  if ($nrArgs == 4 || $nrArgs > 6) {
+    my $msg = "ZoneMinder ($name) - Wrong syntax: define <name> ZoneMinder <ZM_URL> [<ZM_USERNAME> <ZM_PASSWORD>]";
+    Log3 $name, 2, $msg;
+    return $msg;
+  }
+ 
+  if ($nrArgs == 5 || $nrArgs == 6) {
+    $hash->{helper}{ZM_USERNAME} = $a[3];
+    $hash->{helper}{ZM_PASSWORD} = $a[4];
+  }
+
+#  Log3 $name, 3, "ZoneMinder ($name) - Define done ... module=$module, zmHost=$zmHost";
+
+  DevIo_CloseDev($hash) if (DevIo_IsOpen($hash));
+  DevIo_OpenDev($hash, 0, undef);
+
+  ZoneMinder_afterInitialized($hash);
+
+  return undef;
+}
+
+sub ZoneMinder_afterInitialized {
+  my ($hash) = @_;
+
+  ZoneMinder_API_Login($hash);
+
+  return undef;
+}
+
+# so far only used for generating the link to the ZM Web console
+# usePublic 0: zmHost, usePublic 1: publicAddress, usePublic undef: use public if publicAddress defined
+sub ZoneMinder_getZmWebUrl {
+  my ($hash, $usePublic) = @_;
+  my $name = $hash->{NAME};
+  
+  #use private or public LAN for Web access?
+  my $publicAddress = ZoneMinder_getPublicAddress($hash);
+  my $zmHost = '';
+#  Log3 $name, 0, "ZoneMinder ($name) - publicAddress: $publicAddress, usePublic: $usePublic";
+  if ($publicAddress and $usePublic) {
+    $zmHost = $publicAddress;
+  } else {
+    $zmHost = $hash->{helper}{ZM_HOST};
+    $zmHost = "http://$zmHost";
+  }
+  $zmHost .= '/' if (not $zmHost =~ m/\/$/);
+
+  my $zmWebContext = $attr{$name}{webConsoleContext};
+  if (not $zmWebContext) {
+    $zmWebContext = 'zm';
+  }
+  $zmHost .= $zmWebContext;
+  
+  return $zmHost;
+}
+
+sub ZoneMinder_getPublicAddress {
+  my ($hash) = @_;
+  my $name = $hash->{NAME};
+
+  return $attr{$name}{publicAddress};
+}
+
+# is built by using web-url, and adding /api
+sub ZoneMinder_getZmApiUrl {
+  my ($hash) = @_;
+  
+  #use private LAN for API access for a start
+  my $zmWebUrl = ZoneMinder_getZmWebUrl($hash, 0);
+  return "$zmWebUrl/api";
+}
+
+sub ZoneMinder_API_Login {
+  my ($hash) = @_;
+  my $name = $hash->{NAME};
+
+  my $username = urlEncode($hash->{helper}{ZM_USERNAME});
+  my $password = urlEncode($hash->{helper}{ZM_PASSWORD});
+
+  my $zmWebUrl = ZoneMinder_getZmWebUrl($hash);
+  my $loginUrl = "$zmWebUrl/index.php?username=$username&password=$password&action=login&view=console";
+
+  Log3 $name, 4, "ZoneMinder ($name) - loginUrl: $loginUrl";
+  my $apiParam = {
+    url => $loginUrl,
+    method => "POST",
+    callback => \&ZoneMinder_API_Login_Callback,
+    hash => $hash
+  };
+  HttpUtils_NonblockingGet($apiParam);
+  
+#  Log3 $name, 3, "ZoneMinder ($name) - ZoneMinder_API_Login err: $apiErr, data: $apiParam->{httpheader}";
+  
+  return undef;
+}
+
+sub ZoneMinder_API_Login_Callback {
+  my ($param, $err, $data) = @_;
+  my $hash = $param->{hash};
+  my $name = $hash->{NAME};
+
+  $hash->{APILoginStatus} = $param->{code};
+
+  if($err ne "") {
+    Log3 $name, 0, "error while requesting ".$param->{url}." - $err";
+    $hash->{APILoginError} = $err;
+  } elsif($data ne "") {
+    if ($data =~ m/Invalid username or password/) {
+      $hash->{APILoginError} = "Invalid username or password.";
+    } else {
+      delete($defs{$name}{APILoginError});
+      
+      ZoneMinder_GetCookies($hash, $param->{httpheader});
+
+      my $isFirst = !$hash->{helper}{apiInitialized};
+      if ($isFirst) {
+        $hash->{helper}{apiInitialized} = 1;
+        my $zmApiUrl = ZoneMinder_getZmApiUrl($hash);
+        ZoneMinder_SimpleGet($hash, "$zmApiUrl/host/getVersion.json", \&ZoneMinder_API_ReadHostInfo_Callback);
+        ZoneMinder_SimpleGet($hash, "$zmApiUrl/configs.json", \&ZoneMinder_API_ReadConfig_Callback);
+        ZoneMinder_API_getLoad($hash);
+      }
+    }
+  }
+
+  InternalTimer(gettimeofday() + 3600, "ZoneMinder_API_Login", $hash);
+  
+  return undef;
+}
+
+sub ZoneMinder_API_getLoad {
+  my ($hash) = @_;
+
+  my $zmApiUrl = ZoneMinder_getZmApiUrl($hash);
+  ZoneMinder_SimpleGet($hash, "$zmApiUrl/host/getLoad.json", \&ZoneMinder_API_ReadHostLoad_Callback);
+}
+
+sub ZoneMinder_SimpleGet {
+  my ($hash, $url, $callback) = @_;
+  my $name = $hash->{NAME};
+
+  my $apiParam = {
+    url => $url,
+    method => "GET",
+    callback => $callback,
+    hash => $hash
+  };
+
+  if ($hash->{HTTPCookies}) {
+    $apiParam->{header} .= "\r\n" if ($apiParam->{header});
+    $apiParam->{header} .= "Cookie: " . $hash->{HTTPCookies};
+  }
+
+  HttpUtils_NonblockingGet($apiParam);
+}
+
+sub ZoneMinder_API_ReadHostInfo_Callback {
+  my ($param, $err, $data) = @_;
+  my $hash = $param->{hash};
+  my $name = $hash->{NAME};
+
+  if($err ne "") {
+    Log3 $name, 0, "error while requesting ".$param->{url}." - $err";
+    $hash->{ZM_VERSION} = 'error';
+    $hash->{ZM_API_VERSION} = 'error';
+  } elsif($data ne "") {
+      
+      my $zmVersion = ZoneMinder_GetConfigValueByKey($hash, $data, 'version');
+      if (not $zmVersion) {
+        $zmVersion = 'unknown';
+      }
+      $hash->{ZM_VERSION} = $zmVersion;
+
+      my $zmApiVersion = ZoneMinder_GetConfigValueByKey($hash, $data, 'apiversion');
+      if (not $zmApiVersion) {
+        $zmApiVersion = 'unknown';
+      }
+      $hash->{ZM_API_VERSION} = $zmApiVersion;
+  }
+
+  return undef;
+}
+
+sub ZoneMinder_API_ReadHostLoad_Callback {
+  my ($param, $err, $data) = @_;
+  my $hash = $param->{hash};
+  my $name = $hash->{NAME};
+
+  if($err ne "") {
+    Log3 $name, 0, "error while requesting ".$param->{url}." - $err";
+    readingsSingleUpdate($hash, 'CPU_Load', 'error', 0);
+  } elsif($data ne "") {
+    my $load = ZoneMinder_GetConfigArrayByKey($hash, $data, 'load');
+    readingsSingleUpdate($hash, 'CPU_Load', $load, 1);
+
+    InternalTimer(gettimeofday() + 60, "ZoneMinder_API_getLoad", $hash);
+  }
+
+  return undef;
+}
+
+#this extracts ZM_PATH_ZMS and ZM_AUTH_HASH_SECRET from the ZoneMinder config
+sub ZoneMinder_API_ReadConfig_Callback {
+  my ($param, $err, $data) = @_;
+  my $hash = $param->{hash};
+  my $name = $hash->{NAME};
+
+  if($err ne "") {
+    Log3 $name, 0, "error while requesting ".$param->{url}." - $err";
+  } elsif($data ne "") {
+      my $zmPathZms = ZoneMinder_GetConfigValueByName($hash, $data, 'ZM_PATH_ZMS');
+      if ($zmPathZms) {
+        $zmPathZms =~ s/\\//g;
+        $hash->{helper}{ZM_PATH_ZMS} = $zmPathZms;
+      }
+
+      my $authHashSecret = ZoneMinder_GetConfigValueByName($hash, $data, 'ZM_AUTH_HASH_SECRET');
+      if ($authHashSecret) {
+        $hash->{helper}{ZM_AUTH_HASH_SECRET} = $authHashSecret;
+        ZoneMinder_calcAuthHash($hash);
+      }
+  }
+
+  return undef;
+}
+
+sub ZoneMinder_GetConfigValueByKey {
+  my ($hash, $config, $key) = @_;
+  my $searchString = '"'.$key.'":"';
+  return ZoneMinder_GetFromJson($hash, $config, $searchString, '"');
+}
+
+sub ZoneMinder_GetConfigArrayByKey {
+  my ($hash, $config, $key) = @_;
+  my $searchString = '"'.$key.'":[';
+  return ZoneMinder_GetFromJson($hash, $config, $searchString, ']');
+}
+
+sub ZoneMinder_GetConfigValueByName {
+  my ($hash, $config, $key) = @_;
+  my $searchString = '"Name":"'.$key.'","Value":"';
+  return ZoneMinder_GetFromJson($hash, $config, $searchString, '"');
+}
+
+sub ZoneMinder_GetFromJson {
+  my ($hash, $config, $searchString, $endChar) = @_;
+  my $name = $hash->{NAME};
+
+#  Log3 $name, 5, "json: $config";
+  my $searchLength = length($searchString);
+  my $startIdx = index($config, $searchString);
+  Log3 $name, 5, "$searchString found at $startIdx";
+  $startIdx += $searchLength;
+  my $endIdx = index($config, $endChar, $startIdx);
+  my $frame = $endIdx - $startIdx;
+  my $searchResult = substr $config, $startIdx, $frame;
+
+  Log3 $name, 5, "looking for $searchString - length: $searchLength. start: $startIdx. end: $endIdx. result: $searchResult";
+  
+  return $searchResult;
+}
+
+sub ZoneMinder_API_UpdateMonitors_Callback {
+  my ($param, $err, $data) = @_;
+  my $hash = $param->{hash};
+  my $name = $hash->{NAME};
+
+  my @monitors = split(/\{"Monitor"\:\{/, $data);
+
+  foreach my $monitorData (@monitors) {
+    my $monitorId = ZoneMinder_GetConfigValueByKey($hash, $monitorData, 'Id');
+
+    if ( $monitorId =~ /^[0-9]+$/ ) {
+      ZoneMinder_UpdateMonitorAttributes($hash, $monitorData, $monitorId);
+    } else {
+      Log3 $name, 0, "Invalid monitorId: $monitorId" unless ('itors' eq $monitorId);
+    }
+  }
+
+  return undef;
+}
+
+sub ZoneMinder_UpdateMonitorAttributes {
+  my ( $hash, $monitorData, $monitorId ) = @_;
+
+  my $function = ZoneMinder_GetConfigValueByKey($hash, $monitorData, 'Function');
+  my $enabled = ZoneMinder_GetConfigValueByKey($hash, $monitorData, 'Enabled');
+  my $streamReplayBuffer = ZoneMinder_GetConfigValueByKey($hash, $monitorData, 'StreamReplayBuffer');
+
+  my $msg = "monitor:$monitorId|$function|$enabled|$streamReplayBuffer";
+  
+  my $dispatchResult = Dispatch($hash, $msg, undef);
+}
+
+sub ZoneMinder_API_CreateMonitors_Callback {
+  my ($param, $err, $data) = @_;
+  my $hash = $param->{hash};
+  my $name = $hash->{NAME};
+
+  my @monitors = split(/\{"Monitor"\:\{/, $data);
+
+  foreach my $monitorData (@monitors) {
+    my $monitorId = ZoneMinder_GetConfigValueByKey($hash, $monitorData, 'Id');
+
+    if ( $monitorId =~ /^[0-9]+$/ ) {
+      my $dispatchResult = Dispatch($hash, "createMonitor:$monitorId", undef);
+    }
+  }
+  my $zmApiUrl = ZoneMinder_getZmApiUrl($hash);
+  ZoneMinder_SimpleGet($hash, "$zmApiUrl/monitors.json", \&ZoneMinder_API_UpdateMonitors_Callback);
+
+  return undef;
+}
+
+sub ZoneMinder_GetCookies {
+    my ($hash, $header) = @_;
+    my $name = $hash->{NAME};
+    foreach my $cookie ($header =~ m/set-cookie: ?(.*)/gi) {
+        $cookie =~ /([^,; ]+)=([^,; ]+)[;, ]*(.*)/;
+        $hash->{HTTPCookieHash}{$1}{Value} = $2;
+        $hash->{HTTPCookieHash}{$1}{Options} = ($3 ? $3 : "");
+    }
+    $hash->{HTTPCookies} = join ("; ", map ($_ . "=".$hash->{HTTPCookieHash}{$_}{Value},
+                                        sort keys %{$hash->{HTTPCookieHash}}));
+}
+
+sub ZoneMinder_Write {
+  my ( $hash, $arguments) = @_;
+  my $method = $arguments->{method};
+
+  if ($method eq 'changeMonitorFunction') {
+
+    my $zmMonitorId = $arguments->{zmMonitorId};
+    my $zmFunction = $arguments->{zmFunction};
+    Log3 $hash->{NAME}, 4, "method: $method, monitorId:$zmMonitorId, Function:$zmFunction";
+    return ZoneMinder_API_ChangeMonitorState($hash, $zmMonitorId, $zmFunction, undef);
+
+  } elsif ($method eq 'changeMonitorEnabled') {
+
+    my $zmMonitorId = $arguments->{zmMonitorId};
+    my $zmEnabled = $arguments->{zmEnabled};
+    Log3 $hash->{NAME}, 4, "method: $method, monitorId:$zmMonitorId, Enabled:$zmEnabled";
+    return ZoneMinder_API_ChangeMonitorState($hash, $zmMonitorId, undef, $zmEnabled);
+
+  } elsif ($method eq 'changeMonitorAlarm') {
+
+    my $zmMonitorId = $arguments->{zmMonitorId};
+    my $zmAlarm = $arguments->{zmAlarm};
+    Log3 $hash->{NAME}, 4, "method: $method, monitorId:$zmMonitorId, Alarm:$zmAlarm";
+    return ZoneMinder_Trigger_ChangeAlarmState($hash, $zmMonitorId, $zmAlarm);
+
+  } elsif ($method eq 'changeMonitorText') {
+
+    my $zmMonitorId = $arguments->{zmMonitorId};
+    my $zmText = $arguments->{text};
+    Log3 $hash->{NAME}, 4, "method: $method, monitorId:$zmMonitorId, Text:$zmText";
+    return ZoneMinder_Trigger_ChangeText($hash, $zmMonitorId, $zmText);
+
+  }
+
+  return undef;
+}
+
+sub ZoneMinder_API_ChangeMonitorState {
+  my ( $hash, $zmMonitorId, $zmFunction, $zmEnabled ) = @_;
+  my $name = $hash->{NAME};
+
+  my $zmApiUrl = ZoneMinder_getZmApiUrl($hash);
+  my $apiParam = {
+    url => "$zmApiUrl/monitors/$zmMonitorId.json",
+    method => "POST",
+    callback => \&ZoneMinder_API_ChangeMonitorState_Callback,
+    hash => $hash,
+    zmMonitorId => $zmMonitorId,
+    zmFunction => $zmFunction,
+    zmEnabled => $zmEnabled
+  };
+
+  if ( $zmFunction ) {
+    $apiParam->{data} = "Monitor[Function]=$zmFunction";
+  } elsif ( $zmEnabled || $zmEnabled eq '0' ) {
+    $apiParam->{data} = "Monitor[Enabled]=$zmEnabled";
+  }
+
+  if ($hash->{HTTPCookies}) {
+    $apiParam->{header} .= "\r\n" if ($apiParam->{header});
+    $apiParam->{header} .= "Cookie: " . $hash->{HTTPCookies};
+  }
+
+  HttpUtils_NonblockingGet($apiParam);
+
+  return undef;
+}
+
+sub ZoneMinder_API_ChangeMonitorState_Callback {
+  my ($param, $err, $data) = @_;  
+  my $hash = $param->{hash};
+  my $name = $hash->{NAME};
+  if ($data) {
+    my $monitorId = $param->{zmMonitorId};
+    my $logDevHash = $modules{ZM_Monitor}{defptr}{$name.'_'.$monitorId};
+    my $function = $param->{zmFunction};
+    my $enabled = $param->{zmEnabled};
+    Log3 $name, 4, "ZM_Monitor ($name) - ChangeMonitorState callback data: $data, enabled: $enabled";
+
+    if ($function) {
+      readingsSingleUpdate($logDevHash, 'monitorFunction', $function, 1);
+    } elsif ($enabled || $enabled eq '0') {
+      readingsSingleUpdate($logDevHash, 'motionDetectionEnabled', $enabled, 1);
+    }
+
+  } else {
+    Log3 $name, 2, "ZoneMinder ($name) - ChangeMonitorState callback err: $err";
+  }
+  
+  return undef;
+}
+
+sub ZoneMinder_Trigger_ChangeAlarmState {
+  my ( $hash, $zmMonitorId, $zmAlarm ) = @_;
+  my $name = $hash->{NAME};
+
+  my $msg = "$zmMonitorId|";
+  if ( 'on' eq $zmAlarm ) {
+    DevIo_SimpleWrite( $hash, $msg.'on|1|fhem', 2 );
+  } elsif ( 'off' eq $zmAlarm ) {
+    DevIo_SimpleWrite( $hash, $msg.'off|1|fhem', 2);
+  } elsif ( $zmAlarm =~ /^on\-for\-timer/ ) {
+    my $duration = $zmAlarm =~ s/on\-for\-timer\ /on\ /r;
+    DevIo_SimpleWrite( $hash, $msg.$duration.'|1|fhem', 2);
+  }
+
+  return undef;
+}
+
+sub ZoneMinder_Trigger_ChangeText {
+  my ( $hash, $zmMonitorId, $zmText ) = @_;
+  my $name = $hash->{NAME};
+
+  my $msg = "$zmMonitorId|show||||$zmText";
+  Log3 $name, 4, "ZoneMinder ($name) - Change Text $msg";
+  DevIo_SimpleWrite( $hash, $msg, 2 );
+
+  return undef;
+}
+
+sub ZoneMinder_calcAuthHash {
+  my ($hash) = @_;
+  my $name = $hash->{NAME};
+
+  Log3 $name, 4, "ZoneMinder ($name) - calling calcAuthHash";
+
+  my ($sec,$min,$curHour,$dayOfMonth,$curMonth,$curYear,$wday,$yday,$isdst) = localtime();
+
+  my $zmAuthHashSecret = $hash->{helper}{ZM_AUTH_HASH_SECRET};
+  if (not $zmAuthHashSecret) {
+    Log3 $name, 0, "ZoneMinder ($name) - calcAuthHash was called, but no hash secret was found. This shouldn't happen. Please contact the module maintainer.";
+    return undef;
+  }
+  my $username = $hash->{helper}{ZM_USERNAME};
+  my $password = $hash->{helper}{ZM_PASSWORD};
+  my $hashedPassword = password41($password);
+
+  my $authHash = $zmAuthHashSecret . $username . $hashedPassword . $curHour . $dayOfMonth . $curMonth . $curYear;
+  my $authKey = md5_hex($authHash);
+  
+  readingsSingleUpdate($hash, 'authHash', $authKey, 1);
+  InternalTimer(gettimeofday() + 3600, "ZoneMinder_calcAuthHash", $hash);
+
+  return undef;
+}
+
+sub ZoneMinder_Shutdown {
+  ZoneMinder_Undef(@_);
+}  
+
+sub ZoneMinder_Undef {
+  my ($hash, $arg) = @_; 
+  my $name = $hash->{NAME};
+
+  DevIo_CloseDev($hash) if (DevIo_IsOpen($hash));
+  RemoveInternalTimer($hash);
+
+  return undef;
+}
+
+sub ZoneMinder_Read {
+  my ($hash) = @_;
+  my $name = $hash->{NAME};
+
+  my $data = DevIo_SimpleRead($hash);
+  return if (!defined($data)); # connection lost
+
+  my $buffer = $hash->{PARTIAL};
+  $buffer .= $data;
+  #as long as the buffer contains newlines
+  while ($buffer =~ m/\n/) {
+    my $msg;
+    ($msg, $buffer) = split("\n", $buffer, 2);
+    chomp $msg;
+    $msg = "event:$msg";
+#    Log3 $name, 3, "ZoneMinder ($name) incoming message $msg.";
+    my $dispatchResult = Dispatch($hash, $msg, undef);
+  }
+  $hash->{PARTIAL} = $buffer;
+}
+
+sub ZoneMinder_DetailFn {
+  my ( $FW_wname, $deviceName, $FW_room ) = @_;
+
+  my $hash = $defs{$deviceName};
+
+  my $zmWebUrl = ZoneMinder_getZmWebUrl($hash, 1);
+  my $zmUsername = urlEncode($hash->{helper}{ZM_USERNAME});
+  my $zmPassword = urlEncode($hash->{helper}{ZM_PASSWORD});
+  my $zmConsoleUrl = "$zmWebUrl/index.php?username=$zmUsername&password=$zmPassword&action=login&view=console";
+
+  if ($zmConsoleUrl) {
+    return "<div><a href='$zmConsoleUrl' target='_blank'>Go to ZoneMinder console</a></div>";
+  } else {
+    return undef;
+  }
+}
+
+sub ZoneMinder_Get {
+  my ( $hash, $name, $opt, $args ) = @_;
+
+  my $zmApiUrl = ZoneMinder_getZmApiUrl($hash);
+  if ("autocreateMonitors" eq $opt) {
+    ZoneMinder_SimpleGet($hash, "$zmApiUrl/monitors.json", \&ZoneMinder_API_CreateMonitors_Callback);
+    return undef;
+  } elsif ("updateMonitorConfig" eq $opt) {
+    ZoneMinder_SimpleGet($hash, "$zmApiUrl/monitors.json", \&ZoneMinder_API_UpdateMonitors_Callback);
+    return undef;
+  } elsif ("calcAuthHash" eq $opt) {
+    ZoneMinder_calcAuthHash($hash);
+    return undef;
+  }
+
+#  Log3 $name, 3, "ZoneMinder ($name) - Get done ...";
+  return "Unknown argument $opt, choose one of autocreateMonitors updateMonitorConfig calcAuthHash";
+}
+
+sub ZoneMinder_Set {
+  my ( $hash, $param ) = @_;
+
+  my $name = $hash->{NAME};
+#  Log3 $name, 3, "ZoneMinder ($name) - Set done ...";
+  return undef;
+}
+
+sub ZoneMinder_Ready {
+  my ( $hash ) = @_;
+  my $name = $hash->{NAME};
+
+  if ( $hash->{STATE} eq "disconnected" ) {
+    return DevIo_OpenDev($hash, 1, undef ); #if success, $err is undef
+  }
+
+  # This is relevant for Windows/USB only
+  if(defined($hash->{USBDev})) {
+    my $po = $hash->{USBDev};
+    my ( $BlockingFlags, $InBytes, $OutBytes, $ErrorFlags ) = $po->status;
+    return ( $InBytes > 0 );
+  }
+}
+
+1;
+
+
+# Beginn der Commandref
+
+=pod
+=item device
+=item summary Maintain ZoneMinder events and monitor operation modes in FHEM
+=item summary_DE ZoneMinder events und Monitor Konfiguration in FHEM warten
+
+=begin html
+
+<a name="ZoneMinder"></a>
+<h3>ZoneMinder</h3>
+
+<a name="ZoneMinderdefine"></a>
+  <b>Define</b>
+  <ul>
+    <code>define &lt;name&gt; ZoneMinder  &lt;ZM-Host&gt; [&lt;username&gt; &lt;password&gt;]</code>
+    <br><br>
+    Defines a ZoneMinder device at the given host address. This allows you to exchange events between ZoneMinder and FHEM.
+    Also providing <code>username</code> and <code>password</code> provides access to ZoneMinder API and more functionality.
+    <br>
+    Example:
+    <ul>
+      <code>define zm ZoneMinder 10.0.0.100</code><br>
+      <code>define zm ZoneMinder 10.0.0.100 fhemApiUser fhemApiPass</code>
+    </ul>
+    <br>
+  </ul>
+  <br><br>
+
+  <a name="ZoneMinderget"></a>
+  <b>Get</b>
+  <ul>
+    <li><code>autocreateMonitors</code><br>Queries the ZoneMinder API and autocreates all ZM_Monitor devices that belong to that installation.
+    </li>
+    <li><code>updateMonitorConfig</code><br>Queries the ZoneMinder API and updates the Readings of ZM_Monitor devices (monitorFunction, motionDetectionEnabled, ...)
+    </li>
+    <li><code>calcAuthHash</code><br>Calculates a fresh auth hash. Please note that the hash only changes with every full hour. So, calling this doesn't necessarily change any Readings, depending on the age of the current hash.
+    </li>
+  </ul>
+
+  <br><br>
+  <a name="ZoneMinderattr"></a>
+  <b>Attributes</b>
+  <br><br>
+  <ul>
+    <li><code>publicAddress &lt;address&gt;</code><br>This configures public accessibility of your LAN (eg your ddns address). Define a valid URL here, eg <code>https://my.own.domain:2344</code></li>
+    <li><code>webConsoleContext &lt;path&gt;</code><br>If not set, this defaults to <code>/zm</code>. This is used for building the URL to the ZoneMinder web console.</li> 
+  </ul>
+
+  <br><br>
+  
+  <a name="ZoneMinderreadings"></a>
+  <b>Readings</b>
+  <br><br>
+  <ul>
+    <li>CPU_Load<br/>The CPU load of the ZoneMinder host. Provides 1, 5 and 15 minutes interval.</li>
+    <li>authHash<br/>The auth hash that allows access to Stream URLs without requiring username or password.</li>
+    <li>state<br/>The current connection state to the ZoneMinder Trigger Port (6802 per default)</li>
+  </ul>
+  
+
+=end html
+
+# Ende der Commandref
+=cut

+ 27 - 12
fhem/core/FHEM/71_YAMAHA_AVR.pm

@@ -1,4 +1,4 @@
-# $Id: 71_YAMAHA_AVR.pm 17193 2018-08-23 19:15:25Z markusbloch $
+# $Id: 71_YAMAHA_AVR.pm 17547 2018-10-16 18:17:01Z markusbloch $
 ##############################################################################
 #
 #     71_YAMAHA_AVR.pm
@@ -1884,7 +1884,12 @@ YAMAHA_AVR_ParseResponse($$$)
                     readingsBulkUpdateIfChanged($hash, "currentArtist", "");
                 }
 
-                if($data =~ /<Meta_Info>.*?<Station>(.+?)<\/Station>.*?<\/Meta_Info>/)
+
+                if($data =~ /<Band>DAB<\/Band>/ and $data =~ /<Meta_Info>.*?<Service_Label>(.+?)<\/Service_Label>.*?<\/Meta_Info>/) # RX-481D provides always Meta-Info for DAB and Tuner
+                {
+                    readingsBulkUpdate($hash, "currentStation", YAMAHA_AVR_html2txt($1));
+                }
+                elsif($data =~ /<Meta_Info>.*?<Station>(.+?)<\/Station>.*?<\/Meta_Info>/)
                 {
                     readingsBulkUpdate($hash, "currentStation", YAMAHA_AVR_html2txt($1));
                 }
@@ -1892,7 +1897,7 @@ YAMAHA_AVR_ParseResponse($$$)
                 {
                     readingsBulkUpdate($hash, "currentStation", YAMAHA_AVR_html2txt($1));
                 }
-                elsif($data =~ /<DAB>.*?<Meta_Info>.*?<Service_Label>(.+?)<\/Service_Label>.*?<\/Meta_Info>.*?<\/DAB>/)
+                elsif($data =~ /<Meta_Info>.*?<Station>(.+?)<\/Station>.*?<\/Meta_Info>/)
                 {
                     readingsBulkUpdate($hash, "currentStation", YAMAHA_AVR_html2txt($1));
                 }
@@ -1918,8 +1923,12 @@ YAMAHA_AVR_ParseResponse($$$)
                 {
                     readingsBulkUpdateIfChanged($hash, "currentAlbum", "");
                 }
-                
-                if($data =~ /<Meta_Info>.*?<Song>(.+?)<\/Song>.*?<\/Meta_Info>/)
+
+                if($data =~ /<Band>DAB<\/Band>/ and $data =~ /<Meta_Info>.*?<DLS>(.+?)<\/DLS>.*?<\/Meta_Info>/) # RX-481D provides always Meta-Info for DAB and FM
+                {
+                    readingsBulkUpdate($hash, "currentTitle", YAMAHA_AVR_html2txt($1));
+                }
+                elsif($data =~ /<Meta_Info>.*?<Song>(.+?)<\/Song>.*?<\/Meta_Info>/)
                 {
                     readingsBulkUpdate($hash, "currentTitle", YAMAHA_AVR_html2txt($1));
                 }
@@ -1943,11 +1952,15 @@ YAMAHA_AVR_ParseResponse($$$)
                 elsif($data =~ /<Meta_Info>.*?<Radio_Text_B>(.+?)<\/Radio_Text_B>.*?<\/Meta_Info>/)    
                 {         
                     readingsBulkUpdate($hash, "currentTitle", YAMAHA_AVR_html2txt($1));        
+                }
+                elsif($data =~ /<Meta_Info>.*?<Radio_Text>(.+?)<\/Radio_Text>.*?<\/Meta_Info>/) # RX-V481D
+                {
+                    readingsBulkUpdate($hash, "currentTitle", YAMAHA_AVR_html2txt($1));
                 }    
-                elsif($data =~ /<DAB>.*?<DLS>(.+?)<\/DLS>.*?<\/DAB>/)
-                {         
-                    readingsBulkUpdate($hash, "currentTitle", YAMAHA_AVR_html2txt($1));        
-                } 
+                                                                     
+                          
+                                                                                               
+                  
                 else
                 {
                     readingsBulkUpdateIfChanged($hash, "currentTitle", "");
@@ -1964,12 +1977,14 @@ YAMAHA_AVR_ParseResponse($$$)
                     readingsBulkUpdate($hash, "playStatus", "playing");
                 }
                 
-                if($data =~ /<Tuning>.*?<Freq>(?:<Current>)?<Val>(\d+?)<\/Val><Exp>(\d+?)<\/Exp><Unit>(.*?)<\/Unit>(?:<\/Current>)?.*<\/Tuning>/ or $data =~ /<DAB>.*?<Signal_Info>.*?<Freq><Val>(\d+?)<\/Val><Exp>(\d+?)<\/Exp><Unit>(.*?)<\/Unit><\/Freq>.*<\/Signal_Info>.*<\/DAB>/ or(YAMAHA_AVR_isModel_DSP($hash) and $data =~ /<Tuning>.*?<Freq><Val>(\d+?)<\/Val><Exp>(\d+?)<\/Exp><Unit>(.*?)<\/Unit><\/Freq>.*?<\/Tuning>/))
+                if(($data =~ /<Band>DAB<\/Band>/ and $data =~ /<DAB>.*?<Signal_Info>.*?<Freq><Val>(\d+?)<\/Val><Exp>(\d+?)<\/Exp><Unit>(.*?)<\/Unit><\/Freq>.*?<\/Signal_Info>.*?<\/DAB>/) or  # RX-481D provides always Meta-Info for DAB and FM
+                   ($data =~ /<Tuning>.*?<Freq>(?:<Current>)?<Val>(\d+?)<\/Val><Exp>(\d+?)<\/Exp><Unit>(.*?)<\/Unit>(?:<\/Current>)?.*?<\/Tuning>/)  or 
+                   (YAMAHA_AVR_isModel_DSP($hash) and $data =~ /<Tuning>.*?<Freq><Val>(\d+?)<\/Val><Exp>(\d+?)<\/Exp><Unit>(.*?)<\/Unit><\/Freq>.*?<\/Tuning>/))
                 {
                     readingsBulkUpdate($hash, "currentStationFrequency", sprintf("%.$2f", ($1 / (10 ** $2)))." $3");
                     readingsBulkUpdate($hash, "tunerFrequency", sprintf("%.$2f", ($1 / (10 ** $2))));
-                    
-                    if($data =~ /<(?:Tuning|DAB)>.*?<Band>(.+?)<\/Band>.*?<\/(?:Tuning|DAB)>/)
+                                                                          
+                    if($data =~ /<Band>(.+?)<\/Band>/)
                     {
                         readingsBulkUpdate($hash, "tunerFrequencyBand", uc($1));
                     }

+ 504 - 0
fhem/core/FHEM/71_ZM_Monitor.pm

@@ -0,0 +1,504 @@
+##############################################################################
+#
+#     71_ZM_Monitor.pm
+#
+#     This file is part of Fhem.
+#
+#     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/>.
+#
+##############################################################################
+#
+# ZoneMinder (c) Martin Gutenbrunner / https://github.com/delmar43/FHEM
+#
+# This module is designed to work as a logical device in connection with 70_ZoneMinder
+# as a physical device.
+#
+# Discussed in FHEM Forum: https://forum.fhem.de/index.php/topic,91847.0.html
+#
+# $Id: 71_ZM_Monitor.pm 17479 2018-10-07 16:53:23Z delmar $
+#
+##############################################################################
+
+package main;
+use strict;
+use warnings;
+use HttpUtils;
+
+my @ZM_Functions = qw( None Monitor Modect Record Mocord Nodect );
+my @ZM_Alarms = qw( on off on-for-timer );
+
+sub ZM_Monitor_Initialize {
+  my ($hash) = @_;
+  $hash->{NotifyOrderPrefix} = "71-";
+
+  $hash->{GetFn}       = "ZM_Monitor_Get";
+  $hash->{SetFn}       = "ZM_Monitor_Set";
+  $hash->{DefFn}       = "ZM_Monitor_Define";
+  $hash->{UndefFn}     = "ZM_Monitor_Undef";
+  $hash->{FW_detailFn} = "ZM_Monitor_DetailFn";
+  $hash->{ParseFn}     = "ZM_Monitor_Parse";
+  $hash->{NotifyFn}    = "ZM_Monitor_Notify";
+
+  $hash->{AttrList} = 'showLiveStreamInDetail:0,1 '.$readingFnAttributes;
+  $hash->{Match} = "^.*";
+
+  return undef;
+}
+
+sub ZM_Monitor_Define {
+  my ( $hash, $def ) = @_;
+  $hash->{NOTIFYDEV} = "TYPE=ZoneMinder";
+
+  my @a = split( "[ \t][ \t]*", $def );
+ 
+  my $name   = $a[0];
+  my $module = $a[1];
+  my $zmMonitorId = $a[2];
+  
+  if(@a < 3 || @a > 3) {
+     my $msg = "ZM_Monitor ($name) - Wrong syntax: define <name> ZM_Monitor <ZM_MONITOR_ID>";
+     Log3 $name, 2, $msg;
+     return $msg;
+  }
+
+  $hash->{NAME} = $name;
+  readingsSingleUpdate($hash, "state", "idle", 1);
+
+  AssignIoPort($hash);
+  
+  my $ioDevName = $hash->{IODev}{NAME};
+  my $logDevAddress = $ioDevName.'_'.$zmMonitorId;
+  # Adresse rückwärts dem Hash zuordnen (für ParseFn)
+#  Log3 $name, 3, "ZM_Monitor ($name) - Logical device address: $logDevAddress";
+  $modules{ZM_Monitor}{defptr}{$logDevAddress} = $hash;
+  
+#  Log3 $name, 3, "ZM_Monitor ($name) - Define done ... module=$module, zmHost=$zmHost, zmMonitorId=$zmMonitorId";
+
+  $hash->{helper}{ZM_MONITOR_ID} = $zmMonitorId;
+
+  ZM_Monitor_UpdateStreamUrls($hash);
+
+  return undef;
+}
+
+sub ZM_Monitor_UpdateStreamUrls {
+  my ( $hash ) = @_;
+  my $ioDevName = $hash->{IODev}{NAME};
+
+  my $zmPathZms = $hash->{IODev}{helper}{ZM_PATH_ZMS};
+  if (not $zmPathZms) {
+    return undef;
+  }
+
+  my $zmHost = $hash->{IODev}{helper}{ZM_HOST};
+  my $streamUrl = "http://$zmHost";
+  my $zmUsername = urlEncode($hash->{IODev}{helper}{ZM_USERNAME});
+  my $zmPassword = urlEncode($hash->{IODev}{helper}{ZM_PASSWORD});
+  my $authPart = "&user=$zmUsername&pass=$zmPassword";
+
+  readingsBeginUpdate($hash);
+  ZM_Monitor_WriteStreamUrlToReading($hash, $streamUrl, 'streamUrl', $authPart);
+
+  my $pubStreamUrl = $attr{$ioDevName}{publicAddress};
+  if ($pubStreamUrl) {
+    my $authHash = ReadingsVal($ioDevName, 'authHash', '');
+    if ($authHash) { #if ZM_AUTH_KEY is defined, use the auth-hash. otherwise, use the previously defined username/pwd
+      $authPart = "&auth=$authHash";
+    }
+    ZM_Monitor_WriteStreamUrlToReading($hash, $pubStreamUrl, 'pubStreamUrl', $authPart);
+  }
+  readingsEndUpdate($hash, 1);
+
+  return undef;
+}
+
+# is build by using hosname, NPH_ZMS, monitorId, streamBufferSize, and auth
+sub ZM_Monitor_getZmStreamUrl {
+  my ($hash) = @_;
+
+  #use private or public LAN for streaming access?
+
+  return undef;
+}
+
+sub ZM_Monitor_WriteStreamUrlToReading {
+  my ( $hash, $streamUrl, $readingName, $authPart ) = @_;
+  my $name = $hash->{NAME};
+
+  my $zmPathZms = $hash->{IODev}{helper}{ZM_PATH_ZMS};
+  my $zmMonitorId = $hash->{helper}{ZM_MONITOR_ID};
+  my $buffer = ReadingsVal($name, 'streamReplayBuffer', '1000');
+
+  my $imageUrl = $streamUrl."$zmPathZms?mode=single&scale=100&monitor=$zmMonitorId".$authPart;
+  my $imageReadingName = $readingName;
+  $imageReadingName =~ s/Stream/Image/g;
+  readingsBulkUpdate($hash, $imageReadingName, $imageUrl, 1);
+  
+  $streamUrl = $streamUrl."$zmPathZms?mode=jpeg&scale=100&maxfps=30&buffer=$buffer&monitor=$zmMonitorId".$authPart;
+  readingsBulkUpdate($hash, $readingName, "$streamUrl", 1);
+}
+
+sub ZM_Monitor_DetailFn {
+  my ( $FW_wname, $deviceName, $FW_room ) = @_;
+
+  my $hash = $defs{$deviceName};
+  my $name = $hash->{NAME};
+  
+  my $showLiveStream = $attr{$name}{showLiveStreamInDetail};
+  return "<div>To view a live stream here, execute: attr $name showLiveStreamInDetail 1</div>" if (not $showLiveStream);
+
+  my $streamDisabled = (ReadingsVal($deviceName, 'monitorFunction', 'None') eq 'None');
+  if ($streamDisabled) {
+    return '<div>Streaming disabled</div>';
+  }
+
+  my $streamUrl = ReadingsVal($deviceName, 'pubStreamUrl', undef);
+  if (not $streamUrl) {
+    $streamUrl = ReadingsVal($deviceName, 'streamUrl', undef);
+  }
+  if ($streamUrl) {
+    return "<div><img src='$streamUrl'></img></div>";
+  } else {
+    return undef;
+  }
+}
+
+sub ZM_Monitor_Undef {
+  my ($hash, $arg) = @_; 
+  my $name = $hash->{NAME};
+
+  return undef;
+}
+
+sub ZM_Monitor_Get {
+  my ( $hash, $name, $opt, @args ) = @_;
+
+#  return "Unknown argument $opt, choose one of config";
+  return undef;
+}
+
+sub ZM_Monitor_Set {
+  my ( $hash, $name, $cmd, @args ) = @_;
+
+  if ( "monitorFunction" eq $cmd ) {
+    my $arg = $args[0];
+    if (grep { $_ eq $arg } @ZM_Functions) {
+      my $arguments = {
+        method => 'changeMonitorFunction',
+        zmMonitorId => $hash->{helper}{ZM_MONITOR_ID},
+        zmFunction => $arg
+      };
+      my $result = IOWrite($hash, $arguments);
+      return $result;
+    }
+    return "Unknown value $arg for $cmd, choose one of ".join(' ', @ZM_Functions);
+  } elsif ("motionDetectionEnabled" eq $cmd ) {
+    my $arg = $args[0];
+    if ($arg eq '1' || $arg eq '0') {
+      my $arguments = {
+        method => 'changeMonitorEnabled',
+        zmMonitorId => $hash->{helper}{ZM_MONITOR_ID},
+        zmEnabled => $arg
+      };
+      my $result = IOWrite($hash, $arguments);
+      return $result;
+    }
+    return "Unknown value $arg for $cmd, choose one of 0 1";
+  } elsif ("alarmState" eq $cmd) {
+    my $arg = $args[0];
+    if (grep { $_ eq $arg } @ZM_Alarms) {
+
+      $arg .= ' '.$args[1] if ( 'on-for-timer' eq $arg );
+      my $arguments = {
+        method => 'changeMonitorAlarm',
+        zmMonitorId => $hash->{helper}{ZM_MONITOR_ID},
+        zmAlarm => $arg
+      };
+      my $result = IOWrite($hash, $arguments);
+      return $result;
+    }
+    return "Unknown value $arg for $cmd, chose one of ".join(' '. @ZM_Alarms);
+  } elsif ("text" eq $cmd) {
+    my $arg = join ' ', @args;
+    if (not $arg) {
+      $arg = '';    
+    }
+
+    my $arguments = {
+      method => 'changeMonitorText',
+      zmMonitorId => $hash->{helper}{ZM_MONITOR_ID},
+      text => $arg
+    };
+    my $result = IOWrite($hash, $arguments);
+    return $result;
+  }
+
+  return 'monitorFunction:'.join(',', @ZM_Functions).' motionDetectionEnabled:0,1 alarmState:on,off,on-for-timer text';
+}
+
+# incoming messages from physical device module (70_ZoneMinder in this case).
+sub ZM_Monitor_Parse {
+  my ( $io_hash, $message) = @_;
+
+  my @msg = split(/\:/, $message, 2);
+  my $msgType = $msg[0];
+  if ($msgType eq 'event') {
+    return ZM_Monitor_handleEvent($io_hash, $msg[1]);
+  } elsif ($msgType eq 'createMonitor') {
+    return ZM_Monitor_handleMonitorCreation($io_hash, $msg[1]);
+  } elsif ($msgType eq 'monitor') {
+    return ZM_Monitor_handleMonitorUpdate($io_hash, $msg[1]);
+  } else {
+    Log3 $io_hash, 0, "Unknown message type: $msgType";
+  }
+
+  return undef;
+}
+
+sub ZM_Monitor_handleEvent {
+  my ( $io_hash, $message ) = @_;
+
+  my $ioName = $io_hash->{NAME};
+  my @msgTokens = split(/\|/, $message);
+  my $zmMonitorId = $msgTokens[0];
+  my $alertState = $msgTokens[1];
+  my $eventTs = $msgTokens[2];
+  my $eventId = $msgTokens[3];
+
+  my $logDevAddress = $ioName.'_'.$zmMonitorId;
+  Log3 $io_hash, 5, "Handling event for logical device $logDevAddress";
+  # wenn bereits eine Gerätedefinition existiert (via Definition Pointer aus Define-Funktion)
+  if(my $hash = $modules{ZM_Monitor}{defptr}{$logDevAddress}) {
+    Log3 $hash, 5, "Logical device $logDevAddress found. Writing readings";
+
+    readingsBeginUpdate($hash);
+    ZM_Monitor_createEventStreamUrl($hash, $eventId);
+    my $state;
+    if ($alertState eq "on") {
+      $state = "alert";
+    } elsif ($alertState eq "off") {
+      $state = "idle";
+    }
+    readingsBulkUpdate($hash, "state", $state, 1);
+    readingsBulkUpdate($hash, "alert", $alertState, 1);
+    readingsBulkUpdate($hash, "lastEventTimestamp", $eventTs);
+    readingsBulkUpdate($hash, "lastEventId", $eventId);
+    readingsEndUpdate($hash, 1);
+
+    Log3 $hash, 5, "Writing readings done. Now returning log dev name: $hash->{NAME}";
+    # Rückgabe des Gerätenamens, für welches die Nachricht bestimmt ist.
+    return $hash->{NAME};
+  } else {
+    # Keine Gerätedefinition verfügbar. Daher Vorschlag define-Befehl: <NAME> <MODULNAME> <ADDRESSE>
+    my $autocreate = "UNDEFINED ZM_Monitor_$logDevAddress ZM_Monitor $zmMonitorId";
+    Log3 $io_hash, 5, "logical device with address $logDevAddress not found. returning autocreate: $autocreate";
+    return $autocreate;
+  }
+}
+
+#for now, this is nearly a duplicate of writing the streamUrl reading.
+#will need some love to make better use of existing code.
+sub ZM_Monitor_createEventStreamUrl {
+  my ( $hash, $eventId ) = @_;
+  my $ioDevName = $hash->{IODev}{NAME};
+
+  my $zmPathZms = $hash->{IODev}{helper}{ZM_PATH_ZMS};
+  if (not $zmPathZms) {
+    return undef;
+  }
+
+  my $zmHost = $hash->{IODev}{helper}{ZM_HOST};
+  my $streamUrl = "http://$zmHost";
+  my $zmUsername = urlEncode($hash->{IODev}{helper}{ZM_USERNAME});
+  my $zmPassword = urlEncode($hash->{IODev}{helper}{ZM_PASSWORD});
+  my $authPart = "&user=$zmUsername&pass=$zmPassword";
+  ZM_Monitor_WriteEventStreamUrlToReading($hash, $streamUrl, 'eventStreamUrl', $authPart, $eventId);
+
+  my $pubStreamUrl = $attr{$ioDevName}{publicAddress};
+  if ($pubStreamUrl) {
+    my $authHash = ReadingsVal($ioDevName, 'authHash', '');
+    if ($authHash) { #if ZM_AUTH_KEY is defined, use the auth-hash. otherwise, use the previously defined username/pwd
+      $authPart = "&auth=$authHash";
+    }
+    ZM_Monitor_WriteEventStreamUrlToReading($hash, $pubStreamUrl, 'pubEventStreamUrl', $authPart, $eventId);
+  }
+}
+
+sub ZM_Monitor_handleMonitorUpdate {
+  my ( $io_hash, $message ) = @_;
+
+  my $ioName = $io_hash->{NAME};
+  my @msgTokens = split(/\|/, $message); #$message = "$monitorId|$function|$enabled|$streamReplayBuffer";
+  my $zmMonitorId = $msgTokens[0];
+  my $function = $msgTokens[1];
+  my $enabled = $msgTokens[2];
+  my $streamReplayBuffer = $msgTokens[3];
+  my $logDevAddress = $ioName.'_'.$zmMonitorId;
+
+  if ( my $hash = $modules{ZM_Monitor}{defptr}{$logDevAddress} ) {
+    readingsBeginUpdate($hash);
+    readingsBulkUpdateIfChanged($hash, 'monitorFunction', $function);
+    readingsBulkUpdateIfChanged($hash, 'motionDetectionEnabled', $enabled);
+    my $bufferChanged = readingsBulkUpdateIfChanged($hash, 'streamReplayBuffer', $streamReplayBuffer);
+    readingsEndUpdate($hash, 1);
+
+    ZM_Monitor_UpdateStreamUrls($hash);
+
+    return $hash->{NAME};
+#  } else {
+#    my $autocreate = "UNDEFINED ZM_Monitor_$logDevAddress ZM_Monitor $zmMonitorId";
+#    Log3 $io_hash, 5, "logical device with address $logDevAddress not found. returning autocreate: $autocreate";
+#    return $autocreate;
+  }
+
+  return undef;
+}
+
+sub ZM_Monitor_handleMonitorCreation {
+  my ( $io_hash, $message ) = @_;
+
+  my $ioName = $io_hash->{NAME};
+  my @msgTokens = split(/\|/, $message); #$message = "$monitorId";
+  my $zmMonitorId = $msgTokens[0];
+  my $logDevAddress = $ioName.'_'.$zmMonitorId;
+
+  if ( my $hash = $modules{ZM_Monitor}{defptr}{$logDevAddress} ) {
+    return $hash->{NAME};
+  } else {
+    my $autocreate = "UNDEFINED ZM_Monitor_$logDevAddress ZM_Monitor $zmMonitorId";
+    Log3 $io_hash, 5, "logical device with address $logDevAddress not found. returning autocreate: $autocreate";
+    return $autocreate;
+  }
+
+  return undef;
+}
+
+sub ZM_Monitor_WriteEventStreamUrlToReading {
+  my ( $hash, $streamUrl, $readingName, $authPart, $eventId ) = @_;
+
+  my $zmPathZms = $hash->{IODev}{helper}{ZM_PATH_ZMS};
+  $streamUrl = $streamUrl."/" if (not $streamUrl =~ m/\/$/);
+
+  my $zmMonitorId = $hash->{helper}{ZM_MONITOR_ID};
+  my $imageUrl = $streamUrl."$zmPathZms?mode=single&scale=100&monitor=$zmMonitorId".$authPart;
+  my $imageReadingName = $readingName;
+  $imageReadingName =~ s/Stream/Image/g;
+  readingsBulkUpdate($hash, $imageReadingName, $imageUrl, 1);
+
+  $streamUrl = $streamUrl."$zmPathZms?source=event&mode=jpeg&event=$eventId&frame=1&scale=100&rate=100&maxfps=30".$authPart;
+  readingsBulkUpdate($hash, $readingName, $streamUrl, 1);
+
+}
+
+sub ZM_Monitor_Notify {
+  my ($own_hash, $dev_hash) = @_;
+  my $name = $own_hash->{NAME}; # own name / hash
+
+  return "" if(IsDisabled($name)); # Return without any further action if the module is disabled
+
+  my $devName = $dev_hash->{NAME}; # Device that created the events
+
+  my $events = deviceEvents($dev_hash,1);
+  return if( !$events );
+
+  foreach my $event (@{$events}) {
+    $event = "" if(!defined($event));
+    Log3 $name, 4, "ZM_Monitor ($name) - Incoming event: $event";
+
+    my @msg = split(/\:/, $event, 2);
+    if ($msg[0] eq 'authHash') {
+      ZM_Monitor_UpdateStreamUrls($own_hash);
+    } else {
+      Log3 $name, 4, "ZM_Monitor ($name) - ignoring";
+    }
+
+    # Examples:
+    # $event = "readingname: value" 
+    # or
+    # $event = "INITIALIZED" (for $devName equal "global")
+    #
+    # processing $event with further code
+  }
+}
+
+# Eval-Rückgabewert für erfolgreiches
+# Laden des Moduls
+1;
+
+
+# Beginn der Commandref
+
+=pod
+=item device
+=item summary Logical device to change Monitor operation modes in ZoneMinder
+=item summary_DE Logisches Modul zum Verändern der Kameraeinstellungen in ZoneMinder
+
+=begin html
+
+<a name="ZM_Monitor"></a>
+<h3>ZM_Monitor</h3>
+
+<a name="ZM_Monitordefine"></a>
+  <b>Define</b>
+  <ul>
+    <code>define &lt;name&gt; ZM_Monitor  &lt;ZM-Monitor ID&gt;</code>
+    <br><br>
+    This is usually called by autocreate and triggered by the ZoneMinder IODevice.
+    <br>
+  </ul>
+  <br><br>
+
+  <a name="ZM_Monitorset"></a>
+  <b>Set</b>
+  <ul>
+    <li><code>alarmState</code><br>Puts a monitor into alarm state or out of alarm state via the ZoneMinder trigger port.</li>
+    <li><code>monitorFunction</code><br>Sets the operating mode of a Monitor in ZoneMinder via the ZoneMinder API.</li>
+    <li><code>motionDetectionEnabled</code><br>Enables or disables monitor detection of a monitor via ZoneMinder API.</li>
+    <li><code>text</code><br/>Allows you to set a text for a Timestamp's <code>%Q</code> portion in ZoneMinder via the ZoneMinder trigger port.</li>
+  </ul>
+
+  <br><br>
+  <a name="ZM_Monitorattr"></a>
+  <b>Attributes</b>
+  <br><br>
+  <ul>
+    <li><code>showLiveStreamInDetail</code><br/>If set to <code>1</code>, a live-stream of the current monitor will be shown on top of the FHEMWEB detail page.</li>
+  </ul>
+
+  <br><br>
+
+  <a name="ZM_Monitorreadings"></a>
+  <b>Readings</b>
+  <br><br>
+  <ul>
+    <li><code>alert</code><br/>The alert state.</li>
+    <li><code>eventImageUrl</code><br/>Link to the first image of the latest event recording, based on the ZM-Host parameter used in the device definition.</li>
+    <li><code>eventStreamUrl</code><br/>Link to the latest event recording, based on the ZM-Host parameter used in the device definition.</li>
+    <li><code>lastEventId</code><br/>ID of the latest event in ZoneMinder.</li>
+    <li><code>lastEventTimestamp</code><br/>Timestamp of the latest event from ZoneMinder.</li>
+    <li><code>monitorFunction</code><br/>Current operation mode of the monitor.</li>
+    <li><code>motionDetectionEnabled</code><br/>Equals the 'enabled' setting in ZoneMinder. Allows you to put the monitor into a more passive state (according to ZoneMinder documentation).</li>
+    <li><code>pubEventImageUrl</code><br/>Link to the first image of the latest event recording, based on the <code>publicAddress</code> attribute used in the ZoneMinder device.</li>
+    <li><code>pubEventStreamUrl</code><br/>Link to the latest event recording, based on the <code>publicAddress</code> attribute used in the ZoneMinder device.</li>
+    <li><code>pubImageUrl</code><br/>Link to the current live image, based on the <code>publicAddress</code> attribute used in the ZoneMinder device.</li>
+    <li><code>pubStreamUrl</code>Link to the live-stream, based on the <code>publicAddress</code> attribute used in the ZoneMinder device.<br/></li>
+    <li><code>streamReplayBuffer</code><br/>Taken from the ZoneMinder configuration. Used for the <code>buffer</code> parameter of stream URLs.</li>
+    <li><code>streamUrl</code><br/>Link to the live-stream, based on the ZM-Host parameter used in the device definition.</li>
+
+  </ul>
+
+=end html
+
+# Ende der Commandref
+=cut

+ 21 - 14
fhem/core/FHEM/72_FRITZBOX.pm

@@ -1,5 +1,5 @@
 ###############################################################
-# $Id: 72_FRITZBOX.pm 17073 2018-08-01 17:35:58Z tupol $
+# $Id: 72_FRITZBOX.pm 17437 2018-09-30 18:24:58Z tupol $
 #
 #  72_FRITZBOX.pm 
 #
@@ -227,7 +227,7 @@ sub FRITZBOX_Define($$)
 
    $hash->{STATE}              = "Initializing";
    $hash->{INTERVAL}           = 300; 
-   $hash->{fhem}{modulVersion} = '$Date: 2018-08-01 19:35:58 +0200 (Wed, 01 Aug 2018) $';
+   $hash->{fhem}{modulVersion} = '$Date: 2018-09-30 20:24:58 +0200 (Sun, 30 Sep 2018) $';
    $hash->{fhem}{lastHour}     = 0;
    $hash->{fhem}{LOCAL}        = 0;
 
@@ -1304,7 +1304,7 @@ sub FRITZBOX_Readout_Run_Web($)
    $queryStr .= "&box_dect=dect:settings/enabled"; # DECT Sender
    $queryStr .= "&handsetCount=dect:settings/Handset/count"; # Anzahl Handsets
    $queryStr .= "&handset=dect:settings/Handset/list(User,Manufacturer,Model,FWVersion)"; # DECT Handsets
-   $queryStr .= "&wlanList=wlan:settings/wlanlist/list(mac,speed,speed_rx,rssi)"; # WLAN devices
+   $queryStr .= "&wlanList=wlan:settings/wlanlist/list(mac,speed,speed_rx,rssi,is_guest)"; # WLAN devices
    $queryStr .= "&wlanListNew=wlan:settings/wlanlist/list(mac,speed,rssi)"; # WLAN devices fw>=6.69
    #wlan:settings/wlanlist/list(hostname,mac,UID,state,rssi,quality,is_turbo,cipher,wmm_active,powersave,is_ap,ap_state,is_repeater,flags,flags_set,mode,is_guest,speed,speed_rx,channel_width,streams)   #wlan:settings/wlanlist/list(hostname,mac,UID,state,rssi,quality,is_turbo,wmm_active,cipher,powersave,is_repeater,flags,flags_set,mode,is_guest,speed,speed_rx,speed_rx_max,speed_tx_max,channel_width,streams,mu_mimo_group,is_fail_client)   
    $queryStr .= "&lanDevice=landevice:settings/landevice/list(mac,ip,ethernet,ethernet_port,guest,name,active,online,wlan,speed,UID)"; # LAN devices
@@ -1388,13 +1388,13 @@ sub FRITZBOX_Readout_Run_Web($)
       return $name."|".encode_base64($returnStr,"");
    }
 
-   # !!! copes with fw 6.69 !!!
+   # !!! copes with fw >=6.69 and fw < 7 !!!
    if ( ref $result->{wlanList} ne 'ARRAY' ) {
-      FRITZBOX_Log $hash, 4, "Recognized query answer of firmware 6.69";
+      FRITZBOX_Log $hash, 4, "Recognized query answer of firmware >=6.69 and < 7";
       my $result2;
       my $newQueryPart; 
       
-    # gets WLAN speed for fw>=6.69
+    # gets WLAN speed for fw>=6.69 and < 7
       $queryStr="";
       foreach ( @{ $result->{wlanListNew} } ) {
          $newQueryPart = "&".$_->{_node}."=wlan:settings/".$_->{_node}."/speed_rx";
@@ -1408,7 +1408,7 @@ sub FRITZBOX_Readout_Run_Web($)
          }
       }
 
-    # gets LAN-Port for fw>=6.69
+    # gets LAN-Port for fw>=6.69 and fw<7
       foreach ( @{ $result->{lanDeviceNew} } ) {
          $newQueryPart = "&".$_->{_node}."=landevice:settings/".$_->{_node}."/ethernet_port";
          if (length($queryStr.$newQueryPart) < 4050) {
@@ -1532,15 +1532,17 @@ sub FRITZBOX_Readout_Run_Web($)
 
 # Create WLAN-List
    my %wlanList;
-   #to keep compatibility with firmware <= v3.67
+   #to keep compatibility with firmware <= v3.67 and >=7
    if ( ref $result->{wlanList} eq 'ARRAY' ) {
       foreach ( @{ $result->{wlanList} } ) {
          my $mac = $_->{mac};
          $mac =~ s/:/_/g;
-         $wlanList{$mac}{speed} .= $_->{speed};
-         $wlanList{$mac}{speed_rx} .= $_->{speed_rx};
+         # Anscheinend gibt es Anmeldungen sowohl für Repeater als auch für FBoxen 
+         $wlanList{$mac}{speed} = $_->{speed}   if ! defined $wlanList{$mac}{speed} || $_->{speed} ne "0";
+         $wlanList{$mac}{speed_rx} = $_->{speed_rx} if ! defined $wlanList{$mac}{speed_rx} || $_->{speed_rx} ne "0";
          #$wlanList{$mac}{speed_rx} = $result_lan->{$_->{_node}};
-         $wlanList{$mac}{rssi} .= $_->{rssi};
+         $wlanList{$mac}{rssi} = $_->{rssi} if ! defined $wlanList{$mac}{rssi} || $_->{rssi} ne "0";
+         $wlanList{$mac}{is_guest} = $_->{is_guest} if ! defined $wlanList{$mac}{is_guest} || $_->{is_guest} ne "0";
          FRITZBOX_Readout_Add_Reading $hash, \@roReadings, "fhem->wlanDevice->".$mac."->speed", $_->{speed};
          FRITZBOX_Readout_Add_Reading $hash, \@roReadings, "fhem->wlanDevice->".$mac."->speed_rx", $wlanList{$mac}{speed_rx};
          FRITZBOX_Readout_Add_Reading $hash, \@roReadings, "fhem->wlanDevice->".$mac."->rssi", $_->{rssi};
@@ -1569,7 +1571,12 @@ sub FRITZBOX_Readout_Run_Web($)
          if ( $_->{active} ) {
             my $mac = $_->{mac};
             $mac =~ s/:/_/g;
-            if ( !$_->{ethernet} && $_->{wlan} ) {
+            # if ( !$_->{ethernet} && $_->{wlan} ) { # funktioniert nicht mehr seit v7
+            if ( defined $wlanList{$mac} ) {
+               # Copes with fw>=7
+               $_->{guest} = $wlanList{$mac}{is_guest}  if defined $wlanList{$mac}{is_guest} && $_->{guest} eq "";
+               $wlanCount++;
+               $gWlanCount++      if $_->{guest} eq "1";
                $dName .= " (";
                $dName .= "g"    if $_->{guest};
                $dName .= "WLAN";
@@ -1588,8 +1595,8 @@ sub FRITZBOX_Readout_Run_Web($)
             }
             my $rName = "mac_".$mac;
             FRITZBOX_Readout_Add_Reading $hash, \@roReadings, $rName, $dName;
-            $wlanCount++      if $_->{wlan} ;
-            $gWlanCount++      if $_->{wlan}  && $_->{guest} ;
+            # $wlanCount++      if $_->{wlan} ;
+            # $gWlanCount++      if $_->{wlan}  && $_->{guest} ;
             # Remove mac address from oldLanDevice-List
             delete $oldLanDevice{$rName}   if exists $oldLanDevice{$rName};
          }

+ 78 - 21
fhem/core/FHEM/72_XiaomiDevice.pm

@@ -1,5 +1,5 @@
-##############################################
-# $Id: 72_XiaomiDevice.pm 17305 2018-09-09 11:33:58Z moises $$$
+##############################################
+# $Id: 72_XiaomiDevice.pm 17464 2018-10-05 22:29:21Z moises $$$
 #
 #  72_XiaomiDevice.pm
 #
@@ -211,7 +211,7 @@ sub XiaomiDevice_Define($$$) {
 
   $hash->{helper}{delay} = 0;
 
-  #my $token = '';
+  $a[3] = '' if(!defined($a[3]));
   if(length($a[3]) == 32) {
     $hash->{helper}{token} = $a[3];
   } elsif(length($a[3]) == 96) {
@@ -1587,12 +1587,12 @@ sub XiaomiDevice_GetUpdate($)
   elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "AirPurifier")
   {
     $hash->{helper}{packet}{$packetid} = "air_data";
-    XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["power","mode","motor1_speed","temp_dec","humidity","aqi","average_aqi","favorite_level","use_time","purify_volume","filter1_life"]}' );
+    XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["power","mode","motor1_speed","temp_dec","humidity","aqi","average_aqi","favorite_level","use_time","purify_volume","filter1_life","f1_hour_used","f1_hour","button_pressed","motor2_speed"]}' );
   }
   elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "Humidifier")
   {
     $hash->{helper}{packet}{$packetid} = "hum_data";
-    XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["power","mode","temp_dec","humidity"]}' );
+    XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["power","mode","temp_dec","humidity","button_pressed"]}' );
   }
   elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "SmartFan")
   {
@@ -1612,7 +1612,7 @@ sub XiaomiDevice_GetUpdate($)
   elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "WaterPurifier")
   {
     $hash->{helper}{packet}{$packetid} = "water_data";
-    XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["power","mode","tds","filter1_life","filter1_state","filter_life","filter_state","life","state","level","volume","filter","usage","temperature","uv_life","uv_state","elecval_state"]}' );
+    XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["power","mode","tds","filter1_life","filter1_state","filter_life","filter_state","life","state","level","volume","filter","usage","temperature","uv_life","uv_state","elecval_state","button_pressed"]}' );
   }
   elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "Camera")
   {
@@ -1622,7 +1622,7 @@ sub XiaomiDevice_GetUpdate($)
   elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "RiceCooker")
   {
     $hash->{helper}{packet}{$packetid} = "ricecooker_data";
-    XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["func", "menu", "stage", "temp", "t_func", "t_precook", "t_cook", "setting", "delay", "version"]}' );
+    XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["func", "menu", "stage", "temp", "t_func", "t_precook", "t_cook", "setting", "delay", "version","button_pressed"]}' );
   }
   elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "PowerPlug")
   {
@@ -1648,7 +1648,7 @@ sub XiaomiDevice_GetSettings($)
     my $packetid = $hash->{helper}{packetid};
     $hash->{helper}{packetid} = $packetid+1;
     $hash->{helper}{packet}{$packetid} = "air_settings";
-    return XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["buzzer","led_b","child_lock","app_extra","act_sleep","sleep_time"]}' );
+    return XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["buzzer","led_b","child_lock","app_extra","act_sleep","sleep_time","volume","rfid_product_id","rfid_tag"]}' );
   }
 
   if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "Humidifier")
@@ -1733,6 +1733,16 @@ sub XiaomiDevice_GetSettings($)
   $hash->{helper}{packet}{$packetid} = "get_carpet_mode";
   XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_carpet_mode","params":[""]}' );
 
+  $packetid = $hash->{helper}{packetid};
+  $hash->{helper}{packetid} = $packetid+1;
+  $hash->{helper}{packet}{$packetid} = "get_fw_features";
+  XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_fw_features","params":[""]}' );
+
+  $packetid = $hash->{helper}{packetid};
+  $hash->{helper}{packetid} = $packetid+1;
+  $hash->{helper}{packet}{$packetid} = "app_get_locale";
+  XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"app_get_locale","params":[""]}' );
+
   return undef;
 }
 
@@ -1794,7 +1804,7 @@ sub XiaomiDevice_GetSpeed($)
   elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "AirPurifier")
   {
     $hash->{helper}{packet}{$packetid} = "air_status";
-    XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["power","mode","motor1_speed","favorite_level"]}' );
+    XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["power","mode","motor1_speed","favorite_level","motor2_speed"]}' );
   }
   elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "Humidifier")
   {
@@ -1961,8 +1971,12 @@ sub XiaomiDevice_ParseJSON($$)
     readingsBulkUpdate( $hash, "pm25_average", $json->{result}[6], 1 ) if(defined($json->{result}[6]));
     readingsBulkUpdate( $hash, "favorite", $json->{result}[7], 1 ) if(defined($json->{result}[7]));
     readingsBulkUpdate( $hash, "usage", sprintf( "%.1f", $json->{result}[8]/3600), 1 ) if(defined($json->{result}[8]));
-    readingsBulkUpdate( $hash, "volume", $json->{result}[9], 1 ) if(defined($json->{result}[9]));
+    readingsBulkUpdate( $hash, "filter_volume", $json->{result}[9], 1 ) if(defined($json->{result}[9]));
     readingsBulkUpdate( $hash, "filter", $json->{result}[10], 1 ) if(defined($json->{result}[10]));
+    readingsBulkUpdate( $hash, "filter_used", $json->{result}[11], 1 ) if(defined($json->{result}[11]));
+    readingsBulkUpdate( $hash, "filter_life", $json->{result}[12], 1 ) if(defined($json->{result}[12]));
+    readingsBulkUpdate( $hash, "button_pressed", $json->{result}[13], 1 ) if(defined($json->{result}[13]));
+    readingsBulkUpdate( $hash, "speed2", $json->{result}[14], 1 ) if(defined($json->{result}[14]));
     readingsBulkUpdate( $hash, "state", $stateval, 1 ) if(defined($stateval));
     readingsEndUpdate($hash,1);
     return undef;
@@ -1978,6 +1992,9 @@ sub XiaomiDevice_ParseJSON($$)
     readingsBulkUpdate( $hash, "turbo", ($json->{result}[3] eq "0" ? 'off' : 'on'), 1 ) if(defined($json->{result}[3]));
     readingsBulkUpdate( $hash, "sleep_auto", $json->{result}[4], 1 ) if(defined($json->{result}[4]));
     readingsBulkUpdate( $hash, "sleep_time", $json->{result}[6], 1 ) if(defined($json->{result}[6]));
+    readingsBulkUpdate( $hash, "filter_volume", $json->{result}[7], 1 ) if(defined($json->{result}[7]));
+    readingsBulkUpdate( $hash, "rfid_product_id", $json->{result}[8], 1 ) if(defined($json->{result}[8]));
+    readingsBulkUpdate( $hash, "rfid_tag", $json->{result}[9], 1 ) if(defined($json->{result}[9]));
     readingsEndUpdate($hash,1);
     return undef;
   }
@@ -1992,6 +2009,7 @@ sub XiaomiDevice_ParseJSON($$)
     readingsBulkUpdate( $hash, "mode", $json->{result}[1], 1 ) if(defined($json->{result}[1]));
     readingsBulkUpdate( $hash, "speed", $json->{result}[2], 1 ) if(defined($json->{result}[2]));
     readingsBulkUpdate( $hash, "favorite", $json->{result}[3], 1 ) if(defined($json->{result}[3]));
+    readingsBulkUpdate( $hash, "speed2", $json->{result}[4], 1 ) if(defined($json->{result}[4]));
     readingsBulkUpdate( $hash, "state", $stateval, 1 ) if(defined($stateval));
     readingsEndUpdate($hash,1);
     return undef;
@@ -2005,6 +2023,7 @@ sub XiaomiDevice_ParseJSON($$)
     readingsBulkUpdate( $hash, "mode", ($json->{result}[0] eq "off") ? "idle" : $json->{result}[1], 1 ) if(defined($json->{result}[1]));
     readingsBulkUpdate( $hash, "temperature", ($json->{result}[2]/10), 1 ) if(defined($json->{result}[2]));
     readingsBulkUpdate( $hash, "humidity", $json->{result}[3], 1 ) if(defined($json->{result}[3]));
+    readingsBulkUpdate( $hash, "button_pressed", $json->{result}[4], 1 ) if(defined($json->{result}[4]));
     readingsEndUpdate($hash,1);
     return undef;
   }
@@ -2139,7 +2158,7 @@ sub XiaomiDevice_ParseJSON($$)
     readingsBulkUpdate( $hash, "child_lock", $json->{result}[11], 1 ) if(defined($json->{result}[11]));
     readingsBulkUpdate( $hash, "temperature", $json->{result}[12]/10, 1 ) if(defined($json->{result}[12]) && $json->{result}[12] ne "null");
     readingsBulkUpdate( $hash, "humidity", $json->{result}[13], 1 ) if(defined($json->{result}[13]) && $json->{result}[13] ne "null");
-    readingsBulkUpdate( $hash, "speed", $json->{result}[14], 1 ) if(defined($json->{result}[14]));
+    readingsBulkUpdate( $hash, "speed", (($json->{result}[2] eq "off")?"0":$json->{result}[14]), 1 ) if(defined($json->{result}[14]));
     readingsBulkUpdate( $hash, "button_pressed", $json->{result}[15], 1 ) if(defined($json->{result}[15]) && $json->{result}[15] ne "null");
     readingsEndUpdate($hash,1);
     return undef;
@@ -2157,7 +2176,7 @@ sub XiaomiDevice_ParseJSON($$)
     $fanspeed = 0 if($json->{result}[0] eq "off");
     readingsBulkUpdate( $hash, "level", $fanspeed, 1 ) if(defined($json->{result}[2]));
     readingsBulkUpdate( $hash, "mode", (int($json->{result}[2])>0)?"natural":"straight", 1 ) if(defined($json->{result}[2]));
-    readingsBulkUpdate( $hash, "speed", $json->{result}[3], 1 ) if(defined($json->{result}[3]));
+    readingsBulkUpdate( $hash, "speed", (($json->{result}[0] eq "off")?"0":$json->{result}[3]), 1 ) if(defined($json->{result}[3]));
     readingsEndUpdate($hash,1);
     return undef;
   }
@@ -2177,13 +2196,14 @@ sub XiaomiDevice_ParseJSON($$)
     readingsBulkUpdate( $hash, "life", $json->{result}[7], 1 ) if(defined($json->{result}[7]));
     readingsBulkUpdate( $hash, "state", $json->{result}[8], 1 ) if(defined($json->{result}[8]));
     readingsBulkUpdate( $hash, "level", $json->{result}[9], 1 ) if(defined($json->{result}[9]));
-    readingsBulkUpdate( $hash, "volume", $json->{result}[10], 1 ) if(defined($json->{result}[10]));
+    readingsBulkUpdate( $hash, "water_volume", $json->{result}[10], 1 ) if(defined($json->{result}[10]));
     readingsBulkUpdate( $hash, "filter", $json->{result}[11], 1 ) if(defined($json->{result}[11]));
     readingsBulkUpdate( $hash, "usage", $json->{result}[12], 1 ) if(defined($json->{result}[12]));
     readingsBulkUpdate( $hash, "temperature", $json->{result}[13], 1 ) if(defined($json->{result}[13]));
     readingsBulkUpdate( $hash, "uv_life", $json->{result}[14], 1 ) if(defined($json->{result}[14]));
     readingsBulkUpdate( $hash, "uv_state", $json->{result}[15], 1 ) if(defined($json->{result}[15]));
     readingsBulkUpdate( $hash, "elecval_state", $json->{result}[16], 1 ) if(defined($json->{result}[16]));
+    readingsBulkUpdate( $hash, "button_pressed", $json->{result}[17], 1 ) if(defined($json->{result}[17]));
     readingsEndUpdate($hash,1);
     return undef;
   }
@@ -2216,6 +2236,7 @@ sub XiaomiDevice_ParseJSON($$)
     readingsBulkUpdate( $hash, "setting", $json->{result}[7], 1 ) if(defined($json->{result}[7]));
     readingsBulkUpdate( $hash, "delay", $json->{result}[8], 1 ) if(defined($json->{result}[8]));
     readingsBulkUpdate( $hash, "version", $json->{result}[9], 1 ) if(defined($json->{result}[9]));
+    readingsBulkUpdate( $hash, "button_pressed", $json->{result}[10], 1 ) if(defined($json->{result}[10]));
     readingsEndUpdate($hash,1);
     return undef;
   }
@@ -2236,6 +2257,7 @@ sub XiaomiDevice_ParseJSON($$)
     readingsBulkUpdate( $hash, "voltage", $json->{result}[0]{voltage}, 1 ) if(defined($json->{result}[0]{voltage}));
     readingsBulkUpdate( $hash, "power_factor", $json->{result}[0]{power_factor}, 1 ) if(defined($json->{result}[0]{power_factor}));
     readingsBulkUpdate( $hash, "elec_leakage", $json->{result}[0]{elec_leakage}, 1 ) if(defined($json->{result}[0]{elec_leakage}));
+    readingsBulkUpdate( $hash, "button_pressed", $json->{result}[0]{button_pressed}, 1 ) if(defined($json->{result}[0]{button_pressed}));
     #readingsBulkUpdate( $hash, "setting", (($json->{result}[0]{setting} eq "1")?"yes":"no"), 1 ) if(defined($json->{result}[0]{setting}));
     readingsEndUpdate($hash,1);
     return undef;
@@ -2306,6 +2328,14 @@ sub XiaomiDevice_ParseJSON($$)
     readingsEndUpdate($hash,1);
     return undef;
   }
+  if($msgtype eq "get_sound_volume")
+  {
+    return undef if(!defined($json->{result}));
+    readingsSingleUpdate( $hash, "volume", "100", 0) if(($json->{result} eq "unknown_method") || (ref($json->{result}) ne "ARRAY" && $json->{result} eq "0"));
+    return undef if(ref($json->{result}) ne "ARRAY");
+    readingsSingleUpdate( $hash, "volume", $json->{result}[0], 1 ) if(defined($json->{result}[0]));
+    return undef;
+  }
   if($msgtype eq "get_carpet_mode")
   {
     return undef if(!defined($json->{result}));
@@ -2319,12 +2349,33 @@ sub XiaomiDevice_ParseJSON($$)
     readingsSingleUpdate( $hash, "carpet_integral", $json->{result}[0]{current_integral}, 1 ) if(defined($json->{result}[0]{current_integral}));
     return undef;
   }
-  if($msgtype eq "get_sound_volume")
+  if($msgtype eq "app_get_locale")
   {
     return undef if(!defined($json->{result}));
-    readingsSingleUpdate( $hash, "volume", "100", 0) if(($json->{result} eq "unknown_method") || (ref($json->{result}) ne "ARRAY" && $json->{result} eq "0"));
     return undef if(ref($json->{result}) ne "ARRAY");
-    readingsSingleUpdate( $hash, "volume", $json->{result}[0], 1 ) if(defined($json->{result}[0]));
+    return undef if(ref($json->{result}[0]) ne "HASH");
+    readingsSingleUpdate( $hash, "app_logserver", $json->{result}[0]{logserver}, 1 ) if(defined($json->{result}[0]{logserver}));
+    readingsSingleUpdate( $hash, "app_wifiplan", $json->{result}[0]{wifiplan}, 1 ) if(defined($json->{result}[0]{wifiplan}) && $json->{result}[0]{wifiplan} ne "");
+    readingsSingleUpdate( $hash, "app_timezone", $json->{result}[0]{timezone}, 1 ) if(defined($json->{result}[0]{timezone}));
+    readingsSingleUpdate( $hash, "app_bom", $json->{result}[0]{bom}, 1 ) if(defined($json->{result}[0]{bom}));
+    readingsSingleUpdate( $hash, "app_language", $json->{result}[0]{language}, 1 ) if(defined($json->{result}[0]{language}));
+    readingsSingleUpdate( $hash, "app_name", $json->{result}[0]{name}, 1 ) if(defined($json->{result}[0]{name}));
+    readingsSingleUpdate( $hash, "app_location", $json->{result}[0]{location}, 1 ) if(defined($json->{result}[0]{location}));
+    return undef;
+  }
+  if($msgtype eq "get_fw_features")
+  {
+    return undef if(!defined($json->{result}));
+    return undef if(ref($json->{result}) ne "ARRAY");
+    my $featurestring = "";
+    $featurestring = $json->{result}[0] if(defined($json->{result}[0]));
+    my $i = 1;
+    while(defined($json->{result}[$i])){
+      $featurestring .= ",";
+      $featurestring .= $json->{result}[$i];
+      $i++;
+    }
+    readingsSingleUpdate( $hash, "device_fw_features", $featurestring, 1 );
     return undef;
   }
   if($msgtype eq "get_custom_mode")
@@ -2812,7 +2863,7 @@ sub XiaomiDevice_Read($) {
   if($len == 32) # token return
   {
     my $token = substr($data,-32,32);
-    if($token eq "ffffffffffffffffffffffffffffffff" && !defined($hash->{helper}{token}))
+    if(($token eq "00000000000000000000000000000000" || $token eq "ffffffffffffffffffffffffffffffff") && !defined($hash->{helper}{token}))
     {
       Log3 $name, 1, "$name: Token could not be retrieved automatically from already cloud-connected device!";
       $attr{$name}{disable} = "1";
@@ -2822,7 +2873,10 @@ sub XiaomiDevice_Read($) {
     RemoveInternalTimer($hash, "XiaomiDevice_connectFail");
     $hash->{helper}{delay} = 0;
 
-    $hash->{helper}{token} = $token if(!defined($hash->{helper}{token}));
+    if(!defined($hash->{helper}{token})){
+      $hash->{helper}{token} = $token;
+      $hash->{DEF} = $hash->{DEF}." ".$hash->{helper}{token};
+    }
 
     return undef;
   }
@@ -2953,13 +3007,16 @@ sub XiaomiDevice_DbLog_splitFn($) {
   $value = $parts[1];
 
   $unit = "";
-  $unit = "%" if($reading =~ /filter/);;
+  $unit = "%" if($reading eq "filter");;
   $unit = "%" if($reading =~ /humidity/);;
   $unit = "µg/m³" if($reading =~ /pm25/);;
   $unit = "rpm" if($reading =~ /speed/);
   $unit = "˚C" if($reading =~ /temperature/);
   $unit = "h" if($reading =~ /usage/);
-  $unit = "m³" if($reading =~ /volume/);
+  $unit = "h" if($reading =~ /_life/);
+  $unit = "h" if($reading =~ /_used/);
+  $unit = "m³" if($reading eq "filter_volume");
+  $unit = "l" if($reading eq "water_volume");
   $unit = "%" if($reading =~ /batteryPercent/);;
   $unit = "%" if($reading =~ /fan_power/);;
   $unit = "h" if($reading =~ /clean_time/);;
@@ -3219,7 +3276,7 @@ sub XiaomiDevice_DbLog_splitFn($) {
     <br>
     Usage time in h<br/>
     </li><br>
-    <li><code>volume</code> <i>(AirPurifier)</i>
+    <li><code>filter_volume</code> <i>(AirPurifier)</i>
     <br>
     Total air volume in m³<br/>
     </li><br>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 741 - 550
fhem/core/FHEM/73_GardenaSmartBridge.pm


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 632 - 427
fhem/core/FHEM/74_GardenaSmartDevice.pm


+ 214 - 35
fhem/core/FHEM/88_HMCCU.pm

@@ -2,9 +2,9 @@
 #
 #  88_HMCCU.pm
 #
-#  $Id: 88_HMCCU.pm 17338 2018-09-13 17:20:18Z zap $
+#  $Id: 88_HMCCU.pm 17454 2018-10-03 08:31:41Z zap $
 #
-#  Version 4.3.001
+#  Version 4.3.004
 #
 #  Module for communication between FHEM and Homematic CCU2.
 #
@@ -25,6 +25,7 @@
 #  set <name> execute <ccu_program>
 #  set <name> importdefaults <filename>
 #  set <name> hmscript {<scriptfile>|!<function>|'['<code>']'} [dump] [<parname>=<value> [...]]
+#  set <name> rpcregister [{all|<interface>}]
 #  set <name> rpcserver {on|off|restart}
 #  set <name> var [<type>] <name> <value> [<parameter>=<value> [...]]
 #
@@ -37,7 +38,7 @@
 #                                        [defattr] [duplicates] [save] [<attr>=<val> [...]]}]
 #  get <name> dump {devtypes|datapoints} [<filter>]
 #  get <name> dutycycle
-#  get <name> exportdefaults {filename}
+#  get <name> exportdefaults {filename} [csv]
 #  get <name> firmware [{type-expr}|full]
 #  get <name> parfile [<parfile>]
 #  get <name> rpcevents
@@ -107,7 +108,7 @@ my %HMCCU_CUST_CHN_DEFAULTS;
 my %HMCCU_CUST_DEV_DEFAULTS;
 
 # HMCCU version
-my $HMCCU_VERSION = '4.3.001';
+my $HMCCU_VERSION = '4.3.004';
 
 # Default RPC port (BidCos-RF)
 my $HMCCU_RPC_PORT_DEFAULT = 2001;
@@ -245,6 +246,7 @@ sub HMCCU_AggregationRules ($$);
 
 # Handling of default attributes
 sub HMCCU_ExportDefaults ($);
+sub HMCCU_ExportDefaultsCSV ($);
 sub HMCCU_ImportDefaults ($);
 sub HMCCU_FindDefaults ($$);
 sub HMCCU_SetDefaults ($);
@@ -313,6 +315,7 @@ sub HMCCU_IODeviceStates ();
 sub HMCCU_IsFlag ($$);
 
 # Handle interfaces, devices and channels
+sub HMCCU_CreateDevice ($$$$$);
 sub HMCCU_FormatDeviceInfo ($);
 sub HMCCU_GetAddress ($$$$);
 sub HMCCU_GetCCUDeviceParam ($$);
@@ -489,7 +492,7 @@ sub HMCCU_Define ($$)
 	if ($hash->{ccustate} eq 'active') {
 		# If CCU is alive read devices, channels, interfaces and groups
 		HMCCU_Log ($hash, 1, "HMCCU: Initializing device", 0);
-		HMCCU_InitDevice ($hash);
+		$rc = HMCCU_InitDevice ($hash);
 	}
 	
 	if ($hash->{ccustate} ne 'active' || $rc > 0) {
@@ -856,6 +859,56 @@ sub HMCCU_ExportDefaults ($)
 }
 
 ######################################################################
+# Export default attributes as CSV file.
+######################################################################
+
+sub HMCCU_ExportDefaultsCSV ($)
+{
+	my ($filename) = @_;
+	
+	my %attrlist = (
+		'_type' => '', '_description' => '', '_channels' => '',
+		'ccureadingfilter' => '', 'ccureadingname' => '', 'ccuscaleval' => '', 'cmdIcon' => '', 'controldatapoint' => '',
+		'eventMap' => '', 'event-on-change-reading' => '', 'event-on-update-reading' => '', 
+		'genericDeviceType' => '',
+		'hmstatevals' => '',
+		'statedatapoint' => '', 'statevals' => '', 'stripnumber' => '', 'substexcl' => '', 'substitute' => '',
+		'webCmd' => '', 'widgetOverride' => ''
+	);
+	
+	return 0 if (!open (DEFFILE, ">$filename"));
+
+	# Write header
+	print DEFFILE "_flag,".join (',', sort keys %attrlist)."\n";
+
+	# Write channel configurations
+	foreach my $t (keys %{$HMCCU_CHN_DEFAULTS}) {
+		print DEFFILE "C";
+		$attrlist{'_type'} = $t;
+		foreach $a (sort keys %attrlist) {
+			my $v = exists ($HMCCU_CHN_DEFAULTS->{$t}{$a}) ? $HMCCU_CHN_DEFAULTS->{$t}{$a} : $attrlist{$a};
+			print DEFFILE ",\"$v\"";
+		}
+		print DEFFILE "\n";
+	}
+
+	# Write device configurations
+	foreach my $t (keys %{$HMCCU_DEV_DEFAULTS}) {
+		print DEFFILE "D";
+		$attrlist{'_type'} = $t;
+		foreach $a (sort keys %attrlist) {
+			my $v = exists ($HMCCU_DEV_DEFAULTS->{$t}{$a}) ? $HMCCU_DEV_DEFAULTS->{$t}{$a} : $attrlist{$a};
+			print DEFFILE ",\"$v\"";
+		}
+		print DEFFILE "\n";
+	}
+
+	close (DEFFILE);
+
+	return 1;
+}
+
+######################################################################
 # Import customer default attributes
 # Returns 1 on success. Returns negative line number on syntax errors.
 # Returns 0 on file open error.
@@ -1327,7 +1380,12 @@ sub HMCCU_Set ($@)
 	my $name = shift @$a;
 	my $opt = shift @$a;
 	my $options = "var delete execute hmscript cleardefaults:noArg defaults:noArg ".
-		"importdefaults rpcserver:on,off,restart datapoint ackmessages:noArg";
+		"importdefaults rpcregister:all rpcserver:on,off,restart datapoint ackmessages:noArg";
+	my @ifList = HMCCU_GetRPCInterfaceList ($hash);
+	if (scalar (@ifList) > 0) {
+		my $ifStr = join (',', @ifList);
+		$options =~ s/register:all/rpcregister:all,$ifStr/;
+	}
 	my $usage = "HMCCU: Unknown argument $opt, choose one of $options";
 	my $host = $hash->{host};
 
@@ -1341,12 +1399,13 @@ sub HMCCU_Set ($@)
 	my $ccureqtimeout = AttrVal ($name, "ccuReqTimeout", $HMCCU_TIMEOUT_REQUEST);
 	my $readingformat = HMCCU_GetAttrReadingFormat ($hash, $hash);
 	my $substitute = HMCCU_GetAttrSubstitute ($hash, $hash);
+	my $result;
 
 	# Add program names to command execute
 	if (exists ($hash->{hmccu}{prg})) {
 		my @progs = ();
 		foreach my $p (keys %{$hash->{hmccu}{prg}}) {
-			push (@progs, $p) if ($hash->{hmccu}{prg}{$p}{internal} eq 'false');
+			push (@progs, $p) if ($hash->{hmccu}{prg}{$p}{internal} eq 'false' && $p !~ /^\$/);
 		}
 		if (scalar (@progs) > 0) {
 			my $prgopt = "execute:".join(',', @progs);
@@ -1361,7 +1420,6 @@ sub HMCCU_Set ($@)
 		my $objname = shift @$a;
 		my $objvalue = shift @$a;
 		$usage = "set $name $opt [{'bool'|'list'|'number'|'test'}] variable value [param=value [...]]";
-		my $result;
 		
 		return HMCCU_SetError ($hash, $usage) if (!defined ($objvalue));
 
@@ -1395,7 +1453,7 @@ sub HMCCU_Set ($@)
 		return HMCCU_SetError ($hash, $usage)
 			if (!defined ($objname) || $objtype !~ /^(OT_VARDP|OT_DEVICE)$/);
 		
-		my $result = HMCCU_HMScriptExt ($hash, "!DeleteObject", { name => $objname, type => $objtype });
+		$result = HMCCU_HMScriptExt ($hash, "!DeleteObject", { name => $objname, type => $objtype });
 
 		return HMCCU_SetError ($hash, -2) if ($result =~ /^ERROR:.*/);
 		return HMCCU_SetState ($hash, "OK");
@@ -1469,6 +1527,25 @@ sub HMCCU_Set ($@)
 
 		return defined ($dump) ? $response : undef;
 	}
+	elsif ($opt eq 'rpcregister') {
+		return HMCCU_SetError ($hash, "HMCCU: Command not supported by internal RPC server")
+			if ($ccuflags !~ /procrpc/);
+			
+		my $ifName = shift @$a;
+		$result = '';
+		@ifList = (defined ($ifName) && $ifName ne 'all') ? ($ifName) : HMCCU_GetRPCInterfaceList ($hash);
+		
+		foreach my $i (@ifList) {
+			my ($rpcdev, $save) = HMCCU_GetRPCDevice ($hash, 0, $i);
+			if ($rpcdev eq '') {
+				Log3 $name, 2, "HMCCU: Can't find HMCCURPCPROC device for interface $i";
+				next;
+			}
+			my $res = AnalyzeCommandChain (undef, "set $rpcdev register");
+			$result .= $res if (defined ($res));
+		}
+		return HMCCU_SetState ($hash, "OK", $result);
+	}
 	elsif ($opt eq 'rpcserver') {
 		my $action = shift @$a;
 		$action = shift @$a if ($action eq $opt);
@@ -1884,10 +1961,14 @@ sub HMCCU_Get ($@)
 	}
 	elsif ($opt eq 'exportdefaults') {
 		my $filename = shift @$a;
-		$usage = "Usage: get $name $opt filename";		
-		return HMCCU_SetError ($hash, $usage) if (!defined ($filename));
+		my $csv = shift @$a;
 		
-		my $rc = HMCCU_ExportDefaults ($filename);
+		$csv = 'default' if (!defined ($csv));
+		$usage = "Usage: get $name $opt filename [{csv|default}]";		
+		return HMCCU_SetError ($hash, $usage) if (!defined ($filename));
+		return HMCCU_SetError ($hash, $usage) if ($csv !~ /^(default|csv)$/);
+
+		my $rc = $csv ne 'csv' ? HMCCU_ExportDefaults ($filename) : HMCCU_ExportDefaultsCSV ($filename);
 		return HMCCU_SetError ($hash, -16) if ($rc == 0);
 		return HMCCU_SetState ($hash, "OK", "Default attributes written to $filename");
 	}
@@ -2688,7 +2769,7 @@ sub HMCCU_UpdateClients ($$$$$)
 				}
 				else {
 					Log3 $fhname, 2, "HMCCU: Update of device ".$ch->{ccuaddr}." failed"
-						if ($ch->{ccuif} ne 'VirtualDevices');
+						if ($ch->{ccuif} ne 'VirtualDevices' && $ch->{ccuif} ne 'fhem');
 				}
 				$c_err++;
 			}
@@ -2702,6 +2783,61 @@ sub HMCCU_UpdateClients ($$$$$)
 }
 
 ##########################################################################
+# Create virtual device in internal device tables.
+# If sourceAddr is specified, parameter newType will be ignored.
+# Return 0 on success or error code:
+# 1 = newType not defined
+# 2 = Device with newType not found in internal tables
+##########################################################################
+
+sub HMCCU_CreateDevice ($$$$$)
+{
+	my ($hash, $newAddr, $newName, $newType, $sourceAddr) = @_;
+	
+	my %object;
+	
+	if (!defined ($sourceAddr)) {
+		# Search for type in device table
+		return 1 if (!defined ($newType));
+		for my $da (keys %{$hash->{hmccu}{dev}}) {
+			if ($hash->{hmccu}{dev}{$da}{type} eq $newType) {
+				$sourceAddr = $da;
+				last;
+			}
+		}
+		return 2 if (!defined ($sourceAddr));
+	}
+	else {
+		$newType = $hash->{hmccu}{dev}{$sourceAddr}{type};
+	}
+	
+	# Device attributes
+	$object{$newAddr}{flag}      = 'N';
+	$object{$newAddr}{addtype}   = 'dev';
+	$object{$newAddr}{channels}  = $hash->{hmccu}{dev}{$sourceAddr}{channels};
+	$object{$newAddr}{name}      = $newName;
+	$object{$newAddr}{type}      = $newType;
+   $object{$newAddr}{interface} = 'fhem';
+	$object{$newAddr}{chndir}    = 0;
+	
+	# Channel attributes
+	for (my $chn=0; $chn<$object{$newAddr}{channels}; $chn++) {
+		my $ca = "$newAddr:$chn";
+		$object{$ca}{flag}     = 'N';
+		$object{$ca}{addtype}  = 'chn';
+		$object{$ca}{channels} = 1;
+		$object{$ca}{name}     = "$newName:$chn";
+		$object{$ca}{chndir}   = $hash->{hmccu}{dev}{"$sourceAddr:$chn"}{chndir};
+		$object{$ca}{rxmode}   = $hash->{hmccu}{dev}{"$sourceAddr:$chn"}{rxmode};
+		$object{$ca}{usetype}  = $hash->{hmccu}{dev}{"$sourceAddr:$chn"}{usetype};
+	}
+	
+	HMCCU_UpdateDeviceTable ($hash, \%object);
+		
+	return 0;
+}
+
+##########################################################################
 # Update parameters in internal device tables and client devices.
 # Parameter devices is a hash reference with following keys:
 #  {address}
@@ -2768,9 +2904,11 @@ sub HMCCU_UpdateDeviceTable ($$)
 				if (defined ($devices->{$da}{rxmode}));
 			$hash->{hmccu}{dev}{$da}{chndir}    = $devices->{$da}{chndir}
 				if (defined ($devices->{$da}{chndir}));
-			$hash->{hmccu}{adr}{$nm}{address}   = $da;
-			$hash->{hmccu}{adr}{$nm}{addtype}   = $hash->{hmccu}{dev}{$da}{addtype};
-			$hash->{hmccu}{adr}{$nm}{valid}     = 1 if (defined ($nm));
+			if (defined ($nm)) {
+				$hash->{hmccu}{adr}{$nm}{address}   = $da;
+				$hash->{hmccu}{adr}{$nm}{addtype}   = $hash->{hmccu}{dev}{$da}{addtype};
+				$hash->{hmccu}{adr}{$nm}{valid}     = 1;
+			}
 		}
 		elsif ($devices->{$da}{flag} eq 'D' && exists ($hash->{hmccu}{dev}{$da})) {
 			# Device deleted, mark as invalid
@@ -2932,7 +3070,7 @@ sub HMCCU_UpdateSingleDevice ($$$)
 	
 	# Build device list including virtual devices
 	my @grplist = ($cltname);
-	my @virlist = HMCCU_FindClientDevices ($ccuhash, "HMCCUDEV", undef, "ccuif=VirtualDevices");
+	my @virlist = HMCCU_FindClientDevices ($ccuhash, "HMCCUDEV", undef, "ccuif=(VirtualDevices|fhem)");
 	foreach my $vd (@virlist) {
 		my $vh = $defs{$vd};
 		next if (!defined ($vh->{ccugroup}));
@@ -2974,7 +3112,7 @@ sub HMCCU_UpdateSingleDevice ($$$)
 
 		my @devlist = ($ch->{ccuaddr});
 		push @devlist, split (",", $ch->{ccugroup})
-			if ($ch->{ccuif} eq 'VirtualDevices' && exists ($ch->{ccugroup}));
+			if (($ch->{ccuif} eq 'VirtualDevices' || $ch->{ccuif} eq 'fhem') && exists ($ch->{ccugroup}));
 				
 		readingsBeginUpdate ($ch);
 		
@@ -3730,27 +3868,39 @@ sub HMCCU_GetDeviceInfo ($$$)
 	my ($hash, $device, $ccuget) = @_;
 	my $name = $hash->{NAME};
 	my $devname = '';
+	my $response = '';
 
 	my $hmccu_hash = HMCCU_GetHash ($hash);
 	return '' if (!defined ($hmccu_hash));
 
-	$ccuget = HMCCU_GetAttribute ($hmccu_hash, $hash, 'ccuget', 'Value') if ($ccuget eq 'Attr');
-
-	my ($int, $add, $chn, $dpt, $nam, $flags) = HMCCU_ParseObject ($hmccu_hash, $device, 0);
-	if ($flags == $HMCCU_FLAG_ADDRESS) {
-		$devname = HMCCU_GetDeviceName ($hmccu_hash, $add, '');
-		return '' if ($devname eq '');
+	my @devlist;
+	if ($hash->{ccuif} eq 'fhem' && exists ($hash->{ccugroup})) {
+		push @devlist, split (",", $hash->{ccugroup}); 
 	}
 	else {
-		$devname = $nam;
+		push @devlist, $device;
 	}
+	return '' if (scalar (@devlist) == 0);
+	
+	$ccuget = HMCCU_GetAttribute ($hmccu_hash, $hash, 'ccuget', 'Value') if ($ccuget eq 'Attr');
 
-	my $response = HMCCU_HMScriptExt ($hmccu_hash, "!GetDeviceInfo", 
-		{ devname => $devname, ccuget => $ccuget });
-	HMCCU_Trace ($hash, 2, undef,
-		"Device=$device Devname=$devname<br>".
-		"Script response = \n".$response."<br>".
-		"Script = GetDeviceInfo");
+	for my $dev (@devlist) {
+		my ($int, $add, $chn, $dpt, $nam, $flags) = HMCCU_ParseObject ($hmccu_hash, $dev, 0);
+		if ($flags == $HMCCU_FLAG_ADDRESS) {
+			$devname = HMCCU_GetDeviceName ($hmccu_hash, $add, '');
+			return '' if ($devname eq '');
+		}
+		else {
+			$devname = $nam;
+		}
+
+		$response .= HMCCU_HMScriptExt ($hmccu_hash, "!GetDeviceInfo", 
+			{ devname => $devname, ccuget => $ccuget });
+		HMCCU_Trace ($hash, 2, undef,
+			"Device=$devname Devname=$devname<br>".
+			"Script response = \n".$response."<br>".
+			"Script = GetDeviceInfo");
+	}
 
 	return $response;
 }
@@ -3976,6 +4126,7 @@ sub HMCCU_GetDeviceList ($)
 		my $typeprefix = '';
 
 		if ($hmdata[0] eq 'D') {
+			# Device
 			next if (scalar (@hmdata) != 6);
 			# 1=Interface 2=Device-Address 3=Device-Name 4=Device-Type 5=Channel-Count
 			$objects{$hmdata[2]}{addtype}   = 'dev';
@@ -3995,6 +4146,7 @@ sub HMCCU_GetDeviceList ($)
 			}
 		}
 		elsif ($hmdata[0] eq 'C') {
+			# Channel
 			next if (scalar (@hmdata) != 4);
 			# 1=Channel-Address 2=Channel-Name 3=Direction
 			$objects{$hmdata[1]}{addtype}   = 'chn';
@@ -4005,6 +4157,7 @@ sub HMCCU_GetDeviceList ($)
 			$objects{$hmdata[1]}{chndir}    = $hmdata[3];
 		}
 		elsif ($hmdata[0] eq 'I') {
+			# Interface
 			next if (scalar (@hmdata) != 4);
 			# 1=Interface-Name 2=Interface Info 3=URL
 			my $ifurl = $hmdata[3];
@@ -4040,6 +4193,7 @@ sub HMCCU_GetDeviceList ($)
 			}
 		}
 		elsif ($hmdata[0] eq 'P') {
+			# Program
 			next if (scalar (@hmdata) != 4);
 			# 1=Program-Name 2=Active-Flag 3=Internal-Flag
 			$hash->{hmccu}{prg}{$hmdata[1]}{active} = $hmdata[2]; 
@@ -4284,8 +4438,13 @@ sub HMCCU_GetCCUDeviceParam ($$)
 		}
 		else {
 			if (exists ($hash->{hmccu}{adr}{$param})) {
+				# param is a device name
 				$add = $hash->{hmccu}{adr}{$param}{address};
 			}
+			elsif (exists ($hash->{hmccu}{dev}{$param})) {
+				# param is a non standard device or channel address
+				$add = $param;
+			}
 		}
 	}
 	
@@ -4786,7 +4945,8 @@ sub HMCCU_GetRPCDevice ($$$)
 			if (!defined ($ifname));
 		$rpcdevname = HMCCU_GetRPCServerInfo ($hash, $ifname, 'device');
 		$rpchost = HMCCU_GetRPCServerInfo ($hash, $ifname, 'host');
-		return ($rpcdevname, 0) if (defined ($rpcdevname) || !defined ($rpchost));
+		return ($rpcdevname, 0) if (defined ($rpcdevname));
+		return ('', 0) if (!defined ($rpchost));
 #		$rpcdevtype = 'HMCCURPCPROC';
 	}
 # 	elsif ($ccuflags =~ /extrpc/) {
@@ -6035,11 +6195,12 @@ sub HMCCU_GetUpdate ($$$)
 	elsif (HMCCU_IsValidDevice ($hmccu_hash, $addr, $HMCCU_FL_ADDRESS)) {
 		$nam = HMCCU_GetDeviceName ($hmccu_hash, $addr, '');
 		return -1 if ($nam eq '');
-		$list = $nam;
+		$list = $nam if ($cl_hash->{ccuif} ne 'fhem');
 		$script = "!GetDatapointsByDevice";
 
 		# Consider members of group device
-		if ($type eq 'HMCCUDEV' && $cl_hash->{ccuif} eq 'VirtualDevices' &&
+		if ($type eq 'HMCCUDEV' &&
+			($cl_hash->{ccuif} eq 'VirtualDevices' || $cl_hash->{ccuif} eq 'fhem') &&
 			exists ($cl_hash->{ccugroup})) {
 			foreach my $gd (split (",", $cl_hash->{ccugroup})) {
 				$nam = HMCCU_GetDeviceName ($hmccu_hash, $gd, '');
@@ -6078,6 +6239,21 @@ sub HMCCU_GetUpdate ($$$)
 		$events{$add}{$chn}{$dpt} = $value;
 	}
 	
+	if ($cl_hash->{ccuif} eq 'fhem') {
+		# Calculate datapoints of virtual group device
+		if ($cl_hash->{ccutype} ne 'n/a') {
+			foreach my $da (split (",", $cl_hash->{ccugroup})) {
+				foreach my $cn (keys %{$events{$da}}) {
+					foreach my $dp (keys %{$events{$da}{$cn}}) {
+						if (defined ($events{$da}{$cn}{$dp})) {
+							$events{$cl_hash->{ccuaddr}}{$cn}{$dp} = $events{$da}{$cn}{$dp}
+						}
+					}
+				}
+			}
+		}
+	}
+	
 	HMCCU_UpdateSingleDevice ($hmccu_hash, $cl_hash, \%events);
 
 	return 1;
@@ -7373,6 +7549,9 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
       <li><b>set &lt;name&gt; importdefaults &lt;filename&gt;</b><br/>
       	Import default attributes from file.
       </li><br/>
+      <li><b>set &lt;name&gt; rpcregister [{all | &lt;interface&gt;}]</b><br/>
+      	Register RPC servers at CCU.
+      </li><br/>
       <li><b>set &lt;name&gt; rpcserver {on | off | restart}</b><br/>
          Start, stop or restart RPC server(s). This command executed with option 'on'
          will fork a RPC server process for each RPC interface defined in attribute 'rpcinterfaces'.
@@ -7441,7 +7620,7 @@ sub HMCCU_CCURPC_ListDevicesCB ($$)
          iface_conn_n = interface connection state (1=connected, 0=disconnected)<br/>
          iface_ducy_n = duty cycle of interface (0-100)
       </li><br/>
-      <li><b>get &lt;name&gt; exportdefaults &lt;filename&gt;</b><br/>
+      <li><b>get &lt;name&gt; exportdefaults &lt;filename&gt; [{<u>default</u>|csv}]</b><br/>
       	Export default attributes into file.
       </li><br/>
       <li><b>get &lt;name&gt; firmware [{&lt;type-expr&gt; | full}]</b><br/>

+ 5 - 3
fhem/core/FHEM/88_HMCCUCHN.pm

@@ -2,9 +2,9 @@
 #
 #  88_HMCCUCHN.pm
 #
-#  $Id: 88_HMCCUCHN.pm 17325 2018-09-11 07:54:43Z zap $
+#  $Id: 88_HMCCUCHN.pm 17355 2018-09-16 09:34:40Z zap $
 #
-#  Version 4.3
+#  Version 4.3.002
 #
 #  (c) 2018 zap (zap01 <at> t-online <dot> de)
 #
@@ -123,7 +123,9 @@ sub HMCCUCHN_Define ($@)
 	while (defined ($arg)) {
 		return $usage if ($n == 3);
 		if    ($arg eq 'readonly') { $hash->{statevals} = $arg; }
-		elsif ($arg eq 'defaults' && !$init_done) { HMCCU_SetDefaults ($hash); }
+		elsif ($arg eq 'defaults') {
+			HMCCU_SetDefaults ($hash) if ($init_done);
+		}
 		else { return $usage; }
 		$n++;
 		$arg = shift @$a;

+ 108 - 62
fhem/core/FHEM/88_HMCCUDEV.pm

@@ -2,9 +2,9 @@
 #
 #  88_HMCCUDEV.pm
 #
-#  $Id: 88_HMCCUDEV.pm 17325 2018-09-11 07:54:43Z zap $
+#  $Id: 88_HMCCUDEV.pm 17372 2018-09-19 11:39:04Z zap $
 #
-#  Version 4.3
+#  Version 4.3.003
 #
 #  (c) 2018 zap (zap01 <at> t-online <dot> de)
 #
@@ -108,6 +108,16 @@ sub HMCCUDEV_Define ($@)
 		"['readonly'] ['defaults'] [iodev={iodev-name}] ".
 		"[{groupexp=regexp|group={device|channel}[,...]]";
 	return $usage if (scalar (@$a) < 3);
+	
+	my @errmsg = (
+		"OK",
+		"Invalid or unknown CCU device name or address",
+		"Can't assign I/O device",
+		"No devices in group",
+		"No matching CCU devices found",
+		"Type of virtual device not defined",
+		"Device type not found"
+	);
 
 	my $devname = shift @$a;
 	my $devtype = shift @$a;
@@ -126,7 +136,9 @@ sub HMCCUDEV_Define ($@)
 	# Parse optional command line parameters
 	foreach my $arg (@$a) {
 		if    ($arg eq 'readonly') { $hash->{statevals} = $arg; }
-		elsif ($arg eq 'defaults' && !$init_done) { HMCCU_SetDefaults ($hash); }
+		elsif ($arg eq 'defaults') {
+			HMCCU_SetDefaults ($hash) if ($init_done);
+		}
 		elsif ($arg =~ /^[0-9]+$/) { $attr{$name}{statechannel} = $arg; }
 		else { return $usage; }
 	}
@@ -142,22 +154,6 @@ sub HMCCUDEV_Define ($@)
 		# The following call will fail during FHEM start if CCU is not ready
 		$hmccu_hash = $devspec eq 'virtual' ? HMCCU_GetHash (0) : HMCCU_FindIODevice ($devspec);
 	}
-		
-	if ($devspec eq 'virtual') {
-		# Virtual device FHEM only
-		my $no = 0;
-		foreach my $d (sort keys %defs) {
-			my $ch = $defs{$d};
-			next if (!exists ($ch->{TYPE}));
-			next if ($ch->{TYPE} ne 'HMCCUDEV' || $d eq $name);
-			next if ($ch->{ccuif} ne 'VirtualDevices' || $ch->{ccuname} ne 'none');
-			$no++;
-		}
-		$hash->{ccuif} = "VirtualDevices";
-		$hash->{ccuaddr} = sprintf ("VIR%07d", $no+1);
-		$hash->{ccuname} = "none";
-		$hash->{statevals} = 'readonly';
-	}
 	
 	if ($init_done) {
 		# Interactive define command while CCU not ready
@@ -183,9 +179,7 @@ sub HMCCUDEV_Define ($@)
 
 	# Initialize FHEM device, set IO device
 	my $rc = HMCCUDEV_InitDevice ($hmccu_hash, $hash);
-	return "Invalid or unknown CCU device name or address" if ($rc == 1);
-	return "Can't assign I/O device ".$hmccu_hash->{NAME} if ($rc == 2);
-	return "No devices in group" if ($rc == 3);
+	return $errmsg[$rc] if ($rc > 0);
 
 	return undef;
 }
@@ -197,64 +191,107 @@ sub HMCCUDEV_Define ($@)
 # 1 = Invalid channel name or address
 # 2 = Cannot assign IO device
 # 3 = No devices in group
+# 4 = No matching CCU devices found
+# 5 = Type of virtual device not defined
+# 6 = Device type not found
 ######################################################################
 
 sub HMCCUDEV_InitDevice ($$)
 {
 	my ($hmccu_hash, $dev_hash) = @_;
+	my $name = $dev_hash->{NAME};
 	my $devspec = $dev_hash->{hmccu}{devspec};
 	my $gdcount = 0;
 	my $gdname = $devspec;
 	
-	return 1 if (! HMCCU_IsValidDevice ($hmccu_hash, $devspec, 7));
+	if ($devspec eq 'virtual') {
+		# Virtual device FHEM only, search for free address
+		my $no = 0;
+		foreach my $d (sort keys %defs) {
+			my $ch = $defs{$d};
+			next if (!exists ($ch->{TYPE}));
+			next if ($ch->{TYPE} ne 'HMCCUDEV' || $d eq $name);
+			next if ($ch->{ccuif} ne 'fhem' || $ch->{ccuname} ne 'virtual');
+			$no++;
+		}
+		$dev_hash->{ccuif}     = 'fhem';
+		$dev_hash->{ccuaddr}   = sprintf ("VIR%07d", $no+1);
+		$dev_hash->{ccuname}   = 'virtual';
+	}
+	else {
+		return 1 if (!HMCCU_IsValidDevice ($hmccu_hash, $devspec, 7));
 
-	my ($di, $da, $dn, $dt, $dc) = HMCCU_GetCCUDeviceParam ($hmccu_hash, $devspec);
-	return 1 if (!defined ($da));
-	$gdname = $dn;
-	
-	$dev_hash->{ccuif} = $di;
-	$dev_hash->{ccuaddr} = $da;
-	$dev_hash->{ccuname} = $dn;
-	$dev_hash->{ccutype} = $dt;
-	$dev_hash->{channels} = $dc;
+		my ($di, $da, $dn, $dt, $dc) = HMCCU_GetCCUDeviceParam ($hmccu_hash, $devspec);
+		return 1 if (!defined ($da));
+		$gdname = $dn;
 
+		$dev_hash->{ccuif}    = $di;
+		$dev_hash->{ccuaddr}  = $da;
+		$dev_hash->{ccuname}  = $dn;
+		$dev_hash->{ccutype}  = $dt;
+		$dev_hash->{channels} = $dc;
+	}
+	
 	# Parse group options
-	if ($dev_hash->{ccuif} eq "VirtualDevices") {
+	if ($dev_hash->{ccuif} eq 'VirtualDevices' || $dev_hash->{ccuif} eq 'fhem') {
+		my @devlist = ();
 		if (exists ($dev_hash->{hmccu}{groupexp})) {
-			my @devlist;
+			# Group devices specified by name expression
 			$gdcount = HMCCU_GetMatchingDevices ($hmccu_hash, $dev_hash->{hmccu}{groupexp}, 'dev', \@devlist);
-			return "No matching CCU devices found" if ($gdcount == 0);
-			$dev_hash->{ccugroup} = join (',', @devlist);
+			return 4 if ($gdcount == 0);
 		}
 		elsif (exists ($dev_hash->{hmccu}{group})) {
+			# Group devices specified by comma separated name list
 			my @gdevlist = split (",", $dev_hash->{hmccu}{group});
 			$dev_hash->{ccugroup} = '' if (@gdevlist > 0);
 			foreach my $gd (@gdevlist) {
 				my ($gda, $gdc, $gdo) = ('', '', '', '');
 
-				return "Invalid device or channel $gd" if (!HMCCU_IsValidDevice ($hmccu_hash, $gd, 7));
+				return 1 if (!HMCCU_IsValidDevice ($hmccu_hash, $gd, 7));
 
 				($gda, $gdc) = HMCCU_GetAddress ($hmccu_hash, $gd, '', '');
 				$gdo = $gda;
 				$gdo .= ':'.$gdc if ($gdc ne '');
-
-				if (exists ($dev_hash->{ccugroup}) && $dev_hash->{ccugroup} ne '') {
-					$dev_hash->{ccugroup} .= ",".$gdo;
-				}
-				else {
-					$dev_hash->{ccugroup} = $gdo;
-				}
-				
+				push @devlist, $gdo;
 				$gdcount++;
 			}
 		}
 		else {
-			my @devlist = HMCCU_GetGroupMembers ($hmccu_hash, $gdname);
+			# Group specified by CCU virtual group name
+			@devlist = HMCCU_GetGroupMembers ($hmccu_hash, $gdname);
 			$gdcount = scalar (@devlist);
-			$dev_hash->{ccugroup} = join (',', @devlist);
+			return 5 if ($gdcount == 0);
 		}
 
 		return 3 if ($gdcount == 0);
+		
+		$dev_hash->{ccugroup} = join (',', @devlist);
+		if ($devspec eq 'virtual') {
+			my $dev = shift @devlist;
+			my $devtype = HMCCU_GetDeviceType ($hmccu_hash, $dev, 'n/a');
+			my $devna = $devtype eq 'n/a' ? 1 : 0;
+			for my $d (@devlist) {
+				if (HMCCU_GetDeviceType ($hmccu_hash, $d, 'n/a') ne $devtype) {
+					$devna = 1;
+					last;
+				}
+			}
+			
+			my $rc = 0;
+			if ($devna) {
+				$dev_hash->{ccutype} = 'n/a';
+				$dev_hash->{statevals} = 'readonly';
+				$rc = HMCCU_CreateDevice ($hmccu_hash, $dev_hash->{ccuaddr}, $name, undef, $dev); 
+			}
+			else {
+				$dev_hash->{ccutype} = $devtype;
+				$rc = HMCCU_CreateDevice ($hmccu_hash, $dev_hash->{ccuaddr}, $name, $devtype, $dev); 
+			}
+			return $rc+4 if ($rc > 0);
+						
+			# Set default attributes
+			$attr{$name}{ccureadingformat} = 'name';
+		}
 	}
 
 	# Inform HMCCU device about client device
@@ -466,10 +503,18 @@ sub HMCCUDEV_Set ($@)
 		return HMCCU_SetState ($hash, "OK");
 	}
 	elsif ($opt eq 'pct') {
-		return HMCCU_SetError ($hash, -11) if ($sc eq '');
-		return HMCCU_SetError ($hash, "Can't find LEVEL datapoint for device type $ccutype")
-		   if (!HMCCU_IsValidDatapoint ($hash, $ccutype, $sc, "LEVEL", 2));
-
+		return HMCCU_SetError ($hash, -11) if ($sc eq '' && $cc eq '');
+		my $dp;
+		if (HMCCU_IsValidDatapoint ($hash, $ccutype, $cc, "LEVEL", 2)) {
+			$dp = "$cc.LEVEL";
+		}
+		elsif (HMCCU_IsValidDatapoint ($hash, $ccutype, $sc, "LEVEL", 2)) {
+			$dp = "$sc.LEVEL";
+		}
+		else {
+			return HMCCU_SetError ($hash, "Can't find LEVEL datapoint for device type $ccutype")
+		}
+		
 		my $objname = '';
 		my $objvalue = shift @$a;
 		return HMCCU_SetError ($hash, "Usage: set $name pct {value} [{ontime} [{ramptime}]]")
@@ -503,7 +548,7 @@ sub HMCCUDEV_Set ($@)
 		}
 
 		# Set level	
-		$objname = $ccuif.'.'.$ccuaddr.':'.$sc.'.LEVEL';
+		$objname = $ccuif.'.'.$ccuaddr.':'.$dp;
 		$rc = HMCCU_SetDatapoint ($hash, $objname, $objvalue);
 		return HMCCU_SetError ($hash, $rc) if ($rc < 0);
 		
@@ -588,7 +633,8 @@ sub HMCCUDEV_Set ($@)
 				$retmsg .= " on-for-timer on-till"
 					if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $sc, "ON_TIME", 2));
 				$retmsg .= " pct"
-					if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $sc, "LEVEL", 2));
+					if (HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $sc, "LEVEL", 2) ||
+						HMCCU_IsValidDatapoint ($hash, $hash->{ccutype}, $cc, "LEVEL", 2));
 			}
 		}
 
@@ -631,7 +677,7 @@ sub HMCCUDEV_Get ($@)
 	my $result = '';
 	my $rc;
 
-	if ($ccuif eq "VirtualDevices" && $hash->{ccuname} eq "none" && $opt ne 'update') {
+	if ($ccuif eq "VirtualDevices" && $hash->{ccuname} eq "virtual" && $opt ne 'update') {
 		return "HMCCUDEV: Unknown argument $opt, choose one of update:noArg";
 	}
 
@@ -677,19 +723,19 @@ sub HMCCUDEV_Get ($@)
 			return HMCCU_SetError ($hash, "Usage: get $name update [{'State'|'Value'}]");
 		}
 
-		if ($hash->{ccuname} ne 'none') {
+		if ($hash->{ccuname} ne 'virtual') {
 			$rc = HMCCU_GetUpdate ($hash, $ccuaddr, $ccuget);
 			return HMCCU_SetError ($hash, $rc) if ($rc < 0);
 		}
 
 		# Update other devices belonging to group
-		if ($hash->{ccuif} eq "VirtualDevices" && exists ($hash->{ccugroup})) {
-			my @vdevs = split (",", $hash->{ccugroup});
-			foreach my $vd (@vdevs) {
-				$rc = HMCCU_GetUpdate ($hash, $vd, $ccuget);
-				return HMCCU_SetError ($hash, $rc) if ($rc < 0);
-			}
-		}
+# 		if ($hash->{ccuif} eq "VirtualDevices" && exists ($hash->{ccugroup})) {
+# 			my @vdevs = split (",", $hash->{ccugroup});
+# 			foreach my $vd (@vdevs) {
+# 				$rc = HMCCU_GetUpdate ($hash, $vd, $ccuget);
+# 				return HMCCU_SetError ($hash, $rc) if ($rc < 0);
+# 			}
+# 		}
 
 		return undef;
 	}

+ 3 - 3
fhem/core/FHEM/90_at.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 90_at.pm 17252 2018-09-02 09:35:58Z rudolfkoenig $
+# $Id: 90_at.pm 17507 2018-10-11 11:47:46Z rudolfkoenig $
 package main;
 
 use strict;
@@ -61,7 +61,7 @@ at_Define($$)
   }
 
   return "Wrong timespec, use \"[+][*[{count}]]<time or func>\""
-                                        if($tm !~ m/^(\+)?(\*({\d+})?)?(.*)$/);
+                                        if($tm !~ m/^(\+)?(\*(\{\d+\})?)?(.*)$/);
   my ($rel, $rep, $cnt, $tspec) = ($1, $2, $3, $4);
 
   my ($abstime, $err, $hr, $min, $sec, $fn);
@@ -211,7 +211,7 @@ at_adjustAlign($$)
   my($hash, $attrVal) = @_;
 
   my ($tm, $command) = split("[ \t]+", $hash->{DEF}, 2);
-  $tm =~ m/^(\+)?(\*({\d+})?)?(.*)$/;
+  $tm =~ m/^(\+)?(\*(\{\d+\})?)?(.*)$/;
   my ($rel, $rep, $cnt, $tspec) = ($1, $2, $3, $4);
   return "startTimes: $hash->{NAME} is not relative" if(!$rel);
 

+ 76 - 46
fhem/core/FHEM/93_DbLog.pm

@@ -1,5 +1,5 @@
 ############################################################################################################################################
-# $Id: 93_DbLog.pm 17313 2018-09-09 19:37:39Z DS_Starter $
+# $Id: 93_DbLog.pm 17545 2018-10-16 13:46:11Z DS_Starter $
 #
 # 93_DbLog.pm
 # written by Dr. Boris Neubert 2007-12-30
@@ -16,6 +16,12 @@
 ############################################################################################################################################
 #  Versions History done by DS_Starter & DeeSPe:
 #
+# 3.12.5     12.10.2018       charFilter: "\xB0C" substitution by "°C" added and usage in DbLog_Log changed
+# 3.12.4     10.10.2018       return non-saved datasets back in asynch mode only if transaction is used
+# 3.12.3     08.10.2018       Log output of recuceLogNbl enhanced, some functions renamed
+# 3.12.2     07.10.2018       $hash->{HELPER}{REOPEN_RUNS_UNTIL} contains the time the DB is closed 
+# 3.12.1     19.09.2018       use Time::Local (forum:#91285)
+# 3.12.0     04.09.2018       corrected SVG-select (https://forum.fhem.de/index.php/topic,65860.msg815640.html#msg815640)
 # 3.11.0     02.09.2018       reduceLog, reduceLogNbl - optional "days newer than" part added
 # 3.10.10    05.08.2018       commandref revised reducelogNbl
 # 3.10.9     23.06.2018       commandref added hint about special characters in passwords
@@ -146,7 +152,7 @@
 # 2.10.8     27.01.2017       DbLog_setinternalcols delayed at fhem start
 # 2.10.7     25.01.2017       $hash->{HELPER}{COLSET} in DbLog_setinternalcols, DbLog_Push changed due to 
 #                             issue Turning on AutoCommit failed
-# 2.10.6     24.01.2017       DbLog_connect changed "connect_cashed" to "connect", DbLog_Get, chartQuery now uses 
+# 2.10.6     24.01.2017       DbLog_connect changed "connect_cashed" to "connect", DbLog_Get, DbLog_chartQuery now uses 
 #                             DbLog_ConnectNewDBH, Attr asyncMode changed -> delete reading cacheusage reliable if mode was switched
 # 2.10.5     23.01.2017       count, userCommand, deleteOldDays now uses DbLog_ConnectNewDBH
 #                             DbLog_Push line 1107 changed
@@ -156,7 +162,7 @@
 # 2.10.1     19.01.2017       commandref edited, cache events don't get lost even if other errors than "db not available" occure  
 # 2.10       18.10.2017       new attribute cacheLimit, showNotifyTime
 # 2.9.3      17.01.2017       new sub DbLog_ConnectNewDBH (own new dbh for separate use in functions except logging functions),
-#                             DbLog_sampleDataFn, dbReadings now use DbLog_ConnectNewDBH
+#                             DbLog_sampleDataFn, DbLog_dbReadings now use DbLog_ConnectNewDBH
 # 2.9.2      16.01.2017       new bugfix for SQLite issue SVGs, DbLog_Log changed to $dev_hash->{CHANGETIME}, DbLog_Push 
 #                             changed (db handle new separated)
 # 2.9.1      14.01.2017       changed DbLog_ParseEvent to CallInstanceFn, renamed flushCache to purgeCache,
@@ -204,10 +210,11 @@ eval "use DBI;1" or my $DbLogMMDBI = "DBI";
 use Data::Dumper;
 use Blocking;
 use Time::HiRes qw(gettimeofday tv_interval);
+use Time::Local;
 use Encode qw(encode_utf8);
 no if $] >= 5.017011, warnings => 'experimental::smartmatch'; 
 
-my $DbLogVersion = "3.11.0";
+my $DbLogVersion = "3.12.5";
 
 my %columns = ("DEVICE"  => 64,
                "TYPE"    => 64,
@@ -217,7 +224,7 @@ my %columns = ("DEVICE"  => 64,
                "UNIT"    => 32
               );
 					 
-sub dbReadings($@);
+sub DbLog_dbReadings($@);
 
 ################################################################
 sub DbLog_Initialize($)
@@ -585,6 +592,7 @@ sub DbLog_Set($@) {
             DbLog_ConnectPush($hash);
             if($hash->{HELPER}{REOPEN_RUNS}) {
 			    delete $hash->{HELPER}{REOPEN_RUNS};
+                delete $hash->{HELPER}{REOPEN_RUNS_UNTIL};
                 RemoveInternalTimer($hash, "reopen");
             }
 			DbLog_execmemcache($hash) if($async);
@@ -607,7 +615,8 @@ sub DbLog_Set($@) {
 			my $ts = (split(" ",FmtDateTime(gettimeofday()+$a[2])))[1];
 			Log3($name, 2, "DbLog $name: Connection closed until $ts ($a[2] seconds).");
 			readingsSingleUpdate($hash, "state", "closed until $ts ($a[2] seconds)", 1);
-            InternalTimer(gettimeofday()+$a[2], "DbLog_reopen", $hash, 0);			
+            InternalTimer(gettimeofday()+$a[2], "DbLog_reopen", $hash, 0);
+            $hash->{HELPER}{REOPEN_RUNS_UNTIL} = $ts;			
 		}
     }
     elsif ($a[1] eq 'rereadcfg') {
@@ -946,7 +955,6 @@ sub DbLog_ParseEvent($$$)
   if   ($reading =~ m(^temperature)) { $unit= "°C"; } # wenn reading mit temperature beginnt
   elsif($reading =~ m(^humidity)) { $unit= "%"; }
 
-
   # the interpretation of the argument depends on the device type
   # EMEM, M232Counter, M232Voltage return plain numbers
   if(($type eq "M232Voltage") ||
@@ -1276,6 +1284,8 @@ sub DbLog_Log($$) {
               if(!defined $value) {$value = "";}
               if(!defined $unit || $unit eq "") {$unit = AttrVal("$dev_name", "unit", "");}
               
+              $unit = DbLog_charfilter($unit) if(AttrVal($name, "useCharfilter",0));
+              
               # Devices / Readings ausschließen durch Attribut "excludeDevs"
               # attr <device> excludeDevs [<devspec>#]<Reading1>,[<devspec>#]<Reading2>,[<devspec>#]<Reading..>
               my ($exc,@excldr,$ds,$rd,@exdvs);
@@ -2091,7 +2101,7 @@ sub DbLog_PushAsync(@) {
       $errorh = $@;
       Log3 $hash->{NAME}, 2, "DbLog $name -> Error table history - $errorh";
       $error = encode_base64($errorh,"");
-      $rowlback = $rowlist;	
+      $rowlback = $rowlist if($useta);	# nicht gespeicherte Datensätze nur zurück geben wenn Transaktion ein
   } 
   
   # update or insert current
@@ -2262,11 +2272,12 @@ sub DbLog_PushAsyncAborted(@) {
 sub DbLog_explode_datetime($%) {
   my ($t, %def) = @_;
   my %retv;
-
+  
   my (@datetime, @date, @time);
   @datetime = split(" ", $t); #Datum und Zeit auftrennen
   @date = split("-", $datetime[0]);
   @time = split(":", $datetime[1]) if ($datetime[1]);
+  
   if ($date[0]) {$retv{year}  = $date[0];} else {$retv{year}  = $def{year};}
   if ($date[1]) {$retv{month} = $date[1];} else {$retv{month} = $def{month};}
   if ($date[2]) {$retv{day}   = $date[2];} else {$retv{day}   = $def{day};}
@@ -2275,8 +2286,8 @@ sub DbLog_explode_datetime($%) {
   if ($time[2]) {$retv{second}= $time[2];} else {$retv{second}= $def{second};}
 
   $retv{datetime}=DbLog_implode_datetime($retv{year}, $retv{month}, $retv{day}, $retv{hour}, $retv{minute}, $retv{second});
-
-  #Log 1, Dumper(%retv);
+  
+  # Log 1, Dumper(%retv);
   return %retv
 }
 
@@ -2504,7 +2515,7 @@ sub DbLog_Get($@) {
   my $utf8 = defined($hash->{UTF8})?$hash->{UTF8}:0;
   my $dbh;
   
-  return dbReadings($hash,@a) if $a[1] =~ m/^Readings/;
+  return DbLog_dbReadings($hash,@a) if $a[1] =~ m/^Readings/;
 
   return "Usage: get $a[0] <in> <out> <from> <to> <column_spec>...\n".
      "  where column_spec is <device>:<reading>:<default>:<fn>\n" .
@@ -2534,8 +2545,8 @@ sub DbLog_Get($@) {
   } elsif($outf eq "array"){
 
   } elsif(lc($outf) eq "webchart") {
-    # redirect the get request to the chartQuery function
-    return chartQuery($hash, @_);
+    # redirect the get request to the DbLog_chartQuery function
+    return DbLog_chartQuery($hash, @_);
   }
 
   my @readings = ();
@@ -2549,7 +2560,14 @@ sub DbLog_Get($@) {
   %to_datetime   = DbLog_explode_datetime($to, DbLog_explode_datetime("2099-01-01 00:00:00", ()));
   $from = $from_datetime{datetime};
   $to = $to_datetime{datetime};
-
+  
+  if($to =~ /(\d{4})-(\d{2})-(\d{2}) 23:59:59/) {
+     # 03.09.2018 : https://forum.fhem.de/index.php/topic,65860.msg815640.html#msg815640
+     $to =~ /(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/;
+     my $tc = timelocal($6, $5, $4, $3, $2-1, $1-1900);
+     $tc++;
+     $to = strftime "%Y-%m-%d %H:%M:%S", localtime($tc);
+  }
 
   my ($retval,$retvaldummy,$hour,$sql_timestamp, $sql_device, $sql_reading, $sql_value, $type, $event, $unit) = "";
   my @ReturnArray;
@@ -2696,12 +2714,12 @@ sub DbLog_Get($@) {
     $stm2 .= "AND READING LIKE '".$readings[$i]->[1]."' " if(($readings[$i]->[1] !~ m(^%$)) && ($readings[$i]->[1] =~ m(\%)));
 
     $stm2 .= "AND TIMESTAMP >= $sqlspec{from_timestamp} ";
-    $stm2 .= "AND TIMESTAMP < $sqlspec{to_timestamp} ";
+    $stm2 .= "AND TIMESTAMP <= $sqlspec{to_timestamp} ";           # 03.09.2018 : https://forum.fhem.de/index.php/topic,65860.msg815640.html#msg815640            
     $stm2 .= "ORDER BY TIMESTAMP";
 
     if($deltacalc) {
       $stmdelta .= "AND TIMESTAMP >= $sqlspec{from_timestamp} ";
-      $stmdelta .= "AND TIMESTAMP < $sqlspec{to_timestamp} ";
+      $stmdelta .= "AND TIMESTAMP <= $sqlspec{to_timestamp} ";     # 03.09.2018 : https://forum.fhem.de/index.php/topic,65860.msg815640.html#msg815640    
 
       $stmdelta .= "GROUP BY $sqlspec{order_by_hour} " if($deltacalc);
       $stmdelta .= "ORDER BY TIMESTAMP";
@@ -2710,7 +2728,6 @@ sub DbLog_Get($@) {
       $stm = $stm2;
     }
 
-
     Log3 $hash->{NAME}, 4, "Processing Statement: $stm";
 
     my $sth= $dbh->prepare($stm) ||
@@ -3853,12 +3870,13 @@ return($useac,$useta);
 }
 
 ###############################################################################
-#              Zeichencodierung für Payload filtern 
+#              Zeichen von Feldevents filtern 
 ###############################################################################
 sub DbLog_charfilter ($) { 
   my ($txt) = @_;
-  
-  # nur erwünschte Zeichen in payload, ASCII %d32-126
+  my ($p,$a);
+
+  # nur erwünschte Zeichen ASCII %d32-126 und Sonderzeichen
   $txt =~ s/ß/ss/g;
   $txt =~ s/ä/ae/g;
   $txt =~ s/ö/oe/g;
@@ -3867,7 +3885,11 @@ sub DbLog_charfilter ($) {
   $txt =~ s/Ö/Oe/g;
   $txt =~ s/Ü/Ue/g;
   $txt =~ s/€/EUR/g;
-  $txt =~ tr/ A-Za-z0-9!"#$%&'()*+,-.\/:;<=>?@[\\]^_`{|}~//cd;      
+  $txt =~ s/\xb0/1degree1/g;
+  
+  $txt =~ tr/ A-Za-z0-9!"#$%&'()*+,-.\/:;<=>?@[\\]^_`{|}~//cd;
+  
+  $txt =~ s/1degree1/°/g;
   
 return($txt);
 }
@@ -3877,7 +3899,7 @@ return($txt);
 #########################################################################################
 sub DbLog_reduceLog($@) {
     my ($hash,@a) = @_;
-    my ($ret,$row,$filter,$exclude,$c,$day,$hour,$lastHour,$updDate,$updHour,$average,$processingDay,$lastUpdH,%hourlyKnown,%averageHash,@excludeRegex,@dayRows,@averageUpd,@averageUpdD);
+    my ($ret,$row,$err,$filter,$exclude,$c,$day,$hour,$lastHour,$updDate,$updHour,$average,$processingDay,$lastUpdH,%hourlyKnown,%averageHash,@excludeRegex,@dayRows,@averageUpd,@averageUpdD);
     my ($name,$startTime,$currentHour,$currentDay,$deletedCount,$updateCount,$sum,$rowCount,$excludeCount) = ($hash->{NAME},time(),99,0,0,0,0,0,0);
     my $dbh = DbLog_ConnectNewDBH($hash);
     return if(!$dbh);
@@ -4024,7 +4046,8 @@ sub DbLog_reduceLog($@) {
                             }
                         };
                         if ($@) {
-                            Log3($hash->{NAME}, 3, "DbLog $name: reduceLog average=hour ! FAILED ! for day $processingDay");
+                            $err = $@;
+                            Log3($hash->{NAME}, 2, "DbLog $name - reduceLogNbl ! FAILED ! for day $processingDay: $err");
                             eval {$dbh->rollback() if(!$dbh->{AutoCommit});};
                             @averageUpdD = ();
                         } else {
@@ -4090,7 +4113,8 @@ sub DbLog_reduceLog($@) {
 							}
                         };
                         if ($@) {
-                            Log3($hash->{NAME}, 3, "DbLog $name: reduceLog average=day ! FAILED ! for day $processingDay");
+                            $err = $@;
+                            Log3($hash->{NAME}, 2, "DbLog $name - reduceLogNbl ! FAILED ! for day $processingDay: $err");
                             eval {$dbh->rollback() if(!$dbh->{AutoCommit});};
                         } else {
                             eval {$dbh->commit() if(!$dbh->{AutoCommit});};
@@ -4161,7 +4185,7 @@ sub DbLog_reduceLogNbl($) {
     delete $hash->{HELPER}{REDUCELOG};
     my ($ret,$row,$filter,$exclude,$c,$day,$hour,$lastHour,$updDate,$updHour,$average,$processingDay,$lastUpdH,%hourlyKnown,%averageHash,@excludeRegex,@dayRows,@averageUpd,@averageUpdD);
     my ($startTime,$currentHour,$currentDay,$deletedCount,$updateCount,$sum,$rowCount,$excludeCount) = (time(),99,0,0,0,0,0,0);
-    my $dbh;
+    my ($dbh,$err);
 	
 	Log3 ($name, 5, "DbLog $name -> Start DbLog_reduceLogNbl");
 	
@@ -4175,7 +4199,7 @@ sub DbLog_reduceLogNbl($) {
         eval {$dbh = DBI->connect("dbi:$dbconn", $dbuser, $dbpassword, { PrintError => 0, RaiseError => 1 });};
     }
     if ($@) {
-        my $err = encode_base64($@,"");
+        $err = encode_base64($@,"");
         Log3 ($name, 2, "DbLog $name -> DbLog_reduceLogNbl - $@");
         Log3 ($name, 5, "DbLog $name -> DbLog_reduceLogNbl finished");
         return "$name|''|$err";
@@ -4225,7 +4249,7 @@ sub DbLog_reduceLogNbl($) {
                            ."TIMESTAMP < $ots".($nts?" AND TIMESTAMP >= $nts ":" ")."ORDER BY TIMESTAMP ASC");  # '' was EVENT, no longer in use
 		     };
         if ($@) {
-            my $err = encode_base64($@,"");
+            $err = encode_base64($@,"");
             Log3 ($name, 2, "DbLog $name -> DbLog_reduceLogNbl - $@");
             Log3 ($name, 5, "DbLog $name -> DbLog_reduceLogNbl finished");
             return "$name|''|$err";
@@ -4233,7 +4257,7 @@ sub DbLog_reduceLogNbl($) {
 		
 		eval { $sth_get->execute(); };
         if ($@) {
-            my $err = encode_base64($@,"");
+            $err = encode_base64($@,"");
             Log3 ($name, 2, "DbLog $name -> DbLog_reduceLogNbl - $@");
             Log3 ($name, 5, "DbLog $name -> DbLog_reduceLogNbl finished");
             return "$name|''|$err";
@@ -4280,7 +4304,8 @@ sub DbLog_reduceLogNbl($) {
                                 }
                             };
                             if ($@) {
-                                Log3($hash->{NAME}, 3, "DbLog $name: reduceLogNbl ! FAILED ! for day $processingDay");
+                                $err = $@;
+                                Log3($hash->{NAME}, 2, "DbLog $name - reduceLogNbl ! FAILED ! for day $processingDay: $err");
                                 eval {$dbh->rollback() if(!$dbh->{AutoCommit});};
 								if ($@) {
                                     Log3 ($name, 2, "DbLog $name -> DbLog_reduceLogNbl - $@");
@@ -4347,7 +4372,8 @@ sub DbLog_reduceLogNbl($) {
                             }
                         };
                         if ($@) {
-                            Log3($hash->{NAME}, 3, "DbLog $name: reduceLogNbl average=hour ! FAILED ! for day $processingDay");
+                            $err = $@;
+                            Log3($hash->{NAME}, 2, "DbLog $name - reduceLogNbl average=hour ! FAILED ! for day $processingDay: $err");
                             eval {$dbh->rollback() if(!$dbh->{AutoCommit});};
 							if ($@) {
                                 Log3 ($name, 2, "DbLog $name -> DbLog_reduceLogNbl - $@");
@@ -4720,6 +4746,7 @@ sub DbLog_reopen ($){
   if(DbLog_ConnectPush($hash)) {
       # Statusbit "Kein Schreiben in DB erlauben" löschen
       my $delay = delete $hash->{HELPER}{REOPEN_RUNS};
+      delete $hash->{HELPER}{REOPEN_RUNS_UNTIL};
 	  Log3($name, 2, "DbLog $name: Database connection reopened (it was $delay seconds closed).") if($delay);
 	  readingsSingleUpdate($hash, "state", "reopened", 1);
 	  $hash->{HELPER}{OLDSTATE} = "reopened";
@@ -4852,7 +4879,7 @@ return ($desc, \@htmlArr, join("<br>", @example));
 # Error handling, returns a JSON String
 #
 ################################################################
-sub jsonError($) {
+sub DbLog_jsonError($) {
   my $errormsg = $_[0]; 
   my $json = '{"success": "false", "msg":"'.$errormsg.'"}';
   return $json;
@@ -4864,7 +4891,7 @@ sub jsonError($) {
 # Prepare the SQL String
 #
 ################################################################
-sub prepareSql(@) {
+sub DbLog_prepareSql(@) {
 
     my ($hash, @a) = @_;
     my $starttime = $_[5];
@@ -5042,14 +5069,14 @@ sub prepareSql(@) {
 # Do the query
 #
 ################################################################
-sub chartQuery($@) {
+sub DbLog_chartQuery($@) {
 
-    my ($sql, $countsql) = prepareSql(@_);
+    my ($sql, $countsql) = DbLog_prepareSql(@_);
 
     if ($sql eq "error") {
-       return jsonError("Could not setup SQL String. Maybe the Database is busy, please try again!");
+       return DbLog_jsonError("Could not setup SQL String. Maybe the Database is busy, please try again!");
     } elsif ($sql eq "errordb") {
-       return jsonError("The Database Type is not supported!");
+       return DbLog_jsonError("The Database Type is not supported!");
     }
 
     my ($hash, @a) = @_;
@@ -5060,10 +5087,10 @@ sub chartQuery($@) {
     
     if (defined $countsql && $countsql ne "") {
         my $query_handle = $dbhf->prepare($countsql) 
-        or return jsonError("Could not prepare statement: " . $dbhf->errstr . ", SQL was: " .$countsql);
+        or return DbLog_jsonError("Could not prepare statement: " . $dbhf->errstr . ", SQL was: " .$countsql);
         
         $query_handle->execute() 
-        or return jsonError("Could not execute statement: " . $query_handle->errstr);
+        or return DbLog_jsonError("Could not execute statement: " . $query_handle->errstr);
 
         my @data = $query_handle->fetchrow_array();
         $totalcount = join(", ", @data);
@@ -5072,11 +5099,11 @@ sub chartQuery($@) {
 
     # prepare the query
     my $query_handle = $dbhf->prepare($sql) 
-        or return jsonError("Could not prepare statement: " . $dbhf->errstr . ", SQL was: " .$sql);
+        or return DbLog_jsonError("Could not prepare statement: " . $dbhf->errstr . ", SQL was: " .$sql);
     
     # execute the query
     $query_handle->execute() 
-        or return jsonError("Could not execute statement: " . $query_handle->errstr);
+        or return DbLog_jsonError("Could not execute statement: " . $query_handle->errstr);
     
     my $columns = $query_handle->{'NAME'};
     my $columncnt;
@@ -5135,7 +5162,7 @@ return $jsonstring;
 # get <dbLog> ReadingsVal       <device> <reading> <default>
 # get <dbLog> ReadingsTimestamp <device> <reading> <default>
 #
-sub dbReadings($@) {
+sub DbLog_dbReadings($@) {
   my($hash,@a) = @_;
   
   my $dbhf = DbLog_ConnectNewDBH($hash);
@@ -5781,15 +5808,16 @@ sub dbReadings($@) {
 	  <code>attr &lt;device&gt; commitMode [basic_ta:on | basic_ta:off | ac:on_ta:on | ac:on_ta:off | ac:off_ta:on]
 	  </code><br>
 	  
-      Change the usage of database autocommit- and/or transaction- behavior. <br> 
-	  This attribute is an advanced feature and should only be used in a concrete case of need or support case. <br><br>
+      Change the usage of database autocommit- and/or transaction- behavior. <br>
+      If transaction "off" is used, not saved datasets are not returned to cache in asynchronous mode. <br>      
+	  This attribute is an advanced feature and should only be used in a concrete situation or support case. <br><br>
 	  
 	  <ul>
       <li>basic_ta:on   - autocommit server basic setting / transaktion on (default) </li>
 	  <li>basic_ta:off  - autocommit server basic setting / transaktion off </li>
 	  <li>ac:on_ta:on   - autocommit on / transaktion on </li>
 	  <li>ac:on_ta:off  - autocommit on / transaktion off </li>
-	  <li>ac:off_ta:on  - autocommit off / transaktion on (autocommit off set transaktion on implicitly) </li>
+	  <li>ac:off_ta:on  - autocommit off / transaktion on (autocommit "off" set transaktion "on" implicitly) </li>
 	  </ul>
 	  
     </ul>
@@ -6887,6 +6915,8 @@ sub dbReadings($@) {
 	  </code><br>
 	  
       Ändert die Verwendung der Datenbank Autocommit- und/oder Transaktionsfunktionen. 
+      Wird Transaktion "aus" verwendet, werden im asynchronen Modus nicht gespeicherte Datensätze nicht an den Cache zurück
+      gegeben.      
 	  Dieses Attribut ist ein advanced feature und sollte nur im konkreten Bedarfs- bzw. Supportfall geändert werden.<br><br>
 	  
 	  <ul>
@@ -6894,7 +6924,7 @@ sub dbReadings($@) {
 	  <li>basic_ta:off  - Autocommit Servereinstellung / Transaktion aus </li>
 	  <li>ac:on_ta:on   - Autocommit ein / Transaktion ein </li>
 	  <li>ac:on_ta:off  - Autocommit ein / Transaktion aus </li>
-	  <li>ac:off_ta:on  - Autocommit aus / Transaktion ein (Autocommit aus impliziert Transaktion ein) </li>
+	  <li>ac:off_ta:on  - Autocommit aus / Transaktion ein (Autocommit "aus" impliziert Transaktion "ein") </li>
 	  </ul>
 	  
     </ul>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 683 - 453
fhem/core/FHEM/93_DbRep.pm


+ 31 - 6
fhem/core/FHEM/93_FHEM2FHEM.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 93_FHEM2FHEM.pm 17126 2018-08-12 11:56:13Z rudolfkoenig $
+# $Id: 93_FHEM2FHEM.pm 17361 2018-09-17 11:44:10Z rudolfkoenig $
 package main;
 
 use strict;
@@ -370,9 +370,19 @@ FHEM2FHEM_Attr(@)
   port on the remote FHEM, defaults to 7072. The optional :SSL suffix is
   needed, if the remote FHEM configured SSL for this telnet port. In this case
   the IO::Socket::SSL perl module must be installed for the local host too.<br>
-
-  Note: if the remote FHEM is on a separate host, the telnet port on the remote
-  FHEM musst be specified with the global option.<br>
+  <br>
+  Notes:
+  <ul>
+    <li>if the remote FHEM is on a separate host, the telnet port on the remote
+      FHEM must be specified with the global option.</li>
+    <li>as of FHEM 5.9 the telnet instance is not configured in default fhem.cfg, so
+    it has to be defined, e.g. as
+    <ul><code>
+    define telnetPort telnet 7072 global
+    </code></ul>
+    </li>
+  </ul>
+  <br>
 
   The next parameter specifies the connection
   type:
@@ -479,8 +489,23 @@ FHEM2FHEM_Attr(@)
     SSL-Verschl&uuml;sselung voraussetzt.  Auch auf dem lokalen Host muss dann
     das Perl-Modul IO::Socket::SSL installiert sein.<br>
 
-   Anmerkung: Wenn das remote FHEM auf einem eigenen Host l&auml;uft, muss
-   "telnetPort" des remote FHEM als global festgelegt sein.  <br>
+  <br>
+  Achtung:
+  <ul>
+    <li>
+     Wenn das remote FHEM auf einem eigenen Host l&auml;uft, muss
+     "telnetPort" des remote FHEM mit der global Option definiert sein.
+      </li>
+    <li>ab FHEM Version 5.9 wird in der ausgelieferten Initialversion der fhem.cfg
+      keine telnet Instanz vorkonfiguriert, man muss sie z.Bsp.
+      folgenderma&szlig;en definieren:
+      <ul><code>
+      define telnetPort telnet 7072 global
+      </code></ul>
+      </li>
+  </ul>
+  <br>
+
 
    Der n&auml;chste Parameter spezifiziert den Verbindungs-Typ:
    <ul>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 922 - 333
fhem/core/FHEM/93_Log2Syslog.pm


+ 74 - 43
fhem/core/FHEM/95_Alarm.pm

@@ -6,7 +6,7 @@
 #
 # Prof. Dr. Peter A. Henning
 #
-# $Id: 95_Alarm.pm 16368 2018-03-10 07:01:51Z phenning $
+# $Id: 95_Alarm.pm 17344 2018-09-14 14:05:23Z phenning $
 #
 ########################################################################################
 #
@@ -43,13 +43,15 @@ my $alarmlinkname   = "Alarms";    # link text
 my $alarmhiddenroom = "AlarmRoom"; # hidden room
 my $alarmpublicroom = "Alarm";     # public room
 my $alarmno         = 8;
-my $alarmversion    = "4.05";
+my $alarmversion    = "5.0";
 
 my %alarm_transtable_EN = ( 
     "ok"                =>  "OK",
     "notok"             =>  "Not OK",
+    "cond"              =>  "Condition",
     "start"             =>  "Start",
     "end"               =>  "End",
+    "autocan"           =>  "AutoCancel",
     "status"            =>  "Status",
     "notstarted"        =>  "Not started",
     "next"              =>  "Next",
@@ -101,9 +103,11 @@ my %alarm_transtable_EN = (
     
  my %alarm_transtable_DE = ( 
     "ok"                =>  "OK",
-    "notok"             =>  "Nicht OK",
+    "notok"             =>  "Nicht OK",  
+    "cond"              =>  "Bedingung",
     "start"             =>  "Start",
     "end"               =>  "Ende",
+    "autocan"           =>  "AutoWiderruf",
     "status"            =>  "Status",
     "notstarted"        =>  "Nicht gestartet",
     "next"              =>  "Nächste",
@@ -174,7 +178,7 @@ sub Alarm_Initialize ($) {
   my $attst            = "lockstate:locked,unlocked testbutton:0,1 statedisplay:simple,color,table,none noicons iconmap disarmcolor ".
                          "armwaitcolor armcolor alarmcolor armdelay armwait armact disarmact cancelact";
   for( my $level=0;$level<$alarmno;$level++ ){
-     $attst .=" level".$level."start level".$level."end level".$level."msg level".$level."xec level".$level."onact level".$level."offact ";
+     $attst .=" level".$level."cond level".$level."start level".$level."end level".$level."autocan level".$level."msg level".$level."onact level".$level."offact ";
   }
   $hash->{AttrList}    = $attst;
   
@@ -198,6 +202,7 @@ sub Alarm_Initialize ($) {
   return undef;
 }
 
+
 #########################################################################################
 #
 # Alarm_Define - Implements DefFn function
@@ -253,23 +258,6 @@ sub Alarm_Define ($$) {
   return;
 }
 
-sub Alarm_transform($){
-  my ($hash) = @_;
-
-  Log 1,"[Alarm] transforming old data format into new one";
-
-  my $md = 0;
-  for( my $i=0;$i<$alarmno;$i++){ 
-    if( defined(AttrVal($hash->{NAME},"level".$i."xec",undef)) ){
-      $md = 1;
-      $hash->{DATA}{"armstate"}{"level".$i} = AttrVal($hash->{NAME},"level".$i."xec","");
-      fhem("deleteattr ".$hash->{NAME}." level".$i."xec");
-    }
-  }
-  Alarm_save($hash)
-    if( $md==1 );
-}
-
 #########################################################################################
 #
 # Alarm_Undef - Implements Undef function
@@ -451,6 +439,11 @@ sub Alarm_getsettings($$$){
     if( $aval[0] eq "" || $aval[1] eq "" ){
       Log3 $hash, 1, "[Alarm] Settings $avl incomplete for alarmActor $dev";
     }
+    #-- check if empty unset action
+    if( !defined($aval[3]) ){
+      $aval[3] = $aval[2];
+      $aval[2] = "";
+    }
     #-- check delay time
     if( $aval[3] =~ /^\d+$/ ){
       if( $aval[3] > 3559 ){
@@ -608,29 +601,51 @@ sub Alarm_Exec($$$$$){
    if( $act eq "on" ){
       #-- only if this level is armed and not yet active
       if( ($xec eq "armed") && ($xac eq "armed") ){ 
+         #-- check for condition
+         my $cond = AttrVal($name, "level".$level."cond", 1);
+         $cond =~ s/\$NAME/$dev/g;  
+         if( eval($cond) ne "1" ){
+           Log3 $hash,1,"[Alarm $level] Cannot be executed due to condition {$cond} not equal to 1 for level".$level."cond";
+           return;
+         }
+     
          #-- check for time
-         my $start = AttrVal($name, "level".$level."start", 0);
+         my $start = AttrVal($name, "level".$level."start", "0:00");
          if(  index($start, '{') != -1){
            $start = eval($start);
          }
+     
          my @st = split(':',$start);
          if( (int(@st)>3) || (int(@st)<2) || ($st[0] > 23) || ($st[0] < 0) || ($st[1] > 59) || ($st[1] < 0) ){
            Log3 $hash,1,"[Alarm $level] Cannot be executed due to wrong time spec $start for level".$level."start";
            return;
          }
          
-         my $end   = AttrVal($name, "level".$level."end", 0);
+         my $end   = AttrVal($name, "level".$level."end", "23:59");
          if(  index($end, '{') != -1){
            $end = eval($end);
          }
+    
          my @et  = split(':',$end);
          if( (int(@et)>3) || (int(@et)<2) || ($et[0] > 23) || ($et[0] < 0) || ($et[1] > 59) || ($et[1] < 0) ){
            Log3 $hash,1,"[Alarm $level] Cannot be executed due to wrong time spec $end for level".$level."end";
            return;
          }
          
+         my $dur   = AttrVal($name, "level".$level."autocan", "0:00");
+         if(  index($dur, '{') != -1){
+           $dur = eval($dur);
+         }
+      
+         my @dt  = split(':',$dur);
+         if( (int(@dt)>3) || (int(@dt)<2) || ($dt[0] > 23) || ($dt[0] < 0) || ($dt[1] > 59) || ($dt[1] < 0) ){
+           Log3 $hash,1,"[Alarm $level] Cannot be executed due to wrong time spec $dur for level".$level."autocan";
+           return;
+         }
+         
          my $stp = $st[0]*60+$st[1];
          my $etp = $et[0]*60+$et[1];
+         my $dtp = $dt[0]*60+$dt[1];
      
          my ($sec, $min, $hour, $day, $month, $year, $wday, $yday, $isdst) = localtime(time);
          my $ntp = $hour*60+$min;
@@ -659,6 +674,15 @@ sub Alarm_Exec($$$$$){
               $msg = Alarm_getstate($hash)." ".$mga;
               readingsSingleUpdate( $hash, "state", $msg, 1 );
               $msg = "[Alarm $level] raised from device $dev with event $evt";
+              
+              #-- set up timer for auto cancel
+              if( $dur ne "0:00" ){
+                my $cmd  = "alarm".$level.".autocancel at +$dur {Alarm_Exec('$name',$level,'".$alarm_tt->{"autocan"}."','dummy','off')}";
+                CommandDefine(undef,$cmd);
+                CommandAttr (undef,'alarm'.$level.'.autocancel room '.$alarmpublicroom); 
+                CommandAttr (undef,'alarm'.$level.'.autocancel group alarmHelper'); 
+              }
+              
               #-- calling actors AFTER state update
               $cmd = AttrVal($name, "level".$level."onact", 0);
               $cmd =~ s/\$NAME/$dev/g;
@@ -686,6 +710,7 @@ sub Alarm_Exec($$$$$){
       if( ($xac ne "armed")&&($xac ne "disarmed") ){
          #-- TODO: ohne intAt auskommen
          #-- deleting all running ats
+         CommandDelete(undef,'alarm'.$level.'.autocancel');
          $dly = sprintf("alarm%1ddly",$level);
          foreach my $d ( devspec2array("NAME=alarm.dly.*")) {
             Log3 $hash,1,"[Alarm] Killing delayed action $d";
@@ -834,9 +859,6 @@ sub Alarm_CreateNotifiers($){
      return "State locked, cannot create new notifiers";
   }
   
-  #-- temporary code: transferm from attributes to hash
-  #Alarm_transform($hash);
-  
   for( my $level=0;$level<$alarmno;$level++ ){
   
      #-- delete old defs in any case
@@ -1198,6 +1220,7 @@ sub Alarm_widget($){
   }
 }
 
+
 #########################################################################################
 #
 # Alarm_Html - returns HTML code for the Alarm page
@@ -1206,8 +1229,7 @@ sub Alarm_widget($){
 #
 #########################################################################################
 
-sub Alarm_Html($)
-{
+sub Alarm_Html($){
 	my ($name) = @_; 
 
     my $ret = "";
@@ -1333,28 +1355,38 @@ sub Alarm_Html($)
     $ret .= "</td><td></td></tr><tr class=\"odd\"><td class=\"col1\" style=\"text-align:right\">".$alarm_tt->{"cancelbutton"}."&nbsp;&#8608;</td><td class=\"col2\" style=\"text-align:right\"> ".$alarm_tt->{"cancelaction"}." ";
     $ret .= sprintf("<input type=\"text\" id=\"cancelaction\" size=\"50\" maxlength=\"512\" value=\"%s\"/>",(AttrVal($name, "cancelact","") eq "1")?"":AttrVal($name, "cancelact","")); 
     $ret .= "</td><td></td></tr></table></td></tr>";
-    $ret .= "<tr class=\"odd\"><td class=\"col1\">".$alarm_tt->{"level"}."</td><td class=\"col2\">".$alarm_tt->{"time"}." [hh:mm]<br/>".
-             $alarm_tt->{"start"}."&nbsp;&nbsp;&nbsp;&nbsp;".$alarm_tt->{"end"}."&nbsp;</td><td class=\"col3\">".$alarm_tt->{"messagepart"}." II</td>".
-            "<td class=\"col4\">".$alarm_tt->{"arm"}."/".$alarm_tt->{"cancel"}."</td></tr>\n";
+    $ret .= "<tr class=\"odd\"><td colspan=\"3\"style=\"padding:20px 0 20px 0;\"><table><tr><td></td><td></td><td class=\"col2\" colspan=\"3\">".$alarm_tt->{"time"}." [hh:mm]<td/></tr>".
+            "<tr><td class=\"col1\"  style=\"padding:0 10px 0 10px;\">".$alarm_tt->{"level"}."</td><td class=\"col2\" style=\"padding:0 10px 0 10px;\">".$alarm_tt->{"cond"}.
+            "</td><td class=\"col2\" style=\"padding:0 10px 0 10px;\">".$alarm_tt->{"start"}."</td><td class=\"col2\" style=\"padding:0 10px 0 10px;\">".$alarm_tt->{"end"}.
+            "</td><td class=\"col2\" style=\"padding:0 10px 0 15px;\">".$alarm_tt->{"autocan"}.
+            "</td><td class=\"col3\" style=\"padding:0 10px 0 10px;\">".$alarm_tt->{"messagepart"}." II</td>".
+            "<td class=\"col4\"style=\"padding:0 10px 0 10px;\">".$alarm_tt->{"arm"}."</td><td class=\"col4\">".$alarm_tt->{"cancel"}."</td></tr>\n";
+    #-- foreach level
     for( my $k=0;$k<$alarmno;$k++ ){
       $row++;
-      my $sval = AttrVal($name, "level".$k."start", 0);
+      my $cval = AttrVal($name, "level".$k."cond", 1);
+      my $sval = AttrVal($name, "level".$k."start", "0:00");
       $sval = ""
         if( $sval eq "1");
-      my $eval = AttrVal($name, "level".$k."end", 0);
+      my $eval = AttrVal($name, "level".$k."end", "23:59");
       $eval = ""
         if( $eval eq "1");
-      my $mval = AttrVal($name, "level".$k."msg", 0);
+      my $oval = AttrVal($name, "level".$k."autocan", "0:00");
+      $oval = ""
+        if( $oval eq "1");
+      my $mval = AttrVal($name, "level".$k."msg", "--");
       $mval = ""
         if( $mval eq "1");
 
       my $xval = $hash->{DATA}{"armstate"}{"level".$k};
       $ret .= sprintf("<tr class=\"%s\"><td class=\"col1\">".$alarm_tt->{"alarm"}." $k</td>\n", ($row&1)?"odd":"even"); 
-      $ret .=                          "<td class=\"col2\"><input type=\"text\" id=\"l".$k."s\" size=\"4\" maxlength=\"120\" value=\"$sval\"/>&nbsp;&nbsp;&nbsp;".
-                                                          "<input type=\"text\" id=\"l".$k."e\" size=\"4\" maxlength=\"120\" value=\"$eval\"/></td>".
+      $ret .=                          "<td class=\"col2\" style=\"padding:0 10px 0 10px;\"><input type=\"text\" id=\"l".$k."c\" size=\"15\" maxlength=\"512\" value=\"$cval\"/></td><td class=\"col2\">".
+                                                          "<input type=\"text\" id=\"l".$k."s\" size=\"4\" maxlength=\"120\" value=\"$sval\"/></td><td class=\"col2\">".
+                                                          "<input type=\"text\" id=\"l".$k."e\" size=\"4\" maxlength=\"120\" value=\"$eval\"/></td><td class=\"col2\">".
+                                                          "<input type=\"text\" id=\"l".$k."o\" size=\"4\" maxlength=\"120\" value=\"$oval\"/></td>".
               "<td class=\"col3\"><input type=\"text\" id=\"l".$k."m\" size=\"25\" maxlength=\"256\" value=\"$mval\"/></td>";
-      $ret .= sprintf("<td class=\"col4\"><input type=\"checkbox\" id=\"l".$k."x\" %s onclick=\"javascript:alarm_arm('$name','$k')\"/>",($xval eq "armed")?"checked=\"checked\"":"").
-              "<input type=\"button\" value=\"".$alarm_tt->{"cancel"}."\" onclick=\"javascript:alarm_cancel('$name','$k')\"/></td></tr>\n";
+      $ret .= sprintf("<td class=\"col4\" width=\"30px\" align=\"center\"><input type=\"checkbox\" id=\"l".$k."x\" %s onclick=\"javascript:alarm_arm('$name','$k')\"/>",($xval eq "armed")?"checked=\"checked\"":"").
+              "</td><td class=\"col4\"><input type=\"button\" value=\"".$alarm_tt->{"cancel"}."\" onclick=\"javascript:alarm_cancel('$name','$k')\"/></td></tr>\n";
     }    
     $ret .= "</table></td></tr>";
    
@@ -1428,7 +1460,7 @@ sub Alarm_Html($)
              if( AttrVal($name,"testbutton",0) == 1);
            $ret .= "</td><td class=\"col4\"><input type=\"text\" name=\"delay\" size=\"5\" maxlength=\"8\" value=\"$aval[3]\"/></td></tr>\n";
        }
-    }  
+    } 
 	$ret .= "</table></td></tr>\n";
 	
 	$ret .= "</table>";
@@ -1516,8 +1548,7 @@ sub Alarm_Html($)
                     <li> simple = OXOOOOO</li>
                     <li> color = <span style="color:lightgray"> 0 </span><span style="font-weight:bold;color:#53f3c7">1 <span style="font-weight:bold;color:#fd5777"
                                 >2</span> 3 4 5 6 7</span></li>
-                    <li> table = HTML mini table with colored fields for alarms
-                    </li>
+                    <li> table = HTML mini table with colored fields for alarms</li>
                     <li> none = no state display</li>
                 </ul>
             </li>
@@ -1535,14 +1566,14 @@ sub Alarm_Html($)
             <li><a name="alarm_armwait"><code>attr &lt;name&gt; armwait <i>action</i></code></a>
                 <br />FHEM action to be carried out immediately after the arm event</li>
             <li><a name="alarm_armact"><code>attr &lt;name&gt; armact <i>action</i></code></a>
-                <br />FHEM action to be carried out at the arme event after the delay time </li>
+                <br />FHEM action to be carried out at the arm event after the delay time </li>
             <li><a name="alarm_disarmact"><code>attr &lt;name&gt; disarmact <i>action</i></code></a>
                 <br />FHEM action to be carried out on the disarming of an alarm</li>
             <li><a name="alarm_cancelact"><code>attr &lt;name&gt; cancelact <i>action</i></code></a>
                 <br />FHEM action to be carried out on the canceling of an alarm</li>
             <li><a name="alarm_internals"></a>For each of the 8 alarm levels, several attributes
                 hold the alarm setup. They should not be changed by hand, but through the web
-                interface to avoid confusion: <code>level&lt;level&gt;start, level&lt;level&gt;end,
+                interface to avoid confusion: <code>level&lt;level&gt;cond, level&lt;level&gt;start, level&lt;level&gt;end, level&lt;level&gt;autocan,
                     level&lt;level&gt;msg, level&lt;level&gt;onact,
                     level&lt;level&gt;offact</code></li>
             <li>Standard attributes <a href="#alias">alias</a>, <a href="#comment">comment</a>, <a

+ 5 - 6
fhem/core/FHEM/95_Astro.pm

@@ -9,7 +9,7 @@
 # Program skeleton (with some errors) by Arnold Barmettler 
 # http://lexikon.astronomie.info/java/sunmoon/
 #
-#  $Id: 95_Astro.pm 17177 2018-08-19 17:32:06Z phenning $
+#  $Id: 95_Astro.pm 17517 2018-10-12 15:45:49Z phenning $
 #
 ########################################################################################
 #
@@ -47,7 +47,7 @@ my $deltaT   = 65;  # Correction time in s
 my %Astro;
 my %Date;
 
-my $astroversion = 1.49;
+my $astroversion = 1.50;
 
 #-- These we may get on request
 my %gets = (
@@ -1217,7 +1217,7 @@ sub Astro_Compute($){
   $Astro{MoonDiameter} = Astro_round($moonCoor->{diameter}*$RAD*60.,1); # angular diameter in arc seconds
   $Astro{MoonAge}      = Astro_round($moonCoor->{age}*$RAD,1);
   $Astro{MoonPhaseN}   = Astro_round($moonCoor->{phasen},2);
-  $Astro{MoonPhaseI}   = $astro_tt->{$moonCoor->{phasei}};
+  $Astro{MoonPhaseI}   = $moonCoor->{phasei};
   $Astro{MoonPhaseS}   = $astro_tt->{$moonCoor->{phases}};
   
   #-- calculate distance from the observer (on the surface of earth) to the center of the moon
@@ -1521,7 +1521,7 @@ sub Astro_Get($@) {
          (default: EN=english). For German output set <code>attr global language DE</code>.</li>
         <li>The time zone is determined automatically from the local settings of the <br/>
         operating system. If geocordinates from a different time zone are used, the results are<br/>
-        not corrected automatically.
+        not corrected automatically.</li>
         <li>Some definitions determining the observer position are used<br/>
         from the global device, i.e.<br/>
         <code>attr global longitude &lt;value&gt;</code><br/>
@@ -1539,7 +1539,6 @@ sub Astro_Get($@) {
         <a name="Astroget"></a>
         <h4>Get</h4>
         Attention: Get-calls are NOT written into the readings of the device ! Readings change only through periodic updates !<br/>
-       </li>
         <ul>
             <li><a name="astro_json"></a>
                 <code>get &lt;name&gt; json [&lt;reading&gt;]</code><br/>
@@ -1581,7 +1580,7 @@ sub Astro_Get($@) {
 <a name="Astro"></a>
 <h3>Astro</h3>
 <ul>
-Absichtlich keine deutsche Dokumentation vorhanden, die englische Version gibt es hier: <a href="/fhem/docs/commandref.html#Astro">Astro</a> 
+<a href="https://wiki.fhem.de/wiki/Modul_Astro">Deutsche Dokumentation im Wiki</a> vorhanden, die englische Version gibt es hier: <a href="/fhem/docs/commandref.html#Astro">Astro</a> 
 </ul>
 =end html_DE
 =cut

+ 198 - 137
fhem/core/FHEM/95_YAAHM.pm

@@ -3,10 +3,13 @@
 # YAAHM.pm
 #
 # Yet Another Auto Home Module for FHEM
+# 
+# Problem: Wenn man trotz Feiertag geweckt werden möchte, wird zwar die Weckzeit richtig gesetzt - 
+# aber da "Wakeup" voM Timer ausgelöst wird und der nach den Zusatzbedingungen daytype etc fragt, gehen die rollläden eben nicht auf
 #
 # Prof. Dr. Peter A. Henning
 #
-# $Id: 95_YAAHM.pm 16731 2018-05-11 16:27:48Z phenning $
+# $Id: 95_YAAHM.pm 17475 2018-10-07 11:20:01Z phenning $
 #
 ########################################################################################
 #
@@ -47,7 +50,7 @@ my $yaahmname;
 my $yaahmlinkname   = "Profile";     # link text
 my $yaahmhiddenroom = "ProfileRoom"; # hidden room
 my $yaahmpublicroom = "Unsorted";    # public room
-my $yaahmversion    = "1.54";
+my $yaahmversion    = "2.02";
 my $firstcall       = 1;
     
 my %yaahm_transtable_EN = ( 
@@ -353,7 +356,7 @@ sub YAAHM_Initialize ($) {
   $hash->{GetFn}       = "YAAHM_Get";
   $hash->{UndefFn}     = "YAAHM_Undef";   
   $hash->{AttrFn}      = "YAAHM_Attr";
-  my $attst            = "linkname publicroom hiddenroom lockstate:locked,unlocked simulation:0,1 ".
+  my $attst            = "linkname publicroom hiddenroom lockstate:locked,unlocked simulation:0,1 norepeat:0,1 ".
                          "modecolor0 modecolor1 modecolor2 modecolor3 statecolor0 statecolor1 statecolor2 statecolor3 ".
                          "timeHelper modeHelper modeAuto:0,1 stateDevices:textField-long stateInterval noicons:0,1 stateWarning stateHelper stateAuto:0,1 ".
                          "holidayDevices:textField-long vacationDevices:textField-long specialDevices:textField-long";
@@ -450,6 +453,10 @@ sub YAAHM_Define ($$) {
    $hash->{DATA}{"DD"}  = ();
    push(@{$hash->{DATA}{"DD"}},{%defaultdayproperties});
    push(@{$hash->{DATA}{"DD"}},{%defaultdayproperties});
+   
+   #-- initial mode and state
+   $hash->{DATA}{"HSM"}{"state"}="unsecured";
+   $hash->{DATA}{"HSM"}{"mode"}="normal";
  }
  
  #-- determine Astro device
@@ -1123,73 +1130,90 @@ sub YAAHM_time {
   my $lval = sprintf("%02d%02d",$hour,$min);
   my $mval = $dailytable{"morning"}[0];
   my $nval = $dailytable{"night"}[0];
-  my $tval = $dailytable{$targettime}[0];
-  
-  $mval =~ s/://;
-  $nval =~ s/://;
-  $tval =~ s/://;
-  
-  #-- targetphase always according to real time, not to command time
-  my $targetphase = ( ($lval >= $mval) && ( $nval > $lval ) ) ? "daytime" : "nighttime";
-  
-  #-- iterate through table to find next event
-  my $nexttime;
-  my $sval;   
-  my $oval="0000";
-  foreach my $key (sort YAAHM_dsort keys %dailytable){
-    $nexttime = $key;
-    $sval     = $dailytable{$key}[0];
-    next
-      if (!defined($sval));
-    $sval     =~ s/://;
-    last
-      if ( ($lval <= $sval) && ( $lval > $oval ) );
-    $oval     = $sval;
-  }
-  my $ma = defined($attr{$name}{"modeAuto"}) && ($attr{$name}{"modeAuto"} == 1);
-  my $sa = defined($attr{$name}{"stateAuto"}) && ($attr{$name}{"stateAuto"} == 1);
-  
-  #Log 1,"===================> YAAHM Fehlersuche ma,sa = $ma,$sa   exec=$exec";
-      
-  #-- automatically leave party mode at morning time or when going to bed
-  # TODO 
-  # TODO Fehler ! Wird nach party um Mitternacht aufgerufen und sichert das Haus, aber in jeder Zeile ein fehler undefined
-  if( $currmode eq "party" && $targettime =~ /(morning)|(sleep)/ && $ma ){
-    $msg  = YAAHM_mode($name,"normal",$exec)."\n";
-    $msg .= YAAHM_state($name,"secured",$exec)."\n"
-      if( $currstate eq "unsecured" && $targettime eq "sleep" && $sa );
   
-  #-- automatically leave absence mode at wakeup time
-  #   Ist das wirklich clever ? Was passiert, wenn der Wecker nicht ausgestellt wurde
-  }elsif( $currmode eq "absence" && $targettime =~ /(wakeup)/ && $ma ){
-    $msg = YAAHM_mode($name,"normal",$exec)."\n";
+  #-- is this a daily timer event ?
+  my $regex = "((".join(")|(",@times)."))";
+  my $isdaily = ( $targettime =~ /$regex/ )?1:0;
+  if( $isdaily ){
+    my $tval = $dailytable{$targettime}[0];
   
-  #-- automatically leave donotdisturb mode at any time event
-  }elsif( $currmode eq "donotdisturb" && $ma ){
-    $msg = YAAHM_mode($name,"normal",$exec)."\n";
-    
-  #-- automatically secure the house at night time or when going to bed (if not absence, and if not party)
-  }elsif( $currmode eq "normal" && $currstate eq "unsecured" && $targettime =~ /(night)|(sleep)/ && $sa ){
-    $msg = YAAHM_state($name,"secured",$exec)."\n";
-  }
+    $mval =~ s/://;
+    $nval =~ s/://;
+    $tval =~ s/://;
   
-  $hash->{DATA}{"HSM"}{"time"} = $targettime;
+    #-- targetphase always according to real time, not to command time
+    my $targetphase = ( ($lval >= $mval) && ( $nval > $lval ) ) ? "daytime" : "nighttime";
   
-  YAAHM_checkMonthly($hash,'event',$targettime);
+    #-- iterate through table to find next event
+    my $nexttime;
+    my $sval;   
+    my $oval="0000";
+    foreach my $key (sort YAAHM_dsort keys %dailytable){
+      $nexttime = $key;
+      $sval     = $dailytable{$key}[0];
+      next
+        if (!defined($sval));
+      $sval     =~ s/://;
+      last
+        if ( ($lval <= $sval) && ( $lval > $oval ) );
+      $oval     = $sval;
+    }
+    my $ma = defined($attr{$name}{"modeAuto"}) && ($attr{$name}{"modeAuto"} == 1);
+    my $sa = defined($attr{$name}{"stateAuto"}) && ($attr{$name}{"stateAuto"} == 1);
+      
+    #-- automatically leave party mode at morning time or when going to bed
+    # TODO 
+    # TODO Fehler ! Wird nach party um Mitternacht aufgerufen und sichert das Haus, aber in jeder Zeile ein fehler undefined
+    if( $currmode eq "party" && $targettime =~ /(morning)|(sleep)/ && $ma ){
+      Log3 $name, 1,"[YAAHM_time] Leaving party mode because modeAuto=1";
+      $msg  = YAAHM_mode($name,"normal",$exec)."\n";
+      if( $currstate eq "unsecured" && $targettime eq "sleep" && $sa ){
+        Log3 $name, 1,"[YAAHM_time] Securing the house because stateAuto=1";
+        $msg .= YAAHM_state($name,"secured",$exec)."\n"
+      };
   
-  readingsBeginUpdate($hash);
-  readingsBulkUpdate($hash,"prev_housetime",$prevtime);
-  readingsBulkUpdate($hash,"next_housetime",$nexttime);
-  readingsBulkUpdate($hash,"housetime",$targettime);
-  readingsBulkUpdate($hash,"tr_housetime",$yaahm_tt->{$targettime});
-  readingsBulkUpdate($hash,"housephase",$targetphase);
-  readingsBulkUpdate($hash,"tr_housephase",$yaahm_tt->{$targetphase});
+    #-- automatically leave absence mode at wakeup time
+    #   Ist das wirklich clever ? Was passiert, wenn der Wecker nicht ausgestellt wurde
+    }elsif( $currmode eq "absence" && $targettime =~ /(wakeup)/ && $ma ){ 
+      Log3 $name, 1,"[YAAHM_time] Leaving absence mode because modeAuto=1";
+      $msg = YAAHM_mode($name,"normal",$exec)."\n";
+  
+    #-- automatically leave donotdisturb mode at any time event
+    }elsif( $currmode eq "donotdisturb" && $ma ){
+      Log3 $name, 1,"[YAAHM_time] Leaving donotdisturb mode because modeAuto=1"; 
+      $msg = YAAHM_mode($name,"normal",$exec)."\n";
+    
+    #-- automatically secure the house at night time or when going to bed (if not absence, and if not party)
+    }elsif( $currmode eq "normal" && $currstate eq "unsecured" && $targettime =~ /(night)|(sleep)/ && $sa ){
+      Log3 $name, 1,"[YAAHM_time] Securing the house because stateAuto=1";
+      $msg = YAAHM_state($name,"secured",$exec)."\n";
+    }
+  
+    $hash->{DATA}{"HSM"}{"time"} = $targettime;
+  
+    YAAHM_checkMonthly($hash,'event',$targettime);
+  
+    readingsBeginUpdate($hash);
+    readingsBulkUpdate($hash,"prev_housetime",$prevtime);
+    readingsBulkUpdate($hash,"next_housetime",$nexttime);
+    readingsBulkUpdate($hash,"housetime",$targettime);
+    readingsBulkUpdate($hash,"tr_housetime",$yaahm_tt->{$targettime});
+    readingsBulkUpdate($hash,"housephase",$targetphase);
+    readingsBulkUpdate($hash,"tr_housephase",$yaahm_tt->{$targetphase});
+  }
+  #-- before fixing new times
+  #   if manual wake/sleep/timer, the time for the current day needs to be set to empty, 
+  #   but second execution on the same day is blocked if attribute norepeat is set.
   if( $targettime eq "wakeup" ){
     $hash->{DATA}{"WT"}[0]{"next"} = "";
     readingsBulkUpdate($hash,"next_0","");
   }elsif( $targettime eq "sleep" ){ 
     $hash->{DATA}{"WT"}[1]{"next"} = "";
     readingsBulkUpdate($hash,"next_1","");
+  }elsif( $targettime =~ /timer_(\d+)/ ){ 
+    my $i = $1;
+    $hash->{DATA}{"WT"}[$i]{"next"} = "";
+    readingsBulkUpdate($hash,"next_".$i,"");
   }
   readingsEndUpdate($hash,1); 
   YAAHM_setWeeklyTime($hash);
@@ -1203,31 +1227,52 @@ sub YAAHM_time {
   my $ival;
   my $wupn;
   
-  #-- todo here: what should we do, if the timer is NOT enabled and we get up or go to bed anyhow ???
-  if( $targettime eq "wakeup" ){
-    $wupn = $hash->{DATA}{"WT"}[0]{"name"};
-    $ival = (ReadingsVal($name.".wtimer_0.IF","mode","") ne "disabled");
-    $xval = $ival ? $hash->{DATA}{"WT"}[0]{"action"} : "";
-    $msg .= "Simulation ".$xval." from weekly profile ".$wupn;
-    $msg .= " (disabled)"
-      if !$ival;
-  }elsif( $targettime eq "sleep" ){ 
-    $wupn = $hash->{DATA}{"WT"}[1]{"name"};
-    $ival = (ReadingsVal($name.".wtimer_1.IF","mode","") ne "disabled");
-    $xval = $ival ? $hash->{DATA}{"WT"}[1]{"action"} : "";
-    $msg .= "Simulation ".$xval." from weekly profile ".$wupn;
-    $msg .= " (disabled)"
-      if !$ival;
+  #-- after fixing new time
+  #-- TODO here: what should we do, if the timer is NOT enabled and we get up or go to bed anyhow ???
+  if( $targettime =~ /((wakeup)|(sleep)|(timer_(\d+)))/ ){
+    my $timerno="";
+    if( $targettime eq "wakeup" ){
+      $timerno=0;
+    }elsif( $targettime eq "sleep" ){ 
+      $timerno=1;
+    }elsif( $targettime =~ /timer_(\d+)/ ){ 
+      $timerno = $1;
+    }
+    $wupn = $hash->{DATA}{"WT"}[$timerno]{"name"};
+    $ival = (ReadingsVal($name.".wtimer_".$timerno.".IF","mode","") ne "disabled");
+    $xval = $ival ? $hash->{DATA}{"WT"}[$timerno]{"action"} : "";
+    my $norep = AttrVal($name,"norepeat",0);
+    if( $exec==1 ){
+      if( $hash->{DATA}{"WT"}[$timerno]{"done"} && $norep ){
+        $msg = "Not executing action for timer $targettime, already done";
+        Log3 $name,3,"[YAAHM_time] ".$msg;
+        return $msg;
+      }else{
+        Log3 $name,1,"[YAAHM_time] executing action $xval for timer $targettime";
+        fhem($xval);
+        $hash->{DATA}{"WT"}[$timerno]{"done"} = 1;
+        return;
+      }
+    }elsif( $exec==0 ){
+      $msg .= "Simulation ".$xval." from timer ".$targettime;
+      $msg .= " (disabled)"
+        if !$ival;
+      Log3 $name,1,"[YAAHM_time] ".$msg;
+      return $msg;
+    }
+ 
+  #-- any other 
   }else{
     $xval  = $dailytable{$targettime}[2];
-    $msg  .= "Simulation ".$xval;
-  }
-  if( $exec==1 ){
-    Log3 $name,1,"[YAAHM_time] ecxecuting $xval";
-    fhem($xval);
-  }elsif( $exec==0 ){
-    Log3 $name,1,"[YAAHM_time] ".$msg;
-    return $msg;
+    $msg   = "Simulation ".$xval;
+    if( $exec==1 ){
+      Log3 $name,1,"[YAAHM_time] executing $xval";
+      fhem($xval);
+      return
+    }elsif( $exec==0 ){
+      Log3 $name,1,"[YAAHM_time] ".$msg;
+      return $msg;
+    }
   }
 }
 
@@ -1281,6 +1326,7 @@ sub YAAHM_nextWeeklyTime {
   
   #-- all logic in setweeklytime     
   $hash->{DATA}{"WT"}[$i]{"next"} = $time; 
+  $hash->{DATA}{"WT"}[$i]{"done"} = 0;
   YAAHM_setWeeklyTime($hash);                                                   
    
 }
@@ -1654,8 +1700,7 @@ sub YAAHM_startDayTimer($) {
     my $f2 = defined($defaultdailytable{$key}[1]);
     my $f3 = defined($hash->{DATA}{"DT"}{$key}[2]) && $hash->{DATA}{"DT"}{$key}[2] ne "";
 
-    #-- uh oh, double execution of functions !!! Do this in YAAHM_time, NOT in timer
-    #my $xval = "{YAAHM_time('".$name."','".$key."')},".$hash->{DATA}{"DT"}{$key}[2];
+    #-- to prevent double execution of functions do this in YAAHM_time, NOT in timer
     my $xval = "{YAAHM_time('".$name."','".$key."',1)}";
     
     #-- entries in the default table with no entry are single-timers
@@ -1728,19 +1773,19 @@ sub YAAHM_startWeeklyTimer($) {
     my $v4a = ($g4a ne "") ? "(normal)|(".join(')|(',split(',',$g4a)).")" : "(normal)";
     my $v4b = ($g4b ne "") ? "(workday)|(weekend)|(".join(')|(',split(',',$g4b)).")" : "(workday)|(weekend)";
     
-    $res .= "\nand ([" .$name. ":housemode] =~ \"".$v4a."\")";
-    $res .= "\nand ([" .$name. ":todayType] =~ \"".$v4b."\")";
+    $res .= "\nand ((([" .$name. ":housemode] =~ \"".$v4a."\")";
+    $res .= "\nand ([" .$name. ":todayType] =~ \"".$v4b."\")) ";
+    $res .= "\nor ([".$name.":ring_".$i."x] =~ \".*\(man\)\")) ";
     
-    #-- action - explicitly in timer, not in YAAHM_time
-    my $xval = "";
+    #-- to prevent double execution of functions do this in YAAHM_time, NOT in timer
+    my $xval;
     if( $i==0 ){
-      $xval  = "{YAAHM_time('".$name."','wakeup',0)},".$hash->{DATA}{"WT"}[$i]{"action"};
+      $xval  = "{YAAHM_time('".$name."','wakeup',1)}";
     }elsif( $i==1 ){
-      $xval  = "{YAAHM_time('".$name."','sleep',0)},".$hash->{DATA}{"WT"}[$i]{"action"};
+      $xval  = "{YAAHM_time('".$name."','sleep',1)}";
     }else{
-      $xval  = $hash->{DATA}{"WT"}[$i]{"action"};
+      $xval  = "{YAAHM_time('".$name."','timer_".$i."',1)}";
     }
-
     #-- action
     $res .= ")\n(".$xval.")";
     
@@ -1770,7 +1815,7 @@ sub YAAHM_setWeeklyTime($) {
   my $name = $hash->{NAME};
   
   #-- weekly profile times
-  my ($sg0,$sg1,$sg0mod,$sg1mod,$sg0en,$sg1en,$ring_0,$ring_1,$ng);
+  my ($sg0,$sg1,$ring_0x,$ring_1x,$ring_0e,$ring_1e,$ring_0,$ring_1,$ng);
   
   #-- iterate over timers
   for( my $i=0;$i<int( @{$hash->{DATA}{"WT"}} );$i++){
@@ -1780,45 +1825,45 @@ sub YAAHM_setWeeklyTime($) {
     if( ReadingsVal($name.".wtimer_".$i.".IF","mode","") eq "disabled" ){
       $sg0 = "off";
       $sg1 = "off";
-      $sg0en = "disabled (timer)";
-      $sg1en = "disabled (timer)";
+      $ring_0e = "disabled (timer)";
+      $ring_1e = "disabled (timer)";
     #-- if the timer is enabled, we'll use its timing values
     }else{
       $sg0 = $hash->{DATA}{"WT"}[$i]{ $weeklytable[$hash->{DATA}{"DD"}[0]{"weekday"}] } ;
       $sg1 = $hash->{DATA}{"WT"}[$i]{ $weeklytable[$hash->{DATA}{"DD"}[1]{"weekday"}] };
-      $sg0en = "enabled";
-      $sg1en = "enabled";
+      $ring_0e = "enabled";
+      $ring_1e = "enabled";
       #-- next higher priority for "off" is daytype 
       my $wupad = $hash->{DATA}{"WT"}[$i]{"acti_d"}.",workday,weekend"; 
       #-- start with tomorrow
       if( index($wupad, $hash->{DATA}{"DD"}[1]{"daytype"}) == -1 ){
-        $sg1mod = "off (".substr(ReadingsVal($name,"tr_tomorrowType",""),0,3).")";
-        $sg1en  = "disabled (".ReadingsVal($name,"tomorrowType","").")";
+        $ring_1x = "off (".substr(ReadingsVal($name,"tr_tomorrowType",""),0,3).")";
+        $ring_1e  = "disabled (".ReadingsVal($name,"tomorrowType","").")";
       }elsif( ($hash->{DATA}{"DD"}[1]{"vacflag"} == 1 ) && index($wupad,"vacation") == -1 ){
-        $sg1mod = "off (".substr($yaahm_tt->{"vacation"},0,3).")";
-        $sg1en  = "disabled (vacation)";
+        $ring_1x = "off (".substr($yaahm_tt->{"vacation"},0,3).")";
+        $ring_1e  = "disabled (vacation)";
       }else{
-        $sg1mod = $sg1;
+        $ring_1x = $sg1;
       }
       #-- because today we might also have an influence of housemode
       if( index($wupad, $hash->{DATA}{"DD"}[0]{"daytype"}) == -1 ){
-        $sg0mod = "off (".substr(ReadingsVal($name,"tr_todayType",""),0,3).")";
-        $sg0en  = "disabled (".ReadingsVal($name,"todayType","").")";
+        $ring_0x = "off (".substr(ReadingsVal($name,"tr_todayType",""),0,3).")";
+        $ring_0e  = "disabled (".ReadingsVal($name,"todayType","").")";
       }elsif( ($hash->{DATA}{"DD"}[0]{"vacflag"} == 1 ) && index($wupad,"vacation") == -1 ){
-        $sg0mod = "off (".substr($yaahm_tt->{"vacation"},0,3).")";
-        $sg0en  = "disabled (vacation)";
+        $ring_0x = "off (".substr($yaahm_tt->{"vacation"},0,3).")";
+        $ring_0e  = "disabled (vacation)";
       }else{
       #-- next higher priority for "off" (only today !) is housemode 
         my $wupam = $hash->{DATA}{"WT"}[$i]{"acti_m"}.",normal";
         if( index($wupam, ReadingsVal($name,"housemode","")) == -1 ){
-          $sg0mod = "off (".substr(ReadingsVal($name,"tr_housemode",""),0,3).")";
-          $sg0en  = "disabled (".ReadingsVal($name,"housemode","").")";
+          $ring_0x = "off (".substr(ReadingsVal($name,"tr_housemode",""),0,3).")";
+          $ring_0e  = "disabled (".ReadingsVal($name,"housemode","").")";
         }else{
-          $sg0mod = $sg0;
+          $ring_0x = $sg0;
         }
       }   
     }  
-    #Log 1,"====> AFTER INITIAL CHECK TIMER $i sg0=$sg0  sg0mod=$sg0mod  sg1=$sg1  sg1mod=$sg1mod  ng=$ng";
+    
     #-- no "next" time specification
     if( !defined($ng) || $ng eq "" ){
       $ring_0 = $sg0;
@@ -1853,15 +1898,15 @@ sub YAAHM_setWeeklyTime($) {
       }elsif( $nga eq "off" ){
         #-- today's waketime not over => we mean today
         if( $tga ne "off" && ($tga > $lga)){
-          if( $sg0mod !~ /^off/ ){
-            $sg0mod = "off (man)";
+          if( $ring_0x !~ /^off/ ){
+            $ring_0x = "off (man)";
             $ring_0 = "off";
             $ring_1 = $sg1;
           }
         #-- today's waketime over => we mean tomorrow
         }else{
-          if( $sg1mod !~ /^off/ ){
-            $sg1mod = "off (man)";
+          if( $ring_1x !~ /^off/ ){
+            $ring_1x = "off (man)";
             $ring_0 = $sg0;
             $ring_1 = "$sg1 (off)";
           }
@@ -1872,39 +1917,39 @@ sub YAAHM_setWeeklyTime($) {
         if( $nga > $lga ){
           #-- the same as original waketime => restore ! (do we come here at all ?)
           #if( $ng eq $sg0 ){
-          #  $sg0mod = $sg0;
+          #  $ring_0x = $sg0;
           #  $ring_0 = $sg0;
           #  $ng     = "";
           #  $hash->{DATA}{"WT"}[$i]{ "next" } = "";
           #-- new manual waketime tomorrow
           #}else{
-            $sg0mod = "$ng (man)";
-            $ring_0 = $ng;
+            $ring_0x = "$ng (man)";
+            $ring_0  = $ng;
           #}
           $ring_1 = $sg1;
         #-- "next" before current time => we mean tomorrow
         }else{
           #-- the same as original waketime => restore ! (do we come here at all ?)
           #if( $ng eq $sg1 ){
-          #  $sg0mod = $sg1;
+          #  $ring_0x = $sg1;
           #  $ring_1 = $sg1;
           #  $ng     = "";
           #  $hash->{DATA}{"WT"}[$i]{ "next" } = "";
           #}else{
-            $sg1mod = "$ng (man)";
-            $ring_1 = "$sg1 ($ng)";
+            $ring_1x = "$ng (man)";
+            $ring_1  = "$sg1 ($ng)";
           #}
           $ring_0 = $sg0;
         }
       }
     }
-    $hash->{DATA}{"WT"}[$i]{"ring_0"} = $ring_0; 
-    $hash->{DATA}{"WT"}[$i]{"ring_1"} = $ring_1; 
-    $hash->{DATA}{"WT"}[$i]{"ring_0x"} = $sg0mod; 
-    $hash->{DATA}{"WT"}[$i]{"ring_1x"} = $sg1mod;  
-    $hash->{DATA}{"WT"}[$i]{"ring_0e"} = $sg0en;  
-    $hash->{DATA}{"WT"}[$i]{"ring_1e"} = $sg1en;  
-    #Log 1,"====> AFTER FINAL   CHECK TIMER $i sg0=$sg0  sg0mod=$sg0mod  sg1=$sg1  sg1mod=$sg1mod  ng=$ng";
+    $hash->{DATA}{"WT"}[$i]{"ring_0"}  = $ring_0; 
+    $hash->{DATA}{"WT"}[$i]{"ring_1"}  = $ring_1; 
+    $hash->{DATA}{"WT"}[$i]{"ring_0x"} = $ring_0x; 
+    $hash->{DATA}{"WT"}[$i]{"ring_1x"} = $ring_1x;  
+    $hash->{DATA}{"WT"}[$i]{"ring_0e"} = $ring_0e;  
+    $hash->{DATA}{"WT"}[$i]{"ring_1e"} = $ring_1e;  
+    #Log 1,"====> AFTER FINAL   CHECK TIMER $i sg0=$sg0  ring_0x=$ring_0x  sg1=$sg1  ring_1x=$ring_1x  ng=$ng";
     #Log 1,"                                   ".$hash->{DATA}{"WT"}[$i]{"ring_0x"}."               ".$hash->{DATA}{"WT"}[$i]{"ring_1x"}; 
     #-- notation: 
     #  today_i    is today's waketime of timer i   
@@ -1920,12 +1965,12 @@ sub YAAHM_setWeeklyTime($) {
     readingsBeginUpdate($hash);
     readingsBulkUpdate( $hash, "today_".$i,$sg0 );  
     readingsBulkUpdate( $hash, "tomorrow_".$i,$sg1 );
-    readingsBulkUpdate( $hash, "today_".$i."_e",$sg0en );  
-    readingsBulkUpdate( $hash, "tomorrow_".$i."_e",$sg1en );
+    readingsBulkUpdate( $hash, "today_".$i."_e",$ring_0e );  
+    readingsBulkUpdate( $hash, "tomorrow_".$i."_e",$ring_1e );
     readingsBulkUpdate( $hash, "ring_".$i,$ring_0 );    
     readingsBulkUpdate( $hash, "ring_".$i."_1",$ring_1 ); 
-    readingsBulkUpdate( $hash, "ring_".$i."x",$sg0mod );    
-    readingsBulkUpdate( $hash, "ring_".$i."_1x",$sg1mod ); 
+    readingsBulkUpdate( $hash, "ring_".$i."x",$ring_0x );    
+    readingsBulkUpdate( $hash, "ring_".$i."_1x",$ring_1x ); 
     readingsBulkUpdate( $hash, "next_".$i,$ng );    
    
     readingsEndUpdate($hash,1);  
@@ -1937,7 +1982,9 @@ sub YAAHM_setWeeklyTime($) {
 #
 # YAAHM_sayWeeklyTime - say the next weekly time
 #
-# Parameter name = name of device addressed
+# Parameter hash = hash of YAAHM dvice
+#           timer = number of timer 
+#           sp = 1 => output for speech device
 #
 #########################################################################################
 
@@ -1945,13 +1992,13 @@ sub YAAHM_sayWeeklyTime($$$) {
   my ($hash,$timer,$sp) = @_;
   my $name = $hash->{NAME};
   
-  my ($tod,$tom,$ton,$hl,$ml,$tl,$ht,$mt,$tt,$tsay,$chg,$msg,$hw,$mw,$pt,$rea);
+  my ($tod,$tom,$ton,$hl,$ml,$tl,$ht,$mt,$tt,$tsay,$chg,$msg,$hw,$mw,$pt,$rea,$done,$norep);
   
   #--determine which timer (duplicate check when coming from set)
   
   if( $timer >= int( @{$hash->{DATA}{"WT"}}) ){
     $msg = "Error, timer number $timer does not exist, number musst be smaller than ".int( @{$hash->{DATA}{"WT"}});
-    Log3 $name,1,"[YAAHM_sayNextTime] ".$msg;
+    Log3 $name,1,"[YAAHM_sayWeeklyTime] ".$msg;
     return $msg;
   }
   
@@ -1961,6 +2008,8 @@ sub YAAHM_sayWeeklyTime($$$) {
   #-- get timer values from readings, because these include vacation settings and special time
   $tod  = $hash->{DATA}{"WT"}[$timer]{"ring_0x"};
   $tom  = $hash->{DATA}{"WT"}[$timer]{"ring_1x"};
+  $done = $hash->{DATA}{"WT"}[$timer]{"done"};
+  $norep = AttrVal($name,"norepeat",0);
 
   #-- current local time
   ($hl,$ml) = split(':',strftime('%H:%M', localtime(time)));
@@ -1968,11 +2017,13 @@ sub YAAHM_sayWeeklyTime($$$) {
   
   #-- today off AND tomorrow any time or off => compare this time with current time
   if( $tod =~ /^off.*/ ){
+  
     #-- tomorrow any time
     if( $tom =~ /(\d?\d):(\d\d)(:(\d\d))?/ && $tom !~ /.*\(off\)$/ ){
       #Log 1,"===========> |$1|$2|$3|$4";
       ($ht,$mt) = split('[\s:]',$tom);
       $tt=60*$ht+$mt;
+      
       #-- wakeup tomorrow later than now
       if( $tt < $tl ){
         $hw = $1*1;
@@ -1992,13 +2043,15 @@ sub YAAHM_sayWeeklyTime($$$) {
       $pt  = $yaahm_tt->{"undecid"}; 
       $msg .= " ".$yaahm_tt->{"undecid"};
     }
+    
   #-- today nontrivial => compare this time with current time
   }elsif( $tod =~ /(\d?\d):(\d\d)(:(\d\d))?/ ){
     #Log 1,"===========> |$1|$2|$3|$4";
     ($ht,$mt) = split('[\s:]',$tod);
     $tt=60*$ht+$mt;
-    #-- wakeup later today
-    if( $tt >= $tl ){
+    
+    #-- wakeup later today, and ( not yet done - or done, but repeat possible)
+    if( ($tt >= $tl) && (($done == 0) | (($done == 1)&&($norep == 0))) ){
       $hw = $1*1;
       $mw = $2*1;
       $pt = sprintf("%d:%02d",$hw,$mw)." ".lc($yaahm_tt->{"today"});
@@ -2338,6 +2391,11 @@ sub YAAHM_GetDayStatus($) {
     $yaahm_tt = \%yaahm_transtable_EN;
   }
   
+  #-- iterate over timers to reset the "done" flag
+  for( my $i=0;$i<int( @{$hash->{DATA}{"WT"}} );$i++){
+   $hash->{DATA}{"WT"}[$i]{"done"} = 0;
+  }
+  
   my ($ret,$line,$fline,$date);
   my (@lines,@chunks,@tday,@eday,@sday,@tmor,@ttwo);
   my ($todaydesc,$todaytype,$tomdesc,$tomtype,$twodesc,$twotype);
@@ -2650,6 +2708,7 @@ sub YAAHM_GetDayStatus($) {
   YAAHM_setWeeklyTime($hash);
   
   readingsEndUpdate($hash,1);
+  
   return undef;
 
 }
@@ -3924,7 +3983,9 @@ sub YAAHM_Longtable($){
                 <li>On time (event) <i>wakeup</i>, <i>absence</i> mode will be reset to <i>normal</i> mode.</li>
                 <li>On <i>any</i> time (event), <i>donotdisturb</i> mode will be reset to <i>normal</i> mode.</li>
                 </ul>
-                </li>          
+                </li>    
+            <li><<a name="yaahm_norepeat"><code>attr &lt;name&gt; norepeat 0|1</code></a>
+                <br/> if set to 1, repeated executions of wakeup, sleep and other timer events from the weekly programme will be suppressed.</li>      
             <li><a name="yaahm_statedevices"><code>attr &lt;name&gt; stateDevices (&lt;device&gt;:&lt;state-unsecured&gt;:&lt;state-secured&gt;:&lt;state-protected&gt;:&lt;state-guarded&gt;,)*</code></a>
                 <br />comma separated list of devices and their state in each of the house (security) states. Each of the listed devices will be checked in the interval given by the <i>stateInterval</i> attribute
                 for its proper state, and a <i>stateWarning</i> function will be called if it is not in the proper state.</li>

+ 4 - 5
fhem/core/FHEM/96_allowed.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 96_allowed.pm 17179 2018-08-20 15:49:33Z rudolfkoenig $
+# $Id: 96_allowed.pm 17506 2018-10-11 11:01:44Z rudolfkoenig $
 package main;
 
 use strict;
@@ -35,7 +35,7 @@ allowed_Initialize($)
     validFor
   );
   use warnings 'qw';
-  $hash->{AttrList} = join(" ", @attrList);
+  $hash->{AttrList} = join(" ", @attrList)." ".$readingFnAttributes;
 
   $hash->{UndefFn} = "allowed_Undef";
   $hash->{FW_detailFn} = "allowed_fhemwebFn";
@@ -200,13 +200,12 @@ allowed_Authenticate($$$$)
 
     return ($pw eq $param) ? 1 : 2;
 
-  } elsif(!$param || ($param && $param =~ m/^basicAuth:(.*)/)) {
+  } else {
+    $param =~ m/^basicAuth:(.*)/ if($param);
     return allowed_CheckBasicAuth($me, $cl, $1,
                                 AttrVal($aName,"basicAuth",undef), $param);
 
   }
-
-  return 0;
 }
 
 sub

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 696 - 366
fhem/core/FHEM/98_DOIF.pm


+ 2 - 3
fhem/core/FHEM/98_HMinfo.pm

@@ -1,6 +1,6 @@
 ##############################################
 ##############################################
-# $Id: 98_HMinfo.pm 17311 2018-09-09 17:14:49Z martinp876 $
+# $Id: 98_HMinfo.pm 17467 2018-10-06 08:56:13Z martinp876 $
 package main;
 use strict;
 use warnings;
@@ -1080,8 +1080,7 @@ sub HMinfo_getEntities(@) { ###################################################
     my $eHash = $modules{CUL_HM}{defptr}{$id};
     my $eName = $eHash->{NAME};
     next if ( !$eName || $eName !~ m/$re/);
-    my $eIg   = CUL_HM_Get($eHash,$eName,"param","ignore");
-    $eIg = "" if ($eIg eq "undefined");
+    my $eIg   = CUL_HM_getAttr($eName,"ignore","");
     next if (!$doIgn && $eIg);
     next if (!(($doDev && $eHash->{helper}{role}{dev}) ||
                ($doChn && $eHash->{helper}{role}{chn})));

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1878 - 865
fhem/core/FHEM/98_MSwitch.pm


+ 2 - 2
fhem/core/FHEM/98_SVG.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 98_SVG.pm 17306 2018-09-09 13:39:59Z rudolfkoenig $
+# $Id: 98_SVG.pm 17457 2018-10-04 12:33:53Z rudolfkoenig $
 package main;
 
 use strict;
@@ -1429,7 +1429,7 @@ SVG_render($$$$$$$$$$)
   ######################
   # Rectangle
   SVG_pO "<rect x='$x' y='$y' width ='$w' height ='$h' rx='8' ry='8' ".
-        "fill='red' class='border'/>";
+        "fill='none' class='border'/>";
 
   my ($off1,$off2) = ($x+$w/2, 3*$y/4);
   my $title = ($conf{title} ? $conf{title} : " ");

+ 29 - 7
fhem/core/FHEM/98_telnet.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: 98_telnet.pm 17116 2018-08-10 11:52:49Z rudolfkoenig $
+# $Id: 98_telnet.pm 17529 2018-10-14 12:57:06Z rudolfkoenig $
 
 # Note: this is not really a telnet server, but a TCP server with slight telnet
 # features (disable echo on password)
@@ -20,9 +20,22 @@ telnet_Initialize($)
   $hash->{AsyncOutputFn}  = "telnet_Output";
   $hash->{UndefFn} = "telnet_Undef";
   $hash->{AttrFn}  = "telnet_Attr";
-  $hash->{AttrList} = "globalpassword password prompt allowedCommands ".
-                        "allowfrom SSL connectTimeout connectInterval ".
-                        "encoding:utf8,latin1 sslVersion";
+  no warnings 'qw';
+  my @attrList = qw(
+    SSL
+    allowedCommands
+    allowfrom
+    connectInterval
+    connectTimeout
+    encoding:utf8,latin1
+    globalpassword
+    password
+    prompt
+    sslCertPrefix
+    sslVersion
+  );
+  use warnings 'qw';
+  $hash->{AttrList} = join(" ", @attrList);
   $hash->{ActivateInformFn} = "telnet_ActivateInform";
   $hash->{CanAuthenticate} = 2;
 
@@ -229,9 +242,9 @@ telnet_Read($)
     }
 
     $gotCmd = 1;
-    if($cmd) {
-      if($cmd =~ m/\\ *$/) {                     # Multi-line
-        $cmd =~ s/\\ *$//;
+    if($cmd || $hash->{prevlines}) {
+      if($cmd =~ m/\\\s*$/) {                     # Multi-line
+        $cmd =~ s/\\\s*$//;
         $hash->{prevlines} .= $cmd . "\n";
       } else {
         if($hash->{prevlines}) {
@@ -514,6 +527,10 @@ telnet_ActivateInform($)
      <li>sslVersion<br>
         See the global attribute sslVersion.
         </li><br>
+     <li>sslCertPrefix<br>
+        Set the prefix for the SSL certificate, default is certs/server-, see
+        also the SSL attribute.
+        </li><br>
 
   </ul>
 
@@ -656,6 +673,11 @@ telnet_ActivateInform($)
         Siehe das global Attribut sslVersion.
         </li><br>
 
+     <li>sslCertPrefix<br>
+       Setzt das Pr&auml;fix der SSL-Zertifikate, die Voreinstellung ist
+       certs/server-, siehe auch das SSL Attribut.
+      </li><br>
+
   </ul>
 
 </ul>

+ 79 - 6
fhem/core/FHEM/HMCCUConf.pm

@@ -2,9 +2,9 @@
 #
 #  HMCCUConf.pm
 #
-#  $Id: HMCCUConf.pm 17325 2018-09-11 07:54:43Z zap $
+#  $Id: HMCCUConf.pm 17372 2018-09-19 11:39:04Z zap $
 #
-#  Version 4.2.005
+#  Version 4.3
 #
 #  Configuration parameters for HomeMatic devices.
 #
@@ -131,6 +131,21 @@ use vars qw(%HMCCU_SCRIPTS);
 	webCmd           => "control:on:off",
 	widgetOverride   => "control:slider,0,10,100"	
 	},
+	"HmIP-BDT" => {
+	_description     => "Dimmaktor",
+	_channels        => "4",
+	ccureadingfilter => "(ERROR_CODE|ERROR_OVERHEAT|ACTUAL_TEMPERATURE|ACTIVITY_STATE|LEVEL)",
+	ccuscaleval      => "LEVEL:0:1:0:100",
+	controldatapoint => "LEVEL",
+	hmstatevals      => "ACTUAL_TEMPERATURE_STATUS!2:tempOverflow,3:tempUnderflow;ERROR_OVERHEAT!(1|true):overheat",
+	statedatapoint   => "LEVEL",
+	statevals        => "on:100,off:0",
+	stripnumber      => 1,
+	substexcl        => "control",
+	substitute       => "LEVEL!#0-0:off,#1-100:on;ACTIVITY_STATE!0:unknown,1:up,2:down,3:stop;ERROR_OVERHEAT!(0|false):no,(1|true):yes;ACTUAL_TEMPERATURE_STATUS!0:normal,1:unknown,2:overflow,3:underflow",
+	webCmd           => "control:on:off",
+	widgetOverride   => "control:slider,0,10,100"	
+	},
 	"HM-LC-Dim1T-Pl|HM-LC-Dim1T-CV|HM-LC-Dim1T-FM|HM-LC-Dim1T-CV-2|HM-LC-Dim2T-SM|HM-LC-Dim2T-SM-2|HM-LC-Dim1T-DR|HM-LC-Dim1T-FM-LF|HM-LC-Dim1T-FM-2|HM-LC-Dim1T-Pl-3|HM-LC-Dim1TPBU-FM|HM-LC-Dim1TPBU-FM-2" => {
 	_description     => "Funk-Abschnitt-Dimmaktor",
 	_channels        => "1",
@@ -276,6 +291,23 @@ use vars qw(%HMCCU_SCRIPTS);
 	webCmd           => "control:up:stop:down",
 	widgetOverride   => "control:slider,0,10,100"
 	},
+	"HmIP-BROLL" => {
+	_description     => "Rollladenaktor",
+	_channels        => "4",
+	ccureadingfilter => "(ERROR_CODE|ERROR_OVERHEAT|ACTUAL_TEMPERATURE|LEVEL|ACTIVITY_STATE)",
+	ccureadingname   => "LEVEL:+pct",
+	ccuscaleval      => "LEVEL:0:1:0:100",
+	cmdIcon          => "up:fts_shutter_up stop:fts_shutter_manual down:fts_shutter_down",
+	controldatapoint => "LEVEL",
+	hmstatevals      => "ACTUAL_TEMPERATURE_STATUS!2:tempOverflow,3:tempUnderflow;ERROR_OVERHEAT!(1|true):overheat",
+	eventMap         => "/datapoint STOP true:stop/datapoint LEVEL 0:down/datapoint LEVEL 100:up/",
+	statedatapoint   => "LEVEL",
+	stripnumber      => 1,
+	substexcl        => "control|pct",
+	substitute       => "LEVEL!#0-0:closed,#100-100:open;ACTIVITY_STATE!0:unknown,1:up,2:down,3:stop;ERROR_OVERHEAT!(0|false):no,(1|true):yes;ACTUAL_TEMPERATURE_STATUS!0:normal,1:unknown,2:overflow,3:underflow",
+	webCmd           => "control:up:stop:down",
+	widgetOverride   => "control:slider,0,10,100"	
+	},
 	"HM-WDS40-TH-I|HM-WDS10-TH-O|HM-WDS20-TH-O|IS-WDS-TH-OD-S-R3|ASH550I|ASH550" => {
 	_description     => "Temperatur/Luftfeuchte Sensor",
 	_channels        => "1",
@@ -504,7 +536,8 @@ use vars qw(%HMCCU_SCRIPTS);
 	substitute       => "STATE!(true|1):on,(false|0):off",
 	webCmd           => "control",
 	widgetOverride   => "control:uzsuToggle,off,on"
-	},	"HM-LC-Dim1L-Pl|HM-LC-Dim1L-Pl-2|HM-LC-Dim1L-CV|HM-LC-Dim2L-CV|HM-LC-Dim2L-SM|HM-LC-Dim1L-Pl-3|HM-LC-Dim1L-CV-2" => {
+	},
+	"HM-LC-Dim1L-Pl|HM-LC-Dim1L-Pl-2|HM-LC-Dim1L-CV|HM-LC-Dim2L-CV|HM-LC-Dim2L-SM|HM-LC-Dim1L-Pl-3|HM-LC-Dim1L-CV-2" => {
 	_description     => "Funk-Anschnitt-Dimmaktor",
 	ccureadingfilter => "(^LEVEL\$|DIRECTION)",
 	ccuscaleval      => "LEVEL:0:1:0:100",
@@ -549,6 +582,20 @@ use vars qw(%HMCCU_SCRIPTS);
 	webCmd           => "control:on:off",
 	widgetOverride   => "control:slider,0,10,100"	
 	},
+	"HmIP-BDT" => {
+	_description     => "Dimmaktor",
+	ccureadingfilter => "(ERROR_CODE|ERROR_OVERHEAT|ACTUAL_TEMPERATURE|ACTIVITY_STATE|LEVEL)",
+	ccuscaleval      => "LEVEL:0:1:0:100",
+	controldatapoint => "4.LEVEL",
+	hmstatevals      => "ACTUAL_TEMPERATURE_STATUS!2:tempOverflow,3:tempUnderflow;ERROR_OVERHEAT!(1|true):overheat",
+	statedatapoint   => "4.LEVEL",
+	statevals        => "on:100,off:0",
+	stripnumber      => 1,
+	substexcl        => "control",
+	substitute       => "LEVEL!#0-0:off,#1-100:on;ACTIVITY_STATE!0:unknown,1:up,2:down,3:stop;ERROR_OVERHEAT!(0|false):no,(1|true):yes;ACTUAL_TEMPERATURE_STATUS!0:normal,1:unknown,2:overflow,3:underflow",
+	webCmd           => "control:on:off",
+	widgetOverride   => "control:slider,0,10,100"	
+	},
 	"HM-PB-2-FM" => {
 	_description     => "Funk-Wandtaster 2-fach",
 	ccureadingfilter => "PRESS",
@@ -639,6 +686,22 @@ use vars qw(%HMCCU_SCRIPTS);
 	webCmd           => "control:up:stop:down",
 	widgetOverride   => "control:slider,0,10,100"
 	},
+	"HmIP-BROLL" => {
+	_description     => "Rollladenaktor",
+	ccureadingfilter => "(ERROR_CODE|ERROR_OVERHEAT|ACTUAL_TEMPERATURE|LEVEL|ACTIVITY_STATE|SELF_CALIBRATION_RESULT)",
+	ccureadingname   => "LEVEL:+pct",
+	ccuscaleval      => "LEVEL:0:1:0:100",
+	cmdIcon          => "up:fts_shutter_up stop:fts_shutter_manual down:fts_shutter_down",
+	controldatapoint => "4.LEVEL",
+	hmstatevals      => "ACTUAL_TEMPERATURE_STATUS!2:tempOverflow,3:tempUnderflow;ERROR_OVERHEAT!(1|true):overheat",
+	eventMap         => "/datapoint 4.STOP true:stop/datapoint 4.LEVEL 0:down/datapoint 4.LEVEL 100:up/datapoint 3.SELF_CALIBRATION 0:stopCalibration/datapoint 3.SELF_CALIBRATION 1:startCalibration/",
+	statedatapoint   => "4.LEVEL",
+	stripnumber      => 1,
+	substexcl        => "control|pct",
+	substitute       => "LEVEL!#0-0:closed,#100-100:open;ACTIVITY_STATE!0:unknown,1:up,2:down,3:stop;ERROR_OVERHEAT!(0|false):no,(1|true):yes;ACTUAL_TEMPERATURE_STATUS!0:normal,1:unknown,2:overflow,3:underflow;SELF_CALIBRATION_RESULT!(0|false):failed,(1|true):ok",
+	webCmd           => "control:up:stop:down",
+	widgetOverride   => "control:slider,0,10,100"	
+	},
 	"HM-TC-IT-WM-W-EU" => {
 	_description     => "Wandthermostat",
 	ccureadingfilter => "(^HUMIDITY|^TEMPERATURE|^SET_TEMPERATURE|^WINDOW_OPEN)",
@@ -683,7 +746,7 @@ use vars qw(%HMCCU_SCRIPTS);
 	webCmd           => "control:Boost:Auto:Manual:Holiday:on:off",
 	widgetOverride   => "control:slider,4.5,0.5,30.5,1"
 	},
-	"HmIP-WTH|HmIP-WTH-2" => {
+	"HmIP-WTH|HmIP-WTH-2|HmIP-BWTH" => {
 	_description     => "Wandthermostat HM-IP",
 	controldatapoint => "1.SET_POINT_TEMPERATURE",
 	eventMap         => "/datapoint 1.BOOST_MODE true:Boost/datapoint 1.CONTROL_MODE 0:Auto/datapoint 1.CONTROL_MODE 1:Manual/datapoint 1.CONTROL_MODE 2:Holiday/datapoint 1.SET_POINT_TEMPERATURE 4.5:off/datapoint 1.SET_POINT_TEMPERATURE 30.5:on/",
@@ -730,16 +793,18 @@ use vars qw(%HMCCU_SCRIPTS);
 	},
 	"HM-CC-VG-1" => {
 	_description     => "Heizungsgruppe",
-	ccureadingfilter => "(^SET_TEMPERATURE|^TEMPERATURE|^HUMIDITY|^VALVE|^CONTROL|^WINDOW_OPEN)",
+	ccucalculate     => "dewpoint:DEWPOINT:1.ACTUAL_TEMPERATURE,1.ACTUAL_HUMIDITY",
+	ccureadingfilter => "1.(^SET_TEMPERATURE|^ACTUAL|^VALVE|^CONTROL);2.^WINDOW_OPEN;4.^VALVE",
 	cmdIcon          => "Auto:sani_heating_automatic Manu:sani_heating_manual Boost:sani_heating_boost on:general_an off:general_aus",
 	controldatapoint => "1.SET_TEMPERATURE",
 	eventMap         => "/datapoint 1.MANU_MODE 20.0:Manu/datapoint 1.AUTO_MODE 1:Auto/datapoint 1.BOOST_MODE 1:Boost/datapoint 1.MANU_MODE 4.5:off/datapoint 1.MANU_MODE 30.5:on/",
 	statedatapoint   => "1.SET_TEMPERATURE",
+	stateFormat      => "T: 1.ACTUAL_TEMPERATURE° H: 1.ACTUAL_HUMIDITY% D: 1.SET_TEMPERATURE° P: DEWPOINT° V: 4.VALVE_STATE% 1.CONTROL_MODE",
 	stripnumber      => 1,
 	substexcl        => "control",
 	substitute       => "CONTROL_MODE!0:AUTO,1:MANU,2:PARTY,3:BOOST;WINDOW_OPEN_REPORTING!(true|1):open,(false|0):closed;SET_TEMPERATURE!#0-4.5:off,#30.5-40:on",
 	webCmd           => "control:Auto:Manu:Boost:on:off",
-	widgetOverride   => "control:slider,3.5,0.5,30.5,1"
+	widgetOverride   => "control:slider,4.5,0.5,30.5,1"
 	},
 	"HM-Sec-MD|HM-Sec-MDIR|HM-Sec-MDIR-2|HM-Sec-MDIR-3" => {
 	_description     => "Bewegungsmelder",
@@ -755,6 +820,14 @@ use vars qw(%HMCCU_SCRIPTS);
 	statedatapoint   => "1.MOTION",
 	substitute       => "MOTION!(0|false):no,(1|true):yes"
 	},
+	"HmIP-SMI55" => {
+	_description     => "Bewegungsmelder",
+	ccureadingfilter => "(ILLUMINATION|MOTION|PRESS)",
+	"event-on-update-reading" => ".*",
+	eventMap         => "/datapoint 3.MOTION_DETECTION_ACTIVE 1:detection-on/datapoint 3.MOTION_DETECTION_ACTIVE 0:detection-off/datapoint 3.RESET_MOTION 1:reset/",
+	statedatapoint   => "3.MOTION",
+	substitute       => "PRESS_LONG,PRESS_SHORT!(1|true):pressed,(0|false):released;MOTION,MOTION_DETECTION_ACTIVE!(0|false):no,(1|true):yes;ILLUMINATION_STATUS!0:normal,1:unknown,2:overflow"
+	},
 	"HmIP-SPI" => {
 	_description     => "Anwesenheitssensor",
 	ccureadingfilter => "(ILLUMINATION|PRESENCE)",

+ 5 - 3
fhem/core/FHEM/TcpServerUtils.pm

@@ -1,5 +1,5 @@
 ##############################################
-# $Id: TcpServerUtils.pm 17124 2018-08-11 06:54:58Z rudolfkoenig $
+# $Id: TcpServerUtils.pm 17529 2018-10-14 12:57:06Z rudolfkoenig $
 
 package main;
 use strict;
@@ -111,16 +111,18 @@ TcpServer_Accept($$)
     # Certs directory must be in the modpath, i.e. at the same level as the
     # FHEM directory
     my $mp = AttrVal("global", "modpath", ".");
+    my $certPrefix = AttrVal($name, "sslCertPrefix", "certs/server-");
     my $ret;
     eval {
       $ret = IO::Socket::SSL->start_SSL($clientinfo[0], {
         SSL_server    => 1, 
-        SSL_key_file  => "$mp/certs/server-key.pem",
-        SSL_cert_file => "$mp/certs/server-cert.pem",
+        SSL_key_file  => "$mp/${certPrefix}key.pem",
+        SSL_cert_file => "$mp/${certPrefix}cert.pem",
         SSL_version => $sslVersion,
         SSL_cipher_list => 'HIGH:!RC4:!eNULL:!aNULL',
         Timeout       => 4,
         });
+      $! = EINVAL if(!$clientinfo[0]->blocking() && $!==EWOULDBLOCK);
     };
     my $err = $!;
     if( !$ret

+ 244 - 0
fhem/core/FHEM/TradfriUtils.pm

@@ -0,0 +1,244 @@
+# @author Clemens Bergmann
+# @author Peter Kappelt  (small changes)
+# @version 1.16.dev-cf.9
+
+package main;
+use strict;
+use warnings;
+
+use SetExtensions;
+
+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 Tradfri_devStateIcon($){
+	my ($name) = @_;
+	my $pct = ReadingsVal($name,"pct","100");
+	my $s = $dim_values{int($pct/7)};
+	$s="on" if( $pct eq "100" );
+	$s="off" if( $pct eq "0" );
+	return ".*:$s:toggle";
+}
+
+#get the on state of the group depending on the dimm value
+sub Tradfri_stateString($){
+	my ($value) = @_;
+	if($value <= 0){
+		return 'off';
+	}elsif($value >= 99){
+		return 'on';
+	}else{
+		return "dim$value%";
+	}
+}
+
+sub Tradfri_setBrightness($$){
+	my ($hash, $dimpercent) = @_;
+	#readingsSingleUpdate($hash, "pct", $dimpercent, 1);
+	$hash->{STATE} = Tradfri_stateString($dimpercent);
+	my $address = $hash->{address};
+
+	#type, can be device or group
+	my $type;
+	if($hash->{TYPE} eq 'TradfriGroup'){
+		$type = 1;
+	}elsif($hash->{TYPE} eq 'TradfriDevice'){
+		$type = 0;
+	}else{
+		Log(0, "[Tradfri] Trying to call Tradfri-Functions with a non-Tradfri device (with $hash->{TYPE}");
+	}
+
+	if( $dimpercent == 0 ){
+		return IOWrite($hash, $type, 'set', $address, "onoff::0");
+	}else{
+		my $dimvalue = int($dimpercent * 2.54 + 0.5);
+		#readingsSingleUpdate($hash, "dimvalue", $dimvalue, 1);
+		return IOWrite($hash, $type, 'set', $address, "dimvalue::${dimvalue}");
+	}
+}
+
+sub Tradfri_Set($@) {
+	my ($hash, $name, $opt, @param) = @_;
+
+	return "\"set $hash->{TYPE}\" needs at least one argument" unless(defined($opt));
+
+	my $value = join("", @param);
+
+	#parameters that can be set
+	my $cmdList = "on off pct:colorpicker,BRI,0,1,100";
+
+	#dynamic option: dimvalue depends on attribute usePercentDimming
+	my $dimvalueMax = 254;
+	$dimvalueMax = 100 if (AttrVal($hash->{name}, 'usePercentDimming', 0) == 1);
+	$cmdList .= " dimvalue:slider,0,1,$dimvalueMax";
+
+	#parameters that are only applicable to devices
+	if($hash->{TYPE} eq 'TradfriDevice'){
+		$cmdList .= " color:warm,cold,standard" ;
+	}
+
+	#parameters that are only applicable to groups
+	if($hash->{TYPE} eq 'TradfriGroup'){
+		#dynamic option: moods
+		my $moodsList = join(",", map { "$_" } keys %{$hash->{helper}{moods}});
+		$cmdList .= " mood:$moodsList" 
+	}
+
+	if ($opt eq "toggle") {
+		$opt = (ReadingsVal($hash->{name}, 'onoff', 0) eq 'off') ? "on" : "off";
+	}elsif($opt eq "on"){
+		my $dimpercent = ReadingsVal($hash->{name}, 'dimvalue', 254);
+		$dimpercent = int($dimpercent / 2.54 + 0.5) if(AttrVal($hash->{name}, 'usePercentDimming', 0) == 0);
+		#@todo ist das Setzen der Helligkeit hier angebracht, welche Auswirkungen hat das? Fehlerfälle betrachten!, evtl. nur einschalten?
+		Tradfri_setBrightness($hash,$dimpercent);
+	}elsif($opt eq "off"){
+		Tradfri_setBrightness($hash,0);
+	}elsif($opt eq "dimvalue"){
+		return '"set TradfriDevice dimvalue" requires a brightness-value between 0 and 254!'  if ! @param;
+
+		my $dimpercent = int($value);
+		$dimpercent = int($value / 2.54 + 0.5) if(AttrVal($hash->{name}, 'usePercentDimming', 0) == 0);
+
+		Tradfri_setBrightness($hash,$dimpercent);
+	}elsif($opt eq "pct"){
+		return '"set TradfriDevice dimvalue" requires a brightness-value between 0 and 100!'  if ! @param;
+
+		Tradfri_setBrightness($hash,int($value));
+	}elsif(($hash->{TYPE} eq 'TradfriGroup') and ($opt eq "mood")){
+		return '"set TradfriGroup mood" requires a mood ID or a mood name. You can list the available moods for this group by running "get moods"'  if ! @param;
+
+		if(!($value =~ /^[0-9]+$/)){
+			#user wrote a string -> a mood name
+			if(exists($hash->{helper}{moods}->{"$value"})){
+				$value = $hash->{helper}{moods}->{"$value"}->{moodid};
+			}else{
+				return "Unknown mood!";
+			}
+		}
+
+		return IOWrite($hash, 1, 'set', $hash->{address}, "mood::${value}");
+	}elsif(($hash->{TYPE} eq 'TradfriDevice') and ($opt eq "color")){
+		return '"set TradfriDevice color" requires RGB value in format "RRGGBB" or "warm", "cold", "standard"!'  if ! @param;
+
+		my $rgb;
+
+		if($value eq "warm"){
+			$rgb = 'EFD275';
+		}elsif($value eq "cold"){
+			$rgb = 'F5FAF6';
+		}elsif($value eq "standard"){
+			$rgb = 'F1E0B5';
+		}else{
+			$rgb = $value;
+		}
+
+		return IOWrite($hash, 0, 'set', $hash->{address}, "color::${rgb}");
+	}else{
+		return SetExtensions($hash, $cmdList, $name, $opt, @param);
+	}
+
+	return undef;
+}
+
+sub Tradfri_Undef($$) {
+	my ($hash, $arg) = @_; 
+	# nothing to do
+	return undef;
+}
+
+sub Tradfri_Attr(@) {
+	my ($cmd,$name,$attr_name,$attr_value) = @_;
+	if($cmd eq "set") {
+		if($attr_name eq ""){
+
+		}
+	}
+	return undef;
+}
+
+sub Tradfri_Define($$) {
+	my ($hash, $def) = @_;
+	my @param = split('[ \t]+', $def);
+
+	if(int(@param) < 3) {
+		return "too few parameters: define <name> $hash->{TYPE} <Address>";
+	}
+
+	$hash->{name}  = $param[0];
+	$hash->{address} = $param[2];
+
+	#$attr{$hash->{name}}{webCmd} = 'pct:toggle:on:off';
+	#$attr{$hash->{name}}{devStateIcon} = '{(Tradfri_devStateIcon($name),"toggle")}' if( !defined( $attr{$hash->{name}}{devStateIcon} ) );
+
+	AssignIoPort($hash);
+
+	#start observing the coap resource, so the module will be informed about status updates
+	#@todo stop observing, when deleting module, or stopping FHEM
+
+	#reverse search, for Parse
+	$modules{$hash->{TYPE}}{defptr}{$hash->{address}} = $hash;
+	if($hash->{TYPE} eq 'TradfriDevice'){
+		IOWrite($hash, 0, 'subscribe', $hash->{address});
+	}elsif($hash->{TYPE} eq 'TradfriGroup'){
+		IOWrite($hash, 1, 'subscribe', $hash->{address});
+		#update the moodlist
+		IOWrite($hash, 1, 'moodlist', $hash->{address});
+	}
+
+	return undef;
+}
+
+sub Tradfri_Get($@) {
+	my ($hash, @param) = @_;
+
+	return "\"get $hash->{TYPE}\" needs at least one argument" if (int(@param) < 2);
+
+	my $name = shift @param;
+	my $opt = shift @param;
+
+	my $cmdList = "";
+
+	#Only applicable for groups: moods list
+	$cmdList .= " moods" if($hash->{TYPE} eq 'TradfriGroup');	
+
+	#$cmdList .= " groupInfo" if($hash->{TYPE} eq 'TradfriGroup');	
+	#$cmdList .= " deviceInfo" if($hash->{TYPE} eq 'TradfriDevice');	
+
+	if($hash->{TYPE} eq 'TradfriGroup' and $opt eq 'moods'){
+		IOWrite($hash, 1, 'moodlist', $hash->{address});
+		return undef
+		#return '';
+#	}elsif($hash->{TYPE} eq 'TradfriGroup' and $opt eq 'groupInfo'){
+#	}elsif($hash->{TYPE} eq 'TradfriDevice' and $opt eq 'deviceInfo'){
+#		my $jsonText = IOWrite($hash, 'get', PATH_DEVICE_ROOT . "/" . $hash->{address}, '');
+#
+#               if(!defined($jsonText)){
+#                     return "Error while fetching device info!";
+#               }
+#               
+#               #parse the JSON data
+#               my $jsonData = eval{ JSON->new->utf8->decode($jsonText) };
+#               if($@){
+#                     return $jsonText; #the string was probably not valid JSON
+#               }
+#
+#               return Dumper($jsonData);
+	}else{
+		return "Unknown argument $opt, choose one of $cmdList";
+	}
+}

+ 1 - 0
fhem/core/FHEM/controls.txt

@@ -3,3 +3,4 @@ https://raw.githubusercontent.com/klein0r/fhem-style-haus-automatisierung/versio
 https://raw.githubusercontent.com/knowthelist/fhem-tablet-ui/master/controls_fhemtabletui.txt
 https://raw.githubusercontent.com/jowiemann/DBPlan-for-Fhem/master/controls_dbplan.txt
 https://raw.githubusercontent.com/uniqueck/fhem-abfall/master/controls_fhemabfall.txt
+https://raw.githubusercontent.com/peterkappelt/Tradfri-FHEM/dev-cf/src/controls_tradfri.txt

+ 76 - 72
fhem/core/FHEM/controls_fhem.txt

@@ -1,4 +1,4 @@
-REV 17341
+REV 17549
 DIR unused
 MOV www/pgm2/fhemweb_multiple.js unused
 MOV www/pgm2/fhemweb_noArg.js unused
@@ -21,11 +21,11 @@ MOV www/pgm2/ios7smallscreensvg_style.css unused
 MOV www/pgm2/iossmallscreensvg_defs.svg unused
 MOV www/pgm2/iossmallscreensvg_style.css unused
 MOV FHEM/firmware/LaCrosseGateway.bin unused
-UPD 2018-09-14_07:45:02 258187 ./CHANGED
-UPD 2018-09-05_07:45:04 37945 ./MAINTAINER.txt
+UPD 2018-10-17_07:45:02 260955 ./CHANGED
+UPD 2018-10-13_07:45:03 38075 ./MAINTAINER.txt
 UPD 2018-09-09_07:45:03 39688 ./configDB.pm
-UPD 2018-08-27_07:45:04 20439 ./fhem.cfg.demo
-UPD 2018-09-13_07:45:13 150752 ./fhem.pl
+UPD 2018-09-17_07:45:03 19987 ./fhem.cfg.demo
+UPD 2018-10-15_07:45:09 150899 ./fhem.pl
 UPD 2018-03-11_07:45:03 22076 FHEM/00_CM11.pm
 UPD 2018-06-26_07:45:03 57519 FHEM/00_CUL.pm
 UPD 2018-03-04_07:45:03 76990 FHEM/00_DFPlayerMini.pm
@@ -38,8 +38,8 @@ UPD 2018-03-11_07:45:03 3685 FHEM/00_HXB.pm
 UPD 2017-02-04_07:45:02 51672 FHEM/00_KM271.pm
 UPD 2016-08-19_14:49:02 3718 FHEM/00_LIRC.pm
 UPD 2016-04-26_07:45:11 33379 FHEM/00_MAXLAN.pm
-UPD 2018-08-19_07:45:02 31133 FHEM/00_MQTT.pm
-UPD 2018-09-07_07:45:03 16036 FHEM/00_MQTT2_SERVER.pm
+UPD 2018-09-18_07:45:02 31171 FHEM/00_MQTT.pm
+UPD 2018-10-16_07:45:03 16549 FHEM/00_MQTT2_SERVER.pm
 UPD 2018-09-07_07:45:03 20140 FHEM/00_MYSENSORS.pm
 UPD 2017-09-07_07:45:02 25519 FHEM/00_NetzerI2C.pm
 UPD 2018-06-12_07:45:06 35015 FHEM/00_Neuron.pm
@@ -54,7 +54,7 @@ UPD 2018-09-04_07:45:03 143235 FHEM/00_THZ.pm
 UPD 2017-12-16_07:45:02 35823 FHEM/00_TUL.pm
 UPD 2018-04-05_07:45:03 21475 FHEM/00_ZWCUL.pm
 UPD 2018-08-21_07:45:03 45122 FHEM/00_ZWDongle.pm
-UPD 2018-09-10_07:45:02 153295 FHEM/01_FHEMWEB.pm
+UPD 2018-10-15_07:45:02 153853 FHEM/01_FHEMWEB.pm
 UPD 2016-09-07_07:45:12 15690 FHEM/02_FRAMEBUFFER.pm
 UPD 2017-04-23_07:45:02 42910 FHEM/02_FTUISRV.pm
 UPD 2018-06-16_07:45:03 7921 FHEM/02_HTTPSRV.pm
@@ -62,25 +62,25 @@ UPD 2018-06-04_07:45:02 52079 FHEM/02_RSS.pm
 UPD 2018-03-11_07:45:03 4860 FHEM/09_BS.pm
 UPD 2018-08-04_07:45:03 21814 FHEM/09_CUL_FHTTK.pm
 UPD 2018-03-11_07:45:03 6824 FHEM/09_USF1000.pm
-UPD 2018-08-16_07:45:03 576598 FHEM/10_CUL_HM.pm
+UPD 2018-10-15_07:45:02 576934 FHEM/10_CUL_HM.pm
 UPD 2015-07-28_14:05:31 15566 FHEM/10_CUL_IR.pm
 UPD 2017-04-24_07:45:02 16860 FHEM/10_DUOFERNSTICK.pm
 UPD 2018-03-12_07:45:04 44764 FHEM/10_EIB.pm
-UPD 2018-03-22_07:45:04 29430 FHEM/10_EQ3BT.pm
-UPD 2018-09-04_07:45:03 895983 FHEM/10_EnOcean.pm
+UPD 2018-10-08_07:45:02 30638 FHEM/10_EQ3BT.pm
+UPD 2018-09-16_07:45:03 899293 FHEM/10_EnOcean.pm
 UPD 2018-05-30_07:45:03 19903 FHEM/10_FBDECT.pm
 UPD 2018-01-21_07:45:02 70159 FHEM/10_FRM.pm
 UPD 2017-08-14_07:45:02 32138 FHEM/10_FS20.pm
 UPD 2018-03-11_07:45:03 10293 FHEM/10_HXBDevice.pm
-UPD 2017-08-07_07:45:02 73643 FHEM/10_IT.pm
+UPD 2018-10-16_07:45:03 73727 FHEM/10_IT.pm
 UPD 2016-02-05_07:45:11 4427 FHEM/10_Itach_IR.pm
-UPD 2018-08-20_07:45:03 82847 FHEM/10_KNX.pm
+UPD 2018-10-08_07:45:02 83806 FHEM/10_KNX.pm
 UPD 2016-12-22_07:45:02 36655 FHEM/10_KOPP_FC.pm
 UPD 2018-06-11_07:45:06 61938 FHEM/10_MAX.pm
-UPD 2018-09-07_07:45:03 14094 FHEM/10_MQTT2_DEVICE.pm
-UPD 2018-04-30_07:45:05 10978 FHEM/10_MQTT_BRIDGE.pm
-UPD 2018-04-30_07:45:05 13504 FHEM/10_MQTT_DEVICE.pm
-UPD 2018-09-10_07:45:02 97369 FHEM/10_MQTT_GENERIC_BRIDGE.pm
+UPD 2018-10-09_07:45:03 14516 FHEM/10_MQTT2_DEVICE.pm
+UPD 2018-09-18_07:45:02 11022 FHEM/10_MQTT_BRIDGE.pm
+UPD 2018-09-18_07:45:02 13548 FHEM/10_MQTT_DEVICE.pm
+UPD 2018-10-15_07:45:02 138462 FHEM/10_MQTT_GENERIC_BRIDGE.pm
 UPD 2018-06-23_07:45:04 30428 FHEM/10_MYSENSORS_DEVICE.pm
 UPD 2018-06-12_07:45:06 19100 FHEM/10_NeuronPin.pm
 UPD 2018-03-28_07:45:02 33933 FHEM/10_OWServer.pm
@@ -97,8 +97,8 @@ UPD 2018-04-29_07:45:04 19133 FHEM/11_OWX_FRM.pm
 UPD 2018-04-29_07:45:04 27106 FHEM/11_OWX_SER.pm
 UPD 2018-04-29_07:45:04 25762 FHEM/11_OWX_TCP.pm
 UPD 2018-05-30_07:45:03 10823 FHEM/12_HMS.pm
-UPD 2018-09-14_07:45:02 13159 FHEM/12_HProtocolGateway.pm
-UPD 2018-09-14_07:45:02 4602 FHEM/12_HProtocolTank.pm
+UPD 2018-10-12_07:45:02 13338 FHEM/12_HProtocolGateway.pm
+UPD 2018-10-12_07:45:02 4601 FHEM/12_HProtocolTank.pm
 UPD 2017-12-17_13:17:44 14463 FHEM/13_KS300.pm
 UPD 2016-10-27_07:45:10 29342 FHEM/14_CUL_MAX.pm
 UPD 2015-12-05_07:45:11 10226 FHEM/14_CUL_REDIRECT.pm
@@ -118,7 +118,7 @@ UPD 2017-01-07_07:45:02 8875 FHEM/16_STACKABLE_CC.pm
 UPD 2017-04-23_07:45:02 17724 FHEM/17_EGPM2LAN.pm
 UPD 2015-07-28_14:05:31 9862 FHEM/17_SIS_PMS.pm
 UPD 2017-11-28_07:45:03 3107 FHEM/18_CUL_HOERMANN.pm
-UPD 2015-07-28_14:05:31 4358 FHEM/19_Revolt.pm
+UPD 2018-10-02_07:45:02 7819 FHEM/19_Revolt.pm
 UPD 2017-01-07_07:45:02 7975 FHEM/19_VBUSIF.pm
 UPD 2018-01-20_07:45:02 8749 FHEM/20_FRM_AD.pm
 UPD 2015-07-28_14:05:31 3725 FHEM/20_FRM_I2C.pm
@@ -134,7 +134,7 @@ UPD 2017-04-30_07:45:02 32225 FHEM/20_GUEST.pm
 UPD 2017-12-31_07:45:03 13856 FHEM/20_N4HBUS.pm
 UPD 2015-07-28_14:05:31 10373 FHEM/20_OWFS.pm
 UPD 2017-04-30_07:45:02 32802 FHEM/20_ROOMMATE.pm
-UPD 2018-03-11_07:45:03 19719 FHEM/20_X10.pm
+UPD 2018-09-29_07:45:03 19905 FHEM/20_X10.pm
 UPD 2018-03-14_07:45:03 27768 FHEM/21_HEOSGroup.pm
 UPD 2018-03-14_07:45:03 55770 FHEM/21_HEOSMaster.pm
 UPD 2018-03-14_07:45:03 48189 FHEM/21_HEOSPlayer.pm
@@ -157,11 +157,12 @@ UPD 2017-11-01_07:45:02 37938 FHEM/23_KOSTALPIKO.pm
 UPD 2018-04-13_07:45:03 136840 FHEM/23_LUXTRONIK2.pm
 UPD 2015-07-28_14:05:31 5069 FHEM/23_WEBIO.pm
 UPD 2015-07-28_14:05:31 5626 FHEM/23_WEBIO_12DIGITAL.pm
+UPD 2018-10-16_07:45:03 4949 FHEM/24_Iluminize.pm
 UPD 2015-07-28_14:05:31 10651 FHEM/24_NetIO230B.pm
-UPD 2018-07-03_07:45:02 19968 FHEM/24_TPLinkHS110.pm
+UPD 2018-10-07_07:45:02 20649 FHEM/24_TPLinkHS110.pm
 UPD 2018-05-09_07:45:02 355062 FHEM/26_KM273.pm
 UPD 2018-06-12_07:45:06 61702 FHEM/26_tahoma.pm
-UPD 2018-05-27_13:13:25 91572 FHEM/30_DUOFERN.pm
+UPD 2018-10-08_07:45:02 91527 FHEM/30_DUOFERN.pm
 UPD 2015-07-28_14:05:31 4923 FHEM/30_ENECSYSGW.pm
 UPD 2018-03-03_07:47:08 61628 FHEM/30_HUEBridge.pm
 UPD 2017-03-12_07:45:02 32401 FHEM/30_LIGHTIFY.pm
@@ -185,13 +186,13 @@ UPD 2016-01-31_07:45:12 18305 FHEM/32_TechemWZ.pm
 UPD 2018-01-17_07:45:02 209352 FHEM/32_WifiLight.pm
 UPD 2018-03-02_07:47:09 15860 FHEM/32_mailcheck.pm
 UPD 2016-08-23_07:45:12 5646 FHEM/32_speedtest.pm
-UPD 2018-09-14_07:45:02 127260 FHEM/32_withings.pm
+UPD 2018-09-30_07:45:02 128356 FHEM/32_withings.pm
 UPD 2016-09-30_07:45:11 15878 FHEM/32_yowsup.pm
 UPD 2017-12-15_07:45:02 4576 FHEM/33_readingsChange.pm
 UPD 2018-03-02_07:47:09 89206 FHEM/33_readingsGroup.pm
 UPD 2018-03-02_07:47:09 23233 FHEM/33_readingsHistory.pm
 UPD 2018-03-02_07:47:09 12101 FHEM/33_readingsProxy.pm
-UPD 2018-05-27_07:45:07 135399 FHEM/34_ESPEasy.pm
+UPD 2018-09-23_07:45:03 166195 FHEM/34_ESPEasy.pm
 UPD 2015-08-06_07:45:08 13198 FHEM/34_NUT.pm
 UPD 2016-08-23_07:45:12 47048 FHEM/34_SWAP.pm
 UPD 2016-08-23_07:45:12 16074 FHEM/34_panStamp.pm
@@ -209,6 +210,7 @@ UPD 2018-08-13_07:45:02 32737 FHEM/36_LaCrosseGateway.pm
 UPD 2017-06-16_07:45:03 6518 FHEM/36_Level.pm
 UPD 2016-08-23_07:45:12 9997 FHEM/36_PCA301.pm
 UPD 2017-11-03_07:45:02 17579 FHEM/36_PrecipitationSensor.pm
+UPD 2018-10-17_07:45:02 37721 FHEM/36_Shelly.pm
 UPD 2018-08-20_07:45:03 71356 FHEM/36_Vallox.pm
 UPD 2018-07-06_07:45:03 22937 FHEM/36_WMBUS.pm
 UPD 2018-02-10_07:45:02 10932 FHEM/37_NotifyAndroidTV.pm
@@ -222,7 +224,7 @@ UPD 2017-07-01_07:45:02 152744 FHEM/37_plex.pm
 UPD 2017-12-10_07:45:03 31453 FHEM/38_Broadlink.pm
 UPD 2017-05-31_07:45:02 28640 FHEM/38_CO20.pm
 UPD 2016-02-09_07:45:13 15879 FHEM/38_JawboneUp.pm
-UPD 2018-09-08_07:45:03 236301 FHEM/38_netatmo.pm
+UPD 2018-10-08_07:45:02 237505 FHEM/38_netatmo.pm
 UPD 2018-03-18_07:45:02 80208 FHEM/39_Talk2Fhem.pm
 UPD 2018-03-02_07:47:09 24286 FHEM/39_alexa.pm
 UPD 2017-04-21_07:45:02 2759 FHEM/39_siri.pm
@@ -261,14 +263,14 @@ UPD 2015-07-28_14:05:31 26441 FHEM/49_IPCAM.pm
 UPD 2018-09-03_07:45:03 457841 FHEM/49_SSCam.pm
 UPD 2018-07-04_07:45:03 10638 FHEM/49_SSCamSTRM.pm
 UPD 2018-03-12_07:45:04 46280 FHEM/49_TBot_List.pm
-UPD 2018-08-02_07:45:03 62338 FHEM/50_HP1000.pm
+UPD 2018-10-15_07:45:02 62521 FHEM/50_HP1000.pm
 UPD 2018-02-07_07:45:03 28816 FHEM/50_MOBILEALERTSGW.pm
 UPD 2018-03-12_07:45:04 153999 FHEM/50_TelegramBot.pm
 UPD 2015-07-28_14:05:31 18601 FHEM/50_WS300.pm
 UPD 2016-10-06_07:45:12 19924 FHEM/51_I2C_BH1750.pm
 UPD 2016-08-23_07:45:12 25149 FHEM/51_I2C_BMP180.pm
 UPD 2017-12-30_07:45:03 61748 FHEM/51_I2C_TSL2561.pm
-UPD 2018-08-30_07:45:03 60231 FHEM/51_MOBILEALERTS.pm
+UPD 2018-10-09_07:45:03 60238 FHEM/51_MOBILEALERTS.pm
 UPD 2016-08-23_07:45:12 18068 FHEM/51_Netzer.pm
 UPD 2018-05-05_07:45:03 39938 FHEM/51_RPI_GPIO.pm
 UPD 2016-12-05_07:45:14 23142 FHEM/52_I2C_BME280.pm
@@ -289,14 +291,14 @@ UPD 2017-03-05_07:45:02 18357 FHEM/52_I2C_PCF8574.pm
 UPD 2016-08-23_07:45:12 12461 FHEM/52_I2C_SHT21.pm
 UPD 2016-07-11_07:45:14 12508 FHEM/52_I2C_SHT3x.pm
 UPD 2018-02-21_07:45:47 28917 FHEM/53_GHoma.pm
-UPD 2018-05-16_07:45:03 71780 FHEM/55_DWD_OpenData.pm
+UPD 2018-09-29_07:45:03 77974 FHEM/55_DWD_OpenData.pm
 UPD 2018-05-29_07:45:03 65815 FHEM/55_InfoPanel.pm
 UPD 2016-08-24_07:45:10 20855 FHEM/55_PIFACE.pm
 UPD 2015-07-28_14:05:31 17091 FHEM/56_POKEYS.pm
 UPD 2018-07-27_07:45:05 27136 FHEM/57_CALVIEW.pm
-UPD 2018-05-16_07:45:03 145820 FHEM/57_Calendar.pm
-UPD 2018-08-19_07:45:02 33285 FHEM/59_HCS.pm
-UPD 2017-10-18_07:45:02 26092 FHEM/59_LuftdatenInfo.pm
+UPD 2018-10-15_07:45:02 148508 FHEM/57_Calendar.pm
+UPD 2018-10-01_07:45:02 33614 FHEM/59_HCS.pm
+UPD 2018-10-17_07:45:02 26685 FHEM/59_LuftdatenInfo.pm
 UPD 2018-03-19_07:45:04 25560 FHEM/59_OPENWEATHER.pm
 UPD 2018-08-03_07:45:03 38886 FHEM/59_PROPLANTA.pm
 UPD 2018-01-27_07:45:02 43120 FHEM/59_Twilight.pm
@@ -338,19 +340,21 @@ UPD 2016-08-21_07:45:14 20340 FHEM/70_VolumeLink.pm
 UPD 2018-03-14_07:45:03 44782 FHEM/70_WINCONNECT.pm
 UPD 2017-03-01_07:45:02 22279 FHEM/70_WS3600.pm
 UPD 2016-12-30_07:45:02 55308 FHEM/70_XBMC.pm
+UPD 2018-10-17_07:45:02 21994 FHEM/70_ZoneMinder.pm
 UPD 2017-05-29_07:45:02 63897 FHEM/71_ONKYO_AVR_ZONE.pm
 UPD 2017-04-20_07:45:02 96053 FHEM/71_PHILIPS_AUDIO.pm
 UPD 2017-06-27_07:45:02 24467 FHEM/71_PIONEERAVRZONE.pm
-UPD 2018-08-24_07:45:02 165073 FHEM/71_YAMAHA_AVR.pm
+UPD 2018-10-17_07:45:02 166011 FHEM/71_YAMAHA_AVR.pm
 UPD 2017-09-27_07:45:02 51988 FHEM/71_YAMAHA_BD.pm
 UPD 2018-01-29_07:45:02 123938 FHEM/71_YAMAHA_NP.pm
+UPD 2018-10-08_07:45:02 17604 FHEM/71_ZM_Monitor.pm
 UPD 2018-03-19_07:45:04 80986 FHEM/72_FB_CALLLIST.pm
 UPD 2018-05-09_07:45:02 113204 FHEM/72_FB_CALLMONITOR.pm
-UPD 2018-08-02_07:45:03 220936 FHEM/72_FRITZBOX.pm
-UPD 2018-09-10_07:45:02 147167 FHEM/72_XiaomiDevice.pm
+UPD 2018-10-01_07:45:02 221693 FHEM/72_FRITZBOX.pm
+UPD 2018-10-06_07:45:02 151068 FHEM/72_XiaomiDevice.pm
 UPD 2018-06-03_07:45:03 47236 FHEM/73_AMADCommBridge.pm
 UPD 2018-04-14_07:45:02 89324 FHEM/73_ElectricityCalculator.pm
-UPD 2018-08-24_07:45:02 35702 FHEM/73_GardenaSmartBridge.pm
+UPD 2018-10-16_07:45:03 35233 FHEM/73_GardenaSmartBridge.pm
 UPD 2018-04-14_07:45:02 81612 FHEM/73_GasCalculator.pm
 UPD 2018-01-09_07:45:03 87372 FHEM/73_MPD.pm
 UPD 2018-07-12_07:45:02 30806 FHEM/73_NUKIBridge.pm
@@ -359,7 +363,7 @@ UPD 2017-11-06_07:45:02 81399 FHEM/73_UpsPico.pm
 UPD 2018-04-14_07:45:02 84632 FHEM/73_WaterCalculator.pm
 UPD 2017-05-09_07:45:02 118318 FHEM/73_km200.pm
 UPD 2018-06-20_07:45:04 67279 FHEM/74_AMADDevice.pm
-UPD 2018-08-24_07:45:02 42014 FHEM/74_GardenaSmartDevice.pm
+UPD 2018-10-16_07:45:03 42084 FHEM/74_GardenaSmartDevice.pm
 UPD 2018-03-22_07:45:04 41340 FHEM/74_HOMBOT.pm
 UPD 2018-06-20_07:45:04 30459 FHEM/74_HusqvarnaAutomower.pm
 UPD 2018-07-12_07:45:02 22747 FHEM/74_NUKIDevice.pm
@@ -389,9 +393,9 @@ UPD 2018-07-17_07:45:04 6465 FHEM/84_IOhomecontrolDevice.pm
 UPD 2018-08-20_07:45:03 42219 FHEM/86_Robonect.pm
 UPD 2015-07-28_14:05:31 17571 FHEM/87_WS2000.pm
 UPD 2015-07-28_14:05:31 4345 FHEM/88_ALL4000T.pm
-UPD 2018-09-14_07:45:02 251563 FHEM/88_HMCCU.pm
-UPD 2018-09-12_07:45:02 42477 FHEM/88_HMCCUCHN.pm
-UPD 2018-09-12_07:45:02 37134 FHEM/88_HMCCUDEV.pm
+UPD 2018-10-04_07:45:03 257236 FHEM/88_HMCCU.pm
+UPD 2018-09-17_07:45:03 42487 FHEM/88_HMCCUCHN.pm
+UPD 2018-09-20_07:45:03 38344 FHEM/88_HMCCUDEV.pm
 UPD 2018-02-14_07:45:02 90153 FHEM/88_HMCCURPC.pm
 UPD 2018-09-12_07:45:02 84095 FHEM/88_HMCCURPCPROC.pm
 UPD 2015-07-28_14:05:31 5836 FHEM/88_IPWE.pm
@@ -406,32 +410,32 @@ UPD 2018-08-29_07:45:04 21359 FHEM/89_FULLY.pm
 UPD 2016-01-05_07:45:12 44169 FHEM/89_HEATRONIC.pm
 UPD 2016-07-11_07:45:14 62016 FHEM/89_VCONTROL.pm
 UPD 2016-12-07_07:45:10 15485 FHEM/90_SIGNALduino_un.pm
-UPD 2018-09-03_07:45:03 25584 FHEM/90_at.pm
+UPD 2018-10-12_07:45:02 25588 FHEM/90_at.pm
 UPD 2017-08-14_07:45:02 6980 FHEM/91_eventTypes.pm
 UPD 2018-08-30_07:45:03 27694 FHEM/91_notify.pm
 UPD 2018-05-11_07:45:02 9863 FHEM/91_sequence.pm
 UPD 2018-07-10_07:45:14 16502 FHEM/91_watchdog.pm
 UPD 2018-08-21_07:45:03 54919 FHEM/92_FileLog.pm
 UPD 2017-08-14_07:45:02 11589 FHEM/92_SingleFileLog.pm
-UPD 2018-09-10_07:45:02 344726 FHEM/93_DbLog.pm
-UPD 2018-09-13_07:45:06 654319 FHEM/93_DbRep.pm
-UPD 2018-08-13_07:45:02 17527 FHEM/93_FHEM2FHEM.pm
-UPD 2018-08-25_07:45:03 110254 FHEM/93_Log2Syslog.pm
+UPD 2018-10-17_07:45:02 346819 FHEM/93_DbLog.pm
+UPD 2018-10-13_07:45:03 668506 FHEM/93_DbRep.pm
+UPD 2018-09-18_07:45:02 18106 FHEM/93_FHEM2FHEM.pm
+UPD 2018-10-17_07:45:02 135339 FHEM/93_Log2Syslog.pm
 UPD 2018-02-09_07:45:02 66936 FHEM/93_PWMR.pm
 UPD 2017-09-13_07:45:02 7907 FHEM/93_RFHEM.pm
 UPD 2018-02-06_07:45:02 47244 FHEM/94_PWM.pm
-UPD 2018-03-11_07:45:03 66217 FHEM/95_Alarm.pm
-UPD 2018-08-20_07:45:03 63118 FHEM/95_Astro.pm
+UPD 2018-09-15_07:45:02 68569 FHEM/95_Alarm.pm
+UPD 2018-10-13_07:45:03 63139 FHEM/95_Astro.pm
 UPD 2018-04-28_07:45:03 91427 FHEM/95_Babble.pm
 UPD 2018-06-30_07:45:03 57430 FHEM/95_Dashboard.pm
 UPD 2017-03-20_07:45:03 62531 FHEM/95_FLOORPLAN.pm
 UPD 2018-02-19_07:45:04 64284 FHEM/95_PostMe.pm
-UPD 2018-05-12_07:45:03 173097 FHEM/95_YAAHM.pm
+UPD 2018-10-08_07:45:02 175580 FHEM/95_YAAHM.pm
 UPD 2018-03-28_07:45:02 19730 FHEM/95_holiday.pm
 UPD 2016-02-05_07:45:11 21301 FHEM/95_remotecontrol.pm
 UPD 2018-08-01_07:45:02 79187 FHEM/96_SIP.pm
 UPD 2017-01-24_07:45:02 31432 FHEM/96_Snapcast.pm
-UPD 2018-08-21_07:45:03 20872 FHEM/96_allowed.pm
+UPD 2018-10-12_07:45:02 20875 FHEM/96_allowed.pm
 UPD 2018-08-17_07:45:03 65927 FHEM/97_PiXtendV2.pm
 UPD 2016-12-21_07:45:02 12157 FHEM/97_TrashCal.pm
 UPD 2018-09-05_07:45:04 69276 FHEM/98_ArduCounter.pm
@@ -440,7 +444,7 @@ UPD 2016-08-19_14:49:02 4984 FHEM/98_CULflash.pm
 UPD 2017-05-10_07:45:02 47704 FHEM/98_ComfoAir.pm
 UPD 2017-09-20_07:45:02 9013 FHEM/98_CustomReadings.pm
 UPD 2018-01-10_07:45:02 60933 FHEM/98_DLNARenderer.pm
-UPD 2018-09-07_07:45:03 254982 FHEM/98_DOIF.pm
+UPD 2018-10-15_07:45:02 266672 FHEM/98_DOIF.pm
 UPD 2018-09-09_07:45:03 111923 FHEM/98_DOIFtools.pm
 UPD 2017-11-06_07:45:02 40228 FHEM/98_Dooya.pm
 UPD 2016-10-29_07:45:14 28678 FHEM/98_EDIPLUG.pm
@@ -449,7 +453,7 @@ UPD 2017-12-14_07:45:02 40432 FHEM/98_GAEBUS.pm
 UPD 2017-04-27_07:45:02 24956 FHEM/98_GEOFANCY.pm
 UPD 2018-04-12_07:45:02 30163 FHEM/98_GOOGLECAST.pm
 UPD 2017-01-21_07:45:02 11576 FHEM/98_GoogleAuth.pm
-UPD 2018-09-10_07:45:02 174754 FHEM/98_HMinfo.pm
+UPD 2018-10-07_07:45:02 174706 FHEM/98_HMinfo.pm
 UPD 2018-04-14_07:45:02 29909 FHEM/98_HMtemplate.pm
 UPD 2018-07-07_07:45:03 192504 FHEM/98_HTTPMOD.pm
 UPD 2018-01-27_07:45:02 26432 FHEM/98_Heating_Control.pm
@@ -457,7 +461,7 @@ UPD 2016-04-26_07:45:11 33223 FHEM/98_HourCounter.pm
 UPD 2017-12-02_07:45:02 66644 FHEM/98_Hyperion.pm
 UPD 2017-01-04_07:45:02 21573 FHEM/98_IF.pm
 UPD 2018-08-31_07:45:03 4884 FHEM/98_JsonList2.pm
-UPD 2018-09-11_07:45:03 231296 FHEM/98_MSwitch.pm
+UPD 2018-10-13_07:45:03 257263 FHEM/98_MSwitch.pm
 UPD 2016-03-12_07:45:14 49251 FHEM/98_MaxScanner.pm
 UPD 2017-09-05_07:45:02 34104 FHEM/98_MediaList.pm
 UPD 2018-01-14_07:45:02 121875 FHEM/98_Modbus.pm
@@ -469,7 +473,7 @@ UPD 2016-02-05_07:45:11 34730 FHEM/98_PID20.pm
 UPD 2017-01-24_07:45:02 27747 FHEM/98_QRCode.pm
 UPD 2018-06-07_07:45:10 22527 FHEM/98_RandomTimer.pm
 UPD 2016-10-05_07:45:11 20567 FHEM/98_STOCKQUOTES.pm
-UPD 2018-09-10_07:45:02 95594 FHEM/98_SVG.pm
+UPD 2018-10-05_07:45:02 95595 FHEM/98_SVG.pm
 UPD 2018-03-24_07:45:03 97508 FHEM/98_Siro.pm
 UPD 2017-05-04_07:45:02 52568 FHEM/98_THRESHOLD.pm
 UPD 2018-03-19_07:45:04 49956 FHEM/98_TRAFFIC.pm
@@ -511,7 +515,7 @@ UPD 2017-01-16_07:45:02 44459 FHEM/98_rssFeed.pm
 UPD 2018-03-19_07:45:04 60215 FHEM/98_statistics.pm
 UPD 2018-06-15_07:45:04 31985 FHEM/98_structure.pm
 UPD 2018-08-26_07:45:02 12075 FHEM/98_systemd_watchdog.pm
-UPD 2018-08-11_07:45:03 19982 FHEM/98_telnet.pm
+UPD 2018-10-15_07:45:02 20417 FHEM/98_telnet.pm
 UPD 2017-03-13_07:45:02 7818 FHEM/98_template.pm
 UPD 2018-04-05_07:45:03 22580 FHEM/98_update.pm
 UPD 2017-07-14_07:45:02 1921 FHEM/98_uptime.pm
@@ -527,7 +531,7 @@ UPD 2015-07-28_14:05:29 562 FHEM/FhemUtils/release.pm
 UPD 2015-07-28_14:05:29 5739 FHEM/FhemUtils/update-20130127-001
 UPD 2018-05-06_07:45:03 3548 FHEM/FritzBoxUtils.pm
 UPD 2015-07-28_14:05:31 1582 FHEM/GPUtils.pm
-UPD 2018-09-12_07:45:02 50986 FHEM/HMCCUConf.pm
+UPD 2018-09-20_07:45:03 55373 FHEM/HMCCUConf.pm
 UPD 2018-09-10_07:45:02 201333 FHEM/HMConfig.pm
 UPD 2017-05-22_07:45:02 42698 FHEM/HOMESTATEtk.pm
 UPD 2018-07-28_07:45:02 29104 FHEM/HttpUtils.pm
@@ -544,7 +548,7 @@ UPD 2015-07-28_14:05:31 11758 FHEM/SHC_parser.pm
 UPD 2018-08-08_07:45:02 5504 FHEM/SetExtensions.pm
 UPD 2017-05-21_07:45:02 7416 FHEM/SubProcess.pm
 UPD 2017-11-16_07:45:02 3372 FHEM/TR064Utils.pm
-UPD 2018-08-12_07:45:03 7720 FHEM/TcpServerUtils.pm
+UPD 2018-10-15_07:45:02 7861 FHEM/TcpServerUtils.pm
 UPD 2016-02-22_07:45:12 27613 FHEM/TimeSeries.pm
 UPD 2017-05-29_07:45:02 49037 FHEM/UConv.pm
 UPD 2017-04-30_07:45:02 137531 FHEM/Unit.pm
@@ -654,9 +658,9 @@ UPD 2017-05-15_07:45:02 9633 FHEM/lib/UPnP/sonos_playbar_round.png
 UPD 2015-07-28_14:05:30 5469 FHEM/lib/UPnP/sonos_playlist.jpg
 UPD 2017-05-15_07:45:02 2779 FHEM/lib/UPnP/sonos_tunein_quadratic.jpg
 UPD 2017-05-15_07:45:02 1979 FHEM/lib/UPnP/sonos_tunein_round.png
-UPD 2018-05-24_07:45:02 11476 FHEM/lib/fhem_zwave_deviceconfig.xml.gz
+UPD 2018-10-09_07:45:03 14596 FHEM/lib/fhem_zwave_deviceconfig.xml.gz
 UPD 2018-08-09_07:45:03 249740 FHEM/lib/openzwave_deviceconfig.xml.gz
-UPD 2018-08-09_07:45:03 102661 FHEM/lib/openzwave_manufacturer_specific.xml
+UPD 2018-10-14_07:45:02 102783 FHEM/lib/openzwave_manufacturer_specific.xml
 UPD 2018-07-23_07:45:03 26070 FHEM/lib/zwave_alliancelinks.csv.gz
 UPD 2017-01-11_07:45:02 22473 FHEM/lib/zwave_pepperlinks.csv.gz
 UPD 2018-03-10_07:45:02 16040 FHEM/msgSchema.pm
@@ -669,7 +673,7 @@ UPD 2018-08-27_07:45:04 390 demolog/LightScenes.save
 UPD 2015-07-28_14:05:35 28600 demolog/cellar.log
 UPD 2015-07-28_14:05:35 71762 demolog/dewpoint.log
 UPD 2015-07-28_14:05:35 2944 demolog/eventTypes.txt
-UPD 2018-08-27_07:45:04 19305 demolog/fhem.save
+UPD 2018-09-17_07:45:03 19247 demolog/fhem.save
 UPD 2015-07-28_14:05:35 71800 demolog/garden.log
 UPD 2015-07-28_14:05:35 2225 demolog/layout
 UPD 2015-07-28_14:05:35 219 demolog/pictures-copyright.txt
@@ -679,12 +683,12 @@ UPD 2016-07-21_07:45:27 24829 docs/HOWTO_DE.html
 UPD 2015-07-28_14:05:35 46151 docs/IMG_0483.jpg
 UPD 2017-06-30_07:45:02 37394 docs/Landis-Gyr-E350-meter.jpg
 UPD 2015-07-28_14:05:35 14548 docs/ccc.jpg
-UPD 2018-09-14_07:45:05 3554273 docs/commandref.html
-UPD 2018-09-13_07:45:11 2186974 docs/commandref_DE.html
-UPD 2018-07-16_07:45:03 76259 docs/commandref_frame.html
-UPD 2018-07-16_07:45:03 85715 docs/commandref_frame_DE.html
-UPD 2018-09-14_07:45:08 196347 docs/commandref_modular.html
-UPD 2018-09-14_07:45:09 207061 docs/commandref_modular_DE.html
+UPD 2018-10-17_07:45:05 3589756 docs/commandref.html
+UPD 2018-10-17_07:45:07 2207200 docs/commandref_DE.html
+UPD 2018-10-08_07:45:02 76228 docs/commandref_frame.html
+UPD 2018-10-08_07:45:02 85677 docs/commandref_frame_DE.html
+UPD 2018-10-17_07:45:08 198933 docs/commandref_modular.html
+UPD 2018-10-17_07:45:09 209614 docs/commandref_modular_DE.html
 UPD 2015-07-28_14:05:35 4778 docs/cul_rfr.jpg
 UPD 2015-07-28_14:05:35 19949 docs/faq.html
 UPD 2015-07-28_14:05:35 938 docs/fhemdoc.js
@@ -2008,14 +2012,14 @@ UPD 2015-07-28_14:05:34 590 www/jscolor/hue_background.svg
 UPD 2015-07-28_14:05:34 2865 www/jscolor/hv.png
 UPD 2015-07-28_14:05:34 29813 www/jscolor/jscolor.js
 UPD 2015-07-28_14:05:34 331 www/pgm2/RSS.js
-UPD 2018-01-30_07:45:02 12018 www/pgm2/alarm.js
+UPD 2018-09-24_07:45:03 12353 www/pgm2/alarm.js
 UPD 2018-05-11_07:45:02 13108 www/pgm2/babble.js
 UPD 2017-02-18_07:45:02 6202 www/pgm2/brightstyle.css
 UPD 2018-03-08_07:45:03 9598 www/pgm2/console.js
 UPD 2017-02-18_07:45:02 1064 www/pgm2/darkCommon.css
 UPD 2015-07-28_14:05:34 4334 www/pgm2/darkfloorplanstyle.css
-UPD 2017-02-24_07:45:02 4813 www/pgm2/darksmallscreenstyle.css
-UPD 2017-02-18_07:45:02 5640 www/pgm2/darkstyle.css
+UPD 2018-10-08_07:45:02 5419 www/pgm2/darksmallscreenstyle.css
+UPD 2018-10-08_07:45:02 5742 www/pgm2/darkstyle.css
 UPD 2015-11-22_17:53:09 2502 www/pgm2/darksvg_defs.svg
 UPD 2015-07-28_14:05:34 2808 www/pgm2/darksvg_style.css
 UPD 2015-07-28_14:05:34 30 www/pgm2/darktouchpadstyle.css
@@ -2026,10 +2030,10 @@ UPD 2015-07-28_14:05:34 14171 www/pgm2/dashboard_style.css
 UPD 2018-03-26_07:45:02 5908 www/pgm2/defaultCommon.css
 UPD 2018-09-11_07:45:03 3636 www/pgm2/defaultfloorplanstyle.css
 UPD 2017-12-04_07:45:02 2286 www/pgm2/doif.js
-UPD 2018-09-11_07:45:03 28592 www/pgm2/f18.js
-UPD 2018-09-10_07:45:02 8768 www/pgm2/f18style.css
+UPD 2018-10-08_07:45:02 28719 www/pgm2/f18.js
+UPD 2018-10-08_07:45:02 9836 www/pgm2/f18style.css
 UPD 2018-05-27_13:13:25 7515 www/pgm2/fhemdoc_modular.js
-UPD 2018-09-10_07:45:02 57884 www/pgm2/fhemweb.js
+UPD 2018-10-08_07:45:02 58283 www/pgm2/fhemweb.js
 UPD 2017-10-04_07:45:02 13775 www/pgm2/fhemweb_colorpicker.js
 UPD 2018-02-01_07:45:02 4063 www/pgm2/fhemweb_fbcalllist.js
 UPD 2018-01-14_07:45:02 8950 www/pgm2/fhemweb_iconButtons.js
@@ -2086,7 +2090,7 @@ UPD 2015-07-28_14:05:34 240427 www/pgm2/jquery-ui.min.js
 UPD 2015-07-28_14:05:34 11892 www/pgm2/jquery.knob.min.js
 UPD 2015-07-28_14:05:34 95931 www/pgm2/jquery.min.js
 UPD 2017-03-08_07:45:03 4545 www/pgm2/jquery.vticker.min.js
-UPD 2017-02-24_07:45:02 2525 www/pgm2/smallscreenstyle.css
+UPD 2018-10-08_07:45:02 3106 www/pgm2/smallscreenstyle.css
 UPD 2017-01-17_07:45:03 18745 www/pgm2/sorttable.js
 UPD 2015-07-28_14:05:34 649 www/pgm2/style.css
 UPD 2018-07-28_07:45:02 12566 www/pgm2/svg.js

+ 44 - 43
fhem/core/FHEM/controls_fhemtabletui.txt

@@ -12,10 +12,10 @@ UPD 2018-04-03_01:31:38 7326 www/tablet/css/fhem-darkgreen-ui.css
 UPD 2018-04-02_23:22:10 2658 www/tablet/css/fhem-green-ui.css
 UPD 2018-04-02_23:24:25 7123 www/tablet/css/fhem-mobil-ui.css
 UPD 2017-02-05_13:50:40 6916 www/tablet/css/fhem-tablet-ui-wdtimer.css
-UPD 2018-09-04_23:47:30 63600 www/tablet/css/fhem-tablet-ui.css
-UPD 2018-09-05_00:00:53 47686 www/tablet/css/fhem-tablet-ui.min.css
+UPD 2018-10-06_20:55:55 64557 www/tablet/css/fhem-tablet-ui.css
+UPD 2018-10-03_01:06:51 48206 www/tablet/css/fhem-tablet-ui.min.css
 UPD 2017-02-11_14:27:26 2049 www/tablet/css/ftui-bright-mint-ui.css
-UPD 2018-03-17_21:48:45 5632 www/tablet/css/ftui_chart.css
+UPD 2018-09-21_20:59:49 6600 www/tablet/css/ftui_chart.css
 UPD 2017-10-03_08:03:02 1132 www/tablet/css/ftui_checklist.css
 UPD 2017-02-11_14:27:26 1023 www/tablet/css/ftui_colorwheel.css
 UPD 2017-09-22_14:45:40 650 www/tablet/css/ftui_controlbutton.css
@@ -28,29 +28,29 @@ UPD 2018-08-15_22:45:20 945 www/tablet/css/ftui_slider.css
 UPD 2018-04-05_17:36:29 1637 www/tablet/css/ftui_spinner.css
 UPD 2017-06-13_20:19:36 6936 www/tablet/css/ftui_weekprofile.css
 UPD 2017-11-13_21:51:49 4650 www/tablet/demo_ftui.html
-UPD 2018-06-20_19:48:12 115052 www/tablet/fonts/fa-brands-400.eot
-UPD 2018-06-20_19:48:12 622420 www/tablet/fonts/fa-brands-400.svg
-UPD 2018-06-20_19:48:12 114816 www/tablet/fonts/fa-brands-400.ttf
-UPD 2018-06-20_19:48:12 73920 www/tablet/fonts/fa-brands-400.woff
-UPD 2018-06-20_19:48:12 63376 www/tablet/fonts/fa-brands-400.woff2
-UPD 2018-06-20_19:48:12 40744 www/tablet/fonts/fa-regular-400.eot
-UPD 2018-06-20_19:48:12 141551 www/tablet/fonts/fa-regular-400.svg
-UPD 2018-06-20_19:48:12 40516 www/tablet/fonts/fa-regular-400.ttf
-UPD 2018-06-20_19:48:12 18212 www/tablet/fonts/fa-regular-400.woff
-UPD 2018-06-20_19:48:12 14952 www/tablet/fonts/fa-regular-400.woff2
-UPD 2018-06-20_19:48:12 160768 www/tablet/fonts/fa-solid-900.eot
-UPD 2018-06-20_19:48:12 592839 www/tablet/fonts/fa-solid-900.svg
-UPD 2018-06-20_19:48:12 160548 www/tablet/fonts/fa-solid-900.ttf
-UPD 2018-06-20_19:48:12 76632 www/tablet/fonts/fa-solid-900.woff
-UPD 2018-06-20_19:48:12 59572 www/tablet/fonts/fa-solid-900.woff2
+UPD 2018-08-28_17:26:38 118560 www/tablet/fonts/fa-brands-400.eot
+UPD 2018-08-28_17:26:38 648265 www/tablet/fonts/fa-brands-400.svg
+UPD 2018-08-28_17:26:38 118324 www/tablet/fonts/fa-brands-400.ttf
+UPD 2018-08-28_17:26:38 76344 www/tablet/fonts/fa-brands-400.woff
+UPD 2018-08-28_17:26:38 65316 www/tablet/fonts/fa-brands-400.woff2
+UPD 2018-08-28_17:26:38 40576 www/tablet/fonts/fa-regular-400.eot
+UPD 2018-08-28_17:26:38 142267 www/tablet/fonts/fa-regular-400.svg
+UPD 2018-08-28_17:26:38 40348 www/tablet/fonts/fa-regular-400.ttf
+UPD 2018-08-28_17:26:38 18168 www/tablet/fonts/fa-regular-400.woff
+UPD 2018-08-28_17:26:38 14868 www/tablet/fonts/fa-regular-400.woff2
+UPD 2018-08-28_17:26:38 180720 www/tablet/fonts/fa-solid-900.eot
+UPD 2018-08-28_17:26:38 680510 www/tablet/fonts/fa-solid-900.svg
+UPD 2018-08-28_17:26:38 180500 www/tablet/fonts/fa-solid-900.ttf
+UPD 2018-08-28_17:26:38 86876 www/tablet/fonts/fa-solid-900.woff
+UPD 2018-08-28_17:26:38 67400 www/tablet/fonts/fa-solid-900.woff2
 UPD 2017-10-03_19:36:50 226696 www/tablet/fonts/fhemSVG.eot
 UPD 2017-11-09_19:58:14 979105 www/tablet/fonts/fhemSVG.svg
 UPD 2017-10-03_19:36:50 226532 www/tablet/fonts/fhemSVG.ttf
 UPD 2017-10-03_19:36:50 226608 www/tablet/fonts/fhemSVG.woff
 UPD 2018-08-31_23:24:32 1076 www/tablet/fonts/fontawesome-License.txt
-UPD 2017-02-11_14:27:26 3165 www/tablet/fonts/FTUI-icons.svg
-UPD 2017-02-11_14:27:26 1688 www/tablet/fonts/FTUI-icons.ttf
-UPD 2017-02-11_14:27:26 1764 www/tablet/fonts/FTUI-icons.woff
+UPD 2018-10-03_11:25:34 6811 www/tablet/fonts/FTUI-icons.svg
+UPD 2018-10-03_11:25:34 2904 www/tablet/fonts/FTUI-icons.ttf
+UPD 2018-10-03_11:25:34 2980 www/tablet/fonts/FTUI-icons.woff
 UPD 2017-02-11_14:27:26 143258 www/tablet/fonts/MaterialIcons-Regular.eot
 UPD 2017-02-11_14:27:26 28416 www/tablet/fonts/MaterialIcons-Regular.ijmap
 UPD 2017-02-11_14:27:26 281658 www/tablet/fonts/MaterialIcons-Regular.svg
@@ -78,38 +78,38 @@ UPD 2016-03-24_07:51:54 56468 www/tablet/fonts/weathericons-regular-webfont.woff
 UPD 2016-03-24_07:51:54 44720 www/tablet/fonts/weathericons-regular-webfont.woff2
 UPD 2018-03-10_09:12:29 6893 www/tablet/ftui_check.html
 UPD 2018-09-04_21:25:28 2646 www/tablet/ftui_snippet_tester.html
-UPD 2017-11-14_00:10:10 584382 www/tablet/icons_table.html
+UPD 2018-09-27_07:35:03 705403 www/tablet/icons_table.html
 UPD 2017-02-11_14:27:26 17624 www/tablet/index-example.html
 UPD 2018-04-02_23:29:18 3213 www/tablet/index_empty.html
 UPD 2018-03-19_08:56:00 9958 www/tablet/index_layout.html
 UPD 2018-03-04_10:25:23 4343 www/tablet/index_state.html
-UPD 2018-08-31_07:37:49 96663 www/tablet/js/fhem-tablet-ui.js
-UPD 2018-08-31_23:34:46 49530 www/tablet/js/fhem-tablet-ui.min.js
+UPD 2018-09-28_03:09:40 97762 www/tablet/js/fhem-tablet-ui.js
+UPD 2018-09-28_03:09:44 50163 www/tablet/js/fhem-tablet-ui.min.js
 UPD 2017-11-19_14:35:00 21234 www/tablet/js/highcharts-meteogram.js
 UPD 2017-02-11_14:27:26 6503 www/tablet/js/widget_agenda.js
 UPD 2018-01-21_14:49:42 1769 www/tablet/js/widget_button.js
 UPD 2018-08-21_23:58:12 18187 www/tablet/js/widget_calview.js
-UPD 2018-06-04_23:00:50 262029 www/tablet/js/widget_chart.js
+UPD 2018-09-28_01:17:14 272177 www/tablet/js/widget_chart.js
 UPD 2018-08-24_23:15:53 5252 www/tablet/js/widget_checkbox.js
 UPD 2018-02-06_11:06:22 3390 www/tablet/js/widget_checklist.js
 UPD 2018-08-23_21:25:06 3143 www/tablet/js/widget_circlemenu.js
 UPD 2018-01-21_14:50:26 1684 www/tablet/js/widget_classchanger.js
-UPD 2018-03-12_23:10:34 5829 www/tablet/js/widget_clock.js
+UPD 2018-10-08_22:27:44 7050 www/tablet/js/widget_clock.js
 UPD 2018-01-13_16:41:02 3312 www/tablet/js/widget_colorwheel.js
 UPD 2018-01-21_14:50:53 5053 www/tablet/js/widget_controlbutton.js
 UPD 2017-09-22_08:48:55 7740 www/tablet/js/widget_controller.js
-UPD 2018-01-21_14:51:13 2650 www/tablet/js/widget_datetimepicker.js
+UPD 2018-10-04_19:13:06 2844 www/tablet/js/widget_datetimepicker.js
 UPD 2018-01-13_21:05:02 6777 www/tablet/js/widget_departure.js
 UPD 2018-03-20_14:38:04 6100 www/tablet/js/widget_dimmer.js
 UPD 2017-02-11_14:27:26 3298 www/tablet/js/widget_eventmonitor.js
 UPD 2018-01-21_14:51:58 2931 www/tablet/js/widget_example.js
-UPD 2018-08-24_20:45:30 25200 www/tablet/js/widget_famultibutton.js
+UPD 2018-09-18_23:23:14 24478 www/tablet/js/widget_famultibutton.js
 UPD 2017-11-16_18:41:57 5018 www/tablet/js/widget_filelog.js
 UPD 2018-01-21_14:53:01 8691 www/tablet/js/widget_fullcalview.js
 UPD 2018-01-21_14:52:43 5415 www/tablet/js/widget_gds.js
 UPD 2017-02-28_22:02:28 8689 www/tablet/js/widget_highchart.js
 UPD 2017-02-28_22:04:09 8209 www/tablet/js/widget_highchart3d.js
-UPD 2018-09-05_07:28:05 12075 www/tablet/js/widget_homestatus.js
+UPD 2018-09-19_00:49:29 13766 www/tablet/js/widget_homestatus.js
 UPD 2018-02-04_14:05:00 10142 www/tablet/js/widget_html.js
 UPD 2017-12-26_20:31:09 5013 www/tablet/js/widget_iframe.js
 UPD 2018-08-16_21:35:22 5689 www/tablet/js/widget_image.js
@@ -117,7 +117,7 @@ UPD 2018-01-21_14:54:12 1113 www/tablet/js/widget_include.js
 UPD 2018-01-18_21:41:36 3060 www/tablet/js/widget_input.js
 UPD 2017-02-11_14:27:26 8419 www/tablet/js/widget_itunes_artwork.js
 UPD 2018-01-09_20:25:53 2978 www/tablet/js/widget_joinedlabel.js
-UPD 2018-01-21_14:54:44 4568 www/tablet/js/widget_klimatrend.js
+UPD 2018-10-01_18:13:16 4777 www/tablet/js/widget_klimatrend.js
 UPD 2018-08-28_01:16:57 8382 www/tablet/js/widget_knob.js
 UPD 2018-04-09_21:33:59 6478 www/tablet/js/widget_label.js
 UPD 2017-12-26_20:30:09 806 www/tablet/js/widget_level.js
@@ -127,19 +127,20 @@ UPD 2017-12-30_20:03:28 5596 www/tablet/js/widget_medialist.js
 UPD 2017-11-19_14:35:00 2975 www/tablet/js/widget_meteogram.js
 UPD 2018-03-14_00:09:15 3530 www/tablet/js/widget_multistatebutton.js
 UPD 2018-01-21_14:55:44 3841 www/tablet/js/widget_notify.js
-UPD 2018-03-12_23:10:22 11332 www/tablet/js/widget_pagebutton.js
+UPD 2018-09-22_00:09:27 10390 www/tablet/js/widget_pagebutton.js
 UPD 2018-03-24_10:12:21 8748 www/tablet/js/widget_pagetab.js
-UPD 2018-08-27_22:21:48 5772 www/tablet/js/widget_playstream.js
+UPD 2018-09-29_22:33:53 5838 www/tablet/js/widget_playstream.js
 UPD 2018-08-16_07:43:39 9665 www/tablet/js/widget_popup.js
 UPD 2017-12-30_19:57:22 3564 www/tablet/js/widget_progress.js
-UPD 2018-03-28_23:46:50 1785 www/tablet/js/widget_push.js
+UPD 2018-10-02_23:13:57 1776 www/tablet/js/widget_push.js
 UPD 2017-02-06_03:22:08 6051 www/tablet/js/widget_range.js
 UPD 2017-03-03_17:46:38 2323 www/tablet/js/widget_readingsgroup.js
+UPD 2018-09-19_01:16:29 1311 www/tablet/js/widget_reload.js
 UPD 2018-01-21_14:59:28 1612 www/tablet/js/widget_rotor.js
 UPD 2017-12-01_22:36:55 7249 www/tablet/js/widget_scale.js
 UPD 2017-12-22_09:57:40 5722 www/tablet/js/widget_select.js
 UPD 2018-01-21_14:59:59 9875 www/tablet/js/widget_settimer.js
-UPD 2018-03-24_10:28:08 12991 www/tablet/js/widget_simplechart.js
+UPD 2018-10-01_18:04:53 13743 www/tablet/js/widget_simplechart.js
 UPD 2017-02-20_22:59:41 3118 www/tablet/js/widget_slideout.js
 UPD 2018-08-15_22:43:29 16306 www/tablet/js/widget_slider.js
 UPD 2018-04-05_17:32:57 12476 www/tablet/js/widget_spinner.js
@@ -148,22 +149,22 @@ UPD 2017-12-30_19:39:40 4299 www/tablet/js/widget_swiper.js
 UPD 2018-03-29_00:09:31 1557 www/tablet/js/widget_switch.js
 UPD 2018-03-28_23:04:46 1931 www/tablet/js/widget_symbol.js
 UPD 2018-03-28_20:05:24 1458 www/tablet/js/widget_theme.js
-UPD 2018-08-28_01:08:30 11506 www/tablet/js/widget_thermostat.js
+UPD 2018-09-09_19:57:26 11493 www/tablet/js/widget_thermostat.js
 UPD 2017-02-23_20:23:39 2609 www/tablet/js/widget_tts.js
 UPD 2018-01-21_15:01:52 9506 www/tablet/js/widget_uwz.js
 UPD 2018-04-02_22:16:32 9888 www/tablet/js/widget_volume.js
 UPD 2017-02-11_14:27:26 988 www/tablet/js/widget_wakeup.js
 UPD 2017-03-01_00:28:34 95955 www/tablet/js/widget_wdtimer.js
-UPD 2018-09-05_00:00:35 32806 www/tablet/js/widget_weather.js
+UPD 2018-09-19_00:45:42 32840 www/tablet/js/widget_weather.js
 UPD 2018-01-21_15:06:55 31168 www/tablet/js/widget_weekprofile.js
 UPD 2018-04-02_23:09:47 8151 www/tablet/js/widget_wind_direction.js
-UPD 2018-03-18_10:41:17 21439 www/tablet/lib/fa-multi-button.js
-UPD 2018-03-12_22:15:33 7225 www/tablet/lib/fa-multi-button.min.js
+UPD 2018-10-06_20:55:37 21397 www/tablet/lib/fa-multi-button.js
+UPD 2018-10-07_01:24:28 7225 www/tablet/lib/fa-multi-button.min.js
 UPD 2017-02-11_14:27:26 17041 www/tablet/lib/farbtastic.js
 UPD 2017-12-27_22:02:52 7295 www/tablet/lib/farbtastic.min.js
 UPD 2017-10-03_20:01:46 72785 www/tablet/lib/fhemSVG.css
 UPD 2017-12-27_22:02:52 21144 www/tablet/lib/fhemSVG.min.css
-UPD 2018-07-11_21:52:19 72322 www/tablet/lib/font-awesome.min.css
+UPD 2018-09-20_23:29:47 75289 www/tablet/lib/font-awesome.min.css
 UPD 2017-12-27_22:02:52 24968 www/tablet/lib/genericons.min.css
 UPD 2017-02-11_14:27:26 40391 www/tablet/lib/highcharts/graphics/meteogram-symbols-30px.png
 UPD 2017-02-11_14:27:26 20437 www/tablet/lib/highcharts/highcharts-3d.js
@@ -179,18 +180,18 @@ UPD 2017-02-11_14:27:26 1211 www/tablet/lib/highcharts/themes/grid-light.js
 UPD 2017-02-11_14:27:26 1794 www/tablet/lib/highcharts/themes/grid.js
 UPD 2017-02-11_14:27:26 1684 www/tablet/lib/highcharts/themes/sand-signika.js
 UPD 2017-02-11_14:27:26 1764 www/tablet/lib/highcharts/themes/skies.js
-UPD 2016-09-14_17:34:06 253669 www/tablet/lib/jquery-ui.min.js
+UPD 2018-09-22_08:35:20 228509 www/tablet/lib/jquery-ui.min.js
 UPD 2018-08-23_21:24:32 11308 www/tablet/lib/jquery.circlemenu.js
 UPD 2018-08-23_21:24:47 5746 www/tablet/lib/jquery.circlemenu.min.js
 UPD 2017-02-11_14:27:26 18412 www/tablet/lib/jquery.datetimepicker.css
 UPD 2017-02-11_14:27:26 80573 www/tablet/lib/jquery.datetimepicker.js
-UPD 2017-12-27_22:02:52 16080 www/tablet/lib/jquery.datetimepicker.min.css
-UPD 2017-12-27_22:02:52 49369 www/tablet/lib/jquery.datetimepicker.min.js
+UPD 2018-10-04_19:06:39 16502 www/tablet/lib/jquery.datetimepicker.min.css
+UPD 2018-10-04_19:07:56 60579 www/tablet/lib/jquery.datetimepicker.min.js
 UPD 2017-02-11_14:27:26 3021 www/tablet/lib/jquery.gridster.min.css
 UPD 2017-02-11_14:27:26 44235 www/tablet/lib/jquery.gridster.min.js
 UPD 2018-03-23_21:12:27 27376 www/tablet/lib/jquery.knob.mod.js
 UPD 2018-03-23_23:10:33 11139 www/tablet/lib/jquery.knob.mod.min.js
-UPD 2017-02-11_14:27:26 86709 www/tablet/lib/jquery.min.js
+UPD 2018-09-20_23:38:37 86926 www/tablet/lib/jquery.min.js
 UPD 2017-02-11_14:27:26 4508 www/tablet/lib/jquery.toast.min.css
 UPD 2017-02-11_14:27:26 6255 www/tablet/lib/jquery.toast.min.js
 UPD 2017-12-27_21:52:03 1793 www/tablet/lib/jquery.unveil.js

+ 23 - 23
fhem/core/FHEM/controls_ha_theme.txt

@@ -1,34 +1,34 @@
-DEL ./www/hausautomatisierung-com/custom.js
-UPD 2018-06-27_10:27:48 4871 www/hausautomatisierung-com/custom.js
-DEL ./www/hausautomatisierung-com/images/bg_wrapper.jpg
-UPD 2017-07-04_22:17:20 81828 www/hausautomatisierung-com/images/bg_wrapper.jpg
-DEL ./www/hausautomatisierung-com/images/haus_automatisierung.png
-UPD 2017-07-04_22:17:20 20144 www/hausautomatisierung-com/images/haus_automatisierung.png
-DEL ./www/images/hausautomatisierung_com/doorhandle.svg
-UPD 2017-08-17_20:17:57 2398 www/images/hausautomatisierung_com/doorhandle.svg
+DEL ./www/images/hausautomatisierung_com/weather.svg
+UPD 2017-08-17_20:17:57 6623 www/images/hausautomatisierung_com/weather.svg
 DEL ./www/images/hausautomatisierung_com/favicon.ico
 UPD 2017-10-14_09:47:39 15086 www/images/hausautomatisierung_com/favicon.ico
-DEL ./www/images/hausautomatisierung_com/heater.svg
-UPD 2017-08-17_20:17:57 4911 www/images/hausautomatisierung_com/heater.svg
+DEL ./www/images/hausautomatisierung_com/smartphone.svg
+UPD 2017-08-17_20:17:57 2329 www/images/hausautomatisierung_com/smartphone.svg
+DEL ./www/images/hausautomatisierung_com/power.svg
+UPD 2017-08-17_20:17:57 1802 www/images/hausautomatisierung_com/power.svg
 DEL ./www/images/hausautomatisierung_com/off.svg
 UPD 2017-08-17_20:17:57 3199 www/images/hausautomatisierung_com/off.svg
-DEL ./www/images/hausautomatisierung_com/on.svg
-UPD 2017-08-17_20:17:57 3199 www/images/hausautomatisierung_com/on.svg
+DEL ./www/images/hausautomatisierung_com/tv.svg
+UPD 2017-08-17_20:17:57 2274 www/images/hausautomatisierung_com/tv.svg
+DEL ./www/images/hausautomatisierung_com/heater.svg
+UPD 2017-08-17_20:17:57 4911 www/images/hausautomatisierung_com/heater.svg
 DEL ./www/images/hausautomatisierung_com/plug.svg
 UPD 2017-08-17_20:17:57 2727 www/images/hausautomatisierung_com/plug.svg
-DEL ./www/images/hausautomatisierung_com/power.svg
-UPD 2017-08-17_20:17:57 1802 www/images/hausautomatisierung_com/power.svg
+DEL ./www/images/hausautomatisierung_com/on.svg
+UPD 2017-08-17_20:17:57 3199 www/images/hausautomatisierung_com/on.svg
 DEL ./www/images/hausautomatisierung_com/router.svg
 UPD 2017-08-17_20:17:57 6250 www/images/hausautomatisierung_com/router.svg
-DEL ./www/images/hausautomatisierung_com/smartphone.svg
-UPD 2017-08-17_20:17:57 2329 www/images/hausautomatisierung_com/smartphone.svg
-DEL ./www/images/hausautomatisierung_com/tv.svg
-UPD 2017-08-17_20:17:57 2274 www/images/hausautomatisierung_com/tv.svg
-DEL ./www/images/hausautomatisierung_com/weather.svg
-UPD 2017-08-17_20:17:57 6623 www/images/hausautomatisierung_com/weather.svg
-DEL ./www/pgm2/hausautomatisierung_comfloorplanstyle.css
-UPD 2018-06-27_10:27:48 4942 www/pgm2/hausautomatisierung_comfloorplanstyle.css
+DEL ./www/images/hausautomatisierung_com/doorhandle.svg
+UPD 2017-08-17_20:17:57 2398 www/images/hausautomatisierung_com/doorhandle.svg
 DEL ./www/pgm2/hausautomatisierung_comstyle.css
-UPD 2018-06-27_10:27:48 27054 www/pgm2/hausautomatisierung_comstyle.css
+UPD 2018-09-15_13:48:01 26959 www/pgm2/hausautomatisierung_comstyle.css
+DEL ./www/pgm2/hausautomatisierung_comfloorplanstyle.css
+UPD 2018-09-15_13:48:01 4942 www/pgm2/hausautomatisierung_comfloorplanstyle.css
 DEL ./www/pgm2/hausautomatisierung_comsvg_style.css
 UPD 2018-01-30_20:22:38 5011 www/pgm2/hausautomatisierung_comsvg_style.css
+DEL ./www/hausautomatisierung-com/images/haus_automatisierung.png
+UPD 2017-07-04_22:17:20 20144 www/hausautomatisierung-com/images/haus_automatisierung.png
+DEL ./www/hausautomatisierung-com/images/bg_wrapper.jpg
+UPD 2017-07-04_22:17:20 81828 www/hausautomatisierung-com/images/bg_wrapper.jpg
+DEL ./www/hausautomatisierung-com/custom.js
+UPD 2018-09-15_13:48:01 5043 www/hausautomatisierung-com/custom.js

+ 4 - 0
fhem/core/FHEM/controls_tradfri.txt

@@ -0,0 +1,4 @@
+UPD 2017-08-27_13:59:40 10334 FHEM/30_TradfriGateway.pm
+UPD 2017-08-27_13:59:40 10213 FHEM/31_TradfriDevice.pm
+UPD 2017-08-27_13:59:40 9629 FHEM/31_TradfriGroup.pm
+UPD 2017-08-27_13:59:40 7005 FHEM/TradfriUtils.pm

BIN=BIN
fhem/core/FHEM/lib/fhem_zwave_deviceconfig.xml.gz


+ 3 - 2
fhem/core/FHEM/lib/openzwave_manufacturer_specific.xml

@@ -524,6 +524,7 @@
 		<Product type="0800" id="3001" name="FGMS001 Motion Sensor" config="fibaro/fgms.xml" />
 		<Product type="0800" id="4001" name="FGMS001 Motion Sensor" config="fibaro/fgms.xml" />
 		<Product type="0801" id="1001" name="FGMS001-ZW5 Motion Sensor" config="fibaro/fgmszw5.xml"/>
+		<Product type="0801" id="1002" name="FGMS001-ZW5 Motion Sensor" config="fibaro/fgmszw5.xml"/>
 		<Product type="0801" id="2001" name="FGMS001-ZW5 Motion Sensor" config="fibaro/fgmszw5.xml"/>
 		<Product type="0801" id="3001" name="FGMS001-ZW5 Motion Sensor" config="fibaro/fgmszw5.xml"/>
 		<Product type="0b00" id="3001" name="FGFS101 Flood Sensor" config="fibaro/fgfs101.xml" />
@@ -922,7 +923,7 @@
 		<Product type="0003" id="1087" name="Power plug 12A" config="shenzen_neo/nas-wr01z.xml"/>
 		<Product type="0003" id="0083" name="Battery Powered PIR Sensor" config="shenzen_neo/nas-pd01z.xml"/>
 		<Product type="0003" id="1083" name="Battery Powered PIR Sensor" config="shenzen_neo/nas-pd01z.xml"/>
-		<Product type="0003" id="108d" name="Battery Powered PIR Sensor" config="shenzen_neo/nas-pd01z.xml"/>
+		<Product type="0003" id="108d" name="Battery Powered PIR Sensor" config="shenzen_neo/nas-pd02z.xml" forumNr="91363"/>
 		<Product type="0003" id="0085" name="Water Leakage Detector" config="shenzen_neo/nas-ws02z.xml"/>
 		<Product type="0003" id="1085" name="Water Leakage Detector" config="shenzen_neo/nas-ws02z.xml"/>
 		<Product type="0003" id="6085" name="Water Leakage Detector" config="shenzen_neo/nas-ws02z.xml"/>
@@ -1148,7 +1149,7 @@
 		<Product type="0002" id="1a72" name="IS 140-2" config="steinel/is140-2.xml"/>
 		<Product type="0001" id="1a73" name="XLED Home 2" config="steinel/is140-2.xml"/>
 		<Product type="0001" id="1a74" name="RS LED D2" config="steinel/rs-led-d2.xml"/>
-		<Product type="0001" id="1a75" name="L 810 LED iHF" forumNr="no-statistics"/>
+		<Product type="0001" id="1a75" name="L 810 LED iHF" config="steinel/l810-led-ihf.xml"/>
 	</Manufacturer>
 	<Manufacturer id="0166" name="Swiid">
 		<Product type="0100" id="0100" name="SwiidInter" config="swiid/swiidinter.xml"/>

+ 4 - 2
fhem/core/MAINTAINER.txt

@@ -105,7 +105,7 @@ FHEM/14_SD_WS09.pm           Sidey/pejonp         Sonstige Systeme
 FHEM/14_SD_RSL.pm            Sidey/Ralf9          Sonstige Systeme
 FHEM/17_SIS_PMS.pm           painseeker           Sonstiges
 FHEM/18_CUL_HOERMANN.pm      rudolfkoenig         SlowRF
-FHEM/19_Revolt.pm            martinppp/mehf       SlowRF
+FHEM/19_Revolt.pm            yoda_gh              SlowRF
 FHEM/19_VBUSIF.pm            Tobias/pejonp        Sonstige Systeme
 FHEM/20_FRM_AD.pm            jensb                Sonstige Systeme
 FHEM/20_FRM_ROTENC.pm        ntruchsess           Sonstige Systeme
@@ -145,6 +145,7 @@ FHEM/23_LUXTRONIK2.pm        tupol                Heizungssteuerung/Raumklima (l
 FHEM/23_WEBIO.pm             sachag               Sonstiges
 FHEM/23_WEBIO_12DIGITAL.pm   sachag               Sonstiges
 FHEM/24_NetIO230B.pm         rudolfkoenig/orphan  Sonstiges
+FHEM/24_Iluminize.pm	     VolkerKettenbach     Sonstige Systeme
 FHEM/24_TPLinkHS110.pm	     VolkerKettenbach     Sonstige Systeme
 FHEM/26_tahoma.pm            mike3436             Sonstige Systeme
 FHEM/30_DUOFERN              telekatz             Sonstige Systeme
@@ -196,6 +197,7 @@ FHEM/36_LaCrosse.pm          HCS                  Sonstige Systeme
 FHEM/36_LaCrosseGateway.pm   HCS                  Sonstige Systeme
 FHEM/36_EMT7110.pm           HCS                  Sonstige Systeme
 FHEM/36_Level.pm             HCS                  Sonstige Systeme
+FHEM/36_Shelly.pm            pahenning            Sonstige Systeme
 FHEM/36_Vallox.pm            Skjall               http://forum.fhem.de/index.php/topic,71325.0.html
 FHEM/36_WMBUS.pm             kaihs                Sonstige Systeme
 FHEM/37_NotifyAndroidTV.pm   justme1968           Multimedia
@@ -405,7 +407,7 @@ FHEM/94_PWM.pm               jamesgo              Heizungssteuerung/Raumklima
 FHEM/95_FLOORPLAN.pm         ulimaass             Frontends/FLOORPLAN
 FHEM/95_Alarm.pm             pahenning            Unterstützende Dienste
 FHEM/95_Astro.pm             pahenning            Unterstützende Dienste
-FHEM/95_Babble.pm             pahenning            Unterstützende Dienste
+FHEM/95_Babble.pm            pahenning            Unterstützende Dienste
 FHEM/95_PostMe.pm            pahenning            Unterstützende Dienste
 FHEM/95_YAAHM.pm             pahenning            Unterstützende Dienste
 FHEM/95_Dashboard.pm         DS_Starter/orphan    Frontends

+ 0 - 1
fhem/core/demolog/fhem.save

@@ -347,5 +347,4 @@ setstate sunRise Next: 05:57:09
 setstate sunRise 2018-08-26 08:08:39 state Next: 05:57:09
 setstate sunSet Next: 20:57:55
 setstate sunSet 2018-08-25 21:11:31 state Next: 20:57:55
-setstate telnetPort 2018-08-25 21:11:31 state Initialized
 setstate wlCinema initialized

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1398 - 322
fhem/core/docs/commandref.html


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 844 - 335
fhem/core/docs/commandref_DE.html


+ 5 - 8
fhem/core/docs/commandref_frame.html

@@ -4,24 +4,22 @@
 
 <head>
   <title>FHEM reference</title>
-  <script type="text/javascript" src="fhemdoc.js"></script>
-  <noscript>
-    <link rel="stylesheet" type="text/css" href="../www/pgm2/style.css" />
-  </noscript>
+  <link rel="stylesheet" type="text/css" href="../www/pgm2/style.css" />
   <meta http-equiv="Content-type" content="text/html; charset=UTF-8">
+  <meta name="viewport" content="initial-scale=1.0,user-scalable=1">
   <link rel="shortcut icon" href="/fhem/icons/favicon.ico"/>
 </head>
 
 
-<body style="word-wrap: break-word;">
+<body style="word-wrap: break-word;" class="commandref">
   <div id="menuScrollArea">
     <div id="logo"></div>
     <div id="menu">
       <h3>fhem.pl reference</h3>
       <br><br>
-      <a href="#doctop">Scroll to top</a>
+      <a href="#">Scroll to top</a>
       <br><br>
-      <a style="display:none" href="#" name="loadAll">Load complete doc</a>
+      <a style="display:none" href="#" name="loadAll">Load everything</a>
       <br><br>
       <a id="otherLang" style="display:none" href="#" name="otherLang">
         Load <span style="display:none" lang="DE">german</span> 
@@ -32,7 +30,6 @@
   </div>
 
   <div id="right">
-<a name="doctop"></a>
 <h3>Contents</h3>
 <ul>
   <a href="#intro">Introduction</a><br>

+ 5 - 8
fhem/core/docs/commandref_frame_DE.html

@@ -4,24 +4,22 @@
 
 <head>
   <title>FHEM Referenz</title>
-  <script type="text/javascript" src="fhemdoc.js"></script>
-  <noscript>
-    <link rel="stylesheet" type="text/css" href="../www/pgm2/style.css" />
-  </noscript>
+  <link rel="stylesheet" type="text/css" href="../www/pgm2/style.css" />
   <meta http-equiv="Content-type" content="text/html; charset=UTF-8">
+  <meta name="viewport" content="initial-scale=1.0,user-scalable=1">
   <link rel="shortcut icon" href="/fhem/icons/favicon.ico"/>
 </head>
 
 
-<body style="word-wrap: break-word;">
+<body style="word-wrap: break-word;" class="commandref">
   <div id="menuScrollArea">
     <div id="logo"></div>
     <div id="menu">
       <h3>fhem.pl Referenz</h3>
       <br><br>
-      <a href="#doctop">Zum Anfang</a>
+      <a href="#">Zum Anfang</a>
       <br><br>
-      <a style="display:none" href="#" name="loadAll">Komplette Doku laden</a>
+      <a style="display:none" href="#" name="loadAll">Alles laden</a>
       <br><br>
       <a id="otherLang" style="display:none" href="#" name="otherLang">
         <span style="display:none" lang="DE">Deutsche</span> 
@@ -32,7 +30,6 @@
   </div>
 
   <div id="right">
-<a name="doctop"></a>
 
 <h3>Inhalt</h3>
 <ul>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 15 - 8
fhem/core/fhem.cfg


+ 0 - 13
fhem/core/fhem.cfg.demo

@@ -1,25 +1,12 @@
 attr global userattr cmdIcon devStateIcon devStateStyle icon lightSceneParamsToSave lightSceneRestoreOnlyIfChanged:1,0 sortby structexclude webCmd webCmdLabel:textField-long widgetOverride
-attr global autoload_undefined_devices 1
-attr global autosave 0
-attr global backup_before_update 0
 attr global logfile -
 attr global modpath .
-attr global motd SecurityCheck:\
-  WEB is not password protected\
-  telnetPort is not password protected\
-\
-Protect this FHEM installation by defining an allowed device with define allowed allowed\
-You can disable this message with attr global motd none
 attr global mseclog 1
 attr global room System
 attr global sendStatistics never
 attr global statefile ./demolog/fhem.save
-attr global updateInBackground 1
 attr global verbose 3
 
-define telnetPort telnet 7072 global
-attr telnetPort room System
-
 define WEB FHEMWEB 8083 global
 attr WEB JavaScripts codemirror/fhem_codemirror.js
 attr WEB defaultRoom Light

+ 15 - 12
fhem/core/fhem.pl

@@ -27,7 +27,7 @@
 #
 #  Homepage:  http://fhem.de
 #
-# $Id: fhem.pl 17329 2018-09-12 07:03:11Z rudolfkoenig $
+# $Id: fhem.pl 17528 2018-10-14 11:53:38Z rudolfkoenig $
 
 
 use strict;
@@ -265,7 +265,7 @@ use vars qw($fhemForked);       # 1 in a fhemFork()'ed process, else undef
 use vars qw($addTimerStacktrace);# set to 1 by fhemdebug
 
 $selectTimestamp = gettimeofday();
-$cvsid = '$Id: fhem.pl 17329 2018-09-12 07:03:11Z rudolfkoenig $';
+$cvsid = '$Id: fhem.pl 17528 2018-10-14 11:53:38Z rudolfkoenig $';
 
 my $AttrList = "alias comment:textField-long eventMap:textField-long ".
                "group room suppressReading userReadings:textField-long ".
@@ -290,7 +290,7 @@ my %sleepers;                   # list of sleepers
 $init_done = 0;
 $lastDefChange = 0;
 $readytimeout = ($^O eq "MSWin32") ? 0.1 : 5.0;
-$featurelevel = 5.8; # see also GlobalAttr
+$featurelevel = 5.9; # see also GlobalAttr
 
 
 $modules{Global}{ORDER} = -1;
@@ -316,7 +316,7 @@ my @globalAttrList = qw(
   dnsServer
   dupTimeout
   exclude_from_update
-  featurelevel:5.5,5.6,5.7,5.8,99.99
+  featurelevel:5.5,5.6,5.7,5.8,5.9,99.99
   genericDisplayType:switch,outlet,light,blind,speaker,thermostat
   holiday2we
   httpcompress:0,1
@@ -1165,14 +1165,16 @@ AnalyzeCommand($$;$)
   #############
   # Search for abbreviation
   sub
-  getAbbr($$)
+  getAbbr($$;$)
   {
-    my ($fn,$h) = @_;
+    my ($fn,$h,$isMod) = @_;
     my $lcfn = lc($fn);
     my $fnlen = length($fn);
-    return $fn if(defined($h->{$fn}));
+    return $fn if(defined($h->{$fn}) && ($isMod || $h->{$fn}{Fn})); # speedup
     foreach my $f (sort keys %{$h}) {
-      if(length($f) >= $fnlen && lc(substr($f,0,$fnlen)) eq $lcfn) {
+      if(length($f) >= $fnlen && 
+         lc(substr($f,0,$fnlen)) eq $lcfn &&
+         ($isMod || $h->{$f}{Fn})) {
         Log 5, "AnalyzeCommand: trying $f for $fn";
         return $f;
       }
@@ -1190,7 +1192,7 @@ AnalyzeCommand($$;$)
   if(!$cmds{$fn} || !defined($cmds{$fn}{Fn})) {
     my $modName;
     $modName = $cmds{$fn}{ModuleName} if($cmds{$fn} && $cmds{$fn}{ModuleName});
-    $modName = getAbbr($fn,\%modules) if(!$modName);
+    $modName = getAbbr($fn,\%modules,1) if(!$modName);
 
     LoadModule($modName) if($modName);
     my $lfn = getAbbr($fn,\%cmds);
@@ -2634,7 +2636,7 @@ GlobalAttr($$$$)
   if($type eq "del") {
     my %noDel = ( modpath=>1, verbose=>1, logfile=>1 );
     return "The global attribute $name cannot be deleted" if($noDel{$name});
-    $featurelevel = 5.8 if($name eq "featurelevel");
+    $featurelevel = 5.9 if($name eq "featurelevel");
     $haveInet6    = 0   if($name eq "useInet6"); # IPv6
     return undef;
   }
@@ -4988,7 +4990,8 @@ json2nameValue($;$)
       while($val) {
         $val = eObj($ret, $name."_$idx", $val, $val, $prefix);
         $val =~ s/^\s*,\s*//;
-        $idx++
+        $val =~ s/\s*$//;
+        $idx++;
       }
     } elsif($val =~ m/^([0-9.-]+)(.*)$/s) {
       $ret->{"$prefix$name"} = $1;
@@ -4999,7 +5002,7 @@ json2nameValue($;$)
       $in = $2;
 
     } else {
-      Log 1, "Error parsing $val";
+      Log 1, "Error parsing >$val< for $prefix$name";
       $in = "";
     }
     return $in;

+ 7 - 1
fhem/core/www/hausautomatisierung-com/custom.js

@@ -19,11 +19,17 @@ function getClock() {
 
 jQuery(document).ready(function ($) {
 
-    var themeVersion = '2.7';
+    var themeVersion = '2.8';
 
     // Check für JS-Installation entfernen
     $('#hdr').addClass('js-installed');
 
+    // attr WEB hiddenroom input -> Ansicht anpassen
+    if ($('#hdr .maininput').length == 0) {
+        $('#hdr').hide();
+        $('#content').css({top: '10px'});
+    }
+
     // Add version to logo
     $('#logo').append(
         $('<span class="theme-version">' + themeVersion + '</span>')

+ 4 - 2
fhem/core/www/pgm2/alarm.js

@@ -1,6 +1,6 @@
 //########################################################################################
 // alarm.js
-// Version 4.03
+// Version 5.01
 // See 95_Alarm for licensing
 //########################################################################################
 //# Prof. Dr. Peter A. Henning
@@ -258,15 +258,17 @@ function alarm_set(name) {
     for (var i = 0;
     i < alarmno;
     i++) {
+        FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '=attr%20' + name + '%20level' + i + 'cond%20' + document.getElementById('l' + i + 'c').value);
         FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '=attr%20' + name + '%20level' + i + 'start%20' + document.getElementById('l' + i + 's').value);
         FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '=attr%20' + name + '%20level' + i + 'end%20' + document.getElementById('l' + i + 'e').value);
+        FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '=attr%20' + name + '%20level' + i + 'autocan%20' + document.getElementById('l' + i + 'o').value);
         FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '=attr%20' + name + '%20level' + i + 'msg%20' + document.getElementById('l' + i + 'm').value);
         if (document.getElementById('l' + i + 'x').checked == true) {
             val = "armed";
         } else {
             val = "disarmed";
         }
-        FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '=attr%20' + name + '%20level' + i + 'xec%20' + val);
+        //FW_cmd(url + '?XHR=1&fwcsrf=' + csrfToken + '&cmd.' + name + '=attr%20' + name + '%20level' + i + 'xec%20' + val);
     }
     
     // acquiring data for each sensor

+ 15 - 0
fhem/core/www/pgm2/darksmallscreenstyle.css

@@ -115,3 +115,18 @@ div.tiny { font-size:10px; }
   table.block slider { float: none; }
   table.block select { text-align: center; }
 }
+
+/* commandref */
+body.commandref ul { -webkit-padding-start:0; }
+body.commandref div#menuScrollArea div#menu a { margin-left:10px; }
+body.commandref table.class_device { word-break:break-all; }
+
+body.commandref div#right { top:48px; left:5px; right:5px; }
+body.commandref div#menuScrollArea {
+  top:0px; left:10px; right:10px; height:48px; 
+  position:fixed; z-index:30; background-color:inherit;
+}
+body.commandref div#menu { top:15px; left:40px; }
+body.commandref div#menu a { margin-left:20px; }
+body.commandref div#menu br { display:none; }
+body.commandref div#menu h3 { display:none; }

+ 3 - 0
fhem/core/www/pgm2/darkstyle.css

@@ -103,6 +103,7 @@ span.sort-item-delete-link {
 
 svg { height:32px; width:32px; vertical-align:middle; margin:2px 0; }
 svg:not([fill]) { fill:#fff; }
+svg.on,svg.FS20_on { fill:orange!important; }
 g.on { fill:red; }
 
 /* next lines are for remotecontrol */
@@ -139,3 +140,5 @@ div#svgmarker {
   color:white; background:black;
   border:2px solid white; border-radius:4px;
 }
+
+body.commandref div#menuScrollArea { position:fixed; }

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 20 - 9
fhem/core/www/pgm2/f18.js


+ 28 - 1
fhem/core/www/pgm2/f18style.css

@@ -27,6 +27,12 @@ input.maininput { margin-left:10px; }
 }
 #menu > table td { padding-top: 2px; }
 
+/* Hide the scrollbar for fixed input */
+body.fixedInput[data-os=linux] div#content::-webkit-scrollbar   {display:none;}
+body.fixedInput[data-os=linux] div#menu::-webkit-scrollbar      {display:none;}
+body.fixedInput[data-os=windows] div#content::-webkit-scrollbar {display:none;}
+body.fixedInput[data-os=windows] div#menu::-webkit-scrollbar    {display:none;}
+
 #menu.hidden  { left:-120%!important; }
 #console { width:100%; top:2em; bottom:0px; position:absolute; overflow-y:auto;}
 #errmsg  { background-color: #000000; color: #FFFFFF;
@@ -185,7 +191,6 @@ table.room,table.block.wide,table.fileList {
 }
 table.block.wide td > div, #menu td td { padding:2px 4px; }
 
-#menuScrollArea { display:none; } /* commandref */
 body[fw_id] #menuScrollArea { display:block; } /* not commandref */
 
 input[type=submit] { border:none; background:none; }
@@ -252,6 +257,16 @@ body.fixedInput #hdr {
 
   .wrapcolumns tr td:nth-child(1) { flex-grow: 1; }
   .wrapcolumns tr td:nth-child(2) { width: auto!important; }
+
+  body.commandref div#menuScrollArea div#menu a { margin-left:10px; }
+  body.commandref table.class_device { word-break:break-all; }
+}
+
+@media screen and (max-width: 480px) {
+  body.commandref ul { -webkit-padding-start:0; }
+}
+@media screen and (max-height: 480px) {
+  body.commandref ul { -webkit-padding-start:0; }
 }
 
 body.pinnedMenu #menuBtn { display:none; }
@@ -260,3 +275,15 @@ body.pinnedMenu #logo { left:10px; }
 body { background-repeat: no-repeat; background-size:cover; }
 
 div.SVGlabel { display:inline-block; }
+
+/* commandref */
+body.commandref { background-color:#FFFFE7; }
+body.commandref div#right { top:48px; }
+body.commandref div#menuScrollArea { 
+  top:0px; left:10px; right:10px; height:48px; 
+  position:fixed; z-index:30; background-color:inherit;
+}
+body.commandref div#menu { top:15px; left:40px; }
+body.commandref div#menu a { margin-left:20px; }
+body.commandref div#menu br { display:none; }
+body.commandref div#menu h3 { display:none; }

+ 13 - 3
fhem/core/www/pgm2/fhemweb.js

@@ -1,6 +1,6 @@
 "use strict";
 var FW_version={};
-FW_version["fhemweb.js"] = "$Id: fhemweb.js 17308 2018-09-09 13:43:37Z rudolfkoenig $";
+FW_version["fhemweb.js"] = "$Id: fhemweb.js 17478 2018-10-07 16:45:18Z rudolfkoenig $";
 
 var FW_serverGenerated;
 var FW_serverFirstMsg = (new Date()).getTime()/1000;
@@ -13,6 +13,14 @@ var FW_root = "/fhem";  // root
 var FW_availableJs={};
 var FW_urlParams={};
 var embedLoadRetry = 100;
+var FW_os = "unknown";
+
+if(FW_isiOS) { FW_os = "iOS";
+} else if(navigator.userAgent.indexOf("Android") >= 0) { FW_os = "android";
+} else if(navigator.userAgent.indexOf("OS X")    >= 0) { FW_os = "osx";
+} else if(navigator.userAgent.indexOf("Windows") >= 0) { FW_os = "windows";
+} else if(navigator.userAgent.indexOf("Linux")   >= 0) { FW_os = "linux";
+}
 
 // createFn returns an HTML Element, which may contain 
 // - setValueFn, which is called when data via longpoll arrives
@@ -72,6 +80,7 @@ FW_jqueryReadyFn()
     return;
   FW_docReady = true;
   FW_serverGenerated = $("body").attr("generated");
+
   FW_longpollType = $("body").attr("longpoll");
   var ajs = $("body").attr("data-availableJs");
   if(ajs) {
@@ -281,7 +290,7 @@ FW_jqueryReadyFn()
   $("select[id^=sel_attr],select[id^=sel_set],select[id^=sel_get]")
   .change(function(){ // online help
     var val = $(this).val();
-    var m = $(this).attr("id").match(/sel_(set|get|attr)(.*)/);
+    var m = $(this).attr("name").match(/arg.(set|get|attr)(.*)/);
     if(!m)
       return;
     $("#devSpecHelp").remove();
@@ -309,6 +318,7 @@ FW_jqueryReadyFn()
   FW_rawDef();
   FW_treeMenu();
 
+  $("body").attr("data-os", FW_os);
   // automatic reload for style change
   if(location.search.indexOf("cmd=style%20select") > 0) {
     $('a[href*="style set"],a[onclick*="style set"]').each(function(){
@@ -876,7 +886,7 @@ FW_rawDef()
   });
 }
 
-var FW_arrowDown, FW_arrowRight;
+var FW_arrowDown="", FW_arrowRight="";
 function
 FW_treeMenu()
 {

+ 1 - 1
fhem/core/www/pgm2/hausautomatisierung_comfloorplanstyle.css

@@ -18,7 +18,7 @@ a img { border: none; }
 article, aside, details, figcaption, figure, footer, header, hgroup, main, menu, nav, section, summary { display: block; }
 
 #logo { background: url(../hausautomatisierung-com/images/haus_automatisierung.png) no-repeat; height: 120px; margin-left: 40px; margin-top: 10px; position: fixed; width: 220px; top: 0; left: 0; }
-#logo:after { color: #e56524; content: "2.7"; opacity: 0.7; }
+#logo:after { color: #e56524; content: "2.8"; opacity: 0.7; }
 
 body { background: #354d69 url(../hausautomatisierung-com/images/bg_wrapper.jpg) no-repeat center 0 fixed; color: #fff; font-family: 'Open Sans', sans-serif; font-size: 14px; margin: 0 auto; padding: 0; tab-size: 4; }
 

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 11 - 10
fhem/core/www/pgm2/hausautomatisierung_comstyle.css


+ 16 - 0
fhem/core/www/pgm2/smallscreenstyle.css

@@ -73,3 +73,19 @@ button.dist { margin:5px; }
   table.block slider { float: none; }
   table.block select { text-align: center; }
 }
+
+
+body.commandref ul { -webkit-padding-start:0; }
+body.commandref div#menuScrollArea div#menu a { margin-left:10px; }
+body.commandref table.class_device { word-break:break-all; }
+
+body.commandref div#right { top:48px; }
+body.commandref div#logo { display:none; }
+body.commandref div#menu {
+  position:fixed; z-index:30; background-color:#FFFFE7;
+  top:0px; left:0px; right:10px; height:48px; 
+}
+body.commandref div#menu a { margin:20px; }
+body.commandref div#menu br { display:none; }
+body.commandref div#menu h3 { display:none; }
+body.commandref div#right { left:5px; right:5px }

+ 68 - 7
fhem/core/www/tablet/css/fhem-tablet-ui.css

@@ -112,6 +112,22 @@ body {
 .ftui-window:before {
     content: "\ea01";
 }
+.ftui-circle-thin:before {
+    content: "\e906";
+}
+i#bg.fa.fa-stack-2x.ftui-circle-thin {
+    font-size: 1.8em;
+    margin-top: 0.055em;
+    margin-left: 0.005em;
+}
+.ftui-square-o:before {
+    content: "\e907";
+}
+i#bg.fa.fa-stack-2x.ftui-square-o {
+    font-size: 2.2em;
+    margin-top: -0.035em;
+    margin-left: 0.01em;
+}
 
 a:link {
     text-decoration: none;
@@ -410,7 +426,8 @@ a:active {
 /* . */
 
 #header-nav {
-    height: 55px;
+    height: 4.5em;
+    line-height: 4.5em;
     width: 100%;
     top: 0;
     left: 0;
@@ -770,13 +787,13 @@ section {
     align-items: initial;
 }
 
-.h100,
+.w100,
 .full-width,
 width-100 {
     width: 100% !important;
 }
 
-.v100,
+.h100,
 .full-height,
 .height-100 {
     height: 100%;
@@ -797,6 +814,12 @@ main > .box.horizontal {
     height: 100%;
 }
 
+.vbox {
+    -webkit-box-pack: justify;
+    -webkit-justify-content: space-around;
+    justify-content: space-around;
+}
+
 .vbox,
 .card,
 .box.vertical,
@@ -965,6 +988,11 @@ main > .box.horizontal {
     align-items: flex-start;
 }
 
+.box.vertical.items-center,
+.vbox.items-center {
+    align-items: center;
+}
+
 .card {
     margin: 4px;
     justify-content: flex-start;
@@ -1983,7 +2011,7 @@ div[data-type="link"] > div.fa {
 
 /* label in fa-multibutton */
 .famultibutton div {
-    margin-top: -5%;
+    margin-top: -0.03em;
 }
 
 .wider {
@@ -2081,81 +2109,101 @@ div[data-type="link"] > div.fa {
     margin-right: -4em !important;
 }
 
+.mt,
+.mt-1,
 .top-space,
 .top-space-50 {
     margin-top: 1em !important;
 }
 
+.mt-2,
 .top-space-2x,
 .top-space-2,
 .top-space-100 {
     margin-top: 2em !important;
 }
 
+.mt-3,
 .top-space-3x,
 .top-space-3,
 .top-space-150 {
     margin-top: 3em !important;
 }
 
+.mt-4,
 .top-space-4x,
 .top-space-4,
 .top-space-200 {
     margin-top: 4em !important;
 }
 
+.ml,
+.ml-1,
 .left-space {
     margin-left: 1em !important;
 }
 
+.ml-2,
 .left-space-2x,
 .left-space-2 {
     margin-left: 2em !important;
 }
 
+.ml-3,
 .left-space-3x,
 .left-space-3 {
     margin-left: 3em !important;
 }
 
+.ml-4,
 .left-space-4x,
 .left-space-4 {
     margin-left: 4em !important;
 }
 
+.mr,
+.mr-1,
 .right-space {
     margin-right: 1em !important;
 }
 
+.mr-2,
 .right-space-2x,
 .right-space-2 {
     margin-right: 2em !important;
 }
 
+.mr-3,
 .right-space-3x,
 .right-space-3 {
     margin-right: 3em !important;
 }
 
+.mr-4,
 .right-space-4x,
 .right-space-4 {
     margin-right: 4em !important;
 }
 
+.mb,
+.mb-1,
 .bottom-space {
     margin-bottom: 1em !important;
 }
 
+.mb-2,
 .bottom-space-2x,
 .bottom-space-2 {
     margin-bottom: 2em !important;
 }
 
+.mb-3,
 .bottom-space-3x,
 .bottom-space-3 {
     margin-bottom: 3em !important;
 }
 
+.mb-4,
 .bottom-space-4x,
 .bottom-space-4 {
     margin-bottom: 4em !important;
@@ -2653,6 +2701,20 @@ img.mini,
     padding-left: 52px;
 }
 
+.badge {
+    position: absolute;
+    min-width: 1.5em;
+    padding: 0.25em 0.3em;
+    font-weight: 300;
+    font-style: normal;
+    line-height: 1;
+    color: #fff;
+    text-align: center;
+    white-space: nowrap;
+    vertical-align: middle;
+    background-color: #aa2200;
+    border-radius: 1em;
+}
 .famultibutton #warn,
 .weather #warn {
     position: absolute;
@@ -2723,13 +2785,12 @@ img.mini,
 /* FA 5.* circle is bigger */
 
 .famultibutton .fa-stack-2x:not(.fa-v5) {
-    font-size: 1.8em;
-    margin-top: 0.01em;
+    font-size: 1.92em;
+    margin-top: 0.02em;
 }
 
 .famultibutton [class^="fa fa-stack-1x fa-"]:not(.fa-v5) {
     font-size: 0.95em;
-    margin-top: -0.05em;
 }
 
 /* FA 5.* normal and inverted */

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 1
fhem/core/www/tablet/css/fhem-tablet-ui.min.css


+ 10 - 0
fhem/core/www/tablet/css/ftui_chart.css

@@ -46,6 +46,8 @@
 .buttons {
 	font-size: 24px;
 	fill: #555;
+	font-weight: 900;
+	font-family: 'Font Awesome 5 Free';
 }
 /* Definition for activated texts ('legend' and 'cursor') */
 .caption.active {
@@ -142,5 +144,13 @@ text.ftui.l3, text.ftui.l3sym, text.ftui.l3dot, text.ftui.l3dash, text.ftui.l3fi
 text.ftui.l4, text.ftui.l4sym, text.ftui.l4dot, text.ftui.l4dash, text.ftui.l4fill { stroke:none; fill:#33CC33; }
 text.ftui.l5, text.ftui.l5sym, text.ftui.l5dot, text.ftui.l5dash, text.ftui.l5fill { stroke:none; fill:#33CCCC; }
 text.ftui.l6, text.ftui.l6sym, text.ftui.l6dot, text.ftui.l6dash, text.ftui.l6fill { stroke:none; fill:#3333CC; }
+/* additional settings for the tspan (mainly for legend window) */
+tspan.ftui.l0, tspan.ftui.l0sym, tspan.ftui.l0dot, tspan.ftui.l0dash, tspan.ftui.l0fill { stroke:none; fill:#DDA400; } 
+tspan.ftui.l1, tspan.ftui.l1sym, tspan.ftui.l1dot, tspan.ftui.l1dash, tspan.ftui.l1fill { stroke:none; fill:#BBBBBB; } 
+tspan.ftui.l2, tspan.ftui.l2sym, tspan.ftui.l2dot, tspan.ftui.l2dash, tspan.ftui.l2fill { stroke:none; fill:#CC0000; }
+tspan.ftui.l3, tspan.ftui.l3sym, tspan.ftui.l3dot, tspan.ftui.l3dash, tspan.ftui.l3fill { stroke:none; fill:#CCCC00; }
+tspan.ftui.l4, tspan.ftui.l4sym, tspan.ftui.l4dot, tspan.ftui.l4dash, tspan.ftui.l4fill { stroke:none; fill:#33CC33; }
+tspan.ftui.l5, tspan.ftui.l5sym, tspan.ftui.l5dot, tspan.ftui.l5dash, tspan.ftui.l5fill { stroke:none; fill:#33CCCC; }
+tspan.ftui.l6, tspan.ftui.l6sym, tspan.ftui.l6dot, tspan.ftui.l6dash, tspan.ftui.l6fill { stroke:none; fill:#3333CC; }
 
 /* end of styles for chart widget */

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 9 - 18
fhem/core/www/tablet/fonts/FTUI-icons.svg


BIN=BIN
fhem/core/www/tablet/fonts/FTUI-icons.ttf


BIN=BIN
fhem/core/www/tablet/fonts/FTUI-icons.woff


BIN=BIN
fhem/core/www/tablet/fonts/fa-brands-400.eot


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 27 - 0
fhem/core/www/tablet/fonts/fa-brands-400.svg


BIN=BIN
fhem/core/www/tablet/fonts/fa-brands-400.ttf


BIN=BIN
fhem/core/www/tablet/fonts/fa-brands-400.woff


BIN=BIN
fhem/core/www/tablet/fonts/fa-brands-400.woff2


BIN=BIN
fhem/core/www/tablet/fonts/fa-regular-400.eot


+ 0 - 0
fhem/core/www/tablet/fonts/fa-regular-400.svg


Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio