这篇主要说说如何通过微信公众号来查看室内传感器数据,至于硬件部分和物联网平台以后再详细说明。
准备工作:
1:申请微信公众号
2:搭建云服务器
首先说明一下整体流程:用户发送相应的指令到公众号后台,服务器根据指令的内容调用OneNET的API获取传感器数据在返回给用户
详细步骤:
申请公众号后我们需要启用服务器配置,具体步骤请看微信的开发者文档,这个地方需要注意一下在填写URL的时候不要添加端口号,这样会导致验证不通过的(开发者文档上这个是错误的)
在云服务器上安装运行环境:
安装pip
安装libxml2
安装lxml
安装web.py
我们使用的物联网平台是中国移动的OneNet,它提供了很多API接口可以让我们获取数据、发送指令,在GitHub上有人用python写了常用的API调用示例,这里我们拿来直接使用就好了https://github.com/jiangxiaobai1989/pythonOneNetAPI
首先呢我们需要能够接收用户发过来的消息,使用者发送消息后后台收到的为lxml格式
<xml>
<ToUserName><![CDATA[粉丝号]]></ToUserName>
<FromUserName><![CDATA[公众号]]></FromUserName>
<CreateTime>1460541339</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[test]]></Content>
</xml>
然后呢我们需要解析这些内容
# -*- coding: utf-8 -*-
# filename: receive.py
import xml.etree.ElementTree as ET
def parse_xml(web_data):
if len(web_data) == 0:
return None
xmlData = ET.fromstring(web_data)
msg_type = xmlData.find('MsgType').text
if msg_type == 'text':
return TextMsg(xmlData)
elif msg_type == 'image':
return ImageMsg(xmlData)
elif msg_type == 'voice':
return VoiceMsg(xmlData)
class Msg(object):
def __init__(self, xmlData):
self.ToUserName = xmlData.find('ToUserName').text
self.FromUserName = xmlData.find('FromUserName').text
self.CreateTime = xmlData.find('CreateTime').text
self.MsgType = xmlData.find('MsgType').text
self.MsgId = xmlData.find('MsgId').text
class TextMsg(Msg):
def __init__(self, xmlData):
Msg.__init__(self, xmlData)
self.Content = xmlData.find('Content').text.encode("utf-8")
class VoiceMsg(Msg):
def __init__(self, xmlData):
Msg.__init__(self, xmlData)
self.Recognition = xmlData.find('Recognition').text.encode("utf-8")
获取消息后我们需要服务器做出相应的反应,首先需要判断消息类型和消息内容,然后通过API获取数据后再返回个用户,例如下面这段获取室内温湿度的例子
# -*- coding: utf-8 -*-
# filename: handle.py
import hashlib
import reply
import receive
import web
from getData import *
class Handle(object):
def POST(self):
try:
webData = web.data()
print "Handle Post webdata is ", webData #后台打日志
recMsg = receive.parse_xml(webData)
if isinstance(recMsg, receive.Msg):
toUser = recMsg.FromUserName
fromUser = recMsg.ToUserName
if recMsg.MsgType == 'text':
if recMsg.Content == '温度':
content = str(getData_time('temperature'))+'
室内温度为'+str(getData_value('temperature')) +'℃'
elif recMsg.Content == '湿度':
content = str(getData_time('humidity'))+'
室内湿度为'+str(getData_value('humidity')) +'%'
else:
content = '抱歉尚未开通这项指令功能,你可以尝试发送‘温度’、‘湿度’来查看最新的室内信息,或者发送相应的语音消息 '
replyMsg = reply.TextMsg(toUser, fromUser, content)
return replyMsg.send()
if recMsg.MsgType == 'voice':
if recMsg.Recognition =='温度。':
content = str(getData_time('temperature'))+'
室内温度为'+str(getData_value('temperature')) +'℃'
elif recMsg.Recognition =='湿度。':
content = str(getData_time('humidity'))+'
室内湿度为'+str(getData_value('humidity')) +'%'
else:
content =recMsg.Recognition+'
无法识别这条语音消息'
replyMsg = reply.TextMsg(toUser, fromUser, content)
return replyMsg.send()
else:
return reply.Msg().send()
else:
print "暂且不处理"
return reply.Msg().send()
except Exception, Argment:
return Argment
通过API调用我们获取到的json数据,这样是不能直接给用户发送过去的,还需要对json进行处理提取主要的数据,例如提取温湿度数据和数据节点时间
# -*- coding: UTF-8
from OneNetApi import *
import json
def getData_value(datastreamid):
test = OneNetApi("***************************") # your API
datastream_id = datastreamid
limit = 1
res3 = test.datapoint_get(device_id = "6975064", limit = limit, datastream_id = datastream_id)
data = json.loads(res3.content.replace(']',' ').replace('[',' '))
value = data['data']['datastreams']['datapoints']['value']
return value
def getData_time(datastreamid):
test = OneNetApi("***************************") # your API
datastream_id = datastreamid
limit = 1
res3 = test.datapoint_get(device_id = "6975064", limit = limit, datastream_id = datastream_id)
data = json.loads(res3.content.replace(']',' ').replace('[',' '))
time = data['data']['datastreams']['datapoints']['at'][0:19]
return time
至于返回用户消息呢,依然是按照lxml格式,将我们获取到的数据和需要返回的用户信息添加进去就可以了
class TextMsg(Msg):
def __init__(self, toUserName, fromUserName, content):
self.__dict = dict()
self.__dict['ToUserName'] = toUserName
self.__dict['FromUserName'] = fromUserName
self.__dict['CreateTime'] = int(time.time())
self.__dict['Content'] = content
def send(self):
XmlForm = """
<xml>
<ToUserName><![CDATA[{ToUserName}]]></ToUserName>
<FromUserName><![CDATA[{FromUserName}]]></FromUserName>
<CreateTime>{CreateTime}</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[{Content}]]></Content>
</xml>
"""
return XmlForm.format(**self.__dict)
微信后天还提供了语音识别接口,默认是打开的,当用户发送的是语音命令的时候后台接收的lxml中会比text消息多出Recognition这项,把识别结果当做text一样处理就能让公众号处理语音消息了,再次不再赘述了。
至此整个流程就结束了,当编写所有的代码后使用 python main.py 80 即可打开这项服务了,下面是效果图
国际惯例:源码