From 6ad06723a9b306daf27d726e4b44189be0669a24 Mon Sep 17 00:00:00 2001 From: Philippe Coval Date: Mon, 29 Sep 2025 14:36:14 +0200 Subject: [PATCH 1/5] p/UnifySDK: Add Application Status cc Origin: https://github.com/SiliconLabsSoftware/z-wave-protocol-controller/pull/147 Signed-off-by: Philippe Coval --- ...-Allow-to-regenerate-files-using-zap.patch | 37 + ...75-Custom-Application-Status-cluster.patch | 62 + ...ed-files-for-Application-Status-Clus.patch | 3209 +++++++++++++++++ ...egen-zapfiles-for-Application-status.patch | 677 ++++ 4 files changed, 3985 insertions(+) create mode 100644 patches/UnifySDK/0007-fix-build-Allow-to-regenerate-files-using-zap.patch create mode 100644 patches/UnifySDK/0008-UIC-3275-Custom-Application-Status-cluster.patch create mode 100644 patches/UnifySDK/0009-UIC-3275-Generated-files-for-Application-Status-Clus.patch create mode 100644 patches/UnifySDK/0010-UIC-3275-Regen-zapfiles-for-Application-status.patch diff --git a/patches/UnifySDK/0007-fix-build-Allow-to-regenerate-files-using-zap.patch b/patches/UnifySDK/0007-fix-build-Allow-to-regenerate-files-using-zap.patch new file mode 100644 index 000000000..49e2f7541 --- /dev/null +++ b/patches/UnifySDK/0007-fix-build-Allow-to-regenerate-files-using-zap.patch @@ -0,0 +1,37 @@ +From f1b677b4127c8776244b272bcf20c9afeebe5522 Mon Sep 17 00:00:00 2001 +From: Philippe Coval +Date: Mon, 15 Sep 2025 10:43:26 +0200 +Subject: [PATCH] fix(build): Allow to regenerate files using zap + +For the record, files can be regenerated from developer using: + + rm -rf build ; cmake_options=-DZAP_GENERATE=ON ./helper.mk + +For some reasons developer will have to commit also generated files, +which is a bad practice, but I believe it was intentional to avoid +propagation to zap as build dependency, this may be revised to avoid +serialization of development. + +Relate-to: https://github.com/project-chip/zap/releases/download/v2025.07.24/zap-linux-x64.deb +Bug-SiliconLabs: UIC-3222 +Signed-off-by: Philippe Coval +--- + helper.mk | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/helper.mk b/helper.mk +index be052d5af6..75d1051981 100755 +--- a/helper.mk ++++ b/helper.mk +@@ -16,7 +16,7 @@ project?=unifysdk + BUILD_DEV_GUI?=OFF + BUILD_IMAGE_PROVIDER?=ON + +-cmake_options?=-B ${build_dir} ++cmake_options+=-B ${build_dir} + + CMAKE_GENERATOR?=Ninja + export CMAKE_GENERATOR +-- +2.39.5 + diff --git a/patches/UnifySDK/0008-UIC-3275-Custom-Application-Status-cluster.patch b/patches/UnifySDK/0008-UIC-3275-Custom-Application-Status-cluster.patch new file mode 100644 index 000000000..f0de56b38 --- /dev/null +++ b/patches/UnifySDK/0008-UIC-3275-Custom-Application-Status-cluster.patch @@ -0,0 +1,62 @@ +From 4ea4074565a40f60022a4da11106786412c1f69e Mon Sep 17 00:00:00 2001 +From: Viet +Date: Tue, 1 Oct 2024 14:21:40 +0700 +Subject: [PATCH] UIC-3275: Custom Application Status cluster + +Relate-to: https://github.com/SiliconLabsSoftware/z-wave-protocol-controller/pull/147 +Signed-off-by: Philippe Coval +--- + .../dotdot-xml/Unify_ApplicationStatus.xml | 29 +++++++++++++++++++ + components/uic_dotdot/dotdot-xml/library.xml | 1 + + 2 files changed, 30 insertions(+) + create mode 100644 components/uic_dotdot/dotdot-xml/Unify_ApplicationStatus.xml + +diff --git a/components/uic_dotdot/dotdot-xml/Unify_ApplicationStatus.xml b/components/uic_dotdot/dotdot-xml/Unify_ApplicationStatus.xml +new file mode 100644 +index 0000000000..2d51efb7ee +--- /dev/null ++++ b/components/uic_dotdot/dotdot-xml/Unify_ApplicationStatus.xml +@@ -0,0 +1,29 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/components/uic_dotdot/dotdot-xml/library.xml b/components/uic_dotdot/dotdot-xml/library.xml +index 4210b67fa4..d8649f4339 100644 +--- a/components/uic_dotdot/dotdot-xml/library.xml ++++ b/components/uic_dotdot/dotdot-xml/library.xml +@@ -501,4 +501,5 @@ applicable to this document can be found in the LICENSE.md file. + + + ++ + +\ No newline at end of file +-- +2.39.5 + diff --git a/patches/UnifySDK/0009-UIC-3275-Generated-files-for-Application-Status-Clus.patch b/patches/UnifySDK/0009-UIC-3275-Generated-files-for-Application-Status-Clus.patch new file mode 100644 index 000000000..2d6f6f7f7 --- /dev/null +++ b/patches/UnifySDK/0009-UIC-3275-Generated-files-for-Application-Status-Clus.patch @@ -0,0 +1,3209 @@ +From 76d2bcc535b35b0b1870df2ea3aa749a36344cac Mon Sep 17 00:00:00 2001 +From: Viet +Date: Tue, 1 Oct 2024 14:22:59 +0700 +Subject: [PATCH] UIC-3275: Generated files for Application Status Cluster + +Relate-to: https://github.com/SiliconLabsSoftware/z-wave-protocol-controller/pull/147 +Signed-off-by: Philippe Coval +--- + .../cluster-types/cluster-type-attributes.ts | 2 +- + .../zap-generated/include/dotdot_mqtt.h | 215 +++++++ + .../include/dotdot_mqtt_attributes.h | 48 ++ + .../include/dotdot_mqtt_generated_commands.h | 22 + + .../include/dotdot_mqtt_group_commands.h | 17 + + .../include/dotdot_mqtt_helpers.h | 7 + + .../include/dotdot_mqtt_helpers.hpp | 17 + + ...dotdot_mqtt_supported_generated_commands.h | 28 + + .../zap-generated/src/dotdot_mqtt.cpp | 561 ++++++++++++++++++ + .../zap-generated/src/dotdot_mqtt.hpp | 28 + + .../src/dotdot_mqtt_attributes.cpp | 290 +++++++++ + .../src/dotdot_mqtt_command_helpers.cpp | 16 + + .../src/dotdot_mqtt_command_helpers.hpp | 12 + + .../src/dotdot_mqtt_generated_commands.cpp | 40 ++ + .../src/dotdot_mqtt_group_commands.cpp | 99 ++++ + .../zap-generated/src/dotdot_mqtt_helpers.cpp | 92 +++ + ...tdot_mqtt_supported_generated_commands.cpp | 43 ++ + .../test/dotdot_mqtt_test.include | 1 + + .../include/dotdot_attributes.uam | 5 + + .../include/dotdot_attributes_camel_case.uam | 5 + + .../unify_dotdot_attribute_store_helpers.h | 346 +++++++++++ + .../unify_dotdot_defined_attribute_types.h | 4 + + ...ot_attribute_store_attribute_publisher.cpp | 301 ++++++++++ + ..._force_read_attributes_command_callbacks.c | 42 ++ + .../unify_dotdot_attribute_store_helpers.cpp | 361 +++++++++++ + ...fy_dotdot_attribute_store_registration.cpp | 72 +++ + ...store_write_attributes_command_callbacks.c | 33 ++ + .../test/unify_dotdot_attribute_store_test.c | 29 + + .../test/unify_dotdot_attribute_store_test.h | 4 + + 29 files changed, 2739 insertions(+), 1 deletion(-) + +diff --git a/applications/dev_ui/dev_gui/zap-generated/src/cluster-types/cluster-type-attributes.ts b/applications/dev_ui/dev_gui/zap-generated/src/cluster-types/cluster-type-attributes.ts +index a46049dbe7..85b3c53dd3 100644 +--- a/applications/dev_ui/dev_gui/zap-generated/src/cluster-types/cluster-type-attributes.ts ++++ b/applications/dev_ui/dev_gui/zap-generated/src/cluster-types/cluster-type-attributes.ts +@@ -1,7 +1,7 @@ + //This file is generated automatically. Don't try to change something here. + //To add support for new clusters, modify addon-helper.js + //To change the stucture of the ClusterTypeAttrs, modify cluster-type-attributes.zapt +- ++ + + //generate ClusterTypes + export let ClusterTypeAttrs: any = { +diff --git a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt.h b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt.h +index bd703a2daf..f6ed4bc665 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt.h ++++ b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt.h +@@ -40803,6 +40803,221 @@ void uic_mqtt_dotdot_unify_humidity_control_publish_supported_commands( + void uic_mqtt_dotdot_unify_humidity_control_publish_empty_supported_commands( + const dotdot_unid_t unid + ,dotdot_endpoint_id_t endpoint); ++// Callback types used by the application_status cluster ++ ++typedef struct { ++ uint8_t busy_status; ++ uint8_t wait_time; ++ bool reject_status; ++} uic_mqtt_dotdot_application_status_state_t; ++ ++typedef struct { ++ bool busy_status; ++ bool wait_time; ++ bool reject_status; ++} uic_mqtt_dotdot_application_status_updated_state_t; ++ ++typedef sl_status_t (*uic_mqtt_dotdot_application_status_write_attributes_callback_t)( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint, ++ uic_mqtt_dotdot_callback_call_type_t call_type, ++ uic_mqtt_dotdot_application_status_state_t, ++ uic_mqtt_dotdot_application_status_updated_state_t ++); ++ ++typedef sl_status_t (*uic_mqtt_dotdot_application_status_force_read_attributes_callback_t)( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint, ++ uic_mqtt_dotdot_callback_call_type_t call_type, ++ uic_mqtt_dotdot_application_status_updated_state_t ++); ++ ++ ++ ++ ++/** ++ * @brief Setup a callback for WriteAttribute to be called when a ++ * +/application_status/Commands/WriteAttributes is received. ++ * ++ * Setting this callback will not overwrite the previous set callback ++ * @param callback Function to be called on command reception ++ */ ++void uic_mqtt_dotdot_set_application_status_write_attributes_callback( ++ const uic_mqtt_dotdot_application_status_write_attributes_callback_t callback ++); ++/** ++ * @brief Unsets a callback for WriteAttribute to be called when a ++ * +/application_status/Commands/WriteAttributes is received. ++ * @param callback Function to be no longer called on command reception ++ */ ++void uic_mqtt_dotdot_unset_application_status_write_attributes_callback( ++ const uic_mqtt_dotdot_application_status_write_attributes_callback_t callback ++); ++/** ++ * @brief Clears all callbacks registered for when ++ * +/application_status/Commands/WriteAttributes is received. ++ */ ++void uic_mqtt_dotdot_clear_application_status_write_attributes_callbacks(); ++ ++/** ++ * @brief Setup a callback for ForceReadAttributes to be called when a ++ * +/application_status/Commands/ForceReadAttributes is received. ++ * ++ * Setting this callback will not overwrite the previous set callback ++ * @param callback Function to be called on command reception ++ */ ++void uic_mqtt_dotdot_set_application_status_force_read_attributes_callback( ++ const uic_mqtt_dotdot_application_status_force_read_attributes_callback_t callback ++); ++/** ++ * @brief Unsets a callback for ForceReadAttributes to be called when a ++ * +/application_status/Commands/ForceReadAttributes is received. ++ * ++ * @param callback Function to be no longer called on command reception ++ */ ++void uic_mqtt_dotdot_unset_application_status_force_read_attributes_callback( ++ const uic_mqtt_dotdot_application_status_force_read_attributes_callback_t callback ++); ++/** ++ * @brief Clears all callbacks registered for when ++ * +/application_status/Commands/ForceReadAttributes is received. ++ */ ++void uic_mqtt_dotdot_clear_application_status_force_read_attributes_callbacks(); ++ ++/** ++ * @brief Publish the attribute; ApplicationStatus/Attributes/BusyStatus ++ * ++ * @param base_topic topic prefix to publish, /busy_status ++ * will be appended ++ * @param value Value to publish ++ * @param publish_type Whether to publish as Desired, Reported, or Both. ++ * ++ * @returns SL_STATUS_OK on success ++ */ ++sl_status_t uic_mqtt_dotdot_application_status_busy_status_publish( ++ const char *base_topic, ++ uint8_t value, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type ++); ++ ++/** ++ * @brief Unretains a published attribute; ApplicationStatus/Attributes/BusyStatus ++ * ++ * @param base_topic topic prefix to publish, /busy_status ++ * will be appended ++ * @param publish_type Whether to publish as Desired, Reported, or Both. ++ * ++ * @returns SL_STATUS_OK on success ++ */ ++sl_status_t uic_mqtt_dotdot_application_status_busy_status_unretain( ++ const char *base_topic, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type ++); ++ ++/** ++ * @brief Publish the attribute; ApplicationStatus/Attributes/WaitTime ++ * ++ * @param base_topic topic prefix to publish, /wait_time ++ * will be appended ++ * @param value Value to publish ++ * @param publish_type Whether to publish as Desired, Reported, or Both. ++ * ++ * @returns SL_STATUS_OK on success ++ */ ++sl_status_t uic_mqtt_dotdot_application_status_wait_time_publish( ++ const char *base_topic, ++ uint8_t value, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type ++); ++ ++/** ++ * @brief Unretains a published attribute; ApplicationStatus/Attributes/WaitTime ++ * ++ * @param base_topic topic prefix to publish, /wait_time ++ * will be appended ++ * @param publish_type Whether to publish as Desired, Reported, or Both. ++ * ++ * @returns SL_STATUS_OK on success ++ */ ++sl_status_t uic_mqtt_dotdot_application_status_wait_time_unretain( ++ const char *base_topic, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type ++); ++ ++/** ++ * @brief Publish the attribute; ApplicationStatus/Attributes/RejectStatus ++ * ++ * @param base_topic topic prefix to publish, /reject_status ++ * will be appended ++ * @param value Value to publish ++ * @param publish_type Whether to publish as Desired, Reported, or Both. ++ * ++ * @returns SL_STATUS_OK on success ++ */ ++sl_status_t uic_mqtt_dotdot_application_status_reject_status_publish( ++ const char *base_topic, ++ bool value, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type ++); ++ ++/** ++ * @brief Unretains a published attribute; ApplicationStatus/Attributes/RejectStatus ++ * ++ * @param base_topic topic prefix to publish, /reject_status ++ * will be appended ++ * @param publish_type Whether to publish as Desired, Reported, or Both. ++ * ++ * @returns SL_STATUS_OK on success ++ */ ++sl_status_t uic_mqtt_dotdot_application_status_reject_status_unretain( ++ const char *base_topic, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type ++); ++ ++ ++/** ++ * @brief Publish the ApplicationStatus/ClusterRevision attribute ++ * ++ * @param base_topic topic prefix to publish, /ApplicationStatus/Attributes/ClusterRevision ++ * will be appended. ++ * @param value Value to publish. ++ */ ++void uic_mqtt_dotdot_application_status_publish_cluster_revision(const char* base_topic, uint16_t value); ++ ++/** ++ * @brief Unretain a publication to ApplicationStatus/ClusterRevision attribute ++ * ++ * @param base_topic topic prefix to publish, /ApplicationStatus/Attributes/ClusterRevision ++ * will be appended. ++ */ ++void uic_mqtt_dotdot_application_status_unretain_cluster_revision(const char* base_topic); ++ ++/** ++ * @brief Publish the SupportedCommands for UNID/EndPoint for the ApplicationStatus Cluster ++ * ++ * This function will iterate over all Commands in the ApplicationStatus Cluster and ++ * call all registered callback functions with UNID/endpoint, and ++ * callback_type = UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK. ++ * All Cluster Command callback functions that return SL_STATUS_OK ++ * will be added to the list of supported commands and published. ++ * ++ * @param unid ++ * @param endpoint ++ */ ++void uic_mqtt_dotdot_application_status_publish_supported_commands( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint); ++ ++/** ++ * @brief Publish an empty array of SupportedCommands for UNID/EndPoint for ++ * the ApplicationStatus Cluster ++ * ++ * @param unid ++ * @param endpoint ) ++ */ ++void uic_mqtt_dotdot_application_status_publish_empty_supported_commands( ++ const dotdot_unid_t unid ++ ,dotdot_endpoint_id_t endpoint); + + /** + * @brief Publish the SupportedCommands for UNID/EndPoint +diff --git a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_attributes.h b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_attributes.h +index 68b51b7e03..11fee456ed 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_attributes.h ++++ b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_attributes.h +@@ -5173,6 +5173,28 @@ typedef sl_status_t (*uic_mqtt_dotdot_unify_humidity_control_attribute_auto_setp + uic_mqtt_dotdot_attribute_update_type_t update_type, + uint8_t auto_setpoint_precision + ); ++// Callback types used by the application_status cluster ++typedef sl_status_t (*uic_mqtt_dotdot_application_status_attribute_busy_status_callback_t)( ++ dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint, ++ bool unretained, ++ uic_mqtt_dotdot_attribute_update_type_t update_type, ++ uint8_t busy_status ++); ++typedef sl_status_t (*uic_mqtt_dotdot_application_status_attribute_wait_time_callback_t)( ++ dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint, ++ bool unretained, ++ uic_mqtt_dotdot_attribute_update_type_t update_type, ++ uint8_t wait_time ++); ++typedef sl_status_t (*uic_mqtt_dotdot_application_status_attribute_reject_status_callback_t)( ++ dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint, ++ bool unretained, ++ uic_mqtt_dotdot_attribute_update_type_t update_type, ++ bool reject_status ++); + + #ifdef __cplusplus + extern "C" { +@@ -9941,6 +9963,32 @@ void uic_mqtt_dotdot_unify_humidity_control_attribute_auto_setpoint_scale_callba + void uic_mqtt_dotdot_unify_humidity_control_attribute_auto_setpoint_precision_callback_set(const uic_mqtt_dotdot_unify_humidity_control_attribute_auto_setpoint_precision_callback_t callback); + + ++/** ++ * Initializes the attributes features for the ApplicationStatus cluster, ++ * allowing to receive attribute updates from other UNIDs. ++ */ ++sl_status_t uic_mqtt_dotdot_application_status_attributes_init(); ++ ++/** ++ * Setup callback to be called when a ++ * ApplicationStatus/Attributes/busy_status/# is received. Setting ++ * this callback will overwrite the previous set callback ++ */ ++void uic_mqtt_dotdot_application_status_attribute_busy_status_callback_set(const uic_mqtt_dotdot_application_status_attribute_busy_status_callback_t callback); ++/** ++ * Setup callback to be called when a ++ * ApplicationStatus/Attributes/wait_time/# is received. Setting ++ * this callback will overwrite the previous set callback ++ */ ++void uic_mqtt_dotdot_application_status_attribute_wait_time_callback_set(const uic_mqtt_dotdot_application_status_attribute_wait_time_callback_t callback); ++/** ++ * Setup callback to be called when a ++ * ApplicationStatus/Attributes/reject_status/# is received. Setting ++ * this callback will overwrite the previous set callback ++ */ ++void uic_mqtt_dotdot_application_status_attribute_reject_status_callback_set(const uic_mqtt_dotdot_application_status_attribute_reject_status_callback_t callback); ++ ++ + #ifdef __cplusplus + } + #endif // __cplusplus +diff --git a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_generated_commands.h b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_generated_commands.h +index 011a75ddf9..dd0b69a8e9 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_generated_commands.h ++++ b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_generated_commands.h +@@ -5040,6 +5040,28 @@ void uic_mqtt_dotdot_unify_humidity_control_publish_generated_write_attributes_c + ); + + ++/** ++ * @brief Publishes an incoming/generated WriteAttributes command for ++ * the ApplicationStatus cluster. ++ * ++ * Publication will be made at the following topic ++ * ucl/by-unid/UNID/epID/ApplicationStatus/GeneratedCommands/WriteAttributes ++ * ++ * @param unid The UNID of the node that sent us the command. ++ * ++ * @param endpoint The Endpoint ID of the node that sent us the command. ++ * ++ * @param attribute_values Values to assign to the attributes ++ * @param attribute_list List of attributes that are written ++ */ ++void uic_mqtt_dotdot_application_status_publish_generated_write_attributes_command( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint, ++ uic_mqtt_dotdot_application_status_state_t attribute_values, ++ uic_mqtt_dotdot_application_status_updated_state_t attribute_list ++); ++ ++ + #ifdef __cplusplus + } + #endif // __cplusplus +diff --git a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_group_commands.h b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_group_commands.h +index ac85664a4f..60a90e992c 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_group_commands.h ++++ b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_group_commands.h +@@ -3741,6 +3741,23 @@ void uic_mqtt_dotdot_by_group_unify_humidity_control_write_attributes_callback_s + + + ++typedef void (*uic_mqtt_dotdot_by_group_application_status_write_attributes_callback_t)( ++ const dotdot_group_id_t group_id, ++ uic_mqtt_dotdot_application_status_state_t, ++ uic_mqtt_dotdot_application_status_updated_state_t ++); ++ ++/** ++ * Setup a callback for WriteAttribute to be called when a ++ * ucl/by-group/+/application_status/Commands/WriteAttributes is received. ++ * Setting this callback will overwrite any previously set callback. ++ */ ++void uic_mqtt_dotdot_by_group_application_status_write_attributes_callback_set( ++ const uic_mqtt_dotdot_by_group_application_status_write_attributes_callback_t callback ++); ++ ++ ++ + #ifdef __cplusplus + } + #endif // __cplusplus +diff --git a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_helpers.h b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_helpers.h +index 80d87abc45..a08bd7784d 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_helpers.h ++++ b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_helpers.h +@@ -55,6 +55,13 @@ char *aox_locator_reporting_mode_get_enum_value_name_c( + uint32_t value, char *result, size_t max_result_size); + /** Get aox_locator_reporting_mode enum representation from string. */ + uint32_t aox_locator_reporting_mode_get_enum_value_number_c(const char *str); ++#define APPLICATION_STATUS_BUSY_STATUS_ENUM_NAME_AVAILABLE 1 ++ ++/** Get application_status_busy_status string representation from enum. */ ++char *application_status_busy_status_get_enum_value_name_c( ++ uint32_t value, char *result, size_t max_result_size); ++/** Get application_status_busy_status enum representation from string. */ ++uint32_t application_status_busy_status_get_enum_value_number_c(const char *str); + #define ARM_ARM_MODE_ENUM_NAME_AVAILABLE 1 + + /** Get arm_arm_mode string representation from enum. */ +diff --git a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_helpers.hpp b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_helpers.hpp +index 14befa43ce..8f752c4b25 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_helpers.hpp ++++ b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_helpers.hpp +@@ -98,6 +98,23 @@ std::string aox_locator_reporting_mode_get_enum_value_name( + */ + uint32_t aox_locator_reporting_mode_get_enum_value_number(const std::string &str); + ++#define APPLICATION_STATUS_BUSY_STATUS_ENUM_NAME_AVAILABLE 1 ++ ++/** ++ * @brief Finds the name of a field for the ApplicationStatusBusyStatus enum ++ * ++ * @returns A string representation of the value. ++ */ ++std::string application_status_busy_status_get_enum_value_name( ++ uint32_t value); ++ ++/** ++ * @brief Finds the enum number of a string representation for the ApplicationStatusBusyStatus enum ++ * ++ * @returns A number enum value. ++ */ ++uint32_t application_status_busy_status_get_enum_value_number(const std::string &str); ++ + #define ARM_ARM_MODE_ENUM_NAME_AVAILABLE 1 + + /** +diff --git a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_supported_generated_commands.h b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_supported_generated_commands.h +index 3ee01c0c62..c5c6cd9a70 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_supported_generated_commands.h ++++ b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_supported_generated_commands.h +@@ -1694,6 +1694,34 @@ void uic_mqtt_dotdot_unify_humidity_control_publish_supported_generated_commands + ); + + ++/** ++ * @brief Struct containing the list of commands for ApplicationStatus ++ */ ++typedef struct _uic_mqtt_dotdot_application_status_supported_commands_ { ++ bool write_attributes; ++} uic_mqtt_dotdot_application_status_supported_commands_t; ++ ++/** ++ * @brief Sends/Publishes a the SupportedGenerated commands for ++ * the ApplicationStatus cluster for a UNID/Endpoint ++ * ++ * Publication will be made at the following topic ++ * ucl/by-unid/UNID/epID/ApplicationStatus/SupportedGeneratedCommands ++ * ++ * @param unid The UNID of the node on behalf of which the advertisment is made ++ * ++ * @param endpoint The Endpoint ID of the node on behalf of which the advertisment is made ++ * ++ * @param command_list Struct pointer with the fields value indicating if ++ * individual commands can be generated. ++ */ ++void uic_mqtt_dotdot_application_status_publish_supported_generated_commands( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint, ++ const uic_mqtt_dotdot_application_status_supported_commands_t *command_list ++); ++ ++ + + #ifdef __cplusplus + } +diff --git a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.cpp b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.cpp +index d1122bf8dd..6686df255a 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.cpp ++++ b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.cpp +@@ -95550,6 +95550,413 @@ sl_status_t uic_mqtt_dotdot_unify_humidity_control_init() + return SL_STATUS_OK; + } + ++// Callbacks pointers ++static std::set uic_mqtt_dotdot_application_status_write_attributes_callback; ++static std::set uic_mqtt_dotdot_application_status_force_read_attributes_callback; ++ ++// Callbacks setters ++ ++void uic_mqtt_dotdot_set_application_status_write_attributes_callback( ++ const uic_mqtt_dotdot_application_status_write_attributes_callback_t callback) ++{ ++ if (callback != nullptr) { ++ uic_mqtt_dotdot_application_status_write_attributes_callback.insert(callback); ++ } ++} ++void uic_mqtt_dotdot_unset_application_status_write_attributes_callback( ++ const uic_mqtt_dotdot_application_status_write_attributes_callback_t callback) ++{ ++ uic_mqtt_dotdot_application_status_write_attributes_callback.erase(callback); ++} ++void uic_mqtt_dotdot_clear_application_status_write_attributes_callbacks() ++{ ++ uic_mqtt_dotdot_application_status_write_attributes_callback.clear(); ++} ++std::set& get_uic_mqtt_dotdot_application_status_write_attributes_callback() ++{ ++ return uic_mqtt_dotdot_application_status_write_attributes_callback; ++} ++ ++void uic_mqtt_dotdot_set_application_status_force_read_attributes_callback( ++ const uic_mqtt_dotdot_application_status_force_read_attributes_callback_t callback) ++{ ++ if (callback != nullptr) { ++ uic_mqtt_dotdot_application_status_force_read_attributes_callback.insert(callback); ++ } ++} ++void uic_mqtt_dotdot_unset_application_status_force_read_attributes_callback( ++ const uic_mqtt_dotdot_application_status_force_read_attributes_callback_t callback) ++{ ++ uic_mqtt_dotdot_application_status_force_read_attributes_callback.erase(callback); ++} ++void uic_mqtt_dotdot_clear_application_status_force_read_attributes_callbacks() ++{ ++ uic_mqtt_dotdot_application_status_force_read_attributes_callback.clear(); ++} ++ ++ ++// Callback function for incoming publications on ucl/by-unid/+/+/ApplicationStatus/Commands/WriteAttributes ++void uic_mqtt_dotdot_on_application_status_WriteAttributes( ++ const char *topic, ++ const char *message, ++ const size_t message_length) ++{ ++ if (uic_mqtt_dotdot_application_status_write_attributes_callback.empty()) { ++ return; ++ } ++ ++ if (message_length == 0) { ++ return; ++ } ++ ++ std::string unid; ++ uint8_t endpoint = 0; // Default value for endpoint-less topics. ++ if(! uic_dotdot_mqtt::parse_topic(topic,unid,endpoint)) { ++ sl_log_debug(LOG_TAG, ++ "Error parsing UNID / Endpoint ID from topic %s. Ignoring", ++ topic); ++ return; ++ } ++ ++ uic_mqtt_dotdot_application_status_state_t new_state = {}; ++ uic_mqtt_dotdot_application_status_updated_state_t new_updated_state = {}; ++ ++ ++ nlohmann::json jsn; ++ try { ++ jsn = nlohmann::json::parse(std::string(message)); ++ ++ uic_mqtt_dotdot_parse_application_status_write_attributes( ++ jsn, ++ new_state, ++ new_updated_state ++ ); ++ } catch (const nlohmann::json::parse_error& e) { ++ // Catch JSON object field parsing errors ++ sl_log_debug(LOG_TAG, LOG_FMT_JSON_PARSE_FAIL, "ApplicationStatus", "WriteAttributes"); ++ return; ++ } catch (const nlohmann::json::exception& e) { ++ // Catch JSON object field parsing errors ++ sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "ApplicationStatus", "WriteAttributes", e.what()); ++ return; ++ } catch (const std::exception& e) { ++ sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "ApplicationStatus", "WriteAttributes", ""); ++ return; ++ } ++ ++ for (const auto& callback: uic_mqtt_dotdot_application_status_write_attributes_callback){ ++ callback( ++ static_cast(unid.c_str()), ++ endpoint, ++ UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL, ++ new_state, ++ new_updated_state ++ ); ++ } ++ ++} ++ ++static void uic_mqtt_dotdot_on_application_status_force_read_attributes( ++ const char *topic, ++ const char *message, ++ const size_t message_length) ++{ ++ uint8_t endpoint = 0; ++ std::string unid; ++ ++ if ((message_length == 0) || (uic_mqtt_dotdot_application_status_force_read_attributes_callback.empty())) { ++ return; ++ } ++ ++ if(! uic_dotdot_mqtt::parse_topic(topic, unid, endpoint)) { ++ sl_log_debug(LOG_TAG, ++ "Error parsing UNID / Endpoint ID from topic %s. Ignoring", ++ topic); ++ return; ++ } ++ ++ try { ++ uic_mqtt_dotdot_application_status_updated_state_t force_update = {0}; ++ bool trigger_handler = false; ++ ++ nlohmann::json jsn = nlohmann::json::parse(std::string(message)); ++ std::vector attributes = jsn["value"].get>(); ++ ++ // Assume all attributes to be read on empty array received ++ if (attributes.size() == 0) { ++ force_update.busy_status = true; ++ force_update.wait_time = true; ++ force_update.reject_status = true; ++ trigger_handler = true; ++ } else { ++ std::unordered_map supported_attrs = { ++ {"BusyStatus", &force_update.busy_status }, ++ {"WaitTime", &force_update.wait_time }, ++ {"RejectStatus", &force_update.reject_status }, ++ }; ++ ++ for (auto& attribute : attributes) { ++ auto found_attr = supported_attrs.find(attribute); ++ if (found_attr != supported_attrs.end()) { ++ *(found_attr->second) = true; ++ trigger_handler = true; ++ } ++ } ++ } ++ ++ if (trigger_handler == true) { ++ for (const auto& callback: uic_mqtt_dotdot_application_status_force_read_attributes_callback) { ++ callback( ++ static_cast(unid.c_str()), ++ endpoint, ++ UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL, ++ force_update ++ ); ++ } ++ } ++ } catch (...) { ++ sl_log_debug(LOG_TAG, "ApplicationStatus/Commands/ForceReadAttributes: Unable to parse JSON payload"); ++ return; ++ } ++} ++ ++sl_status_t uic_mqtt_dotdot_application_status_busy_status_publish( ++ const char *base_topic, ++ uint8_t value, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type ++) ++{ ++ nlohmann::json jsn; ++ ++ // This is a single value ++ ++ #ifdef APPLICATION_STATUS_BUSY_STATUS_ENUM_NAME_AVAILABLE ++ jsn["value"] = application_status_busy_status_get_enum_value_name((uint32_t)value); ++ #elif defined(ENUM8_ENUM_NAME_AVAILABLE) ++ jsn["value"] = enum8_get_enum_value_name((uint32_t)value); ++ #else ++ sl_log_warning(LOG_TAG,"Warning: Enum name not available for APPLICATION_STATUS_BUSY_STATUS. Using number instead."); ++ jsn["value"] = static_cast(value); ++ #endif ++ ++ ++ std::string payload_str; ++ try { ++ // Payload contains data from end nodes, which we cannot control, thus we handle if there are non-utf8 characters ++ payload_str = jsn.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); ++ } catch (const nlohmann::json::exception& e) { ++ sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "ApplicationStatus/Attributes/BusyStatus", e.what()); ++ return SL_STATUS_OK; ++ } ++ ++ ++ std::string topic = std::string(base_topic) + "/ApplicationStatus/Attributes/BusyStatus"; ++ if (publish_type & UCL_MQTT_PUBLISH_TYPE_DESIRED) ++ { ++ std::string topic_desired = topic + "/Desired"; ++ uic_mqtt_publish(topic_desired.c_str(), ++ payload_str.c_str(), ++ payload_str.length(), ++ true); ++ } ++ if (publish_type & UCL_MQTT_PUBLISH_TYPE_REPORTED) ++ { ++ std::string topic_reported = topic + "/Reported"; ++ uic_mqtt_publish(topic_reported.c_str(), ++ payload_str.c_str(), ++ payload_str.length(), ++ true); ++ } ++ return SL_STATUS_OK; ++} ++ ++sl_status_t uic_mqtt_dotdot_application_status_busy_status_unretain( ++ const char *base_topic, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type) ++{ ++ // clang-format on ++ std::string topic ++ = std::string(base_topic) ++ + "/ApplicationStatus/Attributes/BusyStatus"; ++ ++ if ((publish_type == UCL_MQTT_PUBLISH_TYPE_DESIRED) ++ || (publish_type == UCL_MQTT_PUBLISH_TYPE_ALL)) { ++ std::string topic_desired = topic + "/Desired"; ++ uic_mqtt_publish(topic_desired.c_str(), NULL, 0, true); ++ } ++ if ((publish_type == UCL_MQTT_PUBLISH_TYPE_REPORTED) ++ || (publish_type == UCL_MQTT_PUBLISH_TYPE_ALL)) { ++ std::string topic_reported = topic + "/Reported"; ++ uic_mqtt_publish(topic_reported.c_str(), NULL, 0, true); ++ } ++ return SL_STATUS_OK; ++} ++// clang-format off ++ ++sl_status_t uic_mqtt_dotdot_application_status_wait_time_publish( ++ const char *base_topic, ++ uint8_t value, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type ++) ++{ ++ nlohmann::json jsn; ++ ++ // This is a single value ++ ++ if (true == uic_dotdot_has_attribute_value_a_name(65442,1,value)) { ++ jsn["value"] = uic_dotdot_get_attribute_value_name(65442,1,value); ++ }else{ ++ jsn["value"] = value; ++ } ++ ++ ++ std::string payload_str; ++ try { ++ // Payload contains data from end nodes, which we cannot control, thus we handle if there are non-utf8 characters ++ payload_str = jsn.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); ++ } catch (const nlohmann::json::exception& e) { ++ sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "ApplicationStatus/Attributes/WaitTime", e.what()); ++ return SL_STATUS_OK; ++ } ++ ++ ++ std::string topic = std::string(base_topic) + "/ApplicationStatus/Attributes/WaitTime"; ++ if (publish_type & UCL_MQTT_PUBLISH_TYPE_DESIRED) ++ { ++ std::string topic_desired = topic + "/Desired"; ++ uic_mqtt_publish(topic_desired.c_str(), ++ payload_str.c_str(), ++ payload_str.length(), ++ true); ++ } ++ if (publish_type & UCL_MQTT_PUBLISH_TYPE_REPORTED) ++ { ++ std::string topic_reported = topic + "/Reported"; ++ uic_mqtt_publish(topic_reported.c_str(), ++ payload_str.c_str(), ++ payload_str.length(), ++ true); ++ } ++ return SL_STATUS_OK; ++} ++ ++sl_status_t uic_mqtt_dotdot_application_status_wait_time_unretain( ++ const char *base_topic, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type) ++{ ++ // clang-format on ++ std::string topic ++ = std::string(base_topic) ++ + "/ApplicationStatus/Attributes/WaitTime"; ++ ++ if ((publish_type == UCL_MQTT_PUBLISH_TYPE_DESIRED) ++ || (publish_type == UCL_MQTT_PUBLISH_TYPE_ALL)) { ++ std::string topic_desired = topic + "/Desired"; ++ uic_mqtt_publish(topic_desired.c_str(), NULL, 0, true); ++ } ++ if ((publish_type == UCL_MQTT_PUBLISH_TYPE_REPORTED) ++ || (publish_type == UCL_MQTT_PUBLISH_TYPE_ALL)) { ++ std::string topic_reported = topic + "/Reported"; ++ uic_mqtt_publish(topic_reported.c_str(), NULL, 0, true); ++ } ++ return SL_STATUS_OK; ++} ++// clang-format off ++ ++sl_status_t uic_mqtt_dotdot_application_status_reject_status_publish( ++ const char *base_topic, ++ bool value, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type ++) ++{ ++ nlohmann::json jsn; ++ ++ // This is a single value ++ ++ if (true == uic_dotdot_has_attribute_value_a_name(65442,2,value)) { ++ jsn["value"] = uic_dotdot_get_attribute_value_name(65442,2,value); ++ }else{ ++ jsn["value"] = value; ++ } ++ ++ ++ std::string payload_str; ++ try { ++ // Payload contains data from end nodes, which we cannot control, thus we handle if there are non-utf8 characters ++ payload_str = jsn.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); ++ } catch (const nlohmann::json::exception& e) { ++ sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "ApplicationStatus/Attributes/RejectStatus", e.what()); ++ return SL_STATUS_OK; ++ } ++ ++ ++ std::string topic = std::string(base_topic) + "/ApplicationStatus/Attributes/RejectStatus"; ++ if (publish_type & UCL_MQTT_PUBLISH_TYPE_DESIRED) ++ { ++ std::string topic_desired = topic + "/Desired"; ++ uic_mqtt_publish(topic_desired.c_str(), ++ payload_str.c_str(), ++ payload_str.length(), ++ true); ++ } ++ if (publish_type & UCL_MQTT_PUBLISH_TYPE_REPORTED) ++ { ++ std::string topic_reported = topic + "/Reported"; ++ uic_mqtt_publish(topic_reported.c_str(), ++ payload_str.c_str(), ++ payload_str.length(), ++ true); ++ } ++ return SL_STATUS_OK; ++} ++ ++sl_status_t uic_mqtt_dotdot_application_status_reject_status_unretain( ++ const char *base_topic, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type) ++{ ++ // clang-format on ++ std::string topic ++ = std::string(base_topic) ++ + "/ApplicationStatus/Attributes/RejectStatus"; ++ ++ if ((publish_type == UCL_MQTT_PUBLISH_TYPE_DESIRED) ++ || (publish_type == UCL_MQTT_PUBLISH_TYPE_ALL)) { ++ std::string topic_desired = topic + "/Desired"; ++ uic_mqtt_publish(topic_desired.c_str(), NULL, 0, true); ++ } ++ if ((publish_type == UCL_MQTT_PUBLISH_TYPE_REPORTED) ++ || (publish_type == UCL_MQTT_PUBLISH_TYPE_ALL)) { ++ std::string topic_reported = topic + "/Reported"; ++ uic_mqtt_publish(topic_reported.c_str(), NULL, 0, true); ++ } ++ return SL_STATUS_OK; ++} ++// clang-format off ++ ++ ++sl_status_t uic_mqtt_dotdot_application_status_init() ++{ ++ std::string base_topic = "ucl/by-unid/+/+/"; ++ ++ std::string subscription_topic; ++ if(!uic_mqtt_dotdot_application_status_write_attributes_callback.empty()) { ++ subscription_topic = base_topic + "ApplicationStatus/Commands/WriteAttributes"; ++ uic_mqtt_subscribe(subscription_topic.c_str(), uic_mqtt_dotdot_on_application_status_WriteAttributes); ++ } ++ ++ if(!uic_mqtt_dotdot_application_status_force_read_attributes_callback.empty()) { ++ subscription_topic = base_topic + "ApplicationStatus/Commands/ForceReadAttributes"; ++ uic_mqtt_subscribe(subscription_topic.c_str(), uic_mqtt_dotdot_on_application_status_force_read_attributes); ++ } ++ ++ // Init the attributes for that cluster ++ uic_mqtt_dotdot_application_status_attributes_init(); ++ ++ uic_mqtt_dotdot_by_group_application_status_init(); ++ ++ return SL_STATUS_OK; ++} ++ + + sl_status_t uic_mqtt_dotdot_init() { + +@@ -95767,6 +96174,10 @@ sl_status_t uic_mqtt_dotdot_init() { + status_flag = uic_mqtt_dotdot_unify_humidity_control_init(); + } + ++ if (status_flag == SL_STATUS_OK) { ++ status_flag = uic_mqtt_dotdot_application_status_init(); ++ } ++ + + return status_flag; + } +@@ -95829,6 +96240,7 @@ void uic_mqtt_dotdot_publish_supported_commands( + uic_mqtt_dotdot_unify_fan_control_publish_supported_commands(unid, endpoint_id); + uic_mqtt_dotdot_unify_thermostat_publish_supported_commands(unid, endpoint_id); + uic_mqtt_dotdot_unify_humidity_control_publish_supported_commands(unid, endpoint_id); ++ uic_mqtt_dotdot_application_status_publish_supported_commands(unid, endpoint_id); + } + + void uic_mqtt_dotdot_publish_empty_supported_commands( +@@ -95888,6 +96300,7 @@ void uic_mqtt_dotdot_publish_empty_supported_commands( + uic_mqtt_dotdot_unify_fan_control_publish_empty_supported_commands(unid, endpoint_id); + uic_mqtt_dotdot_unify_thermostat_publish_empty_supported_commands(unid, endpoint_id); + uic_mqtt_dotdot_unify_humidity_control_publish_empty_supported_commands(unid, endpoint_id); ++ uic_mqtt_dotdot_application_status_publish_empty_supported_commands(unid, endpoint_id); + } + + // Publishing Cluster Revision for Basic Cluster +@@ -109514,6 +109927,154 @@ void uic_mqtt_dotdot_unify_humidity_control_publish_empty_supported_commands( + } + } + ++// Publishing Cluster Revision for ApplicationStatus Cluster ++void uic_mqtt_dotdot_application_status_publish_cluster_revision(const char* base_topic, uint16_t value) ++{ ++ std::string cluster_topic = std::string(base_topic) + "/ApplicationStatus/Attributes/ClusterRevision"; ++ // Publish Desired ++ std::string pub_topic_des = cluster_topic + "/Desired"; ++ std::string payload = std::string(R"({"value": )") ++ + std::to_string(value) + std::string("}"); ++ uic_mqtt_publish(pub_topic_des.c_str(), ++ payload.c_str(), ++ payload.size(), ++ true); ++ // Publish Reported ++ std::string pub_topic_rep = cluster_topic + "/Reported"; ++ uic_mqtt_publish(pub_topic_rep.c_str(), ++ payload.c_str(), ++ payload.size(), ++ true); ++} ++ ++// Unretain Cluster Revision for ApplicationStatus Cluster ++void uic_mqtt_dotdot_application_status_unretain_cluster_revision(const char* base_topic) ++{ ++ // clang-format on ++ std::string cluster_topic ++ = std::string(base_topic) ++ + "/ApplicationStatus/Attributes/ClusterRevision"; ++ // Publish Desired ++ std::string desired_topic = cluster_topic + "/Desired"; ++ uic_mqtt_publish(desired_topic.c_str(), NULL, 0, true); ++ // Publish Reported ++ std::string reported_topic = cluster_topic + "/Reported"; ++ uic_mqtt_publish(reported_topic.c_str(), NULL, 0, true); ++ // clang-format off ++} ++ ++ ++static inline bool uic_mqtt_dotdot_application_status_write_attributes_is_supported( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id) ++{ ++ for (const auto& callback: uic_mqtt_dotdot_application_status_write_attributes_callback) { ++ uic_mqtt_dotdot_application_status_state_t application_status_new_state = {}; ++ uic_mqtt_dotdot_application_status_updated_state_t application_status_new_updated_state = {}; ++ ++ if (callback( ++ unid, ++ endpoint_id, ++ UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK, ++ application_status_new_state, ++ application_status_new_updated_state ++ ) == SL_STATUS_OK) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++static inline bool uic_mqtt_dotdot_application_status_force_read_attributes_is_supported( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id) ++{ ++ for (const auto& callback: uic_mqtt_dotdot_application_status_force_read_attributes_callback) { ++ uic_mqtt_dotdot_application_status_updated_state_t application_status_force_update = {0}; ++ if (callback( ++ unid, ++ endpoint_id, ++ UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK, ++ application_status_force_update ++ ) == SL_STATUS_OK) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++// Publishing Supported Commands for ApplicationStatus Cluster ++void uic_mqtt_dotdot_application_status_publish_supported_commands( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id) ++{ ++ std::stringstream ss; ++ bool first_command = true; ++ ss.str(""); ++ ++ // check if there is callback for each command ++ ++ // Check for a WriteAttributes Callback ++ if(uic_mqtt_dotdot_application_status_write_attributes_is_supported(unid, endpoint_id)) { ++ if (first_command == false) { ++ ss << ", "; ++ } ++ first_command = false; ++ ss << R"("WriteAttributes")"; ++ } ++ ++ // Check for a ForceReadAttributes Callback ++ if (uic_mqtt_dotdot_application_status_force_read_attributes_is_supported(unid, endpoint_id)) { ++ if (first_command == false) { ++ ss << ", "; ++ } ++ first_command = false; ++ ss << R"("ForceReadAttributes")"; ++ } ++ ++ // Publish supported commands ++ std::string topic = "ucl/by-unid/" + std::string(unid); ++ topic += "/ep"+ std::to_string(endpoint_id); ++ topic += "/ApplicationStatus/SupportedCommands"; ++ std::string payload_str("{\"value\": [" + ss.str() + "]" + "}"); ++ if (first_command == false) { ++ uic_mqtt_publish(topic.c_str(), ++ payload_str.c_str(), ++ payload_str.length(), ++ true); ++ } else if (uic_mqtt_count_topics(topic.c_str()) == 0) { ++ // There are no supported commands, but make sure we publish some ++ // SupportedCommands = [] if any attribute has been published for a cluster. ++ std::string attributes_topic = "ucl/by-unid/" + std::string(unid); ++ attributes_topic += "/ep"+ std::to_string(endpoint_id); ++ attributes_topic += "/ApplicationStatus/Attributes"; ++ ++ if (uic_mqtt_count_topics(attributes_topic.c_str()) > 0) { ++ uic_mqtt_publish(topic.c_str(), ++ EMPTY_VALUE_ARRAY, ++ strlen(EMPTY_VALUE_ARRAY), ++ true); ++ } ++ } ++} ++ ++// Publishing empty/no Supported Commands for ApplicationStatus Cluster ++void uic_mqtt_dotdot_application_status_publish_empty_supported_commands( ++ const dotdot_unid_t unid ++ , dotdot_endpoint_id_t endpoint_id) ++{ ++ std::string topic = "ucl/by-unid/" + std::string(unid); ++ topic += "/ep"+ std::to_string(endpoint_id); ++ topic += "/ApplicationStatus/SupportedCommands"; ++ ++ if (uic_mqtt_count_topics(topic.c_str()) > 0) { ++ uic_mqtt_publish(topic.c_str(), ++ EMPTY_VALUE_ARRAY, ++ strlen(EMPTY_VALUE_ARRAY), ++ true); ++ } ++} ++ + + //////////////////////////////////////////////////////////////////////////////// + // Generated Commands publications functions +diff --git a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.hpp b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.hpp +index 63ed3b0949..83095d21dd 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.hpp ++++ b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.hpp +@@ -371,6 +371,13 @@ sl_status_t uic_mqtt_dotdot_by_group_unify_thermostat_init(); + */ + sl_status_t uic_mqtt_dotdot_by_group_unify_humidity_control_init(); + ++/** ++ * @brief Initialize ApplicationStatus dotdot bygroup command handlers ++ * ++ * @returns SL_STATUS_OK on success, error otherwise. ++ */ ++sl_status_t uic_mqtt_dotdot_by_group_application_status_init(); ++ + + + // clang-format on +@@ -5115,6 +5122,27 @@ void uic_mqtt_dotdot_on_unify_humidity_control_WriteAttributes( + const size_t message_length); + + ++// clang-format on ++ ++/** ++ * @brief Retrieves the container with callback pointers for by-unid ++ * /Commands/WriteAttributes messages ++ * ++ * @returns std::set of callbacks. ++ */ ++std::set & get_uic_mqtt_dotdot_application_status_write_attributes_callback(); ++ ++/** ++ * @brief MQTT Subscribe handler for incoming publications on: ++ * ucl/by-unid/+/+/ApplicationStatus/Commands/WriteAttributes ++ */ ++// clang-format off ++void uic_mqtt_dotdot_on_application_status_WriteAttributes( ++ const char *topic, ++ const char *message, ++ const size_t message_length); ++ ++ + + + // All bitmaps are defined as the cluster label for the bitmap plus the command/attribute name +diff --git a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_attributes.cpp b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_attributes.cpp +index 51bb0cd1eb..da6cc64cbf 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_attributes.cpp ++++ b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_attributes.cpp +@@ -63640,3 +63640,293 @@ void uic_mqtt_dotdot_unify_humidity_control_attribute_auto_setpoint_precision_ca + + // End of supported cluster. + ++/////////////////////////////////////////////////////////////////////////////// ++// Callback pointers for ApplicationStatus ++/////////////////////////////////////////////////////////////////////////////// ++static uic_mqtt_dotdot_application_status_attribute_busy_status_callback_t uic_mqtt_dotdot_application_status_attribute_busy_status_callback = nullptr; ++static uic_mqtt_dotdot_application_status_attribute_wait_time_callback_t uic_mqtt_dotdot_application_status_attribute_wait_time_callback = nullptr; ++static uic_mqtt_dotdot_application_status_attribute_reject_status_callback_t uic_mqtt_dotdot_application_status_attribute_reject_status_callback = nullptr; ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Attribute update handlers for ApplicationStatus ++/////////////////////////////////////////////////////////////////////////////// ++static void uic_mqtt_dotdot_on_application_status_busy_status_attribute_update( ++ const char *topic, ++ const char *message, ++ const size_t message_length) { ++ if (uic_mqtt_dotdot_application_status_attribute_busy_status_callback == nullptr) { ++ return; ++ } ++ ++ std::string unid; ++ uint8_t endpoint = 0; // Default value for endpoint-less topics. ++ if(! uic_dotdot_mqtt::parse_topic(topic,unid,endpoint)) { ++ sl_log_debug(LOG_TAG, ++ "Error parsing UNID / Endpoint ID from topic %s. Ignoring", ++ topic); ++ return; ++ } ++ ++ std::string last_item; ++ if (SL_STATUS_OK != uic_dotdot_mqtt::get_topic_last_item(topic,last_item)){ ++ sl_log_debug(LOG_TAG, ++ "Error parsing last item from topic %s. Ignoring", ++ topic); ++ return; ++ } ++ ++ uic_mqtt_dotdot_attribute_update_type_t update_type; ++ if (last_item == "Reported") { ++ update_type = UCL_REPORTED_UPDATED; ++ } else if (last_item == "Desired") { ++ update_type = UCL_DESIRED_UPDATED; ++ } else { ++ sl_log_debug(LOG_TAG, ++ "Unknown value type (neither Desired/Reported) for topic %s. Ignoring", ++ topic); ++ return; ++ } ++ ++ // Empty message means unretained value. ++ bool unretained = false; ++ if (message_length == 0) { ++ unretained = true; ++ } ++ ++ ++ uint8_t busy_status = {}; ++ ++ nlohmann::json json_payload; ++ try { ++ ++ if (unretained == false) { ++ json_payload = nlohmann::json::parse(std::string(message)); ++ ++ if (json_payload.find("value") == json_payload.end()) { ++ sl_log_debug(LOG_TAG, "ApplicationStatus::BusyStatus: Missing attribute element: 'value'\n"); ++ return; ++ } ++// Start parsing value ++ uint32_t tmp = get_enum_decimal_value("value", json_payload); ++ if (tmp == numeric_limits::max()) { ++ #ifdef APPLICATION_STATUS_BUSY_STATUS_ENUM_NAME_AVAILABLE ++ tmp = application_status_busy_status_get_enum_value_number(json_payload.at("value").get()); ++ #elif defined(BUSY_STATUS_ENUM_NAME_AVAILABLE) ++ tmp = busy_status_get_enum_value_number(json_payload.at("value").get()); ++ #endif ++ } ++ busy_status = static_cast(tmp); ++ ++ // End parsing value ++ } ++ ++ } catch (const std::exception& e) { ++ sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "value", message); ++ return; ++ } ++ ++ uic_mqtt_dotdot_application_status_attribute_busy_status_callback( ++ static_cast(unid.c_str()), ++ endpoint, ++ unretained, ++ update_type, ++ busy_status ++ ); ++ ++} ++static void uic_mqtt_dotdot_on_application_status_wait_time_attribute_update( ++ const char *topic, ++ const char *message, ++ const size_t message_length) { ++ if (uic_mqtt_dotdot_application_status_attribute_wait_time_callback == nullptr) { ++ return; ++ } ++ ++ std::string unid; ++ uint8_t endpoint = 0; // Default value for endpoint-less topics. ++ if(! uic_dotdot_mqtt::parse_topic(topic,unid,endpoint)) { ++ sl_log_debug(LOG_TAG, ++ "Error parsing UNID / Endpoint ID from topic %s. Ignoring", ++ topic); ++ return; ++ } ++ ++ std::string last_item; ++ if (SL_STATUS_OK != uic_dotdot_mqtt::get_topic_last_item(topic,last_item)){ ++ sl_log_debug(LOG_TAG, ++ "Error parsing last item from topic %s. Ignoring", ++ topic); ++ return; ++ } ++ ++ uic_mqtt_dotdot_attribute_update_type_t update_type; ++ if (last_item == "Reported") { ++ update_type = UCL_REPORTED_UPDATED; ++ } else if (last_item == "Desired") { ++ update_type = UCL_DESIRED_UPDATED; ++ } else { ++ sl_log_debug(LOG_TAG, ++ "Unknown value type (neither Desired/Reported) for topic %s. Ignoring", ++ topic); ++ return; ++ } ++ ++ // Empty message means unretained value. ++ bool unretained = false; ++ if (message_length == 0) { ++ unretained = true; ++ } ++ ++ ++ uint8_t wait_time = {}; ++ ++ nlohmann::json json_payload; ++ try { ++ ++ if (unretained == false) { ++ json_payload = nlohmann::json::parse(std::string(message)); ++ ++ if (json_payload.find("value") == json_payload.end()) { ++ sl_log_debug(LOG_TAG, "ApplicationStatus::WaitTime: Missing attribute element: 'value'\n"); ++ return; ++ } ++// Start parsing value ++ wait_time = json_payload.at("value").get(); ++ ++ // End parsing value ++ } ++ ++ } catch (const std::exception& e) { ++ sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "value", message); ++ return; ++ } ++ ++ uic_mqtt_dotdot_application_status_attribute_wait_time_callback( ++ static_cast(unid.c_str()), ++ endpoint, ++ unretained, ++ update_type, ++ wait_time ++ ); ++ ++} ++static void uic_mqtt_dotdot_on_application_status_reject_status_attribute_update( ++ const char *topic, ++ const char *message, ++ const size_t message_length) { ++ if (uic_mqtt_dotdot_application_status_attribute_reject_status_callback == nullptr) { ++ return; ++ } ++ ++ std::string unid; ++ uint8_t endpoint = 0; // Default value for endpoint-less topics. ++ if(! uic_dotdot_mqtt::parse_topic(topic,unid,endpoint)) { ++ sl_log_debug(LOG_TAG, ++ "Error parsing UNID / Endpoint ID from topic %s. Ignoring", ++ topic); ++ return; ++ } ++ ++ std::string last_item; ++ if (SL_STATUS_OK != uic_dotdot_mqtt::get_topic_last_item(topic,last_item)){ ++ sl_log_debug(LOG_TAG, ++ "Error parsing last item from topic %s. Ignoring", ++ topic); ++ return; ++ } ++ ++ uic_mqtt_dotdot_attribute_update_type_t update_type; ++ if (last_item == "Reported") { ++ update_type = UCL_REPORTED_UPDATED; ++ } else if (last_item == "Desired") { ++ update_type = UCL_DESIRED_UPDATED; ++ } else { ++ sl_log_debug(LOG_TAG, ++ "Unknown value type (neither Desired/Reported) for topic %s. Ignoring", ++ topic); ++ return; ++ } ++ ++ // Empty message means unretained value. ++ bool unretained = false; ++ if (message_length == 0) { ++ unretained = true; ++ } ++ ++ ++ bool reject_status = {}; ++ ++ nlohmann::json json_payload; ++ try { ++ ++ if (unretained == false) { ++ json_payload = nlohmann::json::parse(std::string(message)); ++ ++ if (json_payload.find("value") == json_payload.end()) { ++ sl_log_debug(LOG_TAG, "ApplicationStatus::RejectStatus: Missing attribute element: 'value'\n"); ++ return; ++ } ++// Start parsing value ++ reject_status = get_bool_from_json(json_payload, "value"); ++ ++ // End parsing value ++ } ++ ++ } catch (const std::exception& e) { ++ sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "value", message); ++ return; ++ } ++ ++ uic_mqtt_dotdot_application_status_attribute_reject_status_callback( ++ static_cast(unid.c_str()), ++ endpoint, ++ unretained, ++ update_type, ++ reject_status ++ ); ++ ++} ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Attribute init functions for ApplicationStatus ++/////////////////////////////////////////////////////////////////////////////// ++sl_status_t uic_mqtt_dotdot_application_status_attributes_init() ++{ ++ std::string base_topic = "ucl/by-unid/+/+/"; ++ ++ std::string subscription_topic; ++ if(uic_mqtt_dotdot_application_status_attribute_busy_status_callback) { ++ subscription_topic = base_topic + "ApplicationStatus/Attributes/BusyStatus/#"; ++ uic_mqtt_subscribe(subscription_topic.c_str(), &uic_mqtt_dotdot_on_application_status_busy_status_attribute_update); ++ } ++ if(uic_mqtt_dotdot_application_status_attribute_wait_time_callback) { ++ subscription_topic = base_topic + "ApplicationStatus/Attributes/WaitTime/#"; ++ uic_mqtt_subscribe(subscription_topic.c_str(), &uic_mqtt_dotdot_on_application_status_wait_time_attribute_update); ++ } ++ if(uic_mqtt_dotdot_application_status_attribute_reject_status_callback) { ++ subscription_topic = base_topic + "ApplicationStatus/Attributes/RejectStatus/#"; ++ uic_mqtt_subscribe(subscription_topic.c_str(), &uic_mqtt_dotdot_on_application_status_reject_status_attribute_update); ++ } ++ ++ return SL_STATUS_OK; ++} ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Callback setters and getters for ApplicationStatus ++/////////////////////////////////////////////////////////////////////////////// ++void uic_mqtt_dotdot_application_status_attribute_busy_status_callback_set(const uic_mqtt_dotdot_application_status_attribute_busy_status_callback_t callback) ++{ ++ uic_mqtt_dotdot_application_status_attribute_busy_status_callback = callback; ++} ++void uic_mqtt_dotdot_application_status_attribute_wait_time_callback_set(const uic_mqtt_dotdot_application_status_attribute_wait_time_callback_t callback) ++{ ++ uic_mqtt_dotdot_application_status_attribute_wait_time_callback = callback; ++} ++void uic_mqtt_dotdot_application_status_attribute_reject_status_callback_set(const uic_mqtt_dotdot_application_status_attribute_reject_status_callback_t callback) ++{ ++ uic_mqtt_dotdot_application_status_attribute_reject_status_callback = callback; ++} ++ ++// End of supported cluster. ++ +diff --git a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_command_helpers.cpp b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_command_helpers.cpp +index a738479fb0..81b8c06095 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_command_helpers.cpp ++++ b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_command_helpers.cpp +@@ -14664,5 +14664,21 @@ void uic_mqtt_dotdot_parse_unify_humidity_control_write_attributes( + + + ++} ++ ++ ++/** ++ * @brief JSON parser for ::WriteAttributes command arguments. ++ * ++ * Parse incoming JSON object to populate command arguments passed in by reference. ++ */ ++void uic_mqtt_dotdot_parse_application_status_write_attributes( ++ nlohmann::json &jsn, ++ uic_mqtt_dotdot_application_status_state_t &new_state, ++ uic_mqtt_dotdot_application_status_updated_state_t &new_updated_state ++) { ++ ++ ++ + } + +diff --git a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_command_helpers.hpp b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_command_helpers.hpp +index 6f34c390dc..bce1539a65 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_command_helpers.hpp ++++ b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_command_helpers.hpp +@@ -6091,6 +6091,18 @@ void uic_mqtt_dotdot_parse_unify_humidity_control_write_attributes( + ); + + ++/** ++ * @brief JSON parser for ApplicationStatus WriteAttributes command arguments. ++ * ++ * Parse incoming JSON object to populate command arguments passed in by reference. ++ */ ++void uic_mqtt_dotdot_parse_application_status_write_attributes( ++ nlohmann::json &jsn, ++ uic_mqtt_dotdot_application_status_state_t &new_state, ++ uic_mqtt_dotdot_application_status_updated_state_t &new_updated_state ++); ++ ++ + + #endif //DOTDOT_MQTT_COMMAND_HELPERS_HPP + /** @} end dotdot_mqtt_command_helpers */ +diff --git a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_generated_commands.cpp b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_generated_commands.cpp +index 6faf125b5f..e6cb0037cd 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_generated_commands.cpp ++++ b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_generated_commands.cpp +@@ -11307,3 +11307,43 @@ void uic_mqtt_dotdot_unify_humidity_control_publish_generated_write_attributes_c + false); + } + ++ ++ ++/** ++ * @brief Publishes an incoming/generated WriteAttributes command for ++ * the ApplicationStatus cluster. ++ * ++ * Publication will be made at the following topic ++ * ucl/by-unid/UNID/epID/ApplicationStatus/GeneratedCommands/WriteAttributes ++ * ++ * @param unid The UNID of the node that sent us the command. ++ * ++ * @param endpoint The Endpoint ID of the node that sent us the command. ++ * ++ * @param attribute_values Values to assign to the attributes ++ * @param attribute_list List of attributes that are written ++ */ ++void uic_mqtt_dotdot_application_status_publish_generated_write_attributes_command( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint, ++ uic_mqtt_dotdot_application_status_state_t attribute_values, ++ uic_mqtt_dotdot_application_status_updated_state_t attribute_list ++){ ++ // Create the topic ++ std::string topic = "ucl/by-unid/"+ std::string(unid) + "/ep" + ++ std::to_string(endpoint) + "/"; ++ topic += "ApplicationStatus/GeneratedCommands/WriteAttributes"; ++ ++ nlohmann::json json_object = nlohmann::json::object(); ++ ++ ++ // Payload contains data from end nodes, which we cannot control, thus we handle if there are non-utf8 characters ++ std::string payload = json_object.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); ++ ++ // Publish our command ++ uic_mqtt_publish(topic.c_str(), ++ payload.c_str(), ++ payload.size(), ++ false); ++} ++ +diff --git a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_group_commands.cpp b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_group_commands.cpp +index 7c2fca7faf..350dd75b8a 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_group_commands.cpp ++++ b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_group_commands.cpp +@@ -370,6 +370,9 @@ static uic_mqtt_dotdot_by_group_unify_humidity_control_setpoint_set_callback_t u + static uic_mqtt_dotdot_by_group_unify_humidity_control_write_attributes_callback_t uic_mqtt_dotdot_by_group_unify_humidity_control_write_attributes_callback = nullptr; + + ++static uic_mqtt_dotdot_by_group_application_status_write_attributes_callback_t uic_mqtt_dotdot_by_group_application_status_write_attributes_callback = nullptr; ++ ++ + + // Callbacks setters + +@@ -1895,6 +1898,15 @@ void uic_mqtt_dotdot_by_group_unify_humidity_control_write_attributes_callback_s + + + ++// Callbacks setters ++void uic_mqtt_dotdot_by_group_application_status_write_attributes_callback_set( ++ const uic_mqtt_dotdot_by_group_application_status_write_attributes_callback_t callback) ++{ ++ uic_mqtt_dotdot_by_group_application_status_write_attributes_callback = callback; ++} ++ ++ ++ + + // Callback function for incoming publications on ucl/by-group/+/Basic/Commands/ResetToFactoryDefaults + static void uic_mqtt_dotdot_on_by_group_basic_reset_to_factory_defaults( +@@ -23610,6 +23622,91 @@ sl_status_t uic_mqtt_dotdot_by_group_unify_humidity_control_init() + + + ++ ++static void uic_mqtt_dotdot_on_by_group_application_status_WriteAttributes( ++ const char *topic, ++ const char *message, ++ const size_t message_length) ++{ ++ ++ if ((group_dispatch_callback == nullptr) && (uic_mqtt_dotdot_by_group_application_status_write_attributes_callback == nullptr)) { ++ return; ++ } ++ if (message_length == 0) { ++ return; ++ } ++ ++ dotdot_group_id_t group_id = 0U; ++ if(!uic_dotdot_mqtt::parse_topic_group_id(topic,group_id)) { ++ sl_log_debug(LOG_TAG, ++ "Failed to parse GroupId from topic %s. Ignoring", ++ topic); ++ return; ++ } ++ ++ if ((group_dispatch_callback != nullptr) && (!get_uic_mqtt_dotdot_application_status_write_attributes_callback().empty())) { ++ try { ++ group_dispatch_callback(group_id, ++ "ApplicationStatus", ++ "WriteAttributes", ++ message, ++ message_length, ++ uic_mqtt_dotdot_on_application_status_WriteAttributes); ++ ++ } catch (...) { ++ sl_log_debug(LOG_TAG, "ApplicationStatus: Unable to parse JSON payload.\n"); ++ return; ++ } ++ } else if (uic_mqtt_dotdot_by_group_application_status_write_attributes_callback != nullptr) { ++ ++ uic_mqtt_dotdot_application_status_state_t new_state = {}; ++ uic_mqtt_dotdot_application_status_updated_state_t new_updated_state = {}; ++ ++ ++ nlohmann::json jsn; ++ try { ++ jsn = nlohmann::json::parse(std::string(message)); ++ ++ uic_mqtt_dotdot_parse_application_status_write_attributes( ++ jsn, ++ new_state, ++ new_updated_state ++ ); ++ } catch (const nlohmann::json::parse_error& e) { ++ // Catch JSON object field parsing errors ++ sl_log_debug(LOG_TAG, LOG_FMT_JSON_PARSE_FAIL, "ApplicationStatus", "WriteAttributes"); ++ return; ++ } catch (const nlohmann::json::exception& e) { ++ // Catch JSON object field parsing errors ++ sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "ApplicationStatus", "WriteAttributes", e.what()); ++ return; ++ } catch (const std::exception& e) { ++ sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "ApplicationStatus", "WriteAttributes", ""); ++ return; ++ } ++ ++ uic_mqtt_dotdot_by_group_application_status_write_attributes_callback( ++ group_id, ++ new_state, ++ new_updated_state ++ ); ++ } ++} ++ ++sl_status_t uic_mqtt_dotdot_by_group_application_status_init() ++{ ++ std::string subscription_topic; ++ const std::string topic_bygroup = TOPIC_BY_GROUP_PREFIX; ++ if(uic_mqtt_dotdot_by_group_application_status_write_attributes_callback) { ++ subscription_topic = topic_bygroup + "ApplicationStatus/Commands/WriteAttributes"; ++ uic_mqtt_subscribe(subscription_topic.c_str(), uic_mqtt_dotdot_on_by_group_application_status_WriteAttributes); ++ } ++ ++ return SL_STATUS_OK; ++} ++ ++ ++ + void uic_mqtt_dotdot_set_group_dispatch_callback(group_dispatch_t callback) + { + // Check for uninitialized value in order to subscribe with on_group handlers +@@ -23894,6 +23991,8 @@ void uic_mqtt_dotdot_set_group_dispatch_callback(group_dispatch_t callback) + uic_mqtt_subscribe("ucl/by-group/+/UnifyHumidityControl/Commands/ModeSet", uic_mqtt_dotdot_on_by_group_unify_humidity_control_mode_set); + uic_mqtt_subscribe("ucl/by-group/+/UnifyHumidityControl/Commands/SetpointSet", uic_mqtt_dotdot_on_by_group_unify_humidity_control_setpoint_set); + ++ uic_mqtt_subscribe("ucl/by-group/+/ApplicationStatus/Commands/WriteAttributes", uic_mqtt_dotdot_on_by_group_application_status_WriteAttributes); ++ + } + + group_dispatch_callback = callback; +diff --git a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_helpers.cpp b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_helpers.cpp +index c1531c376c..4ff5c95f8f 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_helpers.cpp ++++ b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_helpers.cpp +@@ -208,6 +208,45 @@ uint32_t aox_locator_reporting_mode_get_enum_value_number(const std::string &str + return std::numeric_limits::max(); + } + ++// Enum to string map for ApplicationStatusBusyStatus ++const std::map application_status_busy_status_enum_id_to_string_map { ++ { 0, "Try again later" }, ++ { 1, "Try again in WaitTime seconds" }, ++ { 2, "Request queued" }, ++}; ++ ++// String to enum map for ApplicationStatusBusyStatus ++const std::map application_status_busy_status_enum_string_to_id_map { ++ { "Try again later", 0 }, ++ { "Try again in WaitTime seconds", 1 }, ++ { "Request queued", 2 }, ++}; ++ ++std::string application_status_busy_status_get_enum_value_name( ++ uint32_t value) ++{ ++ auto it = application_status_busy_status_enum_id_to_string_map.find(value); ++ if (it != application_status_busy_status_enum_id_to_string_map.end()){ ++ return it->second; ++ } ++ ++ // No known name value is set for this field. ++ // Set it to a string version of the value. ++ return std::to_string(value); ++} ++ ++uint32_t application_status_busy_status_get_enum_value_number(const std::string &str) ++{ ++ auto it = application_status_busy_status_enum_string_to_id_map.find(str); ++ if (it != application_status_busy_status_enum_string_to_id_map.end()){ ++ return it->second; ++ } ++ ++ // No known numeric value is set for this string. ++ // Return UINT32_MAX to indicate an error. ++ return std::numeric_limits::max(); ++} ++ + // Enum to string map for ArmArmMode + const std::map arm_arm_mode_enum_id_to_string_map { + { 0, "Disarm" }, +@@ -10370,6 +10409,27 @@ std::string get_enum_value_name( + #endif + } + ++ if (65442 == cluster_id) { ++ #ifdef APPLICATION_STATUS_BUSY_STATUS_ENUM_NAME_AVAILABLE ++ if (0 == attribute_id) { ++ // FIXME: Some attributes don't work because multi-upper case names end up like this: application_statusbusy_status instead of this: application_status_busy_status ++ return application_status_busy_status_get_enum_value_name(value); ++ } ++ #endif ++ #ifdef APPLICATION_STATUS_WAIT_TIME_ENUM_NAME_AVAILABLE ++ if (1 == attribute_id) { ++ // FIXME: Some attributes don't work because multi-upper case names end up like this: application_statuswait_time instead of this: application_status_wait_time ++ return application_status_wait_time_get_enum_value_name(value); ++ } ++ #endif ++ #ifdef APPLICATION_STATUS_REJECT_STATUS_ENUM_NAME_AVAILABLE ++ if (2 == attribute_id) { ++ // FIXME: Some attributes don't work because multi-upper case names end up like this: application_statusreject_status instead of this: application_status_reject_status ++ return application_status_reject_status_get_enum_value_name(value); ++ } ++ #endif ++ } ++ + + std::string value_name; + return value_name; +@@ -14877,6 +14937,27 @@ uint32_t get_enum_name_value( + #endif + } + ++ if (65442 == cluster_id) { ++ #ifdef APPLICATION_STATUS_BUSY_STATUS_ENUM_NAME_AVAILABLE ++ if (0 == attribute_id) { ++ // FIXME: Some attributes don't work because multi-upper case names end up like this: application_statusbusy_status instead of this: application_status_busy_status ++ return application_status_busy_status_get_enum_value_number(name); ++ } ++ #endif ++ #ifdef APPLICATION_STATUS_WAIT_TIME_ENUM_NAME_AVAILABLE ++ if (1 == attribute_id) { ++ // FIXME: Some attributes don't work because multi-upper case names end up like this: application_statuswait_time instead of this: application_status_wait_time ++ return application_status_wait_time_get_enum_value_number(name); ++ } ++ #endif ++ #ifdef APPLICATION_STATUS_REJECT_STATUS_ENUM_NAME_AVAILABLE ++ if (2 == attribute_id) { ++ // FIXME: Some attributes don't work because multi-upper case names end up like this: application_statusreject_status instead of this: application_status_reject_status ++ return application_status_reject_status_get_enum_value_number(name); ++ } ++ #endif ++ } ++ + + // No known numeric value is set for this string. + // Return UINT32_MAX to indicate an error. +@@ -14929,6 +15010,17 @@ uint32_t aox_locator_reporting_mode_get_enum_value_number_c(const char *str) + { + return aox_locator_reporting_mode_get_enum_value_number(std::string(str)); + } ++char *application_status_busy_status_get_enum_value_name_c( ++ uint32_t value, char *result, size_t max_result_size) ++{ ++ snprintf(result, max_result_size, "%s", application_status_busy_status_get_enum_value_name(value).c_str()); ++ return result; ++} ++ ++uint32_t application_status_busy_status_get_enum_value_number_c(const char *str) ++{ ++ return application_status_busy_status_get_enum_value_number(std::string(str)); ++} + char *arm_arm_mode_get_enum_value_name_c( + uint32_t value, char *result, size_t max_result_size) + { +diff --git a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_supported_generated_commands.cpp b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_supported_generated_commands.cpp +index 4d6e0f16c3..f5d99d9d89 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_supported_generated_commands.cpp ++++ b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_supported_generated_commands.cpp +@@ -2861,3 +2861,46 @@ void uic_mqtt_dotdot_unify_humidity_control_publish_supported_generated_commands + + } + ++ ++/** ++ * @brief Sends/Publishes a the SupportedGenerated commands for ++ * the ApplicationStatus cluster for a UNID/Endpoint ++ * ++ * Publication will be made at the following topic ++ * ucl/by-unid/UNID/epID/ApplicationStatus/SupportedGeneratedCommands ++ * ++ * @param unid The UNID of the node on behalf of which the advertisment is made ++ * ++ * @param endpoint The Endpoint ID of the node on behalf of which the advertisment is made ++ * ++ * @param command_list Struct pointer with the fields value indicating if ++ * individual commands can be generated. ++ */ ++void uic_mqtt_dotdot_application_status_publish_supported_generated_commands( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint, ++ const uic_mqtt_dotdot_application_status_supported_commands_t *command_list) ++{ ++ std::string topic = "ucl/by-unid/" + std::string(unid); ++ topic += "/ep"+ std::to_string(endpoint); ++ topic += "/ApplicationStatus/SupportedGeneratedCommands"; ++ ++ // Assemble of vector of strings for the Supported Commands: ++ std::vector command_vector; ++ if (command_list->write_attributes == true) { ++ command_vector.emplace_back("WriteAttributes"); ++ } ++ ++ // JSONify, then Stringify ++ nlohmann::json json_payload; ++ json_payload["value"] = command_vector; ++ std::string string_payload = json_payload.dump(); ++ ++ // Publish to MQTT ++ uic_mqtt_publish(topic.c_str(), ++ string_payload.c_str(), ++ string_payload.length(), ++ true); ++ ++} ++ +diff --git a/components/uic_dotdot_mqtt/zap-generated/test/dotdot_mqtt_test.include b/components/uic_dotdot_mqtt/zap-generated/test/dotdot_mqtt_test.include +index f3ec21da18..d86254dcc1 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/test/dotdot_mqtt_test.include ++++ b/components/uic_dotdot_mqtt/zap-generated/test/dotdot_mqtt_test.include +@@ -3663,6 +3663,7 @@ static void unset_all_callbacks() + uic_mqtt_dotdot_unify_humidity_control_mode_set_callback_clear(); + uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback_clear(); + uic_mqtt_dotdot_clear_unify_humidity_control_write_attributes_callbacks(); ++ uic_mqtt_dotdot_clear_application_status_write_attributes_callbacks(); + } + + static void reset_callback_counters() +diff --git a/components/unify_dotdot_attribute_store/zap-generated/include/dotdot_attributes.uam b/components/unify_dotdot_attribute_store/zap-generated/include/dotdot_attributes.uam +index 2bf45a75f1..590fef83c8 100644 +--- a/components/unify_dotdot_attribute_store/zap-generated/include/dotdot_attributes.uam ++++ b/components/unify_dotdot_attribute_store/zap-generated/include/dotdot_attributes.uam +@@ -894,3 +894,8 @@ def DOTDOT_ATTRIBUTE_ID_UNIFY_HUMIDITY_CONTROL_AUTO_SETPOINT 0xfda00011 + def DOTDOT_ATTRIBUTE_ID_UNIFY_HUMIDITY_CONTROL_AUTO_SETPOINT_SCALE 0xfda00012 + def DOTDOT_ATTRIBUTE_ID_UNIFY_HUMIDITY_CONTROL_AUTO_SETPOINT_PRECISION 0xfda00013 + ++// This represents the attributes in the DotDot ApplicationStatus cluster ++def DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS 0xffa20000 ++def DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME 0xffa20001 ++def DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS 0xffa20002 ++ +diff --git a/components/unify_dotdot_attribute_store/zap-generated/include/dotdot_attributes_camel_case.uam b/components/unify_dotdot_attribute_store/zap-generated/include/dotdot_attributes_camel_case.uam +index ceb159d2c2..86c4dcd817 100644 +--- a/components/unify_dotdot_attribute_store/zap-generated/include/dotdot_attributes_camel_case.uam ++++ b/components/unify_dotdot_attribute_store/zap-generated/include/dotdot_attributes_camel_case.uam +@@ -894,3 +894,8 @@ def zb_AutoSetpoint 0xfda00011 + def zb_AutoSetpointScale 0xfda00012 + def zb_AutoSetpointPrecision 0xfda00013 + ++// This represents short CamelCase labels the attributes in the DotDot ApplicationStatus cluster ++def zb_BusyStatus 0xffa20000 ++def zb_WaitTime 0xffa20001 ++def zb_RejectStatus 0xffa20002 ++ +diff --git a/components/unify_dotdot_attribute_store/zap-generated/include/unify_dotdot_attribute_store_helpers.h b/components/unify_dotdot_attribute_store/zap-generated/include/unify_dotdot_attribute_store_helpers.h +index f99ee31747..37fc4853cd 100644 +--- a/components/unify_dotdot_attribute_store/zap-generated/include/unify_dotdot_attribute_store_helpers.h ++++ b/components/unify_dotdot_attribute_store/zap-generated/include/unify_dotdot_attribute_store_helpers.h +@@ -78298,6 +78298,352 @@ bool dotdot_is_any_unify_humidity_control_writable_attribute_supported( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint_id); + ++//////////////////////////////////////////////////////////////////////////////// ++// Start of cluster ApplicationStatus ++//////////////////////////////////////////////////////////////////////////////// ++// ApplicationStatus BusyStatus ++/** ++ * @brief Verifies if the DotDot ApplicationStatus - BusyStatus is supported ++ * under a UNID/EndpoinID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * ++ * @returns true if BusyStatus is supported ++ * @returns false if BusyStatus is not supported ++ */ ++bool dotdot_is_supported_application_status_busy_status ( ++ const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Gets the DotDot ApplicationStatus - BusyStatus attribute value under a UNID/EndpoinID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @param value_state value state to get, ++ * see \ref attribute_store_get_node_attribute_value ++ * ++ * ++ * @returns BusyStatus attribute ++ */ ++uint8_t dotdot_get_application_status_busy_status( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state); ++ ++/** ++ * @brief Set the DotDot ApplicationStatus - BusyStatus attribute under a UNID/EndpoinID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @param value_state value state to write for the node, ++ * see \ref attribute_store_set_node_attribute_value ++ * ++ * @param new_busy_status new value to set ++ * @returns sl_status_t SL_STATUS_OK on success ++ */ ++sl_status_t dotdot_set_application_status_busy_status( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state, ++ uint8_t new_busy_status ++ ); ++ ++/** ++ * @brief Undefines the Reported value of the the DotDot ApplicationStatus - BusyStatus ++ * attribute under a UNID/EndpoinID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @returns sl_status_t SL_STATUS_OK on success ++ */ ++sl_status_t dotdot_application_status_busy_status_undefine_reported( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Undefines the Desired value of the DotDot ++ * ApplicationStatus - BusyStatus attribute under a UNID/EndpointID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @returns sl_status_t SL_STATUS_OK on success ++ */ ++sl_status_t dotdot_application_status_busy_status_undefine_desired( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Checks if the reported value is defined for the DotDot ++ * ApplicationStatus - BusyStatus attribute under a UNID/EndpointID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @returns true if defined, false is undefined or non-existent ++ */ ++bool dotdot_application_status_busy_status_is_reported_defined( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Checks if the desired value is defined for the DotDot ++ * ApplicationStatus - BusyStatus attribute under a UNID/EndpointID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @returns true if defined, false is undefined or non-existent ++ */ ++bool dotdot_application_status_busy_status_is_desired_defined( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Creates a DotDot ApplicationStatus - BusyStatus attribute under a UNID/EndpoinID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @returns sl_status_t SL_STATUS_OK on success ++ */ ++sl_status_t dotdot_create_application_status_busy_status( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++// ApplicationStatus WaitTime ++/** ++ * @brief Verifies if the DotDot ApplicationStatus - WaitTime is supported ++ * under a UNID/EndpoinID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * ++ * @returns true if WaitTime is supported ++ * @returns false if WaitTime is not supported ++ */ ++bool dotdot_is_supported_application_status_wait_time ( ++ const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Gets the DotDot ApplicationStatus - WaitTime attribute value under a UNID/EndpoinID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @param value_state value state to get, ++ * see \ref attribute_store_get_node_attribute_value ++ * ++ * ++ * @returns WaitTime attribute ++ */ ++uint8_t dotdot_get_application_status_wait_time( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state); ++ ++/** ++ * @brief Set the DotDot ApplicationStatus - WaitTime attribute under a UNID/EndpoinID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @param value_state value state to write for the node, ++ * see \ref attribute_store_set_node_attribute_value ++ * ++ * @param new_wait_time new value to set ++ * @returns sl_status_t SL_STATUS_OK on success ++ */ ++sl_status_t dotdot_set_application_status_wait_time( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state, ++ uint8_t new_wait_time ++ ); ++ ++/** ++ * @brief Undefines the Reported value of the the DotDot ApplicationStatus - WaitTime ++ * attribute under a UNID/EndpoinID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @returns sl_status_t SL_STATUS_OK on success ++ */ ++sl_status_t dotdot_application_status_wait_time_undefine_reported( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Undefines the Desired value of the DotDot ++ * ApplicationStatus - WaitTime attribute under a UNID/EndpointID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @returns sl_status_t SL_STATUS_OK on success ++ */ ++sl_status_t dotdot_application_status_wait_time_undefine_desired( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Checks if the reported value is defined for the DotDot ++ * ApplicationStatus - WaitTime attribute under a UNID/EndpointID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @returns true if defined, false is undefined or non-existent ++ */ ++bool dotdot_application_status_wait_time_is_reported_defined( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Checks if the desired value is defined for the DotDot ++ * ApplicationStatus - WaitTime attribute under a UNID/EndpointID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @returns true if defined, false is undefined or non-existent ++ */ ++bool dotdot_application_status_wait_time_is_desired_defined( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Creates a DotDot ApplicationStatus - WaitTime attribute under a UNID/EndpoinID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @returns sl_status_t SL_STATUS_OK on success ++ */ ++sl_status_t dotdot_create_application_status_wait_time( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++// ApplicationStatus RejectStatus ++/** ++ * @brief Verifies if the DotDot ApplicationStatus - RejectStatus is supported ++ * under a UNID/EndpoinID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * ++ * @returns true if RejectStatus is supported ++ * @returns false if RejectStatus is not supported ++ */ ++bool dotdot_is_supported_application_status_reject_status ( ++ const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Gets the DotDot ApplicationStatus - RejectStatus attribute value under a UNID/EndpoinID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @param value_state value state to get, ++ * see \ref attribute_store_get_node_attribute_value ++ * ++ * ++ * @returns RejectStatus attribute ++ */ ++bool dotdot_get_application_status_reject_status( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state); ++ ++/** ++ * @brief Set the DotDot ApplicationStatus - RejectStatus attribute under a UNID/EndpoinID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @param value_state value state to write for the node, ++ * see \ref attribute_store_set_node_attribute_value ++ * ++ * @param new_reject_status new value to set ++ * @returns sl_status_t SL_STATUS_OK on success ++ */ ++sl_status_t dotdot_set_application_status_reject_status( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state, ++ bool new_reject_status ++ ); ++ ++/** ++ * @brief Undefines the Reported value of the the DotDot ApplicationStatus - RejectStatus ++ * attribute under a UNID/EndpoinID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @returns sl_status_t SL_STATUS_OK on success ++ */ ++sl_status_t dotdot_application_status_reject_status_undefine_reported( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Undefines the Desired value of the DotDot ++ * ApplicationStatus - RejectStatus attribute under a UNID/EndpointID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @returns sl_status_t SL_STATUS_OK on success ++ */ ++sl_status_t dotdot_application_status_reject_status_undefine_desired( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Checks if the reported value is defined for the DotDot ++ * ApplicationStatus - RejectStatus attribute under a UNID/EndpointID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @returns true if defined, false is undefined or non-existent ++ */ ++bool dotdot_application_status_reject_status_is_reported_defined( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Checks if the desired value is defined for the DotDot ++ * ApplicationStatus - RejectStatus attribute under a UNID/EndpointID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @returns true if defined, false is undefined or non-existent ++ */ ++bool dotdot_application_status_reject_status_is_desired_defined( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Creates a DotDot ApplicationStatus - RejectStatus attribute under a UNID/EndpoinID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @returns sl_status_t SL_STATUS_OK on success ++ */ ++sl_status_t dotdot_create_application_status_reject_status( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Checks if a UNID/Endpoint supports any attribute for the ApplicationStatus ++ * Cluster ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @returns true if at least 1 attribute in the Attribute Store, false otherwise ++ */ ++bool dotdot_is_any_application_status_attribute_supported( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Checks if a UNID/Endpoint supports any writable attribute for the ++ * ApplicationStatus Cluster ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * @returns true if at least 1 writable attribute in the Attribute Store, false otherwise ++ */ ++bool dotdot_is_any_application_status_writable_attribute_supported( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ + #ifdef __cplusplus + } + #endif // __cplusplus +diff --git a/components/unify_dotdot_attribute_store/zap-generated/include/unify_dotdot_defined_attribute_types.h b/components/unify_dotdot_attribute_store/zap-generated/include/unify_dotdot_defined_attribute_types.h +index 46668d1948..5b6cfc000e 100644 +--- a/components/unify_dotdot_attribute_store/zap-generated/include/unify_dotdot_defined_attribute_types.h ++++ b/components/unify_dotdot_attribute_store/zap-generated/include/unify_dotdot_defined_attribute_types.h +@@ -801,6 +801,10 @@ DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_UNIFY_HUMIDITY_CONTROL_AUTO_SETPOINT_MAX , + DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_UNIFY_HUMIDITY_CONTROL_AUTO_SETPOINT , 0xfda00011) + DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_UNIFY_HUMIDITY_CONTROL_AUTO_SETPOINT_SCALE , 0xfda00012) + DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_UNIFY_HUMIDITY_CONTROL_AUTO_SETPOINT_PRECISION , 0xfda00013) ++// Attribute Defines for ApplicationStatus ++DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS , 0xffa20000) ++DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME , 0xffa20001) ++DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS , 0xffa20002) + + // Additional manually defined types: + +diff --git a/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_attribute_publisher.cpp b/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_attribute_publisher.cpp +index 8446b11a47..16c8c67c24 100644 +--- a/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_attribute_publisher.cpp ++++ b/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_attribute_publisher.cpp +@@ -26006,6 +26006,265 @@ static void unify_humidity_control_cluster_cluster_revision_callback( + } + + ++/** ++ * @brief Publishes the desired value of an updated attribute store node for ++ * the ApplicationStatus cluster. ++ * @param updated_node Updated attribute store node ++ * @param change Type of change applied ++ */ ++static void application_status_cluster_publish_desired_value_callback( ++ attribute_store_node_t updated_node, attribute_store_change_t change) ++{ ++ // clang-format on ++ if (false == is_publish_desired_attribute_values_to_mqtt_enabled()) { ++ return; ++ } ++ if (change == ATTRIBUTE_DELETED || change == ATTRIBUTE_CREATED) { ++ return; ++ } ++ // Scene exception: check that the attribute is not under the Scene Table extension, which is a config and not the node's state. ++ if (ATTRIBUTE_STORE_INVALID_NODE ++ != attribute_store_get_first_parent_with_type( ++ updated_node, ++ DOTDOT_ATTRIBUTE_ID_SCENES_SCENE_TABLE)) { ++ return; ++ } ++ ++ // Get the UNID and EndPoint, and prepare the basic topic ++ char unid[MAXIMUM_UNID_SIZE] = {}; ++ // clang-format off ++ // clang-format on ++ dotdot_endpoint_id_t endpoint_id = 0; ++ if (SL_STATUS_OK ++ != unify_dotdot_attributes_get_unid_endpoint()(updated_node, ++ unid, ++ &endpoint_id)) { ++ return; ++ } ++ // clang-format off ++ // clang-format on ++ ++ std::string base_topic = "ucl/by-unid/" + std::string(unid); ++ // clang-format off ++ base_topic += "/ep" + std::to_string(endpoint_id); ++ // clang-format on ++ ++ attribute_store_type_t type = attribute_store_get_node_type(updated_node); ++ if (type == ATTRIBUTE_STORE_INVALID_ATTRIBUTE_TYPE) { ++ sl_log_debug(LOG_TAG, ++ "Warning: Invalid type for Attribute ID %d, " ++ "this should not happen.", ++ updated_node); ++ return; ++ } ++ ++ // If the value got updated but both Reported and Desired undefined, we skip publication ++ if (false == attribute_store_is_reported_defined(updated_node) ++ && false == attribute_store_is_desired_defined(updated_node)) { ++ sl_log_debug(LOG_TAG, ++ "Reported/Desired values are undefined. " ++ "Skipping publication"); ++ return; ++ } ++ ++ // clang-format off ++ try { ++ attribute_store::attribute attr(updated_node); ++ if (type == DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS) { ++ uic_mqtt_dotdot_application_status_busy_status_publish( ++ base_topic.c_str(), ++ static_cast(attr.desired_or_reported()), ++ UCL_MQTT_PUBLISH_TYPE_DESIRED); ++ return; ++ } ++ if (type == DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME) { ++ uic_mqtt_dotdot_application_status_wait_time_publish( ++ base_topic.c_str(), ++ static_cast(attr.desired_or_reported()), ++ UCL_MQTT_PUBLISH_TYPE_DESIRED); ++ return; ++ } ++ if (type == DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS) { ++ uic_mqtt_dotdot_application_status_reject_status_publish( ++ base_topic.c_str(), ++ static_cast(attr.desired_or_reported()), ++ UCL_MQTT_PUBLISH_TYPE_DESIRED); ++ return; ++ } ++ } catch (std::exception &ex) { ++ sl_log_warning(LOG_TAG, "Failed to publish the Desired attribute value: %s", ex.what()); ++ } ++} ++ ++/** ++ * @brief Publishes the reported value of an updated attribute store node for ++ * the ApplicationStatus cluster. ++ * @param updated_node Updated attribute store node ++ * @param change Type of change applied ++ */ ++static void application_status_cluster_publish_reported_value_callback( ++ attribute_store_node_t updated_node, attribute_store_change_t change) ++{ ++ // clang-format on ++ if (false == is_publish_reported_attribute_values_to_mqtt_enabled()) { ++ return; ++ } ++ if (change == ATTRIBUTE_CREATED) { ++ return; ++ } ++ // Scene exception: check that the attribute is not under the Scene Table extension, which is a config and not the node's state. ++ if (ATTRIBUTE_STORE_INVALID_NODE ++ != attribute_store_get_first_parent_with_type( ++ updated_node, ++ DOTDOT_ATTRIBUTE_ID_SCENES_SCENE_TABLE)) { ++ return; ++ } ++ ++ // Get the UNID and EndPoint, and prepare the basic topic ++ char unid[MAXIMUM_UNID_SIZE] = {}; ++ // clang-format off ++ // clang-format on ++ dotdot_endpoint_id_t endpoint_id = 0; ++ if (SL_STATUS_OK ++ != unify_dotdot_attributes_get_unid_endpoint()(updated_node, ++ unid, ++ &endpoint_id)) { ++ return; ++ } ++ // clang-format off ++ // clang-format on ++ ++ std::string base_topic = "ucl/by-unid/" + std::string(unid); ++ // clang-format off ++ base_topic += "/ep" + std::to_string(endpoint_id); ++ // clang-format on ++ ++ attribute_store_type_t type = attribute_store_get_node_type(updated_node); ++ if (type == ATTRIBUTE_STORE_INVALID_ATTRIBUTE_TYPE) { ++ sl_log_debug(LOG_TAG, ++ "Warning: Invalid type for Attribute ID %d, " ++ "this should not happen.", ++ updated_node); ++ return; ++ } ++ ++ // Deletion case: ++ if (change == ATTRIBUTE_DELETED) { ++ // clang-format off ++ switch(type) { ++ case DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS: ++ // clang-format on ++ sl_log_debug(LOG_TAG, ++ "Unretaining ApplicationStatus::BusyStatus under topic %s", ++ base_topic.c_str()); ++ // clang-format off ++ uic_mqtt_dotdot_application_status_busy_status_unretain(base_topic.c_str(), UCL_MQTT_PUBLISH_TYPE_ALL); ++ break; ++ case DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME: ++ // clang-format on ++ sl_log_debug(LOG_TAG, ++ "Unretaining ApplicationStatus::WaitTime under topic %s", ++ base_topic.c_str()); ++ // clang-format off ++ uic_mqtt_dotdot_application_status_wait_time_unretain(base_topic.c_str(), UCL_MQTT_PUBLISH_TYPE_ALL); ++ break; ++ case DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS: ++ // clang-format on ++ sl_log_debug(LOG_TAG, ++ "Unretaining ApplicationStatus::RejectStatus under topic %s", ++ base_topic.c_str()); ++ // clang-format off ++ uic_mqtt_dotdot_application_status_reject_status_unretain(base_topic.c_str(), UCL_MQTT_PUBLISH_TYPE_ALL); ++ break; ++ default: ++ break; ++ } ++ // clang-format on ++ return; ++ } ++ ++ // If the value got updated but undefined, we skip publication ++ if (false == attribute_store_is_reported_defined(updated_node)) { ++ sl_log_debug(LOG_TAG, "Reported value is undefined. Skipping publication"); ++ return; ++ } ++ ++ // Else we assume update case: ++ // clang-format off ++ try { ++ attribute_store::attribute attr(updated_node); ++ if (type == DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS) { ++ uic_mqtt_dotdot_application_status_busy_status_publish( ++ base_topic.c_str(), ++ static_cast(attr.reported()), ++ (attr.desired_exists() && !attribute_store_is_value_matched(updated_node)) ? UCL_MQTT_PUBLISH_TYPE_REPORTED : UCL_MQTT_PUBLISH_TYPE_ALL); ++ return; ++ } ++ if (type == DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME) { ++ uic_mqtt_dotdot_application_status_wait_time_publish( ++ base_topic.c_str(), ++ static_cast(attr.reported()), ++ (attr.desired_exists() && !attribute_store_is_value_matched(updated_node)) ? UCL_MQTT_PUBLISH_TYPE_REPORTED : UCL_MQTT_PUBLISH_TYPE_ALL); ++ return; ++ } ++ if (type == DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS) { ++ uic_mqtt_dotdot_application_status_reject_status_publish( ++ base_topic.c_str(), ++ static_cast(attr.reported()), ++ (attr.desired_exists() && !attribute_store_is_value_matched(updated_node)) ? UCL_MQTT_PUBLISH_TYPE_REPORTED : UCL_MQTT_PUBLISH_TYPE_ALL); ++ return; ++ } ++ } catch (std::exception &ex) { ++ sl_log_warning(LOG_TAG, "Failed to publish the Reported attribute value: %s", ex.what()); ++ } ++} ++ ++static void application_status_cluster_cluster_revision_callback( ++ attribute_store_node_t updated_node, attribute_store_change_t change) ++{ ++ // clang-format on ++ if (false == is_publish_reported_attribute_values_to_mqtt_enabled()) { ++ return; ++ } ++ ++ // Get the UNID and EndPoint, and prepare the basic topic ++ char unid[MAXIMUM_UNID_SIZE] = {}; ++ dotdot_endpoint_id_t endpoint_id = 0; ++ // clang-format off ++ // clang-format on ++ if (SL_STATUS_OK ++ != unify_dotdot_attributes_get_unid_endpoint()(updated_node, ++ unid, ++ &endpoint_id)) { ++ return; ++ } ++ // clang-format off ++ // clang-format on ++ ++ std::string base_topic = "ucl/by-unid/" + std::string(unid); ++ // clang-format off ++ base_topic += "/ep" + std::to_string(endpoint_id); ++ ++ if ((change == ATTRIBUTE_CREATED) || (change == ATTRIBUTE_UPDATED)) { ++ // On attribute creation, make sure to publish the attribute revision for the first time ++ std::string cluster_revision_topic = base_topic + "/ApplicationStatus/Attributes/ClusterRevision"; ++ if (uic_mqtt_count_topics(cluster_revision_topic.c_str()) == 0) { ++ uic_mqtt_dotdot_application_status_publish_cluster_revision(base_topic.c_str(), 1); ++ } ++ } ++ ++ if (change == ATTRIBUTE_DELETED) { ++ // Check if we just erased the last attribute under a cluster, if yes, unretain ++ // the Cluster revision too. ++ if (false == dotdot_is_any_application_status_attribute_supported(unid, endpoint_id)) { ++ base_topic += "/ApplicationStatus"; ++ sl_log_debug(LOG_TAG, "No more attributes supported for ApplicationStatus cluster for UNID %s Endpoint %d. Unretaining leftover topics at %s",unid, endpoint_id, base_topic.c_str()); ++ uic_mqtt_unretain(base_topic.c_str()); ++ } ++ } ++} ++ ++ + + // Initialization of the component. + sl_status_t unify_dotdot_attribute_store_attribute_publisher_init() +@@ -36132,6 +36391,48 @@ sl_status_t unify_dotdot_attribute_store_attribute_publisher_init() + attribute_store_register_callback_by_type( + unify_humidity_control_cluster_cluster_revision_callback, + DOTDOT_ATTRIBUTE_ID_UNIFY_HUMIDITY_CONTROL_AUTO_SETPOINT_PRECISION); ++ //Desired attribute state ++ attribute_store_register_callback_by_type_and_state( ++ application_status_cluster_publish_desired_value_callback, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS, ++ DESIRED_ATTRIBUTE); ++ //Reported attribute state ++ attribute_store_register_callback_by_type_and_state( ++ application_status_cluster_publish_reported_value_callback, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS, ++ REPORTED_ATTRIBUTE); ++ //registering a callback when an attribute is created for publishing cluster revision ++ attribute_store_register_callback_by_type( ++ application_status_cluster_cluster_revision_callback, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS); ++ //Desired attribute state ++ attribute_store_register_callback_by_type_and_state( ++ application_status_cluster_publish_desired_value_callback, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME, ++ DESIRED_ATTRIBUTE); ++ //Reported attribute state ++ attribute_store_register_callback_by_type_and_state( ++ application_status_cluster_publish_reported_value_callback, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME, ++ REPORTED_ATTRIBUTE); ++ //registering a callback when an attribute is created for publishing cluster revision ++ attribute_store_register_callback_by_type( ++ application_status_cluster_cluster_revision_callback, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME); ++ //Desired attribute state ++ attribute_store_register_callback_by_type_and_state( ++ application_status_cluster_publish_desired_value_callback, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS, ++ DESIRED_ATTRIBUTE); ++ //Reported attribute state ++ attribute_store_register_callback_by_type_and_state( ++ application_status_cluster_publish_reported_value_callback, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS, ++ REPORTED_ATTRIBUTE); ++ //registering a callback when an attribute is created for publishing cluster revision ++ attribute_store_register_callback_by_type( ++ application_status_cluster_cluster_revision_callback, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS); + + return SL_STATUS_OK; + } +diff --git a/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_force_read_attributes_command_callbacks.c b/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_force_read_attributes_command_callbacks.c +index 8237315595..20928e3b69 100644 +--- a/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_force_read_attributes_command_callbacks.c ++++ b/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_force_read_attributes_command_callbacks.c +@@ -4554,6 +4554,46 @@ static sl_status_t uic_mqtt_dotdot_unify_humidity_control_force_read_attributes_ + } + return SL_STATUS_OK; + } ++//////////////////////////////////////////////////////////////////////////////// ++// Start of cluster ApplicationStatus ++//////////////////////////////////////////////////////////////////////////////// ++static sl_status_t uic_mqtt_dotdot_application_status_force_read_attributes_callback ( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id, ++ uic_mqtt_dotdot_callback_call_type_t call_type, ++ uic_mqtt_dotdot_application_status_updated_state_t attribute_list) { ++ ++ if (false == is_force_read_attributes_enabled()){ ++ return SL_STATUS_FAIL; ++ } ++ ++ if (call_type == UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK) { ++ if (is_automatic_deduction_of_supported_commands_enabled()) { ++ return dotdot_is_any_application_status_attribute_supported(unid, endpoint_id) ? ++ SL_STATUS_OK : SL_STATUS_FAIL; ++ } else { ++ return SL_STATUS_FAIL; ++ } ++ } ++ ++ // Go and undefine everything that needs to be read again: ++ if (true == attribute_list.busy_status) { ++ if (SL_STATUS_OK == dotdot_application_status_busy_status_undefine_reported(unid, endpoint_id)) { ++ sl_log_debug(LOG_TAG, "Undefined Reported value of ApplicationStatus::BusyStatus under %s - Endpoint %d", unid, endpoint_id); ++ } ++ } ++ if (true == attribute_list.wait_time) { ++ if (SL_STATUS_OK == dotdot_application_status_wait_time_undefine_reported(unid, endpoint_id)) { ++ sl_log_debug(LOG_TAG, "Undefined Reported value of ApplicationStatus::WaitTime under %s - Endpoint %d", unid, endpoint_id); ++ } ++ } ++ if (true == attribute_list.reject_status) { ++ if (SL_STATUS_OK == dotdot_application_status_reject_status_undefine_reported(unid, endpoint_id)) { ++ sl_log_debug(LOG_TAG, "Undefined Reported value of ApplicationStatus::RejectStatus under %s - Endpoint %d", unid, endpoint_id); ++ } ++ } ++ return SL_STATUS_OK; ++} + // clang-format on + + //////////////////////////////////////////////////////////////////////////////// +@@ -4666,6 +4706,8 @@ sl_status_t + + uic_mqtt_dotdot_set_unify_humidity_control_force_read_attributes_callback(&uic_mqtt_dotdot_unify_humidity_control_force_read_attributes_callback); + ++ uic_mqtt_dotdot_set_application_status_force_read_attributes_callback(&uic_mqtt_dotdot_application_status_force_read_attributes_callback); ++ + // clang-format on + + return SL_STATUS_OK; +diff --git a/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_helpers.cpp b/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_helpers.cpp +index bbcc3f7fad..49a6ef47f7 100644 +--- a/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_helpers.cpp ++++ b/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_helpers.cpp +@@ -84235,5 +84235,366 @@ bool dotdot_is_any_unify_humidity_control_writable_attribute_supported( + const dotdot_endpoint_id_t endpoint_id) { + + ++ return false; ++} ++//////////////////////////////////////////////////////////////////////////////// ++// Start of cluster ApplicationStatus ++//////////////////////////////////////////////////////////////////////////////// ++bool dotdot_is_supported_application_status_busy_status( ++ const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id) ++{ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS); ++ return attribute_store_node_exists(node); ++} ++ ++uint8_t dotdot_get_application_status_busy_status( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state) ++{ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS); ++ ++ uint8_t result = {}; ++ attribute_store_read_value(node, ++ value_state, ++ (uint8_t *)&result, ++ sizeof(result)); ++ return result; ++} ++ ++sl_status_t dotdot_set_application_status_busy_status( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state, ++ uint8_t new_busy_status ++ ) ++{ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS); ++ ++ return attribute_store_set_node_attribute_value(node, ++ value_state, ++ (uint8_t *)&new_busy_status, ++ sizeof(uint8_t)); ++ } ++ ++sl_status_t dotdot_application_status_busy_status_undefine_reported( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) { ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS); ++ attribute_store_undefine_reported(node); ++ return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; ++} ++ ++sl_status_t dotdot_application_status_busy_status_undefine_desired( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) { ++ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS); ++ attribute_store_undefine_desired(node); ++ return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; ++} ++ ++ ++bool dotdot_application_status_busy_status_is_reported_defined( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) ++{ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS); ++ return attribute_store_is_reported_defined(node); ++} ++ ++bool dotdot_application_status_busy_status_is_desired_defined( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) ++{ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS); ++ return attribute_store_is_desired_defined(node); ++} ++ ++sl_status_t dotdot_create_application_status_busy_status( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) { ++ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node = ++ attribute_store_create_child_if_missing(endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS); ++ ++ return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; ++} ++bool dotdot_is_supported_application_status_wait_time( ++ const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id) ++{ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME); ++ return attribute_store_node_exists(node); ++} ++ ++uint8_t dotdot_get_application_status_wait_time( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state) ++{ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME); ++ ++ uint8_t result = {}; ++ attribute_store_read_value(node, ++ value_state, ++ (uint8_t *)&result, ++ sizeof(result)); ++ return result; ++} ++ ++sl_status_t dotdot_set_application_status_wait_time( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state, ++ uint8_t new_wait_time ++ ) ++{ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME); ++ ++ return attribute_store_set_node_attribute_value(node, ++ value_state, ++ (uint8_t *)&new_wait_time, ++ sizeof(uint8_t)); ++ } ++ ++sl_status_t dotdot_application_status_wait_time_undefine_reported( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) { ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME); ++ attribute_store_undefine_reported(node); ++ return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; ++} ++ ++sl_status_t dotdot_application_status_wait_time_undefine_desired( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) { ++ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME); ++ attribute_store_undefine_desired(node); ++ return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; ++} ++ ++ ++bool dotdot_application_status_wait_time_is_reported_defined( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) ++{ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME); ++ return attribute_store_is_reported_defined(node); ++} ++ ++bool dotdot_application_status_wait_time_is_desired_defined( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) ++{ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME); ++ return attribute_store_is_desired_defined(node); ++} ++ ++sl_status_t dotdot_create_application_status_wait_time( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) { ++ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node = ++ attribute_store_create_child_if_missing(endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME); ++ ++ return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; ++} ++bool dotdot_is_supported_application_status_reject_status( ++ const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id) ++{ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS); ++ return attribute_store_node_exists(node); ++} ++ ++bool dotdot_get_application_status_reject_status( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state) ++{ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS); ++ ++ bool result = {}; ++ attribute_store_read_value(node, ++ value_state, ++ (uint8_t *)&result, ++ sizeof(result)); ++ return result; ++} ++ ++sl_status_t dotdot_set_application_status_reject_status( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state, ++ bool new_reject_status ++ ) ++{ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS); ++ ++ return attribute_store_set_node_attribute_value(node, ++ value_state, ++ (uint8_t *)&new_reject_status, ++ sizeof(bool)); ++ } ++ ++sl_status_t dotdot_application_status_reject_status_undefine_reported( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) { ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS); ++ attribute_store_undefine_reported(node); ++ return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; ++} ++ ++sl_status_t dotdot_application_status_reject_status_undefine_desired( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) { ++ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS); ++ attribute_store_undefine_desired(node); ++ return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; ++} ++ ++ ++bool dotdot_application_status_reject_status_is_reported_defined( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) ++{ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS); ++ return attribute_store_is_reported_defined(node); ++} ++ ++bool dotdot_application_status_reject_status_is_desired_defined( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) ++{ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node ++ = attribute_store_get_first_child_by_type( ++ endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS); ++ return attribute_store_is_desired_defined(node); ++} ++ ++sl_status_t dotdot_create_application_status_reject_status( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) { ++ ++ attribute_store_node_t endpoint_node = unify_dotdot_attributes_get_endpoint_node()(unid, endpoint_id); ++ attribute_store_node_t node = ++ attribute_store_create_child_if_missing(endpoint_node, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS); ++ ++ return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; ++} ++ ++bool dotdot_is_any_application_status_attribute_supported( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) { ++ ++ if (true == dotdot_is_supported_application_status_busy_status(unid, endpoint_id)) { ++ return true; ++ } ++ if (true == dotdot_is_supported_application_status_wait_time(unid, endpoint_id)) { ++ return true; ++ } ++ if (true == dotdot_is_supported_application_status_reject_status(unid, endpoint_id)) { ++ return true; ++ } ++ ++ return false; ++} ++ ++bool dotdot_is_any_application_status_writable_attribute_supported( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) { ++ ++ + return false; + } +diff --git a/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_registration.cpp b/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_registration.cpp +index a321941cf6..aa858a9fbd 100644 +--- a/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_registration.cpp ++++ b/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_registration.cpp +@@ -17455,6 +17455,78 @@ sl_status_t unify_dotdot_attribute_store_registration_init() + // clang-format off + // clang-format on + ++ { ++ // enum8 // enum8 // uint8_t ++ std::string attribute_type_string = "uint8_t"; ++ attribute_store_storage_type_t storage_type = UNKNOWN_STORAGE_TYPE; ++ ++ // clang-format off ++ storage_type = attribute_storage_type_conversion(attribute_type_string); ++ ++ if (storage_type == UNKNOWN_STORAGE_TYPE) { ++ sl_log_warning(LOG_TAG, ++ "Unkown storage type for ZCL ApplicationStatus BusyStatus, " ++ "type: enum8 // uint8_t"); ++ } ++ ++ status |= attribute_store_register_type( ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS, ++ "ZCL ApplicationStatus BusyStatus", ++ ATTRIBUTE_STORE_INVALID_ATTRIBUTE_TYPE, ++ storage_type); ++ } ++ ++ // clang-format off ++ // clang-format on ++ ++ { ++ // uint8 // uint8 // uint8_t ++ std::string attribute_type_string = "uint8_t"; ++ attribute_store_storage_type_t storage_type = UNKNOWN_STORAGE_TYPE; ++ ++ // clang-format off ++ storage_type = attribute_storage_type_conversion(attribute_type_string); ++ ++ if (storage_type == UNKNOWN_STORAGE_TYPE) { ++ sl_log_warning(LOG_TAG, ++ "Unkown storage type for ZCL ApplicationStatus WaitTime, " ++ "type: uint8 // uint8_t"); ++ } ++ ++ status |= attribute_store_register_type( ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME, ++ "ZCL ApplicationStatus WaitTime", ++ ATTRIBUTE_STORE_INVALID_ATTRIBUTE_TYPE, ++ storage_type); ++ } ++ ++ // clang-format off ++ // clang-format on ++ ++ { ++ // bool // bool // bool ++ std::string attribute_type_string = "bool"; ++ attribute_store_storage_type_t storage_type = UNKNOWN_STORAGE_TYPE; ++ ++ // clang-format off ++ storage_type = attribute_storage_type_conversion(attribute_type_string); ++ ++ if (storage_type == UNKNOWN_STORAGE_TYPE) { ++ sl_log_warning(LOG_TAG, ++ "Unkown storage type for ZCL ApplicationStatus RejectStatus, " ++ "type: bool // bool"); ++ } ++ ++ status |= attribute_store_register_type( ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS, ++ "ZCL ApplicationStatus RejectStatus", ++ ATTRIBUTE_STORE_INVALID_ATTRIBUTE_TYPE, ++ storage_type); ++ } ++ ++ // clang-format off ++ // clang-format on ++ + // Additional attributes: + for (auto const &a: zcl_additional_attribute_schema) { + status |= attribute_store_register_type(a.type, +diff --git a/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_write_attributes_command_callbacks.c b/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_write_attributes_command_callbacks.c +index 799780c184..ce114c8592 100644 +--- a/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_write_attributes_command_callbacks.c ++++ b/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_write_attributes_command_callbacks.c +@@ -2555,6 +2555,36 @@ static sl_status_t unify_humidity_control_cluster_write_attributes_callback( + endpoint_id); + return SL_STATUS_OK; + } ++//////////////////////////////////////////////////////////////////////////////// ++// Start of cluster ApplicationStatus ++//////////////////////////////////////////////////////////////////////////////// ++// WriteAttribute Callbacks application_status ++static sl_status_t application_status_cluster_write_attributes_callback( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id, ++ uic_mqtt_dotdot_callback_call_type_t call_type, ++ uic_mqtt_dotdot_application_status_state_t attributes, ++ uic_mqtt_dotdot_application_status_updated_state_t updated_attributes) ++{ ++ if (false == is_write_attributes_enabled()) { ++ return SL_STATUS_FAIL; ++ } ++ ++ if (call_type == UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK) { ++ if (is_automatic_deduction_of_supported_commands_enabled()) { ++ return dotdot_is_any_application_status_writable_attribute_supported(unid, endpoint_id) ? ++ SL_STATUS_OK : SL_STATUS_FAIL; ++ } else { ++ return SL_STATUS_FAIL; ++ } ++ } ++ ++ sl_log_debug(LOG_TAG, ++ "application_status: Incoming WriteAttributes command for %s, endpoint %d.\n", ++ unid, ++ endpoint_id); ++ return SL_STATUS_OK; ++} + // clang-format on + + //////////////////////////////////////////////////////////////////////////////// +@@ -2725,6 +2755,9 @@ sl_status_t + uic_mqtt_dotdot_set_unify_humidity_control_write_attributes_callback( + &unify_humidity_control_cluster_write_attributes_callback); + ++ uic_mqtt_dotdot_set_application_status_write_attributes_callback( ++ &application_status_cluster_write_attributes_callback); ++ + // clang-format on + + return SL_STATUS_OK; +diff --git a/components/unify_dotdot_attribute_store/zap-generated/test/unify_dotdot_attribute_store_test.c b/components/unify_dotdot_attribute_store/zap-generated/test/unify_dotdot_attribute_store_test.c +index d43942b157..3c5c797717 100644 +--- a/components/unify_dotdot_attribute_store/zap-generated/test/unify_dotdot_attribute_store_test.c ++++ b/components/unify_dotdot_attribute_store/zap-generated/test/unify_dotdot_attribute_store_test.c +@@ -1330,6 +1330,16 @@ static uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback_t test_uic_m + uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback_t get_uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback(){ + return test_uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback; + } ++static uic_mqtt_dotdot_application_status_force_read_attributes_callback_t test_uic_mqtt_dotdot_application_status_force_read_attributes_callback = NULL; ++static uic_mqtt_dotdot_application_status_write_attributes_callback_t test_uic_mqtt_dotdot_application_status_write_attributes_callback = NULL; ++ ++uic_mqtt_dotdot_application_status_force_read_attributes_callback_t get_uic_mqtt_dotdot_application_status_force_read_attributes_callback(){ ++ return test_uic_mqtt_dotdot_application_status_force_read_attributes_callback; ++} ++uic_mqtt_dotdot_application_status_write_attributes_callback_t get_uic_mqtt_dotdot_application_status_write_attributes_callback(){ ++ return test_uic_mqtt_dotdot_application_status_write_attributes_callback; ++} ++ + // clang-format on + + #define TEST_UNID "test-unid-123" +@@ -2931,6 +2941,16 @@ void uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback_set_stub( + { + test_uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback = callback; + } ++void set_uic_mqtt_dotdot_application_status_force_read_attributes_callback_stub( ++ const uic_mqtt_dotdot_application_status_force_read_attributes_callback_t callback, int cmock_num_calls) ++{ ++ test_uic_mqtt_dotdot_application_status_force_read_attributes_callback = callback; ++} ++void set_uic_mqtt_dotdot_application_status_write_attributes_callback_stub( ++ const uic_mqtt_dotdot_application_status_write_attributes_callback_t callback, int cmock_num_calls) ++{ ++ test_uic_mqtt_dotdot_application_status_write_attributes_callback = callback; ++} + // clang-format on + + // Test functions +@@ -3863,6 +3883,12 @@ void setUp() + test_uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback = NULL; + uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback_set_Stub( + &uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback_set_stub); ++ test_uic_mqtt_dotdot_application_status_force_read_attributes_callback = NULL; ++ uic_mqtt_dotdot_set_application_status_force_read_attributes_callback_Stub( ++ &set_uic_mqtt_dotdot_application_status_force_read_attributes_callback_stub); ++ test_uic_mqtt_dotdot_application_status_write_attributes_callback = NULL; ++ uic_mqtt_dotdot_set_application_status_write_attributes_callback_Stub( ++ &set_uic_mqtt_dotdot_application_status_write_attributes_callback_stub); + // clang-format on + + group_command_dispatch = NULL; +@@ -4631,6 +4657,9 @@ void test_automatic_deduction_of_supported_commands() + TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_unify_humidity_control_auto_setpoint(expected_unid,expected_endpoint_id) ); + TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_unify_humidity_control_auto_setpoint_scale(expected_unid,expected_endpoint_id) ); + TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_unify_humidity_control_auto_setpoint_precision(expected_unid,expected_endpoint_id) ); ++ TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_application_status_busy_status(expected_unid,expected_endpoint_id) ); ++ TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_application_status_wait_time(expected_unid,expected_endpoint_id) ); ++ TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_application_status_reject_status(expected_unid,expected_endpoint_id) ); + + // clang-format on + // ColorControl checks the value in the bitmask: +diff --git a/components/unify_dotdot_attribute_store/zap-generated/test/unify_dotdot_attribute_store_test.h b/components/unify_dotdot_attribute_store/zap-generated/test/unify_dotdot_attribute_store_test.h +index 73c36434ca..b2ec3fb4a5 100644 +--- a/components/unify_dotdot_attribute_store/zap-generated/test/unify_dotdot_attribute_store_test.h ++++ b/components/unify_dotdot_attribute_store/zap-generated/test/unify_dotdot_attribute_store_test.h +@@ -803,4 +803,8 @@ + + uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback_t get_uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback(); + ++ ++ uic_mqtt_dotdot_application_status_force_read_attributes_callback_t get_uic_mqtt_dotdot_application_status_force_read_attributes_callback(); ++ uic_mqtt_dotdot_application_status_write_attributes_callback_t get_uic_mqtt_dotdot_application_status_write_attributes_callback(); ++ + #endif // UNIFY_DOTDOT_ATTRIBUTE_STORE_TEST_H +\ No newline at end of file +-- +2.39.5 + diff --git a/patches/UnifySDK/0010-UIC-3275-Regen-zapfiles-for-Application-status.patch b/patches/UnifySDK/0010-UIC-3275-Regen-zapfiles-for-Application-status.patch new file mode 100644 index 000000000..5fc78fa15 --- /dev/null +++ b/patches/UnifySDK/0010-UIC-3275-Regen-zapfiles-for-Application-status.patch @@ -0,0 +1,677 @@ +From 5cc8191b69b10ad09f51f85d7a33b5da51c63770 Mon Sep 17 00:00:00 2001 +From: Philippe Coval +Date: Mon, 29 Sep 2025 14:30:26 +0200 +Subject: [PATCH] UIC-3275: Regen zapfiles for Application status + +Relate-to: https://github.com/SiliconLabsSoftware/z-wave-protocol-controller/pull/147 +Signed-off-by: Philippe Coval +--- + .../src/eed_attribute_store_clusters.c | 12 + + .../src/eed_dotdot_create_clusters.cpp | 42 ++ + .../include/dotdot_attribute_id_definitions.h | 4 + + .../dotdot_cluster_command_id_definitions.h | 2 + + .../include/dotdot_cluster_id_definitions.h | 4 + + .../zap-generated/include/zap-types.h | 7 + + .../readme_ucl_mqtt_reference.md | 378 ++++++++++++++++++ + .../src/dotdot_attribute_id_definitions.c | 47 +++ + .../src/dotdot_cluster_id_definitions.c | 5 + + 9 files changed, 501 insertions(+) + +diff --git a/applications/examples/applications/emulated_end_device/components/eed_attribute_store/zap-generated/src/eed_attribute_store_clusters.c b/applications/examples/applications/emulated_end_device/components/eed_attribute_store/zap-generated/src/eed_attribute_store_clusters.c +index 8780c887c8..7edd076395 100644 +--- a/applications/examples/applications/emulated_end_device/components/eed_attribute_store/zap-generated/src/eed_attribute_store_clusters.c ++++ b/applications/examples/applications/emulated_end_device/components/eed_attribute_store/zap-generated/src/eed_attribute_store_clusters.c +@@ -2791,6 +2791,18 @@ sl_status_t eed_attribute_store_clusters_init() + &on_zcl_desired_value_update, + DOTDOT_ATTRIBUTE_ID_UNIFY_HUMIDITY_CONTROL_AUTO_SETPOINT_PRECISION, + DESIRED_ATTRIBUTE); ++ attribute_store_register_callback_by_type_and_state( ++ &on_zcl_desired_value_update, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS, ++ DESIRED_ATTRIBUTE); ++ attribute_store_register_callback_by_type_and_state( ++ &on_zcl_desired_value_update, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME, ++ DESIRED_ATTRIBUTE); ++ attribute_store_register_callback_by_type_and_state( ++ &on_zcl_desired_value_update, ++ DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS, ++ DESIRED_ATTRIBUTE); + // clang-format on + return SL_STATUS_OK; + } +\ No newline at end of file +diff --git a/applications/examples/applications/emulated_end_device/components/eed_attribute_store/zap-generated/src/eed_dotdot_create_clusters.cpp b/applications/examples/applications/emulated_end_device/components/eed_attribute_store/zap-generated/src/eed_dotdot_create_clusters.cpp +index 8230ced6c8..0f0a4839ee 100644 +--- a/applications/examples/applications/emulated_end_device/components/eed_attribute_store/zap-generated/src/eed_dotdot_create_clusters.cpp ++++ b/applications/examples/applications/emulated_end_device/components/eed_attribute_store/zap-generated/src/eed_dotdot_create_clusters.cpp +@@ -4157,6 +4157,27 @@ void dotdot_create_unify_humidity_control_wrapper(const dotdot_unid_t unid, cons + + } + ++void dotdot_create_application_status_wrapper(const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id){ ++ // Create and set a default value if undefined. ++ dotdot_create_application_status_busy_status(unid,endpoint_id); ++ if (false == dotdot_application_status_busy_status_is_reported_defined(unid,endpoint_id)){ ++ dotdot_set_application_status_busy_status(unid,endpoint_id,REPORTED_ATTRIBUTE, static_cast(0)); ++ } ++ ++ // Create and set a default value if undefined. ++ dotdot_create_application_status_wait_time(unid,endpoint_id); ++ if (false == dotdot_application_status_wait_time_is_reported_defined(unid,endpoint_id)){ ++ dotdot_set_application_status_wait_time(unid,endpoint_id,REPORTED_ATTRIBUTE, static_cast(0)); ++ } ++ ++ // Create and set a default value if undefined. ++ dotdot_create_application_status_reject_status(unid,endpoint_id); ++ if (false == dotdot_application_status_reject_status_is_reported_defined(unid,endpoint_id)){ ++ dotdot_set_application_status_reject_status(unid,endpoint_id,REPORTED_ATTRIBUTE, static_cast(0)); ++ } ++ ++} ++ + + void dotdot_unretain_basic_wrapper(const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id){ + char base_topic[256]; +@@ -6633,6 +6654,25 @@ void dotdot_unretain_unify_humidity_control_wrapper(const dotdot_unid_t unid, co + uic_mqtt_dotdot_unify_humidity_control_publish_empty_supported_commands(unid, endpoint_id); + } + ++void dotdot_unretain_application_status_wrapper(const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id){ ++ char base_topic[256]; ++ snprintf(base_topic, sizeof(base_topic), "ucl/by-unid/%s/ep%d", unid, endpoint_id); ++ attribute_store::attribute ep_node = eed_attribute_store_get_endpoint_node(unid, endpoint_id); ++ ++ uic_mqtt_dotdot_application_status_busy_status_unretain(base_topic,UCL_MQTT_PUBLISH_TYPE_ALL); ++ ep_node.child_by_type(DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_BUSY_STATUS).delete_node(); ++ ++ uic_mqtt_dotdot_application_status_wait_time_unretain(base_topic,UCL_MQTT_PUBLISH_TYPE_ALL); ++ ep_node.child_by_type(DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_WAIT_TIME).delete_node(); ++ ++ uic_mqtt_dotdot_application_status_reject_status_unretain(base_topic,UCL_MQTT_PUBLISH_TYPE_ALL); ++ ep_node.child_by_type(DOTDOT_ATTRIBUTE_ID_APPLICATION_STATUS_REJECT_STATUS).delete_node(); ++ ++ ++ uic_mqtt_dotdot_application_status_unretain_cluster_revision(base_topic); ++ uic_mqtt_dotdot_application_status_publish_empty_supported_commands(unid, endpoint_id); ++} ++ + + void eed_dotdot_create_clusters(const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id) { + for (auto& pair : CreateClusterMap) { +@@ -6711,6 +6751,7 @@ std::map CreateClusterMap = { + { "UnifyFanControl", dotdot_create_unify_fan_control_wrapper }, + { "UnifyThermostat", dotdot_create_unify_thermostat_wrapper }, + { "UnifyHumidityControl", dotdot_create_unify_humidity_control_wrapper }, ++{ "ApplicationStatus", dotdot_create_application_status_wrapper }, + }; + + std::map CreateUnretainMap = { +@@ -6762,4 +6803,5 @@ std::map CreateUnretainMap = { + { "UnifyFanControl", dotdot_unretain_unify_fan_control_wrapper }, + { "UnifyThermostat", dotdot_unretain_unify_thermostat_wrapper }, + { "UnifyHumidityControl", dotdot_unretain_unify_humidity_control_wrapper }, ++{ "ApplicationStatus", dotdot_unretain_application_status_wrapper }, + }; +\ No newline at end of file +diff --git a/components/uic_dotdot/zap-generated/include/dotdot_attribute_id_definitions.h b/components/uic_dotdot/zap-generated/include/dotdot_attribute_id_definitions.h +index a988971041..a23a865439 100644 +--- a/components/uic_dotdot/zap-generated/include/dotdot_attribute_id_definitions.h ++++ b/components/uic_dotdot/zap-generated/include/dotdot_attribute_id_definitions.h +@@ -884,6 +884,10 @@ typedef enum { + #define DOTDOT_UNIFY_HUMIDITY_CONTROL_AUTO_SETPOINT_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x11) + #define DOTDOT_UNIFY_HUMIDITY_CONTROL_AUTO_SETPOINT_SCALE_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x12) + #define DOTDOT_UNIFY_HUMIDITY_CONTROL_AUTO_SETPOINT_PRECISION_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x13) ++// Definitions for cluster: ApplicationStatus ++#define DOTDOT_APPLICATION_STATUS_BUSY_STATUS_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x0) ++#define DOTDOT_APPLICATION_STATUS_WAIT_TIME_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x1) ++#define DOTDOT_APPLICATION_STATUS_REJECT_STATUS_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x2) + + // clang-format on + +diff --git a/components/uic_dotdot/zap-generated/include/dotdot_cluster_command_id_definitions.h b/components/uic_dotdot/zap-generated/include/dotdot_cluster_command_id_definitions.h +index 7e33175d4a..bbc96f7d2f 100644 +--- a/components/uic_dotdot/zap-generated/include/dotdot_cluster_command_id_definitions.h ++++ b/components/uic_dotdot/zap-generated/include/dotdot_cluster_command_id_definitions.h +@@ -375,6 +375,8 @@ + #define DOTDOT_UNIFY_HUMIDITY_CONTROL_MODE_SET_COMMAND_ID (0x1) + #define DOTDOT_UNIFY_HUMIDITY_CONTROL_SETPOINT_SET_COMMAND_ID (0x2) + ++// Commands for cluster: ApplicationStatus ++ + #ifdef __cplusplus + extern "C" { + #endif +diff --git a/components/uic_dotdot/zap-generated/include/dotdot_cluster_id_definitions.h b/components/uic_dotdot/zap-generated/include/dotdot_cluster_id_definitions.h +index 34db7d7b79..ee828797d3 100644 +--- a/components/uic_dotdot/zap-generated/include/dotdot_cluster_id_definitions.h ++++ b/components/uic_dotdot/zap-generated/include/dotdot_cluster_id_definitions.h +@@ -266,6 +266,10 @@ + #define DOTDOT_UNIFY_HUMIDITY_CONTROL_CLUSTER_ID ((dotdot_cluster_id_t)0xFDA0) + + ++// Definitions for cluster: ApplicationStatus ++#define DOTDOT_APPLICATION_STATUS_CLUSTER_ID ((dotdot_cluster_id_t)0xFFA2) ++ ++ + #ifdef __cplusplus + extern "C" { + #endif +diff --git a/components/uic_dotdot/zap-generated/include/zap-types.h b/components/uic_dotdot/zap-generated/include/zap-types.h +index daf85dbdac..cd869533db 100644 +--- a/components/uic_dotdot/zap-generated/include/zap-types.h ++++ b/components/uic_dotdot/zap-generated/include/zap-types.h +@@ -201,6 +201,13 @@ typedef enum { + ZCL_AO_X_LOCATOR_REPORTING_MODE_ANGLE_REPORT = 1, + } AoXLocatorReportingMode; + ++// Enum for ApplicationStatusBusyStatus ++typedef enum { ++ ZCL_APPLICATION_STATUS_BUSY_STATUS_TRY_AGAIN_LATER = 0, ++ ZCL_APPLICATION_STATUS_BUSY_STATUS_TRY_AGAIN_IN_WAIT_TIME_SECONDS = 1, ++ ZCL_APPLICATION_STATUS_BUSY_STATUS_REQUEST_QUEUED = 2, ++} ApplicationStatusBusyStatus; ++ + // Enum for ArmArmMode + typedef enum { + ZCL_ARM_ARM_MODE_DISARM = 0, +diff --git a/components/uic_dotdot/zap-generated/readme_ucl_mqtt_reference.md b/components/uic_dotdot/zap-generated/readme_ucl_mqtt_reference.md +index 5316b884c2..32567f3167 100644 +--- a/components/uic_dotdot/zap-generated/readme_ucl_mqtt_reference.md ++++ b/components/uic_dotdot/zap-generated/readme_ucl_mqtt_reference.md +@@ -54432,6 +54432,360 @@ mosquitto_pub -t 'ucl/by-unid///UnifyHumidityControl/Commands/ForceRea + + + ++


++ ++ ++ ++ ++ ++ ++ ++\page application_status ApplicationStatus Cluster ++The following commands and attributes are accepted as JSON payloads for the ++ApplicationStatus cluster. ++ ++

++ ++ ++ ++ ++\section application_status_attrs ApplicationStatus Attributes ++The following attribute topics are used to retrieve the ApplicationStatus cluster state. ++ ++
++ ++\subsection application_status_attr_busy_status ApplicationStatus/BusyStatus Attribute ++ ++**MQTT Topic Pattern:** ++ ++``` ++[PREFIX]/ApplicationStatus/Attributes/BusyStatus/Reported ++[PREFIX]/ApplicationStatus/Attributes/BusyStatus/Desired ++``` ++ ++**MQTT Payload JSON Schema:** ++ ++```json ++{ ++ "$schema": "http://json-schema.org/draft-07/schema#", ++ "title": "ApplicationStatus Cluster BusyStatus Attribute Properties", ++ "type": "object", ++ "properties": { ++ "value": { ++ "type": "ApplicationStatusBusyStatus" ++ } ++ }, ++ "required": [ ++ "value" ++ ] ++} ++``` ++ ++ ++**Example Mosquitto CLI Tool Usage** ++ ++To see desired/reported value for BusyStatus attribute under the by-unid topic space: ++ ++```console ++mosquitto_sub -t 'ucl/by-unid/+/+/ApplicationStatus/Attributes/BusyStatus/+' ++ ++# Example output ++ ++ucl/by-unid//ep0/ApplicationStatus/Attributes/BusyStatus/Desired { "value": } ++ucl/by-unid//ep0/ApplicationStatus/Attributes/BusyStatus/Reported { "value": } ++ ++``` ++ ++

++ ++\subsection application_status_attr_wait_time ApplicationStatus/WaitTime Attribute ++ ++**MQTT Topic Pattern:** ++ ++``` ++[PREFIX]/ApplicationStatus/Attributes/WaitTime/Reported ++[PREFIX]/ApplicationStatus/Attributes/WaitTime/Desired ++``` ++ ++**MQTT Payload JSON Schema:** ++ ++```json ++{ ++ "$schema": "http://json-schema.org/draft-07/schema#", ++ "title": "ApplicationStatus Cluster WaitTime Attribute Properties", ++ "type": "object", ++ "properties": { ++ "value": { ++ "type": "integer" ++ } ++ }, ++ "required": [ ++ "value" ++ ] ++} ++``` ++ ++ ++**Example Mosquitto CLI Tool Usage** ++ ++To see desired/reported value for WaitTime attribute under the by-unid topic space: ++ ++```console ++mosquitto_sub -t 'ucl/by-unid/+/+/ApplicationStatus/Attributes/WaitTime/+' ++ ++# Example output ++ ++ucl/by-unid//ep0/ApplicationStatus/Attributes/WaitTime/Desired { "value": } ++ucl/by-unid//ep0/ApplicationStatus/Attributes/WaitTime/Reported { "value": } ++ ++``` ++ ++

++ ++\subsection application_status_attr_reject_status ApplicationStatus/RejectStatus Attribute ++ ++**MQTT Topic Pattern:** ++ ++``` ++[PREFIX]/ApplicationStatus/Attributes/RejectStatus/Reported ++[PREFIX]/ApplicationStatus/Attributes/RejectStatus/Desired ++``` ++ ++**MQTT Payload JSON Schema:** ++ ++```json ++{ ++ "$schema": "http://json-schema.org/draft-07/schema#", ++ "title": "ApplicationStatus Cluster RejectStatus Attribute Properties", ++ "type": "object", ++ "properties": { ++ "value": { ++ "type": "boolean" ++ } ++ }, ++ "required": [ ++ "value" ++ ] ++} ++``` ++ ++ ++**Example Mosquitto CLI Tool Usage** ++ ++To see desired/reported value for RejectStatus attribute under the by-unid topic space: ++ ++```console ++mosquitto_sub -t 'ucl/by-unid/+/+/ApplicationStatus/Attributes/RejectStatus/+' ++ ++# Example output ++ ++ucl/by-unid//ep0/ApplicationStatus/Attributes/RejectStatus/Desired { "value": } ++ucl/by-unid//ep0/ApplicationStatus/Attributes/RejectStatus/Reported { "value": } ++ ++``` ++ ++

++ ++ ++\subsection application_status_attr_cluster_revision ApplicationStatus/ClusterRevision Attribute ++ ++**MQTT Topic Pattern:** ++ ++``` ++[PREFIX]/ApplicationStatus/Attributes/ClusterRevision/Reported ++[PREFIX]/ApplicationStatus/Attributes/ClusterRevision/Desired ++``` ++ ++**MQTT Payload JSON Schema:** ++ ++```json ++{ ++ "$schema": "http://json-schema.org/draft-07/schema#", ++ "title": "ApplicationStatus Cluster ClusterRevision Attribute Properties", ++ "type": "object", ++ "properties": { ++ "value": { ++ "type": "integer" ++ } ++ }, ++ "required": [ ++ "value" ++ ] ++} ++``` ++ ++**Example Mosquitto CLI Tool Usage** ++ ++To see desired/reported value for ClusterRevision attribute under the by-unid topic space: ++ ++```console ++mosquitto_sub -t 'ucl/by-unid///ApplicationStatus/Attributes/ClusterRevision/+' ++# Example output ++ucl/by-unid///ApplicationStatus/Attributes/ClusterRevision/Desired { "value": } ++ucl/by-unid///ApplicationStatus/Attributes/ClusterRevision/Reported { "value": } ++``` ++ ++ ++ ++ ++ ++

++ ++ ++ ++ ++\section application_status_recv_cmd_support ApplicationStatus Command Support ++ ++**MQTT Topic Pattern:** ++ ++``` ++[PREFIX]/ApplicationStatus/SupportedCommands ++[PREFIX]/ApplicationStatus/SupportedGeneratedCommands ++``` ++ ++**MQTT Payload JSON Schema:** ++ ++```json ++{ ++ "$schema": "http://json-schema.org/draft-07/schema#", ++ "title": "ApplicationStatus Command Support Properties", ++ "type": "object", ++ "properties": { ++ "value": { ++ "type": "array", ++ "items" : { ++ "type": "string", ++ "enum": [ ++ ] ++ } ++ } ++ } ++ }, ++ "required": [ ++ "value" ++ ] ++} ++``` ++ ++**Example Mosquitto CLI Tool Usage** ++ ++To see supported commands for ApplicationStatus cluster under the by-unid topic space: ++ ++```console ++mosquitto_sub -t 'ucl/by-unid///ApplicationStatus/SupportedCommands' ++# Example output ++ucl/by-unid///ApplicationStatus/SupportedCommands { "value": [] } ++``` ++ ++To see supported generated commands for ApplicationStatus cluster under the by-unid topic space: ++ ++```console ++mosquitto_sub -t 'ucl/by-unid///ApplicationStatus/SupportedGeneratedCommands' ++# Example output ++ucl/by-unid///ApplicationStatus/SupportedGeneratedCommands { "value": [] } ++``` ++ ++ ++ ++ ++ ++

++ ++ ++ ++ ++\section application_status_cmds ApplicationStatus Commands ++ ++

++ ++\subsection application_status_write_attr_cmd ApplicationStatus/WriteAttributes Command ++ ++**MQTT Topic Pattern:** ++ ++``` ++[PREFIX]/ApplicationStatus/Commands/WriteAttributes ++``` ++ ++**MQTT Payload JSON Schema:** ++ ++```json ++{ ++ "$schema": "http://json-schema.org/draft-07/schema#", ++ "title": "ApplicationStatus Cluster WriteAttributes Command Properties", ++ "type": "object", ++ "properties": { ++ }, ++ "required": [ ++ "value" ++ ] ++} ++``` ++ ++**Example Mosquitto CLI Tool Usage** ++ ++To update all ApplicationStatus attributes under the by-unid topic space: ++ ++```console ++mosquitto_pub -t 'ucl/by-unid///ApplicationStatus/Commands/WriteAttributes' -m '{ }' ++``` ++ ++> NOTE: Specify only the list of attributes to write in this command. ++> Unspecified attributes will not be updated. ++ ++

++ ++\subsection application_status_force_read_attr_cmd ApplicationStatus/ForceReadAttributes Command ++ ++**MQTT Topic Pattern:** ++ ++``` ++[PREFIX]/ApplicationStatus/Commands/ForceReadAttributes ++``` ++ ++**MQTT Payload JSON Schema:** ++ ++```json ++{ ++ "$schema": "http://json-schema.org/draft-07/schema#", ++ "title": "ApplicationStatus Cluster ForceReadAttributes Command Properties", ++ "type": "object", ++ "properties": { ++ "value": { ++ "type": "array" ++ "items": { ++ "type": "string", ++ "enum": [ ++ "BusyStatus", ++ "WaitTime", ++ "RejectStatus" ++ ] ++ } ++ } ++ }, ++ "required": [ ++ "value" ++ ] ++} ++``` ++ ++**Example Mosquitto CLI Tool Usage** ++ ++To force read all ApplicationStatus attributes under the by-unid topic space (by sending an empty array): ++ ++```console ++mosquitto_pub -t 'ucl/by-unid///ApplicationStatus/Commands/ForceReadAttributes' -m '{ "value": [] }' ++``` ++ ++To force read one of the ApplicationStatus attributes under the by-unid topic space: ++ ++```console ++mosquitto_pub -t 'ucl/by-unid///ApplicationStatus/Commands/ForceReadAttributes' -m '{ "value": ["BusyStatus"] }' ++``` ++ ++ ++ ++ ++ + +


+ +@@ -55114,6 +55468,30 @@ mosquitto_pub -t 'ucl/by-unid///UnifyHumidityControl/Commands/ForceRea + +

+ ++ ++ ++ ++\section enum_application_status_busy_status ApplicationStatusBusyStatus Enum ++ ++```json ++{ ++ "$schema": "http://json-schema.org/draft-07/schema#", ++ "title": "ApplicationStatusBusyStatus Enum Properties", ++ "type": "string", ++ "enum": [ ++ "Try again later", ++ "Try again in WaitTime seconds", ++ "Request queued" ++ ] ++} ++``` ++ ++ ++ ++ ++ ++

++ + + + +diff --git a/components/uic_dotdot/zap-generated/src/dotdot_attribute_id_definitions.c b/components/uic_dotdot/zap-generated/src/dotdot_attribute_id_definitions.c +index badc743381..7acea602d2 100644 +--- a/components/uic_dotdot/zap-generated/src/dotdot_attribute_id_definitions.c ++++ b/components/uic_dotdot/zap-generated/src/dotdot_attribute_id_definitions.c +@@ -2102,6 +2102,21 @@ const char *uic_dotdot_get_attribute_name(dotdot_cluster_id_t cluster_id, + return "Unknown"; + } + // clang-format off ++ case DOTDOT_APPLICATION_STATUS_CLUSTER_ID: ++ // clang-format on ++ switch (attribute_id) { ++ // clang-format off ++ case DOTDOT_APPLICATION_STATUS_BUSY_STATUS_ATTRIBUTE_ID: ++ return "BusyStatus"; ++ case DOTDOT_APPLICATION_STATUS_WAIT_TIME_ATTRIBUTE_ID: ++ return "WaitTime"; ++ case DOTDOT_APPLICATION_STATUS_REJECT_STATUS_ATTRIBUTE_ID: ++ return "RejectStatus"; ++ // clang-format on ++ default: ++ return "Unknown"; ++ } ++ // clang-format off + // clang-format on + default: + return "Unknown"; +@@ -4567,6 +4582,17 @@ dotdot_attribute_id_t + return DOTDOT_UNIFY_HUMIDITY_CONTROL_AUTO_SETPOINT_PRECISION_ATTRIBUTE_ID; + } + break; ++ case DOTDOT_APPLICATION_STATUS_CLUSTER_ID: ++ if (strcmp ("BusyStatus", attribute_name) == 0) { ++ return DOTDOT_APPLICATION_STATUS_BUSY_STATUS_ATTRIBUTE_ID; ++ } ++ if (strcmp ("WaitTime", attribute_name) == 0) { ++ return DOTDOT_APPLICATION_STATUS_WAIT_TIME_ATTRIBUTE_ID; ++ } ++ if (strcmp ("RejectStatus", attribute_name) == 0) { ++ return DOTDOT_APPLICATION_STATUS_REJECT_STATUS_ATTRIBUTE_ID; ++ } ++ break; + default: + return DOTDOT_INVALID_ATTRIBUTE_ID; + } +@@ -6661,6 +6687,21 @@ dotdot_attribute_json_type_t + return JSON_TYPE_UNKNOWN; + } + // clang-format off ++ case DOTDOT_APPLICATION_STATUS_CLUSTER_ID: ++ // clang-format on ++ switch (attribute_id) { ++ // clang-format off ++ case DOTDOT_APPLICATION_STATUS_BUSY_STATUS_ATTRIBUTE_ID: ++ return JSON_TYPE_NUMBER; ++ case DOTDOT_APPLICATION_STATUS_WAIT_TIME_ATTRIBUTE_ID: ++ return JSON_TYPE_NUMBER; ++ case DOTDOT_APPLICATION_STATUS_REJECT_STATUS_ATTRIBUTE_ID: ++ return JSON_TYPE_BOOL; ++ // clang-format on ++ default: ++ return JSON_TYPE_UNKNOWN; ++ } ++ // clang-format off + // clang-format on + default: + return JSON_TYPE_UNKNOWN; +@@ -7024,5 +7065,11 @@ bool uic_dotdot_attribute_is_enum(dotdot_cluster_id_t cluster_id, + } + } + ++ if (65442 == cluster_id) { ++ if (0 == attribute_id) { ++ return true; ++ } ++ } ++ + return false; + } +diff --git a/components/uic_dotdot/zap-generated/src/dotdot_cluster_id_definitions.c b/components/uic_dotdot/zap-generated/src/dotdot_cluster_id_definitions.c +index 8699e827dc..53f13778eb 100644 +--- a/components/uic_dotdot/zap-generated/src/dotdot_cluster_id_definitions.c ++++ b/components/uic_dotdot/zap-generated/src/dotdot_cluster_id_definitions.c +@@ -134,6 +134,8 @@ const char* uic_dotdot_get_cluster_name(dotdot_cluster_id_t cluster_id) { + return "UnifyThermostat"; + case DOTDOT_UNIFY_HUMIDITY_CONTROL_CLUSTER_ID: + return "UnifyHumidityControl"; ++ case DOTDOT_APPLICATION_STATUS_CLUSTER_ID: ++ return "ApplicationStatus"; + default: + return "Unknown"; + } +@@ -314,6 +316,9 @@ dotdot_cluster_id_t uic_dotdot_get_cluster_id(const char* cluster_name) { + if (strcmp ("UnifyHumidityControl", cluster_name) == 0) { + return DOTDOT_UNIFY_HUMIDITY_CONTROL_CLUSTER_ID; + } ++ if (strcmp ("ApplicationStatus", cluster_name) == 0) { ++ return DOTDOT_APPLICATION_STATUS_CLUSTER_ID; ++ } + + // Return an invalid ID if we did not get any match. + return DOTDOT_INVALID_CLUSTER_ID; +-- +2.39.5 + From fd9b415e380a5781de7d892374f037b76ff542eb Mon Sep 17 00:00:00 2001 From: Viet Date: Tue, 1 Oct 2024 14:19:46 +0700 Subject: [PATCH 2/5] UIC-3275: Application Status CC Origin: https://github.com/SiliconLabsSoftware/z-wave-protocol-controller/pull/147 Signed-off-by: Philippe Coval --- .../attribute_store_defined_attribute_types.h | 17 ++ ...e_command_class_application_status_types.h | 36 +++ .../zpc_attribute_store_type_registration.cpp | 8 + .../zwave_command_classes/CMakeLists.txt | 1 + ...zwave_command_class_application_status.cpp | 263 ++++++++++++++++++ .../zwave_command_class_application_status.h | 102 +++++++ .../src/zwave_command_classes_fixt.c | 2 + .../zwave_command_classes/test/CMakeLists.txt | 19 ++ ..._command_class_application_status_test.cpp | 170 +++++++++++ 9 files changed, 618 insertions(+) create mode 100644 applications/zpc/components/zpc_attribute_store/include/command_class_types/zwave_command_class_application_status_types.h create mode 100644 applications/zpc/components/zwave_command_classes/src/zwave_command_class_application_status.cpp create mode 100644 applications/zpc/components/zwave_command_classes/src/zwave_command_class_application_status.h create mode 100644 applications/zpc/components/zwave_command_classes/test/zwave_command_class_application_status_test.cpp diff --git a/applications/zpc/components/zpc_attribute_store/include/attribute_store_defined_attribute_types.h b/applications/zpc/components/zpc_attribute_store/include/attribute_store_defined_attribute_types.h index 8d5e36adb..e855978df 100644 --- a/applications/zpc/components/zpc_attribute_store/include/attribute_store_defined_attribute_types.h +++ b/applications/zpc/components/zpc_attribute_store/include/attribute_store_defined_attribute_types.h @@ -1113,6 +1113,23 @@ DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_SOUND_SWITCH_TONE_INFO_NAME, DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_SOUND_SWITCH_TONE_PLAY, ((COMMAND_CLASS_SOUND_SWITCH << 8) | 0x08)) +///////////////////////////////////////////////// +// Application Status Command Class +///< This represents the version of the Sound Switch Command class. +/// zwave_cc_version_t +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_APPLICATION_STATUS_VERSION, + ZWAVE_CC_VERSION_ATTRIBUTE(COMMAND_CLASS_APPLICATION_STATUS)) +// Application Status Busy Status +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_APPLICATION_STATUS_BUSY_STATUS, + ((COMMAND_CLASS_APPLICATION_STATUS << 8) | 0x02)) +// Application Status Busy Wait time +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_APPLICATION_STATUS_WAIT_TIME, + ((COMMAND_CLASS_APPLICATION_STATUS << 8) | 0x03)) +// Application Status Reject Status +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_APPLICATION_STATUS_REJECT_STATUS, + ((COMMAND_CLASS_APPLICATION_STATUS << 8) | 0x04)) + + ///////////////////////////////////////////////// // Z-Wave Plus Info CC DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_ZWAVEPLUS_INFO_VERSION, diff --git a/applications/zpc/components/zpc_attribute_store/include/command_class_types/zwave_command_class_application_status_types.h b/applications/zpc/components/zpc_attribute_store/include/command_class_types/zwave_command_class_application_status_types.h new file mode 100644 index 000000000..9986f1435 --- /dev/null +++ b/applications/zpc/components/zpc_attribute_store/include/command_class_types/zwave_command_class_application_status_types.h @@ -0,0 +1,36 @@ +/****************************************************************************** + * # License + * Copyright 2021 Silicon Laboratories Inc. www.silabs.com + ****************************************************************************** + * The licensor of this software is Silicon Laboratories Inc. Your use of this + * software is governed by the terms of Silicon Labs Master Software License + * Agreement (MSLA) available at + * www.silabs.com/about-us/legal/master-software-license-agreement. This + * software is distributed to you in Source Code format and is governed by the + * sections of the MSLA applicable to Source Code. + * + *****************************************************************************/ + + +#ifndef ZWAVE_COMMAND_CLASS_APPLICATION_STATUS_TYPES_H +#define ZWAVE_COMMAND_CLASS_APPLICATION_STATUS_TYPES_H + +#include + +///> Application busy status. uint8_t +typedef uint8_t application_busy_status; + +///> Application busy wait time. uint8_t +typedef uint8_t application_busy_wait_time; + +///> Application reject request. uint8_t +typedef uint8_t application_reject_request; + +typedef enum { + TRY_AGAIN_LATER = 0, + TRY_AGAIN_WAIT_TIME = 1, + REQUEST_QUEUED = 2 +} application_busy_status_enum; + +#endif //ZWAVE_COMMAND_CLASS_APPLICATION_STATUS_TYPES_H +/** @} end zwave_command_class_application_status_types */ diff --git a/applications/zpc/components/zpc_attribute_store/src/zpc_attribute_store_type_registration.cpp b/applications/zpc/components/zpc_attribute_store/src/zpc_attribute_store_type_registration.cpp index 574642769..09e91eb10 100644 --- a/applications/zpc/components/zpc_attribute_store/src/zpc_attribute_store_type_registration.cpp +++ b/applications/zpc/components/zpc_attribute_store/src/zpc_attribute_store_type_registration.cpp @@ -514,6 +514,14 @@ static const std::vector attribute_schema = { {ATTRIBUTE_COMMAND_CLASS_SOUND_SWITCH_TONE_INFO_NAME, "Tone Info Name", ATTRIBUTE_COMMAND_CLASS_SOUND_SWITCH_TONE_INFO_IDENTIFIER, C_STRING_STORAGE_TYPE}, {ATTRIBUTE_COMMAND_CLASS_SOUND_SWITCH_TONE_PLAY, "Tone Play", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE}, + ///////////////////////////////////////////////////////////////////// + // Application Status Command Class attributes + ///////////////////////////////////////////////////////////////////// + {ATTRIBUTE_COMMAND_CLASS_APPLICATION_STATUS_VERSION, "Application Status Version", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE}, + {ATTRIBUTE_COMMAND_CLASS_APPLICATION_STATUS_BUSY_STATUS, "Application Busy Status", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE}, + {ATTRIBUTE_COMMAND_CLASS_APPLICATION_STATUS_WAIT_TIME, "Application Busy Wait Time", ATTRIBUTE_COMMAND_CLASS_APPLICATION_STATUS_BUSY_STATUS, U8_STORAGE_TYPE}, + {ATTRIBUTE_COMMAND_CLASS_APPLICATION_STATUS_REJECT_STATUS, "Application Reject Status", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE}, + //DoorLock Command Class attributes {ATTRIBUTE_DOOR_LOCK_CONFIGURATION_OPERATION_TYPE, "Door Lock Configuration Operation Type", ATTRIBUTE_ENDPOINT_ID, I32_STORAGE_TYPE }, {ATTRIBUTE_DOOR_LOCK_CONFIGURATION_INSIDE_DOOR_HANDLES_STATE, "Door Lock Configuration Inside Handles State", ATTRIBUTE_ENDPOINT_ID, I32_STORAGE_TYPE }, diff --git a/applications/zpc/components/zwave_command_classes/CMakeLists.txt b/applications/zpc/components/zwave_command_classes/CMakeLists.txt index 99c75db5f..6d2e8b67c 100644 --- a/applications/zpc/components/zwave_command_classes/CMakeLists.txt +++ b/applications/zpc/components/zwave_command_classes/CMakeLists.txt @@ -10,6 +10,7 @@ add_library( platform/${COMPATIBLE_PLATFORM}/platform_exec.c src/zwave_command_class_agi.cpp src/zwave_command_class_alarm_sensor.c + src/zwave_command_class_application_status.cpp src/zwave_command_class_association.cpp src/zwave_command_class_association_send.cpp src/zwave_command_class_basic.c diff --git a/applications/zpc/components/zwave_command_classes/src/zwave_command_class_application_status.cpp b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_application_status.cpp new file mode 100644 index 000000000..ce4a66475 --- /dev/null +++ b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_application_status.cpp @@ -0,0 +1,263 @@ +/****************************************************************************** + * # License + * Copyright 2024 Silicon Laboratories Inc. www.silabs.com + ****************************************************************************** + * The licensor of this software is Silicon Laboratories Inc. Your use of this + * software is governed by the terms of Silicon Labs Master Software License + * Agreement (MSLA) available at + * www.silabs.com/about-us/legal/master-software-license-agreement. This + * software is distributed to you in Source Code format and is governed by the + * sections of the MSLA applicable to Source Code. + * + *****************************************************************************/ +// Includes from this component +#include "zwave_command_class_application_status.h" +#include "zwave_command_class_application_status_types.h" +#include "zwave_command_classes_utils.h" + +// Generic includes +#include +#include + +// Includes from other ZPC Components +#include "zwave_command_class_indices.h" +#include "zwave_command_handler.h" +#include "zpc_attribute_store_network_helper.h" +#include "attribute_store_defined_attribute_types.h" +#include "ZW_classcmd.h" +#include "zpc_attribute_resolver.h" + +// Includes from other Unify Components +#include "dotdot_mqtt.h" +#include "dotdot_mqtt_generated_commands.h" +#include "attribute_store_helper.h" +#include "attribute_store.h" +#include "attribute_resolver.h" +#include "attribute_timeouts.h" +#include "sl_log.h" + +// Cpp include +#include "attribute.hpp" +#include "zwave_frame_generator.hpp" +#include "zwave_frame_parser.hpp" + +// Attribute macro, shortening those long defines for attribute types: +#define ATTRIBUTE(type) ATTRIBUTE_COMMAND_CLASS_APPLICATION_STATUS_##type + +// Log tag +constexpr char LOG_TAG[] = "zwave_command_class_application_status"; + +namespace +{ +zwave_frame_generator frame_generator(COMMAND_CLASS_APPLICATION_STATUS); +} + +/////////////////////////////////////////////////////////////////////////////// +// Helper functions +/////////////////////////////////////////////////////////////////////////////// +zwave_cc_version_t + get_current_application_status_version(attribute_store_node_t node) +{ + zwave_cc_version_t version = zwave_command_class_get_version_from_node( + node, + COMMAND_CLASS_APPLICATION_STATUS); + + if (version == 0) { + sl_log_error(LOG_TAG, "Application_status Command Class Version not found"); + } + + return version; +} + +/////////////////////////////////////////////////////////////////////////////// +// Validation function +/////////////////////////////////////////////////////////////////////////////// +bool zwave_command_class_application_status_validate_reject_value(uint8_t value) +{ + return (value == 0x00 || value == 0x01); +} + +/////////////////////////////////////////////////////////////////////////////// +// Frame parsing functions +/////////////////////////////////////////////////////////////////////////////// +static sl_status_t zwave_command_class_application_status_handle_status_busy( + const zwave_controller_connection_info_t *connection_info, + const uint8_t *frame_data, + uint16_t frame_length) +{ + // Setup + attribute_store::attribute endpoint_node( + zwave_command_class_get_endpoint_node(connection_info)); + + sl_log_debug(LOG_TAG, + "Application Status Report Application Busy frame received"); + + // Compute expected size for report frame + const uint8_t expected_size = sizeof(ZW_APPLICATION_BUSY_FRAME); + + // Parse the frame + try { + zwave_frame_parser parser(frame_data, frame_length); + + // Validate frame size with desired size + if (!parser.is_frame_size_valid(expected_size)) { + sl_log_error(LOG_TAG, + "Invalid frame size for Application Status Report " + "Application Busy frame"); + return SL_STATUS_FAIL; + } + + // Create the busy status attributes if not exists + auto busy_status_node = endpoint_node.emplace_node(ATTRIBUTE(BUSY_STATUS)); + + //Read Status value from report frame and store attribute + parser.read_byte(busy_status_node); + + // Create the wait time attributes if not exists, Read Wait time from report frame store attribute + parser.read_byte(busy_status_node.emplace_node(ATTRIBUTE(WAIT_TIME))); + + } catch (const std::exception &e) { + sl_log_error(LOG_TAG, + "Error while parsing Application Status Report Application " + "Busy frame : %s", + e.what()); + return SL_STATUS_FAIL; + } + return SL_STATUS_OK; +} + +static sl_status_t zwave_command_class_application_status_handle_status_reject( + const zwave_controller_connection_info_t *connection_info, + const uint8_t *frame_data, + uint16_t frame_length) +{ + // Setup + attribute_store::attribute endpoint_node( + zwave_command_class_get_endpoint_node(connection_info)); + + sl_log_debug(LOG_TAG, + "Application Status Report Application Reject frame received"); + + // Compute expected size for report frame + const uint8_t expected_size = sizeof(ZW_APPLICATION_REJECTED_REQUEST_FRAME); + + // Parse the frame + try { + zwave_frame_parser parser(frame_data, frame_length); + + // Validate frame size with desired size + if (!parser.is_frame_size_valid(expected_size)) { + sl_log_error(LOG_TAG, + "Invalid frame size for Application Status Report " + "Application Reject frame"); + return SL_STATUS_FAIL; + } + + //Read Status and store attribute + application_busy_status status = parser.read_byte(); + sl_log_debug(LOG_TAG, "Application Status Reject Status : %d", status); + + if (!zwave_command_class_application_status_validate_reject_value(status)) { + return SL_STATUS_FAIL; + } + + // Create the reject status attributes if not exists + auto reject_status_node + = endpoint_node.emplace_node(ATTRIBUTE(REJECT_STATUS)); + reject_status_node.set_reported(status); + + } catch (const std::exception &e) { + sl_log_error(LOG_TAG, + "Error while parsing Application Status Report Application " + "Reject frame : %s", + e.what()); + return SL_STATUS_FAIL; + } + return SL_STATUS_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// Incoming commands handler +/////////////////////////////////////////////////////////////////////////////// +sl_status_t zwave_command_class_application_status_control_handler( + const zwave_controller_connection_info_t *connection_info, + const uint8_t *frame_data, + uint16_t frame_length) +{ + // Frame too short, it should have not come here. + if (frame_length <= COMMAND_INDEX) { + return SL_STATUS_NOT_SUPPORTED; + } + + switch (frame_data[COMMAND_INDEX]) { + case APPLICATION_BUSY: + return zwave_command_class_application_status_handle_status_busy( + connection_info, + frame_data, + frame_length); + case APPLICATION_REJECTED_REQUEST: + return zwave_command_class_application_status_handle_status_reject( + connection_info, + frame_data, + frame_length); + default: + return SL_STATUS_NOT_SUPPORTED; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Attribute Store callback functions +/////////////////////////////////////////////////////////////////////////////// +static void zwave_command_class_application_status_on_version_attribute_update( + attribute_store_node_t updated_node, attribute_store_change_t change) +{ + if (change == ATTRIBUTE_DELETED) { + return; + } + + // Confirm that we have a version attribute update + assert(ATTRIBUTE(VERSION) == attribute_store_get_node_type(updated_node)); + + attribute_store::attribute version_node(updated_node); + // Do not create the attributes until we are sure of the version + zwave_cc_version_t supporting_node_version = 0; + + // Wait for the version + if (!version_node.reported_exists()) { + return; + } + supporting_node_version = version_node.reported(); + + // Wait that the version becomes non-zero. + if (supporting_node_version == 0) { + return; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Public interface functions +/////////////////////////////////////////////////////////////////////////////// +sl_status_t zwave_command_class_application_status_init() +{ + // Attribute store callbacks + attribute_store_register_callback_by_type( + zwave_command_class_application_status_on_version_attribute_update, + ATTRIBUTE(VERSION)); + + // The support side of things: Register our handler to the Z-Wave CC framework: + zwave_command_handler_t handler = {}; + handler.support_handler = NULL; + handler.control_handler + = &zwave_command_class_application_status_control_handler; + // Not supported, so this does not really matter + handler.minimal_scheme = ZWAVE_CONTROLLER_ENCAPSULATION_NETWORK_SCHEME; + handler.manual_security_validation = false; + handler.command_class = COMMAND_CLASS_APPLICATION_STATUS; + handler.version = APPLICATION_STATUS_VERSION; + handler.command_class_name = "Application Status"; + handler.comments = ""; + + zwave_command_handler_register_handler(handler); + + return SL_STATUS_OK; +} diff --git a/applications/zpc/components/zwave_command_classes/src/zwave_command_class_application_status.h b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_application_status.h new file mode 100644 index 000000000..a9310e411 --- /dev/null +++ b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_application_status.h @@ -0,0 +1,102 @@ +/****************************************************************************** + * # License + * Copyright 2021 Silicon Laboratories Inc. www.silabs.com + ****************************************************************************** + * The licensor of this software is Silicon Laboratories Inc. Your use of this + * software is governed by the terms of Silicon Labs Master Software License + * Agreement (MSLA) available at + * www.silabs.com/about-us/legal/master-software-license-agreement. This + * software is distributed to you in Source Code format and is governed by the + * sections of the MSLA applicable to Source Code. + * + *****************************************************************************/ + +/** + * @defgroup binary_switch_command_class Binary Switch Command Class + * @ingroup command_classes + * @brief Binary Switch Command Class handlers and control functions + * + * This module implement functions for generating and parsing the Z-Wave frames + * for controlling the Binary Switch Command Class. + * + * The data model used for this command class is tailored to be mapped to the + * ZCL Level cluster. + * + * The State attribute is a hook for the Set / Get rule registrations. The + * actual values being set and resolved are the duration and the value. + * If the duration and/or the value needs a resolution, the Command Class + * handler adjusts the state in order to trigger the resolver: + * + * - state = reported [] for a get resolution + * - state = desired [1] reported [0] for a set resolution + * - state = desired [0] reported [0] for a no resolution + * +@startuml{attribute_store_binary_switch_command_class.png} "Binary Switch data model" width=10cm +title Binary Switch data model +allow_mixing +skinparam objectBorderColor black + +legend top +ATTRIBUTE(type) : ATTRIBUTE_COMMAND_CLASS_BINARY_SWITCH_type +endlegend + +package "Attribute Store" <> { + object "NodeID" as node #f2ffe6 + node : Attribute Type = ATTRIBUTE_NODE_ID + node : value = Desired: [], Reported: [03] + + object "Endpoint Attribute" as endpoint #e6fff7 + endpoint : Attribute Type = ATTRIBUTE_ENDPOINT_ID + endpoint : value = Desired: [] - Reported: [04] + + object "Version" as version #FEFEFE + version : Attribute Type = ATTRIBUTE(VERSION) + version : value = Desired: [] - Reported: [2] + + object "State" as state #FFFFFF + state : Attribute Type = ATTRIBUTE(STATE) + state : value = Desired: [1] - Reported: [0] + + object "Value" as value #FFFFFF + value : Attribute Type = ATTRIBUTE(VALUE) + value : value = Desired: [0x00], Reported: [0xFF] + + object "Duration" as duration #FFFFFF + duration : Attribute Type = ATTRIBUTE(DURATION) + duration : value = Desired: [10], Reported: [10] +} + +node *-- endpoint +endpoint *-- version +endpoint *-- state +state *-- value +state *-- duration + +@enduml + * + * @{ +*/ + +#ifndef ZWAVE_COMMAND_CLASS_APPLICATION_STATUS_H +#define ZWAVE_COMMAND_CLASS_APPLICATION_STATUS_H + +#include "sl_status.h" +#include "attribute_store.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief This function initialize the Application Status Command Class handler + * + * @return SL_STATUS_OK on success, any other error code for an error. + */ +sl_status_t zwave_command_class_application_status_init(); + +#ifdef __cplusplus +} +#endif + +#endif //ZWAVE_COMMAND_CLASS_APPLICATION_STATUS_H +/** @} end zwave_command_class_application_status */ diff --git a/applications/zpc/components/zwave_command_classes/src/zwave_command_classes_fixt.c b/applications/zpc/components/zwave_command_classes/src/zwave_command_classes_fixt.c index a20c668f3..59415879e 100644 --- a/applications/zpc/components/zwave_command_classes/src/zwave_command_classes_fixt.c +++ b/applications/zpc/components/zwave_command_classes/src/zwave_command_classes_fixt.c @@ -16,6 +16,7 @@ #include "zwave_command_class_association.h" #include "zwave_command_class_agi.h" #include "zwave_command_class_alarm_sensor.h" +#include "zwave_command_class_application_status.h" #include "zwave_command_class_barrier_operator.h" #include "zwave_command_class_binary_switch.h" #include "zwave_command_class_central_scene.h" @@ -127,6 +128,7 @@ sl_status_t zwave_command_classes_init() status |= zwave_command_class_time_init(); status |= zwave_command_class_transport_service_init(); status |= zwave_command_class_user_code_init(); + status |= zwave_command_class_application_status_init(); status |= zwave_command_class_version_init(); status |= zwave_command_class_wake_up_init(); status |= zwave_command_class_zwave_plus_info_init(); diff --git a/applications/zpc/components/zwave_command_classes/test/CMakeLists.txt b/applications/zpc/components/zwave_command_classes/test/CMakeLists.txt index 9d31a4368..05206931e 100644 --- a/applications/zpc/components/zwave_command_classes/test/CMakeLists.txt +++ b/applications/zpc/components/zwave_command_classes/test/CMakeLists.txt @@ -482,6 +482,25 @@ target_add_unittest( zwave_tx_mock uic_dotdot_mqtt_mock) +# Z-Wave Application Status Command Class test +target_add_unittest( + zwave_command_classes + NAME + zwave_command_class_application_status_test + SOURCES + zwave_command_class_application_status_test.cpp + DEPENDS + zwave_command_class_test_helpers + zpc_attribute_store_test_helper + zwave_command_handler_mock + zwave_controller_mock + uic_attribute_resolver_mock + zpc_attribute_resolver_mock + zpc_config_mock + zwave_tx_scheme_selector_mock + zwave_tx_mock + uic_dotdot_mqtt_mock) + # Z-Wave Firmware Update functionality test target_add_unittest( zwave_command_classes diff --git a/applications/zpc/components/zwave_command_classes/test/zwave_command_class_application_status_test.cpp b/applications/zpc/components/zwave_command_classes/test/zwave_command_class_application_status_test.cpp new file mode 100644 index 000000000..fefb98c15 --- /dev/null +++ b/applications/zpc/components/zwave_command_classes/test/zwave_command_class_application_status_test.cpp @@ -0,0 +1,170 @@ +/****************************************************************************** + * # License + * Copyright 2024 Silicon Laboratories Inc. www.silabs.com + ****************************************************************************** + * The licensor of this software is Silicon Laboratories Inc. Your use of this + * software is governed by the terms of Silicon Labs Master Software License + * Agreement (MSLA) available at + * www.silabs.com/about-us/legal/master-software-license-agreement. This + * software is distributed to you in Source Code format and is governed by the + * sections of the MSLA applicable to Source Code. + * + *****************************************************************************/ +// Base class +#include "zwave_command_class_application_status.h" +#include "zwave_command_class_application_status_types.h" +#include "zwave_command_classes_utils.h" +#include "unity.h" + +// Generic includes +#include + +// Unify +#include "datastore.h" +#include "attribute_store.h" +#include "attribute_store_fixt.h" +// Interface includes +#include "ZW_classcmd.h" + +// ZPC includes +#include "attribute_store_defined_attribute_types.h" +#include "zpc_attribute_store_type_registration.h" + +// Test helpers +#include "zwave_command_class_test_helper.hpp" + +// Includes from other Unify Components +#include "sl_log.h" +#include "attribute_store.h" + +// Attribute macro, shortening those long defines for attribute types: +#define ATTRIBUTE(type) ATTRIBUTE_COMMAND_CLASS_APPLICATION_STATUS_##type + +// Log tag +constexpr char LOG_TAG[] = "zwave_command_class_application_status"; + +using namespace zwave_command_class_test_helper; + +extern "C" { + +/// Setup the test suite (called once before all test_xxx functions are called) +void suiteSetUp() +{ + datastore_init(":memory:"); + attribute_store_init(); + zpc_attribute_store_register_known_attribute_types(); +} + +/// Teardown the test suite (called once after all test_xxx functions are called) +int suiteTearDown(int num_failures) +{ + attribute_store_teardown(); + datastore_teardown(); + return num_failures; +} + +// Tested command class handler +const zwave_struct_handler_args command_class_handler + = {.command_class_id = COMMAND_CLASS_APPLICATION_STATUS, + .supported_version = APPLICATION_STATUS_VERSION}; +// Get Set function map +const resolver_function_map attribute_bindings = {}; + +/// Called before each and every test +void setUp() +{ + zwave_setUp(command_class_handler, + &zwave_command_class_application_status_init, + attribute_bindings); +} + +/////////////////////////////////////////////////////////////////////////////// +// Internal helpers +/////////////////////////////////////////////////////////////////////////////// +attribute_store::attribute helper_get_busy_status_node() +{ + return helper_test_and_get_node(ATTRIBUTE(BUSY_STATUS)); +} + +attribute_store::attribute helper_get_wait_time_node() +{ + auto busy_status_node = helper_get_busy_status_node(); + return helper_test_and_get_node(ATTRIBUTE(WAIT_TIME), busy_status_node); +} + +attribute_store::attribute helper_get_reject_status_node() +{ + return helper_test_and_get_node(ATTRIBUTE(REJECT_STATUS)); +} + +/////////////////////////////////////////////////////////////////////////////// +// Test cases +/////////////////////////////////////////////////////////////////////////////// +void test_application_status_interview_v1_happy_case() +{ + helper_set_version(1); + + // Verify that we do not create node when version update + helper_test_node_does_not_exists(ATTRIBUTE(BUSY_STATUS)); + helper_test_node_does_not_exists(ATTRIBUTE(REJECT_STATUS)); +} + +void test_application_status_busy_command_happy_case() +{ + helper_set_version(1); + + uint8_t tested_status = 0x01; + uint8_t tested_wait_time = 0x05; + + helper_test_report_frame(APPLICATION_BUSY, {tested_status, tested_wait_time}); + + auto busy_status_node = helper_get_busy_status_node(); + sl_log_debug(LOG_TAG, "busy_status_node: %d", busy_status_node); + auto wait_time_node = helper_get_wait_time_node(); + sl_log_debug(LOG_TAG, "wait_time_node: %d", wait_time_node); + + // Verify that we create necessary nodes + helper_test_node_exists(ATTRIBUTE(BUSY_STATUS)); + helper_test_node_exists(ATTRIBUTE(WAIT_TIME), busy_status_node); + + // Verify that the application busy is updated + TEST_ASSERT_EQUAL_MESSAGE( + tested_status, + busy_status_node.reported(), + "Application Status Busy isn't updated after report"); + // Verify that the wait time is updated + TEST_ASSERT_EQUAL_MESSAGE( + tested_wait_time, + wait_time_node.reported(), + "Wait time isn't updated after report"); +} + +void test_application_status_reject_command_happy_case() +{ + helper_set_version(1); + + uint8_t tested_status = 0x01; + + helper_test_report_frame(APPLICATION_REJECTED_REQUEST, {tested_status}); + + auto reject_status_node = helper_get_reject_status_node(); + + // Verify that the application busy is updated + TEST_ASSERT_EQUAL_MESSAGE( + tested_status, + reject_status_node.reported(), + "Application Status Reject isn't updated after report"); +} + +void test_application_status_reject_command_invalid_status() +{ + helper_set_version(1); + + uint8_t tested_status = 0x02; + + helper_test_report_frame(APPLICATION_REJECTED_REQUEST, + {tested_status}, + SL_STATUS_FAIL); +} + +} // extern "C" \ No newline at end of file From 041c2fc33a9136dafbc1dfff1ab35265a46a442b Mon Sep 17 00:00:00 2001 From: Viet Date: Tue, 1 Oct 2024 14:21:40 +0700 Subject: [PATCH 3/5] UIC-3275: Custom Application Status cluster Origin: https://github.com/SiliconLabsSoftware/z-wave-protocol-controller/pull/147 Signed-off-by: Philippe Coval --- .../zwave_command_classes/src/zwave_command_classes_fixt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/zpc/components/zwave_command_classes/src/zwave_command_classes_fixt.c b/applications/zpc/components/zwave_command_classes/src/zwave_command_classes_fixt.c index 59415879e..be171ab7a 100644 --- a/applications/zpc/components/zwave_command_classes/src/zwave_command_classes_fixt.c +++ b/applications/zpc/components/zwave_command_classes/src/zwave_command_classes_fixt.c @@ -90,6 +90,7 @@ sl_status_t zwave_command_classes_init() // Note: AGI should stay first, it allows others to register commands. status |= zwave_command_class_agi_init(); status |= zwave_command_class_alarm_sensor_init(); + status |= zwave_command_class_application_status_init(); status |= zwave_command_class_association_init(); status |= zwave_command_class_barrier_operator_init(); status |= zwave_command_class_basic_init(); @@ -128,7 +129,6 @@ sl_status_t zwave_command_classes_init() status |= zwave_command_class_time_init(); status |= zwave_command_class_transport_service_init(); status |= zwave_command_class_user_code_init(); - status |= zwave_command_class_application_status_init(); status |= zwave_command_class_version_init(); status |= zwave_command_class_wake_up_init(); status |= zwave_command_class_zwave_plus_info_init(); From 36d6bf8e25fdaf613229ea2c46a7732d798d186e Mon Sep 17 00:00:00 2001 From: Viet Date: Tue, 1 Oct 2024 14:23:57 +0700 Subject: [PATCH 4/5] UIC-3275: UAM file for Application Status cluster Origin: https://github.com/SiliconLabsSoftware/z-wave-protocol-controller/pull/147 Signed-off-by: Philippe Coval --- .../dotdot_mapper/rules/ApplicationStatus.uam | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 applications/zpc/components/dotdot_mapper/rules/ApplicationStatus.uam diff --git a/applications/zpc/components/dotdot_mapper/rules/ApplicationStatus.uam b/applications/zpc/components/dotdot_mapper/rules/ApplicationStatus.uam new file mode 100644 index 000000000..be6dd1500 --- /dev/null +++ b/applications/zpc/components/dotdot_mapper/rules/ApplicationStatus.uam @@ -0,0 +1,23 @@ + +// Swtich Color Command Class +def zw_BUSY_STATUS 0x2202 +def zw_WAIT_TIME 0x2203 +def zw_REJECT_STATUS 0x2204 + +// ZCL Swich Color cluster attributes +def zb_BusyStatus 0xffa20000 +def zb_WaitTime 0xffa20001 +def zb_RejectStatus 0xffa20002 + + +scope 25 chain_reaction(0) { + + r'zw_BUSY_STATUS = r'zb_BusyStatus + r'zw_BUSY_STATUS.zw_WAIT_TIME = r'zb_WaitTime + r'zw_REJECT_STATUS = r'zb_RejectStatus + + + r'zb_BusyStatus = r'zw_BUSY_STATUS + r'zb_WaitTime = r'zw_BUSY_STATUS.zw_WAIT_TIME + r'zb_RejectStatus = r'zw_REJECT_STATUS +} From 02f734b9b14e97a63fa7033570a578b53a8e52bd Mon Sep 17 00:00:00 2001 From: ptphan Date: Mon, 4 Nov 2024 14:53:47 +0700 Subject: [PATCH 5/5] UIC-3275: Updated documentation for the Application Status command class. Origin: https://github.com/SiliconLabsSoftware/z-wave-protocol-controller/pull/147 Signed-off-by: Philippe Coval --- .../command_classes/application_status.rst | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 applications/zpc/doc/command_classes/application_status.rst diff --git a/applications/zpc/doc/command_classes/application_status.rst b/applications/zpc/doc/command_classes/application_status.rst new file mode 100644 index 000000000..34eb0f56e --- /dev/null +++ b/applications/zpc/doc/command_classes/application_status.rst @@ -0,0 +1,111 @@ +Application Status +============= + +Version supported : 1 + +.. contents:: + :depth: 2 + :local: + :backlinks: none + + +Interview process +***************** + + +Command Class Commands +********************** + +.. _color-switch-supported-get-command: + +Application Busy +--------------------------- + +Mapping between command and attribute store : + +.. list-table:: + :header-rows: 1 + + * - Report Field Command + - Z-Wave Attribute Store + * - ``Status`` + - ``BUSY_STATUS`` + * - ``Wait Time`` + - ``WAIT_TIME`` +Application Rejected Request +------------------------------ + +Mapping between Report command and attribute store : + +.. list-table:: + :header-rows: 1 + + * - Z-Wave Command Attribute + - Z-Wave Attribute Store + * - ``Status`` + - ``REJECT_STATUS`` + +.. note:: + The structure of the attribute store is : + + .. code:: text + + |__ BUSY_STATUS + | |__ WAIT_TIME + |__ REJECT_STATUS + +Unify Clusters +************** + +UAM files +--------- + +.. list-table:: + :header-rows: 1 + + * - UAM File + - Cluster + - Comments + * - ``ApplicationStatus.uam`` + - ``Unify_ApplicationStatus.xml`` + - Mapping between ApplicationStatus command class and ApplicationStatus cluster + +Bindings +-------- + +.. list-table:: + :header-rows: 1 + + * - Z-Wave Attribute Store + - Cluster attribute + - Comments + * - ``BUSY_STATUS`` + - ApplicationStatus BusyStatus + - Z-Wave <-> Cluster (Read only) + * - ``WAIT_TIME`` + - ApplicationStatus WaitTime + - Z-Wave <-> Cluster (Read only) + * - ``REJECT_STATUS`` + - ApplicationStatus RejectStatus + - Z-Wave <-> Cluster (Read only) + + +Command actions +--------------- + +.. list-table:: + :widths: 20 50 30 + :header-rows: 1 + + * - Action + - MQTT Topic + - Comments + * - Report BusyStatus + - ``ucl/by-unid//ep0/ApplicationStatus/Attributes/BusyStatus {"value" : "Try again later"}`` + - ``BusyStatus`` value: ``Try again later`` or ``Try again in Wait Time seconds`` or ``Request queued, executed later`` + * - Report WaitTime + - ``ucl/by-unid//ep0/ApplicationStatus/Attributes/WaitTime {"value" : 10}`` + - Number of second + * - Report RejectStatus + - ``ucl/by-unid//ep0/ApplicationStatus/Attributes/RejectStatus {"value" : false}`` + - ``false`` indicate that the received (supported) command has been rejected by the application at the receiving node \ No newline at end of file