File Coverage

blib/lib/WebService/Freebox.pm
Criterion Covered Total %
statement 18 73 24.6
branch 0 12 0.0
condition n/a
subroutine 6 15 40.0
pod 5 5 100.0
total 29 105 27.6


line stmt bran cond sub pod time code
1 5     5   151892 use strict;
  5         12  
  5         186  
2 5     5   25 use warnings;
  5         8  
  5         224  
3              
4             package WebService::Freebox;
5              
6             # ABSTRACT: Freebox API wrappers.
7              
8              
9 5     5   4340 use Mouse;
  5         222193  
  5         29  
10              
11 5     5   7430 use JSON;
  5         112938  
  5         42  
12 5     5   8526 use REST::Client;
  5         340108  
  5         3688  
13              
14             our $VERSION = '0.001'; # VERSION
15              
16             has app_id => ( is => 'ro', isa => 'Str', required => 1 );
17             has app_version => ( is => 'ro', isa => 'Str', required => 1 );
18              
19             has app_token => ( is => 'ro', isa => 'Str' );
20              
21             has _client => ( is => 'ro', builder => '_create_client' );
22             has _api_version => ( is => 'ro', builder => '_get_api_version' );
23              
24             # Create the REST client we're going to use for all our requests.
25             sub _create_client {
26 0     0     my $self = shift;
27              
28 0           my $c = REST::Client->new(host => 'http://mafreebox.freebox.fr');
29 0           $c->addHeader('Accept', 'application/json');
30 0           $c->addHeader('Content-Type', 'application/json');
31              
32 0           return $c;
33             }
34              
35             # Wrapper around REST::Client request() checking for errors: the first
36             # argument is the error message given if the request failed.
37             sub _request {
38 0     0     my ($self, $errmsg, $request, $url, $body) = @_;
39              
40 0           my $c = $self->_client;
41 0           $c->request($request, $url, $body);
42              
43 0           my $errcode = $c->responseCode();
44 0 0         if ($errcode != 200) {
45 0           die qq{$errmsg ("$request $url" failed with HTTP error $errcode).\n}
46             }
47              
48 0           return decode_json $c->responseContent()
49             }
50              
51             # Helper for making normal API requests, i.e. all except for the initial one,
52             # checking for the Freebox presence and detecting the API version.
53             sub _api_request {
54 0     0     my ($self, $errmsg, $request, $url, @rest) = @_;
55 0           $self->_request($errmsg, $request, '/api/v' . $self->_api_version . "/$url", @rest);
56             }
57              
58             # Detect the Freebox and get the API version used by it.
59             sub _get_api_version {
60 0     0     my $self = shift;
61              
62 0           my $res = $self->_request('Freebox v6 not detected', 'GET', '/api_version');
63              
64 0           my $api_version = $res->{api_version};
65 0 0         die "Unexpected Freebox API version $api_version.\n" if $api_version !~ '[23].0';
66              
67             # We need to use just the major number in the HTTP requests.
68 0           $api_version =~ s/\.\d$//;
69              
70 0           return $api_version;
71             }
72              
73              
74              
75              
76              
77             sub authorize {
78 0     0 1   my ($self, $app_name, $device_name) = @_;
79              
80 0           my $app_id = $self->app_id;
81              
82 0           my $res = $self->_api_request(
83             'Requesting authorization failed',
84             'POST',
85             'login/authorize/',
86             encode_json({
87             app_id => $app_id,
88             app_name => $app_name,
89             app_version => $self->app_version,
90             device_name => $device_name
91             })
92             );
93              
94 0           my $app_token = $res->{result}{app_token};
95              
96 0           my $track_id = $res->{result}{track_id};
97 0           while (1) {
98 0           $res = $self->_api_request(
99             'Waiting for authorization failed',
100             'GET',
101             "login/authorize/$track_id"
102             );
103              
104 0 0         last if $res->{result}{status} ne 'pending';
105              
106 0           sleep 1;
107             }
108              
109 0 0         die "Failed to obtain authorization for $app_id: $res->{result}{status}.\n"
110             unless $res->{result}{status} eq 'granted';
111              
112 0           return $app_token
113             }
114              
115              
116              
117             sub login {
118 0     0 1   my ($self, $session_token) = @_;
119              
120 0 0         if (defined $session_token) {
121 0           $self->_client->addHeader('X-Fbx-App-Auth', $session_token);
122             }
123              
124 0           my $res = $self->_api_request(
125             'Checking logged in status failed',
126             'GET',
127             'login/'
128             );
129              
130 0 0         if (!$res->{result}{logged_in}) {
131 5     5   6266 use Digest::SHA qw(hmac_sha1_hex);
  5         22321  
  5         2156  
132              
133 0           my $challenge = $res->{result}{challenge};
134 0           my $password = hmac_sha1_hex($challenge, $self->app_token);
135              
136 0           $res = $self->_api_request(
137             'Logging in failed',
138             'POST',
139             "login/session/",
140             encode_json({
141             app_id => $self->app_id,
142             app_version => $self->app_version,
143             password => $password,
144             })
145             );
146              
147 0           $session_token = $res->{result}{session_token};
148              
149 0           $self->_client->addHeader('X-Fbx-App-Auth', $session_token);
150             }
151              
152 0           return $session_token;
153             }
154              
155              
156              
157             sub get_system_config {
158 0     0 1   my $self = shift;
159              
160 0           my $res = $self->_api_request(
161             'Getting system configuration failed',
162             'GET',
163             'system/'
164             );
165              
166 0           return $res->{result}
167             }
168              
169              
170              
171             sub get_connection_status {
172 0     0 1   my $self = shift;
173              
174 0           my $res = $self->_api_request(
175             'Getting connection status failed',
176             'GET',
177             'connection/'
178             );
179              
180 0           return $res->{result}
181             }
182              
183              
184              
185             sub get_all_freeplugs {
186 0     0 1   my $self = shift;
187              
188 0           my $res = $self->_api_request(
189             'Failed to get freeplugs list',
190             'GET',
191             'freeplug/'
192             );
193              
194 0           my $fps = [];
195 0           foreach my $fp (@{$res->{result}[0]{members}}) {
  0            
196 0           push @$fps, $fp;
197             }
198              
199 0           return $fps
200             }
201              
202             __PACKAGE__->meta->make_immutable();
203              
204             __END__