mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 01:38:02 +00:00
Add zwave_js device triggers (#51968)
* Initial support for zwave_js device triggers * lint * Add node status changed trigger * comments * create helper function and simplify trigger logic * simplify code * fix exception * remove unused type ignore * switch to append to make future changes easier * make exception consistent * Add state config schema validation * comment * remove 0 from falsy check * increase test coverage * typos * Add central scene and scene activation value notification triggers * reorder things for readability and enumerate node statuses * Add support for Basic CC value notifications * fix schemas since additional fields on triggers aren't very flexible * pylint * remove extra logger statement * fix comment * dont use get when we know key will be available in dict * tweak text * use better schema for required extra fields that are ints * rename trigger types to make them easier to parse * fix strings * missed renaming of one trigger type * typo * Fix strings * reduce complexity * Use Al's suggestion for strings * add additional failure test cases * remove errant logging statement * make CC required * raise vol.Invalid when value ID isn't legit to prepare for next PR * Use helper function * fix tests * black
This commit is contained in:
parent
4d711898c7
commit
dd908caeba
371
homeassistant/components/zwave_js/device_trigger.py
Normal file
371
homeassistant/components/zwave_js/device_trigger.py
Normal file
@ -0,0 +1,371 @@
|
||||
"""Provides device triggers for Z-Wave JS."""
|
||||
from __future__ import annotations
|
||||
|
||||
import voluptuous as vol
|
||||
from zwave_js_server.const import CommandClass
|
||||
|
||||
from homeassistant.components.automation import AutomationActionType
|
||||
from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA
|
||||
from homeassistant.components.homeassistant.triggers import event, state
|
||||
from homeassistant.const import (
|
||||
CONF_DEVICE_ID,
|
||||
CONF_DOMAIN,
|
||||
CONF_ENTITY_ID,
|
||||
CONF_PLATFORM,
|
||||
CONF_TYPE,
|
||||
)
|
||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import (
|
||||
config_validation as cv,
|
||||
device_registry,
|
||||
entity_registry,
|
||||
)
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import (
|
||||
ATTR_COMMAND_CLASS,
|
||||
ATTR_DATA_TYPE,
|
||||
ATTR_ENDPOINT,
|
||||
ATTR_EVENT,
|
||||
ATTR_EVENT_LABEL,
|
||||
ATTR_EVENT_TYPE,
|
||||
ATTR_LABEL,
|
||||
ATTR_PROPERTY,
|
||||
ATTR_PROPERTY_KEY,
|
||||
ATTR_TYPE,
|
||||
ATTR_VALUE,
|
||||
ATTR_VALUE_RAW,
|
||||
DOMAIN,
|
||||
ZWAVE_JS_NOTIFICATION_EVENT,
|
||||
ZWAVE_JS_VALUE_NOTIFICATION_EVENT,
|
||||
)
|
||||
from .helpers import (
|
||||
async_get_node_from_device_id,
|
||||
async_get_node_status_sensor_entity_id,
|
||||
get_zwave_value_from_config,
|
||||
)
|
||||
|
||||
CONF_SUBTYPE = "subtype"
|
||||
CONF_VALUE_ID = "value_id"
|
||||
|
||||
# Trigger types
|
||||
ENTRY_CONTROL_NOTIFICATION = "event.notification.entry_control"
|
||||
NOTIFICATION_NOTIFICATION = "event.notification.notification"
|
||||
BASIC_VALUE_NOTIFICATION = "event.value_notification.basic"
|
||||
CENTRAL_SCENE_VALUE_NOTIFICATION = "event.value_notification.central_scene"
|
||||
SCENE_ACTIVATION_VALUE_NOTIFICATION = "event.value_notification.scene_activation"
|
||||
NODE_STATUS = "state.node_status"
|
||||
|
||||
NOTIFICATION_EVENT_CC_MAPPINGS = (
|
||||
(ENTRY_CONTROL_NOTIFICATION, CommandClass.ENTRY_CONTROL),
|
||||
(NOTIFICATION_NOTIFICATION, CommandClass.NOTIFICATION),
|
||||
)
|
||||
|
||||
# Event based trigger schemas
|
||||
BASE_EVENT_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(ATTR_COMMAND_CLASS): vol.In([cc.value for cc in CommandClass]),
|
||||
}
|
||||
)
|
||||
|
||||
NOTIFICATION_NOTIFICATION_SCHEMA = BASE_EVENT_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_TYPE): NOTIFICATION_NOTIFICATION,
|
||||
vol.Optional(f"{ATTR_TYPE}."): vol.Coerce(int),
|
||||
vol.Optional(ATTR_LABEL): cv.string,
|
||||
vol.Optional(ATTR_EVENT): vol.Coerce(int),
|
||||
vol.Optional(ATTR_EVENT_LABEL): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
ENTRY_CONTROL_NOTIFICATION_SCHEMA = BASE_EVENT_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_TYPE): ENTRY_CONTROL_NOTIFICATION,
|
||||
vol.Optional(ATTR_EVENT_TYPE): vol.Coerce(int),
|
||||
vol.Optional(ATTR_DATA_TYPE): vol.Coerce(int),
|
||||
}
|
||||
)
|
||||
|
||||
BASE_VALUE_NOTIFICATION_EVENT_SCHEMA = BASE_EVENT_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(ATTR_PROPERTY): vol.Any(int, str),
|
||||
vol.Required(ATTR_PROPERTY_KEY): vol.Any(None, int, str),
|
||||
vol.Required(ATTR_ENDPOINT): vol.Coerce(int),
|
||||
vol.Required(ATTR_VALUE): vol.Coerce(int),
|
||||
vol.Required(CONF_SUBTYPE): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
BASIC_VALUE_NOTIFICATION_SCHEMA = BASE_VALUE_NOTIFICATION_EVENT_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_TYPE): BASIC_VALUE_NOTIFICATION,
|
||||
}
|
||||
)
|
||||
|
||||
CENTRAL_SCENE_VALUE_NOTIFICATION_SCHEMA = BASE_VALUE_NOTIFICATION_EVENT_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_TYPE): CENTRAL_SCENE_VALUE_NOTIFICATION,
|
||||
}
|
||||
)
|
||||
|
||||
SCENE_ACTIVATION_VALUE_NOTIFICATION_SCHEMA = (
|
||||
BASE_VALUE_NOTIFICATION_EVENT_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_TYPE): SCENE_ACTIVATION_VALUE_NOTIFICATION,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
# State based trigger schemas
|
||||
BASE_STATE_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_ENTITY_ID): cv.entity_id,
|
||||
}
|
||||
)
|
||||
|
||||
NODE_STATUSES = ["asleep", "awake", "dead", "alive"]
|
||||
|
||||
NODE_STATUS_SCHEMA = BASE_STATE_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_TYPE): NODE_STATUS,
|
||||
vol.Optional(state.CONF_FROM): vol.In(NODE_STATUSES),
|
||||
vol.Optional(state.CONF_TO): vol.In(NODE_STATUSES),
|
||||
vol.Optional(state.CONF_FOR): cv.positive_time_period_dict,
|
||||
}
|
||||
)
|
||||
|
||||
TRIGGER_SCHEMA = vol.Any(
|
||||
ENTRY_CONTROL_NOTIFICATION_SCHEMA,
|
||||
NOTIFICATION_NOTIFICATION_SCHEMA,
|
||||
BASIC_VALUE_NOTIFICATION_SCHEMA,
|
||||
CENTRAL_SCENE_VALUE_NOTIFICATION_SCHEMA,
|
||||
SCENE_ACTIVATION_VALUE_NOTIFICATION_SCHEMA,
|
||||
NODE_STATUS_SCHEMA,
|
||||
)
|
||||
|
||||
|
||||
async def async_get_triggers(hass: HomeAssistant, device_id: str) -> list[dict]:
|
||||
"""List device triggers for Z-Wave JS devices."""
|
||||
dev_reg = device_registry.async_get(hass)
|
||||
node = async_get_node_from_device_id(hass, device_id, dev_reg)
|
||||
|
||||
triggers = []
|
||||
base_trigger = {
|
||||
CONF_PLATFORM: "device",
|
||||
CONF_DEVICE_ID: device_id,
|
||||
CONF_DOMAIN: DOMAIN,
|
||||
}
|
||||
|
||||
# We can add a node status trigger if the node status sensor is enabled
|
||||
ent_reg = entity_registry.async_get(hass)
|
||||
entity_id = async_get_node_status_sensor_entity_id(
|
||||
hass, device_id, ent_reg, dev_reg
|
||||
)
|
||||
if (entity := ent_reg.async_get(entity_id)) is not None and not entity.disabled:
|
||||
triggers.append(
|
||||
{**base_trigger, CONF_TYPE: NODE_STATUS, CONF_ENTITY_ID: entity_id}
|
||||
)
|
||||
|
||||
# Handle notification event triggers
|
||||
triggers.extend(
|
||||
[
|
||||
{**base_trigger, CONF_TYPE: event_type, ATTR_COMMAND_CLASS: command_class}
|
||||
for event_type, command_class in NOTIFICATION_EVENT_CC_MAPPINGS
|
||||
if any(cc.id == command_class for cc in node.command_classes)
|
||||
]
|
||||
)
|
||||
|
||||
# Handle central scene value notification event triggers
|
||||
triggers.extend(
|
||||
[
|
||||
{
|
||||
**base_trigger,
|
||||
CONF_TYPE: CENTRAL_SCENE_VALUE_NOTIFICATION,
|
||||
ATTR_PROPERTY: value.property_,
|
||||
ATTR_PROPERTY_KEY: value.property_key,
|
||||
ATTR_ENDPOINT: value.endpoint,
|
||||
ATTR_COMMAND_CLASS: CommandClass.CENTRAL_SCENE,
|
||||
CONF_SUBTYPE: f"Endpoint {value.endpoint} Scene {value.property_key}",
|
||||
}
|
||||
for value in node.get_command_class_values(
|
||||
CommandClass.CENTRAL_SCENE
|
||||
).values()
|
||||
if value.property_ == "scene"
|
||||
]
|
||||
)
|
||||
|
||||
# Handle scene activation value notification event triggers
|
||||
triggers.extend(
|
||||
[
|
||||
{
|
||||
**base_trigger,
|
||||
CONF_TYPE: SCENE_ACTIVATION_VALUE_NOTIFICATION,
|
||||
ATTR_PROPERTY: value.property_,
|
||||
ATTR_PROPERTY_KEY: value.property_key,
|
||||
ATTR_ENDPOINT: value.endpoint,
|
||||
ATTR_COMMAND_CLASS: CommandClass.SCENE_ACTIVATION,
|
||||
CONF_SUBTYPE: f"Endpoint {value.endpoint}",
|
||||
}
|
||||
for value in node.get_command_class_values(
|
||||
CommandClass.SCENE_ACTIVATION
|
||||
).values()
|
||||
if value.property_ == "sceneId"
|
||||
]
|
||||
)
|
||||
|
||||
# Handle basic value notification event triggers
|
||||
# Nodes will only send Basic CC value notifications if a compatibility flag is set
|
||||
if node.device_config.compat.get("treatBasicSetAsEvent", False):
|
||||
triggers.extend(
|
||||
[
|
||||
{
|
||||
**base_trigger,
|
||||
CONF_TYPE: BASIC_VALUE_NOTIFICATION,
|
||||
ATTR_PROPERTY: value.property_,
|
||||
ATTR_PROPERTY_KEY: value.property_key,
|
||||
ATTR_ENDPOINT: value.endpoint,
|
||||
ATTR_COMMAND_CLASS: CommandClass.BASIC,
|
||||
CONF_SUBTYPE: f"Endpoint {value.endpoint}",
|
||||
}
|
||||
for value in node.get_command_class_values(CommandClass.BASIC).values()
|
||||
if value.property_ == "event"
|
||||
]
|
||||
)
|
||||
|
||||
return triggers
|
||||
|
||||
|
||||
def copy_available_params(
|
||||
input_dict: dict, output_dict: dict, params: list[str]
|
||||
) -> None:
|
||||
"""Copy available params from input into output."""
|
||||
for param in params:
|
||||
if (val := input_dict.get(param)) not in ("", None):
|
||||
output_dict[param] = val
|
||||
|
||||
|
||||
async def async_attach_trigger(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
action: AutomationActionType,
|
||||
automation_info: dict,
|
||||
) -> CALLBACK_TYPE:
|
||||
"""Attach a trigger."""
|
||||
trigger_type = config[CONF_TYPE]
|
||||
trigger_platform = trigger_type.split(".")[0]
|
||||
|
||||
event_data = {CONF_DEVICE_ID: config[CONF_DEVICE_ID]}
|
||||
event_config = {
|
||||
event.CONF_PLATFORM: "event",
|
||||
event.CONF_EVENT_DATA: event_data,
|
||||
}
|
||||
|
||||
if ATTR_COMMAND_CLASS in config:
|
||||
event_data[ATTR_COMMAND_CLASS] = config[ATTR_COMMAND_CLASS]
|
||||
|
||||
# Take input data from automation trigger UI and add it to the trigger we are
|
||||
# attaching to
|
||||
if trigger_platform == "event":
|
||||
if trigger_type == ENTRY_CONTROL_NOTIFICATION:
|
||||
event_config[event.CONF_EVENT_TYPE] = ZWAVE_JS_NOTIFICATION_EVENT
|
||||
copy_available_params(config, event_data, [ATTR_EVENT_TYPE, ATTR_DATA_TYPE])
|
||||
elif trigger_type == NOTIFICATION_NOTIFICATION:
|
||||
event_config[event.CONF_EVENT_TYPE] = ZWAVE_JS_NOTIFICATION_EVENT
|
||||
copy_available_params(
|
||||
config, event_data, [ATTR_LABEL, ATTR_EVENT_LABEL, ATTR_EVENT]
|
||||
)
|
||||
if (val := config.get(f"{ATTR_TYPE}.")) not in ("", None):
|
||||
event_data[ATTR_TYPE] = val
|
||||
elif trigger_type in (
|
||||
BASIC_VALUE_NOTIFICATION,
|
||||
CENTRAL_SCENE_VALUE_NOTIFICATION,
|
||||
SCENE_ACTIVATION_VALUE_NOTIFICATION,
|
||||
):
|
||||
event_config[event.CONF_EVENT_TYPE] = ZWAVE_JS_VALUE_NOTIFICATION_EVENT
|
||||
copy_available_params(
|
||||
config, event_data, [ATTR_PROPERTY, ATTR_PROPERTY_KEY, ATTR_ENDPOINT]
|
||||
)
|
||||
event_data[ATTR_VALUE_RAW] = config[ATTR_VALUE]
|
||||
else:
|
||||
raise HomeAssistantError(f"Unhandled trigger type {trigger_type}")
|
||||
|
||||
event_config = event.TRIGGER_SCHEMA(event_config)
|
||||
return await event.async_attach_trigger(
|
||||
hass, event_config, action, automation_info, platform_type="device"
|
||||
)
|
||||
|
||||
state_config = {state.CONF_PLATFORM: "state"}
|
||||
|
||||
if trigger_platform == "state" and trigger_type == NODE_STATUS:
|
||||
state_config[state.CONF_ENTITY_ID] = config[CONF_ENTITY_ID]
|
||||
copy_available_params(
|
||||
config, state_config, [state.CONF_FOR, state.CONF_FROM, state.CONF_TO]
|
||||
)
|
||||
|
||||
state_config = state.TRIGGER_SCHEMA(state_config)
|
||||
return await state.async_attach_trigger(
|
||||
hass, state_config, action, automation_info, platform_type="device"
|
||||
)
|
||||
|
||||
raise HomeAssistantError(f"Unhandled trigger type {trigger_type}")
|
||||
|
||||
|
||||
async def async_get_trigger_capabilities(
|
||||
hass: HomeAssistant, config: ConfigType
|
||||
) -> dict[str, vol.Schema]:
|
||||
"""List trigger capabilities."""
|
||||
node = async_get_node_from_device_id(hass, config[CONF_DEVICE_ID])
|
||||
value = (
|
||||
get_zwave_value_from_config(node, config) if ATTR_PROPERTY in config else None
|
||||
)
|
||||
# Add additional fields to the automation trigger UI
|
||||
if config[CONF_TYPE] == NOTIFICATION_NOTIFICATION:
|
||||
return {
|
||||
"extra_fields": vol.Schema(
|
||||
{
|
||||
vol.Optional(f"{ATTR_TYPE}."): cv.string,
|
||||
vol.Optional(ATTR_LABEL): cv.string,
|
||||
vol.Optional(ATTR_EVENT): cv.string,
|
||||
vol.Optional(ATTR_EVENT_LABEL): cv.string,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if config[CONF_TYPE] == ENTRY_CONTROL_NOTIFICATION:
|
||||
return {
|
||||
"extra_fields": vol.Schema(
|
||||
{
|
||||
vol.Optional(ATTR_EVENT_TYPE): cv.string,
|
||||
vol.Optional(ATTR_DATA_TYPE): cv.string,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if config[CONF_TYPE] == NODE_STATUS:
|
||||
return {
|
||||
"extra_fields": vol.Schema(
|
||||
{
|
||||
vol.Optional(state.CONF_FROM): vol.In(NODE_STATUSES),
|
||||
vol.Optional(state.CONF_TO): vol.In(NODE_STATUSES),
|
||||
vol.Optional(state.CONF_FOR): cv.positive_time_period_dict,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if config[CONF_TYPE] in (
|
||||
BASIC_VALUE_NOTIFICATION,
|
||||
CENTRAL_SCENE_VALUE_NOTIFICATION,
|
||||
SCENE_ACTIVATION_VALUE_NOTIFICATION,
|
||||
):
|
||||
if value.metadata.states:
|
||||
value_schema = vol.In({int(k): v for k, v in value.metadata.states.items()})
|
||||
else:
|
||||
value_schema = vol.All(
|
||||
vol.Coerce(int),
|
||||
vol.Range(min=value.metadata.min, max=value.metadata.max),
|
||||
)
|
||||
|
||||
return {"extra_fields": vol.Schema({vol.Required(ATTR_VALUE): value_schema})}
|
||||
|
||||
return {}
|
@ -8,9 +8,11 @@ from zwave_js_server.client import Client as ZwaveClient
|
||||
from zwave_js_server.model.node import Node as ZwaveNode
|
||||
from zwave_js_server.model.value import Value as ZwaveValue, get_value_id
|
||||
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import __version__ as HA_VERSION
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.device_registry import (
|
||||
DeviceRegistry,
|
||||
async_get as async_get_dev_reg,
|
||||
@ -175,3 +177,35 @@ def get_zwave_value_from_config(node: ZwaveNode, config: ConfigType) -> ZwaveVal
|
||||
if value_id not in node.values:
|
||||
raise vol.Invalid(f"Value {value_id} can't be found on node {node}")
|
||||
return node.values[value_id]
|
||||
|
||||
|
||||
@callback
|
||||
def async_get_node_status_sensor_entity_id(
|
||||
hass: HomeAssistant,
|
||||
device_id: str,
|
||||
ent_reg: EntityRegistry | None = None,
|
||||
dev_reg: DeviceRegistry | None = None,
|
||||
) -> str:
|
||||
"""Get the node status sensor entity ID for a given Z-Wave JS device."""
|
||||
if not ent_reg:
|
||||
ent_reg = async_get_ent_reg(hass)
|
||||
if not dev_reg:
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = dev_reg.async_get(device_id)
|
||||
if not device:
|
||||
raise HomeAssistantError("Invalid Device ID provided")
|
||||
|
||||
entry_id = next(entry_id for entry_id in device.config_entries)
|
||||
client = hass.data[DOMAIN][entry_id][DATA_CLIENT]
|
||||
node = async_get_node_from_device_id(hass, device_id, dev_reg)
|
||||
entity_id = ent_reg.async_get_entity_id(
|
||||
SENSOR_DOMAIN,
|
||||
DOMAIN,
|
||||
f"{client.driver.controller.home_id}.{node.node_id}.node_status",
|
||||
)
|
||||
if not entity_id:
|
||||
raise HomeAssistantError(
|
||||
"Node status sensor entity not found. Device may not be a zwave_js device"
|
||||
)
|
||||
|
||||
return entity_id
|
||||
|
@ -99,6 +99,14 @@
|
||||
}
|
||||
},
|
||||
"device_automation": {
|
||||
"trigger_type": {
|
||||
"event.notification.entry_control": "Sent an Entry Control notification",
|
||||
"event.notification.notification": "Sent a notification",
|
||||
"event.value_notification.basic": "Basic CC event on {subtype}",
|
||||
"event.value_notification.central_scene": "Central Scene action on {subtype}",
|
||||
"event.value_notification.scene_activation": "Scene Activation on {subtype}",
|
||||
"state.node_status": "Node status changed"
|
||||
},
|
||||
"condition_type": {
|
||||
"node_status": "Node status",
|
||||
"config_parameter": "Config parameter {subtype} value",
|
||||
|
@ -56,6 +56,14 @@
|
||||
"config_parameter": "Config parameter {subtype} value",
|
||||
"node_status": "Node status",
|
||||
"value": "Current value of a Z-Wave Value"
|
||||
},
|
||||
"trigger_type": {
|
||||
"event.notification.entry_control": "Sent an Entry Control notification",
|
||||
"event.notification.notification": "Sent a notification",
|
||||
"event.value_notification.basic": "Basic CC event on {subtype}",
|
||||
"event.value_notification.central_scene": "Central Scene action on {subtype}",
|
||||
"event.value_notification.scene_activation": "Scene Activation on {subtype}",
|
||||
"state.node_status": "Node status changed"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -429,6 +429,12 @@ def wallmote_central_scene_state_fixture():
|
||||
return json.loads(load_fixture("zwave_js/wallmote_central_scene_state.json"))
|
||||
|
||||
|
||||
@pytest.fixture(name="ge_in_wall_dimmer_switch_state", scope="session")
|
||||
def ge_in_wall_dimmer_switch_state_fixture():
|
||||
"""Load the ge in-wall dimmer switch node state fixture data."""
|
||||
return json.loads(load_fixture("zwave_js/ge_in_wall_dimmer_switch_state.json"))
|
||||
|
||||
|
||||
@pytest.fixture(name="aeotec_zw164_siren_state", scope="session")
|
||||
def aeotec_zw164_siren_state_fixture():
|
||||
"""Load the aeotec zw164 siren node state fixture data."""
|
||||
@ -795,6 +801,14 @@ def wallmote_central_scene_fixture(client, wallmote_central_scene_state):
|
||||
return node
|
||||
|
||||
|
||||
@pytest.fixture(name="ge_in_wall_dimmer_switch")
|
||||
def ge_in_wall_dimmer_switch_fixture(client, ge_in_wall_dimmer_switch_state):
|
||||
"""Mock a ge in-wall dimmer switch scene node."""
|
||||
node = Node(client, copy.deepcopy(ge_in_wall_dimmer_switch_state))
|
||||
client.driver.controller.nodes[node.node_id] = node
|
||||
return node
|
||||
|
||||
|
||||
@pytest.fixture(name="aeotec_zw164_siren")
|
||||
def aeotec_zw164_siren_fixture(client, aeotec_zw164_siren_state):
|
||||
"""Mock a wallmote central scene node."""
|
||||
|
831
tests/components/zwave_js/test_device_trigger.py
Normal file
831
tests/components/zwave_js/test_device_trigger.py
Normal file
@ -0,0 +1,831 @@
|
||||
"""The tests for Z-Wave JS device triggers."""
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
import voluptuous_serialize
|
||||
from zwave_js_server.const import CommandClass
|
||||
from zwave_js_server.event import Event
|
||||
from zwave_js_server.model.node import Node
|
||||
|
||||
from homeassistant.components import automation
|
||||
from homeassistant.components.zwave_js import DOMAIN, device_trigger
|
||||
from homeassistant.components.zwave_js.device_trigger import (
|
||||
async_attach_trigger,
|
||||
async_get_trigger_capabilities,
|
||||
)
|
||||
from homeassistant.components.zwave_js.helpers import (
|
||||
async_get_node_status_sensor_entity_id,
|
||||
)
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.device_registry import (
|
||||
async_entries_for_config_entry,
|
||||
async_get as async_get_dev_reg,
|
||||
)
|
||||
from homeassistant.helpers.entity_registry import async_get as async_get_ent_reg
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import (
|
||||
assert_lists_same,
|
||||
async_get_device_automations,
|
||||
async_mock_service,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def calls(hass):
|
||||
"""Track calls to a mock service."""
|
||||
return async_mock_service(hass, "test", "automation")
|
||||
|
||||
|
||||
async def test_get_notification_notification_triggers(
|
||||
hass, client, lock_schlage_be469, integration
|
||||
):
|
||||
"""Test we get the expected triggers from a zwave_js device with the Notification CC."""
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
expected_trigger = {
|
||||
"platform": "device",
|
||||
"domain": DOMAIN,
|
||||
"type": "event.notification.notification",
|
||||
"device_id": device.id,
|
||||
"command_class": CommandClass.NOTIFICATION,
|
||||
}
|
||||
triggers = await async_get_device_automations(hass, "trigger", device.id)
|
||||
assert expected_trigger in triggers
|
||||
|
||||
|
||||
async def test_if_notification_notification_fires(
|
||||
hass, client, lock_schlage_be469, integration, calls
|
||||
):
|
||||
"""Test for event.notification.notification trigger firing."""
|
||||
node: Node = lock_schlage_be469
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
automation.DOMAIN,
|
||||
{
|
||||
automation.DOMAIN: [
|
||||
{
|
||||
"trigger": {
|
||||
"platform": "device",
|
||||
"domain": DOMAIN,
|
||||
"device_id": device.id,
|
||||
"type": "event.notification.notification",
|
||||
"command_class": CommandClass.NOTIFICATION.value,
|
||||
"type.": 6,
|
||||
"event": 5,
|
||||
"label": "Access Control",
|
||||
},
|
||||
"action": {
|
||||
"service": "test.automation",
|
||||
"data_template": {
|
||||
"some": (
|
||||
"event.notification.notification - "
|
||||
"{{ trigger.platform}} - "
|
||||
"{{ trigger.event.event_type}} - "
|
||||
"{{ trigger.event.data.command_class }}"
|
||||
)
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
# Publish fake Notification CC notification
|
||||
event = Event(
|
||||
type="notification",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "notification",
|
||||
"nodeId": node.node_id,
|
||||
"ccId": 113,
|
||||
"args": {
|
||||
"type": 6,
|
||||
"event": 5,
|
||||
"label": "Access Control",
|
||||
"eventLabel": "Keypad lock operation",
|
||||
"parameters": {"userId": 1},
|
||||
},
|
||||
},
|
||||
)
|
||||
node.receive_event(event)
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
assert calls[0].data[
|
||||
"some"
|
||||
] == "event.notification.notification - device - zwave_js_notification - {}".format(
|
||||
CommandClass.NOTIFICATION
|
||||
)
|
||||
|
||||
|
||||
async def test_get_trigger_capabilities_notification_notification(
|
||||
hass, client, lock_schlage_be469, integration
|
||||
):
|
||||
"""Test we get the expected capabilities from a notification.notification trigger."""
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
capabilities = await device_trigger.async_get_trigger_capabilities(
|
||||
hass,
|
||||
{
|
||||
"platform": "device",
|
||||
"domain": DOMAIN,
|
||||
"device_id": device.id,
|
||||
"type": "event.notification.notification",
|
||||
"command_class": CommandClass.NOTIFICATION.value,
|
||||
},
|
||||
)
|
||||
assert capabilities and "extra_fields" in capabilities
|
||||
|
||||
assert_lists_same(
|
||||
voluptuous_serialize.convert(
|
||||
capabilities["extra_fields"], custom_serializer=cv.custom_serializer
|
||||
),
|
||||
[
|
||||
{"name": "type.", "optional": True, "type": "string"},
|
||||
{"name": "label", "optional": True, "type": "string"},
|
||||
{"name": "event", "optional": True, "type": "string"},
|
||||
{"name": "event_label", "optional": True, "type": "string"},
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
async def test_if_entry_control_notification_fires(
|
||||
hass, client, lock_schlage_be469, integration, calls
|
||||
):
|
||||
"""Test for notification.entry_control trigger firing."""
|
||||
node: Node = lock_schlage_be469
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
automation.DOMAIN,
|
||||
{
|
||||
automation.DOMAIN: [
|
||||
{
|
||||
"trigger": {
|
||||
"platform": "device",
|
||||
"domain": DOMAIN,
|
||||
"device_id": device.id,
|
||||
"type": "event.notification.entry_control",
|
||||
"command_class": CommandClass.ENTRY_CONTROL.value,
|
||||
"event_type": 5,
|
||||
"data_type": 2,
|
||||
},
|
||||
"action": {
|
||||
"service": "test.automation",
|
||||
"data_template": {
|
||||
"some": (
|
||||
"event.notification.notification - "
|
||||
"{{ trigger.platform}} - "
|
||||
"{{ trigger.event.event_type}} - "
|
||||
"{{ trigger.event.data.command_class }}"
|
||||
)
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
# Publish fake Entry Control CC notification
|
||||
event = Event(
|
||||
type="notification",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "notification",
|
||||
"nodeId": node.node_id,
|
||||
"ccId": 111,
|
||||
"args": {"eventType": 5, "dataType": 2, "eventData": "555"},
|
||||
},
|
||||
)
|
||||
node.receive_event(event)
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
assert calls[0].data[
|
||||
"some"
|
||||
] == "event.notification.notification - device - zwave_js_notification - {}".format(
|
||||
CommandClass.ENTRY_CONTROL
|
||||
)
|
||||
|
||||
|
||||
async def test_get_trigger_capabilities_entry_control_notification(
|
||||
hass, client, lock_schlage_be469, integration
|
||||
):
|
||||
"""Test we get the expected capabilities from a notification.entry_control trigger."""
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
capabilities = await device_trigger.async_get_trigger_capabilities(
|
||||
hass,
|
||||
{
|
||||
"platform": "device",
|
||||
"domain": DOMAIN,
|
||||
"device_id": device.id,
|
||||
"type": "event.notification.entry_control",
|
||||
"command_class": CommandClass.ENTRY_CONTROL.value,
|
||||
},
|
||||
)
|
||||
assert capabilities and "extra_fields" in capabilities
|
||||
|
||||
assert_lists_same(
|
||||
voluptuous_serialize.convert(
|
||||
capabilities["extra_fields"], custom_serializer=cv.custom_serializer
|
||||
),
|
||||
[
|
||||
{"name": "event_type", "optional": True, "type": "string"},
|
||||
{"name": "data_type", "optional": True, "type": "string"},
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
async def test_get_node_status_triggers(hass, client, lock_schlage_be469, integration):
|
||||
"""Test we get the expected triggers from a device with node status sensor enabled."""
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
ent_reg = async_get_ent_reg(hass)
|
||||
entity_id = async_get_node_status_sensor_entity_id(
|
||||
hass, device.id, ent_reg, dev_reg
|
||||
)
|
||||
ent_reg.async_update_entity(entity_id, **{"disabled_by": None})
|
||||
await hass.config_entries.async_reload(integration.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
expected_trigger = {
|
||||
"platform": "device",
|
||||
"domain": DOMAIN,
|
||||
"type": "state.node_status",
|
||||
"device_id": device.id,
|
||||
"entity_id": entity_id,
|
||||
}
|
||||
triggers = await async_get_device_automations(hass, "trigger", device.id)
|
||||
assert expected_trigger in triggers
|
||||
|
||||
|
||||
async def test_if_node_status_change_fires(
|
||||
hass, client, lock_schlage_be469, integration, calls
|
||||
):
|
||||
"""Test for node_status trigger firing."""
|
||||
node: Node = lock_schlage_be469
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
ent_reg = async_get_ent_reg(hass)
|
||||
entity_id = async_get_node_status_sensor_entity_id(
|
||||
hass, device.id, ent_reg, dev_reg
|
||||
)
|
||||
ent_reg.async_update_entity(entity_id, **{"disabled_by": None})
|
||||
await hass.config_entries.async_reload(integration.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
automation.DOMAIN,
|
||||
{
|
||||
automation.DOMAIN: [
|
||||
{
|
||||
"trigger": {
|
||||
"platform": "device",
|
||||
"domain": DOMAIN,
|
||||
"device_id": device.id,
|
||||
"entity_id": entity_id,
|
||||
"type": "state.node_status",
|
||||
"from": "alive",
|
||||
},
|
||||
"action": {
|
||||
"service": "test.automation",
|
||||
"data_template": {
|
||||
"some": (
|
||||
"state.node_status - "
|
||||
"{{ trigger.platform}} - "
|
||||
"{{ trigger.from_state.state }}"
|
||||
)
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
# Test status change
|
||||
event = Event(
|
||||
"dead", data={"source": "node", "event": "dead", "nodeId": node.node_id}
|
||||
)
|
||||
node.receive_event(event)
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
assert calls[0].data["some"] == "state.node_status - device - alive"
|
||||
|
||||
|
||||
async def test_get_trigger_capabilities_node_status(
|
||||
hass, client, lock_schlage_be469, integration
|
||||
):
|
||||
"""Test we get the expected capabilities from a node_status trigger."""
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
ent_reg = async_get_ent_reg(hass)
|
||||
entity_id = async_get_node_status_sensor_entity_id(
|
||||
hass, device.id, ent_reg, dev_reg
|
||||
)
|
||||
ent_reg.async_update_entity(entity_id, **{"disabled_by": None})
|
||||
await hass.config_entries.async_reload(integration.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
capabilities = await device_trigger.async_get_trigger_capabilities(
|
||||
hass,
|
||||
{
|
||||
"platform": "device",
|
||||
"domain": DOMAIN,
|
||||
"device_id": device.id,
|
||||
"entity_id": entity_id,
|
||||
"type": "state.node_status",
|
||||
},
|
||||
)
|
||||
assert capabilities and "extra_fields" in capabilities
|
||||
|
||||
assert voluptuous_serialize.convert(
|
||||
capabilities["extra_fields"], custom_serializer=cv.custom_serializer
|
||||
) == [
|
||||
{
|
||||
"name": "from",
|
||||
"optional": True,
|
||||
"options": [
|
||||
("asleep", "asleep"),
|
||||
("awake", "awake"),
|
||||
("dead", "dead"),
|
||||
("alive", "alive"),
|
||||
],
|
||||
"type": "select",
|
||||
},
|
||||
{
|
||||
"name": "to",
|
||||
"optional": True,
|
||||
"options": [
|
||||
("asleep", "asleep"),
|
||||
("awake", "awake"),
|
||||
("dead", "dead"),
|
||||
("alive", "alive"),
|
||||
],
|
||||
"type": "select",
|
||||
},
|
||||
{"name": "for", "optional": True, "type": "positive_time_period_dict"},
|
||||
]
|
||||
|
||||
|
||||
async def test_get_basic_value_notification_triggers(
|
||||
hass, client, ge_in_wall_dimmer_switch, integration
|
||||
):
|
||||
"""Test we get the expected triggers from a zwave_js device with the Basic CC."""
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
expected_trigger = {
|
||||
"platform": "device",
|
||||
"domain": DOMAIN,
|
||||
"type": "event.value_notification.basic",
|
||||
"device_id": device.id,
|
||||
"command_class": CommandClass.BASIC,
|
||||
"property": "event",
|
||||
"property_key": None,
|
||||
"endpoint": 0,
|
||||
"subtype": "Endpoint 0",
|
||||
}
|
||||
triggers = await async_get_device_automations(hass, "trigger", device.id)
|
||||
assert expected_trigger in triggers
|
||||
|
||||
|
||||
async def test_if_basic_value_notification_fires(
|
||||
hass, client, ge_in_wall_dimmer_switch, integration, calls
|
||||
):
|
||||
"""Test for event.value_notification.basic trigger firing."""
|
||||
node: Node = ge_in_wall_dimmer_switch
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
automation.DOMAIN,
|
||||
{
|
||||
automation.DOMAIN: [
|
||||
{
|
||||
"trigger": {
|
||||
"platform": "device",
|
||||
"domain": DOMAIN,
|
||||
"type": "event.value_notification.basic",
|
||||
"device_id": device.id,
|
||||
"command_class": CommandClass.BASIC.value,
|
||||
"property": "event",
|
||||
"property_key": None,
|
||||
"endpoint": 0,
|
||||
"subtype": "Endpoint 0",
|
||||
"value": 0,
|
||||
},
|
||||
"action": {
|
||||
"service": "test.automation",
|
||||
"data_template": {
|
||||
"some": (
|
||||
"event.value_notification.basic - "
|
||||
"{{ trigger.platform}} - "
|
||||
"{{ trigger.event.event_type}} - "
|
||||
"{{ trigger.event.data.command_class }}"
|
||||
)
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
# Publish fake Basic CC value notification
|
||||
event = Event(
|
||||
type="value notification",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value notification",
|
||||
"nodeId": node.node_id,
|
||||
"args": {
|
||||
"commandClassName": "Basic",
|
||||
"commandClass": 32,
|
||||
"endpoint": 0,
|
||||
"property": "event",
|
||||
"propertyName": "event",
|
||||
"value": 0,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": True,
|
||||
"writeable": False,
|
||||
"label": "Event value",
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
},
|
||||
"ccVersion": 1,
|
||||
},
|
||||
},
|
||||
)
|
||||
node.receive_event(event)
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
assert calls[0].data[
|
||||
"some"
|
||||
] == "event.value_notification.basic - device - zwave_js_value_notification - {}".format(
|
||||
CommandClass.BASIC
|
||||
)
|
||||
|
||||
|
||||
async def test_get_trigger_capabilities_basic_value_notification(
|
||||
hass, client, ge_in_wall_dimmer_switch, integration
|
||||
):
|
||||
"""Test we get the expected capabilities from a value_notification.basic trigger."""
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
capabilities = await device_trigger.async_get_trigger_capabilities(
|
||||
hass,
|
||||
{
|
||||
"platform": "device",
|
||||
"domain": DOMAIN,
|
||||
"type": "event.value_notification.basic",
|
||||
"device_id": device.id,
|
||||
"command_class": CommandClass.BASIC.value,
|
||||
"property": "event",
|
||||
"property_key": None,
|
||||
"endpoint": 0,
|
||||
"subtype": "Endpoint 0",
|
||||
},
|
||||
)
|
||||
assert capabilities and "extra_fields" in capabilities
|
||||
|
||||
assert voluptuous_serialize.convert(
|
||||
capabilities["extra_fields"], custom_serializer=cv.custom_serializer
|
||||
) == [
|
||||
{
|
||||
"name": "value",
|
||||
"required": True,
|
||||
"type": "integer",
|
||||
"valueMin": 0,
|
||||
"valueMax": 255,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
async def test_get_central_scene_value_notification_triggers(
|
||||
hass, client, wallmote_central_scene, integration
|
||||
):
|
||||
"""Test we get the expected triggers from a zwave_js device with the Central Scene CC."""
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
expected_trigger = {
|
||||
"platform": "device",
|
||||
"domain": DOMAIN,
|
||||
"type": "event.value_notification.central_scene",
|
||||
"device_id": device.id,
|
||||
"command_class": CommandClass.CENTRAL_SCENE,
|
||||
"property": "scene",
|
||||
"property_key": "001",
|
||||
"endpoint": 0,
|
||||
"subtype": "Endpoint 0 Scene 001",
|
||||
}
|
||||
triggers = await async_get_device_automations(hass, "trigger", device.id)
|
||||
assert expected_trigger in triggers
|
||||
|
||||
|
||||
async def test_if_central_scene_value_notification_fires(
|
||||
hass, client, wallmote_central_scene, integration, calls
|
||||
):
|
||||
"""Test for event.value_notification.central_scene trigger firing."""
|
||||
node: Node = wallmote_central_scene
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
automation.DOMAIN,
|
||||
{
|
||||
automation.DOMAIN: [
|
||||
{
|
||||
"trigger": {
|
||||
"platform": "device",
|
||||
"domain": DOMAIN,
|
||||
"device_id": device.id,
|
||||
"type": "event.value_notification.central_scene",
|
||||
"command_class": CommandClass.CENTRAL_SCENE.value,
|
||||
"property": "scene",
|
||||
"property_key": "001",
|
||||
"endpoint": 0,
|
||||
"subtype": "Endpoint 0 Scene 001",
|
||||
"value": 0,
|
||||
},
|
||||
"action": {
|
||||
"service": "test.automation",
|
||||
"data_template": {
|
||||
"some": (
|
||||
"event.value_notification.central_scene - "
|
||||
"{{ trigger.platform}} - "
|
||||
"{{ trigger.event.event_type}} - "
|
||||
"{{ trigger.event.data.command_class }}"
|
||||
)
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
# Publish fake Central Scene CC value notification
|
||||
event = Event(
|
||||
type="value notification",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value notification",
|
||||
"nodeId": node.node_id,
|
||||
"args": {
|
||||
"commandClassName": "Central Scene",
|
||||
"commandClass": 91,
|
||||
"endpoint": 0,
|
||||
"property": "scene",
|
||||
"propertyName": "scene",
|
||||
"propertyKey": "001",
|
||||
"propertyKey": "001",
|
||||
"value": 0,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": True,
|
||||
"writeable": False,
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"label": "Scene 004",
|
||||
"states": {
|
||||
"0": "KeyPressed",
|
||||
"1": "KeyReleased",
|
||||
"2": "KeyHeldDown",
|
||||
},
|
||||
},
|
||||
"ccVersion": 1,
|
||||
},
|
||||
},
|
||||
)
|
||||
node.receive_event(event)
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
assert calls[0].data[
|
||||
"some"
|
||||
] == "event.value_notification.central_scene - device - zwave_js_value_notification - {}".format(
|
||||
CommandClass.CENTRAL_SCENE
|
||||
)
|
||||
|
||||
|
||||
async def test_get_trigger_capabilities_central_scene_value_notification(
|
||||
hass, client, wallmote_central_scene, integration
|
||||
):
|
||||
"""Test we get the expected capabilities from a value_notification.central_scene trigger."""
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
capabilities = await device_trigger.async_get_trigger_capabilities(
|
||||
hass,
|
||||
{
|
||||
"platform": "device",
|
||||
"domain": DOMAIN,
|
||||
"type": "event.value_notification.central_scene",
|
||||
"device_id": device.id,
|
||||
"command_class": CommandClass.CENTRAL_SCENE.value,
|
||||
"property": "scene",
|
||||
"property_key": "001",
|
||||
"endpoint": 0,
|
||||
"subtype": "Endpoint 0 Scene 001",
|
||||
},
|
||||
)
|
||||
assert capabilities and "extra_fields" in capabilities
|
||||
|
||||
assert voluptuous_serialize.convert(
|
||||
capabilities["extra_fields"], custom_serializer=cv.custom_serializer
|
||||
) == [
|
||||
{
|
||||
"name": "value",
|
||||
"required": True,
|
||||
"type": "select",
|
||||
"options": [(0, "KeyPressed"), (1, "KeyReleased"), (2, "KeyHeldDown")],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
async def test_get_scene_activation_value_notification_triggers(
|
||||
hass, client, hank_binary_switch, integration
|
||||
):
|
||||
"""Test we get the expected triggers from a zwave_js device with the SceneActivation CC."""
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
expected_trigger = {
|
||||
"platform": "device",
|
||||
"domain": DOMAIN,
|
||||
"type": "event.value_notification.scene_activation",
|
||||
"device_id": device.id,
|
||||
"command_class": CommandClass.SCENE_ACTIVATION.value,
|
||||
"property": "sceneId",
|
||||
"property_key": None,
|
||||
"endpoint": 0,
|
||||
"subtype": "Endpoint 0",
|
||||
}
|
||||
triggers = await async_get_device_automations(hass, "trigger", device.id)
|
||||
assert expected_trigger in triggers
|
||||
|
||||
|
||||
async def test_if_scene_activation_value_notification_fires(
|
||||
hass, client, hank_binary_switch, integration, calls
|
||||
):
|
||||
"""Test for event.value_notification.scene_activation trigger firing."""
|
||||
node: Node = hank_binary_switch
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
automation.DOMAIN,
|
||||
{
|
||||
automation.DOMAIN: [
|
||||
{
|
||||
"trigger": {
|
||||
"platform": "device",
|
||||
"domain": DOMAIN,
|
||||
"device_id": device.id,
|
||||
"type": "event.value_notification.scene_activation",
|
||||
"command_class": CommandClass.SCENE_ACTIVATION.value,
|
||||
"property": "sceneId",
|
||||
"property_key": None,
|
||||
"endpoint": 0,
|
||||
"subtype": "Endpoint 0",
|
||||
"value": 1,
|
||||
},
|
||||
"action": {
|
||||
"service": "test.automation",
|
||||
"data_template": {
|
||||
"some": (
|
||||
"event.value_notification.scene_activation - "
|
||||
"{{ trigger.platform}} - "
|
||||
"{{ trigger.event.event_type}} - "
|
||||
"{{ trigger.event.data.command_class }}"
|
||||
)
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
# Publish fake Scene Activation CC value notification
|
||||
event = Event(
|
||||
type="value notification",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value notification",
|
||||
"nodeId": node.node_id,
|
||||
"args": {
|
||||
"commandClassName": "Scene Activation",
|
||||
"commandClass": 43,
|
||||
"endpoint": 0,
|
||||
"property": "sceneId",
|
||||
"propertyName": "sceneId",
|
||||
"value": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": True,
|
||||
"writeable": True,
|
||||
"min": 1,
|
||||
"max": 255,
|
||||
"label": "Scene ID",
|
||||
},
|
||||
"ccVersion": 1,
|
||||
},
|
||||
},
|
||||
)
|
||||
node.receive_event(event)
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
assert calls[0].data[
|
||||
"some"
|
||||
] == "event.value_notification.scene_activation - device - zwave_js_value_notification - {}".format(
|
||||
CommandClass.SCENE_ACTIVATION
|
||||
)
|
||||
|
||||
|
||||
async def test_get_trigger_capabilities_scene_activation_value_notification(
|
||||
hass, client, hank_binary_switch, integration
|
||||
):
|
||||
"""Test we get the expected capabilities from a value_notification.scene_activation trigger."""
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
capabilities = await device_trigger.async_get_trigger_capabilities(
|
||||
hass,
|
||||
{
|
||||
"platform": "device",
|
||||
"domain": DOMAIN,
|
||||
"type": "event.value_notification.scene_activation",
|
||||
"device_id": device.id,
|
||||
"command_class": CommandClass.SCENE_ACTIVATION.value,
|
||||
"property": "sceneId",
|
||||
"property_key": None,
|
||||
"endpoint": 0,
|
||||
"subtype": "Endpoint 0",
|
||||
},
|
||||
)
|
||||
assert capabilities and "extra_fields" in capabilities
|
||||
|
||||
assert voluptuous_serialize.convert(
|
||||
capabilities["extra_fields"], custom_serializer=cv.custom_serializer
|
||||
) == [
|
||||
{
|
||||
"name": "value",
|
||||
"required": True,
|
||||
"type": "integer",
|
||||
"valueMin": 1,
|
||||
"valueMax": 255,
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
async def test_failure_scenarios(hass, client, hank_binary_switch, integration):
|
||||
"""Test failure scenarios."""
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await async_attach_trigger(
|
||||
hass, {"type": "failed.test", "device_id": "invalid_device_id"}, None, {}
|
||||
)
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await async_attach_trigger(
|
||||
hass,
|
||||
{"type": "event.failed_type", "device_id": "invalid_device_id"},
|
||||
None,
|
||||
{},
|
||||
)
|
||||
|
||||
dev_reg = async_get_dev_reg(hass)
|
||||
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await async_attach_trigger(
|
||||
hass, {"type": "failed.test", "device_id": device.id}, None, {}
|
||||
)
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await async_attach_trigger(
|
||||
hass,
|
||||
{"type": "event.failed_type", "device_id": device.id},
|
||||
None,
|
||||
{},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.zwave_js.device_trigger.async_get_node_from_device_id",
|
||||
return_value=None,
|
||||
), patch(
|
||||
"homeassistant.components.zwave_js.helpers.get_zwave_value_from_config",
|
||||
return_value=None,
|
||||
):
|
||||
assert (
|
||||
await async_get_trigger_capabilities(
|
||||
hass, {"type": "failed.test", "device_id": "invalid_device_id"}
|
||||
)
|
||||
== {}
|
||||
)
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
async_get_node_status_sensor_entity_id(hass, "invalid_device_id")
|
642
tests/fixtures/zwave_js/ge_in_wall_dimmer_switch_state.json
vendored
Normal file
642
tests/fixtures/zwave_js/ge_in_wall_dimmer_switch_state.json
vendored
Normal file
@ -0,0 +1,642 @@
|
||||
{
|
||||
"nodeId": 2,
|
||||
"index": 0,
|
||||
"installerIcon": 1536,
|
||||
"userIcon": 1536,
|
||||
"status": 4,
|
||||
"ready": true,
|
||||
"isListening": true,
|
||||
"isRouting": true,
|
||||
"isSecure": false,
|
||||
"manufacturerId": 99,
|
||||
"productId": 12344,
|
||||
"productType": 18756,
|
||||
"firmwareVersion": "5.26",
|
||||
"zwavePlusVersion": 1,
|
||||
"name": "LivingRoomLight",
|
||||
"location": "LivingRoom",
|
||||
"deviceConfig": {
|
||||
"filename": "/opt/node_modules/@zwave-js/config/config/devices/0x0063/ge_14294_zw3005.json",
|
||||
"manufacturer": "GE/Jasco",
|
||||
"manufacturerId": 99,
|
||||
"label": "14294 / ZW3005",
|
||||
"description": "In-Wall Dimmer Switch",
|
||||
"devices": [
|
||||
{
|
||||
"productType": 18756,
|
||||
"productId": 12344
|
||||
}
|
||||
],
|
||||
"firmwareVersion": {
|
||||
"min": "0.0",
|
||||
"max": "255.255"
|
||||
},
|
||||
"associations": {},
|
||||
"paramInformation": {
|
||||
"_map": {}
|
||||
},
|
||||
"compat": {
|
||||
"valueIdRegex": {},
|
||||
"treatBasicSetAsEvent": true
|
||||
},
|
||||
"isEmbedded": true
|
||||
},
|
||||
"label": "14294 / ZW3005",
|
||||
"interviewAttempts": 0,
|
||||
"endpoints": [
|
||||
{
|
||||
"nodeId": 2,
|
||||
"index": 0,
|
||||
"installerIcon": 1536,
|
||||
"userIcon": 1536,
|
||||
"deviceClass": {
|
||||
"basic": {
|
||||
"key": 4,
|
||||
"label": "Routing Slave"
|
||||
},
|
||||
"generic": {
|
||||
"key": 17,
|
||||
"label": "Multilevel Switch"
|
||||
},
|
||||
"specific": {
|
||||
"key": 1,
|
||||
"label": "Multilevel Power Switch"
|
||||
},
|
||||
"mandatorySupportedCCs": [32, 38, 39],
|
||||
"mandatoryControlledCCs": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"values": [
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 32,
|
||||
"commandClassName": "Basic",
|
||||
"property": "currentValue",
|
||||
"propertyName": "currentValue",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Current value",
|
||||
"min": 0,
|
||||
"max": 99
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 32,
|
||||
"commandClassName": "Basic",
|
||||
"property": "targetValue",
|
||||
"propertyName": "targetValue",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"label": "Target value",
|
||||
"min": 0,
|
||||
"max": 99
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 32,
|
||||
"commandClassName": "Basic",
|
||||
"property": "event",
|
||||
"propertyName": "event",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Event value",
|
||||
"min": 0,
|
||||
"max": 255
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 38,
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"property": "targetValue",
|
||||
"propertyName": "targetValue",
|
||||
"ccVersion": 2,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"label": "Target value",
|
||||
"min": 0,
|
||||
"max": 99
|
||||
},
|
||||
"value": 255
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 38,
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"property": "duration",
|
||||
"propertyName": "duration",
|
||||
"ccVersion": 2,
|
||||
"metadata": {
|
||||
"type": "duration",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"label": "Transition duration"
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 38,
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"property": "currentValue",
|
||||
"propertyName": "currentValue",
|
||||
"ccVersion": 2,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Current value",
|
||||
"min": 0,
|
||||
"max": 99
|
||||
},
|
||||
"value": 35
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 38,
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"property": "Up",
|
||||
"propertyName": "Up",
|
||||
"ccVersion": 2,
|
||||
"metadata": {
|
||||
"type": "boolean",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"label": "Perform a level change (Up)",
|
||||
"ccSpecific": {
|
||||
"switchType": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 38,
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"property": "Down",
|
||||
"propertyName": "Down",
|
||||
"ccVersion": 2,
|
||||
"metadata": {
|
||||
"type": "boolean",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"label": "Perform a level change (Down)",
|
||||
"ccSpecific": {
|
||||
"switchType": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 43,
|
||||
"commandClassName": "Scene Activation",
|
||||
"property": "sceneId",
|
||||
"propertyName": "sceneId",
|
||||
"ccVersion": 0,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"label": "Scene ID",
|
||||
"min": 1,
|
||||
"max": 255
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 43,
|
||||
"commandClassName": "Scene Activation",
|
||||
"property": "dimmingDuration",
|
||||
"propertyName": "dimmingDuration",
|
||||
"ccVersion": 0,
|
||||
"metadata": {
|
||||
"type": "any",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"label": "Dimming duration"
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 112,
|
||||
"commandClassName": "Configuration",
|
||||
"property": 3,
|
||||
"propertyName": "Night Light",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"description": "Defines the behavior of the blue LED. Default is on when switch is off.",
|
||||
"label": "Night Light",
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 2,
|
||||
"states": {
|
||||
"0": "LED on when switch is OFF",
|
||||
"1": "LED on when switch is ON",
|
||||
"2": "LED always off"
|
||||
},
|
||||
"valueSize": 1,
|
||||
"format": 0,
|
||||
"allowManualEntry": false,
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 112,
|
||||
"commandClassName": "Configuration",
|
||||
"property": 4,
|
||||
"propertyName": "Invert Switch",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"description": "Invert the ON/OFF Switch State.",
|
||||
"label": "Invert Switch",
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 1,
|
||||
"states": {
|
||||
"0": "No",
|
||||
"1": "Yes"
|
||||
},
|
||||
"valueSize": 1,
|
||||
"format": 0,
|
||||
"allowManualEntry": false,
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 112,
|
||||
"commandClassName": "Configuration",
|
||||
"property": 7,
|
||||
"propertyName": "Dim Rate Steps (Z-Wave Command)",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"description": "Number of steps or levels",
|
||||
"label": "Dim Rate Steps (Z-Wave Command)",
|
||||
"default": 1,
|
||||
"min": 0,
|
||||
"max": 99,
|
||||
"valueSize": 1,
|
||||
"format": 1,
|
||||
"allowManualEntry": true,
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 112,
|
||||
"commandClassName": "Configuration",
|
||||
"property": 8,
|
||||
"propertyName": "Dim Rate Timing (Z-Wave)",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"description": "Timing of steps or levels",
|
||||
"label": "Dim Rate Timing (Z-Wave)",
|
||||
"default": 3,
|
||||
"min": 1,
|
||||
"max": 255,
|
||||
"unit": "10ms",
|
||||
"valueSize": 2,
|
||||
"format": 1,
|
||||
"allowManualEntry": true,
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 3
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 112,
|
||||
"commandClassName": "Configuration",
|
||||
"property": 9,
|
||||
"propertyName": "Dim Rate Steps (Manual)",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"description": "Number of steps or levels",
|
||||
"label": "Dim Rate Steps (Manual)",
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
"max": 99,
|
||||
"valueSize": 1,
|
||||
"format": 1,
|
||||
"allowManualEntry": true,
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 112,
|
||||
"commandClassName": "Configuration",
|
||||
"property": 10,
|
||||
"propertyName": "Dim Rate Timing (Manual)",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"description": "Timing of steps",
|
||||
"label": "Dim Rate Timing (Manual)",
|
||||
"default": 3,
|
||||
"min": 1,
|
||||
"max": 255,
|
||||
"unit": "10ms",
|
||||
"valueSize": 2,
|
||||
"format": 1,
|
||||
"allowManualEntry": true,
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 3
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 112,
|
||||
"commandClassName": "Configuration",
|
||||
"property": 11,
|
||||
"propertyName": "Dim Rate Steps (All-On/All-Off)",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"description": "Number of steps or levels",
|
||||
"label": "Dim Rate Steps (All-On/All-Off)",
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
"max": 99,
|
||||
"valueSize": 1,
|
||||
"format": 1,
|
||||
"allowManualEntry": true,
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 112,
|
||||
"commandClassName": "Configuration",
|
||||
"property": 12,
|
||||
"propertyName": "Dim Rate Timing (All-On/All-Off)",
|
||||
"ccVersion": 1,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"description": "Timing of steps or levels",
|
||||
"label": "Dim Rate Timing (All-On/All-Off)",
|
||||
"default": 3,
|
||||
"min": 1,
|
||||
"max": 255,
|
||||
"unit": "10ms",
|
||||
"valueSize": 2,
|
||||
"format": 1,
|
||||
"allowManualEntry": true,
|
||||
"isFromConfig": true
|
||||
},
|
||||
"value": 3
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 114,
|
||||
"commandClassName": "Manufacturer Specific",
|
||||
"property": "manufacturerId",
|
||||
"propertyName": "manufacturerId",
|
||||
"ccVersion": 2,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Manufacturer ID",
|
||||
"min": 0,
|
||||
"max": 65535
|
||||
},
|
||||
"value": 99
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 114,
|
||||
"commandClassName": "Manufacturer Specific",
|
||||
"property": "productType",
|
||||
"propertyName": "productType",
|
||||
"ccVersion": 2,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Product type",
|
||||
"min": 0,
|
||||
"max": 65535
|
||||
},
|
||||
"value": 18756
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 114,
|
||||
"commandClassName": "Manufacturer Specific",
|
||||
"property": "productId",
|
||||
"propertyName": "productId",
|
||||
"ccVersion": 2,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Product ID",
|
||||
"min": 0,
|
||||
"max": 65535
|
||||
},
|
||||
"value": 12344
|
||||
},
|
||||
{
|
||||
"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.34"
|
||||
},
|
||||
{
|
||||
"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": ["5.26"]
|
||||
},
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
],
|
||||
"isFrequentListening": false,
|
||||
"maxDataRate": 40000,
|
||||
"supportedDataRates": [40000],
|
||||
"protocolVersion": 3,
|
||||
"zwavePlusNodeType": 0,
|
||||
"zwavePlusRoleType": 5,
|
||||
"deviceClass": {
|
||||
"basic": {
|
||||
"key": 4,
|
||||
"label": "Routing Slave"
|
||||
},
|
||||
"generic": {
|
||||
"key": 17,
|
||||
"label": "Multilevel Switch"
|
||||
},
|
||||
"specific": {
|
||||
"key": 1,
|
||||
"label": "Multilevel Power Switch"
|
||||
},
|
||||
"mandatorySupportedCCs": [32, 38, 39],
|
||||
"mandatoryControlledCCs": []
|
||||
},
|
||||
"commandClasses": [
|
||||
{
|
||||
"id": 32,
|
||||
"name": "Basic",
|
||||
"version": 1,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 38,
|
||||
"name": "Multilevel Switch",
|
||||
"version": 2,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 43,
|
||||
"name": "Scene Activation",
|
||||
"version": 1,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 44,
|
||||
"name": "Scene Actuator Configuration",
|
||||
"version": 1,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 86,
|
||||
"name": "CRC-16 Encapsulation",
|
||||
"version": 1,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 89,
|
||||
"name": "Association Group Information",
|
||||
"version": 1,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 90,
|
||||
"name": "Device Reset Locally",
|
||||
"version": 1,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 94,
|
||||
"name": "Z-Wave Plus Info",
|
||||
"version": 2,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 112,
|
||||
"name": "Configuration",
|
||||
"version": 1,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 114,
|
||||
"name": "Manufacturer Specific",
|
||||
"version": 2,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 122,
|
||||
"name": "Firmware Update Meta Data",
|
||||
"version": 2,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 133,
|
||||
"name": "Association",
|
||||
"version": 2,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 134,
|
||||
"name": "Version",
|
||||
"version": 2,
|
||||
"isSecure": false
|
||||
}
|
||||
],
|
||||
"interviewStage": "Complete",
|
||||
"deviceDatabaseUrl": "https://devices.zwave-js.io/?jumpTo=0x0063:0x4944:0x3038:5.26"
|
||||
}
|
@ -50,7 +50,68 @@
|
||||
"index": 0
|
||||
}
|
||||
],
|
||||
"commandClasses": [],
|
||||
"commandClasses": [
|
||||
{
|
||||
"id": 98,
|
||||
"name": "Door Lock",
|
||||
"version": 1,
|
||||
"isSecure": true
|
||||
},
|
||||
{
|
||||
"id": 99,
|
||||
"name": "User Code",
|
||||
"version": 1,
|
||||
"isSecure": true
|
||||
},
|
||||
{
|
||||
"id": 112,
|
||||
"name": "Configuration",
|
||||
"version": 1,
|
||||
"isSecure": true
|
||||
},
|
||||
{
|
||||
"id": 113,
|
||||
"name": "Notification",
|
||||
"version": 1,
|
||||
"isSecure": true
|
||||
},
|
||||
{
|
||||
"id": 114,
|
||||
"name": "Manufacturer Specific",
|
||||
"version": 1,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 122,
|
||||
"name": "Firmware Update Meta Data",
|
||||
"version": 1,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 128,
|
||||
"name": "Battery",
|
||||
"version": 1,
|
||||
"isSecure": true
|
||||
},
|
||||
{
|
||||
"id": 133,
|
||||
"name": "Association",
|
||||
"version": 1,
|
||||
"isSecure": true
|
||||
},
|
||||
{
|
||||
"id": 134,
|
||||
"name": "Version",
|
||||
"version": 1,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 152,
|
||||
"name": "Security",
|
||||
"version": 1,
|
||||
"isSecure": true
|
||||
}
|
||||
],
|
||||
"values": [
|
||||
{
|
||||
"commandClassName": "Door Lock",
|
||||
|
Loading…
x
Reference in New Issue
Block a user