iOS 版门铃设备指南

门铃设备类型使用两个特征实现:PushAvStreamTransportTrait,用于使用基于推送的协议处理音频和视频流传输;WebRtcLiveViewTrait,用于提供控制直播和对讲的功能。

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

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

门铃

GoogleDoorbellDeviceType

home.matter.6006.types.0113

一种通过门外的按钮启动的设备,可发出声音和/或视觉信号,用于引起门另一侧的人的注意。门铃可能具有无障碍直播、双向对讲或检测事件等功能。

必需的特征
     google PushAvStreamTransportTrait
     google WebRtcLiveViewTrait

门铃

开始直播

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

  • 会话的 SDP。
  • 会话时长(以秒为单位)。
  • 会话 ID,可用于延长或终止会话。
public func sendOffer(offerSdp: String) async throws
-> (answerSdp: String, mediaSessionId: String, liveViewDuration: TimeInterval)
{
  do {
    Logger.info("Sending StartLiveView command...")
    let response = try await liveViewTrait.startLiveView(
      offerSdp: offerSdp
    )
    Logger.info("Received StartLiveView response: \(response)")
    return (
      answerSdp: response.answerSdp,
      mediaSessionId: response.mediaSessionId,
      liveViewDuration: TimeInterval(response.liveSessionDurationSeconds)
    )
  } catch {
    Logger.error("Failed to send StartLiveView command: \(error)")
    throw error
  }
}

延长直播

直播具有预设时长,超过该时长后直播会过期。如需延长有效直播的时长,请使用 extendLiveView(mediaSessionId:optionalArgsProvider:) 方法发出延期请求:

public func extendLiveView(mediaSessionId: String) async throws {
  do {
    Logger.info("Extending live view...")
    let extendedDuration = try await liveViewTrait.extendLiveView(mediaSessionId: mediaSessionId)
    Logger.info("Extended live view for \(extendedDuration.liveSessionDurationSeconds) seconds.")
  } catch {
    Logger.error("Failed to extend live view: \(error)")
    throw error
  }
}

启动和停止 TalkBack

如需启动 TalkBack,请调用 WebRtcLiveViewTrait 特征的 startTalkback(mediaSessionId:optionalArgsProvider:) 方法。如需停止,请使用 stopTalkback(mediaSessionId:)

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

启用和停用录制功能

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

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

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

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

当录制功能处于停用状态(相机视频处于关闭状态)时:

  • 根据设备类型的 connectivityState,摄像头仍可显示为在线。
  • 无法访问实时视频画面,摄像头也未检测到任何云事件。

检查录制功能是否已启用

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

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

音频设置

您可以通过 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
  }
}

其他设置

您还可以通过 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
}

调整设备唤醒灵敏度

设备的唤醒灵敏度用于通过以下方式节省电池电量:缩短设备可感应活动的范围,以及在检测到活动后延长唤醒时间。

在 Home API 中,可以使用设备 transportOptionstriggerOptionsmotionSensitivity 属性来设置此值。这些选项在每个设备的 PushAvStreamTransportTrait 特征中定义。

唤醒灵敏度只能设置为以下值:

  • 1 = 低
  • 5 = 中等
  • 10 = 高

更新过程如下:使用 findTransport 命令查找有效录制流的传输配置,然后使用 modifyPushTransport 命令修改配置,使其包含新的灵敏度值。

modifyPushTransport 命令需要传递完整的 TransportOptionsStruct,因此您必须先从当前配置中复制现有值。请参阅为执行此操作的辅助函数生成 TransportOptionsStruct

func setWakeUpSensitivity(to value: UInt8) async {
  do {
    let connection = try await getRecordingConnection()
    guard let connection,
      let transportOptions = connection.transportOptions
    else {
      // Error - Transport options not available
      return
    }

    guard transportOptions.triggerOptions.motionSensitivity != nil else {
      // Error - Motion sensitivity not available to be updated for this device
      return
    }

    try await pushAvStreamTransportTrait.modifyPushTransport(
      connectionID: connection.connectionID,
      transportOptions: self.getTransportOptions(
        transportOptions: transportOptions,
        wakeUpSensitivity: value,
        maxEventLength: nil
      )
    )

  } catch {
    // Error
  }
}

调整事件录制时长上限

事件时长上限是指摄像头为事件录制片段的时长。通过 Home API,您可以按设备将此参数配置为与通过 Google Home app (GHA) 配置的长度相同的长度(以秒为单位):

  • 10 秒
  • 15 秒
  • 30 秒
  • 60 秒(1 分钟)
  • 120 秒(2 分钟)
  • 180 秒(3 分钟)

在 Home API 中,可以使用设备 transportOptionstriggerOptionsmotionTimeControl 属性来设置此值。这些选项在每个设备的 PushAvStreamTransportTrait 特征中定义。

更新过程如下:使用 findTransport 命令查找有效录制流的传输配置,然后使用 modifyPushTransport 命令修改配置,使其包含新的活动时长值。

modifyPushTransport 命令需要传递完整的 TransportOptionsStruct,因此您必须先从当前配置中复制现有值。请参阅为执行此操作的辅助函数生成 TransportOptionsStruct

func setMaxEventLength(to value: UInt32) async {
  do {
    let connection = try await getRecordingConnection()
    guard let connection,
      let transportOptions = connection.transportOptions
    else {
      // Error - Transport options not available
      return
    }

    guard transportOptions.triggerOptions.motionTimeControl != nil else {
      // Error - Motion time control not available to be updated for this device
      return
    }

    try await pushAvStreamTransportTrait.modifyPushTransport(
      connectionID: connection.connectionID,
      transportOptions: self.getTransportOptions(
        transportOptions: transportOptions,
        wakeUpSensitivity: nil,
        maxEventLength: value
      )
    )

  } catch {
    // Error
  }
}

电铃设置

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

更改电铃声音

如需更改门铃响铃声音,请先使用 ChimeTrait 特征的 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
  }
}

使用外部门铃

您可以将门铃配置为使用外部铃声,例如安装在住宅内的机械铃。应在安装门铃时配置此设置,以避免可能损坏外部门铃。

如需指明安装的外部门铃类型,请使用 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
  }
}