Esempio n. 1
0
    def connect(self, reconnect=False):
        """Creates the connection to the modem via pySerial, optionally
           killing and re-creating any existing connection."""
           
        self._log("Connecting")
        
        # if no connection exists, create it
        # the reconnect flag is irrelevant
        if not hasattr(self, "device") or (self.device is None):
            with self.modem_lock:
                if 'rtscts' in self.device_kwargs:
                    self.device_kwargs['rtscts'] = int(self.device_kwargs['rtscts'])
                self.device = DeviceWrapper(
                    self.logger, *self.device_args,
                    **self.device_kwargs)
                
        # the port already exists, but if we're
        # reconnecting, then kill it and recurse
        # to recreate it. this is useful when the
        # connection has died, but nobody noticed
        elif reconnect:
            
            self.disconnect()
            self.connect(False)

        return self.device
Esempio n. 2
0
    def connect(self, reconnect=False):
        """Creates the connection to the modem via pySerial, optionally
           killing and re-creating any existing connection."""

        self._log("Connecting")

        # if no connection exists, create it
        # the reconnect flag is irrelevant
        if not hasattr(self, "device") or (self.device is None):
            with self.modem_lock:
                self.device = DeviceWrapper(self.logger, *self.device_args,
                                            **self.device_kwargs)

        # the port already exists, but if we're
        # reconnecting, then kill it and recurse
        # to recreate it. this is useful when the
        # connection has died, but nobody noticed
        elif reconnect:

            self.disconnect()
            self.connect(False)

        return self.device
Esempio n. 3
0
class GsmModem(object):
    """pyGSM is a Python module which uses pySerial to provide a nifty
       interface to send and receive SMS via a GSM Modem. It was ported
       from RubyGSM, and provides (almost) all of the same features. It's
       easy to get started:

          # create a GsmModem object:
          >>> import pygsm
          >>> modem = pygsm.GsmModem(port="/dev/ttyUSB0")

          # harass Evan over SMS:
          # (try to do this before 11AM)
          >>> modem.send_sms("+13364130840", "Hey, wake up!")

          # check for incoming SMS:
          >>> print modem.next_message()
          <pygsm.IncomingMessage from +13364130840: "Leave me alone!">


       There are various ways of polling for incoming messages -- a choice
       which has been deliberately left to the application author (unlike
       RubyGSM). Execute `python -m pygsm.gsmmodem` to run this example:

          # connect to the modem
          modem = pygsm.GsmModem(port=sys.argv[1])

          # check for new messages every two
          # seconds for the rest of forever
          while True:
              msg = modem.next_message()

              # we got a message! respond with
              # something useless, as an example
              if msg is not None:
                  msg.respond("Thanks for those %d characters!" %
                      len(msg.text))

              # no messages? wait a couple
              # of seconds and try again
              else: time.sleep(2)


       pyGSM is distributed via GitHub:
       http://github.com/adammck/pygsm

       Bugs reports (especially for
       unsupported devices) are welcome:
       http://github.com/adammck/pygsm/issues"""

    # override these after init, and
    # before boot. they're not sanity
    # checked, so go crazy.
    cmd_delay = 0.1
    retry_delay = 2
    max_retries = 10
    modem_lock = threading.RLock()

    def __init__(self, *args, **kwargs):
        """Creates, connects to, and boots a GSM Modem. All of the arguments
           are optional (although "port=" should almost always be provided),
           and passed along to serial.Serial.__init__ verbatim. For all of
           the possible configration options, see:

           http://pyserial.wiki.sourceforge.net/pySerial#tocpySerial10

           Alternatively, a single "device" kwarg can be passed, which overrides
           the default proxy-args-to-pySerial behavior. This is useful when testing,
           or wrapping the serial connection with some custom logic."""

        if "logger" in kwargs:
            self.logger = kwargs.pop("logger")

        mode = "PDU"
        if "mode" in kwargs:
            mode = kwargs.pop("mode")

        # if a ready-made device was provided, store it -- self.connect
        # will see that we're already connected, and do nothing. we'll
        # just assume it quacks like a serial port
        if "device" in kwargs:
            self.device = kwargs.pop("device")

            # if a device is given, the other args are never
            # used, so were probably included by mistake.
            if len(args) or len(kwargs):
                raise (TypeError(
                    "__init__() does not accept other arguments when a 'device' is given"
                ))

        # for regular serial connections, store the connection args, since
        # we might need to recreate the serial connection again later
        else:
            self.device_args = args
            self.device_kwargs = kwargs

        # to cache parts of multi-part messages
        # until the last part is delivered
        self.multipart = {}

        # to store unhandled incoming messages
        self.incoming_queue = []

        if mode.lower() == "text":
            self.smshandler = TextSmsHandler(self)
        else:
            self.smshandler = PduSmsHandler(self)
        # boot the device on init, to fail as
        # early as possible if it can't be opened
        self.boot()

    LOG_LEVELS = {
        "traffic": 4,
        "read": 4,
        "write": 4,
        "debug": 3,
        "warn": 2,
        "error": 1
    }

    def _log(self, str_, type_="debug"):
        """Proxies a log message to this Modem's logger, if one has been set.
           This is useful for applications embedding pyGSM that wish to show
           or log what's going on inside.

           The *logger* should be a function with three arguments:
             modem:   a reference to this GsmModem instance
             message: the log message (a unicode string)
             type:    a string contaning one of the keys
                      of GsmModem.LOG_LEVELS, indicating
                      the importance of this message.

           GsmModem.__init__ accepts an optional "logger" kwarg, and a minimal
           (dump to STDOUT) logger is available at GsmModem.logger:

           >>> GsmModem("/dev/ttyUSB0", logger=GsmModem.logger)"""

        if hasattr(self, "logger"):
            self.logger(self, str_, type_)

    @staticmethod
    def logger(_modem, message_, type_):
        print "%8s %s" % (type_, message_)

    def connect(self, reconnect=False):
        """Creates the connection to the modem via pySerial, optionally
           killing and re-creating any existing connection."""

        self._log("Connecting")

        # if no connection exists, create it
        # the reconnect flag is irrelevant
        if not hasattr(self, "device") or (self.device is None):
            with self.modem_lock:
                self.device = DeviceWrapper(self.logger, *self.device_args,
                                            **self.device_kwargs)

        # the port already exists, but if we're
        # reconnecting, then kill it and recurse
        # to recreate it. this is useful when the
        # connection has died, but nobody noticed
        elif reconnect:

            self.disconnect()
            self.connect(False)

        return self.device

    def disconnect(self):
        """Disconnects from the modem."""

        self._log("Disconnecting")

        # attempt to close and destroy the device
        if hasattr(self, "device") and (self.device is None):
            with self.modem_lock:
                if self.device.isOpen():
                    self.device.close()
                    self.device = None
                    return True

        # for some reason, the device
        # couldn't be closed. it probably
        # just isn't open yet
        return False

    def set_modem_config(self):
        """initialize the modem configuration with settings needed to process
           commands and send/receive SMS.
        """

        # set some sensible defaults, to make
        # the various modems more consistant
        self.command("ATE0", raise_errors=False)  # echo off
        self.command("AT+CMEE=1", raise_errors=False)  # useful error messages
        self.command("AT+WIND=0", raise_errors=False)  # disable notifications
        self.command("AT+CSMS=1",
                     raise_errors=False)  # set SMS mode to phase 2+
        self.command(self.smshandler.get_mode_cmd())  # make sure in PDU mode

        # enable new message notification
        self.command("AT+CNMI=2,2,0,0,0", raise_errors=False)

    def boot(self, reboot=False):
        """Initializes the modem. Must be called after init and connect,
           but before doing anything that expects the modem to be ready."""

        self._log("Booting")

        if reboot:
            # If reboot==True, force a reconnection and full modem reset. SLOW
            self.connect(reconnect=True)
            self.command("AT+CFUN=1")
        else:
            # else just verify connection
            self.connect()

        # In both cases, reset the modem's config
        self.set_modem_config()

        # And check for any waiting messages PRIOR to setting
        # the CNMI call--this is not supported by all modems--
        # in which case we catch the exception and plow onward
        try:
            self._fetch_stored_messages()
        except errors.GsmError:
            pass

    def reboot(self):
        """Forces a reconnect to the serial port and then a full modem reset to factory
           and reconnect to GSM network. SLOW.
        """
        self.boot(reboot=True)

    def _write(self, str):
        """Write a string to the modem."""

        self._log(repr(str), "write")

        try:
            self.device.write(str)

        # if the device couldn't be written to,
        # wrap the error in something that can
        # sensibly be caught at a higher level
        except OSError, err:
            raise (errors.GsmWriteError)
