mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 21:57:51 +00:00
Parametrize deCONZ binary sensors (#65012)
* Improve test coverage prior to improving deCONZ binary sensor platform * Define all relevant binary sensors as DeconzBinarySensorDescription * Fix review comment * Allow providing extra update keys if sensor provides extra attributes * Minor touch up of naming * Remove duplicate assert
This commit is contained in:
parent
b7007b364a
commit
96c4e33b24
@ -7,7 +7,6 @@ from dataclasses import dataclass
|
|||||||
from pydeconz.sensor import (
|
from pydeconz.sensor import (
|
||||||
Alarm,
|
Alarm,
|
||||||
CarbonMonoxide,
|
CarbonMonoxide,
|
||||||
DeconzBinarySensor as PydeconzBinarySensor,
|
|
||||||
DeconzSensor as PydeconzSensor,
|
DeconzSensor as PydeconzSensor,
|
||||||
Fire,
|
Fire,
|
||||||
GenericFlag,
|
GenericFlag,
|
||||||
@ -34,21 +33,21 @@ from .const import ATTR_DARK, ATTR_ON
|
|||||||
from .deconz_device import DeconzDevice
|
from .deconz_device import DeconzDevice
|
||||||
from .gateway import DeconzGateway, get_gateway_from_config_entry
|
from .gateway import DeconzGateway, get_gateway_from_config_entry
|
||||||
|
|
||||||
DECONZ_BINARY_SENSORS = (
|
|
||||||
Alarm,
|
|
||||||
CarbonMonoxide,
|
|
||||||
Fire,
|
|
||||||
GenericFlag,
|
|
||||||
OpenClose,
|
|
||||||
Presence,
|
|
||||||
Vibration,
|
|
||||||
Water,
|
|
||||||
)
|
|
||||||
|
|
||||||
ATTR_ORIENTATION = "orientation"
|
ATTR_ORIENTATION = "orientation"
|
||||||
ATTR_TILTANGLE = "tiltangle"
|
ATTR_TILTANGLE = "tiltangle"
|
||||||
ATTR_VIBRATIONSTRENGTH = "vibrationstrength"
|
ATTR_VIBRATIONSTRENGTH = "vibrationstrength"
|
||||||
|
|
||||||
|
PROVIDES_EXTRA_ATTRIBUTES = (
|
||||||
|
"alarm",
|
||||||
|
"carbon_monoxide",
|
||||||
|
"fire",
|
||||||
|
"flag",
|
||||||
|
"open",
|
||||||
|
"presence",
|
||||||
|
"vibration",
|
||||||
|
"water",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class DeconzBinarySensorDescriptionMixin:
|
class DeconzBinarySensorDescriptionMixin:
|
||||||
@ -56,7 +55,6 @@ class DeconzBinarySensorDescriptionMixin:
|
|||||||
|
|
||||||
suffix: str
|
suffix: str
|
||||||
update_key: str
|
update_key: str
|
||||||
required_attr: str
|
|
||||||
value_fn: Callable[[PydeconzSensor], bool | None]
|
value_fn: Callable[[PydeconzSensor], bool | None]
|
||||||
|
|
||||||
|
|
||||||
@ -69,41 +67,90 @@ class DeconzBinarySensorDescription(
|
|||||||
|
|
||||||
|
|
||||||
ENTITY_DESCRIPTIONS = {
|
ENTITY_DESCRIPTIONS = {
|
||||||
Alarm: BinarySensorEntityDescription(
|
Alarm: [
|
||||||
key="alarm",
|
DeconzBinarySensorDescription(
|
||||||
device_class=BinarySensorDeviceClass.SAFETY,
|
key="alarm",
|
||||||
),
|
value_fn=lambda device: device.alarm,
|
||||||
CarbonMonoxide: BinarySensorEntityDescription(
|
suffix="",
|
||||||
key="carbonmonoxide",
|
update_key="alarm",
|
||||||
device_class=BinarySensorDeviceClass.CO,
|
device_class=BinarySensorDeviceClass.SAFETY,
|
||||||
),
|
)
|
||||||
Fire: BinarySensorEntityDescription(
|
],
|
||||||
key="fire",
|
CarbonMonoxide: [
|
||||||
device_class=BinarySensorDeviceClass.SMOKE,
|
DeconzBinarySensorDescription(
|
||||||
),
|
key="carbon_monoxide",
|
||||||
OpenClose: BinarySensorEntityDescription(
|
value_fn=lambda device: device.carbon_monoxide,
|
||||||
key="openclose",
|
suffix="",
|
||||||
device_class=BinarySensorDeviceClass.OPENING,
|
update_key="carbonmonoxide",
|
||||||
),
|
device_class=BinarySensorDeviceClass.CO,
|
||||||
Presence: BinarySensorEntityDescription(
|
)
|
||||||
key="presence",
|
],
|
||||||
device_class=BinarySensorDeviceClass.MOTION,
|
Fire: [
|
||||||
),
|
DeconzBinarySensorDescription(
|
||||||
Vibration: BinarySensorEntityDescription(
|
key="fire",
|
||||||
key="vibration",
|
value_fn=lambda device: device.fire,
|
||||||
device_class=BinarySensorDeviceClass.VIBRATION,
|
suffix="",
|
||||||
),
|
update_key="fire",
|
||||||
Water: BinarySensorEntityDescription(
|
device_class=BinarySensorDeviceClass.SMOKE,
|
||||||
key="water",
|
),
|
||||||
device_class=BinarySensorDeviceClass.MOISTURE,
|
DeconzBinarySensorDescription(
|
||||||
),
|
key="in_test_mode",
|
||||||
|
value_fn=lambda device: device.in_test_mode,
|
||||||
|
suffix="Test Mode",
|
||||||
|
update_key="test",
|
||||||
|
device_class=BinarySensorDeviceClass.SMOKE,
|
||||||
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
GenericFlag: [
|
||||||
|
DeconzBinarySensorDescription(
|
||||||
|
key="flag",
|
||||||
|
value_fn=lambda device: device.flag,
|
||||||
|
suffix="",
|
||||||
|
update_key="flag",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
OpenClose: [
|
||||||
|
DeconzBinarySensorDescription(
|
||||||
|
key="open",
|
||||||
|
value_fn=lambda device: device.open,
|
||||||
|
suffix="",
|
||||||
|
update_key="open",
|
||||||
|
device_class=BinarySensorDeviceClass.OPENING,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
Presence: [
|
||||||
|
DeconzBinarySensorDescription(
|
||||||
|
key="presence",
|
||||||
|
value_fn=lambda device: device.presence,
|
||||||
|
suffix="",
|
||||||
|
update_key="presence",
|
||||||
|
device_class=BinarySensorDeviceClass.MOTION,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
Vibration: [
|
||||||
|
DeconzBinarySensorDescription(
|
||||||
|
key="vibration",
|
||||||
|
value_fn=lambda device: device.vibration,
|
||||||
|
suffix="",
|
||||||
|
update_key="vibration",
|
||||||
|
device_class=BinarySensorDeviceClass.VIBRATION,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
Water: [
|
||||||
|
DeconzBinarySensorDescription(
|
||||||
|
key="water",
|
||||||
|
value_fn=lambda device: device.water,
|
||||||
|
suffix="",
|
||||||
|
update_key="water",
|
||||||
|
device_class=BinarySensorDeviceClass.MOISTURE,
|
||||||
|
)
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
BINARY_SENSOR_DESCRIPTIONS = [
|
BINARY_SENSOR_DESCRIPTIONS = [
|
||||||
DeconzBinarySensorDescription(
|
DeconzBinarySensorDescription(
|
||||||
key="tamper",
|
key="tampered",
|
||||||
required_attr="tampered",
|
|
||||||
value_fn=lambda device: device.tampered,
|
value_fn=lambda device: device.tampered,
|
||||||
suffix="Tampered",
|
suffix="Tampered",
|
||||||
update_key="tampered",
|
update_key="tampered",
|
||||||
@ -112,22 +159,12 @@ BINARY_SENSOR_DESCRIPTIONS = [
|
|||||||
),
|
),
|
||||||
DeconzBinarySensorDescription(
|
DeconzBinarySensorDescription(
|
||||||
key="low_battery",
|
key="low_battery",
|
||||||
required_attr="low_battery",
|
|
||||||
value_fn=lambda device: device.low_battery,
|
value_fn=lambda device: device.low_battery,
|
||||||
suffix="Low Battery",
|
suffix="Low Battery",
|
||||||
update_key="lowbattery",
|
update_key="lowbattery",
|
||||||
device_class=BinarySensorDeviceClass.BATTERY,
|
device_class=BinarySensorDeviceClass.BATTERY,
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
DeconzBinarySensorDescription(
|
|
||||||
key="in_test_mode",
|
|
||||||
required_attr="in_test_mode",
|
|
||||||
value_fn=lambda device: device.in_test_mode,
|
|
||||||
suffix="Test Mode",
|
|
||||||
update_key="test",
|
|
||||||
device_class=BinarySensorDeviceClass.SMOKE,
|
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -146,32 +183,26 @@ async def async_setup_entry(
|
|||||||
| ValuesView[PydeconzSensor] = gateway.api.sensors.values(),
|
| ValuesView[PydeconzSensor] = gateway.api.sensors.values(),
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Add binary sensor from deCONZ."""
|
"""Add binary sensor from deCONZ."""
|
||||||
entities: list[DeconzBinarySensor | DeconzPropertyBinarySensor] = []
|
entities: list[DeconzBinarySensor] = []
|
||||||
|
|
||||||
for sensor in sensors:
|
for sensor in sensors:
|
||||||
|
|
||||||
if not gateway.option_allow_clip_sensor and sensor.type.startswith("CLIP"):
|
if not gateway.option_allow_clip_sensor and sensor.type.startswith("CLIP"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if (
|
known_entities = set(gateway.entities[DOMAIN])
|
||||||
isinstance(sensor, DECONZ_BINARY_SENSORS)
|
for description in (
|
||||||
and sensor.unique_id not in gateway.entities[DOMAIN]
|
ENTITY_DESCRIPTIONS.get(type(sensor), []) + BINARY_SENSOR_DESCRIPTIONS
|
||||||
):
|
):
|
||||||
entities.append(DeconzBinarySensor(sensor, gateway))
|
|
||||||
|
|
||||||
known_sensor_entities = set(gateway.entities[DOMAIN])
|
|
||||||
for sensor_description in BINARY_SENSOR_DESCRIPTIONS:
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
not hasattr(sensor, sensor_description.required_attr)
|
not hasattr(sensor, description.key)
|
||||||
or sensor_description.value_fn(sensor) is None
|
or description.value_fn(sensor) is None
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
new_sensor = DeconzPropertyBinarySensor(
|
new_sensor = DeconzBinarySensor(sensor, gateway, description)
|
||||||
sensor, gateway, sensor_description
|
if new_sensor.unique_id not in known_entities:
|
||||||
)
|
|
||||||
if new_sensor.unique_id not in known_sensor_entities:
|
|
||||||
entities.append(new_sensor)
|
entities.append(new_sensor)
|
||||||
|
|
||||||
if entities:
|
if entities:
|
||||||
@ -194,30 +225,50 @@ class DeconzBinarySensor(DeconzDevice, BinarySensorEntity):
|
|||||||
"""Representation of a deCONZ binary sensor."""
|
"""Representation of a deCONZ binary sensor."""
|
||||||
|
|
||||||
TYPE = DOMAIN
|
TYPE = DOMAIN
|
||||||
_device: PydeconzBinarySensor
|
_device: PydeconzSensor
|
||||||
|
entity_description: DeconzBinarySensorDescription
|
||||||
|
|
||||||
def __init__(self, device: PydeconzBinarySensor, gateway: DeconzGateway) -> None:
|
def __init__(
|
||||||
|
self,
|
||||||
|
device: PydeconzSensor,
|
||||||
|
gateway: DeconzGateway,
|
||||||
|
description: DeconzBinarySensorDescription,
|
||||||
|
) -> None:
|
||||||
"""Initialize deCONZ binary sensor."""
|
"""Initialize deCONZ binary sensor."""
|
||||||
|
self.entity_description: DeconzBinarySensorDescription = description
|
||||||
super().__init__(device, gateway)
|
super().__init__(device, gateway)
|
||||||
|
|
||||||
if entity_description := ENTITY_DESCRIPTIONS.get(type(device)):
|
if description.suffix:
|
||||||
self.entity_description = entity_description
|
self._attr_name = f"{self._device.name} {description.suffix}"
|
||||||
|
|
||||||
|
self._update_keys = {description.update_key, "reachable"}
|
||||||
|
if self.entity_description.key in PROVIDES_EXTRA_ATTRIBUTES:
|
||||||
|
self._update_keys.update({"on", "state"})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self) -> str:
|
||||||
|
"""Return a unique identifier for this device."""
|
||||||
|
if self.entity_description.suffix:
|
||||||
|
return f"{self.serial}-{self.entity_description.suffix.lower()}"
|
||||||
|
return super().unique_id
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_callback(self) -> None:
|
def async_update_callback(self) -> None:
|
||||||
"""Update the sensor's state."""
|
"""Update the sensor's state."""
|
||||||
keys = {"on", "reachable", "state"}
|
if self._device.changed_keys.intersection(self._update_keys):
|
||||||
if self._device.changed_keys.intersection(keys):
|
|
||||||
super().async_update_callback()
|
super().async_update_callback()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool:
|
def is_on(self) -> bool | None:
|
||||||
"""Return true if sensor is on."""
|
"""Return the state of the sensor."""
|
||||||
return self._device.state # type: ignore[no-any-return]
|
return self.entity_description.value_fn(self._device)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def extra_state_attributes(self) -> dict[str, bool | float | int | list | None]:
|
def extra_state_attributes(self) -> dict[str, bool | float | int | list | None]:
|
||||||
"""Return the state attributes of the sensor."""
|
"""Return the state attributes of the sensor."""
|
||||||
|
if self.entity_description.key not in PROVIDES_EXTRA_ATTRIBUTES:
|
||||||
|
return
|
||||||
|
|
||||||
attr: dict[str, bool | float | int | list | None] = {}
|
attr: dict[str, bool | float | int | list | None] = {}
|
||||||
|
|
||||||
if self._device.on is not None:
|
if self._device.on is not None:
|
||||||
@ -237,40 +288,3 @@ class DeconzBinarySensor(DeconzDevice, BinarySensorEntity):
|
|||||||
attr[ATTR_VIBRATIONSTRENGTH] = self._device.vibration_strength
|
attr[ATTR_VIBRATIONSTRENGTH] = self._device.vibration_strength
|
||||||
|
|
||||||
return attr
|
return attr
|
||||||
|
|
||||||
|
|
||||||
class DeconzPropertyBinarySensor(DeconzDevice, BinarySensorEntity):
|
|
||||||
"""Representation of a deCONZ Property sensor."""
|
|
||||||
|
|
||||||
TYPE = DOMAIN
|
|
||||||
_device: PydeconzSensor
|
|
||||||
entity_description: DeconzBinarySensorDescription
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
device: PydeconzSensor,
|
|
||||||
gateway: DeconzGateway,
|
|
||||||
description: DeconzBinarySensorDescription,
|
|
||||||
) -> None:
|
|
||||||
"""Initialize deCONZ binary sensor."""
|
|
||||||
self.entity_description = description
|
|
||||||
super().__init__(device, gateway)
|
|
||||||
|
|
||||||
self._attr_name = f"{self._device.name} {description.suffix}"
|
|
||||||
self._update_keys = {description.update_key, "reachable"}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def unique_id(self) -> str:
|
|
||||||
"""Return a unique identifier for this device."""
|
|
||||||
return f"{self.serial}-{self.entity_description.suffix.lower()}"
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_update_callback(self) -> None:
|
|
||||||
"""Update the sensor's state."""
|
|
||||||
if self._device.changed_keys.intersection(self._update_keys):
|
|
||||||
super().async_update_callback()
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_on(self) -> bool | None:
|
|
||||||
"""Return the state of the sensor."""
|
|
||||||
return self.entity_description.value_fn(self._device)
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
||||||
from homeassistant.components.deconz.const import (
|
from homeassistant.components.deconz.const import (
|
||||||
CONF_ALLOW_CLIP_SENSOR,
|
CONF_ALLOW_CLIP_SENSOR,
|
||||||
@ -10,14 +12,13 @@ from homeassistant.components.deconz.const import (
|
|||||||
DOMAIN as DECONZ_DOMAIN,
|
DOMAIN as DECONZ_DOMAIN,
|
||||||
)
|
)
|
||||||
from homeassistant.components.deconz.services import SERVICE_DEVICE_REFRESH
|
from homeassistant.components.deconz.services import SERVICE_DEVICE_REFRESH
|
||||||
from homeassistant.components.sensor import SensorDeviceClass
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_DEVICE_CLASS,
|
ATTR_DEVICE_CLASS,
|
||||||
STATE_OFF,
|
STATE_OFF,
|
||||||
STATE_ON,
|
STATE_ON,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
from homeassistant.helpers.entity import EntityCategory
|
from homeassistant.helpers.entity import EntityCategory
|
||||||
from homeassistant.helpers.entity_registry import async_entries_for_config_entry
|
from homeassistant.helpers.entity_registry import async_entries_for_config_entry
|
||||||
|
|
||||||
@ -34,204 +35,512 @@ async def test_no_binary_sensors(hass, aioclient_mock):
|
|||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
|
|
||||||
|
|
||||||
async def test_binary_sensors(hass, aioclient_mock, mock_deconz_websocket):
|
TEST_DATA = [
|
||||||
|
( # Alarm binary sensor
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"battery": 100,
|
||||||
|
"on": True,
|
||||||
|
"reachable": True,
|
||||||
|
"temperature": 2600,
|
||||||
|
},
|
||||||
|
"ep": 1,
|
||||||
|
"etag": "18c0f3c2100904e31a7f938db2ba9ba9",
|
||||||
|
"manufacturername": "dresden elektronik",
|
||||||
|
"modelid": "lumi.sensor_motion.aq2",
|
||||||
|
"name": "Alarm 10",
|
||||||
|
"state": {
|
||||||
|
"alarm": False,
|
||||||
|
"lastupdated": "none",
|
||||||
|
"lowbattery": None,
|
||||||
|
"tampered": None,
|
||||||
|
},
|
||||||
|
"swversion": "20170627",
|
||||||
|
"type": "ZHAAlarm",
|
||||||
|
"uniqueid": "00:15:8d:00:02:b5:d1:80-01-0500",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity_count": 3,
|
||||||
|
"device_count": 3,
|
||||||
|
"entity_id": "binary_sensor.alarm_10",
|
||||||
|
"unique_id": "00:15:8d:00:02:b5:d1:80-01-0500",
|
||||||
|
"state": STATE_OFF,
|
||||||
|
"entity_category": None,
|
||||||
|
"device_class": BinarySensorDeviceClass.SAFETY,
|
||||||
|
"attributes": {
|
||||||
|
"on": True,
|
||||||
|
"temperature": 26.0,
|
||||||
|
"device_class": "safety",
|
||||||
|
"friendly_name": "Alarm 10",
|
||||||
|
},
|
||||||
|
"websocket_event": {"alarm": True},
|
||||||
|
"next_state": STATE_ON,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
( # Carbon monoxide binary sensor
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"battery": 100,
|
||||||
|
"on": True,
|
||||||
|
"pending": [],
|
||||||
|
"reachable": True,
|
||||||
|
},
|
||||||
|
"ep": 1,
|
||||||
|
"etag": "b7599df551944df97b2aa87d160b9c45",
|
||||||
|
"manufacturername": "Heiman",
|
||||||
|
"modelid": "CO_V16",
|
||||||
|
"name": "Cave CO",
|
||||||
|
"state": {
|
||||||
|
"carbonmonoxide": False,
|
||||||
|
"lastupdated": "none",
|
||||||
|
"lowbattery": False,
|
||||||
|
"tampered": False,
|
||||||
|
},
|
||||||
|
"swversion": "20150330",
|
||||||
|
"type": "ZHACarbonMonoxide",
|
||||||
|
"uniqueid": "00:15:8d:00:02:a5:21:24-01-0101",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity_count": 4,
|
||||||
|
"device_count": 3,
|
||||||
|
"entity_id": "binary_sensor.cave_co",
|
||||||
|
"unique_id": "00:15:8d:00:02:a5:21:24-01-0101",
|
||||||
|
"state": STATE_OFF,
|
||||||
|
"entity_category": None,
|
||||||
|
"device_class": BinarySensorDeviceClass.CO,
|
||||||
|
"attributes": {
|
||||||
|
"on": True,
|
||||||
|
"device_class": "carbon_monoxide",
|
||||||
|
"friendly_name": "Cave CO",
|
||||||
|
},
|
||||||
|
"websocket_event": {"carbonmonoxide": True},
|
||||||
|
"next_state": STATE_ON,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
( # Fire binary sensor
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"on": True,
|
||||||
|
"reachable": True,
|
||||||
|
},
|
||||||
|
"ep": 1,
|
||||||
|
"etag": "2b585d2c016bfd665ba27a8fdad28670",
|
||||||
|
"manufacturername": "LUMI",
|
||||||
|
"modelid": "lumi.sensor_smoke",
|
||||||
|
"name": "sensor_kitchen_smoke",
|
||||||
|
"state": {
|
||||||
|
"fire": False,
|
||||||
|
"lastupdated": "2018-02-20T11:25:02",
|
||||||
|
},
|
||||||
|
"type": "ZHAFire",
|
||||||
|
"uniqueid": "00:15:8d:00:01:d9:3e:7c-01-0500",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity_count": 2,
|
||||||
|
"device_count": 3,
|
||||||
|
"entity_id": "binary_sensor.sensor_kitchen_smoke",
|
||||||
|
"unique_id": "00:15:8d:00:01:d9:3e:7c-01-0500",
|
||||||
|
"state": STATE_OFF,
|
||||||
|
"entity_category": None,
|
||||||
|
"device_class": BinarySensorDeviceClass.SMOKE,
|
||||||
|
"attributes": {
|
||||||
|
"on": True,
|
||||||
|
"device_class": "smoke",
|
||||||
|
"friendly_name": "sensor_kitchen_smoke",
|
||||||
|
},
|
||||||
|
"websocket_event": {"fire": True},
|
||||||
|
"next_state": STATE_ON,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
( # Fire test mode binary sensor
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"on": True,
|
||||||
|
"reachable": True,
|
||||||
|
},
|
||||||
|
"ep": 1,
|
||||||
|
"etag": "2b585d2c016bfd665ba27a8fdad28670",
|
||||||
|
"manufacturername": "LUMI",
|
||||||
|
"modelid": "lumi.sensor_smoke",
|
||||||
|
"name": "sensor_kitchen_smoke",
|
||||||
|
"state": {
|
||||||
|
"fire": False,
|
||||||
|
"test": False,
|
||||||
|
"lastupdated": "2018-02-20T11:25:02",
|
||||||
|
},
|
||||||
|
"type": "ZHAFire",
|
||||||
|
"uniqueid": "00:15:8d:00:01:d9:3e:7c-01-0500",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity_count": 2,
|
||||||
|
"device_count": 3,
|
||||||
|
"entity_id": "binary_sensor.sensor_kitchen_smoke_test_mode",
|
||||||
|
"unique_id": "00:15:8d:00:01:d9:3e:7c-test mode",
|
||||||
|
"state": STATE_OFF,
|
||||||
|
"entity_category": EntityCategory.DIAGNOSTIC,
|
||||||
|
"device_class": BinarySensorDeviceClass.SMOKE,
|
||||||
|
"attributes": {
|
||||||
|
"device_class": "smoke",
|
||||||
|
"friendly_name": "sensor_kitchen_smoke Test Mode",
|
||||||
|
},
|
||||||
|
"websocket_event": {"test": True},
|
||||||
|
"next_state": STATE_ON,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
( # Generic flag binary sensor
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"on": True,
|
||||||
|
"reachable": True,
|
||||||
|
},
|
||||||
|
"modelid": "Switch",
|
||||||
|
"name": "Kitchen Switch",
|
||||||
|
"state": {
|
||||||
|
"flag": True,
|
||||||
|
"lastupdated": "2018-07-01T10:40:35",
|
||||||
|
},
|
||||||
|
"swversion": "1.0.0",
|
||||||
|
"type": "CLIPGenericFlag",
|
||||||
|
"uniqueid": "kitchen-switch",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity_count": 1,
|
||||||
|
"device_count": 2,
|
||||||
|
"entity_id": "binary_sensor.kitchen_switch",
|
||||||
|
"unique_id": "kitchen-switch",
|
||||||
|
"state": STATE_ON,
|
||||||
|
"entity_category": None,
|
||||||
|
"device_class": None,
|
||||||
|
"attributes": {
|
||||||
|
"on": True,
|
||||||
|
"friendly_name": "Kitchen Switch",
|
||||||
|
},
|
||||||
|
"websocket_event": {"flag": False},
|
||||||
|
"next_state": STATE_OFF,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
( # Open/Close binary sensor
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"battery": 95,
|
||||||
|
"on": True,
|
||||||
|
"reachable": True,
|
||||||
|
"temperature": 3300,
|
||||||
|
},
|
||||||
|
"ep": 1,
|
||||||
|
"etag": "66cc641d0368110da6882b50090174ac",
|
||||||
|
"manufacturername": "LUMI",
|
||||||
|
"modelid": "lumi.sensor_magnet.aq2",
|
||||||
|
"name": "Back Door",
|
||||||
|
"state": {
|
||||||
|
"lastupdated": "2019-05-05T14:54:32",
|
||||||
|
"open": False,
|
||||||
|
},
|
||||||
|
"swversion": "20161128",
|
||||||
|
"type": "ZHAOpenClose",
|
||||||
|
"uniqueid": "00:15:8d:00:02:2b:96:b4-01-0006",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity_count": 3,
|
||||||
|
"device_count": 3,
|
||||||
|
"entity_id": "binary_sensor.back_door",
|
||||||
|
"unique_id": "00:15:8d:00:02:2b:96:b4-01-0006",
|
||||||
|
"state": STATE_OFF,
|
||||||
|
"entity_category": None,
|
||||||
|
"device_class": BinarySensorDeviceClass.OPENING,
|
||||||
|
"attributes": {
|
||||||
|
"on": True,
|
||||||
|
"temperature": 33.0,
|
||||||
|
"device_class": "opening",
|
||||||
|
"friendly_name": "Back Door",
|
||||||
|
},
|
||||||
|
"websocket_event": {"open": True},
|
||||||
|
"next_state": STATE_ON,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
( # Presence binary sensor
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"alert": "none",
|
||||||
|
"battery": 100,
|
||||||
|
"delay": 0,
|
||||||
|
"ledindication": False,
|
||||||
|
"on": True,
|
||||||
|
"pending": [],
|
||||||
|
"reachable": True,
|
||||||
|
"sensitivity": 1,
|
||||||
|
"sensitivitymax": 2,
|
||||||
|
"usertest": False,
|
||||||
|
},
|
||||||
|
"ep": 2,
|
||||||
|
"etag": "5cfb81765e86aa53ace427cfd52c6d52",
|
||||||
|
"manufacturername": "Philips",
|
||||||
|
"modelid": "SML001",
|
||||||
|
"name": "Motion sensor 4",
|
||||||
|
"state": {
|
||||||
|
"dark": False,
|
||||||
|
"lastupdated": "2019-05-05T14:37:06",
|
||||||
|
"presence": False,
|
||||||
|
},
|
||||||
|
"swversion": "6.1.0.18912",
|
||||||
|
"type": "ZHAPresence",
|
||||||
|
"uniqueid": "00:17:88:01:03:28:8c:9b-02-0406",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity_count": 3,
|
||||||
|
"device_count": 3,
|
||||||
|
"entity_id": "binary_sensor.motion_sensor_4",
|
||||||
|
"unique_id": "00:17:88:01:03:28:8c:9b-02-0406",
|
||||||
|
"state": STATE_OFF,
|
||||||
|
"entity_category": None,
|
||||||
|
"device_class": BinarySensorDeviceClass.MOTION,
|
||||||
|
"attributes": {
|
||||||
|
"on": True,
|
||||||
|
"dark": False,
|
||||||
|
"device_class": "motion",
|
||||||
|
"friendly_name": "Motion sensor 4",
|
||||||
|
},
|
||||||
|
"websocket_event": {"presence": True},
|
||||||
|
"next_state": STATE_ON,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
( # Water leak binary sensor
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"battery": 100,
|
||||||
|
"on": True,
|
||||||
|
"reachable": True,
|
||||||
|
"temperature": 2500,
|
||||||
|
},
|
||||||
|
"ep": 1,
|
||||||
|
"etag": "fae893708dfe9b358df59107d944fa1c",
|
||||||
|
"manufacturername": "LUMI",
|
||||||
|
"modelid": "lumi.sensor_wleak.aq1",
|
||||||
|
"name": "water2",
|
||||||
|
"state": {
|
||||||
|
"lastupdated": "2019-01-29T07:13:20",
|
||||||
|
"lowbattery": False,
|
||||||
|
"tampered": False,
|
||||||
|
"water": False,
|
||||||
|
},
|
||||||
|
"swversion": "20170721",
|
||||||
|
"type": "ZHAWater",
|
||||||
|
"uniqueid": "00:15:8d:00:02:2f:07:db-01-0500",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity_count": 5,
|
||||||
|
"device_count": 3,
|
||||||
|
"entity_id": "binary_sensor.water2",
|
||||||
|
"unique_id": "00:15:8d:00:02:2f:07:db-01-0500",
|
||||||
|
"state": STATE_OFF,
|
||||||
|
"entity_category": None,
|
||||||
|
"device_class": BinarySensorDeviceClass.MOISTURE,
|
||||||
|
"attributes": {
|
||||||
|
"on": True,
|
||||||
|
"temperature": 25.0,
|
||||||
|
"device_class": "moisture",
|
||||||
|
"friendly_name": "water2",
|
||||||
|
},
|
||||||
|
"websocket_event": {"water": True},
|
||||||
|
"next_state": STATE_ON,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
( # Vibration binary sensor
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"battery": 91,
|
||||||
|
"on": True,
|
||||||
|
"pending": [],
|
||||||
|
"reachable": True,
|
||||||
|
"sensitivity": 21,
|
||||||
|
"sensitivitymax": 21,
|
||||||
|
"temperature": 3200,
|
||||||
|
},
|
||||||
|
"ep": 1,
|
||||||
|
"etag": "b7599df551944df97b2aa87d160b9c45",
|
||||||
|
"manufacturername": "LUMI",
|
||||||
|
"modelid": "lumi.vibration.aq1",
|
||||||
|
"name": "Vibration 1",
|
||||||
|
"state": {
|
||||||
|
"lastupdated": "2019-03-09T15:53:07",
|
||||||
|
"orientation": [10, 1059, 0],
|
||||||
|
"tiltangle": 83,
|
||||||
|
"vibration": True,
|
||||||
|
"vibrationstrength": 114,
|
||||||
|
},
|
||||||
|
"swversion": "20180130",
|
||||||
|
"type": "ZHAVibration",
|
||||||
|
"uniqueid": "00:15:8d:00:02:a5:21:24-01-0101",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity_count": 3,
|
||||||
|
"device_count": 3,
|
||||||
|
"entity_id": "binary_sensor.vibration_1",
|
||||||
|
"unique_id": "00:15:8d:00:02:a5:21:24-01-0101",
|
||||||
|
"state": STATE_ON,
|
||||||
|
"entity_category": None,
|
||||||
|
"device_class": BinarySensorDeviceClass.VIBRATION,
|
||||||
|
"attributes": {
|
||||||
|
"on": True,
|
||||||
|
"temperature": 32.0,
|
||||||
|
"orientation": [10, 1059, 0],
|
||||||
|
"tiltangle": 83,
|
||||||
|
"vibrationstrength": 114,
|
||||||
|
"device_class": "vibration",
|
||||||
|
"friendly_name": "Vibration 1",
|
||||||
|
},
|
||||||
|
"websocket_event": {"vibration": False},
|
||||||
|
"next_state": STATE_OFF,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
( # Tampering binary sensor
|
||||||
|
{
|
||||||
|
"name": "Presence sensor",
|
||||||
|
"type": "ZHAPresence",
|
||||||
|
"state": {
|
||||||
|
"dark": False,
|
||||||
|
"lowbattery": False,
|
||||||
|
"presence": False,
|
||||||
|
"tampered": False,
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"on": True,
|
||||||
|
"reachable": True,
|
||||||
|
"temperature": 10,
|
||||||
|
},
|
||||||
|
"uniqueid": "00:00:00:00:00:00:00:00-00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity_count": 4,
|
||||||
|
"device_count": 3,
|
||||||
|
"entity_id": "binary_sensor.presence_sensor_tampered",
|
||||||
|
"unique_id": "00:00:00:00:00:00:00:00-tampered",
|
||||||
|
"state": STATE_OFF,
|
||||||
|
"entity_category": EntityCategory.DIAGNOSTIC,
|
||||||
|
"device_class": BinarySensorDeviceClass.TAMPER,
|
||||||
|
"attributes": {
|
||||||
|
"device_class": "tamper",
|
||||||
|
"friendly_name": "Presence sensor Tampered",
|
||||||
|
},
|
||||||
|
"websocket_event": {"tampered": True},
|
||||||
|
"next_state": STATE_ON,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
( # Low battery binary sensor
|
||||||
|
{
|
||||||
|
"name": "Presence sensor",
|
||||||
|
"type": "ZHAPresence",
|
||||||
|
"state": {
|
||||||
|
"dark": False,
|
||||||
|
"lowbattery": False,
|
||||||
|
"presence": False,
|
||||||
|
"tampered": False,
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"on": True,
|
||||||
|
"reachable": True,
|
||||||
|
"temperature": 10,
|
||||||
|
},
|
||||||
|
"uniqueid": "00:00:00:00:00:00:00:00-00",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entity_count": 4,
|
||||||
|
"device_count": 3,
|
||||||
|
"entity_id": "binary_sensor.presence_sensor_low_battery",
|
||||||
|
"unique_id": "00:00:00:00:00:00:00:00-low battery",
|
||||||
|
"state": STATE_OFF,
|
||||||
|
"entity_category": EntityCategory.DIAGNOSTIC,
|
||||||
|
"device_class": BinarySensorDeviceClass.BATTERY,
|
||||||
|
"attributes": {
|
||||||
|
"device_class": "battery",
|
||||||
|
"friendly_name": "Presence sensor Low Battery",
|
||||||
|
},
|
||||||
|
"websocket_event": {"lowbattery": True},
|
||||||
|
"next_state": STATE_ON,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("sensor_data, expected", TEST_DATA)
|
||||||
|
async def test_binary_sensors(
|
||||||
|
hass, aioclient_mock, mock_deconz_websocket, sensor_data, expected
|
||||||
|
):
|
||||||
"""Test successful creation of binary sensor entities."""
|
"""Test successful creation of binary sensor entities."""
|
||||||
|
ent_reg = er.async_get(hass)
|
||||||
|
dev_reg = dr.async_get(hass)
|
||||||
|
|
||||||
|
with patch.dict(DECONZ_WEB_REQUEST, {"sensors": {"1": sensor_data}}):
|
||||||
|
config_entry = await setup_deconz_integration(
|
||||||
|
hass, aioclient_mock, options={CONF_ALLOW_CLIP_SENSOR: True}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(hass.states.async_all()) == expected["entity_count"]
|
||||||
|
|
||||||
|
# Verify state data
|
||||||
|
|
||||||
|
sensor = hass.states.get(expected["entity_id"])
|
||||||
|
assert sensor.state == expected["state"]
|
||||||
|
assert sensor.attributes.get(ATTR_DEVICE_CLASS) == expected["device_class"]
|
||||||
|
assert sensor.attributes == expected["attributes"]
|
||||||
|
|
||||||
|
# Verify entity registry data
|
||||||
|
|
||||||
|
ent_reg_entry = ent_reg.async_get(expected["entity_id"])
|
||||||
|
assert ent_reg_entry.entity_category is expected["entity_category"]
|
||||||
|
assert ent_reg_entry.unique_id == expected["unique_id"]
|
||||||
|
|
||||||
|
# Verify device registry data
|
||||||
|
|
||||||
|
assert (
|
||||||
|
len(dr.async_entries_for_config_entry(dev_reg, config_entry.entry_id))
|
||||||
|
== expected["device_count"]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Change state
|
||||||
|
|
||||||
|
event_changed_sensor = {
|
||||||
|
"t": "event",
|
||||||
|
"e": "changed",
|
||||||
|
"r": "sensors",
|
||||||
|
"id": "1",
|
||||||
|
"state": expected["websocket_event"],
|
||||||
|
}
|
||||||
|
await mock_deconz_websocket(data=event_changed_sensor)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert hass.states.get(expected["entity_id"]).state == expected["next_state"]
|
||||||
|
|
||||||
|
# Unload entry
|
||||||
|
|
||||||
|
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
|
assert hass.states.get(expected["entity_id"]).state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
# Remove entry
|
||||||
|
|
||||||
|
await hass.config_entries.async_remove(config_entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(hass.states.async_all()) == 0
|
||||||
|
|
||||||
|
|
||||||
|
async def test_not_allow_clip_sensor(hass, aioclient_mock):
|
||||||
|
"""Test that CLIP sensors are not allowed."""
|
||||||
data = {
|
data = {
|
||||||
"sensors": {
|
"sensors": {
|
||||||
"1": {
|
"1": {
|
||||||
"name": "Presence sensor",
|
|
||||||
"type": "ZHAPresence",
|
|
||||||
"state": {"dark": False, "presence": False},
|
|
||||||
"config": {"on": True, "reachable": True, "temperature": 10},
|
|
||||||
"uniqueid": "00:00:00:00:00:00:00:00-00",
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"name": "Temperature sensor",
|
|
||||||
"type": "ZHATemperature",
|
|
||||||
"state": {"temperature": False},
|
|
||||||
"config": {},
|
|
||||||
"uniqueid": "00:00:00:00:00:00:00:01-00",
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"name": "CLIP presence sensor",
|
"name": "CLIP presence sensor",
|
||||||
"type": "CLIPPresence",
|
"type": "CLIPPresence",
|
||||||
"state": {"presence": False},
|
"state": {"presence": False},
|
||||||
"config": {},
|
"config": {},
|
||||||
"uniqueid": "00:00:00:00:00:00:00:02-00",
|
"uniqueid": "00:00:00:00:00:00:00:02-00",
|
||||||
},
|
},
|
||||||
"4": {
|
|
||||||
"name": "Vibration sensor",
|
|
||||||
"type": "ZHAVibration",
|
|
||||||
"state": {
|
|
||||||
"orientation": [1, 2, 3],
|
|
||||||
"tiltangle": 36,
|
|
||||||
"vibration": True,
|
|
||||||
"vibrationstrength": 10,
|
|
||||||
},
|
|
||||||
"config": {"on": True, "reachable": True, "temperature": 10},
|
|
||||||
"uniqueid": "00:00:00:00:00:00:00:03-00",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
with patch.dict(DECONZ_WEB_REQUEST, data):
|
with patch.dict(DECONZ_WEB_REQUEST, data):
|
||||||
config_entry = await setup_deconz_integration(hass, aioclient_mock)
|
await setup_deconz_integration(
|
||||||
|
hass, aioclient_mock, options={CONF_ALLOW_CLIP_SENSOR: False}
|
||||||
|
)
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 5
|
|
||||||
presence_sensor = hass.states.get("binary_sensor.presence_sensor")
|
|
||||||
assert presence_sensor.state == STATE_OFF
|
|
||||||
assert (
|
|
||||||
presence_sensor.attributes[ATTR_DEVICE_CLASS] == BinarySensorDeviceClass.MOTION
|
|
||||||
)
|
|
||||||
presence_temp = hass.states.get("sensor.presence_sensor_temperature")
|
|
||||||
assert presence_temp.state == "0.1"
|
|
||||||
assert presence_temp.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.TEMPERATURE
|
|
||||||
assert hass.states.get("binary_sensor.temperature_sensor") is None
|
|
||||||
assert hass.states.get("binary_sensor.clip_presence_sensor") is None
|
|
||||||
vibration_sensor = hass.states.get("binary_sensor.vibration_sensor")
|
|
||||||
assert vibration_sensor.state == STATE_ON
|
|
||||||
assert (
|
|
||||||
vibration_sensor.attributes[ATTR_DEVICE_CLASS]
|
|
||||||
== BinarySensorDeviceClass.VIBRATION
|
|
||||||
)
|
|
||||||
vibration_temp = hass.states.get("sensor.vibration_sensor_temperature")
|
|
||||||
assert vibration_temp.state == "0.1"
|
|
||||||
assert vibration_temp.attributes[ATTR_DEVICE_CLASS] == SensorDeviceClass.TEMPERATURE
|
|
||||||
|
|
||||||
event_changed_sensor = {
|
|
||||||
"t": "event",
|
|
||||||
"e": "changed",
|
|
||||||
"r": "sensors",
|
|
||||||
"id": "1",
|
|
||||||
"state": {"presence": True},
|
|
||||||
}
|
|
||||||
await mock_deconz_websocket(data=event_changed_sensor)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert hass.states.get("binary_sensor.presence_sensor").state == STATE_ON
|
|
||||||
|
|
||||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
|
||||||
|
|
||||||
assert hass.states.get("binary_sensor.presence_sensor").state == STATE_UNAVAILABLE
|
|
||||||
|
|
||||||
await hass.config_entries.async_remove(config_entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
|
||||||
|
|
||||||
|
|
||||||
async def test_tampering_sensor(hass, aioclient_mock, mock_deconz_websocket):
|
|
||||||
"""Verify tampering sensor works."""
|
|
||||||
data = {
|
|
||||||
"sensors": {
|
|
||||||
"1": {
|
|
||||||
"name": "Presence sensor",
|
|
||||||
"type": "ZHAPresence",
|
|
||||||
"state": {
|
|
||||||
"dark": False,
|
|
||||||
"lowbattery": False,
|
|
||||||
"presence": False,
|
|
||||||
"tampered": False,
|
|
||||||
},
|
|
||||||
"config": {"on": True, "reachable": True, "temperature": 10},
|
|
||||||
"uniqueid": "00:00:00:00:00:00:00:00-00",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
with patch.dict(DECONZ_WEB_REQUEST, data):
|
|
||||||
config_entry = await setup_deconz_integration(hass, aioclient_mock)
|
|
||||||
|
|
||||||
ent_reg = er.async_get(hass)
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 4
|
|
||||||
hass.states.get("binary_sensor.presence_sensor_low_battery").state == STATE_OFF
|
|
||||||
assert (
|
|
||||||
ent_reg.async_get("binary_sensor.presence_sensor_low_battery").entity_category
|
|
||||||
is EntityCategory.DIAGNOSTIC
|
|
||||||
)
|
|
||||||
presence_tamper = hass.states.get("binary_sensor.presence_sensor_tampered")
|
|
||||||
assert presence_tamper.state == STATE_OFF
|
|
||||||
assert (
|
|
||||||
presence_tamper.attributes[ATTR_DEVICE_CLASS] == BinarySensorDeviceClass.TAMPER
|
|
||||||
)
|
|
||||||
assert (
|
|
||||||
ent_reg.async_get("binary_sensor.presence_sensor_tampered").entity_category
|
|
||||||
is EntityCategory.DIAGNOSTIC
|
|
||||||
)
|
|
||||||
|
|
||||||
event_changed_sensor = {
|
|
||||||
"t": "event",
|
|
||||||
"e": "changed",
|
|
||||||
"r": "sensors",
|
|
||||||
"id": "1",
|
|
||||||
"state": {"tampered": True},
|
|
||||||
}
|
|
||||||
await mock_deconz_websocket(data=event_changed_sensor)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert hass.states.get("binary_sensor.presence_sensor_tampered").state == STATE_ON
|
|
||||||
|
|
||||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
|
||||||
|
|
||||||
assert (
|
|
||||||
hass.states.get("binary_sensor.presence_sensor_tampered").state
|
|
||||||
== STATE_UNAVAILABLE
|
|
||||||
)
|
|
||||||
|
|
||||||
await hass.config_entries.async_remove(config_entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 0
|
|
||||||
|
|
||||||
|
|
||||||
async def test_fire_sensor(hass, aioclient_mock, mock_deconz_websocket):
|
|
||||||
"""Verify smoke alarm sensor works."""
|
|
||||||
data = {
|
|
||||||
"sensors": {
|
|
||||||
"1": {
|
|
||||||
"name": "Fire alarm",
|
|
||||||
"type": "ZHAFire",
|
|
||||||
"state": {"fire": False, "test": False},
|
|
||||||
"config": {"on": True, "reachable": True},
|
|
||||||
"uniqueid": "00:00:00:00:00:00:00:00-00",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
with patch.dict(DECONZ_WEB_REQUEST, data):
|
|
||||||
config_entry = await setup_deconz_integration(hass, aioclient_mock)
|
|
||||||
|
|
||||||
ent_reg = er.async_get(hass)
|
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 2
|
|
||||||
assert hass.states.get("binary_sensor.fire_alarm").state == STATE_OFF
|
|
||||||
assert ent_reg.async_get("binary_sensor.fire_alarm").entity_category is None
|
|
||||||
|
|
||||||
assert hass.states.get("binary_sensor.fire_alarm_test_mode").state == STATE_OFF
|
|
||||||
assert (
|
|
||||||
ent_reg.async_get("binary_sensor.fire_alarm_test_mode").entity_category
|
|
||||||
is EntityCategory.DIAGNOSTIC
|
|
||||||
)
|
|
||||||
|
|
||||||
event_changed_sensor = {
|
|
||||||
"t": "event",
|
|
||||||
"e": "changed",
|
|
||||||
"r": "sensors",
|
|
||||||
"id": "1",
|
|
||||||
"state": {"fire": True, "test": True},
|
|
||||||
}
|
|
||||||
await mock_deconz_websocket(data=event_changed_sensor)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert hass.states.get("binary_sensor.fire_alarm").state == STATE_ON
|
|
||||||
assert hass.states.get("binary_sensor.fire_alarm_test_mode").state == STATE_ON
|
|
||||||
|
|
||||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
|
||||||
assert hass.states.get("binary_sensor.fire_alarm").state == STATE_UNAVAILABLE
|
|
||||||
assert (
|
|
||||||
hass.states.get("binary_sensor.fire_alarm_test_mode").state == STATE_UNAVAILABLE
|
|
||||||
)
|
|
||||||
|
|
||||||
await hass.config_entries.async_remove(config_entry.entry_id)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert len(hass.states.async_all()) == 0
|
assert len(hass.states.async_all()) == 0
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user