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 (ระบบคลาวด์ต่อระบบคลาวด์และ Matter)
- เวิร์กสเตชันที่ติดตั้ง Android Studio (2024.3.1 Ladybug ขึ้นไป)
- โทรศัพท์ Android ที่เป็นไปตามข้อกำหนดของ Home API (ดูข้อกําหนดเบื้องต้น) ซึ่งติดตั้ง Google Play Services และแอป Google Home
- Google Home Hub ที่เข้ากันได้ซึ่งรองรับ Google Home API
- ไม่บังคับ - อุปกรณ์สมาร์ทโฮมที่เข้ากันได้กับ Google Home API
สิ่งที่คุณจะได้เรียนรู้
- วิธีสร้างการทำงานอัตโนมัติสำหรับอุปกรณ์สมาร์ทโฮมโดยใช้ Home API
- วิธีใช้ Discovery API เพื่อสำรวจความสามารถของอุปกรณ์ที่รองรับ
- วิธีใช้แนวทางปฏิบัติแนะนำเมื่อสร้างแอปด้วย Home API
2. การสร้างโปรเจ็กต์
แผนภาพต่อไปนี้แสดงสถาปัตยกรรมของแอป Home APIs
- โค้ดแอป: โค้ดหลักที่นักพัฒนาแอปใช้สร้างอินเทอร์เฟซผู้ใช้ของแอปและตรรกะในการโต้ตอบกับ SDK ของ Home APIs
- Home APIs SDK: Home APIs SDK ที่ Google ให้บริการจะทํางานร่วมกับบริการ Home APIs ใน GMSCore เพื่อควบคุมอุปกรณ์สมาร์ทโฮม นักพัฒนาแอปสร้างแอปที่ทำงานร่วมกับ Home API ได้โดยรวมแอปเข้ากับ Home APIs SDK
- GMSCore ใน Android: GMSCore หรือที่เรียกว่าบริการ Google Play เป็นแพลตฟอร์มของ Google ที่ให้บริการหลักของระบบ ซึ่งเปิดใช้ฟังก์ชันหลักในอุปกรณ์ Android ที่ผ่านการรับรองทั้งหมด โมดูล Home ของบริการ Google Play มีบริการที่โต้ตอบกับ Home API
ในโค้ดแล็บนี้ เราจะต่อยอดจากสิ่งที่ได้พูดถึงในหัวข้อสร้างแอปบนอุปกรณ์เคลื่อนที่โดยใช้ Home API ใน Android
ตรวจสอบว่าคุณมีโครงสร้างที่มีอุปกรณ์ที่รองรับอย่างน้อย 2 เครื่องที่ตั้งค่าและใช้งานได้ในบัญชี เนื่องจากเราจะตั้งค่าการทำงานอัตโนมัติในโค้ดแล็บนี้ (การเปลี่ยนแปลงสถานะของอุปกรณ์จะทริกเกอร์การดำเนินการในอุปกรณ์อีกเครื่องหนึ่ง) คุณจึงต้องใช้อุปกรณ์ 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 ประเภทที่เรียกว่าnodes ได้แก่ เงื่อนไขเริ่มต้น การดำเนินการ และเงื่อนไข โดยโหนดเหล่านี้จะทำงานร่วมกันเพื่อทําให้การทำงานเป็นแบบอัตโนมัติโดยใช้อุปกรณ์สมาร์ทโฮม โดยปกติแล้ว ระบบจะประเมินตามลำดับต่อไปนี้
- Starter — กําหนดเงื่อนไขเริ่มต้นที่เปิดใช้งานการทำงานอัตโนมัติ เช่น การเปลี่ยนแปลงค่าลักษณะ การทำงานอัตโนมัติต้องมีStarter
- เงื่อนไข — ข้อจำกัดเพิ่มเติมที่จะประเมินหลังจากทริกเกอร์การทำงานอัตโนมัติ นิพจน์ในเงื่อนไขต้องประเมินค่าเป็น "จริง" เพื่อให้การดำเนินการของการทำงานอัตโนมัติทำงาน
- การดำเนินการ — คำสั่งหรือการอัปเดตสถานะที่ดำเนินการเมื่อเป็นไปตามเงื่อนไขทั้งหมด
เช่น คุณอาจตั้งค่าการทำงานอัตโนมัติให้หรี่ไฟในห้องเมื่อมีการกดสวิตช์ขณะที่ทีวีในห้องนั้นเปิดอยู่ ในตัวอย่างนี้
- Starter — สวิตช์ในห้องเปิด/ปิด
- เงื่อนไข - ระบบประเมินสถานะเปิด/ปิดทีวีเป็นเปิด
- การดำเนินการ — หลอดไฟในห้องเดียวกับสวิตช์จะหรี่ลง
เครื่องยนต์การทำงานอัตโนมัติจะประเมินโหนดเหล่านี้แบบอนุกรมหรือแบบขนาน
ขั้นตอนการดําเนินการตามลําดับประกอบด้วยโหนดที่ทํางานตามลําดับ โดยปกติแล้ว เงื่อนไขเหล่านี้จะเป็นเงื่อนไขเริ่มต้น เงื่อนไข และการดำเนินการ
โฟลว์ขนานอาจมีโหนดการดำเนินการหลายรายการที่ทำงานพร้อมกัน เช่น การเปิดไฟหลายดวงพร้อมกัน โหนดตามเวิร์กโฟลว์แบบขนานจะไม่ทํางานจนกว่าสาขาทั้งหมดของเวิร์กโฟลว์แบบขนานจะเสร็จสิ้น
โหนดประเภทอื่นๆ ในสคีมาการทำงานอัตโนมัติ ดูข้อมูลเพิ่มเติมเกี่ยวกับโหนดได้ในส่วนโหนดของคู่มือสำหรับนักพัฒนาซอฟต์แวร์ Home API นอกจากนี้ นักพัฒนาแอปยังรวมโหนดประเภทต่างๆ เพื่อสร้างการทำงานอัตโนมัติที่ซับซ้อนได้ เช่น
นักพัฒนาแอปจะส่งโหนดเหล่านี้ไปยังเครื่องมือการทำงานอัตโนมัติโดยใช้ภาษาเฉพาะโดเมน (DSL) ที่สร้างขึ้นเพื่อการทำงานอัตโนมัติของ Google Home โดยเฉพาะ
สำรวจ DSL การทำงานอัตโนมัติ
ภาษาเฉพาะโดเมน (DSL) คือภาษาที่ใช้บันทึกลักษณะการทํางานของระบบเป็นโค้ด คอมไพเลอร์จะสร้างคลาสข้อมูลที่แปลงเป็น JSON บัฟเฟอร์โปรโตคอล และใช้เพื่อเรียกใช้บริการอัตโนมัติของ 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()
)
เมื่อทํางานกับการทำงานอัตโนมัติ โปรดทราบว่ามีขีดจํากัดของทรัพยากร
การทำงานอัตโนมัติเป็นเครื่องมือที่มีประโยชน์มากในการสร้างความสามารถแบบอัตโนมัติในสมาร์ทโฮม ใน Use Case พื้นฐานที่สุด คุณสามารถเขียนโค้ดการทำงานอัตโนมัติเพื่อใช้อุปกรณ์และลักษณะเฉพาะที่เฉพาะเจาะจงได้ แต่กรณีการใช้งานที่ใช้งานได้จริงคือกรณีที่แอปอนุญาตให้ผู้ใช้กําหนดค่าอุปกรณ์ คําสั่ง และพารามิเตอร์ของระบบอัตโนมัติ ส่วนถัดไปจะอธิบายวิธีสร้างเครื่องมือแก้ไขการทำงานอัตโนมัติที่ช่วยให้ผู้ใช้ทําสิ่งนั้นได้
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 Boolean> = mapOf(
OnOffValue.On to true,
OnOffValue.Off to false,
)
val valuesThermostat: Map 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 = 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 = 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 = 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.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 Trait>, Actions> = mapOf(
OnOff to OnOffActions,
LevelControl to LevelActions,
// BooleanState - No Actions
// OccupancySensing - No Actions
Thermostat to ThermostatActions,
)
สําหรับคําสั่งที่ใช้พารามิเตอร์อย่างน้อย 1 รายการ จะมีตัวแปรด้วย ดังนี้
val valueLevel: MutableStateFlow
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 = remember {
mutableStateOf(actionVM.deviceVM.value) }
...
DropdownMenu(expanded = expandedTraitSelection, onDismissRequest = { expandedTraitSelection = false }) {
// TODO: 4.2.2 - Action device traits selection dropdown
// val deviceTraits: List = 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.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 = starterVMs.value
val actionVMs: List = actionVMs.value
...
fun getDraftAutomation() : DraftAutomation {
...
val starterVMs: List = 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 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 OnOff> =
// starterExpression as TypedExpression 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 = starterVMs.value
val actionVMs: List = 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) {
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 แล้ว ตอนนี้ก็ถึงเวลาดูว่าคําสั่งดังกล่าวทํางานโดยใช้เงื่อนไขเริ่มต้นที่คุณกําหนดไว้ด้วยหรือไม่
ไปที่แท็บอุปกรณ์ เลือกอุปกรณ์การดำเนินการและลักษณะ แล้วตั้งค่าเป็นค่าอื่น (เช่น ตั้งค่า LevelControl
(ความสว่าง) ของ light2
เป็น 50% ดังที่แสดงในภาพหน้าจอต่อไปนี้
ตอนนี้เราจะพยายามทริกเกอร์การทำงานอัตโนมัติโดยใช้อุปกรณ์เงื่อนไขเริ่มต้น เลือกอุปกรณ์เงื่อนไขเริ่มต้นที่คุณเลือกไว้เมื่อสร้างการทำงานอัตโนมัติ สลับลักษณะที่คุณเลือก (เช่น ตั้งค่า OnOff
ของ starter outlet1
เป็น 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 = 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 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 และกรองผลลัพธ์ที่เรารองรับในแอปตัวอย่างได้แล้ว เราจะมาพูดถึงวิธีผสานรวมข้อมูลนี้เข้ากับเครื่องมือแก้ไข
ดูข้อมูลเพิ่มเติมเกี่ยวกับ Discovery API ได้ที่ใช้ประโยชน์จากการค้นพบอุปกรณ์ใน Android
ผสานรวมเครื่องมือแก้ไข
วิธีที่พบบ่อยที่สุดในการใช้การดําเนินการที่ค้นพบคือการนําเสนอให้ผู้ใช้ปลายทางเลือก ก่อนที่ผู้ใช้จะเลือกช่องการทำงานอัตโนมัติฉบับร่าง เราสามารถแสดงรายการการดำเนินการที่ค้นพบให้ผู้ใช้เห็น และป้อนข้อมูลโหนดการดำเนินการในฉบับร่างการทำงานอัตโนมัติล่วงหน้าโดยขึ้นอยู่กับค่าที่ผู้ใช้เลือก
ไฟล์ CandidatesView.kt
มีคลาสมุมมองที่แสดงผู้สมัครที่ค้นพบ ยกเลิกการคอมเมนต์ขั้นตอนที่ 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
ในเงื่อนไขเริ่มต้น
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. ยินดีด้วย
ยินดีด้วย คุณทำส่วนที่ 2 ของการพัฒนาแอป Android โดยใช้ Google Home API เสร็จเรียบร้อยแล้ว ตลอดทั้งโค้ดแล็บนี้ คุณได้สำรวจ Automation และ Discovery API
เราหวังว่าคุณจะสนุกกับการสร้างแอปที่ควบคุมอุปกรณ์ภายในระบบนิเวศของ Google Home อย่างสร้างสรรค์ และสร้างสถานการณ์การทำงานอัตโนมัติที่น่าตื่นเต้นโดยใช้ Home API
ขั้นตอนถัดไป
- อ่านการแก้ปัญหาเพื่อดูวิธีแก้ไขข้อบกพร่องของแอปและแก้ปัญหาเกี่ยวกับ Home API อย่างมีประสิทธิภาพ
- คุณสามารถติดต่อเราเพื่อขอคำแนะนำหรือรายงานปัญหาผ่านเครื่องมือติดตามปัญหาในหัวข้อการสนับสนุนสมาร์ทโฮม