File Coverage

blib/lib/HiPi/Energenie.pm
Criterion Covered Total %
statement 24 131 18.3
branch 0 48 0.0
condition 0 18 0.0
subroutine 8 14 57.1
pod 0 6 0.0
total 32 217 14.7


line stmt bran cond sub pod time code
1             #########################################################################################
2             # Package HiPi::Energenie
3             # Description: Control Energenie devices
4             # Copyright : Copyright (c) 2016-2017 Mark Dootson
5             # License : This is free software; you can redistribute it and/or modify it under
6             # the same terms as the Perl 5 programming language system itself.
7             #########################################################################################
8              
9             package HiPi::Energenie;
10              
11             #########################################################################################
12              
13 1     1   3008 use strict;
  1         2  
  1         47  
14 1     1   5 use warnings;
  1         2  
  1         54  
15 1     1   5 use parent qw( HiPi::Interface );
  1         2  
  1         7  
16 1     1   82 use HiPi qw( :energenie :openthings :rpi );
  1         1  
  1         1012  
17 1     1   695 use HiPi::RF::OpenThings::Message;
  1         4  
  1         61  
18 1     1   641 use UNIVERSAL::require;
  1         1312  
  1         11  
19              
20 1     1   31 use Carp;
  1         3  
  1         77  
21              
22             __PACKAGE__->create_accessors( qw( backend ook_repeat can_rx ) );
23              
24             our $VERSION ='0.80';
25              
26             use constant {
27 1         1334 STATE_LISTEN => 2,
28             STATE_PROCESS_JOIN => 3,
29             STATE_PROCESS_SWITCH => 4,
30             STATE_PROCESS_QUERY => 5,
31 1     1   6 };
  1         2  
32              
33             # OOK Switch Data
34             # $data = $switchmask->[$socketnum - 1]->[$offon];
35             # where $socketnum == 0 | 1 | 2 | 3 | 4 and $offon == 0|1;
36             # when $socketnum == 0 then $offon is applied to all sockets
37              
38             my $_ook_switchdata = [
39             [ 0b1100, 0b1101 ], # off / on all sockets
40             [ 0b1110, 0b1111 ], # off / on socket 1
41             [ 0b0110, 0b0111 ], # off / on socket 2
42             [ 0b1010, 0b1011 ], # off / on socket 3
43             [ 0b0010, 0b0011 ], # off / on socket 4
44             ];
45              
46             sub new {
47 0     0 0   my( $class, %userparams ) = @_;
48            
49 0           my %params = (
50             backend => 'ENER314_RT',
51             device => undef,
52             can_rx => 1,
53             devicename => '/dev/spidev0.1',
54             ook_repeat => ENERGENIE_TXOOK_REPEAT_RATE,
55             reset_gpio => RPI_PIN_22,
56             );
57            
58 0           foreach my $key (sort keys(%userparams)) {
59 0           $params{$key} = $userparams{$key};
60             }
61            
62 0 0         unless( defined($params{device}) ) {
63            
64 0 0         if ( $params{backend} eq 'ENER314_RT' ) {
    0          
65            
66             # Two way configurable board
67 0           require HiPi::Energenie::ENER314_RT;
68             my $dev = HiPi::Energenie::ENER314_RT->new(
69             led_on => 0,
70             devicename => $params{devicename},
71             reset_gpio => $params{reset_gpio},
72 0           );
73 0           $params{device} = $dev;
74            
75             } elsif( $params{backend} eq 'ENER314' ) {
76             # simple 1 way single group board
77 0           require HiPi::Energenie::ENER314;
78 0           my $dev = HiPi::Energenie::ENER314->new();
79 0           $params{device} = $dev;
80 0           $params{can_rx} = 0,
81             } else {
82 0           croak qq(Invalid backend $params{backend} specified);
83             }
84             }
85            
86 0           my $self = $class->SUPER::new( %params );
87 0           return $self;
88             }
89              
90             sub pair_socket {
91 0     0 0   my($self, $group_id, $socket) = @_;
92 0 0         croak(qq(Invalid socket $socket)) unless $socket =~ /^1|2|3|4$/;
93 0           my $data = $_ook_switchdata->[$socket]->[0]; # broadcast 'off' message for socket
94 0           $self->device->switch_ook_socket( $group_id, $data, $self->ook_repeat );
95 0           return;
96             }
97              
98             sub switch_socket {
99 0     0 0   my($self, $group_id, $socket, $offon ) = @_;
100 0 0         croak(qq(Invalid socket $socket)) unless $socket =~ /^0|1|2|3|4$/;
101 0           my $data = $_ook_switchdata->[$socket]->[$offon];
102 0           $self->device->switch_ook_socket($group_id, $data, $self->ook_repeat );
103 0           return;
104             }
105              
106             # test what we actually send
107             sub dump_message {
108 0     0 0   my($self, $socket, $offon) = @_;
109 0 0         croak(q(Method requires backend 'ENER314_RT')) if $self->backend ne 'ENER314_RT';
110 0 0         croak(qq(Invalid socket $socket)) unless $socket =~ /^0|1|2|3|4$/;
111 0 0         $offon = ( $offon ) ? 1 : 0;
112 0           my $data = $_ook_switchdata->[$socket]->[$offon];
113 0           my @tvals = $self->device->make_ook_message( $self->groupid, $data );
114            
115             # print preamble
116 0           print sprintf("preamble : 0x%x, 0x%x, 0x%x, 0x%x\n", @tvals[0..3]);
117             # print group id
118 0           print sprintf("group id : 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", @tvals[4..13]);
119             # print data
120 0           print sprintf("set data : 0x%x, 0x%x\n", @tvals[14..15]);
121 0           return;
122             }
123              
124             sub process_request {
125 0     0 0   my( $self, %params ) = @_;
126            
127 0 0         croak q(Cannot receive and transmit using board ENER314) unless $self->can_rx;
128            
129 0           my $state = STATE_LISTEN;
130            
131 0   0       my $command = $params{command} || 'listen';
132 0           my $timeout = $params{timeout};
133 0   0       $timeout //= 60;
134            
135 0           my $returnval = {
136             success => 0,
137             data => {},
138             error => 'unknown failure to process request',
139             };
140            
141 0           my $waitkeys = {} ;
142            
143 0 0         if($timeout) {
144 0           $timeout = time() + $timeout;
145             }
146            
147 0           my $resendmessage;
148            
149 0 0         if( $command eq 'join' ) {
150 0           $state = STATE_PROCESS_JOIN;
151             }
152            
153 0 0         if( $command eq 'switch' ) {
154 0           $state = STATE_PROCESS_SWITCH;
155            
156 0           my $switchvalue = $params{switch_state};
157 0           my $sensorkey = $params{sensor_key};
158            
159 0           $resendmessage = HiPi::RF::OpenThings::Message->new(
160             cryptseed => ENERGENIE_DEFAULT_CRYPTSEED,
161             sensor_key => $sensorkey,
162             pip => ENERGENIE_DEFAULT_CRYPTPIP,
163             );
164            
165 0           $resendmessage->add_record(
166             id => OPENTHINGS_PARAM_SWITCH_STATE,
167             command => 1,
168             typeid => OPENTHINGS_UINT,
169             value => $switchvalue,
170             );
171            
172 0           $waitkeys->{$sensorkey} = $switchvalue;
173            
174 0           $self->device->send_fsk_message( $resendmessage );
175            
176 0           $returnval->{success} = 0;
177 0           $returnval->{error} = 'switch did not confirm status';
178             }
179            
180 0 0         if( $command eq 'query' ) {
181 0           $state = STATE_PROCESS_QUERY;
182 0           $waitkeys->{$params{sensor_key}} = 1;
183 0           $returnval->{success} = 0;
184 0           $returnval->{error} = 'the queried device did not confirm status';
185             }
186            
187 0           my $continue = 1;
188            
189 0           while ( $continue ) {
190            
191 0 0 0       if( $timeout && $timeout < time() ) {
192 0           $returnval->{success} = 0;
193 0   0       $returnval->{error} ||= '';
194 0           $returnval->{error} .= ' Timed Out';
195 0           last;
196             }
197            
198 0           my $msg = $self->device->receive_fsk_message( ENERGENIE_DEFAULT_CRYPTSEED );
199            
200 0 0         if ( $msg ) {
201            
202 0 0         $msg->decode_buffer unless $msg->is_decoded;
203            
204 0 0         if ( $msg->ok ) {
205            
206 0           my $sensorkey = $msg->sensor_key;
207            
208             # are we interested in this sensor key ? we'll look at everything for now
209            
210             # handle states we are waiting for
211 0 0 0       if( $state == STATE_PROCESS_JOIN && $msg->has_join_cmd ) {
    0 0        
    0          
212            
213 0           my $outmsg = HiPi::RF::OpenThings::Message->new(
214             cryptseed => ENERGENIE_DEFAULT_CRYPTSEED,
215             mid => $msg->manufacturer_id,
216             pid => $msg->product_id,
217             sid => $msg->sensor_id,
218             pip => $msg->encrypt_pip,
219             );
220            
221 0           $outmsg->add_record(
222             id => OPENTHINGS_PARAM_JOIN,
223             command => 0,
224             typeid => OPENTHINGS_UINT,
225             value => undef,
226             );
227 0           $self->device->send_fsk_message( $outmsg );
228 0           $returnval->{success} = 1;
229 0           $returnval->{data} = $msg;
230 0           $returnval->{error} = '';
231 0           last;
232             }
233            
234             elsif( $state == STATE_PROCESS_SWITCH && $msg->has_switch_state ) {
235 0 0         if(exists($waitkeys->{$sensorkey}) ) {
236 0 0         if( $waitkeys->{$sensorkey} == $msg->switch_state ) {
237 0           delete($waitkeys->{$sensorkey});
238 0           $returnval->{success} = 1;
239 0           $returnval->{data} = $msg;
240 0           $returnval->{error} = '';
241 0           last;
242             }
243             }
244             }
245            
246             elsif( $state == STATE_PROCESS_QUERY ) {
247 0 0         if(exists($waitkeys->{$sensorkey})) {
248 0           delete($waitkeys->{$sensorkey});
249 0           $returnval->{success} = 1;
250 0           $returnval->{data} = $msg;
251 0           $returnval->{error} = '';
252 0           last;
253             }
254             }
255            
256             # if we sent msg whilst target was broadcasting, it won't have
257             # received it, so send it once again
258            
259 0 0         if( $resendmessage ) {
260 0           $self->device->send_fsk_message( $resendmessage );
261 0           $resendmessage = undef;
262             }
263            
264             } else {
265             # msg not ok - ignore it
266             }
267             }
268            
269 0           $self->delay( 10 );
270             }
271            
272 0           return $returnval;
273             }
274              
275             sub get_timestamp {
276 0     0 0   my($self, $epoch) = @_;
277 0   0       $epoch ||= time();
278 0           my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($epoch);
279 0           my $timestamp = sprintf('%u-%02u-%02u %02u:%02u:%02u',
280             $year + 1900, $mon + 1, $mday, $hour, $min, $sec
281             );
282 0           return $timestamp;
283             }
284              
285             1;
286              
287             __END__