Skip to main content

Home API Usage

Below you can find examples of using the Home API.

Commissioner APIs

Commission a Device

Begin by creating a CommissioningClient instance:

val commissioningClient = Matter.getCommissioningClient()

Request to start commission of your device:

fun startCommissionDevice(context: Context) {
viewModelScope.launch {
val commissioningClient = Matter.getCommissioningClient()
val intentSender = commissioningClient.commissionDevice(context)
}
}

Get a result of commissioning:

fun commissionDeviceSuccess(activityResult: ActivityResult) {
val result = CommissioningResult.fromIntentSenderResult(activityResult)
Timber.d("deviceId:${result.deviceId}, deviceName:${result.deviceName}")
}

Full code:

@HiltViewModel
class MainViewModel : ViewModel() {

private val _intentSender = MutableLiveData<IntentSender?>()
val intentSender: LiveData<IntentSender?> get() = _intentSender

fun startCommissionDevice(context: Context) {
viewModelScope.launch {
val commissioningClient = Matter.getCommissioningClient()
val intentSender = commissioningClient.commissionDevice(context)

_intentSender.value = intentSender
}
}

fun commissionDeviceSuccess(activityResult: ActivityResult) {
val result = CommissioningResult.fromIntentSenderResult(activityResult)
viewModelScope.launch {
dataStoreRepository.addDevice(Device(result.deviceId, result.deviceName))
}
}

fun clearIntentSender() {
_intentSender.postValue(null)
}
}

Sharing a Device

Begin by creating a CommissioningClient instance:

val commissioningClient = Matter.getCommissioningClient()

Request to start sharing of your device:

fun shareDevice(context: Context, deviceId: String) {
val commissioningClient = Matter.getCommissioningClient()
val intentSender = commissioningClient.shareDevice(context, CommissioningClient.ShareDeviceRequest(deviceId))
}

Get a result of sharing your device:

You will receive the device delete event through onDeviceDeleted from IHomeServiceListener.

Full code:

@AndroidEntryPoint
class LightFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val layoutShareDeviceBinding = setupShareDeviceLayout()
layoutShareDeviceBinding.shareButton.setOnClickListener {
viewModel.shareDevice(requireContext())
}
}
}

@HiltViewModel
class LightViewModel : ViewModel() {

private val _intentSender = MutableLiveData<IntentSender?>()
val intentSender: LiveData<IntentSender?> get() = _intentSender

fun shareDevice(context: Context) {
val commissioningClient = Matter.getCommissioningClient()
val intentSender = commissioningClient.shareDevice(
context,
CommissioningClient.ShareDeviceRequest(deviceId)
)
_intentSender.value = intentSender
}

fun clearIntentSenderForShareDevice() {
_intentSender.postValue(null)
}
}

Removing a Device

Begin by creating a CommissioningClient instance:

val commissioningClient = Matter.getCommissioningClient()

Request to start removal of your device:

fun shareDevice(context: Context, deviceId: String) {
val commissioningClient = Matter.getCommissioningClient()
val intentSender = commissioningClient.removeDevice(context, CommissioningClient.RemoveDeviceRequest(deviceId))
}

Get a result of your device removal:

info

There is no result for deviceDevice.

Full code:

@AndroidEntryPoint
class LightFragment : Fragment() {
protected abstract fun setupAppbar(): LayoutAppbarBinding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
setupAppbar().toolbarMore.setOnClickListener {
showMoreMenuPopup(it)
}
}

private fun showMoreMenuPopup(anchor: View) {
val items = listOf(
MoreMenuItem(
MoreMenuItem.Type.REMOVE_DEVICE,
R.drawable.round_delete_outline_24,
R.color.red,
getString(R.string.remove_device)
)
)

ListPopupHelper.getListPopup(
anchor,
requireContext(),
MoreMenuAdapter(requireContext(), items)
) { _, _, position, _ ->
val item = items[position]
when (item.type) {
MoreMenuItem.Type.REMOVE_DEVICE -> {
viewModel.removeDevice(requireContext())
}
}
}.show()
}
}

@HiltViewModel
class LightViewModel : ViewModel() {

private val _intentSender = MutableLiveData<IntentSender?>()
val intentSender: LiveData<IntentSender?> get() = _intentSender

fun removeDevice(context: Context) {
val commissioningClient = Matter.getCommissioningClient()
val intentSender = commissioningClient.removeDevice(
context,
CommissioningClient.RemoveDeviceRequest(deviceId)
)
_intentSender.value = intentSender
}

fun clearIntentSenderForRemoveDevice() {
_intentSender.postValue(null)
}
}

Controller APIs

Control a Device

Begin by creating a MatterClient instance:

val matterClient = Matter.getClient(context)

Get a device by device ID:

val device = matterClient.getDevice(deviceId)

Get a Capability of the device:

/* Switch Device */
val switchCapability = device.readCapability(Switch)
/* Light Device */
val lightCapability = device.readCapability(ColorControl)

Control the device using the APIs supported by the Capability:

/* Switch Device */
switchCapability.on()
switchCapability.off()
/* Light Device */
lightCapability.setColor(hue = 24.5, saturation = 78.6)

Get attributes of the device:

