From 714a49b3996912611236911c51406b802a4afd66 Mon Sep 17 00:00:00 2001 From: herrmannj <> Date: Tue, 1 Sep 2020 20:10:39 +0000 Subject: [PATCH] 10_SchellenbergHandle.pm: initial check-in git-svn-id: https://svn.fhem.de/fhem/trunk@22710 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/10_SchellenbergHandle.pm | 266 +++++++++++++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 fhem/FHEM/10_SchellenbergHandle.pm diff --git a/fhem/FHEM/10_SchellenbergHandle.pm b/fhem/FHEM/10_SchellenbergHandle.pm new file mode 100644 index 000000000..eb39c76fe --- /dev/null +++ b/fhem/FHEM/10_SchellenbergHandle.pm @@ -0,0 +1,266 @@ +# $Id$ +############################################################################### +# +# This file is part of fhem. +# +# Fhem is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# Fhem is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with fhem. If not, see . +# +# +############################################################################### + +# Thanks to hypfer for doing the basic research + +package main; + +use 5.018; +use feature qw( lexical_subs ); + +use strict; +use warnings; +use utf8; +use Fcntl qw( :DEFAULT ); + +no warnings qw( experimental::lexical_subs ); + +sub SchellenbergHandle_Initialize { + my ($hash) = @_; + + $hash->{'DefFn'} = 'SchellenbergHandle_Define'; + $hash->{'UndefFn'} = 'SchellenbergHandle_Undef'; + #$hash->{'DeleteFn'} = 'Schellenberg_Delete'; + #$hash->{'SetFn'} = 'SchellenbergHandle_Set'; + #$hash->{'ReadFn'} = "Schellenberg_Read"; + #$hash->{'ReadyFn'} = "Schellenberg_Ready"; + $hash->{'ParseFn'} = "SchellenbergHandle_Parse"; + $hash->{'Match'} = '^ss[[:xdigit:]]{1}4[[:xdigit:]]{16}'; + $hash->{'AttrList'} = $readingFnAttributes; + + return; +}; + +sub SchellenbergHandle_Define { + my ($hash, $def) = @_; + my ($name, $type, $id) = split /\s/, $def, 3; + + my $cvsid = '$Id$'; + $cvsid =~ s/^.*pm\s//; + $cvsid =~ s/Z\s\S+\s\$$/ UTC/; + $hash->{'SVN'} = $cvsid; + + # id valid? + return 'invalid id' if ($id !~ m/^([[:xdigit:]]{6})$/); + + # id exists AND device exists? + return 'handle already defined' if (exists($modules{'SchellenbergHandle'}{'defptr'}{$id})); + + # set id to defptr + $hash->{'ID'} = $id; + $modules{'SchellenbergHandle'}{'defptr'}{$id} = $hash; + InternalTimer(0, \&SchellenbergHandle_Run, $hash); + return; +}; + +sub SchellenbergHandle_Undef { + my ($hash, $name) = @_; + # delete defptr + delete $modules{'SchellenbergHandle'}{'defptr'}{$hash->{'ID'}}; + return; +}; + +# modul, all readings and attribute are loaded +sub SchellenbergHandle_Run { + my ($hash) = @_; + return; +}; + +sub SchellenbergHandle_Set { + my ($hash, $name, $cmd, @args) = @_; + + return "Unknown argument $cmd, choose one of none" if ($cmd eq '?'); +}; + +# not used atm, rolling-code is synchronized after each startup +sub SchellenbergHandle_SetPersistentData { + my ($hash, $mc, $close) = @_; + + my $filename = File::Spec->catfile(Logdir(), "$hash->{'FUUID'}.data"); + if (sysopen(my $fh, $filename, O_WRONLY|O_CREAT|O_TRUNC|O_SYNC)) { + binmode $fh, ':encoding(UTF-8):crlf'; + say $fh sprintf('# This file is automatically generated by %s; do not modify.', $hash->{'NAME'}); + # SBWH, version, gmt-timestamp, rolling-code, 0: intermediate, 1: clean shutdown LINE-END + say $fh sprintf('SBWH,1,%s,%s,%s', time(), 1234, 0); + close($fh); + } else { + say "error $!"; + }; +}; + +# not used atm, rolling-code is synchronized after each startup +sub SchellenbergHandle_GetPersistentData { + my ($hash) = @_; + + my $filename = File::Spec->catfile(Logdir(), "$hash->{'FUUID'}.data"); + if (sysopen(my $fh, $filename, O_RDONLY)) { + binmode $fh, ':encoding(UTF-8):crlf'; + my @lines = readline $fh; + say @lines; + close($fh); + } else { + say "error $!"; + }; +}; + +sub SchellenbergHandle_ProcessMsg { + my ($hash, $mt, $fn, $mc, $lc, $rssi) = @_; + + my $unknown1 = hex($lc) & 3; # 2 lsb in message repetition. Only seen as zero + $lc = hex($lc) >> 2; # right shift 2 bit / message repetition + $rssi = hex($rssi) - 256; + Log3 ($hash, 4, sprintf('type: %s, fn: %s, mc: %s, lc: %s, rssi: %s dBm, unkown: %s', $mt, $fn, $mc, $lc, $rssi, $unknown1)); + + my sub statefn { + my ($fncode) = shift; + my $table = { + '3B' => 'tilted', + '1B' => 'open', + '1A' => 'closed', + '18' => 'alarm', + '19' => 'alarm-end', + }; + return exists($table->{$fncode})?$table->{$fncode}:$fncode; + }; + + my sub watchdog { + readingsBeginUpdate($hash); + readingsBulkUpdateIfChanged($hash, 'state', 'dead'); + readingsBulkUpdateIfChanged($hash, 'alive', 'dead'); + readingsEndUpdate($hash, 1); + }; + + if ($mt eq '1') { + # message counter > last known ? + $mc = hex($mc); + my $lastmc = $hash->{'.MC'} // hex($mc) -1; + my $diff; + { + use integer; + $diff = (0x10000 + hex($mc) - $lastmc) & 0xFFFF; + }; + if ($diff == 0) { + return; + } elsif ($diff < 256) { + #$hash->{'MISSED_PACKET'} += $lc; + readingsBeginUpdate($hash); + #readingsBulkUpdate($hash, '.mc', hex($mc)); + $hash->{'.MC'} = hex($mc); + readingsBulkUpdateIfChanged($hash, 'state', statefn($fn)); + readingsBulkUpdateIfChanged($hash, 'alive', 'ok'); + readingsBulkUpdate($hash, 'rssi', $rssi); + readingsEndUpdate($hash, 1); + SchellenbergHandle_SetPersistentData($hash, $mc, 0) if ($mc % 8 == 0); + } else { + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, 'state', 'out-of-sync'); + eadingsBulkUpdateIfChanged($hash, 'alive', 'ok'); + readingsBulkUpdate($hash, 'rssi', $rssi); + readingsEndUpdate($hash, 1); + }; + } elsif ($mt eq '0') { + my $f = hex($mc) >> 8; + if ($f == 0x84) { + RemoveInternalTimer($hash, \&watchdog); + readingsBeginUpdate($hash); + my $battery = sprintf('%.1f', (hex($mc) & 0xFF) / 10); + readingsBulkUpdateIfChanged($hash, 'state', 'alive') if ($hash->{'STATE'} eq 'dead'); + readingsBulkUpdateIfChanged($hash, 'voltage', $battery); + readingsBulkUpdateIfChanged($hash, 'battery', ($battery > 2.0)?'ok':'low'); + readingsBulkUpdateIfChanged($hash, 'alive', 'ok'); + readingsBulkUpdate($hash, 'rssi', $rssi); + readingsEndUpdate($hash, 1); + InternalTimer(Time::HiRes::time() + 86400, \&watchdog, $hash); + #InternalTimer(Time::HiRes::time() + 5, \&watchdog, $hash); + }; + }; +}; + +sub SchellenbergHandle_Parse { + my ($io_hash, $msg) = @_; + + if (my ($mt, $id, $fn, $mc, $lc, $rssi) = ($msg =~ m/^ss + ([[:xdigit:]]{1}) + 4 + ([[:xdigit:]]{6}) + ([[:xdigit:]]{2}) + ([[:xdigit:]]{4}) + ([[:xdigit:]]{2}) + ([[:xdigit:]]{2})/x)) { + + my $hash = $modules{'SchellenbergHandle'}{'defptr'}{$id}; + + if (defined($hash) and exists($hash->{'NAME'}) and exists($defs{$hash->{'NAME'}})) { + SchellenbergHandle_ProcessMsg($hash, $mt, $fn, $mc, $lc, $rssi); + return $hash->{'NAME'}; + } else { + # pair cmd + if (1 or exists($io_hash->{'PAIRING'}) and $io_hash->{'PAIRING'} and $mt eq '1' and $fn eq '40') { + return "UNDEFINED SchellenbergHandle_$id SchellenbergHandle $id"; + } else { + Log3 ($io_hash, 3, sprintf('SchellenbergHandle: unpaired handle %s', $id)) if (lc eq '00'); + return (undef); + }; + }; + }; + # print POSIX::strftime '%Y.%m.%d %H:%M:%S: ', localtime(); + # print "incoming SOME -----> msg $msg -> $mt $id $fn $mc $lc\r"; + return; +}; + +1; + +=pod +=item device +=item summary Schellenberg RF Alarm Door Handle +=item summary_DE Schellenberg Funk-Sicherheits-Alarmgriff +=begin html + + +

SchellenbergHandle

+ + +=end html + +=cut \ No newline at end of file