Руководство по использованию дверного звонка для Android

Тип устройства «Дверной звонок» реализован с использованием двух трейтов: PushAvStreamTransport , который обрабатывает передачу аудио- и видеопотоков с использованием протоколов на основе push-уведомлений, и WebRtcLiveView , который предоставляет возможность управления прямыми трансляциями и обратной связью.

Перед использованием каких-либо функций или попыткой обновления атрибутов всегда проверяйте поддержку атрибутов и команд для устройства. См. раздел «Управление устройствами» .Android для получения дополнительной информации.

Главная API Тип устройства Черты Пример приложения на Kotlin Вариант использования

Дверной звонок

Google Doorbell Device

home.matter.6006.types.0113

Устройство, активируемое кнопкой снаружи двери, которое подает звуковой и/или визуальный сигнал и используется для привлечения внимания человека, находящегося по другую сторону двери. Дверные звонки могут иметь доступные функции прямой трансляции, двусторонней связи или обнаружения событий.

Необходимые характеристики
google PushAvStreamTransport
google WebRtcLiveView

Дверной звонок

Получите основную информацию об устройстве.

Параметр BasicInformation включает в себя такую ​​информацию, как название производителя, идентификатор производителя, идентификатор продукта, название продукта (включая информацию о модели), версия программного обеспечения и серийный номер устройства:

// 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}")
        println("serialNumber ${basicInformation.serialNumber}")
    }

Проверьте подключение устройства.

Проверка подключения устройства фактически осуществляется на уровне типа устройства, поскольку некоторые устройства поддерживают несколько типов. Возвращаемое состояние представляет собой комбинацию состояний подключения для всех характеристик данного устройства.

val lightConnectivity = dimmableLightDevice.metadata.sourceConnectivity.connectivityState

Состояние PARTIALLY_ONLINE может наблюдаться в случае использования устройств разных типов при отсутствии подключения к интернету. Стандартные характеристики Matter могут оставаться в сети благодаря локальной маршрутизации, но облачные характеристики будут отключены.

Начать прямую трансляцию

Для запуска прямой трансляции отправьте строку протокола описания сессии (SDP) в метод startLiveView() трейта WebRtcLiveView , который вернет объект WebRtcLiveViewTrait.StartLiveViewCommand.Response , содержащий три значения:

  • План развития науки и техники для данной сессии.
  • Продолжительность сессии в секундах.
  • Идентификатор сессии, который может использоваться для продления или завершения сессии.
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
}

Запуск и остановка обратной связи

Чтобы запустить обратную связь, вызовите метод startTalkback() трейта WebRtcLiveView . Чтобы остановить её, используйте 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 методу setTransportStatus() трейта PushAvStreamTransport . Чтобы отключить запись, передайте 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
    }
}

Настройки батареи

Различные параметры батареи можно контролировать через API Home.

Настройте параметры использования батареи.

Настройка баланса энергии позволяет определить оптимальное соотношение между временем автономной работы и производительностью устройства. Вы можете создавать различные профили работы батареи, такие как «Расширенный», «Сбалансированный» и «Производительный», и переключаться между ними.

Эта функция реализуется путем обновления атрибута currentEnergyBalance трейта EnergyPreference . Атрибут принимает целочисленный индекс, соответствующий определенному профилю, заданному в списке 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"
  }
]
// 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)

Включите автоматический режим экономии заряда батареи.

Для настройки этой функции обновите атрибут currentLowPowerModeSensitivity в свойстве EnergyPreference . Этот атрибут использует индекс для выбора уровня чувствительности, где 0 обычно означает Disabled , а 1Enabled или Automatic .

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

Определите состояние зарядки аккумулятора.

Чтобы получить текущее состояние зарядки устройства (заряжается, полностью заряжено или не заряжается), используйте атрибут batChargeState трейта PowerSource .

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

Проверьте уровень заряда батареи.

Чтобы узнать текущий уровень заряда батареи, используйте атрибут batChargeLevel трейта PowerSource . Уровень может быть либо OK , Warning (низкий), либо 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"
}

Найдите источник питания

Чтобы определить источник питания устройства, используйте атрибуты BatPresent и wiredPresent трейта PowerSource .

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

Настройки звука

Различные настройки звука можно контролировать с помощью API Home.

Включение или выключение микрофона

Чтобы включить или выключить микрофон устройства, обновите атрибут microphoneMuted трейта CameraAvStreamManagement , используя встроенную функцию Kotlin setMicrophoneMuted :

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

Включение или выключение записи звука

Чтобы включить или выключить запись звука на устройстве, обновите атрибут recordingMicrophoneMuted трейта CameraAvStreamManagement , используя встроенную функцию Kotlin setRecordingMicrophoneMuted :

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

Отрегулируйте громкость динамиков

Для регулировки громкости динамика устройства обновите атрибут speakerVolumeLevel трейта CameraAvStreamManagement , используя встроенную функцию Kotlin setSpeakerVolumeLevel :

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

Другие настройки

Различные другие параметры можно контролировать с помощью API Home.

Включение или выключение ночного видения

Чтобы включить или выключить ночное видение для камеры, используйте TriStateAutoEnum для обновления атрибута nightVision трейта CameraAvStreamManagement с помощью встроенной функции Kotlin setNightVision :

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

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

Изменить яркость индикатора состояния

Чтобы изменить яркость светодиода состояния, используйте ThreeLevelAutoEnum для обновления атрибута statusLightBrightness трейта CameraAvStreamManagement с помощью встроенной функции Kotlin setStatusLightBrightness :

// 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 зависит от пользовательского интерфейса приложения и реализации камеры. На самом базовом уровне, чтобы установить область просмотра видео с камеры, обновите атрибут viewport трейта CameraAvStreamManagement , указав ViewportStruct , используя встроенную функцию Kotlin setViewport :

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

Отрегулируйте чувствительность пробуждения устройства.

Чувствительность устройства к пробуждению используется для экономии заряда батареи за счет уменьшения диапазона, в котором устройство может обнаруживать активность, и увеличения времени пробуждения после обнаружения этой активности.

В API Home это можно установить с помощью свойства motionSensitivity объекта triggerOptions в transportOptions устройства. Эти параметры определены в трейте PushAvStreamTransport для каждого устройства.

Чувствительность к пробуждению можно установить только на следующие значения:

  • 1 = Низкий
  • 5 = Средний
  • 10 = Высокий

Процесс обновления заключается в следующем: с помощью команды findTransport необходимо найти конфигурацию транспорта для активных потоков записи, а затем изменить конфигурацию, указав новое значение чувствительности, с помощью команды modifyPushTransport .

// Create a struct with the new wake-up sensitivity
val toUpdate =  TransportOptionsStruct(
  triggerOptions =
    TransportTriggerOptionsStruct(
      motionSensitivity =
        OptionalValue.present(wakeUpSensitivity.toUByte())
    )
  )

// Get the configurations for active connections
val connections  = pushAvStreamTransport.findTransport().transportConfigurations
  // Update all recording streams with the new transport options.
  for (connection in connections) {
    if (connection.transportOptions.getOrNull()?.streamUsage == StreamUsageEnum.Recording) {
      trait.modifyPushTransport(
        connectionId = connection.connectionId,
        transportOptions = toUpdate,
      )
    }
  }

Отрегулируйте максимальную продолжительность события.

Максимальная продолжительность события — это время, в течение которого камера будет записывать видеоролик для данного события. Через API Home это можно настроить для каждого устройства отдельно, установив ту же продолжительность, что и через GHA , с интервалом в секундах:

  • 10 секунд
  • 15 секунд
  • 30 секунд
  • 60 секунд (1 минута)
  • 120 секунд (2 минуты)
  • 180 секунд (3 минуты)

В API Home это можно установить с помощью свойства motionTimeControl объекта triggerOptions в transportOptions устройства. Эти параметры определены в трейте PushAvStreamTransport для каждого устройства.

Процесс обновления заключается в следующем: с помощью команды findTransport необходимо найти конфигурацию транспорта для активных потоков записи, а затем изменить конфигурацию, указав новое значение длины события, используя команду modifyPushTransport .

// Create a struct with the new max event length
// where maxDuration is the length in seconds
val toUpdate =  TransportOptionsStruct(
  triggerOptions =
    TransportTriggerOptionsStruct(
      motionTimeControl =
        OptionalValue.present(
          TransportMotionTriggerTimeControlStruct(maxDuration = it.toUInt())
        )
    )
  )

// Get the configurations for active connections
val connections  = pushAvStreamTransport.findTransport().transportConfigurations
  // Update all recording streams with the new transport options.
  for (connection in connections) {
    if (connection.transportOptions.getOrNull()?.streamUsage == StreamUsageEnum.Recording) {
      trait.modifyPushTransport(
        connectionId = connection.connectionId,
        transportOptions = toUpdate,
      )
    }
  }

Настройки звукового сигнала

Различные настройки дверного звонка можно контролировать через API Home.

Изменить звук колокольчика

Чтобы изменить звук дверного звонка, сначала получите список установленных на устройстве звуков звонка, используя атрибут installedChimeSounds трейта Chime :

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

Затем обновите атрибут selectedChime трейта Chime , используя встроенную функцию Kotlin setSelectedChime :

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

Используйте внешний звонок

Дверной звонок можно настроить на использование внешнего звонка, например, механического звонка, установленного внутри дома. Это следует сделать во время установки дверного звонка, чтобы избежать возможного повреждения внешнего звонка.

Чтобы указать тип установленного внешнего Chime, используйте ExternalChimeType для обновления атрибута externalChime трейта Chime с помощью встроенной функции Kotlin setExternalChime :

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

Изменить продолжительность внешнего звукового сигнала

Продолжительность звонка внешнего звонка в секундах можно настроить через API Home. Если внешний звонок поддерживает настройку продолжительности звонка, пользователь может захотеть это сделать.

Значение, установленное здесь, зависит от технических характеристик самого внешнего звонка и рекомендуемой продолжительности его звучания.

Чтобы изменить продолжительность внешнего звукового сигнала, обновите атрибут externalChimeDurationSeconds трейта Chime , используя встроенную функцию Kotlin setExternalChimeDurationSeconds :

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

Включить тему звукового сигнала

Некоторые дверные звонки могут иметь мелодии, доступные пользователям только в течение ограниченного времени. Например, мелодии, приуроченные к праздникам. Такие мелодии называются тематическими.

Чтобы узнать, какие темы оформления Chime доступны пользователю, создайте фильтр временного интервала и используйте его для фильтрации результатов команды getAvailableThemes() из трейта ChimeThemes . Это вернет список доступных тем, включая их названия.

Следующий пример показывает, как отфильтровать список. Тема считается активной, если текущее время находится в пределах ее начального и конечного времени (значения startTimeSeconds и endTimeSeconds соответственно). Если начальное время не задано, тема считается активной с начала времени. Если конечное время не задано, тема остается активной неограниченно долго. Если оба параметра отсутствуют, тема всегда активна.

// 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 , вы можете выбрать её с помощью функции setSelectedTimeboxedThemeName() в трейте ChimeThemes :

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