Clean zwave_js api driver access (#72419)

This commit is contained in:
Martin Hjelmare 2022-05-24 23:16:58 +02:00 committed by GitHub
parent cd769a55c2
commit c4ca106379
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 84 additions and 42 deletions

View File

@ -566,7 +566,7 @@ async def setup_driver( # noqa: C901
# If opt in preference hasn't been specified yet, we do nothing, otherwise # If opt in preference hasn't been specified yet, we do nothing, otherwise
# we apply the preference # we apply the preference
if opted_in := entry.data.get(CONF_DATA_COLLECTION_OPTED_IN): if opted_in := entry.data.get(CONF_DATA_COLLECTION_OPTED_IN):
await async_enable_statistics(client) await async_enable_statistics(driver)
elif opted_in is False: elif opted_in is False:
await driver.async_disable_statistics() await driver.async_disable_statistics()

View File

@ -32,6 +32,7 @@ from zwave_js_server.model.controller import (
ProvisioningEntry, ProvisioningEntry,
QRProvisioningInformation, QRProvisioningInformation,
) )
from zwave_js_server.model.driver import Driver
from zwave_js_server.model.firmware import ( from zwave_js_server.model.firmware import (
FirmwareUpdateFinished, FirmwareUpdateFinished,
FirmwareUpdateProgress, FirmwareUpdateProgress,
@ -243,8 +244,17 @@ def async_get_entry(orig_func: Callable) -> Callable:
) )
return return
client = hass.data[DOMAIN][entry_id][DATA_CLIENT] client: Client = hass.data[DOMAIN][entry_id][DATA_CLIENT]
await orig_func(hass, connection, msg, entry, client)
if client.driver is None:
connection.send_error(
msg[ID],
ERR_NOT_LOADED,
f"Config entry {entry_id} not loaded, driver not ready",
)
return
await orig_func(hass, connection, msg, entry, client, client.driver)
return async_get_entry_func return async_get_entry_func
@ -373,16 +383,20 @@ async def websocket_network_status(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Get the status of the Z-Wave JS network.""" """Get the status of the Z-Wave JS network."""
controller = client.driver.controller controller = driver.controller
client_version_info = client.version
assert client_version_info # When client is connected version info is set.
await controller.async_get_state() await controller.async_get_state()
data = { data = {
"client": { "client": {
"ws_server_url": client.ws_server_url, "ws_server_url": client.ws_server_url,
"state": "connected" if client.connected else "disconnected", "state": "connected" if client.connected else "disconnected",
"driver_version": client.version.driver_version, "driver_version": client_version_info.driver_version,
"server_version": client.version.server_version, "server_version": client_version_info.server_version,
}, },
"controller": { "controller": {
"home_id": controller.home_id, "home_id": controller.home_id,
@ -404,9 +418,7 @@ async def websocket_network_status(
"supports_timers": controller.supports_timers, "supports_timers": controller.supports_timers,
"is_heal_network_active": controller.is_heal_network_active, "is_heal_network_active": controller.is_heal_network_active,
"inclusion_state": controller.inclusion_state, "inclusion_state": controller.inclusion_state,
"nodes": [ "nodes": [node_status(node) for node in driver.controller.nodes.values()],
node_status(node) for node in client.driver.controller.nodes.values()
],
}, },
} }
connection.send_result( connection.send_result(
@ -533,9 +545,10 @@ async def websocket_add_node(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Add a node to the Z-Wave network.""" """Add a node to the Z-Wave network."""
controller = client.driver.controller controller = driver.controller
inclusion_strategy = InclusionStrategy(msg[INCLUSION_STRATEGY]) inclusion_strategy = InclusionStrategy(msg[INCLUSION_STRATEGY])
force_security = msg.get(FORCE_SECURITY) force_security = msg.get(FORCE_SECURITY)
provisioning = ( provisioning = (
@ -672,13 +685,14 @@ async def websocket_grant_security_classes(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Choose SecurityClass grants as part of S2 inclusion process.""" """Choose SecurityClass grants as part of S2 inclusion process."""
inclusion_grant = InclusionGrant( inclusion_grant = InclusionGrant(
[SecurityClass(sec_cls) for sec_cls in msg[SECURITY_CLASSES]], [SecurityClass(sec_cls) for sec_cls in msg[SECURITY_CLASSES]],
msg[CLIENT_SIDE_AUTH], msg[CLIENT_SIDE_AUTH],
) )
await client.driver.controller.async_grant_security_classes(inclusion_grant) await driver.controller.async_grant_security_classes(inclusion_grant)
connection.send_result(msg[ID]) connection.send_result(msg[ID])
@ -699,9 +713,10 @@ async def websocket_validate_dsk_and_enter_pin(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Validate DSK and enter PIN as part of S2 inclusion process.""" """Validate DSK and enter PIN as part of S2 inclusion process."""
await client.driver.controller.async_validate_dsk_and_enter_pin(msg[PIN]) await driver.controller.async_validate_dsk_and_enter_pin(msg[PIN])
connection.send_result(msg[ID]) connection.send_result(msg[ID])
@ -728,6 +743,7 @@ async def websocket_provision_smart_start_node(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Pre-provision a smart start node.""" """Pre-provision a smart start node."""
try: try:
@ -758,7 +774,7 @@ async def websocket_provision_smart_start_node(
"QR code version S2 is not supported for this command", "QR code version S2 is not supported for this command",
) )
return return
await client.driver.controller.async_provision_smart_start_node(provisioning_info) await driver.controller.async_provision_smart_start_node(provisioning_info)
connection.send_result(msg[ID]) connection.send_result(msg[ID])
@ -780,6 +796,7 @@ async def websocket_unprovision_smart_start_node(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Unprovision a smart start node.""" """Unprovision a smart start node."""
try: try:
@ -792,7 +809,7 @@ async def websocket_unprovision_smart_start_node(
) )
return return
dsk_or_node_id = msg.get(DSK) or msg[NODE_ID] dsk_or_node_id = msg.get(DSK) or msg[NODE_ID]
await client.driver.controller.async_unprovision_smart_start_node(dsk_or_node_id) await driver.controller.async_unprovision_smart_start_node(dsk_or_node_id)
connection.send_result(msg[ID]) connection.send_result(msg[ID])
@ -812,11 +829,10 @@ async def websocket_get_provisioning_entries(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Get provisioning entries (entries that have been pre-provisioned).""" """Get provisioning entries (entries that have been pre-provisioned)."""
provisioning_entries = ( provisioning_entries = await driver.controller.async_get_provisioning_entries()
await client.driver.controller.async_get_provisioning_entries()
)
connection.send_result( connection.send_result(
msg[ID], [dataclasses.asdict(entry) for entry in provisioning_entries] msg[ID], [dataclasses.asdict(entry) for entry in provisioning_entries]
) )
@ -839,6 +855,7 @@ async def websocket_parse_qr_code_string(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Parse a QR Code String and return QRProvisioningInformation dict.""" """Parse a QR Code String and return QRProvisioningInformation dict."""
qr_provisioning_information = await async_parse_qr_code_string( qr_provisioning_information = await async_parse_qr_code_string(
@ -864,9 +881,10 @@ async def websocket_supports_feature(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Check if controller supports a particular feature.""" """Check if controller supports a particular feature."""
supported = await client.driver.controller.async_supports_feature(msg[FEATURE]) supported = await driver.controller.async_supports_feature(msg[FEATURE])
connection.send_result( connection.send_result(
msg[ID], msg[ID],
{"supported": supported}, {"supported": supported},
@ -889,9 +907,10 @@ async def websocket_stop_inclusion(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Cancel adding a node to the Z-Wave network.""" """Cancel adding a node to the Z-Wave network."""
controller = client.driver.controller controller = driver.controller
result = await controller.async_stop_inclusion() result = await controller.async_stop_inclusion()
connection.send_result( connection.send_result(
msg[ID], msg[ID],
@ -915,9 +934,10 @@ async def websocket_stop_exclusion(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Cancel removing a node from the Z-Wave network.""" """Cancel removing a node from the Z-Wave network."""
controller = client.driver.controller controller = driver.controller
result = await controller.async_stop_exclusion() result = await controller.async_stop_exclusion()
connection.send_result( connection.send_result(
msg[ID], msg[ID],
@ -942,9 +962,10 @@ async def websocket_remove_node(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Remove a node from the Z-Wave network.""" """Remove a node from the Z-Wave network."""
controller = client.driver.controller controller = driver.controller
@callback @callback
def async_cleanup() -> None: def async_cleanup() -> None:
@ -1021,9 +1042,10 @@ async def websocket_replace_failed_node(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Replace a failed node with a new node.""" """Replace a failed node with a new node."""
controller = client.driver.controller controller = driver.controller
node_id = msg[NODE_ID] node_id = msg[NODE_ID]
inclusion_strategy = InclusionStrategy(msg[INCLUSION_STRATEGY]) inclusion_strategy = InclusionStrategy(msg[INCLUSION_STRATEGY])
force_security = msg.get(FORCE_SECURITY) force_security = msg.get(FORCE_SECURITY)
@ -1173,7 +1195,9 @@ async def websocket_remove_failed_node(
node: Node, node: Node,
) -> None: ) -> None:
"""Remove a failed node from the Z-Wave network.""" """Remove a failed node from the Z-Wave network."""
controller = node.client.driver.controller driver = node.client.driver
assert driver is not None # The node comes from the driver instance.
controller = driver.controller
@callback @callback
def async_cleanup() -> None: def async_cleanup() -> None:
@ -1217,9 +1241,10 @@ async def websocket_begin_healing_network(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Begin healing the Z-Wave network.""" """Begin healing the Z-Wave network."""
controller = client.driver.controller controller = driver.controller
result = await controller.async_begin_healing_network() result = await controller.async_begin_healing_network()
connection.send_result( connection.send_result(
@ -1243,9 +1268,10 @@ async def websocket_subscribe_heal_network_progress(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Subscribe to heal Z-Wave network status updates.""" """Subscribe to heal Z-Wave network status updates."""
controller = client.driver.controller controller = driver.controller
@callback @callback
def async_cleanup() -> None: def async_cleanup() -> None:
@ -1286,9 +1312,10 @@ async def websocket_stop_healing_network(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Stop healing the Z-Wave network.""" """Stop healing the Z-Wave network."""
controller = client.driver.controller controller = driver.controller
result = await controller.async_stop_healing_network() result = await controller.async_stop_healing_network()
connection.send_result( connection.send_result(
msg[ID], msg[ID],
@ -1313,7 +1340,10 @@ async def websocket_heal_node(
node: Node, node: Node,
) -> None: ) -> None:
"""Heal a node on the Z-Wave network.""" """Heal a node on the Z-Wave network."""
controller = node.client.driver.controller driver = node.client.driver
assert driver is not None # The node comes from the driver instance.
controller = driver.controller
result = await controller.async_heal_node(node.node_id) result = await controller.async_heal_node(node.node_id)
connection.send_result( connection.send_result(
msg[ID], msg[ID],
@ -1540,9 +1570,9 @@ async def websocket_subscribe_log_updates(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Subscribe to log message events from the server.""" """Subscribe to log message events from the server."""
driver = client.driver
@callback @callback
def async_cleanup() -> None: def async_cleanup() -> None:
@ -1627,9 +1657,10 @@ async def websocket_update_log_config(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Update the driver log config.""" """Update the driver log config."""
await client.driver.async_update_log_config(LogConfig(**msg[CONFIG])) await driver.async_update_log_config(LogConfig(**msg[CONFIG]))
connection.send_result( connection.send_result(
msg[ID], msg[ID],
) )
@ -1650,11 +1681,12 @@ async def websocket_get_log_config(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Get log configuration for the Z-Wave JS driver.""" """Get log configuration for the Z-Wave JS driver."""
connection.send_result( connection.send_result(
msg[ID], msg[ID],
dataclasses.asdict(client.driver.log_config), dataclasses.asdict(driver.log_config),
) )
@ -1675,15 +1707,16 @@ async def websocket_update_data_collection_preference(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Update preference for data collection and enable/disable collection.""" """Update preference for data collection and enable/disable collection."""
opted_in = msg[OPTED_IN] opted_in = msg[OPTED_IN]
update_data_collection_preference(hass, entry, opted_in) update_data_collection_preference(hass, entry, opted_in)
if opted_in: if opted_in:
await async_enable_statistics(client) await async_enable_statistics(driver)
else: else:
await client.driver.async_disable_statistics() await driver.async_disable_statistics()
connection.send_result( connection.send_result(
msg[ID], msg[ID],
@ -1706,11 +1739,12 @@ async def websocket_data_collection_status(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Return data collection preference and status.""" """Return data collection preference and status."""
result = { result = {
OPTED_IN: entry.data.get(CONF_DATA_COLLECTION_OPTED_IN), OPTED_IN: entry.data.get(CONF_DATA_COLLECTION_OPTED_IN),
ENABLED: await client.driver.async_is_statistics_enabled(), ENABLED: await driver.async_is_statistics_enabled(),
} }
connection.send_result(msg[ID], result) connection.send_result(msg[ID], result)
@ -1890,9 +1924,10 @@ async def websocket_check_for_config_updates(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Check for config updates.""" """Check for config updates."""
config_update = await client.driver.async_check_for_config_updates() config_update = await driver.async_check_for_config_updates()
connection.send_result( connection.send_result(
msg[ID], msg[ID],
{ {
@ -1918,9 +1953,10 @@ async def websocket_install_config_update(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Check for config updates.""" """Check for config updates."""
success = await client.driver.async_install_config_update() success = await driver.async_install_config_update()
connection.send_result(msg[ID], success) connection.send_result(msg[ID], success)
@ -1956,6 +1992,7 @@ async def websocket_subscribe_controller_statistics(
msg: dict, msg: dict,
entry: ConfigEntry, entry: ConfigEntry,
client: Client, client: Client,
driver: Driver,
) -> None: ) -> None:
"""Subsribe to the statistics updates for a controller.""" """Subsribe to the statistics updates for a controller."""
@ -1979,7 +2016,7 @@ async def websocket_subscribe_controller_statistics(
) )
) )
controller = client.driver.controller controller = driver.controller
msg[DATA_UNSUBSCRIBE] = unsubs = [ msg[DATA_UNSUBSCRIBE] = unsubs = [
controller.on("statistics updated", forward_stats) controller.on("statistics updated", forward_stats)

View File

@ -9,6 +9,7 @@ from typing import Any, cast
import voluptuous as vol import voluptuous as vol
from zwave_js_server.client import Client as ZwaveClient from zwave_js_server.client import Client as ZwaveClient
from zwave_js_server.const import ConfigurationValueType from zwave_js_server.const import ConfigurationValueType
from zwave_js_server.model.driver import Driver
from zwave_js_server.model.node import Node as ZwaveNode from zwave_js_server.model.node import Node as ZwaveNode
from zwave_js_server.model.value import ( from zwave_js_server.model.value import (
ConfigurationValue, ConfigurationValue,
@ -92,10 +93,10 @@ def get_value_of_zwave_value(value: ZwaveValue | None) -> Any | None:
return value.value if value else None return value.value if value else None
async def async_enable_statistics(client: ZwaveClient) -> None: async def async_enable_statistics(driver: Driver) -> None:
"""Enable statistics on the driver.""" """Enable statistics on the driver."""
await client.driver.async_enable_statistics("Home Assistant", HA_VERSION) await driver.async_enable_statistics("Home Assistant", HA_VERSION)
await client.driver.async_enable_error_reporting() await driver.async_enable_error_reporting()
@callback @callback
@ -194,7 +195,11 @@ def async_get_node_from_device_id(
f"Device {device_id} is not from an existing zwave_js config entry" f"Device {device_id} is not from an existing zwave_js config entry"
) )
client = hass.data[DOMAIN][entry.entry_id][DATA_CLIENT] client: ZwaveClient = hass.data[DOMAIN][entry.entry_id][DATA_CLIENT]
driver = client.driver
if driver is None:
raise ValueError("Driver is not ready.")
# Get node ID from device identifier, perform some validation, and then get the # Get node ID from device identifier, perform some validation, and then get the
# node # node
@ -202,10 +207,10 @@ def async_get_node_from_device_id(
node_id = identifiers[1] if identifiers else None node_id = identifiers[1] if identifiers else None
if node_id is None or node_id not in client.driver.controller.nodes: if node_id is None or node_id not in driver.controller.nodes:
raise ValueError(f"Node for device {device_id} can't be found") raise ValueError(f"Node for device {device_id} can't be found")
return client.driver.controller.nodes[node_id] return driver.controller.nodes[node_id]
@callback @callback