Esempio n. 4
0
class GsmModem(object):
    """pyGSM is a Python module which uses pySerial to provide a nifty
       interface to send and receive SMS via a GSM Modem. It was ported
       from RubyGSM, and provides (almost) all of the same features. It's
       easy to get started:

          # create a GsmModem object:
          >>> import pygsm
          >>> modem = pygsm.GsmModem(port="/dev/ttyUSB0")

          # harass Evan over SMS:
          # (try to do this before 11AM)
          >>> modem.send_sms("+13364130840", "Hey, wake up!")

          # check for incoming SMS:
          >>> print modem.next_message()
          <pygsm.IncomingMessage from +13364130840: "Leave me alone!">


       There are various ways of polling for incoming messages -- a choice
       which has been deliberately left to the application author (unlike
       RubyGSM). Execute `python -m pygsm.gsmmodem` to run this example:

          # connect to the modem
          modem = pygsm.GsmModem(port=sys.argv[1])

          # check for new messages every two
          # seconds for the rest of forever
          while True:
              msg = modem.next_message()

              # we got a message! respond with
              # something useless, as an example
              if msg is not None:
                  msg.respond("Thanks for those %d characters!" %
                      len(msg.text))

              # no messages? wait a couple
              # of seconds and try again
              else: time.sleep(2)


       pyGSM is distributed via GitHub:
       http://github.com/adammck/pygsm

       Bugs reports (especially for
       unsupported devices) are welcome:
       http://github.com/adammck/pygsm/issues"""


    # override these after init, and
    # before boot. they're not sanity
    # checked, so go crazy.
    cmd_delay = 0.1
    retry_delay = 2
    max_retries = 10
    modem_lock = threading.RLock()
    modem_type = ""
    
    def __init__(self, *args, **kwargs):
        """Creates, connects to, and boots a GSM Modem. All of the arguments
           are optional (although "port=" should almost always be provided),
           and passed along to serial.Serial.__init__ verbatim. For all of
           the possible configration options, see:

           http://pyserial.wiki.sourceforge.net/pySerial#tocpySerial10

           Alternatively, a single "device" kwarg can be passed, which overrides
           the default proxy-args-to-pySerial behavior. This is useful when testing,
           or wrapping the serial connection with some custom logic."""

        if "logger" in kwargs:
            self.logger = kwargs.pop("logger")
        
        mode = "PDU"
        if "mode" in kwargs:
            mode = kwargs.pop("mode")
        
        # if a ready-made device was provided, store it -- self.connect
        # will see that we're already connected, and do nothing. we'll
        # just assume it quacks like a serial port
        if "device" in kwargs:
            self.device = kwargs.pop("device")

            # if a device is given, the other args are never
            # used, so were probably included by mistake.
            if len(args) or len(kwargs):
                raise(TypeError("__init__() does not accept other arguments when a 'device' is given"))

        # for regular serial connections, store the connection args, since
        # we might need to recreate the serial connection again later
        else:
            self.device_args = args
            self.device_kwargs = kwargs

        # to cache parts of multi-part messages
        # until the last part is delivered
        self.multipart = {}

        # to store unhandled incoming messages
        self.incoming_queue = []
        
        if mode.lower() == "text":
            self.smshandler = TextSmsHandler(self)
        else:
            self.smshandler = PduSmsHandler(self)
        # boot the device on init, to fail as
        # early as possible if it can't be opened
        self.boot()
    
    
    LOG_LEVELS = {
        "traffic": 4,
        "read":    4,
        "write":   4,
        "debug":   3,
        "warn":    2,
        "error":   1 }
    
    
    def _log(self, str_, type_="debug"):
        """Proxies a log message to this Modem's logger, if one has been set.
           This is useful for applications embedding pyGSM that wish to show
           or log what's going on inside.

           The *logger* should be a function with three arguments:
             modem:   a reference to this GsmModem instance
             message: the log message (a unicode string)
             type:    a string contaning one of the keys
                      of GsmModem.LOG_LEVELS, indicating
                      the importance of this message.

           GsmModem.__init__ accepts an optional "logger" kwarg, and a minimal
           (dump to STDOUT) logger is available at GsmModem.logger:

           >>> GsmModem("/dev/ttyUSB0", logger=GsmModem.logger)"""
        
        if hasattr(self, "logger"):
            self.logger(self, str_, type_)
    
    
    @staticmethod
    def logger(_modem, message_, type_):
        print "%8s %s" % (type_, message_)
    
    
    def connect(self, reconnect=False):
        """Creates the connection to the modem via pySerial, optionally
           killing and re-creating any existing connection."""
           
        self._log("Connecting")
        
        # if no connection exists, create it
        # the reconnect flag is irrelevant
        if not hasattr(self, "device") or (self.device is None):
            with self.modem_lock:
                self.device = DeviceWrapper(
                    self.logger, *self.device_args,
                    **self.device_kwargs)
                
        # the port already exists, but if we're
        # reconnecting, then kill it and recurse
        # to recreate it. this is useful when the
        # connection has died, but nobody noticed
        elif reconnect:
            
            self.disconnect()
            self.connect(False)

        return self.device


    def disconnect(self):
        """Disconnects from the modem."""
        
        self._log("Disconnecting")
        
        # attempt to close and destroy the device
        if hasattr(self, "device") and (self.device is not None):
            with self.modem_lock:
                if self.device.isOpen():
                    self.device.close()
                    self.device = None
                    return True
        
        # for some reason, the device
        # couldn't be closed. it probably
        # just isn't open yet
        return False


    def set_modem_config(self):
        """initialize the modem configuration with settings needed to process
           commands and send/receive SMS.
        """
        
        # set some sensible defaults, to make
        # the various modems more consistant
        self.command("ATE0",      raise_errors=False) # echo off
        #sniff the modem type
        modem_type = self.query("AT+CGMM")
        self._log("Modem type: " + modem_type)
        self.command("AT+CMEE=1", raise_errors=False) # useful error messages
        self.command("AT+CSMS=1", raise_errors=False) # set SMS mode to phase 2+
        
        if (modem_type == "TC35i"):
            cnmi_command = "AT+CNMI=2,2,0,0,1"  # according to the TC35i documentation the last parameter of
                                                # AT+CNMI can only be one. It will error on it and you will
                                                # not receive sms messages if it's not set like this.
        else:
            self.command("AT+WIND=0", raise_errors=False) # disable notifications, on the TC35i this command
                                                          # does not exsist
            cnmi_command = "AT+CNMI=2,2,0,0,0"

        self.command(self.smshandler.get_mode_cmd()      ) # make sure in PDU mode  
        self.command(cnmi_command, raise_errors=False)     # enable new message notification

    def boot(self, reboot=False):
        """Initializes the modem. Must be called after init and connect,
           but before doing anything that expects the modem to be ready."""
        
        self._log("Booting")
        
        if reboot:
            # If reboot==True, force a reconnection and full modem reset. SLOW
            self.connect(reconnect=True)
            self.command("AT+CFUN=1")
        else:
            # else just verify connection
            self.connect()

        # In both cases, reset the modem's config
        self.set_modem_config()        

        # And check for any waiting messages PRIOR to setting
        # the CNMI call--this is not supported by all modems--
        # in which case we catch the exception and plow onward
        try:
            self._fetch_stored_messages()
        except errors.GsmError:
            pass


    def reboot(self):
        """Forces a reconnect to the serial port and then a full modem reset to factory
           and reconnect to GSM network. SLOW.
        """
        self.boot(reboot=True)


    def _write(self, str):
        """Write a string to the modem."""
        
        self._log(repr(str), "write")

        try:
            self.device.write(str)

        # if the device couldn't be written to,
        # wrap the error in something that can
        # sensibly be caught at a higher level
        except OSError, err:
            raise(errors.GsmWriteError)