Doorbell device guide for Android

The Doorbell device type is implemented using two traits: PushAvStreamTransport, which handles audio and video stream transport using push-based protocols, and WebRtcLiveView, which provides the ability to control livestreams and talkback.

Always check for attribute and command support for a device prior to using any features or attempting to update attributes. See Control devices on Android for more information.

Home APIs Device Type Traits Kotlin Sample App Use Case

Doorbell

GoogleDoorbellDevice

home.matter.6006.types.0113

A device actuated by a button outside a door that makes an audible and/or visual signal, used to request the attention of a person who is somewhere on the other side of the door. Doorbells may feature accessible livestreams, two-way talkback, or detection events.

Required Traits
     google PushAvStreamTransport
     google WebRtcLiveView

Doorbell

Get basic information about a device

The BasicInformation trait includes information like vendor name, vendor ID, product ID, product name (includes model information), software version, and the serial number for a device:

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

Get the most recent time of device cloud contact

To find the most recent time that the device had contact with the cloud, use the lastContactTimestamp attribute of the ExtendedGeneralDiagnostics trait:

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

Check connectivity for a device

Connectivity for a device is actually checked at the device type level because some devices support multiple device types. The state returned is a combination of the connectivity states for all traits on that device.

val lightConnectivity = dimmableLightDevice.metadata.sourceConnectivity.connectivityState

A state of PARTIALLY_ONLINE may be observed in the case of mixed device types when there is no internet connectivity. Matter standard traits may still be online due to local routing, but cloud-based traits will be offline.

Start a livestream

To start a livestream, send the Session Description Protocol (SDP) string to the WebRtcLiveView trait's startLiveView() method, which returns a WebRtcLiveViewTrait.StartLiveViewCommand.Response containing three values:

  • The SDP for the session.
  • The session duration in seconds.
  • The session ID, which may be used to extend or terminate the session.
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)

Extend a livestream

Livestreams have a preset duration after which they expire. To lengthen the duration of an active stream, issue an extension request using the WebRtcLiveView.extendLiveView() method:

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

Start and stop talkback

To start talkback, call the WebRtcLiveView trait's startTalkback() method. To stop, use 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)
  }
}

Enable and disable recording capability

To enable the camera's recording capability, pass TransportStatusEnum.Active to the PushAvStreamTransport trait's setTransportStatus() method. To disable the recording capability, pass it TransportStatusEnum.Inactive. In the following example, we wrap these calls in a single call that uses a Boolean to toggle the recording capability:

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

Enabling or disabling the camera's recording capability is the same as turning the camera video on or off. When a camera's video is on, it is recording (for purposes of events and related clips).

When the recording capability is disabled (the camera video is off):

Check if recording capability is enabled

To determine if a camera's recording capability is enabled, check to see if any connections are active. The following example defines two functions to do this:

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

Another way to check is using the findTransport() function with a predicate:

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

Battery settings

Various battery settings can be controlled through the Home APIs.

Set the battery usage preference

Setting the energy balance lets you configure the tradeoff between battery life and performance for a device. You can create different battery profiles, such as "Extended," "Balanced," and "Performance," and switch between them.

This feature is implemented by updating the currentEnergyBalance attribute of the EnergyPreference trait. The attribute accepts an integer index that corresponds to a specific profile defined in the device's energyBalances list (for example, 0 for EXTENDED, 1 for BALANCED, and 2 for PERFORMANCE).

A null value for currentEnergyBalance indicates the device is using a custom profile. This is a read-only state.

The following shows an example of a structure that the currentEnergyBalance attribute will use, followed by the actual code snippet that uses the attribute.

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

Turn on automatic battery saver

To configure this feature, update the currentLowPowerModeSensitivity attribute of the EnergyPreference trait. This attribute uses an index to select a sensitivity level, where 0 typically represents Disabled and 1 represents Enabled or 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) }
}

Get the battery charging state

To get the device's current charging state (charging, fully charged, or not charging), use the batChargeState attribute of the PowerSource trait.

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

Get the battery level

To get the current battery level, use the batChargeLevel attribute of the PowerSource trait. The level is either OK, Warning (low), or 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"
}

Get the power source

To determine the power source the device is using, use the BatPresent and wiredPresent attributes of the PowerSource trait.

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

Audio settings

Various audio settings can be controlled through the Home APIs.

Turn microphone on or off

To turn the device's microphone on or off, update the microphoneMuted attribute of the CameraAvStreamManagement trait using the built-in setMicrophoneMuted Kotlin function:

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

Turn audio recording on or off

To turn audio recording on or off for the device, update the recordingMicrophoneMuted attribute of the CameraAvStreamManagement trait using the built-in setRecordingMicrophoneMuted Kotlin function:

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

Adjust the speaker volume

To adjust the speaker volume for the device, update the speakerVolumeLevel attribute of the CameraAvStreamManagement trait using the built-in setSpeakerVolumeLevel Kotlin function:

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

Activity zone settings

The ZoneManagement trait provides an interface for managing custom regions of interest (activity zones) on camera and doorbell devices. These zones are used to filter event detection (such as person or vehicle motion) to specific areas within the device's field of view.

Activity zones are configured by the user within a partner application, allowing them to draw zones over specific areas in the camera's field of view. These user-defined zones are then translated into the structures used by this trait. For more information on how activity zones work, see Set up and use Activity Zones.

Activity zones are typically defined using 2D Cartesian coordinates. The trait provides the TwoDCartesianVertexStruct for vertices and the TwoDCartesianZoneStruct for the zone definition (name, vertices, color, and usage).

Check activity zones

To display activity zones, check the zones attribute of the ZoneManagement trait.

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

Add an activity zone

To create a new zone, use the createTwoDCartesianZone command. This command takes a TwoDCartesianZoneStruct, which defines the zone's name, vertices, color, and usage.

The following example shows how to create a zone named "Front Porch" with four vertices, colored salmon (#F439A0), and used for motion detection.

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

Update an activity zone

To update an existing zone, use the updateTwoDCartesianZone command. This command requires the zoneId and the updated TwoDCartesianZoneStruct.

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

Delete an activity zone

To remove a zone, use the removeZone command with the specific zoneId.

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

Sound Event Triggers

The AvStreamAnalysis trait provides an interface for managing event detection triggers on camera and doorbell devices. While vision-based triggers (such as people or vehicles) can be zone-specific, sound-related triggers are typically device-level configurations.

The following trigger types are available for sound detection with the EventTriggerTypeEnum:

Mode Enum value Description
Sound Sound General sound detection.
Person talking PersonTalking Detects speech.
Dog bark DogBark Detects canine vocalizations.
Glass break GlassBreak Detects the sound of breaking glass.
Smoke alarm SmokeAlarm Detects smoke alarms, often recognized by the T3 audible pattern (three short beeps followed by a pause).
CO alarm CoAlarm Detects carbon monoxide (CO) alarms, typically recognized by the T4 audible pattern (four short beeps followed by a pause).

Check sound detection status

To display the current state of sound detection to the user, you must check what the device supports and what is enabled by the device hardware. The two attributes to check are:

In Android development using Kotlin Flows, you typically would observe the AvStreamAnalysis trait from the HomeDevice.

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

Update the set of enabled triggers

To update the set of enabled triggers, use the SetOrUpdateEventDetectionTriggers command, which takes a list of EventTriggerEnablement structures.

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

Recording modes

The RecordingMode trait provides an interface for managing the video and image recording behavior on camera and doorbell devices. It allows users to choose between continuous recording, event-based recording, or disabling recording entirely (Live View only).

The RecordingModeEnum defines the available recording strategies:

Mode Enum value Description
Disabled Disabled Recording is completely disabled. Used mainly by legacy devices.
CVR (Continuous Video Recording) Cvr Video is recorded 24x7. Requires a subscription (for example, Google Google Home Premium.
EBR (Event Based Recording) Ebr Recording is triggered by events (person, motion). Video length depends on the event duration and subscription.
ETR (Event Triggered Recording) Etr Short preview recording (for example, 10 seconds) triggered by events.
Live View LiveView Recording is disabled, but users can still access the livestream.
Still Images Images Snapshots are recorded instead of video when events occur.

Check recording modes

To display the current recording configuration, check the attributes of the RecordingMode trait:

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

Change the recording mode

Before updating, ensure that the chosen index from the supportedRecordingModes attribute is present in the availableRecordingModes attribute.

To update the selected mode, use the setSelectedRecordingMode function, passing the index of the chosen mode:

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

Other settings

Various other settings can be controlled through the Home APIs.

Turn night vision on or off

To turn night vision on or off for the camera, use TriStateAutoEnum to update the nightVision attribute of the CameraAvStreamManagement trait using the built-in setNightVision Kotlin function:

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

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

Change the brightness of the status LED

To change the brightness of the status LED, use ThreeLevelAutoEnum to update the statusLightBrightness attribute of the CameraAvStreamManagement trait using the built-in setStatusLightBrightness Kotlin function:

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

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

Change the camera viewport

The camera viewport is the same as the Zoom and Crop feature described in the Zoom and enhance Nest camera video support article.

The viewport is defined in a ViewportStruct that contains four values, which are used as the coordinates of the viewport. The coordinates are defined as:

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

Determining the values for the ViewportStruct depends on an app's UI and camera implementation. At a very basic level, to set the viewport of the camera video, update the viewport attribute of the CameraAvStreamManagement trait with a ViewportStruct, using the built-in setViewport Kotlin function:

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

Adjust the device wake-up sensitivity

The device's wake-up sensitivity is used to conserve battery by decreasing the range at which the device can sense activity and increasing the time to wake up after detecting that activity.

In the Home APIs, this can be set using the motionSensitivity property of the triggerOptions in the device's transportOptions. These options are defined within the PushAvStreamTransport trait for each device.

The wake-up sensitivity can only be set to the following values:

  • 1 = Low
  • 5 = Medium
  • 10 = High

The process for updating is to find the transport configuration for active recording streams using the findTransport command, then modify the configuration with the new sensitivity value using the modifyPushTransport command:

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

Adjust the maximum event length

The maximum event length is the length of time the camera will record a clip for an event. Through the Home APIs this can be configured, per device, to the same lengths as through the GHA, in intervals of seconds:

  • 10 seconds
  • 15 seconds
  • 30 seconds
  • 60 seconds (1 minute)
  • 120 seconds (2 minutes)
  • 180 seconds (3 minutes)

In the Home APIs, this can be set using the motionTimeControl property of the triggerOptions in the device's transportOptions. These options are defined within the PushAvStreamTransport trait for each device.

The process for updating is to find the transport configuration for active recording streams using the findTransport command, then modify the configuration with the new event length value using the modifyPushTransport command:

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

Enable or disable analytics

Each device may individually opt-in to send detailed analytics data to the Google Home cloud (see Cloud Monitoring for Home APIs).

To enable analytics for a device, set the analyticsEnabled property of the ExtendedGeneralDiagnosticsTrait to true. When you set analyticsEnabled, another property, logUploadEnabled, is automatically set to true, which allows the analytics log files to be uploaded to the Google Home cloud.

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

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

Chime settings

Various doorbell chime settings can be controlled through the Home APIs.

Change the chime sound

To change the doorbell chime sound, first get the list of chime sounds that are installed the device, using the installedChimeSounds attribute of the Chime trait:

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

Then, update the selectedChime attribute of the Chime trait using the built-in setSelectedChime Kotlin function:

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

Use an external chime

The doorbell can be configured to use an external chime, such as a mechanical bell installed inside the home. This should be configured during doorbell installation to avoid potential damage to the external chime.

To indicate what type of external chime is installed, use ExternalChimeType to update the externalChime attribute of the Chime trait using the built-in setExternalChime Kotlin function:

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

Change the external chime duration

The duration, in seconds, that an external chime rings can be configured through the Home APIs. If the external chime supports a chime duration, a user may want to configure this.

The value set here is dependent on the specifications of the external chime itself, and what its recommended chime duration is.

To change the external chime duration, update the externalChimeDurationSeconds attribute of the Chime trait using the built-in setExternalChimeDurationSeconds Kotlin function:

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

Enable a chime theme

Some doorbells may have chimes that are only available to users for a limited time. For example, chimes specific to holidays. These are called chime themes.

To see which chime themes are available for a user, create a timebox filter and use it to filter the results of the getAvailableThemes() command from the ChimeThemes trait. This returns a list of available themes, including theme names.

The following example shows how to filter the list. A theme is considered active if the current time is within its start and end times (the startTimeSeconds and endTimeSeconds values, respectively). If a start time isn't set, it's considered active from the beginning of time. If an end time isn't set, it continues to be active indefinitely. If both are missing, the theme is always active.

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

Once you have the name of the theme you want, such as Christmas, you can select it using the setSelectedTimeboxedThemeName() function on the ChimeThemes trait:

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