Kiểm tra xem một đặc điểm có hỗ trợ một lệnh hay không
Bạn cũng có thể kiểm tra xem một lệnh đặc điểm có được hỗ trợ hay không. Bạn cũng có thể dùng hàm supports
ở cấp đặc điểm để kiểm tra xem một lệnh có được hỗ trợ cho một thiết bị cụ thể hay không.
Ví dụ: để kiểm tra xem một thiết bị có hỗ trợ lệnh toggle
của đặc điểm Bật/tắt hay không:
// Check if the OnOff trait supports the toggle command. if (onOffTrait.supports(OnOff.Command.Toggle)) { println("onOffTrait supports toggle command") } else { println("onOffTrait does not support stateful toggle command") }
Gửi lệnh đến một thiết bị
Gửi lệnh cũng tương tự như việc đọc một thuộc tính trạng thái từ một đặc điểm. Để bật hoặc tắt thiết bị, hãy dùng lệnh Bật/tắt của đặc điểm OnOff
. Lệnh này được xác định trong mô hình dữ liệu hệ sinh thái Google Home là toggle()
. Phương thức này thay đổi onOff
thành false
nếu là true
hoặc thành true
nếu là false
:
// Calling a command on a trait. try { onOffTrait.toggle() } catch (e: HomeException) { // Code for handling the exception }
Tất cả các lệnh đặc điểm đều là hàm suspend
và chỉ hoàn tất khi API trả về một phản hồi (chẳng hạn như xác nhận rằng trạng thái thiết bị đã thay đổi).
Các lệnh có thể trả về một ngoại lệ nếu phát hiện thấy vấn đề với quy trình thực thi. Là nhà phát triển, bạn nên sử dụng khối try-catch
để xử lý đúng cách các trường hợp ngoại lệ này và cung cấp thông tin chi tiết cho người dùng trong trường hợp họ có thể thực hiện hành động để khắc phục lỗi. Các ngoại lệ chưa được xử lý sẽ dừng thời gian chạy ứng dụng và có thể dẫn đến sự cố trong ứng dụng của bạn.
Ngoài ra, hãy dùng lệnh off()
hoặc on()
để đặt trạng thái một cách rõ ràng:
onOffTrait.off() onOffTrait.on()
Sau khi gửi lệnh thay đổi trạng thái, bạn có thể đọc trạng thái như mô tả trong phần Đọc trạng thái thiết bị để xử lý trạng thái đó trong ứng dụng của mình. Ngoài ra, hãy sử dụng các luồng như mô tả trong phần Theo dõi trạng thái (đây là phương thức ưu tiên).
Gửi lệnh có tham số
Một số lệnh có thể sử dụng các tham số, chẳng hạn như các tham số trên đặc điểm OnOff
hoặc LevelControl
:
offWithEffect
// Turn off the light using the DyingLight effect. onOffTrait.offWithEffect( effectIdentifier = OnOffTrait.EffectIdentifierEnum.DyingLight, effectVariant = 0u, )
moveToLevel
// Change the brightness of the light to 50% levelControlTrait.moveToLevel( level = 127u.toUByte(), transitionTime = null, optionsMask = LevelControlTrait.OptionsBitmap(), optionsOverride = LevelControlTrait.OptionsBitmap(), )
Một số lệnh có đối số không bắt buộc, xuất hiện sau các đối số bắt buộc.
Ví dụ: lệnh step
cho đặc điểm FanControl
có 2 đối số không bắt buộc:
val fanControlTraitFlow: Flow<FanControl?> = device.type(FanDevice).map { it.standardTraits.fanControl }.distinctUntilChanged() val fanControl = fanControlTraitFlow.firstOrNull() // Calling a command with optional parameters not set. fanControl?.step(direction = FanControlTrait.StepDirectionEnum.Increase) // Calling a command with optional parameters. fanControl?.step(direction = FanControlTrait.StepDirectionEnum.Increase) { wrap = true }
Kiểm tra xem một đặc điểm có hỗ trợ một thuộc tính hay không
Một số thiết bị có thể hỗ trợ một đặc điểm Matter, nhưng không hỗ trợ một thuộc tính cụ thể. Ví dụ: thiết bị Cloud-to-cloud được liên kết với Matter có thể không hỗ trợ mọi thuộc tính Matter. Để xử lý những trường hợp như thế này, hãy sử dụng hàm supports
ở cấp đặc điểm và enum Attribute
của đặc điểm để kiểm tra xem thuộc tính có được hỗ trợ cho một thiết bị cụ thể hay không.
Ví dụ: để kiểm tra xem thiết bị có hỗ trợ thuộc tính onOff
của đặc điểm Bật/tắt hay không:
// Check if the OnOff trait supports the onOff attribute. if (onOffTrait.supports(OnOff.Attribute.onOff)) { println("onOffTrait supports onOff state") } else { println("onOffTrait is for a command only device!") }
Một số thuộc tính có thể rỗng trong quy cách Matter hoặc giản đồ Cloud-to-cloud smart home. Đối với các thuộc tính này, bạn có thể xác định xem giá trị null do thuộc tính trả về là do thiết bị không báo cáo giá trị đó hay giá trị của thuộc tính thực sự là null
, bằng cách sử dụng isNullable
ngoài supports
:
// Check if a nullable attribute is set or is not supported. if (onOffTrait.supports(OnOff.Attribute.startUpOnOff)) { // The device supports startupOnOff, it is safe to expect this value in the trait. if (OnOff.Attribute.startUpOnOff.isNullable && onOffTrait.startUpOnOff == null) { // This value is nullable and set to null. Check the specification as to // what null in this case means println("onOffTrait supports startUpOnOff and it is null") } else { // This value is nullable and set to a value. println("onOffTrait supports startUpOnOff and it is set to ${onOffTrait.startUpOnOff}") } } else { println("onOffTrait does not support startUpOnOff!") }
Cập nhật các thuộc tính đặc điểm
Nếu bạn muốn thay đổi giá trị của một thuộc tính nhất định và không có lệnh nào của đặc điểm làm như vậy, thì thuộc tính đó có thể hỗ trợ việc đặt giá trị một cách rõ ràng.
Việc bạn có thể thay đổi giá trị của một thuộc tính hay không phụ thuộc vào 2 yếu tố:
- Thuộc tính này có thể ghi không?
- Giá trị của thuộc tính có thể thay đổi do tác dụng phụ của việc gửi lệnh đặc điểm không?
Tài liệu tham khảo về các đặc điểm và thuộc tính của đặc điểm cung cấp thông tin này.
Do đó, các tổ hợp thuộc tính quy định cách giá trị của một thuộc tính có thể thay đổi là:
Chỉ đọc và không bị ảnh hưởng bởi các lệnh khác. Điều này có nghĩa là giá trị của thuộc tính không thay đổi. Ví dụ: thuộc tính
currentPosition
của đặc điểmSwitch
.Chỉ đọc và chịu ảnh hưởng của các lệnh khác. Điều này có nghĩa là cách duy nhất để giá trị của thuộc tính có thể thay đổi là do gửi một lệnh. Ví dụ: thuộc tính
currentLevel
của đặc điểmLevelControl
Matter chỉ có thể đọc, nhưng giá trị của thuộc tính này có thể bị thay đổi bởi các lệnh nhưmoveToLevel
.Có thể ghi và không bị ảnh hưởng bởi các lệnh khác. Điều này có nghĩa là bạn có thể trực tiếp thay đổi giá trị của thuộc tính bằng cách sử dụng hàm
update
của đặc điểm, nhưng không có lệnh nào ảnh hưởng đến giá trị của thuộc tính. Ví dụ: thuộc tínhWrongCodeEntryLimit
của đặc điểmDoorLock
.Có thể ghi và chịu ảnh hưởng của các lệnh khác. Điều này có nghĩa là bạn có thể trực tiếp thay đổi giá trị của thuộc tính bằng cách sử dụng hàm
update
của đặc điểm và giá trị của thuộc tính có thể thay đổi do gửi một lệnh. Ví dụ: bạn có thể ghi trực tiếp thuộc tínhspeedSetting
củaFanControlTrait
, nhưng cũng có thể thay đổi bằng lệnhstep
.
Ví dụ về cách dùng hàm cập nhật để thay đổi giá trị của một thuộc tính
Ví dụ này cho thấy cách đặt giá trị của thuộc tính DoorLockTrait.WrongCodeEntryLimit
một cách rõ ràng.
Để đặt giá trị thuộc tính, hãy gọi hàm update
của đặc điểm và truyền cho hàm đó một hàm đột biến để đặt giá trị mới.
Bạn nên xác minh rằng đặc điểm hỗ trợ một thuộc tính.
Ví dụ:
val doorLockDevice = home.devices().list().first { device -> device.has(DoorLock) } val traitFlow: Flow<DoorLock?> = doorLockDevice.type(DoorLockDevice).map { it.standardTraits.doorLock }.distinctUntilChanged() val doorLockTrait: DoorLock = traitFlow.first()!! if (doorLockTrait.supports(DoorLock.Attribute.wrongCodeEntryLimit)) { val unused = doorLockTrait.update { setWrongCodeEntryLimit(3u) } }
Gửi nhiều lệnh cùng lúc
Batching API cho phép một ứng dụng gửi nhiều lệnh thiết bị Home API trong một tải trọng duy nhất. Các lệnh được gộp thành một tải trọng duy nhất và thực thi song song, tương tự như cách người dùng có thể tạo quy trình tự động hoá Home API bằng nút song song, chẳng hạn như ví dụ Mở rèm trước khi mặt trời mọc. Tuy nhiên, Batching API cho phép các hành vi phức tạp và tinh vi hơn so với Automation API, chẳng hạn như khả năng chọn thiết bị một cách linh hoạt trong thời gian chạy theo bất kỳ tiêu chí nào.
Các lệnh trong một lô có thể nhắm đến nhiều đặc điểm trên nhiều thiết bị, trong nhiều phòng, trong nhiều cấu trúc.
Việc gửi các lệnh theo lô cho phép các thiết bị thực hiện đồng thời các thao tác. Điều này không thực sự có thể thực hiện được khi các lệnh được gửi tuần tự trong các yêu cầu riêng biệt. Hành vi đạt được bằng cách sử dụng các lệnh theo lô cho phép nhà phát triển đặt trạng thái của một nhóm thiết bị sao cho khớp với trạng thái tổng hợp được xác định trước.
Sử dụng Batching API
Có 3 bước cơ bản liên quan đến việc gọi các lệnh thông qua Batching API:
- Gọi phương thức
Home.sendBatchedCommands()
. - Trong phần nội dung của khối
sendBatchedCommands()
, hãy chỉ định các lệnh sẽ được đưa vào lô. - Kiểm tra kết quả của các lệnh đã gửi để xem lệnh có thành công hay không.
Gọi phương thức sendBatchedCommands()
Gọi phương thức Home.sendBatchedCommands()
. Trên thực tế, phương thức này thiết lập một biểu thức lambda trong một ngữ cảnh hàng loạt đặc biệt.
home.sendBatchedCommands() {
Chỉ định các lệnh hàng loạt
Trong phần nội dung của khối sendBatchedCommands()
, hãy điền sẵn các lệnh có thể xử lý theo lô. Các lệnh có thể xử lý theo lô là phiên bản "bóng" của các lệnh Device API hiện có có thể dùng trong ngữ cảnh lô và được đặt tên bằng hậu tố Batchable
. Ví dụ: lệnh moveToLevel()
của đặc điểm LevelControl
có một lệnh tương ứng tên là moveToLevelBatchable()
.
Ví dụ:
val response1 = add(command1)
val response2 = add(command2)
Lệnh này sẽ tự động được gửi sau khi tất cả các lệnh đã được thêm vào ngữ cảnh hàng loạt và quá trình thực thi đã thoát khỏi ngữ cảnh.
Các phản hồi được ghi lại trong các đối tượng DeferredResponse<T>
.
Các thực thể DeferredResponse<T>
có thể được thu thập vào một đối tượng thuộc bất kỳ loại nào, chẳng hạn như Collection
hoặc một lớp dữ liệu mà bạn xác định. Bất kể loại đối tượng nào bạn chọn để tập hợp các phản hồi, đó là những gì được trả về bởi sendBatchedCommands()
. Ví dụ: bối cảnh hàng loạt có thể trả về 2 thực thể DeferredResponse
trong một Pair
:
val (response1, response2) = homeClient.sendBatchedComamnds {
val response1 = add(someCommandBatched(...))
val response2 = add(someOtherCommandBatched(...))
Pair(response1, response2)
}
Ngoài ra, ngữ cảnh hàng loạt có thể trả về các phiên bản DeferredResponse
trong một lớp dữ liệu tuỳ chỉnh:
// Custom data class
data class SpecialResponseHolder(
val response1: DeferredResponse<String>,
val response2: DeferredResponse<Int>,
val other: OtherResponses
)
data class OtherResponses(...)
Kiểm tra từng câu trả lời
Bên ngoài khối sendBatchedCommands()
, hãy kiểm tra các phản hồi để xác định xem lệnh tương ứng có thành công hay không. Việc này được thực hiện bằng cách gọi DeferredResponse.getOrThrow()
, có thể:
– trả về kết quả của lệnh đã thực thi,
– hoặc, nếu phạm vi hàng loạt chưa hoàn tất hoặc lệnh không thành công, sẽ gửi một lỗi.
Bạn chỉ nên kiểm tra kết quả bên ngoài phạm vi lambda sendBatchedCommands()
.
Ví dụ:
Giả sử bạn muốn tạo một ứng dụng dùng Batching API để thiết lập một cảnh "chúc ngủ ngon" nhằm định cấu hình tất cả các thiết bị trong nhà cho ban đêm, khi mọi người đều ngủ. Ứng dụng này sẽ tắt đèn và khoá cửa trước cũng như cửa sau.
Sau đây là một cách tiếp cận nhiệm vụ này:
val lightDevices: List<OnOffLightDevice>
val doorlockDevices: List<DoorLockDevice>
// Send all the commands
val responses: List<DeferredResponse<Unit>> = home.sendBatchedCommands {
// For each light device, send a Batchable command to turn it on
val lightResponses: List<DeferredResponse<Unit>> = lightDevices.map { lightDevice ->
add(lightDevice.standardTraits.onOff.onBatchable())
}
// For each doorlock device, send a Batchable command to lock it
val doorLockResponse: List<DeferredResponse<Unit>> = doorlockDevices.map { doorlockDevice ->
add(doorlockDevice.standardTraits.doorLock.lockDoorBatchable())
}
lightResponses + doorLockResponses
}
// Check that all responses were successful
for (response in responses) {
response.getOrThrow()
}