From: snickersun Date: Thu, 21 May 2026 01:35:11 +0000 (+0800) Subject: ota地址更新,地址 配置在 default_srv.py中 X-Git-Url: http://git.snicker.com.cn/?a=commitdiff_plain;h=d555c455dbea9440bd397cd54ea7581cab689998;p=mirrors%2FASR1603_G5.git ota地址更新,地址 配置在 default_srv.py中 --- diff --git "a/1.doc/K8_diaozhui/design/\344\270\211\344\273\243IOT\350\256\276\350\256\241\346\226\207\346\241\243 \357\274\210\347\256\200\345\214\226\347\211\210\357\274\211.pdf" "b/1.doc/K8_diaozhui/design/\344\270\211\344\273\243IOT\350\256\276\350\256\241\346\226\207\346\241\243 \357\274\210\347\256\200\345\214\226\347\211\210\357\274\211.pdf" new file mode 100644 index 00000000..82eeb234 Binary files /dev/null and "b/1.doc/K8_diaozhui/design/\344\270\211\344\273\243IOT\350\256\276\350\256\241\346\226\207\346\241\243 \357\274\210\347\256\200\345\214\226\347\211\210\357\274\211.pdf" differ diff --git "a/1.doc/K8_diaozhui/design/\344\270\211\344\273\243IOT\350\256\276\350\256\241\346\226\207\346\241\243\357\274\210\347\256\200\345\214\226\347\211\210\357\274\211.md" "b/1.doc/K8_diaozhui/design/\344\270\211\344\273\243IOT\350\256\276\350\256\241\346\226\207\346\241\243\357\274\210\347\256\200\345\214\226\347\211\210\357\274\211.md" new file mode 100644 index 00000000..773b353c --- /dev/null +++ "b/1.doc/K8_diaozhui/design/\344\270\211\344\273\243IOT\350\256\276\350\256\241\346\226\207\346\241\243\357\274\210\347\256\200\345\214\226\347\211\210\357\274\211.md" @@ -0,0 +1,2054 @@ +# 三代 IOT 设计文档(简化版) + +> 由 PDF 自动提取:`1.doc/K8_diaozhui/design/三代IOT设计文档 (简化版).pdf`(30 页) +> 提取方式:PyMuPDF 文本导出;页眉页脚与版式可能有残留字符。 + +## 目录(与固件版本相关) + +| 章节 | 指令 | 是否含 `version` | +|------|------|------------------| +| 设备 WebSocket 上报 | `device_info` | **是**(`data.version`) | +| 设备 WebSocket 上报 | `post_heart_beat` | 否(仅 `battery`) | +| 平台下发 | `get_device_state` | 触发设备回 `device_info` | +| 小程序 WebSocket 推送 | `push_device_info_rest` | **是**(平台转发 `device_info` 结果) | +| HTTP 查询 | `/ws-bus/query/device-state/{deviceId}` | 文档示例**无** `version` 字段 | + +--- + +## Page 1 + +三代IOT设计文档 (简化版)​ +概述​ +IOT物联网云平台:实现设备连接、消息上报。​ +业务通讯架构​ +IOT云平台​ +Websocket基础通讯协议​ +Json指令协议​ +字段​ +magic​ +version​ +type​ +playload_le +n​ +payload​ +## Page 2 + +说明​ +固定0xFFFE​ +0x01​ +参考类型说明​ +载荷长度​ +载荷​ +大小​ +2byte​ +1byte​ +1byte​ +4byte​ +N​ +type类型说明​ +类型值​ +类型名称​ +描述​ +0x00​ +设备登录​ +废弃,通过url中携带bz-dt来登录​ +0x01​ +用户登录​ +废弃,通过url中携带bz-ut来登录​ +0x02​ +心跳​ +废弃,通过Json来心跳即可​ +0x03​ +json内容​ +具体的内容,请参考协议内容,包括小程序与iot的,设备与iot的。​ +数据流格式​ +字段​ +magic​ +version​ +type​ +msgId​ +flag​ +playload_le +n​ +payload​ +说明​ +固定0xFFFE​ +​ +0x01​ +​ +参考类型说 +明​ +​ +消息ID​ +​ +标记0x00结 +束,0x01未 +结束​ +载荷长度​ +载荷​ +大小​ +2byte​ +1byte​ +1byte​ +4byte​ +1byte​ +4byte​ +N​ +类型说明​ +类型值​ +类型名称​ +描述​ +0x04​ +下行监听音频​ +PCM/WAV/MP3流,由产品协商​ +0x05​ +上行监听音频​ +PCM/WAV/MP3流,由产品协商​ +0x06​ +图片下行​ +图片内容,格式由产品协商​ +0x07​ +图片上行​ +图片内容,格式由产品协商​ +0x08​ +上行留言语音​ +PCM/WAV/MP3流,由产品协商​ +连接认证​ +## Page 3 + +在header上或者query上:设备携带token值,header上key是`authorization`。query上是:bz- +ut/bz-dt,其中bz-ut是用户令牌,bz-dt是设备令牌。​ +访问地址​ +代码块​ +测试环境设备连接地址:ws(s)://dmp-dev.raisound.com/iot-api/ws-hub/dev/v1?bz-dt=​ +测试环境小程序连接地址:wss://dmp-dev.raisound.com/iot-api/ws-hub/user/v1?bz-ut​ +生产环境设备连接地址:ws(s)://wsiot-nocdn.raisound.com/ws-hub/dev/v1?bz-dt=​ +生产环境小程序连接地址:wss://wsiot-nocdn.raisound.com/ws-hub/user/v1?bz-ut=​ +(CDN)生产环境设备连接地址:ws(s)://wsiot.raisound.com/ws-hub/dev/v1?bz-dt=​ +(CDN)生产环境小程序连接地址:wss://wsiot.raisound.com/ws-hub/user/v1?bz-ut=​ +query需要携带设备令牌key为:bz-dt,用户令牌key为:bz-ut​ +Json指令数据结构​ +传输的数据由json格式进行传输。文件/图片/音频等数据传到对象存储中,然后把连接以指令的方式传 +给设备,由设备根据对象地址进行获取使用。​ +上报数据和下发数据的数据结构:​ +代码块​ +{​ + "cmd":"命令字符串",​ + "ts":1738283829​ + "msg":"消息说明",​ + "data":"JsonObject类型的数据对象"​ +}​ +• +根据指令使用data里的数据​ +• +data里的数据是个JsonObject​ +• +设备ID会根据header上的token进行解析​ +NLP与IOT的http协议内容​ +通用指令接口✅​ +请求路径​ +/ws-bus/cmd/ai​ +应用场景​ +转发命令字符串给设备​ +1 +2 +3 +4 +5 +6 +1 +2 +3 +4 +5 +6 +## Page 4 + +请求方法​ +POST​ +请求参数​ +​ +• +Query​ +◦ +deviceId:设备ID​ +◦ +deviceToken:设备token​ +◦ +cmd:ai_cmd​ +◦ +data:指令数据​ +响应结果​ +• +下发失败​ +代码块​ +{​ + "success": false,​ + "msg": "设备不在线.",​ + "code": 50000,​ + "data": null​ +}​ +• +下发成功​ +代码块​ +{​ + "success": true,​ + "msg": "下发成功.",​ + "code": 20000,​ + "data": null​ +}​ +​ +设备接收指令​ +代码块​ +{​ + "cmd": "ai_cmd",​ + "msg": "AI指令:ai_cmd",​ + "data": "指令数据",​ + "ts": 1761153310392,​ + "targetDeviceId": "E405F8648007"​ +}​ +1 +2 +3 +4 +5 +6 +1 +2 +3 +4 +5 +6 +1 +2 +3 +4 +5 +6 +7 +设备-IOT的WebSocket内容​ +## Page 5 + +设备与iot云平台走的是websocket协议。​ +心跳✓​ +协议名称​ +心跳​ +应用场景​ +心跳,不超过5分钟即可,响应的bind表示已绑定用户,unbind表示未绑定,unbind的情况下 +需要打开蓝牙,供app扫描绑定。​ +指令​ +post_heart_beat​ +上报内容​ +代码块​ +{​ + "cmd":"post_heart_beat",​ + "ts":1738283829,​ + "msg":"心跳",​ + "data":{​ + "battery": 20​ + }​ +}​ +响应内容​ +代码块​ +{​ + "cmd":"post_rest",​ + "ts":1738283829,​ + "msg":"消息收到响应",​ + "data":{​ + "cmd":"post_heart_beat",​ + "rest":"bind/unbind",​ + "msg":"上报成功/格式错误"​ + }​ +}​ +1 +2 +3 +4 +5 +6 +7 +8 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +接收指令​ +下发用户解绑✓​ +协议名称​ +用户解绑设备推送​ +应用场景​ +设备收到这个指令以后,广播蓝牙,让小程序可以搜索到设备进行绑定​ +## Page 6 + +指令​ +unbind_dev​ +下发内容​ +​ +代码块​ +{​ + "cmd":"user_unbind_dev",​ + "ts":1738283829,​ + "msg":"解绑设备",​ + "data":null​ +}​ +响应内容​ +​ +代码块​ +{​ + "cmd":"dispatch_rest",​ + "ts":1738283829,​ + "msg":"消息收到响应",​ + "data":{​ + "cmd":"user_unbind_dev",​ + "rest":"success|error",​ + "msg":"收到用户解绑设备指令"​ + }​ +}​ +1 +2 +3 +4 +5 +6 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +下发查找设备✓​ +指令​ +查找设备find_device​ +指令说明​ +收到该指令后,响铃 震动​ +指令内容​ +​ +代码块​ +{​ + "cmd": "find_device",​ + "msg": "查找设备",​ + "data": null,​ + "ts": 1759328001026,​ + "targetDeviceId": "E405F8F71009"​ +}​ +1 +2 +3 +4 +5 +6 +7 +## Page 7 + +下发音量修改✓​ +指令​ +音量修改switch_volume​ +指令说明​ +收到该指令后,对设备音量进行修改​ +指令内容​ +​ +代码块​ +{​ + "cmd":"switch_volume",​ + "data":{​ + "target":"+增加音量;-减少音量;[0,100]修改音量为特定百分比 +值"​ + }​ +}​ +1 +2 +3 +4 +5 +6 +下发开始声音克隆✓​ +指令​ +开始声音克隆start_voice_clone​ +指令说明​ +收到该指令后,开始声音克隆​ +指令内容​ +​ +代码块​ +{​ + "cmd": "start_voice_clone",​ + "msg": "开始声音克隆",​ + "data": null,​ + "ts": 1759329865731,​ + "targetDeviceId": "E405F8F71009"​ +}​ +1 +2 +3 +4 +5 +6 +7 +下发获取获取设备状态✓​ +指令​ +get_device_state​ +应用场景​ +获取设备状态,状态会在推送中得到;调用时机,当UI需要电量、音量、wifi信号强弱时调 +用。​ +请求内容​ +代码块​ +{​ +1 +## Page 8 + + "cmd": "get_device_state",​ + "msg": "获取设备状态",​ + "data": null,​ + "ts": 1759330147313,​ + "targetDeviceId": "E405F8F71009"​ +}​ +2 +3 +4 +5 +6 +7 +下发清除配网✓​ +指令​ +wifi_clean_pair​ +应用场景​ +用户在app上点击清除配网,用于毛绒玩具,没有按键的设备;前提条件是已连接当前网络。​ +请求内容​ +代码块​ +{​ + "cmd": "start_voice_clone",​ + "msg": "开始声音克隆",​ + "data": null,​ + "ts": 1759329865731,​ + "targetDeviceId": "E405F8F71009"​ +}​ +1 +2 +3 +4 +5 +6 +7 +解绑​ +指令​ +device_unbind​ +应用场景​ +用户解绑设备时,下发这个指令,然后设备清除配网,清除本地数据,打开蓝牙广播。​ +请求内容​ +代码块​ +{​ + "cmd":"device_unbind",​ + "data":null​ +}​ +1 +2 +3 +4 +socket通道上报​ +当前服务状态上传✓​ +(聆听中|播放中|空闲中|思考中)​ +## Page 9 + +指令​ +chat_state​ +指令说明​ +当前对话状态的上传​ +上报内容​ +​ +代码块​ +{​ + "msg": "当前服务状态上传",​ + "data": {​ + "state": "listening|speaking|idle|thinking",​ + "deviceId": "E405F8F71009"​ + },​ + "cmd": "chat_state",​ + "ts": 1759979110​ +}​ +响应内容​ +代码块​ +{​ + "cmd": "post_rest",​ + "ts": 1759979110661,​ + "data": {​ + "cmd": "chat_state",​ + "rest": "success",​ + "msg": "收到服务状态上报结果."​ + }​ +}​ +1 +2 +3 +4 +5 +6 +7 +8 +9 +1 +2 +3 +4 +5 +6 +7 +8 +9 +上报对话文本(用于显示对话)✓​ +指令​ +chat_text​ +指令说明​ +对话内容上传​ +上报内容​ +​ +代码块​ +{​ + "msg": "上报对话文本",​ + "data": [​ + {​ + "deviceId": "E405F8F71009",​ + "type": "req", //req:请求说的话​ + "text": "今天天气如何?",​ + "createTime": 1759979306​ +1 +2 +3 +4 +5 +6 +7 +8 +## Page 10 + + },​ + {​ + "deviceId": "E405F8F71009",​ + "type": "res", //res:响应的内容​ + "text": "今天暴雨,记得要带伞哦.?",​ + "createTime": 1759979306​ + }​ + ],​ + "cmd": "chat_text",​ + "ts": 1759979306​ +}​ +响应内容​ +代码块​ +{​ + "cmd": "post_rest",​ + "ts": 1759977562040,​ + "data": {​ + "cmd": "chat_text",​ + "rest": "success",​ + "msg": "收到对话文本结果."​ + }​ +}​ +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +1 +2 +3 +4 +5 +6 +7 +8 +9 +上报设备信息(电量,Wi-Fi信号强度)✓​ +指令​ +device_info​ +指令说明​ +设备信息上报​ +上报内容​ +​ +代码块​ +{​ + "msg": "上报设备信息",​ + "data": {​ + "deviceId": "E405F8F71009",​ + "battery": "电池,百分比值,比如说30,25",​ + "wifiRssi": "wifi强弱:0,1,2,3,4,5。转成这几个梯度",​ + "volume": "百分比数值",​ + "bargein": "打断模式:0/1",​ + "currentRoleName": "当前音色的name",​ + "charge": "0表示未充电,1表示充电中",​ + "version": "版本号"​ + },​ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +## Page 11 + + "cmd": "device_info",​ + "ts": 1759979415​ +}​ +响应内容​ +代码块​ +{​ + "cmd": "post_rest",​ + "ts": 1759975798004,​ + "data": {​ + "cmd": "device_info",​ + "rest": "success",​ + "msg": "收到设备信息结果."​ + }​ +}​ +13 +14 +15 +1 +2 +3 +4 +5 +6 +7 +8 +9 +获取智能体ID(主动上报)✓​ +指令​ +get_bot​ +指令说明​ +获取智能体配置,会在set_bot指令响应​ +上报内容​ +​ +代码块​ +{​ + "msg": "获取智能体ID",​ + "data": null,​ + "cmd": "get_bot",​ + "ts": 1759979520​ +}​ +响应内容​ +代码块​ +{​ + "cmd": "set_bot",​ + "msg": "简单设备信息",​ + "data": {​ + "agent": {​ + "name": "孙悟空",​ + "speakerId": "zh_male_sunwukong_mars_bigtts",​ + "id": "sunwukong",​ + "speakerType": "default"​ + },​ +1 +2 +3 +4 +5 +6 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +## Page 12 + + "speaker": {​ + "name": "猴哥",​ + "language": "中文",​ + "id": "zh_male_sunwukong_mars_bigtts",​ + "type": "default"​ + },​ + "bargein": null,​ + "memoryId": null,​ + "longTermMemory": null​ + },​ + "ts": 1759974598622,​ + "targetDeviceId": "E405F8F71009"​ +}​ +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +小程序与IOT的http协议内容​ +小程序与iot云平台:指令下发,数据回显,走的是Https协议。​ +参考文档:http://118.145.159.222:30700/doc.html​ +下发指令​ +通用接口✓​ +请求路径​ +/ws-bus/cmd/{deviceId}​ +请求方法​ +POST​ +请求参数​ +• +path​ +◦ +deviceId 设备Id​ +• +Query​ +◦ +cmd:指令内容​ +◦ +data:下发数据​ +响应结果​ +代码块​ +{​ + "success": true,​ + "msg": "下发成功.",​ + "code": 20000,​ + "data": null​ +}​ +1 +2 +3 +4 +5 +6 +## Page 13 + +查找设备✓​ +指令​ +查找设备find_device​ +请求示例​ +• +path​ +◦ +deviceId:E405F8F71009​ +• +Query​ +◦ +cmd:find_device​ +◦ +data:​ +应用场景​ +app向设备下发此指令,设备收到后响铃震动,用于找到设备所在的位置​ +设备接收指令 +内容​ +代码块​ +{​ + "cmd": "find_device",​ + "msg": "查找设备",​ + "data": null,​ + "ts": 1759328001026,​ + "targetDeviceId": "E405F8F71009"​ +}​ +1 +2 +3 +4 +5 +6 +7 +音色切换✓​ +指令​ +音色切换switch_accent​ +请求示例​ +• +path​ +◦ +deviceId:E405F8F71009​ +• +Query​ +◦ +cmd:switch_accent​ +◦ +data:​ +应用场景​ +修改音色,通过此接口切换设备TTS播报的音色​ +设备接收指令 +内容​ +​ +代码块​ +{​ + "cmd": "switch_accent",​ + "msg": "音色切换",​ + "data": {​ + "target": "1"​ +1 +2 +3 +4 +5 +## Page 14 + + },​ + "ts": 1759328048259,​ + "targetDeviceId": "E405F8F71009"​ +}​ +6 +7 +8 +9 +设置智能体✓​ +指令​ +设置智能体set_bot​ +请求示例​ +• +path​ +◦ +deviceId:E405F8F71009​ +• +Query​ +◦ +cmd:set_bot​ +◦ +data: {"botId": "7462617881076351002", "botName": "小量博士"}​ +应用场景​ +app向设备下发智能体配置,该配置会被后台保存,设备开机时向后台获取上次配置​ +设备接收指令 +内容​ +代码块​ +{​ + "cmd": "set_bot",​ + "msg": "设置智能体",​ + "data": {​ + "bot_name": "小量博士",​ + "bot_id": "7462617881076351002"​ + },​ + "ts": 1759331069327,​ + "targetDeviceId": "E405F8F71009"​ +}​ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +开始声音克隆✓​ +指令​ +开始声音克隆start_voice_clone​ +## Page 15 + +请求示例​ +• +path​ +◦ +deviceId:E405F8F71009​ +• +Query​ +◦ +cmd:start_voice_clone​ +◦ +data:​ +应用场景​ +声音克隆,音色复刻​ +设备接收指令 +内容​ +代码块​ +{​ + "cmd": "start_voice_clone",​ + "msg": "开始声音克隆",​ + "data": null,​ + "ts": 1759329865731,​ + "targetDeviceId": "E405F8F71009"​ +}​ +1 +2 +3 +4 +5 +6 +7 +清除配网✓​ +指令​ +清除配网 wifi_clean_pair​ +请求示例​ +• +path​ +◦ +deviceId:E405F8F71009​ +• +Query​ +◦ +cmd:wifi_clean_pair​ +◦ +data:​ +应用场景​ +用户在app上点击清除配网,用于毛绒玩具,没有按键的设备;前提条件是已连接当前网络。​ +设备接收指令 +内容​ +代码块​ +{​ + "cmd": "wifi_clean_pair",​ + "msg": "清除配网",​ + "data": null,​ + "ts": 1759329910033,​ + "targetDeviceId": "E405F8F71009"​ +}​ +1 +2 +3 +4 +5 +6 +7 +## Page 16 + +获取设备状态✓​ +指令​ +get_device_state​ +请求示例​ +• +path​ +◦ +deviceId:E405F8F71009​ +• +Query​ +◦ +cmd:get_device_state​ +◦ +data:​ +应用场景​ +获取设备状态,状态会在推送中得到;调用时机,当UI需要电量、音量、wifi信号强弱时调 +用。​ +设备接收指令 +内容​ +代码块​ +{​ + "cmd": "get_device_state",​ + "msg": "获取设备状态",​ + "data": null,​ + "ts": 1759330147313,​ + "targetDeviceId": "E405F8F71009"​ +}​ +1 +2 +3 +4 +5 +6 +7 +更新设备✓​ +指令​ +让设备重新获取配置信息renew_device​ +请求示例​ +• +path​ +◦ +deviceId:E405F8F71009​ +• +Query​ +◦ +cmd:renew_device​ +◦ +data:​ +应用场景​ +app向设备下发此指令,设备收到后提示“更新配置中,设备将自动重启”,设备从dmp接口 +获取设备的配置信息,并重新发起对话​ +设备接收指令 +内容​ +​ +代码块​ +{​ + "cmd": "renew_device",​ + "msg": "更新设备",​ +1 +2 +3 +## Page 17 + + "data": null,​ + "ts": 1759330208898,​ + "targetDeviceId": "E405F8F71009"​ +}​ +4 +5 +6 +7 +更新配置角色-dmp平台的角色✓​ +指令​ +通知设备更新了角色配置 setup_role​ +请求示例​ +• +path​ +◦ +deviceId:E405F8F71009​ +• +Query​ +◦ +cmd:setup_role​ +◦ +data:​ +应用场景​ +app向设备下发此指令,设备收到后提示“更新配置中”,并重新发起对话​ +设备接收指令 +内容​ +​ +代码块​ +{​ + "cmd": "setup_role",​ + "msg": "更新配置角色",​ + "data": null,​ + "ts": 1759330265367,​ + "targetDeviceId": "E405F8F71009"​ +}​ +1 +2 +3 +4 +5 +6 +7 +设置打断模式✓​ +指令​ +set_break_off​ +请求示例​ +• +path​ +◦ +deviceId:E405F8F71009​ +• +Query​ +◦ +cmd:set_break_off​ +◦ +data:1​ +应用场景​ +用户在app上选择打断模式。​ +## Page 18 + +设备接收指令 +内容​ +代码块​ +{​ + "cmd": "set_break_off",​ + "msg": "设置打断模式",​ + "data": {​ + "target": "1:按键打断;2:插话打断"​ + },​ + "ts": 1759330363089,​ + "targetDeviceId": "E405F8F71009"​ +}​ +1 +2 +3 +4 +5 +6 +7 +8 +9 +设置亮度✓​ +指令​ +亮度修改switch_brightness​ +请求示例​ +• +path​ +◦ +deviceId:E405F8F71009​ +• +Query​ +◦ +cmd:switch_brightness​ +◦ +data:50​ +指令说明​ +用户在app上滑动亮度调节。​ +设备接收指令 +内容​ +​ +代码块​ +{​ + "cmd": "switch_brightness",​ + "msg": "设置亮度",​ + "data": {​ + "target": "[0,100]修改亮度为特定百分比值"​ + },​ + "ts": 1759330840342,​ + "targetDeviceId": "E405F8F71009"​ +}​ +1 +2 +3 +4 +5 +6 +7 +8 +9 +设置音量✓​ +指令​ +设置音量修改switch_volume​ +请求示例​ +• +path​ +## Page 19 + +◦ +deviceId:E405F8F71009​ +• +Query​ +◦ +cmd:switch_volume​ +◦ +data:+20​ +指令说明​ +收到该指令后,对设备音量进行修改​ +设备接收指令 +内容​ +​ +代码块​ +{​ + "cmd": "switch_volume",​ + "msg": "音量修改",​ + "data": {​ + "target":"+增加音量;-减少音量;[0,100]修改音量为特定百分比 +值"​ + },​ + "ts": 1759331616977,​ + "targetDeviceId": "E405F8F71009"​ +}​ +1 +2 +3 +4 +5 +6 +7 +8 +9 +查询接口​ +获取设备的在线状态✓​ +接口名称​ +获取设备的在线状态​ +业务说明​ +小程序APP在打开首页的时候,先检查设备的在线状态。如果不在线,显示配网按钮​ +请求路径​ +/ws-bus/query/device-state/{deviceId}​ +请求方法​ +GET​ +请求参数​ +• +path​ +◦ +deviceId 设备Id​ +响应结果​ +​ +• +设备不在线​ +代码块​ +{​ + "success": true,​ + "msg": "获取设备状态成功.",​ + "code": 20000,​ + "data": {​ +1 +2 +3 +4 +5 +## Page 20 + + "id": "68d4e5acbe41c865bbf8055d",​ + "deviceId": "E405F8F71009",​ + "battery": 1,​ + "mediaState": "stop",​ + "mediaTitle": null,​ + "deviceState": "offline",​ + "volume": 50,​ + "mediaCover": null,​ + "createTime": 1758782892683,​ + "updateTime": 1758782892683​ + }​ +}​ +• +无权限获取​ +代码块​ +{​ + "timestamp": "2025-10-01 21:36",​ + "status": 403,​ + "error": "Forbidden",​ + "path": "/query/device-state/E405F8F71001"​ +}​ +• + 设备在线​ +代码块​ +{​ + "success": true,​ + "msg": "获取设备状态成功.",​ + "code": 20000,​ + "data": {​ + "id": "68d4e5acbe41c865bbf8055d",​ + "deviceId": "E405F8F71009",​ + "battery": 1,​ + "mediaState": "stop",​ + "mediaTitle": null,​ + "deviceState": "online",​ + "volume": 50,​ + "mediaCover": null,​ + "createTime": 1758782892683,​ + "updateTime": 1758782892683​ + }​ +}​ +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +1 +2 +3 +4 +5 +6 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +## Page 21 + +IOT与小程序的websocket协议​ +iot平台推送数据给小程序,走的是websocket协议,传输的内容参考以下格式:​ +心跳✓​ +协议名称​ +心跳​ +应用场景​ +心跳,不超过1分钟即可​ +指令​ +post_heart_beat​ +上传内容​ +代码块​ +{​ + "cmd":"post_heart_beat",​ + "ts":1738283829,​ + "msg":"心跳",​ + "data":null​ +}​ +响应内容​ +代码块​ +{​ + "cmd":"post_rest",​ + "ts":1738283829,​ + "msg":"消息收到响应",​ + "data":{​ + "cmd":"post_heart_beat",​ + "rest":"success|error",​ + "msg":"上报成功/格式错误"​ + }​ +}​ +1 +2 +3 +4 +5 +6 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +接收指令​ +接收设备状态(上线/下线)变化✓​ +协议名称​ +设备状态(上线/下线)变化 +应用场景​ +## Page 22 + +当设备连上iot的websocket,通知用户设备已上线;当设备断开iot的websocket时,通知用户和 +设备已下线​ +指令​ +push_dev_state​ +设备上线通 +知​ +​ +代码块​ +{​ + "cmd": "push_dev_state",​ + "msg": null,​ + "data": {​ + "id": "68d4e5acbe41c865bbf8055d",​ + "deviceId": "E405F8F71009",​ + "battery": 1,​ + "mediaState": "stop",​ + "mediaTitle": null,​ + "deviceState": "online",​ + "volume": 50,​ + "mediaCover": null,​ + "createTime": 1758782892683,​ + "updateTime": 1758782892683​ + },​ + "ts": 1759324958781,​ + "targetDeviceId": "E405F8F71009"​ +}​ +设备下线通 +知​ +​ +代码块​ +{​ + "cmd": "push_dev_state",​ + "msg": null,​ + "data": {​ + "id": "68d4e5acbe41c865bbf8055d",​ + "deviceId": "E405F8F71009",​ + "battery": 1,​ + "mediaState": "stop",​ + "mediaTitle": null,​ + "deviceState": "offline",​ + "volume": 50,​ + "mediaCover": null,​ + "createTime": 1758782892683,​ + "updateTime": 1758782892683​ + },​ + "ts": 1759325118496,​ + "targetDeviceId": "E405F8F71009"​ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +## Page 23 + +}​ +18 +接收当前服务状态✓​ +(聆听中|播放中|空闲中|思考中)​ +指令​ +push_chat_state_rest​ +应用场景​ +设备对话中,把对话的状态上报给后台,后台把状态推送给app​ +请求内容​ +代码块​ +{​ + "cmd": "push_chat_state_rest",​ + "msg": "收到设备服务状态上报结果.",​ + "data": {​ + "deviceId": "E405F8F71009",​ + "state": "listening"​ + },​ + "ts": 1759979110670,​ + "targetDeviceId": "E405F8F71009"​ +}​ +​ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +接收对话文本(用于显示对话)✓​ +指令​ +push_chat_text_rest​ +指令说明​ +当前对话状态的上传​ +上报内容​ +​ +代码块​ +{​ + "cmd": "push_chat_text_rest",​ + "msg": "收到对话文本结果.",​ + "data": [​ + {​ + "deviceId": "E405F8F71009",​ +1 +2 +3 +4 +5 +6 +## Page 24 + + "type": "req",​ + "text": "今天天气如何?",​ + "createTime": 1759979306​ + },​ + {​ + "deviceId": "E405F8F71009",​ + "type": "res",​ + "text": "今天暴雨,记得要带伞哦.?",​ + "createTime": 1759979306​ + }​ + ],​ + "ts": 1759979306740,​ + "targetDeviceId": "E405F8F71009"​ +}​ +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +接收设备信息(电量,Wi-Fi信号强度)✓​ +指令​ +push_device_info_rest​ +指令说明​ +设备信息上报​ +上报内容​ +​ +代码块​ +{​ + "cmd": "push_device_info_rest",​ + "msg": "收到设备信息结果.",​ + "data": {​ + "deviceId": "E405F8F71009",​ + "battery": "电池,百分比值,比如说30,25",​ + "wifiRssi": "wifi强弱:0,1,2,3,4,5。转成这几个梯度",​ + "volume": "百分比数值",​ + "bargein": "打断模式:0/1",​ + "currentRoleName": "当前音色的name",​ + "charge": "0表示未充电,1表示充电中",​ + "version": "版本号"​ + },​ + "ts": 1759979416209,​ + "targetDeviceId": "E405F8F71009"​ +}​ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +SDK​ +## Page 25 + +go版本​ +代码块​ +package test​ +​ +import (​ + "bytes"​ + "context"​ + "encoding/binary"​ + "fmt"​ + "net/http"​ + "os"​ + "sync"​ + "time"​ +​ + ".go_vvvdevice/common"​ + ".go_vvvdevice/log"​ + ".go_vvvdevice/modules"​ + "github.com/gorilla/websocket"​ +)​ +​ +var wg sync.WaitGroup​ +​ +func wsClient() *websocket.Conn {​ + ctx, cancel := context.WithTimeout(context.Background(), 7*time.Second)​ + defer cancel()​ +​ + urlStr := fmt.Sprintf("wss://dmp-dev.raisound.com/iot-api/ws-hub/dev/v1?bz- +dt=%s", "954668a315d61dd4cacacb01b255387bca")​ + header := http.Header{​ + "User-Agent": []string{"Mozilla/5.0 (Windows NT 10.0; Win64; x64) +AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"},​ + }​ +​ + fmt.Println(urlStr)​ + ws, _, wsErr := websocket.DefaultDialer.DialContext(ctx, urlStr, header)​ + if wsErr != nil {​ + log.Warn(fmt.Sprintf("Dial failed: %s", wsErr.Error()))​ + }​ + return ws​ +}​ +​ +// Json数据​ +func fmtJsonMsg(jsonStr string) []byte {​ + log.Debug(fmt.Sprintf("拼接数据: %s", jsonStr))​ + // 创建一个字节缓冲区​ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +## Page 26 + + buf := new(bytes.Buffer)​ + // 写入头部​ + buf.Write([]byte{0xFF, 0xFE})​ + // 写入版本​ + buf.WriteByte(0x01)​ + // 写入类型​ + buf.WriteByte(0x03)​ + // 将 JSON 字符串转换为字节数组​ + jsonBytes := []byte(jsonStr)​ + // 计算载荷长度​ + jsonLen := len(jsonBytes)​ + jsonLenBytes := make([]byte, 4)​ + // 载荷长度转大端序(高位在前)​ + binary.BigEndian.PutUint32(jsonLenBytes, uint32(jsonLen))​ + // 写入长度​ + buf.Write(jsonLenBytes)​ + // 写入 JSON 数据​ + buf.Write(jsonBytes)​ + return buf.Bytes()​ +}​ +​ +func Run() {​ + wg.Add(1)​ +​ + // 创建WebSocket连接​ + client := wsClient()​ + defer func() {​ + _ = client.Close()​ + os.Exit(0)​ + }()​ +​ + heartTicker := time.NewTicker(3 * time.Second)​ + // 心跳协程​ + go func() {​ + for {​ + select {​ + case <-heartTicker.C:​ + heartData := +fmtJsonMsg(fmt.Sprintf(`{"cmd":"post_heart_beat","ts":%d,"msg":"心跳","data": +{"battery":%d}}`, time.Now().Unix(), common.RandInt(1, 101)))​ + log.Debug(fmt.Sprintf("Write Message: %v", heartData))​ + _, wErr := modules.SendMsg(client, heartData, +websocket.BinaryMessage)​ + if wErr != nil {​ + log.Error(fmt.Sprintf("SendMsg Error: %s", wErr))​ + reStart()​ + }​ +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +## Page 27 + + }​ + }​ + }()​ +​ + // 接收协程​ + go func() {​ + for {​ + // 接收服务器返回的信息​ + _, result, readErr := client.ReadMessage()​ + log.Debug(fmt.Sprintf("服务器返回: %v\n字符串内容: %s", result, +string(result)))​ + if readErr != nil {​ + log.Error(fmt.Sprintf("ReadMessage Error: %s", readErr.Error()))​ + reStart()​ + }​ + }​ + }()​ + wg.Wait()​ +}​ +​ +func reStart() {​ + wg.Done()​ + time.Sleep(300 * time.Millisecond)​ + Run()​ +}​ +java版本​ +代码块​ +package com.dobell.iot;​ +​ +import java.net.URI;​ +import java.net.http.HttpClient;​ +import java.net.http.WebSocket;​ +import java.nio.ByteBuffer;​ +import java.nio.ByteOrder;​ +import java.time.Instant;​ +import java.util.Random;​ +import java.util.concurrent.*;​ +​ +public class WsClient {​ +​ + private static final String URL =​ + "wss://dmp-dev.raisound.com/iot-api/ws-hub/dev/v1?bz- +dt=954668a315d61dd4cacacb01b255387bca";​ +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +## Page 28 + +​ + private static WebSocket webSocket;​ + private static ScheduledExecutorService scheduler;​ + private static final Random RANDOM = new Random();​ +​ + /** ===== main 启动入口 ===== */​ + public static void main(String[] args) {​ + start();​ + }​ +​ + /** 启动逻辑(等价 Go 的 Run) */​ + private static void start() {​ + try {​ + scheduler = Executors.newScheduledThreadPool(2);​ + connect();​ + startHeartbeat();​ +​ + // 阻塞主线程,防止 main 退出​ + Thread.currentThread().join();​ + } catch (Exception e) {​ + e.printStackTrace();​ + restart();​ + }​ + }​ +​ + /** 建立 WebSocket 连接 */​ + private static void connect() {​ + HttpClient client = HttpClient.newHttpClient();​ +​ + webSocket = client.newWebSocketBuilder()​ + .connectTimeout(java.time.Duration.ofSeconds(7))​ + .header("User-Agent",​ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) +AppleWebKit/537.36 " +​ + "(KHTML, like Gecko) Chrome/138.0.0.0 +Safari/537.36")​ + .buildAsync(URI.create(URL), new WsListener())​ + .join();​ +​ + System.out.println("WebSocket connected");​ + }​ +​ + /** 心跳任务(3 秒一次) */​ + private static void startHeartbeat() {​ + scheduler.scheduleAtFixedRate(() -> {​ + try {​ + long ts = Instant.now().getEpochSecond();​ +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +## Page 29 + + int battery = RANDOM.nextInt(100) + 1;​ +​ + String json = String.format(​ + "{\"cmd\":\"post_heart_beat\",\"ts\":%d,\"msg\":\"心跳 +\",\"data\":{\"battery\":%d}}",​ + ts, battery​ + );​ +​ + ByteBuffer data = buildBinaryMsg(json);​ + webSocket.sendBinary(data, true).join();​ +​ + System.out.println("Send heartbeat: " + json);​ + } catch (Exception e) {​ + e.printStackTrace();​ + restart();​ + }​ + }, 0, 3, TimeUnit.SECONDS);​ + }​ +​ + /** 二进制协议封包(完全等价 Go fmtJsonMsg) */​ + private static ByteBuffer buildBinaryMsg(String json) {​ + byte[] jsonBytes = json.getBytes();​ + ByteBuffer buffer = ByteBuffer.allocate(2 + 1 + 1 + 4 + +jsonBytes.length);​ + buffer.order(ByteOrder.BIG_ENDIAN);​ +​ + buffer.put((byte) 0xFF);​ + buffer.put((byte) 0xFE);​ + buffer.put((byte) 0x01); // version​ + buffer.put((byte) 0x03); // type​ + buffer.putInt(jsonBytes.length);​ + buffer.put(jsonBytes);​ + buffer.flip();​ +​ + return buffer;​ + }​ +​ + /** 重连(等价 Go reStart) */​ + private static synchronized void restart() {​ + try {​ + System.err.println("Restarting...");​ + if (scheduler != null) {​ + scheduler.shutdownNow();​ + }​ + if (webSocket != null) {​ + webSocket.abort();​ + }​ +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +## Page 30 + + Thread.sleep(300);​ + start();​ + } catch (Exception ignored) {​ + }​ + }​ +​ + /** WebSocket 回调 */​ + static class WsListener implements WebSocket.Listener {​ +​ + @Override​ + public CompletionStage onBinary(WebSocket ws,​ + ByteBuffer data,​ + boolean last) {​ + byte[] bytes = new byte[data.remaining()];​ + data.get(bytes);​ + System.out.println("Server(binary): " + new String(bytes));​ + return WebSocket.Listener.super.onBinary(ws, data, last);​ + }​ +​ + @Override​ + public CompletionStage onText(WebSocket ws,​ + CharSequence data,​ + boolean last) {​ + System.out.println("Server(text): " + data);​ + return WebSocket.Listener.super.onText(ws, data, last);​ + }​ +​ + @Override​ + public void onError(WebSocket ws, Throwable error) {​ + System.err.println("WebSocket error: " + error.getMessage());​ + restart();​ + }​ +​ + @Override​ + public CompletionStage onClose(WebSocket ws,​ + int statusCode,​ + String reason) {​ + System.err.println("WebSocket closed: " + statusCode + " " + +reason);​ + restart();​ + return WebSocket.Listener.super.onClose(ws, statusCode, reason);​ + }​ + }​ +}​ +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 diff --git a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/constants.py b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/constants.py index b905e290..bd92bd2b 100644 --- a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/constants.py +++ b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/constants.py @@ -208,10 +208,17 @@ try: DMP_BASE = _srv.get_addr("DMP_BASE", "") AMP_SERVER = _srv.get_addr("AMP_SERVER", "") IOT_WS_URL = _srv.get_addr("IOT_WS_URL", "") + OTA_TASK_URL_FMT = _srv.get_addr("OTA_TASK_URL_FMT", "") + OTA_RESULT_PATH_FMT = _srv.get_addr( + "OTA_RESULT_PATH_FMT", + "/ota/result?deviceId={}¤tVersion={}", + ) else: DMP_BASE = "" AMP_SERVER = "" IOT_WS_URL = "" + OTA_TASK_URL_FMT = "" + OTA_RESULT_PATH_FMT = "/ota/result?deviceId={}¤tVersion={}" # 兼容历史:提供与旧 _SERVER_CFG 相似的结构 _SERVER_CFG = { @@ -226,6 +233,8 @@ except Exception: DMP_BASE = "" AMP_SERVER = "" IOT_WS_URL = "" + OTA_TASK_URL_FMT = "" + OTA_RESULT_PATH_FMT = "/ota/result?deviceId={}¤tVersion={}" _SERVER_CFG = {SERVER_ENV: {"dmp_base": "", "amp_server": "", "iot_ws_url": ""}} diff --git a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/default_srv.py b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/default_srv.py index c333e1a3..d282d900 100644 --- a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/default_srv.py +++ b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/default_srv.py @@ -47,10 +47,9 @@ # 路径:ai_VC 内目前为字面量 "/dev/..."(与下方 DMP_*_PATH 取值应对齐) # 示例:{DMP_BASE}/dev/time 、{DMP_BASE}/dev/auth/active # -# 【OTA 检查任务 — ai_VC OTA 查询】 -# 当前 ai_VC 使用完整 URL 字面量(应与 OTA_TASK_URL_FMT 一致): -# OTA_TASK_URL_FMT.format(imei, sourceVersion) -# OTA 结果上报:{DMP_BASE}/ota/result?deviceId=...(路径与 OTA_RESULT_PATH_FMT 应对齐) +# 【OTA 检查任务 — vc_ota.min_sys_ota / bt_ota】 +# constants.OTA_TASK_URL_FMT.format(imei, sourceVersion) +# OTA 结果上报:{DMP_BASE} + OTA_RESULT_PATH_FMT(vc_ota.ota_result) # # 【AMP 健康检查 — ai_VC 错误上报】 # 使用 self.server_ip(同 AMP_SERVER)+ 硬编码路径: @@ -89,7 +88,7 @@ DMP_DEV_CONFIG_PATH_FMT = "/dev/config/{}" # 设备配置,format(deviceId) # ---- OTA ---- # 完整 URL 模板;占位符 {} 依次为 deviceId(IMEI)、sourceVersion -OTA_TASK_URL_FMT = "https://dmp-api.qipai.com/qp-dmp-api/ota/task?deviceId={}&sourceVersion={}" +OTA_TASK_URL_FMT = "http://dmp-dev.raisound.com/dmp-api/ota/task?deviceId={}&sourceVersion={}" # 相对 DMP_BASE 的路径模板(上报 OTA 结果) OTA_RESULT_PATH_FMT = "/ota/result?deviceId={}¤tVersion={}" diff --git a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/srv_configs/beike/default_srv.py b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/srv_configs/beike/default_srv.py index 79152258..dda1084d 100644 --- a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/srv_configs/beike/default_srv.py +++ b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/srv_configs/beike/default_srv.py @@ -17,7 +17,7 @@ DMP_TOKEN_PATH = "/dev/auth/token" DMP_ICCID_PATH = "/dev/iccid" DMP_DEV_CONFIG_PATH_FMT = "/dev/config/{}" -OTA_TASK_URL_FMT = "https://dmp-api.qipai.com/qp-dmp-api/ota/task?deviceId={}&sourceVersion={}" +OTA_TASK_URL_FMT = "http://dmp.raisound.com/bz-api/ota/task?deviceId={}&sourceVersion={}" OTA_RESULT_PATH_FMT = "/ota/result?deviceId={}¤tVersion={}" AMP_HEALTH_URL_FMT = "http://{}/health?task_id={}" diff --git a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/srv_configs/beike_dev/default_srv.py b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/srv_configs/beike_dev/default_srv.py index c333e1a3..d282d900 100644 --- a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/srv_configs/beike_dev/default_srv.py +++ b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/products/srv_configs/beike_dev/default_srv.py @@ -47,10 +47,9 @@ # 路径:ai_VC 内目前为字面量 "/dev/..."(与下方 DMP_*_PATH 取值应对齐) # 示例:{DMP_BASE}/dev/time 、{DMP_BASE}/dev/auth/active # -# 【OTA 检查任务 — ai_VC OTA 查询】 -# 当前 ai_VC 使用完整 URL 字面量(应与 OTA_TASK_URL_FMT 一致): -# OTA_TASK_URL_FMT.format(imei, sourceVersion) -# OTA 结果上报:{DMP_BASE}/ota/result?deviceId=...(路径与 OTA_RESULT_PATH_FMT 应对齐) +# 【OTA 检查任务 — vc_ota.min_sys_ota / bt_ota】 +# constants.OTA_TASK_URL_FMT.format(imei, sourceVersion) +# OTA 结果上报:{DMP_BASE} + OTA_RESULT_PATH_FMT(vc_ota.ota_result) # # 【AMP 健康检查 — ai_VC 错误上报】 # 使用 self.server_ip(同 AMP_SERVER)+ 硬编码路径: @@ -89,7 +88,7 @@ DMP_DEV_CONFIG_PATH_FMT = "/dev/config/{}" # 设备配置,format(deviceId) # ---- OTA ---- # 完整 URL 模板;占位符 {} 依次为 deviceId(IMEI)、sourceVersion -OTA_TASK_URL_FMT = "https://dmp-api.qipai.com/qp-dmp-api/ota/task?deviceId={}&sourceVersion={}" +OTA_TASK_URL_FMT = "http://dmp-dev.raisound.com/dmp-api/ota/task?deviceId={}&sourceVersion={}" # 相对 DMP_BASE 的路径模板(上报 OTA 结果) OTA_RESULT_PATH_FMT = "/ota/result?deviceId={}¤tVersion={}" diff --git a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/vc_ota.py b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/vc_ota.py index 168903dc..8df37fe6 100644 --- a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/vc_ota.py +++ b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/vc_ota.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""FOTA / DMP 上报。""" +"""FOTA / DMP 上报。小程序上的版本,是这个ota接口上报的。""" try: from qp_log import qp_print as print except Exception: @@ -16,6 +16,29 @@ import constants import version_util from misc import Power +def _build_ota_task_url(source_version): + """OTA 检查任务 URL:default_srv.OTA_TASK_URL_FMT,否则 DMP_BASE + /ota/task。""" + fmt = getattr(constants, "OTA_TASK_URL_FMT", None) or "" + if not fmt: + base = getattr(constants, "DMP_BASE", None) or "" + if base: + fmt = base + "/ota/task?deviceId={}&sourceVersion={}" + if not fmt: + return None + return fmt.format(modem.getDevImei(), source_version) + + +def _build_ota_result_url(current_version): + """OTA 结果上报:DMP_BASE + default_srv.OTA_RESULT_PATH_FMT。""" + path_fmt = getattr( + constants, + "OTA_RESULT_PATH_FMT", + "/ota/result?deviceId={}¤tVersion={}", + ) + base = getattr(constants, "DMP_BASE", None) or "" + return base + path_fmt.format(modem.getDevImei(), current_version) + + class OTA(): def __init__(self): #EventMesh.subscribe("min_sys_ota",self.min_sys_ota) @@ -48,9 +71,10 @@ class OTA(): if bt_versions == None: bt_versions="" print("bt_versions:{}".format(bt_versions)) - #url = "http://iot.api.thisiot.com/ota/task?deviceId=860132060348836&sourceVersion=2024-12-20-15:35-1" - url =None - url = "https://dmp-api.qipai.com/qp-dmp-api/ota/task?deviceId={}&sourceVersion={}".format(modem.getDevImei(),sourceVersion) + url = _build_ota_task_url(sourceVersion) + if not url: + print("bt_ota: OTA_TASK_URL_FMT 未配置") + return 0 print(url) response = request.get(url,timeout=5) if response.status_code == 200: @@ -109,8 +133,10 @@ class OTA(): try: sourceVersion = version_util.get_unified_version() print("sourceVersion:{}".format(sourceVersion)) - url =None - url = "https://dmp-api.qipai.com/qp-dmp-api/ota/task?deviceId={}&sourceVersion={}".format(modem.getDevImei(),sourceVersion) + url = _build_ota_task_url(sourceVersion) + if not url: + print("min_sys_ota: OTA_TASK_URL_FMT 未配置") + return 0 print(url) response = request.get(url,timeout=5) if response.status_code == 200: @@ -198,7 +224,7 @@ class OTA(): try: currentVersion = version_util.get_unified_version() print("currentVersion:{}".format(currentVersion)) - url = (constants.DMP_BASE + "/ota/result?deviceId={}¤tVersion={}").format(modem.getDevImei(),currentVersion) + url = _build_ota_result_url(currentVersion) print(url) response = request.post(url,timeout=5) if response.status_code == 200: diff --git a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/version_util.py b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/version_util.py index abeff68f..2c1ecf70 100644 --- a/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/version_util.py +++ b/2.sdk/project/7PIN_1_5/sdk/services/microPython/ports/quectel/core/version_util.py @@ -9,7 +9,7 @@ def _char_is_digit(ch): def _build_time_token_sanitize(s): - """ASCII 兜底:不依赖 isdigit/逐字符迭代,仅 replace + split。""" + #ASCII 兜底:不依赖 isdigit/逐字符迭代,仅 replace + split。 if not s: return "" out = str(s).strip() @@ -31,7 +31,8 @@ def _build_time_token_sanitize(s): def _build_time_token(): - """从 versions.Build_Time 解析,输出 yyyy_MM_dd_HH_mm(不含秒)。""" + #从 versions.Build_Time 解析,输出 yyyy_MM_dd_HH_mm(不含秒)。 + #Build_Time="2026-05-20 17:39:09" --> yyyy_MM_dd_HH_mm try: s = str(getattr(versions, "Build_Time", "") or "").strip().strip('"').strip("'") except Exception: @@ -77,6 +78,9 @@ def get_unified_version(): ver = str(getattr(versions, "Version", "") or "").strip() except Exception: ver = "" + # 对外展示统一为小写 v(k8_v1.2_xxx),与 product_*.ini 中 VERSION=V1.2 兼容 + if len(ver) >= 2 and ver[0] == "V" and ver[1].isdigit(): + ver = "v" + ver[1:] bt = _build_time_token() if bt: return "{}_{}_{}".format(pn, ver, bt)