Acessar dispositivos e metadados de dispositivos para iOS

As APIs de dispositivo podem ser acessadas pelas APIs do Google Home para iOS. Importe os seguintes pacotes para o app:

import GoogleHomeSDK
import GoogleHomeTypes

Para mais informações, consulte Modelo de dados no iOS.

Tratamento de erros

Alguns métodos nas APIs do Google Home geram uma HomeError. Por isso, recomendamos que você use um bloco do-catch para capturar HomeError nessas chamadas.

Ao processar HomeError, verifique os campos code e message para saber o que deu errado.

Qualquer erro não processado vai causar uma falha no app.

Para mais informações, consulte Tratamento de erros.

Consulte Enviar um comando para um dispositivo para ver um exemplo.

Exemplos de chamadas

Receber uma lista de dispositivos

Com uma referência ao objeto Home, invocar devices() para receber um Query de dispositivos acessíveis. Chame o método batched() do Query, que emite um Set que reflete o estado atual da casa com cada mudança de metadados do dispositivo. Ou chame Query.list() para ver um snapshot dos dispositivos disponíveis. Esse é um método de conveniência que se inscreve no stream batched() e retorna o primeiro valor emitido. Query.stream() produz um stream que emite novos valores em mudanças de metadados do dispositivo, como nome, ambiente ou estrutura. Internamente, isso usa batched() e emite apenas as propriedades modificadas.

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

Nela, é possível acessar os estados de cada dispositivo e enviar comandos para eles.

Com a versão 1.8 das APIs do Google Home, é possível fazer com que a API represente cada dispositivo de várias partes como um único dispositivo, definindo o parâmetro enableMultipartDevices do método devices() como true. Consulte Dispositivos de várias partes no iOS para mais informações.

Acessar os tipos do dispositivo

Para conferir os tipos de dispositivo associados a um dispositivo, leia a propriedade types dele, que retorna um DeviceTypeController.

Chame DeviceTypeController.subscribe(_:) para se inscrever em atualizações de um tipo de dispositivo específico:

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 o dispositivo não for compatível com o tipo especificado, ele vai retornar um Empty Publisher que é concluído imediatamente.

Se o dispositivo for compatível com um tipo específico, você poderá receber um identificador para esse tipo chamando get():

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

Se o dispositivo não for compatível com o tipo especificado, ele vai retornar nil.

Chame DeviceTypeController.subscribeAll() para receber um Publisher de DeviceTypeCollection. Essa classe permite verificar se o dispositivo tem um tipo específico:

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

Receber um atributo de tipo de dispositivo

Os tipos de dispositivo são o ponto de entrada para ler atributos, já que eles decompõem um dispositivo em partes funcionais (como endpoints em Matter).

Eles também consideram colisões de atributos caso um dispositivo tenha dois tipos, ambos com o mesmo atributo. Por exemplo, se um dispositivo for um alto-falante e uma luz regulável, ele terá dois atributos On/Off e dois atributos Level Control.

Outro tipo de colisão de atributo pode ocorrer quando um dispositivo tem dois atributos com o mesmo nome. Por exemplo, onOff pode se referir a uma instância do atributo padrão OnOff ou a uma instância de um atributo OnOff definido pelo fabricante. Para eliminar qualquer ambiguidade em relação ao atributo pretendido, faça referência a um atributo usando uma das duas coleções de atributos em cada tipo de dispositivo.

Para características padrão, ou seja, aquelas que são análogas aos clusters padrão do Matter, use matterTraits. Por exemplo, para receber um atributo específico para o tipo de dispositivo de luz regulável:

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

Para atributos do Google, use googleTraits:

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

Para acessar um atributo específico do fabricante, faça referência a ele usando a propriedade traits, mas coloque o nome do pacote do fabricante antes:

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
}

Ler o estado de um dispositivo

Confira este exemplo de verificação do atributo OnOff do atributo On/Off do 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
}

Receber uma lista de dispositivos com um atributo específico

Para receber uma lista de dispositivos que têm um atributo específico, é necessário iterar sobre os dispositivos, os tipos de dispositivo de cada um e os atributos de cada tipo. Por exemplo, para receber uma lista de dispositivos na casa que têm o atributo 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)
    }
  }
}

Consulte o índice de atributos no iOS para ver uma lista completa de atributos disponíveis nas APIs Home.

Receber uma lista de dispositivos com tipos semelhantes

Para ver uma lista de dispositivos que representam todas as luzes de uma 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)
  }

Há vários tipos de dispositivos nas APIs do Google Home que podem representar um tipo de dispositivo principal. Por exemplo, não há um tipo de dispositivo "Luz". Em vez disso, há quatro tipos de dispositivos diferentes que podem representar uma luz, como mostrado no exemplo anterior. Portanto, para ter uma visão abrangente do tipo de dispositivo de nível superior em uma casa, vários tipos de dispositivo precisam ser incluídos.

Consulte os tipos de dispositivo compatíveis com o iOS para ver uma lista completa de tipos de dispositivos e os atributos disponíveis nas APIs Home.

Encontrar o nome do fornecedor, o ID do fornecedor ou o ID do produto de um dispositivo

O atributo BasicInformationTrait inclui informações como ID do fornecedor, ID do produto, nome do produto e número de série de um 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")
}

Identificação de dispositivos de nuvem para nuvem para fabricantes de dispositivos

Se você é um fabricante de dispositivos e cria dispositivos Cloud-to-cloud, para identificar seus dispositivos Cloud-to-cloud pelo atributo BasicInformation, inclua estes campos de string na resposta SYNC:

  • O ID do fornecedor emitido pelo Connectivity Standards Alliance (Alliance): "matterOriginalVendorId": "0xfff1",

  • Um identificador de produto que identifica exclusivamente um produto de um fornecedor: "matterOriginalProductId": "0x1234",

  • Um identificador exclusivo do dispositivo, que é construído de maneira específica do fabricante: "matterUniqueId": "matter-device-id",

Ao preencher esses campos de string, use os IDs de fornecedor e ID do produto do Matter, se tiver. Se você não for membro da Alliance e não tiver esses IDs, deixe os campos matterOriginalVendorId e matterOriginalProductId em branco e forneça o matterUniqueId como identificador.

O exemplo de resposta SYNC mostra o uso desses campos:

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

Para mais informações, consulte a documentação do Cloud-to-cloud SYNC.

Metadados de dispositivos e atributos

Os dispositivos e atributos nas APIs do Google Home têm metadados associados a eles, o que pode ajudar a gerenciar a experiência do usuário em um app.

Cada atributo nas APIs do Google Home contém uma propriedade sourceConnectivity, que tem informações sobre o status on-line e a localidade de um atributo (roteamento local ou remoto).

Receber o tipo principal de um dispositivo

Alguns dispositivos podem apresentar vários tipos de dispositivos pelas APIs do Google Home. Para garantir que os usuários vejam as opções adequadas em um app (como controle de dispositivos e automações sugeridas) para os dispositivos deles, é útil verificar se um tipo de dispositivo é o tipo principal do 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.")
  }
}

Verificar se um atributo está on-line

Leia a propriedade connectivityState para verificar a conectividade de um atributo:

let levelControlConnectivity =
  levelControlTrait.metadata.sourceConnectivity
  .connectivityState

Alguns atributos, normalmente do Google smart home, podem aparecer off-line se o dispositivo não tiver conexão de internet. Isso ocorre porque esses atributos são baseados na nuvem e não têm roteamento local.

Verificar a conectividade de um dispositivo

A conectividade de um dispositivo é verificada no nível do tipo de dispositivo, porque alguns dispositivos são compatíveis com vários tipos. O estado retornado é uma combinação dos estados de conectividade de todos os atributos desse dispositivo.

let lightConnectivity =
  dimmableLightDeviceType.metadata.sourceConnectivity
  .connectivityState

Um estado de partiallyOnline pode ser observado no caso de tipos de dispositivos mistos quando não há conectividade com a Internet. Os atributos padrão do Matter ainda podem estar on-line devido ao roteamento local, mas os atributos baseados na nuvem estarão off-line.

Encontrar o endereço IP do dispositivo

Para encontrar o endereço IP do dispositivo, use o atributo networkInterfaces do GeneralDiagnosticsTrait. Os endereços são retornados como objetos Data, que podem ser formatados como strings IPv4 ou IPv6 padrão usando o 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
}

Verificar o roteamento de rede de uma característica

A localidade de um atributo também está disponível nas APIs Home. O dataSourceLocality indica se o atributo é roteado remotamente (pela nuvem), localmente (por um hub local) ou ponto a ponto (diretamente de dispositivo para dispositivo, sem hub).

O valor de localidade desconhecido unspecified é possível, por exemplo, enquanto um app está sendo inicializado e ainda não alcançou um hub ou servidor para conectividade do dispositivo. Esses dispositivos não estão acessíveis e vão falhar nas solicitações de interação de comandos ou eventos. Cabe ao cliente determinar como lidar com esses dispositivos.

let levelControlLocality =
  levelControlTrait.metadata.sourceConnectivity
  .dataSourceLocality

Verificar o roteamento de rede de um dispositivo

Assim como a conectividade, a localidade é verificada no nível do tipo de dispositivo. O estado retornado é uma combinação da localidade de todos os atributos nesse dispositivo.

let lightLocality =
  dimmableLightDeviceType.metadata.sourceConnectivity.dataSourceLocality

Um estado de mixed pode ser observado em um cenário semelhante ao da conectividade partiallyOnline: algumas características são baseadas na nuvem, enquanto outras são locais.

Mudar o nome de um dispositivo

Chame o método setName(_:) para mudar o nome de um dispositivo:

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

Ao mudar o nome de um dispositivo, o struct HomeDevice original permanece o mesmo, e a mudança é refletida no objeto HomeDevice atualizado retornado.

Os nomes serão truncados se ultrapassarem o limite de 60 pontos de código Unicode (caracteres), e nenhum erro será gerado. Os desenvolvedores são responsáveis por lidar com nomes longos e, por exemplo, podem decidir se querem informar aos usuários que os nomes serão truncados.

Verificar a associação de um dispositivo a um grupo

Um dispositivo físico pode pertencer a um ou mais grupos de dispositivos. Para inspecionar a quais grupos um dispositivo pertence, verifique o GroupMembershipTrait no dispositivo:

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

Para inspecionar os tipos de dispositivo compatíveis com um DeviceGroup e enviar comandos para o grupo como uma única entidade, consulte Controlar um grupo de dispositivos.