Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-33: Thermostat Fan Control/State Mode CC implementation #33

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -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
}
Expand Up @@ -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
Expand Down
@@ -0,0 +1,74 @@

/******************************************************************************
* # License
* <b>Copyright 2021 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* 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;
}
@@ -0,0 +1,46 @@
/******************************************************************************
* # License
* <b>Copyright 2024 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* 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 */
Expand Up @@ -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"
Expand All @@ -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;
}

Expand Down
Expand Up @@ -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
Expand Down
@@ -0,0 +1,110 @@
/******************************************************************************
* # License
* <b>Copyright 2022 Silicon Laboratories Inc. www.silabs.com</b>
******************************************************************************
* 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);
}