Android 门铃设备指南

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

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

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

门铃

GoogleDoorbellDevice

home.matter.6006.types.0113

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

必需的特征
     google PushAvStreamTransport
     google WebRtcLiveView

门铃

获取有关设备的基本信息

BasicInformation 特征包含设备的相关信息,例如供应商名称、供应商 ID、产品 ID、产品名称(包括型号信息)和软件版本:

// Get device basic information. All general information traits are on the RootNodeDevice type.
    device.type(RootNodeDevice).first().standardTraits.basicInformation?.let { basicInformation ->
        println("vendorName ${basicInformation.vendorName}")
        println("vendorId ${basicInformation.vendorId}")
        println("productId ${basicInformation.productId}")
        println("productName ${basicInformation.productName}")
        println("softwareVersion ${basicInformation.softwareVersion}")
    }

获取序列号

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

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

设置说话语言

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

import java.util.Locale

// Convert underscore format (en_US) to Java Locale
fun String.toLocale(): Locale = Locale.forLanguageTag(this.replace('_', '-'))

// Setting the active language
val trait: LocalizationConfiguration = device.getTrait(LocalizationConfiguration)
val selectedLocale = "en_US" // Target locale string
trait.update {
    setActiveLocale(selectedLocale)
}

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

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

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

检查设备的连接

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

val lightConnectivity = dimmableLightDevice.metadata.sourceConnectivity.connectivityState

如果设备类型混杂且没有互联网连接,可能会出现 PARTIALLY_ONLINE 状态。由于本地路由,Matter 标准特征可能仍处于在线状态,但基于云的特征将处于离线状态。

获取设备的 IP 地址

如需查找设备的 IP 地址,请使用 GeneralDiagnostics 特征的 networkInterfaces 属性。地址以字节数组的形式返回,您可以将其格式化为标准的 IPv4 或 IPv6 字符串:

val ipAddresses =
  trait.networkInterfaces?.flatMap { networkInterface ->
    (networkInterface.ipv4Addresses + networkInterface.ipv6Addresses).mapNotNull { bytes ->
      try {
        java.net.InetAddress.getByAddress(bytes).hostAddress
      } catch (e: java.net.UnknownHostException) {
        null
      }
    }
  } ?: emptyList()

开始直播

如需开始直播,请将会话描述协议 (SDP) 字符串发送到 WebRtcLiveView 特征的 startLiveView() 方法,该方法会返回一个 WebRtcLiveViewTrait.StartLiveViewCommand.Response,其中包含三个值:

  • 会话的 SDP。
  • 会话时长(以秒为单位)。
  • 会话 ID,可用于延长或终止会话。
suspend fun getWebRtcLiveViewTrait(cameraDevice: HomeDevice) {
 return cameraDevice.type(GoogleDoorbellDevice).trait(WebRtcLiveView).first {
    it?.metadata?.sourceConnectivity?.connectivityState == ConnectivityState.ONLINE
  }

}

// Start the live view
suspend fun startCameraStream(trait: WebRtcLiveView, offerSdp: String) {
  val response = trait.startLiveView(offerSdp)
  // Response contains three fields (see below)
  return response
}
  ...

// This is used to manage the WebRTC connection
val peerConnection: RTCPeerConnection = ...

   ...

val startResponse = startCameraStream(sdp)
val answerSdp = startResponse?.answerSdp
val sessionDuration = startResponse?.liveSessionDurationSeconds
val mediaSessionId = startResponse?.mediaSessionId

peerConnection.setRemoteDescription(SessionDescription.Type.ANSWER,
                                    answerSdp)

延长直播时长

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

// Assuming camera stream has just been started
suspend fun scheduleExtension(trait: WebRtcLiveView, mediaSessionId: String, liveSessionDurationSeconds: UShort ) {
  delay(liveSessionDurationSeconds - BUFFER_SECONDS * 1000)
  val response = trait.extendLiveView(mediaSessionId)
  // returns how long the session will be live for
  return response.liveSessionDurationSeconds
}

启动和停止 TalkBack

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

// Make sure camera stream is on
suspend fun setTalkback(isOn: Boolean, trait: WebRtcLiveView, mediaSessionId: String) {
  if(isOn) {
    trait.startTalkback(mediaSessionId)
  } else {
    trait.stopTalkback(mediaSessionId)
  }
}

启用和停用录制功能

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

// Start or stop recording for all connections.
suspend fun setCameraRecording(trait: PushAvStreamTransport, isOn: Boolean) {
  if(isOn) {
    trait.setTransportStatus(TransportStatusEnum.Active)
  } else {
    trait.setTransportStatus(TransportStatusEnum.Inactive)
  }
}

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

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

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

检查录制功能是否已启用

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

// Get the on/off state
suspend fun onOffState(pushAvStreamTransport: PushAvStreamTransport) {
  return pushAvStreamTransport
    .currentConnections?.any { it.transportStatus == TransportStatusEnum.Active } ?: false
}

// Check if the camera's recording capability is enabled
fun PushAvStreamTransport.recordModeActive(): Boolean {
  return currentConnections?.any { it.transportStatus == TransportStatusEnum.Active } ?: false
}

另一种检查方法是使用带有谓词的 findTransport() 函数:

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

电池设置

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

设置电池用量偏好

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

此功能通过更新 EnergyPreference 特征的 currentEnergyBalance 属性来实现。该属性接受一个整数索引,该索引对应于设备 energyBalances 列表中定义的特定配置文件(例如,0 表示 EXTENDED1 表示 BALANCED2 表示 PERFORMANCE)。

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

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

// Example energyBalances list
energy_balances: [
  {
    step: 0,
    label: "EXTENDED"
  },
  {
    step: 50,
    label: "BALANCED"
  },
  {
    step: 100,
    label: "PERFORMANCE"
  }
]
// The index parameter must be within the UByte range (0-255).
suspend fun setEnergyBalance(trait: EnergyPreference, index: Int) {
  trait.update { setCurrentEnergyBalance(index.toUByte()) }
}

// Setting the battery usage to more recording ie performance
setEnergyBalance(energyPreference, 2)

开启自动省电模式

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

suspend fun setAutomaticBatterySaver(enable: Boolean, trait: EnergyPreference) {
  // 0 is Disabled, 1 is Enabled
  val value = if (enable) 1.toUByte() else 0.toUByte()
  trait.update { setCurrentLowPowerModeSensitivity(value) }
}

获取电池充电状态

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

// Get the battery charging state
val batteryChargeState = powerSource.batChargeState

when (batteryChargeState) {
    PowerSourceTrait.BatChargeStateEnum.IsCharging -> "Charging"
    PowerSourceTrait.BatChargeStateEnum.IsAtFullCharge -> "Full"
    PowerSourceTrait.BatChargeStateEnum.IsNotCharging -> "Not Charging"
    else -> "Unknown"
}

获取电池电量

如需获取当前电池电量,请使用 PowerSource 特征的 batChargeLevel 属性。该级别为 OKWarning(低)或 Critical

// Get the battery charge level
val batteryLevel = powerSourceTrait.batChargeLevel

when (batteryLevel) {
    PowerSourceTrait.BatChargeLevelEnum.OK -> "OK"
    PowerSourceTrait.BatChargeLevelEnum.Warning -> "Warning"
    PowerSourceTrait.BatChargeLevelEnum.Critical -> "Critical"
    else -> "Unknown"
}

获取电源

如需确定设备正在使用的电源,请使用 PowerSource 特征的 BatPresentwiredPresent 属性。

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

音频设置

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

开启或关闭麦克风

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

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

开启或关闭录音功能

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

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

调整音箱音量

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

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

活动所涉区域设置

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

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

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

检查活动所涉区域

如需显示活动区域,请检查 ZoneManagement 特征的 zones 属性。

// 1. Obtain the trait flow from the device
private val zoneManagementFlow: Flow =
  device.type(CAMERA_TYPE).flatMapLatest { it.trait(ZoneManagement) }

// 2. Map the flow to the list of zone structures
val activityZones: Flow<List<ZoneManagementTrait.ZoneInformationStruct>> =
  zoneManagementFlow.map { trait ->
    trait.zones ?: emptyList()
  }

添加活动区域

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

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

import com.google.home.google.ZoneManagement
import com.google.home.google.ZoneManagementTrait
import com.google.home.matter.serialization.OptionalValue

/**
 * Creates a custom activity zone named "Front Porch" with a salmon color
 * configured for motion detection.
 */
suspend fun createFrontPorchZone(zoneManagement: ZoneManagement) {
  // 1. Define the vertices for the zone (2D Cartesian coordinates)
  // Values are typically scaled to a maximum defined by the device's twoDCartesianMax attribute.
  val vertices =
    listOf(
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 260u, y = 422u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 1049u, y = 0u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 2048u, y = 0u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 2048u, y = 950u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 1630u, y = 1349u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 880u, y = 2048u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 0u, y = 2048u),
      ZoneManagementTrait.TwoDCartesianVertexStruct(x = 638u, y = 1090u)
    )

  // 2. Define the zone structure
  val newZone =
    ZoneManagementTrait.TwoDCartesianZoneStruct(
      name = "Front Porch",
      vertices = vertices,
      // Usage defines what the zone filters (for example, Motion, Person, Vehicle)
      use = listOf(ZoneManagementTrait.ZoneUseEnum.Motion),
      // Color is typically a hex string (for example, Salmon/Pink)
      color = OptionalValue.present("#F439A0")
    )

  try {
    // 3. Execute the command to add the zone to the device
    zoneManagement.createTwoDCartesianZone(newZone)
    println("Successfully created activity zone.")
  } catch (e: Exception) {
    // Handle potential HomeException or Timeout
    println("Failed to create activity zone: ${e.message}")
  }
}

