ตรวจสอบว่าลักษณะรองรับคำสั่งหรือไม่
นอกจากนี้ คุณยังตรวจสอบการรองรับคำสั่งลักษณะได้ด้วย นอกจากนี้ ให้ใช้ฟังก์ชัน 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
ลักษณะ
มีอาร์กิวเมนต์ที่ไม่บังคับ 2 รายการ ดังนี้
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!") }
แอตทริบิวต์บางรายการเป็นค่า Null ได้ในข้อกำหนด 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 function ของลักษณะและส่งฟังก์ชัน 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
การเรียกใช้คำสั่งผ่าน Batching API มีขั้นตอนพื้นฐาน 3 ขั้นตอนดังนี้
- เรียกใช้เมธอด
Home.sendBatchedCommands() - ภายในเนื้อความของบล็อก
sendBatchedCommands()ให้ระบุคำสั่งที่จะรวมไว้ในกลุ่ม - ตรวจสอบผลลัพธ์ของคำสั่งที่ส่งเพื่อดูว่าสำเร็จหรือล้มเหลว
ส่งคำสั่งแบบกลุ่ม
เรียกใช้เมธอด Home.sendBatchedCommands() เบื้องหลังการทำงาน วิธีนี้จะตั้งค่านิพจน์ Lambda ในบริบทกลุ่มพิเศษ
home.sendBatchedCommands() {
ระบุคำสั่งแบบกลุ่ม
ในส่วนเนื้อหาของบล็อก sendBatchedCommands() ให้ป้อนคำสั่งที่รวมเป็นกลุ่มได้ คำสั่งที่จัดกลุ่มได้คือคำสั่ง Device API ที่มีอยู่ซึ่งเป็นเวอร์ชัน "Shadow" ซึ่งใช้ในบริบทกลุ่มได้ และตั้งชื่อโดยเพิ่มคำต่อท้าย Batchable ตัวอย่างเช่น คำสั่ง moveToLevel() ของลักษณะ LevelControl มีคำสั่งที่ทำงานร่วมกันชื่อ 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() เท่านั้น
ตัวอย่าง
สมมติว่าคุณต้องการสร้างแอปที่ใช้ 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()
}
ควบคุมกลุ่มอุปกรณ์
นอกจากการควบคุมแบบละเอียดสำหรับสมาชิกทุกรายในกลุ่มด้วยการทำซ้ำผ่านอุปกรณ์ของสมาชิกแล้ว คุณยังควบคุม DeviceGroup เป็นเอนทิตีเดียวได้ด้วย DeviceGroup จะใช้อินเทอร์เฟซ PartsProvider ซึ่งแสดงส่วนต่างๆ ของ DeviceType ที่อุปกรณ์ในกลุ่มรองรับ ดังนี้
group.parts()จะแสดงผลFlow<Set<DeviceType>>ของอุปกรณ์ทุกประเภทที่มีอยู่ในกลุ่มgroup.part()และgroup.partOrNull()จะแสดงผลโฟลว์สำหรับประเภทอุปกรณ์ที่เฉพาะเจาะจง เช่นOnOffLightDeviceหรือDimmableLightDevice
เมื่อคุณเรียกใช้คำสั่งลักษณะในประเภทอุปกรณ์ที่เป็นส่วนหนึ่งของ DeviceGroup SDK จะส่งต่อคำสั่งไปยังอุปกรณ์สมาชิกที่เกี่ยวข้องทั้งหมดในกลุ่มที่รองรับความสามารถนั้น โดยไม่คำนึงถึงการเชื่อมต่อหรือการรับส่งข้อมูลที่สำคัญ (เช่น Matter หรือ Cloud-to-cloud)
แอตทริบิวต์ลักษณะของส่วนประกอบกลุ่มและการจัดการข้อผิดพลาด
การกำหนดเป้าหมายส่วนของกลุ่มจะช่วยลดความซับซ้อนของกรณีทั่วไปที่คุณต้องการส่งคำสั่งแบบ Idempotent (เช่น การเปิดหรือปิดไฟ หรือการตั้งค่าระดับความสว่าง) ไปยังทั้งกลุ่ม เมื่อทำงานกับลักษณะและคำสั่งในส่วน DeviceGroup โปรดคำนึงถึงลักษณะการทำงานต่อไปนี้
- โดยทั่วไปแล้ว แอตทริบิวต์สถานะแบบไดนามิก (เช่น แอตทริบิวต์
onOffของลักษณะOnOffหรือแอตทริบิวต์currentLevelของลักษณะLevelControl) จะไม่ป้อนข้อมูลในลักษณะของส่วนกลุ่ม เนื่องจากอุปกรณ์ที่เป็นสมาชิกในกลุ่มอาจอยู่ในสถานะที่แตกต่างกันซึ่งไม่สามารถรวมเป็นค่าเดียวได้ อย่างไรก็ตาม ระบบจะป้อนคำสั่งที่รองรับ (ตรวจสอบด้วยsupports()เช่นLevelControl.Command.MoveToLevelWithOnOffในหลอดไฟที่หรี่แสงได้) และแอตทริบิวต์ความสามารถบางอย่าง (เช่นcolorTempPhysicalMinMiredsและcolorTempPhysicalMaxMiredsในColorControlสำหรับหลอดไฟสี) เพื่อให้แอปของคุณกำหนดค่าตัวควบคุม UI ได้อย่างเหมาะสม - คำสั่งบางรายการเกี่ยวกับลักษณะของสมาชิกกลุ่มอาจใช้ไม่ได้กับส่วนของกลุ่ม โดยเฉพาะอย่างยิ่ง คำสั่งที่แสดงผลค่าจะใช้ไม่ได้
- คำสั่งในส่วนของกลุ่มจะไม่แสดงผลลัพธ์แต่ละรายการสำหรับคำสั่งอุปกรณ์ของสมาชิกที่เกี่ยวข้องแต่ละรายการ หากต้องการตรวจสอบผลลัพธ์ของคำสั่งแต่ละรายการหรือเรียกใช้คำสั่งที่แสดงผลค่า ให้ทำซ้ำผ่านอุปกรณ์สมาชิกของกลุ่มโดยตรง
- คำสั่งที่ส่งไปยังส่วนหนึ่งใน
DeviceGroupจะเสร็จสมบูรณ์เมื่อการกระจายทั้งหมดในอุปกรณ์สมาชิกเสร็จสิ้น และระบบรวบรวมความสำเร็จหรือความล้มเหลวของคำสั่งที่เกี่ยวข้องทั้งหมดแล้ว คำสั่งจะดำเนินการไม่สำเร็จ (แสดงHomeException) ก็ต่อเมื่อคำสั่งดำเนินการไม่สำเร็จสำหรับอุปกรณ์สมาชิกที่เชื่อมโยงทุกเครื่อง หรือหากอุปกรณ์เป้าหมายทั้งหมดไม่พร้อมใช้งาน โดยจะแสดงเป็นคำสั่งเดียวที่ดำเนินการไม่สำเร็จซึ่งตรงกับไวยากรณ์การเรียกใช้คำสั่งเดียว หากอุปกรณ์สมาชิกอย่างน้อย 1 เครื่องดำเนินการตามคำสั่ง คำสั่งกลุ่มจะเสร็จสมบูรณ์โดยไม่มีการแสดงข้อยกเว้น
ส่งคำสั่งไปยังส่วนของกลุ่ม
วิธีเปิดไฟ OnOffLightDevice ทั้งหมดในกลุ่มอุปกรณ์
val lightPart = group.partOrNull(OnOffLightDevice).firstOrNull()
val onOffTrait = lightPart?.trait(OnOff)
if (onOffTrait?.supports(OnOff.Command.On) == true) {
onOffTrait.on()
}
นอกจากนี้ คุณยังตรวจสอบประเภทอุปกรณ์ทั้งหมดที่มีอยู่ในกลุ่มได้โดยใช้ group.parts() เพื่อกำหนดเป้าหมายลักษณะเฉพาะในอุปกรณ์ที่เกี่ยวข้องหลายประเภท (เช่น การปิดอุปกรณ์ประเภทหลอดไฟในกลุ่มที่รองรับ OnOff)
val lightTypes = listOf(
OnOffLightDevice,
DimmableLightDevice,
ColorTemperatureLightDevice,
ExtendedColorLightDevice,
)
val parts = group.parts().first()
val onOffTrait = lightTypes.firstNotNullOfOrNull { type ->
parts.firstOrNull { it.factory == type }
}?.trait(OnOff)
onOffTrait?.off()
คุณสามารถตรวจสอบการรองรับคำสั่งหรืออ่านขอบเขตความสามารถในส่วนของกลุ่มก่อนส่งคำสั่ง (เช่น ตรวจสอบ MoveToLevelWithOnOff ในส่วนของหลอดไฟที่หรี่แสงได้ หรืออ่าน colorTempPhysicalMinMireds และ colorTempPhysicalMaxMireds ในส่วนของหลอดไฟที่ปรับอุณหภูมิสีได้) ดังนี้
val dimmableLight = group.partOrNull(DimmableLightDevice).firstOrNull()
val levelControl = dimmableLight?.trait(LevelControl)
if (
levelControl?.supports(
LevelControl.Command.MoveToLevelWithOnOff
) == true
) {
levelControl.moveToLevelWithOnOff(
level = 127u,
transitionTime = null,
optionsMask = LevelControlTrait.OptionsBitmap(),
optionsOverride = LevelControlTrait.OptionsBitmap(),
)
}
val colorLight = group.partOrNull(ColorTemperatureLightDevice).firstOrNull()
val colorControl = colorLight?.trait(ColorControl)
val minMireds = colorControl?.colorTempPhysicalMinMireds
val maxMireds = colorControl?.colorTempPhysicalMaxMireds
if (colorControl != null && minMireds != null && maxMireds != null) {
val midMireds = ((minMireds.toInt() + maxMireds.toInt()) / 2).toUShort()
colorControl.moveToColorTemperature(
colorTemperatureMireds = midMireds,
transitionTime = 0u,
optionsMask = ColorControlTrait.OptionsBitmap(),
optionsOverride = ColorControlTrait.OptionsBitmap(),
)
}