diff --git a/applications/zpc/components/zpc_stdin/src/zpc_stdin_command_handling.cpp b/applications/zpc/components/zpc_stdin/src/zpc_stdin_command_handling.cpp index ea2033f4f..05f263299 100644 --- a/applications/zpc/components/zpc_stdin/src/zpc_stdin_command_handling.cpp +++ b/applications/zpc/components/zpc_stdin/src/zpc_stdin_command_handling.cpp @@ -98,6 +98,7 @@ static sl_status_t handle_zwave_reset_mpan(const handle_args_t &arg); static sl_status_t handle_zwave_home_id(const handle_args_t &arg); static sl_status_t handle_enable_nls(const handle_args_t &arg); static sl_status_t handle_get_nls_state(const handle_args_t &arg); +static sl_status_t handle_set_priority_route(const handle_args_t &arg); /// Map that holds all the commands (used for printing help) /// @@ -168,6 +169,10 @@ const std::map> commands = { &handle_add_zwave_node_abort}}, {"zwave_add_node", {" :Add a Z-Wave node to the network", &handle_add_zwave_node}}, + {"zwave_set_priority_route", + {" :Set the priority route to a destination node", + &handle_set_priority_route}}, + {"zwave_grant_keys", {COLOR_START "<1/0 to accept/reject the requested keys>,,,,,, " + "all values in hexadecimal\n"); + + return SL_STATUS_FAIL; + } + + try { + dst_node_id + = static_cast(std::stoi(arg[1].c_str(), nullptr, 16)); + for (size_t i = 0; i < 5; ++i) { + route[i] + = static_cast(std::stoi(arg[i + 2].c_str(), nullptr, 16)); + } + } catch (const std::invalid_argument &e) { + dprintf(out_stream, "Invalid argument: %s\n", e.what()); + return SL_STATUS_FAIL; + } + + if (dst_node_id <= 0 || dst_node_id > max_node_id) { + dprintf(out_stream, "Invalid destination node id\n"); + return SL_STATUS_FAIL; + } + + bool previous_hop_was_direct = false; + for (size_t i = 0; i < 4; ++i) { + if (route[i] < 0 || route[i] > max_node_id) { + dprintf(out_stream, "Invalid hop node id\n"); + return SL_STATUS_FAIL; + } + if (previous_hop_was_direct && route[i] != 0) { + dprintf(out_stream, "Invalid route. Cannot set a node id after a zero\n"); + return SL_STATUS_FAIL; + } + if (0 == route[i]) { + previous_hop_was_direct = true; + } + } + + if (route[4] <= 0 || route[4] > 3) { + dprintf(out_stream, "Invalid Route Speed.\n"); + return SL_STATUS_FAIL; + } + + return zwave_network_management_set_priority_route(dst_node_id, route); +} + static sl_status_t handle_add_zwave_node(const handle_args_t &arg) { return zwave_network_management_add_node(); diff --git a/applications/zpc/components/zpc_stdin/test/zpc_stdin_test.c b/applications/zpc/components/zpc_stdin/test/zpc_stdin_test.c index 3f4c668f0..6cb9e7de1 100644 --- a/applications/zpc/components/zpc_stdin/test/zpc_stdin_test.c +++ b/applications/zpc/components/zpc_stdin/test/zpc_stdin_test.c @@ -543,3 +543,106 @@ void test_zwave_command_handler_dispatch() TEST_ASSERT_EQUAL(SL_STATUS_OK, state); } +void test_zwave_set_priority_route_should_return_ok_with_valid_arguments(void) +{ + sl_status_t state = SL_STATUS_FAIL; + + zwave_network_management_set_priority_route_IgnoreAndReturn(SL_STATUS_OK); + state + = uic_stdin_handle_command("zwave_set_priority_route 02,01,00,00,00,01"); + TEST_ASSERT_EQUAL(SL_STATUS_OK, state); + + state + = uic_stdin_handle_command("zwave_set_priority_route 01,02,03,04,05,01"); + TEST_ASSERT_EQUAL(SL_STATUS_OK, state); + + state + = uic_stdin_handle_command("zwave_set_priority_route E8,02,03,04,05,01"); + TEST_ASSERT_EQUAL(SL_STATUS_OK, state); + + state + = uic_stdin_handle_command("zwave_set_priority_route E8,E7,E8,E7,E0,01"); + TEST_ASSERT_EQUAL(SL_STATUS_OK, state); +} + +void test_zwave_set_priority_route_should_return_fail_with_invalid_argument_count( + void) +{ + sl_status_t state = SL_STATUS_OK; + + state = uic_stdin_handle_command("zwave_set_priority_route"); + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, state); + + state = uic_stdin_handle_command("zwave_set_priority_route ,,,,"); + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, state); + + state = uic_stdin_handle_command("zwave_set_priority_route 01"); + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, state); + + state = uic_stdin_handle_command("zwave_set_priority_route 01,02,03,04,05"); + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, state); +} + +void test_zwave_set_priority_route_should_return_fail_with_wrong_argument_order( + void) +{ + sl_status_t state = SL_STATUS_OK; + + state + = uic_stdin_handle_command("zwave_set_priority_route 02,00,02,03,04,01"); + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, state); + + state + = uic_stdin_handle_command("zwave_set_priority_route 01,02,03,00,04,01"); + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, state); +} + +void test_zwave_set_priority_route_should_return_ok_with_valid_route(void) +{ + uint8_t expected_route[] = {0x2, 0x3, 0x4, 0x5, 0x1}; + sl_status_t state = SL_STATUS_FAIL; + + zwave_network_management_set_priority_route_ExpectAndReturn(0x01, + expected_route, + SL_STATUS_OK); + state + = uic_stdin_handle_command("zwave_set_priority_route 01,02,03,04,05,01"); + TEST_ASSERT_EQUAL(SL_STATUS_OK, state); + + expected_route[0] = 0xE7; + expected_route[1] = 0xE6; + expected_route[2] = 0xE5; + expected_route[4] = 0xE4; + expected_route[3] = 0x01; + zwave_network_management_set_priority_route_ExpectAndReturn(0xE8, + expected_route, + SL_STATUS_OK); + state + = uic_stdin_handle_command("zwave_set_priority_route E8,E7,E6,E5,E4,01"); + TEST_ASSERT_EQUAL(SL_STATUS_OK, state); +} + +void test_zwave_set_priority_route_should_return_fail_with_invalid_node_id(void) +{ + sl_status_t state = SL_STATUS_OK; + + state + = uic_stdin_handle_command("zwave_set_priority_route -1,02,03,04,05,01"); + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, state); + + state + = uic_stdin_handle_command("zwave_set_priority_route FF,02,03,04,05,01"); + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, state); + + state + = uic_stdin_handle_command("zwave_set_priority_route 01,FF,03,04,05,01"); + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, state); + + state + = uic_stdin_handle_command("zwave_set_priority_route E9,FF,03,04,05,01"); + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, state); + + state + = uic_stdin_handle_command("zwave_set_priority_route 01,E9,03,04,05,01"); + TEST_ASSERT_EQUAL(SL_STATUS_FAIL, state); +} diff --git a/applications/zpc/components/zwave/zwave_network_management/include/zwave_network_management.h b/applications/zpc/components/zwave/zwave_network_management/include/zwave_network_management.h index 30acf2875..e23b85a60 100644 --- a/applications/zpc/components/zwave/zwave_network_management/include/zwave_network_management.h +++ b/applications/zpc/components/zwave/zwave_network_management/include/zwave_network_management.h @@ -333,6 +333,24 @@ bool zwave_network_management_is_busy(); */ bool we_have_return_routes_to_assign(zwave_node_id_t node_id); +/** + * @brief Assign a priority route to a node. + * + * This function will set a priority route to a given node. The priority route + * is a list with the NodeID of the hops that the Z-Wave controller will use as + * a preferred route to the node. The last byte is the route speed. See Z-Wave + * Host API Specification, section 4.4.3.14 for more information. + * + * @param node_id NodeID for which the priority route will be set + * @param priority_route Array of 4 NodeIDs for the priority route + 1 byte + * for the route speed. + * + * @returns sl_status_t SL_STATUS_OK if the operation was accepted, any other + * code in case of failure. + */ +sl_status_t zwave_network_management_set_priority_route( + zwave_node_id_t node_id, const uint8_t *const priority_route); + /* An application MUST time out waiting for the * ADD_NODE_STATUS_NODE_FOUND status if it does not receive the indication * after calling AddNodeToNetwork(ADD_NODE_ANY). @@ -369,6 +387,9 @@ bool we_have_return_routes_to_assign(zwave_node_id_t node_id); #define ADD_NODE_PROTOCOL_NEIGHBOR_DISCOVERY_TIMEOUT \ (76000 + ZW_MAX_NODES * (3517 + 732)) +/// 4 hops + speed +#define PRIORITY_ROUTE_SIZE 5 + #ifdef __cplusplus } #endif diff --git a/applications/zpc/components/zwave/zwave_network_management/src/nm_state_machine.c b/applications/zpc/components/zwave/zwave_network_management/src/nm_state_machine.c index a6c2e0e8c..3f31a3d1c 100644 --- a/applications/zpc/components/zwave/zwave_network_management/src/nm_state_machine.c +++ b/applications/zpc/components/zwave/zwave_network_management/src/nm_state_machine.c @@ -457,6 +457,11 @@ void nm_fsm_post_event(nm_event_t ev, void *event_data) == zwave_network_management_return_route_assign_next()) { nms.state = NM_ASSIGNING_RETURN_ROUTE; } + } else if (ev == NM_EV_SET_PRIORITY_ROUTE) { + route_event_data_t *priority_route_data + = (route_event_data_t *)(event_data); + zwapi_set_priority_route(priority_route_data->node_id, + priority_route_data->route); } break; // End of case NM_IDLE: diff --git a/applications/zpc/components/zwave/zwave_network_management/src/nm_state_machine.h b/applications/zpc/components/zwave/zwave_network_management/src/nm_state_machine.h index 9a6d0fa7a..794a258c3 100644 --- a/applications/zpc/components/zwave/zwave_network_management/src/nm_state_machine.h +++ b/applications/zpc/components/zwave/zwave_network_management/src/nm_state_machine.h @@ -93,6 +93,11 @@ typedef struct smartstart_event_data { zwave_protocol_t preferred_inclusion; } smartstart_event_data_t; +typedef struct route_event_data { + zwave_node_id_t node_id; + uint8_t route[PRIORITY_ROUTE_SIZE]; +} route_event_data_t; + void nm_fsm_post_event(nm_event_t ev, void *event_data); /*****************************************************************************/ /** diff --git a/applications/zpc/components/zwave/zwave_network_management/src/state_logging.c b/applications/zpc/components/zwave/zwave_network_management/src/state_logging.c index 79fa1baf2..ec01de841 100644 --- a/applications/zpc/components/zwave/zwave_network_management/src/state_logging.c +++ b/applications/zpc/components/zwave/zwave_network_management/src/state_logging.c @@ -107,6 +107,7 @@ const char *nm_event_name(nm_event_t event) STR_CASE(NM_EV_LEARN_DONE) STR_CASE(NM_EV_ASSIGN_RETURN_ROUTE_START) STR_CASE(NM_EV_ASSIGN_RETURN_ROUTE_COMPLETED) + STR_CASE(NM_EV_SET_PRIORITY_ROUTE) STR_CASE(NM_EV_MAX) default: diff --git a/applications/zpc/components/zwave/zwave_network_management/src/zwave_network_management.c b/applications/zpc/components/zwave/zwave_network_management/src/zwave_network_management.c index 393e3861b..feba6fbf7 100644 --- a/applications/zpc/components/zwave/zwave_network_management/src/zwave_network_management.c +++ b/applications/zpc/components/zwave/zwave_network_management/src/zwave_network_management.c @@ -34,6 +34,7 @@ #define LOG_TAG "zwave_network_managment" static smartstart_event_data_t smartstart_event_data; +static route_event_data_t priority_route_event_data; void zwave_network_management_enable_smart_start_add_mode(bool enabled) { @@ -333,3 +334,25 @@ zwave_keyset_t zwave_network_management_get_granted_keys() { return zwave_s2_keystore_get_assigned_keys(); } + +sl_status_t zwave_network_management_set_priority_route( + zwave_node_id_t node_id, const uint8_t *const priority_route) +{ + if (false == network_management_is_ready_for_a_new_operation()) { + sl_log_info(LOG_TAG, + "Network management is not ready for a new operation. " + "Ignoring Set Priority Route.\n"); + return SL_STATUS_FAIL; + } + + sl_log_info(LOG_TAG, "Setting Priority Route\n"); + priority_route_event_data.node_id = node_id; + + memcpy(priority_route_event_data.route, priority_route, PRIORITY_ROUTE_SIZE); + + process_post(&zwave_network_management_process, + NM_EV_SET_PRIORITY_ROUTE, + (void *)&priority_route_event_data); + + return SL_STATUS_OK; +} diff --git a/applications/zpc/components/zwave/zwave_network_management/src/zwave_network_management_process.h b/applications/zpc/components/zwave/zwave_network_management/src/zwave_network_management_process.h index b3aebd2be..55a7d8092 100644 --- a/applications/zpc/components/zwave/zwave_network_management/src/zwave_network_management_process.h +++ b/applications/zpc/components/zwave/zwave_network_management/src/zwave_network_management_process.h @@ -94,6 +94,7 @@ typedef enum { NM_EV_REQUEST_NODE_NEIGHBOR_REQUEST_COMPLETE, NM_EV_ASSIGN_RETURN_ROUTE_START, NM_EV_ASSIGN_RETURN_ROUTE_COMPLETED, + NM_EV_SET_PRIORITY_ROUTE, NM_EV_MAX, //This MUST always to be last entry in this enum } nm_event_t; diff --git a/applications/zpc/components/zwave/zwave_network_management/test/zwave_network_management_state_logging_test.c b/applications/zpc/components/zwave/zwave_network_management/test/zwave_network_management_state_logging_test.c index 07f1c4e2f..7585341d7 100644 --- a/applications/zpc/components/zwave/zwave_network_management/test/zwave_network_management_state_logging_test.c +++ b/applications/zpc/components/zwave/zwave_network_management/test/zwave_network_management_state_logging_test.c @@ -109,6 +109,7 @@ void test_zwave_network_management_state_event_logging_test() TEST_ASSERT_EQUAL_STRING("NM_EV_LEARN_DONE", nm_event_name(NM_EV_LEARN_DONE)); TEST_ASSERT_EQUAL_STRING("NM_EV_ASSIGN_RETURN_ROUTE_START", nm_event_name(NM_EV_ASSIGN_RETURN_ROUTE_START)); TEST_ASSERT_EQUAL_STRING("NM_EV_ASSIGN_RETURN_ROUTE_COMPLETED", nm_event_name(NM_EV_ASSIGN_RETURN_ROUTE_COMPLETED)); + TEST_ASSERT_EQUAL_STRING("NM_EV_SET_PRIORITY_ROUTE", nm_event_name(NM_EV_SET_PRIORITY_ROUTE)); TEST_ASSERT_EQUAL_STRING("NM_EV_MAX", nm_event_name(NM_EV_MAX)); TEST_ASSERT_EQUAL_STRING("Unknown", nm_event_name(NM_EV_MAX+1)); -} \ No newline at end of file +} diff --git a/applications/zpc/release_notes.md b/applications/zpc/release_notes.md index b061bfed9..66484dfa5 100644 --- a/applications/zpc/release_notes.md +++ b/applications/zpc/release_notes.md @@ -1,5 +1,9 @@ # ZPC Release Notes +## [Next] + +* [Support command Set Priority Route Command](https://github.com/SiliconLabsSoftware/z-wave-protocol-controller/pull/96) + ## [1.7.0] - Feb 2025 **BREAKING**: ZPC has been relocated outside of UnifySDK and is now dependent on it.