Add SmartStart provisioning support to zwave_js WS API (#59037)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io> Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
694f6d0abe
commit
eeafa36abc
2 changed files with 1213 additions and 27 deletions
|
@ -9,7 +9,10 @@ from zwave_js_server.const import (
|
|||
CommandClass,
|
||||
InclusionStrategy,
|
||||
LogLevel,
|
||||
Protocols,
|
||||
QRCodeVersion,
|
||||
SecurityClass,
|
||||
ZwaveFeature,
|
||||
)
|
||||
from zwave_js_server.event import Event
|
||||
from zwave_js_server.exceptions import (
|
||||
|
@ -19,31 +22,49 @@ from zwave_js_server.exceptions import (
|
|||
NotFoundError,
|
||||
SetValueFailed,
|
||||
)
|
||||
from zwave_js_server.model.controller import (
|
||||
ProvisioningEntry,
|
||||
QRProvisioningInformation,
|
||||
)
|
||||
from zwave_js_server.model.node import Node
|
||||
from zwave_js_server.model.value import _get_value_id_from_dict, get_value_id
|
||||
|
||||
from homeassistant.components.websocket_api.const import ERR_NOT_FOUND
|
||||
from homeassistant.components.zwave_js.api import (
|
||||
APPLICATION_VERSION,
|
||||
CLIENT_SIDE_AUTH,
|
||||
COMMAND_CLASS_ID,
|
||||
CONFIG,
|
||||
DSK,
|
||||
ENABLED,
|
||||
ENTRY_ID,
|
||||
ERR_NOT_LOADED,
|
||||
FEATURE,
|
||||
FILENAME,
|
||||
FORCE_CONSOLE,
|
||||
GENERIC_DEVICE_CLASS,
|
||||
ID,
|
||||
INCLUSION_STRATEGY,
|
||||
INSTALLER_ICON_TYPE,
|
||||
LEVEL,
|
||||
LOG_TO_FILE,
|
||||
MANUFACTURER_ID,
|
||||
NODE_ID,
|
||||
OPTED_IN,
|
||||
PIN,
|
||||
PLANNED_PROVISIONING_ENTRY,
|
||||
PRODUCT_ID,
|
||||
PRODUCT_TYPE,
|
||||
PROPERTY,
|
||||
PROPERTY_KEY,
|
||||
QR_CODE_STRING,
|
||||
QR_PROVISIONING_INFORMATION,
|
||||
SECURITY_CLASSES,
|
||||
SPECIFIC_DEVICE_CLASS,
|
||||
TYPE,
|
||||
UNPROVISION,
|
||||
VALUE,
|
||||
VERSION,
|
||||
)
|
||||
from homeassistant.components.zwave_js.const import (
|
||||
CONF_DATA_COLLECTION_OPTED_IN,
|
||||
|
@ -421,9 +442,10 @@ async def test_add_node(
|
|||
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
# Test inclusion with no provisioning input
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 3,
|
||||
ID: 1,
|
||||
TYPE: "zwave_js/add_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
INCLUSION_STRATEGY: InclusionStrategy.DEFAULT.value,
|
||||
|
@ -542,6 +564,193 @@ async def test_add_node(
|
|||
msg = await ws_client.receive_json()
|
||||
assert msg["event"]["event"] == "interview failed"
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
# Test S2 planned provisioning entry
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 2,
|
||||
TYPE: "zwave_js/add_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
INCLUSION_STRATEGY: InclusionStrategy.SECURITY_S2.value,
|
||||
PLANNED_PROVISIONING_ENTRY: {
|
||||
DSK: "test",
|
||||
SECURITY_CLASSES: [0],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
assert client.async_send_command.call_args[0][0] == {
|
||||
"command": "controller.begin_inclusion",
|
||||
"options": {
|
||||
"strategy": InclusionStrategy.SECURITY_S2,
|
||||
"provisioning": ProvisioningEntry(
|
||||
"test", [SecurityClass.S2_UNAUTHENTICATED]
|
||||
).to_dict(),
|
||||
},
|
||||
}
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
# Test S2 QR provisioning information
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 3,
|
||||
TYPE: "zwave_js/add_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
INCLUSION_STRATEGY: InclusionStrategy.SECURITY_S2.value,
|
||||
QR_PROVISIONING_INFORMATION: {
|
||||
VERSION: 0,
|
||||
SECURITY_CLASSES: [0],
|
||||
DSK: "test",
|
||||
GENERIC_DEVICE_CLASS: 1,
|
||||
SPECIFIC_DEVICE_CLASS: 1,
|
||||
INSTALLER_ICON_TYPE: 1,
|
||||
MANUFACTURER_ID: 1,
|
||||
PRODUCT_TYPE: 1,
|
||||
PRODUCT_ID: 1,
|
||||
APPLICATION_VERSION: "test",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
assert client.async_send_command.call_args[0][0] == {
|
||||
"command": "controller.begin_inclusion",
|
||||
"options": {
|
||||
"strategy": InclusionStrategy.SECURITY_S2,
|
||||
"provisioning": QRProvisioningInformation(
|
||||
QRCodeVersion.S2,
|
||||
[SecurityClass.S2_UNAUTHENTICATED],
|
||||
"test",
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
"test",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).to_dict(),
|
||||
},
|
||||
}
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
# Test S2 QR code string
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
TYPE: "zwave_js/add_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
INCLUSION_STRATEGY: InclusionStrategy.SECURITY_S2.value,
|
||||
QR_CODE_STRING: "90testtesttesttesttesttesttesttesttesttesttesttesttest",
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
assert client.async_send_command.call_args[0][0] == {
|
||||
"command": "controller.begin_inclusion",
|
||||
"options": {
|
||||
"strategy": InclusionStrategy.SECURITY_S2,
|
||||
"provisioning": "90testtesttesttesttesttesttesttesttesttesttesttesttest",
|
||||
},
|
||||
}
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
# Test Smart Start QR provisioning information with S2 inclusion strategy fails
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 5,
|
||||
TYPE: "zwave_js/add_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
INCLUSION_STRATEGY: InclusionStrategy.SECURITY_S2.value,
|
||||
QR_PROVISIONING_INFORMATION: {
|
||||
VERSION: 1,
|
||||
SECURITY_CLASSES: [0],
|
||||
DSK: "test",
|
||||
GENERIC_DEVICE_CLASS: 1,
|
||||
SPECIFIC_DEVICE_CLASS: 1,
|
||||
INSTALLER_ICON_TYPE: 1,
|
||||
MANUFACTURER_ID: 1,
|
||||
PRODUCT_TYPE: 1,
|
||||
PRODUCT_ID: 1,
|
||||
APPLICATION_VERSION: "test",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert not msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 0
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
# Test QR provisioning information with S0 inclusion strategy fails
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 5,
|
||||
TYPE: "zwave_js/add_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
INCLUSION_STRATEGY: InclusionStrategy.SECURITY_S0,
|
||||
QR_PROVISIONING_INFORMATION: {
|
||||
VERSION: 1,
|
||||
SECURITY_CLASSES: [0],
|
||||
DSK: "test",
|
||||
GENERIC_DEVICE_CLASS: 1,
|
||||
SPECIFIC_DEVICE_CLASS: 1,
|
||||
INSTALLER_ICON_TYPE: 1,
|
||||
MANUFACTURER_ID: 1,
|
||||
PRODUCT_TYPE: 1,
|
||||
PRODUCT_ID: 1,
|
||||
APPLICATION_VERSION: "test",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert not msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 0
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {" success": True}
|
||||
|
||||
# Test ValueError is caught as failure
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 6,
|
||||
TYPE: "zwave_js/add_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
INCLUSION_STRATEGY: InclusionStrategy.DEFAULT.value,
|
||||
QR_CODE_STRING: "90testtesttesttesttesttesttesttesttesttesttesttesttest",
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert not msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 0
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.controller.Controller.async_begin_inclusion",
|
||||
|
@ -549,7 +758,7 @@ async def test_add_node(
|
|||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
ID: 7,
|
||||
TYPE: "zwave_js/add_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
|
@ -565,7 +774,7 @@ async def test_add_node(
|
|||
await hass.async_block_till_done()
|
||||
|
||||
await ws_client.send_json(
|
||||
{ID: 5, TYPE: "zwave_js/add_node", ENTRY_ID: entry.entry_id}
|
||||
{ID: 8, TYPE: "zwave_js/add_node", ENTRY_ID: entry.entry_id}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
|
@ -661,6 +870,465 @@ async def test_validate_dsk_and_enter_pin(hass, integration, client, hass_ws_cli
|
|||
assert msg["error"]["code"] == ERR_NOT_LOADED
|
||||
|
||||
|
||||
async def test_provision_smart_start_node(hass, integration, client, hass_ws_client):
|
||||
"""Test provision_smart_start_node websocket command."""
|
||||
entry = integration
|
||||
ws_client = await hass_ws_client(hass)
|
||||
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
# Test provisioning entry
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 2,
|
||||
TYPE: "zwave_js/provision_smart_start_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
PLANNED_PROVISIONING_ENTRY: {
|
||||
DSK: "test",
|
||||
SECURITY_CLASSES: [0],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
assert client.async_send_command.call_args[0][0] == {
|
||||
"command": "controller.provision_smart_start_node",
|
||||
"entry": ProvisioningEntry(
|
||||
"test", [SecurityClass.S2_UNAUTHENTICATED]
|
||||
).to_dict(),
|
||||
}
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
# Test QR provisioning information
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 3,
|
||||
TYPE: "zwave_js/provision_smart_start_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
QR_PROVISIONING_INFORMATION: {
|
||||
VERSION: 1,
|
||||
SECURITY_CLASSES: [0],
|
||||
DSK: "test",
|
||||
GENERIC_DEVICE_CLASS: 1,
|
||||
SPECIFIC_DEVICE_CLASS: 1,
|
||||
INSTALLER_ICON_TYPE: 1,
|
||||
MANUFACTURER_ID: 1,
|
||||
PRODUCT_TYPE: 1,
|
||||
PRODUCT_ID: 1,
|
||||
APPLICATION_VERSION: "test",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
assert client.async_send_command.call_args[0][0] == {
|
||||
"command": "controller.provision_smart_start_node",
|
||||
"entry": QRProvisioningInformation(
|
||||
QRCodeVersion.SMART_START,
|
||||
[SecurityClass.S2_UNAUTHENTICATED],
|
||||
"test",
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
"test",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).to_dict(),
|
||||
}
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
# Test QR code string
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
TYPE: "zwave_js/provision_smart_start_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
QR_CODE_STRING: "90testtesttesttesttesttesttesttesttesttesttesttesttest",
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
assert client.async_send_command.call_args[0][0] == {
|
||||
"command": "controller.provision_smart_start_node",
|
||||
"entry": "90testtesttesttesttesttesttesttesttesttesttesttesttest",
|
||||
}
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
# Test QR provisioning information with S2 version throws error
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 5,
|
||||
TYPE: "zwave_js/provision_smart_start_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
QR_PROVISIONING_INFORMATION: {
|
||||
VERSION: 0,
|
||||
SECURITY_CLASSES: [0],
|
||||
DSK: "test",
|
||||
GENERIC_DEVICE_CLASS: 1,
|
||||
SPECIFIC_DEVICE_CLASS: 1,
|
||||
INSTALLER_ICON_TYPE: 1,
|
||||
MANUFACTURER_ID: 1,
|
||||
PRODUCT_TYPE: 1,
|
||||
PRODUCT_ID: 1,
|
||||
APPLICATION_VERSION: "test",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert not msg["success"]
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
assert len(client.async_send_command.call_args_list) == 0
|
||||
|
||||
# Test no provisioning parameter provided causes failure
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 6,
|
||||
TYPE: "zwave_js/provision_smart_start_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.controller.Controller.async_provision_smart_start_node",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 7,
|
||||
TYPE: "zwave_js/provision_smart_start_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
QR_CODE_STRING: "90testtesttesttesttesttesttesttesttesttesttesttesttest",
|
||||
}
|
||||
)
|
||||
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: 8,
|
||||
TYPE: "zwave_js/provision_smart_start_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
QR_CODE_STRING: "90testtesttesttesttesttesttesttesttesttesttesttesttest",
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == ERR_NOT_LOADED
|
||||
|
||||
|
||||
async def test_unprovision_smart_start_node(hass, integration, client, hass_ws_client):
|
||||
"""Test unprovision_smart_start_node websocket command."""
|
||||
entry = integration
|
||||
ws_client = await hass_ws_client(hass)
|
||||
|
||||
client.async_send_command.return_value = {}
|
||||
|
||||
# Test node ID as input
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 1,
|
||||
TYPE: "zwave_js/unprovision_smart_start_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 1,
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
assert client.async_send_command.call_args[0][0] == {
|
||||
"command": "controller.unprovision_smart_start_node",
|
||||
"dskOrNodeId": 1,
|
||||
}
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {}
|
||||
|
||||
# Test DSK as input
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 2,
|
||||
TYPE: "zwave_js/unprovision_smart_start_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
DSK: "test",
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
assert client.async_send_command.call_args[0][0] == {
|
||||
"command": "controller.unprovision_smart_start_node",
|
||||
"dskOrNodeId": "test",
|
||||
}
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {}
|
||||
|
||||
# Test not including DSK or node ID as input fails
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 3,
|
||||
TYPE: "zwave_js/unprovision_smart_start_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert not msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 0
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.controller.Controller.async_unprovision_smart_start_node",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 6,
|
||||
TYPE: "zwave_js/unprovision_smart_start_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
DSK: "test",
|
||||
}
|
||||
)
|
||||
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,
|
||||
TYPE: "zwave_js/unprovision_smart_start_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
DSK: "test",
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == ERR_NOT_LOADED
|
||||
|
||||
|
||||
async def test_get_provisioning_entries(hass, integration, client, hass_ws_client):
|
||||
"""Test get_provisioning_entries websocket command."""
|
||||
entry = integration
|
||||
ws_client = await hass_ws_client(hass)
|
||||
|
||||
client.async_send_command.return_value = {
|
||||
"entries": [{"dsk": "test", "securityClasses": [0], "fake": "test"}]
|
||||
}
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 1,
|
||||
TYPE: "zwave_js/get_provisioning_entries",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
assert msg["result"] == [
|
||||
{
|
||||
"dsk": "test",
|
||||
"security_classes": [SecurityClass.S2_UNAUTHENTICATED],
|
||||
"additional_properties": {"fake": "test"},
|
||||
}
|
||||
]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
assert client.async_send_command.call_args[0][0] == {
|
||||
"command": "controller.get_provisioning_entries",
|
||||
}
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.controller.Controller.async_get_provisioning_entries",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 6,
|
||||
TYPE: "zwave_js/get_provisioning_entries",
|
||||
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: 7, TYPE: "zwave_js/get_provisioning_entries", 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_parse_qr_code_string(hass, integration, client, hass_ws_client):
|
||||
"""Test parse_qr_code_string websocket command."""
|
||||
entry = integration
|
||||
ws_client = await hass_ws_client(hass)
|
||||
|
||||
client.async_send_command.return_value = {
|
||||
"qrProvisioningInformation": {
|
||||
"version": 0,
|
||||
"securityClasses": [0],
|
||||
"dsk": "test",
|
||||
"genericDeviceClass": 1,
|
||||
"specificDeviceClass": 1,
|
||||
"installerIconType": 1,
|
||||
"manufacturerId": 1,
|
||||
"productType": 1,
|
||||
"productId": 1,
|
||||
"applicationVersion": "test",
|
||||
"maxInclusionRequestInterval": 1,
|
||||
"uuid": "test",
|
||||
"supportedProtocols": [0],
|
||||
}
|
||||
}
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 1,
|
||||
TYPE: "zwave_js/parse_qr_code_string",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
QR_CODE_STRING: "90testtesttesttesttesttesttesttesttesttesttesttesttest",
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
assert msg["result"] == {
|
||||
"version": 0,
|
||||
"security_classes": [SecurityClass.S2_UNAUTHENTICATED],
|
||||
"dsk": "test",
|
||||
"generic_device_class": 1,
|
||||
"specific_device_class": 1,
|
||||
"installer_icon_type": 1,
|
||||
"manufacturer_id": 1,
|
||||
"product_type": 1,
|
||||
"product_id": 1,
|
||||
"application_version": "test",
|
||||
"max_inclusion_request_interval": 1,
|
||||
"uuid": "test",
|
||||
"supported_protocols": [Protocols.ZWAVE],
|
||||
}
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
assert client.async_send_command.call_args[0][0] == {
|
||||
"command": "utils.parse_qr_code_string",
|
||||
"qr": "90testtesttesttesttesttesttesttesttesttesttesttesttest",
|
||||
}
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"homeassistant.components.zwave_js.api.async_parse_qr_code_string",
|
||||
side_effect=FailedZWaveCommand("failed_command", 1, "error message"),
|
||||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 6,
|
||||
TYPE: "zwave_js/parse_qr_code_string",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
QR_CODE_STRING: "90testtesttesttesttesttesttesttesttesttesttesttesttest",
|
||||
}
|
||||
)
|
||||
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,
|
||||
TYPE: "zwave_js/parse_qr_code_string",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
QR_CODE_STRING: "90testtesttesttesttesttesttesttesttesttesttesttesttest",
|
||||
}
|
||||
)
|
||||
msg = await ws_client.receive_json()
|
||||
|
||||
assert not msg["success"]
|
||||
assert msg["error"]["code"] == ERR_NOT_LOADED
|
||||
|
||||
|
||||
async def test_supports_feature(hass, integration, client, hass_ws_client):
|
||||
"""Test supports_feature websocket command."""
|
||||
entry = integration
|
||||
ws_client = await hass_ws_client(hass)
|
||||
|
||||
client.async_send_command.return_value = {"supported": True}
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 1,
|
||||
TYPE: "zwave_js/supports_feature",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
FEATURE: ZwaveFeature.SMART_START,
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
assert msg["result"] == {"supported": True}
|
||||
|
||||
|
||||
async def test_cancel_inclusion_exclusion(hass, integration, client, hass_ws_client):
|
||||
"""Test cancelling the inclusion and exclusion process."""
|
||||
entry = integration
|
||||
|
@ -754,12 +1422,17 @@ async def test_remove_node(
|
|||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
await ws_client.send_json(
|
||||
{ID: 3, TYPE: "zwave_js/remove_node", ENTRY_ID: entry.entry_id}
|
||||
{ID: 1, TYPE: "zwave_js/remove_node", ENTRY_ID: entry.entry_id}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
assert client.async_send_command.call_args[0][0] == {
|
||||
"command": "controller.begin_exclusion",
|
||||
}
|
||||
|
||||
event = Event(
|
||||
type="exclusion started",
|
||||
data={
|
||||
|
@ -792,6 +1465,28 @@ async def test_remove_node(
|
|||
)
|
||||
assert device is None
|
||||
|
||||
# Test unprovision parameter
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 2,
|
||||
TYPE: "zwave_js/remove_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
UNPROVISION: True,
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
assert client.async_send_command.call_args[0][0] == {
|
||||
"command": "controller.begin_exclusion",
|
||||
"unprovision": True,
|
||||
}
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.controller.Controller.async_begin_exclusion",
|
||||
|
@ -847,11 +1542,12 @@ async def test_replace_failed_node(
|
|||
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
# Test replace failed node with no provisioning information
|
||||
# Order of events we receive for a successful replacement is `inclusion started`,
|
||||
# `inclusion stopped`, `node removed`, `node added`, then interview stages.
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 3,
|
||||
ID: 1,
|
||||
TYPE: "zwave_js/replace_failed_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 67,
|
||||
|
@ -997,6 +1693,140 @@ async def test_replace_failed_node(
|
|||
msg = await ws_client.receive_json()
|
||||
assert msg["event"]["event"] == "interview failed"
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
# Test S2 planned provisioning entry
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 2,
|
||||
TYPE: "zwave_js/replace_failed_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 67,
|
||||
INCLUSION_STRATEGY: InclusionStrategy.SECURITY_S2.value,
|
||||
PLANNED_PROVISIONING_ENTRY: {
|
||||
DSK: "test",
|
||||
SECURITY_CLASSES: [0],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
assert client.async_send_command.call_args[0][0] == {
|
||||
"command": "controller.replace_failed_node",
|
||||
"nodeId": 67,
|
||||
"options": {
|
||||
"strategy": InclusionStrategy.SECURITY_S2,
|
||||
"provisioning": ProvisioningEntry(
|
||||
"test", [SecurityClass.S2_UNAUTHENTICATED]
|
||||
).to_dict(),
|
||||
},
|
||||
}
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
# Test S2 QR provisioning information
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 3,
|
||||
TYPE: "zwave_js/replace_failed_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 67,
|
||||
INCLUSION_STRATEGY: InclusionStrategy.SECURITY_S2.value,
|
||||
QR_PROVISIONING_INFORMATION: {
|
||||
VERSION: 0,
|
||||
SECURITY_CLASSES: [0],
|
||||
DSK: "test",
|
||||
GENERIC_DEVICE_CLASS: 1,
|
||||
SPECIFIC_DEVICE_CLASS: 1,
|
||||
INSTALLER_ICON_TYPE: 1,
|
||||
MANUFACTURER_ID: 1,
|
||||
PRODUCT_TYPE: 1,
|
||||
PRODUCT_ID: 1,
|
||||
APPLICATION_VERSION: "test",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
assert client.async_send_command.call_args[0][0] == {
|
||||
"command": "controller.replace_failed_node",
|
||||
"nodeId": 67,
|
||||
"options": {
|
||||
"strategy": InclusionStrategy.SECURITY_S2,
|
||||
"provisioning": QRProvisioningInformation(
|
||||
QRCodeVersion.S2,
|
||||
[SecurityClass.S2_UNAUTHENTICATED],
|
||||
"test",
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
"test",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).to_dict(),
|
||||
},
|
||||
}
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
# Test S2 QR code string
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
TYPE: "zwave_js/replace_failed_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 67,
|
||||
INCLUSION_STRATEGY: InclusionStrategy.SECURITY_S2.value,
|
||||
QR_CODE_STRING: "90testtesttesttesttesttesttesttesttesttesttesttesttest",
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 1
|
||||
assert client.async_send_command.call_args[0][0] == {
|
||||
"command": "controller.replace_failed_node",
|
||||
"nodeId": 67,
|
||||
"options": {
|
||||
"strategy": InclusionStrategy.SECURITY_S2,
|
||||
"provisioning": "90testtesttesttesttesttesttesttesttesttesttesttesttest",
|
||||
},
|
||||
}
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
# Test ValueError is caught as failure
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 6,
|
||||
TYPE: "zwave_js/replace_failed_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 67,
|
||||
INCLUSION_STRATEGY: InclusionStrategy.DEFAULT.value,
|
||||
QR_CODE_STRING: "90testtesttesttesttesttesttesttesttesttesttesttesttest",
|
||||
}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert not msg["success"]
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 0
|
||||
|
||||
# Test FailedZWaveCommand is caught
|
||||
with patch(
|
||||
"zwave_js_server.model.controller.Controller.async_replace_failed_node",
|
||||
|
@ -1004,7 +1834,7 @@ async def test_replace_failed_node(
|
|||
):
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 4,
|
||||
ID: 7,
|
||||
TYPE: "zwave_js/replace_failed_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 67,
|
||||
|
@ -1022,7 +1852,7 @@ async def test_replace_failed_node(
|
|||
|
||||
await ws_client.send_json(
|
||||
{
|
||||
ID: 5,
|
||||
ID: 8,
|
||||
TYPE: "zwave_js/replace_failed_node",
|
||||
ENTRY_ID: entry.entry_id,
|
||||
NODE_ID: 67,
|
||||
|
@ -2324,7 +3154,7 @@ async def test_update_log_config(hass, client, integration, hass_ws_client):
|
|||
)
|
||||
msg = await ws_client.receive_json()
|
||||
assert not msg["success"]
|
||||
assert "error" in msg and "value must be one of" in msg["error"]["message"]
|
||||
assert "error" in msg and msg["error"]["code"] == "invalid_format"
|
||||
|
||||
# Test error without service data
|
||||
await ws_client.send_json(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue