Guia de dispositivos de câmera para Android

O tipo de dispositivo câmera é implementado usando três atributos: PushAvStreamTransport, que processa o transporte de stream de áudio e vídeo usando protocolos baseados em push, WebRtcLiveView, que oferece a capacidade de controlar transmissões ao vivo e talkback, e CameraSnapshot, que fornece capturas de tela de visualização em cache e capturas de tela ao vivo.

Sempre verifique se o dispositivo é compatível com atributos e comandos antes de usar qualquer recurso ou tentar atualizar atributos. Consulte Controlar dispositivos no Android para mais informações.

Tipo de dispositivo das APIs Home Características App de exemplo em Kotlin Caso de uso

Câmera

GoogleCameraDevice

home.matter.6006.types.0158

Um dispositivo que captura imagens estáticas ou vídeos. As câmeras podem ter transmissões ao vivo acessíveis, conversas bidirecionais ou eventos de detecção.

Atributos obrigatórios
     google PushAvStreamTransport
     google WebRtcLiveView

Câmera

Receber informações básicas sobre um dispositivo

   Implementado no app de exemplo para Android   

O atributo BasicInformation inclui informações como nome do fornecedor, ID do fornecedor, ID do produto, nome do produto (inclui informações do modelo) e versão do software de um dispositivo:

// Get device basic information. All general information traits are on the RootNodeDevice type.
    device.type(RootNodeDevice).first().standardTraits.basicInformation?.let { basicInformation ->
        println("vendorName ${basicInformation.vendorName}")
        println("vendorId ${basicInformation.vendorId}")
        println("productId ${basicInformation.productId}")
        println("productName ${basicInformation.productName}")
        println("softwareVersion ${basicInformation.softwareVersion}")
    }

Encontrar o número de série

Para ver o número de série do dispositivo, use o comando GetSerialNumber da característica ExtendedBasicInformation. O exemplo mostra como salvar o número de série em uma variável chamada serialNumber:

val basicInfo: ExtendedBasicInformation = device.getTrait(ExtendedBasicInformation)
val serialNumber = basicInfo.getSerialNumber().serialNumber

Receber o horário mais recente de contato do dispositivo com a nuvem

Para encontrar o horário mais recente em que o dispositivo teve contato com a nuvem, use o atributo lastContactTimestamp da característica ExtendedGeneralDiagnostics:

fun getLastContactTimeStamp(trait: ExtendedGeneralDiagnostics): java.time.Instant {
  val timestamp = trait.lastContactTimestamp
  return Instant.ofEpochSecond(timestamp.toLong())
}

Configurações do tipo de montagem de câmera

A característica Mount contém as configurações de montagem da câmera e informações de status. É possível ler atributos como o estado de montagem, o tipo de detecção e o nome do tipo de montagem. Além disso, é possível usar o atributo Mount para substituir a configuração padrão do tipo de montagem.

// Get the Mount trait
val mountTrait: Mount = device.getTrait(Mount)

// Read the current mount state and detection type
val mountState = mountTrait.mountState
val mountDetectionType = mountTrait.mountDetectionType

// Read the current mount type name
val mountTypeName = mountTrait.mountTypeName

// Update the mount type override
mountTrait.update {
  setMountTypeOverride(MountTrait.MountTypeOverrideEnum.Official)
}

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.

val cameraConnectivity = cameraDevice.metadata.sourceConnectivity.connectivityState

Um estado de PARTIALLY_ONLINE pode ser observado no caso de tipos de dispositivos mistos quando não há conectividade com a Internet. As características padrão do Matter ainda podem estar on-line devido ao roteamento local, mas as características baseadas 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 trait GeneralDiagnostics. Os endereços são retornados como matrizes de bytes, que podem ser formatadas como strings IPv4 ou IPv6 padrão:

val ipAddresses =
  trait.networkInterfaces?.flatMap { networkInterface ->
    (networkInterface.ipv4Addresses + networkInterface.ipv6Addresses).mapNotNull { bytes ->
      try {
        java.net.InetAddress.getByAddress(bytes).hostAddress
      } catch (e: java.net.UnknownHostException) {
        null
      }
    }
  } ?: emptyList()

Iniciar uma transmissão ao vivo

   Implementado no app de exemplo para Android   

Para iniciar uma transmissão ao vivo, envie a string do Protocolo de Descrição de Sessão (SDP, na sigla em inglês) para o método startLiveView() do atributo WebRtcLiveView, que retorna um WebRtcLiveViewTrait.StartLiveViewCommand.Response com três valores:

  • O SDP da sessão.
  • A duração da sessão em segundos.
  • O ID da sessão, que pode ser usado para estender ou encerrar a sessão.
suspend fun getWebRtcLiveViewTrait(cameraDevice: HomeDevice) {
 return cameraDevice.type(GoogleCameraDevice).trait(WebRtcLiveView).first {
    it?.metadata?.sourceConnectivity?.connectivityState == ConnectivityState.ONLINE
  }

}

// Start the live view
suspend fun startCameraStream(trait: WebRtcLiveView, offerSdp: String) {
  val response = trait.startLiveView(offerSdp)
  // Response contains three fields (see below)
  return response
}
  ...

// This is used to manage the WebRTC connection
val peerConnection: RTCPeerConnection = ...

   ...

val startResponse = startCameraStream(sdp)
val answerSdp = startResponse?.answerSdp
val sessionDuration = startResponse?.liveSessionDurationSeconds
val mediaSessionId = startResponse?.mediaSessionId

peerConnection.setRemoteDescription(SessionDescription.Type.ANSWER,
                                    answerSdp)

Estender uma transmissão ao vivo

   Implementado no app de exemplo para Android   

As transmissões ao vivo têm uma duração predefinida e expiram após esse período. Para aumentar a duração de um stream ativo, emita uma solicitação de extensão usando o método WebRtcLiveView.extendLiveView():

// Assuming camera stream has just been started
suspend fun scheduleExtension(trait: WebRtcLiveView, mediaSessionId: String, liveSessionDurationSeconds: UShort ) {
  delay(liveSessionDurationSeconds - BUFFER_SECONDS * 1000)
  val response = trait.extendLiveView(mediaSessionId)
  // returns how long the session will be live for
  return response.liveSessionDurationSeconds
}

Iniciar e interromper o TalkBack

   Implementado no app de exemplo para Android   

Para iniciar o talkback, chame o método startTalkback() da característica WebRtcLiveView. Para interromper, use stopTalkback().

// Make sure camera stream is on
suspend fun setTalkback(isOn: Boolean, trait: WebRtcLiveView, mediaSessionId: String) {
  if(isOn) {
    trait.startTalkback(mediaSessionId)
  } else {
    trait.stopTalkback(mediaSessionId)
  }
}

Gerenciar transmissão ao vivo

Ajustar a qualidade da transmissão ao vivo é útil para otimizar o uso da largura de banda com base no contexto de visualização do cliente (por exemplo, mudar para uma resolução mais baixa ao exibir um bloco de visualização menor, uma visualização em grade ou o modo picture-in-picture).

Mudar a qualidade dinamicamente usando o atributo WebRtcLiveView gerencia especificamente a resolução da sessão de transmissão ao vivo ativa em um cliente específico. Isso não é o mesmo que definir uma configuração de uso de largura de banda para todo o dispositivo, o que afetaria todos os espectadores simultâneos e a qualidade das gravações de vídeo históricas salvas na nuvem.

O exemplo a seguir demonstra como recuperar e atualizar a qualidade da transmissão ao vivo de um dispositivo:

  • Recuperar opções de qualidade compatíveis: receba as resoluções de streaming disponíveis compatíveis com o dispositivo. O código consulta o atributo supportedQualityHints do atributo WebRtcLiveView no fluxo do tipo de dispositivo e expõe as qualidades compatíveis como um StateFlow que contém uma lista de valores QualityHint (como QUALITY_HINT_SD, QUALITY_HINT_HD, QUALITY_HINT_FHD, QUALITY_HINT_QHD ou QUALITY_HINT_UHD).

  • Mudar a qualidade da transmissão ao vivo: aplique um QualityHint selecionado para mudar a resolução da transmissão ao vivo ativa (por exemplo, mudar de definição padrão para alta definição). A função changeQuality resolve o atributo de transmissão ao vivo do dispositivo e chama changeLiveViewQuality com o mediaSessionId ativo e a configuração QualityHint escolhida.

// Assuming you have a HomeDevice instance 'device'
val availableQualityHints: StateFlow<List> =
    device.type(GoogleCameraDevice)
        .trait(WebRtcLiveView)
        .map { trait -> 
            trait?.supportedQualityHints ?: emptyList() 
        }
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())

// Assuming you have a HomeDevice instance 'device'
suspend fun changeQuality(mediaSessionId: String, qualityHint: QualityHint) {
    // Get the trait from the device
    val trait = device.type(GoogleCameraDevice).trait(WebRtcLiveView).first() ?: return
    try {
        trait.changeLiveViewQuality(mediaSessionId, qualityHint)
    } catch (e: Exception) {
        // Handle error
    }
}

Ativar e desativar o recurso de gravação

Para ativar o recurso de gravação da câmera, transmita TransportStatusEnum.Active para o método setTransportStatus() da característica PushAvStreamTransport. Para desativar o recurso de gravação, transmita TransportStatusEnum.Inactive. No exemplo a seguir, agrupamos essas chamadas em uma única chamada que usa um Boolean para alternar o recurso de gravação:

// Start or stop recording for all connections.
suspend fun setCameraRecording(trait: PushAvStreamTransport, isOn: Boolean) {
  if(isOn) {
    trait.setTransportStatus(TransportStatusEnum.Active)
  } else {
    trait.setTransportStatus(TransportStatusEnum.Inactive)
  }
}

Ativar ou desativar a capacidade de gravação da câmera é o mesmo que ligar ou desligar o vídeo da câmera. Quando o vídeo de uma câmera está ativado, ela está gravando (para fins de eventos e clipes relacionados).

Quando o recurso de gravação está desativado (o vídeo da câmera está desligado):

Verificar se o recurso de gravação está ativado

Para determinar se o recurso de gravação de uma câmera está ativado, verifique se há conexões ativas. O exemplo a seguir define duas funções para fazer isso:

// Get the on/off state
suspend fun onOffState(pushAvStreamTransport: PushAvStreamTransport) {
  return pushAvStreamTransport
    .currentConnections?.any { it.transportStatus == TransportStatusEnum.Active } ?: false
}

// Check if the camera's recording capability is enabled
fun PushAvStreamTransport.recordModeActive(): Boolean {
  return currentConnections?.any { it.transportStatus == TransportStatusEnum.Active } ?: false
}

Outra maneira de verificar é usando a função findTransport() com um predicado:

