Add proper S2 support for adding zwave_js nodes (#56516)

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Raman Gupta 2021-09-28 18:37:45 -04:00 committed by GitHub
parent db30c27455
commit e76ddb4b27
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 294 additions and 95 deletions

View file

@ -10,7 +10,12 @@ from aiohttp import hdrs, web, web_exceptions, web_request
import voluptuous as vol
from zwave_js_server import dump
from zwave_js_server.client import Client
from zwave_js_server.const import CommandClass, InclusionStrategy, LogLevel
from zwave_js_server.const import (
CommandClass,
InclusionStrategy,
LogLevel,
SecurityClass,
)
from zwave_js_server.exceptions import (
BaseZwaveJSServerError,
FailedCommand,
@ -19,7 +24,7 @@ from zwave_js_server.exceptions import (
SetValueFailed,
)
from zwave_js_server.firmware import begin_firmware_update
from zwave_js_server.model.controller import ControllerStatistics
from zwave_js_server.model.controller import ControllerStatistics, InclusionGrant
from zwave_js_server.model.firmware import (
FirmwareUpdateFinished,
FirmwareUpdateProgress,
@ -67,7 +72,8 @@ TYPE = "type"
PROPERTY = "property"
PROPERTY_KEY = "property_key"
VALUE = "value"
SECURE = "secure"
INCLUSION_STRATEGY = "inclusion_strategy"
PIN = "pin"
# constants for log config commands
CONFIG = "config"
@ -85,6 +91,10 @@ STATUS = "status"
ENABLED = "enabled"
OPTED_IN = "opted_in"
# constants for granting security classes
SECURITY_CLASSES = "security_classes"
CLIENT_SIDE_AUTH = "client_side_auth"
def async_get_entry(orig_func: Callable) -> Callable:
"""Decorate async function to get entry."""
@ -171,6 +181,8 @@ def async_register_api(hass: HomeAssistant) -> None:
websocket_api.async_register_command(hass, websocket_node_metadata)
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_grant_security_classes)
websocket_api.async_register_command(hass, websocket_validate_dsk_and_enter_pin)
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)
@ -371,7 +383,9 @@ async def websocket_ping_node(
{
vol.Required(TYPE): "zwave_js/add_node",
vol.Required(ENTRY_ID): str,
vol.Optional(SECURE, default=False): bool,
vol.Optional(INCLUSION_STRATEGY, default=InclusionStrategy.DEFAULT): vol.In(
[strategy.value for strategy in InclusionStrategy]
),
}
)
@websocket_api.async_response
@ -386,11 +400,7 @@ async def websocket_add_node(
) -> None:
"""Add a node to the Z-Wave network."""
controller = client.driver.controller
if msg[SECURE]:
inclusion_strategy = InclusionStrategy.SECURITY_S0
else:
inclusion_strategy = InclusionStrategy.INSECURE
inclusion_strategy = InclusionStrategy(msg[INCLUSION_STRATEGY])
@callback
def async_cleanup() -> None:
@ -404,6 +414,26 @@ async def websocket_add_node(
websocket_api.event_message(msg[ID], {"event": event["event"]})
)
@callback
def forward_dsk(event: dict) -> None:
connection.send_message(
websocket_api.event_message(
msg[ID], {"event": event["event"], "dsk": event["dsk"]}
)
)
@callback
def forward_requested_grant(event: dict) -> None:
connection.send_message(
websocket_api.event_message(
msg[ID],
{
"event": event["event"],
"requested_grant": event["requested_grant"].to_dict(),
},
)
)
@callback
def forward_stage(event: dict) -> None:
connection.send_message(
@ -426,6 +456,7 @@ async def websocket_add_node(
"node_id": node.node_id,
"status": node.status,
"ready": node.ready,
"low_security": event["result"].get("lowSecurity", False),
}
connection.send_message(
websocket_api.event_message(
@ -452,6 +483,8 @@ async def websocket_add_node(
controller.on("inclusion started", forward_event),
controller.on("inclusion failed", forward_event),
controller.on("inclusion stopped", forward_event),
controller.on("validate dsk and enter pin", forward_dsk),
controller.on("grant security classes", forward_requested_grant),
controller.on("node added", node_added),
async_dispatcher_connect(
hass, EVENT_DEVICE_ADDED_TO_REGISTRY, device_registered
@ -465,6 +498,59 @@ async def websocket_add_node(
)
@websocket_api.require_admin
@websocket_api.websocket_command(
{
vol.Required(TYPE): "zwave_js/grant_security_classes",
vol.Required(ENTRY_ID): str,
vol.Required(SECURITY_CLASSES): [
vol.In([sec_cls.value for sec_cls in SecurityClass])
],
vol.Optional(CLIENT_SIDE_AUTH, default=False): bool,
}
)
@websocket_api.async_response
@async_handle_failed_command
@async_get_entry
async def websocket_grant_security_classes(
hass: HomeAssistant,
connection: ActiveConnection,
msg: dict,
entry: ConfigEntry,
client: Client,
) -> None:
"""Add a node to the Z-Wave network."""
inclusion_grant = InclusionGrant(
[SecurityClass(sec_cls) for sec_cls in msg[SECURITY_CLASSES]],
msg[CLIENT_SIDE_AUTH],
)
await client.driver.controller.async_grant_security_classes(inclusion_grant)
connection.send_result(msg[ID])
@websocket_api.require_admin
@websocket_api.websocket_command(
{
vol.Required(TYPE): "zwave_js/validate_dsk_and_enter_pin",
vol.Required(ENTRY_ID): str,
vol.Required(PIN): str,
}
)
@websocket_api.async_response
@async_handle_failed_command
@async_get_entry
async def websocket_validate_dsk_and_enter_pin(
hass: HomeAssistant,
connection: ActiveConnection,
msg: dict,
entry: ConfigEntry,
client: Client,
) -> None:
"""Add a node to the Z-Wave network."""
await client.driver.controller.async_validate_dsk_and_enter_pin(msg[PIN])
connection.send_result(msg[ID])
@websocket_api.require_admin
@websocket_api.websocket_command(
{
@ -583,7 +669,9 @@ async def websocket_remove_node(
vol.Required(TYPE): "zwave_js/replace_failed_node",
vol.Required(ENTRY_ID): str,
vol.Required(NODE_ID): int,
vol.Optional(SECURE, default=False): bool,
vol.Optional(INCLUSION_STRATEGY, default=InclusionStrategy.DEFAULT): vol.In(
[strategy.value for strategy in InclusionStrategy]
),
}
)
@websocket_api.async_response
@ -599,11 +687,7 @@ async def websocket_replace_failed_node(
"""Replace a failed node with a new node."""
controller = client.driver.controller
node_id = msg[NODE_ID]
if msg[SECURE]:
inclusion_strategy = InclusionStrategy.SECURITY_S0
else:
inclusion_strategy = InclusionStrategy.INSECURE
inclusion_strategy = InclusionStrategy(msg[INCLUSION_STRATEGY])
@callback
def async_cleanup() -> None:
@ -617,6 +701,26 @@ async def websocket_replace_failed_node(
websocket_api.event_message(msg[ID], {"event": event["event"]})
)
@callback
def forward_dsk(event: dict) -> None:
connection.send_message(
websocket_api.event_message(
msg[ID], {"event": event["event"], "dsk": event["dsk"]}
)
)
@callback
def forward_requested_grant(event: dict) -> None:
connection.send_message(
websocket_api.event_message(
msg[ID],
{
"event": event["event"],
"requested_grant": event["requested_grant"].to_dict(),
},
)
)
@callback
def forward_stage(event: dict) -> None:
connection.send_message(
@ -678,6 +782,8 @@ async def websocket_replace_failed_node(
controller.on("inclusion started", forward_event),
controller.on("inclusion failed", forward_event),
controller.on("inclusion stopped", forward_event),
controller.on("validate dsk and enter pin", forward_dsk),
controller.on("grant security classes", forward_requested_grant),
controller.on("node removed", node_removed),
controller.on("node added", node_added),
async_dispatcher_connect(