Skip to content

Commit 7b26c81

Browse files
lboueclaude
andcommitted
Add Matter Binding cluster support to MatterSwitch
Lets a Matter switch (opt-in via beginWithBinding()) be bound directly to another device, e.g. a light, and control it without a hub relaying every command - closes #101. Adds MatterBindingManager to serve the Binding cluster's list attribute and dispatch bound OnOff commands over CASE/group sessions, built entirely from SDK pieces already available (BindingTable, CASESessionManager, CommandSender) since the official bindings cluster server/BindingManager aren't vendored. Plain begin() and the existing matter_switch/matter_lightbulb examples are untouched; matter_switch_binding and matter_lightbulb_binding demonstrate the new opt-in path. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent e98487e commit 7b26c81

7 files changed

Lines changed: 723 additions & 4 deletions

File tree

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
Matter lightbulb example - Binding target
3+
4+
Companion example to matter_switch_binding: a plain on/off Matter lightbulb
5+
meant to be bound to a matter_switch_binding device so it can be toggled
6+
directly by the switch, without a hub relaying every button press.
7+
8+
This device itself needs no special code for binding - a Matter Binding
9+
only requires configuration on the initiating side (the switch's Binding
10+
cluster + logic in MatterSwitch::beginWithBinding()). This lightbulb is a
11+
plain OnOff server, identical to the matter_lightbulb example; it simply
12+
accepts On/Off/Toggle commands from whichever node is authorized to send
13+
them - which includes a bound switch once your Matter controller has:
14+
1. Commissioned this device AND a matter_switch_binding device onto the
15+
same Matter fabric.
16+
2. Created the binding from the switch's endpoint to this device's
17+
endpoint (see matter_switch_binding.ino for how).
18+
3. Granted the switch's node the "Operate" ACL privilege on this
19+
device's OnOff cluster - most controllers set this up automatically
20+
as part of creating the binding.
21+
22+
The device has to be commissioned to a Matter hub first.
23+
24+
Compatible boards:
25+
- Arduino Nano Matter
26+
- SparkFun Thing Plus MGM240P
27+
- xG24 Explorer Kit
28+
- xG24 Dev Kit
29+
- Seeed Studio XIAO MG24 (Sense)
30+
31+
Author: Silicon Labs
32+
*/
33+
#include <Matter.h>
34+
#include <MatterLightbulb.h>
35+
36+
MatterLightbulb matter_bulb;
37+
38+
void setup()
39+
{
40+
Serial.begin(115200);
41+
Matter.begin();
42+
matter_bulb.begin();
43+
44+
pinMode(LED_BUILTIN, OUTPUT);
45+
digitalWrite(LED_BUILTIN, LED_BUILTIN_INACTIVE);
46+
47+
Serial.println("Matter lightbulb - binding target");
48+
49+
if (!Matter.isDeviceCommissioned()) {
50+
Serial.println("Matter device is not commissioned");
51+
Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
52+
Serial.printf("Manual pairing code: %s\n", Matter.getManualPairingCode().c_str());
53+
Serial.printf("QR code URL: %s\n", Matter.getOnboardingQRCodeUrl().c_str());
54+
}
55+
while (!Matter.isDeviceCommissioned()) {
56+
delay(200);
57+
}
58+
59+
Serial.println("Waiting for Thread network...");
60+
while (!Matter.isDeviceThreadConnected()) {
61+
delay(200);
62+
}
63+
Serial.println("Connected to Thread network");
64+
65+
Serial.println("Waiting for Matter device discovery...");
66+
while (!matter_bulb.is_online()) {
67+
delay(200);
68+
}
69+
Serial.println("Matter device is now online");
70+
Serial.println("Bind a matter_switch_binding device to this endpoint's OnOff cluster from your Matter controller");
71+
}
72+
73+
void loop()
74+
{
75+
static bool matter_lightbulb_last_state = false;
76+
bool matter_lightbulb_current_state = matter_bulb.get_onoff();
77+
78+
// If the current state is ON and the previous was OFF - turn on the LED
79+
if (matter_lightbulb_current_state && !matter_lightbulb_last_state) {
80+
matter_lightbulb_last_state = matter_lightbulb_current_state;
81+
digitalWrite(LED_BUILTIN, LED_BUILTIN_ACTIVE);
82+
Serial.println("Bulb ON");
83+
}
84+
85+
// If the current state is OFF and the previous was ON - turn off the LED
86+
if (!matter_lightbulb_current_state && matter_lightbulb_last_state) {
87+
matter_lightbulb_last_state = matter_lightbulb_current_state;
88+
digitalWrite(LED_BUILTIN, LED_BUILTIN_INACTIVE);
89+
Serial.println("Bulb OFF");
90+
}
91+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
Matter switch with Binding cluster example
3+
4+
The example shows how to create a Matter momentary switch that uses the
5+
standard Matter Binding cluster to control a bound light directly, without
6+
a hub relaying every button press (see MatterSwitch::beginWithBinding()).
7+
8+
The device has to be commissioned to a Matter hub/controller first, same as
9+
the plain matter_switch example. Once commissioned:
10+
1. Commission this device AND a matter_lightbulb_binding device (the
11+
companion example - a plain lightbulb, no special code needed on its
12+
side) onto the same Matter fabric.
13+
2. Create a binding from this switch's endpoint to the light's endpoint.
14+
This isn't done from Home Assistant's own UI - use the underlying
15+
Matter Server's dashboard (matterjs-server, the component behind HA's
16+
Matter integration) to add the binding, or another controller with
17+
binding support such as chip-tool.
18+
3. Make sure the light grants this switch's node the "Operate" ACL
19+
privilege on its OnOff cluster - most controllers set this up
20+
automatically as part of creating the binding.
21+
22+
Once bound, pressing the on-board button toggles the bound light directly,
23+
switch-to-light, without going through the controller for that command.
24+
25+
Compatible boards:
26+
- Arduino Nano Matter
27+
- SparkFun Thing Plus MGM240P
28+
- xG24 Explorer Kit
29+
- xG24 Dev Kit
30+
- Seeed Studio XIAO MG24 (Sense)
31+
32+
Author: Silicon Labs
33+
*/
34+
#include <Matter.h>
35+
#include <MatterSwitch.h>
36+
37+
MatterSwitch matter_switch;
38+
39+
void handle_button_press();
40+
void handle_button_release();
41+
volatile bool button_pressed = false;
42+
43+
void setup()
44+
{
45+
Serial.begin(115200);
46+
Matter.begin();
47+
matter_switch.beginWithBinding();
48+
49+
pinMode(LED_BUILTIN, OUTPUT);
50+
digitalWrite(LED_BUILTIN, LED_BUILTIN_INACTIVE);
51+
52+
// Set up the onboard button
53+
#ifndef BTN_BUILTIN
54+
#define BTN_BUILTIN PA0
55+
#endif
56+
pinMode(BTN_BUILTIN, INPUT_PULLUP);
57+
attachInterrupt(BTN_BUILTIN, &handle_button_press, FALLING);
58+
attachInterrupt(BTN_BUILTIN, &handle_button_release, RISING);
59+
60+
Serial.println("Matter switch with binding");
61+
62+
if (!Matter.isDeviceCommissioned()) {
63+
Serial.println("Matter device is not commissioned");
64+
Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
65+
Serial.printf("Manual pairing code: %s\n", Matter.getManualPairingCode().c_str());
66+
Serial.printf("QR code URL: %s\n", Matter.getOnboardingQRCodeUrl().c_str());
67+
}
68+
while (!Matter.isDeviceCommissioned()) {
69+
delay(200);
70+
}
71+
72+
Serial.println("Waiting for Thread network...");
73+
while (!Matter.isDeviceThreadConnected()) {
74+
delay(200);
75+
}
76+
Serial.println("Connected to Thread network");
77+
78+
Serial.println("Waiting for Matter device discovery...");
79+
while (!matter_switch.is_online()) {
80+
delay(200);
81+
}
82+
Serial.println("Matter device is now online");
83+
Serial.println("Use your Matter controller to bind this switch to a light, then press the button to toggle it directly");
84+
}
85+
86+
void loop()
87+
{
88+
// If the physical button state changes - update the switch's state
89+
static bool button_pressed_last = false;
90+
if (button_pressed != button_pressed_last) {
91+
button_pressed_last = button_pressed;
92+
matter_switch.set_state(button_pressed);
93+
}
94+
95+
// Get the current state of the Matter switch
96+
static bool switch_last_state = false;
97+
bool switch_current_state = matter_switch.get_state();
98+
99+
// If the current state is 'pressed' and the previous was 'not pressed' - switch pressed
100+
if (switch_current_state && !switch_last_state) {
101+
switch_last_state = switch_current_state;
102+
digitalWrite(LED_BUILTIN, LED_BUILTIN_ACTIVE);
103+
Serial.println("Switch pressed - toggling bound light(s)");
104+
}
105+
106+
// If the current state is 'not pressed' and the previous was 'pressed' - switch released
107+
if (!switch_current_state && switch_last_state) {
108+
switch_last_state = switch_current_state;
109+
digitalWrite(LED_BUILTIN, LED_BUILTIN_INACTIVE);
110+
Serial.println("Switch released");
111+
}
112+
}
113+
114+
void handle_button_press()
115+
{
116+
static uint32_t btn_last_press = 0;
117+
if (millis() < btn_last_press + 200) {
118+
return;
119+
}
120+
btn_last_press = millis();
121+
button_pressed = true;
122+
}
123+
124+
void handle_button_release()
125+
{
126+
static uint32_t btn_last_press = 0;
127+
if (millis() < btn_last_press + 200) {
128+
return;
129+
}
130+
btn_last_press = millis();
131+
button_pressed = false;
132+
}

0 commit comments

Comments
 (0)