mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-02-25 22:15:09 +00:00
SD_ProtocolData.pm: Updated to v1.53 new protocols and sensors git-svn-id: https://svn.fhem.de/fhem/trunk@27949 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2326 lines
79 KiB
Perl
2326 lines
79 KiB
Perl
################################################################################
|
||
# $Id$
|
||
#
|
||
# The file is part of the SIGNALduino project
|
||
# v3.5.x - https://github.com/RFD-FHEM/RFFHEM
|
||
#
|
||
# 2016-2019 S.Butzek, Ralf9
|
||
# 2019-2021 S.Butzek, HomeAutoUser, elektron-bbs
|
||
#
|
||
################################################################################
|
||
package lib::SD_Protocols;
|
||
|
||
use strict;
|
||
use warnings;
|
||
use Carp qw(croak carp);
|
||
use constant HAS_DigestCRC => defined eval { require Digest::CRC; };
|
||
use constant HAS_JSON => defined eval { require JSON; };
|
||
|
||
our $VERSION = '2.07';
|
||
use Storable qw(dclone);
|
||
use Scalar::Util qw(blessed);
|
||
|
||
use Data::Dumper;
|
||
|
||
############################# package lib::SD_Protocols
|
||
=item new()
|
||
|
||
This function will initialize the given Filename containing a valid protocolHash.
|
||
First Parameter is for filename (full or relativ path) to be loaded.
|
||
Returns created object
|
||
|
||
=cut
|
||
|
||
sub new {
|
||
my $class = shift;
|
||
croak "Illegal parameter list has odd number of values" if @_ % 2;
|
||
my %args = @_;
|
||
my $self = {};
|
||
|
||
$self->{_protocolFilename} = $args{filename} // q[];
|
||
$self->{_protocols} = undef;
|
||
$self->{_filetype} = $args{filetype} // 'PerlModule';
|
||
$self->{_logCallback} = undef;
|
||
bless $self, $class;
|
||
|
||
if ( $self->{_protocolFilename} ) {
|
||
|
||
( $self->{_filetype} eq 'json' )
|
||
? $self->LoadHashFromJson( $self->{_protocolFilename} )
|
||
: $self->LoadHash( $self->{_protocolFilename} );
|
||
}
|
||
return $self;
|
||
}
|
||
|
||
############################# package lib::SD_Protocols
|
||
=item STORABLE_freeze()
|
||
|
||
This function is not currently explained.
|
||
|
||
Input:
|
||
Output:
|
||
|
||
=cut
|
||
|
||
sub STORABLE_freeze {
|
||
my $self = shift;
|
||
return join( q[:], ( $self->{_protocolFilename}, $self->{_filetype} ) );
|
||
}
|
||
|
||
############################# package lib::SD_Protocols
|
||
=item STORABLE_thaw()
|
||
|
||
This function is not currently explained.
|
||
|
||
Input:
|
||
Output:
|
||
|
||
=cut
|
||
|
||
sub STORABLE_thaw {
|
||
my ( $self, $cloning, $frozen ) = @_;
|
||
( $self->{_protocolFilename}, $self->{_filetype} ) =
|
||
split( /:/xms, $frozen );
|
||
$self->LoadHash();
|
||
$self->LoadHashFromJson();
|
||
return;
|
||
}
|
||
|
||
|
||
############################# package lib::SD_Protocols
|
||
=item _checkInvocant()
|
||
|
||
This function, checks if input param is a valid object otherwise it will croak with error message
|
||
Input: ($object);
|
||
Output: $object or croak if not an object
|
||
|
||
=cut
|
||
|
||
sub _checkInvocant {
|
||
my $thing = shift;
|
||
my $caller = caller;
|
||
|
||
if( !defined $thing ) {
|
||
croak "The invocant is not defined";
|
||
}
|
||
elsif( !ref $thing ) {
|
||
croak "The invocant is not a reference";
|
||
}
|
||
elsif( !blessed $thing ) {
|
||
croak "The invocant is not an object";
|
||
}
|
||
elsif( !$thing->isa($caller) ) {
|
||
croak "The invocant is not a subclass of $caller";
|
||
}
|
||
|
||
return $thing;
|
||
}
|
||
|
||
|
||
############################# package lib::SD_Protocols
|
||
=item LoadHashFromJson()
|
||
|
||
This function, will load protocol hash from json file into a hash.
|
||
First Parameter is for filename (full or relativ path) to be loaded.
|
||
Returns error or undef on success
|
||
|
||
Input: ($object,$filename);
|
||
Output:
|
||
|
||
=cut
|
||
|
||
sub LoadHashFromJson {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $filename = shift // $self->{_protocolFilename};
|
||
|
||
return if ( $self->{_filetype} ne 'json' );
|
||
|
||
if ( !-e $filename ) {
|
||
return qq[File $filename does not exsits];
|
||
}
|
||
|
||
open( my $json_fh, '<:encoding(UTF-8)', $filename )
|
||
or croak("Can't open \$filename\": $!\n");
|
||
my $json_text = do { local $/ = undef; <$json_fh> };
|
||
close $json_fh or croak "Can't close '$filename' after reading";
|
||
|
||
if (!HAS_JSON)
|
||
{
|
||
croak("Perl Module JSON not availble. Needs to be installed.");
|
||
}
|
||
|
||
my $json = JSON->new;
|
||
$json = $json->relaxed(1);
|
||
my $ver = $json->incr_parse($json_text);
|
||
my $prot = $json->incr_parse();
|
||
|
||
$self->{_protocols} = $prot // 'undef';
|
||
$self->{_protocolsVersion} = $ver->{version} // 'undef';
|
||
|
||
$self->setDefaults();
|
||
$self->{_protocolFilename} = $filename;
|
||
return;
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item LoadHash()
|
||
|
||
This function, will load protocol hash from perlmodule file.
|
||
First Parameter is for filename (full or relativ path) to be loaded.
|
||
Returns error or undef on success
|
||
|
||
Input: ($object,$filename);
|
||
Output:
|
||
|
||
=cut
|
||
|
||
sub LoadHash {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $filename = shift // $self->{_protocolFilename};
|
||
|
||
return if ( $self->{_filetype} ne "PerlModule" );
|
||
|
||
if ( !-e $filename ) {
|
||
return qq[File $filename does not exists];
|
||
}
|
||
|
||
return $@ if ( !eval { require $filename; 1 } );
|
||
$self->{_protocols} = \%lib::SD_ProtocolData::protocols;
|
||
$self->{_protocolsVersion} = $lib::SD_ProtocolData::VERSION;
|
||
|
||
delete( $INC{$filename} ); # Unload package, because we only wanted the hash
|
||
|
||
$self->setDefaults();
|
||
$self->{_protocolFilename} = $filename;
|
||
return;
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item protocolexists()
|
||
|
||
This function, will return true if the given ID exists otherwise false
|
||
|
||
Input: ($object,$protocolID);
|
||
Output:
|
||
|
||
=cut
|
||
|
||
sub protocolExists {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $pId= shift // carp "Illegal parameter number, protocol id was not specified";
|
||
return exists($self->{_protocols}->{$pId});
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item getProtocolList()
|
||
|
||
This function, will return a reference to the protocol hash
|
||
|
||
=cut
|
||
|
||
sub getProtocolList {
|
||
my $self = shift // carp 'Not called within an object';
|
||
return $self->{_protocols};
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item getKeys()
|
||
|
||
This function, will return all keys from the protocol hash
|
||
|
||
=cut
|
||
|
||
sub getKeys {
|
||
my $self = shift // carp 'Not called within an object';
|
||
|
||
my $filter = shift // undef;
|
||
if (defined $filter)
|
||
{
|
||
my (@keys) = grep { exists $self->{_protocols}->{$_}->{$filter} } keys %{$self->{_protocols}};
|
||
return @keys;
|
||
}
|
||
|
||
my (@ret) = keys %{ $self->{_protocols} };
|
||
return @ret;
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item checkProperty()
|
||
|
||
This function, will return a value from the Protocolist and
|
||
check if the key exists and a value is defined optional you can specify a optional default value that will be returned
|
||
|
||
returns undef if the var is not defined
|
||
|
||
Input: ($object,$id,$valueName);
|
||
Output:
|
||
|
||
=cut
|
||
|
||
sub checkProperty {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $id = shift // return;
|
||
my $valueName = shift // return;
|
||
my $default = shift // undef;
|
||
|
||
return $self->{_protocols}->{$id}->{$valueName}
|
||
if exists( $self->{_protocols}->{$id}->{$valueName} )
|
||
&& defined( $self->{_protocols}->{$id}->{$valueName} );
|
||
return $default; # Will return undef if $default is not provided
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item getProperty()
|
||
|
||
This function, will return a value from the Protocolist without any checks
|
||
|
||
returns undef if the var is not defined
|
||
|
||
Input: ($object,$protocolID,$valueName);
|
||
Output:
|
||
|
||
=cut
|
||
|
||
sub getProperty {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $id = shift // return;
|
||
my $valueName = shift // return;
|
||
|
||
return $self->{_protocols}->{$id}->{$valueName}
|
||
if ( exists $self->{_protocols}->{$id}->{$valueName} );
|
||
return;
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item getProtocolVersion()
|
||
|
||
This function, will return a version value of the Protocolist
|
||
|
||
=cut
|
||
|
||
sub getProtocolVersion {
|
||
my $self = shift // carp 'Not called within an object';
|
||
return $self->{_protocolsVersion};
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item setDefaults()
|
||
|
||
This function will add common Defaults to the Protocollist
|
||
|
||
=cut
|
||
|
||
sub setDefaults {
|
||
my $self = shift // carp 'Not called within an object';
|
||
|
||
for my $id ( $self->getKeys() )
|
||
{
|
||
my $format = $self->getProperty($id,'format');
|
||
|
||
if ( defined $format && ($format eq 'manchester' || $format =~ 'FSK') )
|
||
{
|
||
# Manchester defaults :
|
||
my $cref = $self->checkProperty( $id, 'method' );
|
||
( !defined $cref && $format eq 'manchester' )
|
||
? $self->{_protocols}->{$id}->{method} =
|
||
\&lib::SD_Protocols::MCRAW
|
||
: undef;
|
||
|
||
if ( defined $cref ) {
|
||
$cref =~ s/^\\&//xms;
|
||
( ref $cref ne 'CODE' )
|
||
? $self->{_protocols}->{$id}->{method} = eval { \&$cref }
|
||
: undef;
|
||
}
|
||
}
|
||
elsif ( defined( $self->getProperty( $id, 'sync' ) ) ) {
|
||
|
||
# Messages with sync defaults :
|
||
}
|
||
elsif ( defined( $self->getProperty( $id, 'clockabs' ) ) ) {
|
||
|
||
# Messages without sync defaults :
|
||
( !defined( $self->checkProperty( $id, 'length_min' ) ) )
|
||
? $self->{_protocols}->{$id}->{length_min} = 8
|
||
: undef;
|
||
}
|
||
else {
|
||
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item binStr2hexStr()
|
||
|
||
This function will convert binary string into its hex representation as string
|
||
|
||
Input: binary string
|
||
Output:
|
||
hex string
|
||
|
||
=cut
|
||
|
||
sub binStr2hexStr {
|
||
shift if ref $_[0] eq __PACKAGE__;
|
||
|
||
my $num = shift // return;
|
||
return if ( $num !~ /^[01]+$/xms );
|
||
my $WIDTH = 4;
|
||
my $index = length($num) - $WIDTH;
|
||
my $hex = '';
|
||
do {
|
||
my $width = $WIDTH;
|
||
if ( $index < 0 ) {
|
||
$width += $index;
|
||
$index = 0;
|
||
}
|
||
my $cut_string = substr( $num, $index, $width );
|
||
$hex = sprintf( '%X', oct("0b$cut_string") ) . $hex;
|
||
$index -= $WIDTH;
|
||
} while ( $index > ( -1 * $WIDTH ) );
|
||
return $hex;
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
|
||
=item LengthInRange()
|
||
|
||
This function checks if a given length is in range of the valid min and max length for the given protocolId
|
||
|
||
Input: ($object,$protocolID,$message_length);
|
||
Output:
|
||
on success array (returnCode=1, '')
|
||
otherwise array (returncode=0,"Error message")
|
||
=cut
|
||
|
||
sub LengthInRange {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $id = shift // carp 'protocol ID must be provided';
|
||
my $message_length = shift // return (0,'no message_length provided');
|
||
|
||
return (0,'protocol does not exists') if (!$self->protocolExists($id));
|
||
|
||
if ($message_length < $self->checkProperty($id,'length_min',-1)) {
|
||
return (0, 'message is to short');
|
||
}
|
||
elsif (defined $self->getProperty($id,'length_max') && $message_length > $self->getProperty($id,'length_max')) {
|
||
return (0, 'message is to long');
|
||
}
|
||
return (1,q{});
|
||
}
|
||
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item mc2dmc()
|
||
|
||
This function is a helper for remudlation of a manchester signal to a differental manchester signal afterwards
|
||
|
||
Input: $object,$bitData (string)
|
||
Output:
|
||
string of converted bits
|
||
or array (-1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub mc2dmc
|
||
{
|
||
my $self = shift // carp 'Not called within an object' && return (0,'no object provided');
|
||
my $bitData = shift // carp 'bitData must be perovided' && return (0,'no bitData provided');
|
||
|
||
my @bitmsg;
|
||
my $i;
|
||
|
||
$bitData =~ s/1/lh/g; # 0 ersetzen mit low high
|
||
$bitData =~ s/0/hl/g; # 1 ersetzen durch high low ersetzen
|
||
|
||
for ($i=1;$i<length($bitData)-1;$i+=2)
|
||
{
|
||
push (@bitmsg, (substr($bitData,$i,1) eq substr($bitData,$i+1,1)) ? 0 : 1); # demodulated differential manchester
|
||
}
|
||
return join "", @bitmsg ; # demodulated differential manchester as string
|
||
}
|
||
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item mcBit2Funkbus()
|
||
|
||
This function is a output helper for funkbus manchester signals.
|
||
|
||
Input: $object,$name,$bitData,$id,$mcbitnum
|
||
Output:
|
||
hex string
|
||
or array (-1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub mcBit2Funkbus
|
||
{
|
||
my $self = shift // carp 'Not called within an object' && return (0,'no object provided');
|
||
my $name = shift // 'anonymous';
|
||
my $bitData = shift // carp 'bitData must be perovided' && return (0,'no bitData provided');
|
||
my $id = shift // carp 'protocol ID must be provided' && return (0,'no protocolId provided');
|
||
my $mcbitnum = shift // length $bitData;
|
||
|
||
return (-1,' message is to short') if ($mcbitnum < $self->checkProperty($id,'length_min',-1) );
|
||
return (-1,' message is to long') if (defined $self->getProperty($id,'length_max' ) && $mcbitnum > $self->getProperty($id,'length_max') );
|
||
|
||
$self->_logging( qq[lib/mcBitFunkbus, $name Funkbus: raw=$bitData], 5 );
|
||
|
||
$bitData =~ s/1/lh/g; # 0 ersetzen mit low high
|
||
$bitData =~ s/0/hl/g; # 1 ersdetzen durch high low ersetzen
|
||
|
||
my $s_bitmsg = $self->mc2dmc($bitData); # Convert to differential manchester
|
||
|
||
if ($id == 119) {
|
||
my $pos = index($s_bitmsg,'01100');
|
||
if ($pos >= 0 && $pos < 5) {
|
||
$s_bitmsg = '001' . substr($s_bitmsg,$pos);
|
||
return (-1,'wrong bits at begin') if (length($s_bitmsg) < 48);
|
||
} else {
|
||
return (-1,'wrong bits at begin');
|
||
}
|
||
} else {
|
||
$s_bitmsg = q[0] . $s_bitmsg;
|
||
}
|
||
|
||
my $data;
|
||
my $xor = 0;
|
||
my $chk = 0;
|
||
my $p = 0; # parity
|
||
my $hex = q[];
|
||
for (my $i=0; $i<6;$i++) { # checksum
|
||
$data = oct(q[b].substr($s_bitmsg, $i*8,8));
|
||
$hex .= sprintf('%02X', $data);
|
||
if ($i<5) {
|
||
$xor ^= $data;
|
||
} else {
|
||
$chk = $data & 0x0F;
|
||
$xor ^= $data & 0xE0;
|
||
$data &= 0xF0;
|
||
}
|
||
while ($data) { # parity
|
||
$p^=($data & 1);
|
||
$data>>=1;
|
||
}
|
||
}
|
||
return (-1,'parity error') if ($p == 1);
|
||
|
||
my $xor_nibble = (($xor & 0xF0) >> 4) ^ ($xor & 0x0F);
|
||
my $result = 0;
|
||
$result = ($xor_nibble & 0x8) ? $result ^ 0xC : $result;
|
||
$result = ($xor_nibble & 0x4) ? $result ^ 0x2 : $result;
|
||
$result = ($xor_nibble & 0x2) ? $result ^ 0x8 : $result;
|
||
$result = ($xor_nibble & 0x1) ? $result ^ 0x3 : $result;
|
||
|
||
return (-1,'checksum error') if ($result != $chk);
|
||
|
||
$self->_logging( qq[lib/mcBitFunkbus, $name Funkbus: len=]. length($s_bitmsg).q[ bit49=].substr($s_bitmsg,48,1).qq[ parity=$p res=$result chk=$chk msg=$s_bitmsg hex=$hex], 4 );
|
||
|
||
return (1,$hex);
|
||
}
|
||
|
||
|
||
|
||
=item MCRAW()
|
||
|
||
This function is desired to be used as a default output helper for manchester signals.
|
||
It will check for length_max and return a hex string
|
||
|
||
Input: $object,$name,$bitData,$id,$mcbitnum
|
||
Output:
|
||
hex string
|
||
or array (-1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub MCRAW {
|
||
my ( $self, $name, $bitData, $id, $mcbitnum ) = @_;
|
||
$self // carp 'Not called within an object';
|
||
|
||
return (-1," message is to long") if ($mcbitnum > $self->checkProperty($id,"length_max",0) );
|
||
return(1,binStr2hexStr($bitData));
|
||
}
|
||
|
||
=item mcBit2Sainlogic()
|
||
|
||
This function checks the Manchester signals from a Sainlogic weather sensor.
|
||
It will check for length_max, length_min and return a hex string
|
||
|
||
Input: $object,$name,$bitData,$id,$mcbitnum
|
||
Output:
|
||
array (1,hex string)
|
||
or array (-1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub mcBit2Sainlogic {
|
||
my ( $self, $name, $bitData, $id, $mcbitnum ) = @_;
|
||
$self // carp 'Not called within an object';
|
||
|
||
$self->_logging( "$name: lib/mcBit2Sainlogic, protocol $id, lenght $mcbitnum", 5 );
|
||
$self->_logging( "$name: lib/mcBit2Sainlogic, $bitData", 5 );
|
||
|
||
return (-1,' message is to long') if ($mcbitnum > $self->checkProperty($id,"length_max",0) );
|
||
|
||
if ($mcbitnum < 128) {
|
||
my $start = index($bitData, '010100');
|
||
$self->_logging( "$name: lib/mcBit2Sainlogic, protocol $id, start found at pos $start", 5 );
|
||
if ($start < 0 || $start > 10) {
|
||
$self->_logging( "$name: lib/mcBit2Sainlogic, protocol $id, start 010100 not found", 4 );
|
||
return (-1, "$name: lib/mcBit2Sainlogic, start 010100 not found");
|
||
}
|
||
while($start < 10) {
|
||
$bitData = q[1] . $bitData;
|
||
$start = index($bitData, '010100');
|
||
}
|
||
$bitData = substr($bitData, 0, 128);
|
||
$mcbitnum = length($bitData);
|
||
}
|
||
$self->_logging( "$name: lib/mcBit2Sainlogic, $bitData", 5 );
|
||
return (-1,' message is to short') if ($mcbitnum < $self->checkProperty($id,"length_min",0) );
|
||
return(1,binStr2hexStr($bitData));
|
||
}
|
||
|
||
############################# package lib::SD_Protocols
|
||
=item registerLogCallback()
|
||
|
||
=cut
|
||
|
||
sub registerLogCallback {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $callback = shift // carp 'coderef must be provided';
|
||
|
||
( ref $callback eq 'CODE' )
|
||
? $self->{_logCallback} = $callback
|
||
: carp 'coderef must be provided for callback';
|
||
|
||
return;
|
||
}
|
||
|
||
############################# package lib::SD_Protocols
|
||
=item _logging()
|
||
|
||
This function transfers the data to the sub which is referenced by the code ref.
|
||
example: $self->_logging('something happend','3')
|
||
|
||
=cut
|
||
|
||
sub _logging {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $message = shift // carp 'message must be provided';
|
||
my $level = shift // 3;
|
||
|
||
if ( defined $self->{_logCallback} ) {
|
||
$self->{_logCallback}->( $message, $level );
|
||
}
|
||
return;
|
||
}
|
||
|
||
######################### package lib::SD_Protocols #########################
|
||
### all functions for RAWmsg processing or module preparation ###
|
||
#############################################################################
|
||
|
||
############################
|
||
# ASK/OOK method functions #
|
||
############################
|
||
|
||
sub _ASK_OOK_methods_behind_here {
|
||
# only for functionslist - no function!
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item dec2binppari()
|
||
|
||
This function calculated. It converts a decimal number with a width of 8 bits into binary format,
|
||
calculates the parity, appends the parity bit and returns this 9 bit.
|
||
|
||
Input: $num
|
||
Output:
|
||
calculated number binary with parity
|
||
|
||
=cut
|
||
|
||
sub dec2binppari { # dec to bin . parity
|
||
shift if ref $_[0] eq __PACKAGE__;
|
||
my $num = shift // carp 'must be called with an number';
|
||
my $parity = 0;
|
||
my $nbin = sprintf( "%08b", $num );
|
||
for my $c ( split //, $nbin ) {
|
||
$parity ^= $c;
|
||
}
|
||
return qq[$nbin$parity]; # bin(num) . paritybit
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item mcBit2AS()
|
||
|
||
extract the message from the bitdata if it looks like valid data
|
||
|
||
Input: ($object,$name,$bitData,$protocolID, optional: length $bitData);
|
||
Output:
|
||
on success array (returnCode=1, hexData)
|
||
otherwise array (returncode=-1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub mcBit2AS {
|
||
my $self = shift // carp 'Not called within an object' && return (0,'no object provided');
|
||
my $name = shift // 'anonymous';
|
||
my $bitData = shift // carp 'bitData must be perovided' && return (0,'no bitData provided');
|
||
my $id = shift // carp 'protocol ID must be provided' && return (0,'no protocolId provided');
|
||
my $mcbitnum = shift // length $bitData;
|
||
|
||
if(index($bitData,'1100',16) >= 0) # $rawData =~ m/^A{2,3}/)
|
||
{ # Valid AS detected!
|
||
my $message_start = index($bitData,'1100',16);
|
||
$self->_logging( qq[lib/mcBit2AS, AS protocol detected], 5 );
|
||
|
||
my $message_end=index($bitData,'1100',$message_start+16);
|
||
$message_end = length($bitData) if ($message_end == -1);
|
||
my $message_length = $message_end - $message_start;
|
||
|
||
return (-1,' message is to short') if ($message_length < $self->checkProperty($id,'length_min',-1) );
|
||
return (-1,' message is to long') if (defined $self->getProperty($id,'length_max' ) && $message_length > $self->getProperty($id,'length_max') );
|
||
|
||
my $msgbits =substr($bitData,$message_start);
|
||
my $ashex = lib::SD_Protocols::binStr2hexStr($msgbits); # output with length before
|
||
|
||
$self->_logging( qq[$name: AS, protocol converted to hex: ($ashex) with length ($message_length) bits \n], 5 );
|
||
|
||
return (1,$ashex);
|
||
}
|
||
return (-1,undef);
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item mcBit2Grothe()
|
||
|
||
extract the message from the bitdata if it looks like valid data
|
||
|
||
Input: ($object,$name,$bitData,$protocolID, optional: length $bitData);
|
||
Output:
|
||
on success array (returnCode=1, hexData)
|
||
otherwise array (returncode=-1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub mcBit2Grothe {
|
||
my $self = shift // carp 'Not called within an object' && return (0,'no object provided');
|
||
my $name = shift // "anonymous";
|
||
my $bitData = shift // carp 'bitData must be perovided' && return (0,'no bitData provided');
|
||
my $id = shift // carp 'protocol ID must be provided' && return (0,'no protocolId provided');;;
|
||
my $message_length = shift // length $bitData;
|
||
|
||
my $bitLength;
|
||
|
||
$bitData = substr($bitData, 0, $message_length);
|
||
my $preamble = '01000111';
|
||
my $pos = index($bitData, $preamble);
|
||
if ($pos < 0 || $pos > 5) {
|
||
$self->_logging( qq[lib/mcBit2Grothe, protocol id $id, start pattern ($preamble) not found], 3 );
|
||
return (-1,qq[Start pattern ($preamble) not found]);
|
||
} else {
|
||
if ($pos == 1) { # eine Null am Anfang zuviel
|
||
$bitData =~ s/^0//; # eine Null am Anfang entfernen
|
||
}
|
||
$bitLength = length($bitData);
|
||
my ($rcode, $rtxt) = $self->LengthInRange($id, $bitLength);
|
||
if (!$rcode) {
|
||
$self->_logging( qq[lib/mcBit2Grothe, protocol id $id, $rtxt], 3 );
|
||
return (-1,qq[$rtxt]);
|
||
}
|
||
}
|
||
my $hex = lib::SD_Protocols::binStr2hexStr($bitData);
|
||
$self->_logging( q[lib/mcBit2Grothe, protocol id $id detected, $bitData ($bitLength], 4 );
|
||
return (1,$hex); ## Return the bits unchanged in hex
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item mcBit2Hideki()
|
||
|
||
extract the message from the bitdata if it looks like valid data
|
||
|
||
Input: ($object,$name,$bitData,$protocolID, optional: length $bitData);
|
||
Output:
|
||
on success array (returnCode=1, hexData)
|
||
otherwise array (returncode=-1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub mcBit2Hideki {
|
||
my $self = shift // carp 'Not called within an object' && return (0,'no object provided');
|
||
my $name = shift // 'anonymous';
|
||
my $bitData = shift // carp 'bitData must be perovided' && return (0,'no bitData provided');
|
||
my $id = shift // carp 'protocol ID must be provided' && return (0,'no protocolId provided');
|
||
my $mcbitnum = shift // length $bitData;
|
||
|
||
if ($mcbitnum == 89) { # optimization when the beginning was missing
|
||
my $bit0 = substr($bitData,0,1);
|
||
$bit0 = $bit0 ^ 1;
|
||
$bitData = $bit0 . $bitData;
|
||
$self->_logging( qq[lib/mcBit2Hideki, L=$mcbitnum add bit $bit0 at begin $bitData], 5 );
|
||
}
|
||
|
||
my $message_start = index($bitData,'10101110'); # normal rawMSG
|
||
my $invert = 0;
|
||
my $message_start_invert = index($bitData,'01010001'); # invert rawMSG
|
||
# 10101110 can occur again in raw MSG -> comparison with inverted start 01010001
|
||
|
||
if ( $message_start < 0 || ( $message_start_invert!= -1 && $message_start > 0 && ($message_start_invert < $message_start) ) ) {
|
||
$bitData =~ tr/01/10/; # invert message
|
||
$message_start = index($bitData,'10101110'); # 0x75 but in reverse order
|
||
$invert = 1;
|
||
}
|
||
|
||
if ($message_start >= 0 ) # 0x75 but in reverse order
|
||
{
|
||
$self->_logging( qq[lib/mcBit2Hideki, Hideki protocol (invert=$invert) detected], 5 );
|
||
|
||
# Todo: Mindest Laenge fuer startpunkt vorspringen
|
||
# Todo: Wiederholung auch an das Modul weitergeben, damit es dort geprueft werden kann
|
||
my $message_end = index($bitData,'10101110',$message_start+71); # pruefen auf ein zweites 0x75, mindestens 72 bit nach 1. 0x75, da der Regensensor minimum 8 Byte besitzt je byte haben wir 9 bit
|
||
$message_end = length($bitData) if ($message_end == -1);
|
||
my $message_length = $message_end - $message_start;
|
||
|
||
return (-1,' message is to short') if ($message_length < $self->checkProperty($id,'length_min',-1) );
|
||
return (-1,' message is to long') if (defined $self->getProperty($id,'length_max' ) && $message_length > $self->getProperty($id,'length_max') );
|
||
|
||
my $hidekihex = q{};
|
||
my $idx;
|
||
|
||
for ($idx=$message_start; $idx<$message_end; $idx=$idx+9)
|
||
{
|
||
my $byte = q{};
|
||
$byte= substr($bitData,$idx,8); ## Ignore every 9th bit
|
||
$self->_logging( qq[lib/mcBit2Hideki, byte in order $byte], 5 );
|
||
$byte = scalar reverse $byte;
|
||
$self->_logging( qq[lib/mcBit2Hideki, byte reversed $byte , as hex: "].sprintf('%X', oct("0b$byte")), 5 );
|
||
|
||
$hidekihex=$hidekihex.sprintf('%02X', oct("0b$byte"));
|
||
}
|
||
|
||
($invert == 0)
|
||
? $self->_logging( qq[lib/mcBit2Hideki, receive data is not inverted], 4 )
|
||
: $self->_logging( qq[lib/mcBit2Hideki, receive data is inverted], 4 );
|
||
|
||
$self->_logging( qq[lib/mcBit2Hideki, protocol converted to hex: $hidekihex with $message_length bits, messagestart $message_start], 4 );
|
||
|
||
return (1,$hidekihex); ## Return only the original bits, include length
|
||
}
|
||
$self->_logging( qq[lib/mcBit2Hideki, start pattern (10101110) not found], 4 );
|
||
return (-1,undef);
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item mcBit2Maverick()
|
||
|
||
This function extract the message from the bitdata if it looks like valid data
|
||
|
||
Input: ($object,$name,$bitData,$protocolID, optional: length $bitData);
|
||
Output:
|
||
on success array (returnCode=1, hexData)
|
||
otherwise array (returncode=-1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub mcBit2Maverick {
|
||
my $self = shift // carp 'Not called within an object' && return (0,'no object provided');
|
||
my $name = shift // 'anonymous';
|
||
my $bitData = shift // carp 'bitData must be perovided' && return (0,'no bitData provided');
|
||
my $id = shift // carp 'protocol ID must be provided' && return (0,'no protocolId provided');
|
||
my $mcbitnum = shift // length $bitData;
|
||
|
||
|
||
if ($bitData =~ m/(101010101001100110010101)/xms)
|
||
{ # Valid Maverick header detected
|
||
my $header_pos=$+[1];
|
||
$self->_logging( qq[lib/mcBit2Maverick, protocol detected: header_pos = $header_pos], 4 );
|
||
my $hex=lib::SD_Protocols::binStr2hexStr(substr($bitData,$header_pos,26*4));
|
||
return (1,$hex); ## Return the bits unchanged in hex
|
||
} else {
|
||
return return (-1,undef);
|
||
}
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item mcBit2OSV1()
|
||
|
||
extract the message from the bitdata if it looks like valid data
|
||
|
||
Input: ($object,$name,$bitData,$protocolID, optional: length $bitData);
|
||
Output:
|
||
on success array (returnCode=1, hexData)
|
||
otherwise array (returncode=-1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub mcBit2OSV1 {
|
||
my $self = shift // carp 'Not called within an object' && return (0,'no object provided');
|
||
my $name = shift // 'anonymous';
|
||
my $bitData = shift // carp 'bitData must be perovided' && return (0,'no bitData provided');
|
||
my $id = shift // carp 'protocol ID must be provided' && return (0,'no protocolId provided');;;
|
||
my $mcbitnum = shift // length $bitData;
|
||
|
||
return (-1,' message is to short') if ($mcbitnum < $self->checkProperty($id,'length_min',-1) );
|
||
return (-1,' message is to long') if (defined $self->getProperty($id,'length_max') && $mcbitnum > $self->getProperty($id,'length_max') );
|
||
|
||
if (substr($bitData,20,1) != 0) {
|
||
$bitData =~ tr/01/10/; # invert message and check if it is possible to deocde now
|
||
}
|
||
my $calcsum = oct( '0b' . reverse substr($bitData,0,8));
|
||
$calcsum += oct( '0b' . reverse substr($bitData,8,8));
|
||
$calcsum += oct( '0b' . reverse substr($bitData,16,8));
|
||
$calcsum = ($calcsum & 0xFF) + ($calcsum >> 8);
|
||
my $checksum = oct( '0b' . reverse substr($bitData,24,8));
|
||
|
||
if ($calcsum != $checksum) { # Checksum
|
||
return (-1,qq[OSV1 - ERROR checksum not equal: $calcsum != $checksum]);
|
||
}
|
||
|
||
$self->_logging( qq[lib/mcBit2OSV1, input data: $bitData], 4 );
|
||
my $newBitData = '00001010'; # Byte 0: Id1 = 0x0A
|
||
$newBitData .= '01001101'; # Byte 1: Id2 = 0x4D
|
||
my $channel = substr($bitData,6,2); # Byte 2 h: Channel
|
||
if ($channel eq '00') { # in 0 LSB first
|
||
$newBitData .= '0001'; # out 1 MSB first
|
||
} elsif ($channel eq '10') { # in 4 LSB first
|
||
$newBitData .= '0010'; # out 2 MSB first
|
||
} elsif ($channel eq '01') { # in 4 LSB first
|
||
$newBitData .= '0011'; # out 3 MSB first
|
||
} else { # in 8 LSB first
|
||
return (-1,qq[$name: OSV1 - ERROR channel not valid: $channel]);
|
||
}
|
||
$newBitData .= '0000'; # Byte 2 l: ????
|
||
$newBitData .= '0000'; # Byte 3 h: address
|
||
$newBitData .= reverse substr($bitData,0,4); # Byte 3 l: address (Rolling Code)
|
||
$newBitData .= reverse substr($bitData,8,4); # Byte 4 h: T 0,1
|
||
$newBitData .= '0' . substr($bitData,23,1) . '00'; # Byte 4 l: Bit 2 - Batterie 0=ok, 1=low (< 2,5 Volt)
|
||
$newBitData .= reverse substr($bitData,16,4); # Byte 5 h: T 10
|
||
$newBitData .= reverse substr($bitData,12,4); # Byte 5 l: T 1
|
||
$newBitData .= '0000'; # Byte 6 h: immer 0000
|
||
$newBitData .= substr($bitData,21,1) . '000'; # Byte 6 l: Bit 3 - Temperatur 0=pos | 1=neg, Rest 0
|
||
$newBitData .= '00000000'; # Byte 7: immer 0000 0000
|
||
# calculate new checksum over first 16 nibbles
|
||
$checksum = 0;
|
||
for (my $i = 0; $i < 64; $i = $i + 4) {
|
||
$checksum += oct( '0b' . substr($newBitData, $i, 4));
|
||
}
|
||
$checksum = ($checksum - 0xa) & 0xff;
|
||
$newBitData .= sprintf('%08b',$checksum); # Byte 8: new Checksum
|
||
$newBitData .= '00000000'; # Byte 9: immer 0000 0000
|
||
my $osv1hex = '50' . lib::SD_Protocols::binStr2hexStr($newBitData); # output with length before
|
||
$self->_logging( qq[lib/mcBit2OSV1, protocol id $id translated to RFXSensor format], 4 );
|
||
$self->_logging( qq[lib/mcBit2OSV1, converted to hex: $osv1hex], 4 );
|
||
|
||
return (1,$osv1hex);
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item mcBit2OSV2o3()
|
||
|
||
extract the message from the bitdata if it looks like valid data
|
||
|
||
Input: ($object,$name,$bitData,$protocolID, optional: length $bitData);
|
||
Output:
|
||
on success array (returnCode=1, hexData)
|
||
otherwise array (returncode=-1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub mcBit2OSV2o3 {
|
||
my $self = shift // carp 'Not called within an object' && return (0,'no object provided');
|
||
my $name = shift // "anonymous";
|
||
my $bitData = shift // carp 'bitData must be perovided' && return (0,'no bitData provided');
|
||
my $id = shift // carp 'protocol ID must be provided' && return (0,'no protocolId provided');;;
|
||
my $mcbitnum = shift // length $bitData;
|
||
|
||
my $preamble_pos;
|
||
my $message_end;
|
||
my $message_length;
|
||
my $msg_start;
|
||
|
||
#$bitData =~ tr/10/01/;
|
||
if ($bitData =~ m/^.?(01){12,17}.?10011001/xms)
|
||
{
|
||
# Valid OSV2 detected!
|
||
#$preamble_pos=index($bitData,"10011001",24);
|
||
$preamble_pos=$+[1];
|
||
|
||
$self->_logging( qq[lib/mcBit2OSV2, mesprotocol detected: preamble_pos = $preamble_pos], 4 );
|
||
return return (-1," sync not found") if ($preamble_pos <24);
|
||
|
||
$message_end=$-[1] if ($bitData =~ m/^.{44,}(01){16,17}.?10011001/); #Todo regex .{44,} 44 should be calculated from $preamble_pos+ min message lengh (44)
|
||
if (!defined($message_end) || $message_end < $preamble_pos) {
|
||
$message_end = length($bitData);
|
||
} else {
|
||
$message_end += 16;
|
||
$self->_logging( qq[lib/mcBit2OSV2, message end pattern found at pos $message_end lengthBitData=].length($bitData), 4 );
|
||
}
|
||
$message_length = ($message_end - $preamble_pos)/2;
|
||
|
||
return (-1," message is to short") if ($message_length < $self->checkProperty($id,'length_min',-1));
|
||
return (-1," message is to long") if (defined $self->getProperty($id,'length_max') && $message_length > $self->getProperty($id,'length_max') );
|
||
|
||
my $idx=0;
|
||
my $osv2bits="";
|
||
my $osv2hex ="";
|
||
|
||
for ($idx=$preamble_pos;$idx<$message_end;$idx=$idx+16)
|
||
{
|
||
if ($message_end-$idx < 8 )
|
||
{
|
||
last;
|
||
}
|
||
my $osv2byte=substr($bitData,$idx,16);
|
||
|
||
my $rvosv2byte=q{};
|
||
|
||
for (my $p=0;$p<length($osv2byte);$p=$p+2)
|
||
{
|
||
$rvosv2byte = substr($osv2byte,$p,1).$rvosv2byte;
|
||
}
|
||
$rvosv2byte =~ tr/10/01/;
|
||
|
||
if (length($rvosv2byte) == 8) {
|
||
$osv2hex=$osv2hex.sprintf('%02X', oct("0b$rvosv2byte")) ;
|
||
} else {
|
||
$osv2hex=$osv2hex.sprintf('%X', oct("0b$rvosv2byte")) ;
|
||
}
|
||
$osv2bits = $osv2bits.$rvosv2byte;
|
||
}
|
||
my $osv2len=length($osv2hex)*4;
|
||
$osv2hex = sprintf '%02X%s', $osv2len,$osv2hex;
|
||
|
||
$self->_logging( qq[lib/mcBit2OSV2, protocol converted to hex: ($osv2hex) with length $osv2len bits], 4 );
|
||
|
||
#$found=1;
|
||
#$dmsg=$osv2hex;
|
||
return (1,$osv2hex);
|
||
}
|
||
elsif ($bitData =~ m/1{12,24}(0101)/g) { # min Preamble 12 x 1, Valid OSV3 detected!
|
||
$preamble_pos = $-[1];
|
||
$msg_start = $preamble_pos + 4;
|
||
if ($bitData =~ m/\G.+?(1{24})0101/xms) { # preamble + sync der zweiten Nachricht
|
||
$message_end = $-[1];
|
||
$self->_logging( qq[lib/mcBit2OSV2, protocol OSV3 with two messages detected: length of second message = ] . ($mcbitnum - $message_end - 28), 4 );
|
||
}
|
||
else { # es wurde keine zweite Nachricht gefunden
|
||
$message_end = $mcbitnum;
|
||
}
|
||
$message_length = $message_end - $msg_start;
|
||
$self->_logging( qq[lib/mcBit2OSV2, protocol OSV3 detected: msg_start = $msg_start, message_length = $message_length], 4 );
|
||
|
||
return (-1," message with length ($message_length) is to short") if ($message_length < $self->checkProperty($id,'length_min',-1) );
|
||
|
||
my $idx=0;
|
||
my $osv3hex =q{};
|
||
|
||
for ($idx=$msg_start; $idx<$message_end; $idx=$idx+4)
|
||
{
|
||
if (length($bitData)-$idx < 4 )
|
||
{
|
||
last;
|
||
}
|
||
my $osv3nibble = q{};
|
||
#$osv3nibble=NULL;
|
||
$osv3nibble=substr($bitData,$idx,4);
|
||
|
||
my $rvosv3nibble = q{};
|
||
|
||
for (my $p=0;$p<length($osv3nibble);$p++)
|
||
{
|
||
$rvosv3nibble = substr($osv3nibble,$p,1).$rvosv3nibble;
|
||
}
|
||
$osv3hex=$osv3hex.sprintf('%X', oct("0b$rvosv3nibble"));
|
||
#$osv3bits = $osv3bits.$rvosv3nibble;
|
||
}
|
||
$self->_logging( qq[lib/mcBit2OSV2, protocol OSV3 = $osv3hex], 4 );
|
||
|
||
my $korr = 10;
|
||
# Check if nibble 1 is A
|
||
if (substr($osv3hex,1,1) ne 'A')
|
||
{
|
||
my $n1=substr($osv3hex,1,1);
|
||
$korr = hex(substr($osv3hex,3,1));
|
||
substr($osv3hex,1,1,'A'); # nibble 1 = A
|
||
substr($osv3hex,3,1,$n1); # nibble 3 = nibble1
|
||
}
|
||
# Korrektur nibble
|
||
my $insKorr = sprintf('%X', $korr);
|
||
# Check for ending 00
|
||
if (substr($osv3hex,-2,2) eq '00')
|
||
{
|
||
#substr($osv3hex,1,-2); # remove 00 at end
|
||
$osv3hex = substr($osv3hex, 0, length($osv3hex)-2);
|
||
}
|
||
my $osv3len = length($osv3hex);
|
||
$osv3hex .= '0';
|
||
my $turn0 = substr($osv3hex,5, $osv3len-4);
|
||
my $turn = '';
|
||
for ($idx=0; $idx<$osv3len-5; $idx=$idx+2) {
|
||
$turn = $turn . substr($turn0,$idx+1,1) . substr($turn0,$idx,1);
|
||
}
|
||
$osv3hex = substr($osv3hex,0,5) . $insKorr . $turn;
|
||
$osv3hex = substr($osv3hex,0,$osv3len+1);
|
||
$osv3hex = sprintf("%02X", length($osv3hex)*4).$osv3hex;
|
||
$self->_logging( qq[lib/mcBit2OSV2, protocol OSV3 converted to hex: ($osv3hex) with length (].((length($osv3hex)-2)*4).q[) bits], 4 );
|
||
#$found=1;
|
||
#$dmsg=$osv2hex;
|
||
return (1,$osv3hex);
|
||
}
|
||
return (-1,undef);
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item mcBit2OSPIR()
|
||
|
||
This function extract the message from the bitdata if it looks like valid data
|
||
|
||
Input: ($object,$name,$bitData,$protocolID, optional: length $bitData);
|
||
Output:
|
||
on success array (returnCode=1, hexData)
|
||
otherwise array (returncode=-1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub mcBit2OSPIR {
|
||
my $self = shift // carp 'Not called within an object' && return (0,'no object provided');
|
||
my $name = shift // 'anonymous';
|
||
my $bitData = shift // carp 'bitData must be perovided' && return (0,'no bitData provided');
|
||
my $id = shift // carp 'protocol ID must be provided' && return (0,'no protocolId provided');
|
||
my $mcbitnum = shift // length $bitData;
|
||
|
||
if ($bitData =~ m/(1{14}|0{14})/xms)
|
||
{ # Valid Oregon PIR detected
|
||
my $header_pos=$+[1];
|
||
$self->_logging( qq[lib/mcBit2OSPIR, protocol detected: header_pos = $header_pos], 4 );
|
||
my $hex=lib::SD_Protocols::binStr2hexStr($bitData);
|
||
|
||
return (1,$hex); ## Return the bits unchanged in hex
|
||
} else {
|
||
return return (-1,undef);
|
||
}
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item mcBit2SomfyRTS()
|
||
|
||
This function extract the message from the bitdata if it looks like valid data
|
||
|
||
Input: ($object,$name,$bitData,$protocolID, optional: length $bitData);
|
||
Output:
|
||
on success array (returnCode=1, hexData)
|
||
otherwise array (returncode=-1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub mcBit2SomfyRTS {
|
||
my $self = shift // carp 'Not called within an object' && return (0,'no object provided');
|
||
my $name = shift // 'anonymous';
|
||
my $bitData = shift // carp 'bitData must be perovided' && return (0,'no bitData provided');
|
||
my $id = shift // carp 'protocol ID must be provided' && return (0,'no protocolId provided');
|
||
my $mcbitnum = shift // length $bitData;
|
||
|
||
$self->_logging( qq[lib/mcBit2SomfyRTS, bitdata: $bitData ($mcbitnum)], 4 );
|
||
|
||
if ($mcbitnum == 57) {
|
||
$bitData = substr($bitData, 1, 56);
|
||
$self->_logging( qq[lib/mcBit2SomfyRTS, bitdata: $bitData, truncated to length: ]. length($bitData), 4 );
|
||
}
|
||
my $encData = lib::SD_Protocols::binStr2hexStr($bitData);
|
||
|
||
return (1, $encData);
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item mcBit2TFA()
|
||
|
||
extract the message from the bitdata if it looks like valid data
|
||
|
||
Input: ($object,$name,$bitData,$protocolID, optional: length $bitData);
|
||
Output:
|
||
on success array (returnCode=1, hexData)
|
||
otherwise array (returncode=-1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub mcBit2TFA {
|
||
my $self = shift // carp 'Not called within an object' && return (0,'no object provided');
|
||
my $name = shift // "anonymous";
|
||
my $bitData = shift // carp 'bitData must be perovided' && return (0,'no bitData provided');
|
||
my $id = shift // carp 'protocol ID must be provided' && return (0,'no protocolId provided');;;
|
||
my $mcbitnum = shift // length $bitData;
|
||
|
||
my $preamble_pos;
|
||
my $message_end;
|
||
my $message_length;
|
||
|
||
#if ($bitData =~ m/^.?(1){16,24}0101/) {
|
||
if ($bitData =~ m/(1{9}101)/xms )
|
||
{
|
||
$preamble_pos=$+[1];
|
||
$self->_logging( qq[lib/mcBit2TFA, 30.3208.0 preamble_pos = $preamble_pos], 4 );
|
||
return return (-1,q[ sync not found]) if ($preamble_pos <=0);
|
||
my @messages;
|
||
|
||
my $i=1;
|
||
my $retmsg = q{};
|
||
do
|
||
{
|
||
$message_end = index($bitData,'1111111111101',$preamble_pos);
|
||
if ($message_end < $preamble_pos)
|
||
{
|
||
$message_end=$mcbitnum; # length($bitData);
|
||
}
|
||
$message_length = ($message_end - $preamble_pos);
|
||
|
||
my $part_str=substr($bitData,$preamble_pos,$message_length);
|
||
$self->_logging( qq[lib/mcBit2TFA, message start($i)=$preamble_pos end=$message_end with length=$message_length], 4 );
|
||
$self->_logging( qq[lib/mcBit2TFA, message part($i)=$part_str], 5 );
|
||
|
||
my ($rcode, $rtxt) = $self->LengthInRange($id, $message_length);
|
||
if ($rcode) {
|
||
my $hex=lib::SD_Protocols::binStr2hexStr($part_str);
|
||
push (@messages,$hex);
|
||
$self->_logging( qq[lib/mcBit2TFA, message part($i)=$hex], 4 );
|
||
}
|
||
else {
|
||
$retmsg = q[, ] . $rtxt;
|
||
}
|
||
|
||
$preamble_pos=index($bitData,'1101',$message_end)+4;
|
||
$i++;
|
||
} while ($message_end < $mcbitnum);
|
||
|
||
my %seen;
|
||
my @dupmessages = map { 1==$seen{$_}++ ? $_ : () } @messages;
|
||
|
||
return ($i,q[loop error, please report this data $bitData]) if ($i==10);
|
||
if (scalar(@dupmessages) > 0 ) {
|
||
$self->_logging( qq[lib/mcBit2TFA, repeated hex $dupmessages[0] found $seen{$dupmessages[0]} times"], 4 );
|
||
return (1,$dupmessages[0]);
|
||
} else {
|
||
return (-1,qq[ no duplicate found$retmsg]);
|
||
}
|
||
}
|
||
return (-1,undef);
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item postDemo_EM()
|
||
|
||
This function checks the bit sequence. On an error in the CRC or no start, it issues an output.
|
||
|
||
Input: $id,$sum,$msg
|
||
Output:
|
||
prepares message
|
||
|
||
=cut
|
||
|
||
sub postDemo_EM {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my ( $name, @bit_msg ) = @_;
|
||
my $msg = join( q[], @bit_msg );
|
||
my $msg_start = index( $msg, '0000000001' ); # find start
|
||
$msg = substr( $msg, $msg_start + 10 ); # delete preamble + 1 bit
|
||
my $new_msg = q[];
|
||
my $crcbyte;
|
||
my $msgcrc = 0;
|
||
my $msgLength = length $msg;
|
||
|
||
if ( $msg_start > 0 && $msgLength == 89 ) {
|
||
for my $count ( 0 .. $msgLength ) {
|
||
next if $count % 9 != 0;
|
||
$crcbyte = substr( $msg, $count, 8 );
|
||
if ( $count < ( length($msg) - 10 ) ) {
|
||
$new_msg .= join q[],
|
||
reverse @bit_msg[ $msg_start + 10 + $count .. $msg_start + 17 + $count ];
|
||
$msgcrc = $msgcrc ^ oct("0b$crcbyte");
|
||
}
|
||
}
|
||
return (1,split(//xms,$new_msg)) if ($msgcrc == oct( "0b$crcbyte" ));
|
||
|
||
$self->_logging( q[lib/postDemo_EM, protocol - CRC ERROR], 3 );
|
||
return 0, undef;
|
||
}
|
||
|
||
$self->_logging(qq[lib/postDemo_EM, protocol - Start not found or length msg ($msgLength) not correct], 3);
|
||
return 0, undef;
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item postDemo_Revolt()
|
||
|
||
This function checks the bit sequence. On an error in the CRC, it issues an output.
|
||
|
||
Input: $object,$name,@bit_msg
|
||
Output:
|
||
(returncode = 0 on success, prepared message or undef)
|
||
|
||
=cut
|
||
|
||
sub postDemo_Revolt {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $name = shift // carp 'no $name provided';
|
||
my @bit_msg = @_;
|
||
|
||
my $protolength = scalar @bit_msg;
|
||
my $sum = 0;
|
||
|
||
my $checksum = oct( '0b' . ( join "", @bit_msg[ 88 .. 95 ] ) );
|
||
$self->_logging( qq[lib/postDemo_Revolt, length=$protolength], 5 );
|
||
for ( my $b = 0 ; $b < 88 ; $b += 8 ) {
|
||
# build sum over first 11 bytes
|
||
$sum += oct( '0b' . ( join "", @bit_msg[ $b .. $b + 7 ] ) );
|
||
}
|
||
$sum = $sum & 0xFF;
|
||
|
||
if ($sum != $checksum) {
|
||
my $dmsg = lib::SD_Protocols::binStr2hexStr( join "", @bit_msg[ 0 .. 95 ] );
|
||
$self->_logging(qq[lib/postDemo_Revolt, ERROR checksum mismatch, $sum != $checksum in msg $dmsg], 3 );
|
||
return 0, undef;
|
||
}
|
||
my @new_bitmsg = splice @bit_msg, 0,88;
|
||
return 1, @new_bitmsg;
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item postDemo_FS20()
|
||
|
||
This function checks the bit sequence. On an error in the CRC or no start, it issues an output.
|
||
|
||
Input: $object,$name,@bit_msg
|
||
Output:
|
||
(returncode = 0 on success, prepared message or undef)
|
||
|
||
=cut
|
||
|
||
sub postDemo_FS20 {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $name = shift // carp 'no $name provided';
|
||
my @bit_msg = @_;
|
||
|
||
my $protolength = scalar @bit_msg;
|
||
my $datastart = 0;
|
||
my $sum = 6;
|
||
my $b = 0;
|
||
my $i = 0;
|
||
for ( $datastart = 0 ; $datastart < $protolength ; $datastart++ ) {
|
||
# Start bei erstem Bit mit Wert 1 suchen
|
||
last if $bit_msg[$datastart] == 1;
|
||
}
|
||
if ( $datastart == $protolength ) { # all bits are 0
|
||
$self->_logging(qq[lib/postDemo_FS20, ERROR message all bits are zeros], 3 );
|
||
return 0, undef;
|
||
}
|
||
splice( @bit_msg, 0, $datastart + 1 ); # delete preamble + 1 bit
|
||
$protolength = scalar @bit_msg;
|
||
$self->_logging( qq[lib/postDemo_FS20, pos=$datastart length=$protolength], 5 );
|
||
if ( $protolength == 46 || $protolength == 55 )
|
||
{ # If it 1 bit too long, then it will be removed (EOT-Bit)
|
||
pop(@bit_msg);
|
||
$protolength--;
|
||
}
|
||
if ( $protolength == 45 || $protolength == 54 ) { ### FS20 length 45 or 54
|
||
|
||
my $b=0;
|
||
for ( my $b = 0 ; $b < $protolength - 9 ; $b += 9 ) {
|
||
# build sum over first 4 or 5 bytes
|
||
$sum += oct( '0b' . ( join "", @bit_msg[ $b .. $b + 7 ] ) );
|
||
}
|
||
my $checksum = oct( '0b' . ( join "", @bit_msg[ $protolength - 9 .. $protolength - 2 ] ) ) ; # Checksum Byte 5 or 6
|
||
if ( ( ( $sum + 6 ) & 0xFF ) == $checksum )
|
||
{ # Message from FHT80 roothermostat
|
||
$self->_logging(qq[lib/postDemo_FS20, FS20, Detection aborted, checksum matches FHT code], 5 );
|
||
return 0, undef;
|
||
}
|
||
if ( ( $sum & 0xFF ) == $checksum ) { ## FH20 remote control
|
||
for my $b ($b..$protolength-1) {
|
||
next if $b % 9 != 0;
|
||
my $parity = 0; # Parity even
|
||
for my $i ($b..$b+8) { # Parity over 1 byte + 1 bit
|
||
$parity += $bit_msg[$i];
|
||
}
|
||
if ( $parity % 2 != 0 ) {
|
||
$self->_logging(qq[lib/postDemo_FS20, FS20, ERROR - Parity not even], 3 );
|
||
return 0, undef;
|
||
}
|
||
} # parity ok
|
||
for ( my $b = $protolength - 1 ; $b > 0 ; $b -= 9 ) { # delete 5 or 6 parity bits
|
||
splice( @bit_msg, $b, 1 );
|
||
}
|
||
if ( $protolength == 45 ) { ### FS20 length 45
|
||
splice( @bit_msg, 32, 8 ); # delete checksum
|
||
splice( @bit_msg, 24, 0, ( 0, 0, 0, 0, 0, 0, 0, 0 ) ); # insert Byte 3
|
||
}
|
||
else { ### FS20 length 54
|
||
splice( @bit_msg, 40, 8 ); # delete checksum
|
||
}
|
||
my $dmsg = lib::SD_Protocols::binStr2hexStr( join "", @bit_msg );
|
||
$self->_logging(qq[lib/postDemo_FS20, remote control post demodulation $dmsg length $protolength], 4 );
|
||
return ( 1, @bit_msg ); ## FHT80TF ok
|
||
}
|
||
else {
|
||
$self->_logging(qq[lib/postDemo_FS20, ERROR - wrong checksum], 4 );
|
||
}
|
||
}
|
||
else {
|
||
$self->_logging(qq[lib/postDemo_FS20, ERROR - wrong length=$protolength (must be 45 or 54)], 5 );
|
||
}
|
||
return 0, undef;
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item postDemo_FHT80()
|
||
|
||
This function checks the bit sequence. On an error in the CRC or no start, it issues an output.
|
||
|
||
Input: $object,$name,@bit_msg
|
||
Output:
|
||
(returncode = 0 on success, prepared message or undef)
|
||
|
||
=cut
|
||
|
||
sub postDemo_FHT80 {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $name = shift // carp 'no $name provided';
|
||
my @bit_msg = @_;
|
||
|
||
my $datastart = 0;
|
||
my $protolength = scalar @bit_msg;
|
||
my $sum = 12;
|
||
my $b = 0;
|
||
my $i = 0;
|
||
for ($datastart = 0; $datastart < $protolength; $datastart++) { # Start bei erstem Bit mit Wert 1 suchen
|
||
last if $bit_msg[$datastart] == 1;
|
||
}
|
||
if ($datastart == $protolength) { # all bits are 0
|
||
$self->_logging(qq[lib/postDemo_FHT80, ERROR message all bit are zeros], 3 );
|
||
return 0, undef;
|
||
}
|
||
splice(@bit_msg, 0, $datastart + 1); # delete preamble + 1 bit
|
||
$protolength = scalar @bit_msg;
|
||
$self->_logging(qq[lib/postDemo_FHT80, pos=$datastart length=$protolength], 5 );
|
||
if ($protolength == 55) { # If it 1 bit too long, then it will be removed (EOT-Bit)
|
||
pop(@bit_msg);
|
||
$protolength--;
|
||
}
|
||
if ($protolength == 54) { ### FHT80 fixed length
|
||
for($b = 0; $b < 45; $b += 9) { # build sum over first 5 bytes
|
||
$sum += oct( "0b".(join "", @bit_msg[$b .. $b + 7]));
|
||
}
|
||
my $checksum = oct( "0b".(join "", @bit_msg[45 .. 52])); # Checksum Byte 6
|
||
if ((($sum - 6) & 0xFF) == $checksum) { ## Message from FS20 remote contro
|
||
$self->_logging(qq[lib/postDemo_FHT80, Detection aborted, checksum matches FS20 code], 5 );
|
||
return 0, undef;
|
||
}
|
||
if (($sum & 0xFF) == $checksum) { ## FHT80 Raumthermostat
|
||
for($b = 0; $b < 54; $b += 9) { # check parity over 6 byte
|
||
my $parity = 0; # Parity even
|
||
for($i = $b; $i < $b + 9; $i++) { # Parity over 1 byte + 1 bit
|
||
$parity += $bit_msg[$i];
|
||
}
|
||
if ($parity % 2 != 0) {
|
||
$self->_logging(qq[lib/postDemo_FHT80, ERROR - Parity not even], 3 );
|
||
return 0, undef;
|
||
}
|
||
} # parity ok
|
||
for($b = 53; $b > 0; $b -= 9) { # delete 6 parity bits
|
||
splice(@bit_msg, $b, 1);
|
||
}
|
||
if ($bit_msg[26] != 1) { # Bit 5 Byte 3 must 1
|
||
$self->_logging(qq[lib/postDemo_FHT80, ERROR - byte 3 bit 5 not 1], 3 );
|
||
return 0, undef;
|
||
}
|
||
splice(@bit_msg, 40, 8); # delete checksum
|
||
splice(@bit_msg, 24, 0, (0,0,0,0,0,0,0,0)); # insert Byte 3
|
||
my $dmsg = lib::SD_Protocols::binStr2hexStr(join "", @bit_msg);
|
||
$self->_logging(qq[lib/postDemo_FHT80, roomthermostat post demodulation $dmsg], 4 );
|
||
return (1, @bit_msg); ## FHT80 ok
|
||
}
|
||
else {
|
||
$self->_logging(qq[lib/postDemo_FHT80, ERROR - wrong checksum], 4 );
|
||
}
|
||
}
|
||
else {
|
||
$self->_logging(qq[lib/postDemo_FHT80, ERROR - wrong length=$protolength (must be 54)], 5 );
|
||
}
|
||
return 0, undef;
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item postDemo_FHT80TF()
|
||
|
||
This function checks the bit sequence. On an error in the CRC or no start, it issues an output.
|
||
|
||
Input: $object,$name,@bit_msg
|
||
Output:
|
||
(returncode = 0 on success, prepared message or undef)
|
||
|
||
=cut
|
||
|
||
sub postDemo_FHT80TF {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $name = shift // carp 'no $name provided';
|
||
my @bit_msg = @_;
|
||
|
||
my $protolength = scalar @bit_msg;
|
||
my $datastart = 0;
|
||
my $sum = 12;
|
||
my $b = 0;
|
||
if ($protolength < 46) { # min 5 bytes + 6 bits
|
||
$self->_logging(qq[lib/postDemo_FHT80TF, ERROR lenght of message < 46], 4 );
|
||
return 0, undef;
|
||
}
|
||
for ($datastart = 0; $datastart < $protolength; $datastart++) { # Start bei erstem Bit mit Wert 1 suchen
|
||
last if $bit_msg[$datastart] == 1;
|
||
}
|
||
if ($datastart == $protolength) { # all bits are 0
|
||
$self->_logging(qq[lib/postDemo_FHT80TF, ERROR message all bit are zeros], 3 );
|
||
return 0, undef;
|
||
}
|
||
splice(@bit_msg, 0, $datastart + 1); # delete preamble + 1 bit
|
||
$protolength = scalar @bit_msg;
|
||
if ($protolength == 45) { ### FHT80TF fixed length
|
||
for(my $b = 0; $b < 36; $b += 9) { # build sum over first 4 bytes
|
||
$sum += oct( "0b".(join "", @bit_msg[$b .. $b + 7]));
|
||
}
|
||
my $checksum = oct( "0b".(join "", @bit_msg[36 .. 43])); # Checksum Byte 5
|
||
if (($sum & 0xFF) == $checksum) { ## FHT80TF Tuer-/Fensterkontakt
|
||
for(my $b = 0; $b < 45; $b += 9) { # check parity over 5 byte
|
||
my $parity = 0; # Parity even
|
||
for(my $i = $b; $i < $b + 9; $i++) { # Parity over 1 byte + 1 bit
|
||
$parity += $bit_msg[$i];
|
||
}
|
||
if ($parity % 2 != 0) {
|
||
$self->_logging(qq[lib/postDemo_FHT80TF, ERROR Parity not even], 4 );
|
||
return 0, undef;
|
||
}
|
||
} # parity ok
|
||
for(my $b = 44; $b > 0; $b -= 9) { # delete 5 parity bits
|
||
splice(@bit_msg, $b, 1);
|
||
}
|
||
if ($bit_msg[26] != 0) { # Bit 5 Byte 3 must 0
|
||
$self->_logging(qq[lib/postDemo_FHT80TF, ERROR - byte 3 bit 5 not 0], 3 );
|
||
return 0, undef;
|
||
}
|
||
splice(@bit_msg, 32, 8); # delete checksum
|
||
my $dmsg = lib::SD_Protocols::binStr2hexStr(join "", @bit_msg);
|
||
$self->_logging(qq[lib/postDemo_FHT80TF, door/window switch post demodulation $dmsg], 4 );
|
||
return (1, @bit_msg); ## FHT80TF ok
|
||
}
|
||
}
|
||
return 0, undef;
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item postDemo_WS2000()
|
||
|
||
This function checks the bit sequence. On an error in the CRC or no start, it issues an output.
|
||
|
||
Input: $object,$name,@bit_msg
|
||
Output:
|
||
(returncode = 0 on failure, prepared message or undef)
|
||
|
||
=cut
|
||
|
||
sub postDemo_WS2000 {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $name = shift // carp 'no $name provided';
|
||
my @bit_msg = @_;
|
||
|
||
my $protolength = scalar @bit_msg;
|
||
my @new_bit_msg = q{};
|
||
my @datalenghtws = (35,50,35,50,70,40,40,85);
|
||
my $datastart = 0;
|
||
my $datalength = 0;
|
||
my $datalength1 = 0;
|
||
my $index = 0;
|
||
my $data = 0;
|
||
my $dataindex = 0;
|
||
my $check = 0;
|
||
my $sum = 5;
|
||
my $typ = 0;
|
||
my $adr = 0;
|
||
my @sensors = (
|
||
'Thermo',
|
||
'Thermo/Hygro',
|
||
'Rain',
|
||
'Wind',
|
||
'Thermo/Hygro/Baro',
|
||
'Brightness',
|
||
'Pyrano',
|
||
'Kombi'
|
||
);
|
||
|
||
for ($datastart = 0; $datastart < $protolength; $datastart++) { # Start bei erstem Bit mit Wert 1 suchen
|
||
last if $bit_msg[$datastart] == 1;
|
||
}
|
||
if ($datastart == $protolength) { # all bits are 0
|
||
$self->_logging(qq[lib/postDemo_WS2000, ERROR message all bit are zeros],4);
|
||
return 0, undef;
|
||
}
|
||
$datalength = $protolength - $datastart;
|
||
$datalength1 = $datalength - ($datalength % 5); # modulo 5
|
||
$self->_logging(qq[lib/postDemo_WS2000, protolength: $protolength, datastart: $datastart, datalength $datalength],5);
|
||
$typ = oct( '0b'.(join "", reverse @bit_msg[$datastart + 1.. $datastart + 4])); # Sensortyp
|
||
if ($typ > 7) {
|
||
$self->_logging(qq[lib/postDemo_WS2000, Sensortyp $typ - ERROR typ to big (0-7)],5);
|
||
return 0, undef;
|
||
}
|
||
if ($typ == 1 && ($datalength == 45 || $datalength == 46)) {$datalength1 += 5;} # Typ 1 ohne Summe
|
||
if ($datalenghtws[$typ] != $datalength1) { # check lenght of message
|
||
$self->_logging(qq[lib/postDemo_WS2000, Sensortyp $typ - ERROR lenght of message $datalength1 ($datalenghtws[$typ])],4);
|
||
return 0, undef;
|
||
} elsif ($datastart > 10) { # max 10 Bit preamble
|
||
$self->_logging(qq[lib/postDemo_WS2000, ERROR preamble > 10 ($datastart)],4);
|
||
return 0, undef;
|
||
} else {
|
||
do {
|
||
if ($bit_msg[$index + $datastart] != 1) { # jedes 5. Bit muss 1 sein
|
||
$self->_logging(qq[lib/postDemo_WS2000, Sensortyp $typ - ERROR checking bit $index],4);
|
||
return (0, undef);
|
||
}
|
||
$dataindex = $index + $datastart + 1;
|
||
my $rest = $protolength - $dataindex;
|
||
if ($rest < 4) {
|
||
$self->_logging(qq[lib/postDemo_WS2000, Sensortyp $typ - ERROR rest of message < 4 ($rest)],4);
|
||
return (0, undef);
|
||
}
|
||
$data = oct( '0b'.(join '', reverse @bit_msg[$dataindex .. $dataindex + 3]));
|
||
if ($index == 5) {$adr = ($data & 0x07)} # Sensoradresse
|
||
if ($datalength == 45 || $datalength == 46) { # Typ 1 ohne Summe
|
||
if ($index <= $datalength - 5) {
|
||
$check = $check ^ $data; # Check - Typ XOR Adresse XOR bis XOR Check muss 0 ergeben
|
||
}
|
||
} else {
|
||
if ($index <= $datalength - 10) {
|
||
$check = $check ^ $data; # Check - Typ XOR Adresse XOR bis XOR Check muss 0 ergeben
|
||
$sum += $data;
|
||
}
|
||
}
|
||
$index += 5;
|
||
} until ($index >= $datalength -1 );
|
||
}
|
||
if ($check != 0) {
|
||
$self->_logging(qq[lib/postDemo_WS2000, Sensortyp $typ Adr $adr - ERROR check XOR],4);
|
||
return (0, undef);
|
||
} else {
|
||
if ($datalength < 45 || $datalength > 46) { # Summe pruefen, au<61>er Typ 1 ohne Summe
|
||
$data = oct( "0b".(join '', reverse @bit_msg[$dataindex .. $dataindex + 3]));
|
||
if ($data != ($sum & 0x0F)) {
|
||
$self->_logging(qq[lib/postDemo_WS2000, Sensortyp $typ Adr $adr - ERROR sum],4);
|
||
return (0, undef);
|
||
}
|
||
}
|
||
$self->_logging(qq[lib/postDemo_WS2000, Sensortyp $typ Adr $adr - $sensors[$typ]],4);
|
||
$datastart += 1; # [x] - 14_CUL_WS
|
||
@new_bit_msg[4 .. 7] = reverse @bit_msg[$datastart .. $datastart+3]; # [2] Sensortyp
|
||
@new_bit_msg[0 .. 3] = reverse @bit_msg[$datastart+5 .. $datastart+8]; # [1] Sensoradresse
|
||
@new_bit_msg[12 .. 15] = reverse @bit_msg[$datastart+10 .. $datastart+13]; # [4] T 0.1, R LSN, Wi 0.1, B 1, Py 1
|
||
@new_bit_msg[8 .. 11] = reverse @bit_msg[$datastart+15 .. $datastart+18]; # [3] T 1, R MID, Wi 1, B 10, Py 10
|
||
if ($typ == 0 || $typ == 2) { # Thermo (AS3), Rain (S2000R, WS7000-16)
|
||
@new_bit_msg[16 .. 19] = reverse @bit_msg[$datastart+20 .. $datastart+23]; # [5] T 10, R MSN
|
||
} else {
|
||
@new_bit_msg[20 .. 23] = reverse @bit_msg[$datastart+20 .. $datastart+23]; # [6] T 10, Wi 10, B 100, Py 100
|
||
@new_bit_msg[16 .. 19] = reverse @bit_msg[$datastart+25 .. $datastart+28]; # [5] H 0.1, Wr 1, B Fak, Py Fak
|
||
if ($typ == 1 || $typ == 3 || $typ == 4 || $typ == 7) { # Thermo/Hygro, Wind, Thermo/Hygro/Baro, Kombi
|
||
@new_bit_msg[28 .. 31] = reverse @bit_msg[$datastart+30 .. $datastart+33]; # [8] H 1, Wr 10
|
||
@new_bit_msg[24 .. 27] = reverse @bit_msg[$datastart+35 .. $datastart+38]; # [7] H 10, Wr 100
|
||
if ($typ == 4) { # Thermo/Hygro/Baro (S2001I, S2001ID)
|
||
@new_bit_msg[36 .. 39] = reverse @bit_msg[$datastart+40 .. $datastart+43]; # [10] P 1
|
||
@new_bit_msg[32 .. 35] = reverse @bit_msg[$datastart+45 .. $datastart+48]; # [9] P 10
|
||
@new_bit_msg[44 .. 47] = reverse @bit_msg[$datastart+50 .. $datastart+53]; # [12] P 100
|
||
@new_bit_msg[40 .. 43] = reverse @bit_msg[$datastart+55 .. $datastart+58]; # [11] P Null
|
||
}
|
||
}
|
||
}
|
||
return (1, @new_bit_msg);
|
||
}
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item postDemo_WS7035()
|
||
|
||
This function checks the bit sequence. On an error in the CRC or no start, it issues an output.
|
||
|
||
Input: $object,$name,@bit_msg
|
||
Output:
|
||
(returncode = 1 on success, prepared message or undef)
|
||
|
||
=cut
|
||
|
||
sub postDemo_WS7035 {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $name = shift // carp 'no $name provided';
|
||
my @bit_msg = @_;
|
||
|
||
my $msg = join('',@bit_msg);
|
||
my $parity = 0; # Parity even
|
||
my $sum = 0; # checksum
|
||
$self->_logging(qq[lib/postDemo_WS7035, $msg], 4 );
|
||
if (substr($msg,0,8) ne '10100000') { # check ident
|
||
$self->_logging(qq[lib/postDemo_WS7035, ERROR - Ident not 1010 0000],3 );
|
||
return 0, undef;
|
||
} else {
|
||
for(my $i = 15; $i < 28; $i++) { # Parity over bit 15 and 12 bit temperature
|
||
$parity += substr($msg, $i, 1);
|
||
}
|
||
if ($parity % 2 != 0) {
|
||
$self->_logging(qq[lib/postDemo_WS7035, ERROR - Parity not even],3 );
|
||
return 0, undef;
|
||
} else {
|
||
for(my $i = 0; $i < 39; $i += 4) { # Sum over nibble 0 - 9
|
||
$sum += oct('0b'.substr($msg,$i,4));
|
||
}
|
||
if (($sum &= 0x0F) != oct('0b'.substr($msg,40,4))) {
|
||
$self->_logging(qq[lib/postDemo_WS7035, ERROR - wrong checksum],3 );
|
||
return 0, undef;
|
||
} else {
|
||
### ToDo: Regex anstelle der viele substr einfuegen ##
|
||
$self->_logging(qq[lib/postDemo_WS7035, ]. substr($msg,0,4) ." ". substr($msg,4,4) ." ". substr($msg,8,4) ." ". substr($msg,12,4) ." ". substr($msg,16,4) ." ". substr($msg,20,4) ." ". substr($msg,24,4) ." ". substr($msg,28,4) ." ". substr($msg,32,4) ." ". substr($msg,36,4) ." ". substr($msg,40),4 );
|
||
substr($msg, 27, 4, ''); # delete nibble 8
|
||
return (1,split(//,$msg));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item postDemo_WS7053()
|
||
|
||
This function checks the bit sequence. On an error in the CRC or no start, it issues an output.
|
||
|
||
Input: $object,$name,@bit_msg
|
||
Output:
|
||
(returncode = 0 on failure, prepared message or undef)
|
||
|
||
=cut
|
||
|
||
sub postDemo_WS7053 {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $name = shift // carp 'no $name provided';
|
||
my @bit_msg = @_;
|
||
|
||
my $msg = join("",@bit_msg);
|
||
my $parity = 0; # Parity even
|
||
$self->_logging(qq[lib/postDemo_WS7053, MSG = $msg],4);
|
||
my $msg_start = index($msg, '10100000');
|
||
if ($msg_start > 0) { # start not correct
|
||
$msg = substr($msg, $msg_start);
|
||
$msg .= '0';
|
||
$self->_logging(qq[lib/postDemo_WS7053, cut $msg_start char(s) at begin],5);
|
||
}
|
||
if ($msg_start < 0) { # start not found
|
||
$self->_logging(qq[lib/postDemo_WS7053, ERROR - Ident 10100000 not found],3);
|
||
return 0, undef;
|
||
} else {
|
||
if (length($msg) < 32) { # msg too short
|
||
$self->_logging(qq[lib/postDemo_WS7053, ERROR - msg too short, length ] . length($msg),3);
|
||
return 0, undef;
|
||
} else {
|
||
for(my $i = 15; $i < 28; $i++) { # Parity over bit 15 and 12 bit temperature
|
||
$parity += substr($msg, $i, 1);
|
||
}
|
||
if ($parity % 2 != 0) {
|
||
$self->_logging(qq[lib/postDemo_WS7053, ERROR - Parity not even] . length($msg),3);
|
||
return 0, undef;
|
||
} else {
|
||
# Todo substr durch regex ersetzen
|
||
$self->_logging(qq[lib/postDemo_WS7053, before: ] . substr($msg,0,4) ." ". substr($msg,4,4) ." ". substr($msg,8,4) ." ". substr($msg,12,4) ." ". substr($msg,16,4) ." ". substr($msg,20,4) ." ". substr($msg,24,4) ." ". substr($msg,28,4),5);
|
||
# Format from 7053: Bit 0-7 Ident, Bit 8-15 Rolling Code/Parity, Bit 16-27 Temperature (12.3), Bit 28-31 Zero
|
||
my $new_msg = substr($msg,0,28) . substr($msg,16,8) . substr($msg,28,4);
|
||
# Format for CUL_TX: Bit 0-7 Ident, Bit 8-15 Rolling Code/Parity, Bit 16-27 Temperature (12.3), Bit 28 - 35 Temperature (12), Bit 36-39 Zero
|
||
$self->_logging(qq[lib/postDemo_WS7053, after: ] . substr($new_msg,0,4) ." ". substr($new_msg,4,4) ." ". substr($new_msg,8,4) ." ". substr($new_msg,12,4) ." ". substr($new_msg,16,4) ." ". substr($new_msg,20,4) ." ". substr($new_msg,24,4) ." ". substr($new_msg,28,4) ." ". substr($new_msg,32,4) ." ". substr($new_msg,36,4),5);
|
||
return (1,split("",$new_msg));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item postDemo_lengtnPrefix()
|
||
|
||
calculates the hex (in bits) and adds it at the beginning of the message
|
||
|
||
Input: $object,$name,@bit_msg
|
||
Output:
|
||
(returncode = 0 on failure, prepared message or undef)
|
||
|
||
=cut
|
||
|
||
sub postDemo_lengtnPrefix {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $name = shift // carp 'no $name provided';
|
||
my @bit_msg = @_;
|
||
|
||
my $msg = join('',@bit_msg);
|
||
$msg=sprintf('%08b', length($msg)).$msg;
|
||
|
||
return (1,split('',$msg));
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item Convbit2Arctec()
|
||
|
||
This function convert 0 -> 01, 1 -> 10 to be compatible with IT Module.
|
||
|
||
Input: @bit_msg
|
||
Output:
|
||
converted message
|
||
|
||
=cut
|
||
|
||
sub Convbit2Arctec {
|
||
my ( $self, undef, @bitmsg ) = @_;
|
||
$self // carp 'Not called within an object';
|
||
@bitmsg // carp 'no bitmsg provided';
|
||
my $convmsg = join( "", @bitmsg );
|
||
my @replace = qw(01 10);
|
||
|
||
# Convert 0 -> 01 1 -> 10 to be compatible with IT Module
|
||
$convmsg =~ s/(0|1)/$replace[$1]/gx;
|
||
return ( 1, split( //, $convmsg ) );
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item Convbit2itv1()
|
||
|
||
This function convert 0F -> 01 (F) to be compatible with CUL.
|
||
|
||
Input: $msg
|
||
Output:
|
||
converted message
|
||
|
||
=cut
|
||
|
||
sub Convbit2itv1 {
|
||
shift if ref $_[0] eq __PACKAGE__;
|
||
my ( undef, @bitmsg ) = @_;
|
||
@bitmsg // carp 'no bitmsg provided';
|
||
my $msg = join( "", @bitmsg );
|
||
|
||
$msg =~ s/0F/01/gsm; # Convert 0F -> 01 (F) to be compatible with CUL
|
||
return ( 1, split( //, $msg ) ) if ( index( $msg, 'F' ) == -1 );
|
||
return ( 0, 0 );
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item ConvHE800()
|
||
|
||
This function checks the length of the bits.
|
||
If the length is less than 40, it adds a 0.
|
||
|
||
Input: $name, @bit_msg
|
||
Output:
|
||
scalar converted message on success
|
||
|
||
=cut
|
||
|
||
sub ConvHE800 {
|
||
my ( $self, $name, @bit_msg ) = @_;
|
||
$self // carp 'Not called within an object';
|
||
|
||
my $protolength = scalar @bit_msg;
|
||
|
||
if ( $protolength < 40 ) {
|
||
for ( my $i = 0 ; $i < ( 40 - $protolength ) ; $i++ ) {
|
||
push( @bit_msg, 0 );
|
||
}
|
||
}
|
||
return ( 1, @bit_msg );
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item ConvHE_EU()
|
||
|
||
This function checks the length of the bits.
|
||
If the length is less than 72, it adds a 0.
|
||
|
||
Input: $name, @bit_msg
|
||
Output:
|
||
scalar converted message on success
|
||
|
||
=cut
|
||
|
||
sub ConvHE_EU {
|
||
my ( $self, $name, @bit_msg ) = @_;
|
||
my $protolength = scalar @bit_msg;
|
||
|
||
if ( $protolength < 72 ) {
|
||
for ( my $i = 0 ; $i < ( 72 - $protolength ) ; $i++ ) {
|
||
push( @bit_msg, 0 );
|
||
}
|
||
}
|
||
return ( 1, @bit_msg );
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item ConvITV1_tristateToBit()
|
||
|
||
This function Convert 0 -> 00, 1 -> 11, F => 01 to be compatible with IT Module.
|
||
|
||
Input: $msg
|
||
Output:
|
||
converted message
|
||
|
||
=cut
|
||
|
||
sub ConvITV1_tristateToBit {
|
||
shift if ref $_[0] eq __PACKAGE__;
|
||
my ($msg) = @_;
|
||
|
||
$msg =~ s/0/00/gsm;
|
||
$msg =~ s/1/11/gsm;
|
||
$msg =~ s/F/01/gsm;
|
||
$msg =~ s/D/10/gsm;
|
||
|
||
return ( 1, $msg );
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item PreparingSend_FS20_FHT()
|
||
|
||
This function prepares the send message.
|
||
|
||
Input: $id,$sum,$msg
|
||
Output:
|
||
prepares message
|
||
|
||
=cut
|
||
|
||
sub PreparingSend_FS20_FHT {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $id = shift // carp 'no idprovided';
|
||
my $sum = shift // carp 'no sum provided';
|
||
my $msg = shift // carp 'no msg provided';
|
||
|
||
return if ( $id > 74 || $id < 73 );
|
||
|
||
my $temp = 0;
|
||
my $newmsg = q[P] . $id . q[#0000000000001]; # 12 Bit Praeambel, 1 bit
|
||
my $msgLength = length $msg;
|
||
|
||
for my $i ( 0 .. $msgLength - 1 ) {
|
||
next if $i % 2 != 0;
|
||
$temp = hex( substr( $msg, $i, 2 ) );
|
||
$sum += $temp;
|
||
$newmsg .= dec2binppari($temp);
|
||
}
|
||
|
||
$newmsg .= dec2binppari( $sum & 0xFF ); # Checksum
|
||
my $repeats = $id - 71; # FS20(74)=3, FHT(73)=2
|
||
return $newmsg . q[0P#R] . $repeats; # EOT, Pause, 3 Repeats
|
||
}
|
||
|
||
#########################
|
||
# xFSK method functions #
|
||
#########################
|
||
|
||
sub _xFSK_methods_behind_here {
|
||
# only for functionslist - no function!
|
||
}
|
||
|
||
=item ConvBresser_5in1()
|
||
|
||
This function checks number/count of set bits within bytes 14-25 and inverted data of 13 byte further.
|
||
Delete inverted data (nibble 1-27)and reduce message length (nibble 53).
|
||
|
||
Input: $hexData
|
||
Output: $hexData
|
||
scalar converted message on success
|
||
or array (1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub ConvBresser_5in1 {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $hexData = shift // croak 'Error: called without $hexdata as input';
|
||
my $d2;
|
||
my $bit;
|
||
my $bitsumRef;
|
||
my $bitadd = 0;
|
||
my $hexLength = length ($hexData);
|
||
|
||
return ( 1, 'ConvBresser_5in1, hexData is to short' )
|
||
if ( $hexLength < 52 ); # check double, in def length_min set
|
||
|
||
for (my $i = 0; $i < 13; $i++) {
|
||
$d2 = hex(substr($hexData,($i+13)*2,2));
|
||
return ( 1, qq[ConvBresser_5in1, inverted data at pos $i] ) if ((hex(substr($hexData,$i*2,2)) ^ $d2) != 255);
|
||
if ($i == 0) {
|
||
$bitsumRef = $d2;
|
||
} else {
|
||
|
||
while ($d2) {
|
||
$bitadd += $d2 & 1;
|
||
$d2 >>= 1;
|
||
}
|
||
}
|
||
}
|
||
return (1, qq[ConvBresser_5in1, checksumCalc:$bitadd != checksum:$bitsumRef ] ) if ($bitadd != $bitsumRef);
|
||
return substr($hexData, 28, 24);
|
||
}
|
||
|
||
=item ConvBresser_6in1()
|
||
|
||
This function checks CRC16 over bytes 2 - 17 and sum over bytes 2 - 17 (must be 255).
|
||
|
||
Input: $hexData
|
||
Output: $hexData
|
||
scalar converted message on success
|
||
or array (1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub ConvBresser_6in1 {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $hexData = shift // croak 'Error: called without $hexdata as input';
|
||
my $hexLength = length ($hexData);
|
||
|
||
return ( 1, 'ConvBresser_6in1, hexData is to short' ) if ( $hexLength < 36 ); # check double, in def length_min set
|
||
|
||
|
||
return ( 1,'ConvBresser_6in1, missing module , please install modul Digest::CRC' )
|
||
if (!HAS_DigestCRC);
|
||
|
||
|
||
|
||
my $crc = substr( $hexData, 0, 4 );
|
||
my $ctx = Digest::CRC->new(width => 16, poly => 0x1021);
|
||
my $calcCrc = sprintf( "%04X", $ctx->add( pack 'H*', substr( $hexData, 4, 30 ) )->digest );
|
||
$self->_logging(qq[ConvBresser_6in1, calcCRC16 = 0x$calcCrc, CRC16 = 0x$crc],5);
|
||
return ( 1, qq[ConvBresser_6in1, checksumCalc:0x$calcCrc != checksum:0x$crc] ) if ($calcCrc ne $crc);
|
||
|
||
my $sum = 0;
|
||
for (my $i = 2; $i < 18; $i++) {
|
||
$sum += hex(substr($hexData,($i) * 2, 2));
|
||
}
|
||
$sum &= 0xFF;
|
||
$self->_logging(qq[ConvBresser_6in1, sum = $sum],5);
|
||
return ( 1, qq[ConvBresser_6in1, sum $sum != 255] ) if ($sum != 255);
|
||
|
||
return $hexData;
|
||
}
|
||
|
||
=item ConvBresser_7in1()
|
||
|
||
This function makes xor 0xa over all bytes and checks LFSR_digest16
|
||
|
||
Input: $hexData
|
||
Output: $hexDataXorA
|
||
scalar converted message on success
|
||
or array (1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub ConvBresser_7in1 {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $hexData = shift // croak 'Error: called without $hexdata as input';
|
||
my $hexLength = length($hexData);
|
||
|
||
return (1, 'ConvBresser_7in1, hexData is to short') if ($hexLength < 44); # check double, in def length_min set
|
||
return (1, 'ConvBresser_7in1, byte 21 is 0x00') if (substr($hexData,42,2) eq '00'); # check byte 21
|
||
|
||
my $hexDataXorA ='';
|
||
for (my $i = 0; $i < $hexLength; $i++) {
|
||
my $xor = hex(substr($hexData,$i,1)) ^ 0xA;
|
||
$hexDataXorA .= sprintf('%X',$xor);
|
||
}
|
||
$self->_logging(qq[ConvBresser_7in1, msg=$hexData],5);
|
||
$self->_logging(qq[ConvBresser_7in1, xor=$hexDataXorA],5);
|
||
|
||
my $checksum = lib::SD_Protocols::LFSR_digest16(20, 0x8810, 0xba95, substr($hexDataXorA,4,40));
|
||
my $checksumcalc = sprintf('%04X',$checksum ^ hex(substr($hexDataXorA,0,4)));
|
||
$self->_logging(qq[ConvBresser_7in1, checksumCalc:0x$checksumcalc, must be 0x6DF1],5);
|
||
return ( 1, qq[ConvBresser_7in1, checksumCalc:0x$checksumcalc != checksum:0x6DF1] ) if ($checksumcalc ne '6DF1');
|
||
|
||
return $hexDataXorA;
|
||
}
|
||
|
||
=item LFSR_digest16()
|
||
|
||
This function checks 16 bit LFSR
|
||
|
||
Input: $bytes, $gen, $key, $rawData
|
||
Output: $lfsr
|
||
|
||
=cut
|
||
|
||
sub LFSR_digest16 {
|
||
my ($bytes, $gen, $key, $rawData) = @_;
|
||
carp "LFSR_digest16, too few arguments ($bytes, $gen, $key, $rawData)" if @_ < 4;
|
||
return (1, 'LFSR_digest16, rawData is to short') if (length($rawData) < $bytes * 2);
|
||
|
||
my $lfsr = 0;
|
||
for (my $k = 0; $k < $bytes; $k++) {
|
||
my $data = hex(substr($rawData, $k * 2, 2));
|
||
for (my $i = 7; $i >= 0; $i--) {
|
||
if (($data >> $i) & 0x01) {
|
||
$lfsr ^= $key;
|
||
}
|
||
if ($key & 0x01) {
|
||
$key = ($key >> 1) ^ $gen;
|
||
} else {
|
||
$key = ($key >> 1);
|
||
}
|
||
}
|
||
}
|
||
return $lfsr;
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item ConvPCA301()
|
||
|
||
This function checks crc and converts data to a format which the PCA301 module can handle
|
||
croaks if called with less than one parameters
|
||
|
||
Input: $hexData
|
||
Output:
|
||
scalar converted message on success
|
||
or array (1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub ConvPCA301 {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $hexData = shift // croak 'Error: called without $hexdata as input';
|
||
|
||
return ( 1,
|
||
'ConvPCA301, Usage: Input #1, $hexData needs to be at least 24 chars long'
|
||
) if ( length($hexData) < 24 ); # check double, in def length_min set
|
||
|
||
return ( 1,'ConvPCA301, missing module , please install modul Digest::CRC' )
|
||
if (!HAS_DigestCRC);
|
||
|
||
my $checksum = substr( $hexData, 20, 4 );
|
||
my $ctx = Digest::CRC->new(
|
||
width => 16,
|
||
poly => 0x8005,
|
||
init => 0x0000,
|
||
refin => 0,
|
||
refout => 0,
|
||
xorout => 0x0000
|
||
);
|
||
my $calcCrc = sprintf( "%04X",
|
||
$ctx->add( pack 'H*', substr( $hexData, 0, 20 ) )->digest );
|
||
|
||
return ( 1, qq[ConvPCA301, checksumCalc:$calcCrc != checksum:$checksum] )
|
||
if ( $calcCrc ne $checksum );
|
||
|
||
my $channel = hex( substr( $hexData, 0, 2 ) );
|
||
my $command = hex( substr( $hexData, 2, 2 ) );
|
||
my $addr1 = hex( substr( $hexData, 4, 2 ) );
|
||
my $addr2 = hex( substr( $hexData, 6, 2 ) );
|
||
my $addr3 = hex( substr( $hexData, 8, 2 ) );
|
||
my $plugstate = substr( $hexData, 11, 1 );
|
||
my $power1 = hex( substr( $hexData, 12, 2 ) );
|
||
my $power2 = hex( substr( $hexData, 14, 2 ) );
|
||
my $consumption1 = hex( substr( $hexData, 16, 2 ) );
|
||
my $consumption2 = hex( substr( $hexData, 18, 2 ) );
|
||
|
||
return ("OK 24 $channel $command $addr1 $addr2 $addr3 $plugstate $power1 $power2 $consumption1 $consumption2 $checksum" );
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item ConvKoppFreeControl()
|
||
|
||
This function checks crc and converts data to a format which the KoppFreeControl module can handle
|
||
croaks if called with less than one parameters
|
||
|
||
Input: $hexData
|
||
Output:
|
||
scalar converted message on success
|
||
or array (1,"Error message")
|
||
|
||
=cut
|
||
|
||
sub ConvKoppFreeControl {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $hexData = shift // croak 'Error: called without $hexdata as input';
|
||
|
||
# kr07C2AD1A30CC0F0328
|
||
# || |||| || ++-------- Transmitter Code 2
|
||
# || |||| ++-------------- Keycode
|
||
# || ++++------------------ Transmitter Code 1
|
||
# ++------------------------ kr wird von der culfw bei Empfang einer Kopp Botschaft als Kennung gesendet
|
||
#
|
||
# right rawMSG MN;D=07FA5E1721CC0F02FE000000000000;
|
||
# wrong rawMSG MN;D=0A018200CA043A90;
|
||
|
||
return ( 1,
|
||
'ConvKoppFreeControl, Usage: Input #1, $hexData needs to be at least 4 chars long'
|
||
) if ( length($hexData) < 4 ); # check double, in def length_min set
|
||
|
||
my $anz = hex( substr( $hexData, 0, 2 ) ) + 1;
|
||
|
||
return ( 1, 'ConvKoppFreeControl, hexData is to short' )
|
||
if ( length($hexData) < $anz * 2 ); # check double, in def length_min set
|
||
|
||
my $blkck = 0xAA;
|
||
|
||
for my $i ( 0 .. $anz - 1 ) {
|
||
my $d = hex( substr( $hexData, $i * 2, 2 ) );
|
||
$blkck ^= $d;
|
||
}
|
||
|
||
my $checksum = hex( substr( $hexData, $anz * 2, 2 ) );
|
||
|
||
return ( 1,
|
||
qq[ConvKoppFreeControl, checksumCalc:$blkck != checksum:$checksum] )
|
||
if ( $blkck != $checksum );
|
||
return ( "kr" . substr( $hexData, 0, $anz * 2 ) );
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test exists
|
||
=item ConvLaCrosse()
|
||
|
||
This function checks crc and converts data to a format which the LaCrosse module can handle
|
||
croaks if called with less than one parameter
|
||
|
||
Input: $hexData
|
||
Output:
|
||
scalar converted message on success
|
||
or array (1,"Error message")
|
||
|
||
Message Format:
|
||
|
||
.- [0] -. .- [1] -. .- [2] -. .- [3] -. .- [4] -.
|
||
| | | | | | | | | |
|
||
SSSS.DDDD DDN_.TTTT TTTT.TTTT WHHH.HHHH CCCC.CCCC
|
||
| | | || | | | | | | || | | |
|
||
| | | || | | | | | | || | `--------- CRC
|
||
| | | || | | | | | | |`-------- Humidity
|
||
| | | || | | | | | | |
|
||
| | | || | | | | | | `---- weak battery
|
||
| | | || | | | | | |
|
||
| | | || | | | | `----- Temperature T * 0.1
|
||
| | | || | | | |
|
||
| | | || | | `---------- Temperature T * 1
|
||
| | | || | |
|
||
| | | || `--------------- Temperature T * 10
|
||
| | | | `--- new battery
|
||
| | `---------- ID
|
||
`---- START
|
||
|
||
=cut
|
||
|
||
sub ConvLaCrosse {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $hexData = shift // croak 'Error: called without $hexdata as input';
|
||
|
||
croak qq[ConvLaCrosse, Usage: Input #1, $hexData is not valid HEX]
|
||
if (not $hexData =~ /^[0-9a-fA-F]+$/xms) ; # check valid hexData
|
||
|
||
return ( 1,'ConvLaCrosse, Usage: Input #1, $hexData needs to be at least 8 chars long' )
|
||
if ( length($hexData) < 8 ) ; # check number of length for this sub to not throw an error
|
||
|
||
return ( 1,'ConvLaCrosse, missing module , please install modul Digest::CRC' )
|
||
if (!HAS_DigestCRC);
|
||
|
||
my $ctx = Digest::CRC->new( width => 8, poly => 0x31 );
|
||
my $calcCrc = $ctx->add( pack 'H*', substr( $hexData, 0, 8 ) )->digest;
|
||
my $checksum = sprintf( "%d", hex( substr( $hexData, 8, 2 ) ) );
|
||
return ( 1, qq[ConvLaCrosse, checksumCalc:$calcCrc != checksum:$checksum] )
|
||
if ( $calcCrc != $checksum );
|
||
|
||
my $addr =
|
||
( ( hex( substr( $hexData, 0, 2 ) ) & 0x0F ) << 2 ) |
|
||
( ( hex( substr( $hexData, 2, 2 ) ) & 0xC0 ) >> 6 );
|
||
my $temperature = (
|
||
(
|
||
( ( hex( substr( $hexData, 2, 2 ) ) & 0x0F ) * 100 ) +
|
||
( ( ( hex( substr( $hexData, 4, 2 ) ) & 0xF0 ) >> 4 ) * 10 ) +
|
||
( hex( substr( $hexData, 4, 2 ) ) & 0x0F )
|
||
) / 10
|
||
) - 40;
|
||
return ( 1, qq[ConvLaCrosse, temp:$temperature (out of Range)] )
|
||
if ( $temperature >= 60 || $temperature <= -40 )
|
||
; # Shoud be checked in logical module
|
||
|
||
my $humidity = hex( substr( $hexData, 6, 2 ) );
|
||
my $batInserted = ( hex( substr( $hexData, 2, 2 ) ) & 0x20 ) << 2;
|
||
my $SensorType = 1;
|
||
|
||
my $humObat = $humidity & 0x7F;
|
||
|
||
if ( $humObat == 125 ) { # Channel 2 ??? doubtful
|
||
$SensorType = 2;
|
||
}
|
||
### humidity check is in Lacrosse module and some sensors without hum, send a value over 100 ###
|
||
# elsif ( $humObat > 99 ) { # Shoud be checked in logical module
|
||
# return ( -1, qq[ConvLaCrosse: hum:$humObat (out of Range)] );
|
||
# }
|
||
|
||
# build string for 36_LaCrosse.pm
|
||
$temperature = ( ( $temperature * 10 + 1000 ) & 0xFFFF );
|
||
my $t1 = ( $temperature >> 8 ) & 0xFF;
|
||
my $t2 = $temperature & 0xFF;
|
||
my $sensTypeBat = $SensorType | $batInserted;
|
||
return (qq[OK 9 $addr $sensTypeBat $t1 $t2 $humidity]);
|
||
}
|
||
|
||
############################# package lib::SD_Protocols, test not exists
|
||
=item PreparingSend_KOPP_FC()
|
||
|
||
This function calculated crc and prepares the send message.
|
||
|
||
Input: $blkctrInternal,$Keycode,$TransCode1,$TransCode2
|
||
Output:
|
||
prepares message
|
||
|
||
Message Format:
|
||
|
||
https://wiki.fhem.de/wiki/Kopp_Allgemein | https://github.com/heliflieger/a-culfw/blob/master/culfw/clib/kopp-fc.c
|
||
kr07C2AD1A30CC0F0328
|
||
|| |||| || ++-------- Transmitter Code 2
|
||
|| |||| ++-------------- Keycode
|
||
|| ++++------------------ Transmitter Code 1
|
||
++------------------------ kr wird von der culfw bei Empfang einer Kopp Botschaft als Kennung gesendet
|
||
|
||
# $message = "s"
|
||
# . $keycodehex
|
||
# . $hash->{TRANSMITTERCODE1}
|
||
# . $hash->{TRANSMITTERCODE2}
|
||
# . $hash->{TIMEOUT}
|
||
# . "N"; # N for do not print messages (FHEM will write error messages to log files if CCD/CUL sends status info
|
||
|
||
=cut
|
||
|
||
sub PreparingSend_KOPP_FC {
|
||
my $self = shift // carp 'Not called within an object';
|
||
my $blkctrInternal = shift // carp 'Error: called without Internal blkctr as input';
|
||
my $Keycode = shift // carp 'Error: called without $Keycode as input';
|
||
my $TransCode1 = shift // carp 'Error: called without $TransCode1 as input';
|
||
my $TransCode2 = shift // carp 'Error: called without $TransCode2 as input';
|
||
my $blkck = 0xAA;
|
||
my $d;
|
||
|
||
# check from Keycode, TransCode1 and TransCode2 direct in modul 10_KOPP_FC.pm
|
||
$self->_logging(qq[lib/PreparingSend_KOPP_FC, called with all parameters],5);
|
||
|
||
my $dmsg = '07' . $TransCode1 . $blkctrInternal . $Keycode . 'CC0F' . $TransCode2;
|
||
|
||
## checksum to calculate
|
||
for my $i (0..7) {
|
||
$d = hex(substr($dmsg,$i*2,2));
|
||
$blkck ^= $d;
|
||
}
|
||
|
||
$dmsg.= sprintf("%02x",$blkck) . '000000000000;';
|
||
|
||
## additional length check | ToDo: must be checked, CUL data without preamble kr == 18
|
||
# if (length($dmsg) != 31) { # working dmsg with comma == 31 (30 + 1)
|
||
# $self->_logging(qq[lib/PreparingSend_KOPP_FC, ERROR! dmsg wrong length - STOPPING send],2);
|
||
# return;
|
||
# }
|
||
|
||
my $msg = 'SN;R=13;N=4;D=' . $dmsg; # N=4 | to compatible @Ralf
|
||
|
||
return $msg;
|
||
}
|
||
|
||
1;
|