Guia do dispositivo de campainha para iOS

O tipo de dispositivo campainha é implementado usando três atributos: PushAvStreamTransportTrait, que processa o transporte de stream de áudio e vídeo usando protocolos baseados em push, WebRtcLiveViewTrait, que oferece a capacidade de controlar transmissões ao vivo e talkback, e CameraSnapshotTrait, que fornece snapshots de visualização em cache e capturas de snapshots 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 iOS para mais informações.

Tipo de dispositivo das APIs Home Características App de exemplo do Swift Caso de uso

Campainha

GoogleDoorbellDeviceType

home.matter.6006.types.0113

Um dispositivo acionado por um botão do lado de fora de uma porta que emite um sinal sonoro e/ou visual, usado para chamar a atenção de uma pessoa que está do outro lado da porta. As campainhas podem ter transmissões ao vivo acessíveis, comunicação bidirecional ou eventos de detecção.

Atributos obrigatórios
     google PushAvStreamTransportTrait
     google WebRtcLiveViewTrait

Campainha

Receber informações básicas sobre um dispositivo

   Implementado no app de exemplo para iOS   

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:

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!

Encontrar o número de série

   Implementado no app de exemplo para iOS   

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:

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

Respostas rápidas

O recurso de respostas rápidas permite que o usuário envie uma mensagem predefinida para um dispositivo de campainha.

Esse recurso está disponível apenas em campainhas. A lista de mensagens predefinidas está disponível para o app do parceiro, que pode apresentá-las ao usuário para que ele escolha uma. As mensagens predefinidas não podem ser editadas pelo usuário.

As respostas rápidas são implementadas pelo atributo PresetMessage.

Reproduzir uma mensagem predefinida

Para reproduzir uma mensagem predefinida, chame o método playPresetMessage, transmitindo um dos valores de string encontrados na propriedade 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)")
    }
}

Definir idioma falado

Definir idioma falado

Defina o idioma falado ativo de um dispositivo para uma localidade específica (por exemplo, "en_US") usando o método setActiveLocale da característica LocalizationConfiguration.

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

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:

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

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

O atributo 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.

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

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.

let doorbellConnectivity = doorbellDeviceType.metadata.sourceConnectivity.connectivityState

Um estado de partiallyOnline pode ser observado no caso de tipos de dispositivos mistos quando não há conectividade com a Internet. Os atributos padrão do Matter ainda podem estar on-line devido ao roteamento local, mas os atributos baseados 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 GeneralDiagnosticsTrait. Os endereços são retornados como objetos Data, que podem ser formatados como strings IPv4 ou IPv6 padrão usando o 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
}

Iniciar uma transmissão ao vivo

   Implementado no app de exemplo para iOS   

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(offerSdp:) da característica WebRtcLiveViewTrait, que retorna 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.
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
  }
}

Estender uma transmissão ao vivo

   Implementado no app de exemplo para iOS   

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

Iniciar e interromper o TalkBack

   Implementado no app de exemplo para iOS   

Para iniciar o talkback, chame o método startTalkback(mediaSessionId:optionalArgsProvider:) da característica WebRtcLiveViewTrait. Para interromper, use 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)")
  }
}

Gerenciar transmissão ao vivo

   Implementado no app de exemplo para iOS   

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 WebRtcLiveView para expor as qualidades de transmissão compatíveis como uma lista de valores QualityHint (como .sd, .hd, .fhd, .qhd ou .uhd) usando a propriedade supportedQualityHints.

  • 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 updateQualityHint usa o método changeLiveViewQuality da característica WebRtcLiveView para aplicar a configuração QualityHint selecionada à sessão de mídia ativa.

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

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(transportStatus:optionalArgsProvider:) da característica PushAvStreamTransportTrait. 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:

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

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:

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
}

Recuperar capturas de tela da câmera

   Implementado no app de exemplo para iOS   

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:

guard let cameraSnapshotTrait = deviceType.traits[
  Google.CameraSnapshotTrait.self
] else {
  print("CameraSnapshot trait not supported on this device.")
  return
}

if let previewImageUrlString = cameraSnapshotTrait
  .attributes.preview_image_url,
   let url = URL(string: previewImageUrlString) {
  // Load the preview image from the URL
}

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

do {
  let response = try await cameraSnapshotTrait.getLiveSnapshot()
  let snapshotUrlString = response.snapshot_url

  if let url = URL(string: snapshotUrlString) {
    // Load the live snapshot image from the URL
  }
} catch {
  print("Failed to get live snapshot: \(error)")
}

Autenticar solicitações de snapshot

   Implementado no app de exemplo para iOS   

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 UIKit

func loadSnapshotImage(from url: URL, home: Home) async -> UIImage? {
  do {
    // 1. Retrieve the OAuth access token from the Home APIs SDK
    let (accessToken, _) = try await home.permissions.authorization()

    // 2. Configure the HTTP GET request with the Bearer token
    var request = URLRequest(url: url)
    request.setValue(
      "Bearer \(accessToken)",
      forHTTPHeaderField: "Authorization"
    )

    // 3. Fetch the image data
    let (data, _) = try await URLSession.shared.data(for: request)
    return UIImage(data: data)
  } catch {
    print("Error loading snapshot image: \(error)")
    return nil
  }
}

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"
    }
  ]
}
private func setBatteryUsage(to option: UInt8) async throws {
  _ = try await energyPreferenceTrait.update {
    $0.setCurrentEnergyBalance(option)
  }
}

Ativar a economia de bateria automática

   Implementado no app de exemplo para iOS   

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.

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

Verificar o estado de carregamento da bateria

   Implementado no app de exemplo para iOS   

Para saber o estado de carregamento atual do dispositivo (carregando, totalmente carregado ou não carregando), use o atributo batChargeState da característica 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"
}

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.

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

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.

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

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.

Se a bateria for substituível, o atributo batReplacementNeeded da característica PowerSource vai informar se ela precisa ser trocada.

// Get the battery replacement status
var batteryStatus = powerSourceTrait.attributes.batReplacementNeeded

if batteryStatus {
  print("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 do atributo CameraAvStreamManagementTrait usando a função integrada setMicrophoneMuted:

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

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 CameraAvStreamManagementTrait usando a função integrada setRecordingMicrophoneMuted:

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

Ajustar o volume do alto-falante

Para ajustar o volume do alto-falante do dispositivo, atualize o atributo speakerVolumeLevel do atributo CameraAvStreamManagementTrait usando a função integrada setSpeakerVolumeLevel:

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

Configurações de Zonas de Atividade

   Implementado no app de exemplo para iOS   

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.

let zoneManagementTrait: Google.ZoneManagementTrait

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

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

Atualizar uma Zona de Atividade

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

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

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

Excluir uma Zona de Atividade

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

let zoneManagementTrait: Google.ZoneManagementTrait
let zoneID: UInt16

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

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 iOS, normalmente você acessa o atributo AvStreamAnalysis do dispositivo para ler esses 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)
    )
  )
}

Atualizar o conjunto de gatilhos ativados

Para atualizar o conjunto de gatilhos ativados, use o comando SetOrUpdateEventDetectionTriggers, que usa uma lista de estruturas 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 gravação

   Implementado no app de exemplo para iOS   

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:

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

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:

let recordingModeTrait: Google.RecordingModeTrait
let recordingModeID: UInt8

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

Outras configurações

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

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 CameraAvStreamManagementTrait usando a função integrada setNightVision:

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

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 CameraAvStreamManagementTrait usando a função integrada setStatusLightBrightness:

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

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 CameraAvStreamManagementTrait com um ViewportStruct, usando a função integrada setViewport.

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

}

Gerar um TransportOptionsStruct

Algumas configurações exigem modificações nas propriedades em um TransportOptionsStruct, que é transmitido para as opções de transporte de uma conexão de streaming. No Swift, essa struct precisa ser gerada antes de atualizar as propriedades.

Use essa função auxiliar para gerar o struct que será usado com as seguintes mudanças de configuração:

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
}

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
_ = try await extendedGeneralDiagnosticsTrait.update {
  $0.setAnalyticsEnabled(true)
}

// Disable analytics
_ = try await extendedGeneralDiagnosticsTrait.update {
  $0.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.

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

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.

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

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 ver 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 (UInt8)
Baixo 1
Médio 5
Alta 10

Ajustar a duração máxima de eventos

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

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

Configurações da campainha

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

Mudar o som da campainha

Para mudar o som da campainha, primeiro use o atributo installedChimeSounds do atributo ChimeTrait para ver a lista de sons instalados no dispositivo:

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

Em seguida, atualize o atributo selectedChime da característica ChimeTrait usando a função integrada setSelectedChime:

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

Usar uma campainha externa

   Implementado no app de exemplo para iOS   

A campainha pode ser configurada para usar um sino externo, como um sino mecânico instalado dentro da casa. Isso deve ser configurado durante a instalação da campainha para evitar possíveis danos ao sino externo.

Para indicar o tipo de campainha externa instalada, use ExternalChimeType para atualizar o atributo externalChime da característica ChimeTrait usando a função integrada setExternalChime:

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

Mudar a duração da campainha externa

A duração, em segundos, do toque de uma campainha externa pode ser configurada pelas APIs Home. Se o sino externo for compatível com uma duração de toque, o usuário poderá configurar isso.

O valor definido aqui depende das especificações da campainha externa e da duração recomendada para ela.

Para mudar a duração do sino externo, atualize o atributo externalChimeDurationSeconds da característica ChimeTrait usando a função integrada setExternalChimeDurationSeconds:

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

Ativar um tema de sino

   Implementado no app de exemplo para iOS   

Algumas campainhas podem ter sinos que ficam disponíveis para os usuários por um tempo limitado. Por exemplo, sons específicos para datas comemorativas. Eles são chamados de temas de campainha.

Para ver quais temas de campainha estão disponíveis para um usuário, crie um filtro de período e use-o para filtrar os resultados do comando getAvailableThemes do atributo ChimeThemes. Isso retorna uma lista de temas disponíveis, incluindo os nomes deles.

O exemplo a seguir mostra como filtrar a lista. Um tema é considerado ativo se o horário atual estiver entre os horários de início e término (os valores startTimeSeconds e endTimeSeconds, respectivamente). Se um horário de início não for definido, ele será considerado ativo desde o início dos tempos. Se um horário de término não for definido, ele vai continuar ativo indefinidamente. Se ambos estiverem ausentes, o tema estará sempre ativo.

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

Depois de encontrar o nome do tema que você quer, como Christmas, selecione-o usando a função setSelectedTimeboxedThemeName() no atributo ChimeThemes ChimeThemes.

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

Configurações de avisos sobre visitas

É possível consultar e gerenciar as configurações de aviso sobre visitas para campainhas usando o atributo VisitorAnnouncement das APIs Home. Esse atributo controla se a presença de um visitante é anunciada em alto-falantes ou telas inteligentes do Google quando alguém toca a campainha.

O exemplo a seguir mostra como verificar se os avisos sobre visitas estão ativados e como atualizar essa configuração:

let visitorAnnouncementsEnabled: Bool = visitorAnnouncementTrait.attributes.visitorAnnouncementsEnabled

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

Desativar um dispositivo

Para remover uma campainha de uma estrutura, chame device.decommission(). 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.