| New file |
| | |
| | | # HomeAssistant自动化配置 |
| | | |
| | | > **注意**: 本文档使用HomeAssistant 2024.x 及以上版本的新YAML格式。主要变化包括: |
| | | > - `trigger` → `triggers` |
| | | > - `condition` → `conditions` |
| | | > - `action` → `actions` |
| | | > - `service` → `action` |
| | | > - `platform: state` → `trigger: state` |
| | | > - `platform: time_pattern` → `trigger: time_pattern` |
| | | |
| | | ## 配置方法 |
| | | |
| | | ### 方法1: 通过HomeAssistant Web界面配置(推荐) |
| | | |
| | | 1. 登录HomeAssistant Web界面 |
| | | 2. 进入 "配置" → "自动化和场景" → "自动化" |
| | | 3. 点击右下角 "+" 按钮创建新自动化 |
| | | 4. 点击右上角 "⋮" → "编辑为YAML" |
| | | 5. 粘贴以下配置: |
| | | |
| | | ```yaml |
| | | alias: 发布环境温度传感器状态到MQTT |
| | | description: 当米家温度传感器状态变化或每30秒定时时,自动发布到MQTT供Node-RED使用 |
| | | triggers: |
| | | - entity_id: sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1 |
| | | trigger: state |
| | | - seconds: /30 |
| | | trigger: time_pattern |
| | | conditions: |
| | | - condition: template |
| | | value_template: >- |
| | | {{ states('sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1') |
| | | not in ['unavailable', 'unknown', 'None'] and |
| | | states('sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1') | |
| | | float(-999) != -999 }} |
| | | actions: |
| | | - data: |
| | | topic: >- |
| | | homeassistant/sensor/sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1/state |
| | | payload: >- |
| | | {{ |
| | | states('sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1') |
| | | }} |
| | | retain: false |
| | | qos: 0 |
| | | action: mqtt.publish |
| | | mode: single |
| | | ``` |
| | | |
| | | 6. 点击右上角"保存" |
| | | 7. 确认自动化已启用 |
| | | |
| | | ### 方法2: 通过配置文件(高级用户) |
| | | |
| | | 编辑HomeAssistant的 `automations.yaml` 文件,添加: |
| | | |
| | | ```yaml |
| | | - id: publish_temperature_to_mqtt |
| | | alias: 发布环境温度传感器状态到MQTT |
| | | description: 当米家温度传感器状态变化或每30秒定时时,自动发布到MQTT供Node-RED使用 |
| | | triggers: |
| | | - entity_id: sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1 |
| | | trigger: state |
| | | - seconds: /30 |
| | | trigger: time_pattern |
| | | conditions: |
| | | - condition: template |
| | | value_template: >- |
| | | {{ states('sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1') |
| | | not in ['unavailable', 'unknown', 'None'] and |
| | | states('sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1') | |
| | | float(-999) != -999 }} |
| | | actions: |
| | | - data: |
| | | topic: >- |
| | | homeassistant/sensor/sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1/state |
| | | payload: >- |
| | | {{ |
| | | states('sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1') |
| | | }} |
| | | retain: false |
| | | qos: 0 |
| | | action: mqtt.publish |
| | | mode: single |
| | | ``` |
| | | |
| | | 然后重启HomeAssistant或重新加载自动化配置。 |
| | | |
| | | ## 配置验证 |
| | | |
| | | ### 1. 检查MQTT配置 |
| | | |
| | | 确认HomeAssistant的MQTT集成已配置且正常工作: |
| | | |
| | | 1. 进入 "配置" → "集成" |
| | | 2. 找到MQTT集成,确认状态正常 |
| | | 3. 检查MQTT broker地址:192.168.50.83:1883 |
| | | |
| | | 如果MQTT集成未配置,请添加: |
| | | |
| | | ```yaml |
| | | # configuration.yaml |
| | | mqtt: |
| | | broker: 192.168.50.83 |
| | | port: 1883 |
| | | username: dtuser |
| | | password: Dtuse1r |
| | | discovery: true |
| | | ``` |
| | | |
| | | ### 2. 验证传感器实体 |
| | | |
| | | 1. 进入 "开发者工具" → "状态" |
| | | 2. 搜索实体ID:`sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1` |
| | | 3. 确认: |
| | | - 实体存在且状态不是 `unavailable` |
| | | - 状态值是有效的温度数字 |
| | | - 最后更新时间是最近的 |
| | | |
| | | ### 3. 测试自动化 |
| | | |
| | | 1. 进入 "配置" → "自动化和场景" → "自动化" |
| | | 2. 找到创建的自动化规则 |
| | | 3. 点击右侧的 "▶" 按钮手动执行 |
| | | 4. 或者等待传感器状态自然变化 |
| | | |
| | | ### 4. 验证MQTT发布 |
| | | |
| | | 使用MQTT客户端工具验证数据发布: |
| | | |
| | | ```bash |
| | | # 订阅HomeAssistant发布的温度数据 |
| | | mosquitto_sub -h 192.168.50.83 -p 1883 -u dtuser -P Dtuse1r -t "homeassistant/sensor/sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1/state" |
| | | ``` |
| | | |
| | | 应该能看到类似这样的输出: |
| | | ``` |
| | | 23.5 |
| | | 24.1 |
| | | 23.8 |
| | | ``` |
| | | |
| | | ## 故障排除 |
| | | |
| | | ### 自动化未触发 |
| | | |
| | | 1. **检查实体ID**: 确认传感器实体ID完全正确 |
| | | 2. **检查条件**: 确认传感器状态满足条件(不是unavailable/unknown) |
| | | 3. **查看日志**: 在HomeAssistant的"配置" → "日志"中查看错误信息 |
| | | 4. **手动测试**: 使用自动化的手动执行功能测试 |
| | | |
| | | ### MQTT发布失败 |
| | | |
| | | 1. **检查MQTT集成**: 确认MQTT broker连接正常 |
| | | 2. **验证权限**: 确认MQTT用户有发布权限 |
| | | 3. **网络连接**: 检查HomeAssistant到MQTT broker的网络连接 |
| | | 4. **Topic权限**: 确认topic没有发布限制 |
| | | |
| | | ### 传感器数据异常 |
| | | |
| | | 1. **米家插件状态**: 检查米家插件是否正常工作 |
| | | 2. **蓝牙连接**: 确认传感器与HomeAssistant的蓝牙连接 |
| | | 3. **设备电量**: 检查传感器电池电量 |
| | | 4. **设备距离**: 确认设备在蓝牙通信范围内 |
| | | |
| | | ## 高级配置选项 |
| | | |
| | | ### 调整定时发布间隔 |
| | | |
| | | 当前配置为每30秒发布一次,您可以根据需要调整间隔: |
| | | |
| | | ```yaml |
| | | triggers: |
| | | - entity_id: sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1 |
| | | trigger: state |
| | | - seconds: /60 # 每60秒(1分钟)发布一次 |
| | | trigger: time_pattern |
| | | # 或者 |
| | | - minutes: /5 # 每5分钟发布一次 |
| | | trigger: time_pattern |
| | | # 或者 |
| | | - hours: /1 # 每小时发布一次 |
| | | trigger: time_pattern |
| | | ``` |
| | | |
| | | ### 区分触发类型(可选) |
| | | |
| | | 如果需要在日志中区分是状态变化触发还是定时触发,可以添加调试信息: |
| | | |
| | | ```yaml |
| | | actions: |
| | | - data: |
| | | message: >- |
| | | 发布温度数据到MQTT: {{ states('sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1') }}°C |
| | | (触发类型: {% if trigger.trigger == 'state' %}状态变化{% else %}定时{% endif %}) |
| | | level: info |
| | | action: system_log.write |
| | | - data: |
| | | topic: >- |
| | | homeassistant/sensor/sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1/state |
| | | payload: >- |
| | | {{ states('sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1') }} |
| | | action: mqtt.publish |
| | | ``` |
| | | |
| | | ### 添加数据过滤 |
| | | |
| | | 如果需要过滤异常数据,可以修改条件: |
| | | |
| | | ```yaml |
| | | conditions: |
| | | - condition: template |
| | | value_template: >- |
| | | {{ trigger.to_state.state not in ['unavailable', 'unknown', 'None'] and |
| | | trigger.to_state.state | float(-999) != -999 and |
| | | trigger.to_state.state | float > -50 and |
| | | trigger.to_state.state | float < 100 }} |
| | | ``` |
| | | |
| | | ### 添加发布间隔限制 |
| | | |
| | | 避免过于频繁的发布: |
| | | |
| | | ```yaml |
| | | triggers: |
| | | - entity_id: sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1 |
| | | trigger: state |
| | | for: |
| | | seconds: 10 # 状态持续10秒后才触发 |
| | | ``` |
| | | |
| | | ### 添加调试日志 |
| | | |
| | | ```yaml |
| | | actions: |
| | | - data: |
| | | message: "发布温度数据到MQTT: {{ trigger.to_state.state }}°C" |
| | | level: info |
| | | action: system_log.write |
| | | - data: |
| | | topic: >- |
| | | homeassistant/sensor/sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1/state |
| | | payload: "{{ trigger.to_state.state }}" |
| | | action: mqtt.publish |
| | | ``` |
| | | |
| | | ## 完成后的数据流 |
| | | |
| | | ``` |
| | | 米家温度传感器 → HomeAssistant → MQTT Broker → Node-RED → 公司MQTT服务器 |
| | | ``` |
| | | |
| | | 配置完成后,数据流程将变为: |
| | | 1. 米家传感器通过蓝牙发送数据到HomeAssistant |
| | | 2. HomeAssistant接收数据并更新传感器状态 |
| | | 3. 自动化规则触发,将状态发布到MQTT |
| | | 4. Node-RED订阅MQTT接收数据 |
| | | 5. Node-RED处理并转发到公司MQTT服务器 |
| New file |
| | |
| | | [ |
| | | { |
| | | "id": "1407d4531e19a951", |
| | | "type": "tab", |
| | | "label": "透析中心环境温度数据转发", |
| | | "disabled": false, |
| | | "info": "从HomeAssistant获取温度传感器数据并发布到公司MQTT服务器" |
| | | }, |
| | | { |
| | | "id": "97e254f4a87ddde9", |
| | | "type": "mqtt in", |
| | | "z": "1407d4531e19a951", |
| | | "name": "订阅HA温度传感器", |
| | | "topic": "homeassistant/sensor/+/state", |
| | | "qos": "0", |
| | | "datatype": "auto", |
| | | "broker": "mqtt-ha-broker", |
| | | "nl": false, |
| | | "rap": true, |
| | | "rh": 0, |
| | | "inputs": 0, |
| | | "x": 150, |
| | | "y": 100, |
| | | "wires": [ |
| | | [ |
| | | "44d729e67618e4f2" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "44d729e67618e4f2", |
| | | "type": "function", |
| | | "z": "1407d4531e19a951", |
| | | "name": "处理温度数据", |
| | | "func": "// 从全局变量获取共享配置\nconst nameSpace = global.get(\"nameSpace\") || \"Environment\";\nconst clientCode = global.get(\"clientCode\") || \"Data-It_XzOffice\";\nconst targetDeviceId = global.get(\"deviceId\") || \"sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1\";\n\n// 从topic中提取当前设备ID\nconst topicParts = msg.topic.split('/');\nconst currentDeviceId = topicParts.length >= 3 ? topicParts[2] : \"\";\n\n// 只处理目标设备的消息\nif (currentDeviceId !== targetDeviceId) {\n node.log(\"忽略设备消息: \" + currentDeviceId + \" (目标设备: \" + targetDeviceId + \")\");\n return null;\n}\n\n// 生成唯一的消息ID\nconst messageId = \"temp_\" + Date.now() + \"_\" + Math.floor(Math.random() * 1000);\nconst timestamp = Date.now();\nconst deviceType = \"sensor\";\n\n// 解析温度值\nlet temperature = parseFloat(msg.payload);\nif (isNaN(temperature)) {\n node.warn(\"接收到无效的温度值: \" + msg.payload);\n return null;\n}\n\n// 按照胜透物联网通信协议格式化数据\nconst protocolMessage = {\n \"messageId\": messageId,\n \"timestamp\": timestamp,\n \"clientCode\": clientCode,\n \"deviceId\": targetDeviceId,\n \"deviceType\": deviceType,\n \"version\": \"1.0\",\n \"data\": {\n \"properties\": {\n \"temperature\": {\n \"value\": temperature,\n \"unit\": \"°C\",\n \"quality\": \"good\",\n \"timestamp\": timestamp\n }\n }\n }\n};\n\n// 设置输出消息\nmsg.payload = JSON.stringify(protocolMessage);\nmsg.topic = `${nameSpace}/sensor/${clientCode}/${targetDeviceId}/properties`;\nmsg.qos = 0;\n\nnode.log(\"温度数据已处理: \" + temperature + \"°C (设备: \" + targetDeviceId + \")\");\n\nreturn msg;", |
| | | "outputs": 1, |
| | | "timeout": "", |
| | | "noerr": 0, |
| | | "initialize": "", |
| | | "finalize": "", |
| | | "libs": [], |
| | | "x": 420, |
| | | "y": 100, |
| | | "wires": [ |
| | | [ |
| | | "573842ea77192dbb", |
| | | "4c630c164e08dc2c" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "573842ea77192dbb", |
| | | "type": "mqtt out", |
| | | "z": "1407d4531e19a951", |
| | | "name": "发布到公司MQTT", |
| | | "topic": "", |
| | | "qos": "", |
| | | "retain": "", |
| | | "respTopic": "", |
| | | "contentType": "", |
| | | "userProps": "", |
| | | "correl": "", |
| | | "expiry": "", |
| | | "broker": "mqtt-company-broker", |
| | | "x": 990, |
| | | "y": 80, |
| | | "wires": [] |
| | | }, |
| | | { |
| | | "id": "4c630c164e08dc2c", |
| | | "type": "debug", |
| | | "z": "1407d4531e19a951", |
| | | "name": "调试输出", |
| | | "active": true, |
| | | "tosidebar": true, |
| | | "console": false, |
| | | "tostatus": false, |
| | | "complete": "payload", |
| | | "targetType": "msg", |
| | | "statusVal": "", |
| | | "statusType": "auto", |
| | | "x": 1040, |
| | | "y": 360, |
| | | "wires": [] |
| | | }, |
| | | { |
| | | "id": "495c392574ee1bf4", |
| | | "type": "inject", |
| | | "z": "1407d4531e19a951", |
| | | "name": "发布设备物模型", |
| | | "props": [ |
| | | { |
| | | "p": "payload" |
| | | }, |
| | | { |
| | | "p": "topic", |
| | | "vt": "str" |
| | | } |
| | | ], |
| | | "repeat": "60", |
| | | "crontab": "", |
| | | "once": true, |
| | | "onceDelay": "5", |
| | | "topic": "shengtou/sensor/TH001_HA/model", |
| | | "payload": "", |
| | | "payloadType": "date", |
| | | "x": 170, |
| | | "y": 180, |
| | | "wires": [ |
| | | [ |
| | | "3aea3ce2deb4ea99" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "3aea3ce2deb4ea99", |
| | | "type": "function", |
| | | "z": "1407d4531e19a951", |
| | | "name": "生成物模型数据", |
| | | "func": "const messageId = \"mdl_\" + Date.now();\nconst timestamp = Date.now();\n// 从全局变量获取共享配置\nconst nameSpace = global.get(\"nameSpace\") || \"Environment\";\nconst clientCode = global.get(\"clientCode\") || \"Data-It_XzOffice\";\nconst deviceId = global.get(\"deviceId\") || \"sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1\";\nconst deviceType = \"sensor\";\n\n// 按照胜透物联网通信协议生成物模型定义\nconst modelMessage = {\n \"messageId\": messageId,\n \"timestamp\": timestamp,\n \"clientCode\": clientCode,\n \"deviceId\": deviceId,\n \"deviceType\": deviceType,\n \"version\": \"1.0\",\n \"data\": {\n \"model\": {\n \"deviceInfo\": {\n \"manufacturer\": \"小米\",\n \"model\": \"MIAOMIAOC_CN_BLT_3_11A1SBR5K5O01_T2\",\n \"version\": \"1.0.0\",\n \"description\": \"米家温湿度传感器(通过HomeAssistant集成)\"\n },\n \"properties\": {\n \"temperature\": {\n \"dataType\": \"float\",\n \"unit\": \"°C\",\n \"range\": {\n \"min\": -40,\n \"max\": 85\n },\n \"precision\": 1,\n \"description\": \"环境温度\"\n }\n },\n \"events\": {\n \"startup\": {\n \"eventType\": \"info\",\n \"description\": \"设备启动事件\"\n }\n },\n \"alarms\": {\n \"TEMP_HIGH\": {\n \"alarmType\": \"threshold_exceeded\",\n \"description\": \"温度过高报警\",\n \"defaultThreshold\": 35.0\n },\n \"TEMP_LOW\": {\n \"alarmType\": \"threshold_exceeded\",\n \"description\": \"温度过低报警\",\n \"defaultThreshold\": 0.0\n }\n }\n }\n }\n};\n\n// 设置MQTT发布主题\nmsg.topic = `${nameSpace}/sensor/${clientCode}/${deviceId}/model`;\nmsg.payload = JSON.stringify(modelMessage);\nmsg.qos = 1;\n\nnode.log(\"设备物模型数据已生成,发布到主题: \" + msg.topic);\n\nreturn msg;", |
| | | "outputs": 1, |
| | | "timeout": "", |
| | | "noerr": 0, |
| | | "initialize": "", |
| | | "finalize": "", |
| | | "libs": [], |
| | | "x": 400, |
| | | "y": 160, |
| | | "wires": [ |
| | | [ |
| | | "573842ea77192dbb", |
| | | "4c630c164e08dc2c" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "70e1e283448c941e", |
| | | "type": "inject", |
| | | "z": "1407d4531e19a951", |
| | | "name": "设备状态心跳", |
| | | "props": [ |
| | | { |
| | | "p": "payload" |
| | | }, |
| | | { |
| | | "p": "topic", |
| | | "vt": "str" |
| | | } |
| | | ], |
| | | "repeat": "120", |
| | | "crontab": "", |
| | | "once": true, |
| | | "onceDelay": "10", |
| | | "topic": "shengtou/sensor/TH001_HA/status", |
| | | "payload": "", |
| | | "payloadType": "date", |
| | | "x": 160, |
| | | "y": 240, |
| | | "wires": [ |
| | | [ |
| | | "ae74b6d386748045" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "ae74b6d386748045", |
| | | "type": "function", |
| | | "z": "1407d4531e19a951", |
| | | "name": "生成状态数据", |
| | | "func": "const messageId = \"sts_\" + Date.now();\nconst timestamp = Date.now();\n// 从全局变量获取共享配置\nconst nameSpace = global.get(\"nameSpace\") || \"Environment\";\nconst clientCode = global.get(\"clientCode\") || \"Data-It_XzOffice\";\nconst deviceId = global.get(\"deviceId\") || \"sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1\";\nconst deviceType = \"sensor\";\n\n// 计算设备运行时间(从第一次启动开始)\nif (!context.get(\"startTime\")) {\n context.set(\"startTime\", timestamp);\n}\nconst startTime = context.get(\"startTime\");\nconst uptime = timestamp - startTime;\n\n// 按照胜透物联网通信协议生成状态数据\nconst statusMessage = {\n \"messageId\": messageId,\n \"timestamp\": timestamp,\n \"clientCode\": clientCode,\n \"deviceId\": deviceId,\n \"deviceType\": deviceType,\n \"version\": \"1.0\",\n \"data\": {\n \"status\": {\n \"online\": true,\n \"lastHeartbeat\": timestamp,\n \"uptime\": uptime\n }\n }\n};\n\n// 设置MQTT发布主题\nmsg.topic = `${nameSpace}/sensor/${clientCode}/${deviceId}/status`;\nmsg.payload = JSON.stringify(statusMessage);\nmsg.qos = 0;\n\nnode.log(\"设备状态心跳已发送,发布到主题: \" + msg.topic);\n\nreturn msg;", |
| | | "outputs": 1, |
| | | "timeout": "", |
| | | "noerr": 0, |
| | | "initialize": "", |
| | | "finalize": "", |
| | | "libs": [], |
| | | "x": 400, |
| | | "y": 240, |
| | | "wires": [ |
| | | [ |
| | | "573842ea77192dbb", |
| | | "4c630c164e08dc2c" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "7f9acf10d0c6085d", |
| | | "type": "inject", |
| | | "z": "1407d4531e19a951", |
| | | "name": "初始化全局配置", |
| | | "props": [ |
| | | { |
| | | "p": "payload" |
| | | } |
| | | ], |
| | | "repeat": "", |
| | | "crontab": "", |
| | | "once": true, |
| | | "onceDelay": "1", |
| | | "topic": "", |
| | | "payload": "", |
| | | "payloadType": "date", |
| | | "x": 150, |
| | | "y": 40, |
| | | "wires": [ |
| | | [ |
| | | "6f6b825dfc5e9512" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "6f6b825dfc5e9512", |
| | | "type": "function", |
| | | "z": "1407d4531e19a951", |
| | | "name": "设置全局配置", |
| | | "func": "// 设置全局共享常量\nglobal.set(\"nameSpace\", \"Environment\");\nglobal.set(\"clientCode\", \"Data-It_XzOffice\");\nglobal.set(\"deviceId\", \"sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1\");\n\nnode.log(\"全局配置已设置: nameSpace=\" + global.get(\"nameSpace\") + \", clientCode=\" + global.get(\"clientCode\") + \", deviceId=\" + global.get(\"deviceId\"));\n\nreturn msg;", |
| | | "outputs": 1, |
| | | "timeout": "", |
| | | "noerr": 0, |
| | | "initialize": "", |
| | | "finalize": "", |
| | | "libs": [], |
| | | "x": 360, |
| | | "y": 40, |
| | | "wires": [ |
| | | [ |
| | | "794c422d39898a7f" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "794c422d39898a7f", |
| | | "type": "function", |
| | | "z": "1407d4531e19a951", |
| | | "name": "更新MQTT订阅Topic", |
| | | "func": "// 从全局变量生成HomeAssistant MQTT订阅Topic\nconst deviceId = global.get(\"deviceId\") || \"sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1\";\nconst haTopic = `homeassistant/sensor/${deviceId}/state`;\n\n// 将Topic保存到全局变量供其他地方使用\nglobal.set(\"haSubscribeTopic\", haTopic);\n\nnode.log(\"HomeAssistant订阅Topic已更新: \" + haTopic);\nnode.log(\"注意:如需更改MQTT In节点的订阅Topic,请手动修改为: \" + haTopic);\n\nreturn msg;", |
| | | "outputs": 1, |
| | | "timeout": "", |
| | | "noerr": 0, |
| | | "initialize": "", |
| | | "finalize": "", |
| | | "libs": [], |
| | | "x": 590, |
| | | "y": 40, |
| | | "wires": [ |
| | | [] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "3bf854e9bd3e6937", |
| | | "type": "inject", |
| | | "z": "1407d4531e19a951", |
| | | "name": "启动事件", |
| | | "props": [ |
| | | { |
| | | "p": "payload" |
| | | }, |
| | | { |
| | | "p": "topic", |
| | | "vt": "str" |
| | | } |
| | | ], |
| | | "repeat": "", |
| | | "crontab": "", |
| | | "once": true, |
| | | "onceDelay": "15", |
| | | "topic": "shengtou/sensor/TH001_HA/events", |
| | | "payload": "", |
| | | "payloadType": "date", |
| | | "x": 160, |
| | | "y": 340, |
| | | "wires": [ |
| | | [ |
| | | "26f6196b2d6fba29" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "26f6196b2d6fba29", |
| | | "type": "function", |
| | | "z": "1407d4531e19a951", |
| | | "name": "生成启动事件", |
| | | "func": "const messageId = \"evt_startup_\" + Date.now();\nconst timestamp = Date.now();\n// 从全局变量获取共享配置\nconst nameSpace = global.get(\"nameSpace\") || \"Environment\";\nconst clientCode = global.get(\"clientCode\") || \"Data-It_XzOffice\";\nconst deviceId = global.get(\"deviceId\") || \"sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1\";\nconst deviceType = \"sensor\";\n\n// 按照胜透物联网通信协议生成事件数据\nconst eventMessage = {\n \"messageId\": messageId,\n \"timestamp\": timestamp,\n \"clientCode\": clientCode,\n \"deviceId\": deviceId,\n \"deviceType\": deviceType,\n \"version\": \"1.0\",\n \"data\": {\n \"events\": [\n {\n \"eventType\": \"startup\",\n \"eventLevel\": \"info\",\n \"eventCode\": \"EVT_001\",\n \"description\": \"HomeAssistant温度传感器代理设备启动完成\",\n \"timestamp\": timestamp\n }\n ]\n }\n};\n\n// 设置MQTT发布主题\nmsg.topic = `${nameSpace}/sensor/${clientCode}/${deviceId}/events`;\nmsg.payload = JSON.stringify(eventMessage);\nmsg.qos = 1;\n\nnode.log(\"设备启动事件已生成,发布到主题: \" + msg.topic);\n\nreturn msg;", |
| | | "outputs": 1, |
| | | "timeout": "", |
| | | "noerr": 0, |
| | | "initialize": "", |
| | | "finalize": "", |
| | | "libs": [], |
| | | "x": 380, |
| | | "y": 320, |
| | | "wires": [ |
| | | [ |
| | | "573842ea77192dbb", |
| | | "4c630c164e08dc2c" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "mqtt-ha-broker", |
| | | "type": "mqtt-broker", |
| | | "name": "HomeAssistant MQTT", |
| | | "broker": "192.168.50.83", |
| | | "port": "1883", |
| | | "clientid": "", |
| | | "autoConnect": true, |
| | | "usetls": false, |
| | | "protocolVersion": "4", |
| | | "keepalive": "60", |
| | | "cleansession": true, |
| | | "autoUnsubscribe": true, |
| | | "birthTopic": "", |
| | | "birthQos": "0", |
| | | "birthPayload": "", |
| | | "birthMsg": {}, |
| | | "closeTopic": "", |
| | | "closeQos": "0", |
| | | "closePayload": "", |
| | | "closeMsg": {}, |
| | | "willTopic": "", |
| | | "willQos": "0", |
| | | "willPayload": "", |
| | | "willMsg": {}, |
| | | "sessionExpiry": "" |
| | | }, |
| | | { |
| | | "id": "mqtt-company-broker", |
| | | "type": "mqtt-broker", |
| | | "name": "公司MQTT服务器", |
| | | "broker": "mqtt-test.ihemodialysis.com", |
| | | "port": "62183", |
| | | "clientid": "", |
| | | "usetls": false, |
| | | "protocolVersion": "4", |
| | | "keepalive": "60", |
| | | "cleansession": true, |
| | | "birthTopic": "", |
| | | "birthQos": "0", |
| | | "birthPayload": "", |
| | | "birthMsg": {}, |
| | | "closeTopic": "", |
| | | "closeQos": "0", |
| | | "closePayload": "", |
| | | "closeMsg": {}, |
| | | "willTopic": "shengtou/sensor/TH001_HA/status", |
| | | "willQos": "0", |
| | | "willPayload": "{\"messageId\":\"sts_offline\",\"timestamp\":0,\"deviceId\":\"TH001_HA\",\"deviceType\":\"sensor\",\"version\":\"1.0\",\"data\":{\"status\":{\"online\":false,\"lastHeartbeat\":0,\"uptime\":0}}}", |
| | | "willMsg": {}, |
| | | "sessionExpiry": "" |
| | | } |
| | | ] |
| New file |
| | |
| | | // 生成唯一的消息ID |
| | | const messageId = "temp_" + Date.now() + "_" + Math.floor(Math.random() * 1000); |
| | | const timestamp = Date.now(); |
| | | //nameSpace:命名空间,需要改改为正确的命名空间代码 |
| | | const nameSpace ="Environment" |
| | | //此处需要改为正确的客户代码 |
| | | const clientCode = "Data-It_XzOffice"; |
| | | //此片 从topic中提取传感器实体ID 无需更改 |
| | | const topicParts = msg.topic.split('/'); |
| | | const deviceId = topicParts.length >= 3 ? topicParts[2] : msg.topic; |
| | | const deviceType = "sensor"; |
| | | |
| | | // 解析温度值 |
| | | let temperature = parseFloat(msg.payload); |
| | | if (isNaN(temperature)) { |
| | | node.warn("接收到无效的温度值: " + msg.payload); |
| | | return null; |
| | | } |
| | | |
| | | // 按照胜透物联网通信协议格式化数据 |
| | | const protocolMessage = { |
| | | "messageId": messageId, |
| | | "timestamp": timestamp, |
| | | "clientCode": clientCode, |
| | | "deviceId": deviceId, |
| | | "deviceType": deviceType, |
| | | "version": "1.0", |
| | | "data": { |
| | | "properties": { |
| | | "temperature": { |
| | | "value": temperature, |
| | | "unit": "°C", |
| | | "quality": "good", |
| | | "timestamp": timestamp |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 设置输出消息 |
| | | msg.payload = JSON.stringify(protocolMessage); |
| | | msg.topic = `${nameSpace}/sensor/${deviceId}/properties`; |
| | | msg.qos = 0; |
| | | |
| | | node.log("温度数据已处理: " + temperature + "°C"); |
| | | |
| | | return msg; |
| New file |
| | |
| | | const messageId = "evt_startup_" + Date.now(); |
| | | const timestamp = Date.now(); |
| | | //nameSpace:命名空间,需要改改为正确的命名空间代码 |
| | | const nameSpace = "Environment" |
| | | const clientCode = "Data-It_XzOffice"; |
| | | //此处需改为正确的设备实体唯一ID |
| | | const deviceId = "sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1"; |
| | | const deviceType = "sensor"; |
| | | |
| | | // 按照胜透物联网通信协议生成事件数据 |
| | | const eventMessage = { |
| | | "messageId": messageId, |
| | | "timestamp": timestamp, |
| | | "clientCode": clientCode, |
| | | "deviceId": deviceId, |
| | | "deviceType": deviceType, |
| | | "version": "1.0", |
| | | "data": { |
| | | "events": [ |
| | | { |
| | | "eventType": "startup", |
| | | "eventLevel": "info", |
| | | "eventCode": "EVT_001", |
| | | "description": "HomeAssistant温度传感器代理设备启动完成", |
| | | "timestamp": timestamp |
| | | } |
| | | ] |
| | | } |
| | | }; |
| | | |
| | | // 设置MQTT发布主题 |
| | | msg.topic = `${nameSpace}/${deviceType}/${deviceId}/events`; |
| | | msg.payload = JSON.stringify(eventMessage); |
| | | msg.qos = 1; |
| | | |
| | | node.log("设备启动事件已生成,发布到主题: " + msg.topic); |
| | | |
| | | return msg; |
| New file |
| | |
| | | const messageId = "mdl_" + Date.now(); |
| | | const timestamp = Date.now(); |
| | | //nameSpace:命名空间,需要改改为正确的命名空间代码 |
| | | const nameSpace = "Environment" |
| | | const clientCode = "Data-It_XzOffice"; |
| | | //此处需改为正确的设备实体唯一ID |
| | | const deviceId = "sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1"; |
| | | const deviceType = "sensor"; |
| | | |
| | | // 按照胜透物联网通信协议生成物模型定义 |
| | | const modelMessage = { |
| | | "messageId": messageId, |
| | | "timestamp": timestamp, |
| | | "clientCode": clientCode, |
| | | "deviceId": deviceId, |
| | | "deviceType": deviceType, |
| | | "version": "1.0", |
| | | "data": { |
| | | "model": { |
| | | "deviceInfo": { |
| | | "manufacturer": "小米", |
| | | "model": "MIAOMIAOC_CN_BLT_3_11A1SBR5K5O01_T2", |
| | | "version": "1.0.0", |
| | | "description": "米家温湿度传感器(通过HomeAssistant集成)" |
| | | }, |
| | | "properties": { |
| | | "temperature": { |
| | | "dataType": "float", |
| | | "unit": "°C", |
| | | "range": { |
| | | "min": -40, |
| | | "max": 85 |
| | | }, |
| | | "precision": 1, |
| | | "description": "环境温度" |
| | | } |
| | | }, |
| | | "events": { |
| | | "startup": { |
| | | "eventType": "info", |
| | | "description": "设备启动事件" |
| | | } |
| | | }, |
| | | "alarms": { |
| | | "TEMP_HIGH": { |
| | | "alarmType": "threshold_exceeded", |
| | | "description": "温度过高报警", |
| | | "defaultThreshold": 35.0 |
| | | }, |
| | | "TEMP_LOW": { |
| | | "alarmType": "threshold_exceeded", |
| | | "description": "温度过低报警", |
| | | "defaultThreshold": 0.0 |
| | | } |
| | | } |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 设置MQTT发布主题 |
| | | msg.topic = `${nameSpace}/${deviceType}/${deviceId}/model`; |
| | | msg.payload = JSON.stringify(modelMessage); |
| | | msg.qos = 1; |
| | | |
| | | node.log("设备物模型数据已生成,发布到主题: " + msg.topic); |
| | | |
| | | return msg; |
| New file |
| | |
| | | const messageId = "sts_" + Date.now(); |
| | | const timestamp = Date.now(); |
| | | //nameSpace:命名空间,需要改改为正确的命名空间代码 |
| | | const nameSpace = "Environment" |
| | | const clientCode = "Data-It_XzOffice"; |
| | | //此处需改为正确的设备实体唯一ID |
| | | const deviceId = "sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1"; |
| | | const deviceType = "sensor"; |
| | | |
| | | // 计算设备运行时间(从第一次启动开始) |
| | | if (!context.get("startTime")) { |
| | | context.set("startTime", timestamp); |
| | | } |
| | | const startTime = context.get("startTime"); |
| | | const uptime = timestamp - startTime; |
| | | |
| | | // 按照胜透物联网通信协议生成状态数据 |
| | | const statusMessage = { |
| | | "messageId": messageId, |
| | | "timestamp": timestamp, |
| | | "clientCode": clientCode, |
| | | "deviceId": deviceId, |
| | | "deviceType": deviceType, |
| | | "version": "1.0", |
| | | "data": { |
| | | "status": { |
| | | "online": true, |
| | | "lastHeartbeat": timestamp, |
| | | "uptime": uptime |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // 设置MQTT发布主题 |
| | | msg.topic = `${nameSpace}/${deviceType}/${deviceId}/status`; |
| | | msg.payload = JSON.stringify(statusMessage); |
| | | msg.qos = 0; |
| | | |
| | | node.log("设备状态心跳已发送,发布到主题: " + msg.topic); |
| | | |
| | | return msg; |
| New file |
| | |
| | | ## 透析中心环境温度发送到胜透的操作指南 |
| | | |
| | | ### 1. 前提条件 |
| | | |
| | | - 已安装并配置好Home Assistant |
| | | - 已经安装并配置了MQTT Broker |
| | | - 已经安装并配置了Node-RED |
| | | - 已经安装并配置了米家温度传感器(如Miaomiaoc CN BLT 3) |
| | | - 已经获取到了胜透的MQTT服务器Broker地址和端口 |
| | | |
| | | ### 2. 在Home Assistant中配置传感器自动化 |
| | | |
| | | #### 1.找到环境温度传感器实体ID |
| | | |
| | | 透析中心可能安装了多个米家温度传感器,首先需要找到对应的实体ID。 |
| | | 可以在Home Assistant的开发者工具中查看当前所有传感器的状态。 |
| | | |
| | |  |
| | | 也可以在已经设备与服务,集成,中找到米家的设备,找到那个传感器 |
| | |  |
| | | |
| | |  |
| | | 找到需要配置为环境温度的传感器 |
| | |  |
| | | 点击温度实体 |
| | |  |
| | | |
| | | 点击设置图标 |
| | |  |
| | | |
| | |  |
| | | 如本例获取到的实体ID为`sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1`。记住此ID,后面很多地方都会用到. |
| | | |
| | | #### 2. 创建自动化规则 |
| | | |
| | | 我们需要创建一个自动化规则,当传感器状态更新时及每隔30秒,将温度数据发送到Home Assistant内部集成的的MQTT服务器。 |
| | | 进入 "配置" → "自动化和场景" → "自动化",点击右下角的 "+" 创建新的自动化规则。 |
| | |  |
| | | 进入自动化规则编辑界面后,点击右下角的"创建自动化"按钮。在弹出的对话框中选择"创建新的自动化"。 |
| | |  |
| | | 在新建自动化界面,点右上角的三个点,如下图所示 |
| | |  |
| | | 在弹出的下拉菜单中,点击进入YHAML编辑,进入代码模式,如下所示 |
| | |  |
| | | |
| | |  |
| | | 在这个大的文本框中粘入如下代码: |
| | | |
| | | ```YAML |
| | | alias: 发布环境温度传感器状态到MQTT |
| | | description: 当米家温度传感器状态变化或每30秒定时时,自动发布到MQTT供Node-RED使用 |
| | | triggers: |
| | | - entity_id: sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1 |
| | | trigger: state |
| | | - seconds: /30 |
| | | trigger: time_pattern |
| | | conditions: |
| | | - condition: template |
| | | value_template: >- |
| | | {{ states('sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1') |
| | | not in ['unavailable', 'unknown', 'None'] and |
| | | states('sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1') | |
| | | float(-999) != -999 }} |
| | | actions: |
| | | - data: |
| | | topic: >- |
| | | homeassistant/sensor/sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1/state |
| | | payload: >- |
| | | {{ |
| | | states('sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1') |
| | | }} |
| | | retain: false |
| | | qos: 0 |
| | | action: mqtt.publish |
| | | mode: single |
| | | ``` |
| | | |
| | | ***注意:*** |
| | | 以上代码中的 `sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1` 是示例实体ID,请替换为你**实际的温度传感器实体ID**。 |
| | | 放置完成后,如下图示意 |
| | |  |
| | |  |
| | | 观察上次触发列是否会每隔30秒执行一次 |
| | |  |
| | | 返回到传感器的设备界面也能看到设备的自动化下面有一条自动化规则,如下图 |
| | |  |
| | | 到此,自动化规则已配置完成。 |
| | | |
| | | ### 3. 在Node-RED中配置MQTT接收转发 |
| | | |
| | | #### 1. 打开Node-RED编辑器 |
| | | |
| | | 打开nod-red编辑器,有的HA可能是配置在左侧航菜单中,如下图志示,点击即可进入 |
| | |  |
| | | 有的是没有配置node-red到左侧导航菜单的,这种情况下,可以点击"设置-加载项"进入加载项页面 |
| | |  |
| | | 找到Node-RED加载项,点击进入 |
| | |  |
| | |  |
| | | 进入界面如下: |
| | |  |
| | | |
| | | #### 2. 创建新工作流 |
| | | |
| | | 点击右上角的"菜单"按钮,再点导入。 |
| | |  |
| | | |
| | | 在此处贴入工作流代码,注意:要选择导入到新流程 |
| | |  |
| | | 工作流代码如下 |
| | | |
| | | ```json |
| | | [ |
| | | { |
| | | "id": "1407d4531e19a951", |
| | | "type": "tab", |
| | | "label": "透析中心环境温度数据转发", |
| | | "disabled": false, |
| | | "info": "从HomeAssistant获取温度传感器数据并发布到公司MQTT服务器" |
| | | }, |
| | | { |
| | | "id": "97e254f4a87ddde9", |
| | | "type": "mqtt in", |
| | | "z": "1407d4531e19a951", |
| | | "name": "订阅HA温度传感器", |
| | | "topic": "homeassistant/sensor/+/state", |
| | | "qos": "0", |
| | | "datatype": "auto", |
| | | "broker": "mqtt-ha-broker", |
| | | "nl": false, |
| | | "rap": true, |
| | | "rh": 0, |
| | | "inputs": 0, |
| | | "x": 150, |
| | | "y": 100, |
| | | "wires": [ |
| | | [ |
| | | "44d729e67618e4f2" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "44d729e67618e4f2", |
| | | "type": "function", |
| | | "z": "1407d4531e19a951", |
| | | "name": "处理温度数据", |
| | | "func": "// 从全局变量获取共享配置\nconst nameSpace = global.get(\"nameSpace\") || \"Environment\";\nconst clientCode = global.get(\"clientCode\") || \"Data-It_XzOffice\";\nconst targetDeviceId = global.get(\"deviceId\") || \"sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1\";\n\n// 从topic中提取当前设备ID\nconst topicParts = msg.topic.split('/');\nconst currentDeviceId = topicParts.length >= 3 ? topicParts[2] : \"\";\n\n// 只处理目标设备的消息\nif (currentDeviceId !== targetDeviceId) {\n node.log(\"忽略设备消息: \" + currentDeviceId + \" (目标设备: \" + targetDeviceId + \")\");\n return null;\n}\n\n// 生成唯一的消息ID\nconst messageId = \"temp_\" + Date.now() + \"_\" + Math.floor(Math.random() * 1000);\nconst timestamp = Date.now();\nconst deviceType = \"sensor\";\n\n// 解析温度值\nlet temperature = parseFloat(msg.payload);\nif (isNaN(temperature)) {\n node.warn(\"接收到无效的温度值: \" + msg.payload);\n return null;\n}\n\n// 按照胜透物联网通信协议格式化数据\nconst protocolMessage = {\n \"messageId\": messageId,\n \"timestamp\": timestamp,\n \"clientCode\": clientCode,\n \"deviceId\": targetDeviceId,\n \"deviceType\": deviceType,\n \"version\": \"1.0\",\n \"data\": {\n \"properties\": {\n \"temperature\": {\n \"value\": temperature,\n \"unit\": \"°C\",\n \"quality\": \"good\",\n \"timestamp\": timestamp\n }\n }\n }\n};\n\n// 设置输出消息\nmsg.payload = JSON.stringify(protocolMessage);\nmsg.topic = `${nameSpace}/sensor/${clientCode}/${targetDeviceId}/properties`;\nmsg.qos = 0;\n\nnode.log(\"温度数据已处理: \" + temperature + \"°C (设备: \" + targetDeviceId + \")\");\n\nreturn msg;", |
| | | "outputs": 1, |
| | | "timeout": "", |
| | | "noerr": 0, |
| | | "initialize": "", |
| | | "finalize": "", |
| | | "libs": [], |
| | | "x": 420, |
| | | "y": 100, |
| | | "wires": [ |
| | | [ |
| | | "573842ea77192dbb", |
| | | "4c630c164e08dc2c" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "573842ea77192dbb", |
| | | "type": "mqtt out", |
| | | "z": "1407d4531e19a951", |
| | | "name": "发布到公司MQTT", |
| | | "topic": "", |
| | | "qos": "", |
| | | "retain": "", |
| | | "respTopic": "", |
| | | "contentType": "", |
| | | "userProps": "", |
| | | "correl": "", |
| | | "expiry": "", |
| | | "broker": "mqtt-company-broker", |
| | | "x": 990, |
| | | "y": 80, |
| | | "wires": [] |
| | | }, |
| | | { |
| | | "id": "4c630c164e08dc2c", |
| | | "type": "debug", |
| | | "z": "1407d4531e19a951", |
| | | "name": "调试输出", |
| | | "active": true, |
| | | "tosidebar": true, |
| | | "console": false, |
| | | "tostatus": false, |
| | | "complete": "payload", |
| | | "targetType": "msg", |
| | | "statusVal": "", |
| | | "statusType": "auto", |
| | | "x": 1040, |
| | | "y": 360, |
| | | "wires": [] |
| | | }, |
| | | { |
| | | "id": "495c392574ee1bf4", |
| | | "type": "inject", |
| | | "z": "1407d4531e19a951", |
| | | "name": "发布设备物模型", |
| | | "props": [ |
| | | { |
| | | "p": "payload" |
| | | }, |
| | | { |
| | | "p": "topic", |
| | | "vt": "str" |
| | | } |
| | | ], |
| | | "repeat": "60", |
| | | "crontab": "", |
| | | "once": true, |
| | | "onceDelay": "5", |
| | | "topic": "shengtou/sensor/TH001_HA/model", |
| | | "payload": "", |
| | | "payloadType": "date", |
| | | "x": 170, |
| | | "y": 180, |
| | | "wires": [ |
| | | [ |
| | | "3aea3ce2deb4ea99" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "3aea3ce2deb4ea99", |
| | | "type": "function", |
| | | "z": "1407d4531e19a951", |
| | | "name": "生成物模型数据", |
| | | "func": "const messageId = \"mdl_\" + Date.now();\nconst timestamp = Date.now();\n// 从全局变量获取共享配置\nconst nameSpace = global.get(\"nameSpace\") || \"Environment\";\nconst clientCode = global.get(\"clientCode\") || \"Data-It_XzOffice\";\nconst deviceId = global.get(\"deviceId\") || \"sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1\";\nconst deviceType = \"sensor\";\n\n// 按照胜透物联网通信协议生成物模型定义\nconst modelMessage = {\n \"messageId\": messageId,\n \"timestamp\": timestamp,\n \"clientCode\": clientCode,\n \"deviceId\": deviceId,\n \"deviceType\": deviceType,\n \"version\": \"1.0\",\n \"data\": {\n \"model\": {\n \"deviceInfo\": {\n \"manufacturer\": \"小米\",\n \"model\": \"MIAOMIAOC_CN_BLT_3_11A1SBR5K5O01_T2\",\n \"version\": \"1.0.0\",\n \"description\": \"米家温湿度传感器(通过HomeAssistant集成)\"\n },\n \"properties\": {\n \"temperature\": {\n \"dataType\": \"float\",\n \"unit\": \"°C\",\n \"range\": {\n \"min\": -40,\n \"max\": 85\n },\n \"precision\": 1,\n \"description\": \"环境温度\"\n }\n },\n \"events\": {\n \"startup\": {\n \"eventType\": \"info\",\n \"description\": \"设备启动事件\"\n }\n },\n \"alarms\": {\n \"TEMP_HIGH\": {\n \"alarmType\": \"threshold_exceeded\",\n \"description\": \"温度过高报警\",\n \"defaultThreshold\": 35.0\n },\n \"TEMP_LOW\": {\n \"alarmType\": \"threshold_exceeded\",\n \"description\": \"温度过低报警\",\n \"defaultThreshold\": 0.0\n }\n }\n }\n }\n};\n\n// 设置MQTT发布主题\nmsg.topic = `${nameSpace}/sensor/${clientCode}/${deviceId}/model`;\nmsg.payload = JSON.stringify(modelMessage);\nmsg.qos = 1;\n\nnode.log(\"设备物模型数据已生成,发布到主题: \" + msg.topic);\n\nreturn msg;", |
| | | "outputs": 1, |
| | | "timeout": "", |
| | | "noerr": 0, |
| | | "initialize": "", |
| | | "finalize": "", |
| | | "libs": [], |
| | | "x": 400, |
| | | "y": 160, |
| | | "wires": [ |
| | | [ |
| | | "573842ea77192dbb", |
| | | "4c630c164e08dc2c" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "70e1e283448c941e", |
| | | "type": "inject", |
| | | "z": "1407d4531e19a951", |
| | | "name": "设备状态心跳", |
| | | "props": [ |
| | | { |
| | | "p": "payload" |
| | | }, |
| | | { |
| | | "p": "topic", |
| | | "vt": "str" |
| | | } |
| | | ], |
| | | "repeat": "120", |
| | | "crontab": "", |
| | | "once": true, |
| | | "onceDelay": "10", |
| | | "topic": "shengtou/sensor/TH001_HA/status", |
| | | "payload": "", |
| | | "payloadType": "date", |
| | | "x": 160, |
| | | "y": 240, |
| | | "wires": [ |
| | | [ |
| | | "ae74b6d386748045" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "ae74b6d386748045", |
| | | "type": "function", |
| | | "z": "1407d4531e19a951", |
| | | "name": "生成状态数据", |
| | | "func": "const messageId = \"sts_\" + Date.now();\nconst timestamp = Date.now();\n// 从全局变量获取共享配置\nconst nameSpace = global.get(\"nameSpace\") || \"Environment\";\nconst clientCode = global.get(\"clientCode\") || \"Data-It_XzOffice\";\nconst deviceId = global.get(\"deviceId\") || \"sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1\";\nconst deviceType = \"sensor\";\n\n// 计算设备运行时间(从第一次启动开始)\nif (!context.get(\"startTime\")) {\n context.set(\"startTime\", timestamp);\n}\nconst startTime = context.get(\"startTime\");\nconst uptime = timestamp - startTime;\n\n// 按照胜透物联网通信协议生成状态数据\nconst statusMessage = {\n \"messageId\": messageId,\n \"timestamp\": timestamp,\n \"clientCode\": clientCode,\n \"deviceId\": deviceId,\n \"deviceType\": deviceType,\n \"version\": \"1.0\",\n \"data\": {\n \"status\": {\n \"online\": true,\n \"lastHeartbeat\": timestamp,\n \"uptime\": uptime\n }\n }\n};\n\n// 设置MQTT发布主题\nmsg.topic = `${nameSpace}/sensor/${clientCode}/${deviceId}/status`;\nmsg.payload = JSON.stringify(statusMessage);\nmsg.qos = 0;\n\nnode.log(\"设备状态心跳已发送,发布到主题: \" + msg.topic);\n\nreturn msg;", |
| | | "outputs": 1, |
| | | "timeout": "", |
| | | "noerr": 0, |
| | | "initialize": "", |
| | | "finalize": "", |
| | | "libs": [], |
| | | "x": 400, |
| | | "y": 240, |
| | | "wires": [ |
| | | [ |
| | | "573842ea77192dbb", |
| | | "4c630c164e08dc2c" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "7f9acf10d0c6085d", |
| | | "type": "inject", |
| | | "z": "1407d4531e19a951", |
| | | "name": "初始化全局配置", |
| | | "props": [ |
| | | { |
| | | "p": "payload" |
| | | } |
| | | ], |
| | | "repeat": "", |
| | | "crontab": "", |
| | | "once": true, |
| | | "onceDelay": "1", |
| | | "topic": "", |
| | | "payload": "", |
| | | "payloadType": "date", |
| | | "x": 150, |
| | | "y": 40, |
| | | "wires": [ |
| | | [ |
| | | "6f6b825dfc5e9512" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "6f6b825dfc5e9512", |
| | | "type": "function", |
| | | "z": "1407d4531e19a951", |
| | | "name": "设置全局配置", |
| | | "func": "// 设置全局共享常量\nglobal.set(\"nameSpace\", \"Environment\");\nglobal.set(\"clientCode\", \"Data-It_XzOffice\");\nglobal.set(\"deviceId\", \"sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1\");\n\nnode.log(\"全局配置已设置: nameSpace=\" + global.get(\"nameSpace\") + \", clientCode=\" + global.get(\"clientCode\") + \", deviceId=\" + global.get(\"deviceId\"));\n\nreturn msg;", |
| | | "outputs": 1, |
| | | "timeout": "", |
| | | "noerr": 0, |
| | | "initialize": "", |
| | | "finalize": "", |
| | | "libs": [], |
| | | "x": 360, |
| | | "y": 40, |
| | | "wires": [ |
| | | [ |
| | | "794c422d39898a7f" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "794c422d39898a7f", |
| | | "type": "function", |
| | | "z": "1407d4531e19a951", |
| | | "name": "更新MQTT订阅Topic", |
| | | "func": "// 从全局变量生成HomeAssistant MQTT订阅Topic\nconst deviceId = global.get(\"deviceId\") || \"sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1\";\nconst haTopic = `homeassistant/sensor/${deviceId}/state`;\n\n// 将Topic保存到全局变量供其他地方使用\nglobal.set(\"haSubscribeTopic\", haTopic);\n\nnode.log(\"HomeAssistant订阅Topic已更新: \" + haTopic);\nnode.log(\"注意:如需更改MQTT In节点的订阅Topic,请手动修改为: \" + haTopic);\n\nreturn msg;", |
| | | "outputs": 1, |
| | | "timeout": "", |
| | | "noerr": 0, |
| | | "initialize": "", |
| | | "finalize": "", |
| | | "libs": [], |
| | | "x": 590, |
| | | "y": 40, |
| | | "wires": [ |
| | | [] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "3bf854e9bd3e6937", |
| | | "type": "inject", |
| | | "z": "1407d4531e19a951", |
| | | "name": "启动事件", |
| | | "props": [ |
| | | { |
| | | "p": "payload" |
| | | }, |
| | | { |
| | | "p": "topic", |
| | | "vt": "str" |
| | | } |
| | | ], |
| | | "repeat": "", |
| | | "crontab": "", |
| | | "once": true, |
| | | "onceDelay": "15", |
| | | "topic": "shengtou/sensor/TH001_HA/events", |
| | | "payload": "", |
| | | "payloadType": "date", |
| | | "x": 160, |
| | | "y": 340, |
| | | "wires": [ |
| | | [ |
| | | "26f6196b2d6fba29" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "26f6196b2d6fba29", |
| | | "type": "function", |
| | | "z": "1407d4531e19a951", |
| | | "name": "生成启动事件", |
| | | "func": "const messageId = \"evt_startup_\" + Date.now();\nconst timestamp = Date.now();\n// 从全局变量获取共享配置\nconst nameSpace = global.get(\"nameSpace\") || \"Environment\";\nconst clientCode = global.get(\"clientCode\") || \"Data-It_XzOffice\";\nconst deviceId = global.get(\"deviceId\") || \"sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1\";\nconst deviceType = \"sensor\";\n\n// 按照胜透物联网通信协议生成事件数据\nconst eventMessage = {\n \"messageId\": messageId,\n \"timestamp\": timestamp,\n \"clientCode\": clientCode,\n \"deviceId\": deviceId,\n \"deviceType\": deviceType,\n \"version\": \"1.0\",\n \"data\": {\n \"events\": [\n {\n \"eventType\": \"startup\",\n \"eventLevel\": \"info\",\n \"eventCode\": \"EVT_001\",\n \"description\": \"HomeAssistant温度传感器代理设备启动完成\",\n \"timestamp\": timestamp\n }\n ]\n }\n};\n\n// 设置MQTT发布主题\nmsg.topic = `${nameSpace}/sensor/${clientCode}/${deviceId}/events`;\nmsg.payload = JSON.stringify(eventMessage);\nmsg.qos = 1;\n\nnode.log(\"设备启动事件已生成,发布到主题: \" + msg.topic);\n\nreturn msg;", |
| | | "outputs": 1, |
| | | "timeout": "", |
| | | "noerr": 0, |
| | | "initialize": "", |
| | | "finalize": "", |
| | | "libs": [], |
| | | "x": 380, |
| | | "y": 320, |
| | | "wires": [ |
| | | [ |
| | | "573842ea77192dbb", |
| | | "4c630c164e08dc2c" |
| | | ] |
| | | ] |
| | | }, |
| | | { |
| | | "id": "mqtt-ha-broker", |
| | | "type": "mqtt-broker", |
| | | "name": "HomeAssistant MQTT", |
| | | "broker": "192.168.50.83", |
| | | "port": "1883", |
| | | "clientid": "", |
| | | "autoConnect": true, |
| | | "usetls": false, |
| | | "protocolVersion": "4", |
| | | "keepalive": "60", |
| | | "cleansession": true, |
| | | "autoUnsubscribe": true, |
| | | "birthTopic": "", |
| | | "birthQos": "0", |
| | | "birthPayload": "", |
| | | "birthMsg": {}, |
| | | "closeTopic": "", |
| | | "closeQos": "0", |
| | | "closePayload": "", |
| | | "closeMsg": {}, |
| | | "willTopic": "", |
| | | "willQos": "0", |
| | | "willPayload": "", |
| | | "willMsg": {}, |
| | | "sessionExpiry": "" |
| | | }, |
| | | { |
| | | "id": "mqtt-company-broker", |
| | | "type": "mqtt-broker", |
| | | "name": "公司MQTT服务器", |
| | | "broker": "mqtt-test.ihemodialysis.com", |
| | | "port": "62183", |
| | | "clientid": "", |
| | | "usetls": false, |
| | | "protocolVersion": "4", |
| | | "keepalive": "60", |
| | | "cleansession": true, |
| | | "birthTopic": "", |
| | | "birthQos": "0", |
| | | "birthPayload": "", |
| | | "birthMsg": {}, |
| | | "closeTopic": "", |
| | | "closeQos": "0", |
| | | "closePayload": "", |
| | | "closeMsg": {}, |
| | | "willTopic": "shengtou/sensor/TH001_HA/status", |
| | | "willQos": "0", |
| | | "willPayload": "{\"messageId\":\"sts_offline\",\"timestamp\":0,\"deviceId\":\"TH001_HA\",\"deviceType\":\"sensor\",\"version\":\"1.0\",\"data\":{\"status\":{\"online\":false,\"lastHeartbeat\":0,\"uptime\":0}}}", |
| | | "willMsg": {}, |
| | | "sessionExpiry": "" |
| | | } |
| | | ] |
| | | ``` |
| | | |
| | | 当然,你也可以从[工作流文件](http://datacdn.data-it.tech/HomeAssistant/%E8%BD%AC%E5%8F%91%E7%8E%AF%E5%A2%83%E6%B8%A9%E5%BA%A6/flows%28%E8%BD%AC%E5%8F%91%E7%8E%AF%E5%A2%83%E6%B8%A9%E5%BA%A6%29.zip)导入新新工作流。 |
| | | 点击上述链接后,下载文件,并解压,得到一个名为`flows.json`的文件。 |
| | |  |
| | | 点击导入节点文件, |
| | | |
| | |  |
| | | |
| | |  |
| | | 部署成功后,会出现如下图所示的工作流 |
| | |  |
| | | 此时,工作流已创完成. |
| | | |
| | | #### 3. 配置MQTT节点 |
| | | |
| | | 工作流中有两个MQTT节点,分别是Home Assistant的MQTT Broker和公司MQTT服务器的Broker。这两个节点的配置需要根据实际情况进行调整。 |
| | | 首先,要先配置Home Assistant的MQTT Broker节点,确保其连接信息正确无误。 |
| | | 在我们没有配置mqtt in 的连接信息时,我们发现这个节点一直处理连接中 |
| | |  |
| | | 双击该节点,进入配置界面 |
| | |  |
| | | 点击编辑图标 |
| | |  |
| | | 在编辑mqtt-broker节点的界面中的连接参数选项卡中,输入正确的服务端与端口号 |
| | |  |
| | | 如果MQTT有用户名和密码,请在认证选项卡中输入正确的用户名和密码后,然后点更新 |
| | |  |
| | | 然后,再点击完成 |
| | |  |
| | | 最后点击部署 |
| | |  |
| | | 部署完成后,我们可以看到,此节点会变为已联接 |
| | |  |
| | | 对于"发布到公司MQTT"节点,我们需要配置连接到公司MQTT服务器的Broker。也时同样的操作方法,双击 |
| | |  |
| | |  |
| | |  |
| | | 然后,更新,完成,部署即可。 |
| | | |
| | | ***查找HomeAssistant的Mqtt配置信息*** |
| | | |
| | | 对于HomeAssistant我们一般都是提前部署了内置的MQTT Broker,在不知端口与密码的情况下,我样可以在HomeAssistant中去查找 |
| | | 进入设置加载项 |
| | |  |
| | | 找到Mosquitto broker这个选项卡,点击进入 |
| | |  |
| | | 点击配置选项卡 |
| | |  |
| | |  |
| | |  |
| | | 在这里可以看到MQTT的端口号与用户名密码等信息,及下面的mqtt开放的端口信息,ip地址,就是ha主机所在的Ip地址。以上信息,用来配置工作流的mqtt in的参数。 |
| | | |
| | | ***公司mqtt服务端口信息*** |
| | | 这个参数,请向公司管理员获取,或者在公司MQTT服务器的配置文件中查看。 |
| | | |
| | | #### 4.配置工作流全局变量 |
| | | |
| | | 双击工作流的节点"设置全局配置",打开编辑function节点 |
| | |  |
| | | 对如下三行,进行更改 |
| | | |
| | | ```javascript |
| | | // 设置全局共享常量 |
| | | global.set("nameSpace", "Environment"); //命名空间,默认为:Environment,如果有需要,以联系相关工程师确认后,更改. |
| | | global.set("clientCode", "Data-It_XzOffice");// 客户端代码,默认为:Data-It_XzOffice,一定要改为中心对就的ClientCode, |
| | | global.set("deviceId", "sensor.miaomiaoc_cn_blt_3_11a1sbr5k5o01_t2_temperature_p_2_1");//一定要改为之前我们定的传感器实体ID |
| | | ``` |
| | | |
| | | 如果不确定,请联系相关工程师确认。 |
| | |  |
| | | 然后,点击,完成,部署 |
| | |  |
| | | 然后,下一步,非常重要,一定要执行 |
| | |  |
| | | 这一步,非常重要,必须执行,否则工作流没有把全局变量写入工作流,会导致发布数据不正确。 |
| | | 然后,我样就可以用在debug的日志中看到我们发布的节点数据了, |
| | |  |
| | | 此图,可以观察到mqtt发布到公司的数据,clientCode,deviceid是否正常了。 |
| | | |
| | | ##### 5. 测试工作流的数据是否正常 |
| | | |
| | | 我们可以使用第三方工具来查看公司mqtt服务收到的数据是否正常如下图 |
| | |  |
| | | |
| | | 这里我推荐使用工具[MQTTX](https://mqttx.app/) |
| | | 下载地址为:https://mqttx.app/downloads |
| | |  |
| | | |
| | | 至此,透析中心HA环境温度数据发送到胜透的工作流配置完成。 |
| | | <end> |
| New file |
| | |
| | | # 胜透物联网通信协议 - MQTT版本 |
| | | |
| | | |
| | | |
| | | ## 1. 协议概述 |
| | | |
| | | 本协议用于胜透物联网设备与服务端基于MQTT协议的数据通信,统一设备数据上报、事件、报警、物模型、命令等交互格式,便于服务端自动发现、解析和管理设备。 |
| | | |
| | | ## 2. Topic约定 |
| | | |
| | | - Topic结构:`{nameSpace}/{deviceType}/{clientCode}/{deviceId}/{messageType}` |
| | | - nameSpace:命名空间,默认为`shengtou` |
| | | - deviceType:设备类型(如sensor、gateway等) |
| | | - clientCode:客户代码/客户ID |
| | | - deviceId:设备唯一标识 |
| | | - messageType:消息类型,见下表 |
| | | |
| | | | 消息类型 | 方向 | 说明 | |
| | | |--------------|--------------|----------------------| |
| | | | properties | 设备→服务端 | 设备属性数据上报 | |
| | | | events | 设备→服务端 | 设备事件数据上报 | |
| | | | alarms | 设备→服务端 | 设备报警数据上报 | |
| | | | model | 设备→服务端 | 设备物模型定义 | |
| | | | status | 双向 | 设备在线状态 | |
| | | | commands | 服务端→设备 | 设备控制命令 | |
| | | | config | 服务端→设备 | 设备配置下发 | |
| | | |
| | | - 设备端需严格按照上述Topic结构进行消息发布和订阅。 |
| | | |
| | | ## 3. 消息格式约定 |
| | | |
| | | - 所有消息均为JSON格式,包含如下通用字段: |
| | | - messageId:唯一消息ID |
| | | - timestamp:消息时间戳(毫秒) |
| | | - clientCode:客户代码/客户ID |
| | | - deviceId:设备ID |
| | | - deviceType:设备类型 |
| | | - version:协议版本号 |
| | | - data:具体数据内容 |
| | | |
| | | ### 3.1 设备属性数据(properties) |
| | | |
| | | ```json |
| | | { |
| | | "messageId": "msg_20250614_001", |
| | | "timestamp": 1718380800000, |
| | | "clientCode": "Data-It_XzOffice", |
| | | "deviceId": "TH001", |
| | | "deviceType": "sensor", |
| | | "version": "1.0", |
| | | "data": { |
| | | "properties": { |
| | | "temperature": { "value": 25.6, "unit": "°C", "quality": "good", "timestamp": 1718380800000 }, |
| | | "humidity": { "value": 65.2, "unit": "%RH", "quality": "good", "timestamp": 1718380800000 } |
| | | } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 3.2 设备事件数据(events) |
| | | |
| | | ```json |
| | | { |
| | | "messageId": "evt_20250614_001", |
| | | "timestamp": 1718380800000, |
| | | "clientCode": "Data-It_XzOffice", |
| | | "deviceId": "TH001", |
| | | "deviceType": "sensor", |
| | | "version": "1.0", |
| | | "data": { |
| | | "events": [ |
| | | { "eventType": "startup", "eventLevel": "info", "eventCode": "EVT_001", "description": "设备启动完成", "timestamp": 1718380800000 } |
| | | ] |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 3.3 设备报警数据(alarms) |
| | | |
| | | ```json |
| | | { |
| | | "messageId": "alm_20250614_001", |
| | | "timestamp": 1718380800000, |
| | | "clientCode": "Data-It_XzOffice", |
| | | "deviceId": "TH001", |
| | | "deviceType": "sensor", |
| | | "version": "1.0", |
| | | "data": { |
| | | "alarms": [ |
| | | { "alarmId": "ALM_001", "alarmType": "threshold_exceeded", "alarmLevel": "warning", "alarmCode": "TEMP_HIGH", "description": "温度超出上限阈值", "triggerValue": 35.8, "threshold": 35.0, "property": "temperature", "timestamp": 1718380800000, "status": "active" } |
| | | ] |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 3.4 设备物模型定义(model) |
| | | |
| | | ```json |
| | | { |
| | | "messageId": "mdl_20250614_001", |
| | | "timestamp": 1718380800000, |
| | | "clientCode": "Data-It_XzOffice", |
| | | "deviceId": "TH001", |
| | | "deviceType": "sensor", |
| | | "version": "1.0", |
| | | "data": { |
| | | "model": { |
| | | "deviceInfo": { "manufacturer": "胜透科技", "model": "ST-TH-001", "version": "1.0.0", "description": "温湿度传感器" }, |
| | | "properties": { |
| | | "temperature": { "dataType": "float", "unit": "°C", "range": { "min": -40, "max": 85 }, "precision": 1, "description": "环境温度" }, |
| | | "humidity": { "dataType": "float", "unit": "%RH", "range": { "min": 0, "max": 100 }, "precision": 1, "description": "相对湿度" } |
| | | }, |
| | | "events": { "startup": { "eventType": "info", "description": "设备启动事件" } }, |
| | | "alarms": { "TEMP_HIGH": { "alarmType": "threshold_exceeded", "description": "温度过高报警", "defaultThreshold": 35.0 } } |
| | | } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 3.5 设备状态(status) |
| | | |
| | | ```json |
| | | { |
| | | "messageId": "sts_20250614_001", |
| | | "timestamp": 1718380800000, |
| | | "clientCode": "Data-It_XzOffice", |
| | | "deviceId": "TH001", |
| | | "deviceType": "sensor", |
| | | "version": "1.0", |
| | | "data": { |
| | | "status": { "online": true, "lastHeartbeat": 1718380800000, "uptime": 86400000 } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 3.6 服务端下行命令(commands/config) |
| | | |
| | | ```json |
| | | { |
| | | "messageId": "cmd_20250614_001", |
| | | "timestamp": 1718380800000, |
| | | "clientCode": "Data-It_XzOffice", |
| | | "deviceId": "TH001", |
| | | "deviceType": "sensor", |
| | | "version": "1.0", |
| | | "data": { |
| | | "command": { "commandType": "setProperty", "commandId": "SET_001", "parameters": { "reportInterval": 60000 }, "timeout": 30000 } |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ## 4. 物模型自动发现约定 |
| | | |
| | | - 设备首次上线后,需主动向`model` Topic发布自身物模型定义。 |
| | | - 物模型内容应完整描述设备的属性、事件、报警、设备信息等。 |
| | | - 设备物模型发生变更时,需重新发布。 |
| | | - 服务端通过订阅`{nameSpace}/+/+/+/model`实现自动发现和注册新设备。 |
| | | - 设备重启时建议再次发布物模型。 |
| | | |
| | | ### 4.1 物模型消息重发机制 |
| | | |
| | | 为降低物模型消息丢失导致服务端无法及时发现设备的风险,协议建议如下重发机制: |
| | | |
| | | - 设备在首次上线、重启或物模型变更时,主动向`model` Topic发布物模型消息。 |
| | | - 设备在首次发布后,建议在短时间内(如3~5秒)自动重发1~2次物模型消息,以提高消息送达成功率。 |
| | | - 物模型消息建议使用QoS 1,确保消息至少送达一次。 |
| | | - 若设备端支持,建议在收到服务端确认(如下行命令或配置)后停止重发。 |
| | | - 服务端应具备**幂等**处理能力,能正确识别和忽略重复的物模型消息。 |
| | | |
| | | 通过上述机制,可有效提升设备自动发现的可靠性,避免因单次消息丢失导致设备无法被服务端识别。 |
| | | |
| | | ### 4.2 设备主动上报依赖与健壮性提升方法 |
| | | |
| | | 为降低协议对设备主动上报物模型和状态的依赖、提升自动发现的健壮性,建议如下: |
| | | |
| | | - 设备端应严格遵循协议,确保每次上线、重启、物模型变更时,均主动上报物模型和状态。 |
| | | - 设备端应实现自动重发机制,确保物模型和状态消息可靠送达。 |
| | | - 服务端可定期检查设备状态Topic的活跃性,若长时间未收到设备状态或属性数据,可主动下发请求,提醒设备重新上报物模型和状态。 |
| | | - 服务端可对设备的活跃性和数据上报情况进行监控,发现异常及时告警和处理。 |
| | | - 协议可约定设备端在收到服务端特定请求(如“重新上报物模型”命令)时,必须立即重新上报物模型和状态。 |
| | | |
| | | 通过上述方法,即使部分设备端实现不规范,也能通过服务端主动检测和交互机制,提升自动发现和管理的健壮性,减少因设备未主动上报导致的识别遗漏。 |
| | | |
| | | ### 4.3 消息幂等处理机制 |
| | | |
| | | 为避免因消息重发、网络抖动等原因导致的重复数据处理,协议要求服务端具备消息幂等处理能力。实现原理与方法如下: |
| | | |
| | | - 协议规定所有上报消息(如物模型、属性、事件、报警等)必须包含唯一的messageId字段。 |
| | | - 服务端在接收每条消息时,应根据messageId、deviceId等关键信息进行去重判断。 |
| | | - 对于已处理过的messageId,服务端应忽略重复消息,确保数据只被处理一次。 |
| | | - 服务端可通过维护消息ID缓存、数据库唯一索引等方式实现幂等性。 |
| | | - 幂等机制适用于所有可能因重发导致重复的消息类型,尤其是物模型、报警、事件等重要消息。 |
| | | |
| | | 通过上述机制,可有效防止重复数据写入和业务逻辑重复执行,保障系统数据一致性和协议健壮性。 |
| | | |
| | | ### 4.4 大规模场景下的优化与扩展 |
| | | |
| | | 为支持大规模设备接入和高并发数据处理,协议建议如下优化与分布式扩展方法: |
| | | |
| | | - Topic设计应支持分层和分组,便于服务端按需批量订阅和分区处理。 |
| | | - 服务端可采用分布式MQTT集群或多实例部署,提升消息接收和处理能力。 |
| | | - 数据存储层建议采用分布式数据库或分区表设计,支持高并发写入和大数据量存储。 |
| | | - 消息处理流程可通过消息队列、流式处理等方式实现异步解耦和负载均衡。 |
| | | - 对于热点设备或高频Topic,可采用分区消费、负载均衡订阅等机制,避免单点瓶颈。 |
| | | - 服务端应具备动态扩容能力,支持根据设备规模和业务压力灵活扩展。 |
| | | - 协议版本和物模型管理应支持多版本并存,便于平滑升级和兼容历史设备。 |
| | | |
| | | 通过上述方法,可有效支撑成千上万设备的并发接入和数据上报,保障系统的高可用性和可扩展性。 |
| | | |
| | | ### 4.5 物模型变更的兼容性与变更通知 |
| | | |
| | | 为保障设备物模型变更后的兼容性和服务端适配能力,协议建议如下: |
| | | |
| | | - 设备物模型发生变更(如属性、事件、报警结构调整)时,必须及时向model Topic重新发布完整的最新物模型。 |
| | | - 物模型消息中应包含明确的modelVersion字段,标识物模型版本。 |
| | | - 服务端应支持多版本物模型的并存和兼容,确保历史数据和新数据均可正确解析。 |
| | | - 服务端在检测到设备物模型版本变更时,应自动适配新模型,并可根据需要进行数据结构升级或兼容处理。 |
| | | - 协议建议服务端在物模型变更后,向相关订阅方或管理系统推送变更通知,便于业务系统及时感知和适配。 |
| | | - 设备端如收到服务端下发的“重新上报物模型”命令时,需立即重新发布当前物模型。 |
| | | |
| | | 通过上述机制,可确保设备物模型变更时系统具备良好的兼容性和可维护性,避免因模型变更导致的数据解析或业务中断。 |
| | | |
| | | |
| | | ## 5. 其它约定 |
| | | |
| | | - 所有消息建议使用UTF-8编码。 |
| | | - 时间戳统一为毫秒级Unix时间戳。 |
| | | - 建议属性、事件、报警等字段与物模型保持一致。 |
| | | - 设备属性、事件、报警等数据字段应与物模型定义严格对应。 |
| | | - 设备应定期发布状态(status)消息,建议每60秒一次。 |
| | | - 设备离线时,Broker应通过Last Will机制发布离线状态。 |
| | | |
| | | ## 6. 数据质量与报警级别 |
| | | |
| | | | 质量标识 | 说明 | |
| | | |--------------|----------| |
| | | | good | 良好 | |
| | | | uncertain | 不确定 | |
| | | | bad | 坏值 | |
| | | | maintenance | 维护中 | |
| | | |
| | | | 报警级别 | 说明 | |
| | | |--------------|--------------| |
| | | | info | 信息 | |
| | | | warning | 警告 | |
| | | | minor | 次要报警 | |
| | | | major | 重要报警 | |
| | | | critical | 严重报警 | |
| | | |
| | | ## 7. QoS建议 |
| | | |
| | | | 消息类型 | 建议QoS | |
| | | |------------|---------| |
| | | | 属性数据 | 0 | |
| | | | 事件数据 | 1 | |
| | | | 报警数据 | 1 | |
| | | | 物模型 | 1 | |
| | | | 控制命令 | 1 | |
| | | | 设备状态 | 0 | |
| | | |
| | | ## 8. 安全与权限 |
| | | |
| | | - 设备需使用唯一ID和密钥进行MQTT认证。 |
| | | - 建议使用TLS/SSL加密。 |
| | | - 设备仅能发布/订阅自身相关Topic。 |
| | | - 服务端拥有所有Topic的读写权限。 |
| | | |
| | | ## 9. 版本管理 |
| | | |
| | | - 当前协议版本:1.1 |
| | | - 版本号需在消息头中标识 |
| | | - 重大变更需升级协议版本 |
| | | |
| | | --- |
| | | |
| | | **文档版本**: 1.1 |
| | | **创建日期**: 2025年7月14日 |
| | | **维护者**: 岱特智能物联网团队 |
| | | |