存取 iOS 裝置和裝置中繼資料

您可以透過 iOS 版 Home API 存取裝置 API。將下列套件匯入應用程式:

import GoogleHomeSDK
import GoogleHomeTypes

詳情請參閱「iOS 上的資料模型」。

處理錯誤

Home API 中的某些方法會擲回 HomeError,因此建議您使用 do-catch 區塊,在這些呼叫中擷取 HomeError

處理 HomeError 時,請檢查其 codemessage 欄位,瞭解錯誤所在。

任何未處理的錯誤都會導致應用程式當機。

詳情請參閱「錯誤處理」一文。

如需範例,請參閱「將指令傳送至裝置」。

呼叫範例

取得裝置清單

使用 Home 物件的參照,請叫用 devices() 取得可存取裝置的 Query。呼叫 Querybatched() 方法,該方法會在每次裝置中繼資料變更時,發出反映住家目前狀態的 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() 取得 DeviceTypeCollectionPublisher。這個類別可讓您檢查裝置是否具有特定裝置類型:

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 中的端點)。

當裝置具有兩種裝置類型,且兩者可能具有相同特徵時,這些特徵也會考量特徵衝突。舉例來說,如果裝置同時是喇叭和可調光燈具,就會有兩個開/關和兩個 Level Control 特徵。

如果裝置有兩個同名的特徵,就可能發生另一種特徵衝突。舉例來說,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 屬性的範例:

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

如需 Home API 中可用的特徵完整清單,請參閱「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)
  }

Home API 中有多種裝置類型可代表核心裝置類型。例如,沒有「燈具」裝置類型。相反地,有四種不同的裝置類型可代表燈具,如上述範例所示。因此,如要全面掌握住家中較高層級的裝置類型,就必須納入多種裝置類型。

如需裝置類型和 Home API 中可用的特徵完整清單,請參閱「iOS 支援的裝置類型」。

取得裝置的供應商名稱、供應商 ID 或產品 ID

BasicInformationTrait 特徵包含供應商 ID、產品 ID、產品名稱和裝置序號等資訊:

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 裝置,為了透過 BasicInformationTrait 識別您的 Cloud-to-cloud 裝置,您可以在 SYNC 回應中加入下列字串欄位:

  • Connectivity Standards Alliance (CSA) 核發的供應商 ID: "matterOriginalVendorId": "0xfff1",

  • 產品 ID:用於唯一識別供應商產品的 ID: "matterOriginalProductId": "0x1234",

  • 裝置的專屬 ID,以製造商專屬方式建構:"matterUniqueId": "matter-device-id",

輸入這些字串欄位時,請使用 Matter 供應商和產品 ID (如有)。如果您不是 CSA 會員,且尚未指派這些 ID,可以將 matterOriginalVendorIdmatterOriginalProductId 欄位留空,並提供 matterUniqueId 做為 ID。

以下 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 說明文件

裝置和特徵中繼資料

Home API 中的裝置和特徵會連結中繼資料,有助於管理應用程式中的使用者體驗。

Home API 中的每個特徵都包含 sourceConnectivity 屬性,其中包含特徵的線上狀態和位置資訊 (本地或遠端路由)。

取得裝置的主要類型

部分裝置可能會透過 Google Home API 呈現多種裝置類型。為確保使用者在應用程式中看到適合其裝置的選項 (例如裝置控制和建議的自動化動作),建議您檢查裝置類型是否為裝置的主要類型。

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 由於本地路由,標準特徵可能仍會上線,但雲端特徵會離線。

檢查特徵的網路路由

特徵的地區資訊也可在 Home API 中使用。dataSourceLocality 會指出特徵是透過雲端遠端路由、透過本機路由 (透過本機中樞) 或點對點 (直接從裝置傳送至裝置,不經過中樞) 傳送。

例如,當應用程式正在啟動,但尚未連上裝置連線的中心或伺服器時,就可能會出現不明的區域值 unspecified。這些裝置無法連線,因此無法回應指令或事件的互動要求。客戶可以自行決定如何處理這類裝置。

let levelControlLocality =
  levelControlTrait.metadata.sourceConnectivity
  .dataSourceLocality

檢查裝置的網路路由

與連線性一樣,系統會在裝置類型層級檢查區域性。系統會傳回該裝置上所有特徵的區域組合。

let lightLocality =
  dimmableLightDeviceType.metadata.sourceConnectivity.dataSourceLocality

partiallyOnline 連線類似的情況下,可能會觀察到 mixed 的狀態:部分特徵是雲端式,其他則是本機式。

API 清單

建立 Home 的例項後,即可透過該例項存取下列 Device API:

API 說明
device(id:) 針對指定裝置傳回 Publisher,該裝置會在裝置狀態發生變更時發出。
devices() 取得 Google 帳戶中所有結構體的所有裝置。傳回 Query<HomeDevice>,提供進一步的擷取和篩選選項。

取得 HomeDevice 後,您可以透過該物件存取下列 API:

API 說明
id 裝置的專屬系統 ID。
name 使用者提供的裝置名稱。
structureID 裝置所屬結構體的 ID。傳回 String?
roomID 裝置指派到的房間 ID。傳回 String?
types 取得裝置上的特定類型或所有可用類型。
isMatterDevice 如果裝置由 Matter 備份。
sourceConnectivity 裝置的來源連線,代表裝置特徵的匯總連線狀態和網路位置。