feat(zigbee): Remove static variables, improve binding, new example (#11316)
* feat(zigbee): Remove static variables, improve binding, new example * feat(example): Add missing header * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
This commit is contained in:
parent
9090b46da5
commit
542274d5ea
13 changed files with 863 additions and 103 deletions
110
libraries/Zigbee/examples/Zigbee_On_Off_MultiSwitch/README.md
Normal file
110
libraries/Zigbee/examples/Zigbee_On_Off_MultiSwitch/README.md
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
# Arduino-ESP32 Zigbee Multi-Switch Example
|
||||
|
||||
This example demonstrates how to configure a Zigbee device as a multi-switch controller that can control up to three different Zigbee lights independently. The switch can operate in either coordinator or router mode, making it compatible with Home Assistant integration.
|
||||
|
||||
# Supported Targets
|
||||
|
||||
Currently, this example supports the following targets.
|
||||
|
||||
| Supported Targets | ESP32-C6 | ESP32-H2 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
||||
## Hardware Required
|
||||
|
||||
* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee multi-switch controller
|
||||
* One or more Zigbee light devices (loaded with Zigbee_On_Off_Light example)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the Project
|
||||
|
||||
The example uses the BOOT button (pin 9) on ESP32-C6 and ESP32-H2 as the physical switch input. The switch can be configured to operate in two modes:
|
||||
|
||||
1. **Coordinator Mode**: For running your own Zigbee network
|
||||
2. **Router Mode**: For Home Assistant integration
|
||||
|
||||
#### Using Arduino IDE
|
||||
|
||||
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
|
||||
|
||||
* Before Compile/Verify, select the correct board: `Tools -> Board`
|
||||
* Select the Zigbee mode: `Tools -> Zigbee mode: Zigbee ZCZR (coordinator/router)`
|
||||
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
|
||||
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port
|
||||
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`
|
||||
|
||||
## Features
|
||||
|
||||
The multi-switch example provides the following functionality:
|
||||
|
||||
1. **Light Configuration**
|
||||
- Configure up to 3 different lights using their endpoint and IEEE address
|
||||
- Configuration is stored in NVS (Non-Volatile Storage) and persists after power loss
|
||||
- Remove configured lights when needed
|
||||
|
||||
2. **Control Commands**
|
||||
- Control all bound lights simultaneously:
|
||||
- Turn all bound lights ON
|
||||
- Turn all bound lights OFF
|
||||
- Toggle all bound lights
|
||||
- Control individual lights (1-3):
|
||||
- Turn light ON
|
||||
- Turn light OFF
|
||||
- Toggle light
|
||||
|
||||
3. **Network Management**
|
||||
- Factory reset capability
|
||||
- Open network for device joining
|
||||
- View bound devices and current light configurations
|
||||
|
||||
## Serial Commands
|
||||
|
||||
The example accepts the following commands through the serial interface:
|
||||
|
||||
* `config` - Configure a new light (requires light number, endpoint, and IEEE address)
|
||||
* `remove` - Remove a configured light
|
||||
* `on` - Turn all bound lights ON
|
||||
* `off` - Turn all bound lights OFF
|
||||
* `toggle` - Toggle all bound lights
|
||||
* `1on`, `2on`, `3on` - Turn individual light ON
|
||||
* `1off`, `2off`, `3off` - Turn individual light OFF
|
||||
* `1toggle`, `2toggle`, `3toggle` - Toggle individual light
|
||||
* `freset` - Perform factory reset
|
||||
* `open_network` - Open network for device joining (only for coordinator role)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the End device flashed with the example `Zigbee_On_Off_Light` is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
|
||||
You can do the following:
|
||||
|
||||
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`
|
||||
* In the `Zigbee_On_Off_Light` example sketch call `Zigbee.factoryReset()`
|
||||
|
||||
By default, the coordinator network is closed after rebooting or flashing new firmware.
|
||||
To open the network you have 2 options:
|
||||
|
||||
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time)` before calling `Zigbee.begin()`
|
||||
* In application you can anytime call `Zigbee.openNetwork(time)` to open the network for devices to join
|
||||
|
||||
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
|
||||
|
||||
* **LED not blinking:** Check the wiring connection and the IO selection
|
||||
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed
|
||||
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation
|
||||
|
||||
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
|
||||
|
||||
## Contribute
|
||||
|
||||
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
|
||||
|
||||
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
|
||||
|
||||
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
|
||||
|
||||
## Resources
|
||||
|
||||
* Official ESP32 Forum: [Link](https://esp32.com)
|
||||
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
|
||||
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
|
||||
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
|
||||
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
|
||||
|
|
@ -0,0 +1,274 @@
|
|||
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @brief This example demonstrates simple Zigbee multi-light switch.
|
||||
*
|
||||
* The example demonstrates how to use Zigbee library to control multiple light bulbs.
|
||||
* The light bulbs are Zigbee devices, which are controlled by a Zigbee coordinator/router (Multi-Switch).
|
||||
* Settings are stored in NVS to not be lost after power loss.
|
||||
* Configuring and controlling the lights is done via serial input.
|
||||
*
|
||||
* Proper Zigbee mode must be selected in Tools->Zigbee mode
|
||||
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
|
||||
*
|
||||
* Please check the README.md for instructions and more detailed description.
|
||||
*
|
||||
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
|
||||
*/
|
||||
|
||||
#ifndef ZIGBEE_MODE_ZCZR
|
||||
#error "Zigbee coordinator mode is not selected in Tools->Zigbee mode"
|
||||
#endif
|
||||
|
||||
#include "Zigbee.h"
|
||||
#include <Preferences.h>
|
||||
|
||||
#define ZIGBEE_ROLE ZIGBEE_ROUTER // ZIGBEE_ROUTER for HomeAssistant integration, ZIGBEE_COORDINATOR for running own network
|
||||
|
||||
/* Zigbee switch configuration */
|
||||
#define SWITCH_ENDPOINT_NUMBER 1
|
||||
|
||||
uint8_t button = BOOT_PIN;
|
||||
|
||||
ZigbeeSwitch zbSwitch = ZigbeeSwitch(SWITCH_ENDPOINT_NUMBER);
|
||||
|
||||
int buttonState;
|
||||
int lastButtonState = LOW;
|
||||
unsigned long lastDebounceTime = 0; // the last time the output pin was toggled
|
||||
unsigned long debounceDelay = 50; // the debounce time; increase if the output flickers
|
||||
|
||||
// To be stored in NVS to not be lost after power loss
|
||||
Preferences prefs;
|
||||
|
||||
zb_device_params_t light_1;
|
||||
zb_device_params_t light_2;
|
||||
zb_device_params_t light_3;
|
||||
|
||||
void storeLightParams(zb_device_params_t *light, int light_number) {
|
||||
char key[10];
|
||||
snprintf(key, sizeof(key), "light_%d", light_number);
|
||||
prefs.putBytes(key, light, sizeof(zb_device_params_t));
|
||||
}
|
||||
|
||||
void loadLightParams(zb_device_params_t *light, int light_number) {
|
||||
char key[10];
|
||||
snprintf(key, sizeof(key), "light_%d", light_number);
|
||||
prefs.getBytes(key, light, sizeof(zb_device_params_t));
|
||||
}
|
||||
|
||||
/********************* Arduino functions **************************/
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Initialize Preferences
|
||||
prefs.begin("lights", false); // false means read/write mode
|
||||
|
||||
// Load saved light parameters
|
||||
loadLightParams(&light_1, 1);
|
||||
loadLightParams(&light_2, 2);
|
||||
loadLightParams(&light_3, 3);
|
||||
|
||||
// Init button switch
|
||||
pinMode(button, INPUT_PULLUP);
|
||||
|
||||
// Set Zigbee device name and model
|
||||
zbSwitch.setManufacturerAndModel("Espressif", "ZBMultiSwitch");
|
||||
|
||||
// Set binding settings depending on the role
|
||||
if (ZIGBEE_ROLE == ZIGBEE_COORDINATOR) {
|
||||
zbSwitch.allowMultipleBinding(true); // To allow binding multiple lights to the switch
|
||||
} else {
|
||||
zbSwitch.setManualBinding(true); //Set manual binding to true, so binding is done on Home Assistant side
|
||||
}
|
||||
|
||||
// Add endpoint to Zigbee Core
|
||||
Serial.println("Adding ZigbeeSwitch endpoint to Zigbee Core");
|
||||
Zigbee.addEndpoint(&zbSwitch);
|
||||
|
||||
// When all EPs are registered, start Zigbee with given role
|
||||
if (!Zigbee.begin(ZIGBEE_ROLE)) {
|
||||
Serial.println("Zigbee failed to start!");
|
||||
Serial.println("Rebooting...");
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
Serial.println("Connecting to network");
|
||||
while (!Zigbee.connected()) {
|
||||
Serial.print(".");
|
||||
delay(100);
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Handle button switch in loop()
|
||||
if (digitalRead(button) == LOW) { // Push button pressed
|
||||
// Key debounce handling
|
||||
while (digitalRead(button) == LOW) {
|
||||
delay(50);
|
||||
}
|
||||
// Print bound devices
|
||||
Serial.println("Bound devices:");
|
||||
zbSwitch.printBoundDevices(Serial);
|
||||
Serial.println("Lights configured:");
|
||||
Serial.printf("Light 1: %d %s\n", light_1.endpoint, Zigbee.formatIEEEAddress(light_1.ieee_addr));
|
||||
Serial.printf("Light 2: %d %s\n", light_2.endpoint, Zigbee.formatIEEEAddress(light_2.ieee_addr));
|
||||
Serial.printf("Light 3: %d %s\n", light_3.endpoint, Zigbee.formatIEEEAddress(light_3.ieee_addr));
|
||||
}
|
||||
// Handle serial input to configure and control the lights
|
||||
if (Serial.available()) {
|
||||
String command = Serial.readString();
|
||||
Serial.println("Command: " + command);
|
||||
|
||||
if (command == "config") {
|
||||
//wait for light number, endpoint and ieee address
|
||||
Serial.println("Enter light number (1-3):");
|
||||
while (!Serial.available()) {
|
||||
delay(100);
|
||||
}
|
||||
int light_number = Serial.parseInt();
|
||||
Serial.println("Enter endpoint:");
|
||||
while (!Serial.available()) {
|
||||
delay(100);
|
||||
}
|
||||
int endpoint = Serial.parseInt();
|
||||
Serial.println("Enter ieee address:");
|
||||
while (!Serial.available()) {
|
||||
delay(100);
|
||||
}
|
||||
String ieee_address = Serial.readStringUntil('\n');
|
||||
ieee_address.trim();
|
||||
//convert ieee address to uint8_t array (format in string is 00:00:00:00:00:00:00:00)
|
||||
uint8_t ieee_address_array[8];
|
||||
int index = 0;
|
||||
bool valid = true;
|
||||
|
||||
// Check if the string has the correct format (8 hex pairs with colons)
|
||||
if (ieee_address.length() != 23) { // 8 pairs * 2 + 7 colons
|
||||
Serial.println("Invalid IEEE address format. Expected format: 00:00:00:00:00:00:00:00");
|
||||
valid = false;
|
||||
} else {
|
||||
for (int i = 0; i < ieee_address.length() && index < 8 && valid; i += 3) {
|
||||
// Check for colon at expected positions
|
||||
if (i > 0 && ieee_address.charAt(i - 1) != ':') {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
// Convert two hex characters to a byte
|
||||
char hex[3] = {ieee_address.charAt(i), ieee_address.charAt(i + 1), '\0'};
|
||||
char *endptr;
|
||||
long value = strtol(hex, &endptr, 16);
|
||||
if (*endptr != '\0' || value < 0 || value > 255) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
// Store bytes in reverse order to match Zigbee standard
|
||||
ieee_address_array[7 - index++] = (uint8_t)value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid || index != 8) {
|
||||
Serial.println("Invalid IEEE address. Please enter a valid address in format: 00:00:00:00:00:00:00:00");
|
||||
return;
|
||||
}
|
||||
//set the light parameters
|
||||
if (light_number == 1) {
|
||||
light_1.endpoint = endpoint;
|
||||
memcpy(light_1.ieee_addr, ieee_address_array, 8);
|
||||
storeLightParams(&light_1, 1);
|
||||
} else if (light_number == 2) {
|
||||
light_2.endpoint = endpoint;
|
||||
memcpy(light_2.ieee_addr, ieee_address_array, 8);
|
||||
storeLightParams(&light_2, 2);
|
||||
} else if (light_number == 3) {
|
||||
light_3.endpoint = endpoint;
|
||||
memcpy(light_3.ieee_addr, ieee_address_array, 8);
|
||||
storeLightParams(&light_3, 3);
|
||||
}
|
||||
Serial.printf("Light %d configured\n", light_number);
|
||||
} else if (command == "remove") {
|
||||
//wait for light number
|
||||
Serial.println("Enter light number (1-3):");
|
||||
while (!Serial.available()) {
|
||||
delay(100);
|
||||
}
|
||||
int light_number = Serial.parseInt();
|
||||
uint8_t ieee_address_empty[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
if (light_number == 1) {
|
||||
light_1.endpoint = 0;
|
||||
memcpy(light_1.ieee_addr, ieee_address_empty, 8);
|
||||
storeLightParams(&light_1, 1);
|
||||
} else if (light_number == 2) {
|
||||
light_2.endpoint = 0;
|
||||
memcpy(light_2.ieee_addr, ieee_address_empty, 8);
|
||||
storeLightParams(&light_2, 2);
|
||||
} else if (light_number == 3) {
|
||||
light_3.endpoint = 0;
|
||||
memcpy(light_3.ieee_addr, ieee_address_empty, 8);
|
||||
storeLightParams(&light_3, 3);
|
||||
}
|
||||
Serial.printf("Light %d removed\n", light_number);
|
||||
} else if (command == "on") {
|
||||
Serial.println(" --> SIG Input : All Lights ON");
|
||||
zbSwitch.lightOn();
|
||||
} else if (command == "off") {
|
||||
Serial.println(" --> SIG Input : All Lights OFF");
|
||||
zbSwitch.lightOff();
|
||||
} else if (command == "toggle") {
|
||||
Serial.println(" --> SIG Input : All Lights Toggle");
|
||||
zbSwitch.lightToggle();
|
||||
} else if (command == "1on") {
|
||||
Serial.println(" --> SIG Input : Light 1 ON");
|
||||
zbSwitch.lightOn(light_1.endpoint, light_1.ieee_addr);
|
||||
} else if (command == "1off") {
|
||||
Serial.println(" --> SIG Input : Light 1 OFF");
|
||||
zbSwitch.lightOff(light_1.endpoint, light_1.ieee_addr);
|
||||
} else if (command == "1toggle") {
|
||||
Serial.println(" --> SIG Input : Light 1 Toggle");
|
||||
zbSwitch.lightToggle(light_1.endpoint, light_1.ieee_addr);
|
||||
} else if (command == "2on") {
|
||||
Serial.println(" --> SIG Input : Light 2 ON");
|
||||
zbSwitch.lightOn(light_2.endpoint, light_2.ieee_addr);
|
||||
} else if (command == "2off") {
|
||||
Serial.println(" --> SIG Input : Light 2 OFF");
|
||||
zbSwitch.lightOff(light_2.endpoint, light_2.ieee_addr);
|
||||
} else if (command == "2toggle") {
|
||||
Serial.println(" --> SIG Input : Light 2 Toggle");
|
||||
zbSwitch.lightToggle(light_2.endpoint, light_2.ieee_addr);
|
||||
} else if (command == "3on") {
|
||||
Serial.println(" --> SIG Input : Light 3 ON");
|
||||
zbSwitch.lightOn(light_3.endpoint, light_3.ieee_addr);
|
||||
} else if (command == "3off") {
|
||||
Serial.println(" --> SIG Input : Light 3 OFF");
|
||||
zbSwitch.lightOff(light_3.endpoint, light_3.ieee_addr);
|
||||
} else if (command == "3toggle") {
|
||||
Serial.println(" --> SIG Input : Light 3 Toggle");
|
||||
zbSwitch.lightToggle(light_3.endpoint, light_3.ieee_addr);
|
||||
} else if (command == "freset") {
|
||||
Serial.println(" --> SIG Input : Factory Reset!");
|
||||
delay(1500);
|
||||
Zigbee.factoryReset();
|
||||
} else if (command == "open_network") {
|
||||
Serial.println(" --> SIG Input : Open Network");
|
||||
if (ZIGBEE_ROLE == ZIGBEE_COORDINATOR) {
|
||||
Zigbee.openNetwork(180);
|
||||
} else {
|
||||
Serial.println("Open network is only available for coordinator role");
|
||||
}
|
||||
} else {
|
||||
Serial.println("Unknown command");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr",
|
||||
"requires": [
|
||||
"CONFIG_SOC_IEEE802154_SUPPORTED=y"
|
||||
]
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "ZigbeeHandlers.cpp"
|
||||
#include "Arduino.h"
|
||||
#include <set>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
@ -30,6 +31,7 @@ ZigbeeCore::ZigbeeCore() {
|
|||
_connected = false;
|
||||
_scan_duration = 3; // default scan duration
|
||||
_rx_on_when_idle = true;
|
||||
_debug = false;
|
||||
if (!lock) {
|
||||
lock = xSemaphoreCreateBinary();
|
||||
if (lock == NULL) {
|
||||
|
|
@ -40,6 +42,7 @@ ZigbeeCore::ZigbeeCore() {
|
|||
|
||||
//forward declaration
|
||||
static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void *message);
|
||||
bool zb_apsde_data_indication_handler(esp_zb_apsde_data_ind_t ind);
|
||||
|
||||
bool ZigbeeCore::begin(esp_zb_cfg_t *role_cfg, bool erase_nvs) {
|
||||
if (!zigbeeInit(role_cfg, erase_nvs)) {
|
||||
|
|
@ -173,6 +176,9 @@ bool ZigbeeCore::zigbeeInit(esp_zb_cfg_t *zb_cfg, bool erase_nvs) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Register APSDATA INDICATION handler to catch bind/unbind requests
|
||||
esp_zb_aps_data_indication_handler_register(zb_apsde_data_indication_handler);
|
||||
|
||||
//Erase NVRAM before creating connection to new Coordinator
|
||||
if (erase_nvs) {
|
||||
esp_zb_nvram_erase_at_start(true);
|
||||
|
|
@ -223,6 +229,13 @@ void ZigbeeCore::openNetwork(uint8_t time) {
|
|||
}
|
||||
}
|
||||
|
||||
void ZigbeeCore::closeNetwork() {
|
||||
if (started()) {
|
||||
log_v("Closing network");
|
||||
esp_zb_bdb_close_network();
|
||||
}
|
||||
}
|
||||
|
||||
static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask) {
|
||||
ESP_ERROR_CHECK(esp_zb_bdb_start_top_level_commissioning(mode_mask));
|
||||
}
|
||||
|
|
@ -234,6 +247,8 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
|
|||
esp_zb_app_signal_type_t sig_type = (esp_zb_app_signal_type_t)*p_sg_p;
|
||||
//coordinator variables
|
||||
esp_zb_zdo_signal_device_annce_params_t *dev_annce_params = NULL;
|
||||
//router variables
|
||||
esp_zb_zdo_signal_device_update_params_t *dev_update_params = NULL;
|
||||
|
||||
//main switch
|
||||
switch (sig_type) {
|
||||
|
|
@ -267,7 +282,7 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
|
|||
} else {
|
||||
// Save the channel mask to NVRAM in case of reboot which may be on a different channel after a change in the network
|
||||
Zigbee.setNVRAMChannelMask(1 << esp_zb_get_current_channel());
|
||||
Zigbee._connected = true;
|
||||
Zigbee._connected = true; // Coordinator is always connected
|
||||
}
|
||||
Zigbee.searchBindings();
|
||||
}
|
||||
|
|
@ -287,6 +302,7 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
|
|||
extended_pan_id[7], extended_pan_id[6], extended_pan_id[5], extended_pan_id[4], extended_pan_id[3], extended_pan_id[2], extended_pan_id[1],
|
||||
extended_pan_id[0], esp_zb_get_pan_id(), esp_zb_get_current_channel(), esp_zb_get_short_address()
|
||||
);
|
||||
Zigbee._connected = true;
|
||||
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
|
||||
} else {
|
||||
log_i("Restart network formation (status: %s)", esp_err_to_name(err_status));
|
||||
|
|
@ -340,20 +356,60 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
|
|||
*/
|
||||
// for each endpoint in the list call the findEndpoint function if not bounded or allowed to bind multiple devices
|
||||
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
|
||||
if (!(*it)->bound() || (*it)->epAllowMultipleBinding()) {
|
||||
// Check if the device is already bound
|
||||
bool found = false;
|
||||
// Get the list of devices bound to the EP
|
||||
std::list<zb_device_params_t *> bound_devices = (*it)->getBoundDevices();
|
||||
for (std::list<zb_device_params_t *>::iterator device = bound_devices.begin(); device != bound_devices.end(); ++device) {
|
||||
if (((*device)->short_addr == dev_annce_params->device_short_addr) || (memcmp((*device)->ieee_addr, dev_annce_params->ieee_addr, 8) == 0)) {
|
||||
found = true;
|
||||
log_d("Device already bound to endpoint %d", (*it)->getEndpoint());
|
||||
break;
|
||||
log_d("Checking endpoint %d", (*it)->getEndpoint());
|
||||
if (!(*it)->epUseManualBinding()) {
|
||||
if (!(*it)->bound() || (*it)->epAllowMultipleBinding()) {
|
||||
// Check if the device is already bound
|
||||
bool found = false;
|
||||
// Get the list of devices bound to the EP
|
||||
std::list<zb_device_params_t *> bound_devices = (*it)->getBoundDevices();
|
||||
for (std::list<zb_device_params_t *>::iterator device = bound_devices.begin(); device != bound_devices.end(); ++device) {
|
||||
if (((*device)->short_addr == dev_annce_params->device_short_addr) || (memcmp((*device)->ieee_addr, dev_annce_params->ieee_addr, 8) == 0)) {
|
||||
found = true;
|
||||
log_d("Device already bound to endpoint %d", (*it)->getEndpoint());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
log_d("Device not bound to endpoint %d and it is free to bound!", (*it)->getEndpoint());
|
||||
(*it)->findEndpoint(&cmd_req);
|
||||
log_d("Endpoint %d is searching for device", (*it)->getEndpoint());
|
||||
break; // Only one endpoint per device
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
(*it)->findEndpoint(&cmd_req);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ESP_ZB_ZDO_SIGNAL_DEVICE_UPDATE: // Router
|
||||
if ((zigbee_role_t)Zigbee.getRole() == ZIGBEE_ROUTER) {
|
||||
dev_update_params = (esp_zb_zdo_signal_device_update_params_t *)esp_zb_app_signal_get_params(p_sg_p);
|
||||
log_i("New device commissioned or rejoined (short: 0x%04hx)", dev_update_params->short_addr);
|
||||
esp_zb_zdo_match_desc_req_param_t cmd_req;
|
||||
cmd_req.dst_nwk_addr = dev_update_params->short_addr;
|
||||
cmd_req.addr_of_interest = dev_update_params->short_addr;
|
||||
// for each endpoint in the list call the findEndpoint function if not bounded or allowed to bind multiple devices
|
||||
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
|
||||
log_d("Checking endpoint %d", (*it)->getEndpoint());
|
||||
if (!(*it)->epUseManualBinding()) {
|
||||
if (!(*it)->bound() || (*it)->epAllowMultipleBinding()) {
|
||||
// Check if the device is already bound
|
||||
bool found = false;
|
||||
// Get the list of devices bound to the EP
|
||||
std::list<zb_device_params_t *> bound_devices = (*it)->getBoundDevices();
|
||||
for (std::list<zb_device_params_t *>::iterator device = bound_devices.begin(); device != bound_devices.end(); ++device) {
|
||||
if (((*device)->short_addr == dev_update_params->short_addr) || (memcmp((*device)->ieee_addr, dev_update_params->long_addr, 8) == 0)) {
|
||||
found = true;
|
||||
log_d("Device already bound to endpoint %d", (*it)->getEndpoint());
|
||||
break;
|
||||
}
|
||||
}
|
||||
log_d("Device not bound to endpoint %d and it is free to bound!", (*it)->getEndpoint());
|
||||
if (!found) {
|
||||
(*it)->findEndpoint(&cmd_req);
|
||||
log_d("Endpoint %d is searching for device", (*it)->getEndpoint());
|
||||
break; // Only one endpoint per device
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -380,6 +436,30 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
|
|||
}
|
||||
}
|
||||
|
||||
// APS DATA INDICATION HANDLER to catch bind/unbind requests
|
||||
bool zb_apsde_data_indication_handler(esp_zb_apsde_data_ind_t ind) {
|
||||
if (Zigbee.getDebugMode()) {
|
||||
log_d("APSDE INDICATION - Received APSDE-DATA indication, status: %d", ind.status);
|
||||
log_d(
|
||||
"APSDE INDICATION - dst_endpoint: %d, src_endpoint: %d, dst_addr_mode: %d, src_addr_mode: %d, cluster_id: 0x%04x, asdu_length: %d", ind.dst_endpoint,
|
||||
ind.src_endpoint, ind.dst_addr_mode, ind.src_addr_mode, ind.cluster_id, ind.asdu_length
|
||||
);
|
||||
log_d(
|
||||
"APSDE INDICATION - dst_short_addr: 0x%04x, src_short_addr: 0x%04x, profile_id: 0x%04x, security_status: %d, lqi: %d, rx_time: %d", ind.dst_short_addr,
|
||||
ind.src_short_addr, ind.profile_id, ind.security_status, ind.lqi, ind.rx_time
|
||||
);
|
||||
}
|
||||
if (ind.status == 0x00) {
|
||||
// Catch bind/unbind requests to update the bound devices list
|
||||
if (ind.cluster_id == 0x21 || ind.cluster_id == 0x22) {
|
||||
Zigbee.searchBindings();
|
||||
}
|
||||
} else {
|
||||
log_e("APSDE INDICATION - Invalid status of APSDE-DATA indication, error code: %d", ind.status);
|
||||
}
|
||||
return false; //False to let the stack process the message as usual
|
||||
}
|
||||
|
||||
void ZigbeeCore::factoryReset(bool restart) {
|
||||
if (restart) {
|
||||
log_v("Factory resetting Zigbee stack, device will reboot");
|
||||
|
|
@ -444,63 +524,194 @@ void ZigbeeCore::scanDelete() {
|
|||
_scan_status = ZB_SCAN_FAILED;
|
||||
}
|
||||
|
||||
// Recall bounded devices from the binding table after reboot
|
||||
// Recall bounded devices from the binding table after reboot or when requested
|
||||
void ZigbeeCore::bindingTableCb(const esp_zb_zdo_binding_table_info_t *table_info, void *user_ctx) {
|
||||
bool done = true;
|
||||
esp_zb_zdo_mgmt_bind_param_t *req = (esp_zb_zdo_mgmt_bind_param_t *)user_ctx;
|
||||
esp_zb_zdp_status_t zdo_status = (esp_zb_zdp_status_t)table_info->status;
|
||||
log_d("Binding table callback for address 0x%04x with status %d", req->dst_addr, zdo_status);
|
||||
|
||||
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
|
||||
// Print binding table log simple
|
||||
log_d("Binding table info: total %d, index %d, count %d", table_info->total, table_info->index, table_info->count);
|
||||
|
||||
if (table_info->total == 0) {
|
||||
log_d("No binding table entries found");
|
||||
// Clear all bound devices since there are no entries
|
||||
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
|
||||
log_d("Clearing bound devices for EP %d", (*it)->getEndpoint());
|
||||
(*it)->clearBoundDevices();
|
||||
}
|
||||
free(req);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a set to track found devices using both short and IEEE addresses
|
||||
struct DeviceIdentifier {
|
||||
uint8_t endpoint;
|
||||
uint16_t short_addr;
|
||||
esp_zb_ieee_addr_t ieee_addr;
|
||||
bool is_ieee;
|
||||
|
||||
bool operator<(const DeviceIdentifier &other) const {
|
||||
if (endpoint != other.endpoint) {
|
||||
return endpoint < other.endpoint;
|
||||
}
|
||||
if (is_ieee != other.is_ieee) {
|
||||
return is_ieee < other.is_ieee;
|
||||
}
|
||||
if (is_ieee) {
|
||||
return memcmp(ieee_addr, other.ieee_addr, sizeof(esp_zb_ieee_addr_t)) < 0;
|
||||
}
|
||||
return short_addr < other.short_addr;
|
||||
}
|
||||
};
|
||||
static std::set<DeviceIdentifier> found_devices;
|
||||
static std::vector<esp_zb_zdo_binding_table_record_t> all_records;
|
||||
|
||||
// If this is the first chunk (index 0), clear the previous data
|
||||
if (table_info->index == 0) {
|
||||
found_devices.clear();
|
||||
all_records.clear();
|
||||
}
|
||||
|
||||
// Add current records to our collection
|
||||
esp_zb_zdo_binding_table_record_t *record = table_info->record;
|
||||
for (int i = 0; i < table_info->count; i++) {
|
||||
log_d(
|
||||
"Binding table record: src_endp %d, dst_endp %d, cluster_id 0x%04x, dst_addr_mode %d", record->src_endp, record->dst_endp, record->cluster_id,
|
||||
"Processing record %d: src_endp %d, dst_endp %d, cluster_id 0x%04x, dst_addr_mode %d", i, record->src_endp, record->dst_endp, record->cluster_id,
|
||||
record->dst_addr_mode
|
||||
);
|
||||
|
||||
zb_device_params_t *device = (zb_device_params_t *)calloc(1, sizeof(zb_device_params_t));
|
||||
device->endpoint = record->dst_endp;
|
||||
if (record->dst_addr_mode == ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT || record->dst_addr_mode == ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT) {
|
||||
device->short_addr = record->dst_address.addr_short;
|
||||
} else { //ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT
|
||||
memcpy(device->ieee_addr, record->dst_address.addr_long, sizeof(esp_zb_ieee_addr_t));
|
||||
}
|
||||
|
||||
// Add to list of bound devices of proper endpoint
|
||||
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
|
||||
if ((*it)->getEndpoint() == record->src_endp) {
|
||||
(*it)->addBoundDevice(device);
|
||||
log_d(
|
||||
"Device bound to EP %d -> device endpoint: %d, short addr: 0x%04x, ieee addr: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", record->src_endp,
|
||||
device->endpoint, device->short_addr, device->ieee_addr[7], device->ieee_addr[6], device->ieee_addr[5], device->ieee_addr[4], device->ieee_addr[3],
|
||||
device->ieee_addr[2], device->ieee_addr[1], device->ieee_addr[0]
|
||||
);
|
||||
}
|
||||
}
|
||||
all_records.push_back(*record);
|
||||
record = record->next;
|
||||
}
|
||||
|
||||
// Continue reading the binding table
|
||||
// If this is not the last chunk, request the next one
|
||||
if (table_info->index + table_info->count < table_info->total) {
|
||||
/* There are unreported binding table entries, request for them. */
|
||||
log_d("Requesting next chunk of binding table (current index: %d, count: %d, total: %d)", table_info->index, table_info->count, table_info->total);
|
||||
req->start_index = table_info->index + table_info->count;
|
||||
esp_zb_zdo_binding_table_req(req, bindingTableCb, req);
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This is the last chunk, process all records
|
||||
log_d("Processing final chunk of binding table, total records: %d", all_records.size());
|
||||
for (const auto &record : all_records) {
|
||||
|
||||
if (done) {
|
||||
// Print bound devices
|
||||
log_d("Filling bounded devices finished");
|
||||
DeviceIdentifier dev_id;
|
||||
dev_id.endpoint = record.src_endp;
|
||||
dev_id.is_ieee = (record.dst_addr_mode == ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT);
|
||||
|
||||
if (dev_id.is_ieee) {
|
||||
memcpy(dev_id.ieee_addr, record.dst_address.addr_long, sizeof(esp_zb_ieee_addr_t));
|
||||
dev_id.short_addr = 0xFFFF; // Invalid short address
|
||||
} else {
|
||||
dev_id.short_addr = record.dst_address.addr_short;
|
||||
memset(dev_id.ieee_addr, 0, sizeof(esp_zb_ieee_addr_t));
|
||||
}
|
||||
|
||||
// Track this device as found
|
||||
found_devices.insert(dev_id);
|
||||
}
|
||||
|
||||
// Now process each endpoint and update its bound devices
|
||||
for (std::list<ZigbeeEP *>::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) {
|
||||
log_d("Processing endpoint %d", (*it)->getEndpoint());
|
||||
std::list<zb_device_params_t *> bound_devices = (*it)->getBoundDevices();
|
||||
std::list<zb_device_params_t *> devices_to_remove;
|
||||
|
||||
// First, identify devices that need to be removed
|
||||
for (std::list<zb_device_params_t *>::iterator dev_it = bound_devices.begin(); dev_it != bound_devices.end(); ++dev_it) {
|
||||
DeviceIdentifier dev_id;
|
||||
dev_id.endpoint = (*it)->getEndpoint();
|
||||
|
||||
// Create both short and IEEE address identifiers for the device
|
||||
bool found = false;
|
||||
|
||||
// Check if device exists with short address
|
||||
if ((*dev_it)->short_addr != 0xFFFF) {
|
||||
dev_id.is_ieee = false;
|
||||
dev_id.short_addr = (*dev_it)->short_addr;
|
||||
memset(dev_id.ieee_addr, 0, sizeof(esp_zb_ieee_addr_t));
|
||||
if (found_devices.find(dev_id) != found_devices.end()) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if device exists with IEEE address
|
||||
if (!found) {
|
||||
dev_id.is_ieee = true;
|
||||
memcpy(dev_id.ieee_addr, (*dev_it)->ieee_addr, sizeof(esp_zb_ieee_addr_t));
|
||||
dev_id.short_addr = 0xFFFF;
|
||||
if (found_devices.find(dev_id) != found_devices.end()) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
devices_to_remove.push_back(*dev_it);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove devices that are no longer in the binding table
|
||||
for (std::list<zb_device_params_t *>::iterator dev_it = devices_to_remove.begin(); dev_it != devices_to_remove.end(); ++dev_it) {
|
||||
(*it)->removeBoundDevice(*dev_it);
|
||||
free(*dev_it);
|
||||
}
|
||||
|
||||
// Now add new devices from the binding table
|
||||
for (const auto &record : all_records) {
|
||||
if (record.src_endp == (*it)->getEndpoint()) {
|
||||
log_d("Processing binding record for EP %d", record.src_endp);
|
||||
zb_device_params_t *device = (zb_device_params_t *)calloc(1, sizeof(zb_device_params_t));
|
||||
if (!device) {
|
||||
log_e("Failed to allocate memory for device params");
|
||||
continue;
|
||||
}
|
||||
device->endpoint = record.dst_endp;
|
||||
|
||||
bool is_ieee = (record.dst_addr_mode == ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT);
|
||||
if (is_ieee) {
|
||||
memcpy(device->ieee_addr, record.dst_address.addr_long, sizeof(esp_zb_ieee_addr_t));
|
||||
device->short_addr = 0xFFFF;
|
||||
} else {
|
||||
device->short_addr = record.dst_address.addr_short;
|
||||
memset(device->ieee_addr, 0, sizeof(esp_zb_ieee_addr_t));
|
||||
}
|
||||
|
||||
// Check if device already exists
|
||||
bool device_exists = false;
|
||||
for (std::list<zb_device_params_t *>::iterator dev_it = bound_devices.begin(); dev_it != bound_devices.end(); ++dev_it) {
|
||||
if (is_ieee) {
|
||||
if (memcmp((*dev_it)->ieee_addr, device->ieee_addr, sizeof(esp_zb_ieee_addr_t)) == 0) {
|
||||
device_exists = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ((*dev_it)->short_addr == device->short_addr) {
|
||||
device_exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!device_exists) {
|
||||
(*it)->addBoundDevice(device);
|
||||
log_d(
|
||||
"Device bound to EP %d -> device endpoint: %d, %s: %s", record.src_endp, device->endpoint, is_ieee ? "ieee addr" : "short addr",
|
||||
is_ieee ? formatIEEEAddress(device->ieee_addr) : formatShortAddress(device->short_addr)
|
||||
);
|
||||
} else {
|
||||
log_d("Device already exists, freeing allocated memory");
|
||||
free(device); // Free the device if it already exists
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print bound devices
|
||||
log_d("Filling bounded devices finished");
|
||||
free(req);
|
||||
}
|
||||
} else {
|
||||
log_e("Binding table request failed with status: %d", zdo_status);
|
||||
free(req);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "esp_zigbee_core.h"
|
||||
#include "zdo/esp_zigbee_zdo_common.h"
|
||||
#include "aps/esp_zigbee_aps.h"
|
||||
#include <esp32-hal-log.h>
|
||||
#include <list>
|
||||
#include "ZigbeeEP.h"
|
||||
|
|
@ -100,6 +101,7 @@ private:
|
|||
uint8_t _open_network;
|
||||
zigbee_scan_result_t *_scan_result;
|
||||
SemaphoreHandle_t lock;
|
||||
bool _debug;
|
||||
|
||||
bool zigbeeInit(esp_zb_cfg_t *zb_cfg, bool erase_nvs);
|
||||
static void scanCompleteCallback(esp_zb_zdp_status_t zdo_status, uint8_t count, esp_zb_network_descriptor_t *nwk_descriptor);
|
||||
|
|
@ -156,6 +158,7 @@ public:
|
|||
}
|
||||
void setRebootOpenNetwork(uint8_t time);
|
||||
void openNetwork(uint8_t time);
|
||||
void closeNetwork();
|
||||
|
||||
//scan_duration Time spent scanning each channel, in units of ((1 << scan_duration) + 1) * a beacon time. (15.36 microseconds)
|
||||
void scanNetworks(uint32_t channel_mask = ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK, uint8_t scan_duration = 5);
|
||||
|
|
@ -166,8 +169,29 @@ public:
|
|||
|
||||
void factoryReset(bool restart = true);
|
||||
|
||||
void setDebugMode(bool debug) {
|
||||
_debug = debug;
|
||||
}
|
||||
bool getDebugMode() {
|
||||
return _debug;
|
||||
}
|
||||
|
||||
// Friend function declaration to allow access to private members
|
||||
friend void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct);
|
||||
friend bool zb_apsde_data_indication_handler(esp_zb_apsde_data_ind_t ind);
|
||||
|
||||
// Helper functions for formatting addresses
|
||||
static inline const char *formatIEEEAddress(const esp_zb_ieee_addr_t addr) {
|
||||
static char buf[24];
|
||||
snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", addr[7], addr[6], addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static inline const char *formatShortAddress(uint16_t addr) {
|
||||
static char buf[7];
|
||||
snprintf(buf, sizeof(buf), "0x%04X", addr);
|
||||
return buf;
|
||||
}
|
||||
};
|
||||
|
||||
extern ZigbeeCore Zigbee;
|
||||
|
|
|
|||
|
|
@ -7,11 +7,6 @@
|
|||
#include "esp_zigbee_cluster.h"
|
||||
#include "zcl/esp_zigbee_zcl_power_config.h"
|
||||
|
||||
bool ZigbeeEP::_is_bound = false;
|
||||
bool ZigbeeEP::_allow_multiple_binding = false;
|
||||
|
||||
//TODO: is_bound and allow_multiple_binding to make not static
|
||||
|
||||
/* Zigbee End Device Class */
|
||||
ZigbeeEP::ZigbeeEP(uint8_t endpoint) {
|
||||
_endpoint = endpoint;
|
||||
|
|
@ -22,6 +17,9 @@ ZigbeeEP::ZigbeeEP(uint8_t endpoint) {
|
|||
_read_model = NULL;
|
||||
_read_manufacturer = NULL;
|
||||
_time_status = 0;
|
||||
_is_bound = false;
|
||||
_use_manual_binding = false;
|
||||
_allow_multiple_binding = false;
|
||||
if (!lock) {
|
||||
lock = xSemaphoreCreateBinary();
|
||||
if (lock == NULL) {
|
||||
|
|
@ -562,6 +560,54 @@ void ZigbeeEP::requestOTAUpdate() {
|
|||
esp_zb_lock_release();
|
||||
}
|
||||
|
||||
void ZigbeeEP::removeBoundDevice(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) {
|
||||
log_d(
|
||||
"Attempting to remove device with endpoint %d and IEEE address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", endpoint, ieee_addr[7], ieee_addr[6], ieee_addr[5],
|
||||
ieee_addr[4], ieee_addr[3], ieee_addr[2], ieee_addr[1], ieee_addr[0]
|
||||
);
|
||||
|
||||
for (std::list<zb_device_params_t *>::iterator it = _bound_devices.begin(); it != _bound_devices.end(); ++it) {
|
||||
if ((*it)->endpoint == endpoint && memcmp((*it)->ieee_addr, ieee_addr, sizeof(esp_zb_ieee_addr_t)) == 0) {
|
||||
log_d("Found matching device, removing it");
|
||||
_bound_devices.erase(it);
|
||||
if (_bound_devices.empty()) {
|
||||
_is_bound = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
log_w("No matching device found for removal");
|
||||
}
|
||||
|
||||
void ZigbeeEP::removeBoundDevice(zb_device_params_t *device) {
|
||||
if (!device) {
|
||||
log_e("Invalid device parameters provided");
|
||||
return;
|
||||
}
|
||||
|
||||
log_d(
|
||||
"Attempting to remove device with endpoint %d, short address 0x%04x, IEEE address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", device->endpoint,
|
||||
device->short_addr, device->ieee_addr[7], device->ieee_addr[6], device->ieee_addr[5], device->ieee_addr[4], device->ieee_addr[3], device->ieee_addr[2],
|
||||
device->ieee_addr[1], device->ieee_addr[0]
|
||||
);
|
||||
|
||||
for (std::list<zb_device_params_t *>::iterator it = _bound_devices.begin(); it != _bound_devices.end(); ++it) {
|
||||
bool endpoint_matches = ((*it)->endpoint == device->endpoint);
|
||||
bool short_addr_matches = (device->short_addr != 0xFFFF && (*it)->short_addr == device->short_addr);
|
||||
bool ieee_addr_matches = (memcmp((*it)->ieee_addr, device->ieee_addr, sizeof(esp_zb_ieee_addr_t)) == 0);
|
||||
|
||||
if (endpoint_matches && (short_addr_matches || ieee_addr_matches)) {
|
||||
log_d("Found matching device by %s, removing it", short_addr_matches ? "short address" : "IEEE address");
|
||||
_bound_devices.erase(it);
|
||||
if (_bound_devices.empty()) {
|
||||
_is_bound = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
log_w("No matching device found for removal");
|
||||
}
|
||||
|
||||
const char *ZigbeeEP::esp_zb_zcl_status_to_name(esp_zb_zcl_status_t status) {
|
||||
switch (status) {
|
||||
case ESP_ZB_ZCL_STATUS_SUCCESS: return "Success";
|
||||
|
|
|
|||
|
|
@ -66,12 +66,15 @@ public:
|
|||
return _bound_devices;
|
||||
}
|
||||
|
||||
static bool bound() {
|
||||
bool bound() {
|
||||
return _is_bound;
|
||||
}
|
||||
static void allowMultipleBinding(bool bind) {
|
||||
void allowMultipleBinding(bool bind) {
|
||||
_allow_multiple_binding = bind;
|
||||
}
|
||||
void setManualBinding(bool bind) {
|
||||
_use_manual_binding = bind;
|
||||
}
|
||||
|
||||
// Set Manufacturer name and model
|
||||
bool setManufacturerAndModel(const char *name, const char *model);
|
||||
|
|
@ -98,6 +101,9 @@ public:
|
|||
bool epAllowMultipleBinding() {
|
||||
return _allow_multiple_binding;
|
||||
}
|
||||
bool epUseManualBinding() {
|
||||
return _use_manual_binding;
|
||||
}
|
||||
|
||||
// OTA methods
|
||||
/**
|
||||
|
|
@ -138,6 +144,14 @@ public:
|
|||
_is_bound = true;
|
||||
}
|
||||
|
||||
virtual void removeBoundDevice(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr);
|
||||
virtual void removeBoundDevice(zb_device_params_t *device);
|
||||
|
||||
virtual void clearBoundDevices() {
|
||||
_bound_devices.clear();
|
||||
_is_bound = false;
|
||||
}
|
||||
|
||||
void onIdentify(void (*callback)(uint16_t)) {
|
||||
_on_identify = callback;
|
||||
}
|
||||
|
|
@ -157,8 +171,9 @@ protected:
|
|||
esp_zb_ha_standard_devices_t _device_id;
|
||||
esp_zb_endpoint_config_t _ep_config;
|
||||
esp_zb_cluster_list_t *_cluster_list;
|
||||
static bool _is_bound;
|
||||
static bool _allow_multiple_binding;
|
||||
bool _is_bound;
|
||||
bool _allow_multiple_binding;
|
||||
bool _use_manual_binding;
|
||||
std::list<zb_device_params_t *> _bound_devices;
|
||||
SemaphoreHandle_t lock;
|
||||
zb_power_source_t _power_source;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ ZigbeeColorDimmerSwitch *ZigbeeColorDimmerSwitch::_instance = nullptr;
|
|||
|
||||
ZigbeeColorDimmerSwitch::ZigbeeColorDimmerSwitch(uint8_t endpoint) : ZigbeeEP(endpoint) {
|
||||
_device_id = ESP_ZB_HA_COLOR_DIMMER_SWITCH_DEVICE_ID;
|
||||
_instance = this; // Set the static pointer to this instance
|
||||
_instance = this; // Set the static pointer to this instance
|
||||
_device = nullptr; // Initialize light pointer to null
|
||||
|
||||
esp_zb_color_dimmable_switch_cfg_t switch_cfg = ESP_ZB_DEFAULT_COLOR_DIMMABLE_SWITCH_CONFIG();
|
||||
_cluster_list = esp_zb_color_dimmable_switch_clusters_create(&switch_cfg);
|
||||
|
|
@ -17,20 +18,39 @@ ZigbeeColorDimmerSwitch::ZigbeeColorDimmerSwitch(uint8_t endpoint) : ZigbeeEP(en
|
|||
}
|
||||
|
||||
void ZigbeeColorDimmerSwitch::bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx) {
|
||||
ZigbeeColorDimmerSwitch *instance = static_cast<ZigbeeColorDimmerSwitch *>(user_ctx);
|
||||
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
|
||||
log_i("Bound successfully!");
|
||||
if (user_ctx) {
|
||||
zb_device_params_t *light = (zb_device_params_t *)user_ctx;
|
||||
if (instance->_device) {
|
||||
zb_device_params_t *light = (zb_device_params_t *)instance->_device;
|
||||
log_i("The light originating from address(0x%x) on endpoint(%d)", light->short_addr, light->endpoint);
|
||||
_instance->_bound_devices.push_back(light);
|
||||
log_d("Light bound to a switch on EP %d", instance->_endpoint);
|
||||
instance->_bound_devices.push_back(light);
|
||||
}
|
||||
_is_bound = true;
|
||||
instance->_is_bound = true;
|
||||
} else {
|
||||
log_e("Binding failed!");
|
||||
instance->_device = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeColorDimmerSwitch::bindCbWrapper(esp_zb_zdp_status_t zdo_status, void *user_ctx) {
|
||||
ZigbeeColorDimmerSwitch *instance = static_cast<ZigbeeColorDimmerSwitch *>(user_ctx);
|
||||
if (instance) {
|
||||
log_d("bindCbWrapper on EP %d", instance->_endpoint);
|
||||
instance->bindCb(zdo_status, user_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeColorDimmerSwitch::findCbWrapper(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx) {
|
||||
ZigbeeColorDimmerSwitch *instance = static_cast<ZigbeeColorDimmerSwitch *>(user_ctx);
|
||||
if (instance) {
|
||||
log_d("findCbWrapper on EP %d", instance->_endpoint);
|
||||
instance->findCb(zdo_status, addr, endpoint, user_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeColorDimmerSwitch::findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx) {
|
||||
ZigbeeColorDimmerSwitch *instance = static_cast<ZigbeeColorDimmerSwitch *>(user_ctx);
|
||||
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
|
||||
log_d("Found light endpoint");
|
||||
esp_zb_zdo_bind_req_param_t bind_req;
|
||||
|
|
@ -39,22 +59,23 @@ void ZigbeeColorDimmerSwitch::findCb(esp_zb_zdp_status_t zdo_status, uint16_t ad
|
|||
light->short_addr = addr;
|
||||
esp_zb_ieee_address_by_short(light->short_addr, light->ieee_addr);
|
||||
esp_zb_get_long_address(bind_req.src_address);
|
||||
bind_req.src_endp = *((uint8_t *)user_ctx); //_endpoint;
|
||||
bind_req.src_endp = instance->_endpoint;
|
||||
bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF;
|
||||
bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED;
|
||||
memcpy(bind_req.dst_address_u.addr_long, light->ieee_addr, sizeof(esp_zb_ieee_addr_t));
|
||||
bind_req.dst_endp = endpoint;
|
||||
bind_req.req_dst_addr = esp_zb_get_short_address();
|
||||
instance->_device = light;
|
||||
log_v("Try to bind on/off control of dimmable light");
|
||||
esp_zb_zdo_device_bind_req(&bind_req, bindCb, NULL);
|
||||
esp_zb_zdo_device_bind_req(&bind_req, ZigbeeColorDimmerSwitch::bindCbWrapper, NULL);
|
||||
bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL;
|
||||
log_v("Try to bind level control of dimmable light");
|
||||
esp_zb_zdo_device_bind_req(&bind_req, bindCb, NULL);
|
||||
esp_zb_zdo_device_bind_req(&bind_req, ZigbeeColorDimmerSwitch::bindCbWrapper, NULL);
|
||||
bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL;
|
||||
log_v("Try to bind color control of dimmable light");
|
||||
esp_zb_zdo_device_bind_req(&bind_req, bindCb, (void *)light);
|
||||
esp_zb_zdo_device_bind_req(&bind_req, ZigbeeColorDimmerSwitch::bindCbWrapper, this);
|
||||
} else {
|
||||
log_v("No color dimmable light endpoint found");
|
||||
log_d("No color dimmable light endpoint found");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -70,7 +91,7 @@ void ZigbeeColorDimmerSwitch::findEndpoint(esp_zb_zdo_match_desc_req_param_t *cm
|
|||
.num_out_clusters = 3,
|
||||
.cluster_list = cluster_list,
|
||||
};
|
||||
esp_zb_zdo_match_cluster(&color_dimmable_light_req, findCb, &_endpoint);
|
||||
esp_zb_zdo_match_cluster(&color_dimmable_light_req, ZigbeeColorDimmerSwitch::findCbWrapper, this);
|
||||
}
|
||||
|
||||
// Methods to control the light
|
||||
|
|
|
|||
|
|
@ -47,10 +47,13 @@ public:
|
|||
private:
|
||||
// save instance of the class in order to use it in static functions
|
||||
static ZigbeeColorDimmerSwitch *_instance;
|
||||
zb_device_params_t *_device;
|
||||
|
||||
void findEndpoint(esp_zb_zdo_match_desc_req_param_t *cmd_req);
|
||||
static void bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
static void findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
void bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
void findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
static void bindCbWrapper(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
static void findCbWrapper(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
|
||||
void calculateXY(uint8_t red, uint8_t green, uint8_t blue, uint16_t &x, uint16_t &y);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ ZigbeeSwitch *ZigbeeSwitch::_instance = nullptr;
|
|||
ZigbeeSwitch::ZigbeeSwitch(uint8_t endpoint) : ZigbeeEP(endpoint) {
|
||||
_device_id = ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID;
|
||||
_instance = this; // Set the static pointer to this instance
|
||||
_device = nullptr;
|
||||
|
||||
esp_zb_on_off_switch_cfg_t switch_cfg = ESP_ZB_DEFAULT_ON_OFF_SWITCH_CONFIG();
|
||||
_cluster_list = esp_zb_on_off_switch_clusters_create(&switch_cfg);
|
||||
|
|
@ -15,18 +16,40 @@ ZigbeeSwitch::ZigbeeSwitch(uint8_t endpoint) : ZigbeeEP(endpoint) {
|
|||
}
|
||||
|
||||
void ZigbeeSwitch::bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx) {
|
||||
ZigbeeSwitch *instance = static_cast<ZigbeeSwitch *>(user_ctx);
|
||||
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
|
||||
log_i("Bound successfully!");
|
||||
if (user_ctx) {
|
||||
zb_device_params_t *light = (zb_device_params_t *)user_ctx;
|
||||
if (instance->_device) {
|
||||
zb_device_params_t *light = (zb_device_params_t *)instance->_device;
|
||||
log_i("The light originating from address(0x%x) on endpoint(%d)", light->short_addr, light->endpoint);
|
||||
_instance->_bound_devices.push_back(light);
|
||||
log_d("Light bound to a switch on EP %d", instance->_endpoint);
|
||||
instance->_bound_devices.push_back(light);
|
||||
}
|
||||
_is_bound = true;
|
||||
instance->_is_bound = true;
|
||||
} else {
|
||||
instance->_device = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeSwitch::bindCbWrapper(esp_zb_zdp_status_t zdo_status, void *user_ctx) {
|
||||
ZigbeeSwitch *instance = static_cast<ZigbeeSwitch *>(user_ctx);
|
||||
if (instance) {
|
||||
log_d("bindCbWrapper on EP %d", instance->_endpoint);
|
||||
instance->bindCb(zdo_status, user_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// Static wrapper for findCb
|
||||
void ZigbeeSwitch::findCbWrapper(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx) {
|
||||
ZigbeeSwitch *instance = static_cast<ZigbeeSwitch *>(user_ctx);
|
||||
if (instance) {
|
||||
log_d("findCbWrapper on EP %d", instance->_endpoint);
|
||||
instance->findCb(zdo_status, addr, endpoint, user_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeSwitch::findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx) {
|
||||
ZigbeeSwitch *instance = static_cast<ZigbeeSwitch *>(user_ctx);
|
||||
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
|
||||
log_d("Found light endpoint");
|
||||
esp_zb_zdo_bind_req_param_t bind_req;
|
||||
|
|
@ -34,15 +57,21 @@ void ZigbeeSwitch::findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t
|
|||
light->endpoint = endpoint;
|
||||
light->short_addr = addr;
|
||||
esp_zb_ieee_address_by_short(light->short_addr, light->ieee_addr);
|
||||
log_d("Light found: short address(0x%x), endpoint(%d)", light->short_addr, light->endpoint);
|
||||
|
||||
esp_zb_get_long_address(bind_req.src_address);
|
||||
bind_req.src_endp = *((uint8_t *)user_ctx); //_endpoint;
|
||||
bind_req.src_endp = instance->_endpoint;
|
||||
bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF;
|
||||
bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED;
|
||||
memcpy(bind_req.dst_address_u.addr_long, light->ieee_addr, sizeof(esp_zb_ieee_addr_t));
|
||||
bind_req.dst_endp = endpoint;
|
||||
bind_req.req_dst_addr = esp_zb_get_short_address();
|
||||
log_i("Try to bind On/Off");
|
||||
esp_zb_zdo_device_bind_req(&bind_req, bindCb, (void *)light);
|
||||
log_v("Try to bind On/Off");
|
||||
//save light params in the class
|
||||
instance->_device = light;
|
||||
|
||||
log_d("Find callback on EP %d", instance->_endpoint);
|
||||
esp_zb_zdo_device_bind_req(&bind_req, ZigbeeSwitch::bindCbWrapper, this);
|
||||
} else {
|
||||
log_d("No light endpoint found");
|
||||
}
|
||||
|
|
@ -59,7 +88,7 @@ void ZigbeeSwitch::findEndpoint(esp_zb_zdo_match_desc_req_param_t *cmd_req) {
|
|||
.num_out_clusters = 1,
|
||||
.cluster_list = cluster_list,
|
||||
};
|
||||
esp_zb_zdo_match_cluster(&on_off_req, findCb, &_endpoint);
|
||||
esp_zb_zdo_match_cluster(&on_off_req, ZigbeeSwitch::findCbWrapper, this);
|
||||
}
|
||||
|
||||
// Methods to control the light
|
||||
|
|
|
|||
|
|
@ -37,10 +37,12 @@ public:
|
|||
private:
|
||||
// save instance of the class in order to use it in static functions
|
||||
static ZigbeeSwitch *_instance;
|
||||
|
||||
zb_device_params_t *_device;
|
||||
void findEndpoint(esp_zb_zdo_match_desc_req_param_t *cmd_req);
|
||||
static void bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
static void findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
void bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
void findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
static void findCbWrapper(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
static void bindCbWrapper(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
};
|
||||
|
||||
#endif // CONFIG_ZB_ENABLED
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ ZigbeeThermostat *ZigbeeThermostat::_instance = nullptr;
|
|||
|
||||
ZigbeeThermostat::ZigbeeThermostat(uint8_t endpoint) : ZigbeeEP(endpoint) {
|
||||
_device_id = ESP_ZB_HA_THERMOSTAT_DEVICE_ID;
|
||||
_instance = this; // Set the static pointer to this instance
|
||||
_instance = this; // Set the static pointer to this instance
|
||||
_device = nullptr; // Initialize sensor pointer to null
|
||||
|
||||
//use custom config to avoid narrowing error -> must be fixed in zigbee-sdk
|
||||
esp_zb_thermostat_cfg_t thermostat_cfg = ZB_DEFAULT_THERMOSTAT_CONFIG();
|
||||
|
|
@ -29,21 +30,39 @@ ZigbeeThermostat::ZigbeeThermostat(uint8_t endpoint) : ZigbeeEP(endpoint) {
|
|||
}
|
||||
|
||||
void ZigbeeThermostat::bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx) {
|
||||
ZigbeeThermostat *instance = static_cast<ZigbeeThermostat *>(user_ctx);
|
||||
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
|
||||
if (user_ctx) {
|
||||
zb_device_params_t *sensor = (zb_device_params_t *)user_ctx;
|
||||
log_i("The temperature sensor originating from address(0x%x) on endpoint(%d)", sensor->short_addr, sensor->endpoint);
|
||||
_instance->_bound_devices.push_back(sensor);
|
||||
} else {
|
||||
log_v("Local binding success");
|
||||
log_i("Bound successfully!");
|
||||
if (instance->_device) {
|
||||
zb_device_params_t *sensor = (zb_device_params_t *)instance->_device;
|
||||
log_i("The sensor originating from address(0x%x) on endpoint(%d)", sensor->short_addr, sensor->endpoint);
|
||||
log_d("Sensor bound to thermostat on EP %d", instance->_endpoint);
|
||||
instance->_bound_devices.push_back(sensor);
|
||||
}
|
||||
_is_bound = true;
|
||||
instance->_is_bound = true;
|
||||
} else {
|
||||
log_e("Binding failed!");
|
||||
instance->_device = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeThermostat::bindCbWrapper(esp_zb_zdp_status_t zdo_status, void *user_ctx) {
|
||||
ZigbeeThermostat *instance = static_cast<ZigbeeThermostat *>(user_ctx);
|
||||
if (instance) {
|
||||
log_d("bindCbWrapper on EP %d", instance->_endpoint);
|
||||
instance->bindCb(zdo_status, user_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeThermostat::findCbWrapper(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx) {
|
||||
ZigbeeThermostat *instance = static_cast<ZigbeeThermostat *>(user_ctx);
|
||||
if (instance) {
|
||||
log_d("findCbWrapper on EP %d", instance->_endpoint);
|
||||
instance->findCb(zdo_status, addr, endpoint, user_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeThermostat::findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx) {
|
||||
ZigbeeThermostat *instance = static_cast<ZigbeeThermostat *>(user_ctx);
|
||||
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
|
||||
log_i("Found temperature sensor");
|
||||
esp_zb_zdo_bind_req_param_t bind_req;
|
||||
|
|
@ -56,37 +75,34 @@ void ZigbeeThermostat::findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uin
|
|||
|
||||
/* 1. Send binding request to the sensor */
|
||||
bind_req.req_dst_addr = addr;
|
||||
log_d("Request temperature sensor to bind us");
|
||||
|
||||
/* populate the src information of the binding */
|
||||
memcpy(bind_req.src_address, sensor->ieee_addr, sizeof(esp_zb_ieee_addr_t));
|
||||
bind_req.src_endp = endpoint;
|
||||
bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT;
|
||||
log_d("Bind temperature sensor");
|
||||
|
||||
/* populate the dst information of the binding */
|
||||
bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED;
|
||||
esp_zb_get_long_address(bind_req.dst_address_u.addr_long);
|
||||
bind_req.dst_endp = *((uint8_t *)user_ctx); //_endpoint;
|
||||
bind_req.dst_endp = instance->_endpoint;
|
||||
|
||||
log_i("Request temperature sensor to bind us");
|
||||
esp_zb_zdo_device_bind_req(&bind_req, bindCb, NULL);
|
||||
esp_zb_zdo_device_bind_req(&bind_req, ZigbeeThermostat::bindCbWrapper, NULL);
|
||||
|
||||
/* 2. Send binding request to self */
|
||||
bind_req.req_dst_addr = esp_zb_get_short_address();
|
||||
|
||||
/* populate the src information of the binding */
|
||||
esp_zb_get_long_address(bind_req.src_address);
|
||||
bind_req.src_endp = *((uint8_t *)user_ctx); //_endpoint;
|
||||
bind_req.src_endp = instance->_endpoint;
|
||||
bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT;
|
||||
|
||||
/* populate the dst information of the binding */
|
||||
bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED;
|
||||
memcpy(bind_req.dst_address_u.addr_long, sensor->ieee_addr, sizeof(esp_zb_ieee_addr_t));
|
||||
bind_req.dst_endp = endpoint;
|
||||
log_i("Try to bind Temperature Measurement");
|
||||
//save sensor params in the class
|
||||
instance->_device = sensor;
|
||||
|
||||
log_i("Bind temperature sensor");
|
||||
esp_zb_zdo_device_bind_req(&bind_req, bindCb, (void *)sensor);
|
||||
log_d("Find callback on EP %d", instance->_endpoint);
|
||||
esp_zb_zdo_device_bind_req(&bind_req, ZigbeeThermostat::bindCbWrapper, this);
|
||||
} else {
|
||||
log_d("No temperature sensor endpoint found");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +112,7 @@ void ZigbeeThermostat::findEndpoint(esp_zb_zdo_match_desc_req_param_t *param) {
|
|||
param->num_in_clusters = 1;
|
||||
param->num_out_clusters = 0;
|
||||
param->cluster_list = cluster_list;
|
||||
esp_zb_zdo_match_cluster(param, findCb, &_endpoint);
|
||||
esp_zb_zdo_match_cluster(param, ZigbeeThermostat::findCbWrapper, this);
|
||||
}
|
||||
|
||||
void ZigbeeThermostat::zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute) {
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ public:
|
|||
private:
|
||||
// save instance of the class in order to use it in static functions
|
||||
static ZigbeeThermostat *_instance;
|
||||
zb_device_params_t *_device;
|
||||
|
||||
void (*_on_temp_recieve)(float);
|
||||
void (*_on_config_recieve)(float, float, float);
|
||||
|
|
@ -56,8 +57,10 @@ private:
|
|||
float _tolerance;
|
||||
|
||||
void findEndpoint(esp_zb_zdo_match_desc_req_param_t *cmd_req);
|
||||
static void bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
static void findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
void bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
void findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
static void bindCbWrapper(esp_zb_zdp_status_t zdo_status, void *user_ctx);
|
||||
static void findCbWrapper(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx);
|
||||
|
||||
void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute) override;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue