diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 55d404d0e..563081619 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -28,7 +28,7 @@ variables: IDF_CHECKOUT_REF: "v5.4.1" # This variable represents the short hash of the connectedhomeip submodule. # Note: Do change this short hash on submodule update MRs. - CHIP_SHORT_HASH: "cf84d0360c" + CHIP_SHORT_HASH: "8f943388a" DOCKER_IMAGE_NAME: "espressif/chip-idf" .add_gitlab_ssh_key: &add_gitlab_ssh_key | diff --git a/README.md b/README.md index be10365f5..776884835 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ section in the ESP-Matter Programming Guide. ## Supported ESP-IDF and connectedhomeip versions -- This SDK currently works with commit [cf84d0360c] (https://github.com/project-chip/connectedhomeip/tree/cf84d0360c) of connectedhomeip. +- This SDK currently works with commit [8f943388af] (https://github.com/project-chip/connectedhomeip/tree/8f943388af) of connectedhomeip. - For Matter projects development with this SDK, it is recommended to utilize ESP-IDF [v5.4.1](https://github.com/espressif/esp-idf/tree/v5.4.1). - For ESP32C5 and ESP32C61, it is recommended to utilize ESP-IDF [v5.5.1](https://github.com/espressif/esp-idf/tree/v5.5.1). diff --git a/components/esp_matter/CMakeLists.txt b/components/esp_matter/CMakeLists.txt index 1e13bda50..b237f795b 100644 --- a/components/esp_matter/CMakeLists.txt +++ b/components/esp_matter/CMakeLists.txt @@ -22,25 +22,30 @@ if (CONFIG_ESP_MATTER_ENABLE_MATTER_SERVER) get_supported_cluster_dirs(SUPPORTED_CLUSTER_DIRS) foreach(CLUSTER_DIR ${SUPPORTED_CLUSTER_DIRS}) # If the cluster directory has .c/.cpp file, add it to SRC_DIRS_LIST - file(GLOB_RECURSE C_CPP_FILES "${CLUSTER_DIR}/*.c" "${CLUSTER_DIR}/*.cpp") + file(GLOB C_CPP_FILES "${CLUSTER_DIR}/*.c" "${CLUSTER_DIR}/*.cpp") if (C_CPP_FILES) list(APPEND SRC_DIRS_LIST "${CLUSTER_DIR}") endif() - file(GLOB_RECURSE CODEGEN_INTEGRATION_FILE "${CLUSTER_DIR}/CodegenIntegration.cpp") - if (CODEGEN_INTEGRATION_FILE) - list(APPEND EXCLUDE_SRCS_LIST "${CLUSTER_DIR}/CodegenIntegration.cpp") + file(GLOB_RECURSE CODEGEN_FILES "${CLUSTER_DIR}/Codegen*.cpp") + if (CODEGEN_FILES) + list(APPEND EXCLUDE_SRCS_LIST ${CODEGEN_FILES}) endif() endforeach() + list(APPEND SRC_DIRS_LIST "${MATTER_SDK_PATH}/src/app/clusters/on-off-server/codegen" + "${MATTER_SDK_PATH}/src/app/clusters/level-control/codegen") + + file(GLOB ESP_MATTER_CLUSTER_DIRS_LIST "data_model_provider/clusters/*") + foreach(ESP_MATTER_CLUSTER_DIR ${ESP_MATTER_CLUSTER_DIRS_LIST}) + list(APPEND SRC_DIRS_LIST ${ESP_MATTER_CLUSTER_DIR}) + endforeach() + list(APPEND SRC_DIRS_LIST "data_model" "data_model_provider" - "data_model_provider/clusters" "data_model/private") list(APPEND INCLUDE_DIRS_LIST "zap_common" "data_model") list(APPEND PRIV_INCLUDE_DIRS_LIST "data_model/private") - list(APPEND EXCLUDE_SRCS_LIST "${MATTER_SDK_PATH}/src/app/clusters/network-commissioning/CodegenInstance.cpp" - "${MATTER_SDK_PATH}/src/app/clusters/general-commissioning-server/TemporaryTestCoupling.cpp") endif(CONFIG_ESP_MATTER_ENABLE_DATA_MODEL) else() list(APPEND EXCLUDE_SRCS_LIST "esp_matter_identify.cpp" diff --git a/components/esp_matter/data_model/esp_matter_cluster.cpp b/components/esp_matter/data_model/esp_matter_cluster.cpp index 75fa80996..524c8d58c 100644 --- a/components/esp_matter/data_model/esp_matter_cluster.cpp +++ b/components/esp_matter/data_model/esp_matter_cluster.cpp @@ -847,6 +847,8 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) /* Commands */ command::create_set_utc_time(cluster); + cluster::set_init_and_shutdown_callbacks(cluster, ESPMatterTimeSynchronizationClusterServerInitCallback, + ESPMatterTimeSynchronizationClusterServerShutdownCallback); } event::create_time_failure(cluster); @@ -874,6 +876,8 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) /* Attributes not managed internally */ global::attribute::create_cluster_revision(cluster, cluster_revision); + cluster::set_init_and_shutdown_callbacks(cluster, ESPMatterUnitLocalizationClusterServerInitCallback, + ESPMatterUnitLocalizationClusterServerShutdownCallback); } return cluster; @@ -971,8 +975,7 @@ 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, IcdManagement::Id)); #if CONFIG_ENABLE_ICD_SERVER if (flags & CLUSTER_FLAG_SERVER) { - static const auto plugin_server_init_cb = CALL_ONCE(MatterIcdManagementPluginServerInitCallback); - set_plugin_server_init_callback(cluster, plugin_server_init_cb); + set_plugin_server_init_callback(cluster, nullptr); add_function_list(cluster, function_list, function_flags); /* Attributes managed internally */ @@ -983,6 +986,8 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) /* Attributes not managed internally */ global::attribute::create_cluster_revision(cluster, cluster_revision); + cluster::set_init_and_shutdown_callbacks(cluster, ESPMatterIcdManagementClusterServerInitCallback, + ESPMatterIcdManagementClusterServerShutdownCallback); } #endif // CONFIG_ENABLE_ICD_SERVER return cluster; @@ -1130,11 +1135,8 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) } /* groups */ namespace scenes_management { -const function_generic_t function_list[] = { - (function_generic_t)emberAfScenesManagementClusterServerInitCallback, - (function_generic_t)MatterScenesManagementClusterServerShutdownCallback, -}; -const int function_flags = CLUSTER_FLAG_INIT_FUNCTION | CLUSTER_FLAG_SHUTDOWN_FUNCTION; +const function_generic_t *function_list = nullptr; +const int function_flags = CLUSTER_FLAG_NONE; cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) { @@ -1158,6 +1160,8 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) } else { ESP_LOGE(TAG, "Config is NULL. Cannot add some attributes."); } + cluster::set_init_and_shutdown_callbacks(cluster, ESPMatterScenesManagementClusterServerInitCallback, + ESPMatterScenesManagementClusterServerShutdownCallback); } /* Commands */ @@ -1540,14 +1544,24 @@ static cluster_t *create(endpoint_t *endpoint, T *config, uint8_t flags, uint32_ namespace hepa_filter_monitoring { cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) { - return resource_monitoring::create(endpoint, config, flags,HepaFilterMonitoring::Id, cluster_revision); + cluster_t *cluster = resource_monitoring::create(endpoint, config, flags,HepaFilterMonitoring::Id, cluster_revision); + if (cluster && (flags & CLUSTER_FLAG_SERVER)) { + cluster::set_init_and_shutdown_callbacks(cluster, ESPMatterHepaFilterMonitoringClusterServerInitCallback, + ESPMatterHepaFilterMonitoringClusterServerShutdownCallback); + } + return cluster; } } /* hepa_filter_monitoring */ namespace activated_carbon_filter_monitoring { cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) { - return resource_monitoring::create(endpoint, config, flags,ActivatedCarbonFilterMonitoring::Id, cluster_revision); + cluster_t *cluster = resource_monitoring::create(endpoint, config, flags,ActivatedCarbonFilterMonitoring::Id, cluster_revision); + if (cluster && (flags & CLUSTER_FLAG_SERVER)) { + cluster::set_init_and_shutdown_callbacks(cluster, ESPMatterActivatedCarbonFilterMonitoringClusterServerInitCallback, + ESPMatterActivatedCarbonFilterMonitoringClusterServerShutdownCallback); + } + return cluster; } } /* activated_carbon_filter_monitoring */ @@ -2238,10 +2252,8 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) } /* relative_humidity_measurement */ namespace occupancy_sensing { -const function_generic_t function_list[] = { - (function_generic_t)emberAfOccupancySensingClusterServerInitCallback, -}; -const int function_flags = CLUSTER_FLAG_INIT_FUNCTION; +const function_generic_t *function_list = nullptr; +const int function_flags = CLUSTER_FLAG_NONE; cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) { @@ -2296,6 +2308,8 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) if (has(feature::vision::get_id())) { feature::vision::add(cluster); } + cluster::set_init_and_shutdown_callbacks(cluster, ESPMatterOccupancySensingClusterServerInitCallback, + ESPMatterOccupancySensingClusterServerShutdownCallback); } // if (flags & CLUSTER_FLAG_SERVER) if (flags & CLUSTER_FLAG_CLIENT) { @@ -2365,6 +2379,8 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) /* Attributes not managed internally */ global::attribute::create_cluster_revision(cluster, cluster_revision); + cluster::set_init_and_shutdown_callbacks(cluster, ESPMatterBooleanStateConfigurationClusterServerInitCallback, + ESPMatterBooleanStateConfigurationClusterServerShutdownCallback); } if (flags & CLUSTER_FLAG_CLIENT) { @@ -3121,6 +3137,8 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) feature::dynamic_power_flow::add(cluster); } } + cluster::set_init_and_shutdown_callbacks(cluster, ESPMatterPowerTopologyClusterServerInitCallback, + ESPMatterPowerTopologyClusterServerShutdownCallback); } // if (flags & CLUSTER_FLAG_SERVER) return cluster; @@ -3176,6 +3194,8 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) feature::power_quality::add(cluster); } } + cluster::set_init_and_shutdown_callbacks(cluster, ESPMatterElectricalPowerMeasurementClusterServerInitCallback, + ESPMatterElectricalPowerMeasurementClusterServerShutdownCallback); } // if (flags & CLUSTER_FLAG_SERVER) return cluster; @@ -3221,6 +3241,8 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) if (has(feature::periodic_energy::get_id())) { feature::periodic_energy::add(cluster); } + cluster::set_init_and_shutdown_callbacks(cluster, ESPMatterElectricalEnergyMeasurementClusterServerInitCallback, + ESPMatterElectricalEnergyMeasurementClusterServerShutdownCallback); } // if (flags & CLUSTER_FLAG_SERVER) return cluster; @@ -3379,6 +3401,8 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) attribute::create_abs_max_power(cluster, 0); /** Attributes not managed internally **/ global::attribute::create_cluster_revision(cluster, cluster_revision); + cluster::set_init_and_shutdown_callbacks(cluster, ESPMatterDeviceEnergyManagementClusterServerInitCallback, + ESPMatterDeviceEnergyManagementClusterServerShutdownCallback); } if (flags & CLUSTER_FLAG_CLIENT) { @@ -3925,7 +3949,8 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) command::create_provide_answer(cluster); command::create_provide_ice_candidates(cluster); command::create_end_session(cluster); - + cluster::set_init_and_shutdown_callbacks(cluster, ESPMatterWebRTCTransportProviderClusterServerInitCallback, + ESPMatterWebRTCTransportProviderClusterServerShutdownCallback); } if (flags & CLUSTER_FLAG_CLIENT) { @@ -4065,6 +4090,8 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) global::attribute::create_cluster_revision(cluster, cluster_revision); command::create_play_chime_sound(cluster); + cluster::set_init_and_shutdown_callbacks(cluster, ESPMatterChimeClusterServerInitCallback, + ESPMatterChimeClusterServerShutdownCallback); } if (flags & CLUSTER_FLAG_CLIENT) { diff --git a/components/esp_matter/data_model/esp_matter_command.cpp b/components/esp_matter/data_model/esp_matter_command.cpp index 467e85dec..e11323449 100644 --- a/components/esp_matter/data_model/esp_matter_command.cpp +++ b/components/esp_matter/data_model/esp_matter_command.cpp @@ -160,49 +160,6 @@ static esp_err_t esp_matter_command_callback_add_group_if_identifying(const Conc return ESP_OK; } -static esp_err_t esp_matter_command_callback_register_client(const ConcreteCommandPath &command_path, - TLVReader &tlv_data, void *opaque_ptr) -{ - chip::app::Clusters::IcdManagement::Commands::RegisterClient::DecodableType command_data; - chip::app::CommandHandler *command_obj = (chip::app::CommandHandler *)opaque_ptr; - CHIP_ERROR error = command_data.Decode(tlv_data, command_obj->GetAccessingFabricIndex()); - - if (error == CHIP_NO_ERROR) { -#if CONFIG_ENABLE_ICD_SERVER - emberAfIcdManagementClusterRegisterClientCallback((CommandHandler *)opaque_ptr, command_path, command_data); -#endif - } - return ESP_OK; -} - -static esp_err_t esp_matter_command_callback_unregister_client(const ConcreteCommandPath &command_path, - TLVReader &tlv_data, void *opaque_ptr) -{ - chip::app::Clusters::IcdManagement::Commands::UnregisterClient::DecodableType command_data; - chip::app::CommandHandler *command_obj = (chip::app::CommandHandler *)opaque_ptr; - CHIP_ERROR error = command_data.Decode(tlv_data, command_obj->GetAccessingFabricIndex()); - - if (error == CHIP_NO_ERROR) { -#if CONFIG_ENABLE_ICD_SERVER - emberAfIcdManagementClusterUnregisterClientCallback((CommandHandler *)opaque_ptr, command_path, command_data); -#endif - } - return ESP_OK; -} - -static esp_err_t esp_matter_command_callback_stay_active_request(const ConcreteCommandPath &command_path, - TLVReader &tlv_data, void *opaque_ptr) -{ - chip::app::Clusters::IcdManagement::Commands::StayActiveRequest::DecodableType command_data; - CHIP_ERROR error = Decode(tlv_data, command_data); - if (error == CHIP_NO_ERROR) { -#if CONFIG_ENABLE_ICD_SERVER - emberAfIcdManagementClusterStayActiveRequestCallback((CommandHandler *)opaque_ptr, command_path, command_data); -#endif - } - return ESP_OK; -} - static esp_err_t esp_matter_command_callback_off(const ConcreteCommandPath &command_path, TLVReader &tlv_data, void *opaque_ptr) { @@ -1112,26 +1069,6 @@ static esp_err_t esp_matter_command_callback_send_key(const ConcreteCommandPath return ESP_OK; } -static esp_err_t esp_matter_command_callback_suppress_alarm(const ConcreteCommandPath &command_path, TLVReader &tlv_data, void *opaque_ptr) -{ - chip::app::Clusters::BooleanStateConfiguration::Commands::SuppressAlarm::DecodableType command_data; - CHIP_ERROR error = Decode(tlv_data, command_data); - if (error == CHIP_NO_ERROR) { - emberAfBooleanStateConfigurationClusterSuppressAlarmCallback((CommandHandler *)opaque_ptr, command_path, command_data); - } - return ESP_OK; -} - -static esp_err_t esp_matter_command_callback_enable_disable_alarm(const ConcreteCommandPath &command_path, TLVReader &tlv_data, void *opaque_ptr) -{ - chip::app::Clusters::BooleanStateConfiguration::Commands::EnableDisableAlarm::DecodableType command_data; - CHIP_ERROR error = Decode(tlv_data, command_data); - if (error == CHIP_NO_ERROR) { - emberAfBooleanStateConfigurationClusterEnableDisableAlarmCallback((CommandHandler *)opaque_ptr, command_path, command_data); - } - return ESP_OK; -} - static esp_err_t esp_matter_command_callback_open(const ConcreteCommandPath &command_path, TLVReader &tlv_data, void *opaque_ptr) { chip::app::Clusters::ValveConfigurationAndControl::Commands::Open::DecodableType command_data; @@ -1152,64 +1089,6 @@ static esp_err_t esp_matter_command_callback_close(const ConcreteCommandPath &co return ESP_OK; } -static esp_err_t esp_matter_command_callback_set_utc_time(const ConcreteCommandPath &command_path, TLVReader &tlv_data, - void *opaque_ptr) -{ - chip::app::Clusters::TimeSynchronization::Commands::SetUTCTime::DecodableType command_data; - CHIP_ERROR error = Decode(tlv_data, command_data); - if (error == CHIP_NO_ERROR) { - emberAfTimeSynchronizationClusterSetUTCTimeCallback((CommandHandler *)opaque_ptr, command_path, command_data); - } - return ESP_OK; -} - -static esp_err_t esp_matter_command_callback_set_trusted_time_source(const ConcreteCommandPath &command_path, - TLVReader &tlv_data, void *opaque_ptr) -{ - chip::app::Clusters::TimeSynchronization::Commands::SetTrustedTimeSource::DecodableType command_data; - chip::app::CommandHandler *command_obj = (chip::app::CommandHandler *)opaque_ptr; - CHIP_ERROR error = command_data.Decode(tlv_data, command_obj->GetAccessingFabricIndex()); - - if (error == CHIP_NO_ERROR) { - emberAfTimeSynchronizationClusterSetTrustedTimeSourceCallback((CommandHandler *)opaque_ptr, command_path, - command_data); - } - return ESP_OK; -} - -static esp_err_t esp_matter_command_callback_set_time_zone(const ConcreteCommandPath &command_path, TLVReader &tlv_data, - void *opaque_ptr) -{ - chip::app::Clusters::TimeSynchronization::Commands::SetTimeZone::DecodableType command_data; - CHIP_ERROR error = Decode(tlv_data, command_data); - if (error == CHIP_NO_ERROR) { - emberAfTimeSynchronizationClusterSetTimeZoneCallback((CommandHandler *)opaque_ptr, command_path, command_data); - } - return ESP_OK; -} - -static esp_err_t esp_matter_command_callback_set_dst_offset(const ConcreteCommandPath &command_path, TLVReader &tlv_data, - void *opaque_ptr) -{ - chip::app::Clusters::TimeSynchronization::Commands::SetDSTOffset::DecodableType command_data; - CHIP_ERROR error = Decode(tlv_data, command_data); - if (error == CHIP_NO_ERROR) { - emberAfTimeSynchronizationClusterSetDSTOffsetCallback((CommandHandler *)opaque_ptr, command_path, command_data); - } - return ESP_OK; -} - -static esp_err_t esp_matter_command_callback_set_default_ntp(const ConcreteCommandPath &command_path, TLVReader &tlv_data, - void *opaque_ptr) -{ - chip::app::Clusters::TimeSynchronization::Commands::SetDefaultNTP::DecodableType command_data; - CHIP_ERROR error = Decode(tlv_data, command_data); - if (error == CHIP_NO_ERROR) { - emberAfTimeSynchronizationClusterSetDefaultNTPCallback((CommandHandler *)opaque_ptr, command_path, command_data); - } - return ESP_OK; -} - namespace esp_matter { namespace cluster { @@ -1829,7 +1708,7 @@ namespace command { command_t *create_register_client(cluster_t *cluster) { return esp_matter::command::create(cluster, IcdManagement::Commands::RegisterClient::Id, COMMAND_FLAG_ACCEPTED, - esp_matter_command_callback_register_client); + nullptr); } command_t *create_register_client_response(cluster_t *cluster) @@ -1841,13 +1720,13 @@ command_t *create_register_client_response(cluster_t *cluster) command_t *create_unregister_client(cluster_t *cluster) { return esp_matter::command::create(cluster, IcdManagement::Commands::UnregisterClient::Id, COMMAND_FLAG_ACCEPTED, - esp_matter_command_callback_unregister_client); + nullptr); } command_t *create_stay_active_request(cluster_t *cluster) { return esp_matter::command::create(cluster, IcdManagement::Commands::StayActiveRequest::Id, COMMAND_FLAG_ACCEPTED, - esp_matter_command_callback_stay_active_request); + nullptr); } command_t *create_stay_active_response(cluster_t *cluster) { @@ -2527,12 +2406,12 @@ namespace boolean_state_configuration { namespace command { command_t *create_suppress_alarm(cluster_t *cluster) { - return esp_matter::command::create(cluster, BooleanStateConfiguration::Commands::SuppressAlarm::Id, COMMAND_FLAG_ACCEPTED, esp_matter_command_callback_suppress_alarm); + return esp_matter::command::create(cluster, BooleanStateConfiguration::Commands::SuppressAlarm::Id, COMMAND_FLAG_ACCEPTED, nullptr); } command_t *create_enable_disable_alarm(cluster_t *cluster) { - return esp_matter::command::create(cluster, BooleanStateConfiguration::Commands::EnableDisableAlarm::Id, COMMAND_FLAG_ACCEPTED, esp_matter_command_callback_enable_disable_alarm); + return esp_matter::command::create(cluster, BooleanStateConfiguration::Commands::EnableDisableAlarm::Id, COMMAND_FLAG_ACCEPTED, nullptr); } } /* command */ @@ -2819,19 +2698,19 @@ namespace command { command_t *create_set_utc_time(cluster_t *cluster) { return esp_matter::command::create(cluster, TimeSynchronization::Commands::SetUTCTime::Id, COMMAND_FLAG_ACCEPTED, - esp_matter_command_callback_set_utc_time); + nullptr); } command_t *create_set_trusted_time_source(cluster_t *cluster) { return esp_matter::command::create(cluster, TimeSynchronization::Commands::SetTrustedTimeSource::Id, - COMMAND_FLAG_ACCEPTED, esp_matter_command_callback_set_trusted_time_source); + COMMAND_FLAG_ACCEPTED, nullptr); } command_t *create_set_time_zone(cluster_t *cluster) { return esp_matter::command::create(cluster, TimeSynchronization::Commands::SetTimeZone::Id, COMMAND_FLAG_ACCEPTED, - esp_matter_command_callback_set_time_zone); + nullptr); } command_t *create_set_time_zone_response(cluster_t *cluster) @@ -2843,13 +2722,13 @@ command_t *create_set_time_zone_response(cluster_t *cluster) command_t *create_set_dst_offset(cluster_t *cluster) { return esp_matter::command::create(cluster, TimeSynchronization::Commands::SetDSTOffset::Id, - COMMAND_FLAG_ACCEPTED, esp_matter_command_callback_set_dst_offset); + COMMAND_FLAG_ACCEPTED, nullptr); } command_t *create_set_default_ntp(cluster_t *cluster) { return esp_matter::command::create(cluster, TimeSynchronization::Commands::SetDefaultNTP::Id, - COMMAND_FLAG_ACCEPTED, esp_matter_command_callback_set_default_ntp); + COMMAND_FLAG_ACCEPTED, nullptr); } } /* command */ 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 3ebd54391..480d3633a 100644 --- a/components/esp_matter/data_model/esp_matter_data_model.cpp +++ b/components/esp_matter/data_model/esp_matter_data_model.cpp @@ -41,6 +41,7 @@ #include #include #include +#include "support/CodeUtils.h" #define ESP_MATTER_MAX_DEVICE_TYPE_COUNT CONFIG_ESP_MATTER_MAX_DEVICE_TYPE_COUNT #define ESP_MATTER_MAX_SEMANTIC_TAG_COUNT 3 @@ -881,7 +882,7 @@ esp_err_t get_val(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_ writer.Init(scoped_buf.Get(), k_max_tlv_size_to_read_attribute_value); chip::app::AttributeReportIBs::Builder reportBuilder = chip::app::AttributeReportIBs::Builder(); - reportBuilder.Init(&writer); + VerifyOrReturnValue(reportBuilder.Init(&writer) == CHIP_NO_ERROR, ESP_FAIL); chip::Access::SubjectDescriptor subjectDescriptor; auto concrete_path = chip::app::ConcreteAttributePath(endpoint_id, cluster_id, attribute_id); diff --git a/components/esp_matter/data_model/esp_matter_data_model.h b/components/esp_matter/data_model/esp_matter_data_model.h index 32cfc3d5c..7fe49906e 100644 --- a/components/esp_matter/data_model/esp_matter_data_model.h +++ b/components/esp_matter/data_model/esp_matter_data_model.h @@ -17,6 +17,7 @@ #include #include #include "app/ConcreteCommandPath.h" +#include "app/server-cluster/ServerClusterInterface.h" #include "app/util/af-types.h" #include "lib/core/DataModelTypes.h" #include "lib/core/TLVReader.h" @@ -550,7 +551,7 @@ typedef void (*initialization_callback_t)(uint16_t endpoint_id); * * This callback will be called when the data model is shutdown. */ -typedef void (*shutdown_callback_t)(uint16_t endpoint_id); +typedef void (*shutdown_callback_t)(uint16_t endpoint_id, chip::app::ClusterShutdownType shutdownType); /** Create cluster * diff --git a/components/esp_matter/data_model/esp_matter_delegate_callbacks.cpp b/components/esp_matter/data_model/esp_matter_delegate_callbacks.cpp index c3dfed90e..9213fba01 100644 --- a/components/esp_matter/data_model/esp_matter_delegate_callbacks.cpp +++ b/components/esp_matter/data_model/esp_matter_delegate_callbacks.cpp @@ -26,13 +26,9 @@ #include #include #include -#include #include #include -#include #include -#include -#include #include #include #include @@ -44,19 +40,25 @@ #include #include #include -#include -#include #include -#include #include #include -#include #include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + using namespace chip::app::Clusters; namespace esp_matter { namespace cluster { @@ -95,21 +97,6 @@ chip::BitMask get_energy_evse_enabled_optional_a return optional_attrs; } -chip::BitMask get_power_topology_enabled_optional_attributes(uint16_t endpoint_id) -{ - chip::BitMask optional_attrs = 0; - - if (endpoint::is_attribute_enabled(endpoint_id, PowerTopology::Id, PowerTopology::Attributes::AvailableEndpoints::Id)) { - optional_attrs.Set(PowerTopology::OptionalAttributes::kOptionalAttributeAvailableEndpoints); - } - - if (endpoint::is_attribute_enabled(endpoint_id, PowerTopology::Id, PowerTopology::Attributes::ActiveEndpoints::Id)) { - optional_attrs.Set(PowerTopology::OptionalAttributes::kOptionalAttributeActiveEndpoints); - } - - return optional_attrs; -} - chip::BitMask get_electrical_power_measurement_enabled_optional_attributes(uint16_t endpoint_id) { chip::BitMask optional_attrs = 0; @@ -191,7 +178,7 @@ void InitModeDelegate(void *delegate, uint16_t endpoint_id, uint32_t cluster_id) ModeBase::Delegate *mode_delegate = static_cast(delegate); uint32_t feature_map = get_feature_map_value(endpoint_id, cluster_id); modeInstance = new ModeBase::Instance(mode_delegate, endpoint_id, cluster_id, feature_map); - modeInstance->Init(); + (void)modeInstance->Init(); } void LaundryWasherModeDelegateInitCB(void *delegate, uint16_t endpoint_id) @@ -243,12 +230,16 @@ void MicrowaveOvenModeDelegateInitCB(void *delegate, uint16_t endpoint_id) } else { modeInstance = s_microwave_oven_mode_instances[endpoint_id]; } - modeInstance->Init(); + (void)modeInstance->Init(); } void DeviceEnergyManagementModeDelegateInitCB(void *delegate, uint16_t endpoint_id) { - InitModeDelegate(delegate, endpoint_id, DeviceEnergyManagementMode::Id); + VerifyOrReturn(delegate != nullptr); + DeviceEnergyManagement::Delegate *_delegate = static_cast(delegate); + chip::BitMask feature_map(get_feature_map_value(endpoint_id, DeviceEnergyManagement::Id)); + DeviceEnergyManagement::Instance *instance = new DeviceEnergyManagement::Instance(endpoint_id, *_delegate, feature_map); + (void)instance->Init(); } void EnergyEvseDelegateInitCB(void *delegate, uint16_t endpoint_id) @@ -261,7 +252,7 @@ void EnergyEvseDelegateInitCB(void *delegate, uint16_t endpoint_id) chip::BitMask optional_cmds = get_energy_evse_enabled_optional_commands(endpoint_id); energyEvseInstance = new EnergyEvse::Instance(endpoint_id, *energy_evse_delegate, chip::BitMask(feature_map), optional_attrs, optional_cmds); - energyEvseInstance->Init(); + (void)energyEvseInstance->Init(); } void MicrowaveOvenControlDelegateInitCB(void *delegate, uint16_t endpoint_id) @@ -300,7 +291,7 @@ void MicrowaveOvenControlDelegateInitCB(void *delegate, uint16_t endpoint_id) uint32_t feature_map = get_feature_map_value(endpoint_id, MicrowaveOvenControl::Id); microwaveOvenControlInstance = new MicrowaveOvenControl::Instance(microwave_oven_control_delegate, endpoint_id, MicrowaveOvenControl::Id, feature_map, *operationalStateInstance, *microwaveOvenModeInstance); - microwaveOvenControlInstance->Init(); + (void)microwaveOvenControlInstance->Init(); } void OperationalStateDelegateInitCB(void *delegate, uint16_t endpoint_id) @@ -316,7 +307,7 @@ void OperationalStateDelegateInitCB(void *delegate, uint16_t endpoint_id) } else { operationalStateInstance = s_operational_state_instances[endpoint_id]; } - operationalStateInstance->Init(); + (void)operationalStateInstance->Init(); } void FanControlDelegateInitCB(void *delegate, uint16_t endpoint_id) @@ -329,23 +320,17 @@ void FanControlDelegateInitCB(void *delegate, uint16_t endpoint_id) void HepaFilterMonitoringDelegateInitCB(void *delegate, uint16_t endpoint_id) { VerifyOrReturn(delegate != nullptr); - static ResourceMonitoring::Instance * hepaFilterMonitoringInstance = nullptr; ResourceMonitoring::Delegate *resource_monitoring_delegate = static_cast(delegate); - uint32_t feature_map = get_feature_map_value(endpoint_id, HepaFilterMonitoring::Id); - hepaFilterMonitoringInstance = new ResourceMonitoring::Instance(resource_monitoring_delegate, endpoint_id, HepaFilterMonitoring::Id, - static_cast(feature_map), ResourceMonitoring::DegradationDirectionEnum::kDown, true); - hepaFilterMonitoringInstance->Init(); + (void) chip::app::Clusters::ResourceMonitoring::SetDefaultDelegate(endpoint_id, HepaFilterMonitoring::Id, + resource_monitoring_delegate); } void ActivatedCarbonFilterMonitoringDelegateInitCB(void *delegate, uint16_t endpoint_id) { VerifyOrReturn(delegate != nullptr); - static ResourceMonitoring::Instance * activatedCarbonFilterMonitoringInstance = nullptr; ResourceMonitoring::Delegate *resource_monitoring_delegate = static_cast(delegate); - uint32_t feature_map = get_feature_map_value(endpoint_id, ActivatedCarbonFilterMonitoring::Id); - activatedCarbonFilterMonitoringInstance = new ResourceMonitoring::Instance(resource_monitoring_delegate, endpoint_id, ActivatedCarbonFilterMonitoring::Id, - static_cast(feature_map), ResourceMonitoring::DegradationDirectionEnum::kDown, true); - activatedCarbonFilterMonitoringInstance->Init(); + (void) chip::app::Clusters::ResourceMonitoring::SetDefaultDelegate(endpoint_id, ActivatedCarbonFilterMonitoring::Id, + resource_monitoring_delegate); } void LaundryDryerControlsDelegateInitCB(void *delegate, uint16_t endpoint_id) @@ -369,14 +354,14 @@ void DeviceEnergyManagementDelegateInitCB(void *delegate, uint16_t endpoint_id) DeviceEnergyManagement::Delegate *device_energy_management_delegate = static_cast(delegate); uint32_t feature_map = get_feature_map_value(endpoint_id, DeviceEnergyManagement::Id); deviceEnergyManagementInstance = new DeviceEnergyManagement::Instance(endpoint_id, *device_energy_management_delegate, chip::BitMask(feature_map)); - deviceEnergyManagementInstance->Init(); + (void)deviceEnergyManagementInstance->Init(); } void DoorLockDelegateInitCB(void *delegate, uint16_t endpoint_id) { VerifyOrReturn(delegate != nullptr); DoorLock::Delegate *door_lock_delegate = static_cast(delegate); - DoorLockServer::Instance().SetDelegate(endpoint_id, door_lock_delegate); + (void)DoorLockServer::Instance().SetDelegate(endpoint_id, door_lock_delegate); } void BooleanStateConfigurationDelegateInitCB(void *delegate, uint16_t endpoint_id) @@ -405,11 +390,9 @@ void PowerTopologyDelegateInitCB(void *delegate, uint16_t endpoint_id) VerifyOrReturn(delegate != nullptr); static PowerTopology::Instance * powerTopologyInstance = nullptr; PowerTopology::Delegate *power_topology_delegate = static_cast(delegate); - uint32_t feature_map = get_feature_map_value(endpoint_id, PowerTopology::Id); - chip::BitMask optional_attrs = get_power_topology_enabled_optional_attributes(endpoint_id); - powerTopologyInstance = new PowerTopology::Instance(endpoint_id, *power_topology_delegate, chip::BitMask(feature_map), optional_attrs); - powerTopologyInstance->Init(); + chip::BitMask feature_map(get_feature_map_value(endpoint_id, PowerTopology::Id)); + powerTopologyInstance = new PowerTopology::Instance(endpoint_id, *power_topology_delegate, feature_map); + (void)powerTopologyInstance->Init(); } void ElectricalPowerMeasurementDelegateInitCB(void *delegate, uint16_t endpoint_id) @@ -421,7 +404,7 @@ void ElectricalPowerMeasurementDelegateInitCB(void *delegate, uint16_t endpoint_ chip::BitMask optional_attrs = get_electrical_power_measurement_enabled_optional_attributes(endpoint_id); electricalPowerMeasurementInstance = new ElectricalPowerMeasurement::Instance(endpoint_id, *electrical_power_measurement_delegate, chip::BitMask(feature_map), optional_attrs); - electricalPowerMeasurementInstance->Init(); + (void)electricalPowerMeasurementInstance->Init(); } void LaundryWasherControlsDelegateInitCB(void *delegate, uint16_t endpoint_id) @@ -473,7 +456,7 @@ void ThreadBorderRouterManagementDelegateInitCB(void *delegate, uint16_t endpoin assert(thread_br_delegate->GetPanChangeSupported() == pan_change_supported); ThreadBorderRouterManagement::ServerInstance *server_instance = chip::Platform::New(endpoint_id, thread_br_delegate, chip::Server::GetInstance().GetFailSafeContext()); - server_instance->Init(); + (void)server_instance->Init(); } void ServiceAreaDelegateInitCB(void *delegate, uint16_t endpoint_id) @@ -491,7 +474,7 @@ void WaterHeaterManagementDelegateInitCB(void *delegate, uint16_t endpoint_id) WaterHeaterManagement::Delegate *whtr_delegate = static_cast(delegate); uint32_t feature_map = get_feature_map_value(endpoint_id, WaterHeaterManagement::Id); wHtrInstance = new WaterHeaterManagement::Instance(endpoint_id, *whtr_delegate, chip::BitMask(feature_map)); - wHtrInstance->Init(); + (void)wHtrInstance->Init(); } void EnergyPreferenceDelegateInitCB(void *delegate, uint16_t endpoint_id) @@ -514,7 +497,7 @@ void CommissionerControlDelegateInitCB(void *delegate, uint16_t endpoint_id) CommissionerControl::CommissionerControlServer *commissioner_control_instance = nullptr; commissioner_control_instance = new CommissionerControl::CommissionerControlServer(commissioner_control_delegate, endpoint_id); - commissioner_control_instance->Init(); + (void)commissioner_control_instance->Init(); } void ActionsDelegateInitCB(void *delegate, uint16_t endpoint_id) @@ -523,7 +506,7 @@ void ActionsDelegateInitCB(void *delegate, uint16_t endpoint_id) static Actions::ActionsServer *actionsServer = nullptr; Actions::Delegate *actions_delegate = static_cast(delegate); actionsServer = new Actions::ActionsServer(endpoint_id, *actions_delegate); - actionsServer->Init(); + (void)actionsServer->Init(); } @@ -542,17 +525,17 @@ void OtaSoftwareUpdateProviderDelegateInitCB(void *delegate, uint16_t endpoint_i void DiagnosticLogsDelegateInitCB(void *delegate, uint16_t endpoint_id) { - VerifyOrReturn(delegate != nullptr); + VerifyOrReturn(delegate != nullptr && endpoint_id == chip::kRootEndpointId); DiagnosticLogs::DiagnosticLogsProviderDelegate *diagnostic_logs_delegate = static_cast(delegate); - DiagnosticLogs::DiagnosticLogsServer::Instance().SetDiagnosticLogsProviderDelegate(endpoint_id, diagnostic_logs_delegate); + DiagnosticLogs::SetDiagnosticLogsProviderDelegate(diagnostic_logs_delegate); } void ChimeDelegateInitCB(void *delegate, uint16_t endpoint_id) { VerifyOrReturn(delegate != nullptr); ChimeDelegate *chime_delegate = static_cast(delegate); - ChimeServer *chime_server = new ChimeServer(endpoint_id, *chime_delegate); - chime_server->Init(); + Chime::ChimeServer *chime_server = new Chime::ChimeServer(endpoint_id, *chime_delegate); + (void)chime_server->Init(); } void ClosureControlDelegateInitCB(void *delegate, uint16_t endpoint_id) @@ -562,7 +545,7 @@ void ClosureControlDelegateInitCB(void *delegate, uint16_t endpoint_id) ClosureControl::MatterContext *matter_context = new ClosureControl::MatterContext(endpoint_id); ClosureControl::ClusterLogic *cluster_logic = new ClosureControl::ClusterLogic(*closure_control_delegate, *matter_context); ClosureControl::Interface *server_interface = new ClosureControl::Interface(endpoint_id, *cluster_logic); - server_interface->Init(); + (void)server_interface->Init(); } @@ -573,19 +556,15 @@ void ClosureDimensionDelegateInitCB(void *delegate, uint16_t endpoint_id) ClosureDimension::MatterContext *matter_context = new ClosureDimension::MatterContext(endpoint_id); ClosureDimension::ClusterLogic *cluster_logic = new ClosureDimension::ClusterLogic(*closure_dimension_delegate, *matter_context); ClosureDimension::Interface *server_interface = new ClosureDimension::Interface(endpoint_id, *cluster_logic); - server_interface->Init(); + (void)server_interface->Init(); } void PushAvStreamTransportDelegateInitCB(void *delegate, uint16_t endpoint_id) { VerifyOrReturn(delegate != nullptr); - static PushAvStreamTransportServer * pushavstreamtransportserverinstance = nullptr; PushAvStreamTransportDelegate *push_av_stream_transport_delegate = static_cast(delegate); - uint32_t feature_map = get_feature_map_value(endpoint_id, PushAvStreamTransport::Id); - pushavstreamtransportserverinstance = new PushAvStreamTransportServer(endpoint_id, chip::BitMask(feature_map)); - pushavstreamtransportserverinstance->SetDelegate(push_av_stream_transport_delegate); - pushavstreamtransportserverinstance->Init(); + chip::app::Clusters::PushAvStreamTransport::SetDelegate(endpoint_id, push_av_stream_transport_delegate); } @@ -595,7 +574,7 @@ void CommodityTariffDelegateInitCB(void *delegate, uint16_t endpoint_id) CommodityTariff::Delegate *commodity_tariff_delegate = static_cast(delegate); uint32_t feature_map = get_feature_map_value(endpoint_id, CommodityTariff::Id); CommodityTariff::Instance *commodity_tariff_instance = new CommodityTariff::Instance(endpoint_id, *commodity_tariff_delegate, chip::BitMask(feature_map)); - commodity_tariff_instance->Init(); + (void)commodity_tariff_instance->Init(); } @@ -605,7 +584,7 @@ void CommodityPriceDelegateInitCB(void *delegate, uint16_t endpoint_id) CommodityPrice::Delegate *commodity_price_delegate = static_cast(delegate); uint32_t feature_map = get_feature_map_value(endpoint_id, CommodityPrice::Id); CommodityPrice::Instance *commodity_price_instance = new CommodityPrice::Instance(endpoint_id, *commodity_price_delegate, chip::BitMask(feature_map)); - commodity_price_instance->Init(); + (void)commodity_price_instance->Init(); } @@ -615,7 +594,7 @@ void ElectricalGridConditionsDelegateInitCB(void *delegate, uint16_t endpoint_id ElectricalGridConditions::Delegate *electrical_grid_conditions_delegate = static_cast(delegate); uint32_t feature_map = get_feature_map_value(endpoint_id, ElectricalGridConditions::Id); ElectricalGridConditions::Instance *electrical_grid_conditions_instance = new ElectricalGridConditions::Instance(endpoint_id, *electrical_grid_conditions_delegate, chip::BitMask(feature_map)); - electrical_grid_conditions_instance->Init(); + (void)electrical_grid_conditions_instance->Init(); } /* Not a delegate but an Initialization callback */ diff --git a/components/esp_matter/data_model_provider/clusters/access_control_integration.cpp b/components/esp_matter/data_model_provider/clusters/access_control/integration.cpp similarity index 94% rename from components/esp_matter/data_model_provider/clusters/access_control_integration.cpp rename to components/esp_matter/data_model_provider/clusters/access_control/integration.cpp index b1e69c4aa..28e816b07 100644 --- a/components/esp_matter/data_model_provider/clusters/access_control_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/access_control/integration.cpp @@ -36,11 +36,11 @@ void ESPMatterAccessControlClusterServerInitCallback(EndpointId endpoint) } } -void ESPMatterAccessControlClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterAccessControlClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { // We implement the cluster as a singleton on the root endpoint. VerifyOrReturn(endpointId == kRootEndpointId); - CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster()); + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster(), shutdownType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to unregister AccessControl - Error %" CHIP_ERROR_FORMAT, err.Format()); } diff --git a/components/esp_matter/data_model_provider/clusters/administrator_commissioning_integration.cpp b/components/esp_matter/data_model_provider/clusters/administrator_commissioning/integration.cpp similarity index 95% rename from components/esp_matter/data_model_provider/clusters/administrator_commissioning_integration.cpp rename to components/esp_matter/data_model_provider/clusters/administrator_commissioning/integration.cpp index 7495eb28c..9b1e8ef1d 100644 --- a/components/esp_matter/data_model_provider/clusters/administrator_commissioning_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/administrator_commissioning/integration.cpp @@ -52,9 +52,9 @@ void ESPMatterAdministratorCommissioningClusterServerInitCallback(EndpointId end } } -void ESPMatterAdministratorCommissioningClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterAdministratorCommissioningClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { - CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster()); + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster(), shutdownType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Admin Commissioning unregister error: %" CHIP_ERROR_FORMAT, err.Format()); } diff --git a/components/esp_matter/data_model_provider/clusters/basic_information_integration.cpp b/components/esp_matter/data_model_provider/clusters/basic_information/integration.cpp similarity index 96% rename from components/esp_matter/data_model_provider/clusters/basic_information_integration.cpp rename to components/esp_matter/data_model_provider/clusters/basic_information/integration.cpp index a5528acef..4d035fea3 100644 --- a/components/esp_matter/data_model_provider/clusters/basic_information_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/basic_information/integration.cpp @@ -59,12 +59,12 @@ void ESPMatterBasicInformationClusterServerInitCallback(EndpointId endpoint) } } -void ESPMatterBasicInformationClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterBasicInformationClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { // We implement the cluster as a singleton on the root endpoint. VerifyOrReturn(endpointId == kRootEndpointId); CHIP_ERROR err = - esp_matter::data_model::provider::get_instance().registry().Unregister(gRegistration.serverClusterInterface); + esp_matter::data_model::provider::get_instance().registry().Unregister(gRegistration.serverClusterInterface, shutdownType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to unregister BasicInformation - Error: %" CHIP_ERROR_FORMAT, err.Format()); } diff --git a/components/esp_matter/data_model_provider/clusters/binding_integration.cpp b/components/esp_matter/data_model_provider/clusters/binding/integration.cpp similarity index 92% rename from components/esp_matter/data_model_provider/clusters/binding_integration.cpp rename to components/esp_matter/data_model_provider/clusters/binding/integration.cpp index af16164aa..b88881e64 100644 --- a/components/esp_matter/data_model_provider/clusters/binding_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/binding/integration.cpp @@ -13,7 +13,7 @@ // limitations under the License. #include -#include +#include #include #include @@ -41,10 +41,10 @@ void ESPMatterBindingClusterServerInitCallback(EndpointId endpointId) } } -void ESPMatterBindingClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterBindingClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { CHIP_ERROR err = - esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[endpointId].Cluster()); + esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[endpointId].Cluster(), shutdownType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to unregister Binding on endpoint %u - Error: %" CHIP_ERROR_FORMAT, endpointId, err.Format()); diff --git a/components/esp_matter/data_model_provider/clusters/boolean_state_integration.cpp b/components/esp_matter/data_model_provider/clusters/boolean_state/integration.cpp similarity index 79% rename from components/esp_matter/data_model_provider/clusters/boolean_state_integration.cpp rename to components/esp_matter/data_model_provider/clusters/boolean_state/integration.cpp index ff2486090..43df8e41d 100644 --- a/components/esp_matter/data_model_provider/clusters/boolean_state_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/boolean_state/integration.cpp @@ -13,9 +13,10 @@ // limitations under the License. #include -#include +#include #include #include +#include "integration.h" using namespace chip; using namespace chip::app; @@ -41,10 +42,10 @@ void ESPMatterBooleanStateClusterServerInitCallback(EndpointId endpointId) } } -void ESPMatterBooleanStateClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterBooleanStateClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { CHIP_ERROR err = - esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[endpointId].Cluster()); + esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[endpointId].Cluster(), shutdownType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to unregister BooleanState on endpoint %u - Error: %" CHIP_ERROR_FORMAT, endpointId, err.Format()); @@ -55,3 +56,15 @@ void ESPMatterBooleanStateClusterServerShutdownCallback(EndpointId endpointId) void MatterBooleanStatePluginServerInitCallback() {} void MatterBooleanStatePluginServerShutdownCallback() {} + +namespace chip::app::Clusters::BooleanState { + +BooleanStateCluster * FindClusterOnEndpoint(EndpointId endpointId) +{ + if (gServers[endpointId].IsConstructed()) { + return &gServers[endpointId].Cluster(); + } + return nullptr; +} + +} // namespace chip::app::Clusters::BooleanState diff --git a/components/esp_matter/data_model_provider/clusters/boolean_state/integration.h b/components/esp_matter/data_model_provider/clusters/boolean_state/integration.h new file mode 100644 index 000000000..10920ea3d --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/boolean_state/integration.h @@ -0,0 +1,23 @@ +// Copyright 2026 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. + +#pragma once + +#include + +namespace chip::app::Clusters::BooleanState { + +BooleanStateCluster * FindClusterOnEndpoint(EndpointId endpointId); + +} // namespace chip::app::Clusters::BooleanState diff --git a/components/esp_matter/data_model_provider/clusters/boolean_state_configuration/integration.cpp b/components/esp_matter/data_model_provider/clusters/boolean_state_configuration/integration.cpp new file mode 100644 index 000000000..eb7e1a7ce --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/boolean_state_configuration/integration.cpp @@ -0,0 +1,137 @@ +// Copyright 2026 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. + +#include "integration.h" +#include "esp_matter_attribute_utils.h" +#include "esp_matter_data_model.h" +#include "esp_matter_data_model_priv.h" +#include "esp_matter_data_model_provider.h" +#include +#include "app/clusters/boolean-state-configuration-server/BooleanStateConfigurationCluster.h" +#include "app/server-cluster/ServerClusterInterfaceRegistry.h" +#include "clusters/BooleanStateConfiguration/Enums.h" + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::BooleanStateConfiguration; + +namespace { +std::unordered_map> gServers; + +esp_err_t get_attr_val(esp_matter::cluster_t *cluster, uint32_t attribute_id, esp_matter_attr_val_t &val) +{ + esp_matter::attribute_t *attr = esp_matter::attribute::get(cluster, attribute_id); + if (!attr) { + return ESP_FAIL; + } + return esp_matter::attribute::get_val_internal(attr, &val); +} + +CHIP_ERROR GetClusterConfig(EndpointId endpointId, BitMask &featureMap, uint8_t &supportedSensitivityLevels, + uint8_t &defaultSensitivityLevel, + BooleanStateConfiguration::AlarmModeBitmap &alarmsSupported, + BooleanStateConfigurationCluster::OptionalAttributesSet &optionalAttrSet) +{ + esp_matter::cluster_t *cluster = esp_matter::cluster::get(endpointId, BooleanStateConfiguration::Id); + if (!cluster) { + return CHIP_ERROR_NOT_FOUND; + } + esp_matter_attr_val_t tmp_attr_val; + VerifyOrReturnError(get_attr_val(cluster, Globals::Attributes::FeatureMap::Id, tmp_attr_val) == ESP_OK && + tmp_attr_val.type == ESP_MATTER_VAL_TYPE_BITMAP32, + CHIP_ERROR_INTERNAL); + featureMap = BitMask(tmp_attr_val.val.u32); + if (featureMap.Has(Feature::kSensitivityLevel)) { + VerifyOrReturnError(get_attr_val(cluster, Attributes::SupportedSensitivityLevels::Id, tmp_attr_val) == ESP_OK && + tmp_attr_val.type == ESP_MATTER_VAL_TYPE_UINT8, + CHIP_ERROR_INTERNAL); + supportedSensitivityLevels = tmp_attr_val.val.u8; + if (get_attr_val(cluster, Attributes::DefaultSensitivityLevel::Id, tmp_attr_val) == ESP_OK && + tmp_attr_val.type == ESP_MATTER_VAL_TYPE_UINT8) { + defaultSensitivityLevel = tmp_attr_val.val.u8; + optionalAttrSet.Set(); + } + } + if (featureMap.Has(Feature::kAudible) || featureMap.Has(Feature::kVisual)) { + VerifyOrReturnError(get_attr_val(cluster, Attributes::AlarmsSupported::Id, tmp_attr_val) == ESP_OK && + tmp_attr_val.type == ESP_MATTER_VAL_TYPE_BITMAP8, + CHIP_ERROR_INTERNAL); + alarmsSupported = AlarmModeBitmap(tmp_attr_val.val.u8); + if (esp_matter::attribute::get(cluster, Attributes::AlarmsEnabled::Id)) { + optionalAttrSet.Set(); + } + } + if (esp_matter::attribute::get(cluster, Attributes::SensorFault::Id)) { + optionalAttrSet.Set(); + } + return CHIP_NO_ERROR; +} + +} // namespace + +namespace chip::app::Clusters::BooleanStateConfiguration { + +BooleanStateConfigurationCluster *FindClusterOnEndpoint(EndpointId endpointId) +{ + if (gServers[endpointId].IsConstructed()) { + return &gServers[endpointId].Cluster(); + } + return nullptr; +} +} // namespace chip::app::Clusters::BooleanStateConfiguration + +void ESPMatterBooleanStateConfigurationClusterServerInitCallback(EndpointId endpointId) +{ + if (gServers[endpointId].IsConstructed()) { + return; + } + BitMask featureMap; + uint8_t supportedSensitivityLevels = 0, defaultSensitivityLevel = 0; + AlarmModeBitmap alarmsSupported = AlarmModeBitmap::kAudible; + BooleanStateConfigurationCluster::OptionalAttributesSet optionalAttrSet; + CHIP_ERROR err = GetClusterConfig(endpointId, featureMap, supportedSensitivityLevels, defaultSensitivityLevel, + alarmsSupported, optionalAttrSet); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to get config of BooleanStateConfiguration - Error %" CHIP_ERROR_FORMAT, + err.Format()); + return; + } + gServers[endpointId].Create(endpointId, featureMap, optionalAttrSet, + BooleanStateConfigurationCluster::StartupConfiguration{ + .supportedSensitivityLevels = supportedSensitivityLevels, + .defaultSensitivityLevel = defaultSensitivityLevel, + .alarmsSupported = alarmsSupported, + }); + err = esp_matter::data_model::provider::get_instance().registry().Register(gServers[endpointId].Registration()); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to register BooleanStateConfiguration - Error %" CHIP_ERROR_FORMAT, + err.Format()); + } +} + +void ESPMatterBooleanStateConfigurationClusterServerShutdownCallback(EndpointId endpointId, + ClusterShutdownType shutdownType) +{ + if (!gServers[endpointId].IsConstructed()) { + return; + } + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister( + &gServers[endpointId].Cluster(), shutdownType); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to unregister BooleanStateConfiguration - Error %" CHIP_ERROR_FORMAT, + err.Format()); + } + gServers[endpointId].Destroy(); +} diff --git a/components/esp_matter/data_model_provider/clusters/boolean_state_configuration/integration.h b/components/esp_matter/data_model_provider/clusters/boolean_state_configuration/integration.h new file mode 100644 index 000000000..40955b213 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/boolean_state_configuration/integration.h @@ -0,0 +1,101 @@ +// Copyright 2026 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. + +#pragma once + +#include +#include + +namespace chip::app::Clusters::BooleanStateConfiguration { + +// Get access to the underlying cluster registered on the given endpoint +BooleanStateConfigurationCluster *FindClusterOnEndpoint(EndpointId endpointId); + +//////////////////////////////////////////////////////////////////////////////////// +// The methods below are DEPRECATED. Please interact with the cluster directly +// via `FindClusterOnEndpoint` (for code generated builds) +//////////////////////////////////////////////////////////////////////////////////// +inline void SetDefaultDelegate(EndpointId endpoint, Delegate *delegate) +{ + // NOTE: this will only work AFTER cluster startup (i.e. emberAfClusterInit) + if (auto cluster = FindClusterOnEndpoint(endpoint); cluster != nullptr) { + cluster->SetDelegate(delegate); + } +} + +inline Delegate *GetDefaultDelegate(EndpointId endpoint) +{ + if (auto cluster = FindClusterOnEndpoint(endpoint); cluster != nullptr) { + return cluster->GetDelegate(); + } + return nullptr; +} + +inline CHIP_ERROR SetAlarmsActive(EndpointId ep, BitMask alarms) +{ + auto cluster = FindClusterOnEndpoint(ep); + VerifyOrReturnError(cluster != nullptr, CHIP_ERROR_NO_ENDPOINT); + auto status = cluster->SetAlarmsActive(alarms); + return (status == Protocols::InteractionModel::Status::Success) ? CHIP_NO_ERROR + : CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(status); +} + +inline CHIP_ERROR SetAllEnabledAlarmsActive(EndpointId ep) +{ + auto cluster = FindClusterOnEndpoint(ep); + VerifyOrReturnError(cluster != nullptr, CHIP_ERROR_NO_ENDPOINT); + auto status = cluster->SetAllEnabledAlarmsActive(); + return (status == Protocols::InteractionModel::Status::Success) ? CHIP_NO_ERROR + : CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(status); +} + +inline CHIP_ERROR ClearAllAlarms(EndpointId ep) +{ + auto cluster = FindClusterOnEndpoint(ep); + VerifyOrReturnError(cluster != nullptr, CHIP_ERROR_NO_ENDPOINT); + cluster->ClearAllAlarms(); + return CHIP_NO_ERROR; +} + +inline CHIP_ERROR SuppressAlarms(EndpointId ep, BitMask alarms) +{ + auto cluster = FindClusterOnEndpoint(ep); + VerifyOrReturnError(cluster != nullptr, CHIP_ERROR_NO_ENDPOINT); + auto status = cluster->SuppressAlarms(alarms); + return (status == Protocols::InteractionModel::Status::Success) ? CHIP_NO_ERROR + : CHIP_ERROR_IM_GLOBAL_STATUS_VALUE(status); +} + +inline CHIP_ERROR SetCurrentSensitivityLevel(EndpointId ep, uint8_t level) +{ + auto cluster = FindClusterOnEndpoint(ep); + VerifyOrReturnError(cluster != nullptr, CHIP_ERROR_NO_ENDPOINT); + return cluster->SetCurrentSensitivityLevel(level); +} + +inline CHIP_ERROR EmitSensorFault(EndpointId ep, BitMask fault) +{ + auto cluster = FindClusterOnEndpoint(ep); + VerifyOrReturnError(cluster != nullptr, CHIP_ERROR_NO_ENDPOINT); + cluster->GenerateSensorFault(fault); + return CHIP_NO_ERROR; +} + +inline bool HasFeature(EndpointId ep, Feature feature) +{ + auto cluster = FindClusterOnEndpoint(ep); + VerifyOrReturnValue(cluster != nullptr, false); + return cluster->GetFeatures().Has(feature); +} +} // namespace chip::app::Clusters::BooleanStateConfiguration diff --git a/components/esp_matter/data_model_provider/clusters/chime/integration.cpp b/components/esp_matter/data_model_provider/clusters/chime/integration.cpp new file mode 100644 index 000000000..6ceef1b5f --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/chime/integration.cpp @@ -0,0 +1,39 @@ +// Copyright 2026 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. + +#include "integration.h" +#include "esp_matter_data_model_provider.h" + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::Chime; + +namespace chip::app::Clusters::Chime { +ChimeServer::~ChimeServer() +{ + RETURN_SAFELY_IGNORED esp_matter::data_model::provider::get_instance().registry().Unregister(&(mCluster.Cluster())); +} + +CHIP_ERROR ChimeServer::Init() +{ + return esp_matter::data_model::provider::get_instance().registry().Register(mCluster.Registration()); +} +} // namespace chip::app::Clusters +void ESPMatterChimeClusterServerInitCallback(EndpointId) {} +void ESPMatterChimeClusterServerShutdownCallback(EndpointId, ClusterShutdownType) {} + +// Stub callbacks for ZAP generated code +void MatterChimePluginServerInitCallback() {} +void MatterChimePluginServerShutdownCallback() {} diff --git a/components/esp_matter/data_model_provider/clusters/chime/integration.h b/components/esp_matter/data_model_provider/clusters/chime/integration.h new file mode 100644 index 000000000..2e3e3bb39 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/chime/integration.h @@ -0,0 +1,81 @@ +// Copyright 2026 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. + +#pragma once + +#include +#include + +namespace chip::app::Clusters::Chime { + +class ChimeServer { +public: + /** + * Creates a chime server instance. This is just a backwards compatibility wrapper around the ChimeCluster. + * @param aEndpointId The endpoint on which this cluster exists. This must match the zap configuration. + * @param aDelegate A reference to the delegate to be used by this server. + * Note: the caller must ensure that the delegate lives throughout the instance's lifetime. + */ + ChimeServer(EndpointId endpointId, ChimeDelegate &delegate) + : mCluster(endpointId, delegate) + { + } + ~ChimeServer(); + + /** + * Register the chime cluster instance with the ESP-Matter data model provider. + * @return Returns an error if registration fails. + */ + CHIP_ERROR Init(); + + // Attribute Setters + /** + * Sets the SelectedChime attribute. Note, this also handles writing the new value into non-volatile storage. + * @param chimeSoundID The value to which the SelectedChime is to be set. + * @return Returns a ConstraintError if the chimeSoundID value is not valid. Returns Success otherwise. + */ + Protocols::InteractionModel::Status SetSelectedChime(uint8_t chimeSoundID) + { + return mCluster.Cluster().SetSelectedChime(chimeSoundID); + } + + /** + * Sets the Enabled attribute. Note, this also handles writing the new value into non-volatile storage. + * @param Enabled The value to which the Enabled is to be set. + */ + Protocols::InteractionModel::Status SetEnabled(bool enabled) { return mCluster.Cluster().SetEnabled(enabled); } + + // Attribute Getters + /** + * @return The Current SelectedChime. + */ + uint8_t GetSelectedChime() const { return mCluster.Cluster().GetSelectedChime(); } + + /** + * @return The Enabled attribute.. + */ + bool GetEnabled() const { return mCluster.Cluster().GetEnabled(); } + + /** + * @return The endpoint ID. + */ + EndpointId GetEndpointId() { return mCluster.Cluster().GetPaths()[0].mEndpointId; } + + // Cluster constants from the spec + static constexpr uint8_t kMaxChimeSoundNameSize = ChimeCluster::kMaxChimeSoundNameSize; + + // The Code Driven ChimeCluster instance + chip::app::RegisteredServerCluster mCluster; +}; +} // namespace chip::app::Clusters::Chime diff --git a/components/esp_matter/data_model_provider/clusters/descriptor_integration.cpp b/components/esp_matter/data_model_provider/clusters/descriptor/integration.cpp similarity index 98% rename from components/esp_matter/data_model_provider/clusters/descriptor_integration.cpp rename to components/esp_matter/data_model_provider/clusters/descriptor/integration.cpp index 5433aaabb..c525c5c9a 100644 --- a/components/esp_matter/data_model_provider/clusters/descriptor_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/descriptor/integration.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include +#include #include #include @@ -111,7 +111,7 @@ void ESPMatterDescriptorClusterServerInitCallback(EndpointId endpointId) } } -void ESPMatterDescriptorClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterDescriptorClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[endpointId].Cluster()); diff --git a/components/esp_matter/data_model_provider/clusters/device_energy_management/integration.cpp b/components/esp_matter/data_model_provider/clusters/device_energy_management/integration.cpp new file mode 100644 index 000000000..a7f8c15c2 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/device_energy_management/integration.cpp @@ -0,0 +1,68 @@ +// Copyright 2026 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. + +#include +#include +#include + +#include "integration.h" + +namespace chip { +namespace app { +namespace Clusters { +namespace DeviceEnergyManagement { + +Instance::Instance(EndpointId aEndpointId, Delegate & aDelegate, BitMask aFeature) : + mCluster(DeviceEnergyManagementCluster::Config(aEndpointId, aFeature, aDelegate)) +{} + +CHIP_ERROR Instance::Init() +{ + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Register(mCluster.Registration()); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Failed to register cluster %u/" ChipLogFormatMEI ": %" CHIP_ERROR_FORMAT, + mCluster.Cluster().GetPaths()[0].mEndpointId, ChipLogValueMEI(DeviceEnergyManagement::Id), err.Format()); + } + return err; +} + +void Instance::Shutdown() +{ + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&mCluster.Cluster()); + + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Failed to unregister cluster %u/" ChipLogFormatMEI ": %" CHIP_ERROR_FORMAT, + mCluster.Cluster().GetPaths()[0].mEndpointId, ChipLogValueMEI(DeviceEnergyManagement::Id), err.Format()); + } +} + +bool Instance::HasFeature(Feature aFeature) const +{ + return mCluster.Cluster().Features().Has(aFeature); +} + +} // namespace DeviceEnergyManagement +} // namespace Clusters +} // namespace app +} // namespace chip + +void ESPMatterDeviceEnergyManagementClusterServerInitCallback(chip::EndpointId endpoint) {} +void ESPMatterDeviceEnergyManagementClusterServerShutdownCallback(chip::EndpointId endpoint, chip::app::ClusterShutdownType shutdownType) {} + +// Legacy callback stubs +void MatterDeviceEnergyManagementPluginServerInitCallback() {} +void MatterDeviceEnergyManagementPluginServerShutdownCallback() {} diff --git a/components/esp_matter/data_model_provider/clusters/device_energy_management/integration.h b/components/esp_matter/data_model_provider/clusters/device_energy_management/integration.h new file mode 100644 index 000000000..ee9304c5c --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/device_energy_management/integration.h @@ -0,0 +1,36 @@ +// Copyright 2026 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. + +#pragma once + +#include +#include + +namespace chip::app::Clusters::DeviceEnergyManagement { + +class Instance +{ +public: + Instance(EndpointId aEndpointId, Delegate & aDelegate, BitMask aFeature); + + CHIP_ERROR Init(); + void Shutdown(); + + bool HasFeature(Feature aFeature) const; + +private: + RegisteredServerCluster mCluster; +}; + +} diff --git a/components/esp_matter/data_model_provider/clusters/diagnostic_logs_integration.cpp b/components/esp_matter/data_model_provider/clusters/diagnostic_logs/integration.cpp similarity index 85% rename from components/esp_matter/data_model_provider/clusters/diagnostic_logs_integration.cpp rename to components/esp_matter/data_model_provider/clusters/diagnostic_logs/integration.cpp index 3d33fe56f..51194946d 100644 --- a/components/esp_matter/data_model_provider/clusters/diagnostic_logs_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/diagnostic_logs/integration.cpp @@ -16,6 +16,7 @@ #include #include #include +#include "integration.h" using namespace chip; using namespace chip::app; @@ -35,12 +36,12 @@ void ESPMatterDiagnosticLogsClusterServerInitCallback(EndpointId endpoint) } } -void ESPMatterDiagnosticLogsClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterDiagnosticLogsClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { // We implement the cluster as a singleton on the root endpoint. VerifyOrReturn(endpointId == kRootEndpointId && gServer.IsConstructed()); CHIP_ERROR err = - esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster()); + esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster(), shutdownType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to unregister DiagnosticLogs on endpoint %u - Error: %" CHIP_ERROR_FORMAT, endpointId, err.Format()); @@ -50,3 +51,12 @@ void ESPMatterDiagnosticLogsClusterServerShutdownCallback(EndpointId endpointId) void MatterDiagnosticLogsPluginServerInitCallback() {} void MatterDiagnosticLogsPluginServerShutdownCallback() {} + +namespace chip::app::Clusters::DiagnosticLogs { + +void SetDiagnosticLogsProviderDelegate(DiagnosticLogsProviderDelegate * delegate) +{ + gServer.Cluster().SetDelegate(delegate); +} + +} // namespace chip::app::Clusters::DiagnosticLogs diff --git a/components/esp_matter/data_model_provider/clusters/diagnostic_logs/integration.h b/components/esp_matter/data_model_provider/clusters/diagnostic_logs/integration.h new file mode 100644 index 000000000..3408b198d --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/diagnostic_logs/integration.h @@ -0,0 +1,23 @@ +// Copyright 2026 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. + +#pragma once + +#include + +namespace chip::app::Clusters::DiagnosticLogs { + +void SetDiagnosticLogsProviderDelegate(DiagnosticLogsProviderDelegate * delegate); + +} // namespace chip::app::Clusters::DiagnosticLogs diff --git a/components/esp_matter/data_model_provider/clusters/electrical_energy_measurement/integration.cpp b/components/esp_matter/data_model_provider/clusters/electrical_energy_measurement/integration.cpp new file mode 100644 index 000000000..9e5512749 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/electrical_energy_measurement/integration.cpp @@ -0,0 +1,144 @@ +// Copyright 2026 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. + +#include "integration.h" +#include "esp_matter_data_model.h" +#include "esp_matter_data_model_priv.h" +#include "esp_matter_data_model_provider.h" +#include +#include "app/clusters/electrical-energy-measurement-server/ElectricalEnergyMeasurementCluster.h" +#include "app/server-cluster/ServerClusterInterfaceRegistry.h" +#include "clusters/ElectricalEnergyMeasurement/Enums.h" + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::ElectricalEnergyMeasurement; + +namespace { +std::unordered_map> gServers; + +uint32_t get_feature_map(esp_matter::cluster_t *cluster) +{ + esp_matter::attribute_t *attribute = esp_matter::attribute::get(cluster, Globals::Attributes::FeatureMap::Id); + if (attribute) { + esp_matter_attr_val_t val = esp_matter_invalid(nullptr); + if (esp_matter::attribute::get_val_internal(attribute, &val) == ESP_OK && + val.type == ESP_MATTER_VAL_TYPE_BITMAP32) { + return val.val.u32; + } + } + return 0; +} +ElectricalEnergyMeasurementCluster::Config GetClusterConfig(EndpointId endpointId) +{ + ElectricalEnergyMeasurementCluster::Config config; + config.endpointId = endpointId; + esp_matter::cluster_t *cluster = esp_matter::cluster::get(endpointId, ElectricalEnergyMeasurement::Id); + config.featureFlags = BitMask(get_feature_map(cluster)); + if (esp_matter::attribute::get(cluster, Attributes::CumulativeEnergyReset::Id)) { + config.optionalAttributes.SetField(OptionalAttributes::kOptionalAttributeCumulativeEnergyReset, 1); + } else { + config.optionalAttributes.Clear(OptionalAttributes::kOptionalAttributeCumulativeEnergyReset); + } + return config; +} +} // namespace + +namespace chip::app::Clusters::ElectricalEnergyMeasurement { + +ElectricalEnergyMeasurementCluster *GetClusterInstance(EndpointId endpointId) +{ + if (gServers[endpointId].IsConstructed()) { + return &gServers[endpointId].Cluster(); + } + return nullptr; +} + +const ElectricalEnergyMeasurement::MeasurementData *MeasurementDataForEndpoint(EndpointId endpointId) +{ + ElectricalEnergyMeasurementCluster *cluster = GetClusterInstance(endpointId); + VerifyOrReturnValue(cluster != nullptr, nullptr); + + return cluster->GetMeasurementData(); +} + +CHIP_ERROR SetMeasurementAccuracy(EndpointId endpointId, const Structs::MeasurementAccuracyStruct::Type &accuracy) +{ + ElectricalEnergyMeasurementCluster *cluster = GetClusterInstance(endpointId); + VerifyOrReturnError(cluster != nullptr, CHIP_ERROR_NOT_FOUND); + return cluster->SetMeasurementAccuracy(accuracy); +} + +CHIP_ERROR SetCumulativeReset(EndpointId endpointId, + const Optional &cumulativeReset) +{ + ElectricalEnergyMeasurementCluster *cluster = GetClusterInstance(endpointId); + VerifyOrReturnError(cluster != nullptr, CHIP_ERROR_NOT_FOUND); + return cluster->SetCumulativeEnergyReset(cumulativeReset); +} + +bool NotifyCumulativeEnergyMeasured(EndpointId endpointId, + const Optional &energyImported, + const Optional &energyExported) +{ + ElectricalEnergyMeasurementCluster *cluster = GetClusterInstance(endpointId); + + VerifyOrReturnValue(cluster != nullptr, false); + VerifyOrReturnValue(cluster->Features().Has(Feature::kCumulativeEnergy), false); + + cluster->CumulativeEnergySnapshot(energyImported, energyExported); + return true; +} + +bool NotifyPeriodicEnergyMeasured(EndpointId endpointId, + const Optional &energyImported, + const Optional &energyExported) +{ + ElectricalEnergyMeasurementCluster *cluster = GetClusterInstance(endpointId); + + VerifyOrReturnValue(cluster != nullptr, false); + VerifyOrReturnValue(cluster->Features().Has(Feature::kPeriodicEnergy), false); + + cluster->PeriodicEnergySnapshot(energyImported, energyExported); + return true; +} + +} // namespace chip::app::Clusters::ElectricalEnergyMeasurement + +void ESPMatterElectricalEnergyMeasurementClusterServerInitCallback(EndpointId endpoint) +{ + if (gServers[endpoint].IsConstructed()) { + return; + } + ElectricalEnergyMeasurementCluster::Config config = GetClusterConfig(endpoint); + gServers[endpoint].Create(config); + CHIP_ERROR err = + esp_matter::data_model::provider::get_instance().registry().Register(gServers[endpoint].Registration()); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to register AccessControl - Error %" CHIP_ERROR_FORMAT, err.Format()); + } +} + +void ESPMatterElectricalEnergyMeasurementClusterServerShutdownCallback(EndpointId endpointId, + ClusterShutdownType shutdownType) +{ + VerifyOrReturn(gServers[endpointId].IsConstructed()); + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister( + &gServers[endpointId].Cluster(), shutdownType); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to unregister AccessControl - Error %" CHIP_ERROR_FORMAT, err.Format()); + } + gServers[endpointId].Destroy(); +} diff --git a/components/esp_matter/data_model_provider/clusters/electrical_energy_measurement/integration.h b/components/esp_matter/data_model_provider/clusters/electrical_energy_measurement/integration.h new file mode 100644 index 000000000..8e3ea7653 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/electrical_energy_measurement/integration.h @@ -0,0 +1,34 @@ +// Copyright 2026 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. + +#pragma once + +#include + +namespace chip::app::Clusters::ElectricalEnergyMeasurement { + +bool NotifyCumulativeEnergyMeasured(EndpointId endpointId, const Optional & energyImported, + const Optional & energyExported); + +bool NotifyPeriodicEnergyMeasured(EndpointId endpointId, const Optional & energyImported, + const Optional & energyExported); + +CHIP_ERROR SetMeasurementAccuracy(EndpointId endpointId, const Structs::MeasurementAccuracyStruct::Type & accuracy); + +CHIP_ERROR SetCumulativeReset(EndpointId endpointId, const Optional & cumulativeReset); + +const ElectricalEnergyMeasurement::MeasurementData * MeasurementDataForEndpoint(EndpointId endpointId); + +ElectricalEnergyMeasurementCluster *GetClusterInstance(EndpointId endpointId); +} diff --git a/components/esp_matter/data_model_provider/clusters/electrical_power_measurement/integration.cpp b/components/esp_matter/data_model_provider/clusters/electrical_power_measurement/integration.cpp new file mode 100644 index 000000000..9b3088631 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/electrical_power_measurement/integration.cpp @@ -0,0 +1,72 @@ +// Copyright 2026 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. + +#include "integration.h" +#include +#include + +namespace chip { +namespace app { +namespace Clusters { +namespace ElectricalPowerMeasurement { + +ElectricalPowerMeasurementCluster::OptionalAttributesSet Instance::FromLegacyOptionalAttributes( + BitMask aOptionalAttributes) +{ + ElectricalPowerMeasurementCluster::OptionalAttributesSet attrs; + attrs.Set(aOptionalAttributes.Has(OptionalAttributes::kOptionalAttributeRanges)); + attrs.Set(aOptionalAttributes.Has(OptionalAttributes::kOptionalAttributeVoltage)); + attrs.Set( + aOptionalAttributes.Has(OptionalAttributes::kOptionalAttributeActiveCurrent)); + attrs.Set( + aOptionalAttributes.Has(OptionalAttributes::kOptionalAttributeReactiveCurrent)); + attrs.Set( + aOptionalAttributes.Has(OptionalAttributes::kOptionalAttributeApparentCurrent)); + attrs.Set( + aOptionalAttributes.Has(OptionalAttributes::kOptionalAttributeReactivePower)); + attrs.Set( + aOptionalAttributes.Has(OptionalAttributes::kOptionalAttributeApparentPower)); + attrs.Set(aOptionalAttributes.Has(OptionalAttributes::kOptionalAttributeRMSVoltage)); + attrs.Set(aOptionalAttributes.Has(OptionalAttributes::kOptionalAttributeRMSCurrent)); + attrs.Set(aOptionalAttributes.Has(OptionalAttributes::kOptionalAttributeRMSPower)); + attrs.Set(aOptionalAttributes.Has(OptionalAttributes::kOptionalAttributeFrequency)); + attrs.Set(aOptionalAttributes.Has(OptionalAttributes::kOptionalAttributePowerFactor)); + attrs.Set( + aOptionalAttributes.Has(OptionalAttributes::kOptionalAttributeNeutralCurrent)); + + return attrs; +} + +CHIP_ERROR Instance::Init() +{ + return esp_matter::data_model::provider::get_instance().registry().Register(mCluster.Registration()); +} +void Instance::Shutdown() +{ + RETURN_SAFELY_IGNORED esp_matter::data_model::provider::get_instance().registry().Unregister(&(mCluster.Cluster())); +} + +} // namespace ElectricalPowerMeasurement +} // namespace Clusters +} // namespace app +} // namespace chip + +void ESPMatterElectricalPowerMeasurementClusterServerInitCallback(chip::EndpointId endpointId) +{ +} + +void ESPMatterElectricalPowerMeasurementClusterServerShutdownCallback(chip::EndpointId endpointId, + chip::app::ClusterShutdownType shutdownType) +{ +} diff --git a/components/esp_matter/data_model_provider/clusters/electrical_power_measurement/integration.h b/components/esp_matter/data_model_provider/clusters/electrical_power_measurement/integration.h new file mode 100644 index 000000000..a64a425e4 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/electrical_power_measurement/integration.h @@ -0,0 +1,65 @@ +// Copyright 2026 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. + +#pragma once + +#include +#include +#include + +namespace chip::app::Clusters::ElectricalPowerMeasurement { + +enum class OptionalAttributes : uint32_t +{ + kOptionalAttributeRanges = 0x1, + kOptionalAttributeVoltage = 0x2, + kOptionalAttributeActiveCurrent = 0x4, + kOptionalAttributeReactiveCurrent = 0x8, + kOptionalAttributeApparentCurrent = 0x10, + kOptionalAttributeReactivePower = 0x20, + kOptionalAttributeApparentPower = 0x40, + kOptionalAttributeRMSVoltage = 0x80, + kOptionalAttributeRMSCurrent = 0x100, + kOptionalAttributeRMSPower = 0x200, + kOptionalAttributeFrequency = 0x400, + kOptionalAttributePowerFactor = 0x800, + kOptionalAttributeNeutralCurrent = 0x1000, +}; + +class Instance +{ +public: + Instance(EndpointId aEndpointId, Delegate & aDelegate, BitMask aFeature, + BitMask aOptionalAttributes) : + mCluster(ElectricalPowerMeasurementCluster::Config{ + .endpointId = aEndpointId, + .delegate = aDelegate, + .features = aFeature, + .optionalAttributes = FromLegacyOptionalAttributes(aOptionalAttributes), + }) + {} + + CHIP_ERROR Init(); + void Shutdown(); + + ~Instance() {} + +private: + static ElectricalPowerMeasurementCluster::OptionalAttributesSet + FromLegacyOptionalAttributes(BitMask aOptionalAttributes); + + RegisteredServerCluster mCluster; +}; + +} diff --git a/components/esp_matter/data_model_provider/clusters/ethernet_network_diagnostics_integration.cpp b/components/esp_matter/data_model_provider/clusters/ethernet_network_diagnostics/integration.cpp similarity index 84% rename from components/esp_matter/data_model_provider/clusters/ethernet_network_diagnostics_integration.cpp rename to components/esp_matter/data_model_provider/clusters/ethernet_network_diagnostics/integration.cpp index 3ac49141d..c89cf43b3 100644 --- a/components/esp_matter/data_model_provider/clusters/ethernet_network_diagnostics_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/ethernet_network_diagnostics/integration.cpp @@ -13,9 +13,10 @@ // limitations under the License. #include -#include +#include #include #include +#include #include using namespace chip; @@ -30,7 +31,8 @@ uint32_t get_feature_map(esp_matter::cluster_t *cluster) esp_matter::attribute_t *attribute = esp_matter::attribute::get(cluster, Globals::Attributes::FeatureMap::Id); if (attribute) { esp_matter_attr_val_t val = esp_matter_invalid(nullptr); - if (esp_matter::attribute::get_val(attribute, &val) == ESP_OK && val.type == ESP_MATTER_VAL_TYPE_BITMAP32) { + if (esp_matter::attribute::get_val_internal(attribute, &val) == ESP_OK && + val.type == ESP_MATTER_VAL_TYPE_BITMAP32) { return val.val.u32; } } @@ -76,10 +78,11 @@ void ESPMatterEthernetNetworkDiagnosticsClusterServerInitCallback(EndpointId end } } -void ESPMatterEthernetNetworkDiagnosticsClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterEthernetNetworkDiagnosticsClusterServerShutdownCallback(EndpointId endpointId, + ClusterShutdownType shutdownType) { - CHIP_ERROR err = - esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[endpointId].Cluster()); + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister( + &gServers[endpointId].Cluster(), shutdownType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to unregister EthernetNetworkDiagnostics on endpoint %u - Error: %" CHIP_ERROR_FORMAT, @@ -88,6 +91,10 @@ void ESPMatterEthernetNetworkDiagnosticsClusterServerShutdownCallback(EndpointId gServers[endpointId].Destroy(); } -void MatterEthernetNetworkDiagnosticsPluginServerInitCallback() {} +void MatterEthernetNetworkDiagnosticsPluginServerInitCallback() +{ +} -void MatterEthernetNetworkDiagnosticsPluginServerShutdownCallback() {} +void MatterEthernetNetworkDiagnosticsPluginServerShutdownCallback() +{ +} diff --git a/components/esp_matter/data_model_provider/clusters/fixed_label_integration.cpp b/components/esp_matter/data_model_provider/clusters/fixed_label/integration.cpp similarity index 91% rename from components/esp_matter/data_model_provider/clusters/fixed_label_integration.cpp rename to components/esp_matter/data_model_provider/clusters/fixed_label/integration.cpp index 31a17b946..907e7b214 100644 --- a/components/esp_matter/data_model_provider/clusters/fixed_label_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/fixed_label/integration.cpp @@ -13,7 +13,7 @@ // limitations under the License. #include -#include +#include #include #include @@ -41,10 +41,10 @@ void ESPMatterFixedLabelClusterServerInitCallback(EndpointId endpointId) } } -void ESPMatterFixedLabelClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterFixedLabelClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { CHIP_ERROR err = - esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[endpointId].Cluster()); + esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[endpointId].Cluster(), shutdownType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to unregister FixedLabel on endpoint %u - Error: %" CHIP_ERROR_FORMAT, endpointId, err.Format()); diff --git a/components/esp_matter/data_model_provider/clusters/general_commissioning/integration.cpp b/components/esp_matter/data_model_provider/clusters/general_commissioning/integration.cpp new file mode 100644 index 000000000..e4b538b97 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/general_commissioning/integration.cpp @@ -0,0 +1,110 @@ +// 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. + +#include +#include +#include +#include +#include +#include +#include +#include "integration.h" + +#if CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED +#include +#endif + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; + +namespace { +LazyRegisteredServerCluster gServer; +} + +namespace chip::app::Clusters::GeneralCommissioning { + +GeneralCommissioningCluster * Instance() +{ + GeneralCommissioningCluster::OptionalAttributes optionalAttrs; + if (esp_matter::endpoint::is_attribute_enabled(kRootEndpointId, GeneralCommissioning::Id, GeneralCommissioning::Attributes::IsCommissioningWithoutPower::Id)) { + optionalAttrs.Set(); + } + + if (!gServer.IsConstructed()) { + gServer.Create( + GeneralCommissioningCluster::Context { + .commissioningWindowManager = Server::GetInstance().GetCommissioningWindowManager(), // + .configurationManager = DeviceLayer::ConfigurationMgr(), // + .deviceControlServer = DeviceLayer::DeviceControlServer::DeviceControlSvr(), // + .fabricTable = Server::GetInstance().GetFabricTable(), // + .failsafeContext = Server::GetInstance().GetFailSafeContext(), // + .platformManager = DeviceLayer::PlatformMgr(), // +#if CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED + .termsAndConditionsProvider = TermsAndConditionsManager::GetInstance(), +#endif // CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED + }, + optionalAttrs); + } + + return &gServer.Cluster(); +} + +} // namespace chip::app::Clusters::GeneralCommissioning + +void ESPMatterGeneralCommissioningClusterServerInitCallback(EndpointId endpointId) +{ + if (endpointId != kRootEndpointId) { + return; + } + GeneralCommissioningCluster::OptionalAttributes optionalAttrs; + if (esp_matter::endpoint::is_attribute_enabled(endpointId, GeneralCommissioning::Id, GeneralCommissioning::Attributes::IsCommissioningWithoutPower::Id)) { + optionalAttrs.Set(); + } + + if (!gServer.IsConstructed()) { + gServer.Create( + GeneralCommissioningCluster::Context { + .commissioningWindowManager = Server::GetInstance().GetCommissioningWindowManager(), // + .configurationManager = DeviceLayer::ConfigurationMgr(), // + .deviceControlServer = DeviceLayer::DeviceControlServer::DeviceControlSvr(), // + .fabricTable = Server::GetInstance().GetFabricTable(), // + .failsafeContext = Server::GetInstance().GetFailSafeContext(), // + .platformManager = DeviceLayer::PlatformMgr(), // +#if CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED + .termsAndConditionsProvider = TermsAndConditionsManager::GetInstance(), +#endif // CHIP_CONFIG_TERMS_AND_CONDITIONS_REQUIRED + }, + optionalAttrs); + } + + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Register(gServer.Registration()); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to register GeneralCommissioning - Error: %" CHIP_ERROR_FORMAT, err.Format()); + } +} + +void ESPMatterGeneralCommissioningClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) +{ + VerifyOrReturn(endpointId == kRootEndpointId && gServer.IsConstructed()); + CHIP_ERROR err = + esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster(), shutdownType); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to unregister GeneralCommissioning - Error: %" CHIP_ERROR_FORMAT, err.Format()); + } + gServer.Destroy(); +} + +void MatterGeneralCommissioningPluginServerInitCallback() {} +void MatterGeneralCommissioningPluginServerShutdownCallback() {} diff --git a/components/esp_matter/data_model_provider/clusters/general_commissioning_integration.h b/components/esp_matter/data_model_provider/clusters/general_commissioning/integration.h similarity index 90% rename from components/esp_matter/data_model_provider/clusters/general_commissioning_integration.h rename to components/esp_matter/data_model_provider/clusters/general_commissioning/integration.h index 4748c346a..e57712923 100644 --- a/components/esp_matter/data_model_provider/clusters/general_commissioning_integration.h +++ b/components/esp_matter/data_model_provider/clusters/general_commissioning/integration.h @@ -14,7 +14,7 @@ #pragma once -#include +#include namespace chip::app::Clusters::GeneralCommissioning { diff --git a/components/esp_matter/data_model_provider/clusters/general_commissioning_integration.cpp b/components/esp_matter/data_model_provider/clusters/general_commissioning_integration.cpp deleted file mode 100644 index 17183f723..000000000 --- a/components/esp_matter/data_model_provider/clusters/general_commissioning_integration.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// 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. - -#include -#include -#include -#include -#include "app/server-cluster/ServerClusterInterfaceRegistry.h" -#include "core/DataModelTypes.h" -#include "support/CodeUtils.h" - -using namespace chip; -using namespace chip::app; -using namespace chip::app::Clusters; - -namespace { -LazyRegisteredServerCluster gServer; -} - -namespace chip::app::Clusters::GeneralCommissioning { - -GeneralCommissioningCluster * Instance() -{ - if (!gServer.IsConstructed()) { - gServer.Create(); - } - return &gServer.Cluster(); -} - -} // namespace chip::app::Clusters::GeneralCommissioning - -void ESPMatterGeneralCommissioningClusterServerInitCallback(EndpointId endpointId) -{ - if (endpointId != kRootEndpointId) { - return; - } - if (!gServer.IsConstructed()) { - gServer.Create(); - } - if (esp_matter::endpoint::is_attribute_enabled(endpointId, GeneralCommissioning::Id, GeneralCommissioning::Attributes::IsCommissioningWithoutPower::Id)) { - gServer.Cluster().GetOptionalAttributes().Set(); - } - - CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Register(gServer.Registration()); - if (err != CHIP_NO_ERROR) { - ChipLogError(AppServer, "Failed to register GeneralCommissioning - Error: %" CHIP_ERROR_FORMAT, err.Format()); - } -} - -void ESPMatterGeneralCommissioningClusterServerShutdownCallback(EndpointId endpointId) -{ - VerifyOrReturn(endpointId == kRootEndpointId && gServer.IsConstructed()); - CHIP_ERROR err = - esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster()); - if (err != CHIP_NO_ERROR) { - ChipLogError(AppServer, "Failed to unregister GeneralCommissioning - Error: %" CHIP_ERROR_FORMAT, err.Format()); - } - gServer.Destroy(); -} - -void MatterGeneralCommissioningPluginServerInitCallback() {} -void MatterGeneralCommissioningPluginServerShutdownCallback() {} diff --git a/components/esp_matter/data_model_provider/clusters/general_diagnostics_integration.cpp b/components/esp_matter/data_model_provider/clusters/general_diagnostics/integration.cpp similarity index 88% rename from components/esp_matter/data_model_provider/clusters/general_diagnostics_integration.cpp rename to components/esp_matter/data_model_provider/clusters/general_diagnostics/integration.cpp index 079a594d7..042c230b1 100644 --- a/components/esp_matter/data_model_provider/clusters/general_diagnostics_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/general_diagnostics/integration.cpp @@ -12,12 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include -#include -#include -#include +#include +#include #include #include +#include "integration.h" + +#include +#include +#include +#include +#include +#include +#include +#include using namespace chip; using namespace chip::app; @@ -88,6 +96,10 @@ void ESPMatterGeneralDiagnosticsClusterServerInitCallback(EndpointId endpointId) if (IsAttributeEnabled(endpointId, GeneralDiagnostics::Attributes::ActiveNetworkFaults::Id)) { attrSet.Set(); } + attribute_t *feature = attribute::get(endpointId, GeneralDiagnostics::Id, Globals::Attributes::FeatureMap::Id); + esp_matter_attr_val_t feature_val = esp_matter_invalid(nullptr); + VerifyOrReturn(attribute::get_val_internal(feature, &feature_val) == ESP_OK && feature_val.type == ESP_MATTER_VAL_TYPE_BITMAP32); + BitFlags featureFlags(feature_val.val.u32); CHIP_ERROR err = CHIP_NO_ERROR; if (IsCommandEnabled(endpointId, GeneralDiagnostics::Commands::PayloadTestRequest::Id, COMMAND_FLAG_ACCEPTED) || @@ -102,11 +114,11 @@ void ESPMatterGeneralDiagnosticsClusterServerInitCallback(EndpointId endpointId) IsCommandEnabled(endpointId, GeneralDiagnostics::Commands::PayloadTestRequest::Id, COMMAND_FLAG_ACCEPTED), }; - gServer.fullConfigurableServer.Create(attrSet, functionsConfig); + gServer.fullConfigurableServer.Create(attrSet,featureFlags, InteractionModelEngine::GetInstance(), functionsConfig); err = esp_matter::data_model::provider::get_instance().registry().Register( gServer.fullConfigurableServer.Registration()); } else { - gServer.server.Create(attrSet); + gServer.server.Create(attrSet, featureFlags, InteractionModelEngine::GetInstance()); err = esp_matter::data_model::provider::get_instance().registry().Register(gServer.server.Registration()); } if (err != CHIP_NO_ERROR) { @@ -114,7 +126,7 @@ void ESPMatterGeneralDiagnosticsClusterServerInitCallback(EndpointId endpointId) } } -void ESPMatterGeneralDiagnosticsClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterGeneralDiagnosticsClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { VerifyOrReturn(endpointId == kRootEndpointId); CHIP_ERROR err = CHIP_NO_ERROR; diff --git a/components/esp_matter/data_model_provider/clusters/general_diagnostics/integration.h b/components/esp_matter/data_model_provider/clusters/general_diagnostics/integration.h new file mode 100644 index 000000000..a0432cf23 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/general_diagnostics/integration.h @@ -0,0 +1,30 @@ +// Copyright 2026 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. + +#pragma once + +#include + +namespace chip::app::Clusters::GeneralDiagnostics { +void GlobalNotifyDeviceReboot(GeneralDiagnostics::BootReasonEnum bootReason); + +void GlobalNotifyHardwareFaultsDetect(const DeviceLayer::GeneralFaults & previous, + const DeviceLayer::GeneralFaults & current); + +void GlobalNotifyRadioFaultsDetect(const DeviceLayer::GeneralFaults & previous, + const DeviceLayer::GeneralFaults & current); + +void GlobalNotifyNetworkFaultsDetect(const DeviceLayer::GeneralFaults & previous, + const DeviceLayer::GeneralFaults & current); +} // namespace chip::app::Clusters::GeneralDiagnostics diff --git a/components/esp_matter/data_model_provider/clusters/group_key_management_integration.cpp b/components/esp_matter/data_model_provider/clusters/group_key_management/integration.cpp similarity index 90% rename from components/esp_matter/data_model_provider/clusters/group_key_management_integration.cpp rename to components/esp_matter/data_model_provider/clusters/group_key_management/integration.cpp index c309f6730..15a3968e3 100644 --- a/components/esp_matter/data_model_provider/clusters/group_key_management_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/group_key_management/integration.cpp @@ -13,7 +13,7 @@ // limitations under the License. #include -#include +#include #include using namespace chip; @@ -36,11 +36,11 @@ void ESPMatterGroupKeyManagementClusterServerInitCallback(EndpointId endpoint) } } -void ESPMatterGroupKeyManagementClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterGroupKeyManagementClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { // We implement the cluster as a singleton on the root endpoint. VerifyOrReturn(endpointId == kRootEndpointId); - CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster()); + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster(), shutdownType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to unregister GroupKeyManagement - Error: %" CHIP_ERROR_FORMAT, err.Format()); } diff --git a/components/esp_matter/data_model_provider/clusters/groupcast_integration.cpp b/components/esp_matter/data_model_provider/clusters/groupcast/integration.cpp similarity index 95% rename from components/esp_matter/data_model_provider/clusters/groupcast_integration.cpp rename to components/esp_matter/data_model_provider/clusters/groupcast/integration.cpp index daad00af1..445ac37a3 100644 --- a/components/esp_matter/data_model_provider/clusters/groupcast_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/groupcast/integration.cpp @@ -43,11 +43,11 @@ void ESPMatterGroupcastClusterServerInitCallback(chip::EndpointId endpointId) } } -void ESPMatterGroupcastClusterServerShutdownCallback(chip::EndpointId endpointId) +void ESPMatterGroupcastClusterServerShutdownCallback(chip::EndpointId endpointId, ClusterShutdownType shutdownType) { VerifyOrDie(endpointId == chip::kRootEndpointId); - CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster()); + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster(), shutdownType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to unregister Groupcast - Error %" CHIP_ERROR_FORMAT, err.Format()); } diff --git a/components/esp_matter/data_model_provider/clusters/icd_management/integration.cpp b/components/esp_matter/data_model_provider/clusters/icd_management/integration.cpp new file mode 100644 index 000000000..37e8e609b --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/icd_management/integration.cpp @@ -0,0 +1,111 @@ +// Copyright 2026 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. + +#include "esp_matter_attribute_utils.h" +#include "esp_matter_data_model.h" +#include "esp_matter_data_model_priv.h" +#include "esp_matter_data_model_provider.h" +#include +#include +#include "app/server-cluster/ServerClusterInterfaceRegistry.h" +#include "app/server/Server.h" +#include "clusters/IcdManagement/Enums.h" +#include "core/DataModelTypes.h" +#include "lib/support/BitMask.h" +#include "lib/support/Span.h" +#include "support/CodeUtils.h" + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::IcdManagement; + +namespace { +#if CHIP_CONFIG_ENABLE_ICD_CIP +LazyRegisteredServerCluster gServer; +#else +LazyRegisteredServerCluster gServer; +#endif + +esp_err_t get_attr_val(esp_matter::cluster_t *cluster, uint32_t attribute_id, esp_matter_attr_val_t &val) +{ + esp_matter::attribute_t *attr = esp_matter::attribute::get(cluster, attribute_id); + if (!attr) { + return ESP_FAIL; + } + return esp_matter::attribute::get_val_internal(attr, &val); +} + +CHIP_ERROR GetClusterConfig(EndpointId endpointId, ICDManagementCluster::OptionalAttributeSet &optionalAttrSet, + BitMask &optionalCommands, BitMask &uatHint, + MutableCharSpan &instructionSpan) +{ + esp_matter::cluster_t *cluster = esp_matter::cluster::get(endpointId, IcdManagement::Id); + if (!cluster) { + return CHIP_ERROR_NOT_FOUND; + } + if (esp_matter::command::get(cluster, Commands::StayActiveRequest::Id, esp_matter::COMMAND_FLAG_ACCEPTED)) { + optionalCommands.SetField(OptionalCommands::kStayActive, 1); + } else { + optionalCommands.SetField(OptionalCommands::kStayActive, 0); + } + esp_matter_attr_val_t attr_val; + if (get_attr_val(cluster, Attributes::UserActiveModeTriggerHint::Id, attr_val) == ESP_OK && + attr_val.type == ESP_MATTER_VAL_TYPE_BITMAP32) { + uatHint = BitMask(attr_val.val.u32); + if (get_attr_val(cluster, Attributes::UserActiveModeTriggerInstruction::Id, attr_val) == ESP_OK && + attr_val.type == ESP_MATTER_VAL_TYPE_CHAR_STRING && instructionSpan.size() >= attr_val.val.a.s) { + memcpy(instructionSpan.data(), (const char *)attr_val.val.a.b, attr_val.val.a.s); + instructionSpan.reduce_size(attr_val.val.a.s); + optionalAttrSet.Set(); + } + } + return CHIP_NO_ERROR; +} + +} // namespace + +void ESPMatterIcdManagementClusterServerInitCallback(EndpointId endpointId) +{ + VerifyOrReturn(endpointId == kRootEndpointId); + VerifyOrReturn(!gServer.IsConstructed()); + ICDManagementCluster::OptionalAttributeSet optionalAttrSet; + BitMask optionalCommands; + BitMask uatHint; + char instructionBuffer[kUserActiveModeTriggerInstructionMaxLength]; + MutableCharSpan instructionSpan(instructionBuffer); + CHIP_ERROR err = GetClusterConfig(endpointId, optionalAttrSet, optionalCommands, uatHint, instructionSpan); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to get config for IcdManagement - Error %" CHIP_ERROR_FORMAT, err.Format()); + return; + } + gServer.Create(endpointId, *Server::GetInstance().GetSessionKeystore(), Server::GetInstance().GetFabricTable(), + ICDConfigurationData::GetInstance(), optionalAttrSet, optionalCommands, uatHint, instructionSpan); + err = esp_matter::data_model::provider::get_instance().registry().Register(gServer.Registration()); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to register IcdManagement - Error %" CHIP_ERROR_FORMAT, err.Format()); + } +} + +void ESPMatterIcdManagementClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) +{ + VerifyOrReturn(endpointId == kRootEndpointId); + VerifyOrReturn(gServer.IsConstructed()); + CHIP_ERROR err = + esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster(), shutdownType); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to unregister IcdManagement - Error %" CHIP_ERROR_FORMAT, err.Format()); + } + gServer.Destroy(); +} diff --git a/components/esp_matter/data_model_provider/clusters/identify_integration.cpp b/components/esp_matter/data_model_provider/clusters/identify/integration.cpp similarity index 95% rename from components/esp_matter/data_model_provider/clusters/identify_integration.cpp rename to components/esp_matter/data_model_provider/clusters/identify/integration.cpp index 200822318..c1936ac67 100644 --- a/components/esp_matter/data_model_provider/clusters/identify_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/identify/integration.cpp @@ -19,16 +19,17 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include +#include #include -#include "support/logging/TextOnlyLogging.h" +#include "integration.h" namespace { @@ -167,7 +168,7 @@ void ESPMatterIdentifyClusterServerInitCallback(EndpointId endpointId) // Intentionally make this function empty as the identify cluster will be registered when enabling endpoint. } -void ESPMatterIdentifyClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterIdentifyClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { // Intentionally make this function empty as the identify cluster will be unregistered when disabling endpoint. } @@ -175,3 +176,10 @@ void ESPMatterIdentifyClusterServerShutdownCallback(EndpointId endpointId) // Legacy PluginServer callback stubs void MatterIdentifyPluginServerInitCallback() {} void MatterIdentifyPluginServerShutdownCallback() {} + +namespace chip::app::Clusters::Identify { +IdentifyCluster * FindClusterOnEndpoint(EndpointId endpointId) +{ + return FindIdentifyClusterOnEndpoint(endpointId); +} +} diff --git a/components/esp_matter/data_model_provider/clusters/identify/integration.h b/components/esp_matter/data_model_provider/clusters/identify/integration.h new file mode 100644 index 000000000..fe2166e4c --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/identify/integration.h @@ -0,0 +1,21 @@ +// Copyright 2026 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. + +#pragma once + +#include + +namespace chip::app::Clusters::Identify { +IdentifyCluster * FindClusterOnEndpoint(EndpointId endpointId); +} diff --git a/components/esp_matter/data_model_provider/clusters/localization_configuration_integration.cpp b/components/esp_matter/data_model_provider/clusters/localization_configuration/integration.cpp similarity index 80% rename from components/esp_matter/data_model_provider/clusters/localization_configuration_integration.cpp rename to components/esp_matter/data_model_provider/clusters/localization_configuration/integration.cpp index d65fec579..61cdc5e3c 100644 --- a/components/esp_matter/data_model_provider/clusters/localization_configuration_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/localization_configuration/integration.cpp @@ -12,10 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include +#include #include #include #include +#include #include #include #include @@ -37,7 +38,7 @@ void ESPMatterLocalizationConfigurationClusterServerInitCallback(EndpointId endp esp_matter::attribute_t *active_locale = esp_matter::attribute::get( endpointId, LocalizationConfiguration::Id, LocalizationConfiguration::Attributes::ActiveLocale::Id); esp_matter_attr_val_t attr_val; - if (active_locale && esp_matter::attribute::get_val(active_locale, &attr_val) == ESP_OK && + if (active_locale && esp_matter::attribute::get_val_internal(active_locale, &attr_val) == ESP_OK && attr_val.type == ESP_MATTER_VAL_TYPE_CHAR_STRING && attr_val.val.a.s <= LocalizationConfiguration::Attributes::ActiveLocale::TypeInfo::MaxLength()) { ChipLogError(AppServer, "Failed to get active locale on endpoint %u", endpointId); @@ -46,19 +47,22 @@ void ESPMatterLocalizationConfigurationClusterServerInitCallback(EndpointId endp CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Register(gServer.Registration()); if (err != CHIP_NO_ERROR) { - ChipLogError(AppServer, "Failed to register LocalizationConfiguration - Error: %" CHIP_ERROR_FORMAT, err.Format()); + ChipLogError(AppServer, "Failed to register LocalizationConfiguration - Error: %" CHIP_ERROR_FORMAT, + err.Format()); } } -void ESPMatterLocalizationConfigurationClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterLocalizationConfigurationClusterServerShutdownCallback(EndpointId endpointId, + ClusterShutdownType shutdownType) { if (endpointId != kRootEndpointId) { return; } CHIP_ERROR err = - esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster()); + esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster(), shutdownType); if (err != CHIP_NO_ERROR) { - ChipLogError(AppServer, "Failed to unregister LocalizationConfiguration - Error: %" CHIP_ERROR_FORMAT, err.Format()); + ChipLogError(AppServer, "Failed to unregister LocalizationConfiguration - Error: %" CHIP_ERROR_FORMAT, + err.Format()); } } diff --git a/components/esp_matter/data_model_provider/clusters/network_commissioning_integration.cpp b/components/esp_matter/data_model_provider/clusters/network_commissioning/integration.cpp similarity index 81% rename from components/esp_matter/data_model_provider/clusters/network_commissioning_integration.cpp rename to components/esp_matter/data_model_provider/clusters/network_commissioning/integration.cpp index 65916e64d..d9477aee3 100644 --- a/components/esp_matter/data_model_provider/clusters/network_commissioning_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/network_commissioning/integration.cpp @@ -21,7 +21,7 @@ #include #include #include -#include "general_commissioning_integration.h" +#include using namespace chip; using namespace chip::app; @@ -94,45 +94,45 @@ void ESPMatterNetworkCommissioningClusterServerInitCallback(EndpointId endpointI if (endpointId == CONFIG_THREAD_NETWORK_ENDPOINT_ID) { static DeviceLayer::NetworkCommissioning::GenericThreadDriver sThreadDriver; gServers[index].Create(endpointId, &sThreadDriver, gBreadcrumbTracker); - gServers[index].Cluster().Init(); - (void)esp_matter::data_model::provider::get_instance().registry().Register(gServers[index].Registration()); + LogErrorOnFailure(gServers[index].Cluster().Init()); + LogErrorOnFailure(esp_matter::data_model::provider::get_instance().registry().Register(gServers[index].Registration())); } #endif #ifdef CONFIG_WIFI_NETWORK_COMMISSIONING_DRIVER if (endpointId == CONFIG_WIFI_NETWORK_ENDPOINT_ID) { gServers[index].Create(endpointId, &(DeviceLayer::NetworkCommissioning::ESPWiFiDriver::GetInstance()), gBreadcrumbTracker); - gServers[index].Cluster().Init(); - (void)esp_matter::data_model::provider::get_instance().registry().Register(gServers[index].Registration()); + LogErrorOnFailure(gServers[index].Cluster().Init()); + LogErrorOnFailure(esp_matter::data_model::provider::get_instance().registry().Register(gServers[index].Registration())); } #endif #ifdef CONFIG_ETHERNET_NETWORK_COMMISSIONING_DRIVER if (endpointId == CONFIG_ETHERNET_NETWORK_ENDPOINT_ID) { gServers[index].Create(endpointId, &(DeviceLayer::NetworkCommissioning::ESPEthernetDriver::GetInstance()), gBreadcrumbTracker); - gServers[index].Cluster().Init(); - (void)esp_matter::data_model::provider::get_instance().registry().Register(gServers[index].Registration()); + LogErrorOnFailure(gServers[index].Cluster().Init()); + LogErrorOnFailure(esp_matter::data_model::provider::get_instance().registry().Register(gServers[index].Registration())); } #endif } -void ESPMatterNetworkCommissioningClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterNetworkCommissioningClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { uint16_t index = GetServerIndex(endpointId); VerifyOrReturn(index != UINT16_MAX); #ifdef CONFIG_THREAD_NETWORK_COMMISSIONING_DRIVER if (endpointId == CONFIG_THREAD_NETWORK_ENDPOINT_ID) { - (void)esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[index].Cluster()); + LogErrorOnFailure(esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[index].Cluster(), shutdownType)); gServers[index].Cluster().Deinit(); } #endif #ifdef CONFIG_WIFI_NETWORK_COMMISSIONING_DRIVER if (endpointId == CONFIG_WIFI_NETWORK_ENDPOINT_ID) { - (void)esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[index].Cluster()); + LogErrorOnFailure(esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[index].Cluster(), shutdownType)); gServers[index].Cluster().Deinit(); } #endif #ifdef CONFIG_ETHERNET_NETWORK_COMMISSIONING_DRIVER if (endpointId == CONFIG_ETHERNET_NETWORK_ENDPOINT_ID) { - (void)esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[index].Cluster()); + LogErrorOnFailure(esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[index].Cluster(), shutdownType)); gServers[index].Cluster().Deinit(); } #endif diff --git a/components/esp_matter/data_model_provider/clusters/occupancy_sensing/integration.cpp b/components/esp_matter/data_model_provider/clusters/occupancy_sensing/integration.cpp new file mode 100644 index 000000000..fa98bf557 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/occupancy_sensing/integration.cpp @@ -0,0 +1,120 @@ +// Copyright 2026 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. + +#include "integration.h" +#include "esp_matter_attribute_utils.h" +#include "esp_matter_data_model_priv.h" +#include + +#include +#include +#include +#include +#include +#include + +#include +#include "support/logging/TextOnlyLogging.h" + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::OccupancySensing; +using namespace chip::app::Clusters::OccupancySensing::Attributes; + +namespace { + +std::unordered_map> gServers; + +DefaultTimerDelegate gDefaultTimerDelegate; + +OccupancySensing::Feature getFeature(EndpointId endpointId) +{ + esp_matter::attribute_t *attr = + esp_matter::attribute::get(endpointId, OccupancySensing::Id, Globals::Attributes::FeatureMap::Id); + esp_matter_attr_val_t val = esp_matter_invalid(nullptr); + if (attr && esp_matter::attribute::get_val_internal(attr, &val) == ESP_OK && + val.type == ESP_MATTER_VAL_TYPE_BITMAP32) { + return static_cast(val.val.u32); + } + return static_cast(0); +} + +} // namespace + +void ESPMatterOccupancySensingClusterServerInitCallback(EndpointId endpointId) +{ + if (gServers[endpointId].IsConstructed()) { + return; + } + OccupancySensingCluster::Config config(endpointId); + + config.WithFeatures(getFeature(endpointId)); + if (esp_matter::endpoint::is_attribute_enabled(endpointId, OccupancySensing::Id, + OccupancySensing::Attributes::HoldTime::Id)) { + // Initializes hold time with default limits and timer delegate. The Application can use SetHoldTimeLimits() and + // SetHoldTime() later to customize. Initial defaults come from typical values found in real devices on the + // market. + constexpr chip::app::Clusters::OccupancySensing::Structs::HoldTimeLimitsStruct::Type kDefaultHoldTimeLimits = { + .holdTimeMin = 1, .holdTimeMax = 300, .holdTimeDefault = 30}; + config.WithHoldTime(kDefaultHoldTimeLimits.holdTimeDefault, kDefaultHoldTimeLimits, gDefaultTimerDelegate); + + // Show deprecated attributes if enabled in Zap + config.WithDeprecatedAttributes( + esp_matter::endpoint::is_attribute_enabled(endpointId, OccupancySensing::Id, + Attributes::PIROccupiedToUnoccupiedDelay::Id) || + esp_matter::endpoint::is_attribute_enabled(endpointId, OccupancySensing::Id, + Attributes::UltrasonicOccupiedToUnoccupiedDelay::Id) || + esp_matter::endpoint::is_attribute_enabled(endpointId, OccupancySensing::Id, + Attributes::PhysicalContactOccupiedToUnoccupiedDelay::Id)); + } + gServers[endpointId].Create(config); + CHIP_ERROR err = + esp_matter::data_model::provider::get_instance().registry().Register(gServers[endpointId].Registration()); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to register OccupancySensing - Error %" CHIP_ERROR_FORMAT, err.Format()); + } +} + +void ESPMatterOccupancySensingClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) +{ + if (!gServers[endpointId].IsConstructed()) { + return; + } + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister( + &gServers[endpointId].Cluster(), shutdownType); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to unregister OccupancySensing - Error %" CHIP_ERROR_FORMAT, err.Format()); + } + gServers[endpointId].Destroy(); +} + +namespace chip::app::Clusters::OccupancySensing { + +OccupancySensingCluster *FindClusterOnEndpoint(EndpointId endpointId) +{ + if (gServers[endpointId].IsConstructed()) { + return &gServers[endpointId].Cluster(); + } + return nullptr; +} + +} // namespace chip::app::Clusters::OccupancySensing + +void MatterOccupancySensingPluginServerInitCallback() +{ +} +void MatterOccupancySensingPluginServerShutdownCallback() +{ +} diff --git a/components/esp_matter/data_model_provider/clusters/occupancy_sensing/integration.h b/components/esp_matter/data_model_provider/clusters/occupancy_sensing/integration.h new file mode 100644 index 000000000..35b0ca264 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/occupancy_sensing/integration.h @@ -0,0 +1,23 @@ +// Copyright 2026 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. + +#pragma once + +#include + +namespace chip::app::Clusters::OccupancySensing { + +OccupancySensingCluster * FindClusterOnEndpoint(EndpointId endpointId); + +} // namespace chip::app::Clusters::OccupancySensing diff --git a/components/esp_matter/data_model_provider/clusters/operational_credentials_integration.cpp b/components/esp_matter/data_model_provider/clusters/operational_credentials/integration.cpp similarity index 92% rename from components/esp_matter/data_model_provider/clusters/operational_credentials_integration.cpp rename to components/esp_matter/data_model_provider/clusters/operational_credentials/integration.cpp index a3600d913..4abdb7d8c 100644 --- a/components/esp_matter/data_model_provider/clusters/operational_credentials_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/operational_credentials/integration.cpp @@ -14,7 +14,7 @@ // limitations under the License. #include -#include +#include #include using namespace chip; @@ -42,11 +42,11 @@ void ESPMatterOperationalCredentialsClusterServerInitCallback(EndpointId endpoin } } -void ESPMatterOperationalCredentialsClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterOperationalCredentialsClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { // We implement the cluster as a singleton on the root endpoint. VerifyOrReturn(endpointId == kRootEndpointId); - CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster()); + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster(), shutdownType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to unregister OperationalCredentials - Error: %" CHIP_ERROR_FORMAT, err.Format()); diff --git a/components/esp_matter/data_model_provider/clusters/ota_provider_integration.cpp b/components/esp_matter/data_model_provider/clusters/ota_software_update_provider/integration.cpp similarity index 87% rename from components/esp_matter/data_model_provider/clusters/ota_provider_integration.cpp rename to components/esp_matter/data_model_provider/clusters/ota_software_update_provider/integration.cpp index efc16512d..65e9dc305 100644 --- a/components/esp_matter/data_model_provider/clusters/ota_provider_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/ota_software_update_provider/integration.cpp @@ -13,10 +13,12 @@ // limitations under the License. #include -#include +#include #include #include +#include "integration.h" + using namespace chip; using namespace chip::app; using namespace chip::app::Clusters; @@ -40,10 +42,10 @@ void ESPMatterOtaSoftwareUpdateProviderClusterServerInitCallback(EndpointId endp } } -void ESPMatterOtaSoftwareUpdateProviderClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterOtaSoftwareUpdateProviderClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { CHIP_ERROR err = - esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[endpointId].Cluster()); + esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[endpointId].Cluster(), shutdownType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to unregister OTA on endpoint %u - Error: %" CHIP_ERROR_FORMAT, endpointId, err.Format()); @@ -55,17 +57,11 @@ void MatterOtaSoftwareUpdateProviderPluginServerInitCallback() {} void MatterOtaSoftwareUpdateProviderPluginServerShutdownCallback() {} -namespace chip { -namespace app { -namespace Clusters { -namespace OTAProvider { +namespace chip::app::Clusters::OTAProvider { void SetDelegate(EndpointId endpointId, OTAProviderDelegate *delegate) { gServers[endpointId].Cluster().SetDelegate(delegate); } -} // namespace OTAProvider -} // namespace Clusters -} // namespace app -} // namespace chip +} // namespace chip::app::Clusters::OTAProvider diff --git a/components/esp_matter/data_model_provider/clusters/ota_software_update_provider/integration.h b/components/esp_matter/data_model_provider/clusters/ota_software_update_provider/integration.h new file mode 100644 index 000000000..fef7040a5 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/ota_software_update_provider/integration.h @@ -0,0 +1,21 @@ +// Copyright 2026 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. + +#include + +namespace chip::app::Clusters::OTAProvider { + +void SetDelegate(EndpointId endpointId, OTAProviderDelegate * delegate); + +} // chip::app::Clusters::OTAProvider diff --git a/components/esp_matter/data_model_provider/clusters/power_topology/integration.cpp b/components/esp_matter/data_model_provider/clusters/power_topology/integration.cpp new file mode 100644 index 000000000..c7606defb --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/power_topology/integration.cpp @@ -0,0 +1,50 @@ +// Copyright 2026 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. + +#include +#include +#include +#include + +#include "integration.h" + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; + +namespace { +std::unordered_map> gServers; + +} // namespace + +namespace chip::app::Clusters::PowerTopology { + +CHIP_ERROR Instance::Init() +{ + return esp_matter::data_model::provider::get_instance().registry().Register(mCluster.Registration()); +} + +void Instance::Shutdown() +{ + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&mCluster.Cluster()); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Failed to unregister Power Topology cluster: %" CHIP_ERROR_FORMAT, err.Format()); + } +} + +} // namespace chip::app::Clusters::PowerTopology + +void ESPMatterPowerTopologyClusterServerInitCallback(chip::EndpointId endpoint) {} +void ESPMatterPowerTopologyClusterServerShutdownCallback(chip::EndpointId endpoint, chip::app::ClusterShutdownType shutdownType) {} diff --git a/components/esp_matter/data_model_provider/clusters/power_topology/integration.h b/components/esp_matter/data_model_provider/clusters/power_topology/integration.h new file mode 100644 index 000000000..61071e5c4 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/power_topology/integration.h @@ -0,0 +1,41 @@ +// Copyright 2026 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. + +#pragma once + +#include +#include +#include + +namespace chip::app::Clusters::PowerTopology { + +class Instance +{ +public: + Instance(EndpointId aEndpointId, Delegate & aDelegate, BitMask aFeature) : + mCluster(PowerTopologyCluster::Config{ + .endpointId = aEndpointId, + .delegate = aDelegate, + .features = aFeature, + }) + {} + ~Instance() { Shutdown(); } + + CHIP_ERROR Init(); + void Shutdown(); + +private: + RegisteredServerCluster mCluster; +}; +} // namespace chip::app::Clusters::PowerTopology diff --git a/components/esp_matter/data_model_provider/clusters/push_av_stream_transport_integration.cpp b/components/esp_matter/data_model_provider/clusters/push_av_stream_transport/integration.cpp similarity index 82% rename from components/esp_matter/data_model_provider/clusters/push_av_stream_transport_integration.cpp rename to components/esp_matter/data_model_provider/clusters/push_av_stream_transport/integration.cpp index a299f608b..30fc9ecd6 100644 --- a/components/esp_matter/data_model_provider/clusters/push_av_stream_transport_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/push_av_stream_transport/integration.cpp @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include #include @@ -63,13 +63,13 @@ void ESPMatterPushAvStreamTransportClusterServerInitCallback(EndpointId endpoint } } -void ESPMatterPushAvStreamTransportClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterPushAvStreamTransportClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { if (!IsClusterEnabled(endpointId) || !gServers[endpointId].IsConstructed()) { return; } - CHIP_ERROR err = data_model::provider::get_instance().registry().Unregister(&gServers[endpointId].Cluster()); + CHIP_ERROR err = data_model::provider::get_instance().registry().Unregister(&gServers[endpointId].Cluster(), shutdownType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to unregister Push AV Stream Transport on endpoint %u - Error: %" CHIP_ERROR_FORMAT, @@ -82,18 +82,22 @@ void MatterPushAvStreamTransportPluginServerInitCallback() {} void MatterPushAvStreamTransportPluginServerShutdownCallback() {} -namespace chip { -namespace app { -namespace Clusters { -namespace PushAvStreamTransport { +namespace chip::app::Clusters::PushAvStreamTransport { -void SetDelegate(EndpointId endpointId, PushAvStreamTransportDelegate *delegate) +void SetDelegate(EndpointId endpointId, PushAvStreamTransportDelegate * delegate) { gServers[endpointId].Cluster().SetDelegate(delegate); - gServers[endpointId].Cluster().Init(); + (void)gServers[endpointId].Cluster().Init(); } -} // namespace PushAvStreamTransport -} // namespace Clusters -} // namespace app -} // namespace chip +void SetTLSClientManagementDelegate(EndpointId endpointId, TlsClientManagementDelegate * delegate) +{ + gServers[endpointId].Cluster().SetTLSClientManagementDelegate(delegate); +} + +void SetTlsCertificateManagementDelegate(EndpointId endpointId, TlsCertificateManagementDelegate * delegate) +{ + gServers[endpointId].Cluster().SetTlsCertificateManagementDelegate(delegate); +} + +} diff --git a/components/esp_matter/data_model_provider/clusters/push_av_stream_transport/integration.h b/components/esp_matter/data_model_provider/clusters/push_av_stream_transport/integration.h new file mode 100644 index 000000000..8d97feec3 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/push_av_stream_transport/integration.h @@ -0,0 +1,29 @@ +// Copyright 2026 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. + +#pragma once + +#include +#include +#include + +namespace chip::app::Clusters::PushAvStreamTransport { + +void SetDelegate(EndpointId endpointId, PushAvStreamTransportDelegate * delegate); + +void SetTLSClientManagementDelegate(EndpointId endpointId, TlsClientManagementDelegate * delegate); + +void SetTlsCertificateManagementDelegate(EndpointId endpointId, TlsCertificateManagementDelegate * delegate); + +} diff --git a/components/esp_matter/data_model_provider/clusters/resource_monitor/integration.cpp b/components/esp_matter/data_model_provider/clusters/resource_monitor/integration.cpp new file mode 100644 index 000000000..87408fbba --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/resource_monitor/integration.cpp @@ -0,0 +1,158 @@ +// Copyright 2026 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. + +#include "integration.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::ResourceMonitoring; + +namespace { +std::unordered_map> gServers; + +esp_err_t get_attr_val(esp_matter::cluster_t *cluster, uint32_t attribute_id, esp_matter_attr_val_t &val) +{ + esp_matter::attribute_t *attr = esp_matter::attribute::get(cluster, attribute_id); + if (!attr) { + return ESP_FAIL; + } + return esp_matter::attribute::get_val_internal(attr, &val); +} + +esp_err_t GetClusterConfig(EndpointId endpointId, ClusterId clusterId, + BitFlags &enabledFeatures, + ResourceMonitoringCluster::OptionalAttributeSet &optionalAttributeSet, + Attributes::DegradationDirection::TypeInfo::Type &aDegradationDirection, + bool &aResetConditionCommandSupported) +{ + esp_matter::cluster_t *cluster = esp_matter::cluster::get(endpointId, clusterId); + if (!cluster) { + return ESP_FAIL; + } + esp_matter_attr_val_t feature_val; + ESP_RETURN_ON_ERROR(get_attr_val(cluster, Globals::Attributes::FeatureMap::Id, feature_val), "TimeSync", + "Failed to get feature map"); + VerifyOrReturnError(feature_val.type == ESP_MATTER_VAL_TYPE_BITMAP32, ESP_FAIL); + enabledFeatures = BitFlags(feature_val.val.u32); + esp_matter_attr_val_t attr_val; + ESP_RETURN_ON_ERROR(get_attr_val(cluster, Attributes::DegradationDirection::Id, attr_val), "ResourceMonitoring", + "Failed to get DegradationDirection"); + VerifyOrReturnError(attr_val.type == ESP_MATTER_VAL_TYPE_ENUM8, ESP_FAIL); + aDegradationDirection = (Attributes::DegradationDirection::TypeInfo::Type)attr_val.val.u8; + if (esp_matter::command::get(cluster, Commands::ResetCondition::Id, esp_matter::COMMAND_FLAG_ACCEPTED)) { + aResetConditionCommandSupported = true; + } else { + aResetConditionCommandSupported = false; + } + + if (esp_matter::attribute::get(cluster, Attributes::InPlaceIndicator::Id)) { + optionalAttributeSet.Set(); + } + if (esp_matter::attribute::get(cluster, Attributes::LastChangedTime::Id)) { + optionalAttributeSet.Set(); + } + return ESP_OK; +} + +void ESPMatterResourceMonitoringClusterInitCallback(EndpointId endpointId, ClusterId clusterId) +{ + uint64_t index = ((uint64_t)(endpointId) << 32) + clusterId; + if (gServers[index].IsConstructed()) { + return; + } + BitFlags enabledFeatures; + ResourceMonitoringCluster::OptionalAttributeSet optionalAttributeSet; + Attributes::DegradationDirection::TypeInfo::Type degradationDirection; + bool resetConditionCommandSupported; + if (GetClusterConfig(endpointId, clusterId, enabledFeatures, optionalAttributeSet, degradationDirection, + resetConditionCommandSupported) != ESP_OK) { + ChipLogError(AppServer, "Failed to get config for Cluster %" PRIu32, clusterId); + return; + } + gServers[index].Create(endpointId, clusterId, enabledFeatures, optionalAttributeSet, degradationDirection, + resetConditionCommandSupported); + CHIP_ERROR err = + esp_matter::data_model::provider::get_instance().registry().Register(gServers[index].Registration()); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to register Cluster %" PRIu32 " - Error %" CHIP_ERROR_FORMAT, clusterId, + err.Format()); + } +} + +void ESPMatterResourceMonitoringClusterShutdownCallback(EndpointId endpointId, ClusterId clusterId, + ClusterShutdownType shutdownType) +{ + uint64_t index = ((uint64_t)(endpointId) << 32) + clusterId; + if (!gServers[index].IsConstructed()) { + return; + } + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[index].Cluster(), + shutdownType); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to unregister Cluster %" PRIu32 " - Error %" CHIP_ERROR_FORMAT, clusterId, + err.Format()); + } + gServers[index].Destroy(); +} +} // namespace + +namespace chip::app::Clusters::ResourceMonitoring { + +ResourceMonitoringCluster *GetClusterInstance(EndpointId endpointId, ClusterId clusterId) +{ + uint64_t index = ((uint64_t)(endpointId) << 32) + clusterId; + if (gServers[index].IsConstructed()) { + return &gServers[index].Cluster(); + } + return nullptr; +} + +CHIP_ERROR SetDefaultDelegate(EndpointId endpointId, ClusterId clusterId, Delegate *delegate) +{ + uint64_t index = ((uint64_t)(endpointId) << 32) + clusterId; + if (gServers[index].IsConstructed()) { + return gServers[index].Cluster().SetDelegate(delegate); + } + return CHIP_ERROR_INCORRECT_STATE; +} +} // namespace chip::app::Clusters::ResourceMonitoring + +void ESPMatterActivatedCarbonFilterMonitoringClusterServerInitCallback(EndpointId endpointId) +{ + ESPMatterResourceMonitoringClusterInitCallback(endpointId, ActivatedCarbonFilterMonitoring::Id); +} + +void ESPMatterActivatedCarbonFilterMonitoringClusterServerShutdownCallback(EndpointId endpointId, + ClusterShutdownType shutdownType) +{ + ESPMatterResourceMonitoringClusterShutdownCallback(endpointId, ActivatedCarbonFilterMonitoring::Id, shutdownType); +} + +void ESPMatterHepaFilterMonitoringClusterServerInitCallback(EndpointId endpointId) +{ + ESPMatterResourceMonitoringClusterInitCallback(endpointId, HepaFilterMonitoring::Id); +} + +void ESPMatterHepaFilterMonitoringClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) +{ + ESPMatterResourceMonitoringClusterShutdownCallback(endpointId, HepaFilterMonitoring::Id, shutdownType); +} diff --git a/components/esp_matter/data_model_provider/clusters/resource_monitor/integration.h b/components/esp_matter/data_model_provider/clusters/resource_monitor/integration.h new file mode 100644 index 000000000..e86dd529f --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/resource_monitor/integration.h @@ -0,0 +1,26 @@ +// Copyright 2026 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. + +#pragma once + +#include +#include + +namespace chip::app::Clusters::ResourceMonitoring { + +ResourceMonitoringCluster *GetClusterInstance(EndpointId endpointId, ClusterId clusterId); + +CHIP_ERROR SetDefaultDelegate(EndpointId endpointId, ClusterId clusterId, Delegate *delegate); + +} diff --git a/components/esp_matter/data_model_provider/clusters/scenes_management/attribute_value_pair_validator.cpp b/components/esp_matter/data_model_provider/clusters/scenes_management/attribute_value_pair_validator.cpp new file mode 100644 index 000000000..c22bcc0e3 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/scenes_management/attribute_value_pair_validator.cpp @@ -0,0 +1,196 @@ +// Copyright 2026 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. + +#include "attribute_value_pair_validator.h" +#include +#include +#include + +namespace chip::scenes { + +namespace { + +using AttributeValuePairType = app::Clusters::ScenesManagement::Structs::AttributeValuePairStruct::Type; + +template +typename app::NumericAttributeTraits::WorkingType ConvertDefaultValueToWorkingValue( + const esp_matter_attr_val_t &attr_val) +{ + switch (attr_val.type & (~ESP_MATTER_VAL_NULLABLE_BASE)) { + case ESP_MATTER_VAL_TYPE_BOOLEAN: + return static_cast::WorkingType>(attr_val.val.b); + case ESP_MATTER_VAL_TYPE_INT8: + return static_cast::WorkingType>(attr_val.val.i8); + case ESP_MATTER_VAL_TYPE_INT16: + return static_cast::WorkingType>(attr_val.val.i16); + case ESP_MATTER_VAL_TYPE_INT32: + return static_cast::WorkingType>(attr_val.val.i32); + case ESP_MATTER_VAL_TYPE_INT64: + return static_cast::WorkingType>(attr_val.val.i64); + case ESP_MATTER_VAL_TYPE_UINT8: + case ESP_MATTER_VAL_TYPE_ENUM8: + case ESP_MATTER_VAL_TYPE_BITMAP8: + return static_cast::WorkingType>(attr_val.val.u8); + case ESP_MATTER_VAL_TYPE_UINT16: + case ESP_MATTER_VAL_TYPE_ENUM16: + case ESP_MATTER_VAL_TYPE_BITMAP16: + return static_cast::WorkingType>(attr_val.val.u16); + case ESP_MATTER_VAL_TYPE_UINT32: + case ESP_MATTER_VAL_TYPE_BITMAP32: + return static_cast::WorkingType>(attr_val.val.u32); + case ESP_MATTER_VAL_TYPE_UINT64: + return static_cast::WorkingType>(attr_val.val.u64); + case ESP_MATTER_VAL_TYPE_FLOAT: + return static_cast::WorkingType>(attr_val.val.f); + default: + return 0; + } + return 0; +} +/// IsExactlyOneValuePopulated +/// @brief Helper function to verify that exactly one value is populated in a given AttributeValuePairType +/// @param AttributeValuePairType & type AttributeValuePairType to verify +/// @return bool true if only one value is populated, false otherwise +bool IsExactlyOneValuePopulated(const AttributeValuePairType &type) +{ + int count = 0; + if (type.valueUnsigned8.HasValue()) + count++; + if (type.valueSigned8.HasValue()) + count++; + if (type.valueUnsigned16.HasValue()) + count++; + if (type.valueSigned16.HasValue()) + count++; + if (type.valueUnsigned32.HasValue()) + count++; + if (type.valueSigned32.HasValue()) + count++; + if (type.valueUnsigned64.HasValue()) + count++; + if (type.valueSigned64.HasValue()) + count++; + return count == 1; +} + +/// CapAttributeValue +/// Cap the attribute value based on the attribute's min and max if they are defined, +/// or based on the attribute's size if they are not. +/// +/// The TypeForMinMax template parameter determines the type to use for the +/// min/max computation. The Type template parameter determines how the +/// resulting value is actually represented, because for booleans we +/// unfortunately end up using uint8, not an actual boolean. +/// +/// @param[in] value The value from the AttributeValuePairType that we were given. +/// @param[in] metadata The metadata for the attribute the value is for. +/// +/// +/// +template +void CapAttributeValue(typename app::NumericAttributeTraits::WorkingType &value, esp_matter::attribute_t *attr) +{ + using IntType = app::NumericAttributeTraits; + using WorkingType = std::remove_reference_t; + esp_matter_attr_val_t attr_val; + if (esp_matter::attribute::get_val_internal(attr, &attr_val) != ESP_OK) { + return; + } + + bool is_nullable = (attr_val.type & ESP_MATTER_VAL_NULLABLE_BASE); + WorkingType minValue = IntType::MinValue(is_nullable); + WorkingType maxValue = IntType::MaxValue(is_nullable); + + // Check metadata for min and max values + esp_matter_attr_bounds_t bounds; + if (esp_matter::attribute::get_bounds(attr, &bounds)) { + minValue = ConvertDefaultValueToWorkingValue(bounds.min); + maxValue = ConvertDefaultValueToWorkingValue(bounds.max); + } + + if (is_nullable && (minValue > value || maxValue < value)) { + // If the attribute is nullable, the value can be set to NULL + app::NumericAttributeTraits::SetNull(value); + return; + } + + if (minValue > value) { + value = minValue; + } else if (maxValue < value) { + value = maxValue; + } +} +} // namespace +CHIP_ERROR CodegenAttributeValuePairValidator::Validate(const app::ConcreteClusterPath &clusterPath, + AttributeValuePairType &value) +{ + esp_matter::attribute_t *attr = + esp_matter::attribute::get(clusterPath.mEndpointId, clusterPath.mClusterId, value.attributeID); + esp_matter_attr_val_t attr_val; + if (nullptr == attr || esp_matter::attribute::get_val_internal(attr, &attr_val) != ESP_OK) { + return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); + } + + // There should never be more than one populated value in an ExtensionFieldSet + VerifyOrReturnError(IsExactlyOneValuePopulated(value), CHIP_ERROR_INVALID_ARGUMENT); + + switch (attr_val.type & (~ESP_MATTER_VAL_NULLABLE_BASE)) { + case ESP_MATTER_VAL_TYPE_BOOLEAN: + VerifyOrReturnError(value.valueUnsigned8.HasValue(), CHIP_ERROR_INVALID_ARGUMENT); + CapAttributeValue(value.valueUnsigned8.Value(), attr); + break; + case ESP_MATTER_VAL_TYPE_UINT8: + case ESP_MATTER_VAL_TYPE_ENUM8: + case ESP_MATTER_VAL_TYPE_BITMAP8: + VerifyOrReturnError(value.valueUnsigned8.HasValue(), CHIP_ERROR_INVALID_ARGUMENT); + CapAttributeValue(value.valueUnsigned8.Value(), attr); + break; + case ESP_MATTER_VAL_TYPE_UINT16: + case ESP_MATTER_VAL_TYPE_ENUM16: + case ESP_MATTER_VAL_TYPE_BITMAP16: + VerifyOrReturnError(value.valueUnsigned16.HasValue(), CHIP_ERROR_INVALID_ARGUMENT); + CapAttributeValue(value.valueUnsigned16.Value(), attr); + break; + case ESP_MATTER_VAL_TYPE_UINT32: + case ESP_MATTER_VAL_TYPE_BITMAP32: + VerifyOrReturnError(value.valueUnsigned32.HasValue(), CHIP_ERROR_INVALID_ARGUMENT); + CapAttributeValue(value.valueUnsigned32.Value(), attr); + break; + case ESP_MATTER_VAL_TYPE_UINT64: + VerifyOrReturnError(value.valueUnsigned64.HasValue(), CHIP_ERROR_INVALID_ARGUMENT); + CapAttributeValue(value.valueUnsigned64.Value(), attr); + break; + case ESP_MATTER_VAL_TYPE_INT8: + VerifyOrReturnError(value.valueSigned8.HasValue(), CHIP_ERROR_INVALID_ARGUMENT); + CapAttributeValue(value.valueSigned8.Value(), attr); + break; + case ESP_MATTER_VAL_TYPE_INT16: + VerifyOrReturnError(value.valueSigned16.HasValue(), CHIP_ERROR_INVALID_ARGUMENT); + CapAttributeValue(value.valueSigned16.Value(), attr); + break; + case ESP_MATTER_VAL_TYPE_INT32: + VerifyOrReturnError(value.valueSigned32.HasValue(), CHIP_ERROR_INVALID_ARGUMENT); + CapAttributeValue(value.valueSigned32.Value(), attr); + break; + case ESP_MATTER_VAL_TYPE_INT64: + VerifyOrReturnError(value.valueSigned64.HasValue(), CHIP_ERROR_INVALID_ARGUMENT); + CapAttributeValue(value.valueSigned64.Value(), attr); + break; + default: + return CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute); + } + + return CHIP_NO_ERROR; +} +} // namespace chip::scenes diff --git a/components/esp_matter/data_model_provider/clusters/scenes_management/attribute_value_pair_validator.h b/components/esp_matter/data_model_provider/clusters/scenes_management/attribute_value_pair_validator.h new file mode 100644 index 000000000..fb28e7a4d --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/scenes_management/attribute_value_pair_validator.h @@ -0,0 +1,35 @@ +// Copyright 2026 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. + +#pragma once + +#include + +namespace chip::scenes { + +// This class is used by on-off, level-control, color-control, and mode-select cluster, temporarily use this name until +// those clusters are decoupled with ember. +class CodegenAttributeValuePairValidator : public AttributeValuePairValidator { +public: + ~CodegenAttributeValuePairValidator() override = default; + CHIP_ERROR Validate(const app::ConcreteClusterPath &clusterPath, AttributeValuePairType &value) override; + + static CodegenAttributeValuePairValidator &Instance() + { + static CodegenAttributeValuePairValidator sInstance; + return sInstance; + } +}; + +} // namespace chip::scenes diff --git a/components/esp_matter/data_model_provider/clusters/scenes_management/integration.cpp b/components/esp_matter/data_model_provider/clusters/scenes_management/integration.cpp new file mode 100644 index 000000000..88c78aec2 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/scenes_management/integration.cpp @@ -0,0 +1,223 @@ +// Copyright 2026 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. + +#include "integration.h" +#include "esp_err.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "credentials/GroupDataProvider.h" + +using SceneTable = chip::scenes::SceneTable; + +namespace chip::app::Clusters::ScenesManagement { + +namespace { + +class DefaultScenesManagementTableProvider : public ScenesManagementTableProvider { +public: + void SetParameters(EndpointId endpointId, uint16_t endpointTableSize) + { + mEndpointId = endpointId; + mEndpointTableSize = endpointTableSize; + } + EndpointId GetEndpointId() const { return mEndpointId; } + + ScenesManagementSceneTable *Take() override { return scenes::GetSceneTableImpl(mEndpointId, mEndpointTableSize); } + void Release(ScenesManagementSceneTable *) override {} + +private: + EndpointId mEndpointId = kInvalidEndpointId; + uint16_t mEndpointTableSize = scenes::kMaxScenesPerEndpoint; +}; + +std::unordered_map> gServers; +std::unordered_map gTableProviders; + +} // namespace + +ScenesManagementCluster *FindClusterOnEndpoint(EndpointId endpointId) +{ + if (gServers[endpointId].IsConstructed()) { + return &gServers[endpointId].Cluster(); + } + return nullptr; +} + +ScenesServer &ScenesServer::Instance() +{ + static ScenesServer gInstance; + return gInstance; +} + +bool ScenesServer::IsHandlerRegistered(EndpointId aEndpointId, scenes::SceneHandler *handler) +{ + SceneTable *sceneTable = scenes::GetSceneTableImpl(aEndpointId); + return sceneTable->mHandlerList.Contains(handler); +} + +void ScenesServer::RegisterSceneHandler(EndpointId aEndpointId, scenes::SceneHandler *handler) +{ + SceneTable *sceneTable = scenes::GetSceneTableImpl(aEndpointId); + + if (!IsHandlerRegistered(aEndpointId, handler)) { + sceneTable->RegisterHandler(handler); + } +} + +void ScenesServer::UnregisterSceneHandler(EndpointId aEndpointId, scenes::SceneHandler *handler) +{ + SceneTable *sceneTable = scenes::GetSceneTableImpl(aEndpointId); + + if (IsHandlerRegistered(aEndpointId, handler)) { + sceneTable->UnregisterHandler(handler); + } +} + +void ScenesServer::GroupWillBeRemoved(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId) +{ + ScenesManagementCluster *cluster = FindClusterOnEndpoint(aEndpointId); + VerifyOrReturn(cluster != nullptr); + + TEMPORARY_RETURN_IGNORED cluster->GroupWillBeRemoved(aFabricIx, aGroupId); +} + +void ScenesServer::MakeSceneInvalid(EndpointId aEndpointId, FabricIndex aFabricIx) +{ + ScenesManagementCluster *cluster = FindClusterOnEndpoint(aEndpointId); + VerifyOrReturn(cluster != nullptr); + + TEMPORARY_RETURN_IGNORED cluster->MakeSceneInvalid(aFabricIx); +} + +void ScenesServer::MakeSceneInvalidForAllFabrics(EndpointId aEndpointId) +{ + ScenesManagementCluster *cluster = FindClusterOnEndpoint(aEndpointId); + VerifyOrReturn(cluster != nullptr); + TEMPORARY_RETURN_IGNORED cluster->MakeSceneInvalidForAllFabrics(); +} + +void ScenesServer::StoreCurrentScene(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId, SceneId aSceneId) +{ + ScenesManagementCluster *cluster = FindClusterOnEndpoint(aEndpointId); + VerifyOrReturn(cluster != nullptr); + TEMPORARY_RETURN_IGNORED cluster->StoreCurrentScene(aFabricIx, aGroupId, aSceneId); +} + +void ScenesServer::RecallScene(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId, SceneId aSceneId) +{ + ScenesManagementCluster *cluster = FindClusterOnEndpoint(aEndpointId); + VerifyOrReturn(cluster != nullptr); + TEMPORARY_RETURN_IGNORED cluster->RecallScene(aFabricIx, aGroupId, aSceneId); +} + +void ScenesServer::RemoveFabric(EndpointId aEndpointId, FabricIndex aFabricIndex) +{ + ScenesManagementCluster *cluster = FindClusterOnEndpoint(aEndpointId); + VerifyOrReturn(cluster != nullptr); + TEMPORARY_RETURN_IGNORED cluster->RemoveFabric(aFabricIndex); +} + +} // namespace chip::app::Clusters::ScenesManagement + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::ScenesManagement; + +namespace { + +esp_err_t get_attr_val(esp_matter::cluster_t *cluster, AttributeId attrId, esp_matter_attr_val_t &val) +{ + esp_matter::attribute_t *attr = esp_matter::attribute::get(cluster, attrId); + if (!attr) { + return ESP_ERR_NOT_FOUND; + } + return esp_matter::attribute::get_val_internal(attr, &val); +} + +esp_err_t GetScenesClusterContextParams(EndpointId endpointId, BitMask &featureMap, + bool &supportsCopyScene, uint16_t &tableSize) +{ + esp_matter::cluster_t *cluster = esp_matter::cluster::get(endpointId, ScenesManagement::Id); + esp_matter_attr_val_t attr_val; + ESP_RETURN_ON_ERROR(get_attr_val(cluster, Globals::Attributes::FeatureMap::Id, attr_val), "Scenes", + "Failed to get feature map"); + VerifyOrReturnValue(attr_val.type == ESP_MATTER_VAL_TYPE_BITMAP32, ESP_ERR_INVALID_ARG); + featureMap = BitMask(attr_val.val.u32); + supportsCopyScene = + (esp_matter::command::get(endpointId, ScenesManagement::Id, Commands::CopyScene::Id) != nullptr); + ESP_RETURN_ON_ERROR(get_attr_val(cluster, Attributes::SceneTableSize::Id, attr_val), "Scenes", + "Failed to get scenes table size"); + VerifyOrReturnValue(attr_val.type == ESP_MATTER_VAL_TYPE_UINT16, ESP_ERR_INVALID_ARG); + tableSize = attr_val.val.u16; + return ESP_OK; +} + +} // namespace + +void ESPMatterScenesManagementClusterServerInitCallback(EndpointId endpointId) +{ + if (gServers[endpointId].IsConstructed()) { + return; + } + BitMask featureMap; + bool supportsCopyScene; + uint16_t tableSize; + if (GetScenesClusterContextParams(endpointId, featureMap, supportsCopyScene, tableSize) != ESP_OK) { + ESP_LOGE("Scenes", "Failed to get cluster context parameters"); + return; + } + gTableProviders[endpointId].SetParameters(endpointId, tableSize); + gServers[endpointId].Create(endpointId, + ScenesManagementCluster::Context{ + .groupDataProvider = Credentials::GetGroupDataProvider(), + .fabricTable = &Server::GetInstance().GetFabricTable(), + .features = featureMap, + .sceneTableProvider = gTableProviders[endpointId], + .supportsCopyScene = supportsCopyScene, + }); + CHIP_ERROR err = + esp_matter::data_model::provider::get_instance().registry().Register(gServers[endpointId].Registration()); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to register Scenes on endpoint %u - Error: %" CHIP_ERROR_FORMAT, endpointId, + err.Format()); + } +} + +void ESPMatterScenesManagementClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) +{ + if (!gServers[endpointId].IsConstructed()) { + return; + } + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister( + &gServers[endpointId].Cluster(), shutdownType); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to register Scenes on endpoint %u - Error: %" CHIP_ERROR_FORMAT, endpointId, + err.Format()); + } + gServers[endpointId].Destroy(); +} + +void MatterScenesManagementPluginServerInitCallback() +{ +} diff --git a/components/esp_matter/data_model_provider/clusters/scenes_management/integration.h b/components/esp_matter/data_model_provider/clusters/scenes_management/integration.h new file mode 100644 index 000000000..25ab19e3e --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/scenes_management/integration.h @@ -0,0 +1,55 @@ +// Copyright 2026 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. + +#pragma once + +#include + +// most ember implementations will want access to these +#include + +namespace chip::app::Clusters::ScenesManagement { + +class ScenesServer +{ +public: + static constexpr SceneId kGlobalSceneId = 0x00; + static constexpr GroupId kGlobalSceneGroupId = 0x0000; + + ScenesServer() = default; + ~ScenesServer() = default; + + static ScenesServer & Instance(); + + // Callbacks + void GroupWillBeRemoved(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId); + void MakeSceneInvalid(EndpointId aEndpointId, FabricIndex aFabricIx); + void MakeSceneInvalidForAllFabrics(EndpointId aEndpointId); + void StoreCurrentScene(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId, SceneId aSceneId); + void RecallScene(FabricIndex aFabricIx, EndpointId aEndpointId, GroupId aGroupId, SceneId aSceneId); + + // Handlers for extension field sets + bool IsHandlerRegistered(EndpointId endpointId, scenes::SceneHandler * handler); + void RegisterSceneHandler(EndpointId endpointId, scenes::SceneHandler * handler); + void UnregisterSceneHandler(EndpointId endpointId, scenes::SceneHandler * handler); + + // Fabric + void RemoveFabric(EndpointId aEndpointId, FabricIndex aFabricIndex); +}; + +/// Returns the cluster instance on the given endpoint or nullptr if the cluster does not exist +/// or was not yet initialized +ScenesManagementCluster * FindClusterOnEndpoint(EndpointId endpointId); + +} // namespace chip::app::Clusters::ScenesManagement diff --git a/components/esp_matter/data_model_provider/clusters/software_diagnostics_integration.cpp b/components/esp_matter/data_model_provider/clusters/software_diagnostics/integration.cpp similarity index 90% rename from components/esp_matter/data_model_provider/clusters/software_diagnostics_integration.cpp rename to components/esp_matter/data_model_provider/clusters/software_diagnostics/integration.cpp index 01010ebcd..d7326a546 100644 --- a/components/esp_matter/data_model_provider/clusters/software_diagnostics_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/software_diagnostics/integration.cpp @@ -13,8 +13,8 @@ // limitations under the License. #include -#include -#include +#include +#include #include #include @@ -60,10 +60,10 @@ void ESPMatterSoftwareDiagnosticsClusterServerInitCallback(EndpointId endpointId } } -void ESPMatterSoftwareDiagnosticsClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterSoftwareDiagnosticsClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { VerifyOrReturn(endpointId == kRootEndpointId); - CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster()); + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster(), shutdownType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to unregister SoftwareDiagnostics - Error: %" CHIP_ERROR_FORMAT, err.Format()); } diff --git a/components/esp_matter/data_model_provider/clusters/time_format_localization_integration.cpp b/components/esp_matter/data_model_provider/clusters/time_format_localization/integration.cpp similarity index 81% rename from components/esp_matter/data_model_provider/clusters/time_format_localization_integration.cpp rename to components/esp_matter/data_model_provider/clusters/time_format_localization/integration.cpp index 335c1fdce..5eccb5817 100644 --- a/components/esp_matter/data_model_provider/clusters/time_format_localization_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/time_format_localization/integration.cpp @@ -13,8 +13,9 @@ // limitations under the License. #include "esp_matter_data_model.h" +#include "esp_matter_data_model_priv.h" #include -#include +#include #include #include "clusters/TimeFormatLocalization/Enums.h" @@ -30,7 +31,8 @@ uint32_t get_feature_map(esp_matter::cluster_t *cluster) esp_matter::attribute_t *attribute = esp_matter::attribute::get(cluster, Globals::Attributes::FeatureMap::Id); if (attribute) { esp_matter_attr_val_t val = esp_matter_invalid(nullptr); - if (esp_matter::attribute::get_val(attribute, &val) == ESP_OK && val.type == ESP_MATTER_VAL_TYPE_BITMAP32) { + if (esp_matter::attribute::get_val_internal(attribute, &val) == ESP_OK && + val.type == ESP_MATTER_VAL_TYPE_BITMAP32) { return val.val.u32; } } @@ -43,7 +45,8 @@ TimeFormatLocalization::HourFormatEnum get_default_hour_format(esp_matter::clust esp_matter::attribute::get(cluster, TimeFormatLocalization::Attributes::HourFormat::Id); if (attribute) { esp_matter_attr_val_t val = esp_matter_invalid(nullptr); - if (esp_matter::attribute::get_val(attribute, &val) == ESP_OK && val.type == ESP_MATTER_VAL_TYPE_ENUM8) { + if (esp_matter::attribute::get_val_internal(attribute, &val) == ESP_OK && + val.type == ESP_MATTER_VAL_TYPE_ENUM8) { return TimeFormatLocalization::HourFormatEnum(val.val.u8); } } @@ -56,7 +59,8 @@ TimeFormatLocalization::CalendarTypeEnum get_default_calendar_type(esp_matter::c esp_matter::attribute::get(cluster, TimeFormatLocalization::Attributes::ActiveCalendarType::Id); if (attribute) { esp_matter_attr_val_t val = esp_matter_invalid(nullptr); - if (esp_matter::attribute::get_val(attribute, &val) == ESP_OK && val.type == ESP_MATTER_VAL_TYPE_ENUM8) { + if (esp_matter::attribute::get_val_internal(attribute, &val) == ESP_OK && + val.type == ESP_MATTER_VAL_TYPE_ENUM8) { return TimeFormatLocalization::CalendarTypeEnum(val.val.u8); } } @@ -81,16 +85,19 @@ void ESPMatterTimeFormatLocalizationClusterServerInitCallback(EndpointId endpoin } } -void ESPMatterTimeFormatLocalizationClusterServerShutdownCallback(EndpointId endpoint) +void ESPMatterTimeFormatLocalizationClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType) { // This cluster should only exist in Root endpoint. VerifyOrReturn(endpoint == kRootEndpointId); - CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster()); + CHIP_ERROR err = + esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster(), shutdownType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "TimeFormatLocalization unregister error: %" CHIP_ERROR_FORMAT, err.Format()); } gServer.Destroy(); } -void MatterTimeFormatLocalizationPluginServerInitCallback() {} +void MatterTimeFormatLocalizationPluginServerInitCallback() +{ +} diff --git a/components/esp_matter/data_model_provider/clusters/time_synchronization/integration.cpp b/components/esp_matter/data_model_provider/clusters/time_synchronization/integration.cpp new file mode 100644 index 000000000..8dd8173f6 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/time_synchronization/integration.cpp @@ -0,0 +1,147 @@ +// Copyright 2026 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. + +#include "integration.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "support/CodeUtils.h" + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::TimeSynchronization; + +namespace { +LazyRegisteredServerCluster gServer; +TimeSynchronization::Delegate * gDelegate = nullptr; + +esp_err_t get_attr_val(esp_matter::cluster_t *cluster, uint32_t attribute_id, esp_matter_attr_val_t &val) +{ + esp_matter::attribute_t *attr = esp_matter::attribute::get(cluster, attribute_id); + if (!attr) { + return ESP_FAIL; + } + return esp_matter::attribute::get_val_internal(attr, &val); +} + +esp_err_t GetClusterConfig(EndpointId endpointId, TimeSynchronizationCluster::OptionalAttributeSet &attrSet, + TimeSynchronizationCluster::StartupConfiguration &startupConfig, + BitFlags &featureMap) +{ + esp_matter::cluster_t *cluster = esp_matter::cluster::get(endpointId, TimeSynchronization::Id); + if (!cluster) { + return ESP_FAIL; + } + esp_matter_attr_val_t feature_val; + ESP_RETURN_ON_ERROR(get_attr_val(cluster, Globals::Attributes::FeatureMap::Id, feature_val), "TimeSync", + "Failed to get feature map"); + VerifyOrReturnError(feature_val.type == ESP_MATTER_VAL_TYPE_BITMAP32, ESP_FAIL); + featureMap = BitFlags(feature_val.val.u32); + esp_matter_attr_val_t attr_val; + if (featureMap.Has(Feature::kNTPClient) || featureMap.Has(Feature::kNTPServer)) { + ESP_RETURN_ON_ERROR(get_attr_val(cluster, Attributes::SupportsDNSResolve::Id, attr_val), "TimeSync", + "Failed to get SupportsDNSResolve"); + VerifyOrReturnError(attr_val.type == ESP_MATTER_VAL_TYPE_BOOLEAN, ESP_FAIL); + if (featureMap.Has(Feature::kNTPClient)) { + startupConfig.supportsDNSResolve = attr_val.val.b; + } + if (featureMap.Has(Feature::kNTPServer)) { + startupConfig.ntpServerAvailable = attr_val.val.b; + } + } + if (featureMap.Has(Feature::kTimeZone)) { + ESP_RETURN_ON_ERROR(get_attr_val(cluster, Attributes::TimeZoneDatabase::Id, attr_val), "TimeSync", + "Failed to get TimeZoneDatabase"); + VerifyOrReturnError(attr_val.type == ESP_MATTER_VAL_TYPE_ENUM8, ESP_FAIL); + startupConfig.timeZoneDatabase = (TimeZoneDatabaseEnum)attr_val.val.u8; + } + if (get_attr_val(cluster, Attributes::TimeSource::Id, attr_val) == ESP_OK && + attr_val.type == ESP_MATTER_VAL_TYPE_ENUM8) { + attrSet.Set(); + startupConfig.timeSource = (TimeSourceEnum)attr_val.val.u8; + } + startupConfig.delegate = GetDefaultDelegate(); + return ESP_OK; +} +} + +void ESPMatterTimeSynchronizationClusterServerInitCallback(EndpointId endpointId) +{ + VerifyOrReturn(endpointId == kRootEndpointId); + TimeSynchronizationCluster::OptionalAttributeSet attrSet; + TimeSynchronizationCluster::StartupConfiguration startupConfig; + BitFlags featureMap; + VerifyOrReturn(GetClusterConfig(endpointId, attrSet, startupConfig, featureMap) == ESP_OK); + gServer.Create(endpointId, featureMap, attrSet, startupConfig); + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Register(gServer.Registration()); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to register TimeSynchronization - Error %" CHIP_ERROR_FORMAT, err.Format()); + } +} + +void ESPMatterTimeSynchronizationClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) +{ + VerifyOrReturn(endpointId == kRootEndpointId); + VerifyOrReturn(gServer.IsConstructed()); + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServer.Cluster(), shutdownType); + if (err != CHIP_NO_ERROR) { + ChipLogError(AppServer, "Failed to unregister TimeSynchronization - Error %" CHIP_ERROR_FORMAT, err.Format()); + } + gServer.Destroy(); +} + +void MatterTimeSynchronizationPluginServerInitCallback() {} + +namespace chip::app::Clusters::TimeSynchronization { + +TimeSynchronizationCluster * GetClusterInstance() +{ + VerifyOrReturnValue(gServer.IsConstructed(), nullptr); + return &gServer.Cluster(); +} + +void SetDefaultDelegate(Delegate * delegate) +{ + VerifyOrReturn(delegate != nullptr); + gDelegate = delegate; + auto timeSynchronization = GetClusterInstance(); + if (timeSynchronization != nullptr) + { + timeSynchronization->SetDelegate(gDelegate); + } +} + +Delegate * GetDefaultDelegate() +{ + auto timeSynchronization = GetClusterInstance(); + if (timeSynchronization != nullptr) + { + return timeSynchronization->GetDelegate(); + } + if (gDelegate == nullptr) + { + static DefaultTimeSyncDelegate delegate; + gDelegate = &delegate; + } + return gDelegate; +} + +} // namespace chip::app::Clusters::TimeSynchronization diff --git a/components/esp_matter/data_model_provider/clusters/time_synchronization/integration.h b/components/esp_matter/data_model_provider/clusters/time_synchronization/integration.h new file mode 100644 index 000000000..0466d69bf --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/time_synchronization/integration.h @@ -0,0 +1,27 @@ +// Copyright 2026 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. + +#pragma once + +#include + +namespace chip::app::Clusters::TimeSynchronization { + +TimeSynchronizationCluster * GetClusterInstance(); + +// Delegate functions +void SetDefaultDelegate(Delegate * delegate); +Delegate * GetDefaultDelegate(); + +} // namespace chip::app::Clusters::TimeSynchronization diff --git a/components/esp_matter/data_model_provider/clusters/unit_localization/integration.cpp b/components/esp_matter/data_model_provider/clusters/unit_localization/integration.cpp new file mode 100644 index 000000000..281ccf4bb --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/unit_localization/integration.cpp @@ -0,0 +1,46 @@ +// Copyright 2026 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. + +#include "integration.h" +#include "app/AttributeAccessInterfaceRegistry.h" +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::app::Clusters::UnitLocalization; + +namespace chip::app::Clusters::UnitLocalization { + +UnitLocalizationServer & UnitLocalizationServer::Instance() +{ + static UnitLocalizationServer mInstance; + return mInstance; +} + +} // namespace chip::app::Clusters::UnitLocalization + +void ESPMatterUnitLocalizationServerInitCallback(EndpointId endpointId) {} +void ESPMatterUnitLocalizationServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) {} + +void MatterUnitLocalizationPluginServerInitCallback() +{ + LogErrorOnFailure(UnitLocalizationServer::Instance().Init()); + AttributeAccessInterfaceRegistry::Instance().Register(&UnitLocalizationServer::Instance()); +} + +void MatterUnitLocalizationPluginServerShutdownCallback() +{ + AttributeAccessInterfaceRegistry::Instance().Unregister(&UnitLocalizationServer::Instance()); +} diff --git a/components/esp_matter/data_model_provider/clusters/unit_localization/integration.h b/components/esp_matter/data_model_provider/clusters/unit_localization/integration.h new file mode 100644 index 000000000..8126462fd --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/unit_localization/integration.h @@ -0,0 +1,27 @@ +// Copyright 2026 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. + +#pragma once + +#include + +namespace chip::app::Clusters::UnitLocalization { + +class UnitLocalizationServer : public UnitLocalizationCluster +{ +public: + static UnitLocalizationServer & Instance(); +}; + +} diff --git a/components/esp_matter/data_model_provider/clusters/user_label_integration.cpp b/components/esp_matter/data_model_provider/clusters/user_label/integration.cpp similarity index 91% rename from components/esp_matter/data_model_provider/clusters/user_label_integration.cpp rename to components/esp_matter/data_model_provider/clusters/user_label/integration.cpp index f1093806b..e804a7fc9 100644 --- a/components/esp_matter/data_model_provider/clusters/user_label_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/user_label/integration.cpp @@ -13,7 +13,7 @@ // limitations under the License. #include -#include +#include #include #include @@ -41,10 +41,10 @@ void ESPMatterUserLabelClusterServerInitCallback(EndpointId endpointId) } } -void ESPMatterUserLabelClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterUserLabelClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { CHIP_ERROR err = - esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[endpointId].Cluster()); + esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[endpointId].Cluster(), shutdownType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to unregister UserLabel on endpoint %u - Error: %" CHIP_ERROR_FORMAT, endpointId, err.Format()); diff --git a/components/esp_matter/data_model_provider/clusters/webrtc_transport_provider/integration.cpp b/components/esp_matter/data_model_provider/clusters/webrtc_transport_provider/integration.cpp new file mode 100644 index 000000000..b99af25a4 --- /dev/null +++ b/components/esp_matter/data_model_provider/clusters/webrtc_transport_provider/integration.cpp @@ -0,0 +1,36 @@ +// Copyright 2026 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. + +#include +#include +#include + +void ESPMatterWebRTCTransportProviderClusterServerInitCallback(chip::EndpointId endpointId) +{ +} + +void ESPMatterWebRTCTransportProviderClusterServerShutdownCallback( + chip::EndpointId endpointId, chip::app::ClusterShutdownType shutdownType) +{ +} + +void MatterWebRTCTransportProviderPluginServerInitCallback() +{ + ChipLogProgress(Zcl, "Initializing WebRTC Transport Provider cluster."); +} + +void MatterWebRTCTransportProviderPluginServerShutdownCallback() +{ + ChipLogProgress(Zcl, "Shutdown WebRTC Transport Provider cluster."); +} diff --git a/components/esp_matter/data_model_provider/clusters/wifi_network_diagnostic_integration.cpp b/components/esp_matter/data_model_provider/clusters/wifi_network_diagnostic/integration.cpp similarity index 94% rename from components/esp_matter/data_model_provider/clusters/wifi_network_diagnostic_integration.cpp rename to components/esp_matter/data_model_provider/clusters/wifi_network_diagnostic/integration.cpp index dfacf1e26..ca8cf7981 100644 --- a/components/esp_matter/data_model_provider/clusters/wifi_network_diagnostic_integration.cpp +++ b/components/esp_matter/data_model_provider/clusters/wifi_network_diagnostic/integration.cpp @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include #include @@ -79,10 +79,10 @@ void ESPMatterWiFiNetworkDiagnosticsClusterServerInitCallback(EndpointId endpoin } // This callback is called for any endpoint (fixed or dynamic) that is registered with the Ember machinery. -void ESPMatterWiFiNetworkDiagnosticsClusterServerShutdownCallback(EndpointId endpointId) +void ESPMatterWiFiNetworkDiagnosticsClusterServerShutdownCallback(EndpointId endpointId, ClusterShutdownType shutdownType) { - CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[endpointId].Cluster()); + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&gServers[endpointId].Cluster(), shutdownType); if (err != CHIP_NO_ERROR) { ChipLogError(AppServer, "Failed to unregister WiFiNetworkDiagnostics on endpoint %u - Error: %" CHIP_ERROR_FORMAT, endpointId, diff --git a/components/esp_matter/data_model_provider/esp_matter_attr_data_buffer.cpp b/components/esp_matter/data_model_provider/esp_matter_attr_data_buffer.cpp index 4fe1c0631..9bd7a212b 100644 --- a/components/esp_matter/data_model_provider/esp_matter_attr_data_buffer.cpp +++ b/components/esp_matter/data_model_provider/esp_matter_attr_data_buffer.cpp @@ -165,7 +165,7 @@ CHIP_ERROR attribute_data_encode_buffer::Encode(chip::TLV::TLVWriter &writer, ch switch (m_attr_val.type) { case ESP_MATTER_VAL_TYPE_NULLABLE_BOOLEAN: if (chip::app::NumericAttributeTraits::IsNullValue(m_attr_val.val.b)) { - writer.PutNull(tag); + ReturnErrorOnFailure(writer.PutNull(tag)); break; } // Fall through @@ -176,7 +176,7 @@ CHIP_ERROR attribute_data_encode_buffer::Encode(chip::TLV::TLVWriter &writer, ch case ESP_MATTER_VAL_TYPE_NULLABLE_ENUM8: case ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP8: if (chip::app::NumericAttributeTraits::IsNullValue(m_attr_val.val.u8)) { - writer.PutNull(tag); + ReturnErrorOnFailure(writer.PutNull(tag)); break; } // Fall through @@ -189,7 +189,7 @@ CHIP_ERROR attribute_data_encode_buffer::Encode(chip::TLV::TLVWriter &writer, ch case ESP_MATTER_VAL_TYPE_NULLABLE_ENUM16: case ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP16: if (chip::app::NumericAttributeTraits::IsNullValue(m_attr_val.val.u16)) { - writer.PutNull(tag); + ReturnErrorOnFailure(writer.PutNull(tag)); break; } // Fall through @@ -201,7 +201,7 @@ CHIP_ERROR attribute_data_encode_buffer::Encode(chip::TLV::TLVWriter &writer, ch case ESP_MATTER_VAL_TYPE_NULLABLE_UINT32: case ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP32: if (chip::app::NumericAttributeTraits::IsNullValue(m_attr_val.val.u32)) { - writer.PutNull(tag); + ReturnErrorOnFailure(writer.PutNull(tag)); break; } // Fall through @@ -211,7 +211,7 @@ CHIP_ERROR attribute_data_encode_buffer::Encode(chip::TLV::TLVWriter &writer, ch break; case ESP_MATTER_VAL_TYPE_NULLABLE_UINT64: if (chip::app::NumericAttributeTraits::IsNullValue(m_attr_val.val.u64)) { - writer.PutNull(tag); + ReturnErrorOnFailure(writer.PutNull(tag)); break; } // Fall through @@ -220,7 +220,7 @@ CHIP_ERROR attribute_data_encode_buffer::Encode(chip::TLV::TLVWriter &writer, ch break; case ESP_MATTER_VAL_TYPE_NULLABLE_INT8: if (chip::app::NumericAttributeTraits::IsNullValue(m_attr_val.val.i8)) { - writer.PutNull(tag); + ReturnErrorOnFailure(writer.PutNull(tag)); break; } // Fall through @@ -229,7 +229,7 @@ CHIP_ERROR attribute_data_encode_buffer::Encode(chip::TLV::TLVWriter &writer, ch break; case ESP_MATTER_VAL_TYPE_NULLABLE_INT16: if (chip::app::NumericAttributeTraits::IsNullValue(m_attr_val.val.i16)) { - writer.PutNull(tag); + ReturnErrorOnFailure(writer.PutNull(tag)); break; } // Fall through @@ -238,7 +238,7 @@ CHIP_ERROR attribute_data_encode_buffer::Encode(chip::TLV::TLVWriter &writer, ch break; case ESP_MATTER_VAL_TYPE_NULLABLE_INT32: if (chip::app::NumericAttributeTraits::IsNullValue(m_attr_val.val.i32)) { - writer.PutNull(tag); + ReturnErrorOnFailure(writer.PutNull(tag)); break; } // Fall through @@ -247,7 +247,7 @@ CHIP_ERROR attribute_data_encode_buffer::Encode(chip::TLV::TLVWriter &writer, ch break; case ESP_MATTER_VAL_TYPE_NULLABLE_INT64: if (chip::app::NumericAttributeTraits::IsNullValue(m_attr_val.val.i64)) { - writer.PutNull(tag); + ReturnErrorOnFailure(writer.PutNull(tag)); break; } // Fall through @@ -256,7 +256,7 @@ CHIP_ERROR attribute_data_encode_buffer::Encode(chip::TLV::TLVWriter &writer, ch break; case ESP_MATTER_VAL_TYPE_NULLABLE_FLOAT: if (chip::app::NumericAttributeTraits::IsNullValue(m_attr_val.val.f)) { - writer.PutNull(tag); + ReturnErrorOnFailure(writer.PutNull(tag)); break; } // Fall through @@ -267,7 +267,7 @@ CHIP_ERROR attribute_data_encode_buffer::Encode(chip::TLV::TLVWriter &writer, ch case ESP_MATTER_VAL_TYPE_LONG_CHAR_STRING: { if (m_attr_val.val.a.b == nullptr && m_attr_val.val.a.s == (m_attr_val.type == ESP_MATTER_VAL_TYPE_CHAR_STRING ? 0xFF : 0xFFFF)) { - writer.PutNull(tag); + ReturnErrorOnFailure(writer.PutNull(tag)); break; } if (m_attr_val.val.a.b == nullptr) { @@ -281,7 +281,7 @@ CHIP_ERROR attribute_data_encode_buffer::Encode(chip::TLV::TLVWriter &writer, ch case ESP_MATTER_VAL_TYPE_LONG_OCTET_STRING: { if (m_attr_val.val.a.b == nullptr && m_attr_val.val.a.s == (m_attr_val.type == ESP_MATTER_VAL_TYPE_OCTET_STRING ? 0xFF : 0xFFFF)) { - writer.PutNull(tag); + ReturnErrorOnFailure(writer.PutNull(tag)); break; } ReturnErrorOnFailure(writer.PutBytes(tag, m_attr_val.val.a.b, m_attr_val.val.a.s)); diff --git a/components/esp_matter/data_model_provider/esp_matter_data_model_provider.cpp b/components/esp_matter/data_model_provider/esp_matter_data_model_provider.cpp index 57fa06bc1..7190d6c29 100644 --- a/components/esp_matter/data_model_provider/esp_matter_data_model_provider.cpp +++ b/components/esp_matter/data_model_provider/esp_matter_data_model_provider.cpp @@ -258,7 +258,7 @@ CHIP_ERROR provider::Startup(InteractionModelContext context) ep = endpoint::get_next(ep); } if (GetAttributePersistenceProvider() == nullptr) { - gDefaultAttributePersistence.Init(&Server::GetInstance().GetPersistentStorage()); + ReturnErrorOnFailure(gDefaultAttributePersistence.Init(&Server::GetInstance().GetPersistentStorage())); SetAttributePersistenceProvider(&gDefaultAttributePersistence); } return mRegistry.SetContext(ServerClusterContext{ diff --git a/components/esp_matter/data_model_provider/esp_matter_ember_stubs.cpp b/components/esp_matter/data_model_provider/esp_matter_ember_stubs.cpp index b0abf29ce..fc099fc26 100644 --- a/components/esp_matter/data_model_provider/esp_matter_ember_stubs.cpp +++ b/components/esp_matter/data_model_provider/esp_matter_ember_stubs.cpp @@ -734,6 +734,8 @@ EmberAfDefaultAttributeValue get_default_attr_value_from_val(esp_matter_attr_val namespace chip { namespace app { +// TODO: Remove EnabledEndpointsWithServerCluster when door-lock, power-source-configuration, and ota-requestor server +// is decoupled from ember. EnabledEndpointsWithServerCluster::EnabledEndpointsWithServerCluster(ClusterId clusterId) : mEndpointCount(esp_matter::endpoint::get_count(esp_matter::node::get())) , mClusterId(clusterId) @@ -743,7 +745,7 @@ EnabledEndpointsWithServerCluster::EnabledEndpointsWithServerCluster(ClusterId c EndpointId EnabledEndpointsWithServerCluster::operator*() const { - return emberAfEndpointFromIndex(mEndpointIndex); + return esp_matter::endpoint::get_id(get_endpoint_at_index(mEndpointIndex)); } EnabledEndpointsWithServerCluster &EnabledEndpointsWithServerCluster::operator++() @@ -766,181 +768,16 @@ void EnabledEndpointsWithServerCluster::EnsureMatchingEndpoint() } } -namespace Compatibility { -namespace Internal { - -// Remove AttributeBaseType() when scenes management cluster is decoupled from ember. -EmberAfAttributeType AttributeBaseType(EmberAfAttributeType type) -{ - switch (type) { - case ZCL_ACTION_ID_ATTRIBUTE_TYPE: // Action Id - case ZCL_FABRIC_IDX_ATTRIBUTE_TYPE: // Fabric Index - case ZCL_BITMAP8_ATTRIBUTE_TYPE: // 8-bit bitmap - case ZCL_ENUM8_ATTRIBUTE_TYPE: // 8-bit enumeration - case ZCL_STATUS_ATTRIBUTE_TYPE: // Status Code - case ZCL_PERCENT_ATTRIBUTE_TYPE: // Percentage - static_assert(std::is_same::value, - "chip::Percent is expected to be uint8_t, change this when necessary"); - return ZCL_INT8U_ATTRIBUTE_TYPE; - - case ZCL_ENDPOINT_NO_ATTRIBUTE_TYPE: // Endpoint Number - case ZCL_GROUP_ID_ATTRIBUTE_TYPE: // Group Id - case ZCL_VENDOR_ID_ATTRIBUTE_TYPE: // Vendor Id - case ZCL_ENUM16_ATTRIBUTE_TYPE: // 16-bit enumeration - case ZCL_BITMAP16_ATTRIBUTE_TYPE: // 16-bit bitmap - case ZCL_PERCENT100THS_ATTRIBUTE_TYPE: // 100ths of a percent - static_assert(std::is_same::value, - "chip::EndpointId is expected to be uint16_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::GroupId is expected to be uint16_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::Percent100ths is expected to be uint16_t, change this when necessary"); - return ZCL_INT16U_ATTRIBUTE_TYPE; - - case ZCL_CLUSTER_ID_ATTRIBUTE_TYPE: // Cluster Id - case ZCL_ATTRIB_ID_ATTRIBUTE_TYPE: // Attribute Id - case ZCL_FIELD_ID_ATTRIBUTE_TYPE: // Field Id - case ZCL_EVENT_ID_ATTRIBUTE_TYPE: // Event Id - case ZCL_COMMAND_ID_ATTRIBUTE_TYPE: // Command Id - case ZCL_TRANS_ID_ATTRIBUTE_TYPE: // Transaction Id - case ZCL_DEVTYPE_ID_ATTRIBUTE_TYPE: // Device Type Id - case ZCL_DATA_VER_ATTRIBUTE_TYPE: // Data Version - case ZCL_BITMAP32_ATTRIBUTE_TYPE: // 32-bit bitmap - case ZCL_EPOCH_S_ATTRIBUTE_TYPE: // Epoch Seconds - case ZCL_ELAPSED_S_ATTRIBUTE_TYPE: // Elapsed Seconds - static_assert(std::is_same::value, - "chip::Cluster is expected to be uint32_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::AttributeId is expected to be uint32_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::AttributeId is expected to be uint32_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::EventId is expected to be uint32_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::CommandId is expected to be uint32_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::TransactionId is expected to be uint32_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::DeviceTypeId is expected to be uint32_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::DataVersion is expected to be uint32_t, change this when necessary"); - return ZCL_INT32U_ATTRIBUTE_TYPE; - - case ZCL_AMPERAGE_MA_ATTRIBUTE_TYPE: // Amperage - case ZCL_ENERGY_MWH_ATTRIBUTE_TYPE: // Energy - case ZCL_ENERGY_MVAH_ATTRIBUTE_TYPE: // Apparent Energy - case ZCL_ENERGY_MVARH_ATTRIBUTE_TYPE: // Reactive Energy - case ZCL_POWER_MW_ATTRIBUTE_TYPE: // Power - case ZCL_POWER_MVA_ATTRIBUTE_TYPE: // Apparent Power - case ZCL_POWER_MVAR_ATTRIBUTE_TYPE: // Reactive Power - case ZCL_VOLTAGE_MV_ATTRIBUTE_TYPE: // Voltage - case ZCL_MONEY_ATTRIBUTE_TYPE: // Money - return ZCL_INT64S_ATTRIBUTE_TYPE; - - case ZCL_EVENT_NO_ATTRIBUTE_TYPE: // Event Number - case ZCL_FABRIC_ID_ATTRIBUTE_TYPE: // Fabric Id - case ZCL_NODE_ID_ATTRIBUTE_TYPE: // Node Id - case ZCL_BITMAP64_ATTRIBUTE_TYPE: // 64-bit bitmap - case ZCL_EPOCH_US_ATTRIBUTE_TYPE: // Epoch Microseconds - case ZCL_POSIX_MS_ATTRIBUTE_TYPE: // POSIX Milliseconds - case ZCL_SYSTIME_MS_ATTRIBUTE_TYPE: // System time Milliseconds - case ZCL_SYSTIME_US_ATTRIBUTE_TYPE: // System time Microseconds - static_assert(std::is_same::value, - "chip::EventNumber is expected to be uint64_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::FabricId is expected to be uint64_t, change this when necessary"); - static_assert(std::is_same::value, - "chip::NodeId is expected to be uint64_t, change this when necessary"); - return ZCL_INT64U_ATTRIBUTE_TYPE; - - case ZCL_TEMPERATURE_ATTRIBUTE_TYPE: // Temperature - return ZCL_INT16S_ATTRIBUTE_TYPE; - - default: - return type; - } -} - -} // namespace Internal -} // namespace Compatibility } // namespace app } // namespace chip // Override Ember functions -// TODO: Remove the emberAfEndpointIndexIsEnabled and emberAfEndpointCount functions when FabricTableImpl is decoupled -// from ember -chip::EndpointId emberAfEndpointFromIndex(uint16_t index) -{ - esp_matter::endpoint_t *ep = get_endpoint_at_index(index); - if (ep) { - return esp_matter::endpoint::get_id(ep); - } - return chip::kInvalidEndpointId; -} - -bool emberAfEndpointIndexIsEnabled(uint16_t index) -{ - esp_matter::endpoint_t *ep = get_endpoint_at_index(index); - if (ep) { - return esp_matter::endpoint::is_enabled(ep); - } - return false; -} - -uint16_t emberAfEndpointCount() -{ - return esp_matter::endpoint::get_count(esp_matter::node::get()); -} - -// TODO: Remove the emberAfGetClusterCountForEndpoint when scenes cluster is decoupled from ember -uint8_t emberAfGetClusterCountForEndpoint(chip::EndpointId endpoint) -{ - esp_matter::endpoint_t *ep = esp_matter::endpoint::get(endpoint); - if (ep) { - esp_matter::cluster_t *cluster = esp_matter::cluster::get_first(ep); - if (cluster) { - uint8_t count = 0; - while (cluster) { - count++; - cluster = esp_matter::cluster::get_next(cluster); - } - return count; - } - } - return 0; -} - -// TODO: Remove the emberAfGetClustersFromEndpoint function when scenes cluster is decoupled from ember -uint8_t emberAfGetClustersFromEndpoint(chip::EndpointId endpoint, chip::ClusterId *clusterList, uint8_t listLen, - bool server) -{ - esp_matter::endpoint_t *ep = esp_matter::endpoint::get(endpoint); - if (ep) { - esp_matter::cluster_t *cluster = esp_matter::cluster::get_first(ep); - if (cluster) { - uint8_t count = 0; - while (cluster) { - if ((server && (esp_matter::cluster::get_flags(cluster) & esp_matter::CLUSTER_FLAG_SERVER)) || - (!server && (esp_matter::cluster::get_flags(cluster) & esp_matter::CLUSTER_FLAG_CLIENT))) { - clusterList[count++] = esp_matter::cluster::get_id(cluster); - if (count >= listLen) { - break; - } - } - cluster = esp_matter::cluster::get_next(cluster); - } - return count; - } - } - return 0; -} - // TODO: Remove the emberAfGetClusterServerEndpointIndex function when laundry-dryer-controls, keypad-input, // door-lock, level-control, target-navigator, fan-control, occupancy-sensor, valve-configuration-and-control, // media-playback, content-launch, audio-output, power-source, application-basic, low-power, diagnostic-logs, -// scenes, color-control, channel, laundry-washer-controls, wake-on-lan, window-covering, content-control, -// dishwasher-alarm, on-off, media-input, application-launcher, account-login, thermostat, electrical-energy-measurement, +// color-control, channel, laundry-washer-controls, wake-on-lan, window-covering, content-control, dishwasher-alarm, +// on-off, media-input, application-launcher, account-login, thermostat, electrical-energy-measurement, // content-app-observer, and boolean-state-configuration clusters are decoupled from ember. uint16_t emberAfGetClusterServerEndpointIndex(chip::EndpointId endpoint, chip::ClusterId clusterId, uint16_t fixedClusterServerEndpointCount) diff --git a/components/esp_matter/esp_matter_client.cpp b/components/esp_matter/esp_matter_client.cpp index c0c5682e8..131fa2bf0 100644 --- a/components/esp_matter/esp_matter_client.cpp +++ b/components/esp_matter/esp_matter_client.cpp @@ -28,6 +28,7 @@ #include #include #include +#include "support/CodeUtils.h" #ifdef CONFIG_ESP_MATTER_ENABLE_MATTER_SERVER #include #endif @@ -186,7 +187,9 @@ static void __binding_manager_init(intptr_t arg) .mStorage = &server.GetPersistentStorage(), }; - chip::app::Clusters::Binding::Manager::GetInstance().Init(binding_init_params); + if (chip::app::Clusters::Binding::Manager::GetInstance().Init(binding_init_params) != CHIP_NO_ERROR) { + ESP_LOGE(TAG, "Failed to initialize Binding Manager"); + } chip::app::Clusters::Binding::Manager::GetInstance().RegisterBoundDeviceChangedHandler(esp_matter_command_client_binding_callback); chip::app::Clusters::Binding::Manager::GetInstance().RegisterBoundDeviceContextReleaseHandler(esp_matter_binding_context_release); } @@ -195,7 +198,9 @@ void binding_manager_init() { #ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL if (endpoint::get_cluster_count(chip::kInvalidEndpointId, Binding::Id, CLUSTER_FLAG_SERVER) > 0) { - chip::DeviceLayer::PlatformMgr().ScheduleWork(__binding_manager_init); + if (chip::DeviceLayer::PlatformMgr().ScheduleWork(__binding_manager_init) != CHIP_NO_ERROR) { + ESP_LOGE(TAG, "Failed to schedule work to initialize Binding Manager"); + } } #endif // CONFIG_ESP_MATTER_ENABLE_DATA_MODEL } @@ -248,7 +253,7 @@ esp_err_t send_request(void *ctx, peer_device_t *remote_device, const CommandPat timed_invoke_timeout_ms.HasValue()); VerifyOrReturnError(command_sender != nullptr, ESP_ERR_NO_MEM, ESP_LOGE(TAG, "No memory for command sender")); chip::app::CommandSender::AddRequestDataParameters add_request_data_params(timed_invoke_timeout_ms); - command_sender->AddRequestData(command_path, encodable, add_request_data_params); + VerifyOrReturnError(command_sender->AddRequestData(command_path, encodable, add_request_data_params) == CHIP_NO_ERROR, ESP_FAIL, ESP_LOGE(TAG, "Failed to add request data"));; VerifyOrReturnError(command_sender->SendCommandRequest(remote_device->GetSecureSession().Value(), response_timeout) == CHIP_NO_ERROR, ESP_FAIL, ESP_LOGE(TAG, "Failed to send command request")); (void)decoder.release(); (void)command_sender.release(); @@ -265,7 +270,7 @@ esp_err_t send_group_request(const uint8_t fabric_index, const CommandPathParams auto command_sender = chip::Platform::MakeUnique(nullptr, exchange_mgr); VerifyOrReturnError(command_sender != nullptr, ESP_ERR_NO_MEM, ESP_LOGE(TAG, "No memory for command sender")); chip::app::CommandSender::AddRequestDataParameters add_request_data_params; - command_sender->AddRequestData(command_path, encodeable, add_request_data_params); + VerifyOrReturnError(command_sender->AddRequestData(command_path, encodeable, add_request_data_params) == CHIP_NO_ERROR, ESP_FAIL, ESP_LOGE(TAG, "Failed to add request data")); VerifyOrReturnError(command_sender->SendGroupCommandRequest(SessionHandle(session)) == CHIP_NO_ERROR, ESP_FAIL, ESP_LOGE(TAG, "Failed to send command request")); return ESP_OK; } diff --git a/components/esp_matter/esp_matter_core.cpp b/components/esp_matter/esp_matter_core.cpp index 7b97e2e9c..40ec31371 100644 --- a/components/esp_matter/esp_matter_core.cpp +++ b/components/esp_matter/esp_matter_core.cpp @@ -185,7 +185,10 @@ static void esp_matter_chip_init_task(intptr_t context) chip::CommonCaseDeviceServerInitParams &initParams = s_server_init_params ? *s_server_init_params : defaultInitParams; - initParams.InitializeStaticResourcesBeforeServerInit(); + if (initParams.InitializeStaticResourcesBeforeServerInit() != CHIP_NO_ERROR) { + ESP_LOGE(TAG, "Failed to initialize static resources before server initialization"); + return; + } if (!initParams.appDelegate) { initParams.appDelegate = &s_app_delegate; } @@ -209,7 +212,9 @@ static void esp_matter_chip_init_task(intptr_t context) CHIP_CONFIG_MAX_GROUP_KEYS_PER_FABRIC); groupDataProvider.SetStorageDelegate(initParams.persistentStorageDelegate); groupDataProvider.SetSessionKeystore(initParams.sessionKeystore); - groupDataProvider.Init(); + if (groupDataProvider.Init() != CHIP_NO_ERROR) { + ESP_LOGE(TAG, "Failed to initialize group data provider"); + } initParams.groupDataProvider = &groupDataProvider; } #endif // CONFIG_ESP_MATTER_ENABLE_DATA_MODEL @@ -219,7 +224,10 @@ static void esp_matter_chip_init_task(intptr_t context) { ESP_LOGE(TAG, "Failed to add fabric delegate, err:%" CHIP_ERROR_FORMAT, ret.Format()); } - chip::Server::GetInstance().Init(initParams); + ret = chip::Server::GetInstance().Init(initParams); + if (ret != CHIP_NO_ERROR) { + ESP_LOGE(TAG, "Failed to init server instance, err:%" CHIP_ERROR_FORMAT, ret.Format()); + } #ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL if (endpoint::enable_all() != ESP_OK) { @@ -239,7 +247,9 @@ static void esp_matter_chip_init_task(intptr_t context) chip::Server::GetInstance().GetICDManager().Shutdown(); } #endif // CHIP_CONFIG_ENABLE_ICD_SERVER - PlatformMgr().ScheduleWork(deinit_ble_if_commissioned, reinterpret_cast(nullptr)); + if (PlatformMgr().ScheduleWork(deinit_ble_if_commissioned, reinterpret_cast(nullptr)) != CHIP_NO_ERROR) { + ESP_LOGE(TAG, "Schedule ble deinitialization fails"); + } xTaskNotifyGive(task_to_notify); } #endif // CONFIG_ESP_MATTER_ENABLE_MATTER_SERVER @@ -265,7 +275,9 @@ static void device_callback_internal(const ChipDeviceEvent * event, intptr_t arg case chip::DeviceLayer::DeviceEventType::kCommissioningComplete: ESP_LOGI(TAG, "Commissioning Complete"); - PlatformMgr().ScheduleWork(deinit_ble_if_commissioned, reinterpret_cast(nullptr)); + if (PlatformMgr().ScheduleWork(deinit_ble_if_commissioned, reinterpret_cast(nullptr)) != CHIP_NO_ERROR) { + ESP_LOGE(TAG, "Schedule ble deinitialization fails"); + } break; case chip::DeviceLayer::DeviceEventType::kCHIPoBLEConnectionClosed: @@ -318,13 +330,28 @@ static esp_err_t chip_init(event_callback_t callback, intptr_t callback_arg) ESP_LOGE(TAG, "Failed to launch Matter main task"); return ESP_FAIL; } - PlatformMgr().AddEventHandler(device_callback_internal, static_cast(NULL)); + if (PlatformMgr().AddEventHandler(device_callback_internal, static_cast(NULL)) != CHIP_NO_ERROR) { + (void)PlatformMgr().StopEventLoopTask(); + chip::Platform::MemoryShutdown(); + ESP_LOGE(TAG, "Failed to add internal device callback"); + return ESP_FAIL; + } if(callback) { - PlatformMgr().AddEventHandler(callback, callback_arg); + if (PlatformMgr().AddEventHandler(callback, callback_arg) != CHIP_NO_ERROR) { + (void)PlatformMgr().StopEventLoopTask(); + chip::Platform::MemoryShutdown(); + ESP_LOGE(TAG, "Failed to add user callback"); + return ESP_FAIL; + } } init_thread_stack_and_start_thread_task(); #if CONFIG_ESP_MATTER_ENABLE_MATTER_SERVER - PlatformMgr().ScheduleWork(esp_matter_chip_init_task, reinterpret_cast(xTaskGetCurrentTaskHandle())); + if (PlatformMgr().ScheduleWork(esp_matter_chip_init_task, reinterpret_cast(xTaskGetCurrentTaskHandle())) != CHIP_NO_ERROR) { + (void)PlatformMgr().StopEventLoopTask(); + chip::Platform::MemoryShutdown(); + ESP_LOGE(TAG, "Failed to schedule chip init task"); + return ESP_FAIL; + } // Wait for the matter stack to be initialized xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); #endif // CONFIG_ESP_MATTER_ENABLE_MATTER_SERVER diff --git a/components/esp_matter/esp_matter_icd_configuration.cpp b/components/esp_matter/esp_matter_icd_configuration.cpp index b26cfa597..8c03d9c3e 100644 --- a/components/esp_matter/esp_matter_icd_configuration.cpp +++ b/components/esp_matter/esp_matter_icd_configuration.cpp @@ -28,7 +28,7 @@ using namespace chip::System::Clock; static constexpr char *TAG = "icd"; namespace chip { -namespace Test { +namespace Testing { class ICDConfigurationDataTestAccess { // This class is a friend class for ICDConfigurationData, so it can access private members of ICDConfigurationData. public: @@ -39,7 +39,7 @@ public: static void SetSlowPollingInterval(Milliseconds32 pollInterval) { - ICDConfigurationData::GetInstance().SetSlowPollingInterval(pollInterval); + (void)ICDConfigurationData::GetInstance().SetSlowPollingInterval(pollInterval); if (pollInterval >= Milliseconds32(15000)) { // Active Threshold SHALL NOT be smaller than 5 seconds for LIT ICD. ICDConfigurationData::GetInstance().SetICDMode(ICDConfigurationData::ICDMode::LIT); @@ -84,10 +84,10 @@ static esp_err_t set_polling_intervals(std::optional fast_interval_ms, return ESP_ERR_INVALID_ARG; } #endif - chip::Test::ICDConfigurationDataTestAccess::SetSlowPollingInterval(Milliseconds32(slow_interval_ms.value())); + chip::Testing::ICDConfigurationDataTestAccess::SetSlowPollingInterval(Milliseconds32(slow_interval_ms.value())); } if (fast_interval_ms.has_value()) { - chip::Test::ICDConfigurationDataTestAccess::SetFastPollingInterval(Milliseconds32(fast_interval_ms.value())); + chip::Testing::ICDConfigurationDataTestAccess::SetFastPollingInterval(Milliseconds32(fast_interval_ms.value())); } return ESP_OK; } @@ -101,13 +101,13 @@ static esp_err_t set_mode_durations(std::optional active_mode_duration Seconds32 idle_mode_duration = idle_mode_duration_s.has_value() ? Seconds32(idle_mode_duration_s.value()) : chip::ICDConfigurationData::GetInstance().GetIdleModeDuration(); - return chip::Test::ICDConfigurationDataTestAccess::SetModeDurations(chip::MakeOptional(active_mode_duration), + return chip::Testing::ICDConfigurationDataTestAccess::SetModeDurations(chip::MakeOptional(active_mode_duration), chip::MakeOptional(idle_mode_duration)); } static esp_err_t set_active_threshold(uint32_t active_threshold_ms) { - return chip::Test::ICDConfigurationDataTestAccess::SetActiveThreshold(Milliseconds32(active_threshold_ms)); + return chip::Testing::ICDConfigurationDataTestAccess::SetActiveThreshold(Milliseconds32(active_threshold_ms)); } static bool s_enable_icd_server = CHIP_CONFIG_ENABLE_ICD_SERVER; diff --git a/components/esp_matter/esp_matter_ota.cpp b/components/esp_matter/esp_matter_ota.cpp index e733a7a5e..1e2b3bfdb 100644 --- a/components/esp_matter/esp_matter_ota.cpp +++ b/components/esp_matter/esp_matter_ota.cpp @@ -97,7 +97,10 @@ void esp_matter_ota_requestor_start(void) chip::SetRequestorInstance(&gRequestorCore); gRequestorStorage.Init(Server::GetInstance().GetPersistentStorage()); - gRequestorCore.Init(Server::GetInstance(), gRequestorStorage, *s_ota_requestor_impl.driver, gDownloader); + if (gRequestorCore.Init(Server::GetInstance(), gRequestorStorage, *s_ota_requestor_impl.driver, gDownloader) != CHIP_NO_ERROR) { + ESP_LOGE("OTARequestor", "Failed to init OTARequestor core"); + return; + } gImageProcessor.SetOTADownloader(&gDownloader); diff --git a/components/esp_matter/utils/json_to_tlv.cpp b/components/esp_matter/utils/json_to_tlv.cpp index d9263a45f..d40619636 100644 --- a/components/esp_matter/utils/json_to_tlv.cpp +++ b/components/esp_matter/utils/json_to_tlv.cpp @@ -21,6 +21,7 @@ #include #include +#include "support/CodeUtils.h" using namespace chip; using chip::TLV::TLVElementType; @@ -394,7 +395,7 @@ static esp_err_t encode_tlv_element(const cJSON *val, TLV::TLVWriter &writer, co for (size_t i = 0; i < array_size; ++i) { if ((err = encode_tlv_element(cJSON_GetArrayItem(val, i), writer, nested_element_ctx)) != ESP_OK) { ESP_LOGE(TAG, "Failed to encode"); - writer.EndContainer(container_type); + ReturnValueOnFailure(writer.EndContainer(container_type), ESP_FAIL); return err; } } @@ -423,7 +424,8 @@ static esp_err_t encode_tlv_element(const cJSON *val, TLV::TLVWriter &writer, co if ((err = encode_tlv_element(cJSON_GetObjectItem(val, element_array[element_idx].json_name), writer, element_array[element_idx])) != ESP_OK) { ESP_LOGE(TAG, "Failed to encode"); - writer.EndContainer(container_type); + // Ignore the return value of EndContainer() + (void)writer.EndContainer(container_type); return err; } } diff --git a/components/esp_matter/zap_common/app/ClusterCallbacks.h b/components/esp_matter/zap_common/app/ClusterCallbacks.h index 9bf7bac18..83fce8155 100644 --- a/components/esp_matter/zap_common/app/ClusterCallbacks.h +++ b/components/esp_matter/zap_common/app/ClusterCallbacks.h @@ -1,429 +1,434 @@ #pragma once #include +#include using chip::EndpointId; +using chip::app::ClusterShutdownType; void ESPMatterAccessControlClusterServerInitCallback(EndpointId endpoint); -void ESPMatterAccessControlClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterAccessControlClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterAccountLoginClusterServerInitCallback(EndpointId endpoint); -void ESPMatterAccountLoginClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterAccountLoginClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterActionsClusterServerInitCallback(EndpointId endpoint); -void ESPMatterActionsClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterActionsClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterActivatedCarbonFilterMonitoringClusterServerInitCallback(EndpointId endpoint); -void ESPMatterActivatedCarbonFilterMonitoringClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterActivatedCarbonFilterMonitoringClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterAdministratorCommissioningClusterServerInitCallback(EndpointId endpoint); -void ESPMatterAdministratorCommissioningClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterAdministratorCommissioningClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterAirQualityClusterServerInitCallback(EndpointId endpoint); -void ESPMatterAirQualityClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterAirQualityClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); + +void ESPMatterAmbientContextSensingClusterServerInitCallback(EndpointId endpoint); +void ESPMatterAmbientContextSensingClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterApplicationBasicClusterServerInitCallback(EndpointId endpoint); -void ESPMatterApplicationBasicClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterApplicationBasicClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterApplicationLauncherClusterServerInitCallback(EndpointId endpoint); -void ESPMatterApplicationLauncherClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterApplicationLauncherClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterAudioOutputClusterServerInitCallback(EndpointId endpoint); -void ESPMatterAudioOutputClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterAudioOutputClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterBallastConfigurationClusterServerInitCallback(EndpointId endpoint); -void ESPMatterBallastConfigurationClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterBallastConfigurationClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterBasicInformationClusterServerInitCallback(EndpointId endpoint); -void ESPMatterBasicInformationClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterBasicInformationClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterBindingClusterServerInitCallback(EndpointId endpoint); -void ESPMatterBindingClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterBindingClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterBooleanStateClusterServerInitCallback(EndpointId endpoint); -void ESPMatterBooleanStateClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterBooleanStateClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterBooleanStateConfigurationClusterServerInitCallback(EndpointId endpoint); -void ESPMatterBooleanStateConfigurationClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterBooleanStateConfigurationClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterBridgedDeviceBasicInformationClusterServerInitCallback(EndpointId endpoint); -void ESPMatterBridgedDeviceBasicInformationClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterBridgedDeviceBasicInformationClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterCameraAvSettingsUserLevelManagementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterCameraAvSettingsUserLevelManagementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterCameraAvSettingsUserLevelManagementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterCameraAvStreamManagementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterCameraAvStreamManagementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterCameraAvStreamManagementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterCarbonDioxideConcentrationMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterCarbonDioxideConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterCarbonDioxideConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterCarbonMonoxideConcentrationMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterCarbonMonoxideConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterCarbonMonoxideConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterChannelClusterServerInitCallback(EndpointId endpoint); -void ESPMatterChannelClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterChannelClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterChimeClusterServerInitCallback(EndpointId endpoint); -void ESPMatterChimeClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterChimeClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterClosureControlClusterServerInitCallback(EndpointId endpoint); -void ESPMatterClosureControlClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterClosureControlClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterClosureDimensionClusterServerInitCallback(EndpointId endpoint); -void ESPMatterClosureDimensionClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterClosureDimensionClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterColorControlClusterServerInitCallback(EndpointId endpoint); -void ESPMatterColorControlClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterColorControlClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterCommissionerControlClusterServerInitCallback(EndpointId endpoint); -void ESPMatterCommissionerControlClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterCommissionerControlClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterCommodityMeteringClusterServerInitCallback(EndpointId endpoint); -void ESPMatterCommodityMeteringClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterCommodityMeteringClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterCommodityPriceClusterServerInitCallback(EndpointId endpoint); -void ESPMatterCommodityPriceClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterCommodityPriceClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterCommodityTariffClusterServerInitCallback(EndpointId endpoint); -void ESPMatterCommodityTariffClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterCommodityTariffClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterContentAppObserverClusterServerInitCallback(EndpointId endpoint); -void ESPMatterContentAppObserverClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterContentAppObserverClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterContentControlClusterServerInitCallback(EndpointId endpoint); -void ESPMatterContentControlClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterContentControlClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterContentLauncherClusterServerInitCallback(EndpointId endpoint); -void ESPMatterContentLauncherClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterContentLauncherClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterDescriptorClusterServerInitCallback(EndpointId endpoint); -void ESPMatterDescriptorClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterDescriptorClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterDeviceEnergyManagementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterDeviceEnergyManagementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterDeviceEnergyManagementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterDeviceEnergyManagementModeClusterServerInitCallback(EndpointId endpoint); -void ESPMatterDeviceEnergyManagementModeClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterDeviceEnergyManagementModeClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterDiagnosticLogsClusterServerInitCallback(EndpointId endpoint); -void ESPMatterDiagnosticLogsClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterDiagnosticLogsClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterDishwasherAlarmClusterServerInitCallback(EndpointId endpoint); -void ESPMatterDishwasherAlarmClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterDishwasherAlarmClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterDishwasherModeClusterServerInitCallback(EndpointId endpoint); -void ESPMatterDishwasherModeClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterDishwasherModeClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterDoorLockClusterServerInitCallback(EndpointId endpoint); -void ESPMatterDoorLockClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterDoorLockClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterEcosystemInformationClusterServerInitCallback(EndpointId endpoint); -void ESPMatterEcosystemInformationClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterEcosystemInformationClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterElectricalEnergyMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterElectricalEnergyMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterElectricalEnergyMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterElectricalGridConditionsClusterServerInitCallback(EndpointId endpoint); -void ESPMatterElectricalGridConditionsClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterElectricalGridConditionsClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterElectricalPowerMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterElectricalPowerMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterElectricalPowerMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterEnergyEvseClusterServerInitCallback(EndpointId endpoint); -void ESPMatterEnergyEvseClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterEnergyEvseClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterEnergyEvseModeClusterServerInitCallback(EndpointId endpoint); -void ESPMatterEnergyEvseModeClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterEnergyEvseModeClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterEnergyPreferenceClusterServerInitCallback(EndpointId endpoint); -void ESPMatterEnergyPreferenceClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterEnergyPreferenceClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterEthernetNetworkDiagnosticsClusterServerInitCallback(EndpointId endpoint); -void ESPMatterEthernetNetworkDiagnosticsClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterEthernetNetworkDiagnosticsClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterFanControlClusterServerInitCallback(EndpointId endpoint); -void ESPMatterFanControlClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterFanControlClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterFaultInjectionClusterServerInitCallback(EndpointId endpoint); -void ESPMatterFaultInjectionClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterFaultInjectionClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterFixedLabelClusterServerInitCallback(EndpointId endpoint); -void ESPMatterFixedLabelClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterFixedLabelClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterFlowMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterFlowMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterFlowMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterFormaldehydeConcentrationMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterFormaldehydeConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterFormaldehydeConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterGeneralCommissioningClusterServerInitCallback(EndpointId endpoint); -void ESPMatterGeneralCommissioningClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterGeneralCommissioningClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterGeneralDiagnosticsClusterServerInitCallback(EndpointId endpoint); -void ESPMatterGeneralDiagnosticsClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterGeneralDiagnosticsClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterGroupKeyManagementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterGroupKeyManagementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterGroupKeyManagementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterGroupcastClusterServerInitCallback(EndpointId endpoint); -void ESPMatterGroupcastClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterGroupcastClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterGroupsClusterServerInitCallback(EndpointId endpoint); -void ESPMatterGroupsClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterGroupsClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterHepaFilterMonitoringClusterServerInitCallback(EndpointId endpoint); -void ESPMatterHepaFilterMonitoringClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterHepaFilterMonitoringClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterIcdManagementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterIcdManagementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterIcdManagementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterIdentifyClusterServerInitCallback(EndpointId endpoint); -void ESPMatterIdentifyClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterIdentifyClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterIlluminanceMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterIlluminanceMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterIlluminanceMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterJointFabricAdministratorClusterServerInitCallback(EndpointId endpoint); -void ESPMatterJointFabricAdministratorClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterJointFabricAdministratorClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterJointFabricDatastoreClusterServerInitCallback(EndpointId endpoint); -void ESPMatterJointFabricDatastoreClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterJointFabricDatastoreClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterKeypadInputClusterServerInitCallback(EndpointId endpoint); -void ESPMatterKeypadInputClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterKeypadInputClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterLaundryDryerControlsClusterServerInitCallback(EndpointId endpoint); -void ESPMatterLaundryDryerControlsClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterLaundryDryerControlsClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterLaundryWasherControlsClusterServerInitCallback(EndpointId endpoint); -void ESPMatterLaundryWasherControlsClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterLaundryWasherControlsClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterLaundryWasherModeClusterServerInitCallback(EndpointId endpoint); -void ESPMatterLaundryWasherModeClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterLaundryWasherModeClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterLevelControlClusterServerInitCallback(EndpointId endpoint); -void ESPMatterLevelControlClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterLevelControlClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterLocalizationConfigurationClusterServerInitCallback(EndpointId endpoint); -void ESPMatterLocalizationConfigurationClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterLocalizationConfigurationClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterLowPowerClusterServerInitCallback(EndpointId endpoint); -void ESPMatterLowPowerClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterLowPowerClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterMediaInputClusterServerInitCallback(EndpointId endpoint); -void ESPMatterMediaInputClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterMediaInputClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterMediaPlaybackClusterServerInitCallback(EndpointId endpoint); -void ESPMatterMediaPlaybackClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterMediaPlaybackClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterMessagesClusterServerInitCallback(EndpointId endpoint); -void ESPMatterMessagesClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterMessagesClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterMeterIdentificationClusterServerInitCallback(EndpointId endpoint); -void ESPMatterMeterIdentificationClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterMeterIdentificationClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterMicrowaveOvenControlClusterServerInitCallback(EndpointId endpoint); -void ESPMatterMicrowaveOvenControlClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterMicrowaveOvenControlClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterMicrowaveOvenModeClusterServerInitCallback(EndpointId endpoint); -void ESPMatterMicrowaveOvenModeClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterMicrowaveOvenModeClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterModeSelectClusterServerInitCallback(EndpointId endpoint); -void ESPMatterModeSelectClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterModeSelectClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterNetworkCommissioningClusterServerInitCallback(EndpointId endpoint); -void ESPMatterNetworkCommissioningClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterNetworkCommissioningClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterNitrogenDioxideConcentrationMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterNitrogenDioxideConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterNitrogenDioxideConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterOccupancySensingClusterServerInitCallback(EndpointId endpoint); -void ESPMatterOccupancySensingClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterOccupancySensingClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterOnOffClusterServerInitCallback(EndpointId endpoint); -void ESPMatterOnOffClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterOnOffClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterOperationalCredentialsClusterServerInitCallback(EndpointId endpoint); -void ESPMatterOperationalCredentialsClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterOperationalCredentialsClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterOperationalStateClusterServerInitCallback(EndpointId endpoint); -void ESPMatterOperationalStateClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterOperationalStateClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterOtaSoftwareUpdateProviderClusterServerInitCallback(EndpointId endpoint); -void ESPMatterOtaSoftwareUpdateProviderClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterOtaSoftwareUpdateProviderClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterOtaSoftwareUpdateRequestorClusterServerInitCallback(EndpointId endpoint); -void ESPMatterOtaSoftwareUpdateRequestorClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterOtaSoftwareUpdateRequestorClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterOvenCavityOperationalStateClusterServerInitCallback(EndpointId endpoint); -void ESPMatterOvenCavityOperationalStateClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterOvenCavityOperationalStateClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterOvenModeClusterServerInitCallback(EndpointId endpoint); -void ESPMatterOvenModeClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterOvenModeClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterOzoneConcentrationMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterOzoneConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterOzoneConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterPm10ConcentrationMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterPm10ConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterPm10ConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterPm1ConcentrationMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterPm1ConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterPm1ConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterPm25ConcentrationMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterPm25ConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterPm25ConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterPowerSourceClusterServerInitCallback(EndpointId endpoint); -void ESPMatterPowerSourceClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterPowerSourceClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterPowerSourceConfigurationClusterServerInitCallback(EndpointId endpoint); -void ESPMatterPowerSourceConfigurationClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterPowerSourceConfigurationClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterPowerTopologyClusterServerInitCallback(EndpointId endpoint); -void ESPMatterPowerTopologyClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterPowerTopologyClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterPressureMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterPressureMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterPressureMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterProxyConfigurationClusterServerInitCallback(EndpointId endpoint); -void ESPMatterProxyConfigurationClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterProxyConfigurationClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterProxyDiscoveryClusterServerInitCallback(EndpointId endpoint); -void ESPMatterProxyDiscoveryClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterProxyDiscoveryClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterProxyValidClusterServerInitCallback(EndpointId endpoint); -void ESPMatterProxyValidClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterProxyValidClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterPulseWidthModulationClusterServerInitCallback(EndpointId endpoint); -void ESPMatterPulseWidthModulationClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterPulseWidthModulationClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterPumpConfigurationAndControlClusterServerInitCallback(EndpointId endpoint); -void ESPMatterPumpConfigurationAndControlClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterPumpConfigurationAndControlClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterPushAvStreamTransportClusterServerInitCallback(EndpointId endpoint); -void ESPMatterPushAvStreamTransportClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterPushAvStreamTransportClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterRadonConcentrationMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterRadonConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterRadonConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterRefrigeratorAlarmClusterServerInitCallback(EndpointId endpoint); -void ESPMatterRefrigeratorAlarmClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterRefrigeratorAlarmClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterRefrigeratorAndTemperatureControlledCabinetModeClusterServerInitCallback(EndpointId endpoint); -void ESPMatterRefrigeratorAndTemperatureControlledCabinetModeClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterRefrigeratorAndTemperatureControlledCabinetModeClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterRelativeHumidityMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterRelativeHumidityMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterRelativeHumidityMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterRvcCleanModeClusterServerInitCallback(EndpointId endpoint); -void ESPMatterRvcCleanModeClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterRvcCleanModeClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterRvcOperationalStateClusterServerInitCallback(EndpointId endpoint); -void ESPMatterRvcOperationalStateClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterRvcOperationalStateClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterRvcRunModeClusterServerInitCallback(EndpointId endpoint); -void ESPMatterRvcRunModeClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterRvcRunModeClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterSampleMeiClusterServerInitCallback(EndpointId endpoint); -void ESPMatterSampleMeiClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterSampleMeiClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterScenesManagementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterScenesManagementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterScenesManagementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterServiceAreaClusterServerInitCallback(EndpointId endpoint); -void ESPMatterServiceAreaClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterServiceAreaClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterSmokeCoAlarmClusterServerInitCallback(EndpointId endpoint); -void ESPMatterSmokeCoAlarmClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterSmokeCoAlarmClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterSoftwareDiagnosticsClusterServerInitCallback(EndpointId endpoint); -void ESPMatterSoftwareDiagnosticsClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterSoftwareDiagnosticsClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterSoilMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterSoilMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterSoilMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterSwitchClusterServerInitCallback(EndpointId endpoint); -void ESPMatterSwitchClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterSwitchClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterTargetNavigatorClusterServerInitCallback(EndpointId endpoint); -void ESPMatterTargetNavigatorClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterTargetNavigatorClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterTemperatureControlClusterServerInitCallback(EndpointId endpoint); -void ESPMatterTemperatureControlClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterTemperatureControlClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterTemperatureMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterTemperatureMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterTemperatureMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterThermostatClusterServerInitCallback(EndpointId endpoint); -void ESPMatterThermostatClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterThermostatClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterThermostatUserInterfaceConfigurationClusterServerInitCallback(EndpointId endpoint); -void ESPMatterThermostatUserInterfaceConfigurationClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterThermostatUserInterfaceConfigurationClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterThreadBorderRouterManagementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterThreadBorderRouterManagementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterThreadBorderRouterManagementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterThreadNetworkDiagnosticsClusterServerInitCallback(EndpointId endpoint); -void ESPMatterThreadNetworkDiagnosticsClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterThreadNetworkDiagnosticsClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterThreadNetworkDirectoryClusterServerInitCallback(EndpointId endpoint); -void ESPMatterThreadNetworkDirectoryClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterThreadNetworkDirectoryClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterTimeFormatLocalizationClusterServerInitCallback(EndpointId endpoint); -void ESPMatterTimeFormatLocalizationClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterTimeFormatLocalizationClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterTimeSynchronizationClusterServerInitCallback(EndpointId endpoint); -void ESPMatterTimeSynchronizationClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterTimeSynchronizationClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterTlsCertificateManagementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterTlsCertificateManagementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterTlsCertificateManagementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterTlsClientManagementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterTlsClientManagementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterTlsClientManagementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterTotalVolatileOrganicCompoundsConcentrationMeasurementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterTotalVolatileOrganicCompoundsConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterTotalVolatileOrganicCompoundsConcentrationMeasurementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterUnitLocalizationClusterServerInitCallback(EndpointId endpoint); -void ESPMatterUnitLocalizationClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterUnitLocalizationClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterUnitTestingClusterServerInitCallback(EndpointId endpoint); -void ESPMatterUnitTestingClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterUnitTestingClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterUserLabelClusterServerInitCallback(EndpointId endpoint); -void ESPMatterUserLabelClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterUserLabelClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterValveConfigurationAndControlClusterServerInitCallback(EndpointId endpoint); -void ESPMatterValveConfigurationAndControlClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterValveConfigurationAndControlClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterWakeOnLanClusterServerInitCallback(EndpointId endpoint); -void ESPMatterWakeOnLanClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterWakeOnLanClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterWaterHeaterManagementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterWaterHeaterManagementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterWaterHeaterManagementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterWaterHeaterModeClusterServerInitCallback(EndpointId endpoint); -void ESPMatterWaterHeaterModeClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterWaterHeaterModeClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterWaterTankLevelMonitoringClusterServerInitCallback(EndpointId endpoint); -void ESPMatterWaterTankLevelMonitoringClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterWaterTankLevelMonitoringClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterWebRTCTransportProviderClusterServerInitCallback(EndpointId endpoint); -void ESPMatterWebRTCTransportProviderClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterWebRTCTransportProviderClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterWebRTCTransportRequestorClusterServerInitCallback(EndpointId endpoint); -void ESPMatterWebRTCTransportRequestorClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterWebRTCTransportRequestorClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterWiFiNetworkDiagnosticsClusterServerInitCallback(EndpointId endpoint); -void ESPMatterWiFiNetworkDiagnosticsClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterWiFiNetworkDiagnosticsClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterWiFiNetworkManagementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterWiFiNetworkManagementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterWiFiNetworkManagementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterWindowCoveringClusterServerInitCallback(EndpointId endpoint); -void ESPMatterWindowCoveringClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterWindowCoveringClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); void ESPMatterZoneManagementClusterServerInitCallback(EndpointId endpoint); -void ESPMatterZoneManagementClusterServerShutdownCallback(EndpointId endpoint); +void ESPMatterZoneManagementClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType); diff --git a/components/esp_matter/zap_common/app/PluginApplicationCallbacks.h b/components/esp_matter/zap_common/app/PluginApplicationCallbacks.h index 883b88aee..ae17be304 100644 --- a/components/esp_matter/zap_common/app/PluginApplicationCallbacks.h +++ b/components/esp_matter/zap_common/app/PluginApplicationCallbacks.h @@ -5,6 +5,7 @@ void MatterActionsPluginServerInitCallback(); void MatterActivatedCarbonFilterMonitoringPluginServerInitCallback(); void MatterAdministratorCommissioningPluginServerInitCallback(); void MatterAirQualityPluginServerInitCallback(); +void MatterAmbientContextSensingPluginServerInitCallback(); void MatterApplicationBasicPluginServerInitCallback(); void MatterApplicationLauncherPluginServerInitCallback(); void MatterAudioOutputPluginServerInitCallback(); diff --git a/components/esp_matter/zap_common/generate_zap_common_files.py b/components/esp_matter/zap_common/generate_zap_common_files.py index 876e53f1c..0f3afcf31 100755 --- a/components/esp_matter/zap_common/generate_zap_common_files.py +++ b/components/esp_matter/zap_common/generate_zap_common_files.py @@ -113,17 +113,20 @@ def generate_plugin_application_callbacks_h(xml_files, output_dir): header_file.write('void Matter{}PluginServerInitCallback();\n'.format( format_cluster_name(get_cluster_name(cluster)))) + def generate_cluster_callbacks_h(xml_files, output_dir): with open(os.path.join(output_dir, 'app/ClusterCallbacks.h'), 'w') as header_file: header_file.write('#pragma once\n\n') - header_file.write('#include \n\n') - header_file.write('using chip::EndpointId;\n\n') + header_file.write('#include \n') + header_file.write('#include \n\n') + header_file.write('using chip::EndpointId;\n') + header_file.write('using chip::app::ClusterShutdownType;\n\n') clusters = get_clusters_from_xml_files(xml_files) clusters.sort(key=get_formatted_cluster_name) for cluster in clusters: header_file.write('void ESPMatter{}ClusterServerInitCallback(EndpointId endpoint);\n'.format( format_cluster_name(get_cluster_name(cluster)))) - header_file.write('void ESPMatter{}ClusterServerShutdownCallback(EndpointId endpoint);\n\n'.format( + header_file.write('void ESPMatter{}ClusterServerShutdownCallback(EndpointId endpoint, ClusterShutdownType shutdownType);\n\n'.format( format_cluster_name(get_cluster_name(cluster)))) diff --git a/components/esp_matter/zap_common/zap-generated/access.h b/components/esp_matter/zap_common/zap-generated/access.h index 1f0664bd7..fea501a16 100644 --- a/components/esp_matter/zap_common/zap-generated/access.h +++ b/components/esp_matter/zap_common/zap-generated/access.h @@ -320,6 +320,7 @@ 0x00000406, /* Cluster: Occupancy Sensing, Attribute: PhysicalContactOccupiedToUnoccupiedDelay, Privilege: manage */ \ 0x00000406, /* Cluster: Occupancy Sensing, Attribute: PhysicalContactUnoccupiedToOccupiedDelay, Privilege: manage */ \ 0x00000406, /* Cluster: Occupancy Sensing, Attribute: PhysicalContactUnoccupiedToOccupiedThreshold, Privilege: manage */ \ + 0x00000431, /* Cluster: Ambient Context Sensing, Attribute: HoldTime, Privilege: manage */ \ 0x00000453, /* Cluster: Thread Network Directory, Attribute: PreferredExtendedPanID, Privilege: manage */ \ 0x00000551, /* Cluster: Camera AV Stream Management, Attribute: HDRModeEnabled, Privilege: manage */ \ 0x00000551, /* Cluster: Camera AV Stream Management, Attribute: NightVision, Privilege: manage */ \ @@ -435,6 +436,7 @@ 0x00000030, /* Cluster: Occupancy Sensing, Attribute: PhysicalContactOccupiedToUnoccupiedDelay, Privilege: manage */ \ 0x00000031, /* Cluster: Occupancy Sensing, Attribute: PhysicalContactUnoccupiedToOccupiedDelay, Privilege: manage */ \ 0x00000032, /* Cluster: Occupancy Sensing, Attribute: PhysicalContactUnoccupiedToOccupiedThreshold, Privilege: manage */ \ + 0x00000009, /* Cluster: Ambient Context Sensing, Attribute: HoldTime, Privilege: manage */ \ 0x00000000, /* Cluster: Thread Network Directory, Attribute: PreferredExtendedPanID, Privilege: manage */ \ 0x0000000D, /* Cluster: Camera AV Stream Management, Attribute: HDRModeEnabled, Privilege: manage */ \ 0x00000016, /* Cluster: Camera AV Stream Management, Attribute: NightVision, Privilege: manage */ \ @@ -550,6 +552,7 @@ chip::Access::Privilege::kManage, /* Cluster: Occupancy Sensing, Attribute: PhysicalContactOccupiedToUnoccupiedDelay, Privilege: manage */ \ chip::Access::Privilege::kManage, /* Cluster: Occupancy Sensing, Attribute: PhysicalContactUnoccupiedToOccupiedDelay, Privilege: manage */ \ chip::Access::Privilege::kManage, /* Cluster: Occupancy Sensing, Attribute: PhysicalContactUnoccupiedToOccupiedThreshold, Privilege: manage */ \ + chip::Access::Privilege::kManage, /* Cluster: Ambient Context Sensing, Attribute: HoldTime, Privilege: manage */ \ chip::Access::Privilege::kManage, /* Cluster: Thread Network Directory, Attribute: PreferredExtendedPanID, Privilege: manage */ \ chip::Access::Privilege::kManage, /* Cluster: Camera AV Stream Management, Attribute: HDRModeEnabled, Privilege: manage */ \ chip::Access::Privilege::kManage, /* Cluster: Camera AV Stream Management, Attribute: NightVision, Privilege: manage */ \ diff --git a/components/esp_matter_console/esp_matter_console_attribute.cpp b/components/esp_matter_console/esp_matter_console_attribute.cpp index 422001d04..99d5d9a73 100644 --- a/components/esp_matter_console/esp_matter_console_attribute.cpp +++ b/components/esp_matter_console/esp_matter_console_attribute.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #define TAG "attribute_console" diff --git a/connectedhomeip/connectedhomeip b/connectedhomeip/connectedhomeip index cf84d0360..8f943388a 160000 --- a/connectedhomeip/connectedhomeip +++ b/connectedhomeip/connectedhomeip @@ -1 +1 @@ -Subproject commit cf84d0360c48dbc194c48b47b09169f302a9745b +Subproject commit 8f943388af4d12dc5c484eae21b22723e03c3616 diff --git a/examples/all_device_types_app/main/electrical_measurement/electrical_measurement.cpp b/examples/all_device_types_app/main/electrical_measurement/electrical_measurement.cpp index e270131e7..7810fd932 100644 --- a/examples/all_device_types_app/main/electrical_measurement/electrical_measurement.cpp +++ b/examples/all_device_types_app/main/electrical_measurement/electrical_measurement.cpp @@ -12,7 +12,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -31,13 +32,10 @@ using namespace chip::app::Clusters::ElectricalPowerMeasurement::Structs; static const char *TAG = "electrical_measurement"; -// A pointer to store our attribute access object -static std::unique_ptr gEEMAttrAccess; - // Global pointer to our ElectricalPowerMeasurementDelegate static std::unique_ptr gEPMDelegate; // Global pointer to our ElectricalPowerMeasurementInstance -static std::unique_ptr gEPMInstance; +static std::unique_ptr gEPMInstance; CHIP_ERROR PowerTopology::PowerTopologyDelegate::GetAvailableEndpointAtIndex(size_t index, EndpointId & endpointId) { @@ -116,21 +114,11 @@ esp_err_t electrical_measurement_example(uint16_t endpoint_id) ESP_LOGI(TAG, "Initializing Electrical Energy Measurement cluster for endpoint %d", endpoint_id); // First ensure we don't initialize it twice - if (gEEMAttrAccess) { + if (GetClusterInstance(endpoint_id)) { ESP_LOGI(TAG, "Electrical Energy Measurement cluster already initialized"); return send_energy_measurement_events(endpoint_id); } - // Create the attribute access instance with the desired features - gEEMAttrAccess = std::make_unique( - BitMask( - ElectricalEnergyMeasurement::Feature::kImportedEnergy, - ElectricalEnergyMeasurement::Feature::kExportedEnergy, - ElectricalEnergyMeasurement::Feature::kCumulativeEnergy, - ElectricalEnergyMeasurement::Feature::kPeriodicEnergy), - BitMask( - ElectricalEnergyMeasurement::OptionalAttributes::kOptionalAttributeCumulativeEnergyReset)); - // Create accuracy ranges for energy measurements MeasurementAccuracyRangeStruct::Type energyAccuracyRanges[] = { { @@ -159,10 +147,7 @@ esp_err_t electrical_measurement_example(uint16_t endpoint_id) }; // Initialize and set values if attribute access was created successfully - if (gEEMAttrAccess) { - ESP_LOGI(TAG, "Initializing electrical energy measurement attribute access"); - gEEMAttrAccess->Init(); - + if (GetClusterInstance(endpoint_id)) { // Set accuracy and reset information CHIP_ERROR err = SetMeasurementAccuracy(endpoint_id, accuracy); if (err != CHIP_NO_ERROR) { diff --git a/examples/all_device_types_app/main/electrical_measurement/electrical_measurement.h b/examples/all_device_types_app/main/electrical_measurement/electrical_measurement.h index a16004f1a..d040d1374 100644 --- a/examples/all_device_types_app/main/electrical_measurement/electrical_measurement.h +++ b/examples/all_device_types_app/main/electrical_measurement/electrical_measurement.h @@ -9,7 +9,7 @@ #pragma once #include -#include +#include #include #include diff --git a/examples/camera/main/camera-device.cpp b/examples/camera/main/camera-device.cpp index da54dc7a9..36d53b5b2 100644 --- a/examples/camera/main/camera-device.cpp +++ b/examples/camera/main/camera-device.cpp @@ -1,10 +1,21 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * + * 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. + */ #include "camera-device.h" -#include -#include -#include #include -#include // For PATH_MAX -#include using namespace chip::app::Clusters; using namespace chip::app::Clusters::CameraAvStreamManagement; @@ -12,597 +23,618 @@ using namespace chip::app::Clusters::WebRTCTransportProvider; using namespace Camera; -CameraDevice::CameraDevice() { - // Set the CameraHALInterface in CameraAVStreamManager - mCameraAVStreamManager.SetCameraDeviceHAL(this); +CameraDevice::CameraDevice() +{ + // Set the CameraHALInterface in CameraAVStreamManager + mCameraAVStreamManager.SetCameraDeviceHAL(this); - // Set the CameraDevice interface in WebRTCManager - mWebRTCProviderManager.SetCameraDevice(this); + // Set the CameraDevice interface in WebRTCManager + mWebRTCProviderManager.SetCameraDevice(this); } CameraDevice::~CameraDevice() {} -void CameraDevice::Init() { - InitializeCameraDevice(); - InitializeStreams(); - mWebRTCProviderManager.Init(); +void CameraDevice::Init() +{ + InitializeCameraDevice(); + InitializeStreams(); + mWebRTCProviderManager.Init(); } -CameraError CameraDevice::InitializeCameraDevice() { - return CameraError::SUCCESS; +CameraError CameraDevice::InitializeCameraDevice() +{ + return CameraError::SUCCESS; } -CameraError CameraDevice::InitializeStreams() { - InitializeVideoStreams(); - InitializeAudioStreams(); - InitializeSnapshotStreams(); +CameraError CameraDevice::InitializeStreams() +{ + InitializeVideoStreams(); + InitializeAudioStreams(); + InitializeSnapshotStreams(); - return CameraError::SUCCESS; + return CameraError::SUCCESS; } // Find the closest allocated snapshot stream with resolution >= requested, or // closest possible -bool CameraDevice::MatchClosestSnapshotParams( - const VideoResolutionStruct &requested, - VideoResolutionStruct &matchedResolution, ImageCodecEnum &matchedCodec) { - int64_t requestedPixels = - static_cast(requested.width) * requested.height; - int64_t bestDiff = std::numeric_limits::max(); - int64_t bestGEQDiff = std::numeric_limits::max(); +bool CameraDevice::MatchClosestSnapshotParams(const VideoResolutionStruct & requested, VideoResolutionStruct & matchedResolution, + ImageCodecEnum & matchedCodec) +{ + int64_t requestedPixels = static_cast(requested.width) * requested.height; + int64_t bestDiff = std::numeric_limits::max(); + int64_t bestGEQDiff = std::numeric_limits::max(); - const SnapshotStream *bestStream = nullptr; - const SnapshotStream *bestGEQStream = nullptr; + const SnapshotStream * bestStream = nullptr; + const SnapshotStream * bestGEQStream = nullptr; - for (const auto &stream : mSnapshotStreams) { - int64_t streamPixels = - static_cast(stream.snapshotStreamParams.minResolution.width) * - stream.snapshotStreamParams.minResolution.height; - int64_t diff = streamPixels - requestedPixels; - int64_t absDiff = std::abs(diff); + for (const auto & stream : mSnapshotStreams) + { + int64_t streamPixels = static_cast(stream.snapshotStreamParams.minResolution.width) * + stream.snapshotStreamParams.minResolution.height; + int64_t diff = streamPixels - requestedPixels; + int64_t absDiff = std::abs(diff); - // Candidate 1: First stream with resolution >= requested - if (diff >= 0 && diff < bestGEQDiff) { - bestGEQDiff = diff; - bestGEQStream = &stream; + // Candidate 1: First stream with resolution >= requested + if (diff >= 0 && diff < bestGEQDiff) + { + bestGEQDiff = diff; + bestGEQStream = &stream; + } + + // Candidate 2: Closest stream (absolute difference) + if (absDiff < bestDiff) + { + bestDiff = absDiff; + bestStream = &stream; + } } - // Candidate 2: Closest stream (absolute difference) - if (absDiff < bestDiff) { - bestDiff = absDiff; - bestStream = &stream; + const SnapshotStream * chosen = bestGEQStream ? bestGEQStream : bestStream; + if (chosen) + { + matchedResolution = chosen->snapshotStreamParams.minResolution; + matchedCodec = chosen->snapshotStreamParams.imageCodec; + return true; } - } - - const SnapshotStream *chosen = bestGEQStream ? bestGEQStream : bestStream; - if (chosen) { - matchedResolution = chosen->snapshotStreamParams.minResolution; - matchedCodec = chosen->snapshotStreamParams.imageCodec; - return true; - } - return false; + return false; } -CameraError CameraDevice::CaptureSnapshot( - const chip::app::DataModel::Nullable streamID, - const VideoResolutionStruct &resolution, ImageSnapshot &outImageSnapshot) { - VideoResolutionStruct matchedRes; - ImageCodecEnum matchedCodec; +CameraError CameraDevice::CaptureSnapshot(const chip::app::DataModel::Nullable streamID, + const VideoResolutionStruct & resolution, ImageSnapshot & outImageSnapshot) +{ + VideoResolutionStruct matchedRes; + ImageCodecEnum matchedCodec; - if (streamID.IsNull()) { - if (!MatchClosestSnapshotParams(resolution, matchedRes, matchedCodec)) { - ChipLogError( - Camera, - "No matching snapshot stream found for requested resolution %ux%u", - resolution.width, resolution.height); - return CameraError::ERROR_CAPTURE_SNAPSHOT_FAILED; + if (streamID.IsNull()) + { + if (!MatchClosestSnapshotParams(resolution, matchedRes, matchedCodec)) + { + ChipLogError(Camera, "No matching snapshot stream found for requested resolution %ux%u", resolution.width, + resolution.height); + return CameraError::ERROR_CAPTURE_SNAPSHOT_FAILED; + } } - } else { - uint16_t streamId = streamID.Value(); - auto it = std::find_if(mSnapshotStreams.begin(), mSnapshotStreams.end(), - [streamId](const SnapshotStream &s) { - return s.snapshotStreamParams.snapshotStreamID == - streamId; - }); - if (it == mSnapshotStreams.end()) { - ChipLogError(Camera, "Snapshot stream not found for stream ID %u", - streamId); - return CameraError::ERROR_CAPTURE_SNAPSHOT_FAILED; + else + { + uint16_t streamId = streamID.Value(); + auto it = std::find_if(mSnapshotStreams.begin(), mSnapshotStreams.end(), [streamId](const SnapshotStream & s) { + return s.snapshotStreamParams.snapshotStreamID == streamId; + }); + if (it == mSnapshotStreams.end()) + { + ChipLogError(Camera, "Snapshot stream not found for stream ID %u", streamId); + return CameraError::ERROR_CAPTURE_SNAPSHOT_FAILED; + } + matchedRes = it->snapshotStreamParams.minResolution; + matchedCodec = it->snapshotStreamParams.imageCodec; } - matchedRes = it->snapshotStreamParams.minResolution; - matchedCodec = it->snapshotStreamParams.imageCodec; - } - // Create a meaningful dummy JPEG image for ESP32 - // This is a 32x32 pixel JPEG with "ESP32" text pattern (428 bytes) - static const uint8_t dummy_jpeg[] = { - 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, - 0x01, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, - 0x00, 0x08, 0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07, 0x07, 0x07, 0x09, - 0x09, 0x08, 0x0A, 0x0C, 0x14, 0x0D, 0x0C, 0x0B, 0x0B, 0x0C, 0x19, 0x12, - 0x13, 0x0F, 0x14, 0x1D, 0x1A, 0x1F, 0x1E, 0x1D, 0x1A, 0x1C, 0x1C, 0x20, - 0x24, 0x2E, 0x27, 0x20, 0x22, 0x2C, 0x23, 0x1C, 0x1C, 0x28, 0x37, 0x29, - 0x2C, 0x30, 0x31, 0x34, 0x34, 0x34, 0x1F, 0x27, 0x39, 0x3D, 0x38, 0x32, - 0x3C, 0x2E, 0x33, 0x34, 0x32, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x20, - 0x00, 0x20, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, - 0xC4, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xFF, 0xC4, - 0x00, 0x14, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xDA, 0x00, 0x0C, - 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3F, 0x00, 0x80, 0xFF, - 0xD9}; + // Create a dummy JPEG image + static const uint8_t dummy_jpeg[] = { + 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x48, 0x00, 0x48, 0x00, + 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x08, 0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07, 0x07, 0x07, 0x09, 0x09, 0x08, + 0x0A, 0x0C, 0x14, 0x0D, 0x0C, 0x0B, 0x0B, 0x0C, 0x19, 0x12, 0x13, 0x0F, 0x14, 0x1D, 0x1A, 0x1F, 0x1E, 0x1D, 0x1A, + 0x1C, 0x1C, 0x20, 0x24, 0x2E, 0x27, 0x20, 0x22, 0x2C, 0x23, 0x1C, 0x1C, 0x28, 0x37, 0x29, 0x2C, 0x30, 0x31, 0x34, + 0x34, 0x34, 0x1F, 0x27, 0x39, 0x3D, 0x38, 0x32, 0x3C, 0x2E, 0x33, 0x34, 0x32, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, + 0x20, 0x00, 0x20, 0x01, 0x11, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xC4, 0x00, 0x14, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xFF, 0xC4, 0x00, + 0x14, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3F, 0x00, 0x80, 0xFF, 0xD9 + }; - // Copy the dummy JPEG data to the output - outImageSnapshot.data.assign(dummy_jpeg, dummy_jpeg + sizeof(dummy_jpeg)); + // Copy the dummy JPEG data to the output + outImageSnapshot.data.assign(dummy_jpeg, dummy_jpeg + sizeof(dummy_jpeg)); - outImageSnapshot.imageRes = matchedRes; - outImageSnapshot.imageCodec = matchedCodec; + outImageSnapshot.imageRes = matchedRes; + outImageSnapshot.imageCodec = matchedCodec; - return CameraError::SUCCESS; + return CameraError::SUCCESS; } // Allocate snapshot stream -CameraError CameraDevice::AllocateSnapshotStream( - const CameraAVStreamMgmtDelegate::SnapshotStreamAllocateArgs &args, - uint16_t &outStreamID) { +CameraError CameraDevice::AllocateSnapshotStream(const CameraAVStreamManagementDelegate::SnapshotStreamAllocateArgs & args, + uint16_t & outStreamID) +{ - if (AddSnapshotStream(args, outStreamID)) { - auto it = std::find_if(mSnapshotStreams.begin(), mSnapshotStreams.end(), - [outStreamID](const SnapshotStream &s) { - return s.snapshotStreamParams.snapshotStreamID == - outStreamID; - }); - if (it == mSnapshotStreams.end()) { - ChipLogError(Camera, "Snapshot stream with ID %u not found", outStreamID); - return CameraError::ERROR_RESOURCE_EXHAUSTED; + if (AddSnapshotStream(args, outStreamID)) + { + auto it = std::find_if(mSnapshotStreams.begin(), mSnapshotStreams.end(), [outStreamID](const SnapshotStream & s) { + return s.snapshotStreamParams.snapshotStreamID == outStreamID; + }); + if (it == mSnapshotStreams.end()) + { + ChipLogError(Camera, "Snapshot stream with ID %u not found", outStreamID); + return CameraError::ERROR_RESOURCE_EXHAUSTED; + } + it->isAllocated = true; + ChipLogProgress(Camera, "Allocated snapshot stream with ID: %u", outStreamID); + return CameraError::SUCCESS; } - it->isAllocated = true; - ChipLogProgress(Camera, "Allocated snapshot stream with ID: %u", - outStreamID); + return CameraError::ERROR_RESOURCE_EXHAUSTED; +} + +uint8_t CameraDevice::GetMaxConcurrentEncoders() +{ + return kMaxConcurrentEncoders; +} + +uint32_t CameraDevice::GetMaxEncodedPixelRate() +{ + return kMaxEncodedPixelRate; +} + +VideoSensorParamsStruct & CameraDevice::GetVideoSensorParams() +{ + static VideoSensorParamsStruct videoSensorParams = { kVideoSensorWidthPixels, kVideoSensorHeightPixels, kMaxVideoFrameRate, + chip::Optional(30) }; // Typical numbers for Pi camera. + return videoSensorParams; +} + +bool CameraDevice::GetCameraSupportsHDR() +{ + return false; +} + +bool CameraDevice::GetCameraSupportsNightVision() +{ + return false; +} + +bool CameraDevice::GetNightVisionUsesInfrared() +{ + return false; +} + +bool CameraDevice::GetCameraSupportsWatermark() +{ + return true; +} + +bool CameraDevice::GetCameraSupportsOSD() +{ + return true; +} + +bool CameraDevice::GetCameraSupportsSoftPrivacy() +{ + return false; +} + +bool CameraDevice::GetCameraSupportsImageControl() +{ + return false; +} + +VideoResolutionStruct & CameraDevice::GetMinViewport() +{ + static VideoResolutionStruct minViewport = { kMinResolutionWidth, kMinResolutionHeight }; + return minViewport; +} + +std::vector & CameraDevice::GetRateDistortionTradeOffPoints() +{ + static std::vector rateDistTradeOffs = { { + VideoCodecEnum::kH264, { kMinResolutionWidth, kMinResolutionHeight }, 10000 /* bitrate */ + } }; + return rateDistTradeOffs; +} + +uint32_t CameraDevice::GetMaxContentBufferSize() +{ + return kMaxContentBufferSizeBytes; +} + +AudioCapabilitiesStruct & CameraDevice::GetMicrophoneCapabilities() +{ + static std::array audioCodecs = { AudioCodecEnum::kOpus, AudioCodecEnum::kAacLc }; + static std::array sampleRates = { 48000, 32000 }; // Sample rates in Hz + static std::array bitDepths = { 24, 32 }; + static AudioCapabilitiesStruct audioCapabilities = { kMicrophoneMaxChannelCount, chip::Span(audioCodecs), + chip::Span(sampleRates), chip::Span(bitDepths) }; + return audioCapabilities; +} + +AudioCapabilitiesStruct & CameraDevice::GetSpeakerCapabilities() +{ + static std::array audioCodecs = { AudioCodecEnum::kOpus, AudioCodecEnum::kAacLc }; + static std::array sampleRates = { 48000, 32000 }; // Sample rates in Hz + static std::array bitDepths = { 24, 32 }; + static AudioCapabilitiesStruct speakerCapabilities = { kSpeakerMaxChannelCount, chip::Span(audioCodecs), + chip::Span(sampleRates), chip::Span(bitDepths) }; + return speakerCapabilities; +} + +std::vector & CameraDevice::GetSnapshotCapabilities() +{ + static std::vector snapshotCapabilities = { { { kMinResolutionWidth, kMinResolutionHeight }, + kSnapshotStreamFrameRate, + ImageCodecEnum::kJpeg, + false, + chip::MakeOptional(static_cast(false)) } }; + return snapshotCapabilities; +} + +CameraError CameraDevice::SetNightVision(TriStateAutoEnum nightVision) +{ + mNightVision = nightVision; + return CameraError::SUCCESS; - } - return CameraError::ERROR_RESOURCE_EXHAUSTED; } -uint8_t CameraDevice::GetMaxConcurrentEncoders() { - return kMaxConcurrentEncoders; +uint32_t CameraDevice::GetMaxNetworkBandwidth() +{ + return kMaxNetworkBandwidthbps; } -uint32_t CameraDevice::GetMaxEncodedPixelRate() { return kMaxEncodedPixelRate; } - -VideoSensorParamsStruct &CameraDevice::GetVideoSensorParams() { - static VideoSensorParamsStruct videoSensorParams = { - kVideoSensorWidthPixels, kVideoSensorHeightPixels, kMaxVideoFrameRate, - chip::Optional(30)}; // Typical numbers for Pi camera. - return videoSensorParams; +uint16_t CameraDevice::GetCurrentFrameRate() +{ + return mCurrentVideoFrameRate; } -bool CameraDevice::GetCameraSupportsHDR() { return false; } +CameraError CameraDevice::SetHDRMode(bool hdrMode) +{ + mHDREnabled = hdrMode; -bool CameraDevice::GetCameraSupportsNightVision() { return false; } - -bool CameraDevice::GetNightVisionUsesInfrared() { return false; } - -bool CameraDevice::GetCameraSupportsWatermark() { return true; } - -bool CameraDevice::GetCameraSupportsOSD() { return true; } - -bool CameraDevice::GetCameraSupportsSoftPrivacy() { return false; } - -bool CameraDevice::GetCameraSupportsImageControl() { return false; } - -VideoResolutionStruct &CameraDevice::GetMinViewport() { - static VideoResolutionStruct minViewport = {kMinResolutionWidth, - kMinResolutionHeight}; - return minViewport; + return CameraError::SUCCESS; } -std::vector & -CameraDevice::GetRateDistortionTradeOffPoints() { - static std::vector rateDistTradeOffs = { - {VideoCodecEnum::kH264, - {kMinResolutionWidth, kMinResolutionHeight}, - 10000 /* bitrate */}}; - return rateDistTradeOffs; +CameraError CameraDevice::SetHardPrivacyMode(bool hardPrivacyMode) +{ + ChipLogProgress(Camera, "SetHardPrivacyMode: Setting hard privacy mode to %s", hardPrivacyMode ? "true" : "false"); + mHardPrivacyModeOn = hardPrivacyMode; + + return CameraError::SUCCESS; } -uint32_t CameraDevice::GetMaxContentBufferSize() { - return kMaxContentBufferSizeBytes; +CameraError CameraDevice::SetStreamUsagePriorities(std::vector streamUsagePriorities) +{ + mStreamUsagePriorities = streamUsagePriorities; + + return CameraError::SUCCESS; } -AudioCapabilitiesStruct &CameraDevice::GetMicrophoneCapabilities() { - static std::array audioCodecs = {AudioCodecEnum::kOpus, - AudioCodecEnum::kAacLc}; - static std::array sampleRates = {48000, - 32000}; // Sample rates in Hz - static std::array bitDepths = {24, 32}; - static AudioCapabilitiesStruct audioCapabilities = { - kMicrophoneMaxChannelCount, chip::Span(audioCodecs), - chip::Span(sampleRates), chip::Span(bitDepths)}; - return audioCapabilities; +std::vector & CameraDevice::GetSupportedStreamUsages() +{ + static std::vector supportedStreamUsage = { StreamUsageEnum::kLiveView, StreamUsageEnum::kRecording }; + return supportedStreamUsage; } -AudioCapabilitiesStruct &CameraDevice::GetSpeakerCapabilities() { - static std::array audioCodecs = {AudioCodecEnum::kOpus, - AudioCodecEnum::kAacLc}; - static std::array sampleRates = {48000, - 32000}; // Sample rates in Hz - static std::array bitDepths = {24, 32}; - static AudioCapabilitiesStruct speakerCapabilities = { - kSpeakerMaxChannelCount, chip::Span(audioCodecs), - chip::Span(sampleRates), chip::Span(bitDepths)}; - return speakerCapabilities; +CameraError CameraDevice::SetViewport(const chip::app::Clusters::Globals::Structs::ViewportStruct::Type & viewPort) +{ + mViewport = viewPort; + + return CameraError::SUCCESS; } -std::vector & -CameraDevice::GetSnapshotCapabilities() { - static std::vector snapshotCapabilities = { - {{kMinResolutionWidth, kMinResolutionHeight}, - kSnapshotStreamFrameRate, - ImageCodecEnum::kJpeg, - false, - chip::MakeOptional(static_cast(false))}}; - return snapshotCapabilities; +CameraError CameraDevice::SetViewport(VideoStream & stream, + const chip::app::Clusters::Globals::Structs::ViewportStruct::Type & viewport) +{ + ChipLogDetail(Camera, "Setting per stream viewport for stream %d.", stream.videoStreamParams.videoStreamID); + ChipLogDetail(Camera, "New viewport. x1=%d, x2=%d, y1=%d, y2=%d.", viewport.x1, viewport.x2, viewport.y1, viewport.y2); + stream.viewport = viewport; + return CameraError::SUCCESS; } -CameraError CameraDevice::SetNightVision(TriStateAutoEnum nightVision) { - mNightVision = nightVision; +CameraError CameraDevice::SetSoftRecordingPrivacyModeEnabled(bool softRecordingPrivacyMode) +{ + mSoftRecordingPrivacyModeEnabled = softRecordingPrivacyMode; - return CameraError::SUCCESS; + return CameraError::SUCCESS; } -uint32_t CameraDevice::GetMaxNetworkBandwidth() { - return kMaxNetworkBandwidthbps; -} +CameraError CameraDevice::SetSoftLivestreamPrivacyModeEnabled(bool softLivestreamPrivacyMode) +{ + mSoftLivestreamPrivacyModeEnabled = softLivestreamPrivacyMode; -uint16_t CameraDevice::GetCurrentFrameRate() { return mCurrentVideoFrameRate; } + // Notify WebRTCProviderManager about change + mWebRTCProviderManager.LiveStreamPrivacyModeChanged(softLivestreamPrivacyMode); -CameraError CameraDevice::SetHDRMode(bool hdrMode) { - mHDREnabled = hdrMode; - - return CameraError::SUCCESS; -} - -CameraError CameraDevice::SetHardPrivacyMode(bool hardPrivacyMode) { - ChipLogProgress(Camera, "SetHardPrivacyMode: Setting hard privacy mode to %s", - hardPrivacyMode ? "true" : "false"); - mHardPrivacyModeOn = hardPrivacyMode; - - return CameraError::SUCCESS; -} - -CameraError CameraDevice::SetStreamUsagePriorities( - std::vector streamUsagePriorities) { - mStreamUsagePriorities = streamUsagePriorities; - - return CameraError::SUCCESS; -} - -std::vector &CameraDevice::GetSupportedStreamUsages() { - static std::vector supportedStreamUsage = { - StreamUsageEnum::kLiveView, StreamUsageEnum::kRecording}; - return supportedStreamUsage; -} - -CameraError CameraDevice::SetViewport( - const chip::app::Clusters::Globals::Structs::ViewportStruct::Type - &viewPort) { - mViewport = viewPort; - - return CameraError::SUCCESS; -} - -CameraError CameraDevice::SetViewport( - VideoStream &stream, - const chip::app::Clusters::Globals::Structs::ViewportStruct::Type - &viewport) { - ChipLogDetail(Camera, "Setting per stream viewport for stream %d.", - stream.videoStreamParams.videoStreamID); - ChipLogDetail(Camera, "New viewport. x1=%d, x2=%d, y1=%d, y2=%d.", - viewport.x1, viewport.x2, viewport.y1, viewport.y2); - stream.viewport = viewport; - return CameraError::SUCCESS; -} - -CameraError CameraDevice::SetSoftRecordingPrivacyModeEnabled( - bool softRecordingPrivacyMode) { - mSoftRecordingPrivacyModeEnabled = softRecordingPrivacyMode; - - return CameraError::SUCCESS; -} - -CameraError CameraDevice::SetSoftLivestreamPrivacyModeEnabled( - bool softLivestreamPrivacyMode) { - mSoftLivestreamPrivacyModeEnabled = softLivestreamPrivacyMode; - - // Notify WebRTCProviderManager about change - mWebRTCProviderManager.LiveStreamPrivacyModeChanged( - softLivestreamPrivacyMode); - - return CameraError::SUCCESS; + return CameraError::SUCCESS; } // Mute/Unmute speaker. -CameraError CameraDevice::SetSpeakerMuted(bool muteSpeaker) { - mSpeakerMuted = muteSpeaker; +CameraError CameraDevice::SetSpeakerMuted(bool muteSpeaker) +{ + mSpeakerMuted = muteSpeaker; - return CameraError::SUCCESS; + return CameraError::SUCCESS; } // Set speaker volume level. -CameraError CameraDevice::SetSpeakerVolume(uint8_t speakerVol) { - mSpeakerVol = speakerVol; +CameraError CameraDevice::SetSpeakerVolume(uint8_t speakerVol) +{ + mSpeakerVol = speakerVol; - return CameraError::SUCCESS; + return CameraError::SUCCESS; } // Mute/Unmute microphone. -CameraError CameraDevice::SetMicrophoneMuted(bool muteMicrophone) { - mMicrophoneMuted = muteMicrophone; +CameraError CameraDevice::SetMicrophoneMuted(bool muteMicrophone) +{ + mMicrophoneMuted = muteMicrophone; - return CameraError::SUCCESS; + return CameraError::SUCCESS; } // Set microphone volume level. -CameraError CameraDevice::SetMicrophoneVolume(uint8_t microphoneVol) { - mMicrophoneVol = microphoneVol; +CameraError CameraDevice::SetMicrophoneVolume(uint8_t microphoneVol) +{ + mMicrophoneVol = microphoneVol; - return CameraError::SUCCESS; + return CameraError::SUCCESS; } // Set image rotation attributes -CameraError CameraDevice::SetImageRotation(uint16_t imageRotation) { - mImageRotation = imageRotation; +CameraError CameraDevice::SetImageRotation(uint16_t imageRotation) +{ + mImageRotation = imageRotation; - return CameraError::SUCCESS; + return CameraError::SUCCESS; } -CameraError CameraDevice::SetImageFlipHorizontal(bool imageFlipHorizontal) { - mImageFlipHorizontal = imageFlipHorizontal; +CameraError CameraDevice::SetImageFlipHorizontal(bool imageFlipHorizontal) +{ + mImageFlipHorizontal = imageFlipHorizontal; - return CameraError::SUCCESS; + return CameraError::SUCCESS; } -CameraError CameraDevice::SetImageFlipVertical(bool imageFlipVertical) { - mImageFlipVertical = imageFlipVertical; +CameraError CameraDevice::SetImageFlipVertical(bool imageFlipVertical) +{ + mImageFlipVertical = imageFlipVertical; - return CameraError::SUCCESS; + return CameraError::SUCCESS; } -CameraError -CameraDevice::SetLocalVideoRecordingEnabled(bool localVideoRecordingEnabled) { - mLocalVideoRecordingEnabled = localVideoRecordingEnabled; +CameraError CameraDevice::SetLocalVideoRecordingEnabled(bool localVideoRecordingEnabled) +{ + mLocalVideoRecordingEnabled = localVideoRecordingEnabled; - return CameraError::SUCCESS; + return CameraError::SUCCESS; } -CameraError CameraDevice::SetLocalSnapshotRecordingEnabled( - bool localSnapshotRecordingEnabled) { - mLocalSnapshotRecordingEnabled = localSnapshotRecordingEnabled; +CameraError CameraDevice::SetLocalSnapshotRecordingEnabled(bool localSnapshotRecordingEnabled) +{ + mLocalSnapshotRecordingEnabled = localSnapshotRecordingEnabled; - return CameraError::SUCCESS; + return CameraError::SUCCESS; } -CameraError CameraDevice::SetStatusLightEnabled(bool statusLightEnabled) { - mStatusLightEnabled = statusLightEnabled; +CameraError CameraDevice::SetStatusLightEnabled(bool statusLightEnabled) +{ + mStatusLightEnabled = statusLightEnabled; - return CameraError::SUCCESS; + return CameraError::SUCCESS; } -void CameraDevice::InitializeVideoStreams() { - // Create a video stream with a max resolution of 720p and max frame rate of - // 60 fps - VideoStream videoStream1 = { - { - 1 /* Id */, - StreamUsageEnum::kLiveView /* StreamUsage */, - VideoCodecEnum::kH264, - kMinVideoFrameRate /* MinFrameRate */, - k60fpsVideoFrameRate /* MaxFrameRate */, - {kMinResolutionWidth, kMinResolutionHeight} /* MinResolution */, - {k720pResolutionWidth, k720pResolutionHeight} /* MaxResolution */, - kMinBitRateBps /* MinBitRate */, - kMaxBitRateBps /* MaxBitRate */, - kKeyFrameIntervalMsec /* KeyFrameInterval */, - chip::MakeOptional(static_cast(false)) /* WMark */, - chip::MakeOptional(static_cast(false)) /* OSD */, - 0 /* RefCount */ - }, - false, - {mViewport.x1, mViewport.y1, mViewport.x2, mViewport.y2}, - nullptr}; - mVideoStreams.push_back(videoStream1); +void CameraDevice::InitializeVideoStreams() +{ + // Create a video stream with a max resolution of 720p and max frame rate of + // 60 fps + VideoStream videoStream1 = { { + 1 /* Id */, + StreamUsageEnum::kLiveView /* StreamUsage */, + VideoCodecEnum::kH264, + kMinVideoFrameRate /* MinFrameRate */, + k60fpsVideoFrameRate /* MaxFrameRate */, + { kMinResolutionWidth, kMinResolutionHeight } /* MinResolution */, + { k720pResolutionWidth, k720pResolutionHeight } /* MaxResolution */, + kMinBitRateBps /* MinBitRate */, + kMaxBitRateBps /* MaxBitRate */, + kKeyFrameIntervalMsec /* KeyFrameInterval */, + chip::MakeOptional(static_cast(false)) /* WMark */, + chip::MakeOptional(static_cast(false)) /* OSD */, + 0 /* RefCount */ + }, + false, + { mViewport.x1, mViewport.y1, mViewport.x2, mViewport.y2 }, + nullptr }; + mVideoStreams.push_back(videoStream1); - // Create a video stream with a min framerate of 60 fps and min resolution - // of 720p - VideoStream videoStream2 = { - { - 2 /* Id */, - StreamUsageEnum::kLiveView /* StreamUsage */, - VideoCodecEnum::kH264, - k60fpsVideoFrameRate /* MinFrameRate */, - kMaxVideoFrameRate /* MaxFrameRate */, - {k720pResolutionWidth, k720pResolutionHeight} /* MinResolution */, - {kMaxResolutionWidth, kMaxResolutionHeight} /* MaxResolution */, - kMinBitRateBps /* MinBitRate */, - kMaxBitRateBps /* MaxBitRate */, - kKeyFrameIntervalMsec /* KeyFrameInterval */, - chip::MakeOptional(static_cast(false)) /* WMark */, - chip::MakeOptional(static_cast(false)) /* OSD */, - 0 /* RefCount */ - }, - false, - {mViewport.x1, mViewport.y1, mViewport.x2, mViewport.y2}, - nullptr}; + // Create a video stream for the full range(fps, resolution, bitrate) + // supported by the camera. + VideoStream videoStream2 = { { + 2 /* Id */, + StreamUsageEnum::kLiveView /* StreamUsage */, + VideoCodecEnum::kH264, + kMinVideoFrameRate /* MinFrameRate */, + k60fpsVideoFrameRate /* MaxFrameRate */, + { kMinResolutionWidth, kMinResolutionHeight } /* MinResolution */, + { kMaxResolutionWidth, kMaxResolutionHeight } /* MaxResolution */, + kMinBitRateBps /* MinBitRate */, + kMaxBitRateBps /* MaxBitRate */, + kKeyFrameIntervalMsec /* KeyFrameInterval */, + chip::MakeOptional(static_cast(false)) /* WMark */, + chip::MakeOptional(static_cast(false)) /* OSD */, + 0 /* RefCount */ + }, + false, + { mViewport.x1, mViewport.y1, mViewport.x2, mViewport.y2 }, + nullptr }; - mVideoStreams.push_back(videoStream2); + mVideoStreams.push_back(videoStream2); - // Create a video stream for the full range(fps, resolution, bitrate) - // supported by the camera. - VideoStream videoStream3 = { - { - 3 /* Id */, - StreamUsageEnum::kLiveView /* StreamUsage */, - VideoCodecEnum::kH264, - kMinVideoFrameRate /* MinFrameRate */, - kMaxVideoFrameRate /* MaxFrameRate */, - {kMinResolutionWidth, kMinResolutionHeight} /* MinResolution */, - {kMaxResolutionWidth, kMaxResolutionHeight} /* MaxResolution */, - kMinBitRateBps /* MinBitRate */, - kMaxBitRateBps /* MaxBitRate */, - kKeyFrameIntervalMsec /* KeyFrameInterval */, - chip::MakeOptional(static_cast(false)) /* WMark */, - chip::MakeOptional(static_cast(false)) /* OSD */, - 0 /* RefCount */ - }, - false, - {mViewport.x1, mViewport.y1, mViewport.x2, mViewport.y2}, - nullptr}; + VideoStream videoStream3 = { { + 3 /* Id */, + StreamUsageEnum::kLiveView /* StreamUsage */, + VideoCodecEnum::kH264, + kMinVideoFrameRate /* MinFrameRate */, + k60fpsVideoFrameRate /* MaxFrameRate */, + { kMinResolutionWidth, kMinResolutionHeight } /* MinResolution */, + { kMaxResolutionWidth, kMaxResolutionHeight } /* MaxResolution */, + kMinBitRateBps /* MinBitRate */, + kMaxBitRateBps /* MaxBitRate */, + kKeyFrameIntervalMsec /* KeyFrameInterval */, + chip::MakeOptional(static_cast(false)) /* WMark */, + chip::MakeOptional(static_cast(false)) /* OSD */, + 0 /* RefCount */ + }, + false, + { mViewport.x1, mViewport.y1, mViewport.x2, mViewport.y2 }, + nullptr }; - mVideoStreams.push_back(videoStream3); + mVideoStreams.push_back(videoStream3); } -void CameraDevice::InitializeAudioStreams() { +void CameraDevice::InitializeAudioStreams() +{ - // Mono stream - AudioStream monoStream = { - { - 1 /* Id */, StreamUsageEnum::kLiveView, AudioCodecEnum::kOpus, - 1 /* ChannelCount: Mono */, 48000 /* SampleRate */, - 20000 /* BitRate */, 24 /* BitDepth */, 0 /* RefCount */ - }, - false, - nullptr}; - mAudioStreams.push_back(monoStream); + // Mono stream + AudioStream monoStream = { { + 1 /* Id */, StreamUsageEnum::kLiveView, AudioCodecEnum::kOpus, 1 /* ChannelCount: Mono */, + 48000 /* SampleRate */, 20000 /* BitRate */, 24 /* BitDepth */, 0 /* RefCount */ + }, + false, + nullptr }; + mAudioStreams.push_back(monoStream); - // Stereo stream - AudioStream stereoStream = { - { - 2 /* Id */, StreamUsageEnum::kLiveView, AudioCodecEnum::kOpus, - 2 /* ChannelCount: Stereo */, 48000 /* SampleRate */, - 32000 /* BitRate */, 24 /* BitDepth */, 0 /* RefCount */ - }, - false, - nullptr}; - mAudioStreams.push_back(stereoStream); + // Stereo stream + AudioStream stereoStream = { { + 2 /* Id */, StreamUsageEnum::kLiveView, AudioCodecEnum::kOpus, 2 /* ChannelCount: Stereo */, + 48000 /* SampleRate */, 32000 /* BitRate */, 24 /* BitDepth */, 0 /* RefCount */ + }, + false, + nullptr }; + mAudioStreams.push_back(stereoStream); - // Max channel count stream (from spec constant) - AudioStream maxChannelStream = { - { - 3 /* Id */, StreamUsageEnum::kLiveView, AudioCodecEnum::kOpus, - kMicrophoneMaxChannelCount /* Max from Spec */, - 48000 /* SampleRate */, 64000 /* BitRate */, 24 /* BitDepth */, - 0 /* RefCount */ - }, - false, - nullptr}; - mAudioStreams.push_back(maxChannelStream); + // Max channel count stream (from spec constant) + AudioStream maxChannelStream = { { + 3 /* Id */, StreamUsageEnum::kLiveView, AudioCodecEnum::kOpus, + kMicrophoneMaxChannelCount /* Max from Spec */, 48000 /* SampleRate */, + 64000 /* BitRate */, 24 /* BitDepth */, 0 /* RefCount */ + }, + false, + nullptr }; + mAudioStreams.push_back(maxChannelStream); } -void CameraDevice::InitializeSnapshotStreams() { - // Create single snapshot stream with typical supported parameters - uint16_t streamId = kInvalidStreamID; - AddSnapshotStream( - {ImageCodecEnum::kJpeg, - kSnapshotStreamFrameRate /* FrameRate */, - {kMinResolutionWidth, kMinResolutionHeight} /* MinResolution*/, - {kMaxResolutionWidth, kMaxResolutionHeight} /* MaxResolution */, - 90 /* Quality */}, - streamId); +void CameraDevice::InitializeSnapshotStreams() +{ + // Create single snapshot stream with typical supported parameters + uint16_t streamId = kInvalidStreamID; + AddSnapshotStream({ ImageCodecEnum::kJpeg, + kSnapshotStreamFrameRate /* FrameRate */, + { kMinResolutionWidth, kMinResolutionHeight } /* MinResolution*/, + { kMaxResolutionWidth, kMaxResolutionHeight } /* MaxResolution */, + 90 /* Quality */ }, + streamId); } bool CameraDevice::AddSnapshotStream( - const CameraAVStreamMgmtDelegate::SnapshotStreamAllocateArgs - &snapshotStreamAllocateArgs, - uint16_t &outStreamID) { - constexpr uint16_t kMaxSnapshotStreams = std::numeric_limits::max(); + const CameraAVStreamManagementDelegate::SnapshotStreamAllocateArgs & snapshotStreamAllocateArgs, uint16_t & outStreamID) +{ + constexpr uint16_t kMaxSnapshotStreams = std::numeric_limits::max(); - if (mSnapshotStreams.size() >= kMaxSnapshotStreams) { - ChipLogError(Camera, "Maximum number of snapshot streams reached. Cannot a " - "allocate new one"); - return false; - } - - uint16_t streamId = 0; - // Fetch a new stream ID if the passed ID is kInvalidStreamID, otherwise use - // the ID that was passed in. A valid streamID would be passed in when the - // stream list is being constructed from the persisted list of allocated - // streams that was loaded at Init() - if (outStreamID == kInvalidStreamID) { - for (const auto &s : mSnapshotStreams) { - // Find the highest existing stream ID. - if (s.snapshotStreamParams.snapshotStreamID > streamId) { - streamId = s.snapshotStreamParams.snapshotStreamID; - } - } - - // Find a unique stream id, starting from the last used one above, - // incrementing and wrapping at 65535. - for (uint16_t attempts = 0; attempts < kMaxSnapshotStreams; ++attempts) { - auto found = std::find_if( - mSnapshotStreams.begin(), mSnapshotStreams.end(), - [streamId](const SnapshotStream &s) { - return s.snapshotStreamParams.snapshotStreamID == streamId; - }); - if (found == mSnapshotStreams.end()) { - break; - } - if (attempts == kMaxSnapshotStreams - 1) { - ChipLogError(Camera, "No available slot for stream allocation"); + if (mSnapshotStreams.size() >= kMaxSnapshotStreams) + { + ChipLogError(Camera, "Maximum number of snapshot streams reached. Cannot allocate new one"); return false; - } - streamId = static_cast( - (streamId + 1) % kMaxSnapshotStreams); // Wraps to 0 after max-1 } - outStreamID = streamId; - } else { - // Have a sanity check that the passed streamID does not already exist - // in the list - auto found = std::find_if( - mSnapshotStreams.begin(), mSnapshotStreams.end(), - [outStreamID](const SnapshotStream &s) { - return s.snapshotStreamParams.snapshotStreamID == outStreamID; + uint16_t streamId = 0; + // Fetch a new stream ID if the passed ID is kInvalidStreamID, otherwise use + // the ID that was passed in. A valid streamID would be passed in when the + // stream list is being constructed from the persisted list of allocated + // streams that was loaded at Init() + if (outStreamID == kInvalidStreamID) + { + for (const auto & s : mSnapshotStreams) + { + // Find the highest existing stream ID. + if (s.snapshotStreamParams.snapshotStreamID > streamId) + { + streamId = s.snapshotStreamParams.snapshotStreamID; + } + } + + // Find a unique stream id, starting from the last used one above, + // incrementing and wrapping at 65535. + for (uint16_t attempts = 0; attempts < kMaxSnapshotStreams; ++attempts) + { + auto found = std::find_if(mSnapshotStreams.begin(), mSnapshotStreams.end(), [streamId](const SnapshotStream & s) { + return s.snapshotStreamParams.snapshotStreamID == streamId; + }); + if (found == mSnapshotStreams.end()) + { + break; + } + if (attempts == kMaxSnapshotStreams - 1) + { + ChipLogError(Camera, "No available slot for stream allocation"); + return false; + } + streamId = static_cast((streamId + 1) % kMaxSnapshotStreams); // Wraps to 0 after max-1 + } + + outStreamID = streamId; + } + else + { + // Have a sanity check that the passed streamID does not already exist + // in the list + auto found = std::find_if(mSnapshotStreams.begin(), mSnapshotStreams.end(), [outStreamID](const SnapshotStream & s) { + return s.snapshotStreamParams.snapshotStreamID == outStreamID; }); - if (found == mSnapshotStreams.end()) { - streamId = outStreamID; - } else { - ChipLogError( - Camera, - "StreamID %d already exists in the available snapshot stream list", - outStreamID); - return false; + if (found == mSnapshotStreams.end()) + { + streamId = outStreamID; + } + else + { + ChipLogError(Camera, "StreamID %d already exists in the available snapshot stream list", outStreamID); + return false; + } } - } - SnapshotStream snapshotStream = { - { - streamId, snapshotStreamAllocateArgs.imageCodec, - snapshotStreamAllocateArgs.maxFrameRate, - snapshotStreamAllocateArgs.minResolution, - snapshotStreamAllocateArgs.maxResolution, - snapshotStreamAllocateArgs.quality, 0 /* RefCount */ - }, - false, - nullptr}; + SnapshotStream snapshotStream = { { + streamId, snapshotStreamAllocateArgs.imageCodec, snapshotStreamAllocateArgs.maxFrameRate, + snapshotStreamAllocateArgs.minResolution, snapshotStreamAllocateArgs.maxResolution, + snapshotStreamAllocateArgs.quality, 0 /* RefCount */ + }, + false, + nullptr }; - mSnapshotStreams.push_back(snapshotStream); - return true; + mSnapshotStreams.push_back(snapshotStream); + return true; } -WebRTCTransportProvider::Delegate &CameraDevice::GetWebRTCProviderDelegate() { - return mWebRTCProviderManager; +WebRTCTransportProvider::Delegate & CameraDevice::GetWebRTCProviderDelegate() +{ + return mWebRTCProviderManager; } -WebRTCTransportProvider::WebRTCTransportProviderController & -CameraDevice::GetWebRTCProviderController() { - return mWebRTCProviderManager; +void CameraDevice::SetWebRTCTransportProvider(WebRTCTransportProvider::WebRTCTransportProviderCluster * provider) +{ + mWebRTCProviderManager.SetWebRTCTransportProvider(provider); } -CameraAVStreamMgmtDelegate &CameraDevice::GetCameraAVStreamMgmtDelegate() { - return mCameraAVStreamManager; +CameraAVStreamManagementDelegate & CameraDevice::GetCameraAVStreamMgmtDelegate() +{ + return mCameraAVStreamManager; } -CameraAVStreamController &CameraDevice::GetCameraAVStreamMgmtController() { - return mCameraAVStreamManager; +CameraAVStreamController & CameraDevice::GetCameraAVStreamMgmtController() +{ + return mCameraAVStreamManager; } diff --git a/examples/camera/main/camera-device.h b/examples/camera/main/camera-device.h index ff1781a0e..afe98f401 100644 --- a/examples/camera/main/camera-device.h +++ b/examples/camera/main/camera-device.h @@ -25,38 +25,35 @@ // Camera Constraints set to typical values. // TODO: Look into ways to fetch from hardware, if required/possible. static constexpr uint32_t kMaxContentBufferSizeBytes = 4096; -static constexpr uint32_t kMaxNetworkBandwidthbps = 128000000; // 128 Mbps -static constexpr uint8_t kMaxConcurrentEncoders = 1; -static constexpr uint8_t kSpeakerMinLevel = 1; -static constexpr uint8_t kSpeakerMaxLevel = 254; // Spec constraint -static constexpr uint8_t kSpeakerMaxChannelCount = 8; // Same as Microphone -static constexpr uint32_t kMaxEncodedPixelRate = - 248832000; // 1080p at 120fps(1920 * 1080 * 120) -static constexpr uint8_t kMicrophoneMinLevel = 1; -static constexpr uint8_t kMicrophoneMaxLevel = 254; // Spec constraint -static constexpr uint8_t kMicrophoneMaxChannelCount = - 8; // Spec Constraint in AudioStreamAllocate -static constexpr uint16_t kMinResolutionWidth = 640; // Low SD resolution -static constexpr uint16_t kMinResolutionHeight = 360; // Low SD resolution -static constexpr uint16_t k720pResolutionWidth = 1280; // 720p resolution -static constexpr uint16_t k720pResolutionHeight = 720; // 720p resolution -static constexpr uint16_t kMaxResolutionWidth = 1920; // 1080p resolution -static constexpr uint16_t kMaxResolutionHeight = 1080; // 1080p resolution -static constexpr uint16_t kSnapshotStreamFrameRate = 30; -static constexpr uint16_t kMaxVideoFrameRate = 120; -static constexpr uint16_t k60fpsVideoFrameRate = 60; -static constexpr uint16_t kMinVideoFrameRate = 30; -static constexpr uint32_t kMinBitRateBps = 10000; // 10 kbps -static constexpr uint32_t kMaxBitRateBps = 2000000; // 2 mbps -static constexpr uint32_t kKeyFrameIntervalMsec = - 4000; // 4 sec; recommendation from Spec -static constexpr uint16_t kVideoSensorWidthPixels = 1920; // 1080p resolution -static constexpr uint16_t kVideoSensorHeightPixels = 1080; // 1080p resolution -static constexpr uint16_t kMinImageRotation = 0; -static constexpr uint16_t kMaxImageRotation = 359; // Spec constraint -static constexpr uint8_t kMaxZones = 10; // Spec has min 1 -static constexpr uint8_t kMaxUserDefinedZones = 10; // Spec has min 5 -static constexpr uint8_t kSensitivityMax = 10; // Spec has 2 to 10 +static constexpr uint32_t kMaxNetworkBandwidthbps = 128000000; // 128 Mbps +static constexpr uint8_t kMaxConcurrentEncoders = 2; +static constexpr uint8_t kSpeakerMinLevel = 1; +static constexpr uint8_t kSpeakerMaxLevel = 254; // Spec constraint +static constexpr uint8_t kSpeakerMaxChannelCount = 8; // Same as Microphone +static constexpr uint32_t kMaxEncodedPixelRate = 373248000; // 1080p at 60fps + 120fps(1920 * 1080 * 180) +static constexpr uint8_t kMicrophoneMinLevel = 1; +static constexpr uint8_t kMicrophoneMaxLevel = 254; // Spec constraint +static constexpr uint8_t kMicrophoneMaxChannelCount = 8; // Spec Constraint in AudioStreamAllocate +static constexpr uint16_t kMinResolutionWidth = 640; // Low SD resolution +static constexpr uint16_t kMinResolutionHeight = 360; // Low SD resolution +static constexpr uint16_t k720pResolutionWidth = 1280; // 720p resolution +static constexpr uint16_t k720pResolutionHeight = 720; // 720p resolution +static constexpr uint16_t kMaxResolutionWidth = 1920; // 1080p resolution +static constexpr uint16_t kMaxResolutionHeight = 1080; // 1080p resolution +static constexpr uint16_t kSnapshotStreamFrameRate = 30; +static constexpr uint16_t kMaxVideoFrameRate = 120; +static constexpr uint16_t k60fpsVideoFrameRate = 60; +static constexpr uint16_t kMinVideoFrameRate = 30; +static constexpr uint32_t kMinBitRateBps = 10000; // 10 kbps +static constexpr uint32_t kMaxBitRateBps = 2000000; // 2 mbps +static constexpr uint32_t kKeyFrameIntervalMsec = 4000; // 4 sec; recommendation from Spec +static constexpr uint16_t kVideoSensorWidthPixels = 1920; // 1080p resolution +static constexpr uint16_t kVideoSensorHeightPixels = 1080; // 1080p resolution +static constexpr uint16_t kMinImageRotation = 0; +static constexpr uint16_t kMaxImageRotation = 359; // Spec constraint +static constexpr uint8_t kMaxZones = 10; // Spec has min 1 +static constexpr uint8_t kMaxUserDefinedZones = 10; // Spec has min 5 +static constexpr uint8_t kSensitivityMax = 10; // Spec has 2 to 10 // StreamIDs typically start from 0 and monotonically increase. Setting // Invalid value to a large and practically unused value. @@ -65,278 +62,224 @@ static constexpr uint16_t kInvalidStreamID = 65500; namespace Camera { -class CameraDevice : public CameraDeviceInterface, - public CameraDeviceInterface::CameraHALInterface { +class CameraDevice : public CameraDeviceInterface, public CameraDeviceInterface::CameraHALInterface +{ public: - chip::app::Clusters::WebRTCTransportProvider::Delegate & - GetWebRTCProviderDelegate() override; - chip::app::Clusters::WebRTCTransportProvider:: - WebRTCTransportProviderController & - GetWebRTCProviderController() override; - chip::app::Clusters::CameraAvStreamManagement::CameraAVStreamMgmtDelegate & - GetCameraAVStreamMgmtDelegate() override; - chip::app::Clusters::CameraAvStreamManagement::CameraAVStreamController & - GetCameraAVStreamMgmtController() override; - CameraDevice(); - ~CameraDevice(); + chip::app::Clusters::WebRTCTransportProvider::Delegate & GetWebRTCProviderDelegate() override; + void + SetWebRTCTransportProvider(chip::app::Clusters::WebRTCTransportProvider::WebRTCTransportProviderCluster * provider) override; + chip::app::Clusters::CameraAvStreamManagement::CameraAVStreamManagementDelegate & GetCameraAVStreamMgmtDelegate() override; + chip::app::Clusters::CameraAvStreamManagement::CameraAVStreamController & GetCameraAVStreamMgmtController() override; + CameraDevice(); + ~CameraDevice(); - CameraDeviceInterface::CameraHALInterface &GetCameraHALInterface() override { - return *this; - } + CameraDeviceInterface::CameraHALInterface & GetCameraHALInterface() override { return *this; } - void Init(); + void Init(); - // HAL interface impl - CameraError InitializeCameraDevice() override; + // HAL interface impl + CameraError InitializeCameraDevice() override; - CameraError InitializeStreams() override; + CameraError InitializeStreams() override; - CameraError - CaptureSnapshot(const chip::app::DataModel::Nullable streamID, - const VideoResolutionStruct &resolution, - ImageSnapshot &outImageSnapshot) override; + CameraError CaptureSnapshot(const chip::app::DataModel::Nullable streamID, const VideoResolutionStruct & resolution, + ImageSnapshot & outImageSnapshot) override; - // Allocate snapshot stream - CameraError AllocateSnapshotStream( - const chip::app::Clusters::CameraAvStreamManagement:: - CameraAVStreamMgmtDelegate::SnapshotStreamAllocateArgs &args, - uint16_t &outStreamID) override; + // Allocate snapshot stream + CameraError AllocateSnapshotStream( + const chip::app::Clusters::CameraAvStreamManagement::CameraAVStreamManagementDelegate::SnapshotStreamAllocateArgs & args, + uint16_t & outStreamID) override; - uint8_t GetMaxConcurrentEncoders() override; + uint8_t GetMaxConcurrentEncoders() override; - uint32_t GetMaxEncodedPixelRate() override; + uint32_t GetMaxEncodedPixelRate() override; - VideoSensorParamsStruct &GetVideoSensorParams() override; + VideoSensorParamsStruct & GetVideoSensorParams() override; - bool GetCameraSupportsHDR() override; + bool GetCameraSupportsHDR() override; - bool GetCameraSupportsNightVision() override; + bool GetCameraSupportsNightVision() override; - bool GetNightVisionUsesInfrared() override; + bool GetNightVisionUsesInfrared() override; - bool GetCameraSupportsWatermark() override; + bool GetCameraSupportsWatermark() override; - bool GetCameraSupportsOSD() override; + bool GetCameraSupportsOSD() override; - bool GetCameraSupportsSoftPrivacy() override; + bool GetCameraSupportsSoftPrivacy() override; - bool GetCameraSupportsImageControl() override; + bool GetCameraSupportsImageControl() override; - VideoResolutionStruct &GetMinViewport() override; + VideoResolutionStruct & GetMinViewport() override; - std::vector & - GetRateDistortionTradeOffPoints() override; + std::vector & GetRateDistortionTradeOffPoints() override; - uint32_t GetMaxContentBufferSize() override; + uint32_t GetMaxContentBufferSize() override; - AudioCapabilitiesStruct &GetMicrophoneCapabilities() override; + AudioCapabilitiesStruct & GetMicrophoneCapabilities() override; - AudioCapabilitiesStruct &GetSpeakerCapabilities() override; + AudioCapabilitiesStruct & GetSpeakerCapabilities() override; - std::vector &GetSnapshotCapabilities() override; + std::vector & GetSnapshotCapabilities() override; - uint32_t GetMaxNetworkBandwidth() override; + uint32_t GetMaxNetworkBandwidth() override; - uint16_t GetCurrentFrameRate() override; + uint16_t GetCurrentFrameRate() override; - CameraError SetHDRMode(bool hdrMode) override; - bool GetHDRMode() override { return mHDREnabled; } + CameraError SetHDRMode(bool hdrMode) override; + bool GetHDRMode() override { return mHDREnabled; } - CameraError SetHardPrivacyMode(bool hardPrivacyMode) override; - bool GetHardPrivacyMode() override { return mHardPrivacyModeOn; } + CameraError SetHardPrivacyMode(bool hardPrivacyMode) override; + bool GetHardPrivacyMode() override { return mHardPrivacyModeOn; } - CameraError - SetNightVision(chip::app::Clusters::CameraAvStreamManagement::TriStateAutoEnum - nightVision) override; - chip::app::Clusters::CameraAvStreamManagement::TriStateAutoEnum - GetNightVision() override { - return mNightVision; - } + CameraError SetNightVision(chip::app::Clusters::CameraAvStreamManagement::TriStateAutoEnum nightVision) override; + chip::app::Clusters::CameraAvStreamManagement::TriStateAutoEnum GetNightVision() override { return mNightVision; } - std::vector &GetSupportedStreamUsages() override; + std::vector & GetSupportedStreamUsages() override; - std::vector &GetStreamUsagePriorities() override { - return mStreamUsagePriorities; - } - CameraError SetStreamUsagePriorities( - std::vector streamUsagePriorities) override; + std::vector & GetStreamUsagePriorities() override { return mStreamUsagePriorities; } + CameraError SetStreamUsagePriorities(std::vector streamUsagePriorities) override; - // Sets the Default Camera Viewport - CameraError - SetViewport(const chip::app::Clusters::Globals::Structs::ViewportStruct::Type - &viewPort) override; - const chip::app::Clusters::Globals::Structs::ViewportStruct::Type & - GetViewport() override { - return mViewport; - } + // Sets the Default Camera Viewport + CameraError SetViewport(const chip::app::Clusters::Globals::Structs::ViewportStruct::Type & viewPort) override; + const chip::app::Clusters::Globals::Structs::ViewportStruct::Type & GetViewport() override { return mViewport; } - /** - * Sets the Viewport for a specific stream. The implementation of this HAL API - * is responsible for updating the stream identified with the provided - * viewport. The invoker of this API shall have already ensured that the - * provided viewport conforms to the specification requirements on size and - * aspect ratio. - * - * @param stream the currently allocated video stream on which the viewport - * is being set - * @param viewport the viewport to be set on the stream - */ - CameraError - SetViewport(VideoStream &stream, - const chip::app::Clusters::Globals::Structs::ViewportStruct::Type - &viewport) override; + /** + * Sets the Viewport for a specific stream. The implementation of this HAL API + * is responsible for updating the stream identified with the provided + * viewport. The invoker of this API shall have already ensured that the + * provided viewport conforms to the specification requirements on size and + * aspect ratio. + * + * @param stream the currently allocated video stream on which the viewport + * is being set + * @param viewport the viewport to be set on the stream + */ + CameraError SetViewport(VideoStream & stream, + const chip::app::Clusters::Globals::Structs::ViewportStruct::Type & viewport) override; - // Get/Set SoftRecordingPrivacyMode. - CameraError - SetSoftRecordingPrivacyModeEnabled(bool softRecordingPrivacyMode) override; - bool GetSoftRecordingPrivacyModeEnabled() override { - return mSoftRecordingPrivacyModeEnabled; - } + // Get/Set SoftRecordingPrivacyMode. + CameraError SetSoftRecordingPrivacyModeEnabled(bool softRecordingPrivacyMode) override; + bool GetSoftRecordingPrivacyModeEnabled() override { return mSoftRecordingPrivacyModeEnabled; } - // Get/Set SoftLivestreamPrivacyMode. - CameraError - SetSoftLivestreamPrivacyModeEnabled(bool softLivestreamPrivacyMode) override; - bool GetSoftLivestreamPrivacyModeEnabled() override { - return mSoftLivestreamPrivacyModeEnabled; - } + // Get/Set SoftLivestreamPrivacyMode. + CameraError SetSoftLivestreamPrivacyModeEnabled(bool softLivestreamPrivacyMode) override; + bool GetSoftLivestreamPrivacyModeEnabled() override { return mSoftLivestreamPrivacyModeEnabled; } - // Currently, defaulting to not supporting hard privacy switch. - bool HasHardPrivacySwitch() override { return false; } + // Currently, defaulting to not supporting hard privacy switch. + bool HasHardPrivacySwitch() override { return false; } - // Currently, defaulting to not supporting speaker. - bool HasSpeaker() override { return false; } + // Currently, defaulting to not supporting speaker. + bool HasSpeaker() override { return false; } - // Mute/Unmute speaker. - CameraError SetSpeakerMuted(bool muteSpeaker) override; - bool GetSpeakerMuted() override { return mSpeakerMuted; } + // Mute/Unmute speaker. + CameraError SetSpeakerMuted(bool muteSpeaker) override; + bool GetSpeakerMuted() override { return mSpeakerMuted; } - // Get/Set speaker volume level. - CameraError SetSpeakerVolume(uint8_t speakerVol) override; - uint8_t GetSpeakerVolume() override { return mSpeakerVol; } + // Get/Set speaker volume level. + CameraError SetSpeakerVolume(uint8_t speakerVol) override; + uint8_t GetSpeakerVolume() override { return mSpeakerVol; } - // Get the speaker max and min levels. - uint8_t GetSpeakerMaxLevel() override { return INVALID_SPKR_LEVEL; } - uint8_t GetSpeakerMinLevel() override { return INVALID_SPKR_LEVEL; } + // Get the speaker max and min levels. + uint8_t GetSpeakerMaxLevel() override { return INVALID_SPKR_LEVEL; } + uint8_t GetSpeakerMinLevel() override { return INVALID_SPKR_LEVEL; } - // Does camera have a microphone - bool HasMicrophone() override { return true; } + // Does camera have a microphone + bool HasMicrophone() override { return true; } - // Mute/Unmute microphone. - CameraError SetMicrophoneMuted(bool muteMicrophone) override; - bool GetMicrophoneMuted() override { return mMicrophoneMuted; } + // Mute/Unmute microphone. + CameraError SetMicrophoneMuted(bool muteMicrophone) override; + bool GetMicrophoneMuted() override { return mMicrophoneMuted; } - // Set microphone volume level. - CameraError SetMicrophoneVolume(uint8_t microphoneVol) override; - uint8_t GetMicrophoneVolume() override { return mMicrophoneVol; } + // Set microphone volume level. + CameraError SetMicrophoneVolume(uint8_t microphoneVol) override; + uint8_t GetMicrophoneVolume() override { return mMicrophoneVol; } - // Get the microphone max and min levels. - uint8_t GetMicrophoneMaxLevel() override { return mMicrophoneMaxLevel; } - uint8_t GetMicrophoneMinLevel() override { return mMicrophoneMinLevel; } + // Get the microphone max and min levels. + uint8_t GetMicrophoneMaxLevel() override { return mMicrophoneMaxLevel; } + uint8_t GetMicrophoneMinLevel() override { return mMicrophoneMinLevel; } - // Get/Set image control attributes - CameraError SetImageRotation(uint16_t imageRotation) override; - uint16_t GetImageRotation() override { return mImageRotation; } + // Get/Set image control attributes + CameraError SetImageRotation(uint16_t imageRotation) override; + uint16_t GetImageRotation() override { return mImageRotation; } - CameraError SetImageFlipHorizontal(bool imageFlipHorizontal) override; - bool GetImageFlipHorizontal() override { return mImageFlipHorizontal; } + CameraError SetImageFlipHorizontal(bool imageFlipHorizontal) override; + bool GetImageFlipHorizontal() override { return mImageFlipHorizontal; } - CameraError SetImageFlipVertical(bool imageFlipVertical) override; - bool GetImageFlipVertical() override { return mImageFlipVertical; } + CameraError SetImageFlipVertical(bool imageFlipVertical) override; + bool GetImageFlipVertical() override { return mImageFlipVertical; } - // Does camera have local storage - bool HasLocalStorage() override { return false; } + // Does camera have local storage + bool HasLocalStorage() override { return false; } - // Set/Get LocalVideoRecordingEnabled - CameraError - SetLocalVideoRecordingEnabled(bool localVideoRecordingEnabled) override; - bool GetLocalVideoRecordingEnabled() override { - return mLocalVideoRecordingEnabled; - } + // Set/Get LocalVideoRecordingEnabled + CameraError SetLocalVideoRecordingEnabled(bool localVideoRecordingEnabled) override; + bool GetLocalVideoRecordingEnabled() override { return mLocalVideoRecordingEnabled; } - // Set/Get LocalSnapshotRecordingEnabled - CameraError - SetLocalSnapshotRecordingEnabled(bool localSnapshotRecordingEnabled) override; - bool GetLocalSnapshotRecordingEnabled() override { - return mLocalSnapshotRecordingEnabled; - } + // Set/Get LocalSnapshotRecordingEnabled + CameraError SetLocalSnapshotRecordingEnabled(bool localSnapshotRecordingEnabled) override; + bool GetLocalSnapshotRecordingEnabled() override { return mLocalSnapshotRecordingEnabled; } - // Does camera have a status light - bool HasStatusLight() override { return true; } + // Does camera have a status light + bool HasStatusLight() override { return true; } - // Set/Get StatusLightEnabled - CameraError SetStatusLightEnabled(bool statusLightEnabled) override; - bool GetStatusLightEnabled() override { return mStatusLightEnabled; } + // Set/Get StatusLightEnabled + CameraError SetStatusLightEnabled(bool statusLightEnabled) override; + bool GetStatusLightEnabled() override { return mStatusLightEnabled; } - std::vector &GetAvailableVideoStreams() override { - return mVideoStreams; - } + std::vector & GetAvailableVideoStreams() override { return mVideoStreams; } - std::vector &GetAvailableAudioStreams() override { - return mAudioStreams; - } + std::vector & GetAvailableAudioStreams() override { return mAudioStreams; } - std::vector &GetAvailableSnapshotStreams() override { - return mSnapshotStreams; - } + std::vector & GetAvailableSnapshotStreams() override { return mSnapshotStreams; } private: - std::vector - mVideoStreams; // Vector to hold available video streams - std::vector - mAudioStreams; // Vector to hold available audio streams - std::vector - mSnapshotStreams; // Vector to hold available snapshot streams + std::vector mVideoStreams; // Vector to hold available video streams + std::vector mAudioStreams; // Vector to hold available audio streams + std::vector mSnapshotStreams; // Vector to hold available snapshot streams - void InitializeVideoStreams(); - void InitializeAudioStreams(); - void InitializeSnapshotStreams(); + void InitializeVideoStreams(); + void InitializeAudioStreams(); + void InitializeSnapshotStreams(); - bool AddSnapshotStream(const chip::app::Clusters::CameraAvStreamManagement:: - CameraAVStreamManager::SnapshotStreamAllocateArgs - &snapshotStreamAllocateArgs, - uint16_t &outStreamID); + bool AddSnapshotStream(const chip::app::Clusters::CameraAvStreamManagement::CameraAVStreamManager::SnapshotStreamAllocateArgs & + snapshotStreamAllocateArgs, + uint16_t & outStreamID); - bool MatchClosestSnapshotParams( - const VideoResolutionStruct &requested, - VideoResolutionStruct &outResolution, - chip::app::Clusters::CameraAvStreamManagement::ImageCodecEnum &outCodec); + bool MatchClosestSnapshotParams(const VideoResolutionStruct & requested, VideoResolutionStruct & outResolution, + chip::app::Clusters::CameraAvStreamManagement::ImageCodecEnum & outCodec); - // Various cluster server delegates - chip::app::Clusters::WebRTCTransportProvider::WebRTCProviderManager - mWebRTCProviderManager; + // Various cluster server delegates + chip::app::Clusters::WebRTCTransportProvider::WebRTCProviderManager mWebRTCProviderManager; - chip::app::Clusters::CameraAvStreamManagement::CameraAVStreamManager - mCameraAVStreamManager; + chip::app::Clusters::CameraAvStreamManagement::CameraAVStreamManager mCameraAVStreamManager; - // Use a standard 1080p aspect ratio - chip::app::Clusters::Globals::Structs::ViewportStruct::Type mViewport = { - 0, 0, 1920, 1080}; - uint16_t mCurrentVideoFrameRate = kMinVideoFrameRate; - bool mHDREnabled = false; - bool mSpeakerMuted = false; - bool mMicrophoneMuted = false; - bool mHardPrivacyModeOn = false; - chip::app::Clusters::CameraAvStreamManagement::TriStateAutoEnum mNightVision = - chip::app::Clusters::CameraAvStreamManagement::TriStateAutoEnum::kOff; - bool mSoftRecordingPrivacyModeEnabled = false; - bool mSoftLivestreamPrivacyModeEnabled = false; - uint8_t mSpeakerVol = kSpeakerMinLevel; - uint8_t mSpeakerMinLevel = kSpeakerMinLevel; - uint8_t mSpeakerMaxLevel = kSpeakerMaxLevel; - uint8_t mMicrophoneVol = kMicrophoneMinLevel; - uint8_t mMicrophoneMinLevel = kMicrophoneMinLevel; - uint8_t mMicrophoneMaxLevel = kMicrophoneMaxLevel; - bool mLocalVideoRecordingEnabled = false; - bool mLocalSnapshotRecordingEnabled = false; - bool mStatusLightEnabled = false; - uint16_t mImageRotation = kMinImageRotation; - bool mImageFlipHorizontal = false; - bool mImageFlipVertical = false; - uint8_t mDetectionSensitivity = - (1 + kSensitivityMax) / 2; // Average over the range + // Use a standard 1080p aspect ratio + chip::app::Clusters::Globals::Structs::ViewportStruct::Type mViewport = { 0, 0, 1920, 1080 }; + uint16_t mCurrentVideoFrameRate = kMinVideoFrameRate; + bool mHDREnabled = false; + bool mSpeakerMuted = false; + bool mMicrophoneMuted = false; + bool mHardPrivacyModeOn = false; + chip::app::Clusters::CameraAvStreamManagement::TriStateAutoEnum mNightVision = + chip::app::Clusters::CameraAvStreamManagement::TriStateAutoEnum::kOff; + bool mSoftRecordingPrivacyModeEnabled = false; + bool mSoftLivestreamPrivacyModeEnabled = false; + uint8_t mSpeakerVol = kSpeakerMinLevel; + uint8_t mSpeakerMinLevel = kSpeakerMinLevel; + uint8_t mSpeakerMaxLevel = kSpeakerMaxLevel; + uint8_t mMicrophoneVol = kMicrophoneMinLevel; + uint8_t mMicrophoneMinLevel = kMicrophoneMinLevel; + uint8_t mMicrophoneMaxLevel = kMicrophoneMaxLevel; + bool mLocalVideoRecordingEnabled = false; + bool mLocalSnapshotRecordingEnabled = false; + bool mStatusLightEnabled = false; + uint16_t mImageRotation = kMinImageRotation; + bool mImageFlipHorizontal = false; + bool mImageFlipVertical = false; + uint8_t mDetectionSensitivity = (1 + kSensitivityMax) / 2; // Average over the range - std::vector mStreamUsagePriorities = { - StreamUsageEnum::kLiveView, StreamUsageEnum::kRecording}; + std::vector mStreamUsagePriorities = { StreamUsageEnum::kLiveView, StreamUsageEnum::kRecording }; }; } // namespace Camera diff --git a/examples/camera/main/clusters/camera-av-stream-manager.cpp b/examples/camera/main/clusters/camera-av-stream-manager.cpp index e2bd2faca..9bdfee018 100644 --- a/examples/camera/main/clusters/camera-av-stream-manager.cpp +++ b/examples/camera/main/clusters/camera-av-stream-manager.cpp @@ -1,3 +1,20 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * All rights reserved. + * + * 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. + */ #include #include #include @@ -22,860 +39,851 @@ constexpr uint16_t kInvalidStreamID = 65500; } // namespace -void CameraAVStreamManager::SetCameraDeviceHAL( - CameraDeviceInterface *aCameraDeviceHAL) { - mCameraDeviceHAL = aCameraDeviceHAL; +void CameraAVStreamManager::SetCameraDeviceHAL(CameraDeviceInterface * aCameraDeviceHAL) +{ + mCameraDeviceHAL = aCameraDeviceHAL; } -CHIP_ERROR CameraAVStreamManager::ValidateStreamUsage( - StreamUsageEnum streamUsage, - Optional> &videoStreamId, - Optional> &audioStreamId) { - // The server ensures that at least one stream Id has a value, and that there - // are streams allocated If a stream id(s) are provided, it's sufficient to - // have verified that the provide usage is supported by the camera. If they're - // Null, look for a stream ID that matches the usage. A match does not need to - // be exact. - bool exactlyMatchedVideoStream = false; - bool looselyMatchedVideoStream = false; - uint16_t looseVideoStreamID; - bool exactlyMatchedAudioStream = false; - bool looselyMatchedAudioStream = false; - uint16_t looseAudioStreamID; +CHIP_ERROR CameraAVStreamManager::ValidateStreamUsage(StreamUsageEnum streamUsage, + Optional> & videoStreamId, + Optional> & audioStreamId) +{ + // The server ensures that at least one stream Id has a value, and that there + // are streams allocated If a stream id(s) are provided, it's sufficient to + // have verified that the provide usage is supported by the camera. If they're + // Null, look for a stream ID that matches the usage. A match does not need to + // be exact. + bool exactlyMatchedVideoStream = false; + bool looselyMatchedVideoStream = false; + uint16_t looseVideoStreamID; + bool exactlyMatchedAudioStream = false; + bool looselyMatchedAudioStream = false; + uint16_t looseAudioStreamID; - // Is the requested stream usage supported by the camera? - auto myStreamUsages = - GetCameraAVStreamMgmtServer()->GetSupportedStreamUsages(); - auto it = - std::find(myStreamUsages.begin(), myStreamUsages.end(), streamUsage); - if (it == myStreamUsages.end()) { - ChipLogError(Camera, - "Requested stream usage not found in supported stream usages"); - return CHIP_ERROR_NOT_FOUND; - } + // Is the requested stream usage supported by the camera? + auto myStreamUsages = GetCameraAVStreamManagementCluster()->GetSupportedStreamUsages(); + auto it = std::find(myStreamUsages.begin(), myStreamUsages.end(), streamUsage); + if (it == myStreamUsages.end()) + { + ChipLogError(Camera, "Requested stream usage not found in supported stream usages"); + return CHIP_ERROR_NOT_FOUND; + } - if (videoStreamId.HasValue()) { - const std::vector &allocatedVideoStreams = - GetCameraAVStreamMgmtServer()->GetAllocatedVideoStreams(); + if (videoStreamId.HasValue()) + { + const std::vector & allocatedVideoStreams = + GetCameraAVStreamManagementCluster()->GetAllocatedVideoStreams(); - // If no Video ID is provided, match to an allocated ID. Exact is preferred - // if found. We know the stream requested is in supported streams. - if (videoStreamId.Value().IsNull()) { - for (const auto &stream : allocatedVideoStreams) { - if (stream.streamUsage == streamUsage) { - videoStreamId.Emplace(stream.videoStreamID); - exactlyMatchedVideoStream = true; - break; + // If no Video ID is provided, match to an allocated ID. Exact is preferred + // if found. We know the stream requested is in supported streams. + if (videoStreamId.Value().IsNull()) + { + for (const auto & stream : allocatedVideoStreams) + { + if (stream.streamUsage == streamUsage) + { + videoStreamId.Emplace(stream.videoStreamID); + exactlyMatchedVideoStream = true; + break; + } + + looselyMatchedVideoStream = true; + looseVideoStreamID = stream.videoStreamID; + } } - - looselyMatchedVideoStream = true; - looseVideoStreamID = stream.videoStreamID; - } - } else { - // We've been provided with a stream ID, and we know the stream usage is - // supported by the camera, classify as an exact match - exactlyMatchedVideoStream = true; - } - } - - if (audioStreamId.HasValue()) { - const std::vector &allocatedAudioStreams = - GetCameraAVStreamMgmtServer()->GetAllocatedAudioStreams(); - - // If no Audio ID is provided, match to an allocated ID. Exact is preferred - // if found. We know the stream requested is in supported streams. - if (audioStreamId.Value().IsNull()) { - for (const auto &stream : allocatedAudioStreams) { - if (stream.streamUsage == streamUsage) { - audioStreamId.Emplace(stream.audioStreamID); - exactlyMatchedAudioStream = true; - break; + else + { + // We've been provided with a stream ID, and we know the stream usage is + // supported by the camera, classify as an exact match + exactlyMatchedVideoStream = true; } - - looselyMatchedAudioStream = true; - looseAudioStreamID = stream.audioStreamID; - } - } else { - // We've been provided with a stream ID, and we know the stream usage is - // supported by the camera, classify as an exact match - exactlyMatchedAudioStream = true; } - } - // If we have a loose match and no exact match, update the provided stream IDs - // with the loose match values - // - if (looselyMatchedAudioStream && !exactlyMatchedAudioStream) { - audioStreamId.Emplace(looseAudioStreamID); - } + if (audioStreamId.HasValue()) + { + const std::vector & allocatedAudioStreams = + GetCameraAVStreamManagementCluster()->GetAllocatedAudioStreams(); - if (looselyMatchedVideoStream && !exactlyMatchedVideoStream) { - videoStreamId.Emplace(looseVideoStreamID); - } + // If no Audio ID is provided, match to an allocated ID. Exact is preferred + // if found. We know the stream requested is in supported streams. + if (audioStreamId.Value().IsNull()) + { + for (const auto & stream : allocatedAudioStreams) + { + if (stream.streamUsage == streamUsage) + { + audioStreamId.Emplace(stream.audioStreamID); + exactlyMatchedAudioStream = true; + break; + } - return CHIP_NO_ERROR; + looselyMatchedAudioStream = true; + looseAudioStreamID = stream.audioStreamID; + } + } + else + { + // We've been provided with a stream ID, and we know the stream usage is + // supported by the camera, classify as an exact match + exactlyMatchedAudioStream = true; + } + } + + // If we have a loose match and no exact match, update the provided stream IDs + // with the loose match values + // + if (looselyMatchedAudioStream && !exactlyMatchedAudioStream) + { + audioStreamId.Emplace(looseAudioStreamID); + } + + if (looselyMatchedVideoStream && !exactlyMatchedVideoStream) + { + videoStreamId.Emplace(looseVideoStreamID); + } + + return CHIP_NO_ERROR; } -const std::vector< - chip::app::Clusters::CameraAvStreamManagement::VideoStreamStruct> & -CameraAVStreamManager::GetAllocatedVideoStreams() const { - return GetCameraAVStreamMgmtServer()->GetAllocatedVideoStreams(); +const std::vector & +CameraAVStreamManager::GetAllocatedVideoStreams() const +{ + return GetCameraAVStreamManagementCluster()->GetAllocatedVideoStreams(); } -const std::vector< - chip::app::Clusters::CameraAvStreamManagement::AudioStreamStruct> & -CameraAVStreamManager::GetAllocatedAudioStreams() const { - return GetCameraAVStreamMgmtServer()->GetAllocatedAudioStreams(); +const std::vector & +CameraAVStreamManager::GetAllocatedAudioStreams() const +{ + return GetCameraAVStreamManagementCluster()->GetAllocatedAudioStreams(); } -void CameraAVStreamManager::GetBandwidthForStreams( - const Optional> &videoStreamId, - const Optional> &audioStreamId, - uint32_t &outBandwidthbps) { +void CameraAVStreamManager::GetBandwidthForStreams(const Optional> & videoStreamId, + const Optional> & audioStreamId, + uint32_t & outBandwidthbps) +{ - outBandwidthbps = 0; - if (videoStreamId.HasValue() && !videoStreamId.Value().IsNull()) { - uint16_t vStreamId = videoStreamId.Value().Value(); - auto &allocatedVideoStreams = - GetCameraAVStreamMgmtServer()->GetAllocatedVideoStreams(); - for (const chip::app::Clusters::CameraAvStreamManagement::Structs:: - VideoStreamStruct::Type &stream : allocatedVideoStreams) { - if (stream.videoStreamID == vStreamId) { - outBandwidthbps += stream.maxBitRate; - ChipLogProgress( - Camera, - "GetBandwidthForStreams: VideoStream %u maxBitRate: %lu bps", - vStreamId, stream.maxBitRate); - break; - } + outBandwidthbps = 0; + if (videoStreamId.HasValue() && !videoStreamId.Value().IsNull()) + { + uint16_t vStreamId = videoStreamId.Value().Value(); + auto & allocatedVideoStreams = GetCameraAVStreamManagementCluster()->GetAllocatedVideoStreams(); + for (const chip::app::Clusters::CameraAvStreamManagement::Structs::VideoStreamStruct::Type & stream : allocatedVideoStreams) + { + if (stream.videoStreamID == vStreamId) + { + outBandwidthbps += stream.maxBitRate; + ChipLogProgress(Camera, "GetBandwidthForStreams: VideoStream %u maxBitRate: %lu bps", vStreamId, stream.maxBitRate); + break; + } + } } - } - if (audioStreamId.HasValue() && !audioStreamId.Value().IsNull()) { - uint16_t aStreamId = audioStreamId.Value().Value(); - auto &allocatedAudioStreams = - GetCameraAVStreamMgmtServer()->GetAllocatedAudioStreams(); - for (const chip::app::Clusters::CameraAvStreamManagement::Structs:: - AudioStreamStruct::Type &stream : allocatedAudioStreams) { - if (stream.audioStreamID == aStreamId) { - outBandwidthbps += stream.bitRate; - ChipLogProgress( - Camera, "GetBandwidthForStreams: AudioStream %u bitRate: %lu bps", - aStreamId, stream.bitRate); - break; - } + if (audioStreamId.HasValue() && !audioStreamId.Value().IsNull()) + { + uint16_t aStreamId = audioStreamId.Value().Value(); + auto & allocatedAudioStreams = GetCameraAVStreamManagementCluster()->GetAllocatedAudioStreams(); + for (const chip::app::Clusters::CameraAvStreamManagement::Structs::AudioStreamStruct::Type & stream : allocatedAudioStreams) + { + if (stream.audioStreamID == aStreamId) + { + outBandwidthbps += stream.bitRate; + ChipLogProgress(Camera, "GetBandwidthForStreams: AudioStream %u bitRate: %lu bps", aStreamId, stream.bitRate); + break; + } + } } - } - return; + return; } CHIP_ERROR -CameraAVStreamManager::ValidateVideoStreamID(uint16_t videoStreamId) { - const std::vector &allocatedVideoStreams = - GetCameraAVStreamMgmtServer()->GetAllocatedVideoStreams(); +CameraAVStreamManager::ValidateVideoStreamID(uint16_t videoStreamId) +{ + const std::vector & allocatedVideoStreams = GetCameraAVStreamManagementCluster()->GetAllocatedVideoStreams(); - // Check if the videoStreamId exists in allocated streams - for (const auto &stream : allocatedVideoStreams) { - if (stream.videoStreamID == videoStreamId) { - ChipLogProgress(Camera, "Video stream ID %u is valid and allocated", - videoStreamId); - return CHIP_NO_ERROR; + // Check if the videoStreamId exists in allocated streams + for (const auto & stream : allocatedVideoStreams) + { + if (stream.videoStreamID == videoStreamId) + { + ChipLogProgress(Camera, "Video stream ID %u is valid and allocated", videoStreamId); + return CHIP_NO_ERROR; + } } - } - ChipLogError(Camera, - "Video stream ID %u not found in allocated video streams", - videoStreamId); - return CHIP_ERROR_INVALID_ARGUMENT; + ChipLogError(Camera, "Video stream ID %u not found in allocated video streams", videoStreamId); + return CHIP_ERROR_INVALID_ARGUMENT; } CHIP_ERROR -CameraAVStreamManager::ValidateAudioStreamID(uint16_t audioStreamId) { - const std::vector &allocatedAudioStreams = - GetCameraAVStreamMgmtServer()->GetAllocatedAudioStreams(); +CameraAVStreamManager::ValidateAudioStreamID(uint16_t audioStreamId) +{ + const std::vector & allocatedAudioStreams = GetCameraAVStreamManagementCluster()->GetAllocatedAudioStreams(); - // Check if the audioStreamId exists in allocated streams - for (const auto &stream : allocatedAudioStreams) { - if (stream.audioStreamID == audioStreamId) { - ChipLogProgress(Camera, "Audio stream ID %u is valid and allocated", - audioStreamId); - return CHIP_NO_ERROR; + // Check if the audioStreamId exists in allocated streams + for (const auto & stream : allocatedAudioStreams) + { + if (stream.audioStreamID == audioStreamId) + { + ChipLogProgress(Camera, "Audio stream ID %u is valid and allocated", audioStreamId); + return CHIP_NO_ERROR; + } } - } - ChipLogError(Camera, - "Audio stream ID %u not found in allocated audio streams", - audioStreamId); - return CHIP_ERROR_INVALID_ARGUMENT; + ChipLogError(Camera, "Audio stream ID %u not found in allocated audio streams", audioStreamId); + return CHIP_ERROR_INVALID_ARGUMENT; } -CHIP_ERROR CameraAVStreamManager::IsHardPrivacyModeActive(bool &isActive) { - // Check privacy mode attributes - isActive = GetCameraAVStreamMgmtServer()->GetHardPrivacyModeOn(); - return CHIP_NO_ERROR; +CHIP_ERROR CameraAVStreamManager::IsHardPrivacyModeActive(bool & isActive) +{ + // Check privacy mode attributes + isActive = GetCameraAVStreamManagementCluster()->GetHardPrivacyModeOn(); + return CHIP_NO_ERROR; } CHIP_ERROR -CameraAVStreamManager::IsSoftRecordingPrivacyModeActive(bool &isActive) { - // Check privacy mode attributes - isActive = - GetCameraAVStreamMgmtServer()->GetSoftRecordingPrivacyModeEnabled(); - return CHIP_NO_ERROR; +CameraAVStreamManager::IsSoftRecordingPrivacyModeActive(bool & isActive) +{ + // Check privacy mode attributes + isActive = GetCameraAVStreamManagementCluster()->GetSoftRecordingPrivacyModeEnabled(); + return CHIP_NO_ERROR; } CHIP_ERROR -CameraAVStreamManager::IsSoftLivestreamPrivacyModeActive(bool &isActive) { - // Check privacy mode attributes - isActive = - GetCameraAVStreamMgmtServer()->GetSoftLivestreamPrivacyModeEnabled(); - return CHIP_NO_ERROR; +CameraAVStreamManager::IsSoftLivestreamPrivacyModeActive(bool & isActive) +{ + // Check privacy mode attributes + isActive = GetCameraAVStreamManagementCluster()->GetSoftLivestreamPrivacyModeEnabled(); + return CHIP_NO_ERROR; } -CHIP_ERROR CameraAVStreamManager::SetHardPrivacyModeOn(bool hardPrivacyMode) { - return GetCameraAVStreamMgmtServer()->SetHardPrivacyModeOn(hardPrivacyMode); +CHIP_ERROR CameraAVStreamManager::SetHardPrivacyModeOn(bool hardPrivacyMode) +{ + return GetCameraAVStreamManagementCluster()->SetHardPrivacyModeOn(hardPrivacyMode); } -bool CameraAVStreamManager::HasAllocatedVideoStreams() { - const std::vector &allocatedVideoStreams = - GetCameraAVStreamMgmtServer()->GetAllocatedVideoStreams(); - return !allocatedVideoStreams.empty(); +bool CameraAVStreamManager::HasAllocatedVideoStreams() +{ + const std::vector & allocatedVideoStreams = GetCameraAVStreamManagementCluster()->GetAllocatedVideoStreams(); + return !allocatedVideoStreams.empty(); } -bool CameraAVStreamManager::HasAllocatedAudioStreams() { - const std::vector &allocatedAudioStreams = - GetCameraAVStreamMgmtServer()->GetAllocatedAudioStreams(); - return !allocatedAudioStreams.empty(); +bool CameraAVStreamManager::HasAllocatedAudioStreams() +{ + const std::vector & allocatedAudioStreams = GetCameraAVStreamManagementCluster()->GetAllocatedAudioStreams(); + return !allocatedAudioStreams.empty(); } -Protocols::InteractionModel::Status CameraAVStreamManager::VideoStreamAllocate( - const VideoStreamStruct &allocateArgs, uint16_t &outStreamID) { - outStreamID = kInvalidStreamID; - bool isRequestSupportedByAnyAvailableStream = false; +Protocols::InteractionModel::Status CameraAVStreamManager::VideoStreamAllocate(const VideoStreamStruct & allocateArgs, + uint16_t & outStreamID) +{ + outStreamID = kInvalidStreamID; + bool isRequestSupportedByAnyAvailableStream = false; - // Check if allocation request can be supported - for (const auto &stream : - mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams()) { - if (stream.IsCompatible(allocateArgs)) { - isRequestSupportedByAnyAvailableStream = true; - break; + // Check if allocation request can be supported + for (const auto & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams()) + { + if (stream.IsCompatible(allocateArgs)) + { + isRequestSupportedByAnyAvailableStream = true; + break; + } } - } - if (!isRequestSupportedByAnyAvailableStream) { - return Status::DynamicConstraintError; - } - - // Try to reuse an allocated stream - std::optional reusableStreamId = - GetCameraAVStreamMgmtServer()->GetReusableVideoStreamId(allocateArgs); - - if (reusableStreamId.has_value()) { - // Found a stream that can be reused - outStreamID = reusableStreamId.value(); - ChipLogProgress(Camera, "Matching pre-allocated stream with ID: %d exists", - outStreamID); - return Status::Success; - } - - // Try to find an unused compatible available stream - for (auto &stream : - mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams()) { - if (!stream.isAllocated && stream.IsCompatible(allocateArgs)) { - uint32_t candidateEncodedPixelRate = allocateArgs.maxFrameRate * - allocateArgs.maxResolution.height * - allocateArgs.maxResolution.width; - bool encoderRequired = true; - if (!GetCameraAVStreamMgmtServer() - ->IsResourceAvailableForStreamAllocation( - candidateEncodedPixelRate, encoderRequired)) { - return Status::ResourceExhausted; - } - stream.isAllocated = true; - outStreamID = stream.videoStreamParams.videoStreamID; - - // Set the default viewport on the newly allocated stream - mCameraDeviceHAL->GetCameraHALInterface().SetViewport( - stream, mCameraDeviceHAL->GetCameraHALInterface().GetViewport()); - - // Set the current frame rate attribute from HAL - GetCameraAVStreamMgmtServer()->SetCurrentFrameRate( - mCameraDeviceHAL->GetCameraHALInterface().GetCurrentFrameRate()); - - return Status::Success; + if (!isRequestSupportedByAnyAvailableStream) + { + return Status::DynamicConstraintError; } - } - // No compatible stream available for use. - return Status::ResourceExhausted; -} + // Try to reuse an allocated stream + std::optional reusableStreamId = GetCameraAVStreamManagementCluster()->GetReusableVideoStreamId(allocateArgs); -void CameraAVStreamManager::OnVideoStreamAllocated( - const VideoStreamStruct &allocatedStream, StreamAllocationAction action) { - switch (action) { - case StreamAllocationAction::kNewAllocation: - ChipLogProgress(Camera, "Starting new video stream with ID: %u", - allocatedStream.videoStreamID); - // mCameraDeviceHAL->GetCameraHALInterface().StartVideoStream(allocatedStream); - - // Set the current frame rate attribute from HAL once stream has started - GetCameraAVStreamMgmtServer()->SetCurrentFrameRate( - mCameraDeviceHAL->GetCameraHALInterface().GetCurrentFrameRate()); - break; - - case StreamAllocationAction::kModification: - // Find the stream and restart it with new parameters - for (VideoStream &stream : - mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams()) { - if (stream.videoStreamParams.videoStreamID == - allocatedStream.videoStreamID && - stream.isAllocated) { - // For modifications, we always stop and restart the stream to ensure - // new parameters are applied - ChipLogProgress( - Camera, "Restarting video stream with ID: %u due to modifications", - allocatedStream.videoStreamID); - break; - } - } - break; - - case StreamAllocationAction::kReuse: - ChipLogProgress(Camera, - "Reusing existing video stream with ID: %u without changes", - allocatedStream.videoStreamID); - break; - } -} - -Protocols::InteractionModel::Status CameraAVStreamManager::VideoStreamModify( - const uint16_t streamID, const chip::Optional waterMarkEnabled, - const chip::Optional osdEnabled) { - for (VideoStream &stream : - mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams()) { - if (stream.videoStreamParams.videoStreamID == streamID && - stream.isAllocated) { - // TODO: Link with HAL APIs to return error - if (waterMarkEnabled.HasValue()) { - stream.videoStreamParams.watermarkEnabled = waterMarkEnabled; - } - if (osdEnabled.HasValue()) { - stream.videoStreamParams.OSDEnabled = osdEnabled; - } - ChipLogError(Camera, "Modified video stream with ID: %d", streamID); - return Status::Success; - } - } - - ChipLogError(Camera, "Allocated video stream with ID: %d not found", - streamID); - - return Status::NotFound; -} - -Protocols::InteractionModel::Status -CameraAVStreamManager::VideoStreamDeallocate(const uint16_t streamID) { - for (VideoStream &stream : - mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams()) { - if (stream.videoStreamParams.videoStreamID == streamID && - stream.isAllocated) { - stream.isAllocated = false; - return Status::Success; - } - } - - ChipLogError(Camera, - "Allocated video stream with ID: %d not found internally", - streamID); - - return Status::NotFound; -} - -Protocols::InteractionModel::Status CameraAVStreamManager::AudioStreamAllocate( - const AudioStreamStruct &allocateArgs, uint16_t &outStreamID) { - outStreamID = kInvalidStreamID; - - for (AudioStream &stream : - mCameraDeviceHAL->GetCameraHALInterface().GetAvailableAudioStreams()) { - if (stream.IsCompatible(allocateArgs)) { - outStreamID = stream.audioStreamParams.audioStreamID; - if (!stream.isAllocated) { - stream.isAllocated = true; + if (reusableStreamId.has_value()) + { + // Found a stream that can be reused + outStreamID = reusableStreamId.value(); + ChipLogProgress(Camera, "Matching pre-allocated stream with ID: %d exists", outStreamID); return Status::Success; - } else { - ChipLogProgress(Camera, - "Matching pre-allocated stream with ID: %d exists", - outStreamID); - } - return Status::Success; } - } - return Status::DynamicConstraintError; -} + // Try to find an unused compatible available stream + for (auto & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams()) + { + if (!stream.isAllocated && stream.IsCompatible(allocateArgs)) + { + uint32_t candidateEncodedPixelRate = + allocateArgs.maxFrameRate * allocateArgs.maxResolution.height * allocateArgs.maxResolution.width; + bool encoderRequired = true; + if (!GetCameraAVStreamManagementCluster()->IsResourceAvailableForStreamAllocation(candidateEncodedPixelRate, + encoderRequired)) + { + return Status::ResourceExhausted; + } + stream.isAllocated = true; + outStreamID = stream.videoStreamParams.videoStreamID; -Protocols::InteractionModel::Status -CameraAVStreamManager::AudioStreamDeallocate(const uint16_t streamID) { - for (AudioStream &stream : - mCameraDeviceHAL->GetCameraHALInterface().GetAvailableAudioStreams()) { - if (stream.audioStreamParams.audioStreamID == streamID && - stream.isAllocated) { - stream.isAllocated = false; - return Status::Success; + // Set the default viewport on the newly allocated stream + mCameraDeviceHAL->GetCameraHALInterface().SetViewport(stream, mCameraDeviceHAL->GetCameraHALInterface().GetViewport()); + + // Set the current frame rate attribute from HAL + GetCameraAVStreamManagementCluster()->SetCurrentFrameRate( + mCameraDeviceHAL->GetCameraHALInterface().GetCurrentFrameRate()); + + return Status::Success; + } } - } - ChipLogError(Camera, - "Allocated audio stream with ID: %d not found internally", - streamID); - - return Status::NotFound; -} - -Protocols::InteractionModel::Status -CameraAVStreamManager::SnapshotStreamAllocate( - const SnapshotStreamAllocateArgs &allocateArgs, uint16_t &outStreamID) { - outStreamID = kInvalidStreamID; - bool isRequestSupportedByAnyAvailableStream = false; - - // Check if allocation request can be supported - for (const auto &stream : mCameraDeviceHAL->GetCameraHALInterface() - .GetAvailableSnapshotStreams()) { - if (stream.IsCompatible(allocateArgs)) { - isRequestSupportedByAnyAvailableStream = true; - break; - } - } - - if (!isRequestSupportedByAnyAvailableStream) { - return Status::DynamicConstraintError; - } - - // Try to reuse an allocated stream. - std::optional reusableStreamId = - GetCameraAVStreamMgmtServer()->GetReusableSnapshotStreamId(allocateArgs); - - if (reusableStreamId.has_value()) { - // Found a stream that can be reused - outStreamID = reusableStreamId.value(); - ChipLogProgress(Camera, "Matching pre-allocated stream with ID: %d exists", - outStreamID); - return Status::Success; - } - - uint32_t candidateEncodedPixelRate = 0; - bool encoderRequired = false; - if (allocateArgs.encodedPixels) { - candidateEncodedPixelRate += allocateArgs.maxFrameRate * - allocateArgs.maxResolution.height * - allocateArgs.maxResolution.width; - if (allocateArgs.hardwareEncoder) { - encoderRequired = true; - } - } - - if (!GetCameraAVStreamMgmtServer()->IsResourceAvailableForStreamAllocation( - candidateEncodedPixelRate, encoderRequired)) { + // No compatible stream available for use. return Status::ResourceExhausted; - } +} - // If no pre-allocated stream matches, try allocating a new one. - if (mCameraDeviceHAL->GetCameraHALInterface().AllocateSnapshotStream( - allocateArgs, outStreamID) == CameraError::SUCCESS) { - return Status::Success; - } +void CameraAVStreamManager::OnVideoStreamAllocated(const VideoStreamStruct & allocatedStream, StreamAllocationAction action) +{ + switch (action) + { + case StreamAllocationAction::kNewAllocation: + ChipLogProgress(Camera, "Starting new video stream with ID: %u", allocatedStream.videoStreamID); + // mCameraDeviceHAL->GetCameraHALInterface().StartVideoStream(allocatedStream); - // Try to find an unused compatible available stream - for (auto &stream : mCameraDeviceHAL->GetCameraHALInterface() - .GetAvailableSnapshotStreams()) { - if (!stream.isAllocated && stream.IsCompatible(allocateArgs)) { - stream.isAllocated = true; - outStreamID = stream.snapshotStreamParams.snapshotStreamID; + // Set the current frame rate attribute from HAL once stream has started + GetCameraAVStreamManagementCluster()->SetCurrentFrameRate(mCameraDeviceHAL->GetCameraHALInterface().GetCurrentFrameRate()); + break; - // Set the optional Watermark and OSD values that may have been provided. - // This is the initial setting of these values, they may be subsequently - // modified. If the values have no value that is ok, the allocated stream - // will store as such and ignore. - stream.snapshotStreamParams.watermarkEnabled = - allocateArgs.watermarkEnabled; - stream.snapshotStreamParams.OSDEnabled = allocateArgs.OSDEnabled; + case StreamAllocationAction::kModification: + // Find the stream and restart it with new parameters + for (VideoStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams()) + { + if (stream.videoStreamParams.videoStreamID == allocatedStream.videoStreamID && stream.isAllocated) + { + // For modifications, we always stop and restart the stream to ensure + // new parameters are applied + ChipLogProgress(Camera, "Restarting video stream with ID: %u due to modifications", allocatedStream.videoStreamID); + break; + } + } + break; - return Status::Success; + case StreamAllocationAction::kReuse: + ChipLogProgress(Camera, "Reusing existing video stream with ID: %u without changes", allocatedStream.videoStreamID); + break; } - } - - // No compatible stream available for use. - return Status::ResourceExhausted; } -Protocols::InteractionModel::Status CameraAVStreamManager::SnapshotStreamModify( - const uint16_t streamID, const chip::Optional waterMarkEnabled, - const chip::Optional osdEnabled) { - for (SnapshotStream &stream : mCameraDeviceHAL->GetCameraHALInterface() - .GetAvailableSnapshotStreams()) { - if (stream.snapshotStreamParams.snapshotStreamID == streamID && - stream.isAllocated) { - // TODO: Link with HAL APIs to return error - if (waterMarkEnabled.HasValue()) { - stream.snapshotStreamParams.watermarkEnabled = waterMarkEnabled; - } - if (osdEnabled.HasValue()) { - stream.snapshotStreamParams.OSDEnabled = osdEnabled; - } - ChipLogError(Camera, "Modified snapshot stream with ID: %d", streamID); - return Status::Success; +Protocols::InteractionModel::Status CameraAVStreamManager::VideoStreamModify(const uint16_t streamID, + const chip::Optional waterMarkEnabled, + const chip::Optional osdEnabled) +{ + for (VideoStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams()) + { + if (stream.videoStreamParams.videoStreamID == streamID && stream.isAllocated) + { + // TODO: Link with HAL APIs to return error + if (waterMarkEnabled.HasValue()) + { + stream.videoStreamParams.watermarkEnabled = waterMarkEnabled; + } + if (osdEnabled.HasValue()) + { + stream.videoStreamParams.OSDEnabled = osdEnabled; + } + ChipLogError(Camera, "Modified video stream with ID: %d", streamID); + return Status::Success; + } } - } - ChipLogError(Camera, "Allocated snapshot stream with ID: %d not found", - streamID); + ChipLogError(Camera, "Allocated video stream with ID: %d not found", streamID); - return Status::NotFound; + return Status::NotFound; } -Protocols::InteractionModel::Status -CameraAVStreamManager::SnapshotStreamDeallocate(const uint16_t streamID) { - for (SnapshotStream &stream : mCameraDeviceHAL->GetCameraHALInterface() - .GetAvailableSnapshotStreams()) { - if (stream.snapshotStreamParams.snapshotStreamID == streamID && - stream.isAllocated) { - if (stream.snapshotStreamParams.referenceCount > 0) { - ChipLogError(Camera, "Snapshot stream with ID: %d still in use", - streamID); - return Status::InvalidInState; - } - stream.isAllocated = false; - - return Status::Success; +Protocols::InteractionModel::Status CameraAVStreamManager::VideoStreamDeallocate(const uint16_t streamID) +{ + for (VideoStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams()) + { + if (stream.videoStreamParams.videoStreamID == streamID && stream.isAllocated) + { + stream.isAllocated = false; + return Status::Success; + } } - } - ChipLogError(Camera, "Allocated snapshot stream with ID: %d not found", - streamID); + ChipLogError(Camera, "Allocated video stream with ID: %d not found internally", streamID); - return Status::NotFound; + return Status::NotFound; } -void CameraAVStreamManager::OnStreamUsagePrioritiesChanged() { - ChipLogProgress(Camera, "Stream usage priorities changed"); - mCameraDeviceHAL->GetCameraHALInterface().SetStreamUsagePriorities( - GetCameraAVStreamMgmtServer()->GetStreamUsagePriorities()); -} +Protocols::InteractionModel::Status CameraAVStreamManager::AudioStreamAllocate(const AudioStreamStruct & allocateArgs, + uint16_t & outStreamID) +{ + outStreamID = kInvalidStreamID; -void CameraAVStreamManager::OnAttributeChanged(AttributeId attributeId) { - ChipLogProgress(Camera, - "Attribute changed for AttributeId = " ChipLogFormatMEI, - ChipLogValueMEI(attributeId)); - - switch (attributeId) { - case HDRModeEnabled::Id: { - - mCameraDeviceHAL->GetCameraHALInterface().SetHDRMode( - GetCameraAVStreamMgmtServer()->GetHDRModeEnabled()); - break; - } - case SoftRecordingPrivacyModeEnabled::Id: { - mCameraDeviceHAL->GetCameraHALInterface() - .SetSoftRecordingPrivacyModeEnabled( - GetCameraAVStreamMgmtServer() - ->GetSoftRecordingPrivacyModeEnabled()); - break; - } - case SoftLivestreamPrivacyModeEnabled::Id: { - mCameraDeviceHAL->GetCameraHALInterface() - .SetSoftLivestreamPrivacyModeEnabled( - GetCameraAVStreamMgmtServer() - ->GetSoftLivestreamPrivacyModeEnabled()); - break; - } - case NightVision::Id: { - mCameraDeviceHAL->GetCameraHALInterface().SetNightVision( - GetCameraAVStreamMgmtServer()->GetNightVision()); - break; - } - case NightVisionIllum::Id: { - break; - } - case Viewport::Id: { - // Update the device default - mCameraDeviceHAL->GetCameraHALInterface().SetViewport( - GetCameraAVStreamMgmtServer()->GetViewport()); - - // Update the per stream viewports on the camera - for (VideoStream &stream : - mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams()) { - mCameraDeviceHAL->GetCameraHALInterface().SetViewport( - stream, GetCameraAVStreamMgmtServer()->GetViewport()); + for (AudioStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableAudioStreams()) + { + if (stream.IsCompatible(allocateArgs)) + { + outStreamID = stream.audioStreamParams.audioStreamID; + if (!stream.isAllocated) + { + stream.isAllocated = true; + return Status::Success; + } + else + { + ChipLogProgress(Camera, "Matching pre-allocated stream with ID: %d exists", outStreamID); + } + return Status::Success; + } } - break; - } - case SpeakerMuted::Id: { - mCameraDeviceHAL->GetCameraHALInterface().SetSpeakerMuted( - GetCameraAVStreamMgmtServer()->GetSpeakerMuted()); - break; - } - case SpeakerVolumeLevel::Id: { - mCameraDeviceHAL->GetCameraHALInterface().SetSpeakerVolume( - GetCameraAVStreamMgmtServer()->GetSpeakerVolumeLevel()); - break; - } - case MicrophoneMuted::Id: { - mCameraDeviceHAL->GetCameraHALInterface().SetMicrophoneMuted( - GetCameraAVStreamMgmtServer()->GetMicrophoneMuted()); - break; - } - case MicrophoneVolumeLevel::Id: { - mCameraDeviceHAL->GetCameraHALInterface().SetMicrophoneVolume( - GetCameraAVStreamMgmtServer()->GetMicrophoneVolumeLevel()); - break; - } - case LocalVideoRecordingEnabled::Id: { - mCameraDeviceHAL->GetCameraHALInterface().SetLocalVideoRecordingEnabled( - GetCameraAVStreamMgmtServer()->GetLocalVideoRecordingEnabled()); - break; - } - case LocalSnapshotRecordingEnabled::Id: { - mCameraDeviceHAL->GetCameraHALInterface().SetLocalSnapshotRecordingEnabled( - GetCameraAVStreamMgmtServer()->GetLocalSnapshotRecordingEnabled()); - break; - } - case StatusLightEnabled::Id: { - mCameraDeviceHAL->GetCameraHALInterface().SetStatusLightEnabled( - GetCameraAVStreamMgmtServer()->GetStatusLightEnabled()); - break; - } - case ImageRotation::Id: { - mCameraDeviceHAL->GetCameraHALInterface().SetImageRotation( - GetCameraAVStreamMgmtServer()->GetImageRotation()); - break; - } - case ImageFlipHorizontal::Id: { - mCameraDeviceHAL->GetCameraHALInterface().SetImageFlipHorizontal( - GetCameraAVStreamMgmtServer()->GetImageFlipHorizontal()); - break; - } - case ImageFlipVertical::Id: { - mCameraDeviceHAL->GetCameraHALInterface().SetImageFlipVertical( - GetCameraAVStreamMgmtServer()->GetImageFlipVertical()); - break; - } - default: - ChipLogProgress(Camera, - "Unknown Attribute with AttributeId = " ChipLogFormatMEI, - ChipLogValueMEI(attributeId)); - } + + return Status::DynamicConstraintError; } -Protocols::InteractionModel::Status -CameraAVStreamManager::CaptureSnapshot(const Nullable streamID, - const VideoResolutionStruct &resolution, - ImageSnapshot &outImageSnapshot) { - if (mCameraDeviceHAL->GetCameraHALInterface().CaptureSnapshot( - streamID, resolution, outImageSnapshot) == CameraError::SUCCESS) { - return Status::Success; - } else { +Protocols::InteractionModel::Status CameraAVStreamManager::AudioStreamDeallocate(const uint16_t streamID) +{ + for (AudioStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableAudioStreams()) + { + if (stream.audioStreamParams.audioStreamID == streamID && stream.isAllocated) + { + stream.isAllocated = false; + return Status::Success; + } + } + + ChipLogError(Camera, "Allocated audio stream with ID: %d not found internally", streamID); + + return Status::NotFound; +} + +Protocols::InteractionModel::Status CameraAVStreamManager::SnapshotStreamAllocate(const SnapshotStreamAllocateArgs & allocateArgs, + uint16_t & outStreamID) +{ + outStreamID = kInvalidStreamID; + bool isRequestSupportedByAnyAvailableStream = false; + + // Check if allocation request can be supported + for (const auto & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableSnapshotStreams()) + { + if (stream.IsCompatible(allocateArgs)) + { + isRequestSupportedByAnyAvailableStream = true; + break; + } + } + + if (!isRequestSupportedByAnyAvailableStream) + { + return Status::DynamicConstraintError; + } + + // Try to reuse an allocated stream. + std::optional reusableStreamId = GetCameraAVStreamManagementCluster()->GetReusableSnapshotStreamId(allocateArgs); + + if (reusableStreamId.has_value()) + { + // Found a stream that can be reused + outStreamID = reusableStreamId.value(); + ChipLogProgress(Camera, "Matching pre-allocated stream with ID: %d exists", outStreamID); + return Status::Success; + } + + uint32_t candidateEncodedPixelRate = 0; + bool encoderRequired = false; + if (allocateArgs.encodedPixels) + { + candidateEncodedPixelRate += + allocateArgs.maxFrameRate * allocateArgs.maxResolution.height * allocateArgs.maxResolution.width; + if (allocateArgs.hardwareEncoder) + { + encoderRequired = true; + } + } + + if (!GetCameraAVStreamManagementCluster()->IsResourceAvailableForStreamAllocation(candidateEncodedPixelRate, encoderRequired)) + { + return Status::ResourceExhausted; + } + + // If no pre-allocated stream matches, try allocating a new one. + if (mCameraDeviceHAL->GetCameraHALInterface().AllocateSnapshotStream(allocateArgs, outStreamID) == CameraError::SUCCESS) + { + return Status::Success; + } + + // Try to find an unused compatible available stream + for (auto & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableSnapshotStreams()) + { + if (!stream.isAllocated && stream.IsCompatible(allocateArgs)) + { + stream.isAllocated = true; + outStreamID = stream.snapshotStreamParams.snapshotStreamID; + + // Set the optional Watermark and OSD values that may have been provided. + // This is the initial setting of these values, they may be subsequently + // modified. If the values have no value that is ok, the allocated stream + // will store as such and ignore. + stream.snapshotStreamParams.watermarkEnabled = allocateArgs.watermarkEnabled; + stream.snapshotStreamParams.OSDEnabled = allocateArgs.OSDEnabled; + + return Status::Success; + } + } + + // No compatible stream available for use. + return Status::ResourceExhausted; +} + +Protocols::InteractionModel::Status CameraAVStreamManager::SnapshotStreamModify(const uint16_t streamID, + const chip::Optional waterMarkEnabled, + const chip::Optional osdEnabled) +{ + for (SnapshotStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableSnapshotStreams()) + { + if (stream.snapshotStreamParams.snapshotStreamID == streamID && stream.isAllocated) + { + // TODO: Link with HAL APIs to return error + if (waterMarkEnabled.HasValue()) + { + stream.snapshotStreamParams.watermarkEnabled = waterMarkEnabled; + } + if (osdEnabled.HasValue()) + { + stream.snapshotStreamParams.OSDEnabled = osdEnabled; + } + ChipLogError(Camera, "Modified snapshot stream with ID: %d", streamID); + return Status::Success; + } + } + + ChipLogError(Camera, "Allocated snapshot stream with ID: %d not found", streamID); + + return Status::NotFound; +} + +Protocols::InteractionModel::Status CameraAVStreamManager::SnapshotStreamDeallocate(const uint16_t streamID) +{ + for (SnapshotStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableSnapshotStreams()) + { + if (stream.snapshotStreamParams.snapshotStreamID == streamID && stream.isAllocated) + { + if (stream.snapshotStreamParams.referenceCount > 0) + { + ChipLogError(Camera, "Snapshot stream with ID: %d still in use", streamID); + return Status::InvalidInState; + } + stream.isAllocated = false; + + return Status::Success; + } + } + + ChipLogError(Camera, "Allocated snapshot stream with ID: %d not found", streamID); + + return Status::NotFound; +} + +void CameraAVStreamManager::OnStreamUsagePrioritiesChanged() +{ + ChipLogProgress(Camera, "Stream usage priorities changed"); + mCameraDeviceHAL->GetCameraHALInterface().SetStreamUsagePriorities( + GetCameraAVStreamManagementCluster()->GetStreamUsagePriorities()); +} + +void CameraAVStreamManager::OnAttributeChanged(AttributeId attributeId) +{ + ChipLogProgress(Camera, "Attribute changed for AttributeId = " ChipLogFormatMEI, ChipLogValueMEI(attributeId)); + + switch (attributeId) + { + case HDRModeEnabled::Id: { + + mCameraDeviceHAL->GetCameraHALInterface().SetHDRMode(GetCameraAVStreamManagementCluster()->GetHDRModeEnabled()); + break; + } + case SoftRecordingPrivacyModeEnabled::Id: { + mCameraDeviceHAL->GetCameraHALInterface().SetSoftRecordingPrivacyModeEnabled( + GetCameraAVStreamManagementCluster()->GetSoftRecordingPrivacyModeEnabled()); + break; + } + case SoftLivestreamPrivacyModeEnabled::Id: { + mCameraDeviceHAL->GetCameraHALInterface().SetSoftLivestreamPrivacyModeEnabled( + GetCameraAVStreamManagementCluster()->GetSoftLivestreamPrivacyModeEnabled()); + break; + } + case NightVision::Id: { + mCameraDeviceHAL->GetCameraHALInterface().SetNightVision(GetCameraAVStreamManagementCluster()->GetNightVision()); + break; + } + case NightVisionIllum::Id: { + break; + } + case Viewport::Id: { + // Update the device default + mCameraDeviceHAL->GetCameraHALInterface().SetViewport(GetCameraAVStreamManagementCluster()->GetViewport()); + + // Update the per stream viewports on the camera + for (VideoStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams()) + { + mCameraDeviceHAL->GetCameraHALInterface().SetViewport(stream, GetCameraAVStreamManagementCluster()->GetViewport()); + } + break; + } + case SpeakerMuted::Id: { + mCameraDeviceHAL->GetCameraHALInterface().SetSpeakerMuted(GetCameraAVStreamManagementCluster()->GetSpeakerMuted()); + break; + } + case SpeakerVolumeLevel::Id: { + mCameraDeviceHAL->GetCameraHALInterface().SetSpeakerVolume(GetCameraAVStreamManagementCluster()->GetSpeakerVolumeLevel()); + break; + } + case MicrophoneMuted::Id: { + mCameraDeviceHAL->GetCameraHALInterface().SetMicrophoneMuted(GetCameraAVStreamManagementCluster()->GetMicrophoneMuted()); + break; + } + case MicrophoneVolumeLevel::Id: { + mCameraDeviceHAL->GetCameraHALInterface().SetMicrophoneVolume( + GetCameraAVStreamManagementCluster()->GetMicrophoneVolumeLevel()); + break; + } + case LocalVideoRecordingEnabled::Id: { + mCameraDeviceHAL->GetCameraHALInterface().SetLocalVideoRecordingEnabled( + GetCameraAVStreamManagementCluster()->GetLocalVideoRecordingEnabled()); + break; + } + case LocalSnapshotRecordingEnabled::Id: { + mCameraDeviceHAL->GetCameraHALInterface().SetLocalSnapshotRecordingEnabled( + GetCameraAVStreamManagementCluster()->GetLocalSnapshotRecordingEnabled()); + break; + } + case StatusLightEnabled::Id: { + mCameraDeviceHAL->GetCameraHALInterface().SetStatusLightEnabled( + GetCameraAVStreamManagementCluster()->GetStatusLightEnabled()); + break; + } + case ImageRotation::Id: { + mCameraDeviceHAL->GetCameraHALInterface().SetImageRotation(GetCameraAVStreamManagementCluster()->GetImageRotation()); + break; + } + case ImageFlipHorizontal::Id: { + mCameraDeviceHAL->GetCameraHALInterface().SetImageFlipHorizontal( + GetCameraAVStreamManagementCluster()->GetImageFlipHorizontal()); + break; + } + case ImageFlipVertical::Id: { + mCameraDeviceHAL->GetCameraHALInterface().SetImageFlipVertical( + GetCameraAVStreamManagementCluster()->GetImageFlipVertical()); + break; + } + default: + ChipLogProgress(Camera, "Unknown Attribute with AttributeId = " ChipLogFormatMEI, ChipLogValueMEI(attributeId)); + } +} + +Protocols::InteractionModel::Status CameraAVStreamManager::CaptureSnapshot(const Nullable streamID, + const VideoResolutionStruct & resolution, + ImageSnapshot & outImageSnapshot) +{ + if (mCameraDeviceHAL->GetCameraHALInterface().CaptureSnapshot(streamID, resolution, outImageSnapshot) == CameraError::SUCCESS) + { + return Status::Success; + } + else + { + return Status::Failure; + } + return Status::Failure; - } - - return Status::Failure; } CHIP_ERROR -CameraAVStreamManager::AllocatedVideoStreamsLoaded() { - const std::vector &persistedStreams = - GetCameraAVStreamMgmtServer()->GetAllocatedVideoStreams(); - auto &halStreams = - mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams(); +CameraAVStreamManager::AllocatedVideoStreamsLoaded() +{ + const std::vector & persistedStreams = GetCameraAVStreamManagementCluster()->GetAllocatedVideoStreams(); + auto & halStreams = mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams(); - for (auto &halStream : halStreams) { - auto it = std::find_if(persistedStreams.begin(), persistedStreams.end(), - [&](const VideoStreamStruct &persistedStream) { - return persistedStream.videoStreamID == - halStream.videoStreamParams.videoStreamID; - }); + for (auto & halStream : halStreams) + { + auto it = std::find_if(persistedStreams.begin(), persistedStreams.end(), [&](const VideoStreamStruct & persistedStream) { + return persistedStream.videoStreamID == halStream.videoStreamParams.videoStreamID; + }); - if (it != persistedStreams.end()) { - // Found in persisted streams, mark as allocated in HAL - halStream.isAllocated = true; - ChipLogProgress( - Camera, - "HAL Video Stream ID %u marked as allocated from persisted state.", - halStream.videoStreamParams.videoStreamID); + if (it != persistedStreams.end()) + { + // Found in persisted streams, mark as allocated in HAL + halStream.isAllocated = true; + ChipLogProgress(Camera, "HAL Video Stream ID %u marked as allocated from persisted state.", + halStream.videoStreamParams.videoStreamID); - // Signal for starting the video stream - OnVideoStreamAllocated(*it, StreamAllocationAction::kNewAllocation); + // Signal for starting the video stream + OnVideoStreamAllocated(*it, StreamAllocationAction::kNewAllocation); + } } - } - return CHIP_NO_ERROR; + return CHIP_NO_ERROR; } CHIP_ERROR -CameraAVStreamManager::AllocatedAudioStreamsLoaded() { - const std::vector &persistedStreams = - GetCameraAVStreamMgmtServer()->GetAllocatedAudioStreams(); - auto &halStreams = - mCameraDeviceHAL->GetCameraHALInterface().GetAvailableAudioStreams(); +CameraAVStreamManager::AllocatedAudioStreamsLoaded() +{ + const std::vector & persistedStreams = GetCameraAVStreamManagementCluster()->GetAllocatedAudioStreams(); + auto & halStreams = mCameraDeviceHAL->GetCameraHALInterface().GetAvailableAudioStreams(); - for (auto &halStream : halStreams) { - auto it = std::find_if(persistedStreams.begin(), persistedStreams.end(), - [&](const AudioStreamStruct &persistedStream) { - return persistedStream.audioStreamID == - halStream.audioStreamParams.audioStreamID; - }); + for (auto & halStream : halStreams) + { + auto it = std::find_if(persistedStreams.begin(), persistedStreams.end(), [&](const AudioStreamStruct & persistedStream) { + return persistedStream.audioStreamID == halStream.audioStreamParams.audioStreamID; + }); - if (it != persistedStreams.end()) { - // Found in persisted streams, mark as allocated in HAL - halStream.isAllocated = true; - ChipLogProgress( - Camera, - "HAL Audio Stream ID %u marked as allocated from persisted state.", - halStream.audioStreamParams.audioStreamID); + if (it != persistedStreams.end()) + { + // Found in persisted streams, mark as allocated in HAL + halStream.isAllocated = true; + ChipLogProgress(Camera, "HAL Audio Stream ID %u marked as allocated from persisted state.", + halStream.audioStreamParams.audioStreamID); + } } - } - return CHIP_NO_ERROR; + return CHIP_NO_ERROR; } CHIP_ERROR -CameraAVStreamManager::AllocatedSnapshotStreamsLoaded() { - ChipLogError( - Camera, - "Allocated snapshot streams could not be loaded: %" CHIP_ERROR_FORMAT, - CHIP_ERROR_NOT_IMPLEMENTED.Format()); - return CHIP_ERROR_NOT_IMPLEMENTED; +CameraAVStreamManager::AllocatedSnapshotStreamsLoaded() +{ + ChipLogError(Camera, "Allocated snapshot streams could not be loaded: %" CHIP_ERROR_FORMAT, + CHIP_ERROR_NOT_IMPLEMENTED.Format()); + return CHIP_ERROR_NOT_IMPLEMENTED; } CHIP_ERROR -CameraAVStreamManager::PersistentAttributesLoadedCallback() { - ChipLogDetail(Camera, "Successfully loaded persistent attributes"); +CameraAVStreamManager::PersistentAttributesLoadedCallback() +{ + ChipLogDetail(Camera, "Successfully loaded persistent attributes"); - CHIP_ERROR err = AllocatedVideoStreamsLoaded(); - if (err != CHIP_NO_ERROR) { - ChipLogError( - Camera, - "Allocated video streams could not be loaded: %" CHIP_ERROR_FORMAT, - err.Format()); - return err; - } + CHIP_ERROR err = AllocatedVideoStreamsLoaded(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Camera, "Allocated video streams could not be loaded: %" CHIP_ERROR_FORMAT, err.Format()); + return err; + } - err = AllocatedAudioStreamsLoaded(); - if (err != CHIP_NO_ERROR) { - ChipLogError( - Camera, - "Allocated audio streams could not be loaded: %" CHIP_ERROR_FORMAT, - err.Format()); - return err; - } + err = AllocatedAudioStreamsLoaded(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Camera, "Allocated audio streams could not be loaded: %" CHIP_ERROR_FORMAT, err.Format()); + return err; + } - err = AllocatedSnapshotStreamsLoaded(); - if (err != CHIP_NO_ERROR) { - ChipLogError( - Camera, - "Allocated snapshot streams could not be loaded: %" CHIP_ERROR_FORMAT, - err.Format()); - return err; - } + err = AllocatedSnapshotStreamsLoaded(); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Camera, "Allocated snapshot streams could not be loaded: %" CHIP_ERROR_FORMAT, err.Format()); + return err; + } - return CHIP_NO_ERROR; + return CHIP_NO_ERROR; } CHIP_ERROR -CameraAVStreamManager::OnTransportAcquireAudioVideoStreams( - uint16_t audioStreamID, uint16_t videoStreamID) { - // Update the available audio stream in the HAL - for (AudioStream &stream : - mCameraDeviceHAL->GetCameraHALInterface().GetAvailableAudioStreams()) { - if (stream.audioStreamParams.audioStreamID == audioStreamID && - stream.isAllocated) { - if (stream.audioStreamParams.referenceCount < UINT8_MAX) { - stream.audioStreamParams.referenceCount++; - } else { - ChipLogError( - Camera, - "Attempted to increment audio stream %u ref count beyond max limit", - audioStreamID); - } +CameraAVStreamManager::OnTransportAcquireAudioVideoStreams(uint16_t audioStreamID, uint16_t videoStreamID) +{ + // Update the available audio stream in the HAL + for (AudioStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableAudioStreams()) + { + if (stream.audioStreamParams.audioStreamID == audioStreamID && stream.isAllocated) + { + if (stream.audioStreamParams.referenceCount < UINT8_MAX) + { + stream.audioStreamParams.referenceCount++; + } + else + { + ChipLogError(Camera, "Attempted to increment audio stream %u ref count beyond max limit", audioStreamID); + } + } } - } - // Update the available video stream in the HAL - for (VideoStream &stream : - mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams()) { - if (stream.videoStreamParams.videoStreamID == videoStreamID && - stream.isAllocated) { - if (stream.videoStreamParams.referenceCount < UINT8_MAX) { - stream.videoStreamParams.referenceCount++; - } else { - ChipLogError( - Camera, - "Attempted to increment video stream %u ref count beyond max limit", - videoStreamID); - } + // Update the available video stream in the HAL + for (VideoStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams()) + { + if (stream.videoStreamParams.videoStreamID == videoStreamID && stream.isAllocated) + { + if (stream.videoStreamParams.referenceCount < UINT8_MAX) + { + stream.videoStreamParams.referenceCount++; + } + else + { + ChipLogError(Camera, "Attempted to increment video stream %u ref count beyond max limit", videoStreamID); + } + } } - } - // Update the counts in the SDK allocated stream attributes - if (GetCameraAVStreamMgmtServer()->UpdateAudioStreamRefCount( - audioStreamID, /* shouldIncrement = */ true) != CHIP_NO_ERROR) { - ChipLogError(Camera, "Failed to increment audio stream %u ref count in SDK", - audioStreamID); - } + // Update the counts in the SDK allocated stream attributes + if (GetCameraAVStreamManagementCluster()->UpdateAudioStreamRefCount(audioStreamID, /* shouldIncrement = */ true) != + CHIP_NO_ERROR) + { + ChipLogError(Camera, "Failed to increment audio stream %u ref count in SDK", audioStreamID); + } - if (GetCameraAVStreamMgmtServer()->UpdateVideoStreamRefCount( - videoStreamID, /* shouldIncrement = */ true) != CHIP_NO_ERROR) { - ChipLogError(Camera, "Failed to increment video stream %u ref count in SDK", - videoStreamID); - } + if (GetCameraAVStreamManagementCluster()->UpdateVideoStreamRefCount(videoStreamID, /* shouldIncrement = */ true) != + CHIP_NO_ERROR) + { + ChipLogError(Camera, "Failed to increment video stream %u ref count in SDK", videoStreamID); + } - return CHIP_NO_ERROR; + return CHIP_NO_ERROR; } CHIP_ERROR -CameraAVStreamManager::OnTransportReleaseAudioVideoStreams( - uint16_t audioStreamID, uint16_t videoStreamID) { - // Update the available audio stream in the HAL - for (AudioStream &stream : - mCameraDeviceHAL->GetCameraHALInterface().GetAvailableAudioStreams()) { - if (stream.audioStreamParams.audioStreamID == audioStreamID && - stream.isAllocated) { - if (stream.audioStreamParams.referenceCount > 0) { - stream.audioStreamParams.referenceCount--; - } else { - ChipLogError(Camera, - "Attempted to decrement audio stream %u ref count when it " - "was already 0", - audioStreamID); - } +CameraAVStreamManager::OnTransportReleaseAudioVideoStreams(uint16_t audioStreamID, uint16_t videoStreamID) +{ + // Update the available audio stream in the HAL + for (AudioStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableAudioStreams()) + { + if (stream.audioStreamParams.audioStreamID == audioStreamID && stream.isAllocated) + { + if (stream.audioStreamParams.referenceCount > 0) + { + stream.audioStreamParams.referenceCount--; + } + else + { + ChipLogError(Camera, + "Attempted to decrement audio stream %u ref count when it " + "was already 0", + audioStreamID); + } + } } - } - // Update the available video stream in the HAL - for (VideoStream &stream : - mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams()) { - if (stream.videoStreamParams.videoStreamID == videoStreamID && - stream.isAllocated) { - if (stream.videoStreamParams.referenceCount > 0) { - stream.videoStreamParams.referenceCount--; - } else { - ChipLogError(Camera, - "Attempted to decrement video stream %u ref count when it " - "was already 0", - videoStreamID); - } + // Update the available video stream in the HAL + for (VideoStream & stream : mCameraDeviceHAL->GetCameraHALInterface().GetAvailableVideoStreams()) + { + if (stream.videoStreamParams.videoStreamID == videoStreamID && stream.isAllocated) + { + if (stream.videoStreamParams.referenceCount > 0) + { + stream.videoStreamParams.referenceCount--; + } + else + { + ChipLogError(Camera, + "Attempted to decrement video stream %u ref count when it " + "was already 0", + videoStreamID); + } + } } - } - // Update the counts in the SDK allocated stream attributes - if (GetCameraAVStreamMgmtServer()->UpdateAudioStreamRefCount( - audioStreamID, /* shouldIncrement = */ false) != CHIP_NO_ERROR) { - ChipLogError(Camera, "Failed to decrement audio stream %u ref count in SDK", - audioStreamID); - } + // Update the counts in the SDK allocated stream attributes + if (GetCameraAVStreamManagementCluster()->UpdateAudioStreamRefCount(audioStreamID, /* shouldIncrement = */ false) != + CHIP_NO_ERROR) + { + ChipLogError(Camera, "Failed to decrement audio stream %u ref count in SDK", audioStreamID); + } - if (GetCameraAVStreamMgmtServer()->UpdateVideoStreamRefCount( - videoStreamID, /* shouldIncrement = */ false) != CHIP_NO_ERROR) { - ChipLogError(Camera, "Failed to decrement video stream %u ref count in SDK", - videoStreamID); - } + if (GetCameraAVStreamManagementCluster()->UpdateVideoStreamRefCount(videoStreamID, /* shouldIncrement = */ false) != + CHIP_NO_ERROR) + { + ChipLogError(Camera, "Failed to decrement video stream %u ref count in SDK", videoStreamID); + } - return CHIP_NO_ERROR; + return CHIP_NO_ERROR; } diff --git a/examples/camera/main/clusters/camera-av-stream-manager.h b/examples/camera/main/clusters/camera-av-stream-manager.h index 494aaf8f3..c3819b515 100644 --- a/examples/camera/main/clusters/camera-av-stream-manager.h +++ b/examples/camera/main/clusters/camera-av-stream-manager.h @@ -1,8 +1,25 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * All rights reserved. + * + * 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. + */ #pragma once #include "camera-avstream-controller.h" #include "camera-device-interface.h" -#include +#include #include #include @@ -14,112 +31,90 @@ namespace CameraAvStreamManagement { /** * The application delegate to define the options & implement commands. */ -class CameraAVStreamManager : public CameraAVStreamMgmtDelegate, - public CameraAVStreamController { +class CameraAVStreamManager : public CameraAVStreamManagementDelegate, public CameraAVStreamController +{ public: - Protocols::InteractionModel::Status - VideoStreamAllocate(const VideoStreamStruct &allocateArgs, - uint16_t &outStreamID) override; + Protocols::InteractionModel::Status VideoStreamAllocate(const VideoStreamStruct & allocateArgs, + uint16_t & outStreamID) override; - Protocols::InteractionModel::Status - VideoStreamModify(const uint16_t streamID, - const chip::Optional waterMarkEnabled, - const chip::Optional osdEnabled) override; + Protocols::InteractionModel::Status VideoStreamModify(const uint16_t streamID, const chip::Optional waterMarkEnabled, + const chip::Optional osdEnabled) override; - Protocols::InteractionModel::Status - VideoStreamDeallocate(const uint16_t streamID) override; + Protocols::InteractionModel::Status VideoStreamDeallocate(const uint16_t streamID) override; - Protocols::InteractionModel::Status - AudioStreamAllocate(const AudioStreamStruct &allocateArgs, - uint16_t &outStreamID) override; + Protocols::InteractionModel::Status AudioStreamAllocate(const AudioStreamStruct & allocateArgs, + uint16_t & outStreamID) override; - Protocols::InteractionModel::Status - AudioStreamDeallocate(const uint16_t streamID) override; + Protocols::InteractionModel::Status AudioStreamDeallocate(const uint16_t streamID) override; - Protocols::InteractionModel::Status - SnapshotStreamAllocate(const SnapshotStreamAllocateArgs &allocateArgs, - uint16_t &outStreamID) override; + Protocols::InteractionModel::Status SnapshotStreamAllocate(const SnapshotStreamAllocateArgs & allocateArgs, + uint16_t & outStreamID) override; - Protocols::InteractionModel::Status - SnapshotStreamModify(const uint16_t streamID, - const chip::Optional waterMarkEnabled, - const chip::Optional osdEnabled) override; + Protocols::InteractionModel::Status SnapshotStreamModify(const uint16_t streamID, const chip::Optional waterMarkEnabled, + const chip::Optional osdEnabled) override; - Protocols::InteractionModel::Status - SnapshotStreamDeallocate(const uint16_t streamID) override; + Protocols::InteractionModel::Status SnapshotStreamDeallocate(const uint16_t streamID) override; - void OnVideoStreamAllocated(const VideoStreamStruct &allocatedStream, - StreamAllocationAction action) override; + void OnVideoStreamAllocated(const VideoStreamStruct & allocatedStream, StreamAllocationAction action) override; - void OnStreamUsagePrioritiesChanged() override; + void OnStreamUsagePrioritiesChanged() override; - void OnAttributeChanged(AttributeId attributeId) override; + void OnAttributeChanged(AttributeId attributeId) override; - Protocols::InteractionModel::Status - CaptureSnapshot(const chip::app::DataModel::Nullable streamID, - const VideoResolutionStruct &resolution, - ImageSnapshot &outImageSnapshot) override; + Protocols::InteractionModel::Status CaptureSnapshot(const chip::app::DataModel::Nullable streamID, + const VideoResolutionStruct & resolution, + ImageSnapshot & outImageSnapshot) override; - CHIP_ERROR - ValidateStreamUsage( - StreamUsageEnum streamUsage, - Optional> &videoStreamId, - Optional> &audioStreamId) override; + CHIP_ERROR + ValidateStreamUsage(StreamUsageEnum streamUsage, Optional> & videoStreamId, + Optional> & audioStreamId) override; - CHIP_ERROR - ValidateVideoStreamID(uint16_t videoStreamId) override; + CHIP_ERROR + ValidateVideoStreamID(uint16_t videoStreamId) override; - CHIP_ERROR - ValidateAudioStreamID(uint16_t audioStreamId) override; + CHIP_ERROR + ValidateAudioStreamID(uint16_t audioStreamId) override; - CHIP_ERROR IsHardPrivacyModeActive(bool &isActive) override; + CHIP_ERROR IsHardPrivacyModeActive(bool & isActive) override; - CHIP_ERROR IsSoftRecordingPrivacyModeActive(bool &isActive) override; + CHIP_ERROR IsSoftRecordingPrivacyModeActive(bool & isActive) override; - CHIP_ERROR IsSoftLivestreamPrivacyModeActive(bool &isActive) override; + CHIP_ERROR IsSoftLivestreamPrivacyModeActive(bool & isActive) override; - bool HasAllocatedVideoStreams() override; + bool HasAllocatedVideoStreams() override; - bool HasAllocatedAudioStreams() override; + bool HasAllocatedAudioStreams() override; - CHIP_ERROR SetHardPrivacyModeOn(bool hardPrivacyMode) override; + CHIP_ERROR SetHardPrivacyModeOn(bool hardPrivacyMode) override; - CHIP_ERROR PersistentAttributesLoadedCallback() override; + CHIP_ERROR PersistentAttributesLoadedCallback() override; - CHIP_ERROR - OnTransportAcquireAudioVideoStreams(uint16_t audioStreamID, - uint16_t videoStreamID) override; + CHIP_ERROR + OnTransportAcquireAudioVideoStreams(uint16_t audioStreamID, uint16_t videoStreamID) override; - CHIP_ERROR - OnTransportReleaseAudioVideoStreams(uint16_t audioStreamID, - uint16_t videoStreamID) override; + CHIP_ERROR + OnTransportReleaseAudioVideoStreams(uint16_t audioStreamID, uint16_t videoStreamID) override; - const std::vector< - chip::app::Clusters::CameraAvStreamManagement::VideoStreamStruct> & - GetAllocatedVideoStreams() const override; + const std::vector & GetAllocatedVideoStreams() const override; - const std::vector< - chip::app::Clusters::CameraAvStreamManagement::AudioStreamStruct> & - GetAllocatedAudioStreams() const override; + const std::vector & GetAllocatedAudioStreams() const override; - void GetBandwidthForStreams( - const Optional> &videoStreamId, - const Optional> &audioStreamId, - uint32_t &outBandwidthbps) override; + void GetBandwidthForStreams(const Optional> & videoStreamId, + const Optional> & audioStreamId, uint32_t & outBandwidthbps) override; - CameraAVStreamManager() = default; - ~CameraAVStreamManager() = default; + CameraAVStreamManager() = default; + ~CameraAVStreamManager() = default; - void SetCameraDeviceHAL(CameraDeviceInterface *aCameraDevice); + void SetCameraDeviceHAL(CameraDeviceInterface * aCameraDevice); private: - CHIP_ERROR AllocatedVideoStreamsLoaded(); + CHIP_ERROR AllocatedVideoStreamsLoaded(); - CHIP_ERROR AllocatedAudioStreamsLoaded(); + CHIP_ERROR AllocatedAudioStreamsLoaded(); - CHIP_ERROR AllocatedSnapshotStreamsLoaded(); + CHIP_ERROR AllocatedSnapshotStreamsLoaded(); - CameraDeviceInterface *mCameraDeviceHAL = nullptr; + CameraDeviceInterface * mCameraDeviceHAL = nullptr; }; } // namespace CameraAvStreamManagement diff --git a/examples/camera/main/clusters/webrtc-provider-manager.cpp b/examples/camera/main/clusters/webrtc-provider-manager.cpp index 05c29a93c..224e3b55f 100644 --- a/examples/camera/main/clusters/webrtc-provider-manager.cpp +++ b/examples/camera/main/clusters/webrtc-provider-manager.cpp @@ -1,6 +1,24 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * All rights reserved. + * + * 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. + */ #include "webrtc-provider-manager.h" #include "webrtc-kvs_esp_port.h" +#include "webrtc-kvs_esp_port_utils.h" #include #include #include @@ -13,7 +31,6 @@ using namespace chip::app; using namespace chip::app::Clusters; using namespace chip::app::Clusters::WebRTCTransportProvider; -extern void sdp_mem_dump(); extern bool deferred_offer; namespace { @@ -23,423 +40,440 @@ constexpr uint16_t kMaxConcurrentWebRTCSessions = 3; } // namespace -void WebRTCProviderManager::SetCameraDevice( - CameraDeviceInterface *aCameraDevice) { - mCameraDevice = aCameraDevice; +void WebRTCProviderManager::SetCameraDevice(CameraDeviceInterface * aCameraDevice) +{ + mCameraDevice = aCameraDevice; } -void WebRTCProviderManager::Init() { - ChipLogProgress(Camera, "Initializing WebRTC PeerConnection"); - // Register our handler for signaling messages - webrtc_bridge_register_handler( - (webrtc_bridge_msg_cb_t)&webrtc_bridge_message_received_cb); +void WebRTCProviderManager::Init() +{ + ChipLogProgress(Camera, "Initializing WebRTC PeerConnection"); + // Register our handler for signaling messages + webrtc_bridge_register_handler((webrtc_bridge_msg_cb_t) &webrtc_bridge_message_received_cb); } -void WebRTCProviderManager::CloseConnection() { - // Clean up all the Webrtc Transports - mWebrtcTransportMap.clear(); - mSessionIdMap.clear(); +void WebRTCProviderManager::CloseConnection() +{ + // Clean up all the Webrtc Transports + mWebrtcTransportMap.clear(); + mSessionIdMap.clear(); } -void WebRTCProviderManager::SetWebRTCTransportProvider( - std::unique_ptr aWebRTCTransportProvider) { - mWebRTCTransportProvider = std::move(aWebRTCTransportProvider); +void WebRTCProviderManager::SetWebRTCTransportProvider(WebRTCTransportProviderCluster * webRTCTransportProvider) +{ + mWebRTCTransportProvider = webRTCTransportProvider; } CHIP_ERROR -WebRTCProviderManager::HandleSolicitOffer(const OfferRequestArgs &args, - WebRTCSessionStruct &outSession, - bool &outDeferredOffer) { - // Initialize a new WebRTC session from the SolicitOfferRequestArgs - outSession.id = args.sessionId; - outSession.peerNodeID = args.peerNodeId; - outSession.peerEndpointID = args.originatingEndpointId; - outSession.streamUsage = args.streamUsage; - outSession.fabricIndex = args.fabricIndex; - uint16_t videoStreamID = 0; - uint16_t audioStreamID = 0; +WebRTCProviderManager::HandleSolicitOffer(const OfferRequestArgs & args, WebRTCSessionStruct & outSession, bool & outDeferredOffer) +{ + // Initialize a new WebRTC session from the SolicitOfferRequestArgs + outSession.id = args.sessionId; + outSession.peerNodeID = args.peerNodeId; + outSession.peerEndpointID = args.originatingEndpointId; + outSession.streamUsage = args.streamUsage; + outSession.fabricIndex = args.fabricIndex; + uint16_t videoStreamID = 0; + uint16_t audioStreamID = 0; - // Resolve or allocate a VIDEO stream - if (args.videoStreamId.HasValue()) { - if (args.videoStreamId.Value().IsNull()) { - // TODO: Automatically select the closest matching video stream for the - // StreamUsage requested by looking at the and the server MAY allocate a - // new video stream if there are available resources. - } else { - outSession.videoStreamID = args.videoStreamId.Value(); - videoStreamID = args.videoStreamId.Value().Value(); + // Resolve or allocate a VIDEO stream + if (args.videoStreamId.HasValue()) + { + if (args.videoStreamId.Value().IsNull()) + { + // TODO: Automatically select the closest matching video stream for the + // StreamUsage requested by looking at the and the server MAY allocate a + // new video stream if there are available resources. + } + else + { + outSession.videoStreamID = args.videoStreamId.Value(); + videoStreamID = args.videoStreamId.Value().Value(); + } } - } else { - outSession.videoStreamID.SetNull(); - } - - // Resolve or allocate an AUDIO stream - if (args.audioStreamId.HasValue()) { - if (args.audioStreamId.Value().IsNull()) { - // TODO: Automatically select the closest matching audio stream for the - // StreamUsage requested and the server MAY allocate a new audio stream if - // there are available resources. - } else { - outSession.audioStreamID = args.audioStreamId.Value(); - audioStreamID = args.audioStreamId.Value().Value(); + else + { + outSession.videoStreamID.SetNull(); } - } else { - outSession.audioStreamID.SetNull(); - } - outDeferredOffer = deferred_offer; + // Resolve or allocate an AUDIO stream + if (args.audioStreamId.HasValue()) + { + if (args.audioStreamId.Value().IsNull()) + { + // TODO: Automatically select the closest matching audio stream for the + // StreamUsage requested and the server MAY allocate a new audio stream if + // there are available resources. + } + else + { + outSession.audioStreamID = args.audioStreamId.Value(); + audioStreamID = args.audioStreamId.Value().Value(); + } + } + else + { + outSession.audioStreamID.SetNull(); + } - ChipLogError(Camera, "Deferred Offer is set to: %d", outDeferredOffer); + outDeferredOffer = deferred_offer; - WebrtcTransport *transport = GetTransport(args.sessionId); - WebrtcTransport::RequestArgs requestArgs; - requestArgs.sessionId = args.sessionId; - requestArgs.fabricIndex = args.fabricIndex; - requestArgs.peerNodeId = args.peerNodeId; - requestArgs.originatingEndpointId = args.originatingEndpointId; - requestArgs.videoStreamId = videoStreamID; - requestArgs.audioStreamId = audioStreamID; - requestArgs.peerId = ScopedNodeId(args.peerNodeId, args.fabricIndex); + ChipLogError(Camera, "Deferred Offer is set to: %d", outDeferredOffer); - if (transport == nullptr) { - mWebrtcTransportMap[args.sessionId] = - std::unique_ptr(new WebrtcTransport()); - mSessionIdMap[ScopedNodeId(args.peerNodeId, args.fabricIndex)] = - args.sessionId; - transport = mWebrtcTransportMap[args.sessionId].get(); - transport->SetCallbacks( - [this](const std::string &sdp, SDPType type, const uint16_t sessionId) { - this->OnLocalDescription(sdp, type, sessionId); - }, - [this](bool connected, const uint16_t sessionId) { - this->OnConnectionStateChanged(connected, sessionId); - }); - } + WebrtcTransport * transport = GetTransport(args.sessionId); + WebrtcTransport::RequestArgs requestArgs; + requestArgs.sessionId = args.sessionId; + requestArgs.fabricIndex = args.fabricIndex; + requestArgs.peerNodeId = args.peerNodeId; + requestArgs.originatingEndpointId = args.originatingEndpointId; + requestArgs.videoStreamId = videoStreamID; + requestArgs.audioStreamId = audioStreamID; + requestArgs.peerId = ScopedNodeId(args.peerNodeId, args.fabricIndex); - transport->SetRequestArgs(requestArgs); + if (transport == nullptr) + { + mWebrtcTransportMap[args.sessionId] = std::unique_ptr(new WebrtcTransport()); + mSessionIdMap[ScopedNodeId(args.peerNodeId, args.fabricIndex)] = args.sessionId; + transport = mWebrtcTransportMap[args.sessionId].get(); + transport->SetCallbacks( + [this](const std::string & sdp, SDPType type, const uint16_t sessionId) { + this->OnLocalDescription(sdp, type, sessionId); + }, + [this](bool connected, const uint16_t sessionId) { this->OnConnectionStateChanged(connected, sessionId); }); + } - // Check resource availability before proceeding - // If we cannot allocate resources, send End command with OutOfResources - // reason - if (mWebrtcTransportMap.size() > kMaxConcurrentWebRTCSessions) { - ChipLogProgress( - Camera, "Resource exhaustion detected: maximum WebRTC sessions (%u)", - kMaxConcurrentWebRTCSessions); + transport->SetRequestArgs(requestArgs); - transport->SetCommandType(WebrtcTransport::CommandType::kEnd); - transport->MoveToState(WebrtcTransport::State::SendingEnd); + // Check resource availability before proceeding + // If we cannot allocate resources, send End command with OutOfResources + // reason + if (mWebrtcTransportMap.size() > kMaxConcurrentWebRTCSessions) + { + ChipLogProgress(Camera, "Resource exhaustion detected: maximum WebRTC sessions (%u)", kMaxConcurrentWebRTCSessions); - // The resource exhaustion happens internally in the DUT, but it still - // creates a session and then sends an End command with OutOfResources - // reason. - ScheduleEndSend(args.sessionId); + transport->SetCommandType(WebrtcTransport::CommandType::kEnd); + transport->MoveToState(WebrtcTransport::State::SendingEnd); + + // The resource exhaustion happens internally in the DUT, but it still + // creates a session and then sends an End command with OutOfResources + // reason. + ScheduleEndSend(args.sessionId); + + return CHIP_NO_ERROR; + } + + transport->Start(); + transport->AddTracks(); + + // Acquire the Video and Audio Streams from the CameraAVStreamManagement + // cluster and update the reference counts. + AcquireAudioVideoStreams(args.sessionId); + + transport->MoveToState(WebrtcTransport::State::SendingOffer); + + ChipLogProgress(Camera, "Generate and set the SDP"); + if (transport->GetPeerConnection()) + transport->GetPeerConnection()->CreateOffer(args.sessionId); return CHIP_NO_ERROR; - } - - transport->Start(); - transport->AddTracks(); - - // Acquire the Video and Audio Streams from the CameraAVStreamManagement - // cluster and update the reference counts. - AcquireAudioVideoStreams(args.sessionId); - - transport->MoveToState(WebrtcTransport::State::SendingOffer); - - ChipLogProgress(Camera, "Generate and set the SDP"); - if (transport->GetPeerConnection()) - transport->GetPeerConnection()->CreateOffer(args.sessionId); - - return CHIP_NO_ERROR; } -void WebRTCProviderManager::RegisterWebrtcTransport(uint16_t sessionId) { +void WebRTCProviderManager::RegisterWebrtcTransport(uint16_t sessionId) +{ - WebrtcTransport *transport = GetTransport(sessionId); - if (transport == nullptr) { - ChipLogProgress(Camera, - "WebRTC Transport is null for sessionId %u. Failed to " - "Register WebRTC Transport", - sessionId); - return; - } - - WebrtcTransport::RequestArgs args = transport->GetRequestArgs(); -} - -void WebRTCProviderManager::UnregisterWebrtcTransport(uint16_t sessionId) { - ChipLogProgress(Camera, "UnregisterWebrtcTransport called for sessionId: %u", - sessionId); - - WebrtcTransport *transport = GetTransport(sessionId); - if (transport == nullptr) { - ChipLogProgress(Camera, - "WebRTC Transport is null for sessionId %u. Already " - "unregistered or not found", - sessionId); - return; - } - ChipLogProgress(Camera, - "Successfully unregistered transport for sessionId: %u", - sessionId); -} - -CHIP_ERROR -WebRTCProviderManager::HandleProvideOffer(const ProvideOfferRequestArgs &args, - WebRTCSessionStruct &outSession) { - ChipLogError(Zcl, "Handle Provide Offer Delegate called"); - // Initialize a new WebRTC session from the SolicitOfferRequestArgs - outSession.id = args.sessionId; - outSession.peerNodeID = args.peerNodeId; - outSession.peerEndpointID = args.originatingEndpointId; - outSession.streamUsage = args.streamUsage; - outSession.fabricIndex = args.fabricIndex; - uint16_t videoStreamID = 0; - uint16_t audioStreamID = 0; - - // Resolve or allocate a VIDEO stream - if (args.videoStreamId.HasValue()) { - if (args.videoStreamId.Value().IsNull()) { - // TODO: Automatically select the closest matching video stream for the - // StreamUsage requested. - } else { - outSession.videoStreamID = args.videoStreamId.Value(); - videoStreamID = args.videoStreamId.Value().Value(); + WebrtcTransport * transport = GetTransport(sessionId); + if (transport == nullptr) + { + ChipLogProgress(Camera, + "WebRTC Transport is null for sessionId %u. Failed to " + "Register WebRTC Transport", + sessionId); + return; } - } else { - outSession.videoStreamID.SetNull(); - } - // Resolve or allocate an AUDIO stream - if (args.audioStreamId.HasValue()) { - if (args.audioStreamId.Value().IsNull()) { - // TODO: Automatically select the closest matching audio stream for the - // StreamUsage requested - } else { - outSession.audioStreamID = args.audioStreamId.Value(); - audioStreamID = args.audioStreamId.Value().Value(); - } - } else { - outSession.audioStreamID.SetNull(); - } - - // Process the SDP Offer, begin the ICE Candidate gathering phase, create the - // SDP Answer, and invoke Answer. - WebrtcTransport::RequestArgs requestArgs; - requestArgs.sessionId = args.sessionId; - requestArgs.fabricIndex = args.fabricIndex; - requestArgs.peerNodeId = args.peerNodeId; - requestArgs.originatingEndpointId = args.originatingEndpointId; - requestArgs.videoStreamId = videoStreamID; - requestArgs.audioStreamId = audioStreamID; - requestArgs.peerId = ScopedNodeId(args.peerNodeId, args.fabricIndex); - - WebrtcTransport *transport = GetTransport(args.sessionId); - if (transport == nullptr) { - mWebrtcTransportMap[args.sessionId] = - std::unique_ptr(new WebrtcTransport()); - mSessionIdMap[ScopedNodeId(args.peerNodeId, args.fabricIndex)] = - args.sessionId; - transport = mWebrtcTransportMap[args.sessionId].get(); - transport->SetCallbacks( - [this](const std::string &sdp, SDPType type, const uint16_t sessionId) { - this->OnLocalDescription(sdp, type, sessionId); - }, - [this](bool connected, const uint16_t sessionId) { - this->OnConnectionStateChanged(connected, sessionId); - }); - } - - // Check resource availability before proceeding - // If we cannot allocate resources, respond with a response status of - // RESOURCE_EXHAUSTED - if (mWebrtcTransportMap.size() > kMaxConcurrentWebRTCSessions) { - ChipLogProgress(Camera, - "Resource exhaustion detected in ProvideOffer: maximum " - "WebRTC sessions (%u)", - kMaxConcurrentWebRTCSessions); - return CHIP_IM_GLOBAL_STATUS(ResourceExhausted); - } - - transport->SetRequestArgs(requestArgs); - transport->Start(); - transport->AddTracks(); - - // Acquire the Video and Audio Streams from the CameraAVStreamManagement - // cluster and update the reference counts. - AcquireAudioVideoStreams(args.sessionId); - - transport->MoveToState(WebrtcTransport::State::SendingAnswer); - - if (transport->GetPeerConnection()) { - transport->GetPeerConnection()->SetRemoteDescription(args.sdp, - SDPType::Offer); - transport->GetPeerConnection()->CreateAnswer(); - } - - return CHIP_NO_ERROR; -} - -CHIP_ERROR -WebRTCProviderManager::HandleProvideAnswer(uint16_t sessionId, - const std::string &sdpAnswer) { - ChipLogProgress(Camera, "HandleProvideAnswer called with sessionId: %u", - sessionId); - - // Check if the provided sessionId matches your current sessions - WebrtcTransport *transport = GetTransport(sessionId); - if (transport == nullptr) { - ChipLogError(Camera, "Session ID %u does not match the current sessions", - sessionId); - return CHIP_ERROR_INVALID_ARGUMENT; - } - - if (sdpAnswer.empty()) { - ChipLogError(Camera, "Provided SDP Answer is empty for session ID %u", - sessionId); - return CHIP_ERROR_INVALID_ARGUMENT; - } - - if (!transport->GetPeerConnection()) { - ChipLogError(Camera, - "Cannot set remote description: mPeerConnection is null for " - "session ID %u", - sessionId); - return CHIP_ERROR_INCORRECT_STATE; - } - - transport->GetPeerConnection()->SetRemoteDescription(sdpAnswer, - SDPType::Answer); - - transport->MoveToState(WebrtcTransport::State::SendingICECandidates); - ScheduleICECandidatesSend(sessionId); - - return CHIP_NO_ERROR; -} - -CHIP_ERROR WebRTCProviderManager::HandleProvideICECandidates( - uint16_t sessionId, const std::vector &candidates) { - ChipLogProgress(Camera, - "HandleProvideICECandidates called with sessionId: %u", - sessionId); - - // Check if the provided sessionId matches your current sessions - WebrtcTransport *transport = GetTransport(sessionId); - if (transport == nullptr) { - ChipLogError(Camera, "Session ID %u does not match the current sessions", - sessionId); - return CHIP_ERROR_INVALID_ARGUMENT; - } - - if (!transport->GetPeerConnection()) { - ChipLogError(Camera, - "Cannot process ICE candidates: PeerConnection is null for " - "session ID %u", - sessionId); - return CHIP_ERROR_INCORRECT_STATE; - } - - if (candidates.empty()) { - ChipLogError( - Camera, "Candidate list is empty. At least one candidate is expected."); - return CHIP_ERROR_INVALID_ARGUMENT; - } - - for (const auto &candidate : candidates) { - ChipLogProgress( - Camera, "Applying candidate: %s", - std::string(candidate.candidate.begin(), candidate.candidate.end()) - .c_str()); - std::string mid = candidate.SDPMid.IsNull() - ? "" - : std::string(candidate.SDPMid.Value().begin(), - candidate.SDPMid.Value().end()); - transport->AddRemoteCandidate( - std::string(candidate.candidate.begin(), candidate.candidate.end()), - mid); - } - - // Schedule sending Ice Candidates when remote candidates are received. This - // keeps the exchange simple - transport->MoveToState(WebrtcTransport::State::SendingICECandidates); - ScheduleICECandidatesSend(sessionId); - - return CHIP_NO_ERROR; -} - -CHIP_ERROR WebRTCProviderManager::HandleEndSession( - uint16_t sessionId, WebRTCEndReasonEnum reasonCode, - DataModel::Nullable videoStreamID, - DataModel::Nullable audioStreamID) { - WebrtcTransport *transport = GetTransport(sessionId); - if (transport == nullptr) { - ChipLogError(Camera, "Session ID %u does not match the current sessions", - sessionId); - return CHIP_ERROR_INVALID_ARGUMENT; - } - if (transport != nullptr) { - ChipLogProgress(Camera, "Delete Webrtc Transport for the session: %u", - sessionId); - - // Release the Video and Audio Streams from the CameraAVStreamManagement - // cluster and update the reference counts. - // TODO: Lookup the sessionID to get the Video/Audio StreamID - ReleaseAudioVideoStreams(sessionId); - - UnregisterWebrtcTransport(sessionId); - mWebrtcTransportMap.erase(sessionId); WebrtcTransport::RequestArgs args = transport->GetRequestArgs(); - mSessionIdMap.erase(ScopedNodeId(args.peerNodeId, args.fabricIndex)); - } +} - if (transport->ClosePeerConnection()) { - ChipLogProgress(Camera, "Closing peer connection: %u", sessionId); - } +void WebRTCProviderManager::UnregisterWebrtcTransport(uint16_t sessionId) +{ + ChipLogProgress(Camera, "UnregisterWebrtcTransport called for sessionId: %u", sessionId); - return CHIP_NO_ERROR; + WebrtcTransport * transport = GetTransport(sessionId); + if (transport == nullptr) + { + ChipLogProgress(Camera, + "WebRTC Transport is null for sessionId %u. Already " + "unregistered or not found", + sessionId); + return; + } + ChipLogProgress(Camera, "Successfully unregistered transport for sessionId: %u", sessionId); } CHIP_ERROR -WebRTCProviderManager::ValidateStreamUsage( - StreamUsageEnum streamUsage, - Optional> &videoStreamId, - Optional> &audioStreamId) { - if (mCameraDevice == nullptr) { - ChipLogError(Camera, "CameraDeviceInterface not initialized"); - return CHIP_ERROR_INCORRECT_STATE; - } +WebRTCProviderManager::HandleProvideOffer(const ProvideOfferRequestArgs & args, WebRTCSessionStruct & outSession) +{ + ChipLogError(Zcl, "Handle Provide Offer Delegate called"); + // Initialize a new WebRTC session from the SolicitOfferRequestArgs + outSession.id = args.sessionId; + outSession.peerNodeID = args.peerNodeId; + outSession.peerEndpointID = args.originatingEndpointId; + outSession.streamUsage = args.streamUsage; + outSession.fabricIndex = args.fabricIndex; + uint16_t videoStreamID = 0; + uint16_t audioStreamID = 0; - auto &avsmController = mCameraDevice->GetCameraAVStreamMgmtController(); + // Resolve or allocate a VIDEO stream + if (args.videoStreamId.HasValue()) + { + if (args.videoStreamId.Value().IsNull()) + { + // TODO: Automatically select the closest matching video stream for the + // StreamUsage requested. + } + else + { + outSession.videoStreamID = args.videoStreamId.Value(); + videoStreamID = args.videoStreamId.Value().Value(); + } + } + else + { + outSession.videoStreamID.SetNull(); + } - return avsmController.ValidateStreamUsage(streamUsage, videoStreamId, - audioStreamId); + // Resolve or allocate an AUDIO stream + if (args.audioStreamId.HasValue()) + { + if (args.audioStreamId.Value().IsNull()) + { + // TODO: Automatically select the closest matching audio stream for the + // StreamUsage requested + } + else + { + outSession.audioStreamID = args.audioStreamId.Value(); + audioStreamID = args.audioStreamId.Value().Value(); + } + } + else + { + outSession.audioStreamID.SetNull(); + } + + // Process the SDP Offer, begin the ICE Candidate gathering phase, create the + // SDP Answer, and invoke Answer. + WebrtcTransport::RequestArgs requestArgs; + requestArgs.sessionId = args.sessionId; + requestArgs.fabricIndex = args.fabricIndex; + requestArgs.peerNodeId = args.peerNodeId; + requestArgs.originatingEndpointId = args.originatingEndpointId; + requestArgs.videoStreamId = videoStreamID; + requestArgs.audioStreamId = audioStreamID; + requestArgs.peerId = ScopedNodeId(args.peerNodeId, args.fabricIndex); + + WebrtcTransport * transport = GetTransport(args.sessionId); + if (transport == nullptr) + { + mWebrtcTransportMap[args.sessionId] = std::unique_ptr(new WebrtcTransport()); + mSessionIdMap[ScopedNodeId(args.peerNodeId, args.fabricIndex)] = args.sessionId; + transport = mWebrtcTransportMap[args.sessionId].get(); + transport->SetCallbacks( + [this](const std::string & sdp, SDPType type, const uint16_t sessionId) { + this->OnLocalDescription(sdp, type, sessionId); + }, + [this](bool connected, const uint16_t sessionId) { this->OnConnectionStateChanged(connected, sessionId); }); + } + + // Check resource availability before proceeding + // If we cannot allocate resources, respond with a response status of + // RESOURCE_EXHAUSTED + if (mWebrtcTransportMap.size() > kMaxConcurrentWebRTCSessions) + { + ChipLogProgress(Camera, + "Resource exhaustion detected in ProvideOffer: maximum " + "WebRTC sessions (%u)", + kMaxConcurrentWebRTCSessions); + return CHIP_IM_GLOBAL_STATUS(ResourceExhausted); + } + + transport->SetRequestArgs(requestArgs); + transport->Start(); + transport->AddTracks(); + + // Acquire the Video and Audio Streams from the CameraAVStreamManagement + // cluster and update the reference counts. + AcquireAudioVideoStreams(args.sessionId); + + transport->MoveToState(WebrtcTransport::State::SendingAnswer); + + if (transport->GetPeerConnection()) + { + transport->GetPeerConnection()->SetRemoteDescription(args.sdp, SDPType::Offer); + transport->GetPeerConnection()->CreateAnswer(); + } + + return CHIP_NO_ERROR; } CHIP_ERROR -WebRTCProviderManager::ValidateVideoStreamID(uint16_t videoStreamId) { - if (mCameraDevice == nullptr) { - ChipLogError(Camera, "CameraDeviceInterface not initialized"); - return CHIP_ERROR_INCORRECT_STATE; - } +WebRTCProviderManager::HandleProvideAnswer(uint16_t sessionId, const std::string & sdpAnswer) +{ + ChipLogProgress(Camera, "HandleProvideAnswer called with sessionId: %u", sessionId); - auto &avsmController = mCameraDevice->GetCameraAVStreamMgmtController(); + // Check if the provided sessionId matches your current sessions + WebrtcTransport * transport = GetTransport(sessionId); + if (transport == nullptr) + { + ChipLogError(Camera, "Session ID %u does not match the current sessions", sessionId); + return CHIP_ERROR_INVALID_ARGUMENT; + } - return avsmController.ValidateVideoStreamID(videoStreamId); + if (sdpAnswer.empty()) + { + ChipLogError(Camera, "Provided SDP Answer is empty for session ID %u", sessionId); + return CHIP_ERROR_INVALID_ARGUMENT; + } + + if (!transport->GetPeerConnection()) + { + ChipLogError(Camera, + "Cannot set remote description: mPeerConnection is null for " + "session ID %u", + sessionId); + return CHIP_ERROR_INCORRECT_STATE; + } + + transport->GetPeerConnection()->SetRemoteDescription(sdpAnswer, SDPType::Answer); + + transport->MoveToState(WebrtcTransport::State::SendingICECandidates); + ScheduleICECandidatesSend(sessionId); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR WebRTCProviderManager::HandleProvideICECandidates(uint16_t sessionId, const std::vector & candidates) +{ + ChipLogProgress(Camera, "HandleProvideICECandidates called with sessionId: %u", sessionId); + + // Check if the provided sessionId matches your current sessions + WebrtcTransport * transport = GetTransport(sessionId); + if (transport == nullptr) + { + ChipLogError(Camera, "Session ID %u does not match the current sessions", sessionId); + return CHIP_ERROR_INVALID_ARGUMENT; + } + + if (!transport->GetPeerConnection()) + { + ChipLogError(Camera, + "Cannot process ICE candidates: PeerConnection is null for " + "session ID %u", + sessionId); + return CHIP_ERROR_INCORRECT_STATE; + } + + if (candidates.empty()) + { + ChipLogError(Camera, "Candidate list is empty. At least one candidate is expected."); + return CHIP_ERROR_INVALID_ARGUMENT; + } + + for (const auto & candidate : candidates) + { + ChipLogProgress(Camera, "Applying candidate: %s", + std::string(candidate.candidate.begin(), candidate.candidate.end()).c_str()); + std::string mid = + candidate.SDPMid.IsNull() ? "" : std::string(candidate.SDPMid.Value().begin(), candidate.SDPMid.Value().end()); + transport->AddRemoteCandidate(std::string(candidate.candidate.begin(), candidate.candidate.end()), mid); + } + + // Schedule sending Ice Candidates when remote candidates are received. This + // keeps the exchange simple + transport->MoveToState(WebrtcTransport::State::SendingICECandidates); + ScheduleICECandidatesSend(sessionId); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR WebRTCProviderManager::HandleEndSession(uint16_t sessionId, WebRTCEndReasonEnum reasonCode, + DataModel::Nullable videoStreamID, + DataModel::Nullable audioStreamID) +{ + WebrtcTransport * transport = GetTransport(sessionId); + if (transport == nullptr) + { + ChipLogError(Camera, "Session ID %u does not match the current sessions", sessionId); + return CHIP_ERROR_INVALID_ARGUMENT; + } + if (transport != nullptr) + { + ChipLogProgress(Camera, "Delete Webrtc Transport for the session: %u", sessionId); + + // Release the Video and Audio Streams from the CameraAVStreamManagement + // cluster and update the reference counts. + // TODO: Lookup the sessionID to get the Video/Audio StreamID + ReleaseAudioVideoStreams(sessionId); + + UnregisterWebrtcTransport(sessionId); + mWebrtcTransportMap.erase(sessionId); + WebrtcTransport::RequestArgs args = transport->GetRequestArgs(); + mSessionIdMap.erase(ScopedNodeId(args.peerNodeId, args.fabricIndex)); + } + + if (transport->ClosePeerConnection()) + { + ChipLogProgress(Camera, "Closing peer connection: %u", sessionId); + } + + return CHIP_NO_ERROR; } CHIP_ERROR -WebRTCProviderManager::ValidateAudioStreamID(uint16_t audioStreamId) { - if (mCameraDevice == nullptr) { - ChipLogError(Camera, "CameraDeviceInterface not initialized"); - return CHIP_ERROR_INCORRECT_STATE; - } +WebRTCProviderManager::ValidateStreamUsage(StreamUsageEnum streamUsage, Optional> & videoStreamId, + Optional> & audioStreamId) +{ + if (mCameraDevice == nullptr) + { + ChipLogError(Camera, "CameraDeviceInterface not initialized"); + return CHIP_ERROR_INCORRECT_STATE; + } - auto &avsmController = mCameraDevice->GetCameraAVStreamMgmtController(); + auto & avsmController = mCameraDevice->GetCameraAVStreamMgmtController(); - return avsmController.ValidateAudioStreamID(audioStreamId); + return avsmController.ValidateStreamUsage(streamUsage, videoStreamId, audioStreamId); } -CHIP_ERROR WebRTCProviderManager::IsStreamUsageSupported(StreamUsageEnum streamUsage) +CHIP_ERROR +WebRTCProviderManager::ValidateVideoStreamID(uint16_t videoStreamId) +{ + if (mCameraDevice == nullptr) + { + ChipLogError(Camera, "CameraDeviceInterface not initialized"); + return CHIP_ERROR_INCORRECT_STATE; + } + + auto & avsmController = mCameraDevice->GetCameraAVStreamMgmtController(); + + return avsmController.ValidateVideoStreamID(videoStreamId); +} + +CHIP_ERROR +WebRTCProviderManager::ValidateAudioStreamID(uint16_t audioStreamId) +{ + if (mCameraDevice == nullptr) + { + ChipLogError(Camera, "CameraDeviceInterface not initialized"); + return CHIP_ERROR_INCORRECT_STATE; + } + + auto & avsmController = mCameraDevice->GetCameraAVStreamMgmtController(); + + return avsmController.ValidateAudioStreamID(audioStreamId); +} + +CHIP_ERROR +WebRTCProviderManager::IsStreamUsageSupported(StreamUsageEnum streamUsage) { if (mCameraDevice == nullptr) { @@ -463,69 +497,79 @@ CHIP_ERROR WebRTCProviderManager::IsStreamUsageSupported(StreamUsageEnum streamU return CHIP_ERROR_NOT_FOUND; } -CHIP_ERROR WebRTCProviderManager::IsHardPrivacyModeActive(bool &isActive) { - if (mCameraDevice == nullptr) { - ChipLogError(Camera, "CameraDeviceInterface not initialized"); - return CHIP_ERROR_INCORRECT_STATE; - } +CHIP_ERROR WebRTCProviderManager::IsHardPrivacyModeActive(bool & isActive) +{ + if (mCameraDevice == nullptr) + { + ChipLogError(Camera, "CameraDeviceInterface not initialized"); + return CHIP_ERROR_INCORRECT_STATE; + } - auto &avsmController = mCameraDevice->GetCameraAVStreamMgmtController(); + auto & avsmController = mCameraDevice->GetCameraAVStreamMgmtController(); - return avsmController.IsHardPrivacyModeActive(isActive); + return avsmController.IsHardPrivacyModeActive(isActive); } CHIP_ERROR -WebRTCProviderManager::IsSoftRecordingPrivacyModeActive(bool &isActive) { - if (mCameraDevice == nullptr) { - ChipLogError(Camera, "CameraDeviceInterface not initialized"); - return CHIP_ERROR_INCORRECT_STATE; - } +WebRTCProviderManager::IsSoftRecordingPrivacyModeActive(bool & isActive) +{ + if (mCameraDevice == nullptr) + { + ChipLogError(Camera, "CameraDeviceInterface not initialized"); + return CHIP_ERROR_INCORRECT_STATE; + } - auto &avsmController = mCameraDevice->GetCameraAVStreamMgmtController(); + auto & avsmController = mCameraDevice->GetCameraAVStreamMgmtController(); - return avsmController.IsSoftRecordingPrivacyModeActive(isActive); + return avsmController.IsSoftRecordingPrivacyModeActive(isActive); } CHIP_ERROR -WebRTCProviderManager::IsSoftLivestreamPrivacyModeActive(bool &isActive) { - if (mCameraDevice == nullptr) { - ChipLogError(Camera, "CameraDeviceInterface not initialized"); - return CHIP_ERROR_INCORRECT_STATE; - } +WebRTCProviderManager::IsSoftLivestreamPrivacyModeActive(bool & isActive) +{ + if (mCameraDevice == nullptr) + { + ChipLogError(Camera, "CameraDeviceInterface not initialized"); + return CHIP_ERROR_INCORRECT_STATE; + } - auto &avsmController = mCameraDevice->GetCameraAVStreamMgmtController(); + auto & avsmController = mCameraDevice->GetCameraAVStreamMgmtController(); - return avsmController.IsSoftLivestreamPrivacyModeActive(isActive); + return avsmController.IsSoftLivestreamPrivacyModeActive(isActive); } -bool WebRTCProviderManager::HasAllocatedVideoStreams() { - if (mCameraDevice == nullptr) { - ChipLogError(Camera, "CameraDeviceInterface not initialized"); - return false; - } +bool WebRTCProviderManager::HasAllocatedVideoStreams() +{ + if (mCameraDevice == nullptr) + { + ChipLogError(Camera, "CameraDeviceInterface not initialized"); + return false; + } - auto &avsmController = mCameraDevice->GetCameraAVStreamMgmtController(); + auto & avsmController = mCameraDevice->GetCameraAVStreamMgmtController(); - return avsmController.HasAllocatedVideoStreams(); + return avsmController.HasAllocatedVideoStreams(); } -bool WebRTCProviderManager::HasAllocatedAudioStreams() { - if (mCameraDevice == nullptr) { - ChipLogError(Camera, "CameraDeviceInterface not initialized"); - return false; - } +bool WebRTCProviderManager::HasAllocatedAudioStreams() +{ + if (mCameraDevice == nullptr) + { + ChipLogError(Camera, "CameraDeviceInterface not initialized"); + return false; + } - auto &avsmController = mCameraDevice->GetCameraAVStreamMgmtController(); + auto & avsmController = mCameraDevice->GetCameraAVStreamMgmtController(); - return avsmController.HasAllocatedAudioStreams(); + return avsmController.HasAllocatedAudioStreams(); } CHIP_ERROR WebRTCProviderManager::ValidateSFrameConfig(uint16_t cipherSuite, size_t baseKeyLength) { // Define supported cipher suites and their expected key lengths - // Based on SFrame RFC: https://datatracker.ietf.org/doc/html/draft-ietf-sframe-enc - // 0x0001: AES-128-GCM-SHA256 (16 byte key) - // 0x0002: AES-256-GCM-SHA512 (32 byte key) + // Based on SFrame RFC: + // https://datatracker.ietf.org/doc/html/draft-ietf-sframe-enc 0x0001: + // AES-128-GCM-SHA256 (16 byte key) 0x0002: AES-256-GCM-SHA512 (32 byte key) constexpr uint16_t kCipherSuite_AES_128_GCM = 0x0001; constexpr uint16_t kCipherSuite_AES_256_GCM = 0x0002; constexpr size_t kAES_128_KeyLength = 16; @@ -550,7 +594,9 @@ CHIP_ERROR WebRTCProviderManager::ValidateSFrameConfig(uint16_t cipherSuite, siz // Validate base key length matches the expected length for the cipher suite if (baseKeyLength != expectedKeyLength) { - ChipLogError(Camera, "SFrame base key length mismatch - expected %u bytes for cipher suite 0x%04X, got %u bytes", + ChipLogError(Camera, + "SFrame base key length mismatch - expected %u bytes for " + "cipher suite 0x%04X, got %u bytes", static_cast(expectedKeyLength), cipherSuite, static_cast(baseKeyLength)); return CHIP_ERROR_INVALID_ARGUMENT; } @@ -561,534 +607,505 @@ CHIP_ERROR WebRTCProviderManager::ValidateSFrameConfig(uint16_t cipherSuite, siz CHIP_ERROR WebRTCProviderManager::IsUTCTimeNull(bool & isNull) { // TODO: The implementation SHALL: - // - Read the UTCTime attribute from the Time Synchronization cluster (0x0038) + // - Read the UTCTime attribute from the Time Synchronization cluster + // (0x0038) // - Return whether the attribute is null or has a valid value return CHIP_NO_ERROR; } -void WebRTCProviderManager::ScheduleOfferSend(uint16_t sessionId) { - ChipLogProgress(Camera, "ScheduleOfferSend called."); +void WebRTCProviderManager::ScheduleOfferSend(uint16_t sessionId) +{ + ChipLogProgress(Camera, "ScheduleOfferSend called."); - DeviceLayer::SystemLayer().ScheduleLambda([this, sessionId]() { - WebrtcTransport *transport = GetTransport(sessionId); - if (transport == nullptr) { - return; - } + DeviceLayer::SystemLayer().ScheduleLambda([this, sessionId]() { + WebrtcTransport * transport = GetTransport(sessionId); + if (transport == nullptr) + { + return; + } - WebrtcTransport::RequestArgs args = transport->GetRequestArgs(); - ChipLogProgress(Camera, "Sending Offer command to node " ChipLogFormatX64, - ChipLogValueX64(args.peerNodeId)); + WebrtcTransport::RequestArgs args = transport->GetRequestArgs(); + ChipLogProgress(Camera, "Sending Offer command to node " ChipLogFormatX64, ChipLogValueX64(args.peerNodeId)); - transport->SetCommandType(WebrtcTransport::CommandType::kOffer); + transport->SetCommandType(WebrtcTransport::CommandType::kOffer); - // Attempt to find or establish a CASE session to the target PeerId. - CASESessionManager *caseSessionMgr = - Server::GetInstance().GetCASESessionManager(); - VerifyOrDie(caseSessionMgr != nullptr); + // Attempt to find or establish a CASE session to the target PeerId. + CASESessionManager * caseSessionMgr = Server::GetInstance().GetCASESessionManager(); + VerifyOrDie(caseSessionMgr != nullptr); - // WebRTC Answer requires a large payload session establishment. - caseSessionMgr->FindOrEstablishSession( - args.peerId, &mOnConnectedCallback, &mOnConnectionFailureCallback, - TransportPayloadCapability::kLargePayload); - }); + // WebRTC Answer requires a large payload session establishment. + caseSessionMgr->FindOrEstablishSession(args.peerId, &mOnConnectedCallback, &mOnConnectionFailureCallback, + TransportPayloadCapability::kLargePayload); + }); } -void WebRTCProviderManager::ScheduleAnswerSend(uint16_t sessionId) { - ChipLogProgress(Camera, "ScheduleAnswerSend called."); +void WebRTCProviderManager::ScheduleAnswerSend(uint16_t sessionId) +{ + ChipLogProgress(Camera, "ScheduleAnswerSend called."); - DeviceLayer::SystemLayer().ScheduleLambda([this, sessionId]() { - WebrtcTransport *transport = GetTransport(sessionId); - if (transport == nullptr) { - return; - } + DeviceLayer::SystemLayer().ScheduleLambda([this, sessionId]() { + WebrtcTransport * transport = GetTransport(sessionId); + if (transport == nullptr) + { + return; + } - WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs(); - chip::ScopedNodeId peerId = requestArgs.peerId; - ChipLogProgress(Camera, "Sending Answer command to node " ChipLogFormatX64, - ChipLogValueX64(peerId.GetNodeId())); + WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs(); + chip::ScopedNodeId peerId = requestArgs.peerId; + ChipLogProgress(Camera, "Sending Answer command to node " ChipLogFormatX64, ChipLogValueX64(peerId.GetNodeId())); - transport->SetCommandType(WebrtcTransport::CommandType::kAnswer); + transport->SetCommandType(WebrtcTransport::CommandType::kAnswer); - // Attempt to find or establish a CASE session to the target PeerId. - CASESessionManager *caseSessionMgr = - Server::GetInstance().GetCASESessionManager(); - VerifyOrDie(caseSessionMgr != nullptr); + // Attempt to find or establish a CASE session to the target PeerId. + CASESessionManager * caseSessionMgr = Server::GetInstance().GetCASESessionManager(); + VerifyOrDie(caseSessionMgr != nullptr); - // WebRTC Answer requires a large payload session establishment. - caseSessionMgr->FindOrEstablishSession( - peerId, &mOnConnectedCallback, &mOnConnectionFailureCallback, - TransportPayloadCapability::kLargePayload); - }); + // WebRTC Answer requires a large payload session establishment. + caseSessionMgr->FindOrEstablishSession(peerId, &mOnConnectedCallback, &mOnConnectionFailureCallback, + TransportPayloadCapability::kLargePayload); + }); } -void WebRTCProviderManager::ScheduleEndSend(uint16_t sessionId) { - ChipLogProgress(Camera, "ScheduleEndSend called."); +void WebRTCProviderManager::ScheduleEndSend(uint16_t sessionId) +{ + ChipLogProgress(Camera, "ScheduleEndSend called."); - DeviceLayer::SystemLayer().ScheduleLambda([this, sessionId]() { - WebrtcTransport *transport = GetTransport(sessionId); - if (transport == nullptr) { - return; - } + DeviceLayer::SystemLayer().ScheduleLambda([this, sessionId]() { + WebrtcTransport * transport = GetTransport(sessionId); + if (transport == nullptr) + { + return; + } - WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs(); - chip::ScopedNodeId peerId = requestArgs.peerId; - ChipLogProgress(Camera, "Sending End command to node " ChipLogFormatX64, - ChipLogValueX64(peerId.GetNodeId())); + WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs(); + chip::ScopedNodeId peerId = requestArgs.peerId; + ChipLogProgress(Camera, "Sending End command to node " ChipLogFormatX64, ChipLogValueX64(peerId.GetNodeId())); - transport->SetCommandType(WebrtcTransport::CommandType::kEnd); + transport->SetCommandType(WebrtcTransport::CommandType::kEnd); - // Attempt to find or establish a CASE session to the target PeerId. - CASESessionManager *caseSessionMgr = - Server::GetInstance().GetCASESessionManager(); - VerifyOrDie(caseSessionMgr != nullptr); + // Attempt to find or establish a CASE session to the target PeerId. + CASESessionManager * caseSessionMgr = Server::GetInstance().GetCASESessionManager(); + VerifyOrDie(caseSessionMgr != nullptr); - // WebRTC Answer requires a large payload session establishment. - caseSessionMgr->FindOrEstablishSession( - peerId, &mOnConnectedCallback, &mOnConnectionFailureCallback, - TransportPayloadCapability::kLargePayload); - }); + // WebRTC Answer requires a large payload session establishment. + caseSessionMgr->FindOrEstablishSession(peerId, &mOnConnectedCallback, &mOnConnectionFailureCallback, + TransportPayloadCapability::kLargePayload); + }); } -void WebRTCProviderManager::ScheduleICECandidatesSend(uint16_t sessionId) { - ChipLogProgress(Camera, "ScheduleICECandidatesSend called."); +void WebRTCProviderManager::ScheduleICECandidatesSend(uint16_t sessionId) +{ + ChipLogProgress(Camera, "ScheduleICECandidatesSend called."); - DeviceLayer::SystemLayer().ScheduleLambda([this, sessionId]() { - WebrtcTransport *transport = GetTransport(sessionId); - if (transport == nullptr) { - return; + DeviceLayer::SystemLayer().ScheduleLambda([this, sessionId]() { + WebrtcTransport * transport = GetTransport(sessionId); + if (transport == nullptr) + { + return; + } + + WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs(); + chip::ScopedNodeId peerId = requestArgs.peerId; + ChipLogProgress(Camera, "Sending ICECandidates command to node " ChipLogFormatX64, ChipLogValueX64(peerId.GetNodeId())); + + transport->SetCommandType(WebrtcTransport::CommandType::kICECandidates); + + // Attempt to find or establish a CASE session to the target PeerId. + CASESessionManager * caseSessionMgr = Server::GetInstance().GetCASESessionManager(); + VerifyOrDie(caseSessionMgr != nullptr); + + // WebRTC Answer requires a large payload session establishment. + caseSessionMgr->FindOrEstablishSession(peerId, &mOnConnectedCallback, &mOnConnectionFailureCallback, + TransportPayloadCapability::kLargePayload); + }); +} + +void WebRTCProviderManager::OnDeviceConnected(void * context, Messaging::ExchangeManager & exchangeMgr, + const SessionHandle & sessionHandle) +{ + WebRTCProviderManager * self = reinterpret_cast(context); + VerifyOrReturn(self != nullptr, ChipLogError(Camera, "OnDeviceConnected:: context is null")); + + // Derive sessionId from sessionHandle by looking up the peer ScopedNodeId + // (NodeId + FabricIndex) + ScopedNodeId peerScopedNodeId = sessionHandle->GetPeer(); + auto sessionIt = self->mSessionIdMap.find(peerScopedNodeId); + if (sessionIt == self->mSessionIdMap.end()) + { + ChipLogError(Camera, + "OnDeviceConnected:: no session found for peer ScopedNodeId: " + "[%d:" ChipLogFormatX64 "]", + peerScopedNodeId.GetFabricIndex(), ChipLogValueX64(peerScopedNodeId.GetNodeId())); + return; + } + + uint16_t sessionId = sessionIt->second; + WebrtcTransport * transport = self->GetTransport(sessionId); + if (transport == nullptr) + { + ChipLogError(Camera, "OnDeviceConnected:: transport not found for sessionId: %u", sessionId); + return; } - WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs(); - chip::ScopedNodeId peerId = requestArgs.peerId; ChipLogProgress(Camera, - "Sending ICECandidates command to node " ChipLogFormatX64, - ChipLogValueX64(peerId.GetNodeId())); + "CASE session established, sending command with Command " + "Type: %d, for sessionID: %u", + static_cast(transport->GetCommandType()), sessionId); - transport->SetCommandType(WebrtcTransport::CommandType::kICECandidates); + CHIP_ERROR err = CHIP_NO_ERROR; - // Attempt to find or establish a CASE session to the target PeerId. - CASESessionManager *caseSessionMgr = - Server::GetInstance().GetCASESessionManager(); - VerifyOrDie(caseSessionMgr != nullptr); + switch (transport->GetCommandType()) + { + case WebrtcTransport::CommandType::kOffer: + err = self->SendOfferCommand(exchangeMgr, sessionHandle, sessionId); + transport->MoveToState(WebrtcTransport::State::Idle); + break; - // WebRTC Answer requires a large payload session establishment. - caseSessionMgr->FindOrEstablishSession( - peerId, &mOnConnectedCallback, &mOnConnectionFailureCallback, - TransportPayloadCapability::kLargePayload); - }); -} + case WebrtcTransport::CommandType::kAnswer: + err = self->SendAnswerCommand(exchangeMgr, sessionHandle, sessionId); + transport->MoveToState(WebrtcTransport::State::Idle); + break; + case WebrtcTransport::CommandType::kICECandidates: + err = self->SendICECandidatesCommand(exchangeMgr, sessionHandle, sessionId); + transport->MoveToState(WebrtcTransport::State::Idle); + break; + case WebrtcTransport::CommandType::kEnd: { + // Determine the end reason - check if it's due to privacy mode or resource + // exhaustion + WebRTCEndReasonEnum endReason = WebRTCEndReasonEnum::kOutOfResources; -void WebRTCProviderManager::OnDeviceConnected( - void *context, Messaging::ExchangeManager &exchangeMgr, - const SessionHandle &sessionHandle) { - WebRTCProviderManager *self = - reinterpret_cast(context); - VerifyOrReturn(self != nullptr, - ChipLogError(Camera, "OnDeviceConnected:: context is null")); + if (self->mSoftLiveStreamPrivacyEnabled) + { + endReason = WebRTCEndReasonEnum::kPrivacyMode; + } - // Derive sessionId from sessionHandle by looking up the peer ScopedNodeId - // (NodeId + FabricIndex) - ScopedNodeId peerScopedNodeId = sessionHandle->GetPeer(); - auto sessionIt = self->mSessionIdMap.find(peerScopedNodeId); - if (sessionIt == self->mSessionIdMap.end()) { - ChipLogError(Camera, - "OnDeviceConnected:: no session found for peer ScopedNodeId: " - "[%d:" ChipLogFormatX64 "]", - peerScopedNodeId.GetFabricIndex(), - ChipLogValueX64(peerScopedNodeId.GetNodeId())); - return; - } + err = self->SendEndCommand(exchangeMgr, sessionHandle, sessionId, endReason); + // Release the Video and Audio Streams from the CameraAVStreamManagement + // cluster and update the reference counts. + self->ReleaseAudioVideoStreams(sessionId); + self->UnregisterWebrtcTransport(sessionId); + WebrtcTransport::RequestArgs args = transport->GetRequestArgs(); + self->mSessionIdMap.erase(ScopedNodeId(args.peerNodeId, args.fabricIndex)); - uint16_t sessionId = sessionIt->second; - WebrtcTransport *transport = self->GetTransport(sessionId); - if (transport == nullptr) { - ChipLogError(Camera, - "OnDeviceConnected:: transport not found for sessionId: %u", - sessionId); - return; - } + transport->MoveToState(WebrtcTransport::State::Idle); - ChipLogProgress(Camera, - "CASE session established, sending command with Command " - "Type: %d, for sessionID: %u", - static_cast(transport->GetCommandType()), sessionId); - - CHIP_ERROR err = CHIP_NO_ERROR; - - switch (transport->GetCommandType()) { - case WebrtcTransport::CommandType::kOffer: - err = self->SendOfferCommand(exchangeMgr, sessionHandle, sessionId); - transport->MoveToState(WebrtcTransport::State::Idle); - break; - - case WebrtcTransport::CommandType::kAnswer: - err = self->SendAnswerCommand(exchangeMgr, sessionHandle, sessionId); - transport->MoveToState(WebrtcTransport::State::Idle); - break; - case WebrtcTransport::CommandType::kICECandidates: - err = self->SendICECandidatesCommand(exchangeMgr, sessionHandle, sessionId); - transport->MoveToState(WebrtcTransport::State::Idle); - break; - case WebrtcTransport::CommandType::kEnd: { - // Determine the end reason - check if it's due to privacy mode or resource - // exhaustion - WebRTCEndReasonEnum endReason = WebRTCEndReasonEnum::kOutOfResources; - - if (self->mSoftLiveStreamPrivacyEnabled) { - endReason = WebRTCEndReasonEnum::kPrivacyMode; + // remove from current sessions list + self->mWebRTCTransportProvider->RemoveSession(sessionId); + self->mWebrtcTransportMap.erase(sessionId); + break; + } + default: + err = CHIP_ERROR_INVALID_ARGUMENT; + break; } - err = - self->SendEndCommand(exchangeMgr, sessionHandle, sessionId, endReason); - // Release the Video and Audio Streams from the CameraAVStreamManagement - // cluster and update the reference counts. - self->ReleaseAudioVideoStreams(sessionId); - self->UnregisterWebrtcTransport(sessionId); - WebrtcTransport::RequestArgs args = transport->GetRequestArgs(); - self->mSessionIdMap.erase(ScopedNodeId(args.peerNodeId, args.fabricIndex)); - - transport->MoveToState(WebrtcTransport::State::Idle); - - // remove from current sessions list - self->mWebRTCTransportProvider->RemoveSession(sessionId); - self->mWebrtcTransportMap.erase(sessionId); - break; - } - default: - err = CHIP_ERROR_INVALID_ARGUMENT; - break; - } - - if (err != CHIP_NO_ERROR) { - ChipLogError(Camera, - "OnDeviceConnected::SendCommand failed: %" CHIP_ERROR_FORMAT, - err.Format()); - } -} - -void WebRTCProviderManager::OnDeviceConnectionFailure( - void *context, const ScopedNodeId &peerId, CHIP_ERROR err) { - LogErrorOnFailure(err); - WebRTCProviderManager *self = - reinterpret_cast(context); - VerifyOrReturn( - self != nullptr, - ChipLogError(Camera, "OnDeviceConnectionFailure: context is null")); -} - -WebrtcTransport *WebRTCProviderManager::GetTransport(uint16_t sessionId) { - WebrtcTransport *transport = nullptr; - if (mWebrtcTransportMap.find(sessionId) != mWebrtcTransportMap.end()) { - transport = mWebrtcTransportMap[sessionId].get(); - } - - return transport; -} - -void WebRTCProviderManager::LiveStreamPrivacyModeChanged( - bool privacyModeEnabled) { - mSoftLiveStreamPrivacyEnabled = privacyModeEnabled; - - if (privacyModeEnabled) { - WebrtcTransport *transport = nullptr; - uint16_t sessionId = 0; - for (auto &mapEntry : mWebrtcTransportMap) { - sessionId = mapEntry.first; - - transport = (WebrtcTransport *)mapEntry.second.get(); - - if (transport == nullptr) { - continue; - } - - transport->MoveToState(WebrtcTransport::State::SendingEnd); - - ScheduleEndSend(sessionId); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Camera, "OnDeviceConnected::SendCommand failed: %" CHIP_ERROR_FORMAT, err.Format()); + } +} + +void WebRTCProviderManager::OnDeviceConnectionFailure(void * context, const ScopedNodeId & peerId, CHIP_ERROR err) +{ + LogErrorOnFailure(err); + WebRTCProviderManager * self = reinterpret_cast(context); + VerifyOrReturn(self != nullptr, ChipLogError(Camera, "OnDeviceConnectionFailure: context is null")); +} + +WebrtcTransport * WebRTCProviderManager::GetTransport(uint16_t sessionId) +{ + WebrtcTransport * transport = nullptr; + if (mWebrtcTransportMap.find(sessionId) != mWebrtcTransportMap.end()) + { + transport = mWebrtcTransportMap[sessionId].get(); + } + + return transport; +} + +void WebRTCProviderManager::LiveStreamPrivacyModeChanged(bool privacyModeEnabled) +{ + mSoftLiveStreamPrivacyEnabled = privacyModeEnabled; + + if (privacyModeEnabled) + { + WebrtcTransport * transport = nullptr; + uint16_t sessionId = 0; + for (auto & mapEntry : mWebrtcTransportMap) + { + sessionId = mapEntry.first; + + transport = (WebrtcTransport *) mapEntry.second.get(); + + if (transport == nullptr) + { + continue; + } + + transport->MoveToState(WebrtcTransport::State::SendingEnd); + + ScheduleEndSend(sessionId); + } + } + else + { + ChipLogProgress(Camera, "Privacy mode is disabled"); } - } else { - ChipLogProgress(Camera, "Privacy mode is disabled"); - } } CHIP_ERROR -WebRTCProviderManager::SendOfferCommand(Messaging::ExchangeManager &exchangeMgr, - const SessionHandle &sessionHandle, - uint16_t sessionId) { - auto onSuccess = [](const ConcreteCommandPath &commandPath, - const StatusIB &status, const auto &dataResponse) { - ChipLogProgress(Camera, "Offer command succeeds"); - }; +WebRTCProviderManager::SendOfferCommand(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle, + uint16_t sessionId) +{ + auto onSuccess = [](const ConcreteCommandPath & commandPath, const StatusIB & status, const auto & dataResponse) { + ChipLogProgress(Camera, "Offer command succeeds"); + }; - auto onFailure = [](CHIP_ERROR error) { - ChipLogError(Camera, "Offer command failed: %" CHIP_ERROR_FORMAT, - error.Format()); - }; + auto onFailure = [](CHIP_ERROR error) { ChipLogError(Camera, "Offer command failed: %" CHIP_ERROR_FORMAT, error.Format()); }; - WebrtcTransport *transport = GetTransport(sessionId); - if (transport == nullptr) { - ChipLogError( - Camera, - "SendOfferCommand failed, WebTransport not found for sessionId: %u", - sessionId); - return CHIP_ERROR_INTERNAL; - } + WebrtcTransport * transport = GetTransport(sessionId); + if (transport == nullptr) + { + ChipLogError(Camera, "SendOfferCommand failed, WebTransport not found for sessionId: %u", sessionId); + return CHIP_ERROR_INTERNAL; + } - // Build the command - WebRTCTransportRequestor::Commands::Offer::Type command; - command.webRTCSessionID = sessionId; - std::string localSdp = transport->GetLocalDescription(); - command.sdp = CharSpan::fromCharString(localSdp.c_str()); + // Build the command + WebRTCTransportRequestor::Commands::Offer::Type command; + command.webRTCSessionID = sessionId; + std::string localSdp = transport->GetLocalDescription(); + command.sdp = CharSpan::fromCharString(localSdp.c_str()); - WebrtcTransport::RequestArgs args = transport->GetRequestArgs(); - // Now invoke the command using the found session handle - return Controller::InvokeCommandRequest( - &exchangeMgr, sessionHandle, args.originatingEndpointId, command, - onSuccess, onFailure, - /* timedInvokeTimeoutMs = */ NullOptional, - /* responseTimeout = */ NullOptional, - /* outCancelFn = */ nullptr, /*allowLargePayload = */ true); + WebrtcTransport::RequestArgs args = transport->GetRequestArgs(); + // Now invoke the command using the found session handle + return Controller::InvokeCommandRequest(&exchangeMgr, sessionHandle, args.originatingEndpointId, command, onSuccess, onFailure, + /* timedInvokeTimeoutMs = */ NullOptional, + /* responseTimeout = */ NullOptional, + /* outCancelFn = */ nullptr, /*allowLargePayload = */ true); } -void WebRTCProviderManager::OnLocalDescription(const std::string &sdp, - SDPType type, - const uint16_t sessionId) { - WebrtcTransport *transport = GetTransport(sessionId); - if (transport == nullptr) { - ChipLogError( - Camera, - "SendOfferCommand failed, WebTransport not found for sessionId: %u", - sessionId); - return; - } - - WebrtcTransport::State state = transport->GetState(); - if (state == WebrtcTransport::State::SendingAnswer && - type != SDPType::Answer) { - return; - } - const char *typeStr = (type == SDPType::Offer) ? "offer" : "answer"; - std::string localSdp = sdp; - ChipLogProgress(Camera, "Local Description (%s):", typeStr); - ChipLogProgress(Camera, "%s", localSdp.c_str()); - printf("Size of SDP: %zu\n", localSdp.size()); - - switch (state) { - case WebrtcTransport::State::SendingOffer: - ScheduleOfferSend(sessionId); - break; - case WebrtcTransport::State::SendingAnswer: - // sdp_mem_dump(); - ScheduleAnswerSend(sessionId); - break; - default: - break; - } -} - -void WebRTCProviderManager::OnConnectionStateChanged(bool connected, - const uint16_t sessionId) { - ChipLogProgress(Camera, "Connection state changed for session %u: %s", - sessionId, connected ? "connected" : "disconnected"); - - if (connected) { - RegisterWebrtcTransport(sessionId); - } else { - // Schedule cleanup on Matter thread to ensure proper locking when calling - // RemoveSession. Safe to capture 'this' by value: WebRTCProviderManager is - // a member of the global CameraDevice object which has static storage - // duration and lives for the entire program lifetime. - DeviceLayer::SystemLayer().ScheduleLambda([this, sessionId]() { - WebrtcTransport *transport = GetTransport(sessionId); - if (transport == nullptr) { - ChipLogProgress(Camera, - "Transport not found for session %u during disconnect; " - "session may have already been cleaned up", - sessionId); +void WebRTCProviderManager::OnLocalDescription(const std::string & sdp, SDPType type, const uint16_t sessionId) +{ + WebrtcTransport * transport = GetTransport(sessionId); + if (transport == nullptr) + { + ChipLogError(Camera, "SendOfferCommand failed, WebTransport not found for sessionId: %u", sessionId); return; - } + } - // Connection was closed/disconnected by the peer - clean up the session - ChipLogProgress( - Camera, - "Peer connection closed for session %u, cleaning up resources", - sessionId); + WebrtcTransport::State state = transport->GetState(); + if (state == WebrtcTransport::State::SendingAnswer && type != SDPType::Answer) + { + return; + } + const char * typeStr = (type == SDPType::Offer) ? "offer" : "answer"; + std::string localSdp = sdp; + ChipLogProgress(Camera, "Local Description (%s):", typeStr); + ChipLogProgress(Camera, "%s", localSdp.c_str()); + ChipLogProgress(Camera, "Size of SDP: %zu", localSdp.size()); - // Release the Video and Audio Streams from the CameraAVStreamManagement - // cluster and update the reference counts. - ReleaseAudioVideoStreams(sessionId); - - // Capture args before unregistering in case the transport is invalidated - WebrtcTransport::RequestArgs args = transport->GetRequestArgs(); - - // Unregister the transport from the media controller - UnregisterWebrtcTransport(sessionId); - - // Remove from session maps - mSessionIdMap.erase(ScopedNodeId(args.peerNodeId, args.fabricIndex)); - - // Remove from current sessions list in the WebRTC Transport Provider - // This MUST be called on the Matter thread with the stack lock held - if (mWebRTCTransportProvider != nullptr) { - mWebRTCTransportProvider->RemoveSession(sessionId); - } - - // Finally, remove and destroy the transport - mWebrtcTransportMap.erase(sessionId); - - ChipLogProgress(Camera, "Session %u cleanup completed", sessionId); - }); - } -} -CHIP_ERROR WebRTCProviderManager::SendAnswerCommand( - Messaging::ExchangeManager &exchangeMgr, const SessionHandle &sessionHandle, - uint16_t sessionId) { - auto onSuccess = [](const ConcreteCommandPath &commandPath, - const StatusIB &status, const auto &dataResponse) { - ChipLogProgress(Camera, "Answer command succeeds"); - }; - - auto onFailure = [](CHIP_ERROR error) { - ChipLogError(Camera, "Answer command failed: %" CHIP_ERROR_FORMAT, - error.Format()); - }; - - WebrtcTransport *transport = GetTransport(sessionId); - if (transport == nullptr) { - ChipLogError( - Camera, - "SendOfferCommand failed, WebTransport not found for sessionId: %u", - sessionId); - return CHIP_ERROR_INTERNAL; - } - - // Build the command - WebRTCTransportRequestor::Commands::Answer::Type command; - command.webRTCSessionID = sessionId; - std::string localSdp = transport->GetLocalDescription(); - command.sdp = CharSpan::fromCharString(localSdp.c_str()); - - WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs(); - printf("\nInvoke Answer Command\n"); - // Now invoke the command using the found session handle - return Controller::InvokeCommandRequest( - &exchangeMgr, sessionHandle, requestArgs.originatingEndpointId, command, - onSuccess, onFailure, - /* timedInvokeTimeoutMs = */ NullOptional, - /* responseTimeout = */ NullOptional, - /* outCancelFn = */ nullptr, /*allowLargePayload = */ true); + switch (state) + { + case WebrtcTransport::State::SendingOffer: + ScheduleOfferSend(sessionId); + break; + case WebrtcTransport::State::SendingAnswer: + ScheduleAnswerSend(sessionId); + break; + default: + break; + } } -CHIP_ERROR WebRTCProviderManager::SendICECandidatesCommand( - Messaging::ExchangeManager &exchangeMgr, const SessionHandle &sessionHandle, - uint16_t sessionId) { - auto onSuccess = [](const ConcreteCommandPath &commandPath, - const StatusIB &status, const auto &dataResponse) { - ChipLogProgress(Camera, "ICECandidates command succeeds"); - }; +void WebRTCProviderManager::OnConnectionStateChanged(bool connected, const uint16_t sessionId) +{ + ChipLogProgress(Camera, "Connection state changed for session %u: %s", sessionId, connected ? "connected" : "disconnected"); - auto onFailure = [](CHIP_ERROR error) { - ChipLogError(Camera, "ICECandidates command failed: %" CHIP_ERROR_FORMAT, - error.Format()); - }; + if (connected) + { + RegisterWebrtcTransport(sessionId); + } + else + { + // Schedule cleanup on Matter thread to ensure proper locking when calling + // RemoveSession. Safe to capture 'this' by value: WebRTCProviderManager is + // a member of the global CameraDevice object which has static storage + // duration and lives for the entire program lifetime. + DeviceLayer::SystemLayer().ScheduleLambda([this, sessionId]() { + WebrtcTransport * transport = GetTransport(sessionId); + if (transport == nullptr) + { + ChipLogProgress(Camera, + "Transport not found for session %u during disconnect; " + "session may have already been cleaned up", + sessionId); + return; + } - WebrtcTransport *transport = GetTransport(sessionId); - if (transport == nullptr) { - ChipLogError(Camera, "WebTransport not found for the sessionId: %u", - sessionId); - return CHIP_ERROR_INTERNAL; - } - std::vector localCandidates = transport->GetCandidates(); - // Build the command - WebRTCTransportRequestor::Commands::ICECandidates::Type command; + // Connection was closed/disconnected by the peer - clean up the session + ChipLogProgress(Camera, "Peer connection closed for session %u, cleaning up resources", sessionId); - if (localCandidates.empty()) { - ChipLogError(Camera, "No local ICE candidates to send"); - return CHIP_ERROR_INCORRECT_STATE; - } + // Release the Video and Audio Streams from the CameraAVStreamManagement + // cluster and update the reference counts. + ReleaseAudioVideoStreams(sessionId); - std::vector iceCandidateStructList; - for (const auto &candidate : localCandidates) { - ICECandidateStruct iceCandidate = { - CharSpan::fromCharString(candidate.c_str())}; - iceCandidateStructList.push_back(iceCandidate); - } + // Capture args before unregistering in case the transport is invalidated + WebrtcTransport::RequestArgs args = transport->GetRequestArgs(); - command.webRTCSessionID = sessionId; - command.ICECandidates = DataModel::List( - iceCandidateStructList.data(), iceCandidateStructList.size()); + // Unregister the transport from the media controller + UnregisterWebrtcTransport(sessionId); - WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs(); - // Now invoke the command using the found session handle - return Controller::InvokeCommandRequest( - &exchangeMgr, sessionHandle, requestArgs.originatingEndpointId, command, - onSuccess, onFailure, - /* timedInvokeTimeoutMs = */ NullOptional, - /* responseTimeout = */ NullOptional, - /* outCancelFn = */ nullptr, /*allowLargePayload = */ true); + // Remove from session maps + mSessionIdMap.erase(ScopedNodeId(args.peerNodeId, args.fabricIndex)); + + // Remove from current sessions list in the WebRTC Transport Provider + // This MUST be called on the Matter thread with the stack lock held + if (mWebRTCTransportProvider != nullptr) + { + mWebRTCTransportProvider->RemoveSession(sessionId); + } + + // Finally, remove and destroy the transport + mWebrtcTransportMap.erase(sessionId); + + ChipLogProgress(Camera, "Session %u cleanup completed", sessionId); + }); + } +} +CHIP_ERROR WebRTCProviderManager::SendAnswerCommand(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle, + uint16_t sessionId) +{ + auto onSuccess = [](const ConcreteCommandPath & commandPath, const StatusIB & status, const auto & dataResponse) { + ChipLogProgress(Camera, "Answer command succeeds"); + }; + + auto onFailure = [](CHIP_ERROR error) { ChipLogError(Camera, "Answer command failed: %" CHIP_ERROR_FORMAT, error.Format()); }; + + WebrtcTransport * transport = GetTransport(sessionId); + if (transport == nullptr) + { + ChipLogError(Camera, "SendOfferCommand failed, WebTransport not found for sessionId: %u", sessionId); + return CHIP_ERROR_INTERNAL; + } + + // Build the command + WebRTCTransportRequestor::Commands::Answer::Type command; + command.webRTCSessionID = sessionId; + std::string localSdp = transport->GetLocalDescription(); + command.sdp = CharSpan::fromCharString(localSdp.c_str()); + + WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs(); + // Now invoke the command using the found session handle + return Controller::InvokeCommandRequest(&exchangeMgr, sessionHandle, requestArgs.originatingEndpointId, command, onSuccess, + onFailure, + /* timedInvokeTimeoutMs = */ NullOptional, + /* responseTimeout = */ NullOptional, + /* outCancelFn = */ nullptr, /*allowLargePayload = */ true); } -CHIP_ERROR WebRTCProviderManager::SendEndCommand( - Messaging::ExchangeManager &exchangeMgr, const SessionHandle &sessionHandle, - uint16_t sessionId, WebRTCEndReasonEnum endReason) { - auto onSuccess = [](const ConcreteCommandPath &commandPath, - const StatusIB &status, const auto &dataResponse) { - ChipLogProgress(Camera, "End command succeeds"); - }; +CHIP_ERROR WebRTCProviderManager::SendICECandidatesCommand(Messaging::ExchangeManager & exchangeMgr, + const SessionHandle & sessionHandle, uint16_t sessionId) +{ + auto onSuccess = [](const ConcreteCommandPath & commandPath, const StatusIB & status, const auto & dataResponse) { + ChipLogProgress(Camera, "ICECandidates command succeeds"); + }; - auto onFailure = [](CHIP_ERROR error) { - ChipLogError(Camera, "End command failed: %" CHIP_ERROR_FORMAT, - error.Format()); - }; + auto onFailure = [](CHIP_ERROR error) { + ChipLogError(Camera, "ICECandidates command failed: %" CHIP_ERROR_FORMAT, error.Format()); + }; - WebrtcTransport *transport = GetTransport(sessionId); - if (transport == nullptr) { - ChipLogError(Camera, "WebTransport not found for the sessionId: %u", - sessionId); - return CHIP_ERROR_INTERNAL; - } + WebrtcTransport * transport = GetTransport(sessionId); + if (transport == nullptr) + { + ChipLogError(Camera, "WebTransport not found for the sessionId: %u", sessionId); + return CHIP_ERROR_INTERNAL; + } + std::vector localCandidates = transport->GetCandidates(); + // Build the command + WebRTCTransportRequestor::Commands::ICECandidates::Type command; - // Build the command - WebRTCTransportRequestor::Commands::End::Type command; + if (localCandidates.empty()) + { + ChipLogError(Camera, "No local ICE candidates to send"); + return CHIP_ERROR_INCORRECT_STATE; + } - command.webRTCSessionID = sessionId; - command.reason = endReason; + std::vector iceCandidateStructList; + for (const auto & candidate : localCandidates) + { + ICECandidateStruct iceCandidate = { CharSpan::fromCharString(candidate.c_str()) }; + iceCandidateStructList.push_back(iceCandidate); + } - WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs(); - // Now invoke the command using the found session handle - return Controller::InvokeCommandRequest( - &exchangeMgr, sessionHandle, requestArgs.originatingEndpointId, command, - onSuccess, onFailure, - /* timedInvokeTimeoutMs = */ NullOptional, - /* responseTimeout = */ NullOptional, - /* outCancelFn = */ nullptr, /*allowLargePayload = */ true); + command.webRTCSessionID = sessionId; + command.ICECandidates = DataModel::List(iceCandidateStructList.data(), iceCandidateStructList.size()); + + WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs(); + // Now invoke the command using the found session handle + return Controller::InvokeCommandRequest(&exchangeMgr, sessionHandle, requestArgs.originatingEndpointId, command, onSuccess, + onFailure, + /* timedInvokeTimeoutMs = */ NullOptional, + /* responseTimeout = */ NullOptional, + /* outCancelFn = */ nullptr, /*allowLargePayload = */ true); } -CHIP_ERROR WebRTCProviderManager::AcquireAudioVideoStreams(uint16_t sessionId) { - WebrtcTransport *transport = GetTransport(sessionId); - if (transport == nullptr) { - ChipLogError(Camera, "WebTransport not found for the sessionId: %u", - sessionId); - return CHIP_ERROR_INTERNAL; - } +CHIP_ERROR WebRTCProviderManager::SendEndCommand(Messaging::ExchangeManager & exchangeMgr, const SessionHandle & sessionHandle, + uint16_t sessionId, WebRTCEndReasonEnum endReason) +{ + auto onSuccess = [](const ConcreteCommandPath & commandPath, const StatusIB & status, const auto & dataResponse) { + ChipLogProgress(Camera, "End command succeeds"); + }; - WebrtcTransport::RequestArgs args = transport->GetRequestArgs(); - return mCameraDevice->GetCameraAVStreamMgmtDelegate() - .OnTransportAcquireAudioVideoStreams(args.audioStreamId, - args.videoStreamId); + auto onFailure = [](CHIP_ERROR error) { ChipLogError(Camera, "End command failed: %" CHIP_ERROR_FORMAT, error.Format()); }; + + WebrtcTransport * transport = GetTransport(sessionId); + if (transport == nullptr) + { + ChipLogError(Camera, "WebTransport not found for the sessionId: %u", sessionId); + return CHIP_ERROR_INTERNAL; + } + + // Build the command + WebRTCTransportRequestor::Commands::End::Type command; + + command.webRTCSessionID = sessionId; + command.reason = endReason; + + WebrtcTransport::RequestArgs requestArgs = transport->GetRequestArgs(); + // Now invoke the command using the found session handle + return Controller::InvokeCommandRequest(&exchangeMgr, sessionHandle, requestArgs.originatingEndpointId, command, onSuccess, + onFailure, + /* timedInvokeTimeoutMs = */ NullOptional, + /* responseTimeout = */ NullOptional, + /* outCancelFn = */ nullptr, /*allowLargePayload = */ true); } -CHIP_ERROR WebRTCProviderManager::ReleaseAudioVideoStreams(uint16_t sessionId) { - WebrtcTransport *transport = GetTransport(sessionId); - if (transport == nullptr) { - ChipLogError(Camera, "WebTransport not found for the sessionId: %u", - sessionId); - return CHIP_ERROR_INTERNAL; - } +CHIP_ERROR WebRTCProviderManager::AcquireAudioVideoStreams(uint16_t sessionId) +{ + WebrtcTransport * transport = GetTransport(sessionId); + if (transport == nullptr) + { + ChipLogError(Camera, "WebTransport not found for the sessionId: %u", sessionId); + return CHIP_ERROR_INTERNAL; + } - WebrtcTransport::RequestArgs args = transport->GetRequestArgs(); - // TODO: Use passed in audio/video stream ids corresponding to a sessionId. - return mCameraDevice->GetCameraAVStreamMgmtDelegate() - .OnTransportReleaseAudioVideoStreams(args.audioStreamId, - args.videoStreamId); + WebrtcTransport::RequestArgs args = transport->GetRequestArgs(); + return mCameraDevice->GetCameraAVStreamMgmtDelegate().OnTransportAcquireAudioVideoStreams(args.audioStreamId, + args.videoStreamId); +} + +CHIP_ERROR WebRTCProviderManager::ReleaseAudioVideoStreams(uint16_t sessionId) +{ + WebrtcTransport * transport = GetTransport(sessionId); + if (transport == nullptr) + { + ChipLogError(Camera, "WebTransport not found for the sessionId: %u", sessionId); + return CHIP_ERROR_INTERNAL; + } + + WebrtcTransport::RequestArgs args = transport->GetRequestArgs(); + // TODO: Use passed in audio/video stream ids corresponding to a sessionId. + return mCameraDevice->GetCameraAVStreamMgmtDelegate().OnTransportReleaseAudioVideoStreams(args.audioStreamId, + args.videoStreamId); } diff --git a/examples/camera/main/clusters/webrtc-provider-manager.h b/examples/camera/main/clusters/webrtc-provider-manager.h index 6f5d4b8ec..6ac943541 100644 --- a/examples/camera/main/clusters/webrtc-provider-manager.h +++ b/examples/camera/main/clusters/webrtc-provider-manager.h @@ -1,9 +1,26 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * All rights reserved. + * + * 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. + */ #pragma once #include "camera-device-interface.h" #include #include -#include +#include #include #include @@ -12,156 +29,129 @@ namespace app { namespace Clusters { namespace WebRTCTransportProvider { -using ICEServerDecodableStruct = - chip::app::Clusters::Globals::Structs::ICEServerStruct::DecodableType; -using WebRTCSessionStruct = - chip::app::Clusters::Globals::Structs::WebRTCSessionStruct::Type; -using ICECandidateStruct = - chip::app::Clusters::Globals::Structs::ICECandidateStruct::Type; -using StreamUsageEnum = chip::app::Clusters::Globals::StreamUsageEnum; -using WebRTCEndReasonEnum = chip::app::Clusters::Globals::WebRTCEndReasonEnum; +using ICEServerDecodableStruct = chip::app::Clusters::Globals::Structs::ICEServerStruct::DecodableType; +using WebRTCSessionStruct = chip::app::Clusters::Globals::Structs::WebRTCSessionStruct::Type; +using ICECandidateStruct = chip::app::Clusters::Globals::Structs::ICECandidateStruct::Type; +using StreamUsageEnum = chip::app::Clusters::Globals::StreamUsageEnum; +using WebRTCEndReasonEnum = chip::app::Clusters::Globals::WebRTCEndReasonEnum; -class WebRTCProviderManager : public Delegate, - public WebRTCTransportProviderController { +class WebRTCProviderManager : public Delegate +{ public: - WebRTCProviderManager() - : mOnConnectedCallback(OnDeviceConnected, this), - mOnConnectionFailureCallback(OnDeviceConnectionFailure, this) {} + WebRTCProviderManager() : + mOnConnectedCallback(OnDeviceConnected, this), mOnConnectionFailureCallback(OnDeviceConnectionFailure, this) + {} - ~WebRTCProviderManager() { CloseConnection(); }; + ~WebRTCProviderManager() { CloseConnection(); }; - void Init(); + void Init(); - void CloseConnection(); + void CloseConnection(); - void SetWebRTCTransportProvider(std::unique_ptr - webRTCTransportProvider) override; + void SetWebRTCTransportProvider(WebRTCTransportProviderCluster * webRTCTransportProvider); - CHIP_ERROR HandleSolicitOffer( - const OfferRequestArgs &args, - chip::app::Clusters::WebRTCTransportProvider::WebRTCSessionStruct - &outSession, - bool &outDeferredOffer) override; + CHIP_ERROR HandleSolicitOffer(const OfferRequestArgs & args, + chip::app::Clusters::WebRTCTransportProvider::WebRTCSessionStruct & outSession, + bool & outDeferredOffer) override; - CHIP_ERROR - HandleProvideOffer( - const ProvideOfferRequestArgs &args, - chip::app::Clusters::WebRTCTransportProvider::WebRTCSessionStruct - &outSession) override; + CHIP_ERROR + HandleProvideOffer(const ProvideOfferRequestArgs & args, + chip::app::Clusters::WebRTCTransportProvider::WebRTCSessionStruct & outSession) override; - CHIP_ERROR HandleProvideAnswer(uint16_t sessionId, - const std::string &sdpAnswer) override; + CHIP_ERROR HandleProvideAnswer(uint16_t sessionId, const std::string & sdpAnswer) override; - CHIP_ERROR HandleProvideICECandidates( - uint16_t sessionId, - const std::vector &candidates) override; + CHIP_ERROR HandleProvideICECandidates(uint16_t sessionId, const std::vector & candidates) override; - CHIP_ERROR HandleEndSession( - uint16_t sessionId, - chip::app::Clusters::WebRTCTransportProvider::WebRTCEndReasonEnum - reasonCode, - chip::app::DataModel::Nullable videoStreamID, - chip::app::DataModel::Nullable audioStreamID) override; + CHIP_ERROR HandleEndSession(uint16_t sessionId, chip::app::Clusters::WebRTCTransportProvider::WebRTCEndReasonEnum reasonCode, + chip::app::DataModel::Nullable videoStreamID, + chip::app::DataModel::Nullable audioStreamID) override; - CHIP_ERROR ValidateStreamUsage( - StreamUsageEnum streamUsage, - chip::Optional> &videoStreamId, - chip::Optional> &audioStreamId) - override; + CHIP_ERROR ValidateStreamUsage(StreamUsageEnum streamUsage, + chip::Optional> & videoStreamId, + chip::Optional> & audioStreamId) override; - void SetCameraDevice(CameraDeviceInterface *aCameraDevice); + void SetCameraDevice(CameraDeviceInterface * aCameraDevice); - CHIP_ERROR ValidateVideoStreamID(uint16_t videoStreamId) override; + CHIP_ERROR ValidateVideoStreamID(uint16_t videoStreamId) override; - CHIP_ERROR ValidateAudioStreamID(uint16_t audioStreamId) override; + CHIP_ERROR ValidateAudioStreamID(uint16_t audioStreamId) override; - CHIP_ERROR IsStreamUsageSupported(StreamUsageEnum streamUsage) override; + CHIP_ERROR IsStreamUsageSupported(StreamUsageEnum streamUsage) override; - CHIP_ERROR IsHardPrivacyModeActive(bool &isActive) override; + CHIP_ERROR IsHardPrivacyModeActive(bool & isActive) override; - CHIP_ERROR IsSoftRecordingPrivacyModeActive(bool &isActive) override; + CHIP_ERROR IsSoftRecordingPrivacyModeActive(bool & isActive) override; - CHIP_ERROR IsSoftLivestreamPrivacyModeActive(bool &isActive) override; + CHIP_ERROR IsSoftLivestreamPrivacyModeActive(bool & isActive) override; - bool HasAllocatedVideoStreams() override; + bool HasAllocatedVideoStreams() override; - bool HasAllocatedAudioStreams() override; + bool HasAllocatedAudioStreams() override; - CHIP_ERROR ValidateSFrameConfig(uint16_t cipherSuite, size_t baseKeyLength) override; + CHIP_ERROR ValidateSFrameConfig(uint16_t cipherSuite, size_t baseKeyLength) override; - CHIP_ERROR IsUTCTimeNull(bool & isNull) override; + CHIP_ERROR IsUTCTimeNull(bool & isNull) override; - void LiveStreamPrivacyModeChanged(bool privacyModeEnabled); + void LiveStreamPrivacyModeChanged(bool privacyModeEnabled); - WebrtcTransport *GetTransport(uint16_t sessionId); + WebrtcTransport * GetTransport(uint16_t sessionId); private: - void ScheduleOfferSend(uint16_t sessionId); + void ScheduleOfferSend(uint16_t sessionId); - void ScheduleICECandidatesSend(uint16_t sessionId); + void ScheduleICECandidatesSend(uint16_t sessionId); - void ScheduleAnswerSend(uint16_t sessionId); + void ScheduleAnswerSend(uint16_t sessionId); - void ScheduleEndSend(uint16_t sessionId); + void ScheduleEndSend(uint16_t sessionId); - void RegisterWebrtcTransport(uint16_t sessionId); + void RegisterWebrtcTransport(uint16_t sessionId); - void UnregisterWebrtcTransport(uint16_t sessionId); + void UnregisterWebrtcTransport(uint16_t sessionId); - CHIP_ERROR SendOfferCommand(chip::Messaging::ExchangeManager &exchangeMgr, - const chip::SessionHandle &sessionHandle, - uint16_t sessionId); + CHIP_ERROR SendOfferCommand(chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle, + uint16_t sessionId); - CHIP_ERROR SendAnswerCommand(chip::Messaging::ExchangeManager &exchangeMgr, - const chip::SessionHandle &sessionHandle, - uint16_t sessionId); + CHIP_ERROR SendAnswerCommand(chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle, + uint16_t sessionId); - CHIP_ERROR - SendICECandidatesCommand(chip::Messaging::ExchangeManager &exchangeMgr, - const chip::SessionHandle &sessionHandle, - uint16_t sessionId); + CHIP_ERROR + SendICECandidatesCommand(chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle, + uint16_t sessionId); - CHIP_ERROR SendEndCommand(chip::Messaging::ExchangeManager &exchangeMgr, - const chip::SessionHandle &sessionHandle, - uint16_t sessionId, WebRTCEndReasonEnum endReason); + CHIP_ERROR SendEndCommand(chip::Messaging::ExchangeManager & exchangeMgr, const chip::SessionHandle & sessionHandle, + uint16_t sessionId, WebRTCEndReasonEnum endReason); - CHIP_ERROR AcquireAudioVideoStreams(uint16_t sessionId); + CHIP_ERROR AcquireAudioVideoStreams(uint16_t sessionId); - CHIP_ERROR ReleaseAudioVideoStreams(uint16_t sessionId); + CHIP_ERROR ReleaseAudioVideoStreams(uint16_t sessionId); - static void OnDeviceConnected(void *context, - chip::Messaging::ExchangeManager &exchangeMgr, - const chip::SessionHandle &sessionHandle); + static void OnDeviceConnected(void * context, chip::Messaging::ExchangeManager & exchangeMgr, + const chip::SessionHandle & sessionHandle); - static void OnDeviceConnectionFailure(void *context, - const chip::ScopedNodeId &peerId, - CHIP_ERROR error); + static void OnDeviceConnectionFailure(void * context, const chip::ScopedNodeId & peerId, CHIP_ERROR error); - // WebRTC Callbacks - void OnLocalDescription(const std::string &sdp, SDPType type, - const uint16_t sessionId); - void OnConnectionStateChanged(bool connected, const uint16_t sessionId); + // WebRTC Callbacks + void OnLocalDescription(const std::string & sdp, SDPType type, const uint16_t sessionId); + void OnConnectionStateChanged(bool connected, const uint16_t sessionId); - chip::Callback::Callback mOnConnectedCallback; - chip::Callback::Callback - mOnConnectionFailureCallback; + chip::Callback::Callback mOnConnectedCallback; + chip::Callback::Callback mOnConnectionFailureCallback; - std::unordered_map> - mWebrtcTransportMap; - // This is to retrieve the sessionIds for a given NodeId - std::map mSessionIdMap; + std::unordered_map> mWebrtcTransportMap; + // This is to retrieve the sessionIds for a given NodeId + std::map mSessionIdMap; - std::unique_ptr mWebRTCTransportProvider = - nullptr; + WebRTCTransportProviderCluster * mWebRTCTransportProvider = nullptr; - // Handle to the Camera Device interface. For accessing other - // clusters, if required. - CameraDeviceInterface *mCameraDevice = nullptr; + // Handle to the Camera Device interface. For accessing other + // clusters, if required. + CameraDeviceInterface * mCameraDevice = nullptr; - bool mSoftLiveStreamPrivacyEnabled = false; + bool mSoftLiveStreamPrivacyEnabled = false; }; } // namespace WebRTCTransportProvider } // namespace Clusters } // namespace app -} // namespace chip \ No newline at end of file +} // namespace chip diff --git a/examples/camera/main/common/camera-app.cpp b/examples/camera/main/common/camera-app.cpp index 7f3770ede..922215849 100644 --- a/examples/camera/main/common/camera-app.cpp +++ b/examples/camera/main/common/camera-app.cpp @@ -1,4 +1,22 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * All rights reserved. + * + * 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. + */ #include "camera-app.h" +#include "esp_matter_data_model_provider.h" using namespace chip; using namespace chip::app; @@ -9,235 +27,225 @@ using namespace chip::app::Clusters::CameraAvStreamManagement; static constexpr uint32_t kBitsPerMegabit = 1000000; -template using List = chip::app::DataModel::List; +template +using List = chip::app::DataModel::List; using Status = Protocols::InteractionModel::Status; -CameraApp::CameraApp(chip::EndpointId aClustersEndpoint, - CameraDeviceInterface *aCameraDevice) { - mEndpoint = aClustersEndpoint; - mCameraDevice = aCameraDevice; +CameraApp::CameraApp(chip::EndpointId aClustersEndpoint, CameraDeviceInterface * aCameraDevice) +{ + mEndpoint = aClustersEndpoint; + mCameraDevice = aCameraDevice; - // Instantiate WebRTCTransport Provider - mWebRTCTransportProviderPtr = std::make_unique( - mCameraDevice->GetWebRTCProviderDelegate(), mEndpoint); + // Fetch all initialization parameters for CameraAVStreamMgmt Server + BitFlags avsmFeatures; + BitFlags avsmOptionalAttrs; + avsmFeatures.Set(CameraAvStreamManagement::Feature::kSnapshot); + avsmFeatures.Set(CameraAvStreamManagement::Feature::kVideo); - // Fetch all initialization parameters for CameraAVStreamMgmt Server - BitFlags avsmFeatures; - BitFlags avsmOptionalAttrs; - avsmFeatures.Set(CameraAvStreamManagement::Feature::kSnapshot); - avsmFeatures.Set(CameraAvStreamManagement::Feature::kVideo); + // Enable the Watermark and OSD features if camera supports + if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsWatermark()) + { + avsmFeatures.Set(CameraAvStreamManagement::Feature::kWatermark); + } - // Enable the Watermark and OSD features if camera supports - if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsWatermark()) { - avsmFeatures.Set(CameraAvStreamManagement::Feature::kWatermark); - } + if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsOSD()) + { + avsmFeatures.Set(CameraAvStreamManagement::Feature::kOnScreenDisplay); + } - if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsOSD()) { - avsmFeatures.Set(CameraAvStreamManagement::Feature::kOnScreenDisplay); - } + if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsSoftPrivacy()) + { + avsmFeatures.Set(CameraAvStreamManagement::Feature::kPrivacy); + } - if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsSoftPrivacy()) { - avsmFeatures.Set(CameraAvStreamManagement::Feature::kPrivacy); - } + // Check microphone support to set Audio feature + if (mCameraDevice->GetCameraHALInterface().HasMicrophone()) + { + avsmFeatures.Set(CameraAvStreamManagement::Feature::kAudio); + avsmOptionalAttrs.Set(CameraAvStreamManagement::OptionalAttribute::kMicrophoneAGCEnabled); + } - // Check microphone support to set Audio feature - if (mCameraDevice->GetCameraHALInterface().HasMicrophone()) { - avsmFeatures.Set(CameraAvStreamManagement::Feature::kAudio); - avsmOptionalAttrs.Set( - CameraAvStreamManagement::OptionalAttribute::kMicrophoneAGCEnabled); - } + if (mCameraDevice->GetCameraHALInterface().HasLocalStorage()) + { + avsmFeatures.Set(CameraAvStreamManagement::Feature::kLocalStorage); + } - if (mCameraDevice->GetCameraHALInterface().HasLocalStorage()) { - avsmFeatures.Set(CameraAvStreamManagement::Feature::kLocalStorage); - } + // Check if camera has speaker + if (mCameraDevice->GetCameraHALInterface().HasSpeaker()) + { + avsmFeatures.Set(CameraAvStreamManagement::Feature::kSpeaker); + } - // Check if camera has speaker - if (mCameraDevice->GetCameraHALInterface().HasSpeaker()) { - avsmFeatures.Set(CameraAvStreamManagement::Feature::kSpeaker); - } + if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsHDR()) + { + avsmFeatures.Set(CameraAvStreamManagement::Feature::kHighDynamicRange); + } - if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsHDR()) { - avsmFeatures.Set(CameraAvStreamManagement::Feature::kHighDynamicRange); - } + if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsNightVision()) + { + avsmFeatures.Set(CameraAvStreamManagement::Feature::kNightVision); + avsmOptionalAttrs.Set(CameraAvStreamManagement::OptionalAttribute::kNightVisionIllum); + } - if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsNightVision()) { - avsmFeatures.Set(CameraAvStreamManagement::Feature::kNightVision); - avsmOptionalAttrs.Set( - CameraAvStreamManagement::OptionalAttribute::kNightVisionIllum); - } + if (mCameraDevice->GetCameraHALInterface().HasHardPrivacySwitch()) + { + avsmOptionalAttrs.Set(CameraAvStreamManagement::OptionalAttribute::kHardPrivacyModeOn); + } - if (mCameraDevice->GetCameraHALInterface().HasHardPrivacySwitch()) { - avsmOptionalAttrs.Set( - CameraAvStreamManagement::OptionalAttribute::kHardPrivacyModeOn); - } + if (mCameraDevice->GetCameraHALInterface().HasStatusLight()) + { + avsmOptionalAttrs.Set(CameraAvStreamManagement::OptionalAttribute::kStatusLightEnabled); + avsmOptionalAttrs.Set(CameraAvStreamManagement::OptionalAttribute::kStatusLightBrightness); + } - if (mCameraDevice->GetCameraHALInterface().HasStatusLight()) { - avsmOptionalAttrs.Set( - CameraAvStreamManagement::OptionalAttribute::kStatusLightEnabled); - avsmOptionalAttrs.Set( - CameraAvStreamManagement::OptionalAttribute::kStatusLightBrightness); - } + if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsImageControl()) + { + avsmFeatures.Set(CameraAvStreamManagement::Feature::kImageControl); + avsmOptionalAttrs.Set(CameraAvStreamManagement::OptionalAttribute::kImageFlipVertical); + avsmOptionalAttrs.Set(CameraAvStreamManagement::OptionalAttribute::kImageFlipHorizontal); + avsmOptionalAttrs.Set(CameraAvStreamManagement::OptionalAttribute::kImageRotation); + } - if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsImageControl()) { - avsmFeatures.Set(CameraAvStreamManagement::Feature::kImageControl); - avsmOptionalAttrs.Set( - CameraAvStreamManagement::OptionalAttribute::kImageFlipVertical); - avsmOptionalAttrs.Set( - CameraAvStreamManagement::OptionalAttribute::kImageFlipHorizontal); - avsmOptionalAttrs.Set( - CameraAvStreamManagement::OptionalAttribute::kImageRotation); - } + uint32_t maxConcurrentVideoEncoders = mCameraDevice->GetCameraHALInterface().GetMaxConcurrentEncoders(); + uint32_t maxEncodedPixelRate = mCameraDevice->GetCameraHALInterface().GetMaxEncodedPixelRate(); + VideoSensorParamsStruct sensorParams = mCameraDevice->GetCameraHALInterface().GetVideoSensorParams(); + bool nightVisionUsesInfrared = mCameraDevice->GetCameraHALInterface().GetNightVisionUsesInfrared(); + VideoResolutionStruct minViewport = mCameraDevice->GetCameraHALInterface().GetMinViewport(); + std::vector rateDistortionTradeOffPoints = + mCameraDevice->GetCameraHALInterface().GetRateDistortionTradeOffPoints(); - uint32_t maxConcurrentVideoEncoders = - mCameraDevice->GetCameraHALInterface().GetMaxConcurrentEncoders(); - uint32_t maxEncodedPixelRate = - mCameraDevice->GetCameraHALInterface().GetMaxEncodedPixelRate(); - VideoSensorParamsStruct sensorParams = - mCameraDevice->GetCameraHALInterface().GetVideoSensorParams(); - bool nightVisionUsesInfrared = - mCameraDevice->GetCameraHALInterface().GetNightVisionUsesInfrared(); - VideoResolutionStruct minViewport = - mCameraDevice->GetCameraHALInterface().GetMinViewport(); - std::vector rateDistortionTradeOffPoints = - mCameraDevice->GetCameraHALInterface().GetRateDistortionTradeOffPoints(); + uint32_t maxContentBufferSize = mCameraDevice->GetCameraHALInterface().GetMaxContentBufferSize(); + AudioCapabilitiesStruct micCapabilities = mCameraDevice->GetCameraHALInterface().GetMicrophoneCapabilities(); + AudioCapabilitiesStruct spkrCapabilities = mCameraDevice->GetCameraHALInterface().GetSpeakerCapabilities(); + TwoWayTalkSupportTypeEnum twowayTalkSupport = + mCameraDevice->GetCameraHALInterface().HasMicrophone() && mCameraDevice->GetCameraHALInterface().HasSpeaker() + ? TwoWayTalkSupportTypeEnum::kFullDuplex + : TwoWayTalkSupportTypeEnum::kNotSupported; + std::vector snapshotCapabilities = mCameraDevice->GetCameraHALInterface().GetSnapshotCapabilities(); + uint32_t maxNetworkBandwidth = mCameraDevice->GetCameraHALInterface().GetMaxNetworkBandwidth() * kBitsPerMegabit; + std::vector supportedStreamUsages = mCameraDevice->GetCameraHALInterface().GetSupportedStreamUsages(); + std::vector streamUsagePriorities = mCameraDevice->GetCameraHALInterface().GetStreamUsagePriorities(); - uint32_t maxContentBufferSize = - mCameraDevice->GetCameraHALInterface().GetMaxContentBufferSize(); - AudioCapabilitiesStruct micCapabilities = - mCameraDevice->GetCameraHALInterface().GetMicrophoneCapabilities(); - AudioCapabilitiesStruct spkrCapabilities = - mCameraDevice->GetCameraHALInterface().GetSpeakerCapabilities(); - TwoWayTalkSupportTypeEnum twowayTalkSupport = - mCameraDevice->GetCameraHALInterface().HasMicrophone() && - mCameraDevice->GetCameraHALInterface().HasSpeaker() - ? TwoWayTalkSupportTypeEnum::kFullDuplex - : TwoWayTalkSupportTypeEnum::kNotSupported; - std::vector snapshotCapabilities = - mCameraDevice->GetCameraHALInterface().GetSnapshotCapabilities(); - uint32_t maxNetworkBandwidth = - mCameraDevice->GetCameraHALInterface().GetMaxNetworkBandwidth() * - kBitsPerMegabit; - std::vector supportedStreamUsages = - mCameraDevice->GetCameraHALInterface().GetSupportedStreamUsages(); - std::vector streamUsagePriorities = - mCameraDevice->GetCameraHALInterface().GetStreamUsagePriorities(); - - // Instantiate the CameraAVStreamMgmt Server - mAVStreamMgmtServerPtr = std::make_unique( - mCameraDevice->GetCameraAVStreamMgmtDelegate(), mEndpoint, avsmFeatures, - avsmOptionalAttrs, maxConcurrentVideoEncoders, maxEncodedPixelRate, - sensorParams, nightVisionUsesInfrared, minViewport, - rateDistortionTradeOffPoints, maxContentBufferSize, micCapabilities, - spkrCapabilities, twowayTalkSupport, snapshotCapabilities, - maxNetworkBandwidth, supportedStreamUsages, streamUsagePriorities); + // Instantiate the CameraAVStreamMgmt Server + mAVStreamMgmtServerPtr = std::make_unique( + mCameraDevice->GetCameraAVStreamMgmtDelegate(), mEndpoint, avsmFeatures, avsmOptionalAttrs, maxConcurrentVideoEncoders, + maxEncodedPixelRate, sensorParams, nightVisionUsesInfrared, minViewport, rateDistortionTradeOffPoints, maxContentBufferSize, + micCapabilities, spkrCapabilities, twowayTalkSupport, snapshotCapabilities, maxNetworkBandwidth, supportedStreamUsages, + streamUsagePriorities); } -void CameraApp::InitializeCameraAVStreamMgmt() { - // Set the attribute defaults - if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsHDR()) { - mAVStreamMgmtServerPtr->SetHDRModeEnabled( - mCameraDevice->GetCameraHALInterface().GetHDRMode()); - } +void CameraApp::InitializeCameraAVStreamMgmt() +{ + // Set the attribute defaults + if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsHDR()) + { + mAVStreamMgmtServerPtr->SetHDRModeEnabled(mCameraDevice->GetCameraHALInterface().GetHDRMode()); + } - if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsSoftPrivacy()) { - mAVStreamMgmtServerPtr->SetSoftRecordingPrivacyModeEnabled( - mCameraDevice->GetCameraHALInterface() - .GetSoftRecordingPrivacyModeEnabled()); - mAVStreamMgmtServerPtr->SetSoftLivestreamPrivacyModeEnabled( - mCameraDevice->GetCameraHALInterface() - .GetSoftLivestreamPrivacyModeEnabled()); - } + if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsSoftPrivacy()) + { + mAVStreamMgmtServerPtr->SetSoftRecordingPrivacyModeEnabled( + mCameraDevice->GetCameraHALInterface().GetSoftRecordingPrivacyModeEnabled()); + mAVStreamMgmtServerPtr->SetSoftLivestreamPrivacyModeEnabled( + mCameraDevice->GetCameraHALInterface().GetSoftLivestreamPrivacyModeEnabled()); + } - if (mCameraDevice->GetCameraHALInterface().HasHardPrivacySwitch()) { - mAVStreamMgmtServerPtr->SetHardPrivacyModeOn( - mCameraDevice->GetCameraHALInterface().GetHardPrivacyMode()); - } + if (mCameraDevice->GetCameraHALInterface().HasHardPrivacySwitch()) + { + mAVStreamMgmtServerPtr->SetHardPrivacyModeOn(mCameraDevice->GetCameraHALInterface().GetHardPrivacyMode()); + } - if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsNightVision()) { - mAVStreamMgmtServerPtr->SetNightVision( - mCameraDevice->GetCameraHALInterface().GetNightVision()); - } + if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsNightVision()) + { + mAVStreamMgmtServerPtr->SetNightVision(mCameraDevice->GetCameraHALInterface().GetNightVision()); + } - mAVStreamMgmtServerPtr->SetViewport( - mCameraDevice->GetCameraHALInterface().GetViewport()); + mAVStreamMgmtServerPtr->SetViewport(mCameraDevice->GetCameraHALInterface().GetViewport()); - if (mCameraDevice->GetCameraHALInterface().HasSpeaker()) { - mAVStreamMgmtServerPtr->SetSpeakerMuted( - mCameraDevice->GetCameraHALInterface().GetSpeakerMuted()); - mAVStreamMgmtServerPtr->SetSpeakerVolumeLevel( - mCameraDevice->GetCameraHALInterface().GetSpeakerVolume()); - mAVStreamMgmtServerPtr->SetSpeakerMaxLevel( - mCameraDevice->GetCameraHALInterface().GetSpeakerMaxLevel()); - mAVStreamMgmtServerPtr->SetSpeakerMinLevel( - mCameraDevice->GetCameraHALInterface().GetSpeakerMinLevel()); - } + if (mCameraDevice->GetCameraHALInterface().HasSpeaker()) + { + mAVStreamMgmtServerPtr->SetSpeakerMuted(mCameraDevice->GetCameraHALInterface().GetSpeakerMuted()); + mAVStreamMgmtServerPtr->SetSpeakerVolumeLevel(mCameraDevice->GetCameraHALInterface().GetSpeakerVolume()); + mAVStreamMgmtServerPtr->SetSpeakerMaxLevel(mCameraDevice->GetCameraHALInterface().GetSpeakerMaxLevel()); + mAVStreamMgmtServerPtr->SetSpeakerMinLevel(mCameraDevice->GetCameraHALInterface().GetSpeakerMinLevel()); + } - if (mCameraDevice->GetCameraHALInterface().HasMicrophone()) { - mAVStreamMgmtServerPtr->SetMicrophoneMuted( - mCameraDevice->GetCameraHALInterface().GetMicrophoneMuted()); - mAVStreamMgmtServerPtr->SetMicrophoneVolumeLevel( - mCameraDevice->GetCameraHALInterface().GetMicrophoneVolume()); - mAVStreamMgmtServerPtr->SetMicrophoneMaxLevel( - mCameraDevice->GetCameraHALInterface().GetMicrophoneMaxLevel()); - mAVStreamMgmtServerPtr->SetMicrophoneMinLevel( - mCameraDevice->GetCameraHALInterface().GetMicrophoneMinLevel()); - } + if (mCameraDevice->GetCameraHALInterface().HasMicrophone()) + { + mAVStreamMgmtServerPtr->SetMicrophoneMuted(mCameraDevice->GetCameraHALInterface().GetMicrophoneMuted()); + mAVStreamMgmtServerPtr->SetMicrophoneVolumeLevel(mCameraDevice->GetCameraHALInterface().GetMicrophoneVolume()); + mAVStreamMgmtServerPtr->SetMicrophoneMaxLevel(mCameraDevice->GetCameraHALInterface().GetMicrophoneMaxLevel()); + mAVStreamMgmtServerPtr->SetMicrophoneMinLevel(mCameraDevice->GetCameraHALInterface().GetMicrophoneMinLevel()); + } - // Video and Snapshot features are already enabled. - if (mCameraDevice->GetCameraHALInterface().HasLocalStorage()) { - mAVStreamMgmtServerPtr->SetLocalVideoRecordingEnabled( - mCameraDevice->GetCameraHALInterface().GetLocalVideoRecordingEnabled()); - mAVStreamMgmtServerPtr->SetLocalSnapshotRecordingEnabled( - mCameraDevice->GetCameraHALInterface() - .GetLocalSnapshotRecordingEnabled()); - } + // Video and Snapshot features are already enabled. + if (mCameraDevice->GetCameraHALInterface().HasLocalStorage()) + { + mAVStreamMgmtServerPtr->SetLocalVideoRecordingEnabled( + mCameraDevice->GetCameraHALInterface().GetLocalVideoRecordingEnabled()); + mAVStreamMgmtServerPtr->SetLocalSnapshotRecordingEnabled( + mCameraDevice->GetCameraHALInterface().GetLocalSnapshotRecordingEnabled()); + } - if (mCameraDevice->GetCameraHALInterface().HasStatusLight()) { - mAVStreamMgmtServerPtr->SetStatusLightEnabled( - mCameraDevice->GetCameraHALInterface().GetStatusLightEnabled()); - } + if (mCameraDevice->GetCameraHALInterface().HasStatusLight()) + { + mAVStreamMgmtServerPtr->SetStatusLightEnabled(mCameraDevice->GetCameraHALInterface().GetStatusLightEnabled()); + } - if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsImageControl()) { - mAVStreamMgmtServerPtr->SetImageRotation( - mCameraDevice->GetCameraHALInterface().GetImageRotation()); - mAVStreamMgmtServerPtr->SetImageFlipVertical( - mCameraDevice->GetCameraHALInterface().GetImageFlipVertical()); - mAVStreamMgmtServerPtr->SetImageFlipHorizontal( - mCameraDevice->GetCameraHALInterface().GetImageFlipHorizontal()); - } + if (mCameraDevice->GetCameraHALInterface().GetCameraSupportsImageControl()) + { + mAVStreamMgmtServerPtr->SetImageRotation(mCameraDevice->GetCameraHALInterface().GetImageRotation()); + mAVStreamMgmtServerPtr->SetImageFlipVertical(mCameraDevice->GetCameraHALInterface().GetImageFlipVertical()); + mAVStreamMgmtServerPtr->SetImageFlipHorizontal(mCameraDevice->GetCameraHALInterface().GetImageFlipHorizontal()); + } - mAVStreamMgmtServerPtr->Init(); + mAVStreamMgmtServerPtr->Init(); } -void CameraApp::InitCameraDeviceClusters() { - // Initialize Cluster Servers - mWebRTCTransportProviderPtr->Init(); - mCameraDevice->GetWebRTCProviderController().SetWebRTCTransportProvider( - std::move(mWebRTCTransportProviderPtr)); - InitializeCameraAVStreamMgmt(); +void CameraApp::InitCameraDeviceClusters() +{ + // Initialize Cluster Servers + mWebRTCTransportProviderServer.Create(mEndpoint, mCameraDevice->GetWebRTCProviderDelegate()); + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Register(mWebRTCTransportProviderServer.Registration()); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Camera, + "Failed to register WebRTCTransportProvider on endpoint %u: " + "%" CHIP_ERROR_FORMAT, + mEndpoint, err.Format()); + } + InitializeCameraAVStreamMgmt(); } -void CameraApp::ShutdownCameraDeviceClusters() { - ChipLogDetail(Camera, - "CameraAppShutdown: Shutting down Camera device clusters"); - mWebRTCTransportProviderPtr->Shutdown(); +void CameraApp::ShutdownCameraDeviceClusters() +{ + ChipLogDetail(Camera, "CameraAppShutdown: Shutting down Camera device clusters"); + + CHIP_ERROR err = esp_matter::data_model::provider::get_instance().registry().Unregister(&mWebRTCTransportProviderServer.Cluster()); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Camera, "WebRTCTransportProvider unregister error: %" CHIP_ERROR_FORMAT, err.Format()); + } + mWebRTCTransportProviderServer.Destroy(); } static constexpr EndpointId kCameraEndpointId = 1; std::unique_ptr gCameraApp; -void CameraAppInit(CameraDeviceInterface *cameraDevice) { +void CameraAppInit(CameraDeviceInterface * cameraDevice) +{ - gCameraApp = std::make_unique(kCameraEndpointId, cameraDevice); + gCameraApp = std::make_unique(kCameraEndpointId, cameraDevice); - gCameraApp.get()->InitCameraDeviceClusters(); + gCameraApp.get()->InitCameraDeviceClusters(); - ChipLogDetail(Camera, "CameraAppInit: Initialized Camera clusters"); + ChipLogDetail(Camera, "CameraAppInit: Initialized Camera clusters"); } -void CameraAppShutdown() { - ChipLogDetail(Camera, "CameraAppShutdown: Shutting down Camera app"); - gCameraApp.get()->ShutdownCameraDeviceClusters(); - gCameraApp = nullptr; +void CameraAppShutdown() +{ + ChipLogDetail(Camera, "CameraAppShutdown: Shutting down Camera app"); + gCameraApp.get()->ShutdownCameraDeviceClusters(); + gCameraApp = nullptr; } diff --git a/examples/camera/main/common/camera-app.h b/examples/camera/main/common/camera-app.h index c3b5b0aa9..37136cb0f 100644 --- a/examples/camera/main/common/camera-app.h +++ b/examples/camera/main/common/camera-app.h @@ -1,3 +1,20 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * All rights reserved. + * + * 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. + */ #pragma once #include @@ -5,43 +22,38 @@ #include "camera-device-interface.h" #include #include +#include #include #include // Camera App defines all the cluster servers needed for a particular device -class CameraApp { +class CameraApp +{ public: - // This class is responsible for initialising all the camera clusters and - // managing the interactions between them - CameraApp(chip::EndpointId aClustersEndpoint, - CameraDeviceInterface *cameraDevice); + // This class is responsible for initialising all the camera clusters and + // managing the interactions between them + CameraApp(chip::EndpointId aClustersEndpoint, CameraDeviceInterface * cameraDevice); - // Initialize all the camera device clusters. - void InitCameraDeviceClusters(); + // Initialize all the camera device clusters. + void InitCameraDeviceClusters(); - // Shutdown all the camera device clusters - void ShutdownCameraDeviceClusters(); + // Shutdown all the camera device clusters + void ShutdownCameraDeviceClusters(); private: - chip::EndpointId mEndpoint; - CameraDeviceInterface *mCameraDevice; + chip::EndpointId mEndpoint; + CameraDeviceInterface * mCameraDevice; - // SDK cluster servers - std::unique_ptr - mWebRTCTransportProviderPtr; - // std::unique_ptr mChimeServerPtr; - std::unique_ptr< - chip::app::Clusters::CameraAvStreamManagement::CameraAVStreamMgmtServer> - mAVStreamMgmtServerPtr; - // std::unique_ptr - // mAVSettingsUserLevelMgmtServerPtr; + // SDK cluster servers + chip::app::LazyRegisteredServerCluster + mWebRTCTransportProviderServer; + std::unique_ptr mAVStreamMgmtServerPtr; - // Helper to set attribute defaults for CameraAVStreamMgmt - void InitializeCameraAVStreamMgmt(); + // Helper to set attribute defaults for CameraAVStreamMgmt + void InitializeCameraAVStreamMgmt(); }; -void CameraAppInit(CameraDeviceInterface *cameraDevice); +void CameraAppInit(CameraDeviceInterface * cameraDevice); void CameraAppShutdown(); diff --git a/examples/camera/main/common/camera-avstream-controller.h b/examples/camera/main/common/camera-avstream-controller.h index f0def90c1..f4c90e846 100644 --- a/examples/camera/main/common/camera-avstream-controller.h +++ b/examples/camera/main/common/camera-avstream-controller.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace chip { namespace app { diff --git a/examples/camera/main/common/camera-device-interface.h b/examples/camera/main/common/camera-device-interface.h index b2ae3c097..a08ddfec6 100644 --- a/examples/camera/main/common/camera-device-interface.h +++ b/examples/camera/main/common/camera-device-interface.h @@ -1,18 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + #pragma once #include "camera-avstream-controller.h" -// #include -#include "webrtc-provider-controller.h" -// #include -// -#include -// #include -#include +#include +#include using chip::app::Clusters::CameraAvStreamManagement::AudioCapabilitiesStruct; using chip::app::Clusters::CameraAvStreamManagement::AudioStreamStruct; using chip::app::Clusters::CameraAvStreamManagement::ImageSnapshot; -using chip::app::Clusters::CameraAvStreamManagement:: - RateDistortionTradeOffStruct; +using chip::app::Clusters::CameraAvStreamManagement::RateDistortionTradeOffStruct; using chip::app::Clusters::CameraAvStreamManagement::SnapshotCapabilitiesStruct; using chip::app::Clusters::CameraAvStreamManagement::SnapshotStreamStruct; using chip::app::Clusters::CameraAvStreamManagement::TriStateAutoEnum; @@ -21,312 +21,290 @@ using chip::app::Clusters::CameraAvStreamManagement::VideoSensorParamsStruct; using chip::app::Clusters::CameraAvStreamManagement::VideoStreamStruct; using chip::app::Clusters::Globals::StreamUsageEnum; -struct VideoStream { - VideoStreamStruct videoStreamParams; - bool isAllocated; // Flag to indicate if the stream is allocated. - chip::app::Clusters::Globals::Structs::ViewportStruct::Type - viewport; // Stream specific viewport, defaults to the camera viewport - void *videoContext; // Platform-specific context object associated with - // video stream; +struct VideoStream +{ + VideoStreamStruct videoStreamParams; + bool isAllocated; // Flag to indicate if the stream is allocated. + chip::app::Clusters::Globals::Structs::ViewportStruct::Type + viewport; // Stream specific viewport, defaults to the camera viewport + void * videoContext; // Platform-specific context object associated with + // video stream; - bool IsCompatible(const VideoStreamStruct &inputParams) const { - return (videoStreamParams.videoCodec == inputParams.videoCodec && - videoStreamParams.minFrameRate <= inputParams.minFrameRate && - videoStreamParams.maxFrameRate >= inputParams.maxFrameRate && - videoStreamParams.minResolution.width <= - inputParams.minResolution.width && - videoStreamParams.minResolution.height <= - inputParams.minResolution.height && - videoStreamParams.maxResolution.width >= - inputParams.maxResolution.width && - videoStreamParams.maxResolution.height >= - inputParams.maxResolution.height && - videoStreamParams.minBitRate <= inputParams.minBitRate && - videoStreamParams.maxBitRate >= inputParams.maxBitRate && - videoStreamParams.keyFrameInterval == inputParams.keyFrameInterval); - } + bool IsCompatible(const VideoStreamStruct & inputParams) const + { + return (videoStreamParams.videoCodec == inputParams.videoCodec && + videoStreamParams.minFrameRate >= inputParams.minFrameRate && + videoStreamParams.maxFrameRate <= inputParams.maxFrameRate && + videoStreamParams.minResolution.width <= inputParams.minResolution.width && + videoStreamParams.minResolution.height <= inputParams.minResolution.height && + videoStreamParams.maxResolution.width >= inputParams.maxResolution.width && + videoStreamParams.maxResolution.height >= inputParams.maxResolution.height && + videoStreamParams.minBitRate <= inputParams.minBitRate && videoStreamParams.maxBitRate >= inputParams.maxBitRate && + videoStreamParams.keyFrameInterval == inputParams.keyFrameInterval); + } }; -struct AudioStream { - AudioStreamStruct audioStreamParams; - bool isAllocated; // Flag to indicate if the stream is allocated. - void *audioContext; // Platform-specific context object associated with - // video stream; +struct AudioStream +{ + AudioStreamStruct audioStreamParams; + bool isAllocated; // Flag to indicate if the stream is allocated. + void * audioContext; // Platform-specific context object associated with + // video stream; - bool IsCompatible(const AudioStreamStruct &inputParams) const { - return (audioStreamParams.audioCodec == inputParams.audioCodec && - audioStreamParams.channelCount == inputParams.channelCount && - audioStreamParams.sampleRate == inputParams.sampleRate && - audioStreamParams.bitDepth == inputParams.bitDepth); - } + bool IsCompatible(const AudioStreamStruct & inputParams) const + { + return (audioStreamParams.audioCodec == inputParams.audioCodec && + audioStreamParams.channelCount == inputParams.channelCount && + audioStreamParams.sampleRate == inputParams.sampleRate && audioStreamParams.bitDepth == inputParams.bitDepth); + } }; -struct SnapshotStream { - SnapshotStreamStruct snapshotStreamParams; - bool isAllocated; // Flag to indicate if the stream is allocated - void *snapshotContext; // Platform-specific context object associated with - // snapshot stream; +struct SnapshotStream +{ + SnapshotStreamStruct snapshotStreamParams; + bool isAllocated; // Flag to indicate if the stream is allocated + void * snapshotContext; // Platform-specific context object associated with + // snapshot stream; - bool IsCompatible(const chip::app::Clusters::CameraAvStreamManagement:: - CameraAVStreamMgmtDelegate::SnapshotStreamAllocateArgs - &inputParams) const { - return (snapshotStreamParams.imageCodec == inputParams.imageCodec && - snapshotStreamParams.quality == inputParams.quality && - snapshotStreamParams.frameRate <= inputParams.maxFrameRate && - snapshotStreamParams.minResolution.width <= - inputParams.minResolution.width && - snapshotStreamParams.minResolution.height <= - inputParams.minResolution.height && - snapshotStreamParams.maxResolution.width >= - inputParams.maxResolution.width && - snapshotStreamParams.maxResolution.height >= - inputParams.maxResolution.height); - } + bool + IsCompatible(const chip::app::Clusters::CameraAvStreamManagement::CameraAVStreamManagementDelegate::SnapshotStreamAllocateArgs & + inputParams) const + { + return (snapshotStreamParams.imageCodec == inputParams.imageCodec && snapshotStreamParams.quality == inputParams.quality && + snapshotStreamParams.frameRate <= inputParams.maxFrameRate && + snapshotStreamParams.minResolution.width <= inputParams.minResolution.width && + snapshotStreamParams.minResolution.height <= inputParams.minResolution.height && + snapshotStreamParams.maxResolution.width >= inputParams.maxResolution.width && + snapshotStreamParams.maxResolution.height >= inputParams.maxResolution.height); + } }; // Enumeration for common camera errors -enum class CameraError { - SUCCESS, - ERROR_INIT_FAILED, - ERROR_VIDEO_STREAM_START_FAILED, - ERROR_VIDEO_STREAM_STOP_FAILED, - ERROR_AUDIO_STREAM_START_FAILED, - ERROR_AUDIO_STREAM_STOP_FAILED, - ERROR_SNAPSHOT_STREAM_START_FAILED, - ERROR_SNAPSHOT_STREAM_STOP_FAILED, - ERROR_CAPTURE_SNAPSHOT_FAILED, - ERROR_CONFIG_FAILED, - ERROR_RESOURCE_EXHAUSTED, - ERROR_NOT_IMPLEMENTED, // For features not supported on a platform +enum class CameraError +{ + SUCCESS, + ERROR_INIT_FAILED, + ERROR_VIDEO_STREAM_START_FAILED, + ERROR_VIDEO_STREAM_STOP_FAILED, + ERROR_AUDIO_STREAM_START_FAILED, + ERROR_AUDIO_STREAM_STOP_FAILED, + ERROR_SNAPSHOT_STREAM_START_FAILED, + ERROR_SNAPSHOT_STREAM_STOP_FAILED, + ERROR_CAPTURE_SNAPSHOT_FAILED, + ERROR_CONFIG_FAILED, + ERROR_RESOURCE_EXHAUSTED, + ERROR_NOT_IMPLEMENTED, // For features not supported on a platform }; // Camera Device Interface defines all the clusters that need to be implemented // for a Camera Device -class CameraDeviceInterface { +class CameraDeviceInterface +{ public: - virtual ~CameraDeviceInterface() = default; + virtual ~CameraDeviceInterface() = default; - // Getter for WebRTCProvider Delegate - virtual chip::app::Clusters::WebRTCTransportProvider::Delegate & - GetWebRTCProviderDelegate() = 0; + // Getter for WebRTCProvider Delegate + virtual chip::app::Clusters::WebRTCTransportProvider::Delegate & GetWebRTCProviderDelegate() = 0; - // Getter for WebRTCProvider Controller - virtual chip::app::Clusters::WebRTCTransportProvider:: - WebRTCTransportProviderController & - GetWebRTCProviderController() = 0; + // Set the WebRTC Transport Provider server instance + virtual void + SetWebRTCTransportProvider(chip::app::Clusters::WebRTCTransportProvider::WebRTCTransportProviderCluster * provider) = 0; - // Getter for CameraAVStreamManagement Delegate - virtual chip::app::Clusters::CameraAvStreamManagement:: - CameraAVStreamMgmtDelegate & - GetCameraAVStreamMgmtDelegate() = 0; + // Getter for CameraAVStreamManagement Delegate + virtual chip::app::Clusters::CameraAvStreamManagement::CameraAVStreamManagementDelegate & GetCameraAVStreamMgmtDelegate() = 0; - // Getter for CameraAVStreamManagement Controller - virtual chip::app::Clusters::CameraAvStreamManagement:: - CameraAVStreamController & - GetCameraAVStreamMgmtController() = 0; + // Getter for CameraAVStreamManagement Controller + virtual chip::app::Clusters::CameraAvStreamManagement::CameraAVStreamController & GetCameraAVStreamMgmtController() = 0; - // Class defining the Camera HAL interface - class CameraHALInterface { - public: - // Virtual destructor - virtual ~CameraHALInterface() = default; + // Class defining the Camera HAL interface + class CameraHALInterface + { + public: + // Virtual destructor + virtual ~CameraHALInterface() = default; - // Initialize the camera hardware - virtual CameraError InitializeCameraDevice() = 0; + // Initialize the camera hardware + virtual CameraError InitializeCameraDevice() = 0; - virtual CameraError InitializeStreams() = 0; + virtual CameraError InitializeStreams() = 0; - virtual std::vector &GetAvailableVideoStreams() = 0; + virtual std::vector & GetAvailableVideoStreams() = 0; - virtual std::vector &GetAvailableAudioStreams() = 0; + virtual std::vector & GetAvailableAudioStreams() = 0; - virtual std::vector &GetAvailableSnapshotStreams() = 0; + virtual std::vector & GetAvailableSnapshotStreams() = 0; - // Capture a snapshot image - virtual CameraError - CaptureSnapshot(const chip::app::DataModel::Nullable streamID, - const VideoResolutionStruct &resolution, - ImageSnapshot &outImageSnapshot) = 0; + // Capture a snapshot image + virtual CameraError CaptureSnapshot(const chip::app::DataModel::Nullable streamID, + const VideoResolutionStruct & resolution, ImageSnapshot & outImageSnapshot) = 0; - // Allocate snapshot stream - virtual CameraError AllocateSnapshotStream( - const chip::app::Clusters::CameraAvStreamManagement:: - CameraAVStreamMgmtDelegate::SnapshotStreamAllocateArgs &args, - uint16_t &outStreamID) = 0; + // Allocate snapshot stream + virtual CameraError AllocateSnapshotStream( + const chip::app::Clusters::CameraAvStreamManagement::CameraAVStreamManagementDelegate::SnapshotStreamAllocateArgs & + args, + uint16_t & outStreamID) = 0; - // Get the maximum number of concurrent encoders supported by camera. - virtual uint8_t GetMaxConcurrentEncoders() = 0; + // Get the maximum number of concurrent encoders supported by camera. + virtual uint8_t GetMaxConcurrentEncoders() = 0; - // Get the maximum data rate in encoded pixels per second that the - // camera can produce given the hardware encoders it has. - virtual uint32_t GetMaxEncodedPixelRate() = 0; + // Get the maximum data rate in encoded pixels per second that the + // camera can produce given the hardware encoders it has. + virtual uint32_t GetMaxEncodedPixelRate() = 0; - // Get the Video sensor params(sensor dimensions, framerate, HDR - // capabilities) - virtual VideoSensorParamsStruct &GetVideoSensorParams() = 0; + // Get the Video sensor params(sensor dimensions, framerate, HDR + // capabilities) + virtual VideoSensorParamsStruct & GetVideoSensorParams() = 0; - // Get indication whether camera supports high dynamic range for video - virtual bool GetCameraSupportsHDR() = 0; + // Get indication whether camera supports high dynamic range for video + virtual bool GetCameraSupportsHDR() = 0; - // Get indication whether camera supports night vision - virtual bool GetCameraSupportsNightVision() = 0; + // Get indication whether camera supports night vision + virtual bool GetCameraSupportsNightVision() = 0; - // Get indication whether camera night vision using infrared - virtual bool GetNightVisionUsesInfrared() = 0; + // Get indication whether camera night vision using infrared + virtual bool GetNightVisionUsesInfrared() = 0; - // Get indication whether camera supports image control - virtual bool GetCameraSupportsImageControl() = 0; + // Get indication whether camera supports image control + virtual bool GetCameraSupportsImageControl() = 0; - // Get indication whether camera supports watermark for video and snapshot - virtual bool GetCameraSupportsWatermark() = 0; + // Get indication whether camera supports watermark for video and snapshot + virtual bool GetCameraSupportsWatermark() = 0; - // Get indication whether camera supports on-screen display for video and - // snapshot - virtual bool GetCameraSupportsOSD() = 0; + // Get indication whether camera supports on-screen display for video and + // snapshot + virtual bool GetCameraSupportsOSD() = 0; - // Get indication whether camera supports soft recording and livestream - // privacy modes - virtual bool GetCameraSupportsSoftPrivacy() = 0; + // Get indication whether camera supports soft recording and livestream + // privacy modes + virtual bool GetCameraSupportsSoftPrivacy() = 0; - // Get indication of the min resolution(pixels) that camera allows for - // its viewport. - virtual VideoResolutionStruct &GetMinViewport() = 0; + // Get indication of the min resolution(pixels) that camera allows for + // its viewport. + virtual VideoResolutionStruct & GetMinViewport() = 0; - // Get the rate distortion tradeoff points(min bitrate for resolutions) for - // video codecs. - virtual std::vector & - GetRateDistortionTradeOffPoints() = 0; + // Get the rate distortion tradeoff points(min bitrate for resolutions) for + // video codecs. + virtual std::vector & GetRateDistortionTradeOffPoints() = 0; - // Get the maximum size of content buffer in bytes. This buffer holds - // compressed and/or raw audio/video content. - virtual uint32_t GetMaxContentBufferSize() = 0; + // Get the maximum size of content buffer in bytes. This buffer holds + // compressed and/or raw audio/video content. + virtual uint32_t GetMaxContentBufferSize() = 0; - // Get microphone capabilities. - virtual AudioCapabilitiesStruct &GetMicrophoneCapabilities() = 0; + // Get microphone capabilities. + virtual AudioCapabilitiesStruct & GetMicrophoneCapabilities() = 0; - // Get speaker capabilities. - virtual AudioCapabilitiesStruct &GetSpeakerCapabilities() = 0; + // Get speaker capabilities. + virtual AudioCapabilitiesStruct & GetSpeakerCapabilities() = 0; - // Get snapshot capabilities - virtual std::vector & - GetSnapshotCapabilities() = 0; + // Get snapshot capabilities + virtual std::vector & GetSnapshotCapabilities() = 0; - // Get the maximum network bandwidth(mbps) that the camera would consume - // for transmission of its media streams. - virtual uint32_t GetMaxNetworkBandwidth() = 0; + // Get the maximum network bandwidth(mbps) that the camera would consume + // for transmission of its media streams. + virtual uint32_t GetMaxNetworkBandwidth() = 0; - // Get the current frame rate of the camera sensor. - virtual uint16_t GetCurrentFrameRate() = 0; + // Get the current frame rate of the camera sensor. + virtual uint16_t GetCurrentFrameRate() = 0; - // Enable/Disable High Dynamic Range mode. - virtual CameraError SetHDRMode(bool hdrMode) = 0; + // Enable/Disable High Dynamic Range mode. + virtual CameraError SetHDRMode(bool hdrMode) = 0; - // Get the current camera HDR mode. - virtual bool GetHDRMode() = 0; + // Get the current camera HDR mode. + virtual bool GetHDRMode() = 0; - // Get Supported Stream usages; Typically set by manudacturer. - // This also sets the default priority of the stream usages. - virtual std::vector &GetSupportedStreamUsages() = 0; + // Get Supported Stream usages; Typically set by manudacturer. + // This also sets the default priority of the stream usages. + virtual std::vector & GetSupportedStreamUsages() = 0; - // Get stream usage priorities as an ordered list. This is expected to - // be a subset of the SupportedStreamUsages. - virtual std::vector &GetStreamUsagePriorities() = 0; - virtual CameraError SetStreamUsagePriorities( - std::vector streamUsagePriorities) = 0; + // Get stream usage priorities as an ordered list. This is expected to + // be a subset of the SupportedStreamUsages. + virtual std::vector & GetStreamUsagePriorities() = 0; + virtual CameraError SetStreamUsagePriorities(std::vector streamUsagePriorities) = 0; - // Get/Set soft recording privacy mode - virtual CameraError - SetSoftRecordingPrivacyModeEnabled(bool softRecordingPrivacyMode) = 0; - virtual bool GetSoftRecordingPrivacyModeEnabled() = 0; + // Get/Set soft recording privacy mode + virtual CameraError SetSoftRecordingPrivacyModeEnabled(bool softRecordingPrivacyMode) = 0; + virtual bool GetSoftRecordingPrivacyModeEnabled() = 0; - // Get/Set soft livestream privacy mode - virtual CameraError - SetSoftLivestreamPrivacyModeEnabled(bool softLivestreamPrivacyMode) = 0; - virtual bool GetSoftLivestreamPrivacyModeEnabled() = 0; + // Get/Set soft livestream privacy mode + virtual CameraError SetSoftLivestreamPrivacyModeEnabled(bool softLivestreamPrivacyMode) = 0; + virtual bool GetSoftLivestreamPrivacyModeEnabled() = 0; - // Does camera have a hard privacy switch - virtual bool HasHardPrivacySwitch() = 0; + // Does camera have a hard privacy switch + virtual bool HasHardPrivacySwitch() = 0; - // Get/Set hard privacy mode - virtual CameraError SetHardPrivacyMode(bool hardPrivacyMode) = 0; - virtual bool GetHardPrivacyMode() = 0; + // Get/Set hard privacy mode + virtual CameraError SetHardPrivacyMode(bool hardPrivacyMode) = 0; + virtual bool GetHardPrivacyMode() = 0; - // Get/Set night vision - virtual CameraError SetNightVision(TriStateAutoEnum nightVision) = 0; - virtual TriStateAutoEnum GetNightVision() = 0; + // Get/Set night vision + virtual CameraError SetNightVision(TriStateAutoEnum nightVision) = 0; + virtual TriStateAutoEnum GetNightVision() = 0; - // Set the viewport for all streams - virtual CameraError SetViewport( - const chip::app::Clusters::Globals::Structs::ViewportStruct::Type - &viewPort) = 0; + // Set the viewport for all streams + virtual CameraError SetViewport(const chip::app::Clusters::Globals::Structs::ViewportStruct::Type & viewPort) = 0; - // Get the current camera viewport. - virtual const chip::app::Clusters::Globals::Structs::ViewportStruct::Type & - GetViewport() = 0; + // Get the current camera viewport. + virtual const chip::app::Clusters::Globals::Structs::ViewportStruct::Type & GetViewport() = 0; - // Set the viewport for a specific stream - virtual CameraError SetViewport( - VideoStream &stream, - const chip::app::Clusters::Globals::Structs::ViewportStruct::Type - &viewPort) = 0; + // Set the viewport for a specific stream + virtual CameraError SetViewport(VideoStream & stream, + const chip::app::Clusters::Globals::Structs::ViewportStruct::Type & viewPort) = 0; - // Does camera have a speaker - virtual bool HasSpeaker() = 0; + // Does camera have a speaker + virtual bool HasSpeaker() = 0; - // Mute/Unmute speaker. - virtual bool GetSpeakerMuted() = 0; - virtual CameraError SetSpeakerMuted(bool muteSpeaker) = 0; + // Mute/Unmute speaker. + virtual bool GetSpeakerMuted() = 0; + virtual CameraError SetSpeakerMuted(bool muteSpeaker) = 0; - // Get/Set speaker volume level. - virtual uint8_t GetSpeakerVolume() = 0; - virtual CameraError SetSpeakerVolume(uint8_t speakerVol) = 0; + // Get/Set speaker volume level. + virtual uint8_t GetSpeakerVolume() = 0; + virtual CameraError SetSpeakerVolume(uint8_t speakerVol) = 0; - // Get the speaker max and min levels. - virtual uint8_t GetSpeakerMaxLevel() = 0; - virtual uint8_t GetSpeakerMinLevel() = 0; + // Get the speaker max and min levels. + virtual uint8_t GetSpeakerMaxLevel() = 0; + virtual uint8_t GetSpeakerMinLevel() = 0; - // Does camera have a microphone - virtual bool HasMicrophone() = 0; + // Does camera have a microphone + virtual bool HasMicrophone() = 0; - // Mute/Unmute microphone. - virtual CameraError SetMicrophoneMuted(bool muteMicrophone) = 0; - virtual bool GetMicrophoneMuted() = 0; + // Mute/Unmute microphone. + virtual CameraError SetMicrophoneMuted(bool muteMicrophone) = 0; + virtual bool GetMicrophoneMuted() = 0; - // Set microphone volume level. - virtual CameraError SetMicrophoneVolume(uint8_t microphoneVol) = 0; - virtual uint8_t GetMicrophoneVolume() = 0; + // Set microphone volume level. + virtual CameraError SetMicrophoneVolume(uint8_t microphoneVol) = 0; + virtual uint8_t GetMicrophoneVolume() = 0; - // Get the microphone max and min levels. - virtual uint8_t GetMicrophoneMaxLevel() = 0; - virtual uint8_t GetMicrophoneMinLevel() = 0; + // Get the microphone max and min levels. + virtual uint8_t GetMicrophoneMaxLevel() = 0; + virtual uint8_t GetMicrophoneMinLevel() = 0; - // Get/Set image control attributes - virtual CameraError SetImageRotation(uint16_t imageRotation) = 0; - virtual uint16_t GetImageRotation() = 0; + // Get/Set image control attributes + virtual CameraError SetImageRotation(uint16_t imageRotation) = 0; + virtual uint16_t GetImageRotation() = 0; - virtual CameraError SetImageFlipHorizontal(bool imageFlipHorizontal) = 0; - virtual bool GetImageFlipHorizontal() = 0; + virtual CameraError SetImageFlipHorizontal(bool imageFlipHorizontal) = 0; + virtual bool GetImageFlipHorizontal() = 0; - virtual CameraError SetImageFlipVertical(bool imageFlipVertical) = 0; - virtual bool GetImageFlipVertical() = 0; + virtual CameraError SetImageFlipVertical(bool imageFlipVertical) = 0; + virtual bool GetImageFlipVertical() = 0; - // Does camera have local storage - virtual bool HasLocalStorage() = 0; + // Does camera have local storage + virtual bool HasLocalStorage() = 0; - virtual CameraError - SetLocalVideoRecordingEnabled(bool localVideoRecordingEnabled) = 0; - virtual bool GetLocalVideoRecordingEnabled() = 0; + virtual CameraError SetLocalVideoRecordingEnabled(bool localVideoRecordingEnabled) = 0; + virtual bool GetLocalVideoRecordingEnabled() = 0; - virtual CameraError - SetLocalSnapshotRecordingEnabled(bool localSnapshotRecordingEnabled) = 0; - virtual bool GetLocalSnapshotRecordingEnabled() = 0; + virtual CameraError SetLocalSnapshotRecordingEnabled(bool localSnapshotRecordingEnabled) = 0; + virtual bool GetLocalSnapshotRecordingEnabled() = 0; - // Does camera have a status light - virtual bool HasStatusLight() = 0; + // Does camera have a status light + virtual bool HasStatusLight() = 0; - virtual CameraError SetStatusLightEnabled(bool statusLightEnabled) = 0; - virtual bool GetStatusLightEnabled(); - }; + virtual CameraError SetStatusLightEnabled(bool statusLightEnabled) = 0; + virtual bool GetStatusLightEnabled(); + }; - virtual CameraHALInterface &GetCameraHALInterface() = 0; + virtual CameraHALInterface & GetCameraHALInterface() = 0; }; diff --git a/examples/camera/main/common/webrtc-provider-controller.h b/examples/camera/main/common/webrtc-provider-controller.h index 81f89636b..7d37d2b5e 100644 --- a/examples/camera/main/common/webrtc-provider-controller.h +++ b/examples/camera/main/common/webrtc-provider-controller.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace chip { namespace app { @@ -14,9 +14,7 @@ class WebRTCTransportProviderController { public: virtual ~WebRTCTransportProviderController() = default; - virtual void - SetWebRTCTransportProvider(std::unique_ptr - webRTCTransportProvider) = 0; + virtual void SetWebRTCTransportProvider(WebRTCTransportProviderCluster *webRTCTransportProvider) = 0; }; } // namespace WebRTCTransportProvider diff --git a/examples/camera/main/webrtc/webrtc-abstract.h b/examples/camera/main/webrtc/webrtc-abstract.h index c1d1ee79e..b624ffd95 100644 --- a/examples/camera/main/webrtc/webrtc-abstract.h +++ b/examples/camera/main/webrtc/webrtc-abstract.h @@ -1,3 +1,20 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * All rights reserved. + * + * 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. + */ #pragma once #include @@ -9,46 +26,50 @@ class WebRTCPeerConnection; class WebRTCTrack; -enum class SDPType : uint8_t { Offer, Answer, Pranswer, Rollback }; - -enum class MediaType : uint8_t { - Audio, - Video, +enum class SDPType : uint8_t +{ + Offer, + Answer, + Pranswer, + Rollback }; -using OnLocalDescriptionCallback = - std::function; -using OnICECandidateCallback = - std::function; -using OnConnectionStateCallback = std::function; -using OnTrackCallback = std::function track)>; +enum class MediaType : uint8_t +{ + Audio, + Video, +}; + +using OnLocalDescriptionCallback = std::function; +using OnICECandidateCallback = std::function; +using OnConnectionStateCallback = std::function; +using OnTrackCallback = std::function track)>; // Abstract track interface -class WebRTCTrack { +class WebRTCTrack +{ public: - virtual ~WebRTCTrack() = default; + virtual ~WebRTCTrack() = default; - virtual void SendData(const char *data, size_t size) = 0; - virtual bool IsReady() = 0; - virtual std::string GetType() = 0; // "video" or "audio" + virtual void SendData(const char * data, size_t size) = 0; + virtual bool IsReady() = 0; + virtual std::string GetType() = 0; // "video" or "audio" }; // Abstract peer connection interface -class WebRTCPeerConnection { +class WebRTCPeerConnection +{ public: - virtual ~WebRTCPeerConnection() = default; + virtual ~WebRTCPeerConnection() = default; - virtual void SetCallbacks(OnLocalDescriptionCallback onLocalDescription, - OnICECandidateCallback onICECandidate, - OnConnectionStateCallback onConnectionState, - OnTrackCallback onTrack) = 0; - virtual void Close() = 0; - virtual void CreateOffer(uint16_t sessionId) = 0; - virtual void CreateAnswer() = 0; - virtual void SetRemoteDescription(const std::string &sdp, SDPType type) = 0; - virtual void AddRemoteCandidate(const std::string &candidate, - const std::string &mid) = 0; - virtual std::shared_ptr AddTrack(MediaType mediaType) = 0; + virtual void SetCallbacks(OnLocalDescriptionCallback onLocalDescription, OnICECandidateCallback onICECandidate, + OnConnectionStateCallback onConnectionState, OnTrackCallback onTrack) = 0; + virtual void Close() = 0; + virtual void CreateOffer(uint16_t sessionId) = 0; + virtual void CreateAnswer() = 0; + virtual void SetRemoteDescription(const std::string & sdp, SDPType type) = 0; + virtual void AddRemoteCandidate(const std::string & candidate, const std::string & mid) = 0; + virtual std::shared_ptr AddTrack(MediaType mediaType) = 0; }; std::shared_ptr CreateWebRTCPeerConnection(); diff --git a/examples/camera/main/webrtc/webrtc-transport.cpp b/examples/camera/main/webrtc/webrtc-transport.cpp index d130b1de3..9275db324 100644 --- a/examples/camera/main/webrtc/webrtc-transport.cpp +++ b/examples/camera/main/webrtc/webrtc-transport.cpp @@ -1,157 +1,183 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * All rights reserved. + * + * 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. + */ #include "webrtc-abstract.h" #include #include -WebrtcTransport::WebrtcTransport() { - ChipLogProgress(Camera, "WebrtcTransport created"); - mRequestArgs = {0, 0, 0, 0, 0, 0}; // Initialize request arguments to zero +WebrtcTransport::WebrtcTransport() +{ + ChipLogProgress(Camera, "WebrtcTransport created"); + mRequestArgs = { 0, 0, 0, 0, 0, 0 }; // Initialize request arguments to zero } -WebrtcTransport::~WebrtcTransport() { - ClosePeerConnection(); - ChipLogProgress(Camera, "WebrtcTransport destroyed for sessionID: [%u]", - mRequestArgs.sessionId); +WebrtcTransport::~WebrtcTransport() +{ + ClosePeerConnection(); + ChipLogProgress(Camera, "WebrtcTransport destroyed for sessionID: [%u]", mRequestArgs.sessionId); } -void WebrtcTransport::SetCallbacks( - OnTransportLocalDescriptionCallback onLocalDescription, - OnTransportConnectionStateCallback onConnectionState) { - mOnLocalDescription = onLocalDescription; - mOnConnectionState = onConnectionState; +void WebrtcTransport::SetCallbacks(OnTransportLocalDescriptionCallback onLocalDescription, + OnTransportConnectionStateCallback onConnectionState) +{ + mOnLocalDescription = onLocalDescription; + mOnConnectionState = onConnectionState; } -void WebrtcTransport::SetRequestArgs(const RequestArgs &args) { - mRequestArgs = args; +void WebrtcTransport::SetRequestArgs(const RequestArgs & args) +{ + mRequestArgs = args; } -WebrtcTransport::RequestArgs &WebrtcTransport::GetRequestArgs() { - return mRequestArgs; +WebrtcTransport::RequestArgs & WebrtcTransport::GetRequestArgs() +{ + return mRequestArgs; } -const char *WebrtcTransport::GetStateStr() const { - switch (mState) { - case State::Idle: - return "Idle"; +const char * WebrtcTransport::GetStateStr() const +{ + switch (mState) + { + case State::Idle: + return "Idle"; - case State::SendingOffer: - return "SendingOffer"; + case State::SendingOffer: + return "SendingOffer"; - case State::SendingAnswer: - return "SendingAnswer"; + case State::SendingAnswer: + return "SendingAnswer"; - case State::SendingICECandidates: - return "SendingICECandidates"; + case State::SendingICECandidates: + return "SendingICECandidates"; - case State::SendingEnd: - return "SendingEnd"; - } - return "N/A"; + case State::SendingEnd: + return "SendingEnd"; + } + return "N/A"; } -void WebrtcTransport::MoveToState(const State targetState) { - mState = targetState; - ChipLogProgress(Camera, "WebrtcTransport moving to [ %s ]", GetStateStr()); +void WebrtcTransport::MoveToState(const State targetState) +{ + mState = targetState; + ChipLogProgress(Camera, "WebrtcTransport moving to [ %s ]", GetStateStr()); } -void WebrtcTransport::SetCommandType(const CommandType commandtype) { - mCommandType = commandtype; +void WebrtcTransport::SetCommandType(const CommandType commandtype) +{ + mCommandType = commandtype; } -void WebrtcTransport::Start() { - if (mPeerConnection.get()) { - ChipLogProgress(Camera, "Start, mPeerConnection is already created"); - return; - } +void WebrtcTransport::Start() +{ + if (mPeerConnection.get()) + { + ChipLogProgress(Camera, "Start, mPeerConnection is already created"); + return; + } - mPeerConnection = CreateWebRTCPeerConnection(); + mPeerConnection = CreateWebRTCPeerConnection(); - mPeerConnection->SetCallbacks( - [this](const std::string &sdp, SDPType type) { - this->OnLocalDescription(sdp, type); - }, - [this](const std::string &candidate) { this->OnICECandidate(candidate); }, - [this](bool connected) { this->OnConnectionStateChanged(connected); }, - [this](std::shared_ptr track) { this->OnTrack(track); }); + mPeerConnection->SetCallbacks([this](const std::string & sdp, SDPType type) { this->OnLocalDescription(sdp, type); }, + [this](const std::string & candidate) { this->OnICECandidate(candidate); }, + [this](bool connected) { this->OnConnectionStateChanged(connected); }, + [this](std::shared_ptr track) { this->OnTrack(track); }); } -void WebrtcTransport::Stop() { - mVideoTrack = nullptr; - mAudioTrack = nullptr; - if (mPeerConnection != nullptr) { - // KVSWebRTC close is handled by the KVSWebRTCManager. - // mPeerConnection->Close(); - } +void WebrtcTransport::Stop() +{ + mVideoTrack = nullptr; + mAudioTrack = nullptr; } -void WebrtcTransport::AddTracks() { - if (mPeerConnection != nullptr) { - mVideoTrack = mPeerConnection->AddTrack(MediaType::Video); - mAudioTrack = mPeerConnection->AddTrack(MediaType::Audio); - } +void WebrtcTransport::AddTracks() +{ + if (mPeerConnection != nullptr) + { + mVideoTrack = mPeerConnection->AddTrack(MediaType::Video); + mAudioTrack = mPeerConnection->AddTrack(MediaType::Audio); + } } // Implementation of SetVideoTrack method -void WebrtcTransport::SetVideoTrack(std::shared_ptr videoTrack) { - ChipLogProgress(Camera, "Setting video track for sessionID: %u", - mRequestArgs.sessionId); - mVideoTrack = videoTrack; +void WebrtcTransport::SetVideoTrack(std::shared_ptr videoTrack) +{ + ChipLogProgress(Camera, "Setting video track for sessionID: %u", mRequestArgs.sessionId); + mVideoTrack = videoTrack; } // Implementation of SetAudioTrack method -void WebrtcTransport::SetAudioTrack(std::shared_ptr audioTrack) { - ChipLogProgress(Camera, "Setting audio track for sessionID: %u", - mRequestArgs.sessionId); - mAudioTrack = audioTrack; +void WebrtcTransport::SetAudioTrack(std::shared_ptr audioTrack) +{ + ChipLogProgress(Camera, "Setting audio track for sessionID: %u", mRequestArgs.sessionId); + mAudioTrack = audioTrack; } -void WebrtcTransport::AddRemoteCandidate(const std::string &candidate, - const std::string &mid) { - ChipLogProgress(Camera, "Adding remote candidate for sessionID: %u", - mRequestArgs.sessionId); - mPeerConnection->AddRemoteCandidate(candidate, mid); +void WebrtcTransport::AddRemoteCandidate(const std::string & candidate, const std::string & mid) +{ + ChipLogProgress(Camera, "Adding remote candidate for sessionID: %u", mRequestArgs.sessionId); + mPeerConnection->AddRemoteCandidate(candidate, mid); } // WebRTC Callbacks -void WebrtcTransport::OnLocalDescription(const std::string &sdp, SDPType type) { - ChipLogProgress(Camera, "Local description received for sessionID: %u", - mRequestArgs.sessionId); - mLocalSdp = sdp; - mLocalSdpType = type; - if (mOnLocalDescription) - mOnLocalDescription(sdp, type, mRequestArgs.sessionId); +void WebrtcTransport::OnLocalDescription(const std::string & sdp, SDPType type) +{ + ChipLogProgress(Camera, "Local description received for sessionID: %u", mRequestArgs.sessionId); + mLocalSdp = sdp; + mLocalSdpType = type; + if (mOnLocalDescription) + mOnLocalDescription(sdp, type, mRequestArgs.sessionId); } -bool WebrtcTransport::ClosePeerConnection() { - if (mPeerConnection == nullptr) { - return false; - } - // KVSWebRTC close is handled by the KVSWebRTCManager. - return true; +bool WebrtcTransport::ClosePeerConnection() +{ + if (mPeerConnection == nullptr) + { + return false; + } + // KVSWebRTC close is handled by the KVSWebRTCManager. + return true; } -void WebrtcTransport::OnICECandidate(const std::string &candidate) { - ChipLogProgress(Camera, "ICE Candidate received for sessionID: %u", - mRequestArgs.sessionId); - mLocalCandidates.push_back(candidate); - ChipLogProgress(Camera, "Local Candidate:"); - ChipLogProgress(Camera, "%s", candidate.c_str()); +void WebrtcTransport::OnICECandidate(const std::string & candidate) +{ + ChipLogProgress(Camera, "ICE Candidate received for sessionID: %u", mRequestArgs.sessionId); + mLocalCandidates.push_back(candidate); + ChipLogProgress(Camera, "Local Candidate:"); + ChipLogProgress(Camera, "%s", candidate.c_str()); } -void WebrtcTransport::OnConnectionStateChanged(bool connected) { - ChipLogProgress(Camera, "Connection state changed for sessionID: %u", - mRequestArgs.sessionId); - if (mOnConnectionState) - mOnConnectionState(connected, mRequestArgs.sessionId); +void WebrtcTransport::OnConnectionStateChanged(bool connected) +{ + ChipLogProgress(Camera, "Connection state changed for sessionID: %u", mRequestArgs.sessionId); + if (mOnConnectionState) + mOnConnectionState(connected, mRequestArgs.sessionId); } -void WebrtcTransport::OnTrack(std::shared_ptr track) { - ChipLogProgress(Camera, "Track received for sessionID: %u, type: %s", - mRequestArgs.sessionId, track->GetType().c_str()); - if (track->GetType() == "video") { - ChipLogProgress(Camera, "Video track updated from remote peer"); - SetVideoTrack(track); - } else if (track->GetType() == "audio") { - ChipLogProgress(Camera, "audio track updated from remote peer"); - SetAudioTrack(track); - } +void WebrtcTransport::OnTrack(std::shared_ptr track) +{ + ChipLogProgress(Camera, "Track received for sessionID: %u, type: %s", mRequestArgs.sessionId, track->GetType().c_str()); + if (track->GetType() == "video") + { + ChipLogProgress(Camera, "Video track updated from remote peer"); + SetVideoTrack(track); + } + else if (track->GetType() == "audio") + { + ChipLogProgress(Camera, "audio track updated from remote peer"); + SetAudioTrack(track); + } } diff --git a/examples/camera/main/webrtc/webrtc-transport.h b/examples/camera/main/webrtc/webrtc-transport.h index ce31bcfc8..3d317e4d0 100644 --- a/examples/camera/main/webrtc/webrtc-transport.h +++ b/examples/camera/main/webrtc/webrtc-transport.h @@ -1,112 +1,126 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * All rights reserved. + * + * 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. + */ #pragma once #include "webrtc-abstract.h" #include #include -using OnTransportLocalDescriptionCallback = std::function; -using OnTransportConnectionStateCallback = - std::function; +using OnTransportLocalDescriptionCallback = std::function; +using OnTransportConnectionStateCallback = std::function; -class WebrtcTransport { +class WebrtcTransport +{ public: - enum class CommandType : uint8_t { - kUndefined = 0, - kOffer = 1, - kAnswer = 2, - kICECandidates = 3, - kEnd = 4, - }; + enum class CommandType : uint8_t + { + kUndefined = 0, + kOffer = 1, + kAnswer = 2, + kICECandidates = 3, + kEnd = 4, + }; - enum class State : uint8_t { - Idle, ///< Default state, no communication initiated yet - SendingOffer, ///< Sending Offer command from camera - SendingAnswer, ///< Sending Answer command from camera - SendingICECandidates, ///< Sending ICECandidates command from camera - SendingEnd, ///< Sending End command from camera - }; + enum class State : uint8_t + { + Idle, ///< Default state, no communication initiated yet + SendingOffer, ///< Sending Offer command from camera + SendingAnswer, ///< Sending Answer command from camera + SendingICECandidates, ///< Sending ICECandidates command from camera + SendingEnd, ///< Sending End command from camera + }; - struct RequestArgs { - uint16_t sessionId; - uint16_t videoStreamId; - uint16_t audioStreamId; - chip::NodeId peerNodeId; - chip::FabricIndex fabricIndex; - chip::EndpointId originatingEndpointId; - chip::ScopedNodeId peerId; - }; + struct RequestArgs + { + uint16_t sessionId; + uint16_t videoStreamId; + uint16_t audioStreamId; + chip::NodeId peerNodeId; + chip::FabricIndex fabricIndex; + chip::EndpointId originatingEndpointId; + chip::ScopedNodeId peerId; + }; - WebrtcTransport(); + WebrtcTransport(); - ~WebrtcTransport(); + ~WebrtcTransport(); - void SetCallbacks(OnTransportLocalDescriptionCallback onLocalDescription, - OnTransportConnectionStateCallback onConnectionState); + void SetCallbacks(OnTransportLocalDescriptionCallback onLocalDescription, OnTransportConnectionStateCallback onConnectionState); - void MoveToState(const State targetState); - const char *GetStateStr() const; + void MoveToState(const State targetState); + const char * GetStateStr() const; - State GetState() { return mState; } + State GetState() { return mState; } - // Takes care of creation WebRTC peer connection and registering the necessary - // callbacks - void Start(); + // Takes care of creation WebRTC peer connection and registering the necessary + // callbacks + void Start(); - // Stops WebRTC peer connection and cleanup - void Stop(); + // Stops WebRTC peer connection and cleanup + void Stop(); - void AddTracks(); + void AddTracks(); - // Set video track for the transport - void SetVideoTrack(std::shared_ptr videoTrack); + // Set video track for the transport + void SetVideoTrack(std::shared_ptr videoTrack); - // Set audio track for the transport - void SetAudioTrack(std::shared_ptr audioTrack); + // Set audio track for the transport + void SetAudioTrack(std::shared_ptr audioTrack); - std::shared_ptr GetPeerConnection() { - return mPeerConnection; - } + std::shared_ptr GetPeerConnection() { return mPeerConnection; } - std::string GetLocalDescription() { return mLocalSdp; } + std::string GetLocalDescription() { return mLocalSdp; } - void SetSdpAnswer(std::string localSdp) { mLocalSdp = localSdp; } + void SetSdpAnswer(std::string localSdp) { mLocalSdp = localSdp; } - std::vector GetCandidates() { return mLocalCandidates; } + std::vector GetCandidates() { return mLocalCandidates; } - void SetCandidates(std::vector candidates) { - mLocalCandidates = candidates; - } + void SetCandidates(std::vector candidates) { mLocalCandidates = candidates; } - void AddRemoteCandidate(const std::string &candidate, const std::string &mid); + void AddRemoteCandidate(const std::string & candidate, const std::string & mid); - bool ClosePeerConnection(); + bool ClosePeerConnection(); - void SetCommandType(const CommandType commandtype); + void SetCommandType(const CommandType commandtype); - CommandType GetCommandType() { return mCommandType; } + CommandType GetCommandType() { return mCommandType; } - // WebRTC Callbacks - void OnLocalDescription(const std::string &sdp, SDPType type); - void OnICECandidate(const std::string &candidate); - void OnConnectionStateChanged(bool connected); - void OnTrack(std::shared_ptr track); + // WebRTC Callbacks + void OnLocalDescription(const std::string & sdp, SDPType type); + void OnICECandidate(const std::string & candidate); + void OnConnectionStateChanged(bool connected); + void OnTrack(std::shared_ptr track); - void SetRequestArgs(const RequestArgs &args); - RequestArgs &GetRequestArgs(); + void SetRequestArgs(const RequestArgs & args); + RequestArgs & GetRequestArgs(); private: - CommandType mCommandType = CommandType::kUndefined; - State mState = State::Idle; + CommandType mCommandType = CommandType::kUndefined; + State mState = State::Idle; - std::shared_ptr mPeerConnection; - std::shared_ptr mVideoTrack; - std::shared_ptr mAudioTrack; - std::string mLocalSdp; - SDPType mLocalSdpType; - std::vector mLocalCandidates; + std::shared_ptr mPeerConnection; + std::shared_ptr mVideoTrack; + std::shared_ptr mAudioTrack; + std::string mLocalSdp; + SDPType mLocalSdpType; + std::vector mLocalCandidates; - RequestArgs mRequestArgs; - OnTransportLocalDescriptionCallback mOnLocalDescription = nullptr; - OnTransportConnectionStateCallback mOnConnectionState = nullptr; + RequestArgs mRequestArgs; + OnTransportLocalDescriptionCallback mOnLocalDescription = nullptr; + OnTransportConnectionStateCallback mOnConnectionState = nullptr; }; diff --git a/examples/controller/CMakeLists.txt b/examples/controller/CMakeLists.txt index d78b9eb0d..d0817bfc9 100644 --- a/examples/controller/CMakeLists.txt +++ b/examples/controller/CMakeLists.txt @@ -18,7 +18,7 @@ set(EXTRA_COMPONENT_DIRS project(controller) -idf_build_set_property(CXX_COMPILE_OPTIONS "-std=gnu++17;-Os;-DCHIP_HAVE_CONFIG_H;-Wno-overloaded-virtual" APPEND) +idf_build_set_property(CXX_COMPILE_OPTIONS "-std=gnu++17;-Os;-DCHIP_HAVE_CONFIG_H;-Wno-overloaded-virtual;-Wno-error=unused-result" APPEND) idf_build_set_property(C_COMPILE_OPTIONS "-Os" APPEND) # We don't need Thread Network Commissioning Driver diff --git a/examples/ota_provider/main/app_main.cpp b/examples/ota_provider/main/app_main.cpp index fa57030a0..0f8427e50 100644 --- a/examples/ota_provider/main/app_main.cpp +++ b/examples/ota_provider/main/app_main.cpp @@ -16,8 +16,6 @@ #include -#include -#include #include #include #include diff --git a/install.sh b/install.sh index 3860c74c6..7a7efcfa6 100755 --- a/install.sh +++ b/install.sh @@ -63,7 +63,7 @@ export ZAP_INSTALL_PATH=${MATTER_PATH}/.environment/cipd/packages/zap if [ $NO_BOOTSTRAP = false ]; then echo_log "Running Matter Setup" cd ${MATTER_PATH} - source ${MATTER_PATH}/scripts/bootstrap.sh + source ${MATTER_PATH}/scripts/bootstrap.sh -p all,esp32 cd ${ESP_MATTER_PATH} else echo_log "Skipping Matter Setup" diff --git a/tools/docker/chip_idf/Dockerfile b/tools/docker/chip_idf/Dockerfile index 80c1d077e..fdc3341df 100644 --- a/tools/docker/chip_idf/Dockerfile +++ b/tools/docker/chip_idf/Dockerfile @@ -58,7 +58,7 @@ RUN set -x \ && git fetch origin --depth=1 ${CHIP_CHECKOUT_REF} \ && git checkout FETCH_HEAD \ && ./scripts/checkout_submodules.py --platform esp32 linux --shallow \ - && bash -c "source scripts/bootstrap.sh" \ + && bash -c "source scripts/bootstrap.sh -p all,esp32" \ && ./scripts/build_python.sh --enable_ble true --chip_detail_logging true -i out/py-env \ && bash -c "source out/py-env/bin/activate" \ && ./scripts/examples/gn_build_example.sh examples/chip-tool out/host \