mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add zwave_js.value_updated automation trigger (#54897)
* Add zwave_js automation trigger * Rename to align with zwave-js api * Improve test coverage * Add additional template variables * Support states values in addition to keys when present * remove entity ID from trigger payload * comments and order * Add init and dynamically define platform_type * reduce mypy ignores * pylint * pylint * review * use module map
This commit is contained in:
parent
dc74a52f58
commit
63f6a3b46b
@ -48,6 +48,13 @@ ATTR_OPTIONS = "options"
|
|||||||
ATTR_NODE = "node"
|
ATTR_NODE = "node"
|
||||||
ATTR_ZWAVE_VALUE = "zwave_value"
|
ATTR_ZWAVE_VALUE = "zwave_value"
|
||||||
|
|
||||||
|
# automation trigger attributes
|
||||||
|
ATTR_PREVIOUS_VALUE = "previous_value"
|
||||||
|
ATTR_PREVIOUS_VALUE_RAW = "previous_value_raw"
|
||||||
|
ATTR_CURRENT_VALUE = "current_value"
|
||||||
|
ATTR_CURRENT_VALUE_RAW = "current_value_raw"
|
||||||
|
ATTR_DESCRIPTION = "description"
|
||||||
|
|
||||||
# service constants
|
# service constants
|
||||||
SERVICE_SET_VALUE = "set_value"
|
SERVICE_SET_VALUE = "set_value"
|
||||||
SERVICE_RESET_METER = "reset_meter"
|
SERVICE_RESET_METER = "reset_meter"
|
||||||
|
54
homeassistant/components/zwave_js/trigger.py
Normal file
54
homeassistant/components/zwave_js/trigger.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
"""Z-Wave JS trigger dispatcher."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from types import ModuleType
|
||||||
|
from typing import Any, Callable, cast
|
||||||
|
|
||||||
|
from homeassistant.const import CONF_PLATFORM
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
|
from .triggers import value_updated
|
||||||
|
|
||||||
|
TRIGGERS = {
|
||||||
|
"value_updated": value_updated,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _get_trigger_platform(config: ConfigType) -> ModuleType:
|
||||||
|
"""Return trigger platform."""
|
||||||
|
platform_split = config[CONF_PLATFORM].split(".", maxsplit=1)
|
||||||
|
if len(platform_split) < 2 or platform_split[1] not in TRIGGERS:
|
||||||
|
raise ValueError(f"Unknown Z-Wave JS trigger platform {config[CONF_PLATFORM]}")
|
||||||
|
return TRIGGERS[platform_split[1]]
|
||||||
|
|
||||||
|
|
||||||
|
async def async_validate_trigger_config(
|
||||||
|
hass: HomeAssistant, config: ConfigType
|
||||||
|
) -> ConfigType:
|
||||||
|
"""Validate config."""
|
||||||
|
platform = _get_trigger_platform(config)
|
||||||
|
if hasattr(platform, "async_validate_trigger_config"):
|
||||||
|
return cast(
|
||||||
|
ConfigType,
|
||||||
|
await getattr(platform, "async_validate_trigger_config")(hass, config),
|
||||||
|
)
|
||||||
|
assert hasattr(platform, "TRIGGER_SCHEMA")
|
||||||
|
return cast(ConfigType, getattr(platform, "TRIGGER_SCHEMA")(config))
|
||||||
|
|
||||||
|
|
||||||
|
async def async_attach_trigger(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config: ConfigType,
|
||||||
|
action: Callable,
|
||||||
|
automation_info: dict[str, Any],
|
||||||
|
) -> Callable:
|
||||||
|
"""Attach trigger of specified platform."""
|
||||||
|
platform = _get_trigger_platform(config)
|
||||||
|
assert hasattr(platform, "async_attach_trigger")
|
||||||
|
return cast(
|
||||||
|
Callable,
|
||||||
|
await getattr(platform, "async_attach_trigger")(
|
||||||
|
hass, config, action, automation_info
|
||||||
|
),
|
||||||
|
)
|
1
homeassistant/components/zwave_js/triggers/__init__.py
Normal file
1
homeassistant/components/zwave_js/triggers/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""Z-Wave JS triggers."""
|
193
homeassistant/components/zwave_js/triggers/value_updated.py
Normal file
193
homeassistant/components/zwave_js/triggers/value_updated.py
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
"""Offer Z-Wave JS value updated listening automation rules."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import functools
|
||||||
|
import logging
|
||||||
|
from typing import Any, Callable
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
from zwave_js_server.const import CommandClass
|
||||||
|
from zwave_js_server.event import Event
|
||||||
|
from zwave_js_server.model.node import Node
|
||||||
|
from zwave_js_server.model.value import Value, get_value_id
|
||||||
|
|
||||||
|
from homeassistant.components.zwave_js.const import (
|
||||||
|
ATTR_COMMAND_CLASS,
|
||||||
|
ATTR_COMMAND_CLASS_NAME,
|
||||||
|
ATTR_CURRENT_VALUE,
|
||||||
|
ATTR_CURRENT_VALUE_RAW,
|
||||||
|
ATTR_ENDPOINT,
|
||||||
|
ATTR_NODE_ID,
|
||||||
|
ATTR_PREVIOUS_VALUE,
|
||||||
|
ATTR_PREVIOUS_VALUE_RAW,
|
||||||
|
ATTR_PROPERTY,
|
||||||
|
ATTR_PROPERTY_KEY,
|
||||||
|
ATTR_PROPERTY_KEY_NAME,
|
||||||
|
ATTR_PROPERTY_NAME,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
|
from homeassistant.components.zwave_js.helpers import (
|
||||||
|
async_get_node_from_device_id,
|
||||||
|
async_get_node_from_entity_id,
|
||||||
|
get_device_id,
|
||||||
|
)
|
||||||
|
from homeassistant.const import ATTR_DEVICE_ID, ATTR_ENTITY_ID, CONF_PLATFORM, MATCH_ALL
|
||||||
|
from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, callback
|
||||||
|
from homeassistant.helpers import config_validation as cv, device_registry as dr
|
||||||
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Platform type should be <DOMAIN>.<SUBMODULE_NAME>
|
||||||
|
PLATFORM_TYPE = f"{DOMAIN}.{__name__.rsplit('.', maxsplit=1)[-1]}"
|
||||||
|
|
||||||
|
ATTR_FROM = "from"
|
||||||
|
ATTR_TO = "to"
|
||||||
|
|
||||||
|
VALUE_SCHEMA = vol.Any(
|
||||||
|
bool,
|
||||||
|
vol.Coerce(int),
|
||||||
|
vol.Coerce(float),
|
||||||
|
cv.boolean,
|
||||||
|
cv.string,
|
||||||
|
)
|
||||||
|
|
||||||
|
TRIGGER_SCHEMA = vol.All(
|
||||||
|
cv.TRIGGER_BASE_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_PLATFORM): PLATFORM_TYPE,
|
||||||
|
vol.Optional(ATTR_DEVICE_ID): vol.All(cv.ensure_list, [cv.string]),
|
||||||
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
||||||
|
vol.Required(ATTR_COMMAND_CLASS): vol.In(
|
||||||
|
{cc.value: cc.name for cc in CommandClass}
|
||||||
|
),
|
||||||
|
vol.Required(ATTR_PROPERTY): vol.Any(vol.Coerce(int), cv.string),
|
||||||
|
vol.Optional(ATTR_ENDPOINT): vol.Coerce(int),
|
||||||
|
vol.Optional(ATTR_PROPERTY_KEY): vol.Any(vol.Coerce(int), cv.string),
|
||||||
|
vol.Optional(ATTR_FROM, default=MATCH_ALL): vol.Any(
|
||||||
|
VALUE_SCHEMA, [VALUE_SCHEMA]
|
||||||
|
),
|
||||||
|
vol.Optional(ATTR_TO, default=MATCH_ALL): vol.Any(
|
||||||
|
VALUE_SCHEMA, [VALUE_SCHEMA]
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
cv.has_at_least_one_key(ATTR_ENTITY_ID, ATTR_DEVICE_ID),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_attach_trigger(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config: ConfigType,
|
||||||
|
action: Callable,
|
||||||
|
automation_info: dict[str, Any],
|
||||||
|
*,
|
||||||
|
platform_type: str = PLATFORM_TYPE,
|
||||||
|
) -> CALLBACK_TYPE:
|
||||||
|
"""Listen for state changes based on configuration."""
|
||||||
|
nodes: set[Node] = set()
|
||||||
|
if ATTR_DEVICE_ID in config:
|
||||||
|
nodes.update(
|
||||||
|
{
|
||||||
|
async_get_node_from_device_id(hass, device_id)
|
||||||
|
for device_id in config.get(ATTR_DEVICE_ID, [])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if ATTR_ENTITY_ID in config:
|
||||||
|
nodes.update(
|
||||||
|
{
|
||||||
|
async_get_node_from_entity_id(hass, entity_id)
|
||||||
|
for entity_id in config.get(ATTR_ENTITY_ID, [])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
from_value = config[ATTR_FROM]
|
||||||
|
to_value = config[ATTR_TO]
|
||||||
|
command_class = config[ATTR_COMMAND_CLASS]
|
||||||
|
property_ = config[ATTR_PROPERTY]
|
||||||
|
endpoint = config.get(ATTR_ENDPOINT)
|
||||||
|
property_key = config.get(ATTR_PROPERTY_KEY)
|
||||||
|
unsubs = []
|
||||||
|
job = HassJob(action)
|
||||||
|
|
||||||
|
trigger_data: dict = {}
|
||||||
|
if automation_info:
|
||||||
|
trigger_data = automation_info.get("trigger_data", {})
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_on_value_updated(
|
||||||
|
value: Value, device: dr.DeviceEntry, event: Event
|
||||||
|
) -> None:
|
||||||
|
"""Handle value update."""
|
||||||
|
event_value: Value = event["value"]
|
||||||
|
if event_value != value:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get previous value and its state value if it exists
|
||||||
|
prev_value_raw = event["args"]["prevValue"]
|
||||||
|
prev_value = value.metadata.states.get(str(prev_value_raw), prev_value_raw)
|
||||||
|
# Get current value and its state value if it exists
|
||||||
|
curr_value_raw = event["args"]["newValue"]
|
||||||
|
curr_value = value.metadata.states.get(str(curr_value_raw), curr_value_raw)
|
||||||
|
# Check from and to values against previous and current values respectively
|
||||||
|
for value_to_eval, raw_value_to_eval, match in (
|
||||||
|
(prev_value, prev_value_raw, from_value),
|
||||||
|
(curr_value, curr_value_raw, to_value),
|
||||||
|
):
|
||||||
|
if (
|
||||||
|
match != MATCH_ALL
|
||||||
|
and value_to_eval != match
|
||||||
|
and not (
|
||||||
|
isinstance(match, list)
|
||||||
|
and (value_to_eval in match or raw_value_to_eval in match)
|
||||||
|
)
|
||||||
|
and raw_value_to_eval != match
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
device_name = device.name_by_user or device.name
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
**trigger_data,
|
||||||
|
CONF_PLATFORM: platform_type,
|
||||||
|
ATTR_DEVICE_ID: device.id,
|
||||||
|
ATTR_NODE_ID: value.node.node_id,
|
||||||
|
ATTR_COMMAND_CLASS: value.command_class,
|
||||||
|
ATTR_COMMAND_CLASS_NAME: value.command_class_name,
|
||||||
|
ATTR_PROPERTY: value.property_,
|
||||||
|
ATTR_PROPERTY_NAME: value.property_name,
|
||||||
|
ATTR_ENDPOINT: endpoint,
|
||||||
|
ATTR_PROPERTY_KEY: value.property_key,
|
||||||
|
ATTR_PROPERTY_KEY_NAME: value.property_key_name,
|
||||||
|
ATTR_PREVIOUS_VALUE: prev_value,
|
||||||
|
ATTR_PREVIOUS_VALUE_RAW: prev_value_raw,
|
||||||
|
ATTR_CURRENT_VALUE: curr_value,
|
||||||
|
ATTR_CURRENT_VALUE_RAW: curr_value_raw,
|
||||||
|
"description": f"Z-Wave value {value_id} updated on {device_name}",
|
||||||
|
}
|
||||||
|
|
||||||
|
hass.async_run_hass_job(job, {"trigger": payload})
|
||||||
|
|
||||||
|
dev_reg = dr.async_get(hass)
|
||||||
|
for node in nodes:
|
||||||
|
device_identifier = get_device_id(node.client, node)
|
||||||
|
device = dev_reg.async_get_device({device_identifier})
|
||||||
|
assert device
|
||||||
|
value_id = get_value_id(node, command_class, property_, endpoint, property_key)
|
||||||
|
value = node.values[value_id]
|
||||||
|
# We need to store the current value and device for the callback
|
||||||
|
unsubs.append(
|
||||||
|
node.on(
|
||||||
|
"value updated",
|
||||||
|
functools.partial(async_on_value_updated, value, device),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_remove() -> None:
|
||||||
|
"""Remove state listeners async."""
|
||||||
|
for unsub in unsubs:
|
||||||
|
unsub()
|
||||||
|
unsubs.clear()
|
||||||
|
|
||||||
|
return async_remove
|
276
tests/components/zwave_js/test_trigger.py
Normal file
276
tests/components/zwave_js/test_trigger.py
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
"""The tests for Z-Wave JS automation triggers."""
|
||||||
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
|
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
|
||||||
|
from homeassistant.components.zwave_js.trigger import async_validate_trigger_config
|
||||||
|
from homeassistant.const import SERVICE_RELOAD
|
||||||
|
from homeassistant.helpers.device_registry import (
|
||||||
|
async_entries_for_config_entry,
|
||||||
|
async_get as async_get_dev_reg,
|
||||||
|
)
|
||||||
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
from .common import SCHLAGE_BE469_LOCK_ENTITY
|
||||||
|
|
||||||
|
from tests.common import async_capture_events
|
||||||
|
|
||||||
|
|
||||||
|
async def test_zwave_js_value_updated(hass, client, lock_schlage_be469, integration):
|
||||||
|
"""Test for zwave_js.value_updated automation trigger."""
|
||||||
|
trigger_type = f"{DOMAIN}.value_updated"
|
||||||
|
node: Node = lock_schlage_be469
|
||||||
|
dev_reg = async_get_dev_reg(hass)
|
||||||
|
device = async_entries_for_config_entry(dev_reg, integration.entry_id)[0]
|
||||||
|
|
||||||
|
no_value_filter = async_capture_events(hass, "no_value_filter")
|
||||||
|
single_from_value_filter = async_capture_events(hass, "single_from_value_filter")
|
||||||
|
multiple_from_value_filters = async_capture_events(
|
||||||
|
hass, "multiple_from_value_filters"
|
||||||
|
)
|
||||||
|
from_and_to_value_filters = async_capture_events(hass, "from_and_to_value_filters")
|
||||||
|
different_value = async_capture_events(hass, "different_value")
|
||||||
|
|
||||||
|
def clear_events():
|
||||||
|
"""Clear all events in the event list."""
|
||||||
|
no_value_filter.clear()
|
||||||
|
single_from_value_filter.clear()
|
||||||
|
multiple_from_value_filters.clear()
|
||||||
|
from_and_to_value_filters.clear()
|
||||||
|
different_value.clear()
|
||||||
|
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
automation.DOMAIN,
|
||||||
|
{
|
||||||
|
automation.DOMAIN: [
|
||||||
|
# no value filter
|
||||||
|
{
|
||||||
|
"trigger": {
|
||||||
|
"platform": trigger_type,
|
||||||
|
"entity_id": SCHLAGE_BE469_LOCK_ENTITY,
|
||||||
|
"command_class": CommandClass.DOOR_LOCK.value,
|
||||||
|
"property": "latchStatus",
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"event": "no_value_filter",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
# single from value filter
|
||||||
|
{
|
||||||
|
"trigger": {
|
||||||
|
"platform": trigger_type,
|
||||||
|
"device_id": device.id,
|
||||||
|
"command_class": CommandClass.DOOR_LOCK.value,
|
||||||
|
"property": "latchStatus",
|
||||||
|
"from": "ajar",
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"event": "single_from_value_filter",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
# multiple from value filters
|
||||||
|
{
|
||||||
|
"trigger": {
|
||||||
|
"platform": trigger_type,
|
||||||
|
"entity_id": SCHLAGE_BE469_LOCK_ENTITY,
|
||||||
|
"command_class": CommandClass.DOOR_LOCK.value,
|
||||||
|
"property": "latchStatus",
|
||||||
|
"from": ["closed", "opened"],
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"event": "multiple_from_value_filters",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
# from and to value filters
|
||||||
|
{
|
||||||
|
"trigger": {
|
||||||
|
"platform": trigger_type,
|
||||||
|
"entity_id": SCHLAGE_BE469_LOCK_ENTITY,
|
||||||
|
"command_class": CommandClass.DOOR_LOCK.value,
|
||||||
|
"property": "latchStatus",
|
||||||
|
"from": ["closed", "opened"],
|
||||||
|
"to": ["opened"],
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"event": "from_and_to_value_filters",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
# different value
|
||||||
|
{
|
||||||
|
"trigger": {
|
||||||
|
"platform": trigger_type,
|
||||||
|
"entity_id": SCHLAGE_BE469_LOCK_ENTITY,
|
||||||
|
"command_class": CommandClass.DOOR_LOCK.value,
|
||||||
|
"property": "boltStatus",
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"event": "different_value",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Test that no value filter is triggered
|
||||||
|
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": "boo",
|
||||||
|
"prevValue": "hiss",
|
||||||
|
"propertyName": "latchStatus",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
node.receive_event(event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(no_value_filter) == 1
|
||||||
|
assert len(single_from_value_filter) == 0
|
||||||
|
assert len(multiple_from_value_filters) == 0
|
||||||
|
assert len(from_and_to_value_filters) == 0
|
||||||
|
assert len(different_value) == 0
|
||||||
|
|
||||||
|
clear_events()
|
||||||
|
|
||||||
|
# Test that a single_from_value_filter is triggered
|
||||||
|
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": "boo",
|
||||||
|
"prevValue": "ajar",
|
||||||
|
"propertyName": "latchStatus",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
node.receive_event(event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(no_value_filter) == 1
|
||||||
|
assert len(single_from_value_filter) == 1
|
||||||
|
assert len(multiple_from_value_filters) == 0
|
||||||
|
assert len(from_and_to_value_filters) == 0
|
||||||
|
assert len(different_value) == 0
|
||||||
|
|
||||||
|
clear_events()
|
||||||
|
|
||||||
|
# Test that multiple_from_value_filters are triggered
|
||||||
|
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": "boo",
|
||||||
|
"prevValue": "closed",
|
||||||
|
"propertyName": "latchStatus",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
node.receive_event(event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(no_value_filter) == 1
|
||||||
|
assert len(single_from_value_filter) == 0
|
||||||
|
assert len(multiple_from_value_filters) == 1
|
||||||
|
assert len(from_and_to_value_filters) == 0
|
||||||
|
assert len(different_value) == 0
|
||||||
|
|
||||||
|
clear_events()
|
||||||
|
|
||||||
|
# Test that from_and_to_value_filters is triggered
|
||||||
|
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": "opened",
|
||||||
|
"prevValue": "closed",
|
||||||
|
"propertyName": "latchStatus",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
node.receive_event(event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(no_value_filter) == 1
|
||||||
|
assert len(single_from_value_filter) == 0
|
||||||
|
assert len(multiple_from_value_filters) == 1
|
||||||
|
assert len(from_and_to_value_filters) == 1
|
||||||
|
assert len(different_value) == 0
|
||||||
|
|
||||||
|
clear_events()
|
||||||
|
|
||||||
|
event = Event(
|
||||||
|
type="value updated",
|
||||||
|
data={
|
||||||
|
"source": "node",
|
||||||
|
"event": "value updated",
|
||||||
|
"nodeId": node.node_id,
|
||||||
|
"args": {
|
||||||
|
"commandClassName": "Door Lock",
|
||||||
|
"commandClass": 98,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "boltStatus",
|
||||||
|
"newValue": "boo",
|
||||||
|
"prevValue": "hiss",
|
||||||
|
"propertyName": "boltStatus",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
node.receive_event(event)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(no_value_filter) == 0
|
||||||
|
assert len(single_from_value_filter) == 0
|
||||||
|
assert len(multiple_from_value_filters) == 0
|
||||||
|
assert len(from_and_to_value_filters) == 0
|
||||||
|
assert len(different_value) == 1
|
||||||
|
|
||||||
|
clear_events()
|
||||||
|
|
||||||
|
with patch("homeassistant.config.load_yaml", return_value={}):
|
||||||
|
await hass.services.async_call(automation.DOMAIN, SERVICE_RELOAD, blocking=True)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_async_validate_trigger_config(hass):
|
||||||
|
"""Test async_validate_trigger_config."""
|
||||||
|
mock_platform = AsyncMock()
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.zwave_js.trigger._get_trigger_platform",
|
||||||
|
return_value=mock_platform,
|
||||||
|
):
|
||||||
|
mock_platform.async_validate_trigger_config.return_value = {}
|
||||||
|
await async_validate_trigger_config(hass, {})
|
||||||
|
mock_platform.async_validate_trigger_config.assert_awaited()
|
Loading…
x
Reference in New Issue
Block a user