Add binary_sensor platform to zwave_js (#45081)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
e1427c45f2
commit
071c8cc67d
7 changed files with 811 additions and 1 deletions
354
homeassistant/components/zwave_js/binary_sensor.py
Normal file
354
homeassistant/components/zwave_js/binary_sensor.py
Normal file
|
@ -0,0 +1,354 @@
|
||||||
|
"""Representation of Z-Wave binary sensors."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Callable, List, Optional, TypedDict
|
||||||
|
|
||||||
|
from zwave_js_server.client import Client as ZwaveClient
|
||||||
|
from zwave_js_server.const import CommandClass
|
||||||
|
|
||||||
|
from homeassistant.components.binary_sensor import (
|
||||||
|
DEVICE_CLASS_BATTERY,
|
||||||
|
DEVICE_CLASS_DOOR,
|
||||||
|
DEVICE_CLASS_GAS,
|
||||||
|
DEVICE_CLASS_HEAT,
|
||||||
|
DEVICE_CLASS_LOCK,
|
||||||
|
DEVICE_CLASS_MOISTURE,
|
||||||
|
DEVICE_CLASS_MOTION,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
DEVICE_CLASS_PROBLEM,
|
||||||
|
DEVICE_CLASS_SAFETY,
|
||||||
|
DEVICE_CLASS_SMOKE,
|
||||||
|
DEVICE_CLASS_SOUND,
|
||||||
|
DOMAIN as BINARY_SENSOR_DOMAIN,
|
||||||
|
BinarySensorEntity,
|
||||||
|
)
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
|
||||||
|
from .const import DATA_CLIENT, DATA_UNSUBSCRIBE, DOMAIN
|
||||||
|
from .discovery import ZwaveDiscoveryInfo
|
||||||
|
from .entity import ZWaveBaseEntity
|
||||||
|
|
||||||
|
LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
NOTIFICATION_SMOKE_ALARM = 1
|
||||||
|
NOTIFICATION_CARBON_MONOOXIDE = 2
|
||||||
|
NOTIFICATION_CARBON_DIOXIDE = 3
|
||||||
|
NOTIFICATION_HEAT = 4
|
||||||
|
NOTIFICATION_WATER = 5
|
||||||
|
NOTIFICATION_ACCESS_CONTROL = 6
|
||||||
|
NOTIFICATION_HOME_SECURITY = 7
|
||||||
|
NOTIFICATION_POWER_MANAGEMENT = 8
|
||||||
|
NOTIFICATION_SYSTEM = 9
|
||||||
|
NOTIFICATION_EMERGENCY = 10
|
||||||
|
NOTIFICATION_CLOCK = 11
|
||||||
|
NOTIFICATION_APPLIANCE = 12
|
||||||
|
NOTIFICATION_HOME_HEALTH = 13
|
||||||
|
NOTIFICATION_SIREN = 14
|
||||||
|
NOTIFICATION_WATER_VALVE = 15
|
||||||
|
NOTIFICATION_WEATHER = 16
|
||||||
|
NOTIFICATION_IRRIGATION = 17
|
||||||
|
NOTIFICATION_GAS = 18
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationSensorMapping(TypedDict, total=False):
|
||||||
|
"""Represent a notification sensor mapping dict type."""
|
||||||
|
|
||||||
|
type: int # required
|
||||||
|
states: List[int] # required
|
||||||
|
device_class: str
|
||||||
|
enabled: bool
|
||||||
|
|
||||||
|
|
||||||
|
# Mappings for Notification sensors
|
||||||
|
NOTIFICATION_SENSOR_MAPPINGS: List[NotificationSensorMapping] = [
|
||||||
|
{
|
||||||
|
# NotificationType 1: Smoke Alarm - State Id's 1 and 2
|
||||||
|
# Assuming here that Value 1 and 2 are not present at the same time
|
||||||
|
"type": NOTIFICATION_SMOKE_ALARM,
|
||||||
|
"states": [1, 2],
|
||||||
|
"device_class": DEVICE_CLASS_SMOKE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 1: Smoke Alarm - All other State Id's
|
||||||
|
# Create as disabled sensors
|
||||||
|
"type": NOTIFICATION_SMOKE_ALARM,
|
||||||
|
"states": [3, 4, 5, 6, 7, 8],
|
||||||
|
"device_class": DEVICE_CLASS_SMOKE,
|
||||||
|
"enabled": False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 2: Carbon Monoxide - State Id's 1 and 2
|
||||||
|
"type": NOTIFICATION_CARBON_MONOOXIDE,
|
||||||
|
"states": [1, 2],
|
||||||
|
"device_class": DEVICE_CLASS_GAS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 2: Carbon Monoxide - All other State Id's
|
||||||
|
"type": NOTIFICATION_CARBON_MONOOXIDE,
|
||||||
|
"states": [4, 5, 7],
|
||||||
|
"device_class": DEVICE_CLASS_GAS,
|
||||||
|
"enabled": False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 3: Carbon Dioxide - State Id's 1 and 2
|
||||||
|
"type": NOTIFICATION_CARBON_DIOXIDE,
|
||||||
|
"states": [1, 2],
|
||||||
|
"device_class": DEVICE_CLASS_GAS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 3: Carbon Dioxide - All other State Id's
|
||||||
|
"type": NOTIFICATION_CARBON_DIOXIDE,
|
||||||
|
"states": [4, 5, 7],
|
||||||
|
"device_class": DEVICE_CLASS_GAS,
|
||||||
|
"enabled": False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 4: Heat - State Id's 1, 2, 5, 6 (heat/underheat)
|
||||||
|
"type": NOTIFICATION_HEAT,
|
||||||
|
"states": [1, 2, 5, 6],
|
||||||
|
"device_class": DEVICE_CLASS_HEAT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 4: Heat - All other State Id's
|
||||||
|
"type": NOTIFICATION_HEAT,
|
||||||
|
"states": [3, 4, 8, 10, 11],
|
||||||
|
"device_class": DEVICE_CLASS_HEAT,
|
||||||
|
"enabled": False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 5: Water - State Id's 1, 2, 3, 4
|
||||||
|
"type": NOTIFICATION_WATER,
|
||||||
|
"states": [1, 2, 3, 4],
|
||||||
|
"device_class": DEVICE_CLASS_MOISTURE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 5: Water - All other State Id's
|
||||||
|
"type": NOTIFICATION_WATER,
|
||||||
|
"states": [5],
|
||||||
|
"device_class": DEVICE_CLASS_MOISTURE,
|
||||||
|
"enabled": False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 6: Access Control - State Id's 1, 2, 3, 4 (Lock)
|
||||||
|
"type": NOTIFICATION_ACCESS_CONTROL,
|
||||||
|
"states": [1, 2, 3, 4],
|
||||||
|
"device_class": DEVICE_CLASS_LOCK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 6: Access Control - State Id 22 (door/window open)
|
||||||
|
"type": NOTIFICATION_ACCESS_CONTROL,
|
||||||
|
"states": [22],
|
||||||
|
"device_class": DEVICE_CLASS_DOOR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 7: Home Security - State Id's 1, 2 (intrusion)
|
||||||
|
# Assuming that value 1 and 2 are not present at the same time
|
||||||
|
"type": NOTIFICATION_HOME_SECURITY,
|
||||||
|
"states": [1, 2],
|
||||||
|
"device_class": DEVICE_CLASS_SAFETY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 7: Home Security - State Id's 3, 4, 9 (tampering)
|
||||||
|
"type": NOTIFICATION_HOME_SECURITY,
|
||||||
|
"states": [3, 4, 9],
|
||||||
|
"device_class": DEVICE_CLASS_SAFETY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 7: Home Security - State Id's 5, 6 (glass breakage)
|
||||||
|
# Assuming that value 5 and 6 are not present at the same time
|
||||||
|
"type": NOTIFICATION_HOME_SECURITY,
|
||||||
|
"states": [5, 6],
|
||||||
|
"device_class": DEVICE_CLASS_SAFETY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 7: Home Security - State Id's 7, 8 (motion)
|
||||||
|
"type": NOTIFICATION_HOME_SECURITY,
|
||||||
|
"states": [7, 8],
|
||||||
|
"device_class": DEVICE_CLASS_MOTION,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 8: Power management - Values 1...9
|
||||||
|
"type": NOTIFICATION_POWER_MANAGEMENT,
|
||||||
|
"states": [1, 2, 3, 4, 5, 6, 7, 8, 9],
|
||||||
|
"device_class": DEVICE_CLASS_POWER,
|
||||||
|
"enabled": False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 8: Power management - Values 10...15
|
||||||
|
# Battery values (mutually exclusive)
|
||||||
|
"type": NOTIFICATION_POWER_MANAGEMENT,
|
||||||
|
"states": [10, 11, 12, 13, 14, 15],
|
||||||
|
"device_class": DEVICE_CLASS_BATTERY,
|
||||||
|
"enabled": False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 9: System - State Id's 1, 2, 6, 7
|
||||||
|
"type": NOTIFICATION_SYSTEM,
|
||||||
|
"states": [1, 2, 6, 7],
|
||||||
|
"device_class": DEVICE_CLASS_PROBLEM,
|
||||||
|
"enabled": False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 10: Emergency - State Id's 1, 2, 3
|
||||||
|
"type": NOTIFICATION_EMERGENCY,
|
||||||
|
"states": [1, 2, 3],
|
||||||
|
"device_class": DEVICE_CLASS_PROBLEM,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 11: Clock - State Id's 1, 2
|
||||||
|
"type": NOTIFICATION_CLOCK,
|
||||||
|
"states": [1, 2],
|
||||||
|
"enabled": False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 12: Appliance - All State Id's
|
||||||
|
"type": NOTIFICATION_APPLIANCE,
|
||||||
|
"states": list(range(1, 22)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 13: Home Health - State Id's 1,2,3,4,5
|
||||||
|
"type": NOTIFICATION_APPLIANCE,
|
||||||
|
"states": [1, 2, 3, 4, 5],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 14: Siren
|
||||||
|
"type": NOTIFICATION_SIREN,
|
||||||
|
"states": [1],
|
||||||
|
"device_class": DEVICE_CLASS_SOUND,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 15: Water valve
|
||||||
|
# ignore non-boolean values
|
||||||
|
"type": NOTIFICATION_WATER_VALVE,
|
||||||
|
"states": [3, 4],
|
||||||
|
"device_class": DEVICE_CLASS_PROBLEM,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 16: Weather
|
||||||
|
"type": NOTIFICATION_WEATHER,
|
||||||
|
"states": [1, 2],
|
||||||
|
"device_class": DEVICE_CLASS_PROBLEM,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 17: Irrigation
|
||||||
|
# ignore non-boolean values
|
||||||
|
"type": NOTIFICATION_IRRIGATION,
|
||||||
|
"states": [1, 2, 3, 4, 5],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 18: Gas
|
||||||
|
"type": NOTIFICATION_GAS,
|
||||||
|
"states": [1, 2, 3, 4],
|
||||||
|
"device_class": DEVICE_CLASS_GAS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
# NotificationType 18: Gas
|
||||||
|
"type": NOTIFICATION_GAS,
|
||||||
|
"states": [6],
|
||||||
|
"device_class": DEVICE_CLASS_PROBLEM,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: Callable
|
||||||
|
) -> None:
|
||||||
|
"""Set up Z-Wave binary sensor from config entry."""
|
||||||
|
client: ZwaveClient = hass.data[DOMAIN][config_entry.entry_id][DATA_CLIENT]
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_add_binary_sensor(info: ZwaveDiscoveryInfo) -> None:
|
||||||
|
"""Add Z-Wave Binary Sensor."""
|
||||||
|
entities: List[ZWaveBaseEntity] = []
|
||||||
|
|
||||||
|
if info.platform_hint == "notification":
|
||||||
|
entities.append(ZWaveNotificationBinarySensor(client, info))
|
||||||
|
else:
|
||||||
|
# boolean sensor
|
||||||
|
entities.append(ZWaveBooleanBinarySensor(client, info))
|
||||||
|
|
||||||
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
hass.data[DOMAIN][config_entry.entry_id][DATA_UNSUBSCRIBE].append(
|
||||||
|
async_dispatcher_connect(
|
||||||
|
hass, f"{DOMAIN}_add_{BINARY_SENSOR_DOMAIN}", async_add_binary_sensor
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ZWaveBooleanBinarySensor(ZWaveBaseEntity, BinarySensorEntity):
|
||||||
|
"""Representation of a Z-Wave binary_sensor."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self) -> bool:
|
||||||
|
"""Return if the sensor is on or off."""
|
||||||
|
return bool(self.info.primary_value.value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_class(self) -> Optional[str]:
|
||||||
|
"""Return device class."""
|
||||||
|
if self.info.primary_value.command_class == CommandClass.BATTERY:
|
||||||
|
return DEVICE_CLASS_BATTERY
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def entity_registry_enabled_default(self) -> bool:
|
||||||
|
"""Return if the entity should be enabled when first added to the entity registry."""
|
||||||
|
if self.info.primary_value.command_class == CommandClass.SENSOR_BINARY:
|
||||||
|
# Legacy binary sensors are phased out (replaced by notification sensors)
|
||||||
|
# Disable by default to not confuse users
|
||||||
|
if self.info.node.device_class.generic != "Binary Sensor":
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class ZWaveNotificationBinarySensor(ZWaveBaseEntity, BinarySensorEntity):
|
||||||
|
"""Representation of a Z-Wave binary_sensor from Notification CommandClass."""
|
||||||
|
|
||||||
|
def __init__(self, client: ZwaveClient, info: ZwaveDiscoveryInfo) -> None:
|
||||||
|
"""Initialize a ZWaveNotificationBinarySensor entity."""
|
||||||
|
super().__init__(client, info)
|
||||||
|
# check if we have a custom mapping for this value
|
||||||
|
self._mapping_info = self._get_sensor_mapping()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self) -> bool:
|
||||||
|
"""Return if the sensor is on or off."""
|
||||||
|
if self._mapping_info:
|
||||||
|
return self.info.primary_value.value in self._mapping_info["states"]
|
||||||
|
return bool(self.info.primary_value.value != 0)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_class(self) -> Optional[str]:
|
||||||
|
"""Return device class."""
|
||||||
|
return self._mapping_info.get("device_class")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def entity_registry_enabled_default(self) -> bool:
|
||||||
|
"""Return if the entity should be enabled when first added to the entity registry."""
|
||||||
|
# We hide some more advanced sensors by default to not overwhelm users
|
||||||
|
if not self._mapping_info:
|
||||||
|
# consider value for which we do not have a mapping as advanced.
|
||||||
|
return False
|
||||||
|
return self._mapping_info.get("enabled", True)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _get_sensor_mapping(self) -> NotificationSensorMapping:
|
||||||
|
"""Try to get a device specific mapping for this sensor."""
|
||||||
|
for mapping in NOTIFICATION_SENSOR_MAPPINGS:
|
||||||
|
if mapping["type"] != int(
|
||||||
|
self.info.primary_value.metadata.cc_specific["notificationType"]
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
for state_key in self.info.primary_value.metadata.states:
|
||||||
|
# make sure the key is int
|
||||||
|
state_key = int(state_key)
|
||||||
|
if state_key not in mapping["states"]:
|
||||||
|
continue
|
||||||
|
# match found
|
||||||
|
mapping_info = mapping.copy()
|
||||||
|
return mapping_info
|
||||||
|
return {}
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
DOMAIN = "zwave_js"
|
DOMAIN = "zwave_js"
|
||||||
NAME = "Z-Wave JS"
|
NAME = "Z-Wave JS"
|
||||||
PLATFORMS = ["light", "sensor", "switch"]
|
PLATFORMS = ["binary_sensor", "light", "sensor", "switch"]
|
||||||
|
|
||||||
DATA_CLIENT = "client"
|
DATA_CLIENT = "client"
|
||||||
DATA_UNSUBSCRIBE = "unsubs"
|
DATA_UNSUBSCRIBE = "unsubs"
|
||||||
|
|
|
@ -72,6 +72,24 @@ DISCOVERY_SCHEMAS = [
|
||||||
property={"currentValue"},
|
property={"currentValue"},
|
||||||
type={"number"},
|
type={"number"},
|
||||||
),
|
),
|
||||||
|
# binary sensors
|
||||||
|
ZWaveDiscoverySchema(
|
||||||
|
platform="binary_sensor",
|
||||||
|
hint="boolean",
|
||||||
|
command_class={
|
||||||
|
CommandClass.SENSOR_BINARY,
|
||||||
|
CommandClass.BATTERY,
|
||||||
|
},
|
||||||
|
type={"boolean"},
|
||||||
|
),
|
||||||
|
ZWaveDiscoverySchema(
|
||||||
|
platform="binary_sensor",
|
||||||
|
hint="notification",
|
||||||
|
command_class={
|
||||||
|
CommandClass.NOTIFICATION,
|
||||||
|
},
|
||||||
|
type={"number"},
|
||||||
|
),
|
||||||
# generic text sensors
|
# generic text sensors
|
||||||
ZWaveDiscoverySchema(
|
ZWaveDiscoverySchema(
|
||||||
platform="sensor",
|
platform="sensor",
|
||||||
|
|
|
@ -3,3 +3,7 @@ AIR_TEMPERATURE_SENSOR = "sensor.multisensor_6_air_temperature"
|
||||||
ENERGY_SENSOR = "sensor.smart_plug_with_two_usb_ports_value_electric_consumed_2"
|
ENERGY_SENSOR = "sensor.smart_plug_with_two_usb_ports_value_electric_consumed_2"
|
||||||
POWER_SENSOR = "sensor.smart_plug_with_two_usb_ports_value_electric_consumed"
|
POWER_SENSOR = "sensor.smart_plug_with_two_usb_ports_value_electric_consumed"
|
||||||
SWITCH_ENTITY = "switch.smart_plug_with_two_usb_ports_current_value"
|
SWITCH_ENTITY = "switch.smart_plug_with_two_usb_ports_current_value"
|
||||||
|
LOW_BATTERY_BINARY_SENSOR = "binary_sensor.multisensor_6_low_battery_level"
|
||||||
|
ENABLED_LEGACY_BINARY_SENSOR = "binary_sensor.z_wave_door_window_sensor_any"
|
||||||
|
DISABLED_LEGACY_BINARY_SENSOR = "binary_sensor.multisensor_6_any"
|
||||||
|
NOTIFICATION_MOTION_BINARY_SENSOR = "binary_sensor.multisensor_6_motion_sensor_status"
|
||||||
|
|
|
@ -31,6 +31,12 @@ def multisensor_6_state_fixture():
|
||||||
return json.loads(load_fixture("zwave_js/multisensor_6_state.json"))
|
return json.loads(load_fixture("zwave_js/multisensor_6_state.json"))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="ecolink_door_sensor_state", scope="session")
|
||||||
|
def ecolink_door_sensor_state_fixture():
|
||||||
|
"""Load the Ecolink Door/Window Sensor node state fixture data."""
|
||||||
|
return json.loads(load_fixture("zwave_js/ecolink_door_sensor_state.json"))
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="hank_binary_switch_state", scope="session")
|
@pytest.fixture(name="hank_binary_switch_state", scope="session")
|
||||||
def binary_switch_state_fixture():
|
def binary_switch_state_fixture():
|
||||||
"""Load the hank binary switch node state fixture data."""
|
"""Load the hank binary switch node state fixture data."""
|
||||||
|
@ -62,6 +68,14 @@ def multisensor_6_fixture(client, multisensor_6_state):
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="ecolink_door_sensor")
|
||||||
|
def legacy_binary_sensor_fixture(client, ecolink_door_sensor_state):
|
||||||
|
"""Mock a legacy_binary_sensor node."""
|
||||||
|
node = Node(client, ecolink_door_sensor_state)
|
||||||
|
client.driver.controller.nodes[node.node_id] = node
|
||||||
|
return node
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="hank_binary_switch")
|
@pytest.fixture(name="hank_binary_switch")
|
||||||
def hank_binary_switch_fixture(client, hank_binary_switch_state):
|
def hank_binary_switch_fixture(client, hank_binary_switch_state):
|
||||||
"""Mock a binary switch node."""
|
"""Mock a binary switch node."""
|
||||||
|
|
86
tests/components/zwave_js/test_binary_sensor.py
Normal file
86
tests/components/zwave_js/test_binary_sensor.py
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
"""Test the Z-Wave JS binary sensor platform."""
|
||||||
|
from zwave_js_server.event import Event
|
||||||
|
|
||||||
|
from homeassistant.components.binary_sensor import DEVICE_CLASS_MOTION
|
||||||
|
from homeassistant.const import DEVICE_CLASS_BATTERY, STATE_OFF, STATE_ON
|
||||||
|
|
||||||
|
from .common import (
|
||||||
|
DISABLED_LEGACY_BINARY_SENSOR,
|
||||||
|
ENABLED_LEGACY_BINARY_SENSOR,
|
||||||
|
LOW_BATTERY_BINARY_SENSOR,
|
||||||
|
NOTIFICATION_MOTION_BINARY_SENSOR,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_low_battery_sensor(hass, multisensor_6, integration):
|
||||||
|
"""Test boolean binary sensor of type low battery."""
|
||||||
|
state = hass.states.get(LOW_BATTERY_BINARY_SENSOR)
|
||||||
|
|
||||||
|
assert state
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
assert state.attributes["device_class"] == DEVICE_CLASS_BATTERY
|
||||||
|
|
||||||
|
|
||||||
|
async def test_enabled_legacy_sensor(hass, ecolink_door_sensor, integration):
|
||||||
|
"""Test enabled legacy boolean binary sensor."""
|
||||||
|
node = ecolink_door_sensor
|
||||||
|
# this node has Notification CC not (fully) implemented
|
||||||
|
# so legacy binary sensor should be enabled
|
||||||
|
|
||||||
|
state = hass.states.get(ENABLED_LEGACY_BINARY_SENSOR)
|
||||||
|
assert state
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
assert state.attributes.get("device_class") is None
|
||||||
|
|
||||||
|
# Test state updates from value updated event
|
||||||
|
event = Event(
|
||||||
|
type="value updated",
|
||||||
|
data={
|
||||||
|
"source": "node",
|
||||||
|
"event": "value updated",
|
||||||
|
"nodeId": 53,
|
||||||
|
"args": {
|
||||||
|
"commandClassName": "Binary Sensor",
|
||||||
|
"commandClass": 48,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "Any",
|
||||||
|
"newValue": True,
|
||||||
|
"prevValue": False,
|
||||||
|
"propertyName": "Any",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
node.receive_event(event)
|
||||||
|
|
||||||
|
state = hass.states.get(ENABLED_LEGACY_BINARY_SENSOR)
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
|
||||||
|
|
||||||
|
async def test_disabled_legacy_sensor(hass, multisensor_6, integration):
|
||||||
|
"""Test disabled legacy boolean binary sensor."""
|
||||||
|
# this node has Notification CC implemented so legacy binary sensor should be disabled
|
||||||
|
|
||||||
|
registry = await hass.helpers.entity_registry.async_get_registry()
|
||||||
|
entity_id = DISABLED_LEGACY_BINARY_SENSOR
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state is None
|
||||||
|
entry = registry.async_get(entity_id)
|
||||||
|
assert entry
|
||||||
|
assert entry.disabled
|
||||||
|
assert entry.disabled_by == "integration"
|
||||||
|
|
||||||
|
# Test enabling legacy entity
|
||||||
|
updated_entry = registry.async_update_entity(
|
||||||
|
entry.entity_id, **{"disabled_by": None}
|
||||||
|
)
|
||||||
|
assert updated_entry != entry
|
||||||
|
assert updated_entry.disabled is False
|
||||||
|
|
||||||
|
|
||||||
|
async def test_notification_sensor(hass, multisensor_6, integration):
|
||||||
|
"""Test binary sensor created from Notification CC."""
|
||||||
|
state = hass.states.get(NOTIFICATION_MOTION_BINARY_SENSOR)
|
||||||
|
|
||||||
|
assert state
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
assert state.attributes["device_class"] == DEVICE_CLASS_MOTION
|
334
tests/fixtures/zwave_js/ecolink_door_sensor_state.json
vendored
Normal file
334
tests/fixtures/zwave_js/ecolink_door_sensor_state.json
vendored
Normal file
|
@ -0,0 +1,334 @@
|
||||||
|
{
|
||||||
|
"nodeId": 53,
|
||||||
|
"index": 0,
|
||||||
|
"status": 1,
|
||||||
|
"ready": true,
|
||||||
|
"deviceClass": {
|
||||||
|
"basic": "Static Controller",
|
||||||
|
"generic": "Binary Sensor",
|
||||||
|
"specific": "Routing Binary Sensor",
|
||||||
|
"mandatorySupportedCCs": [
|
||||||
|
"Basic",
|
||||||
|
"Binary Sensor"
|
||||||
|
],
|
||||||
|
"mandatoryControlCCs": [
|
||||||
|
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"isListening": false,
|
||||||
|
"isFrequentListening": false,
|
||||||
|
"isRouting": true,
|
||||||
|
"maxBaudRate": 40000,
|
||||||
|
"isSecure": false,
|
||||||
|
"version": 4,
|
||||||
|
"isBeaming": true,
|
||||||
|
"manufacturerId": 330,
|
||||||
|
"productId": 2,
|
||||||
|
"productType": 1,
|
||||||
|
"firmwareVersion": "2.0",
|
||||||
|
"deviceConfig": {
|
||||||
|
"manufacturerId": 330,
|
||||||
|
"manufacturer": "Ecolink",
|
||||||
|
"label": "DWZWAVE2",
|
||||||
|
"description": "Z-Wave Door/Window Sensor",
|
||||||
|
"devices": [
|
||||||
|
{
|
||||||
|
"productType": "0x0001",
|
||||||
|
"productId": "0x0002"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"firmwareVersion": {
|
||||||
|
"min": "0.0",
|
||||||
|
"max": "255.255"
|
||||||
|
},
|
||||||
|
"associations": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"paramInformation": {
|
||||||
|
"_map": {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"label": "DWZWAVE2",
|
||||||
|
"neighbors": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"interviewAttempts": 1,
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"nodeId": 2,
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"commandClassName": "Basic",
|
||||||
|
"commandClass": 32,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "currentValue",
|
||||||
|
"propertyName": "currentValue",
|
||||||
|
"metadata": {
|
||||||
|
"type": "number",
|
||||||
|
"readable": true,
|
||||||
|
"writeable": false,
|
||||||
|
"min": 0,
|
||||||
|
"max": 99,
|
||||||
|
"label": "Current value"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"commandClassName": "Basic",
|
||||||
|
"commandClass": 32,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "targetValue",
|
||||||
|
"propertyName": "targetValue",
|
||||||
|
"metadata": {
|
||||||
|
"type": "number",
|
||||||
|
"readable": true,
|
||||||
|
"writeable": true,
|
||||||
|
"min": 0,
|
||||||
|
"max": 99,
|
||||||
|
"label": "Target value"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"commandClassName": "Binary Sensor",
|
||||||
|
"commandClass": 48,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "Any",
|
||||||
|
"propertyName": "Any",
|
||||||
|
"metadata": {
|
||||||
|
"type": "boolean",
|
||||||
|
"readable": true,
|
||||||
|
"writeable": false,
|
||||||
|
"label": "Any",
|
||||||
|
"ccSpecific": {
|
||||||
|
"sensorType": 255
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"value": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"commandClassName": "Configuration",
|
||||||
|
"commandClass": 112,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": 1,
|
||||||
|
"propertyName": "Sending Basic Sets to Association group 2",
|
||||||
|
"metadata": {
|
||||||
|
"type": "number",
|
||||||
|
"readable": true,
|
||||||
|
"writeable": true,
|
||||||
|
"valueSize": 1,
|
||||||
|
"min": 0,
|
||||||
|
"max": 255,
|
||||||
|
"default": 0,
|
||||||
|
"format": 1,
|
||||||
|
"allowManualEntry": false,
|
||||||
|
"states": {
|
||||||
|
"0": "Off",
|
||||||
|
"255": "On"
|
||||||
|
},
|
||||||
|
"label": "Sending Basic Sets to Association group 2",
|
||||||
|
"description": "Sending Basic Sets to Association group 2",
|
||||||
|
"isFromConfig": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"commandClassName": "Configuration",
|
||||||
|
"commandClass": 112,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": 2,
|
||||||
|
"propertyName": "Sending sensor binary report",
|
||||||
|
"metadata": {
|
||||||
|
"type": "number",
|
||||||
|
"readable": true,
|
||||||
|
"writeable": true,
|
||||||
|
"valueSize": 1,
|
||||||
|
"min": 0,
|
||||||
|
"max": 255,
|
||||||
|
"default": 0,
|
||||||
|
"format": 1,
|
||||||
|
"allowManualEntry": false,
|
||||||
|
"states": {
|
||||||
|
"0": "Off",
|
||||||
|
"255": "On"
|
||||||
|
},
|
||||||
|
"label": "Sending sensor binary report",
|
||||||
|
"description": "Sending sensor binary report",
|
||||||
|
"isFromConfig": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"commandClassName": "Notification",
|
||||||
|
"commandClass": 113,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "Home Security",
|
||||||
|
"propertyKey": "Cover status",
|
||||||
|
"propertyName": "Home Security",
|
||||||
|
"propertyKeyName": "Cover status",
|
||||||
|
"metadata": {
|
||||||
|
"type": "any",
|
||||||
|
"readable": true,
|
||||||
|
"writeable": true
|
||||||
|
},
|
||||||
|
"value": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"commandClassName": "Manufacturer Specific",
|
||||||
|
"commandClass": 114,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "manufacturerId",
|
||||||
|
"propertyName": "manufacturerId",
|
||||||
|
"metadata": {
|
||||||
|
"type": "number",
|
||||||
|
"readable": true,
|
||||||
|
"writeable": false,
|
||||||
|
"min": 0,
|
||||||
|
"max": 65535,
|
||||||
|
"label": "Manufacturer ID"
|
||||||
|
},
|
||||||
|
"value": 330
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"commandClassName": "Manufacturer Specific",
|
||||||
|
"commandClass": 114,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "productType",
|
||||||
|
"propertyName": "productType",
|
||||||
|
"metadata": {
|
||||||
|
"type": "number",
|
||||||
|
"readable": true,
|
||||||
|
"writeable": false,
|
||||||
|
"min": 0,
|
||||||
|
"max": 65535,
|
||||||
|
"label": "Product type"
|
||||||
|
},
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"commandClassName": "Manufacturer Specific",
|
||||||
|
"commandClass": 114,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "productId",
|
||||||
|
"propertyName": "productId",
|
||||||
|
"metadata": {
|
||||||
|
"type": "number",
|
||||||
|
"readable": true,
|
||||||
|
"writeable": false,
|
||||||
|
"min": 0,
|
||||||
|
"max": 65535,
|
||||||
|
"label": "Product ID"
|
||||||
|
},
|
||||||
|
"value": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"commandClassName": "Battery",
|
||||||
|
"commandClass": 128,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "level",
|
||||||
|
"propertyName": "level",
|
||||||
|
"metadata": {
|
||||||
|
"type": "number",
|
||||||
|
"readable": true,
|
||||||
|
"writeable": false,
|
||||||
|
"min": 0,
|
||||||
|
"max": 100,
|
||||||
|
"unit": "%",
|
||||||
|
"label": "Battery level"
|
||||||
|
},
|
||||||
|
"value": 61
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"commandClassName": "Battery",
|
||||||
|
"commandClass": 128,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "isLow",
|
||||||
|
"propertyName": "isLow",
|
||||||
|
"metadata": {
|
||||||
|
"type": "boolean",
|
||||||
|
"readable": true,
|
||||||
|
"writeable": false,
|
||||||
|
"label": "Low battery level"
|
||||||
|
},
|
||||||
|
"value": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"commandClassName": "Wake Up",
|
||||||
|
"commandClass": 132,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "wakeUpInterval",
|
||||||
|
"propertyName": "wakeUpInterval",
|
||||||
|
"metadata": {
|
||||||
|
"type": "number",
|
||||||
|
"readable": false,
|
||||||
|
"writeable": true,
|
||||||
|
"min": 3600,
|
||||||
|
"max": 604800,
|
||||||
|
"label": "Wake Up interval",
|
||||||
|
"steps": 200,
|
||||||
|
"default": 14400
|
||||||
|
},
|
||||||
|
"value": 14400
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"commandClassName": "Wake Up",
|
||||||
|
"commandClass": 132,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "controllerNodeId",
|
||||||
|
"propertyName": "controllerNodeId",
|
||||||
|
"metadata": {
|
||||||
|
"type": "any",
|
||||||
|
"readable": true,
|
||||||
|
"writeable": false,
|
||||||
|
"label": "Node ID of the controller"
|
||||||
|
},
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"commandClassName": "Version",
|
||||||
|
"commandClass": 134,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "libraryType",
|
||||||
|
"propertyName": "libraryType",
|
||||||
|
"metadata": {
|
||||||
|
"type": "any",
|
||||||
|
"readable": true,
|
||||||
|
"writeable": false,
|
||||||
|
"label": "Library type"
|
||||||
|
},
|
||||||
|
"value": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"commandClassName": "Version",
|
||||||
|
"commandClass": 134,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "protocolVersion",
|
||||||
|
"propertyName": "protocolVersion",
|
||||||
|
"metadata": {
|
||||||
|
"type": "any",
|
||||||
|
"readable": true,
|
||||||
|
"writeable": false,
|
||||||
|
"label": "Z-Wave protocol version"
|
||||||
|
},
|
||||||
|
"value": "3.40"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"commandClassName": "Version",
|
||||||
|
"commandClass": 134,
|
||||||
|
"endpoint": 0,
|
||||||
|
"property": "firmwareVersions",
|
||||||
|
"propertyName": "firmwareVersions",
|
||||||
|
"metadata": {
|
||||||
|
"type": "any",
|
||||||
|
"readable": true,
|
||||||
|
"writeable": false,
|
||||||
|
"label": "Z-Wave chip firmware versions"
|
||||||
|
},
|
||||||
|
"value": [
|
||||||
|
"2.0"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue