diff --git a/homeassistant/components/zwave_js/api.py b/homeassistant/components/zwave_js/api.py index 6c186e2f840..6a4aaf0264e 100644 --- a/homeassistant/components/zwave_js/api.py +++ b/homeassistant/components/zwave_js/api.py @@ -14,6 +14,7 @@ from zwave_js_server.const import ( InclusionStrategy, LogLevel, Protocols, + ProvisioningEntryStatus, QRCodeVersion, SecurityClass, ZwaveFeature, @@ -148,6 +149,8 @@ MAX_INCLUSION_REQUEST_INTERVAL = "max_inclusion_request_interval" UUID = "uuid" SUPPORTED_PROTOCOLS = "supported_protocols" ADDITIONAL_PROPERTIES = "additional_properties" +STATUS = "status" +REQUESTED_SECURITY_CLASSES = "requested_security_classes" FEATURE = "feature" UNPROVISION = "unprovision" @@ -160,19 +163,22 @@ def convert_planned_provisioning_entry(info: dict) -> ProvisioningEntry: """Handle provisioning entry dict to ProvisioningEntry.""" return ProvisioningEntry( dsk=info[DSK], - security_classes=[SecurityClass(sec_cls) for sec_cls in info[SECURITY_CLASSES]], + security_classes=info[SECURITY_CLASSES], + status=info[STATUS], + requested_security_classes=info.get(REQUESTED_SECURITY_CLASSES), additional_properties={ - k: v for k, v in info.items() if k not in (DSK, SECURITY_CLASSES) + k: v + for k, v in info.items() + if k not in (DSK, SECURITY_CLASSES, STATUS, REQUESTED_SECURITY_CLASSES) }, ) def convert_qr_provisioning_information(info: dict) -> QRProvisioningInformation: """Convert QR provisioning information dict to QRProvisioningInformation.""" - protocols = [Protocols(proto) for proto in info.get(SUPPORTED_PROTOCOLS, [])] return QRProvisioningInformation( - version=QRCodeVersion(info[VERSION]), - security_classes=[SecurityClass(sec_cls) for sec_cls in info[SECURITY_CLASSES]], + version=info[VERSION], + security_classes=info[SECURITY_CLASSES], dsk=info[DSK], generic_device_class=info[GENERIC_DEVICE_CLASS], specific_device_class=info[SPECIFIC_DEVICE_CLASS], @@ -183,7 +189,9 @@ def convert_qr_provisioning_information(info: dict) -> QRProvisioningInformation application_version=info[APPLICATION_VERSION], max_inclusion_request_interval=info.get(MAX_INCLUSION_REQUEST_INTERVAL), uuid=info.get(UUID), - supported_protocols=protocols if protocols else None, + supported_protocols=info.get(SUPPORTED_PROTOCOLS), + status=info[STATUS], + requested_security_classes=info.get(REQUESTED_SECURITY_CLASSES), additional_properties=info.get(ADDITIONAL_PROPERTIES, {}), ) @@ -197,6 +205,12 @@ PLANNED_PROVISIONING_ENTRY_SCHEMA = vol.All( cv.ensure_list, [vol.Coerce(SecurityClass)], ), + vol.Optional(STATUS, default=ProvisioningEntryStatus.ACTIVE): vol.Coerce( + ProvisioningEntryStatus + ), + vol.Optional(REQUESTED_SECURITY_CLASSES): vol.All( + cv.ensure_list, [vol.Coerce(SecurityClass)] + ), }, # Provisioning entries can have extra keys for SmartStart extra=vol.ALLOW_EXTRA, @@ -226,6 +240,12 @@ QR_PROVISIONING_INFORMATION_SCHEMA = vol.All( cv.ensure_list, [vol.Coerce(Protocols)], ), + vol.Optional(STATUS, default=ProvisioningEntryStatus.ACTIVE): vol.Coerce( + ProvisioningEntryStatus + ), + vol.Optional(REQUESTED_SECURITY_CLASSES): vol.All( + cv.ensure_list, [vol.Coerce(SecurityClass)] + ), vol.Optional(ADDITIONAL_PROPERTIES): dict, } ), diff --git a/tests/components/zwave_js/test_api.py b/tests/components/zwave_js/test_api.py index da8cfad9624..6c3dd796a7d 100644 --- a/tests/components/zwave_js/test_api.py +++ b/tests/components/zwave_js/test_api.py @@ -10,6 +10,7 @@ from zwave_js_server.const import ( InclusionStrategy, LogLevel, Protocols, + ProvisioningEntryStatus, QRCodeVersion, SecurityClass, ZwaveFeature, @@ -63,8 +64,10 @@ from homeassistant.components.zwave_js.api import ( PROPERTY_KEY, QR_CODE_STRING, QR_PROVISIONING_INFORMATION, + REQUESTED_SECURITY_CLASSES, SECURITY_CLASSES, SPECIFIC_DEVICE_CLASS, + STATUS, TYPE, UNPROVISION, VALUE, @@ -619,13 +622,68 @@ async def test_add_node( client.async_send_command.reset_mock() client.async_send_command.return_value = {"success": True} - # Test S2 QR code string + # Test S2 QR provisioning information await ws_client.send_json( { ID: 4, 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", + STATUS: 1, + REQUESTED_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": QRProvisioningInformation( + version=QRCodeVersion.S2, + 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=None, + uuid=None, + supported_protocols=None, + status=ProvisioningEntryStatus.INACTIVE, + requested_security_classes=[SecurityClass.S2_UNAUTHENTICATED], + ).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: 5, + TYPE: "zwave_js/add_node", + ENTRY_ID: entry.entry_id, + INCLUSION_STRATEGY: InclusionStrategy.SECURITY_S2.value, QR_CODE_STRING: "90testtesttesttesttesttesttesttesttesttesttesttesttest", } ) @@ -648,7 +706,7 @@ async def test_add_node( # Test Smart Start QR provisioning information with S2 inclusion strategy fails await ws_client.send_json( { - ID: 5, + ID: 6, TYPE: "zwave_js/add_node", ENTRY_ID: entry.entry_id, INCLUSION_STRATEGY: InclusionStrategy.SECURITY_S2.value, @@ -678,7 +736,7 @@ async def test_add_node( # Test QR provisioning information with S0 inclusion strategy fails await ws_client.send_json( { - ID: 5, + ID: 7, TYPE: "zwave_js/add_node", ENTRY_ID: entry.entry_id, INCLUSION_STRATEGY: InclusionStrategy.SECURITY_S0, @@ -708,7 +766,7 @@ async def test_add_node( # Test ValueError is caught as failure await ws_client.send_json( { - ID: 6, + ID: 8, TYPE: "zwave_js/add_node", ENTRY_ID: entry.entry_id, INCLUSION_STRATEGY: InclusionStrategy.DEFAULT.value, @@ -728,7 +786,7 @@ async def test_add_node( ): await ws_client.send_json( { - ID: 7, + ID: 9, TYPE: "zwave_js/add_node", ENTRY_ID: entry.entry_id, } @@ -744,7 +802,7 @@ async def test_add_node( await hass.async_block_till_done() await ws_client.send_json( - {ID: 8, TYPE: "zwave_js/add_node", ENTRY_ID: entry.entry_id} + {ID: 10, TYPE: "zwave_js/add_node", ENTRY_ID: entry.entry_id} ) msg = await ws_client.receive_json()