2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 12:49:34 +00:00

10_KNX.pm: few fixes and optimisation, pls. check (Forum Thread #122582)

git-svn-id: https://svn.fhem.de/fhem/trunk@26625 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
erwin 2022-10-30 17:47:03 +00:00
parent fd7d9839bb
commit f21afabd59
2 changed files with 162 additions and 53 deletions

View File

@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # 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. # Do not insert empty lines here, update check depends on it.
- bugfix: 10_KNX: a few fixes and optimisation
- bugfix: 98_weekprofile:create reference profile - bugfix: 98_weekprofile:create reference profile
from an other referenced profile from an other referenced profile
- feature: 70_ESCVP21net: added 2-value commands - feature: 70_ESCVP21net: added 2-value commands

View File

@ -90,9 +90,16 @@
# .... both will be completly removed with next version # .... both will be completly removed with next version
# new: Internal "RAWMSG" shows msg from Bus while device is disabled (debugging) # new: Internal "RAWMSG" shows msg from Bus while device is disabled (debugging)
# bugfix: allowed group-format corrected: was 0-31/0-15/0-255 -> now: 0-31/0-7/0-255 lt.KNX-spec # bugfix: allowed group-format corrected: was 0-31/0-15/0-255 -> now: 0-31/0-7/0-255 lt.KNX-spec
# MH 202210xx changed package name FHEM::KNX -> KNX
# changed svnid format
# fix dpt4,dpt16 encode/decode (ascii vs. ISO-8859-1)
# fix dpt14.057 unit 'dpt14.057' { cosφ vs. cosφ ) =>need UTF8 in DbLog spec !
# new dpt217 - for EBUSD KNX implementation
# no default slider in FHEMWEB-set/get for dpt7,8,9,12,13 - use widgetoverride slider !
# cmdref formatting
package FHEM::KNX; ## no critic 'package' package KNX; ## no critic 'package'
use strict; use strict;
use warnings; use warnings;
@ -115,7 +122,8 @@ use GPUtils qw(GP_Import GP_Export); # Package Helper Fn
BEGIN { BEGIN {
# Import from main context # Import from main context
GP_Import( GP_Import(
qw(readingsSingleUpdate readingsBulkUpdate readingsBulkUpdateIfChanged readingsBeginUpdate readingsEndUpdate qw(readingsSingleUpdate readingsBulkUpdate readingsBulkUpdateIfChanged
readingsBeginUpdate readingsEndUpdate
Log3 Log3
AttrVal InternalVal ReadingsVal ReadingsNum AttrVal InternalVal ReadingsVal ReadingsNum
addToDevAttrList addToDevAttrList
@ -323,7 +331,8 @@ my %dpttypes = (
'dpt14.056' => {CODE=>'dpt14', UNIT=>q{W}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>undef, MAX=>undef}, 'dpt14.056' => {CODE=>'dpt14', UNIT=>q{W}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>undef, MAX=>undef},
'dpt14.068' => {CODE=>'dpt14', UNIT=>q{°C}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>undef, MAX=>undef}, 'dpt14.068' => {CODE=>'dpt14', UNIT=>q{°C}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>undef, MAX=>undef},
'dpt14.076' => {CODE=>'dpt14', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>undef, MAX=>undef}, 'dpt14.076' => {CODE=>'dpt14', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>undef, MAX=>undef},
'dpt14.057' => {CODE=>'dpt14', UNIT=>q{cosφ}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>undef, MAX=>undef}, 'dpt14.057' => {CODE=>'dpt14', UNIT=>q{cosφ}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>undef, MAX=>undef},
# 'dpt14.057' => {CODE=>'dpt14', UNIT=>q{cosφ}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>undef, MAX=>undef},
# Access data - receive only # Access data - receive only
'dpt15' => {CODE=>'dpt15', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/noset/ix, MIN=>undef, MAX=>undef, 'dpt15' => {CODE=>'dpt15', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/noset/ix, MIN=>undef, MAX=>undef,
@ -362,6 +371,11 @@ my %dpttypes = (
DEC=>\&dec_dpt22,}, DEC=>\&dec_dpt22,},
'dpt22.101' => {CODE=>'dpt22', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/noset/ix, MIN=>undef, MAX=>undef}, 'dpt22.101' => {CODE=>'dpt22', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/noset/ix, MIN=>undef, MAX=>undef},
# Version Info - receive only!!! for EBUSD KNX implementation
'dpt217' => {CODE=>'dpt217', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/\d+\.\d+/ix, MIN=>0, MAX=>255.255,
DEC=>\&dec_dpt217,},
'dpt217.001' => {CODE=>'dpt217', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/\d+\.\d+/ix, MIN=>0, MAX=>255.255},
# Color-Code # Color-Code
'dpt232' => {CODE=>'dpt232', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/[0-9a-f]{6}/ix, MIN=>undef, MAX=>undef, SETLIST=>'colorpicker', 'dpt232' => {CODE=>'dpt232', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/[0-9a-f]{6}/ix, MIN=>undef, MAX=>undef, SETLIST=>'colorpicker',
DEC=>\&dec_dpt232,ENC=>\&enc_dpt232,} DEC=>\&dec_dpt232,ENC=>\&enc_dpt232,}
@ -411,7 +425,7 @@ sub KNX_Define {
my $name = $a[0]; my $name = $a[0];
$hash->{NAME} = $name; $hash->{NAME} = $name;
$svnid =~ s/.*\.pm\s(.+)Z.*/$1 UTC/ix; $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 my $logtxt = qq{KNX_define ($name): }; # leading txt
@ -419,14 +433,17 @@ sub KNX_Define {
Log3 ($name, 5, $logtxt . join (q{ }, @a)); Log3 ($name, 5, $logtxt . join (q{ }, @a));
#too less arguments or no valid 1st gad #too less arguments or no valid 1st gad
return ($logtxt . q{wrong syntax or wrong group-format (0-31/0-7/0-255)} . qq{\n} .q{ "define <name> KNX <group:model[:GAD-name][:set|get|listenonly]> } . return ($logtxt . q{wrong syntax or wrong group-format (0-31/0-7/0-255)} .
qq{\n} . q{ "define <name> KNX <group:model[:GAD-name][:set|get|listenonly]> } .
q{[<group:model[:GAD-name][:set|get|listenonly]>]"}) if (int(@a) < 3 || $a[2] !~ m/^(?:$PAT_GAD|$PAT_GAD_HEX)/ix); q{[<group:model[:GAD-name][:set|get|listenonly]>]"}) if (int(@a) < 3 || $a[2] !~ m/^(?:$PAT_GAD|$PAT_GAD_HEX)/ix);
# check if the last arg matches any IO-Device - and assign it - else use the automatic mechanism # check if the last arg matches any IO-Device - and assign it - else use the automatic mechanism
if ( $a[int(@a) - 1] !~ m/^(?:$PAT_GAD|$PAT_GAD_HEX)/ix ) { if ( $a[int(@a) - 1] !~ m/^(?:$PAT_GAD|$PAT_GAD_HEX)/ix ) {
my $iodevCandidate = pop(@a); my $iodevCandidate = pop(@a);
Log3 ($name, 2, $logtxt . qq{specifying IODev $iodevCandidate is deprecated in define - use "attr $name IODev $iodevCandidate"}); Log3 ($name, 2, qq{$logtxt specifying IODev $iodevCandidate is deprecated in define } .
return ($logtxt . qq{specifying IODev $iodevCandidate is deprecated in define - use "attr $name IODev $iodevCandidate"}) if ($init_done); # allow durin start qq{- use "attr $name IODev $iodevCandidate"});
return qq{$logtxt specifying IODev $iodevCandidate is deprecated in define } .
qq{- use "attr $name IODev $iodevCandidate"} if ($init_done); # allow durin start
my $ret = KNX_chkIODev($hash,$iodevCandidate); # returns undef on success! my $ret = KNX_chkIODev($hash,$iodevCandidate); # returns undef on success!
if (! defined($ret)) { if (! defined($ret)) {
@ -473,7 +490,7 @@ sub KNX_Define {
} }
elsif (!defined($dpttypes{$gadModel})) { #check model-type elsif (!defined($dpttypes{$gadModel})) { #check model-type
return $logtxt . qq{invalid model: $gadModel for group-number $gadNo} . return $logtxt . qq{invalid model: $gadModel for group-number $gadNo} .
'. Please consult commanref - avaliable DPT for correct model definition.'; '. Please consult commandref - avaliable DPT for correct model definition.';
} }
elsif ($gadNo == 1) { # gadModel ok elsif ($gadNo == 1) { # gadModel ok
$hash->{model} = lc($gadModel) =~ s/^(dpt[\d]+)\..*/$1/rx; # use first gad as mdl reference for fheminfo $hash->{model} = lc($gadModel) =~ s/^(dpt[\d]+)\..*/$1/rx; # use first gad as mdl reference for fheminfo
@ -505,7 +522,7 @@ sub KNX_Define {
#if not defined yet, define a new hash #if not defined yet, define a new hash
if (not(defined($tableHashRef))) { if (not(defined($tableHashRef))) {
$tableHashRef={}; $tableHashRef={};
$hash->{GADTABLE}=$tableHashRef; $hash->{GADTABLE} = $tableHashRef;
} }
return ($logtxt . qq{GAD $gad may be supplied only once per device.}) if (defined($hash->{GADTABLE}->{$gadCode})); return ($logtxt . qq{GAD $gad may be supplied only once per device.}) if (defined($hash->{GADTABLE}->{$gadCode}));
@ -536,12 +553,14 @@ sub KNX_Define {
if (defined ($dptDetails->{SETLIST})) { # list is given, pass it through if (defined ($dptDetails->{SETLIST})) { # list is given, pass it through
$setlist = q{:} . $dptDetails->{SETLIST}; $setlist = q{:} . $dptDetails->{SETLIST};
} }
elsif (defined ($dptDetails->{MIN}) and ($dptDetails->{MIN} =~ m/0|[+-]?\d*[(.|,)\d*]/x)) { #number? - place slider elsif (defined ($dptDetails->{MIN}) and looks_like_number($dptDetails->{MIN})) { #number? - place slider
# elsif (defined ($dptDetails->{MIN}) and ($dptDetails->{MIN} =~ m/0|[+-]?\d*[(.|,)\d*]/x)) { #number? - place slider
my $min = $dptDetails->{MIN}; my $min = $dptDetails->{MIN};
my $max = $dptDetails->{MAX}; my $max = $dptDetails->{MAX};
my $interval = int(($max-$min)/100); my $interval = int(($max-$min)/100);
$interval = 1 if ($interval == 0); $interval = 1 if ($interval == 0);
$setlist = ':slider,' . $min . q{,} . $interval . q{,} . $max; $setlist = ':slider,' . $min . q{,} . $interval . q{,} . $max if ($interval < 50); # nonsense for dpt7,8,9,12,13
# $setlist = ':slider,' . $min . q{,} . $interval . q{,} . $max;
} }
elsif (defined ($dptDetails->{MIN})) { #on/off/... elsif (defined ($dptDetails->{MIN})) { #on/off/...
my $min = $dptDetails->{MIN}; my $min = $dptDetails->{MIN};
@ -979,19 +998,20 @@ sub KNX_DbLog_split {
sub KNX_Parse { sub KNX_Parse {
my $iohash = shift; # this is IO-Device hash ! my $iohash = shift; # this is IO-Device hash !
my $msg = shift; my $msg = shift;
my $ioName = $iohash->{NAME};
my $ioName = $iohash->{NAME};
return q{} if ((IsDisabled($ioName) == 1) || IsDummy($ioName)); # IO - device is disabled or dummy return q{} if ((IsDisabled($ioName) == 1) || IsDummy($ioName)); # IO - device is disabled or dummy
#Msg format: C<src>[wrp]<group><value> i.e. Cw00000101 #Msg format: C<src>[wrp]<group><value> i.e. Cw00000101
my ($src,$cmd,$gadCode,$val) = $msg =~ m/^$TULid([0-9a-f]{5})([prw])([0-9a-f]{5})(.*)$/ix; my ($src,$cmd,$gadCode,$val) = $msg =~ m/^$TULid([0-9a-f]{5})([prw])([0-9a-f]{5})(.*)$/ix;
my @foundMsgs; my @foundMsgs;
Log3 ($ioName, 4, qq{KNX_Parse -enter: IO-name= $ioName DEST= $gadCode MSG= $msg}); Log3 ($ioName, 4, qq{KNX_Parse -enter: IO-name= $ioName dest= $gadCode msg= $msg});
#gad not defined yet, give feedback for autocreate #gad not defined yet, give feedback for autocreate
if (not (exists $modules{KNX}->{defptr}->{$gadCode})) { if (not (exists $modules{KNX}->{defptr}->{$gadCode})) {
#format gad return KNX_autoCreate($iohash,$gadCode);
=pod #format gad
my $gad = KNX_hexToName($gadCode); my $gad = KNX_hexToName($gadCode);
#create name #create name
my $newDevName = sprintf('KNX_%.2d%.2d%.3d',split (/\//x, $gad)); my $newDevName = sprintf('KNX_%.2d%.2d%.3d',split (/\//x, $gad));
@ -1002,6 +1022,7 @@ sub KNX_Parse {
return q{} if($newDevName =~ /$igntypes/x); return q{} if($newDevName =~ /$igntypes/x);
} }
return qq{UNDEFINED $newDevName KNX $gad} . q{:} . $MODELERR; return qq{UNDEFINED $newDevName KNX $gad} . q{:} . $MODELERR;
=cut
} }
#get list from device-hashes using given gadCode (==destination) #get list from device-hashes using given gadCode (==destination)
@ -1021,8 +1042,8 @@ sub KNX_Parse {
next; next;
} }
Log3 ($deviceName, 4, qq{KNX_Parse ($deviceName): -process IO-name= $ioName GADNAME= $gadName } . Log3 ($deviceName, 4, qq{KNX_Parse ($deviceName): -process IO-name= $ioName gadName= $gadName } .
qq{GADCODE= $gadCode CMD= $cmd}); qq{gadCode= $gadCode cmd= $cmd});
#handle write and reply messages #handle write and reply messages
if ($cmd =~ /[w|p]/ix) { if ($cmd =~ /[w|p]/ix) {
@ -1031,11 +1052,11 @@ sub KNX_Parse {
my $transval = KNX_decodeByDpt ($deviceHash, $val, $gadName); my $transval = KNX_decodeByDpt ($deviceHash, $val, $gadName);
#message invalid #message invalid
if (not defined($transval) or ($transval eq q{})) { if (not defined($transval) or ($transval eq q{})) {
Log3 ($deviceName, 2, qq{KNX_Parse ($deviceName): [wp] READINGNAME= $getName MESSAGE= $msg} . Log3 ($deviceName, 2, qq{KNX_Parse ($deviceName): [wp] readingName= $getName message= $msg} .
' could not be decoded'); ' could not be decoded');
next; next;
} }
Log3 ($deviceName, 4, qq{KNX_Parse ($deviceName): [wp] READINGNAME= $getName VALUE= $transval SENDER= $src}); Log3 ($deviceName, 4, qq{KNX_Parse ($deviceName): [wp] readingName= $getName value= $transval sender= $src});
#apply post processing for state and set all readings #apply post processing for state and set all readings
KNX_SetReadings($deviceHash, $gadName, $transval, $getName, $src); KNX_SetReadings($deviceHash, $gadName, $transval, $getName, $src);
@ -1050,7 +1071,7 @@ sub KNX_Parse {
my $value = undef; my $value = undef;
if (AttrVal($deviceName, 'answerReading', 0) != 0) { if (AttrVal($deviceName, 'answerReading', 0) != 0) {
my $putVal = ReadingsVal($deviceName, $putName, undef); my $putVal = ReadingsVal($deviceName, $putName, undef);
if ((defined($putVal)) && ($putVal ne q{})) { if (defined($putVal) && ($putVal ne q{})) {
$value = $putVal; #medium priority, overwrite $value $value = $putVal; #medium priority, overwrite $value
} }
else { else {
@ -1061,14 +1082,14 @@ sub KNX_Parse {
#high priority - eval #high priority - eval
my $cmdAttr = AttrVal($deviceName, 'putCmd', undef); my $cmdAttr = AttrVal($deviceName, 'putCmd', undef);
if ((defined($cmdAttr)) && ($cmdAttr ne q{})) { if ((defined($cmdAttr)) && ($cmdAttr ne q{})) {
$value = ReadingsVal($deviceName, 'state', undef); # get default value from state # $value = ReadingsVal($deviceName, 'state', undef); # get default value from state
$value = KNX_eval ($deviceHash, $gadName, $value, $cmdAttr); $value = KNX_eval ($deviceHash, $gadName, $value, $cmdAttr);
if (defined($value) && ($value ne q{}) && ($value ne 'ERROR')) { # answer only, if eval was successful 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}); Log3 ($deviceName, 5, qq{KNX_Parse ($deviceName): [r] replaced by Attr putCmd= $cmdAttr - VALUE= $value});
readingsSingleUpdate($deviceHash, $putName, $value,1); readingsSingleUpdate($deviceHash, $putName, $value,1);
} }
else { else {
Log3 ($deviceName, 5, qq{KNX_Parse ($deviceName): [r] GADNAME= $gadName - no reply sent!}); Log3 ($deviceName, 5, qq{KNX_Parse ($deviceName): [r] gadName= $gadName - no reply sent!});
$value = undef; # dont send ! $value = undef; # dont send !
} }
} }
@ -1076,7 +1097,7 @@ sub KNX_Parse {
#send transval #send transval
if (defined($value)) { if (defined($value)) {
my $transval = KNX_encodeByDpt($deviceHash, $value, $gadName); 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 ($deviceName): [r] send answer: reading= $gadName value= $transval});
IOWrite ($deviceHash, $TULid, 'p' . $gadCode . $transval); IOWrite ($deviceHash, $TULid, 'p' . $gadCode . $transval);
} }
} }
@ -1087,6 +1108,26 @@ sub KNX_Parse {
########## begin of private functions ########## ########## begin of private functions ##########
# KNX_autoCreate
# check wether we must do autocreate...
# on entry: $iohash, $gadcode
# on exit: return string for autocreate
sub KNX_autoCreate {
my $iohash = shift;
my $gadCode = shift;
my $gad = KNX_hexToName($gadCode); #format gad
my $newDevName = sprintf('KNX_%.2d%.2d%.3d',split (/\//x, $gad)); #create name
# check if any autocreate device has ignoretype "KNX..." set
my @acList = devspec2array('TYPE=autocreate');
foreach my $acdev (@acList) {
my $igntypes = AttrVal($acdev,'ignoreTypes',q{});
return q{} if($newDevName =~ /$igntypes/x);
}
return qq{UNDEFINED $newDevName KNX $gad} . q{:} . $MODELERR;
}
# KNX_SetReadings is called from KNX_Set and KNX_Parse # KNX_SetReadings is called from KNX_Set and KNX_Parse
# calling param: $hash, $gadName, $transval, $rdName, caller (set/parse) # calling param: $hash, $gadName, $transval, $rdName, caller (set/parse)
sub KNX_SetReadings { sub KNX_SetReadings {
@ -1378,16 +1419,16 @@ sub KNX_encodeByDpt {
my $ivalue = $value; # save for compare my $ivalue = $value; # save for compare
$value = KNX_limit ($hash, $value, $model, 'ENCODE'); $value = KNX_limit ($hash, $value, $model, 'ENCODE');
Log3 ($name, 4, qq{KNX_limit ($name): GadName= $gadName modified... Input= $ivalue Output= $value Model= $model}) if ($ivalue ne $value); Log3 ($name, 4, qq{KNX_limit ($name): gadName= $gadName modified... Input= $ivalue Output= $value Model= $model}) if ($ivalue ne $value);
if (ref($dpttypes{$code}->{ENC}) eq 'CODE') { if (ref($dpttypes{$code}->{ENC}) eq 'CODE') {
$hexval = $dpttypes{$code}->{ENC}->($value, $model); $hexval = $dpttypes{$code}->{ENC}->($value, $model);
Log3 ($name, 5, qq{KNX_encodeByDpt ($name): GadName= $gadName model= $model code= $code } . Log3 ($name, 5, qq{KNX_encodeByDpt ($name): gadName= $gadName model= $model code= $code } .
qq{in-Value= $ivalue out-value= $value out-hexval= $hexval}); qq{in-Value= $ivalue out-value= $value out-hexval= $hexval});
return $hexval; return $hexval;
} }
else { else {
Log3 ($name, 2, qq{KNX_encodeByDpt ($name): GadName= $gadName model= $model not valid}); Log3 ($name, 2, qq{KNX_encodeByDpt ($name): gadName= $gadName model= $model not valid});
} }
return; return;
} }
@ -1409,11 +1450,12 @@ sub KNX_decodeByDpt {
if (ref($dpttypes{$code}->{DEC}) eq 'CODE') { if (ref($dpttypes{$code}->{DEC}) eq 'CODE') {
$state = $dpttypes{$code}->{DEC}->($value, $model, $hash); $state = $dpttypes{$code}->{DEC}->($value, $model, $hash);
Log3 ($name, 5, qq{KNX_decodeByDpt ($name): model= $model code= $code value= $value length-value=: } . length($value) . qq{ state= $state}); Log3 ($name, 5, qq{KNX_decodeByDpt ($name): gadName= $gadName model= $model code= $code value= $value length-value= } .
length($value) . qq{ state= $state});
return $state; return $state;
} }
else { else {
Log3 ($name, 2, qq{KNX_decodeByDpt ($name): model= $model no valid model defined}); Log3 ($name, 2, qq{KNX_decodeByDpt ($name): gadName= $gadName model= $model not valid});
} }
return; return;
} }
@ -1451,18 +1493,22 @@ sub enc_dpt3 { #Step value (four-bit)
return sprintf('%.2x',$numval); return sprintf('%.2x',$numval);
} }
sub enc_dpt4 { #single char ascii / iso-8859-1 String sub enc_dpt4 { #single ascii or iso-8859-1 char
my $value = shift; my $value = shift;
my $model = shift; my $model = shift;
my $numval = q{}; my $numval = encode('iso-8859-1', decode('utf8', $value)); #always convert to latin-1
$numval =~ s/[\x80-\xff]/?/gx if ($model eq 'dpt4.001'); #replace values >= 0x80 if ascii
=pod
my $numval = q{};
if ($model eq 'dpt4.002' ) { if ($model eq 'dpt4.002' ) {
$numval = encode('iso-8859-1', decode('utf8', $value)); #convert to latin-1 $numval = encode('iso-8859-1', decode('utf8', $value)); #convert to latin-1
} else { } else {
$numval = encode('ascii', decode('utf8', $value)); # plain ascii $numval = encode('ascii', decode('utf8', $value)); # plain ascii
} }
#convert to hex-string =cut
my $dat = unpack('H*', $numval); #convert to hex-string
return sprintf('00%s',$dat); my $dat = unpack('H*', $numval);
return sprintf('00%s',$dat);
} }
sub enc_dpt5 { #1-Octet unsigned value sub enc_dpt5 { #1-Octet unsigned value
@ -1558,12 +1604,16 @@ sub enc_dpt14 { #4-Octet single precision float
sub enc_dpt16 { #14-Octet String sub enc_dpt16 { #14-Octet String
my $value = shift; my $value = shift;
my $model = shift; my $model = shift;
my $numval = encode('iso-8859-1', decode('utf8', $value)); #always convert to latin-1
$numval =~ s/[\x80-\xff]/?/gx if ($model =~ /dpt16\.000/ix); #replace values >= 0x80 if ascii
=pod
my $numval = q{}; my $numval = q{};
if ($model eq 'dpt16.001' ) { if ($model eq 'dpt16.001' ) {
$numval = encode('iso-8859-1', decode('utf8', $value)); #convert to latin-1 $numval = encode('iso-8859-1', decode('utf8', $value)); #convert to latin-1
} else { } else {
$numval = encode('ascii', decode('utf8', $value)); # plain ascii $numval = encode('ascii', decode('utf8', $value)); # plain ascii
} }
=cut
#convert to hex-string #convert to hex-string
my $dat = unpack('H*', $numval); my $dat = unpack('H*', $numval);
$dat = '00' if ($value =~ /^$PAT_DPT16_CLR/ix); # send all zero string if "clear line string" $dat = '00' if ($value =~ /^$PAT_DPT16_CLR/ix); # send all zero string if "clear line string"
@ -1754,7 +1804,9 @@ sub dec_dpt16 { #14-Octet String or dpt4: single Char string
$value =~ s/\s*$//gx; # strip trailing blanks $value =~ s/\s*$//gx; # strip trailing blanks
my $state = pack('H*',$value); my $state = pack('H*',$value);
#convert from latin-1 #convert from latin-1
$state = encode ('utf8', decode('iso-8859-1',$state)) if ($model =~ m/(16.001|4.002)/x); $state = encode ('utf8', decode('iso-8859-1',$state)) if ($model !~ m/^dpt(?:16.000|4.001)$/x);
## $state = encode ('utf8', decode('iso-8859-1',$state)) if ($model =~ m/^dpt(16.001|4.002|16|4)$/x);
# $state = encode ('utf8', decode('iso-8859-1',$state)) if ($model =~ m/(16.001|4.002)/x);
$state = q{} if ($state =~ m/^[\x00]/ix); # case all zeros received $state = q{} if ($state =~ m/^[\x00]/ix); # case all zeros received
$state =~ s/[\x00-\x1F]+//gx; # remove non printable chars $state =~ s/[\x00-\x1F]+//gx; # remove non printable chars
return $state; return $state;
@ -1793,6 +1845,13 @@ sub dec_dpt22 { #HVAC dpt22.101 only
return $state; return $state;
} }
sub dec_dpt217 { #version
my $numval = hex (shift);
my $maj = $numval >> 8;
my $min = $numval & 0x00FF;
return sprintf('%d.%d',$maj,$min);
}
sub dec_dpt232 { #RGB-Code sub dec_dpt232 { #RGB-Code
my $numval = hex (shift); my $numval = hex (shift);
return sprintf ('%.6x',$numval); return sprintf ('%.6x',$numval);
@ -1824,14 +1883,12 @@ sub main::KNX_scan {
foreach my $knxdef (@devlist) { foreach my $knxdef (@devlist) {
my $devhash = $defs{$knxdef}; my $devhash = $defs{$knxdef};
next if ((! defined($devhash)) || ($devhash->{TYPE} ne 'KNX') || $devhash->{DEF} =~ /$MODELERR/ix); next if ((! defined($devhash)) || ($devhash->{TYPE} ne 'KNX') || $devhash->{DEF} =~ /$MODELERR/ix);
# next if ((! defined($devhash)) || ($devhash->{TYPE} ne 'KNX') || $devhash->{DEF} =~ /MODEL_NOT_DEFINED/ix);
#check if IO-device is ready #check if IO-device is ready
my $iodev = $devhash->{IODev}->{NAME}; my $iodev = $devhash->{IODev}->{NAME};
# my $iodev = ReadingsVal($knxdef,'IODev',undef);
next if (! defined($iodev)); next if (! defined($iodev));
my $iostate = InternalVal($iodev,'STATE',q{}); # my $iostate = InternalVal($iodev,'STATE',q{});
# my $iostate = ReadingsVal($iodev,'state',q{}); my $iostate = ReadingsVal($iodev,'state',q{});
next if ($iostate ne 'connected'); next if ($iostate ne 'connected');
$i++; $i++;
@ -1903,6 +1960,52 @@ sub doKNX_scan {
.pad20l {padding-left: 20px;} .pad20l {padding-left: 20px;}
.pad30l {padding-left: 30px;} .pad30l {padding-left: 30px;}
.pad40l {padding-left: 40px;} .pad40l {padding-left: 40px;}
#KNX-dpt_ul {
list-style-type: none;
padding-left: 30px;
width:95%;
column-count:2;
column-gap:10px;
-moz-column-count:2;
-moz-column-gap:20px;
-webkit-column-count:2;
-webkit-column-gap:20px;
float: left;
}
#KNX-dpt_ul li {
padding-left: 1em; white-space: pre; overflow: clip;
}
#KNX-dpt_ul li b {
display: inline-block;
width: 6em;
overflow: clip;
}
/* For mobile phones: */
@media only screen and (max-width: 1050px) {
#KNX-dpt_ul {column-count:1; -moz-column-count:1; -webkit-column-count:1;}
}
#KNX-attr_ul {
list-style-type: none;
padding-left: 30px;
width:95%;
column-count:2;
column-gap:10px;
-moz-column-count:2;
-moz-column-gap:20px;
-webkit-column-count:2;
-webkit-column-gap:20px;
float: left;
margin: 3px;
padding: 3px;
}
#KNX-attr_ul a {
padding-left: 1em; width: 100%;
}
/* For mobile phones: */
@media only screen and (max-width: 1050px) {
#KNX-attr_ul {column-count:1; -moz-column-count:1; -webkit-column-count:1;}
}
</style> </style>
<a id="KNX"></a> <a id="KNX"></a>
<h3>KNX</h3> <h3>KNX</h3>
@ -1999,7 +2102,8 @@ The answer from the bus-device updates reading and state.</p>
<a id="KNX-attr"></a> <a id="KNX-attr"></a>
<p><strong>Common attributes</strong></p> <p><strong>Common attributes</strong></p>
<ul> <!-- <ul> -->
<ol id="KNX-attr_ul">
<a href="#DbLogattr">DbLogInclude</a><br /> <a href="#DbLogattr">DbLogInclude</a><br />
<a href="#DbLogattr">DbLogExclude</a><br /> <a href="#DbLogattr">DbLogExclude</a><br />
<a href="#DbLogattr">DbLogValueFn</a><br /> <a href="#DbLogattr">DbLogValueFn</a><br />
@ -2027,8 +2131,9 @@ The answer from the bus-device updates reading and state.</p>
<a href="#webCmd">webCmd</a><br /> <a href="#webCmd">webCmd</a><br />
<a href="#webCmdLabel">webCmdLabel</a><br /> <a href="#webCmdLabel">webCmdLabel</a><br />
<a href="#widgetOverride">widgetOverride</a> <a href="#widgetOverride">widgetOverride</a>
</ul> <!-- </ul> -->
</ol>
<br/>&nbsp;<br/>&nbsp;
<p><strong>Special attributes</strong></p> <p><strong>Special attributes</strong></p>
<ul> <ul>
<a id="KNX-attr-answerReading"></a><li>answerReading<br/> <a id="KNX-attr-answerReading"></a><li>answerReading<br/>
@ -2079,14 +2184,15 @@ The answer from the bus-device updates reading and state.</p>
<br/> <br/>
<a id="KNX-attr-listenonly"></a><li>listenonly - This attr is deprecated - use "listenonly" option in device definition</li> <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-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 &lt;readingname&gt;:slider,<start->,<step->,<end-range> instead</li> <a id="KNX-attr-slider"></a><li>slider - This attr is deprecated - use attribute widgetOverride &lt;gadName&gt;:slider,&lt;start-&gt;,&lt;step-&gt;,&lt;end-range&gt; instead</li>
</ul> </ul>
<a id="KNX-dpt"></a> <a id="KNX-dpt"></a>
<p><strong>DPT - data-point-types</strong></p> <p><strong>DPT - data-point-types</strong></p>
<p>The following dpt are implemented and have to be assigned within the device definition. <p>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 and units.</p> The values right to the dpt define the valid range of Set-command values and Get-command return values and units.</p>
<ul> <!-- <ul> -->
<ol id="KNX-dpt_ul">
<li><b>dpt1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</b>off, on, toggle</li> <li><b>dpt1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</b>off, on, toggle</li>
<li><b>dpt1.000 </b> 0, 1</li> <li><b>dpt1.000 </b> 0, 1</li>
<li><b>dpt1.001 </b> off, on, toggle</li> <li><b>dpt1.001 </b> off, on, toggle</li>
@ -2117,8 +2223,8 @@ The answer from the bus-device updates reading and state.</p>
<li><b>dpt3.007 </b> -100..+100 %</li> <li><b>dpt3.007 </b> -100..+100 %</li>
<li><b>dpt3.008 </b> -100..+100 %</li> <li><b>dpt3.008 </b> -100..+100 %</li>
<li><b>dpt4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</b>single char</li> <li><b>dpt4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</b>single char</li>
<li><b>dpt4.001 </b> ascii</li> <li><b>dpt4.001 </b> ascii char</li>
<li><b>dpt4.002 </b> iso-8859-1</li> <li><b>dpt4.002 </b> ISO-8859-1 char</li>
<li><b>dpt5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</b>0..255</li> <li><b>dpt5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</b>0..255</li>
<li><b>dpt5.001 </b> 0..100 %</li> <li><b>dpt5.001 </b> 0..100 %</li>
<li><b>dpt5.003 </b> 0..360 &deg;</li> <li><b>dpt5.003 </b> 0..360 &deg;</li>
@ -2180,22 +2286,24 @@ The answer from the bus-device updates reading and state.</p>
<li><b>dpt14.033</b> -Inf.0..+Inf.0 Hz</li> <li><b>dpt14.033</b> -Inf.0..+Inf.0 Hz</li>
<li><b>dpt14.039</b> -Inf.0..+Inf.0 m</li> <li><b>dpt14.039</b> -Inf.0..+Inf.0 m</li>
<li><b>dpt14.056</b> -Inf.0..+Inf.0 W</li> <li><b>dpt14.056</b> -Inf.0..+Inf.0 W</li>
<li><b>dpt14.057</b> -Inf.0..+Inf.0 cos&Phi;</li> <li><b>dpt14.057</b> -Inf.0..+Inf.0 cos&phi;</li>
<li><b>dpt14.068</b> -Inf.0..+Inf.0 &deg;C</li> <li><b>dpt14.068</b> -Inf.0..+Inf.0 &deg;C</li>
<li><b>dpt14.076</b> -Inf.0..+Inf.0 m&sup3;</li> <li><b>dpt14.076</b> -Inf.0..+Inf.0 m&sup3;</li>
<li><b>dpt15.000</b> Access-code - receive only!</li> <li><b>dpt15.000</b> Access-code - receive only!</li>
<li><b>dpt16&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</b>14 char string</li> <li><b>dpt16&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</b>14 char string</li>
<li><b>dpt16.000</b> ASCII-String</li> <li><b>dpt16.000</b> ASCII string</li>
<li><b>dpt16.001</b> ISO-8859-1-String (Latin1)</li> <li><b>dpt16.001</b> ISO-8859-1 string (Latin1)</li>
<li><b>dpt17.001</b> Scene Nr: 0..63</li> <li><b>dpt17.001</b> Scene Nr: 0..63</li>
<li><b>dpt18.001</b> Scene Nr: 1..64. - only "activation" works..</li> <li><b>dpt18.001</b> Scene Nr: 1..64. - only "activation" works..</li>
<li><b>dpt19&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</b>01.12.2020_01:02:03 (Date&amp;Time)</li> <li><b>dpt19&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</b>01.12.2020_01:02:03 (Date&amp;Time)</li>
<li><b>dpt19.001</b> 01.12.2020_01:02:03</li> <li><b>dpt19.001</b> 01.12.2020_01:02:03</li>
<li><b>dpt20.102</b> HVAC mode</li> <li><b>dpt20.102</b> HVAC mode</li>
<li><b>dpt22.101</b> HVAC RHCC Status</li> <li><b>dpt22.101</b> HVAC RHCC Status (readonly)</li>
<li><b>dpt217.001</b> dpt version (readonly)</li>
<li><b>dpt232&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</b>RGB-Value RRGGBB</li> <li><b>dpt232&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</b>RGB-Value RRGGBB</li>
</ul> <!-- </ul> -->
</ol>
<br/>&nbsp;<br/>&nbsp;
<a id="KNX-utilities"></a> <a id="KNX-utilities"></a>
<p><strong>KNX Utility Functions</strong></p> <p><strong>KNX Utility Functions</strong></p>
<ul> <ul>
@ -2212,7 +2320,7 @@ Returns number of "get's" issued.<br/>
<code>KNX_scan('room=Kueche') - scan all KNX-devices in room Kueche</code><br/> <code>KNX_scan('room=Kueche') - scan all KNX-devices in room Kueche</code><br/>
<code>{KNX_scan('device')} - syntax when used from FHEM-cmdline</code><br/> <code>{KNX_scan('device')} - syntax when used from FHEM-cmdline</code><br/>
<code>knxscan device - syntax when used from FHEM-cmdline via cmd-alias definition, see below</code><br/> <code>knxscan device - syntax when used from FHEM-cmdline via cmd-alias definition, see below</code><br/>
<br/>When using KNX_scan() or any 'set|get <device> ...' in a global:INITIALIZED notify, pls. ensure to have some delay in processing the cmd's by using <b>fhem sleep</b>. <br/>When using KNX_scan() or any 'set|get &lt;device&gt; ...' in a global:INITIALIZED notify, pls. ensure to have some delay in processing the cmd's by using <b>fhem sleep</b>.
<br/>Example: <code>defmod initialized_nf notify global:INITIALIZED sleep 10 quiet;; set KNX_date now;; set KNX_time now;; {KNX_scan();;}</code> <br/>Example: <code>defmod initialized_nf notify global:INITIALIZED sleep 10 quiet;; set KNX_date now;; set KNX_time now;; {KNX_scan();;}</code>
<br/>This avoids sending requests while the KNX-Gateway has not finished its initial handshake-procedure with FHEM (the KNX-IO-device). <br/>This avoids sending requests while the KNX-Gateway has not finished its initial handshake-procedure with FHEM (the KNX-IO-device).
<br/><br/>If you want to use this function as a FHEM cmd, define a cmdalias-device, e.g:<br/> <br/><br/>If you want to use this function as a FHEM cmd, define a cmdalias-device, e.g:<br/>