Handle FailedCommand exceptions in zwave_js WS API (#52461)
* Handle zwave-js errors in WS API * Unsubscribe callbacks when zwave-js error is caught * fix tests * simplify unsub logic * add tests * add kwargs to be safe * use existing msg format * switch to generic failed command handling * remove unneeded constant * Update homeassistant/components/zwave_js/api.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update homeassistant/components/zwave_js/api.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * fix Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
92e4013f73
commit
e915f5be53
2 changed files with 507 additions and 31 deletions
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
|||
import dataclasses
|
||||
from functools import partial, wraps
|
||||
import json
|
||||
from typing import Callable
|
||||
from typing import Any, Callable
|
||||
|
||||
from aiohttp import hdrs, web, web_exceptions, web_request
|
||||
import voluptuous as vol
|
||||
|
@ -13,6 +13,7 @@ from zwave_js_server.client import Client
|
|||
from zwave_js_server.const import CommandClass, LogLevel
|
||||
from zwave_js_server.exceptions import (
|
||||
BaseZwaveJSServerError,
|
||||
FailedCommand,
|
||||
InvalidNewValue,
|
||||
NotFoundError,
|
||||
SetValueFailed,
|
||||
|
@ -47,6 +48,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
|||
from .const import (
|
||||
CONF_DATA_COLLECTION_OPTED_IN,
|
||||
DATA_CLIENT,
|
||||
DATA_UNSUBSCRIBE,
|
||||
DOMAIN,
|
||||
EVENT_DEVICE_ADDED_TO_REGISTRY,
|
||||
)
|
||||
|
@ -134,6 +136,30 @@ def async_get_node(orig_func: Callable) -> Callable:
|
|||
return async_get_node_func
|
||||
|
||||
|
||||
def async_handle_failed_command(orig_func: Callable) -> Callable:
|
||||
"""Decorate async function to handle FailedCommand and send relevant error."""
|
||||
|
||||
@wraps(orig_func)
|
||||
async def async_handle_failed_command_func(
|
||||
hass: HomeAssistant,
|
||||
connection: ActiveConnection,
|
||||
msg: dict,
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Handle FailedCommand within function and send relevant error."""
|
||||
try:
|
||||
await orig_func(hass, connection, msg, *args, **kwargs)
|
||||
except FailedCommand as err:
|
||||
# Unsubscribe to callbacks
|
||||
if unsubs := msg.get(DATA_UNSUBSCRIBE):
|
||||
for unsub in unsubs:
|
||||
unsub()
|
||||
connection.send_error(msg[ID], err.error_code, err.args[0])
|
||||
|
||||
return async_handle_failed_command_func
|
||||
|
||||
|
||||
@callback
|
||||
def async_register_api(hass: HomeAssistant) -> None:
|
||||
"""Register all of our api endpoints."""
|
||||
|
@ -318,6 +344,7 @@ async def websocket_node_metadata(
|
|||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_node
|
||||
async def websocket_ping_node(
|
||||
hass: HomeAssistant,
|
||||
|
@ -342,6 +369,7 @@ async def websocket_ping_node(
|
|||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_entry
|
||||
async def websocket_add_node(
|
||||
hass: HomeAssistant,
|
||||
|
@ -410,7 +438,7 @@ async def websocket_add_node(
|
|||
)
|
||||
|
||||
connection.subscriptions[msg["id"]] = async_cleanup
|
||||
unsubs = [
|
||||
msg[DATA_UNSUBSCRIBE] = unsubs = [
|
||||
controller.on("inclusion started", forward_event),
|
||||
controller.on("inclusion failed", forward_event),
|
||||
controller.on("inclusion stopped", forward_event),
|
||||
|
@ -435,6 +463,7 @@ async def websocket_add_node(
|
|||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_entry
|
||||
async def websocket_stop_inclusion(
|
||||
hass: HomeAssistant,
|
||||
|
@ -460,6 +489,7 @@ async def websocket_stop_inclusion(
|
|||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_entry
|
||||
async def websocket_stop_exclusion(
|
||||
hass: HomeAssistant,
|
||||
|
@ -485,6 +515,7 @@ async def websocket_stop_exclusion(
|
|||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_entry
|
||||
async def websocket_remove_node(
|
||||
hass: HomeAssistant,
|
||||
|
@ -522,7 +553,7 @@ async def websocket_remove_node(
|
|||
)
|
||||
|
||||
connection.subscriptions[msg["id"]] = async_cleanup
|
||||
unsubs = [
|
||||
msg[DATA_UNSUBSCRIBE] = unsubs = [
|
||||
controller.on("exclusion started", forward_event),
|
||||
controller.on("exclusion failed", forward_event),
|
||||
controller.on("exclusion stopped", forward_event),
|
||||
|
@ -546,6 +577,7 @@ async def websocket_remove_node(
|
|||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_entry
|
||||
async def websocket_replace_failed_node(
|
||||
hass: HomeAssistant,
|
||||
|
@ -628,7 +660,7 @@ async def websocket_replace_failed_node(
|
|||
)
|
||||
|
||||
connection.subscriptions[msg["id"]] = async_cleanup
|
||||
unsubs = [
|
||||
msg[DATA_UNSUBSCRIBE] = unsubs = [
|
||||
controller.on("inclusion started", forward_event),
|
||||
controller.on("inclusion failed", forward_event),
|
||||
controller.on("inclusion stopped", forward_event),
|
||||
|
@ -655,6 +687,7 @@ async def websocket_replace_failed_node(
|
|||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_entry
|
||||
async def websocket_remove_failed_node(
|
||||
hass: HomeAssistant,
|
||||
|
@ -670,7 +703,8 @@ async def websocket_remove_failed_node(
|
|||
@callback
|
||||
def async_cleanup() -> None:
|
||||
"""Remove signal listeners."""
|
||||
unsub()
|
||||
for unsub in unsubs:
|
||||
unsub()
|
||||
|
||||
@callback
|
||||
def node_removed(event: dict) -> None:
|
||||
|
@ -686,7 +720,7 @@ async def websocket_remove_failed_node(
|
|||
)
|
||||
|
||||
connection.subscriptions[msg["id"]] = async_cleanup
|
||||
unsub = controller.on("node removed", node_removed)
|
||||
msg[DATA_UNSUBSCRIBE] = unsubs = [controller.on("node removed", node_removed)]
|
||||
|
||||
result = await controller.async_remove_failed_node(node_id)
|
||||
connection.send_result(
|
||||
|
@ -703,6 +737,7 @@ async def websocket_remove_failed_node(
|
|||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_entry
|
||||
async def websocket_begin_healing_network(
|
||||
hass: HomeAssistant,
|
||||
|
@ -755,7 +790,7 @@ async def websocket_subscribe_heal_network_progress(
|
|||
)
|
||||
|
||||
connection.subscriptions[msg["id"]] = async_cleanup
|
||||
unsubs = [
|
||||
msg[DATA_UNSUBSCRIBE] = unsubs = [
|
||||
controller.on("heal network progress", partial(forward_event, "progress")),
|
||||
controller.on("heal network done", partial(forward_event, "result")),
|
||||
]
|
||||
|
@ -771,6 +806,7 @@ async def websocket_subscribe_heal_network_progress(
|
|||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_entry
|
||||
async def websocket_stop_healing_network(
|
||||
hass: HomeAssistant,
|
||||
|
@ -797,6 +833,7 @@ async def websocket_stop_healing_network(
|
|||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_entry
|
||||
async def websocket_heal_node(
|
||||
hass: HomeAssistant,
|
||||
|
@ -824,6 +861,7 @@ async def websocket_heal_node(
|
|||
},
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_node
|
||||
async def websocket_refresh_node_info(
|
||||
hass: HomeAssistant,
|
||||
|
@ -854,7 +892,7 @@ async def websocket_refresh_node_info(
|
|||
)
|
||||
|
||||
connection.subscriptions[msg["id"]] = async_cleanup
|
||||
unsubs = [
|
||||
msg[DATA_UNSUBSCRIBE] = unsubs = [
|
||||
node.on("interview started", forward_event),
|
||||
node.on("interview completed", forward_event),
|
||||
node.on("interview stage completed", forward_stage),
|
||||
|
@ -874,6 +912,7 @@ async def websocket_refresh_node_info(
|
|||
},
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_node
|
||||
async def websocket_refresh_node_values(
|
||||
hass: HomeAssistant,
|
||||
|
@ -896,6 +935,7 @@ async def websocket_refresh_node_values(
|
|||
},
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_node
|
||||
async def websocket_refresh_node_cc_values(
|
||||
hass: HomeAssistant,
|
||||
|
@ -930,6 +970,7 @@ async def websocket_refresh_node_cc_values(
|
|||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_node
|
||||
async def websocket_set_config_parameter(
|
||||
hass: HomeAssistant,
|
||||
|
@ -1027,6 +1068,7 @@ def filename_is_present_if_logging_to_file(obj: dict) -> dict:
|
|||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_entry
|
||||
async def websocket_subscribe_log_updates(
|
||||
hass: HomeAssistant,
|
||||
|
@ -1076,7 +1118,7 @@ async def websocket_subscribe_log_updates(
|
|||
)
|
||||
)
|
||||
|
||||
unsubs = [
|
||||
msg[DATA_UNSUBSCRIBE] = unsubs = [
|
||||
driver.on("logging", log_messages),
|
||||
driver.on("log config updated", log_config_updates),
|
||||
]
|
||||
|
@ -1114,6 +1156,7 @@ async def websocket_subscribe_log_updates(
|
|||
},
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_entry
|
||||
async def websocket_update_log_config(
|
||||
hass: HomeAssistant,
|
||||
|
@ -1161,6 +1204,7 @@ async def websocket_get_log_config(
|
|||
},
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_entry
|
||||
async def websocket_update_data_collection_preference(
|
||||
hass: HomeAssistant,
|
||||
|
@ -1191,6 +1235,7 @@ async def websocket_update_data_collection_preference(
|
|||
},
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_entry
|
||||
async def websocket_data_collection_status(
|
||||
hass: HomeAssistant,
|
||||
|
@ -1273,6 +1318,7 @@ async def websocket_version_info(
|
|||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_node
|
||||
async def websocket_abort_firmware_update(
|
||||
hass: HomeAssistant,
|
||||
|
@ -1337,7 +1383,7 @@ async def websocket_subscribe_firmware_update_status(
|
|||
)
|
||||
)
|
||||
|
||||
unsubs = [
|
||||
msg[DATA_UNSUBSCRIBE] = unsubs = [
|
||||
node.on("firmware update progress", forward_progress),
|
||||
node.on("firmware update finished", forward_finished),
|
||||
]
|
||||
|
@ -1400,6 +1446,7 @@ class FirmwareUploadView(HomeAssistantView):
|
|||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_entry
|
||||
async def websocket_check_for_config_updates(
|
||||
hass: HomeAssistant,
|
||||
|
@ -1427,6 +1474,7 @@ async def websocket_check_for_config_updates(
|
|||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_handle_failed_command
|
||||
@async_get_entry
|
||||
async def websocket_install_config_update(
|
||||
hass: HomeAssistant,
|
||||
|
|
|
@ -7,6 +7,7 @@ from zwave_js_server.const import LogLevel
|
|||
from zwave_js_server.event import Event
|
||||
from zwave_js_server.exceptions import (
|
||||
FailedCommand,
|
||||
FailedZWaveCommand,
|
||||
InvalidNewValue,
|
||||
NotFoundError,
|
||||
SetValueFailed,
|
||||
|
@ -280,13 +281,32 @@ async def test_ping_node(
|
|||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == ERR_NOT_FOUND
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.node.Node.async_ping",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 5,
|
||||
TYPE: "zwave_js/ping_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: node.node_id,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 5,
|
||||
ID: 6,
|
||||
TYPE: "zwave_js/ping_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: node.node_id,
|
||||
|
@ -385,12 +405,30 @@ async def test_add_node(
|
|||
msg = await ws_client.receive_json()
|
||||
assert msg["event"]["event"] == "interview failed"
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.controller.Controller.async_begin_inclusion",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
TYPE: "zwave_js/add_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{ID: 4, TYPE: "zwave_js/add_node", ENTRY_ID: entry.entry_id}
|
||||
{ID: 5, TYPE: "zwave_js/add_node", ENTRY_ID: entry.entry_id}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
|
@ -419,12 +457,48 @@ async def test_cancel_inclusion_exclusion(hass, integration, client, hass_ws_cli
|
|||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.controller.Controller.async_stop_inclusion",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 6,
|
||||
TYPE: "zwave_js/stop_inclusion",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.controller.Controller.async_stop_exclusion",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 7,
|
||||
TYPE: "zwave_js/stop_exclusion",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{ID: 6, TYPE: "zwave_js/stop_inclusion", ENTRY_ID: entry.entry_id}
|
||||
{ID: 8, TYPE: "zwave_js/stop_inclusion", ENTRY_ID: entry.entry_id}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
|
@ -432,7 +506,7 @@ async def test_cancel_inclusion_exclusion(hass, integration, client, hass_ws_cli
|
|||
assert msg["error"]["code"] == ERR_NOT_LOADED
|
||||
|
||||
await ws_client.send_json(
|
||||
{ID: 7, TYPE: "zwave_js/stop_exclusion", ENTRY_ID: entry.entry_id}
|
||||
{ID: 9, TYPE: "zwave_js/stop_exclusion", ENTRY_ID: entry.entry_id}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
|
@ -494,12 +568,30 @@ async def test_remove_node(
|
|||
)
|
||||
assert device is None
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.controller.Controller.async_begin_exclusion",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
TYPE: "zwave_js/remove_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{ID: 4, TYPE: "zwave_js/remove_node", ENTRY_ID: entry.entry_id}
|
||||
{ID: 5, TYPE: "zwave_js/remove_node", ENTRY_ID: entry.entry_id}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
|
@ -641,13 +733,32 @@ async def test_replace_failed_node(
|
|||
msg = await ws_client.receive_json()
|
||||
assert msg["event"]["event"] == "interview failed"
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.controller.Controller.async_replace_failed_node",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
TYPE: "zwave_js/replace_failed_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 67,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
ID: 5,
|
||||
TYPE: "zwave_js/replace_failed_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 67,
|
||||
|
@ -705,13 +816,32 @@ async def test_remove_failed_node(
|
|||
)
|
||||
assert device is None
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.controller.Controller.async_remove_failed_node",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
TYPE: "zwave_js/remove_failed_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 67,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
ID: 5,
|
||||
TYPE: "zwave_js/remove_failed_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 67,
|
||||
|
@ -747,13 +877,31 @@ async def test_begin_healing_network(
|
|||
assert msg["success"]
|
||||
assert msg["result"]
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.controller.Controller.async_begin_healing_network",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
TYPE: "zwave_js/begin_healing_network",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
ID: 5,
|
||||
TYPE: "zwave_js/begin_healing_network",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
|
@ -837,13 +985,31 @@ async def test_stop_healing_network(
|
|||
assert msg["success"]
|
||||
assert msg["result"]
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.controller.Controller.async_stop_healing_network",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
TYPE: "zwave_js/stop_healing_network",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
ID: 5,
|
||||
TYPE: "zwave_js/stop_healing_network",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
|
@ -879,13 +1045,32 @@ async def test_heal_node(
|
|||
assert msg["success"]
|
||||
assert msg["result"]
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.controller.Controller.async_heal_node",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
TYPE: "zwave_js/heal_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 67,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
ID: 5,
|
||||
TYPE: "zwave_js/heal_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 67,
|
||||
|
@ -978,13 +1163,32 @@ async def test_refresh_node_info(
|
|||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == ERR_NOT_FOUND
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.node.Node.async_refresh_info",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 3,
|
||||
TYPE: "zwave_js/refresh_node_info",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 52,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 3,
|
||||
ID: 4,
|
||||
TYPE: "zwave_js/refresh_node_info",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 52,
|
||||
|
@ -1048,6 +1252,42 @@ async def test_refresh_node_values(
|
|||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == ERR_NOT_FOUND
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.node.Node.async_refresh_values",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
TYPE: "zwave_js/refresh_node_values",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 52,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 5,
|
||||
TYPE: "zwave_js/refresh_node_values",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 52,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == ERR_NOT_LOADED
|
||||
|
||||
|
||||
async def test_refresh_node_cc_values(
|
||||
hass, client, multisensor_6, integration, hass_ws_client
|
||||
|
@ -1105,13 +1345,33 @@ async def test_refresh_node_cc_values(
|
|||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == ERR_NOT_FOUND
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.node.Node.async_refresh_cc_values",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
TYPE: "zwave_js/refresh_node_cc_values",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 52,
|
||||
COMMAND_CLASS_ID: 112,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
ID: 5,
|
||||
TYPE: "zwave_js/refresh_node_cc_values",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 52,
|
||||
|
@ -1307,13 +1567,35 @@ async def test_set_config_parameter(
|
|||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == ERR_NOT_FOUND
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"homeassistant.components.zwave_js.api.async_set_config_parameter",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 7,
|
||||
TYPE: "zwave_js/set_config_parameter",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 52,
|
||||
PROPERTY: 102,
|
||||
PROPERTY_KEY: 1,
|
||||
VALUE: 1,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 7,
|
||||
ID: 8,
|
||||
TYPE: "zwave_js/set_config_parameter",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 52,
|
||||
|
@ -1625,12 +1907,30 @@ async def test_subscribe_log_updates(hass, integration, client, hass_ws_client):
|
|||
},
|
||||
}
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.driver.Driver.async_start_listening_logs",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 2,
|
||||
TYPE: "zwave_js/subscribe_log_updates",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{ID: 2, TYPE: "zwave_js/subscribe_log_updates", ENTRY_ID: entry.entry_id}
|
||||
{ID: 3, TYPE: "zwave_js/subscribe_log_updates", ENTRY_ID: entry.entry_id}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
|
@ -1757,13 +2057,32 @@ async def test_update_log_config(hass, client, integration, hass_ws_client):
|
|||
and "must be provided if logging to file" in msg["error"]["message"]
|
||||
)
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.driver.Driver.async_update_log_config",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 7,
|
||||
TYPE: "zwave_js/update_log_config",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
CONFIG: {LEVEL: "Error"},
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 7,
|
||||
ID: 8,
|
||||
TYPE: "zwave_js/update_log_config",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
CONFIG: {LEVEL: "Error"},
|
||||
|
@ -1884,13 +2203,50 @@ async def test_data_collection(hass, client, integration, hass_ws_client):
|
|||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.driver.Driver.async_is_statistics_enabled",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
TYPE: "zwave_js/data_collection_status",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.driver.Driver.async_enable_statistics",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 5,
|
||||
TYPE: "zwave_js/update_data_collection_preference",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
OPTED_IN: True,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
ID: 6,
|
||||
TYPE: "zwave_js/data_collection_status",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
|
@ -1902,7 +2258,7 @@ async def test_data_collection(hass, client, integration, hass_ws_client):
|
|||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 5,
|
||||
ID: 7,
|
||||
TYPE: "zwave_js/update_data_collection_preference",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
OPTED_IN: True,
|
||||
|
@ -1938,6 +2294,42 @@ async def test_abort_firmware_update(
|
|||
assert args["command"] == "node.abort_firmware_update"
|
||||
assert args["nodeId"] == multisensor_6.node_id
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.node.Node.async_abort_firmware_update",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 2,
|
||||
TYPE: "zwave_js/abort_firmware_update",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: multisensor_6.node_id,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 3,
|
||||
TYPE: "zwave_js/abort_firmware_update",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: multisensor_6.node_id,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == ERR_NOT_LOADED
|
||||
|
||||
|
||||
async def test_abort_firmware_update_failures(
|
||||
hass, integration, multisensor_6, client, hass_ws_client
|
||||
|
@ -2128,13 +2520,31 @@ async def test_check_for_config_updates(hass, client, integration, hass_ws_clien
|
|||
assert config_update["update_available"]
|
||||
assert config_update["new_version"] == "test"
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.driver.Driver.async_check_for_config_updates",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 2,
|
||||
TYPE: "zwave_js/check_for_config_updates",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 2,
|
||||
ID: 3,
|
||||
TYPE: "zwave_js/check_for_config_updates",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
|
@ -2146,7 +2556,7 @@ async def test_check_for_config_updates(hass, client, integration, hass_ws_clien
|
|||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 3,
|
||||
ID: 4,
|
||||
TYPE: "zwave_js/check_for_config_updates",
|
||||
ENTRY_ID: "INVALID",
|
||||
}
|
||||
|
@ -2175,13 +2585,31 @@ async def test_install_config_update(hass, client, integration, hass_ws_client):
|
|||
assert msg["result"]
|
||||
assert msg["success"]
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.driver.Driver.async_install_config_update",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 2,
|
||||
TYPE: "zwave_js/install_config_update",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == "zwave_error"
|
||||
assert msg["error"]["message"] == "Z-Wave error 1: error message"
|
||||
|
||||
# Test sending command with not loaded entry fails
|
||||
await hass.config_entries.async_unload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 2,
|
||||
ID: 3,
|
||||
TYPE: "zwave_js/install_config_update",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
|
@ -2193,7 +2621,7 @@ async def test_install_config_update(hass, client, integration, hass_ws_client):
|
|||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 3,
|
||||
ID: 4,
|
||||
TYPE: "zwave_js/install_config_update",
|
||||
ENTRY_ID: "INVALID",
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue