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

Add configuration flow for Buienradar integration #37796

Merged
merged 58 commits into from
May 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
69f942d
Add configuration flow for Buienradar integration
RobBie1221 Jul 12, 2020
28d1df7
Update buienradar camera tests to work with config flow
RobBie1221 Jul 13, 2020
e7404dd
Update buienradar weather tests to work with config flow
RobBie1221 Jul 13, 2020
6cb7b82
Update buienradar sensor tests to work with config flow
RobBie1221 Jul 13, 2020
e140cb9
Remove buienradar config_flow tests to pass tests
RobBie1221 Jul 13, 2020
5faefce
Add config flow tests for buienradar integration
RobBie1221 Jul 13, 2020
63d0483
Increase test coverage for buienradar config_flow tests
RobBie1221 Jul 13, 2020
2e11aab
Move data into domain
RobBie1221 Aug 5, 2020
e9306b8
Remove forecast option
RobBie1221 Aug 5, 2020
17cd62a
Move data to options
RobBie1221 Aug 5, 2020
57ac7ad
Remove options from config flow
RobBie1221 Aug 5, 2020
825f6a0
Adjust tests
RobBie1221 Aug 5, 2020
10d14b3
Adjust string
RobBie1221 Aug 5, 2020
2b9da3f
Fix pylint issues
RobBie1221 Aug 5, 2020
aedc670
Rework review comments
RobBie1221 Aug 21, 2020
8d56802
Handle import
RobBie1221 Oct 11, 2020
0bb0392
Change config flow to setup camera or weather
RobBie1221 Oct 11, 2020
c2374fc
Fix tests
RobBie1221 Oct 11, 2020
9b5e537
Remove translated file
RobBie1221 Oct 11, 2020
c07feac
Fix pylint
RobBie1221 Oct 11, 2020
7d6763e
Fix flake8
RobBie1221 Oct 12, 2020
43b57de
Fix unload
RobBie1221 Oct 12, 2020
b438ae8
Minor name changes
RobBie1221 Dec 26, 2020
8e3f815
Update homeassistant/components/buienradar/config_flow.py
RobBie1221 Feb 19, 2021
443c11a
Remove asynctest
RobBie1221 Feb 20, 2021
eaeb6e6
Add translation
RobBie1221 Mar 10, 2021
42540a8
Disable sensors by default
RobBie1221 Mar 10, 2021
7fcdaff
Remove integration name from translations
RobBie1221 Mar 10, 2021
dded4d8
Remove import method
RobBie1221 Mar 10, 2021
dad5129
Drop selection between platforms, disable camera by default
RobBie1221 Mar 13, 2021
4f09f12
Minor fix in configured_instances
RobBie1221 Mar 13, 2021
4089f69
Bugfix in weather
RobBie1221 Mar 13, 2021
56a8778
Rework import
RobBie1221 Mar 15, 2021
eb5e915
Change unique ids of camera
RobBie1221 Mar 15, 2021
2332701
Fix in import
RobBie1221 Mar 15, 2021
e4596a6
Fix camera tests
RobBie1221 Mar 15, 2021
91ddf2b
Fix sensor test
RobBie1221 Mar 15, 2021
9166031
Fix sensor test 2
RobBie1221 Mar 15, 2021
fe11489
Fix config flow tests
RobBie1221 Mar 15, 2021
4e5321e
Add option flow
RobBie1221 Mar 15, 2021
0e4ebc3
Add tests for option flow
RobBie1221 Mar 15, 2021
e433de2
Add import tests
RobBie1221 Mar 16, 2021
828ff24
Some cleanups
RobBie1221 Mar 16, 2021
8de1c1a
Apply suggestions from code review
RobBie1221 Apr 30, 2021
f5f3d59
Fix isort,black,mypy
RobBie1221 Apr 30, 2021
e65a143
Small tweaks and added typing to new parts
frenck Apr 30, 2021
895741e
Fix review comments (1)
RobBie1221 May 2, 2021
653b2c1
Apply suggestions from code review
RobBie1221 May 2, 2021
bcc6225
Fix review comments (2)
RobBie1221 May 2, 2021
8661ab9
Merge branch 'buienradar_cfg_flow' of https://github.com/RobBie1221/c…
RobBie1221 May 2, 2021
cf7242e
Fix issues
RobBie1221 May 2, 2021
7e7a1f0
Fix unique id
RobBie1221 May 2, 2021
b11919d
Improve tests
RobBie1221 May 2, 2021
bd0f86c
Extend tests
RobBie1221 May 2, 2021
aaf549e
Fix issue with unload
RobBie1221 May 3, 2021
5144929
Address review comments
RobBie1221 May 4, 2021
cddba93
Add warning when loading platform
RobBie1221 May 4, 2021
c68172c
Add load/unload test
RobBie1221 May 4, 2021
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
2 changes: 1 addition & 1 deletion CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ homeassistant/components/brother/* @bieniu
homeassistant/components/brunt/* @eavanvalkenburg
homeassistant/components/bsblan/* @liudger
homeassistant/components/bt_smarthub/* @jxwolstenholme
homeassistant/components/buienradar/* @mjj4791 @ties
homeassistant/components/buienradar/* @mjj4791 @ties @Robbie1221
RobBie1221 marked this conversation as resolved.
Show resolved Hide resolved
homeassistant/components/cast/* @emontnemery
homeassistant/components/cert_expiry/* @Cereal2nd @jjlawren
homeassistant/components/circuit/* @braam
Expand Down
142 changes: 141 additions & 1 deletion homeassistant/components/buienradar/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,141 @@
"""The buienradar component."""
"""The buienradar integration."""
from __future__ import annotations

import logging

from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry
from homeassistant.helpers.typing import ConfigType

from .const import (
CONF_COUNTRY,
CONF_DELTA,
CONF_DIMENSION,
CONF_TIMEFRAME,
DEFAULT_COUNTRY,
DEFAULT_DELTA,
DEFAULT_DIMENSION,
DEFAULT_TIMEFRAME,
DOMAIN,
)

PLATFORMS = ["camera", "sensor", "weather"]

_LOGGER = logging.getLogger(__name__)


async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the buienradar component."""
hass.data.setdefault(DOMAIN, {})

weather_configs = _filter_domain_configs(config, "weather", DOMAIN)
sensor_configs = _filter_domain_configs(config, "sensor", DOMAIN)
camera_configs = _filter_domain_configs(config, "camera", DOMAIN)

_import_configs(hass, weather_configs, sensor_configs, camera_configs)

return True
RobBie1221 marked this conversation as resolved.
Show resolved Hide resolved


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up buienradar from a config entry."""
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
entry.async_on_unload(entry.add_update_listener(async_update_options))
return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
RobBie1221 marked this conversation as resolved.
Show resolved Hide resolved
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

return unload_ok


async def async_update_options(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
"""Update options."""
await hass.config_entries.async_reload(config_entry.entry_id)


def _import_configs(
hass: HomeAssistant,
weather_configs: list[ConfigType],
sensor_configs: list[ConfigType],
camera_configs: list[ConfigType],
) -> None:
camera_config = {}
if camera_configs:
camera_config = camera_configs[0]

for config in sensor_configs:
# Remove weather configurations which share lat/lon with sensor configurations
matching_weather_config = None
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
for weather_config in weather_configs:
weather_latitude = config.get(CONF_LATITUDE, hass.config.latitude)
weather_longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
if latitude == weather_latitude and longitude == weather_longitude:
matching_weather_config = weather_config
break

if matching_weather_config is not None:
weather_configs.remove(matching_weather_config)

configs = weather_configs + sensor_configs

if not configs and camera_configs:
config = {
CONF_LATITUDE: hass.config.latitude,
CONF_LONGITUDE: hass.config.longitude,
}
configs.append(config)

if configs:
_try_update_unique_id(hass, configs[0], camera_config)

for config in configs:
data = {
CONF_LATITUDE: config.get(CONF_LATITUDE, hass.config.latitude),
CONF_LONGITUDE: config.get(CONF_LONGITUDE, hass.config.longitude),
CONF_TIMEFRAME: config.get(CONF_TIMEFRAME, DEFAULT_TIMEFRAME),
CONF_COUNTRY: camera_config.get(CONF_COUNTRY, DEFAULT_COUNTRY),
CONF_DELTA: camera_config.get(CONF_DELTA, DEFAULT_DELTA),
CONF_NAME: config.get(CONF_NAME, "Buienradar"),
}

hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data=data,
)
)


def _try_update_unique_id(
hass: HomeAssistant, config: ConfigType, camera_config: ConfigType
) -> None:
dimension = camera_config.get(CONF_DIMENSION, DEFAULT_DIMENSION)
country = camera_config.get(CONF_COUNTRY, DEFAULT_COUNTRY)

registry = entity_registry.async_get(hass)
entity_id = registry.async_get_entity_id("camera", DOMAIN, f"{dimension}_{country}")

if entity_id is not None:
latitude = config[CONF_LATITUDE]
longitude = config[CONF_LONGITUDE]

new_unique_id = f"{latitude:2.6f}{longitude:2.6f}"
registry.async_update_entity(entity_id, new_unique_id=new_unique_id)


def _filter_domain_configs(
config: ConfigType, domain: str, platform: str
) -> list[ConfigType]:
configs = []
for entry in config:
if entry.startswith(domain):
configs += [x for x in config[entry] if x["platform"] == platform]
return configs
55 changes: 42 additions & 13 deletions homeassistant/components/buienradar/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,22 @@
import voluptuous as vol

from homeassistant.components.camera import PLATFORM_SCHEMA, Camera
from homeassistant.const import CONF_NAME
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.util import dt as dt_util

CONF_DIMENSION = "dimension"
CONF_DELTA = "delta"
CONF_COUNTRY = "country_code"
from .const import (
CONF_COUNTRY,
CONF_DELTA,
CONF_DIMENSION,
DEFAULT_COUNTRY,
DEFAULT_DELTA,
DEFAULT_DIMENSION,
)

_LOGGER = logging.getLogger(__name__)

Expand All @@ -41,13 +49,27 @@


async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up buienradar camera platform."""
_LOGGER.warning(
"Platform configuration is deprecated, will be removed in a future release"
)


async def async_setup_entry(
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up buienradar radar-loop camera component."""
dimension = config[CONF_DIMENSION]
delta = config[CONF_DELTA]
name = config[CONF_NAME]
country = config[CONF_COUNTRY]
config = entry.data
options = entry.options

country = options.get(CONF_COUNTRY, config.get(CONF_COUNTRY, DEFAULT_COUNTRY))

async_add_entities([BuienradarCam(name, dimension, delta, country)])
delta = options.get(CONF_DELTA, config.get(CONF_DELTA, DEFAULT_DELTA))

latitude = config.get(CONF_LATITUDE, hass.config.latitude)
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)

async_add_entities([BuienradarCam(latitude, longitude, delta, country)])


class BuienradarCam(Camera):
Expand All @@ -59,18 +81,20 @@ class BuienradarCam(Camera):
[0]: https://www.buienradar.nl/overbuienradar/gratis-weerdata
"""

def __init__(self, name: str, dimension: int, delta: float, country: str):
def __init__(
self, latitude: float, longitude: float, delta: float, country: str
) -> None:
"""
Initialize the component.

This constructor must be run in the event loop.
"""
super().__init__()

self._name = name
self._name = "Buienradar"

# dimension (x and y) of returned radar image
self._dimension = dimension
self._dimension = DEFAULT_DIMENSION

# time a cached image stays valid for
self._delta = delta
Expand All @@ -94,7 +118,7 @@ def __init__(self, name: str, dimension: int, delta: float, country: str):
# deadline for image refresh - self.delta after last successful load
self._deadline: datetime | None = None

self._unique_id = f"{self._dimension}_{self._country}"
self._unique_id = f"{latitude:2.6f}{longitude:2.6f}"

@property
def name(self) -> str:
Expand Down Expand Up @@ -192,3 +216,8 @@ async def async_camera_image(self) -> bytes | None:
def unique_id(self):
"""Return the unique id."""
return self._unique_id

@property
def entity_registry_enabled_default(self) -> bool:
"""Disable entity by default."""
return False
129 changes: 129 additions & 0 deletions homeassistant/components/buienradar/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
"""Config flow for buienradar integration."""
from __future__ import annotations

import logging
from typing import Any

import voluptuous as vol

from homeassistant import config_entries
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
import homeassistant.helpers.config_validation as cv

from .const import (
CONF_COUNTRY,
CONF_DELTA,
CONF_TIMEFRAME,
DEFAULT_COUNTRY,
DEFAULT_DELTA,
DEFAULT_TIMEFRAME,
DOMAIN,
SUPPORTED_COUNTRY_CODES,
)

_LOGGER = logging.getLogger(__name__)


class BuienradarFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for buienradar."""

VERSION = 1

@staticmethod
@callback
def async_get_options_flow(
config_entry: ConfigEntry,
) -> BuienradarOptionFlowHandler:
"""Get the options flow for this handler."""
return BuienradarOptionFlowHandler(config_entry)

async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle a flow initialized by the user."""
if user_input is not None:
lat = user_input.get(CONF_LATITUDE)
lon = user_input.get(CONF_LONGITUDE)

await self.async_set_unique_id(f"{lat}-{lon}")
self._abort_if_unique_id_configured()

return self.async_create_entry(title=f"{lat},{lon}", data=user_input)

data_schema = vol.Schema(
{
vol.Required(
CONF_LATITUDE, default=self.hass.config.latitude
): cv.latitude,
vol.Required(
CONF_LONGITUDE, default=self.hass.config.longitude
): cv.longitude,
}
)

return self.async_show_form(
step_id="user",
data_schema=data_schema,
errors={},
)

async def async_step_import(self, import_input: dict[str, Any]) -> FlowResult:
"""Import a config entry."""
latitude = import_input[CONF_LATITUDE]
longitude = import_input[CONF_LONGITUDE]

await self.async_set_unique_id(f"{latitude}-{longitude}")
self._abort_if_unique_id_configured()

return self.async_create_entry(
title=f"{latitude},{longitude}", data=import_input
)


class BuienradarOptionFlowHandler(config_entries.OptionsFlow):
"""Handle options."""

def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry

async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Manage the options."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)

return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Optional(
CONF_COUNTRY,
default=self.config_entry.options.get(
CONF_COUNTRY,
self.config_entry.data.get(CONF_COUNTRY, DEFAULT_COUNTRY),
),
): vol.In(SUPPORTED_COUNTRY_CODES),
vol.Optional(
CONF_DELTA,
default=self.config_entry.options.get(
CONF_DELTA,
self.config_entry.data.get(CONF_DELTA, DEFAULT_DELTA),
),
): vol.All(vol.Coerce(int), vol.Range(min=0)),
vol.Optional(
CONF_TIMEFRAME,
default=self.config_entry.options.get(
CONF_TIMEFRAME,
self.config_entry.data.get(
CONF_TIMEFRAME, DEFAULT_TIMEFRAME
),
),
): vol.All(vol.Coerce(int), vol.Range(min=5, max=120)),
}
),
)
Loading