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

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

フラット表現

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

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

  • 複数のスイッチを備えた壁パネルの例。ネイティブの 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() を呼び出すと、0 個以上のサブパートのリストが生成されます。

次の例は、マルチスイッチ デバイスの各部分にアクセスする方法を示しています。

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