From 06153587106c467960521af3091fc55250ce6fa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20Labb=C3=A9?= Date: Fri, 16 Feb 2024 09:42:39 +0100 Subject: [PATCH] GH-27: Humidity Control Setpoint CC ZPC implementation only Forwarded: https://github.com/SiliconLabs/UnifySDK/pull/27 Bug-SiliconLabs: UIC-3067 Bug-Github: https://github.com/SiliconLabs/UnifySDK/pull/27 --- .../attribute_store_defined_attribute_types.h | 35 + ...ave_command_class_humidity_control_types.h | 16 + .../zpc_attribute_store_type_registration.cpp | 22 + .../zwave_command_classes/CMakeLists.txt | 1 + ..._command_class_humidity_control_setpoint.c | 731 ++++++++++++++ ..._command_class_humidity_control_setpoint.h | 41 + .../zwave_command_classes/test/CMakeLists.txt | 18 +- ...d_class_humidity_control_setpoint_test.cpp | 935 ++++++++++++++++++ 8 files changed, 1797 insertions(+), 2 deletions(-) create mode 100644 applications/zpc/components/zwave_command_classes/src/zwave_command_class_humidity_control_setpoint.c create mode 100644 applications/zpc/components/zwave_command_classes/src/zwave_command_class_humidity_control_setpoint.h create mode 100644 applications/zpc/components/zwave_command_classes/test/zwave_command_class_humidity_control_setpoint_test.cpp diff --git a/applications/zpc/components/zpc_attribute_store/include/attribute_store_defined_attribute_types.h b/applications/zpc/components/zpc_attribute_store/include/attribute_store_defined_attribute_types.h index c0a0b5b75..b140159e9 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 @@ -555,6 +555,41 @@ DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_OPERATING_STATE_VERSIO DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_OPERATING_STATE_CURRENT_STATE, ((COMMAND_CLASS_HUMIDITY_CONTROL_OPERATING_STATE << 8) | 0x02)) +///////////////////////////////////////////////// +// Humidity Control Setpoint Command Class +///< This represents the version of the Humidity Control Mode Command class. +/// zwave_cc_version_t +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_VERSION, + ZWAVE_CC_VERSION_ATTRIBUTE(COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT)) + +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_SUPPORTED_TYPES, + ((COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT << 8) | 0x02)) +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_TYPE, + ((COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT << 8) | 0x03)) +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_SUPPORTED_SCALE, + ((COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT << 8) | 0x04)) + +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_VALUE, + ((COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT << 8) | 0x05)) +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_VALUE_SCALE, + ((COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT << 8) | 0x06)) +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_VALUE_PRECISION, + ((COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT << 8) | 0x07)) + +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_MIN_VALUE, + ((COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT << 8) | 0x08)) +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_MIN_VALUE_SCALE, + ((COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT << 8) | 0x09)) +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_MIN_VALUE_PRECISION, + ((COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT << 8) | 0x0A)) + +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_MAX_VALUE, + ((COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT << 8) | 0x0B)) +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_MAX_VALUE_SCALE, + ((COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT << 8) | 0x0C)) +DEFINE_ATTRIBUTE(ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_MAX_VALUE_PRECISION, + ((COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT << 8) | 0x0D)) + ///////////////////////////////////////////////// // Inclusion Controller Command Class ///< This represents the version of the Inclusion Controller Command class. diff --git a/applications/zpc/components/zpc_attribute_store/include/command_class_types/zwave_command_class_humidity_control_types.h b/applications/zpc/components/zpc_attribute_store/include/command_class_types/zwave_command_class_humidity_control_types.h index 67add5d86..634b4d51a 100644 --- a/applications/zpc/components/zpc_attribute_store/include/command_class_types/zwave_command_class_humidity_control_types.h +++ b/applications/zpc/components/zpc_attribute_store/include/command_class_types/zwave_command_class_humidity_control_types.h @@ -41,6 +41,22 @@ typedef uint8_t humidity_control_supported_modes_t; ///> Humidity Control Operating State. uint8_t typedef uint8_t humidity_control_operating_state_t; +//>> Humidity Control Setpoint CC +///> Humidity Control Setpoint Supported Types Bitmask. uint8_t +typedef uint8_t humidity_control_setpoint_supported_types_t; +///> Humidity Control Setpoint Supported Scale Bitmask. uint8_t +typedef uint8_t humidity_control_setpoint_supported_scales_t; +///> Humidity Control Setpoint Type. uint8_t +typedef uint8_t humidity_control_setpoint_type_t; +///> Humidity Control Setpoint Value. int32_t +typedef int32_t humidity_control_setpoint_value_t; +///> Humidity Control Setpoint Scale. uint8_t +typedef uint8_t humidity_control_setpoint_scale_t; +///> Humidity Control Setpoint Precision. uint8_t +typedef uint8_t humidity_control_setpoint_precision_t; +///> Humidity Control Setpoint Size. uint8_t +typedef uint8_t humidity_control_setpoint_size_t; + #ifdef __cplusplus extern "C" { #endif 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 379afc5cc..57a97d77c 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 @@ -207,6 +207,28 @@ static const std::vector attribute_schema = { {ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_OPERATING_STATE_VERSION, "Humidity Control Operating State Version", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE}, {ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_OPERATING_STATE_CURRENT_STATE, "Humidity Control Operating State Current State", ATTRIBUTE_INDICATOR_INDICATOR_ID, U8_STORAGE_TYPE}, + ///////////////////////////////////////////////////////////////////// + // Humidity Control Setpoint Command Class attributes + ///////////////////////////////////////////////////////////////////// + {ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_VERSION, "Humidity Control Setpoint Version", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE}, + {ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_SUPPORTED_TYPES, "Humidity Control Supported Setpoint Types", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE}, + {ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_TYPE, "Humidity Control Setpoint Type", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE}, + + {ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_SUPPORTED_SCALE, "Humidity Control Supported Scales", ATTRIBUTE_ENDPOINT_ID, U8_STORAGE_TYPE}, + + {ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_VALUE, "Value", ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_TYPE, I32_STORAGE_TYPE}, + {ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_VALUE_SCALE, "Value Scale", ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_VALUE, U8_STORAGE_TYPE}, + {ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_VALUE_PRECISION, "Value Precision", ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_VALUE, U8_STORAGE_TYPE}, + + {ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_MIN_VALUE, "Min Value", ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_TYPE, I32_STORAGE_TYPE}, + {ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_MIN_VALUE_SCALE, "Min Value Scale", ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_MIN_VALUE, U8_STORAGE_TYPE}, + {ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_MIN_VALUE_PRECISION, "Min Value Precision", ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_MIN_VALUE, U8_STORAGE_TYPE}, + + {ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_MAX_VALUE, "Max Value", ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_TYPE, I32_STORAGE_TYPE}, + {ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_MAX_VALUE_SCALE, "Max Value Scale", ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_MAX_VALUE, U8_STORAGE_TYPE}, + {ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_MAX_VALUE_PRECISION, "Max Value Precision", ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_MAX_VALUE, U8_STORAGE_TYPE}, + + ///////////////////////////////////////////////////////////////////// // Meter Command Class attributes ///////////////////////////////////////////////////////////////////// diff --git a/applications/zpc/components/zwave_command_classes/CMakeLists.txt b/applications/zpc/components/zwave_command_classes/CMakeLists.txt index 85dd73816..c417dfbaa 100644 --- a/applications/zpc/components/zwave_command_classes/CMakeLists.txt +++ b/applications/zpc/components/zwave_command_classes/CMakeLists.txt @@ -23,6 +23,7 @@ add_library( src/zwave_command_class_firmware_update.c src/zwave_command_class_humidity_control_mode.c src/zwave_command_class_humidity_control_operating_state.c + src/zwave_command_class_humidity_control_setpoint.c src/zwave_command_class_indicator.c src/zwave_command_class_indicator_control.cpp src/zwave_command_class_manufacturer_specific.c diff --git a/applications/zpc/components/zwave_command_classes/src/zwave_command_class_humidity_control_setpoint.c b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_humidity_control_setpoint.c new file mode 100644 index 000000000..5fbe9a77b --- /dev/null +++ b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_humidity_control_setpoint.c @@ -0,0 +1,731 @@ + +/****************************************************************************** + * # 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_humidity_control_setpoint.h" +#include "zwave_command_class_humidity_control_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_humidity_control_setpoint" +#define ATTRIBUTE(type) ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_##type + +// Aliases +#define MASK_PRECISION \ + HUMIDITY_CONTROL_SETPOINT_CAPABILITIES_REPORT_PROPERTIES2_PRECISION1_MASK +#define MASK_SCALE \ + HUMIDITY_CONTROL_SETPOINT_CAPABILITIES_REPORT_PROPERTIES2_SCALE1_MASK +#define MASK_SIZE \ + HUMIDITY_CONTROL_SETPOINT_CAPABILITIES_REPORT_PROPERTIES2_SIZE1_MASK + +// Max values in Humidity Control Setpoint Supported Report values +#define MAX_SUPPORTED_SETPOINT_MODES 4 + +enum humidity_control_setpoint_value_type { + SETPOINT_CURRENT_VALUE, + SETPOINT_MIN_VALUE, + SETPOINT_MAX_VALUE +}; + +///////////////////////////////////////////////////////////////////////////// +// Utils +///////////////////////////////////////////////////////////////////////////// + +/** + * @brief Check if current value can be set (in bounds of value_min and value_max) + * + * If value_min and value_max field doesn't exists, it will return false + * + * @param setpoint_node Current setpoint node + * @param value Value to check + * + * @return true Value in bounds + * @return false Value not in bounds or bounds cannot be found + */ +bool check_setpoint_value_bounds(attribute_store_node_t setpoint_node, + humidity_control_setpoint_value_t value) +{ + humidity_control_setpoint_value_t min_value; + humidity_control_setpoint_value_t max_value; + + sl_status_t status = attribute_store_get_child_reported(setpoint_node, + ATTRIBUTE(MIN_VALUE), + &min_value, + sizeof(min_value)); + + if (status != SL_STATUS_OK) { + sl_log_error(LOG_TAG, "Can't get MIN_VALUE attribute value"); + return false; + } + + status = attribute_store_get_child_reported(setpoint_node, + ATTRIBUTE(MAX_VALUE), + &max_value, + sizeof(max_value)); + + if (status != SL_STATUS_OK) { + sl_log_error(LOG_TAG, "Can't get MAX_VALUE attribute value"); + return false; + } + + bool result = value >= min_value && value <= max_value; + + if (!result) { + sl_log_error(LOG_TAG, "Value is out of bounds"); + } + + return result; +} + +/** + * @brief Interpret value from a report frame and store it in the attribute store + * + * @warning This function doesn't check any bounds in frame_data + * + * @param setpoint_node Current setpoint node + * @param value_type Determines the attribute field of value report (min_value, max_value or value) + * @param frame_data Frame raw data + * @param start_index Starting index in the frame raw data + * + * @return uint8_t Byte read count + */ +uint8_t interpret_value_from_report( + attribute_store_node_t setpoint_node, + enum humidity_control_setpoint_value_type value_type, + const uint8_t *frame_data, + uint8_t start_index) +{ + // Save current index + uint8_t current_index = start_index; + // Determine used types + attribute_store_type_t value_store_type; + attribute_store_type_t value_scale_type; + attribute_store_type_t value_precision_type; + switch (value_type) { + case SETPOINT_CURRENT_VALUE: + value_store_type = ATTRIBUTE(VALUE); + value_scale_type = ATTRIBUTE(VALUE_SCALE); + value_precision_type = ATTRIBUTE(VALUE_PRECISION); + break; + case SETPOINT_MIN_VALUE: + value_store_type = ATTRIBUTE(MIN_VALUE); + value_scale_type = ATTRIBUTE(MIN_VALUE_SCALE); + value_precision_type = ATTRIBUTE(MIN_VALUE_PRECISION); + break; + case SETPOINT_MAX_VALUE: + value_store_type = ATTRIBUTE(MAX_VALUE); + value_scale_type = ATTRIBUTE(MAX_VALUE_SCALE); + value_precision_type = ATTRIBUTE(MAX_VALUE_PRECISION); + break; + default: + sl_log_error(LOG_TAG, "Invalid humidity_control_setpoint_value_type"); + return 0; + } + + humidity_control_setpoint_precision_t precision + = (frame_data[current_index] & MASK_PRECISION) >> 5; + + humidity_control_setpoint_scale_t scale + = (frame_data[current_index] & MASK_SCALE) >> 3; + + humidity_control_setpoint_size_t size = frame_data[current_index] & MASK_SIZE; + + if (size > 4) { + sl_log_error(LOG_TAG, "Incorrect reported size for setpoint capabilities"); + return 0; + } + + // Next field(s) (value) + current_index++; + + humidity_control_setpoint_value_t value + = command_class_get_int32_value(size, + precision, + &frame_data[current_index]); + + // Check bounds + if (value_type == SETPOINT_CURRENT_VALUE + && !check_setpoint_value_bounds(setpoint_node, value)) { + return 0; + } + + // Create the value node if missing + attribute_store_node_t value_node + = attribute_store_create_child_if_missing(setpoint_node, value_store_type); + + // Set computed value + attribute_store_set_reported(value_node, &value, sizeof(value)); + + // Set scale value + attribute_store_set_child_reported(value_node, + value_scale_type, + &scale, + sizeof(scale)); + // Set precision value + attribute_store_set_child_reported(value_node, + value_precision_type, + &precision, + sizeof(precision)); + // Return # of bytes read + return 1 + size; +} + +// Remove all ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_TYPE from attribute store +void remove_all_humidity_setpoint_type_attributes( + attribute_store_node_t endpoint_node) +{ + attribute_store_node_t type_node; + do { + type_node = attribute_store_get_node_child_by_type(endpoint_node, + ATTRIBUTE(TYPE), + 0); + attribute_store_delete_node(type_node); + } while (type_node != ATTRIBUTE_STORE_INVALID_NODE); +} + +// Create humidity setpoint +void create_humidity_setpoint_type_node( + attribute_store_node_t endpoint_node, + humidity_control_setpoint_type_t setpoint_type) +{ + // Check compatibility + // if (!is_thermostat_setpoint_mode_compatible_with_version(type, version)) { + // return ATTRIBUTE_STORE_INVALID_NODE; + // } + + attribute_store_node_t type_node + = attribute_store_emplace(endpoint_node, + ATTRIBUTE(TYPE), + &setpoint_type, + sizeof(setpoint_type)); + + // Add the six other nodes under the type. + const attribute_store_type_t additional_nodes[] + = {// Scale support to check value reported (Not supported for now) + // ATTRIBUTE(SUPPORTED_SCALE), + // Min and Max attribute first to check value reported + ATTRIBUTE(MIN_VALUE), + ATTRIBUTE(MAX_VALUE), + // Finally, actual value + ATTRIBUTE(VALUE)}; + + attribute_store_add_if_missing(type_node, + additional_nodes, + COUNT_OF(additional_nodes)); +} + +// Create ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_TYPE based on bitmask in supported_types +// This function will also check version +void create_all_supported_humidity_setpoint_type( + attribute_store_node_t endpoint_node, + humidity_control_setpoint_supported_types_t supported_types) +{ + // Contains current bit tested + uint8_t setpoint_mode_current_bit = 0x0; + + for (uint8_t i = 0; i <= MAX_SUPPORTED_SETPOINT_MODES; i++) { + setpoint_mode_current_bit = 1 << i; + setpoint_mode_current_bit &= supported_types; + humidity_control_setpoint_type_t current_type; + + // Check if current bit tested correspond to a registered type + switch (setpoint_mode_current_bit) { + case 0x02: + current_type + = HUMIDITY_CONTROL_SETPOINT_REPORT_SETPOINT_TYPE_HUMIDIFIER; + break; + case 0x04: + current_type + = HUMIDITY_CONTROL_SETPOINT_REPORT_SETPOINT_TYPE_DEHUMIDIFIER; + break; + case 0x08: + current_type = HUMIDITY_CONTROL_SETPOINT_REPORT_SETPOINT_TYPE_AUTO_V2; + break; + default: + sl_log_error( + LOG_TAG, + "Invalid bit in Humidity Control SetPoint supported types"); + continue; + } + + create_humidity_setpoint_type_node(endpoint_node, current_type); + } +} + +// Get associated ATTRIBUTE(TYPE) from current_node. +// The ATTRIBUTE(TYPE) should be a parent of current_node +humidity_control_setpoint_type_t + get_associated_setpoint_type(attribute_store_node_t current_node) +{ + attribute_store_node_t type_node + = attribute_store_get_first_parent_with_type(current_node, ATTRIBUTE(TYPE)); + + if (type_node == ATTRIBUTE_STORE_INVALID_NODE) { + sl_log_error(LOG_TAG, "Can't get type node."); + return 0; + } + + humidity_control_setpoint_type_t current_type; + sl_status_t status = attribute_store_get_reported(type_node, + ¤t_type, + sizeof(current_type)); + + if (status != SL_STATUS_OK) { + sl_log_error(LOG_TAG, "Can't get type node reported value."); + return 0; + } + + return current_type; +} + +// Get setpoint node with specified value +attribute_store_node_t + get_setpoint_node(attribute_store_node_t endpoint_node, + humidity_control_setpoint_type_t setpoint_type) +{ + return attribute_store_get_node_child_by_value(endpoint_node, + ATTRIBUTE(TYPE), + REPORTED_ATTRIBUTE, + &setpoint_type, + sizeof(setpoint_type), + 0); +} + +///////////////////////////////////////////////////////////////////////////// +// Version & Attribute Creation +///////////////////////////////////////////////////////////////////////////// +static void + zwave_command_class_humidity_control_setpoint_on_version_attribute_update( + attribute_store_node_t updated_node, attribute_store_change_t change) +{ + if (change == ATTRIBUTE_DELETED) { + 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(SUPPORTED_TYPES)}; + + attribute_store_add_if_missing(endpoint_node, + attributes, + COUNT_OF(attributes)); +} + + +static void zwave_command_class_humidity_control_setpoint_on_scale_attribute_update( + attribute_store_node_t updated_node, attribute_store_change_t change) { + if (change == ATTRIBUTE_DELETED) { + return; + } + + humidity_control_setpoint_scale_t scale; + attribute_store_get_reported(updated_node, &scale, sizeof(scale)); + + // Nothing to do here + if (scale <= 1) { + return; + } + + sl_log_warning(LOG_TAG, "Unsupported scale value, setting scale to 0."); + + // Default value + scale = 0; + // Else if value is not supported + attribute_store_set_reported(updated_node, &scale, sizeof(scale)); +} + + +///////////////////////////////////////////////////////////////////////////// +// Humidity Control SetPoint Supported Types Get/Report +///////////////////////////////////////////////////////////////////////////// +static sl_status_t + zwave_command_class_humidity_control_setpoint_supported_types_get( + attribute_store_node_t node, uint8_t *frame, uint16_t *frame_length) +{ + (void)node; // unused. + ZW_HUMIDITY_CONTROL_SETPOINT_SUPPORTED_GET_FRAME *get_frame + = (ZW_HUMIDITY_CONTROL_SETPOINT_SUPPORTED_GET_FRAME *)frame; + get_frame->cmdClass = COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT; + get_frame->cmd = HUMIDITY_CONTROL_SETPOINT_SUPPORTED_GET; + *frame_length = sizeof(ZW_HUMIDITY_CONTROL_SETPOINT_SUPPORTED_GET_FRAME); + return SL_STATUS_OK; +} + +sl_status_t + zwave_command_class_humidity_control_setpoint_supported_handle_report( + const zwave_controller_connection_info_t *connection_info, + const uint8_t *frame_data, + uint16_t frame_length) +{ + // We expect a frame length of 3 since the bitmask is supposed to be on a single byte + if (frame_length != 3) { + return SL_STATUS_NOT_SUPPORTED; + } + + humidity_control_setpoint_supported_types_t supported_types = frame_data[2]; + + attribute_store_node_t endpoint_node + = zwave_command_class_get_endpoint_node(connection_info); + + attribute_store_set_child_reported(endpoint_node, + ATTRIBUTE(SUPPORTED_TYPES), + &supported_types, + sizeof(supported_types)); + + // First remove all existing type nodes + // This is done to easily update the bitmask or bitmask interpretation so we don't have any leftovers. + remove_all_humidity_setpoint_type_attributes(endpoint_node); + // Then create all type nodes + create_all_supported_humidity_setpoint_type(endpoint_node, supported_types); + + return SL_STATUS_OK; +} + +///////////////////////////////////////////////////////////////////////////// +// Humidity Control SetPoint Capabilities Get/Report +///////////////////////////////////////////////////////////////////////////// +static sl_status_t + zwave_command_class_humidity_control_setpoint_capabilities_get( + attribute_store_node_t node, uint8_t *frame, uint16_t *frame_length) +{ + humidity_control_setpoint_type_t setpoint_type + = get_associated_setpoint_type(node); + + if (setpoint_type == 0) { + sl_log_error(LOG_TAG, "Can't get setpoint type for GET Capabilities."); + return SL_STATUS_NOT_SUPPORTED; + } + + ZW_HUMIDITY_CONTROL_SETPOINT_CAPABILITIES_GET_FRAME *get_frame + = (ZW_HUMIDITY_CONTROL_SETPOINT_CAPABILITIES_GET_FRAME *)frame; + get_frame->cmdClass = COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT; + get_frame->cmd = HUMIDITY_CONTROL_SETPOINT_CAPABILITIES_GET; + get_frame->properties1 = setpoint_type; + *frame_length = sizeof(ZW_HUMIDITY_CONTROL_SETPOINT_CAPABILITIES_GET_FRAME); + return SL_STATUS_OK; +} + +sl_status_t + zwave_command_class_humidity_control_setpoint_capabilities_handle_report( + const zwave_controller_connection_info_t *connection_info, + const uint8_t *frame_data, + uint16_t frame_length) +{ + if (frame_length < 5) { + return SL_STATUS_NOT_SUPPORTED; + } + + humidity_control_setpoint_type_t setpoint_type + = frame_data[2] + & HUMIDITY_CONTROL_SETPOINT_CAPABILITIES_REPORT_PROPERTIES1_SETPOINT_TYPE_MASK; + + attribute_store_node_t endpoint_node + = zwave_command_class_get_endpoint_node(connection_info); + + attribute_store_node_t setpoint_node + = get_setpoint_node(endpoint_node, setpoint_type); + + if (setpoint_node == ATTRIBUTE_STORE_INVALID_NODE) { + sl_log_error( + LOG_TAG, + "Can't get setpoint type node for setpoint capabilities report"); + return SL_STATUS_NOT_SUPPORTED; + } + + const uint8_t current_index = 3; + uint8_t byte_read_count = interpret_value_from_report(setpoint_node, + SETPOINT_MIN_VALUE, + frame_data, + current_index); + + // If we didn't read anything or if we will go OOB + if (byte_read_count == 0 + || (current_index + byte_read_count + 1) >= frame_length) { + return SL_STATUS_NOT_SUPPORTED; + } + + byte_read_count + = interpret_value_from_report(setpoint_node, + SETPOINT_MAX_VALUE, + frame_data, + current_index + byte_read_count); + + if (byte_read_count == 0) { + return SL_STATUS_NOT_SUPPORTED; + } + + return SL_STATUS_OK; +} + +///////////////////////////////////////////////////////////////////////////// +// Humidity Control SetPoint Capabilities Set/Get/Report +///////////////////////////////////////////////////////////////////////////// +static sl_status_t zwave_command_class_humidity_control_setpoint_set( + attribute_store_node_t node, uint8_t *frame, uint16_t *frame_length) +{ + humidity_control_setpoint_type_t setpoint_type + = get_associated_setpoint_type(node); + + if (setpoint_type == 0) { + sl_log_error(LOG_TAG, "Can't get setpoint type for SET."); + return SL_STATUS_NOT_SUPPORTED; + } + + humidity_control_setpoint_value_t desired_value; + attribute_store_get_desired(node, &desired_value, sizeof(desired_value)); + + attribute_store_node_t setpoint_node = attribute_store_get_node_parent(node); + if (!check_setpoint_value_bounds(setpoint_node, desired_value)) { + return SL_STATUS_NOT_SUPPORTED; + } + + humidity_control_setpoint_precision_t precision; + sl_status_t status + = attribute_store_get_child_reported(node, + ATTRIBUTE(VALUE_PRECISION), + &precision, + sizeof(precision)); + + if (status != SL_STATUS_OK) { + sl_log_error(LOG_TAG, "Can't get precision for SET."); + return SL_STATUS_NOT_SUPPORTED; + } + + humidity_control_setpoint_scale_t scale; + status = attribute_store_get_child_reported(node, + ATTRIBUTE(VALUE_SCALE), + &scale, + sizeof(scale)); + + if (status != SL_STATUS_OK) { + sl_log_error(LOG_TAG, "Can't get scale for SET."); + return SL_STATUS_NOT_SUPPORTED; + } + + uint8_t value_properties_field = (precision << 5) | (scale << 3); + + if (abs(desired_value) < INT8_MAX) { + ZW_HUMIDITY_CONTROL_SETPOINT_SET_1BYTE_FRAME *set_frame + = (ZW_HUMIDITY_CONTROL_SETPOINT_SET_1BYTE_FRAME *)frame; + set_frame->cmdClass = COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT; + set_frame->cmd = HUMIDITY_CONTROL_SETPOINT_SET; + set_frame->properties1 = setpoint_type; + set_frame->properties2 = value_properties_field | 1; + + set_frame->value1 = (uint8_t)(desired_value & 0x000000FF); + *frame_length = sizeof(ZW_HUMIDITY_CONTROL_SETPOINT_SET_1BYTE_FRAME); + + } else if (abs(desired_value) < INT16_MAX) { + ZW_HUMIDITY_CONTROL_SETPOINT_SET_2BYTE_FRAME *set_frame + = (ZW_HUMIDITY_CONTROL_SETPOINT_SET_2BYTE_FRAME *)frame; + set_frame->cmdClass = COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT; + set_frame->cmd = HUMIDITY_CONTROL_SETPOINT_SET; + set_frame->properties1 = setpoint_type; + set_frame->properties2 = value_properties_field | 2; + + set_frame->value1 = (uint8_t)((desired_value & 0x0000FF00) >> 8); + set_frame->value2 = (uint8_t)(desired_value & 0x000000FF); + + + *frame_length = sizeof(ZW_HUMIDITY_CONTROL_SETPOINT_SET_2BYTE_FRAME); + } else if (abs(desired_value) < INT32_MAX) { + ZW_HUMIDITY_CONTROL_SETPOINT_SET_4BYTE_FRAME *set_frame + = (ZW_HUMIDITY_CONTROL_SETPOINT_SET_4BYTE_FRAME *)frame; + set_frame->cmdClass = COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT; + set_frame->cmd = HUMIDITY_CONTROL_SETPOINT_SET; + set_frame->properties1 = setpoint_type; + set_frame->properties2 = value_properties_field | 4; + + set_frame->value1 = (uint8_t)((desired_value & 0xFF000000) >> 24); // MSB + set_frame->value2 = (uint8_t)((desired_value & 0x00FF0000) >> 16); + set_frame->value3 = (uint8_t)((desired_value & 0x0000FF00) >> 8); + set_frame->value4 = (uint8_t)(desired_value & 0x000000FF); // LSB + + *frame_length = sizeof(ZW_HUMIDITY_CONTROL_SETPOINT_SET_4BYTE_FRAME); + } else { + sl_log_error(LOG_TAG, "Invalid desired value size"); + return SL_STATUS_NOT_SUPPORTED; + } + + return SL_STATUS_OK; +} + +static sl_status_t zwave_command_class_humidity_control_setpoint_get( + attribute_store_node_t node, uint8_t *frame, uint16_t *frame_length) +{ + humidity_control_setpoint_type_t setpoint_type + = get_associated_setpoint_type(node); + + if (setpoint_type == 0) { + sl_log_error(LOG_TAG, "Can't get setpoint type for GET."); + return SL_STATUS_NOT_SUPPORTED; + } + + ZW_HUMIDITY_CONTROL_SETPOINT_GET_FRAME *set_frame + = (ZW_HUMIDITY_CONTROL_SETPOINT_GET_FRAME *)frame; + set_frame->cmdClass = COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT; + set_frame->cmd = HUMIDITY_CONTROL_SETPOINT_GET; + set_frame->properties1 = setpoint_type; + *frame_length = sizeof(ZW_HUMIDITY_CONTROL_SETPOINT_GET_FRAME); + return SL_STATUS_OK; +} + +sl_status_t zwave_command_class_humidity_control_setpoint_handle_report( + const zwave_controller_connection_info_t *connection_info, + const uint8_t *frame_data, + uint16_t frame_length) +{ + if (frame_length < 5) { + return SL_STATUS_NOT_SUPPORTED; + } + + humidity_control_setpoint_type_t setpoint_type + = frame_data[2] + & HUMIDITY_CONTROL_SETPOINT_REPORT_PROPERTIES1_SETPOINT_TYPE_MASK; + + attribute_store_node_t endpoint_node + = zwave_command_class_get_endpoint_node(connection_info); + + attribute_store_node_t setpoint_node + = get_setpoint_node(endpoint_node, setpoint_type); + + if (setpoint_node == ATTRIBUTE_STORE_INVALID_NODE) { + sl_log_error(LOG_TAG, "Can't get setpoint type node for setpoint report"); + return SL_STATUS_NOT_SUPPORTED; + } + + const uint8_t current_index = 3; + uint8_t byte_read_count = interpret_value_from_report(setpoint_node, + SETPOINT_CURRENT_VALUE, + frame_data, + current_index); + + // If we didn't read anything + if (byte_read_count == 0) { + return SL_STATUS_NOT_SUPPORTED; + } + + return SL_STATUS_OK; +} + +///////////////////////////////////////////////////////////////////////////// +// Class logic +///////////////////////////////////////////////////////////////////////////// + +// Control handler +sl_status_t zwave_command_class_humidity_control_setpoint_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 HUMIDITY_CONTROL_SETPOINT_SUPPORTED_REPORT: + return zwave_command_class_humidity_control_setpoint_supported_handle_report( + connection_info, + frame_data, + frame_length); + case HUMIDITY_CONTROL_SETPOINT_CAPABILITIES_REPORT: + return zwave_command_class_humidity_control_setpoint_capabilities_handle_report( + connection_info, + frame_data, + frame_length); + case HUMIDITY_CONTROL_SETPOINT_REPORT: + return zwave_command_class_humidity_control_setpoint_handle_report( + connection_info, + frame_data, + frame_length); + default: + return SL_STATUS_NOT_SUPPORTED; + } +} + +// Entry point +sl_status_t zwave_command_class_humidity_control_setpoint_init() +{ + attribute_store_register_callback_by_type( + &zwave_command_class_humidity_control_setpoint_on_version_attribute_update, + ATTRIBUTE(VERSION)); + + attribute_resolver_register_rule( + ATTRIBUTE(SUPPORTED_TYPES), + NULL, + &zwave_command_class_humidity_control_setpoint_supported_types_get); + + // Only monitor one of ATTRIBUTE(MIN_VALUE) or ATTRIBUTE(MAX_VALUE) we don't need both + attribute_resolver_register_rule( + ATTRIBUTE(MIN_VALUE), + NULL, + &zwave_command_class_humidity_control_setpoint_capabilities_get); + + // Only monitor one of ATTRIBUTE(MIN_VALUE) or ATTRIBUTE(MAX_VALUE) we don't need both + attribute_resolver_register_rule( + ATTRIBUTE(VALUE), + &zwave_command_class_humidity_control_setpoint_set, + &zwave_command_class_humidity_control_setpoint_get); + + // Scale limits + attribute_store_register_callback_by_type( + &zwave_command_class_humidity_control_setpoint_on_scale_attribute_update, + ATTRIBUTE(VALUE_SCALE)); + attribute_store_register_callback_by_type( + &zwave_command_class_humidity_control_setpoint_on_scale_attribute_update, + ATTRIBUTE(MIN_VALUE_SCALE)); + attribute_store_register_callback_by_type( + &zwave_command_class_humidity_control_setpoint_on_scale_attribute_update, + ATTRIBUTE(MAX_VALUE_SCALE)); + + zwave_command_handler_t handler = {}; + handler.support_handler = NULL; + handler.control_handler + = zwave_command_class_humidity_control_setpoint_control_handler; + handler.minimal_scheme = ZWAVE_CONTROLLER_ENCAPSULATION_NONE; + handler.manual_security_validation = false; + handler.command_class = COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT; + handler.version = 2; + handler.command_class_name = "Humidity Control SetPoint"; + handler.comments = "Experimental. Supported Scale command not supported " + "since the report doesn't contain Setpoint type."; + + 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_humidity_control_setpoint.h b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_humidity_control_setpoint.h new file mode 100644 index 000000000..994fbd2ef --- /dev/null +++ b/applications/zpc/components/zwave_command_classes/src/zwave_command_class_humidity_control_setpoint.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_humidity_control_setpoint + * @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_HUMIDITY_CONTROL_SETPOINT_H +#define ZWAVE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_H + +#include "sl_status.h" + +#ifdef __cplusplus +extern "C" { +#endif + +sl_status_t zwave_command_class_humidity_control_setpoint_init(); + +#ifdef __cplusplus +} +#endif + +#endif //ZWAVE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_H + /** @} end zwave_command_class_humidity_control_setpoint */ \ No newline at end of file diff --git a/applications/zpc/components/zwave_command_classes/test/CMakeLists.txt b/applications/zpc/components/zwave_command_classes/test/CMakeLists.txt index 3c60bd5ea..54a1d9f03 100644 --- a/applications/zpc/components/zwave_command_classes/test/CMakeLists.txt +++ b/applications/zpc/components/zwave_command_classes/test/CMakeLists.txt @@ -742,7 +742,7 @@ target_add_unittest( zpc_attribute_resolver_mock uic_dotdot_mqtt_mock) - # Humidity Control Mode test + # Humidity Control Operating State test target_add_unittest( zwave_command_classes NAME @@ -757,5 +757,19 @@ target_add_unittest( zpc_attribute_resolver_mock uic_dotdot_mqtt_mock) - + # Humidity Control SetPoint test + target_add_unittest( + zwave_command_classes + NAME + zwave_command_class_humidity_control_setpoint_test + SOURCES + zwave_command_class_humidity_control_setpoint_test.cpp + DEPENDS + zpc_attribute_store_test_helper + zwave_controller + zwave_command_handler_mock + uic_attribute_resolver_mock + zpc_attribute_resolver_mock + uic_dotdot_mqtt_mock) + \ No newline at end of file diff --git a/applications/zpc/components/zwave_command_classes/test/zwave_command_class_humidity_control_setpoint_test.cpp b/applications/zpc/components/zwave_command_classes/test/zwave_command_class_humidity_control_setpoint_test.cpp new file mode 100644 index 000000000..bd9386ade --- /dev/null +++ b/applications/zpc/components/zwave_command_classes/test/zwave_command_class_humidity_control_setpoint_test.cpp @@ -0,0 +1,935 @@ +/****************************************************************************** + * # 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 +#include "workaround_for_test.hpp" + +extern "C" { +#include "zwave_command_class_humidity_control_setpoint.h" +#include "zwave_command_class_humidity_control_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" + +#define ATTRIBUTE(type) ATTRIBUTE_COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT_##type + +constexpr uint8_t PRECISION_SHIFT = 5; +constexpr uint8_t SCALE_SHIFT = 3; + +static zwave_command_handler_t handler = {}; + +static attribute_resolver_function_t supported_types_get = NULL; +static attribute_resolver_function_t capabilities_get = NULL; +static attribute_resolver_function_t setpoint_get = NULL; +static attribute_resolver_function_t setpoint_set = NULL; +// Buffer for frame +static uint8_t received_frame[255] = {}; +static uint16_t received_frame_size = 0; +// Custom types +enum humidity_control_setpoint_value_type { + SETPOINT_CURRENT_VALUE, + SETPOINT_MIN_VALUE, + SETPOINT_MAX_VALUE +}; +enum set_bytes { SET_1BYTES = 1, SET_2BYTES = 2, SET_4BYTES = 4 }; + +///////////////////////////////////////////////////// +// HELPERS +///////////////////////////////////////////////////// +attribute_store_node_t create_type_node(humidity_control_setpoint_type_t type) +{ + attribute_store_node_t type_node = attribute_store_emplace(endpoint_id_node, + ATTRIBUTE(TYPE), + &type, + sizeof(type)); + + TEST_ASSERT_NOT_EQUAL_MESSAGE(ATTRIBUTE_STORE_INVALID_NODE, + type_node, + "Should be able to crate ATTRIBUTE(TYPE)"); + return type_node; +} + +void helper_test_type_values( + enum humidity_control_setpoint_value_type value_type, + attribute_store_node_t value_node, + humidity_control_setpoint_precision_t precision, + humidity_control_setpoint_scale_t scale, + humidity_control_setpoint_size_t size, + uint8_t *value_buffer) +{ + // Determine used types + attribute_store_type_t value_scale_type; + attribute_store_type_t value_precision_type; + switch (value_type) { + case SETPOINT_CURRENT_VALUE: + printf("Testing current value type\n"); + value_scale_type = ATTRIBUTE(VALUE_SCALE); + value_precision_type = ATTRIBUTE(VALUE_PRECISION); + break; + case SETPOINT_MIN_VALUE: + printf("Testing min value type\n"); + value_scale_type = ATTRIBUTE(MIN_VALUE_SCALE); + value_precision_type = ATTRIBUTE(MIN_VALUE_PRECISION); + break; + case SETPOINT_MAX_VALUE: + printf("Testing max value type\n"); + value_scale_type = ATTRIBUTE(MAX_VALUE_SCALE); + value_precision_type = ATTRIBUTE(MAX_VALUE_PRECISION); + break; + default: + TEST_ABORT(); + } + + // Check Value + humidity_control_setpoint_value_t reported_value, expected_value; + attribute_store_get_reported(value_node, + &reported_value, + sizeof(reported_value)); + expected_value + = command_class_get_int32_value(size, precision, &value_buffer[0]); + + TEST_ASSERT_EQUAL_MESSAGE(expected_value, + reported_value, + "VALUE is incorrect"); + + humidity_control_setpoint_scale_t reported_scale; + sl_status_t status + = attribute_store_get_child_reported(value_node, + value_scale_type, + &reported_scale, + sizeof(reported_scale)); + TEST_ASSERT_EQUAL_MESSAGE( + SL_STATUS_OK, + status, + "Should have access to reported value of SCALE attribute"); + + TEST_ASSERT_EQUAL_MESSAGE(scale, reported_scale, "SCALE value is incorrect"); + + humidity_control_setpoint_precision_t reported_precision; + status = attribute_store_get_child_reported(value_node, + value_precision_type, + &reported_precision, + sizeof(reported_precision)); + TEST_ASSERT_EQUAL_MESSAGE( + SL_STATUS_OK, + status, + "Should have access to reported value of PRECISION attribute"); + + TEST_ASSERT_EQUAL_MESSAGE(precision, + reported_precision, + "PRECISION value is incorrect"); +} + +// Return value node +attribute_store_node_t + create_setpoint_structure(humidity_control_setpoint_type_t type, + humidity_control_setpoint_value_t current_value, + humidity_control_setpoint_precision_t precision, + humidity_control_setpoint_scale_t scale, + humidity_control_setpoint_value_t min_value, + humidity_control_setpoint_value_t max_value) +{ + attribute_store_node_t type_node = create_type_node(type); + + attribute_store_emplace(type_node, + ATTRIBUTE(MIN_VALUE), + &min_value, + sizeof(min_value)); + attribute_store_emplace(type_node, + ATTRIBUTE(MAX_VALUE), + &max_value, + sizeof(max_value)); + + attribute_store_node_t value_node + = attribute_store_emplace_desired(type_node, + ATTRIBUTE(VALUE), + ¤t_value, + sizeof(current_value)); + attribute_store_emplace(value_node, + ATTRIBUTE(VALUE_PRECISION), + &precision, + sizeof(precision)); + attribute_store_emplace(value_node, + ATTRIBUTE(VALUE_SCALE), + &scale, + sizeof(scale)); + + return value_node; +} + +uint8_t + helper_get_attribute_field(humidity_control_setpoint_precision_t precision, + humidity_control_setpoint_scale_t scale, + humidity_control_setpoint_size_t size) +{ + return (precision << PRECISION_SHIFT) | (scale << SCALE_SHIFT) | size; +} + +std::vector explode_value(uint8_t size, + humidity_control_setpoint_value_t value) +{ + std::vector exploded_value; + for (uint8_t i = size; i > 0; i--) { + uint8_t offset = i - 1; + uint8_t shift = 8 * offset; + uint32_t bitmask = 0xFF << shift; + + uint8_t value_8bit = (value & bitmask) >> shift; + exploded_value.push_back(value_8bit); + } + + return exploded_value; +} + +void helper_set_setpoint(enum set_bytes bytes_count, bool happy_case) +{ + // Ask for a Get Command, should always be the same + TEST_ASSERT_NOT_NULL(setpoint_set); + // Not supported with invalid node + TEST_ASSERT_EQUAL(SL_STATUS_NOT_SUPPORTED, + setpoint_set(0, received_frame, &received_frame_size)); + + humidity_control_setpoint_type_t type + = HUMIDITY_CONTROL_SETPOINT_GET_SETPOINT_TYPE_HUMIDIFIER; + humidity_control_setpoint_value_t current_value = 50; + humidity_control_setpoint_precision_t precision = 3; + humidity_control_setpoint_scale_t scale = 1; + humidity_control_setpoint_value_t min_value = -100; + humidity_control_setpoint_value_t max_value = 100; + std::vector expected_value; + + switch (bytes_count) { + case SET_1BYTES: + current_value = -50; + precision = 3; + scale = 1; + min_value = happy_case ? -100 : -10; + max_value = 10; + break; + case SET_2BYTES: + current_value = INT16_MAX - 2; + precision = 4; + scale = 1; + min_value = -100; + max_value = happy_case ? INT16_MAX : 10; + break; + case SET_4BYTES: + current_value = INT32_MAX - 2; + precision = 1; + scale = 0; + min_value = -100; + max_value = happy_case ? INT32_MAX : 10; + break; + default: + TEST_ABORT(); + } + + // Transform 32 bit value into chunk of 8 bit values + expected_value = explode_value(bytes_count, current_value); + + attribute_store_node_t value_node = create_setpoint_structure(type, + current_value, + precision, + scale, + min_value, + max_value); + + TEST_ASSERT_EQUAL( + happy_case ? SL_STATUS_OK : SL_STATUS_NOT_SUPPORTED, + setpoint_set(value_node, received_frame, &received_frame_size)); + + if (happy_case) { + std::vector expected_frame { + COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT, + HUMIDITY_CONTROL_SETPOINT_SET, + type, + helper_get_attribute_field(precision, scale, bytes_count)}; + + for (uint8_t value: expected_value) { + expected_frame.emplace_back(value); + } + + TEST_ASSERT_EQUAL(expected_frame.size(), received_frame_size); + TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_frame.data(), + received_frame, + received_frame_size); + } +} + +void helper_report_setpoint(enum set_bytes bytes_count, bool happy_case) +{ + humidity_control_setpoint_type_t expected_type + = HUMIDITY_CONTROL_SETPOINT_REPORT_SETPOINT_TYPE_HUMIDIFIER; + + zwave_controller_connection_info_t info = {}; + info.remote.node_id = node_id; + info.remote.endpoint_id = endpoint_id; + info.local.is_multicast = false; + + attribute_store_node_t value_node; + + humidity_control_setpoint_precision_t precision; + humidity_control_setpoint_scale_t scale; + humidity_control_setpoint_value_t value; + + humidity_control_setpoint_size_t size = bytes_count; + + switch (size) { + case SET_1BYTES: + precision = 2; + scale = 1; + value = -40; + value_node = create_setpoint_structure(expected_type, + 0, + 0, + 0, + happy_case ? -420 : 20, + 500); + break; + case SET_2BYTES: + precision = 5; + scale = 1; + value = INT16_MIN + 5; + value_node = create_setpoint_structure(expected_type, + 0, + 0, + 0, + happy_case ? INT16_MIN : 0, + 50); + break; + break; + case SET_4BYTES: + precision = 1; + scale = 0; + value = INT16_MAX + 3256; + value_node + = create_setpoint_structure(expected_type, + 0, + 0, + 0, + happy_case ? INT16_MAX : INT32_MAX - 1, + INT32_MAX); + break; + } + + std::vector expected_frame { + COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT, + HUMIDITY_CONTROL_SETPOINT_REPORT, + expected_type, + helper_get_attribute_field(precision, scale, size)}; + + for (uint8_t field: explode_value(size, value)) { + expected_frame.emplace_back(field); + } + + // Should be ok + TEST_ASSERT_EQUAL_MESSAGE(happy_case ? SL_STATUS_OK : SL_STATUS_NOT_SUPPORTED, + handler.control_handler(&info, + expected_frame.data(), + expected_frame.size()), + "Report handler returned incorrect value"); + + if (happy_case) { + humidity_control_setpoint_value_t reported_value; + attribute_store_get_reported(value_node, + &reported_value, + sizeof(reported_value)); + TEST_ASSERT_EQUAL_MESSAGE( + command_class_get_int32_value(size, + precision, + explode_value(size, value).data()), + reported_value, + "Setpoint value is not correct"); + + humidity_control_setpoint_precision_t reported_precision; + attribute_store_get_child_reported(value_node, + ATTRIBUTE(VALUE_PRECISION), + &reported_precision, + sizeof(reported_precision)); + TEST_ASSERT_EQUAL_MESSAGE(precision, + reported_precision, + "Precision is not correct"); + + humidity_control_setpoint_scale_t reported_scale; + attribute_store_get_child_reported(value_node, + ATTRIBUTE(VALUE_SCALE), + &reported_scale, + sizeof(reported_scale)); + TEST_ASSERT_EQUAL_MESSAGE(scale, reported_scale, "Scale is not correct"); + } +} + +///////////////////////////////////////////////////// +// Test case +///////////////////////////////////////////////////// +// 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(SUPPORTED_TYPES)) { + TEST_ASSERT_NULL(set_func); + TEST_ASSERT_NOT_NULL(get_func); + supported_types_get = get_func; + } else if (node_type == ATTRIBUTE(MIN_VALUE)) { + TEST_ASSERT_NULL(set_func); + TEST_ASSERT_NOT_NULL(get_func); + capabilities_get = get_func; + } else if (node_type == ATTRIBUTE(VALUE)) { + TEST_ASSERT_NOT_NULL(get_func); + TEST_ASSERT_NOT_NULL(set_func); + setpoint_get = get_func; + setpoint_set = set_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_HUMIDITY_CONTROL_SETPOINT, + 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 + supported_types_get = NULL; + setpoint_get = NULL; + setpoint_set = NULL; + capabilities_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_humidity_control_setpoint_init()); +} + +/// Called after each and every test +void tearDown() {} + +//////////////////////////////////////////////////////////////////////////// +// Setpoint Set/Get/Report +//////////////////////////////////////////////////////////////////////////// +void test_humidity_control_setpoint_get_happy_case() +{ + // Ask for a Get Command, should always be the same + TEST_ASSERT_NOT_NULL(setpoint_get); + // Not supported with invalid node + TEST_ASSERT_EQUAL(SL_STATUS_NOT_SUPPORTED, + setpoint_get(0, received_frame, &received_frame_size)); + + humidity_control_setpoint_type_t expected_type + = HUMIDITY_CONTROL_SETPOINT_GET_SETPOINT_TYPE_HUMIDIFIER; + attribute_store_node_t type_node = create_type_node(expected_type); + + // This is the attribute that trigger the setpoint_get + attribute_store_node_t value_node + = attribute_store_add_node(ATTRIBUTE(VALUE), type_node); + + TEST_ASSERT_EQUAL( + SL_STATUS_OK, + setpoint_get(value_node, received_frame, &received_frame_size)); + + const uint8_t expected_frame[] = {COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT, + HUMIDITY_CONTROL_SETPOINT_GET, + expected_type}; + TEST_ASSERT_EQUAL(sizeof(expected_frame), received_frame_size); + TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_frame, + received_frame, + received_frame_size); +} + +void test_humidity_control_setpoint_set_1bytes_happy_case() +{ + helper_set_setpoint(SET_1BYTES, true); +} + +void test_humidity_control_setpoint_set_2bytes_happy_case() +{ + helper_set_setpoint(SET_2BYTES, true); +} + +void test_humidity_control_setpoint_set_4bytes_happy_case() +{ + helper_set_setpoint(SET_4BYTES, true); +} + +void test_humidity_control_setpoint_set_1bytes_out_of_bounds() +{ + helper_set_setpoint(SET_1BYTES, false); +} + +void test_humidity_control_setpoint_set_2bytes_out_of_bounds() +{ + helper_set_setpoint(SET_2BYTES, false); +} + +void test_humidity_control_setpoint_set_4bytes_out_of_bounds() +{ + helper_set_setpoint(SET_4BYTES, false); +} + +void test_humidity_control_setpoint_report_1bytes_happy_case() +{ + helper_report_setpoint(SET_1BYTES, true); + // TODO : Test scale number that doesn't overflow + // TODO : Check sonaar +} + +void test_humidity_control_setpoint_report_2bytes_happy_case() +{ + helper_report_setpoint(SET_2BYTES, true); +} + +void test_humidity_control_setpoint_report_4bytes_happy_case() +{ + helper_report_setpoint(SET_4BYTES, true); +} + +void test_humidity_control_setpoint_report_1bytes_out_of_bounds() +{ + helper_report_setpoint(SET_1BYTES, false); +} +void test_humidity_control_setpoint_report_2bytes_out_of_bounds() +{ + helper_report_setpoint(SET_2BYTES, false); +} +void test_humidity_control_setpoint_report_4bytes_out_of_bounds() +{ + helper_report_setpoint(SET_4BYTES, false); +} + +//////////////////////////////////////////////////////////////////////////// +// Capabilities types Get/Report +//////////////////////////////////////////////////////////////////////////// +void test_humidity_control_setpoint_capabilities_get_happy_case() +{ + // Ask for a Get Command, should always be the same + TEST_ASSERT_NOT_NULL(capabilities_get); + // Not supported with invalid node + TEST_ASSERT_EQUAL(SL_STATUS_NOT_SUPPORTED, + capabilities_get(0, received_frame, &received_frame_size)); + + humidity_control_setpoint_type_t expected_type + = HUMIDITY_CONTROL_SETPOINT_GET_SETPOINT_TYPE_HUMIDIFIER; + attribute_store_node_t type_node + = attribute_store_add_node(ATTRIBUTE(TYPE), endpoint_id_node); + + attribute_store_set_reported(type_node, + &expected_type, + sizeof(expected_type)); + + // This is the attribute that trigger the capabilities_get + attribute_store_node_t min_node + = attribute_store_add_node(ATTRIBUTE(MIN_VALUE), type_node); + + TEST_ASSERT_EQUAL( + SL_STATUS_OK, + capabilities_get(min_node, received_frame, &received_frame_size)); + + const uint8_t expected_frame[] = {COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT, + HUMIDITY_CONTROL_SETPOINT_CAPABILITIES_GET, + expected_type}; + TEST_ASSERT_EQUAL(sizeof(expected_frame), received_frame_size); + TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_frame, + received_frame, + received_frame_size); +} + +void test_humidity_control_setpoint_capabilities_report_happy_case() +{ + humidity_control_setpoint_type_t expected_type + = HUMIDITY_CONTROL_SETPOINT_CAPABILITIES_REPORT_SETPOINT_TYPE_DEHUMIDIFIER_V2; + + zwave_controller_connection_info_t info = {}; + info.remote.node_id = node_id; + info.remote.endpoint_id = endpoint_id; + info.local.is_multicast = false; + + humidity_control_setpoint_precision_t precision_min = 2; + humidity_control_setpoint_scale_t scale_min = 0; + humidity_control_setpoint_size_t size_min = 2; + uint8_t value_min[] = {12, 13}; + + humidity_control_setpoint_precision_t precision_max = 4; + humidity_control_setpoint_scale_t scale_max = 1; + humidity_control_setpoint_size_t size_max = 4; + uint8_t value_max[] = {14, 15, 16, 17}; + + const uint8_t frame[] = { + COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT, + HUMIDITY_CONTROL_SETPOINT_CAPABILITIES_REPORT, + (uint8_t)(0b11110000 + | expected_type), // This test if we properly ignore the first 4 bits + helper_get_attribute_field(precision_min, scale_min, size_min), + value_min[0], + value_min[1], + helper_get_attribute_field(precision_max, scale_max, size_max), + value_max[0], + value_max[1], + value_max[2], + value_max[3]}; + + // Type not found + TEST_ASSERT_EQUAL(SL_STATUS_NOT_SUPPORTED, + handler.control_handler(&info, frame, sizeof(frame))); + + // Create type + attribute_store_node_t type_node = create_type_node(expected_type); + + TEST_ASSERT_EQUAL(SL_STATUS_OK, + handler.control_handler(&info, frame, sizeof(frame))); + + attribute_store_node_t min_node + = attribute_store_get_node_child_by_type(type_node, + ATTRIBUTE(MIN_VALUE), + 0); + + attribute_store_node_t max_node + = attribute_store_get_node_child_by_type(type_node, + ATTRIBUTE(MAX_VALUE), + 0); + + TEST_ASSERT_NOT_EQUAL_MESSAGE(ATTRIBUTE_STORE_INVALID_NODE, + min_node, + "Min value node should be defined"); + TEST_ASSERT_NOT_EQUAL_MESSAGE(ATTRIBUTE_STORE_INVALID_NODE, + max_node, + "Max value node should be defined"); + + helper_test_type_values(SETPOINT_MIN_VALUE, + min_node, + precision_min, + scale_min, + size_min, + value_min); + + helper_test_type_values(SETPOINT_MAX_VALUE, + max_node, + precision_max, + scale_max, + size_max, + value_max); +} + +void test_humidity_control_setpoint_capabilities_report_fail_only_min() +{ + humidity_control_setpoint_type_t expected_type + = HUMIDITY_CONTROL_SETPOINT_CAPABILITIES_REPORT_SETPOINT_TYPE_DEHUMIDIFIER_V2; + + zwave_controller_connection_info_t info = {}; + info.remote.node_id = node_id; + info.remote.endpoint_id = endpoint_id; + info.local.is_multicast = false; + + uint8_t precision_min = 2; + uint8_t scale_min = 0; + uint8_t size_min = 2; + uint8_t value_min[] = {12, 13}; + + const uint8_t frame[] + = {COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT, + HUMIDITY_CONTROL_SETPOINT_CAPABILITIES_REPORT, + expected_type, + helper_get_attribute_field(precision_min, scale_min, size_min), + value_min[0], + value_min[1]}; + + // Type not found + TEST_ASSERT_EQUAL(SL_STATUS_NOT_SUPPORTED, + handler.control_handler(&info, frame, sizeof(frame))); +} + +void test_humidity_control_setpoint_capabilities_report_fail_incorrect_size_field() +{ + humidity_control_setpoint_type_t expected_type + = HUMIDITY_CONTROL_SETPOINT_CAPABILITIES_REPORT_SETPOINT_TYPE_DEHUMIDIFIER_V2; + + zwave_controller_connection_info_t info = {}; + info.remote.node_id = node_id; + info.remote.endpoint_id = endpoint_id; + info.local.is_multicast = false; + + uint8_t precision_min = 2; + uint8_t scale_min = 0; + uint8_t size_min = 12; // incorrect size + uint8_t value_min[] = {12, 13}; + + uint8_t precision_max = 4; + uint8_t scale_max = 1; + uint8_t size_max = 4; + uint8_t value_max[] = {14, 15, 16, 17}; + + const uint8_t frame[] = { + COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT, + HUMIDITY_CONTROL_SETPOINT_CAPABILITIES_REPORT, + (uint8_t)(0b11110000 + | expected_type), // This test if we properly ignore the first 4 bits + helper_get_attribute_field(precision_min, scale_min, size_min), + value_min[0], + value_min[1], + helper_get_attribute_field(precision_max, scale_max, size_max), + value_max[0], + value_max[1], + value_max[2], + value_max[3]}; + + // Type not found + TEST_ASSERT_EQUAL(SL_STATUS_NOT_SUPPORTED, + handler.control_handler(&info, frame, sizeof(frame))); +} + +void test_humidity_control_setpoint_capabilities_report_fail_incorrect_frame_size() +{ + zwave_controller_connection_info_t info = {}; + info.remote.node_id = node_id; + info.remote.endpoint_id = endpoint_id; + info.local.is_multicast = false; + + const uint8_t frame[] = {COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT, + HUMIDITY_CONTROL_SETPOINT_CAPABILITIES_REPORT, + 12, + 13}; + // Type not found + TEST_ASSERT_EQUAL(SL_STATUS_NOT_SUPPORTED, + handler.control_handler(&info, frame, sizeof(frame))); +} + +//////////////////////////////////////////////////////////////////////////// +// Supported types Get/Report +//////////////////////////////////////////////////////////////////////////// +void test_humidity_control_setpoint_supported_types_get_happy_case() +{ + // Ask for a Get Command, should always be the same + TEST_ASSERT_NOT_NULL(supported_types_get); + TEST_ASSERT_EQUAL( + SL_STATUS_OK, + supported_types_get(0, received_frame, &received_frame_size)); + + const uint8_t expected_frame[] = {COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT, + HUMIDITY_CONTROL_SETPOINT_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_humidity_control_setpoint_supported_types_report_happy_case() +{ + size_t expected_type_count = 3; + humidity_control_setpoint_supported_types_t expected_supported_types + = 0b00001110; + + const uint8_t frame[] = {COMMAND_CLASS_HUMIDITY_CONTROL_SETPOINT, + HUMIDITY_CONTROL_SETPOINT_SUPPORTED_REPORT, + expected_supported_types}; + + zwave_controller_connection_info_t info = {}; + info.remote.node_id = node_id; + info.remote.endpoint_id = endpoint_id; + info.local.is_multicast = false; + + // Simulate previous node + create_type_node(0x0F); + + TEST_ASSERT_EQUAL(SL_STATUS_OK, + handler.control_handler(&info, frame, sizeof(frame))); + + humidity_control_setpoint_supported_types_t reported_supported_types; + + sl_status_t status + = attribute_store_get_child_reported(endpoint_id_node, + ATTRIBUTE(SUPPORTED_TYPES), + &reported_supported_types, + sizeof(reported_supported_types)); + + TEST_ASSERT_EQUAL_MESSAGE(SL_STATUS_OK, + status, + "Should be able to get SUPPORTED_TYPES value"); + + TEST_ASSERT_EQUAL_MESSAGE( + expected_supported_types, + reported_supported_types, + "Supported type should be the same as in the report frame"); + + size_t type_count + = attribute_store_get_node_child_count_by_type(endpoint_id_node, + ATTRIBUTE(TYPE)); + + TEST_ASSERT_EQUAL_MESSAGE(expected_type_count, + type_count, + "TYPE node count is incorrect"); + + for (size_t i = 0; i < expected_type_count; i++) { + attribute_store_node_t node + = attribute_store_get_node_child_by_type(endpoint_id_node, + ATTRIBUTE(TYPE), + i); + + humidity_control_setpoint_type_t reported_value; + sl_status_t status = attribute_store_get_reported(node, + &reported_value, + sizeof(reported_value)); + TEST_ASSERT_EQUAL_MESSAGE(SL_STATUS_OK, + status, + "Should be able to get TYPE reported value"); + + TEST_ASSERT_EQUAL_MESSAGE(i + 1, + reported_value, + "Incorrect TYPE value set"); + } +} + +//////////////////////////////////////////////////////////////////////////// +// Misc +//////////////////////////////////////////////////////////////////////////// +void test_attribute_creation_happy_case() +{ + attribute_store_node_t supported_types_node + = attribute_store_get_first_child_by_type(endpoint_id_node, + ATTRIBUTE(SUPPORTED_TYPES)); + + TEST_ASSERT_EQUAL_MESSAGE( + ATTRIBUTE_STORE_INVALID_NODE, + supported_types_node, + "Supported type node shouldn't exist at this stage"); + + zwave_cc_version_t version = 1; + attribute_store_set_child_reported(endpoint_id_node, + ATTRIBUTE(VERSION), + &version, + sizeof(version)); + + supported_types_node + = attribute_store_get_first_child_by_type(endpoint_id_node, + ATTRIBUTE(SUPPORTED_TYPES)); + + TEST_ASSERT_NOT_EQUAL_MESSAGE( + ATTRIBUTE_STORE_INVALID_NODE, + supported_types_node, + "Supported type node should exist at this stage"); +} + +void test_scale_bounds() +{ + attribute_store_node_t type_node = create_type_node(2); + + std::map + correspondence_table {{ATTRIBUTE(VALUE), ATTRIBUTE(VALUE_SCALE)}, + {ATTRIBUTE(MIN_VALUE), ATTRIBUTE(MIN_VALUE_SCALE)}, + {ATTRIBUTE(MAX_VALUE), ATTRIBUTE(MAX_VALUE_SCALE)}}; + + std::map scale_table; + // Create correspondence between parent node ID and scale attribute + for (auto &table_entry: correspondence_table) { + attribute_store_node_t parent_node + = attribute_store_add_node(table_entry.first, type_node); + + scale_table[parent_node] = table_entry.second; + } + + for (auto &scale: scale_table) { + humidity_control_setpoint_scale_t scale_value = 12; + printf("Testing scale attribute #%d\n", scale.second); + // Test at attribute creation + attribute_store_node_t scale_node + = attribute_store_emplace(scale.first, + scale.second, + &scale_value, + sizeof(scale_value)); + + attribute_store_get_reported(scale_node, &scale_value, sizeof(scale_value)); + + TEST_ASSERT_EQUAL_MESSAGE( + 0, + scale_value, + "Scale value should be equal to 0 if overflows (>1)"); + + // Test at attribute update + scale_value = 4; + attribute_store_set_reported(scale_node, &scale_value, sizeof(scale_value)); + + attribute_store_get_reported(scale_node, &scale_value, sizeof(scale_value)); + TEST_ASSERT_EQUAL_MESSAGE( + 0, + scale_value, + "Scale value should be equal to 0 if overflows (>1)"); + } +} + +} // extern "C" \ No newline at end of file