diff --git a/applications/zpc/components/dotdot_mapper/rules/UnifySwitchAll_to_SwitchAllCC.uam b/applications/zpc/components/dotdot_mapper/rules/UnifySwitchAll_to_SwitchAllCC.uam new file mode 100644 index 000000000..5bcda9538 --- /dev/null +++ b/applications/zpc/components/dotdot_mapper/rules/UnifySwitchAll_to_SwitchAllCC.uam @@ -0,0 +1,35 @@ +// Switch All Command Class +def zwSWITCH_ALL_MODE 0x2702 +def zwSWITCH_ALL_ON_OFF 0x2703 + +// Unify Switch All Cluster +def zbSWITCH_ALL_MODE 0xFD200000 +def zbSWITCH_ALL_ON_OFF 0xFD200001 + +def zwave_no_switch_all (e'zwSWITCH_ALL_MODE == 0) + +// 25 is higher priority than other generic switches +scope 25 chain_reaction(0) { + + // Linking attributes zwave -> zigbee + r'zbSWITCH_ALL_MODE = r'zwSWITCH_ALL_MODE + d'zbSWITCH_ALL_MODE = d'zwSWITCH_ALL_MODE + r'zbSWITCH_ALL_ON_OFF = + if (zwave_no_switch_all) undefined + if (r'zwSWITCH_ALL_ON_OFF != 0) 1 0 + d'zbSWITCH_ALL_ON_OFF = + if (zwave_no_switch_all) undefined + if (d'zwSWITCH_ALL_ON_OFF != 0) 1 0 + + // Linking attributes zigbee -> zwave + r'zwSWITCH_ALL_MODE = if (zwave_no_switch_all) undefined r'zbSWITCH_ALL_MODE + d'zwSWITCH_ALL_MODE = if (zwave_no_switch_all) undefined d'zbSWITCH_ALL_MODE + d'zwSWITCH_ALL_ON_OFF = + if (zwave_no_switch_all) undefined + if (d'zbSWITCH_ALL_ON_OFF != 0) 1 0 + + r'zwSWITCH_ALL_ON_OFF = + if (zwave_no_switch_all) undefined + if (r'zbSWITCH_ALL_ON_OFF != 0) 1 0 +} + 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..33dc47c2b 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 @@ -1084,8 +1084,12 @@ DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_POWERLEVEL_VERSION, // All Switch Command Class ///< This represents the version of the All Switch Command class. /// zwave_cc_version_t -DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_ALL_SWITCH_VERSION, - ((COMMAND_CLASS_SWITCH_ALL << 8) | 0x01)) +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_SWITCH_ALL_VERSION, + ZWAVE_CC_VERSION_ATTRIBUTE(COMMAND_CLASS_SWITCH_ALL)) +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_SWITCH_ALL_MODE, + ((COMMAND_CLASS_SWITCH_ALL << 8) | 0x02)) +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_SWITCH_ALL_ON_OFF, + ((COMMAND_CLASS_SWITCH_ALL << 8) | 0x03)) ///////////////////////////////////////////////// // Sound Switch Command Class 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..26a5460ed 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 @@ -70,7 +70,9 @@ static const std::vector attribute_schema = { ///////////////////////////////////////////////////////////////////// // All Switch Command Class attributes ///////////////////////////////////////////////////////////////////// - {ATTRIBUTE_COMMAND_CLASS_ALL_SWITCH_VERSION, "All Switch Command Class version", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE}, + {ATTRIBUTE_COMMAND_CLASS_SWITCH_ALL_VERSION, "All Switch Command Class version", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE}, + {ATTRIBUTE_COMMAND_CLASS_SWITCH_ALL_MODE, "Mode", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE}, + {ATTRIBUTE_COMMAND_CLASS_SWITCH_ALL_ON_OFF, "Switch All", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE}, ///////////////////////////////////////////////////////////////////// // Association Group Information Command Class attributes ///////////////////////////////////////////////////////////////////// diff --git a/applications/zpc/components/zwave_command_classes/CMakeLists.txt b/applications/zpc/components/zwave_command_classes/CMakeLists.txt index 99c75db5f..533597a2f 100644 --- a/applications/zpc/components/zwave_command_classes/CMakeLists.txt +++ b/applications/zpc/components/zwave_command_classes/CMakeLists.txt @@ -46,6 +46,7 @@ add_library( src/zwave_command_class_supervision.c src/zwave_command_class_supervision_process.cpp src/zwave_command_class_sound_switch.c + src/zwave_command_class_switch_all.cpp src/zwave_command_class_switch_color.cpp src/zwave_command_class_switch_multilevel.c src/zwave_command_class_thermostat_mode.c diff --git a/applications/zpc/components/zwave_command_classes/src/zwave_command_class_switch_all.cpp b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_switch_all.cpp new file mode 100644 index 000000000..74fe267e2 --- /dev/null +++ b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_switch_all.cpp @@ -0,0 +1,344 @@ +/****************************************************************************** + * # 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_switch_all.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_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_SWITCH_ALL_##type + +// Log tag +constexpr char LOG_TAG[] = "zwave_command_class_switch_all"; + +enum class switch_all_mode_t : uint8_t { + DEVICE_EXCLUDED = 0x00, + DEVICE_ONLY_ON = 0x01, + DEVICE_ONLY_OFF = 0x02, + DEVICE_ON_OFF = 0xFF +}; + +constexpr uint8_t SWITCH_ALL_ON_VALUE = 1; +constexpr uint8_t SWITCH_ALL_OFF_VALUE = 0; + +namespace +{ +zwave_frame_generator frame_generator(COMMAND_CLASS_SWITCH_ALL); +} + +/////////////////////////////////////////////////////////////////////////////// +// Helper functions +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Resolution functions +/////////////////////////////////////////////////////////////////////////////// +static sl_status_t zwave_command_class_switch_all_set_on_off( + attribute_store_node_t node, uint8_t *frame, uint16_t *frame_length) +{ + try { + attribute_store::attribute value_node(node); + // Creating the frame + if (value_node.type() != ATTRIBUTE(ON_OFF)) { + return SL_STATUS_INVALID_TYPE; + } + auto mode = static_cast( + value_node.parent().child_by_type(ATTRIBUTE(MODE)).reported()); + bool send_command = false; + uint8_t zwave_command_id; + auto on_off_desired = value_node.desired(); + + switch (on_off_desired) { + case SWITCH_ALL_ON_VALUE: + zwave_command_id = SWITCH_ALL_ON; + send_command = (mode == switch_all_mode_t::DEVICE_ONLY_ON + || mode == switch_all_mode_t::DEVICE_ON_OFF); + break; + case SWITCH_ALL_OFF_VALUE: + zwave_command_id = SWITCH_ALL_OFF; + send_command = (mode == switch_all_mode_t::DEVICE_ONLY_OFF + || mode == switch_all_mode_t::DEVICE_ON_OFF); + break; + default: + return SL_STATUS_INVALID_RANGE; + } + if (!send_command) { + sl_log_debug(LOG_TAG, + "Not sending command %d since mode is %d", + zwave_command_id, + mode); + return SL_STATUS_OK; + } + return frame_generator.generate_no_args_frame(zwave_command_id, + frame, + frame_length); + } catch (const std::exception &e) { + sl_log_error(LOG_TAG, + "Error while generating switch_all Set frame : %s", + e.what()); + return SL_STATUS_FAIL; + } + + return SL_STATUS_OK; +} + +static sl_status_t zwave_command_class_switch_all_get( + attribute_store_node_t node, uint8_t *frame, uint16_t *frame_length) +{ + if (attribute_store_get_node_type(node) == ATTRIBUTE(MODE)) { + return frame_generator.generate_no_args_frame(SWITCH_ALL_GET, + frame, + frame_length); + } + return SL_STATUS_INVALID_TYPE; +} + +static sl_status_t zwave_command_class_switch_all_set( + attribute_store_node_t node, uint8_t *frame, uint16_t *frame_length) +{ + try { + attribute_store::attribute value_node(node); + // Creating the frame + if (value_node.type() != ATTRIBUTE(MODE)) { + return SL_STATUS_INVALID_TYPE; + } + frame_generator.initialize_frame(SWITCH_ALL_SET, frame, 3); + frame_generator.add_value(value_node, DESIRED_ATTRIBUTE); + frame_generator.validate_frame(frame_length); + } catch (const std::exception &e) { + sl_log_error(LOG_TAG, + "Error while generating switch_all Set frame : %s", + e.what()); + return SL_STATUS_FAIL; + } + + return SL_STATUS_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// Frame parsing functions +/////////////////////////////////////////////////////////////////////////////// +static sl_status_t zwave_command_class_switch_all_handle_report( + 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, "Switch All Report frame received"); + + // Compute expected size for report frame + const uint8_t expected_size = 3; + + // Parse the frame + try { + zwave_frame_parser parser(frame_data, frame_length); + + if (!parser.is_frame_size_valid(expected_size)) { + sl_log_error(LOG_TAG, "Invalid frame size for switch_all Report frame"); + return SL_STATUS_FAIL; + } + + attribute_store::attribute mode + = endpoint_node.child_by_type(ATTRIBUTE(MODE)); + parser.read_byte(mode); + + } catch (const std::exception &e) { + sl_log_error(LOG_TAG, + "Error while parsing switch_all Report frame : %s", + e.what()); + return SL_STATUS_FAIL; + } + + return SL_STATUS_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// Incoming commands handler +/////////////////////////////////////////////////////////////////////////////// +sl_status_t zwave_command_class_switch_all_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 SWITCH_ALL_REPORT: + return zwave_command_class_switch_all_handle_report(connection_info, + frame_data, + frame_length); + default: + return SL_STATUS_NOT_SUPPORTED; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Attribute Store callback functions +/////////////////////////////////////////////////////////////////////////////// +static void zwave_command_class_switch_all_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(); + + // Now we know we have a switch_all supporting endpoint. + attribute_store::attribute endpoint_node + = version_node.first_parent(ATTRIBUTE_ENDPOINT_ID); + + // Create the switch_all attributes + endpoint_node.emplace_node(ATTRIBUTE(MODE)); + + endpoint_node.emplace_node(ATTRIBUTE(ON_OFF)); +} + +sl_status_t + zwave_command_class_check_ep_support(const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint) +{ + attribute_store::attribute node + = attribute_store_network_helper_get_node_id_node(unid); + attribute_store::attribute ep + = node.child_by_type_and_value<>(ATTRIBUTE_ENDPOINT_ID, endpoint); + + if (ep.child_by_type(ATTRIBUTE(MODE)).is_valid()) + return SL_STATUS_OK; + return SL_STATUS_FAIL; +} + +sl_status_t zwave_command_class_unify_switch_all_write_attributes_callback( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint, + uic_mqtt_dotdot_callback_call_type_t call_type, + uic_mqtt_dotdot_unify_switch_all_state_t value, + uic_mqtt_dotdot_unify_switch_all_updated_state_t changed) +{ + if (call_type == UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK) { + return zwave_command_class_check_ep_support(unid, endpoint); + } + + attribute_store::attribute home + = attribute_store_network_helper_get_home_id_node(unid); + + if (changed.mode) { + attribute_store::attribute node + = attribute_store_network_helper_get_node_id_node(unid); + attribute_store::attribute ep + = node.child_by_type_and_value<>(ATTRIBUTE_ENDPOINT_ID, endpoint); + attribute_store::attribute mode + = ep.emplace_node(DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_MODE); + + sl_log_debug(LOG_TAG, "write callback set desired mode"); + mode.set_desired<>(value.mode); + } + if (changed.on_off) { + for (auto node: home.children()) { + auto ep = node.child_by_type(ATTRIBUTE_ENDPOINT_ID); + if (!ep.child_by_type(ATTRIBUTE(MODE)).is_valid()) { + sl_log_debug(LOG_TAG, + "Switch All CC not supported by ep %u of node %x", + ep.reported(), + node.reported()); + continue; + } + auto on_off + = ep.emplace_node(DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_ON_OFF); + + sl_log_debug(LOG_TAG, "write callback set desired on_off"); + on_off.set_desired<>(value.on_off); + } + } + + return SL_STATUS_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// Public interface functions +/////////////////////////////////////////////////////////////////////////////// +sl_status_t zwave_command_class_switch_all_init() +{ + // Attribute store callbacks + attribute_store_register_callback_by_type( + zwave_command_class_switch_all_on_version_attribute_update, + ATTRIBUTE(VERSION)); + + // Attribute resolver rules + attribute_resolver_register_rule(ATTRIBUTE(MODE), + zwave_command_class_switch_all_set, + zwave_command_class_switch_all_get); + attribute_resolver_register_rule(ATTRIBUTE(ON_OFF), + zwave_command_class_switch_all_set_on_off, + nullptr); + // Attribute write callback + uic_mqtt_dotdot_set_unify_switch_all_write_attributes_callback( + zwave_command_class_unify_switch_all_write_attributes_callback); + + // 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_switch_all_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_SWITCH_ALL; + handler.version = SWITCH_ALL_VERSION; + handler.command_class_name = "switch_all"; + 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_switch_all.h b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_switch_all.h new file mode 100644 index 000000000..24f926830 --- /dev/null +++ b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_switch_all.h @@ -0,0 +1,40 @@ +/****************************************************************************** + * # 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. + * + *****************************************************************************/ + +/** + * @defgroup zwave_command_class_switch_all + * @brief TODO: Switch All Command Class handlers and control function + * + * This module implement some of the functions to control the + * Switch All Command Class + * + * @{ + */ + +#ifndef ZWAVE_COMMAND_CLASS_SWITCH_ALL_H +#define ZWAVE_COMMAND_CLASS_SWITCH_ALL_H + +#include "sl_status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +sl_status_t zwave_command_class_switch_all_init(); + +#ifdef __cplusplus +} +#endif + +#endif //ZWAVE_COMMAND_CLASS_SWITCH_ALL_H +/** @} end zwave_command_class_switch_all */ 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..89c908dc8 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 @@ -59,6 +59,7 @@ #include "zwave_command_class_indicator_control.h" #include "zwave_command_class_manufacturer_specific_control.h" #include "zwave_command_class_humidity_control_mode.h" +#include "zwave_command_class_switch_all.h" #include "zwave_command_class_protocol.h" // Generic includes @@ -116,6 +117,7 @@ sl_status_t zwave_command_classes_init() status |= zwave_command_class_security_2_init(); status |= zwave_command_class_sound_switch_init(); status |= zwave_command_class_supervision_init(); + status |= zwave_command_class_switch_all_init(); status |= zwave_command_class_switch_color_init(); status |= zwave_command_class_switch_multilevel_init(); status |= zwave_command_class_thermostat_mode_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..bd4c784e4 100644 --- a/applications/zpc/components/zwave_command_classes/test/CMakeLists.txt +++ b/applications/zpc/components/zwave_command_classes/test/CMakeLists.txt @@ -902,3 +902,23 @@ target_add_unittest( zwave_tx_mock zwave_tx_scheme_selector_mock ) + + +# Switch All Command Class Test +target_add_unittest( + zwave_command_classes + NAME + zwave_command_class_switch_all_test + SOURCES + zwave_command_class_switch_all_test.cpp + DEPENDS + zpc_attribute_store_test_helper + zwave_command_class_test_helpers + zwave_controller + zwave_command_handler_mock + uic_attribute_resolver_mock + zpc_attribute_resolver_mock + zpc_attribute_store_mock + uic_dotdot_mqtt_mock +) + diff --git a/applications/zpc/components/zwave_command_classes/test/zwave_command_class_switch_all_test.cpp b/applications/zpc/components/zwave_command_classes/test/zwave_command_class_switch_all_test.cpp new file mode 100644 index 000000000..836d92afb --- /dev/null +++ b/applications/zpc/components/zwave_command_classes/test/zwave_command_class_switch_all_test.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. + * + *****************************************************************************/ +// Base class +#include "zwave_command_class_switch_all.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" + + +constexpr uint8_t SWITCH_ALL_ON_VALUE = 1; +constexpr uint8_t SWITCH_ALL_OFF_VALUE = 0; + +// Attribute macro, shortening those long defines for attribute types: +#define ATTRIBUTE(type) ATTRIBUTE_COMMAND_CLASS_SWITCH_ALL_##type + + +using namespace zwave_command_class_test_helper; + +extern "C" { + +// Mock helper +#include "dotdot_mqtt_mock.h" +#include "zpc_attribute_store_network_helper_mock.h" + +static uic_mqtt_dotdot_unify_switch_all_write_attributes_callback_t switch_all_mock_callback; +static void uic_mqtt_dotdot_unify_switch_all_write_attributes_callback_stub( + const uic_mqtt_dotdot_unify_switch_all_write_attributes_callback_t callback, + int cmock_num_calls) +{ + switch_all_mock_callback = callback; +} + +/// 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_SWITCH_ALL, + .supported_version = SWITCH_ALL_VERSION}; +// Get Set function map +const resolver_function_map attribute_bindings = { + {ATTRIBUTE(MODE), {SWITCH_ALL_GET, SWITCH_ALL_SET}}, + {ATTRIBUTE(ON_OFF), {0, SWITCH_ALL_ON}}, + {ATTRIBUTE(ON_OFF), {0, SWITCH_ALL_OFF}} +}; + +/// Called before each and every test +void setUp() +{ + uic_mqtt_dotdot_set_unify_switch_all_write_attributes_callback_Stub(uic_mqtt_dotdot_unify_switch_all_write_attributes_callback_stub); + zwave_setUp(command_class_handler, + &zwave_command_class_switch_all_init, + attribute_bindings); +} + +/////////////////////////////////////////////////////////////////////////////// +// Test cases +/////////////////////////////////////////////////////////////////////////////// +void test_switch_all_interview_happy_case() +{ + helper_set_version(SWITCH_ALL_VERSION); + + // Verify that we have the correct node(s) + helper_test_node_exists(ATTRIBUTE(MODE)); + helper_test_node_exists(ATTRIBUTE(ON_OFF)); +} + +void test_switch_all_get_happy_case() +{ + helper_set_version(SWITCH_ALL_VERSION); + + auto mode_node = helper_test_and_get_node(ATTRIBUTE(MODE)); + + helper_test_get_set_frame_happy_case(SWITCH_ALL_GET, mode_node); +} + +void test_switch_all_set_happy_case() +{ + helper_set_version(SWITCH_ALL_VERSION); + + auto mode_node = helper_test_and_get_node(ATTRIBUTE(MODE)); + uint8_t mode = 0xFF; + + // Test with desired value + mode_node.set_desired(mode); + helper_test_get_set_frame_happy_case(SWITCH_ALL_SET, + mode_node, + {mode}); +} + +void test_switch_all_set_on_happy_case() +{ + helper_set_version(SWITCH_ALL_VERSION); + + auto mode_node = helper_test_and_get_node(ATTRIBUTE(MODE)); + auto on_off_node = helper_test_and_get_node(ATTRIBUTE(ON_OFF)); + uint8_t mode = 0xFF; + + // Test with desired value + mode_node.set_reported(mode); + on_off_node.set_desired(SWITCH_ALL_ON_VALUE); + helper_test_get_set_frame_happy_case(SWITCH_ALL_ON, + on_off_node); +} + +void test_switch_all_set_on_fail_case() +{ + helper_set_version(SWITCH_ALL_VERSION); + + auto mode_node = helper_test_and_get_node(ATTRIBUTE(MODE)); + auto on_off_node = helper_test_and_get_node(ATTRIBUTE(ON_OFF)); + uint8_t mode = 0x00; + + // Test with desired value + mode_node.set_reported(mode); + on_off_node.set_desired(SWITCH_ALL_ON_VALUE); + helper_test_get_set_fail_case(SWITCH_ALL_ON, + SL_STATUS_OK, + on_off_node); + + helper_test_get_set_fail_case(SWITCH_ALL_ON, + SL_STATUS_INVALID_TYPE); + + mode = 0xFF; + mode_node.set_reported(mode); + on_off_node.set_desired(0xFF); + helper_test_get_set_fail_case(SWITCH_ALL_ON, + SL_STATUS_INVALID_RANGE, + on_off_node); + +} + +void test_switch_all_report_happy_case() +{ + helper_set_version(SWITCH_ALL_VERSION); + auto mode_node = helper_test_and_get_node(ATTRIBUTE(MODE)); + uint8_t mode = 0; + + attribute_store_network_helper_get_endpoint_node_IgnoreAndReturn(cpp_endpoint_id_node); + helper_test_report_frame(SWITCH_ALL_REPORT, {mode}); + TEST_ASSERT_EQUAL(mode, mode_node.reported()); + + mode = 0x02; + helper_test_report_frame(SWITCH_ALL_REPORT, {mode}); + attribute_store_network_helper_get_endpoint_node_StopIgnore(); + TEST_ASSERT_EQUAL(mode, mode_node.reported()); +} + +void test_switch_all_report_fail_case() +{ + helper_set_version(SWITCH_ALL_VERSION); + uint8_t mode = 0; + + attribute_store_network_helper_get_endpoint_node_IgnoreAndReturn(cpp_endpoint_id_node); + helper_test_report_frame(SWITCH_ALL_REPORT, {mode, mode}, SL_STATUS_FAIL); + attribute_store_network_helper_get_endpoint_node_StopIgnore(); +} + +void test_switch_all_set_off_happy_case() +{ + helper_set_version(SWITCH_ALL_VERSION); + + auto mode_node = helper_test_and_get_node(ATTRIBUTE(MODE)); + auto on_off_node = helper_test_and_get_node(ATTRIBUTE(ON_OFF)); + uint8_t mode = 0xFF; + + // Test with desired value + mode_node.set_reported(mode); + on_off_node.set_desired(SWITCH_ALL_OFF_VALUE); + helper_test_get_set_frame_happy_case(SWITCH_ALL_OFF, + on_off_node); +} + +void test_switch_all_set_off_fail_case() +{ + helper_set_version(SWITCH_ALL_VERSION); + + auto mode_node = helper_test_and_get_node(ATTRIBUTE(MODE)); + auto on_off_node = helper_test_and_get_node(ATTRIBUTE(ON_OFF)); + uint8_t mode = 0x0; + + // Test with desired value + mode_node.set_reported(mode); + on_off_node.set_desired(SWITCH_ALL_OFF_VALUE); + helper_test_get_set_fail_case(SWITCH_ALL_OFF, + SL_STATUS_OK, + on_off_node); + + helper_test_get_set_fail_case(SWITCH_ALL_OFF, + SL_STATUS_INVALID_TYPE); +} + +void test_switch_all_write_command_handler() +{ + helper_set_version(SWITCH_ALL_VERSION); + + attribute_store::attribute home_node(home_id_node); + + auto unid_node_second = home_node.add_node(ATTRIBUTE_NODE_ID); + unid_node_second.set_reported("zw-CAFECAFE-0008"); + auto ep_node_second = unid_node_second.emplace_node<>(ATTRIBUTE_ENDPOINT_ID, endpoint_id); + helper_set_version(SWITCH_ALL_VERSION, ep_node_second); + + attribute_store_network_helper_get_home_id_node_ExpectAndReturn("zw-CAFECAFE-0008", home_node); + attribute_store_network_helper_get_node_id_node_ExpectAndReturn("zw-CAFECAFE-0008", unid_node_second); + uic_mqtt_dotdot_unify_switch_all_state_t state = {0xFF, 1}; + uic_mqtt_dotdot_unify_switch_all_updated_state_t updates = {true, false}; + switch_all_mock_callback("zw-CAFECAFE-0008", endpoint_id, UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL, + state, updates); + auto dotdot_mode_second = helper_test_and_get_node(DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_MODE, ep_node_second); + TEST_ASSERT_EQUAL(state.mode, dotdot_mode_second.desired()); + + attribute_store_network_helper_get_home_id_node_ExpectAndReturn("zw-CAFECAFE-0008", home_node); + updates.mode = false; + updates.on_off = true; + switch_all_mock_callback("zw-CAFECAFE-0008", endpoint_id, UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL, + state, updates); + auto dotdot_onoff_second = helper_test_and_get_node(DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_ON_OFF, ep_node_second); + auto dotdot_onoff = helper_test_and_get_node(DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_ON_OFF, cpp_endpoint_id_node); + TEST_ASSERT_EQUAL(state.on_off, dotdot_onoff.desired()); + TEST_ASSERT_EQUAL(state.on_off, dotdot_onoff_second.desired()); +} + +} // extern "C" \ No newline at end of file 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-Pull-request-2833-UIC-3444-Created-custom-dotdot-clu.patch b/patches/UnifySDK/0008-Pull-request-2833-UIC-3444-Created-custom-dotdot-clu.patch new file mode 100644 index 000000000..fde753278 --- /dev/null +++ b/patches/UnifySDK/0008-Pull-request-2833-UIC-3444-Created-custom-dotdot-clu.patch @@ -0,0 +1,2755 @@ +From 9ecda2fa811952617ed7a17f39afa37c947b45cb Mon Sep 17 00:00:00 2001 +From: Suhas Shankar +Date: Tue, 10 Sep 2024 09:57:46 +0000 +Subject: [PATCH] Pull request #2833: UIC-3444: Created custom dotdot cluster + for SwitchAll CC along with mapper + +Merge in UIC/uic from feature/uic-3444-dotdot-cluster-and-mapper to feature/UIC-3276-all-switch-cc +--- + .../cluster-types/cluster-type-attributes.ts | 28 + + .../src/cluster-types/cluster-types.ts | 1 + + .../src/cluster-types/supported-clusters.js | 1 + + .../dev_ui/dev_gui/zap/addon-helper.js | 3 +- + .../uic_dotdot/dotdot-xml/Unify_SwitchAll.xml | 16 + + components/uic_dotdot/dotdot-xml/library.xml | 1 + + .../zap-generated/include/dotdot_mqtt.h | 183 +++++++ + .../include/dotdot_mqtt_attributes.h | 35 ++ + .../include/dotdot_mqtt_generated_commands.h | 22 + + .../include/dotdot_mqtt_group_commands.h | 17 + + ...dotdot_mqtt_supported_generated_commands.h | 28 + + .../zap-generated/src/dotdot_mqtt.cpp | 486 ++++++++++++++++++ + .../zap-generated/src/dotdot_mqtt.hpp | 28 + + .../src/dotdot_mqtt_attributes.cpp | 197 +++++++ + .../src/dotdot_mqtt_command_helpers.cpp | 30 ++ + .../src/dotdot_mqtt_command_helpers.hpp | 12 + + .../src/dotdot_mqtt_generated_commands.cpp | 60 +++ + .../src/dotdot_mqtt_group_commands.cpp | 99 ++++ + .../zap-generated/src/dotdot_mqtt_helpers.cpp | 30 ++ + ...tdot_mqtt_supported_generated_commands.cpp | 43 ++ + .../test/dotdot_mqtt_test.include | 1 + + .../include/dotdot_attributes.uam | 4 + + .../include/dotdot_attributes_camel_case.uam | 4 + + .../unify_dotdot_attribute_store_helpers.h | 240 +++++++++ + .../unify_dotdot_defined_attribute_types.h | 3 + + ...ot_attribute_store_attribute_publisher.cpp | 265 ++++++++++ + ..._force_read_attributes_command_callbacks.c | 37 ++ + .../unify_dotdot_attribute_store_helpers.cpp | 253 +++++++++ + ...fy_dotdot_attribute_store_registration.cpp | 48 ++ + ...store_write_attributes_command_callbacks.c | 41 ++ + .../test/unify_dotdot_attribute_store_test.c | 28 + + .../test/unify_dotdot_attribute_store_test.h | 4 + + 32 files changed, 2247 insertions(+), 1 deletion(-) + create mode 100644 components/uic_dotdot/dotdot-xml/Unify_SwitchAll.xml + +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..990fa0fd79 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 +@@ -15352,4 +15352,32 @@ export let ClusterTypeAttrs: any = { + ] + } + }, ++ UnifySwitchAll: { ++ server:{ ++ attributes: [ ++ { ++ id: 0, ++ name: "Mode", ++ type: "number", ++ required: true, ++ reportRequired: false, ++ sceneRequired: false, ++ writable: true, ++ isArray: false, ++ }, ++ { ++ id: 1, ++ name: "OnOff", ++ type: "number", ++ required: true, ++ reportRequired: false, ++ sceneRequired: false, ++ writable: true, ++ isArray: false, ++ } ++ ], ++ commands: [ ++ ] ++ } ++ }, + } +diff --git a/applications/dev_ui/dev_gui/zap-generated/src/cluster-types/cluster-types.ts b/applications/dev_ui/dev_gui/zap-generated/src/cluster-types/cluster-types.ts +index 192c0f68f9..ce024c022c 100644 +--- a/applications/dev_ui/dev_gui/zap-generated/src/cluster-types/cluster-types.ts ++++ b/applications/dev_ui/dev_gui/zap-generated/src/cluster-types/cluster-types.ts +@@ -29,6 +29,7 @@ export enum ClusterTypes { + SoilMoisture = "SoilMoisture", + TemperatureMeasurement = "TemperatureMeasurement", + Thermostat = "Thermostat", ++ UnifySwitchAll = "UnifySwitchAll", + WindSpeedMeasurement = "WindSpeedMeasurement", + WindowCovering = "WindowCovering" + } +diff --git a/applications/dev_ui/dev_gui/zap-generated/src/cluster-types/supported-clusters.js b/applications/dev_ui/dev_gui/zap-generated/src/cluster-types/supported-clusters.js +index e19a8e938b..690c0cd6e5 100644 +--- a/applications/dev_ui/dev_gui/zap-generated/src/cluster-types/supported-clusters.js ++++ b/applications/dev_ui/dev_gui/zap-generated/src/cluster-types/supported-clusters.js +@@ -32,6 +32,7 @@ const SupportedClusters = { + SoilMoisture: "SoilMoisture", + TemperatureMeasurement: "TemperatureMeasurement", + Thermostat: "Thermostat", ++ UnifySwitchAll: "UnifySwitchAll", + WindSpeedMeasurement: "WindSpeedMeasurement", + WindowCovering: "WindowCovering" + } +diff --git a/applications/dev_ui/dev_gui/zap/addon-helper.js b/applications/dev_ui/dev_gui/zap/addon-helper.js +index aa013c9f1a..d1cce49dce 100644 +--- a/applications/dev_ui/dev_gui/zap/addon-helper.js ++++ b/applications/dev_ui/dev_gui/zap/addon-helper.js +@@ -31,6 +31,7 @@ const supportedClusters = [ + "SystemMetrics", + "TemperatureMeasurement", + "Thermostat", ++ "UnifySwitchAll", + "WindSpeedMeasurement", + "WindowCovering" + ]; +@@ -272,4 +273,4 @@ exports.getClusterName = getClusterName + exports.isStruct = isStruct + exports.isArray = isArray + exports.getEnum = getEnum +-exports.getBitmap = getBitmap +\ No newline at end of file ++exports.getBitmap = getBitmap +diff --git a/components/uic_dotdot/dotdot-xml/Unify_SwitchAll.xml b/components/uic_dotdot/dotdot-xml/Unify_SwitchAll.xml +new file mode 100644 +index 0000000000..9686bde3f6 +--- /dev/null ++++ b/components/uic_dotdot/dotdot-xml/Unify_SwitchAll.xml +@@ -0,0 +1,16 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/components/uic_dotdot/dotdot-xml/library.xml b/components/uic_dotdot/dotdot-xml/library.xml +index 4210b67fa4..dff99bc346 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 +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..773e5837a0 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt.h ++++ b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt.h +@@ -39956,6 +39956,189 @@ void uic_mqtt_dotdot_unify_thermostat_publish_supported_commands( + void uic_mqtt_dotdot_unify_thermostat_publish_empty_supported_commands( + const dotdot_unid_t unid + ,dotdot_endpoint_id_t endpoint); ++// Callback types used by the unify_switch_all cluster ++ ++typedef struct { ++ uint8_t mode; ++ uint8_t on_off; ++} uic_mqtt_dotdot_unify_switch_all_state_t; ++ ++typedef struct { ++ bool mode; ++ bool on_off; ++} uic_mqtt_dotdot_unify_switch_all_updated_state_t; ++ ++typedef sl_status_t (*uic_mqtt_dotdot_unify_switch_all_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_unify_switch_all_state_t, ++ uic_mqtt_dotdot_unify_switch_all_updated_state_t ++); ++ ++typedef sl_status_t (*uic_mqtt_dotdot_unify_switch_all_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_unify_switch_all_updated_state_t ++); ++ ++ ++ ++ ++/** ++ * @brief Setup a callback for WriteAttribute to be called when a ++ * +/unify_switch_all/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_unify_switch_all_write_attributes_callback( ++ const uic_mqtt_dotdot_unify_switch_all_write_attributes_callback_t callback ++); ++/** ++ * @brief Unsets a callback for WriteAttribute to be called when a ++ * +/unify_switch_all/Commands/WriteAttributes is received. ++ * @param callback Function to be no longer called on command reception ++ */ ++void uic_mqtt_dotdot_unset_unify_switch_all_write_attributes_callback( ++ const uic_mqtt_dotdot_unify_switch_all_write_attributes_callback_t callback ++); ++/** ++ * @brief Clears all callbacks registered for when ++ * +/unify_switch_all/Commands/WriteAttributes is received. ++ */ ++void uic_mqtt_dotdot_clear_unify_switch_all_write_attributes_callbacks(); ++ ++/** ++ * @brief Setup a callback for ForceReadAttributes to be called when a ++ * +/unify_switch_all/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_unify_switch_all_force_read_attributes_callback( ++ const uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback_t callback ++); ++/** ++ * @brief Unsets a callback for ForceReadAttributes to be called when a ++ * +/unify_switch_all/Commands/ForceReadAttributes is received. ++ * ++ * @param callback Function to be no longer called on command reception ++ */ ++void uic_mqtt_dotdot_unset_unify_switch_all_force_read_attributes_callback( ++ const uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback_t callback ++); ++/** ++ * @brief Clears all callbacks registered for when ++ * +/unify_switch_all/Commands/ForceReadAttributes is received. ++ */ ++void uic_mqtt_dotdot_clear_unify_switch_all_force_read_attributes_callbacks(); ++ ++/** ++ * @brief Publish the attribute; UnifySwitchAll/Attributes/Mode ++ * ++ * @param base_topic topic prefix to publish, /mode ++ * 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_unify_switch_all_mode_publish( ++ const char *base_topic, ++ uint8_t value, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type ++); ++ ++/** ++ * @brief Unretains a published attribute; UnifySwitchAll/Attributes/Mode ++ * ++ * @param base_topic topic prefix to publish, /mode ++ * 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_unify_switch_all_mode_unretain( ++ const char *base_topic, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type ++); ++ ++/** ++ * @brief Publish the attribute; UnifySwitchAll/Attributes/OnOff ++ * ++ * @param base_topic topic prefix to publish, /on_off ++ * 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_unify_switch_all_on_off_publish( ++ const char *base_topic, ++ uint8_t value, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type ++); ++ ++/** ++ * @brief Unretains a published attribute; UnifySwitchAll/Attributes/OnOff ++ * ++ * @param base_topic topic prefix to publish, /on_off ++ * 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_unify_switch_all_on_off_unretain( ++ const char *base_topic, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type ++); ++ ++ ++/** ++ * @brief Publish the UnifySwitchAll/ClusterRevision attribute ++ * ++ * @param base_topic topic prefix to publish, /UnifySwitchAll/Attributes/ClusterRevision ++ * will be appended. ++ * @param value Value to publish. ++ */ ++void uic_mqtt_dotdot_unify_switch_all_publish_cluster_revision(const char* base_topic, uint16_t value); ++ ++/** ++ * @brief Unretain a publication to UnifySwitchAll/ClusterRevision attribute ++ * ++ * @param base_topic topic prefix to publish, /UnifySwitchAll/Attributes/ClusterRevision ++ * will be appended. ++ */ ++void uic_mqtt_dotdot_unify_switch_all_unretain_cluster_revision(const char* base_topic); ++ ++/** ++ * @brief Publish the SupportedCommands for UNID/EndPoint for the UnifySwitchAll Cluster ++ * ++ * This function will iterate over all Commands in the UnifySwitchAll 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_unify_switch_all_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 UnifySwitchAll Cluster ++ * ++ * @param unid ++ * @param endpoint ) ++ */ ++void uic_mqtt_dotdot_unify_switch_all_publish_empty_supported_commands( ++ const dotdot_unid_t unid ++ ,dotdot_endpoint_id_t endpoint); + // Callback types used by the unify_humidity_control cluster + typedef sl_status_t (*uic_mqtt_dotdot_unify_humidity_control_mode_set_callback_t)( + dotdot_unid_t unid, +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..ae84becaee 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 +@@ -5039,6 +5039,21 @@ typedef sl_status_t (*uic_mqtt_dotdot_unify_thermostat_attribute_operating_state + uic_mqtt_dotdot_attribute_update_type_t update_type, + uint8_t operating_state + ); ++// Callback types used by the unify_switch_all cluster ++typedef sl_status_t (*uic_mqtt_dotdot_unify_switch_all_attribute_mode_callback_t)( ++ dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint, ++ bool unretained, ++ uic_mqtt_dotdot_attribute_update_type_t update_type, ++ uint8_t mode ++); ++typedef sl_status_t (*uic_mqtt_dotdot_unify_switch_all_attribute_on_off_callback_t)( ++ dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint, ++ bool unretained, ++ uic_mqtt_dotdot_attribute_update_type_t update_type, ++ uint8_t on_off ++); + // Callback types used by the unify_humidity_control cluster + typedef sl_status_t (*uic_mqtt_dotdot_unify_humidity_control_attribute_reporting_mode_callback_t)( + dotdot_unid_t unid, +@@ -9819,6 +9834,26 @@ void uic_mqtt_dotdot_unify_thermostat_attribute_supported_thermostat_mode_callba + void uic_mqtt_dotdot_unify_thermostat_attribute_operating_state_callback_set(const uic_mqtt_dotdot_unify_thermostat_attribute_operating_state_callback_t callback); + + ++/** ++ * Initializes the attributes features for the UnifySwitchAll cluster, ++ * allowing to receive attribute updates from other UNIDs. ++ */ ++sl_status_t uic_mqtt_dotdot_unify_switch_all_attributes_init(); ++ ++/** ++ * Setup callback to be called when a ++ * UnifySwitchAll/Attributes/mode/# is received. Setting ++ * this callback will overwrite the previous set callback ++ */ ++void uic_mqtt_dotdot_unify_switch_all_attribute_mode_callback_set(const uic_mqtt_dotdot_unify_switch_all_attribute_mode_callback_t callback); ++/** ++ * Setup callback to be called when a ++ * UnifySwitchAll/Attributes/on_off/# is received. Setting ++ * this callback will overwrite the previous set callback ++ */ ++void uic_mqtt_dotdot_unify_switch_all_attribute_on_off_callback_set(const uic_mqtt_dotdot_unify_switch_all_attribute_on_off_callback_t callback); ++ ++ + /** + * Initializes the attributes features for the UnifyHumidityControl cluster, + * allowing to receive attribute updates from other UNIDs. +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..e6197b0003 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 +@@ -4975,6 +4975,28 @@ void uic_mqtt_dotdot_unify_thermostat_publish_generated_write_attributes_command + uic_mqtt_dotdot_unify_thermostat_updated_state_t attribute_list + ); + ++ ++/** ++ * @brief Publishes an incoming/generated WriteAttributes command for ++ * the UnifySwitchAll cluster. ++ * ++ * Publication will be made at the following topic ++ * ucl/by-unid/UNID/epID/UnifySwitchAll/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_unify_switch_all_publish_generated_write_attributes_command( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint, ++ uic_mqtt_dotdot_unify_switch_all_state_t attribute_values, ++ uic_mqtt_dotdot_unify_switch_all_updated_state_t attribute_list ++); ++ + /** + * @brief Publishes an incoming/generated ModeSet command for + * the UnifyHumidityControl cluster. +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..712df20813 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 +@@ -3692,6 +3692,23 @@ void uic_mqtt_dotdot_by_group_unify_thermostat_write_attributes_callback_set( + + + ++typedef void (*uic_mqtt_dotdot_by_group_unify_switch_all_write_attributes_callback_t)( ++ const dotdot_group_id_t group_id, ++ uic_mqtt_dotdot_unify_switch_all_state_t, ++ uic_mqtt_dotdot_unify_switch_all_updated_state_t ++); ++ ++/** ++ * Setup a callback for WriteAttribute to be called when a ++ * ucl/by-group/+/unify_switch_all/Commands/WriteAttributes is received. ++ * Setting this callback will overwrite any previously set callback. ++ */ ++void uic_mqtt_dotdot_by_group_unify_switch_all_write_attributes_callback_set( ++ const uic_mqtt_dotdot_by_group_unify_switch_all_write_attributes_callback_t callback ++); ++ ++ ++ + /** + * @brief Callback signature for by-group UnifyHumidityControl::ModeSet command. + */ +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..8cb6be4209 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 +@@ -1664,6 +1664,34 @@ void uic_mqtt_dotdot_unify_thermostat_publish_supported_generated_commands( + ); + + ++/** ++ * @brief Struct containing the list of commands for UnifySwitchAll ++ */ ++typedef struct _uic_mqtt_dotdot_unify_switch_all_supported_commands_ { ++ bool write_attributes; ++} uic_mqtt_dotdot_unify_switch_all_supported_commands_t; ++ ++/** ++ * @brief Sends/Publishes a the SupportedGenerated commands for ++ * the UnifySwitchAll cluster for a UNID/Endpoint ++ * ++ * Publication will be made at the following topic ++ * ucl/by-unid/UNID/epID/UnifySwitchAll/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_unify_switch_all_publish_supported_generated_commands( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint, ++ const uic_mqtt_dotdot_unify_switch_all_supported_commands_t *command_list ++); ++ ++ + /** + * @brief Struct containing the list of commands for UnifyHumidityControl + */ +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..f5b1ba55a3 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.cpp ++++ b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.cpp +@@ -93627,6 +93627,338 @@ sl_status_t uic_mqtt_dotdot_unify_thermostat_init() + return SL_STATUS_OK; + } + ++// Callbacks pointers ++static std::set uic_mqtt_dotdot_unify_switch_all_write_attributes_callback; ++static std::set uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback; ++ ++// Callbacks setters ++ ++void uic_mqtt_dotdot_set_unify_switch_all_write_attributes_callback( ++ const uic_mqtt_dotdot_unify_switch_all_write_attributes_callback_t callback) ++{ ++ if (callback != nullptr) { ++ uic_mqtt_dotdot_unify_switch_all_write_attributes_callback.insert(callback); ++ } ++} ++void uic_mqtt_dotdot_unset_unify_switch_all_write_attributes_callback( ++ const uic_mqtt_dotdot_unify_switch_all_write_attributes_callback_t callback) ++{ ++ uic_mqtt_dotdot_unify_switch_all_write_attributes_callback.erase(callback); ++} ++void uic_mqtt_dotdot_clear_unify_switch_all_write_attributes_callbacks() ++{ ++ uic_mqtt_dotdot_unify_switch_all_write_attributes_callback.clear(); ++} ++std::set& get_uic_mqtt_dotdot_unify_switch_all_write_attributes_callback() ++{ ++ return uic_mqtt_dotdot_unify_switch_all_write_attributes_callback; ++} ++ ++void uic_mqtt_dotdot_set_unify_switch_all_force_read_attributes_callback( ++ const uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback_t callback) ++{ ++ if (callback != nullptr) { ++ uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback.insert(callback); ++ } ++} ++void uic_mqtt_dotdot_unset_unify_switch_all_force_read_attributes_callback( ++ const uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback_t callback) ++{ ++ uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback.erase(callback); ++} ++void uic_mqtt_dotdot_clear_unify_switch_all_force_read_attributes_callbacks() ++{ ++ uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback.clear(); ++} ++ ++ ++// Callback function for incoming publications on ucl/by-unid/+/+/UnifySwitchAll/Commands/WriteAttributes ++void uic_mqtt_dotdot_on_unify_switch_all_WriteAttributes( ++ const char *topic, ++ const char *message, ++ const size_t message_length) ++{ ++ if (uic_mqtt_dotdot_unify_switch_all_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_unify_switch_all_state_t new_state = {}; ++ uic_mqtt_dotdot_unify_switch_all_updated_state_t new_updated_state = {}; ++ ++ ++ nlohmann::json jsn; ++ try { ++ jsn = nlohmann::json::parse(std::string(message)); ++ ++ uic_mqtt_dotdot_parse_unify_switch_all_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, "UnifySwitchAll", "WriteAttributes"); ++ return; ++ } catch (const nlohmann::json::exception& e) { ++ // Catch JSON object field parsing errors ++ sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifySwitchAll", "WriteAttributes", e.what()); ++ return; ++ } catch (const std::exception& e) { ++ sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifySwitchAll", "WriteAttributes", ""); ++ return; ++ } ++ ++ for (const auto& callback: uic_mqtt_dotdot_unify_switch_all_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_unify_switch_all_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_unify_switch_all_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_unify_switch_all_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.mode = true; ++ force_update.on_off = true; ++ trigger_handler = true; ++ } else { ++ std::unordered_map supported_attrs = { ++ {"Mode", &force_update.mode }, ++ {"OnOff", &force_update.on_off }, ++ }; ++ ++ 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_unify_switch_all_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, "UnifySwitchAll/Commands/ForceReadAttributes: Unable to parse JSON payload"); ++ return; ++ } ++} ++ ++sl_status_t uic_mqtt_dotdot_unify_switch_all_mode_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(64800,0,value)) { ++ jsn["value"] = uic_dotdot_get_attribute_value_name(64800,0,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, "UnifySwitchAll/Attributes/Mode", e.what()); ++ return SL_STATUS_OK; ++ } ++ ++ ++ std::string topic = std::string(base_topic) + "/UnifySwitchAll/Attributes/Mode"; ++ 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_unify_switch_all_mode_unretain( ++ const char *base_topic, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type) ++{ ++ // clang-format on ++ std::string topic ++ = std::string(base_topic) ++ + "/UnifySwitchAll/Attributes/Mode"; ++ ++ 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_unify_switch_all_on_off_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(64800,1,value)) { ++ jsn["value"] = uic_dotdot_get_attribute_value_name(64800,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, "UnifySwitchAll/Attributes/OnOff", e.what()); ++ return SL_STATUS_OK; ++ } ++ ++ ++ std::string topic = std::string(base_topic) + "/UnifySwitchAll/Attributes/OnOff"; ++ 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_unify_switch_all_on_off_unretain( ++ const char *base_topic, ++ uic_mqtt_dotdot_attribute_publish_type_t publish_type) ++{ ++ // clang-format on ++ std::string topic ++ = std::string(base_topic) ++ + "/UnifySwitchAll/Attributes/OnOff"; ++ ++ 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_unify_switch_all_init() ++{ ++ std::string base_topic = "ucl/by-unid/+/+/"; ++ ++ std::string subscription_topic; ++ if(!uic_mqtt_dotdot_unify_switch_all_write_attributes_callback.empty()) { ++ subscription_topic = base_topic + "UnifySwitchAll/Commands/WriteAttributes"; ++ uic_mqtt_subscribe(subscription_topic.c_str(), uic_mqtt_dotdot_on_unify_switch_all_WriteAttributes); ++ } ++ ++ if(!uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback.empty()) { ++ subscription_topic = base_topic + "UnifySwitchAll/Commands/ForceReadAttributes"; ++ uic_mqtt_subscribe(subscription_topic.c_str(), uic_mqtt_dotdot_on_unify_switch_all_force_read_attributes); ++ } ++ ++ // Init the attributes for that cluster ++ uic_mqtt_dotdot_unify_switch_all_attributes_init(); ++ ++ uic_mqtt_dotdot_by_group_unify_switch_all_init(); ++ ++ return SL_STATUS_OK; ++} ++ + // Callbacks pointers + static std::set uic_mqtt_dotdot_unify_humidity_control_mode_set_callback; + static std::set uic_mqtt_dotdot_unify_humidity_control_generated_mode_set_callback; +@@ -95763,6 +96095,10 @@ sl_status_t uic_mqtt_dotdot_init() { + status_flag = uic_mqtt_dotdot_unify_thermostat_init(); + } + ++ if (status_flag == SL_STATUS_OK) { ++ status_flag = uic_mqtt_dotdot_unify_switch_all_init(); ++ } ++ + if (status_flag == SL_STATUS_OK) { + status_flag = uic_mqtt_dotdot_unify_humidity_control_init(); + } +@@ -95828,6 +96164,7 @@ void uic_mqtt_dotdot_publish_supported_commands( + uic_mqtt_dotdot_descriptor_publish_supported_commands(unid, endpoint_id); + 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_switch_all_publish_supported_commands(unid, endpoint_id); + uic_mqtt_dotdot_unify_humidity_control_publish_supported_commands(unid, endpoint_id); + } + +@@ -95887,6 +96224,7 @@ void uic_mqtt_dotdot_publish_empty_supported_commands( + uic_mqtt_dotdot_descriptor_publish_empty_supported_commands(unid, endpoint_id); + 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_switch_all_publish_empty_supported_commands(unid, endpoint_id); + uic_mqtt_dotdot_unify_humidity_control_publish_empty_supported_commands(unid, endpoint_id); + } + +@@ -109304,6 +109642,154 @@ void uic_mqtt_dotdot_unify_thermostat_publish_empty_supported_commands( + } + } + ++// Publishing Cluster Revision for UnifySwitchAll Cluster ++void uic_mqtt_dotdot_unify_switch_all_publish_cluster_revision(const char* base_topic, uint16_t value) ++{ ++ std::string cluster_topic = std::string(base_topic) + "/UnifySwitchAll/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 UnifySwitchAll Cluster ++void uic_mqtt_dotdot_unify_switch_all_unretain_cluster_revision(const char* base_topic) ++{ ++ // clang-format on ++ std::string cluster_topic ++ = std::string(base_topic) ++ + "/UnifySwitchAll/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_unify_switch_all_write_attributes_is_supported( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id) ++{ ++ for (const auto& callback: uic_mqtt_dotdot_unify_switch_all_write_attributes_callback) { ++ uic_mqtt_dotdot_unify_switch_all_state_t unify_switch_all_new_state = {}; ++ uic_mqtt_dotdot_unify_switch_all_updated_state_t unify_switch_all_new_updated_state = {}; ++ ++ if (callback( ++ unid, ++ endpoint_id, ++ UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK, ++ unify_switch_all_new_state, ++ unify_switch_all_new_updated_state ++ ) == SL_STATUS_OK) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++static inline bool uic_mqtt_dotdot_unify_switch_all_force_read_attributes_is_supported( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id) ++{ ++ for (const auto& callback: uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback) { ++ uic_mqtt_dotdot_unify_switch_all_updated_state_t unify_switch_all_force_update = {0}; ++ if (callback( ++ unid, ++ endpoint_id, ++ UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK, ++ unify_switch_all_force_update ++ ) == SL_STATUS_OK) { ++ return true; ++ } ++ } ++ return false; ++} ++ ++// Publishing Supported Commands for UnifySwitchAll Cluster ++void uic_mqtt_dotdot_unify_switch_all_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_unify_switch_all_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_unify_switch_all_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 += "/UnifySwitchAll/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 += "/UnifySwitchAll/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 UnifySwitchAll Cluster ++void uic_mqtt_dotdot_unify_switch_all_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 += "/UnifySwitchAll/SupportedCommands"; ++ ++ if (uic_mqtt_count_topics(topic.c_str()) > 0) { ++ uic_mqtt_publish(topic.c_str(), ++ EMPTY_VALUE_ARRAY, ++ strlen(EMPTY_VALUE_ARRAY), ++ true); ++ } ++} ++ + // Publishing Cluster Revision for UnifyHumidityControl Cluster + void uic_mqtt_dotdot_unify_humidity_control_publish_cluster_revision(const char* base_topic, uint16_t value) + { +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..3d01d600fb 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.hpp ++++ b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.hpp +@@ -364,6 +364,13 @@ sl_status_t uic_mqtt_dotdot_by_group_unify_fan_control_init(); + */ + sl_status_t uic_mqtt_dotdot_by_group_unify_thermostat_init(); + ++/** ++ * @brief Initialize UnifySwitchAll dotdot bygroup command handlers ++ * ++ * @returns SL_STATUS_OK on success, error otherwise. ++ */ ++sl_status_t uic_mqtt_dotdot_by_group_unify_switch_all_init(); ++ + /** + * @brief Initialize UnifyHumidityControl dotdot bygroup command handlers + * +@@ -5035,6 +5042,27 @@ void uic_mqtt_dotdot_on_unify_fan_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_unify_switch_all_write_attributes_callback(); ++ ++/** ++ * @brief MQTT Subscribe handler for incoming publications on: ++ * ucl/by-unid/+/+/UnifySwitchAll/Commands/WriteAttributes ++ */ ++// clang-format off ++void uic_mqtt_dotdot_on_unify_switch_all_WriteAttributes( ++ const char *topic, ++ const char *message, ++ const size_t message_length); ++ ++ + // clang-format on + + /** +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..a8ac9b7827 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 +@@ -61958,6 +61958,203 @@ void uic_mqtt_dotdot_unify_thermostat_attribute_operating_state_callback_set(con + + // End of supported cluster. + ++/////////////////////////////////////////////////////////////////////////////// ++// Callback pointers for UnifySwitchAll ++/////////////////////////////////////////////////////////////////////////////// ++static uic_mqtt_dotdot_unify_switch_all_attribute_mode_callback_t uic_mqtt_dotdot_unify_switch_all_attribute_mode_callback = nullptr; ++static uic_mqtt_dotdot_unify_switch_all_attribute_on_off_callback_t uic_mqtt_dotdot_unify_switch_all_attribute_on_off_callback = nullptr; ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Attribute update handlers for UnifySwitchAll ++/////////////////////////////////////////////////////////////////////////////// ++static void uic_mqtt_dotdot_on_unify_switch_all_mode_attribute_update( ++ const char *topic, ++ const char *message, ++ const size_t message_length) { ++ if (uic_mqtt_dotdot_unify_switch_all_attribute_mode_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 mode = {}; ++ ++ 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, "UnifySwitchAll::Mode: Missing attribute element: 'value'\n"); ++ return; ++ } ++// Start parsing value ++ mode = 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_unify_switch_all_attribute_mode_callback( ++ static_cast(unid.c_str()), ++ endpoint, ++ unretained, ++ update_type, ++ mode ++ ); ++ ++} ++static void uic_mqtt_dotdot_on_unify_switch_all_on_off_attribute_update( ++ const char *topic, ++ const char *message, ++ const size_t message_length) { ++ if (uic_mqtt_dotdot_unify_switch_all_attribute_on_off_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 on_off = {}; ++ ++ 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, "UnifySwitchAll::OnOff: Missing attribute element: 'value'\n"); ++ return; ++ } ++// Start parsing value ++ on_off = 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_unify_switch_all_attribute_on_off_callback( ++ static_cast(unid.c_str()), ++ endpoint, ++ unretained, ++ update_type, ++ on_off ++ ); ++ ++} ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Attribute init functions for UnifySwitchAll ++/////////////////////////////////////////////////////////////////////////////// ++sl_status_t uic_mqtt_dotdot_unify_switch_all_attributes_init() ++{ ++ std::string base_topic = "ucl/by-unid/+/+/"; ++ ++ std::string subscription_topic; ++ if(uic_mqtt_dotdot_unify_switch_all_attribute_mode_callback) { ++ subscription_topic = base_topic + "UnifySwitchAll/Attributes/Mode/#"; ++ uic_mqtt_subscribe(subscription_topic.c_str(), &uic_mqtt_dotdot_on_unify_switch_all_mode_attribute_update); ++ } ++ if(uic_mqtt_dotdot_unify_switch_all_attribute_on_off_callback) { ++ subscription_topic = base_topic + "UnifySwitchAll/Attributes/OnOff/#"; ++ uic_mqtt_subscribe(subscription_topic.c_str(), &uic_mqtt_dotdot_on_unify_switch_all_on_off_attribute_update); ++ } ++ ++ return SL_STATUS_OK; ++} ++ ++ ++/////////////////////////////////////////////////////////////////////////////// ++// Callback setters and getters for UnifySwitchAll ++/////////////////////////////////////////////////////////////////////////////// ++void uic_mqtt_dotdot_unify_switch_all_attribute_mode_callback_set(const uic_mqtt_dotdot_unify_switch_all_attribute_mode_callback_t callback) ++{ ++ uic_mqtt_dotdot_unify_switch_all_attribute_mode_callback = callback; ++} ++void uic_mqtt_dotdot_unify_switch_all_attribute_on_off_callback_set(const uic_mqtt_dotdot_unify_switch_all_attribute_on_off_callback_t callback) ++{ ++ uic_mqtt_dotdot_unify_switch_all_attribute_on_off_callback = callback; ++} ++ ++// End of supported cluster. ++ + /////////////////////////////////////////////////////////////////////////////// + // Callback pointers for UnifyHumidityControl + /////////////////////////////////////////////////////////////////////////////// +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..d76bb33c6e 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 +@@ -14507,6 +14507,36 @@ void uic_mqtt_dotdot_parse_unify_thermostat_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_unify_switch_all_write_attributes( ++ nlohmann::json &jsn, ++ uic_mqtt_dotdot_unify_switch_all_state_t &new_state, ++ uic_mqtt_dotdot_unify_switch_all_updated_state_t &new_updated_state ++) { ++ ++ ++ if (jsn.find("Mode") != jsn.end()) { ++ ++ new_state.mode = jsn.at("Mode").get(); ++ ++ new_updated_state.mode = true; ++ } ++ ++ if (jsn.find("OnOff") != jsn.end()) { ++ ++ new_state.on_off = jsn.at("OnOff").get(); ++ ++ new_updated_state.on_off = true; ++ } ++ ++ ++} ++ ++ + std::string get_json_payload_for_unify_humidity_control_mode_set_command( + + const uic_mqtt_dotdot_unify_humidity_control_command_mode_set_fields_t *fields +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..039e04c0cd 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 +@@ -6017,6 +6017,18 @@ void uic_mqtt_dotdot_parse_unify_thermostat_write_attributes( + ); + + ++/** ++ * @brief JSON parser for UnifySwitchAll WriteAttributes command arguments. ++ * ++ * Parse incoming JSON object to populate command arguments passed in by reference. ++ */ ++void uic_mqtt_dotdot_parse_unify_switch_all_write_attributes( ++ nlohmann::json &jsn, ++ uic_mqtt_dotdot_unify_switch_all_state_t &new_state, ++ uic_mqtt_dotdot_unify_switch_all_updated_state_t &new_updated_state ++); ++ ++ + /** + * @brief Private helper function that will create a JSON string based on the + * fields of a UnifyHumidityControl ModeSet command +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..1aeadf7137 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 +@@ -11195,6 +11195,66 @@ void uic_mqtt_dotdot_unify_thermostat_publish_generated_write_attributes_command + false); + } + ++ ++ ++/** ++ * @brief Publishes an incoming/generated WriteAttributes command for ++ * the UnifySwitchAll cluster. ++ * ++ * Publication will be made at the following topic ++ * ucl/by-unid/UNID/epID/UnifySwitchAll/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_unify_switch_all_publish_generated_write_attributes_command( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint, ++ uic_mqtt_dotdot_unify_switch_all_state_t attribute_values, ++ uic_mqtt_dotdot_unify_switch_all_updated_state_t attribute_list ++){ ++ // Create the topic ++ std::string topic = "ucl/by-unid/"+ std::string(unid) + "/ep" + ++ std::to_string(endpoint) + "/"; ++ topic += "UnifySwitchAll/GeneratedCommands/WriteAttributes"; ++ ++ nlohmann::json json_object = nlohmann::json::object(); ++ ++ ++ if (attribute_list.mode == true) { ++ ++ // This is a single value ++ ++ json_object["Mode"] = attribute_values.mode; ++ ++ ++ } ++ ++ ++ if (attribute_list.on_off == true) { ++ ++ // This is a single value ++ ++ json_object["OnOff"] = attribute_values.on_off; ++ ++ ++ } ++ ++ ++ // 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); ++} ++ + /** + * @brief Publishes an incoming/generated ModeSet command for + * the UnifyHumidityControl cluster. +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..8930bd5fbd 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 +@@ -365,6 +365,9 @@ static uic_mqtt_dotdot_by_group_unify_fan_control_write_attributes_callback_t ui + static uic_mqtt_dotdot_by_group_unify_thermostat_write_attributes_callback_t uic_mqtt_dotdot_by_group_unify_thermostat_write_attributes_callback = nullptr; + + ++static uic_mqtt_dotdot_by_group_unify_switch_all_write_attributes_callback_t uic_mqtt_dotdot_by_group_unify_switch_all_write_attributes_callback = nullptr; ++ ++ + static uic_mqtt_dotdot_by_group_unify_humidity_control_mode_set_callback_t uic_mqtt_dotdot_by_group_unify_humidity_control_mode_set_callback = nullptr; + static uic_mqtt_dotdot_by_group_unify_humidity_control_setpoint_set_callback_t uic_mqtt_dotdot_by_group_unify_humidity_control_setpoint_set_callback = nullptr; + 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; +@@ -1874,6 +1877,15 @@ void uic_mqtt_dotdot_by_group_unify_thermostat_write_attributes_callback_set( + + + ++// Callbacks setters ++void uic_mqtt_dotdot_by_group_unify_switch_all_write_attributes_callback_set( ++ const uic_mqtt_dotdot_by_group_unify_switch_all_write_attributes_callback_t callback) ++{ ++ uic_mqtt_dotdot_by_group_unify_switch_all_write_attributes_callback = callback; ++} ++ ++ ++ + // Callbacks setters + + void uic_mqtt_dotdot_by_group_unify_humidity_control_mode_set_callback_set(const uic_mqtt_dotdot_by_group_unify_humidity_control_mode_set_callback_t callback) +@@ -23330,6 +23342,91 @@ sl_status_t uic_mqtt_dotdot_by_group_unify_thermostat_init() + + + ++static void uic_mqtt_dotdot_on_by_group_unify_switch_all_WriteAttributes( ++ const char *topic, ++ const char *message, ++ const size_t message_length) ++{ ++ ++ if ((group_dispatch_callback == nullptr) && (uic_mqtt_dotdot_by_group_unify_switch_all_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_unify_switch_all_write_attributes_callback().empty())) { ++ try { ++ group_dispatch_callback(group_id, ++ "UnifySwitchAll", ++ "WriteAttributes", ++ message, ++ message_length, ++ uic_mqtt_dotdot_on_unify_switch_all_WriteAttributes); ++ ++ } catch (...) { ++ sl_log_debug(LOG_TAG, "UnifySwitchAll: Unable to parse JSON payload.\n"); ++ return; ++ } ++ } else if (uic_mqtt_dotdot_by_group_unify_switch_all_write_attributes_callback != nullptr) { ++ ++ uic_mqtt_dotdot_unify_switch_all_state_t new_state = {}; ++ uic_mqtt_dotdot_unify_switch_all_updated_state_t new_updated_state = {}; ++ ++ ++ nlohmann::json jsn; ++ try { ++ jsn = nlohmann::json::parse(std::string(message)); ++ ++ uic_mqtt_dotdot_parse_unify_switch_all_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, "UnifySwitchAll", "WriteAttributes"); ++ return; ++ } catch (const nlohmann::json::exception& e) { ++ // Catch JSON object field parsing errors ++ sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifySwitchAll", "WriteAttributes", e.what()); ++ return; ++ } catch (const std::exception& e) { ++ sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifySwitchAll", "WriteAttributes", ""); ++ return; ++ } ++ ++ uic_mqtt_dotdot_by_group_unify_switch_all_write_attributes_callback( ++ group_id, ++ new_state, ++ new_updated_state ++ ); ++ } ++} ++ ++sl_status_t uic_mqtt_dotdot_by_group_unify_switch_all_init() ++{ ++ std::string subscription_topic; ++ const std::string topic_bygroup = TOPIC_BY_GROUP_PREFIX; ++ if(uic_mqtt_dotdot_by_group_unify_switch_all_write_attributes_callback) { ++ subscription_topic = topic_bygroup + "UnifySwitchAll/Commands/WriteAttributes"; ++ uic_mqtt_subscribe(subscription_topic.c_str(), uic_mqtt_dotdot_on_by_group_unify_switch_all_WriteAttributes); ++ } ++ ++ return SL_STATUS_OK; ++} ++ ++ ++ ++ + // Callback function for incoming publications on ucl/by-group/+/UnifyHumidityControl/Commands/ModeSet + static void uic_mqtt_dotdot_on_by_group_unify_humidity_control_mode_set( + const char *topic, +@@ -23890,6 +23987,8 @@ void uic_mqtt_dotdot_set_group_dispatch_callback(group_dispatch_t callback) + + uic_mqtt_subscribe("ucl/by-group/+/UnifyThermostat/Commands/WriteAttributes", uic_mqtt_dotdot_on_by_group_unify_thermostat_WriteAttributes); + ++ uic_mqtt_subscribe("ucl/by-group/+/UnifySwitchAll/Commands/WriteAttributes", uic_mqtt_dotdot_on_by_group_unify_switch_all_WriteAttributes); ++ + uic_mqtt_subscribe("ucl/by-group/+/UnifyHumidityControl/Commands/WriteAttributes", uic_mqtt_dotdot_on_by_group_unify_humidity_control_WriteAttributes); + 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); +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..4f3611bb61 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 +@@ -10253,6 +10253,21 @@ std::string get_enum_value_name( + #endif + } + ++ if (64800 == cluster_id) { ++ #ifdef UNIFY_SWITCH_ALL_MODE_ENUM_NAME_AVAILABLE ++ if (0 == attribute_id) { ++ // FIXME: Some attributes don't work because multi-upper case names end up like this: unify_switch_allmode instead of this: unify_switch_all_mode ++ return unify_switch_all_mode_get_enum_value_name(value); ++ } ++ #endif ++ #ifdef UNIFY_SWITCH_ALL_ON_OFF_ENUM_NAME_AVAILABLE ++ if (1 == attribute_id) { ++ // FIXME: Some attributes don't work because multi-upper case names end up like this: unify_switch_allon_off instead of this: unify_switch_all_on_off ++ return unify_switch_all_on_off_get_enum_value_name(value); ++ } ++ #endif ++ } ++ + if (64928 == cluster_id) { + #ifdef UNIFY_HUMIDITY_CONTROL_REPORTING_MODE_ENUM_NAME_AVAILABLE + if (1 == attribute_id) { +@@ -14760,6 +14775,21 @@ uint32_t get_enum_name_value( + #endif + } + ++ if (64800 == cluster_id) { ++ #ifdef UNIFY_SWITCH_ALL_MODE_ENUM_NAME_AVAILABLE ++ if (0 == attribute_id) { ++ // FIXME: Some attributes don't work because multi-upper case names end up like this: unify_switch_allmode instead of this: unify_switch_all_mode ++ return unify_switch_all_mode_get_enum_value_number(name); ++ } ++ #endif ++ #ifdef UNIFY_SWITCH_ALL_ON_OFF_ENUM_NAME_AVAILABLE ++ if (1 == attribute_id) { ++ // FIXME: Some attributes don't work because multi-upper case names end up like this: unify_switch_allon_off instead of this: unify_switch_all_on_off ++ return unify_switch_all_on_off_get_enum_value_number(name); ++ } ++ #endif ++ } ++ + if (64928 == cluster_id) { + #ifdef UNIFY_HUMIDITY_CONTROL_REPORTING_MODE_ENUM_NAME_AVAILABLE + if (1 == attribute_id) { +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..546783a63d 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 +@@ -2813,6 +2813,49 @@ void uic_mqtt_dotdot_unify_thermostat_publish_supported_generated_commands( + } + + ++/** ++ * @brief Sends/Publishes a the SupportedGenerated commands for ++ * the UnifySwitchAll cluster for a UNID/Endpoint ++ * ++ * Publication will be made at the following topic ++ * ucl/by-unid/UNID/epID/UnifySwitchAll/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_unify_switch_all_publish_supported_generated_commands( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint, ++ const uic_mqtt_dotdot_unify_switch_all_supported_commands_t *command_list) ++{ ++ std::string topic = "ucl/by-unid/" + std::string(unid); ++ topic += "/ep"+ std::to_string(endpoint); ++ topic += "/UnifySwitchAll/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); ++ ++} ++ ++ + /** + * @brief Sends/Publishes a the SupportedGenerated commands for + * the UnifyHumidityControl cluster for a UNID/Endpoint +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..be33552f9f 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 +@@ -3660,6 +3660,7 @@ static void unset_all_callbacks() + uic_mqtt_dotdot_unify_fan_control_turn_off_callback_clear(); + uic_mqtt_dotdot_clear_unify_fan_control_write_attributes_callbacks(); + uic_mqtt_dotdot_clear_unify_thermostat_write_attributes_callbacks(); ++ uic_mqtt_dotdot_clear_unify_switch_all_write_attributes_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(); +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..0e54662a20 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 +@@ -873,6 +873,10 @@ def DOTDOT_ATTRIBUTE_ID_UNIFY_THERMOSTAT_THERMOSTAT_MODE 0xfd150001 + def DOTDOT_ATTRIBUTE_ID_UNIFY_THERMOSTAT_SUPPORTED_THERMOSTAT_MODE 0xfd150002 + def DOTDOT_ATTRIBUTE_ID_UNIFY_THERMOSTAT_OPERATING_STATE 0xfd150003 + ++// This represents the attributes in the DotDot UnifySwitchAll cluster ++def DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_MODE 0xfd200000 ++def DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_ON_OFF 0xfd200001 ++ + // This represents the attributes in the DotDot UnifyHumidityControl cluster + def DOTDOT_ATTRIBUTE_ID_UNIFY_HUMIDITY_CONTROL_REPORTING_MODE 0xfda00001 + def DOTDOT_ATTRIBUTE_ID_UNIFY_HUMIDITY_CONTROL_SUPPORTED_REPORTING_MODE 0xfda00002 +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..29a93ab831 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 +@@ -873,6 +873,10 @@ def zb_ThermostatMode 0xfd150001 + def zb_SupportedThermostatMode 0xfd150002 + def zb_OperatingState 0xfd150003 + ++// This represents short CamelCase labels the attributes in the DotDot UnifySwitchAll cluster ++def zb_Mode 0xfd200000 ++def zb_OnOff 0xfd200001 ++ + // This represents short CamelCase labels the attributes in the DotDot UnifyHumidityControl cluster + def zb_ReportingMode 0xfda00001 + def zb_SupportedReportingMode 0xfda00002 +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..fc2013788c 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 +@@ -76256,6 +76256,246 @@ bool dotdot_is_any_unify_thermostat_writable_attribute_supported( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint_id); + ++//////////////////////////////////////////////////////////////////////////////// ++// Start of cluster UnifySwitchAll ++//////////////////////////////////////////////////////////////////////////////// ++// UnifySwitchAll Mode ++/** ++ * @brief Verifies if the DotDot UnifySwitchAll - Mode is supported ++ * under a UNID/EndpoinID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * ++ * @returns true if Mode is supported ++ * @returns false if Mode is not supported ++ */ ++bool dotdot_is_supported_unify_switch_all_mode ( ++ const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Gets the DotDot UnifySwitchAll - Mode 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 Mode attribute ++ */ ++uint8_t dotdot_get_unify_switch_all_mode( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state); ++ ++/** ++ * @brief Set the DotDot UnifySwitchAll - Mode 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_mode new value to set ++ * @returns sl_status_t SL_STATUS_OK on success ++ */ ++sl_status_t dotdot_set_unify_switch_all_mode( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state, ++ uint8_t new_mode ++ ); ++ ++/** ++ * @brief Undefines the Reported value of the the DotDot UnifySwitchAll - Mode ++ * 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_unify_switch_all_mode_undefine_reported( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Undefines the Desired value of the DotDot ++ * UnifySwitchAll - Mode 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_unify_switch_all_mode_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 ++ * UnifySwitchAll - Mode 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_unify_switch_all_mode_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 ++ * UnifySwitchAll - Mode 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_unify_switch_all_mode_is_desired_defined( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Creates a DotDot UnifySwitchAll - Mode 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_unify_switch_all_mode( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++// UnifySwitchAll OnOff ++/** ++ * @brief Verifies if the DotDot UnifySwitchAll - OnOff is supported ++ * under a UNID/EndpoinID ++ * ++ * @param unid Node's UNID ++ * @param endpoint_id Endpoint ID ++ * ++ * @returns true if OnOff is supported ++ * @returns false if OnOff is not supported ++ */ ++bool dotdot_is_supported_unify_switch_all_on_off ( ++ const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Gets the DotDot UnifySwitchAll - OnOff 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 OnOff attribute ++ */ ++uint8_t dotdot_get_unify_switch_all_on_off( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state); ++ ++/** ++ * @brief Set the DotDot UnifySwitchAll - OnOff 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_on_off new value to set ++ * @returns sl_status_t SL_STATUS_OK on success ++ */ ++sl_status_t dotdot_set_unify_switch_all_on_off( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state, ++ uint8_t new_on_off ++ ); ++ ++/** ++ * @brief Undefines the Reported value of the the DotDot UnifySwitchAll - OnOff ++ * 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_unify_switch_all_on_off_undefine_reported( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Undefines the Desired value of the DotDot ++ * UnifySwitchAll - OnOff 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_unify_switch_all_on_off_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 ++ * UnifySwitchAll - OnOff 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_unify_switch_all_on_off_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 ++ * UnifySwitchAll - OnOff 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_unify_switch_all_on_off_is_desired_defined( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Creates a DotDot UnifySwitchAll - OnOff 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_unify_switch_all_on_off( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ ++/** ++ * @brief Checks if a UNID/Endpoint supports any attribute for the UnifySwitchAll ++ * 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_unify_switch_all_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 ++ * UnifySwitchAll 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_unify_switch_all_writable_attribute_supported( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id); ++ + //////////////////////////////////////////////////////////////////////////////// + // Start of cluster UnifyHumidityControl + //////////////////////////////////////////////////////////////////////////////// +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..241b9039d5 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 +@@ -781,6 +781,9 @@ DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE , 0xfd14 + DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_UNIFY_THERMOSTAT_THERMOSTAT_MODE , 0xfd150001) + DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_UNIFY_THERMOSTAT_SUPPORTED_THERMOSTAT_MODE , 0xfd150002) + DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_UNIFY_THERMOSTAT_OPERATING_STATE , 0xfd150003) ++// Attribute Defines for UnifySwitchAll ++DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_MODE , 0xfd200000) ++DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_ON_OFF , 0xfd200001) + // Attribute Defines for UnifyHumidityControl + DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_UNIFY_HUMIDITY_CONTROL_REPORTING_MODE , 0xfda00001) + DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_UNIFY_HUMIDITY_CONTROL_SUPPORTED_REPORTING_MODE , 0xfda00002) +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..6a187f6596 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 +@@ -25395,6 +25395,243 @@ static void unify_thermostat_cluster_cluster_revision_callback( + } + + ++/** ++ * @brief Publishes the desired value of an updated attribute store node for ++ * the UnifySwitchAll cluster. ++ * @param updated_node Updated attribute store node ++ * @param change Type of change applied ++ */ ++static void unify_switch_all_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_UNIFY_SWITCH_ALL_MODE) { ++ uic_mqtt_dotdot_unify_switch_all_mode_publish( ++ base_topic.c_str(), ++ static_cast(attr.desired_or_reported()), ++ UCL_MQTT_PUBLISH_TYPE_DESIRED); ++ return; ++ } ++ if (type == DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_ON_OFF) { ++ uic_mqtt_dotdot_unify_switch_all_on_off_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 UnifySwitchAll cluster. ++ * @param updated_node Updated attribute store node ++ * @param change Type of change applied ++ */ ++static void unify_switch_all_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_UNIFY_SWITCH_ALL_MODE: ++ // clang-format on ++ sl_log_debug(LOG_TAG, ++ "Unretaining UnifySwitchAll::Mode under topic %s", ++ base_topic.c_str()); ++ // clang-format off ++ uic_mqtt_dotdot_unify_switch_all_mode_unretain(base_topic.c_str(), UCL_MQTT_PUBLISH_TYPE_ALL); ++ break; ++ case DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_ON_OFF: ++ // clang-format on ++ sl_log_debug(LOG_TAG, ++ "Unretaining UnifySwitchAll::OnOff under topic %s", ++ base_topic.c_str()); ++ // clang-format off ++ uic_mqtt_dotdot_unify_switch_all_on_off_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_UNIFY_SWITCH_ALL_MODE) { ++ uic_mqtt_dotdot_unify_switch_all_mode_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_UNIFY_SWITCH_ALL_ON_OFF) { ++ uic_mqtt_dotdot_unify_switch_all_on_off_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 unify_switch_all_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 + "/UnifySwitchAll/Attributes/ClusterRevision"; ++ if (uic_mqtt_count_topics(cluster_revision_topic.c_str()) == 0) { ++ uic_mqtt_dotdot_unify_switch_all_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_unify_switch_all_attribute_supported(unid, endpoint_id)) { ++ base_topic += "/UnifySwitchAll"; ++ sl_log_debug(LOG_TAG, "No more attributes supported for UnifySwitchAll 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()); ++ } ++ } ++} ++ ++ + /** + * @brief Publishes the desired value of an updated attribute store node for + * the UnifyHumidityControl cluster. +@@ -35867,6 +36104,34 @@ sl_status_t unify_dotdot_attribute_store_attribute_publisher_init() + unify_thermostat_cluster_cluster_revision_callback, + DOTDOT_ATTRIBUTE_ID_UNIFY_THERMOSTAT_OPERATING_STATE); + //Desired attribute state ++ attribute_store_register_callback_by_type_and_state( ++ unify_switch_all_cluster_publish_desired_value_callback, ++ DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_MODE, ++ DESIRED_ATTRIBUTE); ++ //Reported attribute state ++ attribute_store_register_callback_by_type_and_state( ++ unify_switch_all_cluster_publish_reported_value_callback, ++ DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_MODE, ++ REPORTED_ATTRIBUTE); ++ //registering a callback when an attribute is created for publishing cluster revision ++ attribute_store_register_callback_by_type( ++ unify_switch_all_cluster_cluster_revision_callback, ++ DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_MODE); ++ //Desired attribute state ++ attribute_store_register_callback_by_type_and_state( ++ unify_switch_all_cluster_publish_desired_value_callback, ++ DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_ON_OFF, ++ DESIRED_ATTRIBUTE); ++ //Reported attribute state ++ attribute_store_register_callback_by_type_and_state( ++ unify_switch_all_cluster_publish_reported_value_callback, ++ DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_ON_OFF, ++ REPORTED_ATTRIBUTE); ++ //registering a callback when an attribute is created for publishing cluster revision ++ attribute_store_register_callback_by_type( ++ unify_switch_all_cluster_cluster_revision_callback, ++ DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_ON_OFF); ++ //Desired attribute state + attribute_store_register_callback_by_type_and_state( + unify_humidity_control_cluster_publish_desired_value_callback, + DOTDOT_ATTRIBUTE_ID_UNIFY_HUMIDITY_CONTROL_REPORTING_MODE, +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..1f52c8f2a1 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 +@@ -4435,6 +4435,41 @@ static sl_status_t uic_mqtt_dotdot_unify_thermostat_force_read_attributes_callba + return SL_STATUS_OK; + } + //////////////////////////////////////////////////////////////////////////////// ++// Start of cluster UnifySwitchAll ++//////////////////////////////////////////////////////////////////////////////// ++static sl_status_t uic_mqtt_dotdot_unify_switch_all_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_unify_switch_all_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_unify_switch_all_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.mode) { ++ if (SL_STATUS_OK == dotdot_unify_switch_all_mode_undefine_reported(unid, endpoint_id)) { ++ sl_log_debug(LOG_TAG, "Undefined Reported value of UnifySwitchAll::Mode under %s - Endpoint %d", unid, endpoint_id); ++ } ++ } ++ if (true == attribute_list.on_off) { ++ if (SL_STATUS_OK == dotdot_unify_switch_all_on_off_undefine_reported(unid, endpoint_id)) { ++ sl_log_debug(LOG_TAG, "Undefined Reported value of UnifySwitchAll::OnOff under %s - Endpoint %d", unid, endpoint_id); ++ } ++ } ++ return SL_STATUS_OK; ++} ++//////////////////////////////////////////////////////////////////////////////// + // Start of cluster UnifyHumidityControl + //////////////////////////////////////////////////////////////////////////////// + static sl_status_t uic_mqtt_dotdot_unify_humidity_control_force_read_attributes_callback ( +@@ -4664,6 +4699,8 @@ sl_status_t + + uic_mqtt_dotdot_set_unify_thermostat_force_read_attributes_callback(&uic_mqtt_dotdot_unify_thermostat_force_read_attributes_callback); + ++ uic_mqtt_dotdot_set_unify_switch_all_force_read_attributes_callback(&uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback); ++ + uic_mqtt_dotdot_set_unify_humidity_control_force_read_attributes_callback(&uic_mqtt_dotdot_unify_humidity_control_force_read_attributes_callback); + + // clang-format on +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..5430df175d 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 +@@ -82053,6 +82053,259 @@ bool dotdot_is_any_unify_thermostat_writable_attribute_supported( + return false; + } + //////////////////////////////////////////////////////////////////////////////// ++// Start of cluster UnifySwitchAll ++//////////////////////////////////////////////////////////////////////////////// ++bool dotdot_is_supported_unify_switch_all_mode( ++ 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_UNIFY_SWITCH_ALL_MODE); ++ return attribute_store_node_exists(node); ++} ++ ++uint8_t dotdot_get_unify_switch_all_mode( ++ 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_UNIFY_SWITCH_ALL_MODE); ++ ++ uint8_t result = {}; ++ attribute_store_read_value(node, ++ value_state, ++ (uint8_t *)&result, ++ sizeof(result)); ++ return result; ++} ++ ++sl_status_t dotdot_set_unify_switch_all_mode( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state, ++ uint8_t new_mode ++ ) ++{ ++ 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_UNIFY_SWITCH_ALL_MODE); ++ ++ return attribute_store_set_node_attribute_value(node, ++ value_state, ++ (uint8_t *)&new_mode, ++ sizeof(uint8_t)); ++ } ++ ++sl_status_t dotdot_unify_switch_all_mode_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_UNIFY_SWITCH_ALL_MODE); ++ attribute_store_undefine_reported(node); ++ return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; ++} ++ ++sl_status_t dotdot_unify_switch_all_mode_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_UNIFY_SWITCH_ALL_MODE); ++ attribute_store_undefine_desired(node); ++ return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; ++} ++ ++ ++bool dotdot_unify_switch_all_mode_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_UNIFY_SWITCH_ALL_MODE); ++ return attribute_store_is_reported_defined(node); ++} ++ ++bool dotdot_unify_switch_all_mode_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_UNIFY_SWITCH_ALL_MODE); ++ return attribute_store_is_desired_defined(node); ++} ++ ++sl_status_t dotdot_create_unify_switch_all_mode( ++ 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_UNIFY_SWITCH_ALL_MODE); ++ ++ return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; ++} ++bool dotdot_is_supported_unify_switch_all_on_off( ++ 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_UNIFY_SWITCH_ALL_ON_OFF); ++ return attribute_store_node_exists(node); ++} ++ ++uint8_t dotdot_get_unify_switch_all_on_off( ++ 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_UNIFY_SWITCH_ALL_ON_OFF); ++ ++ uint8_t result = {}; ++ attribute_store_read_value(node, ++ value_state, ++ (uint8_t *)&result, ++ sizeof(result)); ++ return result; ++} ++ ++sl_status_t dotdot_set_unify_switch_all_on_off( ++ const dotdot_unid_t unid, ++ dotdot_endpoint_id_t endpoint_id, ++ attribute_store_node_value_state_t value_state, ++ uint8_t new_on_off ++ ) ++{ ++ 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_UNIFY_SWITCH_ALL_ON_OFF); ++ ++ return attribute_store_set_node_attribute_value(node, ++ value_state, ++ (uint8_t *)&new_on_off, ++ sizeof(uint8_t)); ++ } ++ ++sl_status_t dotdot_unify_switch_all_on_off_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_UNIFY_SWITCH_ALL_ON_OFF); ++ attribute_store_undefine_reported(node); ++ return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; ++} ++ ++sl_status_t dotdot_unify_switch_all_on_off_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_UNIFY_SWITCH_ALL_ON_OFF); ++ attribute_store_undefine_desired(node); ++ return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; ++} ++ ++ ++bool dotdot_unify_switch_all_on_off_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_UNIFY_SWITCH_ALL_ON_OFF); ++ return attribute_store_is_reported_defined(node); ++} ++ ++bool dotdot_unify_switch_all_on_off_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_UNIFY_SWITCH_ALL_ON_OFF); ++ return attribute_store_is_desired_defined(node); ++} ++ ++sl_status_t dotdot_create_unify_switch_all_on_off( ++ 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_UNIFY_SWITCH_ALL_ON_OFF); ++ ++ return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; ++} ++ ++bool dotdot_is_any_unify_switch_all_attribute_supported( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) { ++ ++ if (true == dotdot_is_supported_unify_switch_all_mode(unid, endpoint_id)) { ++ return true; ++ } ++ if (true == dotdot_is_supported_unify_switch_all_on_off(unid, endpoint_id)) { ++ return true; ++ } ++ ++ return false; ++} ++ ++bool dotdot_is_any_unify_switch_all_writable_attribute_supported( ++ const dotdot_unid_t unid, ++ const dotdot_endpoint_id_t endpoint_id) { ++ ++ if (true == dotdot_is_supported_unify_switch_all_mode(unid, endpoint_id)) { ++ return true; ++ } ++ if (true == dotdot_is_supported_unify_switch_all_on_off(unid, endpoint_id)) { ++ return true; ++ } ++ ++ return false; ++} ++//////////////////////////////////////////////////////////////////////////////// + // Start of cluster UnifyHumidityControl + //////////////////////////////////////////////////////////////////////////////// + bool dotdot_is_supported_unify_humidity_control_reporting_mode( +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..f3c98f2033 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 +@@ -16999,6 +16999,54 @@ sl_status_t unify_dotdot_attribute_store_registration_init() + // 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 UnifySwitchAll Mode, " ++ "type: uint8 // uint8_t"); ++ } ++ ++ status |= attribute_store_register_type( ++ DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_MODE, ++ "ZCL UnifySwitchAll Mode", ++ 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 UnifySwitchAll OnOff, " ++ "type: uint8 // uint8_t"); ++ } ++ ++ status |= attribute_store_register_type( ++ DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_ON_OFF, ++ "ZCL UnifySwitchAll OnOff", ++ ATTRIBUTE_STORE_INVALID_ATTRIBUTE_TYPE, ++ storage_type); ++ } ++ ++ // clang-format off ++ // clang-format on ++ + { + // ModeType // ModeType // ModeType + std::string attribute_type_string = "ModeType"; +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..2107214995 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 +@@ -2526,6 +2526,44 @@ static sl_status_t unify_thermostat_cluster_write_attributes_callback( + return SL_STATUS_OK; + } + //////////////////////////////////////////////////////////////////////////////// ++// Start of cluster UnifySwitchAll ++//////////////////////////////////////////////////////////////////////////////// ++// WriteAttribute Callbacks unify_switch_all ++static sl_status_t unify_switch_all_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_unify_switch_all_state_t attributes, ++ uic_mqtt_dotdot_unify_switch_all_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_unify_switch_all_writable_attribute_supported(unid, endpoint_id) ? ++ SL_STATUS_OK : SL_STATUS_FAIL; ++ } else { ++ return SL_STATUS_FAIL; ++ } ++ } ++ ++ sl_log_debug(LOG_TAG, ++ "unify_switch_all: Incoming WriteAttributes command for %s, endpoint %d.\n", ++ unid, ++ endpoint_id); ++ if (true == updated_attributes.mode) { ++ sl_log_debug(LOG_TAG, "Updating desired value for Mode attribute"); ++ dotdot_set_unify_switch_all_mode(unid, endpoint_id, DESIRED_ATTRIBUTE, attributes.mode); ++ } ++ if (true == updated_attributes.on_off) { ++ sl_log_debug(LOG_TAG, "Updating desired value for OnOff attribute"); ++ dotdot_set_unify_switch_all_on_off(unid, endpoint_id, DESIRED_ATTRIBUTE, attributes.on_off); ++ } ++ return SL_STATUS_OK; ++} ++//////////////////////////////////////////////////////////////////////////////// + // Start of cluster UnifyHumidityControl + //////////////////////////////////////////////////////////////////////////////// + // WriteAttribute Callbacks unify_humidity_control +@@ -2722,6 +2760,9 @@ sl_status_t + uic_mqtt_dotdot_set_unify_thermostat_write_attributes_callback( + &unify_thermostat_cluster_write_attributes_callback); + ++ uic_mqtt_dotdot_set_unify_switch_all_write_attributes_callback( ++ &unify_switch_all_cluster_write_attributes_callback); ++ + uic_mqtt_dotdot_set_unify_humidity_control_write_attributes_callback( + &unify_humidity_control_cluster_write_attributes_callback); + +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..c7f0158026 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 +@@ -1312,6 +1312,16 @@ uic_mqtt_dotdot_unify_thermostat_write_attributes_callback_t get_uic_mqtt_dotdot + return test_uic_mqtt_dotdot_unify_thermostat_write_attributes_callback; + } + ++static uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback_t test_uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback = NULL; ++static uic_mqtt_dotdot_unify_switch_all_write_attributes_callback_t test_uic_mqtt_dotdot_unify_switch_all_write_attributes_callback = NULL; ++ ++uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback_t get_uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback(){ ++ return test_uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback; ++} ++uic_mqtt_dotdot_unify_switch_all_write_attributes_callback_t get_uic_mqtt_dotdot_unify_switch_all_write_attributes_callback(){ ++ return test_uic_mqtt_dotdot_unify_switch_all_write_attributes_callback; ++} ++ + static uic_mqtt_dotdot_unify_humidity_control_force_read_attributes_callback_t test_uic_mqtt_dotdot_unify_humidity_control_force_read_attributes_callback = NULL; + static uic_mqtt_dotdot_unify_humidity_control_write_attributes_callback_t test_uic_mqtt_dotdot_unify_humidity_control_write_attributes_callback = NULL; + +@@ -2911,6 +2921,16 @@ void set_uic_mqtt_dotdot_unify_thermostat_write_attributes_callback_stub( + { + test_uic_mqtt_dotdot_unify_thermostat_write_attributes_callback = callback; + } ++void set_uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback_stub( ++ const uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback_t callback, int cmock_num_calls) ++{ ++ test_uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback = callback; ++} ++void set_uic_mqtt_dotdot_unify_switch_all_write_attributes_callback_stub( ++ const uic_mqtt_dotdot_unify_switch_all_write_attributes_callback_t callback, int cmock_num_calls) ++{ ++ test_uic_mqtt_dotdot_unify_switch_all_write_attributes_callback = callback; ++} + void set_uic_mqtt_dotdot_unify_humidity_control_force_read_attributes_callback_stub( + const uic_mqtt_dotdot_unify_humidity_control_force_read_attributes_callback_t callback, int cmock_num_calls) + { +@@ -3851,6 +3871,12 @@ void setUp() + test_uic_mqtt_dotdot_unify_thermostat_write_attributes_callback = NULL; + uic_mqtt_dotdot_set_unify_thermostat_write_attributes_callback_Stub( + &set_uic_mqtt_dotdot_unify_thermostat_write_attributes_callback_stub); ++ test_uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback = NULL; ++ uic_mqtt_dotdot_set_unify_switch_all_force_read_attributes_callback_Stub( ++ &set_uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback_stub); ++ test_uic_mqtt_dotdot_unify_switch_all_write_attributes_callback = NULL; ++ uic_mqtt_dotdot_set_unify_switch_all_write_attributes_callback_Stub( ++ &set_uic_mqtt_dotdot_unify_switch_all_write_attributes_callback_stub); + test_uic_mqtt_dotdot_unify_humidity_control_force_read_attributes_callback = NULL; + uic_mqtt_dotdot_set_unify_humidity_control_force_read_attributes_callback_Stub( + &set_uic_mqtt_dotdot_unify_humidity_control_force_read_attributes_callback_stub); +@@ -4612,6 +4638,8 @@ void test_automatic_deduction_of_supported_commands() + TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_unify_thermostat_thermostat_mode(expected_unid,expected_endpoint_id) ); + TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_unify_thermostat_supported_thermostat_mode(expected_unid,expected_endpoint_id) ); + TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_unify_thermostat_operating_state(expected_unid,expected_endpoint_id) ); ++ TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_unify_switch_all_mode(expected_unid,expected_endpoint_id) ); ++ TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_unify_switch_all_on_off(expected_unid,expected_endpoint_id) ); + TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_unify_humidity_control_reporting_mode(expected_unid,expected_endpoint_id) ); + TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_unify_humidity_control_supported_reporting_mode(expected_unid,expected_endpoint_id) ); + TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_unify_humidity_control_current_state(expected_unid,expected_endpoint_id) ); +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..5ef50633ea 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 +@@ -794,6 +794,10 @@ + uic_mqtt_dotdot_unify_thermostat_write_attributes_callback_t get_uic_mqtt_dotdot_unify_thermostat_write_attributes_callback(); + + ++ uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback_t get_uic_mqtt_dotdot_unify_switch_all_force_read_attributes_callback(); ++ uic_mqtt_dotdot_unify_switch_all_write_attributes_callback_t get_uic_mqtt_dotdot_unify_switch_all_write_attributes_callback(); ++ ++ + uic_mqtt_dotdot_unify_humidity_control_force_read_attributes_callback_t get_uic_mqtt_dotdot_unify_humidity_control_force_read_attributes_callback(); + uic_mqtt_dotdot_unify_humidity_control_write_attributes_callback_t get_uic_mqtt_dotdot_unify_humidity_control_write_attributes_callback(); + +-- +2.39.5 + diff --git a/patches/UnifySDK/0009-UIC-3444-Regenerate-zap-files.patch b/patches/UnifySDK/0009-UIC-3444-Regenerate-zap-files.patch new file mode 100644 index 000000000..296085d79 --- /dev/null +++ b/patches/UnifySDK/0009-UIC-3444-Regenerate-zap-files.patch @@ -0,0 +1,605 @@ +From 212a71ab46ee6ff264abdd55b5652522173dc70f Mon Sep 17 00:00:00 2001 +From: Philippe Coval +Date: Mon, 22 Sep 2025 10:58:04 +0200 +Subject: [PATCH] UIC-3444: Regenerate zap files + +Signed-off-by: Philippe Coval +--- + .../src/eed_attribute_store_clusters.c | 8 + + .../src/eed_dotdot_create_clusters.cpp | 33 ++ + .../include/dotdot_attribute_id_definitions.h | 3 + + .../dotdot_cluster_command_id_definitions.h | 2 + + .../include/dotdot_cluster_id_definitions.h | 4 + + .../readme_ucl_mqtt_reference.md | 315 ++++++++++++++++++ + .../src/dotdot_attribute_id_definitions.c | 37 ++ + .../src/dotdot_cluster_id_definitions.c | 5 + + .../zap-generated/src/dotdot_mqtt.hpp | 12 +- + 9 files changed, 413 insertions(+), 6 deletions(-) + +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..e94571386c 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 +@@ -2715,6 +2715,14 @@ sl_status_t eed_attribute_store_clusters_init() + &on_zcl_desired_value_update, + DOTDOT_ATTRIBUTE_ID_UNIFY_THERMOSTAT_OPERATING_STATE, + DESIRED_ATTRIBUTE); ++ attribute_store_register_callback_by_type_and_state( ++ &on_zcl_desired_value_update, ++ DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_MODE, ++ DESIRED_ATTRIBUTE); ++ attribute_store_register_callback_by_type_and_state( ++ &on_zcl_desired_value_update, ++ DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_ON_OFF, ++ DESIRED_ATTRIBUTE); + attribute_store_register_callback_by_type_and_state( + &on_zcl_desired_value_update, + DOTDOT_ATTRIBUTE_ID_UNIFY_HUMIDITY_CONTROL_REPORTING_MODE, +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..9837b3e27b 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 +@@ -4040,6 +4040,21 @@ void dotdot_create_unify_thermostat_wrapper(const dotdot_unid_t unid, const dotd + + } + ++void dotdot_create_unify_switch_all_wrapper(const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id){ ++ // Create and set a default value if undefined. ++ dotdot_create_unify_switch_all_mode(unid,endpoint_id); ++ if (false == dotdot_unify_switch_all_mode_is_reported_defined(unid,endpoint_id)){ ++ dotdot_set_unify_switch_all_mode(unid,endpoint_id,REPORTED_ATTRIBUTE, static_cast(0)); ++ } ++ ++ // Create and set a default value if undefined. ++ dotdot_create_unify_switch_all_on_off(unid,endpoint_id); ++ if (false == dotdot_unify_switch_all_on_off_is_reported_defined(unid,endpoint_id)){ ++ dotdot_set_unify_switch_all_on_off(unid,endpoint_id,REPORTED_ATTRIBUTE, static_cast(0)); ++ } ++ ++} ++ + void dotdot_create_unify_humidity_control_wrapper(const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id){ + // Create and set a default value if undefined. + dotdot_create_unify_humidity_control_reporting_mode(unid,endpoint_id); +@@ -6566,6 +6581,22 @@ void dotdot_unretain_unify_thermostat_wrapper(const dotdot_unid_t unid, const do + uic_mqtt_dotdot_unify_thermostat_publish_empty_supported_commands(unid, endpoint_id); + } + ++void dotdot_unretain_unify_switch_all_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_unify_switch_all_mode_unretain(base_topic,UCL_MQTT_PUBLISH_TYPE_ALL); ++ ep_node.child_by_type(DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_MODE).delete_node(); ++ ++ uic_mqtt_dotdot_unify_switch_all_on_off_unretain(base_topic,UCL_MQTT_PUBLISH_TYPE_ALL); ++ ep_node.child_by_type(DOTDOT_ATTRIBUTE_ID_UNIFY_SWITCH_ALL_ON_OFF).delete_node(); ++ ++ ++ uic_mqtt_dotdot_unify_switch_all_unretain_cluster_revision(base_topic); ++ uic_mqtt_dotdot_unify_switch_all_publish_empty_supported_commands(unid, endpoint_id); ++} ++ + void dotdot_unretain_unify_humidity_control_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); +@@ -6710,6 +6741,7 @@ std::map CreateClusterMap = { + { "Descriptor", dotdot_create_descriptor_wrapper }, + { "UnifyFanControl", dotdot_create_unify_fan_control_wrapper }, + { "UnifyThermostat", dotdot_create_unify_thermostat_wrapper }, ++{ "UnifySwitchAll", dotdot_create_unify_switch_all_wrapper }, + { "UnifyHumidityControl", dotdot_create_unify_humidity_control_wrapper }, + }; + +@@ -6761,5 +6793,6 @@ std::map CreateUnretainMap = { + { "Descriptor", dotdot_unretain_descriptor_wrapper }, + { "UnifyFanControl", dotdot_unretain_unify_fan_control_wrapper }, + { "UnifyThermostat", dotdot_unretain_unify_thermostat_wrapper }, ++{ "UnifySwitchAll", dotdot_unretain_unify_switch_all_wrapper }, + { "UnifyHumidityControl", dotdot_unretain_unify_humidity_control_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..9c6224d414 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 +@@ -864,6 +864,9 @@ typedef enum { + #define DOTDOT_UNIFY_THERMOSTAT_THERMOSTAT_MODE_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x1) + #define DOTDOT_UNIFY_THERMOSTAT_SUPPORTED_THERMOSTAT_MODE_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x2) + #define DOTDOT_UNIFY_THERMOSTAT_OPERATING_STATE_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x3) ++// Definitions for cluster: UnifySwitchAll ++#define DOTDOT_UNIFY_SWITCH_ALL_MODE_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x0) ++#define DOTDOT_UNIFY_SWITCH_ALL_ON_OFF_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x1) + // Definitions for cluster: UnifyHumidityControl + #define DOTDOT_UNIFY_HUMIDITY_CONTROL_REPORTING_MODE_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x1) + #define DOTDOT_UNIFY_HUMIDITY_CONTROL_SUPPORTED_REPORTING_MODE_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x2) +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..b39caaa52e 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 +@@ -371,6 +371,8 @@ + + // Commands for cluster: UnifyThermostat + ++// Commands for cluster: UnifySwitchAll ++ + // Commands for cluster: UnifyHumidityControl + #define DOTDOT_UNIFY_HUMIDITY_CONTROL_MODE_SET_COMMAND_ID (0x1) + #define DOTDOT_UNIFY_HUMIDITY_CONTROL_SETPOINT_SET_COMMAND_ID (0x2) +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..c454184dc4 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 +@@ -262,6 +262,10 @@ + #define DOTDOT_UNIFY_THERMOSTAT_CLUSTER_ID ((dotdot_cluster_id_t)0xFD15) + + ++// Definitions for cluster: UnifySwitchAll ++#define DOTDOT_UNIFY_SWITCH_ALL_CLUSTER_ID ((dotdot_cluster_id_t)0xFD20) ++ ++ + // Definitions for cluster: UnifyHumidityControl + #define DOTDOT_UNIFY_HUMIDITY_CONTROL_CLUSTER_ID ((dotdot_cluster_id_t)0xFDA0) + +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..b267f90e71 100644 +--- a/components/uic_dotdot/zap-generated/readme_ucl_mqtt_reference.md ++++ b/components/uic_dotdot/zap-generated/readme_ucl_mqtt_reference.md +@@ -53259,6 +53259,321 @@ mosquitto_pub -t 'ucl/by-unid///UnifyThermostat/Commands/ForceReadAttr +


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

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

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

++ ++ ++\subsection unify_switch_all_attr_cluster_revision UnifySwitchAll/ClusterRevision Attribute ++ ++**MQTT Topic Pattern:** ++ ++``` ++[PREFIX]/UnifySwitchAll/Attributes/ClusterRevision/Reported ++[PREFIX]/UnifySwitchAll/Attributes/ClusterRevision/Desired ++``` ++ ++**MQTT Payload JSON Schema:** ++ ++```json ++{ ++ "$schema": "http://json-schema.org/draft-07/schema#", ++ "title": "UnifySwitchAll 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///UnifySwitchAll/Attributes/ClusterRevision/+' ++# Example output ++ucl/by-unid///UnifySwitchAll/Attributes/ClusterRevision/Desired { "value": } ++ucl/by-unid///UnifySwitchAll/Attributes/ClusterRevision/Reported { "value": } ++``` ++ ++ ++ ++ ++ ++

++ ++ ++ ++ ++\section unify_switch_all_recv_cmd_support UnifySwitchAll Command Support ++ ++**MQTT Topic Pattern:** ++ ++``` ++[PREFIX]/UnifySwitchAll/SupportedCommands ++[PREFIX]/UnifySwitchAll/SupportedGeneratedCommands ++``` ++ ++**MQTT Payload JSON Schema:** ++ ++```json ++{ ++ "$schema": "http://json-schema.org/draft-07/schema#", ++ "title": "UnifySwitchAll 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 UnifySwitchAll cluster under the by-unid topic space: ++ ++```console ++mosquitto_sub -t 'ucl/by-unid///UnifySwitchAll/SupportedCommands' ++# Example output ++ucl/by-unid///UnifySwitchAll/SupportedCommands { "value": [] } ++``` ++ ++To see supported generated commands for UnifySwitchAll cluster under the by-unid topic space: ++ ++```console ++mosquitto_sub -t 'ucl/by-unid///UnifySwitchAll/SupportedGeneratedCommands' ++# Example output ++ucl/by-unid///UnifySwitchAll/SupportedGeneratedCommands { "value": [] } ++``` ++ ++ ++ ++ ++ ++

++ ++ ++ ++ ++\section unify_switch_all_cmds UnifySwitchAll Commands ++ ++

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

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


++ ++ + + + +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..4311450cb0 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 +@@ -2055,6 +2055,19 @@ const char *uic_dotdot_get_attribute_name(dotdot_cluster_id_t cluster_id, + return "Unknown"; + } + // clang-format off ++ case DOTDOT_UNIFY_SWITCH_ALL_CLUSTER_ID: ++ // clang-format on ++ switch (attribute_id) { ++ // clang-format off ++ case DOTDOT_UNIFY_SWITCH_ALL_MODE_ATTRIBUTE_ID: ++ return "Mode"; ++ case DOTDOT_UNIFY_SWITCH_ALL_ON_OFF_ATTRIBUTE_ID: ++ return "OnOff"; ++ // clang-format on ++ default: ++ return "Unknown"; ++ } ++ // clang-format off + case DOTDOT_UNIFY_HUMIDITY_CONTROL_CLUSTER_ID: + // clang-format on + switch (attribute_id) { +@@ -4508,6 +4521,14 @@ dotdot_attribute_id_t + return DOTDOT_UNIFY_THERMOSTAT_OPERATING_STATE_ATTRIBUTE_ID; + } + break; ++ case DOTDOT_UNIFY_SWITCH_ALL_CLUSTER_ID: ++ if (strcmp ("Mode", attribute_name) == 0) { ++ return DOTDOT_UNIFY_SWITCH_ALL_MODE_ATTRIBUTE_ID; ++ } ++ if (strcmp ("OnOff", attribute_name) == 0) { ++ return DOTDOT_UNIFY_SWITCH_ALL_ON_OFF_ATTRIBUTE_ID; ++ } ++ break; + case DOTDOT_UNIFY_HUMIDITY_CONTROL_CLUSTER_ID: + if (strcmp ("ReportingMode", attribute_name) == 0) { + return DOTDOT_UNIFY_HUMIDITY_CONTROL_REPORTING_MODE_ATTRIBUTE_ID; +@@ -6614,6 +6635,19 @@ dotdot_attribute_json_type_t + return JSON_TYPE_UNKNOWN; + } + // clang-format off ++ case DOTDOT_UNIFY_SWITCH_ALL_CLUSTER_ID: ++ // clang-format on ++ switch (attribute_id) { ++ // clang-format off ++ case DOTDOT_UNIFY_SWITCH_ALL_MODE_ATTRIBUTE_ID: ++ return JSON_TYPE_NUMBER; ++ case DOTDOT_UNIFY_SWITCH_ALL_ON_OFF_ATTRIBUTE_ID: ++ return JSON_TYPE_NUMBER; ++ // clang-format on ++ default: ++ return JSON_TYPE_UNKNOWN; ++ } ++ // clang-format off + case DOTDOT_UNIFY_HUMIDITY_CONTROL_CLUSTER_ID: + // clang-format on + switch (attribute_id) { +@@ -7006,6 +7040,9 @@ bool uic_dotdot_attribute_is_enum(dotdot_cluster_id_t cluster_id, + } + } + ++ if (64800 == cluster_id) { ++ } ++ + if (64928 == cluster_id) { + if (1 == attribute_id) { + return true; +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..809c9fc1bf 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 +@@ -132,6 +132,8 @@ const char* uic_dotdot_get_cluster_name(dotdot_cluster_id_t cluster_id) { + return "UnifyFanControl"; + case DOTDOT_UNIFY_THERMOSTAT_CLUSTER_ID: + return "UnifyThermostat"; ++ case DOTDOT_UNIFY_SWITCH_ALL_CLUSTER_ID: ++ return "UnifySwitchAll"; + case DOTDOT_UNIFY_HUMIDITY_CONTROL_CLUSTER_ID: + return "UnifyHumidityControl"; + default: +@@ -311,6 +313,9 @@ dotdot_cluster_id_t uic_dotdot_get_cluster_id(const char* cluster_name) { + if (strcmp ("UnifyThermostat", cluster_name) == 0) { + return DOTDOT_UNIFY_THERMOSTAT_CLUSTER_ID; + } ++ if (strcmp ("UnifySwitchAll", cluster_name) == 0) { ++ return DOTDOT_UNIFY_SWITCH_ALL_CLUSTER_ID; ++ } + if (strcmp ("UnifyHumidityControl", cluster_name) == 0) { + return DOTDOT_UNIFY_HUMIDITY_CONTROL_CLUSTER_ID; + } +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 3d01d600fb..713bbc599e 100644 +--- a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.hpp ++++ b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.hpp +@@ -5050,14 +5050,14 @@ void uic_mqtt_dotdot_on_unify_fan_control_WriteAttributes( + * + * @returns std::set of callbacks. + */ +-std::set & get_uic_mqtt_dotdot_unify_switch_all_write_attributes_callback(); ++std::set & get_uic_mqtt_dotdot_unify_thermostat_write_attributes_callback(); + + /** + * @brief MQTT Subscribe handler for incoming publications on: +- * ucl/by-unid/+/+/UnifySwitchAll/Commands/WriteAttributes ++ * ucl/by-unid/+/+/UnifyThermostat/Commands/WriteAttributes + */ + // clang-format off +-void uic_mqtt_dotdot_on_unify_switch_all_WriteAttributes( ++void uic_mqtt_dotdot_on_unify_thermostat_WriteAttributes( + const char *topic, + const char *message, + const size_t message_length); +@@ -5071,14 +5071,14 @@ void uic_mqtt_dotdot_on_unify_switch_all_WriteAttributes( + * + * @returns std::set of callbacks. + */ +-std::set & get_uic_mqtt_dotdot_unify_thermostat_write_attributes_callback(); ++std::set & get_uic_mqtt_dotdot_unify_switch_all_write_attributes_callback(); + + /** + * @brief MQTT Subscribe handler for incoming publications on: +- * ucl/by-unid/+/+/UnifyThermostat/Commands/WriteAttributes ++ * ucl/by-unid/+/+/UnifySwitchAll/Commands/WriteAttributes + */ + // clang-format off +-void uic_mqtt_dotdot_on_unify_thermostat_WriteAttributes( ++void uic_mqtt_dotdot_on_unify_switch_all_WriteAttributes( + const char *topic, + const char *message, + const size_t message_length); +-- +2.39.5 +