File Coverage

blib/lib/Device/RadioThermostat.pm
Criterion Covered Total %
statement 26 118 22.0
branch 0 30 0.0
condition 0 6 0.0
subroutine 9 33 27.2
pod 22 22 100.0
total 57 209 27.2


line stmt bran cond sub pod time code
1             package Device::RadioThermostat;
2              
3 1     1   13112 use strict;
  1         2  
  1         22  
4 1     1   3 use warnings;
  1         1  
  1         20  
5              
6 1     1   18 use 5.008_001;
  1         4  
7             our $VERSION = '0.04';
8              
9 1     1   4 use Carp;
  1         1  
  1         64  
10 1     1   579 use JSON;
  1         9653  
  1         4  
11 1     1   630 use Socket 'inet_aton';
  1         3006  
  1         132  
12 1     1   460 use Time::HiRes 'usleep';
  1         888  
  1         2  
13 1     1   684 use LWP::UserAgent;
  1         28723  
  1         28  
14 1     1   440 use IO::Socket::INET;
  1         12293  
  1         5  
15              
16             sub new {
17 0     0 1   my ( $class, %args ) = @_;
18             my $self = {
19             address => $args{address},
20 0           ua => LWP::UserAgent->new() };
21 0 0         croak 'Must pass address to new.' unless $self->{address};
22              
23 0           return bless $self, $class;
24             }
25              
26             sub find_all {
27 0     0 1   my ( $class, $low, $high ) = @_;
28 0 0 0       croak 'Must pass two addresses to find_all.' unless $low && $high;
29              
30 0           my $lowint = unpack( "N", inet_aton($low) );
31 0           my $highint = unpack( "N", inet_aton($high) );
32              
33 0   0       my $s = IO::Socket::INET->new(Proto => 'udp') || croak @$;
34              
35 0           for ( 0 .. 4 ) { # retry 5 times
36 0           for ( my $addr = $lowint; $addr <= $highint; $addr++ ) {
37 0           my $hissockaddr = sockaddr_in( 1900, pack( "N", $addr ) );
38 0           $s->send(
39             "TYPE: WM-DISCOVER\r\n"
40             . "VERSION: 1.0\r\n\r\n"
41             . "services: com.marvell.wm.system*\r\n\r\n",
42             0, $hissockaddr
43             );
44 0           usleep 10000;
45             }
46              
47 0           my $rin = '';
48 0           vec($rin, $s->fileno, 1) = 1;
49 0           my ($rout, %result);
50 0           while (select($rout = $rin, undef, undef, 1)) {
51 0           my $response = '';
52 0           my $hispaddr = $s->recv( $response, 1024, 0 );
53 0           my ( $port, $hisiaddr ) = sockaddr_in($hispaddr);
54 0           my ($hisaddr) = $response =~ m!location:\s*http://([0-9.]+)/sys!i;
55 0 0         next unless $hisaddr;
56              
57 0           my $tstat = Device::RadioThermostat->new(
58             address => 'http://' . $hisaddr );
59 0           my $uuid = $tstat->get_uuid();
60 0 0         next unless $uuid;
61              
62 0           $result{$uuid} = $tstat;
63             }
64              
65 0 0         return \%result if %result;
66             }
67              
68 0           return;
69             }
70              
71             sub tstat {
72 0     0 1   my $self = shift;
73 0           return $self->_ua_get('/tstat');
74             }
75              
76             sub sys {
77 0     0 1   my $self = shift;
78 0           return $self->_ua_get('/sys');
79             }
80              
81             sub model {
82 0     0 1   my $self = shift;
83 0           return $self->_ua_get('/tstat/model');
84             }
85              
86             sub get_uuid {
87 0     0 1   my $self = shift;
88 0           return $self->sys()->{uuid};
89             }
90              
91             sub set_mode {
92 0     0 1   my ( $self, $mode ) = @_;
93 0           return $self->_ua_post( '/tstat', { tmode => int($mode) } );
94             }
95              
96             sub get_target {
97 0     0 1   my ($self) = @_;
98 0           my $mode = $self->tstat->{tmode};
99 0 0         return if $mode == 0;
100 0           my $targets = $self->get_targets();
101 0 0         if ( $mode == 1 ) {
    0          
102 0           return $targets->{t_heat};
103             }
104             elsif ( $mode == 2 ) {
105 0           return $targets->{t_cool};
106             }
107             else {
108 0           return [ $targets->{t_cool}, $targets->{t_heat} ];
109             }
110             }
111              
112             sub get_targets {
113 0     0 1   my ($self) = @_;
114 0           return $self->_ua_get('/tstat/ttemp');
115             }
116              
117             sub get_humidity {
118 0     0 1   my ($self) = @_;
119 0           return $self->_ua_get('/tstat/humidity');
120             }
121              
122             sub temp_heat {
123 0     0 1   my ( $self, $temp ) = @_;
124 0           return $self->_ua_post( '/tstat', { t_heat => 0 + $temp } );
125             }
126              
127             sub temp_cool {
128 0     0 1   my ( $self, $temp ) = @_;
129 0           return $self->_ua_post( '/tstat', { t_cool => 0 + $temp } );
130             }
131              
132             sub remote_temp {
133 0     0 1   my ($self) = @_;
134 0           return $self->_ua_get('/tstat/remote_temp');
135             }
136              
137             sub disable_remote_temp {
138 0     0 1   my ($self) = @_;
139 0           return $self->_ua_post( '/tstat/remote_temp', { rem_mode => 0 } );
140             }
141              
142             sub set_remote_temp {
143 0     0 1   my ( $self, $temp ) = @_;
144 0           return $self->_ua_post( '/tstat/remote_temp', { rem_temp => 0 + sprintf("%d", $temp) } );
145             }
146              
147             sub lock {
148 0     0 1   my ($self, $mode) = @_;
149 0 0         if ($mode) {
150 0 0         return unless $self->_ua_post( '/tstat/lock', { lock_mode => int($mode) } );
151             }
152 0           return $self->_ua_get('/tstat/lock');
153             }
154              
155             sub user_message {
156 0     0 1   my ( $self, $line, $message ) = @_;
157 0           return $self->_ua_post( '/tstat/uma', { line => int($line), message => $message } );
158             }
159              
160             sub price_message {
161 0     0 1   my ( $self, $line, $message ) = @_;
162 0           return $self->_ua_post( '/tstat/pma', { line => int($line), message => $message } );
163             }
164              
165             sub clear_user_message {
166 0     0 1   my ($self) = @_;
167 0           return $self->_ua_post( '/tstat/uma', { mode => 0 } );
168             }
169              
170             sub clear_price_message {
171 0     0 1   my ($self) = @_;
172 0           return $self->_ua_post( '/tstat/pma', { mode => 0 } );
173             }
174              
175             sub clear_message {
176 0     0 1   my ($self) = @_;
177 0           return $self->clear_price_message();
178             }
179              
180             sub datalog {
181 0     0 1   my ($self) = @_;
182 0           return $self->_ua_get('/tstat/datalog');
183             }
184              
185             sub _ua_post {
186 0     0     my ( $self, $path, $data ) = @_;
187             my $response
188 0           = $self->{ua}->post( $self->{address} . $path, content => encode_json $data );
189 0 0         if ( $response->is_success ) {
190 0           my $result = decode_json $response->decoded_content();
191              
192             # return $result;
193 0 0         return exists( $result->{success} ) ? 1 : 0;
194             }
195             else {
196 0           my ($code, $err) = ($response->code, $response->message);
197 0 0         carp $code ? "$code response: $err" : "Connection error: $err";
198 0           return;
199             }
200             }
201              
202             sub _ua_get {
203 0     0     my ( $self, $path ) = @_;
204 0           my $response = $self->{ua}->get( $self->{address} . $path );
205 0 0         if ( $response->is_success ) {
206 0           return decode_json $response->decoded_content();
207             }
208             else {
209 0           my ($code, $err) = ($response->code, $response->message);
210 0 0         carp $code ? "$code response: $err" : "Connection error: $err";
211 0           return;
212             }
213             }
214              
215             1;
216             __END__