adammck-rubygsm 0.3.1 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,9 +8,9 @@ require "#{dir}/../lib/rubygsm.rb"
8
8
 
9
9
 
10
10
  begin
11
- modem = GsmModem.new
11
+ modem = Gsm::Modem.new
12
12
 
13
- rescue GsmModem::Error => err
13
+ rescue Gsm::Modem::Error => err
14
14
  puts "Initialization Error:"
15
15
  puts " " + err.desc
16
16
  exit
data/bin/sms ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: noet
3
+
4
+ dir = File.dirname(__FILE__)
5
+ require "#{dir}/../lib/rubygsm.rb"
6
+
7
+
8
+ # expand args (TODO: optparser,
9
+ # which we need so very badly)
10
+ # or fail with USAGE message
11
+ if (ARGV.length == 2)
12
+ recipient, msg = *ARGV
13
+ port = :auto
14
+
15
+ elsif (ARGV.length == 3)
16
+ port, recipient, msg = *ARGV
17
+
18
+ else
19
+ puts "Usage: sms [PORT] RECIPIENT MESSAGE"
20
+ puts "(don't forget to quote the message)"
21
+ puts
22
+ puts "Examples:"
23
+ puts ' sms +13474201234 Hello'
24
+ puts ' sms /dev/ttyS0 +13474201234 "Hello from RubyGSM"'
25
+ exit
26
+ end
27
+
28
+ # initialize the modem, send the sms, and
29
+ # terminate. currently, rubygsm does a lot
30
+ # of things that aren't strictly required
31
+ # to get this done; maybe refactor
32
+ begin
33
+ modem = Gsm::Modem.new(port)
34
+ modem.send_sms!(recipient, msg)
35
+
36
+ rescue Gsm::Error => err
37
+ puts "Error: #{err.desc}"
38
+ end
@@ -5,3 +5,14 @@ dir = File.dirname(__FILE__)
5
5
  require "#{dir}/rubygsm/core.rb"
6
6
  require "#{dir}/rubygsm/errors.rb"
7
7
  require "#{dir}/rubygsm/log.rb"
8
+
9
+ # messages are now passed around
10
+ # using objects, rather than flat
11
+ # arguments (from, time, msg, etc)
12
+ require "#{dir}/rubygsm/msg/incoming.rb"
13
+ require "#{dir}/rubygsm/msg/outgoing.rb"
14
+
15
+ # during development, it's important to EXPLODE
16
+ # as early as possible when something goes wrong
17
+ Thread.abort_on_exception = true
18
+ Thread.current["name"] = "main"
@@ -14,8 +14,8 @@ require "date.rb"
14
14
  require "rubygems"
15
15
  require "serialport"
16
16
 
17
-
18
- class GsmModem
17
+ module Gsm
18
+ class Modem
19
19
  include Timeout
20
20
 
21
21
 
@@ -23,7 +23,7 @@ class GsmModem
23
23
  attr_reader :device, :port
24
24
 
25
25
  # call-seq:
26
- # GsmModem.new(port, verbosity=:warn)
26
+ # Gsm::Modem.new(port, verbosity=:warn)
27
27
  #
28
28
  # Create a new instance, to initialize and communicate exclusively with a
29
29
  # single modem device via the _port_ (which is usually either /dev/ttyS0
@@ -76,12 +76,8 @@ class GsmModem
76
76
  # the last part is delivered
77
77
  @multipart = {}
78
78
 
79
- # (re-) open the full log file
80
- @log = File.new "rubygsm.log", "w"
81
-
82
- # initialization message (yes, it's underlined)
83
- msg = "RubyGSM Initialized at: #{Time.now}"
84
- log msg + "\n" + ("=" * msg.length), :file
79
+ # start logging to file
80
+ log_init
85
81
 
86
82
  # to store incoming messages
87
83
  # until they're dealt with by
@@ -144,8 +140,18 @@ class GsmModem
144
140
  # notify the network that we accepted
145
141
  # the incoming message (for read receipt)
146
142
  # BEFORE pushing it to the incoming queue
147
- # (to avoid really ugly race condition)
148
- command "AT+CNMA"
143
+ # (to avoid really ugly race condition if
144
+ # the message is grabbed from the queue
145
+ # and responded to quickly, before we get
146
+ # a chance to issue at+cnma)
147
+ begin
148
+ command "AT+CNMA"
149
+
150
+ # not terribly important if it
151
+ # fails, even though it shouldn't
152
+ rescue Gsm::Error
153
+ log "Receipt acknowledgement (CNMA) was rejected"
154
+ end
149
155
 
150
156
  # we might abort if this is
151
157
  catch :skip_processing do
@@ -184,8 +190,8 @@ class GsmModem
184
190
  # store the incoming data to be picked up
185
191
  # from the attr_accessor as a tuple (this
186
192
  # is kind of ghetto, and WILL change later)
187
- dt = parse_incoming_timestamp(timestamp)
188
- @incoming.push [from, dt, msg]
193
+ sent = parse_incoming_timestamp(timestamp)
194
+ @incoming.push Gsm::Incoming.new(self, from, sent, msg)
189
195
  end
190
196
 
191
197
  # drop the two CMT lines (meta-info and message),
@@ -211,7 +217,7 @@ class GsmModem
211
217
  # which probably means that it has
212
218
  # crashed or been unplugged
213
219
  rescue Errno::EIO
214
- raise GsmModem::WriteError
220
+ raise Gsm::WriteError
215
221
  end
216
222
  end
217
223
 
@@ -235,7 +241,7 @@ class GsmModem
235
241
 
236
242
  # die if we couldn't read
237
243
  # (nil signifies an error)
238
- raise GsmModem::ReadError\
244
+ raise Gsm::ReadError\
239
245
  if char.nil?
240
246
 
241
247
  # convert the character to ascii,
@@ -346,13 +352,13 @@ class GsmModem
346
352
  # some errors contain useful error codes,
347
353
  # so raise a proper error with a description
348
354
  if m = buf.match(/^\+(CM[ES]) ERROR: (\d+)$/)
349
- log_then_decr "!! Raising GsmModem::Error #{$1} #{$2}"
355
+ log_then_decr "!! Raising Gsm::Error #{$1} #{$2}"
350
356
  raise Error.new(*m.captures)
351
357
  end
352
358
 
353
359
  # some errors are not so useful :|
354
360
  if buf == "ERROR"
355
- log_then_decr "!! Raising GsmModem::Error"
361
+ log_then_decr "!! Raising Gsm::Error"
356
362
  raise Error
357
363
  end
358
364
 
@@ -380,7 +386,9 @@ class GsmModem
380
386
  begin
381
387
 
382
388
  # prevent other threads from issuing
383
- # commands while this block is working
389
+ # commands TO THIS MODDEM while this
390
+ # block is working. this does not lock
391
+ # threads, just the gsm device
384
392
  if @locked_to and (@locked_to != Thread.current)
385
393
  log "Locked by #{@locked_to["name"]}, waiting..."
386
394
 
@@ -404,7 +412,7 @@ class GsmModem
404
412
 
405
413
  # something went bang, which happens, but
406
414
  # just pass it on (after unlocking...)
407
- rescue GsmModem::Error
415
+ rescue Gsm::Error
408
416
  raise
409
417
 
410
418
 
@@ -514,10 +522,10 @@ class GsmModem
514
522
  # Sets the band currently selected for use
515
523
  # by the modem, using either a literal band
516
524
  # number (passed directly to the modem, see
517
- # GsmModem.Bands) or a named area from
518
- # GsmModem.BandAreas:
525
+ # Gsm::Modem.Bands) or a named area from
526
+ # Gsm::Modem.BandAreas:
519
527
  #
520
- # m = GsmModem.new
528
+ # m = Gsm::Modem.new
521
529
  # m.band = :usa => "850/1900"
522
530
  # m.band = :africa => "900E/1800"
523
531
  # m.band = :monkey => ArgumentError
@@ -526,7 +534,7 @@ class GsmModem
526
534
  # America is wearing its ass backwards.)
527
535
  #
528
536
  # Raises ArgumentError if an unrecognized band was
529
- # given, or raises GsmModem::Error if the modem does
537
+ # given, or raises Gsm::Error if the modem does
530
538
  # not support the given band.
531
539
  def band=(new_band)
532
540
 
@@ -548,7 +556,7 @@ class GsmModem
548
556
 
549
557
  # set the band right now (second wmbs
550
558
  # argument is: 0=NEXT-BOOT, 1=NOW). if it
551
- # fails, allowGsmModem::Error to propagate
559
+ # fails, allow Gsm::Error to propagate
552
560
  command("AT+WMBS=#{new_band},1")
553
561
  end
554
562
 
@@ -576,7 +584,7 @@ class GsmModem
576
584
 
577
585
  # if the command failed, then
578
586
  # the pin was not accepted
579
- rescue GsmModem::Error
587
+ rescue Gsm::Error
580
588
  return false
581
589
  end
582
590
  end
@@ -631,24 +639,70 @@ class GsmModem
631
639
 
632
640
 
633
641
  # call-seq:
634
- # send(recipient, message) => true or false
642
+ # send_sms(message) => true or false
643
+ # send_sms(recipient, text) => true or false
644
+ #
645
+ # Sends an SMS message via _send_sms!_, but traps
646
+ # any exceptions raised, and returns false instead.
647
+ # Use this when you don't really care if the message
648
+ # was sent, which is... never.
649
+ def send_sms(*args)
650
+ begin
651
+ send_sms!(*args)
652
+ return true
653
+
654
+ # something went wrong
655
+ rescue Gsm::Error
656
+ return false
657
+ end
658
+ end
659
+
660
+
661
+ # call-seq:
662
+ # send_sms!(message) => true or raises Gsm::Error
663
+ # send_sms!(receipt, text) => true or raises Gsm::Error
635
664
  #
636
665
  # Sends an SMS message, and returns true if the network
637
666
  # accepted it for delivery. We currently can't handle read
638
- # receipts, so have no way of confirming delivery.
667
+ # receipts, so have no way of confirming delivery. If the
668
+ # device or network rejects the message, a Gsm::Error is
669
+ # raised containing (hopefully) information about what went
670
+ # wrong.
639
671
  #
640
672
  # Note: the recipient is passed directly to the modem, which
641
673
  # in turn passes it straight to the SMSC (sms message center).
642
- # for maximum compatibility, use phone numbers in international
674
+ # For maximum compatibility, use phone numbers in international
643
675
  # format, including the *plus* and *country code*.
644
- def send(to, msg)
676
+ def send_sms!(*args)
677
+
678
+ # extract values from Outgoing object.
679
+ # for now, this does not offer anything
680
+ # in addition to the recipient/text pair,
681
+ # but provides an upgrade path for future
682
+ # features (like FLASH and VALIDITY TIME)
683
+ if args.length == 1\
684
+ and args[0].is_a? Gsm::Outgoing
685
+ to = args[0].recipient
686
+ msg = args[0].text
687
+
688
+ # the < v0.4 arguments. maybe
689
+ # deprecate this one day
690
+ elsif args.length == 2
691
+ to, msg = *args
692
+
693
+ else
694
+ raise ArgumentError,\
695
+ "The Gsm::Modem#send_sms method accepts" +\
696
+ "a single Gsm::Outgoing instance, " +\
697
+ "or recipient and text strings"
698
+ end
645
699
 
646
700
  # the number must be in the international
647
701
  # format for some SMSCs (notably, the one
648
702
  # i'm on right now) so maybe add a PLUS
649
703
  #to = "+#{to}" unless(to[0,1]=="+")
650
704
 
651
- # 1..9 is a special number which does not
705
+ # 1..9 is a special number which does notm
652
706
  # result in a real sms being sent (see inject.rb)
653
707
  if to == "+123456789"
654
708
  log "Not sending test message: #{msg}"
@@ -677,12 +731,16 @@ class GsmModem
677
731
  # an escpae, to... escape
678
732
  rescue Exception, Timeout::Error => err
679
733
  log "Rescued #{err.desc}"
680
- return false
681
- #write 27.chr
682
- #wait
734
+ write 27.chr
735
+
736
+ # allow the error to propagate,
737
+ # so the application can catch
738
+ # it for more useful info
739
+ raise
740
+
741
+ ensure
742
+ log_decr
683
743
  end
684
-
685
- log_decr
686
744
  end
687
745
 
688
746
  # if no error was raised,
@@ -699,16 +757,16 @@ class GsmModem
699
757
  # for each.
700
758
  #
701
759
  # class Receiver
702
- # def incoming(caller, datetime, message)
703
- # puts "From #{caller} at #{datetime}:", message
760
+ # def incoming(msg)
761
+ # puts "From #{msg.from} at #{msg.sent}:", msg.text
704
762
  # end
705
763
  # end
706
764
  #
707
765
  # # create the instances,
708
766
  # # and start receiving
709
767
  # rcv = Receiver.new
710
- # m = GsmModem.new "/dev/ttyS0"
711
- # m.receive inst.method :incoming
768
+ # m = Gsm::Modem.new "/dev/ttyS0"
769
+ # m.receive rcv.method :incoming
712
770
  #
713
771
  # # block until ctrl+c
714
772
  # while(true) { sleep 2 }
@@ -738,10 +796,10 @@ class GsmModem
738
796
  # in the same format that they were built
739
797
  # back in _parse_incoming_sms!_
740
798
  unless @incoming.empty?
741
- @incoming.each do |inc|
799
+ @incoming.each do |msg|
742
800
  begin
743
- callback.call *inc
744
-
801
+ callback.call(msg)
802
+
745
803
  rescue StandardError => err
746
804
  log "Error in callback: #{err}"
747
805
  end
@@ -764,4 +822,5 @@ class GsmModem
764
822
  # threaded (like debugging handsets)
765
823
  @thr.join if join_thread
766
824
  end
767
- end
825
+ end # Modem
826
+ end # Gsm
@@ -4,7 +4,7 @@
4
4
  # vim: noet
5
5
  #++
6
6
 
7
- class GsmModem
7
+ module Gsm
8
8
  class Error < StandardError
9
9
  ERRORS = {
10
10
  "CME" => {
@@ -94,7 +94,11 @@ class GsmModem
94
94
  "[type=#{@type}] [code=#{code}]"
95
95
  end
96
96
 
97
- alias :to_s :desc
97
+ # not the same as alias :to_s, :desc,
98
+ # because this works on subclasses
99
+ def to_s
100
+ desc
101
+ end
98
102
  end
99
103
 
100
104
  # TODO: what the hell is going on with
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
  # vim: noet
3
3
 
4
- class GsmModem
4
+ module Gsm
5
+ class Modem
5
6
  private
6
7
 
7
- # Symbols accepted by the GsmModem.new _verbosity_
8
+ # Symbols accepted by the Gsm::Modem.new _verbosity_
8
9
  # argument. Each level includes all of the levels
9
10
  # below it (ie. :debug includes all :warn messages)
10
11
  LOG_LEVELS = {
@@ -14,6 +15,25 @@ class GsmModem
14
15
  :warn => 2,
15
16
  :error => 1 }
16
17
 
18
+ def log_init
19
+
20
+ fn_port = File.basename(@port)
21
+ fn_time = Time.now.strftime("%Y-%m-%d.%H-%M-%S")
22
+
23
+ # (re-) open the full log file
24
+ filename = "rubygsm.#{fn_port}.#{fn_time}"
25
+ @log = File.new filename, "w"
26
+
27
+ # dump some useful information
28
+ # at the top, for debugging
29
+ log "RUBYGSM"
30
+ log " port: #{@port}"
31
+ log " timeout: #{@read_timeout}"
32
+ log " verbosity: #{@verbosity}"
33
+ log " started at: #{Time.now}"
34
+ log "===="
35
+ end
36
+
17
37
  def log(msg, level=:debug)
18
38
  ind = " " * (@log_indents[Thread.current] or 0)
19
39
 
@@ -36,7 +56,6 @@ class GsmModem
36
56
  end
37
57
  end
38
58
 
39
-
40
59
  # log a message, and increment future messages
41
60
  # in this thread. useful for nesting logic
42
61
  def log_incr(*args)
@@ -56,4 +75,5 @@ class GsmModem
56
75
  log(*args)
57
76
  log_decr
58
77
  end
59
- end
78
+ end # Modem
79
+ end # Gsm
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: noet
3
+
4
+ module Gsm
5
+ class Incoming
6
+ attr_reader :device, :sender, :sent, :received, :text
7
+
8
+ def initialize(device, sender, sent, text)
9
+
10
+ # move all arguments into read-only
11
+ # attributes. ugly, but Struct only
12
+ # supports read/write attrs
13
+ @device = device
14
+ @sender = sender
15
+ @sent = sent
16
+ @text = text
17
+
18
+ # assume that the message was
19
+ # received right now, since we
20
+ # don't have an incoming buffer
21
+ @received = Time.now
22
+ end
23
+
24
+ # Returns the sender of this message,
25
+ # so incoming and outgoing messages
26
+ # can be logged in the same way.
27
+ def number
28
+ sender
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: noet
3
+
4
+ module Gsm
5
+ class Outgoing
6
+ attr_accessor :recipient, :text
7
+ attr_reader :device, :sent
8
+
9
+ def initialize(device, recipient=nil, text=nil)
10
+
11
+ # check that the device is
12
+ #raise ArgumentError, "Invalid device"\
13
+ # unless device.respond_to?(:send_sms)
14
+
15
+ # init the instance vars
16
+ @recipient = recipient
17
+ @device = device
18
+ @text = text
19
+ end
20
+
21
+ def send!
22
+ @device.send_sms(self)
23
+ @sent = Time.now
24
+
25
+ # once sent, allow no
26
+ # more modifications
27
+ freeze
28
+ end
29
+
30
+ # Returns the recipient of this message,
31
+ # so incoming and outgoing messages
32
+ # can be logged in the same way.
33
+ def number
34
+ recipient
35
+ end
36
+ end
37
+ end
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "rubygsm"
3
- s.version = "0.3.1"
4
- s.date = "2009-01-09"
3
+ s.version = "0.4"
4
+ s.date = "2009-01-29"
5
5
  s.summary = "Send and receive SMS with a GSM modem"
6
6
  s.email = "adam.mckaig@gmail.com"
7
7
  s.homepage = "http://github.com/adammck/rubygsm"
@@ -15,11 +15,15 @@ Gem::Specification.new do |s|
15
15
  "lib/rubygsm/core.rb",
16
16
  "lib/rubygsm/errors.rb",
17
17
  "lib/rubygsm/log.rb",
18
- "bin/gsm-modem-band"
18
+ "lib/rubygsm/msg/incoming.rb",
19
+ "lib/rubygsm/msg/outgoing.rb",
20
+ "bin/gsm-modem-band",
21
+ "bin/gsm-app-monitor"
19
22
  ]
20
23
 
21
24
  s.executables = [
22
- "gsm-modem-band"
25
+ "gsm-modem-band",
26
+ "sms"
23
27
  ]
24
28
 
25
29
  s.add_dependency("toholio-serialport", ["> 0.7.1"])
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adammck-rubygsm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: "0.4"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Mckaig
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-01-09 00:00:00 -08:00
12
+ date: 2009-01-29 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -25,6 +25,7 @@ description:
25
25
  email: adam.mckaig@gmail.com
26
26
  executables:
27
27
  - gsm-modem-band
28
+ - sms
28
29
  extensions: []
29
30
 
30
31
  extra_rdoc_files: []
@@ -36,7 +37,10 @@ files:
36
37
  - lib/rubygsm/core.rb
37
38
  - lib/rubygsm/errors.rb
38
39
  - lib/rubygsm/log.rb
40
+ - lib/rubygsm/msg/incoming.rb
41
+ - lib/rubygsm/msg/outgoing.rb
39
42
  - bin/gsm-modem-band
43
+ - bin/gsm-app-monitor
40
44
  has_rdoc: true
41
45
  homepage: http://github.com/adammck/rubygsm
42
46
  post_install_message: