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

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

import GoogleHomeSDK
import GoogleHomeTypes

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

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

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

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

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

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

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

שיחות לדוגמה

הצגת רשימת מכשירים

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

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

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
}

הצגת רשימה של מכשירים עם מאפיין ספציפי

כדי לקבל רשימה של מכשירים שיש להם מאפיין ספציפי, צריך לעבור על המכשירים, על סוגי המכשירים של כל מכשיר ועל המאפיינים של כל סוג מכשיר. לדוגמה, כדי לקבל רשימה של כל המכשירים בבית שיש להם את המאפיין 'הפעלה/כיבוי':

// 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)
    }
  }
}

באינדקס המאפיינים ב-iOS מופיעה רשימה מלאה של המאפיינים שזמינים בממשקי ה-API של Home.

הצגת רשימה של מכשירים עם סוגי מכשירים דומים

כדי לקבל רשימה של המכשירים שמייצגים את כל האורות בבית:

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

כל מאפיין ב-Home APIs מכיל את המאפיין 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

יכול להיות שחלק מהמאפיינים, בדרך כלל מאפייני smart home של Google, יוצגו במצב אופליין אם אין למכשיר חיבור לאינטרנט. הסיבה לכך היא שהמאפיינים האלה מבוססי-ענן ואין להם ניתוב מקומי.

בדיקת הקישוריות של מכשיר

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

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 המעודכן שמוחזר.

רשימת ממשקי ה-API

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

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

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

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