Skip to content

Commit 07d6a5a

Browse files
lboueP-R-O-C-H-Ypre-commit-ci-lite[bot]
authored
feat(Zigbee): Adding Zigbee Wind speed sensor endpoint (#10455)
* Create ZigbeeWindSpeedSensor.h * Create ZigbeeWindSpeedSensor.cpp * Update ZigbeeWindSpeedSensor.cpp * Update ZigbeeWindSpeedSensor.cpp * Create ZigbeeWindSpeedSensor.ino * Update ZigbeeWindSpeedSensor.ino * Update ZigbeeWindSpeedSensor.ino * Create ci.json * Rename ZigbeeWindSpeedSensor.ino to Zigbee_Wind_Speed_Sensor.ino * Rename ci.json to ci.json * Update CMakeLists.txt * Update Zigbee_Wind_Speed_Sensor.ino * Update Zigbee_Wind_Speed_Sensor.ino * Update Zigbee_Wind_Speed_Sensor.ino * Update ZigbeeWindSpeedSensor.cpp * Update ZigbeeWindSpeedSensor.cpp Use ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID device id * feat(zigbee): Add windspeed sensor endpoint * Update Zigbee.h Add ZigbeeWindSpeedSensor.h * update example * add missing sdkconfig include * add readme * ci(pre-commit): Apply automatic fixes * Update README.md --------- Co-authored-by: Jan Procházka <[email protected]> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
1 parent bda7c48 commit 07d6a5a

File tree

7 files changed

+342
-0
lines changed

7 files changed

+342
-0
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ set(ARDUINO_LIBRARY_Zigbee_SRCS
298298
libraries/Zigbee/src/ep/ZigbeeAnalog.cpp
299299
libraries/Zigbee/src/ep/ZigbeeRangeExtender.cpp
300300
libraries/Zigbee/src/ep/ZigbeeGateway.cpp
301+
libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.cpp
301302
)
302303

303304
set(ARDUINO_LIBRARY_BLE_SRCS
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Zigbee Wind Speed Sensor Integration with HomeAssistant ZHA
2+
3+
This guide provides a workaround for integrating a Zigbee Wind Speed Sensor with HomeAssistant using the ZHA integration. Since the wind speed cluster is not natively supported, we will use the ZHA Toolkit from HACS to read the wind speed attribute and store it in a helper variable.
4+
## Alternative Option: Creating a Custom Quirk
5+
6+
For advanced users, a more robust solution is to create a custom quirk for your Zigbee Wind Speed Sensor. This approach involves writing a custom device handler that directly supports the wind speed cluster, providing a more seamless integration with HomeAssistant.
7+
8+
Creating a custom quirk can be complex and requires familiarity with Python and the Zigbee protocol. However, it offers greater flexibility and control over your device's behavior.
9+
10+
For more information and guidance on creating custom quirks, visit the [ZHA Device Handlers repository](https://github.com/zigpy/zha-device-handlers/).
11+
12+
## Prerequisites
13+
14+
- HomeAssistant installed and running
15+
- Zigbee Wind Speed Sensor paired with HomeAssistant ZHA
16+
- HACS (Home Assistant Community Store) installed. For more information, visit [HACS](https://hacs.xyz)
17+
18+
## Steps
19+
20+
### 1. Install ZHA Toolkit
21+
22+
1. Open HomeAssistant.
23+
2. Navigate to HACS > Integrations.
24+
3. Search for "ZHA Toolkit - Service for advanced Zigbee Usage" and install it. For more information, visit the [ZHA Toolkit repository](https://github.com/mdeweerd/zha-toolkit).
25+
4. Restart HomeAssistant to apply changes.
26+
27+
### 2. Create a Helper Variable
28+
29+
1. Go to Configuration -> Devices & Services -> Helpers.
30+
2. Click on "Add Helper" and select "Number".
31+
3. Name the helper (e.g., `wind_speed`), set the minimum and maximum values, and save it.
32+
33+
### 3. Create an Automation
34+
35+
1. Go to Configuration > Automations & Scenes.
36+
2. Click on "Add Automation" and choose "Start with an empty automation".
37+
3. Set a name for the automation (e.g., `Read Wind Speed`).
38+
4. Add a trigger:
39+
- Trigger Type: Time Pattern
40+
- Every: 30 seconds
41+
5. Add an action (Then do):
42+
- Action Type: ZHA Toolkit: Read Attribute
43+
- Setup the action:
44+
```yaml
45+
action: zha_toolkit.attr_read
46+
metadata: {}
47+
data:
48+
ieee: f0:f5:bd:ff:fe:0e:61:30 #set device IEEE address
49+
endpoint: 10 #set windspeed device endpoint
50+
cluster: 1035 #use this windspeed cluster
51+
attribute: 0 #read measurement value
52+
state_id: input_number.wind_speed #save to created helper variable
53+
state_value_template: value/100 #use correct value format (convert u16 to float)
54+
```
55+
6. Save the automation.
56+
57+
## Conclusion
58+
59+
By following these steps, you can successfully integrate your Zigbee Wind Speed Sensor with HomeAssistant using the ZHA integration and ZHA Toolkit. The wind speed readings will be updated every 30 seconds and stored in the helper variable for use in your HomeAssistant setup.
60+
The helper variable `wind_speed` is now an entity in HomeAssistant. You can use this entity to display the wind speed on your dashboard or in other automations.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright 2024 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 windspeed sensor.
17+
*
18+
* The example demonstrates how to use Zigbee library to create a end device wind speed sensor.
19+
* The wind speed sensor is a Zigbee end device, which is controlled by a Zigbee coordinator.
20+
*
21+
* Proper Zigbee mode must be selected in Tools->Zigbee mode
22+
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
23+
*
24+
* Please check the README.md for instructions and more detailed description.
25+
*
26+
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
27+
*/
28+
29+
#ifndef ZIGBEE_MODE_ED
30+
#error "Zigbee coordinator mode is not selected in Tools->Zigbee mode"
31+
#endif
32+
33+
#include "Zigbee.h"
34+
35+
#define BUTTON_PIN 9 //Boot button for C6/H2
36+
#define WIND_SPEED_SENSOR_ENDPOINT_NUMBER 10
37+
38+
ZigbeeWindSpeedSensor zbWindSpeedSensor = ZigbeeWindSpeedSensor(WIND_SPEED_SENSOR_ENDPOINT_NUMBER);
39+
40+
/************************ WindSpeed sensor *****************************/
41+
static void windspeed_sensor_value_update(void *arg) {
42+
for (;;) {
43+
// Read wind speed sensor value (simulated now by temperature sensor)
44+
float windspeed = temperatureRead();
45+
log_v("Wind speed sensor value: %.2fm/s", windspeed);
46+
// Update windspeed value in Windspeed sensor EP
47+
zbWindSpeedSensor.setWindSpeed(windspeed);
48+
delay(1000);
49+
}
50+
}
51+
52+
/********************* Arduino functions **************************/
53+
void setup() {
54+
Serial.begin(115200);
55+
while (!Serial) {
56+
delay(10);
57+
}
58+
59+
// Init button switch
60+
pinMode(BUTTON_PIN, INPUT);
61+
62+
// Optional: set Zigbee device name and model
63+
zbWindSpeedSensor.setManufacturerAndModel("Espressif", "ZigbeeWindSpeedSensor");
64+
65+
// Set minimum and maximum windspeed measurement value in m/s
66+
zbWindSpeedSensor.setMinMaxValue(0, 50);
67+
68+
// Set tolerance for windspeed measurement in m/s (lowest possible value is 0.01 m/s)
69+
zbWindSpeedSensor.setTolerance(1);
70+
71+
// Add endpoint to Zigbee Core
72+
Zigbee.addEndpoint(&zbWindSpeedSensor);
73+
74+
Serial.println("Starting Zigbee...");
75+
// When all EPs are registered, start Zigbee in End Device mode
76+
if (!Zigbee.begin()) {
77+
Serial.println("Zigbee failed to start!");
78+
Serial.println("Rebooting...");
79+
ESP.restart();
80+
} else {
81+
Serial.println("Zigbee started successfully!");
82+
}
83+
Serial.println("Connecting to network");
84+
while (!Zigbee.connected()) {
85+
Serial.print(".");
86+
delay(100);
87+
}
88+
Serial.println();
89+
90+
// Start Wind speed sensor reading task
91+
xTaskCreate(windspeed_sensor_value_update, "wind_speed_sensor_update", 2048, NULL, 10, NULL);
92+
93+
// Set reporting interval for windspeed measurement in seconds, must be called after Zigbee.begin()
94+
// min_interval and max_interval in seconds, delta (WindSpeed change in m/s)
95+
// if min = 1 and max = 0, reporting is sent only when windspeed changes by delta
96+
// if min = 0 and max = 10, reporting is sent every 10 seconds or windspeed changes by delta
97+
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of windspeed change
98+
zbWindSpeedSensor.setReporting(1, 0, 1);
99+
}
100+
101+
void loop() {
102+
// Checking button for factory reset
103+
if (digitalRead(BUTTON_PIN) == LOW) { // Push button pressed
104+
// Key debounce handling
105+
delay(100);
106+
int startTime = millis();
107+
while (digitalRead(BUTTON_PIN) == LOW) {
108+
delay(50);
109+
if ((millis() - startTime) > 3000) {
110+
// If key pressed for more than 3secs, factory reset Zigbee and reboot
111+
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
112+
delay(1000);
113+
Zigbee.factoryReset();
114+
}
115+
}
116+
zbWindSpeedSensor.reportWindSpeed();
117+
}
118+
delay(100);
119+
}
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

+1
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@
2525
#include "ep/ZigbeeVibrationSensor.h"
2626
#include "ep/ZigbeeRangeExtender.h"
2727
#include "ep/ZigbeeGateway.h"
28+
#include "ep/ZigbeeWindSpeedSensor.h"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#include "ZigbeeWindSpeedSensor.h"
2+
#if CONFIG_ZB_ENABLED
3+
4+
esp_zb_cluster_list_t *zigbee_wind_speed_sensor_clusters_create(zigbee_wind_speed_sensor_cfg_t *wind_speed_sensor) {
5+
esp_zb_basic_cluster_cfg_t *basic_cfg = wind_speed_sensor ? &(wind_speed_sensor->basic_cfg) : NULL;
6+
esp_zb_identify_cluster_cfg_t *identify_cfg = wind_speed_sensor ? &(wind_speed_sensor->identify_cfg) : NULL;
7+
esp_zb_wind_speed_measurement_cluster_cfg_t *wind_speed_cfg = wind_speed_sensor ? &(wind_speed_sensor->wind_speed_meas_cfg) : NULL;
8+
esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create();
9+
esp_zb_cluster_list_add_basic_cluster(cluster_list, esp_zb_basic_cluster_create(basic_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
10+
esp_zb_cluster_list_add_identify_cluster(cluster_list, esp_zb_identify_cluster_create(identify_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
11+
esp_zb_cluster_list_add_wind_speed_measurement_cluster(
12+
cluster_list, esp_zb_wind_speed_measurement_cluster_create(wind_speed_cfg), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE
13+
);
14+
return cluster_list;
15+
}
16+
17+
// There is no device_id for wind speed sensor, we use a generic one
18+
ZigbeeWindSpeedSensor::ZigbeeWindSpeedSensor(uint8_t endpoint) : ZigbeeEP(endpoint) {
19+
_device_id = ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID;
20+
21+
zigbee_wind_speed_sensor_cfg_t windspeed_sensor_cfg = ZIGBEE_DEFAULT_WIND_SPEED_SENSOR_CONFIG();
22+
_cluster_list = zigbee_wind_speed_sensor_clusters_create(&windspeed_sensor_cfg);
23+
24+
_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};
25+
}
26+
27+
static uint16_t zb_windspeed_to_u16(float windspeed) {
28+
return (uint16_t)(windspeed * 100);
29+
}
30+
31+
void ZigbeeWindSpeedSensor::setMinMaxValue(float min, float max) {
32+
uint16_t zb_min = zb_windspeed_to_u16(min);
33+
uint16_t zb_max = zb_windspeed_to_u16(max);
34+
esp_zb_attribute_list_t *windspeed_measure_cluster =
35+
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_WIND_SPEED_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
36+
//
37+
esp_zb_cluster_update_attr(windspeed_measure_cluster, ESP_ZB_ZCL_ATTR_WIND_SPEED_MEASUREMENT_MIN_MEASURED_VALUE_ID, (void *)&zb_min);
38+
esp_zb_cluster_update_attr(windspeed_measure_cluster, ESP_ZB_ZCL_ATTR_WIND_SPEED_MEASUREMENT_MAX_MEASURED_VALUE_ID, (void *)&zb_max);
39+
}
40+
41+
void ZigbeeWindSpeedSensor::setTolerance(float tolerance) {
42+
// Convert tolerance to ZCL uint16_t
43+
uint16_t zb_tolerance = zb_windspeed_to_u16(tolerance);
44+
esp_zb_attribute_list_t *windspeed_measure_cluster =
45+
esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_WIND_SPEED_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
46+
esp_zb_wind_speed_measurement_cluster_add_attr(windspeed_measure_cluster, ESP_ZB_ZCL_ATTR_WIND_SPEED_MEASUREMENT_TOLERANCE_ID, (void *)&zb_tolerance);
47+
}
48+
49+
void ZigbeeWindSpeedSensor::setReporting(uint16_t min_interval, uint16_t max_interval, float delta) {
50+
esp_zb_zcl_reporting_info_t reporting_info;
51+
memset(&reporting_info, 0, sizeof(esp_zb_zcl_reporting_info_t));
52+
reporting_info.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV;
53+
reporting_info.ep = _endpoint;
54+
reporting_info.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_WIND_SPEED_MEASUREMENT;
55+
reporting_info.cluster_role = ESP_ZB_ZCL_CLUSTER_SERVER_ROLE;
56+
reporting_info.attr_id = ESP_ZB_ZCL_ATTR_WIND_SPEED_MEASUREMENT_MEASURED_VALUE_ID;
57+
reporting_info.u.send_info.min_interval = min_interval;
58+
reporting_info.u.send_info.max_interval = max_interval;
59+
reporting_info.u.send_info.def_min_interval = min_interval;
60+
reporting_info.u.send_info.def_max_interval = max_interval;
61+
reporting_info.u.send_info.delta.u16 = (uint16_t)(delta * 100); // Convert delta to ZCL uint16_t
62+
reporting_info.dst.profile_id = ESP_ZB_AF_HA_PROFILE_ID;
63+
reporting_info.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC;
64+
esp_zb_lock_acquire(portMAX_DELAY);
65+
esp_zb_zcl_update_reporting_info(&reporting_info);
66+
esp_zb_lock_release();
67+
}
68+
69+
void ZigbeeWindSpeedSensor::setWindSpeed(float windspeed) {
70+
uint16_t zb_windspeed = zb_windspeed_to_u16(windspeed);
71+
log_v("Updating windspeed sensor value...");
72+
/* Update windspeed sensor measured value */
73+
log_d("Setting windspeed to %d", zb_windspeed);
74+
esp_zb_lock_acquire(portMAX_DELAY);
75+
esp_zb_zcl_set_attribute_val(
76+
_endpoint, ESP_ZB_ZCL_CLUSTER_ID_WIND_SPEED_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WIND_SPEED_MEASUREMENT_MEASURED_VALUE_ID,
77+
&zb_windspeed, false
78+
);
79+
esp_zb_lock_release();
80+
}
81+
82+
void ZigbeeWindSpeedSensor::reportWindSpeed() {
83+
/* Send report attributes command */
84+
esp_zb_zcl_report_attr_cmd_t report_attr_cmd;
85+
report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
86+
report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_WIND_SPEED_MEASUREMENT_MEASURED_VALUE_ID;
87+
report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI;
88+
report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_WIND_SPEED_MEASUREMENT;
89+
report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint;
90+
report_attr_cmd.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC;
91+
92+
esp_zb_lock_acquire(portMAX_DELAY);
93+
esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd);
94+
esp_zb_lock_release();
95+
log_v("Wind speed measurement report sent");
96+
}
97+
98+
#endif //CONFIG_ZB_ENABLED
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/* Class of Zigbee WindSpeed 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+
#define ZIGBEE_DEFAULT_WIND_SPEED_SENSOR_CONFIG() \
13+
{ \
14+
.basic_cfg = \
15+
{ \
16+
.zcl_version = ESP_ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE, \
17+
.power_source = ESP_ZB_ZCL_BASIC_POWER_SOURCE_DEFAULT_VALUE, \
18+
}, \
19+
.identify_cfg = \
20+
{ \
21+
.identify_time = ESP_ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE, \
22+
}, \
23+
.wind_speed_meas_cfg = { \
24+
.measured_value = ESP_ZB_ZCL_WIND_SPEED_MEASUREMENT_MEASURED_VALUE_DEFAULT, \
25+
.min_measured_value = ESP_ZB_ZCL_WIND_SPEED_MEASUREMENT_MIN_MEASURED_VALUE_DEFAULT, \
26+
.max_measured_value = ESP_ZB_ZCL_WIND_SPEED_MEASUREMENT_MAX_MEASURED_VALUE_DEFAULT, \
27+
}, \
28+
}
29+
30+
typedef struct zigbee_wind_speed_sensor_cfg_s {
31+
esp_zb_basic_cluster_cfg_t basic_cfg; /*!< Basic cluster configuration, @ref esp_zb_basic_cluster_cfg_s */
32+
esp_zb_identify_cluster_cfg_t identify_cfg; /*!< Identify cluster configuration, @ref esp_zb_identify_cluster_cfg_s */
33+
esp_zb_wind_speed_measurement_cluster_cfg_t
34+
wind_speed_meas_cfg; /*!< Wind speed measurement cluster configuration, @ref esp_zb_wind_speed_measurement_cluster_cfg_s */
35+
} zigbee_wind_speed_sensor_cfg_t;
36+
37+
class ZigbeeWindSpeedSensor : public ZigbeeEP {
38+
public:
39+
ZigbeeWindSpeedSensor(uint8_t endpoint);
40+
~ZigbeeWindSpeedSensor() {}
41+
42+
// Set the WindSpeed value in 0,01 m/s
43+
void setWindSpeed(float value);
44+
45+
// Set the min and max value for the WindSpeed sensor
46+
void setMinMaxValue(float min, float max);
47+
48+
// Set the tolerance value for the WindSpeed sensor
49+
void setTolerance(float tolerance);
50+
51+
// Set the reporting interval for WindSpeed measurement in seconds and delta
52+
void setReporting(uint16_t min_interval, uint16_t max_interval, float delta);
53+
void reportWindSpeed();
54+
};
55+
56+
#endif //CONFIG_ZB_ENABLED

0 commit comments

Comments
 (0)