1. ก่อนเริ่มต้น
นี่คือ Codelab ที่ 2 ในชุดการสร้างแอป Android โดยใช้ Google Home API ในโค้ดแล็บนี้ เราจะอธิบายวิธีสร้างการทำงานอัตโนมัติในบ้านและให้คำแนะนำเกี่ยวกับแนวทางปฏิบัติแนะนำในการใช้ API หากยังไม่ได้ทำ Codelab แรก สร้างแอปบนอุปกรณ์เคลื่อนที่โดยใช้ Home API ใน Android เราขอแนะนำให้คุณทำ Codelab นั้นให้เสร็จก่อนเริ่ม Codelab นี้
Google Home API มีชุดไลบรารีสำหรับนักพัฒนาแอป Android เพื่อควบคุมอุปกรณ์สมาร์ทโฮมภายในระบบนิเวศของ Google Home API ใหม่เหล่านี้จะช่วยให้นักพัฒนาแอปตั้งค่าการทำงานอัตโนมัติสำหรับสมาร์ทโฮมที่ควบคุมความสามารถของอุปกรณ์ตามเงื่อนไขที่กำหนดไว้ล่วงหน้าได้ นอกจากนี้ Google ยังมี Discovery API ที่ช่วยให้คุณค้นหาอุปกรณ์เพื่อดูว่าอุปกรณ์รองรับแอตทริบิวต์และคำสั่งใดบ้าง
ข้อกำหนดเบื้องต้น
- ทำ Codelab สร้างแอปบนอุปกรณ์เคลื่อนที่โดยใช้ Home API ใน Android ให้เสร็จสมบูรณ์
- ความรู้เกี่ยวกับระบบนิเวศของ Google Home (Cloud-to-Cloud และ Matter)
- เวิร์กสเตชันที่ติดตั้ง Android Studio (2024.3.1 Ladybug ขึ้นไป)
- โทรศัพท์ Android ที่เป็นไปตามข้อกำหนดของ Home API (ดูข้อกำหนดเบื้องต้น) โดยติดตั้งบริการ Google Play และแอป Google Home โปรแกรมจำลองจะใช้ไม่ได้ แอปตัวอย่างรองรับเฉพาะโทรศัพท์ Android จริงเท่านั้น
- Google Home Hub ที่รองรับซึ่งใช้ได้กับ Google Home API
- ไม่บังคับ - อุปกรณ์สมาร์ทโฮมที่ใช้ร่วมกับ Google Home API ได้
สิ่งที่คุณจะได้เรียนรู้
- วิธีสร้างการทำงานอัตโนมัติสำหรับอุปกรณ์สมาร์ทโฮมโดยใช้ Home API
- วิธีใช้ Discovery API เพื่อสำรวจความสามารถของอุปกรณ์ที่รองรับ
- วิธีใช้แนวทางปฏิบัติแนะนำเมื่อสร้างแอปด้วย Home API
2. การตั้งค่าโปรเจ็กต์
แผนภาพต่อไปนี้แสดงสถาปัตยกรรมของแอป Home APIs
- โค้ดแอป: โค้ดหลักที่นักพัฒนาแอปใช้สร้างอินเทอร์เฟซผู้ใช้ของแอปและตรรกะสำหรับการโต้ตอบกับ SDK ของ Home API
- SDK ของ Home APIs: SDK ของ Home APIs ที่ Google จัดเตรียมให้จะทำงานร่วมกับบริการ Home APIs ใน GMSCore เพื่อควบคุมอุปกรณ์สมาร์ทโฮม นักพัฒนาแอปสร้างแอปที่ทำงานร่วมกับ Home API โดยการรวมแอปเข้ากับ Home API SDK
- GMSCore ใน Android: GMSCore หรือที่เรียกว่าบริการ Google Play เป็นแพลตฟอร์มของ Google ที่ให้บริการระบบหลัก ซึ่งจะเปิดใช้ฟังก์ชันหลักในอุปกรณ์ Android ที่ผ่านการรับรองทั้งหมด โมดูลบ้านของบริการ Google Play มีบริการที่โต้ตอบกับ Home API
ในโค้ดแล็บนี้ เราจะต่อยอดจากเนื้อหาที่ครอบคลุมในสร้างแอปบนอุปกรณ์เคลื่อนที่โดยใช้ Home API ใน Android
ตรวจสอบว่าคุณมีโครงสร้างที่มีอุปกรณ์ที่รองรับอย่างน้อย 2 เครื่องซึ่งตั้งค่าและใช้งานได้ในบัญชี เนื่องจากเราจะตั้งค่าการทำงานอัตโนมัติใน Codelab นี้ (การเปลี่ยนแปลงสถานะของอุปกรณ์หนึ่งจะทริกเกอร์การดำเนินการในอีกอุปกรณ์หนึ่ง) คุณจะต้องมีอุปกรณ์ 2 เครื่องเพื่อดูผลลัพธ์
ดาวน์โหลดแอปตัวอย่าง
ซอร์สโค้ดสำหรับแอปตัวอย่างพร้อมให้บริการบน GitHub ในที่เก็บ google-home/google-home-api-sample-app-android
Codelab นี้ใช้ตัวอย่างจากสาขา codelab-branch-2
ของแอปตัวอย่าง
ไปที่ตำแหน่งที่ต้องการบันทึกโปรเจ็กต์ แล้วโคลนกิ่ง codelab-branch-2
$ git clone -b codelab-branch-2 https://github.com/google-home/google-home-api-sample-app-android.git
โปรดทราบว่านี่เป็นสาขาที่แตกต่างจากที่ใช้ในสร้างแอปบนอุปกรณ์เคลื่อนที่โดยใช้ Home API ใน Android โค้ดเบสสาขานี้สร้างขึ้นจากจุดที่ Codelab แรกสิ้นสุดลง คราวนี้ตัวอย่างจะแนะนำวิธีสร้างการทำงานอัตโนมัติ หากคุณทำ Codelab ก่อนหน้าเสร็จแล้วและสามารถทำให้ฟังก์ชันการทำงานทั้งหมดทำงานได้ คุณอาจเลือกใช้โปรเจ็กต์ Android Studio เดียวกันเพื่อทำ Codelab นี้ให้เสร็จแทนการใช้ codelab-branch-2
เมื่อคอมไพล์ซอร์สโค้ดและพร้อมที่จะเรียกใช้บนอุปกรณ์เคลื่อนที่แล้ว ให้ไปที่ส่วนถัดไป
3. ดูข้อมูลเกี่ยวกับการทำงานอัตโนมัติ
การทำงานอัตโนมัติคือชุดคำสั่ง "หากเป็นเช่นนี้ ให้ทำเช่นนั้น" ที่ควบคุมสถานะของอุปกรณ์ตามปัจจัยที่เลือกได้โดยอัตโนมัติ นักพัฒนาแอปสามารถใช้การทำงานอัตโนมัติเพื่อสร้างฟีเจอร์แบบอินเทอร์แอกทีฟขั้นสูงใน API ของตนได้
การทำงานอัตโนมัติประกอบด้วยคอมโพเนนต์ 3 ประเภทที่แตกต่างกันซึ่งเรียกว่าโหนด ได้แก่ เงื่อนไขเริ่มต้น การดำเนินการ และเงื่อนไข โหนดเหล่านี้จะทำงานร่วมกันเพื่อทำให้พฤติกรรมเป็นไปโดยอัตโนมัติโดยใช้อุปกรณ์สมาร์ทโฮม โดยปกติแล้ว ระบบจะประเมินตามลำดับต่อไปนี้
- เงื่อนไขเริ่มต้น — กำหนดเงื่อนไขเริ่มต้นที่เปิดใช้งานการทำงานอัตโนมัติ เช่น การเปลี่ยนแปลงค่าลักษณะ การทำงานอัตโนมัติต้องมีเงื่อนไขเริ่มต้น
- เงื่อนไข - ข้อจำกัดเพิ่มเติมใดๆ ที่จะประเมินหลังจากทริกเกอร์การทำงานอัตโนมัติ นิพจน์ในเงื่อนไขต้องประเมินค่าเป็นจริงเพื่อให้การดำเนินการของการทำงานอัตโนมัติทำงาน
- การดำเนินการ - คำสั่งหรือการอัปเดตสถานะที่จะดำเนินการเมื่อตรงตามเงื่อนไขทั้งหมด
เช่น คุณสามารถตั้งค่าการทำงานอัตโนมัติให้หรี่ไฟในห้องเมื่อมีการเปิด/ปิดสวิตช์ ขณะที่ทีวีในห้องนั้นเปิดอยู่ ในตัวอย่างนี้
- Starter - มีการเปิด/ปิดสวิตช์ในห้อง
- เงื่อนไข - สถานะเปิด/ปิดทีวีได้รับการประเมินเป็น "เปิด"
- การดำเนินการ - ไฟในห้องเดียวกับสวิตช์จะหรี่ลง
โดย Automation Engine จะประเมินโหนดเหล่านี้แบบอนุกรมหรือแบบขนาน
โฟลว์แบบลำดับประกอบด้วยโหนดที่ทำงานตามลำดับ โดยปกติแล้ว จะเป็นเงื่อนไขเริ่มต้น เงื่อนไข และการดำเนินการ
โฟลว์แบบขนานอาจมีโหนดการดำเนินการหลายรายการที่ทำงานพร้อมกัน เช่น การเปิดไฟหลายดวงพร้อมกัน โหนดที่อยู่หลังโฟลว์แบบขนานจะไม่ทำงานจนกว่ากิ่งก้านทั้งหมดของโฟลว์แบบขนานจะเสร็จสิ้น
นอกจากนี้ยังมีโหนดประเภทอื่นๆ ในสคีมาการทำงานอัตโนมัติ ดูข้อมูลเพิ่มเติมเกี่ยวกับอุปกรณ์เหล่านี้ได้ในส่วนโหนดของคู่มือสำหรับนักพัฒนาซอฟต์แวร์ Home APIs นอกจากนี้ นักพัฒนายังสามารถรวมโหนดประเภทต่างๆ เพื่อสร้างการทำงานอัตโนมัติที่ซับซ้อนได้ เช่น
นักพัฒนาแอปจะระบุโหนดเหล่านี้ให้กับเครื่องมืออัตโนมัติโดยใช้ภาษาเฉพาะโดเมน (DSL) ที่สร้างขึ้นสําหรับระบบอัตโนมัติของ Google Home โดยเฉพาะ
สำรวจ DSL การทำงานอัตโนมัติ
ภาษาเฉพาะโดเมน (DSL) คือภาษาที่ใช้เพื่อบันทึกพฤติกรรมของระบบในโค้ด คอมไพเลอร์จะสร้างคลาสข้อมูลที่ได้รับการซีเรียลไลซ์เป็น JSON ของ Protocol Buffer และใช้เพื่อทำการเรียกไปยังบริการอัตโนมัติของ Google
DSL จะมองหาสคีมาต่อไปนี้
automation {
name = "AutomationName"
description = "An example automation description."
isActive = true
sequential {
val onOffTrait = starter<_>(device1, OnOffLightDevice, OnOff)
condition() { expression = onOffTrait.onOff equals true }
action(device2, OnOffLightDevice) { command(OnOff.on()) }
}
}
การทำงานอัตโนมัติในตัวอย่างก่อนหน้าจะซิงค์หลอดไฟ 2 หลอด เมื่อสถานะ OnOff
ของ device1
เปลี่ยนเป็น On
(onOffTrait.onOff equals true
) ระบบจะเปลี่ยนสถานะ OnOff
ของ device2
เป็น On
(command(OnOff.on()
)
โปรดทราบว่าเมื่อใช้การทำงานอัตโนมัติจะมีขีดจำกัดของทรัพยากร
การทำงานอัตโนมัติเป็นเครื่องมือที่มีประโยชน์มากในการสร้างความสามารถอัตโนมัติในสมาร์ทโฮม ในกรณีการใช้งานขั้นพื้นฐานที่สุด คุณสามารถเขียนโค้ดการทำงานอัตโนมัติอย่างชัดเจนเพื่อใช้อุปกรณ์และลักษณะเฉพาะที่ต้องการได้ แต่กรณีการใช้งานที่ใช้งานได้จริงมากกว่าคือกรณีที่แอปช่วยให้ผู้ใช้กำหนดค่าอุปกรณ์ คำสั่ง และพารามิเตอร์ของการทำงานอัตโนมัติ ส่วนถัดไปจะอธิบายวิธีสร้างเครื่องมือแก้ไขการทำงานอัตโนมัติที่ช่วยให้ผู้ใช้ทำสิ่งดังกล่าวได้
4. สร้างเครื่องมือแก้ไขการทำงานอัตโนมัติ
ภายในแอปตัวอย่าง เราจะสร้างเครื่องมือแก้ไขการทำงานอัตโนมัติซึ่งผู้ใช้สามารถเลือกอุปกรณ์ ความสามารถ (การดำเนินการ) ที่ต้องการใช้ และวิธีทริกเกอร์การทำงานอัตโนมัติโดยใช้ตัวเริ่มต้น
ตั้งค่าเงื่อนไขเริ่มต้น
เงื่อนไขเริ่มต้นสำหรับการทำงานอัตโนมัติคือจุดแรกเข้าสำหรับการทำงานอัตโนมัติ เงื่อนไขเริ่มต้นจะทริกเกอร์การทำงานอัตโนมัติเมื่อเกิดเหตุการณ์ที่กำหนด ในแอปตัวอย่าง เราจะบันทึกตัวเริ่มต้นการทำงานอัตโนมัติโดยใช้คลาส StarterViewModel
ซึ่งอยู่ในไฟล์แหล่งที่มา StarterViewModel.kt
และแสดงมุมมองเอดิเตอร์โดยใช้ StarterView
(StarterView.kt
)
โหนดเริ่มต้นต้องมีองค์ประกอบต่อไปนี้
- อุปกรณ์
- ลักษณะ
- การดำเนินการ
- ค่า
คุณเลือกอุปกรณ์และลักษณะจากออบเจ็กต์ที่ Devices API แสดงผลได้ คำสั่งและพารามิเตอร์สำหรับอุปกรณ์ที่รองรับแต่ละเครื่องเป็นเรื่องที่ซับซ้อนกว่าและต้องจัดการแยกกัน
แอปจะกำหนดรายการการดำเนินการที่ตั้งค่าไว้ล่วงหน้าดังนี้
// List of operations available when creating automation starters:
enum class Operation {
EQUALS,
NOT_EQUALS,
GREATER_THAN,
GREATER_THAN_OR_EQUALS,
LESS_THAN,
LESS_THAN_OR_EQUALS
}
จากนั้นสำหรับลักษณะที่รองรับแต่ละรายการ ให้ติดตามการดำเนินการที่รองรับ
// List of operations available when comparing booleans:
object BooleanOperations : Operations(listOf(
Operation.EQUALS,
Operation.NOT_EQUALS
))
// List of operations available when comparing values:
object LevelOperations : Operations(listOf(
Operation.GREATER_THAN,
Operation.GREATER_THAN_OR_EQUALS,
Operation.LESS_THAN,
Operation.LESS_THAN_OR_EQUALS
))
ในทำนองเดียวกัน แอปตัวอย่างจะติดตามค่าที่กำหนดให้กับลักษณะดังนี้
enum class OnOffValue {
On,
Off,
}
enum class ThermostatValue {
Heat,
Cool,
Off,
}
และติดตามการแมประหว่างค่าที่แอปกำหนดกับค่าที่ API กำหนด
val valuesOnOff: Map<OnOffValue, Boolean> = mapOf(
OnOffValue.On to true,
OnOffValue.Off to false,
)
val valuesThermostat: Map<ThermostatValue, ThermostatTrait.SystemModeEnum> = mapOf(
ThermostatValue.Heat to ThermostatTrait.SystemModeEnum.Heat,
ThermostatValue.Cool to ThermostatTrait.SystemModeEnum.Cool,
ThermostatValue.Off to ThermostatTrait.SystemModeEnum.Off,
)
จากนั้นแอปจะแสดงชุดองค์ประกอบมุมมองที่ผู้ใช้ใช้เพื่อเลือกช่องที่จำเป็นได้
ยกเลิกการแสดงความคิดเห็นในขั้นตอนที่ 4.1.1 ในไฟล์ StarterView.kt
เพื่อแสดงอุปกรณ์เริ่มต้นทั้งหมดและใช้การเรียกกลับเมื่อคลิกใน DropdownMenu
val deviceVMs: List<DeviceViewModel> = structureVM.deviceVMs.collectAsState().value
...
DropdownMenu(expanded = expandedDeviceSelection, onDismissRequest = { expandedDeviceSelection = false }) {
// TODO: 4.1.1 - Starter device selection dropdown
// for (deviceVM in deviceVMs) {
// DropdownMenuItem(
// text = { Text(deviceVM.name) },
// onClick = {
// scope.launch {
// starterDeviceVM.value = deviceVM
// starterType.value = deviceVM.type.value
// starterTrait.value = null
// starterOperation.value = null
// }
// expandedDeviceSelection = false
// }
// )
// }
}
ยกเลิกการแสดงความคิดเห็นในขั้นตอนที่ 4.1.2 ในไฟล์ StarterView.kt
เพื่อแสดงลักษณะทั้งหมดของอุปกรณ์เริ่มต้น และใช้การเรียกกลับเมื่อคลิกใน DropdownMenu
// Selected starter attributes for StarterView on screen:
val starterDeviceVM: MutableState<DeviceViewModel?> = remember {
mutableStateOf(starterVM.deviceVM.value) }
...
DropdownMenu(expanded = expandedTraitSelection, onDismissRequest = { expandedTraitSelection = false }) {
// TODO: 4.1.2 - Starter device traits selection dropdown
// val deviceTraits = starterDeviceVM.value?.traits?.collectAsState()?.value!!
// for (trait in deviceTraits) {
// DropdownMenuItem(
// text = { Text(trait.factory.toString()) },
// onClick = {
// scope.launch {
// starterTrait.value = trait.factory
// starterOperation.value = null
// }
// expandedTraitSelection = false
// }
// )
}
}
ยกเลิกการแสดงความคิดเห็นในขั้นตอนที่ 4.1.3 ในไฟล์ StarterView.kt
เพื่อแสดงผลการดำเนินการทั้งหมดของลักษณะที่เลือก และใช้การเรียกกลับเมื่อคลิกใน DropdownMenu
val starterOperation: MutableState<StarterViewModel.Operation?> = remember {
mutableStateOf(starterVM.operation.value) }
...
DropdownMenu(expanded = expandedOperationSelection, onDismissRequest = { expandedOperationSelection = false }) {
// ...
if (!StarterViewModel.starterOperations.containsKey(starterTrait.value))
return@DropdownMenu
// TODO: 4.1.3 - Starter device trait operations selection dropdown
// val operations: List<StarterViewModel.Operation> = StarterViewModel.starterOperations.get(starterTrait.value ?: OnOff)?.operations!!
// for (operation in operations) {
// DropdownMenuItem(
// text = { Text(operation.toString()) },
// onClick = {
// scope.launch {
// starterOperation.value = operation
// }
// expandedOperationSelection = false
// }
// )
// }
}
ยกเลิกการแสดงความคิดเห็นในขั้นตอนที่ 4.1.4 ในไฟล์ StarterView.kt
เพื่อแสดงค่าทั้งหมดของลักษณะที่เลือก และใช้การเรียกกลับการคลิกใน DropdownMenu
when (starterTrait.value) {
OnOff -> {
...
DropdownMenu(expanded = expandedBooleanSelection, onDismissRequest = { expandedBooleanSelection = false }) {
// TODO: 4.1.4 - Starter device trait values selection dropdown
// for (value in StarterViewModel.valuesOnOff.keys) {
// DropdownMenuItem(
// text = { Text(value.toString()) },
// onClick = {
// scope.launch {
// starterValueOnOff.value = StarterViewModel.valuesOnOff.get(value)
// }
// expandedBooleanSelection = false
// }
// )
// }
}
...
}
LevelControl -> {
...
}
}
ยกเลิกการแสดงความคิดเห็นในขั้นตอนที่ 4.1.5 ในไฟล์ StarterView.kt
เพื่อจัดเก็บตัวแปรเริ่มต้นทั้งหมดของ ViewModel
ไว้ในตัวแปรเริ่มต้นของฉบับร่างการทำงานอัตโนมัติ ViewModel
(draftVM.starterVMs
)
val draftVM: DraftViewModel = homeAppVM.selectedDraftVM.collectAsState().value!!
// Save starter button:
Button(
enabled = isOptionsSelected && isValueProvided,
onClick = {
scope.launch {
// TODO: 4.1.5 - store all starter ViewModel variables into draft ViewModel
// starterVM.deviceVM.emit(starterDeviceVM.value)
// starterVM.trait.emit(starterTrait.value)
// starterVM.operation.emit(starterOperation.value)
// starterVM.valueOnOff.emit(starterValueOnOff.value!!)
// starterVM.valueLevel.emit(starterValueLevel.value!!)
// starterVM.valueBooleanState.emit(starterValueBooleanState.value!!)
// starterVM.valueOccupancy.emit(starterValueOccupancy.value!!)
// starterVM.valueThermostat.emit(starterValueThermostat.value!!)
//
// draftVM.starterVMs.value.add(starterVM)
// draftVM.selectedStarterVM.emit(null)
}
})
{ Text(stringResource(R.string.starter_button_create)) }
การเรียกใช้แอปและการเลือกการทำงานอัตโนมัติและเงื่อนไขเริ่มต้นใหม่ควรแสดงมุมมองดังต่อไปนี้
แอปตัวอย่างรองรับเฉพาะการเริ่มต้นที่อิงตามลักษณะของอุปกรณ์
ตั้งค่าการดำเนินการ
การดำเนินการอัตโนมัติแสดงให้เห็นวัตถุประสงค์หลักของการทำงานอัตโนมัติ ซึ่งก็คือวิธีที่การทำงานอัตโนมัติทำให้เกิดการเปลี่ยนแปลงในโลกจริง ในแอปตัวอย่าง เราจะบันทึกการดำเนินการอัตโนมัติโดยใช้คลาส ActionViewModel
และแสดงมุมมองเอดิเตอร์โดยใช้คลาส ActionView
แอปตัวอย่างใช้เอนทิตี Home API ต่อไปนี้เพื่อกำหนดโหนดการดำเนินการอัตโนมัติ
- อุปกรณ์
- ลักษณะ
- คำสั่ง
- ค่า (ไม่บังคับ)
การดำเนินการคำสั่งอุปกรณ์แต่ละรายการจะใช้คำสั่ง แต่บางรายการจะต้องมีค่าพารามิเตอร์ที่เชื่อมโยงด้วย เช่น MoveToLevel()
และเปอร์เซ็นต์เป้าหมาย
คุณเลือกอุปกรณ์และลักษณะจากออบเจ็กต์ที่ Devices API แสดงผลได้
แอปจะกำหนดรายการคำสั่งที่กำหนดไว้ล่วงหน้าดังนี้
// List of operations available when creating automation starters:
enum class Action {
ON,
OFF,
MOVE_TO_LEVEL,
MODE_HEAT,
MODE_COOL,
MODE_OFF,
}
แอปจะติดตามการดำเนินการที่รองรับสำหรับลักษณะที่รองรับแต่ละรายการ
// List of operations available when comparing booleans:
object OnOffActions : Actions(listOf(
Action.ON,
Action.OFF,
))
// List of operations available when comparing booleans:
object LevelActions : Actions(listOf(
Action.MOVE_TO_LEVEL
))
// List of operations available when comparing booleans:
object ThermostatActions : Actions(listOf(
Action.MODE_HEAT,
Action.MODE_COOL,
Action.MODE_OFF,
))
// Map traits and the comparison operations they support:
val actionActions: Map<TraitFactory<out Trait>, Actions> = mapOf(
OnOff to OnOffActions,
LevelControl to LevelActions,
// BooleanState - No Actions
// OccupancySensing - No Actions
Thermostat to ThermostatActions,
)
สำหรับคำสั่งที่ใช้พารามิเตอร์อย่างน้อย 1 รายการ จะมีตัวแปรดังนี้
val valueLevel: MutableStateFlow<UByte?>
API จะแสดงชุดองค์ประกอบมุมมองที่ผู้ใช้ใช้เพื่อเลือกช่องที่ต้องการได้
ยกเลิกการแสดงความคิดเห็นในขั้นตอนที่ 4.2.1 ในไฟล์ ActionView.kt
เพื่อแสดงอุปกรณ์การดำเนินการทั้งหมด และใช้การเรียกกลับการคลิกใน DropdownMenu
เพื่อตั้งค่า actionDeviceVM
val deviceVMs = structureVM.deviceVMs.collectAsState().value
...
DropdownMenu(expanded = expandedDeviceSelection, onDismissRequest = { expandedDeviceSelection = false }) {
// TODO: 4.2.1 - Action device selection dropdown
// for (deviceVM in deviceVMs) {
// DropdownMenuItem(
// text = { Text(deviceVM.name) },
// onClick = {
// scope.launch {
// actionDeviceVM.value = deviceVM
// actionTrait.value = null
// actionAction.value = null
// }
// expandedDeviceSelection = false
// }
// )
// }
}
ยกเลิกการแสดงความคิดเห็นในขั้นตอนที่ 4.2.2 ในไฟล์ ActionView.kt
เพื่อแสดงลักษณะทั้งหมดของ actionDeviceVM
และใช้การเรียกกลับการคลิกใน DropdownMenu
เพื่อตั้งค่า actionTrait
ซึ่งแสดงลักษณะที่คำสั่งเป็นของ
val actionDeviceVM: MutableState<DeviceViewModel?> = remember {
mutableStateOf(actionVM.deviceVM.value) }
...
DropdownMenu(expanded = expandedTraitSelection, onDismissRequest = { expandedTraitSelection = false }) {
// TODO: 4.2.2 - Action device traits selection dropdown
// val deviceTraits: List<Trait> = actionDeviceVM.value?.traits?.collectAsState()?.value!!
// for (trait in deviceTraits) {
// DropdownMenuItem(
// text = { Text(trait.factory.toString()) },
// onClick = {
// scope.launch {
// actionTrait.value = trait
// actionAction.value = null
// }
// expandedTraitSelection = false
// }
// )
// }
}
ยกเลิกการแสดงความคิดเห็นในขั้นตอนที่ 4.2.3 ในไฟล์ ActionView.kt
เพื่อแสดงการดำเนินการทั้งหมดที่ใช้ได้ของ actionTrait
และใช้การเรียกกลับการคลิกใน DropdownMenu
เพื่อตั้งค่า actionAction
ซึ่งแสดงถึงการดำเนินการอัตโนมัติที่เลือก
DropdownMenu(expanded = expandedActionSelection, onDismissRequest = { expandedActionSelection = false }) {
// ...
if (!ActionViewModel.actionActions.containsKey(actionTrait.value?.factory))
return@DropdownMenu
// TODO: 4.2.3 - Action device trait actions (commands) selection dropdown
// val actions: List<ActionViewModel.Action> = ActionViewModel.actionActions.get(actionTrait.value?.factory)?.actions!!
// for (action in actions) {
// DropdownMenuItem(
// text = { Text(action.toString()) },
// onClick = {
// scope.launch {
// actionAction.value = action
// }
// expandedActionSelection = false
// }
// )
// }
}
ยกเลิกการแสดงความคิดเห็นในขั้นตอนที่ 4.2.4 ในไฟล์ ActionView.kt
เพื่อแสดงค่าที่ใช้ได้ของการดำเนินการลักษณะ (คำสั่ง) และจัดเก็บค่าลงใน actionValueLevel
ในการเรียกกลับการเปลี่ยนแปลงค่า
when (actionTrait.value?.factory) {
LevelControl -> {
// TODO: 4.2.4 - Action device trait action(command) values selection widget
// Column (Modifier.padding(horizontal = 16.dp, vertical = 8.dp).fillMaxWidth()) {
// Text(stringResource(R.string.action_title_value), fontSize = 16.sp, fontWeight = FontWeight.SemiBold)
// }
//
// Box (Modifier.padding(horizontal = 24.dp, vertical = 8.dp)) {
// LevelSlider(value = actionValueLevel.value?.toFloat()!!, low = 0f, high = 254f, steps = 0,
// modifier = Modifier.padding(top = 16.dp),
// onValueChange = { value : Float -> actionValueLevel.value = value.toUInt().toUByte() }
// isEnabled = true
// )
// }
...
}
ยกเลิกการแสดงความคิดเห็นในขั้นตอนที่ 4.2.5 ในไฟล์ ActionView.kt
เพื่อจัดเก็บตัวแปรของการดำเนินการทั้งหมด ViewModel
ในการดำเนินการของระบบอัตโนมัติฉบับร่าง ViewModel
(draftVM.actionVMs
)
val draftVM: DraftViewModel = homeAppVM.selectedDraftVM.collectAsState().value!!
// Save action button:
Button(
enabled = isOptionsSelected,
onClick = {
scope.launch {
// TODO: 4.2.5 - store all action ViewModel variables into draft ViewModel
// actionVM.deviceVM.emit(actionDeviceVM.value)
// actionVM.trait.emit(actionTrait.value)
// actionVM.action.emit(actionAction.value)
// actionVM.valueLevel.emit(actionValueLevel.value)
//
// draftVM.actionVMs.value.add(actionVM)
// draftVM.selectedActionVM.emit(null)
}
})
{ Text(stringResource(R.string.action_button_create)) }
การเรียกใช้แอปและการเลือกการทำงานอัตโนมัติและการดำเนินการใหม่ควรส่งผลให้ได้มุมมองดังต่อไปนี้
เรารองรับเฉพาะการดำเนินการตามลักษณะของอุปกรณ์ในแอปตัวอย่าง
แสดงการทำงานอัตโนมัติฉบับร่าง
เมื่อ DraftViewModel
เสร็จสมบูรณ์แล้ว HomeAppView.kt
จะแสดงผลได้ดังนี้
fun HomeAppView (homeAppVM: HomeAppViewModel) {
...
// If a draft automation is selected, show the draft editor:
if (selectedDraftVM != null) {
DraftView(homeAppVM)
}
...
}
ใน DraftView.kt
fun DraftView (homeAppVM: HomeAppViewModel) {
val draftVM: DraftViewModel = homeAppVM.selectedDraftVM.collectAsState().value!!
...
// Draft Starters:
DraftStarterList(draftVM)
// Draft Actions:
DraftActionList(draftVM)
}
สร้างการทำงานอัตโนมัติ
ตอนนี้คุณได้เรียนรู้วิธีสร้างตัวเริ่มต้นและการดำเนินการแล้ว คุณจึงพร้อมที่จะสร้างฉบับร่างการทำงานอัตโนมัติและส่งไปยัง Automation API API มีฟังก์ชัน createAutomation()
ที่รับฉบับร่างการทำงานอัตโนมัติเป็นอาร์กิวเมนต์ และแสดงผลอินสแตนซ์การทำงานอัตโนมัติใหม่
การเตรียมการทำงานอัตโนมัติฉบับร่างจะเกิดขึ้นในคลาส DraftViewModel
ในแอปตัวอย่าง ดูฟังก์ชัน getDraftAutomation()
เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีที่เราจัดโครงสร้างฉบับร่างการทำงานอัตโนมัติโดยใช้ตัวแปรเริ่มต้นและการดำเนินการในส่วนก่อนหน้า
ยกเลิกการแสดงความคิดเห็นในขั้นตอนที่ 4.4.1 ในไฟล์ DraftViewModel.kt
เพื่อสร้างนิพจน์ "select" ที่จำเป็นต่อการสร้างกราฟการทำงานอัตโนมัติเมื่อลักษณะเริ่มต้นเป็น OnOff
val starterVMs: List<StarterViewModel> = starterVMs.value
val actionVMs: List<ActionViewModel> = actionVMs.value
...
fun getDraftAutomation() : DraftAutomation {
...
val starterVMs: List<StarterViewModel> = starterVMs.value
...
return automation {
this.name = name
this.description = description
this.isActive = true
// The sequential block wrapping all nodes:
sequential {
// The select block wrapping all starters:
select {
// Iterate through the selected starters:
for (starterVM in starterVMs) {
// The sequential block for each starter (should wrap the Starter Expression!)
sequential {
...
val starterTrait: TraitFactory<out Trait> = starterVM.trait.value!!
...
when (starterTrait) {
OnOff -> {
// TODO: 4.4.1 - Set starter expressions according to trait type
// val onOffValue: Boolean = starterVM.valueOnOff.value
// val onOffExpression: TypedExpression<out OnOff> =
// starterExpression as TypedExpression<out OnOff>
// when (starterOperation) {
// StarterViewModel.Operation.EQUALS ->
// condition { expression = onOffExpression.onOff equals onOffValue }
// StarterViewModel.Operation.NOT_EQUALS ->
// condition { expression = onOffExpression.onOff notEquals onOffValue }
// else -> { MainActivity.showError(this, "Unexpected operation for OnOf
// }
}
LevelControl -> {
...
// Function to allow manual execution of the automation:
manualStarter()
...
}
ยกเลิกการแสดงความคิดเห็นในขั้นตอนที่ 4.4.2 ในไฟล์ DraftViewModel.kt
เพื่อสร้างนิพจน์คู่ขนานที่จำเป็นต่อการสร้างกราฟการทำงานอัตโนมัติเมื่อลักษณะการทำงานที่เลือกคือ LevelControl
และการทำงานที่เลือกคือ MOVE_TO_LEVEL
val starterVMs: List<StarterViewModel> = starterVMs.value
val actionVMs: List<ActionViewModel> = actionVMs.value
...
fun getDraftAutomation() : DraftAutomation {
...
return automation {
this.name = name
this.description = description
this.isActive = true
// The sequential block wrapping all nodes:
sequential {
...
// Parallel block wrapping all actions:
parallel {
// Iterate through the selected actions:
for (actionVM in actionVMs) {
val actionDeviceVM: DeviceViewModel = actionVM.deviceVM.value!!
// Action Expression that the DSL will check for:
action(actionDeviceVM.device, actionDeviceVM.type.value.factory) {
val actionCommand: Command = when (actionVM.action.value) {
ActionViewModel.Action.ON -> { OnOff.on() }
ActionViewModel.Action.OFF -> { OnOff.off() }
// TODO: 4.4.2 - Set starter expressions according to trait type
// ActionViewModel.Action.MOVE_TO_LEVEL -> {
// LevelControl.moveToLevelWithOnOff(
// actionVM.valueLevel.value!!,
// 0u,
// LevelControlTrait.OptionsBitmap(),
// LevelControlTrait.OptionsBitmap()
// )
// }
ActionViewModel.Action.MODE_HEAT -> { SimplifiedThermostat
.setSystemMode(SimplifiedThermostatTrait.SystemModeEnum.Heat) }
...
}
ขั้นตอนสุดท้ายในการทําการทำงานอัตโนมัติให้เสร็จสมบูรณ์คือการใช้ฟังก์ชัน getDraftAutomation
เพื่อสร้าง AutomationDraft.
ยกเลิกการแสดงความคิดเห็นในขั้นตอนที่ 4.4.3 ในไฟล์ HomeAppViewModel.kt
เพื่อสร้างการทำงานอัตโนมัติโดยการเรียกใช้ Home API และจัดการข้อยกเว้น
fun createAutomation(isPending: MutableState<Boolean>) {
viewModelScope.launch {
val structure : Structure = selectedStructureVM.value?.structure!!
val draft : DraftAutomation = selectedDraftVM.value?.getDraftAutomation()!!
isPending.value = true
// TODO: 4.4.3 - Call the Home API to create automation and handle exceptions
// // Call Automation API to create an automation from a draft:
// try {
// structure.createAutomation(draft)
// }
// catch (e: Exception) {
// MainActivity.showError(this, e.toString())
// isPending.value = false
// return@launch
// }
// Scrap the draft and automation candidates used in the process:
selectedCandidateVMs.emit(null)
selectedDraftVM.emit(null)
isPending.value = false
}
}
ตอนนี้ให้เรียกใช้แอปและดูการเปลี่ยนแปลงในอุปกรณ์
เมื่อเลือกเงื่อนไขเริ่มต้นและการดำเนินการแล้ว คุณก็พร้อมที่จะสร้างการทำงานอัตโนมัติได้โดยทำดังนี้
ตรวจสอบว่าคุณตั้งชื่อการทำงานอัตโนมัติให้ไม่ซ้ำใคร จากนั้นแตะปุ่มสร้างการทำงานอัตโนมัติ ซึ่งควรเรียกใช้ API และนำคุณกลับไปที่มุมมองรายการการทำงานอัตโนมัติพร้อมกับการทำงานอัตโนมัติของคุณ
แตะการทำงานอัตโนมัติที่คุณเพิ่งสร้างขึ้น แล้วดูว่า API ส่งคืนการทำงานอัตโนมัตินั้นอย่างไร
โปรดทราบว่า API จะแสดงค่าที่ระบุว่าการทำงานอัตโนมัติถูกต้องและใช้งานอยู่หรือไม่ คุณสร้างการทำงานอัตโนมัติที่ไม่ผ่านการตรวจสอบได้เมื่อมีการแยกวิเคราะห์ในฝั่งเซิร์ฟเวอร์ หากการแยกวิเคราะห์การทำงานอัตโนมัติไม่ผ่านการตรวจสอบ ระบบจะตั้งค่า isValid
เป็น false
ซึ่งบ่งชี้ว่าการทำงานอัตโนมัติไม่ถูกต้องและไม่ได้ใช้งาน หากการทำงานอัตโนมัติไม่ถูกต้อง ให้ดูรายละเอียดในช่องautomation.validationIssues
ตรวจสอบว่าการทำงานอัตโนมัติได้รับการตั้งค่าเป็น "ใช้งานได้" และ "ใช้งานอยู่" จากนั้นคุณจะลองใช้การทำงานอัตโนมัติได้
ลองใช้การทำงานอัตโนมัติ
การทำงานอัตโนมัติสามารถดำเนินการได้ 2 วิธี ดังนี้
- ด้วยเหตุการณ์เงื่อนไขเริ่มต้น หากเงื่อนไขตรงกัน ระบบจะทริกเกอร์การดำเนินการที่คุณตั้งค่าไว้ในการทำงานอัตโนมัติ
- ด้วยการเรียก API การดำเนินการด้วยตนเอง
หากการทำงานอัตโนมัติฉบับร่างมี manualStarter()
ที่กำหนดไว้ในบล็อก DSL ของการทำงานอัตโนมัติฉบับร่าง เครื่องมือการทำงานอัตโนมัติจะรองรับการเรียกใช้ด้วยตนเองสำหรับการทำงานอัตโนมัตินั้น ซึ่งมีอยู่ในตัวอย่างโค้ดในแอปตัวอย่างอยู่แล้ว
เนื่องจากคุณยังอยู่ในหน้าจอมุมมองการทำงานอัตโนมัติบนอุปกรณ์เคลื่อนที่ ให้แตะปุ่มดำเนินการด้วยตนเอง ซึ่งควรเรียกใช้ automation.execute()
ซึ่งจะเรียกใช้คำสั่งการดำเนินการในอุปกรณ์ที่คุณเลือกเมื่อตั้งค่าการทำงานอัตโนมัติ
เมื่อตรวจสอบความถูกต้องของคำสั่งการดำเนินการผ่านการดำเนินการด้วยตนเองโดยใช้ API แล้ว ตอนนี้ก็ถึงเวลาดูว่าคำสั่งดังกล่าวจะดำเนินการโดยใช้ตัวเริ่มต้นที่คุณกำหนดไว้ด้วยหรือไม่
ไปที่แท็บอุปกรณ์ เลือกอุปกรณ์การดำเนินการและลักษณะ จากนั้นตั้งค่าเป็นค่าอื่น (เช่น ตั้งค่า light2
's LevelControl
(ความสว่าง) เป็น 50% ดังที่แสดงในภาพหน้าจอต่อไปนี้
ตอนนี้เราจะลองทริกเกอร์การทำงานอัตโนมัติโดยใช้อุปกรณ์เริ่มต้น เลือกอุปกรณ์เริ่มต้นที่คุณเลือกเมื่อสร้างการทำงานอัตโนมัติ สลับลักษณะที่คุณเลือก (เช่น ตั้งค่า starter outlet1
OnOff
เป็น On
)
คุณจะเห็นว่าการดำเนินการนี้ยังเรียกใช้การทำงานอัตโนมัติและตั้งค่าลักษณะLevelControl
ของอุปกรณ์การดำเนินการ light2
เป็นค่าเดิม ซึ่งก็คือ 100% ด้วย
ขอแสดงความยินดี คุณใช้ Home API เพื่อสร้างการทำงานอัตโนมัติเรียบร้อยแล้ว
ดูข้อมูลเพิ่มเติมเกี่ยวกับ Automation API ได้ที่ Android Automation API
5. ค้นพบความสามารถ
Home API มี API เฉพาะที่เรียกว่า Discovery API ซึ่งนักพัฒนาแอปสามารถใช้เพื่อค้นหาว่าอุปกรณ์หนึ่งๆ รองรับลักษณะที่เปิดใช้การทำงานอัตโนมัติใดบ้าง แอปตัวอย่างมีตัวอย่างที่คุณใช้ API นี้เพื่อดูว่ามีคำสั่งใดบ้างที่พร้อมใช้งาน
ค้นพบคำสั่ง
ในส่วนนี้ เราจะพูดถึงวิธีค้นหา CommandCandidates
ที่รองรับและวิธีสร้างการทำงานอัตโนมัติตามโหนดผู้สมัครที่ค้นพบ
ในแอปตัวอย่าง เราจะเรียกใช้ device.candidates()
เพื่อรับรายชื่อผู้สมัคร ซึ่งอาจรวมถึงอินสแตนซ์ของ CommandCandidate
, EventCandidate
หรือ TraitAttributesCandidate
ไปที่HomeAppViewModel.kt
แล้วยกเลิกการแสดงความคิดเห็นในขั้นตอนที่ 5.1.1 เพื่อดึงรายการผู้สมัครและกรองด้วยประเภท Candidate
fun showCandidates() {
...
// TODO: 5.1.1 - Retrieve automation candidates, filtering to include CommandCandidate types only
// // Retrieve a set of initial automation candidates from the device:
// val candidates: Set<NodeCandidate> = deviceVM.device.candidates().first()
//
// for (candidate in candidates) {
// // Check whether the candidate trait is supported:
// if(candidate.trait !in HomeApp.supportedTraits)
// continue
// // Check whether the candidate type is supported:
// when (candidate) {
// // Command candidate type:
// is CommandCandidate -> {
// // Check whether the command candidate has a supported command:
// if (candidate.commandDescriptor !in ActionViewModel.commandMap)
// continue
// }
// // Other candidate types are currently unsupported:
// else -> { continue }
// }
//
// candidateVMList.add(CandidateViewModel(candidate, deviceVM))
// }
...
// Store the ViewModels:
selectedCandidateVMs.emit(candidateVMList)
}
ดูวิธีที่ฟิลเตอร์ทำงานกับ CommandCandidate.
ผู้สมัครที่ API แสดงผลมีหลายประเภท แอปตัวอย่างรองรับ CommandCandidate
ยกเลิกการแสดงความคิดเห็นในขั้นตอนที่ 5.1.2 ใน commandMap
ที่กำหนดไว้ใน ActionViewModel.kt
เพื่อตั้งค่าลักษณะที่รองรับต่อไปนี้
// Map of supported commands from Discovery API:
val commandMap: Map<CommandDescriptor, Action> = mapOf(
// TODO: 5.1.2 - Set current supported commands
// OnOffTrait.OnCommand to Action.ON,
// OnOffTrait.OffCommand to Action.OFF,
// LevelControlTrait.MoveToLevelWithOnOffCommand to Action.MOVE_TO_LEVEL
)
ตอนนี้เราสามารถเรียกใช้ Discovery API และกรองผลการค้นหาที่เรารองรับในแอปตัวอย่างได้แล้ว เราจะมาพูดถึงวิธีผสานรวม API นี้เข้ากับเครื่องมือแก้ไข
ดูข้อมูลเพิ่มเติมเกี่ยวกับ Discovery API ได้ที่ใช้ประโยชน์จากการค้นพบอุปกรณ์ใน Android
ผสานรวมตัวแก้ไข
วิธีที่พบบ่อยที่สุดในการใช้การดำเนินการที่ค้นพบคือการนำเสนอต่อผู้ใช้ปลายทางเพื่อให้เลือก ก่อนที่ผู้ใช้จะเลือกช่องการทำงานอัตโนมัติฉบับร่าง เราจะแสดงรายการการดำเนินการที่ค้นพบให้ผู้ใช้เห็น และเราจะป้อนข้อมูลล่วงหน้าในโหนดการดำเนินการในการทำงานอัตโนมัติฉบับร่างได้โดยขึ้นอยู่กับค่าที่ผู้ใช้เลือก
CandidatesView.kt
ไฟล์มีคลาสมุมมองที่แสดงผู้สมัครที่ค้นพบ Uncomment ขั้นตอนที่ 5.2.1 เพื่อเปิดใช้ฟังก์ชัน .clickable{}
ของ CandidateListItem
ซึ่งจะตั้งค่า homeAppVM.selectedDraftVM
เป็น candidateVM
fun CandidateListItem (candidateVM: CandidateViewModel, homeAppVM: HomeAppViewModel) {
val scope: CoroutineScope = rememberCoroutineScope()
Box (Modifier.padding(horizontal = 24.dp, vertical = 8.dp)) {
Column (Modifier.fillMaxWidth().clickable {
// TODO: 5.2.1 - Set the selectedDraftVM to the selected candidate
// scope.launch { homeAppVM.selectedDraftVM.emit(DraftViewModel(candidateVM)) }
}) {
...
}
}
}
เช่นเดียวกับขั้นตอนที่ 4.3 ใน HomeAppView.kt
เมื่อตั้งค่า selectedDraftVM
แล้ว ระบบจะแสดงผล DraftView(...) in
DraftView.kt`:
fun HomeAppView (homeAppVM: HomeAppViewModel) {
...
val selectedDraftVM: DraftViewModel? by homeAppVM.selectedDraftVM.collectAsState()
...
// If a draft automation is selected, show the draft editor:
if (selectedDraftVM != null) {
DraftView(homeAppVM)
}
...
}
ลองอีกครั้งโดยแตะ light2 - MOVE_TO_LEVEL ที่แสดงในส่วนก่อนหน้า ซึ่งจะแจ้งให้คุณสร้างการทำงานอัตโนมัติใหม่ตามคำสั่งของผู้สมัคร
ตอนนี้คุณคุ้นเคยกับการสร้างการทำงานอัตโนมัติในแอปตัวอย่างแล้ว คุณก็สามารถผสานรวมการทำงานอัตโนมัติในแอปของคุณได้
6. ตัวอย่างการทำงานอัตโนมัติขั้นสูง
ก่อนจะสรุปการพูดคุยกันในวันนี้ เราจะมาดูตัวอย่าง DSL ของการทำงานอัตโนมัติเพิ่มเติมกัน ซึ่งแสดงให้เห็นถึงความสามารถขั้นสูงบางส่วนที่คุณทำได้ด้วย API
เวลาของวันเป็นตัวเริ่มต้น
นอกจากลักษณะของอุปกรณ์แล้ว Google Home API ยังมีลักษณะตามโครงสร้าง เช่น Time
คุณสร้างการทำงานอัตโนมัติที่มีตัวเริ่มต้นตามเวลาได้ เช่น การทำงานต่อไปนี้
automation {
name = "AutomationName"
description = "An example automation description."
isActive = true
description = "Do ... actions when time is up."
sequential {
// starter
val starter = starter<_>(structure, Time.ScheduledTimeEvent) {
parameter(
Time.ScheduledTimeEvent.clockTime(
LocalTime.of(hour, min, sec, 0)
)
)
}
// action
...
}
}
การออกอากาศของ Assistant เป็นการดำเนินการ
คุณลักษณะ AssistantBroadcast
จะพร้อมใช้งานเป็นคุณลักษณะระดับอุปกรณ์ใน SpeakerDevice
(หากลำโพงรองรับ) หรือเป็นคุณลักษณะระดับโครงสร้าง (เนื่องจากลำโพง Google และอุปกรณ์เคลื่อนที่ Android สามารถเล่นการออกอากาศของ Assistant ได้) เช่น
automation {
name = "AutomationName"
description = "An example automation description."
isActive = true
description = "Broadcast in Speaker when ..."
sequential {
// starter
...
// action
action(structure) {
command(
AssistantBroadcast.broadcast("Time is up!!")
)
}
}
}
ใช้ DelayFor
และ suppressFor
นอกจากนี้ Automation API ยังมีตัวดำเนินการขั้นสูง เช่น delayFor ซึ่งใช้สำหรับหน่วงเวลาคำสั่ง และ suppressFor ซึ่งสามารถระงับการทริกเกอร์การทำงานอัตโนมัติจากเหตุการณ์เดียวกันภายในช่วงเวลาที่กำหนด ตัวอย่างการใช้ตัวดำเนินการเหล่านี้มีดังนี้
sequential {
val starterNode = starter<_>(device, OccupancySensorDevice, MotionDetection)
// only proceed if there is currently motion taking place
condition { starterNode.motionDetectionEventInProgress equals true }
// ignore the starter for one minute after it was last triggered
suppressFor(Duration.ofMinutes(1))
// make announcements three seconds apart
action(device, SpeakerDevice) {
command(AssistantBroadcast.broadcast("Intruder detected!"))
}
delayFor(Duration.ofSeconds(3))
action(device, SpeakerDevice) {
command(AssistantBroadcast.broadcast("Intruder detected!"))
}
...
}
ใช้ AreaPresenceState
ใน Starter
AreaPresenceState
เป็นลักษณะระดับโครงสร้างที่ตรวจหาว่ามีคนอยู่บ้านหรือไม่
ตัวอย่างเช่น ตัวอย่างต่อไปนี้แสดงการล็อกประตูโดยอัตโนมัติเมื่อมีคนอยู่บ้านหลังเวลา 22:00 น.
automation {
name = "Lock the doors when someone is home after 10pm"
description = "1 starter, 2 actions"
sequential {
val unused =
starter(structure, event = Time.ScheduledTimeEvent) {
parameter(Time.ScheduledTimeEvent.clockTime(LocalTime.of(22, 0, 0, 0)))
}
val stateReaderNode = stateReader<_>(structure, AreaPresenceState)
condition {
expression =
stateReaderNode.presenceState equals
AreaPresenceStateTrait.PresenceState.PresenceStateOccupied
}
action(structure) { command(AssistantBroadcast.broadcast("Locks are being applied")) }
for (lockDevice in lockDevices) {
action(lockDevice, DoorLockDevice) {
command(Command(DoorLock, DoorLockTrait.LockDoorCommand.requestId.toString(), mapOf()))
}
}
}
ตอนนี้คุณคุ้นเคยกับความสามารถในการทำงานอัตโนมัติขั้นสูงเหล่านี้แล้ว ก็ไปสร้างแอปที่ยอดเยี่ยมกันเลย
7. ยินดีด้วย
ยินดีด้วย คุณพัฒนาแอป Android โดยใช้ Google Home API ส่วนที่ 2 เสร็จสมบูรณ์แล้ว ใน Codelab นี้ คุณได้สำรวจ Automation API และ Discovery API
เราหวังว่าคุณจะสนุกกับการสร้างแอปที่ควบคุมอุปกรณ์อย่างสร้างสรรค์ภายในระบบนิเวศของ Google Home และสร้างสถานการณ์การทำงานอัตโนมัติที่น่าตื่นเต้นโดยใช้ Home API
ขั้นตอนถัดไป
- อ่านการแก้ปัญหาเพื่อดูวิธีแก้ไขข้อบกพร่องของแอปอย่างมีประสิทธิภาพและแก้ปัญหาที่เกี่ยวข้องกับ Home API
- คุณติดต่อเราเพื่อส่งคำแนะนำหรือรายงานปัญหาผ่านเครื่องมือติดตามปัญหาในหัวข้อการสนับสนุนสมาร์ทโฮมได้