core/tests/components/eafm/test_sensor.py

462 lines
16 KiB
Python
Raw Normal View History

"""Tests for polling measures."""
from collections.abc import Callable, Coroutine
import datetime
from typing import Any
from unittest.mock import AsyncMock
import aiohttp
import pytest
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT, STATE_UNAVAILABLE
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
import homeassistant.util.dt as dt_util
from tests.common import MockConfigEntry, async_fire_time_changed
DUMMY_REQUEST_INFO = aiohttp.client.RequestInfo(
url="http://example.com", method="GET", headers={}, real_url="http://example.com"
)
CONNECTION_EXCEPTIONS = [
aiohttp.ClientConnectionError("Mock connection error"),
aiohttp.ClientResponseError(DUMMY_REQUEST_INFO, [], message="Mock response error"),
]
async def async_setup_test_fixture(
hass: HomeAssistant, mock_get_station: AsyncMock, initial_value: dict[str, Any]
) -> tuple[MockConfigEntry, Callable[[Any], Coroutine[Any, Any, None]]]:
"""Create a dummy config entry for testing polling."""
mock_get_station.return_value = initial_value
entry = MockConfigEntry(
version=1,
domain="eafm",
entry_id="VikingRecorder1234",
data={"station": "L1234"},
title="Viking Recorder",
)
entry.add_to_hass(hass)
assert await async_setup_component(hass, "eafm", {})
assert entry.state is ConfigEntryState.LOADED
await hass.async_block_till_done()
async def poll(value):
mock_get_station.reset_mock(return_value=True, side_effect=True)
if isinstance(value, Exception):
mock_get_station.side_effect = value
else:
mock_get_station.return_value = value
next_update = dt_util.utcnow() + datetime.timedelta(60 * 15)
async_fire_time_changed(hass, next_update)
await hass.async_block_till_done()
return entry, poll
async def test_reading_measures_not_list(hass: HomeAssistant, mock_get_station) -> None:
2023-02-03 22:08:48 +00:00
"""Test that a measure can be a dict not a list.
E.g. https://environment.data.gov.uk/flood-monitoring/id/stations/751110
"""
_ = await async_setup_test_fixture(
hass,
mock_get_station,
{
"label": "My station",
"measures": {
"@id": "really-long-unique-id",
"label": "York Viking Recorder - level-stage-i-15_min----",
"qualifier": "Stage",
"parameterName": "Water Level",
"latestReading": {"value": 5},
"stationReference": "L1234",
},
},
)
state = hass.states.get("sensor.my_station_water_level_stage")
assert state.state == "5"
async def test_reading_no_unit(hass: HomeAssistant, mock_get_station) -> None:
2023-02-03 22:08:48 +00:00
"""Test that a sensor functions even if its unit is not known.
E.g. https://environment.data.gov.uk/flood-monitoring/id/stations/L0410
"""
_ = await async_setup_test_fixture(
hass,
mock_get_station,
{
"label": "My station",
"measures": [
{
"@id": "really-long-unique-id",
"label": "York Viking Recorder - level-stage-i-15_min----",
"qualifier": "Stage",
"parameterName": "Water Level",
"latestReading": {"value": 5},
"stationReference": "L1234",
}
],
},
)
state = hass.states.get("sensor.my_station_water_level_stage")
assert state.state == "5"
async def test_ignore_invalid_latest_reading(
hass: HomeAssistant, mock_get_station
) -> None:
2023-02-03 22:08:48 +00:00
"""Test that a sensor functions even if its unit is not known.
E.g. https://environment.data.gov.uk/flood-monitoring/id/stations/L0410
"""
_ = await async_setup_test_fixture(
hass,
mock_get_station,
{
"label": "My station",
"measures": [
{
"@id": "really-long-unique-id",
"label": "York Viking Recorder - level-stage-i-15_min----",
"qualifier": "Stage",
"parameterName": "Water Level",
"latestReading": "http://environment.data.gov.uk/flood-monitoring/data/readings/L0410-level-stage-i-15_min----/2017-02-22T10-30-00Z",
"stationReference": "L0410",
},
{
"@id": "really-long-unique-id",
"label": "York Viking Recorder - level-stage-i-15_min----",
"qualifier": "Stage",
"parameterName": "Other",
"latestReading": {"value": 5},
"stationReference": "L0411",
},
],
},
)
state = hass.states.get("sensor.my_station_water_level_stage")
assert state is None
state = hass.states.get("sensor.my_station_other_stage")
assert state.state == "5"
@pytest.mark.parametrize("exception", CONNECTION_EXCEPTIONS)
async def test_reading_unavailable(
hass: HomeAssistant, mock_get_station, exception
) -> None:
"""Test that a sensor is marked as unavailable if there is a connection error."""
_, poll = await async_setup_test_fixture(
hass,
mock_get_station,
{
"label": "My station",
"measures": [
{
"@id": "really-long-unique-id",
"label": "York Viking Recorder - level-stage-i-15_min----",
"qualifier": "Stage",
"parameterName": "Water Level",
"latestReading": {"value": 5},
"stationReference": "L1234",
"unit": "http://qudt.org/1.1/vocab/unit#Meter",
"unitName": "m",
}
],
},
)
state = hass.states.get("sensor.my_station_water_level_stage")
assert state.state == "5"
await poll(exception)
state = hass.states.get("sensor.my_station_water_level_stage")
assert state.state == "unavailable"
@pytest.mark.parametrize("exception", CONNECTION_EXCEPTIONS)
async def test_recover_from_failure(
hass: HomeAssistant, mock_get_station, exception
) -> None:
"""Test that a sensor recovers from failures."""
_, poll = await async_setup_test_fixture(
hass,
mock_get_station,
{
"label": "My station",
"measures": [
{
"@id": "really-long-unique-id",
"label": "York Viking Recorder - level-stage-i-15_min----",
"qualifier": "Stage",
"parameterName": "Water Level",
"latestReading": {"value": 5},
"stationReference": "L1234",
"unit": "http://qudt.org/1.1/vocab/unit#Meter",
"unitName": "m",
}
],
},
)
state = hass.states.get("sensor.my_station_water_level_stage")
assert state.state == "5"
await poll(exception)
state = hass.states.get("sensor.my_station_water_level_stage")
assert state.state == "unavailable"
await poll(
{
"label": "My station",
"measures": [
{
"@id": "really-long-unique-id",
"label": "York Viking Recorder - level-stage-i-15_min----",
"qualifier": "Stage",
"parameterName": "Water Level",
"latestReading": {"value": 56},
"stationReference": "L1234",
"unit": "http://qudt.org/1.1/vocab/unit#Meter",
"unitName": "m",
}
],
},
)
state = hass.states.get("sensor.my_station_water_level_stage")
assert state.state == "56"
async def test_reading_is_sampled(hass: HomeAssistant, mock_get_station) -> None:
"""Test that a sensor is added and polled."""
await async_setup_test_fixture(
hass,
mock_get_station,
{
"label": "My station",
"measures": [
{
"@id": "really-long-unique-id",
"label": "York Viking Recorder - level-stage-i-15_min----",
"qualifier": "Stage",
"parameterName": "Water Level",
"latestReading": {"value": 5},
"stationReference": "L1234",
"unit": "http://qudt.org/1.1/vocab/unit#Meter",
"unitName": "m",
}
],
},
)
state = hass.states.get("sensor.my_station_water_level_stage")
assert state.state == "5"
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == "m"
async def test_multiple_readings_are_sampled(
hass: HomeAssistant, mock_get_station
) -> None:
"""Test that multiple sensors are added and polled."""
await async_setup_test_fixture(
hass,
mock_get_station,
{
"label": "My station",
"measures": [
{
"@id": "really-long-unique-id",
"label": "York Viking Recorder - level-stage-i-15_min----",
"qualifier": "Stage",
"parameterName": "Water Level",
"latestReading": {"value": 5},
"stationReference": "L1234",
"unit": "http://qudt.org/1.1/vocab/unit#Meter",
"unitName": "m",
},
{
"@id": "really-long-unique-id-2",
"label": "York Viking Recorder - level-stage-i-15_min----",
"qualifier": "Second Stage",
"parameterName": "Water Level",
"latestReading": {"value": 4},
"stationReference": "L1234",
"unit": "http://qudt.org/1.1/vocab/unit#Meter",
"unitName": "m",
},
],
},
)
state = hass.states.get("sensor.my_station_water_level_stage")
assert state.state == "5"
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == "m"
state = hass.states.get("sensor.my_station_water_level_second_stage")
assert state.state == "4"
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == "m"
async def test_ignore_no_latest_reading(hass: HomeAssistant, mock_get_station) -> None:
"""Test that a measure is ignored if it has no latest reading."""
await async_setup_test_fixture(
hass,
mock_get_station,
{
"label": "My station",
"measures": [
{
"@id": "really-long-unique-id",
"label": "York Viking Recorder - level-stage-i-15_min----",
"qualifier": "Stage",
"parameterName": "Water Level",
"latestReading": {"value": 5},
"stationReference": "L1234",
"unit": "http://qudt.org/1.1/vocab/unit#Meter",
"unitName": "m",
},
{
"@id": "really-long-unique-id-2",
"label": "York Viking Recorder - level-stage-i-15_min----",
"qualifier": "Second Stage",
"parameterName": "Water Level",
"stationReference": "L1234",
"unit": "http://qudt.org/1.1/vocab/unit#Meter",
"unitName": "m",
},
],
},
)
state = hass.states.get("sensor.my_station_water_level_stage")
assert state.state == "5"
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == "m"
state = hass.states.get("sensor.my_station_water_level_second_stage")
assert state is None
async def test_no_measures(hass: HomeAssistant, mock_get_station) -> None:
"""Test no measures in the data."""
await async_setup_test_fixture(
hass,
mock_get_station,
{
"label": "My station",
},
)
assert hass.states.async_entity_ids_count() == 0
async def test_mark_existing_as_unavailable_if_no_latest(
hass: HomeAssistant, mock_get_station
) -> None:
"""Test that a measure is marked as unavailable if it has no latest reading."""
_, poll = await async_setup_test_fixture(
hass,
mock_get_station,
{
"label": "My station",
"measures": [
{
"@id": "really-long-unique-id",
"label": "York Viking Recorder - level-stage-i-15_min----",
"qualifier": "Stage",
"parameterName": "Water Level",
"latestReading": {"value": 5},
"stationReference": "L1234",
"unit": "http://qudt.org/1.1/vocab/unit#Meter",
"unitName": "m",
}
],
},
)
state = hass.states.get("sensor.my_station_water_level_stage")
assert state.state == "5"
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == "m"
await poll(
{
"label": "My station",
"measures": [
{
"@id": "really-long-unique-id",
"label": "York Viking Recorder - level-stage-i-15_min----",
"qualifier": "Stage",
"parameterName": "Water Level",
"stationReference": "L1234",
"unit": "http://qudt.org/1.1/vocab/unit#Meter",
"unitName": "m",
}
],
}
)
state = hass.states.get("sensor.my_station_water_level_stage")
assert state.state == "unavailable"
await poll(
{
"label": "My station",
"measures": [
{
"@id": "really-long-unique-id",
"label": "York Viking Recorder - level-stage-i-15_min----",
"qualifier": "Stage",
"parameterName": "Water Level",
"latestReading": {"value": 5},
"stationReference": "L1234",
"unit": "http://qudt.org/1.1/vocab/unit#Meter",
"unitName": "m",
}
],
}
)
state = hass.states.get("sensor.my_station_water_level_stage")
assert state.state == "5"
async def test_unload_entry(hass: HomeAssistant, mock_get_station) -> None:
"""Test being able to unload an entry."""
entry, _ = await async_setup_test_fixture(
hass,
mock_get_station,
{
"label": "My station",
"measures": [
{
"@id": "really-long-unique-id",
"label": "York Viking Recorder - level-stage-i-15_min----",
"qualifier": "Stage",
"parameterName": "Water Level",
"latestReading": {"value": 5},
"stationReference": "L1234",
"unit": "http://qudt.org/1.1/vocab/unit#Meter",
"unitName": "m",
}
],
},
)
# And there should be an entity
state = hass.states.get("sensor.my_station_water_level_stage")
assert state.state == "5"
await hass.config_entries.async_unload(entry.entry_id)
# And the entity should be unavailable
assert (
hass.states.get("sensor.my_station_water_level_stage").state
== STATE_UNAVAILABLE
)