גישה למכשירים ולמטא-נתונים של המכשיר ב-iOS

אפשר לגשת לממשקי API של מכשירים דרך ממשקי ה-API של Home ל-iOS. מייבאים את החבילות הבאות לאפליקציה:

import GoogleHomeSDK
import GoogleHomeTypes

מידע נוסף זמין במאמר בנושא מודל הנתונים ב-iOS.

טיפול בשגיאות

חלק מהשיטות ב-Home APIs מחזירות HomeError, ולכן מומלץ להשתמש בבלוק do-catch כדי לזהות HomeError בקריאות האלה.

כשמטפלים ב-HomeError, בודקים את השדות code ו-message כדי להבין מה השתבש.

שגיאות שלא טופלו יגרמו לקריסת האפליקציה.

מידע נוסף זמין במאמר בנושא טיפול בשגיאות.

דוגמה לשליחת פקודה למכשיר

דוגמאות לשיחות

קבלת רשימת מכשירים

בעזרת הפניה לאובייקט Home, מפעילים את devices() כדי לקבל Query של מכשירים נגישים. קוראים לשיטה 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()

משם אפשר לגשת למצבים של כל מכשיר, ולשלוח למכשיר פקודות נתמכות.

קבלת סוגי המכשירים

כדי לקבל את סוגי המכשירים שמשויכים למכשיר, קוראים את המאפיין 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 deviceType = await device.types.get(OnOffLightDeviceType.self)
}

אם המכשיר לא תומך בסוג שצוין, הפונקציה מחזירה nil.

מתקשרים למספר DeviceTypeController.subscribeAll() כדי לקבל Publisher של 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 (אור ניתן לעמעום):

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.googleTraits.doorbellPressTrait.self
}

כדי לגשת לתכונה ספציפית של היצרן, צריך להפנות אליה דרך המאפיין traits ולציין לפניו את שם החבילה של היצרן:

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 מהתכונה On/Off (הפעלה/השבתה) של המכשיר:

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] = []
var 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 ‏ (CSA): "matterOriginalVendorId": "0xfff1",

  • מזהה מוצר שמזהה באופן ייחודי מוצר של ספק: "matterOriginalProductId": "0x1234",

  • מזהה ייחודי של המכשיר, שנוצר באופן ספציפי ליצרן: "matterUniqueId": "matter-device-id",

כשמזינים את שדות המחרוזת האלה, משתמשים במזהי הספק והמוצר אם יש לכם אותם.Matter אם אתם לא חברים ב-CSA ולא הוקצו לכם המזהים האלה, אתם יכולים להשאיר את השדות 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 יכול להיות שמאפיינים רגילים עדיין יהיו אונליין בגלל ניתוב מקומי, אבל מאפיינים מבוססי-ענן יהיו אופליין.

בדיקת ניתוב הרשת של מאפיין

הלוקאל של מאפיין זמין גם בממשקי ה-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 תווים יחתכו, ולא יוצגו שגיאות. המפתחים אחראים לטיפול בשמות ארוכים. לדוגמה, הם יכולים להחליט אם הם רוצים להודיע למשתמשים שהשמות יקוצרו.

רשימת ממשקי API

אחרי שיוצרים מופע של Home, אפשר לגשת דרכו לממשקי ה-API הבאים של המכשיר:

API תיאור
device(id:) מחזירה Publisher למכשיר שצוין, שפולט את מצב המכשיר בכל פעם שהוא משתנה.
devices() לקבל את כל המכשירים בכל המבנים בחשבון Google. הפונקציה מחזירה Query<HomeDevice> שמספק אפשרויות נוספות לאחזור ולסינון.

אחרי שיש לכם HomeDevice, תוכלו לגשת לממשקי ה-API הבאים דרכו:

API תיאור
id המזהה הייחודי של המכשיר במערכת.
name השם שהמשתמש נתן למכשיר.
structureID המזהה של המבנה שהמכשיר משויך אליו. הפונקציה מחזירה String?.
roomID המזהה של החדר שהמכשיר מוקצה לו. הפונקציה מחזירה String?.
types קבלת סוג ספציפי או כל הסוגים שזמינים במכשיר.
isMatterDevice אם המכשיר מגובה על ידי Matter.
sourceConnectivity קישוריות המקור של המכשיר, שמייצגת מצבי קישוריות מצטברים ומיקום ברשת של מאפייני המכשיר.