Structure API には、iOS 向け Google Home API を介してアクセスできます。
Structure API を使用するには、まず GoogleHomeSDK パッケージをアプリにインポートします。
import GoogleHomeSDK
エラー処理
Google Home API の一部のメソッドは HomeError をスローするため、do-catch ブロックを使用して、これらの呼び出しで HomeError をキャッチすることをおすすめします。
HomeError を処理する際は、code フィールドと message フィールドをチェックして、問題の内容を確認します。
処理されないエラーがあると、アプリがクラッシュします。
詳細については、エラー処理をご覧ください。
Structure API
Home はホームグラフを表し、Structure API のエントリ ポイントです。ストラクチャ、部屋、デバイスへの参照を提供します。
Structure は、ホームグラフ内のストラクチャを表します。id や name などの構造メタデータへのアクセスを提供します。
structures() を使用して、アカウント内のすべてのストラクチャを取得します。構造は Query の形式で返されます。この形式では、次の方法でデータを使用できます。
| API | 説明 |
|---|---|
stream() |
変更が発生するたびに各オブジェクトを個別に発行する Publisher を返します。 |
batched() |
現在の結果をオブジェクトの Set として出力する Publisher を返します。出力される各 Set は、オブジェクト グラフの現在の状態を表します。 |
list() |
現在の結果をオブジェクトの Set として返します。 |
structures().list() 呼び出しは、有効な構造セットをすぐに返さない場合があります。アプリがリアクティブで、stream() を呼び出してすべてのストラクチャの変更をサブスクライブして UI を駆動する場合、最終的に有効なストラクチャのリストが返されます。空のストラクチャ リストが返される状況は他にもあります。たとえば、ユーザーのスマートフォンが接続性を失った場合や、ユーザーがアプリへの権限を取り消した場合などです。アプリでこれらのケースを処理する必要があります。
@Published public private(set) var structures: [Structure] = []
private var structuresCancellable: AnyCancellable?
self.structuresCancellable = home
.structures()
.batched()
.receive(on: DispatchQueue.main)
.map { Array($0) }
.catch {
// Failed to load structures
return Just([Structure]())
}
.assign(to: \.structures, on: self)
ストラクチャの呼び出し例
ストラクチャのセットを取得する
Query<Structure> で list() を呼び出すと、最新の要素の Set が返されます。
// Get a stream of all structures accessible to the user let allStructuresChanges = self.home.structures() let allStructures = try? await allStructuresChanges.list()
リアクティブ アプリを設計する場合は、list() ではなく batched() 呼び出しと stream() 呼び出しを使用します。これらは、ホームグラフが変更されると自動的にデータを生成するためです。
ストラクチャのプロパティを取得する
ストラクチャのリストを取得したら、それらのプロパティにアクセスできます。
// Get a stream of changes taking place on a structure. let structureChanges = try await home.structures().list().filter { $0.id == structureID } // Get a snapshot of the structure. let structure = structureChanges.first! // Get structure properties print("id \(structure.id) ") print("name \(structure.name) ")
名前でストラクチャを検索する
ストラクチャの名前がわかっている場合は、name プロパティを使用してアクセスすることもできます。
do { structure1 = try await home.structures().list().first(where: { $0.name == "Main House" }) } catch let _ as HomeError { // Code for handling the exception }
そこから、各ストラクチャのプロパティ、部屋、デバイスにアクセスできます。
複数のストラクチャを操作する
複数のストラクチャを使用するには、各ストラクチャへの参照を個別に取得します。
var structure1: Structure! var structure2: Structure! do { structure1 = try await home.structures().list().first(where: { $0.name == "Main House" }) } catch let _ as HomeError { // Code for handling the exception } do { structure2 = try await home.structures().list().first(where: { $0.name == "Guest Cottage" }) } catch let _ as HomeError { // Code for handling the exception }
Rooms
部屋にはデバイスのグループが含まれます。部屋は常にストラクチャの一部であり、ストラクチャには複数の部屋を含めることができます。ストラクチャから部屋を削除しても、その部屋にあるデバイスはストラクチャから削除されません。ただし、部屋が削除されると、その部屋のデバイスは割り当て解除されます。
Home.rooms() を使用してアカウント内のすべての部屋を取得し、roomID = device.roomID を使用して各部屋の対応するデバイスを表示します。
self.home.rooms().batched()
.combineLatest(self.home.devices().batched())
.receive(on: DispatchQueue.main)
.catch { error in
// Failed to load rooms and devices
return Just((Set<Room>(), Set<HomeDevice>()))
}
.map { rooms, devices in
var devicesByRoom = [Room: [HomeDevice]]()
for room in rooms where room.structureID == currentStructureID {
devicesByRoom[room] = devices.filter { $0.roomID == room.id }
}
return devicesByRoom
}.assign(to: &self.$devicesByRoom)
サンプルルームの通話
会議室のリストを取得する
Home クラスを使用すると、部屋のリストを取得して、そのプロパティにアクセスできます。
let allRoomsChanges = self.home.rooms() let allRooms = try await allRoomsChanges.list() let room = allRooms.first! XCTAssertTrue(allRooms.contains(room)) print("id \(room.id) ") print("name \(room.name) ")
チャットルームを作成する
Structure で新しい部屋を作成するには:
let testName = "Test Room Name" var newRoom: Room! do { newRoom = try await structure.createRoom(name: testName) XCTAssertNotNil(newRoom) } catch let _ as HomeError { // Code for handling the exception }
部屋を削除する
または、部屋を削除することもできます。
val roomToDelete = structure.rooms().list().filter { it.name == "room_id1" }.firstOrNull() structure.deleteRoom(roomToDelete!!)
ID を使用してルームを削除することもできます。
let roomToDelete = allRooms.first(where: { $0.id == room.id }) if let roomToDelete1 = roomToDelete { do { try await structure.deleteRoom(roomToDelete1) } catch let _ as HomeError { // Code for handling the exception } }
デバイスが割り当てられている部屋を削除しても、デバイスはストラクチャに残りますが、部屋には割り当てられなくなります。
デバイスを別の部屋に移動する
Structure を使用して、デバイスを別の部屋に移動することもできます。
do { try await structure.move(device: light, to: room) } catch let _ as HomeError { // Code for handling the exception }
部屋の名前を変更する
setName(_:) メソッドを呼び出して、部屋の名前を変更します。
let updatedRoom = try await theRoom.setName("new room name")
部屋の名前を変更しても、元の Room 構造体は変更されず、変更は返された更新済みの Room オブジェクトに反映されます。
名前が 60 Unicode コードポイント(文字)の上限を超えると、切り捨てられます。エラーはスローされません。長い名前の処理はデベロッパーの責任であり、たとえば、名前が切り捨てられることをユーザーに通知するかどうかを決定できます。
デバイス グループ
デバイス グループは、1 つのストラクチャ内のデバイスのユーザー定義コレクションを表します。グループ メンバーシップは Google サービスに保持され、ストラクチャの他のメンバーや、ストラクチャにアクセスできる他のアプリに表示されます。
物理的な部屋と同様に、デバイス グループは 1 つのストラクチャに属します。ただし、部屋とは異なり、デバイス グループは論理的なグループです。デバイスは、複数のデバイス グループに同時に属することができます。デバイス グループには最大 100 台のデバイスを含めることができます。
デバイス グループはDeviceGroup エンティティで表されます。これは HomeDevice とは異なります。デバイス グループを操作するには、Home.configure で ReferencedAutomationTypes を構成するときに、関連するデバイスタイプとトレイトを、GroupTrait、GroupManagementTrait、GroupMembershipTrait トレイトとともに含めます。グループ内のデバイスを操作するには、メンバー デバイスを直接反復処理するか(グループ メンバー デバイスを操作するを参照)、DeviceGroup のデバイスタイプ部分をターゲットにします(デバイス グループを操作するを参照)。
デバイス グループのリストを取得する
家にあるすべてのデバイス グループをクエリするには:
let groups = try await home.entities(DeviceGroup.self).list()
for group in groups {
let groupTrait = await group.traits.get(Google.GroupTrait.self)
let name = groupTrait?.attributes.name ?? "Device Group"
print("Found group: \(name) with ID: \(group.id)")
}
グループ メンバーのデバイスを操作する
グループのメンバー デバイスを反復処理することで、個々のメンバーをきめ細かく制御できます。また、1 つのリクエストでコマンドをバッチ処理したり、メンバー間で属性を集計または書き込んだりできます。
まず、各デバイスの GroupMembershipTrait でターゲット グループ ID を確認して、家にあるデバイスをフィルタします。
let allDevices = try await home.devices().list()
var members: [HomeDevice] = []
for device in allDevices {
if let membership = await device.traits.get(
Google.GroupMembershipTrait.self
),
membership.attributes.groupObjectIdsArray.contains(group.id) {
members.append(device)
}
}
グループ内のすべての互換性のあるメンバーに対して、トレイトのコマンドを含む単一のバッチ リクエストを送信するには:
var onOffTraits: [Matter.OnOffTrait] = []
for device in members {
if let light = await device.types.get(OnOffLightDeviceType.self),
let onOff = light.matterTraits.onOffTrait {
onOffTraits.append(onOff)
}
}
let traitsToUse = onOffTraits
let responses = try await home.sendBatchedCommands { batcher in
return try traitsToUse.map { batcher.add(try $0.onBatchable()) }
}
for response in responses {
try response.getOrThrow()
}
また、グループ内の一致するすべてのメンバー デバイスで属性を同時に更新し(たとえば、すべての DimmableLightDeviceType メンバーで onTransitionTime を 1 に設定)、期限内に完了するのを待つこともできます。
try await withThrowingTaskGroup(of: Void.self) { taskGroup in
taskGroup.addTask {
try await Task.sleep(for: .seconds(5))
throw CancellationError()
}
for device in members {
taskGroup.addTask {
guard let light = await device.types.get(DimmableLightDeviceType.self),
let levelControl = light.matterTraits.levelControlTrait,
levelControl.attributes.$onTransitionTime.isSupported
else { return }
_ = try await levelControl.update {
$0.setOnTransitionTime(1)
}
}
}
for _ in 0..<members.count {
try await taskGroup.next()
}
taskGroup.cancelAll()
}
グループのメンバー間で属性を集計するには(グループ内のすべての色温度ライトで最小の colorTempPhysicalMaxMireds を見つけるなど):
var maxMiredsValues: [UInt16] = []
for device in members {
if let light = await device.types.get(ColorTemperatureLightDeviceType.self),
let mireds = light.matterTraits.colorControlTrait?.attributes
.colorTempPhysicalMaxMireds {
maxMiredsValues.append(mireds)
}
}
let minMaxMireds = maxMiredsValues.min()
デバイス グループを作成する
構造体で GroupManagementTrait を使用して、デバイス グループを作成します。
let groupsManagementTrait =
try await structure.traits(Google.GroupManagementTrait)
let _ = try await groupsManagementTrait.createUserDefinedGroup(
withName: "Kitchen Lights",
withDevices: [light1, light2]
)
グループ メンバーを管理する
既存のグループにメンバーを追加するには:
let groupManagementTrait =
try await structure.traits(Google.GroupManagementTrait.self)
groupManagementTrait.addGroupMembers(
groupObjectId: group.id,
memberDeviceObjectIds = [light3.id],
)
グループからメンバーを削除するには:
let groupManagementTrait =
try await structure.traits(Google.GroupManagementTrait.self)
_ = try await groupManagementTrait.removeGroupMembers(
groupObjectId: group.id,
memberDeviceObjectIdsArray: Array(removedIDs)
)
デバイス グループの名前を変更する
デバイス グループの名前を変更するには、GroupTrait の name フィールドを更新します。
let groupTrait = group.traits.get(Google.GroupTrait.self)
_ = try await groupTrait.update { $0.name = "Movie Corner" }
デバイス グループを削除する
ストラクチャからデバイス グループを削除するには:
let groupManagementTrait =
try await structure.traits.get(Google.GroupManagementTrait.self)
_ = try await groupManagementTrait.deleteGroup(groupObjectId: group.id)