diff --git a/fhem/CHANGED b/fhem/CHANGED index d599318fe..746e442ca 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # 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. + - feature: 10_KNX: KNX_scan Utility function - bugfix: 82_LGTV_WebOS: rewrite and change code, fix bugs of older version - bugfix: 88_HMCCU: Bugfixes and improvements - bugfix: 73_.*Calculator: bugfix - Correct unit for SymcCounter diff --git a/fhem/FHEM/10_KNX.pm b/fhem/FHEM/10_KNX.pm index 0cd0ed588..3dcebf888 100644 --- a/fhem/FHEM/10_KNX.pm +++ b/fhem/FHEM/10_KNX.pm @@ -66,6 +66,10 @@ # removed examples from cmdref -> wiki # MH 20211118 E04.90 fix dpt10 now, fix dpt19 workingdays # fix dpt3 encode +# MH 20220107 E05.00 feature: add support for FHEM2FHEM as IO-Device +# E05.01 feature: utitity KNX_scan +# corrections cmd-ref +# optimize replaceByRegex package FHEM::KNX; ## no critic 'package' @@ -109,18 +113,15 @@ BEGIN { AnalyzePerlCommand AnalyzeCommandChain EvalSpecials fhemTimeLocal) ); + # export to main context (with different name) + GP_Export( + qw(Initialize KNX_scan) + ); } -### export to main context (with different name) -GP_Export(qw(Initialize)); - #string constants my $MODELERR = "MODEL_NOT_DEFINED"; # for autocreate -#my $ONFORTIMER = "on-for-timer"; -#my $OFFFORTIMER = "off-for-timer"; -#my $ONUNTIL = "on-until"; -#my $OFFUNTIL = "off-until"; my $BLINK = "blink"; my $TOGGLE = "toggle"; my $RAW = "raw"; @@ -151,7 +152,7 @@ my $PAT_DPT1_PAT = '(on)|(off)|(0?1)|(0?0)'; my $PAT_DTSEP = qr/(?:_)/ix; # date/time separator my $PAT_DATE = qr/(3[01]|[0-2]?[0-9])\.(1[0-2]|0?[0-9])\.((?:19|20)[0-9][0-9])/ix; #pattern for time -my $PAT_TIME = qr/(2[0-4]|[01]{0,1}[0-9]):([0-5]{0,1}[0-9]):([0-5]{0,1}[0-9])/ix; #E04.90 +my $PAT_TIME = qr/(2[0-4]|[01]{0,1}[0-9]):([0-5]{0,1}[0-9]):([0-5]{0,1}[0-9])/ix; #my $PAT_TIME = qr/(2[0-4]|[0?1][0-9]):([0?1-5][0-9]):([0?1-5][0-9])/ix; my $PAT_DPT16_CLR = qr/>CLR{rawDevice}; #name of fake local IO-dev or remote IO-dev + if (defined($rawdef)) { + return if (exists($defs{$rawdef}) && $defs{$rawdef}->{TYPE} eq 'KNXIO' && $defs{$rawdef}->{model} eq 'X'); # only if model of fake device eq 'X' + return if (exists($iohash->{'.RTYPE'}) && $iohash->{'.RTYPE'} eq 'KNXIO'); # remote TYPE is KNXIO + } + } return $aVal . ' is not a valid IO-Device for this Device'; } } # /set @@ -1017,6 +1029,11 @@ sub KNX_Parse { #gad not defined yet, give feedback for autocreate if (not (exists $modules{KNX}{defptr}{$gadCode})) { +# #E05.00 check if any autocreate device is disabled +# my @acList = devspec2array('TYPE=autocreate'); +# foreach my $acdev (@acList) { +# return q{} if (Value($defs{$acdev}) eq 'disabled'); # dont go thru "UNDEFINED...." +# } #format gad my $gad = KNX_hexToName($gadCode); #create name @@ -1151,14 +1168,15 @@ sub KNX_SetReadings { my $name = $hash->{NAME}; #append post-string, if supplied - my $suffix = AttrVal($name, "format",undef); + my $suffix = AttrVal($name, 'format', undef); $transval .= q{ } . $suffix if (defined($suffix)); - #execute regex, if defined - my $regAttr = AttrVal($name, "stateRegex", undef); - my $state = KNX_replaceByRegex ($regAttr, $rdName, $transval); - - my $logstr = (defined($state))?$state:'UNDEFINED'; - Log3 ($name, 5, "KNX_SetReadings: $name - replaced $rdName value from: $transval to $logstr") if ($transval ne $logstr); +#E05.01 #execute stateRegex + my $state = KNX_replaceByRegex ($hash, $rdName, $transval); +# my $regAttr = AttrVal($name, "stateRegex", undef); +# my $state = KNX_replaceByRegex ($regAttr, $rdName, $transval); +# +# my $logstr = (defined($state))?$state:'UNDEFINED'; +# Log3 ($name, 5, "KNX_SetReadings: $name - replaced $rdName value from: $transval to $logstr") if ($transval ne $logstr); my $lsvalue = 'fhem'; # called from set $lsvalue = KNX_hexToName2($src) if (defined($src) && ($src ne q{})); # called from parse @@ -1171,7 +1189,7 @@ sub KNX_SetReadings { #execute state-command if defined #must be placed after first reading, because it may have a reference my $deviceName = $name; #hack for being backward compatible - serve $name and $devname - my $cmdAttr = AttrVal($name, "stateCmd", undef); + my $cmdAttr = AttrVal($name, 'stateCmd', undef); if ((defined($cmdAttr)) && ($cmdAttr ne q{})) { my $newstate = KNX_eval ($hash, $gadName, $state, $cmdAttr); @@ -1184,7 +1202,7 @@ sub KNX_SetReadings { } } - readingsBulkUpdate($hash, "state", $state); + readingsBulkUpdate($hash, 'state', $state); } readingsEndUpdate($hash, 1); return; @@ -1291,12 +1309,20 @@ sub KNX_checkAndClean { return $value; } -# replace state-values Attribute: stateRegex +#E05.01 +# replace state-values by Attr stateRegex sub KNX_replaceByRegex { - my ($regAttr, $rdName, $input) = @_; + my ($hash, $rdName, $input) = @_; + my $name = $hash->{NAME}; + my $regAttr = AttrVal($name, 'stateRegex', undef); return $input if (! defined($regAttr)); +#sub KNX_replaceByRegex { +# my ($regAttr, $rdName, $input) = @_; +# +# return $input if (! defined($regAttr)); +# my $retVal = $input; #execute regex, if defined @@ -1317,7 +1343,9 @@ sub KNX_replaceByRegex { if (not defined ($regPair[1])) { #cut value - $retVal = undef; + Log3 ($name, 5, "KNX_replaceByRegex: $name - replaced $rdName value from: $input to undefined"); + return; +# $retVal = undef; } elsif ($regPair[0] eq $tempVal) { # complete match $retVal = $regPair[1]; @@ -1333,6 +1361,7 @@ sub KNX_replaceByRegex { last; } + Log3 ($name, 5, "KNX_replaceByRegex: $name - replaced $rdName value from: $input to $retVal") if ($input ne $retVal); return $retVal; } @@ -1485,8 +1514,7 @@ sub enc_dpt3 { #Step value (four-bit) my $numval = 0; my $sign = ($value >=0 )?1:0; $value = abs($value); -# my @values = qw( 75 50 25 12 6 3 1 ); - my @values = qw( 75 50 25 12 6 3 1 0); #E04.90 + my @values = qw( 75 50 25 12 6 3 1 0); foreach my $key (@values) { $numval++; if ($value >= $key) { @@ -1533,23 +1561,6 @@ sub enc_dpt9 { #2-Octet Float value sub enc_dpt10 { #Time of Day my $value = shift; my $numval = 0; -=pod - if ($value =~ m/now/ix) { - #get actual time - my ($secs,$mins,$hours,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); - #add offsets - $year+=1900; - $mon++; - # calculate offset for weekday - $wday = 7 if ($wday == 0); - $hours += 32 * $wday; - } - else { - my ($hh, $mm, $ss) = split(/:/x, $value); - $numval = $ss + ($mm << 8) + ($hh << 16); - } -=cut -#E04.90 new code my ($secs,$mins,$hours,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); # default now if ($value =~ /$PAT_TIME/ix) { ($hours,$mins,$secs) = split(/[:]/ix,$value); @@ -1632,9 +1643,8 @@ sub enc_dpt19 { #DateTime my ($secs,$mins,$hours,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($ts); $wday = 7 if ($wday eq "0"); # calculate offset for weekday $hours += ($wday << 5); # add day of week - my $status1 = 0x40; #E04.90 Fault=0, WD = 1, NWD = 0 (WD Field valid), NY = 0, ND = 0, NDOW= 0,NT=0, SUTI = 0 -# my $status1 = 0x20; # Fault=0, WD = 0, NWD = 1 (WD Field valid), NY = 0, ND = 0, NDOW= 0,NT=0, SUTI = 0 - $status1 = $status1 & 0xBF if ($wday >= 6); #E04.90 Saturday & Sunday is non working day + my $status1 = 0x40; # Fault=0, WD = 1, NWD = 0 (WD Field valid), NY = 0, ND = 0, NDOW= 0,NT=0, SUTI = 0 + $status1 = $status1 & 0xBF if ($wday >= 6); # Saturday & Sunday is non working day $status1 += 1 if ($isdst == 1); my $status0 = 0x00; # CLQ=0 $mon++; @@ -1824,6 +1834,55 @@ sub dec_dpt232 { #RGB-Code return sprintf ("%.6x",$numval); } +########## public utility functions ########## + +### get state of devices from KNX_Hardware +### called with devspec as argument +### e.g : scanKNX() / scanKNX('device1') / scanKNX('device1, dev2,dev3,...' / scanKNX('room=Kueche'), ... +### returns number of "gets" executed +#E05.01 +sub KNX_scan { + my $devs = shift; + my @devlist = (); + + $devs = 'TYPE=KNX' if (! defined($devs)); # select all + @devlist = devspec2array($devs); + + my $i = 0; #counter devices + my $j = 0; #counter devices with get + my $k = 0; #counter total get's + my $getsarr = q{}; + + foreach my $knxdef (@devlist) { + my $devhash = $defs{$knxdef}; + next if ((! defined($devhash)) || ($devhash->{TYPE} ne 'KNX') || $devhash->{DEF} =~ /MODEL_NOT_DEFINED/ix); + $i++; + my $getstring = $devhash->{GETSTRING}; + next if ((! defined($getstring)) || $getstring eq q{}); + $j++; + my @getnames = split(/\s/ix,$getstring); + + foreach my $gads (@getnames) { + my $gad = (split(/[:]/ix,$gads))[0]; + $k++; + Log3 $knxdef, 4, "scanKNXexec: [$k] get $knxdef $gad"; + $getsarr .= "$knxdef $gad,"; + } + } + Log3 undef, 3, "scanKNX: $i devices selected / $j devices with get / $k gets executing..."; + doKNX_scan($getsarr) if ($k > 0); + return $k; +} + +### issue all get cmd's - each one delayed by InternalTimer +sub doKNX_scan { + my ($devgad, $arr) = split(/,/x,shift,2); +# Log3 undef,1, "doKNX_scan: get $devgad"; + main::fhem("get $devgad"); + return if (length($arr) <= 1); + return InternalTimer(gettimeofday() + 0.2,\&doKNX_scan,$arr); # does not support array-> use string... +} + 1; @@ -1872,14 +1931,14 @@ sub dec_dpt232 { #RGB-Code
KNX is a standard for building automation / home automation. It is mainly based on a twisted pair wiring, but also other mediums (ip, wireless) are specified.
-For getting started, please refer to this document: KNX for your home auf der knx.org WebSeite.
+For getting started, please refer to this document: KNX for your home - knx.org web-site.
While the TUL-module, KNXTUL-module, or KNXIO-module represent the connection to the KNX network,
the KNX module represent a individual KNX device.
This module provides a basic set of operations (on, off, toggle, on-until, on-for-timer) to switch on/off KNX devices and to send values to the bus.
Sophisticated setups can be achieved by combining multiple KNX-groupaddresses:datapoints (GAD's:dpt's) in one KNX device instance.
-KNX defines a series of Datapoint Type as standard data types used to allow general interpretation of values of devices manufactured by different companies. - These datatypes are used to interpret the status of a device, so the state in FHEM will then show the correct value.
-For each received telegram there will be a reading with containing the received value and the sender address.
+
KNX defines a series of Datapoint Type as standard data types used to allow general interpretation of values of devices manufactured by different vendors. + These datatypes are used to interpret the status of a device, so the state in FHEM will show the correct value.
+For each received telegram there will be a reading containing the received value and the sender address.
For every set, there will be a reading containing the sent value.
The reading <state> will be updated with the last sent or received value.
A (german) wiki page is available here: FHEM Wiki @@ -1890,11 +1949,11 @@ The reading <state> will be updated with the last sent or received value.&
Important: a KNX device needs at least one concrete DPT. Please refer to available DPT. Otherwise the system cannot en- or decode the messages.
Devices defined by autocreate have to be reworked with the suitable dpt and the disable attribute cleared. Otherwise they won't do anything.
The <group> parameters are either a group name notation (0-31/0-15/0-255) or the hex representation of the value ([00-1f][0-f][00-ff]) (5 digits). +
The <group> parameter is either a group name notation (0-31/0-15/0-255) or the hex representation of it ([00-1f][0-f][00-ff]) (5 digits).
All of the defined groups can be used for bus-communication.
- It is not allowed to have the same group more then once in one device. You can have multiple devices containing the same adresses.
+ It is not allowed to have the same group more then once in one device. You can have multiple devices containing the same group-adresses.
As described above the parameter <DPT> must contain the corresponding DPT.
-The optional parameteter [gadName] may contain an alias for the GAD. The gadName must not begin with one of the following strings: The following gadNames are not allowed: on, off, on-for-timer,
+The optional parameteter [gadName] may contain an alias for the GAD. The following gadNames are not allowed: on, off, on-for-timer,
on-until, off-for-timer, off-until, toggle, raw, rgb, string, value, set, get, listenonly, nosuffix - because of conflict with cmds & parameters.
Especially if attribute answerReading
is set to 1, it might be useful to modifiy the behaviour of single GADs. If you want to restrict the GAD, you can raise the flags "get", "set", or "listenonly".
The usage should be self-explanatory. It is not possible to combine the flags.
@@ -1903,7 +1962,7 @@ Especially if attribute answerReading
is set to 1, it might be usef
If you supply <gadName> this name is used instead. The readings are <gadName>-get, <gadName>-set and <gadName>-put.
We will use the synonyms <getName>, <setName> and <putName> in this documentation.
If you add the option "nosuffix", <getName>, <setName> and <putName> have the identical name - only <gadName>.
Per default, the first group is used for sending. If you want to send via a different group, you have to address it. E.g: set <name> <gadName> <value>
The first group is used for sending by default. If you want to send to a different group, you have to address it. E.g: set <name> <gadName> <value>
Without further attributes, all incoming and outgoing messages are translated into reading <state>.
If enabled, the module autocreate is creating a new definition for any unknown sender. The device itself will be disabled
until you added a DPT to the definition and clear the disabled attribute. The name will be KNX_nnmmooo where nn is the line adress, mm the area and ooo the device.
@@ -1932,6 +1991,8 @@ If you add the option "nosuffix", <getName>, <setName> and <putNa
For dpt1 and dpt1.001 valid values are on, off, toggle and blink. Also the timer-functions can be used.
For all other binary DPT (dpt1.xxx) the min- and max-values can be used for en- and decoding alternatively to on/off.
After successful sending the value, it is stored in the readings <setName>.
Examples:
set lamp2 on # gadName omitted
set myThermoDev g1 23.44
set myMessageDev g1 Hallo Welt # dpt16 def
More complex examples can be found on the (german) Wiki
DPT - data-point-types
The following dpt are implemented and have to be assigned within the device definition. - The values right to the dpt define the valid range of Set-command values and Get-command return values.
+ The values right to the dpt define the valid range of Set-command values and Get-command return values and units.More complex examples can be found on the (german) Wiki
-KNX Utility Functions
+get <device> <gadName>
from cmd-line.
+KNX_scan - scan all possible devices
KNX_scan('dev-A') - scan device-A only
KNX_scan('dev-A,dev-B,dev-C') - scan device-A, device-B, device-C
KNX_scan('room=Kueche') - scan all KNX-devices in room Kueche
{KNX_scan('device')} - syntax when used from FHEM-cmdline