1. Welcome
Built with the goal of unifying IoT standards, Matter connects smart home devices across various ecosystems like Google Home, Zigbee, Bluetooth Mesh, Z-Wave, and more.
Mobile devices are a central interaction point with smart home devices. If you'd like to build your own Android apps to support Matter devices, we can help you get started fast.
The Google Home Sample App for Matter (GHSA for Matter) showcases the Home Mobile SDK APIs, allowing users to commission and share devices. You can also use the sample app as a learning tool to better understand key Matter concepts, as well as a tool to debug and troubleshoot interactions with Matter devices.
What you'll do
In this Codelab, you'll download the source code for the sample app and learn how to use the Home Mobile SDK to commission and share devices. You'll also learn how to use commissioning and Cluster libraries from the Matter repo (connectedhomeip
).
After you download the sample app, we'll review the source code in Android Studio and implement the following Home Mobile SDK APIs:
You'll also learn more about commissioning concepts, Matter fabrics, and how to control Matter devices.
What you'll need
Before you begin, make sure to complete the following steps:
- Review the Google Home Sample App for Matter Guide.
- Download Android Studio.
- Have an Android O (8.1, API level 27) or newer device available for testing. To make sure that your device has the latest Matter support, review the Verify Matter Modules & Services guide.
- Use a Matter device with On/Off capabilities. To minimize environment issues, we strongly suggest that you use the Matter Virtual Device (MVD) to start with. If you ever run into issues, it will be much easier to investigate if the sample app is used against MVD. Here are a few other options:
- Build a Matter Virtual Device with the
rootnode_dimmablelight_bCwGYSDpoe
app. When you Create a Matter integration in the Home Developer Console, use0xFFF1
as your Vendor ID and0x8000
as your Product ID. - Build an Espressif Device with the
all-clusters-app
. When you Create a Matter integration in the Home Developer Console, use0xFFF1
as your Vendor ID and0x8001
as your Product ID.
- Build a Matter Virtual Device with the
- Review how to Set up Google Play services.
You don't need a hub, for example a Google Nest Hub (2nd Generation), to commission and control devices with the sample app.
2. Get set up
The codelab starter app is located in the codelab
branch. To start working with the codelab source code, you can download the ZIP file.
You'll use this codelab
ZIP file to build a working sample.
Codelab versions
The codelab
branch is tagged with the 2.0.0 release of the sample app. To compare your updates as you work through each step, you can download the completed source code for this release.
If you'd like to clone the GitHub repository, follow the instructions on the Sample app README.
Dependencies
We'll guide you through the source code required to share and commission devices, but it might help to be aware of the following dependencies before you get started. Note that these dependencies are declared in file libs.versions.toml and their usage specified in file build.gradle.kts
- Home Mobile SDK
- Matter SDK libraries.
- Jetpack Compose. The UI is fully implemented using Compose.
- Material Design. To learn more, refer to MDC-103 Android: Material Theming with Color, Elevation and Type (Kotlin) and Material Theme Builder.
- Proto DataStore, used to persist app data. Datastore Repositories and Serializers are stored in
java/data
, including schemas for devices and user preferences. To learn more about DataStore, refer to Working with Proto DataStore. - Hilt to persist data and support dependency injection.
Source code
The user interface and most of the functionality has already been created for you.
For this codelab, we'll be adding Matter functionality to the following files:
java/com/google/homesampleapp/commissioning/AppCommissioningService
: allows you to commission devices to the development fabricjava/com/google/homesampleapp/screens/home/HomeScreen
andjava/com/google/homesampleapp/screens/home/HomeViewModel.kt
: includes the Home Mobile SDK commissioning functionalityjava/com/google/homesampleapp/screens/device/DeviceScreen
andjava/com/google/homesampleapp/screens/device/DeviceViewModel
: includes the Share Device API calls
Each file is commented with the code-block that you'll be modifying, for example:
// CODELAB: add commissioningFunction()
This allows you to quickly locate the corresponding section in the codelab.
3. Commission to Google
Before you can control devices and allow them to communicate with each other within the same fabric, they need to be commissioned by a Commissioner, which in this case is this sample application, the Google Home Sample App for Matter.
It's important to understand the following concepts about Matter commissioning:
- Fabrics allow devices to communicate with each other.
- Fabrics maintain a shared set of unique credentials.
- Ecosystems are responsible for issuing trusted root certificates, assigning fabric IDs, and assigning unique node IDs. An ecosystem is the back-end service of a commissioner, for example the Home Graph for the Google Home ecosystem.
- Devices can be commissioned to more than one fabric (multi-admin feature).
To commission a device, you'll need to use the CommissioningClient API. A call to .commissionDevice()
returns an IntentSender, which launches the proper activity in Google Play Services:
interface CommissioningClient { Task<IntentSender> commissionDevice(CommissioningRequest request); }
In the next sections, we'll go over the minimal code required to commission devices to the Google fabric.
Step 1: Activity Launcher
To handle the IntentSender
from the CommissioningClient
, you can use an 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) } }
Step 2: Commissioning function
Here's a basic example that uses the CommissioningClient API to commission a device to the Google fabric.
- The commissioning process starts with the
commissionDevice()
function. First, aCommissioningRequest
is defined. With this default configuration, devices are commissioned only to the Local Android fabric. Matter
is the entry point for the Home Mobile SDK. In the next call,.getCommissioningClient
gets a CommissioningClient bythis
(Activity)..commissionDevice()
accepts theCommissioningRequest
.- And finally,
.addOnSuccessListener
is called to process theCommissioningResult
and launch the Google Play Services (GPS) Commission Device Activity.
private fun commissionDevice() { val request: CommissioningRequest = CommissioningRequest.builder().build() Matter.getCommissioningClient(this) .commissionDevice(request) .addOnSuccessListener { result -> commissioningLauncher.launch(IntentSenderRequest.Builder(result).build()) } }
The Local Android Fabric can be leveraged via Android settings to simplify the process of commissioning its devices to other fabrics.
Next, you'll learn how to commission a device to a development fabric.
For an overview of the user interface during the commissioning process, refer to the Google Home Sample App for Matter Guide.
4. Commission to a development fabric
Devices can be commissioned to more than one fabric. To manage trusted pairings, devices store a FabricTable
containing various FabricInfo
members, for example:
- Fabric identification
- Node Id assigned by the fabric to the device
- Vendor Id
- Fabric Id
- Device operational credentials
The administrative domain manager (ADM) defines fabric credentials. In the previous scenario, Google Play Services is the ecosystem that acts as a trusted root certificate authority (CA). When you commission devices to the Local Android fabric, every device includes the same set of fabric credentials, and the same set of CAs.
Custom Commissioning Services
To commission to the Local Android fabric, we used the default parameters to build the CommissioningRequest
in the CommissioningClient API:
val request: CommissioningRequest = CommissioningRequest.builder().build()
If you'd like to control and manage new devices from your app, you need to create a local development fabric and obtain the operational credentials to commission devices. In this scenario, your app becomes a unique, independent ecosystem that assigns devices the appropriate node credentials.
You can inform Home Mobile SDK that you'd like to commission devices to your own fabric by passing a custom service to the CommissioningRequest:
class CommissioningRequest { static CommissioningRequest.Builder builder(); class Builder { Builder setCommissioningService(@Nullable ComponentName commissioningService); CommissioningRequest build(); } }
In the next steps, we'll modify the commissionDevice()
function to use a custom service. We'll also add an Activity Launcher to the Home fragment and use LiveData objects to manage the API flow.
Step 1: Create a GPS Activity Launcher
First, let's create an Activity Launcher to handle the IntentSender
from the CommissioningClient API.
- Open
HomeScreen
in thejava/com/google/homesampleapp/screens/home/
folder. - Replace the
// CODELAB: commissionDeviceLauncher definition
comment with the following code to register and handle the commissioning Activity result: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) } }
Step 2: Trigger the commission device action
In this step, the user triggers the "Commission Device" action by clicking on the "+" button at the bottom right of the Home screen. A call is then made to commissionDevice()
.
val onCommissionDevice = { ... commissionDevice(activity!!.applicationContext, commissionDeviceLauncher) }
Step 3: Call the API
- Still in
HomeScreen.kt
in thejava/com/google/homesampleapp/screens/home
folder. - Replace the
// CODELAB: commissionDevice
comment with the followingcommissionDeviceRequest
.setCommissioningService
bindsAppCommissioningService
to aCommissioningService
instance, returned in a callback function. When you pass a custom service, Home Mobile SDK will first commission devices to the Android local fabric, then send the onboarding payload back to theAppCommissioningService
.val commissionDeviceRequest = CommissioningRequest.builder() .setCommissioningService(ComponentName( context, AppCommissioningService::class.java)) .build()
- Call
.getCommissioningClient()
, then call.commissionDevice()
.
Matter.getCommissioningClient(context) .commissionDevice(commissionDeviceRequest)
To complete our commissionDevice
function, add an addOnSuccessListener
and addOnFailureListener
:
.addOnSuccessListener { result -> commissionDeviceLauncher.launch(IntentSenderRequest.Builder(result).build()) } .addOnFailureListener { error -> Timber.e(error) }
5. Create a Commissioning Service
In the commissionDevice()
function, we requested to get a CommissioningService from the CommissioningClient API. In this flow, the CommissioningClient API commissions devices to the Local Android fabric first, then returns a callback that includes the CommissioningRequestMetadata object:
public interface CommissioningService { interface Callback { void onCommissioningRequested(CommissioningRequestMetadata metadata); } }
Now, we have to inherit the CommissioningService.Callback and provide the functionality required to commission devices to our sample app. Here's an example of a basic CommissioningService implementation:
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()) } }
Step 1: Explore the custom AppCommissioningService
To help you get started, we've already defined the basic class structure for our custom CommissioningService. Here's a quick overview of the service functionality. To follow along, open AppCommissioningService
in java/commissioning
.
We've added the following imports for the Home Mobile SDK APIs:
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
also includes libraries from the Matter repo (connectedhomeip
):
import com.google.homesampleapp.chip.ChipClient
Finally, the service includes imports to support Hilt and Kotlin coroutines.
Next, we create the constructor and set a few things up, including the commissioningServiceDelegate
, which we'll use to let Google Play Services know when commissioning is complete.
private lateinit var commissioningServiceDelegate: CommissioningService ... commissioningServiceDelegate = CommissioningService.Builder(this).setCallback(this).build()
Now it's time to add the commissioning functions.
Step 2: Override onCommissioningRequested
To commission devices to the app's development fabric, complete the following steps:
- Open
AppCommissioningService
injava/commissioning
. - Locate the
onCommissioningRequested()
function. We've provided a log message that prints out theCommissioningRequestMetadata
. Replace the// CODELAB: onCommissioningRequested()
comment to start theserviceScope
coroutine and get thedeviceId
.// Perform commissioning on custom fabric for the sample app. serviceScope.launch { val deviceId = devicesRepository.incrementAndReturnLastDeviceId()
- Perform commissioning. For this step, we can pass the device information returned in the CommissioningRequestMetadata object. The
ChipClient
uses this metadata information to create a secure channel between the GHSA for Matter app and your device.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 }
- Use the
commissioningServiceDelegate
to let Google Play Services know that commissioning is complete. In.sendCommissioningComplete()
, pass the 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") } }
Run the app
Now that all of the required code is in place to commission to our local fabric, it's time to test it. Choose your Android device, and run the app. From the Home screen, tap Add Device and complete the steps to commission your device.
When commissioning completes, your device now participates in two fabrics: the Local Android fabric, and your local development fabric. Each fabric has its own set of credentials and a unique, 64-bit fabric ID.
6. Control devices
Commissioning to a development fabric allows you to use the libraries from the Matter repo (connectedhomeip
) to control devices from the sample app.
We've created some helper classes to make it easier to access device Clusters and send commands. To learn more, open ClustersHelper
in java/clusters
. This Singleton helper imports the following libraries to access device information:
import chip.devicecontroller.ChipClusters import chip.devicecontroller.ChipStructs
We can use this class to get the On/Off Cluster for a device, then call .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) } }) } }
Toggle a device
After you commission a device, the payload returned in the CommissioningResult gets added to the DataStore. This gives our app access to device information that we can use to send commands.
Matter apps are event driven. When the Matter stack is initialized, Cluster services listen for incoming messages. Once a device is commissioned, Matter clients send commands over the secure operational channel that was established during device commissioning.
On the device, packets are validated, decrypted, then dispatched with a callback. Callback functions include the EndpointId, ClusterId, and AttributeId, accessible from the attributePath
. For example, this code can be implemented on a Matter device:
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; }
In the next steps, you'll use the Matter SDK and ClustersHelper
to toggle a device.
- Go to
DeviceViewModel
injava/screens/device
. - Locate the
updateDeviceStateOn
function. - Replace the
// CODELAB: toggle
comment with the code to callclustersHelper
, then update the device repository: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") }
This function is called from DeviceScreen
:
// On/Off Switch click. val onOnOffClick: (value: Boolean) -> Unit = { value -> deviceViewModel.updateDeviceStateOn(deviceUiModel!!, value) }
Run the app
Run the app to reload your updates. From the Home screen, toggle your device on and off.
7. Share devices with other ecosystems
Sharing a device is referred to as multi-admin flow in the Matter specification.
In the previous steps, we learned that the Home Mobile SDK makes it possible to commission devices to the Local Android fabric and also to a development fabric for the sample app. This is an example of multi-admin flow, where devices can be commissioned to more than one fabric.
Now, you may want to share devices with even more fabrics, especially if this is a household where people have their own preferences when it comes to applications and platforms.
The Home Mobile SDK provides this functionality in the ShareDeviceRequest API, allowing you to:
- Open a temporary commissioning window for devices.
- Change the state of your devices, enabling them to be commissioned to another fabric.
- Control your devices from other apps and ecosystems.
In the next steps, you'll use the Home Mobile SDK to share devices.
Step 1: Create a GPS Activity Launcher
Similar to the Commissioning Activity Launcher that we created when we commissioned to a development fabric, we've created a Share Device Activity Launcher to handle the IntentSender
from the CommissioningClient API.
- Open
DeviceScreen
in thejava/com/google/homesampleapp/screens/device/
folder. - Replace the
// CODELAB: shareDeviceLauncher definition
comment with the following code to register and handle the.shareDevice()
Activity result: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) } }
Step 2: Trigger the share device action
In this step, the user triggers the "Share Device" action by clicking on the "Share" button on the device screen. A call is then made to the deviceViewModel
to open a Pairing Window for device sharing.
// Share Device button click. val onShareDevice: () -> Unit = remember { { deviceViewModel.openPairingWindow(deviceUiModel!!.device.deviceId) } }
After successfully opening the paring window, the deviceViewModel
communicates that fact to the UI. Communication between the ViewModel
and the UI is done via StateFlow
objects.
// Communicate to the UI that the pairing window is open. // UI can then launch the GPS activity for device sharing. _pairingWindowOpenForDeviceSharing.value = true
Upon seeing the change to the StateFlow object, DeviceScreen makes the following call:
shareDevice(activity!!.applicationContext, shareDeviceLauncher, deviceViewModel)
Step 3: Call the API
Now it's time to initiate a share device task.
- Open
DeviceScreen.kt
in thejava/com/google/homesampleapp/screens/device/
folder. - Locate the
shareDevice()
function. Replace the// CODELAB: shareDevice
comment with theShareDeviceRequest
. TheDeviceDescriptor
provides specific information about the device such as its Vendor Id, Product Id, and deviceType. In this example, we hard-code the values.val shareDeviceRequest = ShareDeviceRequest.builder() .setDeviceDescriptor(DeviceDescriptor.builder().build()) .setDeviceName("GHSAFM temp device name")
- Set the CommissioningWindow and parameters.
.setCommissioningWindow( CommissioningWindow.builder() .setDiscriminator(Discriminator.forLongValue(DISCRIMINATOR)) .setPasscode(SETUP_PIN_CODE) .setWindowOpenMillis(SystemClock.elapsedRealtime()) .setDurationSeconds(OPEN_COMMISSIONING_WINDOW_DURATION_SECONDS.toLong()) .build()) .build()
- Call
.getCommissioningClient()
, only this time, use the.shareDevice()
API.Matter.getCommissioningClient(context) .shareDevice(shareDeviceRequest)
The success callback of the commissioningClient.shareDevice()
API provides the IntentSender to be used to launch the Share Device Activity in Google Play Services.
- To complete our
shareDevice
function, add anaddOnSuccessListener
andaddOnFailureListener
. On success,launch
is called onshareDeviceLauncher
to launch the GPS activity for device sharing..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()) }
Run the app
To share your Matter device with other ecosystems, you'll need to have another platform installed on your Android device. We've created another instance of the sample app that you can use as the target commissioner.
Once you have the target commissioner installed on your Android device, verify that you can share your Matter device. The target commissioner app is labeled GHSAFM-TC.
Your devices can now participate in three fabrics:
- The Local Android fabric.
- Your development fabric (this app).
- This third fabric that you've just shared the device with.
8. Next Steps
Congratulations
Congratulations, you've successfully completed this Codelab and learned how to commission and share devices using the Home Mobile SDK.
If you're having issues with the sample app, try completing the steps to verify your environment:
If you have questions about using the sample app or discover a code bug, you can submit issues to the Issue Tracker in the GitHub repository:
To get official guidance from Google on technical questions, use the Smart Home Developer Forum:
To get technical support from the community, use the google-smart-home
tag on Stack Overflow: