mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-02-24 21:34:51 +00:00
10_KNX.pm: new attr. readingNmap (Forum #122582)
git-svn-id: https://svn.fhem.de/fhem/trunk@29680 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
3d0aa04c3d
commit
7b31e22d53
@ -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: new attribute readingNmap
|
||||
- feature: 76_SolarForecast: max. possible inverters set to 4
|
||||
- feature: 98_vitoconnect.pm: enhanced error mapping
|
||||
now also language dependent
|
||||
|
@ -82,9 +82,10 @@
|
||||
# 25/04/2024 changed _open for mode S
|
||||
# replaced/removed experimental given/when
|
||||
# 19/08/2024 fix error-msg when mode S fails to open
|
||||
# xx/11/2024 replace getimeofday w. Time::HiRes::time
|
||||
# 07/11/2024 replace getimeofday w. Time::HiRes::time
|
||||
# use AttrNum instead of AttrVal where possible
|
||||
# PBP remove postfix if
|
||||
# xx/01/2025 add a few responseIDs in readH fn.
|
||||
|
||||
|
||||
package KNXIO; ## no critic 'package'
|
||||
@ -415,7 +416,8 @@ sub KNXIO_ReadH {
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
if ( unpack('n',$buf) != 0x0610) {
|
||||
my ($header, $responseID, $total_length) = unpack('nnn',$buf);
|
||||
if ($header != 0x0610) {
|
||||
KNXIO_Log ($name, 3, 'invalid Frame Header received - discarded');
|
||||
return;
|
||||
}
|
||||
@ -425,23 +427,34 @@ sub KNXIO_ReadH {
|
||||
my $rxseqcntr = 0;
|
||||
my $txseqcntr = 0;
|
||||
my $errcode = 0;
|
||||
my $responseID = unpack('x2n',$buf);
|
||||
|
||||
my %resIDs = (
|
||||
0x0201 => sub { # search request
|
||||
KNXIO_Log ($name, 4, 'SearchRequest received');
|
||||
return;
|
||||
},
|
||||
0x0202 => sub { # Search response
|
||||
KNXIO_Log ($name, 4, 'SearchResponse received');
|
||||
my (@contolpointIp, $controlpointPort) = unpack('x6CCCn',$buf);
|
||||
return;
|
||||
},
|
||||
0x0203 => sub { # Decription request
|
||||
KNXIO_Log ($name, 4, 'DescriptionRequest received');
|
||||
return;
|
||||
},
|
||||
0x0204 => sub { # Decription response
|
||||
KNXIO_Log ($name, 4, 'DescriptionResponse received');
|
||||
return;
|
||||
},
|
||||
0x0206 => sub { # Connection response
|
||||
0x0205 => sub { # Connect request
|
||||
KNXIO_Log ($name, 4, 'ConnectionRequest received');
|
||||
return;
|
||||
},
|
||||
0x0206 => sub { # Connect response
|
||||
($hash->{KNXIOhelper}->{CCID},$errcode) = unpack('x6CC',$buf); # save Comm Channel ID,errcode
|
||||
RemoveInternalTimer($hash,\&KNXIO_keepAlive);
|
||||
if ($errcode > 0) {
|
||||
KNXIO_Log ($name, 3, q{ConnectionResponse received } .
|
||||
KNXIO_Log ($name, 3, q{ConnectResponse received } .
|
||||
qq{CCID= $hash->{KNXIOhelper}->{CCID} Status=} . KNXIO_errCodes($errcode));
|
||||
KNXIO_disconnect($hash,2);
|
||||
return;
|
||||
@ -454,6 +467,10 @@ sub KNXIO_ReadH {
|
||||
InternalTimer(Time::HiRes::time() + 60, \&KNXIO_keepAlive, $hash); # start keepalive
|
||||
return;
|
||||
},
|
||||
0x0207 => sub { # ConnectionState request
|
||||
KNXIO_Log ($name, 4, 'ConnectioStatenRequest received');
|
||||
return;
|
||||
},
|
||||
0x0208 => sub { # ConnectionState response
|
||||
($hash->{KNXIOhelper}->{CCID}, $errcode) = unpack('x6CC',$buf);
|
||||
RemoveInternalTimer($hash,\&KNXIO_keepAlive);
|
||||
@ -1047,8 +1064,8 @@ sub KNXIO_dispatch2 {
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
$hash->{'msg_count'}++;
|
||||
$hash->{'msg_time'} = TimeNow();
|
||||
$hash->{'MSGCNT'}++;
|
||||
$hash->{'MSGTIME'} = TimeNow();
|
||||
|
||||
Dispatch($hash, $buf);
|
||||
|
||||
@ -1132,8 +1149,8 @@ sub KNXIO_closeDev {
|
||||
|
||||
delete $hash->{stacktrace}; # clean
|
||||
delete $hash->{nextOpenDelay};
|
||||
delete $hash->{'msg_count'};
|
||||
delete $hash->{'msg_time'};
|
||||
delete $hash->{'MSGCNT'};
|
||||
delete $hash->{'MSGTIME'};
|
||||
|
||||
#NO! delete $hash->{'.CCID'};
|
||||
delete $hash->{KNXIOhelper}->{SEQUENCECNTR};
|
||||
@ -1479,7 +1496,8 @@ __END__
|
||||
<li><b>S</b> Socket mode - communicate via KNXD's UNIX-socket on localhost. default Socket-path:
|
||||
<code>/var/run/knx</code><br/>
|
||||
Path might be different, depending on knxd-version or -config specification!
|
||||
This mode is tested ok with KNXD version 0.14.30. It does NOT work with ver. 0.10.0!
|
||||
This mode is tested ok with KNXD version 0.14.30. It does NOT work with ver. 0.10.0!</br>
|
||||
If you run FHEM and KNXD in Docker, this is the preferred mode to run both in network-mode bridge - see wiki!.
|
||||
</li>
|
||||
<li><b>X</b> Special mode - for details see KNXIO-wiki!
|
||||
</li>
|
||||
|
@ -193,8 +193,10 @@
|
||||
# dpt18 "learn" now implemented for dpt18.001, experimental dpt18.099 removed
|
||||
# PBP: replace unless w. if not..., fix postfix if
|
||||
# cmd-ref update
|
||||
# MH 20250201 fix dpt16 dblog-split fn
|
||||
# feature: new attr readingNmap - modify readingnames on the fly
|
||||
# internal code change: _define2, _attr, _set, _encodeByDpt
|
||||
#
|
||||
# postponed new attr readingRegex - allow manipulating reading-names on the fly
|
||||
# todo-4/2024 remove support for oldsyntax cmd's: raw,value,string,rgb
|
||||
|
||||
|
||||
@ -241,7 +243,6 @@ BEGIN {
|
||||
AnalyzePerlCommand AnalyzeCommandChain EvalSpecials
|
||||
fhemTimeLocal)
|
||||
);
|
||||
# addToDevAttrList # removed 11/2024
|
||||
}
|
||||
|
||||
#string constants
|
||||
@ -546,9 +547,9 @@ my %dpttypes = (
|
||||
'dpt17.001' => {CODE=>'dpt17', UNIT=>q{}, PATTERN=>qr/[+]?\d{1,2}/xms, MIN=>0, MAX=>63},
|
||||
|
||||
# Scene, 1-64
|
||||
'dpt18' => {CODE=>'dpt18', UNIT=>q{}, PATTERN=>qr/[+]?\d{1,2}/xms, OFFSET=>1, MIN=>1, MAX=>64,
|
||||
'dpt18' => {CODE=>'dpt18', UNIT=>q{}, PATTERN=>qr/(activate[,]|learn[,])?[+]?\d{1,2}/xms, OFFSET=>1, MIN=>1, MAX=>64,
|
||||
DEC=>\&dec_dpt18,ENC=>\&enc_dpt18,},
|
||||
'dpt18.001' => {CODE=>'dpt18', UNIT=>q{}, PATTERN=>qr/(activate|learn)?[,]?[+]?\d{1,2}/xms, OFFSET=>1, MIN=>1, MAX=>64, SETLIST=>'widgetList,3,select,activate,learn,1,textField'},
|
||||
'dpt18.001' => {CODE=>'dpt18', UNIT=>q{}, PATTERN=>qr/(activate[,]|learn[,])?[+]?\d{1,2}/xms, OFFSET=>1, MIN=>1, MAX=>64, SETLIST=>'widgetList,3,select,activate,learn,1,textField'},
|
||||
|
||||
#date and time
|
||||
'dpt19' => {CODE=>'dpt19', UNIT=>q{}, PATTERN=>qr/($PAT_DATE$PAT_DTSEP$PAT_TIME|now)/ixms, MIN=>undef, MAX=>undef,
|
||||
@ -616,7 +617,7 @@ sub Initialize {
|
||||
'showtime:0,1 ' . #show event-time instead of value in device overview
|
||||
'stateRegex:textField-long ' . #modifies state value
|
||||
'stateCmd:textField-long ' . #modify state value
|
||||
#not yet 'readingRegex:textField-long ' . #modify reading name
|
||||
'readingNmap:textField-long ' . #modify reading names
|
||||
'putCmd:textField-long ' . #enable FHEM to answer KNX read telegrams
|
||||
'format ' . #supplies post-string
|
||||
'KNX_toggle:textField ' . #toggle source <device>:<reading>
|
||||
@ -684,8 +685,7 @@ sub KNX_Define2 {
|
||||
foreach my $gaddef (@a) {
|
||||
my $gadOption = undef;
|
||||
my $gadNoSuffix = undef;
|
||||
|
||||
my $gadName = 'g' . $gadNo; # old syntax
|
||||
my $gadName = 'g' . $gadNo; # old syntax
|
||||
|
||||
KNX_Log ($name, 5, qq{gadNr= $gadNo def-string= $gaddef});
|
||||
|
||||
@ -695,6 +695,10 @@ sub KNX_Define2 {
|
||||
push(@logarr,qq{GAD not defined or wrong format for group-number $gadNo, specify as 0-31/0-7/0-255});
|
||||
next;
|
||||
}
|
||||
elsif (defined($hash->{GADTABLE}->{$gadCode})) {
|
||||
push(@logarr,qq{GAD $gad may be supplied only once per device});
|
||||
next;
|
||||
}
|
||||
|
||||
if (! defined($gadModel)) {
|
||||
push(@logarr,qq{no model defined for group-number $gadNo});
|
||||
@ -714,6 +718,7 @@ sub KNX_Define2 {
|
||||
$hash->{model} = $dpttypes{$gadModel}->{CODE};
|
||||
}
|
||||
|
||||
# optional param
|
||||
if (scalar(@gadArgs)) {
|
||||
if ($gadArgs[-1] =~ /$PAT_GAD_SUFFIX/ixms) {$gadNoSuffix = lc(pop(@gadArgs));}
|
||||
if (@gadArgs && $gadArgs[-1] =~ /^($PAT_GAD_OPTIONS)$/ixms) {$gadOption = lc(pop(@gadArgs));}
|
||||
@ -729,29 +734,10 @@ sub KNX_Define2 {
|
||||
$gadName = q{g} . $gadNo;
|
||||
}
|
||||
|
||||
if (defined($hash->{GADTABLE}->{$gadCode})) {
|
||||
push(@logarr,qq{GAD $gad may be supplied only once per device});
|
||||
next;
|
||||
}
|
||||
|
||||
$hash->{GADTABLE}->{$gadCode} = $gadName; #add key and value to GADTABLE
|
||||
|
||||
#cache suffixes
|
||||
my ($suffixGet, $suffixSet) = qw(-get -set);
|
||||
if (defined($gadNoSuffix)) {($suffixGet, $suffixSet) = (q{},q{});}
|
||||
|
||||
# new syntax readingNames
|
||||
my $rdNameGet = $gadName . $suffixGet;
|
||||
my $rdNameSet = $gadName . $suffixSet;
|
||||
if (($gadName eq 'g' . $gadNo) && (! defined($gadNoSuffix))) { # old syntax
|
||||
$rdNameGet = 'getG' . $gadNo;
|
||||
$rdNameSet = 'setG' . $gadNo;
|
||||
}
|
||||
|
||||
if (defined ($gadOption)) {
|
||||
KNX_Log ($name, 5, qq{found GAD: $gad NAME: $gadName NO: $gadNo HEX: $gadCode DPT: $gadModel} .
|
||||
qq{ OPTION: $gadOption});
|
||||
}
|
||||
# create readingnames
|
||||
my ($rdNameSet, $rdNameGet) = KNX_makeRdNames($hash, $gadName, $gadNo, $gadNoSuffix);
|
||||
|
||||
#determine dpt-details
|
||||
my $dptDetails = $dpttypes{$gadModel};
|
||||
@ -768,7 +754,8 @@ sub KNX_Define2 {
|
||||
$setlist = q{:} . $min . q{,} . $max;
|
||||
}
|
||||
|
||||
KNX_Log ($name, 5, qq{Reading-names: $rdNameSet, $rdNameGet SetList: $setlist});
|
||||
KNX_Log ($name, 5, qq{define GAD: $gad NAME: $gadName NO: $gadNo HEX: $gadCode DPT: $gadModel} .
|
||||
qq{ RD-NAMES: $rdNameSet $rdNameGet SETLIST: $setlist} );
|
||||
|
||||
#add details to hash
|
||||
$hash->{GADDETAILS}->{$gadName} = {CODE => $gadCode, MODEL => $gadModel, NO => $gadNo, OPTION => $gadOption,
|
||||
@ -920,12 +907,8 @@ sub KNX_Set {
|
||||
return $err if defined($err);
|
||||
}
|
||||
|
||||
#Text neads special treatment - additional args may be blanked words - truncate to 14 char
|
||||
elsif ($model =~ m/^dpt16(?:[.][\d]{3})?$/xms) {
|
||||
if (scalar(@arg) > 0) {$value .= q{ } . join (q{ }, @arg);}
|
||||
}
|
||||
|
||||
my $transval = KNX_encodeByDpt($hash, $value, $targetGadName); #process set command
|
||||
#special treatment for dpt 10,16,18 done in KNX_encodeByDpt
|
||||
my $transval = KNX_encodeByDpt($hash, $targetGadName, $value, @arg); #process set command
|
||||
return qq{"set $name $targetGadName $value" failed, see Log-Messages} if (!defined($transval)); # encodeByDpt failed
|
||||
|
||||
IOWrite($hash, $KNXID, 'w' . $groupCode . $transval);
|
||||
@ -933,7 +916,9 @@ sub KNX_Set {
|
||||
KNX_Log ($name, 5, qq{cmd= $cmd , value= $value , translated= $transval});
|
||||
|
||||
# decode again for values that have been changed in encode process
|
||||
if ($model =~ m/^dpt(?:3|10|11|16|18|19)(?:[.][\d]{3})?$/xms) {$value = KNX_decodeByDpt($hash, $transval, $targetGadName);}
|
||||
if ($model =~ m/^dpt(?:3|10|11|16|18|19)(?:[.][\d]{3})?$/xms) {
|
||||
$value = KNX_decodeByDpt($hash, $targetGadName, $transval);
|
||||
}
|
||||
|
||||
#apply post processing for state and set all readings
|
||||
KNX_SetReadings($hash, $targetGadName, $value, undef, undef);
|
||||
@ -975,6 +960,7 @@ sub KNX_Set_oldsyntax {
|
||||
# pass thru -for-timer,-until,blink cmds...
|
||||
return (undef, $targetGadName, $cmd, @arg) if ($cmd =~ m/(?:-till|-until|-till-overnight|-for-timer|$BLINK)$/ixms);
|
||||
|
||||
## prepare for deletion of deprecated cmds
|
||||
my $code = $hash->{GADDETAILS}->{$targetGadName}->{MODEL};
|
||||
my $value = shift(@arg);
|
||||
my $rtxt = qq{"set $name $cmd $value }; # log text
|
||||
@ -1000,10 +986,14 @@ sub KNX_Set_oldsyntax {
|
||||
return q{invalid cmd: } . $rtxt . join(q{ },@arg) . q{" issued - ignored};
|
||||
}
|
||||
|
||||
KNX_Log ($name, 3, q{This cmd will be deprecated by 1/2024: } . $rtxt . join(q{ },@arg) .
|
||||
KNX_Log ($name, 2, q{This cmd is deprecated since 1/2024: } . $rtxt . join(q{ },@arg) .
|
||||
qq{" - use: "set $name $targetGadName $value } . join(q{ },@arg) . q{"} );
|
||||
|
||||
return (undef, $targetGadName, $value, @arg);
|
||||
## prepare for deletion of deprecated cmds
|
||||
## my $rtxt = q{invalid cmd: "set } . qq{$name $cmd $value } . join(q{ },@arg) . q{" issued - ignored};
|
||||
## KNX_Log ($name, 2, $rtxt);
|
||||
## return $rtxt;
|
||||
}
|
||||
|
||||
# process special dpt1, dpt1.001 set
|
||||
@ -1060,7 +1050,6 @@ sub KNX_Set_dpt1 {
|
||||
my $hms_til = ::FmtDateTime($endTS) =~ s/[\s]/T/rxms; # fhem.pl alternativ use full datespec
|
||||
$hash->{".TIMER_$groupCode"} = $hms_til; #create local marker
|
||||
#place at-command for switching on / off
|
||||
# CommandDefMod(undef, '-temporary ' . $name . qq{_TIMER_$groupCode at $hms_til set $name $targetGadName $tvalue});
|
||||
CommandDefMod(undef, '-silent ' . $name . qq{_TIMER_$groupCode at $hms_til set $name $targetGadName $tvalue});
|
||||
return (undef,$value);
|
||||
}
|
||||
@ -1131,12 +1120,14 @@ sub KNX_State {
|
||||
sub KNX_Attr {
|
||||
my ($cmd,$name,$aName,$aVal) = @_;
|
||||
|
||||
my $hash = $defs{$name};
|
||||
return KNX_Attr_readingNmap($cmd, $name, $aVal) if ($aName eq q{readingNmap});
|
||||
|
||||
my $hash = $defs{$name};
|
||||
my $value = undef;
|
||||
|
||||
if ($cmd eq 'set' && $init_done) {
|
||||
# check valid IODev
|
||||
if ($aName eq 'IODev') { return KNX_chkIODev($hash,$aVal); }
|
||||
return KNX_chkIODev($hash,$aVal) if ($aName eq 'IODev');
|
||||
|
||||
if ($aName eq 'KNX_toggle') { # validate device/reading
|
||||
my ($srcDev,$srcReading) = split(qr/:/xms,$aVal); # format: <device>:<reading>
|
||||
@ -1152,26 +1143,22 @@ sub KNX_Attr {
|
||||
return qq{syntax check failed for $aName: \n $err} if($err);
|
||||
}
|
||||
|
||||
elsif ($aName =~ /(stateRegex|readingRegex)/xms) { # test for syntax errors
|
||||
# elsif ($aName eq 'stateRegex') { # test for syntax errors
|
||||
elsif ($aName eq 'stateRegex') { # test for syntax errors
|
||||
my @aValS = split(/\s+|\//xms,$aVal);
|
||||
foreach (@aValS) {
|
||||
next if ($_ eq q{} );
|
||||
my $err = main::CheckRegexp($_,'Attr ' . $aName); # fhem.pl
|
||||
# my $err = main::CheckRegexp($_,'Attr stateRegex'); # fhem.pl
|
||||
return qq{syntax check failed for $aName: \n $err} if (defined($err));
|
||||
}
|
||||
}
|
||||
} # /set
|
||||
|
||||
if ($cmd eq 'del') {
|
||||
elsif ($cmd eq 'del') {
|
||||
if ($aName eq 'disable') {
|
||||
my @defentries = split(/\s/ixms,$hash->{DEF});
|
||||
my @defentries = split(/[\s\t\n]+/xms,$hash->{DEF});
|
||||
foreach my $def (@defentries) { # check all entries
|
||||
next if ($def eq ReadingsVal($name,'IODev',undef)); # deprecated IOdev
|
||||
# next if ($def eq ReadingsVal($name,'IODev',undef)); # deprecated IOdev
|
||||
next if ($def =~ /:dpt(?:[\d+]|RAW)/xms);
|
||||
|
||||
KNX_Log ($name, 2, q{Attribut "disable" cannot be deleted for this device until you specify a valid dpt!});
|
||||
return qq{Attribut "disable" cannot be deleted for device $name until you specify a valid dpt!};
|
||||
}
|
||||
delete $hash->{RAWMSG}; # debug internal
|
||||
@ -1180,14 +1167,57 @@ sub KNX_Attr {
|
||||
return;
|
||||
}
|
||||
|
||||
### special Handling Attr readingNmap
|
||||
#############################
|
||||
sub KNX_Attr_readingNmap {
|
||||
my ($cmd, $name, $aVal) = @_;
|
||||
|
||||
if(ref($cmd) eq 'HASH') { # called from timer
|
||||
($cmd, $name, $aVal) = ($cmd->{cmd}, $cmd->{name}, $cmd->{aVal});
|
||||
}
|
||||
my $hash = $defs{$name};
|
||||
|
||||
if ($cmd eq 'set' ) {
|
||||
if (! $init_done) { #postpone attr def
|
||||
my $param = {cmd=>$cmd, name=>$name, aVal=>$aVal};
|
||||
InternalTimer(Time::HiRes::time() + 3, \&KNX_Attr_readingNmap, $param);
|
||||
return;
|
||||
}
|
||||
my @aValS = split(/\s+|\//xms,$aVal);
|
||||
delete $hash->{Helper}->{RDNAMEMAP};
|
||||
foreach my $pairs (@aValS) {
|
||||
my ($gadName, $newrdName) = split(/[:]/xms,$pairs);
|
||||
if (! (defined($gadName) && (exists( $hash->{GADDETAILS}->{$gadName})))) {
|
||||
return qq{invalid first parameter $gadName, use a valid <gadName>};
|
||||
}
|
||||
next if (! defined($newrdName));
|
||||
#delete affected readings
|
||||
::readingsDelete($hash, $hash->{GADDETAILS}->{$gadName}->{RDNAMESET}); # fhem.pl
|
||||
::readingsDelete($hash, $hash->{GADDETAILS}->{$gadName}->{RDNAMEGET}); # fhem.pl
|
||||
$hash->{Helper}->{RDNAMEMAP}->{$gadName} = $newrdName;
|
||||
}
|
||||
}
|
||||
|
||||
elsif ($cmd eq 'del') { # delete mapped readings
|
||||
foreach my $gadName (keys %{$hash->{Helper}->{RDNAMEMAP}}) {
|
||||
::readingsDelete($hash, $hash->{GADDETAILS}->{$gadName}->{RDNAMESET}); # fhem.pl
|
||||
::readingsDelete($hash, $hash->{GADDETAILS}->{$gadName}->{RDNAMEGET}); # fhem.pl
|
||||
}
|
||||
delete $hash->{Helper}->{RDNAMEMAP};
|
||||
}
|
||||
|
||||
CommandDefMod(undef, '-silent ' . $name . ' KNX ' . $hash->{DEF});
|
||||
return;
|
||||
}
|
||||
|
||||
#Split reading for DBLOG
|
||||
#############################
|
||||
sub KNX_DbLog_split {
|
||||
my $event = shift;
|
||||
my $device = shift;
|
||||
|
||||
my $reading = 'state'; # default
|
||||
my $unit = q{}; # default
|
||||
my $reading = q{state}; # default
|
||||
my $unit = q{}; # default
|
||||
my $dpt16flag = 0; # is it a dpt16 ?
|
||||
|
||||
# split event into pieces
|
||||
@ -1202,17 +1232,21 @@ sub KNX_DbLog_split {
|
||||
#numeric value? and last value non numeric? - assume unit - except for dpt16
|
||||
my $devhash = $defs{$device};
|
||||
foreach my $key (keys %{$devhash->{GADDETAILS}}) {
|
||||
next if ($key ne $reading);
|
||||
next if ($devhash->{GADDETAILS}->{$key}->{MODEL} !~ /^dpt16/xms);
|
||||
$dpt16flag = 1;
|
||||
next if ($reading ne q{state} &&
|
||||
$reading ne $devhash->{GADDETAILS}->{$key}->{RDNAMESET} &&
|
||||
$reading ne $devhash->{GADDETAILS}->{$key}->{RDNAMEGET});
|
||||
$dpt16flag = 1;
|
||||
last;
|
||||
}
|
||||
if (($dpt16flag == 0) && Scalar::Util::looks_like_number($strings[0]) && (! Scalar::Util::looks_like_number($strings[scalar(@strings)-1]))) {
|
||||
if (($dpt16flag == 0) &&
|
||||
Scalar::Util::looks_like_number($strings[0]) &&
|
||||
(! Scalar::Util::looks_like_number($strings[scalar(@strings)-1]))) {
|
||||
$unit = pop(@strings);
|
||||
}
|
||||
|
||||
my $value = join(q{ },@strings);
|
||||
if (!defined($unit)) {$unit = q{};}
|
||||
# if (!defined($unit)) {$unit = q{};}
|
||||
|
||||
KNX_Log ($device, 5, qq{EVENT= $event READING= $reading VALUE= $value UNIT= $unit});
|
||||
return ($reading, $value, $unit);
|
||||
@ -1253,7 +1287,6 @@ sub KNX_Parse {
|
||||
my $gadName = $deviceHash->{GADTABLE}->{$gadCode};
|
||||
|
||||
if (IsDevice($deviceName)) { push(@foundMsgs, $deviceName);} # save to list even if dev is disabled - dev must exist!
|
||||
# push(@foundMsgs, $deviceName); # save to list even if dev is disabled
|
||||
|
||||
if (IsDisabled($deviceName) == 1) {
|
||||
$deviceHash->{RAWMSG} = qq{gadName=$gadName cmd=$cmd, hexvalue=$val}; # for debugging
|
||||
@ -1262,8 +1295,7 @@ sub KNX_Parse {
|
||||
|
||||
=begin comment
|
||||
# ignore input from "wrong" IO-dev
|
||||
my $IODevAttr = AttrVal($deviceName,'IODev',$ioName);
|
||||
if ($IODevAttr ne $ioName) {
|
||||
if (AttrVal($deviceName,'IODev',$ioName) ne $ioName) {
|
||||
KNX_Log ($deviceName, 2, qq{msg for gad-name: $gadName from wrong IO-device: $ioName - ignored});
|
||||
next;
|
||||
}
|
||||
@ -1294,7 +1326,7 @@ sub KNX_Parse {
|
||||
#handle GroupValueWrite and GroupValueResponse messages
|
||||
if ($cmd =~ /[w|p]/ixms) {
|
||||
#decode message
|
||||
my $transval = KNX_decodeByDpt ($deviceHash, $val, $gadName);
|
||||
my $transval = KNX_decodeByDpt ($deviceHash, $gadName, $val);
|
||||
#message invalid
|
||||
if (! defined($transval)) {
|
||||
KNX_Log ($deviceName, 2, qq{readingName=$getName message=$msg could not be decoded});
|
||||
@ -1344,7 +1376,7 @@ sub KNX_Parse {
|
||||
KNX_Log ($deviceName, 5, qq{replaced by Attr putCmd=$cmdAttr VALUE=$value});
|
||||
|
||||
#send transval
|
||||
my $transval = KNX_encodeByDpt($deviceHash, $value, $gadName);
|
||||
my $transval = KNX_encodeByDpt($deviceHash, $gadName, $value);
|
||||
if (defined($transval)) {
|
||||
KNX_Log ($deviceName, 4, qq{putCmd send answer: readingName=$putName value=$value sendvalue=$transval});
|
||||
readingsSingleUpdate($deviceHash, $putName, $value,0);
|
||||
@ -1409,9 +1441,6 @@ sub KNX_SetReadings {
|
||||
$rdName = $hash->{GADDETAILS}->{$gadName}->{RDNAMEGET};
|
||||
}
|
||||
|
||||
### test remapping of readings
|
||||
# $rdName = KNX_readingRegex($hash,$rdName); # modify reading name
|
||||
|
||||
#execute stateRegex
|
||||
my $state = KNX_replaceByRegex ($hash, $rdName, $value);
|
||||
|
||||
@ -1446,9 +1475,9 @@ sub doKNX_Set {
|
||||
my @param = @_;
|
||||
my ($hash, $gadName, $cmd) = ($param[0]->{hash}, $param[0]->{gadName}, $param[0]->{cmd});
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
my @arg = ();
|
||||
$arg[0] = $cmd;
|
||||
my $name = $hash->{NAME};
|
||||
my @arg = ();
|
||||
$arg[0] = $cmd;
|
||||
return KNX_Set($hash, $name, $gadName, @arg);
|
||||
}
|
||||
|
||||
@ -1587,39 +1616,34 @@ sub KNX_replaceByRegex {
|
||||
return ($retVal eq 'undefined')?undef:$retVal;
|
||||
}
|
||||
|
||||
### replace reading Name by regex
|
||||
### called from KNX_SetReadings
|
||||
### input: hash, readingname
|
||||
### return: new readingname
|
||||
sub KNX_readingRegex {
|
||||
my $hash = shift;
|
||||
my $rName = shift;
|
||||
### make and replace reading names
|
||||
### called from KNX_define2
|
||||
### input: hash, gadName, gadNo, nosuffix
|
||||
### return: rdNameset, rdNameget
|
||||
sub KNX_makeRdNames {
|
||||
my $hash = shift;
|
||||
my $gadName = shift;
|
||||
my $gadNo = shift; # // $hash->{GADDETAILS}->{$gadName}->{NO}; # sttartup?
|
||||
my $nosuffix = shift;
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
my $regAttr = AttrVal($name, 'readingRegex', undef);
|
||||
return $rName if (! defined($regAttr));
|
||||
|
||||
#get array of given attributes
|
||||
my @reg = split(/\s\//xms, $regAttr);
|
||||
my $newrName = $rName; # default if no match
|
||||
|
||||
#loop over all regex
|
||||
foreach my $regex (@reg) {
|
||||
#trim leading and trailing slashes
|
||||
$regex =~ s/^\/|\/$//gixms;
|
||||
# get pairs
|
||||
my @regPair = split(/\//xms, $regex);
|
||||
#KNX_Log ($name, 1, qq{r0=$regPair[0] r1=$regPair[1] rName=$rName});
|
||||
next if ((! defined($regPair[0])) || ($rName !~ /$regPair[0]/xms) ); # no match
|
||||
if ($regPair[0] =~ /[(].*[)]/xms) { # we have a capture group
|
||||
my $g1 = $rName =~ s/$regPair[0]/$1/rxms; # extract 1st capture group
|
||||
$regPair[1] =~ s/\$1/$g1/xms;
|
||||
}
|
||||
$newrName =~ s/$regPair[0]/$regPair[1]/xms;
|
||||
|
||||
KNX_Log ($name, 4, qq{replaced $rName with $newrName});
|
||||
my $rdNameSet = $gadName;
|
||||
if (exists ($hash->{Helper}->{RDNAMEMAP}->{$gadName})) {
|
||||
$rdNameSet = $hash->{Helper}->{RDNAMEMAP}->{$gadName};
|
||||
}
|
||||
return $newrName;
|
||||
|
||||
my $rdNameGet = $rdNameSet; # assume nosuffix
|
||||
|
||||
if (! defined($nosuffix)) {
|
||||
if ($rdNameSet eq 'g' . $gadNo) { # old syntax
|
||||
$rdNameSet = q{setG} . $gadNo;
|
||||
$rdNameGet = q{getG} . $gadNo;
|
||||
}
|
||||
else {
|
||||
$rdNameSet .= q{-set};
|
||||
$rdNameGet .= q{-get};
|
||||
}
|
||||
}
|
||||
return ($rdNameSet, $rdNameGet);
|
||||
}
|
||||
|
||||
### limit numeric values. Valid directions: encode, decode
|
||||
@ -1693,9 +1717,7 @@ sub KNX_eval {
|
||||
### encode KNX-Message according DPT
|
||||
# on return: hex string to be sent to bus / undef on error
|
||||
sub KNX_encodeByDpt {
|
||||
my $hash = shift;
|
||||
my $value = shift;
|
||||
my $gadName = shift;
|
||||
my ($hash, $gadName, $value, @arg) = @_;
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
my $model = $hash->{GADDETAILS}->{$gadName}->{MODEL};
|
||||
@ -1704,10 +1726,14 @@ sub KNX_encodeByDpt {
|
||||
return if ($model eq $MODELERR); #return unchecked, if this is a autocreate-device
|
||||
|
||||
# special handling
|
||||
if ($model !~ /^dpt16/xms) { # ...except for txt!
|
||||
if ($code eq 'dpt16') { # $arg contains additional words
|
||||
if (scalar(@arg) > 0) {$value .= q{ } . join(q{ }, @arg);}
|
||||
}
|
||||
else { # strip off whitespace and unit
|
||||
$value =~ s/^\s+|\s+$//gxms; # white space at begin/end
|
||||
$value = (split(/\s+/xms,$value))[0]; # strip off unit
|
||||
}
|
||||
|
||||
# compatibility with widgetoverride :time
|
||||
if ($code eq 'dpt10' && $value =~ /^[\d]{2}:[\d]{2}$/xms) {$value .= ':00';}
|
||||
|
||||
@ -1715,9 +1741,16 @@ sub KNX_encodeByDpt {
|
||||
my $arg1 = undef;
|
||||
my $arg2 = undef;
|
||||
if ($code eq 'dpt18') {
|
||||
($arg1, $arg2) = split(/[,]/xms,$value);
|
||||
$value = (defined($arg2))?$arg2:$arg1;
|
||||
if ($value =~ /(activate|learn)$/xms) {
|
||||
$value = shift(@arg); # blank separated
|
||||
$arg1 = $1;
|
||||
}
|
||||
else {
|
||||
($arg1, $arg2) = split(/[,]/xms,$value); # widget or just number?
|
||||
$value = (defined($arg2))?$arg2:$arg1;
|
||||
}
|
||||
}
|
||||
|
||||
# match against model pattern
|
||||
my $pattern = $dpttypes{$model}->{PATTERN};
|
||||
if ($value !~ /^$pattern$/ixms) {
|
||||
@ -1746,12 +1779,12 @@ sub KNX_encodeByDpt {
|
||||
# on return: decoded value from bus / on error: undef
|
||||
sub KNX_decodeByDpt {
|
||||
my $hash = shift;
|
||||
my $value = shift;
|
||||
my $gadName = shift;
|
||||
my $value = shift;
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
my $name = $hash->{NAME};
|
||||
my $model = $hash->{GADDETAILS}->{$gadName}->{MODEL};
|
||||
my $code = $dpttypes{$model}->{CODE};
|
||||
my $code = $dpttypes{$model}->{CODE};
|
||||
|
||||
return if ($model eq $MODELERR); #return unchecked, if this is a autocreate-device
|
||||
|
||||
@ -2417,12 +2450,12 @@ The optional parameteter <gadName> may contain an alias for the GAD. The f
|
||||
state, 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.<br/>
|
||||
If you supply <gadName> this name is used instead as cmd prefix. The reading-names are <gadName>-get and <gadName>-set.
|
||||
The synonyms <getName> and <setName> are used in this documentation. <br/>
|
||||
The synonyms <getName> and <setName> are used in this documentation.
|
||||
Reading-names may be modified by <a href="#KNX-attr-readingNmap">attribute readingNmap</a>, see examples.<br/>
|
||||
If you add the option "nosuffix", <getName> and <setName> have the identical name - <gadName>.
|
||||
Both sent and received bus messages will be stored in the same reading <gadName><br/>
|
||||
Both sent and received bus messages will be stored in the same reading <gadName>.<br/>
|
||||
If you want to restrict the GAD, use the options "get", "set", or "listenonly". The usage is described in Set/Get-cmd chapter.
|
||||
It is not possible to combine the options.<br/>
|
||||
<!-- Reading-names may be modified by <a href="#KNX-attr-readingRegex">attribute readingRegex</a>, see examples. -->
|
||||
It is not possible to combine the options.
|
||||
</p>
|
||||
<p><b>Specifying an IO-Device in define is deprecated!</b> Use attribute <a href="#KNX-attr-IODev">IODev</a> instead,
|
||||
but only if absolutely required!</p>
|
||||
@ -2525,9 +2558,9 @@ Examples:
|
||||
<li><a href="#showtime">showtime</a></li>
|
||||
<li><a href="#FHEMWEB-attr-sortby">sortby</a></li>
|
||||
<li><a href="#readingFnAttributes">stateFormat</a></li>
|
||||
<li><a href="#suppressReading">suppressReading</a></li>
|
||||
<li><a href="#readingFnAttributes">timestamp-on-change-reading</a></li>
|
||||
<li><a href="#readingFnAttributes">userReadings</a></li>
|
||||
<li><a href="#suppressReading">suppressReading</a></li>
|
||||
<li><a href="#userattr">userattr</a></li>
|
||||
<li><a href="#verbose">verbose</a></li>
|
||||
<li><a href="#FHEMWEB-attr-webCmd">webCmd</a></li>
|
||||
@ -2539,23 +2572,24 @@ Examples:
|
||||
|
||||
<li><strong>Special attributes</strong><br/>
|
||||
<ul>
|
||||
<!-- not yet
|
||||
<li><a id="KNX-attr-readingRegex"></a><b>readingRegex</b><br/>
|
||||
This function modifies the reading name (not the value!) if the first part of the regex exactly match the original reading name.
|
||||
You can pass mutiple pairs of regex-patterns and strings to replace, separated by a space.
|
||||
If no match, the "original" reading-name will get updated.
|
||||
<li><a id="KNX-attr-readingNmap"></a><b>readingNmap</b><br/>
|
||||
This function modifies the reading name (not the value!) if the first part exactly match the <b><gadName></b>,
|
||||
the new reading name will be the second part of the mapping. Parts are separated by a colon.
|
||||
You can pass mutiple pairs of mapping-patterns, separated by a space.
|
||||
If no match, the "original" reading-name will be used / updated.
|
||||
Setting/changing this Attribute deletes previously defined readings of the device.
|
||||
<pre>
|
||||
Examples:
|
||||
<code> attr <device> readingRegex /desired-(.*)-get/$1/ # use word in () as the new reading name
|
||||
attr <device> readingRegex /(position|pct)/desired-$1/ # replace "position" or "pct" by "desired-position" or "desired-pct"
|
||||
attr <device> readingRegex /.*-pos-.*/Postion/ # reading Name is now "Position".
|
||||
<code> attr <device> readingNmap desired-temp-get:WunschThemperatur # desired-temp-get -> WunschThemperatur
|
||||
attr <device> readingNmap position:desired-pos # replace "position" by "desired-pos"
|
||||
attr <device> readingNmap pct:Postion g1:Position # both gadNames store their values into reading "Position".
|
||||
</code></pre>
|
||||
This is useful for e.g. setting the actual blind-Position (as received from actor) into a reading "desired-position"
|
||||
or "desired-pct" to update a cmd-slider to the current position of the blind.<br/>
|
||||
Can also be used to make the reading-name independent from the GadName / cmd, to simplify integration of external systems.
|
||||
This attr does <b>not change</b> the value of the reading.
|
||||
Can also be used to make the reading-name independent from the gadName / cmd, to simplify integration of external systems.
|
||||
This attr does <b>not change</b> the value of the reading.<br/>
|
||||
This attr is processed ahead of other attributes, you have to use the mapped reading-name in stateRegex,stateCmd,toggle attributes.
|
||||
<br/></li>
|
||||
-->
|
||||
<li><a id="KNX-attr-stateRegex"></a><b>stateRegex</b><br/>
|
||||
This function modifies the value of reading state.
|
||||
It is executed directly after replacing the reading-names and processing "format" Attr, but before stateCmd.<br/>
|
||||
@ -2639,11 +2673,11 @@ Examples:
|
||||
<br/></li>
|
||||
|
||||
<li><a id="KNX-events"></a><strong>Events</strong><br/>
|
||||
<p>Events are generated for each reading sent or received to/from KNX-Bus unless restricted by <code>event-xxx</code> attributes
|
||||
or modified by <code>eventMap, stateRegex</code> attributes.
|
||||
<p>Events are generated for each message sent or received to/from KNX-Bus unless restricted by <code>event-xxx</code> attributes
|
||||
or modified by <code>readingNmap, eventMap, stateRegex</code> attributes.
|
||||
<br/>KNX-events have this format:</p>
|
||||
<pre> <code><device> <readingName>: <value> # reading event
|
||||
<device> <value> # state event</code></pre>
|
||||
<pre> <code><device> <readingName>: <value> [<unit>] # reading event, unit optional
|
||||
<device> <value> [<unit>] # state event</code></pre>
|
||||
</li>
|
||||
|
||||
<li><a id="KNX-dpt"></a><strong>DPT - data-point-types</strong><br/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user