之前用tornado实现tcp server与硬件的client的连接,以便下发指令。基于django/tornado与wifi模块,lora设备的TCP/UDP即时通讯
现在考虑到天猫精灵可以语音接入。也就用tornado来实现天猫精灵的OAuth2.0.
发了两天空闲的时间去摸索,终于实现成功了。
我的参考文章。
重要的还是官方文档,一开始我也是小白,所以我后面尽量讲的详细一点,因为开始看别人的文章也是一脸懵逼
OAuth2.0复杂就在于我们的站点与天猫之间的几次来回的跳转,详细的可以去看看文档里面的图和上面文章的图,理解跳转之后,就好办多了,在下面的代码中,我也会讲清楚里面的跳转
先上天猫精灵的配置图
tornado中url对应的配置
等我们配置好之后,点击上图的账户配置
就会向天猫精灵的配置图
中的账户授权链接
发起一个get
请求(文档中的(A) 用户打开授权链接进行授权
)
天猫精灵先请求1获取到code,然后在请求2.成功完成认证过程
下图的1处代码进行处理会回把code
传回天猫精灵,然后天猫精灵带着code
会再次请求这个OAuth2Home
url.然后下面图中2处的代码并接url重定向回去。
最后的,天猫精灵会带着code
post
文档中的(B) 通过code换取合作方访问令牌
我们的天猫精灵配置里面的Access Token URL
也就是我们的Token
最后都没问题的话,会出现这样的画面
跳转之后,天猫精灵会post我们的RevTmCommand
,然后就可以根据自己的需要进行响应。
里面还又很多地方需要修改,比如随机生成code,Token,验证Token,client_id,client_sercert,点击天猫精灵的账户配置应该先跳到一个是否授权的页面。
天猫精灵POST我们的RevTmCommand
,初始话我们要返回设备列表回去
class RevTmCommand(web.RequestHandler):
async def post(self):
dicts = eval(self.request.body.decode('utf-8'))
if dicts['header']['namespace'] == "AliGenie.Iot.Device.Discovery":
print('发现设备')
data = '''{
"header":{
"namespace":"AliGenie.Iot.Device.Discovery",
"name":"DiscoveryDevicesResponse",
"messageId":"1bd5d003-31b9-476f-ad03-71d471922820",
"payLoadVersion":1
},
"payload":{
"devices":[
{
"deviceId":"fan-2",
"deviceName":"2号风扇",
"deviceType":"fan",
"zone":"",
"brand":"",
"model":"",
"icon":"https://git.cn-hangzhou.oss-cdn.aliyun-inc.com/uploads/aicloud/aicloud-proxy-service/41baa00903a71c97e3533cf4e19a88bb/image.png",
"properties":[{
"name":"color",
"value":"Red"
}
],
"actions":[
"TurnOn",
"TurnOff",
"SetBrightness",
"AdjustBrightness",
"SetTemperature",
"Query"
],
"extensions":{
"extension1":"",
"extension2":""
}
}]
}
}'''
self.write(data)
elif dicts['header']['namespace'] == "AliGenie.Iot.Device.Control":
name = dicts['header']['name']
messageId = dicts['header']['messageId']
deviceId = dicts['payload']['deviceId']
if name == 'TurnOn':
print('开')
send_data = '''{'device_name': 'fan-lamp-curtain', 'class': 'G406', '%s': '1'}''' % deviceId
elif name == 'TurnOff':
print('关')
send_data = '''{'device_name': 'fan-lamp-curtain', 'class': 'G406', '%s': '0'}'''% deviceId
# try:
print(send_data)
send_data = eval(send_data)
if isinstance(send_data, dict):
if send_data['device_name'] + send_data['class'] in TCP_CONNECTION.keys():
await TCP_CONNECTION[send_data['device_name'] + send_data['class']].write(
bytes(str(send_data), encoding='utf-8'))
return_data = {
"header":{
"namespace":"AliGenie.Iot.Device.Control",
"name":"TurnOnResponse",
"messageId": messageId,
"payLoadVersion":1
},
"payload":{
"deviceId": deviceId
}
}
print('下发成功')
self.write(return_data)
else:
return_data = {
"header":{
"namespace":"AliGenie.Iot.Device.Control",
"name":"ErrorResponse",
"messageId": messageId,
"payLoadVersion":1
},
"payload":{
"deviceId": deviceId,
"errorCode":"DEVICE_NOT_SUPPORT_FUNCTION",
"message": "设备tcp未连接"
}
}
print('下发失败')
self.write(return_data)
else:
return_data = {
"header":{
"namespace":"AliGenie.Iot.Device.Control",
"name":"ErrorResponse",
"messageId": messageId,
"payLoadVersion":1
},
"payload":{
"deviceId": deviceId,
"errorCode":"DEVICE_NOT_SUPPORT_FUNCTION",
"message": "命令有错"
}
}
print('指令有错')
self.write(return_data)
初始化我设置了一个风扇,然后我们在手机上测试,在手机我们与天猫精灵对话,“打开2号风扇,然后天猫那边会post一个请求到我们的RevTmCommand
,格式与文档中的一样,然后我们就用TCP下发指令。
技能设置
意图
意图里面的内容(其中普通语料和连续对话语料填写的信息相同)
回复配置
URL:https://域名/WebHook
下载证书,不要更改名字.稍后我们用nginx来配置.
class WebHook(web.RequestHandler):
"""
天猫技能接口
"""
async def post(self):
return_dict = {
"returnCode": "0",
"returnErrorSolution": "",
"returnMessage": "",
"returnValue": {
"reply": "好的",
"resultType": "RESULT",
"actions": [
{
"name": "audioPlayGenieSource",
"properties": {
"audioGenieId": "123"
}
}
],
"properties": {},
"executeCode": "SUCCESS",
"msgInfo": ""
}
}
get_json = self.request.body.decode('utf-8')
logging.info(get_json)
get_json = get_json.replace('true', '1')
dicts = eval(get_json.replace('false', '0'))
# print(/)
if dicts['intentName'] == '参观':
logging.info('参观')
json_info = {'device_name': 'android', 'class': 'D712'}
if json_info['device_name'] + json_info['class'] in TCP_CONNECTION.keys():
logging.info('TCP_CONNECTION:{}'.format(TCP_CONNECTION))
await TCP_CONNECTION[json_info['device_name'] + json_info['class']].write(
bytes(str('start'), encoding='utf-8'))
# return_data['returnValue']['resultType'] = ''
logging.info("好的{}".format(return_dict))
self.write(return_dict)
else:
return_dict['returnValue']['reply'] = "安卓设备未连接"
logging.info("安卓设备未连接{}".format(return_dict))
self.write(return_dict)
else:
return_dict['returnValue']['reply'] = "非参观意图"
logging.info('非{}'.format(return_dict))
self.write(return_dict)
WebHook证书nginx配置
天猫精灵要求要https,
server {
listen 80;
server_name 你的域名;
rewrite ^(.*)$ https://$host$1 permanent;
# 匹配天猫精灵语音技能webhook证书
}
测试结果:
出现success为成功,返回“安卓设备未连接”是我们自己项目自定义的。