File Coverage

blib/lib/Device/RFXCOM/Decoder/Visonic.pm
Criterion Covered Total %
statement 61 61 100.0
branch 20 20 100.0
condition n/a
subroutine 11 11 100.0
pod 3 3 100.0
total 95 95 100.0


line stmt bran cond sub pod time code
1 3     3   3223 use strict;
  3         5  
  3         110  
2 3     3   16 use warnings;
  3         5  
  3         145  
3             package Device::RFXCOM::Decoder::Visonic;
4             $Device::RFXCOM::Decoder::Visonic::VERSION = '1.142010';
5             # ABSTRACT: Device::RFXCOM::Decoder::Visonic decode Visonic RF messages
6              
7              
8 3     3   59 use 5.006;
  3         12  
  3         150  
9 3     3   15 use constant DEBUG => $ENV{DEVICE_RFXCOM_DECODER_VISONIC_DEBUG};
  3         6  
  3         164  
10 3     3   15 use Carp qw/croak/;
  3         7  
  3         171  
11 3     3   17 use Device::RFXCOM::Decoder qw/hi_nibble lo_nibble/;
  3         7  
  3         250  
12             our @ISA = qw(Device::RFXCOM::Decoder);
13 3     3   15 use Device::RFXCOM::Response::Security;
  3         6  
  3         74  
14 3     3   14 use Device::RFXCOM::Response::Sensor;
  3         5  
  3         2316  
15              
16             my %bits = ( 36 => 'powercode', 66 => 'codesecure' );
17              
18              
19             sub decode {
20 44     44 1 94 my ($self, $parent, $message, $bytes, $bits, $result) = @_;
21 44 100       232 my $method = $bits{$bits} or return;
22 6         27 return $self->$method($parent, $message, $bytes, $bits, $result);
23             }
24              
25              
26             sub codesecure {
27 3     3 1 9 my ($self, $parent, $message, $bytes, $bits, $result) = @_;
28             # parity check?
29              
30 3         18 my $code =
31             sprintf '%02x%02x%02x%02x',
32             $bytes->[0], $bytes->[1], $bytes->[2], $bytes->[3];
33              
34 3         18 my $device =
35             sprintf 'codesecure.%02x%02x%02x%x',
36             $bytes->[4], $bytes->[5], $bytes->[6], hi_nibble($bytes->[7]);
37 3         32 my $event =
38             { 0x1 => "light",
39             0x2 => "arm-away",
40             0x4 => "disarm",
41             0x8 => "arm-home",
42             }->{lo_nibble($bytes->[7])};
43 3 100       429 unless ($event) {
44             # probably invalid message
45             # TOFIX: figure out parity check so this isn't required
46 1         6 return;
47             }
48 2         9 my $repeat = $bytes->[8]&0x4;
49 2         4 my $low_bat = $bytes->[8]&0x8;
50 2         12 my %args =
51             (
52             event => $event,
53             device => $device,
54             );
55 2 100       10 $args{repeat} = 1 if ($repeat);
56 2 100       4 push @{$result->{messages}},
  2         22  
57             Device::RFXCOM::Response::Security->new(%args),
58             Device::RFXCOM::Response::Sensor->new(device => $device,
59             measurement => 'battery',
60             value => $low_bat ? 10 : 90,
61             units => '%');
62 2         13 return 1;
63             }
64              
65              
66             sub powercode {
67 3     3 1 6 my ($self, $parent, $message, $bytes, $bits, $result) = @_;
68 3         6 my $parity;
69 3         7 foreach (0 .. 3) {
70 12         35 $parity ^= hi_nibble($bytes->[$_]);
71 12         31 $parity ^= lo_nibble($bytes->[$_]);
72             }
73 3 100       9 unless ($parity == hi_nibble($bytes->[4])) {
74 1         5 warn
75             sprintf("Possible Visonic powercode with parity error %x != %x\n",
76             $parity, hi_nibble($bytes->[4]));
77 1         12 return;
78             }
79              
80 2         10 my $device = sprintf('powercode.%02x%02x%02x',
81             $bytes->[0], $bytes->[1], $bytes->[2]);
82 2 100       7 $device .= 's' unless ($bytes->[3] & 0x4); # suffix s for secondary contact
83 2         4 my $restore = $bytes->[3] & 0x8;
84 2         3 my $event = $bytes->[3] & 0x10;
85 2         3 my $low_bat = $bytes->[3] & 0x20;
86 2         2 my $alert = $bytes->[3] & 0x40;
87 2         3 my $tamper = $bytes->[3] & 0x80;
88              
89             # I assume $event is to distinguish whether it's a new event or just a
90             # heartbeat message?
91 2 100       11 my %args =
92             (
93             event => $alert ? 'alert' : 'normal',
94             device => $device,
95             );
96 2 100       7 $args{restore} = 1 if ($restore);
97 2 100       5 $args{tamper} = 1 if ($tamper);
98 2 100       3 push @{$result->{messages}},
  2         13  
99             Device::RFXCOM::Response::Security->new(%args),
100             Device::RFXCOM::Response::Sensor->new(device => $device,
101             measurement => 'battery',
102             value => $low_bat ? 10 : 90,
103             units => '%');
104 2         10 return 1;
105             }
106              
107             1;
108              
109             __END__