Add configuration flow for Buienradar integration (#37796)

* Add configuration flow for Buienradar integration

* Update buienradar camera tests to work with config flow

* Update buienradar weather tests to work with config flow

* Update buienradar sensor tests to work with config flow

* Remove buienradar config_flow tests to pass tests

* Add config flow tests for buienradar integration

* Increase test coverage for buienradar config_flow tests

* Move data into domain

* Remove forecast option

* Move data to options

* Remove options from config flow

* Adjust tests

* Adjust string

* Fix pylint issues

* Rework review comments

* Handle import

* Change config flow to setup camera or weather

* Fix tests

* Remove translated file

* Fix pylint

* Fix flake8

* Fix unload

* Minor name changes

* Update homeassistant/components/buienradar/config_flow.py

Co-authored-by: Ties de Kock <ties@tiesdekock.nl>

* Remove asynctest

* Add translation

* Disable sensors by default

* Remove integration name from translations

* Remove import method

* Drop  selection between platforms, disable camera by default

* Minor fix in configured_instances

* Bugfix in weather

* Rework import

* Change unique ids of camera

* Fix in import

* Fix camera tests

* Fix sensor test

* Fix sensor test 2

* Fix config flow tests

* Add option flow

* Add tests for option flow

* Add import tests

* Some cleanups

* Apply suggestions from code review

Apply code suggestions

Co-authored-by: Franck Nijhof <git@frenck.dev>

* Fix isort,black,mypy

* Small tweaks and added typing to new parts

* Fix review comments (1)

* Apply suggestions from code review

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* Fix review comments (2)

* Fix issues

* Fix unique id

* Improve tests

* Extend tests

* Fix issue with unload

* Address review comments

* Add warning when loading platform

* Add load/unload test

Co-authored-by: Ties de Kock <ties@tiesdekock.nl>
Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Rob Bierbooms 2021-05-04 13:49:16 +02:00 committed by GitHub
parent 6931478688
commit c063f14c24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 840 additions and 176 deletions

View File

@ -76,7 +76,7 @@ homeassistant/components/brother/* @bieniu
homeassistant/components/brunt/* @eavanvalkenburg homeassistant/components/brunt/* @eavanvalkenburg
homeassistant/components/bsblan/* @liudger homeassistant/components/bsblan/* @liudger
homeassistant/components/bt_smarthub/* @jxwolstenholme homeassistant/components/bt_smarthub/* @jxwolstenholme
homeassistant/components/buienradar/* @mjj4791 @ties homeassistant/components/buienradar/* @mjj4791 @ties @Robbie1221
homeassistant/components/cast/* @emontnemery homeassistant/components/cast/* @emontnemery
homeassistant/components/cert_expiry/* @Cereal2nd @jjlawren homeassistant/components/cert_expiry/* @Cereal2nd @jjlawren
homeassistant/components/circuit/* @braam homeassistant/components/circuit/* @braam

View File

@ -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
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:
"""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

View File

@ -9,14 +9,22 @@ import aiohttp
import voluptuous as vol import voluptuous as vol
from homeassistant.components.camera import PLATFORM_SCHEMA, Camera 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 import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession 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 from homeassistant.util import dt as dt_util
CONF_DIMENSION = "dimension" from .const import (
CONF_DELTA = "delta" CONF_COUNTRY,
CONF_COUNTRY = "country_code" CONF_DELTA,
CONF_DIMENSION,
DEFAULT_COUNTRY,
DEFAULT_DELTA,
DEFAULT_DIMENSION,
)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -41,13 +49,27 @@ PLATFORM_SCHEMA = vol.All(
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up buienradar radar-loop camera component.""" """Set up buienradar camera platform."""
dimension = config[CONF_DIMENSION] _LOGGER.warning(
delta = config[CONF_DELTA] "Platform configuration is deprecated, will be removed in a future release"
name = config[CONF_NAME] )
country = config[CONF_COUNTRY]
async_add_entities([BuienradarCam(name, dimension, delta, country)])
async def async_setup_entry(
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up buienradar radar-loop camera component."""
config = entry.data
options = entry.options
country = options.get(CONF_COUNTRY, config.get(CONF_COUNTRY, DEFAULT_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): class BuienradarCam(Camera):
@ -59,7 +81,9 @@ class BuienradarCam(Camera):
[0]: https://www.buienradar.nl/overbuienradar/gratis-weerdata [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. Initialize the component.
@ -67,10 +91,10 @@ class BuienradarCam(Camera):
""" """
super().__init__() super().__init__()
self._name = name self._name = "Buienradar"
# dimension (x and y) of returned radar image # dimension (x and y) of returned radar image
self._dimension = dimension self._dimension = DEFAULT_DIMENSION
# time a cached image stays valid for # time a cached image stays valid for
self._delta = delta self._delta = delta
@ -94,7 +118,7 @@ class BuienradarCam(Camera):
# deadline for image refresh - self.delta after last successful load # deadline for image refresh - self.delta after last successful load
self._deadline: datetime | None = None self._deadline: datetime | None = None
self._unique_id = f"{self._dimension}_{self._country}" self._unique_id = f"{latitude:2.6f}{longitude:2.6f}"
@property @property
def name(self) -> str: def name(self) -> str:
@ -192,3 +216,8 @@ class BuienradarCam(Camera):
def unique_id(self): def unique_id(self):
"""Return the unique id.""" """Return the unique id."""
return self._unique_id return self._unique_id
@property
def entity_registry_enabled_default(self) -> bool:
"""Disable entity by default."""
return False

View File

@ -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)),
}
),
)

View File

@ -1,6 +1,24 @@
"""Constants for buienradar component.""" """Constants for buienradar component."""
DOMAIN = "buienradar"
DEFAULT_TIMEFRAME = 60 DEFAULT_TIMEFRAME = 60
DEFAULT_DIMENSION = 700
DEFAULT_DELTA = 600
CONF_DIMENSION = "dimension"
CONF_DELTA = "delta"
CONF_COUNTRY = "country_code"
CONF_TIMEFRAME = "timeframe"
"""Range according to the docs"""
CAMERA_DIM_MIN = 120
CAMERA_DIM_MAX = 700
SUPPORTED_COUNTRY_CODES = ["NL", "BE"]
DEFAULT_COUNTRY = "NL"
"""Schedule next call after (minutes).""" """Schedule next call after (minutes)."""
SCHEDULE_OK = 10 SCHEDULE_OK = 10
"""When an error occurred, new call after (minutes).""" """When an error occurred, new call after (minutes)."""

View File

@ -1,8 +1,9 @@
{ {
"domain": "buienradar", "domain": "buienradar",
"name": "Buienradar", "name": "Buienradar",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/buienradar", "documentation": "https://www.home-assistant.io/integrations/buienradar",
"requirements": ["buienradar==1.0.4"], "requirements": ["buienradar==1.0.4"],
"codeowners": ["@mjj4791", "@ties"], "codeowners": ["@mjj4791", "@ties", "@Robbie1221"],
"iot_class": "cloud_polling" "iot_class": "cloud_polling"
} }

View File

@ -21,6 +21,7 @@ from buienradar.constants import (
import voluptuous as vol import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
ATTR_ATTRIBUTION, ATTR_ATTRIBUTION,
CONF_LATITUDE, CONF_LATITUDE,
@ -37,11 +38,12 @@ from homeassistant.const import (
SPEED_KILOMETERS_PER_HOUR, SPEED_KILOMETERS_PER_HOUR,
TEMP_CELSIUS, TEMP_CELSIUS,
) )
from homeassistant.core import callback from homeassistant.core import HomeAssistant, callback
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from .const import DEFAULT_TIMEFRAME from .const import CONF_TIMEFRAME, DEFAULT_TIMEFRAME
from .util import BrData from .util import BrData
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -186,8 +188,6 @@ SENSOR_TYPES = {
"symbol_5d": ["Symbol 5d", None, None], "symbol_5d": ["Symbol 5d", None, None],
} }
CONF_TIMEFRAME = "timeframe"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{ {
vol.Optional( vol.Optional(
@ -208,14 +208,29 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up buienradar sensor platform."""
_LOGGER.warning(
"Platform configuration is deprecated, will be removed in a future release"
)
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Create the buienradar sensor.""" """Create the buienradar sensor."""
config = entry.data
options = entry.options
latitude = config.get(CONF_LATITUDE, hass.config.latitude) latitude = config.get(CONF_LATITUDE, hass.config.latitude)
longitude = config.get(CONF_LONGITUDE, hass.config.longitude) longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
timeframe = config[CONF_TIMEFRAME]
timeframe = options.get(
CONF_TIMEFRAME, config.get(CONF_TIMEFRAME, DEFAULT_TIMEFRAME)
)
if None in (latitude, longitude): if None in (latitude, longitude):
_LOGGER.error("Latitude or longitude not set in Home Assistant config") _LOGGER.error("Latitude or longitude not set in Home Assistant config")
return False return
coordinates = {CONF_LATITUDE: float(latitude), CONF_LONGITUDE: float(longitude)} coordinates = {CONF_LATITUDE: float(latitude), CONF_LONGITUDE: float(longitude)}
@ -225,12 +240,14 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
timeframe, timeframe,
) )
dev = [] entities = [
for sensor_type in config[CONF_MONITORED_CONDITIONS]: BrSensor(sensor_type, config.get(CONF_NAME, "Buienradar"), coordinates)
dev.append(BrSensor(sensor_type, config.get(CONF_NAME), coordinates)) for sensor_type in SENSOR_TYPES
async_add_entities(dev) ]
data = BrData(hass, coordinates, timeframe, dev) async_add_entities(entities)
data = BrData(hass, coordinates, timeframe, entities)
# schedule the first update in 1 minute from now: # schedule the first update in 1 minute from now:
await data.schedule_update(1) await data.schedule_update(1)
@ -380,7 +397,7 @@ class BrSensor(SensorEntity):
self._state = nested.get(self.type[len(PRECIPITATION_FORECAST) + 1 :]) self._state = nested.get(self.type[len(PRECIPITATION_FORECAST) + 1 :])
return True return True
if self.type == WINDSPEED or self.type == WINDGUST: if self.type in [WINDSPEED, WINDGUST]:
# hass wants windspeeds in km/h not m/s, so convert: # hass wants windspeeds in km/h not m/s, so convert:
self._state = data.get(self.type) self._state = data.get(self.type)
if self._state is not None: if self._state is not None:
@ -463,3 +480,8 @@ class BrSensor(SensorEntity):
def force_update(self): def force_update(self):
"""Return true for continuous sensors, false for discrete sensors.""" """Return true for continuous sensors, false for discrete sensors."""
return self._force_update return self._force_update
@property
def entity_registry_enabled_default(self) -> bool:
"""Return if the entity should be enabled when first added to the entity registry."""
return False

View File

@ -0,0 +1,29 @@
{
"config": {
"step": {
"user": {
"data": {
"latitude": "[%key:common::config_flow::data::latitude%]",
"longitude": "[%key:common::config_flow::data::longitude%]"
}
}
},
"error": {
"already_configured": "[%key:common::config_flow::abort::already_configured_location%]"
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_location%]"
}
},
"options": {
"step": {
"init": {
"data": {
"country_code": "Country code of the country to display camera images.",
"delta": "Time interval in seconds between camera image updates",
"timeframe": "Minutes to look ahead for precipitation forecast"
}
}
}
}
}

View File

@ -0,0 +1,29 @@
{
"config": {
"abort": {
"already_configured": "Location is already configured"
},
"error": {
"already_configured": "Location is already configured"
},
"step": {
"user": {
"data": {
"latitude": "Latitude",
"longitude": "Longitude"
}
}
}
},
"options": {
"step": {
"init": {
"data": {
"country_code": "Country code of the country to display camera images.",
"delta": "Time interval in seconds between camera image updates",
"timeframe": "Minutes to look ahead for precipitation forecast"
}
}
}
}
}

View File

@ -38,36 +38,42 @@ from homeassistant.components.weather import (
PLATFORM_SCHEMA, PLATFORM_SCHEMA,
WeatherEntity, WeatherEntity,
) )
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, TEMP_CELSIUS from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, TEMP_CELSIUS
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
# Reuse data and API logic from the sensor implementation # Reuse data and API logic from the sensor implementation
from .const import DEFAULT_TIMEFRAME from .const import DEFAULT_TIMEFRAME, DOMAIN
from .util import BrData from .util import BrData
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DATA_CONDITION = "buienradar_condition"
CONF_FORECAST = "forecast" CONF_FORECAST = "forecast"
DATA_CONDITION = "buienradar_condition"
CONDITION_CLASSES = { CONDITION_CLASSES = {
ATTR_CONDITION_CLOUDY: ["c", "p"], ATTR_CONDITION_CLOUDY: ("c", "p"),
ATTR_CONDITION_FOG: ["d", "n"], ATTR_CONDITION_FOG: ("d", "n"),
ATTR_CONDITION_HAIL: [], ATTR_CONDITION_HAIL: (),
ATTR_CONDITION_LIGHTNING: ["g"], ATTR_CONDITION_LIGHTNING: ("g",),
ATTR_CONDITION_LIGHTNING_RAINY: ["s"], ATTR_CONDITION_LIGHTNING_RAINY: ("s",),
ATTR_CONDITION_PARTLYCLOUDY: ["b", "j", "o", "r"], ATTR_CONDITION_PARTLYCLOUDY: (
ATTR_CONDITION_POURING: ["l", "q"], "b",
ATTR_CONDITION_RAINY: ["f", "h", "k", "m"], "j",
ATTR_CONDITION_SNOWY: ["u", "i", "v", "t"], "o",
ATTR_CONDITION_SNOWY_RAINY: ["w"], "r",
ATTR_CONDITION_SUNNY: ["a"], ),
ATTR_CONDITION_WINDY: [], ATTR_CONDITION_POURING: ("l", "q"),
ATTR_CONDITION_WINDY_VARIANT: [], ATTR_CONDITION_RAINY: ("f", "h", "k", "m"),
ATTR_CONDITION_EXCEPTIONAL: [], ATTR_CONDITION_SNOWY: ("u", "i", "v", "t"),
ATTR_CONDITION_SNOWY_RAINY: ("w",),
ATTR_CONDITION_SUNNY: ("a",),
ATTR_CONDITION_WINDY: (),
ATTR_CONDITION_WINDY_VARIANT: (),
ATTR_CONDITION_EXCEPTIONAL: (),
} }
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
@ -81,13 +87,24 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up buienradar weather platform."""
_LOGGER.warning(
"Platform configuration is deprecated, will be removed in a future release"
)
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up the buienradar platform.""" """Set up the buienradar platform."""
config = entry.data
latitude = config.get(CONF_LATITUDE, hass.config.latitude) latitude = config.get(CONF_LATITUDE, hass.config.latitude)
longitude = config.get(CONF_LONGITUDE, hass.config.longitude) longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
if None in (latitude, longitude): if None in (latitude, longitude):
_LOGGER.error("Latitude or longitude not set in Home Assistant config") _LOGGER.error("Latitude or longitude not set in Home Assistant config")
return False return
coordinates = {CONF_LATITUDE: float(latitude), CONF_LONGITUDE: float(longitude)} coordinates = {CONF_LATITUDE: float(latitude), CONF_LONGITUDE: float(longitude)}
@ -97,12 +114,12 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
_LOGGER.debug("Initializing buienradar weather: coordinates %s", coordinates) _LOGGER.debug("Initializing buienradar weather: coordinates %s", coordinates)
# create condition helper # create condition helper
if DATA_CONDITION not in hass.data: if DATA_CONDITION not in hass.data[DOMAIN]:
cond_keys = [str(chr(x)) for x in range(97, 123)] cond_keys = [str(chr(x)) for x in range(97, 123)]
hass.data[DATA_CONDITION] = dict.fromkeys(cond_keys) hass.data[DOMAIN][DATA_CONDITION] = dict.fromkeys(cond_keys)
for cond, condlst in CONDITION_CLASSES.items(): for cond, condlst in CONDITION_CLASSES.items():
for condi in condlst: for condi in condlst:
hass.data[DATA_CONDITION][condi] = cond hass.data[DOMAIN][DATA_CONDITION][condi] = cond
async_add_entities([BrWeather(data, config, coordinates)]) async_add_entities([BrWeather(data, config, coordinates)])
@ -115,8 +132,7 @@ class BrWeather(WeatherEntity):
def __init__(self, data, config, coordinates): def __init__(self, data, config, coordinates):
"""Initialise the platform with a data instance and station name.""" """Initialise the platform with a data instance and station name."""
self._stationname = config.get(CONF_NAME) self._stationname = config.get(CONF_NAME, "Buienradar")
self._forecast = config[CONF_FORECAST]
self._data = data self._data = data
self._unique_id = "{:2.6f}{:2.6f}".format( self._unique_id = "{:2.6f}{:2.6f}".format(
@ -141,7 +157,7 @@ class BrWeather(WeatherEntity):
if self._data and self._data.condition: if self._data and self._data.condition:
ccode = self._data.condition.get(CONDCODE) ccode = self._data.condition.get(CONDCODE)
if ccode: if ccode:
conditions = self.hass.data.get(DATA_CONDITION) conditions = self.hass.data[DOMAIN].get(DATA_CONDITION)
if conditions: if conditions:
return conditions.get(ccode) return conditions.get(ccode)
@ -187,11 +203,8 @@ class BrWeather(WeatherEntity):
@property @property
def forecast(self): def forecast(self):
"""Return the forecast array.""" """Return the forecast array."""
if not self._forecast:
return None
fcdata_out = [] fcdata_out = []
cond = self.hass.data[DATA_CONDITION] cond = self.hass.data[DOMAIN][DATA_CONDITION]
if not self._data.forecast: if not self._data.forecast:
return None return None

View File

@ -37,6 +37,7 @@ FLOWS = [
"broadlink", "broadlink",
"brother", "brother",
"bsblan", "bsblan",
"buienradar",
"canary", "canary",
"cast", "cast",
"cert_expiry", "cert_expiry",

View File

@ -1,34 +1,63 @@
"""The tests for generic camera component.""" """The tests for generic camera component."""
import asyncio import asyncio
from contextlib import suppress from contextlib import suppress
import copy
from aiohttp.client_exceptions import ClientResponseError from aiohttp.client_exceptions import ClientResponseError
from homeassistant.const import HTTP_INTERNAL_SERVER_ERROR from homeassistant.components.buienradar.const import CONF_COUNTRY, CONF_DELTA, DOMAIN
from homeassistant.setup import async_setup_component from homeassistant.const import (
CONF_LATITUDE,
CONF_LONGITUDE,
HTTP_INTERNAL_SERVER_ERROR,
)
from homeassistant.helpers.entity_registry import async_get
from homeassistant.util import dt as dt_util from homeassistant.util import dt as dt_util
from tests.common import MockConfigEntry
# An infinitesimally small time-delta. # An infinitesimally small time-delta.
EPSILON_DELTA = 0.0000000001 EPSILON_DELTA = 0.0000000001
TEST_LATITUDE = 51.5288504
TEST_LONGITUDE = 5.4002156
def radar_map_url(dim: int = 512, country_code: str = "NL") -> str: TEST_CFG_DATA = {CONF_LATITUDE: TEST_LATITUDE, CONF_LONGITUDE: TEST_LONGITUDE}
"""Build map url, defaulting to 512 wide (as in component)."""
return f"https://api.buienradar.nl/image/1.0/RadarMap{country_code}?w={dim}&h={dim}"
def radar_map_url(country_code: str = "NL") -> str:
"""Build map URL."""
return f"https://api.buienradar.nl/image/1.0/RadarMap{country_code}?w=700&h=700"
async def _setup_config_entry(hass, entry):
entity_registry = async_get(hass)
entity_registry.async_get_or_create(
domain="camera",
platform="buienradar",
unique_id=f"{TEST_LATITUDE:2.6f}{TEST_LONGITUDE:2.6f}",
config_entry=entry,
original_name="Buienradar",
)
await hass.async_block_till_done()
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
async def test_fetching_url_and_caching(aioclient_mock, hass, hass_client): async def test_fetching_url_and_caching(aioclient_mock, hass, hass_client):
"""Test that it fetches the given url.""" """Test that it fetches the given url."""
aioclient_mock.get(radar_map_url(), text="hello world") aioclient_mock.get(radar_map_url(), text="hello world")
await async_setup_component( mock_entry = MockConfigEntry(domain=DOMAIN, unique_id="TEST_ID", data=TEST_CFG_DATA)
hass, "camera", {"camera": {"name": "config_test", "platform": "buienradar"}}
) mock_entry.add_to_hass(hass)
await hass.async_block_till_done()
await _setup_config_entry(hass, mock_entry)
client = await hass_client() client = await hass_client()
resp = await client.get("/api/camera_proxy/camera.config_test") resp = await client.get("/api/camera_proxy/camera.buienradar_51_5288505_400216")
assert resp.status == 200 assert resp.status == 200
assert aioclient_mock.call_count == 1 assert aioclient_mock.call_count == 1
@ -38,7 +67,7 @@ async def test_fetching_url_and_caching(aioclient_mock, hass, hass_client):
# default delta is 600s -> should be the same when calling immediately # default delta is 600s -> should be the same when calling immediately
# afterwards. # afterwards.
resp = await client.get("/api/camera_proxy/camera.config_test") resp = await client.get("/api/camera_proxy/camera.buienradar_51_5288505_400216")
assert aioclient_mock.call_count == 1 assert aioclient_mock.call_count == 1
@ -46,22 +75,19 @@ async def test_expire_delta(aioclient_mock, hass, hass_client):
"""Test that the cache expires after delta.""" """Test that the cache expires after delta."""
aioclient_mock.get(radar_map_url(), text="hello world") aioclient_mock.get(radar_map_url(), text="hello world")
await async_setup_component( options = {CONF_DELTA: EPSILON_DELTA}
hass,
"camera", mock_entry = MockConfigEntry(
{ domain=DOMAIN, unique_id="TEST_ID", data=TEST_CFG_DATA, options=options
"camera": {
"name": "config_test",
"platform": "buienradar",
"delta": EPSILON_DELTA,
}
},
) )
await hass.async_block_till_done()
mock_entry.add_to_hass(hass)
await _setup_config_entry(hass, mock_entry)
client = await hass_client() client = await hass_client()
resp = await client.get("/api/camera_proxy/camera.config_test") resp = await client.get("/api/camera_proxy/camera.buienradar_51_5288505_400216")
assert resp.status == 200 assert resp.status == 200
assert aioclient_mock.call_count == 1 assert aioclient_mock.call_count == 1
@ -70,7 +96,7 @@ async def test_expire_delta(aioclient_mock, hass, hass_client):
await asyncio.sleep(EPSILON_DELTA) await asyncio.sleep(EPSILON_DELTA)
# tiny delta has passed -> should immediately call again # tiny delta has passed -> should immediately call again
resp = await client.get("/api/camera_proxy/camera.config_test") resp = await client.get("/api/camera_proxy/camera.buienradar_51_5288505_400216")
assert aioclient_mock.call_count == 2 assert aioclient_mock.call_count == 2
@ -78,15 +104,16 @@ async def test_only_one_fetch_at_a_time(aioclient_mock, hass, hass_client):
"""Test that it fetches with only one request at the same time.""" """Test that it fetches with only one request at the same time."""
aioclient_mock.get(radar_map_url(), text="hello world") aioclient_mock.get(radar_map_url(), text="hello world")
await async_setup_component( mock_entry = MockConfigEntry(domain=DOMAIN, unique_id="TEST_ID", data=TEST_CFG_DATA)
hass, "camera", {"camera": {"name": "config_test", "platform": "buienradar"}}
) mock_entry.add_to_hass(hass)
await hass.async_block_till_done()
await _setup_config_entry(hass, mock_entry)
client = await hass_client() client = await hass_client()
resp_1 = client.get("/api/camera_proxy/camera.config_test") resp_1 = client.get("/api/camera_proxy/camera.buienradar_51_5288505_400216")
resp_2 = client.get("/api/camera_proxy/camera.config_test") resp_2 = client.get("/api/camera_proxy/camera.buienradar_51_5288505_400216")
resp = await resp_1 resp = await resp_1
resp_2 = await resp_2 resp_2 = await resp_2
@ -96,44 +123,22 @@ async def test_only_one_fetch_at_a_time(aioclient_mock, hass, hass_client):
assert aioclient_mock.call_count == 1 assert aioclient_mock.call_count == 1
async def test_dimension(aioclient_mock, hass, hass_client):
"""Test that it actually adheres to the dimension."""
aioclient_mock.get(radar_map_url(700), text="hello world")
await async_setup_component(
hass,
"camera",
{"camera": {"name": "config_test", "platform": "buienradar", "dimension": 700}},
)
await hass.async_block_till_done()
client = await hass_client()
await client.get("/api/camera_proxy/camera.config_test")
assert aioclient_mock.call_count == 1
async def test_belgium_country(aioclient_mock, hass, hass_client): async def test_belgium_country(aioclient_mock, hass, hass_client):
"""Test that it actually adheres to another country like Belgium.""" """Test that it actually adheres to another country like Belgium."""
aioclient_mock.get(radar_map_url(country_code="BE"), text="hello world") aioclient_mock.get(radar_map_url(country_code="BE"), text="hello world")
await async_setup_component( data = copy.deepcopy(TEST_CFG_DATA)
hass, data[CONF_COUNTRY] = "BE"
"camera",
{ mock_entry = MockConfigEntry(domain=DOMAIN, unique_id="TEST_ID", data=data)
"camera": {
"name": "config_test", mock_entry.add_to_hass(hass)
"platform": "buienradar",
"country_code": "BE", await _setup_config_entry(hass, mock_entry)
}
},
)
await hass.async_block_till_done()
client = await hass_client() client = await hass_client()
await client.get("/api/camera_proxy/camera.config_test") await client.get("/api/camera_proxy/camera.buienradar_51_5288505_400216")
assert aioclient_mock.call_count == 1 assert aioclient_mock.call_count == 1
@ -142,15 +147,16 @@ async def test_failure_response_not_cached(aioclient_mock, hass, hass_client):
"""Test that it does not cache a failure response.""" """Test that it does not cache a failure response."""
aioclient_mock.get(radar_map_url(), text="hello world", status=401) aioclient_mock.get(radar_map_url(), text="hello world", status=401)
await async_setup_component( mock_entry = MockConfigEntry(domain=DOMAIN, unique_id="TEST_ID", data=TEST_CFG_DATA)
hass, "camera", {"camera": {"name": "config_test", "platform": "buienradar"}}
) mock_entry.add_to_hass(hass)
await hass.async_block_till_done()
await _setup_config_entry(hass, mock_entry)
client = await hass_client() client = await hass_client()
await client.get("/api/camera_proxy/camera.config_test") await client.get("/api/camera_proxy/camera.buienradar_51_5288505_400216")
await client.get("/api/camera_proxy/camera.config_test") await client.get("/api/camera_proxy/camera.buienradar_51_5288505_400216")
assert aioclient_mock.call_count == 2 assert aioclient_mock.call_count == 2
@ -168,22 +174,19 @@ async def test_last_modified_updates(aioclient_mock, hass, hass_client):
headers={"Last-Modified": last_modified}, headers={"Last-Modified": last_modified},
) )
await async_setup_component( options = {CONF_DELTA: EPSILON_DELTA}
hass,
"camera", mock_entry = MockConfigEntry(
{ domain=DOMAIN, unique_id="TEST_ID", data=TEST_CFG_DATA, options=options
"camera": {
"name": "config_test",
"platform": "buienradar",
"delta": EPSILON_DELTA,
}
},
) )
await hass.async_block_till_done()
mock_entry.add_to_hass(hass)
await _setup_config_entry(hass, mock_entry)
client = await hass_client() client = await hass_client()
resp_1 = await client.get("/api/camera_proxy/camera.config_test") resp_1 = await client.get("/api/camera_proxy/camera.buienradar_51_5288505_400216")
# It is not possible to check if header was sent. # It is not possible to check if header was sent.
assert aioclient_mock.call_count == 1 assert aioclient_mock.call_count == 1
@ -197,7 +200,7 @@ async def test_last_modified_updates(aioclient_mock, hass, hass_client):
aioclient_mock.get(radar_map_url(), text=None, status=304) aioclient_mock.get(radar_map_url(), text=None, status=304)
resp_2 = await client.get("/api/camera_proxy/camera.config_test") resp_2 = await client.get("/api/camera_proxy/camera.buienradar_51_5288505_400216")
assert aioclient_mock.call_count == 1 assert aioclient_mock.call_count == 1
assert (await resp_1.read()) == (await resp_2.read()) assert (await resp_1.read()) == (await resp_2.read())
@ -205,10 +208,11 @@ async def test_last_modified_updates(aioclient_mock, hass, hass_client):
async def test_retries_after_error(aioclient_mock, hass, hass_client): async def test_retries_after_error(aioclient_mock, hass, hass_client):
"""Test that it does retry after an error instead of caching.""" """Test that it does retry after an error instead of caching."""
await async_setup_component( mock_entry = MockConfigEntry(domain=DOMAIN, unique_id="TEST_ID", data=TEST_CFG_DATA)
hass, "camera", {"camera": {"name": "config_test", "platform": "buienradar"}}
) mock_entry.add_to_hass(hass)
await hass.async_block_till_done()
await _setup_config_entry(hass, mock_entry)
client = await hass_client() client = await hass_client()
@ -216,7 +220,7 @@ async def test_retries_after_error(aioclient_mock, hass, hass_client):
# A 404 should not return data and throw: # A 404 should not return data and throw:
with suppress(ClientResponseError): with suppress(ClientResponseError):
await client.get("/api/camera_proxy/camera.config_test") await client.get("/api/camera_proxy/camera.buienradar_51_5288505_400216")
assert aioclient_mock.call_count == 1 assert aioclient_mock.call_count == 1
@ -227,7 +231,7 @@ async def test_retries_after_error(aioclient_mock, hass, hass_client):
assert aioclient_mock.call_count == 0 assert aioclient_mock.call_count == 0
# http error should not be cached, immediate retry. # http error should not be cached, immediate retry.
resp_2 = await client.get("/api/camera_proxy/camera.config_test") resp_2 = await client.get("/api/camera_proxy/camera.buienradar_51_5288505_400216")
assert aioclient_mock.call_count == 1 assert aioclient_mock.call_count == 1
# Binary text can not be added as body to `aioclient_mock.get(text=...)`, # Binary text can not be added as body to `aioclient_mock.get(text=...)`,

View File

@ -0,0 +1,131 @@
"""Test the buienradar2 config flow."""
from unittest.mock import patch
from homeassistant import config_entries, data_entry_flow
from homeassistant.components.buienradar.const import DOMAIN
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
from tests.common import MockConfigEntry
TEST_LATITUDE = 51.5288504
TEST_LONGITUDE = 5.4002156
async def test_config_flow_setup_(hass):
"""Test setup of camera."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == "form"
assert result["step_id"] == "user"
assert result["errors"] == {}
with patch(
"homeassistant.components.buienradar.async_setup_entry", return_value=True
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_LATITUDE: TEST_LATITUDE, CONF_LONGITUDE: TEST_LONGITUDE},
)
assert result["type"] == "create_entry"
assert result["title"] == f"{TEST_LATITUDE},{TEST_LONGITUDE}"
assert result["data"] == {
CONF_LATITUDE: TEST_LATITUDE,
CONF_LONGITUDE: TEST_LONGITUDE,
}
async def test_config_flow_already_configured_weather(hass):
"""Test already configured."""
entry = MockConfigEntry(
domain=DOMAIN,
data={
CONF_LATITUDE: TEST_LATITUDE,
CONF_LONGITUDE: TEST_LONGITUDE,
},
unique_id=f"{TEST_LATITUDE}-{TEST_LONGITUDE}",
)
entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == "form"
assert result["step_id"] == "user"
assert result["errors"] == {}
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_LATITUDE: TEST_LATITUDE, CONF_LONGITUDE: TEST_LONGITUDE},
)
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
async def test_import_camera(hass):
"""Test import of camera."""
with patch(
"homeassistant.components.buienradar.async_setup_entry", return_value=True
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data={CONF_LATITUDE: TEST_LATITUDE, CONF_LONGITUDE: TEST_LONGITUDE},
)
assert result["type"] == "create_entry"
assert result["title"] == f"{TEST_LATITUDE},{TEST_LONGITUDE}"
assert result["data"] == {
CONF_LATITUDE: TEST_LATITUDE,
CONF_LONGITUDE: TEST_LONGITUDE,
}
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data={CONF_LATITUDE: TEST_LATITUDE, CONF_LONGITUDE: TEST_LONGITUDE},
)
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
async def test_options_flow(hass):
"""Test options flow."""
entry = MockConfigEntry(
domain=DOMAIN,
data={
CONF_LATITUDE: TEST_LATITUDE,
CONF_LONGITUDE: TEST_LONGITUDE,
},
unique_id=DOMAIN,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
result = await hass.config_entries.options.async_init(entry.entry_id)
assert result["type"] == "form"
assert result["step_id"] == "init"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={"country_code": "BE", "delta": 450, "timeframe": 30},
)
with patch(
"homeassistant.components.buienradar.async_setup_entry", return_value=True
), patch(
"homeassistant.components.buienradar.async_unload_entry", return_value=True
):
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
await hass.async_block_till_done()
assert entry.options == {"country_code": "BE", "delta": 450, "timeframe": 30}

View File

@ -0,0 +1,120 @@
"""Tests for the buienradar component."""
from unittest.mock import patch
from homeassistant.components.buienradar import async_setup
from homeassistant.components.buienradar.const import DOMAIN
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.helpers.entity_registry import async_get_registry
from tests.common import MockConfigEntry
TEST_LATITUDE = 51.5288504
TEST_LONGITUDE = 5.4002156
async def test_import_all(hass):
"""Test import of all platforms."""
config = {
"weather 1": [{"platform": "buienradar", "name": "test1"}],
"sensor 1": [{"platform": "buienradar", "timeframe": 30, "name": "test2"}],
"camera 1": [
{
"platform": "buienradar",
"country_code": "BE",
"delta": 300,
"name": "test3",
}
],
}
with patch(
"homeassistant.components.buienradar.async_setup_entry", return_value=True
):
await async_setup(hass, config)
await hass.async_block_till_done()
conf_entries = hass.config_entries.async_entries(DOMAIN)
assert len(conf_entries) == 1
entry = conf_entries[0]
assert entry.state == "loaded"
assert entry.data == {
"latitude": hass.config.latitude,
"longitude": hass.config.longitude,
"timeframe": 30,
"country_code": "BE",
"delta": 300,
"name": "test2",
}
async def test_import_camera(hass):
"""Test import of camera platform."""
entity_registry = await async_get_registry(hass)
entity_registry.async_get_or_create(
domain="camera",
platform="buienradar",
unique_id="512_NL",
original_name="test_name",
)
await hass.async_block_till_done()
config = {
"camera 1": [{"platform": "buienradar", "country_code": "NL", "dimension": 512}]
}
with patch(
"homeassistant.components.buienradar.async_setup_entry", return_value=True
):
await async_setup(hass, config)
await hass.async_block_till_done()
conf_entries = hass.config_entries.async_entries(DOMAIN)
assert len(conf_entries) == 1
entry = conf_entries[0]
assert entry.state == "loaded"
assert entry.data == {
"latitude": hass.config.latitude,
"longitude": hass.config.longitude,
"timeframe": 60,
"country_code": "NL",
"delta": 600,
"name": "Buienradar",
}
entity_id = entity_registry.async_get_entity_id(
"camera",
"buienradar",
f"{hass.config.latitude:2.6f}{hass.config.longitude:2.6f}",
)
assert entity_id
entity = entity_registry.async_get(entity_id)
assert entity.original_name == "test_name"
async def test_load_unload(hass):
"""Test options flow."""
entry = MockConfigEntry(
domain=DOMAIN,
data={
CONF_LATITUDE: TEST_LATITUDE,
CONF_LONGITUDE: TEST_LONGITUDE,
},
unique_id=DOMAIN,
)
entry.add_to_hass(hass)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.state == "loaded"
await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()
assert entry.state == "not_loaded"

View File

@ -1,26 +1,29 @@
"""The tests for the Buienradar sensor platform.""" """The tests for the Buienradar sensor platform."""
from homeassistant.components import sensor from unittest.mock import patch
from homeassistant.setup import async_setup_component
from homeassistant.components.buienradar.const import DOMAIN
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
from tests.common import MockConfigEntry
CONDITIONS = ["stationname", "temperature"] CONDITIONS = ["stationname", "temperature"]
BASE_CONFIG = { TEST_CFG_DATA = {CONF_LATITUDE: 51.5288504, CONF_LONGITUDE: 5.4002156}
"sensor": [
{
"platform": "buienradar",
"name": "volkel",
"latitude": 51.65,
"longitude": 5.7,
"monitored_conditions": CONDITIONS,
}
]
}
async def test_smoke_test_setup_component(hass): async def test_smoke_test_setup_component(hass):
"""Smoke test for successfully set-up with default config.""" """Smoke test for successfully set-up with default config."""
assert await async_setup_component(hass, sensor.DOMAIN, BASE_CONFIG) mock_entry = MockConfigEntry(domain=DOMAIN, unique_id="TEST_ID", data=TEST_CFG_DATA)
mock_entry.add_to_hass(hass)
with patch(
"homeassistant.components.buienradar.sensor.BrSensor.entity_registry_enabled_default"
) as enabled_by_default_mock:
enabled_by_default_mock.return_value = True
await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
for cond in CONDITIONS: for cond in CONDITIONS:
state = hass.states.get(f"sensor.volkel_{cond}") state = hass.states.get(f"sensor.buienradar_{cond}")
assert state.state == "unknown" assert state.state == "unknown"

View File

@ -1,25 +1,20 @@
"""The tests for the buienradar weather component.""" """The tests for the buienradar weather component."""
from homeassistant.components import weather from homeassistant.components.buienradar.const import DOMAIN
from homeassistant.setup import async_setup_component from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
# Example config snippet from documentation. from tests.common import MockConfigEntry
BASE_CONFIG = {
"weather": [ TEST_CFG_DATA = {CONF_LATITUDE: 51.5288504, CONF_LONGITUDE: 5.4002156}
{
"platform": "buienradar",
"name": "volkel",
"latitude": 51.65,
"longitude": 5.7,
"forecast": True,
}
]
}
async def test_smoke_test_setup_component(hass): async def test_smoke_test_setup_component(hass):
"""Smoke test for successfully set-up with default config.""" """Smoke test for successfully set-up with default config."""
assert await async_setup_component(hass, weather.DOMAIN, BASE_CONFIG) mock_entry = MockConfigEntry(domain=DOMAIN, unique_id="TEST_ID", data=TEST_CFG_DATA)
mock_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
state = hass.states.get("weather.volkel") state = hass.states.get("weather.buienradar")
assert state.state == "unknown" assert state.state == "unknown"