###################################################### # InterTechno Switch Manager as FHM-Module # # Copyright (C) # Olaf Droegehorn / DHS-Computertechnik GmbH # Björn Hempel # 2023- Ralf9 # # Published under GNU GPL License # # $Id: 10_IT.pm 20839 2024-04-12 14:00:00Z Ralf9 $ # ###################################################### package main; use strict; use warnings; #use Data::Dumper qw(Dumper); use SetExtensions; my %codes = ( "XMIToff" => "off", "XMITon" => "on", # Set to previous dim value (before switching it off) "00" => "dim00%", # alt off "01" => "dim06%", "02" => "dim12%", "03" => "dim18%", "04" => "dim25%", "05" => "dim31%", "06" => "dim37%", "07" => "dim43%", "08" => "dim50%", "09" => "dim56%", "0a" => "dim62%", "0b" => "dim68%", "0c" => "dim75%", "0d" => "dim81%", "0e" => "dim87%", "0f" => "dim93%", "10" => "dim100%", "XMITdimup" => "dimup", "XMITdimdown" => "dimdown", "99" => "on-till", ); my %codes_he800 = ( "XMIToff" => "off", "XMITon" => "on", # Set to previous dim value (before switching it off) "00" => "dim00%", #"01" => "last-dim-on", "02" => "dim12%", "03" => "dim25%", "04" => "dim37%", "05" => "dim50%", "06" => "dim62%", "07" => "dim75%", "08" => "dim87%", "09" => "dim100%", "XMITdimup" => "dimup", "XMITdimdown" => "dimdown", "99" => "on-till", ); my %it_c2b; my %it_c2b_he800; my $it_defrepetition = 6; ## Default number of InterTechno Repetitions my %models = ( itremote => 'sender', itswitch => 'simple', itdimmer => 'dimmer', ev1527 => 'ev1527', itswitch_CHN => 'itswitch_CHN', ); my %bintotristate=( "00" => "0", "01" => "F", "10" => "D", "11" => "1" ); my %bintotristateV3=( "10" => "1", "01" => "0", "00" => "D", "11" => "2" ); my %bintotristateHE=( "10" => "1", "01" => "0", "11" => "2", "00" => "D" ); my %ev_action = ( "on" => "0011", "off" => "0000" ); sub bin2dec { unpack("N", pack("B32", substr("0" x 32 . shift, -32))); } sub bin2dec64 { unpack("N", pack("B32", substr("0" x 64 . shift, -64))); } sub IT_Initialize($) { my ($hash) = @_; foreach my $k (keys %codes) { $it_c2b{$codes{$k}} = $k; } foreach my $k (keys %codes_he800) { $it_c2b_he800{$codes_he800{$k}} = $k; } $hash->{Match} = "^i......"; $hash->{SetFn} = \&IT_Set; #$hash->{StateFn} = "IT_SetState"; $hash->{DefFn} = \&IT_Define; $hash->{UndefFn} = \&IT_Undef; $hash->{ParseFn} = \&IT_Parse; $hash->{AttrFn} = \&IT_Attr; $hash->{AttrList} = "IODev ITfrequency ITrepetition ITclock switch_rfmode:1,0 do_not_notify:1,0 ignore:0,1 protocol:V1,V3,HE_EU,SBC_FreeTec,HE800 SIGNALduinoProtocolId userV1setCodes dummy:1,0 " . "$readingFnAttributes " . "model:".join(",", sort keys %models); $hash->{AutoCreate}= { "IT.*" => { GPLOT => "", FILTER => "%NAME", autocreateThreshold => "2:30"} }; } ##################################### sub IT_SetState($$$$) { my ($hash, $tim, $vt, $val) = @_; return undef; $val = $1 if($val =~ m/^(.*) \d+$/); return "Undefined value $val" if(!defined($it_c2b{$val})); } ############################# sub IT_Do_On_Till($@) { my ($hash, $name, @a) = @_; return "Timespec (HH:MM[:SS]) needed for the on-till command" if(@a != 3); my ($err, $hr, $min, $sec, $fn) = GetTimeSpec($a[2]); return $err if($err); my @lt = localtime; my $hms_till = sprintf("%02d:%02d:%02d", $hr, $min, $sec); my $hms_now = sprintf("%02d:%02d:%02d", $lt[2], $lt[1], $lt[0]); if($hms_now ge $hms_till) { Log 4, "on-till: won't switch as now ($hms_now) is later than $hms_till"; return ''; } my @b = ("on"); IT_Set($hash, $name, @b); my $tname = $hash->{NAME} . "_till"; CommandDelete(undef, $tname) if($defs{$tname}); CommandDefine(undef, "$tname at $hms_till set $name off"); } ################################### sub IT_Set($@) { my ($hash, $name, @a) = @_; my $ret = undef; my $na = int(@a); my $message; return "no set value specified" if($na < 1); # return, if this is a dummy device return "Dummydevice $hash->{NAME}: will not set data" if(IsDummy($hash->{NAME})); my $list = ""; if( AttrVal($name, "model", "") ne "itremote" && AttrVal($name, "model", "") ne "itswitch_CHN") { $list .= "off:noArg on:noArg "; if ($hash->{userV1setCodes} && ($hash->{READINGS}{protocol}{VAL} eq "EV1527" || $hash->{READINGS}{protocol}{VAL} eq "V1")) { foreach my $setCode (keys %{$hash->{userV1setCodes}}) { $list .= "$setCode:noArg "; } } } my $c = $it_c2b{$a[0]}; if ($hash->{READINGS}{protocol}{VAL} eq "V3") { if($na > 1 && $a[0] eq "dim") { $a[0] = ($a[1] eq "0" ? "off" : sprintf("dim%02d%%",$a[1]) ); splice @a, 1, 1; $na = int(@a); } elsif ($na == 2 && ($a[0] =~ /dim/)) { return "Bad time spec" if($na == 2 && $a[1] !~ m/^\d*\.?\d+$/); my $val; #$a[0] = ($a[1] eq "0" ? "off" : sprintf("dim%02d%%",$a[1]) ); #splice @a, 1, 1; #$na = int(@a); if($na == 2) { # Timed command. $c = sprintf("%02X", (hex($c) | 0x20)); # Set the extension bit ######################## # Calculating the time. LOOP: for(my $i = 0; $i <= 12; $i++) { for(my $j = 0; $j <= 15; $j++) { $val = (2**$i)*$j*0.25; if($val >= $a[1]) { if($val != $a[1]) { Log3 $name, 2, "$name: changing timeout to $val from $a[1]"; } $c .= sprintf("%x%x", $i, $j); last LOOP; } } } Log3 $hash ,2, "$name: NOT Implemented now!"; return "Specified timeout too large, max is 15360" if(length($c) == 2); } } $list = (join(" ", sort keys %it_c2b) . " dim:slider,0,6.25,100") if( AttrVal($name, "model", "") eq "itdimmer" ); } elsif ($hash->{READINGS}{protocol}{VAL} eq "HE800") { $c = $it_c2b_he800{$a[0]}; if($na > 1 && $a[0] eq "dim") { $a[0] = ($a[1] eq "0" ? "off" : sprintf("dim%02d%%",$a[1]) ); splice @a, 1, 1; $na = int(@a); } $list = (join(" ", sort keys %it_c2b_he800) . " dim:slider,0,12.5,100") if( AttrVal($name, "model", "") eq "itdimmer" ); } else { $list .= "dimup:noArg dimdown:noArg on-till" if( AttrVal($name, "model", "") eq "itdimmer" ); } #if ($hash->{READINGS}{protocol}{VAL} eq "HE800") { # $list .= " learn_on_codes:noArg learn_off_codes:noArg"; #} return SetExtensions($hash, $list, $name, @a) if( $a[0] eq "?" ); return SetExtensions($hash, $list, $name, @a) if( !grep( $_ =~ /^\Q$a[0]\E($|:)/, split( ' ', $list ) ) ); return IT_Do_On_Till($hash, $name, @a) if($a[0] eq "on-till"); return "Bad time spec" if($na == 2 && $a[1] !~ m/^\d*\.?\d+$/); my $io = $hash->{IODev}; return 'no IODev available, adapt attribute IODevList' if (!defined($io)); my $ioNotSIGNALduino = ($io->{TYPE} !~ m/^SIGNALduino/); my $ioTsculfw = 0; if ($io->{TYPE} eq 'TSCUL') { if ($io->{helper}{SVTS}) { $ioTsculfw = 1; } else { Log3 $hash, 2, 'IT set ERROR: TSCUL has not required firmware'; } } my $v = $name ." ". join(" ", @a); ## Log that we are going to switch InterTechno Log3 $hash, 3, "$io->{NAME} IT_set: $v"; my (undef, $cmd) = split(" ", $v, 2); # Not interested in the name... # Look for all devices with the same code, and set state, timestamp my $code; if ($hash->{READINGS}{protocol}{VAL} eq "EV1527") { # beim EV1527 darf der code nicht aus dem $hash->{XMIT} geholt werden $code = "$hash->{CODE}{1}"; } else { $code = "$hash->{XMIT}"; } foreach my $n (keys %{ $modules{IT}{defptr}{$code} }) { my $lh = $modules{IT}{defptr}{$code}{$n}; #$lh->{STATE} = $cmd; if ($hash->{READINGS}{protocol}{VAL} eq "HE800") { my $count = $hash->{"count"}; $count = $count + 1; if ($count > 3) { $count = 0; } $hash->{"count"} = $count; # } } if ($hash->{READINGS}{protocol}{VAL} eq "V3" || $hash->{READINGS}{protocol}{VAL} eq "HE800") { if( AttrVal($name, "model", "") eq "itdimmer" ) { if ($cmd eq "on") { my $lastDimVal = $hash->{READINGS}{lastDimValue}{VAL}; if ($lastDimVal ne "") { #$cmd = $lastDimVal; #readingsSingleUpdate($lh, "state", $lastDimVal,1); readingsSingleUpdate($lh, "dim", substr($lastDimVal, 3, -1),1); } else { readingsSingleUpdate($lh, "dim", "100",1); } readingsSingleUpdate($lh, "state", "on",1); } elsif ($cmd eq "off") { readingsSingleUpdate($lh, "dim", "0",1); readingsSingleUpdate($lh, "state", "off",1); } else { if ($cmd eq "dim100%") { $lh->{STATE} = "on"; readingsSingleUpdate($lh, "state", "on",1); } elsif ($cmd eq "dim00%") { $lh->{STATE} = "off"; readingsSingleUpdate($lh, "lastDimValue", "",1); readingsSingleUpdate($lh, "state", "off",1); #} elsif ($cmd eq "last-dim-on") { # $cmd = AttrVal($name, "lastDimValue", ""); # readingsSingleUpdate($lh, "state", $cmd,1); } else { readingsSingleUpdate($lh, "state", $cmd,1); } if ($cmd eq "dimup") { readingsSingleUpdate($lh, "lastDimValue", "dim100%",1); } elsif ($cmd eq "dimdown") { if ($hash->{READINGS}{protocol}{VAL} eq "HE800") { readingsSingleUpdate($lh, "lastDimValue", "dim12%",1); } else { readingsSingleUpdate($lh, "lastDimValue", "dim06%",1); } } else { readingsSingleUpdate($lh, "lastDimValue", $cmd,1); } } } else { readingsSingleUpdate($lh, "state", $cmd,1); } } else { readingsSingleUpdate($lh, "state", $cmd,1); if( AttrVal($name, "model", "") eq "itdimmer" ) { readingsSingleUpdate($lh,"dim",$cmd,1); } } } Log3 $hash, 5, "$io->{NAME} IT_set: Type=" . $io->{TYPE} . ' Protocol=' . $hash->{READINGS}{protocol}{VAL}; my $oldIOMode; if ($ioNotSIGNALduino) { # das IODev ist kein SIGNALduino return "IODev $io->{NAME} does not support IT" if ($ioTsculfw && defined($io->{CMDS}) && $io->{CMDS} !~ m/i/); # TSCUL muss das i Kommando kennen ## Do we need to change RFMode to SlowRF?? if (AttrVal($name, 'switch_rfmode', '0')) { $oldIOMode = AttrVal($io->{NAME}, 'rfmode', 'SlowRF'); CallFn($io->{NAME}, "AttrFn", "set", ($io->{NAME}, "rfmode", "SlowRF")); } ## Do we need to change ITClock ?? } if (defined($message = AttrVal($name, 'ITclock', undef))) { CallFn($io->{NAME}, "SetFn", $io, ($io->{NAME}, "ITClock", $message)); Log3 $hash, 3, "IT set ITclock: $message for $io->{NAME}"; } ## Do we need to change ITrepetition ?? my $itrep; if($itrep = AttrVal($name, 'ITrepetition', 0)) { $itrep = 254 if ($itrep > 254); $message = 'isr'.$itrep; if ($ioTsculfw) { # tsculfw CallFn($io->{NAME}, 'SetFn', $io, ($io->{NAME}, 'raw', $message)); # if not set before send the tsculfw default is 3 and reverts to it after send } else { # culfw, a-culfw CallFn($io->{NAME}, 'GetFn', $io, (' ', 'raw', $message)); } Log3 $hash,4, "IT set ITrepetition: $message for $io->{NAME}"; } ## Do we need to change ITfrequency ?? my $f; if (defined($f = AttrVal($name, 'ITfrequency', undef))) { $f = $f/26*65536; my $f2 = sprintf("%02x", $f / 65536); my $f1 = sprintf("%02x", int($f % 65536) / 256); my $f0 = sprintf("%02x", $f % 256); $message = "if$f2$f1$f0"; my $arg = sprintf("%.3f", (hex($f2)*65536+hex($f1)*256+hex($f0))/65536*26); Log3 $hash, 3, "Setting ITfrequency (0D,0E,0F) to $f2 $f1 $f0 = $arg MHz"; if ($ioTsculfw) { # tsculfw CallFn($io->{NAME}, 'SetFn', $io, ($io->{NAME}, 'raw', $message)); } else { CallFn($io->{NAME}, 'GetFn', $io, (' ', 'raw', $message)); } } } if ($hash->{READINGS}{protocol}{VAL} eq "V3") { if( AttrVal($name, "model", "") eq "itdimmer" ) { my @itvalues = split(' ', $v); if ($itvalues[1] eq "dimup") { $a[0] = "dim100%"; readingsSingleUpdate($hash, "state", $itvalues[1],1); readingsSingleUpdate($hash, "dim", 100, 1); $message = "is".uc(substr($hash->{XMIT},0,length($hash->{XMIT})-5).$hash->{READINGS}{group}{VAL}."D".$hash->{READINGS}{unit}{VAL}."1111"); } elsif ($itvalues[1] eq "dimdown") { $a[0] = "dim06%"; readingsSingleUpdate($hash, "state", $itvalues[1],1); readingsSingleUpdate($hash, "dim", 6, 1); $message = "is".uc(substr($hash->{XMIT},0,length($hash->{XMIT})-5).$hash->{READINGS}{group}{VAL}."D".$hash->{READINGS}{unit}{VAL}."0000"); } elsif ($itvalues[1] =~ /dim/) { my $dperc = substr($itvalues[1], 3, -1); my $dec = (15*$dperc)/100; my $bin = sprintf ("%b",$dec); while (length($bin) < 4) { # suffix 0 $bin = '0'.$bin; } readingsSingleUpdate($hash, "dim", $dperc, 1); if ($dperc == 0) { $message = "is".uc(substr($hash->{XMIT},0,length($hash->{XMIT})-5).$hash->{READINGS}{group}{VAL}."0".$hash->{READINGS}{unit}{VAL}); } else { $message = "is".uc(substr($hash->{XMIT},0,length($hash->{XMIT})-5).$hash->{READINGS}{group}{VAL}."D".$hash->{READINGS}{unit}{VAL}.$bin); } } else { my $stateVal; if ($a[0] eq "off") { $stateVal = "0"; } else { $stateVal = $hash->{$c}; readingsSingleUpdate($hash, "lastDimValue", "",1); } $message = "is".uc(substr($hash->{XMIT},0,length($hash->{XMIT})-5).$hash->{READINGS}{group}{VAL}.$stateVal.$hash->{READINGS}{unit}{VAL}); } } else { $message = "is".uc(substr($hash->{XMIT},0,length($hash->{XMIT})-5).$hash->{READINGS}{group}{VAL}.$hash->{$c}.$hash->{READINGS}{unit}{VAL}); } } elsif ($hash->{READINGS}{protocol}{VAL} eq "HE_EU") { my $masterVal = "11"; if ($hash->{READINGS}{mode}{VAL} eq "master") { if ($hash->{$c} eq "01") { $masterVal = "01"; } } $message = "ise".uc(substr($hash->{XMIT},0,length($hash->{XMIT})-7).$hash->{$c}.$masterVal.$hash->{READINGS}{unit}{VAL}); } elsif ($hash->{READINGS}{protocol}{VAL} eq "HE800") { my $cVal; my $mode; my @mn; my $msg; my %he800MapingTable = ( 0 => 0, 12 => 2, 25 => 3, 37 => 4, 50 => 5, 62 => 6, 75 => 7, 87 => 8, 100 => 9, ); (undef, $cVal) = split(" ", $v, 2); # Not interested in the name... my @key = (9, 6, 3, 8, 10, 0, 2, 12, 4, 14, 7, 5, 1, 15, 11, 13, 9); # cryptokey my $rollingCode = $hash->{"count"}; if ($rollingCode > 3) { $rollingCode = 0; } my $oldMode = 0; my $sendVal; if ($cVal eq "on") { if (exists($hash->{READINGS}{"on_" . $rollingCode})) { $sendVal = $hash->{READINGS}{"on_" . $rollingCode}{VAL}; } if (defined $sendVal && $sendVal ne "" && $sendVal ne "0") { $message = "ish".uc($sendVal); $oldMode = 1; Log3 $hash,4, "Use old Mode sendVal $sendVal "; } else { readingsSingleUpdate($hash, "lastDimValue", "",1); $mode = 1; } } elsif ($cVal eq "off") { if (exists($hash->{READINGS}{"off_" . $rollingCode})) { $sendVal = $hash->{READINGS}{"off_" . $rollingCode}{VAL}; } if (defined $sendVal && $sendVal ne "" && $sendVal ne "0") { $message = "ish".uc($sendVal); $oldMode = 1; Log3 $hash,4, "Use old Mode sendVal $sendVal "; } else { $mode = 0; } } else { Log3 $hash,5, "mode is DIM MODE: $v Model: " . AttrVal($name, "model", ""); # DIM Mode if( AttrVal($name, "model", "") eq "itdimmer" ) { my @itvalues = split(' ', $v); if ($itvalues[1] eq "dimup") { readingsSingleUpdate($hash, "state", $itvalues[1],1); readingsSingleUpdate($hash, "dim", 100, 1); $mode = 9; } elsif ($itvalues[1] eq "dimdown") { readingsSingleUpdate($hash, "state", $itvalues[1],1); readingsSingleUpdate($hash, "dim", 12, 1); $mode = 2; } else { if ($itvalues[1] =~ /dim/) { my $dperc = substr($itvalues[1], 3, -1); #my $dperc = $itvalues[2]; my $dec = $he800MapingTable{$dperc}; my $bin = sprintf ("%b",$dec); while (length($bin) < 4) { # suffix 0 $bin = '0'.$bin; } readingsSingleUpdate($hash, "dim", $dperc, 1); if ($dperc == 0) { $mode = 0; } else { $mode = $dec; } } } } } #} if ($oldMode == 0) { Log3 $hash,5, "mode is $mode"; my @XMIT_split = split(/_/,$hash->{XMIT}); my $receiverID = $XMIT_split[1]; my $transmitterID = $XMIT_split[0]; #encrypt $mn[0] = $XMIT_split[1]; # mn[0] = iiiib i=receiver-ID $mn[1] = ($rollingCode << 2) & 15; # 2 lowest bits of rolling-code if ($mode > 0) { # ON or OFF $mn[1] |= 2; # mn[1] = rrs0b r=rolling-code, s=ON/OFF, 0=const 0? } $mn[2] = $transmitterID & 15; # mn[2..5] = ttttb t=txID in nibbles -> 4x ttttb $mn[3] = ($transmitterID >> 4) & 15; $mn[4] = ($transmitterID >> 8) & 15; $mn[5] = ($transmitterID >> 12) & 15; if ($mode >= 2 && $mode <= 9) { # mn[6] = dpppb d = dim ON/OFF, p=%dim/10 - 1 $mn[6] = $mode - 2; # dim: 0=10%..7=80% $mn[6] |= 8; # dim: ON } else { $mn[6] = 0; # dim: OFF } #XOR encryption 2 rounds for (my $r=0; $r<=1; $r++){ # 2 encryption rounds $mn[0] = $key[ $mn[0]-$r+1]; # encrypt first nibble #my $i = 0; for (my $i=1; $i<=5 ; $i++){ # encrypt 4 nibbles $mn[$i] = $key[($mn[$i] ^ $mn[$i-1])-$r+1]; # crypted with predecessor & key } } $mn[6] = $mn[6] ^ 9; # no encryption $msg = ($mn[6] << 0x18) | ($mn[5] << 0x14) | # copy the encrypted nibbles in output buffer ($mn[4] << 0x10) | ($mn[3] << 0x0c) | ($mn[2] << 0x08) | ($mn[1] << 0x04) | $mn[0]; $msg = ($msg >> 2) | (($msg & 3) << 0x1a); # shift 2 bits right & copy lowest 2 bits of cbuf[0] in msg bit 27/28 $msg = ~$msg & 0xFFFFFFF; my $bin1=sprintf("%024b",$msg); while (length($bin1) < 28) { # suffix 0 $bin1 = '0'.$bin1; } my $bin = $bin1;# . $bin3; Log3 $hash,4, "msg $msg - bin1 $bin1"; $message = "ish".uc($bin); } } else { my $onoffcode = ''; if (defined($c) && defined($hash->{$c})) { $onoffcode = $hash->{$c}; } else { if ($hash->{userV1setCodes}) { $onoffcode = $hash->{userV1setCodes}{$a[0]}; } } if (length($onoffcode) == 4 && $hash->{READINGS}{protocol}{VAL} ne 'SBC_FreeTec') { # EV1527 $onoffcode = $bintotristate{substr($onoffcode,0,2)} . $bintotristate{substr($onoffcode,2,2)}; } $message = 'is'.uc($hash->{XMIT}.$onoffcode); } if ($ioNotSIGNALduino) { if ($ioTsculfw) { # tsculfw ## Send Message to IODev CallFn($io->{NAME}, 'SetFn', $io, (' ', 'raw', $message)); # tsculfw VTS0.32+ resets frequency, offset, ITrepetition and ITclock back to firmware default values after send } else { # culfw, a-culfw ## Send Message to IODev and wait for correct answer my $msg = CallFn($io->{NAME}, "GetFn", $io, (" ", "raw", $message)); Log3 $hash,5,"IT_Set: GetFn(raw): message = $message Antwort = $msg"; if ($msg =~ m/raw => $message/) { Log 4, "ITSet: Answer from $io->{NAME}: $msg"; } else { Log 2, "IT IODev device didn't answer is command correctly: $msg"; } ## Do we need to change ITrepetition back?? if (defined(AttrVal($name, 'ITrepetition', undef))) { $message = 'isr'.$it_defrepetition; CallFn($io->{NAME}, 'GetFn', $io, (' ', 'raw', $message)); Log3 $hash, 3, "IT set ITrepetition back: $message for $io->{NAME}"; } ## Do we need to change ITfrequency back?? if (defined(AttrVal($name, 'ITfrequency', undef))) { Log3 $hash,4 ,'Setting ITfrequency back to 433.92 MHz'; CallFn($io->{NAME}, 'GetFn', $io, (' ', 'raw', 'if0')); } ## Do we need to change ITClock back?? if (defined(AttrVal($name, 'ITclock', undef))) { Log3 $hash, 3, 'Setting ITClock back to 420'; #CallFn($io->{NAME}, "GetFn", $io, (" ", "raw", "sic250")); CallFn($io->{NAME}, 'SetFn', $io, ($hash->{NAME}, 'ITClock', '420')); } } ## Do we need to change RFMode back to previous mode?? if (AttrVal($name, 'switch_rfmode', '0')) { # do we need to change RFMode of IODev? CallFn($io->{NAME}, 'AttrFn', 'set', ($hash->{NAME}, 'rfmode', $oldIOMode)); } } else { # SIGNALduino my $SignalRepeats = AttrVal($name,'ITrepetition', '6'); my $ITClock = AttrVal($name,'ITclock', undef); my $f = AttrVal($name,'ITfrequency',undef); my $ITfrequency = ''; my $protocolId; if (defined($f)) { $f = $f / 26 * 65536; my $f2 = sprintf("%02x", $f / 65536); my $f1 = sprintf("%02x", int($f % 65536) / 256); my $f0 = sprintf("%02x", $f % 256); my $arg = sprintf("%.3f", (hex($f2)*65536+hex($f1)*256+hex($f0))/65536*26); Log3 $hash, 3, "$io->{NAME} IT_set: Setting ITfrequency (0D,0E,0F) to $f2 $f1 $f0 = $arg MHz"; $ITfrequency = "#F$f2$f1$f0"; } if (defined($ITClock)) { $ITClock = '#C' . $ITClock; } else { $ITClock = ''; } $protocolId = AttrVal($name,'SIGNALduinoProtocolId', undef); if (defined($protocolId)) { $protocolId = 'P' . $protocolId . '#'; } else { if ($hash->{READINGS}{protocol}{VAL} eq "V3") { $protocolId = 'P17#'; } elsif ($hash->{READINGS}{protocol}{VAL} eq "HE800") { # HomeEasy HE800 $protocolId = 'P35#'; } elsif ($hash->{READINGS}{protocol}{VAL} eq "HE_EU") { # HomeEasy HE_EU $protocolId = 'P65#'; } else { $protocolId = 'P3#'; # IT V1 } } if ($hash->{READINGS}{protocol}{VAL} ne "EV1527" && $hash->{READINGS}{protocol}{VAL} ne "V1" && $hash->{READINGS}{protocol}{VAL} ne 'SBC_FreeTec') { # bei ITv1, SBC_FreeTec und EV1527 wird das "is" am Anfang nicht entfernt $message =~ s/^is//; if ($message =~ m/^[he]/) { # h oder e entfernen, falls am Anfang $message = substr($message,1); } } Log3 $hash, 4, "$io->{NAME} IT_set: sendMsg=" . $protocolId . $message . '#R' . $SignalRepeats . $ITClock . $ITfrequency; IOWrite($hash, 'sendMsg', $protocolId . $message . '#R' . $SignalRepeats . $ITClock . $ITfrequency); } return $ret; } ############################# sub IT_Define($$) { my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); my $ioname = $modules{IT}{defptr}{ioname}; # calculate transmit code from IT A-P rotary switches if($a[2] =~ /^([A-O])(([0]{0,1}[1-9])|(1[0-6]))$/i) { my %it_1st = ( "A","0000","B","F000","C","0F00","D","FF00","E","00F0","F","F0F0", "G","0FF0","H","FFF0","I","000F","J","F00F","K","0F0F","L","FF0F", "M","00FF","N","F0FF","O","0FFF","P","FFFF" ); my %it_2nd = ( 1 ,"0000",2 ,"F000",3 ,"0F00",4 ,"FF00",5 ,"00F0",6 ,"F0F0", 7 ,"0FF0",8 ,"FFF0",9 ,"000F",10,"F00F",11,"0F0F",12,"FF0F", 13,"00FF",14,"F0FF",15,"0FFF",16,"FFFF" ); $a[2] = $it_1st{$1}.$it_2nd{int($2)}."0F"; defined $a[3] or $a[3] = "FF"; defined $a[4] or $a[4] = "F0"; defined $a[5] or $a[5] = "0F"; defined $a[6] or $a[6] = "00"; } # calculate transmit code from FLS 100 I,II,III,IV rotary switches if($a[2] =~ /^(I|II|III|IV)([1-4])$/i) { my %fls_1st = ("I","0FFF","II","F0FF","III","FF0F","IV","FFF0" ); my %fls_2nd = (1 ,"0FFF",2 ,"F0FF",3 ,"FF0F",4 ,"FFF0"); $a[2] = $fls_1st{$1}.$fls_2nd{int($2)}."0F"; defined $a[3] or $a[3] = "FF"; defined $a[4] or $a[4] = "F0"; defined $a[5] or $a[5] = "0F"; defined $a[6] or $a[6] = "00"; } my $u = "wrong syntax: define IT 10-bit-housecode " . "off-code on-code [dimup-code] [dimdown-code] or for protocol V3 " . "define IT <26 bit Address> <1 bit group bit> <4 bit unit>"; return $u if(int(@a) < 3); my $housecode; my $oncode; my $offcode; my $unitCode; my $groupBit; my $name = $a[0]; if ($hash->{OLDDEF}) { Log3 $hash,4,"ITdefine: delete OLDDEF $hash->{CODE}{1}"; delete($hash->{CODE}{1}); my @b = split(/[ \t]+/, $hash->{OLDDEF}, 2); delete($modules{IT}{defptr}{lc($b[0])}{$name}); delete($hash->{READINGS}{protocol}); delete($hash->{READINGS}{mode}); delete($hash->{READINGS}{unit}); delete($hash->{READINGS}{group}); for my $c (keys(%it_c2b)) { delete($hash->{$c}); } } if ($a[3] eq "HE800") { # OLD, do not use anymore $housecode = $a[2]; $hash->{READINGS}{protocol}{VAL} = 'HE800'; $hash->{"count"} = '0'; $oncode = "N/A"; $offcode = "N/A"; $unitCode="N/A"; #return "FALSE"; } elsif ($a[2] eq "HE800") { $housecode = ($a[3] + 0) . "_" . ($a[4] + 0); $hash->{READINGS}{protocol}{VAL} = 'HE800'; $hash->{"count"} = '0'; $oncode = "N/A"; $offcode = "N/A"; $unitCode="N/A"; #return "FALSE"; } elsif (length($a[2]) == 26) { # Is Protocol V3 return "Define $a[0]: wrong ITv3-Code format: specify a 26 digits 0/1 " if( ($a[2] !~ m/^[0-1]{26}$/i) ); return "Define $a[0]: wrong Bit Group format: specify a 1 digits 0/1 " if( ($a[3] !~ m/^[0-1]{1}$/i) ); return "Define $a[0]: wrong Unit format: specify 4 digits 0/1 " if( ($a[4] !~ m/^[0-1]{4}$/i) ); #return "Define $a[0]: wrong on/off/dimm format: specify a 1 digits 0/1/d " # if( ($a[3] !~ m/^[d0-1]{1}$/i) ); $housecode=$a[2].$a[3].$a[4]; $groupBit=$a[3]; $unitCode=$a[4]; $oncode = 1; $offcode = 0; $hash->{READINGS}{protocol}{VAL} = 'V3'; $hash->{READINGS}{unit}{VAL} = $unitCode; $hash->{READINGS}{group}{VAL} = $groupBit; } elsif (length($a[2]) == 46) { # HE_EU return "Define $a[0]: wrong IT-Code format: specify a 29 digits 0/1 " if( ($a[2] !~ m/^[0-1]{46}$/i) ); return "Define $a[0]: wrong group format: specify a 1 digits 0/1 " if( ($a[3] !~ m/^[0-1]{1}$/i) ); return "Define $a[0]: wrong unit format: specify a 7 digits 0/1 " if( ($a[4] !~ m/^[0-1]{7}$/i) ); $housecode = $a[2].$a[4]; $groupBit = $a[3]; $unitCode=$a[4]; if ($groupBit == "1") { # looks like a master key $hash->{READINGS}{mode}{VAL} = "master"; $oncode = "01"; $offcode = "00"; } else { $hash->{READINGS}{mode}{VAL} = "single"; $oncode = "10"; $offcode = "01"; } $hash->{READINGS}{unit}{VAL} = $unitCode; $hash->{READINGS}{protocol}{VAL} = 'HE_EU'; } elsif (length($a[2]) == 10 && (substr($a[2],0,4) eq '1527' || AttrVal($name, "model", "") eq "ev1527" || length($a[3]) == 4)) { # Is Protocol EV1527 #Log3 $hash,2,"ITdefine 1527: $name a3=" . $a[3]; $housecode = $a[2]; if (substr($housecode,0,4) eq '1527') { my $evcode = ""; my $bincode = sprintf("%020b",hex(substr($housecode,5))); for (my $n=0; $n<20; $n=$n+2) { $evcode = $evcode . $bintotristate{substr($bincode,$n,2)}; } $hash->{XMIT} = lc($evcode); } else { $hash->{XMIT} = lc($housecode); } return "Define $a[0]: wrong dimup-code format: specify a 4 digits 0/1 " if ( ($a[3] !~ m/^[0-1]{4}$/i) ); return "Define $a[0]: wrong dimup-code format: specify a 4 digits 0/1 " if ( ($a[4] !~ m/^[0-1]{4}$/i) ); $oncode = $a[3]; $offcode = $a[4]; $hash->{READINGS}{protocol}{VAL} = 'EV1527'; } elsif (length($a[2]) == 8) { # SBC, FreeTec return "Define $a[0]: wrong IT-Code format: specify a 8 digits 0/1/f " if( ($a[2] !~ m/^[f0-1]{8}$/i) ); return "Define $a[0]: wrong ON format: specify a 4 digits 0/1/f " if( ($a[3] !~ m/^[f0-1]{4}$/i) ); return "Define $a[0]: wrong OFF format: specify a 4 digits 0/1/f " if( ($a[4] !~ m/^[f0-1]{4}$/i) ); $housecode = $a[2]; $oncode = $a[3]; $offcode = $a[4]; $hash->{READINGS}{protocol}{VAL} = 'SBC_FreeTec'; } else { #Log3 $hash,2,"ITdefine v1: $name"; return "Define $a[0]: wrong IT-Code format: specify a 10 digits 0/1/f " if( ($a[2] !~ m/^[f0-1]{10}$/i) ); return "Define $a[0]: wrong ON format: specify a 2 digits 0/1/f/d " if( ($a[3] !~ m/^[df0-1]{2}$/i) ); return "Define $a[0]: wrong OFF format: specify a 2 digits 0/1/f/d " if( ($a[4] !~ m/^[df0-1]{2}$/i) ); $housecode = $a[2]; $oncode = $a[3]; $offcode = $a[4]; $hash->{READINGS}{protocol}{VAL} = 'V1'; } if ($hash->{READINGS}{protocol}{VAL} ne 'EV1527') { $hash->{XMIT} = lc($housecode); } $hash->{$it_c2b{"on"}} = lc($oncode); $hash->{$it_c2b{"off"}} = lc($offcode); if (int(@a) > 5) { if (length($a[5]) != 4) { # kein EV1527 return "Define $a[0]: wrong dimup-code format: specify a 2 digits 0/1/f/d " if( ($a[5] !~ m/^[df0-1]{2}$/i) ); } else { return "Define $a[0]: wrong dimup-code format: specify a 4 digits 0/1 " if( ($a[5] !~ m/^[0-1]{4}$/i) ); } $hash->{$it_c2b{"dimup"}} = lc($a[5]); if (int(@a) == 7) { if (length($a[6]) != 4) { # kein EV1527 return "Define $a[0]: wrong dimdown-code format: specify a 2 digits 0/1/f/d " if( ($a[6] !~ m/^[df0-1]{2}$/i) ); } else { return "Define $a[0]: wrong dimup-code format: specify a 4 digits 0/1 " if( ($a[6] !~ m/^[0-1]{4}$/i) ); } } $hash->{$it_c2b{"dimdown"}} = lc($a[6]); } else { $hash->{$it_c2b{"dimup"}} = "00"; $hash->{$it_c2b{"dimdown"}} = "00"; } my $code = lc($housecode); my $ncode = 1; $hash->{CODE}{$ncode++} = $code; $modules{IT}{defptr}{$code}{$name} = $hash; AssignIoPort($hash, $ioname); } ############################# sub IT_Undef($$) { my ($hash, $name) = @_; foreach my $c (keys %{ $hash->{CODE} } ) { $c = $hash->{CODE}{$c}; # As after a rename the $name my be different from the $defptr{$c}{$n} # we look for the hash. foreach my $dname (keys %{ $modules{IT}{defptr}{$c} }) { delete($modules{IT}{defptr}{$c}{$dname}) if($modules{IT}{defptr}{$c}{$dname} == $hash); } } return undef; } sub IT_Parse($$) { my ($hash, $msg) = @_; my $ioname = $hash->{NAME}; my $housecode; my $transmittercode; my $dimCode; my $unitCode; my $groupBit; my $onoffcode; my $def; my $newstate; my @list; $modules{IT}{defptr}{ioname} = $ioname; if ((substr($msg, 0, 1)) ne 'i') { Log3 $hash,4,"$ioname IT: message not supported by IT \"$msg\"!"; return ''; } my $bin = undef; my $ishe = (substr($msg, 1, 1) eq 'h'); if ($ishe) { if (length($msg) == 9 || length($msg) == 17) { # from sduino $msg .= '0'; } Log3 $hash,4,"$ioname IT: HE message \"$msg\" (" . length($msg) . ")"; if (length($msg) == 18 || length($msg) == 20) { # HomeEasy EU #Log3 $ioname,3,"HEX Part1: " . substr($msg,2,8); my $bin1=sprintf("%024b",hex(substr($msg,2,8))); while (length($bin1) < 32) { # suffix 0 $bin1 = '0'.$bin1; } #Log3 $ioname,3,"HEX Part2: " . substr($msg,2+8,7); my $bin2=sprintf("%024b",hex(substr($msg,2+8,7))); while (length($bin2) < 28) { # suffix 0 $bin2 = '0'.$bin2; } $bin = $bin1 . $bin2; } elsif (length($msg) == 10 || length($msg) == 12) { # HomeEasy HE800 $bin=sprintf("%024b",hex(substr($msg,2,8))); #my $bin1=sprintf("%024b",hex(substr($msg,2,8))); #while (length($bin1) < 32) { # # suffix 0 # $bin1 = '0'.$bin1; #} #$bin = $bin1; } else { Log3 $ioname,3,"$ioname IT: HE message \"$msg\" (" . length($msg) . ") wrong length!"; return ''; } } else { # IT V1 / V3 Log3 $hash,4,"$ioname IT: message \"$msg\" (" . length($msg) . ")"; #my $isDimMode = 0; if (length($msg) == 17) { # IT V3 my $bin1=sprintf("%024b",hex(substr($msg,1,length($msg)-1-8))); while (length($bin1) < 32) { # suffix 0 $bin1 = '0'.$bin1; } my $bin2=sprintf("%024b",hex(substr($msg,1+8,length($msg)-1))); while (length($bin2) < 32) { # suffix 0 $bin2 = '0'.$bin2; } $bin = $bin1 . $bin2; Log3 $hash,4,"$ioname ITv3: bin message \"$bin\" (" . length($bin) . ")"; } elsif (length($msg) == 19 ) { # IT V3 Dimm my $bin1=sprintf("%024b",hex(substr($msg,1,length($msg)-1-8-8))); while (length($bin1) < 32) { # suffix 0 $bin1 = '0'.$bin1; } my $bin2=sprintf("%024b",hex(substr($msg,1+2,length($msg)-1-8-2))); while (length($bin2) < 32) { # suffix 0 $bin2 = '0'.$bin2; } my $bin3=sprintf("%024b",hex(substr($msg,1+8+2,length($msg)-1))); while (length($bin3) < 32) { # suffix 0 $bin3 = '0'.$bin3; } $bin = substr($bin1 . $bin2 . $bin3,24,length($bin1 . $bin2 . $bin3)-1); Log3 $hash,4,"$ioname ITv3dimm: bin message \"$bin\" (" . length($bin) . ")"; } elsif (length($msg) == 7) { # IT $bin=sprintf("%024b",hex(substr($msg,1,length($msg)-1))); } else { Log3 $ioname,3,"$ioname IT: message \"$msg\" (" . length($msg) . ") wrong length!"; return ''; } } if ((length($bin) % 2) != 0) { # suffix 0 $bin = '0'.$bin; } my $binorg = $bin; my $msgcode=""; if ($ishe) { # HomeEasy if (length($msg) >= 18) { # HomeEasy EU $msgcode=substr($bin, 0, 57); } else { # HomeEasy HE800; #$msgcode=substr($bin, 0, 28); $msgcode=$bin; } } else { while (length($bin)>=2) { if (length($msg) == 7) { if (substr($bin,0,2) != "10") { $msgcode=$msgcode.$bintotristate{substr($bin,0,2)}; } else { if (length($msgcode) >= 10) { Log3 $hash,5,"$ioname IT Parse bintotristate: msgcode=$msgcode, unknown tristate in onoff-code. is evtl a EV1527 sensor"; # $msgcode = substr($msgcode,0,10) . '00'; $msgcode = substr($msgcode,0,10) . $bintotristate{substr($binorg,20,2)} . $bintotristate{substr($binorg,22,2)}; } else { $msgcode = ""; } last; #Log3 $hash,4,"$ioname IT:unknown tristate in \"$bin\""; #return "unknown tristate in \"$bin\"" } #} elsif (length($msg) == 20 && (substr($msg, 1, 1)) eq 'h') { # HomeEasy EU, but this is never reached, see $ishe above! # $msgcode=$msgcode.$bintotristateHE{substr($bin,0,2)}; } else { $msgcode=$msgcode.$bintotristateV3{substr($bin,0,2)}; } $bin=substr($bin,2,length($bin)-2); } } Log3 $hash,4,"$ioname IT: msgcode \"$msgcode\" (" . length($msgcode) . ") bin = $binorg"; my $isEV1527 = undef; if (length($msg) == 7) { if ($msgcode) { # ITv1 or SBC_FreeTec my $sbcFreeTec = substr($msgcode,0,8); if (defined($modules{IT}{defptr}{lc("$sbcFreeTec")}) || substr($msg,6, 1) eq '0' && substr($msgcode,8,2) ne 'FF') { # SBC_FreeTec $housecode=$sbcFreeTec; $onoffcode=substr($msgcode,length($msgcode)-4,4); Log3 $hash,5,"$ioname IT: SBC_FreeTec housecode = $housecode onoffcode = $onoffcode"; } else { # ITv1 $housecode=substr($msgcode,0,10); $onoffcode=substr($msgcode,length($msgcode)-2,2); Log3 $hash,5,"$ioname IT: V1 housecode = $housecode onoffcode = $onoffcode"; } } else { $isEV1527 = 1; $housecode = '1527x' . sprintf("%05x", oct("0b".substr($binorg,0,20))); $onoffcode = substr($binorg, 20); Log3 $hash,5,"$ioname IT: EV1527 housecode = $housecode onoffcode = $onoffcode"; } } elsif (length($msg) == 17 || length($msg) == 19) { $groupBit=substr($msgcode,26,1); $onoffcode=substr($msgcode,27,1); $unitCode=substr($msgcode,28,4); $housecode=substr($msgcode,0,26).$groupBit.$unitCode; if (length($msg) == 19) { $dimCode=substr($msgcode,32,4); } } elsif ($ishe && length($msg) >= 18) { # HomeEasy EU $onoffcode=substr($msgcode,46,2); $groupBit=substr($msgcode,48,2); $unitCode=substr($msgcode,50,7); $housecode=substr($msgcode,0,46).$unitCode; } elsif ($ishe && length($msg) <= 12) { # HomeEasy HE800 #$housecode=substr($msgcode,0,6).substr($msgcode,26,2); #$onoffcode=0; Log3 $hash,4,"$ioname IT: msg:" . $msg . " msgcode:" . substr($msg, 2, 8) ; my $msgVal = hex(substr($msg, 2, 8)); my @mn; my $receiverID; my $mode; my @ikey = (5, 12, 6, 2, 8, 11, 1, 10, 3, 0, 4, 14, 7, 15, 9, 13); #invers cryptokey (exchanged index & value) Log3 $hash,4,"$ioname IT: HEX:" . $msg . " DEC:" . $msgVal ; $msgVal = ~($msgVal >> 4) & 0xFFFFFFF; Log3 $hash,4,"$ioname IT: DEC:" . $msgVal ; $msgVal = (($msgVal << 2) & 0x0FFFFFFF) | (($msgVal & 0xC000000) >> 0x1a); # shift 2 bits left & copy bit 27/28 to bit 1/2 Log3 $hash,4,"$ioname IT: DEC:" . $msgVal ; $mn[0] = $msgVal & 0x0000000F; $mn[1] = ($msgVal & 0x000000F0) >> 0x4; $mn[2] = ($msgVal & 0x00000F00) >> 0x8; $mn[3] = ($msgVal & 0x0000F000) >> 0xc; $mn[4] = ($msgVal & 0x000F0000) >> 0x10; $mn[5] = ($msgVal & 0x00F00000) >> 0x14; $mn[6] = ($msgVal & 0x0F000000) >> 0x18; $mn[6] = $mn[6] ^ 9; # no decryption Log3 $hash,4,"$ioname IT: mn: @mn"; # XOR decryption 2 rounds my $r = 0; for ($r=0; $r<=1; $r++){ # 2 decryption rounds my $i = 5; for ($i=5; $i>=1 ; $i--){ # decrypt 4 nibbles $mn[$i] = (($ikey[$mn[$i]]-$r) & 0x0F) ^ $mn[$i-1]; # decrypted with predecessor & key } $mn[0] = ($ikey[$mn[0]]-$r) & 0x0F; #decrypt first nibble } Log3 $hash,4,"$ioname IT: mn: @mn "; $receiverID = $mn[0]; $mode = ((($mn[1]>>1) & 1) + ($mn[6] & 0x7) + (($mn[6] & 0x8) >> 3)); my $rollingCode = ($mn[1] >> 2); my $transmitterID = (($mn[5] << 12) + ($mn[4] << 8) + ($mn[3] << 4) + $mn[2]); $housecode = $transmitterID . "_" . $receiverID; $transmittercode = $transmitterID; $unitCode = $receiverID; $onoffcode = $mode; Log3 $hash,4,"receiverID : " . $receiverID ; # receiver-ID [0]1..15, 0=Broadcast 1-15 (HE844A button# 1-4 & MASTER=0, HE850 UNIT# 1-15, HE853 = 1) Log3 $hash,4,"OFF/ON/DIM : " . $mode ; # 0=OFF 1=ON, 2=10%dim..9=80%dim (no 90%dim!) Log3 $hash,4,"Rolling-Code : " . $rollingCode ; # rolling-code 0-3 (differentiate new message from repeated message) Log3 $hash,4,"Transmitter-ID: " . $transmitterID ; # unique transmitter-ID [0]1..65535 (0 valid?, 65535 or lower limit?) } else { Log3 $hash,4,"$ioname IT: Wrong IT message received: $msgcode"; return ''; } if(!defined($modules{IT}{defptr}{lc("$housecode")})) { if(length($msg) == 7) { Log3 $hash,4,"$ioname IT: $housecode not defined (Switch code: $onoffcode)"; #return "$housecode not defined (Switch code: $onoffcode)!"; my $tmpOffCode; my $tmpOnCode; if (!defined($isEV1527)) { # itv1 if ($onoffcode eq "F0") { # on code IT Log3 $hash,3,"$ioname IT: For autocreate please use the on button."; return ''; } $tmpOffCode = "F0"; $tmpOnCode = "0F"; if ($onoffcode eq "FF") { # on code IT $tmpOnCode = "FF"; } } else { # ev1527 $tmpOffCode = $ev_action{'off'}; #$tmpOnCode = $ev_action{'on'}; $tmpOnCode = $onoffcode; } if (length($housecode) == 8){ $tmpOffCode = '1000'; $tmpOnCode = '0100'; } return "UNDEFINED IT_$housecode IT $housecode $tmpOnCode $tmpOffCode" if(!$def); } elsif ($ishe && length($msg) >= 18) { # HE_EU my $isGroupCode = '0'; if (($onoffcode == '01' && $groupBit == '01') || ($onoffcode == '00' && $groupBit == '11')) { # Group Code found $isGroupCode = '1'; } Log3 $hash,2,"$ioname IT: $housecode not defined (Address: ".substr($msgcode,0,46)." Unit: $unitCode Switch code: $onoffcode GroupCode: $isGroupCode)"; #return "$housecode not defined (Address: ".substr($msgcode,0,26)." Group: $groupBit Unit: $unitCode Switch code: $onoffcode)!"; return "UNDEFINED IT_$housecode IT " . substr($msgcode,0,46) . " $isGroupCode $unitCode" if(!$def); } elsif ($ishe && length($msg) <= 12) { # HE800 Log3 $hash,2,"$ioname IT: $housecode not defined (HE800)"; return "UNDEFINED IT_HE800_$housecode IT " . "HE800 $transmittercode $unitCode" if(!$def); } else { my $hexCode = sprintf("%x", oct("0b".$housecode)); Log3 $hash,2,"$ioname IT: IT_V3_$hexCode ($housecode) not defined (Address: ".substr($msgcode,0,26)." Group: $groupBit Unit: $unitCode Switch code: $onoffcode)"; return "UNDEFINED IT_V3_$hexCode IT " . substr($msgcode,0,26) . " $groupBit $unitCode" if(!$def); } } $def=$modules{IT}{defptr}{lc($housecode)}; #$lh->{"learn"} = 'ON'; foreach my $name (keys %{$def}) { next if (IsIgnored($name)); if (length($msg) == 17 || length($msg) == 19) { if ($def->{$name}->{READINGS}{group}{VAL} != $groupBit || $def->{$name}->{READINGS}{unit}{VAL} != $unitCode) { next; } } elsif (length($msg) == 7 && !defined($isEV1527) && AttrVal($name, "model", "") eq "ev1527") { $onoffcode = substr($binorg, 20); $def->{$name}->{READINGS}{protocol}{VAL} = 'EV1527'; Log3 $hash,4,"$ioname IT EV1527: " . $def->{$name}{NAME} . ', on code=' . $def->{$name}->{$it_c2b{"on"}} . ", Switch code=$onoffcode"; } $newstate = ""; if ($def->{$name}{userV1setCodes} && ($def->{$name}->{READINGS}{protocol}{VAL} eq "EV1527" || $def->{$name}->{READINGS}{protocol}{VAL} eq "V1")) { foreach my $usercode (keys %{$def->{$name}{userV1setCodes}}) { if ($def->{$name}{userV1setCodes}{$usercode} eq $onoffcode) { $newstate = $usercode; last; } } if ($newstate eq "") { Log3 $def->{$name}{NAME},3,"$ioname IT: Code $onoffcode not found in userV1setCodes, try XMIT"; } } if ($newstate eq "") { if ($def->{$name}->{READINGS}{protocol}{VAL} eq 'HE800') { my %he800MapingTable = ( 0 => 0, 2 => 12, 3 => 25, 4 => 37, 5 => 50, 6 => 62, 7 => 75, 8 => 87, 9 => 100, ); if ($onoffcode == 0) { # OFF my $actState = $hash->{READINGS}{state}{VAL}; $newstate="off"; if( AttrVal($name, "model", "") eq "itdimmer" ) { readingsSingleUpdate($def->{$name},"dim",0,1); } } elsif ($onoffcode == 1) { # On $newstate="on"; if( AttrVal($name, "model", "") eq "itdimmer" ) { my $lastDimVal = $def->{$name}->{READINGS}{lastDimValue}{VAL}; if (defined $lastDimVal && $lastDimVal ne "") { my $dperc = substr($lastDimVal, 3, -1); readingsSingleUpdate($def->{$name},"dim",$dperc,1); readingsSingleUpdate($def->{$name}, "lastDimValue", "",1); } else { readingsSingleUpdate($def->{$name},"dim",100,1); } } } else { my $binVal = $he800MapingTable{$onoffcode}; $binVal = int($binVal); $newstate = sprintf("dim%02d%%",$binVal); readingsSingleUpdate($def->{$name},"dim",$binVal,1); readingsSingleUpdate($def->{$name}, "lastDimValue", $newstate,1); Log3 $hash,4,"$ioname HE800: onoffcode $onoffcode newstate " . $newstate; #if ($binVal == 100) { # $newstate="on"; #} els if ($binVal == 0) { $newstate='off'; } elsif ($binVal == 100) { $newstate='on'; } } } elsif ($def->{$name}->{$it_c2b{"on"}} eq lc($onoffcode)) { $newstate="on"; if( AttrVal($name, "model", "") eq "itdimmer" ) { my $lastDimVal = $def->{$name}->{READINGS}{lastDimValue}{VAL}; if (defined $lastDimVal && $lastDimVal ne "") { my $dperc = substr($lastDimVal, 3, -1); readingsSingleUpdate($def->{$name},"dim",$dperc,1); readingsSingleUpdate($def->{$name}, "lastDimValue", "",1); } else { readingsSingleUpdate($def->{$name},"dim",100,1); } } } elsif ($def->{$name}->{$it_c2b{"off"}} eq lc($onoffcode)) { $newstate="off"; if( AttrVal($name, "model", "") eq "itdimmer" ) { readingsSingleUpdate($def->{$name},"dim",0,1); } } elsif ($def->{$name}->{$it_c2b{"dimup"}} eq lc($onoffcode)) { $newstate="dimup"; if( AttrVal($name, "model", "") eq "itdimmer" ) { readingsSingleUpdate($def->{$name},"dim","dimup",1); } } elsif ($def->{$name}->{$it_c2b{"dimdown"}} eq lc($onoffcode)) { $newstate="dimdown"; if( AttrVal($name, "model", "") eq "itdimmer" ) { readingsSingleUpdate($def->{$name},"dim","dimdown",1); } } elsif ('d' eq lc($onoffcode)) { # dim my $binVal = ((bin2dec($dimCode)+1)*100)/16; $binVal = int($binVal); $newstate = sprintf("dim%02d%%",$binVal); readingsSingleUpdate($def->{$name},"dim",$binVal,1); readingsSingleUpdate($def->{$name}, "lastDimValue", $newstate,1); if ($binVal == 100) { $newstate="on"; } elsif ($binVal == 0) { $newstate="off"; } } else { Log3 $def->{$name}{NAME},3,"$ioname IT: Code $onoffcode not supported by $def->{$name}{NAME}."; next; } } Log3 $def->{$name}{NAME},3,"$ioname IT: $def->{$name}{NAME} ".$def->{$name}->{STATE}."->".$newstate; push(@list,$def->{$name}{NAME}); readingsSingleUpdate($def->{$name},"state",$newstate,1); } return @list; } sub IT_Attr(@) { my ($cmd,$name,$aName,$aVal) = @_; my $hash = $defs{$name}; my $ret; #Log3 $hash, 4, "$name IT_Attr: Calling Getting Attr sub with args: $cmd $aName = $aVal"; if( $aName eq 'model') { if ($aVal eq 'ev1527') { #Log3 $hash, 4, "$name IT_Attr: ev1527"; $hash->{READINGS}{protocol}{VAL} = 'EV1527'; } elsif ($aVal eq 'itswitch_CHN') { $hash->{userV1setCodes} = undef; $hash->{userV1setCodes}{open} = "1010"; $hash->{userV1setCodes}{closed} = "1110"; $hash->{userV1setCodes}{tamper} = "0111"; } } elsif ( $aName eq 'userV1setCodes') { my @array = split(" ",$aVal); $hash->{userV1setCodes} = undef; foreach my $item(@array) { my ($i,$j)= split(/:/, $item); $ret = "$i wrong chars in setname (only [0-9a-zA-Z-_])" if ( ($i !~ m/^[0-9a-z-_]+$/i) ); if (length($j) != 4) { # kein EV1527 $ret = "$j wrong setcode format: specify a 2 digits 0/1/f/d " if( ($j !~ m/^[df0-1]{2}$/i) ); } else { $ret = "$j wrong setcode format: specify a 4 digits 0/1 " if( ($j !~ m/^[0-1]{4}$/i) ); } last if (defined($ret)); $hash->{userV1setCodes}{$i} = $j; } } return $ret; } 1; =pod =item summary supports Intertechno protocol version 1 and version 3 devices =item summary_DE unterstuetzt Intertechno Protocol Version 1 und Version 3 Geraete =begin html

IT - InterTechno

=end html =begin html_DE

IT - InterTechno

=end html_DE =cut