Hướng dẫn về thiết bị chuông cửa dành cho Android

Loại thiết bị Chuông cửa được triển khai bằng 2 đặc điểm: PushAvStreamTransport, xử lý việc truyền luồng âm thanh và video bằng các giao thức dựa trên tính năng đẩy và WebRtcLiveView, cung cấp khả năng kiểm soát luồng phát trực tiếp và tính năng đàm thoại hai chiều.

Luôn kiểm tra xem thiết bị có hỗ trợ thuộc tính và lệnh hay không trước khi sử dụng bất kỳ tính năng nào hoặc cố gắng cập nhật thuộc tính. Hãy xem bài viết Điều khiển thiết bị trênAndroid để biết thêm thông tin.

Loại thiết bị Home API Đặc điểm Ứng dụng mẫu Kotlin Trường hợp sử dụng

Chuông cửa

GoogleDoorbellDevice

home.matter.6006.types.0113

Một thiết bị được kích hoạt bằng nút bên ngoài cửa, tạo ra tín hiệu âm thanh và/hoặc hình ảnh, dùng để yêu cầu sự chú ý của một người ở phía bên kia cửa. Chuông cửa có thể có tính năng phát trực tiếp dễ tiếp cận, đàm thoại hai chiều hoặc phát hiện sự kiện.

Đặc điểm bắt buộc
     google PushAvStreamTransport
     google WebRtcLiveView

Chuông cửa

Lấy thông tin cơ bản về một thiết bị

Đặc điểm BasicInformation bao gồm những thông tin như tên nhà cung cấp, mã nhận dạng nhà cung cấp, mã nhận dạng sản phẩm, tên sản phẩm (bao gồm thông tin về kiểu máy), phiên bản phần mềm và số sê-ri của thiết bị:

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

Kiểm tra khả năng kết nối của thiết bị

Khả năng kết nối của thiết bị thực sự được kiểm tra ở cấp loại thiết bị vì một số thiết bị hỗ trợ nhiều loại thiết bị. Trạng thái được trả về là sự kết hợp của các trạng thái kết nối cho tất cả các đặc điểm trên thiết bị đó.

val lightConnectivity = dimmableLightDevice.metadata.sourceConnectivity.connectivityState

Bạn có thể thấy trạng thái PARTIALLY_ONLINE trong trường hợp có nhiều loại thiết bị khi không có kết nối Internet. Các đặc điểm Matter tiêu chuẩn vẫn có thể trực tuyến do định tuyến cục bộ, nhưng các đặc điểm dựa trên đám mây sẽ không trực tuyến.

Bắt đầu phát trực tiếp

Để bắt đầu phát trực tiếp, hãy gửi chuỗi Giao thức mô tả phiên (SDP) đến phương thức startLiveView() của đặc điểm WebRtcLiveView. Phương thức này sẽ trả về một WebRtcLiveViewTrait.StartLiveViewCommand.Response chứa 3 giá trị:

  • SDP cho phiên.
  • Thời lượng phiên tính bằng giây.
  • Mã phiên, có thể dùng để kéo dài hoặc kết thúc phiên.
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)

Kéo dài thời gian phát trực tiếp

Sự kiện phát trực tiếp có thời lượng đặt sẵn và sẽ hết hạn sau thời lượng đó. Để kéo dài thời lượng của một luồng đang hoạt động, hãy đưa ra yêu cầu gia hạn bằng phương thức 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
}

Bắt đầu và dừng TalkBack

Để bắt đầu TalkBack, hãy gọi phương thức startTalkback() của đặc điểm WebRtcLiveView. Để dừng, hãy sử dụng 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)
  }
}

Bật và tắt tính năng ghi âm

Để bật tính năng ghi hình của camera, hãy truyền TransportStatusEnum.Active đến phương thức setTransportStatus() của đặc điểm PushAvStreamTransport. Để tắt khả năng ghi, hãy truyền TransportStatusEnum.Inactive. Trong ví dụ sau, chúng ta sẽ gói các lệnh gọi này trong một lệnh gọi duy nhất sử dụng Boolean để bật/tắt khả năng ghi:

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

Việc bật hoặc tắt khả năng ghi hình của camera cũng giống như việc bật hoặc tắt video của camera. Khi video của camera đang bật, camera sẽ ghi hình (cho mục đích ghi lại các sự kiện và đoạn video liên quan).

Khi tính năng ghi hình bị tắt (video camera ở trạng thái tắt):

  • Camera vẫn có thể hiển thị là đang trực tuyến theo connectivityState của loại thiết bị.
  • Bạn không truy cập được vào phiên phát trực tiếp và camera cũng không phát hiện thấy sự kiện nào trên đám mây.

Kiểm tra xem bạn đã bật tính năng ghi hình hay chưa

Để xác định xem camera có được bật tính năng ghi hình hay không, hãy kiểm tra xem có kết nối nào đang hoạt động hay không. Ví dụ sau đây xác định 2 hàm để thực hiện việc này:

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

Một cách khác để kiểm tra là sử dụng hàm findTransport() với một vị từ:

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

Cài đặt pin

Bạn có thể kiểm soát nhiều chế độ cài đặt pin thông qua Home API.

Đặt lựa chọn ưu tiên về mức sử dụng pin

Việc thiết lập mức cân bằng năng lượng cho phép bạn định cấu hình sự cân bằng giữa thời lượng pin và hiệu suất của thiết bị. Bạn có thể tạo nhiều hồ sơ pin, chẳng hạn như "Mở rộng", "Cân bằng" và "Hiệu suất", rồi chuyển đổi giữa các hồ sơ này.

Tính năng này được triển khai bằng cách cập nhật thuộc tính currentEnergyBalance của đặc điểm EnergyPreference. Thuộc tính này chấp nhận một chỉ mục số nguyên tương ứng với một hồ sơ cụ thể được xác định trong danh sách energyBalances của thiết bị (ví dụ: 0 cho EXTENDED, 1 cho BALANCED2 cho PERFORMANCE).

Giá trị null cho currentEnergyBalance cho biết thiết bị đang sử dụng một hồ sơ tuỳ chỉnh. Đây là trạng thái chỉ đọc.

Sau đây là ví dụ về một cấu trúc mà thuộc tính currentEnergyBalance sẽ sử dụng, theo sau là đoạn mã thực tế sử dụng thuộc tính này.

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

Bật trình tiết kiệm pin tự động

Để định cấu hình tính năng này, hãy cập nhật thuộc tính currentLowPowerModeSensitivity của đặc điểm EnergyPreference. Thuộc tính này dùng một chỉ mục để chọn mức độ nhạy, trong đó 0 thường biểu thị Disabled1 biểu thị Enabled hoặc 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) }
}

Lấy trạng thái sạc pin

Để biết trạng thái sạc hiện tại của thiết bị (đang sạc, đã sạc đầy hoặc không sạc), hãy sử dụng thuộc tính batChargeState của đặc điểm 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"
}

Nhận mức pin

Để biết mức pin hiện tại, hãy sử dụng thuộc tính batChargeLevel của đặc điểm PowerSource. Cấp độ là OK, Warning (thấp) hoặc 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"
}

Tìm nguồn điện

Để xác định nguồn điện mà thiết bị đang sử dụng, hãy dùng các thuộc tính BatPresentwiredPresent của đặc điểm PowerSource.

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

Cài đặt âm thanh

Bạn có thể kiểm soát nhiều chế độ cài đặt âm thanh thông qua Home API.

Bật hoặc tắt micrô

Để bật hoặc tắt micrô của thiết bị, hãy cập nhật thuộc tính microphoneMuted của đặc điểm CameraAvStreamManagement bằng hàm setMicrophoneMuted Kotlin tích hợp sẵn:

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

Bật hoặc tắt tính năng ghi âm

Để bật hoặc tắt tính năng ghi âm cho thiết bị, hãy cập nhật thuộc tính recordingMicrophoneMuted của đặc điểm CameraAvStreamManagement bằng cách sử dụng hàm setRecordingMicrophoneMuted Kotlin tích hợp sẵn:

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

Điều chỉnh âm lượng loa

Để điều chỉnh âm lượng loa cho thiết bị, hãy cập nhật thuộc tính speakerVolumeLevel của đặc điểm CameraAvStreamManagement bằng cách sử dụng hàm setSpeakerVolumeLevel Kotlin tích hợp sẵn:

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

Chế độ cài đặt khác

Bạn có thể kiểm soát nhiều chế độ cài đặt khác thông qua Home API.

Bật hoặc tắt chế độ cảnh đêm

Để bật hoặc tắt chế độ nhìn ban đêm cho camera, hãy dùng TriStateAutoEnum để cập nhật thuộc tính nightVision của đặc điểm CameraAvStreamManagement bằng hàm setNightVision Kotlin tích hợp sẵn:

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

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

Thay đổi độ sáng của đèn LED trạng thái

Để thay đổi độ sáng của đèn LED trạng thái, hãy dùng ThreeLevelAutoEnum để cập nhật thuộc tính statusLightBrightness của đặc điểm CameraAvStreamManagement bằng hàm setStatusLightBrightness Kotlin tích hợp:

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

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

Thay đổi khung nhìn camera

Khung hiển thị camera giống với tính năng Thu phóng và cắt mà bạn có thể xem trong bài viết hỗ trợ Thu phóng và cải thiện video của camera Nest.

Khung hiển thị được xác định trong một ViewportStruct chứa 4 giá trị, được dùng làm toạ độ của khung hiển thị. Toạ độ được xác định như sau:

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

Việc xác định các giá trị cho ViewportStruct phụ thuộc vào giao diện người dùng và cách triển khai camera của ứng dụng. Ở cấp độ rất cơ bản, để đặt khung hiển thị của video trên camera, hãy cập nhật thuộc tính viewport của đặc điểm CameraAvStreamManagement bằng ViewportStruct, sử dụng hàm setViewport Kotlin tích hợp sẵn:

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

Điều chỉnh độ nhạy đánh thức thiết bị

Độ nhạy đánh thức của thiết bị được dùng để tiết kiệm pin bằng cách giảm phạm vi mà thiết bị có thể cảm nhận được hoạt động và tăng thời gian đánh thức sau khi phát hiện thấy hoạt động đó.

Trong Home API, bạn có thể thiết lập chế độ này bằng cách dùng thuộc tính motionSensitivity của triggerOptions trong transportOptions của thiết bị. Các lựa chọn này được xác định trong đặc điểm PushAvStreamTransport cho từng thiết bị.

Bạn chỉ có thể đặt độ nhạy đánh thức thành các giá trị sau:

  • 1 = Thấp
  • 5 = Trung bình
  • 10 = Cao

Quy trình cập nhật là tìm cấu hình truyền tải cho các luồng ghi đang hoạt động bằng lệnh findTransport, sau đó sửa đổi cấu hình bằng giá trị độ nhạy mới bằng lệnh 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,
      )
    }
  }

Điều chỉnh thời lượng tối đa của sự kiện

Thời lượng tối đa của sự kiện là khoảng thời gian camera sẽ ghi lại một đoạn video cho một sự kiện. Thông qua Home API, bạn có thể định cấu hình giá trị này cho từng thiết bị với cùng độ dài như thông qua GHA, theo khoảng thời gian tính bằng giây:

  • 10 giây
  • 15 giây
  • 30 giây
  • 60 giây (1 phút)
  • 120 giây (2 phút)
  • 180 giây (3 phút)

Trong Home API, bạn có thể thiết lập chế độ này bằng cách dùng thuộc tính motionTimeControl của triggerOptions trong transportOptions của thiết bị. Các lựa chọn này được xác định trong đặc điểm PushAvStreamTransport cho từng thiết bị.

Quy trình cập nhật là tìm cấu hình truyền tải cho các luồng ghi đang hoạt động bằng lệnh findTransport, sau đó sửa đổi cấu hình bằng giá trị độ dài sự kiện mới bằng lệnh 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,
      )
    }
  }

Cài đặt chuông

Bạn có thể kiểm soát nhiều chế độ cài đặt chuông cửa thông qua Home API.

Thay đổi âm thanh chuông

Để thay đổi âm thanh chuông cửa, trước tiên, hãy lấy danh sách âm thanh chuông được cài đặt trên thiết bị bằng cách sử dụng thuộc tính installedChimeSounds của đặc điểm 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()

Sau đó, hãy cập nhật thuộc tính selectedChime của đặc điểm Chime bằng cách sử dụng hàm setSelectedChime Kotlin tích hợp sẵn:

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

Sử dụng chuông báo bên ngoài

Bạn có thể định cấu hình chuông cửa để sử dụng chuông bên ngoài, chẳng hạn như chuông cơ được lắp đặt bên trong nhà. Bạn nên định cấu hình chế độ này trong quá trình lắp đặt chuông cửa để tránh làm hỏng chuông báo bên ngoài.

Để cho biết loại chuông bên ngoài đã được lắp đặt, hãy dùng ExternalChimeType để cập nhật thuộc tính externalChime của đặc điểm Chime bằng hàm setExternalChime Kotlin tích hợp sẵn:

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

Thay đổi thời lượng của chuông bên ngoài

Bạn có thể định cấu hình khoảng thời gian (tính bằng giây) mà chuông bên ngoài đổ chuông thông qua Home API. Nếu chuông bên ngoài hỗ trợ thời lượng đổ chuông, thì người dùng có thể muốn định cấu hình thời lượng này.

Giá trị được đặt ở đây phụ thuộc vào thông số kỹ thuật của chính chuông bên ngoài và thời lượng chuông được đề xuất.

Để thay đổi thời lượng chuông bên ngoài, hãy cập nhật thuộc tính externalChimeDurationSeconds của đặc điểm Chime bằng hàm setExternalChimeDurationSeconds tích hợp sẵn của Kotlin:

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

Bật một chủ đề chuông báo

Một số chuông cửa có thể có chuông báo chỉ được cung cấp cho người dùng trong một khoảng thời gian giới hạn. Ví dụ: chuông báo dành riêng cho các ngày lễ. Đây được gọi là chủ đề chuông báo.

Để xem những chủ đề chuông báo mà người dùng có thể sử dụng, hãy tạo một bộ lọc timebox và dùng bộ lọc này để lọc kết quả của lệnh getAvailableThemes() từ đặc điểm ChimeThemes. Thao tác này sẽ trả về danh sách các giao diện có sẵn, bao gồm cả tên giao diện.

Ví dụ sau đây cho thấy cách lọc danh sách. Một chủ đề được coi là đang hoạt động nếu thời gian hiện tại nằm trong khoảng thời gian bắt đầu và kết thúc của chủ đề đó (tương ứng là giá trị startTimeSecondsendTimeSeconds). Nếu bạn không đặt thời gian bắt đầu, thì thiết bị sẽ được coi là đang hoạt động từ đầu. Nếu bạn không đặt thời gian kết thúc, thì quy tắc sẽ tiếp tục hoạt động vô thời hạn. Nếu thiếu cả hai, giao diện sẽ luôn hoạt động.

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

Sau khi có tên của giao diện bạn muốn, chẳng hạn như Christmas, bạn có thể chọn giao diện đó bằng hàm setSelectedTimeboxedThemeName() trên đặc điểm ChimeThemes:

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