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

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

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

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

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

מה תלמדו

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

אופציונלי: הגדרת הבית

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

פותחים את home-playground.withgoogle.com בדפדפן האינטרנט, נכנסים לחשבון Google ובודקים אם מופיעים המכשירים המדומים הבאים:

  • outlet1: On/Off plug
  • light2: Dimmable light
  • ‫light3: הפעלה/השבתה של התאורה
  • ‫ac3: מזגן
  • blinds4: Window Covering
  • washer5: Smart washer

914d23a42b72df8f.png

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

e9ec257b0d9b1ed2.png29fd7416e274d216.pngd974573af0611fd8.png

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

13108a3a15440151.png8791a6d33748f7c8.png

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

2b021202e6fd1750.png

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

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

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

ארכיטקטורה של ממשקי API של Home לאפליקציית Android

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

יצירת אפליקציה לדוגמה

מבצעים את השלבים 1 עד 5 בקטע פיתוח האפליקציה.

32f2b3c0cd80fcf1.png

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

אימות המשתמשים ב-Home APIs מתבצע באמצעות 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()) }
    }
}

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

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

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

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

בממשקי ה-API של Home, מודל הנתונים מורכב מהרכיבים הבאים:

  • 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, בתור מחזיק המצב) נרשמים ל-Flows מ-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.roomId == null)
  devicesWithoutRooms.add(device)

שוב, בקוד לדוגמה הקיים, אנחנו נרשמים ל-flow כדי לקבל את הרשימה העדכנית של החדרים והמכשירים. בודקים את הקוד בשלבים 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.roomId == null)
//               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 ואת ה-Launcher בפונקציה ייעודית כמו זו שמופיעה בהמשך. אפשר לקשר פונקציה ייעודית לרכיב בממשק המשתמש (כמו הלחצן +הוספת מכשיר) ולהפעיל אותה על סמך בקשת המשתמש:

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 באמצעות Google Home APIs. במהלך ה-codelab הזה, למדתם על הרשאות, מכשירים, מבנים וממשקי API להפעלה. ב-codelab הבא, Create advanced automations using the Home APIs on Android Codelab (יצירת אוטומציות מתקדמות באמצעות ממשקי Home API ב-Android Codelab), נסביר על ממשקי ה-API של Automation ו-Discovery ונשלים את האפליקציה.

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

השלבים הבאים