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>pull/50069/head
parent
6931478688
commit
c063f14c24
|
@ -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
|
||||
homeassistant/components/cast/* @emontnemery
|
||||
homeassistant/components/cert_expiry/* @Cereal2nd @jjlawren
|
||||
homeassistant/components/circuit/* @braam
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -9,14 +9,22 @@ import aiohttp
|
|||
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__)
|
||||
|
||||
|
@ -41,13 +49,27 @@ PLATFORM_SCHEMA = vol.All(
|
|||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up buienradar radar-loop camera component."""
|
||||
dimension = config[CONF_DIMENSION]
|
||||
delta = config[CONF_DELTA]
|
||||
name = config[CONF_NAME]
|
||||
country = config[CONF_COUNTRY]
|
||||
"""Set up buienradar camera platform."""
|
||||
_LOGGER.warning(
|
||||
"Platform configuration is deprecated, will be removed in a future release"
|
||||
)
|
||||
|
||||
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):
|
||||
|
@ -59,7 +81,9 @@ 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.
|
||||
|
||||
|
@ -67,10 +91,10 @@ class BuienradarCam(Camera):
|
|||
"""
|
||||
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
|
||||
|
@ -94,7 +118,7 @@ class BuienradarCam(Camera):
|
|||
# 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:
|
||||
|
@ -192,3 +216,8 @@ class BuienradarCam(Camera):
|
|||
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
|
||||
|
|
|
@ -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)),
|
||||
}
|
||||
),
|
||||
)
|
|
@ -1,6 +1,24 @@
|
|||
"""Constants for buienradar component."""
|
||||
|
||||
DOMAIN = "buienradar"
|
||||
|
||||
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_OK = 10
|
||||
"""When an error occurred, new call after (minutes)."""
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
{
|
||||
"domain": "buienradar",
|
||||
"name": "Buienradar",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/buienradar",
|
||||
"requirements": ["buienradar==1.0.4"],
|
||||
"codeowners": ["@mjj4791", "@ties"],
|
||||
"codeowners": ["@mjj4791", "@ties", "@Robbie1221"],
|
||||
"iot_class": "cloud_polling"
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ from buienradar.constants import (
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION,
|
||||
CONF_LATITUDE,
|
||||
|
@ -37,11 +38,12 @@ from homeassistant.const import (
|
|||
SPEED_KILOMETERS_PER_HOUR,
|
||||
TEMP_CELSIUS,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import DEFAULT_TIMEFRAME
|
||||
from .const import CONF_TIMEFRAME, DEFAULT_TIMEFRAME
|
||||
from .util import BrData
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -186,8 +188,6 @@ SENSOR_TYPES = {
|
|||
"symbol_5d": ["Symbol 5d", None, None],
|
||||
}
|
||||
|
||||
CONF_TIMEFRAME = "timeframe"
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(
|
||||
|
@ -208,14 +208,29 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|||
|
||||
|
||||
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."""
|
||||
config = entry.data
|
||||
options = entry.options
|
||||
|
||||
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
|
||||
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):
|
||||
_LOGGER.error("Latitude or longitude not set in Home Assistant config")
|
||||
return False
|
||||
return
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
dev = []
|
||||
for sensor_type in config[CONF_MONITORED_CONDITIONS]:
|
||||
dev.append(BrSensor(sensor_type, config.get(CONF_NAME), coordinates))
|
||||
async_add_entities(dev)
|
||||
entities = [
|
||||
BrSensor(sensor_type, config.get(CONF_NAME, "Buienradar"), coordinates)
|
||||
for sensor_type in SENSOR_TYPES
|
||||
]
|
||||
|
||||
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:
|
||||
await data.schedule_update(1)
|
||||
|
||||
|
@ -380,7 +397,7 @@ class BrSensor(SensorEntity):
|
|||
self._state = nested.get(self.type[len(PRECIPITATION_FORECAST) + 1 :])
|
||||
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:
|
||||
self._state = data.get(self.type)
|
||||
if self._state is not None:
|
||||
|
@ -463,3 +480,8 @@ class BrSensor(SensorEntity):
|
|||
def force_update(self):
|
||||
"""Return true for continuous sensors, false for discrete sensors."""
|
||||
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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -38,36 +38,42 @@ from homeassistant.components.weather import (
|
|||
PLATFORM_SCHEMA,
|
||||
WeatherEntity,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
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.entity_platform import AddEntitiesCallback
|
||||
|
||||
# Reuse data and API logic from the sensor implementation
|
||||
from .const import DEFAULT_TIMEFRAME
|
||||
from .const import DEFAULT_TIMEFRAME, DOMAIN
|
||||
from .util import BrData
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DATA_CONDITION = "buienradar_condition"
|
||||
|
||||
|
||||
CONF_FORECAST = "forecast"
|
||||
|
||||
DATA_CONDITION = "buienradar_condition"
|
||||
|
||||
CONDITION_CLASSES = {
|
||||
ATTR_CONDITION_CLOUDY: ["c", "p"],
|
||||
ATTR_CONDITION_FOG: ["d", "n"],
|
||||
ATTR_CONDITION_HAIL: [],
|
||||
ATTR_CONDITION_LIGHTNING: ["g"],
|
||||
ATTR_CONDITION_LIGHTNING_RAINY: ["s"],
|
||||
ATTR_CONDITION_PARTLYCLOUDY: ["b", "j", "o", "r"],
|
||||
ATTR_CONDITION_POURING: ["l", "q"],
|
||||
ATTR_CONDITION_RAINY: ["f", "h", "k", "m"],
|
||||
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: [],
|
||||
ATTR_CONDITION_CLOUDY: ("c", "p"),
|
||||
ATTR_CONDITION_FOG: ("d", "n"),
|
||||
ATTR_CONDITION_HAIL: (),
|
||||
ATTR_CONDITION_LIGHTNING: ("g",),
|
||||
ATTR_CONDITION_LIGHTNING_RAINY: ("s",),
|
||||
ATTR_CONDITION_PARTLYCLOUDY: (
|
||||
"b",
|
||||
"j",
|
||||
"o",
|
||||
"r",
|
||||
),
|
||||
ATTR_CONDITION_POURING: ("l", "q"),
|
||||
ATTR_CONDITION_RAINY: ("f", "h", "k", "m"),
|
||||
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(
|
||||
|
@ -81,13 +87,24 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|||
|
||||
|
||||
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."""
|
||||
config = entry.data
|
||||
|
||||
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
|
||||
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
|
||||
|
||||
if None in (latitude, longitude):
|
||||
_LOGGER.error("Latitude or longitude not set in Home Assistant config")
|
||||
return False
|
||||
return
|
||||
|
||||
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)
|
||||
|
||||
# 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)]
|
||||
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 condi in condlst:
|
||||
hass.data[DATA_CONDITION][condi] = cond
|
||||
hass.data[DOMAIN][DATA_CONDITION][condi] = cond
|
||||
|
||||
async_add_entities([BrWeather(data, config, coordinates)])
|
||||
|
||||
|
@ -115,8 +132,7 @@ class BrWeather(WeatherEntity):
|
|||
|
||||
def __init__(self, data, config, coordinates):
|
||||
"""Initialise the platform with a data instance and station name."""
|
||||
self._stationname = config.get(CONF_NAME)
|
||||
self._forecast = config[CONF_FORECAST]
|
||||
self._stationname = config.get(CONF_NAME, "Buienradar")
|
||||
self._data = data
|
||||
|
||||
self._unique_id = "{:2.6f}{:2.6f}".format(
|
||||
|
@ -141,7 +157,7 @@ class BrWeather(WeatherEntity):
|
|||
if self._data and self._data.condition:
|
||||
ccode = self._data.condition.get(CONDCODE)
|
||||
if ccode:
|
||||
conditions = self.hass.data.get(DATA_CONDITION)
|
||||
conditions = self.hass.data[DOMAIN].get(DATA_CONDITION)
|
||||
if conditions:
|
||||
return conditions.get(ccode)
|
||||
|
||||
|
@ -187,11 +203,8 @@ class BrWeather(WeatherEntity):
|
|||
@property
|
||||
def forecast(self):
|
||||
"""Return the forecast array."""
|
||||
if not self._forecast:
|
||||
return None
|
||||
|
||||
fcdata_out = []
|
||||
cond = self.hass.data[DATA_CONDITION]
|
||||
cond = self.hass.data[DOMAIN][DATA_CONDITION]
|
||||
|
||||
if not self._data.forecast:
|
||||
return None
|
||||
|
|
|
@ -37,6 +37,7 @@ FLOWS = [
|
|||
"broadlink",
|
||||
"brother",
|
||||
"bsblan",
|
||||
"buienradar",
|
||||
"canary",
|
||||
"cast",
|
||||
"cert_expiry",
|
||||
|
|
|
@ -1,34 +1,63 @@
|
|||
"""The tests for generic camera component."""
|
||||
import asyncio
|
||||
from contextlib import suppress
|
||||
import copy
|
||||
|
||||
from aiohttp.client_exceptions import ClientResponseError
|
||||
|
||||
from homeassistant.const import HTTP_INTERNAL_SERVER_ERROR
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.components.buienradar.const import CONF_COUNTRY, CONF_DELTA, DOMAIN
|
||||
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 tests.common import MockConfigEntry
|
||||
|
||||
# An infinitesimally small time-delta.
|
||||
EPSILON_DELTA = 0.0000000001
|
||||
|
||||
TEST_LATITUDE = 51.5288504
|
||||
TEST_LONGITUDE = 5.4002156
|
||||
|
||||
def radar_map_url(dim: int = 512, country_code: str = "NL") -> str:
|
||||
"""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}"
|
||||
TEST_CFG_DATA = {CONF_LATITUDE: TEST_LATITUDE, CONF_LONGITUDE: TEST_LONGITUDE}
|
||||
|
||||
|
||||
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):
|
||||
"""Test that it fetches the given url."""
|
||||
aioclient_mock.get(radar_map_url(), text="hello world")
|
||||
|
||||
await async_setup_component(
|
||||
hass, "camera", {"camera": {"name": "config_test", "platform": "buienradar"}}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
mock_entry = MockConfigEntry(domain=DOMAIN, unique_id="TEST_ID", data=TEST_CFG_DATA)
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
await _setup_config_entry(hass, mock_entry)
|
||||
|
||||
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 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
|
||||
# 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
|
||||
|
||||
|
||||
|
@ -46,22 +75,19 @@ async def test_expire_delta(aioclient_mock, hass, hass_client):
|
|||
"""Test that the cache expires after delta."""
|
||||
aioclient_mock.get(radar_map_url(), text="hello world")
|
||||
|
||||
await async_setup_component(
|
||||
hass,
|
||||
"camera",
|
||||
{
|
||||
"camera": {
|
||||
"name": "config_test",
|
||||
"platform": "buienradar",
|
||||
"delta": EPSILON_DELTA,
|
||||
}
|
||||
},
|
||||
options = {CONF_DELTA: EPSILON_DELTA}
|
||||
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN, unique_id="TEST_ID", data=TEST_CFG_DATA, options=options
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
await _setup_config_entry(hass, mock_entry)
|
||||
|
||||
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 aioclient_mock.call_count == 1
|
||||
|
@ -70,7 +96,7 @@ async def test_expire_delta(aioclient_mock, hass, hass_client):
|
|||
|
||||
await asyncio.sleep(EPSILON_DELTA)
|
||||
# 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
|
||||
|
||||
|
||||
|
@ -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."""
|
||||
aioclient_mock.get(radar_map_url(), text="hello world")
|
||||
|
||||
await async_setup_component(
|
||||
hass, "camera", {"camera": {"name": "config_test", "platform": "buienradar"}}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
mock_entry = MockConfigEntry(domain=DOMAIN, unique_id="TEST_ID", data=TEST_CFG_DATA)
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
await _setup_config_entry(hass, mock_entry)
|
||||
|
||||
client = await hass_client()
|
||||
|
||||
resp_1 = client.get("/api/camera_proxy/camera.config_test")
|
||||
resp_2 = 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.buienradar_51_5288505_400216")
|
||||
|
||||
resp = await resp_1
|
||||
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
|
||||
|
||||
|
||||
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):
|
||||
"""Test that it actually adheres to another country like Belgium."""
|
||||
aioclient_mock.get(radar_map_url(country_code="BE"), text="hello world")
|
||||
|
||||
await async_setup_component(
|
||||
hass,
|
||||
"camera",
|
||||
{
|
||||
"camera": {
|
||||
"name": "config_test",
|
||||
"platform": "buienradar",
|
||||
"country_code": "BE",
|
||||
}
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
data = copy.deepcopy(TEST_CFG_DATA)
|
||||
data[CONF_COUNTRY] = "BE"
|
||||
|
||||
mock_entry = MockConfigEntry(domain=DOMAIN, unique_id="TEST_ID", data=data)
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
await _setup_config_entry(hass, mock_entry)
|
||||
|
||||
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
|
||||
|
||||
|
@ -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."""
|
||||
aioclient_mock.get(radar_map_url(), text="hello world", status=401)
|
||||
|
||||
await async_setup_component(
|
||||
hass, "camera", {"camera": {"name": "config_test", "platform": "buienradar"}}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
mock_entry = MockConfigEntry(domain=DOMAIN, unique_id="TEST_ID", data=TEST_CFG_DATA)
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
await _setup_config_entry(hass, mock_entry)
|
||||
|
||||
client = await hass_client()
|
||||
|
||||
await client.get("/api/camera_proxy/camera.config_test")
|
||||
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.buienradar_51_5288505_400216")
|
||||
|
||||
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},
|
||||
)
|
||||
|
||||
await async_setup_component(
|
||||
hass,
|
||||
"camera",
|
||||
{
|
||||
"camera": {
|
||||
"name": "config_test",
|
||||
"platform": "buienradar",
|
||||
"delta": EPSILON_DELTA,
|
||||
}
|
||||
},
|
||||
options = {CONF_DELTA: EPSILON_DELTA}
|
||||
|
||||
mock_entry = MockConfigEntry(
|
||||
domain=DOMAIN, unique_id="TEST_ID", data=TEST_CFG_DATA, options=options
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
await _setup_config_entry(hass, mock_entry)
|
||||
|
||||
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.
|
||||
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)
|
||||
|
||||
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 (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):
|
||||
"""Test that it does retry after an error instead of caching."""
|
||||
await async_setup_component(
|
||||
hass, "camera", {"camera": {"name": "config_test", "platform": "buienradar"}}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
mock_entry = MockConfigEntry(domain=DOMAIN, unique_id="TEST_ID", data=TEST_CFG_DATA)
|
||||
|
||||
mock_entry.add_to_hass(hass)
|
||||
|
||||
await _setup_config_entry(hass, mock_entry)
|
||||
|
||||
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:
|
||||
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
|
||||
|
||||
|
@ -227,7 +231,7 @@ async def test_retries_after_error(aioclient_mock, hass, hass_client):
|
|||
assert aioclient_mock.call_count == 0
|
||||
|
||||
# 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
|
||||
|
||||
# Binary text can not be added as body to `aioclient_mock.get(text=...)`,
|
||||
|
|
|
@ -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}
|
|
@ -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"
|
|
@ -1,26 +1,29 @@
|
|||
"""The tests for the Buienradar sensor platform."""
|
||||
from homeassistant.components import sensor
|
||||
from homeassistant.setup import async_setup_component
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.components.buienradar.const import DOMAIN
|
||||
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
CONDITIONS = ["stationname", "temperature"]
|
||||
BASE_CONFIG = {
|
||||
"sensor": [
|
||||
{
|
||||
"platform": "buienradar",
|
||||
"name": "volkel",
|
||||
"latitude": 51.65,
|
||||
"longitude": 5.7,
|
||||
"monitored_conditions": CONDITIONS,
|
||||
}
|
||||
]
|
||||
}
|
||||
TEST_CFG_DATA = {CONF_LATITUDE: 51.5288504, CONF_LONGITUDE: 5.4002156}
|
||||
|
||||
|
||||
async def test_smoke_test_setup_component(hass):
|
||||
"""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()
|
||||
|
||||
for cond in CONDITIONS:
|
||||
state = hass.states.get(f"sensor.volkel_{cond}")
|
||||
state = hass.states.get(f"sensor.buienradar_{cond}")
|
||||
assert state.state == "unknown"
|
||||
|
|
|
@ -1,25 +1,20 @@
|
|||
"""The tests for the buienradar weather component."""
|
||||
from homeassistant.components import weather
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.components.buienradar.const import DOMAIN
|
||||
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE
|
||||
|
||||
# Example config snippet from documentation.
|
||||
BASE_CONFIG = {
|
||||
"weather": [
|
||||
{
|
||||
"platform": "buienradar",
|
||||
"name": "volkel",
|
||||
"latitude": 51.65,
|
||||
"longitude": 5.7,
|
||||
"forecast": True,
|
||||
}
|
||||
]
|
||||
}
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
TEST_CFG_DATA = {CONF_LATITUDE: 51.5288504, CONF_LONGITUDE: 5.4002156}
|
||||
|
||||
|
||||
async def test_smoke_test_setup_component(hass):
|
||||
"""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()
|
||||
|
||||
state = hass.states.get("weather.volkel")
|
||||
state = hass.states.get("weather.buienradar")
|
||||
assert state.state == "unknown"
|
||||
|
|
Loading…
Reference in New Issue