File Coverage

blib/lib/HiPi/Energenie.pm
Criterion Covered Total %
statement 24 134 17.9
branch 0 50 0.0
condition 0 18 0.0
subroutine 8 14 57.1
pod 0 6 0.0
total 32 222 14.4


line stmt bran cond sub pod time code
1             #########################################################################################
2             # Package HiPi::Energenie
3             # Description: Control Energenie devices
4             # Copyright : Copyright (c) 2016-2020 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   3271 use strict;
  1         2  
  1         49  
14 1     1   6 use warnings;
  1         2  
  1         91  
15 1     1   7 use parent qw( HiPi::Interface );
  1         1  
  1         6  
16 1     1   45 use HiPi qw( :energenie :openthings :rpi );
  1         3  
  1         1034  
17 1     1   543 use HiPi::RF::OpenThings::Message;
  1         3  
  1         54  
18 1     1   566 use UNIVERSAL::require;
  1         1305  
  1         9  
19              
20 1     1   30 use Carp;
  1         2  
  1         83  
21              
22             __PACKAGE__->create_accessors( qw( backend ook_repeat can_rx ) );
23              
24             our $VERSION ='0.82';
25              
26             use constant {
27 1         1541 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          
    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 'RF69HW' ) {
76             # Two way high powered module
77 0           require HiPi::Energenie::ENER314_RT;
78             my $dev = HiPi::Energenie::ENER314_RT->new(
79             led_on => 0,
80             devicename => $params{devicename},
81             reset_gpio => $params{reset_gpio},
82 0           rf_high_power => 1,
83             );
84 0           $params{device} = $dev;
85            
86             } elsif( $params{backend} eq 'ENER314' ) {
87             # simple 1 way single group board
88 0           require HiPi::Energenie::ENER314;
89 0           my $dev = HiPi::Energenie::ENER314->new();
90 0           $params{device} = $dev;
91 0           $params{can_rx} = 0,
92             } else {
93 0           croak qq(Invalid backend $params{backend} specified);
94             }
95             }
96            
97 0           my $self = $class->SUPER::new( %params );
98 0           return $self;
99             }
100              
101             sub pair_socket {
102 0     0 0   my($self, $group_id, $socket) = @_;
103 0 0         croak(qq(Invalid socket $socket)) unless $socket =~ /^1|2|3|4$/;
104 0           my $data = $_ook_switchdata->[$socket]->[0]; # broadcast 'off' message for socket
105 0           $self->device->switch_ook_socket( $group_id, $data, $self->ook_repeat );
106 0           return;
107             }
108              
109             sub switch_socket {
110 0     0 0   my($self, $group_id, $socket, $offon ) = @_;
111 0 0         croak(qq(Invalid socket $socket)) unless $socket =~ /^0|1|2|3|4$/;
112 0           my $data = $_ook_switchdata->[$socket]->[$offon];
113 0           $self->device->switch_ook_socket($group_id, $data, $self->ook_repeat );
114 0           return;
115             }
116              
117             # test what we actually send
118             sub dump_message {
119 0     0 0   my($self, $socket, $offon) = @_;
120 0 0         croak(q(Method requires backend 'ENER314_RT' or 'RF69HW' )) if $self->backend !~ /^ENER314_RT|RF69HW$/;
121 0 0         croak(qq(Invalid socket $socket)) unless $socket =~ /^0|1|2|3|4$/;
122 0 0         $offon = ( $offon ) ? 1 : 0;
123 0           my $data = $_ook_switchdata->[$socket]->[$offon];
124 0           my @tvals = $self->device->make_ook_message( $self->groupid, $data );
125            
126             # print preamble
127 0           print sprintf("preamble : 0x%x, 0x%x, 0x%x, 0x%x\n", @tvals[0..3]);
128             # print group id
129 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]);
130             # print data
131 0           print sprintf("set data : 0x%x, 0x%x\n", @tvals[14..15]);
132 0           return;
133             }
134              
135             sub process_request {
136 0     0 0   my( $self, %params ) = @_;
137            
138 0 0         croak q(Cannot receive and transmit using board ENER314) unless $self->can_rx;
139            
140 0           my $state = STATE_LISTEN;
141            
142 0   0       my $command = $params{command} || 'listen';
143 0           my $timeout = $params{timeout};
144 0   0       $timeout //= 60;
145            
146 0           my $returnval = {
147             success => 0,
148             data => {},
149             error => 'unknown failure to process request',
150             };
151            
152 0           my $waitkeys = {} ;
153            
154 0 0         if($timeout) {
155 0           $timeout = time() + $timeout;
156             }
157            
158 0           my $resendmessage;
159            
160 0 0         if( $command eq 'join' ) {
161 0           $state = STATE_PROCESS_JOIN;
162             }
163            
164 0 0         if( $command eq 'switch' ) {
165 0           $state = STATE_PROCESS_SWITCH;
166            
167 0           my $switchvalue = $params{switch_state};
168 0           my $sensorkey = $params{sensor_key};
169            
170 0           $resendmessage = HiPi::RF::OpenThings::Message->new(
171             cryptseed => ENERGENIE_DEFAULT_CRYPTSEED,
172             sensor_key => $sensorkey,
173             pip => ENERGENIE_DEFAULT_CRYPTPIP,
174             );
175            
176 0           $resendmessage->add_record(
177             id => OPENTHINGS_PARAM_SWITCH_STATE,
178             command => 1,
179             typeid => OPENTHINGS_UINT,
180             value => $switchvalue,
181             );
182            
183 0           $waitkeys->{$sensorkey} = $switchvalue;
184            
185 0           $self->device->send_fsk_message( $resendmessage );
186            
187 0           $returnval->{success} = 0;
188 0           $returnval->{error} = 'switch did not confirm status';
189             }
190            
191 0 0         if( $command eq 'query' ) {
192 0           $state = STATE_PROCESS_QUERY;
193 0           $waitkeys->{$params{sensor_key}} = 1;
194 0           $returnval->{success} = 0;
195 0           $returnval->{error} = 'the queried device did not confirm status';
196             }
197            
198 0           my $continue = 1;
199            
200 0           while ( $continue ) {
201            
202 0 0 0       if( $timeout && $timeout < time() ) {
203 0           $returnval->{success} = 0;
204 0   0       $returnval->{error} ||= '';
205 0           $returnval->{error} .= ' Timed Out';
206 0           last;
207             }
208            
209 0           my $msg = $self->device->receive_fsk_message( ENERGENIE_DEFAULT_CRYPTSEED );
210            
211 0 0         if ( $msg ) {
212            
213 0 0         $msg->decode_buffer unless $msg->is_decoded;
214            
215 0 0         if ( $msg->ok ) {
216            
217 0           my $sensorkey = $msg->sensor_key;
218            
219             # are we interested in this sensor key ? we'll look at everything for now
220            
221             # handle states we are waiting for
222 0 0 0       if( $state == STATE_PROCESS_JOIN && $msg->has_join_cmd ) {
    0 0        
    0          
223            
224 0           my $outmsg = HiPi::RF::OpenThings::Message->new(
225             cryptseed => ENERGENIE_DEFAULT_CRYPTSEED,
226             mid => $msg->manufacturer_id,
227             pid => $msg->product_id,
228             sid => $msg->sensor_id,
229             pip => $msg->encrypt_pip,
230             );
231            
232 0           $outmsg->add_record(
233             id => OPENTHINGS_PARAM_JOIN,
234             command => 0,
235             typeid => OPENTHINGS_UINT,
236             value => undef,
237             );
238 0           $self->device->send_fsk_message( $outmsg );
239 0           $returnval->{success} = 1;
240 0           $returnval->{data} = $msg;
241 0           $returnval->{error} = '';
242 0           last;
243             }
244            
245             elsif( $state == STATE_PROCESS_SWITCH && $msg->has_switch_state ) {
246 0 0         if(exists($waitkeys->{$sensorkey}) ) {
247 0 0         if( $waitkeys->{$sensorkey} == $msg->switch_state ) {
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            
257             elsif( $state == STATE_PROCESS_QUERY ) {
258 0 0         if(exists($waitkeys->{$sensorkey})) {
259 0           delete($waitkeys->{$sensorkey});
260 0           $returnval->{success} = 1;
261 0           $returnval->{data} = $msg;
262 0           $returnval->{error} = '';
263 0           last;
264             }
265             }
266            
267             # if we sent msg whilst target was broadcasting, it won't have
268             # received it, so send it once again
269            
270 0 0         if( $resendmessage ) {
271 0           $self->device->send_fsk_message( $resendmessage );
272 0           $resendmessage = undef;
273             }
274            
275             } else {
276             # msg not ok - ignore it
277             }
278             }
279            
280 0           $self->delay( 10 );
281             }
282            
283 0           return $returnval;
284             }
285              
286             sub get_timestamp {
287 0     0 0   my($self, $epoch) = @_;
288 0   0       $epoch ||= time();
289 0           my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($epoch);
290 0           my $timestamp = sprintf('%u-%02u-%02u %02u:%02u:%02u',
291             $year + 1900, $mon + 1, $mday, $hour, $min, $sec
292             );
293 0           return $timestamp;
294             }
295              
296             1;
297              
298             __END__