diff --git a/lib/sim900/gsm.py b/lib/sim900/gsm.py index 4fc136f..e71427a 100644 --- a/lib/sim900/gsm.py +++ b/lib/sim900/gsm.py @@ -1,24 +1,24 @@ -#The MIT License (MIT) +# The MIT License (MIT) # -#Copyright (c) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua ) +# Copyright (c) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua ) # -#Permission is hereby granted, free of charge, to any person obtaining a copy -#of this software and associated documentation files (the "Software"), to deal -#in the Software without restriction, including without limitation the rights -#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -#copies of the Software, and to permit persons to whom the Software is -#furnished to do so, subject to the following conditions: +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: # -#The above copyright notice and this permission notice shall be included in all -#copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. # -#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -#SOFTWARE. +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. """ This file is part of sim-module package. Implements basic functions of SIM900 modules. @@ -34,42 +34,45 @@ import logging from lib.sim900.simshared import * + class GsmSpecialCharacters: - ctrlz = 26 #//Ascii character for ctr+z. End of a SMS. - cr = 0x0d #//Ascii character for carriage return. - lf = 0x0a #//Ascii character for line feed. + ctrlz = 26 # //Ascii character for ctr+z. End of a SMS. + cr = 0x0d # //Ascii character for carriage return. + lf = 0x0a # //Ascii character for line feed. + class SimGsmState: - UNKNOWN = 0 - ERROR = 1 - IDLE = 2 - READY = 3 - ATTACHED = 4 - TCPSERVERWAIT = 5 - TCPCONNECTEDSERVER = 6 - TCPCONNECTEDCLIENT = 7 + UNKNOWN = 0 + ERROR = 1 + IDLE = 2 + READY = 3 + ATTACHED = 4 + TCPSERVERWAIT = 5 + TCPCONNECTEDSERVER = 6 + TCPCONNECTEDCLIENT = 7 class SimGsmPinRequestState: - UNKNOWN = -1 - NOPINNEEDED = 0 + UNKNOWN = -1 + NOPINNEEDED = 0 + + SIM_PIN = 1 + SIM_PUK = 2 - SIM_PIN = 1 - SIM_PUK = 2 + PH_SIM_PIN = 3 + PH_SIM_PUK = 4 - PH_SIM_PIN = 3 - PH_SIM_PUK = 4 + SIM_PIN2 = 5 + SIM_PUK2 = 6 - SIM_PIN2 = 5 - SIM_PUK2 = 6 class SimGsmSerialPortHandler(AminisLastErrorHolderWithLogging): - def __init__(self, serial, logger = None): + def __init__(self, serial, logger=None): AminisLastErrorHolderWithLogging.__init__(self, logger) - self.input = bytearray() - self.__serial = serial + self.input = bytearray() + self.__serial = serial - #stores last executed command result + # stores last executed command result self.lastResult = None def openPort(self): @@ -85,7 +88,7 @@ def openPort(self): return True - def __sendRawBytes(self, data, maxWaitTime = 1000): + def __sendRawBytes(self, data, maxWaitTime=1000): """ Sends raw bytes to the SIM module @@ -93,9 +96,9 @@ def __sendRawBytes(self, data, maxWaitTime = 1000): :param maxWaitTime: max wait time for sending sequence :return: True if data was send, otherwise returns False """ - bytesToSend = len(data) - sentBytes = 0 - start = time.time() + bytesToSend = len(data) + sentBytes = 0 + start = time.time() self.logger.debug("{0}, sending: {1}".format(inspect.stack()[0][3], data)) @@ -104,14 +107,14 @@ def __sendRawBytes(self, data, maxWaitTime = 1000): self.setWarn("__sendRawBytes(): timed out") return False - sentBytes += self.__serial.write(data[sentBytes : ]) + sentBytes += self.__serial.write(data[sentBytes:]) if sentBytes == 0: time.sleep(0.001) continue return True - def print(self, commandString, encoding = "ascii"): + def send(self, commandString, encoding="ascii"): """ Sends string data to the SIM module @@ -122,17 +125,17 @@ def print(self, commandString, encoding = "ascii"): data = bytearray(commandString, encoding) return self.__sendRawBytes(data) - def simpleWrite(self, commandLine, encoding = "ascii"): + def simpleWrite(self, commandLine, encoding="ascii"): """ - Just alias for print() method + Just alias for send() method :param commandLine: data which must be sent :param encoding: before sending string it will be converted to the bytearray with this encoding :return: True if data sent, otherwise returns False """ - return self.print(commandLine, encoding) + return self.send(commandLine, encoding) - def printLn(self, commandString, encoding = "ascii"): + def sendLn(self, commandString, encoding="ascii"): """ Sends string data and CR/LF in the end to the SIM module @@ -143,16 +146,16 @@ def printLn(self, commandString, encoding = "ascii"): data = bytearray(commandString, encoding) + bytearray([GsmSpecialCharacters.cr, GsmSpecialCharacters.lf]) return self.__sendRawBytes(data) - def simpleWriteLn(self, commandLine, encoding = "ascii"): + def simpleWriteLn(self, commandLine, encoding="ascii"): """ - Just alias for printLn() method + Just alias for sendLn() method :param commandLine: data which must be sent :param encoding: before sending string it will be converted to the bytearray with this encoding :return: True if data sent, otherwise returns False """ - return self.printLn(commandLine, encoding) + return self.sendLn(commandLine, encoding) def flushInput(self): """ @@ -181,11 +184,11 @@ def flushOutput(self): self.setError("error flushing") def readFixedSzieByteArray(self, bytesCount, maxWaitTime): - start = time.time() - buffer = bytearray() + start = time.time() + buffer = bytearray() try: while True: - #checking for timeout + # checking for timeout if timeDelta(start) >= maxWaitTime: return None @@ -203,11 +206,11 @@ def readFixedSzieByteArray(self, bytesCount, maxWaitTime): if len(buffer) == bytesCount: return buffer - #if we have nothing in input - let's go sleep for some time + # if we have nothing in input - let's go sleep for some time if receivedBytesQty == 0: time.sleep(0.003) - #comming there by timeout + # comming there by timeout return None except Exception as e: @@ -217,15 +220,14 @@ def readFixedSzieByteArray(self, bytesCount, maxWaitTime): self.setError("reading error...") return None + def readNullTerminatedLn(self, maxWaitTime=5000, codepage="ascii"): + start = time.time() - def readNullTerminatedLn(self, maxWaitTime = 5000, codepage = "ascii"): - start = time.time() - - start = time.time() - buffer = bytearray() + start = time.time() + buffer = bytearray() try: while True: - #checking for timeout + # checking for timeout if timeDelta(start) >= maxWaitTime: return None @@ -236,7 +238,7 @@ def readNullTerminatedLn(self, maxWaitTime = 5000, codepage = "ascii"): if (b is None) or (len(b) == 0): break - #checking that we have NULL symbol in + # checking that we have NULL symbol in idx = b.find(0x00) if idx != -1: buffer.extend(b[:idx]) @@ -245,11 +247,11 @@ def readNullTerminatedLn(self, maxWaitTime = 5000, codepage = "ascii"): buffer += bytearray(b) receivedBytesQty += len(b) - #if we have nothing in input - let's go sleep for some time + # if we have nothing in input - let's go sleep for some time if receivedBytesQty == 0: time.sleep(0.003) - #comming there by timeout + # comming there by timeout return None except Exception as e: @@ -259,7 +261,7 @@ def readNullTerminatedLn(self, maxWaitTime = 5000, codepage = "ascii"): self.setError("reading error...") return None - def readLn(self, maxWaitTime = 5000, codepage = "ascii"): + def readLn(self, maxWaitTime=5000, codepage="ascii"): """ Returns text string from SIM module. Can return even empty strings. @@ -267,11 +269,11 @@ def readLn(self, maxWaitTime = 5000, codepage = "ascii"): :param codepage: code page of result string :return: received string """ - start = time.time() - buffer = bytearray() + start = time.time() + buffer = bytearray() try: while True: - #checking for timeout + # checking for timeout if timeDelta(start) >= maxWaitTime: return None @@ -286,18 +288,18 @@ def readLn(self, maxWaitTime = 5000, codepage = "ascii"): receivedBytesQty += len(b) if codepage is not None: - #checking for line end symbols + # checking for line end symbols line = buffer.decode(codepage) if '\n' in line: return line.strip() elif ord('\n') in buffer: return buffer - #if we have nothing in input - let's go sleep for some time + # if we have nothing in input - let's go sleep for some time if receivedBytesQty == 0: time.sleep(0) - #comming there by timeout + # comming there by timeout return None except Exception as e: @@ -307,7 +309,7 @@ def readLn(self, maxWaitTime = 5000, codepage = "ascii"): self.setError("reading error...") return None - def readDataLine(self, maxWaitTime = 500, codepage = "ascii"): + def readDataLine(self, maxWaitTime=500, codepage="ascii"): """ Returns non empty data string. So, if it will receive empty string function will continue non empty string retrieving @@ -316,36 +318,36 @@ def readDataLine(self, maxWaitTime = 500, codepage = "ascii"): :param codepage: code page of result string, if it's a None - will return a bytearray :return: received string """ - ret = None - start = time.time() + ret = None + start = time.time() while True: - #checking for timeout + # checking for timeout if timeDelta(start) >= maxWaitTime: break - #reading string - #TODO: need to fix timeout (substract already spent time interval) + # reading string + # TODO: need to fix timeout (substract already spent time interval) line = self.readLn(maxWaitTime, codepage) - #removing garbage symbols + # removing garbage symbols if line is not None: line = str(line).strip() - #if we have non empty string let's return it + # if we have non empty string let's return it if len(line) > 0: return line else: - #if we have empty line - let's continue reading + # if we have empty line - let's continue reading continue else: - #returning None if None received + # returning None if None received if line is None: return None continue - #we will come here by timeout + # we will come here by timeout return None def flush(self): @@ -393,7 +395,7 @@ def getLastNonEmptyString(strings): :return: last non empty string, otherwise None """ - #if there is no data - returning None + # if there is no data - returning None if strings is None: return None @@ -401,9 +403,9 @@ def getLastNonEmptyString(strings): if qty == 0: return None - #looking for last non empty string + # looking for last non empty string for i in range(qty): - s = str(strings[-(i+1)]).strip() + s = str(strings[-(i + 1)]).strip() if len(s) > 0: return s @@ -419,15 +421,15 @@ def removeEndResult(strings, targetString): """ ret = "" - #searching for target string + # searching for target string while len(strings) > 0: s = str(strings[-1]).strip() - strings.pop(len(strings)-1) + strings.pop(len(strings) - 1) if s == targetString: break - #compiling result + # compiling result qty = len(strings) for i in range(qty): ret += strings[i] @@ -435,7 +437,7 @@ def removeEndResult(strings, targetString): return ret @staticmethod - def parseStrings(buffer, encoding = "ascii"): + def parseStrings(buffer, encoding="ascii"): """ Parses string (from given encoding), looks for cr/lf and retutrns strings array :param buffer: input string @@ -443,10 +445,10 @@ def parseStrings(buffer, encoding = "ascii"): :return: strings array """ - #decoding + # decoding bigString = buffer.decode(encoding) - #searching for cr/lf and making strings array + # searching for cr/lf and making strings array if "\r" in bigString: ret = bigString.split("\r") else: @@ -454,19 +456,19 @@ def parseStrings(buffer, encoding = "ascii"): return ret - def commandAndStdResult(self, commandText, maxWaitTime = 5000, possibleResults = None): + def commandAndStdResult(self, commandText, maxWaitTime=5000, possibleResults=None): self.lastResult = None - #setting up standard results + # setting up standard results if possibleResults is None: possibleResults = ["OK", "ERROR"] - start = time.time() - buffer = bytearray() + start = time.time() + buffer = bytearray() self.flush() - #sending command + # sending command self.simpleWriteLn(commandText) try: @@ -487,12 +489,12 @@ def commandAndStdResult(self, commandText, maxWaitTime = 5000, possibleResults = else: break - #if we have no data - let's go sleep for tiny amount of time + # if we have no data - let's go sleep for tiny amount of time if readBytesQty == 0: time.sleep(0.005) continue - #parsing result strings + # parsing result strings strings = SimGsm.parseStrings(buffer[:]) self.logger.debug("{0}: strings = {1}".format(inspect.stack()[0][3], strings)) @@ -500,7 +502,7 @@ def commandAndStdResult(self, commandText, maxWaitTime = 5000, possibleResults = time.sleep(0.01) continue - #if we have some strings let's parse it + # if we have some strings let's parse it if len(strings) > 0: lastString = SimGsm.getLastNonEmptyString(strings[:]) @@ -518,14 +520,14 @@ def commandAndStdResult(self, commandText, maxWaitTime = 5000, possibleResults = self.setError("reading error...") return None - def execSimpleCommand(self, commandText, result, timeout = 500): + def execSimpleCommand(self, commandText, result, timeout=500): ret = self.commandAndStdResult(commandText, timeout, [result]) if (ret is None) or (self.lastResult != result): return False return True - def execSimpleOkCommand(self, commandText, timeout = 500): + def execSimpleOkCommand(self, commandText, timeout=500): self.logger.debug("executing command '{0}'".format(commandText)) ret = self.commandAndStdResult(commandText, timeout, ["OK", "ERROR"]) @@ -541,32 +543,33 @@ def execSimpleCommandsList(self, commandsList): return True + class SimGsm(SimGsmSerialPortHandler): - def __init__(self, serial, logger = None): + def __init__(self, serial, logger=None): SimGsmSerialPortHandler.__init__(self, serial, logger) - self.__state = SimGsmState.UNKNOWN + self.__state = SimGsmState.UNKNOWN self.pinState = SimGsmPinRequestState.UNKNOWN - def begin(self, numberOfAttempts = 5): - ok = False + def begin(self, numberOfAttempts=5): + ok = False self.flush() needDisableEcho = False for i in range(numberOfAttempts): - self.printLn("AT") + self.sendLn("AT") line = self.readDataLine(2000, "ascii") - #if we do not have something in input - let's go sleep + # if we do not have something in input - let's go sleep if line is None: time.sleep(0.2) continue - #we have echo, need to reconfigure + # we have echo, need to reconfigure if line == "AT": - #we have ECHO, need reconfigure + # we have ECHO, need reconfigure needDisableEcho = True line = self.readDataLine(500, "ascii") if line == "OK": @@ -580,7 +583,7 @@ def begin(self, numberOfAttempts = 5): if not ok: return False - #disabling echo if needed + # disabling echo if needed if needDisableEcho: self.logger.info("Disabling echo, calling 'ATE0'") self.simpleWriteLn("ATE0") @@ -588,17 +591,17 @@ def begin(self, numberOfAttempts = 5): self.flush() commands = [ - ["ATV1", 500], #short answer for commands - ["AT+CMEE=0", 500], #disabling error report - ["AT", 5000] #checking state + ["ATV1", 500], # short answer for commands + ["AT+CMEE=0", 500], # disabling error report + ["AT", 5000] # checking state ] for cmd in commands: self.logger.debug("configuring, calling: {0}".format(cmd[0])) - if not self.execSimpleOkCommand(commandText=cmd[0],timeout=cmd[1]): + if not self.execSimpleOkCommand(commandText=cmd[0], timeout=cmd[1]): return False - #checking PIN state + # checking PIN state if not self.__checkPin(): return False diff --git a/lib/sim900/imei.py b/lib/sim900/imei.py index 343f93b..f452c32 100644 --- a/lib/sim900/imei.py +++ b/lib/sim900/imei.py @@ -1,24 +1,24 @@ -#The MIT License (MIT) +# The MIT License (MIT) # -#Copyright (c) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua ) +# Copyright (c) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua ) # -#Permission is hereby granted, free of charge, to any person obtaining a copy -#of this software and associated documentation files (the "Software"), to deal -#in the Software without restriction, including without limitation the rights -#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -#copies of the Software, and to permit persons to whom the Software is -#furnished to do so, subject to the following conditions: +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: # -#The above copyright notice and this permission notice shall be included in all -#copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. # -#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -#SOFTWARE. +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. """ This file is part of sim-module package. Can be used for SIM900 module IMEI retrieving @@ -31,6 +31,7 @@ from lib.sim900.gsm import SimGsm + class SimImeiRetriever(SimGsm): def __init__(self, port, logger): SimGsm.__init__(self, port, logger) @@ -42,4 +43,4 @@ def getIMEI(self): if data is None: return None - return str(data).strip() \ No newline at end of file + return str(data).strip() diff --git a/lib/sim900/smshandler.py b/lib/sim900/smshandler.py index 97e3c0e..7cf77f4 100644 --- a/lib/sim900/smshandler.py +++ b/lib/sim900/smshandler.py @@ -1,24 +1,25 @@ -#The MIT License (MIT) +# -*- coding: utf-8 -*- +# The MIT License (MIT) # -#Copyright (c) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua ) +# Copyright (c) 2014-2015 Bohdan Danishevsky ( dbn@aminis.com.ua ) # -#Permission is hereby granted, free of charge, to any person obtaining a copy -#of this software and associated documentation files (the "Software"), to deal -#in the Software without restriction, including without limitation the rights -#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -#copies of the Software, and to permit persons to whom the Software is -#furnished to do so, subject to the following conditions: +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: # -#The above copyright notice and this permission notice shall be included in all -#copies or substantial portions of the Software. +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. # -#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -#SOFTWARE. +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. """ This file is part of sim-module package. SMS processing classes and functions. @@ -34,23 +35,24 @@ import binascii import random + class SimSmsPduCompiler(AminisLastErrorHolder): def __init__(self, smsCenterNumber="", targetPhoneNumber="", smsTextMessage=""): AminisLastErrorHolder.__init__(self) - #sms center number - self.__smsCenterNumber = self.__preprocessPhoneNumber(smsCenterNumber) + # sms center number + self.__smsCenterNumber = self.__preprocessPhoneNumber(smsCenterNumber) - #sms recipient number - self.__smsRecipientNumber = self.__preprocessPhoneNumber(targetPhoneNumber) + # sms recipient number + self.__smsRecipientNumber = self.__preprocessPhoneNumber(targetPhoneNumber) - #sms text - self.smsText = smsTextMessage + # sms text + self.smsText = smsTextMessage - self.flashMessage = False + self.flashMessage = False - #validation period for message - self.__validationPeriod = None + # validation period for message + self.__validationPeriod = None def clear(self): """ @@ -60,12 +62,12 @@ def clear(self): """ self.clear() - self.__smsCenterNumber = "" - self.__smsRecipientNumber = "" - self.smsText = "" - self.flashMessage = False + self.__smsCenterNumber = "" + self.__smsRecipientNumber = "" + self.smsText = "" + self.flashMessage = False - self.__validationPeriod = None + self.__validationPeriod = None @property def smsCenterNumber(self): @@ -78,9 +80,9 @@ def smsCenterNumber(self): @staticmethod def __preprocessPhoneNumber(value): - value = noneToEmptyString(value) - value = str(value).strip() - value = value.replace(" ", "") + value = noneToEmptyString(value) + value = str(value).strip() + value = value.replace(" ", "") return value.replace("\t", "") @@ -139,15 +141,15 @@ def __encodePhoneNumber(number): num = str(number).strip() num = num.replace("+", "") - #adding pad byte + # adding pad byte if (len(num) % 2) != 0: num += 'F' - #calculating reverted result, according to the + # calculating reverted result, according to the result = "" i = 0 while i < len(num): - result += num[i+1] + num[i] + result += num[i + 1] + num[i] i += 2 return result @@ -162,10 +164,10 @@ def __compileScaPart(self): return "00" smsCenterNumber = SimSmsPduCompiler.__encodePhoneNumber(self.smsCenterNumber) - sca = SimSmsPduCompiler.__byteToHex ( ((len(smsCenterNumber) // 2) + 1)) + "91" + smsCenterNumber + sca = SimSmsPduCompiler.__byteToHex(((len(smsCenterNumber) // 2) + 1)) + "91" + smsCenterNumber return sca - def __canUse7BitsEncoding(self, text = None): + def __canUse7BitsEncoding(self, text=None): """ Checks that message can be encoded in 7 bits. @@ -189,22 +191,22 @@ def __encodeMessageIn7Bits(text): data = bytearray(text.encode("ascii")) - #encoding + # encoding i = 1 while i < len(data): j = len(data) - 1 - while j>=i: + while j >= i: firstBit = 0x80 if ((data[j] % 2) > 0) else 0x00 - data[j-1] = (data[j-1] & 0x7f) | firstBit - data[j] = data[j] >> 1 + data[j - 1] = (data[j - 1] & 0x7f) | firstBit + data[j] = data[j] >> 1 j -= 1 i += 1 - #looking for first 0x00 byte + # looking for first 0x00 byte index = 0 for b in data: if b == 0x00: @@ -241,14 +243,14 @@ def __compilePduTypePart(self, isMultupartMessage): :return: encoded PDU-Type """ - #returning PDU-Type when validation period is not specified + # returning PDU-Type when validation period is not specified if self.__validationPeriod is None: if not isMultupartMessage: return "01" return "41" - #special value when multi-part message + # special value when multi-part message if isMultupartMessage: return "51" @@ -270,7 +272,7 @@ def setValidationPeriodInMinutes(self, value): :return: true if everything is OK, otherwise returns false """ - #0-143 (TP-VP + 1) x 5 minutes 5, 10, 15 minutes ... 11:55, 12:00 hours + # 0-143 (TP-VP + 1) x 5 minutes 5, 10, 15 minutes ... 11:55, 12:00 hours count = value // 5 if count > 143: @@ -287,7 +289,7 @@ def setValidationPeriodInHours(self, value): :param value: hours count (float), must be >= 12 and <= 24 :return: true if everything is OK, otherwise returns false """ - #144-167 (12 + (TP-VP - 143) / 2 ) hours 12:30, 13:00, ... 23:30, 24:00 hours + # 144-167 (12 + (TP-VP - 143) / 2 ) hours 12:30, 13:00, ... 23:30, 24:00 hours if (value < 12) or (value > 24): self.setError("Value must be between 12 and 24 hours") @@ -297,11 +299,11 @@ def setValidationPeriodInHours(self, value): count = int(value) if (value - count) >= 0.5: - count = count*2 + 1 + count = count * 2 + 1 else: - count = count*2 + count = count * 2 - if count>23: + if count > 23: count = 23 self.__validationPeriod = self.__byteToHex(count + 144) @@ -315,7 +317,7 @@ def setValidationPeriodInDays(self, value): :return: true when value is OK, otherwise returns false """ - #168-196 (TP-VP - 166) days 2, 3, 4, ... 30 days + # 168-196 (TP-VP - 166) days 2, 3, 4, ... 30 days if (value < 2) or (value > 30): self.setError("Bad interval, value must be >= 2 days and <= 30 days") @@ -341,7 +343,7 @@ def setValidationPeriodInWeeks(self, value): self.__validationPeriod = self.__byteToHex(value + 197) return True - def __compileTpdu(self, pieceNumber, totalPiecesCount, pieceText, messageId = None): + def __compileTpdu(self, pieceNumber, totalPiecesCount, pieceText, messageId=None): """ Compiles TPDU part of PDU message request. :return: compiled TPDU @@ -350,14 +352,14 @@ def __compileTpdu(self, pieceNumber, totalPiecesCount, pieceText, messageId = No # PDU-Type is the same as SMS-SUBMIT-PDU ret = "" - #checking that message have more than one part + # checking that message have more than one part isMultipartMessage = totalPiecesCount > 1 - #adding PDU-Type + # adding PDU-Type ret += self.__compilePduTypePart(isMultipartMessage) - #adding TP-MR (TP-Message-Reference). - ret += self.__byteToHex(pieceNumber+100) + # adding TP-MR (TP-Message-Reference). + ret += self.__byteToHex(pieceNumber + 100) # if totalPiecesCount > 1: # #setting message reference manually # ret += self.__byteToHex(pieceNumber) @@ -365,21 +367,23 @@ def __compileTpdu(self, pieceNumber, totalPiecesCount, pieceText, messageId = No # #The "00" value here lets the phone set the message reference number itself. # ret += "00" - #encoding TP-DA (TP-Destination-Address - recipient address) - ret += self.__byteToHex(self.__clientPhoneNumberLength(self.smsRecipientNumber)) + "91" + self.__encodePhoneNumber(self.smsRecipientNumber) + # encoding TP-DA (TP-Destination-Address - recipient address) + ret += self.__byteToHex( + self.__clientPhoneNumberLength(self.smsRecipientNumber)) + "91" + self.__encodePhoneNumber( + self.smsRecipientNumber) - #adding TP-PID (TP-Protocol ID) + # adding TP-PID (TP-Protocol ID) ret += "00" - #adding TP-DCS (TP-Data-Coding-Scheme) - #00h: 7-bit encoding (160 symbols [after packing], but only ASCII) - #08h: UCS2 encoding (Unicode), 70 symbols, 2 bytes per symbol + # adding TP-DCS (TP-Data-Coding-Scheme) + # 00h: 7-bit encoding (160 symbols [after packing], but only ASCII) + # 08h: UCS2 encoding (Unicode), 70 symbols, 2 bytes per symbol - #If first octet is "1" message will not be saved in mobile but only flashed on the screen - #10h: Flash-message with 7-bit encoding - #18h: Flash-message with UCS2 encoding + # If first octet is "1" message will not be saved in mobile but only flashed on the screen + # 10h: Flash-message with 7-bit encoding + # 18h: Flash-message with UCS2 encoding - #checking that message CAN be encoded in 7 bits encoding + # checking that message CAN be encoded in 7 bits encoding canBe7BitsEncoded = self.__canUse7BitsEncoding() if canBe7BitsEncoded: @@ -392,44 +396,44 @@ def __compileTpdu(self, pieceNumber, totalPiecesCount, pieceText, messageId = No ret += tpDcs - #adding TP-VP (TP-Validity-Period) is it's specified + # adding TP-VP (TP-Validity-Period) is it's specified if self.__validationPeriod is not None: ret += self.__compilePduTpVpPart() - #encoding message (7-bit or UCS2) + # encoding message (7-bit or UCS2) if canBe7BitsEncoded: encodedMessage = self.__encodeMessageIn7Bits(pieceText) else: encodedMessage = self.__encodeMessageAsUcs2(pieceText) - #checking that message was encoded correctly + # checking that message was encoded correctly if encodedMessage is None: self.setError("error encoding message: {0}".format(self.errorText)) return None - #adding TP-UDL (TP-User-Data-Length - message length) + # adding TP-UDL (TP-User-Data-Length - message length) if not isMultipartMessage: if canBe7BitsEncoded: - #adding TEXT LENGTH IN SYMBOLS + # adding TEXT LENGTH IN SYMBOLS ret += self.__byteToHex(len(self.smsText)) else: - ret += self.__byteToHex(len(encodedMessage)//2) + ret += self.__byteToHex(len(encodedMessage) // 2) else: if canBe7BitsEncoded: ret += self.__byteToHex(len(pieceText) + 8) else: - ret += self.__byteToHex(len(encodedMessage)//2 + 6) + ret += self.__byteToHex(len(encodedMessage) // 2 + 6) - #adding UDHL + UDH for multipart messages + # adding UDHL + UDH for multipart messages if isMultipartMessage: if canBe7BitsEncoded: - #length of UDH + # length of UDH udhl = bytearray([0x06]) - #UDI IED entry type - iei = bytearray([0x08]) + # UDI IED entry type + iei = bytearray([0x08]) - #length of UDH IED + # length of UDH IED iedl = bytearray([0x04]) # messageId @@ -437,49 +441,49 @@ def __compileTpdu(self, pieceNumber, totalPiecesCount, pieceText, messageId = No ied1Hi = ((messageId & 0xff00) >> 8) ied1 = bytearray([ied1Hi, ied1Lo]) - #total pieces count + # total pieces count ied2 = bytearray([totalPiecesCount]) - #piece number + # piece number ied3 = bytearray([pieceNumber]) - #compiling IED - ied = ied1 + ied2 + ied3 + # compiling IED + ied = ied1 + ied2 + ied3 - #compiling UDH - udh = iei + iedl + ied + # compiling UDH + udh = iei + iedl + ied else: - #length of UDH + # length of UDH udhl = bytearray([0x05]) - #UDI IED entry type - iei = bytearray([0x00]) + # UDI IED entry type + iei = bytearray([0x00]) - #length of UDH IED + # length of UDH IED iedl = bytearray([0x03]) - #message id + # message id ied1Lo = messageId & 0xff ied1 = bytearray([ied1Lo]) - #total pieces count + # total pieces count ied2 = bytearray([totalPiecesCount]) - #piece number + # piece number ied3 = bytearray([pieceNumber]) - #compiling IED - ied = ied1 + ied2 + ied3 + # compiling IED + ied = ied1 + ied2 + ied3 - #compiling UDH - udh = iei + iedl + ied + # compiling UDH + udh = iei + iedl + ied cudh = binascii.hexlify(udhl + udh).decode("ascii").upper() print("cudh = '{0}'".format(cudh)) ret += cudh - #adding TP-UD (TP-User-Data - SMS message encoded as described in TP-DCS) + # adding TP-UD (TP-User-Data - SMS message encoded as described in TP-DCS) ret += encodedMessage return ret @@ -525,16 +529,16 @@ def compile(self): """ ret = [] - symbolsCount = len(self.smsText) - msgCount = self.messagesCount() - isUcs2 = not self.__canUse7BitsEncoding() + symbolsCount = len(self.smsText) + msgCount = self.messagesCount() + isUcs2 = not self.__canUse7BitsEncoding() if isUcs2: symbolsInPiece = 67 else: symbolsInPiece = 152 - #generating message id for multi-part messages + # generating message id for multi-part messages messageId = None if msgCount > 1: messageId = random.randint(0, 65535) @@ -546,12 +550,13 @@ def compile(self): else: minIndex = i * symbolsInPiece maxIndex = (minIndex + symbolsInPiece) if (minIndex + symbolsInPiece) < symbolsCount else (symbolsCount) - textPiece = self.smsText[minIndex : maxIndex] + textPiece = self.smsText[minIndex: maxIndex] - ret += [(self.__compileScaPart(), self.__compileTpdu(i+1, msgCount, textPiece, messageId),)] + ret += [(self.__compileScaPart(), self.__compileTpdu(i + 1, msgCount, textPiece, messageId),)] return ret + class SimGsmSmsHandler(SimGsm): def __init__(self, port, logger): SimGsm.__init__(self, port, logger) @@ -562,15 +567,15 @@ def clear(self): SimGsm.clearError(self) self.sendingResult = "" - def sendSms(self, phoneNumber, messageText, numberOfAttempts = 3): + def sendSms(self, phoneNumber, messageText, numberOfAttempts=3): tuneCommands = [ - ["AT+CMGS=?", 300], #checking that sms supported - ["AT+CMGF=1", 1000] + ["AT+CMGS=?", 300], # checking that sms supported + ["AT+CMGF=1", 1000] ] self.logger.debug("initializing SIM module for SMS sending") for cmd in tuneCommands: - if not self.execSimpleOkCommand(commandText=cmd[0],timeout=cmd[1]): + if not self.execSimpleOkCommand(commandText=cmd[0], timeout=cmd[1]): return False for i in range(numberOfAttempts): @@ -596,11 +601,11 @@ def sendSms(self, phoneNumber, messageText, numberOfAttempts = 3): self.setError("error sending sms...") return False - def __sendPduMessageLow(self, sca, pdu, numberOfAttempts = 3): + def __sendPduMessageLow(self, sca, pdu, numberOfAttempts=3): tuneCommands = [ - ["AT+CSCS=\"GSM\"", 500], + ["AT+CSCS=\"GSM\"", 500], # ["AT+CMGS?", 500], #checking that sms supported - ["AT+CMGF=0", 1000] + ["AT+CMGF=0", 1000] ] self.logger.debug("initializing SIM module for SMS sending in PDU mode") @@ -633,8 +638,7 @@ def __sendPduMessageLow(self, sca, pdu, numberOfAttempts = 3): return False - - def sendPduMessage(self, pduHelper, numberOfAttempts = 3): + def sendPduMessage(self, pduHelper, numberOfAttempts=3): d = pduHelper.compile() if d is None: self.setError("error compiling PDU sms") @@ -646,8 +650,6 @@ def sendPduMessage(self, pduHelper, numberOfAttempts = 3): if not self.__sendPduMessageLow(sca, pdu, numberOfAttempts): return False - self.logger.info("Sending result = {0}".format(self.sendingResult)) - return True diff --git a/test_shared.py b/test_shared.py index 3a09bbb..e3d0c30 100644 --- a/test_shared.py +++ b/test_shared.py @@ -4,56 +4,58 @@ from lib.sim900.gsm import SimGsm, SimGsmPinRequestState from lib.sim900.imei import SimImeiRetriever + def initializeUartPort( portName, - baudrate = 57600, - bytesize = serial.EIGHTBITS, - parity = serial.PARITY_NONE, - stopbits = serial.STOPBITS_ONE, - timeout = 0 - ): - + baudrate=57600, + bytesize=serial.EIGHTBITS, + parity=serial.PARITY_NONE, + stopbits=serial.STOPBITS_ONE, + timeout=0 +): port = serial.Serial() - #tuning port object - port.port = portName - port.baudrate = baudrate - port.bytesize = bytesize - port.parity = parity - port.stopbits = stopbits - port.timeout = timeout + # tuning port object + port.port = portName + port.baudrate = baudrate + port.bytesize = bytesize + port.parity = parity + port.stopbits = stopbits + port.timeout = timeout return port + def initializeLogs(loggerLevel, consoleLoggerLevel): - #initializing logging formatter + # initializing logging formatter formatter = logging.Formatter('[%(asctime)s] %(levelname)s: %(message)s') - #initializing logger + # initializing logger logger = logging.getLogger(__name__) logger.setLevel(loggerLevel) - #initializing console handler for logging + # initializing console handler for logging consoleLogger = logging.StreamHandler(sys.stdout) consoleLogger.setLevel(consoleLoggerLevel) consoleLogger.setFormatter(formatter) - #adding console appender + # adding console appender logger.addHandler(consoleLogger) - return (formatter, logger, consoleLogger,) + return formatter, logger, consoleLogger, + def baseOperations(port, logger): - #class for general functions + # class for general functions gsm = SimGsm(port, logger) - #opening COM port + # opening COM port logger.info("opening port") if not gsm.openPort(): logger.error("error opening port: {0}".format(gsm.errorText)) return None - #initializing session with SIM900 + # initializing session with SIM900 logger.info("initializing SIM900 session") if not gsm.begin(5): logger.error("error initializing session: {0}".format(gsm.errorText)) @@ -68,7 +70,7 @@ def baseOperations(port, logger): else: logger.debug("PIN OK") - #retrieving IMEI + # retrieving IMEI sim = SimImeiRetriever(port, logger) logger.info("retrieving IMEI") imei = sim.getIMEI() @@ -78,4 +80,4 @@ def baseOperations(port, logger): logger.info("IMEI = {0}".format(imei)) - return (gsm, imei) \ No newline at end of file + return (gsm, imei) diff --git a/test_sms.py b/test_sms.py index f49bda5..7c5ef2e 100644 --- a/test_sms.py +++ b/test_sms.py @@ -1,33 +1,36 @@ -#!/usr/bin/python3 +#!/usr/bin/python +# -*- coding: utf-8 -*- from test_shared import * from lib.sim900.smshandler import SimGsmSmsHandler, SimSmsPduCompiler import random -COMPORT_NAME = "com22" +COMPORT_NAME = "/dev/ttyS0" -#logging levels -CONSOLE_LOGGER_LEVEL = logging.INFO -LOGGER_LEVEL = logging.INFO +# logging levels +CONSOLE_LOGGER_LEVEL = logging.INFO +LOGGER_LEVEL = logging.INFO -#WARN: scecify recipient number here!!! -TARGET_PHONE_NUMBER = "+38 097 123 45 67" +# WARN: scecify recipient number here!!! +TARGET_PHONE_NUMBER = "+19172512321" -#You can specify SMS center number, but it's not necessary. If you will not specify SMS center number, SIM900 -#module will get SMS center number from memory +# You can specify SMS center number, but it's not necessary. If you will not specify SMS center number, SIM900 +# module will get SMS center number from memory # SMS_CENTER_NUMBER = "+1 050 123 45 67" -SMS_CENTER_NUMBER = "" +SMS_CENTER_NUMBER = "" + def printScaPlusPdu(pdu, logger): - #printing SCA+PDU just for debug + # printing SCA+PDU just for debug d = pdu.compile() if d is None: return False - for (sca, pdu, ) in d: + for (sca, pdu,) in d: logger.info("sendSms(): sca + pdu = \"{0}\"".format(sca + pdu)) + def sendSms(sms, pdu, logger): - #just for debug printing all SCA + PDU parts + # just for debug printing all SCA + PDU parts printScaPlusPdu(pdu, logger) if not sms.sendPduMessage(pdu, 1): @@ -36,6 +39,7 @@ def sendSms(sms, pdu, logger): return True + def main(): """ Tests SMS sending. @@ -43,23 +47,23 @@ def main(): :return: true if everything was OK, otherwise returns false """ - #adding & initializing port object + # adding & initializing port object port = initializeUartPort(portName=COMPORT_NAME) - #initializing logger + # initializing logger (formatter, logger, consoleLogger,) = initializeLogs(LOGGER_LEVEL, CONSOLE_LOGGER_LEVEL) - #making base operations + # making base operations d = baseOperations(port, logger) if d is None: return False (gsm, imei) = d - #creating object for SMS sending + # creating object for SMS sending sms = SimGsmSmsHandler(port, logger) - #ASCII + # ASCII logger.info("sending ASCII (Latin-1) SMS") pduHelper = SimSmsPduCompiler( SMS_CENTER_NUMBER, @@ -70,7 +74,7 @@ def main(): if not sendSms(sms, pduHelper, logger): return False - #UCS2 + # UCS2 logger.info("sending UCS2 message") pduHelper = SimSmsPduCompiler( SMS_CENTER_NUMBER, @@ -80,7 +84,7 @@ def main(): if not sendSms(sms, pduHelper, logger): return False - #long UCS2 message + # long UCS2 message logger.info("sending long UCS2 (Unicode) SMS") pduHelper = SimSmsPduCompiler( SMS_CENTER_NUMBER, @@ -109,6 +113,7 @@ def main(): gsm.closePort() return True + if __name__ == "__main__": main() print("DONE")