docs/development_controllers/matter-repl/Matter_Multi_Fabric_Commissioning.ipynb
This walks through creating multiple controllers on multiple fabrics, using those controllers to commission a target onto those fabrics and finally, interacting with them using the interaction model.
The CertificateAuthorityManager class manages a set of CertificateAuthority instances (both present in matter.CertificateAuthority. package). a CertificateAuthorityrepresents an operational root of trust, a Root Certificate Authority (CA) with a root key pair with the associated public key (i.e "Root PK"). TheCertificateAuthorityclass manages a list ofFabricAdmins` adminstering a fabric within that CA.
The FabricAdmin class (present in the matter.FabricAdmin package) is responsible for administering a fabric. It houses the Fabric ID and is managed by a CertificateAuthority.
The FabricAdmin can be used to vend ChipDeviceController objects that represent a controller instance with a specific identity grounded in the admin's fabric. This controller can then be used to commission and interact with devices.
Let's clear out our persisted storage (if one exists) to start from a clean slate.
import os, subprocess
if os.path.isfile('/tmp/repl-storage.json'):
os.remove('/tmp/repl-storage.json')
# So that the all-clusters-app won't boot with stale prior state.
os.system('rm -rf /tmp/chip_*')
Let's first begin setting up by importing some key modules that are needed to make it easier for us to interact with the Matter stack.
ReplStartup.py is run within the global namespace. This results in all of its imports being made available here.
NOTE: This is not needed if you launch the REPL from the command-line.
%reset -f
import importlib.util
spec = importlib.util.find_spec('matter.ReplStartup')
%run {spec.origin} --debug
At startup, the certificateAuthorityManager within REPL will attempt to find CertificateAuthority instances present in persistent storage.If it can't find any (as is the case here), it will create a new CertificateAuthority instance at CA index 0. This CertificateAuthority instance will construct a default FabricAdmin object on FabricAdmin index 0 with a Fabric ID = 1.
inspect(caList[0].adminList[0])
The Newly created FabricAdmin will automatically construct a device controller (devCtrl) on it's own fabric.
inspect(devCtrl)
Let's launch an instance of the chip-all-clusters-app.
import time, os
import subprocess
os.system('pkill -f chip-all-clusters-app')
time.sleep(1)
appPath = '../../../out/linux-x64-all-clusters/chip-all-clusters-app'
process = subprocess.Popen(appPath, stdout=subprocess.DEVNULL)
time.sleep(1)
Commission the target onto Fabric 1 using the default device controller instance with a NodeId of 2.
await devCtrl.CommissionOnNetwork(2, 20202021)
Read out the OpCreds cluster to confirm membership into Fabric 1.
await devCtrl.ReadAttribute(2, [(Clusters.OperationalCredentials.Attributes.Fabrics)], fabricFiltered=False)
We create a new CertificateAuthority instance, and then create a new FabricAdmin that administers Fabric 2 within that CA.
certificateAuthority = certificateAuthorityManager.NewCertificateAuthority()
fabricAdmin2 = certificateAuthority.NewFabricAdmin(vendorId=0xFFF1, fabricId=2)
Two different CertificateAuthority instances are now present
caList
Here's a brief peek at the JSON data that is in the persisted storage file.
devCtrl2 = fabricAdmin2.NewController()
The Controller of Fabric 1 must open the commissioning window that allows the Commissioner of Fabric 2 to join the node to its fabric.
await devCtrl.SendCommand(2, 0, Clusters.AdministratorCommissioning.Commands.OpenBasicCommissioningWindow(180), timedRequestTimeoutMs=10000)
await devCtrl2.CommissionOnNetwork(2, 20202021)
Read out the OpCreds cluster to confirm membership into Fabric 2.
await devCtrl2.ReadAttribute(2, [(Clusters.OperationalCredentials.Attributes.Fabrics)], fabricFiltered=False)
Let's simulate re-launching the REPL to show-case the capabilities of the persistence storage and its mechanics.
%reset -f
import importlib.util
spec = importlib.util.find_spec('matter.ReplStartup')
%run {spec.origin}
The REPL now loaded (through the certificateAuthorityManager) the two certificateAuthority instances that were created in the previous session into the CaList. It has also created a default controller on the first fabric in that list (Fabric 1) as devCtrl.
caList
To prove that we do indeed have two distinct fabrics and controllers on each fabric, let's go ahead and update the label of each fabric. To do so, you'd need to successfully establish a CASE session through a controller on the respective fabric, and call the 'UpdateLabel' command.
Underneath the covers, each device controller will do operational discovery of the NodeId being read and establish a CASE session before issuing the IM interaction.
devCtrl
await devCtrl.SendCommand(2, 0, Clusters.OperationalCredentials.Commands.UpdateFabricLabel("Fabric1Label"))
await devCtrl.ReadAttribute(2, [(Clusters.OperationalCredentials.Attributes.Fabrics)], fabricFiltered=False)
Instantiate a controller on fabric 2 and use it to read out the op creds from that fabric.
fabricAdmin2 = caList[1].adminList[0]
devCtrl2 = fabricAdmin2.NewController()
await devCtrl2.SendCommand(2, 0, Clusters.OperationalCredentials.Commands.UpdateFabricLabel("Fabric2Label"))
await devCtrl2.ReadAttribute(2, [(Clusters.OperationalCredentials.Attributes.Fabrics)], fabricFiltered=False)
devCtrl2.Shutdown()