4、设备属性上报:属性定义(TSL模型)、使用Python SDK上报设备属性、属性上报的QoS策略

好,咱们进入第四章节。这一章讲的是设备属性上报,说白了就是设备怎么跟云端“说话”,告诉云端它现在是什么状态。

我刚开始做IoT项目的时候,觉得上报属性不就是发个数据嘛,有什么难的?后来踩了坑才发现,这里面的门道还真不少。尤其是TSL模型的定义,如果一开始没规划好,后面改起来那叫一个痛苦。

4.1 属性定义(TSL模型)

TSL模型,全称是Thing Specification Language,也就是物模型规范。你可以把它理解成设备与云端之间的一种“契约”。

举个例子,你有一个温度传感器,它会上报温度值。那这个温度值叫什么名字?是什么数据类型?取值范围是多少?这些都需要在TSL模型里定义清楚。

我个人习惯,在定义属性之前,先画一张表,把设备的所有功能点列出来。比如:

属性名称 标识符 数据类型 取值范围 读写类型
当前温度 CurrentTemperature float -40 ~ 125 只读
当前湿度 CurrentHumidity float 0 ~ 100 只读
设备开关 PowerSwitch bool true/false 读写

有了这张表,再去阿里云物联网平台创建TSL模型就清晰多了。平台支持两种方式:一种是直接在控制台可视化编辑,另一种是通过JSON文件导入。

我推荐你用JSON文件导入的方式,尤其是当设备属性比较多的时候。为什么呢?因为可视化编辑一个个点太慢了,而且容易出错。JSON文件可以批量定义,还能用脚本自动生成。

下面是一个简单的TSL属性定义JSON示例:

{
  "schema": "https://iot-tsl.aliyun.com/schema/2.0",
  "properties": [
    {
      "identifier": "CurrentTemperature",
      "name": "当前温度",
      "dataType": {
        "type": "float",
        "specs": {
          "min": "-40",
          "max": "125",
          "step": "0.1"
        }
      },
      "accessMode": "r",
      "required": true
    },
    {
      "identifier": "PowerSwitch",
      "name": "设备开关",
      "dataType": {
        "type": "bool",
        "specs": {
          "0": "关闭",
          "1": "开启"
        }
      },
      "accessMode": "rw",
      "required": false
    }
  ]
}

这里要注意,accessMode字段决定了属性是只读(r)还是读写(rw)。只读属性设备只能上报,云端不能下发指令修改。读写属性则两边都可以操作。

小提示:定义TSL模型时,尽量把属性粒度做细一点。比如温度和湿度分开定义,不要合成一个“环境数据”对象。这样后续做数据分析和告警会更灵活。

4.2 使用Python SDK上报设备属性

模型定义好了,接下来就是写代码了。阿里云提供了Python SDK,叫alibabacloud-iot-device-sdk。安装很简单:

pip install alibabacloud-iot-device-sdk

嗯,这里要提醒一下,SDK的版本迭代比较快,建议你安装时指定版本号,避免后续接口变化导致代码跑不通。

下面是一个完整的属性上报示例:

from alibabacloud_iot_device_sdk.client import Client
import json
import time

# 设备认证信息
product_key = "你的ProductKey"
device_name = "你的DeviceName"
device_secret = "你的DeviceSecret"

# 初始化客户端
client = Client(product_key, device_name, device_secret)

# 连接到阿里云物联网平台
client.connect()

# 构造属性数据
properties = {
    "CurrentTemperature": 25.6,
    "CurrentHumidity": 68.3,
    "PowerSwitch": 1
}

# 上报属性
payload = json.dumps(properties)
topic = f"/sys/{product_key}/{device_name}/thing/event/property/post"

# 发布消息
client.publish(topic, payload, qos=1)

print("属性上报成功")

# 断开连接
client.disconnect()

这段代码看起来简单,但我曾经在这个地方栽过跟头。你猜怎么着?我忘了把字典转成JSON字符串,直接传了个Python字典进去,结果平台一直收不到数据。排查了半天才发现是类型问题。

所以记住:上报的数据必须是JSON字符串格式

另外,上报的Topic是固定的,格式为/sys/{productKey}/{deviceName}/thing/event/property/post。这个路径不能写错,否则数据发不上去。

重点:属性上报成功后,平台会返回一个响应消息,Topic是/sys/{productKey}/{deviceName}/thing/event/property/post_reply。你可以订阅这个Topic来确认上报是否成功。

4.3 属性上报的QoS策略

QoS,全称是Quality of Service,服务质量等级。MQTT协议定义了三个等级:

  • QoS 0:最多一次。消息发出去就不管了,不保证对方一定能收到。适合那些丢了也无所谓的数据,比如每秒上报一次的温度值。
  • QoS 1:至少一次。消息会重试直到收到确认。适合那些不能丢的数据,比如设备开关状态。
  • QoS 2:恰好一次。最可靠,但性能开销也最大。阿里云物联网平台目前不支持QoS 2。

我建议你根据业务场景来选择。举个例子:

如果你做的是环境监测,温度湿度每秒变化一次,用QoS 0就够了。就算偶尔丢一两条数据,下一秒钟又补上了,不影响整体趋势。

但如果你做的是智能门锁,门锁的开关状态必须准确上报,那就用QoS 1。我曾经遇到过一个项目,门锁状态用QoS 0上报,结果用户出门后APP上还显示“门未关”,吓得用户赶紧跑回家。其实门是关着的,只是那条状态消息丢了。

避坑指南:QoS 1虽然可靠,但会有重复消息的可能。也就是说,平台可能收到两条一模一样的数据。你需要在业务层做去重处理,比如给每条消息加一个递增的序列号。

最后总结一下:

  • TSL模型是设备与云端的“契约”,定义要清晰、粒度要细
  • Python SDK上报属性时,记得用json.dumps()转成字符串
  • QoS策略根据业务场景选择,不重要的数据用QoS 0,重要的用QoS 1

下一章我们会讲设备事件上报,比属性上报稍微复杂一点,但原理是相通的。到时候我会分享一个我在智能家居项目里遇到的“幽灵事件”案例,很有意思。