From f87a4cd119ee30b878d9f10472339d232f68ba4e Mon Sep 17 00:00:00 2001 From: Shubham Patil Date: Thu, 9 Oct 2025 18:29:08 +0530 Subject: [PATCH] components/esp_matter: implement temperature unit handling with compatibility adjustments Added global variable for temperature unit for deferred setting when enabling endpoint. Compatibility functions to handle temperature unit attribute reads and writes, ensuring seamless integration with previous versions. Also, added a function to retrieve the cluster id and endpoint id for internally managed attribues. --- .../data_model/attribute_exceptions.yaml | 12 ++ .../data_model/esp_matter_cluster.cpp | 3 + .../data_model/esp_matter_data_model.cpp | 161 +++++++++++++++++- 3 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 components/esp_matter/data_model/attribute_exceptions.yaml diff --git a/components/esp_matter/data_model/attribute_exceptions.yaml b/components/esp_matter/data_model/attribute_exceptions.yaml new file mode 100644 index 000000000..89aff4091 --- /dev/null +++ b/components/esp_matter/data_model/attribute_exceptions.yaml @@ -0,0 +1,12 @@ +# List of attributes where we did not add the ATTRIBUTE_FLAG_MANAGED_INTERNALLY. +# These are mostly the cases where AAI is not registered for the cluster and +# attribute is of primitive type. +AttributesWithOverridenManagedInternallyFlag: + AirQuality: + - AirQuality + +# List of attributes that have get_val/set_val extentions which reads the attributes +# using the connectedhomeip APIs and populate the esp_matter_attr_val_t. +AttributesWithSetterGetterAPIExceptions: + UnitLocalization: + - TemperatureUnit \ No newline at end of file diff --git a/components/esp_matter/data_model/esp_matter_cluster.cpp b/components/esp_matter/data_model/esp_matter_cluster.cpp index 7c23c8f51..2f6a1b2f4 100644 --- a/components/esp_matter/data_model/esp_matter_cluster.cpp +++ b/components/esp_matter/data_model/esp_matter_cluster.cpp @@ -784,6 +784,9 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) VerifyOrReturnValue(cluster, NULL, ESP_LOGE(TAG, "Could not create cluster. cluster_id: 0x%08" PRIX32, UnitLocalization::Id)); if (flags & CLUSTER_FLAG_SERVER) { + + static const auto plugin_server_init_cb = CALL_ONCE(MatterUnitLocalizationPluginServerInitCallback); + set_plugin_server_init_callback(cluster, plugin_server_init_cb); add_function_list(cluster, function_list, function_flags); /* Attributes managed internally */ diff --git a/components/esp_matter/data_model/esp_matter_data_model.cpp b/components/esp_matter/data_model/esp_matter_data_model.cpp index d7b30ba13..c3ec1806a 100644 --- a/components/esp_matter/data_model/esp_matter_data_model.cpp +++ b/components/esp_matter/data_model/esp_matter_data_model.cpp @@ -27,8 +27,6 @@ #include #include #include -#include "app/server/Server.h" -#include "credentials/GroupDataProviderImpl.h" #define ESP_MATTER_MAX_DEVICE_TYPE_COUNT CONFIG_ESP_MATTER_MAX_DEVICE_TYPE_COUNT @@ -42,6 +40,13 @@ using chip::kInvalidCommandId; using chip::kInvalidClusterId; using chip::kInvalidEndpointId; +// This is the hack to preserve and defer setting the temperature unit in the endpoint::enable() +// we have an exception for this in attribute::create() to cache for later use in endpoint::enable() +#ifdef CONFIG_SUPPORT_UNIT_LOCALIZATION_CLUSTER +#include +uint8_t g_temperature_unit = 0; +#endif // CONFIG_SUPPORT_UNIT_LOCALIZATION_CLUSTER + namespace esp_matter { struct _attribute_base_t { uint16_t flags; // This struct is for attributes managed internally. @@ -529,6 +534,24 @@ esp_err_t enable(endpoint_t *endpoint) } ESP_LOGI(TAG, "Dynamic endpoint %" PRIu16 " added", current_endpoint->endpoint_id); + +#ifdef CONFIG_SUPPORT_UNIT_LOCALIZATION_CLUSTER +// set the temperature unit from unit localization cluster +{ + using namespace ::chip::app::Clusters; + attribute_t *temperature_unit_attr = attribute::get(current_endpoint->endpoint_id, + UnitLocalization::Id, + UnitLocalization::Attributes::TemperatureUnit::Id); + if (temperature_unit_attr) { + auto temp_unit = static_cast(g_temperature_unit); + CHIP_ERROR chip_err = UnitLocalization::UnitLocalizationServer::Instance().SetTemperatureUnit(temp_unit); + if (chip_err != CHIP_NO_ERROR) { + ESP_LOGW(TAG, "Failed to set temperature unit, err:%" CHIP_ERROR_FORMAT, chip_err.Format()); + } + } +} +#endif // CONFIG_SUPPORT_UNIT_LOCALIZATION_CLUSTER + return err; cleanup: @@ -684,6 +707,15 @@ attribute_t *create(cluster_t *cluster, uint32_t attribute_id, uint16_t flags, e /* Add */ SinglyLinkedList<_attribute_base_t>::append(¤t_cluster->attribute_list, attribute); + + // this is an exception to keep the sdk experience same as before v1.4.2 release +#ifdef CONFIG_SUPPORT_UNIT_LOCALIZATION_CLUSTER + if (cluster::get_id(cluster) == chip::app::Clusters::UnitLocalization::Id && + attribute_id == chip::app::Clusters::UnitLocalization::Attributes::TemperatureUnit::Id) { + g_temperature_unit = val.val.u8; + } +#endif // CONFIG_SUPPORT_UNIT_LOCALIZATION_CLUSTER + return (attribute_t *)attribute; } @@ -774,11 +806,131 @@ static void deferred_attribute_write(chip::System::Layer *layer, void *attribute current_attribute->val); } +// This is pure hack to not-break the sdk experience with v1.4.2 release +// Some attributes were moved from external storage to internal storage and the set_val/get_val APIs +// silently broke the application layer. This is a hack to not-break the sdk experience with v1.4.2 release +namespace { + +using namespace ::chip::app::Clusters; +using namespace ::chip::app::Clusters::UnitLocalization; + +// Helper function to find the cluster_id and attribute_id for internally managed attributes +esp_err_t find_cluster_and_endpoint_id_for_internally_managed_attribute(_attribute_base_t *attribute_base, + uint16_t *endpoint_id, uint32_t *cluster_id) +{ + _node_t *node = (_node_t *)node::get(); + if (!node) { + return ESP_ERR_INVALID_STATE; + } + + for (_endpoint_t *endpoint = node->endpoint_list; endpoint != nullptr; endpoint = endpoint->next) { + for (_cluster_t *cluster = endpoint->cluster_list; cluster != nullptr; cluster = cluster->next) { + for (_attribute_base_t *attr = cluster->attribute_list; attr != nullptr; attr = attr->next) { + if (attr == attribute_base) { + *endpoint_id = endpoint->endpoint_id; + *cluster_id = cluster::get_id((cluster_t *)cluster); + return ESP_OK; + } + } + } + } + + return ESP_ERR_NOT_FOUND; +} + +std::optional get_val_with_compatibility_exceptions(_attribute_t *attribute, + esp_matter_attr_val_t *val) +{ + uint32_t cluster_id = kInvalidClusterId; + uint16_t endpoint_id = kInvalidEndpointId; + + if (!(attribute->flags & ATTRIBUTE_FLAG_MANAGED_INTERNALLY)) { + // For non-internally-managed attributes, we can get cluster_id and endpoint_id directly + cluster_id = attribute->cluster_id; + endpoint_id = attribute->endpoint_id; + } else { + esp_err_t err = find_cluster_and_endpoint_id_for_internally_managed_attribute(attribute, &endpoint_id, &cluster_id); + VerifyOrReturnError(err == ESP_OK, err, + ESP_LOGE(TAG, "Failed to find cluster_id and endpoint_id for internally managed attribute")); + } + + switch (cluster_id) { +#ifdef CONFIG_SUPPORT_UNIT_LOCALIZATION_CLUSTER + case UnitLocalization::Id: { + switch (attribute->attribute_id) { + case Attributes::TemperatureUnit::Id: { + lock::status_t lock_status = lock::chip_stack_lock(portMAX_DELAY); + if (lock_status != lock::SUCCESS) { + return ESP_ERR_INVALID_STATE; + } + auto temperature_unit = UnitLocalizationServer::Instance().GetTemperatureUnit(); + lock::chip_stack_unlock(); + esp_matter_attr_val_t read_val = esp_matter_enum8(static_cast(temperature_unit)); + *val = read_val; + return ESP_OK; + } + } + } +#endif // CONFIG_SUPPORT_UNIT_LOCALIZATION_CLUSTER + } + + return std::nullopt; +} + +std::optional set_val_with_compatibility_exceptions(_attribute_t *attribute, + esp_matter_attr_val_t *val) +{ + uint32_t cluster_id = kInvalidClusterId; + uint16_t endpoint_id = kInvalidEndpointId; + + if (!(attribute->flags & ATTRIBUTE_FLAG_MANAGED_INTERNALLY)) { + // For non-internally-managed attributes, we can get cluster_id and endpoint_id directly + cluster_id = attribute->cluster_id; + endpoint_id = attribute->endpoint_id; + } else { + esp_err_t err = find_cluster_and_endpoint_id_for_internally_managed_attribute(attribute, &endpoint_id, &cluster_id); + VerifyOrReturnError(err == ESP_OK, err, + ESP_LOGE(TAG, "Failed to find cluster_id and endpoint_id for internally managed attribute")); + } + + switch (cluster_id) { +#ifdef CONFIG_SUPPORT_UNIT_LOCALIZATION_CLUSTER + + case UnitLocalization::Id: { + switch (attribute->attribute_id) { + case Attributes::TemperatureUnit::Id: { + auto temp_unit = static_cast(val->val.u8); + lock::status_t lock_status = lock::chip_stack_lock(portMAX_DELAY); + if (lock_status != lock::SUCCESS) { + return ESP_ERR_INVALID_STATE; + } + CHIP_ERROR chip_err = UnitLocalizationServer::Instance().SetTemperatureUnit(temp_unit); + lock::chip_stack_unlock(); + if (chip_err != CHIP_NO_ERROR) { + ESP_LOGE(TAG, "Failed to set temperature unit, err:%" CHIP_ERROR_FORMAT, chip_err.Format()); + return ESP_FAIL; + } + return ESP_OK; + } + } + } +#endif // CONFIG_SUPPORT_UNIT_LOCALIZATION_CLUSTER + } + + return std::nullopt; +} +} // anonymous namespace + esp_err_t set_val(attribute_t *attribute, esp_matter_attr_val_t *val) { VerifyOrReturnError(attribute, ESP_FAIL, ESP_LOGE(TAG, "Attribute cannot be NULL")); _attribute_t *current_attribute = (_attribute_t *)attribute; + auto err = set_val_with_compatibility_exceptions(current_attribute, val); + if (err.has_value()) { + return err.value(); + } + ESP_RETURN_ON_FALSE(!(current_attribute->flags & ATTRIBUTE_FLAG_MANAGED_INTERNALLY), ESP_ERR_NOT_SUPPORTED, TAG, "Attribute is not managed by esp matter data model"); @@ -825,6 +977,11 @@ esp_err_t get_val(attribute_t *attribute, esp_matter_attr_val_t *val) VerifyOrReturnError(attribute, ESP_ERR_INVALID_ARG, ESP_LOGE(TAG, "Attribute cannot be NULL")); _attribute_t *current_attribute = (_attribute_t *)attribute; + auto err = get_val_with_compatibility_exceptions(current_attribute, val); + if (err.has_value()) { + return err.value(); + } + ESP_RETURN_ON_FALSE(!(current_attribute->flags & ATTRIBUTE_FLAG_MANAGED_INTERNALLY), ESP_ERR_NOT_SUPPORTED, TAG, "Attribute is not managed by esp matter data model"); memcpy((void *)val, (void *)¤t_attribute->val, sizeof(esp_matter_attr_val_t));