Skip to content

Commit c61647e

Browse files
authored
Merge pull request #31 from golles/refactor_component
Refactor, bring up to standards
2 parents 6ee59b3 + a543f67 commit c61647e

13 files changed

+433
-456
lines changed

README.md

+20-18
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,8 @@
88
![Project Maintenance][maintenance-shield]
99
[![BuyMeCoffee][buymecoffeebadge]][buymecoffee]
1010

11-
[![Discord][discord-shield]][discord]
12-
[![Community Forum][forum-shield]][forum]
13-
14-
KNMI custom component for Home Assistant.
15-
Weather data provided by KNMI, https://weerlive.nl/.
11+
KNMI custom component for Home Assistant. <br>
12+
Weather data provided by KNMI, https://weerlive.nl.
1613

1714
## Installation
1815

@@ -49,20 +46,29 @@ custom_components/knmi/weather.py
4946

5047
## Configuration is done in the UI
5148

52-
You can configure and setup KNMI in your integrations page, look for KNMI in the add integrations dialog.
49+
You can configure and setup the KNMI integration in your integrations page, look for KNMI in the add integrations dialog.
5350

5451
## Known limitations
5552

56-
- This integration is translated into English and Dutch, the entity names and the data (from the API) is only available in Dutch.
57-
- The free API only provides a forecast for 2 days ahead. To make the weather card aesthetically not look too bad, the forecast for today is also shown, resulting in showing only 3 days (see example below).
53+
- This integration is translated into English and Dutch, the entity names and the data (from the API) are only available in Dutch.
54+
- The free API only provides a forecast for 2 days ahead. To make the weather card aesthetically not look too bad, the forecast for today is also shown, resulting in showing only 3 days (see example below).
5855

5956
## Examples
60-
Weather card:
61-
<img width="471" alt="Weather card" src="https://user-images.githubusercontent.com/2211503/146539992-49a75ec1-9b11-4702-b843-88d18c592675.png">
62-
Weather entity:
63-
<img width="388" alt="Weather entity" src="https://user-images.githubusercontent.com/2211503/146540094-a9ac6e44-a45a-49bb-91b5-b3d684c6a249.png">
64-
Integration with all entities:
65-
<img width="666" alt="Integration" src="https://user-images.githubusercontent.com/2211503/146540088-ae2609d0-b317-4dab-8c4d-4a72e6598500.png">
57+
Integration with all entities: <br>
58+
<img width="662" alt="Integration" src="https://user-images.githubusercontent.com/2211503/179353840-009a710e-94b9-41a7-9efd-b9dd98ae5b66.png">
59+
60+
Weather card: <br>
61+
<img width="472" alt="Weather card" src="https://user-images.githubusercontent.com/2211503/179353837-a535059b-b5b6-462a-8519-3bb15dd3fdab.png">
62+
63+
Weather entity: <br>
64+
<img width="396" alt="Weather entity" src="https://user-images.githubusercontent.com/2211503/179353844-32d9c826-5701-4264-91e4-894c797b4e0d.png">
65+
66+
Sun entity: <br>
67+
<img width="392" alt="Sun entity" src="https://user-images.githubusercontent.com/2211503/179353841-8376e62f-1bd8-4ee2-ae16-14e1dde41c9f.png">
68+
69+
Warning entity: <br>
70+
<img width="370" alt="Warning entity" src="https://user-images.githubusercontent.com/2211503/179353843-fee87e24-eabd-4d44-a58c-b933cfe4625c.png">
71+
6672

6773
## Contributions are welcome!
6874

@@ -77,10 +83,6 @@ If you want to contribute to this please read the [Contribution guidelines](CONT
7783
[commits]: https://github.com/golles/ha-knmi/commits/main
7884
[hacs]: https://github.com/custom-components/hacs
7985
[hacsbadge]: https://img.shields.io/badge/HACS-Custom-orange.svg?style=for-the-badge
80-
[discord]: https://discord.gg/Qa5fW2R
81-
[discord-shield]: https://img.shields.io/discord/330944238910963714.svg?style=for-the-badge
82-
[forum-shield]: https://img.shields.io/badge/community-forum-brightgreen.svg?style=for-the-badge
83-
[forum]: https://community.home-assistant.io/
8486
[license-shield]: https://img.shields.io/github/license/golles/ha-knmi.svg?style=for-the-badge
8587
[maintenance-shield]: https://img.shields.io/badge/maintainer-golles-blue.svg?style=for-the-badge
8688
[releases-shield]: https://img.shields.io/github/release/golles/ha-knmi.svg?style=for-the-badge

custom_components/knmi/__init__.py

+52-42
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,28 @@
44
For more details about this integration, please refer to
55
https://github.com/golles/ha-knmi/
66
"""
7-
import asyncio
8-
from datetime import timedelta
9-
import logging
7+
from typing import Any, Callable
108

119
from homeassistant.config_entries import ConfigEntry
1210
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_API_KEY
1311
from homeassistant.core import Config, HomeAssistant
1412
from homeassistant.exceptions import ConfigEntryNotReady
1513
from homeassistant.helpers.aiohttp_client import async_get_clientsession
14+
from homeassistant.helpers.device_registry import DeviceEntryType
15+
from homeassistant.helpers.entity import DeviceInfo
1616
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
1717

1818
from .api import KnmiApiClient
19-
from .const import DOMAIN, PLATFORMS
19+
from .const import _LOGGER, DOMAIN, NAME, VERSION, PLATFORMS, SCAN_INTERVAL
2020

21-
SCAN_INTERVAL = timedelta(seconds=300)
2221

23-
_LOGGER: logging.Logger = logging.getLogger(__package__)
24-
25-
26-
async def async_setup(_hass: HomeAssistant, _config: Config):
22+
async def async_setup(_hass: HomeAssistant, _config: Config) -> bool:
2723
"""Set up this integration using YAML is not supported."""
2824
return True
2925

3026

31-
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
32-
"""Set up this integration using UI."""
27+
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
28+
"""Set up KNMI as config entry."""
3329
if hass.data.get(DOMAIN) is None:
3430
hass.data.setdefault(DOMAIN, {})
3531

@@ -40,62 +36,76 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
4036
session = async_get_clientsession(hass)
4137
client = KnmiApiClient(api_key, latitude, longitude, session)
4238

43-
coordinator = KnmiDataUpdateCoordinator(hass, client=client)
44-
await coordinator.async_refresh()
39+
device_info = DeviceInfo(
40+
entry_type=DeviceEntryType.SERVICE,
41+
identifiers={(DOMAIN, entry.entry_id)},
42+
manufacturer=NAME,
43+
name=NAME,
44+
model=VERSION,
45+
configuration_url="http://weerlive.nl/api/toegang/account.php",
46+
)
47+
48+
coordinator = KnmiDataUpdateCoordinator(
49+
hass=hass, client=client, device_info=device_info
50+
)
51+
await coordinator.async_config_entry_first_refresh()
4552

4653
if not coordinator.last_update_success:
4754
raise ConfigEntryNotReady
4855

49-
hass.data[DOMAIN][entry.entry_id] = coordinator
56+
entry.async_on_unload(entry.add_update_listener(async_reload_entry))
57+
58+
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
5059

5160
for platform in PLATFORMS:
5261
if entry.options.get(platform, True):
5362
coordinator.platforms.append(platform)
5463
hass.async_add_job(
5564
hass.config_entries.async_forward_entry_setup(entry, platform)
5665
)
57-
58-
entry.async_on_unload(entry.add_update_listener(async_reload_entry))
5966
return True
6067

6168

69+
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
70+
"""Unload KNMI config entry."""
71+
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
72+
if unload_ok:
73+
del hass.data[DOMAIN][entry.entry_id]
74+
return unload_ok
75+
76+
77+
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
78+
"""Reload config entry."""
79+
await async_unload_entry(hass, entry)
80+
await async_setup_entry(hass, entry)
81+
82+
6283
class KnmiDataUpdateCoordinator(DataUpdateCoordinator):
6384
"""Class to manage fetching data from the API."""
6485

65-
def __init__(self, hass: HomeAssistant, client: KnmiApiClient) -> None:
86+
def __init__(
87+
self, hass: HomeAssistant, client: KnmiApiClient, device_info: DeviceInfo
88+
) -> None:
6689
"""Initialize."""
6790
self.api = client
91+
self.device_info = device_info
6892
self.platforms = []
6993

70-
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL)
94+
super().__init__(
95+
hass=hass, logger=_LOGGER, name=DOMAIN, update_interval=SCAN_INTERVAL
96+
)
7197

72-
async def _async_update_data(self):
98+
async def _async_update_data(self) -> dict[str, Any]:
7399
"""Update data via library."""
74100
try:
75101
return await self.api.async_get_data()
76102
except Exception as exception:
77103
raise UpdateFailed() from exception
78104

79-
80-
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
81-
"""Handle removal of an entry."""
82-
coordinator = hass.data[DOMAIN][entry.entry_id]
83-
unloaded = all(
84-
await asyncio.gather(
85-
*[
86-
hass.config_entries.async_forward_entry_unload(entry, platform)
87-
for platform in PLATFORMS
88-
if platform in coordinator.platforms
89-
]
90-
)
91-
)
92-
if unloaded:
93-
hass.data[DOMAIN].pop(entry.entry_id)
94-
95-
return unloaded
96-
97-
98-
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
99-
"""Reload config entry."""
100-
await async_unload_entry(hass, entry)
101-
await async_setup_entry(hass, entry)
105+
def get_value(
106+
self, key: str, convert_to: Callable = str
107+
) -> float | int | str | None:
108+
"""Get a value from the retrieved data and convert to given type"""
109+
if self.data and self.data[key]:
110+
return convert_to(self.data[key])
111+
return None

custom_components/knmi/api.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
"""KnmiApiClient"""
2-
import logging
32
import asyncio
43
import socket
54
import aiohttp
65
import async_timeout
76

8-
from .const import API_ENDPOINT, API_TIMEOUT
9-
10-
11-
_LOGGER: logging.Logger = logging.getLogger(__package__)
7+
from .const import _LOGGER, API_ENDPOINT, API_TIMEOUT
128

139

1410
class KnmiApiClient:

0 commit comments

Comments
 (0)