diff --git a/.github/workflows/analyze.yml b/.github/workflows/analyze.yml index 0bf404093..7b341245e 100644 --- a/.github/workflows/analyze.yml +++ b/.github/workflows/analyze.yml @@ -67,6 +67,7 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, sha: process.env.sha, - state: 'failure' + state: 'failure', + context: 'analyze' }) core.setFailed(`Status: ${process.env.status}`); diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bb2d3cd14..8b67af26b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -117,7 +117,8 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, sha: process.env.sha, - state: 'failure' + state: 'failure', + context: 'test' }) core.setFailed(`Status: ${process.env.status}`); process.exit(1); diff --git a/Dockerfile b/Dockerfile index 59b140cc9..92afde9f9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,12 @@ # SPDX-License-Identifier: Zlib # SPDX-FileCopyrightText: Silicon Laboratories Inc. https://www.silabs.com -FROM debian:bookworm AS builder +FROM debian:bookworm AS dev -ARG UNIFYSDK_GIT_REPOSITORY https://github.com/SiliconLabs/UnifySDK -ARG UNIFYSDK_GIT_TAG main +ARG UNIFYSDK_GIT_REPOSITORY=https://github.com/SiliconLabs/UnifySDK +ARG UNIFYSDK_GIT_TAG=main +ENV UNIFYSDK_GIT_REPOSITORY=${UNIFYSDK_GIT_REPOSITORY} \ + UNIFYSDK_GIT_TAG=${UNIFYSDK_GIT_TAG} ENV project z-wave-protocol-controller ENV workdir /usr/local/opt/${project} @@ -13,6 +15,8 @@ ADD . ${workdir} ARG HELPER="./helper.mk" ARG HELPER_SETUP_RULES=setup ARG HELPER_DEFAULT_RULES=default +ENV HELPER=${HELPER} \ + HELPER_DEFAULT_RULES=${HELPER_DEFAULT_RULES} WORKDIR ${workdir} @@ -23,7 +27,10 @@ RUN echo "# log: Setup system" \ && apt-get install -y --no-install-recommends -- make sudo \ && ${HELPER} help ${HELPER_SETUP_RULES} \ && date -u +ENTRYPOINT [ "/usr/bin/bash" ] +CMD [] +FROM dev AS builder RUN echo "# log: Build" \ && set -x \ && ${HELPER} ${HELPER_DEFAULT_RULES} \ @@ -36,7 +43,7 @@ RUN echo "# log: Build" \ && ${HELPER} distclean \ && date -u -FROM debian:bookworm +FROM debian:bookworm AS runtime ENV project=z-wave-protocol-controller ARG workdir=/usr/local/opt/${project} COPY --from=builder ${workdir}/dist/ ${workdir}/dist/ diff --git a/README.md b/README.md index a9c4f8775..a04dd6f6c 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,47 @@ Or relevant sources pages, to get started head to: ## Quickstart +### Docker build + +The fastest (less than 20min) way to build z-wave-protocol-controller from scratch +is to delegate all tasks to docker. The final image will contain the binaries that +you can use. + +```sh +docker build https://github.com/SiliconLabsSoftware/z-wave-protocol-controller.git#ver_1.7.0 +``` + +This one-liner will do download latest release, setup environment, build, test, package... + +Also a docker-compose file is provided to start ZPC and use it along a MQTT client +(eg: +[mosquitto-clients](https://github.com/eclipse-mosquitto/mosquitto/) +). + +### Docker development + +The project Dockerfile has three stages: `dev` (setup only, for interactive +use), `builder` (full build and package), and `runtime` (minimal runnable +image). To work iteratively in a container with your host source bind-mounted +(e.g., build and run ctest without rebuilding the image each time): + +```sh +# Build the dev stage +docker build --target dev -t z-wave-protocol-controller:dev . + +# Run a shell with your source mounted +docker run -it --rm -v "$PWD:/usr/local/opt/z-wave-protocol-controller" -w /usr/local/opt/z-wave-protocol-controller z-wave-protocol-controller:dev + +# Inside the container: build and run unit tests +./helper.mk default + +# Run unit tests +ctest --test-dir build/applications --output-on-failure +``` + +Power users might prefer to work in sources tree in a native GNU/Linux +environment as explained below. + ### Native (Linux) build The project is CMake based, to prepare the environment, @@ -136,7 +177,7 @@ You can use this [script](./scripts/wslusb.ps1). Start by installing the usbipd service as described at: https://learn.microsoft.com/en-us/windows/wsl/connect-usb ```sh -# You can list devices using: +# You can list devices using: (Powershell)$ ./wslusb.ps1 -List @@ -157,26 +198,6 @@ Start by installing the usbipd service as described at: https://learn.microsoft. Refer to [./doc](doc) for more (using shell, MQTT, WebApp etc). - -### Docker build - -The fastest (less than 20min) way to build z-wave-protocol-controller from scratch -is to delegate all tasks to docker. - -```sh -docker build https://github.com/SiliconLabsSoftware/z-wave-protocol-controller.git#ver_1.7.0 -``` - -This one-liner will do download latest release, setup environment, build, test, package... - -Also a docker-compose file is provided to start ZPC and use it along a MQTT client -(eg: -[mosquitto-clients](https://github.com/eclipse-mosquitto/mosquitto/) -). - -Power users might prefer to work in sources tree in a native GNU/Linux -environment as explained above. - ## Contributing - [CONTRIBUTING.md](CONTRIBUTING.md) 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 1cd7682c0..4f62cc0c9 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 32bea36bd..ae9f2fed0 100644 --- a/applications/zpc/components/zpc_stdin/test/zpc_stdin_test.c +++ b/applications/zpc/components/zpc_stdin/test/zpc_stdin_test.c @@ -545,3 +545,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..00c90be30 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/156) + ## [1.7.0] - Feb 2025 **BREAKING**: ZPC has been relocated outside of UnifySDK and is now dependent on it.