APIs de Structure en Android

Se puede acceder a las APIs de Structure 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.Id
import com.google.home.Structure

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 controles HomeException, verifica sus campos error.code y error.message para saber qué ocurrió. También puede haber códigos de error secundarios, por lo que debes llamar al método getSubErrorCodes() y verificar el resultado.

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

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

Llamadas de muestra

Obtén una lista de estructuras

Una vez inicializada, una llamada a structures() devuelve un flujo de estructuras a las que puedes acceder:

// Get a flow of all structures accessible to the user
val allStructuresFlow: HomeObjectsFlow<Structure> = home.structures()

// Calling list() on a HomeObjectsFlow returns the first Set of elements.
val allStructures: Set<Structure> = allStructuresFlow.list()

La API de structures() es un flujo que podría no devolver de inmediato una lista válida de estructuras. Si tu app es reactiva y se suscribe a ese flujo para controlar la IU, con el tiempo, se debería mostrar una lista válida de estructuras. Existen otras situaciones en las que se podría mostrar una lista de estructuras vacía, por ejemplo, si el teléfono del usuario pierde la conectividad o si el usuario revocó los permisos de tu app. Asegúrate de controlar estos casos en tu app.

Como alternativa, si se requiere programación imperativa en lugar de programación reactiva, se puede usar un operador de flujo de terminal:

val everyStructure = withTimeout(5000) { home.structures().first { it.isNotEmpty() } }

Esta llamada espera que llegue una lista válida de estructuras a través del flujo y se agota el tiempo de espera si no se recibe la lista dentro de un tiempo de espera designado por la app.

Obtener propiedades de la estructura

Con la lista de estructuras en mano, puedes acceder a sus propiedades:

// Get a flow on a structure. Flow emits new values on structure metadata changes: name.
val structureFlow: Flow<Structure> = home.structures().itemFlow(myStructureId)

// Get a snapshot of the structure.
val structure: Structure = structureFlow.first()

// Get structure properties
println("id ${structure.id}")
println("name ${structure.name}")

Busca una estructura por nombre

Si conoces el nombre de una estructura, también puedes acceder a ella con la propiedad name:

val myHome = home.structures().list().first { it.name == "My home" }

Desde allí, se puede acceder a las propiedades, las habitaciones y los dispositivos de cada estructura.

Trabaja con varias estructuras

Para usar más de una estructura, obtén una referencia independiente para cada una:

var structure1: Structure? = null
var structure2: Structure? = null

try {
  structure1 = home.structures().list().firstOrNull { it.name == "Main House" }
} catch (e: HomeException) {
  // Code for handling the exception
}
try {
  structure2 = home.structures().list().firstOrNull { it.name == "Guest Cottage" }
} catch (e: HomeException) {
  // Code for handling the exception
}

Obtén una lista de salas

Con una estructura en mano, puedes obtener una lista de habitaciones y acceder a sus propiedades:

val allRoomsFlow: HomeObjectsFlow<Room> = structure.rooms()
val allRooms: Set<Room> = allRoomsFlow.list()
val room: Room = allRooms.first()

println("id ${room.id}")
println("name ${room.name}")

Crear una sala

Para crear una sala nueva, sigue estos pasos:

val testName = "Test Room Name"
val newRoom: Room = structure.createRoom(testName)

Cómo borrar una habitación

Como alternativa, puedes borrar una sala de la siguiente manera:

val roomToDelete = structure.rooms().list().filter { it.name == "room_id1" }.firstOrNull()
    structure.deleteRoom(roomToDelete!!)

También puedes borrar una sala con solo un ID:

val roomToDelete1 = allRooms.filter { it.id == testRoomId }.firstOrNull()
structure.deleteRoom(roomToDelete1!!)

Si se borra una habitación con dispositivos, estos seguirán estando en la estructura, pero ya no estarán asignados a una habitación.

Cómo mover dispositivos a otra habitación

Una vez que tengas una estructura, podrás mover los dispositivos a otra habitación dentro de ella:

val room2 = structure.rooms().get(Id("room_id_other_structure"))
    val device1 = structure.devices().get(Id("device_id1"))
    structure.moveDevicesToRoom(room2!!, listOf(device1!!))

Si solo tienes los IDs de dispositivo y de habitación, también puedes mover los dispositivos de la siguiente manera:

structure.moveDevicesToRoom(Id("room_id_other_structure"), listOf(Id("device_id1")))

Cómo cambiar el nombre de una habitación

Llama al método setName() para cambiar el nombre de una sala:

livingRoom.setName("Living Room")

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 controlar los nombres largos y, por ejemplo, pueden decidir si quieren informar a los usuarios que se truncarán los nombres.

En el ecosistema de Google Home, para la mayoría de los tipos de dispositivos, los usuarios pueden otorgar permisos para todos los dispositivos de ese tipo a la vez. En el caso de los tipos de dispositivos sensibles o restringidos, como cerraduras, cámaras o timbres, los usuarios deben otorgarles permiso de forma individual.

Para determinar si un usuario otorgó permiso para acceder a un tipo de dispositivo sensible o restringido, usa la función consentedDeviceTypes() a nivel de la estructura:

import com.google.home.Structure
import com.google.home.DeviceType
import com.google.home.DeviceTypeFactory
import com.google.home.consentedDeviceTypes // Extension function from the SDK
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch

/**
 * Example of how an app may monitor which device types have been granted access by a user.
 */
fun monitorDeviceConsent(structure: Structure, myScope: CoroutineScope) {
    // Obtain the flow of consented device type factories
    val consentedTypesFlow: Flow<Set<DeviceTypeFactory<out DeviceType>>> =
        structure.consentedDeviceTypes()

    myScope.launch {
        consentedTypesFlow.collect { consentedSet ->
            // Check if the user has consented to share a specific restricted
            // type, such as a Doorbell or Camera.
            val hasCameraAccess = consentedSet.any {
                it.toString() == "matter.google.type.GoogleDoorbellDevice"
            }

            if (hasCameraAccess) {
                // Enable features that require camera access
            } else {
                // Inform the user or disable camera-specific features
            }
        }
    }
}

Grupos de dispositivos

Un grupo de dispositivos representa una colección de dispositivos definida por el usuario dentro de una sola estructura. La membresía del grupo se conserva en los servicios de Google y es visible para otros miembros de la estructura y otras apps con acceso a la estructura.

Al igual que las habitaciones físicas, los grupos de dispositivos pertenecen a una sola estructura. Sin embargo, a diferencia de las habitaciones, los grupos de dispositivos son agrupaciones lógicas. Un dispositivo puede pertenecer a varios grupos de dispositivos de forma simultánea. Los grupos de dispositivos pueden contener hasta 100 dispositivos.

Los grupos de dispositivos se representan con la entidad DeviceGroup, que es distinta de HomeDevice. Para consultar grupos de dispositivos y, también, interactuar con ellos, incluye DeviceGroup en la lista entityTypes cuando inicialices tu FactoryRegistry, junto con los atributos Group, GroupManagement y GroupMembership.

Puedes interactuar con los dispositivos de un grupo iterando directamente en sus dispositivos miembros (consulta Cómo interactuar con los dispositivos miembros de un grupo) o segmentando una parte del tipo de dispositivo en el DeviceGroup (consulta Cómo controlar un grupo de dispositivos).

Obtén una lista de grupos de dispositivos

Para observar todos los grupos de dispositivos de una estructura, llama a home.entities(DeviceGroup) y filtra por el ID de la estructura:

home.entities(DeviceGroup).collect { allGroups ->
  val structureGroups = allGroups.filter { it.structureId == structure.id }
  for (group in structureGroups) {
    val groupName = group.trait(Group)?.Name
    println("Found group: $groupName with ID: ${group.id.id}")
  }
}

Interactúa con los dispositivos de los miembros del grupo

La iteración en los dispositivos miembros de un grupo con group.devices() te brinda un control detallado sobre los miembros individuales, te permite agrupar comandos en una sola solicitud y te permite agregar o escribir atributos en todos los miembros.

Por ejemplo, para enviar una sola solicitud por lotes con comandos para un rasgo en todos los miembros compatibles de un grupo, haz lo siguiente:

val members = group.devices(enableMultipartDevices = true).first()
val onOffTraits = members.mapNotNull { device ->
  device.partOrNull(OnOffLightDevice).first()?.standardTraits?.onOff
}

val responses = home.sendBatchedCommands {
  onOffTraits.map { onOff ->
    add(onOff.onBatchable())
  }
}

for (response in responses) {
  response.getOrThrow()
}

También puedes actualizar un atributo en todos los dispositivos miembros que coincidan en un grupo (por ejemplo, establecer onTransitionTime en 1u en todos los miembros DimmableLightDevice) y esperar a que se complete dentro de un plazo:

val members = group.devices(enableMultipartDevices = true).first()

withTimeout(5.seconds) {
  members.map { device ->
    async {
      val light = device.partOrNull(DimmableLightDevice).first()
      val levelControl = light?.standardTraits?.levelControl
      if (
        levelControl?.supports(
          LevelControl.Attribute.onTransitionTime
        ) == true
      ) {
        levelControl.update { setOnTransitionTime(1u) }
      }
    }
  }.awaitAll()
}

Para agregar un atributo entre los miembros de un grupo, como encontrar el colorTempPhysicalMaxMireds mínimo en todas las luces de temperatura de color del grupo, haz lo siguiente:

val members = group.devices(enableMultipartDevices = true).first()
val minMaxMireds: UShort? = members.mapNotNull { device ->
  val light = device.partOrNull(ColorTemperatureLightDevice).first()
  light?.standardTraits?.colorControl?.colorTempPhysicalMaxMireds
}.minOrNull()

Crea un grupo de dispositivos

Usa GroupManagement en la estructura para crear un grupo de dispositivos:

val groupManagement = structure.trait(GroupManagementTrait)
val response = groupManagement?.createUserDefinedGroup(
  name = "Living room lights",
  memberDeviceObjectIds = listOf(light1.id.id, light2.id.id)
)

Administra la pertenencia a grupos

Para agregar miembros a un grupo existente, sigue estos pasos:

structure.trait(GroupManagementTrait)?.addGroupMembers(
  groupObjectId = group.id.id,
  memberDeviceObjectIds = listOf(light3.id.id)
)

Para quitar miembros de un grupo, sigue estos pasos:

structure.trait(GroupManagementTrait)?.removeGroupMembers(
  groupObjectId = group.id.id,
  memberDeviceObjectIds = listOf(light1.id.id)
)

Cómo cambiar el nombre de un grupo de dispositivos

Para cambiar el nombre de un grupo de dispositivos, actualiza el atributo de nombre en el GroupTrait del grupo:

group.trait(GroupTrait)?.update {
  name = "Movie corner"
}

Borra un grupo de dispositivos

Para borrar un grupo de dispositivos de una estructura, haz lo siguiente:

structure.trait(GroupManagementTrait)?.deleteGroup(
  groupObjectId = group.id.id
)

Automatizaciones

El punto de entrada a la API de Automation es a través de una estructura. Para obtener más información sobre las automatizaciones en las APIs de Home, consulta la descripción general de la API de Automation en Android.