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 all 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
1 change: 1 addition & 0 deletions .strict-typing
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ homeassistant.components.starlink.*
homeassistant.components.statistics.*
homeassistant.components.steamist.*
homeassistant.components.stookalert.*
homeassistant.components.stookwijzer.*
homeassistant.components.stream.*
homeassistant.components.streamlabswater.*
homeassistant.components.stt.*
Expand Down
86 changes: 74 additions & 12 deletions homeassistant/components/stookwijzer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,91 @@

from __future__ import annotations

from typing import Any

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.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er, issue_registry as ir
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from .const import DOMAIN
from .const import DOMAIN, LOGGER
from .coordinator import StookwijzerConfigEntry, StookwijzerCoordinator

PLATFORMS = [Platform.SENSOR]


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_setup_entry(hass: HomeAssistant, entry: StookwijzerConfigEntry) -> bool:
"""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],
)
await er.async_migrate_entries(hass, entry.entry_id, async_migrate_entity_entry)

coordinator = StookwijzerCoordinator(hass, entry)
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:
async def async_unload_entry(
hass: HomeAssistant, entry: StookwijzerConfigEntry
) -> bool:
"""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)


async def async_migrate_entry(
hass: HomeAssistant, entry: StookwijzerConfigEntry
) -> bool:
"""Migrate old entry."""
LOGGER.debug("Migrating from version %s", entry.version)

if entry.version == 1:
frenck marked this conversation as resolved.
Show resolved Hide resolved
session = async_get_clientsession(hass)
latitude, longitude = await Stookwijzer.async_transform_coordinates(
session,
entry.data[CONF_LOCATION][CONF_LATITUDE],
entry.data[CONF_LOCATION][CONF_LONGITUDE],
)

if not latitude or not longitude:
ir.async_create_issue(
hass,
DOMAIN,
"location_migration_failed",
is_fixable=False,
severity=ir.IssueSeverity.ERROR,
translation_key="location_migration_failed",
translation_placeholders={
"entry_title": entry.title,
},
)
return False

hass.config_entries.async_update_entry(
entry,
version=2,
data={
CONF_LATITUDE: latitude,
CONF_LONGITUDE: longitude,
},
)

LOGGER.debug("Migration to version %s successful", entry.version)

return True


@callback
def async_migrate_entity_entry(entity_entry: er.RegistryEntry) -> dict[str, Any] | None:
"""Migrate Stookwijzer entity entries.

- Migrates unique ID for the old Stookwijzer sensors to the new unique ID.
"""
if entity_entry.unique_id == entity_entry.config_entry_id:
return {"new_unique_id": f"{entity_entry.config_entry_id}_advice"}

# No migration needed
return None
20 changes: 15 additions & 5 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,29 @@
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,
latitude, longitude = await Stookwijzer.async_transform_coordinates(
async_get_clientsession(self.hass),
user_input[CONF_LOCATION][CONF_LATITUDE],
user_input[CONF_LOCATION][CONF_LONGITUDE],
)
if latitude and longitude:
return self.async_create_entry(
title="Stookwijzer",
data={CONF_LATITUDE: latitude, CONF_LONGITUDE: longitude},
)
errors["base"] = "unknown"

return self.async_show_form(
step_id="user",
errors=errors,
data_schema=vol.Schema(
{
vol.Required(
Expand Down
9 changes: 0 additions & 9 deletions homeassistant/components/stookwijzer/const.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
"""Constants for the Stookwijzer integration."""

from enum import StrEnum
import logging
from typing import Final

DOMAIN: Final = "stookwijzer"
LOGGER = logging.getLogger(__package__)


class StookwijzerState(StrEnum):
"""Stookwijzer states for sensor entity."""

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

from datetime import timedelta

from stookwijzer import Stookwijzer

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import DOMAIN, LOGGER

SCAN_INTERVAL = timedelta(minutes=60)

type StookwijzerConfigEntry = ConfigEntry[StookwijzerCoordinator]


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

def __init__(self, hass: HomeAssistant, entry: StookwijzerConfigEntry) -> None:
"""Initialize the coordinator."""
super().__init__(
hass,
LOGGER,
name=DOMAIN,
update_interval=SCAN_INTERVAL,
)
self.client = Stookwijzer(
async_get_clientsession(hass),
entry.data[CONF_LATITUDE],
entry.data[CONF_LONGITUDE],
)

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(
translation_domain=DOMAIN,
translation_key="no_data_received",
)
23 changes: 6 additions & 17 deletions homeassistant/components/stookwijzer/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,18 @@

from typing import Any

from stookwijzer import Stookwijzer

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

from .const import DOMAIN
from .coordinator import StookwijzerConfigEntry


async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry
hass: HomeAssistant, entry: StookwijzerConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
client: Stookwijzer = hass.data[DOMAIN][entry.entry_id]

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

client = entry.runtime_data.client
return {
"state": client.state,
"last_updated": last_updated,
"lqi": client.lqi,
"windspeed": client.windspeed,
"weather": client.weather,
"concentrations": client.concentrations,
"advice": client.advice,
"air_quality_index": client.lki,
"windspeed_ms": client.windspeed_ms,
}
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.5.1"]
}
89 changes: 89 additions & 0 deletions homeassistant/components/stookwijzer/quality_scale.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
rules:
# Bronze
action-setup:
status: exempt
comment: |
The integration doesn't provide any additional service actions.
appropriate-polling: done
brands: done
common-modules: done
config-flow-test-coverage: done
config-flow: done
dependency-transparency: done
docs-actions:
status: exempt
comment: |
The integration doesn't provide any additional service actions.
docs-high-level-description: done
docs-installation-instructions: done
docs-removal-instructions: todo
entity-event-setup:
status: exempt
comment: |
The integration doesn't subscribe to any events.
entity-unique-id: done
has-entity-name: done
runtime-data: done
test-before-configure: todo
test-before-setup: done
unique-config-entry: todo

# Silver
action-exceptions:
status: exempt
comment: |
This integration is read-only and doesn't provide any actions.
config-entry-unloading: done
docs-configuration-parameters: todo
docs-installation-parameters: todo
entity-unavailable: done
integration-owner: done
log-when-unavailable: done
parallel-updates:
status: exempt
comment: |
This integration is read-only and doesn't provide any actions.
reauthentication-flow:
status: exempt
comment: |
This integration doesn't require re-authentication.
test-coverage: todo
# Gold
devices: done
diagnostics: done
discovery-update-info:
status: exempt
comment: |
The integration cannot be discovered, as it is an external service.
discovery:
status: exempt
comment: |
The integration cannot be discovered, as it is an external service.
docs-data-update: todo
docs-examples: todo
docs-known-limitations: todo
docs-supported-devices: todo
docs-supported-functions: todo
docs-troubleshooting: todo
docs-use-cases: todo
dynamic-devices:
status: exempt
comment: |
This integration provides a single device entry for the service.
entity-category: done
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
icon-translations: todo
reconfiguration-flow: todo
repair-issues: done
stale-devices:
status: exempt
comment: |
This integration provides a single device entry for the service.

# Platinum
async-dependency: done
inject-websession: done
strict-typing: done
Loading
Loading