feat(matter): initial commit with arduino matter lib (#10467)

* feat(matter): initial commit with arduino matter lib

* feat(matter): add matter library to cmakelists.txt

* fix(matter): add correct guard for ci

* fix(matter): using correct ci requirements in ci.json

* fix(matter): using correct ci requirements in header files

* fix(matter): using correct ci requirements header and examples

* fix(typo): typo and commentaries

* fix(typo): typo and commentaries

* fix(typo): typo and commentaries

* fix(commentary): longer explanation

* feat(matter): api simplification with begin

* feat(matter): testing flashmode=qio in CI

* feat(matter): testing flashmode=qio in CI

* fix(matter): changes CI FQBN

* fix(matte): include all fqbn in ci.json using qio

* fix(matter): revert ci and guard changes

* fix(matter): typo and commentaties

* feat(matter): adds a light toggle switch button

* feat(matter): improved the button control

* feat(matter): using switch instead of if() for attibute change

* fix(matter): switch/case scope

* fix(matter): problems found after pressing reset

* feat(matter): improve example using preferences

* fix(pre-commit): Fix and apply pre-commit hooks

---------

Co-authored-by: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com>
This commit is contained in:
Rodrigo Garcia 2024-10-21 10:42:43 -03:00 committed by GitHub
parent f668557b54
commit c40444ab33
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 735 additions and 0 deletions

View file

@ -26,6 +26,7 @@ repos:
- id: trailing-whitespace - id: trailing-whitespace
args: [--markdown-linebreak-ext=md] args: [--markdown-linebreak-ext=md]
- id: pretty-format-json - id: pretty-format-json
stages: [manual]
args: [--autofix] args: [--autofix]
types_or: [json] types_or: [json]
exclude: | exclude: |

View file

@ -94,6 +94,7 @@ set(ARDUINO_ALL_LIBRARIES
HTTPUpdate HTTPUpdate
Insights Insights
LittleFS LittleFS
Matter
NetBIOS NetBIOS
Network Network
OpenThread OpenThread
@ -165,6 +166,10 @@ set(ARDUINO_LIBRARY_OpenThread_SRCS
libraries/OpenThread/src/OThreadCLI.cpp libraries/OpenThread/src/OThreadCLI.cpp
libraries/OpenThread/src/OThreadCLI_Util.cpp) libraries/OpenThread/src/OThreadCLI_Util.cpp)
set(ARDUINO_LIBRARY_Matter_SRCS
libraries/Matter/src/MatterOnOffLight.cpp
libraries/Matter/src/Matter.cpp)
set(ARDUINO_LIBRARY_PPP_SRCS set(ARDUINO_LIBRARY_PPP_SRCS
libraries/PPP/src/PPP.cpp libraries/PPP/src/PPP.cpp
libraries/PPP/src/ppp.c) libraries/PPP/src/ppp.c)

View file

@ -0,0 +1,65 @@
// Matter Manager
#include <Matter.h>
#include <WiFi.h>
// List of Matter Endpoints for this Node
// On/Off Light Endpoint
#include <MatterOnOffLight.h>
MatterOnOffLight OnOffLight;
// WiFi is manually set and started
const char *ssid = "your-ssid"; // Change this to your WiFi SSID
const char *password = "your-password"; // Change this to your WiFi password
void setup() {
Serial.begin(115200);
while (!Serial) {
delay(100);
}
// We start by connecting to a WiFi network
Serial.print("Connecting to ");
Serial.println(ssid);
// enable IPv6
WiFi.enableIPv6(true);
// Manually connect to WiFi
WiFi.begin(ssid, password);
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\r\nWiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
delay(500);
// Initialize at least one Matter EndPoint
OnOffLight.begin();
// Matter beginning - Last step, after all EndPoints are initialized
Matter.begin();
}
void loop() {
// Check Matter Commissioning state
if (!Matter.isDeviceCommissioned()) {
Serial.println("");
Serial.println("Matter Node is not commissioned yet.");
Serial.println("Initiate the device discovery in your Matter environment.");
Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str());
Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str());
// waits for Matter Light Commissioning.
while (!Matter.isDeviceCommissioned()) {
delay(5000);
Serial.println("Matter Fabric not commissioned yet. Waiting for commissioning.");
}
}
Serial.println("Matter Node is commissioned and connected to Wi-Fi.");
Serial.println("====> Decommissioning in 30 seconds. <====");
delay(30000);
Matter.decommission();
Serial.println("Matter Node is decommissioned. Commsssioning widget shall start over.");
}

View file

@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=huge_app",
"requires": [
"CONFIG_SOC_WIFI_SUPPORTED=y",
"CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y"
]
}

View file

@ -0,0 +1,94 @@
// Matter Manager
#include <Matter.h>
#include <WiFi.h>
// List of Matter Endpoints for this Node
// There will be 3 On/Off Light Endpoints in the same Node
#include <MatterOnOffLight.h>
MatterOnOffLight OnOffLight1;
MatterOnOffLight OnOffLight2;
MatterOnOffLight OnOffLight3;
// Matter Protocol Endpoint Callback for each Light Accessory
bool setLightOnOff1(bool state) {
Serial.printf("CB-Light1 changed state to: %s\r\n", state ? "ON" : "OFF");
return true;
}
bool setLightOnOff2(bool state) {
Serial.printf("CB-Light2 changed state to: %s\r\n", state ? "ON" : "OFF");
return true;
}
bool setLightOnOff3(bool state) {
Serial.printf("CB-Light3 changed state to: %s\r\n", state ? "ON" : "OFF");
return true;
}
// WiFi is manually set and started
const char *ssid = "your-ssid"; // Change this to your WiFi SSID
const char *password = "your-password"; // Change this to your WiFi password
void setup() {
Serial.begin(115200);
while (!Serial) {
delay(100);
}
// We start by connecting to a WiFi network
Serial.print("Connecting to ");
Serial.println(ssid);
// enable IPv6
WiFi.enableIPv6(true);
// Manually connect to WiFi
WiFi.begin(ssid, password);
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\r\nWiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
delay(500);
// Initialize all 3 Matter EndPoints
OnOffLight1.begin();
OnOffLight2.begin();
OnOffLight3.begin();
OnOffLight1.onChangeOnOff(setLightOnOff1);
OnOffLight2.onChangeOnOff(setLightOnOff2);
OnOffLight3.onChangeOnOff(setLightOnOff3);
// Matter beginning - Last step, after all EndPoints are initialized
Matter.begin();
}
void loop() {
// Check Matter Light Commissioning state
if (!Matter.isDeviceCommissioned()) {
Serial.println("");
Serial.println("Matter Node is not commissioned yet.");
Serial.println("Initiate the device discovery in your Matter environment.");
Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str());
Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str());
// waits for Matter Light Commissioning.
uint32_t timeCount = 0;
while (!Matter.isDeviceCommissioned()) {
delay(100);
if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec
Serial.println("Matter Node not commissioned yet. Waiting for commissioning.");
}
}
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
}
//displays the Light state every 3 seconds
Serial.println("======================");
Serial.printf("Matter Light #1 is %s\r\n", OnOffLight1.getOnOff() ? "ON" : "OFF");
Serial.printf("Matter Light #2 is %s\r\n", OnOffLight2.getOnOff() ? "ON" : "OFF");
Serial.printf("Matter Light #3 is %s\r\n", OnOffLight3.getOnOff() ? "ON" : "OFF");
delay(3000);
}

View file

@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=huge_app",
"requires": [
"CONFIG_SOC_WIFI_SUPPORTED=y",
"CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y"
]
}

View file

@ -0,0 +1,138 @@
// Matter Manager
#include <Matter.h>
#include <WiFi.h>
#include <Preferences.h>
// List of Matter Endpoints for this Node
// On/Off Light Endpoint
#include <MatterOnOffLight.h>
MatterOnOffLight OnOffLight;
// it will keep last OnOff state stored, using Preferences
Preferences lastStatePref;
// set your board LED pin here
#ifdef LED_BUILTIN
const uint8_t ledPin = LED_BUILTIN;
#else
const uint8_t ledPin = 2; // Set your pin here if your board has not defined LED_BUILTIN
#warning "Do not forget to set the LED pin"
#endif
// set your board USER BUTTON pin here
const uint8_t buttonPin = 0; // Set your pin here. Using BOOT Button. C6/C3 use GPIO9.
// Matter Protocol Endpoint Callback
bool setLightOnOff(bool state) {
Serial.printf("User Callback :: New Light State = %s\r\n", state ? "ON" : "OFF");
if (state) {
digitalWrite(ledPin, HIGH);
} else {
digitalWrite(ledPin, LOW);
}
// store last OnOff state for when the Light is restarted / power goes off
lastStatePref.putBool("lastOnOffState", state);
// This callback must return the success state to Matter core
return true;
}
// WiFi is manually set and started
const char *ssid = "Apartment B15"; // Change this to your WiFi SSID
const char *password = "flat-pony-body"; // Change this to your WiFi password
void setup() {
// Initialize the USER BUTTON (Boot button) GPIO that will act as a toggle switch
pinMode(buttonPin, INPUT_PULLUP);
// Initialize the LED (light) GPIO and Matter End Point
pinMode(ledPin, OUTPUT);
Serial.begin(115200);
while (!Serial) {
delay(100);
}
// We start by connecting to a WiFi network
Serial.print("Connecting to ");
Serial.println(ssid);
// enable IPv6
WiFi.enableIPv6(true);
// Manually connect to WiFi
WiFi.begin(ssid, password);
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\r\nWiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
delay(500);
// Initialize Matter EndPoint
lastStatePref.begin("matterLight", false);
bool lastOnOffState = lastStatePref.getBool("lastOnOffState", true);
OnOffLight.begin(lastOnOffState);
OnOffLight.onChangeOnOff(setLightOnOff);
// Matter beginning - Last step, after all EndPoints are initialized
Matter.begin();
// This may be a restart of a already commissioned Matter accessory
if (Matter.isDeviceCommissioned()) {
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
Serial.printf("Initial state: %s\r\n", OnOffLight.getOnOff() ? "ON" : "OFF");
setLightOnOff(OnOffLight.getOnOff()); // configure the Light based on initial state
}
}
// Button control
uint32_t button_time_stamp = 0; // debouncing control
bool button_state = false; // false = released | true = pressed
const uint32_t debouceTime = 250; // button debouncing time (ms)
const uint32_t decommissioningTimeout = 10000; // keep the button pressed for 10s to decommission the light
void loop() {
// Check Matter Light Commissioning state, which may change during execution of loop()
if (!Matter.isDeviceCommissioned()) {
Serial.println("");
Serial.println("Matter Node is not commissioned yet.");
Serial.println("Initiate the device discovery in your Matter environment.");
Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str());
Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str());
// waits for Matter Light Commissioning.
uint32_t timeCount = 0;
while (!Matter.isDeviceCommissioned()) {
delay(100);
if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec
Serial.println("Matter Node not commissioned yet. Waiting for commissioning.");
}
}
Serial.printf("Initial state: %s\r\n", OnOffLight.getOnOff() ? "ON" : "OFF");
setLightOnOff(OnOffLight.getOnOff()); // configure the Light based on initial state
Serial.println("Matter Node is commissioned and connected to Wi-Fi. Ready for use.");
}
// A button is also used to control the light
// Check if the button has been pressed
if (digitalRead(buttonPin) == LOW && !button_state) {
// deals with button debouncing
button_time_stamp = millis(); // record the time while the button is pressed.
button_state = true; // pressed.
}
// Onboard User Button is used as a Light toggle switch or to decommission it
uint32_t time_diff = millis() - button_time_stamp;
if (button_state && time_diff > debouceTime && digitalRead(buttonPin) == HIGH) {
button_state = false; // released
// Toggle button is released - toggle the light
Serial.println("User button released. Toggling Light!");
OnOffLight.toggle(); // Matter Controller also can see the change
// Factory reset is triggered if the button is pressed longer than 10 seconds
if (time_diff > decommissioningTimeout) {
Serial.println("Decommissioning the Light Matter Accessory. It shall be commissioned again.");
OnOffLight.setOnOff(false); // turn the light off
Matter.decommission();
}
}
}

View file

@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=huge_app",
"requires": [
"CONFIG_SOC_WIFI_SUPPORTED=y",
"CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y"
]
}

View file

@ -0,0 +1,35 @@
#######################################
# Syntax Coloring Map For OpenThread
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
Matter KEYWORD1
MatterOnOffLight KEYWORD1
MatterEndPoint KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
begin KEYWORD2
end KEYWORD2
start KEYWORD2
getManualPairingCode KEYWORD2
getOnboardingQRCodeUrl KEYWORD2
isDeviceCommissioned KEYWORD2
isWiFiConnected KEYWORD2
isThreadConnected KEYWORD2
isDeviceConnected KEYWORD2
decommission KEYWORD2
attributeChangeCB KEYWORD2
setOnOff KEYWORD2
getOnOff KEYWORD2
toggle KEYWORD2
onChangeOnOff KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View file

@ -0,0 +1,9 @@
name=Matter
version=3.1.0
author=Rodrigo Garcia | GitHub @SuGlider
maintainer=Rodrigo Garcia <Rodrigo.Garcia@espressif.com>
sentence=Library for supporting Matter environment on ESP32.
paragraph=This library implements Matter accessories using WiFi network.
category=Communication
url=https://github.com/espressif/arduino-esp32/
architectures=esp32

View file

@ -0,0 +1,163 @@
#include <sdkconfig.h>
#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL
#include <Matter.h>
#include <app/server/Server.h>
#include "MatterEndPoint.h"
using namespace esp_matter;
using namespace esp_matter::attribute;
using namespace esp_matter::endpoint;
using namespace chip::app::Clusters;
constexpr auto k_timeout_seconds = 300;
static bool _matter_has_started = false;
static node::config_t node_config;
static node_t *deviceNode = NULL;
typedef void *app_driver_handle_t;
esp_err_t matter_light_attribute_update(
app_driver_handle_t driver_handle, uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val
);
// This callback is called for every attribute update. The callback implementation shall
// handle the desired attributes and return an appropriate error code. If the attribute
// is not of your interest, please do not return an error code and strictly return ESP_OK.
static esp_err_t app_attribute_update_cb(
attribute::callback_type_t type, uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val, void *priv_data
) {
esp_err_t err = ESP_OK;
MatterEndPoint *ep = (MatterEndPoint *)priv_data; // endpoint pointer to base class
switch (type) {
case PRE_UPDATE: // Callback before updating the value in the database
log_i("Attribute update callback: PRE_UPDATE");
if (ep != NULL) {
err = ep->attributeChangeCB(endpoint_id, cluster_id, attribute_id, val) ? ESP_OK : ESP_FAIL;
}
break;
case POST_UPDATE: // Callback after updating the value in the database
log_i("Attribute update callback: POST_UPDATE");
break;
case READ: // Callback for reading the attribute value. This is used when the `ATTRIBUTE_FLAG_OVERRIDE` is set.
log_i("Attribute update callback: READ");
break;
case WRITE: // Callback for writing the attribute value. This is used when the `ATTRIBUTE_FLAG_OVERRIDE` is set.
log_i("Attribute update callback: WRITE");
break;
default: log_i("Attribute update callback: Unknown type %d", type);
}
return err;
}
// This callback is invoked when clients interact with the Identify Cluster.
// In the callback implementation, an endpoint can identify itself. (e.g., by flashing an LED or light).
static esp_err_t app_identification_cb(identification::callback_type_t type, uint16_t endpoint_id, uint8_t effect_id, uint8_t effect_variant, void *priv_data) {
log_i("Identification callback: type: %u, effect: %u, variant: %u", type, effect_id, effect_variant);
return ESP_OK;
}
// This callback is invoked for all Matter events. The application can handle the events as required.
static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg) {
switch (event->Type) {
case chip::DeviceLayer::DeviceEventType::kInterfaceIpAddressChanged:
log_i(
"Interface %s Address changed", event->InterfaceIpAddressChanged.Type == chip::DeviceLayer::InterfaceIpChangeType::kIpV4_Assigned ? "IPv4" : "IPV6"
);
break;
case chip::DeviceLayer::DeviceEventType::kCommissioningComplete: log_i("Commissioning complete"); break;
case chip::DeviceLayer::DeviceEventType::kFailSafeTimerExpired: log_i("Commissioning failed, fail safe timer expired"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStarted: log_i("Commissioning session started"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStopped: log_i("Commissioning session stopped"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowOpened: log_i("Commissioning window opened"); break;
case chip::DeviceLayer::DeviceEventType::kCommissioningWindowClosed: log_i("Commissioning window closed"); break;
case chip::DeviceLayer::DeviceEventType::kFabricRemoved:
{
log_i("Fabric removed successfully");
if (chip::Server::GetInstance().GetFabricTable().FabricCount() == 0) {
log_i("No fabric left, opening commissioning window");
chip::CommissioningWindowManager &commissionMgr = chip::Server::GetInstance().GetCommissioningWindowManager();
constexpr auto kTimeoutSeconds = chip::System::Clock::Seconds16(k_timeout_seconds);
if (!commissionMgr.IsCommissioningWindowOpen()) {
// After removing last fabric, it does not remove the Wi-Fi credentials and still has IP connectivity so, only advertising on DNS-SD.
CHIP_ERROR err = commissionMgr.OpenBasicCommissioningWindow(kTimeoutSeconds, chip::CommissioningWindowAdvertisement::kDnssdOnly);
if (err != CHIP_NO_ERROR) {
log_e("Failed to open commissioning window, err:%" CHIP_ERROR_FORMAT, err.Format());
}
}
}
break;
}
case chip::DeviceLayer::DeviceEventType::kFabricWillBeRemoved: log_i("Fabric will be removed"); break;
case chip::DeviceLayer::DeviceEventType::kFabricUpdated: log_i("Fabric is updated"); break;
case chip::DeviceLayer::DeviceEventType::kFabricCommitted: log_i("Fabric is committed"); break;
case chip::DeviceLayer::DeviceEventType::kBLEDeinitialized: log_i("BLE deinitialized and memory reclaimed"); break;
default: break;
}
}
void ArduinoMatter::_init() {
if (_matter_has_started) {
return;
}
// Create a Matter node and add the mandatory Root Node device type on endpoint 0
// node handle can be used to add/modify other endpoints.
deviceNode = node::create(&node_config, app_attribute_update_cb, app_identification_cb);
if (deviceNode == nullptr) {
log_e("Failed to create Matter node");
return;
}
_matter_has_started = true;
}
void ArduinoMatter::begin() {
if (!_matter_has_started) {
log_w("No Matter endpoint has been created. Please create an endpoint first.");
return;
}
/* Matter start */
esp_err_t err = esp_matter::start(app_event_cb);
if (err != ESP_OK) {
log_e("Failed to start Matter, err:%d", err);
_matter_has_started = false;
}
}
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
bool ArduinoMatter::isThreadConnected() {
return false; // Thread Network TBD
}
#endif
bool ArduinoMatter::isDeviceCommissioned() {
return chip::Server::GetInstance().GetFabricTable().FabricCount() > 0;
}
#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION
bool ArduinoMatter::isWiFiConnected() {
return chip::DeviceLayer::ConnectivityMgr().IsWiFiStationConnected();
}
#endif
bool ArduinoMatter::isDeviceConnected() {
bool retCode = false;
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
retCode |= ArduinoMatter::isThreadConnected();
#endif
#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION
retCode |= ArduinoMatter::isWiFiConnected();
#endif
return retCode;
}
void ArduinoMatter::decommission() {
esp_matter::factory_reset();
}
// Global Matter Object
ArduinoMatter Matter;
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */

View file

@ -0,0 +1,40 @@
#pragma once
#include <sdkconfig.h>
#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL
#include <Arduino.h>
#include <esp_matter.h>
using namespace esp_matter;
class ArduinoMatter {
public:
static inline String getManualPairingCode() {
// return the pairing code for manual pairing
return String("34970112332");
}
static inline String getOnboardingQRCodeUrl() {
// return the URL for the QR code for onboarding
return String("https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT:Y.K9042C00KA0648G00");
}
static void begin();
static bool isDeviceCommissioned();
#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION
static bool isWiFiConnected();
#endif
#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
static bool isThreadConnected();
#endif
static bool isDeviceConnected();
static void decommission();
// list of Matter EndPoints Friend Classes
friend class MatterOnOffLight;
protected:
static void _init();
};
extern ArduinoMatter Matter;
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */

View file

@ -0,0 +1,23 @@
#pragma once
#include <sdkconfig.h>
#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL
#include <Matter.h>
#include <functional>
// Matter Endpoint Base Class. Controls the endpoint ID and allows the child class to overwrite attribute change call
class MatterEndPoint {
public:
uint16_t getEndPointId() {
return endpoint_id;
}
void setEndPointId(uint16_t ep) {
endpoint_id = ep;
}
virtual bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) = 0;
protected:
uint16_t endpoint_id = 0;
};
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */

View file

@ -0,0 +1,107 @@
#include <sdkconfig.h>
#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL
#include <Matter.h>
#include <app/server/Server.h>
#include <MatterOnOffLight.h>
using namespace esp_matter;
using namespace esp_matter::endpoint;
using namespace chip::app::Clusters;
bool MatterOnOffLight::attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) {
bool ret = true;
if (!started) {
log_w("Matter On-Off Light device has not begun.");
return false;
}
if (endpoint_id == getEndPointId()) {
if (cluster_id == OnOff::Id) {
if (attribute_id == OnOff::Attributes::OnOff::Id) {
if (_onChangeCB != NULL) {
ret = _onChangeCB(val->val.b);
log_d("OnOffLight state changed to %d", val->val.b);
if (ret == true) {
state = val->val.b;
}
}
}
}
}
return ret;
}
MatterOnOffLight::MatterOnOffLight() {}
MatterOnOffLight::~MatterOnOffLight() {
end();
}
bool MatterOnOffLight::begin(bool initialState) {
ArduinoMatter::_init();
on_off_light::config_t light_config;
light_config.on_off.on_off = initialState;
state = initialState;
light_config.on_off.lighting.start_up_on_off = nullptr;
// endpoint handles can be used to add/modify clusters.
endpoint_t *endpoint = on_off_light::create(node::get(), &light_config, ENDPOINT_FLAG_NONE, (void *)this);
if (endpoint == nullptr) {
log_e("Failed to create on-off light endpoint");
return false;
}
setEndPointId(endpoint::get_id(endpoint));
log_i("On-Off Light created with endpoint_id %d", getEndPointId());
started = true;
return true;
}
void MatterOnOffLight::end() {
started = false;
}
bool MatterOnOffLight::setOnOff(bool newState) {
if (!started) {
log_w("Matter On-Off Light device has not begun.");
return false;
}
// avoid processing the a "no-change"
if (state == newState) {
return true;
}
state = newState;
endpoint_t *endpoint = endpoint::get(node::get(), endpoint_id);
cluster_t *cluster = cluster::get(endpoint, OnOff::Id);
attribute_t *attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id);
esp_matter_attr_val_t val = esp_matter_invalid(NULL);
attribute::get_val(attribute, &val);
if (val.val.b != state) {
val.val.b = state;
attribute::update(endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id, &val);
}
return true;
}
bool MatterOnOffLight::getOnOff() {
return state;
}
bool MatterOnOffLight::toggle() {
return setOnOff(!state);
}
MatterOnOffLight::operator bool() {
return getOnOff();
}
void MatterOnOffLight::operator=(bool newState) {
setOnOff(newState);
}
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */

View file

@ -0,0 +1,34 @@
#pragma once
#include <sdkconfig.h>
#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL
#include <Matter.h>
#include <MatterEndPoint.h>
class MatterOnOffLight : public MatterEndPoint {
public:
MatterOnOffLight();
~MatterOnOffLight();
virtual bool begin(bool initialState = false); // default initial state is off
void end(); // this will just stop processing Light Matter events
bool setOnOff(bool newState); // returns true if successful
bool getOnOff(); // returns current light state
bool toggle(); // returns true if successful
operator bool(); // returns current light state
void operator=(bool state); // turns light on or off
// this function is called by Matter internal event processor. It could be overwritten by the application, if necessary.
bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val);
// User Callback for whenever the Light state is changed by the Matter Controller
using EndPointCB = std::function<bool(bool)>;
void onChangeOnOff(EndPointCB onChangeCB) {
_onChangeCB = onChangeCB;
}
protected:
bool started = false;
bool state = false; // default initial state is off, but it can be changed by begin(bool)
EndPointCB _onChangeCB = NULL;
};
#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */