物模型 (Thing Model) 设计规范
1. 核心概念
物模型(Thing Model,简称 YTM)是设备的数字化说明书。它以标准化的 JSON 格式,抽象并描述了设备“是什么”以及“能做什么”。
1.1 设计原则
- 协议无关:物模型只描述能力,不绑定具体的通信协议(MQTT/HTTP/BLE)。
- 强类型:所有数据必须有明确的类型定义,便于后端校验和前端自动生成 UI。
- 三要素:核心能力由 属性 (Properties)、事件 (Events) 和 服务 (Services) 组成。
1.2 三要素定义
| 要素 | 英文名 | 定义 | 典型示例 | 读写特性 |
|---|---|---|---|---|
| 属性 | Properties | 设备运行时的状态,通常是连续变化的数值或离散的状态。 | 开关状态、电流值、LED颜色 | 可读、可写、只读 |
| 事件 | Events | 设备主动上报的重要信息,包含发生的时间和相关数据。 | 过流报警、设备上线、任务完成 | 设备上报(只读) |
| 服务 | Services | 设备可被外部调用的复杂指令,支持输入参数和输出参数。 | 远程重启、OTA升级、定时任务设置 | 云端调用(请求/响应) |
我们参考阿里云的物模型设计规范,进行了调整和扩展。阿里云的物模型如下:
{
"schema": "物模型结构定义的访问URL。",
"profile": {
"productKey": "当前产品的ProductKey。"
},
"properties": [
{
"identifier": "属性唯一标识符(物模型模块下唯一)。",
"name": "属性名称",
"accessMode": "属性读写类型:只读(r)或读写(rw)。",
"required": "是否是标准功能的必选属性:是(true),否(false)。",
"dataType": {
"type": "属性类型: int(原生)、float(原生)、double(原生)、text(原生)、date(String类型UTC毫秒)、bool(0或1的int类型)、enum(int类型,枚举项定义方法与bool类型定义0和1的值方法相同)、struct(结构体类型,可包含前面7种类型,下面使用"specs":[{}]描述包含的对象)、array(数组类型,支持int、double、float、text、struct)。",
"specs": {
"min": "参数最小值(int、float、double类型特有)。",
"max": "参数最大值(int、float、double类型特有)。",
"unit": "属性单位(int、float、double类型特有,非必填)。",
"unitName": "单位名称(int、float、double类型特有,非必填)。",
"size": "数组元素的个数,最大512(array类型特有)。",
"step": "步长(text、enum类型无此参数)。",
"length": "数据长度,最大10240(text类型特有)。",
"0": "0的值(bool类型特有)。",
"1": "1的值(bool类型特有)。",
"item": {
"type": "数组元素的类型(array类型特有)。"
}
}
}
}
],
"events": [
{
"identifier": "事件唯一标识符(物模型模块下唯一,其中post是默认生成的属性上报事件)。",
"name": "事件名称。",
"desc": "事件描述。",
"type": "事件类型(info、alert、error)。",
"required": "是否是标准功能的必选事件:是(true),否(false)。",
"outputData": [
{
"identifier": "参数唯一标识符。",
"name": "参数名称。",
"dataType": {
"type": "属性类型: int(原生)、float(原生)、double(原生)、text(原生)、date(String类型UTC毫秒)、bool(0或1的int类型)、enum(int类型,枚举项定义方法与bool类型定义0和1的值方法相同)、struct(结构体类型,可包含前面7种类型,下面使用"specs":[{}]描述包含的对象)、array(数组类型,支持int、double、float、text、struct)。",
"specs": {
"min": "参数最小值(int、float、double类型特有)。",
"max": "参数最大值(int、float、double类型特有)。",
"unit": "属性单位(int、float、double类型特有,非必填)。",
"unitName": "单位名称(int、float、double类型特有,非必填)。",
"size": "数组元素的个数,最大512(array类型特有)。",
"step": "步长(text、enum类型无此参数)。",
"length": "数据长度,最大10240(text类型特有)。",
"0": "0的值(bool类型特有)。",
"1": "1的值(bool类型特有)。",
"item": {
"type": "数组元素的类型(array类型特有)。"
}
}
}
}
],
"method": "事件对应的方法名称(根据identifier生成)。"
}
],
"services": [
{
"identifier": "服务唯一标识符(物模型模块下唯一,其中set/get是根据属性的accessMode默认生成的服务)。",
"name": "服务名称。",
"desc": "服务描述。",
"required": "是否是标准功能的必选服务:是(true),否(false)。",
"callType": "async(异步调用)或sync(同步调用)。",
"inputData": [
{
"identifier": "入参唯一标识符。",
"name": "入参名称。",
"dataType": {
"type": "属性类型: int(原生)、float(原生)、double(原生)、text(原生)、date(String类型UTC毫秒)、bool(0或1的int类型)、enum(int类型,枚举项定义方法与bool类型定义0和1的值方法相同)、struct(结构体类型,可包含前面7种类型,下面使用"specs":[{}]描述包含的对象)、array(数组类型,支持int、double、float、text、struct)。",
"specs": {
"min": "参数最小值(int、float、double类型特有)。",
"max": "参数最大值(int、float、double类型特有)。",
"unit": "属性单位(int、float、double类型特有,非必填)。",
"unitName": "单位名称(int、float、double类型特有,非必填)。",
"size": "数组元素的个数,最大512(array类型特有)。",
"step": "步长(text、enum类型无此参数)。",
"length": "数据长度,最大10240(text类型特有)。",
"0": "0的值(bool类型特有)。",
"1": "1的值(bool类型特有)。",
"item": {
"type": "数组元素的类型(array类型特有)。"
}
}
}
}
],
"outputData": [
{
"identifier": "出参唯一标识符。",
"name": "出参名称。",
"dataType": {
"type": "属性类型: int(原生)、float(原生)、double(原生)、text(原生)、date(String类型UTC毫秒)、bool(0或1的int类型)、enum(int类型,枚举项定义方法与bool类型定义0和1的方法相同)、struct(结构体类型,可包含前面7种类型,下面使用"specs":[{}]描述包含的对象)、array(数组类型,支持int、double、float、text、struct)。",
"specs": {
"min": "参数最小值(int、float、double类型特有)。",
"max": "参数最大值(int、float、double类型特有)。",
"unit": "属性单位(int、float、double类型特有,非必填)。",
"unitName": "单位名称(int、float、double类型特有,非必填)。",
"size": "数组元素的个数,最大512(array类型特有)。",
"step": "步长(text、enum类型无此参数)。",
"length": "数据长度,最大10240(text类型特有)。",
"0": "0的值(bool类型特有)。",
"1": "1的值(bool类型特有)。",
"item": {
"type": "数组元素的类型(array类型特有)。"
}
}
}
}
],
"method": "服务对应的方法名称(根据identifier生成)。"
}
],
//仅自定义模块的TSL中有以下参数。
"functionBlockId": "自定义模块的唯一标识符,模块ID。",
"functionBlockName": "自定义模块名称。",
//当产品下添加了自定义模块,默认模块TSL中会包含以下参数,表示已添加的自定义模块列表。
"functionBlocks": [
{
"functionBlockId": "自定义模块的唯一标识符,模块ID。",
"functionBlockName": "自定义模块名称。",
"productKey": "产品ProductKey。"
}
]
}作为消费级产品,物模型的设计不需要过于复杂,但需要考虑以下几个方面:
- 设备类型:根据设备的功能和用途,选择合适的设备类型。例如,智能开关、智能插座、智能传感器等。
- 属性定义:根据设备的状态和功能,定义设备的属性。属性包括设备的当前状态、工作模式、工作参数等。
- 事件定义:根据设备的行为和状态变化,定义设备的事件。事件包括设备的状态变化、工作异常、用户操作等。
- 服务定义:根据设备的功能和用途,定义设备的服务。服务包括设备的控制指令、配置参数、查询状态等。
下面是参考阿里云物联网平台的物模型设计规范,最终的物模型设计如下:
2. 物模型结构定义
我们采用 YunStack Lite Thing Model v1.0,专注于消费级智控场景。核心设计原则是:
- 极简:去除 Schema、Profile、Required 等冗余字段,减轻 MCU 解析负担。
- UI 驱动:内置 UI 渲染提示(Hints),便于 App/小程序自动生成控制面板。
- 扁平:不引入复杂的模块(Module)概念,直接使用属性、事件、动作三要素。
一个完整的物模型 JSON 文件结构如下:
json
{
"version": "1.0",
"deviceType": "relay_controller",
"meta": {
"name": "四路智能继电器",
"description": "支持电流监测的四路智能继电器"
},
"properties": [],
"events": [],
"actions": []
}2.1 顶层字段说明
| 字段 | 类型 | 必选 | 说明 |
|---|---|---|---|
version | string | ✔ | 模型版本号,如 1.0。 |
deviceType | string | ✔ | 设备类型标识,如 smart_switch。 |
meta | object | ✖ | 元数据,包含名称、描述等。 |
properties | array | ✔ | 属性定义列表(状态模型)。 |
events | array | ✔ | 事件定义列表(主动上报)。 |
actions | array | ✔ | 动作定义列表(云端调用,即原“服务”)。 |
3. 属性 (Properties) 定义
属性描述设备的状态,支持读写权限控制。
3.1 JSON 结构
json
{
"id": "switch_1",
"name": "开关1",
"type": "bool",
"access": "rw",
"default": false,
"ui": {
"component": "switch",
"icon": "power"
}
}3.2 字段详解
- id: 唯一标识符,使用 snake_case 命名。
- name: 显示名称。
- type: 数据类型(见下文)。
- access: 访问权限。
r: 只读(如传感器数据)。rw: 读写(如开关状态)。
- default: 默认值,便于设备或影子初始化。
- ui: (可选)前端渲染提示,指示 App 应使用什么组件渲染。
4. 数据类型系统
精简后的类型系统,仅保留 7 种核心类型,去除 double/date/text 等冗余类型。
4.1 基础类型一览
| 类型 | 说明 | JSON 示例 |
|---|---|---|
| bool | 布尔 | true / false (不再定义 0/1 映射) |
| int | 整数 | 100 |
| float | 浮点 | 25.5 |
| string | 字符串 | "hello" |
| enum | 枚举 | 0 (配合 values 定义) |
| array | 数组 | [1, 2, 3] |
| struct | 结构体 | {"x": 1, "y": 2} |
4.2 类型详细规范
(1) 数值型 (int / float)
json
{
"id": "temperature",
"type": "float",
"min": -40,
"max": 125,
"unit": "°C",
"step": 0.1
}(2) 枚举型 (enum)
json
{
"id": "mode",
"type": "enum",
"values": [
{ "value": 0, "label": "自动" },
{ "value": 1, "label": "制冷" },
{ "value": 2, "label": "制热" }
]
}(3) 数组型 (array)
json
{
"id": "history_temp",
"type": "array",
"itemType": "float",
"maxSize": 10
}(4) 结构体 (struct)
json
{
"id": "wifi_info",
"type": "struct",
"fields": [
{ "id": "ssid", "type": "string", "name": "SSID" },
{ "id": "rssi", "type": "int", "name": "信号强度" }
]
}5. 事件 (Events) 定义
事件用于设备主动上报关键信息。
5.1 JSON 结构
json
{
"id": "over_current",
"name": "过流告警",
"level": "error",
"params": [
{ "id": "current_val", "type": "float", "unit": "A" }
]
}- level: 事件等级,支持
info(信息),warn(警告),error(故障)。
6. 动作 (Actions) 定义
动作(原服务)用于云端向设备发送指令。
6.1 JSON 结构
json
{
"id": "restart",
"name": "重启设备",
"input": [],
"output": [
{ "id": "success", "type": "bool" }
]
}- input: 输入参数列表。
- output: 输出参数列表。
7. 完整示例:四路继电器物模型
json
{
"version": "1.0",
"deviceType": "relay_controller",
"meta": {
"name": "智能继电器",
"description": "带电流监测的四路控制器"
},
"properties": [
{
"id": "relay_states",
"name": "通道状态",
"type": "array",
"access": "rw",
"itemType": "bool",
"maxSize": 4,
"ui": { "component": "multi-switch" }
},
{
"id": "chip_temp",
"name": "芯片温度",
"type": "float",
"access": "r",
"unit": "°C",
"min": -40,
"max": 85
}
],
"events": [
{
"id": "over_current",
"name": "过流保护触发",
"level": "error",
"params": [
{ "id": "channel_index", "type": "int" },
{ "id": "current_val", "type": "float", "unit": "A" }
]
}
],
"actions": [
{
"id": "factory_reset",
"name": "恢复出厂设置",
"input": [],
"output": [
{ "id": "success", "type": "bool" }
]
}
]
}8. 为什么选择 Lite 模型?
| 特性 | 阿里云 TSL | YunStack Lite | 优势 |
|---|---|---|---|
| 复杂嵌套 | dataType -> specs 多层嵌套 | 扁平化字段 (type, min, max 平铺) | MCU 解析代码量减少 50% |
| 布尔定义 | 需定义 0:关, 1:开 | 统一 true/false | 消除协议歧义 |
| 模块化 | 支持 FunctionBlock | 不支持 | 架构更轻量 |
| UI 适配 | 无内置规范 | 内置 ui 字段 | App 可零代码生成界面 |
