From 95f3abebaabbf6fa42747981137aaa122fd4d70f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20Labb=C3=A9?= Date: Tue, 21 May 2024 11:32:46 +0200 Subject: [PATCH] GH-46: Custom Humidity Control cluster Forwarded: https://github.com/SiliconLabs/UnifySDK/pull/46 Bug-SiliconLabs: UIC-3042 Bug-Github: https://github.com/SiliconLabs/UnifySDK/pull/46 --- .../zcl_cluster_servers/CMakeLists.txt | 4 +- .../src/humidity_control_cluster_server.c | 133 +++++++++++ .../src/humidity_control_cluster_server.h | 38 +++ .../zcl_cluster_servers/test/CMakeLists.txt | 14 +- .../humidity_control_cluster_server_test.c | 224 ++++++++++++++++++ .../dotdot-xml/Unify_HumidityControl.xml | 120 ++++++++++ components/uic_dotdot/dotdot-xml/library.xml | 1 + 7 files changed, 532 insertions(+), 2 deletions(-) create mode 100644 applications/zpc/components/zcl_cluster_servers/src/humidity_control_cluster_server.c create mode 100644 applications/zpc/components/zcl_cluster_servers/src/humidity_control_cluster_server.h create mode 100644 applications/zpc/components/zcl_cluster_servers/test/humidity_control_cluster_server_test.c create mode 100644 components/uic_dotdot/dotdot-xml/Unify_HumidityControl.xml diff --git a/applications/zpc/components/zcl_cluster_servers/CMakeLists.txt b/applications/zpc/components/zcl_cluster_servers/CMakeLists.txt index 663f04f7fc..962fa7ded3 100644 --- a/applications/zpc/components/zcl_cluster_servers/CMakeLists.txt +++ b/applications/zpc/components/zcl_cluster_servers/CMakeLists.txt @@ -14,7 +14,9 @@ add_library( src/zcl_cluster_servers_helpers.cpp src/zcl_OTA_cluster_server.cpp src/zcl_rf_telemetry_cluster_server.c - src/zcl_scenes_cluster_server.cpp) + src/zcl_scenes_cluster_server.cpp + src/humidity_control_cluster_server.c + ) target_include_directories( zcl_cluster_servers diff --git a/applications/zpc/components/zcl_cluster_servers/src/humidity_control_cluster_server.c b/applications/zpc/components/zcl_cluster_servers/src/humidity_control_cluster_server.c new file mode 100644 index 0000000000..cbd4c7f977 --- /dev/null +++ b/applications/zpc/components/zcl_cluster_servers/src/humidity_control_cluster_server.c @@ -0,0 +1,133 @@ + +/****************************************************************************** + * # License + * Copyright 2024 Silicon Laboratories Inc. www.silabs.com + ****************************************************************************** + * The licensor of this software is Silicon Laboratories Inc. Your use of this + * software is governed by the terms of Silicon Labs Master Software License + * Agreement (MSLA) available at + * www.silabs.com/about-us/legal/master-software-license-agreement. This + * software is distributed to you in Source Code format and is governed by the + * sections of the MSLA applicable to Source Code. + * + *****************************************************************************/ +// Includes from this component +#include "humidity_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_humidity_control_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 "unify_humidity_control_cluster_server" + +sl_status_t unify_humidity_control_mode_set( + dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint, + uic_mqtt_dotdot_callback_call_type_t call_type, + ModeType mode) +{ + attribute_store_node_t endpoint_node + = attribute_store_network_helper_get_endpoint_node(unid, endpoint); + + attribute_store_node_t current_mode_node + = attribute_store_get_first_child_by_type( + endpoint_node, + ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_MODE_CURRENT_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 + return attribute_store_node_exists(current_mode_node) ? SL_STATUS_OK + : SL_STATUS_FAIL; + } + + humidity_control_mode_t mode_value = mode; + return attribute_store_set_desired(current_mode_node, + &mode_value, + sizeof(mode_value)); +} + +sl_status_t unify_humidity_control_setpoint_set( + dotdot_unid_t unid, + dotdot_endpoint_id_t endpoint, + uic_mqtt_dotdot_callback_call_type_t call_type, + SetpointType type, + uint8_t precision, + uint8_t scale, + int32_t value) +{ + // First check the call type. This command have too many requirements + // so we support it by default + if (call_type == UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK) { + return SL_STATUS_OK; + } + + attribute_store_node_t endpoint_node + = attribute_store_network_helper_get_endpoint_node(unid, endpoint); + + humidity_control_setpoint_type_t setpoint_type = type; + + attribute_store_node_t setpoint_type_node + = attribute_store_get_node_child_by_value( + endpoint_node, + ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_TYPE, + REPORTED_ATTRIBUTE, + &setpoint_type, + sizeof(setpoint_type), + 0); + + if (setpoint_type == ATTRIBUTE_STORE_INVALID_NODE) { + sl_log_warning(LOG_TAG, + "Can't find humidity setpoint type %d", + setpoint_type); + return SL_STATUS_FAIL; + } + + attribute_store_set_child_reported( + setpoint_type_node, + ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_VALUE_PRECISION, + &precision, + sizeof(precision)); + + attribute_store_set_child_reported( + setpoint_type_node, + ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_VALUE_SCALE, + &scale, + sizeof(scale)); + + return attribute_store_set_child_desired( + setpoint_type_node, + ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_VALUE, + &value, + sizeof(value)); +} + +/////////////////////////////////////////////////////////////////////////////// +// Init and teardown functions +/////////////////////////////////////////////////////////////////////////////// +sl_status_t humidity_control_cluster_server_init() +{ + sl_log_debug(LOG_TAG, "Humidity Control cluster (ZWave) server initialization"); + + // Listen to the BASIC Value attribute is created + uic_mqtt_dotdot_unify_humidity_control_mode_set_callback_set( + &unify_humidity_control_mode_set); + + uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback_set( + &unify_humidity_control_setpoint_set); + + return SL_STATUS_OK; +} diff --git a/applications/zpc/components/zcl_cluster_servers/src/humidity_control_cluster_server.h b/applications/zpc/components/zcl_cluster_servers/src/humidity_control_cluster_server.h new file mode 100644 index 0000000000..43a408af2c --- /dev/null +++ b/applications/zpc/components/zcl_cluster_servers/src/humidity_control_cluster_server.h @@ -0,0 +1,38 @@ +/****************************************************************************** + * # 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 HUMIDITY_CONTROL_CLUSTER_SERVER_H +#define HUMIDITY_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 humidity_control_cluster_server_init(void); + +#ifdef __cplusplus +} +#endif + +#endif //HUMIDITY_CONTROL_CLUSTER_SERVER_H +/** @} end humidity_control_cluster_server */ diff --git a/applications/zpc/components/zcl_cluster_servers/test/CMakeLists.txt b/applications/zpc/components/zcl_cluster_servers/test/CMakeLists.txt index ff9d893c81..c8a60bf1f2 100644 --- a/applications/zpc/components/zcl_cluster_servers/test/CMakeLists.txt +++ b/applications/zpc/components/zcl_cluster_servers/test/CMakeLists.txt @@ -102,4 +102,16 @@ target_add_unittest( zcl_cluster_servers_fixture_test.c DEPENDS unify - zpc_config_mock) \ No newline at end of file + zpc_config_mock) + + # Humidity Control Cluster Mapper test +target_add_unittest( + zcl_cluster_servers + NAME + humidity_control_cluster_server_test + SOURCES + humidity_control_cluster_server_test.c + DEPENDS + zpc_attribute_store_test_helper + uic_dotdot_mqtt_mock + unify_dotdot_attribute_store) \ No newline at end of file diff --git a/applications/zpc/components/zcl_cluster_servers/test/humidity_control_cluster_server_test.c b/applications/zpc/components/zcl_cluster_servers/test/humidity_control_cluster_server_test.c new file mode 100644 index 0000000000..bb8d6a59cf --- /dev/null +++ b/applications/zpc/components/zcl_cluster_servers/test/humidity_control_cluster_server_test.c @@ -0,0 +1,224 @@ +/****************************************************************************** + * # 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 "humidity_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_humidity_control_types.h" + +// Test helpers +#include "zpc_attribute_store_test_helper.h" +#include "attribute_store_defined_attribute_types.h" + +uic_mqtt_dotdot_unify_humidity_control_mode_set_callback_t + uic_mqtt_dotdot_unify_humidity_control_mode_set_callback; + +uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback_t + uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback; + +void uic_mqtt_dotdot_unify_humidity_control_mode_set_callback_set_stub( + const uic_mqtt_dotdot_unify_humidity_control_mode_set_callback_t callback, + int cmock_num_calls) +{ + uic_mqtt_dotdot_unify_humidity_control_mode_set_callback = callback; +} + +void uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback_set_stub( + const uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback_t callback, + int cmock_num_calls) +{ + uic_mqtt_dotdot_unify_humidity_control_setpoint_set_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_humidity_control_mode_set_callback_set_Stub( + &uic_mqtt_dotdot_unify_humidity_control_mode_set_callback_set_stub); + uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback_set_Stub( + &uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback_set_stub); + + // Call init + TEST_ASSERT_EQUAL(SL_STATUS_OK, humidity_control_cluster_server_init()); +} + +/// Called after each and every test +void tearDown() +{ + attribute_store_delete_node(attribute_store_get_root()); +} + +void test_humidity_control_set_mode_command_mapping() +{ + TEST_ASSERT_NOT_NULL( + uic_mqtt_dotdot_unify_humidity_control_mode_set_callback); + + ModeType expected_current_mode = ZCL_MODE_TYPE_DEHUMIDIFY; + + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, + uic_mqtt_dotdot_unify_humidity_control_mode_set_callback( + supporting_node_unid, + endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK, + expected_current_mode)); + + attribute_store_node_t current_mode_node = attribute_store_add_node( + ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_MODE_CURRENT_MODE, + endpoint_id_node); + + // test support + TEST_ASSERT_EQUAL(SL_STATUS_OK, + uic_mqtt_dotdot_unify_humidity_control_mode_set_callback( + supporting_node_unid, + endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK, + expected_current_mode)); + // Test callback + TEST_ASSERT_EQUAL(SL_STATUS_OK, + uic_mqtt_dotdot_unify_humidity_control_mode_set_callback( + supporting_node_unid, + endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL, + expected_current_mode)); + + humidity_control_mode_t current_mode = 0; + + TEST_ASSERT_EQUAL_MESSAGE(SL_STATUS_OK, + attribute_store_get_desired(current_mode_node, + ¤t_mode, + sizeof(current_mode)), + "Can't get current mode value"); + + // Test value + TEST_ASSERT_EQUAL(expected_current_mode, current_mode); +} + +void test_humidity_control_setpoint_set_command_mapping() +{ + TEST_ASSERT_NOT_NULL( + uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback); + + SetpointType expected_setpoint_type_ucl = ZCL_SETPOINT_TYPE_DEHUMIDIFIER; + uint8_t expected_precision_ucl = 2; + uint8_t expected_scale_ucl = 1; + int32_t expected_value_ucl = 10; + + // Always supported + TEST_ASSERT_EQUAL( + SL_STATUS_OK, + uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback( + supporting_node_unid, + endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_SUPPORT_CHECK, + expected_setpoint_type_ucl, + expected_precision_ucl, + expected_scale_ucl, + expected_value_ucl)); + + humidity_control_setpoint_type_t expected_type + = expected_setpoint_type_ucl + 1; + // Emplace with wrong setpoint type + attribute_store_emplace( + endpoint_id_node, + ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_TYPE, + &expected_type, + sizeof(expected_type)); + + // Should fail + TEST_ASSERT_EQUAL( + SL_STATUS_FAIL, + uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback( + supporting_node_unid, + endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL, + expected_setpoint_type_ucl, + expected_precision_ucl, + expected_scale_ucl, + expected_value_ucl)); + + expected_type = expected_setpoint_type_ucl; + // Emplace with wrong setpoint type + attribute_store_node_t current_mode_node = attribute_store_emplace( + endpoint_id_node, + ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_TYPE, + &expected_type, + sizeof(expected_type)); + + TEST_ASSERT_EQUAL( + SL_STATUS_OK, + uic_mqtt_dotdot_unify_humidity_control_setpoint_set_callback( + supporting_node_unid, + endpoint_id, + UIC_MQTT_DOTDOT_CALLBACK_TYPE_NORMAL, + expected_setpoint_type_ucl, + expected_precision_ucl, + expected_scale_ucl, + expected_value_ucl)); + + humidity_control_setpoint_scale_t reported_scale; + humidity_control_setpoint_value_t reported_value; + humidity_control_setpoint_precision_t reported_precision; + + attribute_store_get_child_reported( + current_mode_node, + ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_VALUE_PRECISION, + &reported_precision, + sizeof(reported_precision)); + TEST_ASSERT_EQUAL_MESSAGE(expected_precision_ucl, + reported_precision, + "Precision reported value mismatch"); + attribute_store_get_child_reported( + current_mode_node, + ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_VALUE_SCALE, + &reported_scale, + sizeof(reported_scale)); + TEST_ASSERT_EQUAL_MESSAGE(expected_scale_ucl, + reported_scale, + "Scale reported value mismatch"); + + // Value is desired since it will be sent to end-device + attribute_store_node_t value_node = attribute_store_get_first_child_by_type( + current_mode_node, + ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_VALUE); + + attribute_store_get_desired(value_node, + &reported_value, + sizeof(reported_value)); + TEST_ASSERT_EQUAL_MESSAGE(expected_value_ucl, + reported_value, + "Value desired value mismatch"); +} \ No newline at end of file diff --git a/components/uic_dotdot/dotdot-xml/Unify_HumidityControl.xml b/components/uic_dotdot/dotdot-xml/Unify_HumidityControl.xml new file mode 100644 index 0000000000..0de5acc2b5 --- /dev/null +++ b/components/uic_dotdot/dotdot-xml/Unify_HumidityControl.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/components/uic_dotdot/dotdot-xml/library.xml b/components/uic_dotdot/dotdot-xml/library.xml index 78c9d8140a..78a9ea21ac 100644 --- a/components/uic_dotdot/dotdot-xml/library.xml +++ b/components/uic_dotdot/dotdot-xml/library.xml @@ -491,4 +491,5 @@ applicable to this document can be found in the LICENSE.md file. +