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 中表示为单个设备。如需启用此行为,请对 Structure、Room 或 HomeManager 调用 devices() 方法,并将 enableMultipartDevices 参数设置为 true:

var multipartDevice = structure.devices(enableMultipartDevices = multipartEnabled)
var multipartDevice = multipartDevices.list().filter{ device ->  device.has(PowerStrip)}

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

  • 包含多个开关的墙壁面板示例,展示了原生 Matter 表示法和 Home API 的多部分渲染

    墙板的多部分表示形式。

  • 冰箱示例,展示了原生 Matter 表示法和 Home API 的多部分呈现

    冰箱的多部分表示形式。

图 2:多部分渲染示例

您始终可以选择通过省略 enableMultipartDevices 参数或将其设置为 false 来获取平面表示法。

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

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

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()