用户标签 Cluster(UserLabel)

Cluster ID: 0x0041  |  所在 Endpoint: 通常在 Endpoint 0(Root)或功能端点  |  角色: Server(可读写,无命令)

UserLabel 允许用户或 App 为设备打上自定义的键值对标签,用于分类、分组、备注等用途。 这是一个极其简单的 Cluster —— 0 个命令、0 个事件, 只有 1 个可写属性 LabelList,通过直接写属性来管理标签。

UserLabel vs FixedLabel

Matter 有两个标签 Cluster,区别在于谁能改:

  • FixedLabel(0x0040) —— 厂商在出厂时写入的标签,只读,App 无法修改。例如 "room"/"factory-default"、"model"/"v2"
  • UserLabel(0x0041) —— 用户自定义的标签,可读写,App 可以随时增删改。例如 "zone"/"living-room"、"owner"/"alice"

两者数据结构完全相同(都是 LabelStruct 列表),只是读写权限不同。 读取设备标签时,应该合并两个 Cluster 的结果,FixedLabel 提供厂商默认值,UserLabel 提供用户自定义值。

Per-Fabric 隔离

UserLabel 的标签是 per-fabric(按 Fabric 隔离)的。 每个 Fabric 只能看到和修改自己写入的标签,无法访问其他 Fabric 的标签。 例如,用户通过 Apple Home 写入的 "zone"/"kitchen",在 Google Home 上是看不到的。

属性

UserLabel 只有一个属性,且是必须支持的。

ID 名称 类型 读写 说明
0x00 LabelList list<LabelStruct> 读写 用户自定义的标签列表

LabelList(标签列表)

一个 LabelStruct 的列表,每个元素是一对 Label(键)+ Value(值)字符串。 App 通过 Write Attribute 操作来增删改标签 —— 每次写入都是整体替换, 不是追加。如果想新增一个标签,需要先读取现有列表,追加后再整体写回。

没有命令,全靠写属性

UserLabel 没有定义任何命令。所有操作(新增、修改、删除标签)都通过写 LabelList 属性完成。 这是 Matter 中少数「纯属性驱动」的 Cluster 之一。写入时要注意整体替换的语义 —— 漏掉已有标签相当于删除它。

LabelStruct 结构体

LabelStruct 是 UserLabel 和 FixedLabel 共用的数据结构,表示一个键值对标签。

字段 ID 名称 类型 约束 说明
0x00 Label string 最长 16 字符 标签的键名,如 "zone"、"owner"
0x01 Value string 最长 16 字符 标签的值,如 "living-room"、"alice"
长度限制

Label 和 Value 都有 最长 16 字符的硬限制。 App 端在写入前应做校验,超出长度的写入会被设备拒绝(返回 CONSTRAINT_ERROR)。 建议使用短小精悍的英文缩写作为键名,值可以适当使用中文但要注意字符长度(中文字符按 UTF-8 编码计算可能占 3 字节,但 Matter 按字符数计算,16 个中文字符是允许的)。

示例数据

读取一台智能灯的 UserLabel Cluster 属性:

{
  // --- 属性 ---
  "0x0": [                    // LabelList(标签列表)
    {
      "0": "zone",            // Label = "zone"
      "1": "living-room"      // Value = "living-room"
    },
    {
      "0": "owner",           // Label = "owner"
      "1": "alice"            // Value = "alice"
    },
    {
      "0": "floor",           // Label = "floor"
      "1": "2F"               // Value = "2F"
    }
  ]
}

写入标签(Write Attribute 请求):

{
  "writeRequests": [{
    "attributePath": {
      "endpointId": 1,
      "clusterId": "0x0041",
      "attributeId": "0x00"       // LabelList
    },
    "data": [
      { "0": "zone",  "1": "living-room" },
      { "0": "owner", "1": "alice" },
      { "0": "floor", "1": "2F" }
    ]
  }]
}
开发建议

写属性的流程是读 → 改 → 写三步:

  1. 先 Read Attribute 获取当前 LabelList
  2. 在本地修改列表(增 / 删 / 改某个标签)
  3. 将完整列表通过 Write Attribute 写回设备

直接写入新列表而不先读取,会丢失其他 App 之前写入的标签。 如果多个 App 可能同时操作标签,建议加上乐观锁逻辑(读取时记录版本,写入前再次确认)。

常见场景

场景 1:按区域分组管理设备

用户家中有多台同类设备(例如 5 个智能灯泡),需要按房间、楼层等维度组织管理。 通过 UserLabel 为每台设备打上位置标签,App 就可以按标签分组展示。

  1. 配网完成后,引导用户为设备设置区域标签
  2. 写入标签:{'{"zone": "living-room", "floor": "1F"}'}
  3. App 首页按 zone 值分组显示设备
  4. 用户搬动设备后,可在 App 中修改 zone 值
  5. 支持自定义区域名,不限于预置列表

与 Matter 的 Groups Cluster 不同,UserLabel 是纯元数据标记,不影响设备的群组控制行为。 适合做 App 层面的 UI 分组,而非设备层面的联动控制。

场景 2:多用户家庭的设备归属标记

一个家庭中有多个成员,某些设备归属于特定成员(如儿童房的灯、书房的台灯)。 通过 UserLabel 记录归属信息,App 可以为不同成员展示不同的设备视图。

  1. 为设备写入归属标签:{'{"owner": "alice", "usage": "reading"}'}
  2. App 根据当前登录用户的名字过滤 owner 标签
  3. 「我的设备」页面只展示 owner 匹配的设备
  4. 管理员视图仍可看到全部设备

注意:UserLabel 是 per-fabric 的,如果家庭成员使用不同的 Fabric(不同品牌的 App), 各自的标签互相不可见。同一 Fabric 下的所有 App 共享同一套标签。