本帖最后由 XCray 于 2021-8-5 06:57 编辑
20210619注:新版本(大概从0.110左右开始)加入的“SMS notifications via GSM-modem”已经可以很好的解决短信收发问题,用的也是gammu,所以本贴所述的方法也就不需要了。
20210805注:2021.7.x-2021.8.x版本下““SMS notifications via GSM-modem”组件无法正常工作,使得本贴方法又有了必要性。
0. 背景
群晖docker上跑hassio,NanoPi R2S作主路由(跑mqtt broker)、树莓派跑monitor(一个非常好用的利用低功耗蓝牙检测手机在家的脚本)。
之前把3G上网卡插在群晖上,供hassio发送短信通知用。这个做法虽然稳定简单,但缺点也很明显:
- 不能收短信。
- 启动时要求上网卡必须插好
这个上网卡里的SIM卡有时还需要收个短信啥的,另外,互联网不通的时候,除了告警,短信也是一个很好的控制指令传送通道,不能接收短信的话功能就不完整了。
本着折腾的精神,继续探索更完整、更完美的方案,把树莓派打造成为功能完备的短信网关,有些内容也算自己摸出来的,贴出来供大家参考。
1. 效果
除了之前已经实现的在互联网不通时仍可以用短信发送重大告警消息外,还可以在hass上自动呈现收到的短信、向手机推送短信内容,并且可以通过短信对hass进行远程控制(比如关警戒等)。
2. 原理及实现方法
原理很简单,树莓派收到短信后,用mqtt协议通知hass,hass在前端呈现并推送通知到手机(免费)。在警情发生时,hass用mqtt协议请求树莓派发送短信通知。树莓派当然是积极响应这个请求啦啦啦啦~~~~
这里只描述与短信直接相关的内容,其余内容在别的帖子里,有兴趣的话慢慢找吧:-)
这个方案参考了https://post.smzdm.com/p/a4wme8zx/,感谢原作者erickson_et的热心分享。
2.1 上网卡从群晖上取下,插回树莓派上。修改hass配置,去掉sms及相应notify内容。
2.2 树莓派上安装配套软件,gammu、gammu-smsd、python-gammu等
2.3 树莓派上功能配置及程序代码:520这天我把下面的内容高度精简了一番,这下就很清晰了
- 上网卡插入后/dev下新增3个虚拟串口,ttyUSB0~3,一般都用第一个就行(另有一个是用不了的)。
- 编辑接收配置文件 sudo vim /etc/gammu-smsdrc,这个文件安装gammu-smsd后就有了,重点是修改这几行:
[gammu]
port = /dev/ttyUSB0
connection = at115200
[smsd]
service = files
RunOnReceive = /usr/bin/python3 /home/pi/smsgw/sms2mqtt.py
logfile = /home/pi/smsgw/log/log_smsd.log
# Paths where messages are stored
inboxpath = /home/pi/smsgw/inbox/
outboxpath = /home/pi/smsgw/outbox/
sentsmspath = /home/pi/smsgw/sent/
errorsmspath = /home/pi/smsgw/error/
- 收到短信后的处理程序,也就是上面说的sms2mqtt.py,我根据自己的需要修改如下(把之前的两个文件合并为一个):
#!/usr/bin/env python
# encoding: utf-8
import os
import sys
import requests
import json
import datetime
import subprocess
import paho.mqtt.publish as publish
# MQTT broker
HOST = "10.0.1.1"
PORT = 1883
if __name__ == "__main__":
#----------------------获取短信内容----------------------
numParts = int(os.environ['DECODED_PARTS'])
text = ''
#单条短信内容
if numParts == 0:
text = os.environ['SMS_1_TEXT']
#多条短信内容
else:
text = os.environ['DECODED_0_TEXT']
#发件人
sender = os.environ['SMS_1_NUMBER']
#接收日期和时间就取当前系统时间
recTime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
#完整转发内容
smsContent = '发件人:' + sender + '\n时间:' + recTime + '\n\n' + text
#--------------------转发到MQTT--------------------
publish.single("smsR", smsContent, qos = 1,hostname=HOST,port=PORT)
推送的topic设置为smsR,可以根据喜好随便改,和hass配合好就行。
- 接收daemon开机自启动,/lib/systemd/system/gammu-smsd.service,这个文件安装gammu-smsd之后就有了,不用修改,看一眼就行
- 开机启动,大家应该都知道的,sudo systemctl enable gammu-smsd,启动 start、停止stop等等
到这里,树莓派的功能已经搞好了一半,就是收到短信后通知hass。
那么hass方面呢?当然要配合好才行。hass方面需要做的,就是增加一条自动化:
- id: '1589757859753'
alias: smsFrMQTT
description: ''
trigger:
- platform: mqtt
topic: smsR
condition: []
action:
- data_template:
title: >
收到短信 {{ now().strftime('%Y-%m-%d %H:%M:%S') }}
message: "{{ trigger.payload }}"
service: persistent_notification.create
- data_template:
title: 收到短信
message: "{{ trigger.payload }}"
service: notify.notify
好了!到这儿工作算是完成一半,接收短信的事情没问题了。
--------------------------------------
剩下的一半更简单,因为前面已经把环境都调通了。
- 首先是hass上设置自动化,把原来的发短信的动作稍微修改一下就行,比如:
- data:
payload: 停电了!UPS电池开始放电...
topic: smsT
service: mqtt.publish
再比如:
- data:
payload: 互联网已断开!
topic: smsT
service: mqtt.publish
显然,就是在发生警情的时候,hass用mqtt协议向树莓派发送请求,标题用smsT(当然可以随便改,与树莓派那边匹配就好)。
- 树莓派这边需要另外一个长期运行的进程,监听hass发过来的mqtt消息,并在收到消息后发送短信:
sudo vim /lib/systemd/system/mqtt2smsd.service
[Unit]
Description=daemon for send sms driven by mqtt
[Service]
# Run daemon as root user
ExecReload=/bin/kill -HUP $MAINPID
ExecStopPost=/bin/rm -f /var/run/mqtt2smsd.pid
Type=simple
PIDFile=/var/run/mqtt2smsd.pid
#ExecStartPre=/bin/sleep 30
ExecStart=/usr/bin/python /home/pi/smsgw/mqtt2sms.py --pid /var/run/mqtt2smsd.pid &
[Install]
WantedBy=multi-user.target
- 这个也需要sudo systemctl enable mqtt2smsd.service,然后start一下。。。其中指明的进程代码文件mqtt2sms.py内容如下:
#!/usr/bin/env python
# encoding: utf-8
import paho.mqtt.client as mqtt
import time
import gammu.smsd
import sys
HOST = "10.0.1.1" #自己的mqtt broker
PORT = 1883
receptNum = '18600000000' #自己的电话号码
smsd = gammu.smsd.SMSD('/etc/gammu-smsdrc')
def client_loop():
client_id = time.strftime('%Y%m%d%H%M%S',time.localtime(time.time()))
client = mqtt.Client(client_id) # ClientId不能重复,所以使用当前时间
client.username_pw_set("", "") # 必须设置,否则会返回「Connected with result code 4」
client.on_connect = on_connect
client.on_message = on_message
client.connect(HOST, PORT, 60)
client.loop_forever()
def on_connect(client, userdata, flags, rc):
# print("Connected with result code "+str(rc))
client.subscribe("smsT")
def on_message(client, userdata, msg):
sendContent = msg.payload.decode("utf-8")
#print(sendContent)
message = {
'Text': sendContent,
'SMSC': {'Location': 1},
'Number': receptNum,
'Coding': 'Unicode_No_Compression'
}
#print(message)
smsd.InjectSMS([message])
if __name__ == '__main__':
client_loop()
需要强调的是,由于负责短信接收的gammu-smsd已经打开了所配置的虚拟串口/dev/ttyUSB0,这时候再用gammu官网示例代码类似的方法发送短信就不行了,改用注入到smsd才行(或者用另外的端口号比如ttyUSB2或许也行)。
- 回到 /home/pi目录,创建log error inbox outbox sent等子目录,安装依赖软件包pip3 install paho-mqtt,总的来说缺啥补啥就行。
当然,不用树莓派,虚拟机跑debian也完全可以利用本方法实现同样的功能。
好了,就这么多。
——————————————————————————
我把前两天发的帖子精简了一下,这样就更清晰了。
一共有两个python脚本,分别时 mqtt2sms.py 和 sms2mqtt.py ,看名字就知道干啥的了。
创建一个unit文件(/lib/systemd/system/mqtt2smsd.service)、修改一个配置文件(/etc/gammu-smsdrc)
使能、启动两个service项。
|