Android のマルチパート デバイス

一部の Matter デバイスは、同じデバイス タイプの複数のエンドポイントで構成されています。他の Matter デバイスは階層構造になっており、エンドポイントが他のエンドポイント内にネストされています。Home API では、どちらの種類のデバイスもマルチパート デバイスと呼ばれます。

フラットな表現

Home API のリリース 1.8 より前は、マルチパート デバイスは Home API によって、別々の関連性のないデバイスのセットとして表されます。これはフラット表現と呼ばれます。

たとえば、4 つのスイッチを備えた 1 つの壁パネル デバイスは、Google Home API では 4 つの個別のデバイスとして表示されます。冷蔵庫などの階層型 Matter デバイスは、Home API でデバイスのセットとして表されることがあります。各デバイスはエンドポイントの 1 つに対応します。

  • 複数のスイッチがある壁パネルの例。ネイティブ Matter 表現と Home API のフラット レンダリングを示しています

    4 スイッチの壁パネルの平面図。

  • ネイティブ Matter 表現と Home API のフラット レンダリングを示す冷蔵庫の例

    冷蔵庫のフラットな表現

図 1: フラット レンダリングの例

マルチパート表現

Home API のリリース 1.8 以降では、マルチパート デバイスを API で 1 つのデバイスとして表すことができます。この動作を有効にするには、StructureRoom、または HomeManagerdevices() メソッドを呼び出し、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 セマンティック タグを使用して、親デバイスまたはパーツで階層的に直接アクセスできます。セマンティック タグは、DescriptorTrait.SemanticTagStruct を使用して Home API に実装されます。

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