Cómo acceder a dispositivos y metadatos de dispositivos para Android

Se puede acceder a las APIs de dispositivos a través de las APIs de Home para Android. Importa estos paquetes a tu app:

import com.google.home.Home
import com.google.home.HomeDevice
import com.google.home.Id

Para usar tipos o características de dispositivos específicos con las APIs de dispositivos, se deben importar de forma individual.

Por ejemplo, para usar la característica de encendido/apagado Matter y el tipo de dispositivo de unidad de conexión/desconexión, importa los siguientes paquetes a tu aplicación:

import com.google.home.matter.standard.OnOff
import com.google.home.matter.standard.OnOffPluginUnitDevice

Para obtener más información, consulta Modelo de datos en Android.

Manejo de errores

Cualquier método de las APIs de Home puede arrojar una HomeException, por lo que te recomendamos que uses un bloque try-catch para detectar HomeException en todas las llamadas.

Cuando manejes HomeException, verifica sus campos error.code y error.message para saber qué ocurrió. También puede haber códigos de suberror as well, so call the getSubErrorCodes() method and check the result.

Cualquier excepción no controlada provocará que falle tu app.

Para obtener más información, consulta Manejo de errores.

Consulta Cómo enviar un comando a un dispositivo para ver un ejemplo.

Llamadas de muestra

Obtener una lista de dispositivos

Una vez que tengas una referencia a la instancia Structure, una llamada devices() muestra un Flow de dispositivos a los que puedes acceder desde esa estructura:

// Get a flow of all devices accessible to the user
val allDevicesFlow: HomeObjectsFlow<HomeDevice> = home.devices()

// Calling list() on a HomeObjectsFlow returns the first Set of elements.
val allDevices: Set<HomeDevice> = allDevicesFlow.list()

Desde allí, puedes acceder a los estados de cada dispositivo y enviar comandos a los dispositivos.

Con la versión 1.8 de las APIs de Home, tienes la opción de que la API represente cada dispositivo de varias partes como un solo dispositivo configurando el parámetro enableMultipartDevices del método devices() en true. Consulta Dispositivos de varias partes en Android para obtener más información.

Leer el estado de un dispositivo

Observa un ejemplo de cómo verificar el atributo OnOff de la característica de encendido/apagado del dispositivo. Con el modelo de datos de características de las APIs de Home, en el que esta característica se identifica como OnOff, puedes recuperar datos de características a través de la clase standardTraits del tipo de dispositivo:

// Assuming we have a device.
val deviceFlow = home.devices().itemFlow(myDeviceId)

val device = deviceFlow.first()

// Get a flow of a standard trait on the type. distinctUntilChanged() is needed to only trigger
// on the specific trait changes and not the whole type.
val onOffTraitFlow: Flow<OnOff?> =
  device.type(DimmableLightDevice).map { it.standardTraits.onOff }.distinctUntilChanged()

val onOffTrait: OnOff = onOffTraitFlow.first()!!

Consulta distinctUntilChanged para obtener más información sobre la función de flujo de Kotlin.

Invalidar el estado en una suscripción de características

La interfaz TraitStateInvalidation proporciona la capacidad de invalidar un estado recuperado a través de suscripciones al dispositivo de destino en los casos en que el estado no se informa correctamente. Entre los ejemplos de cuándo es posible que el estado no se informe correctamente, se incluye el uso de atributos en las características de Matter con la calidad "C" o debido a una implementación del dispositivo que causa el problema de forma inesperada.

Esta API emite una lectura forzada del estado actual de la característica y muestra el resultado a través de los flujos de características existentes.

Obtén la característica y, luego, ejecuta un forceRead en ella:

val onOffTrait = device.?type(DimmableLightDevice)?.map{it.trait(OnOff)}.first()
onOffTrait.forceRead()

Obtener una lista de características de tipo de dispositivo

Los tipos de dispositivos se deben usar como punto de entrada para leer características, ya que descomponen un dispositivo en sus partes funcionales (como extremos en Matter).

También tienen en cuenta las colisiones de características en caso de que un dispositivo tenga dos tipos de dispositivos, ambos con la misma característica. Por ejemplo, si un dispositivo es un altavoz y una luz regulable, tendría dos características de encendido/apagado y dos de control de nivel.

Para obtener la lista de características disponibles para el tipo de dispositivo de luz regulable, haz lo siguiente:

// Get all types available on this device. Requires the types to be part of the registry during
// SDK initialization.
val typesFlow: Flow<Set<DeviceType>> = device.types()

// Get a snapshot of all types.
val types: Set<DeviceType> = typesFlow.first()

// Get the DimmableLightDevice instance from the set of types.
val dimmableLightDevice = types.filterIsInstance<DimmableLightDevice>().firstOrNull()

// Get all traits in the type + traits registered
val allTraits: Set<Trait> = dimmableLightDevice!!.traits()

Otro tipo de colisión de características puede ocurrir cuando un dispositivo tiene dos características con el mismo nombre. Por ejemplo, onOff podría hacer referencia a una instancia de la característica OnOff estándar o a una instancia de una característica OnOff definida por el fabricante. Para eliminar cualquier ambigüedad potencial en cuanto a qué característica se pretende, una instancia Trait a la que se hace referencia a través de un dispositivo debe ir precedida de un espacio de nombres calificado. Para las características estándar, es decir, aquellas que son análogas a Matter clústeres estándar, usa standardTraits. Para las características de Google, usa googleTraits:

// Accessing standard traits on the type.
val onOffTrait: OnOff? = dimmableLightDevice.standardTraits.onOff
val levelControlTrait: LevelControl? = dimmableLightDevice.standardTraits.levelControl

Para acceder a una característica específica del fabricante, haz referencia a ella directamente:

// Accessing a custom trait on the type.
val customTrait = dimmableLightDevice.trait(MyCustomTrait)

Obtener una lista de dispositivos con una característica específica

Se puede usar la función filter en Kotlin para refinar aún más las llamadas a la API. Por ejemplo, para obtener una lista de dispositivos en la casa que tengan la característica de encendido/apagado, haz lo siguiente:

// Get all devices that support OnOff
val onOffDevices: Flow<List<HomeDevice>> =
  home.devices().map { devices -> devices.filter { it.has(OnOff) } }

Consulta la interfaz Trait para obtener una lista completa de las características disponibles en las APIs de Home.

Obtener una lista de dispositivos con tipos de dispositivos similares

Para obtener una lista de dispositivos que representen todas las luces de una casa, haz lo siguiente:

// Get a list of devices with similar device types (lights)
val lightDevices =
  home.devices().map { devices ->
    devices.filter {
      it.has(DimmableLightDevice) ||
        it.has(OnOffLightDevice) ||
        it.has(ColorTemperatureLightDevice) ||
        it.has(ExtendedColorLightDevice)
    }
  }

Existen varios tipos de dispositivos en las APIs de Home que podrían representar un tipo de dispositivo principal. Por ejemplo, no hay un tipo de dispositivo "Luz". En cambio, hay cuatro tipos de dispositivos diferentes que podrían representar una luz, como se muestra en el ejemplo anterior. Por lo tanto, para obtener una vista integral del tipo de dispositivo de nivel superior en una casa, se deben incluir varios tipos de dispositivos en los flujos filtrados.

Consulta la interfaz DeviceType para obtener una lista completa de los tipos de dispositivos disponibles en las APIs de Home.

Obtener el ID del proveedor o el ID del producto de un dispositivo

La BasicInformation característica incluye información como el ID del proveedor, el ID del producto, el nombre del producto y el número de serie de un dispositivo:

// Get device basic information. All general information traits are on the RootNodeDevice type.
val basicInformation = device.type(RootNodeDevice).first().standardTraits.basicInformation!!
println("vendorName ${basicInformation.vendorName}")
println("vendorId ${basicInformation.vendorId}")
println("productId ${basicInformation.productId}")

Identificación de dispositivos de nube a nube para fabricantes de dispositivos

Si eres fabricante de dispositivos y compilas Cloud-to-cloud dispositivos, para identificar tus Cloud-to-cloud dispositivos a través de la BasicInformation característica, puedes incluir estos campos de cadena en su respuesta SYNC:

  • El ID del proveedor emitido por Connectivity Standards Alliance (Alliance) issued vendor ID: "matterOriginalVendorId": "0xfff1",

  • Un identificador de producto que identifica de forma única un producto de un proveedor: "matterOriginalProductId": "0x1234",

  • Un identificador único para el dispositivo, que se construye de una manera específica del fabricante: "matterUniqueId": "matter-device-id",

Cuando ingreses estos campos de cadena, usa tus Matter IDs de proveedor y producto si los tienes. Si no eres miembro de Alliance y no se te asignaron estos IDs, puedes dejar en blanco los campos matterOriginalVendorId y matterOriginalProductId y proporcionar el matterUniqueId como identificador.

En la respuesta SYNC de ejemplo, se muestra el uso de estos 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 obtener más información, consulta la documentación de Cloud-to-cloud SYNC.

Metadatos de dispositivos y características

Los dispositivos y las características de las APIs de Home tienen metadatos asociados que pueden ayudar a administrar la experiencia del usuario en una app.

Cada característica de las APIs de Home contiene una sourceConnectivity propiedad, que tiene información sobre el estado en línea y la localidad de una característica (enrutamiento local o remoto).

Obtener el tipo principal de un dispositivo

Algunos dispositivos pueden presentar varios tipos de dispositivos a través de las APIs de Home. Para garantizar que los usuarios vean las opciones adecuadas en una app (como el control del dispositivo y las automatizaciones sugeridas) para sus dispositivos, es útil verificar cuál es el tipo de dispositivo principal de un dispositivo.

Primero, obtén los tipos de dispositivo con type(), luego, determina los tipos principales:

val types = device.types().first()
val primaryTypes = types.filter { it.metadata.isPrimaryType }

Verificar si una característica está en línea

Usa el método connectivityState() para verificar la conectividad de una característica:

val onOffConnectivity = onOffTrait?.metadata?.sourceConnectivity?.connectivityState

Es posible que algunas características, por lo general, las características de smart home Google, se muestren sin conexión si el dispositivo no tiene conexión a Internet. Esto se debe a que estas características se basan en la nube y no tienen enrutamiento local.

Verificar la conectividad de un dispositivo

La conectividad de un dispositivo se verifica en el nivel del tipo de dispositivo, ya que algunos dispositivos admiten varios tipos de dispositivos. El estado que se muestra es una combinación de los estados de conectividad de todas las características de ese dispositivo.

val lightConnectivity = dimmableLightDevice.metadata.sourceConnectivity.connectivityState

Se puede observar un estado de PARTIALLY_ONLINE en el caso de tipos de dispositivos mixtos cuando no hay conectividad a Internet. Matter Las características estándar pueden seguir en línea debido al enrutamiento local, pero las características basadas en la nube estarán sin conexión.

Verificar el enrutamiento de red de una característica

La localidad de una característica también está disponible en las APIs de Home. dataSourceLocality indica si la característica se enruta de forma remota (a través de la nube), localmente (a través de una unidad central local) o de par a par (directamente de dispositivo a dispositivo, sin unidad central).

Es posible que el valor de localidad desconocido UNSPECIFIED se produzca, por ejemplo, mientras se inicia una app y aún no llegó a una unidad central o un servidor para la conectividad del dispositivo. No se puede acceder a estos dispositivos y fallarán las solicitudes de interacción de comandos o eventos. Depende del cliente determinar cómo manejar esos dispositivos.

val onOffLocality = onOffTrait?.metadata?.sourceConnectivity?.dataSourceLocality

Verificar el enrutamiento de red de un dispositivo

Al igual que la conectividad, la localidad se verifica en el nivel del tipo de dispositivo. El estado que se muestra es una combinación de la localidad de todas las características de ese dispositivo.

val lightLocality = dimmableLightDevice.metadata.sourceConnectivity.dataSourceLocality

Se puede observar un estado de MIXED en una situación similar a la de la conectividad PARTIALLY_ONLINE: algunas características se basan en la nube, mientras que otras son locales.

Cambiar el nombre de un dispositivo

Llama al setName() método para cambiar el nombre de un dispositivo:

mixerDevice.setName("Grendel")

Los nombres se truncarán si superan el límite de 60 puntos de código Unicode (caracteres) y no se arrojarán errores. Los desarrolladores son responsables de manejar nombres largos y, por ejemplo, pueden decidir si quieren informar a los usuarios que los nombres se truncarán.