אפשר לגשת לממשקי API של מכשירים דרך ממשקי ה-API של Home ל-iOS. מייבאים את החבילות הבאות לאפליקציה:
import GoogleHomeSDK
import GoogleHomeTypes
מידע נוסף זמין במאמר בנושא מודל נתונים ב-iOS.
טיפול בשגיאות
חלק מהשיטות ב-Home APIs מחזירות HomeError, ולכן מומלץ להשתמש בבלוק do-catch כדי לזהות HomeError בקריאות האלה.
כשמטפלים בשגיאה HomeError, צריך לבדוק את השדות code ו-message כדי להבין מה השתבש.
שגיאות שלא טופלו יגרמו לקריסת האפליקציה.
מידע נוסף מופיע במאמר בנושא טיפול בשגיאות.
שיחות לדוגמה
הורדה של רשימת מכשירים
כדי לקבל Query של מכשירים נגישים, צריך להפעיל את devices() עם הפניה לאובייקט Home.
קוראים לשיטה Query's
batched()
שפולטת Set שמשקף את המצב הנוכחי של הבית עם כל שינוי במטא-נתונים של המכשיר. אפשר גם להתקשר למספר Query.list() כדי לקבל תמונת מצב של המכשירים הזמינים. זוהי שיטה נוחה להירשמה לסטרים batched() ולהחזרת הערך הראשון שמוחזר.
Query.stream()
יוצר זרם שפולט ערכים חדשים בשינויים במטא-נתונים של המכשיר, כמו
השם, החדר או המבנה שלו. באופן פנימי, נעשה שימוש ב-batched() והמאפיינים שמשתנים מועברים בלבד.
// Get a list of all devices accessible to the user let homeDevices = try await self.home.devices().list()
משם אפשר לגשת למצבים של כל מכשיר ולשלוח פקודות למכשירים.
בגרסה 1.8 של Home APIs, יש לכם אפשרות להגדיר את הפרמטר enableMultipartDevices של השיטה devices() לערך true כדי שממשק ה-API ייצג כל מכשיר מרובה חלקים כמכשיר יחיד. מידע נוסף זמין במאמר בנושא מכשירים מרובי חלקים ב-iOS.
קבלת סוגי המכשירים
כדי לקבל את סוגי המכשירים שמשויכים למכשיר, קוראים את המאפיין types של המכשיר, שמחזיר DeviceTypeController.
מתקשרים אל DeviceTypeController.subscribe(_:) כדי להירשם לעדכונים לגבי סוג מכשיר מסוים:
let devices = try await self.home.devices().list() if let device = devices.first(where: { $0.id == myDeviceId }) { var receivedUpdate1 = false var receivedUpdate2 = false device.types.subscribe(OnOffLightDeviceType.self) .assertNoFailure() .sink { device in if !receivedUpdate1 { receivedUpdate1 = true Task { try await device.matterTraits.onOffTrait?.on() } return } if !receivedUpdate2 { receivedUpdate2 = true return } fatalError("Received unexpected update") } }
אם המכשיר לא תומך בסוג המכשיר שצוין, הוא מחזיר Empty
Publisher שמושלם באופן מיידי.
אם המכשיר תומך בסוג מכשיר ספציפי, אפשר לקבל את הטיפול בסוג הזה על ידי קריאה ל-get():
if let device = devices.first(where: { $0.id == myDeviceId }) { let _ = await device.types.get(OnOffLightDeviceType.self) }
אם המכשיר לא תומך בסוג שצוין, הוא מחזיר nil.
Call
DeviceTypeController.subscribeAll()
to get a Publisher of
DeviceTypeCollection.
בעזרת המחלקה הזו אפשר לבדוק אם למכשיר יש סוג מכשיר מסוים:
if let device = devices.first(where: { $0.id == myDeviceId }) { device.types.subscribeAll() .assertNoFailure() .sink { types in let lightDeviceType = types[OnOffLightDeviceType.self] let fanDeviceType = types[FanDeviceType.self] } }
קבלת מאפיין של סוג המכשיר
סוגי המכשירים הם נקודת הכניסה לקריאת המאפיינים, כי הם מפרקים מכשיר לחלקים הפונקציונליים שלו (כמו נקודות קצה ב-Matter).
הם גם מתייחסים להתנגשויות בין מאפיינים במקרה של מכשיר עם שני סוגי מכשירים, שלשניהם יכול להיות אותו מאפיין. לדוגמה, אם מכשיר הוא גם רמקול וגם מנורה עם אפשרות לעמעום, יהיו לו שני מאפיינים של הפעלה/השבתה ושני מאפיינים של בקרת עוצמה.
סוג נוסף של התנגשות מאפיינים יכול להתרחש כשבמכשיר יש שני מאפיינים עם אותו שם. לדוגמה, onOff יכול להתייחס למופע של מאפיין OnOff רגיל, או למופע של מאפיין OnOff שהוגדר על ידי היצרן. כדי למנוע אי בהירות לגבי המאפיין הרצוי, צריך להפנות למאפיין דרך אחת משתי קולקציות המאפיינים בכל סוג מכשיר.
למאפיינים רגילים, כלומר מאפיינים שדומים לאשכולות רגילים של Matter, משתמשים ב-matterTraits. לדוגמה,
כדי לקבל מאפיין ספציפי לסוג המכשיר Dimmable Light (נורת LED ניתנת לעמעום):
if let dimmableLightDeviceType = await device.types.get(DimmableLightDeviceType.self) { // Accessing standard trait on the type. let levelControlTrait = dimmableLightDeviceType.matterTraits.levelControlTrait.self }
למאפיינים של Google, משתמשים ב-googleTraits:
if let doorbellDeviceType = await device.types.get(GoogleDoorbellDeviceType.self) { // Accessing Google trait on the type. let doorbellPressTrait = doorbellDeviceType.traits[Google.DoorbellPressTrait.self] }
כדי לגשת לתכונה ספציפית של יצרן, צריך להפנות אליה דרך המאפיין traits
property, אבל להוסיף לפניו את שם החבילה של היצרן:
let deviceType = await device1?.types.get(OnOffLightDeviceType.self) // Accessing custom trait on the type. if let spinnerTrait = deviceType?.traits[ExampleOrganization.SpinnerTrait.self] { let rpmVal = spinnerTrait.attributes.rpm }
קריאת סטטוס המכשיר
בדוגמה הזו אפשר לראות איך בודקים את המאפיין OnOff ממאפיין ההפעלה/ההשבתה של המכשיר:
let lightDevices = devices.filter { $0.types.contains(OnOffLightDeviceType.self) } let light1 = lightDevices.first let lightDeviceTypeOptional = await light1?.types.get(OnOffLightDeviceType.self) if let onOffTrait = lightDeviceTypeOptional?.matterTraits.onOffTrait { let onOffVal = onOffTrait.attributes.onOff }
קבלת רשימה של מכשירים עם תכונה ספציפית
כדי לקבל רשימה של מכשירים עם מאפיין ספציפי, צריך לבצע איטרציה על המכשירים, על סוגי המכשירים של כל מכשיר ועל המאפיינים של כל סוג מכשיר. לדוגמה, כדי לקבל רשימה של מכשירים בבית שיש להם את המאפיין On/Off (הפעלה/הפסקה):
// Get all light devices that support levelControl var levelControlDevices: [HomeDevice] = [] let allDevices = try await home.devices().list() for device in allDevices { if let deviceType = await device.types.get(OnOffLightDeviceType.self) { if deviceType.traits.contains(Matter.LevelControlTrait.self) { levelControlDevices.append(device) } } }
רשימה מלאה של המאפיינים שזמינים בממשקי ה-API של Home מופיעה במאמר מדד המאפיינים ב-iOS.
קבלת רשימה של מכשירים עם סוגי מכשירים דומים
כדי לקבל רשימה של מכשירים שמייצגים את כל האורות בבית:
// Get a list of devices with similar device types (lights) let lightDevices = try await self.home.devices().list().compactMap { $0.types.contains(DimmableLightDeviceType.self) || $0.types.contains(OnOffLightDeviceType.self) || $0.types.contains(ColorTemperatureLightDeviceType.self) || $0.types.contains(ExtendedColorLightDeviceType.self) }
בממשקי ה-API של Home יש כמה סוגי מכשירים שיכולים לייצג סוג מכשיר מרכזי. לדוגמה, אין סוג מכשיר 'מנורה'. במקום זאת, יש ארבעה סוגי מכשירים שונים שיכולים לייצג מנורה, כמו שמוצג בדוגמה הקודמת. לכן, כדי לקבל תצוגה מקיפה של סוג מכשיר ברמה גבוהה יותר בבית, צריך לכלול כמה סוגי מכשירים.
כאן מופיעה רשימה מלאה של סוגי המכשירים והתכונות שלהם שזמינים בממשקי ה-API של Home.
איך מקבלים את שם הספק, מזהה הספק או מזהה המוצר של מכשיר
מאפיין BasicInformationTrait
כולל מידע כמו מזהה ספק, מזהה מוצר, שם מוצר והמספר הסידורי של המכשיר:
guard let vendorName = basicInfoTrait.attributes.vendorName else { fatalError("Failed to get vendorName") } guard let vendorID = basicInfoTrait.attributes.vendorID else { fatalError("Failed to get vendorID") } guard let productID = basicInfoTrait.attributes.productID else { fatalError("Failed to get productID") }
זיהוי מכשירים בענן ליצרני מכשירים
אם אתם יצרני מכשירים ומייצרים מכשירי Cloud-to-cloud, כדי לזהות את מכשירי Cloud-to-cloud באמצעות מאפיין BasicInformation, אתם יכולים לכלול את שדות המחרוזת האלה בתגובת SYNC שלהם:
Connectivity Standards Alliance (Alliance) מזהה הספק שהונפק:
"matterOriginalVendorId": "0xfff1",מזהה מוצר שמזהה באופן ייחודי מוצר של ספק:
"matterOriginalProductId": "0x1234",מזהה ייחודי של המכשיר, שנוצר באופן ספציפי ליצרן:
"matterUniqueId": "matter-device-id",
כשמזינים את השדות האלה של מחרוזות, צריך להשתמש במזהי הספק והמוצר שלכם, אם יש לכם כאלה. אם אתם לא חברים ב-MatterAlliance ולא הוקצו לכם המזהים האלה, אתם יכולים להשאיר את השדות matterOriginalVendorId ו-matterOriginalProductId ריקים ולציין את matterUniqueId כמזהה.
בדוגמה לתגובת SYNC מוצג השימוש בשדות האלה:
{
"requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
"payload": {
"agentUserId": "1836.15267389",
"devices": [
{
"id": "456",
"type": "action.devices.types.LIGHT",
"traits": [
"action.devices.traits.OnOff",
"action.devices.traits.Brightness",
"action.devices.traits.ColorSetting",
],
"willReportState": true,
"deviceInfo": { ... },
"matterOriginalVendorId": "0xfff1",
"matterOriginalProductId": "0x1234",
"matterUniqueId": "matter-device-id",
"otherDeviceIds": [
{
"deviceId": "local-device-id",
}
]
}
]
}
}
מידע נוסף זמין במסמכי התיעוד של Cloud-to-cloud SYNC.
מטא-נתונים של מכשירים ומאפיינים
למכשירים ולמאפיינים בממשקי ה-API של Home יש מטא-נתונים שמשויכים אליהם, שיכולים לעזור בניהול חוויית המשתמש באפליקציה.
כל מאפיין בממשקי ה-API של Home מכיל מאפיין sourceConnectivity, שכולל מידע על הסטטוס אונליין של המאפיין ועל המיקום שלו (ניתוב מקומי או מרחוק).
קבלת הסוג הראשי של מכשיר
יכול להיות שבמכשירים מסוימים יוצגו כמה סוגים של מכשירים דרך ממשקי ה-API של Home. כדי לוודא שהמשתמשים יראו באפליקציה את האפשרויות המתאימות למכשירים שלהם (כמו שליטה במכשיר והצעות לאוטומציות), כדאי לבדוק אם סוג המכשיר הוא הסוג העיקרי של המכשיר.
if let deviceType = await device?.types.get(HumiditySensorDeviceType.self) { if deviceType.metadata.isPrimaryType { print("Humidity Sensor is the primary type on this device.") } else { print("Humidity Sensor isn't the primary type on this device.") } }
איך בודקים אם מאפיין מסוים זמין באינטרנט
כדי לבדוק את הקישוריות של מאפיין, קוראים את המאפיין connectivityState:
let levelControlConnectivity = levelControlTrait.metadata.sourceConnectivity .connectivityState
חלק מהתכונות, בדרך כלל תכונות של Google smart home, עשויות להופיע במצב'אופליין' אם למכשיר אין קישוריות לאינטרנט. הסיבה לכך היא שהתכונות האלה מבוססות על ענן ואין להן ניתוב מקומי.
בדיקת החיבור של מכשיר
הקישוריות של מכשיר נבדקת למעשה ברמת סוג המכשיר, כי חלק מהמכשירים תומכים בכמה סוגי מכשירים. המצב שמוחזר הוא שילוב של מצבי הקישוריות של כל התכונות במכשיר.
let lightConnectivity = dimmableLightDeviceType.metadata.sourceConnectivity .connectivityState
יכול להיות שתראו את המצב partiallyOnline במקרה של סוגי מכשירים מעורבים
כשאין קישוריות לאינטרנט. יכול להיות שהמאפיינים הרגילים Matter עדיין יהיו אונליין בגלל ניתוב מקומי, אבל מאפיינים מבוססי-ענן יהיו אופליין.
איך מוצאים את כתובת ה-IP של המכשיר
כדי למצוא את כתובת ה-IP של המכשיר, משתמשים במאפיין networkInterfaces של GeneralDiagnosticsTrait. הכתובות מוחזרות כאובייקטים מסוג Data, שאפשר לשנות את הפורמט שלהם למחרוזות IPv4 או IPv6 רגילות באמצעות מסגרת Network:
func getIpAddresses(trait: Matter.GeneralDiagnosticsTrait) -> [String] {
let interfaces = trait.attributes.networkInterfaces ?? []
var ipAddresses: [String] = []
for interface in interfaces {
for data in interface.iPv4Addresses {
if let ipv4 = IPv4Address(data) {
ipAddresses.append(String(describing: ipv4))
}
}
for data in interface.iPv6Addresses {
if let ipv6 = IPv6Address(data) {
ipAddresses.append(String(describing: ipv6))
}
}
}
return ipAddresses
}
בדיקת ניתוב הרשת של מאפיין
הלוקאליות של מאפיין זמינה גם בממשקי ה-API של Home. הערך dataSourceLocality מציין אם המאפיין מנותב מרחוק (דרך הענן), באופן מקומי (דרך רכזת מקומית) או ישירות (ממכשיר למכשיר, ללא רכזת).
לדוגמה, יכול להיות שהערך של המיקום לא ידוע (unspecified) בזמן שאפליקציה מופעלת ולא הגיעה עדיין לרכזת או לשרת לצורך קישוריות המכשיר. אי אפשר להגיע למכשירים האלה, והם לא יגיבו לבקשות אינטראקציה מפקודות או מאירועים. הלקוח הוא זה שקובע איך לטפל במכשירים כאלה.
let levelControlLocality = levelControlTrait.metadata.sourceConnectivity .dataSourceLocality
בדיקת ניתוב הרשת של מכשיר
בדומה לקישוריות, המקומיות נבדקת ברמת סוג המכשיר. המצב שמוחזר הוא שילוב של המיקום עבור כל התכונות במכשיר הזה.
let lightLocality = dimmableLightDeviceType.metadata.sourceConnectivity.dataSourceLocality
מצב mixed יכול להופיע בתרחיש דומה לתרחיש של קישוריות partiallyOnline: חלק מהמאפיינים מבוססים על ענן וחלקם מקומיים.
שינוי השם של מכשיר
מבצעים קריאה לשיטה
setName(_:)
כדי לשנות את השם של מכשיר:
let updatedDevice = try await theDevice.setName("new device name")
כשמשנים את השם של מכשיר, מבנה הנתונים המקורי HomeDevice נשאר זהה והשינוי משתקף באובייקט HomeDevice המעודכן שמוחזר.
שמות ייחתכו אם הם יחרגו ממגבלת 60 מיקומי התווים (code point) ב-Unicode, ולא יוצגו שגיאות. המפתחים אחראים לטפל בשמות ארוכים. לדוגמה, הם יכולים להחליט אם הם רוצים ליידע את המשתמשים שהשמות יקוצרו.