/*
* Switch Device
* Get the current state of switch
*/
private val _onOff = MutableLiveData(false)
val onOff: LiveData<Boolean> get() = _onOff

device.readCapability(Switch)?.onOff?.collect { onOff ->
_onOff.value = onOff
}

Full code:

@AndroidEntryPoint
class LightFragment : Fragment() {
override val viewModel by viewModels<LightViewModel>()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
setupObservers()
}

fun setupObservers() {
viewModel.onOff.observe(viewLifecycleOwner) {
if (it) {
binding.power.value.text = getString(R.string.on)
binding.power.actionButton.setImageResource(R.drawable.ic_power_on)
} else {
binding.power.value.text = getString(R.string.off)
binding.power.actionButton.setImageResource(R.drawable.ic_power_off)
}
}
}

@HiltViewModel
class LightViewModel @Inject constructor(
homeRepository: HomeRepository,
savedStateHandle: SavedStateHandle
) : ViewModel() {

protected lateinit var device: Device

private val _onOff = MutableLiveData(false)
val onOff: LiveData<Boolean> get() = _onOff

init {
viewModelScope.launch {
device = homeRepository.getHomeClient().getDevice(it.deviceId) as Device
device.readCapability(Switch)?.onOff?.collect { onOff ->
_onOff.value = onOff
}
}
}

fun switchOnOff(context: Context, deviceId: String, on: Boolean) {
viewModelScope.launch {
device.readCapability(Switch)?.let { switch ->
if (on) {
switch.on()
} else {
switch.off()
}
}
}
}
}

@Singleton
class HomeRepositoryImpl @Inject constructor(
@ApplicationContext private val context: Context
) : HomeRepository {

override fun getHomeClient(): HomeClient = Matter.getClient(context)
}

Location APIs

Get a List of Locations and Rooms

Begin by creating a MatterClient instance:

val matterClient = Matter.getClient(context)

Get a list of locations:

val locations = matterClient.getLocations()

Get a list of rooms:

matterClient.getLocations().collect { locations ->
locations.forEach { location ->
launch {
location.getRooms().collect { rooms ->
Timber.d("Rooms:$location:$rooms")
}
}
}
}

Get a list of devices:

matterClient.getLocations().collect { locations ->
locations.forEach { location ->
launch {
location.getRooms().collect { rooms ->
rooms.forEach { room ->
launch {
room.getDevices().collect { devices ->
Timber.d("Devices:$devices")
}
}
}
}
}
}
}

Full code:

@HiltViewModel
class MainViewModel : ViewModel() {

private val _locationInfos = MutableStateFlow<List<LocationInfo>>(emptyList())

init {
viewModelScope.launch {
homeRepository.getHomeClient().getLocations().collect { locations ->
_locationInfos.value = locations.map {
LocationInfo(LocationRoomItem(it.locationName, it.locationId), emptyList())
}

locations.forEach { location ->
launch {
location.getRooms().collect { rooms ->
_locationInfos.update { locationInfos ->
locationInfos.firstOrNull { it.location.id == location.locationId }?.roomInfos =
rooms.map {
RoomInfo(
LocationRoomItem(it.roomName, it.roomId),
emptyList()
)
}
locationInfos
}

rooms.forEach { room ->
launch {
room.getDevices().collect { devices ->
_locationInfos.update { locationInfos ->
locationInfos.firstOrNull {
it.location.id == location.locationId
}?.roomInfos?.firstOrNull {
it.room.id == room.roomId
}?.devices = devices.map { device ->
DeviceItem(
device = device,
deviceActionType = getActionType(device),
onOff = getOnOffStateFlow(device),
getDrawableByDeviceType(device.getDeviceType())
)
}
locationInfos
}
}
}
}
}
}
}
}
}
}
}

Observing State Changes

The Home APIs allow observing changes to locations, rooms and device state. Observing changes in device state is possible using the Kotlin flows.

The Home APIs provide StateFlow that can be used to collect changes to the data that is exposed by the API. This is done by collecting from that flow.

When any item in one of those collections is added, deleted, or modified, the latest snapshot of the collection is returned.

Collecting From a Flow

Collect changes to a location, including:

  • Location name
  • Location ID:
matterClient.getLocations().collect { locations ->
locations.forEach { location ->
Timber.d("location = name:${location.locationName}")
Timber.d("location = id:${location.locationId}")
}
}

Collect changes to a room, including:

  • Room name
  • Room ID
matterClient.getLocations().collect { locations ->
locations.forEach { location ->
launch {
location.getRooms().collect { rooms ->
rooms.forEach { room ->
Timber.d("room = name:${room.roomName}")
Timber.d("room = id:${room.roomId}")
}
}
}
}
}

Collect changes to devices, including:

  • Device name
  • Device ID
  • Location ID
  • Room ID
matterClient.getLocations().collect { locations ->
locations.forEach { location ->
launch {
location.getRooms().collect { rooms ->
rooms.forEach { room ->
launch {
room.getDevices().collect { devices ->
devices.forEach { device ->
Timber.d("device = name:${device.deviceName}")
Timber.d("device = id:${device.deviceId}")
Timber.d("device = locationId:${device.locationId}")
Timber.d("device = roomId:${device.roomId}")
}
}
}
}
}
}
}
}