Add zone_entity_id to Risco events (#39678)
* Add zone_entity_id to Risco events * Add comment * Fix dependency on order
This commit is contained in:
parent
7c397a02b7
commit
5a7e4b4dae
6 changed files with 90 additions and 79 deletions
|
@ -5,7 +5,7 @@ from homeassistant.components.binary_sensor import (
|
||||||
)
|
)
|
||||||
from homeassistant.helpers import entity_platform
|
from homeassistant.helpers import entity_platform
|
||||||
|
|
||||||
from .const import DATA_COORDINATOR, DOMAIN
|
from .const import DATA_COORDINATOR, DATA_ZONES, DOMAIN
|
||||||
from .entity import RiscoEntity
|
from .entity import RiscoEntity
|
||||||
|
|
||||||
SERVICE_BYPASS_ZONE = "bypass_zone"
|
SERVICE_BYPASS_ZONE = "bypass_zone"
|
||||||
|
@ -21,12 +21,12 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
)
|
)
|
||||||
|
|
||||||
coordinator = hass.data[DOMAIN][config_entry.entry_id][DATA_COORDINATOR]
|
coordinator = hass.data[DOMAIN][config_entry.entry_id][DATA_COORDINATOR]
|
||||||
entities = [
|
entities = {
|
||||||
RiscoBinarySensor(coordinator, zone_id, zone)
|
zone_id: RiscoBinarySensor(coordinator, zone_id, zone)
|
||||||
for zone_id, zone in coordinator.data.zones.items()
|
for zone_id, zone in coordinator.data.zones.items()
|
||||||
]
|
}
|
||||||
|
hass.data[DOMAIN][config_entry.entry_id][DATA_ZONES] = entities
|
||||||
async_add_entities(entities, False)
|
async_add_entities(entities.values(), False)
|
||||||
|
|
||||||
|
|
||||||
class RiscoBinarySensor(BinarySensorEntity, RiscoEntity):
|
class RiscoBinarySensor(BinarySensorEntity, RiscoEntity):
|
||||||
|
@ -63,7 +63,7 @@ class RiscoBinarySensor(BinarySensorEntity, RiscoEntity):
|
||||||
@property
|
@property
|
||||||
def device_state_attributes(self):
|
def device_state_attributes(self):
|
||||||
"""Return the state attributes."""
|
"""Return the state attributes."""
|
||||||
return {"bypassed": self._zone.bypassed}
|
return {"zone_id": self._zone_id, "bypassed": self._zone.bypassed}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
|
|
|
@ -11,6 +11,7 @@ DOMAIN = "risco"
|
||||||
RISCO_EVENT = "risco_event"
|
RISCO_EVENT = "risco_event"
|
||||||
|
|
||||||
DATA_COORDINATOR = "risco"
|
DATA_COORDINATOR = "risco"
|
||||||
|
DATA_ZONES = "zones"
|
||||||
EVENTS_COORDINATOR = "risco_events"
|
EVENTS_COORDINATOR = "risco_events"
|
||||||
|
|
||||||
DEFAULT_SCAN_INTERVAL = 30
|
DEFAULT_SCAN_INTERVAL = 30
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
from homeassistant.const import DEVICE_CLASS_TIMESTAMP
|
from homeassistant.const import DEVICE_CLASS_TIMESTAMP
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import DOMAIN, EVENTS_COORDINATOR
|
from .const import DATA_ZONES, DOMAIN, EVENTS_COORDINATOR
|
||||||
|
|
||||||
CATEGORIES = {
|
CATEGORIES = {
|
||||||
2: "Alarm",
|
2: "Alarm",
|
||||||
|
@ -29,22 +29,28 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
"""Set up sensors for device."""
|
"""Set up sensors for device."""
|
||||||
coordinator = hass.data[DOMAIN][config_entry.entry_id][EVENTS_COORDINATOR]
|
coordinator = hass.data[DOMAIN][config_entry.entry_id][EVENTS_COORDINATOR]
|
||||||
sensors = [
|
sensors = [
|
||||||
RiscoSensor(coordinator, id, [], name) for id, name in CATEGORIES.items()
|
RiscoSensor(coordinator, id, [], name, config_entry.entry_id)
|
||||||
|
for id, name in CATEGORIES.items()
|
||||||
]
|
]
|
||||||
sensors.append(RiscoSensor(coordinator, None, CATEGORIES.keys(), "Other"))
|
sensors.append(
|
||||||
|
RiscoSensor(
|
||||||
|
coordinator, None, CATEGORIES.keys(), "Other", config_entry.entry_id
|
||||||
|
)
|
||||||
|
)
|
||||||
async_add_entities(sensors)
|
async_add_entities(sensors)
|
||||||
|
|
||||||
|
|
||||||
class RiscoSensor(CoordinatorEntity):
|
class RiscoSensor(CoordinatorEntity):
|
||||||
"""Sensor for Risco events."""
|
"""Sensor for Risco events."""
|
||||||
|
|
||||||
def __init__(self, coordinator, category_id, excludes, name) -> None:
|
def __init__(self, coordinator, category_id, excludes, name, entry_id) -> None:
|
||||||
"""Initialize sensor."""
|
"""Initialize sensor."""
|
||||||
super().__init__(coordinator)
|
super().__init__(coordinator)
|
||||||
self._event = None
|
self._event = None
|
||||||
self._category_id = category_id
|
self._category_id = category_id
|
||||||
self._excludes = excludes
|
self._excludes = excludes
|
||||||
self._name = name
|
self._name = name
|
||||||
|
self._entry_id = entry_id
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
|
@ -88,7 +94,14 @@ class RiscoSensor(CoordinatorEntity):
|
||||||
if self._event is None:
|
if self._event is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return {atr: getattr(self._event, atr, None) for atr in EVENT_ATTRIBUTES}
|
attrs = {atr: getattr(self._event, atr, None) for atr in EVENT_ATTRIBUTES}
|
||||||
|
if self._event.zone_id is not None:
|
||||||
|
zones = self.hass.data[DOMAIN][self._entry_id][DATA_ZONES]
|
||||||
|
zone = zones.get(self._event.zone_id)
|
||||||
|
if zone is not None:
|
||||||
|
attrs["zone_entity_id"] = zone.entity_id
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_class(self):
|
def device_class(self):
|
||||||
|
|
|
@ -1,51 +1,19 @@
|
||||||
"""Tests for the Risco binary sensors."""
|
"""Tests for the Risco binary sensors."""
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant.components.risco import CannotConnectError, UnauthorizedError
|
from homeassistant.components.risco import CannotConnectError, UnauthorizedError
|
||||||
from homeassistant.components.risco.const import DOMAIN
|
from homeassistant.components.risco.const import DOMAIN
|
||||||
from homeassistant.const import STATE_OFF, STATE_ON
|
from homeassistant.const import STATE_OFF, STATE_ON
|
||||||
from homeassistant.helpers.entity_component import async_update_entity
|
from homeassistant.helpers.entity_component import async_update_entity
|
||||||
|
|
||||||
from .util import TEST_CONFIG, TEST_SITE_UUID, setup_risco
|
from .util import TEST_CONFIG, TEST_SITE_UUID, setup_risco
|
||||||
|
from .util import two_zone_alarm # noqa: F401
|
||||||
|
|
||||||
from tests.async_mock import MagicMock, PropertyMock, patch
|
from tests.async_mock import PropertyMock, patch
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
FIRST_ENTITY_ID = "binary_sensor.zone_0"
|
FIRST_ENTITY_ID = "binary_sensor.zone_0"
|
||||||
SECOND_ENTITY_ID = "binary_sensor.zone_1"
|
SECOND_ENTITY_ID = "binary_sensor.zone_1"
|
||||||
|
|
||||||
|
|
||||||
def _zone_mock():
|
|
||||||
return MagicMock(
|
|
||||||
triggered=False,
|
|
||||||
bypassed=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def two_zone_alarm():
|
|
||||||
"""Fixture to mock alarm with two zones."""
|
|
||||||
zone_mocks = {0: _zone_mock(), 1: _zone_mock()}
|
|
||||||
alarm_mock = MagicMock()
|
|
||||||
with patch.object(
|
|
||||||
zone_mocks[0], "id", new_callable=PropertyMock(return_value=0)
|
|
||||||
), patch.object(
|
|
||||||
zone_mocks[0], "name", new_callable=PropertyMock(return_value="Zone 0")
|
|
||||||
), patch.object(
|
|
||||||
zone_mocks[1], "id", new_callable=PropertyMock(return_value=1)
|
|
||||||
), patch.object(
|
|
||||||
zone_mocks[1], "name", new_callable=PropertyMock(return_value="Zone 1")
|
|
||||||
), patch.object(
|
|
||||||
alarm_mock,
|
|
||||||
"zones",
|
|
||||||
new_callable=PropertyMock(return_value=zone_mocks),
|
|
||||||
), patch(
|
|
||||||
"homeassistant.components.risco.RiscoAPI.get_state",
|
|
||||||
return_value=alarm_mock,
|
|
||||||
):
|
|
||||||
yield alarm_mock
|
|
||||||
|
|
||||||
|
|
||||||
async def test_cannot_connect(hass):
|
async def test_cannot_connect(hass):
|
||||||
"""Test connection error."""
|
"""Test connection error."""
|
||||||
|
|
||||||
|
@ -78,7 +46,7 @@ async def test_unauthorized(hass):
|
||||||
assert not registry.async_is_registered(SECOND_ENTITY_ID)
|
assert not registry.async_is_registered(SECOND_ENTITY_ID)
|
||||||
|
|
||||||
|
|
||||||
async def test_setup(hass, two_zone_alarm):
|
async def test_setup(hass, two_zone_alarm): # noqa: F811
|
||||||
"""Test entity setup."""
|
"""Test entity setup."""
|
||||||
registry = await hass.helpers.entity_registry.async_get_registry()
|
registry = await hass.helpers.entity_registry.async_get_registry()
|
||||||
|
|
||||||
|
@ -116,9 +84,10 @@ async def _check_state(hass, alarm, triggered, bypassed, entity_id, zone_id):
|
||||||
expected_triggered = STATE_ON if triggered else STATE_OFF
|
expected_triggered = STATE_ON if triggered else STATE_OFF
|
||||||
assert hass.states.get(entity_id).state == expected_triggered
|
assert hass.states.get(entity_id).state == expected_triggered
|
||||||
assert hass.states.get(entity_id).attributes["bypassed"] == bypassed
|
assert hass.states.get(entity_id).attributes["bypassed"] == bypassed
|
||||||
|
assert hass.states.get(entity_id).attributes["zone_id"] == zone_id
|
||||||
|
|
||||||
|
|
||||||
async def test_states(hass, two_zone_alarm):
|
async def test_states(hass, two_zone_alarm): # noqa: F811
|
||||||
"""Test the various alarm states."""
|
"""Test the various alarm states."""
|
||||||
await setup_risco(hass)
|
await setup_risco(hass)
|
||||||
|
|
||||||
|
@ -132,7 +101,7 @@ async def test_states(hass, two_zone_alarm):
|
||||||
await _check_state(hass, two_zone_alarm, False, False, SECOND_ENTITY_ID, 1)
|
await _check_state(hass, two_zone_alarm, False, False, SECOND_ENTITY_ID, 1)
|
||||||
|
|
||||||
|
|
||||||
async def test_bypass(hass, two_zone_alarm):
|
async def test_bypass(hass, two_zone_alarm): # noqa: F811
|
||||||
"""Test bypassing a zone."""
|
"""Test bypassing a zone."""
|
||||||
await setup_risco(hass)
|
await setup_risco(hass)
|
||||||
with patch("homeassistant.components.risco.RiscoAPI.bypass_zone") as mock:
|
with patch("homeassistant.components.risco.RiscoAPI.bypass_zone") as mock:
|
||||||
|
@ -145,7 +114,7 @@ async def test_bypass(hass, two_zone_alarm):
|
||||||
mock.assert_awaited_once_with(0, True)
|
mock.assert_awaited_once_with(0, True)
|
||||||
|
|
||||||
|
|
||||||
async def test_unbypass(hass, two_zone_alarm):
|
async def test_unbypass(hass, two_zone_alarm): # noqa: F811
|
||||||
"""Test unbypassing a zone."""
|
"""Test unbypassing a zone."""
|
||||||
await setup_risco(hass)
|
await setup_risco(hass)
|
||||||
with patch("homeassistant.components.risco.RiscoAPI.bypass_zone") as mock:
|
with patch("homeassistant.components.risco.RiscoAPI.bypass_zone") as mock:
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
"""Tests for the Risco event sensors."""
|
"""Tests for the Risco event sensors."""
|
||||||
import pytest
|
|
||||||
|
|
||||||
from homeassistant.components.risco import (
|
from homeassistant.components.risco import (
|
||||||
LAST_EVENT_TIMESTAMP_KEY,
|
LAST_EVENT_TIMESTAMP_KEY,
|
||||||
CannotConnectError,
|
CannotConnectError,
|
||||||
|
@ -9,6 +7,7 @@ from homeassistant.components.risco import (
|
||||||
from homeassistant.components.risco.const import DOMAIN, EVENTS_COORDINATOR
|
from homeassistant.components.risco.const import DOMAIN, EVENTS_COORDINATOR
|
||||||
|
|
||||||
from .util import TEST_CONFIG, setup_risco
|
from .util import TEST_CONFIG, setup_risco
|
||||||
|
from .util import two_zone_alarm # noqa: F401
|
||||||
|
|
||||||
from tests.async_mock import MagicMock, patch
|
from tests.async_mock import MagicMock, patch
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
@ -60,7 +59,7 @@ TEST_EVENTS = [
|
||||||
name="Alarm is on",
|
name="Alarm is on",
|
||||||
text="Yes it is.",
|
text="Yes it is.",
|
||||||
partition_id=0,
|
partition_id=0,
|
||||||
zone_id=12,
|
zone_id=1,
|
||||||
user_id=None,
|
user_id=None,
|
||||||
group=None,
|
group=None,
|
||||||
priority=0,
|
priority=0,
|
||||||
|
@ -106,16 +105,6 @@ CATEGORIES_TO_EVENTS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def emptry_alarm():
|
|
||||||
"""Fixture to mock an empty alarm."""
|
|
||||||
with patch(
|
|
||||||
"homeassistant.components.risco.RiscoAPI.get_state",
|
|
||||||
return_value=MagicMock(paritions={}, zones={}),
|
|
||||||
):
|
|
||||||
yield
|
|
||||||
|
|
||||||
|
|
||||||
async def test_cannot_connect(hass):
|
async def test_cannot_connect(hass):
|
||||||
"""Test connection error."""
|
"""Test connection error."""
|
||||||
|
|
||||||
|
@ -151,23 +140,29 @@ async def test_unauthorized(hass):
|
||||||
|
|
||||||
|
|
||||||
def _check_state(hass, category, entity_id):
|
def _check_state(hass, category, entity_id):
|
||||||
event = TEST_EVENTS[CATEGORIES_TO_EVENTS[category]]
|
event_index = CATEGORIES_TO_EVENTS[category]
|
||||||
assert hass.states.get(entity_id).state == event.time
|
event = TEST_EVENTS[event_index]
|
||||||
assert hass.states.get(entity_id).attributes["category_id"] == event.category_id
|
state = hass.states.get(entity_id)
|
||||||
assert hass.states.get(entity_id).attributes["category_name"] == event.category_name
|
assert state.state == event.time
|
||||||
assert hass.states.get(entity_id).attributes["type_id"] == event.type_id
|
assert state.attributes["category_id"] == event.category_id
|
||||||
assert hass.states.get(entity_id).attributes["type_name"] == event.type_name
|
assert state.attributes["category_name"] == event.category_name
|
||||||
assert hass.states.get(entity_id).attributes["name"] == event.name
|
assert state.attributes["type_id"] == event.type_id
|
||||||
assert hass.states.get(entity_id).attributes["text"] == event.text
|
assert state.attributes["type_name"] == event.type_name
|
||||||
assert hass.states.get(entity_id).attributes["partition_id"] == event.partition_id
|
assert state.attributes["name"] == event.name
|
||||||
assert hass.states.get(entity_id).attributes["zone_id"] == event.zone_id
|
assert state.attributes["text"] == event.text
|
||||||
assert hass.states.get(entity_id).attributes["user_id"] == event.user_id
|
assert state.attributes["partition_id"] == event.partition_id
|
||||||
assert hass.states.get(entity_id).attributes["group"] == event.group
|
assert state.attributes["zone_id"] == event.zone_id
|
||||||
assert hass.states.get(entity_id).attributes["priority"] == event.priority
|
assert state.attributes["user_id"] == event.user_id
|
||||||
assert hass.states.get(entity_id).attributes["raw"] == event.raw
|
assert state.attributes["group"] == event.group
|
||||||
|
assert state.attributes["priority"] == event.priority
|
||||||
|
assert state.attributes["raw"] == event.raw
|
||||||
|
if event_index == 2:
|
||||||
|
assert state.attributes["zone_entity_id"] == "binary_sensor.zone_1"
|
||||||
|
else:
|
||||||
|
assert "zone_entity_id" not in state.attributes
|
||||||
|
|
||||||
|
|
||||||
async def test_setup(hass, emptry_alarm):
|
async def test_setup(hass, two_zone_alarm): # noqa: F811
|
||||||
"""Test entity setup."""
|
"""Test entity setup."""
|
||||||
registry = await hass.helpers.entity_registry.async_get_registry()
|
registry = await hass.helpers.entity_registry.async_get_registry()
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
"""Utilities for Risco tests."""
|
"""Utilities for Risco tests."""
|
||||||
|
from pytest import fixture
|
||||||
|
|
||||||
from homeassistant.components.risco.const import DOMAIN
|
from homeassistant.components.risco.const import DOMAIN
|
||||||
from homeassistant.const import CONF_PASSWORD, CONF_PIN, CONF_USERNAME
|
from homeassistant.const import CONF_PASSWORD, CONF_PIN, CONF_USERNAME
|
||||||
|
|
||||||
from tests.async_mock import PropertyMock, patch
|
from tests.async_mock import MagicMock, PropertyMock, patch
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
TEST_CONFIG = {
|
TEST_CONFIG = {
|
||||||
|
@ -35,3 +37,34 @@ async def setup_risco(hass, options={}):
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
return config_entry
|
return config_entry
|
||||||
|
|
||||||
|
|
||||||
|
def _zone_mock():
|
||||||
|
return MagicMock(
|
||||||
|
triggered=False,
|
||||||
|
bypassed=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@fixture
|
||||||
|
def two_zone_alarm():
|
||||||
|
"""Fixture to mock alarm with two zones."""
|
||||||
|
zone_mocks = {0: _zone_mock(), 1: _zone_mock()}
|
||||||
|
alarm_mock = MagicMock()
|
||||||
|
with patch.object(
|
||||||
|
zone_mocks[0], "id", new_callable=PropertyMock(return_value=0)
|
||||||
|
), patch.object(
|
||||||
|
zone_mocks[0], "name", new_callable=PropertyMock(return_value="Zone 0")
|
||||||
|
), patch.object(
|
||||||
|
zone_mocks[1], "id", new_callable=PropertyMock(return_value=1)
|
||||||
|
), patch.object(
|
||||||
|
zone_mocks[1], "name", new_callable=PropertyMock(return_value="Zone 1")
|
||||||
|
), patch.object(
|
||||||
|
alarm_mock,
|
||||||
|
"zones",
|
||||||
|
new_callable=PropertyMock(return_value=zone_mocks),
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.risco.RiscoAPI.get_state",
|
||||||
|
return_value=alarm_mock,
|
||||||
|
):
|
||||||
|
yield alarm_mock
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue