<jupyter_start><jupyter_text>Multi Fabric - Commissioning and InteractionsThis 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.  FabricAdmins and ControllersThe `FabricAdmin` class (present in the `chip.FabricAdmin` package) is responsible for adminstering a fabric. It houses the Fabric ID and Index, as well as an RCAC and ICAC that provides the certificate material grounding that fabric.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.  Clear Persisted StorageLet's clear out our persisted storage (if one exists) to start from a clean slate.<jupyter_code>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_*')<jupyter_output><empty_output><jupyter_text>InitializationLet's first begin by setting up by importing some key modules that are needed to make it easier for us to interact with the Matter stack.`ChipReplStartup.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._<jupyter_code>import chip.native
import pkgutil
module = pkgutil.get_loader('chip.ChipReplStartup')
%run {module.path}<jupyter_output><empty_output><jupyter_text>At startup, the REPL will attempt to find any previously configured fabrics stored in persisted storage. If it can't find any (as is the case here), it will construct a default `FabricAdmin` object on Fabric 1 (Index 1) as well as construct a device controller (`devCtrl`) on that fabric.<jupyter_code>fabricAdmins
devCtrl<jupyter_output><empty_output><jupyter_text>Commission onto Fabric 1  Launch ServerLet's launch an instance of the `chip-all-clusters-app`.<jupyter_code>import time, os
import subprocess
os.system('pkill -f chip-all-clusters-app')
time.sleep(1)

# The location of the all-clusters-app in the cloud playground is one level higher - adjust for this by testing for file presence.
if (os.path.isfile('../../../out/debug/chip-all-clusters-app')):
    appPath = '../../../out/debug/chip-all-clusters-app'
else:
    appPath = '../../../../out/debug/chip-all-clusters-app'

process = subprocess.Popen(appPath, stdout=subprocess.DEVNULL)
time.sleep(1)<jupyter_output><empty_output><jupyter_text>Commission TargetCommission the target onto Fabric 1 using the default device controller instance with a NodeId of 1.<jupyter_code>devCtrl.CommissionIP(b'127.0.0.1', 20202021, 2)<jupyter_output>2022-01-25 16:59:00 johnsj-macbookpro1.roam.corp.google.com chip.CTL[27921] ERROR Unable to find country code, defaulting to WW
2022-01-25 16:59:00 johnsj-macbookpro1.roam.corp.google.com chip.SC[27921] ERROR The device does not support GetClock_RealTimeMS() API. This will eventually result in CASE session setup failures.<jupyter_text>Read OpCreds ClusterRead out the OpCreds cluster to confirm membership into Fabric 1.<jupyter_code>await devCtrl.ReadAttribute(2, [(Clusters.OperationalCredentials.Attributes.FabricsList)], fabricFiltered=False)<jupyter_output><empty_output><jupyter_text>Commission onto Fabric 2  Create new FabricAdmin<jupyter_code>import chip.FabricAdmin as FabricAdmin
fabric2 = FabricAdmin.FabricAdmin(fabricId = 2, fabricIndex = 2)<jupyter_output>New FabricAdmin: FabricId: 2(2)<jupyter_text>Here's a brief peek at the JSON data that is in the persisted storage file.<jupyter_code>builtins.chipStack.GetStorageManager().jsonData
devCtrl2 = fabric2.NewController()<jupyter_output>Allocating new controller with FabricId: 2(2), NodeId: 1<jupyter_text>Open Commissioning Window<jupyter_code>await devCtrl.SendCommand(2, 0, Clusters.AdministratorCommissioning.Commands.OpenBasicCommissioningWindow(180))
devCtrl2.CommissionIP(b'127.0.0.1', 20202021, 2)<jupyter_output>2022-01-25 16:59:00 johnsj-macbookpro1.roam.corp.google.com chip.CTL[27921] ERROR Unable to find country code, defaulting to WW
2022-01-25 16:59:00 johnsj-macbookpro1.roam.corp.google.com chip.SC[27921] ERROR The device does not support GetClock_RealTimeMS() API. This will eventually result in CASE session setup failures.<jupyter_text>Read OpCreds ClusterRead out the OpCreds cluster to confirm membership into Fabric 2.<jupyter_code>await devCtrl2.ReadAttribute(2, [(Clusters.OperationalCredentials.Attributes.FabricsList)], fabricFiltered=False)<jupyter_output><empty_output><jupyter_text>Relaunch REPLLet's simulate re-launching the REPL to show-case the capabilities of the persistence storage and its mechanics.<jupyter_code>import chip.native
import pkgutil
module = pkgutil.get_loader('chip.ChipReplStartup')
%run {module.path}<jupyter_output><empty_output><jupyter_text>The REPL has now loaded the two fabrics that were created in the previous session into the `fabricAdmins` variable. It has also created a default controller on the first fabric in that list (Fabric 1) as `devCtrl`.  Establish CASE and Read OpCredsTo 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 succcessfully 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.<jupyter_code>await devCtrl.SendCommand(2, 0, Clusters.OperationalCredentials.Commands.UpdateFabricLabel("Fabric1Label"))
await devCtrl.ReadAttribute(2, [(Clusters.OperationalCredentials.Attributes.FabricsList)], fabricFiltered=False)<jupyter_output>2022-01-25 16:59:00 johnsj-macbookpro1.roam.corp.google.com chip.SC[27921] ERROR The device does not support GetClock_RealTimeMS() API. This will eventually result in CASE session setup failures.<jupyter_text>Instantiate a controller on fabric 2 and use it to read out the op creds from that fabric.<jupyter_code>devCtrl2 = fabricAdmins[1].NewController()
await devCtrl2.SendCommand(2, 0, Clusters.OperationalCredentials.Commands.UpdateFabricLabel("Fabric2Label"))
await devCtrl2.ReadAttribute(2, [(Clusters.OperationalCredentials.Attributes.FabricsList)], fabricFiltered=False)
devCtrl2.Shutdown()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08333333333333333, 'fraction_numerical': 0.026363636363636363, 'mean_word_length': 6.32630410654828, 'pattern_counts': {'":': 0, '<': 48, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '251310', 'license': 'Apache-2.0', 'n_tokens_mistral': 2116, 'n_tokens_neox': 1935, 'n_words': 653, 'path': 'docs/guides/repl/Matter - Multi Fabric Commissioning.ipynb', 'repo_name': 'adamb-q/connectedhomeip', 'size': '6600'}