File Coverage

blib/lib/Device/Nest.pm
Criterion Covered Total %
statement 34 356 9.5
branch 1 116 0.8
condition 0 9 0.0
subroutine 12 53 22.6
pod 38 39 97.4
total 85 573 14.8


line stmt bran cond sub pod time code
1             package Device::Nest;
2              
3 1     1   15498 use warnings;
  1         2  
  1         30  
4 1     1   4 use strict;
  1         2  
  1         25  
5 1     1   17 use 5.006_001;
  1         10  
  1         125  
6              
7             require Exporter;
8              
9             our @ISA = qw(Exporter);
10              
11             # Items to export into callers namespace by default. Note: do not export
12             # names by default without a very good reason. Use EXPORT_OK instead.
13             # Do not simply export all your public functions/methods/constants.
14              
15             # This allows declaration use Device::Nest ':all';
16             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
17             # will save memory.
18              
19             our %EXPORT_TAGS = ( 'all' => [ qw( new
20             fetch_Auth_Token fetch_Thermostat_Designation fetch_Ambient_Temperature_C
21             fetch_Target_Temperature_C fetch_Target_Temperature_high_C fetch_Target_Temperature_low_C
22             fetch_Away_Temperature_low_C fetch_Ambient_Temperature_F fetch_Away_Temperature_low_F
23             fetch_Away_Temperature_high_F fetch_Target_Temperature_low_F fetch_Target_Temperature_F
24             fetch_Target_Temperature_high_F fetch_Temperature_Scale fetch_Locale fetch_Name
25             fetch_Long_Name fetch_HVAC_Mode fetch_SW_Version fetch_Away_State
26             fetch_Country_Code fetch_Can_Cool fetch_Can_Heat
27             fetch_Has_Fan fetch_Is_Online fetch_Is_Using_Emergency_Heat
28             set_Target_Temperature_C set_Target_Temperature_F set_Target_Temperature_high_C
29             set_Target_Temperature_low_C set_Target_Temperature_high_F set_Target_Temperature_low_F
30             set_Away_State
31             ) ] );
32              
33             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
34             our @EXPORT = qw( $EXPORT_TAGS{'all'});
35              
36             BEGIN
37             {
38 1 50   1   2787 if ($^O eq "MSWin32"){
39 1     1   621 use LWP::UserAgent;
  1         38516  
  1         29  
40 1     1   591 use JSON qw(decode_json encode_json);
  1         9978  
  1         4  
41 1     1   1800 use Time::HiRes qw/gettimeofday/;
  1         1114  
  1         3  
42 1     1   618 use Data::Dumper;
  1         5103  
  1         66  
43             } else {
44 1     1   6 use LWP::UserAgent;
  1         1  
  1         20  
45 1     1   4 use JSON qw(decode_json encode_json);
  1         1  
  1         5  
46 1     1   102 use Time::HiRes qw/gettimeofday/;
  1         1  
  1         6  
47 1     1   69 use Data::Dumper;
  1         2  
  1         30  
48             }
49             }
50              
51              
52             =head1 NAME
53              
54             Device::Nest - Methods for wrapping the Nest API calls so that they are
55             accessible via Perl
56              
57             =head1 VERSION
58              
59             Version 0.09
60              
61             =cut
62              
63             our $VERSION = '0.09';
64              
65             #*****************************************************************
66              
67             =head1 SYNOPSIS
68              
69             This module provides a Perl interface to a Nest Thermostat via the following
70             methods:
71             - new
72             - connect
73             - fetch_Ambient_Temperature
74             - fetch_Designation
75              
76             In order to use this module, you will require a Nest thermostat installed in
77             your home as well. You will also need your ClientID and ClientSecret provided
78             by Nest when you register as a developper at https://developer.nest.com.
79             You will aos need an access code which can be obtained at
80             https://home.nest.com/login/oauth2?client_id=CLIENT_ID&state=FOO
81             Your authorization code will be obtained and stored in this module when you
82             call it.
83              
84             The module is written entirely in Perl and has been developped on Raspbian Linux.
85              
86             =head1 SAMPLE CODE
87              
88             use Device::Nest;
89              
90             $my_Nest = Device::Nest->new($ClientID,$ClientSecret,$code,$phrase,$debug);
91              
92             $my_Nest->connect();
93            
94             undef $my_Nest;
95              
96              
97             You need to get an authorization code by going to https://home.nest.com/login/oauth2?client_id=CLIENT_ID&state=FOO
98             and specifying your client ID in the URL along with a random string for state
99            
100             Use this code, along with your ClientID and ClientSecret to get an authorization code
101             by using the 'connect' function below.
102            
103             From now on, all you need is your auth_token
104            
105              
106              
107             =head2 EXPORT
108              
109             All by default.
110              
111              
112             =head1 SUBROUTINES/METHODS
113              
114             =head2 new - the constructor for a Nest object
115              
116             Creates a new instance which will be able to fetch data from a unique Nest
117             sensor.
118              
119             my $Nest = Device::Nest->new($ClientID, $ClientSecret, $phrase, $debug);
120              
121             This method accepts the following parameters:
122             - $ClientID : Client ID for the account - Required
123             - $ClientSecret : Secret key for the account - Required
124             - $auth_token : authentication token to access the account - Required
125             - $debug : enable or disable debug messages (disabled by default - Optional)
126              
127             Returns a Nest object if successful.
128             Returns 0 on failure
129             =cut
130             sub new {
131 0     0 1   my $class = shift;
132 0           my $self;
133            
134 0           $self->{'ua'} = LWP::UserAgent->new(max_redirect=>3,requests_redirectable=>['GET','HEAD','PUT']);
135 0           $self->{'ClientID'} = shift;
136 0           $self->{'ClientSecret'} = shift;
137 0           $self->{'PIN_code'} = shift;
138 0           $self->{'auth_token'} = shift;
139 0           $self->{'debug'} = shift;
140 0           $self->{'last_code'} = '';
141 0           $self->{'last_reason'} = '';
142 0           $self->{'device_url'} = "https://developer-api.nest.com/devices.json?auth=".$self->{'auth_token'};
143 0           $self->{'last_exec_time'} = 0;
144            
145 0 0         if (!defined $self->{'debug'}) {
146 0           $self->{'debug'} = 0;
147             }
148            
149 0 0 0       if ((!defined $self->{'ClientID'}) || (!defined $self->{'ClientSecret'}) || (!defined $self->{'PIN_code'}) || (!defined $self->{'auth_token'})) {
      0        
      0        
150 0           print "Nest->new(): ClientID, ClientSecret, PIN_code and auth_token are REQUIRED parameters.\n";
151 0           return 0;
152             }
153            
154 0           bless $self, $class;
155            
156 0           return $self;
157             }
158              
159              
160             #*****************************************************************
161              
162             =head2 fetch_Auth_Token - generates and displays the auth_token
163              
164             This function will display the authenticaton token for the PIN code
165             provided. This can only be done once per PIN code. Pleas make sure
166             to note and store your auth code since it will be the only thing requiired
167             for all other API calls.
168              
169             $Nest->fetch_Auth_Token();
170            
171             This method accepts no parameters
172            
173             Returns 1 on success and prints auth_token
174             Returns 0 on failure
175             =cut
176             sub fetch_Auth_Token {
177 0     0 1   my $self = shift;
178 0           my $auth_token = '';
179            
180             # Submit request for authentiaction token.
181 0           my $response = $self->{'ua'}->post('https://api.home.nest.com/oauth2/access_token',
182             { code => $self->{'PIN_code'},
183             grant_type => 'authorization_code',
184             client_id => $self->{'ClientID'},
185             client_secret => $self->{'ClientSecret'},
186             }
187             );
188            
189 0           $self->{'last_code'} = $response->code;
190            
191 0 0         if($response->is_success) {
192 0 0         if ($response->content =~ /\"access_token\":\"(.*?)\"/) {
193 0           print "Found authentication code. Please use it when calling functions\n";
194 0           print "Authentication code: $1\n";
195 0           return 1;
196             } else {
197 0           print "No authentication token found.\n";
198 0           print "Make sure your PIN is correct.\n";
199 0           print "You may need to request a new PIN\n";
200 0           return 0;
201             }
202             } else {
203 0           print "No authentication token found.\n";
204 0           print "Make sure your PIN is correct.\n";
205 0           print "You may need to request a new PIN\n";
206 0           return 0;
207             }
208             }
209              
210              
211             #*****************************************************************
212              
213             =head2 fetch_Thermostat_Designation - fetch the designation for your thermostat
214              
215             Retrieves the code designating your thermostat and stores it in $self
216              
217             $Nest->fetch_Thermostat_Designation();
218              
219             This method accepts no parameters
220            
221             Returns 1 on success
222             Returns 0 on failure
223            
224             =cut
225             sub fetch_Thermostat_Designation {
226 0     0 1   my $self = shift;
227 0           my $response = $self->{'ua'}->get($self->{'device_url'});
228            
229 0           $self->{'last_code'} = $response->code;
230            
231 0 0         if ($response->is_success) {
232 0           my $decoded_response = decode_json($response->content);
233 0           my $designation = ($decoded_response->{'thermostats'});
234 0           my @designation2 = keys(%$designation);
235 0           $self->{'thermostat'} = $designation2[0];
236 0           $self->{'thermostat_url'} = "https://developer-api.nest.com/devices/thermostats/".$self->{'thermostat'};
237 0 0         print "Thermostat designation: ".$self->{'thermostat'}."\n" if ($self->{'debug'});
238              
239 0           my $response = $self->{'ua'}->get("https://developer-api.nest.com/structures?auth=".$self->{'auth_token'});
240              
241 0           $self->{'last_code'} = $response->code;
242            
243 0 0         if ($response->is_success) {
244 0           my $decoded_response = decode_json($response->content);
245 0           my @designation = keys(%$decoded_response);
246 0           $self->{'structure'} = $designation[0];
247 0           $self->{'struct_url'} = "https://developer-api.nest.com/structures/".$self->{'structure'}."?auth=".$self->{'auth_token'};
248 0 0         print "Structure Designation: ".$self->{'structure'}."\n" if ($self->{'debug'});
249 0           return 1;
250             } else {
251 0           print "Nest->fetch_Thermostat_Designation(): Response from server for structure URL is not valid\n";
252 0           print " \"".$response->content."\"\n\n";
253 0           return 0;
254             }
255             } else {
256 0           print "Nest->fetch_Thermostat_Designation(): Failed with return code ".$self->get_last_code()."\n";
257 0           return 0;
258             }
259             }
260              
261              
262             #*****************************************************************
263              
264             =head2 fetch_Ambient_Temperature_C - Fetch the ambient temperature reported by Nest in Celcius
265              
266             Retrieves the ambient temperature reported by the Nest in Celcius
267              
268             $Nest->fetch_Ambient_Temperature_C();
269              
270             This method accepts no parameters
271            
272             Returns the ambient temperature in Celcius
273             Returns 0 on failure
274            
275             =cut
276             sub fetch_Ambient_Temperature_C {
277 0     0 1   my $self = shift;
278            
279 0 0         if (!defined $self->{'thermostat'}) {
280 0           print "Nest->fetch_Ambient_Temperature_C(): No thermostat designation found\n";
281 0           return 0;
282             }
283              
284 0           return $self->__process_get($self->{'device_url'},'ambient_temperature_c');
285             }
286              
287             #*****************************************************************
288              
289             =head2 fetch_Target_Temperature_C - Fetch the target temperature reported by Nest in Celcius
290              
291             Retrieves the target temperature reported by the Nest in Celcius
292              
293             $Nest->fetch_Target_Temperature_C();
294              
295             This method accepts no parameters
296            
297             Returns the target temperature in Celcius
298             Returns 0 on failure
299            
300             =cut
301             sub fetch_Target_Temperature_C {
302 0     0 1   my $self = shift;
303            
304 0 0         if (!defined $self->{'thermostat'}) {
305 0           print "Nest->fetch_Target_Temperature_C(): No thermostat designation found\n";
306 0           return 0;
307             }
308            
309 0           return $self->__process_get($self->{'device_url'},'target_temperature_c');
310             }
311              
312             #*****************************************************************
313              
314             =head2 fetch_Target_Temperature_high_C - Fetch the higher target temperature reported by Nest in Celcius
315              
316             Retrieves the high target temperature reported by the Nest in Celcius
317              
318             $Nest->fetch_Target_Temperature_high_C();
319              
320             This method accepts no parameters
321            
322             Returns the high target temperature in Celcius
323             Returns 0 on failure
324            
325             =cut
326             sub fetch_Target_Temperature_high_C {
327 0     0 1   my $self = shift;
328            
329 0 0         if (!defined $self->{'thermostat'}) {
330 0           print "Nest->fetch_Target_Temperature_high_C(): No thermostat designation found\n";
331 0           return 0;
332             }
333            
334 0           return $self->__process_get($self->{'device_url'},'target_temperature_high_c');
335             }
336              
337             #*****************************************************************
338              
339             =head2 fetch_Target_Temperature_low_C - Fetch the lower target temperature reported by Nest in Celcius
340              
341             Retrieves the lower target temperature reported by the Nest in Celcius
342              
343             $Nest->fetch_Target_Temperature_low_C();
344              
345             This method accepts no parameters
346            
347             Returns the lower target temperature in Celcius
348             Returns 0 on failure
349            
350             =cut
351             sub fetch_Target_Temperature_low_C {
352 0     0 1   my $self = shift;
353            
354 0 0         if (!defined $self->{'thermostat'}) {
355 0           print "Nest->fetch_Target_Temperature_low_C(): No thermostat designation found\n";
356 0           return 0;
357             }
358            
359 0           return $self->__process_get($self->{'device_url'},'target_temperature_low_c');
360             }
361              
362             #*****************************************************************
363              
364             =head2 fetch_Away_Temperature_low_C - Fetch the lower away temperature reported by Nest in Celcius
365              
366             Retrieves the lower away temperature reported by the Nest in Celcius
367              
368             $Nest->fetch_Away_Temperature_low_C();
369              
370             This method accepts no parameters
371            
372             Returns the lower away temperature in Celcius
373             Returns 0 on failure
374            
375             =cut
376             sub fetch_Away_Temperature_low_C {
377 0     0 1   my $self = shift;
378            
379 0 0         if (!defined $self->{'thermostat'}) {
380 0           print "Nest->fetch_Away_Temperature_low_C(): No thermostat designation found\n";
381 0           return 0;
382             }
383            
384 0           return $self->__process_get($self->{'device_url'},'away_temperature_low_c');
385             }
386              
387             #*****************************************************************
388              
389             =head2 fetch_Away_Temperature_high_C - Fetch the high away temperature reported by Nest in Celcius
390              
391             Retrieves the high away temperature reported by the Nest in Celcius
392              
393             $Nest->fetch_Away_Temperature_high_C();
394              
395             This method accepts no parameters
396            
397             Returns the high away temperature in Celcius
398             Returns 0 on failure
399            
400             =cut
401             sub fetch_Away_Temperature_high_C {
402 0     0 1   my $self = shift;
403            
404 0 0         if (!defined $self->{'thermostat'}) {
405 0           print "Nest->fetch_Away_Temperature_high_C(): No thermostat designation found\n";
406 0           return 0;
407             }
408            
409 0           return $self->__process_get($self->{'device_url'},'away_temperature_high_c');
410             }
411              
412             #*****************************************************************
413              
414             =head2 fetch_Ambient_Temperature_F - Fetch the ambient temperature reported by Nest in Fahrenheit
415              
416             Retrieves the ambient temperature reported by the Nest in Fahrenheit
417              
418             $Nest->fetch_Ambient_Temperature_F();
419              
420             This method accepts no parameters
421            
422             Returns the ambient temperature in Fahrenheit
423             Returns 0 on failure
424            
425             =cut
426             sub fetch_Ambient_Temperature_F {
427 0     0 1   my $self = shift;
428            
429 0 0         if (!defined $self->{'thermostat'}) {
430 0           print "Nest->fetch_Ambient_Temperature_F(): No thermostat designation found\n";
431 0           return 0;
432             }
433            
434 0           return $self->__process_get($self->{'device_url'},'ambient_temperature_f');
435             }
436              
437             #*****************************************************************
438              
439             =head2 fetch_Away_Temperature_low_F - Fetch the lower away temperature reported by Nest in Fahrenheit
440              
441             Retrieves the lower away temperature reported by the Nest in Fahrenheit
442              
443             $Nest->fetch_Away_Temperature_low_F();
444              
445             This method accepts no parameters
446            
447             Returns the lower away temperature in Fahrenheit
448             Returns 0 on failure
449            
450             =cut
451             sub fetch_Away_Temperature_low_F {
452 0     0 1   my $self = shift;
453            
454 0 0         if (!defined $self->{'thermostat'}) {
455 0           print "Nest->fetch_Away_Temperature_low_F(): No thermostat designation found\n";
456 0           return 0;
457             }
458            
459 0           return $self->__process_get($self->{'device_url'},'away_temperature_low_f');
460             }
461              
462             #*****************************************************************
463              
464             =head2 fetch_Away_Temperature_high_F - Fetch the higher away temperature reported by Nest in Fahrenheit
465              
466             Retrieves the higher away temperature reported by the Nest in Fahrenheit
467              
468             $Nest->fetch_Away_Temperature_high_F();
469              
470             This method accepts no parameters
471            
472             Returns the higher away temperature in Fahrenheit
473             Returns 0 on failure
474            
475             =cut
476             sub fetch_Away_Temperature_high_F {
477 0     0 1   my $self = shift;
478            
479 0 0         if (!defined $self->{'thermostat'}) {
480 0           print "Nest->fetch_Away_Temperature_high_F(): No thermostat designation found\n";
481 0           return 0;
482             }
483            
484 0           return $self->__process_get($self->{'device_url'},'away_temperature_high_f');
485             }
486              
487             #*****************************************************************
488              
489             =head2 fetch_Target_Temperature_low_F - Fetch the lower target temperature reported by Nest in Fahrenheit
490              
491             Retrieves the lower target temperature reported by the Nest in Fahrenheit
492              
493             $Nest->fetch_Target_Temperature_low_F();
494              
495             This method accepts no parameters
496            
497             Returns the lower target temperature in Fahrenheit
498             Returns 0 on failure
499            
500             =cut
501             sub fetch_Target_Temperature_low_F {
502 0     0 1   my $self = shift;
503            
504 0 0         if (!defined $self->{'thermostat'}) {
505 0           print "Nest->fetch_Target_Temperature_low_F(): No thermostat designation found\n";
506 0           return 0;
507             }
508            
509 0           return $self->__process_get($self->{'device_url'},'target_temperature_low_f');
510             }
511              
512             #*****************************************************************
513              
514             =head2 fetch_Target_Temperature_F - Fetch the target temperature reported by Nest in Fahrenheit
515              
516             Retrieves the target temperature reported by the Nest in Fahrenheit
517              
518             $Nest->fetch_Target_Temperature_F();
519              
520             This method accepts no parameters
521            
522             Returns the target temperature in Fahrenheit
523             Returns 0 on failure
524            
525             =cut
526             sub fetch_Target_Temperature_F {
527 0     0 1   my $self = shift;
528            
529 0 0         if (!defined $self->{'thermostat'}) {
530 0           print "Nest->fetch_Target_Temperature_F(): No thermostat designation found\n";
531 0           return 0;
532             }
533            
534 0           return $self->__process_get($self->{'device_url'},'target_temperature_f');
535             }
536              
537             #*****************************************************************
538              
539             =head2 fetch_Target_Temperature_high_F - Fetch the higher target temperature reported by Nest in Fahrenheit
540              
541             Retrieves the higher target temperature reported by the Nest in Fahrenheit
542              
543             $Nest->fetch_Target_Temperature_high_F();
544              
545             This method accepts no parameters
546            
547             Returns the target temperature in Fahrenheit
548             Returns 0 on failure
549            
550             =cut
551             sub fetch_Target_Temperature_high_F {
552 0     0 1   my $self = shift;
553            
554 0 0         if (!defined $self->{'thermostat'}) {
555 0           print "Nest->fetch_Target_Temperature_high_F(): No thermostat designation found\n";
556 0           return 0;
557             }
558            
559 0           return $self->__process_get($self->{'device_url'},'target_temperature_high_f');
560             }
561              
562             #*****************************************************************
563              
564             =head2 fetch_Temperature_Scale - Fetch the temperature scale reported by Nest
565              
566             Retrieves the temperature scale reported by the Nest as either F (Fahrenheit)
567             or C (Celcius)
568              
569             $Nest->fetch_Temperature_Scale();
570              
571             This method accepts no parameters
572            
573             Returns the temperature scale
574             Returns 0 on failure
575            
576             =cut
577             sub fetch_Temperature_Scale {
578 0     0 1   my $self = shift;
579            
580 0 0         if (!defined $self->{'thermostat'}) {
581 0           print "Nest->fetch_Temperature_Scale(): No thermostat designation found\n";
582 0           return 0;
583             }
584            
585 0           return $self->__process_get($self->{'device_url'},'temperature_scale');
586             }
587              
588              
589             ##*****************************************************************
590             #
591             #=head2 fetch_Humidity - Fetch the humidity reported by Nest
592             #
593             # Retrieves the humidity reported as a percentage by the Nest
594             #
595             # $Nest->fetch_Humidity();
596             #
597             # This method accepts no parameters
598             #
599             # Returns the humidity
600             # Returns 0 on failure
601             #
602             #=cut
603             #sub fetch_Humidity {
604             # my $self = shift;
605             #
606             # if (!defined $self->{'thermostat'}) {
607             # print "Nest->fetch_Humidity(): No thermostat designation found\n";
608             # return 0;
609             # }
610             #
611             # return $self->__process_get($self->{'device_url'},'humidity');
612             #}
613              
614              
615             #*****************************************************************
616              
617             =head2 fetch_Is_Using_Emergency_Heat - Fetches true or false
618              
619             Retrieves the state of the Nest indicating whether it is using Emergency Heat
620              
621             $Nest->fetch_Is_Using_Emergency_Heat();
622              
623             This method accepts no parameters
624            
625             Returns a Boolean
626            
627             =cut
628             sub fetch_Is_Using_Emergency_Heat {
629 0     0 1   my $self = shift;
630            
631 0 0         if (!defined $self->{'thermostat'}) {
632 0           print "Nest->fetch_Is_Using_Emergency_Heat(): No thermostat designation found\n";
633 0           return 0;
634             }
635            
636 0 0         if ($self->__process_get($self->{'device_url'},'is_using_emergency_heat')) {
637 0           return 1;
638             } else {
639 0           return 0;
640             }
641             }
642              
643              
644             #*****************************************************************
645              
646             =head2 fetch_Is_Online - Fetches true or false
647              
648             Retrieves the state of the Nest indicating whether it is online
649              
650             $Nest->fetch_Is_Online();
651              
652             This method accepts no parameters
653            
654             Returns a Boolean
655            
656             =cut
657             sub fetch_Is_Online {
658 0     0 1   my $self = shift;
659            
660 0 0         if (!defined $self->{'thermostat'}) {
661 0           print "Nest->fetch_Is_Online(): No thermostat designation found\n";
662 0           return 0;
663             }
664            
665 0 0         if ($self->__process_get($self->{'device_url'},'is_online')) {
666 0           return 1;
667             } else {
668 0           return 0;
669             }
670             }
671              
672              
673             #*****************************************************************
674              
675             =head2 fetch_Can_Heat - Fetches true or false
676              
677             Retrieves the state of the Nest indicating whether it can heat
678              
679             $Nest->fetch_Can_Heat();
680              
681             This method accepts no parameters
682            
683             Returns a Boolean
684            
685             =cut
686             sub fetch_Can_Heat {
687 0     0 1   my $self = shift;
688            
689 0 0         if (!defined $self->{'thermostat'}) {
690 0           print "Nest->fetch_Can_Heat(): No thermostat designation found\n";
691 0           return 0;
692             }
693            
694 0 0         if ($self->__process_get($self->{'device_url'},'can_heat')) {
695 0           return 1;
696             } else {
697 0           return 0;
698             }
699             }
700              
701              
702             #*****************************************************************
703              
704             =head2 fetch_Can_Cool - Fetches true or false
705              
706             Retrieves the state of the Nest indicating whether it can cool
707              
708             $Nest->fetch_Can_Cool();
709              
710             This method accepts no parameters
711            
712             Returns a Boolean
713            
714             =cut
715             sub fetch_Can_Cool {
716 0     0 1   my $self = shift;
717            
718 0 0         if (!defined $self->{'thermostat'}) {
719 0           print "Nest->fetch_Can_Cool(): No thermostat designation found\n";
720 0           return 0;
721             }
722            
723 0 0         if ($self->__process_get($self->{'device_url'},'can_cool')) {
724 0           return 1;
725             } else {
726 0           return 0;
727             }
728             }
729              
730              
731             #*****************************************************************
732              
733             =head2 fetch_Has_Fan - Fetches true or false
734              
735             Retrieves the state of the Nest indicating whether it has a fan
736              
737             $Nest->fetch_Has_Fan();
738              
739             This method accepts no parameters
740            
741             Returns a Boolean
742            
743             =cut
744             sub fetch_Has_Fan {
745 0     0 1   my $self = shift;
746            
747 0 0         if (!defined $self->{'thermostat'}) {
748 0           print "Nest->fetch_Has_Fan(): No thermostat designation found\n";
749 0           return 0;
750             }
751            
752 0 0         if ($self->__process_get($self->{'device_url'},'has_fan')) {
753 0           return 1;
754             } else {
755 0           return 0;
756             }
757             }
758              
759              
760             #*****************************************************************
761              
762             =head2 fetch_Away_State - Fetch the away state reported by Nest
763              
764             Retrieves the away state reported by the Nest
765              
766             $Nest->fetch_Away_State();
767              
768             This method accepts no parameters
769            
770             Returns the away state
771             Returns 0 on failure
772            
773             =cut
774             sub fetch_Away_State {
775 0     0 1   my $self = shift;
776            
777 0 0         if (!defined $self->{'thermostat'}) {
778 0           print "Nest->fetch_Away_State(): No thermostat designation found\n";
779 0           return 0;
780             }
781            
782 0           my $response = $self->{'ua'}->get($self->{'struct_url'});
783              
784 0           $self->{'last_code'} = $response->code;
785            
786 0 0         if ($response->is_success) {
787 0           my $decoded_response = decode_json($response->content);
788 0           return $decoded_response->{'away'};
789             } else {
790 0           print "Nest->fetch_Away_State(): Failed with return code ".$self->get_last_code()."\n";
791 0           return 0;
792             }
793             }
794              
795              
796             #*****************************************************************
797              
798             =head2 fetch_Country_Code - Fetch the country code reported by Nest
799              
800             Retrieves the country code reported by the Nest
801              
802             $Nest->fetch_Country_Code();
803              
804             This method accepts no parameters
805            
806             Returns the away state
807             Returns 0 on failure
808            
809             =cut
810             sub fetch_Country_Code {
811 0     0 1   my $self = shift;
812            
813 0 0         if (!defined $self->{'thermostat'}) {
814 0           print "Nest->fetch_Country_Code(): No thermostat designation found\n";
815 0           return 0;
816             }
817            
818 0           my $response = $self->{'ua'}->get($self->{'struct_url'});
819            
820 0           $self->{'last_code'} = $response->code;
821            
822 0 0         if ($response->is_success) {
823 0           my $decoded_response = decode_json($response->content);
824 0           return $decoded_response->{$self->{'structure'}}->{'country_code'};
825             } else {
826 0           print "Nest->fetch_Country_Code(): Failed with return code ".$self->get_last_code()."\n";
827 0           return 0;
828             }
829             }
830              
831              
832             #*****************************************************************
833              
834             =head2 fetch_Locale - Fetch the locale reported by Nest
835              
836             Retrieves the locale reported by the Nest
837              
838             $Nest->fetch_Locale();
839              
840             This method accepts no parameters
841            
842             Returns the locale
843             Returns 0 on failure
844            
845             =cut
846             sub fetch_Locale {
847 0     0 1   my $self = shift;
848            
849 0 0         if (!defined $self->{'thermostat'}) {
850 0           print "Nest->fetch_Locale(): No thermostat designation found\n";
851 0           return 0;
852             }
853            
854 0           return $self->__process_get($self->{'device_url'},'locale');
855             }
856              
857             #*****************************************************************
858              
859             =head2 fetch_Name - Fetch the name reported by Nest
860              
861             Retrieves the name reported by the Nest
862              
863             $Nest->fetch_Name();
864              
865             This method accepts no parameters
866            
867             Returns the name of the thermostat
868             Returns 0 on failure
869            
870             =cut
871             sub fetch_Name {
872 0     0 1   my $self = shift;
873            
874 0 0         if (!defined $self->{'thermostat'}) {
875 0           print "Nest->fetch_Name(): No thermostat designation found\n";
876 0           return 0;
877             }
878            
879 0           return $self->__process_get($self->{'device_url'},'name');
880             }
881              
882              
883             #*****************************************************************
884              
885             =head2 fetch_Long_Name - Fetch the long name reported by Nest
886              
887             Retrieves the long name reported by the Nest
888              
889             $Nest->fetch_Long_Name();
890              
891             This method accepts no parameters
892            
893             Returns the long name of the thermostat
894             Returns 0 on failure
895            
896             =cut
897             sub fetch_Long_Name {
898 0     0 1   my $self = shift;
899            
900 0 0         if (!defined $self->{'thermostat'}) {
901 0           print "Nest->fetch_Long_Name(): No thermostat designation found\n";
902 0           return 0;
903             }
904            
905 0           return $self->__process_get($self->{'device_url'},'name_long');
906             }
907              
908              
909             #*****************************************************************
910              
911             =head2 fetch_HVAC_Mode - Fetch the HVAC Mode reported by Nest
912              
913             Retrieves the HVAC Mode reported by the Nest as either 'heat' or 'cool'
914              
915             $Nest->fetch_HVAC_Mode();
916              
917             This method accepts no parameters
918            
919             Returns the HVAC mode
920             Returns 0 on failure
921            
922             =cut
923             sub fetch_HVAC_Mode {
924 0     0 1   my $self = shift;
925            
926 0 0         if (!defined $self->{'thermostat'}) {
927 0           print "Nest->fetch_HVAC_Mode(): No thermostat designation found\n";
928 0           return 0;
929             }
930            
931 0           return $self->__process_get($self->{'device_url'},'hvac_mode');
932             }
933              
934              
935             #*****************************************************************
936              
937             =head2 fetch_SW_Version - Fetch the software version reported by Nest
938              
939             Retrieves the software version reported by the Nest
940              
941             $Nest->fetch_SW_Version();
942              
943             This method accepts no parameters
944            
945             Returns the software version
946             Returns 0 on failure
947            
948             =cut
949             sub fetch_SW_Version {
950 0     0 1   my $self = shift;
951            
952 0 0         if (!defined $self->{'thermostat'}) {
953 0           print "Nest->fetch_SW_Version(): No thermostat designation found\n";
954 0           return 0;
955             }
956            
957 0           return $self->__process_get($self->{'device_url'},'software_version');
958             }
959              
960              
961             #*****************************************************************
962              
963             =head2 set_Target_Temperature_C - Set the target temperature in Celcius
964              
965             Set the target temperature in Celcius
966              
967             $Nest->set_Target_Temperature_C($temperature);
968              
969             This method accepts the following parameters:
970             - $temperature : target temperature in Celcius - Required
971            
972             Returns 1 on success
973             Returns 0 on failure
974            
975             =cut
976             sub set_Target_Temperature_C {
977 0     0 1   my $self = shift;
978 0           my $temperature = shift;
979            
980 0 0         if (!defined $self->{'thermostat'}) {
981 0           print "Nest->set_Target_Temperature_C(): No thermostat designation found\n";
982 0           return 0;
983             }
984 0 0         if (!defined $temperature) {
985 0           print "Nest->set_Target_Temperature_C(): Temperature is a required perameter\n";
986 0           return 0;
987             }
988              
989 0           return $self->__process_set($self->{'thermostat_url'},'target_temperature_C',$temperature);
990             }
991              
992              
993             #*****************************************************************
994              
995             =head2 set_Target_Temperature_high_C - Set the high target temperature in Celcius
996              
997             Set the high target temperature in Celcius
998              
999             $Nest->set_Target_Temperature_high_C($temperature);
1000              
1001             This method accepts the following parameters:
1002             - $temperature : high target temperature in Celcius - Required
1003            
1004             Returns 1 on success
1005             Returns 0 on failure
1006            
1007             =cut
1008             sub set_Target_Temperature_high_C {
1009 0     0 1   my $self = shift;
1010 0           my $temperature = shift;
1011            
1012 0 0         if (!defined $self->{'thermostat'}) {
1013 0           print "Nest->set_Target_Temperature_high_C(): No thermostat designation found\n";
1014 0           return 0;
1015             }
1016 0 0         if (!defined $temperature) {
1017 0           print "Nest->set_Target_Temperature_high_C(): Temperature is a required perameter\n";
1018 0           return 0;
1019             }
1020            
1021 0           return $self->__process_set($self->{'thermostat_url'},'target_temperature_high_C',$temperature);
1022             }
1023              
1024              
1025             #*****************************************************************
1026              
1027             =head2 set_Target_Temperature_low_C - Set the low target temperature in Celcius
1028              
1029             Set the low target temperature in Celcius
1030              
1031             $Nest->set_Target_Temperature_low_C($temperature);
1032              
1033             This method accepts the following parameters:
1034             - $temperature : low target temperature in Celcius - Required
1035            
1036             Returns 1 on success
1037             Returns 0 on failure
1038            
1039             =cut
1040             sub set_Target_Temperature_low_C {
1041 0     0 1   my $self = shift;
1042 0           my $temperature = shift;
1043            
1044 0 0         if (!defined $self->{'thermostat'}) {
1045 0           print "Nest->set_Target_Temperature_low_C(): No thermostat designation found\n";
1046 0           return 0;
1047             }
1048 0 0         if (!defined $temperature) {
1049 0           print "Nest->set_Target_Temperature_low_C(): Temperature is a required perameter\n";
1050 0           return 0;
1051             }
1052            
1053 0           return $self->__process_set($self->{'thermostat_url'},'target_temperature_low_C',$temperature);
1054             }
1055              
1056              
1057             #*****************************************************************
1058              
1059             =head2 set_Target_Temperature_F - Set the target temperature in Fahrenheit
1060              
1061             Set the target temperature in Fahrenheit
1062              
1063             $Nest->set_Target_Temperature_F($temperature);
1064              
1065             This method accepts the following parameters:
1066             - $temperature : target temperature in Fahrenheit - Required
1067            
1068             Returns 1 on success
1069             Returns 0 on failure
1070            
1071             =cut
1072             sub set_Target_Temperature_F {
1073 0     0 1   my $self = shift;
1074 0           my $temperature = shift;
1075            
1076 0 0         if (!defined $self->{'thermostat'}) {
1077 0           print "Nest->set_Target_Temperature_F(): No thermostat designation found\n";
1078 0           return 0;
1079             }
1080 0 0         if (!defined $temperature) {
1081 0           print "Nest->set_Target_Temperature_F(): Temperature is a required perameter\n";
1082 0           return 0;
1083             }
1084            
1085 0           return $self->__process_set($self->{'thermostat_url'},'target_temperature_F',$temperature);
1086             }
1087              
1088              
1089             #*****************************************************************
1090              
1091             =head2 set_Target_Temperature_high_F - Set the high target temperature in Fahrenheit
1092              
1093             Set the high target temperature in Fahrenheit
1094              
1095             $Nest->set_Target_Temperature_high_F($temperature);
1096              
1097             This method accepts the following parameters:
1098             - $temperature : high target temperature in Fahrenheit - Required
1099            
1100             Returns 1 on success
1101             Returns 0 on failure
1102            
1103             =cut
1104             sub set_Target_Temperature_high_F {
1105 0     0 1   my $self = shift;
1106 0           my $temperature = shift;
1107            
1108 0 0         if (!defined $self->{'thermostat'}) {
1109 0           print "Nest->set_Target_Temperature_high_F(): No thermostat designation found\n";
1110 0           return 0;
1111             }
1112 0 0         if (!defined $temperature) {
1113 0           print "Nest->set_Target_Temperature_high_F(): Temperature is a required perameter\n";
1114 0           return 0;
1115             }
1116            
1117 0           return $self->__process_set($self->{'thermostat_url'},'target_temperature_high_F',$temperature);
1118             }
1119              
1120              
1121             #*****************************************************************
1122              
1123             =head2 set_Target_Temperature_low_F - Set the low target temperature in Fahrenheit
1124              
1125             Set the low target temperature in Fahrenheit
1126              
1127             $Nest->set_Target_Temperature_low_F($temperature);
1128              
1129             This method accepts the following parameters:
1130             - $temperature : low target temperature in Fahrenheit - Required
1131              
1132             Returns 1 on success
1133             Returns 0 on failure
1134            
1135             =cut
1136             sub set_Target_Temperature_low_F {
1137 0     0 1   my $self = shift;
1138 0           my $temperature = shift;
1139            
1140 0 0         if (!defined $self->{'thermostat'}) {
1141 0           print "Nest->set_Target_Temperature_low_F(): No thermostat designation found\n";
1142 0           return 0;
1143             }
1144 0 0         if (!defined $temperature) {
1145 0           print "Nest->set_Target_Temperature_low_F(): Temperature is a required perameter\n";
1146 0           return 0;
1147             }
1148            
1149 0           return $self->__process_set($self->{'thermostat_url'},'target_temperature_low_F',$temperature);
1150             }
1151              
1152              
1153             #*****************************************************************
1154              
1155             =head2 set_Away_State - Set the away state of the Nest
1156              
1157             Set the away state of the Nest to either 'home' or 'away'
1158              
1159             $Nest->set_Away_State($state);
1160              
1161             This method accepts the following parameters:
1162             - $state : away state either 'home' or 'away' - Required
1163              
1164             Returns 1 on success
1165             Returns 0 on failure
1166            
1167             =cut
1168             sub set_Away_State {
1169 0     0 1   my $self = shift;
1170 0           my $state = shift;
1171            
1172 0 0         if (!defined $self->{'thermostat'}) {
1173 0           print "Nest->set_Away_State(): No thermostat designation found\n";
1174 0           return 0;
1175             }
1176 0 0         if (!defined $state) {
1177 0           print "Nest->set_Away_State(): State is a required perameter\n";
1178 0           return 0;
1179             }
1180            
1181 0           my %state = ('away' => $state);
1182 0           my $json = encode_json(\%state);
1183              
1184 0           my $response = $self->{'ua'}->put($self->{'struct_url'},
1185             Content_Type => 'application/json',
1186             content => $json);
1187              
1188 0           $self->{'last_code'} = $response->code;
1189            
1190 0 0         if ($response->is_success) {
1191 0           return $response->content;
1192             } else {
1193 0           print "Nest->set_Away_State(): Failed with return code ".$self->get_last_code()."\n";
1194 0           return 0;
1195             }
1196             }
1197              
1198              
1199             ##*****************************************************************
1200             #
1201             #=head2 set_Temperature_Scale - Set the temperature scale
1202             #
1203             # Set the temperature as either F (Fahrenheit) or C (Celcius)
1204             #
1205             # $Nest->set_Temperature_Scale($scale);
1206             #
1207             # This method accepts the following parameters:
1208             # - $scale : F (Fahrenheit) or C (Celcius) - Required
1209             #
1210             # Returns 1 on success
1211             # Returns 0 on failure
1212             #
1213             #=cut
1214             #
1215             #sub set_Temperature_Scale {
1216             # my $self = shift;
1217             # my $scale = shift;
1218             #
1219             # if (!defined $self->{'thermostat'}) {
1220             # print "Nest->set_Temperature_Scale(): No thermostat designation found\n";
1221             # return 0;
1222             # }
1223             # if (!defined $scale) {
1224             # print "Nest->set_Temperature_Scale(): Scale is a required perameter\n";
1225             # return 0;
1226             # }
1227             #
1228             # return $self->__process_set($self->{'thermostat_url'},'temperature_scale');
1229             #}
1230             #
1231              
1232              
1233             #*****************************************************************
1234              
1235             =head2 dump_Object - shows the contents of the local Nest object
1236              
1237             shows the contents of the local Nest object in human readable form
1238              
1239             $Nest->dump_Object();
1240              
1241             This method accepts no parameters
1242            
1243             Returns nothing
1244            
1245             =cut
1246             sub dump_Object {
1247 0     0 1   my $self = shift;
1248            
1249 0           print "ClientID : ".substr($self->{'ClientID'}, 0,120)."\n";
1250 0           print "ClientSecret : ".substr($self->{'ClientSecret'}, 0,120)."\n";
1251 0           print "auth_token : ".substr($self->{'auth_token'}, 0,120)."\n";
1252 0           print "PIN_code : ".substr($self->{'PIN_code'}, 0,120)."\n";
1253 0           print "device_url : ".substr($self->{'device_url'}, 0,120)."\n";
1254 0           print "struct_url : ".substr($self->{'struct_url'}, 0,120)."\n";
1255 0           print "structure : ".substr($self->{'structure'}, 0,120)."\n";
1256 0           print "thermostat_url : ".substr($self->{'thermostat_url'},0,120)."\n";
1257 0           print "thermostat : ".substr($self->{'thermostat'}, 0,120)."\n";
1258 0           print "debug : ".substr($self->{'debug'}, 0,120)."\n";
1259 0           print "last_code : ".substr($self->{'last_code'}, 0,120)."\n";
1260 0           print "last_reason : ".substr($self->{'last_reason'}, 0,120)."\n";
1261 0           print "\n";
1262             }
1263              
1264              
1265             #*****************************************************************
1266              
1267             =head2 get_last_code - returns the code generated by the most recent fetch
1268              
1269             Returns the HTTP Header code for the most recent fetch command
1270              
1271             $Nest->get_last_code();
1272              
1273             This method accepts no parameters
1274            
1275             Returns the numeric code
1276            
1277             =cut
1278              
1279             sub get_last_code {
1280 0     0 1   my $self = shift;
1281 0           return $self->{'last_code'};
1282             }
1283              
1284             #*****************************************************************
1285              
1286             =head2 get_last_reason - returns the text generated by the most recent fetch
1287              
1288             Returns the HTTP Header reason for the most recent fetch command
1289              
1290             $Nest->get_last_reason();
1291              
1292             This method accepts no parameters
1293            
1294             Returns the textual reason
1295            
1296             =cut
1297              
1298             sub get_last_reason {
1299 0     0 1   my $self = shift;
1300 0           return $self->{'last_reason'};
1301             }
1302              
1303             #******************************************************************************
1304             =head2 get_last_exec_time - returns the execution time for the last fetch
1305              
1306             Returns the number of milliseconds it took for the last fetch call
1307              
1308             $Nest->get_last_exec_time();
1309              
1310             This method accepts no parameters
1311            
1312             Returns the number of milliseconds
1313            
1314             =cut
1315              
1316             sub get_last_exec_time {
1317 0     0 0   my $self = shift;
1318 0           return $self->{'last_exec_time'};
1319             }
1320              
1321             #*****************************************************************
1322              
1323             sub __process_get {
1324 0     0     my $self = shift;
1325 0           my $url = shift;
1326 0           my $tag = shift;
1327 0           my $time_before = gettimeofday;
1328 0           my $response = $self->{'ua'}->get($url);
1329 0           my $time_after = gettimeofday;
1330            
1331 0           $self->{'last_exec_time'} = eval{($time_after-$time_before)*1000};
  0            
1332 0           $self->{'last_code'} = $response->code;
1333            
1334 0 0         if ($response->is_success) {
1335 0           my $decoded_response = decode_json($response->content);
1336             # print Dumper($decoded_response);
1337 0           return $decoded_response->{'thermostats'}->{$self->{'thermostat'}}->{$tag};
1338             } else {
1339 0           print "\n".(caller(1))[3]."(): Failed with return code ".$self->get_last_code()."\n";
1340 0           return 0;
1341             }
1342             }
1343              
1344              
1345             #*****************************************************************
1346              
1347             sub __process_set {
1348 0     0     my $self = shift;
1349 0           my $url = shift;
1350 0           my $tag = shift;
1351 0           my $value = shift;
1352              
1353 0           my $response = $self->{'ua'}->put($url."/".$tag."?auth=".$self->{'auth_token'},
1354             Content_Type => 'application/json',
1355             content => $value );
1356 0           $self->{'last_code'} = $response->code;
1357 0           $self->{'last_reason'} = decode_json($response->content)->{'error'};
1358              
1359 0 0         if ($response->is_success) {
1360 0           return $response->content;
1361             } else {
1362 0           print "\n".(caller(1))[3]."(): Failed with return code ".$self->get_last_code()." - ".$self->get_last_reason()."\n";
1363 0           return 0;
1364             }
1365             }
1366              
1367              
1368             #*****************************************************************
1369              
1370             =head1 AUTHOR
1371              
1372             Kedar Warriner, C
1373              
1374             =head1 BUGS
1375              
1376             Please report any bugs or feature requests to C
1377             or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Device-Nest
1378             I will be notified, and then you'll automatically be notified of progress on
1379             your bug as I make changes.
1380              
1381             =head1 SUPPORT
1382              
1383             You can find documentation for this module with the perldoc command.
1384              
1385             perldoc Device::Nest
1386              
1387             You can also look for information at:
1388              
1389             =over 5
1390              
1391             =item * RT: CPAN's request tracker
1392              
1393             L
1394              
1395             =item * AnnoCPAN: Annotated CPAN documentation
1396              
1397             L
1398              
1399             =item * CPAN Ratings
1400              
1401             L
1402              
1403             =item * Search CPAN
1404              
1405             L
1406              
1407             =back
1408              
1409             =head1 ACKNOWLEDGEMENTS
1410              
1411             Many thanks to:
1412             The guys at Nest for creating the Nest Thermostat sensor and
1413             developping the API.
1414             Everyone involved with CPAN.
1415              
1416             =head1 LICENSE AND COPYRIGHT
1417              
1418             Copyright 2014 Kedar Warriner .
1419              
1420             This program is free software; you can redistribute it and/or modify it
1421             under the terms of either: the GNU General Public License as published
1422             by the Free Software Foundation; or the Artistic License.
1423              
1424             See http://dev.perl.org/licenses/ for more information.
1425              
1426             =cut
1427              
1428             #********************************************************************
1429             1; # End of Device::Nest - Return success to require/use statement
1430             #********************************************************************
1431              
1432