Accéder aux appareils et aux métadonnées des appareils pour iOS

Les API d'appareil sont accessibles via les API Home pour iOS. Importez les packages suivants dans votre application :

import GoogleHomeSDK
import GoogleHomeTypes

Pour en savoir plus, consultez Modèle de données sur iOS.

Gestion des exceptions

Certaines méthodes des API Home génèrent une HomeError. Nous vous recommandons donc d'utiliser un bloc do-catch pour intercepter HomeError lors de ces appels.

Lorsque vous gérez HomeError, vérifiez ses champs code et message pour savoir ce qui s'est mal passé.

Toute erreur non gérée entraînera le plantage de votre application.

Pour en savoir plus, consultez Gestion des erreurs.

Pour obtenir un exemple, consultez Envoyer une commande à un appareil.

Exemples d'appels

Obtenir une liste d'appareils

Avec une référence à l'objet Home, appelez devices() pour obtenir un Query d'appareils accessibles. Appelez la méthode batched() de Query, qui émet un ensemble reflétant l'état actuel de la maison à chaque modification des métadonnées de l'appareil. Vous pouvez également appeler Query.list() pour obtenir un instantané des appareils disponibles. Il s'agit d'une méthode pratique qui s'abonne au flux batched() et renvoie la première valeur émise. Query.stream() produit un flux qui émet de nouvelles valeurs lorsque les métadonnées de l'appareil changent, par exemple son nom, sa pièce ou sa structure. En interne, cela utilise batched() et n'émet que les propriétés modifiées.

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

Vous pouvez alors accéder aux états de chaque appareil et leur envoyer des commandes.

Avec la version 1.8 des API Home, vous pouvez faire en sorte que l'API représente chaque appareil multiparte comme un seul appareil en définissant le paramètre enableMultipartDevices de la méthode devices() sur true. Pour en savoir plus, consultez Appareils multiparts sur iOS.

Obtenir les types d'appareils

Pour obtenir les types d'appareils associés à un appareil, lisez la propriété types de l'appareil, qui renvoie un DeviceTypeController.

Appelez DeviceTypeController.subscribe(_:) pour vous abonner aux mises à jour d'un type d'appareil particulier :

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

Si l'appareil n'est pas compatible avec le type d'appareil spécifié, il renvoie un Empty Publisher qui se termine immédiatement.

Si l'appareil est compatible avec un type d'appareil spécifique, vous pouvez obtenir un handle pour ce type en appelant get() :

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

Si l'appareil n'est pas compatible avec le type spécifié, il renvoie nil.

Appelez DeviceTypeController.subscribeAll() pour obtenir un Publisher de DeviceTypeCollection. Cette classe vous permet de vérifier si l'appareil est d'un type particulier :

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

Obtenir un trait de type d'appareil

Les types d'appareils sont le point d'entrée pour la lecture des caractéristiques, car ils décomposent un appareil en ses éléments fonctionnels (comme les points de terminaison dans Matter).

Ils tiennent également compte des collisions d'attributs si un appareil présente deux types d'appareils, qui peuvent tous deux avoir le même attribut. Par exemple, si un appareil est à la fois une enceinte et une ampoule à intensité variable, il possède deux traits On/Off et deux traits Level Control.

Un autre type de conflit de trait peut se produire lorsqu'un appareil possède deux traits portant le même nom. Par exemple, onOff peut faire référence à une instance de la caractéristique OnOff standard ou à une instance d'une caractéristique OnOff définie par le fabricant. Pour éliminer toute ambiguïté potentielle quant au trait prévu, référencez un trait via l'une des deux collections de traits sur chaque type d'appareil.

Pour les caractéristiques standards, c'est-à-dire celles qui sont analogues aux clusters standards Matter, utilisez matterTraits. Par exemple, pour obtenir un trait spécifique pour le type d'appareil "Dimmable Light" :

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

Pour les caractéristiques Google, utilisez googleTraits :

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

Pour accéder à un trait spécifique à un fabricant, référencez-le via la propriété traits, mais faites-le précéder du nom du package du fabricant :

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
}

Lire l'état d'un appareil

Voici un exemple de vérification de l'attribut OnOff à partir du trait On/Off de l'appareil :

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
}

Obtenir la liste des appareils dotés d'une caractéristique spécifique

Pour obtenir la liste des appareils dotés d'un trait spécifique, vous devez effectuer une itération sur les appareils, les types d'appareils de chaque appareil et les traits de chaque type d'appareil. Par exemple, pour obtenir la liste des appareils de la maison qui possèdent tous la caractéristique OnOff :

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

Consultez l'index des caractéristiques sur iOS pour obtenir la liste complète des caractéristiques disponibles dans les API Home.

Obtenir la liste des appareils de types similaires

Pour obtenir la liste des appareils qui représentent toutes les lumières d'une maison :

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

Les API Home incluent plusieurs types d'appareils qui peuvent représenter un type d'appareil principal. Par exemple, il n'existe pas de type d'appareil "Light". Il existe quatre types d'appareils différents qui peuvent représenter une lumière, comme illustré dans l'exemple précédent. Par conséquent, pour obtenir une vue complète d'un type d'appareil de niveau supérieur dans une maison, plusieurs types d'appareils doivent être inclus.

Consultez la section Types d'appareils compatibles sur iOS pour obtenir la liste complète des types d'appareils et de leurs caractéristiques disponibles dans les API Home.

Obtenir le nom du fournisseur, l'ID du fournisseur ou l'identifiant produit pour un appareil

Le trait BasicInformationTrait inclut des informations telles que l'ID du fournisseur, l'identifiant produit, le nom du produit et le numéro de série d'un appareil :

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

Identification des appareils cloud-à-cloud pour les fabricants d'appareils

Si vous êtes un fabricant d'appareils et que vous fabriquez des appareils Cloud-to-cloud, vous pouvez inclure ces champs de chaîne dans leur réponse SYNC afin d'identifier vos appareils Cloud-to-cloud via le trait BasicInformation :

  • ID du fournisseur émis par Connectivity Standards Alliance (Alliance) : "matterOriginalVendorId": "0xfff1",

  • Un identifiant de produit qui identifie de manière unique un produit d'un fournisseur : "matterOriginalProductId": "0x1234",

  • Identifiant unique de l'appareil, construit d'une manière spécifique au fabricant : "matterUniqueId": "matter-device-id",

Lorsque vous saisissez ces champs de chaîne, utilisez vos ID de fournisseur et de produit Matter si vous les avez. Si vous n'êtes pas membre de la Alliance et que ces ID ne vous ont pas été attribués, vous pouvez laisser les champs matterOriginalVendorId et matterOriginalProductId vides et fournir l'matterUniqueId comme identifiant.

L'exemple de réponse SYNC montre l'utilisation de ces champs :

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

Pour en savoir plus, consultez la documentation sur Cloud-to-cloud SYNC.

Métadonnées des appareils et des caractéristiques

Les appareils et les caractéristiques des API Home sont associés à des métadonnées, qui peuvent vous aider à gérer l'expérience utilisateur dans une application.

Chaque trait des API Home contient une propriété sourceConnectivity, qui contient des informations sur l'état en ligne et la localité d'un trait (routage local ou distant).

Obtenir le type principal d'un appareil

Certains appareils peuvent présenter plusieurs types d'appareils via les API Home. Pour vous assurer que les utilisateurs voient les options appropriées dans une application (comme le contrôle de l'appareil et les automatisations suggérées) pour leurs appareils, il est utile de vérifier si un type d'appareil est le type principal de l'appareil.

Important : Un appareil peut avoir plusieurs types principaux.
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.")
  }
}

Vérifier si un trait est en ligne

Lisez la propriété connectivityState pour vérifier la connectivité d'un trait :

let levelControlConnectivity =
  levelControlTrait.metadata.sourceConnectivity
  .connectivityState

Certains traits, généralement les traits Google smart home, peuvent s'afficher hors connexion si l'appareil n'est pas connecté à Internet. En effet, ces caractéristiques sont dans le cloud et ne disposent pas de routage local.

Vérifier la connectivité d'un appareil

La connectivité d'un appareil est en fait vérifiée au niveau du type d'appareil, car certains appareils sont compatibles avec plusieurs types d'appareils. L'état renvoyé est une combinaison des états de connectivité de tous les traits de cet appareil.

let lightConnectivity =
  dimmableLightDeviceType.metadata.sourceConnectivity
  .connectivityState

Un état partiallyOnline peut être observé dans le cas de types d'appareils mixtes lorsqu'il n'y a pas de connectivité Internet. Les caractéristiques standards Matter peuvent toujours être en ligne en raison du routage local, mais les caractéristiques dans le cloud seront hors connexion.

Obtenir l'adresse IP de l'appareil

Pour trouver l'adresse IP de l'appareil, utilisez l'attribut networkInterfaces de GeneralDiagnosticsTrait. Les adresses sont renvoyées sous forme d'objets Data, que vous pouvez formater en chaînes IPv4 ou IPv6 standards à l'aide du 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
}

Vérifier le routage réseau d'un trait

La localité d'une caractéristique est également disponible dans les API Home. dataSourceLocality indique si le trait est acheminé à distance (via le cloud), localement (via un hub local) ou en peer-to-peer (directement d'un appareil à un autre, sans hub).

La valeur de localité inconnue unspecified est possible, par exemple, lorsqu'une application démarre et n'a pas encore atteint un hub ou un serveur pour la connectivité de l'appareil. Ces appareils ne sont pas accessibles et les requêtes d'interaction provenant de commandes ou d'événements échoueront. Il appartient au client de déterminer comment gérer ces appareils.

let levelControlLocality =
  levelControlTrait.metadata.sourceConnectivity
  .dataSourceLocality

Vérifier le routage réseau d'un appareil

Comme la connectivité, la localité est vérifiée au niveau du type d'appareil. L'état renvoyé est une combinaison de la localité de tous les traits de cet appareil.

let lightLocality =
  dimmableLightDeviceType.metadata.sourceConnectivity.dataSourceLocality

Un état mixed peut être observé dans un scénario similaire à celui de la connectivité partiallyOnline : certains traits sont dans le cloud, tandis que d'autres sont locaux.

Modifier le nom d'un appareil

Appelez la méthode setName(_:) pour modifier le nom d'un appareil :

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

Lorsque vous modifiez le nom d'un appareil, la structure HomeDevice d'origine reste inchangée et la modification est reflétée dans l'objet HomeDevice mis à jour renvoyé.

Les noms seront tronqués s'ils dépassent la limite de 60 points de code Unicode (caractères), et aucune erreur ne sera générée. Les développeurs sont responsables de la gestion des noms longs et peuvent, par exemple, décider d'informer les utilisateurs que les noms seront tronqués.

Vérifier l'appartenance d'un appareil à un groupe

Un appareil physique peut appartenir à un ou plusieurs groupes d'appareils. Pour savoir à quels groupes appartient un appareil, vérifiez le GroupMembershipTrait sur l'appareil :

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

Pour inspecter les types d'appareils compatibles avec un DeviceGroup et envoyer des commandes au groupe en tant qu'entité unique, consultez Contrôler un groupe d'appareils.