Dostęp do urządzeń i metadanych urządzeń w iOS

Do interfejsów API urządzeń można uzyskać dostęp za pomocą interfejsów Home API na iOS. Zaimportuj do aplikacji te pakiety:

import GoogleHomeSDK
import GoogleHomeTypes

Więcej informacji znajdziesz w artykule Model danych na iOS.

Obsługa błędów

Niektóre metody w interfejsach Home API zgłaszają błąd HomeError, dlatego zalecamy używanie bloku do-catch do przechwytywania błędów HomeError w tych wywołaniach.

Podczas obsługi błędu HomeError sprawdź pola code i message , aby dowiedzieć się, co poszło nie tak.

Wszelkie nieobsłużone błędy spowodują awarię aplikacji.

Więcej informacji znajdziesz w sekcji Obsługa błędów.

Przykład znajdziesz w artykule Wysyłanie polecenia do urządzenia.

Przykładowe wywołania

Pobieranie listy urządzeń

Używając odwołania do obiektu Home , wywołaj devices(), aby uzyskać Query dostępnych urządzeń. Wywołaj metodę Query's batched(), która emituje zbiór odzwierciedlający bieżący stan domu przy każdej zmianie metadanych urządzenia. Możesz też wywołać metodę Query.list(), aby uzyskać migawkę dostępnych urządzeń. Jest to wygodna metoda, która subskrybuje strumień batched() i zwraca pierwszą wyemitowaną wartość. Query.stream() tworzy strumień, który emituje nowe wartości przy zmianach metadanych urządzenia, takich jak jego nazwa, pomieszczenie lub struktura. Wewnętrznie używa ona metody batched() i emituje tylko zmienione właściwości.

// Get a list of all devices accessible to the user
let homeDevices = try await self.home.devices().list()

Dzięki temu możesz uzyskiwać dostęp do stanów każdego urządzenia i wysyłać do nich polecenia.

W wersji 1.8 interfejsów Home API możesz ustawić parametr enableMultipartDevices metody devices() na true, aby interfejs API reprezentował każde urządzenie wieloczęściowe jako jedno urządzenie. Więcej informacji znajdziesz w artykule Urządzenia wieloczęściowe na iOS.

Pobieranie typów urządzenia

Aby uzyskać typy urządzeń powiązane z urządzeniem, odczytaj właściwość types urządzenia, która zwraca DeviceTypeController.

Wywołaj metodę DeviceTypeController.subscribe(_:), aby subskrybować aktualizacje określonego typu urządzenia:

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

Jeśli urządzenie nie obsługuje określonego typu urządzenia, zwraca Empty Publisher, który natychmiast się kończy.

Jeśli urządzenie obsługuje określony typ urządzenia, możesz uzyskać do niego dostęp, wywołując metodę get():

if let device = devices.first(where: { $0.id == myDeviceId }) {
  let _ = await device.types.get(OnOffLightDeviceType.self)
}

Jeśli urządzenie nie obsługuje określonego typu, zwraca wartość nil.

Wywołaj metodę DeviceTypeController.subscribeAll() , aby uzyskać Publisher obiektu DeviceTypeCollection. Ta klasa umożliwia sprawdzenie, czy urządzenie ma określony typ urządzenia:

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

Pobieranie cechy typu urządzenia

Typy urządzeń są punktem wejścia do odczytywania cech, ponieważ rozkładają urządzenie na jego funkcjonalne części (takie jak punkty końcowe w Matter).

Uwzględniają też kolizje cech w przypadku, gdy urządzenie ma 2 typy urządzeń, z których każdy może mieć tę samą cechę. Jeśli na przykład urządzenie jest zarówno głośnikiem, jak i ściemnianym światłem, będzie miało 2 cechy włączania/wyłączania i 2 cechy sterowania poziomem.

Inny rodzaj kolizji cech może wystąpić, gdy urządzenie ma 2 cechy o tej samej nazwie. Na przykład onOff może odnosić się do instancji standardowej cechy OnOff lub do instancji cechy OnOff zdefiniowanej przez producenta. Aby wyeliminować wszelkie potencjalne niejasności co do tego, która cecha jest zamierzona, odwołaj się do cechy za pomocą jednej z 2 kolekcji cech w każdym typie urządzenia.

W przypadku cech standardowych, czyli analogicznych do Matter standardowych klastrów, użyj matterTraits. Aby na przykład uzyskać konkretną cechę typu urządzenia ściemnianego światła:

if let dimmableLightDeviceType =
  await device.types.get(DimmableLightDeviceType.self)
{
  // Accessing standard trait on the type.
  let levelControlTrait =
    dimmableLightDeviceType.matterTraits.levelControlTrait.self
}

W przypadku cech Google użyj googleTraits:

if let doorbellDeviceType = await device.types.get(GoogleDoorbellDeviceType.self) {
  // Accessing Google trait on the type.
  let doorbellPressTrait =
    doorbellDeviceType.traits[Google.DoorbellPressTrait.self]
}

Aby uzyskać dostęp do cechy specyficznej dla producenta, odwołaj się do niej za pomocą właściwości traits, ale poprzedź ją nazwą pakietu producenta:

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
}

Odczytywanie stanu urządzenia

Oto przykład sprawdzania atrybutu OnOff z cechy włączania/wyłączania urządzenia:

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
}

Pobieranie listy urządzeń z określoną cechą

Aby uzyskać listę urządzeń, które mają określoną cechę, musisz przejść przez urządzenia, typy urządzeń każdego urządzenia i cechy każdego typu urządzenia. Aby na przykład uzyskać listę urządzeń w domu, które mają cechę włączania/wyłączania:

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

Pełną listę cech dostępnych w interfejsach Home API znajdziesz w artykule Indeks cech na iOS.

Pobieranie listy urządzeń o podobnych typach

Aby uzyskać listę urządzeń, które reprezentują wszystkie światła w domu:

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

W interfejsach Home API jest wiele typów urządzeń, które mogą reprezentować podstawowy typ urządzenia. Na przykład nie ma typu urządzenia „Światło”. Zamiast tego są 4 różne typy urządzeń, które mogą reprezentować światło, jak pokazano w poprzednim przykładzie. Aby uzyskać pełny obraz urządzenia wyższego poziomu w domu, należy uwzględnić wiele typów urządzeń.

Pełną listę typów urządzeń i ich cech dostępnych w interfejsach Home API znajdziesz w artykule Obsługiwane typy urządzeń na iOS.

Pobieranie nazwy dostawcy, identyfikatora dostawcy lub identyfikatora produktu urządzenia

Cecha BasicInformationTrait zawiera informacje takie jak identyfikator dostawcy, identyfikator produktu, nazwa produktu i numer seryjny urządzenia:

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

Identyfikacja urządzeń w chmurze dla producentów urządzeń

Jeśli jesteś producentem urządzeń i tworzysz Cloud-to-cloud urządzenia, aby zidentyfikować swoje Cloud-to-cloud urządzenia za pomocą BasicInformation cechy, możesz uwzględnić te pola tekstowe w ich SYNC odpowiedzi:

  • Identyfikator dostawcy wydany przez Connectivity Standards Alliance (Alliance) issued vendor ID: "matterOriginalVendorId": "0xfff1",

  • Identyfikator produktu, który jednoznacznie identyfikuje produkt dostawcy: "matterOriginalProductId": "0x1234",

  • Unikalny identyfikator urządzenia, który jest tworzony w sposób specyficzny dla producenta: "matterUniqueId": "matter-device-id",

Wpisując te pola tekstowe, użyj identyfikatorów Matter dostawcy i produktu, jeśli je masz. Jeśli nie jesteś członkiem Alliance i nie masz przypisanych tych identyfikatorów, możesz pozostawić pola matterOriginalVendorId i matterOriginalProductId puste i podać matterUniqueId jako identyfikator.

W przykładzie odpowiedzi SYNC pokazano użycie tych pól:

{
  "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",
          }
        ]
      }
    ]
  }
}

Więcej informacji znajdziesz w dokumentacji Cloud-to-cloud SYNC.

Metadane urządzenia i cechy

Urządzenia i cechy w interfejsach Home API mają powiązane z nimi metadane, które mogą pomóc w zarządzaniu wrażeniami użytkowników w aplikacji.

Każda cecha w interfejsach Home API zawiera a sourceConnectivity właściwość, która zawiera informacje o stanie online i lokalizacji cechy (routing lokalny lub zdalny).

Pobieranie podstawowego typu urządzenia

Niektóre urządzenia mogą prezentować wiele typów urządzeń za pomocą interfejsów Home API. Aby mieć pewność, że użytkownicy widzą w aplikacji odpowiednie opcje (np. sterowanie urządzeniem i sugerowane automatyzacje) dla swoich urządzeń, warto sprawdzić, czy typ urządzenia jest jego typem podstawowym.

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

Sprawdzanie, czy cecha jest online

Aby sprawdzić łączność cechy, odczytaj właściwość connectivityState:

let levelControlConnectivity =
  levelControlTrait.metadata.sourceConnectivity
  .connectivityState

Niektóre cechy, zwykle cechy Google smart home, mogą być wyświetlane jako offline, jeśli urządzenie nie ma połączenia z internetem. Dzieje się tak, ponieważ te cechy są oparte na chmurze i nie mają routingu lokalnego.

Sprawdzanie łączności urządzenia

Łączność urządzenia jest sprawdzana na poziomie typu urządzenia, ponieważ niektóre urządzenia obsługują wiele typów urządzeń. Zwracany stan jest połączeniem stanów łączności wszystkich cech na tym urządzeniu.

let lightConnectivity =
  dimmableLightDeviceType.metadata.sourceConnectivity
  .connectivityState

W przypadku mieszanych typów urządzeń, gdy nie ma połączenia z internetem, może wystąpić stan partiallyOnline. Matter standard cechy mogą nadal być online ze względu na routing lokalny, ale cechy oparte na chmurze będą offline.

Pobieranie adresu IP urządzenia

Aby znaleźć adres IP urządzenia, użyj atrybutu networkInterfaces cechy the GeneralDiagnosticsTrait. Adresy są zwracane jako obiekty Data, które można sformatować do standardowych ciągów IPv4 lub IPv6 za pomocą frameworka 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
}

Sprawdzanie routingu sieciowego cechy

Lokalizacja cechy jest też dostępna w interfejsach Home API. dataSourceLocality wskazuje, czy cecha jest kierowana zdalnie (przez chmurę), lokalnie (przez lokalny hub) czy peer-to-peer (bezpośrednio z urządzenia na urządzenie, bez huba).

Możliwa jest nieznana wartość lokalizacji unspecified, na przykład podczas uruchamiania aplikacji, która nie ma jeszcze połączenia z hubem ani serwerem. Te urządzenia są niedostępne i nie będą odpowiadać na żądania interakcji z poleceń ani zdarzeń. To klient decyduje, jak obsługiwać takie urządzenia.

let levelControlLocality =
  levelControlTrait.metadata.sourceConnectivity
  .dataSourceLocality

Sprawdzanie routingu sieciowego urządzenia

Podobnie jak łączność, lokalizacja jest sprawdzana na poziomie typu urządzenia. Zwracany stan jest połączeniem lokalizacji wszystkich cech na tym urządzeniu.

let lightLocality =
  dimmableLightDeviceType.metadata.sourceConnectivity.dataSourceLocality

W podobnym scenariuszu jak w przypadku łączności partiallyOnline może wystąpić stan mixed: niektóre cechy są oparte na chmurze, a inne są lokalne.

Zmienianie nazwy urządzenia

Aby zmienić nazwę urządzenia, wywołaj metodę setName(_:):

let updatedDevice = try await theDevice.setName("new device name")

Podczas zmiany nazwy urządzenia oryginalna struktura HomeDevice pozostaje taka sama, a zmiana jest odzwierciedlana w zwróconym zaktualizowanym obiekcie HomeDevice.

Nazwy dłuższe niż 60 znaków Unicode (znaków) zostaną obcięte i nie zostaną zgłoszone żadne błędy. Deweloperzy są odpowiedzialni za obsługę długich nazw i mogą na przykład zdecydować, czy chcą informować użytkowników o tym, że nazwy zostaną obcięte.