File Coverage

lib/Hardware/1Wire/HA7Net.pm
Criterion Covered Total %
statement 56 247 22.6
branch 3 104 2.8
condition 1 6 16.6
subroutine 16 36 44.4
pod 5 5 100.0
total 81 398 20.3


line stmt bran cond sub pod time code
1             package Hardware::1Wire::HA7Net;
2              
3 1     1   4705 use Exporter;
  1         3  
  1         54  
4 1     1   9545 use LWP::UserAgent;
  1         88563  
  1         32  
5 1     1   11 use strict;
  1         7  
  1         35  
6 1     1   5 use vars qw($VERSION @ISA);
  1         2  
  1         98  
7              
8             $VERSION = '1.01';
9             @ISA = qw(Exporter);
10             my ($RCSVERSION) = '$Revision: 1.7 $ ' =~ /\$Revision:\s+([^\s]+)/;
11              
12             ##############################################################################
13             package Hardware::1Wire::HA7Net::Generic;
14              
15 1     1   5 use Exporter;
  1         1  
  1         44  
16 1     1   4 use vars qw($VERSION @ISA);
  1         1  
  1         321  
17              
18             @ISA = qw(Exporter);
19             $VERSION = $Hardware::1Wire::HA7Net::VERSION;
20              
21             sub _new {
22 0     0   0 my ($class, $ha7net, $address) = @_;
23 0         0 my $family = substr($address, -2);
24              
25 0         0 return bless {
26             ha7net => $ha7net,
27             family => $family,
28             address => $address,
29             }, $class;
30             }
31              
32             sub isa {
33 0     0   0 my ($self, $type) = @_;
34              
35 0 0       0 die "Cannot match empty type" unless $type;
36 0         0 for my $match (@{$self->{type}}) {
  0         0  
37 0 0       0 return 1 if uc($type) eq $match;
38             }
39 0         0 return 0;
40             }
41              
42 0     0   0 sub ha7net { shift->{ha7net} }
43              
44 0     0   0 sub family { shift->{family} }
45              
46 0     0   0 sub address { shift->{address} }
47              
48 0     0   0 sub units { shift->{units} }
49              
50             sub type {
51 0     0   0 my $self = shift;
52 0 0       0 if (wantarray) {
53 0         0 return @{ $self->{type} };
  0         0  
54             }
55             else {
56 0         0 return $self->{type}[0];
57             }
58             }
59              
60 0     0   0 sub part_of { shift->{part_of} }
61              
62             ##############################################################################
63             package Hardware::1Wire::HA7Net::DS1820;
64              
65 1     1   6 use Exporter;
  1         2  
  1         28  
66 1     1   4 use vars qw($VERSION @ISA);
  1         2  
  1         248  
67              
68             @ISA = qw(Hardware::1Wire::HA7Net::Generic);
69             $VERSION = $Hardware::1Wire::HA7Net::VERSION;
70              
71             sub _new {
72 0     0   0 my $class = shift;
73 0         0 my $self = $class->SUPER::_new(@_);
74              
75 0         0 $self->{type} = [qw(DS1820 DS18S20 DS1920 1820 18S20 1920)];
76 0         0 $self->{units} = "Degrees C";
77 0         0 return $self;
78             }
79              
80              
81             sub temperature {
82 0     0   0 my $self = shift;
83 0         0 my $response = $self->{ha7net}->{ua}->get($self->{ha7net}->{baseurl} .
84             "1Wire/ReadTemperature.html?Address_Array=$self->{address}");
85 0 0       0 return undef unless $response->is_success;
86 0         0 $response = $response->content;
87              
88 0         0 my @response = $response =~
89             /ID="Address.*?VALUE="([\dA-F]+)"
90             .*?
91             ID="Temperature.*?VALUE="(-?[\d.]+)"/x;
92              
93 0 0       0 if (wantarray) {
94 0         0 return @response;
95             }
96             else {
97 0         0 return $response[1];
98             }
99             }
100              
101             ##############################################################################
102             package Hardware::1Wire::HA7Net::DS18B20;
103              
104 1     1   5 use Exporter;
  1         2  
  1         48  
105 1     1   6 use vars qw($VERSION @ISA);
  1         1  
  1         309  
106              
107             @ISA = qw(Hardware::1Wire::HA7Net::Generic);
108             $VERSION = $Hardware::1Wire::HA7Net::VERSION;
109              
110             sub _new {
111 0     0   0 my $class = shift;
112 0         0 my $self = $class->SUPER::_new(@_);
113              
114 0         0 $self->{resolution} = 12;
115 0         0 $self->{type} = [qw(DS18B20 18B20)];
116 0         0 $self->{units} = "Degrees C";
117 0         0 return $self;
118             }
119              
120             sub resolution {
121 0     0   0 my $self = shift;
122 0 0       0 if (@_) {
123 0         0 my $resolution = shift;
124 0 0 0     0 die "Resolution out of range of 9..12 for DS18B20 $self->{address}"
125             if $resolution < 9 || $ resolution > 12;
126 0         0 $self->{resolution} = $resolution;
127             }
128 0         0 return $self->{resolution};
129             }
130              
131             sub temperature {
132 0     0   0 my $self = shift;
133 0         0 my $response = $self->{ha7net}->{ua}->get($self->{ha7net}->{baseurl} .
134             "1Wire/ReadDS18B20.html?DS18B20Request={$self->{address},$self->{resolution}}");
135 0 0       0 return undef unless $response->is_success;
136 0         0 $response = $response->content;
137              
138 0         0 my @response = $response =~
139             /ID="Address.*?VALUE="([\dA-F]+)"
140             .*?
141             ID="Temperature.*?VALUE="(-?[\d.]+)
142             .*?
143             ID="Resolution.*?VALUE="([\d]+\+?)"/x;
144              
145 0 0       0 if (wantarray) {
146 0         0 return @response;
147             }
148             else {
149 0         0 return $response[1];
150             }
151             }
152              
153             ##############################################################################
154             package Hardware::1Wire::HA7Net::Analog;
155              
156 1     1   5 use Exporter;
  1         2  
  1         33  
157 1     1   5 use vars qw($VERSION @ISA);
  1         2  
  1         1392  
158              
159             @ISA = qw(Hardware::1Wire::HA7Net::Generic);
160             $VERSION = $Hardware::1Wire::HA7Net::VERSION;
161              
162             sub _new {
163 0     0   0 my $class = shift;
164 0         0 my $self = $class->SUPER::_new(@_);
165              
166 0         0 my $response = $self->{ha7net}->{ua}->get($self->{ha7net}->{baseurl} .
167             "1Wire/ReadAnalogProbe.html?Address_Array=$self->{address}")->content;
168              
169 0         0 my ($type, $units) = $response =~
170             /ID="Probe_Type.*?VALUE="(\w+)"
171             .*?
172             ID="Probe_Units.*?VALUE="(.*?)"/x;
173              
174 0         0 $self->{type} = [ $type ];
175 0         0 $self->{units} = $units;
176 0         0 return $self;
177             }
178              
179             sub value {
180 0     0   0 my $self = shift;
181 0         0 my $response = $self->{ha7net}->{ua}->get($self->{ha7net}->{baseurl} .
182             "1Wire/ReadAnalogProbe.html?Address_Array=$self->{address}");
183 0 0       0 return undef unless $response->is_success;
184 0         0 $response = $response->content;
185              
186 0         0 my @response = $response =~
187             /ID="Probe_Address.*?VALUE="([\dA-F]+)"
188             .*?
189             ID="Probe_Value.*?VALUE="(-?[\d.]+)"
190             .*?
191             ID="Temperature_Address.*?VALUE="([\dA-F]+)"
192             .*?
193             ID="Temperature.*?VALUE="(-?[\d.]+?)"/x;
194              
195 0 0       0 if (wantarray) {
196 0         0 return @response;
197             }
198             else {
199 0         0 return $response[1];
200             }
201             }
202              
203             ##############################################################################
204             package Hardware::1Wire::HA7Net;
205              
206             sub new {
207 1     1 1 188 my $class = shift;
208 1         5 my $self = bless {}, $class;
209 1         2 my ($scan, $response, $sensor);
210 1 50       5 if (@_) {
211 1         97 ($self->{baseurl}, $scan) = @_;
212 1         9 $self->{baseurl} =~ s#/*$#/#;
213             }
214             else {
215 0         0 die "Cannot yet probe for HA7Net's";
216             }
217              
218 1         11 $self->{ua} = new LWP::UserAgent;
219 1         4337 $self->{ua}->agent("$class/$VERSION");
220              
221             #
222             # If we do not want to scan the device now, just fill in empty arrays and
223             # return. Otherwise, continue with scanning the devices.
224             #.
225 1 50 33     74 if (defined $scan && $scan == 0) {
226 0         0 @{ $self->{sensors} } = ();
  0         0  
227 0         0 %{ $self->{by_addr} } = ();
  0         0  
228 0         0 return $self;
229             }
230              
231 1         8 $response = $self->{ua}->get($self->{baseurl} .
232             "1Wire/Search.html?ConditionalSearch=on");
233 1 50       137574 die "Cannot access $self->{baseurl}" unless $response->is_success;
234              
235 0           ($self->{version}) = $response->content =~ /HA7Net: (\d+(\.\d+)+)/;
236              
237 0           for my $addr ($response->content =~ /ID="ADDRESS.*?VALUE="([\dA-F]+)"/g) {
238 0 0         if ($addr =~ /10$/) {
    0          
    0          
239 0           $sensor = _new Hardware::1Wire::HA7Net::DS1820 ($self, $addr);
240             }
241             elsif ($addr =~ /28$/) {
242 0           $sensor = _new Hardware::1Wire::HA7Net::DS18B20 ($self, $addr);
243             }
244             elsif ($addr =~ /12$/) {
245 0           $sensor = _new Hardware::1Wire::HA7Net::Analog ($self, $addr);
246             }
247 0           push @{ $self->{sensors} }, $sensor;
  0            
248 0           $self->{by_addr}->{$sensor->{address}} = $sensor;
249             }
250              
251 0           _generate_associated_addresses($self);
252 0           return $self;
253             }
254              
255             sub scan {
256 0     0 1   my $self = shift;
257 0           my ($response, $sensor, %by_addr, @new);
258              
259 0           @{ $self->{sensors} } = ();
  0            
260 0           %by_addr = %{ $self->{by_addr} };
  0            
261 0           %{ $self->{by_addr} } = ();
  0            
262              
263 0           $response = $self->{ua}->get($self->{baseurl} .
264             "1Wire/Search.html?ConditionalSearch=on");
265              
266 0           for my $addr ($response->content =~ /ID="ADDRESS.*?VALUE="([\dA-F]+)"/g) {
267 0 0         unless ($sensor = delete $by_addr{$addr}) {
268 0 0         if ($addr =~ /10$/) {
    0          
    0          
269 0           $sensor = _new Hardware::1Wire::HA7Net::DS1820 ($self, $addr);
270             }
271             elsif ($addr =~ /28$/) {
272 0           $sensor = _new Hardware::1Wire::HA7Net::DS18B20 ($self, $addr);
273             }
274             elsif ($addr =~ /12$/) {
275 0           $sensor = _new Hardware::1Wire::HA7Net::Analog ($self, $addr);
276             }
277 0           push @new, $sensor;
278             }
279 0           push @{ $self->{sensors} }, $sensor;
  0            
280 0           $self->{by_addr}->{$sensor->{address}} = $sensor;
281             }
282              
283 0           _generate_associated_addresses($self);
284              
285 0 0         if (defined $_[0]) {
286 0 0         if (ref $_[0] eq "ARRAY") {
287 0           @{ $_[0] } = @new;
  0            
288             }
289             else {
290 0           $_[0] = \@new;
291             }
292             }
293 0 0         if (defined $_[1]) {
294 0 0         if (ref $_[1] eq "ARRAY") {
295 0           @{ $_[1] } = values %by_addr;
  0            
296             }
297             else {
298 0           $_[1] = [ values %by_addr ];
299             }
300             }
301 0           return (@new + values %by_addr); # Number of (existence of) changes
302             }
303              
304             #
305             # Find the addresses of the Temperature sensor in the analog sensors. Not
306             # to be called externally.
307             #
308             sub _generate_associated_addresses {
309 0     0     my $self = shift;
310 0           my $response;
311              
312 0           ANALOG: for my $addr (keys %{ $self->{by_addr} }) {
  0            
313 0           my $assoc_addr;
314 0 0         next unless $addr =~ /12$/;
315 0           for my $chk (values %{ $self->{by_addr} }) {
  0            
316 1     1   8 no warnings;
  1         2  
  1         1075  
317 0 0         next ANALOG if $chk->{part_of} == $self->{by_addr}->{$addr};
318             }
319 0           $response = $self->{ua}->get($self->{baseurl} .
320             "1Wire/ReadAnalogProbe.html?Address_Array=$addr")->content;
321 0           ($assoc_addr) =
322             $response =~ /ID="Temperature_Address.*?VALUE="([\dA-F]+)"/;
323 0           $self->{by_addr}->{$assoc_addr}->{part_of} = $self->{by_addr}->{$addr};
324             }
325             }
326              
327             sub sensors {
328 0     0 1   @{ shift->{sensors} }
  0            
329             }
330              
331             sub read {
332 0     0 1   my $self = shift;
333 0           my (@objs, @ds1820, @ds18b20, @analog, %analog_objs, $response,
334             $sensor, $sub, @answer, @results);
335              
336             #
337             # Read everything by default, else read the set specified by the caller
338             #
339 0 0         @_ = @{$self->{sensors}} unless @_;
  0            
340             #
341             # First, convert all request entities to objects, preparing for batched
342             # reads. If there is no object associated with a particular address,
343             # create the object (which means reading it). We exclude those from a
344             # second scan, of course. If there are any previously unknown sensors,
345             # there will be @results, so we'll have to _generate_associated_addresses
346             #
347 0           for my $addr (@_) {
348 0 0         if (ref($addr) =~ /^Hardware::1Wire::HA7Net::/) {
    0          
    0          
    0          
349 0           push @objs, $addr;
350             }
351             elsif (ref($addr)) {
352 0           die "Cannot use read on a non Hardware::1Wire::HA7Net:: object";
353             }
354             elsif ($addr !~ /^[\dA-Fa-f]{16}/) {
355 0           die "Illegal Dalls Semiconductor device address";
356             }
357             elsif (exists $self->{by_addr}->{$addr}) {
358 0           push @objs, $self->{by_addr}->{$addr};
359             }
360             else {
361             #
362             # Add a new device!
363             #
364 0 0         if ($addr =~ /10$/) {
    0          
    0          
365 0           $response = $self->{ua}->get($self->{baseurl} .
366             "1Wire/ReadTemperature.html?Address_Array=$addr")->content;
367 0 0         if (@answer = $response =~
368             /ID="Address.*?VALUE="([\dA-F]+)"
369             .*?
370             ID="Temperature.*?VALUE="(-?[\d.]+)"/gx) {
371 0           $sensor = _new Hardware::1Wire::HA7Net::DS1820 ($self, $addr);
372             }
373             else {
374 0           warn "Cannot read device at address $addr";
375             }
376             }
377             elsif ($addr =~ /28$/) {
378 0           $response = $self->{ua}->get($self->{baseurl} .
379             "1Wire/ReadDS18B20.html?DS18B20Request={$addr,12}")->content;
380              
381 0 0         if (@answer = $response =~
382             /ID="Address.*?VALUE="([\dA-F]+)"
383             .*?
384             ID="Temperature.*?VALUE="(-?[\d.]+)/gx) {
385 0           $sensor = _new Hardware::1Wire::HA7Net::DS18B20 ($self, $addr);
386             }
387             else {
388 0           warn "Cannot read device at address $addr";
389             }
390             }
391             elsif ($addr =~ /12$/) {
392 0           $response = $self->{ua}->get($self->{baseurl} .
393             "1Wire/ReadAnalogProbe.html?Address_Array=$addr")->content;
394              
395 0 0         if (@answer = $response =~
396             /ID="Probe_Address.*?VALUE="([\dA-F]+)"
397             .*?
398             ID="Probe_Value.*?VALUE="(-?[\d.]+)"
399             .*?
400             ID="Temperature_Address.*?VALUE="([\dA-F]+)"
401             .*?
402             ID="Temperature.*?VALUE="(-?[\d.]+?)"/gx) {
403 0           $sensor = _new Hardware::1Wire::HA7Net::Analog ($self, $addr);
404             #
405             # Analog devices have a temperature sensor associated
406             # with them - create that device, too (and enter it here)
407             #
408 0 0         if ($answer[2] =~ /10$/) {
    0          
409 0           $sub = _new Hardware::1Wire::HA7Net::DS1820 ($self, $answer[2]);
410             }
411             elsif ($answer[2] =~ /28$/) {
412 0           $sub = _new Hardware::1Wire::HA7Net::DS18B20 ($self, $answer[2]);
413             }
414             else {
415 0           die "Unknown co-sensor associated with $addr";
416             }
417 0           push @{ $self->{sensors} }, $sub;
  0            
418 0           $self->{by_addr}->{$sub->{address}} = $sub;
419             }
420             else {
421 0           warn "Cannot read device at address $addr";
422             }
423             }
424             else {
425 0           die "Unknown sensor type $addr";
426             }
427 0 0         if (@answer) {
428 0           push @{ $self->{sensors} }, $sensor;
  0            
429 0           $self->{by_addr}->{$sensor->{address}} = $sensor;
430 0           push @results, @answer;
431             }
432             }
433             }
434 0 0         _generate_associated_addresses($self) if @results;
435             #
436             #
437             # Next find all the analog sensors in the set of addresses given
438             #
439 0           for my $sensor (@objs) {
440 0 0         if ($sensor->{ha7net} != $self) {
441 0           warn "Cannot read sensor from a different HA7Net";
442 0           next;
443             }
444 0 0         if ($sensor->isa("hmp2001s")) {
445 0           push @analog, $sensor->{address};
446 0           $analog_objs{$sensor}++;
447             }
448             }
449             #
450             # Then find all the temperature sensors, eliminating those that would
451             # be read by the analog read routine (so we don't read them twice)
452             #
453 0           for my $sensor (@objs) {
454 0 0         if ($sensor->{ha7net} != $self) {
455 0           warn "Cannot read sensor from a different HA7Net";
456 0           next;
457             }
458 0 0         if ($sensor->isa("ds1820")) {
    0          
459 1     1   14 no warnings; # Otherwise the "unless" will elicit them
  1         2  
  1         63  
460 0 0         push @ds1820, $sensor->{address}
461             unless $analog_objs{$sensor->part_of};
462             }
463             elsif ($sensor->isa("ds18b20")) {
464 1     1   5 no warnings; # Otherwise the "unless" will elicit them
  1         2  
  1         417  
465 0 0         push @ds18b20, "{$sensor->{address},$sensor->{resolution}}"
466             unless $analog_objs{$sensor->part_of};
467             }
468             }
469             #
470             # Now actually read the devices
471             #
472 0 0         if (@ds1820) {
473 0           $response = $self->{ua}->get($self->{baseurl} .
474             "1Wire/ReadTemperature.html?Address_Array=" .
475             join ",", @ds1820)->content;
476 0           push @results, $response =~
477             /ID="Address.*?VALUE="([\dA-F]+)"
478             .*?
479             ID="Temperature.*?VALUE="(-?[\d.]+)"/gx;
480             }
481 0 0         if (@ds18b20) {
482 0           $response = $self->{ua}->get($self->{baseurl} .
483             "1Wire/ReadDS18B20.html?DS18B20Request=" .
484             join ",", @ds18b20)->content;
485              
486 0           push @results, $response =~
487             /ID="Address.*?VALUE="([\dA-F]+)"
488             .*?
489             ID="Temperature.*?VALUE="(-?[\d.]+)/gx;
490             }
491 0 0         if (@analog) {
492 0           $response = $self->{ua}->get($self->{baseurl} .
493             "1Wire/ReadAnalogProbe.html?Address_Array=" .
494             join ",", @analog)->content;
495              
496 0           push @results, $response =~
497             /ID="Probe_Address.*?VALUE="([\dA-F]+)"
498             .*?
499             ID="Probe_Value.*?VALUE="(-?[\d.]+)"
500             .*?
501             ID="Temperature_Address.*?VALUE="([\dA-F]+)"
502             .*?
503             ID="Temperature.*?VALUE="(-?[\d.]+?)"/gx;
504             }
505              
506 0           return @results;
507             }
508              
509 0     0 1   sub version { shift->{version} }
510              
511             1;
512              
513             __END__