Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update stookwijzer api to atlas leefomgeving and add extra sensors #104846

Closed
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
b16c468
Add files via upload
fwestenberg Nov 3, 2023
9f4eff8
Update requirements_all.txt
fwestenberg Nov 3, 2023
2ca8e8b
Update requirements_test_all.txt
fwestenberg Nov 3, 2023
2bd8707
Add files via upload
fwestenberg Nov 3, 2023
0cdacf1
Update homeassistant/components/stookwijzer/const.py
fwestenberg Nov 3, 2023
26b8893
Bump stookalert==1.4.2
fwestenberg Nov 3, 2023
89f2614
Bump stookalert==1.4.2
fwestenberg Nov 3, 2023
28993d9
Bump stookalert==1.4.2
fwestenberg Nov 3, 2023
b434d9c
Update homeassistant/components/stookwijzer/strings.json
fwestenberg Nov 3, 2023
b955c59
Update homeassistant/components/stookwijzer/diagnostics.py
fwestenberg Nov 3, 2023
128e08b
remove extra state attributes
fwestenberg Nov 28, 2023
e805815
Merge branch 'dev' into Update-Stookwijzer-api-to-Atlas-Leefomgeving
fwestenberg Nov 28, 2023
d1b7e4b
Add multiple sensors and coordinator
fwestenberg Nov 30, 2023
254cceb
Update diagnostics
fwestenberg Nov 30, 2023
f7eef01
Add coordinator to init
fwestenberg Nov 30, 2023
3aa8c2b
Update Stookwijzer tests
fwestenberg Dec 1, 2023
7c7a743
Merge branch 'dev' into update-stookwijzer-to-atlas-leefomgeving-with…
fwestenberg Jan 31, 2024
25d7acd
Fix mypy inheritance error
fwestenberg Feb 3, 2024
b7208f2
Merge branch 'dev' into update-stookwijzer-to-atlas-leefomgeving-with…
fwestenberg Feb 3, 2024
73cc7a6
Merge branch 'dev' into update-stookwijzer-to-atlas-leefomgeving-with…
fwestenberg Sep 24, 2024
ac9890c
Merge branch 'dev' into update-stookwijzer-to-atlas-leefomgeving-with…
fwestenberg Sep 24, 2024
a566b1d
Use entry.runtime_data, improve coding
fwestenberg Sep 25, 2024
5cb3f14
Improve tests, delete py.typed
fwestenberg Sep 25, 2024
f4992bc
Improve translation
fwestenberg Sep 26, 2024
49fbc2f
Improve description
fwestenberg Sep 26, 2024
c730e67
Improve tests
fwestenberg Sep 26, 2024
c58823c
Bump stookwijzer to v1.5.0
fwestenberg Nov 8, 2024
e496596
Merge branch 'dev' into update-stookwijzer-to-atlas-leefomgeving-with…
fwestenberg Nov 8, 2024
8d328f7
Merge branch 'home-assistant:dev' into update-stookwijzer-to-atlas-le…
fwestenberg Nov 8, 2024
b3c5dd0
Update test URL to match stookwijzer v1.5.0
fwestenberg Nov 8, 2024
22a436d
Remove stookalert binary sensor
fwestenberg Nov 14, 2024
71f76f7
Fix entity name translation
fwestenberg Nov 21, 2024
b3d0691
Rename Stookwijzer sensor to Advice
fwestenberg Nov 21, 2024
a466834
Merge entity.py and sensor.py (obsolete)
fwestenberg Nov 21, 2024
f436c29
Merge branch 'dev' into update-stookwijzer-to-atlas-leefomgeving-with…
frenck Nov 24, 2024
767ff87
Slightly tweak migration + translations
frenck Nov 24, 2024
28f702e
Add missing data description
frenck Nov 24, 2024
b4e1a19
Remove domain from unique ID
frenck Nov 24, 2024
c7ffda3
Remove unneeded variable in config flow
frenck Nov 24, 2024
b803b93
Add translations for failed coordinator update
frenck Nov 24, 2024
2157231
Refactoring...
frenck Nov 24, 2024
dd2ce71
Let HA handles translations with devices classes
frenck Nov 24, 2024
ac2b61a
Clean up commented out stuff
frenck Nov 24, 2024
e59ccfc
Migrate entity entry unique ID
frenck Nov 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 45 additions & 8 deletions homeassistant/components/stookwijzer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,66 @@

from __future__ import annotations

import logging

from stookwijzer import Stookwijzer

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_LATITUDE, CONF_LOCATION, CONF_LONGITUDE, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from .coordinator import StookwijzerCoordinator

PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR]

_LOGGER = logging.getLogger(__name__)

type StookwijzerConfigEntry = ConfigEntry[StookwijzerCoordinator]


from .const import DOMAIN
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
"""Migrate old entry."""
_LOGGER.debug("Migrating from version %s", config_entry.version)

PLATFORMS = [Platform.SENSOR]
if config_entry.version == 1:
session = async_get_clientsession(hass)
x, y = await Stookwijzer.async_transform_coordinates(
session,
config_entry.data[CONF_LOCATION][CONF_LATITUDE],
config_entry.data[CONF_LOCATION][CONF_LONGITUDE],
)

if not x or not y:
_LOGGER.error(
"Migration to version %s not successful", config_entry.version
)
return False
hass.config_entries.async_update_entry(
config_entry, version=2, data={CONF_LATITUDE: x, CONF_LONGITUDE: y}
)

_LOGGER.debug("Migration to version %s successful", config_entry.version)

return True


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
frenck marked this conversation as resolved.
Show resolved Hide resolved
"""Set up Stookwijzer from a config entry."""
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = Stookwijzer(
entry.data[CONF_LOCATION][CONF_LATITUDE],
entry.data[CONF_LOCATION][CONF_LONGITUDE],
client = Stookwijzer(
async_get_clientsession(hass),
entry.data[CONF_LATITUDE],
entry.data[CONF_LONGITUDE],
)
frenck marked this conversation as resolved.
Show resolved Hide resolved
coordinator = StookwijzerCoordinator(hass, client)
await coordinator.async_config_entry_first_refresh()

entry.runtime_data = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
frenck marked this conversation as resolved.
Show resolved Hide resolved
"""Unload Stookwijzer config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
del hass.data[DOMAIN][entry.entry_id]
return unload_ok
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
58 changes: 58 additions & 0 deletions homeassistant/components/stookwijzer/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""Support for Stookwijzer Binary Sensors."""

from __future__ import annotations

from collections.abc import Callable
from dataclasses import dataclass

from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .coordinator import StookwijzerCoordinator
from .entity import StookwijzerEntity


@dataclass(kw_only=True, frozen=True)
class StookwijzerBinarySensorDescription(BinarySensorEntityDescription):
"""Class describing Stookwijzer binary sensor entities."""

value_fn: Callable[[StookwijzerCoordinator], bool | None]


STOOKWIJZER_BINARY_SENSORS = [
StookwijzerBinarySensorDescription(
key="stookalert",
device_class=BinarySensorDeviceClass.SAFETY,
value_fn=lambda StookwijzerCoordinator: StookwijzerCoordinator.client.alert,
)
]


async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Stookwijzer binary sensor from a config entry."""

async_add_entities(
StookwijzerBinarySensor(description, entry)
for description in STOOKWIJZER_BINARY_SENSORS
)


class StookwijzerBinarySensor(StookwijzerEntity, BinarySensorEntity):
"""Defines a Stookwijzer binary sensor."""

entity_description: StookwijzerBinarySensorDescription

@property
def is_on(self) -> bool | None:
"""Return the state of the device."""
return self.entity_description.value_fn(self._coordinator)
21 changes: 17 additions & 4 deletions homeassistant/components/stookwijzer/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

from typing import Any

from stookwijzer import Stookwijzer
import voluptuous as vol

from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_LATITUDE, CONF_LOCATION, CONF_LONGITUDE
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.selector import LocationSelector

from .const import DOMAIN
Expand All @@ -16,21 +18,32 @@
class StookwijzerFlowHandler(ConfigFlow, domain=DOMAIN):
"""Config flow for Stookwijzer."""

VERSION = 1
VERSION = 2

async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow initialized by the user."""
errors = {}

if user_input is not None:
return self.async_create_entry(
title="Stookwijzer",
data=user_input,
session = async_get_clientsession(self.hass)
x, y = await Stookwijzer.async_transform_coordinates(
session,
user_input[CONF_LOCATION][CONF_LATITUDE],
user_input[CONF_LOCATION][CONF_LONGITUDE],
)

if x and y:
fwestenberg marked this conversation as resolved.
Show resolved Hide resolved
return self.async_create_entry(
title="Stookwijzer",
data={CONF_LATITUDE: x, CONF_LONGITUDE: y},
)
errors["base"] = "unknown"

return self.async_show_form(
step_id="user",
errors=errors,
data_schema=vol.Schema(
{
vol.Required(
Expand Down
6 changes: 3 additions & 3 deletions homeassistant/components/stookwijzer/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@
class StookwijzerState(StrEnum):
"""Stookwijzer states for sensor entity."""

BLUE = "blauw"
ORANGE = "oranje"
RED = "rood"
CODE_YELLOW = "code_yellow"
CODE_ORANGE = "code_orange"
CODE_RED = "code_red"
36 changes: 36 additions & 0 deletions homeassistant/components/stookwijzer/coordinator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Class representing a Stookwijzer update coordinator."""

from datetime import timedelta
import logging

from stookwijzer import Stookwijzer

from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)

SCAN_INTERVAL = timedelta(minutes=60)


class StookwijzerCoordinator(DataUpdateCoordinator[None]):
"""Stookwijzer update coordinator."""

def __init__(self, hass: HomeAssistant, client: Stookwijzer) -> None:
"""Initialize the coordinator."""
super().__init__(
hass,
_LOGGER,
name=DOMAIN,
update_interval=SCAN_INTERVAL,
)
self.client = client

async def _async_update_data(self) -> None:
"""Fetch data from API endpoint."""
await self.client.async_update()
frenck marked this conversation as resolved.
Show resolved Hide resolved

if self.client.advice is None:
raise UpdateFailed
22 changes: 10 additions & 12 deletions homeassistant/components/stookwijzer/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,27 @@

from typing import Any

from stookwijzer import Stookwijzer

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant

from .const import DOMAIN


async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry
frenck marked this conversation as resolved.
Show resolved Hide resolved
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
client: Stookwijzer = hass.data[DOMAIN][entry.entry_id]
coordinator = entry.runtime_data

last_updated = None
if client.last_updated:
last_updated = client.last_updated.isoformat()
if coordinator.client.last_updated:
last_updated = coordinator.client.last_updated.isoformat()

return {
"state": client.state,
"state": coordinator.client.state,
"last_updated": last_updated,
"lqi": client.lqi,
"windspeed": client.windspeed,
"weather": client.weather,
"concentrations": client.concentrations,
"alert": coordinator.client.alert,
"air_quality_index": coordinator.client.lki,
"windspeed_bft": coordinator.client.windspeed_bft,
"windspeed_ms": coordinator.client.windspeed_ms,
"forecast_advice": coordinator.client.forecast_advice,
"forecast_alert": coordinator.client.forecast_alert,
}
38 changes: 38 additions & 0 deletions homeassistant/components/stookwijzer/entity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""The Stookwijzer integration entities."""

from __future__ import annotations

from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity import Entity, EntityDescription
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN


class StookwijzerEntity(CoordinatorEntity, Entity):
"""Base class for Stookwijzer entities."""

_attr_attribution = "Data provided by atlasleefomgeving.nl"
_attr_has_entity_name = True

def __init__(
self,
description: EntityDescription,
entry: ConfigEntry,
) -> None:
"""Initialize a Stookwijzer device."""

self.entity_description = description
self._coordinator = entry.runtime_data

super().__init__(self._coordinator)

self._attr_unique_id = f"{entry.entry_id}{DOMAIN}{description.key}"
self._attr_name = description.key.title()
fwestenberg marked this conversation as resolved.
Show resolved Hide resolved
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, entry.entry_id)},
manufacturer="Atlas Leefomgeving",
entry_type=DeviceEntryType.SERVICE,
configuration_url="https://www.atlasleefomgeving.nl/stookwijzer",
)
2 changes: 1 addition & 1 deletion homeassistant/components/stookwijzer/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"documentation": "https://www.home-assistant.io/integrations/stookwijzer",
"integration_type": "service",
"iot_class": "cloud_polling",
"requirements": ["stookwijzer==1.3.0"]
"requirements": ["stookwijzer==1.4.10"]
}
Loading
Loading