פיתוח אפליקציה לנייד באמצעות ממשקי ה-API של Home ב-Android

1. לפני שמתחילים

ממשקי ה-API של Google Home מספקים קבוצה של ספריות למפתחי Android, שמאפשרות להם להשתמש בסביבה העסקית של Google Home. בעזרת ממשקי ה-API החדשים האלה, מפתחים יכולים ליצור אפליקציות שמאפשרות להפעיל מכשירים לבית חכם ולנהל אותם בצורה חלקה.

Google מספקת אפליקציית דוגמה ל-Android למפתחים שרוצים לגשת לדוגמה עובדת באמצעות ממשקי ה-API של Google Home. סדנת הקוד הזו מבוססת על הסתעפות של אפליקציית הדוגמה, שבה מוסבר איך להשתמש בממשקי ה-API של ההרשאות, ההפעלה, המכשיר והמבנה.

דרישות מוקדמות

מה תלמדו

  • איך מפתחים אפליקציה ל-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: מכונת כביסה חכמה

914d23a42b72df8f.png

פותחים את אפליקציית Google Home בנייד, מקישים על הלחצן הוספה ובוחרים באפשרות תואם ל-Google Home. מחפשים את 'playground' ברשימה, בוחרים את הפרויקט 'Google Home Playground' ומקישים על Continue.

e9ec257b0d9b1ed2.png29fd7416e274d216.pngd974573af0611fd8.png

בדף Playground של Google Home יוצג דף לאישור החשבון. מקישים על אישור או על כניסה באמצעות חשבון Google. כל המכשירים שהגדרתם באפליקציית האינטרנט יופיעו באפליקציה לנייד.

13108a3a15440151.png8791a6d33748f7c8.png

בוחרים את כל המכשירים ומסיימים את תהליך ההגדרה. כשחוזרים לדף הבית, מוצגים בו כל המכשירים הזמינים.

2b021202e6fd1750.png

המכשירים הנתמכים ברשימה זמינים עכשיו לשימוש עם ממשקי ה-API של Google Home.

2. הגדרת הפרויקט

התרשים הבא מדגים את הארכיטקטורה של אפליקציה עם Home APIs:

הארכיטקטורה של Home APIs לאפליקציה ל-Android

  • קוד האפליקציה: הקוד המרכזי שמפתחים עובדים עליו כדי ליצור את ממשק המשתמש של האפליקציה ואת הלוגיקה של האינטראקציה עם ה-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 בקטע פיתוח האפליקציה.

32f2b3c0cd80fcf1.png

כשהאפליקציה פועלת בטלפון, יוצג הדף הראשי של האפליקציה לדוגמה. עם זאת, לא תוכלו להיכנס לחשבון עד שתגדירו אימות 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()) }
    }
}

עכשיו מריצים את האפליקציה בטלפון, פועלים לפי השלבים ומעניקים את ההרשאות. התהליך אמור להיראות כך:

c263dcee4e945bf1.png f518cfd1fdb8a9d8.png 59937372f28c472f.png 383073ae57d2ced4.png 89f774a2ba6898ae.png

ההודעה 'טעינה' לא נעלמת, אבל הסיבה לכך היא שלא הטמענו את הקוד שקורא את המבנה והמכשירים. נעשה זאת בקטע הבא.

5. הסבר על מודל הנתונים

ב-Home APIs, מודל הנתונים מורכב מ:

  • Structure מייצג בית שמכיל חדרים ומכשירים.
  • Room הוא חלק ממבנה ומכיל מכשירים.
  • אפשר להקצות מכשירים (שמוגדרים בתור HomeDevice) למבנה (או לבית) או לחדר במבנה.
  • מכשירים מורכבים ממכונה אחת או יותר של DeviceType.
  • DeviceType מורכב מ-Trait מכונות.
  • Trait מורכב ממופעי Attribute (לקריאה/כתיבה), ממופעי Command (לשליטה במאפיינים) וממופעי Event (לקריאה או להרשמה לרשומות של שינויים קודמים).
  • מכונות Automation הן חלק ממבנה, והן משתמשות במטא-נתונים ובמכשירים של הבית כדי לבצע אוטומציה של משימות בבית.

76d35b44d5a8035e.png

בקטע הזה תלמדו איך לפתח את קוד המקור כדי להראות איך משתמשים ב-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
//          }
//      )
//  }
}
...

מריצים את האפליקציה שוב. התפריט אמור להופיע כשתקישו על החץ:

f1fc2be1cb6436b6.png

ניתוח המבנה

השלב הבא הוא סריקה של אובייקטי הבית במבנה. אחזור החדרים מהמבנה:

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)
//   }
}

עכשיו, אחרי שהמכשירים הגיעו, נלמד איך לעבוד איתם.

e715ddda50e04839.png

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), אמורים להופיע בה המצבים המעודכנים של כל המכשירים.

1bd8b3b2796c4c7a.png

הפעלת פקודות במכשיר

כדי להנפיק פקודות למכשירים, ממשקי ה-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
//   )
}

אם תפעילו את האפליקציה עכשיו, היא אמורה לאפשר לכם לשלוט במכשירים פיזיים בעולם האמיתי.

אם מקישים על מתג ההפעלה והכיבוי בנורת, המכשיר אמור להיפתח.

c8ed3ecf5031546e.png

מידע נוסף על שליטה במכשירים זמין במאמר שליטה במכשירים ב-Android.

7. חיבור מכשירים

Commissioning API מאפשר למפתחים להוסיף מכשירים לסביבה העסקית של Google Home ולהפוך אותם לזמינים לשליטה באמצעות ממשקי ה-API של Home. יש תמיכה רק במכשירי Matter. בקטע הזה נסביר איך מפעילים את ההרשאה לשימוש במכשיר באפליקציות.

לפני שמתחילים את הקטע הזה, חשוב לוודא שהתנאים המוקדמים הבאים מתקיימים:

אם יש לכם מכשיר פיזי של Matter עם קוד QR להפעלה, אתם יכולים לדלג לקטע הפעלת ה-API להפעלה. אחרת, אפשר להמשיך לקטע הבא, שבו נסביר איך משתמשים באפליקציית Matter Virtual Device (MVD) כדי ליצור מכשירים וירטואליים שאפשר לקבל עליהם עמלות.

אופציונלי: הכנת מכשיר תואם ל-Matter לצורך קבלת עמלות

הדרך הפשוטה ביותר להכין מכשיר Matter שאפשר לקבל עליו עמלות היא באמצעות מכשיר מודולציה שמסופק על ידי האפליקציה Matter Virtual Device (MVD).

אחרי שמתקינים את ה-MVD ומגדירים את חומת האש, מריצים את ה-MVD:

b20283893073ac1b.png

יוצרים מכשיר OnOff. שימו לב שהמכשיר עדיין לא הופעל – תפעילו אותו בהמשך הקודלאב.

5f4855b808312898.png

הפעלת 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())
// }
}

הפעלת הפונקציה הזו אמורה להפעיל את תהליך ההפעלה, שבו אמור להופיע מסך שדומה לצילום המסך הבא:

baae45588f460664.png

הסבר על תהליך ההפעלה

תהליך ההפעלה כולל סדרה של מסכים שמנחים את המשתמש להוסיף מכשיר לחשבון Google שלו:

2fb0404820d4a035.png 3cbfa8ff9cfd5ee4.png a177c197ee7a67bf.png 3fdef24672c77c0.png dec8e599f9aa119.png

המשתמשים יקבלו הודעה על סורק קודי 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.

השלבים הבאים