diff --git a/homeassistant/components/zwave_js/api.py b/homeassistant/components/zwave_js/api.py index 32c23fe760b..8a08da13bfc 100644 --- a/homeassistant/components/zwave_js/api.py +++ b/homeassistant/components/zwave_js/api.py @@ -127,6 +127,8 @@ def async_register_api(hass: HomeAssistant) -> None: """Register all of our api endpoints.""" websocket_api.async_register_command(hass, websocket_network_status) websocket_api.async_register_command(hass, websocket_node_status) + 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_stop_inclusion) websocket_api.async_register_command(hass, websocket_remove_node) @@ -209,6 +211,60 @@ async def websocket_node_status( ) +@websocket_api.websocket_command( + { + vol.Required(TYPE): "zwave_js/node_metadata", + vol.Required(ENTRY_ID): str, + vol.Required(NODE_ID): int, + } +) +@websocket_api.async_response +@async_get_node +async def websocket_node_metadata( + hass: HomeAssistant, + connection: ActiveConnection, + msg: dict, + node: Node, +) -> None: + """Get the metadata of a Z-Wave JS node.""" + data = { + "node_id": node.node_id, + "exclusion": node.device_config.metadata.exclusion, + "inclusion": node.device_config.metadata.inclusion, + "manual": node.device_config.metadata.manual, + "wakeup": node.device_config.metadata.wakeup, + "reset": node.device_config.metadata.reset, + "device_database_url": node.device_database_url, + } + connection.send_result( + msg[ID], + data, + ) + + +@websocket_api.websocket_command( + { + vol.Required(TYPE): "zwave_js/ping_node", + vol.Required(ENTRY_ID): str, + vol.Required(NODE_ID): int, + } +) +@websocket_api.async_response +@async_get_node +async def websocket_ping_node( + hass: HomeAssistant, + connection: ActiveConnection, + msg: dict, + node: Node, +) -> None: + """Ping a Z-Wave JS node.""" + result = await node.async_ping() + connection.send_result( + msg[ID], + result, + ) + + @websocket_api.require_admin @websocket_api.websocket_command( { diff --git a/tests/components/zwave_js/conftest.py b/tests/components/zwave_js/conftest.py index eef79b533d3..3f5b1fbe88d 100644 --- a/tests/components/zwave_js/conftest.py +++ b/tests/components/zwave_js/conftest.py @@ -371,6 +371,12 @@ def zem_31_state_fixture(): return json.loads(load_fixture("zwave_js/zen_31_state.json")) +@pytest.fixture(name="wallmote_central_scene_state", scope="session") +def wallmote_central_scene_state_fixture(): + """Load the wallmote central scene node state fixture data.""" + return json.loads(load_fixture("zwave_js/wallmote_central_scene_state.json")) + + @pytest.fixture(name="client") def mock_client_fixture(controller_state, version_state): """Mock a client.""" @@ -711,3 +717,11 @@ def zen_31_fixture(client, zen_31_state): node = Node(client, copy.deepcopy(zen_31_state)) client.driver.controller.nodes[node.node_id] = node return node + + +@pytest.fixture(name="wallmote_central_scene") +def wallmote_central_scene_fixture(client, wallmote_central_scene_state): + """Mock a wallmote central scene node.""" + node = Node(client, copy.deepcopy(wallmote_central_scene_state)) + client.driver.controller.nodes[node.node_id] = node + return node diff --git a/tests/components/zwave_js/test_api.py b/tests/components/zwave_js/test_api.py index 141998526ee..596ed9c0ed9 100644 --- a/tests/components/zwave_js/test_api.py +++ b/tests/components/zwave_js/test_api.py @@ -59,7 +59,7 @@ async def test_network_status(hass, integration, hass_ws_client): assert msg["error"]["code"] == ERR_NOT_LOADED -async def test_node_status(hass, integration, multisensor_6, hass_ws_client): +async def test_node_status(hass, multisensor_6, integration, hass_ws_client): """Test the node status websocket command.""" entry = integration ws_client = await hass_ws_client(hass) @@ -113,8 +113,139 @@ async def test_node_status(hass, integration, multisensor_6, hass_ws_client): assert msg["error"]["code"] == ERR_NOT_LOADED +async def test_node_metadata(hass, wallmote_central_scene, integration, hass_ws_client): + """Test the node metadata websocket command.""" + entry = integration + ws_client = await hass_ws_client(hass) + + node = wallmote_central_scene + await ws_client.send_json( + { + ID: 3, + TYPE: "zwave_js/node_metadata", + ENTRY_ID: entry.entry_id, + NODE_ID: node.node_id, + } + ) + msg = await ws_client.receive_json() + result = msg["result"] + + assert result[NODE_ID] == 35 + assert result["inclusion"] == ( + "To add the ZP3111 to the Z-Wave network (inclusion), place the Z-Wave " + "primary controller into inclusion mode. Press the Program Switch of ZP3111 " + "for sending the NIF. After sending NIF, Z-Wave will send the auto inclusion, " + "otherwise, ZP3111 will go to sleep after 20 seconds." + ) + assert result["exclusion"] == ( + "To remove the ZP3111 from the Z-Wave network (exclusion), place the Z-Wave " + "primary controller into \u201cexclusion\u201d mode, and following its " + "instruction to delete the ZP3111 to the controller. Press the Program Switch " + "of ZP3111 once to be excluded." + ) + assert result["reset"] == ( + "Remove cover to triggered tamper switch, LED flash once & send out Alarm " + "Report. Press Program Switch 10 times within 10 seconds, ZP3111 will send " + "the \u201cDevice Reset Locally Notification\u201d command and reset to the " + "factory default. (Remark: This is to be used only in the case of primary " + "controller being inoperable or otherwise unavailable.)" + ) + assert result["manual"] == ( + "https://products.z-wavealliance.org/ProductManual/File?folder=&filename=MarketCertificationFiles/2479/ZP3111-5_R2_20170316.pdf" + ) + assert not result["wakeup"] + assert ( + result["device_database_url"] + == "https://devices.zwave-js.io/?jumpTo=0x0086:0x0002:0x0082:0.0" + ) + + # Test getting non-existent node fails + await ws_client.send_json( + { + ID: 4, + TYPE: "zwave_js/node_metadata", + ENTRY_ID: entry.entry_id, + NODE_ID: 99999, + } + ) + msg = await ws_client.receive_json() + assert not msg["success"] + assert msg["error"]["code"] == ERR_NOT_FOUND + + # 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: 5, + TYPE: "zwave_js/node_metadata", + ENTRY_ID: entry.entry_id, + NODE_ID: node.node_id, + } + ) + msg = await ws_client.receive_json() + + assert not msg["success"] + assert msg["error"]["code"] == ERR_NOT_LOADED + + +async def test_ping_node( + hass, wallmote_central_scene, integration, client, hass_ws_client +): + """Test the ping_node websocket command.""" + entry = integration + ws_client = await hass_ws_client(hass) + node = wallmote_central_scene + + client.async_send_command.return_value = {"responded": True} + + await ws_client.send_json( + { + ID: 3, + TYPE: "zwave_js/ping_node", + ENTRY_ID: entry.entry_id, + NODE_ID: node.node_id, + } + ) + + msg = await ws_client.receive_json() + assert msg["success"] + assert msg["result"] + + # Test getting non-existent node fails + await ws_client.send_json( + { + ID: 4, + TYPE: "zwave_js/ping_node", + ENTRY_ID: entry.entry_id, + NODE_ID: 99999, + } + ) + msg = await ws_client.receive_json() + assert not msg["success"] + assert msg["error"]["code"] == ERR_NOT_FOUND + + # 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: 5, + TYPE: "zwave_js/ping_node", + ENTRY_ID: entry.entry_id, + NODE_ID: node.node_id, + } + ) + msg = await ws_client.receive_json() + + assert not msg["success"] + assert msg["error"]["code"] == ERR_NOT_LOADED + + async def test_add_node( - hass, integration, client, hass_ws_client, nortek_thermostat_added_event + hass, nortek_thermostat_added_event, integration, client, hass_ws_client ): """Test the add_node websocket command.""" entry = integration @@ -324,10 +455,10 @@ async def test_remove_node( async def test_replace_failed_node( hass, + nortek_thermostat, integration, client, hass_ws_client, - nortek_thermostat, nortek_thermostat_added_event, nortek_thermostat_removed_event, ): @@ -475,10 +606,10 @@ async def test_replace_failed_node( async def test_remove_failed_node( hass, + nortek_thermostat, integration, client, hass_ws_client, - nortek_thermostat, nortek_thermostat_removed_event, ): """Test the remove_failed_node websocket command.""" @@ -538,7 +669,7 @@ async def test_remove_failed_node( async def test_refresh_node_info( - hass, client, integration, hass_ws_client, multisensor_6 + hass, client, multisensor_6, integration, hass_ws_client ): """Test that the refresh_node_info WS API call works.""" entry = integration @@ -637,7 +768,7 @@ async def test_refresh_node_info( async def test_refresh_node_values( - hass, client, integration, hass_ws_client, multisensor_6 + hass, client, multisensor_6, integration, hass_ws_client ): """Test that the refresh_node_values WS API call works.""" entry = integration @@ -690,7 +821,7 @@ async def test_refresh_node_values( async def test_refresh_node_cc_values( - hass, client, integration, hass_ws_client, multisensor_6 + hass, client, multisensor_6, integration, hass_ws_client ): """Test that the refresh_node_cc_values WS API call works.""" entry = integration @@ -920,7 +1051,7 @@ async def test_set_config_parameter( assert msg["error"]["code"] == ERR_NOT_LOADED -async def test_get_config_parameters(hass, integration, multisensor_6, hass_ws_client): +async def test_get_config_parameters(hass, multisensor_6, integration, hass_ws_client): """Test the get config parameters websocket command.""" entry = integration ws_client = await hass_ws_client(hass) diff --git a/tests/fixtures/zwave_js/wallmote_central_scene_state.json b/tests/fixtures/zwave_js/wallmote_central_scene_state.json new file mode 100644 index 00000000000..22eb05c9ce5 --- /dev/null +++ b/tests/fixtures/zwave_js/wallmote_central_scene_state.json @@ -0,0 +1,698 @@ +{ + "nodeId": 35, + "index": 0, + "installerIcon": 7172, + "userIcon": 7172, + "status": 1, + "ready": true, + "deviceClass": { + "basic": { + "key": 4, + "label": "Routing Slave" + }, + "generic": { + "key": 24, + "label": "Wall Controller" + }, + "specific": { + "key": 1, + "label": "Basic Wall Controller" + }, + "mandatorySupportedCCs": [], + "mandatoryControlledCCs": [] + }, + "isListening": false, + "isFrequentListening": false, + "isRouting": true, + "maxBaudRate": 40000, + "isSecure": false, + "version": 4, + "isBeaming": true, + "manufacturerId": 134, + "productId": 130, + "productType": 258, + "firmwareVersion": "2.3", + "zwavePlusVersion": 1, + "nodeType": 0, + "roleType": 4, + "name": "mbr_wallmote_quad", + "deviceDatabaseUrl": "https://devices.zwave-js.io/?jumpTo=0x0086:0x0002:0x0082:0.0", + "deviceConfig": { + "filename": "/usr/src/app/node_modules/@zwave-js/config/config/devices/0x0086/zw130.json", + "manufacturerId": 134, + "manufacturer": "AEON Labs", + "label": "ZW130", + "description": "WallMote Quad", + "devices": [ + { + "productType": 2, + "productId": 130 + }, + { + "productType": 258, + "productId": 130 + }, + { + "productType": 514, + "productId": 130 + } + ], + "firmwareVersion": { + "min": "0.0", + "max": "255.255" + }, + "paramInformation": { + "_map": {} + }, + "metadata": { + "inclusion": "To add the ZP3111 to the Z-Wave network (inclusion), place the Z-Wave primary controller into inclusion mode. Press the Program Switch of ZP3111 for sending the NIF. After sending NIF, Z-Wave will send the auto inclusion, otherwise, ZP3111 will go to sleep after 20 seconds.", + "exclusion": "To remove the ZP3111 from the Z-Wave network (exclusion), place the Z-Wave primary controller into \u201cexclusion\u201d mode, and following its instruction to delete the ZP3111 to the controller. Press the Program Switch of ZP3111 once to be excluded.", + "reset": "Remove cover to triggered tamper switch, LED flash once & send out Alarm Report. Press Program Switch 10 times within 10 seconds, ZP3111 will send the \u201cDevice Reset Locally Notification\u201d command and reset to the factory default. (Remark: This is to be used only in the case of primary controller being inoperable or otherwise unavailable.)", + "manual": "https://products.z-wavealliance.org/ProductManual/File?folder=&filename=MarketCertificationFiles/2479/ZP3111-5_R2_20170316.pdf" + }, + "isEmbedded": true + }, + "label": "ZW130", + "neighbors": [1, 14, 15, 16, 22, 30, 31, 5, 6, 7, 8], + "endpointCountIsDynamic": false, + "endpointsHaveIdenticalCapabilities": true, + "individualEndpointCount": 4, + "aggregatedEndpointCount": 0, + "interviewAttempts": 1, + "interviewStage": "NodeInfo", + "commandClasses": [ + { + "id": 89, + "name": "Association Group Information", + "version": 1, + "isSecure": false + }, + { + "id": 90, + "name": "Device Reset Locally", + "version": 1, + "isSecure": false + }, + { + "id": 91, + "name": "Central Scene", + "version": 2, + "isSecure": false + }, + { + "id": 94, + "name": "Z-Wave Plus Info", + "version": 2, + "isSecure": false + }, + { + "id": 96, + "name": "Multi Channel", + "version": 4, + "isSecure": false + }, + { + "id": 112, + "name": "Configuration", + "version": 1, + "isSecure": false + }, + { + "id": 113, + "name": "Notification", + "version": 4, + "isSecure": false + }, + { + "id": 114, + "name": "Manufacturer Specific", + "version": 2, + "isSecure": false + }, + { + "id": 122, + "name": "Firmware Update Meta Data", + "version": 2, + "isSecure": false + }, + { + "id": 128, + "name": "Battery", + "version": 1, + "isSecure": false + }, + { + "id": 132, + "name": "Wake Up", + "version": 1, + "isSecure": false + }, + { + "id": 133, + "name": "Association", + "version": 2, + "isSecure": false + }, + { + "id": 134, + "name": "Version", + "version": 2, + "isSecure": false + }, + { + "id": 142, + "name": "Multi Channel Association", + "version": 3, + "isSecure": false + } + ], + "endpoints": [ + { + "nodeId": 35, + "index": 0, + "installerIcon": 7172, + "userIcon": 7172 + }, + { + "nodeId": 35, + "index": 1, + "installerIcon": 7169, + "userIcon": 7169 + }, + { + "nodeId": 35, + "index": 2, + "installerIcon": 7169, + "userIcon": 7169 + }, + { + "nodeId": 35, + "index": 3, + "installerIcon": 7169, + "userIcon": 7169 + }, + { + "nodeId": 35, + "index": 4, + "installerIcon": 7169, + "userIcon": 7169 + } + ], + "values": [ + { + "endpoint": 0, + "commandClass": 91, + "commandClassName": "Central Scene", + "property": "slowRefresh", + "propertyName": "slowRefresh", + "ccVersion": 2, + "metadata": { + "type": "boolean", + "readable": true, + "writeable": true, + "label": "Send held down notifications at a slow rate", + "description": "When this is true, KeyHeldDown notifications are sent every 55s. When this is false, the notifications are sent every 200ms." + } + }, + { + "endpoint": 0, + "commandClass": 91, + "commandClassName": "Central Scene", + "property": "scene", + "propertyKey": "004", + "propertyName": "scene", + "propertyKeyName": "004", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "min": 0, + "max": 255, + "label": "Scene 004", + "states": { + "0": "KeyPressed", + "1": "KeyReleased", + "2": "KeyHeldDown" + } + } + }, + { + "endpoint": 0, + "commandClass": 91, + "commandClassName": "Central Scene", + "property": "scene", + "propertyKey": "001", + "propertyName": "scene", + "propertyKeyName": "001", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "min": 0, + "max": 255, + "label": "Scene 001", + "states": { + "0": "KeyPressed", + "1": "KeyReleased", + "2": "KeyHeldDown" + } + } + }, + { + "endpoint": 0, + "commandClass": 91, + "commandClassName": "Central Scene", + "property": "scene", + "propertyKey": "002", + "propertyName": "scene", + "propertyKeyName": "002", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "min": 0, + "max": 255, + "label": "Scene 002", + "states": { + "0": "KeyPressed", + "1": "KeyReleased", + "2": "KeyHeldDown" + } + } + }, + { + "endpoint": 0, + "commandClass": 91, + "commandClassName": "Central Scene", + "property": "scene", + "propertyKey": "003", + "propertyName": "scene", + "propertyKeyName": "003", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "min": 0, + "max": 255, + "label": "Scene 003", + "states": { + "0": "KeyPressed", + "1": "KeyReleased", + "2": "KeyHeldDown" + } + } + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 1, + "propertyName": "Touch sound", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 1, + "min": 0, + "max": 1, + "default": 1, + "format": 0, + "allowManualEntry": false, + "states": { + "0": "Disable", + "1": "Enable" + }, + "label": "Touch sound", + "description": "Enable/disable the touch sound.", + "isFromConfig": true + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 2, + "propertyName": "Touch vibration", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 1, + "min": 0, + "max": 1, + "default": 1, + "format": 0, + "allowManualEntry": false, + "states": { + "0": "Disable", + "1": "Enable" + }, + "label": "Touch vibration", + "description": "Enable/disable the touch vibration.", + "isFromConfig": true + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 3, + "propertyName": "Button slide", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 1, + "min": 0, + "max": 1, + "default": 1, + "format": 0, + "allowManualEntry": false, + "states": { + "0": "Disable", + "1": "Enable" + }, + "label": "Button slide", + "description": "Enable/disable the function of button slide.", + "isFromConfig": true + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 4, + "propertyName": "Notification report", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 1, + "min": 0, + "max": 3, + "default": 1, + "format": 0, + "allowManualEntry": false, + "states": { + "1": "Central scene", + "3": "Central scene and config" + }, + "label": "Notification report", + "description": "Which notification to be sent to the associated devices.", + "isFromConfig": true + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 39, + "propertyName": "Low battery value", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 1, + "min": 0, + "max": 50, + "default": 5, + "format": 0, + "allowManualEntry": true, + "label": "Low battery value", + "description": "Set the low battery value", + "isFromConfig": true + }, + "value": 20 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 255, + "propertyName": "Reset the WallMote Quad", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": false, + "writeable": true, + "valueSize": 4, + "min": 0, + "max": 1431655765, + "default": 0, + "format": 0, + "allowManualEntry": false, + "states": { + "0": "Reset to factory default", + "1431655765": "Reset and remove" + }, + "label": "Reset the WallMote Quad", + "description": "Reset the WallMote Quad to factory default.", + "isFromConfig": true + } + }, + { + "endpoint": 0, + "commandClass": 113, + "commandClassName": "Notification", + "property": "Power Management", + "propertyKey": "Battery load status", + "propertyName": "Power Management", + "propertyKeyName": "Battery load status", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "min": 0, + "max": 255, + "label": "Battery load status", + "states": { + "0": "idle", + "12": "Battery is charging" + }, + "ccSpecific": { + "notificationType": 8 + } + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 113, + "commandClassName": "Notification", + "property": "Power Management", + "propertyKey": "Battery level status", + "propertyName": "Power Management", + "propertyKeyName": "Battery level status", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "min": 0, + "max": 255, + "label": "Battery level status", + "states": { + "0": "idle", + "13": "Battery is fully charged", + "14": "Charge battery soon", + "15": "Charge battery now" + }, + "ccSpecific": { + "notificationType": 8 + } + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 114, + "commandClassName": "Manufacturer Specific", + "property": "manufacturerId", + "propertyName": "manufacturerId", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "min": 0, + "max": 65535, + "label": "Manufacturer ID" + }, + "value": 134 + }, + { + "endpoint": 0, + "commandClass": 114, + "commandClassName": "Manufacturer Specific", + "property": "productType", + "propertyName": "productType", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "min": 0, + "max": 65535, + "label": "Product type" + }, + "value": 258 + }, + { + "endpoint": 0, + "commandClass": 114, + "commandClassName": "Manufacturer Specific", + "property": "productId", + "propertyName": "productId", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "min": 0, + "max": 65535, + "label": "Product ID" + }, + "value": 130 + }, + { + "endpoint": 0, + "commandClass": 128, + "commandClassName": "Battery", + "property": "level", + "propertyName": "level", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "min": 0, + "max": 100, + "unit": "%", + "label": "Battery level" + }, + "value": 100 + }, + { + "endpoint": 0, + "commandClass": 128, + "commandClassName": "Battery", + "property": "isLow", + "propertyName": "isLow", + "ccVersion": 1, + "metadata": { + "type": "boolean", + "readable": true, + "writeable": false, + "label": "Low battery level" + }, + "value": false + }, + { + "endpoint": 0, + "commandClass": 132, + "commandClassName": "Wake Up", + "property": "wakeUpInterval", + "propertyName": "wakeUpInterval", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": false, + "writeable": true, + "min": 0, + "max": 864000, + "label": "Wake Up interval", + "steps": 240, + "default": 0 + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 132, + "commandClassName": "Wake Up", + "property": "controllerNodeId", + "propertyName": "controllerNodeId", + "ccVersion": 1, + "metadata": { + "type": "any", + "readable": true, + "writeable": false, + "label": "Node ID of the controller" + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "libraryType", + "propertyName": "libraryType", + "ccVersion": 2, + "metadata": { + "type": "any", + "readable": true, + "writeable": false, + "label": "Library type" + }, + "value": 3 + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "protocolVersion", + "propertyName": "protocolVersion", + "ccVersion": 2, + "metadata": { + "type": "any", + "readable": true, + "writeable": false, + "label": "Z-Wave protocol version" + }, + "value": "4.62" + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "firmwareVersions", + "propertyName": "firmwareVersions", + "ccVersion": 2, + "metadata": { + "type": "any", + "readable": true, + "writeable": false, + "label": "Z-Wave chip firmware versions" + }, + "value": ["2.3"] + }, + { + "endpoint": 0, + "commandClass": 134, + "commandClassName": "Version", + "property": "hardwareVersion", + "propertyName": "hardwareVersion", + "ccVersion": 2, + "metadata": { + "type": "any", + "readable": true, + "writeable": false, + "label": "Z-Wave chip hardware version" + } + } + ] +}