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
71 changes: 63 additions & 8 deletions test/util/test_node.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
"""Test node utility functions."""
import pytest

from zwave_js_server.const import CommandClass
from zwave_js_server.exceptions import InvalidNewValue, NotFoundError, ValueTypeError
from zwave_js_server.const import CommandClass, CommandStatus
from zwave_js_server.exceptions import (
InvalidNewValue,
NotFoundError,
SetValueFailed,
ValueTypeError,
)
from zwave_js_server.model.node import Node
from zwave_js_server.model.value import ConfigurationValue
from zwave_js_server.util.node import (
Expand Down Expand Up @@ -47,7 +52,9 @@ async def test_configuration_parameter_values(
{"success": True},
)

await async_set_config_parameter(node_2, 190, 8, 255)
zwave_value, cmd_status = await async_set_config_parameter(node_2, 190, 8, 255)
assert isinstance(zwave_value, ConfigurationValue)
assert cmd_status == CommandStatus.ACCEPTED

value = node_2.values["31-112-0-8-255"]
assert len(ack_commands_2) == 1
Expand All @@ -59,7 +66,9 @@ async def test_configuration_parameter_values(
"messageId": uuid4,
}

await async_set_config_parameter(node_2, "Blue", 8, 255)
zwave_value, cmd_status = await async_set_config_parameter(node_2, "Blue", 8, 255)
assert isinstance(zwave_value, ConfigurationValue)
assert cmd_status == CommandStatus.ACCEPTED

value = node_2.values["31-112-0-8-255"]
assert len(ack_commands_2) == 2
Expand Down Expand Up @@ -92,9 +101,11 @@ async def test_configuration_parameter_values(
await async_set_config_parameter(node, 1, 1, property_key=1)

# Test setting a configuration parameter by state label and property name
await async_set_config_parameter(
zwave_value, cmd_status = await async_set_config_parameter(
node, "2.0\u00b0 F", "Temperature Reporting Threshold"
)
assert isinstance(zwave_value, ConfigurationValue)
assert cmd_status == CommandStatus.ACCEPTED

value = node.values["13-112-0-1"]
assert len(ack_commands) == 3
Expand All @@ -114,7 +125,8 @@ async def test_bulk_set_partial_config_parameters(multisensor_6, uuid4, mock_com
{"command": "node.set_value", "nodeId": node.node_id},
{"success": True},
)
await async_bulk_set_partial_config_parameters(node, 101, 241)
cmd_status = await async_bulk_set_partial_config_parameters(node, 101, 241)
assert cmd_status == CommandStatus.QUEUED
assert len(ack_commands) == 1
assert ack_commands[0] == {
"command": "node.set_value",
Expand All @@ -127,9 +139,10 @@ async def test_bulk_set_partial_config_parameters(multisensor_6, uuid4, mock_com
"messageId": uuid4,
}

await async_bulk_set_partial_config_parameters(
cmd_status = await async_bulk_set_partial_config_parameters(
node, 101, {128: 1, 64: 1, 32: 1, 16: 1, 1: 1}
)
assert cmd_status == CommandStatus.QUEUED
assert len(ack_commands) == 2
assert ack_commands[1] == {
"command": "node.set_value",
Expand All @@ -143,9 +156,10 @@ async def test_bulk_set_partial_config_parameters(multisensor_6, uuid4, mock_com
}

# Only set some values so we use cached values for the rest
await async_bulk_set_partial_config_parameters(
cmd_status = await async_bulk_set_partial_config_parameters(
node, 101, {64: 1, 32: 1, 16: 1, 1: 1}
)
assert cmd_status == CommandStatus.QUEUED
assert len(ack_commands) == 3
assert ack_commands[2] == {
"command": "node.set_value",
Expand All @@ -171,3 +185,44 @@ async def test_bulk_set_partial_config_parameters(multisensor_6, uuid4, mock_com
# Try to bulkset a property that isn't broken into partials
with pytest.raises(ValueTypeError):
await async_bulk_set_partial_config_parameters(node, 252, 1)


async def test_failures(multisensor_6, mock_command):
"""Test setting config parameter failures."""
node: Node = multisensor_6
# We need the node to be alive so we wait for a response
node.handle_alive(node)

mock_command(
{"command": "node.set_value", "nodeId": node.node_id},
{"success": False},
)

with pytest.raises(SetValueFailed):
await async_bulk_set_partial_config_parameters(
node, 101, {64: 1, 32: 1, 16: 1, 1: 1}
)

with pytest.raises(SetValueFailed):
await async_set_config_parameter(node, 1, 101, 64)


async def test_returned_values(multisensor_6, mock_command):
"""Test returned values from setting config parameters."""
node: Node = multisensor_6
# We need the node to be alive so we wait for a response
node.handle_alive(node)

mock_command(
{"command": "node.set_value", "nodeId": node.node_id},
{"success": True},
)

cmd_status = await async_bulk_set_partial_config_parameters(
node, 101, {64: 1, 32: 1, 16: 1, 1: 1}
)
assert cmd_status == CommandStatus.ACCEPTED

zwave_value, cmd_status = await async_set_config_parameter(node, 1, 101, 64)
assert isinstance(zwave_value, ConfigurationValue)
assert cmd_status == CommandStatus.ACCEPTED
7 changes: 7 additions & 0 deletions zwave_js_server/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@
VALUE_UNKNOWN = "unknown"


class CommandStatus(str, Enum):
"""Status of a command sent to zwave-js-server."""

ACCEPTED = "accepted"
QUEUED = "queued"


class EntryControlEventType(IntEnum):
"""Entry control event types."""

Expand Down
27 changes: 19 additions & 8 deletions zwave_js_server/util/node.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""Utility functions for Z-Wave JS nodes."""
import json
from typing import Dict, Optional, Union, cast
from typing import Dict, Optional, Tuple, Union, cast

from ..const import CommandClass, ConfigurationValueType
from ..const import CommandClass, CommandStatus, ConfigurationValueType
from ..exceptions import InvalidNewValue, NotFoundError, SetValueFailed, ValueTypeError
from ..model.node import Node
from ..model.value import ConfigurationValue, get_value_id
Expand Down Expand Up @@ -88,7 +88,7 @@ async def async_bulk_set_partial_config_parameters(
node: Node,
property_: int,
new_value: Union[int, Dict[int, Union[int, str]]],
) -> None:
) -> CommandStatus:
"""Bulk set partial configuration values on this node."""
config_values = node.get_configuration_values()
property_values = [
Expand Down Expand Up @@ -153,28 +153,36 @@ async def async_bulk_set_partial_config_parameters(
remaining_value = remaining_value % multiplication_factor
_validate_and_transform_new_value(zwave_value, partial_value)

response = await node.async_send_command(
cmd_response = await node.async_send_command(
"set_value",
valueId={
"commandClass": CommandClass.CONFIGURATION.value,
"property": property_,
},
value=new_value,
)
if response and not cast(bool, response["success"]):

# If we didn't wait for a response, we assume the command has been queued
if cmd_response is None:
return CommandStatus.QUEUED

if not cast(bool, cmd_response["success"]):
raise SetValueFailed(
"Unable to set value, refer to "
"https://zwave-js.github.io/node-zwave-js/#/api/node?id=setvalue for "
"possible reasons"
)

# If we received a response that is not false, the command was successful
return CommandStatus.ACCEPTED


async def async_set_config_parameter(
node: Node,
new_value: Union[int, str],
property_or_property_name: Union[int, str],
property_key: Optional[Union[int, str]] = None,
) -> ConfigurationValue:
) -> Tuple[ConfigurationValue, CommandStatus]:
"""
Set a value for a config parameter on this node.

Expand Down Expand Up @@ -216,11 +224,14 @@ async def async_set_config_parameter(
new_value = _validate_and_transform_new_value(zwave_value, new_value)

# Finally attempt to set the value and return the Value object if successful
if await node.async_set_value(zwave_value, new_value) is False:
success = await node.async_set_value(zwave_value, new_value)
if success is False:
raise SetValueFailed(
"Unable to set value, refer to "
"https://zwave-js.github.io/node-zwave-js/#/api/node?id=setvalue for "
"possible reasons"
)

return zwave_value
status = CommandStatus.ACCEPTED if success else CommandStatus.QUEUED

return zwave_value, status