端点描述 Cluster(Descriptor)
Cluster ID: 0x001D |
所在 Endpoint: 每个 Endpoint 都必须有(包括 Endpoint 0)
Descriptor 是 Matter 协议中最基础的 Cluster,它回答一个核心问题:这个 Endpoint 上有什么? Controller(手机、音箱、Hub)连上一个 Matter 设备后,第一件事就是读取各个 Endpoint 的 Descriptor, 从而知道设备支持哪些 Device Type、实现了哪些 Cluster、以及端点之间的层级关系。
这个 Cluster 是纯只读的 —— 没有任何 Command,只有 Attribute。设备在出厂时就决定了这些信息,运行时不会改变。
Descriptor Cluster 是 Matter 规范中的硬性要求(Mandatory)。 无论是 Endpoint 0(Root Node)还是功能端点(Endpoint 1、2、3...),都必须包含 Descriptor Cluster。 如果你的设备缺少它,将无法通过 Matter 认证,Controller 也无法正确识别设备能力。
Descriptor 解决什么问题
Matter 设备的数据模型是树状结构:一个 Node(物理设备)包含多个 Endpoint,每个 Endpoint 代表一个功能单元。 但 Controller 连上设备时,并不知道这棵树长什么样。Descriptor 就是这棵树的自描述机制:
- DeviceTypeList —— 告诉 Controller "我是什么"(门锁?灯?开关?)
- ServerList / ClientList —— 告诉 Controller "我能做什么"(支持哪些 Cluster)
- PartsList —— 告诉 Controller "我下面还有谁"(子端点列表)
Endpoint 0 是 Root Node(根节点),它的 Descriptor 有特殊含义:
- 它的
PartsList列出了该设备所有其他 Endpoint 的编号,是设备发现的起点 - Controller 的标准流程是:先读 Endpoint 0 的 PartsList,拿到所有端点编号,再逐个读取每个端点的 Descriptor
- Root Node 的 DeviceType 是
0x0016(Root Node Device Type)
属性总览
Descriptor 只有 5 个属性,但每个都至关重要。点击属性 ID 可跳转到详细说明。
| ID | 名称 | 类型 | 必选 | 说明 |
|---|---|---|---|---|
0x0000 |
DeviceTypeList | list<DeviceTypeStruct> | 是 | 端点支持的 Device Type 列表 |
0x0001 |
ServerList | list<cluster_id> | 是 | 端点实现的 Server Cluster 列表 |
0x0002 |
ClientList | list<cluster_id> | 是 | 端点实现的 Client Cluster 列表 |
0x0003 |
PartsList | list<endpoint_id> | 是 | 子端点编号列表 |
0x0004 |
TagList | list<SemanticTagStruct> | 否 | 语义标签列表(用于区分同类端点) |
属性详解
DeviceTypeList(0x0000)
列出该端点支持的所有 Device Type。每个条目是一个 DeviceTypeStruct,包含设备类型 ID 和版本号。
大多数端点只有一个 Device Type,但规范允许一个端点同时声明多个。
DeviceTypeStruct 结构
| 字段 | 类型 | 说明 |
|---|---|---|
| DeviceType | devtype_id (uint32) | Device Type ID,如 0x000A = Door Lock,0x0100 = On/Off Light |
| Revision | uint16 | 该 Device Type 定义的版本号,用于区分不同规范版本间的差异 |
0x0016 Root Node |
0x000A Door Lock |
0x0100 On/Off Light |
0x010D Extended Color Light |
0x000E Aggregator (Bridge) |
0x0107 Dimmable Light
ServerList(0x0001)
列出该端点作为 Server 实现的所有 Cluster ID。
所谓 Server,就是"持有数据、响应读写请求"的一方。
例如一把门锁的 Endpoint 1 的 ServerList 会包含 0x0101(DoorLock),
因为锁的状态(是否上锁、用户列表等)存储在设备端。
Controller 通过读取 ServerList,就知道可以对这个端点发送哪些 Cluster 的读/写/命令请求。
ClientList(0x0002)
列出该端点作为 Client 实现的所有 Cluster ID。 Client 是"发起请求"的一方 —— 大多数终端设备(灯、锁、传感器)的 ClientList 为空, 因为它们只被控制,不主动控制别人。
ClientList 非空的典型场景:物理开关(Switch)。
一个墙壁开关会在 ClientList 中声明 0x0006(OnOff Client),
表示它会主动向灯具发送开关命令。
PartsList(0x0003)
列出该端点的子端点编号。这个属性定义了端点之间的层级关系。
在 Endpoint 0(Root Node)上:
- PartsList 是一个扁平列表,包含该节点上所有其他 Endpoint 的编号
- 这是 Controller 发现设备全部功能端点的唯一入口
- 例如:
[1, 2, 3]表示设备还有 3 个功能端点
在功能端点上(Endpoint 1, 2, 3...):
- 大多数情况下为空列表(叶子端点,没有子端点)
- 只有 Bridge / Aggregator 设备的端点才会有非空的 PartsList,指向其桥接的子设备端点
TagList(0x0004)— 可选
为端点附加语义标签,用于区分功能相同但位置或用途不同的端点。 例如一个设备有两个温度传感器端点,可以通过 TagList 标记一个是"室内",另一个是"室外"。
TagList 需要设备声明 TAGLIST Feature(Feature Bit 0)才会出现。
SemanticTagStruct 结构
| 字段 | 类型 | 说明 |
|---|---|---|
| MfgCode | vendor_id (nullable) | 厂商代码。null 表示使用标准定义的标签,非 null 表示厂商自定义标签 |
| NamespaceID | uint8 | 标签命名空间 ID,定义标签的分类体系 |
| Tag | uint8 | 标签值,在对应 Namespace 下的具体含义 |
| Label | string (nullable) | 可选的人类可读标签文字,如 "Indoor"、"Left" |
Controller 如何使用 Descriptor 发现设备
Controller 连上一个 Matter 设备后,标准的端点发现流程如下:
- 读取 Endpoint 0 的 PartsList —— 拿到所有功能端点编号,如
[1, 2, 3] - 遍历每个端点,读取 DeviceTypeList —— 知道 Endpoint 1 是门锁、Endpoint 2 是温度传感器...
- 读取 ServerList —— 知道每个端点具体支持哪些 Cluster(能做哪些操作)
- 根据以上信息构建 UI —— 门锁端点显示开锁按钮,温度传感器端点显示温度数值
实际开发中,Controller 通常不会逐个属性读取,而是使用 Wildcard Read(通配符读取) 一次性读取所有 Endpoint 的 Descriptor Cluster,效率更高。 但理解上面的逐步流程有助于理解 Descriptor 各属性的作用。
打开 JSON 解析器,点“设备原始数据”示例再点解析,就能看到一把门锁的 DeviceTypeList、ServerList、PartsList 被逐项翻译成设备类型和 Cluster 名称。
DeviceTypeList 里的数字(如 10 = 0x000A 门锁)可以在 Matter ID 查询 里查到。
示例数据
场景一:普通设备(门锁)
Endpoint 0(Root Node)的 Descriptor:
{
// Endpoint 0(Root Node)的 Descriptor
"DeviceTypeList": [
{ "DeviceType": "0x0016", "Revision": 2 } // Root Node
],
"ServerList": [
"0x001D", // Descriptor
"0x0028", // BasicInformation
"0x002F", // PowerSource
"0x0030", // GeneralCommissioning
"0x0031", // NetworkCommissioning
"0x003E", // OperationalCredentials
"0x0033" // GeneralDiagnostics
],
"ClientList": [],
"PartsList": [ 1, 2, 3 ] // 该节点还有 Endpoint 1、2、3
}
Endpoint 1(门锁功能端点)的 Descriptor:
{
// Endpoint 1(功能端点,比如一把门锁)的 Descriptor
"DeviceTypeList": [
{ "DeviceType": "0x000A", "Revision": 3 } // DoorLock
],
"ServerList": [
"0x001D", // Descriptor(自身)
"0x0003", // Identify
"0x0101", // DoorLock
"0x002F" // PowerSource
],
"ClientList": [],
"PartsList": [] // 叶子端点,没有子端点
}
场景二:Bridge 设备
Bridge(网关/桥接器)的 Endpoint 0 的 PartsList 会列出所有桥接的子设备:
{
// Endpoint 0(Bridge 设备)的 Descriptor
"DeviceTypeList": [
{ "DeviceType": "0x000E", "Revision": 2 } // Aggregator(Bridge)
],
"ServerList": [ "0x001D", "0x0028", "0x0039" ],
"PartsList": [ 1, 2, 3, 4, 5 ] // 桥接了 5 个子设备
// 每个子端点(1~5)各自有独立的 Descriptor,
// 描述各自的 DeviceType 和 Cluster 列表
}
调试时如果发现 Controller 无法识别设备的某个功能,优先检查 Descriptor:
- Endpoint 0 的 PartsList 是否包含了该功能端点?
- 功能端点的 DeviceTypeList 是否正确?
- 功能端点的 ServerList 是否包含了所需的 Cluster?