From ed14c6ac146bce01d0a96b5126d1eadb60fe677e Mon Sep 17 00:00:00 2001 From: erwin <> Date: Tue, 2 Nov 2021 12:53:01 +0000 Subject: [PATCH] 10_KNX.pm: multiple bugfixes, check (Forum Thread #122582) git-svn-id: https://svn.fhem.de/fhem/trunk@25169 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 1 + fhem/FHEM/10_KNX.pm | 1088 ++++++++++++++++++++----------------------- 2 files changed, 509 insertions(+), 580 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index 95c52d494..2068fb5f3 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: rework de- en-code, removed examples from cmdref->WIKI - change: 47_OBIS: Added patch from alkazaa - change: 58_RPI_1Wire: Support for multiple Busmasters, enhancements, fixes - bugfix: 88_HMCCU: Keep eventMap when resetting attributes diff --git a/fhem/FHEM/10_KNX.pm b/fhem/FHEM/10_KNX.pm index ea7ebc8b0..ad383d0a6 100644 --- a/fhem/FHEM/10_KNX.pm +++ b/fhem/FHEM/10_KNX.pm @@ -61,6 +61,9 @@ # fix off-for-timer # add wiki links # add blink cmd for dpt1, dpt1.001 +# MH 20211017 E04.80 rework decode- encode- ByDpt (cascading if/else != performance) +# fix stateregex once more +# removed examples from cmdref -> wiki package FHEM::KNX; ## no critic 'package' @@ -79,7 +82,7 @@ use GPUtils qw(GP_Import GP_Export); # Package Helper Fn ## no critic (ValuesAndExpressions::RequireNumberSeparators,ValuesAndExpressions::ProhibitMagicNumbers) ## no critic (RegularExpressions::RequireDotMatchAnything,RegularExpressions::RequireLineBoundaryMatching) ## no critic (ControlStructures::ProhibitPostfixControls) -## no critic (ControlStructures::ProhibitCascadingIfElse) +### no critic (ControlStructures::ProhibitCascadingIfElse) ## no critic (Documentation::RequirePodSections) ### import FHEM functions / global vars @@ -158,134 +161,158 @@ my $PAT_DPT16_CLR = qr/>CLR {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' => {CODE=>'dpt1', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT)/ix, MIN=>'off', MAX=>'on', SETLIST=>'on,off,toggle'}, + 'dpt1' => {CODE=>'dpt1', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT)/ix, MIN=>'off', MAX=>'on', SETLIST=>'on,off,toggle', + DEC=>\&dec_dpt1,ENC=>\&enc_dpt1,}, + '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', 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.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.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.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.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.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_0|trigger_1)/ix, MIN=>'trigger_0', MAX=>'trigger_1', SETLIST=>'trigger_0,trigger_1',}, '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"}, - "dpt1.023" => {CODE=>"dpt1", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|(move_(up_down|and_step_mode)))/ix, MIN=>"move_up_down", MAX=>"move_and_step_mode"}, + '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'}, + 'dpt1.023' => {CODE=>'dpt1', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DPT1_PAT|move_(up_down|and_step_mode))/ix, MIN=>'move_up_down', MAX=>'move_and_step_mode'}, #Step value (two-bit) - "dpt2" => {CODE=>"dpt2", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(on|off|forceon|forceoff)/ix, MIN=>undef, MAX=>undef, SETLIST=>'on,off,forceon,forceoff'}, - "dpt2.000" => {CODE=>"dpt2", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/(0?[0-3])/ix, MIN=>0, MAX=>3}, + 'dpt2' => {CODE=>'dpt2', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/(on|off|forceon|forceoff)/ix, MIN=>undef, MAX=>undef, SETLIST=>'on,off,forceon,forceoff', + DEC=>\&dec_dpt2,ENC=>\&enc_dpt2,}, + 'dpt2.000' => {CODE=>'dpt2', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/(0?[0-3])/ix, MIN=>0, MAX=>3}, #Step value (four-bit) - "dpt3" => {CODE=>"dpt3", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>-100, MAX=>100}, - "dpt3.007" => {CODE=>"dpt3", UNIT=>q{%}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>-100, MAX=>100}, + 'dpt3' => {CODE=>'dpt3', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>-100, MAX=>100, + DEC=>\&dec_dpt3,ENC=>\&enc_dpt3,}, + 'dpt3.007' => {CODE=>'dpt3', UNIT=>q{%}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>-100, MAX=>100}, # 1-Octet unsigned value - "dpt5" => {CODE=>"dpt5", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>0, MAX=>255}, - "dpt5.001" => {CODE=>"dpt5", UNIT=>q{%}, FACTOR=>100/255, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>0, MAX=>100}, - "dpt5.003" => {CODE=>"dpt5", UNIT=>q{°}, FACTOR=>360/255, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>0, MAX=>360}, - "dpt5.004" => {CODE=>"dpt5", UNIT=>q{%}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>0, MAX=>255}, + 'dpt5' => {CODE=>'dpt5', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>0, MAX=>255, + DEC=>\&dec_dpt5,ENC=>\&enc_dpt5,}, + 'dpt5.001' => {CODE=>'dpt5', UNIT=>q{%}, FACTOR=>100/255, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>0, MAX=>100}, + 'dpt5.003' => {CODE=>'dpt5', UNIT=>q{°}, FACTOR=>360/255, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>0, MAX=>360}, + 'dpt5.004' => {CODE=>'dpt5', UNIT=>q{%}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>0, MAX=>255}, # 1-Octet signed value - "dpt6" => {CODE=>"dpt6", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>-128, MAX=>127}, - "dpt6.001" => {CODE=>"dpt6", UNIT=>q{%}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>0, MAX=>100}, - "dpt6.010" => {CODE=>"dpt6", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>-128, MAX=>127}, + 'dpt6' => {CODE=>'dpt6', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>-128, MAX=>127, + DEC=>\&dec_dpt6,ENC=>\&enc_dpt6,}, + 'dpt6.001' => {CODE=>'dpt6', UNIT=>q{%}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>0, MAX=>100}, + 'dpt6.010' => {CODE=>'dpt6', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>-128, MAX=>127}, # 2-Octet unsigned Value - "dpt7" => {CODE=>"dpt7", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535}, - "dpt7.001" => {CODE=>"dpt7", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535}, - "dpt7.005" => {CODE=>"dpt7", UNIT=>q{s}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535}, - "dpt7.006" => {CODE=>"dpt7", UNIT=>q{m}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535}, - "dpt7.007" => {CODE=>"dpt7", UNIT=>q{h}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535}, - "dpt7.012" => {CODE=>"dpt7", UNIT=>q{mA}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535}, - "dpt7.013" => {CODE=>"dpt7", UNIT=>q{lux}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535}, - "dpt7.600" => {CODE=>"dpt7", UNIT=>q{K}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+]?\d{1,5}/ix, MIN=>0, MAX=>12000}, # Farbtemperatur + 'dpt7' => {CODE=>'dpt7', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535, + DEC=>\&dec_dpt7,ENC=>\&enc_dpt7,}, + 'dpt7.001' => {CODE=>'dpt7', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535}, + 'dpt7.005' => {CODE=>'dpt7', UNIT=>q{s}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535}, + 'dpt7.006' => {CODE=>'dpt7', UNIT=>q{m}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535}, + 'dpt7.007' => {CODE=>'dpt7', UNIT=>q{h}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535}, + 'dpt7.012' => {CODE=>'dpt7', UNIT=>q{mA}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535}, + 'dpt7.013' => {CODE=>'dpt7', UNIT=>q{lux}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>0, MAX=>65535}, + 'dpt7.600' => {CODE=>'dpt7', UNIT=>q{K}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+]?\d{1,5}/ix, MIN=>0, MAX=>12000}, # Farbtemperatur # 2-Octet signed Value - "dpt8" => {CODE=>"dpt8", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>-32768, MAX=>32767}, - "dpt8.005" => {CODE=>"dpt8", UNIT=>q{s}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>-32768, MAX=>32767}, - "dpt8.010" => {CODE=>"dpt8", UNIT=>q{%}, FACTOR=>0.01, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>-327.68, MAX=>327.67}, # min/max - "dpt8.011" => {CODE=>"dpt8", UNIT=>q{°}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>-32768, MAX=>32767}, + 'dpt8' => {CODE=>'dpt8', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>-32768, MAX=>32767, + DEC=>\&dec_dpt8,ENC=>\&enc_dpt8,}, + 'dpt8.005' => {CODE=>'dpt8', UNIT=>q{s}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>-32768, MAX=>32767}, + 'dpt8.010' => {CODE=>'dpt8', UNIT=>q{%}, FACTOR=>0.01, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>-327.68, MAX=>327.67}, # min/max + 'dpt8.011' => {CODE=>'dpt8', UNIT=>q{°}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,5}/ix, MIN=>-32768, MAX=>32767}, # 2-Octet Float value - "dpt9" => {CODE=>"dpt9", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.001" => {CODE=>"dpt9", UNIT=>q{°C}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-274, MAX=>670760}, - "dpt9.002" => {CODE=>"dpt9", UNIT=>q{K}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.003" => {CODE=>"dpt9", UNIT=>q{K/h}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.004" => {CODE=>"dpt9", UNIT=>q{lux}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.005" => {CODE=>"dpt9", UNIT=>q{m/s}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.006" => {CODE=>"dpt9", UNIT=>q{Pa}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.007" => {CODE=>"dpt9", UNIT=>q{%}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.008" => {CODE=>"dpt9", UNIT=>q{ppm}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.009" => {CODE=>"dpt9", UNIT=>q{m³/h}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.010" => {CODE=>"dpt9", UNIT=>q{s}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.011" => {CODE=>"dpt9", UNIT=>q{ms}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.020" => {CODE=>"dpt9", UNIT=>q{mV}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.021" => {CODE=>"dpt9", UNIT=>q{mA}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.022" => {CODE=>"dpt9", UNIT=>q{W/m²}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.023" => {CODE=>"dpt9", UNIT=>q{K/%}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.024" => {CODE=>"dpt9", UNIT=>q{kW}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.025" => {CODE=>"dpt9", UNIT=>q{l/h}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.026" => {CODE=>"dpt9", UNIT=>q{l/h}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.028" => {CODE=>"dpt9", UNIT=>q{km/h}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, - "dpt9.029" => {CODE=>"dpt9", UNIT=>q{g/m³}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, # Abs. Luftfeuchte - "dpt9.030" => {CODE=>"dpt9", UNIT=>q{μg/m³}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, # Dichte + 'dpt9' => {CODE=>'dpt9', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760, + DEC=>\&dec_dpt9,ENC=>\&enc_dpt9,}, + 'dpt9.001' => {CODE=>'dpt9', UNIT=>q{°C}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-274, MAX=>670760}, + 'dpt9.002' => {CODE=>'dpt9', UNIT=>q{K}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.003' => {CODE=>'dpt9', UNIT=>q{K/h}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.004' => {CODE=>'dpt9', UNIT=>q{lux}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.005' => {CODE=>'dpt9', UNIT=>q{m/s}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.006' => {CODE=>'dpt9', UNIT=>q{Pa}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.007' => {CODE=>'dpt9', UNIT=>q{%}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.008' => {CODE=>'dpt9', UNIT=>q{ppm}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.009' => {CODE=>'dpt9', UNIT=>q{m³/h}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.010' => {CODE=>'dpt9', UNIT=>q{s}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.011' => {CODE=>'dpt9', UNIT=>q{ms}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.020' => {CODE=>'dpt9', UNIT=>q{mV}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.021' => {CODE=>'dpt9', UNIT=>q{mA}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.022' => {CODE=>'dpt9', UNIT=>q{W/m²}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.023' => {CODE=>'dpt9', UNIT=>q{K/%}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.024' => {CODE=>'dpt9', UNIT=>q{kW}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.025' => {CODE=>'dpt9', UNIT=>q{l/h}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.026' => {CODE=>'dpt9', UNIT=>q{l/h}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.028' => {CODE=>'dpt9', UNIT=>q{km/h}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, + 'dpt9.029' => {CODE=>'dpt9', UNIT=>q{g/m³}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, # Abs. Luftfeuchte + 'dpt9.030' => {CODE=>'dpt9', UNIT=>q{μg/m³}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[-+]?(?:\d*[\.\,])?\d+/ix, MIN=>-670760, MAX=>670760}, # Dichte # Time of Day - "dpt10" => {CODE=>"dpt10", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_TIME|now)/ix, MIN=>undef, MAX=>undef}, + 'dpt10' => {CODE=>'dpt10', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_TIME|now)/ix, MIN=>undef, MAX=>undef, + DEC=>\&dec_dpt10,ENC=>\&enc_dpt10,}, # Date - "dpt11" => {CODE=>"dpt11", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DATE|now)/ix, MIN=>undef, MAX=>undef}, + 'dpt11' => {CODE=>'dpt11', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DATE|now)/ix, MIN=>undef, MAX=>undef, + DEC=>\&dec_dpt11,ENC=>\&enc_dpt11,}, # 4-Octet unsigned value (handled as dpt7) - "dpt12" => {CODE=>"dpt12", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,10}/ix, MIN=>0, MAX=>4294967295}, + 'dpt12' => {CODE=>'dpt12', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,10}/ix, MIN=>0, MAX=>4294967295, + DEC=>\&dec_dpt12,ENC=>\&enc_dpt12,}, # 4-Octet Signed Value - "dpt13" => {CODE=>"dpt13", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,10}/ix, MIN=>-2147483648, MAX=>2147483647}, - "dpt13.010" => {CODE=>"dpt13", UNIT=>q{Wh}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,10}/ix, MIN=>-2147483648, MAX=>2147483647}, - "dpt13.013" => {CODE=>"dpt13", UNIT=>q{kWh}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,10}/ix, MIN=>-2147483648, MAX=>2147483647}, + 'dpt13' => {CODE=>'dpt13', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,10}/ix, MIN=>-2147483648, MAX=>2147483647, + DEC=>\&dec_dpt13,ENC=>\&enc_dpt13,}, + 'dpt13.010' => {CODE=>'dpt13', UNIT=>q{Wh}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,10}/ix, MIN=>-2147483648, MAX=>2147483647}, + 'dpt13.013' => {CODE=>'dpt13', UNIT=>q{kWh}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,10}/ix, MIN=>-2147483648, MAX=>2147483647}, # 4-Octet single precision float - "dpt14" => {CODE=>"dpt14", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/ix, MIN=>undef, MAX=>undef}, - "dpt14.019" => {CODE=>"dpt14", UNIT=>q{A}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/ix, MIN=>undef, MAX=>undef}, - "dpt14.027" => {CODE=>"dpt14", UNIT=>q{V}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/ix, MIN=>undef, MAX=>undef}, - "dpt14.033" => {CODE=>"dpt14", UNIT=>q{Hz}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/ix, MIN=>undef, MAX=>undef}, - "dpt14.056" => {CODE=>"dpt14", UNIT=>q{W}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/ix, MIN=>undef, MAX=>undef}, - "dpt14.068" => {CODE=>"dpt14", UNIT=>q{°C}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/ix, MIN=>undef, MAX=>undef}, - "dpt14.076" => {CODE=>"dpt14", UNIT=>q{m³}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/ix, MIN=>undef, MAX=>undef}, - "dpt14.057" => {CODE=>"dpt14", UNIT=>q{cos Φ}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/ix, MIN=>undef, MAX=>undef}, + 'dpt14' => {CODE=>'dpt14', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/ix, MIN=>undef, MAX=>undef, + DEC=>\&dec_dpt14,ENC=>\&enc_dpt14,}, + 'dpt14.019' => {CODE=>'dpt14', UNIT=>q{A}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/ix, MIN=>undef, MAX=>undef}, + 'dpt14.027' => {CODE=>'dpt14', UNIT=>q{V}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/ix, MIN=>undef, MAX=>undef}, + 'dpt14.033' => {CODE=>'dpt14', UNIT=>q{Hz}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/ix, MIN=>undef, MAX=>undef}, + 'dpt14.056' => {CODE=>'dpt14', UNIT=>q{W}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/ix, MIN=>undef, MAX=>undef}, + 'dpt14.068' => {CODE=>'dpt14', UNIT=>q{°C}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/ix, MIN=>undef, MAX=>undef}, + 'dpt14.076' => {CODE=>'dpt14', UNIT=>q{m³}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/ix, MIN=>undef, MAX=>undef}, + 'dpt14.057' => {CODE=>'dpt14', UNIT=>q{cos Φ}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,40}[.,]?\d{1,4}/ix, MIN=>undef, MAX=>undef}, # 14-Octet String - "dpt16" => {CODE=>"dpt16", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/ix, MIN=>undef, MAX=>undef, SETLIST=>'multiple,>CLR<'}, - "dpt16.000" => {CODE=>"dpt16", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/ix, MIN=>undef, MAX=>undef, SETLIST=>'multiple,>CLR<'}, - "dpt16.001" => {CODE=>"dpt16", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/ix, MIN=>undef, MAX=>undef, SETLIST=>'multiple,>CLR<'}, + 'dpt16' => {CODE=>'dpt16', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/ix, MIN=>undef, MAX=>undef, SETLIST=>'multiple,>CLR<', + DEC=>\&dec_dpt16,ENC=>\&enc_dpt16,}, + 'dpt16.000' => {CODE=>'dpt16', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/ix, MIN=>undef, MAX=>undef, SETLIST=>'multiple,>CLR<'}, + 'dpt16.001' => {CODE=>'dpt16', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/.{1,14}/ix, MIN=>undef, MAX=>undef, SETLIST=>'multiple,>CLR<'}, # Scene, 0-63 - "dpt17.001" => {CODE=>"dpt5", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>0, MAX=>63}, + 'dpt17' => {CODE=>'dpt5', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>0, MAX=>63, + DEC=>\&dec_dpt5,ENC=>\&enc_dpt5,}, + 'dpt17.001' => {CODE=>'dpt5', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>0, MAX=>63}, # Scene, 1-64 - "dpt18.001" => {CODE=>"dpt5", UNIT=>q{}, FACTOR=>1, OFFSET=>1, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>1, MAX=>64}, + 'dpt18' => {CODE=>'dpt5', UNIT=>q{}, FACTOR=>1, OFFSET=>1, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>1, MAX=>64, + DEC=>\&dec_dpt5,ENC=>\&enc_dpt5,}, + 'dpt18.001' => {CODE=>'dpt5', UNIT=>q{}, FACTOR=>1, OFFSET=>1, PATTERN=>qr/[+-]?\d{1,3}/ix, MIN=>1, MAX=>64}, #date and time - "dpt19" => {CODE=>"dpt19", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DATE$PAT_DTSEP$PAT_TIME|now)/ix, MIN=>undef, MAX=>undef}, - "dpt19.001" => {CODE=>"dpt19", UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DATE$PAT_DTSEP$PAT_TIME|now)/ix, MIN=>undef, MAX=>undef}, + 'dpt19' => {CODE=>'dpt19', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DATE$PAT_DTSEP$PAT_TIME|now)/ix, MIN=>undef, MAX=>undef, + DEC=>\&dec_dpt19,ENC=>\&enc_dpt19,}, + 'dpt19.001' => {CODE=>'dpt19', UNIT=>q{}, FACTOR=>undef, OFFSET=>undef, PATTERN=>qr/($PAT_DATE$PAT_DTSEP$PAT_TIME|now)/ix, MIN=>undef, MAX=>undef}, # HVAC mode, 1Byte - "dpt20.102" => {CODE=>"dpt20", UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/(auto|comfort|standby|(economy|night)|(protection|frost|heat))/ix, MIN=>undef, MAX=>undef, SETLIST=>'Auto,Comfort,Standby,Economy,Protection'}, ## no critic (RegularExpressions::ProhibitComplexRegexes) + 'dpt20' => {CODE=>'dpt20', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/(auto|comfort|standby|(economy|night)|(protection|frost|heat))/ix, MIN=>undef, MAX=>undef, ## no critic (RegularExpressions::ProhibitComplexRegexes) + SETLIST=>'Auto,Comfort,Standby,Economy,Protection', DEC=>\&dec_dpt20,ENC=>\&enc_dpt20,}, + 'dpt20.102' => {CODE=>'dpt20', UNIT=>q{}, FACTOR=>1, OFFSET=>0, PATTERN=>qr/(auto|comfort|standby|(economy|night)|(protection|frost|heat))/ix, MIN=>undef, MAX=>undef, ## no critic (RegularExpressions::ProhibitComplexRegexes) + SETLIST=>'Auto,Comfort,Standby,Economy,Protection'}, # 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,} ); #Init this device @@ -726,12 +753,9 @@ sub KNX_Set_oldsyntax { $groupnr =~ s/^g//gix; #remove "g" } - #unknown groupnr -#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 + if ($cmd =~ /^g[\d]/ix) { # 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{}); } @@ -746,7 +770,6 @@ sub KNX_Set_oldsyntax { my $code = $hash->{GADDETAILS}{$targetGadName}{MODEL}; my $value = $cmd; -#E04.72 my $value = "$cmd " . join(q{ },@a); # default if ($cmd =~ m/$RAW/ix) { #check for 1-16 hex-digits @@ -774,9 +797,6 @@ sub KNX_Set_oldsyntax { $value = lc($a[0]); } -#E04.72 elsif ($code =~ m/^dpt16/ix) { # Forum #122779 -# $value = $cmd if ($cmd eq q{}); # cmd contains first word - rest will be joined in KNX_Set -# } return (undef, $targetGadName, $value); } @@ -1023,6 +1043,7 @@ sub KNX_Parse { } #get list from device-hashes using given gadCode (==destination) + # check on cmd line with: {PrintHash($modules{KNX}{defptr},3) } my @deviceList = @{$modules{KNX}{defptr}{$gadCode}}; #process message for all affected devices and gad's @@ -1282,9 +1303,9 @@ sub KNX_checkAndClean { return if ($found == 0); - $value = KNX_limit ($hash, $value, $gadName, undef); +#E04.80 $value = KNX_limit ($hash, $value, $gadName, undef); - Log3 ($name, 3, "KNX_checkAndClean: name= $name, value= $orgValue was casted to $value") if ($orgValue ne $value); # add dev-name + Log3 ($name, 3, "KNX_checkAndClean: name= $name, gadName= $gadName, value= $orgValue was casted to $value") if ($orgValue ne $value); #E04.80 add dev-name Log3 ($name, 5, "KNX_checkAndClean -exit: value= $value, gadName= $gadName, model= $model, pattern= $pattern"); return $value; @@ -1321,8 +1342,8 @@ sub KNX_replaceByRegex { elsif ($regPair[0] eq $tempVal) { # complete match $retVal = $regPair[1]; } - elsif (($input !~ /$regPair[0]/x) && ($regPair[0] =~ /[:]/x)) { - $retVal = $input; + elsif (($input !~ /$regPair[0]/x) && ($regPair[0] =~ /[:]/x)) { # value dont match! + next; #E04.80 } else { #replace value @@ -1337,14 +1358,15 @@ sub KNX_replaceByRegex { # limit numeric values. Valid directions: encode, decode sub KNX_limit { - my ($hash, $value, $gadName, $direction) = @_; + my ($hash, $value, $model, $direction) = @_; +#E04.80 my ($hash, $value, $gadName, $direction) = @_; #continue only if numeric value return $value if (! looks_like_number ($value)); return $value if (! defined($direction)); my $name = $hash->{NAME}; - my $model = $hash->{GADDETAILS}{$gadName}{MODEL}; +#E04.80 my $model = $hash->{GADDETAILS}{$gadName}{MODEL}; my $retVal = $value; #get correction details @@ -1377,8 +1399,10 @@ sub KNX_limit { $logString .= " OFFSET: $offset" if (defined ($offset)); $logString .= " MIN: $min" if (defined ($min)); $logString .= " MAX: $max" if (defined ($max)); - Log3 ($name, 5, "KNX_limit: $gadName $logString"); - Log3 ($name, 4, "KNX_limit: $gadName modified... Output: $retVal, Input: $value, Model: $model") if ($retVal != $value); +#E04.80 Log3 ($name, 5, "KNX_limit: $gadName $logString"); +# Log3 ($name, 4, "KNX_limit: $gadName modified... Output: $retVal, Input: $value, Model: $model") if ($retVal != $value); + Log3 ($name, 5, "KNX_limit: $logString"); +# Log3 ($name, 4, "KNX_limit: modified... Input: $value, Output: $retVal, Model: $model") if ($retVal != $value); return $retVal; } @@ -1391,7 +1415,6 @@ sub KNX_eval { my $code = EvalSpecials($evalString,("%hash" => $hash, '%name' => $name, '%gadName' => $gadName, '%state' => $state)); # prepare vars for AnalyzePerlCommand $retVal = AnalyzeCommandChain(undef, $code); -# $retVal = AnalyzePerlCommand(undef, $code); $retVal = "ERROR" if (not defined ($retVal)); if ($retVal =~ /(^Forbidden|error)/ix) { # eval error or forbidden by Authorize @@ -1412,197 +1435,30 @@ sub KNX_encodeByDpt { #return unchecked, if this is a autocreate-device return if ($model eq $MODELERR); - #this one stores the translated value (readble) - my $numval = 0; # default +# #this one stores the translated value (readble) +# my $numval = 0; # default #this one stores the translated hex-value my $hexval = undef; - Log3 ($name, 5, "KNX_encodeByDpt: $gadName model: $model, code: $code, value: $value"); + Log3 ($name, 5, "KNX_encodeByDpt -enter: $gadName model: $model, code: $code, value: $value"); - $value = KNX_limit ($hash, $value, $gadName, "ENCODE"); + my $ivalue = $value; #E04.80 save for compare + $value = KNX_limit ($hash, $value, $model, 'ENCODE'); #E04.80 +#E04.80 $value = KNX_limit ($hash, $value, $gadName, 'ENCODE'); + Log3 ($name, 4, "KNX_limit: $gadName modified... Input: $ivalue, Output: $value, Model: $model") if ($ivalue ne $value); #E04.80 - #Binary value - if ($code eq "dpt1") { - $numval = 1 if ($value =~ m/(1|on)$/ix); - $numval = 1 if ($value eq $dpttypes{$model}{MAX}); # dpt1.011 problem - - $hexval = sprintf("%.2x",$numval); - } - #Step value (two-bit) - elsif ($code eq "dpt2") { - $numval = $value if ($value =~ m/^0?[0-3]$/ix); ## JoeALLb request - $numval = 0 if ($value =~ m/off/ix); - $numval = 1 if ($value =~ m/on/ix); - $numval = 2 if ($value =~ m/forceoff/i); - $numval = 3 if ($value =~ m/forceon/i); - - $hexval = sprintf("%.2x",$numval); - } - #Step value (four-bit) - elsif ($code eq "dpt3") { - my $sign = ($value >=0 )?1:0; - $value = abs($value); - - my @values = qw( 75 50 25 12 6 3 1 ); - my $i = 0; - foreach my $key (@values) { - $i++; - if ($value >= $key) { - $numval = $i; - last; - } - } - $numval += 8 if ($sign == 1); - $hexval = sprintf("%.2x",$numval); - } - #1-Octet unsigned value - elsif ($code eq "dpt5") { - $hexval = sprintf("00%.2x",$value); - } - #1-Octet signed value - elsif ($code eq "dpt6") { - #build 2-complement - - $numval = unpack("C", pack("c", $value)); - $hexval = sprintf("00%.2x",$numval); - } - #2-Octet unsigned Value - elsif ($code eq "dpt7") { - $hexval = sprintf("00%.4x",$value); - } - #2-Octet signed Value - elsif ($code eq "dpt8") { - #build 2-complement - - $numval = unpack("S", pack("s", $value)); - $hexval = sprintf("00%.4x",$numval); - } - #2-Octet Float value - elsif ($code eq "dpt9") { - my $sign = ($value <0 ? 0x8000 : 0); - my $exp = 0; - - my $mant = $value * 100; - while (abs($mant) > 0x07FF) { - $mant /= 2; - $exp++; - } - $numval = $sign | ($exp << 11) | ($mant & 0x07FF); - $hexval = sprintf("00%.4x",$numval); - } - #Time of Day - elsif ($code eq "dpt10") { - if ($value =~ m/now/ix) { - #get actual time - my ($secs,$mins,$hours,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); - - #add offsets - $year+=1900; - $mon++; - # calculate offset for weekday - $wday = 7 if ($wday == 0); - $hours += 32 * $wday; - - $value = "$hours:$mins:$secs"; - $numval = $secs + ($mins << 8) + ($hours << 16); - } - else { - my ($hh, $mm, $ss) = split(/:/x, $value); - $numval = $ss + ($mm << 8) + ($hh << 16); - } - $hexval = sprintf("00%.6x",$numval); - } - #Date - elsif ($code eq "dpt11") { - if ($value =~ m/now/ix) { - #get actual time - my ($secs,$mins,$hours,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); - - #add offsets - $year += 1900; - $mon++; - # calculate offset for weekday - $wday = 7 if ($wday eq "0"); - - $value = "$mday.$mon.$year"; - $numval = ($year - 2000) + ($mon << 8) + ($mday << 16); - } - else { - my ($dd, $mm, $yyyy) = split (/\./x, $value); - if ($yyyy >= 2000) { - $yyyy -= 2000; - } - else { - $yyyy -= 1900; - } - $numval = ($yyyy) + ($mm << 8) + ($dd << 16); - } - $hexval = sprintf("00%.6x",$numval); - } - #4-Octet unsigned value - elsif ($code eq "dpt12") { - $hexval = sprintf("00%.8x",$value); - } - #4-Octet Signed Value - elsif ($code eq "dpt13") { - #build 2-complement - $numval = unpack("L", pack("l", $value)); - $hexval = sprintf("00%.8x",$numval); - } - #4-Octet single precision float - elsif ($code eq "dpt14") { - $numval = unpack("L", pack("f", $value)); - $hexval = sprintf("00%.8x",$numval); - } - #14-Octet String - elsif ($code eq "dpt16") { - #convert to latin-1 - $numval = encode("iso-8859-1", decode("utf8", $value)); - - #convert to hex-string - my $dat = unpack "H*", $numval; - - $dat = '00' if ($value =~ /^$PAT_DPT16_CLR/ix); # send all zero string if "clear line string" - - #format for 14-byte-length and replace trailing blanks with zeros - $hexval = sprintf("00%-28s",$dat); - $hexval =~ s/\s/0/gx; - } - #DateTime - elsif ($code eq "dpt19") { - my $ts = time; # default or when "now" is given - # if no match we assume now and use current date/time - $ts = fhemTimeLocal($6, $5, $4, $1, $2-1, $3 - 1900) if ($value =~ m/^$PAT_DATE$PAT_DTSEP$PAT_TIME/x); - my ($secs,$mins,$hours,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($ts); - $wday = 7 if ($wday eq "0"); # calculate offset for weekday - $hours += ($wday << 5); # add day of week - my $status1 = 0x20; # Fault=0, WD = 0, NWD = 1 (WD Field valid), NY = 0, ND = 0, NDOW= 0,NT=0, SUTI = 0 - $status1 += 1 if ($isdst == 1); - my $status0 = 0x00; # CLQ=0 - $mon++; - $hexval = sprintf("00%02x%02x%02x%02x%02x%02x%02x%02x",$year,$mon,$mday,$hours,$mins,$secs,$status1,$status0); - } - # HVAC 1Byte - elsif ($code eq "dpt20") { - $numval = 0 if ($value =~ m/Auto/ix); - $numval = 1 if ($value =~ m/Comfort/ix); - $numval = 2 if ($value =~ m/Standby/ix); - $numval = 3 if ($value =~ m/Economy/ix); - $numval = 4 if ($value =~ m/Protection/ix); - $hexval = sprintf("00%.2x",$numval); - } - #RGB-Code - elsif ($code eq "dpt232") { - $hexval = "00" . $value; +###rework begin + if (ref($dpttypes{$code}->{ENC}) eq 'CODE') { + $hexval = $dpttypes{$code}->{ENC}->($value, $model); + Log3 ($name, 5, "KNX_encodeByDpt -exit: $gadName, model: $model, code: $code, value: $value, hexval: $hexval"); + return $hexval; } else { Log3 ($name, 2, "KNX_encodeByDpt: $gadName, model: $model not valid"); - return; } - - Log3 ($name, 5, "KNX_encodeByDpt -exit: model: $model, code: $code, value: $value, hexval: $hexval"); - return $hexval; + return; } +###rework end # decode KNX-Message according DPT sub KNX_decodeByDpt { @@ -1616,191 +1472,377 @@ sub KNX_decodeByDpt { #return unchecked, if this is a autocreate-device return if ($model eq $MODELERR); - #this one stores the translated value (readble) - my $numval = undef; #this one contains the return-value my $state = undef; Log3 ($name, 5, "KNX_decodeByDpt -enter: model: $model, code: $code, value: $value, length-value: " . length($value)); - #Binary value - if ($code eq "dpt1") { - $numval = hex ($value); - $numval = ($numval & 0x01); - $state = $dpttypes{"$model"}{MIN}; # default +###rework begin + if (ref($dpttypes{$code}->{DEC}) eq 'CODE') { + $state = $dpttypes{$code}->{DEC}->($value, $model); + my $unit = $dpttypes{$model}{UNIT}; + $state = $state . q{ } . $unit if (defined ($unit) && ($unit ne q{})); #append unit, if supplied - $state = $dpttypes{"$model"}{MAX} if ($numval == 1); - } - #Step value (two-bit) - elsif ($code eq "dpt2") { - $numval = hex ($value); - $state = ($numval & 0x03); - my @dpt2txt = qw(off on forceOff forceOn); - $state = $dpt2txt[$state] if ($model ne 'dpt2.000') # JoeALLb request; - - } - #Step value (four-bit) - elsif ($code eq "dpt3") { - $numval = hex ($value); - $numval = $numval & 0x0F; - my $dir = ($numval & 0x08) >> 3; - my $step = ($numval & 0x07); - my $stepcode = 0; - if ($step > 0) { - $stepcode = int(100 / (2**($step-1))); - $stepcode *= -1 if ($dir == 0); - } - $state = sprintf ("%d", $stepcode); - } - #1-Octet unsigned value - elsif ($code eq "dpt5") { - $numval = hex ($value); - $state = KNX_limit ($hash, $numval, $gadName, "DECODE"); - - $state = sprintf ("%.0f", $state); - } - #1-Octet signed value - elsif ($code eq "dpt6") { - $numval = hex ($value); - $numval = unpack("c",pack("C",$numval)); - $state = KNX_limit ($hash, $numval, $gadName, "DECODE"); - - $state = sprintf ("%d", $state); - } - #2-Octet unsigned Value - elsif ($code eq "dpt7") { - $numval = hex ($value); - $state = KNX_limit ($hash, $numval, $gadName, "DECODE"); - - $state = sprintf ("%.0f", $state); - } - #2-Octet signed Value - elsif ($code eq "dpt8") { - $numval = hex ($value); - $numval = unpack("s",pack("S",$numval)); - $state = KNX_limit ($hash, $numval, $gadName, "DECODE"); - - $state = sprintf ("%d", $state); - } - #2-Octet Float value - elsif ($code eq "dpt9") { - $numval = hex($value); - my $sign = 1; - $sign = -1 if(($numval & 0x8000) > 0); - my $exp = ($numval & 0x7800) >> 11; - my $mant = ($numval & 0x07FF); - $mant = -(~($mant-1) & 0x07FF) if($sign == -1); - $numval = (1 << $exp) * 0.01 * $mant; - - $numval = KNX_limit ($hash, $numval, $gadName, "DECODE"); - - $state = sprintf ("%.2f","$numval"); - } - #Time of Day - elsif ($code eq "dpt10") { - $numval = hex($value); - my $hours = ($numval & 0x1F0000) >> 16; - my $mins = ($numval & 0x3F00) >> 8; - my $secs = ($numval & 0x3F); - my $wday = ($numval & 0xE00000) >> 21; - my @wdays = (q{},'Monday, ','Tuesday, ','Wednesday, ','Thursday, ','Friday, ','Saturday, ','Sunday, '); - - $state = sprintf("%02d:%02d:%02d",$hours,$mins,$secs); - # $state = sprintf("%s%02d:%02d:%02d",$wdays[$wday],$hours,$mins,$secs); # new option ? - } - #Date - elsif ($code eq "dpt11") { - $numval = hex($value); - my $day = ($numval & 0x1F0000) >> 16; - my $month = ($numval & 0x0F00) >> 8; - my $year = ($numval & 0x7F); - #translate year (21st cent if <90 / else 20th century) - $year += 1900 if($year >= 90); - $year += 2000 if($year < 90); - - $state = sprintf("%02d.%02d.%04d",$day,$month,$year); - } - #4-Octet unsigned value (handled as dpt7) - elsif ($code eq "dpt12") { - $numval = hex ($value); - $state = KNX_limit ($hash, $numval, $gadName, "DECODE"); - - $state = sprintf ("%.0f", $state); - } - #4-Octet Signed Value - elsif ($code eq "dpt13") { - $numval = hex ($value); - $numval = unpack("l",pack("L",$numval)); - $state = KNX_limit ($hash, $numval, $gadName, "DECODE"); - - $state = sprintf ("%d", $state); - } - #4-Octet single precision float - elsif ($code eq "dpt14") { - $numval = unpack "f", pack "L", hex ($value); - $numval = KNX_limit ($hash, $numval, $gadName, "DECODE"); - - $state = sprintf ("%.3f","$numval"); - } - #14-Octet String - elsif ($code eq "dpt16") { - $numval = 0; - $state = q{}; - $value =~ s/\s*$//gx; # strip trailing blanks - - $state = pack("H*",$value); - - #convert to latin-1 - $state = encode ("utf8", $state) if ($model =~ m/16.001/x); - - $state = q{} if ($state =~ m/^[\x00]/ix); # case all zeros received - - $state =~ s/[\x00-\x1F]+//gx; # remove non printable chars - } - #DateTime - elsif ($code eq "dpt19") { - $numval = substr($value,-16); # strip off 1st byte - my $time = hex (substr ($numval, 6, 6)); - my $date = hex (substr ($numval, 0, 6)); - my $secs = ($time & 0x3F) >> 0; - my $mins = ($time & 0x3F00) >> 8; - my $hours = ($time & 0x1F0000) >> 16; - my $day = ($date & 0x1F) >> 0; - my $month = ($date & 0x0F00) >> 8; - my $year = ($date & 0xFF0000) >> 16; - #extras - my $wday = ($time & 0xE00000) >> 21; # 0 = anyday/not valid, 1= Monday,... - $year += 1900; - - $state = sprintf("%02d.%02d.%04d_%02d:%02d:%02d", $day, $month, $year, $hours, $mins, $secs); - } - elsif ($code eq "dpt20") { - $numval = hex ($value); - $numval = ($numval & 0xff); - my @dpt20_102txt = qw(Auto Comfort Standby Economy Protection reserved); - $numval = 5 if ($numval > 4); # dpt20.102 - - $state = $dpt20_102txt[$numval]; - } - #RGB-Code - elsif ($code eq "dpt232") { - $numval = hex ($value); - - $state = sprintf ("%.6x",$numval); + Log3 ($name, 5, "KNX_decodeByDpt -exit: model: $model, code: $code, value: $value, state: $state"); + return $state; } else { Log3 ($name, 2, "KNX_decodeByDpt: $model, no valid model defined"); - return; } + return; +} +###rework end - #append unit, if supplied - my $unit = $dpttypes{$model}{UNIT}; - $state = $state . q{ } . $unit if (defined ($unit) && ($unit ne q{})); - Log3 ($name, 5, "KNX_decodeByDpt -exit: model: $model, code: $code, value: $value, state: $state"); +############################ +### encode sub functions ### +sub enc_dpt1 { #Binary value + my $value = shift; + my $model = shift; + my $numval = 0; #default + $numval = 1 if ($value =~ m/(1|on)$/ix); + $numval = 1 if ($value eq $dpttypes{$model}{MAX}); # dpt1.011 problem + return sprintf("%.2x",$numval); +} + +sub enc_dpt2 { #Step value (two-bit) + my $value = shift; + my $dpt2list = {off => 0, on => 1, forceoff => 2, forceon =>3}; + my $numval = $dpt2list->{lc($value)}; + $numval = $value if ($value =~ m/^0?[0-3]$/ix); ## JoeALLb request + return sprintf("%.2x",$numval); +} + +sub enc_dpt3 { #Step value (four-bit) + my $value = shift; + my $numval = 0; + my $sign = ($value >=0 )?1:0; + $value = abs($value); + my @values = qw( 75 50 25 12 6 3 1 ); +# my $i = 0; + foreach my $key (@values) { +# $i++; + $numval++; + if ($value >= $key) { +# $numval = $i; + last; + } + } + $numval += 8 if ($sign == 1); + return sprintf("%.2x",$numval); +} + +sub enc_dpt5 { #1-Octet unsigned value + return sprintf("00%.2x",shift); +} + +sub enc_dpt6 { #1-Octet signed value + #build 2-complement + my $numval = unpack("C", pack("c", shift)); + return sprintf("00%.2x",$numval); +} + +sub enc_dpt7 { #2-Octet unsigned Value + return sprintf("00%.4x",shift); +} + +sub enc_dpt8 { #2-Octet signed Value + #build 2-complement + my $numval = unpack("S", pack("s", shift)); + return sprintf("00%.4x",$numval); +} + +sub enc_dpt9 { #2-Octet Float value + my $value = shift; + my $sign = ($value <0 ? 0x8000 : 0); + my $exp = 0; + my $mant = $value * 100; + while (abs($mant) > 0x07FF) { + $mant /= 2; + $exp++; + } + my $numval = $sign | ($exp << 11) | ($mant & 0x07FF); + return sprintf("00%.4x",$numval); +} + +sub enc_dpt10 { #Time of Day + my $value = shift; + my $numval = 0; + if ($value =~ m/now/ix) { + #get actual time + my ($secs,$mins,$hours,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); + #add offsets + $year+=1900; + $mon++; + # calculate offset for weekday + $wday = 7 if ($wday == 0); + $hours += 32 * $wday; + } + else { + my ($hh, $mm, $ss) = split(/:/x, $value); + $numval = $ss + ($mm << 8) + ($hh << 16); + } + return sprintf("00%.6x",$numval); +} + +sub enc_dpt11 { #Date + my $value = shift; + my $numval = 0; + if ($value =~ m/now/ix) { + #get actual time + my ($secs,$mins,$hours,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); + #add offsets + $year+=1900; + $mon++; + # calculate offset for weekday + $wday = 7 if ($wday == 0); + $value = "$mday.$mon.$year"; + $numval = ($year - 2000) + ($mon << 8) + ($mday << 16); + } + else { + my ($dd, $mm, $yyyy) = split (/\./x, $value); + if ($yyyy >= 2000) { + $yyyy -= 2000; + } + else { + $yyyy -= 1900; + } + $numval = ($yyyy) + ($mm << 8) + ($dd << 16); + } + return sprintf("00%.6x",$numval); +} + +sub enc_dpt12 { #4-Octet unsigned value + return sprintf("00%.8x",shift); +} + +sub enc_dpt13 {#4-Octet Signed Value + #build 2-complement + my $numval = unpack("L", pack("l", shift)); + return sprintf("00%.8x",$numval); +} + +sub enc_dpt14 { #4-Octet single precision float + my $numval = unpack("L", pack("f", shift)); + return sprintf("00%.8x",$numval); +} + +sub enc_dpt16 { #14-Octet String + my $value = shift; + #convert to latin-1 + my $numval = encode("iso-8859-1", decode("utf8", $value)); + #convert to hex-string + my $dat = unpack "H*", $numval; + $dat = '00' if ($value =~ /^$PAT_DPT16_CLR/ix); # send all zero string if "clear line string" + #format for 14-byte-length and replace trailing blanks with zeros + my $hexval = sprintf("00%-28s",$dat); + $hexval =~ s/\s/0/gx; + return $hexval; +} + +sub enc_dpt19 { #DateTime + my $value = shift; + my $ts = time; # default or when "now" is given + # if no match we assume now and use current date/time + if ($value =~ m/^$PAT_DATE$PAT_DTSEP$PAT_TIME/x) { + $ts = fhemTimeLocal($6, $5, $4, $1, $2-1, $3 - 1900); # if ($value =~ m/^$PAT_DATE$PAT_DTSEP$PAT_TIME/x); + } + my ($secs,$mins,$hours,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($ts); + $wday = 7 if ($wday eq "0"); # calculate offset for weekday + $hours += ($wday << 5); # add day of week + my $status1 = 0x20; # Fault=0, WD = 0, NWD = 1 (WD Field valid), NY = 0, ND = 0, NDOW= 0,NT=0, SUTI = 0 + $status1 += 1 if ($isdst == 1); + my $status0 = 0x00; # CLQ=0 + $mon++; + return sprintf("00%02x%02x%02x%02x%02x%02x%02x%02x",$year,$mon,$mday,$hours,$mins,$secs,$status1,$status0); +} + +sub enc_dpt20 { # HVAC 1Byte + my $value = shift; + my $dpt20list = {auto => 0, comfort => 1, standby => 2, economy => 3, protection => 4,}; + my $numval = $dpt20list->{lc($value)}; + $numval = 5 if (! defined($numval)); + + return sprintf("00%.2x",$numval); +} + +sub enc_dpt232 { #RGB-Code + return "00" . shift; +} + +############################ +### decode sub functions ### +sub dec_dpt1 { #Binary value + my $value = shift; + my $model = shift; + my $numval = hex ($value); + $numval = ($numval & 0x01); + my $state = $dpttypes{"$model"}{MIN}; # default + $state = $dpttypes{"$model"}{MAX} if ($numval == 1); return $state; } +sub dec_dpt2 { #Step value (two-bit) + my $numval = hex (shift); + my $model = shift; + my $state = ($numval & 0x03); + my @dpt2txt = qw(off on forceOff forceOn); + $state = $dpt2txt[$state] if ($model ne 'dpt2.000'); # JoeALLb request; + return $state; +} + +sub dec_dpt3 { #Step value (four-bit) + my $numval = hex (shift); +# $numval = $numval & 0x0F; + my $dir = ($numval & 0x08) >> 3; + my $step = ($numval & 0x07); + my $stepcode = 0; + if ($step > 0) { + $stepcode = int(100 / (2**($step-1))); + $stepcode *= -1 if ($dir == 0); + } + return sprintf ("%d", $stepcode); +} + +sub dec_dpt5 { #1-Octet unsigned value + my $numval = hex (shift); + my $model = shift; + my $hash = shift; + my $state = KNX_limit ($hash, $numval, $model, 'DECODE'); + return sprintf ("%.0f", $state); +} + +sub dec_dpt6 { #1-Octet signed value + my $numval = hex (shift); + my $model = shift; + my $hash = shift; + $numval = unpack("c",pack("C",$numval)); + my $state = KNX_limit ($hash, $numval, $model, 'DECODE'); + return sprintf ("%d", $state); +} + +sub dec_dpt7 { #2-Octet unsigned Value + my $numval = hex (shift); + my $model = shift; + my $hash = shift; + my $state = KNX_limit ($hash, $numval, $model, 'DECODE'); + return sprintf ("%.0f", $state); +} + +sub dec_dpt8 { #2-Octet signed Value + my $numval = hex (shift); + my $model = shift; + my $hash = shift; + $numval = unpack("s",pack("S",$numval)); + my $state = KNX_limit ($hash, $numval, $model, 'DECODE'); + return sprintf ("%d", $state); +} + +sub dec_dpt9 { #2-Octet Float value + my $numval = hex(shift); + my $model = shift; + my $hash = shift; + my $sign = 1; + $sign = -1 if(($numval & 0x8000) > 0); + my $exp = ($numval & 0x7800) >> 11; + my $mant = ($numval & 0x07FF); + $mant = -(~($mant-1) & 0x07FF) if($sign == -1); + $numval = (1 << $exp) * 0.01 * $mant; + my $state = KNX_limit ($hash, $numval, $model, 'DECODE'); + return sprintf ("%.2f","$state"); +} + +sub dec_dpt10 { #Time of Day + my $numval = hex(shift); + my $hours = ($numval & 0x1F0000) >> 16; + my $mins = ($numval & 0x3F00) >> 8; + my $secs = ($numval & 0x3F); + my $wday = ($numval & 0xE00000) >> 21; + my @wdays = (q{},'Monday, ','Tuesday, ','Wednesday, ','Thursday, ','Friday, ','Saturday, ','Sunday, '); + # return sprintf("%s%02d:%02d:%02d",$wdays[$wday],$hours,$mins,$secs); # new option ? + return sprintf("%02d:%02d:%02d",$hours,$mins,$secs); +} + +sub dec_dpt11 { #Date + my $numval = hex(shift); + my $day = ($numval & 0x1F0000) >> 16; + my $month = ($numval & 0x0F00) >> 8; + my $year = ($numval & 0x7F); + #translate year (21st cent if <90 / else 20th century) + $year += 1900 if($year >= 90); + $year += 2000 if($year < 90); + return sprintf("%02d.%02d.%04d",$day,$month,$year); +} + +sub dec_dpt12 { #4-Octet unsigned value (handled as dpt7) + my $numval = hex (shift); + my $model = shift; + my $hash = shift; + my $state = KNX_limit ($hash, $numval, $model, 'DECODE'); + return sprintf ("%.0f", $state); +} + +sub dec_dpt13 { #4-Octet Signed Value + my $numval = hex (shift); + my $model = shift; + my $hash = shift; + $numval = unpack("l",pack("L",$numval)); + my $state = KNX_limit ($hash, $numval, $model, 'DECODE'); + return sprintf ("%d", $state); +} + +sub dec_dpt14 { #4-Octet single precision float + my $numval = unpack "f", pack "L", hex (shift); + my $model = shift; + my $hash = shift; + $numval = KNX_limit ($hash, $numval, $model, 'DECODE'); + return sprintf ("%.3f","$numval"); +} + +sub dec_dpt16 { #14-Octet String + my $value = shift; + my $model = shift; + my $numval = 0; +# my $state = q{}; + $value =~ s/\s*$//gx; # strip trailing blanks + my $state = pack("H*",$value); + #convert to latin-1 + $state = encode ("utf8", $state) if ($model =~ m/16.001/x); + $state = q{} if ($state =~ m/^[\x00]/ix); # case all zeros received + $state =~ s/[\x00-\x1F]+//gx; # remove non printable chars + return $state; +} + +sub dec_dpt19 { #DateTime + my $numval = substr(shift,-16); # strip off 1st byte + my $time = hex (substr ($numval, 6, 6)); + my $date = hex (substr ($numval, 0, 6)); + my $secs = ($time & 0x3F) >> 0; + my $mins = ($time & 0x3F00) >> 8; + my $hours = ($time & 0x1F0000) >> 16; + my $day = ($date & 0x1F) >> 0; + my $month = ($date & 0x0F00) >> 8; + my $year = ($date & 0xFF0000) >> 16; + #extras + my $wday = ($time & 0xE00000) >> 21; # 0 = anyday/not valid, 1= Monday,... + $year += 1900; + return sprintf("%02d.%02d.%04d_%02d:%02d:%02d", $day, $month, $year, $hours, $mins, $secs); +} + +sub dec_dpt20 { #HVAC + my $numval = hex (shift); + $numval = ($numval & 0xff); + my @dpt20_102txt = qw(Auto Comfort Standby Economy Protection reserved); + $numval = 5 if ($numval > 4); # dpt20.102 + return $dpt20_102txt[$numval]; +} + +sub dec_dpt232 { #RGB-Code + my $numval = hex (shift); + return sprintf ("%.6x",$numval); +} + + 1; =pod @@ -1888,7 +1930,7 @@ If you add the option "nosuffix", <getName>, <setName> and <putNa Another option is to disable autocreate for KNX-devices in production environments (when no changes / additions are expected) by using: attr <autocreate> ignoreTypes KNX_.*

Examples:

-
+
+

Set

@@ -1909,7 +1951,7 @@ If you add the option "nosuffix", <getName>, <setName> and <putNa 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:

-
+
+

Get

@@ -1933,7 +1975,7 @@ The answer from the bus-device updates reading and state.

Common attributes

-
+
+

Special attributes

-

 

-

More complex examples:
-Examples can also be found on the (german) Wiki

-

Rollo:

-define rollo KNX 0/10/12:dpt1.008:wdw1 0/10/13:dpt1
-set rollo wdw1 down moves down rollo at window 1
-set rollo g2 on moves up rollo at window 2
-set rollo g2 off-for-timer 5 moves down rollo at window 2 for 5 sec
- -

Object with feedback, icon showing transistions:

-define sps KNX 0/4/0:dpt1:steuern 0/4/1:dpt1:status
-attr sps devStateIcon status-on:general_an:Aus status-off:general_aus:Ein steuern.*:hourglass:Aus
-attr sps eventMap /steuern on:Ein/steuern off:Aus/
-attr sps stateRegex /steuern-set/steuern-/ /steuern-get// /status-get/status-/
-attr sps webCmd Ein:Aus
- -

Object with feedback, state is always showing status:

-define wasser_status KNX 11/3/0:dpt1.001:status:listenonly 11/3/1:dpt1.001:steuern-auf 11/3/2:dpt1.001:steuern-zu
-attr wasser_status devStateIcon on:general_an off:general_aus
-attr wasser_status stateCmd {sprintf("%s", ReadingsVal($name,"status-get",""))}
-attr wasser_status webCmd :
- -

If requested, fhem answers content of GAD refVal to GAD temp, answer nothing to GAD humidity. Both refVal and temp must -have the same dpt-code (e.g dpt9 in this example), else garbage will be returned!:

-define demo KNX 1/0/30:dpt9.001:temp 1/0/31:dpt9.001:humidity 1/0/32:dpt9:refVal
-attr demo putCmd {ReadingsNum("demo","refVal-get",0) if ($gadName =~ /temp/);}
- -

Time master:

-define timedev KNX 0/0/7:dpt10:time 0/0/8:dpt11:date 0/0/9:dpt19
-set timedev date now or set timedev date 01.11.2020 Send date to the bus
-set timedev [time] now or set timedev time 18:33:44 Send time to the bus
-set timedev g3 now or set timedev g3 01.11.2020_18:33:44 Send date and time to the bus (combined)
- -

Send text to the bus:

-define textdev KNX 0/0/9:dpt16
-set textdev g1 AbCdEfGhIjkLmN send text to Bus (max 14 Char.)
-set textdev g1 >CLR< delete text on the KNX display
- -

Slider:

-define newTest KNX 15/2/2:dpt5
-attr newTest webCmd g1
-attr newTest widgetOverride setG1:slider,0,5,100
- -

Two independent slider and on/off buttons:

-define newTest KNX 15/2/9:dpt5 15/2/3:dpt5 15/2/2:dpt1.001:power
-attr newTest IODev knxd
-attr newTest eventMap { usr=>{'^getG1 (\d+)'=>'g1 $1','^getG2 (\d+)'=>'g2 $1','^An'=>'power on','^Aus'=>'power off'}, \ -
-fw=>{'^getG1 (\d+)'=>'getG1','^getG2 (\d+)'=>'getG2','^power-get'=>'state'} \ -
-}
-attr newTest webCmd An:Aus::Label1:getG1::Label2:getG2
-attr newTest widgetOverride getG1:slider,0,5,100 getG2:slider,0,5,100
- -

Synchronized slider for send and reveive-values, on/off-buttons and a state-icon:

- define testDev11 KNX 15/1/19:dpt1:steuern 15/1/20:dpt1:status 15/1/21:dpt5.001:dimmwert:nosuffix\
- attr testDev11 IODev knxd
- attr testDev11 eventMap {\ -
- #Von Device nach Frontend sollte eigentlich bei einem Dezimalwert \
- #das Reading status-get angezeigt werden. Geht aber nicht. Deshalb: stateCmd... \
- dev=>{# '^(\d+)?.%$'=>ReadingsVal($dev,'status-get',"Zefix...")} \
- #Frontend nach Device: Ersetze "An"/"Aus" durch "Steuern on/off". \
- #Alle numerischen Werte vom Slider landen in "dimmwert" \
- usr=>{'^An'=>'steuern on', '^Aus'=>'steuern off'}, \
- #Tuning f$uuml;r die Detailseite...Zeige An/Aus richtig an \
- fw=>{'^An'=>'An', '^Aus'=>'Aus'} \ -
- }
- attr testDev11 stateRegex /steuern-set/steuern-/ /steuern-get// /status-get/status-/
- attr testDev11 stateCmd {\ -
- if ($state =~ m/dimmwert:/i) {'status-' . ReadingsVal($name,"status-get","")}\
- else {return $state}\ -
- }
- attr testDev11 devStateIcon status-on:general_an:Aus status-off:general_aus:Ein steuern.*:hourglass:Aus
- #Dimmwert muss ein reales reading sein. Kann auch ...-get sein, wenn
- #nosuffix nicht angegeben ist.
- #Achtung: bei einem Userreading sind die Werte nicht persistent, also nicht machen!!!
- attr testDev11 webCmd An:Aus:dimmwert
- attr testDev11 widgetOverride dimmwert:slider,0,5,100
- -

Remap values to/from Bus:
-Problem: the Bus-device send's and expects values 0,31,63,95,127,159,191,223,255 but you want 0-8 in your UI...

- -define testStufen KNX 0/4/2:dpt5:StufeIst:get 0/4/7:dpt5:StufeSoll:set
-attr testStufen eventMap { usr => {'^Stufe.(\d)$' => '".sprintf("StufeSoll %d",$1*32 -1)."' }, fw => {'^Stufe.(\d)$' => 'Stufe'} }
-attr testStufen stateCmd {if ($gadName =~ 'Stufe(Ist|Soll)') { \
-
- my $newval = int(($state +1) / 32); \
- #Log3 $name,1, "StateCmd2: $name $gadName Stufe$1 $state -> $newval"; \
- fhem "sleep 0.1;setreading $name Stufe $newval"; \
- OldValue($name); \ -
- } else { \ -
- $state; \ -
- } \ -
-}
-attr testStufen widgetOverride Stufe:slider,0,1,8 -

- -

How to handle dpt's not available in KNX-Module:
-Problem: You know you receive (from a KNX-Sensor/Device) a 2 Byte Floating Point Value, but the sub-Datapoint is not defined.
-Solution: define a base DPT, in this example dpt9 and add a unit-value with the format attribute.

- -define dpt9test KNX 0/4/6:dpt9
-attr dpt9test format g/m&sup3; # gramm / m³
-
-With attr stateCmd you can do almost any conversion to the received value. Example: convert from Watt to kW:
- -attr dpt9test stateCmd { $state = $state / 1000; }
-

- + +

More complex examples can be found on the (german) Wiki

+
=end html