blob: 62e60b819b7db2927fb817decd59d9f3534e947c [file] [log] [blame] [view]
# Working with Python CHIP Controller
The Python CHIP controller is a library that allows to create a Matter fabric
and commission Matter devices with it.
The `chip-repl` is a REPl which sets up a Python CHIP Controller and allows to
explore the Python CHIP Controller API and communicate with devices from the
command line.
<hr>
- [Source files](#source-files)
- [Building Python CHIP Controller](#building-and-installing)
- [Running the CHIP REPL](#running-the-chip-repl)
- [Using Python CHIP Controller REPL for Matter accessory testing](#using-python-chip-controller-repl-for-matter-accessory-testing)
- [Example usage of the Python CHIP Controller REPL](#example-usage-of-the-python-chip-controller-repl)
- [Explore Clusters, Attributes and Commands](#explore-clusters-attributes-and-commands)
<hr>
## Source files
You can find source files of the Python CHIP Controller tool in the
`src/controller/python` directory.
The tool uses the generic CHIP Device Controller library, available in the
`src/controller` directory.
<hr>
## Building and installing
Before you can use the Python controller, you must compile it from the source on
Linux (amd64 / aarch64) or macOS.
> To ensure compatibility, build the Python CHIP controller and the Matter
> device from the same revision of the connectedhomeip repository.
To build and run the Python CHIP controller:
1. Install all necessary packages and prepare the build system. For more
details, see the [Building Matter](BUILDING.md) documentation:
```
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install git gcc g++ python pkg-config libssl-dev libdbus-1-dev libglib2.0-dev libavahi-client-dev ninja-build python3-venv python3-dev python3-pip unzip libgirepository1.0-dev libcairo2-dev bluez
```
If the Python CHIP controller is built on a Raspberry Pi, install additional
packages and reboot the device:
```
sudo apt-get install pi-bluetooth
sudo reboot
```
2. Clone the Project CHIP repository:
```
git clone https://github.com/project-chip/connectedhomeip.git
```
3. Enter the `connectedhomeip` directory:
```
cd connectedhomeip
```
4. Initialize the git submodules:
```
git submodule update --init
```
5. Build and install the Python CHIP controller:
```
scripts/build_python.sh -m platform -i out/python_env
source out/python_env/bin/activate
```
> Note: This builds the Python CHIP Controller along with the CHIP REPL as
> Python wheels and installs it into a separate Python virtual environment.
> To get more details about available build configurations, run the
> following command: `scripts/build_python.sh --help`
<hr>
## Running the CHIP REPL
1. Activate the Python virtual environment with the Python CHIP Controller
installed:
```
source out/python_env/bin/activate
```
2. Run the CHIP REPL to explore the API of the Python CHIP controller:
```
chip-repl
```
<hr>
## Using Python CHIP Controller REPL for Matter accessory testing
This section describes how to use Python CHIP controller to test the Matter
accessory. Below steps depend on the application clusters that you implemented
on the device side and may be different for your accessory.
### Step 1: Prepare the Matter accessory.
This tutorial is using the Matter Light Bulb example with the Bluetooth LE
commissioning. However, you can adapt this procedure to other available Matter
examples.
Build and program the device with the Matter accessory firmware by following the
example's documentation.
### Step 2: Enable Bluetooth LE advertising on Matter accessory device.
Some examples are configured to advertise automatically on boot. Other examples
require physical trigger, for example pushing a button. Follow the documentation
of the Matter accessory example to learn how Bluetooth LE advertising is enabled
for the given example.
### Step 3: Discover commissionable Matter accessory device
An uncommissioned accessory device advertises over Bluetooth LE or via mDNS if
already on the network. Run the following command to scan all advertised Matter
devices:
```
devCtrl.DiscoverCommissionableNodes()
```
### Step 4: Set network pairing credentials
You must provide the controller with network credentials that will be further
used in the device commissioning procedure to configure the device with a
network interface, such as Thread or Wi-Fi.
#### Setting Thread network credentials
1. Fetch and store the current Active Operational Dataset from the Thread Border
Router. Depending on if the Thread Border Router is running on Docker or
natively on Raspberry Pi, execute the following commands:
- For Docker:
```
sudo docker exec -it otbr sh -c "sudo ot-ctl dataset active -x"
0e080000000000010000000300001335060004001fffe002084fe76e9a8b5edaf50708fde46f999f0698e20510d47f5027a414ffeebaefa92285cc84fa030f4f70656e5468726561642d653439630102e49c0410b92f8c7fbb4f9f3e08492ee3915fbd2f0c0402a0fff8
Done
```
- For native installation:
```
sudo ot-ctl dataset active -x
0e080000000000010000000300001335060004001fffe002084fe76e9a8b5edaf50708fde46f999f0698e20510d47f5027a414ffeebaefa92285cc84fa030f4f70656e5468726561642d653439630102e49c0410b92f8c7fbb4f9f3e08492ee3915fbd2f0c0402a0fff8
Done
```
Matter specification does not define how the Thread or Wi-Fi credentials are
obtained by Controller. For example, for Thread, instead of fetching
datasets directly from the Thread Border Router, you might also use a
different out-of-band method.
2. Set the previously obtained Active Operational Dataset as a byte array using
the following command:
```
thread_dataset = bytes.fromhex("0e080000000000010000000300001335060004001fffe002084fe76e9a8b5edaf50708fde46f999f0698e20510d47f5027a414ffeebaefa92285cc84fa030f4f70656e5468726561642d653439630102e49c0410b92f8c7fbb4f9f3e08492ee3915fbd2f0c0402a0fff8")
devCtrl.SetThreadOperationalDataset(thread_dataset)
```
#### Setting Wi-Fi network credentials
Assuming your Wi-Fi SSID is _TESTSSID_, and your Wi-Fi password is _P455W4RD_,
set the credentials to the controller by executing the following command:
```
devCtrl.SetWiFiCredentials(<ssid>, <password>)
```
### Step 5: Commission the Matter accessory device over Bluetooth LE
The controller uses a 12-bit value called **discriminator** to discern between
multiple commissionable device advertisements, as well as a 27-bit **setup PIN
code** to authenticate the device. You can find these values in the logging
terminal of the device (for example, UART). For example:
```
I: 254 [DL]Device Configuration:
I: 257 [DL] Serial Number: TEST_SN
I: 260 [DL] Vendor Id: 65521 (0xFFF1)
I: 263 [DL] Product Id: 32768 (0x8000)
I: 267 [DL] Hardware Version: 1
I: 270 [DL] Setup Pin Code: 20202021
I: 273 [DL] Setup Discriminator: 3840 (0xF00)
I: 278 [DL] Manufacturing Date: (not set)
I: 281 [DL] Device Type: 65535 (0xFFFF)
```
Run the following command to establish the secure connection over Bluetooth LE,
with the following assumptions for the Matter accessory device:
- The discriminator of the device is _3840_
- The setup pin code of the device is _20202021_
- The temporary Node ID is _1234_
```
devCtrl.ConnectBLE(3840, 20202021, 1234)
```
You can skip the last parameter, the Node ID, in the command. If you skip it,
the controller will assign it randomly. In that case, note down the Node ID,
because it is required later in the configuration process.
It is also possible to use the QR setup code instead. It typically is shown on
the terminal of the device as well. For example:
```
CHIP:SVR: SetupQRCode: [MT:-24J0AFN00KA0648G00]
```
Use the following command to commission the device with the QR code:
```
devCtrl.CommissionWithCode("MT:-24J0AFN00KA0648G00", 1234)
```
After connecting the device over Bluetooth LE, the controller will go through
the following stages:
- Establishing a secure connection that completes the PASE
(Password-Authenticated Session Establishment) session using SPAKE2+
protocol and results in printing the following log:
```
Secure Session to Device Established
```
- Providing the device with a network interface using ZCL Network
Commissioning cluster commands, and the network pairing credentials set in
the previous step.
- Discovering the IPv6 address of the Matter accessory using the SRP (Service
Registration Protocol) for Thread devices, or the mDNS (Multicast Domain
Name System) protocol for Wi-Fi or Ethernet devices. It results in printing
log that indicates that the node address has been updated. The IPv6 address
of the device is cached in the controller for later usage.
- Closing the Bluetooth LE connection, as the commissioning process is
finished and the Python CHIP controller is now using only the IPv6 traffic
to reach the device.
### Step 6: Control application clusters.
For the light bulb example, execute the following command to toggle the LED
state:
```
await devCtrl.SendCommand(1234, 1, Clusters.OnOff.Commands.Toggle())
```
To change the brightness of the LED, use the following command, with the level
value somewhere between 0 and 255.
```
commandToSend = LevelControl.Commands.MoveToLevel(level=50, transitionTime=Null, optionsMask=0, optionsOverride=0)
await devCtrl.SendCommand(1234, 1, commandToSend)
```
### Step 7: Read basic information out of the accessory.
Every Matter accessory device supports a Basic Information Cluster, which
maintains collection of attributes that a controller can obtain from a device,
such as the vendor name, the product name, or software version. Use
`ReadAttribute()` command to read those values from the device:
```
attributes = [
(0, Clusters.BasicInformation.Attributes.VendorName),
(0, Clusters.BasicInformation.Attributes.ProductName),
(0, Clusters.BasicInformation.Attributes.SoftwareVersion),
]
await devCtrl.ReadAttribute(1234, attributes)
```
> In REPL, you can type `Clusters.BasicInformation.Attributes.` and then use the
> TAB key.
<hr>
## Example usage of the Python CHIP Controller REPL
These section covers a few useful commands of the Python CHIP Controller along
with examples demonstrating how they can be called from the REPL.
The
[CHIP Device Controller API documentation offer](https://project-chip.github.io/connectedhomeip-doc/testing/ChipDeviceCtrlAPI.html#chip-chipdevicectrl)
the full list of available commands.
### `SetThreadOperationalDataset(<thread-dataset>)`
Provides the controller with Thread network credentials that will be used in the
device commissioning procedure to configure the device with a Thread interface.
```
thread_dataset = bytes.fromhex("0e080000000000010000000300001335060004001fffe002084fe76e9a8b5edaf50708fde46f999f0698e20510d47f5027a414ffeebaefa92285cc84fa030f4f70656e5468726561642d653439630102e49c0410b92f8c7fbb4f9f3e08492ee3915fbd2f0c0402a0fff8")
devCtrl.SetThreadOperationalDataset(thread_dataset)
```
### `SetWiFiCredentials(<ssid>: str, <password>: str)`
Provides the controller with Wi-Fi network credentials that will be used in the
device commissioning procedure to configure the device with a Wi-Fi interface.
```
devCtrl.SetWiFiCredentials('TESTSSID', 'P455W4RD')
```
### `CommissionWithCode(<setupPayload>: str, <nodeid>: int, <discoveryType>: DiscoveryType)`
Commission with the given nodeid from the setupPayload. setupPayload may be a QR
or the manual setup code.
```
devCtrl.CommissionWithCode("MT:-24J0AFN00KA0648G00", 1234)
```
### `SendCommand(<nodeid>: int, <endpoint>: int, Clusters.<cluster>.Commands.<command>(<arguments>))`
Send a Matter command to the device. For example:
```python
commandToSend = Clusters.LevelControl.Commands.MoveWithOnOff(moveMode=1, rate=2, optionsMask=0, optionsOverride=0)
await devCtrl.SendCommand(1234, 1, commandToSend)
```
To see available arguments just create a command object without argument:
```
Clusters.LevelControl.Commands.MoveWithOnOff()
```
Shows which arguments are available:
```
MoveWithOnOff(
│ moveMode=0,
│ rate=Null,
│ optionsMask=0,
│ optionsOverride=0
)
```
### `ReadAttribute(<nodeid>: int, [(<endpoint id>: int, Clusters.<cluster>.Attributes.<attribute>)])`
Read the value of an attribute. For example:
```python
await devCtrl.ReadAttribute(1234, [(0, Clusters.BasicInformation.Attributes.VendorName)])
```
### `WriteAttribute(<nodeid>: int, [(<endpointid>: int, Clusters.<cluster>.Attributes.<attribute>(value=<attribute value>))])`
Write a value to an attribute. For example:
```python
await devCtrl.WriteAttribute(1234, [(1, Clusters.UnitTesting.Attributes.Int8u(value=1))])
await devCtrl.WriteAttribute(1234, [(1, Clusters.UnitTesting.Attributes.Boolean(value=True))])
await devCtrl.WriteAttribute(1234, [(1, Clusters.UnitTesting.Attributes.OctetString(value=b'123123\x00'))])
await devCtrl.WriteAttribute(1234, [(1, Clusters.UnitTesting.Attributes.CharString(value='233233'))])
```
### `ReadAttribute(<nodeid>: int, [(<endpoint>: int, Clusters.<cluster>.Attributes.<attribute>)], reportInterval=(<min interval>: int, <max interval>: int))`
Configure Matter attribute reporting settings. For example:
```python
await devCtrl.ReadAttribute(1234, [(1, Clusters.OccupancySensing.Attributes.Occupancy)], reportInterval=(10, 20))
```
To shutdown an existing attribute subscription use the `Shutdown()` function on
the returned subscription object:
```python
sub = await devCtrl.ReadAttribute(1234, [(1, Clusters.OccupancySensing.Attributes.Occupancy)], reportInterval=(10, 20))
sub.Shutdown()
```
## Explore Clusters, Attributes and Commands
In the Python REPL the Clusters and Attributes are classes. The `Clusters`
module contains all clusters. Tab completion can be used to explore available
clusters, attributes and commands.
For example, to get a list of Clusters, type `Clusters.` and hit tab. Continue
to hit tab to cycle through the available Clusters. Pressing return will select
the Cluster.
To explore Attributes, use the same technique but with the Attributes sub-class
of the Clusters class, for example, type `Clusters.(cluster name).Attributes.`
and hit tab.
The same is true for Commands, use the Commands sub-class. type
`Clusters.(cluster name).Commands.` and hit tab.