Bump zwave-js-server-python to 0.50.1 (#94760)
* Bump zwave-js-server-python to 0.50.0 * handle additional upstream changes * Additional changes * fix tests * Convert two similar functions to be one function * Fix docstring * Remove conditional pydantic import * Revert scope change * Bump zwave-js-server-python * Set default return value for command * Remove line breaks * Add coverage
This commit is contained in:
parent
2841cbbed2
commit
5d3d66e47d
28 changed files with 171 additions and 128 deletions
|
@ -9,7 +9,7 @@ from typing import Any
|
|||
|
||||
from async_timeout import timeout
|
||||
from zwave_js_server.client import Client as ZwaveClient
|
||||
from zwave_js_server.const import CommandClass
|
||||
from zwave_js_server.const import CommandClass, RemoveNodeReason
|
||||
from zwave_js_server.exceptions import BaseZwaveJSServerError, InvalidServerVersion
|
||||
from zwave_js_server.model.driver import Driver
|
||||
from zwave_js_server.model.node import Node as ZwaveNode
|
||||
|
@ -398,13 +398,13 @@ class ControllerEvents:
|
|||
def async_on_node_removed(self, event: dict) -> None:
|
||||
"""Handle node removed event."""
|
||||
node: ZwaveNode = event["node"]
|
||||
replaced: bool = event.get("replaced", False)
|
||||
reason: RemoveNodeReason = event["reason"]
|
||||
# grab device in device registry attached to this node
|
||||
dev_id = get_device_id(self.driver_events.driver, node)
|
||||
device = self.dev_reg.async_get_device(identifiers={dev_id})
|
||||
# We assert because we know the device exists
|
||||
assert device
|
||||
if replaced:
|
||||
if reason in (RemoveNodeReason.REPLACED, RemoveNodeReason.PROXY_REPLACED):
|
||||
self.discovered_value_ids.pop(device.id, None)
|
||||
|
||||
async_dispatcher_send(
|
||||
|
|
|
@ -1138,6 +1138,7 @@ async def websocket_remove_node(
|
|||
node = event["node"]
|
||||
node_details = {
|
||||
"node_id": node.node_id,
|
||||
"reason": event["reason"],
|
||||
}
|
||||
|
||||
connection.send_message(
|
||||
|
|
|
@ -507,8 +507,9 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity):
|
|||
# Please use Dry and Fan HVAC modes instead.
|
||||
if preset_mode_value in (ThermostatMode.DRY, ThermostatMode.FAN):
|
||||
LOGGER.warning(
|
||||
"Dry and Fan preset modes are deprecated and will be removed in Home Assistant 2024.2. "
|
||||
"Please use the corresponding Dry and Fan HVAC modes instead"
|
||||
"Dry and Fan preset modes are deprecated and will be removed in Home "
|
||||
"Assistant 2024.2. Please use the corresponding Dry and Fan HVAC "
|
||||
"modes instead"
|
||||
)
|
||||
async_create_issue(
|
||||
self.hass,
|
||||
|
|
|
@ -54,9 +54,8 @@ from .device_automation_helpers import (
|
|||
CONF_SUBTYPE,
|
||||
VALUE_ID_REGEX,
|
||||
generate_config_parameter_subtype,
|
||||
get_config_parameter_value_schema,
|
||||
)
|
||||
from .helpers import async_get_node_from_device_id
|
||||
from .helpers import async_get_node_from_device_id, get_value_state_schema
|
||||
|
||||
ACTION_TYPES = {
|
||||
SERVICE_CLEAR_LOCK_USERCODE,
|
||||
|
@ -357,7 +356,7 @@ async def async_get_action_capabilities(
|
|||
property_key=config[ATTR_CONFIG_PARAMETER_BITMASK],
|
||||
endpoint=config[ATTR_ENDPOINT],
|
||||
)
|
||||
value_schema = get_config_parameter_value_schema(node, value_id)
|
||||
value_schema = get_value_state_schema(node.values[value_id])
|
||||
if value_schema is None:
|
||||
return {}
|
||||
return {"extra_fields": vol.Schema({vol.Required(ATTR_VALUE): value_schema})}
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
"""Provides helpers for Z-Wave JS device automations."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import cast
|
||||
|
||||
import voluptuous as vol
|
||||
from zwave_js_server.client import Client as ZwaveClient
|
||||
from zwave_js_server.const import ConfigurationValueType
|
||||
from zwave_js_server.model.node import Node
|
||||
from zwave_js_server.model.value import ConfigurationValue
|
||||
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
|
@ -23,24 +18,6 @@ CONF_VALUE_ID = "value_id"
|
|||
VALUE_ID_REGEX = r"([0-9]+-[0-9]+-[0-9]+-).+"
|
||||
|
||||
|
||||
def get_config_parameter_value_schema(node: Node, value_id: str) -> vol.Schema | None:
|
||||
"""Get the extra fields schema for a config parameter value."""
|
||||
config_value = cast(ConfigurationValue, node.values[value_id])
|
||||
min_ = config_value.metadata.min
|
||||
max_ = config_value.metadata.max
|
||||
|
||||
if config_value.configuration_value_type in (
|
||||
ConfigurationValueType.RANGE,
|
||||
ConfigurationValueType.MANUAL_ENTRY,
|
||||
):
|
||||
return vol.All(vol.Coerce(int), vol.Range(min=min_, max=max_))
|
||||
|
||||
if config_value.configuration_value_type == ConfigurationValueType.ENUMERATED:
|
||||
return vol.In({int(k): v for k, v in config_value.metadata.states.items()})
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def generate_config_parameter_subtype(config_value: ConfigurationValue) -> str:
|
||||
"""Generate the config parameter name used in a device automation subtype."""
|
||||
parameter = str(config_value.property_)
|
||||
|
|
|
@ -31,11 +31,11 @@ from .device_automation_helpers import (
|
|||
NODE_STATUSES,
|
||||
async_bypass_dynamic_config_validation,
|
||||
generate_config_parameter_subtype,
|
||||
get_config_parameter_value_schema,
|
||||
)
|
||||
from .helpers import (
|
||||
async_get_node_from_device_id,
|
||||
check_type_schema_map,
|
||||
get_value_state_schema,
|
||||
get_zwave_value_from_config,
|
||||
remove_keys_with_empty_values,
|
||||
)
|
||||
|
@ -209,7 +209,7 @@ async def async_get_condition_capabilities(
|
|||
# Add additional fields to the automation trigger UI
|
||||
if config[CONF_TYPE] == CONFIG_PARAMETER_TYPE:
|
||||
value_id = config[CONF_VALUE_ID]
|
||||
value_schema = get_config_parameter_value_schema(node, value_id)
|
||||
value_schema = get_value_state_schema(node.values[value_id])
|
||||
if value_schema is None:
|
||||
return {}
|
||||
return {"extra_fields": vol.Schema({vol.Required(ATTR_VALUE): value_schema})}
|
||||
|
|
|
@ -7,8 +7,9 @@ from typing import Any
|
|||
from zwave_js_server.client import Client
|
||||
from zwave_js_server.const import CommandClass
|
||||
from zwave_js_server.dump import dump_msgs
|
||||
from zwave_js_server.model.node import Node, NodeDataType
|
||||
from zwave_js_server.model.node import Node
|
||||
from zwave_js_server.model.value import ValueDataType
|
||||
from zwave_js_server.util.node import dump_node_state
|
||||
|
||||
from homeassistant.components.diagnostics import REDACTED
|
||||
from homeassistant.components.diagnostics.util import async_redact_data
|
||||
|
@ -54,13 +55,20 @@ def optionally_redact_value_of_zwave_value(zwave_value: ValueDataType) -> ValueD
|
|||
return zwave_value
|
||||
|
||||
|
||||
def redact_node_state(node_state: NodeDataType) -> NodeDataType:
|
||||
def redact_node_state(node_state: dict) -> dict:
|
||||
"""Redact node state."""
|
||||
redacted_state: NodeDataType = deepcopy(node_state)
|
||||
redacted_state["values"] = [
|
||||
optionally_redact_value_of_zwave_value(zwave_value)
|
||||
for zwave_value in node_state["values"]
|
||||
]
|
||||
redacted_state: dict = deepcopy(node_state)
|
||||
# dump_msgs returns values in a list but dump_node_state returns them in a dict
|
||||
if isinstance(node_state["values"], list):
|
||||
redacted_state["values"] = [
|
||||
optionally_redact_value_of_zwave_value(zwave_value)
|
||||
for zwave_value in node_state["values"]
|
||||
]
|
||||
else:
|
||||
redacted_state["values"] = {
|
||||
value_id: optionally_redact_value_of_zwave_value(zwave_value)
|
||||
for value_id, zwave_value in node_state["values"].items()
|
||||
}
|
||||
return redacted_state
|
||||
|
||||
|
||||
|
@ -129,8 +137,8 @@ async def async_get_config_entry_diagnostics(
|
|||
handshake_msgs = msgs[:-1]
|
||||
network_state = msgs[-1]
|
||||
network_state["result"]["state"]["nodes"] = [
|
||||
redact_node_state(async_redact_data(node, KEYS_TO_REDACT))
|
||||
for node in network_state["result"]["state"]["nodes"]
|
||||
redact_node_state(async_redact_data(node_data, KEYS_TO_REDACT))
|
||||
for node_data in network_state["result"]["state"]["nodes"]
|
||||
]
|
||||
return {"messages": [*handshake_msgs, network_state]}
|
||||
|
||||
|
@ -148,7 +156,9 @@ async def async_get_device_diagnostics(
|
|||
node = driver.controller.nodes[node_id]
|
||||
entities = get_device_entities(hass, node, config_entry, device)
|
||||
assert client.version
|
||||
node_state = redact_node_state(async_redact_data(node.data, KEYS_TO_REDACT))
|
||||
node_state = redact_node_state(
|
||||
async_redact_data(dump_node_state(node), KEYS_TO_REDACT)
|
||||
)
|
||||
return {
|
||||
"versionInfo": {
|
||||
"driverVersion": client.version.driver_version,
|
||||
|
|
|
@ -1108,7 +1108,7 @@ def async_discover_single_value(
|
|||
def async_discover_single_configuration_value(
|
||||
value: ConfigurationValue,
|
||||
) -> Generator[ZwaveDiscoveryInfo, None, None]:
|
||||
"""Run discovery on a single ZWave configuration value and return matching schema info."""
|
||||
"""Run discovery on single Z-Wave configuration value and return schema matches."""
|
||||
if value.metadata.writeable and value.metadata.readable:
|
||||
if value.configuration_value_type == ConfigurationValueType.ENUMERATED:
|
||||
yield ZwaveDiscoveryInfo(
|
||||
|
@ -1125,36 +1125,29 @@ def async_discover_single_configuration_value(
|
|||
ConfigurationValueType.RANGE,
|
||||
ConfigurationValueType.MANUAL_ENTRY,
|
||||
):
|
||||
if value.metadata.type == ValueType.BOOLEAN or (
|
||||
value.metadata.min == 0 and value.metadata.max == 1
|
||||
):
|
||||
yield ZwaveDiscoveryInfo(
|
||||
node=value.node,
|
||||
primary_value=value,
|
||||
assumed_state=False,
|
||||
platform=Platform.SWITCH,
|
||||
platform_hint="config_parameter",
|
||||
platform_data=None,
|
||||
additional_value_ids_to_watch=set(),
|
||||
entity_registry_enabled_default=False,
|
||||
)
|
||||
else:
|
||||
yield ZwaveDiscoveryInfo(
|
||||
node=value.node,
|
||||
primary_value=value,
|
||||
assumed_state=False,
|
||||
platform=Platform.NUMBER,
|
||||
platform_hint="config_parameter",
|
||||
platform_data=None,
|
||||
additional_value_ids_to_watch=set(),
|
||||
entity_registry_enabled_default=False,
|
||||
)
|
||||
yield ZwaveDiscoveryInfo(
|
||||
node=value.node,
|
||||
primary_value=value,
|
||||
assumed_state=False,
|
||||
platform=Platform.NUMBER,
|
||||
platform_hint="config_parameter",
|
||||
platform_data=None,
|
||||
additional_value_ids_to_watch=set(),
|
||||
entity_registry_enabled_default=False,
|
||||
)
|
||||
elif value.configuration_value_type == ConfigurationValueType.BOOLEAN:
|
||||
yield ZwaveDiscoveryInfo(
|
||||
node=value.node,
|
||||
primary_value=value,
|
||||
assumed_state=False,
|
||||
platform=Platform.SWITCH,
|
||||
platform_hint="config_parameter",
|
||||
platform_data=None,
|
||||
additional_value_ids_to_watch=set(),
|
||||
entity_registry_enabled_default=False,
|
||||
)
|
||||
elif not value.metadata.writeable and value.metadata.readable:
|
||||
if value.metadata.type == ValueType.BOOLEAN or (
|
||||
value.metadata.min == 0
|
||||
and value.metadata.max == 1
|
||||
and not value.metadata.states
|
||||
):
|
||||
if value.configuration_value_type == ConfigurationValueType.BOOLEAN:
|
||||
yield ZwaveDiscoveryInfo(
|
||||
node=value.node,
|
||||
primary_value=value,
|
||||
|
|
|
@ -7,7 +7,11 @@ from typing import Any
|
|||
from zwave_js_server.const import NodeStatus
|
||||
from zwave_js_server.exceptions import BaseZwaveJSServerError
|
||||
from zwave_js_server.model.driver import Driver
|
||||
from zwave_js_server.model.value import Value as ZwaveValue, get_value_id_str
|
||||
from zwave_js_server.model.value import (
|
||||
SetValueResult,
|
||||
Value as ZwaveValue,
|
||||
get_value_id_str,
|
||||
)
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import callback
|
||||
|
@ -70,9 +74,9 @@ class ZWaveBaseEntity(Entity):
|
|||
|
||||
async def _async_poll_value(self, value_or_id: str | ZwaveValue) -> None:
|
||||
"""Poll a value."""
|
||||
# We log an error instead of raising an exception because this service call occurs
|
||||
# in a separate task and we don't want to raise the exception in that separate task
|
||||
# because it is confusing to the user.
|
||||
# We log an error instead of raising an exception because this service call
|
||||
# occurs in a separate task and we don't want to raise the exception in that
|
||||
# separate task because it is confusing to the user.
|
||||
try:
|
||||
await self.info.node.async_poll_value(value_or_id)
|
||||
except BaseZwaveJSServerError as err:
|
||||
|
@ -312,7 +316,7 @@ class ZWaveBaseEntity(Entity):
|
|||
new_value: Any,
|
||||
options: dict | None = None,
|
||||
wait_for_result: bool | None = None,
|
||||
) -> bool | None:
|
||||
) -> SetValueResult | None:
|
||||
"""Set value on node."""
|
||||
try:
|
||||
return await self.info.node.async_set_value(
|
||||
|
|
|
@ -252,7 +252,7 @@ def async_get_node_from_entity_id(
|
|||
entity_entry = ent_reg.async_get(entity_id)
|
||||
|
||||
if entity_entry is None or entity_entry.platform != DOMAIN:
|
||||
raise ValueError(f"Entity {entity_id} is not a valid {DOMAIN} entity.")
|
||||
raise ValueError(f"Entity {entity_id} is not a valid {DOMAIN} entity")
|
||||
|
||||
# Assert for mypy, safe because we know that zwave_js entities are always
|
||||
# tied to a device
|
||||
|
@ -414,9 +414,7 @@ def copy_available_params(
|
|||
)
|
||||
|
||||
|
||||
def get_value_state_schema(
|
||||
value: ZwaveValue,
|
||||
) -> vol.Schema | None:
|
||||
def get_value_state_schema(value: ZwaveValue) -> vol.Schema | None:
|
||||
"""Return device automation schema for a config entry."""
|
||||
if isinstance(value, ConfigurationValue):
|
||||
min_ = value.metadata.min
|
||||
|
@ -427,6 +425,9 @@ def get_value_state_schema(
|
|||
):
|
||||
return vol.All(vol.Coerce(int), vol.Range(min=min_, max=max_))
|
||||
|
||||
if value.configuration_value_type == ConfigurationValueType.BOOLEAN:
|
||||
return vol.Coerce(bool)
|
||||
|
||||
if value.configuration_value_type == ConfigurationValueType.ENUMERATED:
|
||||
return vol.In({int(k): v for k, v in value.metadata.states.items()})
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"iot_class": "local_push",
|
||||
"loggers": ["zwave_js_server"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["pyserial==3.5", "zwave-js-server-python==0.49.0"],
|
||||
"requirements": ["pyserial==3.5", "zwave-js-server-python==0.50.1"],
|
||||
"usb": [
|
||||
{
|
||||
"vid": "0658",
|
||||
|
|
|
@ -8,7 +8,7 @@ from typing import Any
|
|||
|
||||
import voluptuous as vol
|
||||
from zwave_js_server.client import Client as ZwaveClient
|
||||
from zwave_js_server.const import CommandClass, CommandStatus
|
||||
from zwave_js_server.const import SET_VALUE_SUCCESS, CommandClass, CommandStatus
|
||||
from zwave_js_server.exceptions import FailedZWaveCommand, SetValueFailed
|
||||
from zwave_js_server.model.endpoint import Endpoint
|
||||
from zwave_js_server.model.node import Node as ZwaveNode
|
||||
|
@ -39,12 +39,6 @@ from .helpers import (
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SET_VALUE_FAILED_EXC = SetValueFailed(
|
||||
"Unable to set value, refer to "
|
||||
"https://zwave-js.github.io/node-zwave-js/#/api/node?id=setvalue for "
|
||||
"possible reasons"
|
||||
)
|
||||
|
||||
|
||||
def parameter_name_does_not_need_bitmask(
|
||||
val: dict[str, int | str | list[str]]
|
||||
|
@ -538,16 +532,20 @@ class ZWaveServices:
|
|||
nodes_list = list(nodes)
|
||||
# multiple set_values my fail so we will track the entire list
|
||||
set_value_failed_nodes_list: list[ZwaveNode | Endpoint] = []
|
||||
for node_, success in get_valid_responses_from_results(nodes_list, results):
|
||||
if success is False:
|
||||
# If we failed to set a value, add node to SetValueFailed exception list
|
||||
set_value_failed_error_list: list[SetValueFailed] = []
|
||||
for node_, result in get_valid_responses_from_results(nodes_list, results):
|
||||
if result and result.status not in SET_VALUE_SUCCESS:
|
||||
# If we failed to set a value, add node to exception list
|
||||
set_value_failed_nodes_list.append(node_)
|
||||
set_value_failed_error_list.append(
|
||||
SetValueFailed(f"{result.status} {result.message}")
|
||||
)
|
||||
|
||||
# Add the SetValueFailed exception to the results and the nodes to the node
|
||||
# list. No-op if there are no SetValueFailed exceptions
|
||||
# Add the exception to the results and the nodes to the node list. No-op if
|
||||
# no set value commands failed
|
||||
raise_exceptions_from_results(
|
||||
(*nodes_list, *set_value_failed_nodes_list),
|
||||
(*results, *([SET_VALUE_FAILED_EXC] * len(set_value_failed_nodes_list))),
|
||||
(*results, *set_value_failed_error_list),
|
||||
)
|
||||
|
||||
async def async_multicast_set_value(self, service: ServiceCall) -> None:
|
||||
|
@ -611,7 +609,7 @@ class ZWaveServices:
|
|||
new_value = str(new_value)
|
||||
|
||||
try:
|
||||
success = await async_multicast_set_value(
|
||||
result = await async_multicast_set_value(
|
||||
client=client,
|
||||
new_value=new_value,
|
||||
value_data=value,
|
||||
|
@ -621,10 +619,10 @@ class ZWaveServices:
|
|||
except FailedZWaveCommand as err:
|
||||
raise HomeAssistantError("Unable to set value via multicast") from err
|
||||
|
||||
if success is False:
|
||||
if result.status not in SET_VALUE_SUCCESS:
|
||||
raise HomeAssistantError(
|
||||
"Unable to set value via multicast"
|
||||
) from SetValueFailed
|
||||
) from SetValueFailed(f"{result.status} {result.message}")
|
||||
|
||||
async def async_ping(self, service: ServiceCall) -> None:
|
||||
"""Ping node(s)."""
|
||||
|
|
|
@ -2785,7 +2785,7 @@ zigpy==0.56.4
|
|||
zm-py==0.5.2
|
||||
|
||||
# homeassistant.components.zwave_js
|
||||
zwave-js-server-python==0.49.0
|
||||
zwave-js-server-python==0.50.1
|
||||
|
||||
# homeassistant.components.zwave_me
|
||||
zwave-me-ws==0.4.3
|
||||
|
|
|
@ -2049,7 +2049,7 @@ zigpy-znp==0.11.4
|
|||
zigpy==0.56.4
|
||||
|
||||
# homeassistant.components.zwave_js
|
||||
zwave-js-server-python==0.49.0
|
||||
zwave-js-server-python==0.50.1
|
||||
|
||||
# homeassistant.components.zwave_me
|
||||
zwave-me-ws==0.4.3
|
||||
|
|
|
@ -687,6 +687,9 @@ def mock_client_fixture(
|
|||
|
||||
client.version = VersionInfo.from_message(version_state)
|
||||
client.ws_server_url = "ws://test:3000/zjs"
|
||||
client.async_send_command.return_value = {
|
||||
"result": {"success": True, "status": 255}
|
||||
}
|
||||
|
||||
yield client
|
||||
|
||||
|
|
|
@ -270,5 +270,5 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"replaced": false
|
||||
"reason": 0
|
||||
}
|
||||
|
|
|
@ -276,14 +276,16 @@ async def test_subscribe_node_status(
|
|||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
node.data["ready"] = True
|
||||
new_node_data = deepcopy(multisensor_6_state)
|
||||
new_node_data["ready"] = True
|
||||
|
||||
event = Event(
|
||||
"ready",
|
||||
{
|
||||
"source": "node",
|
||||
"event": "ready",
|
||||
"nodeId": node.node_id,
|
||||
"nodeState": node.data,
|
||||
"nodeState": new_node_data,
|
||||
},
|
||||
)
|
||||
node.receive_event(event)
|
||||
|
@ -1715,7 +1717,7 @@ async def test_remove_node(
|
|||
assert len(client.async_send_command.call_args_list) == 1
|
||||
assert client.async_send_command.call_args[0][0] == {
|
||||
"command": "controller.begin_exclusion",
|
||||
"strategy": 0,
|
||||
"options": {"strategy": 0},
|
||||
}
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
|
|
|
@ -731,6 +731,8 @@ async def test_thermostat_raise_repair_issue_and_warning_when_setting_dry_preset
|
|||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test raise of repair issue and warning when setting Dry preset."""
|
||||
client.async_send_command.return_value = {"result": {"status": 1}}
|
||||
|
||||
state = hass.states.get(CLIMATE_AIDOO_HVAC_UNIT_ENTITY)
|
||||
assert state
|
||||
|
||||
|
@ -765,6 +767,7 @@ async def test_thermostat_raise_repair_issue_and_warning_when_setting_fan_preset
|
|||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test raise of repair issue and warning when setting Fan preset."""
|
||||
client.async_send_command.return_value = {"result": {"status": 1}}
|
||||
state = hass.states.get(CLIMATE_AIDOO_HVAC_UNIT_ENTITY)
|
||||
assert state
|
||||
|
||||
|
|
|
@ -126,6 +126,7 @@ async def test_window_cover(
|
|||
assert args["value"]
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test stop after opening
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
|
@ -265,6 +266,7 @@ async def test_fibaro_fgr222_shutter_cover(
|
|||
assert args["value"] == 99
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test closing tilts
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
|
@ -286,6 +288,7 @@ async def test_fibaro_fgr222_shutter_cover(
|
|||
assert args["value"] == 0
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test setting tilt position
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
|
@ -365,6 +368,7 @@ async def test_aeotec_nano_shutter_cover(
|
|||
assert args["value"]
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test stop after opening
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
|
|
|
@ -125,7 +125,13 @@ async def test_device_diagnostics(
|
|||
entity["entity_id"] == "test.unrelated_entity"
|
||||
for entity in diagnostics_data["entities"]
|
||||
)
|
||||
assert diagnostics_data["state"] == multisensor_6.data
|
||||
assert diagnostics_data["state"] == {
|
||||
**multisensor_6.data,
|
||||
"values": {id: val.data for id, val in multisensor_6.values.items()},
|
||||
"endpoints": {
|
||||
str(idx): endpoint.data for idx, endpoint in multisensor_6.endpoints.items()
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
async def test_device_diagnostics_error(hass: HomeAssistant, integration) -> None:
|
||||
|
@ -230,7 +236,11 @@ async def test_device_diagnostics_secret_value(
|
|||
"""Find ultraviolet property value in data."""
|
||||
return next(
|
||||
val
|
||||
for val in data["values"]
|
||||
for val in (
|
||||
data["values"]
|
||||
if isinstance(data["values"], list)
|
||||
else data["values"].values()
|
||||
)
|
||||
if val["commandClass"] == CommandClass.SENSOR_MULTILEVEL
|
||||
and val["property"] == PROPERTY_ULTRAVIOLET
|
||||
)
|
||||
|
|
|
@ -171,6 +171,7 @@ async def test_zooz_zen72(
|
|||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
await hass.services.async_call(
|
||||
NUMBER_DOMAIN,
|
||||
SERVICE_SET_VALUE,
|
||||
|
@ -256,6 +257,7 @@ async def test_indicator_test(
|
|||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == STATE_OFF
|
||||
|
||||
await hass.services.async_call(
|
||||
SWITCH_DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
|
|
|
@ -231,6 +231,7 @@ async def test_configurable_speeds_fan(
|
|||
async def get_zwave_speed_from_percentage(percentage):
|
||||
"""Set the fan to a particular percentage and get the resulting Zwave speed."""
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"turn_on",
|
||||
|
@ -356,6 +357,7 @@ async def test_ge_12730_fan(hass: HomeAssistant, client, ge_12730, integration)
|
|||
async def get_zwave_speed_from_percentage(percentage):
|
||||
"""Set the fan to a particular percentage and get the resulting Zwave speed."""
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"turn_on",
|
||||
|
@ -448,6 +450,7 @@ async def test_inovelli_lzw36(
|
|||
async def get_zwave_speed_from_percentage(percentage):
|
||||
"""Set the fan to a particular percentage and get the resulting Zwave speed."""
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"turn_on",
|
||||
|
@ -518,6 +521,7 @@ async def test_inovelli_lzw36(
|
|||
assert state.attributes[ATTR_PERCENTAGE] is None
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"turn_on",
|
||||
|
@ -553,6 +557,7 @@ async def test_leviton_zw4sf_fan(
|
|||
async def get_zwave_speed_from_percentage(percentage):
|
||||
"""Set the fan to a particular percentage and get the resulting Zwave speed."""
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"turn_on",
|
||||
|
@ -951,6 +956,7 @@ async def test_honeywell_39358_fan(
|
|||
async def get_zwave_speed_from_percentage(percentage):
|
||||
"""Set the fan to a particular percentage and get the resulting Zwave speed."""
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"turn_on",
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
"""Test the Z-Wave JS helpers module."""
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.zwave_js.helpers import (
|
||||
async_get_node_status_sensor_entity_id,
|
||||
async_get_nodes_from_area_id,
|
||||
get_value_state_schema,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import area_registry as ar, device_registry as dr
|
||||
|
@ -22,3 +25,14 @@ async def test_async_get_nodes_from_area_id(hass: HomeAssistant) -> None:
|
|||
area_reg = ar.async_get(hass)
|
||||
area = area_reg.async_create("test")
|
||||
assert not async_get_nodes_from_area_id(hass, area.id)
|
||||
|
||||
|
||||
async def test_get_value_state_schema_boolean_config_value(
|
||||
hass: HomeAssistant, client, aeon_smart_switch_6
|
||||
) -> None:
|
||||
"""Test get_value_state_schema for boolean config value."""
|
||||
schema_validator = get_value_state_schema(
|
||||
aeon_smart_switch_6.values["102-112-0-255"]
|
||||
)
|
||||
assert isinstance(schema_validator, vol.Coerce)
|
||||
assert schema_validator.type == bool
|
||||
|
|
|
@ -1005,7 +1005,7 @@ async def test_node_removed(
|
|||
event = {
|
||||
"source": "controller",
|
||||
"event": "node added",
|
||||
"node": node.data,
|
||||
"node": multisensor_6_state,
|
||||
"result": {},
|
||||
}
|
||||
|
||||
|
@ -1014,7 +1014,7 @@ async def test_node_removed(
|
|||
old_device = dev_reg.async_get_device(identifiers={(DOMAIN, device_id)})
|
||||
assert old_device.id
|
||||
|
||||
event = {"node": node, "replaced": False}
|
||||
event = {"node": node, "reason": 0}
|
||||
|
||||
client.driver.controller.emit("node removed", event)
|
||||
await hass.async_block_till_done()
|
||||
|
@ -1047,14 +1047,14 @@ async def test_replace_same_node(
|
|||
|
||||
assert hass.states.get(AIR_TEMPERATURE_SENSOR)
|
||||
|
||||
# A replace node event has the extra field "replaced" set to True
|
||||
# A replace node event has the extra field "reason"
|
||||
# to distinguish it from an exclusion
|
||||
event = Event(
|
||||
type="node removed",
|
||||
data={
|
||||
"source": "controller",
|
||||
"event": "node removed",
|
||||
"replaced": True,
|
||||
"reason": 3,
|
||||
"node": multisensor_6_state,
|
||||
},
|
||||
)
|
||||
|
@ -1139,8 +1139,8 @@ async def test_replace_different_node(
|
|||
"""Test when a node is replaced with a different node."""
|
||||
dev_reg = dr.async_get(hass)
|
||||
node_id = multisensor_6.node_id
|
||||
hank_binary_switch_state = deepcopy(hank_binary_switch_state)
|
||||
hank_binary_switch_state["nodeId"] = node_id
|
||||
state = deepcopy(hank_binary_switch_state)
|
||||
state["nodeId"] = node_id
|
||||
|
||||
device_id = f"{client.driver.controller.home_id}-{node_id}"
|
||||
multisensor_6_device_id = (
|
||||
|
@ -1148,9 +1148,9 @@ async def test_replace_different_node(
|
|||
f"{multisensor_6.product_type}:{multisensor_6.product_id}"
|
||||
)
|
||||
hank_device_id = (
|
||||
f"{device_id}-{hank_binary_switch_state['manufacturerId']}:"
|
||||
f"{hank_binary_switch_state['productType']}:"
|
||||
f"{hank_binary_switch_state['productId']}"
|
||||
f"{device_id}-{state['manufacturerId']}:"
|
||||
f"{state['productType']}:"
|
||||
f"{state['productId']}"
|
||||
)
|
||||
|
||||
device = dev_reg.async_get_device(identifiers={(DOMAIN, device_id)})
|
||||
|
@ -1171,7 +1171,7 @@ async def test_replace_different_node(
|
|||
data={
|
||||
"source": "controller",
|
||||
"event": "node removed",
|
||||
"replaced": True,
|
||||
"reason": 3,
|
||||
"node": multisensor_6_state,
|
||||
},
|
||||
)
|
||||
|
@ -1228,7 +1228,7 @@ async def test_replace_different_node(
|
|||
"source": "node",
|
||||
"event": "ready",
|
||||
"nodeId": node_id,
|
||||
"nodeState": hank_binary_switch_state,
|
||||
"nodeState": state,
|
||||
},
|
||||
)
|
||||
client.driver.receive_event(event)
|
||||
|
@ -1345,7 +1345,7 @@ async def test_disabled_node_status_entity_on_node_replaced(
|
|||
data={
|
||||
"source": "controller",
|
||||
"event": "node removed",
|
||||
"replaced": True,
|
||||
"reason": 3,
|
||||
"node": zp3111_state,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -414,6 +414,7 @@ async def test_bulk_set_config_parameters(
|
|||
identifiers={get_device_id(client.driver, multisensor_6)}
|
||||
)
|
||||
assert device
|
||||
|
||||
# Test setting config parameter by property and property_key
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
|
@ -875,7 +876,9 @@ async def test_set_value(
|
|||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test that when a command fails we raise an exception
|
||||
client.async_send_command.return_value = {"success": False}
|
||||
client.async_send_command.return_value = {
|
||||
"result": {"status": 2, "message": "test"}
|
||||
}
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
|
@ -924,7 +927,6 @@ async def test_set_value_string(
|
|||
hass: HomeAssistant, client, climate_danfoss_lc_13, lock_schlage_be469, integration
|
||||
) -> None:
|
||||
"""Test set_value service converts number to string when needed."""
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
# Test that number gets converted to a string when needed
|
||||
await hass.services.async_call(
|
||||
|
@ -1240,7 +1242,9 @@ async def test_multicast_set_value(
|
|||
)
|
||||
|
||||
# Test that when a command is unsuccessful we raise an exception
|
||||
client.async_send_command.return_value = {"success": False}
|
||||
client.async_send_command.return_value = {
|
||||
"result": {"status": 2, "message": "test"}
|
||||
}
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
|
@ -1381,7 +1385,7 @@ async def test_multicast_set_value_string(
|
|||
integration,
|
||||
) -> None:
|
||||
"""Test multicast_set_value service converts number to string when needed."""
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
client.async_send_command.return_value = {"result": {"status": 255}}
|
||||
|
||||
# Test that number gets converted to a string when needed
|
||||
await hass.services.async_call(
|
||||
|
|
|
@ -63,6 +63,8 @@ async def test_switch(
|
|||
state = hass.states.get(SWITCH_ENTITY)
|
||||
assert state.state == "on"
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test turning off
|
||||
await hass.services.async_call(
|
||||
"switch", "turn_off", {"entity_id": SWITCH_ENTITY}, blocking=True
|
||||
|
|
|
@ -1158,7 +1158,7 @@ async def test_server_reconnect_event(
|
|||
data={
|
||||
"source": "controller",
|
||||
"event": "node removed",
|
||||
"replaced": False,
|
||||
"reason": 0,
|
||||
"node": lock_schlage_be469_state,
|
||||
},
|
||||
)
|
||||
|
@ -1238,7 +1238,7 @@ async def test_server_reconnect_value_updated(
|
|||
data={
|
||||
"source": "controller",
|
||||
"event": "node removed",
|
||||
"replaced": False,
|
||||
"reason": 0,
|
||||
"node": lock_schlage_be469_state,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -264,6 +264,8 @@ async def test_update_entity_ha_not_running(
|
|||
"""Test update occurs only after HA is running."""
|
||||
await hass.async_stop()
|
||||
|
||||
client.async_send_command.return_value = {"updates": []}
|
||||
|
||||
entry = MockConfigEntry(domain="zwave_js", data={"url": "ws://test.org"})
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
@ -341,7 +343,9 @@ async def test_update_entity_progress(
|
|||
assert attrs[ATTR_LATEST_VERSION] == "11.2.4"
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {"success": False}
|
||||
client.async_send_command.return_value = {
|
||||
"result": {"status": 2, "success": False, "reInterview": False}
|
||||
}
|
||||
|
||||
# Test successful install call without a version
|
||||
install_task = hass.async_create_task(
|
||||
|
@ -437,7 +441,9 @@ async def test_update_entity_install_failed(
|
|||
assert attrs[ATTR_LATEST_VERSION] == "11.2.4"
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {"success": False}
|
||||
client.async_send_command.return_value = {
|
||||
"result": {"status": 2, "success": False, "reInterview": False}
|
||||
}
|
||||
|
||||
# Test install call - we expect it to finish fail
|
||||
install_task = hass.async_create_task(
|
||||
|
@ -577,6 +583,7 @@ async def test_update_entity_delay(
|
|||
) -> None:
|
||||
"""Test update occurs on a delay after HA starts."""
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {"updates": []}
|
||||
await hass.async_stop()
|
||||
|
||||
entry = MockConfigEntry(domain="zwave_js", data={"url": "ws://test.org"})
|
||||
|
@ -710,7 +717,9 @@ async def test_update_entity_full_restore_data_update_available(
|
|||
assert state.attributes[ATTR_SKIPPED_VERSION] is None
|
||||
assert state.attributes[ATTR_LATEST_VERSION] == "11.2.4"
|
||||
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
client.async_send_command.return_value = {
|
||||
"result": {"status": 255, "success": True, "reInterview": False}
|
||||
}
|
||||
|
||||
# Test successful install call without a version
|
||||
install_task = hass.async_create_task(
|
||||
|
|
Loading…
Add table
Reference in a new issue