Add add & remove node commands to zwave_js websocket api (#45232)
This commit is contained in:
parent
adab9adbce
commit
8de0b7b948
8 changed files with 2190 additions and 5 deletions
|
@ -14,7 +14,13 @@ from homeassistant.helpers import device_registry
|
|||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
|
||||
from .const import DATA_CLIENT, DATA_UNSUBSCRIBE, DOMAIN, PLATFORMS
|
||||
from .const import (
|
||||
DATA_CLIENT,
|
||||
DATA_UNSUBSCRIBE,
|
||||
DOMAIN,
|
||||
EVENT_DEVICE_ADDED_TO_REGISTRY,
|
||||
PLATFORMS,
|
||||
)
|
||||
from .discovery import async_discover_values
|
||||
from .websocket_api import async_register_api
|
||||
|
||||
|
@ -30,13 +36,14 @@ async def async_setup(hass: HomeAssistant, config: dict) -> bool:
|
|||
|
||||
@callback
|
||||
def register_node_in_dev_reg(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
dev_reg: device_registry.DeviceRegistry,
|
||||
client: ZwaveClient,
|
||||
node: ZwaveNode,
|
||||
) -> None:
|
||||
"""Register node in dev reg."""
|
||||
dev_reg.async_get_or_create(
|
||||
device = dev_reg.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers={(DOMAIN, f"{client.driver.controller.home_id}-{node.node_id}")},
|
||||
sw_version=node.firmware_version,
|
||||
|
@ -45,6 +52,8 @@ def register_node_in_dev_reg(
|
|||
manufacturer=node.device_config.manufacturer,
|
||||
)
|
||||
|
||||
async_dispatcher_send(hass, EVENT_DEVICE_ADDED_TO_REGISTRY, device)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Z-Wave JS from a config entry."""
|
||||
|
@ -75,7 +84,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
LOGGER.debug("Processing node %s", node)
|
||||
|
||||
# register (or update) node in device registry
|
||||
register_node_in_dev_reg(entry, dev_reg, client, node)
|
||||
register_node_in_dev_reg(hass, entry, dev_reg, client, node)
|
||||
|
||||
# run discovery on all node values and create/update entities
|
||||
for disc_info in async_discover_values(node):
|
||||
|
@ -98,7 +107,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
)
|
||||
# we do submit the node to device registry so user has
|
||||
# some visual feedback that something is (in the process of) being added
|
||||
register_node_in_dev_reg(entry, dev_reg, client, node)
|
||||
register_node_in_dev_reg(hass, entry, dev_reg, client, node)
|
||||
|
||||
async def handle_ha_shutdown(event: Event) -> None:
|
||||
"""Handle HA shutdown."""
|
||||
|
|
|
@ -7,3 +7,5 @@ PLATFORMS = ["binary_sensor", "climate", "light", "lock", "sensor", "switch"]
|
|||
|
||||
DATA_CLIENT = "client"
|
||||
DATA_UNSUBSCRIBE = "unsubs"
|
||||
|
||||
EVENT_DEVICE_ADDED_TO_REGISTRY = f"{DOMAIN}_device_added_to_registry"
|
||||
|
|
|
@ -3,12 +3,17 @@
|
|||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
from zwave_js_server.client import Client as ZwaveClient
|
||||
from zwave_js_server.model.node import Node as ZwaveNode
|
||||
|
||||
from homeassistant.components import websocket_api
|
||||
from homeassistant.components.websocket_api.connection import ActiveConnection
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import device_registry
|
||||
from homeassistant.helpers.device_registry import DeviceEntry
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
||||
from .const import DATA_CLIENT, DOMAIN
|
||||
from .const import DATA_CLIENT, DOMAIN, EVENT_DEVICE_ADDED_TO_REGISTRY
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -21,6 +26,10 @@ TYPE = "type"
|
|||
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_add_node)
|
||||
websocket_api.async_register_command(hass, websocket_stop_inclusion)
|
||||
websocket_api.async_register_command(hass, websocket_remove_node)
|
||||
websocket_api.async_register_command(hass, websocket_stop_exclusion)
|
||||
|
||||
|
||||
@websocket_api.require_admin
|
||||
|
@ -50,3 +59,191 @@ def websocket_network_status(
|
|||
msg[ID],
|
||||
data,
|
||||
)
|
||||
|
||||
|
||||
@websocket_api.require_admin # type: ignore
|
||||
@websocket_api.async_response
|
||||
@websocket_api.websocket_command(
|
||||
{
|
||||
vol.Required(TYPE): "zwave_js/add_node",
|
||||
vol.Required(ENTRY_ID): str,
|
||||
vol.Optional("secure", default=False): bool,
|
||||
}
|
||||
)
|
||||
async def websocket_add_node(
|
||||
hass: HomeAssistant, connection: ActiveConnection, msg: dict
|
||||
) -> None:
|
||||
"""Add a node to the Z-Wave network."""
|
||||
entry_id = msg[ENTRY_ID]
|
||||
client = hass.data[DOMAIN][entry_id][DATA_CLIENT]
|
||||
controller = client.driver.controller
|
||||
include_non_secure = not msg["secure"]
|
||||
|
||||
@callback
|
||||
def async_cleanup() -> None:
|
||||
"""Remove signal listeners."""
|
||||
for unsub in unsubs:
|
||||
unsub()
|
||||
|
||||
@callback
|
||||
def forward_event(event: dict) -> None:
|
||||
connection.send_message(
|
||||
websocket_api.event_message(msg[ID], {"event": event["event"]})
|
||||
)
|
||||
|
||||
@callback
|
||||
def node_added(event: dict) -> None:
|
||||
node = event["node"]
|
||||
node_details = {
|
||||
"node_id": node.node_id,
|
||||
"status": node.status,
|
||||
"ready": node.ready,
|
||||
}
|
||||
connection.send_message(
|
||||
websocket_api.event_message(
|
||||
msg[ID], {"event": "node added", "node": node_details}
|
||||
)
|
||||
)
|
||||
|
||||
@callback
|
||||
def device_registered(device: DeviceEntry) -> None:
|
||||
device_details = {"name": device.name, "id": device.id}
|
||||
connection.send_message(
|
||||
websocket_api.event_message(
|
||||
msg[ID], {"event": "device registered", "device": device_details}
|
||||
)
|
||||
)
|
||||
|
||||
connection.subscriptions[msg["id"]] = async_cleanup
|
||||
unsubs = [
|
||||
controller.on("inclusion started", forward_event),
|
||||
controller.on("inclusion failed", forward_event),
|
||||
controller.on("inclusion stopped", forward_event),
|
||||
controller.on("node added", node_added),
|
||||
async_dispatcher_connect(
|
||||
hass, EVENT_DEVICE_ADDED_TO_REGISTRY, device_registered
|
||||
),
|
||||
]
|
||||
|
||||
result = await controller.async_begin_inclusion(include_non_secure)
|
||||
connection.send_result(
|
||||
msg[ID],
|
||||
result,
|
||||
)
|
||||
|
||||
|
||||
@websocket_api.require_admin # type: ignore
|
||||
@websocket_api.async_response
|
||||
@websocket_api.websocket_command(
|
||||
{
|
||||
vol.Required(TYPE): "zwave_js/stop_inclusion",
|
||||
vol.Required(ENTRY_ID): str,
|
||||
}
|
||||
)
|
||||
async def websocket_stop_inclusion(
|
||||
hass: HomeAssistant, connection: ActiveConnection, msg: dict
|
||||
) -> None:
|
||||
"""Cancel adding a node to the Z-Wave network."""
|
||||
entry_id = msg[ENTRY_ID]
|
||||
client = hass.data[DOMAIN][entry_id][DATA_CLIENT]
|
||||
controller = client.driver.controller
|
||||
result = await controller.async_stop_inclusion()
|
||||
connection.send_result(
|
||||
msg[ID],
|
||||
result,
|
||||
)
|
||||
|
||||
|
||||
@websocket_api.require_admin # type: ignore
|
||||
@websocket_api.async_response
|
||||
@websocket_api.websocket_command(
|
||||
{
|
||||
vol.Required(TYPE): "zwave_js/stop_exclusion",
|
||||
vol.Required(ENTRY_ID): str,
|
||||
}
|
||||
)
|
||||
async def websocket_stop_exclusion(
|
||||
hass: HomeAssistant, connection: ActiveConnection, msg: dict
|
||||
) -> None:
|
||||
"""Cancel removing a node from the Z-Wave network."""
|
||||
entry_id = msg[ENTRY_ID]
|
||||
client = hass.data[DOMAIN][entry_id][DATA_CLIENT]
|
||||
controller = client.driver.controller
|
||||
result = await controller.async_stop_exclusion()
|
||||
connection.send_result(
|
||||
msg[ID],
|
||||
result,
|
||||
)
|
||||
|
||||
|
||||
@websocket_api.require_admin # type:ignore
|
||||
@websocket_api.async_response
|
||||
@websocket_api.websocket_command(
|
||||
{
|
||||
vol.Required(TYPE): "zwave_js/remove_node",
|
||||
vol.Required(ENTRY_ID): str,
|
||||
}
|
||||
)
|
||||
async def websocket_remove_node(
|
||||
hass: HomeAssistant, connection: ActiveConnection, msg: dict
|
||||
) -> None:
|
||||
"""Remove a node from the Z-Wave network."""
|
||||
entry_id = msg[ENTRY_ID]
|
||||
client = hass.data[DOMAIN][entry_id][DATA_CLIENT]
|
||||
controller = client.driver.controller
|
||||
|
||||
@callback
|
||||
def async_cleanup() -> None:
|
||||
"""Remove signal listeners."""
|
||||
for unsub in unsubs:
|
||||
unsub()
|
||||
|
||||
@callback
|
||||
def forward_event(event: dict) -> None:
|
||||
connection.send_message(
|
||||
websocket_api.event_message(msg[ID], {"event": event["event"]})
|
||||
)
|
||||
|
||||
@callback
|
||||
def node_removed(event: dict) -> None:
|
||||
node = event["node"]
|
||||
node_details = {
|
||||
"node_id": node.node_id,
|
||||
}
|
||||
|
||||
# Remove from device registry
|
||||
hass.async_create_task(remove_from_device_registry(hass, client, node))
|
||||
|
||||
connection.send_message(
|
||||
websocket_api.event_message(
|
||||
msg[ID], {"event": "node removed", "node": node_details}
|
||||
)
|
||||
)
|
||||
|
||||
connection.subscriptions[msg["id"]] = async_cleanup
|
||||
unsubs = [
|
||||
controller.on("exclusion started", forward_event),
|
||||
controller.on("exclusion failed", forward_event),
|
||||
controller.on("exclusion stopped", forward_event),
|
||||
controller.on("node removed", node_removed),
|
||||
]
|
||||
|
||||
result = await controller.async_begin_exclusion()
|
||||
connection.send_result(
|
||||
msg[ID],
|
||||
result,
|
||||
)
|
||||
|
||||
|
||||
async def remove_from_device_registry(
|
||||
hass: HomeAssistant, client: ZwaveClient, node: ZwaveNode
|
||||
) -> None:
|
||||
"""Remove a node from the device registry."""
|
||||
registry = await device_registry.async_get_registry(hass)
|
||||
device = registry.async_get_device(
|
||||
{(DOMAIN, f"{client.driver.controller.home_id}-{node.node_id}")}
|
||||
)
|
||||
if device is None:
|
||||
return
|
||||
|
||||
registry.async_remove_device(device.id)
|
||||
|
|
|
@ -3,6 +3,7 @@ import json
|
|||
from unittest.mock import DEFAULT, patch
|
||||
|
||||
import pytest
|
||||
from zwave_js_server.event import Event
|
||||
from zwave_js_server.model.driver import Driver
|
||||
from zwave_js_server.model.node import Node
|
||||
from zwave_js_server.version import VersionInfo
|
||||
|
@ -75,6 +76,12 @@ def climate_radio_thermostat_ct100_plus_state_fixture():
|
|||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="nortek_thermostat_state", scope="session")
|
||||
def nortek_thermostat_state_fixture():
|
||||
"""Load the nortek thermostat node state fixture data."""
|
||||
return json.loads(load_fixture("zwave_js/nortek_thermostat_state.json"))
|
||||
|
||||
|
||||
@pytest.fixture(name="client")
|
||||
def mock_client_fixture(controller_state, version_state):
|
||||
"""Mock a client."""
|
||||
|
@ -140,6 +147,32 @@ def climate_radio_thermostat_ct100_plus_fixture(
|
|||
return node
|
||||
|
||||
|
||||
@pytest.fixture(name="nortek_thermostat")
|
||||
def nortek_thermostat_fixture(client, nortek_thermostat_state):
|
||||
"""Mock a nortek thermostat node."""
|
||||
node = Node(client, nortek_thermostat_state)
|
||||
client.driver.controller.nodes[node.node_id] = node
|
||||
return node
|
||||
|
||||
|
||||
@pytest.fixture(name="nortek_thermostat_added_event")
|
||||
def nortek_thermostat_added_event_fixture(client):
|
||||
"""Mock a Nortek thermostat node added event."""
|
||||
event_data = json.loads(load_fixture("zwave_js/nortek_thermostat_added_event.json"))
|
||||
event = Event("node added", event_data)
|
||||
return event
|
||||
|
||||
|
||||
@pytest.fixture(name="nortek_thermostat_removed_event")
|
||||
def nortek_thermostat_removed_event_fixture(client):
|
||||
"""Mock a Nortek thermostat node removed event."""
|
||||
event_data = json.loads(
|
||||
load_fixture("zwave_js/nortek_thermostat_removed_event.json")
|
||||
)
|
||||
event = Event("node removed", event_data)
|
||||
return event
|
||||
|
||||
|
||||
@pytest.fixture(name="integration")
|
||||
async def integration_fixture(hass, client):
|
||||
"""Set up the zwave_js integration."""
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
"""Test the Z-Wave JS Websocket API."""
|
||||
from zwave_js_server.event import Event
|
||||
|
||||
from homeassistant.components.zwave_js.const import DOMAIN
|
||||
from homeassistant.components.zwave_js.websocket_api import ENTRY_ID, ID, TYPE
|
||||
from homeassistant.helpers.device_registry import async_get_registry
|
||||
|
||||
|
||||
async def test_websocket_api(hass, integration, hass_ws_client):
|
||||
|
@ -16,3 +19,117 @@ async def test_websocket_api(hass, integration, hass_ws_client):
|
|||
|
||||
assert result["client"]["ws_server_url"] == "ws://test:3000/zjs"
|
||||
assert result["client"]["server_version"] == "1.0.0"
|
||||
|
||||
|
||||
async def test_add_node(
|
||||
hass, integration, client, hass_ws_client, nortek_thermostat_added_event
|
||||
):
|
||||
"""Test the add_node websocket command."""
|
||||
entry = integration
|
||||
ws_client = await hass_ws_client(hass)
|
||||
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
await ws_client.send_json(
|
||||
{ID: 3, TYPE: "zwave_js/add_node", ENTRY_ID: entry.entry_id}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
event = Event(
|
||||
type="inclusion started",
|
||||
data={
|
||||
"source": "controller",
|
||||
"event": "inclusion started",
|
||||
"secure": False,
|
||||
},
|
||||
)
|
||||
client.driver.receive_event(event)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["event"]["event"] == "inclusion started"
|
||||
|
||||
client.driver.receive_event(nortek_thermostat_added_event)
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["event"]["event"] == "node added"
|
||||
|
||||
|
||||
async def test_cancel_inclusion_exclusion(hass, integration, client, hass_ws_client):
|
||||
"""Test cancelling the inclusion and exclusion process."""
|
||||
entry = integration
|
||||
ws_client = await hass_ws_client(hass)
|
||||
|
||||
client.async_send_command.return_value = {"success": True}
|
||||
|
||||
await ws_client.send_json(
|
||||
{ID: 4, TYPE: "zwave_js/stop_inclusion", ENTRY_ID: entry.entry_id}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
await ws_client.send_json(
|
||||
{ID: 5, TYPE: "zwave_js/stop_exclusion", ENTRY_ID: entry.entry_id}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
|
||||
async def test_remove_node(
|
||||
hass,
|
||||
integration,
|
||||
client,
|
||||
hass_ws_client,
|
||||
nortek_thermostat,
|
||||
nortek_thermostat_removed_event,
|
||||
):
|
||||
"""Test the remove_node websocket command."""
|
||||
entry = integration
|
||||
ws_client = await hass_ws_client(hass)
|
||||
|
||||
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}
|
||||
)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["success"]
|
||||
|
||||
event = Event(
|
||||
type="exclusion started",
|
||||
data={
|
||||
"source": "controller",
|
||||
"event": "exclusion started",
|
||||
"secure": False,
|
||||
},
|
||||
)
|
||||
client.driver.receive_event(event)
|
||||
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["event"]["event"] == "exclusion started"
|
||||
|
||||
# Add mock node to controller
|
||||
client.driver.controller.nodes[67] = nortek_thermostat
|
||||
|
||||
dev_reg = await async_get_registry(hass)
|
||||
|
||||
# Create device registry entry for mock node
|
||||
device = dev_reg.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers={(DOMAIN, "3245146787-67")},
|
||||
name="Node 67",
|
||||
)
|
||||
|
||||
# Fire node removed event
|
||||
client.driver.receive_event(nortek_thermostat_removed_event)
|
||||
msg = await ws_client.receive_json()
|
||||
assert msg["event"]["event"] == "node removed"
|
||||
|
||||
# Verify device was removed from device registry
|
||||
device = dev_reg.async_get_device(
|
||||
identifiers={(DOMAIN, "3245146787-67")},
|
||||
)
|
||||
assert device is None
|
||||
|
|
260
tests/fixtures/zwave_js/nortek_thermostat_added_event.json
vendored
Normal file
260
tests/fixtures/zwave_js/nortek_thermostat_added_event.json
vendored
Normal file
|
@ -0,0 +1,260 @@
|
|||
{
|
||||
"source": "controller",
|
||||
"event": "node added",
|
||||
"node": {
|
||||
"nodeId": 53,
|
||||
"index": 0,
|
||||
"status": 0,
|
||||
"ready": false,
|
||||
"deviceClass": {
|
||||
"basic": "Static Controller",
|
||||
"generic": "Thermostat",
|
||||
"specific": "Thermostat General V2",
|
||||
"mandatorySupportedCCs": [
|
||||
"Basic",
|
||||
"Manufacturer Specific",
|
||||
"Thermostat Mode",
|
||||
"Thermostat Setpoint",
|
||||
"Version"
|
||||
],
|
||||
"mandatoryControlCCs": []
|
||||
},
|
||||
"neighbors": [],
|
||||
"interviewAttempts": 1,
|
||||
"endpoints": [
|
||||
{
|
||||
"nodeId": 53,
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
"values": [
|
||||
{
|
||||
"commandClassName": "Basic",
|
||||
"commandClass": 32,
|
||||
"endpoint": 0,
|
||||
"property": "currentValue",
|
||||
"propertyName": "currentValue",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"min": 0,
|
||||
"max": 99,
|
||||
"label": "Current value"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Basic",
|
||||
"commandClass": 32,
|
||||
"endpoint": 0,
|
||||
"property": "targetValue",
|
||||
"propertyName": "targetValue",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"min": 0,
|
||||
"max": 99,
|
||||
"label": "Target value"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Manufacturer Specific",
|
||||
"commandClass": 114,
|
||||
"endpoint": 0,
|
||||
"property": "manufacturerId",
|
||||
"propertyName": "manufacturerId",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"min": 0,
|
||||
"max": 65535,
|
||||
"label": "Manufacturer ID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Manufacturer Specific",
|
||||
"commandClass": 114,
|
||||
"endpoint": 0,
|
||||
"property": "productType",
|
||||
"propertyName": "productType",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"min": 0,
|
||||
"max": 65535,
|
||||
"label": "Product type"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Manufacturer Specific",
|
||||
"commandClass": 114,
|
||||
"endpoint": 0,
|
||||
"property": "productId",
|
||||
"propertyName": "productId",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"min": 0,
|
||||
"max": 65535,
|
||||
"label": "Product ID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Thermostat Mode",
|
||||
"commandClass": 64,
|
||||
"endpoint": 0,
|
||||
"property": "mode",
|
||||
"propertyName": "mode",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"min": 0,
|
||||
"max": 31,
|
||||
"label": "Thermostat mode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Thermostat Mode",
|
||||
"commandClass": 64,
|
||||
"endpoint": 0,
|
||||
"property": "manufacturerData",
|
||||
"propertyName": "manufacturerData",
|
||||
"metadata": {
|
||||
"type": "any",
|
||||
"readable": true,
|
||||
"writeable": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Version",
|
||||
"commandClass": 134,
|
||||
"endpoint": 0,
|
||||
"property": "libraryType",
|
||||
"propertyName": "libraryType",
|
||||
"metadata": {
|
||||
"type": "any",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Library type"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Version",
|
||||
"commandClass": 134,
|
||||
"endpoint": 0,
|
||||
"property": "protocolVersion",
|
||||
"propertyName": "protocolVersion",
|
||||
"metadata": {
|
||||
"type": "any",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave protocol version"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Version",
|
||||
"commandClass": 134,
|
||||
"endpoint": 0,
|
||||
"property": "firmwareVersions",
|
||||
"propertyName": "firmwareVersions",
|
||||
"metadata": {
|
||||
"type": "any",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave chip firmware versions"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Thermostat Operating State",
|
||||
"commandClass": 66,
|
||||
"endpoint": 0,
|
||||
"property": "state",
|
||||
"propertyName": "state",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"label": "Operating state",
|
||||
"states": {
|
||||
"0": "Idle",
|
||||
"1": "Heating",
|
||||
"2": "Cooling",
|
||||
"3": "Fan Only",
|
||||
"4": "Pending Heat",
|
||||
"5": "Pending Cool",
|
||||
"6": "Vent/Economizer",
|
||||
"7": "Aux Heating",
|
||||
"8": "2nd Stage Heating",
|
||||
"9": "2nd Stage Cooling",
|
||||
"10": "2nd Stage Aux Heat",
|
||||
"11": "3rd Stage Aux Heat"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Battery",
|
||||
"commandClass": 128,
|
||||
"endpoint": 0,
|
||||
"property": "level",
|
||||
"propertyName": "level",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"unit": "%",
|
||||
"label": "Battery level"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Battery",
|
||||
"commandClass": 128,
|
||||
"endpoint": 0,
|
||||
"property": "isLow",
|
||||
"propertyName": "isLow",
|
||||
"metadata": {
|
||||
"type": "boolean",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Low battery level"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Scene Activation",
|
||||
"commandClass": 43,
|
||||
"endpoint": 0,
|
||||
"property": "sceneId",
|
||||
"propertyName": "sceneId",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"min": 1,
|
||||
"max": 255,
|
||||
"label": "Scene ID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Scene Activation",
|
||||
"commandClass": 43,
|
||||
"endpoint": 0,
|
||||
"property": "dimmingDuration",
|
||||
"propertyName": "dimmingDuration",
|
||||
"metadata": {
|
||||
"type": "any",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"label": "Dimming duration"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
283
tests/fixtures/zwave_js/nortek_thermostat_removed_event.json
vendored
Normal file
283
tests/fixtures/zwave_js/nortek_thermostat_removed_event.json
vendored
Normal file
|
@ -0,0 +1,283 @@
|
|||
{
|
||||
"source": "controller",
|
||||
"event": "node removed",
|
||||
"node": {
|
||||
"nodeId": 67,
|
||||
"index": 0,
|
||||
"status": 4,
|
||||
"ready": true,
|
||||
"deviceClass": {
|
||||
"basic": "Static Controller",
|
||||
"generic": "Thermostat",
|
||||
"specific": "Thermostat General V2",
|
||||
"mandatorySupportedCCs": [
|
||||
"Basic",
|
||||
"Manufacturer Specific",
|
||||
"Thermostat Mode",
|
||||
"Thermostat Setpoint",
|
||||
"Version"
|
||||
],
|
||||
"mandatoryControlCCs": []
|
||||
},
|
||||
"isListening": false,
|
||||
"isFrequentListening": true,
|
||||
"isRouting": true,
|
||||
"maxBaudRate": 40000,
|
||||
"isSecure": false,
|
||||
"version": 4,
|
||||
"isBeaming": true,
|
||||
"deviceConfig": {
|
||||
"manufacturerId": 335,
|
||||
"manufacturer": "Nortek Security & Control LLC",
|
||||
"label": "GoControl GC-TBZ48",
|
||||
"description": "Z-Wave Plus Thermostat",
|
||||
"devices": [
|
||||
{
|
||||
"productType": "0x5442",
|
||||
"productId": "0x5431"
|
||||
},
|
||||
{
|
||||
"productType": "0x5442",
|
||||
"productId": "0x5436"
|
||||
},
|
||||
{
|
||||
"productType": "0x5442",
|
||||
"productId": "0x5437"
|
||||
}
|
||||
],
|
||||
"firmwareVersion": {
|
||||
"min": "0.0",
|
||||
"max": "255.255"
|
||||
},
|
||||
"paramInformation": {
|
||||
"_map": {}
|
||||
}
|
||||
},
|
||||
"label": "GoControl GC-TBZ48",
|
||||
"neighbors": [
|
||||
1,
|
||||
32,
|
||||
39,
|
||||
52
|
||||
],
|
||||
"interviewAttempts": 1,
|
||||
"endpoints": [
|
||||
{
|
||||
"nodeId": 67,
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
"values": [
|
||||
{
|
||||
"commandClassName": "Manufacturer Specific",
|
||||
"commandClass": 114,
|
||||
"endpoint": 0,
|
||||
"property": "manufacturerId",
|
||||
"propertyName": "manufacturerId",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"min": 0,
|
||||
"max": 65535,
|
||||
"label": "Manufacturer ID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Manufacturer Specific",
|
||||
"commandClass": 114,
|
||||
"endpoint": 0,
|
||||
"property": "productType",
|
||||
"propertyName": "productType",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"min": 0,
|
||||
"max": 65535,
|
||||
"label": "Product type"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Manufacturer Specific",
|
||||
"commandClass": 114,
|
||||
"endpoint": 0,
|
||||
"property": "productId",
|
||||
"propertyName": "productId",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"min": 0,
|
||||
"max": 65535,
|
||||
"label": "Product ID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Thermostat Mode",
|
||||
"commandClass": 64,
|
||||
"endpoint": 0,
|
||||
"property": "mode",
|
||||
"propertyName": "mode",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"min": 0,
|
||||
"max": 31,
|
||||
"label": "Thermostat mode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Thermostat Mode",
|
||||
"commandClass": 64,
|
||||
"endpoint": 0,
|
||||
"property": "manufacturerData",
|
||||
"propertyName": "manufacturerData",
|
||||
"metadata": {
|
||||
"type": "any",
|
||||
"readable": true,
|
||||
"writeable": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Version",
|
||||
"commandClass": 134,
|
||||
"endpoint": 0,
|
||||
"property": "libraryType",
|
||||
"propertyName": "libraryType",
|
||||
"metadata": {
|
||||
"type": "any",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Library type"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Version",
|
||||
"commandClass": 134,
|
||||
"endpoint": 0,
|
||||
"property": "protocolVersion",
|
||||
"propertyName": "protocolVersion",
|
||||
"metadata": {
|
||||
"type": "any",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave protocol version"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Version",
|
||||
"commandClass": 134,
|
||||
"endpoint": 0,
|
||||
"property": "firmwareVersions",
|
||||
"propertyName": "firmwareVersions",
|
||||
"metadata": {
|
||||
"type": "any",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave chip firmware versions"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Version",
|
||||
"commandClass": 134,
|
||||
"endpoint": 0,
|
||||
"property": "hardwareVersion",
|
||||
"propertyName": "hardwareVersion",
|
||||
"metadata": {
|
||||
"type": "any",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave chip hardware version"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Thermostat Operating State",
|
||||
"commandClass": 66,
|
||||
"endpoint": 0,
|
||||
"property": "state",
|
||||
"propertyName": "state",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"label": "Operating state",
|
||||
"states": {
|
||||
"0": "Idle",
|
||||
"1": "Heating",
|
||||
"2": "Cooling",
|
||||
"3": "Fan Only",
|
||||
"4": "Pending Heat",
|
||||
"5": "Pending Cool",
|
||||
"6": "Vent/Economizer",
|
||||
"7": "Aux Heating",
|
||||
"8": "2nd Stage Heating",
|
||||
"9": "2nd Stage Cooling",
|
||||
"10": "2nd Stage Aux Heat",
|
||||
"11": "3rd Stage Aux Heat"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Battery",
|
||||
"commandClass": 128,
|
||||
"endpoint": 0,
|
||||
"property": "level",
|
||||
"propertyName": "level",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"unit": "%",
|
||||
"label": "Battery level"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Battery",
|
||||
"commandClass": 128,
|
||||
"endpoint": 0,
|
||||
"property": "isLow",
|
||||
"propertyName": "isLow",
|
||||
"metadata": {
|
||||
"type": "boolean",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Low battery level"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Scene Activation",
|
||||
"commandClass": 43,
|
||||
"endpoint": 0,
|
||||
"property": "sceneId",
|
||||
"propertyName": "sceneId",
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"min": 1,
|
||||
"max": 255,
|
||||
"label": "Scene ID"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commandClassName": "Scene Activation",
|
||||
"commandClass": 43,
|
||||
"endpoint": 0,
|
||||
"property": "dimmingDuration",
|
||||
"propertyName": "dimmingDuration",
|
||||
"metadata": {
|
||||
"type": "any",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"label": "Dimming duration"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
1284
tests/fixtures/zwave_js/nortek_thermostat_state.json
vendored
Normal file
1284
tests/fixtures/zwave_js/nortek_thermostat_state.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue