Skip to content

Instantly share code, notes, and snippets.

@sorz
Last active December 23, 2016 12:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sorz/5805478 to your computer and use it in GitHub Desktop.
Save sorz/5805478 to your computer and use it in GitHub Desktop.
Forward SMS to E-mail via Huawei datacard. See https://blog.sorz.org/p/sms2email/ for detial.
#!/usr/bin/env python
#encoding: utf-8
# Copyright (C) 2013 @XiErCh
#
# 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 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.
import time
import serial
import smtplib
from datetime import datetime
LOGFILE_PATH = 'mail.log'
TTY_DEV_PATH = '/dev/ttyUSB0'
SENDER_EMAIL = 'hw-smsd@somehost'
NOTICE_EMAIL = 'your@email'
CALL_SMSREPLY = u'抱歉,本号码暂时停用。请拨打 13800138000 联系我,谢谢。'
class ATSerial(serial.Serial):
'''Send a AT command and return the response or raise a error.
'''
# Please see HUAWEI CDMA Datacard Modem AT Command Interface Specification
# for detial.
def at(self, cmd):
self.write('AT%s\r\n' % cmd)
self.flush()
self.readline()
rsp = ''
while True:
l = self.readline().strip()
if not l:
continue
elif l == 'OK':
return rsp
elif l == 'ERROR':
raise IOError('AT: ERROR')
elif l == 'COMMAND NOT SUPPORT':
raise IOError('AT: COMMAND NOT SUPPORT')
elif l == 'TOO MANY PARAMETERS':
raise IOError('AT: TOO MANY PARAMETERS')
rsp += '%s\n' % l
def get_serial():
return ATSerial(TTY_DEV_PATH)
def sendmail(msg, subject='',fromaddr=SENDER_EMAIL, toaddr=NOTICE_EMAIL):
s = smtplib.SMTP('localhost')
msg = u"From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s" \
% (fromaddr, toaddr, subject, msg)
s.sendmail(fromaddr, toaddr, msg.encode('utf-8'))
logfile.write(msg.encode('utf-8') + '\n\n')
logfile.flush()
s.quit()
def sendsms(s, number, text, number_type=0):
logfile.write('SMS to %s:\n%s\n\n' % (number, text))
logfile.flush()
s.write('AT^HCMGS="%s",%s\r' % (number, number_type))
s.flush()
time.sleep(0.1)
s.write(text.encode('utf-16-be') + '\x00\x1a')
while True:
l = s.readline().strip()
if l == 'OK':
return
elif l.startswith('+CMS ERROR:'):
raise IOError(l)
def e_new_sms(s, opt):
# opt[0] callerID
# [1:7] yyyy,mm,dd,hh,mm,ss
# [7] lang
# [8] format
# [9] length
# [10-13] ...
text = s.read(int(opt[9])).decode('utf-16-be')
s.read(3) # Ignore ^Z\r\n
send_time = "%s-%s-%s %s:%s:%s" % tuple(opt[1:7])
recv_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
mailsubject = u'来自 %s 的短信' % opt[0]
mailtext = u'%s\r\n\r\n----\r\n发送号码:%s\r\n发送日期:%s\r\n接收日期:%s' \
% (text, opt[0], send_time, recv_time)
sendmail(mailtext, mailsubject)
print("SMS => E-mail")
s.at('+CNMA') # ack
def e_income_call(s, opt):
# opt[0] number
# [1] type
# [5] CLI validity
if not opt[0]:
if opt[5] == '1':
opt[0] = u'隐藏号码'
else:
opt[0] = u'未知号码'
mailsubject = u'来自 %s 的来电' % opt[0]
mailtext = u'来电者:%s\r\n来电时间:%s' \
% (opt[0], datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
sendmail(mailtext, mailsubject)
print("Call => E-mail")
s.at('+CHV') # reject
if opt[0].startswith('1'):
sendsms(s, opt[0], CALL_SMSREPLY, opt[1])
print("Call <= SMS")
def loop(s):
line = s.readline().strip()
if not line:
return
elif line == 'RING':
return
try:
cmd, rsp = line.split(':', 1)
rsp = rsp.strip()
opt = rsp.split(',')
except ValueError:
print('unknow [%s]' % line)
return
if cmd == '^HCMT':
e_new_sms(s, opt)
elif cmd == '+CLIP':
e_income_call(s, opt)
else:
print('unknow - %s:' % cmd)
print(opt)
def main():
s = get_serial()
# SMS Parameter Selection:
# ^HSMSSS=<ack>,<prt>,<fmt>,<prv>
# <fm> 6: Unicode
s.at("^HSMSSS=0,0,6,0")
# New SMS Notification Setting:
# +CNMI=[<mode>[,<mt>[,<bm>[,<ds>[,<bfr>]]]]]
s.at("+CNMI=1,2")
#sendmail('test', 'sendmail test')
while True:
loop(s)
if __name__ == '__main__':
logfile = open(LOGFILE_PATH, 'a')
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment