Guía del dispositivo de timbre para iOS

El tipo de dispositivo Doorbell se implementa con dos rasgos: PushAvStreamTransportTrait, que controla el transporte de transmisiones de audio y video con protocolos basados en la transmisión, y WebRtcLiveViewTrait, que proporciona la capacidad de controlar transmisiones en vivo y la función de intercomunicador.

Siempre verifica la compatibilidad de los atributos y los comandos de un dispositivo antes de usar cualquier función o intentar actualizar los atributos. Consulta Cómo controlar dispositivos eniOS para obtener más información.

Tipo de dispositivo de las APIs de Home Rasgos App de ejemplo de Swift Caso de uso

Timbre

GoogleDoorbellDeviceType

home.matter.6006.types.0113

Dispositivo que se acciona con un botón ubicado fuera de una puerta y que emite una señal audible o visual para solicitar la atención de una persona que se encuentra del otro lado de la puerta. Los timbres pueden incluir transmisiones en vivo accesibles, respuesta bidireccional o eventos de detección.

Required Traits
     google PushAvStreamTransportTrait
     google WebRtcLiveViewTrait

Timbre

Obtén información básica sobre un dispositivo

El rasgo BasicInformation incluye información como el nombre del proveedor, el ID del proveedor, el ID del producto, el nombre del producto (incluye información del modelo) y la versión del software de un dispositivo:

// [START get_device_information]
let vendorName = basicInfoTrait.attributes.vendorName!
let vendorID = basicInfoTrait.attributes.vendorID!
let productID = basicInfoTrait.attributes.productID!
let productName = basicInfoTrait.attributes.productName!
let softwareVersion = basicInfoTrait.attributes.softwareVersion!
// [END get_device_information]

Obtener número de serie

Para obtener el número de serie del dispositivo, usa el comando GetSerialNumber del rasgo ExtendedBasicInformation. En el ejemplo, se muestra cómo guardar el número de serie en una variable llamada serialNumber:

// Assuming extendedBasicInformationTrait: Google.ExtendedBasicInformationTrait
let response = try await extendedBasicInformationTrait.getSerialNumber()
let serialNumber = response.serialNumber

Respuestas rápidas

La función de respuestas rápidas permite que el usuario envíe un mensaje predefinido a un dispositivo Doorbell.

Esta función solo está disponible en dispositivos de timbre. La app del socio tiene disponible la lista de mensajes predefinidos, que puede presentar al usuario para que elija uno. Los mensajes predefinidos no se pueden editar.

Las respuestas rápidas se implementan a través del rasgo PresetMessage.

Cómo reproducir un mensaje predeterminado

Para reproducir un mensaje predeterminado, llama al método playPresetMessage y pásale uno de los valores de cadena que se encuentran en la propiedad availablePhraseTypes.


import GoogleHomeSDK
import GoogleHomeTypes

func playDoorbellPresetMessage(device: HomeDevice, phraseTypeString: String) async {
    // 1. Retrieve the GoogleDoorbellDeviceType helper on the device
    guard let doorbellDeviceType = await device.types.get(GoogleDoorbellDeviceType.self) else {
        print("This device is not a Google Doorbell or is currently uninitialized.")
        return
    }

    // 2. Extract the Google.PresetMessageTrait
    guard let presetMessageTrait = doorbellDeviceType.traits[Google.PresetMessageTrait.self] else {
        print("PresetMessageTrait is not supported on this device.")
        return
    }

    // 3. (Optional) Check available phrase types supported by the device
    if let availablePhrases = presetMessageTrait.attributes.availablePhraseTypes {
        let phraseTypeNames = availablePhrases.map { $0.phraseType }
        print("Supported quick response phrases: \(phraseTypeNames)")
    }

    // 4. Send the playPresetMessage command asynchronously
    do {
        try await presetMessageTrait.playPresetMessage(phraseType: phraseTypeString)
        print("Preset message successfully requested.")
    } catch {
        print("SDK error occurred playing preset message: \(error)")
    }
}

Cómo configurar el idioma hablado

Cómo configurar el idioma hablado

Establece el idioma hablado activo de un dispositivo en una configuración regional específica (por ejemplo, "en_US") con el método setActiveLocale del rasgo LocalizationConfiguration.

// Setting the active language
// Assuming localizationConfigurationTrait: Matter.LocalizationConfigurationTrait
let selectedLocale = "en_US"
try await localizationConfigurationTrait.update {
    $0.setActiveLocale(selectedLocale)
}

Obtén la fecha y hora más recientes en que el dispositivo se conectó a la nube

Para encontrar la hora más reciente en la que el dispositivo tuvo contacto con la nube, usa el atributo lastContactTimestamp del rasgo ExtendedGeneralDiagnostics:

if let lastContactTimeStamp = extendedGeneralDiagnosticsTrait.attributes.lastContactTimestamp {
  self.lastContactTime = Date(timeIntervalSince1970: Double(lastConnectedTimeStamp))
}

Parámetros de configuración del tipo de soporte de la cámara

El rasgo Mount contiene la configuración del soporte de la cámara y la información de estado. Puedes leer atributos como el estado de montaje, el tipo de detección y el nombre del tipo de montaje. Además, puedes usar el rasgo Mount para anular la configuración predeterminada del tipo de montaje.

// 1. Get the Mount trait
guard let mountTrait = deviceType.traits[Google.MountTrait.self] else {
  print("Mount trait not supported or configured on this device.")
  return
}

// 2. Read the current mount state, detection type, and type name
let mountState         = mountTrait.attributes.mountState         // Type: Google.MountTrait.MountStateEnum?
let mountDetectionType = mountTrait.attributes.mountDetectionType // Type: Google.MountTrait.MountDetectionTypeEnum?
let mountTypeName      = mountTrait.attributes.mountTypeName      // Type: String?

// 3. Update the mount type override
try await mountTrait.update { mutableTrait in
  mutableTrait.setMountTypeOverride(.official)
}

Cómo verificar la conectividad de un dispositivo

En realidad, la conectividad de un dispositivo se verifica a nivel del tipo de dispositivo, ya que algunos dispositivos admiten varios tipos. El estado que se devuelve es una combinación de los estados de conectividad de todas las características del dispositivo.

let lightConnectivity =
  dimmableLightDeviceType.metadata.sourceConnectivity
  .connectivityState

Se puede observar un estado de partiallyOnline en el caso de tipos de dispositivos mixtos cuando no hay conectividad a Internet. Es posible que los rasgos estándar de Matter sigan en línea debido al enrutamiento local, pero los rasgos basados en la nube estarán sin conexión.

Obtén la dirección IP del dispositivo

Para encontrar la dirección IP del dispositivo, usa el atributo networkInterfaces de GeneralDiagnosticsTrait. Las direcciones se devuelven como objetos Data, que puedes formatear en cadenas IPv4 o IPv6 estándar con el framework Network:

func getIpAddresses(trait: Matter.GeneralDiagnosticsTrait) -> [String] {
  let interfaces = trait.attributes.networkInterfaces ?? []
  var ipAddresses: [String] = []

  for interface in interfaces {
    for data in interface.iPv4Addresses {
      if let ipv4 = IPv4Address(data) {
        ipAddresses.append(String(describing: ipv4))
      }
    }
    for data in interface.iPv6Addresses {
      if let ipv6 = IPv6Address(data) {
        ipAddresses.append(String(describing: ipv6))
      }
    }
  }

  return ipAddresses
}

Cómo iniciar una transmisión en vivo

Para iniciar una transmisión en vivo, envía la cadena del Protocolo de descripción de sesión (SDP) al método startLiveView(offerSdp:) del rasgo WebRtcLiveViewTrait, que devuelve tres valores:

  • Es el SDP de la sesión.
  • Es la duración de la sesión en segundos.
  • Es el ID de sesión, que se puede usar para extender o finalizar la sesión.
public func sendOffer(offerSdp: String) async throws
-> (answerSdp: String, mediaSessionId: String, liveViewDuration: TimeInterval)
{
  do {
    // Sending StartLiveView command
    let response = try await liveViewTrait.startLiveView(
      offerSdp: offerSdp
    )
    // Received StartLiveView response
    return (
      answerSdp: response.answerSdp,
      mediaSessionId: response.mediaSessionId,
      liveViewDuration: TimeInterval(response.liveSessionDurationSeconds)
    )
  } catch {
    // Failed to send StartLiveView command
    throw error
  }
}

Cómo extender una transmisión en vivo

Las transmisiones en vivo tienen una duración predeterminada después de la cual vencen. Para extender la duración de una transmisión activa, envía una solicitud de extensión con el método extendLiveView(mediaSessionId:optionalArgsProvider:):

public func extendLiveView(mediaSessionId: String) async throws {
  do {
    // Extending live view
    let extendedDuration = try await liveViewTrait.extendLiveView(mediaSessionId: mediaSessionId)
  } catch {
    // Failed to extend live view
    throw error
  }
}

Cómo iniciar y detener TalkBack

Para iniciar TalkBack, llama al método startTalkback(mediaSessionId:optionalArgsProvider:) del rasgo WebRtcLiveViewTrait. Para detenerlo, usa stopTalkback(mediaSessionId:).

public func toggleTwoWayTalk(isOn: Bool, mediaSessionId: String) async throws {
  do {
    if isOn {
      try await liveViewTrait.startTalkback(mediaSessionId: mediaSessionId)
    } else {
      try await liveViewTrait.stopTalkback(mediaSessionId: mediaSessionId)
    }
  } catch {
    throw HomeError.commandFailed("Failed to toggle twoWayTalk: \(error)")
  }
}

Administra la transmisión en vivo

Ajustar la calidad de la transmisión en vivo es útil para optimizar el uso del ancho de banda según el contexto de visualización del cliente (por ejemplo, cambiar a una resolución más baja cuando se muestra un mosaico de vista previa más pequeño, la vista de cuadrícula o el modo de pantalla en pantalla).

El rasgo WebRtcLiveView administra específicamente la resolución de la sesión de transmisión en vivo activa en un cliente en particular. No es lo mismo que configurar un parámetro de configuración de uso de ancho de banda para todo el dispositivo directamente en él, lo que afectaría a todos los usuarios simultáneos y la calidad de las grabaciones de video históricas guardadas en la nube.

En el siguiente ejemplo, se muestra cómo recuperar y actualizar la calidad de la transmisión en vivo de un dispositivo:

  • Recupera las opciones de calidad admitidas: Obtén las resoluciones de transmisión disponibles que admite el dispositivo. El código consulta el rasgo WebRtcLiveView para exponer las calidades de transmisión admitidas como una lista de valores de QualityHint (como .sd, .hd, .fhd, .qhd o .uhd) con la propiedad supportedQualityHints.

  • Cambia la calidad de la transmisión en vivo: Aplica un QualityHint seleccionado para cambiar la resolución de la transmisión en vivo activa (por ejemplo, cambiar de definición estándar a alta definición). La función updateQualityHint usa el método changeLiveViewQuality del rasgo WebRtcLiveView para aplicar la configuración de QualityHint seleccionada a la sesión multimedia activa.

public var supportedQualityHints: [Google.WebRtcLiveViewTrait.QualityHint] {
  return liveViewTrait?.attributes.supportedQualityHints ?? []
}

public func updateQualityHint(
  liveViewTrait: Google.WebRtcLiveViewTrait,
  hint: Google.WebRtcLiveViewTrait.QualityHint,
  mediaSessionId: String
) async {
  do {
    _ = try await liveViewTrait.changeLiveViewQuality(
      mediaSessionId: mediaSessionId,
      qualityHint: hint
    )
  } catch {
    // error
  }
}

Cómo habilitar e inhabilitar la capacidad de grabación

Para habilitar la capacidad de grabación de la cámara, pasa TransportStatusEnum.Active al método setTransportStatus(transportStatus:optionalArgsProvider:) del rasgo PushAvStreamTransportTrait. Para inhabilitar la capacidad de grabación, pasa TransportStatusEnum.Inactive. En el siguiente ejemplo, incluimos estas llamadas en una sola llamada que usa un Boolean para activar o desactivar la capacidad de grabación:

public func toggleIsRecording(isOn: Bool) {
  self.uiState = .loading

  guard let pushAvStreamTransportTrait else {
    // PushAvStreamTransportTrait not found.
    return
  }
  Task {
    do {
      try await pushAvStreamTransportTrait.setTransportStatus(
        transportStatus: isOn ? .active : .inactive)
      if isOn {
        do {
          self.player = try self.createWebRtcPlayer()
        } catch {
          // Failed to initialize WebRtcPlayer
          self.uiState = .disconnected
          return
        }
        await self.player?.initialize()
        self.uiState = .live
      } else {
        self.player = nil
        self.uiState = .off
      }
    } catch {
      // Failed to toggle onOff
    }
  }
}

Habilitar o inhabilitar la capacidad de grabación de la cámara es lo mismo que encender o apagar el video de la cámara. Cuando el video de una cámara está encendido, se está grabando (para fines de eventos y clips relacionados).

Cuando la capacidad de grabación está inhabilitada (el video de la cámara está desactivado):

Comprueba si la capacidad de grabación está habilitada

Para determinar si la capacidad de grabación de una cámara está habilitada, verifica si hay conexiones activas. En el siguiente ejemplo, se definen dos funciones para realizar esta acción:

public func isDeviceRecording() -> Bool {
  guard let pushAvStreamTransportTrait else {
    // PushAvStreamTransportTrait not found.
    return false
  }
  guard
    let hasActiveConnection =
      pushAvStreamTransportTrait
      .attributes
      .currentConnections?
      .contains(where: { $0.transportStatus == .active })
  else {
    return false
  }
  return hasActiveConnection
}

Configuración de la batería

Se pueden controlar varios parámetros de configuración de la batería a través de las APIs de Home.

Cómo establecer la preferencia de uso de la batería

Configurar el balance energético te permite establecer el equilibrio entre la duración de la batería y el rendimiento de un dispositivo. Puedes crear diferentes perfiles de batería, como "Extendido", "Equilibrado" y "Rendimiento", y cambiar entre ellos.

Esta función se implementa actualizando el atributo currentEnergyBalance del arquetipo EnergyPreference. El atributo acepta un índice de número entero que corresponde a un perfil específico definido en la lista energyBalances del dispositivo (por ejemplo, 0 para EXTENDED, 1 para BALANCED y 2 para PERFORMANCE).

Un valor de null para currentEnergyBalance indica que el dispositivo usa un perfil personalizado. Este es un estado de solo lectura.

A continuación, se muestra un ejemplo de una estructura que usará el atributo currentEnergyBalance, seguido del fragmento de código real que usa el atributo.

// Example energyBalances list
{
  "energy_balances": [
    {
      "step": 0,
      "label": "EXTENDED"
    },
    {
      "step": 50,
      "label": "BALANCED"
    },
    {
      "step": 100,
      "label": "PERFORMANCE"
    }
  ]
}
private func setBatteryUsage(to option: UInt8) async throws {
  _ = try await energyPreferenceTrait.update {
    $0.setCurrentEnergyBalance(option)
  }
}

Cómo activar el Ahorro de batería automático

Para configurar esta función, actualiza el atributo currentLowPowerModeSensitivity del rasgo EnergyPreference. Este atributo usa un índice para seleccionar un nivel de sensibilidad, en el que 0 suele representar Disabled y 1 representa Enabled o Automatic.

private func setAutoBatterySaver(to value: Bool) async throws {
  _ = try await energyPreferenceTrait.update {
    $0.setCurrentLowPowerModeSensitivity(value ? 1 : 0)
  }
}

Cómo obtener el estado de carga de la batería

Para obtener el estado de carga actual del dispositivo (cargando, completamente cargado o sin cargar), usa el atributo batChargeState del rasgo PowerSource.

self.chargingState = powerSourceTrait.attributes.batChargeState

var description: String
switch self.chargingState {
case .isCharging:
  description = "Charging"
case .isAtFullCharge:
  description = "Full"
case .isNotCharging:
  description = "Not Charging"
default:
  description = "Unknown"
}

Cómo obtener el nivel de batería

Para obtener el nivel de batería actual, usa el atributo batChargeLevel del rasgo PowerSource. El nivel es OK, Warning (bajo) o Critical.

self.batteryLevel = powerSourceTrait.attributes.batChargeLevel

var description: String
switch self.batteryLevel {
case .ok:
  description = "OK"
case .warning:
  description = "Warning"
case .critical:
  description = "Critical"
default:
  description = "Unknown"
}

Obtén la fuente de alimentación

Para determinar la fuente de alimentación que usa el dispositivo, usa los atributos BatPresent y wiredPresent del rasgo PowerSource.

if powerSourceTrait.attributes.wiredPresent ?? false {
  self.powerSourceType = .wired
} else if powerSourceTrait.attributes.batPresent ?? false {
  self.powerSourceType = .battery
} else {
  self.powerSourceType = nil
}

Configuración de audio

Se pueden controlar varios parámetros de audio a través de las APIs de Home.

Activar o desactivar el micrófono

Para activar o desactivar el micrófono del dispositivo, actualiza el atributo microphoneMuted del rasgo CameraAvStreamManagementTrait con la función setMicrophoneMuted integrada:

// Turn the device's microphone on or off
func setMicrophone(on: Bool) async {
  do {
    _ = try await self.cameraAvStreamManagementTrait?.update {
      $0.setMicrophoneMuted(!on)
    }
  } catch {
    // Error
  }
}

Cómo activar o desactivar la grabación de audio

Para activar o desactivar la grabación de audio en el dispositivo, actualiza el atributo recordingMicrophoneMuted del rasgo CameraAvStreamManagementTrait con la función setRecordingMicrophoneMuted integrada:

// Turn audio recording on or off for the device
func setAudioRecording(on: Bool) async {
  do {
    _ = try await self.cameraAvStreamManagementTrait?.update {
      $0.setRecordingMicrophoneMuted(!on)
    }
  } catch {
    // Error
  }
}

Cómo ajustar el volumen de la bocina

Para ajustar el volumen del altavoz del dispositivo, actualiza el atributo speakerVolumeLevel del rasgo CameraAvStreamManagementTrait con la función setSpeakerVolumeLevel integrada:

// Adjust the camera speaker volume
func setSpeakerVolume(to value: UInt8) async {
  do {
    _ = try await cameraAvStreamManagementTrait.update {
      $0.setSpeakerVolumeLevel(value)
    }
  } catch {
    // Error
  }
}

Configuración de zonas de actividad

El rasgo ZoneManagement proporciona una interfaz para administrar regiones de interés (zonas de actividad) personalizadas en dispositivos de cámara y timbre. Estas zonas se usan para filtrar la detección de eventos (como el movimiento de personas o vehículos) en áreas específicas dentro del campo visual del dispositivo.

El usuario configura las zonas de actividad en una aplicación de socio, lo que le permite dibujar zonas sobre áreas específicas del campo visual de la cámara. Luego, estas zonas definidas por el usuario se traducen en las estructuras que usa este rasgo. Para obtener más información sobre cómo funcionan las zonas de actividad, consulta Cómo configurar y usar las Zonas de actividad.

Las zonas de actividad suelen definirse con coordenadas cartesianas 2D. El rasgo proporciona TwoDCartesianVertexStruct para los vértices y TwoDCartesianZoneStruct para la definición de la zona (nombre, vértices, color y uso).

Cómo revisar las zonas de actividad

Para mostrar las zonas de actividad, verifica el atributo zones del rasgo ZoneManagement.

let zoneManagementTrait: Google.ZoneManagementTrait

self.zones = zoneManagementTrait.attributes.zones ?? []

Cómo agregar una zona de actividad

Para crear una zona nueva, usa el comando createTwoDCartesianZone. Este comando toma un TwoDCartesianZoneStruct, que define el nombre, los vértices, el color y el uso de la zona.

En el siguiente ejemplo, se muestra cómo crear una zona llamada "Porche delantero" con cuatro vértices, de color salmón (código hexadecimal #F439A0) y que se usa para la detección de movimiento.

import GoogleHomeSDK
import GoogleHomeTypes

func createFrontPorchZone(trait: Google.ZoneManagementTrait) async {
  // 1. Define the vertices for the zone (2D Cartesian coordinates)
  // Values are UInt16, typically scaled to the device's twoDCartesianMax.
  let vertices = [
    Google.ZoneManagementTrait.TwoDCartesianVertexStruct(x = 260, y = 422),
    Google.ZoneManagementTrait.TwoDCartesianVertexStruct(x = 1049, y = 0),
    Google.ZoneManagementTrait.TwoDCartesianVertexStruct(x = 2048, y = 0),
    Google.ZoneManagementTrait.TwoDCartesianVertexStruct(x = 2048, y = 950),
    Google.ZoneManagementTrait.TwoDCartesianVertexStruct(x = 1630, y = 1349),
    Google.ZoneManagementTrait.TwoDCartesianVertexStruct(x = 880, y = 2048),
    Google.ZoneManagementTrait.TwoDCartesianVertexStruct(x = 0, y = 2048),
    Google.ZoneManagementTrait.TwoDCartesianVertexStruct(x = 638, y = 1090)
  ]

  // 2. Define the zone structure using the given SDK struct
  let newZone = Google.ZoneManagementTrait.TwoDCartesianZoneStruct(
    name: "Front Porch",
    use: [.motion], // ZoneUseEnum.motion
    vertices: vertices,
    // Color is a hex string (for example, Salmon/Pink)
    color: "#F439A0"
  )

  do {
    // 3. Execute the raw command to add the zone to the device
    // This returns the created zone's ID (UInt16).
    var newZoneID = try await trait.createTwoDCartesianZone(zone: newZone)
  } catch {
    // Error
  }
}

Actualiza una zona de actividad

Para actualizar una zona existente, usa el comando updateTwoDCartesianZone. Este comando requiere el zoneId y el TwoDCartesianZoneStruct actualizado.

let zoneManagementTrait: Google.ZoneManagementTrait
let zoneID: UInt16
let zone: Google.ZoneManagementTrait.TwoDCartesianZoneStruct

do {
  _ = try await zoneManagementTrait.updateTwoDCartesianZone(
        zoneID: zoneID, zone: zone)
} catch {
  // Error
}

Cómo borrar una zona de actividad

Para quitar una zona, usa el comando removeZone con el zoneId específico.

let zoneManagementTrait: Google.ZoneManagementTrait
let zoneID: UInt16

do {
  _ = try await zoneManagementTrait.removeZone(zoneID: zoneID)
} catch {
  // Error
}

Activadores de eventos de sonido

El rasgo AvStreamAnalysis proporciona una interfaz para administrar los activadores de detección de eventos en cámaras y timbres. Si bien los activadores basados en la visión (como personas o vehículos) pueden ser específicos de una zona, los activadores relacionados con el sonido suelen ser configuraciones a nivel del dispositivo.

Los siguientes tipos de activadores están disponibles para la detección de sonido con EventTriggerTypeEnum:

Modo Valor enumerado Descripción
Sonido Sound Detección de sonido general
Persona que habla PersonTalking Detecta la voz.
Ladrido de perro DogBark Detecta las vocalizaciones caninas.
Rotura de vidrio GlassBreak Detecta el sonido de rotura de vidrio.
Alarma de humo SmokeAlarm Detecta alarmas de humo, que a menudo se reconocen por el patrón audible T3 (tres sonidos cortos seguidos de una pausa).
alarma de monóxido de carbono CoAlarm Detecta alarmas de monóxido de carbono (CO), que suelen reconocerse por el patrón audible T4 (cuatro sonidos breves seguidos de una pausa).

Cómo verificar el estado de la detección de sonido

Para mostrarle al usuario el estado actual de la detección de sonido, debes verificar qué admite el dispositivo y qué está habilitado por el hardware del dispositivo. Los dos atributos que se deben verificar son los siguientes:

En el desarrollo para iOS, por lo general, se accede al rasgo AvStreamAnalysis del dispositivo para leer estos atributos.

// Example struct to store event triggers
public struct EventTrigger: Equatable {
  public var id: Google.AvStreamAnalysisTrait.EventTriggerTypeEnum
  public var enabled: Bool
}

let avStreamAnalysisTrait: Google.AvStreamAnalysisTrait

let possibleEventTriggers = avStreamAnalysisTrait.attributes.supportedEventTriggers ?? []
let enabledEventTriggers = avStreamAnalysisTrait.attributes.enabledEventTriggers ?? []

let eventTriggers [EventTrigger] = []
for trigger in possibleEventTriggers {
  self.eventTriggers.append(
    EventTrigger(
      id: trigger,
      enabled: enabledEventTriggers.contains(trigger)
    )
  )
}

Actualiza el conjunto de activadores habilitados

Para actualizar el conjunto de activadores habilitados, usa el comando SetOrUpdateEventDetectionTriggers, que toma una lista de estructuras EventTriggerEnablement.

// Example struct to store event triggers
public struct EventTrigger: Equatable {
  public var id: Google.AvStreamAnalysisTrait.EventTriggerTypeEnum
  public var enabled: Bool
}

let avStreamAnalysisTrait: Google.AvStreamAnalysisTrait
let eventTriggers: [EventTrigger]

let enabledEventTriggers = eventTriggers.map {
  Google.AvStreamAnalysisTrait.EventTriggerEnablement(
    eventTriggerType: $0.id,
    enablementStatus: $0.enabled ? .enabled : .disabled
  )
}

try await avStreamAnalysisTrait.setOrUpdateEventDetectionTriggers(
  eventTriggerEnablements: enabledEventTriggers
)

Modos de grabación

El rasgo RecordingMode proporciona una interfaz para administrar el comportamiento de grabación de video y de imágenes en dispositivos de cámara y timbre. Permite a los usuarios elegir entre la grabación continua, la grabación basada en eventos o inhabilitar la grabación por completo (solo en Live View).

El objeto RecordingModeEnum define las estrategias de grabación disponibles:

Modo Valor enumerado Descripción
Inhabilitada Disabled La grabación está completamente inhabilitada. Se usa principalmente en dispositivos heredados.
CVR (grabación de video continua) Cvr El video se graba las 24 horas, todos los días. Requiere una suscripción (por ejemplo, Google Home Premium).
EBR (grabación basada en eventos) Ebr La grabación se activa por eventos (persona, movimiento). La duración del video depende de la duración del evento y de la suscripción.
ETR (grabación activada por eventos) Etr Grabación de vista previa corta (por ejemplo, de 10 segundos) activada por eventos.
Live View LiveView La grabación está inhabilitada, pero los usuarios aún pueden acceder a la transmisión en vivo.
Imágenes fijas Images Cuando ocurren eventos, se graban instantáneas en lugar de videos.

Verifica los modos de grabación

Para mostrar la configuración de grabación actual, verifica los atributos del rasgo RecordingMode:

// Example struct to store recording modes.
public struct RecordingMode: Hashable {
  public let id: UInt8
  public let mode: Google.RecordingModeTrait.RecordingModeEnum
}

let recordingModeTrait: Google.RecordingModeTrait

if let availableRecordingModes = recordingModeTrait.attributes.availableRecordingModes,
   let supportedRecordingModes = recordingModeTrait.attributes.supportedRecordingModes,
   let selectedRecordingMode = recordingModeTrait.attributes.selectedRecordingMode {

  var recordingModes: [RecordingMode] = []

  for recordingModeId in availableRecordingModes {
    guard Int(recordingModeId) < supportedRecordingModes.count,
          Int(recordingModeId) >= 0 else {
      // Out of bounds error
    }

    recordingModes.append(
      RecordingMode(
        id: recordingModeId,
        mode: supportedRecordingModes[Int(recordingModeId)].recordingMode,
      )
    )
  }
}

Cómo cambiar el modo de grabación

Antes de actualizar, asegúrate de que el índice elegido del atributo supportedRecordingModes esté presente en el atributo availableRecordingModes.

Para actualizar el modo seleccionado, usa la función setSelectedRecordingMode y pasa el índice del modo elegido:

let recordingModeTrait: Google.RecordingModeTrait
let recordingModeID: UInt8

_ = try await recordingModeTrait.update {
  $0.setSelectedRecordingMode(recordingModeID)
}

Otros parámetros de configuración

A través de las APIs de Home, se pueden controlar varios otros parámetros de configuración.

Cómo activar o desactivar la Visión nocturna

Para activar o desactivar la visión nocturna de la cámara, usa TriStateAutoEnum para actualizar el atributo nightVision del rasgo CameraAvStreamManagementTrait con la función setNightVision integrada:

// Turn night vision on or off
func setNightVision(
  to value: Google.CameraAvStreamManagementTrait.TriStateAutoEnum
) async {
  do {
    _ = try await cameraAvStreamManagementTrait.update {
      $0.setNightVision(value)
    }
  } catch {
    // Error
  }
}

Cómo cambiar el brillo del LED de estado

Para cambiar el brillo de la luz LED de estado, usa ThreeLevelAutoEnum para actualizar el atributo statusLightBrightness del rasgo CameraAvStreamManagementTrait con la función setStatusLightBrightness integrada:

// Set the LED brightness
func setStatusLightBrightness(
  to value: Google.CameraAvStreamManagementTrait.ThreeLevelAutoEnum
) async {
  do {
    _ = try await cameraAvStreamManagementTrait.update {
      $0.setStatusLightBrightness(value)
    }
  } catch {
    // Error
  }
}

Cómo cambiar el viewport de la cámara

El visor de la cámara es el mismo que la función Zoom and Crop que se describe en el artículo de asistencia técnica sobre cómo hacer zoom en el video de la cámara Nest y mejorar la detección.

El viewport se define en un ViewportStruct que contiene cuatro valores, los cuales se usan como las coordenadas del viewport. Las coordenadas se definen de la siguiente manera:

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

La determinación de los valores de ViewportStruct depende de la IU y la implementación de la cámara de una app. En un nivel muy básico, para configurar el viewport del video de la cámara, actualiza el atributo viewport del rasgo CameraAvStreamManagementTrait con un ViewportStruct, usando la función setViewport integrada.

func setCrop(x1: UInt16, y1: UInt16, x2: UInt16, y2: UInt16) {

  let viewport = Google.CameraAvStreamManagementTrait.ViewportStruct(
    x1: x1,
    y1: y1,
    x2: x2,
    y2: y2
  )

  Task {
    do {
      try await cameraAvStreamManagementTrait.update {
        $0.setViewport(viewport)
      }
    } catch {
      // Error
    }
  }

}

Genera un TransportOptionsStruct

Algunos parámetros de configuración requieren modificaciones en las propiedades de un TransportOptionsStruct, que luego se pasa a las opciones de transporte de una conexión de transmisión. En el caso de Swift, esta estructura debe generarse antes de actualizar cualquier propiedad.

Usa esta función auxiliar para generar el struct que se usará con los siguientes cambios de configuración:

func getTransportOptions(
  transportOptions: Google.PushAvStreamTransportTrait.TransportOptionsStruct,
  wakeUpSensitivity: UInt8?,
  maxEventLength: UInt32?
) async throws
  -> Google.PushAvStreamTransportTrait.TransportOptionsStruct
{

  var newMotionTimeControl:
    Google.PushAvStreamTransportTrait.TransportMotionTriggerTimeControlStruct? = nil
  if let maxEventLength {
    guard let motionTimeControl = transportOptions.triggerOptions.motionTimeControl else {
      throw HomeError.failedPrecondition(
        // Error - cannot update max event length without motion time control
    }
    newMotionTimeControl =
      Google.PushAvStreamTransportTrait.TransportMotionTriggerTimeControlStruct(
        initialDuration: motionTimeControl.initialDuration,
        augmentationDuration: motionTimeControl.augmentationDuration,
        maxDuration: maxEventLength,
        blindDuration: motionTimeControl.blindDuration
      )
  }

  return Google.PushAvStreamTransportTrait.TransportOptionsStruct(
    streamUsage: .recording,
    videoStreamID: nil,
    audioStreamID: nil,
    tlsEndpointID: transportOptions.tlsEndpointID,
    url: transportOptions.url,
    triggerOptions: Google.PushAvStreamTransportTrait.TransportTriggerOptionsStruct(
      triggerType: .motion,
      motionZones: nil,
      motionSensitivity: wakeUpSensitivity,
      motionTimeControl: newMotionTimeControl,
      maxPreRollLen: nil
    ),
    ingestMethod: .cmafIngest,
    containerOptions: Google.PushAvStreamTransportTrait.ContainerOptionsStruct(
      containerType: .cmaf,
      cmafContainerOptions: nil
    ),
    expiryTime: nil
  )
}

private func getRecordingConnection() async throws
  -> Google.PushAvStreamTransportTrait.TransportConfigurationStruct?
{
  guard let pushAvStreamTransportTrait else {
    // Error - PushAvStreamTransport trait not available
    return nil
  }

  let connections = try await pushAvStreamTransportTrait.findTransport().transportConfigurations

  for connection in connections {
    guard let transportOptions = connection.transportOptions,
      transportOptions.streamUsage == .recording
    else {
      continue
    }

    return connection
  }

  return nil
}

Habilita o inhabilita las estadísticas

Cada dispositivo puede habilitar de forma individual el envío de datos de análisis detallados a la nube de Google Home (consulta Cloud Monitoring for Home APIs).

Para habilitar las estadísticas de un dispositivo, configura la propiedad analyticsEnabled) de ExtendedGeneralDiagnosticsTrait en true. Cuando configuras analyticsEnabled, otra propiedad, logUploadEnabled, se configura automáticamente como true, lo que permite que los archivos de registro de análisis se suban a la nube de Google Home.

// Enable analytics
_ = try await extendedGeneralDiagnosticsTrait.update {
  $0.setAnalyticsEnabled(true)
}

// Disable analytics
_ = try await extendedGeneralDiagnosticsTrait.update {
  $0.setAnalyticsEnabled(false)
}

Configuraciones de transporte y grabación

En esta sección, se abordan los parámetros de configuración relacionados con la calidad de la transmisión de la cámara y la activación de eventos. Estos parámetros de configuración se administran con el rasgo PushAvStreamTransport.

Leer la configuración de transporte

En esta sección, se muestra cómo recuperar la configuración actual de una cámara o un timbre. Recupera el rasgo PushAvStreamTransport, busca la conexión específica que se usó para la grabación y, luego, extrae los valores actuales de la calidad del ancho de banda, la sensibilidad de activación y la duración máxima del evento.

// Assuming access to
// var pushAvStreamTransportTrait: Google.PushAvStreamTransportTrait
let connections = try await pushAvStreamTransportTrait.findTransport().transportConfigurations

// Locate the connection designated for recording
let recordingConnection = connections.first { connection in
    guard let transportOptions = connection.transportOptions else { return false }
    return transportOptions.streamUsage == .recording
}

let options = recordingConnection?.transportOptions

// 1. Bandwidth Quality (Video Stream ID)
let videoStreamId = options?.videoStreamID

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

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

Actualiza la configuración de transporte

En esta sección, se muestra cómo cambiar la configuración de transporte. Crea un nuevo TransportOptionsStruct que contiene los valores nuevos y, luego, usa el comando modifyPushTransport para enviar estos parámetros de configuración actualizados al dispositivo y aplicarlos a la conexión de grabación que se encontró en el paso anterior.

Para modificar estos parámetros de configuración, usa el comando modifyPushTransport con un TransportOptionsStruct.

// Example: Updating to Max Quality and 30s duration
let currentOptions = recordingConnection!.transportOptions!
let newOptions = Google.PushAvStreamTransportTrait.TransportOptionsStruct(
    streamUsage: .recording,
    videoStreamID: 2, // Max Quality
    tlsEndpointID: currentOptions.tlsEndpointID,
    url: currentOptions.url,
    triggerOptions: Google.PushAvStreamTransportTrait.TransportTriggerOptionsStruct(
        triggerType: .motion,
        motionSensitivity: 5, // Medium
        motionTimeControl: Google.PushAvStreamTransportTrait.TransportMotionTriggerTimeControlStruct(
            initialDuration: currentOptions.triggerOptions.motionTimeControl?.initialDuration ?? 10,
            augmentationDuration: currentOptions.triggerOptions.motionTimeControl?.augmentationDuration ?? 5,
            maxDuration: 30,
            blindDuration: currentOptions.triggerOptions.motionTimeControl?.blindDuration ?? 0
        )
    ),
    ingestMethod: currentOptions.ingestMethod,
    containerOptions: currentOptions.containerOptions
)

try await pushAvStreamTransportTrait.modifyPushTransport(
    connectionID: recordingConnection!.connectionID,
    transportOptions: newOptions
)

Cómo determinar la calidad del ancho de banda

La propiedad videoStreamId del objeto TransportOptionsStruct corresponde a una configuración específica de transmisión de video.

Para obtener los flujos de video admitidos, consulta el atributo allocatedVideoStreams, que es una lista de VideoStreamStructs. del rasgo CameraAvStreamManagement para el dispositivo.

Cómo ajustar la sensibilidad de activación del dispositivo

La propiedad motionSensitivity del objeto TransportTriggerOptionsStruct corresponde a los siguientes valores:

Etiqueta Valor (UInt8)
Bajo 1
Medio 5
Alta 10

Cómo ajustar la duración máxima de eventos

La propiedad maxDuration del objeto TransportMotionTriggerTimeControlStruct corresponde a las siguientes duraciones de UInt32 (en segundos):

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

Configuración de la campanilla

Se pueden controlar varios parámetros de configuración del timbre a través de las APIs de Home.

Cómo cambiar el sonido de la campanilla

Para cambiar el sonido de la campanilla del timbre, primero obtén la lista de sonidos de campanilla instalados en el dispositivo con el atributo installedChimeSounds del rasgo ChimeTrait:

doorbellChimeTrait.attributes.installedChimeSounds?.compactMap { chimeSound in
  return chimeSound.chimeID, chimeSound.name
}

Luego, actualiza el atributo selectedChime del rasgo ChimeTrait con la función setSelectedChime integrada:

func setDoorbellChime(chimeID: UInt8) async {
  do {
    _ = try await doorbellChimeTrait.update {
      $0.setSelectedChime(chimeID)
    }
  } catch {
    // Error
  }
}

Cómo usar un timbre externo

El timbre se puede configurar para que use un timbre externo, como una campana mecánica instalada dentro de la casa. Esto se debe configurar durante la instalación del timbre para evitar posibles daños en la campanilla externa.

Para indicar qué tipo de timbre externo está instalado, usa ExternalChimeType para actualizar el atributo externalChime del rasgo ChimeTrait con la función setExternalChime integrada:

// Indicate the external chime is mechanical
func setExternalChime(to value: Google.ChimeTrait.ExternalChimeType) async {
  do {
    _ = try await doorbellChimeTrait.update {
      $0.setExternalChime(value)
    }
  } catch {
    // Error
  }
}

Cómo cambiar la duración de la campanilla externa

La duración, en segundos, en la que suena una campanilla externa se puede configurar a través de las APIs de Home. Si el timbre externo admite una duración, es posible que el usuario quiera configurarla.

El valor establecido aquí depende de las especificaciones de la campanilla externa y de la duración recomendada de la campanilla.

Para cambiar la duración del timbre externo, actualiza el atributo externalChimeDurationSeconds del rasgo ChimeTrait con la función setExternalChimeDurationSeconds integrada:

// Change the external chime duration
func setExternalChimeDuration(to value: UInt16) async {
  do {
    _ = try await doorbellChimeTrait.update {
      $0.setExternalChimeDuration(value)
    }
  } catch {
    // Error
  }
}

Cómo habilitar un tema de timbre

Es posible que algunos timbres tengan sonidos que solo estén disponibles para los usuarios por un tiempo limitado. Por ejemplo, campanillas específicas para festividades. Se llaman temas de timbre.

Para ver qué temas de campanillas están disponibles para un usuario, crea un filtro de período y úsalo para filtrar los resultados del comando getAvailableThemes del rasgo ChimeThemes. Esto devuelve una lista de los temas disponibles, incluidos los nombres de los temas.

En el siguiente ejemplo, se muestra cómo filtrar la lista. Un tema se considera activo si la hora actual se encuentra dentro de sus horas de inicio y finalización (los valores startTimeSeconds y endTimeSeconds, respectivamente). Si no se establece una hora de inicio, se considera que está activo desde el principio de los tiempos. Si no se establece una hora de finalización, seguirá activa de forma indefinida. Si faltan ambos, el tema siempre está activo.

let chimeThemes = try await chimeThemeTrait.getAvailableThemes().themes

if !chimeThemes.isEmpty {
  var chimeThemeSettings = []
  for chimeTheme in chimeThemes {
    let currentDateTime = UInt64(Date().timeIntervalSince1970)

    // Only show chime themes that are active.
    if chimeTheme.startTimeSeconds ?? 0 &lt;= currentDateTime
      &amp;&amp; chimeTheme.endTimeSeconds ?? UInt64.max &gt;= currentDateTime
    {
      self.chimeThemeSettings.append(chimeTheme.name)
    }
  }
}

Una vez que tengas el nombre del tema que deseas, como Christmas, puedes seleccionarlo con la función setSelectedTimeboxedThemeName() en el rasgo ChimeThemes ChimeThemes.

private func setChimeTheme(to value: String) async throws {
  _ = try await chimeThemeTrait.update {
    $0.setSelectedTimeboxedThemeName(value)
  }
}

Configuración de anuncios de visitantes

Puedes consultar y administrar la configuración de anuncios de visitantes para timbres con el rasgo VisitorAnnouncement de las APIs de Home. Este rasgo controla si se anuncia la presencia de un visitante en las bocinas o pantallas inteligentes de Google cuando alguien toca el timbre.

En el siguiente ejemplo, se muestra cómo verificar si los anuncios de visitantes están habilitados y cómo actualizar este parámetro de configuración:

let visitorAnnouncementsEnabled: Bool = visitorAnnouncementTrait.attributes.visitorAnnouncementsEnabled

let value: Bool
_ = try await self.visitorAnnouncementTrait?.update {
  $0.setVisitorAnnouncementsEnabled(value)
}