From 1d2c6c1b9e0dcbec3a79aa1e94d197c5cb69bc89 Mon Sep 17 00:00:00 2001 From: hennikul Date: Tue, 28 Jan 2025 18:38:39 +0100 Subject: [PATCH 1/7] feat(zigbee): Added Endpoint for Window Covering (#10913) --- libraries/Zigbee/src/Zigbee.h | 1 + libraries/Zigbee/src/ZigbeeEP.h | 1 + libraries/Zigbee/src/ZigbeeHandlers.cpp | 36 +++- .../Zigbee/src/ep/ZigbeeWindowCovering.cpp | 181 ++++++++++++++++++ .../Zigbee/src/ep/ZigbeeWindowCovering.h | 140 ++++++++++++++ 5 files changed, 353 insertions(+), 6 deletions(-) create mode 100644 libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp create mode 100644 libraries/Zigbee/src/ep/ZigbeeWindowCovering.h diff --git a/libraries/Zigbee/src/Zigbee.h b/libraries/Zigbee/src/Zigbee.h index cf58dc8d5d8..117b9af51b9 100644 --- a/libraries/Zigbee/src/Zigbee.h +++ b/libraries/Zigbee/src/Zigbee.h @@ -18,3 +18,4 @@ #include "ep/ZigbeeFlowSensor.h" #include "ep/ZigbeeOccupancySensor.h" #include "ep/ZigbeeCarbonDioxideSensor.h" +#include "ep/ZigbeeWindowCovering.h" diff --git a/libraries/Zigbee/src/ZigbeeEP.h b/libraries/Zigbee/src/ZigbeeEP.h index a5e9efa4283..d0496b42895 100644 --- a/libraries/Zigbee/src/ZigbeeEP.h +++ b/libraries/Zigbee/src/ZigbeeEP.h @@ -104,6 +104,7 @@ class ZigbeeEP { virtual void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute) {}; virtual void zbReadBasicCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented virtual void zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message); + virtual void zbWindowCoveringMovementCmd(const esp_zb_zcl_window_covering_movement_message_t *message) {}; void onIdentify(void (*callback)(uint16_t)) { _on_identify = callback; diff --git a/libraries/Zigbee/src/ZigbeeHandlers.cpp b/libraries/Zigbee/src/ZigbeeHandlers.cpp index 881d7ca0c37..d1677d9d1dc 100644 --- a/libraries/Zigbee/src/ZigbeeHandlers.cpp +++ b/libraries/Zigbee/src/ZigbeeHandlers.cpp @@ -10,18 +10,20 @@ static esp_err_t zb_attribute_reporting_handler(const esp_zb_zcl_report_attr_mes static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_resp_message_t *message); static esp_err_t zb_configure_report_resp_handler(const esp_zb_zcl_cmd_config_report_resp_message_t *message); static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_message_t *message); +static esp_err_t zb_window_covering_movement_resp_handler(const esp_zb_zcl_window_covering_movement_message_t *message); // Zigbee action handlers [[maybe_unused]] static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void *message) { esp_err_t ret = ESP_OK; switch (callback_id) { - case ESP_ZB_CORE_SET_ATTR_VALUE_CB_ID: ret = zb_attribute_set_handler((esp_zb_zcl_set_attr_value_message_t *)message); break; - case ESP_ZB_CORE_REPORT_ATTR_CB_ID: ret = zb_attribute_reporting_handler((esp_zb_zcl_report_attr_message_t *)message); break; - case ESP_ZB_CORE_CMD_READ_ATTR_RESP_CB_ID: ret = zb_cmd_read_attr_resp_handler((esp_zb_zcl_cmd_read_attr_resp_message_t *)message); break; - case ESP_ZB_CORE_CMD_REPORT_CONFIG_RESP_CB_ID: ret = zb_configure_report_resp_handler((esp_zb_zcl_cmd_config_report_resp_message_t *)message); break; - case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID: ret = zb_cmd_default_resp_handler((esp_zb_zcl_cmd_default_resp_message_t *)message); break; - default: log_w("Receive unhandled Zigbee action(0x%x) callback", callback_id); break; + case ESP_ZB_CORE_SET_ATTR_VALUE_CB_ID: ret = zb_attribute_set_handler((esp_zb_zcl_set_attr_value_message_t *)message); break; + case ESP_ZB_CORE_REPORT_ATTR_CB_ID: ret = zb_attribute_reporting_handler((esp_zb_zcl_report_attr_message_t *)message); break; + case ESP_ZB_CORE_CMD_READ_ATTR_RESP_CB_ID: ret = zb_cmd_read_attr_resp_handler((esp_zb_zcl_cmd_read_attr_resp_message_t *)message); break; + case ESP_ZB_CORE_CMD_REPORT_CONFIG_RESP_CB_ID: ret = zb_configure_report_resp_handler((esp_zb_zcl_cmd_config_report_resp_message_t *)message); break; + case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID: ret = zb_cmd_default_resp_handler((esp_zb_zcl_cmd_default_resp_message_t *)message); break; + case ESP_ZB_CORE_WINDOW_COVERING_MOVEMENT_CB_ID: ret = zb_window_covering_movement_resp_handler((esp_zb_zcl_window_covering_movement_message_t *)message); break; + default: log_w("Receive unhandled Zigbee action(0x%x) callback", callback_id); break; } return ret; } @@ -138,4 +140,26 @@ static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_m return ESP_OK; } +static esp_err_t zb_window_covering_movement_resp_handler(const esp_zb_zcl_window_covering_movement_message_t *message) { + if (!message) { + log_e("Empty message"); + } + if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Received message: error status(%d)", message->info.status); + } + + log_v( + "Received message: endpoint(%d), cluster(0x%x), command(0x%x), payload(%d)", message->info.dst_endpoint, message->info.cluster, message->command, + message->payload + ); + + // List through all Zigbee EPs and call the callback function, with the message + for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { + if (message->info.dst_endpoint == (*it)->getEndpoint()) { + (*it)->zbWindowCoveringMovementCmd(message); //method zbWindowCoveringMovementCmd must be implemented in specific EP class + } + } + return ESP_OK; +} + #endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp new file mode 100644 index 00000000000..4fad5a6ef37 --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp @@ -0,0 +1,181 @@ + +#include "ZigbeeWindowCovering.h" +#if SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED + +#include "esp_zigbee_cluster.h" + +ZigbeeWindowCovering::ZigbeeWindowCovering(uint8_t endpoint) : ZigbeeEP(endpoint) { + _device_id = ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID; + + zigbee_window_covering_cfg_t window_covering_cfg = ZIGBEE_DEFAULT_WINDOW_COVERING_CONFIG(); + _cluster_list = zigbee_window_covering_clusters_create(&window_covering_cfg); + + _ep_config = {.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID, .app_device_version = 0}; + + // set default values + _current_lift_percentage = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_DEFAULT_VALUE; + _current_tilt_percentage = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_DEFAULT_VALUE; + _installed_open_limit_lift = ESP_ZB_ZCL_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_DEFAULT_VALUE; + _installed_closed_limit_lift = ESP_ZB_ZCL_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_LIFT_DEFAULT_VALUE; + _installed_open_limit_tilt = ESP_ZB_ZCL_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_TILT_DEFAULT_VALUE; + _installed_closed_limit_tilt = ESP_ZB_ZCL_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_TILT_DEFAULT_VALUE; + _current_position_lift = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_LIFT_DEFAULT_VALUE; + _physical_closed_limit_lift = ESP_ZB_ZCL_WINDOW_COVERING_PHYSICAL_CLOSED_LIMIT_LIFT_DEFAULT_VALUE; + _physical_closed_limit_tilt = ESP_ZB_ZCL_WINDOW_COVERING_PHY_CLOSED_LIMIT_TILT_DEFAULT_VALUE; +} + +// set attribute method -> method overridden in child class +void ZigbeeWindowCovering::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) { + //check the data and call right method + log_w("Received message ignored. Cluster ID: %d not supported for Window Covering", message->info.cluster); +} + +void ZigbeeWindowCovering::zbWindowCoveringMovementCmd(const esp_zb_zcl_window_covering_movement_message_t *message) { + // check the data and call right method + if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING) { + if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_UP_OPEN) { + goToLiftPercentage(0); + return; + } else if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_DOWN_CLOSE) { + goToLiftPercentage(100); + return; + } else if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_STOP) { + stop(); + return; + } else if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_GO_TO_LIFT_PERCENTAGE) { + if (_current_lift_percentage != message->payload.percentage_lift_value) { + _current_lift_percentage = message->payload.percentage_lift_value; + goToLiftPercentage(_current_lift_percentage); + } + return; + } else { + log_w("Received message ignored. Command: %d not supported for Window Covering", message->command); + } + } else { + log_w("Received message ignored. Cluster ID: %d not supported for Window Covering", message->info.cluster); + } +} + +void ZigbeeWindowCovering::goToLiftPercentage(uint8_t lift_percentage) { + if (_on_go_to_lift_percentage) { + _on_go_to_lift_percentage(lift_percentage); + } +} + +void ZigbeeWindowCovering::stop() { + if (_on_stop) { + _on_stop(); + } +} + +void ZigbeeWindowCovering::setLiftPosition(uint16_t lift_position) { + // Update all attributes + _current_position_lift = lift_position; + _current_lift_percentage = ((lift_position - _installed_open_limit_lift) * 100) / (_installed_closed_limit_lift - _installed_open_limit_lift); + + log_v("Updating window covering lift position to %d (%d%)", _current_position_lift, _current_lift_percentage); + // set lift state + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID, &_current_position_lift, false + ); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, &_current_lift_percentage, false + ); + + esp_zb_zcl_report_attr_cmd_t report_attr_cmd; + report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI; + report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING; + report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + + report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID; + esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd); + report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID; + esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd); + + esp_zb_lock_release(); +} + +void ZigbeeWindowCovering::setCoveringType(WindowCoveringType covering_type) { + esp_zb_attribute_list_t *window_covering_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_WINDOW_COVERING_TYPE_ID, (void *)&covering_type); +} + +void ZigbeeWindowCovering::setConfigStatus(bool operational, bool online, bool commands_reversed, + bool lift_closed_loop, bool tilt_closed_loop, + bool lift_encoder_controlled, bool tilt_encoder_controlled) { + uint8_t config_status = (operational ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_OPERATIONAL : 0) + | (online ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_ONLINE : 0) + | (commands_reversed ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_REVERSE_COMMANDS : 0) + | (lift_closed_loop ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_LIFT_CONTROL_IS_CLOSED_LOOP : 0) + | (tilt_closed_loop ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_TILT_CONTROL_IS_CLOSED_LOOP : 0) + | (lift_encoder_controlled ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_LIFT_ENCODER_CONTROLLED : 0) + | (tilt_encoder_controlled ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_TILT_ENCODER_CONTROLLED : 0); + + log_v("Updating window covering config status to %d", config_status); + + esp_zb_attribute_list_t *window_covering_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_STATUS_ID, (void *)&config_status); +} + +void ZigbeeWindowCovering::setMode(bool motor_reversed, bool calibration_mode, bool maintenance_mode, bool leds_on) { + uint8_t mode = (motor_reversed ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_REVERSED_MOTOR_DIRECTION : 0) + | (calibration_mode ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_RUN_IN_CALIBRATION_MODE : 0) + | (maintenance_mode ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_MOTOR_IS_RUNNING_IN_MAINTENANCE_MODE : 0) + | (leds_on ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_LEDS_WILL_DISPLAY_FEEDBACK : 0); + + log_v("Updating window covering mode to %d", mode); + + esp_zb_attribute_list_t *window_covering_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_MODE_ID, (void *)&mode); +} + +void ZigbeeWindowCovering::setLimits(uint16_t installed_open_limit_lift, uint16_t installed_closed_limit_lift, + uint16_t installed_open_limit_tilt, uint16_t installed_closed_limit_tilt) { + _installed_open_limit_lift = installed_open_limit_lift; + _installed_closed_limit_lift = installed_closed_limit_lift; + _physical_closed_limit_lift = installed_closed_limit_lift; + _installed_open_limit_tilt = installed_open_limit_tilt; + _installed_closed_limit_tilt = installed_closed_limit_tilt; + _physical_closed_limit_tilt = installed_closed_limit_tilt; + + esp_zb_attribute_list_t *window_covering_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_ID, (void *)&_installed_open_limit_lift); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_LIFT_ID, (void *)&_installed_closed_limit_lift); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_TILT_ID, (void *)&_installed_open_limit_tilt); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_TILT_ID, (void *)&_installed_closed_limit_tilt); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_PHYSICAL_CLOSED_LIMIT_LIFT_ID, (void *)&_physical_closed_limit_lift); + esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_PHY_CLOSED_LIMIT_TILT_ID, (void *)&_physical_closed_limit_tilt); +} + +esp_zb_cluster_list_t *ZigbeeWindowCovering::zigbee_window_covering_clusters_create(zigbee_window_covering_cfg_t *window_covering_cfg) { + esp_zb_attribute_list_t *esp_zb_basic_cluster = esp_zb_basic_cluster_create(&window_covering_cfg->basic_cfg); + esp_zb_attribute_list_t *esp_zb_identify_cluster = esp_zb_identify_cluster_create(&window_covering_cfg->identify_cfg); + esp_zb_attribute_list_t *esp_zb_groups_cluster = esp_zb_groups_cluster_create(&window_covering_cfg->groups_cfg); + esp_zb_attribute_list_t *esp_zb_scenes_cluster = esp_zb_scenes_cluster_create(&window_covering_cfg->scenes_cfg); + esp_zb_attribute_list_t *esp_zb_window_covering_cluster = esp_zb_window_covering_cluster_create(&window_covering_cfg->window_covering_cfg); + + // ------------------------------ Create cluster list ------------------------------ + esp_zb_cluster_list_t *esp_zb_cluster_list = esp_zb_zcl_cluster_list_create(); + esp_zb_cluster_list_add_basic_cluster(esp_zb_cluster_list, esp_zb_basic_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_identify_cluster(esp_zb_cluster_list, esp_zb_identify_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_groups_cluster(esp_zb_cluster_list, esp_zb_groups_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_scenes_cluster(esp_zb_cluster_list, esp_zb_scenes_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_window_covering_cluster(esp_zb_cluster_list, esp_zb_window_covering_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, &_current_lift_percentage); + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_ID, &_installed_open_limit_lift); + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_LIFT_ID, &_installed_closed_limit_lift); + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_TILT_ID, &_installed_open_limit_tilt); + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_TILT_ID, &_installed_closed_limit_tilt); + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID, &_current_position_lift); + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_PHYSICAL_CLOSED_LIMIT_LIFT_ID, &_physical_closed_limit_lift); + return esp_zb_cluster_list; +} + +#endif // SOC_IEEE802154_SUPPORTED diff --git a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h new file mode 100644 index 00000000000..8752bc3c005 --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h @@ -0,0 +1,140 @@ +/* Class of Zigbee Window Covering endpoint inherited from common EP class */ + +#pragma once + +#include "soc/soc_caps.h" +#include "sdkconfig.h" +#if SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED + +#include "ZigbeeEP.h" +#include "ha/esp_zigbee_ha_standard.h" + +/** + * @brief Zigbee HA standard window covering device clusters. + * Added here as not supported by ESP Zigbee library. + * + * + */ +typedef struct zigbee_window_covering_cfg_s { + esp_zb_basic_cluster_cfg_t basic_cfg; /*!< Basic cluster configuration, @ref esp_zb_basic_cluster_cfg_s */ + esp_zb_identify_cluster_cfg_t identify_cfg; /*!< Identify cluster configuration, @ref esp_zb_identify_cluster_cfg_s */ + esp_zb_groups_cluster_cfg_t groups_cfg; /*!< Groups cluster configuration, @ref esp_zb_groups_cluster_cfg_s */ + esp_zb_scenes_cluster_cfg_t scenes_cfg; /*!< Scenes cluster configuration, @ref esp_zb_scenes_cluster_cfg_s */ + esp_zb_window_covering_cluster_cfg_t window_covering_cfg; /*!< Window covering cluster configuration, @ref esp_zb_window_covering_cluster_cfg_s */ +} zigbee_window_covering_cfg_t; + +enum WindowCoveringType { + ROLLERSHADE = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE, + ROLLERSHADE_2_MOTOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_2_MOTOR, + ROLLERSHADE_EXTERIOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_EXTERIOR, + ROLLERSHADE_EXTERIOR_2_MOTOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_EXTERIOR_2_MOTOR, + DRAPERY = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_DRAPERY, + AWNING = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_AWNING, + SHUTTER = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_SHUTTER, + BLIND_TILT_ONLY = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_TILT_BLIND_TILT_ONLY, + BLIND_LIFT_AND_TILT = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_TILT_BLIND_LIFT_AND_TILT, + PROJECTOR_SCREEN = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_PROJECTOR_SCREEN, +}; + +/** + * @brief Zigbee HA standard window covering device default config value. + * Added here as not supported by ESP Zigbee library. + * + */ +// clang-format off +#define ZIGBEE_DEFAULT_WINDOW_COVERING_CONFIG() \ + { \ + .basic_cfg = \ + { \ + .zcl_version = ESP_ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE, \ + .power_source = ESP_ZB_ZCL_BASIC_POWER_SOURCE_DEFAULT_VALUE, \ + }, \ + .identify_cfg = \ + { \ + .identify_time = ESP_ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE, \ + }, \ + .groups_cfg = \ + { \ + .groups_name_support_id = ESP_ZB_ZCL_GROUPS_NAME_SUPPORT_DEFAULT_VALUE, \ + }, \ + .scenes_cfg = \ + { \ + .scenes_count = ESP_ZB_ZCL_SCENES_SCENE_COUNT_DEFAULT_VALUE, \ + .current_scene = ESP_ZB_ZCL_SCENES_CURRENT_SCENE_DEFAULT_VALUE, \ + .current_group = ESP_ZB_ZCL_SCENES_CURRENT_GROUP_DEFAULT_VALUE, \ + .scene_valid = ESP_ZB_ZCL_SCENES_SCENE_VALID_DEFAULT_VALUE, \ + .name_support = ESP_ZB_ZCL_SCENES_NAME_SUPPORT_DEFAULT_VALUE, \ + }, \ + .window_covering_cfg = \ + { \ + .covering_type = ESP_ZB_ZCL_WINDOW_COVERING_WINDOW_COVERING_TYPE_DEFAULT_VALUE, \ + .covering_status = ESP_ZB_ZCL_WINDOW_COVERING_CONFIG_STATUS_DEFAULT_VALUE, \ + .covering_mode = ESP_ZB_ZCL_WINDOW_COVERING_MODE_DEFAULT_VALUE, \ + }, \ + } +// clang-format on + +class ZigbeeWindowCovering : public ZigbeeEP { +public: + ZigbeeWindowCovering(uint8_t endpoint); + ~ZigbeeWindowCovering(); + + void onGoToLiftPercentage(void (*callback)(uint8_t)) { + _on_go_to_lift_percentage = callback; + } + + void onStop(void (*callback)()) { + _on_stop = callback; + } + + void setLiftPosition(uint16_t lift_position); + + // Set the window covering type, see esp_zb_zcl_window_covering_window_covering_type_t + void setCoveringType(WindowCoveringType covering_type); + + // Set window covering config/status, see esp_zb_zcl_window_covering_config_status_t + void setConfigStatus(bool operational, bool online, bool commands_reversed, + bool lift_closed_loop, bool tilt_closed_loop, + bool lift_encoder_controlled, bool tilt_encoder_controlled); + + // Set configuration mode of window covering, see esp_zb_zcl_window_covering_mode_t + void setMode(bool motor_reversed, bool calibration_mode, bool maintenance_mode, bool leds_on); + + // Set limits of motion, see esp_zb_zcl_window_covering_info_attr_t + void setLimits(uint16_t installed_open_limit_lift, uint16_t installed_closed_limit_lift, + uint16_t installed_open_limit_tilt, uint16_t installed_closed_limit_tilt); +private: + void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override; + void zbWindowCoveringMovementCmd(const esp_zb_zcl_window_covering_movement_message_t *message) override; + + void goToLiftPercentage(uint8_t); + void stop(); + + // callback function to be called on lift percentage change (lift percentage) + void (*_on_go_to_lift_percentage)(uint8_t); + void (*_on_stop)(); + + /** + * @brief Create a standard HA window covering cluster list. + * Added here as not supported by ESP Zigbee library. + * + * @note This contains basic, identify, groups, scenes, window_covering, as server side. + * @param[in] window_covering_cfg Configuration parameters for this cluster lists defined by @ref zigbee_window_covering_cfg_t + * + * @return Pointer to cluster list @ref esp_zb_cluster_list_s + * + */ + esp_zb_cluster_list_t *zigbee_window_covering_clusters_create(zigbee_window_covering_cfg_t *window_covering_cfg); + + uint8_t _current_lift_percentage; + uint8_t _current_tilt_percentage; + uint16_t _current_position_lift; + uint16_t _installed_open_limit_lift; + uint16_t _installed_closed_limit_lift; + uint16_t _installed_open_limit_tilt; + uint16_t _installed_closed_limit_tilt; + uint16_t _physical_closed_limit_lift; + uint16_t _physical_closed_limit_tilt; +}; + +#endif // SOC_IEEE802154_SUPPORTED From 5f8c8e2662af84830cc01a526c024f91cc2bcc8f Mon Sep 17 00:00:00 2001 From: hennikul Date: Thu, 30 Jan 2025 20:32:11 +0100 Subject: [PATCH 2/7] Added example Sketch. --- .../examples/Zigbee_Window_Covering/README.md | 69 +++++++++++ .../Zigbee_Window_Covering.ino.ino | 113 ++++++++++++++++++ .../examples/Zigbee_Window_Covering/ci.json | 6 + 3 files changed, 188 insertions(+) create mode 100644 libraries/Zigbee/examples/Zigbee_Window_Covering/README.md create mode 100644 libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino.ino create mode 100644 libraries/Zigbee/examples/Zigbee_Window_Covering/ci.json diff --git a/libraries/Zigbee/examples/Zigbee_Window_Covering/README.md b/libraries/Zigbee/examples/Zigbee_Window_Covering/README.md new file mode 100644 index 00000000000..6f6d59d90ed --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Window_Covering/README.md @@ -0,0 +1,69 @@ +# Arduino-ESP32 Widnow Covering Example + +This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) window covering or port. + +To see if the communication with your Zigbee network works, use the Serial monitor and watch for output there. + +# Supported Targets + +Currently, this example supports the following targets. + +| Supported Targets | ESP32-C6 | ESP32-H2 | +| ----------------- | -------- | -------- | + +## Hardware Required + +* A USB cable for power supply and programming +* Board (ESP32-H2 or ESP32-C6) as Zigbee end device and upload the Zigbee_Window_Covering example +* Zigbee network / coordinator (Other board with switch examples or Zigbee2mqtt or ZigbeeHomeAssistant like application) + +### Configure the Project + +#### 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 End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)` +* Select Tools / USB CDC On Boot: "Enabled" +* 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`. + +## Troubleshooting + +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. +You can do the following: + +* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`. +* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack. + +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) diff --git a/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino.ino b/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino.ino new file mode 100644 index 00000000000..bd731a213f8 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino.ino @@ -0,0 +1,113 @@ +#ifndef ZIGBEE_MODE_ED +#error "Zigbee end device mode is not selected in Tools->Zigbee mode" +#endif + +#include "ZigbeeCore.h" +#include "ep/ZigbeeWindowCovering.h" + + +#define ZIGBEE_COVERING_ENDPOINT 10 +#define BUTTON_PIN 9 // ESP32-C6/H2 Boot button + +#define MAX_LIFT 200 // centimeters from open position +#define MIN_LIFT 0 + +uint8_t currentLift = MAX_LIFT; +uint8_t reportedLift = MIN_LIFT; + +ZigbeeWindowCovering zbCovering = ZigbeeWindowCovering(ZIGBEE_COVERING_ENDPOINT); + +void setup() { + Serial.begin(115200); + + Serial.println("ESP32S3 initialization completed!"); + + // Init button for factory reset + pinMode(BUTTON_PIN, INPUT_PULLUP); + + //Optional: set Zigbee device name and model + zbCovering.setManufacturerAndModel("Espriff", "Arduino"); + zbCovering.setCoveringType(ROLLERSHADE); + + // operational, online, not commands_reversed, closed_loop, encoder_controlled + zbCovering.setConfigStatus(true, true, false, true, true, true, true); + + // not motor_reversed, calibration_mode, not maintenance_mode, not leds_on + zbCovering.setMode(false, true, false, false); + + zbCovering.setLimits(MIN_LIFT, MAX_LIFT, 0, 0); + + // Set callback function for light change + zbCovering.onGoToLiftPercentage(goToLiftPercentage); + zbCovering.onStop(stopMotor); + + //Add endpoint to Zigbee Core + Serial.println("Adding ZigbeeWindowCovering endpoint to Zigbee Core"); + Zigbee.addEndpoint(&zbCovering); + + // When all EPs are registered, start Zigbee. By default acts as ZIGBEE_END_DEVICE + Serial.println("Calling Zigbee.begin()"); + if (!Zigbee.begin()) { + Serial.println("Zigbee failed to start!"); + Serial.println("Rebooting..."); + ESP.restart(); + } + Serial.println("Connecting to network"); + while (!Zigbee.connected()) { + Serial.print("."); + delay(100); + } + reportState(); + Serial.println(); +} + +void loop() { + // Checking button for factory reset + if (digitalRead(BUTTON_PIN) == LOW) { // Push button pressed + // Key debounce handling + delay(100); + int startTime = millis(); + while (digitalRead(BUTTON_PIN) == LOW) { + delay(50); + if ((millis() - startTime) > 3000) { + // If key pressed for more than 3secs, factory reset Zigbee and reboot + Serial.printf("Resetting Zigbee to factory settings, reboot.\n"); + Zigbee.factoryReset(); + delay(30000); + } + } + } + + reportState(); + + delay(500); +} + +void goToLiftPercentage(uint8_t liftPercentage) { + /* This is where you would trigger your motor to go towards liftPercentage, currentLift should + be updated until liftPercentage has been reached in order to provide feedback to controller */ + + // Our cover updates instantly! + currentLift = (liftPercentage * MAX_LIFT) / 100; + + Serial.printf("New requsted lift from Zigbee: %d (%d)\n", currentLift, liftPercentage); +} + +void reportState() { + if (reportedLift == currentLift) { + return; + } + + Serial.printf("Reporting new state: %d (%d)\n", currentLift, reportedLift); + + reportedLift = currentLift; + + // This is where current state is reported to controller, to provide feedback on where the shade is at currently + zbCovering.setLiftPosition((uint16_t) reportedLift); +} + + +void stopMotor() { + // Motor can be stopped while moving cover toward current target + Serial.println("Stopping motor"); +} \ No newline at end of file diff --git a/libraries/Zigbee/examples/Zigbee_Window_Covering/ci.json b/libraries/Zigbee/examples/Zigbee_Window_Covering/ci.json new file mode 100644 index 00000000000..7b7ccef8ed7 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Window_Covering/ci.json @@ -0,0 +1,6 @@ +{ + "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", + "requires": [ + "CONFIG_SOC_IEEE802154_SUPPORTED=y" + ] +} From cfb7ca4e21cf14d2d17d10eb6add1fba54adb9b7 Mon Sep 17 00:00:00 2001 From: hennikul Date: Sat, 1 Feb 2025 09:01:10 +0100 Subject: [PATCH 3/7] Added window covering to CMakeLists.txt. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fd23d14d77..7ccdbbdfb1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -290,6 +290,7 @@ set(ARDUINO_LIBRARY_Zigbee_SRCS libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp + libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp ) set(ARDUINO_LIBRARY_BLE_SRCS From de2cd4a2bc7bd4ae7bd06358165c8628f8e85551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Tue, 4 Feb 2025 14:58:54 +0100 Subject: [PATCH 4/7] feat(zigbee): Window covering tilt support and fixes --- .../Zigbee_Window_Covering.ino | 200 ++++++++++++ .../Zigbee_Window_Covering.ino.ino | 113 ------- .../Zigbee/src/ep/ZigbeeWindowCovering.cpp | 297 ++++++++++++------ .../Zigbee/src/ep/ZigbeeWindowCovering.h | 110 +++---- 4 files changed, 452 insertions(+), 268 deletions(-) create mode 100644 libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino delete mode 100644 libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino.ino diff --git a/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino b/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino new file mode 100644 index 00000000000..1871ba22e94 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino @@ -0,0 +1,200 @@ +// 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 Zigbee Window Covering. + * + * The example demonstrates how to use Zigbee library to create a end device window covering device. + * The window covering is a Zigbee end device, which is moving the blinds (lift+tilt) and reporting + * its current position to the Zigbee network. + * + * Use setCoveingType() to set the type of covering (blind, shade, etc.). + * + * The example also demonstrates how to use the button to manually control the lift position. + * + * 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 hennikul and Jan Procházka (https://github.com/P-R-O-C-H-Y/) + */ + +#ifndef ZIGBEE_MODE_ED +#error "Zigbee end device mode is not selected in Tools->Zigbee mode" +#endif + +#include "ZigbeeCore.h" +#include "ep/ZigbeeWindowCovering.h" + + +#define ZIGBEE_COVERING_ENDPOINT 10 +#define BUTTON_PIN 9 // ESP32-C6/H2 Boot button + +#define MAX_LIFT 200 // centimeters from open position (0-900) +#define MIN_LIFT 0 + +#define MAX_TILT 40 // centimeters from open position (0-900) +#define MIN_TILT 0 + +uint16_t currentLift = MAX_LIFT; +uint8_t currentLiftPercentage = 100; + +uint16_t currentTilt = MAX_TILT; +uint8_t currentTiltPercentage = 100; + +ZigbeeWindowCovering zbCovering = ZigbeeWindowCovering(ZIGBEE_COVERING_ENDPOINT); + +void setup() { + Serial.begin(115200); + + // Init button for factory reset + pinMode(BUTTON_PIN, INPUT_PULLUP); + + // Optional: set Zigbee device name and model + zbCovering.setManufacturerAndModel("Espressif", "WindowBlinds"); + + // Set proper covering type, it defines which attributes are available + zbCovering.setCoveringType(BLIND_LIFT_AND_TILT); + + // Set configuration: operational, online, not commands_reversed, lift / tilt closed_loop, lift / tilt encoder_controlled + zbCovering.setConfigStatus(true, true, false, true, true, true, true); + + // Set mode: not motor_reversed, calibration_mode, not maintenance_mode, not leds_on + zbCovering.setMode(false, true, false, false); + + // Set limits of motion + zbCovering.setLimits(MIN_LIFT, MAX_LIFT, MIN_TILT, MAX_TILT); + + // Set callback function for open, close, filt and tilt change, stop + zbCovering.onOpen(fullOpen); + zbCovering.onClose(fullClose); + zbCovering.onGoToLiftPercentage(goToLiftPercentage); + zbCovering.onGoToTiltPercentage(goToTiltPercentage); + zbCovering.onStop(stopMotor); + + // Add endpoint to Zigbee Core + Serial.println("Adding ZigbeeWindowCovering endpoint to Zigbee Core"); + Zigbee.addEndpoint(&zbCovering); + + // When all EPs are registered, start Zigbee. By default acts as ZIGBEE_END_DEVICE + Serial.println("Calling Zigbee.begin()"); + if (!Zigbee.begin()) { + 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(); + + // Set initial position + zbCovering.setLiftPercentage(currentLiftPercentage); + zbCovering.setTiltPercentage(currentTiltPercentage); +} + +void loop() { + // Checking button for factory reset + if (digitalRead(BUTTON_PIN) == LOW) { // Push button pressed + // Key debounce handling + delay(100); + int startTime = millis(); + while (digitalRead(BUTTON_PIN) == LOW) { + delay(50); + if ((millis() - startTime) > 3000) { + // If key pressed for more than 3secs, factory reset Zigbee and reboot + Serial.printf("Resetting Zigbee to factory settings, reboot.\n"); + Zigbee.factoryReset(); + delay(30000); + } + } + // Manual lift controll simulation by pressing button + manualControl(); + } + delay(500); +} + +void fullOpen(){ + /* This is where you would trigger your motor to go to full open state, currentLift should + be updated until full open has been reached in order to provide feedback to controller of actual position + The stop can be always called, so the movement can be stopped at any time */ + + // Our cover updates instantly! + currentLift = MAX_LIFT; + currentLiftPercentage = 100; + Serial.println("Opening cover"); + // Update the current position + zbCovering.setLiftPercentage(currentLiftPercentage); +} + +void fullClose(){ + /* This is where you would trigger your motor to go to full close state, currentLift should + be updated until full close has been reached in order to provide feedback to controller of actual position + The stop can be always called, so the movement can be stopped at any time */ + + // Our cover updates instantly! + currentLift = MIN_LIFT; + currentLiftPercentage = 0; + Serial.println("Closing cover"); + // Update the current position + zbCovering.setLiftPercentage(currentLiftPercentage); +} + +void goToLiftPercentage(uint8_t liftPercentage) { + /* This is where you would trigger your motor to go towards liftPercentage, currentLift should + be updated until liftPercentage has been reached in order to provide feedback to controller */ + + // Our simulated cover updates instantly! + currentLift = (liftPercentage * MAX_LIFT) / 100; + currentLiftPercentage = liftPercentage; + Serial.printf("New requsted lift from Zigbee: %d (%d)\n", currentLift, liftPercentage); + + //Update the current position + zbCovering.setLiftPercentage(currentLiftPercentage); //or setLiftPosition() +} + +void goToTiltPercentage(uint8_t tiltPercentage) { + /* This is where you would trigger your motor to go towards tiltPercentage, currentTilt should + be updated until tiltPercentage has been reached in order to provide feedback to controller */ + + // Our simulated cover updates instantly! + currentTilt = (tiltPercentage * MAX_TILT) / 100; + currentTiltPercentage = tiltPercentage; + Serial.printf("New requsted tilt from Zigbee: %d (%d)\n", currentTilt, tiltPercentage); + + //Update the current position + zbCovering.setTiltPercentage(currentTiltPercentage); //or setTiltPosition() +} + + +void stopMotor() { + // Motor can be stopped while moving cover toward current target, when stopped the actual position should be updated + Serial.println("Stopping motor"); + // Update the current position of both lift and tilt + zbCovering.setLiftPercentage(currentLiftPercentage); + zbCovering.setTiltPercentage(currentTiltPercentage); +} + +void manualControl() { + // Simulate lift percentage move by increasing it by 20% each time + currentLiftPercentage += 20; + if(currentLiftPercentage > 100) { + currentLiftPercentage = 0; + } + zbCovering.setLiftPercentage(currentLiftPercentage); + // Also setLiftPosition() can be used to set the exact position instead of percentage +} \ No newline at end of file diff --git a/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino.ino b/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino.ino deleted file mode 100644 index bd731a213f8..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino.ino +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef ZIGBEE_MODE_ED -#error "Zigbee end device mode is not selected in Tools->Zigbee mode" -#endif - -#include "ZigbeeCore.h" -#include "ep/ZigbeeWindowCovering.h" - - -#define ZIGBEE_COVERING_ENDPOINT 10 -#define BUTTON_PIN 9 // ESP32-C6/H2 Boot button - -#define MAX_LIFT 200 // centimeters from open position -#define MIN_LIFT 0 - -uint8_t currentLift = MAX_LIFT; -uint8_t reportedLift = MIN_LIFT; - -ZigbeeWindowCovering zbCovering = ZigbeeWindowCovering(ZIGBEE_COVERING_ENDPOINT); - -void setup() { - Serial.begin(115200); - - Serial.println("ESP32S3 initialization completed!"); - - // Init button for factory reset - pinMode(BUTTON_PIN, INPUT_PULLUP); - - //Optional: set Zigbee device name and model - zbCovering.setManufacturerAndModel("Espriff", "Arduino"); - zbCovering.setCoveringType(ROLLERSHADE); - - // operational, online, not commands_reversed, closed_loop, encoder_controlled - zbCovering.setConfigStatus(true, true, false, true, true, true, true); - - // not motor_reversed, calibration_mode, not maintenance_mode, not leds_on - zbCovering.setMode(false, true, false, false); - - zbCovering.setLimits(MIN_LIFT, MAX_LIFT, 0, 0); - - // Set callback function for light change - zbCovering.onGoToLiftPercentage(goToLiftPercentage); - zbCovering.onStop(stopMotor); - - //Add endpoint to Zigbee Core - Serial.println("Adding ZigbeeWindowCovering endpoint to Zigbee Core"); - Zigbee.addEndpoint(&zbCovering); - - // When all EPs are registered, start Zigbee. By default acts as ZIGBEE_END_DEVICE - Serial.println("Calling Zigbee.begin()"); - if (!Zigbee.begin()) { - Serial.println("Zigbee failed to start!"); - Serial.println("Rebooting..."); - ESP.restart(); - } - Serial.println("Connecting to network"); - while (!Zigbee.connected()) { - Serial.print("."); - delay(100); - } - reportState(); - Serial.println(); -} - -void loop() { - // Checking button for factory reset - if (digitalRead(BUTTON_PIN) == LOW) { // Push button pressed - // Key debounce handling - delay(100); - int startTime = millis(); - while (digitalRead(BUTTON_PIN) == LOW) { - delay(50); - if ((millis() - startTime) > 3000) { - // If key pressed for more than 3secs, factory reset Zigbee and reboot - Serial.printf("Resetting Zigbee to factory settings, reboot.\n"); - Zigbee.factoryReset(); - delay(30000); - } - } - } - - reportState(); - - delay(500); -} - -void goToLiftPercentage(uint8_t liftPercentage) { - /* This is where you would trigger your motor to go towards liftPercentage, currentLift should - be updated until liftPercentage has been reached in order to provide feedback to controller */ - - // Our cover updates instantly! - currentLift = (liftPercentage * MAX_LIFT) / 100; - - Serial.printf("New requsted lift from Zigbee: %d (%d)\n", currentLift, liftPercentage); -} - -void reportState() { - if (reportedLift == currentLift) { - return; - } - - Serial.printf("Reporting new state: %d (%d)\n", currentLift, reportedLift); - - reportedLift = currentLift; - - // This is where current state is reported to controller, to provide feedback on where the shade is at currently - zbCovering.setLiftPosition((uint16_t) reportedLift); -} - - -void stopMotor() { - // Motor can be stopped while moving cover toward current target - Serial.println("Stopping motor"); -} \ No newline at end of file diff --git a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp index 4fad5a6ef37..74058f3276f 100644 --- a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp @@ -2,102 +2,59 @@ #include "ZigbeeWindowCovering.h" #if SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED -#include "esp_zigbee_cluster.h" +esp_zb_cluster_list_t *ZigbeeWindowCovering::zigbee_window_covering_clusters_create(zigbee_window_covering_cfg_t *window_covering_cfg) { + esp_zb_attribute_list_t *esp_zb_basic_cluster = esp_zb_basic_cluster_create(&window_covering_cfg->basic_cfg); + esp_zb_attribute_list_t *esp_zb_identify_cluster = esp_zb_identify_cluster_create(&window_covering_cfg->identify_cfg); + esp_zb_attribute_list_t *esp_zb_groups_cluster = esp_zb_groups_cluster_create(&window_covering_cfg->groups_cfg); + esp_zb_attribute_list_t *esp_zb_scenes_cluster = esp_zb_scenes_cluster_create(&window_covering_cfg->scenes_cfg); + esp_zb_attribute_list_t *esp_zb_window_covering_cluster = esp_zb_window_covering_cluster_create(&window_covering_cfg->window_covering_cfg); -ZigbeeWindowCovering::ZigbeeWindowCovering(uint8_t endpoint) : ZigbeeEP(endpoint) { - _device_id = ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID; + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, &_current_lift_percentage); + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID, &_current_tilt_percentage); + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID, &_current_lift_position); + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_ID, &_current_lift_position); + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_ID, &_installed_open_limit_lift); + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_TILT_ID, &_installed_open_limit_tilt); + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_LIFT_ID, &_installed_closed_limit_lift); + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_TILT_ID, &_installed_closed_limit_tilt); + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_PHYSICAL_CLOSED_LIMIT_LIFT_ID, &_physical_closed_limit_lift); + esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_PHY_CLOSED_LIMIT_TILT_ID, &_physical_closed_limit_lift); - zigbee_window_covering_cfg_t window_covering_cfg = ZIGBEE_DEFAULT_WINDOW_COVERING_CONFIG(); - _cluster_list = zigbee_window_covering_clusters_create(&window_covering_cfg); + // ------------------------------ Create cluster list ------------------------------ + esp_zb_cluster_list_t *esp_zb_cluster_list = esp_zb_zcl_cluster_list_create(); + esp_zb_cluster_list_add_basic_cluster(esp_zb_cluster_list, esp_zb_basic_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_identify_cluster(esp_zb_cluster_list, esp_zb_identify_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_groups_cluster(esp_zb_cluster_list, esp_zb_groups_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_scenes_cluster(esp_zb_cluster_list, esp_zb_scenes_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_window_covering_cluster(esp_zb_cluster_list, esp_zb_window_covering_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); - _ep_config = {.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID, .app_device_version = 0}; + return esp_zb_cluster_list; +} - // set default values +ZigbeeWindowCovering::ZigbeeWindowCovering(uint8_t endpoint) : ZigbeeEP(endpoint) { + _device_id = ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID; + + // set default values for window covering attributes _current_lift_percentage = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_DEFAULT_VALUE; _current_tilt_percentage = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_DEFAULT_VALUE; _installed_open_limit_lift = ESP_ZB_ZCL_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_DEFAULT_VALUE; _installed_closed_limit_lift = ESP_ZB_ZCL_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_LIFT_DEFAULT_VALUE; _installed_open_limit_tilt = ESP_ZB_ZCL_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_TILT_DEFAULT_VALUE; _installed_closed_limit_tilt = ESP_ZB_ZCL_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_TILT_DEFAULT_VALUE; - _current_position_lift = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_LIFT_DEFAULT_VALUE; + _current_lift_position = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_LIFT_DEFAULT_VALUE; + _current_tilt_position = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_TILT_DEFAULT_VALUE; _physical_closed_limit_lift = ESP_ZB_ZCL_WINDOW_COVERING_PHYSICAL_CLOSED_LIMIT_LIFT_DEFAULT_VALUE; _physical_closed_limit_tilt = ESP_ZB_ZCL_WINDOW_COVERING_PHY_CLOSED_LIMIT_TILT_DEFAULT_VALUE; -} - -// set attribute method -> method overridden in child class -void ZigbeeWindowCovering::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) { - //check the data and call right method - log_w("Received message ignored. Cluster ID: %d not supported for Window Covering", message->info.cluster); -} - -void ZigbeeWindowCovering::zbWindowCoveringMovementCmd(const esp_zb_zcl_window_covering_movement_message_t *message) { - // check the data and call right method - if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING) { - if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_UP_OPEN) { - goToLiftPercentage(0); - return; - } else if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_DOWN_CLOSE) { - goToLiftPercentage(100); - return; - } else if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_STOP) { - stop(); - return; - } else if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_GO_TO_LIFT_PERCENTAGE) { - if (_current_lift_percentage != message->payload.percentage_lift_value) { - _current_lift_percentage = message->payload.percentage_lift_value; - goToLiftPercentage(_current_lift_percentage); - } - return; - } else { - log_w("Received message ignored. Command: %d not supported for Window Covering", message->command); - } - } else { - log_w("Received message ignored. Cluster ID: %d not supported for Window Covering", message->info.cluster); - } -} - -void ZigbeeWindowCovering::goToLiftPercentage(uint8_t lift_percentage) { - if (_on_go_to_lift_percentage) { - _on_go_to_lift_percentage(lift_percentage); - } -} - -void ZigbeeWindowCovering::stop() { - if (_on_stop) { - _on_stop(); - } -} - -void ZigbeeWindowCovering::setLiftPosition(uint16_t lift_position) { - // Update all attributes - _current_position_lift = lift_position; - _current_lift_percentage = ((lift_position - _installed_open_limit_lift) * 100) / (_installed_closed_limit_lift - _installed_open_limit_lift); - - log_v("Updating window covering lift position to %d (%d%)", _current_position_lift, _current_lift_percentage); - // set lift state - esp_zb_lock_acquire(portMAX_DELAY); - esp_zb_zcl_set_attribute_val( - _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID, &_current_position_lift, false - ); - esp_zb_zcl_set_attribute_val( - _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, &_current_lift_percentage, false - ); - esp_zb_zcl_report_attr_cmd_t report_attr_cmd; - report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; - report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI; - report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING; - report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint; - - report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID; - esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd); - report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID; - esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd); + // Create custom window covering configuration + zigbee_window_covering_cfg_t window_covering_cfg = ZIGBEE_DEFAULT_WINDOW_COVERING_CONFIG(); + _cluster_list = zigbee_window_covering_clusters_create(&window_covering_cfg); - esp_zb_lock_release(); + _ep_config = {.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID, .app_device_version = 0}; } -void ZigbeeWindowCovering::setCoveringType(WindowCoveringType covering_type) { +// Confiuration methods for window covering +void ZigbeeWindowCovering::setCoveringType(ZigbeeWindowCoveringType covering_type) { esp_zb_attribute_list_t *window_covering_cluster = esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_WINDOW_COVERING_TYPE_ID, (void *)&covering_type); @@ -153,29 +110,165 @@ void ZigbeeWindowCovering::setLimits(uint16_t installed_open_limit_lift, uint16_ esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_PHY_CLOSED_LIMIT_TILT_ID, (void *)&_physical_closed_limit_tilt); } -esp_zb_cluster_list_t *ZigbeeWindowCovering::zigbee_window_covering_clusters_create(zigbee_window_covering_cfg_t *window_covering_cfg) { - esp_zb_attribute_list_t *esp_zb_basic_cluster = esp_zb_basic_cluster_create(&window_covering_cfg->basic_cfg); - esp_zb_attribute_list_t *esp_zb_identify_cluster = esp_zb_identify_cluster_create(&window_covering_cfg->identify_cfg); - esp_zb_attribute_list_t *esp_zb_groups_cluster = esp_zb_groups_cluster_create(&window_covering_cfg->groups_cfg); - esp_zb_attribute_list_t *esp_zb_scenes_cluster = esp_zb_scenes_cluster_create(&window_covering_cfg->scenes_cfg); - esp_zb_attribute_list_t *esp_zb_window_covering_cluster = esp_zb_window_covering_cluster_create(&window_covering_cfg->window_covering_cfg); +// Callback for handling incoming messages and commands +void ZigbeeWindowCovering::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) { + //check the data and call right method + if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING) { + log_v("Received attribute id: 0x%x / data.type: 0x%x", message->attribute.id, message->attribute.data.type); + if (message->attribute.id == ESP_ZB_ZCL_ATTR_WINDOW_COVERING_MODE_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_8BITMAP) { + uint8_t mode = *(uint8_t *)message->attribute.data.value; + bool motor_reversed = mode & ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_REVERSED_MOTOR_DIRECTION; + bool calibration_mode = mode & ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_RUN_IN_CALIBRATION_MODE; + bool maintenance_mode = mode & ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_MOTOR_IS_RUNNING_IN_MAINTENANCE_MODE; + bool leds_on = mode & ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_LEDS_WILL_DISPLAY_FEEDBACK; + log_v("Updating window covering mode to motor reversed: %d, calibration mode: %d, maintenance mode: %d, leds on: %d", motor_reversed, calibration_mode, maintenance_mode, leds_on); + setMode(motor_reversed, calibration_mode, maintenance_mode, leds_on); + //Update Configuration status with motor reversed status + uint8_t config_status; + config_status = (*(uint8_t *)esp_zb_zcl_get_attribute(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_STATUS_ID)->data_p); + config_status = motor_reversed ? config_status | ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_REVERSE_COMMANDS : config_status & ~ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_REVERSE_COMMANDS; + log_v("Updating window covering config status to %d", config_status); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_STATUS_ID, &config_status, false + ); + esp_zb_lock_release(); + return; + } + } else { + log_w("Received message ignored. Cluster ID: %d not supported for Window Covering", message->info.cluster); + } +} - // ------------------------------ Create cluster list ------------------------------ - esp_zb_cluster_list_t *esp_zb_cluster_list = esp_zb_zcl_cluster_list_create(); - esp_zb_cluster_list_add_basic_cluster(esp_zb_cluster_list, esp_zb_basic_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); - esp_zb_cluster_list_add_identify_cluster(esp_zb_cluster_list, esp_zb_identify_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); - esp_zb_cluster_list_add_groups_cluster(esp_zb_cluster_list, esp_zb_groups_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); - esp_zb_cluster_list_add_scenes_cluster(esp_zb_cluster_list, esp_zb_scenes_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); - esp_zb_cluster_list_add_window_covering_cluster(esp_zb_cluster_list, esp_zb_window_covering_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); +void ZigbeeWindowCovering::zbWindowCoveringMovementCmd(const esp_zb_zcl_window_covering_movement_message_t *message) { + // check the data and call right method + if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING) { + if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_UP_OPEN) { + open(); + return; + } else if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_DOWN_CLOSE) { + close(); + return; + } else if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_STOP) { + stop(); + return; + } else if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_GO_TO_LIFT_PERCENTAGE) { + if (_current_lift_percentage != message->payload.percentage_lift_value) { + _current_lift_percentage = message->payload.percentage_lift_value; + goToLiftPercentage(_current_lift_percentage); + } + return; + } else if (message->command == ESP_ZB_ZCL_CMD_WINDOW_COVERING_GO_TO_TILT_PERCENTAGE) { + if (_current_tilt_percentage != message->payload.percentage_tilt_value) { + _current_tilt_percentage = message->payload.percentage_tilt_value; + goToTiltPercentage(_current_tilt_percentage); + } + } + else { + log_w("Received message ignored. Command: %d not supported for Window Covering", message->command); + } + } else { + log_w("Received message ignored. Cluster ID: %d not supported for Window Covering", message->info.cluster); + } +} - esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, &_current_lift_percentage); - esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_ID, &_installed_open_limit_lift); - esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_LIFT_ID, &_installed_closed_limit_lift); - esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_TILT_ID, &_installed_open_limit_tilt); - esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_TILT_ID, &_installed_closed_limit_tilt); - esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID, &_current_position_lift); - esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_PHYSICAL_CLOSED_LIMIT_LIFT_ID, &_physical_closed_limit_lift); - return esp_zb_cluster_list; +void ZigbeeWindowCovering::open() { + if (_on_open) { + _on_open(); + } +} + +void ZigbeeWindowCovering::close() { + if (_on_close) { + _on_close(); + } +} + +void ZigbeeWindowCovering::goToLiftPercentage(uint8_t lift_percentage) { + if (_on_go_to_lift_percentage) { + _on_go_to_lift_percentage(lift_percentage); + } +} + +void ZigbeeWindowCovering::goToTiltPercentage(uint8_t tilt_percentage) { + if (_on_go_to_tilt_percentage) { + _on_go_to_tilt_percentage(tilt_percentage); + } +} + +void ZigbeeWindowCovering::stop() { + if (_on_stop) { + _on_stop(); + } +} + +// Methods to control window covering from user application +void ZigbeeWindowCovering::setLiftPosition(uint16_t lift_position) { + // Update both lift attributes + _current_lift_position = lift_position; + _current_lift_percentage = ((lift_position - _installed_open_limit_lift) * 100) / (_installed_closed_limit_lift - _installed_open_limit_lift); + + log_v("Updating window covering lift position to %d (%d%)", _current_lift_position, _current_lift_percentage); + // set lift state + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID, &_current_lift_position, false + ); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, &_current_lift_percentage, false + ); + esp_zb_lock_release(); +} + +void ZigbeeWindowCovering::setLiftPercentage(uint8_t lift_percentage) { + // Update both lift attributes + _current_lift_percentage = lift_percentage; + _current_lift_position = _installed_open_limit_lift + ((_installed_closed_limit_lift - _installed_open_limit_lift) * lift_percentage) / 100; + + log_v("Updating window covering lift position to %d (%d%)", _current_lift_position, _current_lift_percentage); + // set lift state + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID, &_current_lift_position, false + ); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, &_current_lift_percentage, false + ); + esp_zb_lock_release(); +} + +void ZigbeeWindowCovering::setTiltPosition(uint16_t tilt_position) { + // Update both tilt attributes + _current_tilt_position = tilt_position; + _current_tilt_percentage = ((tilt_position - _installed_open_limit_tilt) * 100) / (_installed_closed_limit_tilt - _installed_open_limit_tilt); + + log_v("Updating window covering tilt position to %d (%d%)", _current_tilt_position, _current_tilt_percentage); + // set lift state + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_ID, &_current_tilt_position, false + ); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID, &_current_tilt_percentage, false + ); + esp_zb_lock_release(); +} + +void ZigbeeWindowCovering::setTiltPercentage(uint8_t tilt_percentage) { + // Update both tilt attributes + _current_tilt_percentage = tilt_percentage; + _current_tilt_position = _installed_open_limit_lift + ((_installed_closed_limit_tilt - _installed_open_limit_tilt) * tilt_percentage) / 100; + + log_v("Updating window covering tilt position to %d (%d%)", _current_tilt_position, _current_tilt_percentage); + // set lift state + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_ID, &_current_tilt_position, false + ); + esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID, &_current_tilt_percentage, false + ); + esp_zb_lock_release(); } -#endif // SOC_IEEE802154_SUPPORTED +#endif // SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h index 8752bc3c005..724ffe50b90 100644 --- a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h +++ b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h @@ -9,38 +9,20 @@ #include "ZigbeeEP.h" #include "ha/esp_zigbee_ha_standard.h" -/** - * @brief Zigbee HA standard window covering device clusters. - * Added here as not supported by ESP Zigbee library. - * - * - */ -typedef struct zigbee_window_covering_cfg_s { - esp_zb_basic_cluster_cfg_t basic_cfg; /*!< Basic cluster configuration, @ref esp_zb_basic_cluster_cfg_s */ - esp_zb_identify_cluster_cfg_t identify_cfg; /*!< Identify cluster configuration, @ref esp_zb_identify_cluster_cfg_s */ - esp_zb_groups_cluster_cfg_t groups_cfg; /*!< Groups cluster configuration, @ref esp_zb_groups_cluster_cfg_s */ - esp_zb_scenes_cluster_cfg_t scenes_cfg; /*!< Scenes cluster configuration, @ref esp_zb_scenes_cluster_cfg_s */ - esp_zb_window_covering_cluster_cfg_t window_covering_cfg; /*!< Window covering cluster configuration, @ref esp_zb_window_covering_cluster_cfg_s */ -} zigbee_window_covering_cfg_t; - -enum WindowCoveringType { - ROLLERSHADE = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE, - ROLLERSHADE_2_MOTOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_2_MOTOR, - ROLLERSHADE_EXTERIOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_EXTERIOR, - ROLLERSHADE_EXTERIOR_2_MOTOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_EXTERIOR_2_MOTOR, - DRAPERY = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_DRAPERY, - AWNING = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_AWNING, - SHUTTER = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_SHUTTER, - BLIND_TILT_ONLY = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_TILT_BLIND_TILT_ONLY, - BLIND_LIFT_AND_TILT = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_TILT_BLIND_LIFT_AND_TILT, - PROJECTOR_SCREEN = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_PROJECTOR_SCREEN, +// Window covering types supported by Zigbee Window Covering cluster +enum ZigbeeWindowCoveringType { + ROLLERSHADE = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE, // LIFT support + ROLLERSHADE_2_MOTOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_2_MOTOR, // LIFT support + ROLLERSHADE_EXTERIOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_EXTERIOR, // LIFT support + ROLLERSHADE_EXTERIOR_2_MOTOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_EXTERIOR_2_MOTOR, // LIFT support + DRAPERY = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_DRAPERY, // LIFT support + AWNING = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_AWNING, // LIFT support + SHUTTER = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_SHUTTER, // TILT support + BLIND_TILT_ONLY = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_TILT_BLIND_TILT_ONLY, // TILT support + BLIND_LIFT_AND_TILT = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_TILT_BLIND_LIFT_AND_TILT, // LIFT and TILT support + PROJECTOR_SCREEN = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_PROJECTOR_SCREEN, // LIFT support }; -/** - * @brief Zigbee HA standard window covering device default config value. - * Added here as not supported by ESP Zigbee library. - * - */ // clang-format off #define ZIGBEE_DEFAULT_WINDOW_COVERING_CONFIG() \ { \ @@ -74,67 +56,89 @@ enum WindowCoveringType { } // clang-format on +typedef struct zigbee_window_covering_cfg_s { + esp_zb_basic_cluster_cfg_t basic_cfg; + esp_zb_identify_cluster_cfg_t identify_cfg; + esp_zb_groups_cluster_cfg_t groups_cfg; + esp_zb_scenes_cluster_cfg_t scenes_cfg; + esp_zb_window_covering_cluster_cfg_t window_covering_cfg; +} zigbee_window_covering_cfg_t; + class ZigbeeWindowCovering : public ZigbeeEP { public: ZigbeeWindowCovering(uint8_t endpoint); ~ZigbeeWindowCovering(); + // Set the callback functions for the window covering commands + void onOpen(void (*callback)()) { + _on_open = callback; + } + void onClose(void (*callback)()) { + _on_close = callback; + } void onGoToLiftPercentage(void (*callback)(uint8_t)) { _on_go_to_lift_percentage = callback; } - + void onGoToTiltPercentage(void (*callback)(uint8_t)) { + _on_go_to_tilt_percentage = callback; + } void onStop(void (*callback)()) { _on_stop = callback; } + // Set the window covering position in centimeters or percentage (0-100) void setLiftPosition(uint16_t lift_position); + void setLiftPercentage(uint8_t lift_percentage); + void setTiltPosition(uint16_t tilt_position); + void setTiltPercentage(uint8_t tilt_percentage); - // Set the window covering type, see esp_zb_zcl_window_covering_window_covering_type_t - void setCoveringType(WindowCoveringType covering_type); + // Set the window covering type (see ZigbeeWindowCoveringType) + void setCoveringType(ZigbeeWindowCoveringType covering_type); - // Set window covering config/status, see esp_zb_zcl_window_covering_config_status_t + // Set window covering config/status, for more info see esp_zb_zcl_window_covering_config_status_t void setConfigStatus(bool operational, bool online, bool commands_reversed, bool lift_closed_loop, bool tilt_closed_loop, bool lift_encoder_controlled, bool tilt_encoder_controlled); - // Set configuration mode of window covering, see esp_zb_zcl_window_covering_mode_t + // Set configuration mode of window covering, for more info see esp_zb_zcl_window_covering_mode_t void setMode(bool motor_reversed, bool calibration_mode, bool maintenance_mode, bool leds_on); - // Set limits of motion, see esp_zb_zcl_window_covering_info_attr_t + // Set limits of motion, for more info see esp_zb_zcl_window_covering_info_attr_t void setLimits(uint16_t installed_open_limit_lift, uint16_t installed_closed_limit_lift, uint16_t installed_open_limit_tilt, uint16_t installed_closed_limit_tilt); private: void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override; void zbWindowCoveringMovementCmd(const esp_zb_zcl_window_covering_movement_message_t *message) override; + // Create window covering cluster list + esp_zb_cluster_list_t *zigbee_window_covering_clusters_create(zigbee_window_covering_cfg_t *window_covering_cfg); + + void open(); + void close(); void goToLiftPercentage(uint8_t); + void goToTiltPercentage(uint8_t); void stop(); // callback function to be called on lift percentage change (lift percentage) + void (*_on_open)(); + void (*_on_close)(); void (*_on_go_to_lift_percentage)(uint8_t); + void (*_on_go_to_tilt_percentage)(uint8_t); void (*_on_stop)(); - /** - * @brief Create a standard HA window covering cluster list. - * Added here as not supported by ESP Zigbee library. - * - * @note This contains basic, identify, groups, scenes, window_covering, as server side. - * @param[in] window_covering_cfg Configuration parameters for this cluster lists defined by @ref zigbee_window_covering_cfg_t - * - * @return Pointer to cluster list @ref esp_zb_cluster_list_s - * - */ - esp_zb_cluster_list_t *zigbee_window_covering_clusters_create(zigbee_window_covering_cfg_t *window_covering_cfg); - + // Widows covering lift attributes uint8_t _current_lift_percentage; - uint8_t _current_tilt_percentage; - uint16_t _current_position_lift; + uint16_t _current_lift_position; uint16_t _installed_open_limit_lift; - uint16_t _installed_closed_limit_lift; + uint16_t _installed_closed_limit_lift; + uint16_t _physical_closed_limit_lift; + + // Windows covering tilt attributes + uint8_t _current_tilt_percentage; + uint16_t _current_tilt_position; uint16_t _installed_open_limit_tilt; uint16_t _installed_closed_limit_tilt; - uint16_t _physical_closed_limit_lift; uint16_t _physical_closed_limit_tilt; }; -#endif // SOC_IEEE802154_SUPPORTED +#endif // SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED From 38f5a8eb261890fbd81464f627111f54f07c3d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Tue, 4 Feb 2025 15:04:08 +0100 Subject: [PATCH 5/7] fix(zigbee): Fix typos --- libraries/Zigbee/examples/Zigbee_Window_Covering/README.md | 4 ++-- .../Zigbee_Window_Covering/Zigbee_Window_Covering.ino | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/Zigbee/examples/Zigbee_Window_Covering/README.md b/libraries/Zigbee/examples/Zigbee_Window_Covering/README.md index 6f6d59d90ed..469560a0e0c 100644 --- a/libraries/Zigbee/examples/Zigbee_Window_Covering/README.md +++ b/libraries/Zigbee/examples/Zigbee_Window_Covering/README.md @@ -1,6 +1,6 @@ -# Arduino-ESP32 Widnow Covering Example +# Arduino-ESP32 Window Covering Example -This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) window covering or port. +This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) window covering device. To see if the communication with your Zigbee network works, use the Serial monitor and watch for output there. diff --git a/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino b/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino index 1871ba22e94..b6f167b327c 100644 --- a/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino +++ b/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino @@ -19,7 +19,7 @@ * The window covering is a Zigbee end device, which is moving the blinds (lift+tilt) and reporting * its current position to the Zigbee network. * - * Use setCoveingType() to set the type of covering (blind, shade, etc.). + * Use setCoveringType() to set the type of covering (blind, shade, etc.). * * The example also demonstrates how to use the button to manually control the lift position. * From a73abd751f223c2f9250a9282cc020b7f6d88e2e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Tue, 4 Feb 2025 14:11:07 +0000 Subject: [PATCH 6/7] ci(pre-commit): Apply automatic fixes --- .../Zigbee_Window_Covering.ino | 38 +++--- libraries/Zigbee/src/ZigbeeEP.h | 1 - libraries/Zigbee/src/ZigbeeHandlers.cpp | 4 +- .../Zigbee/src/ep/ZigbeeWindowCovering.cpp | 118 +++++++++++------- .../Zigbee/src/ep/ZigbeeWindowCovering.h | 35 +++--- 5 files changed, 116 insertions(+), 80 deletions(-) diff --git a/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino b/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino index b6f167b327c..dc66fcc0214 100644 --- a/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino +++ b/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino @@ -16,11 +16,11 @@ * @brief This example demonstrates Zigbee Window Covering. * * The example demonstrates how to use Zigbee library to create a end device window covering device. - * The window covering is a Zigbee end device, which is moving the blinds (lift+tilt) and reporting + * The window covering is a Zigbee end device, which is moving the blinds (lift+tilt) and reporting * its current position to the Zigbee network. - * + * * Use setCoveringType() to set the type of covering (blind, shade, etc.). - * + * * The example also demonstrates how to use the button to manually control the lift position. * * Proper Zigbee mode must be selected in Tools->Zigbee mode @@ -38,15 +38,14 @@ #include "ZigbeeCore.h" #include "ep/ZigbeeWindowCovering.h" - #define ZIGBEE_COVERING_ENDPOINT 10 -#define BUTTON_PIN 9 // ESP32-C6/H2 Boot button +#define BUTTON_PIN 9 // ESP32-C6/H2 Boot button -#define MAX_LIFT 200 // centimeters from open position (0-900) -#define MIN_LIFT 0 +#define MAX_LIFT 200 // centimeters from open position (0-900) +#define MIN_LIFT 0 -#define MAX_TILT 40 // centimeters from open position (0-900) -#define MIN_TILT 0 +#define MAX_TILT 40 // centimeters from open position (0-900) +#define MIN_TILT 0 uint16_t currentLift = MAX_LIFT; uint8_t currentLiftPercentage = 100; @@ -57,7 +56,7 @@ uint8_t currentTiltPercentage = 100; ZigbeeWindowCovering zbCovering = ZigbeeWindowCovering(ZIGBEE_COVERING_ENDPOINT); void setup() { - Serial.begin(115200); + Serial.begin(115200); // Init button for factory reset pinMode(BUTTON_PIN, INPUT_PULLUP); @@ -100,7 +99,7 @@ void setup() { Serial.print("."); delay(100); } - Serial.println(); + Serial.println(); // Set initial position zbCovering.setLiftPercentage(currentLiftPercentage); @@ -124,11 +123,11 @@ void loop() { } // Manual lift controll simulation by pressing button manualControl(); - } + } delay(500); } -void fullOpen(){ +void fullOpen() { /* This is where you would trigger your motor to go to full open state, currentLift should be updated until full open has been reached in order to provide feedback to controller of actual position The stop can be always called, so the movement can be stopped at any time */ @@ -141,7 +140,7 @@ void fullOpen(){ zbCovering.setLiftPercentage(currentLiftPercentage); } -void fullClose(){ +void fullClose() { /* This is where you would trigger your motor to go to full close state, currentLift should be updated until full close has been reached in order to provide feedback to controller of actual position The stop can be always called, so the movement can be stopped at any time */ @@ -164,7 +163,7 @@ void goToLiftPercentage(uint8_t liftPercentage) { Serial.printf("New requsted lift from Zigbee: %d (%d)\n", currentLift, liftPercentage); //Update the current position - zbCovering.setLiftPercentage(currentLiftPercentage); //or setLiftPosition() + zbCovering.setLiftPercentage(currentLiftPercentage); //or setLiftPosition() } void goToTiltPercentage(uint8_t tiltPercentage) { @@ -177,13 +176,12 @@ void goToTiltPercentage(uint8_t tiltPercentage) { Serial.printf("New requsted tilt from Zigbee: %d (%d)\n", currentTilt, tiltPercentage); //Update the current position - zbCovering.setTiltPercentage(currentTiltPercentage); //or setTiltPosition() + zbCovering.setTiltPercentage(currentTiltPercentage); //or setTiltPosition() } - void stopMotor() { // Motor can be stopped while moving cover toward current target, when stopped the actual position should be updated - Serial.println("Stopping motor"); + Serial.println("Stopping motor"); // Update the current position of both lift and tilt zbCovering.setLiftPercentage(currentLiftPercentage); zbCovering.setTiltPercentage(currentTiltPercentage); @@ -192,9 +190,9 @@ void stopMotor() { void manualControl() { // Simulate lift percentage move by increasing it by 20% each time currentLiftPercentage += 20; - if(currentLiftPercentage > 100) { + if (currentLiftPercentage > 100) { currentLiftPercentage = 0; } zbCovering.setLiftPercentage(currentLiftPercentage); // Also setLiftPosition() can be used to set the exact position instead of percentage -} \ No newline at end of file +} diff --git a/libraries/Zigbee/src/ZigbeeEP.h b/libraries/Zigbee/src/ZigbeeEP.h index 85c1957e181..fce0500ef0d 100644 --- a/libraries/Zigbee/src/ZigbeeEP.h +++ b/libraries/Zigbee/src/ZigbeeEP.h @@ -125,7 +125,6 @@ class ZigbeeEP { _is_bound = true; } - void onIdentify(void (*callback)(uint16_t)) { _on_identify = callback; } diff --git a/libraries/Zigbee/src/ZigbeeHandlers.cpp b/libraries/Zigbee/src/ZigbeeHandlers.cpp index 9eb884ec00e..575b25ef404 100644 --- a/libraries/Zigbee/src/ZigbeeHandlers.cpp +++ b/libraries/Zigbee/src/ZigbeeHandlers.cpp @@ -29,7 +29,9 @@ static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, case ESP_ZB_CORE_IAS_ZONE_ENROLL_RESPONSE_VALUE_CB_ID: ret = zb_cmd_ias_zone_enroll_response_handler((esp_zb_zcl_ias_zone_enroll_response_message_t *)message); break; - case ESP_ZB_CORE_WINDOW_COVERING_MOVEMENT_CB_ID: ret = zb_window_covering_movement_resp_handler((esp_zb_zcl_window_covering_movement_message_t *)message); break; + case ESP_ZB_CORE_WINDOW_COVERING_MOVEMENT_CB_ID: + ret = zb_window_covering_movement_resp_handler((esp_zb_zcl_window_covering_movement_message_t *)message); + break; case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID: ret = zb_cmd_default_resp_handler((esp_zb_zcl_cmd_default_resp_message_t *)message); break; default: log_w("Receive unhandled Zigbee action(0x%x) callback", callback_id); break; } diff --git a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp index 74058f3276f..b08aa46e11b 100644 --- a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp @@ -9,16 +9,32 @@ esp_zb_cluster_list_t *ZigbeeWindowCovering::zigbee_window_covering_clusters_cre esp_zb_attribute_list_t *esp_zb_scenes_cluster = esp_zb_scenes_cluster_create(&window_covering_cfg->scenes_cfg); esp_zb_attribute_list_t *esp_zb_window_covering_cluster = esp_zb_window_covering_cluster_create(&window_covering_cfg->window_covering_cfg); - esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, &_current_lift_percentage); - esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID, &_current_tilt_percentage); + esp_zb_window_covering_cluster_add_attr( + esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, &_current_lift_percentage + ); + esp_zb_window_covering_cluster_add_attr( + esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID, &_current_tilt_percentage + ); esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID, &_current_lift_position); esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_ID, &_current_lift_position); - esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_ID, &_installed_open_limit_lift); - esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_TILT_ID, &_installed_open_limit_tilt); - esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_LIFT_ID, &_installed_closed_limit_lift); - esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_TILT_ID, &_installed_closed_limit_tilt); - esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_PHYSICAL_CLOSED_LIMIT_LIFT_ID, &_physical_closed_limit_lift); - esp_zb_window_covering_cluster_add_attr(esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_PHY_CLOSED_LIMIT_TILT_ID, &_physical_closed_limit_lift); + esp_zb_window_covering_cluster_add_attr( + esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_ID, &_installed_open_limit_lift + ); + esp_zb_window_covering_cluster_add_attr( + esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_TILT_ID, &_installed_open_limit_tilt + ); + esp_zb_window_covering_cluster_add_attr( + esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_LIFT_ID, &_installed_closed_limit_lift + ); + esp_zb_window_covering_cluster_add_attr( + esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_TILT_ID, &_installed_closed_limit_tilt + ); + esp_zb_window_covering_cluster_add_attr( + esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_PHYSICAL_CLOSED_LIMIT_LIFT_ID, &_physical_closed_limit_lift + ); + esp_zb_window_covering_cluster_add_attr( + esp_zb_window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_PHY_CLOSED_LIMIT_TILT_ID, &_physical_closed_limit_lift + ); // ------------------------------ Create cluster list ------------------------------ esp_zb_cluster_list_t *esp_zb_cluster_list = esp_zb_zcl_cluster_list_create(); @@ -28,7 +44,7 @@ esp_zb_cluster_list_t *ZigbeeWindowCovering::zigbee_window_covering_clusters_cre esp_zb_cluster_list_add_scenes_cluster(esp_zb_cluster_list, esp_zb_scenes_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_list_add_window_covering_cluster(esp_zb_cluster_list, esp_zb_window_covering_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); - return esp_zb_cluster_list; + return esp_zb_cluster_list; } ZigbeeWindowCovering::ZigbeeWindowCovering(uint8_t endpoint) : ZigbeeEP(endpoint) { @@ -37,20 +53,22 @@ ZigbeeWindowCovering::ZigbeeWindowCovering(uint8_t endpoint) : ZigbeeEP(endpoint // set default values for window covering attributes _current_lift_percentage = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_DEFAULT_VALUE; _current_tilt_percentage = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_DEFAULT_VALUE; - _installed_open_limit_lift = ESP_ZB_ZCL_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_DEFAULT_VALUE; + _installed_open_limit_lift = ESP_ZB_ZCL_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_DEFAULT_VALUE; _installed_closed_limit_lift = ESP_ZB_ZCL_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_LIFT_DEFAULT_VALUE; _installed_open_limit_tilt = ESP_ZB_ZCL_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_TILT_DEFAULT_VALUE; _installed_closed_limit_tilt = ESP_ZB_ZCL_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_TILT_DEFAULT_VALUE; _current_lift_position = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_LIFT_DEFAULT_VALUE; _current_tilt_position = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_TILT_DEFAULT_VALUE; _physical_closed_limit_lift = ESP_ZB_ZCL_WINDOW_COVERING_PHYSICAL_CLOSED_LIMIT_LIFT_DEFAULT_VALUE; - _physical_closed_limit_tilt = ESP_ZB_ZCL_WINDOW_COVERING_PHY_CLOSED_LIMIT_TILT_DEFAULT_VALUE; + _physical_closed_limit_tilt = ESP_ZB_ZCL_WINDOW_COVERING_PHY_CLOSED_LIMIT_TILT_DEFAULT_VALUE; // Create custom window covering configuration zigbee_window_covering_cfg_t window_covering_cfg = ZIGBEE_DEFAULT_WINDOW_COVERING_CONFIG(); _cluster_list = zigbee_window_covering_clusters_create(&window_covering_cfg); - _ep_config = {.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID, .app_device_version = 0}; + _ep_config = { + .endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID, .app_device_version = 0 + }; } // Confiuration methods for window covering @@ -60,16 +78,16 @@ void ZigbeeWindowCovering::setCoveringType(ZigbeeWindowCoveringType covering_typ esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_WINDOW_COVERING_TYPE_ID, (void *)&covering_type); } -void ZigbeeWindowCovering::setConfigStatus(bool operational, bool online, bool commands_reversed, - bool lift_closed_loop, bool tilt_closed_loop, - bool lift_encoder_controlled, bool tilt_encoder_controlled) { - uint8_t config_status = (operational ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_OPERATIONAL : 0) - | (online ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_ONLINE : 0) - | (commands_reversed ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_REVERSE_COMMANDS : 0) - | (lift_closed_loop ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_LIFT_CONTROL_IS_CLOSED_LOOP : 0) - | (tilt_closed_loop ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_TILT_CONTROL_IS_CLOSED_LOOP : 0) - | (lift_encoder_controlled ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_LIFT_ENCODER_CONTROLLED : 0) - | (tilt_encoder_controlled ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_TILT_ENCODER_CONTROLLED : 0); +void ZigbeeWindowCovering::setConfigStatus( + bool operational, bool online, bool commands_reversed, bool lift_closed_loop, bool tilt_closed_loop, bool lift_encoder_controlled, + bool tilt_encoder_controlled +) { + uint8_t config_status = (operational ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_OPERATIONAL : 0) | (online ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_ONLINE : 0) + | (commands_reversed ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_REVERSE_COMMANDS : 0) + | (lift_closed_loop ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_LIFT_CONTROL_IS_CLOSED_LOOP : 0) + | (tilt_closed_loop ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_TILT_CONTROL_IS_CLOSED_LOOP : 0) + | (lift_encoder_controlled ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_LIFT_ENCODER_CONTROLLED : 0) + | (tilt_encoder_controlled ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_TILT_ENCODER_CONTROLLED : 0); log_v("Updating window covering config status to %d", config_status); @@ -80,9 +98,9 @@ void ZigbeeWindowCovering::setConfigStatus(bool operational, bool online, bool c void ZigbeeWindowCovering::setMode(bool motor_reversed, bool calibration_mode, bool maintenance_mode, bool leds_on) { uint8_t mode = (motor_reversed ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_REVERSED_MOTOR_DIRECTION : 0) - | (calibration_mode ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_RUN_IN_CALIBRATION_MODE : 0) - | (maintenance_mode ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_MOTOR_IS_RUNNING_IN_MAINTENANCE_MODE : 0) - | (leds_on ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_LEDS_WILL_DISPLAY_FEEDBACK : 0); + | (calibration_mode ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_RUN_IN_CALIBRATION_MODE : 0) + | (maintenance_mode ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_MOTOR_IS_RUNNING_IN_MAINTENANCE_MODE : 0) + | (leds_on ? ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_LEDS_WILL_DISPLAY_FEEDBACK : 0); log_v("Updating window covering mode to %d", mode); @@ -91,8 +109,9 @@ void ZigbeeWindowCovering::setMode(bool motor_reversed, bool calibration_mode, b esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_MODE_ID, (void *)&mode); } -void ZigbeeWindowCovering::setLimits(uint16_t installed_open_limit_lift, uint16_t installed_closed_limit_lift, - uint16_t installed_open_limit_tilt, uint16_t installed_closed_limit_tilt) { +void ZigbeeWindowCovering::setLimits( + uint16_t installed_open_limit_lift, uint16_t installed_closed_limit_lift, uint16_t installed_open_limit_tilt, uint16_t installed_closed_limit_tilt +) { _installed_open_limit_lift = installed_open_limit_lift; _installed_closed_limit_lift = installed_closed_limit_lift; _physical_closed_limit_lift = installed_closed_limit_lift; @@ -101,7 +120,7 @@ void ZigbeeWindowCovering::setLimits(uint16_t installed_open_limit_lift, uint16_ _physical_closed_limit_tilt = installed_closed_limit_tilt; esp_zb_attribute_list_t *window_covering_cluster = - esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_LIFT_ID, (void *)&_installed_open_limit_lift); esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_CLOSED_LIMIT_LIFT_ID, (void *)&_installed_closed_limit_lift); esp_zb_cluster_update_attr(window_covering_cluster, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_INSTALLED_OPEN_LIMIT_TILT_ID, (void *)&_installed_open_limit_tilt); @@ -121,16 +140,24 @@ void ZigbeeWindowCovering::zbAttributeSet(const esp_zb_zcl_set_attr_value_messag bool calibration_mode = mode & ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_RUN_IN_CALIBRATION_MODE; bool maintenance_mode = mode & ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_MOTOR_IS_RUNNING_IN_MAINTENANCE_MODE; bool leds_on = mode & ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_LEDS_WILL_DISPLAY_FEEDBACK; - log_v("Updating window covering mode to motor reversed: %d, calibration mode: %d, maintenance mode: %d, leds on: %d", motor_reversed, calibration_mode, maintenance_mode, leds_on); + log_v( + "Updating window covering mode to motor reversed: %d, calibration mode: %d, maintenance mode: %d, leds on: %d", motor_reversed, calibration_mode, + maintenance_mode, leds_on + ); setMode(motor_reversed, calibration_mode, maintenance_mode, leds_on); //Update Configuration status with motor reversed status uint8_t config_status; - config_status = (*(uint8_t *)esp_zb_zcl_get_attribute(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_STATUS_ID)->data_p); - config_status = motor_reversed ? config_status | ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_REVERSE_COMMANDS : config_status & ~ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_REVERSE_COMMANDS; + config_status = (*(uint8_t *)esp_zb_zcl_get_attribute( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_STATUS_ID + ) + ->data_p); + config_status = motor_reversed ? config_status | ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_REVERSE_COMMANDS + : config_status & ~ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_REVERSE_COMMANDS; log_v("Updating window covering config status to %d", config_status); esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zcl_set_attribute_val( - _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_STATUS_ID, &config_status, false + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CONFIG_STATUS_ID, &config_status, + false ); esp_zb_lock_release(); return; @@ -163,8 +190,7 @@ void ZigbeeWindowCovering::zbWindowCoveringMovementCmd(const esp_zb_zcl_window_c _current_tilt_percentage = message->payload.percentage_tilt_value; goToTiltPercentage(_current_tilt_percentage); } - } - else { + } else { log_w("Received message ignored. Command: %d not supported for Window Covering", message->command); } } else { @@ -212,10 +238,12 @@ void ZigbeeWindowCovering::setLiftPosition(uint16_t lift_position) { // set lift state esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zcl_set_attribute_val( - _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID, &_current_lift_position, false + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID, + &_current_lift_position, false ); esp_zb_zcl_set_attribute_val( - _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, &_current_lift_percentage, false + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, + &_current_lift_percentage, false ); esp_zb_lock_release(); } @@ -229,10 +257,12 @@ void ZigbeeWindowCovering::setLiftPercentage(uint8_t lift_percentage) { // set lift state esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zcl_set_attribute_val( - _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID, &_current_lift_position, false + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_ID, + &_current_lift_position, false ); esp_zb_zcl_set_attribute_val( - _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, &_current_lift_percentage, false + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_ID, + &_current_lift_percentage, false ); esp_zb_lock_release(); } @@ -246,10 +276,12 @@ void ZigbeeWindowCovering::setTiltPosition(uint16_t tilt_position) { // set lift state esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zcl_set_attribute_val( - _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_ID, &_current_tilt_position, false + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_ID, + &_current_tilt_position, false ); esp_zb_zcl_set_attribute_val( - _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID, &_current_tilt_percentage, false + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID, + &_current_tilt_percentage, false ); esp_zb_lock_release(); } @@ -263,10 +295,12 @@ void ZigbeeWindowCovering::setTiltPercentage(uint8_t tilt_percentage) { // set lift state esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zcl_set_attribute_val( - _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_ID, &_current_tilt_position, false + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_ID, + &_current_tilt_position, false ); esp_zb_zcl_set_attribute_val( - _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID, &_current_tilt_percentage, false + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_WINDOW_COVERING_CURRENT_POSITION_TILT_PERCENTAGE_ID, + &_current_tilt_percentage, false ); esp_zb_lock_release(); } diff --git a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h index 724ffe50b90..5cc2b13701a 100644 --- a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h +++ b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h @@ -11,16 +11,16 @@ // Window covering types supported by Zigbee Window Covering cluster enum ZigbeeWindowCoveringType { - ROLLERSHADE = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE, // LIFT support - ROLLERSHADE_2_MOTOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_2_MOTOR, // LIFT support - ROLLERSHADE_EXTERIOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_EXTERIOR, // LIFT support - ROLLERSHADE_EXTERIOR_2_MOTOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_EXTERIOR_2_MOTOR, // LIFT support - DRAPERY = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_DRAPERY, // LIFT support - AWNING = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_AWNING, // LIFT support - SHUTTER = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_SHUTTER, // TILT support - BLIND_TILT_ONLY = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_TILT_BLIND_TILT_ONLY, // TILT support - BLIND_LIFT_AND_TILT = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_TILT_BLIND_LIFT_AND_TILT, // LIFT and TILT support - PROJECTOR_SCREEN = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_PROJECTOR_SCREEN, // LIFT support + ROLLERSHADE = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE, // LIFT support + ROLLERSHADE_2_MOTOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_2_MOTOR, // LIFT support + ROLLERSHADE_EXTERIOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_EXTERIOR, // LIFT support + ROLLERSHADE_EXTERIOR_2_MOTOR = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_ROLLERSHADE_EXTERIOR_2_MOTOR, // LIFT support + DRAPERY = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_DRAPERY, // LIFT support + AWNING = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_AWNING, // LIFT support + SHUTTER = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_SHUTTER, // TILT support + BLIND_TILT_ONLY = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_TILT_BLIND_TILT_ONLY, // TILT support + BLIND_LIFT_AND_TILT = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_TILT_BLIND_LIFT_AND_TILT, // LIFT and TILT support + PROJECTOR_SCREEN = ESP_ZB_ZCL_ATTR_WINDOW_COVERING_TYPE_PROJECTOR_SCREEN, // LIFT support }; // clang-format off @@ -96,16 +96,19 @@ class ZigbeeWindowCovering : public ZigbeeEP { void setCoveringType(ZigbeeWindowCoveringType covering_type); // Set window covering config/status, for more info see esp_zb_zcl_window_covering_config_status_t - void setConfigStatus(bool operational, bool online, bool commands_reversed, - bool lift_closed_loop, bool tilt_closed_loop, - bool lift_encoder_controlled, bool tilt_encoder_controlled); + void setConfigStatus( + bool operational, bool online, bool commands_reversed, bool lift_closed_loop, bool tilt_closed_loop, bool lift_encoder_controlled, + bool tilt_encoder_controlled + ); // Set configuration mode of window covering, for more info see esp_zb_zcl_window_covering_mode_t void setMode(bool motor_reversed, bool calibration_mode, bool maintenance_mode, bool leds_on); // Set limits of motion, for more info see esp_zb_zcl_window_covering_info_attr_t - void setLimits(uint16_t installed_open_limit_lift, uint16_t installed_closed_limit_lift, - uint16_t installed_open_limit_tilt, uint16_t installed_closed_limit_tilt); + void setLimits( + uint16_t installed_open_limit_lift, uint16_t installed_closed_limit_lift, uint16_t installed_open_limit_tilt, uint16_t installed_closed_limit_tilt + ); + private: void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override; void zbWindowCoveringMovementCmd(const esp_zb_zcl_window_covering_movement_message_t *message) override; @@ -130,7 +133,7 @@ class ZigbeeWindowCovering : public ZigbeeEP { uint8_t _current_lift_percentage; uint16_t _current_lift_position; uint16_t _installed_open_limit_lift; - uint16_t _installed_closed_limit_lift; + uint16_t _installed_closed_limit_lift; uint16_t _physical_closed_limit_lift; // Windows covering tilt attributes From 78eb461a8eaa9e541ac2ecfbb6f3df6081dc0343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Tue, 4 Feb 2025 15:15:59 +0100 Subject: [PATCH 7/7] fix(ci): Fixes of typos --- .../Zigbee_Window_Covering/Zigbee_Window_Covering.ino | 10 +++++----- libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino b/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino index dc66fcc0214..c7f9cf84f74 100644 --- a/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino +++ b/libraries/Zigbee/examples/Zigbee_Window_Covering/Zigbee_Window_Covering.ino @@ -121,7 +121,7 @@ void loop() { delay(30000); } } - // Manual lift controll simulation by pressing button + // Manual lift control simulation by pressing button manualControl(); } delay(500); @@ -160,9 +160,9 @@ void goToLiftPercentage(uint8_t liftPercentage) { // Our simulated cover updates instantly! currentLift = (liftPercentage * MAX_LIFT) / 100; currentLiftPercentage = liftPercentage; - Serial.printf("New requsted lift from Zigbee: %d (%d)\n", currentLift, liftPercentage); + Serial.printf("New requested lift from Zigbee: %d (%d)\n", currentLift, liftPercentage); - //Update the current position + // Update the current position zbCovering.setLiftPercentage(currentLiftPercentage); //or setLiftPosition() } @@ -173,9 +173,9 @@ void goToTiltPercentage(uint8_t tiltPercentage) { // Our simulated cover updates instantly! currentTilt = (tiltPercentage * MAX_TILT) / 100; currentTiltPercentage = tiltPercentage; - Serial.printf("New requsted tilt from Zigbee: %d (%d)\n", currentTilt, tiltPercentage); + Serial.printf("New requested tilt from Zigbee: %d (%d)\n", currentTilt, tiltPercentage); - //Update the current position + // Update the current position zbCovering.setTiltPercentage(currentTiltPercentage); //or setTiltPosition() } diff --git a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp index b08aa46e11b..970017165e2 100644 --- a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp @@ -71,7 +71,7 @@ ZigbeeWindowCovering::ZigbeeWindowCovering(uint8_t endpoint) : ZigbeeEP(endpoint }; } -// Confiuration methods for window covering +// Configuration methods for window covering void ZigbeeWindowCovering::setCoveringType(ZigbeeWindowCoveringType covering_type) { esp_zb_attribute_list_t *window_covering_cluster = esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);