Android の構造化 API

Structure API には、Android 向け Google Home API を介してアクセスできます。これらのパッケージをアプリにインポートします。

import com.google.home.Home
import com.google.home.Id
import com.google.home.Structure

エラー処理

Home API のメソッドは HomeException をスローする可能性があるため、try-catch ブロックを使用してすべての呼び出しで HomeException をキャッチすることをおすすめします。

HomeException を処理する際は、 error.code フィールドと error.message フィールドをチェックして、問題の内容を確認します。サブエラーコードが存在する場合もあるため、 getSubErrorCodes() メソッドを呼び出して結果を確認してください。

処理されない例外があると、アプリがクラッシュします。

詳細については、エラー処理をご覧ください。

通話のサンプル

ストラクチャのリストを取得する

初期化されると、structures() 呼び出しは、アクセス可能な構造の Flow を返します。

// Get a flow of all structures accessible to the user
val allStructuresFlow: HomeObjectsFlow<Structure> = home.structures()

// Calling list() on a HomeObjectsFlow returns the first Set of elements.
val allStructures: Set<Structure> = allStructuresFlow.list()

structures() API は、有効なストラクチャのリストをすぐに返さない可能性があるフローです。アプリがリアクティブで、そのフローに登録して UI を駆動する場合、最終的に有効なストラクチャのリストが返されます。他にも、ユーザーのスマートフォンが接続を失った場合や、ユーザーがアプリの権限を取り消した場合など、空のストラクチャ リストが返されることがあります。アプリでこれらのケースを処理する必要があります。

また、リアクティブ プログラミングではなく、命令型プログラミングが強く求められる場合は、ターミナル フロー演算子を使用できます。

val everyStructure = withTimeout(5000) { home.structures().first { it.isNotEmpty() } }

この呼び出しは、有効なストラクチャのリストがフローを通過するのを待ち、アプリで指定されたタイムアウト内にリストが受信されない場合はタイムアウトします。

ストラクチャのプロパティを取得する

ストラクチャのリストを取得したら、それらのプロパティにアクセスできます。

// Get a flow on a structure. Flow emits new values on structure metadata changes: name.
val structureFlow: Flow<Structure> = home.structures().itemFlow(myStructureId)

// Get a snapshot of the structure.
val structure: Structure = structureFlow.first()

// Get structure properties
println("id ${structure.id}")
println("name ${structure.name}")

名前でストラクチャを検索する

ストラクチャの名前がわかっている場合は、name プロパティを使用してアクセスすることもできます。

val myHome = home.structures().list().first { it.name == "My home" }

そこから、各ストラクチャのプロパティ、部屋、デバイスにアクセスできます。

複数のストラクチャを操作する

複数のストラクチャを使用するには、各ストラクチャへの参照を個別に取得します。

var structure1: Structure? = null
var structure2: Structure? = null

try {
  structure1 = home.structures().list().firstOrNull { it.name == "Main House" }
} catch (e: HomeException) {
  // Code for handling the exception
}
try {
  structure2 = home.structures().list().firstOrNull { it.name == "Guest Cottage" }
} catch (e: HomeException) {
  // Code for handling the exception
}

会議室のリストを取得する

ストラクチャを取得したら、部屋のリストを取得して、そのプロパティにアクセスできます。

val allRoomsFlow: HomeObjectsFlow<Room> = structure.rooms()
val allRooms: Set<Room> = allRoomsFlow.list()
val room: Room = allRooms.first()

println("id ${room.id}")
println("name ${room.name}")

チャットルームを作成する

新しい部屋を作成するには:

val testName = "Test Room Name"
val newRoom: Room = structure.createRoom(testName)

部屋を削除する

または、部屋を削除することもできます。

val roomToDelete = structure.rooms().list().filter { it.name == "room_id1" }.firstOrNull()
    structure.deleteRoom(roomToDelete!!)

ID のみを使用して部屋を削除することもできます。

val roomToDelete1 = allRooms.filter { it.id == testRoomId }.firstOrNull()
structure.deleteRoom(roomToDelete1!!)

デバイスが割り当てられている部屋を削除しても、デバイスはストラクチャに残りますが、部屋には割り当てられなくなります。

デバイスを別の部屋に移動する

ストラクチャを作成したら、そのストラクチャ内の別の部屋にデバイスを移動できます。

val room2 = structure.rooms().get(Id("room_id_other_structure"))
    val device1 = structure.devices().get(Id("device_id1"))
    structure.moveDevicesToRoom(room2!!, listOf(device1!!))

デバイス ID と部屋 ID のみがある場合は、次の方法でデバイスを移動することもできます。

structure.moveDevicesToRoom(Id("room_id_other_structure"), listOf(Id("device_id1")))

部屋の名前を変更する

setName() メソッドを呼び出して、部屋の名前を変更します。

livingRoom.setName("Living Room")

名前が 60 Unicode コードポイント(文字)の上限を超えると、切り捨てられます。エラーはスローされません。長い名前の処理はデベロッパーの責任であり、たとえば、名前が切り捨てられることをユーザーに通知するかどうかを決定できます。

Google Home エコシステムでは、ほとんどのデバイスタイプで、ユーザーはそのタイプのすべてのデバイスに一度に権限を付与できます。ロック、カメラ、ドアホンなどの機密性の高いデバイスタイプや制限付きのデバイスタイプについては、ユーザーが個別に権限を付与する必要があります。

ユーザーが機密性の高いデバイスタイプまたは制限付きのデバイスタイプへのアクセスを許可しているかどうかを判断するには、ストラクチャ レベルの consentedDeviceTypes() 関数を使用します。

import com.google.home.Structure
import com.google.home.DeviceType
import com.google.home.DeviceTypeFactory
import com.google.home.consentedDeviceTypes // Extension function from the SDK
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch

/**
 * Example of how an app may monitor which device types have been granted access by a user.
 */
fun monitorDeviceConsent(structure: Structure, myScope: CoroutineScope) {
    // Obtain the flow of consented device type factories
    val consentedTypesFlow: Flow<Set<DeviceTypeFactory<out DeviceType>>> =
        structure.consentedDeviceTypes()

    myScope.launch {
        consentedTypesFlow.collect { consentedSet ->
            // Check if the user has consented to share a specific restricted
            // type, such as a Doorbell or Camera.
            val hasCameraAccess = consentedSet.any {
                it.toString() == "matter.google.type.GoogleDoorbellDevice"
            }

            if (hasCameraAccess) {
                // Enable features that require camera access
            } else {
                // Inform the user or disable camera-specific features
            }
        }
    }
}

デバイス グループ

デバイス グループは、1 つのストラクチャ内のデバイスのユーザー定義コレクションを表します。グループ メンバーシップは Google サービスに保持され、ストラクチャの他のメンバーや、ストラクチャにアクセスできる他のアプリに表示されます。

物理的な部屋と同様に、デバイス グループは 1 つのストラクチャに属します。ただし、部屋とは異なり、デバイス グループは論理的なグループです。デバイスは、複数のデバイス グループに同時に属することができます。デバイス グループには最大 100 台のデバイスを含めることができます。

デバイス グループは DeviceGroup エンティティで表されます。これは HomeDevice とは異なります。デバイス グループをクエリして操作するには、FactoryRegistry を初期化するときに、Group、GroupManagement、GroupMembership トレイトとともに、entityTypes リストに DeviceGroup を含めます。

グループ内のデバイスを操作するには、メンバー デバイスを直接反復処理するか(グループ メンバー デバイスを操作するを参照)、DeviceGroup のデバイスタイプ部分をターゲットにします(デバイス グループを操作するを参照)。

デバイス グループのリストを取得する

ストラクチャ内のすべてのデバイス グループを監視するには、home.entities(DeviceGroup) を呼び出し、ストラクチャの ID でフィルタします。

home.entities(DeviceGroup).collect { allGroups ->
  val structureGroups = allGroups.filter { it.structureId == structure.id }
  for (group in structureGroups) {
    val groupName = group.trait(Group)?.Name
    println("Found group: $groupName with ID: ${group.id.id}")
  }
}

グループ メンバーのデバイスを操作する

group.devices() を使用してグループのメンバー デバイスを反復処理すると、個々のメンバーをきめ細かく制御できます。また、1 つのリクエストでコマンドをバッチ処理したり、メンバー間で属性を集約または書き込んだりできます。

たとえば、グループ内のすべての互換性のあるメンバーに対して、トレイトのコマンドを含む単一のバッチ リクエストを送信するには、次のようにします。

val members = group.devices(enableMultipartDevices = true).first()
val onOffTraits = members.mapNotNull { device ->
  device.partOrNull(OnOffLightDevice).first()?.standardTraits?.onOff
}

val responses = home.sendBatchedCommands {
  onOffTraits.map { onOff ->
    add(onOff.onBatchable())
  }
}

for (response in responses) {
  response.getOrThrow()
}

グループ内の一致するすべてのメンバー デバイスで属性を更新し(たとえば、すべての DimmableLightDevice メンバーで onTransitionTime を 1u に設定)、期限内に完了するのを待つこともできます。

val members = group.devices(enableMultipartDevices = true).first()

withTimeout(5.seconds) {
  members.map { device ->
    async {
      val light = device.partOrNull(DimmableLightDevice).first()
      val levelControl = light?.standardTraits?.levelControl
      if (
        levelControl?.supports(
          LevelControl.Attribute.onTransitionTime
        ) == true
      ) {
        levelControl.update { setOnTransitionTime(1u) }
      }
    }
  }.awaitAll()
}

グループのメンバー間で属性を集計するには(グループ内のすべての色温度ライトで最小の colorTempPhysicalMaxMireds を見つけるなど):

val members = group.devices(enableMultipartDevices = true).first()
val minMaxMireds: UShort? = members.mapNotNull { device ->
  val light = device.partOrNull(ColorTemperatureLightDevice).first()
  light?.standardTraits?.colorControl?.colorTempPhysicalMaxMireds
}.minOrNull()

デバイス グループを作成する

構造体で GroupManagement を使用して、デバイス グループを作成します。

val groupManagement = structure.trait(GroupManagementTrait)
val response = groupManagement?.createUserDefinedGroup(
  name = "Living room lights",
  memberDeviceObjectIds = listOf(light1.id.id, light2.id.id)
)

グループ メンバーを管理する

既存のグループにメンバーを追加するには:

structure.trait(GroupManagementTrait)?.addGroupMembers(
  groupObjectId = group.id.id,
  memberDeviceObjectIds = listOf(light3.id.id)
)

グループからメンバーを削除するには:

structure.trait(GroupManagementTrait)?.removeGroupMembers(
  groupObjectId = group.id.id,
  memberDeviceObjectIds = listOf(light1.id.id)
)

デバイス グループの名前を変更する

デバイス グループの名前を変更するには、グループの GroupTrait の name 属性を更新します。

group.trait(GroupTrait)?.update {
  name = "Movie corner"
}

デバイス グループを削除する

ストラクチャからデバイス グループを削除するには:

structure.trait(GroupManagementTrait)?.deleteGroup(
  groupObjectId = group.id.id
)

自動化

Automation API のエントリ ポイントはストラクチャを介して行われます。Google Home API の自動化の詳細については、Android の Automation API の概要をご覧ください。