Add zwave_js heal node and network WS API commands (#51047)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>pull/51112/head
parent
8edf7f0407
commit
6d9b67ddb2
|
@ -2,7 +2,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
from functools import wraps
|
||||
from functools import partial, wraps
|
||||
import json
|
||||
from typing import Callable
|
||||
|
||||
|
@ -142,18 +142,24 @@ def async_register_api(hass: HomeAssistant) -> None:
|
|||
websocket_api.async_register_command(hass, websocket_ping_node)
|
||||
websocket_api.async_register_command(hass, websocket_add_node)
|
||||
websocket_api.async_register_command(hass, websocket_stop_inclusion)
|
||||
websocket_api.async_register_command(hass, websocket_stop_exclusion)
|
||||
websocket_api.async_register_command(hass, websocket_remove_node)
|
||||
websocket_api.async_register_command(hass, websocket_remove_failed_node)
|
||||
websocket_api.async_register_command(hass, websocket_replace_failed_node)
|
||||
websocket_api.async_register_command(hass, websocket_stop_exclusion)
|
||||
websocket_api.async_register_command(hass, websocket_begin_healing_network)
|
||||
websocket_api.async_register_command(
|
||||
hass, websocket_subscribe_heal_network_progress
|
||||
)
|
||||
websocket_api.async_register_command(hass, websocket_stop_healing_network)
|
||||
websocket_api.async_register_command(hass, websocket_refresh_node_info)
|
||||
websocket_api.async_register_command(hass, websocket_refresh_node_values)
|
||||
websocket_api.async_register_command(hass, websocket_refresh_node_cc_values)
|
||||
websocket_api.async_register_command(hass, websocket_heal_node)
|
||||
websocket_api.async_register_command(hass, websocket_set_config_parameter)
|
||||
websocket_api.async_register_command(hass, websocket_get_config_parameters)
|
||||
websocket_api.async_register_command(hass, websocket_subscribe_logs)
|
||||
websocket_api.async_register_command(hass, websocket_update_log_config)
|
||||
websocket_api.async_register_command(hass, websocket_get_log_config)
|
||||
websocket_api.async_register_command(hass, websocket_get_config_parameters)
|
||||
websocket_api.async_register_command(hass, websocket_set_config_parameter)
|
||||
websocket_api.async_register_command(
|
||||
hass, websocket_update_data_collection_preference
|
||||
)
|
||||
|
@ -180,6 +186,7 @@ async def websocket_network_status(
|
|||
client: Client,
|
||||
) -> None:
|
||||
"""Get the status of the Z-Wave JS network."""
|
||||
controller = client.driver.controller
|
||||
data = {
|
||||
"client": {
|
||||
"ws_server_url": client.ws_server_url,
|
||||
|
@ -188,7 +195,24 @@ async def websocket_network_status(
|
|||
"server_version": client.version.server_version,
|
||||
},
|
||||
"controller": {
|
||||
"home_id": client.driver.controller.data["homeId"],
|
||||
"home_id": controller.home_id,
|
||||
"library_version": controller.library_version,
|
||||
"type": controller.controller_type,
|
||||
"own_node_id": controller.own_node_id,
|
||||
"is_secondary": controller.is_secondary,
|
||||
"is_using_home_id_from_other_network": controller.is_using_home_id_from_other_network,
|
||||
"is_sis_present": controller.is_SIS_present,
|
||||
"was_real_primary": controller.was_real_primary,
|
||||
"is_static_update_controller": controller.is_static_update_controller,
|
||||
"is_slave": controller.is_slave,
|
||||
"serial_api_version": controller.serial_api_version,
|
||||
"manufacturer_id": controller.manufacturer_id,
|
||||
"product_id": controller.product_id,
|
||||
"product_type": controller.product_type,
|
||||
"supported_function_types": controller.supported_function_types,
|
||||
"suc_node_id": controller.suc_node_id,
|
||||
"supports_timers": controller.supports_timers,
|
||||
"is_heal_network_active": controller.is_heal_network_active,
|
||||
"nodes": list(client.driver.controller.nodes),
|
||||
},
|
||||
}
|
||||
|
@ -643,6 +667,126 @@ async def websocket_remove_failed_node(
|
|||
)
|
||||
|
||||
|
||||
@websocket_api.require_admin
|
||||
@websocket_api.websocket_command(
|
||||
{
|
||||
vol.Required(TYPE): "zwave_js/begin_healing_network",
|
||||
vol.Required(ENTRY_ID): str,
|
||||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_get_entry
|
||||
async def websocket_begin_healing_network(
|
||||
hass: HomeAssistant,
|
||||
connection: ActiveConnection,
|
||||
msg: dict,
|
||||
entry: ConfigEntry,
|
||||
client: Client,
|
||||
) -> None:
|
||||
"""Begin healing the Z-Wave network."""
|
||||
controller = client.driver.controller
|
||||
|
||||
result = await controller.async_begin_healing_network()
|
||||
connection.send_result(
|
||||
msg[ID],
|
||||
result,
|
||||
)
|
||||
|
||||
|
||||
@websocket_api.require_admin
|
||||
@websocket_api.websocket_command(
|
||||
{
|
||||
vol.Required(TYPE): "zwave_js/subscribe_heal_network_progress",
|
||||
vol.Required(ENTRY_ID): str,
|
||||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_get_entry
|
||||
async def websocket_subscribe_heal_network_progress(
|
||||
hass: HomeAssistant,
|
||||
connection: ActiveConnection,
|
||||
msg: dict,
|
||||
entry: ConfigEntry,
|
||||
client: Client,
|
||||
) -> None:
|
||||
"""Subscribe to heal Z-Wave network status updates."""
|
||||
controller = client.driver.controller
|
||||
|
||||
@callback
|
||||
def async_cleanup() -> None:
|
||||
"""Remove signal listeners."""
|
||||
for unsub in unsubs:
|
||||
unsub()
|
||||
|
||||
@callback
|
||||
def forward_event(key: str, event: dict) -> None:
|
||||
connection.send_message(
|
||||
websocket_api.event_message(
|
||||
msg[ID], {"event": event["event"], "heal_node_status": event[key]}
|
||||
)
|
||||
)
|
||||
|
||||
connection.subscriptions[msg["id"]] = async_cleanup
|
||||
unsubs = [
|
||||
controller.on("heal network progress", partial(forward_event, "progress")),
|
||||
controller.on("heal network done", partial(forward_event, "result")),
|
||||
]
|
||||
|
||||
connection.send_result(msg[ID])
|
||||
|
||||
|
||||
@websocket_api.require_admin
|
||||
@websocket_api.websocket_command(
|
||||
{
|
||||
vol.Required(TYPE): "zwave_js/stop_healing_network",
|
||||
vol.Required(ENTRY_ID): str,
|
||||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_get_entry
|
||||
async def websocket_stop_healing_network(
|
||||
hass: HomeAssistant,
|
||||
connection: ActiveConnection,
|
||||
msg: dict,
|
||||
entry: ConfigEntry,
|
||||
client: Client,
|
||||
) -> None:
|
||||
"""Stop healing the Z-Wave network."""
|
||||
controller = client.driver.controller
|
||||
result = await controller.async_stop_healing_network()
|
||||
connection.send_result(
|
||||
msg[ID],
|
||||
result,
|
||||
)
|
||||
|
||||
|
||||
@websocket_api.require_admin
|
||||
@websocket_api.websocket_command(
|
||||
{
|
||||
vol.Required(TYPE): "zwave_js/heal_node",
|
||||
vol.Required(ENTRY_ID): str,
|
||||
vol.Required(NODE_ID): int,
|
||||
}
|
||||
)
|
||||
@websocket_api.async_response
|
||||
@async_get_entry
|
||||
async def websocket_heal_node(
|
||||
hass: HomeAssistant,
|
||||
connection: ActiveConnection,
|
||||
msg: dict,
|
||||
entry: ConfigEntry,
|
||||
client: Client,
|
||||
) -> None:
|
||||
"""Heal a node on the Z-Wave network."""
|
||||
controller = client.driver.controller
|
||||
node_id = msg[NODE_ID]
|
||||
result = await controller.async_heal_node(node_id)
|
||||
connection.send_result(
|
||||
msg[ID],
|
||||
result,
|
||||
)
|
||||
|
||||
|
||||
@websocket_api.require_admin
|
||||
@websocket_api.websocket_command(
|
||||
{
|
||||
|
|
|
@ -496,6 +496,7 @@ async def test_replace_failed_node(
|
|||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
assert msg["result"]
|
||||
|
||||
event = Event(
|
||||
type="inclusion started",
|
||||
|
@ -674,6 +675,180 @@ async def test_remove_failed_node(
|
|||
assert msg["error"]["code"] == ERR_NOT_LOADED
|
||||
|
||||
|
||||
async def test_begin_healing_network(
|
||||
hass,
|
||||
integration,
|
||||
client,
|
||||
hass_ws_client,
|
||||
):
|
||||
"""Test the begin_healing_network websocket command."""
|
||||
entry = integration
|
||||
ws_client = await hass_ws_client(hass)
|
||||
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 3,
|
||||
TYPE: "zwave_js/begin_healing_network",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
assert msg["result"]
|
||||
|
||||
# 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/begin_healing_network",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == ERR_NOT_LOADED
|
||||
|
||||
|
||||
async def test_subscribe_heal_network_progress(
|
||||
hass, integration, client, hass_ws_client
|
||||
):
|
||||
"""Test the subscribe_heal_network_progress command."""
|
||||
entry = integration
|
||||
ws_client = await hass_ws_client(hass)
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 3,
|
||||
TYPE: "zwave_js/subscribe_heal_network_progress",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
# Fire heal network progress
|
||||
event = Event(
|
||||
"heal network progress",
|
||||
{
|
||||
"source": "controller",
|
||||
"event": "heal network progress",
|
||||
"progress": {67: "pending"},
|
||||
},
|
||||
)
|
||||
client.driver.controller.receive_event(event)
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["event"]["event"] == "heal network progress"
|
||||
assert msg["event"]["heal_node_status"] == {"67": "pending"}
|
||||
|
||||
# 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/subscribe_heal_network_progress",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == ERR_NOT_LOADED
|
||||
|
||||
|
||||
async def test_stop_healing_network(
|
||||
hass,
|
||||
integration,
|
||||
client,
|
||||
hass_ws_client,
|
||||
):
|
||||
"""Test the stop_healing_network websocket command."""
|
||||
entry = integration
|
||||
ws_client = await hass_ws_client(hass)
|
||||
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 3,
|
||||
TYPE: "zwave_js/stop_healing_network",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
assert msg["result"]
|
||||
|
||||
# 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/stop_healing_network",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == ERR_NOT_LOADED
|
||||
|
||||
|
||||
async def test_heal_node(
|
||||
hass,
|
||||
integration,
|
||||
client,
|
||||
hass_ws_client,
|
||||
):
|
||||
"""Test the heal_node websocket command."""
|
||||
entry = integration
|
||||
ws_client = await hass_ws_client(hass)
|
||||
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 3,
|
||||
TYPE: "zwave_js/heal_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 67,
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
assert msg["result"]
|
||||
|
||||
# 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/heal_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 67,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == ERR_NOT_LOADED
|
||||
|
||||
|
||||
async def test_refresh_node_info(
|
||||
hass, client, multisensor_6, integration, hass_ws_client
|
||||
):
|
||||
|
|
|
@ -91,7 +91,8 @@
|
|||
239
|
||||
],
|
||||
"sucNodeId": 1,
|
||||
"supportsTimers": false
|
||||
"supportsTimers": false,
|
||||
"isHealNetworkActive": false
|
||||
},
|
||||
"nodes": [
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue