1. 歡迎
Matter 的目標是整合 IoT 標準,並連結 Google Home、Zigbee、藍牙網狀網路、Z-Wave 等各種生態系統的智慧住宅裝置。
行動裝置是智慧住宅裝置的重要互動點。如果您想自行建構支援 Matter 裝置的 Android 應用程式,我們可以協助您快速上手。
Google Home 範例應用程式 (適用於 Matter 的 GHSA) 展示了 Home Mobile SDK API,讓使用者註冊及共用裝置。你也可以使用範例應用程式做為學習工具,進一步瞭解 Matter 的重要概念,以及用於偵錯和排解 Matter 裝置互動的問題。
學習內容
在本程式碼研究室中,您將下載範例應用程式的原始碼,並瞭解如何使用 Home Mobile SDK 調用及共用裝置。您也會學到如何使用 Matter 存放區 (connectedhomeip
) 中的調用作業和叢集程式庫。
您下載範例應用程式後,我們會在 Android Studio 中審查原始碼,並導入下列 Home Mobile SDK API:
你也會學到如何調校相關概念、Matter 織品和控制 Matter 裝置的方式。
軟硬體需求
在開始之前,請務必完成下列步驟:
- 請參閱 Matter 適用的 Google Home 範例應用程式。
- 下載 Android Studio。
- 擁有 Android O (8.1,API 級別 27) 以上版本的裝置來進行測試。為確保裝置是否支援最新的 Matter 功能,請參閱驗證 Matter 模組與服務指南。
- 請使用已開啟/關閉功能的 Matter 裝置。為盡可能減少環境問題,我們強烈建議你一開始先使用 Matter Virtual Device (MVD)。如果您遇到問題,那麼若將範例應用程式用於 MVD,就可以快速展開調查。以下是幾個其他選項:
- 使用
rootnode_dimmablelight_bCwGYSDpoe
應用程式建構 Matter 虛擬裝置。在 Home Developer Console 建立 Matter 整合項目時,請使用0xFFF1
做為供應商 ID,並將0x8000
做為產品 ID。 - 使用
all-clusters-app
建構 Espressif 裝置。在 Home Developer Console 建立 Matter 整合時,請使用0xFFF1
做為供應商 ID,並使用0x8001
做為產品 ID。
- 使用
- 參閱這篇文章,瞭解如何設定 Google Play 服務。
你不需要中樞,例如 Google Nest Hub (第 2 代),即可透過範例應用程式操控及控制裝置。
2. 做好準備
程式碼研究室的範例應用程式位於 codelab
分支版本中。如要開始使用程式碼研究室的原始碼,您可以下載 ZIP 檔案。
您將使用這個 codelab
ZIP 檔案建立有效的範例。
程式碼研究室版本
codelab
分支版本會標記範例應用程式的 2.0.0 版標記。如要在執行每個步驟時比較更新內容,您可以下載這個版本的完整原始碼。
如要複製 GitHub 存放區,請按照範例應用程式 README 中的指示操作。
依附元件
我們會引導您瞭解共用裝置及佣金裝置所需的原始碼,但在開始之前,建議您先瞭解下列依附元件。請注意,這些依附元件是在 libs.versions.toml 檔案中宣告,其用途是在 build.gradle.kts 檔案中指定的用途
- Home Mobile SDK
- Matter SDK 程式庫。
- Jetpack Compose。UI 完全使用 Compose 實作。
- Material Design:詳情請參閱「MDC-103 Android:含有顏色、高度和類型的 Material Design 主題設定 (Kotlin)」和「Material Design 主題設定建構工具」。
- Proto DataStore,用於保存應用程式資料。Datastore 存放區和序列化程式會儲存在
java/data
中,包括裝置和使用者偏好設定的結構定義。如要進一步瞭解 DataStore,請參閱「使用 Proto DataStore」。 - Hilt 用於保存資料並支援插入依附元件。
原始碼
我們已為您建立使用者介面和大部分的功能。
在本程式碼研究室中,我們會為下列檔案新增 Matter 功能:
java/com/google/homesampleapp/commissioning/AppCommissioningService
:方便您將裝置委託開發架構java/com/google/homesampleapp/screens/home/HomeScreen
和java/com/google/homesampleapp/screens/home/HomeViewModel.kt
:包含 Home Mobile SDK 調用功能java/com/google/homesampleapp/screens/device/DeviceScreen
和java/com/google/homesampleapp/screens/device/DeviceViewModel
:包含 Share Device API 呼叫
每個檔案都會加上您將修改的程式碼區塊加上註解,例如:
// CODELAB: add commissioningFunction()
這可讓您快速找到程式碼研究室中的對應部分。
3. 支付給 Google 的佣金
如要控制裝置並允許裝置使用相同的織布互相通訊,必須先由 Commissioner 委託,在本範例中為 Google Home 範例應用程式 (本例中為 Matter 應用程式範例)。
請務必瞭解下列關於 Matter 調用的概念:
- Fabrics 可讓裝置相互通訊,
- Fabrics 可維護一組不重複的憑證。
- 生態系統負責核發信任的根憑證、指派架構 ID,以及指派不重複的節點 ID。生態系統是佣金人員的後端服務,例如 Google Home 生態系統的 Home Graph。
- 裝置可搭載多種織布規格 (多重管理員功能)。
如要調控裝置,您必須使用 CommissioningClient API。對 .commissionDevice()
的呼叫會傳回 IntentSender,該動作會在 Google Play 服務中啟動適當的活動:
interface CommissioningClient { Task<IntentSender> commissionDevice(CommissioningRequest request); }
在後續章節中,我們會介紹讓裝置使用 Google 架構所需的最少程式碼。
步驟 1:活動啟動器
如要處理 CommissioningClient
中的 IntentSender
,可以使用 ActivityResultLauncher:
val commissioningLauncher = registerForActivityResult( StartIntentSenderForResult() ) { result: ActivityResult -> if (result.resultCode == RESULT_OK) { Timber.d(TAG, "Commissioning succeeded.") } else { Timber.d(TAG, "Commissioning failed. " + result.resultCode) } }
步驟 2:調試函式
以下是使用 CommissioningClient API 將裝置調給 Google 架構的基本範例。
- 啟動程序會從
commissionDevice()
函式開始。首先,定義CommissioningRequest
。採用這項預設設定時,系統只會依據本機 Android 架構將裝置委託採用。 Matter
是 Home Mobile SDK 的進入點。在下一個呼叫中,.getCommissioningClient
會透過this
(Activity) 取得 CommissioningClient。.commissionDevice()
接受CommissioningRequest
。- 最後,系統會呼叫
.addOnSuccessListener
來處理CommissioningResult
,並啟動 Google Play 服務 (GPS) 許可裝置活動。
private fun commissionDevice() { val request: CommissioningRequest = CommissioningRequest.builder().build() Matter.getCommissioningClient(this) .commissionDevice(request) .addOnSuccessListener { result -> commissioningLauncher.launch(IntentSenderRequest.Builder(result).build()) } }
你可以透過 Android 設定使用 Local Android Fabric,簡化將裝置交給其他織布的流程。
接下來,我們將說明如何將裝置委託開發架構。
如需在調試過程中的使用者介面總覽,請參閱 Google Home 支援 Matter 的範例應用程式。
4. 調試開發架構
你可以為裝置選購多種布料。如要管理信任的配對,裝置會儲存含有多位 FabricInfo
成員的 FabricTable
,例如:
- 織品識別
- 織布指派給裝置的節點 ID
- 供應商 ID
- 織布 ID
- 裝置作業憑證
管理網域管理員 (ADM) 會定義織布憑證。在先前的情境中,Google Play 服務是一個擔任信任根憑證授權單位 (CA) 的生態系統。將裝置委託給本地 Android 架構時,每部裝置都會含有同一組布料憑證和同一組 CA。
客製化調試服務
為了針對「本機 Android 架構」執行佣金,我們使用預設參數在 CommissioningClient API 中建構 CommissioningRequest
:
val request: CommissioningRequest = CommissioningRequest.builder().build()
如要透過應用程式控制及管理新裝置,您必須建立本機開發架構,並取得用於佣金裝置的作業憑證。在這種情況下,您的應用程式就會成為獨特的獨立生態系統,為裝置指派適當的節點憑證。
您可以讓 Home Mobile SDK 傳送自訂服務給 CommissioningRequest,讓 Home Mobile SDK 您指定將裝置佣金給自家架構:
class CommissioningRequest { static CommissioningRequest.Builder builder(); class Builder { Builder setCommissioningService(@Nullable ComponentName commissioningService); CommissioningRequest build(); } }
在後續步驟中,我們會修改 commissionDevice()
函式來使用自訂服務。我們也會在主畫面片段中新增活動啟動器,並使用 LiveData 物件管理 API 流程。
步驟 1:建立 GPS 活動啟動器
首先,我們要建立活動啟動器,以便透過 CommissioningClient API 處理 IntentSender
。
- 在「
java/com/google/homesampleapp/screens/home/
」資料夾中開啟「HomeScreen
」。 - 將
// CODELAB: commissionDeviceLauncher definition
註解替換為下列程式碼,即可註冊及處理調控活動的結果:val commissionDeviceLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.StartIntentSenderForResult() ) { result -> // Commission Device Step 5. // The Commission Device activity in GPS (step 4) has completed. val resultCode = result.resultCode if (resultCode == Activity.RESULT_OK) { Timber.d("CommissionDevice: Success") // We let the ViewModel know that GPS commissioning has completed successfully. // The ViewModel knows that we still need to capture the device name and will\ // update UI state to trigger the NewDeviceAlertDialog. homeViewModel.gpsCommissioningDeviceSucceeded(result) } else { homeViewModel.commissionDeviceFailed(resultCode) } }
步驟 2:觸發佣金裝置動作
在這個步驟中,使用者會觸發「佣金裝置」按一下「+」按鈕。便會撥打 commissionDevice()
。
val onCommissionDevice = { ... commissionDevice(activity!!.applicationContext, commissionDeviceLauncher) }
步驟 3:呼叫 API
- 仍在「
java/com/google/homesampleapp/screens/home
」資料夾的「HomeScreen.kt
」中。 - 將
// CODELAB: commissionDevice
註解替換為下列commissionDeviceRequest
。setCommissioningService
會將AppCommissioningService
繫結至CommissioningService
例項,並透過回呼函式傳回。傳遞自訂服務後,Home Mobile SDK 會先將裝置分配給 Android 本機架構,然後將新手上路酬載傳回AppCommissioningService
。val commissionDeviceRequest = CommissioningRequest.builder() .setCommissioningService(ComponentName( context, AppCommissioningService::class.java)) .build()
- 呼叫
.getCommissioningClient()
,然後呼叫.commissionDevice()
。
Matter.getCommissioningClient(context) .commissionDevice(commissionDeviceRequest)
如要完成 commissionDevice
函式,請新增 addOnSuccessListener
和 addOnFailureListener
:
.addOnSuccessListener { result -> commissionDeviceLauncher.launch(IntentSenderRequest.Builder(result).build()) } .addOnFailureListener { error -> Timber.e(error) }
5. 建立調試服務
在 commissionDevice()
函式中,我們要求從 CommissioningClient API 取得 CommissioningService。在這個流程中,CommissioningClient API 會先將裝置套用至本機 Android 架構,然後傳回包含 CommissioningRequestMetadata 物件的回呼:
public interface CommissioningService { interface Callback { void onCommissioningRequested(CommissioningRequestMetadata metadata); } }
現在,我們必須沿用 CommissioningService.Callback,為範例應用程式提供佣金裝置所需的功能。以下是基本 CommissioningService 實作範例:
class MatterCommissioningService : Service(), CommissioningService.Callback { private val commissioningServiceDelegate = CommissioningService.Builder(this) .setCallback(this) .build() override fun onBind(intent: Intent) = commissioningServiceDelegate.asBinder() override fun onCommissioningRequested(metadata: CommissioningRequestMetadata) { // perform commissioning commissioningServiceDelegate .sendCommissioningComplete(CommissioningCompleteMetadata.builder().build()) } }
步驟 1:探索自訂 AppCommissioningService
為協助您開始使用,我們已為自訂 CommissioningService 定義基本類別結構。以下是服務功能的簡介。如要參與課程,請在「java/commissioning
」中開啟「AppCommissioningService
」。
我們為 Home Mobile SDK API 新增了下列匯入項目:
import com.google.android.gms.home.matter.commissioning.CommissioningCompleteMetadata import com.google.android.gms.home.matter.commissioning.CommissioningRequestMetadata import com.google.android.gms.home.matter.commissioning.CommissioningService
AppCommissioningService
也包含 Matter 存放區 (connectedhomeip
) 中的程式庫:
import com.google.homesampleapp.chip.ChipClient
最後,服務包含匯入功能,以支援 Hilt 和 Kotlin 協同程式。
接下來,我們會建立建構函式並設定一些內容,包括 commissioningServiceDelegate
,並在系統執行佣金時用來通知 Google Play 服務。
private lateinit var commissioningServiceDelegate: CommissioningService ... commissioningServiceDelegate = CommissioningService.Builder(this).setCallback(this).build()
接下來要新增調用函式。
步驟 2:覆寫 onCommissioningRequested
如要將裝置套用至應用程式的開發架構,請完成下列步驟:
- 在「
java/commissioning
」中開啟AppCommissioningService
。 - 找出
onCommissioningRequested()
函式。我們提供可輸出CommissioningRequestMetadata
的記錄訊息。替換// CODELAB: onCommissioningRequested()
註解,啟動serviceScope
協同程式並取得deviceId
。// Perform commissioning on custom fabric for the sample app. serviceScope.launch { val deviceId = devicesRepository.incrementAndReturnLastDeviceId()
- 執行調校作業。在這個步驟中,我們可以傳送 CommissioningRequestMetadata 物件中傳回的裝置資訊。
ChipClient
會使用這項中繼資料資訊,在 Matter 應用程式與裝置之間的 GHSA 之間建立安全通道。try { Timber.d( "Commissioning: App fabric -> ChipClient.establishPaseConnection(): deviceId [${deviceId}]") chipClient.awaitEstablishPaseConnection( deviceId, metadata.networkLocation.ipAddress.hostAddress!!, metadata.networkLocation.port, metadata.passcode) Timber.d( "Commissioning: App fabric -> ChipClient.commissionDevice(): deviceId [${deviceId}]") chipClient.awaitCommissionDevice(deviceId, null) } catch (e: Exception) { Timber.e(e, "onCommissioningRequested() failed") // No way to determine whether this was ATTESTATION_FAILED or DEVICE_UNREACHABLE. commissioningServiceDelegate .sendCommissioningError(CommissioningError.OTHER) .addOnSuccessListener { Timber.d( "Commissioning: commissioningServiceDelegate.sendCommissioningError() succeeded") } .addOnFailureListener { e2 -> Timber.e(e2, "Commissioning: commissioningServiceDelegate.sendCommissioningError() failed") } return@launch }
- 請使用
commissioningServiceDelegate
,讓 Google Play 服務知道調試作業已完成。在.sendCommissioningComplete()
中,傳遞 CommissioningCompleteMetadata。commissioningServiceDelegate .sendCommissioningComplete( CommissioningCompleteMetadata.builder().setToken(deviceId.toString()).build()) .addOnSuccessListener { Timber.d("Commissioning: commissioningServiceDelegate.sendCommissioningComplete() succeeded") } .addOnFailureListener { e -> Timber.e(e, "Commissioning: commissioningServiceDelegate.sendCommissioningComplete() failed") } }
執行應用程式
現在我們已備妥所有必要程式碼,可以對當地佈料進行佣金,讓我們開始測試看看。選擇 Android 裝置並執行應用程式。在主畫面上輕觸「新增裝置」,然後完成裝置佣金的步驟。
調試完成後,裝置就會採用兩種織布設計:本機 Android 織布和本機開發架構。每種布料都有自己的一組憑證和專屬的 64 位元架構 ID。
6. 控制裝置
調試開發架構後,你就可以使用 Matter 存放區 (connectedhomeip
) 中的程式庫,透過範例應用程式控制裝置。
我們建立了一些輔助類別,方便您輕鬆存取裝置叢集及傳送指令。如要瞭解詳情,請在「java/clusters
」中開啟「ClustersHelper
」。此單例輔助程式會匯入下列程式庫,存取裝置資訊:
import chip.devicecontroller.ChipClusters import chip.devicecontroller.ChipStructs
我們可以使用這個類別取得裝置的開啟/關閉叢集,然後呼叫 .toggle
:
suspend fun toggleDeviceStateOnOffCluster(deviceId: Long, endpoint: Int) { Timber.d("toggleDeviceStateOnOffCluster())") val connectedDevicePtr = try { chipClient.getConnectedDevicePointer(deviceId) } catch (e: IllegalStateException) { Timber.e("Can't get connectedDevicePointer.") return } return suspendCoroutine { continuation -> getOnOffClusterForDevice(connectedDevicePtr, endpoint) .toggle( object : ChipClusters.DefaultClusterCallback { override fun onSuccess() { continuation.resume(Unit) } override fun onError(ex: Exception) { Timber.e("readOnOffAttribute command failure: $ex") continuation.resumeWithException(ex) } }) } }
切換裝置
在您委託裝置後,CommissioningResult 傳回的酬載會新增至 DataStore。這麼做可讓應用程式存取裝置資訊,以便我們傳送指令。
Matter 應用程式是由事件驅動。初始化 Matter 堆疊後,叢集服務會監聽收到的訊息。一旦裝置被調用,Matter 用戶端會透過裝置調試期間建立的安全營運通道傳送指令。
在裝置上,封包會經過驗證與解密,再透過回呼分派。回呼函式包括可從 attributePath
存取的 EndpointId、ClusterId 和 AttributeId。舉例來說,以下程式碼可在 Matter 裝置上實作:
void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath, uint8_t mask, uint8_t type, uint16_t size, uint8_t * value) { // handle callback ClusterId clusterId = attributePath.mClusterId; AttributeId attributeId = attributePath.mAttributeId; }
在後續步驟中,您將使用 Matter SDK 和 ClustersHelper
切換裝置。
- 前往
java/screens/device
的「DeviceViewModel
」。 - 找出
updateDeviceStateOn
函式。 - 將
// CODELAB: toggle
註解替換為程式碼,呼叫clustersHelper
,然後更新裝置存放區:Timber.d("Handling real device") try { clustersHelper.setOnOffDeviceStateOnOffCluster(deviceUiModel.device.deviceId, isOn, 1) devicesStateRepository.updateDeviceState(deviceUiModel.device.deviceId, true, isOn) } catch (e: Throwable) { Timber.e("Failed setting on/off state") }
系統會從 DeviceScreen
呼叫這個函式:
// On/Off Switch click. val onOnOffClick: (value: Boolean) -> Unit = { value -> deviceViewModel.updateDeviceStateOn(deviceUiModel!!, value) }
執行應用程式
執行應用程式,重新載入更新內容。在主畫面上,切換裝置的開關狀態。
7. 與其他生態系統共用裝置
在 Matter 的規格中,共用裝置稱為「多管理員流程」。
在先前的步驟中,我們發現 Home Mobile SDK 能夠將裝置委託給 Local Android 架構,以及範例應用程式的開發架構。這是多重管理員流程的例子,也就是裝置可透過多種布料執行的裝置。
現在,您可能還會想與更多布料共用裝置,尤其是在日常生活中,人們對應用程式和平台有自主偏好的設定。
Home Mobile SDK 的 ShareDeviceRequest API 提供此功能,有助您:
- 為裝置開啟臨時佣金期。
- 變更裝置狀態,以便裝置使用其他織布。
- 透過其他應用程式和生態系統控管裝置。
在後續步驟中,您將使用 Home Mobile SDK 共用裝置。
步驟 1:建立 GPS 活動啟動器
如同我們委託開發架構時建立的調用活動啟動器,我們也建立了共用裝置活動啟動器,以便透過 CommissioningClient API 處理 IntentSender
。
- 在「
java/com/google/homesampleapp/screens/device/
」資料夾中開啟「DeviceScreen
」。 - 將
// CODELAB: shareDeviceLauncher definition
註解替換為下列程式碼,即可註冊及處理.shareDevice()
Activity 結果:val shareDeviceLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.StartIntentSenderForResult() ) { result -> // Commission Device Step 5. // The Share Device activity in GPS (step 4) has completed. val resultCode = result.resultCode if (resultCode == Activity.RESULT_OK) { deviceViewModel.shareDeviceSucceeded() } else { deviceViewModel.shareDeviceFailed(resultCode) } }
步驟 2:觸發共用裝置動作
在這個步驟中,使用者觸發「共用裝置」請點選「分享」圖示按鈕。系統會呼叫 deviceViewModel
以開啟配對視窗,用於分享裝置。
// Share Device button click. val onShareDevice: () -> Unit = remember { { deviceViewModel.openPairingWindow(deviceUiModel!!.device.deviceId) } }
成功開啟處理視窗後,deviceViewModel
會將這一點向 UI 傳遞。ViewModel
與 UI 之間的通訊是透過 StateFlow
物件完成。
// Communicate to the UI that the pairing window is open. // UI can then launch the GPS activity for device sharing. _pairingWindowOpenForDeviceSharing.value = true
看到 StateFlow 物件變更後,DeviceScreen 會進行以下呼叫:
shareDevice(activity!!.applicationContext, shareDeviceLauncher, deviceViewModel)
步驟 3:呼叫 API
現在可以開始共用裝置工作了。
- 在「
java/com/google/homesampleapp/screens/device/
」資料夾中開啟「DeviceScreen.kt
」。 - 找出
shareDevice()
函式。將// CODELAB: shareDevice
註解替換為ShareDeviceRequest
。DeviceDescriptor
會提供裝置特定資訊,例如供應商 ID、產品 ID 和 deviceType。在本例中,我們將值進行硬式編碼。val shareDeviceRequest = ShareDeviceRequest.builder() .setDeviceDescriptor(DeviceDescriptor.builder().build()) .setDeviceName("GHSAFM temp device name")
- 設定 CommissioningWindow 和參數。
.setCommissioningWindow( CommissioningWindow.builder() .setDiscriminator(Discriminator.forLongValue(DISCRIMINATOR)) .setPasscode(SETUP_PIN_CODE) .setWindowOpenMillis(SystemClock.elapsedRealtime()) .setDurationSeconds(OPEN_COMMISSIONING_WINDOW_DURATION_SECONDS.toLong()) .build()) .build()
- 呼叫
.getCommissioningClient()
,但這次只使用.shareDevice()
API。Matter.getCommissioningClient(context) .shareDevice(shareDeviceRequest)
commissioningClient.shareDevice()
API 成功回呼會提供 IntentSender,以便在 Google Play 服務中啟動共用裝置活動。
- 如要完成
shareDevice
函式,請新增addOnSuccessListener
和addOnFailureListener
。成功時,系統會在shareDeviceLauncher
上呼叫launch
,以啟動 GPS 裝置共享的 GPS 活動。.addOnSuccessListener { result -> Timber.d("ShareDevice: Success getting the IntentSender: result [${result}]") shareDeviceLauncher.launch(IntentSenderRequest.Builder(result).build()) } .addOnFailureListener { error -> Timber.e(error) deviceViewModel.showMsgDialog("Share device failed", error.toString()) }
執行應用程式
如要與其他生態系統共用 Matter 裝置,則在 Android 裝置上安裝其他平台。我們已建立範例應用程式的另一個執行個體,可做為目標委託者使用。
在 Android 裝置中安裝目標佣金後,請確認您能否共用 Matter 裝置。目標佣金應用程式已加上「GHSAFM-TC」GHSAFM-TC標籤。
裝置現在支援三種布料:
- 本地 Android 布料。
- 您的開發架構 (這個應用程式)。
- 這是你剛與裝置共用的第三層布料。
8. 後續步驟
恭喜
恭喜!您已成功完成本程式碼研究室,並學會如何使用 Home Mobile SDK 佣金和共用裝置。
如果您在使用範例應用程式時遇到問題,請嘗試完成環境驗證步驟:
如果對於使用範例應用程式有疑問或發現程式碼錯誤,可以將問題提交至 GitHub 存放區的 Issue Tracker:
如需 Google 針對技術問題的官方指引,請使用智慧型住宅開發人員論壇:
如要向社群取得技術支援,請在 Stack Overflow 上使用 google-smart-home
標記: