mirror of https://github.com/home-assistant/core
Add tampering sensor to deCONZ integration (#49776)
This commit is contained in:
parent
883513e089
commit
056f636568
|
@ -6,6 +6,7 @@ from homeassistant.components.binary_sensor import (
|
||||||
DEVICE_CLASS_MOISTURE,
|
DEVICE_CLASS_MOISTURE,
|
||||||
DEVICE_CLASS_MOTION,
|
DEVICE_CLASS_MOTION,
|
||||||
DEVICE_CLASS_OPENING,
|
DEVICE_CLASS_OPENING,
|
||||||
|
DEVICE_CLASS_PROBLEM,
|
||||||
DEVICE_CLASS_SMOKE,
|
DEVICE_CLASS_SMOKE,
|
||||||
DEVICE_CLASS_VIBRATION,
|
DEVICE_CLASS_VIBRATION,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
@ -55,6 +56,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
):
|
):
|
||||||
entities.append(DeconzBinarySensor(sensor, gateway))
|
entities.append(DeconzBinarySensor(sensor, gateway))
|
||||||
|
|
||||||
|
if sensor.tampered is not None:
|
||||||
|
known_tampering_sensors = set(gateway.entities[DOMAIN])
|
||||||
|
new_tampering_sensor = DeconzTampering(sensor, gateway)
|
||||||
|
if new_tampering_sensor.unique_id not in known_tampering_sensors:
|
||||||
|
entities.append(new_tampering_sensor)
|
||||||
|
|
||||||
if entities:
|
if entities:
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
@ -113,3 +120,36 @@ class DeconzBinarySensor(DeconzDevice, BinarySensorEntity):
|
||||||
attr[ATTR_VIBRATIONSTRENGTH] = self._device.vibrationstrength
|
attr[ATTR_VIBRATIONSTRENGTH] = self._device.vibrationstrength
|
||||||
|
|
||||||
return attr
|
return attr
|
||||||
|
|
||||||
|
|
||||||
|
class DeconzTampering(DeconzDevice, BinarySensorEntity):
|
||||||
|
"""Representation of a deCONZ tampering sensor."""
|
||||||
|
|
||||||
|
TYPE = DOMAIN
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self) -> str:
|
||||||
|
"""Return a unique identifier for this device."""
|
||||||
|
return f"{self.serial}-tampered"
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_update_callback(self, force_update: bool = False) -> None:
|
||||||
|
"""Update the sensor's state."""
|
||||||
|
keys = {"tampered", "reachable"}
|
||||||
|
if force_update or self._device.changed_keys.intersection(keys):
|
||||||
|
super().async_update_callback(force_update=force_update)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_on(self) -> bool:
|
||||||
|
"""Return the state of the sensor."""
|
||||||
|
return self._device.tampered
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
"""Return the name of the sensor."""
|
||||||
|
return f"{self._device.name} Tampered"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_class(self) -> str:
|
||||||
|
"""Return the class of the sensor."""
|
||||||
|
return DEVICE_CLASS_PROBLEM
|
||||||
|
|
|
@ -82,7 +82,7 @@ async def test_alarm_control_panel(hass, aioclient_mock, mock_deconz_websocket):
|
||||||
with patch.dict(DECONZ_WEB_REQUEST, data):
|
with patch.dict(DECONZ_WEB_REQUEST, data):
|
||||||
config_entry = await setup_deconz_integration(hass, aioclient_mock)
|
config_entry = await setup_deconz_integration(hass, aioclient_mock)
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 1
|
assert len(hass.states.async_all()) == 2
|
||||||
assert hass.states.get("alarm_control_panel.keypad").state == STATE_ALARM_DISARMED
|
assert hass.states.get("alarm_control_panel.keypad").state == STATE_ALARM_DISARMED
|
||||||
|
|
||||||
# Event signals alarm control panel armed away
|
# Event signals alarm control panel armed away
|
||||||
|
@ -261,7 +261,7 @@ async def test_alarm_control_panel(hass, aioclient_mock, mock_deconz_websocket):
|
||||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
|
|
||||||
states = hass.states.async_all()
|
states = hass.states.async_all()
|
||||||
assert len(states) == 1
|
assert len(states) == 2
|
||||||
for state in states:
|
for state in states:
|
||||||
assert state.state == STATE_UNAVAILABLE
|
assert state.state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ from unittest.mock import patch
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import (
|
from homeassistant.components.binary_sensor import (
|
||||||
DEVICE_CLASS_MOTION,
|
DEVICE_CLASS_MOTION,
|
||||||
|
DEVICE_CLASS_PROBLEM,
|
||||||
DEVICE_CLASS_VIBRATION,
|
DEVICE_CLASS_VIBRATION,
|
||||||
)
|
)
|
||||||
from homeassistant.components.deconz.const import (
|
from homeassistant.components.deconz.const import (
|
||||||
|
@ -116,6 +117,52 @@ async def test_binary_sensors(hass, aioclient_mock, mock_deconz_websocket):
|
||||||
assert len(hass.states.async_all()) == 0
|
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, "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)
|
||||||
|
|
||||||
|
assert len(hass.states.async_all()) == 3
|
||||||
|
presence_tamper = hass.states.get("binary_sensor.presence_sensor_tampered")
|
||||||
|
assert presence_tamper.state == STATE_OFF
|
||||||
|
assert presence_tamper.attributes[ATTR_DEVICE_CLASS] == DEVICE_CLASS_PROBLEM
|
||||||
|
|
||||||
|
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_allow_clip_sensor(hass, aioclient_mock):
|
async def test_allow_clip_sensor(hass, aioclient_mock):
|
||||||
"""Test that CLIP sensors can be allowed."""
|
"""Test that CLIP sensors can be allowed."""
|
||||||
data = {
|
data = {
|
||||||
|
|
|
@ -232,7 +232,7 @@ async def test_deconz_alarm_events(hass, aioclient_mock, mock_deconz_websocket):
|
||||||
|
|
||||||
device_registry = await hass.helpers.device_registry.async_get_registry()
|
device_registry = await hass.helpers.device_registry.async_get_registry()
|
||||||
|
|
||||||
assert len(hass.states.async_all()) == 1
|
assert len(hass.states.async_all()) == 2
|
||||||
# 1 alarm control device + 2 additional devices for deconz service and host
|
# 1 alarm control device + 2 additional devices for deconz service and host
|
||||||
assert (
|
assert (
|
||||||
len(async_entries_for_config_entry(device_registry, config_entry.entry_id)) == 3
|
len(async_entries_for_config_entry(device_registry, config_entry.entry_id)) == 3
|
||||||
|
@ -294,7 +294,7 @@ async def test_deconz_alarm_events(hass, aioclient_mock, mock_deconz_websocket):
|
||||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
|
|
||||||
states = hass.states.async_all()
|
states = hass.states.async_all()
|
||||||
assert len(hass.states.async_all()) == 1
|
assert len(hass.states.async_all()) == 2
|
||||||
for state in states:
|
for state in states:
|
||||||
assert state.state == STATE_UNAVAILABLE
|
assert state.state == STATE_UNAVAILABLE
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue