mirror of
https://github.com/home-assistant/core.git
synced 2026-05-18 14:29:57 +01:00
227 lines
8.5 KiB
Python
227 lines
8.5 KiB
Python
"""Test the OMIE sensor platform."""
|
|
|
|
import datetime as dt
|
|
from unittest.mock import MagicMock
|
|
|
|
import aiohttp
|
|
from freezegun import freeze_time
|
|
from pyomie.model import OMIEResults
|
|
import pytest
|
|
|
|
from homeassistant.const import STATE_UNAVAILABLE
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers import entity_registry as er
|
|
|
|
from . import spot_price_fetcher
|
|
|
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
|
|
|
|
|
async def test_sensor_setup(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyomie: MagicMock,
|
|
entity_registry: er.EntityRegistry,
|
|
) -> None:
|
|
"""Test sensor platform setup."""
|
|
mock_config_entry.add_to_hass(hass)
|
|
|
|
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
entities = er.async_entries_for_config_entry(
|
|
entity_registry, mock_config_entry.entry_id
|
|
)
|
|
|
|
# Should have 2 sensors (PT and ES)
|
|
assert len(entities) == 2
|
|
|
|
unique_ids = {entity.unique_id for entity in entities}
|
|
expected_ids = {"pt_spot_price", "es_spot_price"}
|
|
assert unique_ids == expected_ids
|
|
|
|
|
|
@pytest.mark.usefixtures("hass_lisbon")
|
|
async def test_sensor_state_lisbon_timezone(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyomie: MagicMock,
|
|
mock_omie_results_jan15: OMIEResults,
|
|
mock_omie_results_jan16: OMIEResults,
|
|
) -> None:
|
|
"""Test sensor state updates in Lisbon timezone across publication boundary."""
|
|
mock_config_entry.add_to_hass(hass)
|
|
|
|
mock_pyomie.spot_price.side_effect = spot_price_fetcher(
|
|
{
|
|
"2024-01-15": mock_omie_results_jan15,
|
|
"2024-01-16": mock_omie_results_jan16,
|
|
}
|
|
)
|
|
|
|
# Step 1: 1 PM CET (before 13:30 CET publication)
|
|
# Lisbon day spans two CET dates: Jan 15 available, Jan 16 not yet published
|
|
with freeze_time("2024-01-15T12:01:00Z"): # 12:01 UTC = 12:01 Lisbon = 13:01 CET
|
|
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
assert mock_pyomie.spot_price.call_count == 1
|
|
|
|
# Step 2: 3 PM CET - verify listeners update with existing data, no new API calls
|
|
mock_pyomie.spot_price.reset_mock()
|
|
with freeze_time("2024-01-15T14:01:00Z"): # 14:01 UTC = 14:01 Lisbon = 15:01 CET
|
|
async_fire_time_changed(hass)
|
|
await hass.async_block_till_done()
|
|
assert mock_pyomie.spot_price.call_count == 0
|
|
|
|
pt_state_14 = hass.states.get("sensor.omie_portugal_spot_price")
|
|
es_state_14 = hass.states.get("sensor.omie_spain_spot_price")
|
|
assert pt_state_14.state == "351151500.0"
|
|
assert es_state_14.state == "34151500.0"
|
|
assert pt_state_14.attributes["unit_of_measurement"] == "€/kWh"
|
|
assert es_state_14.attributes["unit_of_measurement"] == "€/kWh"
|
|
|
|
# 23 UTC = 23 Lisbon = 00 CET (+1 day)
|
|
mock_pyomie.spot_price.reset_mock()
|
|
with freeze_time("2024-01-15T23:01:00Z"):
|
|
async_fire_time_changed(hass)
|
|
await hass.async_block_till_done()
|
|
|
|
# CET has rolled over to the next date, one additional
|
|
# call to OMIE must be made.
|
|
assert mock_pyomie.spot_price.call_count == 1
|
|
pt_state_23 = hass.states.get("sensor.omie_portugal_spot_price")
|
|
es_state_23 = hass.states.get("sensor.omie_spain_spot_price")
|
|
assert pt_state_23.state == "351160000.0"
|
|
assert es_state_23.state == "34160000.0"
|
|
|
|
# 00 UTC (+1 day) = 00 Lisbon (+1 day) = 01 CET (+1 day)
|
|
mock_pyomie.spot_price.reset_mock()
|
|
with freeze_time("2024-01-16T00:31:00Z"):
|
|
async_fire_time_changed(hass)
|
|
await hass.async_block_till_done()
|
|
|
|
# No additional API calls should be made, was already fetched at 00 PM CET
|
|
assert mock_pyomie.spot_price.call_count == 0
|
|
pt_state_00 = hass.states.get("sensor.omie_portugal_spot_price")
|
|
es_state_00 = hass.states.get("sensor.omie_spain_spot_price")
|
|
assert pt_state_00.state == "351160130.0"
|
|
assert es_state_00.state == "34160130.0"
|
|
|
|
|
|
@pytest.mark.usefixtures("hass_madrid")
|
|
async def test_sensor_state_madrid_timezone(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyomie: MagicMock,
|
|
mock_omie_results_jan15: OMIEResults,
|
|
mock_omie_results_jan16: OMIEResults,
|
|
) -> None:
|
|
"""Test sensor state updates in Madrid timezone across publication boundary."""
|
|
mock_config_entry.add_to_hass(hass)
|
|
|
|
mock_pyomie.spot_price.side_effect = spot_price_fetcher(
|
|
{
|
|
"2024-01-15": mock_omie_results_jan15,
|
|
"2024-01-16": mock_omie_results_jan16,
|
|
}
|
|
)
|
|
|
|
# Step 1: 1 PM CET (before 13:30 CET publication) - only Jan 15 data available
|
|
with freeze_time("2024-01-15T12:01:00Z"): # 12:00 UTC = 1 PM CET (Madrid)
|
|
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
assert mock_pyomie.spot_price.call_count == 1
|
|
|
|
# Step 2: 2 PM CET (after 13:30 CET publication) - Jan 16 data now available
|
|
mock_pyomie.spot_price.reset_mock()
|
|
with freeze_time("2024-01-15T13:01:00Z"): # 13:00 UTC = 2 PM CET (Madrid)
|
|
async_fire_time_changed(hass)
|
|
await hass.async_block_till_done()
|
|
|
|
# No additional call needed - Madrid only needs Jan 15 for the full day
|
|
assert mock_pyomie.spot_price.call_count == 0
|
|
|
|
# Step 3: 3 PM CET - verify listeners update with existing data, no new API calls
|
|
mock_pyomie.spot_price.reset_mock()
|
|
with freeze_time("2024-01-15T14:23:00Z"): # 14:23 UTC = 3:23 PM CET (Madrid)
|
|
async_fire_time_changed(hass)
|
|
await hass.async_block_till_done()
|
|
|
|
# Still no additional call - Madrid doesn't need Jan 16 for Jan 15 prices
|
|
assert mock_pyomie.spot_price.call_count == 0
|
|
|
|
pt_state = hass.states.get("sensor.omie_portugal_spot_price")
|
|
es_state = hass.states.get("sensor.omie_spain_spot_price")
|
|
assert pt_state.state == "351151515.0"
|
|
assert es_state.state == "34151515.0"
|
|
assert pt_state.attributes["unit_of_measurement"] == "€/kWh"
|
|
assert es_state.attributes["unit_of_measurement"] == "€/kWh"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"raise_exc", [Exception("something bad"), aiohttp.ClientError("Connection timeout")]
|
|
)
|
|
async def test_sensor_unavailable_when_pyomie_throws(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyomie: MagicMock,
|
|
mock_omie_results_jan15: OMIEResults,
|
|
raise_exc: Exception,
|
|
) -> None:
|
|
"""Test sensor becomes unavailable when pyomie throws."""
|
|
mock_config_entry.add_to_hass(hass)
|
|
|
|
mock_pyomie.spot_price.side_effect = spot_price_fetcher(
|
|
{"2024-01-15": mock_omie_results_jan15}
|
|
)
|
|
|
|
# Setup at 22:01 UTC (23:01 CET, still Jan 15 in CET)
|
|
with freeze_time("2024-01-15T22:01:00Z"):
|
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
# Advance to 23:16 UTC (00:16 CET Jan 16) — new CET day forces a fresh fetch
|
|
mock_pyomie.spot_price.side_effect = raise_exc
|
|
with freeze_time("2024-01-15T23:16:02Z"):
|
|
async_fire_time_changed(hass)
|
|
await hass.async_block_till_done()
|
|
|
|
pt_state = hass.states.get("sensor.omie_portugal_spot_price")
|
|
es_state = hass.states.get("sensor.omie_spain_spot_price")
|
|
assert pt_state.state == STATE_UNAVAILABLE
|
|
assert es_state.state == STATE_UNAVAILABLE
|
|
|
|
|
|
async def test_sensor_unavailable_when_pyomie_returns_incomplete_data(
|
|
hass: HomeAssistant,
|
|
mock_config_entry: MockConfigEntry,
|
|
mock_pyomie: MagicMock,
|
|
) -> None:
|
|
"""Test sensor becomes unavailable when no data is available."""
|
|
mock_config_entry.add_to_hass(hass)
|
|
|
|
mock_pyomie.spot_price.side_effect = spot_price_fetcher(
|
|
{
|
|
"2024-01-15": OMIEResults(
|
|
updated_at=dt.datetime(2024, 1, 15, 12, 1, tzinfo=dt.UTC),
|
|
market_date=dt.date.fromisoformat("2024-01-15"),
|
|
contents=None,
|
|
raw="",
|
|
)
|
|
}
|
|
)
|
|
|
|
with freeze_time("2024-01-15T12:01:00Z"):
|
|
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
with freeze_time("2024-01-15T12:16:02Z"):
|
|
async_fire_time_changed(hass)
|
|
await hass.async_block_till_done()
|
|
|
|
# Both sensors should be unavailable
|
|
pt_state = hass.states.get("sensor.omie_portugal_spot_price")
|
|
es_state = hass.states.get("sensor.omie_spain_spot_price")
|
|
assert pt_state.state == STATE_UNAVAILABLE
|
|
assert es_state.state == STATE_UNAVAILABLE
|