From dfa973f9ef6d03651cd7084f63e2c5017d006436 Mon Sep 17 00:00:00 2001 From: kpine Date: Sun, 14 Feb 2021 04:24:29 -0800 Subject: [PATCH] Add barrier covers to zwave_js integration (#46379) --- homeassistant/components/zwave_js/cover.py | 79 +- .../components/zwave_js/discovery.py | 47 +- homeassistant/components/zwave_js/switch.py | 67 +- tests/components/zwave_js/conftest.py | 14 + tests/components/zwave_js/test_cover.py | 215 +++- tests/components/zwave_js/test_switch.py | 141 +++ .../fixtures/zwave_js/cover_zw062_state.json | 936 ++++++++++++++++++ 7 files changed, 1483 insertions(+), 16 deletions(-) create mode 100644 tests/fixtures/zwave_js/cover_zw062_state.json diff --git a/homeassistant/components/zwave_js/cover.py b/homeassistant/components/zwave_js/cover.py index 38c891f7376..ff77bdb408d 100644 --- a/homeassistant/components/zwave_js/cover.py +++ b/homeassistant/components/zwave_js/cover.py @@ -3,9 +3,11 @@ import logging from typing import Any, Callable, List, Optional from zwave_js_server.client import Client as ZwaveClient +from zwave_js_server.model.value import Value as ZwaveValue from homeassistant.components.cover import ( ATTR_POSITION, + DEVICE_CLASS_GARAGE, DOMAIN as COVER_DOMAIN, SUPPORT_CLOSE, SUPPORT_OPEN, @@ -20,7 +22,15 @@ from .discovery import ZwaveDiscoveryInfo from .entity import ZWaveBaseEntity LOGGER = logging.getLogger(__name__) -SUPPORT_GARAGE = SUPPORT_OPEN | SUPPORT_CLOSE + +BARRIER_TARGET_CLOSE = 0 +BARRIER_TARGET_OPEN = 255 + +BARRIER_STATE_CLOSED = 0 +BARRIER_STATE_CLOSING = 252 +BARRIER_STATE_STOPPED = 253 +BARRIER_STATE_OPENING = 254 +BARRIER_STATE_OPEN = 255 async def async_setup_entry( @@ -33,7 +43,10 @@ async def async_setup_entry( def async_add_cover(info: ZwaveDiscoveryInfo) -> None: """Add Z-Wave cover.""" entities: List[ZWaveBaseEntity] = [] - entities.append(ZWaveCover(config_entry, client, info)) + if info.platform_hint == "motorized_barrier": + entities.append(ZwaveMotorizedBarrier(config_entry, client, info)) + else: + entities.append(ZWaveCover(config_entry, client, info)) async_add_entities(entities) hass.data[DOMAIN][config_entry.entry_id][DATA_UNSUBSCRIBE].append( @@ -99,3 +112,65 @@ class ZWaveCover(ZWaveBaseEntity, CoverEntity): target_value = self.get_zwave_value("Close") or self.get_zwave_value("Down") if target_value: await self.info.node.async_set_value(target_value, False) + + +class ZwaveMotorizedBarrier(ZWaveBaseEntity, CoverEntity): + """Representation of a Z-Wave motorized barrier device.""" + + def __init__( + self, + config_entry: ConfigEntry, + client: ZwaveClient, + info: ZwaveDiscoveryInfo, + ) -> None: + """Initialize a ZwaveMotorizedBarrier entity.""" + super().__init__(config_entry, client, info) + self._target_state: ZwaveValue = self.get_zwave_value( + "targetState", add_to_watched_value_ids=False + ) + + @property + def supported_features(self) -> Optional[int]: + """Flag supported features.""" + return SUPPORT_OPEN | SUPPORT_CLOSE + + @property + def device_class(self) -> Optional[str]: + """Return the class of this device, from component DEVICE_CLASSES.""" + return DEVICE_CLASS_GARAGE + + @property + def is_opening(self) -> Optional[bool]: + """Return if the cover is opening or not.""" + if self.info.primary_value.value is None: + return None + return bool(self.info.primary_value.value == BARRIER_STATE_OPENING) + + @property + def is_closing(self) -> Optional[bool]: + """Return if the cover is closing or not.""" + if self.info.primary_value.value is None: + return None + return bool(self.info.primary_value.value == BARRIER_STATE_CLOSING) + + @property + def is_closed(self) -> Optional[bool]: + """Return if the cover is closed or not.""" + if self.info.primary_value.value is None: + return None + # If a barrier is in the stopped state, the only way to proceed is by + # issuing an open cover command. Return None in this case which + # produces an unknown state and allows it to be resolved with an open + # command. + if self.info.primary_value.value == BARRIER_STATE_STOPPED: + return None + + return bool(self.info.primary_value.value == BARRIER_STATE_CLOSED) + + async def async_open_cover(self, **kwargs: Any) -> None: + """Open the garage door.""" + await self.info.node.async_set_value(self._target_state, BARRIER_TARGET_OPEN) + + async def async_close_cover(self, **kwargs: Any) -> None: + """Close the garage door.""" + await self.info.node.async_set_value(self._target_state, BARRIER_TARGET_CLOSE) diff --git a/homeassistant/components/zwave_js/discovery.py b/homeassistant/components/zwave_js/discovery.py index 62d45a5ae5e..1aa70ea2fa0 100644 --- a/homeassistant/components/zwave_js/discovery.py +++ b/homeassistant/components/zwave_js/discovery.py @@ -1,7 +1,7 @@ """Map Z-Wave nodes and values to Home Assistant entities.""" from dataclasses import dataclass -from typing import Generator, Optional, Set, Union +from typing import Generator, List, Optional, Set, Union from zwave_js_server.const import CommandClass from zwave_js_server.model.node import Node as ZwaveNode @@ -78,7 +78,7 @@ class ZWaveDiscoverySchema: # [optional] the node's specific device class must match ANY of these values device_class_specific: Optional[Set[str]] = None # [optional] additional values that ALL need to be present on the node for this scheme to pass - required_values: Optional[Set[ZWaveValueDiscoverySchema]] = None + required_values: Optional[List[ZWaveValueDiscoverySchema]] = None # [optional] bool to specify if this primary value may be discovered by multiple platforms allow_multi: bool = False @@ -345,10 +345,22 @@ DISCOVERY_SCHEMAS = [ command_class={CommandClass.SWITCH_BINARY}, property={"currentValue"} ), ), + # binary switch + # barrier operator signaling states + ZWaveDiscoverySchema( + platform="switch", + hint="barrier_event_signaling_state", + primary_value=ZWaveValueDiscoverySchema( + command_class={CommandClass.BARRIER_OPERATOR}, + property={"signalingState"}, + type={"number"}, + ), + ), # cover + # window coverings ZWaveDiscoverySchema( platform="cover", - hint="cover", + hint="window_cover", device_class_generic={"Multilevel Switch"}, device_class_specific={ "Motor Control Class A", @@ -362,6 +374,24 @@ DISCOVERY_SCHEMAS = [ type={"number"}, ), ), + # cover + # motorized barriers + ZWaveDiscoverySchema( + platform="cover", + hint="motorized_barrier", + primary_value=ZWaveValueDiscoverySchema( + command_class={CommandClass.BARRIER_OPERATOR}, + property={"currentState"}, + type={"number"}, + ), + required_values=[ + ZWaveValueDiscoverySchema( + command_class={CommandClass.BARRIER_OPERATOR}, + property={"targetState"}, + type={"number"}, + ), + ], + ), # fan ZWaveDiscoverySchema( platform="fan", @@ -430,13 +460,10 @@ def async_discover_values(node: ZwaveNode) -> Generator[ZwaveDiscoveryInfo, None continue # check additional required values if schema.required_values is not None: - required_values_present = True - for val_scheme in schema.required_values: - for val in node.values.values(): - if not check_value(val, val_scheme): - required_values_present = False - break - if not required_values_present: + if not all( + any(check_value(val, val_scheme) for val in node.values.values()) + for val_scheme in schema.required_values + ): continue # all checks passed, this value belongs to an entity yield ZwaveDiscoveryInfo( diff --git a/homeassistant/components/zwave_js/switch.py b/homeassistant/components/zwave_js/switch.py index 8feba5911f8..a325e9821f7 100644 --- a/homeassistant/components/zwave_js/switch.py +++ b/homeassistant/components/zwave_js/switch.py @@ -17,6 +17,10 @@ from .entity import ZWaveBaseEntity LOGGER = logging.getLogger(__name__) +BARRIER_EVENT_SIGNALING_OFF = 0 +BARRIER_EVENT_SIGNALING_ON = 255 + + async def async_setup_entry( hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: Callable ) -> None: @@ -27,7 +31,12 @@ async def async_setup_entry( def async_add_switch(info: ZwaveDiscoveryInfo) -> None: """Add Z-Wave Switch.""" entities: List[ZWaveBaseEntity] = [] - entities.append(ZWaveSwitch(config_entry, client, info)) + if info.platform_hint == "barrier_event_signaling_state": + entities.append( + ZWaveBarrierEventSignalingSwitch(config_entry, client, info) + ) + else: + entities.append(ZWaveSwitch(config_entry, client, info)) async_add_entities(entities) @@ -62,3 +71,59 @@ class ZWaveSwitch(ZWaveBaseEntity, SwitchEntity): target_value = self.get_zwave_value("targetValue") if target_value is not None: await self.info.node.async_set_value(target_value, False) + + +class ZWaveBarrierEventSignalingSwitch(ZWaveBaseEntity, SwitchEntity): + """This switch is used to turn on or off a barrier device's event signaling subsystem.""" + + def __init__( + self, + config_entry: ConfigEntry, + client: ZwaveClient, + info: ZwaveDiscoveryInfo, + ) -> None: + """Initialize a ZWaveBarrierEventSignalingSwitch entity.""" + super().__init__(config_entry, client, info) + self._name = self.generate_name(include_value_name=True) + self._state: Optional[bool] = None + + self._update_state() + + @callback + def on_value_update(self) -> None: + """Call when a watched value is added or updated.""" + self._update_state() + + @property + def name(self) -> str: + """Return default name from device name and value name combination.""" + return self._name + + @property + def is_on(self) -> Optional[bool]: # type: ignore + """Return a boolean for the state of the switch.""" + return self._state + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn the switch on.""" + await self.info.node.async_set_value( + self.info.primary_value, BARRIER_EVENT_SIGNALING_ON + ) + # this value is not refreshed, so assume success + self._state = True + self.async_write_ha_state() + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn the switch off.""" + await self.info.node.async_set_value( + self.info.primary_value, BARRIER_EVENT_SIGNALING_OFF + ) + # this value is not refreshed, so assume success + self._state = False + self.async_write_ha_state() + + @callback + def _update_state(self) -> None: + self._state = None + if self.info.primary_value.value is not None: + self._state = self.info.primary_value.value == BARRIER_EVENT_SIGNALING_ON diff --git a/tests/components/zwave_js/conftest.py b/tests/components/zwave_js/conftest.py index 9cb950ba6e7..31b7d795a60 100644 --- a/tests/components/zwave_js/conftest.py +++ b/tests/components/zwave_js/conftest.py @@ -158,6 +158,12 @@ def in_wall_smart_fan_control_state_fixture(): return json.loads(load_fixture("zwave_js/in_wall_smart_fan_control_state.json")) +@pytest.fixture(name="gdc_zw062_state", scope="session") +def motorized_barrier_cover_state_fixture(): + """Load the motorized barrier cover node state fixture data.""" + return json.loads(load_fixture("zwave_js/cover_zw062_state.json")) + + @pytest.fixture(name="client") def mock_client_fixture(controller_state, version_state): """Mock a client.""" @@ -345,3 +351,11 @@ def multiple_devices_fixture( node = Node(client, lock_schlage_be469_state) client.driver.controller.nodes[node.node_id] = node return client.driver.controller.nodes + + +@pytest.fixture(name="gdc_zw062") +def motorized_barrier_cover_fixture(client, gdc_zw062_state): + """Mock a motorized barrier node.""" + node = Node(client, gdc_zw062_state) + client.driver.controller.nodes[node.node_id] = node + return node diff --git a/tests/components/zwave_js/test_cover.py b/tests/components/zwave_js/test_cover.py index 5e50ffb226e..2378453e31a 100644 --- a/tests/components/zwave_js/test_cover.py +++ b/tests/components/zwave_js/test_cover.py @@ -1,13 +1,28 @@ """Test the Z-Wave JS cover platform.""" from zwave_js_server.event import Event -from homeassistant.components.cover import ATTR_CURRENT_POSITION +from homeassistant.components.cover import ( + ATTR_CURRENT_POSITION, + DEVICE_CLASS_GARAGE, + DOMAIN, + SERVICE_CLOSE_COVER, + SERVICE_OPEN_COVER, +) +from homeassistant.const import ( + ATTR_DEVICE_CLASS, + STATE_CLOSED, + STATE_CLOSING, + STATE_OPEN, + STATE_OPENING, + STATE_UNKNOWN, +) WINDOW_COVER_ENTITY = "cover.zws_12" +GDC_COVER_ENTITY = "cover.aeon_labs_garage_door_controller_gen5" -async def test_cover(hass, client, chain_actuator_zws12, integration): - """Test the light entity.""" +async def test_window_cover(hass, client, chain_actuator_zws12, integration): + """Test the cover entity.""" node = chain_actuator_zws12 state = hass.states.get(WINDOW_COVER_ENTITY) @@ -282,3 +297,197 @@ async def test_cover(hass, client, chain_actuator_zws12, integration): state = hass.states.get(WINDOW_COVER_ENTITY) assert state.state == "closed" + + +async def test_motor_barrier_cover(hass, client, gdc_zw062, integration): + """Test the cover entity.""" + node = gdc_zw062 + + state = hass.states.get(GDC_COVER_ENTITY) + assert state + assert state.attributes[ATTR_DEVICE_CLASS] == DEVICE_CLASS_GARAGE + + assert state.state == STATE_CLOSED + + # Test open + await hass.services.async_call( + DOMAIN, SERVICE_OPEN_COVER, {"entity_id": GDC_COVER_ENTITY}, blocking=True + ) + + assert len(client.async_send_command.call_args_list) == 1 + args = client.async_send_command.call_args[0][0] + assert args["command"] == "node.set_value" + assert args["nodeId"] == 12 + assert args["value"] == 255 + assert args["valueId"] == { + "ccVersion": 0, + "commandClass": 102, + "commandClassName": "Barrier Operator", + "endpoint": 0, + "metadata": { + "label": "Target Barrier State", + "max": 255, + "min": 0, + "readable": True, + "states": {"0": "Closed", "255": "Open"}, + "type": "number", + "writeable": True, + }, + "property": "targetState", + "propertyName": "targetState", + } + + # state doesn't change until currentState value update is received + state = hass.states.get(GDC_COVER_ENTITY) + assert state.state == STATE_CLOSED + + client.async_send_command.reset_mock() + + # Test close + await hass.services.async_call( + DOMAIN, SERVICE_CLOSE_COVER, {"entity_id": GDC_COVER_ENTITY}, blocking=True + ) + + assert len(client.async_send_command.call_args_list) == 1 + args = client.async_send_command.call_args[0][0] + assert args["command"] == "node.set_value" + assert args["nodeId"] == 12 + assert args["value"] == 0 + assert args["valueId"] == { + "ccVersion": 0, + "commandClass": 102, + "commandClassName": "Barrier Operator", + "endpoint": 0, + "metadata": { + "label": "Target Barrier State", + "max": 255, + "min": 0, + "readable": True, + "states": {"0": "Closed", "255": "Open"}, + "type": "number", + "writeable": True, + }, + "property": "targetState", + "propertyName": "targetState", + } + + # state doesn't change until currentState value update is received + state = hass.states.get(GDC_COVER_ENTITY) + assert state.state == STATE_CLOSED + + client.async_send_command.reset_mock() + + # Barrier sends an opening state + event = Event( + type="value updated", + data={ + "source": "node", + "event": "value updated", + "nodeId": 12, + "args": { + "commandClassName": "Barrier Operator", + "commandClass": 102, + "endpoint": 0, + "property": "currentState", + "newValue": 254, + "prevValue": 0, + "propertyName": "currentState", + }, + }, + ) + node.receive_event(event) + + state = hass.states.get(GDC_COVER_ENTITY) + assert state.state == STATE_OPENING + + # Barrier sends an opened state + event = Event( + type="value updated", + data={ + "source": "node", + "event": "value updated", + "nodeId": 12, + "args": { + "commandClassName": "Barrier Operator", + "commandClass": 102, + "endpoint": 0, + "property": "currentState", + "newValue": 255, + "prevValue": 254, + "propertyName": "currentState", + }, + }, + ) + node.receive_event(event) + + state = hass.states.get(GDC_COVER_ENTITY) + assert state.state == STATE_OPEN + + # Barrier sends a closing state + event = Event( + type="value updated", + data={ + "source": "node", + "event": "value updated", + "nodeId": 12, + "args": { + "commandClassName": "Barrier Operator", + "commandClass": 102, + "endpoint": 0, + "property": "currentState", + "newValue": 252, + "prevValue": 255, + "propertyName": "currentState", + }, + }, + ) + node.receive_event(event) + + state = hass.states.get(GDC_COVER_ENTITY) + assert state.state == STATE_CLOSING + + # Barrier sends a closed state + event = Event( + type="value updated", + data={ + "source": "node", + "event": "value updated", + "nodeId": 12, + "args": { + "commandClassName": "Barrier Operator", + "commandClass": 102, + "endpoint": 0, + "property": "currentState", + "newValue": 0, + "prevValue": 252, + "propertyName": "currentState", + }, + }, + ) + node.receive_event(event) + + state = hass.states.get(GDC_COVER_ENTITY) + assert state.state == STATE_CLOSED + + # Barrier sends a stopped state + event = Event( + type="value updated", + data={ + "source": "node", + "event": "value updated", + "nodeId": 12, + "args": { + "commandClassName": "Barrier Operator", + "commandClass": 102, + "endpoint": 0, + "property": "currentState", + "newValue": 253, + "prevValue": 252, + "propertyName": "currentState", + }, + }, + ) + node.receive_event(event) + + state = hass.states.get(GDC_COVER_ENTITY) + assert state.state == STATE_UNKNOWN diff --git a/tests/components/zwave_js/test_switch.py b/tests/components/zwave_js/test_switch.py index a1d177cc5d8..ea6e27d9b72 100644 --- a/tests/components/zwave_js/test_switch.py +++ b/tests/components/zwave_js/test_switch.py @@ -2,6 +2,9 @@ from zwave_js_server.event import Event +from homeassistant.components.switch import DOMAIN, SERVICE_TURN_OFF, SERVICE_TURN_ON +from homeassistant.const import STATE_OFF, STATE_ON + from .common import SWITCH_ENTITY @@ -83,3 +86,141 @@ async def test_switch(hass, hank_binary_switch, integration, client): "value": False, } assert args["value"] is False + + +async def test_barrier_signaling_switch(hass, gdc_zw062, integration, client): + """Test barrier signaling state switch.""" + node = gdc_zw062 + entity = "switch.aeon_labs_garage_door_controller_gen5_signaling_state_visual" + + state = hass.states.get(entity) + assert state + assert state.state == "on" + + # Test turning off + await hass.services.async_call( + DOMAIN, SERVICE_TURN_OFF, {"entity_id": entity}, blocking=True + ) + + assert len(client.async_send_command.call_args_list) == 1 + args = client.async_send_command.call_args[0][0] + assert args["command"] == "node.set_value" + assert args["nodeId"] == 12 + assert args["value"] == 0 + assert args["valueId"] == { + "ccVersion": 0, + "commandClass": 102, + "commandClassName": "Barrier Operator", + "endpoint": 0, + "metadata": { + "label": "Signaling State (Visual)", + "max": 255, + "min": 0, + "readable": True, + "states": {"0": "Off", "255": "On"}, + "type": "number", + "writeable": True, + }, + "property": "signalingState", + "propertyKey": 2, + "propertyKeyName": "2", + "propertyName": "signalingState", + "value": 255, + } + + # state change is optimistic and writes state + await hass.async_block_till_done() + + state = hass.states.get(entity) + assert state.state == STATE_OFF + + client.async_send_command.reset_mock() + + # Test turning on + await hass.services.async_call( + DOMAIN, SERVICE_TURN_ON, {"entity_id": entity}, blocking=True + ) + + # Note: the valueId's value is still 255 because we never + # received an updated value + assert len(client.async_send_command.call_args_list) == 1 + args = client.async_send_command.call_args[0][0] + assert args["command"] == "node.set_value" + assert args["nodeId"] == 12 + assert args["value"] == 255 + assert args["valueId"] == { + "ccVersion": 0, + "commandClass": 102, + "commandClassName": "Barrier Operator", + "endpoint": 0, + "metadata": { + "label": "Signaling State (Visual)", + "max": 255, + "min": 0, + "readable": True, + "states": {"0": "Off", "255": "On"}, + "type": "number", + "writeable": True, + }, + "property": "signalingState", + "propertyKey": 2, + "propertyKeyName": "2", + "propertyName": "signalingState", + "value": 255, + } + + # state change is optimistic and writes state + await hass.async_block_till_done() + + state = hass.states.get(entity) + assert state.state == STATE_ON + + # Received a refresh off + event = Event( + type="value updated", + data={ + "source": "node", + "event": "value updated", + "nodeId": 12, + "args": { + "commandClassName": "Barrier Operator", + "commandClass": 102, + "endpoint": 0, + "property": "signalingState", + "propertyKey": 2, + "newValue": 0, + "prevValue": 0, + "propertyName": "signalingState", + "propertyKeyName": "2", + }, + }, + ) + node.receive_event(event) + + state = hass.states.get(entity) + assert state.state == STATE_OFF + + # Received a refresh off + event = Event( + type="value updated", + data={ + "source": "node", + "event": "value updated", + "nodeId": 12, + "args": { + "commandClassName": "Barrier Operator", + "commandClass": 102, + "endpoint": 0, + "property": "signalingState", + "propertyKey": 2, + "newValue": 255, + "prevValue": 255, + "propertyName": "signalingState", + "propertyKeyName": "2", + }, + }, + ) + node.receive_event(event) + + state = hass.states.get(entity) + assert state.state == STATE_ON diff --git a/tests/fixtures/zwave_js/cover_zw062_state.json b/tests/fixtures/zwave_js/cover_zw062_state.json new file mode 100644 index 00000000000..107225e0dcc --- /dev/null +++ b/tests/fixtures/zwave_js/cover_zw062_state.json @@ -0,0 +1,936 @@ +{ + "nodeId": 12, + "index": 0, + "installerIcon": 7680, + "userIcon": 7680, + "status": 4, + "ready": true, + "deviceClass": { + "basic": "Routing Slave", + "generic": "Entry Control", + "specific": "Secure Barrier Add-on", + "mandatorySupportedCCs": [ + "Application Status", + "Association", + "Association Group Information", + "Barrier Operator", + "Battery", + "Device Reset Locally", + "Manufacturer Specific", + "Notification", + "Powerlevel", + "Security", + "Security 2", + "Supervision", + "Transport Service", + "Version", + "Z-Wave Plus Info" + ], + "mandatoryControlCCs": [] + }, + "isListening": true, + "isFrequentListening": false, + "isRouting": true, + "maxBaudRate": 40000, + "isSecure": true, + "version": 4, + "isBeaming": true, + "manufacturerId": 134, + "productId": 62, + "productType": 259, + "firmwareVersion": "1.12", + "zwavePlusVersion": 1, + "nodeType": 0, + "roleType": 5, + "deviceConfig": { + "manufacturerId": 134, + "manufacturer": "AEON Labs", + "label": "ZW062", + "description": "Aeon Labs Garage Door Controller Gen5", + "devices": [ + { + "productType": "0x0003", + "productId": "0x003e" + }, + { + "productType": "0x0103", + "productId": "0x003e" + }, + { + "productType": "0x0203", + "productId": "0x003e" + } + ], + "firmwareVersion": { + "min": "0.0", + "max": "255.255" + }, + "associations": {}, + "paramInformation": { + "_map": {} + } + }, + "label": "ZW062", + "neighbors": [ + 1, + 8, + 11, + 15, + 19, + 21, + 22, + 24, + 25, + 26, + 27, + 29 + ], + "interviewAttempts": 1, + "endpoints": [ + { + "nodeId": 12, + "index": 0, + "installerIcon": 7680, + "userIcon": 7680 + } + ], + "values": [ + { + "endpoint": 0, + "commandClass": 37, + "commandClassName": "Binary Switch", + "property": "currentValue", + "propertyName": "currentValue", + "ccVersion": 1, + "metadata": { + "type": "boolean", + "readable": true, + "writeable": false, + "label": "Current value" + }, + "value": false + }, + { + "endpoint": 0, + "commandClass": 37, + "commandClassName": "Binary Switch", + "property": "targetValue", + "propertyName": "targetValue", + "ccVersion": 1, + "metadata": { + "type": "boolean", + "readable": true, + "writeable": true, + "label": "Target value" + } + }, + { + "endpoint": 0, + "commandClass": 102, + "commandClassName": "Barrier Operator", + "property": "currentState", + "propertyName": "currentState", + "ccVersion": 0, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "min": 0, + "max": 255, + "label": "Current Barrier State", + "states": { + "0": "Closed", + "252": "Closing", + "253": "Stopped", + "254": "Opening", + "255": "Open" + } + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 102, + "commandClassName": "Barrier Operator", + "property": "position", + "propertyName": "position", + "ccVersion": 0, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "min": 0, + "max": 100, + "label": "Barrier Position", + "unit": "%" + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 102, + "commandClassName": "Barrier Operator", + "property": "signalingState", + "propertyKey": 1, + "propertyName": "signalingState", + "propertyKeyName": "1", + "ccVersion": 0, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "min": 0, + "max": 255, + "label": "Signaling State (Audible)", + "states": { + "0": "Off", + "255": "On" + } + }, + "value": 255 + }, + { + "endpoint": 0, + "commandClass": 102, + "commandClassName": "Barrier Operator", + "property": "signalingState", + "propertyKey": 2, + "propertyName": "signalingState", + "propertyKeyName": "2", + "ccVersion": 0, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "min": 0, + "max": 255, + "label": "Signaling State (Visual)", + "states": { + "0": "Off", + "255": "On" + } + }, + "value": 255 + }, + { + "endpoint": 0, + "commandClass": 102, + "commandClassName": "Barrier Operator", + "property": "targetState", + "propertyName": "targetState", + "ccVersion": 0, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "min": 0, + "max": 255, + "label": "Target Barrier State", + "states": { + "0": "Closed", + "255": "Open" + } + } + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 32, + "propertyName": "Startup ringtone", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 1, + "min": 1, + "max": 100, + "default": 1, + "format": 0, + "allowManualEntry": true, + "label": "Startup ringtone", + "description": "Configure the default startup ringtone", + "isFromConfig": true + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 35, + "propertyName": "Calibration timout", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 2, + "min": 1, + "max": 255, + "default": 60, + "format": 0, + "allowManualEntry": true, + "label": "Calibration timout", + "description": "Set the timeout of all calibration steps for the Sensor.", + "isFromConfig": true + }, + "value": 13 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 36, + "propertyName": "Number of alarm musics", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "valueSize": 1, + "min": 1, + "max": 100, + "default": 1, + "format": 0, + "allowManualEntry": true, + "label": "Number of alarm musics", + "description": "Get the number of alarm musics", + "isFromConfig": true + }, + "value": 5 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 39, + "propertyName": "Unknown state alarm mode", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 4, + "min": 0, + "max": 0, + "default": 0, + "format": 0, + "allowManualEntry": true, + "label": "Unknown state alarm mode", + "description": "Configuration alarm mode when the garage door is in \"unknown\" state", + "isFromConfig": true + }, + "value": 100927488 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 40, + "propertyName": "Closed alarm mode", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 4, + "min": 0, + "max": 0, + "default": 0, + "format": 0, + "allowManualEntry": true, + "label": "Closed alarm mode", + "description": "Configure the alarm mode when the garage door is in closed position.", + "isFromConfig": true + }, + "value": 33883392 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 41, + "propertyName": "Tamper switch configuration", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 1, + "min": 0, + "max": 255, + "default": 0, + "format": 1, + "allowManualEntry": true, + "label": "Tamper switch configuration", + "description": "Configuration report for the tamper switch State", + "isFromConfig": true + }, + "value": 15 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 42, + "propertyName": "Battery state", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "valueSize": 1, + "min": 0, + "max": 16, + "default": 0, + "format": 0, + "allowManualEntry": true, + "label": "Battery state", + "description": "Configuration report for the battery state of Sensor", + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 45, + "propertyName": "Temperature", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "valueSize": 2, + "min": 0, + "max": 500, + "default": 0, + "format": 0, + "allowManualEntry": true, + "label": "Temperature", + "description": "Get the environment temperature", + "isFromConfig": true + }, + "value": 550 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 47, + "propertyName": "Button definition", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 1, + "min": 0, + "max": 1, + "default": 0, + "format": 0, + "allowManualEntry": false, + "states": { + "0": "Mode 0", + "1": "Mode 1" + }, + "label": "Button definition", + "description": "Define the function of Button- or Button+.", + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 80, + "propertyName": "Door state change report type", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 1, + "min": 1, + "max": 2, + "default": 2, + "format": 0, + "allowManualEntry": false, + "states": { + "1": "Send hail CC", + "2": "Send barrier operator report CC" + }, + "label": "Door state change report type", + "description": "Configure the door state change report type", + "isFromConfig": true + }, + "value": 2 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 241, + "propertyName": "Pair the Sensor", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 4, + "min": 0, + "max": 1431655681, + "default": 0, + "format": 0, + "allowManualEntry": false, + "states": { + "0": "Stop sensor pairing", + "1431655681": "Start sensor pairing" + }, + "label": "Pair the Sensor", + "description": "Pair the Sensor with Garage Door Controller", + "isFromConfig": true + }, + "value": 33554943 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 252, + "propertyName": "Lock Configuration", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 1, + "min": 0, + "max": 1, + "default": 0, + "format": 0, + "allowManualEntry": false, + "states": { + "0": "Configuration enabled", + "1": "Configuration disabled (locked)" + }, + "label": "Lock Configuration", + "description": "Enable/disable configuration", + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 37, + "propertyKey": 255, + "propertyName": "Disable opening alarm", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 4, + "min": 0, + "max": 1, + "default": 1, + "format": 0, + "allowManualEntry": false, + "states": { + "0": "Disable alarm prompt", + "1": "Enable alarm prompt" + }, + "label": "Disable opening alarm", + "isFromConfig": true + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 37, + "propertyKey": 65280, + "propertyName": "Opening alarm volume", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 4, + "min": 1, + "max": 10, + "default": 8, + "format": 0, + "allowManualEntry": true, + "label": "Opening alarm volume", + "isFromConfig": true + }, + "value": 1 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 37, + "propertyKey": 16711680, + "propertyName": "Opening alarm choice", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 4, + "min": 1, + "max": 4, + "default": 1, + "format": 0, + "allowManualEntry": true, + "label": "Opening alarm choice", + "description": "Alarm mode when the garage door is opening", + "isFromConfig": true + }, + "value": 2 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 37, + "propertyKey": 251658240, + "propertyName": "Opening alarm LED mode", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 4, + "min": 1, + "max": 10, + "default": 10, + "format": 0, + "allowManualEntry": true, + "label": "Opening alarm LED mode", + "isFromConfig": true + }, + "value": 5 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 38, + "propertyKey": 255, + "propertyName": "Disable closing alarm", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 4, + "min": 0, + "max": 1, + "default": 1, + "format": 0, + "allowManualEntry": false, + "states": { + "0": "Disable alarm prompt", + "1": "Enable alarm prompt" + }, + "label": "Disable closing alarm", + "isFromConfig": true + }, + "value": 0 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 38, + "propertyKey": 65280, + "propertyName": "Closing alarm volume", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 4, + "min": 1, + "max": 10, + "default": 8, + "format": 0, + "allowManualEntry": true, + "label": "Closing alarm volume", + "isFromConfig": true + }, + "value": 8 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 38, + "propertyKey": 16711680, + "propertyName": "Closing alarm choice", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 4, + "min": 1, + "max": 4, + "default": 2, + "format": 0, + "allowManualEntry": true, + "label": "Closing alarm choice", + "description": "Alarm mode when the garage door is closing", + "isFromConfig": true + }, + "value": 3 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 38, + "propertyKey": 251658240, + "propertyName": "Closing alarm LED mode", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": true, + "writeable": true, + "valueSize": 4, + "min": 1, + "max": 10, + "default": 6, + "format": 0, + "allowManualEntry": true, + "label": "Closing alarm LED mode", + "isFromConfig": true + }, + "value": 8 + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 34, + "propertyName": "Sensor Calibration", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": false, + "writeable": true, + "valueSize": 1, + "min": 0, + "max": 1, + "default": 0, + "format": 0, + "allowManualEntry": false, + "states": { + "0": "Calibration not active", + "1": "Begin calibration" + }, + "label": "Sensor Calibration", + "description": "Perform Sensor Calibration", + "isFromConfig": true + } + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 43, + "propertyName": "Play or Pause ringtone", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": false, + "writeable": true, + "valueSize": 1, + "min": 1, + "max": 255, + "default": 1, + "format": 1, + "allowManualEntry": true, + "label": "Play or Pause ringtone", + "description": "Start playing or Stop playing the ringtone", + "isFromConfig": true + } + }, + { + "endpoint": 0, + "commandClass": 112, + "commandClassName": "Configuration", + "property": 44, + "propertyName": "Ringtone test volume", + "ccVersion": 1, + "metadata": { + "type": "number", + "readable": false, + "writeable": true, + "valueSize": 1, + "min": 1, + "max": 10, + "default": 1, + "format": 0, + "allowManualEntry": true, + "label": "Ringtone test volume", + "description": "Set volume for test of ringtone", + "isFromConfig": true + } + }, + { + "endpoint": 0, + "commandClass": 113, + "commandClassName": "Notification", + "property": "alarmType", + "propertyName": "alarmType", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "min": 0, + "max": 255, + "label": "Alarm Type" + } + }, + { + "endpoint": 0, + "commandClass": 113, + "commandClassName": "Notification", + "property": "alarmLevel", + "propertyName": "alarmLevel", + "ccVersion": 4, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "min": 0, + "max": 255, + "label": "Alarm Level" + } + }, + { + "endpoint": 0, + "commandClass": 114, + "commandClassName": "Manufacturer Specific", + "property": "manufacturerId", + "propertyName": "manufacturerId", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "min": 0, + "max": 65535, + "label": "Manufacturer ID" + }, + "value": 134 + }, + { + "endpoint": 0, + "commandClass": 114, + "commandClassName": "Manufacturer Specific", + "property": "productType", + "propertyName": "productType", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "min": 0, + "max": 65535, + "label": "Product type" + }, + "value": 259 + }, + { + "endpoint": 0, + "commandClass": 114, + "commandClassName": "Manufacturer Specific", + "property": "productId", + "propertyName": "productId", + "ccVersion": 2, + "metadata": { + "type": "number", + "readable": true, + "writeable": false, + "min": 0, + "max": 65535, + "label": "Product ID" + }, + "value": 62 + }, + { + "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": "3.99" + }, + { + "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": [ + "1.12" + ] + }, + { + "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" + } + } + ] +}