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