Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/analyze.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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}`);
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
15 changes: 11 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -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}
Expand All @@ -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}

Expand All @@ -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} \
Expand All @@ -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/
Expand Down
63 changes: 42 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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

Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
///
Expand Down Expand Up @@ -168,6 +169,10 @@ const std::map<std::string, std::pair<std::string, handler_func>> 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>,<Set of keys to"
Expand Down Expand Up @@ -620,6 +625,61 @@ static sl_status_t handle_zwave_learn_mode(const handle_args_t &arg)
return SL_STATUS_OK;
}

static sl_status_t handle_set_priority_route(const handle_args_t &arg)
{
zwave_node_id_t dst_node_id = 0;
uint8_t route[PRIORITY_ROUTE_SIZE] = {0};
const int max_node_id = 0xE8;

if (arg.size() != 7) {
dprintf(out_stream,
"Invalid number of arguments, expected args:"
"<Dst NodeID>,<Hop 1>,<Hop 2>,<Hop 3>,<Hop 4>,<Speed> "
"all values in hexadecimal\n");

return SL_STATUS_FAIL;
}

try {
dst_node_id
= static_cast<zwave_node_id_t>(std::stoi(arg[1].c_str(), nullptr, 16));
for (size_t i = 0; i < 5; ++i) {
route[i]
= static_cast<uint8_t>(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();
Expand Down
103 changes: 103 additions & 0 deletions applications/zpc/components/zpc_stdin/test/zpc_stdin_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
/*****************************************************************************/
/**
Expand Down
Loading
Loading