// Fetch the current connections
suspend fun queryRecordModeState(trait: PushAvStreamTransport) {
  return trait.findTransport().let {
      it.transportConfigurations.any { it.transportStatus == TransportStatusEnum.Active
    }
}

Recuperar capturas de tela da câmera

   Implementado no app de exemplo para Android   

Use a característica CameraSnapshot para recuperar capturas de tela estáticas de uma câmera. Esses snapshots são úteis em várias situações:

  • Como um bloco de marcador de posição na interface enquanto uma transmissão ao vivo do WebRTC está carregando.
  • Como uma imagem de prévia estática que se transforma em uma transmissão ao vivo quando o usuário interage com ela.
  • Como uma tela de fundo para desenhar e configurar Zonas de Atividade personalizadas.

O atributo CameraSnapshot oferece duas maneiras de buscar capturas de tela, dependendo se você precisa de uma captura de tela em cache para renderização rápida ou uma captura de tela ao vivo sob demanda.

Receber um snapshot de visualização em cache

Uma captura de tela de prévia é uma imagem em cache que o SDK pode recuperar sem ativar a câmera. Esse é o método mais rápido para buscar uma imagem estática e é ideal para páginas de visão geral da câmera ou grades de painel.

Para receber o snapshot de prévia, leia o atributo de atributo preview_image_url:

// Get a flow of the CameraSnapshot trait
val cameraSnapshotFlow: Flow<CameraSnapshot?> = cameraDevice
  .type(GoogleCameraDevice)
  .trait(CameraSnapshot)

// Get the current trait state
val cameraSnapshot = cameraSnapshotFlow.firstOrNull()
if (cameraSnapshot != null) {
  val previewImageUrl = cameraSnapshot.previewImageUrl
  // Load the preview image from the URL
} else {
  // CameraSnapshot trait not supported on this device
}

Dependendo da configuração e da atividade da câmera, o URL de visualização aponta para uma imagem em cache gerada de uma filmagem recente (o primeiro frame I do fragmento de vídeo mais recente) ou um snapshot enviado periodicamente pela câmera.

Para indicar a atualização da imagem, a nuvem do Google Home incorpora um cabeçalho X-Home-Image-Captured-At na imagem JPEG/PNG exibida, formatada em UTC. É possível analisar esse cabeçalho no lado do cliente para exibir a idade do snapshot em um app.

Receber um snapshot em tempo real on demand

Para jornadas do usuário que exigem as imagens ao vivo atuais da câmera, como a configuração de Zonas de Atividade, é possível solicitar uma captura de tela ao vivo. Acionar uma captura de tela instantânea instrui a câmera a ativar, capturar um novo frame e fazer upload dele para a nuvem.

Para solicitar um snapshot em tempo real, use o comando getLiveSnapshot():

try {
  val response = cameraSnapshot.getLiveSnapshot()
  val snapshotUrl = response.snapshotUrl
  // Load the live snapshot image from the URL
} catch (e: Exception) {
  Log.e("CameraSnapshot", "Failed to get live snapshot: ${e.message}")
}

Autenticar solicitações de snapshot

   Implementado no app de exemplo para Android   

Todos os URLs de captura de tela apontam para mídia segura e exigem um token OAuth 2.0 para o escopo da v2 das APIs Home. Para baixar e renderizar as imagens, o token OAuth precisa ser inserido como um token do portador no cabeçalho Authorization da solicitação HTTP.

O exemplo a seguir demonstra como configurar uma solicitação de rede segura para fazer o download de um snapshot:

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

/**
 * Downloads a snapshot from a URL using an OAuth access token.
 */
suspend fun loadSnapshotImage(urlString: String, oauthToken: String): Bitmap? =
  withContext(Dispatchers.IO) {
    var connection: HttpURLConnection? = null
    try {
      val url = URL(urlString)
      connection = url.openConnection() as HttpURLConnection
      // 1. Configure the HTTP GET request with the Bearer token
      connection.setRequestProperty("Authorization", "Bearer $oauthToken")

      // 2. Fetch the image data
      if (connection.responseCode == HttpURLConnection.HTTP_OK) {
        connection.inputStream.use { inputStream ->
          BitmapFactory.decodeStream(inputStream)
        }
      } else {
        null
      }
    } catch (e: IOException) {
      e.printStackTrace()
      null
    } finally {
      connection?.disconnect()
    }
  }

Configurações da bateria

Várias configurações de bateria podem ser controladas pelas APIs Home.

Definir a preferência de uso da bateria

A configuração do equilíbrio de energia permite definir a relação entre a duração da bateria e o desempenho de um dispositivo. É possível criar diferentes perfis de bateria, como "Estendido", "Equilibrado" e "Desempenho", e alternar entre eles.

Esse recurso é implementado atualizando o atributo currentEnergyBalance da característica EnergyPreference. O atributo aceita um índice inteiro que corresponde a um perfil específico definido na lista energyBalances do dispositivo (por exemplo, 0 para EXTENDED, 1 para BALANCED e 2 para PERFORMANCE).

Um valor null para currentEnergyBalance indica que o dispositivo está usando um perfil personalizado. Este é um estado somente leitura.

Veja a seguir um exemplo de estrutura que o atributo currentEnergyBalance vai usar, seguido pelo snippet de código real que usa o atributo.

// Example energyBalances list
energy_balances: [
  {
    step: 0,
    label: "EXTENDED"
  },
  {
    step: 50,
    label: "BALANCED"
  },
  {
    step: 100,
    label: "PERFORMANCE"
  }
]
// The index parameter must be within the UByte range (0-255).
suspend fun setEnergyBalance(trait: EnergyPreference, index: Int) {
  trait.update { setCurrentEnergyBalance(index.toUByte()) }
}

// Setting the battery usage to more recording ie performance
setEnergyBalance(energyPreference, 2)

Ativar a economia de bateria automática

Para configurar esse recurso, atualize o atributo currentLowPowerModeSensitivity da característica EnergyPreference. Esse atributo usa um índice para selecionar um nível de sensibilidade, em que 0 normalmente representa Disabled e 1 representa Enabled ou Automatic.

suspend fun setAutomaticBatterySaver(enable: Boolean, trait: EnergyPreference) {
  // 0 is Disabled, 1 is Enabled
  val value = if (enable) 1.toUByte() else 0.toUByte()
  trait.update { setCurrentLowPowerModeSensitivity(value) }
}

Verificar o estado de carregamento da bateria

Para saber o estado de carregamento atual do dispositivo (carregando, totalmente carregado ou não carregando), use o atributo batChargeState da característica PowerSource.

// Get the battery charging state
val batteryChargeState = powerSource.batChargeState

when (batteryChargeState) {
    PowerSourceTrait.BatChargeStateEnum.IsCharging -> "Charging"
    PowerSourceTrait.BatChargeStateEnum.IsAtFullCharge -> "Full"
    PowerSourceTrait.BatChargeStateEnum.IsNotCharging -> "Not Charging"
    else -> "Unknown"
}

Verificar o nível de bateria

Para conferir o Nível de bateria atual, use o atributo batChargeLevel da característica PowerSource. O nível é OK, Warning (baixo) ou Critical.

// Get the battery charge level
val batteryLevel = powerSourceTrait.batChargeLevel

when (batteryLevel) {
    PowerSourceTrait.BatChargeLevelEnum.OK -> "OK"
    PowerSourceTrait.BatChargeLevelEnum.Warning -> "Warning"
    PowerSourceTrait.BatChargeLevelEnum.Critical -> "Critical"
    else -> "Unknown"
}

Acessar a fonte de energia

Para determinar a fonte de energia que o dispositivo está usando, use os atributos BatPresent e wiredPresent da característica PowerSource.

  val trait: PowerSource
  val isWired = trait.wiredPresent
  val hasBattery = trait.batPresent

Verificar se a bateria precisa ser trocada

O atributo batReplaceability indica se a bateria não pode ser substituída, se pode ser substituída pelo usuário ou se pode ser substituída pela fábrica.

O atributo batReplacementNeeded da característica PowerSource informa se a bateria precisa ser substituída.

// Get the battery replacement status
val batteryStatus = powerSourceTrait.batReplacementNeeded

if (batteryStatus) {
  println("Replace the battery.")
}

Quando a bateria pode ser substituída pelo usuário, o batReplacementDescription a descreve, incluindo informações como formato, química e fabricante preferencial.

Configurações de áudio

Várias configurações de áudio podem ser controladas pelas APIs Home.

Ativar ou desativar o microfone

Para ativar ou desativar o microfone do dispositivo, atualize o atributo microphoneMuted da característica CameraAvStreamManagement usando a função integrada setMicrophoneMuted do Kotlin:

// Turn the device's microphone on or off
suspend fun turnOffMicrophone(disableMicrophone: Boolean, trait: CameraAvStreamManagement) {
  trait.update { setMicrophoneMuted(disableMicrophone) }
}

Ativar ou desativar a gravação de áudio

Para ativar ou desativar a gravação de áudio no dispositivo, atualize o atributo recordingMicrophoneMuted da característica CameraAvStreamManagement usando a função Kotlin setRecordingMicrophoneMuted integrada:

// Turn audio recording on or off for the device
suspend fun turnOffAudioRecording(disableAudioRecording: Boolean, trait: CameraAvStreamManagement) {
  trait.update { setRecordingMicrophoneMuted(disableAudioRecording) }
}

Ajustar o volume do alto-falante

Para ajustar o volume do alto-falante do dispositivo, atualize o atributo speakerVolumeLevel da característica CameraAvStreamManagement usando a função integrada setSpeakerVolumeLevel do Kotlin:

// Adjust the camera speaker volume
suspend fun adjustSpeakerVolume(volume: Int, trait: CameraAvStreamManagement) {
  trait.update { setSpeakerVolumeLevel(volume.toUbyte()) }
}

Configurações de Zonas de Atividade

   Implementado no app de exemplo para Android   

A característica ZoneManagement fornece uma interface para gerenciar regiões de interesse personalizadas (zonas de atividade) em dispositivos de câmera e campainha. Essas zonas são usadas para filtrar a detecção de eventos (como movimento de pessoas ou veículos) para áreas específicas no campo de visão do dispositivo.

As zonas de atividade são configuradas pelo usuário em um aplicativo de parceiro, permitindo que ele desenhe zonas sobre áreas específicas no campo de visão da câmera. Essas zonas definidas pelo usuário são traduzidas para as estruturas usadas por essa característica. Para mais informações sobre como as Zonas de Atividade funcionam, consulte Configurar e usar as Zonas de Atividade.

As zonas de atividade geralmente são definidas usando coordenadas cartesianas 2D. O atributo fornece o TwoDCartesianVertexStruct para vértices e o TwoDCartesianZoneStruct para a definição da zona (nome, vértices, cor e uso).

Verificar as Zonas de Atividade

Para exibir as zonas de atividade, verifique o atributo zones da característica ZoneManagement.

// 1. Obtain the trait flow from the device
private val zoneManagementFlow: Flow =
  device.type(CAMERA_TYPE).flatMapLatest { it.trait(ZoneManagement) }

// 2. Map the flow to the list of zone structures
val activityZones: Flow<List<ZoneManagementTrait.ZoneInformationStruct>> =
  zoneManagementFlow.map { trait ->
    trait.zones ?: emptyList()
  }

Adicionar uma Zona de Atividade

Para criar uma zona, use o comando createTwoDCartesianZone. Esse comando usa um TwoDCartesianZoneStruct, que define o nome, os vértices, a cor e o uso da zona.

O exemplo a seguir mostra como criar uma zona chamada "Front Porch" com quatro vértices, na cor salmão (#F439A0), usada para detecção de movimento.

import com.google.home.google.ZoneManagement
import com.google.home.google.ZoneManagementTrait
import com.google.home.matter.serialization.OptionalValue

/**
 * Creates a custom activity zone named "Front Porch" with a salmon color
 * configured for motion detection.
 */
suspend fun createFrontPorchZone(zoneManagement: ZoneManagement) {
  // 1. Define the vertices for the zone (2D Cartesian coordinates)
  // Values are typically scaled to a maximum defined by the device's twoDCartesianMax attribute.
  val vertices =
    listOf(
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 260u, y = 422u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 1049u, y = 0u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 2048u, y = 0u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 2048u, y = 950u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 1630u, y = 1349u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 880u, y = 2048u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 0u, y = 2048u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 638u, y = 1090u)
    )

  // 2. Define the zone structure
  val newZone =
    ZoneManagementTrait.TwoDCartesianZoneStruct(
      name = "Front Porch",
      vertices = vertices,
      // Usage defines what the zone filters (for example, Motion, Person, Vehicle)
      use = listOf(ZoneManagementTrait.ZoneUseEnum.Motion),
      // Color is typically a hex string (for example, Salmon/Pink)
      color = OptionalValue.present("#F439A0")
    )

  try {
    // 3. Execute the command to add the zone to the device
    zoneManagement.createTwoDCartesianZone(newZone)
    println("Successfully created activity zone.")
  } catch (e: Exception) {
    // Handle potential HomeException or Timeout
    println("Failed to create activity zone: ${e.message}")
  }
}

Atualizar uma Zona de Atividade

Para atualizar uma zona, use o comando updateTwoDCartesianZone. Esse comando requer o zoneId e o TwoDCartesianZoneStruct atualizado.

private suspend fun ZoneManagement.updateZone(
  zoneId: UShort,
  zone: ZoneManagementTrait.TwoDCartesianZoneStruct
) {
  // Execute the command to update the zone
  this.updateTwoDCartesianZone(zoneId = zoneId, zone = zone)
}

Excluir uma Zona de Atividade

Para remover uma zona, use o comando removeZone com o zoneId específico.

private suspend fun ZoneManagement.deleteZone(zoneId: UShort) {
  // Execute the command to remove the zone
  this.removeZone(zoneId = zoneId)
}

Gatilhos de detecção de som

O atributo AvStreamAnalysis fornece uma interface para gerenciar acionadores de detecção de eventos em dispositivos de câmera e campainha. Enquanto os acionadores baseados em visão (como pessoas ou veículos) podem ser específicos da zona, os acionadores relacionados ao som normalmente são configurações no nível do dispositivo.

Os seguintes tipos de acionador estão disponíveis para detecção de som com a EventTriggerTypeEnum:

Modo Valor de tipo enumerado Descrição
Som Sound Detecção de som geral.
Pessoa falando PersonTalking Detecta fala.
Latido de cachorro DogBark Detecta vocalizações caninas.
Vidro quebrando GlassBreak Detecta o som de vidro quebrando.
Detector de fumaça SmokeAlarm Detecta detectores de fumaça, geralmente reconhecidos pelo padrão sonoro T3 (três bipes curtos seguidos por uma pausa).
Alarme de monóxido de carbono CoAlarm Detecta alarmes de monóxido de carbono (CO), normalmente reconhecidos pelo padrão sonoro T4 (quatro bipes curtos seguidos por uma pausa).

Verificar o status da detecção de som

Para mostrar o estado atual da detecção de som ao usuário, verifique o que o dispositivo aceita e o que está ativado pelo hardware do dispositivo. Os dois atributos a serem verificados são:

No desenvolvimento para Android usando fluxos do Kotlin, normalmente você observa o atributo AvStreamAnalysis do HomeDevice.

// Example structure to store the
data class EventTriggerAttribute(val type: EventTriggerTypeEnum, val enabled: Boolean)

// 1. Obtain the trait flow from the device
private val avStreamAnalysisFlow: Flow<AvStreamAnalysis> =
  device.traitFromType(AvStreamAnalysis, CAMERA_TYPES.first { device.has(it) })

// 2. Map the flow to a list of sound event attributes
val soundEventTriggersState: Flow<List<EventTriggerAttribute>> =
  avStreamAnalysisFlow.map { trait ->
    // Get raw lists from the trait attributes
    val supported = trait.supportedEventTriggers ?: emptyList()
    val enabled = trait.enabledEventTriggers ?: emptyList()

    // Define sound-specific triggers to filter for
    val soundTypes = setOf(
      EventTriggerTypeEnum.Sound,
      EventTriggerTypeEnum.PersonTalking,
      EventTriggerTypeEnum.DogBark,
      EventTriggerTypeEnum.GlassBreak,
      EventTriggerTypeEnum.SmokeAlarm,
      EventTriggerTypeEnum.CoAlarm,
    )

    // Filter and associate status
    supported
      .filter { soundTypes.contains(it) }
      .map { type ->
        EventTriggerAttribute(
          type = type,
          enabled = enabled.contains(type)
        )
      }
  }

Atualizar o conjunto de gatilhos ativados

Para atualizar o conjunto de gatilhos ativados, use o comando SetOrUpdateEventDetectionTriggers, que usa uma lista de estruturas EventTriggerEnablement.

private suspend fun AvStreamAnalysis.updateEventTriggers(
  eventTriggers: List<EventTriggerAttribute>
) {
  val toUpdate = eventTriggers.map {
    EventTriggerEnablement(
      eventTriggerType = it.type,
      enablementStatus = if (it.enabled) {
        EnablementStatusEnum.Enabled
      } else {
        EnablementStatusEnum.Disabled
      },
    )
  }

  // Execute the command on the device
  setOrUpdateEventDetectionTriggers(toUpdate)
}

Modos de gravação

   Implementado no app de exemplo para Android   

A característica RecordingMode fornece uma interface para gerenciar o comportamento de gravação de vídeo e imagem em dispositivos de câmera e campainha. Ele permite que os usuários escolham entre gravação contínua, gravação baseada em eventos ou desativação total da gravação (somente imagens ao vivo).

O RecordingModeEnum define as estratégias de gravação disponíveis:

Modo Valor de tipo enumerado Descrição
Desativada Disabled A gravação está totalmente desativada. Usado principalmente por dispositivos legados.
CVR (gravação contínua de vídeo) Cvr O vídeo é gravado 24 horas por dia, 7 dias por semana. Requer uma assinatura (por exemplo, Google Home Premium.
EBR (gravação baseada em eventos) Ebr A gravação é acionada por eventos (pessoa, movimento). A duração do vídeo depende da duração do evento e da assinatura.
ETR (gravação acionada por evento) Etr Gravação de prévia curta (por exemplo, 10 segundos) acionada por eventos.
Live View LiveView A gravação está desativada, mas os usuários ainda podem acessar a transmissão ao vivo.
Imagens estáticas Images As capturas de tela são gravadas em vez de vídeos quando ocorrem eventos.

Verificar modos de gravação

Para exibir a configuração de gravação atual, verifique os atributos da característica RecordingMode:

// 1. Obtain the trait flow from the device
private val recordingModeTraitFlow: Flow =
    device.traitFromType(RecordingMode, CAMERA_TYPES.first { device.has(it) })

// 2. Map the flow to recording mode options
data class RecordingModeOptions(
    val recordingMode: RecordingModeTrait.RecordingModeEnum,
    val index: Int,
    val available: Boolean,
    val readableString: String,
)

private val recordingModeOptions: Flow<List> =
    recordingModeTraitFlow.map { trait ->
        val supported = trait.supportedRecordingModes?.map { it.recordingMode } ?: emptyList()
        val available = trait.availableRecordingModes?.map { it.toInt() } ?: emptyList()

        supported.withIndex().map { (index, mode) ->
            RecordingModeOptions(
                recordingMode = mode,
                index = index,
                available = available.contains(index),
                readableString = mode.toReadableString(),
            )
        }
    }

Mudar o modo de gravação

Antes de atualizar, verifique se o índice escolhido do atributo supportedRecordingModes está presente no atributo availableRecordingModes.

Para atualizar o modo selecionado, use a função setSelectedRecordingMode, transmitindo o índice do modo escolhido:

private suspend fun RecordingMode.updateRecordingMode(index: Int) {
    // Execute the command to update the selected mode
    this.setSelectedRecordingMode(index.toUByte())
}

Outras configurações

Várias outras configurações podem ser controladas pelas APIs Home.

Mudar a orientação da imagem

A orientação da imagem da câmera (vídeo) pode ser girada. O vídeo só pode ser girado em 180 graus.

Para mudar a orientação da imagem da câmera, atualize o atributo imageRotation do atributo CameraAvStreamManagement usando a função Kotlin setImageRotation integrada:

// Change the camera's image orientation
val isRotated = false

cameraAvStreamManagement.update { setImageRotation(if (isRotated) 180.toUShort() else 0.toUShort()) }

Ativar ou desativar a Visão Noturna

Para ativar ou desativar a visão noturna da câmera, use TriStateAutoEnum para atualizar o atributo nightVision da característica CameraAvStreamManagement usando a função integrada setNightVision do Kotlin:

// Turn night vision on
cameraAvStreamManagement.update {
  setNightVision(CameraAvStreamManagementTrait.TriStateAutoEnum.On)
}

// Turn night vision off
CameraAvStreamManagement.update {
  setNightVision(CameraAvStreamManagementTrait.TriStateAutoEnum.Off)
}

Mudar o brilho do LED de status

Para mudar o brilho do LED de status, use ThreeLevelAutoEnum para atualizar o atributo statusLightBrightness da característica CameraAvStreamManagement usando a função integrada setStatusLightBrightness do Kotlin:

// Set the LED brightness to high
cameraAvStreamManagement.update {
  setStatusLightBrightness(CameraAvStreamManagementTrait.ThreeLevelAutoEnum.High)
}

// Set the LED brightness to low
cameraAvStreamManagement.update {
  setStatusLightBrightness(CameraAvStreamManagementTrait.ThreeLevelAutoEnum.Low)
}

Mudar a janela de visualização da câmera

A janela de visualização da câmera é igual ao recurso de zoom e corte descrito no artigo de suporte Zoom e modo Enhance no vídeo da câmera Nest.

A janela de visualização é definida em um ViewportStruct que contém quatro valores, que são usados como as coordenadas da janela de visualização. As coordenadas são definidas como:

(x1,y1) -- (x2,y1)
   |          |
(x1,y2) -- (x2,y2)

A determinação dos valores para o ViewportStruct depende da interface do app e da implementação da câmera. Em um nível muito básico, para definir a janela de visualização do vídeo da câmera, atualize o atributo viewport do atributo CameraAvStreamManagement com um ViewportStruct, usando a função integrada setViewport do Kotlin:

cameraAvStreamManagement
  .update { setViewport(
    CameraAvStreamManagementTrait.ViewportStruct(
      x1 = horizontalRange.rangeStart.roundToInt().toUShort(),
      x2 = horizontalRange.rangeEnd.roundToInt().toUShort(),
      y1 = verticalRange.rangeStart.roundToInt().toUShort(),
      y2 = verticalRange.rangeEnd.roundToInt().toUShort(),
    )
) }

Ativar ou desativar as análises

Cada dispositivo pode ativar individualmente o envio de dados de análise detalhados para a nuvem do Google Home (consulte Cloud Monitoring para APIs Home).

Para ativar as análises em um dispositivo, defina a propriedade analyticsEnabled do ExtendedGeneralDiagnosticsTrait como true. Quando você define analyticsEnabled, outra propriedade, logUploadEnabled, é definida automaticamente como true, o que permite que os arquivos de registro de análise sejam enviados para a nuvem do Google Home.

// Enable analytics
extendedGeneralDiagnostics.update {
  setAnalyticsEnabled(true)
}

// Disable analytics
extendedGeneralDiagnostics.update {
  setAnalyticsEnabled(false)
}

Configurações de transporte e gravação

Esta seção aborda as configurações relacionadas à qualidade do stream da câmera e ao acionamento de eventos. Essas configurações são gerenciadas pela característica PushAvStreamTransport.

Ler configurações de transporte

Nesta seção, mostramos como recuperar a configuração atual de uma câmera ou campainha. Ele busca a característica PushAvStreamTransport, encontra a conexão específica usada para gravação e extrai os valores atuais de qualidade da largura de banda, sensibilidade de ativação e duração máxima do evento.

val trait: PushAvStreamTransport = device.getTrait(PushAvStreamTransport)
val connections = trait.findTransport().transportConfigurations

// Locate the connection designated for recording
val recordingConnection = connections.firstOrNull {
    it.transportOptions.getOrNull()?.streamUsage == StreamUsageEnum.Recording
}

val options = recordingConnection?.transportOptions?.getOrNull()

// 1. Bandwidth Quality (Video Stream ID)
val videoStreamId = options?.videoStreamId?.getOrNull()

// 2. Wake-up Sensitivity (Motion Sensitivity)
val wakeUpSensitivity = options?.triggerOptions?.motionSensitivity?.getOrNull()

// 3. Max Event Length (Motion Trigger Time Control)
val maxEventLength = options?.triggerOptions?.motionTimeControl?.getOrNull()?.maxDuration

Atualizar as configurações de transporte

Nesta seção, mostramos como mudar as configurações de transporte. Ele cria um novo TransportOptionsStruct com os novos valores e usa o comando modifyPushTransport para enviar essas configurações atualizadas de volta ao dispositivo, aplicando-as à conexão de gravação encontrada na etapa anterior.

Para modificar essas configurações, use o comando modifyPushTransport com um TransportOptionsStruct.

val toUpdate = TransportOptionsStruct(
    videoStreamId = OptionalValue.present(2u), // e.g., Max Quality
    triggerOptions = TransportTriggerOptionsStruct(
        motionSensitivity = OptionalValue.present(5u), // e.g., Medium
        motionTimeControl = OptionalValue.present(
            TransportMotionTriggerTimeControlStruct(maxDuration = 30u)
        )
    )
)

if (recordingConnection != null) {
    trait.modifyPushTransport(
        connectionId = recordingConnection.connectionId,
        transportOptions = toUpdate
    )
}

Determinar a qualidade da largura de banda

A propriedade videoStreamId da TransportOptionsStruct corresponde a uma configuração específica de stream de vídeo.

Para acessar os streams de vídeo compatíveis, consulte o atributo allocatedVideoStreams, que é uma lista de VideoStreamStructs. do atributo CameraAvStreamManagement do dispositivo.

Ajustar a sensibilidade de ativação do dispositivo

A propriedade motionSensitivity de TransportTriggerOptionsStruct corresponde aos seguintes valores:

Marcador Valor (UByte)
Baixo 1u
Médio 5u
Alta 10u

Ajustar a duração máxima de eventos

A propriedade maxDuration de TransportMotionTriggerTimeControlStruct corresponde às seguintes durações (em segundos):

  • 10u, 15u, 30u, 60u, 120u, 180u

Desativar um dispositivo

Para remover uma câmera de uma estrutura, chame device.decommissionDevice(). A desativação mostra ao usuário uma tela de confirmação gerenciada pelo Google avisando que todo o histórico de vídeo 24 horas, o histórico de eventos, os clipes e os dados da biblioteca de rostos conhecidos serão excluídos permanentemente de toda a estrutura.

Para instruções detalhadas e exemplos de código, consulte Remover um dispositivo.