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.
This commit is contained in:
Shubham Patil
2025-10-09 18:29:08 +05:30
parent 37a2c7d0b7
commit f87a4cd119
3 changed files with 174 additions and 2 deletions
@@ -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
@@ -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 */
@@ -27,8 +27,6 @@
#include <lib/core/DataModelTypes.h>
#include <app/clusters/identify-server/identify-server.h>
#include <app/util/endpoint-config-api.h>
#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 <app/clusters/unit-localization-server/unit-localization-server.h>
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<UnitLocalization::TempUnitEnum>(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(&current_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<esp_err_t> 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<uint8_t>(temperature_unit));
*val = read_val;
return ESP_OK;
}
}
}
#endif // CONFIG_SUPPORT_UNIT_LOCALIZATION_CLUSTER
}
return std::nullopt;
}
std::optional<esp_err_t> 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<TempUnitEnum>(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 *)&current_attribute->val, sizeof(esp_matter_attr_val_t));