אפשר לגשת לממשקי ה-API של המבנה דרך Home APIs ל-iOS.
כדי לעבוד עם ממשקי ה-API של Structure, קודם צריך לייבא את החבילה GoogleHomeSDK לאפליקציה:
import GoogleHomeSDK
טיפול בשגיאות
שיטות מסוימות ב-Home APIs זורקות HomeError, לכן מומלץ להשתמש בבלוק do-catch כדי לתפוס HomeError בקריאות האלה.
כשמטפלים ב-HomeError, צריך לבדוק את השדות code ו-message כדי להבין מה השתבש.
כל שגיאה שלא תטופל תגרום לקריסת האפליקציה.
מידע נוסף מופיע במאמר טיפול בשגיאות.
Structure API
Home מייצג את Home Graph, והוא נקודת הכניסה ל-Structure API.
הוא מספק הפניות למבנים, לחדרים ולמכשירים.
Structure מייצג מבנה ב-Home Graph. הוא מספק גישה למטא-נתונים של המבנה, כמו id ו-name.
משתמשים ב-structures() כדי לקבל את כל המבנים בחשבון. המבנים מוחזרים בצורה של Query, שמציע כמה דרכים לצרוך את הנתונים שלו:
| API | תיאור |
|---|---|
stream() |
הפונקציה מחזירה Publisher שפולט כל אובייקט בנפרד כשהשינויים מתרחשים. |
batched() |
הפונקציה מחזירה Publisher שפולט את התוצאה הנוכחית בתור Set של אובייקטים. כל Set שנפלט מייצג את המצב הנוכחי של תרשים האובייקטים. |
list() |
הפונקציה מחזירה את התוצאה הנוכחית בתור Set של אובייקטים. |
יכול להיות שהקריאה ל-structures().list() לא תחזיר מיד קבוצה תקינה של מבנים. אם האפליקציה שלכם מגיבה וקוראת ל-stream() כדי להירשם לכל השינויים במבנה כדי להפעיל את ממשק המשתמש, בסופו של דבר אמורה לחזור רשימה תקינה של מבנים. יש עוד מצבים שבהם יכולה להתקבל רשימת מבנים ריקה, למשל אם הטלפון של המשתמש מאבד את הקישוריות או אם המשתמש ביטל את ההרשאות לאפליקציה. חשוב לוודא שהאפליקציה מטפלת במקרים האלה.
@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)
שיחות לדוגמה בנושא מבנה השיחה
קבלת קבוצה של מבנים
קריאה ל-list() ב-Query<Structure> מחזירה את קבוצת הרכיבים העדכנית ביותר:
// Get a stream of all structures accessible to the user let allStructuresChanges = self.home.structures() let allStructures = try? await allStructuresChanges.list()
כשמעצבים אפליקציה ריאקטיבית, כדאי להשתמש בקריאות batched() ו-stream() במקום list(), כי הן מייצרות נתונים באופן אוטומטי כשגרף הבית משתנה.
קבלת מאפייני מבנה
אחרי שקיבלתם את רשימת המבנים, אתם יכולים לגשת למאפיינים שלהם:
// 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 }
חדרים
חדר מכיל קבוצה של מכשירים. חדר הוא תמיד חלק ממבנה, ובמבנה יכולים להיות כמה חדרים. אם מסירים חדר ממבנה, המכשירים שבחדר לא יוסרו מהמבנה. אבל אם החדר נמחק, המכשירים שבו לא יהיו מוקצים יותר.
משתמשים ב-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)
שיחות ב-Sample Room
קבלת רשימה של חדרים
באמצעות המחלקה 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!!)
אפשר גם למחוק חדר באמצעות המזהה שלו:
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")
כשמשנים את השם של חדר, ה-struct המקורי של Room לא משתנה, והשינוי משתקף באובייקט המעודכן של Room שמוחזר.
שמות ארוכים יותר ממגבלת 60 מיקומי התווים (code point) של Unicode ייחתכו, ולא יוצגו שגיאות. המפתחים אחראים לטיפול בשמות ארוכים, ויכולים, למשל, להחליט אם הם רוצים להודיע למשתמשים שהשמות ייחתכו.
קבוצות מכשירים
קבוצת מכשירים מייצגת אוסף של מכשירים שהמשתמש הגדיר במבנה אחד. החברות בקבוצה נשמרת בשירותי Google וגלויה לחברים אחרים במבנה ולאפליקציות אחרות שיש להן גישה למבנה.
כמו חדרים פיזיים, קבוצות מכשירים שייכות למבנה אחד. אבל בניגוד לחדרים, קבוצות של מכשירים הן קבוצות לוגיות. מכשיר יכול להשתייך לכמה קבוצות מכשירים בו-זמנית. קבוצות מכשירים יכולות להכיל עד 100 מכשירים.
קבוצות מכשירים מיוצגות על ידי הישותDeviceGroup, שהיא שונה מ-HomeDevice. כדי לבצע אינטראקציה עם קבוצות מכשירים, צריך לכלול את סוגי המכשירים והתכונות הרלוונטיים כשמגדירים את ReferencedAutomationTypes ב-Home.configure, יחד עם התכונות 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)")
}
אינטראקציה עם מכשירים של חברי הקבוצה
חזרה על מכשירים שחברים בקבוצה נותנת לכם שליטה מפורטת על חברים ספציפיים, מאפשרת לכם לשלוח פקודות באצווה בבקשה אחת, ומאפשרת לכם לצבור או לכתוב מאפיינים של חברים.
קודם כל, מסננים את המכשירים בבית על ידי בדיקת GroupMembershipTrait של כל מכשיר כדי למצוא את מזהה קבוצת היעד:
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)
}
}
כדי לשלוח בקשת Batch אחת עם פקודות למאפיין בכל הגורמים התואמים בקבוצה:
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()
}
אפשר גם לעדכן מאפיין בכל המכשירים החברים בקבוצה שתואמים (לדוגמה, להגדיר את onTransitionTime ל-1 בכל החברים ב-DimmableLightDeviceType) בו-זמנית, ולהמתין להשלמת הפעולה עד למועד האחרון:
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)
)
שינוי השם של קבוצת מכשירים
כדי לשנות את השם של קבוצת מכשירים, מעדכנים את השדה name של GroupTrait:
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)