更新活动区域

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

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

删除活动区间

如需移除某个区域,请使用 removeZone 命令并指定 zoneId

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

声音事件触发器

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

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

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

检查声音检测状态

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

在 Android 开发中使用 Kotlin 数据流时,您通常会从 HomeDevice 中观察 AvStreamAnalysis 特征。

// Example structure to store the
data class EventTriggerAttribute(val type: EventTriggerTypeEnum, val enabled: Boolean)

// 1. Obtain the trait flow from the device
private val avStreamAnalysisFlow: Flow<AvStreamAnalysis> =
  device.traitFromType(AvStreamAnalysis, CAMERA_TYPES.first { device.has(it) })

// 2. Map the flow to a list of sound event attributes
val soundEventTriggersState: Flow<List<EventTriggerAttribute>> =
  avStreamAnalysisFlow.map { trait ->
    // Get raw lists from the trait attributes
    val supported = trait.supportedEventTriggers ?: emptyList()
    val enabled = trait.enabledEventTriggers ?: emptyList()

    // Define sound-specific triggers to filter for
    val soundTypes = setOf(
      EventTriggerTypeEnum.Sound,
      EventTriggerTypeEnum.PersonTalking,
      EventTriggerTypeEnum.DogBark,
      EventTriggerTypeEnum.GlassBreak,
      EventTriggerTypeEnum.SmokeAlarm,
      EventTriggerTypeEnum.CoAlarm,
    )

    // Filter and associate status
    supported
      .filter { soundTypes.contains(it) }
      .map { type ->
        EventTriggerAttribute(
          type = type,
          enabled = enabled.contains(type)
        )
      }
  }

更新已启用触发器的集合

如需更新已启用的触发器集,请使用 SetOrUpdateEventDetectionTriggers 命令,该命令接受 EventTriggerEnablement 结构的列表。

private suspend fun AvStreamAnalysis.updateEventTriggers(
  eventTriggers: List<EventTriggerAttribute>
) {
  val toUpdate = eventTriggers.map {
    EventTriggerEnablement(
      eventTriggerType = it.type,
      enablementStatus = if (it.enabled) {
        EnablementStatusEnum.Enabled
      } else {
        EnablementStatusEnum.Disabled
      },
    )
  }

  // Execute the command on the device
  setOrUpdateEventDetectionTriggers(toUpdate)
}

录制模式

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

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

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

检查录制模式

如需显示当前的录制配置,请检查 RecordingMode 特征的属性:

// 1. Obtain the trait flow from the device
private val recordingModeTraitFlow: Flow =
    device.traitFromType(RecordingMode, CAMERA_TYPES.first { device.has(it) })

// 2. Map the flow to recording mode options
data class RecordingModeOptions(
    val recordingMode: RecordingModeTrait.RecordingModeEnum,
    val index: Int,
    val available: Boolean,
    val readableString: String,
)

private val recordingModeOptions: Flow<List> =
    recordingModeTraitFlow.map { trait ->
        val supported = trait.supportedRecordingModes?.map { it.recordingMode } ?: emptyList()
        val available = trait.availableRecordingModes?.map { it.toInt() } ?: emptyList()

        supported.withIndex().map { (index, mode) ->
            RecordingModeOptions(
                recordingMode = mode,
                index = index,
                available = available.contains(index),
                readableString = mode.toReadableString(),
            )
        }
    }

更改录制模式

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

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

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

其他设置

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

开启或关闭夜视功能

如需为相机开启或关闭夜视功能,请使用 TriStateAutoEnum 通过内置的 setNightVision Kotlin 函数更新 CameraAvStreamManagement 特征的 nightVision 属性:

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

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

更改状态 LED 的亮度

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

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

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

更改相机视口

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

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

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

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

cameraAvStreamManagement
  .update { setViewport(
    CameraAvStreamManagementTrait.ViewportStruct(
      x1 = horizontalRange.rangeStart.roundToInt().toUShort(),
      x2 = horizontalRange.rangeEnd.roundToInt().toUShort(),
      y1 = verticalRange.rangeStart.roundToInt().toUShort(),
      y2 = verticalRange.rangeEnd.roundToInt().toUShort(),
    )
) }

启用或停用分析功能

每部设备都可以单独选择是否向 Google Home 云发送详细的分析数据(请参阅 Home API 的 Cloud Monitoring)。

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

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

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

传输和录制配置

本部分介绍与摄像头直播画质和事件触发相关的设置。这些设置由 PushAvStreamTransport 特征管理。

读取传输设置

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

val trait: PushAvStreamTransport = device.getTrait(PushAvStreamTransport)
val connections = trait.findTransport().transportConfigurations

// Locate the connection designated for recording
val recordingConnection = connections.firstOrNull {
    it.transportOptions.getOrNull()?.streamUsage == StreamUsageEnum.Recording
}

val options = recordingConnection?.transportOptions?.getOrNull()

// 1. Bandwidth Quality (Video Stream ID)
val videoStreamId = options?.videoStreamId?.getOrNull()

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

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

更新传输设置

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

如需修改这些设置,请使用带有 TransportOptionsStructmodifyPushTransport 命令

val toUpdate = TransportOptionsStruct(
    videoStreamId = OptionalValue.present(2u), // e.g., Max Quality
    triggerOptions = TransportTriggerOptionsStruct(
        motionSensitivity = OptionalValue.present(5u), // e.g., Medium
        motionTimeControl = OptionalValue.present(
            TransportMotionTriggerTimeControlStruct(maxDuration = 30u)
        )
    )
)

if (recordingConnection != null) {
    trait.modifyPushTransport(
        connectionId = recordingConnection.connectionId,
        transportOptions = toUpdate
    )
}

确定带宽质量

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

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

调整设备唤醒灵敏度

TransportTriggerOptionsStructmotionSensitivity 属性对应于以下值:

标签 值 (UByte)
1u
5u
10u

调整事件录制时长上限

TransportMotionTriggerTimeControlStructmaxDuration 属性对应于以下时长(以秒为单位):

  • 10u15u30u60u120u180u

电铃设置

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

更改电铃声音

如需更改门铃响铃声音,请先使用 Chime 特征的 installedChimeSounds 属性获取设备上安装的响铃声音列表:

// Get a list of chimes and identify the currently selected one
private val doorbellChimeTraitFlow: Flow =
    device.traitFromType(Chime, GoogleDoorbellDevice)
val chimeSounds = doorbellChimeTraitFlow.first().installedChimeSounds ?: emptyList()

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

// Set the chime using the chimeId from the installed list
chimeSounds.firstOrNull { it.name == name }?.let { setSelectedChime(it.chimeId) }

使用外部电铃

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

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

// Indicate the external chime is mechanical
chime.update {
  setExternalChime(ChimeTrait.ExternalChimeType.Mechanical)
}

更改外部电铃时长

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

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

如需更改外部电铃时长,请使用内置的 setExternalChimeDurationSeconds Kotlin 函数更新 Chime trait 的 externalChimeDurationSeconds 属性:

// Change the external chime duration
chime.update {
  setExternalChimeDurationSeconds(newDuration.toUShort())
}

启用铃声主题

部分门铃可能提供仅限时向用户提供的铃声。例如,特定于节假日的铃声。 这些主题称为铃声主题。

如需查看用户可用的电铃主题,请创建时间箱过滤条件,并使用该过滤条件过滤 ChimeThemes trait 的 getAvailableThemes() 命令的结果。此方法会返回可用主题的列表,包括主题名称。

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

// Get themes from the ChimeThemes trait
fun List<ChimeThemesTrait.ThemeStruct>.filterTimeboxedThemes():
    List<ChimeThemesTrait.ThemeStruct> {
  val now = timeSource.instant().epochSecond.toULong()
  return filter { chimeStruct: ChimeThemesTrait.ThemeStruct ->
    val startTime: ULong = chimeStruct.startTimeSeconds.getOrNull() ?: 0UL
    val endTime: ULong = chimeStruct.endTimeSeconds.getOrNull() ?: MAX_VALUE
    startTime <= now && now <= endTime
  }
}

val availableThemes =
  doorbellChimeThemesTraitFlow
    .first()
    .getAvailableThemes()
    .themes
    .filterTimeboxedThemes()

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

// Select a theme using the ChimeThemes trait
val themeToSelect = "Christmas"
if (themeToSelect in availableThemeNames) {
  doorbellChimeThemesTraitFlow.first().setSelectedTimeboxedThemeName(themeToSelect)
}