1. לפני שמתחילים
ממשקי ה-API של Google Home מספקים קבוצה של ספריות למפתחי Android, שמאפשרות להם להשתמש בסביבה העסקית של Google Home. בעזרת ממשקי ה-API החדשים האלה, מפתחים יכולים ליצור אפליקציות שמאפשרות להפעיל מכשירים לבית חכם ולנהל אותם בצורה חלקה.
Google מספקת אפליקציית דוגמה ל-Android למפתחים שרוצים לגשת לדוגמה עובדת באמצעות ממשקי ה-API של Google Home. סדנת הקוד הזו מבוססת על הסתעפות של אפליקציית הדוגמה, שבה מוסבר איך להשתמש בממשקי ה-API של ההרשאות, ההפעלה, המכשיר והמבנה.
דרישות מוקדמות
- ידע בסביבת הבית החכם של Google (מ-Cloud ל-Cloud ו-Matter).
- תחנת עבודה עם Android Studio (גרסה 2024.3.1 Ladybug ואילך) מותקנת.
- טלפון Android שעומד בדרישות של ממשקי ה-API של Home (מפורטות בקטע דרישות מוקדמות) עם שירותי Google Play ואפליקציית Google Home מותקנים.
- Google Home Hub תואם שתומך בממשקי Google Home API.
- אופציונלי – מכשיר בית חכם תואם לממשקי ה-API של Google Home.
מה תלמדו
- איך מפתחים אפליקציה ל-Android באמצעות ממשקי Google Home API, עם שיטות מומלצות.
- איך משתמשים ב-Device API וב-Structure API כדי לייצג בית חכם ולשלוט בו.
- איך משתמשים ב-Commissioning APIs כדי להוסיף מכשירים לסביבה העסקית של Google Home.
אופציונלי: הגדרת הבית
לפני שמשתמשים בממשקי ה-API של Google Home, צריך להגדיר בית בחשבון Google באמצעות אפליקציית Google Home ולהוסיף כמה מכשירים. בקטע הזה נסביר איך לעשות זאת באמצעות Google Home Playground, שמספק מכשירים וירטואליים לבית חכם.
פותחים את הדף home-playground.withgoogle.com בדפדפן האינטרנט, נכנסים באמצעות חשבון Google ומחפשים את המכשירים המהולים הבאים:
- outlet1: On/Off plug
- light2: תאורה שניתן לעמעם
- light3: תאורת הפעלה/כיבוי
- ac3: מזגן
- blinds4: Window Covering
- washer5: מכונת כביסה חכמה
פותחים את אפליקציית Google Home בנייד, מקישים על הלחצן הוספה ובוחרים באפשרות תואם ל-Google Home. מחפשים את 'playground' ברשימה, בוחרים את הפרויקט 'Google Home Playground' ומקישים על Continue.
בדף Playground של Google Home יוצג דף לאישור החשבון. מקישים על אישור או על כניסה באמצעות חשבון Google. כל המכשירים שהגדרתם באפליקציית האינטרנט יופיעו באפליקציה לנייד.
בוחרים את כל המכשירים ומסיימים את תהליך ההגדרה. כשחוזרים לדף הבית, מוצגים בו כל המכשירים הזמינים.
המכשירים הנתמכים ברשימה זמינים עכשיו לשימוש עם ממשקי ה-API של Google Home.
2. הגדרת הפרויקט
התרשים הבא מדגים את הארכיטקטורה של אפליקציה עם Home APIs:
- קוד האפליקציה: הקוד המרכזי שמפתחים עובדים עליו כדי ליצור את ממשק המשתמש של האפליקציה ואת הלוגיקה של האינטראקציה עם ה-SDK של Home APIs.
- Home APIs SDK: Home APIs SDK ש-Google מספקת פועל עם שירות Home APIs ב-GMSCore כדי לשלוט במכשירי הבית החכם. מפתחים יוצרים אפליקציות שפועלות עם Home APIs על ידי הוספת ערכת ה-SDK של Home APIs לחבילת האפליקציה.
- GMSCore ב-Android: GMSCore, שנקרא גם Google Play Services, הוא פלטפורמה של Google שמספקת שירותי ליבה למערכת ומאפשרת להפעיל תכונות חשובות בכל מכשירי Android שאושרו. המודול הביתי של Google Play Services מכיל את השירותים שמקיימים אינטראקציה עם ממשקי ה-API של Home.
הגדרת 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
פיתוח גרסת build של האפליקציה לדוגמה
מבצעים את השלבים 1 עד 5 בקטע פיתוח האפליקציה.
כשהאפליקציה פועלת בטלפון, יוצג הדף הראשי של האפליקציה לדוגמה. עם זאת, לא תוכלו להיכנס לחשבון עד שתגדירו אימות OAuth ותטמיעו את החלקים החסרים באמצעות Permission API.
3. הגדרת אימות
ממשקי ה-API של Home משתמשים ב-OAuth 2.0 כדי להעניק גישה למכשירים במבנה. OAuth מאפשר למשתמש להעניק הרשאה לאפליקציה או לשירות בלי לחשוף את פרטי הכניסה שלו.
פועלים לפי ההוראות במאמר הגדרת הסכמה ל-OAuth כדי להגדיר את מסך ההסכמה. חשוב ליצור לפחות חשבון בדיקה אחד.
לאחר מכן, פועלים לפי ההוראות במאמר הגדרת פרטי כניסה ל-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
כל האפליקציות שמשתמשות בממשקי ה-API של Home מאתחלות אובייקט HomeClient
, שהוא הממשק הראשי לאינטראקציה עם ממשקי ה-API. אנחנו מכינים את האובייקט הזה ב-initializer של הכיתה 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
.
כל האינטראקציות שלנו עם ממשקי ה-API של Home יתבצעו דרך האובייקט HomeClient
.
שימוש ב-Permissions API
אימות המשתמשים של ממשקי ה-API של Home מתבצע באמצעות Permissions API. קובץ המקור PermissionsManager.kt
של אפליקציית הדוגמה מכיל קוד לאימות משתמשים. מסירים את התגובה (comment) מהתוכן של הפונקציות 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 APIs, מודל הנתונים מורכב מ:
Structure
מייצג בית שמכיל חדרים ומכשירים.Room
הוא חלק ממבנה ומכיל מכשירים.- אפשר להקצות מכשירים (שמוגדרים בתור
HomeDevice
) למבנה (או לבית) או לחדר במבנה. - מכשירים מורכבים ממכונה אחת או יותר של
DeviceType
. DeviceType
מורכב מ-Trait
מכונות.Trait
מורכב ממופעיAttribute
(לקריאה/כתיבה), ממופעיCommand
(לשליטה במאפיינים) וממופעיEvent
(לקריאה או להרשמה לרשומות של שינויים קודמים).- מכונות
Automation
הן חלק ממבנה, והן משתמשות במטא-נתונים ובמכשירים של הבית כדי לבצע אוטומציה של משימות בבית.
בקטע הזה תלמדו איך לפתח את קוד המקור כדי להראות איך משתמשים ב-Structure API כדי לנתח ולייצר פריימים של המבנים, החדרים, המכשירים וכו' בבית.
קריאת מבנים
העיצוב של Home APIs מבוסס על Kotlin Flows כדי להעביר את אובייקטי מודל הנתונים (לדוגמה, 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
) נרשמות למודל התצוגה כדי לקבל את המצבים ולייצר את ממשק המשתמש כך שישקף את השינויים האלה. - כשמשתמש לוחץ על לחצן בתצוגה (לדוגמה, הלחצן 'הפעלה' של מכשיר תאורה), האירועים מפעילים את הפונקציות של מודל התצוגה, שמפעילות את הפונקציות של ממשקי ה-API של Home שתואמות לאירוע (לדוגמה, הפקודה
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,
שמפעילה יצירת מחדש של ממשק המשתמש כשנתוני המבנה משתנים. מסירים את ההערה מקוד המקור בשלב 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. עבודה עם מכשירים
ממשקי ה-API של Home משתמשים באובייקט 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), אמורים להופיע בה המצבים המעודכנים של כל המכשירים.
הפעלת פקודות במכשיר
כדי להנפיק פקודות למכשירים, ממשקי ה-API של Home מספקים פונקציות נוחות באובייקטים של מאפיינים כמו 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 ולהפוך אותם לזמינים לשליטה באמצעות ממשקי ה-API של Home. יש תמיכה רק במכשירי Matter. בקטע הזה נסביר איך מפעילים את ההרשאה לשימוש במכשיר באפליקציות.
לפני שמתחילים את הקטע הזה, חשוב לוודא שהתנאים המוקדמים הבאים מתקיימים:
- Google Hub שתומך ב-Matter שנמצא באותה רשת שבה נמצא טלפון Android שלכם נוסף לאפליקציית Google Home.
- יצרתם פרויקט פיתוח ב-מסוף הפיתוח של Google Home עם ה-VID
0xFFF1
וה-PID0x8000
.
אם יש לכם מכשיר פיזי של Matter עם קוד QR להפעלה, אתם יכולים לדלג לקטע הפעלת ה-API להפעלה. אחרת, אפשר להמשיך לקטע הבא, שבו נסביר איך משתמשים באפליקציית Matter Virtual Device (MVD) כדי ליצור מכשירים וירטואליים שאפשר לקבל עליהם עמלות.
אופציונלי: הכנת מכשיר תואם ל-Matter לצורך קבלת עמלות
הדרך הפשוטה ביותר להכין מכשיר Matter שאפשר לקבל עליו עמלות היא באמצעות מכשיר מודולציה שמסופק על ידי האפליקציה Matter Virtual Device (MVD).
אחרי שמתקינים את ה-MVD ומגדירים את חומת האש, מריצים את ה-MVD:
יוצרים מכשיר OnOff. שימו לב שהמכשיר עדיין לא הופעל – תפעילו אותו בהמשך הקודלאב.
הפעלת Commissioning API
ממשק ה-API להפעלה פועל מחוץ ל'פעילות' של האפליקציה, ולכן צריך לטפל בהפעלה באופן שונה מאשר בממשקי ה-API האחרים של Home. כדי להכין את האפליקציה להפעלה, צריך שני משתנים.
אחד המשתנים הוא 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
}
}
אחרי שתגדירו את תהליך ההזמנה, תוכלו ליצור את כוונה ההזמנה ולהפעיל אותה באמצעות מרכז האפליקציות שיצרנו בדוגמה הקודמת. מומלץ להציב את ה-intent ואת מרכז האפליקציות בפונקציה ייעודית, כמו זו שבהמשך. אפשר לקשר פונקציה ייעודית לרכיב בממשק המשתמש (למשל, הלחצן +הוספת מכשיר) ולהפעיל אותה על סמך בקשת המשתמש:
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 שלו:
המשתמשים יקבלו הודעה על סורק קודי QR, שבו יוכלו לסרוק את קודי ה-QR ממכשירי Matter. לאחר מכן, התהליך ימשיך עם הצגת הסכם המשתמש, זיהוי המכשיר והפעלה שלו, ואימות השם של המכשיר. בסיום התהליך, המיקוד יחזור לאפליקציה והתוצאה של ההפעלה תועבר לפונקציית ה-callback שניסחנו בקטע הקודם.
אחד היתרונות של ממשקי ה-API להפעלה הוא שזרימת חוויית המשתמש מטופלת על ידי ה-SDK, כך שמפתחים יכולים להתחיל לעבוד מהר מאוד. כך המשתמשים נהנים מחוויה עקבית כשהם מוסיפים מכשירים באפליקציות שונות.
מידע נוסף על Commissioning API זמין במאמר Commissioning API ב-Android.
8. מעולה!
מעולה! סיימתם ליצור אפליקציה ל-Android באמצעות ממשקי ה-API של Google Home. במהלך הקודלאב הזה, למדתם על הרשאות, מכשירים, מבנים וממשקי API להפעלה. בקודלאב הבא, יצירת אוטומציות מתקדמות באמצעות ממשקי ה-API של Home ב-Android Codelab, נלמד על ממשקי ה-API של אוטומציה ו-Discovery ונשלים את האפליקציה.
אנחנו מקווים שתיהנו לפתח אפליקציות שמאפשרות לשלוט באופן יצירתי במכשירים בסביבה העסקית של Google Home.
השלבים הבאים
- כדי להמשיך את הלמידה של ממשקי ה-API של Home ב-Android, כדאי להשלים את סדנת הקוד השנייה בסדרה הזו: יצירת אוטומציות מתקדמות באמצעות ממשקי ה-API של Home ב-Android.
- אתם יכולים לפנות אלינו עם המלצות או לדווח על בעיות דרך מעקב הבעיות, בנושא התמיכה ב-Smart Home.