File Coverage

blib/lib/HiPi/Energenie/Command.pm
Criterion Covered Total %
statement 36 650 5.5
branch 0 292 0.0
condition 0 93 0.0
subroutine 12 53 22.6
pod 0 29 0.0
total 48 1117 4.3


line stmt bran cond sub pod time code
1             #########################################################################################
2             # Package HiPi::Energenie::Command
3             # Description : Energenie Command Wrapper
4             # Copyright : Copyright (c) 2017-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::Command;
10              
11             #########################################################################################
12              
13 1     1   1150 use strict;
  1         2  
  1         30  
14 1     1   4 use warnings;
  1         2  
  1         26  
15 1     1   4 use feature 'say';
  1         2  
  1         145  
16 1     1   6 use parent qw( HiPi::Class );
  1         2  
  1         5  
17 1     1   68 use HiPi qw( :openthings :energenie :rpi );
  1         2  
  1         570  
18 1     1   8 use HiPi::Energenie;
  1         13  
  1         39  
19 1     1   457 use HiPi::Utils::Config;
  1         5  
  1         56  
20 1     1   769 use Getopt::Long qw( GetOptionsFromArray );
  1         11113  
  1         4  
21 1     1   175 use JSON;
  1         1  
  1         8  
22 1     1   99 use Try::Tiny;
  1         1  
  1         49  
23 1     1   6 use HiPi::RF::OpenThings::Message;
  1         3  
  1         127  
24              
25             our $VERSION ='0.82';
26              
27             __PACKAGE__->create_accessors( qw( config result mode display options pretty user
28             console_display_message ) );
29              
30             use constant {
31 1         9997 ERROR_SUCCESS => 'ERROR_SUCCESS',
32             ERROR_UNKNOWN => 'ERROR_UNKNOWN',
33             ERROR_MISSING_COMMAND => 'ERROR_MISSING_COMMAND',
34             ERROR_BAD_COMMAND => 'ERROR_BAD_COMMAND',
35             ERROR_BAD_OPTIONS => 'ERROR_BAD_OPTIONS',
36            
37             ERROR_SYSTEM => 'ERROR_SYSTEM',
38            
39             ERROR_CONFIG_INVALID_BOARD => 'ERROR_CONFIG_INVALID_BOARD',
40             ERROR_CONFIG_INVALID_DEVICE => 'ERROR_CONFIG_INVALID_DEVICE',
41             ERROR_CONFIG_INVALID_GPIO => 'ERROR_CONFIG_INVALID_GPIO',
42            
43             ERROR_GROUP_INVALID_OPTIONS => 'ERROR_GROUP_INVALID_OPTIONS',
44             ERROR_GROUP_EXISTING_GROUP => 'ERROR_GROUP_EXISTING_GROUP',
45             ERROR_GROUP_EXISTING_NAME => 'ERROR_GROUP_EXISTING_NAME',
46             ERROR_GROUP_NAME_NOT_FOUND => 'ERROR_GROUP_NAME_NOT_FOUND',
47             ERROR_GROUP_ID_NOT_FOUND => 'ERROR_GROUP_ID_NOT_FOUND',
48            
49             ERROR_PAIR_INVALID_OPTIONS => 'ERROR_PAIR_INVALID_OPTIONS',
50             ERROR_PAIR_GROUP_NOT_FOUND => 'ERROR_PAIR_GROUP_NOT_FOUND',
51             ERROR_PAIR_INVALID_SWITCH => 'ERROR_PAIR_INVALID_SWITCH',
52             ERROR_PAIR_NAME_NOT_FOUND => 'ERROR_PAIR_NAME_NOT_FOUND',
53             ERROR_PAIR_EXISTING_SWITCH => 'ERROR_PAIR_EXISTING_SWITCH',
54            
55             ERROR_SWITCH_INVALID_OPTIONS => 'ERROR_SWITCH_INVALID_OPTIONS',
56             ERROR_SWITCH_GROUP_NOT_FOUND => 'ERROR_SWITCH_GROUP_NOT_FOUND',
57             ERROR_SWITCH_INVALID_SWITCH => 'ERROR_SWITCH_INVALID_SWITCH',
58             ERROR_SWITCH_NAME_NOT_FOUND => 'ERROR_SWITCH_NAME_NOT_FOUND',
59            
60             ERROR_ALIAS_INVALID_OPTIONS => 'ERROR_ALIAS_INVALID_OPTIONS',
61             ERROR_ALIAS_GROUP_NOT_FOUND => 'ERROR_ALIAS_GROUP_NOT_FOUND',
62             ERROR_ALIAS_INVALID_SWITCH => 'ERROR_ALIAS_INVALID_SWITCH',
63            
64             ERROR_USUPPORTED_RX_COMMAND => 'ERROR_USUPPORTED_RX_COMMAND',
65            
66             ERROR_JOIN_INVALID_OPTIONS => 'ERROR_JOIN_INVALID_OPTIONS',
67             ERROR_JOIN_EXISTING_ADAPTER => 'ERROR_JOIN_EXISTING_ADAPTER',
68             ERROR_JOIN_EXISTING_MONITOR => 'ERROR_JOIN_EXISTING_MONITOR',
69             ERROR_JOIN_DEVICE_NOT_FOUND => 'ERROR_JOIN_DEVICE_NOT_FOUND',
70             ERROR_JOIN_FAILED => 'ERROR_JOIN_FAILED',
71            
72             ERROR_ADAPTER_INVALID_OPTIONS => 'ERROR_ADAPTER_INVALID_OPTIONS',
73             ERROR_ADAPTER_NAME_NOT_FOUND => 'ERROR_ADAPTER_NAME_NOT_FOUND',
74             ERROR_ADAPTER_FAILED => 'ERROR_ADAPTER_FAILED',
75            
76             ERROR_MONITOR_INVALID_OPTIONS => 'ERROR_MONITOR_INVALID_OPTIONS',
77             ERROR_MONITOR_NAME_NOT_FOUND => 'ERROR_MONITOR_NAME_NOT_FOUND',
78             ERROR_MONITOR_FAILED => 'ERROR_MONITOR_FAILED',
79            
80 1     1   8 };
  1         2  
81              
82             my $commandopts = {
83             help => {
84             template => undef,
85             defaults => {},
86             },
87             version => {
88             template => undef,
89             defaults => {},
90             },
91             config => {
92             template => [ 'help|h!', 'list|l!', 'device|d:s', 'board|b:s', 'reset|r:s' ],
93             defaults => { help => 0, list => 0, device => undef, board => undef, reset => undef },
94             },
95             group => {
96             template => [ 'help|h!', 'create|c:s', 'delete|d:s', 'rename|r:s', 'group|g:o', 'newname|n:s', 'list|l!',],
97             defaults => { },
98             },
99             pair => {
100             template => [ 'help|h!', 'list|l', 'groupname|g:s', 'switch|s:i', 'name|n:s', ],
101             defaults => { groupname => undef, switch => 0, },
102             },
103             switch => {
104             template => [ 'help|h!', 'list|l!', 'groupname|g:s', 'switch|s:i', 'name|n:s', 'on|1!', 'off|0!', 'all!' ],
105             defaults => { help => 0, list => 0, groupname => undef, switch => undef, on => 0, off => 0, all => 0, } ,
106             },
107             alias => {
108             template => [ 'help|h!', 'list|l!', 'groupname|g:s', 'switch|s:i', 'name|n:s', ],
109             defaults => { help => 0, list => 0, } ,
110             },
111             join => {
112             template => [ 'help|h!', 'list|l!', 'name|n:s', 'delete|d:s', 'rename|r:s', 'timeout|t:i' ],
113             defaults => { help => 0, list => 0, name => '', delete => '', rename => '', timeout => 60 },
114             },
115             adapter => {
116             template => [ 'help|h!', 'list|l!', 'name|n:s', 'query|q!', 'on|1!', 'off|0!', 'timeout|t:i' ],
117             defaults => { name => undef, query => 0, on => 0, off => 0, list => 0, help => 0, timeout => 60 },
118             },
119             monitor => {
120             template => [ 'help|h!', 'list|l!', 'name|n:s', 'timeout|t:i' ],
121             defaults => { name => undef, list => 0, help => 0, timeout => 60 } ,
122             },
123             };
124              
125              
126             sub new {
127 0     0 0   my($class, %userparams ) = @_;
128            
129 0           my %params = (
130             display => 'usage',
131             mode => 'console',
132             pretty => 0,
133             user => getpwuid($>),
134             console_display_message => '',
135             result => {
136             success => 0,
137             command => 'unknown',
138             option => '',
139             error => 'unknown error',
140             errorcode => ERROR_UNKNOWN,
141             data => {},
142             },
143             );
144            
145 0           foreach my $key (sort keys(%userparams)) {
146 0           $params{$key} = $userparams{$key};
147             }
148            
149 0           $params{config} = HiPi::Utils::Config->new(
150             configclass => 'scripts/energenie',
151             default => {
152             version => $VERSION,
153             board => 'ENER314_RT',
154             spi_device => '/dev/spidev0.1',
155             reset_gpio => RPI_PIN_22,
156             led_red_gpio => 0,
157             led_green_gpio => 0,
158             groups => {},
159             adapters => {},
160             monitors => {},
161             switches => {},
162             },
163             );
164            
165 0           my $self = $class->SUPER::new( %params );
166 0           return $self;
167             }
168              
169 0     0 0   sub conf { $_[0]->config->config(); }
170              
171             sub valid_command {
172 0     0 0   my( $self, $command) = @_;
173 0 0 0       return 0 if( length($command) > 40 || $command !~ /^[a-z]+$/ );
174 0 0         return ( exists($commandopts->{$command}) ) ? 1 : 0;
175             }
176            
177             sub handle_command {
178 0     0 0   my $self = shift;
179 0           my @commandargs = @ARGV;
180            
181 0           $self->handle_command_arguments( @commandargs );
182             }
183              
184             sub handle_command_arguments {
185 0     0 0   my ($self, @inputargs) = @_;
186            
187 0           my @commandargs = ();
188            
189             my $result = try {
190            
191 0     0     for my $arg ( @inputargs ) {
192 0 0         if( lc($arg) eq '--json' ) {
    0          
193 0           $self->mode('json');
194             } elsif( lc($arg) eq '--pretty' ) {
195 0           $self->mode('json');
196 0           $self->pretty(1);
197             } else {
198 0           push @commandargs, $arg;
199             }
200             }
201            
202 0 0         $commandargs[0] = 'missing' unless @commandargs;
203            
204 0           my $command = shift @commandargs;
205            
206 0 0         if ( $self->valid_command($command) ) {
207 0           $self->result->{command} = $command;
208 0           my $opt = $commandopts->{$command}->{defaults};
209 0           my $opttemplate = $commandopts->{$command}->{template};
210 0 0         if( $opttemplate ) {
211 0           GetOptionsFromArray(\@commandargs, $opt, @$opttemplate )
212             }
213 0           $self->options( $opt );
214 0           my $commandsub = qq(command_$command);
215 0           $self->$commandsub();
216             } else {
217 0           $self->result->{command} = $command;
218 0 0         my $errorcode = ( $command eq 'missing' ) ? ERROR_MISSING_COMMAND : ERROR_BAD_COMMAND;
219 0           $self->set_result_error( $errorcode, qq(bad or missing command provided : $command ))
220             }
221            
222 0           $self->return_result();
223             } catch {
224 0     0     my $error = $_;
225 0           $error =~ s/["\n]/ /g;
226 0           return qq({"success":0,"errorcode":"ERROR_SYSTEM","error":"$error"});
227 0           };
228            
229 0           return $result;
230             }
231              
232             sub return_result {
233 0     0 0   my $self = shift;
234            
235 0 0         if($self->mode eq 'json') {
236 0 0 0       if( exists( $self->result->{data}) && ref($self->result->{data})->isa('HiPi::RF::OpenThings::Message') ) {
237 0           $self->result->{data} = $self->result->{data}->value_hash;
238             }
239 0           my $j = JSON->new;
240 0 0         my $output = ( $self->pretty ) ? $j->pretty->canonical->encode( $self->result ) : $j->encode( $self->result );
241 0           return $output;
242             }
243            
244 0 0         if( $self->display eq 'usage' ) {
245            
246 0           my $output = '';
247 0 0         if( $self->result->{errorcode} ne 'ERROR_SUCCESS' ) {
248 0           $output .= sprintf(qq(\nERROR : %s : %s\n), $self->result->{errorcode}, $self->result->{error});
249             }
250 0           $output .= $self->get_command_usage($self->result->{command});
251            
252 0           return $output;
253             }
254            
255             # display the command result
256 0 0 0       if( $self->result->{errorcode} ne 'ERROR_SUCCESS' || !$self->result->{success} ) {
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0          
257 0           return sprintf(qq(Command : %s : ERROR : %s : %s), $self->result->{command}, $self->result->{errorcode}, $self->result->{error});
258            
259             # CONFIG
260            
261             } elsif( $self->result->{command} eq 'config') {
262            
263 0           my $output = qq(\nCONFIGURATION\n);
264 0           $output .= qq(-----------------------------------------\n);
265            
266 0 0         if( my $data = $self->result->{data} ) {
267            
268 0           my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime( $data->{epoch} );
269 0           my $timestamp = sprintf('%u-%02u-%02u %02u:%02u:%02u',
270             $year + 1900, $mon + 1, $mday, $hour, $min, $sec
271             );
272            
273 0 0         $output .= qq( Config Version : $data->{version}\n) if $data->{version};
274 0 0         $output .= qq( Energenie Board : $data->{board}\n) if $data->{board};
275 0 0         $output .= qq( Receiver : $data->{can_rx}\n) if $data->{can_rx};
276 0 0         $output .= qq( Uses SPI : $data->{uses_spi}\n) if $data->{uses_spi};
277 0 0 0       $output .= qq( SPI Device : $data->{spi_device}\n) if $data->{spi_device} && $data->{can_rx} eq 'YES';
278 0 0 0       $output .= qq( Reset GPIO : $data->{reset_gpio}\n) if $data->{spi_device} && $data->{can_rx} eq 'YES';
279 0           $output .= qq( Config Saved : $timestamp\n);
280             }
281 0           return $output;
282            
283             # VERSION
284            
285             } elsif( $self->result->{command} eq 'version') {
286 0           return $self->result->{data}->{versiontext};
287            
288             # GROUP
289            
290             } elsif( $self->result->{command} eq 'group') {
291 0           my $output = qq(\nGROUPS\n);
292 0           $output .= qq(-----------------------------------------\n);
293             # $output .= qq(NAME ID\n);
294 0           for my $gname ( sort keys %{ $self->result->{data}->{groups} } ) {
  0            
295 0           my $gid = $self->result->{data}->{groups}->{$gname};
296 0           $output .= sprintf(qq( %-30s %s\n), $gname, $gid);
297             }
298 0           return $output;
299            
300             # SWITCH LIST
301            
302             } elsif( $self->result->{command} =~ /^pair|alias$/
303             || ( $self->result->{command} eq 'switch' && $self->result->{option} eq 'list') ) {
304            
305 0           my $output = qq(\nSWITCHES & SOCKETS\n);
306 0           $output .= qq(------------------------------------------\n);
307 0           $output .= qq( NAME GROUP SWITCH\n);
308 0           for my $switch( sort keys %{ $self->result->{data}->{switches} } ) {
  0            
309 0           my $group = $self->result->{data}->{switches}->{$switch}->{group};
310 0           my $sno = $self->result->{data}->{switches}->{$switch}->{switch};
311 0           $output .= sprintf(qq( %-18s %-18s %s\n), $switch, $group, $sno);
312             }
313 0           return $output;
314            
315             # SWITCH BROADCAST
316            
317            
318             } elsif( $self->result->{command} eq 'switch' && $self->result->{option} ne 'list' ) {
319            
320            
321 0           my $output = sprintf(qq(\nSWITCH BROADCAST STATUS - %s\n), $self->result->{data}->{status});
322 0           $output .= qq(-------------------------------\n);
323            
324 0 0         my @snums = ( $self->result->{data}->{switch} ) ? ( $self->result->{data}->{switch} ) : (1,2,3,4);
325 0           my $group = $self->result->{data}->{groupname};
326 0           $output .= qq( GROUP SWITCH\n);
327 0           for my $switch( @snums ) {
328 0           $output .= sprintf(qq( %-25s %s\n), $group, $switch);
329             }
330            
331 0           return $output;
332            
333             } elsif( $self->result->{command} eq 'join' || ( $self->result->{command} =~ /^adapter|monitor$/ && $self->result->{option} eq 'list' ) ) {
334            
335 0           my $output = qq(\nMONITORS AND ADAPTERS\n);
336 0           $output .= qq(---------------------------------------------------------------------\n);
337 0           $output .= (qq( NAME PRODUCT SENSORID SWITCH\n));
338            
339 0 0         my $listname = ( $self->result->{command} eq 'adapter' ) ? 'adapters' : 'monitors';
340            
341 0           for my $monitor ( sort keys %{ $self->result->{data}->{$listname} } ) {
  0            
342 0           my $data = $self->result->{data}->{$listname}->{$monitor};
343 0 0         my $switch = HiPi::RF::OpenThings->product_can_switch( $data->{manufacturer_id}, $data->{product_id} ) ? 'YES' : ' NO';
344             # my $switch = ( exists( $self->result->{data}->{adapters}->{$monitor}) ) ? 'YES' : ' NO';
345             $output .= sprintf(qq( %-20s %-28s 0x%06X %s\n),
346 0           $monitor, $data->{product_name}, $data->{sensor_id}, $switch
347             );
348             }
349            
350            
351 0           return $output;
352            
353             } elsif( $self->result->{command} =~ /^adapter|monitor$/ && $self->result->{option} ne 'list' ) {
354            
355 0           my $data = $self->result->{data}->value_hash;
356            
357 0           my $output = sprintf(qq(\n%s STATUS REPORT FOR "%s"\n), uc($self->result->{command}), $data->{'configured_name'});
358 0           $output .= qq(---------------------------------------------\n);
359 0           $output .= qq( Sensor Type $data->{product_name}\n);
360 0           $output .= qq( Sensor Key $data->{sensor_key}\n);
361 0           $output .= qq( Timestamp $data->{timestamp}\n);
362 0           $output .= qq( -------------------------------------------\n);
363 0           for my $record ( @{ $data->{records} } ) {
  0            
364 0           my $value = $record->{value};
365 0 0         if( $record->{id} == OPENTHINGS_PARAM_SWITCH_STATE ) {
366 0 0         $value = ( $value ) ? 'ON' : 'OFF';
367             }
368 0 0         my $unitpart = ($record->{units}) ? qq( $record->{units}) : '';
369 0           $output .= sprintf(qq( %-20s %s%s\n), $record->{name}, $value, $unitpart);
370            
371             }
372            
373 0           return $output;
374             # other ?
375             } else {
376 0 0 0       if( exists( $self->result->{data}) && ref($self->result->{data})->isa('HiPi::RF::OpenThings::Message') ) {
377 0           $self->result->{data} = $self->result->{data}->value_hash;
378             }
379 0           my $j = JSON->new;
380 0           my $output = $j->pretty->canonical->encode( $self->result );
381 0           return $output;
382             }
383             }
384              
385             sub set_result_success {
386 0     0 0   my( $self, $data, $option ) = @_;
387 0 0         if( $data ) {
388 0           $self->display('data');
389 0           $self->result->{data} = $data;
390             }
391 0           $self->result->{success} = 1;
392 0           $self->result->{error} = '';
393 0           $self->result->{errorcode} = ERROR_SUCCESS;
394 0 0         $self->result->{option} = $option if $option;
395 0           return;
396             }
397              
398             sub set_result_error {
399 0     0 0   my ($self, $errorcode, $error, $option) = @_;
400 0   0       $error //= $errorcode;
401 0           $self->display('error');
402 0           $self->result->{success} = 0;
403 0           $self->result->{error} = $error;
404 0           $self->result->{errorcode} = $errorcode;
405 0 0         $self->result->{option} = $option if $option;
406 0           return;
407             }
408              
409             sub set_result_options_error {
410 0     0 0   my ($self, $errorcode, $error, $option) = @_;
411 0   0       $error //= $errorcode;
412 0           $self->display('options');
413 0           $self->result->{success} = 0;
414 0           $self->result->{error} = $error;
415 0           $self->result->{errorcode} = $errorcode;
416 0 0         $self->result->{option} = $option if $option;
417 0           return;
418             }
419              
420             sub command_help {
421 0     0 0   my $self = shift;
422 0           $self->set_result_success();
423 0           return;
424             }
425              
426             sub command_version {
427 0     0 0   my $self = shift;
428 0           $self->set_result_success(
429             {
430             version => $VERSION,
431             versiontext => qq(HiPi Energenie Version $VERSION),
432             }
433             );
434 0           return;
435             }
436              
437             sub command_config {
438 0     0 0   my( $self ) = @_;
439            
440 0 0         if( $self->options->{help} ) {
    0          
441 0           $self->set_result_success(undef, 'help');
442 0           return;
443             } elsif( $self->options->{list} ) {
444 0           $self->list_configuration('list');
445 0           return;
446             } else {
447 0           my( $newdevice, $newboard, $newreset );
448            
449 0 0         if ( $self->options->{device} ) {
450 0 0         if( my ($devicename) = ( $self->options->{device} =~ /^(\/dev\/spidev\d\.\d)$/i ) ) {
451 0           $newdevice = $devicename;
452             } else {
453             $self->set_result_error(
454             ERROR_CONFIG_INVALID_DEVICE,
455 0           sprintf(q(Invalid device %s specified), $self->options->{device}),
456             'update',
457             );
458 0           return;
459             }
460             }
461            
462 0 0         if( $self->options->{board} ) {
463 0 0         if( my ($board) = ( $self->options->{board} =~ /^(ENER314|ENER314_RT|RF69HW)$/i ) ) {
464 0           $newboard = uc($board);
465             } else {
466             $self->set_result_error(
467             ERROR_CONFIG_INVALID_BOARD,
468 0           sprintf(q(Invalid board %s specified), $self->options->{board}),
469             'update',
470             );
471 0           return;
472             }
473             }
474            
475 0 0         if( defined($self->options->{reset}) ) {
476             # reset should be a number greater than 0
477 0 0 0       if( $self->options->{reset} && $self->options->{reset} =~ /^\d+$/ ) {
478 0           $newreset = $self->options->{reset};
479             } else {
480             $self->set_result_error(
481             ERROR_CONFIG_INVALID_GPIO,
482 0           sprintf(q(Invalid Reset GPIO %s specified), $self->options->{reset}),
483             'update',
484             );
485 0           return;
486             }
487             }
488            
489 0           my $coption = 'update';
490            
491 0 0         if( $newdevice ) {
492 0           $self->conf->{spi_device} = $newdevice;
493 0           $coption = 'refresh';
494             }
495            
496 0 0         if( $newboard ) {
497 0           $self->conf->{board} = $newboard;
498 0           $coption = 'refresh';
499             }
500            
501 0 0         if( $newreset ) {
502 0           $self->conf->{reset_gpio} = $newreset;
503 0           $coption = 'refresh';
504             }
505            
506             # so put config info in data
507 0           $self->list_configuration($coption);
508             }
509 0           return;
510             }
511              
512             sub command_group {
513 0     0 0   my $self = shift;
514            
515 0           my $create = $self->options->{create};
516 0           my $delete = $self->options->{delete};
517 0           my $rename = $self->options->{rename};
518            
519            
520 0 0         if( $self->options->{help} ) {
    0          
    0          
    0          
    0          
521 0           $self->set_result_success(undef, 'help');
522 0           return;
523             } elsif( $self->options->{list} ) {
524 0           $self->list_groups('list');
525 0           return;
526             } elsif ( $create ) {
527            
528 0 0 0       if( $delete || $rename ) {
529 0           $self->set_result_options_error(
530             ERROR_GROUP_INVALID_OPTIONS,
531             'You must specify only one of --create, --delete, --rename, --list, or --help',
532             'missing',
533             );
534 0           return;
535             }
536            
537 0           for my $existing( keys %{ $self->conf->{groups} } ) {
  0            
538 0 0         if(lc($self->conf->{groups}->{$existing}) eq lc($create) ) {
539 0           $self->set_result_error(
540             ERROR_GROUP_EXISTING_NAME,
541             sprintf('The name %s is already in use for group 0x%05x', $create, $existing),
542             'create'
543             );
544 0           return;
545             }
546             }
547            
548 0           my $group = $self->options->{group};
549            
550 0 0         if( $group ) {
551 0 0         if(exists($self->conf->{groups}->{$group})) {
552 0           $self->set_result_error(
553             ERROR_GROUP_EXISTING_GROUP,
554             sprintf('The group id 0x%05x already exists', $group),
555             'create'
556             );
557 0           return;
558             }
559             }
560            
561 0           my $newgroup = $group;
562            
563 0           while(! $newgroup ) {
564 0           $group = $self->generate_switch_group();
565 0 0         unless(exists($self->conf->{groups}->{$group})) {
566 0           $newgroup = $group;
567             }
568             }
569            
570 0           $self->conf->{groups}->{$newgroup} = $create;
571 0           $self->list_groups('create');
572 0           return;
573             } elsif ( $delete ) {
574              
575 0 0 0       if( $create || $rename ) {
576 0           $self->set_result_options_error(
577             ERROR_GROUP_INVALID_OPTIONS,
578             'You must specify only one of --create, --delete, --rename, --list, or --help',
579             'missing',
580             );
581 0           return;
582             }
583            
584 0           for my $group( keys %{ $self->conf->{groups} } ) {
  0            
585 0 0         if(lc($self->conf->{groups}->{$group}) eq lc($delete) ) {
586             # delete any grouped switches first
587 0           for my $switchname ( keys %{ $self->conf->{switches} } ) {
  0            
588 0 0         if( $self->conf->{switches}->{$switchname}->{group} == $group ) {
589 0           delete($self->conf->{switches}->{$switchname});
590             }
591             }
592 0           delete($self->conf->{groups}->{$group});
593 0           $self->list_groups('delete');
594 0           return;
595             }
596             }
597            
598             $self->set_result_error(
599 0           ERROR_GROUP_NAME_NOT_FOUND,
600             qq(A group with name $delete was not found),
601             'delete'
602             );
603 0           return;
604            
605             } elsif( $rename ) {
606            
607 0 0 0       if( $delete || $create ) {
608 0           $self->set_result_options_error(
609             ERROR_GROUP_INVALID_OPTIONS,
610             'You must specify only one of --create, --delete, --rename, --list, or --help',
611             'missing',
612             );
613 0           return;
614             }
615            
616 0           my $newname = $self->options->{newname};
617 0 0         unless( $newname ) {
618 0           $self->set_result_options_error(
619             ERROR_GROUP_INVALID_OPTIONS,
620             'You must provide both options --rename and --newname to rename an existing group',
621             'rename'
622             );
623 0           return;
624             }
625            
626 0           for my $existing( keys %{ $self->conf->{groups} } ) {
  0            
627 0 0         if(lc($self->conf->{groups}->{$existing}) eq lc($newname) ) {
628 0           $self->set_result_error(
629             ERROR_GROUP_EXISTING_NAME,
630             sprintf('The name %s is already in use for group 0x%05x', $newname, $existing),
631             'rename'
632             );
633 0           return;
634             }
635             }
636            
637 0           for my $existing( keys %{ $self->conf->{groups} } ) {
  0            
638 0 0         if(lc($self->conf->{groups}->{$existing}) eq lc($rename) ) {
639 0           $self->conf->{groups}->{$existing} = $newname;
640 0           $self->list_groups('rename');
641 0           return;
642             }
643             }
644            
645             $self->set_result_error(
646 0           ERROR_GROUP_NAME_NOT_FOUND,
647             qq(A group with name $rename was not found),
648             'delete'
649             );
650            
651 0           return;
652             }
653            
654             $self->set_result_options_error(
655 0           ERROR_GROUP_INVALID_OPTIONS,
656             'You must specify one of --create --delete --rename --list --help',
657             'missing',
658             );
659 0           return;
660             }
661              
662             sub command_pair {
663 0     0 0   my $self = shift;
664            
665 0 0         if( $self->options->{help} ) {
666 0           $self->set_result_success(undef, 'help');
667 0           return;
668             }
669            
670 0 0         if( $self->options->{list} ) {
671 0           $self->list_switches('list');
672 0           return;
673             }
674            
675 0           my $group = 0;
676 0           my $groupname = $self->options->{groupname};
677 0           my $name = $self->options->{name};
678            
679 0 0         if( $self->receiver ) {
680             # we care about group name
681 0 0         unless($groupname) {
682 0           $self->set_result_options_error(
683             ERROR_PAIR_INVALID_OPTIONS,
684             'You must provide a --groupname to pair a socket or switch when using tx/rx board ENER314_RT or RF69HW',
685             'pair'
686             );
687 0           return;
688             }
689            
690             # get the group
691 0           for my $checkgroup( keys %{ $self->conf->{groups} } ) {
  0            
692 0 0         if(lc($self->conf->{groups}->{$checkgroup}) eq lc($groupname) ) {
693 0           $group = $checkgroup;
694 0           last;
695             }
696             }
697            
698 0 0         unless( $group ) {
699 0           $self->set_result_error(
700             ERROR_PAIR_GROUP_NOT_FOUND,
701             qq(Could not find a configured group named $groupname),
702             'pair'
703             );
704 0           return;
705             }
706             } else {
707 0           $group = ENERGENIE_ENER314_DUMMY_GROUP;
708 0           $groupname = 'energenie_single_group';
709             }
710              
711 0           my $switch = $self->options->{switch};
712            
713 0 0 0       unless( $switch && $switch =~ /^1|2|3|4$/) {
714 0           $self->set_result_error(
715             ERROR_PAIR_INVALID_SWITCH,
716             q(You must provide a valid switch number --switch 1|2|3|4),
717             'pair'
718             );
719 0           return;
720             }
721            
722             # pair the switch
723            
724             my $error = try {
725             my $handler = HiPi::Energenie->new(
726             backend => $self->conf->{board},
727             devicename => $self->conf->{spi_device},
728             reset_gpio => $self->conf->{reset_gpio},
729 0     0     );
730            
731 0           $handler->pair_socket( $group, $switch, 5 );
732 0           return '';
733             } catch {
734 0     0     my $err = $_;
735 0           $err =~ s/\n/ /g;
736 0           return $err;
737 0           };
738            
739 0 0         if( $error ) {
740 0           $self->set_result_error( ERROR_SYSTEM, $error, 'pair' );
741 0           return;
742             }
743            
744 0   0       $name ||= qq(${groupname}_switch_${switch});
745            
746 0           $self->conf->{switches}->{$name} = { group => $group, switch => $switch };
747            
748 0           $self->list_switches('pair');
749             }
750              
751             sub command_switch {
752 0     0 0   my $self = shift;
753            
754 0 0         if( $self->options->{help} ) {
755 0           $self->set_result_success(undef, 'help');
756 0           return;
757             }
758            
759 0 0         if( $self->options->{list} ) {
760 0           $self->list_switches('list');
761 0           return;
762             }
763            
764 0           my $group = 0;
765 0           my $groupname = $self->options->{groupname};
766 0 0         my $switch = ( $self->options->{all} ) ? 0 : $self->options->{switch};
767            
768 0           my $name = $self->options->{name};
769            
770 0 0         if($name) {
771 0 0         if(exists($self->conf->{switches}->{$name})) {
772 0           $group = $self->conf->{switches}->{$name}->{group};
773 0           $switch = $self->conf->{switches}->{$name}->{switch};
774 0           $groupname = $self->conf->{groups}->{$group};
775             } else {
776 0           $self->set_result_error(
777             ERROR_SWITCH_NAME_NOT_FOUND,
778             qq(Could not find a configured switch named $name),
779             'switch'
780             );
781 0           return;
782             }
783             }
784            
785            
786 0 0 0       if( $self->receiver && !$group ) {
    0          
787             # we care about group name
788 0 0         unless($groupname) {
789 0           $self->set_result_options_error(
790             ERROR_SWITCH_INVALID_OPTIONS,
791             'You must provide a --groupname to turn on/off a socket or switch',
792             'switch'
793             );
794 0           return;
795             }
796            
797             # get the group
798 0           for my $checkgroup( keys %{ $self->conf->{groups} } ) {
  0            
799 0 0         if(lc($self->conf->{groups}->{$checkgroup}) eq lc($groupname) ) {
800 0           $group = $checkgroup;
801 0           last;
802             }
803             }
804            
805 0 0         unless( $group ) {
806 0           $self->set_result_error(
807             ERROR_SWITCH_GROUP_NOT_FOUND,
808             qq(Could not find a configured group named $groupname),
809             'switch'
810             );
811 0           return;
812             }
813             } elsif( !$self->receiver ) {
814 0           $group = ENERGENIE_ENER314_DUMMY_GROUP;
815 0           $groupname = 'energenie_single_group';
816             }
817            
818 0 0 0       unless( defined($switch) && $switch =~ /^0|1|2|3|4$/) {
819 0           $self->set_result_error(
820             ERROR_SWITCH_INVALID_SWITCH,
821             q(You must provide a valid switch number --switch 0|1|2|3|4 ( 0 switches all switches in the group )),
822             'switch'
823             );
824 0           return;
825             }
826            
827 0 0 0       if(!$self->options->{on} && !$self->options->{off}) {
828 0           $self->set_result_options_error(
829             ERROR_SWITCH_INVALID_OPTIONS,
830             'You must specify either --on or --off',
831             'switch'
832             );
833 0           return;
834             }
835            
836 0 0 0       if($self->options->{on} && $self->options->{off}) {
837 0           $self->set_result_options_error(
838             ERROR_SWITCH_INVALID_OPTIONS,
839             'You must specify either --on or --off - not both',
840             'switch'
841             );
842 0           return;
843             }
844            
845 0 0         my $state = ( $self->options->{on} ) ? 1 : 0;
846            
847             # Set the switch on / off
848            
849             my $error = try {
850             my $handler = HiPi::Energenie->new(
851             backend => $self->conf->{board},
852             devicename => $self->conf->{spi_device},
853             reset_gpio => $self->conf->{reset_gpio},
854 0     0     );
855            
856 0           $handler->switch_socket( $group, $switch, $state );
857 0           return '';
858             } catch {
859 0     0     my $err = $_;
860 0           $err =~ s/\n/ /g;
861 0           return $err;
862 0           };
863            
864 0 0         if( $error ) {
865 0           $self->set_result_error( ERROR_SYSTEM, $error, 'switch' );
866 0           return;
867             }
868            
869 0 0         my $groupid = ( $self->receiver ) ? $self->format_group($group) : '';
870            
871 0 0         my $statename = ( $state ) ? 'ON' : 'OFF';
872            
873 0           my $resultdata = { switch => $switch, status => $statename };
874            
875 0           $resultdata->{group} = $self->format_group($group);
876 0           $resultdata->{groupname} = $groupname;
877            
878 0           $self->set_result_success( $resultdata, 'switch' );
879            
880             }
881              
882             sub command_alias {
883 0     0 0   my $self = shift;
884            
885 0 0         if( $self->options->{help} ) {
886 0           $self->set_result_success(undef, 'help');
887 0           return;
888             }
889            
890 0 0         if( $self->options->{list} ) {
891 0           $self->list_switches('list');
892 0           return;
893             }
894            
895 0           my $groupname = $self->options->{groupname};
896 0           my $switch = $self->options->{switch};
897 0           my $name = $self->options->{name};
898 0           my $group = 0;
899            
900 0 0 0       unless( $switch && $switch =~ /^|1|2|3|4$/) {
901 0           $self->set_result_error(
902             ERROR_ALIAS_INVALID_SWITCH,
903             q(You must provide a valid switch number --switch 0|1|2|3|4 ( 0 switches all switches in the group )),
904             'alias'
905             );
906 0           return;
907             }
908            
909 0 0         unless( $name ) {
910 0           $self->set_result_error(
911             ERROR_ALIAS_INVALID_OPTIONS,
912             q(You must provide at least --switch and --name to alias a switch),
913             'alias'
914             );
915 0           return;
916             }
917            
918 0 0         if( $self->receiver ) {
919             # we care about group name
920 0 0         unless($groupname) {
921 0           $self->set_result_options_error(
922             ERROR_ALIAS_INVALID_OPTIONS,
923             'You must provide --groupname, --switch and --name to alias a switch when using the txrx ENER314_RT or RF69HW boards',
924             'alias'
925             );
926 0           return;
927             }
928            
929             # get the group
930 0           for my $checkgroup( keys %{ $self->conf->{groups} } ) {
  0            
931 0 0         if(lc($self->conf->{groups}->{$checkgroup}) eq lc($groupname) ) {
932 0           $group = $checkgroup;
933 0           last;
934             }
935             }
936            
937 0 0         unless( $group ) {
938 0           $self->set_result_error(
939             ERROR_ALIAS_GROUP_NOT_FOUND,
940             qq(Could not find a configured group named $groupname),
941             'alias'
942             );
943 0           return;
944             }
945             } else {
946 0           $group = ENERGENIE_ENER314_DUMMY_GROUP;
947 0           $groupname = 'energenie_single_group';
948             }
949            
950             # do we have an alias for this group already
951            
952 0           for my $switchname ( keys %{ $self->conf->{switches} } ) {
  0            
953 0 0 0       if( $self->conf->{switches}->{$switchname}->{switch} == $switch
954             && $self->conf->{switches}->{$switchname}->{group} == $group )
955             {
956 0           delete($self->conf->{switches}->{$switchname});
957             }
958             }
959            
960 0           $self->conf->{switches}->{$name} = { group => $group, switch => $switch };
961 0           $self->list_switches('alias');
962 0           return;
963             }
964              
965              
966             sub command_join {
967 0     0 0   my $self = shift;
968            
969 0 0         if( $self->options->{help} ) {
970 0           $self->set_result_success(undef, 'help');
971 0           return;
972             }
973            
974 0 0         if( $self->options->{list} ) {
975 0           $self->list_adapters_monitors('list', 'both');
976 0           return;
977             }
978            
979 0 0         unless( $self->receiver ) {
980 0           $self->set_result_error(
981             ERROR_USUPPORTED_RX_COMMAND,
982             q(You must be using board ENER314_RT or RF69HW to use join command),
983             'join'
984             );
985 0           return;
986             }
987            
988 0           my $name = $self->options->{name};
989 0           my $delete = $self->options->{delete};
990 0           my $rename = $self->options->{rename};
991            
992 0 0         if( $delete ) {
    0          
993 0           my $deleted;
994 0 0         if(exists($self->conf->{adapters}->{$delete})) {
995 0           delete($self->conf->{adapters}->{$delete});
996 0           $deleted = 1;
997             }
998 0 0         if(exists($self->conf->{monitors}->{$delete})) {
999 0           delete($self->conf->{monitors}->{$delete});
1000 0           $deleted = 1;
1001             }
1002 0 0         if( $deleted ) {
1003 0           $self->list_adapters_monitors('delete', 'both' );
1004 0           return;
1005             } else {
1006 0           $self->set_result_error(
1007             ERROR_JOIN_DEVICE_NOT_FOUND,
1008             qq(The adaptor or monitor named $delete was not found in configuration),
1009             'delete'
1010             );
1011 0           return;
1012             }
1013             } elsif( $rename ) {
1014 0 0         unless( $name ) {
1015 0           $self->set_result_options_error(
1016             ERROR_JOIN_INVALID_OPTIONS,
1017             q(You must provide a --name option together with iption --rename),
1018             'rename'
1019             );
1020 0           return;
1021             }
1022            
1023             # does name already exist
1024 0 0         if(exists($self->conf->{monitors}->{$name})) {
1025 0           $self->set_result_error(
1026             ERROR_JOIN_EXISTING_ADAPTER,
1027             qq(An adapter or monitor named $name already exists in your configuration ),
1028             'rename'
1029             );
1030 0           return;
1031             }
1032             # does rename exist
1033            
1034 0 0         unless(exists($self->conf->{monitors}->{$rename})) {
1035 0           $self->set_result_error(
1036             ERROR_JOIN_DEVICE_NOT_FOUND,
1037             qq(The adaptor or monitor named $rename was not found in configuration),
1038             'rename'
1039             );
1040 0           return;
1041             }
1042            
1043 0           $self->conf->{monitors}->{$name} = $self->conf->{monitors}->{$rename};
1044 0           delete( $self->conf->{monitors}->{$rename} );
1045            
1046 0 0         if(exists($self->conf->{adapters}->{$rename})) {
1047 0           $self->conf->{adapters}->{$name} = $self->conf->{adapters}->{$rename};
1048 0           delete( $self->conf->{adapters}->{$rename} );
1049             }
1050            
1051 0           $self->list_adapters_monitors('rename', 'both');
1052 0           return;
1053             }
1054            
1055             # does name already exist
1056 0 0         if(exists($self->conf->{adapters}->{$name})) {
1057 0           $self->set_result_error(
1058             ERROR_JOIN_EXISTING_ADAPTER,
1059             qq(An adapter or monitor named $name already exists in your configuration),
1060             'rename'
1061             );
1062 0           return;
1063             }
1064            
1065             # only show on console
1066 0           print STDERR qq(\nListening for join messages from adapters and monitors. Set your device mode to join ....\n\n);
1067            
1068 0           my $timeout = $self->options->{timeout};
1069 0 0         unless( $timeout =~ /^[1-9][0-9]*$/) {
1070 0           $self->set_result_options_error(
1071             ERROR_JOIN_INVALID_OPTIONS,
1072             qq(Invalid value for timeout $timeout),
1073             );
1074 0           return;
1075             }
1076            
1077             my $result = try {
1078             my $handler = HiPi::Energenie->new(
1079             backend => $self->conf->{board},
1080             devicename => $self->conf->{spi_device},
1081             reset_gpio => $self->conf->{reset_gpio},
1082 0     0     );
1083            
1084 0           return $handler->process_request(
1085             command => 'join',
1086             timeout => $timeout,
1087             );
1088             } catch {
1089 0     0     return { success => 0, error => $_ , catch_errorcode => ERROR_SYSTEM };
1090 0           };
1091            
1092 0 0         if( $result->{success} ) {
1093            
1094 0           my $joinmsg = $result->{data};
1095            
1096             # check if we know this sensor key
1097 0 0         if(my $registered_name = $self->registered_sensor( $joinmsg->sensor_key ) ) {
1098 0           my $sensorkey = $joinmsg->sensor_key;
1099 0           $self->set_result_error( ERROR_JOIN_FAILED, qq(Device $sensorkey is already registered as $registered_name), 'join' );
1100 0           return;
1101             }
1102            
1103 0           $self->conf->{monitors}->{$name} = {
1104             manufacturer_id => $joinmsg->manufacturer_id,
1105             product_id => $joinmsg->product_id,
1106             product_name => $joinmsg->product_name,
1107             sensor_id => $joinmsg->sensor_id,
1108             sensor_key => $joinmsg->sensor_key,
1109             };
1110            
1111 0 0         if( HiPi::RF::OpenThings->product_can_switch( $joinmsg->manufacturer_id, $joinmsg->product_id ) ) {
1112 0           $self->conf->{adapters}->{$name} = {
1113             manufacturer_id => $joinmsg->manufacturer_id,
1114             product_id => $joinmsg->product_id,
1115             product_name => $joinmsg->product_name,
1116             sensor_id => $joinmsg->sensor_id,
1117             sensor_key => $joinmsg->sensor_key,
1118             };
1119             }
1120            
1121 0           $self->list_adapters_monitors('join', 'both' );
1122             } else {
1123 0           my $error = $result->{error};
1124 0           $error =~ s/\n/ /g;
1125 0   0       my $errorcode = $result->{catch_errorcode} || ERROR_JOIN_FAILED;
1126 0           $self->set_result_error( $errorcode, $error, 'join' );
1127             }
1128             }
1129              
1130             sub command_adapter {
1131 0     0 0   my $self = shift;
1132            
1133 0 0         if( $self->options->{help} ) {
1134 0           $self->set_result_success(undef, 'help');
1135 0           return;
1136             }
1137            
1138 0 0         if( $self->options->{list} ) {
1139 0           $self->list_adapters_monitors('list', 'adapters');
1140 0           return;
1141             }
1142            
1143 0 0         unless( $self->receiver ) {
1144 0           $self->set_result_error(
1145             ERROR_USUPPORTED_RX_COMMAND,
1146             q(You must be using board ENER314_RT or RF69HW to use the adapter command),
1147             'switch'
1148             );
1149 0           return;
1150             }
1151            
1152 0           my $name = $self->options->{name};
1153            
1154 0 0         unless($name) {
1155 0           $self->set_result_options_error(
1156             ERROR_ADAPTER_INVALID_OPTIONS,
1157             q(You must provide an adapter --name for the adapter command),
1158              
1159             );
1160 0           return;
1161             }
1162            
1163 0           my $nameconfig;
1164            
1165             # get the name
1166 0           for my $checkname( keys %{ $self->conf->{adapters} } ) {
  0            
1167 0 0         if(lc($checkname) eq lc($name) ) {
1168 0           $nameconfig = $self->conf->{adapters}->{$checkname};
1169 0           last;
1170             }
1171             }
1172            
1173 0 0         unless( $nameconfig ) {
1174 0           $self->set_result_error(
1175             ERROR_ADAPTER_NAME_NOT_FOUND,
1176             qq(Could not find a configured adapter named $name)
1177             );
1178 0           return;
1179             }
1180            
1181 0           my $timeout = $self->options->{timeout};
1182              
1183 0 0         if( $self->options->{query} ) {
1184 0 0         unless( $timeout =~ /^[1-9][0-9]*$/) {
1185 0           $self->set_result_options_error(
1186             ERROR_ADAPTER_INVALID_OPTIONS,
1187             qq(Invalid value for timeout $timeout),
1188             'query',
1189             );
1190 0           return;
1191             }
1192 0           $self->do_monitor_query( $nameconfig, 'adapter', $name, $timeout );
1193 0           return;
1194             }
1195            
1196             # in switch mode
1197            
1198 0 0 0       if(!$self->options->{on} && !$self->options->{off}) {
1199 0           $self->set_result_options_error(
1200             ERROR_ADAPTER_INVALID_OPTIONS,
1201             q(You must specify either --on or --off to switch an adapter),
1202             'switch',
1203             );
1204 0           return;
1205             }
1206            
1207 0 0 0       if($self->options->{on} && $self->options->{off}) {
1208 0           $self->set_result_options_error(
1209             ERROR_ADAPTER_INVALID_OPTIONS,
1210             q(You must specify either --on or --off - not both to switch an adapter),
1211             'switch',
1212             );
1213 0           return;
1214             }
1215            
1216 0 0         unless( $timeout =~ /^[1-9][0-9]*$/) {
1217 0           $self->set_result_options_error(
1218             ERROR_ADAPTER_INVALID_OPTIONS,
1219             qq(Invalid value for timeout $timeout),
1220             'switch',
1221             );
1222 0           return;
1223             }
1224            
1225 0 0         my $state = ( $self->options->{on} ) ? 1 : 0;
1226            
1227             # do switch
1228            
1229             my $result = try {
1230             my $handler = HiPi::Energenie->new(
1231             backend => $self->conf->{board},
1232             devicename => $self->conf->{spi_device},
1233             reset_gpio => $self->conf->{reset_gpio},
1234 0     0     );
1235            
1236             my $val = $handler->process_request(
1237             command => 'switch',
1238             sensor_key => $nameconfig->{sensor_key},
1239 0           switch_state => $state,
1240             timeout => $timeout,
1241             );
1242            
1243 0           return $val;
1244             } catch {
1245 0     0     return { success => 0, error => $_ , catch_errorcode => ERROR_SYSTEM };
1246 0           };
1247            
1248 0 0         if( !$result->{success} ) {
1249 0           my $error = $result->{error};
1250 0           $error =~ s/\n/ /g;
1251 0   0       my $errorcode = $result->{catch_errorcode} || ERROR_ADAPTER_FAILED;
1252 0           $self->set_result_error( $errorcode, $error, 'switch' );
1253 0           return;
1254             }
1255            
1256 0           my $data = $result->{data};
1257 0           $data->configured_name( $name );
1258            
1259 0           $self->set_result_success($data, 'switch');
1260             }
1261              
1262             sub command_monitor {
1263 0     0 0   my $self = shift;
1264            
1265 0 0         if( $self->options->{help} ) {
1266 0           $self->set_result_success(undef, 'help');
1267 0           return;
1268             }
1269            
1270 0 0         if( $self->options->{list} ) {
1271 0           $self->list_adapters_monitors('list', 'monitors');
1272 0           return;
1273             }
1274            
1275 0 0         unless( $self->receiver ) {
1276 0           $self->set_result_error(
1277             ERROR_USUPPORTED_RX_COMMAND,
1278             q(You must be using board ENER314_RT or RF69HW to use the monitor command),
1279             'switch'
1280             );
1281 0           return;
1282             }
1283            
1284 0           my $name = $self->options->{name};
1285            
1286 0 0         unless($name) {
1287 0           $self->set_result_options_error(
1288             ERROR_MONITOR_INVALID_OPTIONS,
1289             q(You must provide a monitor --name for the monitor command),
1290              
1291             );
1292 0           return;
1293             }
1294            
1295            
1296 0           my $nameconfig;
1297            
1298             # get the name
1299 0           for my $checkname( keys %{ $self->conf->{monitors} } ) {
  0            
1300 0 0         if(lc($checkname) eq lc($name) ) {
1301 0           $nameconfig = $self->conf->{monitors}->{$checkname};
1302 0           last;
1303             }
1304             }
1305            
1306 0 0         unless( $nameconfig ) {
1307 0           $self->set_result_error(
1308             ERROR_MONITOR_NAME_NOT_FOUND,
1309             qq(Could not find a configured monitor named $name)
1310             );
1311 0           return;
1312             }
1313            
1314 0           my $timeout = $self->options->{timeout};
1315 0 0         unless( $timeout =~ /^[1-9][0-9]*$/) {
1316 0           $self->set_result_options_error(
1317             ERROR_MONITOR_INVALID_OPTIONS,
1318             qq(Invalid value for timeout $timeout),
1319             );
1320 0           return;
1321             }
1322            
1323 0           $self->do_monitor_query( $nameconfig, 'monitor', $name, $timeout );
1324             }
1325              
1326             sub do_monitor_query {
1327 0     0 0   my ( $self, $nameconfig, $type, $configname, $timeout ) = @_;
1328            
1329             my $result = try {
1330             my $handler = HiPi::Energenie->new(
1331             backend => $self->conf->{board},
1332             devicename => $self->conf->{spi_device},
1333             reset_gpio => $self->conf->{reset_gpio},
1334 0     0     );
1335            
1336             my $val = $handler->process_request(
1337             command => 'query',
1338             sensor_key => $nameconfig->{sensor_key},
1339 0           timeout => $timeout,
1340             );
1341            
1342 0           return $val;
1343             } catch {
1344 0     0     return { success => 0, error => $_ , catch_errorcode => ERROR_SYSTEM };
1345 0           };
1346            
1347 0 0         if( !$result->{success} ) {
1348 0           my $error = $result->{error};
1349 0           $error =~ s/\n/ /g;
1350 0 0         my $alterror = ( $type eq 'adapter ') ? ERROR_ADAPTER_FAILED : ERROR_MONITOR_FAILED;
1351 0   0       my $errorcode = $result->{catch_errorcode} || $alterror;
1352 0           $self->set_result_error( $errorcode, $error, 'query' );
1353 0           return;
1354             }
1355            
1356 0           my $data = $result->{data};
1357            
1358 0           $data->configured_name( $configname );
1359            
1360 0           $self->set_result_success($data, 'query');
1361            
1362 0           return;
1363             }
1364              
1365             sub list_configuration {
1366 0     0 0   my ($self, $option ) = @_;
1367 0           $self->config->write_config;
1368             $self->set_result_success( {
1369             'version' => $self->conf->{version},
1370             'board' => $self->conf->{board},
1371             'spi_device' => $self->conf->{spi_device},
1372             'reset_gpio' => $self->conf->{reset_gpio},
1373             'uses_spi' => ( $self->receiver ) ? 'YES' : 'NO',
1374             'can_rx' => ( $self->receiver ) ? 'YES' : 'NO',
1375 0 0 0       'epoch' => $self->conf->{epoch} || 1,
    0          
1376             }, $option );
1377 0           return;
1378             }
1379              
1380             sub list_groups {
1381 0     0 0   my ($self, $option ) = @_;
1382            
1383 0           my $groups = {};
1384            
1385 0           for my $group ( keys %{ $self->conf->{groups} } ) {
  0            
1386 0           my $groupname = $self->conf->{groups}->{$group};
1387 0           $groups->{$groupname} = $self->format_group( $group );
1388             }
1389            
1390 0           $self->set_result_success( { groups => $groups }, $option );
1391 0           return;
1392             }
1393              
1394             sub list_adapters_monitors {
1395 0     0 0   my ($self, $option, $type) = @_;
1396            
1397 0 0         my @todolist = ( $type eq 'both' ) ? ( qw( adapters monitors ) ) : ( $type );
1398            
1399 0           my $resdata = {};
1400            
1401 0           for my $item ( @todolist ) {
1402 0           my $data = {};
1403 0           my $conf = $self->conf->{$item};
1404 0           for my $member ( keys %$conf ) {
1405 0           for my $element ( keys %{ $conf->{$member} } ) {
  0            
1406 0           $data->{$member}->{$element} = $conf->{$member}->{$element};
1407             }
1408             }
1409 0           $resdata->{$item} = $data;
1410             }
1411            
1412 0           $self->set_result_success( $resdata, $option );
1413            
1414 0           return;
1415             }
1416              
1417             sub list_switches {
1418 0     0 0   my ($self, $option) = @_;
1419            
1420 0           my $data = {};
1421 0           my $conf = $self->conf->{switches};
1422 0           for my $member ( keys %$conf ) {
1423            
1424 0           $data->{switches}->{$member}->{switch} = $conf->{$member}->{switch};
1425 0           $data->{switches}->{$member}->{group} = $self->conf->{groups}->{$conf->{$member}->{group}};
1426             }
1427            
1428 0           $self->set_result_success( $data, $option );
1429            
1430 0           return;
1431             }
1432              
1433             sub registered_sensor {
1434 0     0 0   my( $self, $sensorkey ) = @_;
1435 0           $sensorkey = lc($sensorkey);
1436 0           for my $item ( qw( adapters monitors ) ) {
1437 0           my $conf = $self->conf->{$item};
1438 0           for my $member ( keys %$conf ) {
1439 0 0         if( lc($conf->{$member}->{sensor_key}) eq $sensorkey ) {
1440 0           return $member;
1441             }
1442             }
1443             }
1444 0           return undef;
1445             }
1446              
1447             sub generate_switch_group {
1448 0     0 0   my $self = shift;
1449             # a number between 0x1 and 0xFFFFF
1450 0           my $group = 1 + int(rand(0xFFFFE));
1451 0           return $group;
1452             }
1453              
1454             sub format_group {
1455 0     0 0   my ($self, $id) = @_;
1456 0           return sprintf('0x%06X', $id);
1457             }
1458              
1459             sub receiver {
1460 0     0 0   my $self = shift;
1461 0 0         return ( $self->conf->{board} =~ /^ENER314_RT|RF69HW$/ ) ? 1 : 0;
1462             }
1463              
1464             sub get_command_usage {
1465 0     0 0   my($self, $command) = @_;
1466            
1467 0           my $usagetext = {
1468            
1469             #### GENERAL USAGE ################################
1470            
1471             unknown => q(
1472             usage : hipi-energenie [options]
1473            
1474             command :
1475             help Print this message
1476             version Print the version
1477             config Configure the board type ( ENER314_RT, ENER314 or RF69HW )
1478             group Manage groups for use with sockets
1479             pair Pair a socket or switch
1480             alias Rename or name a socket or switch
1481             switch Switch a socket or switch on or off
1482             join Configure a monitor or adaptor
1483             adapter Switch an adapter device on or off
1484             monitor Query a monitoring device for values
1485              
1486             For help on each command use:
1487             hipi-energenie -h
1488             ),
1489              
1490             #### CONFIG USAGE ################################
1491            
1492             config => q(
1493             usage : hipi-energenie config
1494            
1495             options :
1496              
1497             --help -h Display this message
1498              
1499             --list -l List the current config
1500              
1501             --board -b < ENER314 | ENER314_RT | RF69HW > Set the board type that
1502             you have connected to the Raspberry Pi.
1503             Default is 'ENER314_RT'
1504              
1505             --device -d < devicename > Set the SPI device used by the
1506             ENER314_RT or RF69HW board. Default is '/dev/spidev0.1'
1507            
1508             --reset -r < gpio > Specify the GPIO pin connected to
1509             the reset pin on the ENER314_RT or RF69HW board.
1510             Default is 25 ( RPI_PIN_22 )
1511            
1512             --json The command results will be output as a JSON
1513             string. This can be used when you want to parse
1514             the command output from external code.
1515              
1516             --pretty The command results will be output as formatted JSON
1517             with line breaks and indentation.
1518              
1519             ),
1520             #### GROUP USAGE ################################
1521              
1522             group => q(
1523             usage : hipi-energenie group
1524              
1525             description: Set up groups for use with ENER314_RT or RF69HW board to
1526             control multiple sets of simple switches or sockets.
1527              
1528             options :
1529              
1530             --help -h Display this message
1531              
1532             --list -l List the currently configured groups
1533              
1534             --create -c Create a new group. The system will create a new
1535             unused group id and associate the supplied name with it.
1536             e.g. hipi-energenie group -c newname
1537             Provide option --groupid to name an existing group.
1538            
1539             --delete -d Delete an existing group.
1540             e.g. hipi-energenie group -d 'my group 1'
1541            
1542             --rename -r Rename an existing group. Must be accompanied
1543             by option for --newname.
1544             e.g. hipi-energenie group -r oldname -n newname
1545            
1546             --newname -n The new name for a group. (used with --rename)
1547              
1548             --groupid The group id identifier that is used
1549             to control a group of four switches or sockets,
1550             or one 4 way gang extension. This is a number
1551             between 0x01 and 0xFFFFF. The parameter can be
1552             passed in decimal, hexadecimal or binary
1553             notation. It is parsed by the Perl 'oct'
1554             function.
1555              
1556             --json The command results will be output as a JSON
1557             string. This can be used when you want to parse
1558             the command output from external code.
1559              
1560             --pretty The command results will be output as formatted JSON
1561             with line breaks and indentation.
1562              
1563             ),
1564              
1565             #### PAIR USAGE ################################
1566            
1567             pair => q(
1568             usage : hipi-energenie pair
1569            
1570             description: pair with a simple socket or switch such as an ENER002 switch
1571             socket; an ENER010 4 way extension; a Mi|Home MIHO002 adapter.
1572             Set the adapter to pairing mode and run this command.
1573              
1574             options :
1575              
1576             --help -h Display this message
1577              
1578             --list -l list paired switches
1579            
1580             --groupname -g A groupname you have configured using the
1581             group command. If you are using the transmit
1582             only ENER314 board, this option is ignored.
1583              
1584             --switch -s < 1 | 2 | 3 | 4 > The number of the switch or socket
1585             you want to pair. Each groupname can control a
1586             maximum of 4 switches. If you are pairing an
1587             ENER010 4 gang extension, use '1'
1588            
1589             --name -n A name for the paired switch
1590             that you may use in the future to command the switch
1591             rather than specifying both group and switch
1592             number.
1593              
1594             --json The command results will be output as a JSON
1595             string. This can be used when you want to parse
1596             the command output from external code.
1597              
1598             --pretty The command results will be output as formatted JSON
1599             with line breaks and indentation.
1600            
1601             ( to rename a switch / group pair use the 'alias' command )
1602              
1603             ),
1604             #### SWITCH USAGE ################################
1605            
1606             switch => q(
1607             usage : hipi-energenie switch
1608            
1609             description: Turn a paired socket or switch on or off
1610              
1611             options :
1612             --help -h Display this message
1613              
1614             --list -l List the currently configured switches
1615              
1616             --name -n An alias for the groupname / switch pair
1617            
1618             --groupname -g If you don't provide a name you can specify
1619             groupname and switch number instead. This is the
1620             groupname you paired the switch with.
1621             If you are using the transmit only ENER314 board,
1622             this option is ignored.
1623              
1624             --switch -s < 0 | 1 | 2 | 3 | 4 > If you don't provide a name you can
1625             specify groupname and switch number instead. This is the
1626             number of the switch or socket you want to switch.
1627             Specifying 0 switches all members of the group.
1628            
1629             --on -1 Switch the socket on
1630              
1631             --off -0 Switch the socket off
1632            
1633             --all Switch all sockets in the group. The same as specifying
1634             --switch 0
1635              
1636             --json The command results will be output as a JSON
1637             string. This can be used when you want to parse
1638             the command output from external code.
1639              
1640             --pretty The command results will be output as formatted JSON
1641             with line breaks and indentation.
1642              
1643             ),
1644              
1645             #### ALAIS USAGE ################################
1646            
1647             alias => q(
1648             usage : hipi-energenie alias
1649            
1650             description: Give a name to a groupname / switch number pair
1651              
1652             options :
1653             --help -h Display this message
1654            
1655             --list -l List configured aliases
1656            
1657             --name -n The alias you want to use
1658            
1659             --groupname -g The group for the alias
1660              
1661             --switch -s < 1 | 2 | 3 | 4 > The switch number for the alias
1662            
1663             --json The command results will be output as a JSON
1664             string. This can be used when you want to parse
1665             the command output from external code.
1666              
1667             --pretty The command results will be output as formatted JSON
1668             with line breaks and indentation.
1669              
1670             ),
1671             #### JOIN USAGE ################################
1672            
1673             join => q(
1674             usage : hipi-energenie join
1675            
1676             description: Join a Mi|Home monitor or adapter. Run this command first and
1677             then switch your adapter or monitor to join mode.
1678              
1679             options :
1680            
1681             --help -h Display this message
1682            
1683             --list -l List the adapters and monitors already joined
1684              
1685             --name -n A name for the adapter or monitor. This
1686             is required.
1687              
1688             --rename -r You can rename an adapter. If --rename is specified
1689             then --rename contains the existing name and --name contains the
1690             new name.
1691            
1692             --delete -d Remove the named monitor or adapter from configuration
1693            
1694             --timeout -t Timeout in seconds to wait for a join request.
1695             The default is 60.
1696              
1697             --json The command results will be output as a JSON
1698             string. This can be used when you want to parse
1699             the command output from external code.
1700              
1701             --pretty The command results will be output as formatted JSON
1702             with line breaks and indentation.
1703              
1704             ),
1705             #### ADAPTER USAGE ################################
1706            
1707             adapter => q(
1708             usage : hipi-energenie adapter
1709            
1710             description: Switch a Mi|Home Adapter Plus on and off or query its switch state
1711              
1712             options :
1713            
1714             --help -h Display this message
1715            
1716             --list -l List all the adapters registered
1717              
1718             --name -n The name registered for the adapter. This
1719             is required.
1720              
1721             --query -q Get the switch state for the adapter
1722            
1723             --on -1 Switch the adapter on
1724            
1725             --off -0 Switch the adapter off
1726            
1727             --timeout -t Timeout in seconds to wait for a confirmation.
1728             The default is 60.
1729              
1730             --json The command results will be output as a JSON
1731             string. This can be used when you want to parse
1732             the command output from external code.
1733              
1734             --pretty The command results will be output as formatted JSON
1735             with line breaks and indentation.
1736              
1737             ),
1738             #### MONITOR USAGE ################################
1739            
1740             monitor => q(
1741             usage : hipi-energenie monitor
1742            
1743             description: Query status from a Mi|Home adapter or monitor
1744              
1745             options :
1746            
1747             --help -h Display this message
1748            
1749             --list -l List all the monitors registered
1750              
1751             --name -n The name registered for the montitor. This
1752             is required.
1753            
1754             --timeout -t Timeout in seconds to wait for a response.
1755             The default is 60.
1756              
1757             --json The command results will be output as a JSON
1758             string. This can be used when you want to parse
1759             the command output from external code.
1760              
1761             --pretty The command results will be output as formatted JSON
1762             with line breaks and indentation.
1763              
1764             ),
1765             };
1766            
1767            
1768 0 0         if(exists($usagetext->{$command})) {
1769 0           return $usagetext->{$command};
1770             } else {
1771 0           return $usagetext->{unknown};
1772             }
1773             }
1774              
1775             1;
1776              
1777             __END__