mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 12:17:07 +00:00
Add support for zwave_js event entities (#102285)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
a2c60d9015
commit
41b59b6990
@ -162,6 +162,8 @@ class ZWaveValueDiscoverySchema(DataclassMustHaveAtLeastOne):
|
||||
any_available_states: set[tuple[int, str]] | None = None
|
||||
# [optional] the value's value must match this value
|
||||
value: Any | None = None
|
||||
# [optional] the value's metadata_stateful must match this value
|
||||
stateful: bool | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -1045,6 +1047,15 @@ DISCOVERY_SCHEMAS = [
|
||||
any_available_states={(0, "idle")},
|
||||
),
|
||||
),
|
||||
# event
|
||||
# stateful = False
|
||||
ZWaveDiscoverySchema(
|
||||
platform=Platform.EVENT,
|
||||
hint="stateless",
|
||||
primary_value=ZWaveValueDiscoverySchema(
|
||||
stateful=False,
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@ -1294,6 +1305,9 @@ def check_value(value: ZwaveValue, schema: ZWaveValueDiscoverySchema) -> bool:
|
||||
# check value
|
||||
if schema.value is not None and value.value not in schema.value:
|
||||
return False
|
||||
# check metadata_stateful
|
||||
if schema.stateful is not None and value.metadata.stateful != schema.stateful:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
|
98
homeassistant/components/zwave_js/event.py
Normal file
98
homeassistant/components/zwave_js/event.py
Normal file
@ -0,0 +1,98 @@
|
||||
"""Support for Z-Wave controls using the event platform."""
|
||||
from __future__ import annotations
|
||||
|
||||
from zwave_js_server.client import Client as ZwaveClient
|
||||
from zwave_js_server.model.driver import Driver
|
||||
from zwave_js_server.model.value import Value, ValueNotification
|
||||
|
||||
from homeassistant.components.event import DOMAIN as EVENT_DOMAIN, EventEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import ATTR_VALUE, DATA_CLIENT, DOMAIN
|
||||
from .discovery import ZwaveDiscoveryInfo
|
||||
from .entity import ZWaveBaseEntity
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Z-Wave Event entity from Config Entry."""
|
||||
client: ZwaveClient = hass.data[DOMAIN][config_entry.entry_id][DATA_CLIENT]
|
||||
|
||||
@callback
|
||||
def async_add_event(info: ZwaveDiscoveryInfo) -> None:
|
||||
"""Add Z-Wave event entity."""
|
||||
driver = client.driver
|
||||
assert driver is not None # Driver is ready before platforms are loaded.
|
||||
entities: list[ZWaveBaseEntity] = [ZwaveEventEntity(config_entry, driver, info)]
|
||||
async_add_entities(entities)
|
||||
|
||||
config_entry.async_on_unload(
|
||||
async_dispatcher_connect(
|
||||
hass,
|
||||
f"{DOMAIN}_{config_entry.entry_id}_add_{EVENT_DOMAIN}",
|
||||
async_add_event,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _cc_and_label(value: Value) -> str:
|
||||
"""Return a string with the command class and label."""
|
||||
label = value.metadata.label
|
||||
if label:
|
||||
label = label.lower()
|
||||
return f"{value.command_class_name.capitalize()} {label}".strip()
|
||||
|
||||
|
||||
class ZwaveEventEntity(ZWaveBaseEntity, EventEntity):
|
||||
"""Representation of a Z-Wave event entity."""
|
||||
|
||||
def __init__(
|
||||
self, config_entry: ConfigEntry, driver: Driver, info: ZwaveDiscoveryInfo
|
||||
) -> None:
|
||||
"""Initialize a ZwaveEventEntity entity."""
|
||||
super().__init__(config_entry, driver, info)
|
||||
value = self.value = info.primary_value
|
||||
self.states: dict[int, str] = {}
|
||||
|
||||
if states := value.metadata.states:
|
||||
self._attr_event_types = sorted(states.values())
|
||||
self.states = {int(k): v for k, v in states.items()}
|
||||
else:
|
||||
self._attr_event_types = [_cc_and_label(value)]
|
||||
# Entity class attributes
|
||||
self._attr_name = self.generate_name(include_value_name=True)
|
||||
|
||||
@callback
|
||||
def _async_handle_event(self, value_notification: ValueNotification) -> None:
|
||||
"""Handle a value notification event."""
|
||||
# If the notification doesn't match the value we are tracking, we can return
|
||||
value = self.value
|
||||
if (
|
||||
value_notification.command_class != value.command_class
|
||||
or value_notification.endpoint != value.endpoint
|
||||
or value_notification.property_ != value.property_
|
||||
or value_notification.property_key != value.property_key
|
||||
or (notification_value := value_notification.value) is None
|
||||
):
|
||||
return
|
||||
event_name = self.states.get(notification_value, _cc_and_label(value))
|
||||
self._trigger_event(event_name, {ATTR_VALUE: notification_value})
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Call when entity is added."""
|
||||
await super().async_added_to_hass()
|
||||
self.async_on_remove(
|
||||
self.info.node.on(
|
||||
"value notification",
|
||||
lambda event: self._async_handle_event(event["value_notification"]),
|
||||
)
|
||||
)
|
@ -668,6 +668,12 @@ def climate_intermatic_pe653_state_fixture():
|
||||
return json.loads(load_fixture("zwave_js/climate_intermatic_pe653_state.json"))
|
||||
|
||||
|
||||
@pytest.fixture(name="central_scene_node_state", scope="session")
|
||||
def central_scene_node_state_fixture():
|
||||
"""Load node with Central Scene CC node state fixture data."""
|
||||
return json.loads(load_fixture("zwave_js/central_scene_node_state.json"))
|
||||
|
||||
|
||||
# model fixtures
|
||||
|
||||
|
||||
@ -1304,3 +1310,11 @@ def climate_intermatic_pe653_fixture(client, climate_intermatic_pe653_state):
|
||||
node = Node(client, copy.deepcopy(climate_intermatic_pe653_state))
|
||||
client.driver.controller.nodes[node.node_id] = node
|
||||
return node
|
||||
|
||||
|
||||
@pytest.fixture(name="central_scene_node")
|
||||
def central_scene_node_fixture(client, central_scene_node_state):
|
||||
"""Mock a node with the Central Scene CC."""
|
||||
node = Node(client, copy.deepcopy(central_scene_node_state))
|
||||
client.driver.controller.nodes[node.node_id] = node
|
||||
return node
|
||||
|
431
tests/components/zwave_js/fixtures/central_scene_node_state.json
Normal file
431
tests/components/zwave_js/fixtures/central_scene_node_state.json
Normal file
@ -0,0 +1,431 @@
|
||||
{
|
||||
"nodeId": 51,
|
||||
"index": 0,
|
||||
"status": 4,
|
||||
"ready": true,
|
||||
"isListening": true,
|
||||
"isRouting": true,
|
||||
"isSecure": true,
|
||||
"firmwareVersion": "1.3.0",
|
||||
"interviewAttempts": 1,
|
||||
"endpoints": [
|
||||
{
|
||||
"nodeId": 51,
|
||||
"index": 0,
|
||||
"deviceClass": {
|
||||
"basic": {
|
||||
"key": 4,
|
||||
"label": "Routing Slave"
|
||||
},
|
||||
"generic": {
|
||||
"key": 24,
|
||||
"label": "Wall Controller"
|
||||
},
|
||||
"specific": {
|
||||
"key": 0,
|
||||
"label": "Unused"
|
||||
},
|
||||
"mandatorySupportedCCs": [],
|
||||
"mandatoryControlledCCs": []
|
||||
},
|
||||
"commandClasses": [
|
||||
{
|
||||
"id": 159,
|
||||
"name": "Security 2",
|
||||
"version": 1,
|
||||
"isSecure": true
|
||||
},
|
||||
{
|
||||
"id": 152,
|
||||
"name": "Security",
|
||||
"version": 1,
|
||||
"isSecure": true
|
||||
},
|
||||
{
|
||||
"id": 108,
|
||||
"name": "Supervision",
|
||||
"version": 1,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 85,
|
||||
"name": "Transport Service",
|
||||
"version": 2,
|
||||
"isSecure": false
|
||||
},
|
||||
{
|
||||
"id": 134,
|
||||
"name": "Version",
|
||||
"version": 3,
|
||||
"isSecure": true
|
||||
},
|
||||
{
|
||||
"id": 133,
|
||||
"name": "Association",
|
||||
"version": 2,
|
||||
"isSecure": true
|
||||
},
|
||||
{
|
||||
"id": 89,
|
||||
"name": "Association Group Information",
|
||||
"version": 3,
|
||||
"isSecure": true
|
||||
},
|
||||
{
|
||||
"id": 91,
|
||||
"name": "Central Scene",
|
||||
"version": 3,
|
||||
"isSecure": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"values": [
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 91,
|
||||
"commandClassName": "Central Scene",
|
||||
"property": "scene",
|
||||
"propertyKey": "001",
|
||||
"propertyName": "scene",
|
||||
"propertyKeyName": "001",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Scene 001",
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"states": {
|
||||
"0": "KeyPressed"
|
||||
},
|
||||
"stateful": false,
|
||||
"secret": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 91,
|
||||
"commandClassName": "Central Scene",
|
||||
"property": "scene",
|
||||
"propertyKey": "002",
|
||||
"propertyName": "scene",
|
||||
"propertyKeyName": "002",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Scene 002",
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"states": {
|
||||
"0": "KeyPressed",
|
||||
"1": "KeyReleased",
|
||||
"2": "KeyHeldDown",
|
||||
"3": "KeyPressed2x",
|
||||
"4": "KeyPressed3x",
|
||||
"5": "KeyPressed4x",
|
||||
"6": "KeyPressed5x"
|
||||
},
|
||||
"stateful": false,
|
||||
"secret": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 91,
|
||||
"commandClassName": "Central Scene",
|
||||
"property": "slowRefresh",
|
||||
"propertyName": "slowRefresh",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "boolean",
|
||||
"readable": true,
|
||||
"writeable": true,
|
||||
"description": "When this is true, KeyHeldDown notifications are sent every 55s. When this is false, the notifications are sent every 200ms.",
|
||||
"label": "Send held down notifications at a slow rate",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "libraryType",
|
||||
"propertyName": "libraryType",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Library type",
|
||||
"states": {
|
||||
"0": "Unknown",
|
||||
"1": "Static Controller",
|
||||
"2": "Controller",
|
||||
"3": "Enhanced Slave",
|
||||
"4": "Slave",
|
||||
"5": "Installer",
|
||||
"6": "Routing Slave",
|
||||
"7": "Bridge Controller",
|
||||
"8": "Device under Test",
|
||||
"9": "N/A",
|
||||
"10": "AV Remote",
|
||||
"11": "AV Device"
|
||||
},
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
},
|
||||
"value": 3
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "protocolVersion",
|
||||
"propertyName": "protocolVersion",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "string",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave protocol version",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
},
|
||||
"value": "6.81"
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "firmwareVersions",
|
||||
"propertyName": "firmwareVersions",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "string[]",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave chip firmware versions",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
},
|
||||
"value": ["1.3"]
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "hardwareVersion",
|
||||
"propertyName": "hardwareVersion",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave chip hardware version",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
},
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "sdkVersion",
|
||||
"propertyName": "sdkVersion",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "string",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "SDK version",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
},
|
||||
"value": "7.0.0"
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "applicationFrameworkAPIVersion",
|
||||
"propertyName": "applicationFrameworkAPIVersion",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "string",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave application framework API version",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
},
|
||||
"value": "unused"
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "applicationFrameworkBuildNumber",
|
||||
"propertyName": "applicationFrameworkBuildNumber",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "string",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave application framework API build number",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
},
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "hostInterfaceVersion",
|
||||
"propertyName": "hostInterfaceVersion",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "string",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Serial API version",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
},
|
||||
"value": "unused"
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "hostInterfaceBuildNumber",
|
||||
"propertyName": "hostInterfaceBuildNumber",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "string",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Serial API build number",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
},
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "zWaveProtocolVersion",
|
||||
"propertyName": "zWaveProtocolVersion",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "string",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave protocol version",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
},
|
||||
"value": "6.81.0"
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "zWaveProtocolBuildNumber",
|
||||
"propertyName": "zWaveProtocolBuildNumber",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "string",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Z-Wave protocol build number",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
},
|
||||
"value": 255
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "applicationVersion",
|
||||
"propertyName": "applicationVersion",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "string",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Application version",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
},
|
||||
"value": "1.3.0"
|
||||
},
|
||||
{
|
||||
"endpoint": 0,
|
||||
"commandClass": 134,
|
||||
"commandClassName": "Version",
|
||||
"property": "applicationBuildNumber",
|
||||
"propertyName": "applicationBuildNumber",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "string",
|
||||
"readable": true,
|
||||
"writeable": false,
|
||||
"label": "Application build number",
|
||||
"stateful": true,
|
||||
"secret": false
|
||||
},
|
||||
"value": 255
|
||||
}
|
||||
],
|
||||
"isFrequentListening": false,
|
||||
"maxDataRate": 100000,
|
||||
"supportedDataRates": [40000, 100000],
|
||||
"protocolVersion": 3,
|
||||
"supportsBeaming": true,
|
||||
"supportsSecurity": false,
|
||||
"nodeType": 1,
|
||||
"deviceClass": {
|
||||
"basic": {
|
||||
"key": 4,
|
||||
"label": "Routing Slave"
|
||||
},
|
||||
"generic": {
|
||||
"key": 24,
|
||||
"label": "Wall Controller"
|
||||
},
|
||||
"specific": {
|
||||
"key": 0,
|
||||
"label": "Unused"
|
||||
},
|
||||
"mandatorySupportedCCs": [],
|
||||
"mandatoryControlledCCs": []
|
||||
},
|
||||
"interviewStage": "Complete",
|
||||
"statistics": {
|
||||
"commandsTX": 42,
|
||||
"commandsRX": 46,
|
||||
"commandsDroppedRX": 0,
|
||||
"commandsDroppedTX": 0,
|
||||
"timeoutResponse": 0,
|
||||
"rtt": 55.4,
|
||||
"rssi": -72,
|
||||
"lwr": {
|
||||
"protocolDataRate": 3,
|
||||
"repeaters": [],
|
||||
"rssi": -72,
|
||||
"repeaterRSSI": []
|
||||
}
|
||||
},
|
||||
"highestSecurityClass": 0,
|
||||
"isControllerNode": false,
|
||||
"keepAwake": false
|
||||
}
|
175
tests/components/zwave_js/test_event.py
Normal file
175
tests/components/zwave_js/test_event.py
Normal file
@ -0,0 +1,175 @@
|
||||
"""Test the Z-Wave JS event platform."""
|
||||
from datetime import timedelta
|
||||
|
||||
from freezegun import freeze_time
|
||||
from zwave_js_server.event import Event
|
||||
|
||||
from homeassistant.components.event import ATTR_EVENT_TYPE
|
||||
from homeassistant.components.zwave_js.const import ATTR_VALUE
|
||||
from homeassistant.const import STATE_UNKNOWN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
BASIC_EVENT_VALUE_ENTITY = "event.honeywell_in_wall_smart_fan_control_event_value"
|
||||
CENTRAL_SCENE_ENTITY = "event.node_51_scene_002"
|
||||
|
||||
|
||||
async def test_basic(
|
||||
hass: HomeAssistant, client, fan_honeywell_39358, integration
|
||||
) -> None:
|
||||
"""Test the Basic CC event entity."""
|
||||
dt_util.now()
|
||||
fut = dt_util.now() + timedelta(minutes=1)
|
||||
node = fan_honeywell_39358
|
||||
state = hass.states.get(BASIC_EVENT_VALUE_ENTITY)
|
||||
|
||||
assert state
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
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": 255,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": True,
|
||||
"writeable": False,
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"label": "Event value",
|
||||
},
|
||||
"ccVersion": 1,
|
||||
},
|
||||
},
|
||||
)
|
||||
with freeze_time(fut):
|
||||
node.receive_event(event)
|
||||
|
||||
state = hass.states.get(BASIC_EVENT_VALUE_ENTITY)
|
||||
|
||||
assert state
|
||||
assert state.state == dt_util.as_utc(fut).isoformat(timespec="milliseconds")
|
||||
attributes = state.attributes
|
||||
assert attributes[ATTR_EVENT_TYPE] == "Basic event value"
|
||||
assert attributes[ATTR_VALUE] == 255
|
||||
|
||||
|
||||
async def test_central_scene(
|
||||
hass: HomeAssistant, client, central_scene_node, integration
|
||||
) -> None:
|
||||
"""Test the Central Scene CC event entity."""
|
||||
dt_util.now()
|
||||
fut = dt_util.now() + timedelta(minutes=1)
|
||||
node = central_scene_node
|
||||
state = hass.states.get(CENTRAL_SCENE_ENTITY)
|
||||
|
||||
assert state
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
event = Event(
|
||||
type="value notification",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value notification",
|
||||
"nodeId": node.node_id,
|
||||
"args": {
|
||||
"endpoint": 0,
|
||||
"commandClass": 91,
|
||||
"commandClassName": "Central Scene",
|
||||
"property": "scene",
|
||||
"propertyKey": "002",
|
||||
"propertyName": "scene",
|
||||
"propertyKeyName": "002",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": True,
|
||||
"writeable": False,
|
||||
"label": "Scene 002",
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"states": {
|
||||
"0": "KeyPressed",
|
||||
"1": "KeyReleased",
|
||||
"2": "KeyHeldDown",
|
||||
"3": "KeyPressed2x",
|
||||
"4": "KeyPressed3x",
|
||||
"5": "KeyPressed4x",
|
||||
"6": "KeyPressed5x",
|
||||
},
|
||||
"stateful": False,
|
||||
"secret": False,
|
||||
},
|
||||
"value": 1,
|
||||
},
|
||||
},
|
||||
)
|
||||
with freeze_time(fut):
|
||||
node.receive_event(event)
|
||||
|
||||
state = hass.states.get(CENTRAL_SCENE_ENTITY)
|
||||
|
||||
assert state
|
||||
assert state.state == dt_util.as_utc(fut).isoformat(timespec="milliseconds")
|
||||
attributes = state.attributes
|
||||
assert attributes[ATTR_EVENT_TYPE] == "KeyReleased"
|
||||
assert attributes[ATTR_VALUE] == 1
|
||||
|
||||
# Try invalid value
|
||||
event = Event(
|
||||
type="value notification",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value notification",
|
||||
"nodeId": node.node_id,
|
||||
"args": {
|
||||
"endpoint": 0,
|
||||
"commandClass": 91,
|
||||
"commandClassName": "Central Scene",
|
||||
"property": "scene",
|
||||
"propertyKey": "002",
|
||||
"propertyName": "scene",
|
||||
"propertyKeyName": "002",
|
||||
"ccVersion": 3,
|
||||
"metadata": {
|
||||
"type": "number",
|
||||
"readable": True,
|
||||
"writeable": False,
|
||||
"label": "Scene 002",
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"states": {
|
||||
"0": "KeyPressed",
|
||||
"1": "KeyReleased",
|
||||
"2": "KeyHeldDown",
|
||||
"3": "KeyPressed2x",
|
||||
"4": "KeyPressed3x",
|
||||
"5": "KeyPressed4x",
|
||||
"6": "KeyPressed5x",
|
||||
},
|
||||
"stateful": False,
|
||||
"secret": False,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
with freeze_time(fut + timedelta(minutes=10)):
|
||||
node.receive_event(event)
|
||||
|
||||
# Nothing should have changed even though the time has changed
|
||||
state = hass.states.get(CENTRAL_SCENE_ENTITY)
|
||||
|
||||
assert state
|
||||
assert state.state == dt_util.as_utc(fut).isoformat(timespec="milliseconds")
|
||||
attributes = state.attributes
|
||||
assert attributes[ATTR_EVENT_TYPE] == "KeyReleased"
|
||||
assert attributes[ATTR_VALUE] == 1
|
@ -1,4 +1,4 @@
|
||||
"""Test Z-Wave JS (value notification) events."""
|
||||
"""Test Z-Wave JS events."""
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
|
Loading…
x
Reference in New Issue
Block a user