ตรวจสอบว่าลักษณะเฉพาะรองรับคำสั่งหรือไม่
สามารถตรวจสอบการรองรับคำสั่งลักษณะเฉพาะได้ ใช้ฟังก์ชัน supports ระดับลักษณะเฉพาะเพื่อตรวจสอบว่าคำสั่งได้รับการรองรับสำหรับอุปกรณ์เฉพาะหรือไม่
ตัวอย่างเช่น ในการตรวจสอบการรองรับคำสั่ง toggle ของลักษณะเปิด/ปิดของอุปกรณ์:
// 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") }
ส่งคำสั่งไปยังอุปกรณ์
การส่งคำสั่งจะคล้ายกับการอ่านแอตทริบิวต์สถานะจากลักษณะ หากต้องการเปิดหรือปิดอุปกรณ์ ให้ใช้คำสั่งสลับของลักษณะ OnOff
ซึ่งกำหนดไว้ในโมเดลข้อมูลของระบบนิเวศ Google Home เป็น toggle() วิธีการนี้จะเปลี่ยน onOff เป็น false ถ้าเป็น true หรือเป็น true ถ้าเป็น false:
// Calling a command on a trait. try { onOffTrait.toggle() } catch (e: HomeException) { // Code for handling the exception }
คำสั่งลักษณะทั้งหมดเป็นฟังก์ชัน suspend และจะเสร็จสมบูรณ์เมื่อ API ส่งการตอบกลับ
กลับมา (เช่น การยืนยันว่าสถานะของอุปกรณ์มีการเปลี่ยนแปลง)
คำสั่งอาจส่งคืนข้อยกเว้นหากตรวจพบปัญหาในกระแสการดำเนินการ ในฐานะนักพัฒนา คุณควรใช้บล็อก try-catch เพื่อจัดการข้อยกเว้นเหล่านี้อย่างเหมาะสม และแสดงข้อมูลโดยละเอียดแก่ผู้ใช้ในกรณีที่สามารถดำเนินการแก้ไขข้อผิดพลาดได้ ข้อยกเว้นที่ไม่ได้จัดการจะหยุดรันไทม์ของแอปและ
อาจส่งผลให้แอปขัดข้อง
หรือใช้คำสั่ง off() หรือ on() เพื่อตั้งค่าสถานะอย่างชัดเจน:
onOffTrait.off() onOffTrait.on()
หลังจากส่งคำสั่งเพื่อเปลี่ยนสถานะแล้ว เมื่อดำเนินการเสร็จสิ้น คุณจะอ่านสถานะได้ตามที่อธิบายไว้ในอ่านสถานะอุปกรณ์เพื่อจัดการในแอป หรือจะใช้โฟลว์ตามที่อธิบายไว้ในสังเกตสถานะก็ได้ ซึ่งเป็นวิธีที่แนะนำ
ส่งคำสั่งพร้อมพารามิเตอร์
คำสั่งบางอย่างอาจใช้พารามิเตอร์ เช่น คำสั่งในลักษณะ OnOff หรือ 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(), )
คำสั่งบางอย่างมีอาร์กิวเมนต์ที่ไม่บังคับซึ่งจะอยู่หลังอาร์กิวเมนต์ที่บังคับ
ตัวอย่างเช่น คำสั่ง step สำหรับลักษณะ FanControl มีอาร์กิวเมนต์ตัวเลือกสองตัว:
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 }
ตรวจสอบว่าลักษณะเฉพาะรองรับแอตทริบิวต์หรือไม่
อุปกรณ์บางอย่างอาจรองรับคุณลักษณะ Matter แต่ไม่ใช่แอตทริบิวต์เฉพาะ เช่น Cloud-to-cloudอุปกรณ์ที่แมปMatterอาจไม่รองรับแอตทริบิวต์Matterทั้งหมด หากต้องการจัดการกรณีเช่นนี้ ให้ใช้ฟังก์ชัน supports ระดับลักษณะและ Enum ของลักษณะ Attribute เพื่อตรวจสอบว่าอุปกรณ์หนึ่งๆ รองรับแอตทริบิวต์หรือไม่
ตัวอย่างเช่น ในการตรวจสอบการรองรับคุณลักษณะ onOff ของอุปกรณ์:
// 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!") }
แอตทริบิวต์บางอย่างสามารถกำหนดค่าเป็นค่าว่างได้ในข้อกำหนด Matter หรือรูปแบบ Cloud-to-cloud smart home สำหรับแอตทริบิวต์เหล่านี้ คุณสามารถระบุได้ว่าค่า null ที่ส่งคืนโดยแอตทริบิวต์นั้นเกิดจากอุปกรณ์ไม่รายงานค่าดังกล่าวหรือไม่ หรือค่าแอตทริบิวต์นั้นเป็น null จริงหรือไม่ โดยใช้ isNullable ร่วมกับ 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!") }
อัปเดตคุณลักษณะคุณลักษณะ
หากคุณต้องการเปลี่ยนค่าของแอตทริบิวต์ที่กำหนด และไม่มีคำสั่งใดของคุณลักษณะนั้นทำได้ แอตทริบิวต์อาจรองรับการกำหนดค่าอย่างชัดเจน
การที่จะเปลี่ยนค่าแอตทริบิวต์ได้หรือไม่นั้น ขึ้นอยู่กับปัจจัย 2 ประการ ดังนี้
- คุณสมบัติสามารถเขียนได้หรือไม่?
- ค่าแอตทริบิวต์สามารถเปลี่ยนแปลงได้หรือไม่เป็นผลข้างเคียงของการส่งคำสั่งลักษณะเฉพาะ?
เอกสารอ้างอิงสำหรับลักษณะและคุณลักษณะของลักษณะเหล่านั้นให้ข้อมูลนี้
ดังนั้น การรวมกันของคุณสมบัติที่กำหนดว่าค่าแอตทริบิวต์อาจเปลี่ยนแปลงได้อย่างไร มีดังนี้:
อ่านอย่างเดียวและไม่ได้รับผลกระทบจากคำสั่งอื่น ซึ่งหมายความว่าค่าแอตทริบิวต์จะไม่เปลี่ยนแปลง ตัวอย่างเช่น
currentPositionคุณลักษณะของSwitchลักษณะนิสัย -อ่านอย่างเดียวและได้รับผลกระทบจากคำสั่งอื่น ซึ่งหมายความว่าวิธีเดียวที่จะเปลี่ยนค่าแอตทริบิวต์ได้ก็คือโดยส่งคำสั่งเท่านั้น ตัวอย่างเช่น แอตทริบิวต์
currentLevelของลักษณะLevelControlMatter จะเป็นแบบอ่านอย่างเดียว แต่ค่าของแอตทริบิวต์นี้สามารถเปลี่ยนแปลงได้โดยใช้คำสั่ง เช่นmoveToLevelเขียนได้และไม่ได้รับผลกระทบ โดยคำสั่งอื่น ๆ ซึ่งหมายความว่าคุณสามารถเปลี่ยนค่าแอตทริบิวต์ได้โดยตรงโดยใช้ฟังก์ชัน
updateของลักษณะ แต่ไม่มีคำสั่งใดที่จะส่งผลต่อค่าแอตทริบิวต์ ตัวอย่างเช่นWrongCodeEntryLimitคุณลักษณะของDoorLockลักษณะนิสัย -สามารถเขียนได้และได้รับผลกระทบจากคำสั่งอื่น ซึ่งหมายความว่าคุณสามารถ เปลี่ยนค่าของแอตทริบิวต์ได้โดยตรงโดยใช้ฟังก์ชัน
updateของลักษณะ และค่าของแอตทริบิวต์อาจเปลี่ยนแปลงได้อันเป็นผลมาจากการ ส่งคำสั่ง ตัวอย่างเช่น แอตทริบิวต์speedSettingของFanControlTraitสามารถเขียนได้โดยตรง แต่ยังสามารถเปลี่ยนแปลงได้โดยใช้คำสั่งstepได้อีกด้วย
ตัวอย่างการใช้ฟังก์ชันอัปเดตเพื่อเปลี่ยนค่าของแอตทริบิวต์
ตัวอย่างนี้แสดงวิธีการตั้งค่าค่าของอย่างชัดเจนDoorLockTrait.WrongCodeEntryLimit คุณลักษณะ -
หากต้องการตั้งค่าแอตทริบิวต์ ให้เรียกใช้ฟังก์ชัน update ของลักษณะ และส่งฟังก์ชัน Mutator ที่ตั้งค่าใหม่
แนวทางปฏิบัติที่ดีคือการยืนยันว่าลักษณะรองรับแอตทริบิวต์ก่อน
เช่น
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) } }
ส่งคำสั่งหลายรายการพร้อมกัน
Batching API ช่วยให้ไคลเอ็นต์ส่งคำสั่งอุปกรณ์ Home API หลายรายการในเพย์โหลดเดียวได้ คำสั่งจะถูกแบ่งเป็นชุดเป็นเพย์โหลดเดียวและดำเนินการแบบคู่ขนาน ซึ่งคล้ายกับวิธีที่เราอาจสร้างระบบอัตโนมัติของ Home API โดยใช้โหนดคู่ขนาน เช่น ตัวอย่างเปิดม่านก่อนพระอาทิตย์ขึ้น อย่างไรก็ตาม Batching API อนุญาตให้มีลักษณะการทำงานที่ซับซ้อนและซับซ้อนกว่า Automation API เช่น ความสามารถในการเลือกอุปกรณ์แบบไดนามิกในขณะรันไทม์ตามเกณฑ์ใดก็ได้
คำสั่งในกลุ่มเดียวสามารถกำหนดเป้าหมายลักษณะหลายอย่างในอุปกรณ์หลายเครื่อง ในหลายห้อง ในหลายโครงสร้าง
การส่งคำสั่งเป็นกลุ่มช่วยให้อุปกรณ์ดำเนินการพร้อมกันได้ ซึ่งทำไม่ได้เมื่อส่งคำสั่งตามลำดับในคำขอแยกกัน ลักษณะการทำงานที่ได้จากการใช้คำสั่งแบบเป็นชุดช่วยให้นักพัฒนาซอฟต์แวร์ ตั้งค่าสถานะของกลุ่มอุปกรณ์ให้ตรงกับสถานะรวมที่กำหนดไว้ล่วงหน้าได้
ใช้ Batching API
ขั้นตอนพื้นฐาน 3 ขั้นตอนที่เกี่ยวข้องกับการเรียกใช้คำสั่งผ่าน Batching API มีดังนี้
- เรียกใช้เมธอด
Home.sendBatchedCommands() - ภายในเนื้อหาของบล็อก
sendBatchedCommands()ให้ระบุคำสั่งที่จะรวมไว้ในกลุ่ม - ตรวจสอบผลลัพธ์ของคำสั่งที่ส่งเพื่อดูว่าคำสั่งทำงานสำเร็จหรือ ล้มเหลว
เรียกใช้เมธอด sendBatchedCommands()
เรียกใช้เมธอด Home.sendBatchedCommands() เบื้องหลัง วิธีนี้จะตั้งค่านิพจน์ Lambda ในบริบทของกลุ่มพิเศษ
home.sendBatchedCommands() {
ระบุคำสั่งแบบกลุ่ม
ในส่วนเนื้อหาของบล็อก sendBatchedCommands() ให้ป้อนคำสั่งที่จัดกลุ่มได้ คำสั่งที่จัดกลุ่มได้คือคำสั่ง Device API
เวอร์ชัน "เงา" ที่มีอยู่ซึ่งใช้ในบริบทของกลุ่มได้ และมีชื่อที่ลงท้ายด้วย Batchable เช่น คำสั่ง
LevelControl
ลักษณะ
moveToLevel()
มีคำสั่งที่คล้ายกันชื่อ
moveToLevelBatchable()
ตัวอย่าง
val response1 = add(command1)
val response2 = add(command2)
ระบบจะส่งชุดคำสั่งโดยอัตโนมัติเมื่อเพิ่มคำสั่งทั้งหมดลงใน บริบทของชุดคำสั่งและดำเนินการออกจากบริบทแล้ว
การตอบสนองจะถูกบันทึกไว้ในอ็อบเจ็กต์ DeferredResponse<T>
อินสแตนซ์ DeferredResponse<T>
สามารถรวบรวมเป็นออบเจ็กต์ประเภทใดก็ได้ เช่น Collection หรือคลาสข้อมูลที่คุณกำหนด ไม่ว่าคุณจะเลือกประกอบคำตอบด้วยออบเจ็กต์ประเภทใด sendBatchedCommands() จะส่งคืนออบเจ็กต์ประเภทนั้น ตัวอย่างเช่น บริบทของกลุ่มสามารถแสดงผลอินสแตนซ์ DeferredResponse 2 รายการใน Pair ได้ดังนี้
val (response1, response2) = homeClient.sendBatchedComamnds {
val response1 = add(someCommandBatched(...))
val response2 = add(someOtherCommandBatched(...))
Pair(response1, response2)
}
หรือบริบทกลุ่มสามารถแสดงผลอินสแตนซ์ DeferredResponse
ในคลาสข้อมูลที่กำหนดเองได้ดังนี้
// Custom data class
data class SpecialResponseHolder(
val response1: DeferredResponse<String>,
val response2: DeferredResponse<Int>,
val other: OtherResponses
)
data class OtherResponses(...)
ตรวจสอบคำตอบแต่ละรายการ
นอกบล็อก sendBatchedCommands() ให้ตรวจสอบการตอบกลับเพื่อดูว่าคำสั่งที่เกี่ยวข้องสำเร็จหรือไม่ โดยทำได้ด้วยการเรียกใช้
DeferredResponse.getOrThrow() ซึ่งจะ
- แสดงผลลัพธ์ของคำสั่งที่ดำเนินการ
- หรือหากขอบเขตของกลุ่มยังไม่เสร็จสมบูรณ์หรือคำสั่งไม่สำเร็จ
ระบบจะแสดงข้อผิดพลาด
คุณควรตรวจสอบผลลัพธ์ภายนอกsendBatchedCommands()ขอบเขตของ Lambda เท่านั้น
ตัวอย่าง
สมมติว่าคุณต้องการสร้างแอปที่ใช้ Batching API เพื่อตั้งค่าฉาก "ราตรีสวัสดิ์" ที่กำหนดค่าอุปกรณ์ทั้งหมดในบ้านสำหรับเวลากลางคืนเมื่อทุกคนหลับ แอปนี้ควรปิดไฟและล็อกประตูหน้า และประตูหลัง
วิธีหนึ่งในการทำงานนี้มีดังนี้
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()
}