mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-09 20:57:11 +00:00
10_KNX.pm: multiple bugfixes & changes, (Forum #122582)
git-svn-id: https://svn.fhem.de/fhem/trunk@26910 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
5f3523098e
commit
c210296dff
@ -1,5 +1,7 @@
|
||||
# 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: 00_KNXIO: minor changes, Forum #127792
|
||||
- change: 10_KNX: multiple changes, Forum #122582
|
||||
- change: 93_DbLog: roll out version 5.5.7, NOTE: Please read Forum
|
||||
https://forum.fhem.de/index.php/topic,130743.0.html
|
||||
for information about this version
|
||||
|
@ -1,4 +1,4 @@
|
||||
##############################################
|
||||
## no critic (Modules::RequireVersionVar) ######################################
|
||||
# $Id$
|
||||
# base module for KNX-communication
|
||||
# idea: merge some functions of TUL- & KNXTUL-module into one and add more connectivity
|
||||
@ -13,8 +13,7 @@
|
||||
# FIFO - queing of incoming messages (less latency for fhem-system) read= ~4ms vs ~34ms with KNXTUL/TUL
|
||||
# discard duplicate incoming messages
|
||||
# more robust parser of incoming messages
|
||||
#
|
||||
##############################################
|
||||
################################################################################
|
||||
### changelog:
|
||||
# 19/10/2021 01.60 initial beta version
|
||||
# enable hostnames for mode H & T
|
||||
@ -37,11 +36,12 @@
|
||||
# unify Log msgs
|
||||
# 13/11/2022 modify fifo logic
|
||||
# improve cmd-ref
|
||||
# xx/12/2022 change parameter parsing in define
|
||||
# 05/12/2022 change parameter parsing in define
|
||||
# add renameFn - correct reading & attr IODev in KNX-devices after rename of KNXIO-device
|
||||
# change disabled handling
|
||||
# fix src-addr for Mode M,H
|
||||
# change internal PhyAddr to reabable format + range checking on define.
|
||||
# 19/12/2022 cleanup
|
||||
|
||||
|
||||
package KNXIO; ## no critic 'package'
|
||||
@ -57,7 +57,7 @@ use HttpUtils;
|
||||
use GPUtils qw(GP_Import GP_Export); # Package Helper Fn
|
||||
|
||||
### perlcritic parameters
|
||||
# these ones are NOT used! (constants,Policy::Modules::RequireFilenameMatchesPackage,Modules::RequireVersionVar,NamingConventions::Capitalization)
|
||||
# these ones are NOT used! (constants,Policy::Modules::RequireFilenameMatchesPackage,NamingConventions::Capitalization)
|
||||
### the following percritic items will be ignored global ###
|
||||
## no critic (ValuesAndExpressions::RequireNumberSeparators,ValuesAndExpressions::ProhibitMagicNumbers)
|
||||
## no critic (RegularExpressions::RequireDotMatchAnything,RegularExpressions::RequireLineBoundaryMatching)
|
||||
@ -142,16 +142,12 @@ sub KNXIO_Define {
|
||||
return InternalTimer(gettimeofday() + 0.2,\&KNXIO_openDev,$hash) if ($mode eq q{X});
|
||||
|
||||
return q{KNXIO-define syntax: "define <name> KNXIO <H|M|T> <ip-address|hostname>:<port> <phy-adress>" } . "\n" .
|
||||
q{ or "define <name> KNXIO S <pathToUnixSocket> <phy-adress>" } if (scalar(@arg) < 5);
|
||||
q{ or "define <name> KNXIO S <pathToUnixSocket> <phy-address>" } if (scalar(@arg) < 5);
|
||||
|
||||
my ($host,$port) = split(/[:]/ix,$arg[3]);
|
||||
|
||||
return q{KNXIO-define: invalid ip-address or port, correct syntax is: } .
|
||||
q{"define <name> KNXIO <H|M|T> <ip-address|name>:<port> <phy-adress>"} if ($mode =~ /[MHT]/ix && $port !~ /$PAT_PORT/ix);
|
||||
|
||||
if (exists($hash->{OLDDEF})) { # modify definition....
|
||||
KNXIO_closeDev($hash);
|
||||
}
|
||||
q{"define <name> KNXIO <H|M|T> <ip-address|name>:<port> <phy-address>"} if ($mode =~ /[MHT]/ix && $port !~ /$PAT_PORT/ix);
|
||||
|
||||
if ($mode eq q{M}) { # multicast
|
||||
my $host1 = (split(/\./ix,$host))[0];
|
||||
@ -183,12 +179,11 @@ sub KNXIO_Define {
|
||||
}
|
||||
}
|
||||
|
||||
my $phyaddr = (defined($arg[4]))?$arg[4]:'0.0.0';
|
||||
# $hash->{PhyAddr} = sprintf('%05x',KNXIO_hex2addr($phyaddr));
|
||||
my $phyaddr = (defined($arg[4]))?$arg[4]:'0.0.0';
|
||||
my $phytemp = KNXIO_hex2addr($phyaddr);
|
||||
$hash->{PhyAddr} = KNXIO_addr2hex($phytemp,2); #convert 2 times for correcting input!
|
||||
|
||||
KNXIO_closeDev($hash) if ($init_done);
|
||||
KNXIO_closeDev($hash) if ($init_done || exists($hash->{OLDDEF})); # modify definition....
|
||||
|
||||
$hash->{PARTIAL} = q{};
|
||||
# define helpers
|
||||
@ -215,8 +210,6 @@ sub KNXIO_Attr {
|
||||
if ($cmd eq 'set' && defined($aVal) && $aVal == 1) {
|
||||
KNXIO_closeDev($hash);
|
||||
} else {
|
||||
### should work w. KNXIO_Open
|
||||
# CommandModify(undef, qq{-silent $name $hash->{DEF}}); # do a defmod ...
|
||||
InternalTimer(gettimeofday() + 0.2,\&KNXIO_openDev,$hash);
|
||||
}
|
||||
}
|
||||
@ -430,7 +423,6 @@ sub KNXIO_ReadH {
|
||||
}
|
||||
my $phyaddr = unpack('x18n',$buf);
|
||||
$hash->{PhyAddr} = KNXIO_addr2hex($phyaddr,2); # correct Phyaddr.
|
||||
# $hash->{PhyAddr} = sprintf('%05x',$phyaddr); # correct Phyaddr.
|
||||
readingsSingleUpdate($hash, 'state', 'connected', 1);
|
||||
Log3 ($name, 3, qq{KNXIO ($name) connected});
|
||||
InternalTimer(gettimeofday() + 60, \&KNXIO_keepAlive, $hash); # start keepalive
|
||||
@ -528,14 +520,12 @@ sub KNXIO_Write {
|
||||
}
|
||||
elsif ($mode eq 'M') {
|
||||
$completemsg = pack('nnnnnnnCCC*',0x0610,0x0530,$datasize + 16,0x2900,0xBCE0,$src,$dst,$datasize,0,@data); # use src addr
|
||||
# $completemsg = pack('nnnnnnnCCC*',0x0610,0x0530,$datasize + 16,0x2900,0xBCE0,0,$dst,$datasize,0,@data);
|
||||
$ret = TcpServer_MCastSend($hash,$completemsg);
|
||||
}
|
||||
else { # $mode eq 'H'
|
||||
# total length= $size+20 - include 2900BCEO,src,dst,size,0
|
||||
$completemsg = pack('nnnCC',0x0610,0x0420,$datasize + 20,4,$hash->{KNXIOhelper}->{CCID}) .
|
||||
pack('CCnnnnCCC*',$hash->{KNXIOhelper}->{SEQUENCECNTR_W},0,0x1100,0xBCE0,$src,$dst,$datasize,0,@data); # send TunnelInd
|
||||
# pack('CCnnnnCCC*',$hash->{KNXIOhelper}->{SEQUENCECNTR_W},0,0x1100,0xBCE0,0,$dst,$datasize,0,@data); # send TunnelInd
|
||||
|
||||
# Timeout function - expect TunnelAck within 1 sec! - but if fhem has a delay....
|
||||
$hash->{KNXIOhelper}->{LASTSENTMSG} = $completemsg; # save msg for resend in case of TO
|
||||
|
@ -1,5 +1,7 @@
|
||||
##############################################
|
||||
## no critic (Modules::RequireVersionVar) ######################################
|
||||
# $Id$
|
||||
################################################################################
|
||||
### changelog:
|
||||
# ABU 20180218 restructuring, removed older documentation
|
||||
# MH 20210908 deleted part of change history
|
||||
# ABU 20181007 fixed dpt19
|
||||
@ -100,6 +102,12 @@
|
||||
# MH 202212xx fix dpt217 range/fomatting, cmd-ref links,
|
||||
# remove support for IODev in define
|
||||
# modify disabled logic
|
||||
# MH 20221226 device define after init_complete
|
||||
# remove $hash->{DEVNAME}
|
||||
# modify autocreate, get/set logic
|
||||
# changed not user relevant internals to {.XXXX}
|
||||
# changed DbLog_split function
|
||||
# disabled StateFn
|
||||
|
||||
|
||||
package KNX; ## no critic 'package'
|
||||
@ -112,7 +120,7 @@ use Scalar::Util qw(looks_like_number);
|
||||
use GPUtils qw(GP_Import GP_Export); # Package Helper Fn
|
||||
|
||||
### perlcritic parameters
|
||||
# these ones are NOT used! (constants,Policy::Modules::RequireFilenameMatchesPackage,Modules::RequireVersionVar,NamingConventions::Capitalization)
|
||||
# these ones are NOT used! (constants,Policy::Modules::RequireFilenameMatchesPackage,NamingConventions::Capitalization)
|
||||
# these ones are NOT used! (ControlStructures::ProhibitCascadingIfElse)
|
||||
### the following percritic items will be ignored global ###
|
||||
## no critic (ValuesAndExpressions::RequireNumberSeparators,ValuesAndExpressions::ProhibitMagicNumbers)
|
||||
@ -144,10 +152,8 @@ BEGIN {
|
||||
fhemTimeLocal)
|
||||
);
|
||||
}
|
||||
# export to main context
|
||||
GP_Export(
|
||||
qw(Initialize)
|
||||
);
|
||||
# export to main context
|
||||
GP_Export( qw(Initialize) );
|
||||
|
||||
#string constants
|
||||
my $MODELERR = 'MODEL_NOT_DEFINED'; # for autocreate
|
||||
@ -427,7 +433,7 @@ sub KNX_Define {
|
||||
$hash->{NAME} = $name;
|
||||
|
||||
$svnid =~ s/.*\.pm\s(.+)Z.*/$1/ix;
|
||||
$hash->{SVN} = $svnid; # store svn info in dev hash
|
||||
$hash->{'.SVN'} = $svnid; # store svn info in dev hash
|
||||
|
||||
my $logtxt = qq{KNX_define ($name): }; # leading txt
|
||||
|
||||
@ -442,11 +448,30 @@ sub KNX_Define {
|
||||
if ( $a[int(@a) - 1] !~ m/^(?:$PAT_GAD|$PAT_GAD_HEX)/ix ) {
|
||||
my $iodevCandidate = pop(@a); # remove from array, but do nothing with it!
|
||||
my $logtxtIO = qq{$logtxt specifying IODev $iodevCandidate is deprecated in define } .
|
||||
qq{- use "attr $name IODev $iodevCandidate"};
|
||||
qq{- use "attr $name IODev $iodevCandidate"};
|
||||
Log3 ($name, 2, $logtxtIO);
|
||||
return $logtxtIO if ($init_done); # allow durin start
|
||||
}
|
||||
|
||||
### new wait for initdone...
|
||||
$hash->{'.DEFLINE'} = join(q{ },@a); # temp store defs for define2...
|
||||
return InternalTimer(gettimeofday() + 5.0,\&KNX_Define2,$hash) if (! $init_done);
|
||||
return KNX_Define2($hash);
|
||||
}
|
||||
|
||||
### continue define 5 sec after init complete!
|
||||
sub KNX_Define2 {
|
||||
my $hash = shift // return;
|
||||
|
||||
my $name = $hash->{NAME};
|
||||
my $def = $hash->{'.DEFLINE'};
|
||||
delete $hash->{'.DEFLINE'};
|
||||
my @a = split(/\s+/x, $def);
|
||||
RemoveInternalTimer($hash);
|
||||
|
||||
my $logtxt = qq{KNX_define2 ($name): }; # leading txt
|
||||
### end new
|
||||
|
||||
AssignIoPort($hash); # AssignIoPort will take device from $attr{$name}{IODev} if defined
|
||||
|
||||
#reset
|
||||
@ -454,7 +479,7 @@ sub KNX_Define {
|
||||
$hash->{GADTABLE} = {};
|
||||
|
||||
#delete all defptr entries for this device (defmod & copy problem)
|
||||
KNX_delete_defptr($hash) if ($init_done); # verify with: {PrintHash($modules{KNX}->{defptr},3) }
|
||||
KNX_delete_defptr($hash); # verify with: {PrintHash($modules{KNX}->{defptr},3) }
|
||||
|
||||
#create groups and models, iterate through all possible args
|
||||
foreach my $i (2 .. $#a) {
|
||||
@ -478,7 +503,8 @@ sub KNX_Define {
|
||||
return ($logtxt . qq{no model defined for group-number $gadNo}) if(! defined($gadModel));
|
||||
|
||||
if ($gadModel eq $MODELERR) { #within autocreate no model is supplied - throw warning
|
||||
Log3 ($name, 3, $logtxt . 'autocreate device will be disabled, correct def with valid dpt and enable device') if ($init_done);
|
||||
Log3 ($name, 3, $logtxt . 'autocreate device will be disabled, correct def with valid dpt and enable device');
|
||||
$attr{$name}->{disable} = 1 if (AttrVal($name,'disable',0) != 1);
|
||||
}
|
||||
elsif (!defined($dpttypes{$gadModel})) { #check model-type
|
||||
return $logtxt . qq{invalid model: $gadModel for group-number $gadNo} .
|
||||
@ -505,9 +531,6 @@ sub KNX_Define {
|
||||
return ($logtxt . qq{invalid suffix for group-number $gadNo. Use $PAT_GAD_SUFFIX}) if (defined($gadNoSuffix) && ($gadNoSuffix !~ m/$PAT_GAD_SUFFIX/ix));
|
||||
}
|
||||
|
||||
#save 1st gadName for later backwardCompatibility
|
||||
$hash->{FIRSTGADNAME} = $gadName if ($gadNo == 1);
|
||||
|
||||
###GADTABLE
|
||||
#create a hash with gadCode and gadName for later mapping
|
||||
my $tableHashRef = $hash->{GADTABLE};
|
||||
@ -545,7 +568,7 @@ sub KNX_Define {
|
||||
if (defined ($dptDetails->{SETLIST})) { # list is given, pass it through
|
||||
$setlist = q{:} . $dptDetails->{SETLIST};
|
||||
}
|
||||
elsif (defined ($dptDetails->{MIN}) and looks_like_number($dptDetails->{MIN})) { #number? - place slider
|
||||
elsif (defined ($dptDetails->{MIN}) && looks_like_number($dptDetails->{MIN})) { #number? - place slider
|
||||
my $min = $dptDetails->{MIN};
|
||||
my $max = $dptDetails->{MAX};
|
||||
my $interval = int(($max-$min)/100);
|
||||
@ -574,39 +597,21 @@ sub KNX_Define {
|
||||
#restore list
|
||||
@{$modules{KNX}->{defptr}->{$gadCode}} = @devList;
|
||||
|
||||
#create setlist/getlist for setFn / getFn
|
||||
#create setlist for setFn / getlist will be created during get-cmd!
|
||||
my $setString = q{};
|
||||
my $getString = q{};
|
||||
foreach my $key (keys %{$hash->{GADDETAILS}}) {
|
||||
#no set-command for listenonly or get / no get cmds for set
|
||||
#no set-command for get or listenonly
|
||||
my $option = $hash->{GADDETAILS}->{$key}->{OPTION};
|
||||
if (defined ($option)) {
|
||||
if ($option eq 'get') {
|
||||
$getString .= q{ } . $key . ':noArg';
|
||||
}
|
||||
elsif ($option eq 'set') {
|
||||
$setString .= ' on:noArg off:noArg' if (($hash->{GADDETAILS}->{$key}->{NO} == 1) && ($hash->{GADDETAILS}->{$key}->{MODEL} =~ /^(dpt1|dpt1.001)$/x));
|
||||
$setString .= q{ } . $key . $hash->{GADDETAILS}->{$key}->{SETLIST};
|
||||
}
|
||||
# must be listenonly, do nothing
|
||||
}
|
||||
else { # no option def, select all
|
||||
$getString .= q{ } . $key . ':noArg';
|
||||
$setString .= ' on:noArg off:noArg' if (($hash->{GADDETAILS}->{$key}->{NO} == 1) && ($hash->{GADDETAILS}->{$key}->{MODEL} =~ /^(dpt1|dpt1.001)$/x));
|
||||
$setString .= q{ } . $key . $hash->{GADDETAILS}->{$key}->{SETLIST};
|
||||
if ((defined($option) && $option eq 'set') || (! defined($option))) {
|
||||
$setString .= 'on:noArg off:noArg ' if (($hash->{GADDETAILS}->{$key}->{NO} == 1) && ($hash->{GADDETAILS}->{$key}->{MODEL} =~ /^(dpt1|dpt1.001)$/x));
|
||||
$setString .= $key . $hash->{GADDETAILS}->{$key}->{SETLIST} . q{ };
|
||||
}
|
||||
}
|
||||
$setString =~ s/^[\s?](.*)/$1/ix; # trim leading blank
|
||||
$getString =~ s/^[\s?](.*)/$1/ix;
|
||||
$hash->{SETSTRING} = $setString;
|
||||
$hash->{GETSTRING} = $getString;
|
||||
$hash->{'.SETSTRING'} = $setString;
|
||||
|
||||
Log3 ($name, 5, $logtxt . qq{getstring= $hash->{GETSTRING} , setstring= $hash->{SETSTRING}});
|
||||
Log3 ($name, 5, qq{$logtxt setstring= $hash->{'.SETSTRING'}});
|
||||
}
|
||||
|
||||
#backup name for a later rename
|
||||
$hash->{DEVNAME} = $name;
|
||||
|
||||
Log3 ($name, 5, $logtxt . 'define complete');
|
||||
return;
|
||||
}
|
||||
@ -629,45 +634,38 @@ sub KNX_Undef {
|
||||
#The answer is treated as regular telegram
|
||||
#############################
|
||||
sub KNX_Get {
|
||||
my ($hash, $name, @args) = @_;
|
||||
|
||||
#determine gadName to read - use first defined GAD if no argument is supplied
|
||||
my $gadName = $args[0] // $hash->{FIRSTGADNAME};
|
||||
my $hash = shift;
|
||||
my $name = shift;
|
||||
my $gadName = shift // KNX_gadNameByNO($hash,1); # use first defined GAD if no argument is supplied
|
||||
|
||||
return qq{KNX_Get ($name): gadName not defined} if (! defined($gadName));
|
||||
Log3 ($name, 3, qq{KNX_Get ($name): too much arguments. Only one argument allowed (gadName). Other Arguments are discarded.}) if (defined(shift));
|
||||
|
||||
#FHEM asks with a ? at startup - no action, no log - if dev is disabled: no SET/GET pulldown !
|
||||
if ($gadName =~ m/\?/x) {
|
||||
return (IsDisabled($name) == 1)?undef:qq{unknown argument choose one of $hash->{GETSTRING}};
|
||||
=pod
|
||||
### option for future use
|
||||
return if (IsDisabled($name) == 1); # no get option
|
||||
|
||||
#check for a widgetoverride that we dont want in get's...
|
||||
my @getlist = split(/\s/gix,$hash->{GETSTRING});
|
||||
my @wgoverride = split(/\s/gix,AttrVal($name,'widgetOverride',q{}));
|
||||
foreach my $wgentry (@wgoverride) {
|
||||
#tbT $wgentry =~ s/^([^:]).*/$1/gix;
|
||||
$wgentry =~ s/(.*)[:].*/$1/gix;
|
||||
@getlist = grep(! /$wgentry/, @getlist); # remove it
|
||||
my $getter = q{};
|
||||
foreach my $key (keys %{$hash->{GADDETAILS}}) {
|
||||
last if (! defined($key));
|
||||
my $option = $hash->{GADDETAILS}->{$key}->{OPTION};
|
||||
next if (defined($option) && $option =~ /(?:set|listenonly)/ix);
|
||||
$getter .= q{ } . $key . ':noArg';
|
||||
}
|
||||
return qq{unknown argument $cmd choose one of } . join(q{ },@getlist);
|
||||
=cut
|
||||
$getter =~ s/^\s+//gix; #trim leading blank
|
||||
$getter = q{} if (IsDisabled($name) == 1);
|
||||
|
||||
return qq{unknown argument $gadName choose one of $getter};
|
||||
}
|
||||
return qq{KNX_Get ($name): is disabled} if (IsDisabled($name) == 1);
|
||||
|
||||
|
||||
Log3 ($name, 5, qq{KNX_Get ($name): -enter: CMD= $gadName});
|
||||
|
||||
#no more than 1 argument allowed
|
||||
Log3 ($name, 3, qq{KNX_Get ($name): too much arguments. Only one argument allowed (gadName). Other Arguments are discarded.}) if (scalar(@args) > 1);
|
||||
|
||||
#return, if unknown group
|
||||
return qq{KNX_Get ($name): invalid gadName: $gadName} if(! exists($hash->{GADDETAILS}->{$gadName}));
|
||||
#get groupCode, groupAddress, option
|
||||
my $groupc = $hash->{GADDETAILS}->{$gadName}->{CODE};
|
||||
my $group = $hash->{GADDETAILS}->{$gadName}->{GROUP};
|
||||
my $option = $hash->{GADDETAILS}->{$gadName}->{OPTION};
|
||||
|
||||
#return, if unknown group
|
||||
return qq{KNX_Get ($name): no valid address stored for gad: $gadName} if(!$groupc);
|
||||
|
||||
#exit if get is prohibited
|
||||
return qq{KNX_Get ($name): did not request a value - "set" or "listenonly" option is defined.} if (defined ($option) and ($option =~ m/(set|listenonly)/ix));
|
||||
|
||||
@ -691,14 +689,16 @@ sub KNX_Set {
|
||||
|
||||
#FHEM asks with a "?" at startup or any reload of the device-detail-view - if dev is disabled: no SET/GET pulldown !
|
||||
if(defined($cmd) && ($cmd =~ m/\?/x)) {
|
||||
return (IsDisabled($name) == 1)?undef:qq{unknown argument $cmd choose one of $hash->{SETSTRING}};
|
||||
my $setter = exists($hash->{'.SETSTRING'})?$hash->{'.SETSTRING'}:q{};
|
||||
$setter = q{} if (IsDisabled($name) == 1);
|
||||
return qq{unknown argument $cmd choose one of $setter};
|
||||
}
|
||||
|
||||
return $thisSub . 'is disabled' if (IsDisabled($name) == 1);
|
||||
|
||||
Log3 ($name, 5, $thisSub . qq{-enter: $cmd } . join(q{ }, @arg)) if (defined ($cmd));
|
||||
|
||||
return $thisSub . 'no parameter(s) specified for set cmd' if((!defined($cmd)) || ($cmd eq q{})); #return, if no cmd specified
|
||||
|
||||
Log3 ($name, 5, $thisSub . qq{-enter: $cmd } . join(q{ }, @arg));
|
||||
|
||||
my $targetGadName = $cmd =~ s/^\s+|\s+$//girx; # gad-name or cmd (in old syntax)
|
||||
|
||||
if (defined ($hash->{GADDETAILS}->{$targetGadName})) { # #new syntax, if first arg is a valid gadName
|
||||
@ -718,14 +718,14 @@ sub KNX_Set {
|
||||
my $rdName = $hash->{GADDETAILS}->{$targetGadName}->{RDNAMESET};
|
||||
my $model = $hash->{GADDETAILS}->{$targetGadName}->{MODEL};
|
||||
|
||||
return $thisSub . q{did not set a value - "get" or "listenonly" option is defined.} if (defined ($option) and ($option =~ m/(get|listenonly)/ix));
|
||||
return $thisSub . q{did not set a value - "get" or "listenonly" option is defined.} if (defined ($option) and ($option =~ m/(?:get|listenonly)/ix));
|
||||
|
||||
my $value = $cmd; #process set command with $value as output
|
||||
#Text neads special treatment - additional args may be blanked words
|
||||
$value .= q{ } . join (q{ }, @arg) if (($model =~ m/^dpt16/ix) and (scalar (@arg) > 0));
|
||||
|
||||
#Special commands for dpt1 and dpt1.001
|
||||
if ($model =~ m/((dpt1)|(dpt1.001))$/ix) {
|
||||
if ($model =~ m/^(?:dpt1|dpt1.001)$/ix) {
|
||||
(my $err, $value) = KNX_Set_dpt1($hash, $targetGadName, $cmd, @arg);
|
||||
return $err if defined($err);
|
||||
}
|
||||
@ -741,9 +741,7 @@ sub KNX_Set {
|
||||
Log3 ($name, 4, $thisSub . qq{cmd= $cmd , value= $value , translated= $transvale});
|
||||
|
||||
# decode again for values that have been changed in encode process
|
||||
if ($model =~ m/^(dpt3|dpt10|dpt11|dpt19)/ix) {
|
||||
$transval = KNX_decodeByDpt($hash, $transvale, $targetGadName);
|
||||
}
|
||||
$transval = KNX_decodeByDpt($hash, $transvale, $targetGadName) if ($model =~ m/^(?:dpt3|dpt10|dpt11|dpt19)/ix);
|
||||
|
||||
#apply post processing for state and set all readings
|
||||
KNX_SetReadings($hash, $targetGadName, $transval, $rdName, undef);
|
||||
@ -776,12 +774,7 @@ sub KNX_Set_oldsyntax {
|
||||
return qq{an invalid gadName: $cmd was used in set-cmd};
|
||||
}
|
||||
|
||||
foreach my $key (keys %{$hash->{GADDETAILS}}) {
|
||||
if (int ($hash->{GADDETAILS}->{$key}->{NO}) == int ($groupnr)) {
|
||||
$targetGadName = $key;
|
||||
last;
|
||||
}
|
||||
}
|
||||
$targetGadName = KNX_gadNameByNO($hash, $groupnr);
|
||||
return qq{gadName not found for $groupnr} if(!defined($targetGadName));
|
||||
|
||||
# all of the following cmd's need at least 1 Argument (or more)
|
||||
@ -903,6 +896,7 @@ sub KNX_Set_dpt1 {
|
||||
#In case setstate is executed, a readingsupdate is initiated
|
||||
#############################
|
||||
sub KNX_State {
|
||||
=pod
|
||||
my $hash = shift;
|
||||
my $time = shift;
|
||||
my $reading = shift // return;
|
||||
@ -920,7 +914,7 @@ sub KNX_State {
|
||||
|
||||
#write value and update reading
|
||||
readingsSingleUpdate($hash, $reading, $value, 1);
|
||||
|
||||
=cut
|
||||
return;
|
||||
}
|
||||
|
||||
@ -946,6 +940,24 @@ sub KNX_Attr {
|
||||
elsif ($aName eq 'IODev' && $init_done) {
|
||||
return KNX_chkIODev($hash,$aVal);
|
||||
}
|
||||
=pod
|
||||
# force @set for widgetoverride
|
||||
# new function introduced 12/2022 by Rudi
|
||||
elsif ($aName eq 'widgetOverride' ) {
|
||||
my @overrides = split(/\s/ix,$aVal);
|
||||
foreach my $overr (@overrides) {
|
||||
### $overr =~ s/([^\@^\:]+)(?:@.*?)?:([^ ]+)?[\s]?/$1\@set:$2 /ixg; # add @set to each wgoverride
|
||||
$overr =~ /([^\@^\:]+)(?:\@(?:set|get|attr))?:?([^ ]+)?[\s]?/ixg;
|
||||
if (exists($hash->{GADDETAILS}->{$1}) && defined($2)) { # only for existing gadNames
|
||||
$value .= qq{$1\@set:$2 };
|
||||
}
|
||||
else {
|
||||
$value .= $overr . q{ }; # append original
|
||||
}
|
||||
}
|
||||
@_[3] = $value; # change $aVal in caller
|
||||
}
|
||||
=cut
|
||||
} # /set
|
||||
|
||||
if ($cmd eq 'del') {
|
||||
@ -968,24 +980,20 @@ sub KNX_Attr {
|
||||
#############################
|
||||
sub KNX_DbLog_split {
|
||||
my ($event, $device) = @_;
|
||||
my ($reading, $value, $unit);
|
||||
|
||||
#split input-string
|
||||
my @strings = split (q{ }, $event);
|
||||
return if (not defined ($strings[0]));
|
||||
my $unit = q{}; # default
|
||||
|
||||
#detect reading - real reading or state?
|
||||
if ($strings[0] =~ m/.*:$/x) { # real reading
|
||||
$reading = shift(@strings);
|
||||
$reading =~ s/:$//x;
|
||||
}
|
||||
else {
|
||||
# split event into reading & value
|
||||
my ($reading, $value) = split(/:\s/x, $event, 2);
|
||||
if (! defined($reading)) {
|
||||
$reading = 'state';
|
||||
}
|
||||
|
||||
#per default join all single pieces
|
||||
$value = join(q{ }, @strings);
|
||||
$value = $event;
|
||||
}
|
||||
|
||||
# split value
|
||||
my @strings = split(/\s/x, $value);
|
||||
$strings[0] = q{} if (! defined($strings[0]));
|
||||
|
||||
#numeric value? and last value non numeric? - assume unit
|
||||
if (looks_like_number($strings[0]) && (! looks_like_number($strings[scalar(@strings)-1]))) {
|
||||
$value = join(q{ },@strings[0 .. (scalar(@strings)-2)]);
|
||||
@ -1013,9 +1021,7 @@ sub KNX_Parse {
|
||||
Log3 ($ioName, 4, qq{KNX_Parse -enter: IO-name=$ioName src=} . KNX_hexToName2($src) . q{ dest=} . KNX_hexToName($gadCode) . qq{ msg=$msg});
|
||||
|
||||
#gad not defined yet, give feedback for autocreate
|
||||
if (not (exists $modules{KNX}->{defptr}->{$gadCode})) {
|
||||
return KNX_autoCreate($iohash,$gadCode);
|
||||
}
|
||||
return KNX_autoCreate($iohash,$gadCode) if (! (exists $modules{KNX}->{defptr}->{$gadCode}));
|
||||
|
||||
#get list from device-hashes using given gadCode (==destination)
|
||||
# check on cmd line with: {PrintHash($modules{KNX}->{defptr},3) }
|
||||
@ -1042,24 +1048,23 @@ sub KNX_Parse {
|
||||
my $getName = $deviceHash->{GADDETAILS}->{$gadName}->{RDNAMEGET};
|
||||
my $transval = KNX_decodeByDpt ($deviceHash, $val, $gadName);
|
||||
#message invalid
|
||||
if (not defined($transval) or ($transval eq q{})) {
|
||||
Log3 ($deviceName, 2, qq{KNX_Parse ($deviceName): [wp] readingName=$getName message=$msg} .
|
||||
if (! defined($transval) || ($transval eq q{})) {
|
||||
Log3 ($deviceName, 2, qq{KNX_Parse_wp ($deviceName): readingName=$getName message=$msg} .
|
||||
' could not be decoded');
|
||||
next;
|
||||
}
|
||||
Log3 ($deviceName, 4, qq{KNX_Parse ($deviceName): [wp] readingName=$getName value=$transval});
|
||||
Log3 ($deviceName, 4, qq{KNX_Parse_wp ($deviceName): readingName=$getName value=$transval});
|
||||
|
||||
#apply post processing for state and set all readings
|
||||
KNX_SetReadings($deviceHash, $gadName, $transval, $getName, $src);
|
||||
}
|
||||
|
||||
#handle read messages
|
||||
elsif ($cmd =~ /[r]/x) {
|
||||
elsif ($cmd =~ /[r]/ix) {
|
||||
my $putName = $deviceHash->{GADDETAILS}->{$gadName}->{RDNAMEPUT};
|
||||
Log3 ($deviceName, 5, qq{KNX_Parse ($deviceName): [r] GET});
|
||||
Log3 ($deviceName, 5, qq{KNX_Parse_r ($deviceName): GET});
|
||||
|
||||
#answer "old school"
|
||||
# my $value = ReadingsVal($deviceName, 'state', undef); # fetch default value from state
|
||||
my $value = undef;
|
||||
if (AttrVal($deviceName, 'answerReading', 0) != 0) {
|
||||
my $putVal = ReadingsVal($deviceName, $putName, undef);
|
||||
@ -1076,11 +1081,11 @@ sub KNX_Parse {
|
||||
if ((defined($cmdAttr)) && ($cmdAttr ne q{})) {
|
||||
$value = KNX_eval ($deviceHash, $gadName, $value, $cmdAttr);
|
||||
if (defined($value) && ($value ne q{}) && ($value ne 'ERROR')) { # answer only, if eval was successful
|
||||
Log3 ($deviceName, 5, qq{KNX_Parse ($deviceName): [r] replaced by Attr putCmd=$cmdAttr VALUE=$value});
|
||||
readingsSingleUpdate($deviceHash, $putName, $value,1);
|
||||
Log3 ($deviceName, 5, qq{KNX_Parse_r ($deviceName): replaced by Attr putCmd=$cmdAttr VALUE=$value});
|
||||
readingsSingleUpdate($deviceHash, $putName, $value,0);
|
||||
}
|
||||
else {
|
||||
Log3 ($deviceName, 5, qq{KNX_Parse ($deviceName): [r] gadName=$gadName - no reply sent!});
|
||||
Log3 ($deviceName, 5, qq{KNX_Parse_r ($deviceName): gadName=$gadName - no reply sent!});
|
||||
$value = undef; # dont send !
|
||||
}
|
||||
}
|
||||
@ -1088,7 +1093,7 @@ sub KNX_Parse {
|
||||
#send transval
|
||||
if (defined($value)) {
|
||||
my $transval = KNX_encodeByDpt($deviceHash, $value, $gadName);
|
||||
Log3 ($deviceName, 4, qq{KNX_Parse ($deviceName): [r] send answer: reading=$gadName VALUE=$transval});
|
||||
Log3 ($deviceName, 4, qq{KNX_Parse_r ($deviceName): send answer: reading=$gadName VALUE=$transval});
|
||||
IOWrite ($deviceHash, $TULid, 'p' . $gadCode . $transval);
|
||||
}
|
||||
}
|
||||
@ -1185,6 +1190,7 @@ sub KNX_chkIODev {
|
||||
push(@IOList2,$iodev);
|
||||
next if ($iodev ne $iocandidate);
|
||||
return if ($defs{$iodev}->{TYPE} ne 'FHEM2FHEM'); # ok for std io-dev
|
||||
|
||||
# add support for fhem2fhem as io-dev
|
||||
my $rawdef = $defs{$iodev}->{rawDevice}; #name of fake local IO-dev or remote IO-dev
|
||||
if (defined($rawdef)) {
|
||||
@ -1320,8 +1326,7 @@ sub KNX_replaceByRegex {
|
||||
next if ($rdName ne $regName); # must match completely!
|
||||
|
||||
if (not defined ($regPair[1])) { # cut value
|
||||
Log3 ($name, 5, qq{KNX_replaceByRegex ($name): replaced $rdName value from: $input to undefined});
|
||||
return;
|
||||
$retVal = 'undefined';
|
||||
}
|
||||
elsif ($regPair[0] eq $tempVal) { # complete match
|
||||
$retVal = $regPair[1];
|
||||
@ -1336,7 +1341,7 @@ sub KNX_replaceByRegex {
|
||||
last;
|
||||
}
|
||||
Log3 ($name, 5, qq{KNX_replaceByRegex ($name): replaced $rdName value from: $input to $retVal}) if ($input ne $retVal);
|
||||
return $retVal;
|
||||
return ($retVal eq 'undefined')?undef:$retVal;
|
||||
}
|
||||
|
||||
# limit numeric values. Valid directions: encode, decode
|
||||
@ -1834,6 +1839,24 @@ sub dec_dpt232 { #RGB-Code
|
||||
return sprintf ('%.6x',$numval);
|
||||
}
|
||||
|
||||
### lookup gadname by gadnumber
|
||||
### called from: KNX_Get, KNX_setOldsyntax
|
||||
### entry: $hash, desired gadNO
|
||||
### return: undef on error / gadName
|
||||
sub KNX_gadNameByNO {
|
||||
my $hash = shift;
|
||||
my $groupnr = shift // 1; # default: seaarch for g1
|
||||
|
||||
my $targetGadName = undef;
|
||||
foreach my $key (keys %{$hash->{GADDETAILS}}) {
|
||||
if ($hash->{GADDETAILS}->{$key}->{NO} == $groupnr) {
|
||||
$targetGadName = $key;
|
||||
last;
|
||||
}
|
||||
}
|
||||
return $targetGadName;
|
||||
}
|
||||
|
||||
########## public utility functions ##########
|
||||
|
||||
### get state of devices from KNX_Hardware
|
||||
@ -1870,17 +1893,15 @@ sub main::KNX_scan {
|
||||
next if ($iostate ne 'connected');
|
||||
|
||||
$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];
|
||||
my $k0 = $k; #save previous number of get's
|
||||
foreach my $key (keys %{$devhash->{GADDETAILS}}) {
|
||||
last if (! defined($key));
|
||||
my $option = $devhash->{GADDETAILS}->{$key}->{OPTION};
|
||||
next if (defined($option) && $option =~ /(?:set|listenonly)/ix);
|
||||
$k++;
|
||||
Log3 ($knxdef, 4, qq{KNX_scan-exec: [$k] CMD= "get $knxdef $gad"});
|
||||
$getsarr .= $knxdef . q{ } . $gad . q{,};
|
||||
$getsarr .= $knxdef . q{ } . $key . q{,};
|
||||
}
|
||||
$j++ if ($k > $k0);
|
||||
}
|
||||
Log3 (undef, 3, qq{KNX_scan: $i devices selected / $j devices with get / $k "gets" executing...});
|
||||
doKNX_scan($getsarr) if ($k > 0);
|
||||
@ -2070,7 +2091,7 @@ The answer from the bus-device updates the readings <getName> and state.</
|
||||
<a href="#verbose">verbose</a><br />
|
||||
<a href="#FHEMWEB-attr-webCmd">webCmd</a><br />
|
||||
<a href="#FHEMWEB-attr-webCmdLabel">webCmdLabel</a><br />
|
||||
<a href="#FHEMWEB-attr-widgetOverride">widgetOverride</a>
|
||||
<a href="#KNX-attr-widgetOverride">widgetOverride</a>
|
||||
</ol>
|
||||
|
||||
<p><strong>Special attributes</strong></p>
|
||||
@ -2122,6 +2143,11 @@ The answer from the bus-device updates the readings <getName> and state.</
|
||||
except in cases where multiple IO-devices (of type TUL/KNXTUL/KNXIO) exist in your config. Defining more than one IO-device is <b>NOT recommended</b>
|
||||
unless you take special care with yr. knxd or KNX-router definitions - to prevent multiple path from KNX-Bus to FHEM resulting in message loops.</li>
|
||||
<br/>
|
||||
<a id="KNX-attr-widgetOverride"></a><li>widgetOverride<br/>
|
||||
This is a standard FHEMWEB-attribute, the recommendation for use in KNX-module is to specify the following form:
|
||||
<b><gadName>@set:<widgetName,parameter></b> This avoids overwriting the GET pulldown in FHEMWEB detail page.
|
||||
For details, pls see <a href="#FHEMWEB-attr-widgetOverride">FHEMWEB-attribute</a>.</li>
|
||||
<br/>
|
||||
<a id="KNX-attr-listenonly"></a><li>listenonly - This attr is deprecated - use "listenonly" option in device definition</li>
|
||||
<a id="KNX-attr-readonly"></a><li>readonly - This attr is deprecated - use "get" option in device definition</li>
|
||||
<a id="KNX-attr-slider"></a><li>slider - This attr is deprecated - use attribute widgetOverride <gadName>:slider,<start->,<step->,<end-range> instead</li>
|
||||
|
Loading…
x
Reference in New Issue
Block a user