diff --git a/applications/zpc/components/dotdot_mapper/rules/FanControl_to_ThermostatFanModeAndStateCC.uam b/applications/zpc/components/dotdot_mapper/rules/FanControl_to_ThermostatFanModeAndStateCC.uam new file mode 100644 index 0000000000..599b11e532 --- /dev/null +++ b/applications/zpc/components/dotdot_mapper/rules/FanControl_to_ThermostatFanModeAndStateCC.uam @@ -0,0 +1,94 @@ +def zwTHERMOSTAT_FAN_MODE_CURRENT_FAN_MODE 0x4402 +def zwTHERMOSTAT_FAN_MODE_SUPPORTED_FAN_MODE 0x4403 + +def zwTHERMOSTAT_FAN_STATE 0x4502 +def zwTHERMOSTAT_FAN_OFF_FLAG 0x4504 + +def zbZWAVE_FAN_MODE 0xFD140001 +def zbZWAVE_SUPPORTED_FAN_MODE 0xFD140002 +def zbZWAVE_FAN_STATE 0xFD140003 + +def zbFAN_MODE 0x02020000 + +def zwave_no_thermostat_fan_mode (e'zwTHERMOSTAT_FAN_MODE_CURRENT_FAN_MODE == 0) + +// Unify Fan mode (ZWave) <-> FanMode cluster +scope 20 chain_reaction(0) { + r'zbZWAVE_FAN_MODE = + if (zwave_no_thermostat_fan_mode) undefined + if (r'zbFAN_MODE == 5) 0 + if (r'zbFAN_MODE == 1) 1 + if (r'zbFAN_MODE == 2) 5 + undefined + d'zbZWAVE_FAN_MODE = + if (zwave_no_thermostat_fan_mode) undefined + if (d'zbFAN_MODE == 5) 0 + if (d'zbFAN_MODE == 1) 1 + if (d'zbFAN_MODE == 2) 5 + undefined + + r'zwTHERMOSTAT_FAN_OFF_FLAG = + if (zwave_no_thermostat_fan_mode) undefined + if (r'zbFAN_MODE == 0) 1 + 0 + d'zwTHERMOSTAT_FAN_OFF_FLAG = + if (zwave_no_thermostat_fan_mode) undefined + if (d'zbFAN_MODE == 0) 1 + 0 + + // Linking attributes zigbee -> zwave + r'zbFAN_MODE = + if (zwave_no_thermostat_fan_mode) undefined + if (r'zbZWAVE_FAN_MODE == 0) 5 + if (r'zbZWAVE_FAN_MODE == 1) 1 + if (r'zbZWAVE_FAN_MODE == 2) 3 + if (r'zbZWAVE_FAN_MODE == 3) 3 + if (r'zbZWAVE_FAN_MODE == 4) 2 + if (r'zbZWAVE_FAN_MODE == 5) 2 + if (r'zwTHERMOSTAT_FAN_OFF_FLAG == 1) 4 + if (r'zwTHERMOSTAT_FAN_OFF_FLAG == 0) 0 + undefined + + // Linking attributes zigbee -> zwave + d'zbFAN_MODE = + if (zwave_no_thermostat_fan_mode) undefined + if (d'zbZWAVE_FAN_MODE == 0) 5 + if (d'zbZWAVE_FAN_MODE == 1) 1 + if (d'zbZWAVE_FAN_MODE == 2) 3 + if (d'zbZWAVE_FAN_MODE == 3) 3 + if (d'zbZWAVE_FAN_MODE == 4) 2 + if (d'zbZWAVE_FAN_MODE == 5) 2 + if (d'zwTHERMOSTAT_FAN_OFF_FLAG == 1) 4 + if (d'zwTHERMOSTAT_FAN_OFF_FLAG == 0) 0 + undefined +} + +// Unify Fan mode (ZWave) <-> Attribute Store +scope 25 chain_reaction(0) { + // Linking attributes zwave -> zigbee + r'zbZWAVE_FAN_MODE = + if (zwave_no_thermostat_fan_mode) undefined + r'zwTHERMOSTAT_FAN_MODE_CURRENT_FAN_MODE + d'zbZWAVE_FAN_MODE = + if (zwave_no_thermostat_fan_mode) undefined + d'zwTHERMOSTAT_FAN_MODE_CURRENT_FAN_MODE + + + // Linking attributes zigbee -> zwave + r'zwTHERMOSTAT_FAN_MODE_CURRENT_FAN_MODE = + if (zwave_no_thermostat_fan_mode) undefined + r'zbZWAVE_FAN_MODE + d'zwTHERMOSTAT_FAN_MODE_CURRENT_FAN_MODE = + if (zwave_no_thermostat_fan_mode) undefined + d'zbZWAVE_FAN_MODE + + // Supported fan mode (read only) + r'zbZWAVE_SUPPORTED_FAN_MODE = + if (zwave_no_thermostat_fan_mode) undefined + r'zwTHERMOSTAT_FAN_MODE_SUPPORTED_FAN_MODE + + // Supported state(read only) + r'zbZWAVE_FAN_STATE = + if (zwave_no_thermostat_fan_mode) undefined + r'zwTHERMOSTAT_FAN_STATE +} \ No newline at end of file diff --git a/applications/zpc/components/zcl_cluster_servers/CMakeLists.txt b/applications/zpc/components/zcl_cluster_servers/CMakeLists.txt index 663f04f7fc..df7b3c47b4 100644 --- a/applications/zpc/components/zcl_cluster_servers/CMakeLists.txt +++ b/applications/zpc/components/zcl_cluster_servers/CMakeLists.txt @@ -9,6 +9,7 @@ add_library( zcl_cluster_servers src/configuration_parameter_cluster_server.cpp src/user_code_cluster_server.cpp + src/fan_control_cluster_server.c src/zcl_binding_cluster_server.cpp src/zcl_cluster_servers.cpp src/zcl_cluster_servers_helpers.cpp diff --git a/applications/zpc/components/zcl_cluster_servers/src/fan_control_cluster_server.c b/applications/zpc/components/zcl_cluster_servers/src/fan_control_cluster_server.c new file mode 100644 index 0000000000..6f44e62130 --- /dev/null +++ b/applications/zpc/components/zcl_cluster_servers/src/fan_control_cluster_server.c @@ -0,0 +1,74 @@ + +/****************************************************************************** + * # License + * Copyright 2021 Silicon Laboratories Inc. www.silabs.com + ****************************************************************************** + * The licensor of this software is Silicon Laboratories Inc. Your use of this + * software is governed by the terms of Silicon Labs Master Software License + * Agreement (MSLA) available at + * www.silabs.com/about-us/legal/master-software-license-agreement. This + * software is distributed to you in Source Code format and is governed by the + * sections of the MSLA applicable to Source Code. + * + *****************************************************************************/ +// Includes from this component +#include "fan_control_cluster_server.h" + +// Includes from Unify +#include "sl_log.h" +#include "sl_status.h" +#include "attribute_store_helper.h" +#include "zpc_attribute_store_network_helper.h" +#include "zwave_command_class_thermostat_fan_types.h" + +#include "attribute_store_defined_attribute_types.h" +#include "unify_dotdot_defined_attribute_types.h" +#include "unify_dotdot_attribute_store.h" +#include "unify_dotdot_attribute_store_node_state.h" + +// Includes from auto-generated files +#include "dotdot_mqtt.h" + +// Setup Log ID +#define LOG_TAG "fan_control_cluster_server" + +sl_status_t + zwave_fan_control_turn_off(dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint, + uic_mqtt_dotdot_callback_call_type_t call_type) +{ + attribute_store_node_t endpoint_node + = attribute_store_network_helper_get_endpoint_node(unid, endpoint); + + attribute_store_node_t off_flag_node + = attribute_store_get_first_child_by_type( + endpoint_node, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_OFF_FLAG); + + // First check the call type. If this is a support check support call, + // we check the attributes + if (call_type == UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK) { + // Check user option automatic_deduction_of_supported_commands + return attribute_store_node_exists(off_flag_node) ? SL_STATUS_OK + : SL_STATUS_FAIL; + } + + thermostat_fan_mode_off_flag_t off_flag = 1; + return attribute_store_set_desired(off_flag_node, + &off_flag, + sizeof(off_flag)); +} + +/////////////////////////////////////////////////////////////////////////////// +// Init and teardown functions +/////////////////////////////////////////////////////////////////////////////// +sl_status_t fan_control_cluster_server_init() +{ + sl_log_debug(LOG_TAG, "FanControl cluster (ZWave) server initialization"); + + // Listen to the BASIC Value attribute is created + uic_mqtt_dotdot_unify_fan_control_turn_off_callback_set( + &zwave_fan_control_turn_off); + + return SL_STATUS_OK; +} diff --git a/applications/zpc/components/zcl_cluster_servers/src/fan_control_cluster_server.h b/applications/zpc/components/zcl_cluster_servers/src/fan_control_cluster_server.h new file mode 100644 index 0000000000..e14c364919 --- /dev/null +++ b/applications/zpc/components/zcl_cluster_servers/src/fan_control_cluster_server.h @@ -0,0 +1,46 @@ +/****************************************************************************** + * # 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 zpc_on_off_cluster_mapper ZPC Fan Control + * @ingroup dotdot_mapper + * @brief Maps OnOff Cluster incoming Commands to attribute modifications. + * + * @{ + */ + +#ifndef FAN_CONTROL_CLUSTER_SERVER_H +#define FAN_CONTROL_CLUSTER_SERVER_H + +// Generic includes +#include "sl_status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize the FanControl cluster server + * + * @returns true on success + * @returns false on failure + * + */ +sl_status_t fan_control_cluster_server_init(void); + +#ifdef __cplusplus +} +#endif + +#endif //FAN_CONTROL_CLUSTER_SERVER_H +/** @} end fan_control_cluster_server */ diff --git a/applications/zpc/components/zcl_cluster_servers/src/zcl_cluster_servers.cpp b/applications/zpc/components/zcl_cluster_servers/src/zcl_cluster_servers.cpp index 03167980cf..7c959d555b 100644 --- a/applications/zpc/components/zcl_cluster_servers/src/zcl_cluster_servers.cpp +++ b/applications/zpc/components/zcl_cluster_servers/src/zcl_cluster_servers.cpp @@ -18,6 +18,7 @@ #include "zcl_scenes_cluster_server.h" #include "zcl_OTA_cluster_server.hpp" #include "user_code_cluster_server.h" +#include "fan_control_cluster_server.h" //Includes from other components #include "attribute_store.h" @@ -40,6 +41,8 @@ sl_status_t zcl_cluster_servers_init() init_status |= binding_cluster_server_init(); init_status |= zcl_scenes_cluster_server_init(); init_status |= user_code_cluster_server_init(); + init_status |= fan_control_cluster_server_init(); + return init_status; } diff --git a/applications/zpc/components/zcl_cluster_servers/test/CMakeLists.txt b/applications/zpc/components/zcl_cluster_servers/test/CMakeLists.txt index ff9d893c81..6f118f82ec 100644 --- a/applications/zpc/components/zcl_cluster_servers/test/CMakeLists.txt +++ b/applications/zpc/components/zcl_cluster_servers/test/CMakeLists.txt @@ -36,6 +36,18 @@ target_include_directories(zcl_OTA_cluster_server_test PRIVATE ../src) # zwave_api_mock # zwave_api_transport_mock) +# FanControl Cluster Mapper test +target_add_unittest( + zcl_cluster_servers + NAME + fan_control_cluster_server_test + SOURCES + fan_control_cluster_server_test.c + DEPENDS + zpc_attribute_store_test_helper + uic_dotdot_mqtt_mock + unify_dotdot_attribute_store) + # Configuration Parameter Cluster Server test target_add_unittest( zcl_cluster_servers diff --git a/applications/zpc/components/zcl_cluster_servers/test/fan_control_cluster_server_test.c b/applications/zpc/components/zcl_cluster_servers/test/fan_control_cluster_server_test.c new file mode 100644 index 0000000000..12ded94df2 --- /dev/null +++ b/applications/zpc/components/zcl_cluster_servers/test/fan_control_cluster_server_test.c @@ -0,0 +1,110 @@ +/****************************************************************************** + * # License + * Copyright 2022 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. + * + *****************************************************************************/ +#include "fan_control_cluster_server.h" +#include "unify_dotdot_attribute_store.h" +#include "unity.h" + +// Unify components +#include "datastore.h" +#include "attribute_store_fixt.h" +#include "attribute_store_helper.h" +#include "unify_dotdot_defined_attribute_types.h" +#include "dotdot_mqtt_mock.h" + +// ZPC Components +#include "zwave_unid.h" +#include "zwave_command_class_thermostat_fan_types.h" + +// Test helpers +#include "zpc_attribute_store_test_helper.h" +#include "attribute_store_defined_attribute_types.h" + +uic_mqtt_dotdot_unify_fan_control_turn_off_callback_t + uic_mqtt_dotdot_fan_control_turn_off_callback; + +void uic_mqtt_dotdot_fan_control_turn_off_callback_set_stub( + const uic_mqtt_dotdot_unify_fan_control_turn_off_callback_t callback, + int cmock_num_calls) +{ + uic_mqtt_dotdot_fan_control_turn_off_callback = callback; +} + +/// Setup the test suite (called once before all test_xxx functions are called) +void suiteSetUp() +{ + datastore_init(":memory:"); + attribute_store_init(); +} + +/// 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; +} + +/// Called before each and every test +void setUp() +{ + zpc_attribute_store_test_helper_create_network(); + uic_mqtt_dotdot_unify_fan_control_turn_off_callback_set_Stub( + &uic_mqtt_dotdot_fan_control_turn_off_callback_set_stub); + + // Call init + TEST_ASSERT_EQUAL(SL_STATUS_OK, fan_control_cluster_server_init()); +} + +/// Called after each and every test +void tearDown() +{ + attribute_store_delete_node(attribute_store_get_root()); +} + +void test_fan_control_command_mapping() +{ + TEST_ASSERT_NOT_NULL(uic_mqtt_dotdot_fan_control_turn_off_callback); + + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, + uic_mqtt_dotdot_fan_control_turn_off_callback( + supporting_node_unid, + endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK)); + + attribute_store_node_t off_node = attribute_store_add_node( + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_OFF_FLAG, + endpoint_id_node); + + // test support + TEST_ASSERT_EQUAL(SL_STATUS_OK, + uic_mqtt_dotdot_fan_control_turn_off_callback( + supporting_node_unid, + endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK)); + // Test callback + TEST_ASSERT_EQUAL(SL_STATUS_OK, + uic_mqtt_dotdot_fan_control_turn_off_callback( + supporting_node_unid, + endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL)); + + thermostat_fan_mode_off_flag_t off_flag = 0; + + TEST_ASSERT_EQUAL_MESSAGE( + SL_STATUS_OK, + attribute_store_get_desired(off_node, &off_flag, sizeof(off_flag)), + "Can't get Off flag value"); + + // Test value + TEST_ASSERT_EQUAL(1, off_flag); +} 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 78ca2909e5..830a44ee7b 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 @@ -691,6 +691,31 @@ DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_SETPOINT_MAX_VALUE, DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_SETPOINT_MAX_VALUE_SCALE, ((COMMAND_CLASS_THERMOSTAT_SETPOINT << 8) | 0x09)) +///////////////////////////////////////////////// +// Thermostat Fan Mode Command Class +/// zwave_cc_version_t +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_VERSION, + ZWAVE_CC_VERSION_ATTRIBUTE(COMMAND_CLASS_THERMOSTAT_FAN_MODE)) +/// Current Fan mode +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_CURRENT_MODE, + ((COMMAND_CLASS_THERMOSTAT_FAN_MODE << 8) | 0x02)) +/// Supported Fans modes +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_SUPPORTED_MODES, + ((COMMAND_CLASS_THERMOSTAT_FAN_MODE << 8) | 0x03)) +/// Off flag (v2+) +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_OFF_FLAG, + ((COMMAND_CLASS_THERMOSTAT_FAN_MODE << 8) | 0x05)) + +///////////////////////////////////////////////// +// Thermostat Fan State Command Class +/// zwave_cc_version_t +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_STATE_VERSION, + ZWAVE_CC_VERSION_ATTRIBUTE(COMMAND_CLASS_THERMOSTAT_FAN_STATE)) +/// Current Fan operating state +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_STATE_FAN_OPERATING_STATE, + ((COMMAND_CLASS_THERMOSTAT_FAN_STATE << 8) | 0x02)) + + ///////////////////////////////////////////////// // Wakeup command class DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_WAKE_UP_VERSION, diff --git a/applications/zpc/components/zpc_attribute_store/include/command_class_types/zwave_command_class_thermostat_fan_types.h b/applications/zpc/components/zpc_attribute_store/include/command_class_types/zwave_command_class_thermostat_fan_types.h new file mode 100644 index 0000000000..091babfa9d --- /dev/null +++ b/applications/zpc/components/zpc_attribute_store/include/command_class_types/zwave_command_class_thermostat_fan_types.h @@ -0,0 +1,56 @@ +/****************************************************************************** + * # License + * Copyright 2023 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 zpc_attribute_store_command_classes_types Type definitions for attribute storage of Command Classes + * @ingroup zpc_attribute_store + * @brief Type definitions for Command Classes, used for @ref attribute_store storage. + * + */ + +/** + * @defgroup zwave_command_class_thermostat_fan_mode_types Type definitions for attribute storage of the Sound Switch Command Class + * @ingroup zpc_attribute_store_command_classes_types + * @brief Type definitions for the Sound Switch Command Class. + * + * @{ + */ + +#ifndef ZWAVE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_TYPES_H +#define ZWAVE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_TYPES_H + +#include + + +// Fan Mode CC +///> Off flag (version >= 2). uint8_t +typedef uint8_t thermostat_fan_mode_off_flag_t; +///> Fan mode. uint8_t +typedef uint8_t thermostat_fan_mode_t; +///> Supported mode bitmask. uint32_t +typedef uint32_t thermostat_fan_supported_modes_t; + +// Fan State CC +///> Fan state. uint8_t +typedef uint8_t thermostat_fan_state_t; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif //ZWAVE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_TYPES_H +/** @} end zwave_command_class_thermostat_fan_mode_types */ diff --git a/applications/zpc/components/zpc_attribute_store/src/zpc_attribute_store_type_registration.cpp b/applications/zpc/components/zpc_attribute_store/src/zpc_attribute_store_type_registration.cpp index 774ba2882d..c041ac8724 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 @@ -302,6 +302,23 @@ static const std::vector attribute_schema = { {ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_SETPOINT_MIN_VALUE_SCALE, "Min Value Scale", ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_SETPOINT_TYPE, U32_STORAGE_TYPE}, {ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_SETPOINT_MAX_VALUE, "Max Value", ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_SETPOINT_TYPE, I32_STORAGE_TYPE}, {ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_SETPOINT_MAX_VALUE_SCALE, "Max Value Scale", ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_SETPOINT_TYPE, U32_STORAGE_TYPE}, + + + ///////////////////////////////////////////////////////////////////// + // Thermostat Fan Mode Command Class attributes + ///////////////////////////////////////////////////////////////////// + {ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_VERSION, "Thermostat Fan Mode Version", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE}, + {ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_CURRENT_MODE, "Thermostat Current Fan Mode", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE}, + // We use u32 since it store some 8bit mask. This should be resilient to future version of Thermostat Fan Mode + {ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_SUPPORTED_MODES, "Thermostat Supported Fan Mode Bitmask", ATTRIBUTE_ENDPOINT_ID, U32_STORAGE_TYPE}, + {ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_OFF_FLAG, "Thermostat Fan Off Flag", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE }, + + ///////////////////////////////////////////////////////////////////// + // Thermostat Fan State Command Class attributes + ///////////////////////////////////////////////////////////////////// + {ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_STATE_VERSION, "Thermostat Fan State Version", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE}, + {ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_STATE_FAN_OPERATING_STATE, "Thermostat Fan Operating State", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE}, + ///////////////////////////////////////////////////////////////////// // Supervision Command Class attributes ///////////////////////////////////////////////////////////////////// diff --git a/applications/zpc/components/zwave_command_classes/CMakeLists.txt b/applications/zpc/components/zwave_command_classes/CMakeLists.txt index 90bec52fee..e20924a8e8 100644 --- a/applications/zpc/components/zwave_command_classes/CMakeLists.txt +++ b/applications/zpc/components/zwave_command_classes/CMakeLists.txt @@ -45,6 +45,8 @@ add_library( src/zwave_command_class_switch_color.c src/zwave_command_class_switch_multilevel.c src/zwave_command_class_thermostat_mode.c + src/zwave_command_class_thermostat_fan_mode.c + src/zwave_command_class_thermostat_fan_state.c src/zwave_command_class_thermostat_setpoint.c src/zwave_command_class_time.c src/zwave_command_class_user_code.c diff --git a/applications/zpc/components/zwave_command_classes/src/zwave_command_class_thermostat_fan_mode.c b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_thermostat_fan_mode.c new file mode 100644 index 0000000000..c62ce554ee --- /dev/null +++ b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_thermostat_fan_mode.c @@ -0,0 +1,448 @@ +// System +#include + +#include "zwave_command_class_thermostat_fan_mode.h" +#include "zwave_command_class_thermostat_fan_types.h" +#include "zwave_command_classes_utils.h" +#include "ZW_classcmd.h" + +// Includes from other ZPC Components +#include "zwave_command_class_indices.h" +#include "zwave_command_handler.h" +#include "zwave_command_class_version_types.h" +#include "zpc_attribute_store_network_helper.h" +#include "attribute_store_defined_attribute_types.h" + +// Unify +#include "attribute_resolver.h" +#include "attribute_store.h" +#include "attribute_store_helper.h" +#include "sl_log.h" + +#define LOG_TAG "zwave_command_class_thermostat_fan_mode" + +#define MAX_SUPPORTED_FAN_MODE_TYPES 24 + +///////////////////////////////////////////////////////////////////////////// +// Utils +///////////////////////////////////////////////////////////////////////////// +/** + * @brief Get current thermostat fan version of the CC + * + * @param node Endpoint node + * + * @returns zwave_cc_version_t 0 if anything goes wrong, version number otherwise + */ +static zwave_cc_version_t + get_thermostat_fan_version(attribute_store_node_t endpoint_node) +{ + attribute_store_node_t version_node = attribute_store_get_first_child_by_type( + endpoint_node, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_VERSION); + // We need to check the version of the supporting node: + zwave_cc_version_t supporting_node_version = 0; + attribute_store_get_reported(version_node, + &supporting_node_version, + sizeof(supporting_node_version)); + + return supporting_node_version; +} + +// Return true if supported_fan_mode_type is compatible with current_version +// False otherwise +static bool is_fan_mode_compatible_with_version( + thermostat_fan_mode_t supported_fan_mode_type, + zwave_cc_version_t current_version) +{ + bool compatibility = false; + + switch (current_version) { + case 1: + compatibility = (supported_fan_mode_type <= 0x03); + break; + case 2: + compatibility = (supported_fan_mode_type <= 0x05); + break; + case 3: + compatibility = (supported_fan_mode_type <= 0x07); + break; + case 4: + compatibility = (supported_fan_mode_type <= 0x0A); + break; + case 5: + compatibility = (supported_fan_mode_type <= 0x0B); + break; + default: + compatibility = false; + } + + if (!compatibility) { + sl_log_warning( + LOG_TAG, + "Fan mode %#04x is not compatible with Thermostat Fan Mode Version %d", + supported_fan_mode_type, + current_version); + } + + return compatibility; +} + +static bool + is_fan_control_mode_supported(thermostat_fan_mode_t fan_control_mode_type, + attribute_store_node_t endpoint_id_node) +{ + // First check version + zwave_cc_version_t current_version + = get_thermostat_fan_version(endpoint_id_node); + + // Check version compatibility + if (!is_fan_mode_compatible_with_version(fan_control_mode_type, + current_version)) { + return false; + } + + attribute_store_node_t supported_type_node + = attribute_store_get_first_child_by_type( + endpoint_id_node, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_SUPPORTED_MODES); + + if (supported_type_node == ATTRIBUTE_STORE_INVALID_NODE) { + sl_log_warning( + LOG_TAG, + "Can't get ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_SUPPORTED_MODES " + "from attribute store."); + return false; + } + + thermostat_fan_supported_modes_t supported_types; + sl_status_t status = attribute_store_get_reported(supported_type_node, + &supported_types, + sizeof(supported_types)); + + if (status != ATTRIBUTE_STORE_INVALID_NODE) { + sl_log_warning( + LOG_TAG, + "Can't get reported value of " + "ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_SUPPORTED_MODES."); + return false; + } + + switch (fan_control_mode_type) { + case THERMOSTAT_FAN_MODE_REPORT_FAN_MODE_AUTO_LOW: + return supported_types & 0x01; + case THERMOSTAT_FAN_MODE_REPORT_FAN_MODE_LOW: + return supported_types & 0x02; + case THERMOSTAT_FAN_MODE_REPORT_FAN_MODE_AUTO_HIGH: + return supported_types & 0x04; + case THERMOSTAT_FAN_MODE_REPORT_FAN_MODE_HIGH: + return supported_types & 0x08; + case THERMOSTAT_FAN_MODE_REPORT_FAN_MODE_AUTO_MEDIUM_V2: + return supported_types & 0x10; + case THERMOSTAT_FAN_MODE_REPORT_FAN_MODE_MEDIUM_V2: + return supported_types & 0x20; + case THERMOSTAT_FAN_MODE_REPORT_FAN_MODE_CIRCULATION_V3: + return supported_types & 0x40; + case THERMOSTAT_FAN_MODE_REPORT_FAN_MODE_HUMIDITY_V3: + return supported_types & 0x80; + case THERMOSTAT_FAN_MODE_REPORT_FAN_MODE_LEFT_RIGHT_V4: + return supported_types & 0x100; + case THERMOSTAT_FAN_MODE_REPORT_FAN_MODE_UP_DOWN_V4: + return supported_types & 0x200; + case THERMOSTAT_FAN_MODE_REPORT_FAN_MODE_QUIET_V4: + return supported_types & 0x400; + case THERMOSTAT_FAN_MODE_REPORT_FAN_MODE_EXTERNAL_CIRCULATION_V5: + return supported_types & 0x800; + default: + return false; + } +} + +///////////////////////////////////////////////////////////////////////////// +// Version & Attribute Creation +///////////////////////////////////////////////////////////////////////////// +static void zwave_command_class_thermostat_fan_mode_on_version_attribute_update( + attribute_store_node_t updated_node, attribute_store_change_t change) +{ + if (change == ATTRIBUTE_DELETED) { + return; + } + + if (is_zwave_command_class_filtered_for_root_device( + COMMAND_CLASS_THERMOSTAT_FAN_MODE, + updated_node) + == true) { + return; + } + + zwave_cc_version_t version = 0; + attribute_store_get_reported(updated_node, &version, sizeof(version)); + + if (version == 0) { + return; + } + + attribute_store_node_t endpoint_node + = attribute_store_get_first_parent_with_type(updated_node, + ATTRIBUTE_ENDPOINT_ID); + + // The order of the attribute matter since it defines the order of the + // Z-Wave get command order. + const attribute_store_type_t attributes[] + = {ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_SUPPORTED_MODES, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_CURRENT_MODE}; + + attribute_store_add_if_missing(endpoint_node, + attributes, + COUNT_OF(attributes)); + + // Add Off flag needed by version 2+ + if (version >= 2) { + const attribute_store_type_t v2_plus_attributes[] + = {ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_OFF_FLAG}; + attribute_store_add_if_missing(endpoint_node, + v2_plus_attributes, + COUNT_OF(v2_plus_attributes)); + } +} + +///////////////////////////////////////////////////////////////////////////// +// Thermostat Get/Set/Report +///////////////////////////////////////////////////////////////////////////// + +static sl_status_t zwave_command_class_thermostat_fan_mode_get( + attribute_store_node_t node, uint8_t *frame, uint16_t *frame_length) +{ + (void)node; // unused. + ZW_THERMOSTAT_FAN_MODE_GET_FRAME *get_frame + = (ZW_THERMOSTAT_FAN_MODE_GET_FRAME *)frame; + get_frame->cmdClass = COMMAND_CLASS_THERMOSTAT_FAN_MODE; + get_frame->cmd = THERMOSTAT_FAN_MODE_GET; + *frame_length = sizeof(ZW_THERMOSTAT_FAN_MODE_GET_FRAME); + return SL_STATUS_OK; +} + +static sl_status_t zwave_command_class_thermostat_fan_mode_set( + attribute_store_node_t node, uint8_t *frame, uint16_t *frame_length) +{ + attribute_store_node_t endpoint_node + = attribute_store_get_first_parent_with_type(node, ATTRIBUTE_ENDPOINT_ID); + + thermostat_fan_mode_t fan_mode = 0; + sl_status_t result + = attribute_store_get_desired_else_reported(node, + &fan_mode, + sizeof(fan_mode)); + + if (result != SL_STATUS_OK) { + return SL_STATUS_NOT_SUPPORTED; + } + + zwave_cc_version_t current_version + = get_thermostat_fan_version(endpoint_node); + + // Check version compatibility + if (!is_fan_control_mode_supported(fan_mode, endpoint_node)) { + sl_log_warning(LOG_TAG, "Unsupported fan mode %#04x. Clearing desired value.", fan_mode); + attribute_store_undefine_desired(node); + return SL_STATUS_NOT_SUPPORTED; + } + + ZW_THERMOSTAT_FAN_MODE_SET_FRAME *set_frame + = (ZW_THERMOSTAT_FAN_MODE_SET_FRAME *)frame; + set_frame->cmdClass = COMMAND_CLASS_THERMOSTAT_FAN_MODE; + set_frame->cmd = THERMOSTAT_FAN_MODE_SET; + *frame_length = sizeof(ZW_THERMOSTAT_FAN_MODE_SET_FRAME); + + // No Off bit in version 1 + if (current_version == 1) { + set_frame->level = fan_mode; + } else { + attribute_store_node_t off_node = attribute_store_get_node_child_by_type( + endpoint_node, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_OFF_FLAG, + 0); + + if (off_node == ATTRIBUTE_STORE_INVALID_NODE) { + return SL_STATUS_FAIL; + } + + thermostat_fan_mode_off_flag_t off_flag = 0; + result = attribute_store_get_desired_else_reported(off_node, + &off_flag, + sizeof(off_flag)); + + if (result != SL_STATUS_OK) { + return SL_STATUS_FAIL; + } + + if (off_flag > 1) { + off_flag = 1; + } + // Off Bit (1bit) Reserved (3 bits) Fan Mode (4 bits) + set_frame->level = fan_mode | (off_flag << 7); + } + + return SL_STATUS_OK; +} + +sl_status_t zwave_command_class_thermostat_fan_mode_handle_report( + const zwave_controller_connection_info_t *connection_info, + const uint8_t *frame_data, + uint16_t frame_length) +{ + if (frame_length < 3) { + return SL_STATUS_NOT_SUPPORTED; + } + + thermostat_fan_mode_t fan_mode + = frame_data[2] & THERMOSTAT_FAN_MODE_REPORT_PROPERTIES1_FAN_MODE_MASK_V3; + + attribute_store_node_t endpoint_node + = zwave_command_class_get_endpoint_node(connection_info); + + if (!is_fan_control_mode_supported(fan_mode, endpoint_node)) { + return SL_STATUS_NOT_SUPPORTED; + } + + zwave_cc_version_t current_version + = get_thermostat_fan_version(endpoint_node); + + if (current_version >= 3) { + // Extract fan mode from frame (first bit) + thermostat_fan_mode_off_flag_t off_flag + = (frame_data[2] & THERMOSTAT_FAN_MODE_REPORT_PROPERTIES1_OFF_BIT_MASK_V3) + >> 7; + attribute_store_set_child_reported( + endpoint_node, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_OFF_FLAG, + &off_flag, + sizeof(off_flag)); + } + + attribute_store_set_child_reported( + endpoint_node, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_CURRENT_MODE, + &fan_mode, + sizeof(fan_mode)); + + return SL_STATUS_OK; +} + +///////////////////////////////////////////////////////////////////////////// +// Thermostat Supported Get/Report +///////////////////////////////////////////////////////////////////////////// + +static sl_status_t zwave_command_class_thermostat_fan_mode_supported_get( + attribute_store_node_t node, uint8_t *frame, uint16_t *frame_length) +{ + (void)node; // unused. + ZW_THERMOSTAT_FAN_MODE_SUPPORTED_GET_FRAME *get_frame + = (ZW_THERMOSTAT_FAN_MODE_SUPPORTED_GET_FRAME *)frame; + get_frame->cmdClass = COMMAND_CLASS_THERMOSTAT_FAN_MODE; + get_frame->cmd = THERMOSTAT_FAN_MODE_SUPPORTED_GET; + *frame_length = sizeof(ZW_THERMOSTAT_FAN_MODE_SUPPORTED_GET_FRAME); + return SL_STATUS_OK; +} + +sl_status_t zwave_command_class_thermostat_fan_mode_supported_handle_report( + const zwave_controller_connection_info_t *connection_info, + const uint8_t *frame_data, + uint16_t frame_length) +{ + if (frame_length < 3) { + return SL_STATUS_NOT_SUPPORTED; + } + + thermostat_fan_supported_modes_t supported_modes_bitmask = 0x0000; + uint8_t bitmask_length = frame_length - 2; + + // Since we are using uint32_t we can't have more that 4 bit mask + if (bitmask_length > 4) { + sl_log_error(LOG_TAG, + "Supported Fan mode type Bit Mask length is not supported\n"); + return SL_STATUS_NOT_SUPPORTED; + } + + for (int i = bitmask_length; i > 0; i--) { + supported_modes_bitmask + = (supported_modes_bitmask << 8) | frame_data[1 + i]; + } + + attribute_store_node_t endpoint_node + = zwave_command_class_get_endpoint_node(connection_info); + + sl_status_t set_value_status = attribute_store_set_child_reported( + endpoint_node, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_SUPPORTED_MODES, + &supported_modes_bitmask, + sizeof(supported_modes_bitmask)); + + if (set_value_status != SL_STATUS_OK) { + sl_log_error(LOG_TAG, "Can't set ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_SUPPORTED_MODES"); + return SL_STATUS_NOT_SUPPORTED; + } + + return SL_STATUS_OK; +} + +sl_status_t zwave_command_class_thermostat_fan_mode_control_handler( + const zwave_controller_connection_info_t *connection_info, + const uint8_t *frame_data, + uint16_t frame_length) +{ + if (frame_length <= COMMAND_INDEX) { + return SL_STATUS_NOT_SUPPORTED; + } + + switch (frame_data[COMMAND_INDEX]) { + case THERMOSTAT_FAN_MODE_REPORT: + return zwave_command_class_thermostat_fan_mode_handle_report( + connection_info, + frame_data, + frame_length); + case THERMOSTAT_FAN_MODE_SUPPORTED_REPORT: + return zwave_command_class_thermostat_fan_mode_supported_handle_report( + connection_info, + frame_data, + frame_length); + default: + return SL_STATUS_NOT_SUPPORTED; + } + + return SL_STATUS_FAIL; +} + +sl_status_t zwave_command_class_thermostat_fan_mode_init() +{ + attribute_store_register_callback_by_type( + &zwave_command_class_thermostat_fan_mode_on_version_attribute_update, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_VERSION); + + attribute_resolver_register_rule( + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_CURRENT_MODE, + &zwave_command_class_thermostat_fan_mode_set, + &zwave_command_class_thermostat_fan_mode_get); + + attribute_resolver_register_rule( + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_OFF_FLAG, + &zwave_command_class_thermostat_fan_mode_set, + &zwave_command_class_thermostat_fan_mode_get); + + attribute_resolver_register_rule( + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_SUPPORTED_MODES, + NULL, + &zwave_command_class_thermostat_fan_mode_supported_get); + + zwave_command_handler_t handler = {}; + handler.support_handler = NULL; + handler.control_handler + = zwave_command_class_thermostat_fan_mode_control_handler; + handler.minimal_scheme = ZWAVE_CONTROLLER_ENCAPSULATION_NONE; + handler.manual_security_validation = false; + handler.command_class = COMMAND_CLASS_THERMOSTAT_FAN_MODE; + handler.version = 5; + handler.command_class_name = "Thermostat Fan Mode"; + handler.comments = "Experimental"; + + return zwave_command_handler_register_handler(handler); +} \ No newline at end of file diff --git a/applications/zpc/components/zwave_command_classes/src/zwave_command_class_thermostat_fan_mode.h b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_thermostat_fan_mode.h new file mode 100644 index 0000000000..5dd42edd51 --- /dev/null +++ b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_thermostat_fan_mode.h @@ -0,0 +1,45 @@ + +/****************************************************************************** + * # 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_thermostat_fan_mode + * @brief Sound Switch Command Class handlers and control function + * + * This module implement some of the functions to control the + * Sound Switch Command Class + * + * @{ + */ + +#ifndef ZWAVE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_H +#define ZWAVE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_H + +// Definition missing for now +#define THERMOSTAT_FAN_MODE_REPORT_FAN_MODE_EXTERNAL_CIRCULATION_V5 0x0B + + +#include "sl_status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +sl_status_t zwave_command_class_thermostat_fan_mode_init(); + +#ifdef __cplusplus +} +#endif + +#endif //ZWAVE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_H + /** @} end zwave_command_class_thermostat_fan_mode */ \ No newline at end of file diff --git a/applications/zpc/components/zwave_command_classes/src/zwave_command_class_thermostat_fan_state.c b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_thermostat_fan_state.c new file mode 100644 index 0000000000..f03ed1f1ca --- /dev/null +++ b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_thermostat_fan_state.c @@ -0,0 +1,161 @@ + +/****************************************************************************** + * # 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. + * + *****************************************************************************/ + +// System +#include + +#include "zwave_command_class_thermostat_fan_state.h" +#include "zwave_command_class_thermostat_fan_types.h" +#include "zwave_command_classes_utils.h" +#include "ZW_classcmd.h" + +// Includes from other ZPC Components +#include "zwave_command_class_indices.h" +#include "zwave_command_handler.h" +#include "zwave_command_class_version_types.h" +#include "zpc_attribute_store_network_helper.h" +#include "attribute_store_defined_attribute_types.h" + +// Unify +#include "attribute_resolver.h" +#include "attribute_store.h" +#include "attribute_store_helper.h" +#include "sl_log.h" + +#define LOG_TAG "zwave_command_class_thermostat_fan_state" + +///////////////////////////////////////////////////////////////////////////// +// Version & Attribute Creation +///////////////////////////////////////////////////////////////////////////// +static void zwave_command_class_thermostat_fan_state_on_version_attribute_update( + attribute_store_node_t updated_node, attribute_store_change_t change) +{ + if (change == ATTRIBUTE_DELETED) { + return; + } + + if (is_zwave_command_class_filtered_for_root_device( + COMMAND_CLASS_THERMOSTAT_FAN_STATE, + updated_node) + == true) { + return; + } + + zwave_cc_version_t version = 0; + attribute_store_get_reported(updated_node, &version, sizeof(version)); + + if (version == 0) { + return; + } + + attribute_store_node_t endpoint_node + = attribute_store_get_first_parent_with_type(updated_node, + ATTRIBUTE_ENDPOINT_ID); + + // The order of the attribute matter since it defines the order of the + // Z-Wave get command order. + const attribute_store_type_t attributes[] + = {ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_STATE_FAN_OPERATING_STATE}; + + attribute_store_add_if_missing(endpoint_node, + attributes, + COUNT_OF(attributes)); + +} + +///////////////////////////////////////////////////////////////////////////// +// Thermostat Fan State Get/Report +///////////////////////////////////////////////////////////////////////////// + +static sl_status_t zwave_command_class_thermostat_fan_state_get( + attribute_store_node_t node, uint8_t *frame, uint16_t *frame_length) +{ + (void)node; // unused. + ZW_THERMOSTAT_FAN_STATE_GET_FRAME *get_frame + = (ZW_THERMOSTAT_FAN_STATE_GET_FRAME *)frame; + get_frame->cmdClass = COMMAND_CLASS_THERMOSTAT_FAN_STATE; + get_frame->cmd = THERMOSTAT_FAN_STATE_GET; + *frame_length = sizeof(ZW_THERMOSTAT_FAN_STATE_GET_FRAME); + return SL_STATUS_OK; +} + +sl_status_t zwave_command_class_thermostat_fan_state_handle_report( + const zwave_controller_connection_info_t *connection_info, + const uint8_t *frame_data, + uint16_t frame_length) +{ + if (frame_length < 3) { + return SL_STATUS_FAIL; + } + + thermostat_fan_state_t fan_state = frame_data[2] & THERMOSTAT_FAN_STATE_REPORT_LEVEL_FAN_OPERATING_STATE_MASK; + + attribute_store_node_t endpoint_node + = zwave_command_class_get_endpoint_node(connection_info); + + attribute_store_set_child_reported( + endpoint_node, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_STATE_FAN_OPERATING_STATE, + &fan_state, + sizeof(fan_state)); + + return SL_STATUS_OK; +} + +sl_status_t zwave_command_class_thermostat_fan_state_control_handler( + const zwave_controller_connection_info_t *connection_info, + const uint8_t *frame_data, + uint16_t frame_length) +{ + if (frame_length <= COMMAND_INDEX) { + return SL_STATUS_NOT_SUPPORTED; + } + + switch (frame_data[COMMAND_INDEX]) { + case THERMOSTAT_FAN_STATE_REPORT: + return zwave_command_class_thermostat_fan_state_handle_report( + connection_info, + frame_data, + frame_length); + default: + return SL_STATUS_NOT_SUPPORTED; + } +} + + +sl_status_t zwave_command_class_thermostat_fan_state_init() +{ + attribute_store_register_callback_by_type( + &zwave_command_class_thermostat_fan_state_on_version_attribute_update, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_STATE_VERSION); + + attribute_resolver_register_rule( + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_STATE_FAN_OPERATING_STATE, + NULL, + &zwave_command_class_thermostat_fan_state_get); + + + zwave_command_handler_t handler = {}; + handler.support_handler = NULL; + handler.control_handler + = zwave_command_class_thermostat_fan_state_control_handler; + handler.minimal_scheme = ZWAVE_CONTROLLER_ENCAPSULATION_NONE; + handler.manual_security_validation = false; + handler.command_class = COMMAND_CLASS_THERMOSTAT_FAN_STATE; + handler.version = 2; + handler.command_class_name = "Thermostat Fan State"; + handler.comments = "Experimental"; + + return zwave_command_handler_register_handler(handler); +} \ No newline at end of file diff --git a/applications/zpc/components/zwave_command_classes/src/zwave_command_class_thermostat_fan_state.h b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_thermostat_fan_state.h new file mode 100644 index 0000000000..3e11fc9c1d --- /dev/null +++ b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_thermostat_fan_state.h @@ -0,0 +1,41 @@ + +/****************************************************************************** + * # 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_thermostat_fan_state + * @brief Sound Switch Command Class handlers and control function + * + * This module implement some of the functions to control the + * Sound Switch Command Class + * + * @{ + */ + +#ifndef ZWAVE_COMMAND_CLASS_THERMOSTAT_FAN_STATE_H +#define ZWAVE_COMMAND_CLASS_THERMOSTAT_FAN_STATE_H + +#include "sl_status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +sl_status_t zwave_command_class_thermostat_fan_state_init(); + +#ifdef __cplusplus +} +#endif + +#endif //ZWAVE_COMMAND_CLASS_THERMOSTAT_FAN_STATE_H + /** @} end zwave_command_class_thermostat_fan_state */ \ No newline at end of file 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 cac0a8c665..9cca6c774e 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 @@ -40,6 +40,8 @@ #include "zwave_command_class_switch_color.h" #include "zwave_command_class_switch_multilevel.h" #include "zwave_command_class_thermostat_mode.h" +#include "zwave_command_class_thermostat_fan_mode.h" +#include "zwave_command_class_thermostat_fan_state.h" #include "zwave_command_class_thermostat_setpoint.h" #include "zwave_command_class_version.h" #include "zwave_command_class_wake_up.h" @@ -108,6 +110,8 @@ sl_status_t zwave_command_classes_init() status |= zwave_command_class_switch_color_init(); status |= zwave_command_class_switch_multilevel_init(); status |= zwave_command_class_thermostat_mode_init(); + status |= zwave_command_class_thermostat_fan_mode_init(); + status |= zwave_command_class_thermostat_fan_state_init(); status |= zwave_command_class_thermostat_setpoint_init(); status |= zwave_command_class_time_init(); status |= zwave_command_class_transport_service_init(); diff --git a/applications/zpc/components/zwave_command_classes/test/CMakeLists.txt b/applications/zpc/components/zwave_command_classes/test/CMakeLists.txt index 4d72dfb0ef..8c40976f2b 100644 --- a/applications/zpc/components/zwave_command_classes/test/CMakeLists.txt +++ b/applications/zpc/components/zwave_command_classes/test/CMakeLists.txt @@ -637,6 +637,36 @@ DEPENDS uic_dotdot_mqtt_mock ) +# Thermostat fan mode test +target_add_unittest( + zwave_command_classes + NAME + zwave_command_class_thermostat_fan_mode_test + SOURCES + zwave_command_class_thermostat_fan_mode_test.c + DEPENDS + zpc_attribute_store_test_helper + zwave_controller + zwave_command_handler_mock + uic_attribute_resolver_mock + zpc_attribute_resolver_mock + uic_dotdot_mqtt_mock) + + # Thermostat fan state test +target_add_unittest( + zwave_command_classes + NAME + zwave_command_class_thermostat_fan_state_test + SOURCES + zwave_command_class_thermostat_fan_state_test.c + DEPENDS + zpc_attribute_store_test_helper + zwave_controller + zwave_command_handler_mock + uic_attribute_resolver_mock + zpc_attribute_resolver_mock + uic_dotdot_mqtt_mock) + # Tests for generated command classes set(GEN_TEST_INCLUDES "${CMAKE_SOURCE_DIR}/applications/zpc/components/zwave_controller/include" diff --git a/applications/zpc/components/zwave_command_classes/test/zwave_command_class_thermostat_fan_mode_test.c b/applications/zpc/components/zwave_command_classes/test/zwave_command_class_thermostat_fan_mode_test.c new file mode 100644 index 0000000000..44d8a71e9c --- /dev/null +++ b/applications/zpc/components/zwave_command_classes/test/zwave_command_class_thermostat_fan_mode_test.c @@ -0,0 +1,675 @@ +/****************************************************************************** + * # License + * Copyright 2023 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. + * + *****************************************************************************/ + +#include "zwave_command_class_thermostat_fan_mode.h" +#include "zwave_command_class_thermostat_fan_types.h" +#include "zwave_command_classes_utils.h" +#include "unity.h" + +// Generic includes +#include + +// Includes from other components +#include "datastore.h" +#include "attribute_store.h" +#include "attribute_store_helper.h" +#include "attribute_store_fixt.h" +#include "zpc_attribute_store_type_registration.h" + +// Interface includes +#include "attribute_store_defined_attribute_types.h" +#include "ZW_classcmd.h" +#include "zwave_utils.h" +#include "zwave_controller_types.h" + +// Test helpers +#include "zpc_attribute_store_test_helper.h" + +// Mock includes +#include "attribute_resolver_mock.h" +#include "zpc_attribute_resolver_mock.h" +#include "zwave_command_handler_mock.h" +#include "dotdot_mqtt_mock.h" +#include "dotdot_mqtt_generated_commands_mock.h" + +// Missing for now +#define THERMOSTAT_FAN_MODE_VERSION_V5 0x05 +// Value that is unsupported for all Thermostat Fan Mode versions +#define THERMOSTAT_FAN_MODE_UNSUPPORTED 0x0F + + +static zwave_command_handler_t handler = {}; + +static attribute_resolver_function_t current_fan_mode_get = NULL; +static attribute_resolver_function_t current_fan_mode_set = NULL; +static attribute_resolver_function_t supported_fan_get = NULL; + +// Buffer for frame +static uint8_t received_frame[255] = {}; +static uint16_t received_frame_size = 0; + +// Stub functions +static sl_status_t + attribute_resolver_register_rule_stub(attribute_store_type_t node_type, + attribute_resolver_function_t set_func, + attribute_resolver_function_t get_func, + int cmock_num_calls) +{ + if (node_type == ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_CURRENT_MODE + || node_type == ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_OFF_FLAG) { + TEST_ASSERT_NOT_NULL(set_func); + TEST_ASSERT_NOT_NULL(get_func); + current_fan_mode_get = get_func; + current_fan_mode_set = set_func; + } else if (node_type + == ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_SUPPORTED_MODES) { + TEST_ASSERT_NULL(set_func); + TEST_ASSERT_NOT_NULL(get_func); + supported_fan_get = get_func; + } + + return SL_STATUS_OK; +} + +static sl_status_t zwave_command_handler_register_handler_stub( + zwave_command_handler_t new_command_class_handler, int cmock_num_calls) +{ + handler = new_command_class_handler; + + TEST_ASSERT_EQUAL(ZWAVE_CONTROLLER_ENCAPSULATION_NONE, + handler.minimal_scheme); + TEST_ASSERT_EQUAL(COMMAND_CLASS_THERMOSTAT_FAN_MODE, handler.command_class); + TEST_ASSERT_EQUAL(5, handler.version); + TEST_ASSERT_NOT_NULL(handler.control_handler); + TEST_ASSERT_NULL(handler.support_handler); + TEST_ASSERT_FALSE(handler.manual_security_validation); + + return SL_STATUS_OK; +} + +/// 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; +} + +/// Called before each and every test +void setUp() +{ + zpc_attribute_store_test_helper_create_network(); + + // Unset previous definition get/set functions + current_fan_mode_get = NULL; + current_fan_mode_set = NULL; + supported_fan_get = NULL; + memset(received_frame, 0, sizeof(received_frame)); + received_frame_size = 0; + // Unset previous definition of handler + memset(&handler, 0, sizeof(zwave_command_handler_t)); + + // Resolution functions + attribute_resolver_register_rule_Stub(&attribute_resolver_register_rule_stub); + // Handler registration + zwave_command_handler_register_handler_Stub( + &zwave_command_handler_register_handler_stub); + // Call init + TEST_ASSERT_EQUAL(SL_STATUS_OK, + zwave_command_class_thermostat_fan_mode_init()); +} + +/// Called after each and every test +void tearDown() {} + +//////////////////////////////////////////////////////////////////////////// +// UTILS +//////////////////////////////////////////////////////////////////////////// + +// Set version and thus initialize the attribute tree +void set_version(zwave_cc_version_t version) +{ + attribute_store_node_t version_node = attribute_store_add_node( + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_VERSION, + endpoint_id_node); + + attribute_store_set_reported(version_node, &version, sizeof(version)); +} + +void set_supported_modes(thermostat_fan_supported_modes_t supported_modes) +{ + sl_status_t status = attribute_store_set_child_reported( + endpoint_id_node, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_SUPPORTED_MODES, + &supported_modes, + sizeof(supported_modes)); + + TEST_ASSERT_EQUAL_MESSAGE(SL_STATUS_OK, + status, + "Should be able to set supported mode node"); +} + +// Help test happy case +void helper_fan_mode_set(zwave_cc_version_t version, bool happy_case) +{ + printf("helper_fan_mode_set: version %d, happy_case : %d\n", + version, + happy_case); + + set_version(version); + + thermostat_fan_mode_off_flag_t off_flag; + uint8_t supported_bit1 = 0b00000000; + uint8_t supported_bit2 = 0b00000000; + size_t supported_fan_mode_count = 0; + thermostat_fan_mode_t supported_fan_mode_type[16]; + + switch (version) { + case 1: + supported_bit1 = 0b00001010; + supported_bit2 = 0b00000000; + + supported_fan_mode_count = 2; + supported_fan_mode_type[0] = THERMOSTAT_FAN_MODE_SET_FAN_MODE_LOW; + supported_fan_mode_type[1] = THERMOSTAT_FAN_MODE_SET_FAN_MODE_HIGH; + break; + case 2: + off_flag = 1; + + supported_bit1 = 0b00010101; + supported_bit2 = 0b00000000; + + supported_fan_mode_count = 3; + supported_fan_mode_type[0] = THERMOSTAT_FAN_MODE_SET_FAN_MODE_AUTO_LOW; + supported_fan_mode_type[1] = THERMOSTAT_FAN_MODE_SET_FAN_MODE_AUTO_HIGH; + supported_fan_mode_type[2] + = THERMOSTAT_FAN_MODE_SET_FAN_MODE_AUTO_MEDIUM_V2; + break; + case 3: + off_flag = 5; // Should be taken down to 0 + + supported_bit1 = 0b11100000; + supported_bit2 = 0b00000000; + + supported_fan_mode_count = 3; + supported_fan_mode_type[0] = THERMOSTAT_FAN_MODE_SET_FAN_MODE_MEDIUM_V3; + supported_fan_mode_type[1] + = THERMOSTAT_FAN_MODE_SET_FAN_MODE_CIRCULATION_V3; + supported_fan_mode_type[2] = THERMOSTAT_FAN_MODE_SET_FAN_MODE_HUMIDITY_V3; + break; + case 4: + off_flag = 0; + + supported_bit1 = 0b01000001; + supported_bit2 = 0b00000011; + + supported_fan_mode_count = 4; + supported_fan_mode_type[0] = THERMOSTAT_FAN_MODE_SET_FAN_MODE_AUTO_LOW; + supported_fan_mode_type[1] + = THERMOSTAT_FAN_MODE_SET_FAN_MODE_CIRCULATION_V3; + supported_fan_mode_type[2] + = THERMOSTAT_FAN_MODE_SET_FAN_MODE_LEFT_RIGHT_V4; + supported_fan_mode_type[3] = THERMOSTAT_FAN_MODE_SET_FAN_MODE_UP_DOWN_V4; + break; + case 5: + off_flag = 1; + + supported_bit1 = 0b00010101; + supported_bit2 = 0b00001100; + + supported_fan_mode_count = 5; + supported_fan_mode_type[0] = THERMOSTAT_FAN_MODE_SET_FAN_MODE_AUTO_LOW; + supported_fan_mode_type[1] = THERMOSTAT_FAN_MODE_SET_FAN_MODE_AUTO_HIGH; + supported_fan_mode_type[2] + = THERMOSTAT_FAN_MODE_SET_FAN_MODE_AUTO_MEDIUM_V2; + supported_fan_mode_type[3] = THERMOSTAT_FAN_MODE_SET_FAN_MODE_QUIET_V4; + supported_fan_mode_type[4] = THERMOSTAT_FAN_MODE_REPORT_FAN_MODE_EXTERNAL_CIRCULATION_V5; + break; + TEST_FAIL_MESSAGE("Version not supported in helper_fan_mode_set()"); + } + + // Invert bit to make sure that the supported values are not defined + if (!happy_case) { + supported_bit1 = ~supported_bit1; + supported_bit2 = ~supported_bit2; + } + + thermostat_fan_supported_modes_t expected_bitmask + = (supported_bit2 << 8) | supported_bit1; + set_supported_modes(expected_bitmask); + + for (int i = 0; i < supported_fan_mode_count; i++) { + // Setup attribute store + thermostat_fan_mode_t fan_mode = supported_fan_mode_type[i]; + + attribute_store_node_t fan_mode_node + = attribute_store_get_node_child_by_type( + endpoint_id_node, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_CURRENT_MODE, + 0); + sl_status_t result = attribute_store_set_desired(fan_mode_node, + &fan_mode, + sizeof(fan_mode)); + TEST_ASSERT_EQUAL(SL_STATUS_OK, result); + + // Not existant in v1 + attribute_store_node_t off_flag_node + = attribute_store_get_node_child_by_type( + endpoint_id_node, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_OFF_FLAG, + 0); + + if (version == 1) { + TEST_ASSERT_EQUAL(ATTRIBUTE_STORE_INVALID_NODE, off_flag_node); + } else { + result = attribute_store_set_desired(off_flag_node, + &off_flag, + sizeof(off_flag)); + TEST_ASSERT_EQUAL(SL_STATUS_OK, result); + TEST_ASSERT_NOT_EQUAL(ATTRIBUTE_STORE_INVALID_NODE, off_flag_node); + } + + if (happy_case) { + // Call set + TEST_ASSERT_NOT_NULL(current_fan_mode_set); + sl_status_t set_reported_status + = current_fan_mode_set(fan_mode_node, + received_frame, + &received_frame_size); + TEST_ASSERT_EQUAL_MESSAGE(SL_STATUS_OK, + set_reported_status, + "Set function should have worked"); + + // Compute value + uint8_t expected_value = fan_mode; + if (version > 1) { + off_flag = off_flag >= 1 ? 1 : 0; // Adjust off flag if needed + expected_value = fan_mode | (off_flag << 7); + } + + // Check frame + const uint8_t expected_frame[] = {COMMAND_CLASS_THERMOSTAT_FAN_MODE, + THERMOSTAT_FAN_MODE_SET, + expected_value}; + TEST_ASSERT_EQUAL(sizeof(expected_frame), received_frame_size); + TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_frame, + received_frame, + received_frame_size); + + } else { + // Call set and should bail + TEST_ASSERT_NOT_NULL(current_fan_mode_set); + sl_status_t set_reported_status + = current_fan_mode_set(fan_mode_node, + received_frame, + &received_frame_size); + TEST_ASSERT_EQUAL_MESSAGE(SL_STATUS_NOT_SUPPORTED, + set_reported_status, + "Set function should NOT have worked"); + + + // Now we want to test version mismatch + // Set all supported modes + set_supported_modes(0xFFFF); + // Set fan mode to one that isn't supported by any of the version + fan_mode = THERMOSTAT_FAN_MODE_UNSUPPORTED; + result = attribute_store_set_desired(fan_mode_node, + &fan_mode, + sizeof(fan_mode)); + TEST_ASSERT_EQUAL(SL_STATUS_OK, result); + + set_reported_status + = current_fan_mode_set(fan_mode_node, + received_frame, + &received_frame_size); + TEST_ASSERT_EQUAL_MESSAGE(SL_STATUS_NOT_SUPPORTED, + set_reported_status, + "Set function should NOT have worked"); + // Reset supported modes + set_supported_modes(expected_bitmask); + } + } +} + +void helper_fan_mode_report(zwave_cc_version_t version, + bool happy_case) +{ + set_version(version); + + zwave_controller_connection_info_t info = {}; + info.remote.node_id = node_id; + info.remote.endpoint_id = endpoint_id; + info.local.is_multicast = false; + + TEST_ASSERT_NOT_NULL(handler.control_handler); + TEST_ASSERT_EQUAL(SL_STATUS_NOT_SUPPORTED, + handler.control_handler(&info, NULL, 0)); + + thermostat_fan_mode_t fan_mode; + thermostat_fan_mode_off_flag_t off_flag; + thermostat_fan_supported_modes_t current_supported_bitmask; + + switch (version) { + case 1: + fan_mode = THERMOSTAT_FAN_MODE_SET_FAN_MODE_HIGH; + current_supported_bitmask = 0x08; + // Set to 0 to ignore value (not supported) + off_flag = 0; + break; + case 2: + fan_mode = THERMOSTAT_FAN_MODE_SET_FAN_MODE_AUTO_MEDIUM_V2; + current_supported_bitmask = 0x10; + // Test if the value is correctly ignored since it is not supported in V2 + off_flag = 1; + break; + case 3: + fan_mode = THERMOSTAT_FAN_MODE_SET_FAN_MODE_CIRCULATION_V3; + current_supported_bitmask = 0x40; + off_flag = 1; + break; + case 4: + fan_mode = THERMOSTAT_FAN_MODE_SET_FAN_MODE_LEFT_RIGHT_V4; + current_supported_bitmask = 0x100; + off_flag = 0; + break; + case 5: + fan_mode = THERMOSTAT_FAN_MODE_REPORT_FAN_MODE_EXTERNAL_CIRCULATION_V5; + current_supported_bitmask = 0x800; + off_flag = 1; + break; + default: + TEST_FAIL_MESSAGE("Version not supported in helper_fan_mode_set"); + } + + uint8_t frame[] = {COMMAND_CLASS_THERMOSTAT_FAN_MODE, + THERMOSTAT_FAN_MODE_REPORT, + fan_mode | (off_flag << 7)}; + + attribute_store_node_t current_fan_mode_node + = attribute_store_get_node_child_by_type( + endpoint_id_node, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_CURRENT_MODE, + 0); + + if (happy_case) { + set_supported_modes(current_supported_bitmask); + + TEST_ASSERT_EQUAL(SL_STATUS_OK, + handler.control_handler(&info, frame, sizeof(frame))); + + TEST_ASSERT_EQUAL( + fan_mode, + attribute_store_get_reported_number(current_fan_mode_node)); + + attribute_store_node_t off_flag_node + = attribute_store_get_node_child_by_type( + endpoint_id_node, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_OFF_FLAG, + 0); + if (version == 1) { + attribute_store_node_t off_flag_node + = attribute_store_get_node_child_by_type( + endpoint_id_node, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_OFF_FLAG, + 0); + + TEST_ASSERT_EQUAL(ATTRIBUTE_STORE_INVALID_NODE, off_flag_node); + } else if (version == 2) { + // The attribute is defined bcp it exists in the SET command. + // But the value should be ignored since v2 doesn't support Off bit in report + // The test sets the value to 1 in the report and it should not be updated in the attribute store. + TEST_ASSERT_EQUAL(0, attribute_store_get_reported_number(off_flag_node)); + } else { + TEST_ASSERT_EQUAL(off_flag, + attribute_store_get_reported_number(off_flag_node)); + } + } else { + // Make sure that the reported value is not in the supported bitmask + set_supported_modes(~current_supported_bitmask); + TEST_ASSERT_EQUAL(SL_STATUS_NOT_SUPPORTED, + handler.control_handler(&info, frame, sizeof(frame))); + + thermostat_fan_mode_t reported_fan_mode; + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, + attribute_store_get_reported(current_fan_mode_node, + &reported_fan_mode, + sizeof(reported_fan_mode))); + + // Now we support all but we test version mismatch + set_supported_modes(0xFFFF); + uint8_t frame[] = {COMMAND_CLASS_THERMOSTAT_FAN_MODE, + THERMOSTAT_FAN_MODE_REPORT, + THERMOSTAT_FAN_MODE_UNSUPPORTED | (off_flag << 7)}; + + TEST_ASSERT_EQUAL(SL_STATUS_NOT_SUPPORTED, + handler.control_handler(&info, frame, sizeof(frame))); + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, + attribute_store_get_reported(current_fan_mode_node, + &reported_fan_mode, + sizeof(reported_fan_mode))); + } +} + +// happy_case : if true only accepted bit are sent +// if false fill all unused bit with 1 to see if they are ignored correctly +void helper_fan_mode_supported_mode_report(zwave_cc_version_t version) +{ + set_version(version); + + zwave_controller_connection_info_t info = {}; + info.remote.node_id = node_id; + info.remote.endpoint_id = endpoint_id; + info.local.is_multicast = false; + + TEST_ASSERT_NOT_NULL(handler.control_handler); + TEST_ASSERT_EQUAL(SL_STATUS_NOT_SUPPORTED, + handler.control_handler(&info, NULL, 0)); + + uint8_t supported_bit1 = 0b00000000; + uint8_t supported_bit2 = 0b00000000; + + switch (version) { + case 1: + supported_bit1 = 0b00001010; + supported_bit2 = 0b00000000; + break; + case 2: + supported_bit1 = 0b00010101; + supported_bit2 = 0b00000000; + break; + case 3: + supported_bit1 = 0b11100000; + supported_bit2 = 0b00000000; + break; + case 4: + supported_bit1 = 0b01000001; + supported_bit2 = 0b00000011; + break; + case 5: + supported_bit1 = 0b00010101; + supported_bit2 = 0b00000100; + break; + } + thermostat_fan_supported_modes_t expected_bitmask + = (supported_bit2 << 8) | supported_bit1; + set_supported_modes(expected_bitmask); + + const uint8_t frame[] = {COMMAND_CLASS_THERMOSTAT_FAN_MODE, + THERMOSTAT_FAN_MODE_SUPPORTED_REPORT, + supported_bit1, + supported_bit2}; + + TEST_ASSERT_EQUAL(SL_STATUS_OK, + handler.control_handler(&info, frame, sizeof(frame))); + + thermostat_fan_supported_modes_t reported_bitmask; + + sl_status_t reported_status = attribute_store_get_child_reported( + endpoint_id_node, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_SUPPORTED_MODES, + &reported_bitmask, + sizeof(reported_bitmask)); + + TEST_ASSERT_EQUAL_MESSAGE( + SL_STATUS_OK, + reported_status, + "Should be able to get reported value of " + "ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_MODE_SUPPORTED_MODES"); + TEST_ASSERT_EQUAL_MESSAGE(expected_bitmask, + reported_bitmask, + "Incorrect supported fan mode bitmask"); +} +//////////////////////////////////////////////////////////////////////////// +// THERMOSTAT_FAN_MODE_SUPPORTED +//////////////////////////////////////////////////////////////////////////// +void test_thermostat_fan_mode_supported_get_happy_case() +{ + // Ask for a Get Command, should always be the same + TEST_ASSERT_NOT_NULL(supported_fan_get); + supported_fan_get(0, received_frame, &received_frame_size); + const uint8_t expected_frame[] + = {COMMAND_CLASS_THERMOSTAT_FAN_MODE, THERMOSTAT_FAN_MODE_SUPPORTED_GET}; + TEST_ASSERT_EQUAL(sizeof(expected_frame), received_frame_size); + TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_frame, + received_frame, + received_frame_size); +} + +void test_thermostat_fan_mode_supported_report_v1_happy_case() +{ + helper_fan_mode_supported_mode_report(THERMOSTAT_FAN_MODE_VERSION); +} +void test_thermostat_fan_mode_supported_report_v2_happy_case() +{ + helper_fan_mode_supported_mode_report(THERMOSTAT_FAN_MODE_VERSION_V2); +} +void test_thermostat_fan_mode_supported_report_v3_happy_case() +{ + helper_fan_mode_supported_mode_report(THERMOSTAT_FAN_MODE_VERSION_V3); +} +void test_thermostat_fan_mode_supported_report_v4_happy_case() +{ + helper_fan_mode_supported_mode_report(THERMOSTAT_FAN_MODE_VERSION_V4); +} +void test_thermostat_fan_mode_supported_report_v5_happy_case() +{ + helper_fan_mode_supported_mode_report(THERMOSTAT_FAN_MODE_VERSION_V5); +} + +//////////////////////////////////////////////////////////////////////////// +// THERMOSTAT_FAN_MODE +//////////////////////////////////////////////////////////////////////////// +void test_thermostat_fan_mode_get_happy_case() +{ + // Ask for a Get Command, should always be the same + TEST_ASSERT_NOT_NULL(current_fan_mode_get); + current_fan_mode_get(0, received_frame, &received_frame_size); + const uint8_t expected_frame[] + = {COMMAND_CLASS_THERMOSTAT_FAN_MODE, THERMOSTAT_FAN_MODE_GET}; + TEST_ASSERT_EQUAL(sizeof(expected_frame), received_frame_size); + TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_frame, + received_frame, + received_frame_size); +} + +void test_thermostat_fan_mode_set_v1_happy_case() +{ + helper_fan_mode_set(THERMOSTAT_FAN_MODE_VERSION, true); +} +void test_thermostat_fan_mode_set_v2_happy_case() +{ + helper_fan_mode_set(THERMOSTAT_FAN_MODE_VERSION_V2, true); +} +void test_thermostat_fan_mode_set_v3_happy_case() +{ + helper_fan_mode_set(THERMOSTAT_FAN_MODE_VERSION_V3, true); +} +void test_thermostat_fan_mode_set_v4_happy_case() +{ + helper_fan_mode_set(THERMOSTAT_FAN_MODE_VERSION_V4, true); +} +void test_thermostat_fan_mode_set_v5_happy_case() +{ + helper_fan_mode_set(THERMOSTAT_FAN_MODE_VERSION_V5, true); +} +void test_thermostat_fan_mode_set_v1_not_supported_types() +{ + helper_fan_mode_set(THERMOSTAT_FAN_MODE_VERSION, false); +} +void test_thermostat_fan_mode_set_v2_not_supported_types() +{ + helper_fan_mode_set(THERMOSTAT_FAN_MODE_VERSION_V2, false); +} +void test_thermostat_fan_mode_set_v3_not_supported_types() +{ + helper_fan_mode_set(THERMOSTAT_FAN_MODE_VERSION_V3, false); +} +void test_thermostat_fan_mode_set_v4_not_supported_types() +{ + helper_fan_mode_set(THERMOSTAT_FAN_MODE_VERSION_V4, false); +} +void test_thermostat_fan_mode_set_v5_not_supported_types() +{ + helper_fan_mode_set(THERMOSTAT_FAN_MODE_VERSION_V5, false); +} + + +void test_thermostat_fan_mode_report_v1_happy_case() +{ + helper_fan_mode_report(THERMOSTAT_FAN_MODE_VERSION, true); +} +void test_thermostat_fan_mode_report_v2_happy_case() +{ + helper_fan_mode_report(THERMOSTAT_FAN_MODE_VERSION_V2, true); +} +void test_thermostat_fan_mode_report_v3_happy_case() +{ + helper_fan_mode_report(THERMOSTAT_FAN_MODE_VERSION_V3, true); +} +void test_thermostat_fan_mode_report_v4_happy_case() +{ + helper_fan_mode_report(THERMOSTAT_FAN_MODE_VERSION_V4, true); +} +void test_thermostat_fan_mode_report_v5_happy_case() +{ + helper_fan_mode_report(THERMOSTAT_FAN_MODE_VERSION_V5, true); +} + +void test_thermostat_fan_mode_report_v1_not_supported_types() +{ + helper_fan_mode_report(THERMOSTAT_FAN_MODE_VERSION, false); +} +void test_thermostat_fan_mode_report_v2_not_supported_types() +{ + helper_fan_mode_report(THERMOSTAT_FAN_MODE_VERSION_V2, false); +} +void test_thermostat_fan_mode_report_v3_not_supported_types() +{ + helper_fan_mode_report(THERMOSTAT_FAN_MODE_VERSION_V3, false); +} +void test_thermostat_fan_mode_report_v4_not_supported_types() +{ + helper_fan_mode_report(THERMOSTAT_FAN_MODE_VERSION_V4, false); +} +void test_thermostat_fan_mode_report_v5_not_supported_types() +{ + helper_fan_mode_report(THERMOSTAT_FAN_MODE_VERSION_V5, false); +} \ No newline at end of file diff --git a/applications/zpc/components/zwave_command_classes/test/zwave_command_class_thermostat_fan_state_test.c b/applications/zpc/components/zwave_command_classes/test/zwave_command_class_thermostat_fan_state_test.c new file mode 100644 index 0000000000..bb29cc8a2d --- /dev/null +++ b/applications/zpc/components/zwave_command_classes/test/zwave_command_class_thermostat_fan_state_test.c @@ -0,0 +1,186 @@ +/****************************************************************************** + * # License + * Copyright 2023 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. + * + *****************************************************************************/ + +#include "zwave_command_class_thermostat_fan_state.h" +#include "zwave_command_class_thermostat_fan_types.h" +#include "zwave_command_classes_utils.h" +#include "unity.h" + +// Generic includes +#include + +// Includes from other components +#include "datastore.h" +#include "attribute_store.h" +#include "attribute_store_helper.h" +#include "attribute_store_fixt.h" +#include "zpc_attribute_store_type_registration.h" + +// Interface includes +#include "attribute_store_defined_attribute_types.h" +#include "ZW_classcmd.h" +#include "zwave_utils.h" +#include "zwave_controller_types.h" + +// Test helpers +#include "zpc_attribute_store_test_helper.h" + +// Mock includes +#include "attribute_resolver_mock.h" +#include "zpc_attribute_resolver_mock.h" +#include "zwave_command_handler_mock.h" +#include "dotdot_mqtt_mock.h" +#include "dotdot_mqtt_generated_commands_mock.h" + + +static zwave_command_handler_t handler = {}; + +static attribute_resolver_function_t current_fan_state_get = NULL; + +// Buffer for frame +static uint8_t received_frame[255] = {}; +static uint16_t received_frame_size = 0; + +// Stub functions +static sl_status_t + attribute_resolver_register_rule_stub(attribute_store_type_t node_type, + attribute_resolver_function_t set_func, + attribute_resolver_function_t get_func, + int cmock_num_calls) +{ + if (node_type + == ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_STATE_FAN_OPERATING_STATE) { + TEST_ASSERT_NULL(set_func); + TEST_ASSERT_NOT_NULL(get_func); + current_fan_state_get = get_func; + } + + return SL_STATUS_OK; +} + +static sl_status_t zwave_command_handler_register_handler_stub( + zwave_command_handler_t new_command_class_handler, int cmock_num_calls) +{ + handler = new_command_class_handler; + + TEST_ASSERT_EQUAL(ZWAVE_CONTROLLER_ENCAPSULATION_NONE, + handler.minimal_scheme); + TEST_ASSERT_EQUAL(COMMAND_CLASS_THERMOSTAT_FAN_STATE, handler.command_class); + TEST_ASSERT_EQUAL(2, handler.version); + TEST_ASSERT_NOT_NULL(handler.control_handler); + TEST_ASSERT_NULL(handler.support_handler); + TEST_ASSERT_FALSE(handler.manual_security_validation); + + return SL_STATUS_OK; +} + +/// 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; +} + +/// Called before each and every test +void setUp() +{ + zpc_attribute_store_test_helper_create_network(); + + // Unset previous definition get/set functions + current_fan_state_get = NULL; + memset(received_frame, 0, sizeof(received_frame)); + received_frame_size = 0; + // Unset previous definition of handler + memset(&handler, 0, sizeof(zwave_command_handler_t)); + + // Resolution functions + attribute_resolver_register_rule_Stub(&attribute_resolver_register_rule_stub); + // Handler registration + zwave_command_handler_register_handler_Stub( + &zwave_command_handler_register_handler_stub); + // Call init + TEST_ASSERT_EQUAL(SL_STATUS_OK, + zwave_command_class_thermostat_fan_state_init()); +} + +/// Called after each and every test +void tearDown() {} + + +//////////////////////////////////////////////////////////////////////////// +// HELPERS +//////////////////////////////////////////////////////////////////////////// +// Happy case : not setting reserved bit to 1 +void helper_fan_state_report(thermostat_fan_state_t expected_state, bool happy_case) { + + uint8_t happy_case_mask = happy_case ? 0x00 : 0xF0; + + const uint8_t frame[] = {COMMAND_CLASS_THERMOSTAT_FAN_STATE, + THERMOSTAT_FAN_STATE_REPORT, + expected_state | happy_case_mask}; + + zwave_controller_connection_info_t info = {}; + info.remote.node_id = node_id; + info.remote.endpoint_id = endpoint_id; + info.local.is_multicast = false; + + TEST_ASSERT_EQUAL(SL_STATUS_OK, + handler.control_handler(&info, frame, sizeof(frame))); + + attribute_store_node_t fan_state_node + = attribute_store_get_node_child_by_type( + endpoint_id_node, + ATTRIBUTE_COMMAND_CLASS_THERMOSTAT_FAN_STATE_FAN_OPERATING_STATE, + 0); + + TEST_ASSERT_NOT_EQUAL(ATTRIBUTE_STORE_INVALID_NODE, fan_state_node); + + thermostat_fan_state_t reported_state = 0x00; + attribute_store_get_reported(fan_state_node, &reported_state, sizeof(reported_state)); + TEST_ASSERT_EQUAL(expected_state, reported_state); +} + +//////////////////////////////////////////////////////////////////////////// +// THERMOSTAT_FAN_STATE +//////////////////////////////////////////////////////////////////////////// +void test_thermostat_fan_state_get_happy_case() +{ + // Ask for a Get Command, should always be the same + TEST_ASSERT_NOT_NULL(current_fan_state_get); + current_fan_state_get(0, received_frame, &received_frame_size); + const uint8_t expected_frame[] + = {COMMAND_CLASS_THERMOSTAT_FAN_STATE, THERMOSTAT_FAN_STATE_GET}; + TEST_ASSERT_EQUAL(sizeof(expected_frame), received_frame_size); + TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_frame, + received_frame, + received_frame_size); +} + +void test_thermostat_fan_state_report_happy_case() +{ + helper_fan_state_report(0x02, true); +} + +void test_thermostat_fan_state_report_off_bit() +{ + helper_fan_state_report(0x03, false); +} diff --git a/components/uic_dotdot/dotdot-xml/Unify_FanControl.xml b/components/uic_dotdot/dotdot-xml/Unify_FanControl.xml new file mode 100644 index 0000000000..10eb3dd4a7 --- /dev/null +++ b/components/uic_dotdot/dotdot-xml/Unify_FanControl.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/uic_dotdot/dotdot-xml/library.xml b/components/uic_dotdot/dotdot-xml/library.xml index 78c9d8140a..8117f8b799 100644 --- a/components/uic_dotdot/dotdot-xml/library.xml +++ b/components/uic_dotdot/dotdot-xml/library.xml @@ -490,5 +490,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/zap-generated/include/dotdot_attribute_id_definitions.h b/components/uic_dotdot/zap-generated/include/dotdot_attribute_id_definitions.h index 70c0a280c2..aa8964872b 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 @@ -856,6 +856,10 @@ typedef enum { #define DOTDOT_PROTOCOL_CONTROLLER_NETWORK_MANAGEMENT_NETWORK_MANAGEMENT_STATE_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x1) // Definitions for cluster: Descriptor #define DOTDOT_DESCRIPTOR_DEVICE_TYPE_LIST_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x0) +// Definitions for cluster: UnifyFanControl +#define DOTDOT_UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x1) +#define DOTDOT_UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x2) +#define DOTDOT_UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE_ATTRIBUTE_ID ((dotdot_attribute_id_t)0x3) // clang-format on diff --git a/components/uic_dotdot/zap-generated/include/dotdot_cluster_command_id_definitions.h b/components/uic_dotdot/zap-generated/include/dotdot_cluster_command_id_definitions.h index 2833fd9544..9d4ca70162 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 @@ -364,6 +364,10 @@ // Commands for cluster: Descriptor +// Commands for cluster: UnifyFanControl +#define DOTDOT_UNIFY_FAN_CONTROL_SET_FAN_MODE_COMMAND_ID (0x0) +#define DOTDOT_UNIFY_FAN_CONTROL_TURN_OFF_COMMAND_ID (0x1) + #ifdef __cplusplus extern "C" { #endif diff --git a/components/uic_dotdot/zap-generated/include/dotdot_cluster_id_definitions.h b/components/uic_dotdot/zap-generated/include/dotdot_cluster_id_definitions.h index 1878448a59..d1104eab04 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 @@ -254,6 +254,10 @@ #define DOTDOT_DESCRIPTOR_CLUSTER_ID ((dotdot_cluster_id_t)0xFD13) +// Definitions for cluster: UnifyFanControl +#define DOTDOT_UNIFY_FAN_CONTROL_CLUSTER_ID ((dotdot_cluster_id_t)0xFD14) + + #ifdef __cplusplus extern "C" { #endif diff --git a/components/uic_dotdot/zap-generated/include/zap-types.h b/components/uic_dotdot/zap-generated/include/zap-types.h index fb82f117ca..548b4ee057 100644 --- a/components/uic_dotdot/zap-generated/include/zap-types.h +++ b/components/uic_dotdot/zap-generated/include/zap-types.h @@ -1285,6 +1285,35 @@ typedef enum { ZCL_WINDOW_COVERING_WINDOW_COVERING_TYPE_PROJECTOR_SCREEN = 9, } WindowCoveringWindowCoveringType; +// Enum for ZWaveFanModeEnum +typedef enum { + ZCL_Z_WAVE_FAN_MODE_ENUM_AUTO = 0, + ZCL_Z_WAVE_FAN_MODE_ENUM_LOW = 1, + ZCL_Z_WAVE_FAN_MODE_ENUM_AUTO_HIGH = 2, + ZCL_Z_WAVE_FAN_MODE_ENUM_HIGH = 3, + ZCL_Z_WAVE_FAN_MODE_ENUM_AUTO_MEDIUM = 4, + ZCL_Z_WAVE_FAN_MODE_ENUM_MEDIUM = 5, + ZCL_Z_WAVE_FAN_MODE_ENUM_CIRCULATION = 6, + ZCL_Z_WAVE_FAN_MODE_ENUM_HUMIDITY_CIRCULATION = 7, + ZCL_Z_WAVE_FAN_MODE_ENUM_LEFT_RIGHT = 8, + ZCL_Z_WAVE_FAN_MODE_ENUM_UP_DOWN = 9, + ZCL_Z_WAVE_FAN_MODE_ENUM_QUIET = 10, + ZCL_Z_WAVE_FAN_MODE_ENUM_EXTERNAL_CIRCULATION = 11, +} ZWaveFanModeEnum; + +// Enum for ZWaveFanStateEnum +typedef enum { + ZCL_Z_WAVE_FAN_STATE_ENUM_IDLE = 0, + ZCL_Z_WAVE_FAN_STATE_ENUM_RUNNING = 1, + ZCL_Z_WAVE_FAN_STATE_ENUM_RUNNING_HIGH = 2, + ZCL_Z_WAVE_FAN_STATE_ENUM_RUNNING_MEDIUM = 3, + ZCL_Z_WAVE_FAN_STATE_ENUM_CIRCULATION = 4, + ZCL_Z_WAVE_FAN_STATE_ENUM_HUMIDITY_CIRCULATION = 5, + ZCL_Z_WAVE_FAN_STATE_ENUM_RIGHT_LEFT_CIRCULATION = 6, + ZCL_Z_WAVE_FAN_STATE_ENUM_UP_DOWN_CIRCULATION = 7, + ZCL_Z_WAVE_FAN_STATE_ENUM_QUIET_CIRCULATION = 8, +} ZWaveFanStateEnum; + // Enum for ZoneEnrollResponseEnrollResponseCode typedef enum { ZCL_ZONE_ENROLL_RESPONSE_ENROLL_RESPONSE_CODE_SUCCESS = 0, @@ -1954,6 +1983,30 @@ typedef enum { #define TSTAT_SCHEDULE_MODE_HEAT_OFFSET (0) #define TSTAT_SCHEDULE_MODE_COOL (2) #define TSTAT_SCHEDULE_MODE_COOL_OFFSET (1) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_AUTO (1) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_AUTO_OFFSET (0) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_LOW (2) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_LOW_OFFSET (1) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_AUTO_HIGH (4) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_AUTO_HIGH_OFFSET (2) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_HIGH (8) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_HIGH_OFFSET (3) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_AUTO_MEDIUM (16) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_AUTO_MEDIUM_OFFSET (4) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_MEDIUM (32) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_MEDIUM_OFFSET (5) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_CIRCULATION (64) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_CIRCULATION_OFFSET (6) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_HUMIDITY_CIRCULATION (128) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_HUMIDITY_CIRCULATION_OFFSET (7) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_LEFT_RIGHT (256) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_LEFT_RIGHT_OFFSET (8) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_UP_DOWN (512) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_UP_DOWN_OFFSET (9) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_QUIET (1024) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_QUIET_OFFSET (10) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_EXTERNAL_CIRCULATION (2048) +#define UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_EXTERNAL_CIRCULATION_OFFSET (11) #define WINDOW_COVERING_CONFIG_OR_STATUS_OPERATIONAL (1) #define WINDOW_COVERING_CONFIG_OR_STATUS_OPERATIONAL_OFFSET (0) #define WINDOW_COVERING_CONFIG_OR_STATUS_ONLINE (2) 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 dbdfa36ecb..19f0fcd99f 100644 --- a/components/uic_dotdot/zap-generated/readme_ucl_mqtt_reference.md +++ b/components/uic_dotdot/zap-generated/readme_ucl_mqtt_reference.md @@ -53949,6 +53949,449 @@ mosquitto_pub -t 'ucl/by-unid///Descriptor/Commands/ForceReadAttribute +


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

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

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

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

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

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

+ + + + +\section unify_fan_control_cmds UnifyFanControl Commands + +

+ +\subsection unify_fan_control_set_fan_mode_cmd UnifyFanControl/SetFanMode Command + +**MQTT Topic Pattern:** + +``` +[PREFIX]/UnifyFanControl/Commands/SetFanMode +[PREFIX]/UnifyFanControl/GeneratedCommands/SetFanMode +``` + +**MQTT Payload JSON Schema:** + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "UnifyFanControl Cluster SetFanMode Command Properties", + "type": "object", + "properties": { + "FanMode": { + "type": "ZWaveFanModeEnum" + } + }, + "required": [ + "FanMode" + ] +} +``` + +**Example Mosquitto CLI Tool Usage** + +To send a UnifyFanControl/SetFanMode command under the by-unid topic space: + +```console +mosquitto_pub -t 'ucl/by-unid///UnifyFanControl/Commands/SetFanMode' -m '{ "FanMode": }' +``` + +To receive a UnifyFanControl/SetFanMode generated command from a UNID/endpoint: + +```console +mosquitto_sub -t 'ucl/by-unid///UnifyFanControl/GeneratedCommands/SetFanMode' +``` + +

+ +\subsection unify_fan_control_turn_off_cmd UnifyFanControl/TurnOff Command + +**MQTT Topic Pattern:** + +``` +[PREFIX]/UnifyFanControl/Commands/TurnOff +[PREFIX]/UnifyFanControl/GeneratedCommands/TurnOff +``` + +**MQTT Payload JSON Schema:** + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "UnifyFanControl Cluster TurnOff Command Properties", + "type": "object", + "properties": { + }, + "required": [ + ] +} +``` + +**Example Mosquitto CLI Tool Usage** + +To send a UnifyFanControl/TurnOff command under the by-unid topic space: + +```console +mosquitto_pub -t 'ucl/by-unid///UnifyFanControl/Commands/TurnOff' -m '{ }' +``` + +To receive a UnifyFanControl/TurnOff generated command from a UNID/endpoint: + +```console +mosquitto_sub -t 'ucl/by-unid///UnifyFanControl/GeneratedCommands/TurnOff' +``` + +

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

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


@@ -57576,6 +58019,69 @@ mosquitto_pub -t 'ucl/by-unid///Descriptor/Commands/ForceReadAttribute

+ + + +\section enum_z_wave_fan_mode_enum ZWaveFanModeEnum Enum + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ZWaveFanModeEnum Enum Properties", + "type": "string", + "enum": [ + "Auto", + "Low", + "AutoHigh", + "High", + "AutoMedium", + "Medium", + "Circulation", + "HumidityCirculation", + "LeftRight", + "UpDown", + "Quiet", + "ExternalCirculation" + ] +} +``` + + + + + +

+ + + + +\section enum_z_wave_fan_state_enum ZWaveFanStateEnum Enum + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "ZWaveFanStateEnum Enum Properties", + "type": "string", + "enum": [ + "Idle", + "Running", + "RunningHigh", + "RunningMedium", + "Circulation", + "HumidityCirculation", + "RightLeftCirculation", + "UpDownCirculation", + "QuietCirculation" + ] +} +``` + + + + + +

+ @@ -59947,6 +60453,63 @@ mosquitto_pub -t 'ucl/by-unid///Descriptor/Commands/ForceReadAttribute

+ + + +\section enum_unify_fan_controlz_wave_supported_fan_mode UnifyFanControlZWaveSupportedFanMode Bitmap + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "UnifyFanControlZWaveSupportedFanMode Enum Properties", + "type": "object", + "properties": { + "Auto": { + "type": "boolean" + }, + "Low": { + "type": "boolean" + }, + "AutoHigh": { + "type": "boolean" + }, + "High": { + "type": "boolean" + }, + "AutoMedium": { + "type": "boolean" + }, + "Medium": { + "type": "boolean" + }, + "Circulation": { + "type": "boolean" + }, + "HumidityCirculation": { + "type": "boolean" + }, + "LeftRight": { + "type": "boolean" + }, + "UpDown": { + "type": "boolean" + }, + "Quiet": { + "type": "boolean" + }, + "ExternalCirculation": { + "type": "boolean" + } + } +} +``` + + + + + +

+ 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 cd3e175e69..954087bb9d 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 @@ -2025,6 +2025,21 @@ const char *uic_dotdot_get_attribute_name(dotdot_cluster_id_t cluster_id, return "Unknown"; } // clang-format off + case DOTDOT_UNIFY_FAN_CONTROL_CLUSTER_ID: + // clang-format on + switch (attribute_id) { + // clang-format off + case DOTDOT_UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE_ATTRIBUTE_ID: + return "ZWaveFanMode"; + case DOTDOT_UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_ATTRIBUTE_ID: + return "ZWaveSupportedFanMode"; + case DOTDOT_UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE_ATTRIBUTE_ID: + return "ZWaveFanState"; + // clang-format on + default: + return "Unknown"; + } + // clang-format off // clang-format on default: return "Unknown"; @@ -4409,6 +4424,17 @@ dotdot_attribute_id_t return DOTDOT_DESCRIPTOR_DEVICE_TYPE_LIST_ATTRIBUTE_ID; } break; + case DOTDOT_UNIFY_FAN_CONTROL_CLUSTER_ID: + if (strcmp ("ZWaveFanMode", attribute_name) == 0) { + return DOTDOT_UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE_ATTRIBUTE_ID; + } + if (strcmp ("ZWaveSupportedFanMode", attribute_name) == 0) { + return DOTDOT_UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_ATTRIBUTE_ID; + } + if (strcmp ("ZWaveFanState", attribute_name) == 0) { + return DOTDOT_UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE_ATTRIBUTE_ID; + } + break; default: return DOTDOT_INVALID_ATTRIBUTE_ID; } @@ -6426,6 +6452,21 @@ dotdot_attribute_json_type_t return JSON_TYPE_UNKNOWN; } // clang-format off + case DOTDOT_UNIFY_FAN_CONTROL_CLUSTER_ID: + // clang-format on + switch (attribute_id) { + // clang-format off + case DOTDOT_UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE_ATTRIBUTE_ID: + return JSON_TYPE_NUMBER; + case DOTDOT_UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_ATTRIBUTE_ID: + return JSON_TYPE_NUMBER; + case DOTDOT_UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE_ATTRIBUTE_ID: + return JSON_TYPE_NUMBER; + // clang-format on + default: + return JSON_TYPE_UNKNOWN; + } + // clang-format off // clang-format on default: return JSON_TYPE_UNKNOWN; @@ -6774,5 +6815,14 @@ bool uic_dotdot_attribute_is_enum(dotdot_cluster_id_t cluster_id, if (64787 == cluster_id) { } + if (64788 == cluster_id) { + if (1 == attribute_id) { + return true; + } + if (3 == attribute_id) { + return true; + } + } + return false; } diff --git a/components/uic_dotdot/zap-generated/src/dotdot_cluster_id_definitions.c b/components/uic_dotdot/zap-generated/src/dotdot_cluster_id_definitions.c index 18b5402fa7..ff922f680d 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 @@ -128,6 +128,8 @@ const char* uic_dotdot_get_cluster_name(dotdot_cluster_id_t cluster_id) { return "ProtocolController-NetworkManagement"; case DOTDOT_DESCRIPTOR_CLUSTER_ID: return "Descriptor"; + case DOTDOT_UNIFY_FAN_CONTROL_CLUSTER_ID: + return "UnifyFanControl"; default: return "Unknown"; } @@ -299,6 +301,9 @@ dotdot_cluster_id_t uic_dotdot_get_cluster_id(const char* cluster_name) { if (strcmp ("Descriptor", cluster_name) == 0) { return DOTDOT_DESCRIPTOR_CLUSTER_ID; } + if (strcmp ("UnifyFanControl", cluster_name) == 0) { + return DOTDOT_UNIFY_FAN_CONTROL_CLUSTER_ID; + } // Return an invalid ID if we did not get any match. return DOTDOT_INVALID_CLUSTER_ID; diff --git a/components/uic_dotdot/zap/addon-base-helper.js b/components/uic_dotdot/zap/addon-base-helper.js index 738d15f6b4..ecd6172211 100644 --- a/components/uic_dotdot/zap/addon-base-helper.js +++ b/components/uic_dotdot/zap/addon-base-helper.js @@ -100,6 +100,8 @@ zcl_type_map = (function() { CredentialTypeEnum: 'enum8', DataOperationTypeEnum: 'enum8', CredentialRuleEnum: 'enum8', + ZWaveFanModeEnum: 'enum8', + ZWaveFanStateEnum: 'enum8', // zclType: 'uint8', BarrierControlSafetyStatus: 'map16', 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 f0dc62b095..746da877b6 100644 --- a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt.h +++ b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt.h @@ -40444,6 +40444,320 @@ void uic_mqtt_dotdot_descriptor_publish_supported_commands( void uic_mqtt_dotdot_descriptor_publish_empty_supported_commands( const dotdot_unid_t unid ,dotdot_endpoint_id_t endpoint); +// Callback types used by the unify_fan_control cluster +typedef sl_status_t (*uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_t)( + dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint, + uic_mqtt_dotdot_callback_call_type_t call_type, + ZWaveFanModeEnum fan_mode + +); +typedef sl_status_t (*uic_mqtt_dotdot_unify_fan_control_turn_off_callback_t)( + dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint, + uic_mqtt_dotdot_callback_call_type_t call_type +); + +typedef struct { + uint8_t z_wave_fan_mode; + uint16_t z_wave_supported_fan_mode; + uint8_t z_wave_fan_state; +} uic_mqtt_dotdot_unify_fan_control_state_t; + +typedef struct { + bool z_wave_fan_mode; + bool z_wave_supported_fan_mode; + bool z_wave_fan_state; +} uic_mqtt_dotdot_unify_fan_control_updated_state_t; + +typedef sl_status_t (*uic_mqtt_dotdot_unify_fan_control_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_fan_control_state_t, + uic_mqtt_dotdot_unify_fan_control_updated_state_t +); + +typedef sl_status_t (*uic_mqtt_dotdot_unify_fan_control_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_fan_control_updated_state_t +); + + +/** + * @brief Command fields for UnifyFanControl/SetFanMode + */ +typedef struct { + ZWaveFanModeEnum fan_mode; +} uic_mqtt_dotdot_unify_fan_control_command_set_fan_mode_fields_t; + + +/** + * @brief Setup callback to be called when a + * UnifyFanControl/Commands/set_fan_mode 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_unify_fan_control_set_fan_mode_callback_set(const uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_t callback); +/** + * @brief Unsets callback to be called when a + * UnifyFanControl/Commands/set_fan_mode is received. + * + * @param callback Function to be no longer called on command reception + */ +void uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_unset(const uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_t callback); +/** + * @brief Clears all callbacks registered for when + * UnifyFanControl/Commands/set_fan_mode is received. + */ +void uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_clear(); + +/** + * @brief Setup callback to be called when a + * +/UnifyFanControl/GeneratedCommands/set_fan_mode 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_unify_fan_control_generated_set_fan_mode_callback_set(const uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_t callback); +/** + * @brief Unsets callback to be called when a + * +/UnifyFanControl/GeneratedCommands/set_fan_mode is received. + * @param callback Function to be no longer called on command reception + */ +void uic_mqtt_dotdot_unify_fan_control_generated_set_fan_mode_callback_unset(const uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_t callback); +/** + * @brief Clears all callbacks registered for when + * +/UnifyFanControl/GeneratedCommands/set_fan_mode is received. + */ +void uic_mqtt_dotdot_unify_fan_control_generated_set_fan_mode_callback_clear(); +/** + * @brief Setup callback to be called when a + * UnifyFanControl/Commands/turn_off 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_unify_fan_control_turn_off_callback_set(const uic_mqtt_dotdot_unify_fan_control_turn_off_callback_t callback); +/** + * @brief Unsets callback to be called when a + * UnifyFanControl/Commands/turn_off is received. + * + * @param callback Function to be no longer called on command reception + */ +void uic_mqtt_dotdot_unify_fan_control_turn_off_callback_unset(const uic_mqtt_dotdot_unify_fan_control_turn_off_callback_t callback); +/** + * @brief Clears all callbacks registered for when + * UnifyFanControl/Commands/turn_off is received. + */ +void uic_mqtt_dotdot_unify_fan_control_turn_off_callback_clear(); + +/** + * @brief Setup callback to be called when a + * +/UnifyFanControl/GeneratedCommands/turn_off 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_unify_fan_control_generated_turn_off_callback_set(const uic_mqtt_dotdot_unify_fan_control_turn_off_callback_t callback); +/** + * @brief Unsets callback to be called when a + * +/UnifyFanControl/GeneratedCommands/turn_off is received. + * @param callback Function to be no longer called on command reception + */ +void uic_mqtt_dotdot_unify_fan_control_generated_turn_off_callback_unset(const uic_mqtt_dotdot_unify_fan_control_turn_off_callback_t callback); +/** + * @brief Clears all callbacks registered for when + * +/UnifyFanControl/GeneratedCommands/turn_off is received. + */ +void uic_mqtt_dotdot_unify_fan_control_generated_turn_off_callback_clear(); + +/** + * @brief Setup a callback for WriteAttribute to be called when a + * +/unify_fan_control/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_fan_control_write_attributes_callback( + const uic_mqtt_dotdot_unify_fan_control_write_attributes_callback_t callback +); +/** + * @brief Unsets a callback for WriteAttribute to be called when a + * +/unify_fan_control/Commands/WriteAttributes is received. + * @param callback Function to be no longer called on command reception + */ +void uic_mqtt_dotdot_unset_unify_fan_control_write_attributes_callback( + const uic_mqtt_dotdot_unify_fan_control_write_attributes_callback_t callback +); +/** + * @brief Clears all callbacks registered for when + * +/unify_fan_control/Commands/WriteAttributes is received. + */ +void uic_mqtt_dotdot_clear_unify_fan_control_write_attributes_callbacks(); + +/** + * @brief Setup a callback for ForceReadAttributes to be called when a + * +/unify_fan_control/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_fan_control_force_read_attributes_callback( + const uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback_t callback +); +/** + * @brief Unsets a callback for ForceReadAttributes to be called when a + * +/unify_fan_control/Commands/ForceReadAttributes is received. + * + * @param callback Function to be no longer called on command reception + */ +void uic_mqtt_dotdot_unset_unify_fan_control_force_read_attributes_callback( + const uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback_t callback +); +/** + * @brief Clears all callbacks registered for when + * +/unify_fan_control/Commands/ForceReadAttributes is received. + */ +void uic_mqtt_dotdot_clear_unify_fan_control_force_read_attributes_callbacks(); + +/** + * @brief Publish the attribute; UnifyFanControl/Attributes/ZWaveFanMode + * + * @param base_topic topic prefix to publish, /z_wave_fan_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_fan_control_z_wave_fan_mode_publish( + const char *base_topic, + ZWaveFanModeEnum value, + uic_mqtt_dotdot_attribute_publish_type_t publish_type +); + +/** + * @brief Unretains a published attribute; UnifyFanControl/Attributes/ZWaveFanMode + * + * @param base_topic topic prefix to publish, /z_wave_fan_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_fan_control_z_wave_fan_mode_unretain( + const char *base_topic, + uic_mqtt_dotdot_attribute_publish_type_t publish_type +); + +/** + * @brief Publish the attribute; UnifyFanControl/Attributes/ZWaveSupportedFanMode + * + * @param base_topic topic prefix to publish, /z_wave_supported_fan_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_fan_control_z_wave_supported_fan_mode_publish( + const char *base_topic, + uint16_t value, + uic_mqtt_dotdot_attribute_publish_type_t publish_type +); + +/** + * @brief Unretains a published attribute; UnifyFanControl/Attributes/ZWaveSupportedFanMode + * + * @param base_topic topic prefix to publish, /z_wave_supported_fan_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_fan_control_z_wave_supported_fan_mode_unretain( + const char *base_topic, + uic_mqtt_dotdot_attribute_publish_type_t publish_type +); + +/** + * @brief Publish the attribute; UnifyFanControl/Attributes/ZWaveFanState + * + * @param base_topic topic prefix to publish, /z_wave_fan_state + * 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_fan_control_z_wave_fan_state_publish( + const char *base_topic, + ZWaveFanStateEnum value, + uic_mqtt_dotdot_attribute_publish_type_t publish_type +); + +/** + * @brief Unretains a published attribute; UnifyFanControl/Attributes/ZWaveFanState + * + * @param base_topic topic prefix to publish, /z_wave_fan_state + * 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_fan_control_z_wave_fan_state_unretain( + const char *base_topic, + uic_mqtt_dotdot_attribute_publish_type_t publish_type +); + + +/** + * @brief Publish the UnifyFanControl/ClusterRevision attribute + * + * @param base_topic topic prefix to publish, /UnifyFanControl/Attributes/ClusterRevision + * will be appended. + * @param value Value to publish. + */ +void uic_mqtt_dotdot_unify_fan_control_publish_cluster_revision(const char* base_topic, uint16_t value); + +/** + * @brief Unretain a publication to UnifyFanControl/ClusterRevision attribute + * + * @param base_topic topic prefix to publish, /UnifyFanControl/Attributes/ClusterRevision + * will be appended. + */ +void uic_mqtt_dotdot_unify_fan_control_unretain_cluster_revision(const char* base_topic); + +/** + * @brief Publish the SupportedCommands for UNID/EndPoint for the UnifyFanControl Cluster + * + * This function will iterate over all Commands in the UnifyFanControl 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_fan_control_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 UnifyFanControl Cluster + * + * @param unid + * @param endpoint ) + */ +void uic_mqtt_dotdot_unify_fan_control_publish_empty_supported_commands( + const dotdot_unid_t unid + ,dotdot_endpoint_id_t endpoint); /** * @brief Publish the SupportedCommands for UNID/EndPoint diff --git a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_attributes.h b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_attributes.h index 92daf48a23..64e4dce14c 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 @@ -5134,6 +5134,28 @@ typedef sl_status_t (*uic_mqtt_dotdot_descriptor_attribute_device_type_list_call size_t device_type_list_count, const DeviceTypeStruct* device_type_list ); +// Callback types used by the unify_fan_control cluster +typedef sl_status_t (*uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_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 z_wave_fan_mode +); +typedef sl_status_t (*uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_supported_fan_mode_callback_t)( + dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint, + bool unretained, + uic_mqtt_dotdot_attribute_update_type_t update_type, + uint16_t z_wave_supported_fan_mode +); +typedef sl_status_t (*uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_state_callback_t)( + dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint, + bool unretained, + uic_mqtt_dotdot_attribute_update_type_t update_type, + uint8_t z_wave_fan_state +); #ifdef __cplusplus extern "C" { @@ -9858,6 +9880,32 @@ sl_status_t uic_mqtt_dotdot_descriptor_attributes_init(); void uic_mqtt_dotdot_descriptor_attribute_device_type_list_callback_set(const uic_mqtt_dotdot_descriptor_attribute_device_type_list_callback_t callback); +/** + * Initializes the attributes features for the UnifyFanControl cluster, + * allowing to receive attribute updates from other UNIDs. + */ +sl_status_t uic_mqtt_dotdot_unify_fan_control_attributes_init(); + +/** + * Setup callback to be called when a + * UnifyFanControl/Attributes/z_wave_fan_mode/# is received. Setting + * this callback will overwrite the previous set callback + */ +void uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_mode_callback_set(const uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_mode_callback_t callback); +/** + * Setup callback to be called when a + * UnifyFanControl/Attributes/z_wave_supported_fan_mode/# is received. Setting + * this callback will overwrite the previous set callback + */ +void uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_supported_fan_mode_callback_set(const uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_supported_fan_mode_callback_t callback); +/** + * Setup callback to be called when a + * UnifyFanControl/Attributes/z_wave_fan_state/# is received. Setting + * this callback will overwrite the previous set callback + */ +void uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_state_callback_set(const uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_state_callback_t callback); + + #ifdef __cplusplus } #endif // __cplusplus diff --git a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_generated_commands.h b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_generated_commands.h index 16d97617d4..03ff5a39a2 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 @@ -4986,6 +4986,66 @@ void uic_mqtt_dotdot_descriptor_publish_generated_write_attributes_command( uic_mqtt_dotdot_descriptor_updated_state_t attribute_list ); +/** + * @brief Publishes an incoming/generated SetFanMode command for + * the UnifyFanControl cluster. + * + * Publication will be made at the following topic + * ucl/by-unid/UNID/epID/UnifyFanControl/GeneratedCommands/SetFanMode + * + * @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 fields Struct pointer with the fields value of the command + * + */ +void uic_mqtt_dotdot_unify_fan_control_publish_generated_set_fan_mode_command( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint, + const uic_mqtt_dotdot_unify_fan_control_command_set_fan_mode_fields_t *fields + +); +/** + * @brief Publishes an incoming/generated TurnOff command for + * the UnifyFanControl cluster. + * + * Publication will be made at the following topic + * ucl/by-unid/UNID/epID/UnifyFanControl/GeneratedCommands/TurnOff + * + * @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. + * + * + */ +void uic_mqtt_dotdot_unify_fan_control_publish_generated_turn_off_command( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint +); + +/** + * @brief Publishes an incoming/generated WriteAttributes command for + * the UnifyFanControl cluster. + * + * Publication will be made at the following topic + * ucl/by-unid/UNID/epID/UnifyFanControl/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_fan_control_publish_generated_write_attributes_command( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint, + uic_mqtt_dotdot_unify_fan_control_state_t attribute_values, + uic_mqtt_dotdot_unify_fan_control_updated_state_t attribute_list +); + #ifdef __cplusplus } diff --git a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_group_commands.h b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_group_commands.h index a5c1d17ef9..42b755e92b 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 @@ -3709,6 +3709,54 @@ void uic_mqtt_dotdot_by_group_descriptor_write_attributes_callback_set( +/** + * @brief Callback signature for by-group UnifyFanControl::SetFanMode command. + */ +typedef void (*uic_mqtt_dotdot_by_group_unify_fan_control_set_fan_mode_callback_t)( + const dotdot_group_id_t group_id, + const uic_mqtt_dotdot_unify_fan_control_command_set_fan_mode_fields_t *fields +); + +/** + * Setup handler to be called when a + * ucl/by-group/+/UnifyFanControl/set_fan_mode is received. + * Setting this callback will overwrite the previous set callback. + * + */ +void uic_mqtt_dotdot_by_group_unify_fan_control_set_fan_mode_callback_set(const uic_mqtt_dotdot_by_group_unify_fan_control_set_fan_mode_callback_t callback); + +/** + * @brief Callback signature for by-group UnifyFanControl::TurnOff command. + */ +typedef void (*uic_mqtt_dotdot_by_group_unify_fan_control_turn_off_callback_t)( + const dotdot_group_id_t group_id +); + +/** + * Setup handler to be called when a + * ucl/by-group/+/UnifyFanControl/turn_off is received. + * Setting this callback will overwrite the previous set callback. + * + */ +void uic_mqtt_dotdot_by_group_unify_fan_control_turn_off_callback_set(const uic_mqtt_dotdot_by_group_unify_fan_control_turn_off_callback_t callback); + +typedef void (*uic_mqtt_dotdot_by_group_unify_fan_control_write_attributes_callback_t)( + const dotdot_group_id_t group_id, + uic_mqtt_dotdot_unify_fan_control_state_t, + uic_mqtt_dotdot_unify_fan_control_updated_state_t +); + +/** + * Setup a callback for WriteAttribute to be called when a + * ucl/by-group/+/unify_fan_control/Commands/WriteAttributes is received. + * Setting this callback will overwrite any previously set callback. + */ +void uic_mqtt_dotdot_by_group_unify_fan_control_write_attributes_callback_set( + const uic_mqtt_dotdot_by_group_unify_fan_control_write_attributes_callback_t callback +); + + + #ifdef __cplusplus } #endif // __cplusplus diff --git a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_helpers.h b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_helpers.h index dcc29ad5f1..103d553394 100644 --- a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_helpers.h +++ b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_helpers.h @@ -818,6 +818,20 @@ char *window_covering_window_covering_type_get_enum_value_name_c( uint32_t value, char *result, size_t max_result_size); /** Get window_covering_window_covering_type enum representation from string. */ uint32_t window_covering_window_covering_type_get_enum_value_number_c(const char *str); +#define Z_WAVE_FAN_MODE_ENUM_ENUM_NAME_AVAILABLE 1 + +/** Get z_wave_fan_mode_enum string representation from enum. */ +char *z_wave_fan_mode_enum_get_enum_value_name_c( + uint32_t value, char *result, size_t max_result_size); +/** Get z_wave_fan_mode_enum enum representation from string. */ +uint32_t z_wave_fan_mode_enum_get_enum_value_number_c(const char *str); +#define Z_WAVE_FAN_STATE_ENUM_ENUM_NAME_AVAILABLE 1 + +/** Get z_wave_fan_state_enum string representation from enum. */ +char *z_wave_fan_state_enum_get_enum_value_name_c( + uint32_t value, char *result, size_t max_result_size); +/** Get z_wave_fan_state_enum enum representation from string. */ +uint32_t z_wave_fan_state_enum_get_enum_value_number_c(const char *str); #define ZONE_ENROLL_RESPONSE_ENROLL_RESPONSE_CODE_ENUM_NAME_AVAILABLE 1 /** Get zone_enroll_response_enroll_response_code string representation from enum. */ diff --git a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_helpers.hpp b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_helpers.hpp index 8db91a511c..3009ed8d74 100644 --- a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_helpers.hpp +++ b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_helpers.hpp @@ -1951,6 +1951,40 @@ std::string window_covering_window_covering_type_get_enum_value_name( */ uint32_t window_covering_window_covering_type_get_enum_value_number(const std::string &str); +#define Z_WAVE_FAN_MODE_ENUM_ENUM_NAME_AVAILABLE 1 + +/** + * @brief Finds the name of a field for the ZWaveFanModeEnum enum + * + * @returns A string representation of the value. + */ +std::string z_wave_fan_mode_enum_get_enum_value_name( + uint32_t value); + +/** + * @brief Finds the enum number of a string representation for the ZWaveFanModeEnum enum + * + * @returns A number enum value. + */ +uint32_t z_wave_fan_mode_enum_get_enum_value_number(const std::string &str); + +#define Z_WAVE_FAN_STATE_ENUM_ENUM_NAME_AVAILABLE 1 + +/** + * @brief Finds the name of a field for the ZWaveFanStateEnum enum + * + * @returns A string representation of the value. + */ +std::string z_wave_fan_state_enum_get_enum_value_name( + uint32_t value); + +/** + * @brief Finds the enum number of a string representation for the ZWaveFanStateEnum enum + * + * @returns A number enum value. + */ +uint32_t z_wave_fan_state_enum_get_enum_value_number(const std::string &str); + #define ZONE_ENROLL_RESPONSE_ENROLL_RESPONSE_CODE_ENUM_NAME_AVAILABLE 1 /** diff --git a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_send_commands.h b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_send_commands.h index 64af1ec66c..7fd63c6864 100644 --- a/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_send_commands.h +++ b/components/uic_dotdot_mqtt/zap-generated/include/dotdot_mqtt_send_commands.h @@ -7127,6 +7127,76 @@ void uic_mqtt_dotdot_protocol_controller_network_management_publish_write_comman void uic_mqtt_dotdot_protocol_controller_network_management_publish_write_command_to_group( uint16_t destination_group_id ); +/** + * @brief Sends/Publishes a SetFanMode command for + * the UnifyFanControl cluster to a destination. + * + * Publication will be made at the following topic + * ucl/by-unid/UNID/epID/UnifyFanControl/Commands/SetFanMode + * + * @param destination_unid The UNID of the node that should receive the command. + * + * @param destination_endpoint The Endpoint ID of the node that should receive the command. + * + * + * @param fields Struct pointer with the fields value of the command + * + */ +void uic_mqtt_dotdot_unify_fan_control_publish_set_fan_mode_command( + const dotdot_unid_t destination_unid, + const dotdot_endpoint_id_t destination_endpoint, + const uic_mqtt_dotdot_unify_fan_control_command_set_fan_mode_fields_t *fields + +); + +/** + * @brief Sends/Publishes a SetFanMode command for + * the UnifyFanControl cluster to a group. + * + * Publication will be made at the following topic + * ucl/by-group/GroupID/UnifyFanControl/Commands/SetFanMode + * + * @param destination_group_id The GroupID that should receive the command. + * + * @param fields Struct pointer with the fields value of the command + * + */ +void uic_mqtt_dotdot_unify_fan_control_publish_set_fan_mode_command_to_group( + uint16_t destination_group_id, + const uic_mqtt_dotdot_unify_fan_control_command_set_fan_mode_fields_t *fields + +); +/** + * @brief Sends/Publishes a TurnOff command for + * the UnifyFanControl cluster to a destination. + * + * Publication will be made at the following topic + * ucl/by-unid/UNID/epID/UnifyFanControl/Commands/TurnOff + * + * @param destination_unid The UNID of the node that should receive the command. + * + * @param destination_endpoint The Endpoint ID of the node that should receive the command. + * + * + */ +void uic_mqtt_dotdot_unify_fan_control_publish_turn_off_command( + const dotdot_unid_t destination_unid, + const dotdot_endpoint_id_t destination_endpoint +); + +/** + * @brief Sends/Publishes a TurnOff command for + * the UnifyFanControl cluster to a group. + * + * Publication will be made at the following topic + * ucl/by-group/GroupID/UnifyFanControl/Commands/TurnOff + * + * @param destination_group_id The GroupID that should receive the command. + * + */ +void uic_mqtt_dotdot_unify_fan_control_publish_turn_off_command_to_group( + uint16_t destination_group_id +); #ifdef __cplusplus 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 3180bd0498..4516edbd44 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,36 @@ void uic_mqtt_dotdot_descriptor_publish_supported_generated_commands( ); +/** + * @brief Struct containing the list of commands for UnifyFanControl + */ +typedef struct _uic_mqtt_dotdot_unify_fan_control_supported_commands_ { + bool set_fan_mode; + bool turn_off; + bool write_attributes; +} uic_mqtt_dotdot_unify_fan_control_supported_commands_t; + +/** + * @brief Sends/Publishes a the SupportedGenerated commands for + * the UnifyFanControl cluster for a UNID/Endpoint + * + * Publication will be made at the following topic + * ucl/by-unid/UNID/epID/UnifyFanControl/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_fan_control_publish_supported_generated_commands( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint, + const uic_mqtt_dotdot_unify_fan_control_supported_commands_t *command_list +); + + #ifdef __cplusplus } diff --git a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.cpp b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.cpp index 5dbf0c607a..7df0f8bae6 100644 --- a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.cpp +++ b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.cpp @@ -94739,6 +94739,723 @@ sl_status_t uic_mqtt_dotdot_descriptor_init() return SL_STATUS_OK; } +// Callbacks pointers +static std::set uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback; +static std::set uic_mqtt_dotdot_unify_fan_control_generated_set_fan_mode_callback; +static std::set uic_mqtt_dotdot_unify_fan_control_turn_off_callback; +static std::set uic_mqtt_dotdot_unify_fan_control_generated_turn_off_callback; +static std::set uic_mqtt_dotdot_unify_fan_control_write_attributes_callback; +static std::set uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback; + +// Callbacks setters +void uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_set(const uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_t callback) +{ + if (callback != nullptr) { + uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback.insert(callback); + } +} +void uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_unset(const uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_t callback) +{ + uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback.erase(callback); +} +void uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_clear() +{ + uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback.clear(); +} +std::set& get_uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback() +{ + return uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback; +} + +void uic_mqtt_dotdot_unify_fan_control_generated_set_fan_mode_callback_set(const uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_t callback) +{ + if (callback != nullptr) { + uic_mqtt_dotdot_unify_fan_control_generated_set_fan_mode_callback.insert(callback); + } +} +void uic_mqtt_dotdot_unify_fan_control_generated_set_fan_mode_callback_unset(const uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_t callback) +{ + uic_mqtt_dotdot_unify_fan_control_generated_set_fan_mode_callback.erase(callback); +} +void uic_mqtt_dotdot_unify_fan_control_generated_set_fan_mode_callback_clear() +{ + uic_mqtt_dotdot_unify_fan_control_generated_set_fan_mode_callback.clear(); +} +void uic_mqtt_dotdot_unify_fan_control_turn_off_callback_set(const uic_mqtt_dotdot_unify_fan_control_turn_off_callback_t callback) +{ + if (callback != nullptr) { + uic_mqtt_dotdot_unify_fan_control_turn_off_callback.insert(callback); + } +} +void uic_mqtt_dotdot_unify_fan_control_turn_off_callback_unset(const uic_mqtt_dotdot_unify_fan_control_turn_off_callback_t callback) +{ + uic_mqtt_dotdot_unify_fan_control_turn_off_callback.erase(callback); +} +void uic_mqtt_dotdot_unify_fan_control_turn_off_callback_clear() +{ + uic_mqtt_dotdot_unify_fan_control_turn_off_callback.clear(); +} +std::set& get_uic_mqtt_dotdot_unify_fan_control_turn_off_callback() +{ + return uic_mqtt_dotdot_unify_fan_control_turn_off_callback; +} + +void uic_mqtt_dotdot_unify_fan_control_generated_turn_off_callback_set(const uic_mqtt_dotdot_unify_fan_control_turn_off_callback_t callback) +{ + if (callback != nullptr) { + uic_mqtt_dotdot_unify_fan_control_generated_turn_off_callback.insert(callback); + } +} +void uic_mqtt_dotdot_unify_fan_control_generated_turn_off_callback_unset(const uic_mqtt_dotdot_unify_fan_control_turn_off_callback_t callback) +{ + uic_mqtt_dotdot_unify_fan_control_generated_turn_off_callback.erase(callback); +} +void uic_mqtt_dotdot_unify_fan_control_generated_turn_off_callback_clear() +{ + uic_mqtt_dotdot_unify_fan_control_generated_turn_off_callback.clear(); +} + +void uic_mqtt_dotdot_set_unify_fan_control_write_attributes_callback( + const uic_mqtt_dotdot_unify_fan_control_write_attributes_callback_t callback) +{ + if (callback != nullptr) { + uic_mqtt_dotdot_unify_fan_control_write_attributes_callback.insert(callback); + } +} +void uic_mqtt_dotdot_unset_unify_fan_control_write_attributes_callback( + const uic_mqtt_dotdot_unify_fan_control_write_attributes_callback_t callback) +{ + uic_mqtt_dotdot_unify_fan_control_write_attributes_callback.erase(callback); +} +void uic_mqtt_dotdot_clear_unify_fan_control_write_attributes_callbacks() +{ + uic_mqtt_dotdot_unify_fan_control_write_attributes_callback.clear(); +} +std::set& get_uic_mqtt_dotdot_unify_fan_control_write_attributes_callback() +{ + return uic_mqtt_dotdot_unify_fan_control_write_attributes_callback; +} + +void uic_mqtt_dotdot_set_unify_fan_control_force_read_attributes_callback( + const uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback_t callback) +{ + if (callback != nullptr) { + uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback.insert(callback); + } +} +void uic_mqtt_dotdot_unset_unify_fan_control_force_read_attributes_callback( + const uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback_t callback) +{ + uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback.erase(callback); +} +void uic_mqtt_dotdot_clear_unify_fan_control_force_read_attributes_callbacks() +{ + uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback.clear(); +} + + +// Callback function for incoming publications on ucl/by-unid/+/+/UnifyFanControl/Commands/SetFanMode +void uic_mqtt_dotdot_on_unify_fan_control_set_fan_mode( + const char *topic, + const char *message, + const size_t message_length) +{ + if (message_length == 0 || (uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback.empty())) { + 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; + } + + ZWaveFanModeEnum fan_mode = {}; + + + nlohmann::json jsn; + try { + jsn = nlohmann::json::parse(std::string(message)); + + + uic_mqtt_dotdot_parse_unify_fan_control_set_fan_mode( + jsn, + fan_mode + ); + + } catch (const nlohmann::json::parse_error& e) { + // Catch JSON object field parsing errors + sl_log_debug(LOG_TAG, LOG_FMT_JSON_PARSE_FAIL, "UnifyFanControl", "SetFanMode"); + return; + } catch (const nlohmann::json::exception& e) { + // Catch JSON object field parsing errors + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl", "SetFanMode", e.what()); + return; + } catch (const std::exception& e) { + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl", "SetFanMode", ""); + return; + } + + + + for (const auto& callback: uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback){ + callback( + static_cast(unid.c_str()), + endpoint, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL, + fan_mode + + ); + } + +} + +// Callback function for incoming publications on ucl/by-unid/+/+/UnifyFanControl/GeneratedCommands/SetFanMode +static void uic_mqtt_dotdot_on_generated_unify_fan_control_set_fan_mode( + const char *topic, + const char *message, + const size_t message_length) +{ + if (message_length == 0 || (uic_mqtt_dotdot_unify_fan_control_generated_set_fan_mode_callback.empty())) { + 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; + } + + ZWaveFanModeEnum fan_mode = {}; + + + nlohmann::json jsn; + try { + jsn = nlohmann::json::parse(std::string(message)); + + + uic_mqtt_dotdot_parse_unify_fan_control_set_fan_mode( + jsn, + fan_mode + ); + + } catch (const nlohmann::json::parse_error& e) { + // Catch JSON object field parsing errors + sl_log_debug(LOG_TAG, LOG_FMT_JSON_PARSE_FAIL, "UnifyFanControl", "SetFanMode"); + return; + } catch (const nlohmann::json::exception& e) { + // Catch JSON object field parsing errors + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl", "SetFanMode", e.what()); + return; + } catch (const std::exception& e) { + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl", "SetFanMode", ""); + return; + } + + + + + for (const auto& callback: uic_mqtt_dotdot_unify_fan_control_generated_set_fan_mode_callback){ + callback( + static_cast(unid.c_str()), + endpoint, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL, + fan_mode + + ); + } +} + + +// Callback function for incoming publications on ucl/by-unid/+/+/UnifyFanControl/Commands/TurnOff +void uic_mqtt_dotdot_on_unify_fan_control_turn_off( + const char *topic, + const char *message, + const size_t message_length) +{ + if (message_length == 0 || (uic_mqtt_dotdot_unify_fan_control_turn_off_callback.empty())) { + 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; + } + + + + nlohmann::json jsn; + try { + jsn = nlohmann::json::parse(std::string(message)); + + + } catch (const nlohmann::json::parse_error& e) { + // Catch JSON object field parsing errors + sl_log_debug(LOG_TAG, LOG_FMT_JSON_PARSE_FAIL, "UnifyFanControl", "TurnOff"); + return; + } catch (const nlohmann::json::exception& e) { + // Catch JSON object field parsing errors + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl", "TurnOff", e.what()); + return; + } catch (const std::exception& e) { + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl", "TurnOff", ""); + return; + } + + + + for (const auto& callback: uic_mqtt_dotdot_unify_fan_control_turn_off_callback){ + callback( + static_cast(unid.c_str()), + endpoint, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL + ); + } + +} + +// Callback function for incoming publications on ucl/by-unid/+/+/UnifyFanControl/GeneratedCommands/TurnOff +static void uic_mqtt_dotdot_on_generated_unify_fan_control_turn_off( + const char *topic, + const char *message, + const size_t message_length) +{ + if (message_length == 0 || (uic_mqtt_dotdot_unify_fan_control_generated_turn_off_callback.empty())) { + 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; + } + + + + nlohmann::json jsn; + try { + jsn = nlohmann::json::parse(std::string(message)); + + + } catch (const nlohmann::json::parse_error& e) { + // Catch JSON object field parsing errors + sl_log_debug(LOG_TAG, LOG_FMT_JSON_PARSE_FAIL, "UnifyFanControl", "TurnOff"); + return; + } catch (const nlohmann::json::exception& e) { + // Catch JSON object field parsing errors + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl", "TurnOff", e.what()); + return; + } catch (const std::exception& e) { + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl", "TurnOff", ""); + return; + } + + + + + for (const auto& callback: uic_mqtt_dotdot_unify_fan_control_generated_turn_off_callback){ + callback( + static_cast(unid.c_str()), + endpoint, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL + ); + } +} + + +// Callback function for incoming publications on ucl/by-unid/+/+/UnifyFanControl/Commands/WriteAttributes +void uic_mqtt_dotdot_on_unify_fan_control_WriteAttributes( + const char *topic, + const char *message, + const size_t message_length) +{ + if (uic_mqtt_dotdot_unify_fan_control_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_fan_control_state_t new_state = {}; + uic_mqtt_dotdot_unify_fan_control_updated_state_t new_updated_state = {}; + + + nlohmann::json jsn; + try { + jsn = nlohmann::json::parse(std::string(message)); + + uic_mqtt_dotdot_parse_unify_fan_control_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, "UnifyFanControl", "WriteAttributes"); + return; + } catch (const nlohmann::json::exception& e) { + // Catch JSON object field parsing errors + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl", "WriteAttributes", e.what()); + return; + } catch (const std::exception& e) { + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl", "WriteAttributes", ""); + return; + } + + for (const auto& callback: uic_mqtt_dotdot_unify_fan_control_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_fan_control_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_fan_control_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_fan_control_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.z_wave_fan_mode = true; + force_update.z_wave_supported_fan_mode = true; + force_update.z_wave_fan_state = true; + trigger_handler = true; + } else { + std::unordered_map supported_attrs = { + {"ZWaveFanMode", &force_update.z_wave_fan_mode }, + {"ZWaveSupportedFanMode", &force_update.z_wave_supported_fan_mode }, + {"ZWaveFanState", &force_update.z_wave_fan_state }, + }; + + 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_fan_control_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, "UnifyFanControl/Commands/ForceReadAttributes: Unable to parse JSON payload"); + return; + } +} + +sl_status_t uic_mqtt_dotdot_unify_fan_control_z_wave_fan_mode_publish( + const char *base_topic, + ZWaveFanModeEnum value, + uic_mqtt_dotdot_attribute_publish_type_t publish_type +) +{ + nlohmann::json jsn; + + // This is a single value + + #ifdef UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE_ENUM_NAME_AVAILABLE + jsn["value"] = unify_fan_control_z_wave_fan_mode_get_enum_value_name((uint32_t)value); + #elif defined(Z_WAVE_FAN_MODE_ENUM_ENUM_NAME_AVAILABLE) + jsn["value"] = z_wave_fan_mode_enum_get_enum_value_name((uint32_t)value); + #else + sl_log_warning(LOG_TAG,"Warning: Enum name not available for UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE. Using number instead."); + jsn["value"] = static_cast(value); + #endif + + + std::string payload_str; + try { + // Payload contains data from end nodes, which we cannot control, thus we handle if there are non-utf8 characters + payload_str = jsn.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); + } catch (const nlohmann::json::exception& e) { + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl/Attributes/ZWaveFanMode", e.what()); + return SL_STATUS_OK; + } + + + std::string topic = std::string(base_topic) + "/UnifyFanControl/Attributes/ZWaveFanMode"; + 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_fan_control_z_wave_fan_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) + + "/UnifyFanControl/Attributes/ZWaveFanMode"; + + 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_fan_control_z_wave_supported_fan_mode_publish( + const char *base_topic, + uint16_t value, + uic_mqtt_dotdot_attribute_publish_type_t publish_type +) +{ + nlohmann::json jsn; + + // This is a single value + + nlohmann::json bitmap_values = UnifyFanControlZWaveSupportedFanMode.get_bitmap_values_as_json_tree((uint32_t)value); + jsn["value"] = bitmap_values; + + + 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, "UnifyFanControl/Attributes/ZWaveSupportedFanMode", e.what()); + return SL_STATUS_OK; + } + + boost::replace_all(payload_str, "\"true\"", "true"); + boost::replace_all(payload_str, "\"false\"", "false"); + + std::string topic = std::string(base_topic) + "/UnifyFanControl/Attributes/ZWaveSupportedFanMode"; + 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_fan_control_z_wave_supported_fan_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) + + "/UnifyFanControl/Attributes/ZWaveSupportedFanMode"; + + 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_fan_control_z_wave_fan_state_publish( + const char *base_topic, + ZWaveFanStateEnum value, + uic_mqtt_dotdot_attribute_publish_type_t publish_type +) +{ + nlohmann::json jsn; + + // This is a single value + + #ifdef UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE_ENUM_NAME_AVAILABLE + jsn["value"] = unify_fan_control_z_wave_fan_state_get_enum_value_name((uint32_t)value); + #elif defined(Z_WAVE_FAN_STATE_ENUM_ENUM_NAME_AVAILABLE) + jsn["value"] = z_wave_fan_state_enum_get_enum_value_name((uint32_t)value); + #else + sl_log_warning(LOG_TAG,"Warning: Enum name not available for UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE. Using number instead."); + jsn["value"] = static_cast(value); + #endif + + + std::string payload_str; + try { + // Payload contains data from end nodes, which we cannot control, thus we handle if there are non-utf8 characters + payload_str = jsn.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); + } catch (const nlohmann::json::exception& e) { + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl/Attributes/ZWaveFanState", e.what()); + return SL_STATUS_OK; + } + + + std::string topic = std::string(base_topic) + "/UnifyFanControl/Attributes/ZWaveFanState"; + 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_fan_control_z_wave_fan_state_unretain( + const char *base_topic, + uic_mqtt_dotdot_attribute_publish_type_t publish_type) +{ + // clang-format on + std::string topic + = std::string(base_topic) + + "/UnifyFanControl/Attributes/ZWaveFanState"; + + 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_fan_control_init() +{ + std::string base_topic = "ucl/by-unid/+/+/"; + + std::string subscription_topic; + if(!uic_mqtt_dotdot_unify_fan_control_write_attributes_callback.empty()) { + subscription_topic = base_topic + "UnifyFanControl/Commands/WriteAttributes"; + uic_mqtt_subscribe(subscription_topic.c_str(), uic_mqtt_dotdot_on_unify_fan_control_WriteAttributes); + } + + if(!uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback.empty()) { + subscription_topic = base_topic + "UnifyFanControl/Commands/ForceReadAttributes"; + uic_mqtt_subscribe(subscription_topic.c_str(), uic_mqtt_dotdot_on_unify_fan_control_force_read_attributes); + } + if (!uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback.empty()) { + subscription_topic = base_topic + "UnifyFanControl/Commands/SetFanMode"; + uic_mqtt_subscribe(subscription_topic.c_str(), uic_mqtt_dotdot_on_unify_fan_control_set_fan_mode); + } + if (!uic_mqtt_dotdot_unify_fan_control_generated_set_fan_mode_callback.empty()) { + subscription_topic = base_topic + "UnifyFanControl/GeneratedCommands/SetFanMode"; + uic_mqtt_subscribe(subscription_topic.c_str(), uic_mqtt_dotdot_on_generated_unify_fan_control_set_fan_mode); + } + if (!uic_mqtt_dotdot_unify_fan_control_turn_off_callback.empty()) { + subscription_topic = base_topic + "UnifyFanControl/Commands/TurnOff"; + uic_mqtt_subscribe(subscription_topic.c_str(), uic_mqtt_dotdot_on_unify_fan_control_turn_off); + } + if (!uic_mqtt_dotdot_unify_fan_control_generated_turn_off_callback.empty()) { + subscription_topic = base_topic + "UnifyFanControl/GeneratedCommands/TurnOff"; + uic_mqtt_subscribe(subscription_topic.c_str(), uic_mqtt_dotdot_on_generated_unify_fan_control_turn_off); + } + + // Init the attributes for that cluster + uic_mqtt_dotdot_unify_fan_control_attributes_init(); + + uic_mqtt_dotdot_by_group_unify_fan_control_init(); + + return SL_STATUS_OK; +} + sl_status_t uic_mqtt_dotdot_init() { @@ -94952,6 +95669,10 @@ sl_status_t uic_mqtt_dotdot_init() { status_flag = uic_mqtt_dotdot_descriptor_init(); } + if (status_flag == SL_STATUS_OK) { + status_flag = uic_mqtt_dotdot_unify_fan_control_init(); + } + return status_flag; } @@ -95013,6 +95734,7 @@ void uic_mqtt_dotdot_publish_supported_commands( uic_mqtt_dotdot_aox_position_estimation_publish_supported_commands(unid, endpoint_id); uic_mqtt_dotdot_protocol_controller_network_management_publish_supported_commands(unid, 0); uic_mqtt_dotdot_descriptor_publish_supported_commands(unid, endpoint_id); + uic_mqtt_dotdot_unify_fan_control_publish_supported_commands(unid, endpoint_id); } void uic_mqtt_dotdot_publish_empty_supported_commands( @@ -95071,6 +95793,7 @@ void uic_mqtt_dotdot_publish_empty_supported_commands( uic_mqtt_dotdot_aox_position_estimation_publish_empty_supported_commands(unid, endpoint_id); uic_mqtt_dotdot_protocol_controller_network_management_publish_empty_supported_commands(unid); uic_mqtt_dotdot_descriptor_publish_empty_supported_commands(unid, endpoint_id); + uic_mqtt_dotdot_unify_fan_control_publish_empty_supported_commands(unid, endpoint_id); } // Publishing Cluster Revision for Basic Cluster @@ -108533,6 +109256,200 @@ void uic_mqtt_dotdot_descriptor_publish_empty_supported_commands( } } +// Publishing Cluster Revision for UnifyFanControl Cluster +void uic_mqtt_dotdot_unify_fan_control_publish_cluster_revision(const char* base_topic, uint16_t value) +{ + std::string cluster_topic = std::string(base_topic) + "/UnifyFanControl/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 UnifyFanControl Cluster +void uic_mqtt_dotdot_unify_fan_control_unretain_cluster_revision(const char* base_topic) +{ + // clang-format on + std::string cluster_topic + = std::string(base_topic) + + "/UnifyFanControl/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_fan_control_set_fan_mode_is_supported( + const dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint_id) +{ + ZWaveFanModeEnum fan_mode_value; + memset(&fan_mode_value, 0x00, sizeof(fan_mode_value)); + for (const auto& callback: uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback) { + if (callback( unid, endpoint_id, UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK + , + fan_mode_value + + ) == SL_STATUS_OK) { + return true; + } + } + + return false; +} +static inline bool uic_mqtt_dotdot_unify_fan_control_turn_off_is_supported( + const dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint_id) +{ + for (const auto& callback: uic_mqtt_dotdot_unify_fan_control_turn_off_callback) { + if (callback( unid, endpoint_id, UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK + + ) == SL_STATUS_OK) { + return true; + } + } + + return false; +} + +static inline bool uic_mqtt_dotdot_unify_fan_control_write_attributes_is_supported( + const dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint_id) +{ + for (const auto& callback: uic_mqtt_dotdot_unify_fan_control_write_attributes_callback) { + uic_mqtt_dotdot_unify_fan_control_state_t unify_fan_control_new_state = {}; + uic_mqtt_dotdot_unify_fan_control_updated_state_t unify_fan_control_new_updated_state = {}; + + if (callback( + unid, + endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK, + unify_fan_control_new_state, + unify_fan_control_new_updated_state + ) == SL_STATUS_OK) { + return true; + } + } + return false; +} + +static inline bool uic_mqtt_dotdot_unify_fan_control_force_read_attributes_is_supported( + const dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint_id) +{ + for (const auto& callback: uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback) { + uic_mqtt_dotdot_unify_fan_control_updated_state_t unify_fan_control_force_update = {0}; + if (callback( + unid, + endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK, + unify_fan_control_force_update + ) == SL_STATUS_OK) { + return true; + } + } + return false; +} + +// Publishing Supported Commands for UnifyFanControl Cluster +void uic_mqtt_dotdot_unify_fan_control_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 + if (uic_mqtt_dotdot_unify_fan_control_set_fan_mode_is_supported(unid, endpoint_id)) { + if (first_command == false) { + ss << ", "; + } + first_command = false; + ss << R"("SetFanMode")"; + } + if (uic_mqtt_dotdot_unify_fan_control_turn_off_is_supported(unid, endpoint_id)) { + if (first_command == false) { + ss << ", "; + } + first_command = false; + ss << R"("TurnOff")"; + } + + // Check for a WriteAttributes Callback + if(uic_mqtt_dotdot_unify_fan_control_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_fan_control_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 += "/UnifyFanControl/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 += "/UnifyFanControl/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 UnifyFanControl Cluster +void uic_mqtt_dotdot_unify_fan_control_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 += "/UnifyFanControl/SupportedCommands"; + + if (uic_mqtt_count_topics(topic.c_str()) > 0) { + uic_mqtt_publish(topic.c_str(), + EMPTY_VALUE_ARRAY, + strlen(EMPTY_VALUE_ARRAY), + true); + } +} + //////////////////////////////////////////////////////////////////////////////// // Generated Commands publications functions @@ -115181,3 +116098,71 @@ void uic_mqtt_dotdot_protocol_controller_network_management_publish_generated_wr payload.size(), false); } +/** + * @brief Publishes an incoming/generated SetFanMode command for + * the UnifyFanControl cluster. + * + * Publication will be made at the following topic + * ucl/by-unid/UNID/epID/UnifyFanControl/GeneratedCommands/SetFanMode + * + * @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 fields Struct pointer with the fields value of the command + * + */ +void uic_mqtt_dotdot_unify_fan_control_publish_generated_set_fan_mode_command( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint, + const uic_mqtt_dotdot_unify_fan_control_command_set_fan_mode_fields_t *fields + +) { + // Create the topic + std::string topic = "ucl/by-unid/"+ std::string(unid) + "/ep" + + std::to_string(endpoint) + "/"; + topic += "UnifyFanControl/GeneratedCommands/SetFanMode"; + + std::string payload = + get_json_payload_for_unify_fan_control_set_fan_mode_command( + fields); + + // Publish our command + uic_mqtt_publish(topic.c_str(), + payload.c_str(), + payload.size(), + false); +} +/** + * @brief Publishes an incoming/generated TurnOff command for + * the UnifyFanControl cluster. + * + * Publication will be made at the following topic + * ucl/by-unid/UNID/epID/UnifyFanControl/GeneratedCommands/TurnOff + * + * @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. + * + * + */ +void uic_mqtt_dotdot_unify_fan_control_publish_generated_turn_off_command( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint +) { + // Create the topic + std::string topic = "ucl/by-unid/"+ std::string(unid) + "/ep" + + std::to_string(endpoint) + "/"; + topic += "UnifyFanControl/GeneratedCommands/TurnOff"; + + std::string payload = + get_json_payload_for_unify_fan_control_turn_off_command( + ); + + // Publish our command + uic_mqtt_publish(topic.c_str(), + payload.c_str(), + payload.size(), + false); +} diff --git a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.hpp b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.hpp index 156e7b356d..60475fcfb1 100644 --- a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.hpp +++ b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt.hpp @@ -367,6 +367,13 @@ sl_status_t uic_mqtt_dotdot_by_group_aox_position_estimation_init(); */ sl_status_t uic_mqtt_dotdot_by_group_descriptor_init(); +/** + * @brief Initialize UnifyFanControl dotdot bygroup command handlers + * + * @returns SL_STATUS_OK on success, error otherwise. + */ +sl_status_t uic_mqtt_dotdot_by_group_unify_fan_control_init(); + // clang-format on @@ -5052,6 +5059,65 @@ void uic_mqtt_dotdot_on_descriptor_WriteAttributes( const size_t message_length); +// clang-format on + +/** + * @brief Retrieves the container with callbacks pointer for + * by-unid UnifyFanControl/Commands/SetFanMode messages + * + * @returns std::set of callbacks. + */ +std::set &get_uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback(); + +/** + * @brief MQTT Subscribe handler for incoming publications on: + * ucl/by-unid/+/+/UnifyFanControl/Commands/SetFanMode + */ +// clang-format off +void uic_mqtt_dotdot_on_unify_fan_control_set_fan_mode( + const char *topic, + const char *message, + const size_t message_length); +// clang-format on + +/** + * @brief Retrieves the container with callbacks pointer for + * by-unid UnifyFanControl/Commands/TurnOff messages + * + * @returns std::set of callbacks. + */ +std::set &get_uic_mqtt_dotdot_unify_fan_control_turn_off_callback(); + +/** + * @brief MQTT Subscribe handler for incoming publications on: + * ucl/by-unid/+/+/UnifyFanControl/Commands/TurnOff + */ +// clang-format off +void uic_mqtt_dotdot_on_unify_fan_control_turn_off( + const char *topic, + const char *message, + 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_fan_control_write_attributes_callback(); + +/** + * @brief MQTT Subscribe handler for incoming publications on: + * ucl/by-unid/+/+/UnifyFanControl/Commands/WriteAttributes + */ +// clang-format off +void uic_mqtt_dotdot_on_unify_fan_control_WriteAttributes( + const char *topic, + const char *message, + const size_t message_length); + + // All bitmaps are defined as the cluster label for the bitmap plus the command/attribute name @@ -5891,6 +5957,34 @@ const std::vector, std::vector, std::vector>>> UnifyFanControlZWaveSupportedFanMode_bitmap_data { +{ {"Auto", "bool", "0x1", "0"}, { +} }, +{ {"Low", "bool", "0x2", "1"}, { +} }, +{ {"AutoHigh", "bool", "0x4", "2"}, { +} }, +{ {"High", "bool", "0x8", "3"}, { +} }, +{ {"AutoMedium", "bool", "0x10", "4"}, { +} }, +{ {"Medium", "bool", "0x20", "5"}, { +} }, +{ {"Circulation", "bool", "0x40", "6"}, { +} }, +{ {"HumidityCirculation", "bool", "0x80", "7"}, { +} }, +{ {"LeftRight", "bool", "0x100", "8"}, { +} }, +{ {"UpDown", "bool", "0x200", "9"}, { +} }, +{ {"Quiet", "bool", "0x400", "10"}, { +} }, +{ {"ExternalCirculation", "bool", "0x800", "11"}, { +} } +}; +const dotdot_bitmap UnifyFanControlZWaveSupportedFanMode("UnifyFanControlZWaveSupportedFanMode", "Unknown UnifyFanControlZWaveSupportedFanMode", UnifyFanControlZWaveSupportedFanMode_bitmap_data); + const std::vector, std::vector>>> WindowCoveringConfigOrStatus_bitmap_data { { {"Operational", "bool", "0x1", "0"}, { } }, 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 43090fc989..5fb246fa93 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 @@ -63117,3 +63117,301 @@ void uic_mqtt_dotdot_descriptor_attribute_device_type_list_callback_set(const ui // End of supported cluster. +/////////////////////////////////////////////////////////////////////////////// +// Callback pointers for UnifyFanControl +/////////////////////////////////////////////////////////////////////////////// +static uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_mode_callback_t uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_mode_callback = nullptr; +static uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_supported_fan_mode_callback_t uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_supported_fan_mode_callback = nullptr; +static uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_state_callback_t uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_state_callback = nullptr; + +/////////////////////////////////////////////////////////////////////////////// +// Attribute update handlers for UnifyFanControl +/////////////////////////////////////////////////////////////////////////////// +static void uic_mqtt_dotdot_on_unify_fan_control_z_wave_fan_mode_attribute_update( + const char *topic, + const char *message, + const size_t message_length) { + if (uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_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; + } + + + ZWaveFanModeEnum z_wave_fan_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, "UnifyFanControl::ZWaveFanMode: Missing attribute element: 'value'\n"); + return; + } +// Start parsing value + uint32_t tmp = get_enum_decimal_value("value", json_payload); + if (tmp == numeric_limits::max()) { + #ifdef UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE_ENUM_NAME_AVAILABLE + tmp = unify_fan_control_z_wave_fan_mode_get_enum_value_number(json_payload.at("value").get()); + #elif defined(Z_WAVE_FAN_MODE_ENUM_NAME_AVAILABLE) + tmp = z_wave_fan_mode_get_enum_value_number(json_payload.at("value").get()); + #endif + } + z_wave_fan_mode = static_cast(tmp); + + // End parsing value + } + + } catch (const std::exception& e) { + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "value", message); + return; + } + + uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_mode_callback( + static_cast(unid.c_str()), + endpoint, + unretained, + update_type, + z_wave_fan_mode + ); + +} +static void uic_mqtt_dotdot_on_unify_fan_control_z_wave_supported_fan_mode_attribute_update( + const char *topic, + const char *message, + const size_t message_length) { + if (uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_supported_fan_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; + } + + + uint16_t z_wave_supported_fan_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, "UnifyFanControl::ZWaveSupportedFanMode: Missing attribute element: 'value'\n"); + return; + } +// Start parsing value + z_wave_supported_fan_mode = uic_dotdot_mqtt::get_bitmap_decimal_value("value", json_payload, UnifyFanControlZWaveSupportedFanMode); + + // End parsing value + } + + } catch (const std::exception& e) { + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "value", message); + return; + } + + uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_supported_fan_mode_callback( + static_cast(unid.c_str()), + endpoint, + unretained, + update_type, + z_wave_supported_fan_mode + ); + +} +static void uic_mqtt_dotdot_on_unify_fan_control_z_wave_fan_state_attribute_update( + const char *topic, + const char *message, + const size_t message_length) { + if (uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_state_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; + } + + + ZWaveFanStateEnum z_wave_fan_state = {}; + + 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, "UnifyFanControl::ZWaveFanState: Missing attribute element: 'value'\n"); + return; + } +// Start parsing value + uint32_t tmp = get_enum_decimal_value("value", json_payload); + if (tmp == numeric_limits::max()) { + #ifdef UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE_ENUM_NAME_AVAILABLE + tmp = unify_fan_control_z_wave_fan_state_get_enum_value_number(json_payload.at("value").get()); + #elif defined(Z_WAVE_FAN_STATE_ENUM_NAME_AVAILABLE) + tmp = z_wave_fan_state_get_enum_value_number(json_payload.at("value").get()); + #endif + } + z_wave_fan_state = static_cast(tmp); + + // End parsing value + } + + } catch (const std::exception& e) { + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "value", message); + return; + } + + uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_state_callback( + static_cast(unid.c_str()), + endpoint, + unretained, + update_type, + z_wave_fan_state + ); + +} + +/////////////////////////////////////////////////////////////////////////////// +// Attribute init functions for UnifyFanControl +/////////////////////////////////////////////////////////////////////////////// +sl_status_t uic_mqtt_dotdot_unify_fan_control_attributes_init() +{ + std::string base_topic = "ucl/by-unid/+/+/"; + + std::string subscription_topic; + if(uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_mode_callback) { + subscription_topic = base_topic + "UnifyFanControl/Attributes/ZWaveFanMode/#"; + uic_mqtt_subscribe(subscription_topic.c_str(), &uic_mqtt_dotdot_on_unify_fan_control_z_wave_fan_mode_attribute_update); + } + if(uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_supported_fan_mode_callback) { + subscription_topic = base_topic + "UnifyFanControl/Attributes/ZWaveSupportedFanMode/#"; + uic_mqtt_subscribe(subscription_topic.c_str(), &uic_mqtt_dotdot_on_unify_fan_control_z_wave_supported_fan_mode_attribute_update); + } + if(uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_state_callback) { + subscription_topic = base_topic + "UnifyFanControl/Attributes/ZWaveFanState/#"; + uic_mqtt_subscribe(subscription_topic.c_str(), &uic_mqtt_dotdot_on_unify_fan_control_z_wave_fan_state_attribute_update); + } + + return SL_STATUS_OK; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Callback setters and getters for UnifyFanControl +/////////////////////////////////////////////////////////////////////////////// +void uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_mode_callback_set(const uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_mode_callback_t callback) +{ + uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_mode_callback = callback; +} +void uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_supported_fan_mode_callback_set(const uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_supported_fan_mode_callback_t callback) +{ + uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_supported_fan_mode_callback = callback; +} +void uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_state_callback_set(const uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_state_callback_t callback) +{ + uic_mqtt_dotdot_unify_fan_control_attribute_z_wave_fan_state_callback = callback; +} + +// End of supported cluster. + diff --git a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_command_helpers.cpp b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_command_helpers.cpp index 0ee153812b..cd2b1a3405 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 @@ -14799,3 +14799,107 @@ void uic_mqtt_dotdot_parse_descriptor_write_attributes( } + +std::string get_json_payload_for_unify_fan_control_set_fan_mode_command( + + const uic_mqtt_dotdot_unify_fan_control_command_set_fan_mode_fields_t *fields + +){ + bool command_with_no_fields = true; + + // Create a JSON payload from all the parameters + nlohmann::json json_payload; + command_with_no_fields = false; + // Single Value + // Enum SetFanMode / FanMode + #ifdef SET_FAN_MODE_FAN_MODE_ENUM_NAME_AVAILABLE + // Pick up the name from the value. + json_payload["FanMode"] = + set_fan_mode_fan_mode_get_enum_value_name( + (uint32_t)fields->fan_mode); + #elif defined(Z_WAVE_FAN_MODE_ENUM_ENUM_NAME_AVAILABLE) + json_payload["FanMode"] = + z_wave_fan_mode_enum_get_enum_value_name((uint32_t)fields->fan_mode); + #else + // If there is no name value for the enum, just write it directly. + json_payload["FanMode"] = fields->fan_mode; + #endif + + // Get the string + if (command_with_no_fields == true) { + return std::string("{}"); + } + // Payload may contain data from end nodes, which we cannot control, thus we handle if there are non-utf8 characters + return json_payload.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); +} + + +void uic_mqtt_dotdot_parse_unify_fan_control_set_fan_mode( + nlohmann::json &jsn, + ZWaveFanModeEnum &fan_mode + +) { + + uint32_t FanMode_enum_val = get_enum_decimal_value("FanMode", jsn); + if (FanMode_enum_val == std::numeric_limits::max()) { + #ifdef Z_WAVE_FAN_MODE_ENUM_ENUM_NAME_AVAILABLE + FanMode_enum_val = z_wave_fan_mode_enum_get_enum_value_number(jsn.at("FanMode").get()); + #endif + } + if (jsn.at("FanMode").is_null()) { + sl_log_debug(LOG_TAG, "Ignoring JSON Null object"); + return; + } + fan_mode = static_cast(FanMode_enum_val); +} + + +std::string get_json_payload_for_unify_fan_control_turn_off_command( + +){ + bool command_with_no_fields = true; + + // Create a JSON payload from all the parameters + nlohmann::json json_payload; + + // Get the string + if (command_with_no_fields == true) { + return std::string("{}"); + } + // Payload may contain data from end nodes, which we cannot control, thus we handle if there are non-utf8 characters + return json_payload.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); +} + + + + +/** + * @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_fan_control_write_attributes( + nlohmann::json &jsn, + uic_mqtt_dotdot_unify_fan_control_state_t &new_state, + uic_mqtt_dotdot_unify_fan_control_updated_state_t &new_updated_state +) { + + + if (jsn.find("ZWaveFanMode") != jsn.end()) { + + uint32_t tmp = get_enum_decimal_value("ZWaveFanMode", jsn); + if (tmp == std::numeric_limits::max()) { + #ifdef UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE_ENUM_NAME_AVAILABLE + tmp = unify_fan_control_z_wave_fan_mode_get_enum_value_number(jsn.at("ZWaveFanMode").get()); + #elif defined(Z_WAVE_FAN_MODE_ENUM_NAME_AVAILABLE) + tmp = z_wave_fan_mode_get_enum_value_number(jsn.at("ZWaveFanMode").get()); + #endif + } + new_state.z_wave_fan_mode = tmp; + + new_updated_state.z_wave_fan_mode = true; + } + + +} + 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 21f137181d..e178d69892 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 @@ -6071,6 +6071,59 @@ void uic_mqtt_dotdot_parse_descriptor_write_attributes( ); +/** + * @brief Private helper function that will create a JSON string based on the + * fields of a UnifyFanControl SetFanMode command + * + * @param fields Struct pointer with the list of fields for the command + * + * @returns std::string that contains JSON payload + */ +std::string get_json_payload_for_unify_fan_control_set_fan_mode_command( + + const uic_mqtt_dotdot_unify_fan_control_command_set_fan_mode_fields_t *fields + +); + + +/** + * @brief JSON parser for UnifyFanControl SetFanMode command arguments. + * + * Parse incoming JSON object to populate command arguments passed in by reference. + */ +void uic_mqtt_dotdot_parse_unify_fan_control_set_fan_mode( + nlohmann::json &jsn, + ZWaveFanModeEnum &fan_mode + +); + + + +/** + * @brief Private helper function that will create a JSON string based on the + * fields of a UnifyFanControl TurnOff command + * + * @returns std::string that contains JSON payload + */ +std::string get_json_payload_for_unify_fan_control_turn_off_command( + +); + + + + +/** + * @brief JSON parser for UnifyFanControl WriteAttributes command arguments. + * + * Parse incoming JSON object to populate command arguments passed in by reference. + */ +void uic_mqtt_dotdot_parse_unify_fan_control_write_attributes( + nlohmann::json &jsn, + uic_mqtt_dotdot_unify_fan_control_state_t &new_state, + uic_mqtt_dotdot_unify_fan_control_updated_state_t &new_updated_state +); + + #endif //DOTDOT_MQTT_COMMAND_HELPERS_HPP /** @} end dotdot_mqtt_command_helpers */ diff --git a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_generated_commands.cpp b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_generated_commands.cpp index a09f1a69cf..256b5595b6 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 @@ -11396,3 +11396,125 @@ void uic_mqtt_dotdot_descriptor_publish_generated_write_attributes_command( false); } +/** + * @brief Publishes an incoming/generated SetFanMode command for + * the UnifyFanControl cluster. + * + * Publication will be made at the following topic + * ucl/by-unid/UNID/epID/UnifyFanControl/GeneratedCommands/SetFanMode + * + * @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 fields Struct pointer with the fields value of the command + * + */ +void uic_mqtt_dotdot_unify_fan_control_publish_generated_set_fan_mode_command( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint, + const uic_mqtt_dotdot_unify_fan_control_command_set_fan_mode_fields_t *fields + +) { + // Create the topic + std::string topic = "ucl/by-unid/"+ std::string(unid) + "/ep" + + std::to_string(endpoint) + "/"; + topic += "UnifyFanControl/GeneratedCommands/SetFanMode"; + + std::string payload = + get_json_payload_for_unify_fan_control_set_fan_mode_command( + fields); + + // Publish our command + uic_mqtt_publish(topic.c_str(), + payload.c_str(), + payload.size(), + false); +} +/** + * @brief Publishes an incoming/generated TurnOff command for + * the UnifyFanControl cluster. + * + * Publication will be made at the following topic + * ucl/by-unid/UNID/epID/UnifyFanControl/GeneratedCommands/TurnOff + * + * @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. + * + * + */ +void uic_mqtt_dotdot_unify_fan_control_publish_generated_turn_off_command( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint +) { + // Create the topic + std::string topic = "ucl/by-unid/"+ std::string(unid) + "/ep" + + std::to_string(endpoint) + "/"; + topic += "UnifyFanControl/GeneratedCommands/TurnOff"; + + std::string payload = + get_json_payload_for_unify_fan_control_turn_off_command( + ); + + // Publish our command + uic_mqtt_publish(topic.c_str(), + payload.c_str(), + payload.size(), + false); +} + + +/** + * @brief Publishes an incoming/generated WriteAttributes command for + * the UnifyFanControl cluster. + * + * Publication will be made at the following topic + * ucl/by-unid/UNID/epID/UnifyFanControl/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_fan_control_publish_generated_write_attributes_command( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint, + uic_mqtt_dotdot_unify_fan_control_state_t attribute_values, + uic_mqtt_dotdot_unify_fan_control_updated_state_t attribute_list +){ + // Create the topic + std::string topic = "ucl/by-unid/"+ std::string(unid) + "/ep" + + std::to_string(endpoint) + "/"; + topic += "UnifyFanControl/GeneratedCommands/WriteAttributes"; + + nlohmann::json json_object = nlohmann::json::object(); + + + if (attribute_list.z_wave_fan_mode == true) { + + // This is a single value + + #ifdef UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE_ENUM_NAME_AVAILABLE + json_object["ZWaveFanMode"] = unify_fan_control_z_wave_fan_mode_get_enum_value_name((uint32_t)attribute_values.z_wave_fan_mode); + #else + json_object["ZWaveFanMode"] = static_cast(attribute_values.z_wave_fan_mode); + #endif + + + } + + + // Payload contains data from end nodes, which we cannot control, thus we handle if there are non-utf8 characters + std::string payload = json_object.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace); + + // Publish our command + uic_mqtt_publish(topic.c_str(), + payload.c_str(), + payload.size(), + false); +} + diff --git a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_group_commands.cpp b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_group_commands.cpp index 5d85d166d8..edc42989b9 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 @@ -366,6 +366,11 @@ static uic_mqtt_dotdot_by_group_aox_position_estimation_write_attributes_callbac static uic_mqtt_dotdot_by_group_descriptor_write_attributes_callback_t uic_mqtt_dotdot_by_group_descriptor_write_attributes_callback = nullptr; +static uic_mqtt_dotdot_by_group_unify_fan_control_set_fan_mode_callback_t uic_mqtt_dotdot_by_group_unify_fan_control_set_fan_mode_callback = nullptr; +static uic_mqtt_dotdot_by_group_unify_fan_control_turn_off_callback_t uic_mqtt_dotdot_by_group_unify_fan_control_turn_off_callback = nullptr; +static uic_mqtt_dotdot_by_group_unify_fan_control_write_attributes_callback_t uic_mqtt_dotdot_by_group_unify_fan_control_write_attributes_callback = nullptr; + + // Callbacks setters @@ -1876,6 +1881,27 @@ void uic_mqtt_dotdot_by_group_descriptor_write_attributes_callback_set( +// Callbacks setters + +void uic_mqtt_dotdot_by_group_unify_fan_control_set_fan_mode_callback_set(const uic_mqtt_dotdot_by_group_unify_fan_control_set_fan_mode_callback_t callback) +{ + uic_mqtt_dotdot_by_group_unify_fan_control_set_fan_mode_callback = callback; +} + + +void uic_mqtt_dotdot_by_group_unify_fan_control_turn_off_callback_set(const uic_mqtt_dotdot_by_group_unify_fan_control_turn_off_callback_t callback) +{ + uic_mqtt_dotdot_by_group_unify_fan_control_turn_off_callback = callback; +} + +void uic_mqtt_dotdot_by_group_unify_fan_control_write_attributes_callback_set( + const uic_mqtt_dotdot_by_group_unify_fan_control_write_attributes_callback_t callback) +{ + uic_mqtt_dotdot_by_group_unify_fan_control_write_attributes_callback = callback; +} + + + // Callback function for incoming publications on ucl/by-group/+/Basic/Commands/ResetToFactoryDefaults static void uic_mqtt_dotdot_on_by_group_basic_reset_to_factory_defaults( @@ -23492,6 +23518,258 @@ sl_status_t uic_mqtt_dotdot_by_group_descriptor_init() + +// Callback function for incoming publications on ucl/by-group/+/UnifyFanControl/Commands/SetFanMode +static void uic_mqtt_dotdot_on_by_group_unify_fan_control_set_fan_mode( + const char *topic, + const char *message, + const size_t message_length) +{ + if ((group_dispatch_callback == nullptr) && (uic_mqtt_dotdot_by_group_unify_fan_control_set_fan_mode_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; + } + + // Pass to command-specific callback if set. Otherwise, pass to + // group-dispatch callback + if (uic_mqtt_dotdot_by_group_unify_fan_control_set_fan_mode_callback != nullptr) { + + + uic_mqtt_dotdot_unify_fan_control_command_set_fan_mode_fields_t fields; + + + nlohmann::json jsn; + try { + jsn = nlohmann::json::parse(std::string(message)); + + + uic_mqtt_dotdot_parse_unify_fan_control_set_fan_mode( + jsn, + fields.fan_mode + ); + + // Populate list fields from vector or string types + + + } catch (const nlohmann::json::parse_error& e) { + // Catch JSON object field parsing errors + sl_log_debug(LOG_TAG, LOG_FMT_JSON_PARSE_FAIL, "UnifyFanControl", "SetFanMode"); + return; + } catch (const nlohmann::json::exception& e) { + // Catch JSON object field parsing errors + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl", "SetFanMode", e.what()); + return; + } catch (const std::exception& e) { + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl", "SetFanMode", ""); + return; + } + + uic_mqtt_dotdot_by_group_unify_fan_control_set_fan_mode_callback( + group_id, + &fields + ); + } else if ((group_dispatch_callback != nullptr) && (!get_uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback().empty())) { + // group-dispatch callback only called if the command-specific by-unid + // callback is set + try { + nlohmann::json jsn = nlohmann::json::parse(std::string(message)); + if (jsn.find("FanMode") == jsn.end()) { + sl_log_debug(LOG_TAG, "UnifyFanControl::SetFanMode: Missing command-argument: FanMode\n"); + return; + } + + group_dispatch_callback( + group_id, + "UnifyFanControl", + "SetFanMode", + message, + message_length, + uic_mqtt_dotdot_on_unify_fan_control_set_fan_mode); + + } catch (...) { + sl_log_debug(LOG_TAG, "SetFanMode: Unable to parse JSON payload.\n"); + return; + } + } + +} + +// Callback function for incoming publications on ucl/by-group/+/UnifyFanControl/Commands/TurnOff +static void uic_mqtt_dotdot_on_by_group_unify_fan_control_turn_off( + const char *topic, + const char *message, + const size_t message_length) +{ + if ((group_dispatch_callback == nullptr) && (uic_mqtt_dotdot_by_group_unify_fan_control_turn_off_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; + } + + // Pass to command-specific callback if set. Otherwise, pass to + // group-dispatch callback + if (uic_mqtt_dotdot_by_group_unify_fan_control_turn_off_callback != nullptr) { + + + + nlohmann::json jsn; + try { + jsn = nlohmann::json::parse(std::string(message)); + + + + // Populate list fields from vector or string types + + + } catch (const nlohmann::json::parse_error& e) { + // Catch JSON object field parsing errors + sl_log_debug(LOG_TAG, LOG_FMT_JSON_PARSE_FAIL, "UnifyFanControl", "TurnOff"); + return; + } catch (const nlohmann::json::exception& e) { + // Catch JSON object field parsing errors + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl", "TurnOff", e.what()); + return; + } catch (const std::exception& e) { + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl", "TurnOff", ""); + return; + } + + uic_mqtt_dotdot_by_group_unify_fan_control_turn_off_callback( + group_id + ); + } else if ((group_dispatch_callback != nullptr) && (!get_uic_mqtt_dotdot_unify_fan_control_turn_off_callback().empty())) { + // group-dispatch callback only called if the command-specific by-unid + // callback is set + try { + nlohmann::json jsn = nlohmann::json::parse(std::string(message)); + + group_dispatch_callback( + group_id, + "UnifyFanControl", + "TurnOff", + message, + message_length, + uic_mqtt_dotdot_on_unify_fan_control_turn_off); + + } catch (...) { + sl_log_debug(LOG_TAG, "TurnOff: Unable to parse JSON payload.\n"); + return; + } + } + +} + +static void uic_mqtt_dotdot_on_by_group_unify_fan_control_WriteAttributes( + const char *topic, + const char *message, + const size_t message_length) +{ + + if ((group_dispatch_callback == nullptr) && (uic_mqtt_dotdot_by_group_unify_fan_control_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_fan_control_write_attributes_callback().empty())) { + try { + group_dispatch_callback(group_id, + "UnifyFanControl", + "WriteAttributes", + message, + message_length, + uic_mqtt_dotdot_on_unify_fan_control_WriteAttributes); + + } catch (...) { + sl_log_debug(LOG_TAG, "UnifyFanControl: Unable to parse JSON payload.\n"); + return; + } + } else if (uic_mqtt_dotdot_by_group_unify_fan_control_write_attributes_callback != nullptr) { + + uic_mqtt_dotdot_unify_fan_control_state_t new_state = {}; + uic_mqtt_dotdot_unify_fan_control_updated_state_t new_updated_state = {}; + + + nlohmann::json jsn; + try { + jsn = nlohmann::json::parse(std::string(message)); + + uic_mqtt_dotdot_parse_unify_fan_control_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, "UnifyFanControl", "WriteAttributes"); + return; + } catch (const nlohmann::json::exception& e) { + // Catch JSON object field parsing errors + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl", "WriteAttributes", e.what()); + return; + } catch (const std::exception& e) { + sl_log_debug(LOG_TAG, LOG_FMT_JSON_ERROR, "UnifyFanControl", "WriteAttributes", ""); + return; + } + + uic_mqtt_dotdot_by_group_unify_fan_control_write_attributes_callback( + group_id, + new_state, + new_updated_state + ); + } +} + +sl_status_t uic_mqtt_dotdot_by_group_unify_fan_control_init() +{ + std::string subscription_topic; + const std::string topic_bygroup = TOPIC_BY_GROUP_PREFIX; + if(uic_mqtt_dotdot_by_group_unify_fan_control_write_attributes_callback) { + subscription_topic = topic_bygroup + "UnifyFanControl/Commands/WriteAttributes"; + uic_mqtt_subscribe(subscription_topic.c_str(), uic_mqtt_dotdot_on_by_group_unify_fan_control_WriteAttributes); + } + if (uic_mqtt_dotdot_by_group_unify_fan_control_set_fan_mode_callback) { + subscription_topic = topic_bygroup + "UnifyFanControl/Commands/SetFanMode"; + uic_mqtt_subscribe(subscription_topic.c_str(), uic_mqtt_dotdot_on_by_group_unify_fan_control_set_fan_mode); + } + if (uic_mqtt_dotdot_by_group_unify_fan_control_turn_off_callback) { + subscription_topic = topic_bygroup + "UnifyFanControl/Commands/TurnOff"; + uic_mqtt_subscribe(subscription_topic.c_str(), uic_mqtt_dotdot_on_by_group_unify_fan_control_turn_off); + } + + return SL_STATUS_OK; +} + + + void uic_mqtt_dotdot_set_group_dispatch_callback(group_dispatch_t callback) { // Check for uninitialized value in order to subscribe with on_group handlers @@ -23773,6 +24051,10 @@ void uic_mqtt_dotdot_set_group_dispatch_callback(group_dispatch_t callback) uic_mqtt_subscribe("ucl/by-group/+/Descriptor/Commands/WriteAttributes", uic_mqtt_dotdot_on_by_group_descriptor_WriteAttributes); + uic_mqtt_subscribe("ucl/by-group/+/UnifyFanControl/Commands/WriteAttributes", uic_mqtt_dotdot_on_by_group_unify_fan_control_WriteAttributes); + uic_mqtt_subscribe("ucl/by-group/+/UnifyFanControl/Commands/SetFanMode", uic_mqtt_dotdot_on_by_group_unify_fan_control_set_fan_mode); + uic_mqtt_subscribe("ucl/by-group/+/UnifyFanControl/Commands/TurnOff", uic_mqtt_dotdot_on_by_group_unify_fan_control_turn_off); + } group_dispatch_callback = callback; diff --git a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_helpers.cpp b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_helpers.cpp index e777d585ab..9c86f7c45d 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 @@ -5117,6 +5117,114 @@ uint32_t window_covering_window_covering_type_get_enum_value_number(const std::s return std::numeric_limits::max(); } +// Enum to string map for ZWaveFanModeEnum +const std::map z_wave_fan_mode_enum_enum_id_to_string_map { + { 0, "Auto" }, + { 1, "Low" }, + { 2, "AutoHigh" }, + { 3, "High" }, + { 4, "AutoMedium" }, + { 5, "Medium" }, + { 6, "Circulation" }, + { 7, "HumidityCirculation" }, + { 8, "LeftRight" }, + { 9, "UpDown" }, + { 10, "Quiet" }, + { 11, "ExternalCirculation" }, +}; + +// String to enum map for ZWaveFanModeEnum +const std::map z_wave_fan_mode_enum_enum_string_to_id_map { + { "Auto", 0 }, + { "Low", 1 }, + { "AutoHigh", 2 }, + { "High", 3 }, + { "AutoMedium", 4 }, + { "Medium", 5 }, + { "Circulation", 6 }, + { "HumidityCirculation", 7 }, + { "LeftRight", 8 }, + { "UpDown", 9 }, + { "Quiet", 10 }, + { "ExternalCirculation", 11 }, +}; + +std::string z_wave_fan_mode_enum_get_enum_value_name( + uint32_t value) +{ + auto it = z_wave_fan_mode_enum_enum_id_to_string_map.find(value); + if (it != z_wave_fan_mode_enum_enum_id_to_string_map.end()){ + return it->second; + } + + // No known name value is set for this field. + // Set it to a string version of the value. + return std::to_string(value); +} + +uint32_t z_wave_fan_mode_enum_get_enum_value_number(const std::string &str) +{ + auto it = z_wave_fan_mode_enum_enum_string_to_id_map.find(str); + if (it != z_wave_fan_mode_enum_enum_string_to_id_map.end()){ + return it->second; + } + + // No known numeric value is set for this string. + // Return UINT32_MAX to indicate an error. + return std::numeric_limits::max(); +} + +// Enum to string map for ZWaveFanStateEnum +const std::map z_wave_fan_state_enum_enum_id_to_string_map { + { 0, "Idle" }, + { 1, "Running" }, + { 2, "RunningHigh" }, + { 3, "RunningMedium" }, + { 4, "Circulation" }, + { 5, "HumidityCirculation" }, + { 6, "RightLeftCirculation" }, + { 7, "UpDownCirculation" }, + { 8, "QuietCirculation" }, +}; + +// String to enum map for ZWaveFanStateEnum +const std::map z_wave_fan_state_enum_enum_string_to_id_map { + { "Idle", 0 }, + { "Running", 1 }, + { "RunningHigh", 2 }, + { "RunningMedium", 3 }, + { "Circulation", 4 }, + { "HumidityCirculation", 5 }, + { "RightLeftCirculation", 6 }, + { "UpDownCirculation", 7 }, + { "QuietCirculation", 8 }, +}; + +std::string z_wave_fan_state_enum_get_enum_value_name( + uint32_t value) +{ + auto it = z_wave_fan_state_enum_enum_id_to_string_map.find(value); + if (it != z_wave_fan_state_enum_enum_id_to_string_map.end()){ + return it->second; + } + + // No known name value is set for this field. + // Set it to a string version of the value. + return std::to_string(value); +} + +uint32_t z_wave_fan_state_enum_get_enum_value_number(const std::string &str) +{ + auto it = z_wave_fan_state_enum_enum_string_to_id_map.find(str); + if (it != z_wave_fan_state_enum_enum_string_to_id_map.end()){ + return it->second; + } + + // No known numeric value is set for this string. + // Return UINT32_MAX to indicate an error. + return std::numeric_limits::max(); +} + // Enum to string map for ZoneEnrollResponseEnrollResponseCode const std::map zone_enroll_response_enroll_response_code_enum_id_to_string_map { { 0, "Success" }, @@ -9908,6 +10016,27 @@ std::string get_enum_value_name( #endif } + if (64788 == cluster_id) { + #ifdef UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE_ENUM_NAME_AVAILABLE + if (1 == attribute_id) { + // FIXME: Some attributes don't work because multi-upper case names end up like this: unify_fan_controlz_wave_fan_mode instead of this: unify_fan_control_z_wave_fan_mode + return unify_fan_control_z_wave_fan_mode_get_enum_value_name(value); + } + #endif + #ifdef UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_ENUM_NAME_AVAILABLE + if (2 == attribute_id) { + // FIXME: Some attributes don't work because multi-upper case names end up like this: unify_fan_controlz_wave_supported_fan_mode instead of this: unify_fan_control_z_wave_supported_fan_mode + return unify_fan_control_z_wave_supported_fan_mode_get_enum_value_name(value); + } + #endif + #ifdef UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE_ENUM_NAME_AVAILABLE + if (3 == attribute_id) { + // FIXME: Some attributes don't work because multi-upper case names end up like this: unify_fan_controlz_wave_fan_state instead of this: unify_fan_control_z_wave_fan_state + return unify_fan_control_z_wave_fan_state_get_enum_value_name(value); + } + #endif + } + std::string value_name; return value_name; @@ -14376,6 +14505,27 @@ uint32_t get_enum_name_value( #endif } + if (64788 == cluster_id) { + #ifdef UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE_ENUM_NAME_AVAILABLE + if (1 == attribute_id) { + // FIXME: Some attributes don't work because multi-upper case names end up like this: unify_fan_controlz_wave_fan_mode instead of this: unify_fan_control_z_wave_fan_mode + return unify_fan_control_z_wave_fan_mode_get_enum_value_number(name); + } + #endif + #ifdef UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE_ENUM_NAME_AVAILABLE + if (2 == attribute_id) { + // FIXME: Some attributes don't work because multi-upper case names end up like this: unify_fan_controlz_wave_supported_fan_mode instead of this: unify_fan_control_z_wave_supported_fan_mode + return unify_fan_control_z_wave_supported_fan_mode_get_enum_value_number(name); + } + #endif + #ifdef UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE_ENUM_NAME_AVAILABLE + if (3 == attribute_id) { + // FIXME: Some attributes don't work because multi-upper case names end up like this: unify_fan_controlz_wave_fan_state instead of this: unify_fan_control_z_wave_fan_state + return unify_fan_control_z_wave_fan_state_get_enum_value_number(name); + } + #endif + } + // No known numeric value is set for this string. // Return UINT32_MAX to indicate an error. @@ -15627,6 +15777,28 @@ uint32_t window_covering_window_covering_type_get_enum_value_number_c(const char { return window_covering_window_covering_type_get_enum_value_number(std::string(str)); } +char *z_wave_fan_mode_enum_get_enum_value_name_c( + uint32_t value, char *result, size_t max_result_size) +{ + snprintf(result, max_result_size, "%s", z_wave_fan_mode_enum_get_enum_value_name(value).c_str()); + return result; +} + +uint32_t z_wave_fan_mode_enum_get_enum_value_number_c(const char *str) +{ + return z_wave_fan_mode_enum_get_enum_value_number(std::string(str)); +} +char *z_wave_fan_state_enum_get_enum_value_name_c( + uint32_t value, char *result, size_t max_result_size) +{ + snprintf(result, max_result_size, "%s", z_wave_fan_state_enum_get_enum_value_name(value).c_str()); + return result; +} + +uint32_t z_wave_fan_state_enum_get_enum_value_number_c(const char *str) +{ + return z_wave_fan_state_enum_get_enum_value_number(std::string(str)); +} char *zone_enroll_response_enroll_response_code_get_enum_value_name_c( uint32_t value, char *result, size_t max_result_size) { diff --git a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_send_commands.cpp b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_send_commands.cpp index 92d7b490bd..bb9efe9c3e 100644 --- a/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_send_commands.cpp +++ b/components/uic_dotdot_mqtt/zap-generated/src/dotdot_mqtt_send_commands.cpp @@ -13727,3 +13727,143 @@ void uic_mqtt_dotdot_protocol_controller_network_management_publish_write_comman payload.size(), false); } + +/** + * @brief Sends/Publishes a SetFanMode command for + * the UnifyFanControl cluster to a destination. + * + * Publication will be made at the following topic + * ucl/by-unid/UNID/epID/UnifyFanControl/Commands/SetFanMode + * + * @param destination_unid The UNID of the node that should receive the command. + * + * @param destination_endpoint The Endpoint ID of the node that should receive the command. + * + * + * @param fields Struct pointer with the fields value of the command + * + */ +void uic_mqtt_dotdot_unify_fan_control_publish_set_fan_mode_command( + const dotdot_unid_t destination_unid, + const dotdot_endpoint_id_t destination_endpoint, + const uic_mqtt_dotdot_unify_fan_control_command_set_fan_mode_fields_t *fields + +) { + // Create the topic + std::string topic = "ucl/by-unid/"+ std::string(destination_unid) + "/ep" + + std::to_string(destination_endpoint) + "/"; + topic += "UnifyFanControl/Commands/SetFanMode"; + + + std::string payload = + get_json_payload_for_unify_fan_control_set_fan_mode_command( + fields); + + sl_log_debug(LOG_TAG, "Sending command to %s with payload %s ---", topic.c_str() , payload.c_str()); + + // Publish our command, not retained + uic_mqtt_publish(topic.c_str(), + payload.c_str(), + payload.size(), + false); +} + +/** + * @brief Sends/Publishes a SetFanMode command for + * the UnifyFanControl cluster to a group. + * + * Publication will be made at the following topic + * ucl/by-group/GroupID/UnifyFanControl/Commands/SetFanMode + * + * @param destination_group_id The GroupID that should receive the command. + * + * @param fields Struct pointer with the fields value of the command + * + */ +void uic_mqtt_dotdot_unify_fan_control_publish_set_fan_mode_command_to_group( + uint16_t destination_group_id, + const uic_mqtt_dotdot_unify_fan_control_command_set_fan_mode_fields_t *fields + +){ + // Create the topic + std::string topic = "ucl/by-group/"+ std::to_string(destination_group_id) + + "/UnifyFanControl/Commands/SetFanMode"; + + std::string payload = + get_json_payload_for_unify_fan_control_set_fan_mode_command( + fields); + + sl_log_info(LOG_TAG, "Sending group command to %s with payload %s ---", topic.c_str() , payload.c_str()); + + // Publish our command, not retained + uic_mqtt_publish(topic.c_str(), + payload.c_str(), + payload.size(), + false); +} + +/** + * @brief Sends/Publishes a TurnOff command for + * the UnifyFanControl cluster to a destination. + * + * Publication will be made at the following topic + * ucl/by-unid/UNID/epID/UnifyFanControl/Commands/TurnOff + * + * @param destination_unid The UNID of the node that should receive the command. + * + * @param destination_endpoint The Endpoint ID of the node that should receive the command. + * + * + */ +void uic_mqtt_dotdot_unify_fan_control_publish_turn_off_command( + const dotdot_unid_t destination_unid, + const dotdot_endpoint_id_t destination_endpoint +) { + // Create the topic + std::string topic = "ucl/by-unid/"+ std::string(destination_unid) + "/ep" + + std::to_string(destination_endpoint) + "/"; + topic += "UnifyFanControl/Commands/TurnOff"; + + + std::string payload = + get_json_payload_for_unify_fan_control_turn_off_command( + ); + + sl_log_debug(LOG_TAG, "Sending command to %s with payload %s ---", topic.c_str() , payload.c_str()); + + // Publish our command, not retained + uic_mqtt_publish(topic.c_str(), + payload.c_str(), + payload.size(), + false); +} + +/** + * @brief Sends/Publishes a TurnOff command for + * the UnifyFanControl cluster to a group. + * + * Publication will be made at the following topic + * ucl/by-group/GroupID/UnifyFanControl/Commands/TurnOff + * + * @param destination_group_id The GroupID that should receive the command. + * + */ +void uic_mqtt_dotdot_unify_fan_control_publish_turn_off_command_to_group( + uint16_t destination_group_id +){ + // Create the topic + std::string topic = "ucl/by-group/"+ std::to_string(destination_group_id) + + "/UnifyFanControl/Commands/TurnOff"; + + std::string payload = + get_json_payload_for_unify_fan_control_turn_off_command( + ); + + sl_log_info(LOG_TAG, "Sending group command to %s with payload %s ---", topic.c_str() , payload.c_str()); + + // Publish our command, not retained + uic_mqtt_publish(topic.c_str(), + payload.c_str(), + payload.size(), + false); +} 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 a2d5d754d8..991b6fbcda 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 @@ -2812,3 +2812,52 @@ void uic_mqtt_dotdot_descriptor_publish_supported_generated_commands( } + +/** + * @brief Sends/Publishes a the SupportedGenerated commands for + * the UnifyFanControl cluster for a UNID/Endpoint + * + * Publication will be made at the following topic + * ucl/by-unid/UNID/epID/UnifyFanControl/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_fan_control_publish_supported_generated_commands( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint, + const uic_mqtt_dotdot_unify_fan_control_supported_commands_t *command_list) +{ + std::string topic = "ucl/by-unid/" + std::string(unid); + topic += "/ep"+ std::to_string(endpoint); + topic += "/UnifyFanControl/SupportedGeneratedCommands"; + + // Assemble of vector of strings for the Supported Commands: + std::vector command_vector; + if (command_list->set_fan_mode == true) { + command_vector.emplace_back("SetFanMode"); + } + if (command_list->turn_off == true) { + command_vector.emplace_back("TurnOff"); + } + if (command_list->write_attributes == true) { + command_vector.emplace_back("WriteAttributes"); + } + + // JSONify, then Stringify + nlohmann::json json_payload; + json_payload["value"] = command_vector; + std::string string_payload = json_payload.dump(); + + // Publish to MQTT + uic_mqtt_publish(topic.c_str(), + string_payload.c_str(), + string_payload.length(), + true); + +} + diff --git a/components/uic_dotdot_mqtt/zap-generated/test/dotdot_mqtt_test.include b/components/uic_dotdot_mqtt/zap-generated/test/dotdot_mqtt_test.include index 4884cdd28a..49ba7ebbc8 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 @@ -3020,6 +3020,30 @@ static sl_status_t uic_mqtt_dotdot_protocol_controller_network_management_write_ return SL_STATUS_OK; } +static unsigned int uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_count; +static sl_status_t uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_func( + dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint, + uic_mqtt_dotdot_callback_call_type_t callback_type, + ZWaveFanModeEnum fan_mode + +) { + uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_count++; + num_command_callbacks++; + return SL_STATUS_OK; +} + +static unsigned int uic_mqtt_dotdot_unify_fan_control_turn_off_callback_count; +static sl_status_t uic_mqtt_dotdot_unify_fan_control_turn_off_callback_func( + dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint, + uic_mqtt_dotdot_callback_call_type_t callback_type +) { + uic_mqtt_dotdot_unify_fan_control_turn_off_callback_count++; + num_command_callbacks++; + return SL_STATUS_OK; +} + static unsigned int set_all_callbacks() { unsigned int num_callbacks = 0; @@ -3402,6 +3426,10 @@ static unsigned int set_all_callbacks() num_callbacks++; uic_mqtt_dotdot_protocol_controller_network_management_write_callback_set(&uic_mqtt_dotdot_protocol_controller_network_management_write_callback_func); num_callbacks++; + uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_set(&uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_func); + num_callbacks++; + uic_mqtt_dotdot_unify_fan_control_turn_off_callback_set(&uic_mqtt_dotdot_unify_fan_control_turn_off_callback_func); + num_callbacks++; return num_callbacks; } @@ -3649,6 +3677,9 @@ static void unset_all_callbacks() uic_mqtt_dotdot_protocol_controller_network_management_write_callback_clear(); uic_mqtt_dotdot_clear_protocol_controller_network_management_write_attributes_callbacks(); uic_mqtt_dotdot_clear_descriptor_write_attributes_callbacks(); + uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_clear(); + uic_mqtt_dotdot_unify_fan_control_turn_off_callback_clear(); + uic_mqtt_dotdot_clear_unify_fan_control_write_attributes_callbacks(); } static void reset_callback_counters() @@ -3843,4 +3874,6 @@ static void reset_callback_counters() uic_mqtt_dotdot_aox_locator_angle_report_callback_count = 0; uic_mqtt_dotdot_aox_locator_angle_correction_callback_count = 0; uic_mqtt_dotdot_protocol_controller_network_management_write_callback_count = 0; + uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_count = 0; + uic_mqtt_dotdot_unify_fan_control_turn_off_callback_count = 0; } diff --git a/components/unify_dotdot_attribute_store/CMakeLists.txt b/components/unify_dotdot_attribute_store/CMakeLists.txt index e1c467b42c..f3b8ffd833 100644 --- a/components/unify_dotdot_attribute_store/CMakeLists.txt +++ b/components/unify_dotdot_attribute_store/CMakeLists.txt @@ -15,6 +15,7 @@ add_library( src/unify_dotdot_attribute_store_command_callbacks_poll_control.c src/unify_dotdot_attribute_store_command_callbacks_thermostat.c src/unify_dotdot_attribute_store_command_callbacks_window_covering.c + src/unify_dotdot_attribute_store_command_callbacks_zwave_fan_control.c src/unify_dotdot_attribute_store_node_state.cpp src/unify_dotdot_attribute_store_group_cluster.cpp src/unify_dotdot_attribute_store_descriptor.cpp diff --git a/components/unify_dotdot_attribute_store/src/unify_dotdot_attribute_store_command_callbacks.c b/components/unify_dotdot_attribute_store/src/unify_dotdot_attribute_store_command_callbacks.c index d3af4f5ed3..36ae915a51 100644 --- a/components/unify_dotdot_attribute_store/src/unify_dotdot_attribute_store_command_callbacks.c +++ b/components/unify_dotdot_attribute_store/src/unify_dotdot_attribute_store_command_callbacks.c @@ -20,6 +20,7 @@ #include "unify_dotdot_attribute_store_command_callbacks_poll_control.h" #include "unify_dotdot_attribute_store_command_callbacks_thermostat.h" #include "unify_dotdot_attribute_store_command_callbacks_window_covering.h" +#include "unify_dotdot_attribute_store_command_callbacks_zwave_fan_control.h" #include "unify_dotdot_attribute_store_configuration.h" #include "unify_dotdot_attribute_store_helpers.h" #include "unify_dotdot_defined_attribute_types.h" @@ -45,5 +46,7 @@ sl_status_t unify_dotdot_attribute_store_command_callbacks_init() thermostat_cluster_mapper_init(); window_covering_cluster_mapper_init(); + zwave_fan_control_cluster_mapper_init(); + return SL_STATUS_OK; } diff --git a/components/unify_dotdot_attribute_store/src/unify_dotdot_attribute_store_command_callbacks_zwave_fan_control.c b/components/unify_dotdot_attribute_store/src/unify_dotdot_attribute_store_command_callbacks_zwave_fan_control.c new file mode 100644 index 0000000000..2290e2dcfd --- /dev/null +++ b/components/unify_dotdot_attribute_store/src/unify_dotdot_attribute_store_command_callbacks_zwave_fan_control.c @@ -0,0 +1,88 @@ + +/****************************************************************************** + * # 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. + * + *****************************************************************************/ +#include "unify_dotdot_attribute_store_command_callbacks_zwave_fan_control.h" + +// Other Unify components. +// uic_mqtt_dotdot_xxx_callback_set() +#include "dotdot_mqtt.h" +// dotdot_is_supported_xxx +#include "unify_dotdot_attribute_store_helpers.h" +// is_automatic_deduction_of_supported_commands_enabled +#include "unify_dotdot_attribute_store_configuration.h" +#include "sl_log.h" + +#define LOG_TAG \ + "unify_dotdot_attribute_store_command_callbacks_zwave_fan_control" + +sl_status_t + zwave_fan_control_set_fan_mode(dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint, + uic_mqtt_dotdot_callback_call_type_t call_type, + ZWaveFanModeEnum fan_mode) +{ + // First check the call type. If this is a support check support call, + // we check the attributes + if (call_type == UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK) { + // Check user option automatic_deduction_of_supported_commands + if (is_automatic_deduction_of_supported_commands_enabled()) { + return (dotdot_is_supported_unify_fan_control_z_wave_fan_mode(unid, endpoint)) + ? SL_STATUS_OK + : SL_STATUS_FAIL; + } else { + return SL_STATUS_FAIL; + } + } + + if (is_desired_value_update_on_commands_enabled()) { + sl_log_debug( + LOG_TAG, + "Updating ZCL desired values after ZWave_FanControl::SetFanMode command"); + sl_status_t result + = dotdot_set_unify_fan_control_z_wave_fan_mode(unid, + endpoint, + DESIRED_ATTRIBUTE, + fan_mode); + + if (result != SL_STATUS_OK) { + sl_log_warning(LOG_TAG, + "Can't set fan mode for ZWave Fan Control cluster"); + return SL_STATUS_FAIL; + } + + if (is_clear_reported_enabled()) { + sl_log_debug(LOG_TAG, "Clearing ZWave_FanControl reported values"); + dotdot_unify_fan_control_z_wave_fan_mode_undefine_reported(unid, endpoint); + } + + } else { + sl_log_debug( + LOG_TAG, + "No Updating ZCL desired values after ZWave_FanControl::SetFanMode " + "command since is_desired_value_update_on_commands_enabled() is off."); + } + + return SL_STATUS_OK; +} + +//////////////////////////////////////////////////////////////////////////////// +// Internal component function that register callbacks to DotDot MQTT +//////////////////////////////////////////////////////////////////////////////// +void zwave_fan_control_cluster_mapper_init(void) +{ + // Unify Sound Commands. + sl_log_debug(LOG_TAG, "ZWave Fan Control Cluster mapper initialization\n"); + + uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_set( + &zwave_fan_control_set_fan_mode); +} \ No newline at end of file diff --git a/components/unify_dotdot_attribute_store/src/unify_dotdot_attribute_store_command_callbacks_zwave_fan_control.h b/components/unify_dotdot_attribute_store/src/unify_dotdot_attribute_store_command_callbacks_zwave_fan_control.h new file mode 100644 index 0000000000..79276c1857 --- /dev/null +++ b/components/unify_dotdot_attribute_store/src/unify_dotdot_attribute_store_command_callbacks_zwave_fan_control.h @@ -0,0 +1,35 @@ +/****************************************************************************** + * # 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. + * + *****************************************************************************/ + +#ifndef ZWAVE_FAN_CONTROL_CLUSTER_MAPPER_H +#define ZWAVE_FAN_CONTROL_CLUSTER_MAPPER_H + +// Generic includes +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize the Zwave Fan Control cluster mapper + * + */ +void zwave_fan_control_cluster_mapper_init(void); + +#ifdef __cplusplus +} +#endif + +#endif //ZWAVE_FAN_CONTROL_CLUSTER_MAPPER_H +/** @} end unify_cluster_mapper */ \ No newline at end of file diff --git a/components/unify_dotdot_attribute_store/test/CMakeLists.txt b/components/unify_dotdot_attribute_store/test/CMakeLists.txt index f2ff20ede7..1d92c8357f 100644 --- a/components/unify_dotdot_attribute_store/test/CMakeLists.txt +++ b/components/unify_dotdot_attribute_store/test/CMakeLists.txt @@ -23,6 +23,7 @@ target_add_unittest( unify_dotdot_attribute_store_command_mapper_poll_control_test.c unify_dotdot_attribute_store_command_mapper_thermostat_test.c unify_dotdot_attribute_store_command_mapper_window_covering_test.c + unify_dotdot_attribute_store_command_mapper_zwave_fan_control_test.c DEPENDS uic_dotdot_mqtt_mock diff --git a/components/unify_dotdot_attribute_store/test/unify_dotdot_attribute_store_command_mapper_zwave_fan_control_test.c b/components/unify_dotdot_attribute_store/test/unify_dotdot_attribute_store_command_mapper_zwave_fan_control_test.c new file mode 100644 index 0000000000..8380e4e8f8 --- /dev/null +++ b/components/unify_dotdot_attribute_store/test/unify_dotdot_attribute_store_command_mapper_zwave_fan_control_test.c @@ -0,0 +1,229 @@ +/****************************************************************************** + * # License + * Copyright 2022 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. + * + *****************************************************************************/ +// Test includes +#include "unity.h" +#include "unify_dotdot_attribute_store_test.h" + +// Unify components +#include "datastore.h" +#include "attribute_store_fixt.h" +#include "attribute_store_helper.h" +#include "sl_log.h" +// Mock includes +#include "dotdot_mqtt_mock.h" +#include "uic_mqtt_mock.h" + +// Generic includes +#include + +#include "unify_dotdot_attribute_store.h" +#include "unify_dotdot_attribute_store_helpers.h" +#include "unify_dotdot_defined_attribute_types.h" + +// Things from unify_dotdot_attribute_store_test_c.zapt +// (unify_dotdot_attribute_store_test.c) + +uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_t + uic_mqtt_dotdot_fan_control_set_fan_mode_callback; + +#define TEST_UNID "test-unid-123" +extern dotdot_unid_t expected_unid; +extern dotdot_endpoint_id_t expected_endpoint_id; +extern attribute_store_node_t expected_node_id; +extern unify_dotdot_attribute_store_configuration_t test_configuration; +attribute_store_node_t test_get_endpoint_node(const dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint_id); +sl_status_t test_get_unid_endpoint(attribute_store_node_t node, + char *unid, + dotdot_endpoint_id_t *endpoint_id); +//----- + +void test_zwave_fan_control_no_get_endpoint_function_registered() +{ + unify_dotdot_attribute_store_set_configuration(NULL); + + // Default value for support check is fail: + uic_mqtt_dotdot_fan_control_set_fan_mode_callback + = get_uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback(); + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, + uic_mqtt_dotdot_fan_control_set_fan_mode_callback( + expected_unid, + expected_endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK, + ZCL_Z_WAVE_FAN_MODE_ENUM_AUTO_HIGH)); + + // Default value for normal call is OK: + TEST_ASSERT_EQUAL(SL_STATUS_OK, + uic_mqtt_dotdot_fan_control_set_fan_mode_callback( + expected_unid, + expected_endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL, + ZCL_Z_WAVE_FAN_MODE_ENUM_AUTO_HIGH)); +} + +void test_zwave_fan_control_set_fan_mode_command_support() +{ + // Configure auto-supported command setup + test_configuration.get_endpoint_node_function = &test_get_endpoint_node; + test_configuration.automatic_deduction_of_supported_commands = true; + unify_dotdot_attribute_store_set_configuration(&test_configuration); + + // Check that the dispatch exists. + uic_mqtt_dotdot_fan_control_set_fan_mode_callback + = get_uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback(); + TEST_ASSERT_NOT_NULL(uic_mqtt_dotdot_fan_control_set_fan_mode_callback); + + // First test the support (not supported without attributes) + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, + uic_mqtt_dotdot_fan_control_set_fan_mode_callback( + expected_unid, + expected_endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK, + ZCL_Z_WAVE_FAN_MODE_ENUM_AUTO_HIGH)); + + // Add the DOTDOT_ATTRIBUTE_ID_FAN_CONTROL_Z_WAVE_FAN_MODE_ENUM attribute + attribute_store_add_node( + DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE, + attribute_store_get_root()); + + // Now it is supported + TEST_ASSERT_EQUAL(SL_STATUS_OK, + uic_mqtt_dotdot_fan_control_set_fan_mode_callback( + expected_unid, + expected_endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK, + ZCL_Z_WAVE_FAN_MODE_ENUM_AUTO_HIGH)); + + // Configure for no SupportedCommand support + test_configuration.automatic_deduction_of_supported_commands = false; + unify_dotdot_attribute_store_set_configuration(&test_configuration); + + // Now it is not supported, feature is disabled + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, + uic_mqtt_dotdot_fan_control_set_fan_mode_callback( + expected_unid, + expected_endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK, + ZCL_Z_WAVE_FAN_MODE_ENUM_AUTO_HIGH)); +} + +void test_zwave_fan_control_set_fan_mode_command_update_desired() +{ + test_configuration.get_endpoint_node_function = &test_get_endpoint_node; + test_configuration.update_attribute_desired_values_on_commands = true; + unify_dotdot_attribute_store_set_configuration(&test_configuration); + + // Configure auto-desired update and auto-supported command setup + + // Check that the dispatch exists. + uic_mqtt_dotdot_fan_control_set_fan_mode_callback + = get_uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback(); + TEST_ASSERT_NOT_NULL(uic_mqtt_dotdot_fan_control_set_fan_mode_callback); + + // Nothing happens when no Attributes are under the endpoint (root in our test) + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, + uic_mqtt_dotdot_fan_control_set_fan_mode_callback( + expected_unid, + expected_endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL, + ZCL_Z_WAVE_FAN_MODE_ENUM_AUTO_HIGH)); + TEST_ASSERT_EQUAL( + 0, + attribute_store_get_node_child_count(attribute_store_get_root())); + + // Add attributes + attribute_store_node_t fan_control_node = attribute_store_add_node( + DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE, + attribute_store_get_root()); + ZWaveFanModeEnum value = ZCL_Z_WAVE_FAN_MODE_ENUM_LOW; + attribute_store_set_reported(fan_control_node, &value, sizeof(value)); + + // Now the desired value gets set to On when receiving the On Command: + expected_endpoint_id = 4; + TEST_ASSERT_EQUAL(SL_STATUS_OK, + uic_mqtt_dotdot_fan_control_set_fan_mode_callback( + expected_unid, + expected_endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL, + value)); + TEST_ASSERT_TRUE(attribute_store_is_desired_defined(fan_control_node)); + TEST_ASSERT_TRUE(attribute_store_is_reported_defined(fan_control_node)); + TEST_ASSERT_EQUAL( + ZCL_Z_WAVE_FAN_MODE_ENUM_LOW, + dotdot_get_unify_fan_control_z_wave_fan_mode(expected_unid, + expected_endpoint_id, + DESIRED_ATTRIBUTE)); + // Configure for no desired value update + test_configuration.update_attribute_desired_values_on_commands = false; + unify_dotdot_attribute_store_set_configuration(&test_configuration); + + attribute_store_undefine_desired(fan_control_node); + + // Now it is not supported, feature is disabled + TEST_ASSERT_EQUAL(SL_STATUS_OK, + uic_mqtt_dotdot_fan_control_set_fan_mode_callback( + expected_unid, + expected_endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL, + value)); + TEST_ASSERT_FALSE(attribute_store_is_desired_defined(fan_control_node)); +} + +void test_zwave_fan_control_set_fan_mode_command_clear_reported() +{ + test_configuration.get_endpoint_node_function = &test_get_endpoint_node; + test_configuration.update_attribute_desired_values_on_commands = true; + test_configuration.clear_reported_on_desired_updates = true; + unify_dotdot_attribute_store_set_configuration(&test_configuration); + + // Check that the dispatch on_off_command exists. + + // Check that the dispatch exists. + uic_mqtt_dotdot_fan_control_set_fan_mode_callback + = get_uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback(); + TEST_ASSERT_NOT_NULL(uic_mqtt_dotdot_fan_control_set_fan_mode_callback); + + // Nothing happens when no Attributes are under the endpoint (root in our test) + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, + uic_mqtt_dotdot_fan_control_set_fan_mode_callback( + expected_unid, + expected_endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL, + ZCL_Z_WAVE_FAN_MODE_ENUM_AUTO_HIGH)); + TEST_ASSERT_EQUAL( + 0, + attribute_store_get_node_child_count(attribute_store_get_root())); + + // Add attributes + attribute_store_node_t fan_control_node = attribute_store_add_node( + DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE, + attribute_store_get_root()); + ZWaveFanModeEnum value = ZCL_Z_WAVE_FAN_MODE_ENUM_LOW; + attribute_store_set_reported(fan_control_node, &value, sizeof(value)); + + // Now the desired value gets set to On when receiving the On Command: + expected_endpoint_id = 4; + TEST_ASSERT_EQUAL(SL_STATUS_OK, + uic_mqtt_dotdot_fan_control_set_fan_mode_callback( + expected_unid, + expected_endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL, + value)); + TEST_ASSERT_TRUE(attribute_store_is_desired_defined(fan_control_node)); + TEST_ASSERT_FALSE(attribute_store_is_reported_defined(fan_control_node)); + TEST_ASSERT_EQUAL( + ZCL_Z_WAVE_FAN_MODE_ENUM_LOW, + dotdot_get_unify_fan_control_z_wave_fan_mode(expected_unid, + expected_endpoint_id, + DESIRED_ATTRIBUTE)); +} \ No newline at end of file diff --git a/components/unify_dotdot_attribute_store/test/unify_dotdot_attribute_store_test_c.zapt b/components/unify_dotdot_attribute_store/test/unify_dotdot_attribute_store_test_c.zapt index e26af58116..bc7fd5cae3 100644 --- a/components/unify_dotdot_attribute_store/test/unify_dotdot_attribute_store_test_c.zapt +++ b/components/unify_dotdot_attribute_store/test/unify_dotdot_attribute_store_test_c.zapt @@ -138,6 +138,10 @@ void test_window_covering_go_to_lift_value_command(); void test_window_covering_go_to_lift_percentage_command(); void test_window_covering_go_to_tilt_value_command(); void test_window_covering_go_to_tilt_percentage_command(); +void test_zwave_fan_control_no_get_endpoint_function_registered(); +void test_zwave_fan_control_set_fan_mode_command_support(); +void test_zwave_fan_control_set_fan_mode_command_update_desired(); +void test_zwave_fan_control_set_fan_mode_command_clear_reported(); /// Setup the test suite (called once before all test_xxx functions are called) void suiteSetUp() 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 88967fac0a..8dbdb994cf 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 @@ -863,3 +863,8 @@ def DOTDOT_ATTRIBUTE_ID_PROTOCOL_CONTROLLER_NETWORK_MANAGEMENT_NETWORK_MANAGEMEN // This represents the attributes in the DotDot Descriptor cluster def DOTDOT_ATTRIBUTE_ID_DESCRIPTOR_DEVICE_TYPE_LIST 0xfd130000 +// This represents the attributes in the DotDot UnifyFanControl cluster +def DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE 0xfd140001 +def DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE 0xfd140002 +def DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE 0xfd140003 + 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 c4ef2c208c..ef9cfdb942 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 @@ -863,3 +863,8 @@ def zb_NetworkManagementState 0xfd120001 // This represents short CamelCase labels the attributes in the DotDot Descriptor cluster def zb_DeviceTypeList 0xfd130000 +// This represents short CamelCase labels the attributes in the DotDot UnifyFanControl cluster +def zb_ZWaveFanMode 0xfd140001 +def zb_ZWaveSupportedFanMode 0xfd140002 +def zb_ZWaveFanState 0xfd140003 + 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 16d75f051f..edf02ca453 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 @@ -77649,6 +77649,352 @@ bool dotdot_is_any_descriptor_writable_attribute_supported( const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id); +//////////////////////////////////////////////////////////////////////////////// +// Start of cluster UnifyFanControl +//////////////////////////////////////////////////////////////////////////////// +// UnifyFanControl ZWaveFanMode +/** + * @brief Verifies if the DotDot UnifyFanControl - ZWaveFanMode is supported + * under a UNID/EndpoinID + * + * @param unid Node's UNID + * @param endpoint_id Endpoint ID + * + * @returns true if ZWaveFanMode is supported + * @returns false if ZWaveFanMode is not supported + */ +bool dotdot_is_supported_unify_fan_control_z_wave_fan_mode ( + const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id); + +/** + * @brief Gets the DotDot UnifyFanControl - ZWaveFanMode 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 ZWaveFanMode attribute + */ +ZWaveFanModeEnum dotdot_get_unify_fan_control_z_wave_fan_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 UnifyFanControl - ZWaveFanMode 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_z_wave_fan_mode new value to set + * @returns sl_status_t SL_STATUS_OK on success + */ +sl_status_t dotdot_set_unify_fan_control_z_wave_fan_mode( + const dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint_id, + attribute_store_node_value_state_t value_state, + ZWaveFanModeEnum new_z_wave_fan_mode + ); + +/** + * @brief Undefines the Reported value of the the DotDot UnifyFanControl - ZWaveFanMode + * 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_fan_control_z_wave_fan_mode_undefine_reported( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint_id); + +/** + * @brief Undefines the Desired value of the DotDot + * UnifyFanControl - ZWaveFanMode 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_fan_control_z_wave_fan_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 + * UnifyFanControl - ZWaveFanMode 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_fan_control_z_wave_fan_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 + * UnifyFanControl - ZWaveFanMode 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_fan_control_z_wave_fan_mode_is_desired_defined( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint_id); + +/** + * @brief Creates a DotDot UnifyFanControl - ZWaveFanMode 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_fan_control_z_wave_fan_mode( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint_id); +// UnifyFanControl ZWaveSupportedFanMode +/** + * @brief Verifies if the DotDot UnifyFanControl - ZWaveSupportedFanMode is supported + * under a UNID/EndpoinID + * + * @param unid Node's UNID + * @param endpoint_id Endpoint ID + * + * @returns true if ZWaveSupportedFanMode is supported + * @returns false if ZWaveSupportedFanMode is not supported + */ +bool dotdot_is_supported_unify_fan_control_z_wave_supported_fan_mode ( + const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id); + +/** + * @brief Gets the DotDot UnifyFanControl - ZWaveSupportedFanMode 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 ZWaveSupportedFanMode attribute + */ +uint16_t dotdot_get_unify_fan_control_z_wave_supported_fan_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 UnifyFanControl - ZWaveSupportedFanMode 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_z_wave_supported_fan_mode new value to set + * @returns sl_status_t SL_STATUS_OK on success + */ +sl_status_t dotdot_set_unify_fan_control_z_wave_supported_fan_mode( + const dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint_id, + attribute_store_node_value_state_t value_state, + uint16_t new_z_wave_supported_fan_mode + ); + +/** + * @brief Undefines the Reported value of the the DotDot UnifyFanControl - ZWaveSupportedFanMode + * 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_fan_control_z_wave_supported_fan_mode_undefine_reported( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint_id); + +/** + * @brief Undefines the Desired value of the DotDot + * UnifyFanControl - ZWaveSupportedFanMode 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_fan_control_z_wave_supported_fan_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 + * UnifyFanControl - ZWaveSupportedFanMode 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_fan_control_z_wave_supported_fan_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 + * UnifyFanControl - ZWaveSupportedFanMode 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_fan_control_z_wave_supported_fan_mode_is_desired_defined( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint_id); + +/** + * @brief Creates a DotDot UnifyFanControl - ZWaveSupportedFanMode 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_fan_control_z_wave_supported_fan_mode( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint_id); +// UnifyFanControl ZWaveFanState +/** + * @brief Verifies if the DotDot UnifyFanControl - ZWaveFanState is supported + * under a UNID/EndpoinID + * + * @param unid Node's UNID + * @param endpoint_id Endpoint ID + * + * @returns true if ZWaveFanState is supported + * @returns false if ZWaveFanState is not supported + */ +bool dotdot_is_supported_unify_fan_control_z_wave_fan_state ( + const dotdot_unid_t unid, const dotdot_endpoint_id_t endpoint_id); + +/** + * @brief Gets the DotDot UnifyFanControl - ZWaveFanState 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 ZWaveFanState attribute + */ +ZWaveFanStateEnum dotdot_get_unify_fan_control_z_wave_fan_state( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint_id, + attribute_store_node_value_state_t value_state); + +/** + * @brief Set the DotDot UnifyFanControl - ZWaveFanState 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_z_wave_fan_state new value to set + * @returns sl_status_t SL_STATUS_OK on success + */ +sl_status_t dotdot_set_unify_fan_control_z_wave_fan_state( + const dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint_id, + attribute_store_node_value_state_t value_state, + ZWaveFanStateEnum new_z_wave_fan_state + ); + +/** + * @brief Undefines the Reported value of the the DotDot UnifyFanControl - ZWaveFanState + * 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_fan_control_z_wave_fan_state_undefine_reported( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint_id); + +/** + * @brief Undefines the Desired value of the DotDot + * UnifyFanControl - ZWaveFanState 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_fan_control_z_wave_fan_state_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 + * UnifyFanControl - ZWaveFanState 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_fan_control_z_wave_fan_state_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 + * UnifyFanControl - ZWaveFanState 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_fan_control_z_wave_fan_state_is_desired_defined( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint_id); + +/** + * @brief Creates a DotDot UnifyFanControl - ZWaveFanState 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_fan_control_z_wave_fan_state( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint_id); + +/** + * @brief Checks if a UNID/Endpoint supports any attribute for the UnifyFanControl + * 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_fan_control_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 + * UnifyFanControl 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_fan_control_writable_attribute_supported( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint_id); + #ifdef __cplusplus } #endif // __cplusplus diff --git a/components/unify_dotdot_attribute_store/zap-generated/include/unify_dotdot_defined_attribute_types.h b/components/unify_dotdot_attribute_store/zap-generated/include/unify_dotdot_defined_attribute_types.h index 0118d5ba2d..2bb4355397 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 @@ -794,6 +794,10 @@ DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_AOX_POSITION_ESTIMATION_POSITION , 0xfd1100 DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_PROTOCOL_CONTROLLER_NETWORK_MANAGEMENT_NETWORK_MANAGEMENT_STATE , 0xfd120001) // Attribute Defines for Descriptor DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_DESCRIPTOR_DEVICE_TYPE_LIST , 0xfd130000) +// Attribute Defines for UnifyFanControl +DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE , 0xfd140001) +DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE , 0xfd140002) +DEFINE_ATTRIBUTE(DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE , 0xfd140003) // Additional manually defined types: diff --git a/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_attribute_publisher.cpp b/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_attribute_publisher.cpp index 2aba63453d..5c38ba2fb7 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 @@ -25633,6 +25633,265 @@ static void descriptor_cluster_cluster_revision_callback( } +/** + * @brief Publishes the desired value of an updated attribute store node for + * the UnifyFanControl cluster. + * @param updated_node Updated attribute store node + * @param change Type of change applied + */ +static void unify_fan_control_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_FAN_CONTROL_Z_WAVE_FAN_MODE) { + uic_mqtt_dotdot_unify_fan_control_z_wave_fan_mode_publish( + base_topic.c_str(), + static_cast(attr.desired_or_reported()), + UCL_MQTT_PUBLISH_TYPE_DESIRED); + return; + } + if (type == DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE) { + uic_mqtt_dotdot_unify_fan_control_z_wave_supported_fan_mode_publish( + base_topic.c_str(), + static_cast(attr.desired_or_reported()), + UCL_MQTT_PUBLISH_TYPE_DESIRED); + return; + } + if (type == DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE) { + uic_mqtt_dotdot_unify_fan_control_z_wave_fan_state_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 UnifyFanControl cluster. + * @param updated_node Updated attribute store node + * @param change Type of change applied + */ +static void unify_fan_control_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_FAN_CONTROL_Z_WAVE_FAN_MODE: + // clang-format on + sl_log_debug(LOG_TAG, + "Unretaining UnifyFanControl::ZWaveFanMode under topic %s", + base_topic.c_str()); + // clang-format off + uic_mqtt_dotdot_unify_fan_control_z_wave_fan_mode_unretain(base_topic.c_str(), UCL_MQTT_PUBLISH_TYPE_ALL); + break; + case DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE: + // clang-format on + sl_log_debug(LOG_TAG, + "Unretaining UnifyFanControl::ZWaveSupportedFanMode under topic %s", + base_topic.c_str()); + // clang-format off + uic_mqtt_dotdot_unify_fan_control_z_wave_supported_fan_mode_unretain(base_topic.c_str(), UCL_MQTT_PUBLISH_TYPE_ALL); + break; + case DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE: + // clang-format on + sl_log_debug(LOG_TAG, + "Unretaining UnifyFanControl::ZWaveFanState under topic %s", + base_topic.c_str()); + // clang-format off + uic_mqtt_dotdot_unify_fan_control_z_wave_fan_state_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_FAN_CONTROL_Z_WAVE_FAN_MODE) { + uic_mqtt_dotdot_unify_fan_control_z_wave_fan_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_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE) { + uic_mqtt_dotdot_unify_fan_control_z_wave_supported_fan_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_FAN_CONTROL_Z_WAVE_FAN_STATE) { + uic_mqtt_dotdot_unify_fan_control_z_wave_fan_state_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_fan_control_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 + "/UnifyFanControl/Attributes/ClusterRevision"; + if (uic_mqtt_count_topics(cluster_revision_topic.c_str()) == 0) { + uic_mqtt_dotdot_unify_fan_control_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_fan_control_attribute_supported(unid, endpoint_id)) { + base_topic += "/UnifyFanControl"; + sl_log_debug(LOG_TAG, "No more attributes supported for UnifyFanControl cluster for UNID %s Endpoint %d. Unretaining leftover topics at %s",unid, endpoint_id, base_topic.c_str()); + uic_mqtt_unretain(base_topic.c_str()); + } + } +} + + // Initialization of the component. sl_status_t unify_dotdot_attribute_store_attribute_publisher_init() @@ -35675,6 +35934,48 @@ sl_status_t unify_dotdot_attribute_store_attribute_publisher_init() attribute_store_register_callback_by_type( descriptor_cluster_cluster_revision_callback, DOTDOT_ATTRIBUTE_ID_DESCRIPTOR_DEVICE_TYPE_LIST); + //Desired attribute state + attribute_store_register_callback_by_type_and_state( + unify_fan_control_cluster_publish_desired_value_callback, + DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE, + DESIRED_ATTRIBUTE); + //Reported attribute state + attribute_store_register_callback_by_type_and_state( + unify_fan_control_cluster_publish_reported_value_callback, + DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE, + REPORTED_ATTRIBUTE); + //registering a callback when an attribute is created for publishing cluster revision + attribute_store_register_callback_by_type( + unify_fan_control_cluster_cluster_revision_callback, + DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE); + //Desired attribute state + attribute_store_register_callback_by_type_and_state( + unify_fan_control_cluster_publish_desired_value_callback, + DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE, + DESIRED_ATTRIBUTE); + //Reported attribute state + attribute_store_register_callback_by_type_and_state( + unify_fan_control_cluster_publish_reported_value_callback, + DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE, + REPORTED_ATTRIBUTE); + //registering a callback when an attribute is created for publishing cluster revision + attribute_store_register_callback_by_type( + unify_fan_control_cluster_cluster_revision_callback, + DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE); + //Desired attribute state + attribute_store_register_callback_by_type_and_state( + unify_fan_control_cluster_publish_desired_value_callback, + DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE, + DESIRED_ATTRIBUTE); + //Reported attribute state + attribute_store_register_callback_by_type_and_state( + unify_fan_control_cluster_publish_reported_value_callback, + DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE, + REPORTED_ATTRIBUTE); + //registering a callback when an attribute is created for publishing cluster revision + attribute_store_register_callback_by_type( + unify_fan_control_cluster_cluster_revision_callback, + DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE); return SL_STATUS_OK; } diff --git a/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_force_read_attributes_command_callbacks.c b/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_force_read_attributes_command_callbacks.c index 5d488fb5a1..545e737051 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 @@ -4499,6 +4499,46 @@ static sl_status_t uic_mqtt_dotdot_descriptor_force_read_attributes_callback ( } return SL_STATUS_OK; } +//////////////////////////////////////////////////////////////////////////////// +// Start of cluster UnifyFanControl +//////////////////////////////////////////////////////////////////////////////// +static sl_status_t uic_mqtt_dotdot_unify_fan_control_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_fan_control_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_fan_control_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.z_wave_fan_mode) { + if (SL_STATUS_OK == dotdot_unify_fan_control_z_wave_fan_mode_undefine_reported(unid, endpoint_id)) { + sl_log_debug(LOG_TAG, "Undefined Reported value of UnifyFanControl::ZWaveFanMode under %s - Endpoint %d", unid, endpoint_id); + } + } + if (true == attribute_list.z_wave_supported_fan_mode) { + if (SL_STATUS_OK == dotdot_unify_fan_control_z_wave_supported_fan_mode_undefine_reported(unid, endpoint_id)) { + sl_log_debug(LOG_TAG, "Undefined Reported value of UnifyFanControl::ZWaveSupportedFanMode under %s - Endpoint %d", unid, endpoint_id); + } + } + if (true == attribute_list.z_wave_fan_state) { + if (SL_STATUS_OK == dotdot_unify_fan_control_z_wave_fan_state_undefine_reported(unid, endpoint_id)) { + sl_log_debug(LOG_TAG, "Undefined Reported value of UnifyFanControl::ZWaveFanState under %s - Endpoint %d", unid, endpoint_id); + } + } + return SL_STATUS_OK; +} // clang-format on //////////////////////////////////////////////////////////////////////////////// @@ -4609,6 +4649,8 @@ sl_status_t uic_mqtt_dotdot_set_descriptor_force_read_attributes_callback(&uic_mqtt_dotdot_descriptor_force_read_attributes_callback); + uic_mqtt_dotdot_set_unify_fan_control_force_read_attributes_callback(&uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback); + // clang-format on return SL_STATUS_OK; diff --git a/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_helpers.cpp b/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_helpers.cpp index e170bb45b1..aef51e5bcb 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 @@ -83593,5 +83593,369 @@ bool dotdot_is_any_descriptor_writable_attribute_supported( const dotdot_endpoint_id_t endpoint_id) { + return false; +} +//////////////////////////////////////////////////////////////////////////////// +// Start of cluster UnifyFanControl +//////////////////////////////////////////////////////////////////////////////// +bool dotdot_is_supported_unify_fan_control_z_wave_fan_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_FAN_CONTROL_Z_WAVE_FAN_MODE); + return attribute_store_node_exists(node); +} + +ZWaveFanModeEnum dotdot_get_unify_fan_control_z_wave_fan_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_FAN_CONTROL_Z_WAVE_FAN_MODE); + + ZWaveFanModeEnum result = {}; + attribute_store_read_value(node, + value_state, + (uint8_t *)&result, + sizeof(result)); + return result; +} + +sl_status_t dotdot_set_unify_fan_control_z_wave_fan_mode( + const dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint_id, + attribute_store_node_value_state_t value_state, + ZWaveFanModeEnum new_z_wave_fan_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_FAN_CONTROL_Z_WAVE_FAN_MODE); + + return attribute_store_set_node_attribute_value(node, + value_state, + (uint8_t *)&new_z_wave_fan_mode, + sizeof(ZWaveFanModeEnum)); + } + +sl_status_t dotdot_unify_fan_control_z_wave_fan_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_FAN_CONTROL_Z_WAVE_FAN_MODE); + attribute_store_undefine_reported(node); + return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; +} + +sl_status_t dotdot_unify_fan_control_z_wave_fan_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_FAN_CONTROL_Z_WAVE_FAN_MODE); + attribute_store_undefine_desired(node); + return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; +} + + +bool dotdot_unify_fan_control_z_wave_fan_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_FAN_CONTROL_Z_WAVE_FAN_MODE); + return attribute_store_is_reported_defined(node); +} + +bool dotdot_unify_fan_control_z_wave_fan_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_FAN_CONTROL_Z_WAVE_FAN_MODE); + return attribute_store_is_desired_defined(node); +} + +sl_status_t dotdot_create_unify_fan_control_z_wave_fan_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_FAN_CONTROL_Z_WAVE_FAN_MODE); + + return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; +} +bool dotdot_is_supported_unify_fan_control_z_wave_supported_fan_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_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE); + return attribute_store_node_exists(node); +} + +uint16_t dotdot_get_unify_fan_control_z_wave_supported_fan_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_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE); + + uint16_t result = {}; + attribute_store_read_value(node, + value_state, + (uint8_t *)&result, + sizeof(result)); + return result; +} + +sl_status_t dotdot_set_unify_fan_control_z_wave_supported_fan_mode( + const dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint_id, + attribute_store_node_value_state_t value_state, + uint16_t new_z_wave_supported_fan_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_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE); + + return attribute_store_set_node_attribute_value(node, + value_state, + (uint8_t *)&new_z_wave_supported_fan_mode, + sizeof(uint16_t)); + } + +sl_status_t dotdot_unify_fan_control_z_wave_supported_fan_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_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE); + attribute_store_undefine_reported(node); + return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; +} + +sl_status_t dotdot_unify_fan_control_z_wave_supported_fan_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_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE); + attribute_store_undefine_desired(node); + return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; +} + + +bool dotdot_unify_fan_control_z_wave_supported_fan_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_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE); + return attribute_store_is_reported_defined(node); +} + +bool dotdot_unify_fan_control_z_wave_supported_fan_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_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE); + return attribute_store_is_desired_defined(node); +} + +sl_status_t dotdot_create_unify_fan_control_z_wave_supported_fan_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_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE); + + return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; +} +bool dotdot_is_supported_unify_fan_control_z_wave_fan_state( + 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_FAN_CONTROL_Z_WAVE_FAN_STATE); + return attribute_store_node_exists(node); +} + +ZWaveFanStateEnum dotdot_get_unify_fan_control_z_wave_fan_state( + 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_FAN_CONTROL_Z_WAVE_FAN_STATE); + + ZWaveFanStateEnum result = {}; + attribute_store_read_value(node, + value_state, + (uint8_t *)&result, + sizeof(result)); + return result; +} + +sl_status_t dotdot_set_unify_fan_control_z_wave_fan_state( + const dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint_id, + attribute_store_node_value_state_t value_state, + ZWaveFanStateEnum new_z_wave_fan_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_FAN_CONTROL_Z_WAVE_FAN_STATE); + + return attribute_store_set_node_attribute_value(node, + value_state, + (uint8_t *)&new_z_wave_fan_state, + sizeof(ZWaveFanStateEnum)); + } + +sl_status_t dotdot_unify_fan_control_z_wave_fan_state_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_FAN_CONTROL_Z_WAVE_FAN_STATE); + attribute_store_undefine_reported(node); + return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; +} + +sl_status_t dotdot_unify_fan_control_z_wave_fan_state_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_FAN_CONTROL_Z_WAVE_FAN_STATE); + attribute_store_undefine_desired(node); + return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; +} + + +bool dotdot_unify_fan_control_z_wave_fan_state_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_FAN_CONTROL_Z_WAVE_FAN_STATE); + return attribute_store_is_reported_defined(node); +} + +bool dotdot_unify_fan_control_z_wave_fan_state_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_FAN_CONTROL_Z_WAVE_FAN_STATE); + return attribute_store_is_desired_defined(node); +} + +sl_status_t dotdot_create_unify_fan_control_z_wave_fan_state( + 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_FAN_CONTROL_Z_WAVE_FAN_STATE); + + return (node != ATTRIBUTE_STORE_INVALID_NODE) ? SL_STATUS_OK : SL_STATUS_FAIL; +} + +bool dotdot_is_any_unify_fan_control_attribute_supported( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint_id) { + + if (true == dotdot_is_supported_unify_fan_control_z_wave_fan_mode(unid, endpoint_id)) { + return true; + } + if (true == dotdot_is_supported_unify_fan_control_z_wave_supported_fan_mode(unid, endpoint_id)) { + return true; + } + if (true == dotdot_is_supported_unify_fan_control_z_wave_fan_state(unid, endpoint_id)) { + return true; + } + + return false; +} + +bool dotdot_is_any_unify_fan_control_writable_attribute_supported( + const dotdot_unid_t unid, + const dotdot_endpoint_id_t endpoint_id) { + + if (true == dotdot_is_supported_unify_fan_control_z_wave_fan_mode(unid, endpoint_id)) { + return true; + } + return false; } diff --git a/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_registration.cpp b/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_registration.cpp index 0423e9305d..f1ff3edf4e 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 @@ -17311,6 +17311,78 @@ sl_status_t unify_dotdot_attribute_store_registration_init() // clang-format off // clang-format on + { + // ZWaveFanModeEnum // ZWaveFanModeEnum // ZWaveFanModeEnum + std::string attribute_type_string = "ZWaveFanModeEnum"; + 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 UnifyFanControl ZWaveFanMode, " + "type: ZWaveFanModeEnum // ZWaveFanModeEnum"); + } + + status |= attribute_store_register_type( + DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_MODE, + "ZCL UnifyFanControl ZWaveFanMode", + ATTRIBUTE_STORE_INVALID_ATTRIBUTE_TYPE, + storage_type); + } + + // clang-format off + // clang-format on + + { + // map16 // map16 // uint16_t + std::string attribute_type_string = "uint16_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 UnifyFanControl ZWaveSupportedFanMode, " + "type: map16 // uint16_t"); + } + + status |= attribute_store_register_type( + DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_SUPPORTED_FAN_MODE, + "ZCL UnifyFanControl ZWaveSupportedFanMode", + ATTRIBUTE_STORE_INVALID_ATTRIBUTE_TYPE, + storage_type); + } + + // clang-format off + // clang-format on + + { + // ZWaveFanStateEnum // ZWaveFanStateEnum // ZWaveFanStateEnum + std::string attribute_type_string = "ZWaveFanStateEnum"; + 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 UnifyFanControl ZWaveFanState, " + "type: ZWaveFanStateEnum // ZWaveFanStateEnum"); + } + + status |= attribute_store_register_type( + DOTDOT_ATTRIBUTE_ID_UNIFY_FAN_CONTROL_Z_WAVE_FAN_STATE, + "ZCL UnifyFanControl ZWaveFanState", + ATTRIBUTE_STORE_INVALID_ATTRIBUTE_TYPE, + storage_type); + } + + // clang-format off + // clang-format on + // Additional attributes: for (auto const &a: zcl_additional_attribute_schema) { status |= attribute_store_register_type(a.type, diff --git a/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_write_attributes_command_callbacks.c b/components/unify_dotdot_attribute_store/zap-generated/src/unify_dotdot_attribute_store_write_attributes_command_callbacks.c index f45d19784b..2cd7f25e63 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 @@ -2585,6 +2585,40 @@ static sl_status_t descriptor_cluster_write_attributes_callback( endpoint_id); return SL_STATUS_OK; } +//////////////////////////////////////////////////////////////////////////////// +// Start of cluster UnifyFanControl +//////////////////////////////////////////////////////////////////////////////// +// WriteAttribute Callbacks unify_fan_control +static sl_status_t unify_fan_control_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_fan_control_state_t attributes, + uic_mqtt_dotdot_unify_fan_control_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_fan_control_writable_attribute_supported(unid, endpoint_id) ? + SL_STATUS_OK : SL_STATUS_FAIL; + } else { + return SL_STATUS_FAIL; + } + } + + sl_log_debug(LOG_TAG, + "unify_fan_control: Incoming WriteAttributes command for %s, endpoint %d.\n", + unid, + endpoint_id); + if (true == updated_attributes.z_wave_fan_mode) { + sl_log_debug(LOG_TAG, "Updating desired value for ZWaveFanMode attribute"); + dotdot_set_unify_fan_control_z_wave_fan_mode(unid, endpoint_id, DESIRED_ATTRIBUTE, attributes.z_wave_fan_mode); + } + return SL_STATUS_OK; +} // clang-format on //////////////////////////////////////////////////////////////////////////////// @@ -2752,6 +2786,9 @@ sl_status_t uic_mqtt_dotdot_set_descriptor_write_attributes_callback( &descriptor_cluster_write_attributes_callback); + uic_mqtt_dotdot_set_unify_fan_control_write_attributes_callback( + &unify_fan_control_cluster_write_attributes_callback); + // clang-format on return SL_STATUS_OK; diff --git a/components/unify_dotdot_attribute_store/zap-generated/test/unify_dotdot_attribute_store_test.c b/components/unify_dotdot_attribute_store/zap-generated/test/unify_dotdot_attribute_store_test.c index fda0a48f1a..32abe1ccca 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,24 @@ uic_mqtt_dotdot_descriptor_write_attributes_callback_t get_uic_mqtt_dotdot_descr return test_uic_mqtt_dotdot_descriptor_write_attributes_callback; } +static uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback_t test_uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback = NULL; +static uic_mqtt_dotdot_unify_fan_control_write_attributes_callback_t test_uic_mqtt_dotdot_unify_fan_control_write_attributes_callback = NULL; + +uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback_t get_uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback(){ + return test_uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback; +} +uic_mqtt_dotdot_unify_fan_control_write_attributes_callback_t get_uic_mqtt_dotdot_unify_fan_control_write_attributes_callback(){ + return test_uic_mqtt_dotdot_unify_fan_control_write_attributes_callback; +} + +static uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_t test_uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback = NULL; +uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_t get_uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback(){ + return test_uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback; +} +static uic_mqtt_dotdot_unify_fan_control_turn_off_callback_t test_uic_mqtt_dotdot_unify_fan_control_turn_off_callback = NULL; +uic_mqtt_dotdot_unify_fan_control_turn_off_callback_t get_uic_mqtt_dotdot_unify_fan_control_turn_off_callback(){ + return test_uic_mqtt_dotdot_unify_fan_control_turn_off_callback; +} // clang-format on #define TEST_UNID "test-unid-123" @@ -1394,6 +1412,10 @@ void test_window_covering_go_to_lift_value_command(); void test_window_covering_go_to_lift_percentage_command(); void test_window_covering_go_to_tilt_value_command(); void test_window_covering_go_to_tilt_percentage_command(); +void test_zwave_fan_control_no_get_endpoint_function_registered(); +void test_zwave_fan_control_set_fan_mode_command_support(); +void test_zwave_fan_control_set_fan_mode_command_update_desired(); +void test_zwave_fan_control_set_fan_mode_command_clear_reported(); /// Setup the test suite (called once before all test_xxx functions are called) void suiteSetUp() @@ -2878,6 +2900,26 @@ void set_uic_mqtt_dotdot_descriptor_write_attributes_callback_stub( { test_uic_mqtt_dotdot_descriptor_write_attributes_callback = callback; } +void set_uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback_stub( + const uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback_t callback, int cmock_num_calls) +{ + test_uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback = callback; +} +void set_uic_mqtt_dotdot_unify_fan_control_write_attributes_callback_stub( + const uic_mqtt_dotdot_unify_fan_control_write_attributes_callback_t callback, int cmock_num_calls) +{ + test_uic_mqtt_dotdot_unify_fan_control_write_attributes_callback = callback; +} +void uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_set_stub( + const uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_t callback, int cmock_num_calls) +{ + test_uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback = callback; +} +void uic_mqtt_dotdot_unify_fan_control_turn_off_callback_set_stub( + const uic_mqtt_dotdot_unify_fan_control_turn_off_callback_t callback, int cmock_num_calls) +{ + test_uic_mqtt_dotdot_unify_fan_control_turn_off_callback = callback; +} // clang-format on // Test functions @@ -3798,6 +3840,18 @@ void setUp() test_uic_mqtt_dotdot_descriptor_write_attributes_callback = NULL; uic_mqtt_dotdot_set_descriptor_write_attributes_callback_Stub( &set_uic_mqtt_dotdot_descriptor_write_attributes_callback_stub); + test_uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback = NULL; + uic_mqtt_dotdot_set_unify_fan_control_force_read_attributes_callback_Stub( + &set_uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback_stub); + test_uic_mqtt_dotdot_unify_fan_control_write_attributes_callback = NULL; + uic_mqtt_dotdot_set_unify_fan_control_write_attributes_callback_Stub( + &set_uic_mqtt_dotdot_unify_fan_control_write_attributes_callback_stub); + test_uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback = NULL; + uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_set_Stub( + &uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_set_stub); + test_uic_mqtt_dotdot_unify_fan_control_turn_off_callback = NULL; + uic_mqtt_dotdot_unify_fan_control_turn_off_callback_set_Stub( + &uic_mqtt_dotdot_unify_fan_control_turn_off_callback_set_stub); // clang-format on group_command_dispatch = NULL; @@ -4560,6 +4614,9 @@ void test_automatic_deduction_of_supported_commands() TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_aox_position_estimation_position(expected_unid,expected_endpoint_id) ); TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_protocol_controller_network_management_network_management_state(expected_unid,expected_endpoint_id) ); TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_descriptor_device_type_list(expected_unid,expected_endpoint_id) ); + TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_unify_fan_control_z_wave_fan_mode(expected_unid,expected_endpoint_id) ); + TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_unify_fan_control_z_wave_supported_fan_mode(expected_unid,expected_endpoint_id) ); + TEST_ASSERT_EQUAL(SL_STATUS_OK, dotdot_create_unify_fan_control_z_wave_fan_state(expected_unid,expected_endpoint_id) ); // clang-format on // ColorControl checks the value in the bitmask: @@ -7695,6 +7752,24 @@ void test_automatic_deduction_of_supported_commands() )); } + if (NULL != test_uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback) { + // Dummy command parameters + ZWaveFanModeEnum fan_mode_value; + memset(&fan_mode_value, 0x00, sizeof(fan_mode_value)); + // Invoke with support check + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, test_uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback(expected_unid,expected_endpoint_id,UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK + , + fan_mode_value + + )); + } + if (NULL != test_uic_mqtt_dotdot_unify_fan_control_turn_off_callback) { + // Dummy command parameters + // Invoke with support check + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, test_uic_mqtt_dotdot_unify_fan_control_turn_off_callback(expected_unid,expected_endpoint_id,UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK + + )); + } // Invoke all commands with support check, they should return SL_STATUS_OK // because all ZCL attributes are supported @@ -10822,5 +10897,23 @@ void test_automatic_deduction_of_supported_commands() )); } + if (NULL != test_uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback) { + // Dummy command parameters + ZWaveFanModeEnum fan_mode_value; + memset(&fan_mode_value, 0x00, sizeof(fan_mode_value)); + // Invoke with support check + TEST_ASSERT_EQUAL(SL_STATUS_OK, test_uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback(expected_unid,expected_endpoint_id,UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK + , + fan_mode_value + + )); + } + if (NULL != test_uic_mqtt_dotdot_unify_fan_control_turn_off_callback) { + // Dummy command parameters + // Invoke with support check + TEST_ASSERT_EQUAL(SL_STATUS_OK, test_uic_mqtt_dotdot_unify_fan_control_turn_off_callback(expected_unid,expected_endpoint_id,UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK + + )); + } } 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 5d7b13dc5e..10feb83873 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 @@ -793,4 +793,14 @@ uic_mqtt_dotdot_descriptor_force_read_attributes_callback_t get_uic_mqtt_dotdot_descriptor_force_read_attributes_callback(); uic_mqtt_dotdot_descriptor_write_attributes_callback_t get_uic_mqtt_dotdot_descriptor_write_attributes_callback(); + + uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback_t get_uic_mqtt_dotdot_unify_fan_control_force_read_attributes_callback(); + uic_mqtt_dotdot_unify_fan_control_write_attributes_callback_t get_uic_mqtt_dotdot_unify_fan_control_write_attributes_callback(); + + + uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback_t get_uic_mqtt_dotdot_unify_fan_control_set_fan_mode_callback(); + + + uic_mqtt_dotdot_unify_fan_control_turn_off_callback_t get_uic_mqtt_dotdot_unify_fan_control_turn_off_callback(); + #endif // UNIFY_DOTDOT_ATTRIBUTE_STORE_TEST_H \ No newline at end of file