Accedere ai dispositivi e ai metadati dei dispositivi per iOS

È possibile accedere alle API dei dispositivi tramite le API Home per iOS. Importa i seguenti pacchetti nella tua app:

import GoogleHomeSDK
import GoogleHomeTypes

Per saperne di più, consulta Modello di dati su iOS.

Gestione degli errori

Alcuni metodi nelle API Home generano un'eccezione HomeError, pertanto ti consigliamo di utilizzare un blocco do-catch per rilevare l'eccezione HomeError su queste chiamate.

Quando gestisci HomeError, controlla i campi code e message per scoprire cosa non ha funzionato.

Eventuali errori non gestiti causeranno l'arresto anomalo dell'app.

Per saperne di più, consulta Gestione degli errori.

Per un esempio, consulta Invia un comando a un dispositivo.

Chiamate di esempio

Visualizza un elenco di dispositivi

Con un riferimento all'oggetto Home, richiama devices() per ottenere un Query di dispositivi accessibili. Chiama il metodo batched() di Query, che genera un Set che riflette lo stato attuale della casa a ogni modifica dei metadati del dispositivo. In alternativa, chiama Query.list() per ottenere un'istantanea dei dispositivi disponibili. Questo è un metodo pratico che si iscrive allo stream batched() e restituisce il primo valore emesso. Query.stream() produce uno stream che emette nuovi valori in caso di modifiche ai metadati del dispositivo, ad esempio nome, stanza o struttura. Internamente, utilizza batched() ed emette solo le proprietà modificate.

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

Da qui, puoi accedere agli stati di ogni dispositivo e inviare comandi ai dispositivi.

Con la release 1.8 delle API Home, puoi fare in modo che l'API rappresenti ogni dispositivo multiparte come un singolo dispositivo impostando il parametro enableMultipartDevices del metodo devices() su true. Per saperne di più, consulta Dispositivi multiparte su iOS.

Recupera i tipi di dispositivi

Per ottenere i tipi di dispositivi associati a un dispositivo, leggi la proprietà types del dispositivo, che restituisce un DeviceTypeController.

Chiama DeviceTypeController.subscribe(_:) per iscriverti agli aggiornamenti per un tipo di dispositivo specifico:

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

Se il dispositivo non supporta il tipo di dispositivo specificato, restituisce un Empty Publisher che viene completato immediatamente.

Se il dispositivo supporta un tipo di dispositivo specifico, puoi ottenere un handle per quel tipo chiamando get():

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

Se il dispositivo non supporta il tipo specificato, restituisce nil.

Chiama il numero DeviceTypeController.subscribeAll() per ricevere un Publisher di DeviceTypeCollection. Questa classe ti consente di verificare se il dispositivo ha un tipo di dispositivo specifico:

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

Recupera un tratto del tipo di dispositivo

I tipi di dispositivo sono il punto di ingresso per la lettura dei tratti, in quanto scompongono un dispositivo nelle sue parti funzionali (come gli endpoint in Matter).

Inoltre, tengono conto delle collisioni dei tratti nel caso in cui un dispositivo presenti due tipi di dispositivi, entrambi con lo stesso tratto. Ad esempio, se un dispositivo è sia uno speaker che una luce dimmerabile, avrà due tratti On/Off e due tratti Level Control.

Un altro tipo di collisione dei tratti può verificarsi quando un dispositivo ha due tratti con lo stesso nome. Ad esempio, onOff potrebbe fare riferimento a un'istanza del tratto OnOff standard o a un'istanza di un tratto OnOff definito dal produttore. Per eliminare qualsiasi potenziale ambiguità su quale tratto è previsto, fai riferimento a un tratto tramite una delle due raccolte di tratti su ciascun tipo di dispositivo.

Per i trait standard, ovvero quelli analoghi ai cluster standard Matter, utilizza matterTraits. Ad esempio, per ottenere un tratto specifico per il tipo di dispositivo Luce dimmerabile:

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

Per i tratti Google, utilizza googleTraits:

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

Per accedere a un tratto specifico del produttore, fai riferimento alla proprietà traits, ma anteponi il nome del pacchetto del produttore:

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
}

Leggi lo stato di un dispositivo

Guarda questo esempio di controllo dell'attributo OnOff dal tratto On/Off del dispositivo:

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
}

Recupera un elenco di dispositivi con un tratto specifico

Per ottenere un elenco di dispositivi con un tratto specifico, devi eseguire l'iterazione sui dispositivi, sui tipi di dispositivo di ciascun dispositivo e sui tratti di ciascun tipo di dispositivo. Ad esempio, per ottenere un elenco di dispositivi della casa che hanno tutti il tratto 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)
    }
  }
}

Consulta l'indice dei tratti su iOS per un elenco completo dei tratti disponibili nelle API Home.

Ottieni un elenco di dispositivi con tipi di dispositivi simili

Per ottenere un elenco di dispositivi che rappresentano tutte le luci di una casa:

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

Nelle API Home esistono diversi tipi di dispositivi che potrebbero rappresentare un tipo di dispositivo principale. Ad esempio, non esiste un tipo di dispositivo "Luce". Esistono invece quattro diversi tipi di dispositivi che potrebbero rappresentare una luce, come mostrato nell'esempio precedente. Pertanto, per avere una visione completa del tipo di dispositivo di livello superiore in una casa, è necessario includere più tipi di dispositivi.

Consulta i tipi di dispositivi supportati su iOS per un elenco completo dei tipi di dispositivi e dei relativi trait disponibili nelle API Home.

Recupera il nome del fornitore, l'ID fornitore o l'ID prodotto di un dispositivo

Il tratto BasicInformationTrait include informazioni come l'ID fornitore, l'ID prodotto, il nome del prodotto e il numero di serie di un dispositivo:

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

Identificazione dei dispositivi cloud-to-cloud per i produttori di dispositivi

Se sei un produttore di dispositivi e crei dispositivi Cloud-to-cloud, per identificare i tuoi dispositivi Cloud-to-cloud tramite il tratto BasicInformation, puoi includere questi campi stringa nella loro risposta SYNC:

  • L'ID fornitore emesso da Connectivity Standards Alliance (Alliance): "matterOriginalVendorId": "0xfff1",

  • Un ID prodotto che identifica in modo univoco un prodotto di un fornitore: "matterOriginalProductId": "0x1234",

  • Un identificatore univoco del dispositivo, costruito in modo specifico per il produttore: "matterUniqueId": "matter-device-id",

Quando inserisci questi campi di stringa, utilizza gli ID fornitore e prodotto Matter, se li hai. Se non sei un membro di Alliance e non ti sono stati assegnati questi ID, puoi lasciare vuoti i campi matterOriginalVendorId e matterOriginalProductId e fornire matterUniqueId come identificatore.

La risposta SYNC di esempio mostra l'utilizzo di questi campi:

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

Per saperne di più, consulta la documentazione di SYNC Cloud-to-cloud.

Metadati di dispositivi e caratteristiche

Ai dispositivi e ai tratti nelle API Home sono associati metadati che possono aiutare a gestire l'esperienza utente in un'app.

Ogni tratto nelle API Home contiene una proprietà sourceConnectivity, che contiene informazioni sullo stato online e sulla località di un tratto (routing locale o remoto).

Recupera il tipo principale di un dispositivo

Alcuni dispositivi potrebbero presentare più tipi di dispositivi tramite le API Home. Per garantire che agli utenti vengano presentate le opzioni appropriate in un'app (ad esempio il controllo dei dispositivi e le automazioni suggerite) per i loro dispositivi, è utile verificare se un tipo di dispositivo è il tipo principale del dispositivo.

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

Controlla se un tratto è online

Leggi la proprietà connectivityState per verificare la connettività di un tratto:

let levelControlConnectivity =
  levelControlTrait.metadata.sourceConnectivity
  .connectivityState

Alcuni tratti, in genere quelli di Google smart home, potrebbero essere visualizzati offline se il dispositivo non dispone di connessione a internet. Questo perché questi tratti sono basati su cloud e non hanno il routing locale.

Controlla la connettività di un dispositivo

La connettività di un dispositivo viene effettivamente controllata a livello di tipo di dispositivo, perché alcuni dispositivi supportano più tipi di dispositivi. Lo stato restituito è una combinazione degli stati di connettività per tutti i trait di quel dispositivo.

let lightConnectivity =
  dimmableLightDeviceType.metadata.sourceConnectivity
  .connectivityState

In caso di tipi di dispositivi misti, potrebbe essere osservato uno stato di partiallyOnline quando non è presente una connessione a internet. I tratti standard Matter potrebbero essere ancora online grazie al routing locale, ma i tratti basati su cloud saranno offline.

Recupera l'indirizzo IP del dispositivo

Per trovare l'indirizzo IP del dispositivo, utilizza l'attributo networkInterfaces di GeneralDiagnosticsTrait. Gli indirizzi vengono restituiti come oggetti Data, che puoi formattare in stringhe IPv4 o IPv6 standard utilizzando il framework 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
}

Controlla il routing di rete di un tratto

La località di un tratto è disponibile anche nelle API Home. dataSourceLocality indica se il tratto viene instradato da remoto (tramite il cloud), localmente (tramite un hub locale) o peer-to-peer (direttamente da dispositivo a dispositivo, senza hub).

Il valore di località sconosciuta unspecified è possibile, ad esempio, durante l'avvio di un'app che non ha ancora raggiunto un hub o un server per la connettività del dispositivo. Questi dispositivi non sono raggiungibili e non riusciranno a interagire con le richieste di comandi o eventi. Spetta al client determinare come gestire questi dispositivi.

let levelControlLocality =
  levelControlTrait.metadata.sourceConnectivity
  .dataSourceLocality

Controlla il routing di rete per un dispositivo

Come la connettività, la località viene controllata a livello di tipo di dispositivo. Lo stato restituito è una combinazione della località per tutti i tratti di quel dispositivo.

let lightLocality =
  dimmableLightDeviceType.metadata.sourceConnectivity.dataSourceLocality

Uno stato di mixed può essere osservato in uno scenario simile a quello della connettività partiallyOnline: alcuni tratti sono basati su cloud, mentre altri sono locali.

Modifica il nome di un dispositivo

Chiama il metodo setName(_:) per modificare il nome di un dispositivo:

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

Quando cambi il nome di un dispositivo, la struttura HomeDevice originale rimane invariata e la modifica viene applicata all'oggetto HomeDevice aggiornato restituito.

I nomi verranno troncati se superano il limite di 60 punti di codice Unicode (caratteri) e non verranno generati errori. Gli sviluppatori sono responsabili della gestione dei nomi lunghi e, ad esempio, possono decidere se informare gli utenti che i nomi verranno troncati.

Controlla l'appartenenza a un gruppo per un dispositivo

Un dispositivo fisico può appartenere a uno o più gruppi di dispositivi. Per controllare a quali gruppi appartiene un dispositivo, controlla il campo GroupMembershipTrait sul dispositivo:

if let trait = await device.traits.get(Google.GroupMembershipTrait.self) {
  for groupId in trait.attributes.groupObjectIdsArray {
    print("Device belongs to group: \(groupId)")
  }
}

Per esaminare i tipi di dispositivi supportati da un DeviceGroup e inviare comandi al gruppo come singola entità, consulta Controlla un gruppo di dispositivi.