iOS 版门铃设备指南

门铃设备类型使用以下三个特征实现:PushAvStreamTransportTrait,使用基于推送的协议处理音频和视频流传输;WebRtcLiveViewTrait,提供控制直播和对讲的功能;CameraSnapshotTrait,提供缓存的预览快照和实时快照捕获。

在使用任何功能或尝试更新属性之前,请务必检查设备是否支持相应属性和命令。如需了解详情,请参阅在iOS上控制设备。

Home API 设备类型 特征 Swift 示例应用 使用场景

门铃

GoogleDoorbellDeviceType

home.matter.6006.types.0113

一种由门外按钮启动的设备,可发出声音和/或视觉信号,用于引起门内某人的注意。门铃可能具有可访问的直播、双向对讲或检测事件。

必需的特征
     google PushAvStreamTransportTrait
     google WebRtcLiveViewTrait

门铃

获取设备的基本信息

   在 iOS 版示例应用中实现   

BasicInformation 特征包含供应商名称、供应商 ID、产品 ID、商品名称(包含型号信息)和设备软件版本等信息:

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!

获取序列号

   在 iOS 版示例应用中实现   

如需获取设备的序列号,请使用 ExtendedBasicInformation 特性的 GetSerialNumber 命令。以下示例展示了如何将序列号保存在名为 serialNumber 的变量中:

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

快速应答

借助“快速回复”功能,用户可以向门铃设备发送预定义的消息。

此功能仅适用于门铃设备。合作伙伴应用可以获取预定义消息列表,并将其呈现给用户以供选择。用户无法修改预定义的消息。

快速回复通过 PresetMessage 特征实现。

播放预设消息

如需播放预设消息,请调用 playPresetMessage 方法,并向其传递在 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)")
    }
}

设置讲话语言

设置讲话语言

使用 LocalizationConfiguration trait 的 setActiveLocale 方法将设备的活跃口语设置为特定语言区域(例如“en_US”)。

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

获取设备最近一次与云端联系的时间

如需查找设备最近一次与云端联系的时间,请使用 ExtendedGeneralDiagnostics 特性的 lastContactTimestamp 属性:

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

摄像头安装类型设置

Mount 特性包含摄像头安装设置和状态信息。您可以读取装载状态、检测类型和装载类型名称等属性。此外,您可以使用 Mount 特征替换默认装载类型配置。

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

检查设备的连接情况

设备的连接性实际上是在设备类型级别进行检查的,因为某些设备支持多种设备类型。返回的状态是该设备上所有特征的连接状态的组合。

let doorbellConnectivity = doorbellDeviceType.metadata.sourceConnectivity.connectivityState

如果设备类型不一致,且未连接到互联网,则可能会观察到 partiallyOnline 状态。由于本地路由,Matter 标准特性可能仍处于在线状态,但基于云的特性将处于离线状态。

获取设备的 IP 地址

如需查找设备的 IP 地址,请使用 GeneralDiagnosticsTrait 的 networkInterfaces 属性。这些地址会作为 Data 对象返回,您可以使用 Network 框架将其格式化为标准 IPv4 或 IPv6 字符串:

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
}

开始直播

   在 iOS 版示例应用中实现   

如需开始直播,请将会话描述协议 (SDP) 字符串发送到 WebRtcLiveViewTrait 特性的 startLiveView(offerSdp:) 方法,该方法会返回三个值:

  • 会话的 SDP。
  • 会话时长(以秒为单位)。
  • 会话 ID,可用于延长或终止会话。
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
  }
}

延长直播

   在 iOS 版示例应用中实现   

直播具有预设时长,到期后即会失效。如需延长活跃数据流的持续时间,请使用 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
  }
}

启动和停止 TalkBack

   在 iOS 版示例应用中实现   

如需启动 TalkBack,请调用 WebRtcLiveViewTrait 特性的 startTalkback(mediaSessionId:optionalArgsProvider:) 方法。如需停止,请使用 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)")
  }
}

管理直播

   在 iOS 版示例应用中实现   

调整直播画质有助于根据客户端的观看环境优化带宽用量(例如,在显示较小的预览图块、网格视图或画中画模式时切换到较低的分辨率)。

使用 WebRtcLiveView 特性动态更改画质,可专门管理特定客户端上活跃直播会话的分辨率。这与直接在设备上配置设备级带宽用量设置不同,后者会影响所有并发观看者以及保存到云端的历史视频录制内容的质量。

以下示例演示了如何检索和更新设备的直播画质:

  • 检索支持的画质选项:获取设备支持的可用在线播放分辨率。该代码会查询 WebRtcLiveView 特征,以使用 supportedQualityHints 属性将支持的串流画质公开为 QualityHint 值列表(例如 .sd、.hd、.fhd、.qhd 或 .uhd)。

  • 更改直播画质:应用所选的 QualityHint,以更改当前直播的流式传输分辨率(例如,从标清切换到高清)。 updateQualityHint 函数使用 WebRtcLiveView 特性的 changeLiveViewQuality 方法将所选 QualityHint 配置应用于活跃媒体会话。

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

启用和停用录制功能

如需启用摄像头的录制功能,请将 TransportStatusEnum.Active 传递给 PushAvStreamTransportTrait 特性的 setTransportStatus(transportStatus:optionalArgsProvider:) 方法。如需停用录制功能,请向其传递 TransportStatusEnum.Inactive。 在以下示例中,我们将这些调用封装在单个调用中,该调用使用 Boolean 切换录制功能:

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

启用或停用摄像头的录制功能与开启或关闭摄像头视频相同。当摄像头的视频开启时,它会录制(用于事件和相关片段)。

停用录制功能(摄像头视频处于关闭状态)时:

检查是否已启用录制功能

如需确定是否已启用摄像头的录制功能,请检查是否有任何连接处于活动状态。以下示例定义了两个函数来实现此目的:

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
}

检索摄像头快照

   在 iOS 版示例应用中实现   

使用 CameraSnapshot 特性可从摄像头检索静态图片快照。 这些快照适用于以下几种场景:

  • 在 WebRTC 直播加载期间,作为界面中的占位图块。
  • 作为静态预览图片,在用户互动后升级为直播。
  • 作为绘制和配置自定义活动所涉区域的背景画布。

CameraSnapshot 特性提供了两种提取快照的方法,具体取决于您是需要缓存的快照以实现快速渲染,还是需要按需实时快照。

获取缓存的预览快照

预览快照是 SDK 无需唤醒摄像头即可检索的缓存图片。这是获取静态图片的最快方法,非常适合摄像头概览页面或信息中心网格。

如需获取预览快照,请读取 preview_image_url trait 属性:

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
}

根据摄像头配置和活动,预览网址会指向根据近期视频片段(最新视频片段的第一个 I 帧)生成的缓存图片,或摄像头定期上传的快照。

为了指明图片的新鲜度,Google Home 云会在提供的 JPEG/PNG 图片中嵌入一个 X-Home-Image-Captured-At 标头,格式为世界协调时间 (UTC)。您可以在客户端解析此标头,以在应用中显示快照的存在时间。

获取按需实时快照

对于需要摄像头当前实时画面的用户体验历程(例如设置活动所涉区域),您可以请求实时快照。触发实时快照会指示摄像头唤醒、捕获新帧并将其上传到云端。

如需请求实时快照,请使用 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)")
}

对快照请求进行身份验证

   在 iOS 版示例应用中实现   

所有快照网址都指向安全媒体,并且需要 Home API v2 范围的 OAuth 2.0 令牌。如需下载和呈现图片,必须将 OAuth 令牌作为不记名令牌注入到 HTTP 请求的 Authorization 标头中。

以下示例演示了如何配置安全网络请求以下载快照:

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

电池设置

您可以通过 Home API 控制各种电池设置。

设置电池用量偏好

通过设置能耗平衡,您可以配置设备的电池续航时间与性能之间的权衡。您可以创建不同的电池配置文件,例如“延长”“平衡”和“性能”,并在它们之间切换。

此功能通过更新 EnergyPreference trait 的 currentEnergyBalance 属性来实现。该属性接受与设备 energyBalances 列表中定义的特定配置文件相对应的整数索引(例如,0 对应 EXTENDED,1 对应 BALANCED,2 对应 PERFORMANCE)。

currentEnergyBalance 的 null 值表示设备正在使用自定义配置文件。此状态为只读状态。

以下示例展示了 currentEnergyBalance 属性将使用的结构,后跟使用该属性的实际代码段。

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

开启自动省电模式

   在 iOS 版示例应用中实现   

如需配置此功能,请更新 EnergyPreference 特性的 currentLowPowerModeSensitivity 属性。此属性使用索引来选择敏感度级别,其中 0 通常表示 Disabled,1 表示 Enabled 或 Automatic。

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

获取电池充电状态

   在 iOS 版示例应用中实现   

如需获取设备的当前充电状态(正在充电、已充满电或未充电),请使用 PowerSource 特性的 batChargeState 属性。

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

获取电池电量

如需获取当前电池电量,请使用 PowerSource 特性的 batChargeLevel 属性。级别为 OK、Warning(低)或 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"
}

获取电源

如需确定设备使用的电源,请使用 PowerSource 特性的 BatPresent 和 wiredPresent 属性。

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

检查电池是否需要更换

batReplaceability 属性用于指明电池是不可更换、可由用户更换还是可由工厂更换。

如果电池可更换,PowerSource 特性的 batReplacementNeeded 属性会告知您是否需要更换电池。

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

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

如果电池可由用户更换,batReplacementDescription 会描述电池,包括外形规格、化学成分和首选制造商等信息。

音频设置

您可以通过 Home API 控制各种音频设置。

开启或关闭麦克风

如需开启或关闭设备的麦克风,请使用内置 setMicrophoneMuted 函数更新 CameraAvStreamManagementTrait 特性的 microphoneMuted 属性:

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

开启或关闭录音设置

如需为设备开启或关闭录音功能,请使用内置 setRecordingMicrophoneMuted 函数更新 CameraAvStreamManagementTrait 特性的 recordingMicrophoneMuted 属性:

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

调节音箱音量

如需调整设备的扬声器音量,请使用内置 setSpeakerVolumeLevel 函数更新 CameraAvStreamManagementTrait 特性的 speakerVolumeLevel 属性:

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

活动所涉区域设置

   在 iOS 版示例应用中实现   

ZoneManagement 特性提供了一个接口,用于管理摄像头和门铃设备上的自定义关注区域(活动所涉区域)。这些区域用于将事件检测(例如人员或车辆移动)过滤到设备视野范围内的特定区域。

活动所涉区域由用户在合作伙伴应用中配置,允许用户在摄像头视野范围内的特定区域绘制区域。然后,这些用户定义的区域会转换为此 trait 使用的结构。 如需详细了解活动所涉区域的运作方式,请参阅设置和使用活动所涉区域。

活动所涉区域通常使用二维笛卡尔坐标来定义。该 trait 为顶点提供 TwoDCartesianVertexStruct,为区域定义(名称、顶点、颜色和用法)提供 TwoDCartesianZoneStruct。

检查活动所涉区域

如需显示活动所涉区域,请检查 ZoneManagement trait 的 zones 属性。

let zoneManagementTrait: Google.ZoneManagementTrait

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

添加活动所涉区域

如需创建新可用区,请使用 createTwoDCartesianZone 命令。此命令接受 TwoDCartesianZoneStruct,用于定义区域的名称、顶点、颜色和用途。

以下示例展示了如何创建一个名为“Front Porch”的区域,该区域有四个顶点,颜色为鲑鱼色 (#F439A0),用于移动侦测。

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

更新活动所涉区域

如需更新现有可用区,请使用 updateTwoDCartesianZone 命令。此命令需要 zoneId 和更新后的 TwoDCartesianZoneStruct。

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

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

删除活动所涉区域

如需移除可用区,请使用 removeZone 命令并指定 zoneId。

let zoneManagementTrait: Google.ZoneManagementTrait
let zoneID: UInt16

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

声音事件触发器

AvStreamAnalysis 特性提供了一个接口,用于管理摄像头和门铃设备上的事件检测触发器。虽然基于视觉的触发器(例如人员或车辆)可以针对特定区域,但与声音相关的触发器通常是设备级配置。

EventTriggerTypeEnum 声音检测功能支持以下触发器类型:

模式 枚举值 说明
声音 Sound 一般声音检测。
人说话声 PersonTalking 检测语音。
狗吠声 DogBark 检测犬类发出的声音。
玻璃破碎 GlassBreak 检测玻璃破碎的声音。
烟雾警报声 SmokeAlarm 检测烟雾警报器,通常通过 T3 声音模式(三声短促的哔哔声,然后暂停)识别。
一氧化碳警报 CoAlarm 检测一氧化碳 (CO) 警报,通常通过 T4 声音模式(四声短促的哔哔声,然后暂停)识别。

查看声音检测状态

如需向用户显示声音检测的当前状态,您必须检查设备支持的功能以及设备硬件启用的功能。要检查的两个属性如下:

在 iOS 开发中,您通常会从设备访问 AvStreamAnalysis 特征,以读取这些属性。

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

更新已启用的触发器集

如需更新已启用的触发器集,请使用 SetOrUpdateEventDetectionTriggers 命令,该命令接受 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
)

录制模式

   在 iOS 版示例应用中实现   

RecordingMode 特性提供了一个接口,用于管理摄像头和门铃设备上的视频和图片录制行为。用户可以选择连续录制、基于事件的录制,或完全停用录制(仅限实时视图)。

RecordingModeEnum 定义了可用的录制策略:

模式 枚举值 说明
已停用 Disabled 录制功能已完全停用。主要由旧版设备使用。
CVR(连续录像) Cvr 全天候录制视频。需要订阅(例如 Google Home Premium)。
EBR(事件录像) Ebr 录制由事件(人员、动作)触发。 视频时长取决于事件时长和订阅方案。
ETR(事件触发录制) Etr 由事件触发的简短预览录制(例如 10 秒)。
实景功能 LiveView 录制功能已停用,但用户仍可观看直播。
静态图片 Images 事件发生时,系统会记录快照,而不是视频。

查看录制模式

如需显示当前录制配置,请检查 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,
      )
    )
  }
}

更改录制模式

在更新之前,请确保 availableRecordingModes 属性中存在从 supportedRecordingModes 属性中选择的索引。

如需更新所选模式,请使用 setSelectedRecordingMode 函数,并传入所选模式的索引:

let recordingModeTrait: Google.RecordingModeTrait
let recordingModeID: UInt8

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

其他设置

您可以通过 Home API 控制各种其他设置。

开启或关闭夜视功能

如需为摄像头开启或关闭夜视功能,请使用 TriStateAutoEnum,通过内置 setNightVision 函数更新 CameraAvStreamManagementTrait 特性的 nightVision 属性:

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

更改状态 LED 的亮度

如需更改状态 LED 的亮度,请使用 ThreeLevelAutoEnum 通过内置 setStatusLightBrightness 函数更新 CameraAvStreamManagementTrait 特性的 statusLightBrightness 属性:

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

更改摄像头视口

相机视口与缩放和增强 Nest 摄像头视频画面支持文章中所述的“缩放和剪裁”功能相同。

视口在包含四个值的 ViewportStruct 中定义,这些值用作视口的坐标。坐标定义如下:

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

确定 ViewportStruct 的值取决于应用的界面和相机实现。在最基本的层面上,如需设置摄像头视频的视口,请使用内置 setViewport 函数,通过 ViewportStruct 更新 CameraAvStreamManagementTrait 特性的 viewport 属性。

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

}

生成 TransportOptionsStruct

某些设置需要修改 TransportOptionsStruct 中的属性,然后将这些属性传递到流式连接的传输选项中。对于 Swift,需要先生成此结构体,然后才能更新任何属性。

使用此辅助函数生成结构体,以便与以下设置更改搭配使用:

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
}

启用或停用分析功能

每台设备都可以单独选择接受向 Google Home 云发送详细分析数据(请参阅 Cloud Monitoring for Home API)。

如需为设备启用分析功能,请将 ExtendedGeneralDiagnosticsTrait 的 analyticsEnabled 属性设置为 true。设置 analyticsEnabled 时,另一个属性 logUploadEnabled 会自动设置为 true,这样分析日志文件就可以上传到 Google Home 云。

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

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

传输和录制配置

本部分介绍与摄像头视频传输质量和事件触发相关的设置。这些设置由 PushAvStreamTransport 特征管理。

读取传输设置

本部分演示了如何从摄像头或门铃设备检索当前配置。它会提取 PushAvStreamTransport 特征,找到用于录制的特定连接,然后提取带宽质量、唤醒灵敏度和最大事件长度的当前值。

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

更新传输设置

本部分介绍了如何更改传输设置。 它会创建一个包含新值的新 TransportOptionsStruct,然后使用 modifyPushTransport 命令将这些更新后的设置发送回设备,并将其应用于上一步中找到的录制连接。

如需修改这些设置,请使用带有 TransportOptionsStruct 的 modifyPushTransport 命令。

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

确定带宽质量

TransportOptionsStruct 的 videoStreamId 属性对应于特定的视频流配置。

如需获取支持的视频流,请参阅 allocatedVideoStreams 属性,该属性是 VideoStreamStructs 的列表。从设备的 CameraAvStreamManagement 特性中获取。

调整设备唤醒灵敏度

TransportTriggerOptionsStruct 的 motionSensitivity 属性对应于以下值:

标签 值 (UInt8)
低 1
中 5
高 10

调整事件时长上限

TransportMotionTriggerTimeControlStruct 的 maxDuration 属性对应于以下 UInt32 时长(以秒为单位):

  • 10、15、30、60、120、180

电铃设置

您可以通过 Home API 控制各种门铃响铃设置。

更改提示音

如需更改门铃提示音,请先使用 ChimeTrait trait 的 installedChimeSounds 属性获取设备上安装的提示音列表:

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

然后,使用内置 setSelectedChime 函数更新 ChimeTrait 特性的 selectedChime 属性:

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

使用外接电铃

   在 iOS 版示例应用中实现   

门铃可以配置为使用外部铃声,例如安装在住宅内的机械铃。此设置应在门铃安装期间进行配置,以免对外部电铃造成潜在损坏。

如需指明安装的外部响铃类型,请使用 ExternalChimeType 通过内置 setExternalChime 函数更新 ChimeTrait 特性的 externalChime 属性:

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

更改外接电铃时长

您可以通过 Home API 配置外部响铃的持续时间(以秒为单位)。如果外部响铃支持响铃时长,用户可能需要配置此设置。

此处设置的值取决于外部电铃本身的规格,以及其建议的响铃时长。

如需更改外部响铃时长,请使用内置 setExternalChimeDurationSeconds 函数更新 ChimeTrait 特性的 externalChimeDurationSeconds 属性:

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

启用提示音主题

   在 iOS 版示例应用中实现   

某些门铃可能具有仅在限定时间内供用户使用的铃声。例如,节假日专属提示音。 这些称为提示音主题。

如需查看用户可以使用哪些提示音主题,请创建一个时间盒过滤器,并使用它来过滤 ChimeThemes 特性的 getAvailableThemes 命令的结果。这将返回可用主题列表,包括主题名称。

以下示例展示了如何过滤列表。 如果当前时间在主题的开始时间和结束时间(分别为 startTimeSeconds 和 endTimeSeconds 值)之间,则该主题被视为处于有效状态。如果未设置开始时间,则系统会将其视为从一开始就处于有效状态。如果未设置结束时间,则会无限期保持有效。如果两者都缺失,则主题始终处于启用状态。

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

获得所需主题的名称(例如 Christmas)后,您可以使用 ChimeThemes ChimeThemes 特征上的 setSelectedTimeboxedThemeName() 函数来选择该主题。

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

来访者通知设置

您可以使用 Home API 的 VisitorAnnouncement trait 查询和管理门铃的来访者通知设置。此 trait 用于控制当有人按门铃时,是否在 Google 智能音箱或显示屏上播报访客到访。

以下示例展示了如何检查是否已启用访客通知,以及如何更新此设置:

let visitorAnnouncementsEnabled: Bool = visitorAnnouncementTrait.attributes.visitorAnnouncementsEnabled

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

停用设备

如需从结构中移除门铃,请调用 device.decommission()。停用时,系统会向用户显示一个由 Google 管理的确认界面,警告用户所有已录制的全天候视频历史记录、事件记录、片段和熟面孔库数据都将从结构中永久删除。

如需查看详细说明和代码示例,请参阅移除设备。