Merge branch 'fix_endpoint_report' into 'main'

components/esp_matter: report attribute change in endpoint::enable and disable

See merge request app-frameworks/esp-matter!1427
This commit is contained in:
Shu Chen
2026-02-10 11:06:19 +00:00
5 changed files with 105 additions and 57 deletions
@@ -121,20 +121,6 @@ cluster_t * ABORT_CLUSTER_CREATE(cluster_t *cluster)
}
} // anonymous namespace
void delegate_init_callback_common(endpoint_t *endpoint)
{
uint16_t endpoint_id = endpoint::get_id(endpoint);
cluster_t *cluster = get_first(endpoint);
while (cluster) {
/* Delegate server init callback */
delegate_init_callback_t delegate_init_callback = get_delegate_init_callback(cluster);
if (delegate_init_callback) {
delegate_init_callback(get_delegate_impl(cluster), endpoint_id);
}
cluster = get_next(cluster);
}
}
cluster_t *create_default_binding_cluster(endpoint_t *endpoint)
{
/* Don't create binding cluster if it already exists on the endpoint */
@@ -213,11 +213,36 @@ esp_err_t read_min_unused_endpoint_id()
namespace endpoint {
static void report_parts_list_change_internal(endpoint_t *endpoint)
{
chip::EndpointId parent_endpoint_id = endpoint::get_parent_endpoint_id(endpoint);
while (parent_endpoint_id != chip::kInvalidEndpointId) {
MatterReportingAttributeChangeCallback(parent_endpoint_id, chip::app::Clusters::Descriptor::Id,
chip::app::Clusters::Descriptor::Attributes::PartsList::Id);
parent_endpoint_id = endpoint::get_parent_endpoint_id(endpoint::get(parent_endpoint_id));
}
MatterReportingAttributeChangeCallback(/* endpoint = */ 0, chip::app::Clusters::Descriptor::Id,
chip::app::Clusters::Descriptor::Attributes::PartsList::Id);
}
esp_err_t disable(endpoint_t *endpoint)
{
VerifyOrReturnError(endpoint, ESP_ERR_INVALID_ARG, ESP_LOGE(TAG, "Endpoint cannot be NULL"));
_endpoint_t *current_endpoint = (_endpoint_t *)endpoint;
current_endpoint->enabled = false;
{
esp_matter::lock::ScopedChipStackLock lock(portMAX_DELAY);
report_parts_list_change_internal(endpoint);
cluster_t *cluster = cluster::get_first(endpoint);
while (cluster) {
/* kClusterShutdown type shutdown callback */
cluster::shutdown_callback_t shutdown_callback = cluster::get_shutdown_callback(cluster);
if (shutdown_callback) {
shutdown_callback(endpoint::get_id(endpoint), chip::app::ClusterShutdownType::kClusterShutdown);
}
cluster = cluster::get_next(cluster);
}
}
return ESP_OK;
}
@@ -241,43 +266,62 @@ static esp_err_t init_identification(endpoint_t *endpoint)
return ESP_OK;
}
static void invoke_init_callbacks_internal(endpoint_t *endpoint)
{
cluster_t *cluster = cluster::get_first(endpoint);
while (cluster) {
/* Delegate server init callback */
cluster::delegate_init_callback_t delegate_init_callback = cluster::get_delegate_init_callback(cluster);
if (delegate_init_callback) {
delegate_init_callback(cluster::get_delegate_impl(cluster), endpoint::get_id(endpoint));
}
/* Add bounds callback */
cluster::add_bounds_callback_t add_bounds_callback = cluster::get_add_bounds_callback(cluster);
if (add_bounds_callback) {
add_bounds_callback(cluster);
}
/* Plugin server init callback */
cluster::plugin_server_init_callback_t plugin_server_init_callback =
cluster::get_plugin_server_init_callback(cluster);
if (plugin_server_init_callback) {
plugin_server_init_callback();
}
/* Initialization callback */
uint8_t flags = cluster::get_flags(cluster);
cluster::initialization_callback_t init_callback = cluster::get_init_callback(cluster);
if (init_callback) {
init_callback(endpoint::get_id(endpoint));
}
/* Init function */
if ((flags & CLUSTER_FLAG_SERVER) && (flags & CLUSTER_FLAG_INIT_FUNCTION)) {
cluster::function_cluster_init_t init_function =
(cluster::function_cluster_init_t)cluster::get_function(cluster, CLUSTER_FLAG_INIT_FUNCTION);
if (init_function) {
init_function(endpoint::get_id(endpoint));
}
}
cluster = cluster::get_next(cluster);
}
}
esp_err_t enable(endpoint_t *endpoint)
{
VerifyOrReturnError(endpoint, ESP_ERR_INVALID_ARG, ESP_LOGE(TAG, "Endpoint cannot be NULL"));
_endpoint_t *current_endpoint = (_endpoint_t *)endpoint;
current_endpoint->enabled = true;
init_identification(endpoint);
esp_matter::cluster::delegate_init_callback_common(endpoint);
chip::DeviceLayer::SystemLayer().ScheduleLambda([endpoint] {
cluster_t *cluster = cluster::get_first(endpoint);
while (cluster) {
/* Add bounds callback */
cluster::add_bounds_callback_t add_bounds_callback = cluster::get_add_bounds_callback(cluster);
if (add_bounds_callback) {
add_bounds_callback(cluster);
}
/* Plugin server init callback */
cluster::plugin_server_init_callback_t plugin_server_init_callback =
cluster::get_plugin_server_init_callback(cluster);
if (plugin_server_init_callback) {
plugin_server_init_callback();
}
/* Initialization callback */
uint8_t flags = cluster::get_flags(cluster);
cluster::initialization_callback_t init_callback = cluster::get_init_callback(cluster);
if (init_callback) {
init_callback(endpoint::get_id(endpoint));
}
if ((flags & CLUSTER_FLAG_SERVER) && (flags & CLUSTER_FLAG_INIT_FUNCTION)) {
cluster::function_cluster_init_t init_function =
(cluster::function_cluster_init_t)cluster::get_function(cluster, CLUSTER_FLAG_INIT_FUNCTION);
if (init_function) {
init_function(endpoint::get_id(endpoint));
}
}
cluster = cluster::get_next(cluster);
}
});
{
// Use the lock instead of schedule lambda to ensure the callbacks are invoked before esp_matter::start() returns.
esp_matter::lock::ScopedChipStackLock lock(portMAX_DELAY);
invoke_init_callbacks_internal(endpoint);
// Mark the endpoint as dirty so that the data model provider will report the attribute changes.
MatterReportingAttributeChangeCallback(endpoint::get_id(endpoint));
report_parts_list_change_internal(endpoint);
}
return ESP_OK;
}
@@ -1726,6 +1770,25 @@ esp_err_t destroy(node_t *node, endpoint_t *endpoint)
}
VerifyOrReturnError(current_endpoint != NULL, ESP_FAIL, ESP_LOGE(TAG, "Could not find the endpoint to delete"));
{
esp_matter::lock::ScopedChipStackLock lock(portMAX_DELAY);
cluster_t *cluster = cluster::get_first(endpoint);
while (cluster) {
/* TODO: kPermanentRemove type shutdown callback is not implemented yet in upstream code */
/* Shutdown function */
uint8_t flags = cluster::get_flags(cluster);
if ((flags & CLUSTER_FLAG_SERVER) && (flags & CLUSTER_FLAG_SHUTDOWN_FUNCTION)) {
cluster::function_cluster_shutdown_t shutdown_function =
(cluster::function_cluster_shutdown_t)cluster::get_function(cluster, CLUSTER_FLAG_SHUTDOWN_FUNCTION);
if (shutdown_function) {
shutdown_function(endpoint::get_id(endpoint));
}
}
cluster = cluster::get_next(cluster);
}
}
/* Parse and delete all clusters */
_cluster_t *cluster = current_endpoint->cluster_list;
while (cluster) {
@@ -40,15 +40,6 @@ esp_err_t enable_all();
}
namespace cluster {
/** Common cluster delegate init callback
*
* This is the common delegate init callback which calls the delegate init callbacks in the clusters.
*/
void delegate_init_callback_common(endpoint_t *endpoint);
}
namespace attribute {
/** Get the attribute value from the esp-matter storage
@@ -646,9 +646,13 @@ CHIP_ERROR provider::Attributes(const ConcreteClusterPath &path, ReadOnlyBufferB
void provider::Temporary_ReportAttributeChanged(const AttributePathParams &path)
{
cluster_t *cluster = cluster::get(path.mEndpointId, path.mClusterId);
VerifyOrReturn(cluster != nullptr);
VerifyOrReturn(cluster::increase_data_version(cluster) == ESP_OK);
VerifyOrReturn(!path.HasWildcardEndpointId());
// If the cluster is not wildcard, increase the data version
if (!path.HasWildcardClusterId()) {
cluster_t *cluster = cluster::get(path.mEndpointId, path.mClusterId);
VerifyOrReturn(cluster != nullptr);
VerifyOrReturn(cluster::increase_data_version(cluster) == ESP_OK);
}
mContext->dataModelChangeListener.MarkDirty(path);
}