Skip to content

Commit 6c3528a

Browse files
P-R-O-C-H-YCopilotpre-commit-ci-lite[bot]me-no-dev
authoredMay 27, 2025··
feat(zigbee): Add support for Binary input EP + Analog EP extension (#11339)
* feat(zigbee): Add binary input and analog extension * Apply suggestions from code review Co-authored-by: Copilot <[email protected]> * feat(zigbee): Remove setBinaryInputReporting * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Me No Dev <[email protected]>
·
3.3.03.2.1
1 parent 07d662a commit 6c3528a

File tree

10 files changed

+690
-3
lines changed

10 files changed

+690
-3
lines changed
 

‎CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ set(ARDUINO_LIBRARY_Zigbee_SRCS
301301
libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.cpp
302302
libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp
303303
libraries/Zigbee/src/ep/ZigbeePM25Sensor.cpp
304+
libraries/Zigbee/src/ep/ZigbeeBinary.cpp
304305
libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp
305306
)
306307

‎libraries/Zigbee/examples/Zigbee_Analog_Input_Output/Zigbee_Analog_Input_Output.ino

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ uint8_t analogPin = A0;
3939
uint8_t button = BOOT_PIN;
4040

4141
ZigbeeAnalog zbAnalogDevice = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER);
42+
ZigbeeAnalog zbAnalogTemp = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER + 1);
43+
ZigbeeAnalog zbAnalogFan = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER + 2);
44+
ZigbeeAnalog zbAnalogPercent = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER + 3);
4245

4346
void onAnalogOutputChange(float analog_output) {
4447
Serial.printf("Received analog output change: %.1f\r\n", analog_output);
@@ -57,15 +60,43 @@ void setup() {
5760
// Optional: set Zigbee device name and model
5861
zbAnalogDevice.setManufacturerAndModel("Espressif", "ZigbeeAnalogDevice");
5962

60-
// Add analog clusters to Zigbee Analog according your needs
63+
// Set up analog input
6164
zbAnalogDevice.addAnalogInput();
65+
zbAnalogDevice.setAnalogInputApplication(ESP_ZB_ZCL_AI_POWER_IN_WATTS_CONSUMPTION);
66+
zbAnalogDevice.setAnalogInputDescription("Power Consumption (Watts)");
67+
zbAnalogDevice.setAnalogInputResolution(0.01);
68+
69+
// Set up analog output
6270
zbAnalogDevice.addAnalogOutput();
71+
zbAnalogDevice.setAnalogOutputApplication(ESP_ZB_ZCL_AI_RPM_OTHER);
72+
zbAnalogDevice.setAnalogOutputDescription("Fan Speed (RPM)");
6373

6474
// If analog output cluster is added, set callback function for analog output change
6575
zbAnalogDevice.onAnalogOutputChange(onAnalogOutputChange);
6676

77+
// Set up analog input
78+
zbAnalogTemp.addAnalogInput();
79+
zbAnalogTemp.setAnalogInputApplication(ESP_ZB_ZCL_AI_TEMPERATURE_OTHER);
80+
zbAnalogTemp.setAnalogInputDescription("Temperature");
81+
zbAnalogTemp.setAnalogInputResolution(0.1);
82+
83+
// Set up analog input
84+
zbAnalogFan.addAnalogInput();
85+
zbAnalogFan.setAnalogInputApplication(ESP_ZB_ZCL_AI_RPM_OTHER);
86+
zbAnalogFan.setAnalogInputDescription("RPM");
87+
zbAnalogFan.setAnalogInputResolution(1);
88+
89+
// Set up analog input
90+
zbAnalogPercent.addAnalogInput();
91+
zbAnalogPercent.setAnalogInputApplication(ESP_ZB_ZCL_AI_PERCENTAGE_OTHER);
92+
zbAnalogPercent.setAnalogInputDescription("Percentage");
93+
zbAnalogPercent.setAnalogInputResolution(0.01);
94+
6795
// Add endpoints to Zigbee Core
6896
Zigbee.addEndpoint(&zbAnalogDevice);
97+
Zigbee.addEndpoint(&zbAnalogTemp);
98+
Zigbee.addEndpoint(&zbAnalogFan);
99+
Zigbee.addEndpoint(&zbAnalogPercent);
69100

70101
Serial.println("Starting Zigbee...");
71102
// When all EPs are registered, start Zigbee in End Device mode
@@ -95,9 +126,15 @@ void loop() {
95126
float analog = (float)analogRead(analogPin);
96127
Serial.printf("Updating analog input to %.1f\r\n", analog);
97128
zbAnalogDevice.setAnalogInput(analog);
129+
zbAnalogTemp.setAnalogInput(analog / 100);
130+
zbAnalogFan.setAnalogInput(analog);
131+
zbAnalogPercent.setAnalogInput(analog / 10);
98132

99133
// Analog input supports reporting
100134
zbAnalogDevice.reportAnalogInput();
135+
zbAnalogTemp.reportAnalogInput();
136+
zbAnalogFan.reportAnalogInput();
137+
zbAnalogPercent.reportAnalogInput();
101138
}
102139

103140
// Checking button for factory reset and reporting
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Arduino-ESP32 Zigbee Binary Input Example
2+
3+
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) binary input device with two different applications: HVAC fan status and security zone armed status.
4+
5+
# Supported Targets
6+
7+
Currently, this example supports the following targets.
8+
9+
| Supported Targets | ESP32-C6 | ESP32-H2 |
10+
| ----------------- | -------- | -------- |
11+
12+
## Binary Input Functions
13+
14+
* The example implements two binary inputs:
15+
- HVAC Fan Status: Reports the current state of a fan
16+
- Security Zone Armed: Reports the armed state of a security zone
17+
* By clicking the button (BOOT) on this board, it will toggle both binary inputs and immediately send a report of their states to the network.
18+
* Holding the button for more than 3 seconds will trigger a factory reset of the Zigbee device.
19+
20+
## Hardware Required
21+
22+
* A USB cable for power supply and programming
23+
24+
### Configure the Project
25+
26+
The example uses the following default pins:
27+
* Button: `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2)
28+
29+
#### Using Arduino IDE
30+
31+
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
32+
33+
* Before Compile/Verify, select the correct board: `Tools -> Board`.
34+
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
35+
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
36+
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
37+
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
38+
39+
## Troubleshooting
40+
41+
If the End device flashed with this example 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.
42+
You can do the following:
43+
44+
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
45+
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
46+
47+
By default, the coordinator network is closed after rebooting or flashing new firmware.
48+
To open the network you have 2 options:
49+
50+
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
51+
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
52+
53+
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
54+
55+
* **LED not blinking:** Check the wiring connection and the IO selection.
56+
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
57+
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
58+
59+
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
60+
61+
## Contribute
62+
63+
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
64+
65+
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!
66+
67+
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
68+
69+
## Resources
70+
71+
* Official ESP32 Forum: [Link](https://esp32.com)
72+
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
73+
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
74+
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
75+
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright 2025 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
/**
16+
* @brief This example demonstrates Zigbee binary input device.
17+
*
18+
* The example demonstrates how to use Zigbee library to create an end device binary sensor device.
19+
*
20+
* Proper Zigbee mode must be selected in Tools->Zigbee mode
21+
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
22+
*
23+
* Please check the README.md for instructions and more detailed description.
24+
*
25+
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
26+
*/
27+
28+
#ifndef ZIGBEE_MODE_ED
29+
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
30+
#endif
31+
32+
#include "Zigbee.h"
33+
34+
/* Zigbee binary sensor device configuration */
35+
#define BINARY_DEVICE_ENDPOINT_NUMBER 1
36+
37+
uint8_t binaryPin = A0;
38+
uint8_t button = BOOT_PIN;
39+
40+
ZigbeeBinary zbBinaryFan = ZigbeeBinary(BINARY_DEVICE_ENDPOINT_NUMBER);
41+
ZigbeeBinary zbBinaryZone = ZigbeeBinary(BINARY_DEVICE_ENDPOINT_NUMBER + 1);
42+
43+
bool binaryStatus = false;
44+
45+
void setup() {
46+
Serial.begin(115200);
47+
Serial.println("Starting...");
48+
49+
// Init button switch
50+
pinMode(button, INPUT_PULLUP);
51+
52+
// Set analog resolution to 10 bits
53+
analogReadResolution(10);
54+
55+
// Optional: set Zigbee device name and model
56+
zbBinaryFan.setManufacturerAndModel("Espressif", "ZigbeeBinarySensor");
57+
58+
// Set up binary fan status input (HVAC)
59+
zbBinaryFan.addBinaryInput();
60+
zbBinaryFan.setBinaryInputApplication(BINARY_INPUT_APPLICATION_TYPE_HVAC_FAN_STATUS);
61+
zbBinaryFan.setBinaryInputDescription("Fan Status");
62+
63+
// Set up binary zone armed input (Security)
64+
zbBinaryZone.addBinaryInput();
65+
zbBinaryZone.setBinaryInputApplication(BINARY_INPUT_APPLICATION_TYPE_SECURITY_ZONE_ARMED);
66+
zbBinaryZone.setBinaryInputDescription("Zone Armed");
67+
68+
// Add endpoints to Zigbee Core
69+
Zigbee.addEndpoint(&zbBinaryFan);
70+
Zigbee.addEndpoint(&zbBinaryZone);
71+
72+
Serial.println("Starting Zigbee...");
73+
// When all EPs are registered, start Zigbee in End Device mode
74+
if (!Zigbee.begin()) {
75+
Serial.println("Zigbee failed to start!");
76+
Serial.println("Rebooting...");
77+
ESP.restart();
78+
} else {
79+
Serial.println("Zigbee started successfully!");
80+
}
81+
Serial.println("Connecting to network");
82+
while (!Zigbee.connected()) {
83+
Serial.print(".");
84+
delay(100);
85+
}
86+
Serial.println("Connected");
87+
}
88+
89+
void loop() {
90+
// Checking button for factory reset and reporting
91+
if (digitalRead(button) == LOW) { // Push button pressed
92+
// Key debounce handling
93+
delay(100);
94+
int startTime = millis();
95+
while (digitalRead(button) == LOW) {
96+
delay(50);
97+
if ((millis() - startTime) > 3000) {
98+
// If key pressed for more than 3secs, factory reset Zigbee and reboot
99+
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
100+
delay(1000);
101+
Zigbee.factoryReset();
102+
}
103+
}
104+
// Toggle binary input
105+
binaryStatus = !binaryStatus;
106+
zbBinaryFan.setBinaryInput(binaryStatus);
107+
zbBinaryZone.setBinaryInput(binaryStatus);
108+
zbBinaryFan.reportBinaryInput();
109+
zbBinaryZone.reportBinaryInput();
110+
}
111+
delay(100);
112+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
3+
"requires": [
4+
"CONFIG_SOC_IEEE802154_SUPPORTED=y",
5+
"CONFIG_ZB_ENABLED=y"
6+
]
7+
}

‎libraries/Zigbee/src/Zigbee.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "ep/ZigbeePowerOutlet.h"
2121
//// Sensors
2222
#include "ep/ZigbeeAnalog.h"
23+
#include "ep/ZigbeeBinary.h"
2324
#include "ep/ZigbeeCarbonDioxideSensor.h"
2425
#include "ep/ZigbeeContactSwitch.h"
2526
#include "ep/ZigbeeDoorWindowHandle.h"

‎libraries/Zigbee/src/ep/ZigbeeAnalog.cpp

Lines changed: 217 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,33 @@ ZigbeeAnalog::ZigbeeAnalog(uint8_t endpoint) : ZigbeeEP(endpoint) {
1313
}
1414

1515
bool ZigbeeAnalog::addAnalogInput() {
16-
esp_err_t ret = esp_zb_cluster_list_add_analog_input_cluster(_cluster_list, esp_zb_analog_input_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
16+
esp_zb_attribute_list_t *esp_zb_analog_input_cluster = esp_zb_analog_input_cluster_create(NULL);
17+
18+
// Create default description for Analog Input
19+
char default_description[] = "\x0C"
20+
"Analog Input";
21+
uint32_t application_type = 0x00000000 | (ESP_ZB_ZCL_AI_GROUP_ID << 24);
22+
float resolution = 0.1; // Default resolution of 0.1
23+
24+
esp_err_t ret = esp_zb_analog_input_cluster_add_attr(esp_zb_analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_DESCRIPTION_ID, (void *)default_description);
25+
if (ret != ESP_OK) {
26+
log_e("Failed to add description attribute: 0x%x: %s", ret, esp_err_to_name(ret));
27+
return false;
28+
}
29+
30+
ret = esp_zb_analog_input_cluster_add_attr(esp_zb_analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_APPLICATION_TYPE_ID, (void *)&application_type);
31+
if (ret != ESP_OK) {
32+
log_e("Failed to add application type attribute: 0x%x: %s", ret, esp_err_to_name(ret));
33+
return false;
34+
}
35+
36+
ret = esp_zb_analog_input_cluster_add_attr(esp_zb_analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_RESOLUTION_ID, (void *)&resolution);
37+
if (ret != ESP_OK) {
38+
log_e("Failed to add resolution attribute: 0x%x: %s", ret, esp_err_to_name(ret));
39+
return false;
40+
}
41+
42+
ret = esp_zb_cluster_list_add_analog_input_cluster(_cluster_list, esp_zb_analog_input_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
1743
if (ret != ESP_OK) {
1844
log_e("Failed to add Analog Input cluster: 0x%x: %s", ret, esp_err_to_name(ret));
1945
return false;
@@ -22,8 +48,55 @@ bool ZigbeeAnalog::addAnalogInput() {
2248
return true;
2349
}
2450

51+
// Check esp_zigbee_zcl_analog_input.h for application type values
52+
bool ZigbeeAnalog::setAnalogInputApplication(uint32_t application_type) {
53+
if (!(_analog_clusters & ANALOG_INPUT)) {
54+
log_e("Analog Input cluster not added");
55+
return false;
56+
}
57+
58+
// Add the Analog Input group ID (0x00) to the application type
59+
uint32_t application_type_value = (ESP_ZB_ZCL_AI_GROUP_ID << 24) | application_type;
60+
61+
esp_zb_attribute_list_t *analog_input_cluster =
62+
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
63+
esp_err_t ret = esp_zb_cluster_update_attr(analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_APPLICATION_TYPE_ID, (void *)&application_type_value);
64+
if (ret != ESP_OK) {
65+
log_e("Failed to set AI application type: 0x%x: %s", ret, esp_err_to_name(ret));
66+
return false;
67+
}
68+
return true;
69+
}
70+
2571
bool ZigbeeAnalog::addAnalogOutput() {
26-
esp_err_t ret = esp_zb_cluster_list_add_analog_output_cluster(_cluster_list, esp_zb_analog_output_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
72+
esp_zb_attribute_list_t *esp_zb_analog_output_cluster = esp_zb_analog_output_cluster_create(NULL);
73+
74+
// Create default description for Analog Output
75+
char default_description[] = "\x0D"
76+
"Analog Output";
77+
uint32_t application_type = 0x00000000 | (ESP_ZB_ZCL_AO_GROUP_ID << 24);
78+
float resolution = 1; // Default resolution of 1
79+
80+
esp_err_t ret =
81+
esp_zb_analog_output_cluster_add_attr(esp_zb_analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_DESCRIPTION_ID, (void *)default_description);
82+
if (ret != ESP_OK) {
83+
log_e("Failed to add description attribute: 0x%x: %s", ret, esp_err_to_name(ret));
84+
return false;
85+
}
86+
87+
ret = esp_zb_analog_output_cluster_add_attr(esp_zb_analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_APPLICATION_TYPE_ID, (void *)&application_type);
88+
if (ret != ESP_OK) {
89+
log_e("Failed to add application type attribute: 0x%x: %s", ret, esp_err_to_name(ret));
90+
return false;
91+
}
92+
93+
ret = esp_zb_analog_output_cluster_add_attr(esp_zb_analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_RESOLUTION_ID, (void *)&resolution);
94+
if (ret != ESP_OK) {
95+
log_e("Failed to add resolution attribute: 0x%x: %s", ret, esp_err_to_name(ret));
96+
return false;
97+
}
98+
99+
ret = esp_zb_cluster_list_add_analog_output_cluster(_cluster_list, esp_zb_analog_output_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
27100
if (ret != ESP_OK) {
28101
log_e("Failed to add Analog Output cluster: 0x%x: %s", ret, esp_err_to_name(ret));
29102
return false;
@@ -32,6 +105,26 @@ bool ZigbeeAnalog::addAnalogOutput() {
32105
return true;
33106
}
34107

108+
// Check esp_zigbee_zcl_analog_output.h for application type values
109+
bool ZigbeeAnalog::setAnalogOutputApplication(uint32_t application_type) {
110+
if (!(_analog_clusters & ANALOG_OUTPUT)) {
111+
log_e("Analog Output cluster not added");
112+
return false;
113+
}
114+
115+
// Add the Analog Output group ID (0x00) to the application type
116+
uint32_t application_type_value = (ESP_ZB_ZCL_AO_GROUP_ID << 24) | application_type;
117+
118+
esp_zb_attribute_list_t *analog_output_cluster =
119+
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
120+
esp_err_t ret = esp_zb_cluster_update_attr(analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_APPLICATION_TYPE_ID, (void *)&application_type_value);
121+
if (ret != ESP_OK) {
122+
log_e("Failed to set AO application type: 0x%x: %s", ret, esp_err_to_name(ret));
123+
return false;
124+
}
125+
return true;
126+
}
127+
35128
//set attribute method -> method overridden in child class
36129
void ZigbeeAnalog::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) {
37130
if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT) {
@@ -120,4 +213,126 @@ bool ZigbeeAnalog::setAnalogInputReporting(uint16_t min_interval, uint16_t max_i
120213
return true;
121214
}
122215

216+
bool ZigbeeAnalog::setAnalogInputDescription(const char *description) {
217+
if (!(_analog_clusters & ANALOG_INPUT)) {
218+
log_e("Analog Input cluster not added");
219+
return false;
220+
}
221+
222+
// Allocate a new array of size length + 2 (1 for the length, 1 for null terminator)
223+
char zb_description[ZB_MAX_NAME_LENGTH + 2];
224+
225+
// Convert description to ZCL string
226+
size_t description_length = strlen(description);
227+
if (description_length > ZB_MAX_NAME_LENGTH) {
228+
log_e("Description is too long");
229+
return false;
230+
}
231+
232+
// Get and check the analog input cluster
233+
esp_zb_attribute_list_t *analog_input_cluster =
234+
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
235+
if (analog_input_cluster == nullptr) {
236+
log_e("Failed to get analog input cluster");
237+
return false;
238+
}
239+
240+
// Store the length as the first element
241+
zb_description[0] = static_cast<char>(description_length); // Cast size_t to char
242+
// Use memcpy to copy the characters to the result array
243+
memcpy(zb_description + 1, description, description_length);
244+
// Null-terminate the array
245+
zb_description[description_length + 1] = '\0';
246+
247+
// Update the description attribute
248+
esp_err_t ret = esp_zb_cluster_update_attr(analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_DESCRIPTION_ID, (void *)zb_description);
249+
if (ret != ESP_OK) {
250+
log_e("Failed to set description: 0x%x: %s", ret, esp_err_to_name(ret));
251+
return false;
252+
}
253+
return true;
254+
}
255+
256+
bool ZigbeeAnalog::setAnalogOutputDescription(const char *description) {
257+
if (!(_analog_clusters & ANALOG_OUTPUT)) {
258+
log_e("Analog Output cluster not added");
259+
return false;
260+
}
261+
262+
// Allocate a new array of size length + 2 (1 for the length, 1 for null terminator)
263+
char zb_description[ZB_MAX_NAME_LENGTH + 2];
264+
265+
// Convert description to ZCL string
266+
size_t description_length = strlen(description);
267+
if (description_length > ZB_MAX_NAME_LENGTH) {
268+
log_e("Description is too long");
269+
return false;
270+
}
271+
272+
// Get and check the analog output cluster
273+
esp_zb_attribute_list_t *analog_output_cluster =
274+
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
275+
if (analog_output_cluster == nullptr) {
276+
log_e("Failed to get analog output cluster");
277+
return false;
278+
}
279+
280+
// Store the length as the first element
281+
zb_description[0] = static_cast<char>(description_length); // Cast size_t to char
282+
// Use memcpy to copy the characters to the result array
283+
memcpy(zb_description + 1, description, description_length);
284+
// Null-terminate the array
285+
zb_description[description_length + 1] = '\0';
286+
287+
// Update the description attribute
288+
esp_err_t ret = esp_zb_cluster_update_attr(analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_DESCRIPTION_ID, (void *)zb_description);
289+
if (ret != ESP_OK) {
290+
log_e("Failed to set description: 0x%x: %s", ret, esp_err_to_name(ret));
291+
return false;
292+
}
293+
return true;
294+
}
295+
296+
bool ZigbeeAnalog::setAnalogInputResolution(float resolution) {
297+
if (!(_analog_clusters & ANALOG_INPUT)) {
298+
log_e("Analog Input cluster not added");
299+
return false;
300+
}
301+
302+
esp_zb_attribute_list_t *analog_input_cluster =
303+
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
304+
if (analog_input_cluster == nullptr) {
305+
log_e("Failed to get analog input cluster");
306+
return false;
307+
}
308+
309+
esp_err_t ret = esp_zb_cluster_update_attr(analog_input_cluster, ESP_ZB_ZCL_ATTR_ANALOG_INPUT_RESOLUTION_ID, (void *)&resolution);
310+
if (ret != ESP_OK) {
311+
log_e("Failed to set resolution: 0x%x: %s", ret, esp_err_to_name(ret));
312+
return false;
313+
}
314+
return true;
315+
}
316+
317+
bool ZigbeeAnalog::setAnalogOutputResolution(float resolution) {
318+
if (!(_analog_clusters & ANALOG_OUTPUT)) {
319+
log_e("Analog Output cluster not added");
320+
return false;
321+
}
322+
323+
esp_zb_attribute_list_t *analog_output_cluster =
324+
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
325+
if (analog_output_cluster == nullptr) {
326+
log_e("Failed to get analog output cluster");
327+
return false;
328+
}
329+
330+
esp_err_t ret = esp_zb_cluster_update_attr(analog_output_cluster, ESP_ZB_ZCL_ATTR_ANALOG_OUTPUT_RESOLUTION_ID, (void *)&resolution);
331+
if (ret != ESP_OK) {
332+
log_e("Failed to set resolution: 0x%x: %s", ret, esp_err_to_name(ret));
333+
return false;
334+
}
335+
return true;
336+
}
337+
123338
#endif // CONFIG_ZB_ENABLED

‎libraries/Zigbee/src/ep/ZigbeeAnalog.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ class ZigbeeAnalog : public ZigbeeEP {
3131
bool addAnalogInput();
3232
bool addAnalogOutput();
3333

34+
// Set the application type and description for the analog input
35+
bool setAnalogInputApplication(uint32_t application_type); // Check esp_zigbee_zcl_analog_input.h for application type values
36+
bool setAnalogInputDescription(const char *description);
37+
bool setAnalogInputResolution(float resolution);
38+
39+
// Set the application type and description for the analog output
40+
bool setAnalogOutputApplication(uint32_t application_type); // Check esp_zigbee_zcl_analog_output.h for application type values
41+
bool setAnalogOutputDescription(const char *description);
42+
bool setAnalogOutputResolution(float resolution);
43+
3444
// Use to set a cb function to be called on analog output change
3545
void onAnalogOutputChange(void (*callback)(float analog)) {
3646
_on_analog_output_change = callback;
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#include "ZigbeeBinary.h"
2+
#if CONFIG_ZB_ENABLED
3+
4+
ZigbeeBinary::ZigbeeBinary(uint8_t endpoint) : ZigbeeEP(endpoint) {
5+
_device_id = ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID;
6+
7+
//Create basic binary sensor clusters without configuration
8+
_cluster_list = esp_zb_zcl_cluster_list_create();
9+
esp_zb_cluster_list_add_basic_cluster(_cluster_list, esp_zb_basic_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
10+
esp_zb_cluster_list_add_identify_cluster(_cluster_list, esp_zb_identify_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
11+
12+
_ep_config = {.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID, .app_device_version = 0};
13+
}
14+
15+
bool ZigbeeBinary::addBinaryInput() {
16+
esp_zb_attribute_list_t *esp_zb_binary_input_cluster = esp_zb_binary_input_cluster_create(NULL);
17+
18+
// Create default description for Binary Input
19+
char default_description[] = "\x0C"
20+
"Binary Input";
21+
uint32_t application_type = 0x00000000 | (0x03 << 24); // Group ID 0x03
22+
23+
esp_err_t ret = esp_zb_binary_input_cluster_add_attr(esp_zb_binary_input_cluster, ESP_ZB_ZCL_ATTR_BINARY_INPUT_DESCRIPTION_ID, (void *)default_description);
24+
if (ret != ESP_OK) {
25+
log_e("Failed to add description attribute: 0x%x: %s", ret, esp_err_to_name(ret));
26+
return false;
27+
}
28+
29+
ret = esp_zb_binary_input_cluster_add_attr(esp_zb_binary_input_cluster, ESP_ZB_ZCL_ATTR_BINARY_INPUT_APPLICATION_TYPE_ID, (void *)&application_type);
30+
if (ret != ESP_OK) {
31+
log_e("Failed to add application type attribute: 0x%x: %s", ret, esp_err_to_name(ret));
32+
return false;
33+
}
34+
35+
ret = esp_zb_cluster_list_add_binary_input_cluster(_cluster_list, esp_zb_binary_input_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
36+
if (ret != ESP_OK) {
37+
log_e("Failed to add Binary Input cluster: 0x%x: %s", ret, esp_err_to_name(ret));
38+
return false;
39+
}
40+
_binary_clusters |= BINARY_INPUT;
41+
return true;
42+
}
43+
44+
// Check Zigbee Cluster Specification 3.14.11.19.4 Binary Inputs (BI) Types for application type values
45+
bool ZigbeeBinary::setBinaryInputApplication(uint32_t application_type) {
46+
if (!(_binary_clusters & BINARY_INPUT)) {
47+
log_e("Binary Input cluster not added");
48+
return false;
49+
}
50+
51+
// Add the Binary Input group ID (0x03) to the application type
52+
uint32_t application_type_value = (0x03 << 24) | application_type;
53+
54+
esp_zb_attribute_list_t *binary_input_cluster =
55+
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_BINARY_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
56+
esp_err_t ret = esp_zb_cluster_update_attr(binary_input_cluster, ESP_ZB_ZCL_ATTR_BINARY_INPUT_APPLICATION_TYPE_ID, (void *)&application_type_value);
57+
if (ret != ESP_OK) {
58+
log_e("Failed to set Binary Input application type: 0x%x: %s", ret, esp_err_to_name(ret));
59+
return false;
60+
}
61+
return true;
62+
}
63+
64+
bool ZigbeeBinary::setBinaryInput(bool input) {
65+
esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS;
66+
if (!(_binary_clusters & BINARY_INPUT)) {
67+
log_e("Binary Input cluster not added");
68+
return false;
69+
}
70+
log_d("Setting binary input to %d", input);
71+
esp_zb_lock_acquire(portMAX_DELAY);
72+
ret = esp_zb_zcl_set_attribute_val(
73+
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_BINARY_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_BINARY_INPUT_PRESENT_VALUE_ID, &input, false
74+
);
75+
esp_zb_lock_release();
76+
if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) {
77+
log_e("Failed to set binary input: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret));
78+
return false;
79+
}
80+
return true;
81+
}
82+
83+
bool ZigbeeBinary::reportBinaryInput() {
84+
/* Send report attributes command */
85+
esp_zb_zcl_report_attr_cmd_t report_attr_cmd;
86+
report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
87+
report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_BINARY_INPUT_PRESENT_VALUE_ID;
88+
report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI;
89+
report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_BINARY_INPUT;
90+
report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint;
91+
report_attr_cmd.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC;
92+
93+
esp_zb_lock_acquire(portMAX_DELAY);
94+
esp_err_t ret = esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd);
95+
esp_zb_lock_release();
96+
if (ret != ESP_OK) {
97+
log_e("Failed to send Binary Input report: 0x%x: %s", ret, esp_err_to_name(ret));
98+
return false;
99+
}
100+
log_v("Binary Input report sent");
101+
return true;
102+
}
103+
104+
bool ZigbeeBinary::setBinaryInputDescription(const char *description) {
105+
if (!(_binary_clusters & BINARY_INPUT)) {
106+
log_e("Binary Input cluster not added");
107+
return false;
108+
}
109+
110+
// Allocate a new array of size length + 2 (1 for the length, 1 for null terminator)
111+
char zb_description[ZB_MAX_NAME_LENGTH + 2];
112+
113+
// Convert description to ZCL string
114+
size_t description_length = strlen(description);
115+
if (description_length > ZB_MAX_NAME_LENGTH) {
116+
log_e("Description is too long");
117+
return false;
118+
}
119+
120+
// Get and check the binary input cluster
121+
esp_zb_attribute_list_t *binary_input_cluster =
122+
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_BINARY_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
123+
if (binary_input_cluster == nullptr) {
124+
log_e("Failed to get binary input cluster");
125+
return false;
126+
}
127+
128+
// Store the length as the first element
129+
zb_description[0] = static_cast<char>(description_length); // Cast size_t to char
130+
// Use memcpy to copy the characters to the result array
131+
memcpy(zb_description + 1, description, description_length);
132+
// Null-terminate the array
133+
zb_description[description_length + 1] = '\0';
134+
135+
// Update the description attribute
136+
esp_err_t ret = esp_zb_cluster_update_attr(binary_input_cluster, ESP_ZB_ZCL_ATTR_BINARY_INPUT_DESCRIPTION_ID, (void *)zb_description);
137+
if (ret != ESP_OK) {
138+
log_e("Failed to set description: 0x%x: %s", ret, esp_err_to_name(ret));
139+
return false;
140+
}
141+
return true;
142+
}
143+
144+
#endif // CONFIG_ZB_ENABLED
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/* Class of Zigbee Binary sensor endpoint inherited from common EP class */
2+
3+
#pragma once
4+
5+
#include "soc/soc_caps.h"
6+
#include "sdkconfig.h"
7+
#if CONFIG_ZB_ENABLED
8+
9+
#include "ZigbeeEP.h"
10+
#include "ha/esp_zigbee_ha_standard.h"
11+
12+
//enum for bits set to check what analog cluster were added
13+
enum zigbee_binary_clusters {
14+
BINARY_INPUT = 1,
15+
BINARY_OUTPUT = 2
16+
};
17+
18+
// HVAC application types for Binary Input (more can be found in Zigbee Cluster Specification 3.14.11.19.4 Binary Inputs (BI) Types)
19+
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_BOILER_STATUS 0x00000003 // Type 0x00, Index 0x0003
20+
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_CHILLER_STATUS 0x00000013 // Type 0x00, Index 0x0013
21+
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_OCCUPANCY 0x00000031 // Type 0x00, Index 0x0031
22+
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_FAN_STATUS 0x00000035 // Type 0x00, Index 0x0035
23+
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_FILTER_STATUS 0x00000036 // Type 0x00, Index 0x0036
24+
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_HEATING_ALARM 0x0000003E // Type 0x00, Index 0x003E
25+
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_COOLING_ALARM 0x0000001D // Type 0x00, Index 0x001D
26+
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_UNIT_ENABLE 0x00000090 // Type 0x00, Index 0x0090
27+
#define BINARY_INPUT_APPLICATION_TYPE_HVAC_OTHER 0x0000FFFF // Type 0x00, Index 0xFFFF
28+
29+
// Security application types for Binary Input
30+
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_GLASS_BREAKAGE_DETECTION_0 0x01000000 // Type 0x01, Index 0x0000
31+
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_INTRUSION_DETECTION 0x01000001 // Type 0x01, Index 0x0001
32+
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_MOTION_DETECTION 0x01000002 // Type 0x01, Index 0x0002
33+
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_GLASS_BREAKAGE_DETECTION_1 0x01000003 // Type 0x01, Index 0x0003
34+
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_ZONE_ARMED 0x01000004 // Type 0x01, Index 0x0004
35+
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_GLASS_BREAKAGE_DETECTION_2 0x01000005 // Type 0x01, Index 0x0005
36+
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_SMOKE_DETECTION 0x01000006 // Type 0x01, Index 0x0006
37+
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_CARBON_DIOXIDE_DETECTION 0x01000007 // Type 0x01, Index 0x0007
38+
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_HEAT_DETECTION 0x01000008 // Type 0x01, Index 0x0008
39+
#define BINARY_INPUT_APPLICATION_TYPE_SECURITY_OTHER 0x0100FFFF // Type 0x01, Index 0xFFFF
40+
41+
typedef struct zigbee_binary_cfg_s {
42+
esp_zb_basic_cluster_cfg_t basic_cfg;
43+
esp_zb_identify_cluster_cfg_t identify_cfg;
44+
// esp_zb_binary_output_cluster_cfg_t binary_output_cfg;
45+
esp_zb_binary_input_cluster_cfg_t binary_input_cfg;
46+
} zigbee_binary_cfg_t;
47+
48+
class ZigbeeBinary : public ZigbeeEP {
49+
public:
50+
ZigbeeBinary(uint8_t endpoint);
51+
~ZigbeeBinary() {}
52+
53+
// Add binary cluster
54+
bool addBinaryInput();
55+
// bool addBinaryOutput();
56+
57+
// Set the application type and description for the binary input
58+
bool setBinaryInputApplication(uint32_t application_type); // Check esp_zigbee_zcl_binary_input.h for application type values
59+
bool setBinaryInputDescription(const char *description);
60+
61+
// Set the application type and description for the binary output
62+
// bool setBinaryOutputApplication(uint32_t application_type); // Check esp_zigbee_zcl_binary_output.h for application type values
63+
// bool setBinaryOutputDescription(const char *description);
64+
65+
// Use to set a cb function to be called on binary output change
66+
// void onBinaryOutputChange(void (*callback)(bool binary_output)) {
67+
// _on_binary_output_change = callback;
68+
// }
69+
70+
// Set the binary input value
71+
bool setBinaryInput(bool input);
72+
73+
// Report Binary Input value
74+
bool reportBinaryInput();
75+
76+
private:
77+
// void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override;
78+
79+
// void (*_on_binary_output_change)(bool);
80+
// void binaryOutputChanged(bool binary_output);
81+
82+
uint8_t _binary_clusters;
83+
};
84+
85+
#endif // CONFIG_ZB_ENABLED

0 commit comments

Comments
 (0)
Please sign in to comment.