1. ก่อนเริ่มต้น
Google Home API มีชุดไลบรารีสำหรับนักพัฒนาแอป Android ในการเข้าถึงระบบนิเวศของ Google Home API ใหม่เหล่านี้ช่วยให้นักพัฒนาแอปสร้างแอปที่เปิดใช้งานและควบคุมอุปกรณ์สมาร์ทโฮมได้อย่างราบรื่น
Google มีแอปตัวอย่างสำหรับ Android สําหรับนักพัฒนาแอปที่ต้องการเข้าถึงตัวอย่างที่ใช้งานได้โดยใช้ Google Home API Codelab นี้อิงตามสาขาของแอปตัวอย่างซึ่งจะแนะนำวิธีใช้ Permissions, Commissioning, Device และ Structure API
ข้อกำหนดเบื้องต้น
- ความรู้เกี่ยวกับระบบนิเวศของ 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
สิ่งที่คุณจะได้เรียนรู้
- วิธีสร้างแอป Android โดยใช้ Google Home API ด้วยแนวทางปฏิบัติแนะนำ
- วิธีใช้ Device and Structure API เพื่อแสดงและควบคุมสมาร์ทโฮม
- วิธีใช้ Commissioning API เพื่อเพิ่มอุปกรณ์ไปยังระบบนิเวศของ Google Home
ไม่บังคับ: ตั้งค่าบ้าน
ก่อนใช้ Google Home API คุณจะต้องตั้งค่าบ้านในบัญชี Google โดยใช้แอป Google Home และเพิ่มอุปกรณ์ 2-3 เครื่อง ส่วนนี้จะอธิบายวิธีดำเนินการโดยใช้ Google Home Playground ซึ่งมีอุปกรณ์สมาร์ทโฮมเสมือนจริง
เปิด home-playground.withgoogle.com ในเว็บเบราว์เซอร์ ลงชื่อเข้าใช้ด้วยบัญชี Google แล้วดูว่าอุปกรณ์จำลองต่อไปนี้ปรากฏขึ้นหรือไม่
- outlet1: ปลั๊กเปิด/ปิด
- light2: หลอดไฟหรี่แสงได้
- light3: เปิด/ปิดไฟ
- ac3: เครื่องปรับอากาศ
- blinds4: อุปกรณ์ปิดบังหน้าต่าง
- washer5: เครื่องซักผ้าอัจฉริยะ
เปิดแอป Google Home บนอุปกรณ์เคลื่อนที่ แตะปุ่มเพิ่ม แล้วเลือกใช้ได้กับ Google Home ค้นหา "playground" ในรายการ แล้วเลือกโปรเจ็กต์ "Google Home Playground" แล้วแตะต่อไป
Google Home Playground จะแสดงหน้าการให้สิทธิ์บัญชี แตะให้สิทธิ์หรือลงชื่อเข้าใช้ด้วย Google คุณจะเห็นอุปกรณ์ทั้งหมดที่กำหนดค่าจากเว็บแอปในแอปบนอุปกรณ์เคลื่อนที่
เลือกอุปกรณ์ทั้งหมดและทำตามขั้นตอนการตั้งค่าให้เสร็จสมบูรณ์ เมื่อกลับไปที่หน้าแรก คุณจะเห็นอุปกรณ์ทั้งหมดที่ใช้ได้
อุปกรณ์ที่รองรับในรายการพร้อมใช้งานกับ Google 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 SDK
ทําตามขั้นตอนที่ระบุไว้ในตั้งค่า SDK เพื่อรับ SDK เวอร์ชันล่าสุด
รับแอปตัวอย่าง
ซอร์สโค้ดสําหรับแอปตัวอย่างมีอยู่ใน GitHub Codelab นี้ใช้ตัวอย่างจากสาขา codelab-branch-1
ของแอปตัวอย่าง
ไปที่ตำแหน่งที่ต้องการบันทึกโปรเจ็กต์และโคลนสาขา codelab-branch-1
โดยทำดังนี้
$ git clone -b codelab-branch-1 https://github.com/google-home/google-home-api-sample-app-android.git
สร้างแอปตัวอย่าง
ทำตามขั้นตอนที่ 1-5 ในหัวข้อสร้างแอป
เมื่อแอปทำงานบนโทรศัพท์เรียบร้อยแล้ว คุณจะเห็นหน้าหลักของแอปตัวอย่าง แต่คุณจะลงชื่อเข้าใช้ไม่ได้จนกว่าจะตั้งค่าการตรวจสอบสิทธิ์ OAuth และติดตั้งใช้งานส่วนต่างๆ ที่ขาดหายไปโดยใช้ Permission API
3. ตั้งค่าการตรวจสอบสิทธิ์
Home API ใช้ OAuth 2.0 เพื่อมอบสิทธิ์เข้าถึงอุปกรณ์ในโครงสร้าง OAuth ช่วยให้ผู้ใช้สามารถให้สิทธิ์แก่แอปหรือบริการได้โดยไม่ต้องเปิดเผยข้อมูลเข้าสู่ระบบ
ทําตามวิธีการในตั้งค่าความยินยอม OAuth เพื่อกําหนดค่าหน้าจอขอความยินยอม โปรดสร้างบัญชีทดสอบอย่างน้อย 1 บัญชี
จากนั้นทําตามวิธีการในตั้งค่าข้อมูลเข้าสู่ระบบ OAuth เพื่อสร้างข้อมูลเข้าสู่ระบบสําหรับแอป
4. สิทธิ์ในการเริ่มต้นและการจัดการ
ในส่วนนี้ คุณจะได้เรียนรู้วิธีเริ่มต้นใช้งาน SDK และจัดการสิทธิ์ของผู้ใช้โดยทำตามขั้นตอนที่ขาดหายไปโดยใช้ Permissions API
กําหนดประเภทและลักษณะที่รองรับ
เมื่อพัฒนาแอป คุณต้องระบุประเภทและลักษณะของอุปกรณ์ที่แอปจะรองรับอย่างชัดเจน ในแอปตัวอย่าง เราทําเช่นนี้โดยกําหนดรายการแบบคงที่ในออบเจ็กต์ที่ใช้ร่วมกันใน HomeApp.kt
ซึ่งจะอ้างอิงได้ทั่วทั้งแอปตามต้องการ
companion object {
// List of supported device types by this app:
val supportedTypes: List<DeviceTypeFactory<out DeviceType>> = listOf(
OnOffLightDevice,
DimmableLightDevice,
// ...
)
// List of supported device traits by this app:
val supportedTraits: List<TraitFactory<out Trait>> = listOf(
OnOff,
LevelControl,
// ...
)
}
ดูประเภทอุปกรณ์ที่รองรับและดัชนีลักษณะใน Android เพื่อดูประเภทอุปกรณ์และลักษณะที่รองรับทั้งหมด
นำการกำกับเนื้อหาออกในขั้นตอนที่ 4.1.1 และ 4.1.2 ในไฟล์ต้นฉบับ HomeApp.kt
เพื่อเปิดใช้ซอร์สโค้ดที่ขอสิทธิ์
companion object {
// List of supported device types by this app:
val supportedTypes: List<DeviceTypeFactory<out DeviceType>> = listOf(
// TODO: 4.1.1 - Non-registered device types will be unsupported
// ContactSensorDevice,
// ColorTemperatureLightDevice,
// DimmableLightDevice,
// ExtendedColorLightDevice,
// GenericSwitchDevice,
// GoogleDisplayDevice,
// GoogleTVDevice,
// OccupancySensorDevice,
// OnOffLightDevice,
// OnOffLightSwitchDevice,
// OnOffPluginUnitDevice,
// OnOffSensorDevice,
// RootNodeDevice,
// SpeakerDevice,
// ThermostatDevice,
)
// List of supported device traits by this app:
val supportedTraits: List<TraitFactory<out Trait>> = listOf(
// TODO: 4.1.2 - Non-registered traits will be unsupported
// AreaAttendanceState,
// AreaPresenceState,
// Assistant,
// AssistantBroadcast,
// AssistantFulfillment,
// BasicInformation,
// BooleanState,
// OccupancySensing,
// OnOff,
// Notification,
// LevelControl,
// TemperatureControl,
// TemperatureMeasurement,
// Thermostat,
// Time,
// Volume,
)
}
เริ่มต้นออบเจ็กต์ HomeClient
แอปทั้งหมดที่ใช้ Home API จะเริ่มต้นวัตถุ HomeClient
ซึ่งเป็นอินเทอร์เฟซหลักในการโต้ตอบกับ API เราเตรียมออบเจ็กต์นี้ในตัวแปรเริ่มต้นของคลาส HomeApp
(HomeApp.kt
)
// Registry to record device types and traits used in this app:
val registry = FactoryRegistry(
types = supportedTypes,
traits = supportedTraits
)
// Configuration options for the HomeClient:
val config = HomeConfig(
coroutineContext = Dispatchers.IO,
factoryRegistry = registry
)
// Initialize the HomeClient, which is the primary object to use all Home APIs:
homeClient = Home.getClient(context = context, homeConfig = config)
ก่อนอื่น เราจะสร้าง FactoryRegistry
โดยใช้ประเภทและลักษณะที่รองรับซึ่งเราได้กำหนดไว้ก่อนหน้านี้ จากนั้นใช้รีจิสทรีนี้เพื่อเริ่มต้น HomeConfig
ซึ่งมีการกำหนดค่าที่จําเป็นสําหรับเรียกใช้ API ต่อไปเราจะใช้การเรียก Home.getClient(...)
เพื่อรับอินสแตนซ์ HomeClient
การโต้ตอบกับ Home API ทั้งหมดจะผ่านออบเจ็กต์ HomeClient
นี้
ใช้ Permissions API
การตรวจสอบสิทธิ์ผู้ใช้สําหรับ Home API ทำได้ผ่าน Permissions API ไฟล์ต้นฉบับ PermissionsManager.kt
ของแอปตัวอย่างมีโค้ดสําหรับการตรวจสอบสิทธิ์ผู้ใช้ ยกเลิกการคอมเมนต์เนื้อหาของฟังก์ชัน checkPermissions(...)
และ requestPermissions(...)
เพื่อเปิดใช้สิทธิ์สําหรับแอปตัวอย่าง
การลงทะเบียน
homeClient.registerActivityResultCallerForPermissions(activity)
การเปิดตัว
try {
val result: PermissionsResult
result = homeClient.requestPermissions(forceLaunch = true)
when (result.status) {
PermissionsResultStatus.SUCCESS -> // Success Case
PermissionsResultStatus.CANCELLED -> // User Cancelled
PermissionsResultStatus.ERROR -> // Some Error
else -> // Unsupported Case
}
}
catch (e: HomeException) { ... }
การตรวจสอบ
try {
val state: PermissionsState
state = homeClient.hasPermissions().first { state ->
state != PermissionsState.PERMISSIONS_STATE_UNINITIALIZED
}
when (state) {
PermissionsState.GRANTED -> // Signed In
PermissionsState.NOT_GRANTED -> // Not Signed In
PermissionsState.PERMISSIONS_STATE_UNAVAILABLE -> // ...
PermissionsState.PERMISSIONS_STATE_UNINITIALIZED -> // ...
else -> // Unsupported case
}
}
catch (e: HomeException) { ... }
การสมัครใช้บริการ
homeClient.hasPermissions().collect( { state ->
// Track the changes on state
} )
ยกเลิกการคอมเมนต์ขั้นตอนที่ 4.3.1 ใน PermissionsManager.kt
เพื่อเปิดใช้โค้ดที่ขอสิทธิ์
fun requestPermissions() {
scope.launch {
try {
// TODO: 4.3.1 - Request the permissions from the Permissions API
// // Request permissions from the Permissions API and record the result:
// val result: PermissionsResult = client.requestPermissions(forceLaunch = true)
// // Adjust the sign-in status according to permission result:
// if (result.status == PermissionsResultStatus.SUCCESS)
// isSignedIn.emit(true)
// // Report the permission result:
// reportPermissionResult(result)
}
catch (e: HomeException) { MainActivity.showError(this, e.message.toString()) }
}
}
จากนั้นเปิดแอปในโทรศัพท์โดยทำตามขั้นตอนและอนุญาตสิทธิ์ คุณควรเห็นขั้นตอนต่อไปนี้
ข้อความ "กำลังโหลด" จะไม่หายไปเนื่องจากเรายังไม่ได้ติดตั้งใช้งานโค้ดที่อ่านโครงสร้างและอุปกรณ์ เราจะพูดถึงเรื่องนี้ในส่วนถัดไป
5. ทําความเข้าใจรูปแบบข้อมูล
ใน Home API โมเดลข้อมูลประกอบด้วยรายการต่อไปนี้
Structure
แสดงถึงบ้านที่มีห้องและอุปกรณ์Room
เป็นส่วนหนึ่งของโครงสร้างและมีอุปกรณ์- อุปกรณ์ (หมายถึง
HomeDevice
) สามารถกำหนดให้กับโครงสร้าง (หรือบ้าน) หรือห้องในโครงสร้างได้ - อุปกรณ์ประกอบด้วยอินสแตนซ์
DeviceType
อย่างน้อย 1 รายการ DeviceType
ประกอบด้วยอินสแตนซ์Trait
Trait
ประกอบด้วยอินสแตนซ์Attribute
(สําหรับการอ่าน/การเขียน) อินสแตนซ์Command
(สําหรับการควบคุมแอตทริบิวต์) และอินสแตนซ์Event
(สําหรับการอ่านหรือการติดตามบันทึกการเปลี่ยนแปลงที่ผ่านมา)- อินสแตนซ์
Automation
เป็นส่วนหนึ่งของโครงสร้าง และใช้ข้อมูลเมตาและอุปกรณ์ในบ้านเพื่อทำงานต่างๆ ในบ้านแบบอัตโนมัติ
ในส่วนนี้ คุณจะได้เรียนรู้วิธีพัฒนาซอร์สโค้ดเพื่อแสดงวิธีใช้ Structure API เพื่อแยกวิเคราะห์และแสดงผลโครงสร้างบ้าน ห้อง อุปกรณ์ และอื่นๆ
อ่านโครงสร้าง
การออกแบบ Home API อิงตาม Kotlin Flow เพื่อสตรีมออบเจ็กต์โมเดลข้อมูล (เช่น Structure
, HomeDevice
และอื่นๆ) นักพัฒนาแอปสมัครใช้บริการ Flow
เพื่อรับออบเจ็กต์ทั้งหมดที่อยู่ในออบเจ็กต์ (เช่น Structure
, Room
และอื่นๆ)
หากต้องการเรียกข้อมูลโครงสร้างทั้งหมด ให้เรียกใช้ฟังก์ชัน structures()
ซึ่งจะแสดงผลลําดับโครงสร้าง จากนั้นเรียกใช้ฟังก์ชันรายการในโฟลว์เพื่อรับโครงสร้างทั้งหมดที่ผู้ใช้เป็นเจ้าของ
// Get the a snapshot of all structures from the current homeClient
val allStructures : Set<Structure> =
homeClient.structures() // HomeObjectsFlow<Structure>
.list() // Set<Structure>
คู่มือสถาปัตยกรรมแอปขอแนะนําอย่างยิ่งให้ใช้แนวทางการเขียนโปรแกรมแบบรีแอกทีฟสมัยใหม่เพื่อปรับปรุงการไหลของข้อมูลแอปและการจัดการสถานะ
แอปตัวอย่างเป็นไปตามรูปแบบการเขียนโค้ดแบบรีแอ็กทีฟดังนี้
- โมเดลมุมมอง (เช่น
StructureViewModel
และDeviceViewModel
ในฐานะผู้ถือสถานะ) จะสมัครรับข้อมูลเวิร์กโฟลว์จาก Home APIs SDK เพื่อรับการเปลี่ยนแปลงค่าและรักษาสถานะล่าสุด - มุมมอง (เช่น
StructureView
และDeviceView
) จะสมัครรับข้อมูลโมเดลมุมมองเพื่อรับสถานะและแสดงผล UI ให้สอดคล้องกับการเปลี่ยนแปลงเหล่านั้น - เมื่อผู้ใช้คลิกปุ่มในมุมมอง (เช่น ปุ่ม "เปิด" ของอุปกรณ์หลอดไฟ) เหตุการณ์จะทริกเกอร์ฟังก์ชันของโมเดลมุมมอง ซึ่งจะเรียกใช้ฟังก์ชัน Home API ที่ตอบสนอง (เช่น คำสั่ง
On
ของลักษณะOnOff
)
ในขั้นตอนที่ 5.1.1 ใน HomeAppViewModel.kt
เราจะสมัครรับเหตุการณ์การเปลี่ยนแปลงโครงสร้างโดยการเรียกใช้ฟังก์ชัน collect()
ยกเลิกการคอมเมนต์ส่วนที่มีการเรียกใช้ structureSet
ที่แสดงผลโดย Structures API และส่งใน StructureViewModel's
StateFlow
ซึ่งจะช่วยให้แอปตรวจสอบการเปลี่ยนแปลงสถานะของโครงสร้างได้
private suspend fun subscribeToStructures() {
// TODO: 5.1.1 - Subscribe the structure data changes
// // Subscribe to structures returned by the Structures API:
// homeApp.homeClient.structures().collect { structureSet ->
// val structureVMList: MutableList<StructureViewModel> = mutableListOf()
// // Store structures in container ViewModels:
// for (structure in structureSet) {
// structureVMList.add(StructureViewModel(structure))
// }
// // Store the ViewModels:
// structureVMs.emit(structureVMList)
//
// // If a structure isn't selected yet, select the first structure from the list:
// if (selectedStructureVM.value == null && structureVMList.isNotEmpty())
// selectedStructureVM.emit(structureVMList.first())
//
// }
}
ใน DevicesView.kt
แอปจะสมัครรับ StructureViewModel'sStateFlow,
ซึ่งจะทริกเกอร์การจัดองค์ประกอบ UI ใหม่เมื่อ Structured Data มีการเปลี่ยนแปลง นำความคิดเห็นออกจากซอร์สโค้ดในขั้นตอนที่ 5.1.2 เพื่อแสดงผลรายการโครงสร้างเป็นเมนูแบบเลื่อนลง
val structureVMs: List<StructureViewModel> = homeAppVM.structureVMs.collectAsState().value
...
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
// TODO: 5.1.2 - Show list of structures in DropdownMenu
// for (structure in structureVMs) {
// DropdownMenuItem(
// text = { Text(structure.name) },
// onClick = {
// scope.launch { homeAppVM.selectedStructureVM.emit(structure) }
// expanded = false
// }
// )
// }
}
...
เรียกใช้แอปอีกครั้ง คุณควรเห็นเมนูเมื่อแตะลูกศร
แยกวิเคราะห์โครงสร้าง
ขั้นตอนถัดไปคือการไปยังส่วนต่างๆ ของวัตถุบ้านในโครงสร้าง เรียกข้อมูลห้องจากโครงสร้าง
val rooms: Set<Room>
rooms = structure.rooms().list()
จากนั้นคุณจะสามารถไปยังห้องต่างๆ เพื่อเรียกข้อมูลอุปกรณ์ได้ ดังนี้
val devices: Set<HomeDevice>
devices = room.devices().list()
สำคัญ: ในโมเดลข้อมูล Home APIs โครงสร้างอาจมีอุปกรณ์ที่ไม่ได้กำหนดให้กับห้อง ดังนั้นอย่าลืมบันทึกอุปกรณ์ที่ไม่มีห้องในแอปด้วย
val devicesWithoutRooms: MutableSet<HomeDevice> = mutableSetOf()
for (device in structure.devices().list())
if (!device.isInRoom)
devicesWithoutRooms.add(device)
อีกครั้ง ในโค้ดตัวอย่างที่มีอยู่ เราจะสมัครรับข้อมูลเพื่อรับรายการห้องและอุปกรณ์ล่าสุด ตรวจสอบโค้ดในขั้นตอนที่ 5.2.1 และ 5.2.2 ในไฟล์ต้นฉบับ StructureViewModel.kt
แล้วยกเลิกการใส่ความคิดเห็นเพื่อเปิดใช้การสมัครใช้บริการข้อมูลห้อง
val roomVMs : MutableStateFlow<List<RoomViewModel>>
val deviceVMs : MutableStateFlow<List<DeviceViewModel>>
val deviceVMsWithoutRooms : MutableStateFlow<List<DeviceViewModel>>
private suspend fun subscribeToRooms() {
// TODO: 5.2.1 - Subscribe the room data changes
// // Subscribe to changes on rooms:
// structure.rooms().collect { roomSet ->
// val roomVMs = mutableListOf<RoomViewModel>()
// // Store rooms in container ViewModels:
// for (room in roomSet) {
// roomVMs.add(RoomViewModel(room))
// }
// // Store the ViewModels:
// this.roomVMs.emit(roomVMs)
// }
}
private suspend fun subscribeToDevices() {
// TODO: 5.2.2 - Subscribe the device data changes in a structure
// // Subscribe to changes on devices:
// structure.devices().collect { deviceSet ->
// val deviceVMs = mutableListOf<DeviceViewModel>()
// val deviceWithoutRoomVMs = mutableListOf<DeviceViewModel>()
// // Store devices in container ViewModels:
// for (device in deviceSet) {
// val deviceVM = DeviceViewModel(device)
// deviceVMs.add(deviceVM)
// // For any device that's not in a room, additionally keep track of a separate list:
// if (!device.isInRoom)
// deviceWithoutRoomVMs.add(deviceVM)
// }
// // Store the ViewModels:
// this.deviceVMs.emit(deviceVMs)
// deviceVMsWithoutRooms.emit(deviceWithoutRoomVMs)
// }
}
นำการกำกับดูแลขั้นตอนที่ 5.2.3 และ 5.2.4 ในไฟล์ต้นฉบับ DevicesView.kt
ออกเพื่อแสดงผลรายการห้องเป็นเมนู
val selectedRoomVMs: List<RoomViewModel> =
selectedStructureVM.roomVMs.collectAsState().value
...
for (roomVM in selectedRoomVMs) {
// TODO: 5.2.3 - Render the list of rooms
// RoomListItem(roomVM)
// TODO: 5.2.4 - Render the list of devices in a room
// val deviceVMsInRoom: List<DeviceViewModel> = roomVM.deviceVMs.collectAsState().value
//
// for (deviceVM in deviceVMsInRoom) {
// DeviceListItem(deviceVM, homeAppVM)
// }
}
ตอนนี้คุณมีอุปกรณ์แล้ว เรามาเรียนรู้วิธีใช้งานกัน
6. ทำงานกับอุปกรณ์
Home API ใช้ออบเจ็กต์ HomeDevice
เพื่อบันทึกอุปกรณ์และความสามารถของอุปกรณ์ นักพัฒนาแอปสามารถสมัครรับข้อมูลแอตทริบิวต์ของอุปกรณ์และใช้แอตทริบิวต์ดังกล่าวเพื่อแสดงอุปกรณ์สมาร์ทโฮมในแอป
อ่านสถานะอุปกรณ์
ออบเจ็กต์ HomeDevice
จะแสดงชุดค่าคงที่ เช่น ชื่ออุปกรณ์หรือสถานะการเชื่อมต่อ ในฐานะนักพัฒนาแอป คุณจะดึงข้อมูลต่อไปนี้ได้ทันทีหลังจากได้รับอุปกรณ์จาก API
val id: String = device.id.id
val name: String = device.name
val connectivity: ConnectivityState =
device.sourceConnectivity.connectivityState
หากต้องการดูความสามารถของอุปกรณ์ คุณต้องดึงข้อมูลประเภทและลักษณะจาก HomeDevice
โดยคุณสามารถสมัครรับข้อมูลของประเภทอุปกรณ์ตามขั้นตอนต่อไปนี้ และดึงข้อมูลลักษณะจากประเภทอุปกรณ์
device.types().collect { typeSet ->
var primaryType : DeviceType = UnknownDeviceType()
for (typeInSet in typeSet)
if (typeInSet.metadata.isPrimaryType)
primaryType = typeInSet
val traits: List<Trait> = mutableListOf()
for (trait in primaryType.traits())
if (trait.factory in myTraits)
traits.add(trait)
for (trait in traits)
parseTrait(trait, primaryType)
}
อุปกรณ์แต่ละเครื่องจะมีชุด DeviceType
(ความสามารถที่รวมไว้) ที่รองรับ ซึ่งคุณเรียกดูได้โดยใช้ device.types()
อุปกรณ์ประเภทเหล่านี้มีลักษณะที่ดึงข้อมูลได้โดยใช้ type.traits()
อุปกรณ์ทุกเครื่องจะกำหนดประเภทใดประเภทหนึ่งเป็นประเภทหลัก (ซึ่งตรวจสอบได้โดยใช้ type.metadata.isPrimaryType
) ที่คุณควรแสดงในแอป เราขอแนะนำให้เรียกดูประเภทที่แสดงผลทั้งหมดและผสานรวมลักษณะทั้งหมดที่คุณใช้ได้เพื่อให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่สมบูรณ์
เมื่อดึงข้อมูลลักษณะแล้ว คุณสามารถแยกวิเคราะห์โดยใช้ฟังก์ชันต่อไปนี้เพื่อตีความค่า
fun <T : Trait?> parseTrait(trait : T, type: DeviceType) {
val status : String = when (trait) {
is OnOff -> { if (trait.onOff) "On" else "Off" }
is LevelControl -> { trait.currentLevel.toString() }
is BooleanState -> {
when (type.factory) {
ContactSensorDevice -> {
if (trait.stateValue) "Closed"
else "Open"
}
else -> ...
}
}
else -> ...
}
}
โปรดทราบว่าสิ่งที่ลักษณะแสดงอาจแตกต่างกันไปโดยขึ้นอยู่กับประเภทอุปกรณ์ที่แสดงลักษณะนั้น (ดู BooleanState
ในตัวอย่างก่อนหน้านี้) คุณจึงต้องคำนึงถึงบริบทของอุปกรณ์แต่ละประเภทเพื่อให้เข้าใจสิ่งที่ลักษณะของอุปกรณ์แสดง
ยกเลิกการคอมเมนต์ขั้นตอนที่ 6.1.1 และ 6.1.2 ในไฟล์ต้นฉบับ DeviceViewModel.kt
เพื่อเรียกข้อมูลสถานะ
private suspend fun subscribeToType() {
// Subscribe to changes on device type, and the traits/attributes within:
device.types().collect { typeSet ->
// Container for the primary type for this device:
var primaryType : DeviceType = UnknownDeviceType()
...
// TODO: 6.1.1 - Determine the primary type for this device
// // Among all the types returned for this device, find the primary one:
// for (typeInSet in typeSet)
// if (typeInSet.metadata.isPrimaryType)
// primaryType = typeInSet
//
// // Optional: For devices with a single type that did not define a primary:
// if (primaryType is UnknownDeviceType && typeSet.size == 1)
// primaryType = typeSet.first()
// Container for list of supported traits present on the primary device type:
val supportedTraits: List<Trait> = getSupportedTraits(primaryType.traits())
...
}
fun getSupportedTraits(traits: Set<Trait>) : List<Trait> {
val supportedTraits: MutableList<Trait> = mutableListOf()
// TODO: 6.1.2 - Get only the supported traits for this device
// for (trait in traits)
// if (trait.factory in HomeApp.supportedTraits)
// supportedTraits.add(trait)
return supportedTraits
}
นำการคอมเมนต์ออกจากขั้นตอนที่ 6.1.3 ใน DeviceView.kt
เพื่อแสดงผลแอตทริบิวต์ OnOff รวมถึงชื่อและสถานะเป็น String
Box (Modifier.padding(horizontal = 24.dp, vertical = 8.dp)) {
when (trait) {
is OnOff -> {
// TODO: 6.1.3 - Render controls based on the trait type
// Column (Modifier.fillMaxWidth()) {
// Text(trait.factory.toString(), fontSize = 20.sp)
// Text(DeviceViewModel.getTraitStatus(trait, type), fontSize = 16.sp)
// }
...
}
is LevelControl -> {
...
}
is BooleanState -> {
...
}
is OccupancySensing -> {
...
}
...
}
หากคุณเรียกใช้แอปตอนนี้ด้วยอุปกรณ์ประเภทที่รองรับ (เช่น อุปกรณ์ Light) แอปควรแสดงสถานะล่าสุดของอุปกรณ์ทั้งหมด
ใช้คำสั่งในอุปกรณ์
หากต้องการออกคำสั่งไปยังอุปกรณ์ Home API มีฟังก์ชันอำนวยความสะดวกในออบเจ็กต์ลักษณะ เช่น trait.on()
หรือ trait.moveToLevel(...)
ดังนี้
fun <T : Trait?> issueCommand(trait : T) {
when (trait) {
is OnOff -> {
// trait.on()
// trait.off()
}
is LevelControl -> {
// trait.moveToLevel(...)
// trait.moveToLevelWithOnOff(...)
}
}
}
เคล็ดลับ: เมื่อคุณระบุประเภทของแทร็กแล้ว ให้ใช้ฟีเจอร์เติมข้อความอัตโนมัติของ Android Studio เพื่อดูประเภทการดำเนินการที่ใช้โต้ตอบกับแทร็กได้
ยกเลิกการคอมเมนต์ขั้นตอนที่ 6.2.1 ใน DeviceView.kt
เพื่อเพิ่มการควบคุมฟังก์ชันในแอป
Box (Modifier.padding(horizontal = 24.dp, vertical = 8.dp)) {
when (trait) {
is OnOff -> {
....
// TODO: 6.2.1 - Render controls based on the trait type
// Switch (checked = (trait.onOff == true), modifier = Modifier.align(Alignment.CenterEnd),
// onCheckedChange = { state ->
// scope.launch { if (state) trait.on() else trait.off() }
// },
// enabled = isConnected
// )
}
หากเรียกใช้แอปตอนนี้ คุณควรควบคุมอุปกรณ์จริงในชีวิตจริงได้
หากแตะตัวควบคุมเปิด/ปิดบนหลอดไฟ อุปกรณ์ก็ควรจะเปิดขึ้น
ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีควบคุมอุปกรณ์ได้ที่ควบคุมอุปกรณ์ใน Android
7. อุปกรณ์ค่าคอมมิชชัน
Commissioning API ช่วยให้นักพัฒนาแอปเพิ่มอุปกรณ์ลงในระบบนิเวศของ Google Home และทำให้อุปกรณ์พร้อมใช้งานโดยใช้ Home API รองรับเฉพาะอุปกรณ์ Matter เท่านั้น ในส่วนนี้ เราจะดูวิธีเปิดใช้การจัดเตรียมอุปกรณ์ในแอป
ก่อนเริ่มต้นส่วนนี้ โปรดตรวจสอบว่าคุณมีคุณสมบัติตรงตามข้อกําหนดเบื้องต้นต่อไปนี้
- เพิ่ม Google Hub ที่รองรับ Matter ซึ่งอยู่ในเครือข่ายเดียวกับโทรศัพท์ Android ลงในแอป Google Home แล้ว
- คุณได้สร้างโปรเจ็กต์สำหรับนักพัฒนาแอปใน Google Home Developer Console ด้วย VID
0xFFF1
และ PID0x8000
หากมีอุปกรณ์ Matter จริงที่มีคิวอาร์โค้ดสำหรับการเตรียมใช้งาน ให้ข้ามไปที่เปิดใช้ API สำหรับการเตรียมใช้งาน หรือจะข้ามไปที่ส่วนถัดไปก็ได้ ซึ่งเราจะพูดถึงวิธีใช้แอปอุปกรณ์เสมือน Matter (MVD) เพื่อสร้างอุปกรณ์เสมือนที่จะได้รับค่าคอมมิชชัน
ไม่บังคับ: เตรียมอุปกรณ์ Matter ที่เปิดใช้การเรียกเก็บเงิน
วิธีที่ง่ายที่สุดในการเตรียมอุปกรณ์ Matter ที่เรียกเก็บค่าคอมมิชชันได้คือการใช้อุปกรณ์จำลองที่แอป Matter Virtual Device (MVD) มีให้
หลังจากติดตั้ง MVD และตั้งค่าไฟร์วอลล์แล้ว ให้เรียกใช้ MVD โดยทำดังนี้
สร้างอุปกรณ์เปิด/ปิด โปรดทราบว่ายังไม่ได้สั่งทํา คุณจะสั่งทําได้ในภายหลังในโค้ดแล็บนี้
เปิดใช้ Commissioning API
Commissioning API ทำงานนอกกิจกรรมของแอป ดังนั้นการจัดการการต่ออายุจึงต้องดำเนินการแตกต่างจาก Home API อื่นๆ คุณต้องมีตัวแปร 2 รายการเพื่อให้แอปพร้อมสำหรับการเตรียมใช้งาน
ตัวแปรหนึ่งคือ ActivityResultLauncher
ซึ่งใช้เพื่อส่งความตั้งใจในการว่าจ้างและจัดการการเรียกกลับผลลัพธ์ ตัวแปรอีกตัวหนึ่งคือ CommissioningResult
ซึ่งเป็นออบเจ็กต์ที่ใช้จัดเก็บผลการว่าจ้าง ดูตัวอย่างต่อไปนี้สำหรับวิธีตั้งค่าการจัดเตรียม
var launcher: ActivityResultLauncher<IntentSenderRequest>
lateinit var commissioningResult: CommissioningResult?
launcher = activity.registerForActivityResult(StartIntentSenderForResult()) { result ->
try {
commissioningResult = CommissioningResult.fromIntentSenderResult(
result.resultCode, result.data)
} catch (exception: ApiException) {
// Catch any issues
}
}
เมื่อตั้งค่าขั้นตอนการมอบหมายแล้ว คุณจะต้องสร้างความตั้งใจในการมอบหมาย และเปิดใช้งานโดยใช้ Launcher ที่เราสร้างในตัวอย่างก่อนหน้านี้ เราขอแนะนำให้วาง Intent และ Launcher ไว้ในฟังก์ชันเฉพาะ เช่น ต่อไปนี้ ฟังก์ชันเฉพาะสามารถเชื่อมโยงกับองค์ประกอบ UI (เช่น ปุ่ม +เพิ่มอุปกรณ์) และเรียกใช้ตามคำขอของผู้ใช้
fun requestCommissioning() {
// Retrieve the onboarding payload used when commissioning devices:
val payload = activity.intent?.getStringExtra(Matter.EXTRA_ONBOARDING_PAYLOAD)
scope.launch {
// Create a commissioning request to store the device in Google's Fabric:
val request = CommissioningRequest.builder()
.setStoreToGoogleFabric(true)
.setOnboardingPayload(payload)
.build()
// Initialize client and sender for commissioning intent:
val client: CommissioningClient = Matter.getCommissioningClient(context)
val sender: IntentSender = client.commissionDevice(request).await()
// Launch the commissioning intent on the launcher:
launcher.launch(IntentSenderRequest.Builder(sender).build())
}
}
ยกเลิกการคอมเมนต์ขั้นตอนที่ 7.1.1 ใน CommissioningManager.kt
เพื่อเปิดใช้ความสามารถในการจัดเตรียมอุปกรณ์และทำให้ปุ่ม +เพิ่มอุปกรณ์ทำงานในแอปตัวอย่าง
// Called by +Add Device button in DeviceView.kt
fun requestCommissioning() {
// Retrieve the onboarding payload used when commissioning devices:
val payload = activity.intent?.getStringExtra(Matter.EXTRA_ONBOARDING_PAYLOAD)
// TODO: 7.1.1 - Launch the commissioning intent
// scope.launch {
// // Create a commissioning request to store the device in Google's Fabric:
// val request = CommissioningRequest.builder()
// .setStoreToGoogleFabric(true)
// .setOnboardingPayload(payload)
// .build()
// // Initialize client and sender for commissioning intent:
// val client: CommissioningClient = Matter.getCommissioningClient(context)
// val sender: IntentSender = client.commissionDevice(request).await()
// // Launch the commissioning intent on the launcher:
// launcher.launch(IntentSenderRequest.Builder(sender).build())
// }
}
การดำเนินการฟังก์ชันนี้จะเริ่มต้นขั้นตอนการเตรียมใช้งาน ซึ่งควรแสดงหน้าจอที่คล้ายกับภาพหน้าจอต่อไปนี้
ทําความเข้าใจขั้นตอนการเปิดใช้งาน
โฟลว์การจัดเตรียมประกอบด้วยชุดหน้าจอที่แนะนำผู้ใช้ในการเพิ่มอุปกรณ์ลงในบัญชี Google
ผู้ใช้จะเห็นเครื่องสแกนคิวอาร์โค้ดที่สามารถใช้สแกนคิวอาร์โค้ดจากอุปกรณ์ Matter จากนั้นขั้นตอนจะแสดงข้อตกลงของผู้ใช้ การค้นหาและการจัดเตรียมอุปกรณ์ และการตั้งชื่ออุปกรณ์ เมื่อดำเนินการเสร็จแล้ว ขั้นตอนจะเปลี่ยนโฟกัสกลับไปที่แอป และส่งผลลัพธ์การจัดเตรียมในฟังก์ชันการเรียกกลับที่เราร่างไว้ในส่วนก่อนหน้า
ประโยชน์อย่างหนึ่งของ Commissioning API คือ SDK จะจัดการขั้นตอน UX เพื่อให้นักพัฒนาแอปเริ่มต้นใช้งานได้อย่างรวดเร็ว นอกจากนี้ ยังช่วยให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่สอดคล้องกันเมื่อเพิ่มอุปกรณ์ในแอปต่างๆ
ดูข้อมูลเพิ่มเติมเกี่ยวกับ Commissioning API ได้ที่ Commissioning API ใน Android
8. ยินดีด้วย
ยินดีด้วย คุณสร้างแอป Android โดยใช้ Google Home API เรียบร้อยแล้ว ตลอดทั้งโค้ดแล็บนี้ คุณได้สำรวจสิทธิ์ อุปกรณ์ โครงสร้าง และ API การจัดเตรียม ในโค้ดแล็บถัดไปอย่างสร้างการทำงานอัตโนมัติขั้นสูงโดยใช้ Home API ใน Android Codelab เราจะสำรวจ Automation และ Discovery API รวมถึงสร้างแอปให้เสร็จสมบูรณ์
เราหวังว่าคุณจะสนุกกับการสร้างแอปที่ควบคุมอุปกรณ์ภายในระบบนิเวศของ Google Home อย่างสร้างสรรค์
ขั้นตอนถัดไป
- ไปต่อในส่วนถัดไปของการเรียนรู้ Home API ใน Android โดยทำตาม Codelab ลำดับที่ 2 ในชุดนี้ให้เสร็จสิ้น สร้างการทำงานอัตโนมัติขั้นสูงโดยใช้ Home API ใน Android
- คุณสามารถติดต่อเราเพื่อขอคำแนะนำหรือรายงานปัญหาผ่านเครื่องมือติดตามปัญหาในหัวข้อการสนับสนุนสมาร์ทโฮม