File Coverage

blib/lib/Device/Modem/GSM.pm
Criterion Covered Total %
statement 12 136 8.8
branch 0 52 0.0
condition 0 26 0.0
subroutine 4 12 33.3
pod 7 8 87.5
total 23 234 9.8


line stmt bran cond sub pod time code
1             # This program is free software; you can redistribute it and/or
2             # modify it under the same terms as Perl itself.
3             #
4             # This program is distributed in the hope that it will be useful,
5             # but WITHOUT ANY WARRANTY; without even the implied warranty of
6             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
7             # Perl licensing terms for details.
8             #
9             # $Id: GSM.pm 14 2007-12-25 21:41:48Z kattoo $
10              
11             =head1 NAME
12              
13             Device::Modem::GSM - Perl module to communicate with a GSM cell phone connected via some sort of Serial port (including but not limited to most USB data cables, IrDA, ... others ?).
14              
15             =head1 SYNOPSIS
16              
17             use Device::Modem::GSM;
18            
19             my $gsm = new Device::Modem::GSM(
20             port => '/dev/ttyUSB0',
21             log => 'file,gsm_pb.log',
22             loglevel => 'info');
23            
24             if ($gsm->connect(baudrate => 38400)) {
25             print "Connected\n";
26             }
27             else {
28             die "Couldn't connect, stopped";
29             }
30             if (not $gsm->pb_storage("SM")) {
31             croak("Couldn't change phonebook storage");
32             }
33             $gsm->pb_write_entry(
34             index => 0,
35             text => "Daddy",
36             number => '+1234567');
37              
38             $entries = $gsm->pb_read_entries(1,10);
39             # or even $entries = $gsm->pb_read_all;
40             foreach (@$entries) {
41             print $_->{index}, ':', $_->{text}, ':', $_->{number}, "\n";
42             }
43              
44             =head1 DESCRIPTION
45              
46             C extends C (which provides the basic
47             communication layer) to provide access to high-level GSM functionnalities
48             (such as access to phonebook or dealing with SMSes).
49              
50             This module inherits from C so if you need lower level access methods, start looking there.
51              
52             =cut
53              
54             package Device::Modem::GSM;
55              
56 1     1   26096 use strict;
  1         3  
  1         39  
57 1     1   5 use warnings;
  1         2  
  1         28  
58              
59 1     1   5 use Carp;
  1         6  
  1         97  
60 1     1   1209 use Device::Modem;
  1         71901  
  1         1934  
61              
62             our $VERSION = '0.3';
63             our @ISA = ("Device::Modem");
64              
65             =head1 METHODS
66              
67             =head2 pb_storage
68              
69             =over 4
70              
71             pb_storage must be called before any other method dealing with the phonebook. This method will set the storage on which other method calls will operate.
72              
73             Supported storages will depend on the cell phone, but the following should always exist :
74              
75             =over 4
76              
77             =item .
78              
79             SM is the SIM card
80              
81             =item .
82              
83             ME is the phone memory
84              
85             =back
86              
87             Ex :
88             $gsm->pb_storage("SM");
89              
90             =back
91              
92             =cut
93              
94             sub pb_storage {
95 0     0 1   my $self = shift;
96              
97 0 0         if (@_) {
98 0           my $new_pb_storage = shift;
99              
100 0 0 0       if (not defined($self->{pb_storage}) or
101             $new_pb_storage ne $self->{pb_storage}) {
102             # trying to change storage
103 0           $self->atsend('AT+CPBS="' .
104             $new_pb_storage . '"' . Device::Modem::CR);
105 0           my ($result, @lines) = $self->parse_answer;
106 0 0         if ($result eq "OK") {
107 0           $self->{pb_storage} = $new_pb_storage;
108 0           $self->log->write('info',
109             'Phonebook storage changed to ' .
110             $new_pb_storage);
111             # trying to get storage specs
112 0           $self->atsend('AT+CPBR=?' . Device::Modem::CR);
113 0           ($result, @lines) = $self->parse_answer();
114 0 0         if ($result eq 'OK') {
115 0 0         if ($lines[0] =~ /^\+CPBR:\s\((.*)-(.*)\),(.*),(.*)$/) {
116 0           $self->{pb_storage_min} = $1;
117 0           $self->{pb_storage_max} = $2;
118 0           $self->{pb_storage_nlength} = $3;
119 0           $self->{pb_storage_tlength} = $4;
120             } else {
121 0           $self->log->write('warning',
122             "Ill formated phonebook storage specs");
123             }
124             } else {
125 0           $self->log->write('warning',
126             "Couldn't retrieve phonebook storage specs");
127             }
128             } else {
129 0           $self->log->write('error',
130             'Failed to change phonebook storage to ' .
131             $new_pb_storage);
132 0           return undef;
133             }
134             }
135             else {
136             # same storage : do nothing
137             }
138             }
139 0           return $self->{pb_storage};
140             }
141              
142             # check if storage was properly init'd
143             # returns 0 if not, 1 if storage is set, and 2 if storage is set AND
144             # specs were retrieved
145             sub pb_storage_ok {
146 0     0 0   my $self = shift;
147              
148 0 0         if (exists $self->{pb_storage}) {
149 0 0 0       if ((exists $self->{pb_storage_min}) and
150             (exists $self->{pb_storage_max})) {
151             # storage is ok
152 0           return 2;
153             } else {
154             # but the specs were not retrieved
155 0           $self->log->write('warning',
156             'Storage initialized, but was not able to get specs');
157 0           return 1;
158             }
159             } else {
160             # storage wasn't init'd
161 0           $self->log->write('warning',
162             'Storage not initialized ... did you first call pb_storage ?');
163 0           return 0;
164             }
165             }
166              
167             =head2 pb_write_entry
168              
169             =over 4
170              
171             This method will write an entry into the phonebook.
172              
173             Ex :
174              
175             $gsm->pb_write_entry(
176             index => 1,
177             text => 'John Doe',
178             number => '+3312345');
179              
180             The "index" parameter specifies the storage slot to fill. If none specified, then the first empty is used.
181              
182             =back
183              
184             =cut
185              
186             sub pb_write_entry {
187 0     0 1   my $self = shift;
188 0           my %args = @_;
189              
190 0   0       $args{'index'} ||= "";
191              
192 0           $self->log->write('info', "writing entry " . $args{'index'} . "/" .
193             $args{'text'} . "/" . $args{'number'});
194            
195 0 0         if ($args{'index'} ne "") {
196 0 0         if ($self->pb_storage_ok >= 2) {
197 0 0 0       if (($args{'index'} < $self->{pb_storage_min}) or
198             ($args{'index'} > $self->{pb_storage_max})) {
199 0           carp "Index should be between " .
200             $self->{pb_storage_min} . " and " .
201             $self->{pb_storage_max};
202 0           return undef;
203             }
204             } else {
205 0           $self->log->write('warning',
206             "index specified but storage spec unavailable ... will try" .
207             " but might fail !");
208             }
209             }
210 0           my $type;
211 0 0         if ($args{'number'} =~ /^\+(.*)$/) {
212             # international format phone number
213 0           $args{'number'} = $1;
214 0           $type = 145;
215             } else {
216             # not international
217 0           $type = 129;
218             }
219 0 0         if (length($args{'number'}) > $self->{pb_storage_nlength}) {
220 0           carp "Number too long, max is " . $self->{pb_storage_nlength};
221 0           return undef;
222             }
223 0 0         if (length($args{'text'}) > $self->{pb_storage_tlength}) {
224 0           carp "Text too long, max is " . $self->{pb_storage_tlength};
225 0           return undef;
226             }
227 0           my $atcmd =
228             'AT+CPBW=' .
229             $args{'index'} . "," .
230             '"' . $args{'number'} . '",' .
231             $type . "," .
232             '"' . $args{'text'} . '"';
233 0           $self->log->write('info', $atcmd);
234 0           $self->atsend(
235             $atcmd .
236             Device::Modem::CR
237             );
238 0           my ($result, @lines) = $self->parse_answer();
239 0 0         if ($result ne 'OK') {
240 0           $self->log->write('error', "Couldn't write phonebook entry");
241 0           return undef;
242             }
243 0           return 1;
244             }
245              
246             =head2 pb_erase
247              
248             =over 4
249              
250             This method will erase the entry at the specified index of the storage
251              
252             Ex :
253             $gsm->pb_erase(10);
254              
255             =back
256              
257             =cut
258              
259             sub pb_erase {
260 0     0 1   my $self = shift;
261 0           my $idx = shift;
262              
263 0 0         if ($self->pb_storage_ok > 1) {
264 0 0 0       if ($idx < $self->{pb_storage_min} or $idx > $self->{pb_storage_max}) {
265 0           carp("index out of bounds");
266 0           return undef;
267             }
268             }
269 0           $self->pb_write_entry(index => $idx, text => "", number => "");
270             }
271              
272             =head2 pb_erase_all
273              
274             =over 4
275              
276             This method will clear the whole phonebook for the used storage. Handle with care !
277              
278             Ex :
279             $gsm->pb_erase_all;
280              
281             =back
282              
283             =cut
284              
285             sub pb_erase_all {
286 0     0 1   my $self = shift;
287              
288 0 0         if ($self->pb_storage_ok < 2) {
289 0           carp("Storage spec unavailable");
290 0           return undef;
291             }
292 0           for (my $i = $self->{pb_storage_min};
293             $i <= $self->{pb_storage_max}; $i++) {
294 0           $self->pb_write_entry(index => $i, text => "", number => "");
295             }
296             }
297              
298             =head2 pb_read_entries
299              
300             =over 4
301              
302             This method will fetch the specified entries in the phonebook storage and return them in a reference to an array. Each cell of the array is a reference to a hash holding the information.
303              
304             Ex :
305              
306             my $entries = $gsm->pb_read_entries(1,10);
307              
308             foreach (@$entries) {
309             print $_->{index}, ':', $_->{text}, ':', $_->{number}, "\n";
310             }
311              
312             With 2 arguments, the arguments are interpreted as an index range and entries inside of this range are returned.
313              
314             With 1 argument, the argument is interpreted as an index and only this entry is returned.
315              
316             =back
317              
318             =cut
319              
320             sub pb_read_entries {
321 0     0 1   my $self = shift;
322 0           my $idx = shift;
323 0           my $idx2 = shift;
324              
325 0 0         if ($self->pb_storage_ok < 2) {
326 0           $self->log->write('warning',
327             "Storage specs unavailable... will try, but might fail");
328             }
329 0 0 0       if ($idx < $self->{pb_storage_min} or
      0        
      0        
      0        
330             $idx > $self->{pb_storage_max} or
331             (defined($idx2) and
332             ($idx2 < $self->{pb_storage_min} or
333             $idx2 > $self->{pb_storage_max}))) {
334 0           $self->log->write('error', "Index out of bound");
335 0           return undef;
336             }
337              
338 0           my $atcmd = "AT+CPBR=$idx";
339 0 0         if (defined $idx2) {
340 0           $atcmd .= ("," . $idx2);
341             }
342 0           $atcmd .= Device::Modem::CR;
343 0           $self->atsend($atcmd);
344             # timeout of 10sec to leave time to get all the data, or till
345             # OK or ERROR comes back
346 0           my ($result, @lines) = $self->parse_answer(qr/OK|ERROR/, 10000);
347 0           my $entries = [];
348 0           foreach my $line (@lines) {
349 0           my ($type, $number, $text);
350 0 0         next if ($line =~ /^$/);
351 0 0         if ($line =~ /^\+CPBR:\s+([0-9]+),\"([0-9]*)\",([0-9]+),\"(.*)\"$/) {
352 0           $idx = $1;
353 0           $number = $2;
354 0           $type = $3;
355 0           $text = $4;
356             } else {
357 0           $self->log->write('warning',
358             "Phonebook entry is ill formated, got " .
359             $lines[0]);
360             }
361 0 0         if ($type == 145) {
362 0           $number = '+' . $number;
363             }
364 0           push @$entries,
365             { 'number' => $number, 'text' => $text, '$index' => $idx };
366             }
367 0           return $entries;
368             }
369              
370             =head2 pb_read_all
371              
372             =over 4
373              
374             This is equivalent to a pb_read_entry where the range extends from the beginning of the phonebook storage to its end.
375              
376             =back
377              
378             =cut
379              
380             sub pb_read_all {
381 0     0 1   my $self = shift;
382              
383 0 0         if ($self->pb_storage_ok < 2) {
384 0           $self->log->write('error', 'Storage spec were not retrieved, ' .
385             'unable to perform this operation');
386 0           return undef;
387             }
388 0           return $self->pb_read_entries(
389             $self->{pb_storage_min},
390             $self->{pb_storage_max}
391             );
392             }
393              
394             =head2 sms_send
395              
396             =over 4
397              
398             This method will let you send an SMS to the specified phone number
399              
400             Ex :
401              
402             $gsm->sms_send("+33123456", "Message to send as an SMS");
403              
404             =back
405              
406             =cut
407              
408             sub sms_send {
409 0     0 1   my $self = shift;
410 0           my $number = shift;
411 0           my $sms = shift;
412              
413             # sets the SMS format to TEXT instead of default PDU
414 0           my $atcmd = "AT+CMGF=1" . Device::Modem::CR;
415 0           $self->atsend($atcmd);
416 0           my ($result, @lines) = $self->parse_answer;
417              
418 0 0         if ($result ne 'OK') {
419 0           carp('Failed to set SMS format to text');
420 0           return undef;
421             }
422 0           $atcmd = "AT+CMGS=\"".$number."\"".Device::Modem::CR;
423 0           $self->atsend($atcmd);
424 0           $result = $self->answer; # to collect the > sign
425 0           $atcmd = $sms . chr(26); # ^Z terminated string
426 0           $self->atsend($atcmd);
427 0           ($result, @lines) = $self->parse_answer(qr/OK|ERROR/, 10000);;
428 0 0         if ($result ne "OK") {
429 0           carp('Unable to send SMS');
430 0           return undef;
431             }
432 0           return 1;
433             }
434              
435              
436             1;
437              
438             =head1 SUPPORT
439              
440             Feel free to contact me at my email skattoor@cpan.org for questions or suggestions.
441              
442             =head1 AUTHOR
443              
444             Stephane KATTOOR, skattoor@cpan.org
445              
446             =head1 COPYRIGHT
447              
448             (c) 2007, Stephane KATTOOR, skattoor@cpan.org
449              
450             This library is free software; you can only redistribute it and/or modify it under the same terms as Perl itself.
451              
452             =head1 SEE ALSO
453              
454             Device::Modem
455              
456             =cut
457              
458              
459             # vim:ts=4:sw=4: