From 539fc5d82a206c5443f702d1cd2880d43bbd79b6 Mon Sep 17 00:00:00 2001
From: erwin <>
Date: Fri, 15 Oct 2021 14:45:51 +0000
Subject: [PATCH] 10_KNX.pm: multiple bugfixes & new cmd: blink, pls. check
(Forum Thread #122582)
git-svn-id: https://svn.fhem.de/fhem/trunk@25073 2b470e98-0d58-463d-a4d8-8e2adae1ed80
---
fhem/CHANGED | 1 +
fhem/FHEM/10_KNX.pm | 160 +++++++++++++++++++++++++++-----------------
2 files changed, 100 insertions(+), 61 deletions(-)
diff --git a/fhem/CHANGED b/fhem/CHANGED
index 7b6bf844e..a22313ac5 100644
--- a/fhem/CHANGED
+++ b/fhem/CHANGED
@@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
+ - bugfix: 10_KNX: multiple fixes & new cmd: blink - see Forum #122582
- feature: 23_LUXTRONIK2: new set parameter opModeHeating
- bugfix: 73_AutoShuttersControl: multiple bugfixes, change version
- change: 10_KNX: multiple fixes & cleanup - see Forum Thread #122582
diff --git a/fhem/FHEM/10_KNX.pm b/fhem/FHEM/10_KNX.pm
index ff24388e0..ea7ebc8b0 100644
--- a/fhem/FHEM/10_KNX.pm
+++ b/fhem/FHEM/10_KNX.pm
@@ -55,6 +55,12 @@
# prevent deletion of Attr disable until a valid dpt is defined
# changed AnalyzePerlCommand to AnalyzeCommandChain to allow multiple fhem cmds in eval's
# code cleanup
+# MH 20211013 E04.72 fix dpt1.004, .011, .012, .018 encoding
+# remove 'return undef' from initialize & defineFn
+# fix stateregex (KNX_replacebyregex)
+# fix off-for-timer
+# add wiki links
+# add blink cmd for dpt1, dpt1.001
package FHEM::KNX; ## no critic 'package'
@@ -110,6 +116,7 @@ my $MODELERR = "MODEL_NOT_DEFINED"; # for autocreate
#my $OFFFORTIMER = "off-for-timer";
#my $ONUNTIL = "on-until";
#my $OFFUNTIL = "off-until";
+my $BLINK = "blink";
my $TOGGLE = "toggle";
my $RAW = "raw";
my $RGB = "rgb";
@@ -151,26 +158,26 @@ my $PAT_DPT16_CLR = qr/>CLR {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT)/ix, MIN=>"off", MAX=>"on"},
+ "dpt1" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT)/ix, MIN=>"off", MAX=>"on", SETLIST=>'on,off,toggle'},
"dpt1.000" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT)/ix, MIN=>"0", MAX=>"1"},
- "dpt1.001" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT)/ix, MIN=>"off", MAX=>"on"},
+ 'dpt1.001' => {CODE=>'dpt1', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT)/ix, MIN=>'off', MAX=>'on', SETLIST=>'on,off,toggle'},
"dpt1.002" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(true)|(false))/ix, MIN=>"false", MAX=>"true"},
"dpt1.003" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(enable)|(disable))/ix, MIN=>"disable", MAX=>"enable"},
- "dpt1.004" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(no_ramp)|(ramp))/ix, MIN=>"no_ramp", MAX=>"ramp"},
+ 'dpt1.004' => {CODE=>'dpt1', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|no_ramp|ramp)/ix, MIN=>'no_ramp', MAX=>'ramp'},
"dpt1.005" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(no_alarm)|(alarm))/ix, MIN=>"no_alarm", MAX=>"alarm"},
"dpt1.006" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(low)|(high))/ix, MIN=>"low", MAX=>"high"},
"dpt1.007" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(decrease)|(increase))/ix, MIN=>"decrease", MAX=>"increase"},
"dpt1.008" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(up)|(down))/ix, MIN=>"up", MAX=>"down"},
"dpt1.009" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(closed)|(open))/ix, MIN=>"open", MAX=>"closed"},
"dpt1.010" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(start)|(stop))/ix, MIN=>"stop", MAX=>"start"},
- "dpt1.011" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(inactive)|(active))/ix, MIN=>"inactive", MAX=>"active"},
- "dpt1.012" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(not_inverted)|(inverted))/ix, MIN=>"not_inverted", MAX=>"inverted"},
+ 'dpt1.011' => {CODE=>'dpt1', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|inactive|active)/ix, MIN=>'inactive', MAX=>'active'},
+ 'dpt1.012' => {CODE=>'dpt1', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|not_inverted|inverted)/ix, MIN=>'not_inverted', MAX=>'inverted'},
"dpt1.013" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(start_stop)|(cyclically))/ix, MIN=>"start_stop", MAX=>"cyclically"},
"dpt1.014" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(fixed)|(calculated))/ix, MIN=>"fixed", MAX=>"calculated"},
"dpt1.015" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(no_action)|(reset))/ix, MIN=>"no_action", MAX=>"reset"},
"dpt1.016" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(no_action)|(acknowledge))/ix, MIN=>"no_action", MAX=>"acknowledge"},
"dpt1.017" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(trigger)|(trigger))/ix, MIN=>"trigger", MAX=>"trigger"},
- "dpt1.018" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(not_occupied)|(occupied))/ix, MIN=>"not_occupied", MAX=>"occupied"},
+ 'dpt1.018' => {CODE=>'dpt1', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|not_occupied|occupied)/ix, MIN=>'not_occupied', MAX=>'occupied'},
"dpt1.019" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(closed)|(open))/ix, MIN=>"closed", MAX=>"open"},
"dpt1.021" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(logical_or)|(logical_and))/ix, MIN=>"logical_or", MAX=>"logical_and"},
"dpt1.022" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(scene_A)|(scene_B))/ix, MIN=>"scene_A", MAX=>"scene_B"},
@@ -314,7 +321,8 @@ sub Initialize {
"$readingFnAttributes "; #standard attributes
$hash->{noAutocreatedFilelog} = 1; # autocreate devices create no FileLog
$hash->{AutoCreate} = {"KNX_.*" => { ATTR => "disable:1"} }; # autocreate devices are disabled by default
- return undef; # really necessary?
+
+ return;
}
#Define this device
@@ -413,7 +421,7 @@ sub KNX_Define {
$gadOption = $gadArgs[1] if(defined($gadArgs[1]) && $gadArgs[1] =~ m/($PAT_GAD_OPTIONS)/ix);
$gadNoSuffix = 'noSuffix' if (join(q{ },@gadArgs) =~ m/nosuffix/ix);
- return "KNX_define ($name): -invalid option for group-number $gadNo. Use one of: $PAT_GAD_OPTIONS" if (defined($gadOption) && ($gadOption !~ m/^($PAT_GAD_OPTIONS)$/ix));
+ return "KNX_define ($name): -invalid option for group-number $gadNo. Use one of: $PAT_GAD_OPTIONS" if (defined($gadOption) && ($gadOption !~ m/^(?:$PAT_GAD_OPTIONS)$/ix)); #PBP
return "KNX_define ($name): -invalid suffix for group-number $gadNo. Use $PAT_GAD_SUFFIX" if (defined($gadNoSuffix) && ($gadNoSuffix !~ m/$PAT_GAD_SUFFIX/ix));
}
@@ -536,7 +544,7 @@ sub KNX_Define {
Log3 ($name, 5, "KNX_define ($name): -exit");
- return undef; # really necessary?
+ return;
}
#Release this device
@@ -640,7 +648,7 @@ sub KNX_Set {
$cmd = shift(@arg);
}
else { #oldsyntax
- (my $err, $targetGadName, $cmd) = KNX_Set_oldsyntax($hash,$targetGadName,@arg); ## process old syntax
+ (my $err, $targetGadName, $cmd) = KNX_Set_oldsyntax($hash,$targetGadName,@arg); ## process old syntax targetGadName contains command!
return $err if defined($err);
}
@@ -714,52 +722,61 @@ sub KNX_Set_oldsyntax {
#select another group, if the last arg starts with a g
if($na >= 1 && $a[$na - 1] =~ m/$PAT_GNO/ix) {
$groupnr = pop (@a);
- Log3 $name, 3, q{KNX_Set_oldsyntax: you are still using "old syntax", pls. change to "set } . "$name $groupnr $cmd " . join(q{ },@a) . q{"};
+ Log3 $name, 3, q{KNX_Set_syntax2: you are still using "old syntax", pls. change to "set } . "$name $groupnr $cmd " . join(q{ },@a) . q{"};
$groupnr =~ s/^g//gix; #remove "g"
}
#unknown groupnr
- return "KNX_Set_oldsyntax: group-no. not found" if((!defined($groupnr)) || ($groupnr eq q{}));
+#E04.72 return "KNX_Set_syntax2: group-no. not found" if((!defined($groupnr)) || ($groupnr eq q{}));
+
+ # if cmd contains g1: the check for valid gadnames failed !
+ # this is NOT oldsyntax, but a user-error!
+ if ($cmd =~ /^g[\d]/ix) { # E04.72 an invalid Gadname was specified
+ Log3 ($name,2,"KNX_Set_syntax2: an invalid gadName: $cmd was used in set-cmd");
+ return ("KNX_Set_syntax2: an invalid gadName: $cmd was used in set-cmd",q{},q{});
+ }
foreach my $key (keys %{$hash->{GADDETAILS}}) {
$targetGadName = $key if (int ($hash->{GADDETAILS}{$key}{NO}) == int ($groupnr));
}
- return "KNX_Set_oldsyntax: gadName not found for $groupnr" if(!defined($targetGadName));
+ return "KNX_Set_syntax2: gadName not found for $groupnr" if(!defined($targetGadName));
# all of the following cmd's need at least 1 Argument (or more)
return (undef, $targetGadName, $cmd) if (scalar(@a) <= 0);
my $code = $hash->{GADDETAILS}{$targetGadName}{MODEL};
- my $value = "$cmd " . join(q{ },@a); # default
- if ($cmd =~ m/$RAW/ix) { # perlcritic (ControlStructures::ProhibitCascadingIfElse) ?
+ my $value = $cmd;
+#E04.72 my $value = "$cmd " . join(q{ },@a); # default
+ if ($cmd =~ m/$RAW/ix) {
#check for 1-16 hex-digits
- return "KNX_Set_oldsyntax: $cmd $a[0] has wrong syntax. Use hex-format only." if ($a[0] !~ m/[0-9A-F]{1,16}/ix);
+ return "KNX_Set_syntax2: $cmd $a[0] has wrong syntax. Use hex-format only." if ($a[0] !~ m/[0-9A-F]{1,16}/ix);
$value = $a[0];
+
}
elsif ($cmd =~ m/$VALUE/ix) {
- return 'KNX_Set_oldsyntax: "value" not allowed for dpt1, dpt16 and dpt232' if ($code =~ m/(dpt1$)|(dpt16$)|(dpt232$)/ix);
-
+ return 'KNX_Set_syntax2: "value" not allowed for dpt1, dpt16 and dpt232' if ($code =~ m/(dpt1$)|(dpt16$)|(dpt232$)/ix);
$value = $a[0];
$value =~ s/,/\./gx;
+
}
#set string For each received telegram there will be a reading with containing the received value and the sender address. A (german) wiki page is available here: FHEM Wiki
Define
For every set, there will be a reading containing the sent value.
The reading <state> will be updated with the last sent or received value.
Set sends the given value to the bus.
If <gadName> is omitted, the first listed GAD of the device is used.
If the GAD is restricted in the definition with "get" or "listenonly", the set-command will be refused.
- For dpt1 and dpt1.001 valid values are on, off and toggle. Also the timer-functions can be used.
+ For dpt1 and dpt1.001 valid values are on, off, toggle and blink. Also the timer-functions can be used.
For all other binary DPT (dpt1.xxx) the min- and max-values can be used for en- and decoding alternatively to on/off.
After successful sending the value, it is stored in the readings <setName>.
Examples:
@@ -1881,7 +1916,9 @@ If you add the option "nosuffix", <getName>, <setName> and <putNaset lamp2 g1 off
set lamp2 g1 on-for-timer 10
set lamp2 g1 on-until 13:15:00
set lamp2 steuern on-until 13:15:00
set lamp3 steuern on-until 13:15:00
set lamp3 steuern toogle # lamp3 change state
set lamp3 steuern blink 2 4 # lamp3 on for 4 seconds, off for 4 seconds, 2 repeats
set myThermoDev g1 23.44
Common attributes
DPT - datapoint-types
-The following dpt are implemented and have to be assigned within the device definition.
+DPT - data-point-types
+The following dpt are implemented and have to be assigned within the device definition. + The values right to the dpt define the valid range of Set-command values and Get-command return values.
More complex examples:
-Examples can also be found on the (german) Wiki page.
Rollo:
define rollo KNX 0/10/12:dpt1.008:wdw1 0/10/13:dpt1
set rollo wdw1 down
moves down rollo at window 1