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:
On Freund 2020-11-08 20:14:43 +02:00 committed by GitHub
parent 7c397a02b7
commit 5a7e4b4dae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 90 additions and 79 deletions

View file

@ -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):

View file

@ -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

View file

@ -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):

View file

@ -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:

View file

@ -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()

View file

@ -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