Add zwave_js device triggers for any zwave value (#54958)
* Add zwave_js device triggers for any zwave value * translations * Validate valuepull/54970/head
parent
1075a65bbd
commit
2be50eb5b4
|
@ -2,7 +2,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import voluptuous as vol
|
||||
from zwave_js_server.const import CommandClass
|
||||
from zwave_js_server.const import CommandClass, ConfigurationValueType
|
||||
|
||||
from homeassistant.components.automation import AutomationActionType
|
||||
from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA
|
||||
|
@ -23,6 +23,7 @@ from homeassistant.helpers import (
|
|||
)
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from . import trigger
|
||||
from .const import (
|
||||
ATTR_COMMAND_CLASS,
|
||||
ATTR_DATA_TYPE,
|
||||
|
@ -45,6 +46,7 @@ from .helpers import (
|
|||
async_get_node_status_sensor_entity_id,
|
||||
get_zwave_value_from_config,
|
||||
)
|
||||
from .triggers.value_updated import ATTR_FROM, ATTR_TO
|
||||
|
||||
CONF_SUBTYPE = "subtype"
|
||||
CONF_VALUE_ID = "value_id"
|
||||
|
@ -55,8 +57,18 @@ 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"
|
||||
CONFIG_PARAMETER_VALUE_UPDATED = f"{DOMAIN}.value_updated.config_parameter"
|
||||
VALUE_VALUE_UPDATED = f"{DOMAIN}.value_updated.value"
|
||||
NODE_STATUS = "state.node_status"
|
||||
|
||||
VALUE_SCHEMA = vol.Any(
|
||||
bool,
|
||||
vol.Coerce(int),
|
||||
vol.Coerce(float),
|
||||
cv.boolean,
|
||||
cv.string,
|
||||
)
|
||||
|
||||
NOTIFICATION_EVENT_CC_MAPPINGS = (
|
||||
(ENTRY_CONTROL_NOTIFICATION, CommandClass.ENTRY_CONTROL),
|
||||
(NOTIFICATION_NOTIFICATION, CommandClass.NOTIFICATION),
|
||||
|
@ -135,12 +147,39 @@ NODE_STATUS_SCHEMA = BASE_STATE_SCHEMA.extend(
|
|||
}
|
||||
)
|
||||
|
||||
# zwave_js.value_updated based trigger schemas
|
||||
BASE_VALUE_UPDATED_SCHEMA = DEVICE_TRIGGER_BASE_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(ATTR_COMMAND_CLASS): vol.In([cc.value for cc in CommandClass]),
|
||||
vol.Required(ATTR_PROPERTY): vol.Any(int, str),
|
||||
vol.Optional(ATTR_PROPERTY_KEY): vol.Any(None, vol.Coerce(int), str),
|
||||
vol.Optional(ATTR_ENDPOINT): vol.Any(None, vol.Coerce(int)),
|
||||
vol.Optional(ATTR_FROM): VALUE_SCHEMA,
|
||||
vol.Optional(ATTR_TO): VALUE_SCHEMA,
|
||||
}
|
||||
)
|
||||
|
||||
CONFIG_PARAMETER_VALUE_UPDATED_SCHEMA = BASE_VALUE_UPDATED_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_TYPE): CONFIG_PARAMETER_VALUE_UPDATED,
|
||||
vol.Required(CONF_SUBTYPE): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
VALUE_VALUE_UPDATED_SCHEMA = BASE_VALUE_UPDATED_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_TYPE): VALUE_VALUE_UPDATED,
|
||||
}
|
||||
)
|
||||
|
||||
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,
|
||||
CONFIG_PARAMETER_VALUE_UPDATED_SCHEMA,
|
||||
VALUE_VALUE_UPDATED_SCHEMA,
|
||||
NODE_STATUS_SCHEMA,
|
||||
)
|
||||
|
||||
|
@ -233,6 +272,25 @@ async def async_get_triggers(hass: HomeAssistant, device_id: str) -> list[dict]:
|
|||
]
|
||||
)
|
||||
|
||||
# Generic value update event trigger
|
||||
triggers.append({**base_trigger, CONF_TYPE: VALUE_VALUE_UPDATED})
|
||||
|
||||
# Config parameter value update event triggers
|
||||
triggers.extend(
|
||||
[
|
||||
{
|
||||
**base_trigger,
|
||||
CONF_TYPE: CONFIG_PARAMETER_VALUE_UPDATED,
|
||||
ATTR_PROPERTY: config_value.property_,
|
||||
ATTR_PROPERTY_KEY: config_value.property_key,
|
||||
ATTR_ENDPOINT: config_value.endpoint,
|
||||
ATTR_COMMAND_CLASS: config_value.command_class,
|
||||
CONF_SUBTYPE: f"{config_value.value_id} ({config_value.property_name})",
|
||||
}
|
||||
for config_value in node.get_configuration_values().values()
|
||||
]
|
||||
)
|
||||
|
||||
return triggers
|
||||
|
||||
|
||||
|
@ -253,20 +311,25 @@ async def async_attach_trigger(
|
|||
) -> 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]
|
||||
trigger_split = trigger_type.split(".")
|
||||
# Our convention for trigger types is to have the trigger type at the beginning
|
||||
# delimited by a `.`. For zwave_js triggers, there is a `.` in the name
|
||||
trigger_platform = trigger_split[0]
|
||||
if trigger_platform == DOMAIN:
|
||||
trigger_platform = ".".join(trigger_split[:2])
|
||||
|
||||
# Take input data from automation trigger UI and add it to the trigger we are
|
||||
# attaching to
|
||||
if trigger_platform == "event":
|
||||
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]
|
||||
|
||||
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])
|
||||
|
@ -296,19 +359,53 @@ async def async_attach_trigger(
|
|||
hass, event_config, action, automation_info, platform_type="device"
|
||||
)
|
||||
|
||||
state_config = {state.CONF_PLATFORM: "state"}
|
||||
if trigger_platform == "state":
|
||||
if trigger_type == NODE_STATUS:
|
||||
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.CONF_ENTITY_ID] = config[CONF_ENTITY_ID]
|
||||
copy_available_params(
|
||||
config, state_config, [state.CONF_FOR, state.CONF_FROM, state.CONF_TO]
|
||||
)
|
||||
else:
|
||||
raise HomeAssistantError(f"Unhandled trigger type {trigger_type}")
|
||||
|
||||
state_config = state.TRIGGER_SCHEMA(state_config)
|
||||
return await state.async_attach_trigger(
|
||||
hass, state_config, action, automation_info, platform_type="device"
|
||||
)
|
||||
|
||||
if trigger_platform == f"{DOMAIN}.value_updated":
|
||||
# Try to get the value to make sure the value ID is valid
|
||||
try:
|
||||
node = async_get_node_from_device_id(hass, config[CONF_DEVICE_ID])
|
||||
get_zwave_value_from_config(node, config)
|
||||
except (ValueError, vol.Invalid) as err:
|
||||
raise HomeAssistantError("Invalid value specified") from err
|
||||
|
||||
zwave_js_config = {
|
||||
state.CONF_PLATFORM: trigger_platform,
|
||||
CONF_DEVICE_ID: config[CONF_DEVICE_ID],
|
||||
}
|
||||
copy_available_params(
|
||||
config,
|
||||
zwave_js_config,
|
||||
[
|
||||
ATTR_COMMAND_CLASS,
|
||||
ATTR_PROPERTY,
|
||||
ATTR_PROPERTY_KEY,
|
||||
ATTR_ENDPOINT,
|
||||
ATTR_FROM,
|
||||
ATTR_TO,
|
||||
],
|
||||
)
|
||||
zwave_js_config = await trigger.async_validate_trigger_config(
|
||||
hass, zwave_js_config
|
||||
)
|
||||
return await trigger.async_attach_trigger(
|
||||
hass, zwave_js_config, action, automation_info
|
||||
)
|
||||
|
||||
raise HomeAssistantError(f"Unhandled trigger type {trigger_type}")
|
||||
|
||||
|
||||
|
@ -316,12 +413,14 @@ async def async_get_trigger_capabilities(
|
|||
hass: HomeAssistant, config: ConfigType
|
||||
) -> dict[str, vol.Schema]:
|
||||
"""List trigger capabilities."""
|
||||
trigger_type = config[CONF_TYPE]
|
||||
|
||||
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:
|
||||
if trigger_type == NOTIFICATION_NOTIFICATION:
|
||||
return {
|
||||
"extra_fields": vol.Schema(
|
||||
{
|
||||
|
@ -333,7 +432,7 @@ async def async_get_trigger_capabilities(
|
|||
)
|
||||
}
|
||||
|
||||
if config[CONF_TYPE] == ENTRY_CONTROL_NOTIFICATION:
|
||||
if trigger_type == ENTRY_CONTROL_NOTIFICATION:
|
||||
return {
|
||||
"extra_fields": vol.Schema(
|
||||
{
|
||||
|
@ -343,7 +442,7 @@ async def async_get_trigger_capabilities(
|
|||
)
|
||||
}
|
||||
|
||||
if config[CONF_TYPE] == NODE_STATUS:
|
||||
if trigger_type == NODE_STATUS:
|
||||
return {
|
||||
"extra_fields": vol.Schema(
|
||||
{
|
||||
|
@ -354,7 +453,7 @@ async def async_get_trigger_capabilities(
|
|||
)
|
||||
}
|
||||
|
||||
if config[CONF_TYPE] in (
|
||||
if trigger_type in (
|
||||
BASIC_VALUE_NOTIFICATION,
|
||||
CENTRAL_SCENE_VALUE_NOTIFICATION,
|
||||
SCENE_ACTIVATION_VALUE_NOTIFICATION,
|
||||
|
@ -369,4 +468,47 @@ async def async_get_trigger_capabilities(
|
|||
|
||||
return {"extra_fields": vol.Schema({vol.Optional(ATTR_VALUE): value_schema})}
|
||||
|
||||
if trigger_type == CONFIG_PARAMETER_VALUE_UPDATED:
|
||||
# We can be more deliberate about the config parameter schema here because
|
||||
# there are a limited number of types
|
||||
if value.configuration_value_type == ConfigurationValueType.UNDEFINED:
|
||||
return {}
|
||||
if value.configuration_value_type == ConfigurationValueType.ENUMERATED:
|
||||
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.Optional(state.CONF_FROM): value_schema,
|
||||
vol.Optional(state.CONF_TO): value_schema,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if trigger_type == VALUE_VALUE_UPDATED:
|
||||
# Only show command classes on this node and exclude Configuration CC since it
|
||||
# is already covered
|
||||
return {
|
||||
"extra_fields": vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_COMMAND_CLASS): vol.In(
|
||||
{
|
||||
CommandClass(cc.id).value: CommandClass(cc.id).name
|
||||
for cc in sorted(node.command_classes, key=lambda cc: cc.name) # type: ignore[no-any-return]
|
||||
if cc.id != CommandClass.CONFIGURATION
|
||||
}
|
||||
),
|
||||
vol.Required(ATTR_PROPERTY): cv.string,
|
||||
vol.Optional(ATTR_PROPERTY_KEY): cv.string,
|
||||
vol.Optional(ATTR_ENDPOINT): cv.string,
|
||||
vol.Optional(state.CONF_FROM): cv.string,
|
||||
vol.Optional(state.CONF_TO): cv.string,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return {}
|
||||
|
|
|
@ -104,6 +104,8 @@
|
|||
"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}",
|
||||
"zwave_js.value_updated.config_parameter": "Value change on config parameter {subtype}",
|
||||
"zwave_js.value_updated.value": "Value change on a Z-Wave JS Value",
|
||||
"state.node_status": "Node status changed"
|
||||
},
|
||||
"condition_type": {
|
||||
|
|
|
@ -63,7 +63,9 @@
|
|||
"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"
|
||||
"state.node_status": "Node status changed",
|
||||
"zwave_js.value_updated.config_parameter": "Value change on config parameter {subtype}",
|
||||
"zwave_js.value_updated.value": "Value change on a Z-Wave JS Value"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
@ -115,6 +117,5 @@
|
|||
"title": "The Z-Wave JS add-on is starting."
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": "Z-Wave JS"
|
||||
}
|
||||
}
|
|
@ -951,6 +951,333 @@ async def test_get_trigger_capabilities_scene_activation_value_notification(
|
|||
]
|
||||
|
||||
|
||||
async def test_get_value_updated_value_triggers(
|
||||
hass, client, lock_schlage_be469, integration
|
||||
):
|
||||
"""Test we get the zwave_js.value_updated.value trigger from a zwave_js device."""
|
||||
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": "zwave_js.value_updated.value",
|
||||
"device_id": device.id,
|
||||
}
|
||||
triggers = await async_get_device_automations(hass, "trigger", device.id)
|
||||
assert expected_trigger in triggers
|
||||
|
||||
|
||||
async def test_if_value_updated_value_fires(
|
||||
hass, client, lock_schlage_be469, integration, calls
|
||||
):
|
||||
"""Test for zwave_js.value_updated.value 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": "zwave_js.value_updated.value",
|
||||
"command_class": CommandClass.DOOR_LOCK.value,
|
||||
"property": "latchStatus",
|
||||
"property_key": None,
|
||||
"endpoint": None,
|
||||
"from": "open",
|
||||
},
|
||||
"action": {
|
||||
"service": "test.automation",
|
||||
"data_template": {
|
||||
"some": (
|
||||
"zwave_js.value_updated.value - "
|
||||
"{{ trigger.platform}} - "
|
||||
"{{ trigger.previous_value }}"
|
||||
)
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
# Publish fake value update that shouldn't trigger
|
||||
event = Event(
|
||||
type="value updated",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value updated",
|
||||
"nodeId": node.node_id,
|
||||
"args": {
|
||||
"commandClassName": "Door Lock",
|
||||
"commandClass": 98,
|
||||
"endpoint": 0,
|
||||
"property": "insideHandlesCanOpenDoor",
|
||||
"newValue": [True, False, False, False],
|
||||
"prevValue": [False, False, False, False],
|
||||
"propertyName": "insideHandlesCanOpenDoor",
|
||||
},
|
||||
},
|
||||
)
|
||||
node.receive_event(event)
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 0
|
||||
|
||||
# Publish fake value update that should trigger
|
||||
event = Event(
|
||||
type="value updated",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value updated",
|
||||
"nodeId": node.node_id,
|
||||
"args": {
|
||||
"commandClassName": "Door Lock",
|
||||
"commandClass": 98,
|
||||
"endpoint": 0,
|
||||
"property": "latchStatus",
|
||||
"newValue": "closed",
|
||||
"prevValue": "open",
|
||||
"propertyName": "latchStatus",
|
||||
},
|
||||
},
|
||||
)
|
||||
node.receive_event(event)
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
assert (
|
||||
calls[0].data["some"]
|
||||
== "zwave_js.value_updated.value - zwave_js.value_updated - open"
|
||||
)
|
||||
|
||||
|
||||
async def test_get_trigger_capabilities_value_updated_value(
|
||||
hass, client, lock_schlage_be469, integration
|
||||
):
|
||||
"""Test we get the expected capabilities from a zwave_js.value_updated.value 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": "zwave_js.value_updated.value",
|
||||
"command_class": CommandClass.DOOR_LOCK.value,
|
||||
"property": "latchStatus",
|
||||
"property_key": None,
|
||||
"endpoint": None,
|
||||
},
|
||||
)
|
||||
assert capabilities and "extra_fields" in capabilities
|
||||
|
||||
assert voluptuous_serialize.convert(
|
||||
capabilities["extra_fields"], custom_serializer=cv.custom_serializer
|
||||
) == [
|
||||
{
|
||||
"name": "command_class",
|
||||
"required": True,
|
||||
"type": "select",
|
||||
"options": [
|
||||
(133, "ASSOCIATION"),
|
||||
(128, "BATTERY"),
|
||||
(98, "DOOR_LOCK"),
|
||||
(122, "FIRMWARE_UPDATE_MD"),
|
||||
(114, "MANUFACTURER_SPECIFIC"),
|
||||
(113, "NOTIFICATION"),
|
||||
(152, "SECURITY"),
|
||||
(99, "USER_CODE"),
|
||||
(134, "VERSION"),
|
||||
],
|
||||
},
|
||||
{"name": "property", "required": True, "type": "string"},
|
||||
{"name": "property_key", "optional": True, "type": "string"},
|
||||
{"name": "endpoint", "optional": True, "type": "string"},
|
||||
{"name": "from", "optional": True, "type": "string"},
|
||||
{"name": "to", "optional": True, "type": "string"},
|
||||
]
|
||||
|
||||
|
||||
async def test_get_value_updated_config_parameter_triggers(
|
||||
hass, client, lock_schlage_be469, integration
|
||||
):
|
||||
"""Test we get the zwave_js.value_updated.config_parameter trigger from a zwave_js device."""
|
||||
node = lock_schlage_be469
|
||||
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": "zwave_js.value_updated.config_parameter",
|
||||
"device_id": device.id,
|
||||
"property": 3,
|
||||
"property_key": None,
|
||||
"endpoint": 0,
|
||||
"command_class": CommandClass.CONFIGURATION.value,
|
||||
"subtype": f"{node.node_id}-112-0-3 (Beeper)",
|
||||
}
|
||||
triggers = await async_get_device_automations(hass, "trigger", device.id)
|
||||
assert expected_trigger in triggers
|
||||
|
||||
|
||||
async def test_if_value_updated_config_parameter_fires(
|
||||
hass, client, lock_schlage_be469, integration, calls
|
||||
):
|
||||
"""Test for zwave_js.value_updated.config_parameter 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": "zwave_js.value_updated.config_parameter",
|
||||
"property": 3,
|
||||
"property_key": None,
|
||||
"endpoint": 0,
|
||||
"command_class": CommandClass.CONFIGURATION.value,
|
||||
"subtype": f"{node.node_id}-112-0-3 (Beeper)",
|
||||
"from": 255,
|
||||
},
|
||||
"action": {
|
||||
"service": "test.automation",
|
||||
"data_template": {
|
||||
"some": (
|
||||
"zwave_js.value_updated.config_parameter - "
|
||||
"{{ trigger.platform}} - "
|
||||
"{{ trigger.previous_value_raw }}"
|
||||
)
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
# Publish fake value update
|
||||
event = Event(
|
||||
type="value updated",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value updated",
|
||||
"nodeId": node.node_id,
|
||||
"args": {
|
||||
"commandClassName": "Configuration",
|
||||
"commandClass": 112,
|
||||
"endpoint": 0,
|
||||
"property": 3,
|
||||
"newValue": 0,
|
||||
"prevValue": 255,
|
||||
"propertyName": "Beeper",
|
||||
},
|
||||
},
|
||||
)
|
||||
node.receive_event(event)
|
||||
await hass.async_block_till_done()
|
||||
assert len(calls) == 1
|
||||
assert (
|
||||
calls[0].data["some"]
|
||||
== "zwave_js.value_updated.config_parameter - zwave_js.value_updated - 255"
|
||||
)
|
||||
|
||||
|
||||
async def test_get_trigger_capabilities_value_updated_config_parameter_range(
|
||||
hass, client, lock_schlage_be469, integration
|
||||
):
|
||||
"""Test we get the expected capabilities from a range zwave_js.value_updated.config_parameter trigger."""
|
||||
node = lock_schlage_be469
|
||||
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": "zwave_js.value_updated.config_parameter",
|
||||
"property": 6,
|
||||
"property_key": None,
|
||||
"endpoint": 0,
|
||||
"command_class": CommandClass.CONFIGURATION.value,
|
||||
"subtype": f"{node.node_id}-112-0-6 (User Slot Status)",
|
||||
},
|
||||
)
|
||||
assert capabilities and "extra_fields" in capabilities
|
||||
|
||||
assert voluptuous_serialize.convert(
|
||||
capabilities["extra_fields"], custom_serializer=cv.custom_serializer
|
||||
) == [
|
||||
{
|
||||
"name": "from",
|
||||
"optional": True,
|
||||
"valueMin": 0,
|
||||
"valueMax": 255,
|
||||
"type": "integer",
|
||||
},
|
||||
{
|
||||
"name": "to",
|
||||
"optional": True,
|
||||
"valueMin": 0,
|
||||
"valueMax": 255,
|
||||
"type": "integer",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
async def test_get_trigger_capabilities_value_updated_config_parameter_enumerated(
|
||||
hass, client, lock_schlage_be469, integration
|
||||
):
|
||||
"""Test we get the expected capabilities from an enumerated zwave_js.value_updated.config_parameter trigger."""
|
||||
node = lock_schlage_be469
|
||||
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": "zwave_js.value_updated.config_parameter",
|
||||
"property": 3,
|
||||
"property_key": None,
|
||||
"endpoint": 0,
|
||||
"command_class": CommandClass.CONFIGURATION.value,
|
||||
"subtype": f"{node.node_id}-112-0-3 (Beeper)",
|
||||
},
|
||||
)
|
||||
assert capabilities and "extra_fields" in capabilities
|
||||
|
||||
assert voluptuous_serialize.convert(
|
||||
capabilities["extra_fields"], custom_serializer=cv.custom_serializer
|
||||
) == [
|
||||
{
|
||||
"name": "from",
|
||||
"optional": True,
|
||||
"options": [(0, "Disable Beeper"), (255, "Enable Beeper")],
|
||||
"type": "select",
|
||||
},
|
||||
{
|
||||
"name": "to",
|
||||
"optional": True,
|
||||
"options": [(0, "Disable Beeper"), (255, "Enable Beeper")],
|
||||
"type": "select",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
async def test_failure_scenarios(hass, client, hank_binary_switch, integration):
|
||||
"""Test failure scenarios."""
|
||||
with pytest.raises(HomeAssistantError):
|
||||
|
@ -982,6 +1309,30 @@ async def test_failure_scenarios(hass, client, hank_binary_switch, integration):
|
|||
{},
|
||||
)
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await async_attach_trigger(
|
||||
hass,
|
||||
{"type": "state.failed_type", "device_id": device.id},
|
||||
None,
|
||||
{},
|
||||
)
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await async_attach_trigger(
|
||||
hass,
|
||||
{
|
||||
"device_id": device.id,
|
||||
"type": "zwave_js.value_updated.value",
|
||||
"command_class": CommandClass.DOOR_LOCK.value,
|
||||
"property": -1234,
|
||||
"property_key": None,
|
||||
"endpoint": None,
|
||||
"from": "open",
|
||||
},
|
||||
None,
|
||||
{},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.zwave_js.device_trigger.async_get_node_from_device_id",
|
||||
return_value=None,
|
||||
|
|
Loading…
Reference in New Issue