From 5a7e4b4dae8d291a53805b646a725202ffb8000c Mon Sep 17 00:00:00 2001 From: On Freund Date: Sun, 8 Nov 2020 20:14:43 +0200 Subject: [PATCH] Add zone_entity_id to Risco events (#39678) * Add zone_entity_id to Risco events * Add comment * Fix dependency on order --- .../components/risco/binary_sensor.py | 14 ++--- homeassistant/components/risco/const.py | 1 + homeassistant/components/risco/sensor.py | 23 +++++++-- tests/components/risco/test_binary_sensor.py | 45 +++------------- tests/components/risco/test_sensor.py | 51 +++++++++---------- tests/components/risco/util.py | 35 ++++++++++++- 6 files changed, 90 insertions(+), 79 deletions(-) diff --git a/homeassistant/components/risco/binary_sensor.py b/homeassistant/components/risco/binary_sensor.py index 757a30252c28..99995ee5b64e 100644 --- a/homeassistant/components/risco/binary_sensor.py +++ b/homeassistant/components/risco/binary_sensor.py @@ -5,7 +5,7 @@ from homeassistant.components.binary_sensor import ( ) from homeassistant.helpers import entity_platform -from .const import DATA_COORDINATOR, DOMAIN +from .const import DATA_COORDINATOR, DATA_ZONES, DOMAIN from .entity import RiscoEntity 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] - entities = [ - RiscoBinarySensor(coordinator, zone_id, zone) + entities = { + zone_id: RiscoBinarySensor(coordinator, zone_id, zone) for zone_id, zone in coordinator.data.zones.items() - ] - - async_add_entities(entities, False) + } + hass.data[DOMAIN][config_entry.entry_id][DATA_ZONES] = entities + async_add_entities(entities.values(), False) class RiscoBinarySensor(BinarySensorEntity, RiscoEntity): @@ -63,7 +63,7 @@ class RiscoBinarySensor(BinarySensorEntity, RiscoEntity): @property def device_state_attributes(self): """Return the state attributes.""" - return {"bypassed": self._zone.bypassed} + return {"zone_id": self._zone_id, "bypassed": self._zone.bypassed} @property def is_on(self): diff --git a/homeassistant/components/risco/const.py b/homeassistant/components/risco/const.py index 46eb011ba5b5..80153153530f 100644 --- a/homeassistant/components/risco/const.py +++ b/homeassistant/components/risco/const.py @@ -11,6 +11,7 @@ DOMAIN = "risco" RISCO_EVENT = "risco_event" DATA_COORDINATOR = "risco" +DATA_ZONES = "zones" EVENTS_COORDINATOR = "risco_events" DEFAULT_SCAN_INTERVAL = 30 diff --git a/homeassistant/components/risco/sensor.py b/homeassistant/components/risco/sensor.py index 3b4ee882b3ca..4694fc86238a 100644 --- a/homeassistant/components/risco/sensor.py +++ b/homeassistant/components/risco/sensor.py @@ -2,7 +2,7 @@ from homeassistant.const import DEVICE_CLASS_TIMESTAMP from homeassistant.helpers.update_coordinator import CoordinatorEntity -from .const import DOMAIN, EVENTS_COORDINATOR +from .const import DATA_ZONES, DOMAIN, EVENTS_COORDINATOR CATEGORIES = { 2: "Alarm", @@ -29,22 +29,28 @@ async def async_setup_entry(hass, config_entry, async_add_entities): """Set up sensors for device.""" coordinator = hass.data[DOMAIN][config_entry.entry_id][EVENTS_COORDINATOR] 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) class RiscoSensor(CoordinatorEntity): """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.""" super().__init__(coordinator) self._event = None self._category_id = category_id self._excludes = excludes self._name = name + self._entry_id = entry_id @property def name(self): @@ -88,7 +94,14 @@ class RiscoSensor(CoordinatorEntity): if self._event is 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 def device_class(self): diff --git a/tests/components/risco/test_binary_sensor.py b/tests/components/risco/test_binary_sensor.py index 9aa56f64e51a..50b9c43c5c35 100644 --- a/tests/components/risco/test_binary_sensor.py +++ b/tests/components/risco/test_binary_sensor.py @@ -1,51 +1,19 @@ """Tests for the Risco binary sensors.""" -import pytest - from homeassistant.components.risco import CannotConnectError, UnauthorizedError from homeassistant.components.risco.const import DOMAIN from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.helpers.entity_component import async_update_entity 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 FIRST_ENTITY_ID = "binary_sensor.zone_0" 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): """Test connection error.""" @@ -78,7 +46,7 @@ async def test_unauthorized(hass): 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.""" 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 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["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.""" 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) -async def test_bypass(hass, two_zone_alarm): +async def test_bypass(hass, two_zone_alarm): # noqa: F811 """Test bypassing a zone.""" await setup_risco(hass) 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) -async def test_unbypass(hass, two_zone_alarm): +async def test_unbypass(hass, two_zone_alarm): # noqa: F811 """Test unbypassing a zone.""" await setup_risco(hass) with patch("homeassistant.components.risco.RiscoAPI.bypass_zone") as mock: diff --git a/tests/components/risco/test_sensor.py b/tests/components/risco/test_sensor.py index cc76c0b970b3..acb92088478a 100644 --- a/tests/components/risco/test_sensor.py +++ b/tests/components/risco/test_sensor.py @@ -1,6 +1,4 @@ """Tests for the Risco event sensors.""" -import pytest - from homeassistant.components.risco import ( LAST_EVENT_TIMESTAMP_KEY, CannotConnectError, @@ -9,6 +7,7 @@ from homeassistant.components.risco import ( from homeassistant.components.risco.const import DOMAIN, EVENTS_COORDINATOR from .util import TEST_CONFIG, setup_risco +from .util import two_zone_alarm # noqa: F401 from tests.async_mock import MagicMock, patch from tests.common import MockConfigEntry @@ -60,7 +59,7 @@ TEST_EVENTS = [ name="Alarm is on", text="Yes it is.", partition_id=0, - zone_id=12, + zone_id=1, user_id=None, group=None, 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): """Test connection error.""" @@ -151,23 +140,29 @@ async def test_unauthorized(hass): def _check_state(hass, category, entity_id): - event = TEST_EVENTS[CATEGORIES_TO_EVENTS[category]] - assert hass.states.get(entity_id).state == event.time - assert hass.states.get(entity_id).attributes["category_id"] == event.category_id - assert hass.states.get(entity_id).attributes["category_name"] == event.category_name - assert hass.states.get(entity_id).attributes["type_id"] == event.type_id - assert hass.states.get(entity_id).attributes["type_name"] == event.type_name - assert hass.states.get(entity_id).attributes["name"] == event.name - assert hass.states.get(entity_id).attributes["text"] == event.text - assert hass.states.get(entity_id).attributes["partition_id"] == event.partition_id - assert hass.states.get(entity_id).attributes["zone_id"] == event.zone_id - assert hass.states.get(entity_id).attributes["user_id"] == event.user_id - assert hass.states.get(entity_id).attributes["group"] == event.group - assert hass.states.get(entity_id).attributes["priority"] == event.priority - assert hass.states.get(entity_id).attributes["raw"] == event.raw + event_index = CATEGORIES_TO_EVENTS[category] + event = TEST_EVENTS[event_index] + state = hass.states.get(entity_id) + assert state.state == event.time + assert state.attributes["category_id"] == event.category_id + assert state.attributes["category_name"] == event.category_name + assert state.attributes["type_id"] == event.type_id + assert state.attributes["type_name"] == event.type_name + assert state.attributes["name"] == event.name + assert state.attributes["text"] == event.text + assert state.attributes["partition_id"] == event.partition_id + assert state.attributes["zone_id"] == event.zone_id + assert state.attributes["user_id"] == event.user_id + 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.""" registry = await hass.helpers.entity_registry.async_get_registry() diff --git a/tests/components/risco/util.py b/tests/components/risco/util.py index a60be70e8616..704c12fb846b 100644 --- a/tests/components/risco/util.py +++ b/tests/components/risco/util.py @@ -1,8 +1,10 @@ """Utilities for Risco tests.""" +from pytest import fixture + from homeassistant.components.risco.const import DOMAIN 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 TEST_CONFIG = { @@ -35,3 +37,34 @@ async def setup_risco(hass, options={}): await hass.async_block_till_done() 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