使用 Android 上的 Home API 建構行動應用程式

1. 事前準備

Google Home API 提供一組程式庫,可讓 Android 開發人員運用 Google Home 生態系統。有了這些新的 API,開發人員就能建構應用程式,無縫啟用及控制智慧住宅裝置。

Google 提供 Android 範例應用程式,供開發人員使用 Google Home API 存取實際運作範例。本程式碼研究室以範例應用程式的分支為基礎,逐步引導您瞭解如何使用權限、調試、裝置和結構體 API。

必要條件

課程內容

  • 如何使用 Google Home API 建構 Android 應用程式,並遵循最佳做法。
  • 如何使用 Device 和 Structure API 表示及控制智慧住宅。
  • 如何使用啟用 API 將裝置新增至 Google Home 生態系統。

選用:設定住家

使用 Google Home API 前,請先使用 Google Home 應用程式在 Google 帳戶中設定住家,並新增幾個裝置。本節將說明如何使用 Google Home Playground 提供虛擬智慧型家居裝置。

在網路瀏覽器中開啟 home-playground.withgoogle.com,使用 Google 帳戶登入,然後查看是否有下列模擬裝置顯示:

  • outlet1:開/關插頭
  • light2:可調光燈
  • light3:開啟/關閉燈
  • ac3:空調
  • blinds4:窗簾
  • washer5: 智慧型洗衣機

914d23a42b72df8f.png

在行動裝置上開啟 Google Home 應用程式,輕觸「新增」按鈕,然後選取「與 Google Home 連結」。在清單中搜尋「playground」,然後選取「Google Home Playground」專案,然後輕觸「Continue」

e9ec257b0d9b1ed2.png29fd7416e274d216.pngd974573af0611fd8.png

Google Home Playground 會顯示帳戶授權頁面。輕觸「授權」或「使用 Google 帳戶登入」。你會在行動應用程式中看到透過網頁應用程式設定的所有裝置。

13108a3a15440151.png8791a6d33748f7c8.png

選取所有裝置並完成設定程序。返回首頁後,你會看到所有可用的裝置。

2b021202e6fd1750.png

清單中的支援裝置現在可搭配 Google Home API 使用。

2. 設定專案

下圖說明 Home API 應用程式的架構:

Android 應用程式 Home API 的架構

  • 應用程式程式碼:開發人員用來建構應用程式使用者介面和與 Home API SDK 互動邏輯的核心程式碼。
  • Home APIs SDK:Google 提供的 Home APIs SDK 可搭配 GMSCore 中的 Home APIs Service 使用,用於控制智慧型家居裝置。開發人員可以將 Home API 與 Home API SDK 一起打包,藉此建構可與 Home API 搭配使用的應用程式。
  • Android 上的 GMSCore:GMSCore (又稱為 Google Play 服務) 是 Google 平台,可提供核心系統服務,讓所有獲得認證的 Android 裝置執行關鍵功能。Google Play 服務的首頁模組包含與 Home API 互動的服務。

設定 Home SDK

請按照「設定 SDK」一節中的步驟取得最新的 SDK。

取得範例應用程式

您可以在 GitHub 上找到應用程式範例的原始碼。本程式碼研究室會使用範例應用程式的 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

應用程式在手機上成功執行後,您會看到「Sample App」主頁面。不過,您必須先設定 OAuth 驗證,並使用 Permission API 實作缺少的部分,才能登入。

3. 設定驗證方法

Home API 會使用 OAuth 2.0 授予結構體中裝置的存取權。OAuth 可讓使用者授予應用程式或服務權限,而無須揭露登入憑證。

請按照「設定 OAuth 同意聲明」中的指示設定同意畫面。請務必建立至少一個測試帳戶。

然後按照「設定 OAuth 憑證」中的指示,為應用程式建立憑證。

4. 初始化和處理權限

在本節中,您將瞭解如何使用 Permissions API 完成缺少的部分,藉此初始化 SDK 並處理使用者權限。

定義支援的類型和特徵

開發應用程式時,您必須明確指出應用程式支援哪些裝置類型和特徵。在範例應用程式中,我們會在 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 上的特徵索引」。

取消註解 HomeApp.kt 來源檔案中的步驟 4.1.1 和 4.1.2,啟用要求權限的原始碼。

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

使用 Permissions API 驗證 Home 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
        } )

取消註解 PermissionsManager.kt 中的步驟 4.3.1,啟用要求權限的程式碼:

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

「Loading」訊息會一直顯示,這是因為我們尚未實作可讀取結構和裝置的程式碼。我們會在下一節中進行這項操作。

5. 瞭解資料模型

在 Google Home API 中,資料模型由以下項目組成:

  • Structure 代表住家,其中包含房間和裝置。
  • Room 是結構的一部分,且包含裝置。
  • 裝置 (定義為 HomeDevice) 可指派至建築物 (或住家) 或建築物中的房間。
  • 裝置由一或多個 DeviceType 例項組成。
  • DeviceTypeTrait 執行個體組成。
  • TraitAttribute 例項 (用於讀取/寫入)、Command 例項 (用於控制屬性) 和 Event 例項 (用於讀取或訂閱過去變更記錄) 組成。
  • Automation 例項是結構體的一部分,可使用住家中繼資料和裝置,自動執行住家中的任務。

76d35b44d5a8035e.png

在本節中,您將瞭解如何開發原始碼,以便說明如何使用結構體 API 剖析及算繪住家結構體、房間和裝置等。

讀取結構體

Home API 設計是以 Kotlin 資料流為基礎,可將資料模型物件 (例如 StructureHomeDevice 等) 串流傳送。開發人員訂閱 Flow,即可取得物件中包含的所有物件 (例如 StructureRoom 等)。

如要擷取所有結構體,請呼叫 structures() 函式,該函式會傳回結構體的資料流。接著,請在流程中呼叫清單函式,取得使用者擁有的所有結構體。

// Get the a snapshot of all structures from the current homeClient
val allStructures : Set<Structure> =
    homeClient.structures()   // HomeObjectsFlow<Structure>
    .list()                   // Set<Structure>

應用程式架構指南」強烈建議採用新型的 Reactive 程式設計方法,改善應用程式資料流程和狀態管理。

以下是範例應用程式如何遵循 Reactive 程式碼樣式:

  • 檢視模型 (例如 StructureViewModelDeviceViewModel,做為狀態容器) 會訂閱 Home APIs SDK 的資料流,以便接收值變更,並維持最新狀態。
  • 檢視區塊 (例如 StructureViewDeviceView) 會訂閱檢視區塊模型,以便接收狀態,並算繪 UI 以反映這些變更。
  • 當使用者點選檢視畫面上的按鈕 (例如燈具裝置的「開啟」按鈕) 時,事件會觸發檢視模型的函式,而這些函式會呼叫相應的 Home API 函式 (例如 OnOff 特徵的 On 指令)。

HomeAppViewModel.kt 的步驟 5.1.1 中,我們會呼叫 collect() 函式,訂閱結構變更事件。取消註解該部分,該部分會遍歷結構體 API 回應傳回的 structureSet,並在 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 重組。取消註解步驟 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 API 資料模型中,結構體可以包含未指派給房間的裝置,因此請務必在應用程式中擷取沒有房間的裝置:

val devicesWithoutRooms: MutableSet<HomeDevice> = mutableSetOf()

for (device in structure.devices().list())
if (!device.isInRoom)
  devicesWithoutRooms.add(device)

同樣地,在現有的範例程式碼中,我們會訂閱流程,以便取得最新的房間和裝置清單。請檢查 StructureViewModel.kt 來源檔案中的步驟 5.2.1 和 5.2.2 中的程式碼,並取消註解,啟用 Room 資料訂閱:

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

取消註解 DevicesView.kt 來源檔案中的步驟 5.2.3 和 5.2.4,將房間清單顯示為選單:

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. 使用裝置

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),因此您必須瞭解每種裝置類型的內容,才能瞭解各自的特徵代表的內容。

取消註解 DeviceViewModel.kt 來源檔案中的步驟 6.1.1 和 6.1.2,以便擷取狀態:

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
}

取消註解 DeviceView.kt 中的步驟 6.1.3,將 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

發送裝置指令

如要向裝置發出指令,Home API 會在 Trait 物件 (例如 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 的自動完成功能,查看可用於與屬性互動的動作類型。

取消註解 DeviceView.kt 中的步驟 6.2.1,即可在應用程式中新增功能控制項:

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. 佣金裝置

開發人員可以使用啟用 API,將裝置新增至 Google Home 生態系統,並透過 Google Home API 控制裝置。僅支援 Matter 裝置。在本節中,我們將探討如何在應用程式中啟用裝置委派作業。

開始本節之前,請確認您已符合下列必要條件:

如果您有實體 Matter 裝置,且裝置上有用於啟用的 QR code,請直接跳到「啟用啟用 API」一節。否則請繼續閱讀下一節,瞭解如何使用 Matter Virtual Device 應用程式 (MVD) 建立可收取佣金的虛擬裝置。

選用:準備可調試的 Matter 裝置

準備可授權的 Matter 裝置最簡單的方法,就是使用 Matter 虛擬裝置應用程式 (MVD) 提供的模擬裝置。

安裝 MVD 並設定防火牆後,請執行 MVD:

b20283893073ac1b.png

建立 OnOff 裝置。請注意,它尚未啟用,您稍後會在本程式碼研究室中啟用它。

5f4855b808312898.png

啟用調試 API

啟用 API 會在應用程式活動之外運作,因此啟用作業的處理方式與其他 Home API 不同。如要讓應用程式準備好進行委派,您需要兩個變數。

其中一個變數是 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
 }
}

設定好啟用流程後,您將建立啟用意圖,並使用先前範例中建立的啟動器啟動該意圖。建議您將意圖和啟動器放在下列專用函式中。專屬函式可與 UI 元素 (例如「+Add Device」按鈕) 綁定,並根據使用者要求叫用:

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

取消註解 CommissioningManager.kt 中的步驟 7.1.1,啟用調試功能,讓「+Add Device」按鈕在範例應用程式中運作。

// 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 code 掃描器,可用於掃描 Matter 裝置的 QR code。接著,流程會顯示使用者授權協議、裝置探索和調試,並為裝置命名。流程完成後,流程會將焦點改回應用程式,並在我們在上一節中草擬的回呼函式中傳遞委派結果。

啟用 API 的好處之一,就是 SDK 會處理使用者體驗流程,讓開發人員可以快速啟用 API。這樣一來,使用者在不同應用程式中新增裝置時,也能享有一致的體驗。

如要進一步瞭解委派 API,請參閱「Android 裝置的委派 API」。

8. 恭喜!

恭喜!您已成功使用 Google Home API 建立 Android 應用程式。在本程式碼研究室中,您瞭解了權限、裝置、結構體和啟用 API。在下一門程式碼研究室「使用 Android 程式碼研究室中的 Home API 建立進階自動化動作」中,我們將探討 Automation 和 Discovery API,並完成應用程式。

希望您能盡情發揮創意,打造可在 Google Home 生態系統中控制裝置的應用程式。

後續步驟