Guida ai dispositivi di ripresa per Android

Il tipo di dispositivo Fotocamera viene implementato utilizzando tre tratti: PushAvStreamTransport, che gestisce il trasporto di stream audio e video utilizzando protocolli basati sul push, WebRtcLiveView, che offre la possibilità di controllare le live streaming e la funzionalità TalkBack e CameraSnapshot, che fornisce snapshot di anteprima memorizzati nella cache e acquisizioni di snapshot live.

Prima di utilizzare qualsiasi funzionalità o tentare di aggiornare gli attributi, controlla sempre che il dispositivo supporti gli attributi e i comandi. Per saperne di più, consulta Controllare i dispositivi su Android.

Tipo di dispositivo delle API Home Tratti App di esempio Kotlin Caso d'uso

Fotocamera

GoogleCameraDevice

home.matter.6006.types.0158

Un dispositivo che acquisisce immagini fisse o video. Le videocamere possono includere live streaming accessibili, conversazione bidirezionale o eventi di rilevamento.

Tratti obbligatori
     google PushAvStreamTransport
     google WebRtcLiveView

Fotocamera

Ottenere informazioni di base su un dispositivo

   Implementato nell'app di esempio per Android   

La caratteristica BasicInformation include informazioni quali nome del fornitore, ID fornitore, ID prodotto, nome prodotto (incluse informazioni sul modello) e versione software per un 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}")
    }

Recupera il numero di serie

Per ottenere il numero di serie del dispositivo, utilizza il comando GetSerialNumber del tratto ExtendedBasicInformation. L'esempio mostra il salvataggio del numero di serie in una variabile denominata serialNumber:

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

Visualizzare l'ultima volta che il dispositivo ha contattato il cloud

Per trovare l'ultima volta in cui il dispositivo ha avuto contatti con il cloud, utilizza l'attributo lastContactTimestamp del tratto ExtendedGeneralDiagnostics:

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

Impostazioni del tipo di supporto per videocamera

L'attributo Mount contiene le impostazioni di montaggio della videocamera e le informazioni sullo stato. Puoi leggere attributi come lo stato di montaggio, il tipo di rilevamento e il nome del tipo di montaggio. Inoltre, puoi utilizzare l'attributo Mount per ignorare la configurazione predefinita del tipo di supporto.

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

Controllare la connettività di un dispositivo

La connettività di un dispositivo viene effettivamente controllata a livello di tipo di dispositivo perché alcuni dispositivi supportano più tipi di dispositivi. Lo stato restituito è una combinazione degli stati di connettività per tutte le caratteristiche del dispositivo.

val cameraConnectivity = cameraDevice.metadata.sourceConnectivity.connectivityState

In caso di tipi di dispositivi misti in assenza di connettività a internet, potrebbe essere osservato uno stato di PARTIALLY_ONLINE. Le caratteristiche standard di Matter potrebbero essere ancora online a causa del routing locale, ma le caratteristiche basate sul cloud saranno offline.

Ottenere l'indirizzo IP del dispositivo

Per trovare l'indirizzo IP del dispositivo, utilizza l'attributo networkInterfaces del tratto GeneralDiagnostics. Gli indirizzi vengono restituiti come array di byte, che puoi formattare in stringhe IPv4 o IPv6 standard:

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()

Avviare un live streaming

   Implementato nell'app di esempio per Android   

Per avviare un live streaming, invia la stringa Session Description Protocol (SDP) al metodo WebRtcLiveView dell'attributo startLiveView(), che restituisce un WebRtcLiveViewTrait.StartLiveViewCommand.Response contenente tre valori:

  • L'SDP per la sessione.
  • La durata della sessione in secondi.
  • L'ID sessione, che può essere utilizzato per estendere o terminare la sessione.
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)

Estendere un live streaming

   Implementato nell'app di esempio per Android   

I live streaming hanno una durata preimpostata dopo la quale scadono. Per estendere la durata di uno stream attivo, invia una richiesta di estensione utilizzando il metodo 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
}

Avviare e interrompere TalkBack

   Implementato nell'app di esempio per Android   

Per avviare TalkBack, chiama il metodo WebRtcLiveView startTalkback() dell'intent. Per interrompere, utilizza 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)
  }
}

Gestire il live streaming

La regolazione della qualità dello stream live è utile per ottimizzare l'utilizzo della larghezza di banda in base al contesto di visualizzazione del client (ad esempio, il passaggio a una risoluzione inferiore quando viene visualizzata una miniatura di anteprima più piccola, la visualizzazione a griglia o la modalità Picture in picture).

La modifica dinamica della qualità utilizzando l'attributo WebRtcLiveView gestisce in modo specifico la risoluzione della sessione di live streaming attiva su un determinato client. Non è la stessa cosa di configurare un'impostazione di utilizzo della larghezza di banda a livello di dispositivo direttamente sul dispositivo, il che influirebbe su tutti gli spettatori simultanei e sulla qualità delle registrazioni video storiche salvate sul cloud.

L'esempio seguente mostra come recuperare e aggiornare la qualità dello streaming live per un dispositivo:

  • Recupera le opzioni di qualità supportate: ottieni le risoluzioni di streaming disponibili supportate dal dispositivo. Il codice esegue query sull'attributo supportedQualityHints del tratto WebRtcLiveView nel flusso del tipo di dispositivo ed espone le qualità supportate come StateFlow contenente un elenco di valori QualityHint (ad esempio QUALITY_HINT_SD, QUALITY_HINT_HD, QUALITY_HINT_FHD, QUALITY_HINT_QHD o QUALITY_HINT_UHD).

  • Modifica la qualità del live streaming: applica un QualityHint selezionato per modificare la risoluzione dello streaming del live streaming attivo (ad esempio, passando dalla definizione standard all'alta definizione). La funzione changeQuality risolve la caratteristica di live streaming del dispositivo e chiama changeLiveViewQuality con la configurazione mediaSessionId attiva e la configurazione QualityHint scelta.

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

Attivare e disattivare la funzionalità di registrazione

Per attivare la funzionalità di registrazione della videocamera, passa TransportStatusEnum.Active al metodo PushAvStreamTransport setTransportStatus() del tratto. Per disattivare la funzionalità di registrazione, passa TransportStatusEnum.Inactive. Nell'esempio seguente, racchiudiamo queste chiamate in una singola chiamata che utilizza un Boolean per attivare/disattivare la funzionalità di registrazione:

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

L'attivazione o la disattivazione della funzionalità di registrazione della videocamera equivale ad attivare o disattivare il video della videocamera. Quando il video di una videocamera è attivo, la videocamera registra (ai fini degli eventi e dei clip correlati).

Quando la funzionalità di registrazione è disattivata (il video della videocamera è disattivato):

Controllare se la funzionalità di registrazione è abilitata

Per determinare se la funzionalità di registrazione di una videocamera è attiva, controlla se sono attive connessioni. L'esempio seguente definisce due funzioni per eseguire questa operazione:

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

Un altro modo per verificare è utilizzare la funzione findTransport() con un predicato:

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

Recuperare gli snapshot della videocamera

   Implementato nell'app di esempio per Android   

Utilizza il tratto CameraSnapshot per recuperare istantanee di immagini statiche da una videocamera. Questi snapshot sono utili per diversi scenari:

  • Come riquadro segnaposto nell'interfaccia utente durante il caricamento di uno stream live WebRTC.
  • Come immagine di anteprima statica che viene aggiornata a un live streaming quando l'utente interagisce.
  • Come sfondo per disegnare e configurare zone di attività personalizzate.

Il tratto CameraSnapshot offre due modi per recuperare gli snapshot, a seconda che tu abbia bisogno di uno snapshot memorizzato nella cache per un rendering rapido o di uno snapshot live on demand.

Visualizzare un'istantanea dell'anteprima memorizzata nella cache

Un'istantanea di anteprima è un'immagine memorizzata nella cache che l'SDK può recuperare senza attivare la videocamera. Questo è il metodo più rapido per recuperare un'immagine statica ed è ideale per le pagine di panoramica della videocamera o le griglie della dashboard.

Per ottenere lo snapshot dell'anteprima, leggi l'attributo del tratto 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
}

A seconda della configurazione e dell'attività della videocamera, l'URL di anteprima punta a un'immagine memorizzata nella cache generata da filmati recenti (il primo I-frame dell'ultimo frammento video) o a un'istantanea caricata periodicamente dalla videocamera.

Per indicare la freschezza dell'immagine, il cloud Google Home incorpora un'intestazione X-Home-Image-Captured-At nell'immagine JPEG/PNG pubblicata, formattata in formato UTC. Puoi analizzare questo header sul lato client per visualizzare l'età dello snapshot in un'app.

Ottenere uno snapshot live on demand

Per i percorsi utente che richiedono la visione in diretta attuale della videocamera, ad esempio la configurazione delle zone attive, puoi richiedere un'istantanea in diretta. L'attivazione di un'istantanea live indica alla videocamera di riattivarsi, acquisire un nuovo frame e caricarlo sul cloud.

Per richiedere uno snapshot live, utilizza il 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}")
}

Autenticare le richieste di snapshot

   Implementato nell'app di esempio per Android   

Tutti gli URL degli snapshot rimandano a contenuti multimediali protetti e richiedono un token OAuth 2.0 per l'ambito delle API Home v2. Per scaricare e visualizzare il rendering delle immagini, il token OAuth deve essere inserito come token Bearer nell'intestazione Authorization della richiesta HTTP.

L'esempio seguente mostra come configurare una richiesta di rete sicura per scaricare uno 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()
    }
  }

Impostazioni della batteria

Diverse impostazioni della batteria possono essere controllate tramite le API Home.

Impostare la preferenza di utilizzo della batteria

L'impostazione del bilanciamento energetico consente di configurare il compromesso tra durata della batteria e prestazioni di un dispositivo. Puoi creare diversi profili batteria, ad esempio "Esteso", "Bilanciato" e "Prestazioni", e passare da uno all'altro.

Questa funzionalità viene implementata aggiornando l'attributo currentEnergyBalance del tratto EnergyPreference. L'attributo accetta un indice intero che corrisponde a un profilo specifico definito nell'elenco energyBalances del dispositivo (ad esempio, 0 per EXTENDED, 1 per BALANCED e 2 per PERFORMANCE).

Un valore null per currentEnergyBalance indica che il dispositivo utilizza un profilo personalizzato. Questo è uno stato di sola lettura.

Di seguito è riportato un esempio di struttura che utilizzerà l'attributo currentEnergyBalance, seguito dallo snippet di codice effettivo che utilizza l'attributo.

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

Attivare il risparmio energetico automatico

Per configurare questa funzionalità, aggiorna l'attributo currentLowPowerModeSensitivity del tratto EnergyPreference. Questo attributo utilizza un indice per selezionare un livello di sensibilità, dove 0 in genere rappresenta Disabled e 1 rappresenta Enabled o 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) }
}

Ottenere lo stato di ricarica della batteria

Per ottenere lo stato di ricarica attuale del dispositivo (in ricarica, completamente carico o non in ricarica), utilizza l'attributo batChargeState del tratto 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"
}

Ottenere il livello batteria

Per ottenere il livello attuale della batteria, utilizza l'attributo batChargeLevel del tratto PowerSource. Il livello è OK, Warning (basso) o 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"
}

Recupera la fonte di alimentazione

Per determinare la fonte di alimentazione utilizzata dal dispositivo, utilizza gli attributi BatPresent e wiredPresent del tratto PowerSource.

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

Controlla se è necessario sostituire la batteria.

L'attributo batReplaceability indica se la batteria non è sostituibile, è sostituibile dall'utente o è sostituibile in fabbrica.

L'attributo batReplacementNeeded del tratto PowerSource indica se è necessario sostituire la batteria.

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

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

Quando la batteria è sostituibile dall'utente, il batReplacementDescription descrive la batteria, incluse informazioni quali fattore di forma, composizione chimica e produttore preferito.

Impostazioni audio

Diverse impostazioni audio possono essere controllate tramite le API Home.

Attivare o disattivare il microfono

Per attivare o disattivare il microfono del dispositivo, aggiorna l'attributo microphoneMuted del tratto CameraAvStreamManagement utilizzando la funzione Kotlin setMicrophoneMuted integrata:

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

Attivare o disattivare la registrazione audio

Per attivare o disattivare la registrazione audio per il dispositivo, aggiorna l'attributo recordingMicrophoneMuted del tratto CameraAvStreamManagement utilizzando la funzione Kotlin setRecordingMicrophoneMuted integrata:

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

Regolare il volume dello speaker

Per regolare il volume dello speaker per il dispositivo, aggiorna l'attributo speakerVolumeLevel del tratto CameraAvStreamManagement utilizzando la funzione setSpeakerVolumeLevel Kotlin integrata:

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

Impostazioni delle zone attive

   Implementato nell'app di esempio per Android   

La caratteristica ZoneManagement fornisce un'interfaccia per la gestione di regioni di interesse personalizzate (zone attive) su videocamere e campanelli. Queste zone vengono utilizzate per filtrare il rilevamento di eventi (ad esempio movimento di persone o veicoli) in aree specifiche del campo visivo del dispositivo.

Le zone attive vengono configurate dall'utente all'interno di un'applicazione partner, consentendogli di disegnare zone su aree specifiche del campo visivo della videocamera. Queste zone definite dall'utente vengono poi tradotte nelle strutture utilizzate da questo tratto. Per saperne di più su come funzionano le zone attive, consulta Configurare e usare le zone attive.

Le zone attive vengono in genere definite utilizzando coordinate cartesiane 2D. Il tratto fornisce TwoDCartesianVertexStruct per i vertici e TwoDCartesianZoneStruct per la definizione della zona (nome, vertici, colore e utilizzo).

Controllare le zone attive

Per visualizzare le zone attive, seleziona l'attributo zones del tratto 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()
  }

Aggiungere una zona attiva

Per creare una nuova zona, utilizza il comando createTwoDCartesianZone. Questo comando accetta un TwoDCartesianZoneStruct, che definisce il nome, i vertici, il colore e l'utilizzo della zona.

L'esempio seguente mostra come creare una zona denominata "Veranda" con quattro vertici, color salmone (#F439A0) e utilizzata per il rilevamento del 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}")
  }
}

Aggiornare una zona attiva

Per aggiornare una zona esistente, utilizza il comando updateTwoDCartesianZone. Questo comando richiede zoneId e l'TwoDCartesianZoneStruct aggiornato.

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

Eliminare una zona attiva

Per rimuovere una zona, utilizza il comando removeZone con il zoneId specifico.

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

Trigger evento sonoro

La caratteristica AvStreamAnalysis fornisce un'interfaccia per la gestione dei trigger di rilevamento degli eventi su videocamere e campanelli. Mentre i trigger basati sulla visione (come persone o veicoli) possono essere specifici per zona, i trigger correlati al suono sono in genere configurazioni a livello di dispositivo.

Per il rilevamento dei suoni con EventTriggerTypeEnum sono disponibili i seguenti tipi di attivatori:

Modalità Valore enum Descrizione
Suono Sound Rilevamento dei suoni generico.
Persona che parla PersonTalking Rileva la voce.
Abbaiare di cane DogBark Rileva le vocalizzazioni canine.
Rumore di vetri infranti GlassBreak Rileva il rumore di vetri infranti.
Allarme fumo SmokeAlarm Rileva gli allarmi fumo, spesso riconosciuti dal pattern sonoro T3 (tre brevi segnali acustici seguiti da una pausa).
Allarme CO CoAlarm Rileva gli allarmi di monossido di carbonio (CO), in genere riconosciuti dal pattern acustico T4 (quattro brevi segnali acustici seguiti da una pausa).

Controllare lo stato del rilevamento dei suoni

Per mostrare all'utente lo stato attuale del rilevamento del suono, devi verificare cosa supporta il dispositivo e cosa è abilitato dall'hardware del dispositivo. I due attributi da controllare sono:

Nello sviluppo Android utilizzando Kotlin Flows, in genere si osserva il tratto AvStreamAnalysis da 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)
        )
      }
  }

Aggiorna l'insieme di trigger abilitati

Per aggiornare l'insieme di trigger abilitati, utilizza il comando SetOrUpdateEventDetectionTriggers, che accetta un elenco di strutture 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)
}

Modalità di registrazione

   Implementato nell'app di esempio per Android   

La caratteristica RecordingMode fornisce un'interfaccia per la gestione del comportamento di registrazione di video e immagini su videocamere e campanelli. Consente agli utenti di scegliere tra registrazione continua, registrazione basata sugli eventi o disattivazione completa della registrazione (solo Live View).

RecordingModeEnum definisce le strategie di registrazione disponibili:

Modalità Valore enum Descrizione
Disabilitata Disabled La registrazione è completamente disattivata. Utilizzato principalmente dai dispositivi legacy.
CVR (registrazione video continua) Cvr Il video viene registrato 24 ore su 24, 7 giorni su 7. Richiede un abbonamento (ad esempio, Google Home Premium).
EBR (registrazione basata sugli eventi) Ebr La registrazione viene attivata da eventi (persona, movimento). La durata del video dipende dalla durata dell'evento e dall'abbonamento.
ETR (registrazione attivata da eventi) Etr Registrazione di un'anteprima breve (ad esempio, 10 secondi) attivata da eventi.
Live View LiveView La registrazione è disattivata, ma gli utenti possono comunque accedere allo streaming live.
Immagini statiche Images Quando si verificano eventi, vengono registrate istantanee anziché video.

Controllare le modalità di registrazione

Per visualizzare la configurazione di registrazione attuale, controlla gli attributi del tratto 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(),
            )
        }
    }

Modificare la modalità di registrazione

Prima dell'aggiornamento, assicurati che l'indice scelto dall'attributo supportedRecordingModes sia presente nell'attributo availableRecordingModes.

Per aggiornare la modalità selezionata, utilizza la funzione setSelectedRecordingMode, passando l'indice della modalità scelta:

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

Altre impostazioni

Varie altre impostazioni possono essere controllate tramite le API Home.

Modificare l'orientamento dell'immagine

L'orientamento dell'immagine (video) della videocamera può essere ruotato. Il video può essere ruotato solo di 180 gradi.

Per modificare l'orientamento dell'immagine della videocamera, aggiorna l'attributo imageRotation del tratto CameraAvStreamManagement utilizzando la funzione setImageRotation Kotlin integrata:

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

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

Attivare o disattivare la visione notturna

Per attivare o disattivare la visione notturna per la videocamera, utilizza TriStateAutoEnum per aggiornare l'attributo nightVision del tratto CameraAvStreamManagement utilizzando la funzione Kotlin setNightVision integrata:

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

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

Modificare la luminosità del LED di stato

Per modificare la luminosità del LED di stato, utilizza ThreeLevelAutoEnum per aggiornare l'attributo statusLightBrightness del tratto CameraAvStreamManagement utilizzando la funzione Kotlin setStatusLightBrightness integrata:

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

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

Modificare il riquadro della videocamera

La finestra della videocamera è la stessa della funzionalità Zoom e ritaglio descritta nell'articolo del Centro assistenza Zoom e miglioramento dei video della videocamera Nest.

La finestra è definita in un ViewportStruct che contiene quattro valori, che vengono utilizzati come coordinate della finestra. Le coordinate sono definite come segue:

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

La determinazione dei valori per ViewportStruct dipende dall'interfaccia utente e dall'implementazione della fotocamera di un'app. A un livello molto base, per impostare la visualizzazione del video della videocamera, aggiorna l'attributo viewport del tratto CameraAvStreamManagement con un ViewportStruct, utilizzando la funzione Kotlin setViewport integrata:

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(),
    )
) }

Attivare o disattivare l'analisi

Ogni dispositivo può attivare individualmente l'invio di dati di analisi dettagliati al cloud Google Home (vedi API Cloud Monitoring per Home).

Per attivare l'analisi per un dispositivo, imposta la proprietà analyticsEnabled di ExtendedGeneralDiagnosticsTrait su true. Quando imposti analyticsEnabled, un'altra proprietà, logUploadEnabled, viene impostata automaticamente su true, il che consente di caricare i file di log di analisi sul cloud Google Home.

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

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

Configurazioni di trasporto e registrazione

Questa sezione riguarda le impostazioni relative alla qualità dello streaming della videocamera e all'attivazione degli eventi. Queste impostazioni sono gestite dall'attributo PushAvStreamTransport.

Leggere le impostazioni di trasporto

Questa sezione mostra come recuperare la configurazione attuale da una videocamera o un campanello. Recupera il trait PushAvStreamTransport, trova la connessione specifica utilizzata per la registrazione ed estrae i valori correnti per la qualità della larghezza di banda, la sensibilità riattivazione e la durata massima dell'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

Aggiornare le impostazioni di trasporto

Questa sezione mostra come modificare le impostazioni di trasporto. Crea un nuovo TransportOptionsStruct contenente i nuovi valori e poi utilizza il comando modifyPushTransport per inviare queste impostazioni aggiornate al dispositivo, applicandole alla connessione di registrazione trovata nel passaggio precedente.

Per modificare queste impostazioni, utilizza il comando modifyPushTransport con un 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
    )
}

Determinare la qualità della larghezza di banda

La proprietà videoStreamId di TransportOptionsStruct corrisponde a una configurazione specifica dello stream video.

Per ottenere gli stream video supportati, fai riferimento all'attributo allocatedVideoStreams, che è un elenco di VideoStreamStructs. dalla caratteristica CameraAvStreamManagement per il dispositivo.

Regolare la sensibilità riattivazione del dispositivo

La proprietà motionSensitivity di TransportTriggerOptionsStruct corrisponde ai seguenti valori:

Etichetta Valore (UByte)
Bassa 1u
Media 5u
Alta 10u

Regolare la durata massima degli eventi

La proprietà maxDuration di TransportMotionTriggerTimeControlStruct corrisponde alle seguenti durate (in secondi):

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

Dismettere un dispositivo

Per rimuovere una videocamera da una struttura, chiama device.decommissionDevice(). Il ritiro mostra all'utente una schermata di conferma gestita da Google che avvisa che tutti i dati registrati della cronologia video 24/7, della cronologia degli eventi, dei clip e della raccolta dei volti familiari verranno eliminati definitivamente dalla struttura.

Per istruzioni dettagliate ed esempi di codice, vedi Rimuovere un dispositivo.