Skip to content

Commit

Permalink
fix(2024.6.0): Handling time zones without blocking the event loop
Browse files Browse the repository at this point in the history
Until `2024.6.0` is released, this fix uses `2024.6.0b6`
Fix for unloading custom component
  • Loading branch information
golles committed Jun 4, 2024
1 parent c86107c commit 22fae86
Show file tree
Hide file tree
Showing 13 changed files with 62 additions and 68 deletions.
13 changes: 7 additions & 6 deletions custom_components/knmi/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util import dt
import pytz

from .api import KnmiApiClient
from .const import API_TIMEZONE, DOMAIN
Expand Down Expand Up @@ -87,7 +86,7 @@ def get_value_datetime(
Get a datetime value from the data by a given path.
When the value is absent, the default (None) will be returned and an error will be logged.
"""
timezone = pytz.timezone(API_TIMEZONE)
timezone = dt.get_time_zone(API_TIMEZONE)
value = self.get_value(path, default)

# Timestamp.
Expand All @@ -102,7 +101,7 @@ def get_value_datetime(
if re.match(r"^\d{2}:\d{2}$", value):
_LOGGER.debug("convert %s to datetime (from time HH:MM)", value)
time_array = value.split(":")
today = datetime.now(timezone)
today = datetime.now(tz=timezone)
return today.replace(
hour=int(time_array[0]),
minute=int(time_array[1]),
Expand All @@ -113,20 +112,22 @@ def get_value_datetime(
# Date.
if re.match(r"^\d{2}-\d{2}-\d{4}$", value):
_LOGGER.debug("convert %s to datetime (from date DD-MM-YYYY)", value)
return timezone.localize(datetime.strptime(value, "%d-%m-%Y"))
return datetime.strptime(value, "%d-%m-%Y").replace(tzinfo=timezone)

# Date and time.
if re.match(r"^\d{2}-\d{2}-\d{4} \d{2}:\d{2}:\d{2}$", value):
_LOGGER.debug(
"convert %s to datetime (from date and time DD-MM-YYYY HH:MM:SS)", value
)
return timezone.localize(datetime.strptime(value, "%d-%m-%Y %H:%M:%S"))
return datetime.strptime(value, "%d-%m-%Y %H:%M:%S").replace(
tzinfo=timezone
)

# Date and time without seconds.
if re.match(r"^\d{2}-\d{2}-\d{4} \d{2}:\d{2}$", value):
_LOGGER.debug(
"convert %s to datetime (from date and time DD-MM-YYYY HH:MM)", value
)
return timezone.localize(datetime.strptime(value, "%d-%m-%Y %H:%M"))
return datetime.strptime(value, "%d-%m-%Y %H:%M").replace(tzinfo=timezone)

return default
2 changes: 1 addition & 1 deletion hacs.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "KNMI",
"homeassistant": "2023.9.0",
"homeassistant": "2024.6.0b6",
"render_readme": true
}
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
homeassistant>=2023.9.0
homeassistant>=2024.6.0b6
16 changes: 13 additions & 3 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,25 @@

from custom_components.knmi.const import DOMAIN

from .const import MOCK_CONFIG
from .const import MOCK_CONFIG, MOCK_ENTRY_ID


async def setup_component(hass: HomeAssistant) -> MockConfigEntry:
"""Initialize knmi for tests."""
config_entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test")
"""Setup the custom component for tests"""
config_entry = MockConfigEntry(
domain=DOMAIN, data=MOCK_CONFIG, entry_id=MOCK_ENTRY_ID
)
config_entry.add_to_hass(hass)

assert await async_setup_component(hass=hass, domain=DOMAIN, config=MOCK_CONFIG)
await hass.async_block_till_done()

return config_entry


async def unload_component(
hass: HomeAssistant, config_entry: MockConfigEntry
) -> MockConfigEntry:
"""Unload the custom component for tests"""
await hass.config_entries.async_unload(config_entry.entry_id)
await hass.async_block_till_done()
2 changes: 2 additions & 0 deletions tests/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@
CONF_NAME: "Home",
}

MOCK_ENTRY_ID = "test"

MOCK_UPDATE_CONFIG = {CONF_SCAN_INTERVAL: 600}
5 changes: 2 additions & 3 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
)
from custom_components.knmi.const import API_ENDPOINT

from . import setup_component
from . import setup_component, unload_component
from .const import MOCK_CONFIG


Expand Down Expand Up @@ -101,5 +101,4 @@ async def test_invalid_json_fix(hass: HomeAssistant, mocked_data):
state = hass.states.get("sensor.knmi_air_pressure")
assert state.state == "unknown"

assert await config_entry.async_unload(hass)
await hass.async_block_till_done()
await unload_component(hass, config_entry)
14 changes: 5 additions & 9 deletions tests/test_binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from homeassistant.core import HomeAssistant
import pytest

from . import setup_component
from . import setup_component, unload_component


async def test_knmi_binary_alarm_sensor_is_off(hass: HomeAssistant, mocked_data):
Expand All @@ -22,8 +22,7 @@ async def test_knmi_binary_alarm_sensor_is_off(hass: HomeAssistant, mocked_data)
assert state.attributes.get("next_code") == "geel"
assert str(state.attributes.get("timestamp")) == "2024-02-22 18:00:00+01:00"

assert await config_entry.async_unload(hass)
await hass.async_block_till_done()
await unload_component(hass, config_entry)


@pytest.mark.fixture("response_alarm.json")
Expand All @@ -42,8 +41,7 @@ async def test_knmi_binary_alarm_sensor_is_on(hass: HomeAssistant, mocked_data):
assert state.attributes.get("next_code") == "geel"
assert str(state.attributes.get("timestamp")) == "2024-02-22 18:00:00+01:00"

assert await config_entry.async_unload(hass)
await hass.async_block_till_done()
await unload_component(hass, config_entry)


@freeze_time("2023-02-05T03:30:00+01:00")
Expand All @@ -59,8 +57,7 @@ async def test_knmi_binary_sun_sensor_is_off(hass: HomeAssistant, mocked_data):
assert state.attributes.get("sun_chance1") == 8
assert state.attributes.get("sun_chance2") == 14

assert await config_entry.async_unload(hass)
await hass.async_block_till_done()
await unload_component(hass, config_entry)


@freeze_time("2023-02-05T15:30:00+00:00")
Expand All @@ -71,5 +68,4 @@ async def test_knmi_binary_sun_sensor_is_on(hass: HomeAssistant, mocked_data):
state = hass.states.get("binary_sensor.knmi_sun")
assert state.state == "on"

assert await config_entry.async_unload(hass)
await hass.async_block_till_done()
await unload_component(hass, config_entry)
10 changes: 6 additions & 4 deletions tests/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from custom_components.knmi.const import DOMAIN

from . import setup_component, unload_component
from .const import MOCK_CONFIG, MOCK_UPDATE_CONFIG


Expand Down Expand Up @@ -79,11 +80,10 @@ async def test_options_flow(hass: HomeAssistant):
"""Test an options flow."""
# Create a new MockConfigEntry and add to HASS (we're bypassing config
# flow entirely)
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test")
entry.add_to_hass(hass)
config_entry = await setup_component(hass)

# Initialize an options flow
result = await hass.config_entries.options.async_init(entry.entry_id)
result = await hass.config_entries.options.async_init(config_entry.entry_id)

# Verify that the first options step is a user form
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
Expand All @@ -100,4 +100,6 @@ async def test_options_flow(hass: HomeAssistant):
assert result["title"] == MOCK_CONFIG[CONF_NAME]

# Verify that the options were updated
assert entry.options == MOCK_UPDATE_CONFIG
assert config_entry.options == MOCK_UPDATE_CONFIG

await unload_component(hass, config_entry)
11 changes: 4 additions & 7 deletions tests/test_coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from custom_components.knmi.const import DOMAIN
from custom_components.knmi.coordinator import KnmiDataUpdateCoordinator

from . import setup_component
from . import setup_component, unload_component


async def test_get_value(hass: HomeAssistant, mocked_data, caplog):
Expand All @@ -29,8 +29,7 @@ async def test_get_value(hass: HomeAssistant, mocked_data, caplog):
in caplog.text
)

assert await config_entry.async_unload(hass)
await hass.async_block_till_done()
await unload_component(hass, config_entry)


async def test_get_value_missing_values(hass: HomeAssistant, mocked_data, caplog):
Expand Down Expand Up @@ -63,8 +62,7 @@ async def test_get_value_missing_values(hass: HomeAssistant, mocked_data, caplog
in caplog.text
)

assert await config_entry.async_unload(hass)
await hass.async_block_till_done()
await unload_component(hass, config_entry)


@freeze_time("2024-02-14T12:00:00+00:00")
Expand Down Expand Up @@ -133,5 +131,4 @@ async def test_get_value_datetime(hass: HomeAssistant, mocked_data, caplog):
coordinator.get_value_datetime(["uur_verw", 99, "uur"], "missing") == "missing"
)

assert await config_entry.async_unload(hass)
await hass.async_block_till_done()
await unload_component(hass, config_entry)
16 changes: 7 additions & 9 deletions tests/test_diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from homeassistant.util.json import JsonObjectType
from pytest_homeassistant_custom_component.common import MockConfigEntry
from pytest_homeassistant_custom_component.typing import ClientSessionGenerator

from custom_components.knmi.const import DOMAIN
from custom_components.knmi.diagnostics import TO_REDACT

from .const import MOCK_CONFIG
from . import setup_component, unload_component
from .const import MOCK_ENTRY_ID


async def test_config_entry_diagnostics(
Expand All @@ -22,22 +22,20 @@ async def test_config_entry_diagnostics(
mocked_data,
) -> None:
"""Test config entry diagnostics."""
config_entry = await setup_component(hass)

entry = MockConfigEntry(domain=DOMAIN, data=MOCK_CONFIG, entry_id="test")
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()

result = await get_diagnostics_for_config_entry(hass, hass_client, entry)
result = await get_diagnostics_for_config_entry(hass, hass_client, config_entry)

assert result["config_entry"]["entry_id"] == "test"
assert result["config_entry"]["entry_id"] == MOCK_ENTRY_ID
assert result["config_entry"]["domain"] == DOMAIN

for key in TO_REDACT:
assert result["config_entry"]["data"][key] == "**REDACTED**"

assert result["data"]["liveweer"][0]["plaats"] == "Purmerend"

await unload_component(hass, config_entry)


# The following 2 functions are copied from https://github.com/home-assistant/core/blob/dev/tests/components/diagnostics/__init__.py
async def _get_diagnostics_for_config_entry(
Expand Down
5 changes: 2 additions & 3 deletions tests/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from custom_components.knmi.const import DOMAIN
from custom_components.knmi.coordinator import KnmiDataUpdateCoordinator

from . import setup_component
from . import setup_component, unload_component


async def test_setup_unload_and_reload_entry(hass: HomeAssistant, mocked_data):
Expand Down Expand Up @@ -47,8 +47,7 @@ async def test_setup_entry_exception(hass: HomeAssistant, error_on_get_data):
with pytest.raises(ConfigEntryNotReady):
assert await async_setup_entry(hass, config_entry)

assert await config_entry.async_unload(hass)
await hass.async_block_till_done()
await unload_component(hass, config_entry)


async def test_async_migrate_entry_v1_to_v2(
Expand Down
5 changes: 2 additions & 3 deletions tests/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from homeassistant.core import HomeAssistant

from . import setup_component
from . import setup_component, unload_component


async def test_states(hass: HomeAssistant, mocked_data):
Expand Down Expand Up @@ -78,5 +78,4 @@ async def test_states(hass: HomeAssistant, mocked_data):
state = hass.states.get("sensor.knmi_visibility")
assert state.state == "6990"

assert await config_entry.async_unload(hass)
await hass.async_block_till_done()
await unload_component(hass, config_entry)
Loading

0 comments on commit 22fae86

Please sign in to comment.