Android 上的多部分设备

某些 Matter 设备由多个具有 相同设备类型的端点组成。其他 Matter 设备采用 分层结构,端点嵌套在其他端点内。在 Home API 中,这两种设备都称为多部分设备

平面表示法

在 Home API 1.8 版发布之前,多部分设备在 Home API 中表示为一组单独且不相关的设备。这称为平面表示法

例如,一个带有四个开关的单壁面板设备在 Home API 中显示为四个不同的且不相关的设备。而冰箱等分层 Matter设备在 Home API 中可能表示为一组设备,每个设备对应一个端点。

  • 显示原生 Matter 表示法和 Home API 的扁平渲染的带多个开关的墙壁面板示例

    4 开关壁面板的平面表示法。

  • 冰箱示例,显示了原生 Matter 表示法和 Home API 的扁平渲染

    冰箱的平面表示法

图 1:平面渲染 示例

多部分表示法

从 Home API 1.8 版开始,多部分设备可以在 API 中表示为单个设备。如需启用此行为,请对 StructureRoomHomeManager 调用 devices() 方法,并将 enableMultipartDevices 参数设置为 true

let devices = try await self.home.devices(enableMultipartDevices: true).list()
    let device = try XCTUnwrap(devices.first { $0.id == powerstripID })
    let outlets = try await device.types.getAll(of: OnOffPluginUnitDeviceType.self)

下图说明了 enableMultipartDevices 选项如何影响 Home API 中多部分设备的表示法:

  • 显示原生 Matter 表示法和 Home API 的多部分渲染的具有多个开关的墙壁面板示例

    壁面板的多部分表示法。

  • 冰箱示例,显示了原生 Matter 表示法和 Home API 的多部分渲染

    冰箱的多部分表示法。

图 2:多部分渲染示例

您可以随时选择获取平面表示法,方法是省略 enableMultipartDevices 参数,或将其设置为 false

在多部分设备中,设备类型的每个组件实例称为一个部分

可以使用设备类型或 Matter 语义 标记,以 分层方式直接访问父设备或某个部分中的部分。语义标记在 Home API 中通过 DescriptorTrait.SemanticTagStruct实现。

The DeviceType 抽象类 实现了 HasParts 接口, 让开发者可以通过 parts 属性和 part() 方法浏览设备树。每个部分实例也实现了 HasParts 接口,因此对某个部分调用 parts() 会生成一个包含零个或多个子部分的列表。

以下示例展示了如何访问多开关设备的部分:

val device =
      homeManager
        .devices(enableMultipartDevices = true)
        .itemFlow(Id(MULTI_SWITCH_DEVICE))
        .first()

// Here at top-level, we are using the homeDevice.parts() API to access flow of
// all the switches. Then we get the part ids.
val partIds =
      device
        .parts()
        .map { parts ->
          parts.filter { it.has(Switch) }.mapNotNull { it.metadata.partId }
        }
        .first()
        .toSet()

下一个示例展示了如何访问冰箱设备的部分:

val rootDevice = homeManager.devices(true).itemFlow(Id("device@uuid1"))

// On the top level, HomeDevice provides both plural (parts)
// and singular (part) APIs.
// The parts() API returns all the parts accessible from the top level,
// including Endpoint 0 and its children.
val childParts = rootDevice.parts().first()
// childParts contain (EP0 as RootNode, EP1 as Refrigerator)

// The singular part() API accepts DeviceType and tags (optional).
val refrigerator = rootDevice.part(Refrigerator).first()

// Get the refrigerator device which in this case is just device@uuid1
val refrigeratorDevice = homeManager.devices(false).itemFlow(refrigerator.metadata.partId.deviceId)

// DeviceType uses a synchronous API for providing access to parts
val cabinets = refrigerator.parts  // [EP2, EP3]

// Get the HomeDevice for these cabinets (device@uuid2 and device@uuid3)
val cabinetDeviceIds = cabinets.map { it.metadata.partId }

// Now use the devices API with enableMultipartDevices = false.
val cabinetDevices = homeManager.devices(false)
    .map { devices ->
devices.filter { it.id in cabinetDeviceIds }
}.first()