############################################## # $Id: 10_FBDECT.pm 2779 2013-02-21 08:52:27Z rudolfkoenig $ package main; # TODO: test multi-dev, test on the FB use strict; use warnings; use SetExtensions; sub FBDECT_Parse($$@); sub FBDECT_Set($@); sub FBDECT_Get($@); sub FBDECT_Cmd($$@); my @fbdect_models = qw(Powerline546E Dect200); my %fbdect_payload = ( 7 => { n=>"connected" }, 8 => { n=>"disconnected" }, 10 => { n=>"configChanged" }, 15 => { n=>"state", fmt=>'hex($pyld)?"on":"off"' }, 18 => { n=>"current", fmt=>'sprintf("%0.4f A", hex($pyld)/10000)' }, 19 => { n=>"voltage", fmt=>'sprintf("%0.3f V", hex($pyld)/1000)' }, 20 => { n=>"power", fmt=>'sprintf("%0.2f W", hex($pyld)/100)' }, 21 => { n=>"energy", fmt=>'sprintf("%0.0f Wh",hex($pyld))' }, 22 => { n=>"powerFactor", fmt=>'sprintf("%0.3f", hex($pyld))' }, 23 => { n=>"temperature", fmt=>'sprintf("%0.1f C", hex($pyld)/10)' }, ); sub FBDECT_Initialize($) { my ($hash) = @_; $hash->{Match} = ".*"; $hash->{SetFn} = "FBDECT_Set"; $hash->{GetFn} = "FBDECT_Get"; $hash->{DefFn} = "FBDECT_Define"; $hash->{UndefFn} = "FBDECT_Undef"; $hash->{ParseFn} = "FBDECT_Parse"; $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:1,0 dummy:1,0 showtime:1,0 ". "$readingFnAttributes " . "model:".join(",", sort @fbdect_models); } ############################# sub FBDECT_Define($$) { my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); my $name = shift @a; my $type = shift(@a); # always FBDECT my $u = "wrong syntax for $name: define FBDECT id props"; return $u if(int(@a) != 2); my $id = shift @a; return "define $name: wrong id ($id): need a number" if( $id !~ m/^\d+$/i ); $hash->{id} = $id; $hash->{props} = shift @a; $modules{FBDECT}{defptr}{$id} = $hash; AssignIoPort($hash); return undef; } ################################### my %sets = ("on"=>1, "off"=>1, "msgInterval"=>1); sub FBDECT_Set($@) { my ($hash, @a) = @_; my $ret = undef; my $cmd = $a[1]; if(!$sets{$cmd}) { my $usage = join(" ", sort keys %sets); return SetExtensions($hash, $usage, @a); } my $relay; if($cmd eq "on" || $cmd eq "off") { my $relay = sprintf("%08x%04x0000%08x", 15, 4, $cmd eq "on" ? 1 : 0); my $msg = sprintf("%04x0000%08x$relay", $hash->{id}, length($relay)/2); IOWrite($hash, "07", $msg); readingsSingleUpdate($hash, "state", "set_$cmd", 1); } if($cmd eq "msgInterval") { return "msgInterval needs seconds as parameter" if(!defined($a[2]) || $a[2] !~ m/^\d+$/); # Set timer for RELAY, CURRENT, VOLTAGE, POWER, ENERGY, # POWER_FACTOR, TEMP, RELAY_TIMES, foreach my $i (24, 26, 27, 28, 29, 30, 31, 32) { my $txt = sprintf("%08x%04x0000%08x", $i, 4, $a[2]); my $msg = sprintf("%04x0000%08x$txt", $hash->{id}, length($txt)/2); IOWrite($hash, "07", $msg); } } return undef; } my %gets = ("devInfo"=>1); sub FBDECT_Get($@) { my ($hash, @a) = @_; my $ret = undef; my $cmd = ($a[1] ? $a[1] : ""); if(!$gets{$cmd}) { return "Unknown argument $cmd, choose one of ".join(" ", sort keys %gets); } if($cmd eq "devInfo") { my @answ = FBAHA_getDevList($hash->{IODev}, $hash->{id}); return $answ[0] if(@answ == 1); my $d = pop @answ; my $state = "inactive" if($answ[0] =~ m/ inactive,/); while($d) { my ($ptyp, $plen, $pyld) = FBDECT_decodePayload($d); if($ptyp eq "state" && ReadingsVal($hash->{NAME}, $ptyp, "") ne $pyld) { readingsSingleUpdate($hash, $ptyp, ($state ? $state : $pyld), 1); } push @answ, " $ptyp: $pyld"; $d = substr($d, 16+$plen*2); } return join("\n", @answ); } return undef; } ################################### sub FBDECT_Parse($$@) { my ($iodev, $msg, $local) = @_; my $ioName = $iodev->{NAME}; my $mt = substr($msg, 0, 2); if($mt ne "07" && $mt ne "04") { Log3 $ioName, 1, "FBDECT: unknown message type $mt"; return ""; # Nobody else is able to handle this } my $id = hex(substr($msg, 16, 4)); my $hash = $modules{FBDECT}{defptr}{$id}; if(!$hash) { my $ret = "UNDEFINED FBDECT_$id FBDECT $id switch"; Log3 $ioName, 3, "$ret, please define it"; DoTrigger("global", $ret); return ""; } readingsBeginUpdate($hash); if($mt eq "07") { my $d = substr($msg, 32); while($d) { my ($ptyp, $plen, $pyld) = FBDECT_decodePayload($d); readingsBulkUpdate($hash, $ptyp, $pyld); $d = substr($d, 16+$plen*2); } } if($mt eq "04") { my @answ = FBAHA_configInd(substr($msg,16), $id); my $state = ""; if($answ[0] =~ m/ inactive,/) { $state = "inactive"; } else { my $d = pop @answ; while($d) { my ($ptyp, $plen, $pyld) = FBDECT_decodePayload($d); last if(!$plen); push @answ, " $ptyp: $pyld"; $d = substr($d, 16+$plen*2); } # Ignore the rest, is too confusing. @answ = grep /state:/, @answ; (undef, $state) = split(": ", $answ[0], 2); } readingsBulkUpdate($hash, "state", $state); } readingsEndUpdate($hash, 1); return $hash->{NAME}; } sub FBDECT_decodePayload($) { my ($d) = @_; my $ptyp = hex(substr($d, 0, 8)); my $plen = hex(substr($d, 8, 4)); my $pyld = substr($d, 16, $plen*2); if($fbdect_payload{$ptyp}) { $pyld = eval $fbdect_payload{$ptyp}{fmt} if($fbdect_payload{$ptyp}{fmt}); $ptyp = $fbdect_payload{$ptyp}{n}; } return ($ptyp, $plen, $pyld); } ##################################### sub FBDECT_Undef($$) { my ($hash, $arg) = @_; my $homeId = $hash->{homeId}; my $id = $hash->{id}; delete $modules{FBDECT}{defptr}{$id}; return undef; } 1; =pod =begin html

FBDECT

=end html =begin html_DE

FBDECT

=end html_DE =cut