mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 10:17:09 +00:00
Add new Meteoclimatic integration (#36906)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io> Co-authored-by: Martin Hjelmare <marhje52@gmail.com> Co-authored-by: Franck Nijhof <frenck@frenck.nl> Co-authored-by: Franck Nijhof <git@frenck.dev>
This commit is contained in:
parent
3573249720
commit
fe34f42aa5
@ -611,6 +611,9 @@ omit =
|
|||||||
homeassistant/components/meteo_france/sensor.py
|
homeassistant/components/meteo_france/sensor.py
|
||||||
homeassistant/components/meteo_france/weather.py
|
homeassistant/components/meteo_france/weather.py
|
||||||
homeassistant/components/meteoalarm/*
|
homeassistant/components/meteoalarm/*
|
||||||
|
homeassistant/components/meteoclimatic/__init__.py
|
||||||
|
homeassistant/components/meteoclimatic/const.py
|
||||||
|
homeassistant/components/meteoclimatic/weather.py
|
||||||
homeassistant/components/metoffice/sensor.py
|
homeassistant/components/metoffice/sensor.py
|
||||||
homeassistant/components/metoffice/weather.py
|
homeassistant/components/metoffice/weather.py
|
||||||
homeassistant/components/microsoft/tts.py
|
homeassistant/components/microsoft/tts.py
|
||||||
|
@ -290,6 +290,7 @@ homeassistant/components/met/* @danielhiversen @thimic
|
|||||||
homeassistant/components/met_eireann/* @DylanGore
|
homeassistant/components/met_eireann/* @DylanGore
|
||||||
homeassistant/components/meteo_france/* @hacf-fr @oncleben31 @Quentame
|
homeassistant/components/meteo_france/* @hacf-fr @oncleben31 @Quentame
|
||||||
homeassistant/components/meteoalarm/* @rolfberkenbosch
|
homeassistant/components/meteoalarm/* @rolfberkenbosch
|
||||||
|
homeassistant/components/meteoclimatic/* @adrianmo
|
||||||
homeassistant/components/metoffice/* @MrHarcombe
|
homeassistant/components/metoffice/* @MrHarcombe
|
||||||
homeassistant/components/miflora/* @danielhiversen @basnijholt
|
homeassistant/components/miflora/* @danielhiversen @basnijholt
|
||||||
homeassistant/components/mikrotik/* @engrbm87
|
homeassistant/components/mikrotik/* @engrbm87
|
||||||
|
52
homeassistant/components/meteoclimatic/__init__.py
Normal file
52
homeassistant/components/meteoclimatic/__init__.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
"""Support for Meteoclimatic weather data."""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from meteoclimatic import MeteoclimaticClient
|
||||||
|
from meteoclimatic.exceptions import MeteoclimaticError
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.helpers.typing import HomeAssistantType
|
||||||
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
|
from .const import CONF_STATION_CODE, DOMAIN, PLATFORMS, SCAN_INTERVAL
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool:
|
||||||
|
"""Set up a Meteoclimatic entry."""
|
||||||
|
station_code = entry.data[CONF_STATION_CODE]
|
||||||
|
meteoclimatic_client = MeteoclimaticClient()
|
||||||
|
|
||||||
|
async def async_update_data():
|
||||||
|
"""Obtain the latest data from Meteoclimatic."""
|
||||||
|
try:
|
||||||
|
data = await hass.async_add_executor_job(
|
||||||
|
meteoclimatic_client.weather_at_station, station_code
|
||||||
|
)
|
||||||
|
return data.__dict__
|
||||||
|
except MeteoclimaticError as err:
|
||||||
|
raise UpdateFailed(f"Error while retrieving data: {err}") from err
|
||||||
|
|
||||||
|
coordinator = DataUpdateCoordinator(
|
||||||
|
hass,
|
||||||
|
_LOGGER,
|
||||||
|
name=f"Meteoclimatic Coordinator for {station_code}",
|
||||||
|
update_method=async_update_data,
|
||||||
|
update_interval=SCAN_INTERVAL,
|
||||||
|
)
|
||||||
|
|
||||||
|
await coordinator.async_config_entry_first_refresh()
|
||||||
|
|
||||||
|
hass.data.setdefault(DOMAIN, {})
|
||||||
|
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||||
|
|
||||||
|
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool:
|
||||||
|
"""Unload a config entry."""
|
||||||
|
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||||
|
return unload_ok
|
64
homeassistant/components/meteoclimatic/config_flow.py
Normal file
64
homeassistant/components/meteoclimatic/config_flow.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
"""Config flow to configure the Meteoclimatic integration."""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from meteoclimatic import MeteoclimaticClient
|
||||||
|
from meteoclimatic.exceptions import MeteoclimaticError, StationNotFound
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant import config_entries
|
||||||
|
|
||||||
|
from .const import CONF_STATION_CODE, DOMAIN
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class MeteoclimaticFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
|
"""Handle a Meteoclimatic config flow."""
|
||||||
|
|
||||||
|
VERSION = 1
|
||||||
|
|
||||||
|
def _show_setup_form(self, user_input=None, errors=None):
|
||||||
|
"""Show the setup form to the user."""
|
||||||
|
if user_input is None:
|
||||||
|
user_input = {}
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="user",
|
||||||
|
data_schema=vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(
|
||||||
|
CONF_STATION_CODE, default=user_input.get(CONF_STATION_CODE, "")
|
||||||
|
): str
|
||||||
|
}
|
||||||
|
),
|
||||||
|
errors=errors or {},
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_step_user(self, user_input=None):
|
||||||
|
"""Handle a flow initiated by the user."""
|
||||||
|
errors = {}
|
||||||
|
|
||||||
|
if user_input is None:
|
||||||
|
return self._show_setup_form(user_input, errors)
|
||||||
|
|
||||||
|
station_code = user_input[CONF_STATION_CODE]
|
||||||
|
client = MeteoclimaticClient()
|
||||||
|
|
||||||
|
try:
|
||||||
|
weather = await self.hass.async_add_executor_job(
|
||||||
|
client.weather_at_station, station_code
|
||||||
|
)
|
||||||
|
except StationNotFound as exp:
|
||||||
|
_LOGGER.error("Station not found: %s", exp)
|
||||||
|
errors["base"] = "not_found"
|
||||||
|
return self._show_setup_form(user_input, errors)
|
||||||
|
except MeteoclimaticError as exp:
|
||||||
|
_LOGGER.error("Error when obtaining Meteoclimatic weather: %s", exp)
|
||||||
|
return self.async_abort(reason="unknown")
|
||||||
|
|
||||||
|
# Check if already configured
|
||||||
|
await self.async_set_unique_id(station_code, raise_on_progress=False)
|
||||||
|
|
||||||
|
return self.async_create_entry(
|
||||||
|
title=weather.station.name, data={CONF_STATION_CODE: station_code}
|
||||||
|
)
|
134
homeassistant/components/meteoclimatic/const.py
Normal file
134
homeassistant/components/meteoclimatic/const.py
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
"""Meteoclimatic component constants."""
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from meteoclimatic import Condition
|
||||||
|
|
||||||
|
from homeassistant.components.weather import (
|
||||||
|
ATTR_CONDITION_CLEAR_NIGHT,
|
||||||
|
ATTR_CONDITION_CLOUDY,
|
||||||
|
ATTR_CONDITION_EXCEPTIONAL,
|
||||||
|
ATTR_CONDITION_FOG,
|
||||||
|
ATTR_CONDITION_HAIL,
|
||||||
|
ATTR_CONDITION_LIGHTNING,
|
||||||
|
ATTR_CONDITION_LIGHTNING_RAINY,
|
||||||
|
ATTR_CONDITION_PARTLYCLOUDY,
|
||||||
|
ATTR_CONDITION_POURING,
|
||||||
|
ATTR_CONDITION_RAINY,
|
||||||
|
ATTR_CONDITION_SNOWY,
|
||||||
|
ATTR_CONDITION_SNOWY_RAINY,
|
||||||
|
ATTR_CONDITION_SUNNY,
|
||||||
|
ATTR_CONDITION_WINDY,
|
||||||
|
ATTR_CONDITION_WINDY_VARIANT,
|
||||||
|
)
|
||||||
|
from homeassistant.const import (
|
||||||
|
DEGREE,
|
||||||
|
DEVICE_CLASS_HUMIDITY,
|
||||||
|
DEVICE_CLASS_PRESSURE,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
LENGTH_MILLIMETERS,
|
||||||
|
PERCENTAGE,
|
||||||
|
PRESSURE_HPA,
|
||||||
|
SPEED_KILOMETERS_PER_HOUR,
|
||||||
|
TEMP_CELSIUS,
|
||||||
|
)
|
||||||
|
|
||||||
|
DOMAIN = "meteoclimatic"
|
||||||
|
PLATFORMS = ["weather"]
|
||||||
|
ATTRIBUTION = "Data provided by Meteoclimatic"
|
||||||
|
|
||||||
|
SCAN_INTERVAL = timedelta(minutes=10)
|
||||||
|
|
||||||
|
CONF_STATION_CODE = "station_code"
|
||||||
|
|
||||||
|
DEFAULT_WEATHER_CARD = True
|
||||||
|
|
||||||
|
SENSOR_TYPE_NAME = "name"
|
||||||
|
SENSOR_TYPE_UNIT = "unit"
|
||||||
|
SENSOR_TYPE_ICON = "icon"
|
||||||
|
SENSOR_TYPE_CLASS = "device_class"
|
||||||
|
SENSOR_TYPES = {
|
||||||
|
"temp_current": {
|
||||||
|
SENSOR_TYPE_NAME: "Temperature",
|
||||||
|
SENSOR_TYPE_UNIT: TEMP_CELSIUS,
|
||||||
|
SENSOR_TYPE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||||
|
},
|
||||||
|
"temp_max": {
|
||||||
|
SENSOR_TYPE_NAME: "Max Temp.",
|
||||||
|
SENSOR_TYPE_UNIT: TEMP_CELSIUS,
|
||||||
|
SENSOR_TYPE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||||
|
},
|
||||||
|
"temp_min": {
|
||||||
|
SENSOR_TYPE_NAME: "Min Temp.",
|
||||||
|
SENSOR_TYPE_UNIT: TEMP_CELSIUS,
|
||||||
|
SENSOR_TYPE_CLASS: DEVICE_CLASS_TEMPERATURE,
|
||||||
|
},
|
||||||
|
"humidity_current": {
|
||||||
|
SENSOR_TYPE_NAME: "Humidity",
|
||||||
|
SENSOR_TYPE_UNIT: PERCENTAGE,
|
||||||
|
SENSOR_TYPE_CLASS: DEVICE_CLASS_HUMIDITY,
|
||||||
|
},
|
||||||
|
"humidity_max": {
|
||||||
|
SENSOR_TYPE_NAME: "Max Humidity",
|
||||||
|
SENSOR_TYPE_UNIT: PERCENTAGE,
|
||||||
|
SENSOR_TYPE_CLASS: DEVICE_CLASS_HUMIDITY,
|
||||||
|
},
|
||||||
|
"humidity_min": {
|
||||||
|
SENSOR_TYPE_NAME: "Min Humidity",
|
||||||
|
SENSOR_TYPE_UNIT: PERCENTAGE,
|
||||||
|
SENSOR_TYPE_CLASS: DEVICE_CLASS_HUMIDITY,
|
||||||
|
},
|
||||||
|
"pressure_current": {
|
||||||
|
SENSOR_TYPE_NAME: "Pressure",
|
||||||
|
SENSOR_TYPE_UNIT: PRESSURE_HPA,
|
||||||
|
SENSOR_TYPE_CLASS: DEVICE_CLASS_PRESSURE,
|
||||||
|
},
|
||||||
|
"pressure_max": {
|
||||||
|
SENSOR_TYPE_NAME: "Max Pressure",
|
||||||
|
SENSOR_TYPE_UNIT: PRESSURE_HPA,
|
||||||
|
SENSOR_TYPE_CLASS: DEVICE_CLASS_PRESSURE,
|
||||||
|
},
|
||||||
|
"pressure_min": {
|
||||||
|
SENSOR_TYPE_NAME: "Min Pressure",
|
||||||
|
SENSOR_TYPE_UNIT: PRESSURE_HPA,
|
||||||
|
SENSOR_TYPE_CLASS: DEVICE_CLASS_PRESSURE,
|
||||||
|
},
|
||||||
|
"wind_current": {
|
||||||
|
SENSOR_TYPE_NAME: "Wind Speed",
|
||||||
|
SENSOR_TYPE_UNIT: SPEED_KILOMETERS_PER_HOUR,
|
||||||
|
SENSOR_TYPE_ICON: "mdi:weather-windy",
|
||||||
|
},
|
||||||
|
"wind_max": {
|
||||||
|
SENSOR_TYPE_NAME: "Max Wind Speed",
|
||||||
|
SENSOR_TYPE_UNIT: SPEED_KILOMETERS_PER_HOUR,
|
||||||
|
SENSOR_TYPE_ICON: "mdi:weather-windy",
|
||||||
|
},
|
||||||
|
"wind_bearing": {
|
||||||
|
SENSOR_TYPE_NAME: "Wind Bearing",
|
||||||
|
SENSOR_TYPE_UNIT: DEGREE,
|
||||||
|
SENSOR_TYPE_ICON: "mdi:weather-windy",
|
||||||
|
},
|
||||||
|
"rain": {
|
||||||
|
SENSOR_TYPE_NAME: "Rain",
|
||||||
|
SENSOR_TYPE_UNIT: LENGTH_MILLIMETERS,
|
||||||
|
SENSOR_TYPE_ICON: "mdi:weather-rainy",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
CONDITION_CLASSES = {
|
||||||
|
ATTR_CONDITION_CLEAR_NIGHT: [Condition.moon, Condition.hazemoon],
|
||||||
|
ATTR_CONDITION_CLOUDY: [Condition.mooncloud],
|
||||||
|
ATTR_CONDITION_EXCEPTIONAL: [],
|
||||||
|
ATTR_CONDITION_FOG: [Condition.fog, Condition.mist],
|
||||||
|
ATTR_CONDITION_HAIL: [],
|
||||||
|
ATTR_CONDITION_LIGHTNING: [Condition.storm],
|
||||||
|
ATTR_CONDITION_LIGHTNING_RAINY: [],
|
||||||
|
ATTR_CONDITION_PARTLYCLOUDY: [Condition.suncloud, Condition.hazesun],
|
||||||
|
ATTR_CONDITION_POURING: [],
|
||||||
|
ATTR_CONDITION_RAINY: [Condition.rain],
|
||||||
|
ATTR_CONDITION_SNOWY: [],
|
||||||
|
ATTR_CONDITION_SNOWY_RAINY: [],
|
||||||
|
ATTR_CONDITION_SUNNY: [Condition.sun],
|
||||||
|
ATTR_CONDITION_WINDY: [],
|
||||||
|
ATTR_CONDITION_WINDY_VARIANT: [],
|
||||||
|
}
|
13
homeassistant/components/meteoclimatic/manifest.json
Normal file
13
homeassistant/components/meteoclimatic/manifest.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"domain": "meteoclimatic",
|
||||||
|
"name": "Meteoclimatic",
|
||||||
|
"config_flow": true,
|
||||||
|
"documentation": "https://www.home-assistant.io/integrations/meteoclimatic",
|
||||||
|
"requirements": [
|
||||||
|
"pymeteoclimatic==0.0.6"
|
||||||
|
],
|
||||||
|
"codeowners": [
|
||||||
|
"@adrianmo"
|
||||||
|
],
|
||||||
|
"iot_class": "cloud_polling"
|
||||||
|
}
|
20
homeassistant/components/meteoclimatic/strings.json
Normal file
20
homeassistant/components/meteoclimatic/strings.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"title": "Meteoclimatic",
|
||||||
|
"description": "Enter the Meteoclimatic station code (e.g., ESCAT4300000043206B)",
|
||||||
|
"data": {
|
||||||
|
"code": "Station code"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||||
|
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"not_found": "[%key:common::config_flow::abort::no_devices_found%]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
homeassistant/components/meteoclimatic/translations/en.json
Normal file
20
homeassistant/components/meteoclimatic/translations/en.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "Station already configured",
|
||||||
|
"unknown": "Unknown error: please try again later"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"not_found": "The station code did not return any data. Check that the code belongs to a station and it has the right format (e.g., ESCAT4300000043206B)"
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"code": "Station code"
|
||||||
|
},
|
||||||
|
"description": "Enter the Meteoclimatic station code (e.g., ESCAT4300000043206B)",
|
||||||
|
"title": "Meteoclimatic"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
93
homeassistant/components/meteoclimatic/weather.py
Normal file
93
homeassistant/components/meteoclimatic/weather.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
"""Support for Meteoclimatic weather service."""
|
||||||
|
from meteoclimatic import Condition
|
||||||
|
|
||||||
|
from homeassistant.components.weather import WeatherEntity
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.const import TEMP_CELSIUS
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
from homeassistant.helpers.typing import HomeAssistantType
|
||||||
|
from homeassistant.helpers.update_coordinator import (
|
||||||
|
CoordinatorEntity,
|
||||||
|
DataUpdateCoordinator,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .const import ATTRIBUTION, CONDITION_CLASSES, DOMAIN
|
||||||
|
|
||||||
|
|
||||||
|
def format_condition(condition):
|
||||||
|
"""Return condition from dict CONDITION_CLASSES."""
|
||||||
|
for key, value in CONDITION_CLASSES.items():
|
||||||
|
if condition in value:
|
||||||
|
return key
|
||||||
|
if isinstance(condition, Condition):
|
||||||
|
return condition.value
|
||||||
|
return condition
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
|
) -> None:
|
||||||
|
"""Set up the Meteoclimatic weather platform."""
|
||||||
|
coordinator = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
|
||||||
|
async_add_entities([MeteoclimaticWeather(coordinator)], False)
|
||||||
|
|
||||||
|
|
||||||
|
class MeteoclimaticWeather(CoordinatorEntity, WeatherEntity):
|
||||||
|
"""Representation of a weather condition."""
|
||||||
|
|
||||||
|
def __init__(self, coordinator: DataUpdateCoordinator) -> None:
|
||||||
|
"""Initialise the weather platform."""
|
||||||
|
super().__init__(coordinator)
|
||||||
|
self._unique_id = self.coordinator.data["station"].code
|
||||||
|
self._name = self.coordinator.data["station"].name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""Return the name of the sensor."""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unique_id(self):
|
||||||
|
"""Return the unique id of the sensor."""
|
||||||
|
return self._unique_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def condition(self):
|
||||||
|
"""Return the current condition."""
|
||||||
|
return format_condition(self.coordinator.data["weather"].condition)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def temperature(self):
|
||||||
|
"""Return the temperature."""
|
||||||
|
return self.coordinator.data["weather"].temp_current
|
||||||
|
|
||||||
|
@property
|
||||||
|
def temperature_unit(self):
|
||||||
|
"""Return the unit of measurement."""
|
||||||
|
return TEMP_CELSIUS
|
||||||
|
|
||||||
|
@property
|
||||||
|
def humidity(self):
|
||||||
|
"""Return the humidity."""
|
||||||
|
return self.coordinator.data["weather"].humidity_current
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pressure(self):
|
||||||
|
"""Return the pressure."""
|
||||||
|
return self.coordinator.data["weather"].pressure_current
|
||||||
|
|
||||||
|
@property
|
||||||
|
def wind_speed(self):
|
||||||
|
"""Return the wind speed."""
|
||||||
|
return self.coordinator.data["weather"].wind_current
|
||||||
|
|
||||||
|
@property
|
||||||
|
def wind_bearing(self):
|
||||||
|
"""Return the wind bearing."""
|
||||||
|
return self.coordinator.data["weather"].wind_bearing
|
||||||
|
|
||||||
|
@property
|
||||||
|
def attribution(self):
|
||||||
|
"""Return the attribution."""
|
||||||
|
return ATTRIBUTION
|
@ -150,6 +150,7 @@ FLOWS = [
|
|||||||
"met",
|
"met",
|
||||||
"met_eireann",
|
"met_eireann",
|
||||||
"meteo_france",
|
"meteo_france",
|
||||||
|
"meteoclimatic",
|
||||||
"metoffice",
|
"metoffice",
|
||||||
"mikrotik",
|
"mikrotik",
|
||||||
"mill",
|
"mill",
|
||||||
|
@ -1559,6 +1559,9 @@ pymediaroom==0.6.4.1
|
|||||||
# homeassistant.components.melcloud
|
# homeassistant.components.melcloud
|
||||||
pymelcloud==2.5.2
|
pymelcloud==2.5.2
|
||||||
|
|
||||||
|
# homeassistant.components.meteoclimatic
|
||||||
|
pymeteoclimatic==0.0.6
|
||||||
|
|
||||||
# homeassistant.components.somfy
|
# homeassistant.components.somfy
|
||||||
pymfy==0.9.3
|
pymfy==0.9.3
|
||||||
|
|
||||||
|
@ -870,6 +870,9 @@ pymazda==0.1.5
|
|||||||
# homeassistant.components.melcloud
|
# homeassistant.components.melcloud
|
||||||
pymelcloud==2.5.2
|
pymelcloud==2.5.2
|
||||||
|
|
||||||
|
# homeassistant.components.meteoclimatic
|
||||||
|
pymeteoclimatic==0.0.6
|
||||||
|
|
||||||
# homeassistant.components.somfy
|
# homeassistant.components.somfy
|
||||||
pymfy==0.9.3
|
pymfy==0.9.3
|
||||||
|
|
||||||
|
1
tests/components/meteoclimatic/__init__.py
Normal file
1
tests/components/meteoclimatic/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""Tests for the Meteoclimatic component."""
|
13
tests/components/meteoclimatic/conftest.py
Normal file
13
tests/components/meteoclimatic/conftest.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
"""Meteoclimatic generic test utils."""
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def patch_requests():
|
||||||
|
"""Stub out services that makes requests."""
|
||||||
|
patch_client = patch("homeassistant.components.meteoclimatic.MeteoclimaticClient")
|
||||||
|
|
||||||
|
with patch_client:
|
||||||
|
yield
|
88
tests/components/meteoclimatic/test_config_flow.py
Normal file
88
tests/components/meteoclimatic/test_config_flow.py
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
"""Tests for the Meteoclimatic config flow."""
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from meteoclimatic.exceptions import MeteoclimaticError, StationNotFound
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant import data_entry_flow
|
||||||
|
from homeassistant.components.meteoclimatic.const import CONF_STATION_CODE, DOMAIN
|
||||||
|
from homeassistant.config_entries import SOURCE_USER
|
||||||
|
|
||||||
|
TEST_STATION_CODE = "ESCAT4300000043206B"
|
||||||
|
TEST_STATION_NAME = "Reus (Tarragona)"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="client")
|
||||||
|
def mock_controller_client():
|
||||||
|
"""Mock a successful client."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.meteoclimatic.config_flow.MeteoclimaticClient",
|
||||||
|
update=False,
|
||||||
|
) as service_mock:
|
||||||
|
service_mock.return_value.get_data.return_value = {
|
||||||
|
"station_code": TEST_STATION_CODE
|
||||||
|
}
|
||||||
|
weather = service_mock.return_value.weather_at_station.return_value
|
||||||
|
weather.station.name = TEST_STATION_NAME
|
||||||
|
yield service_mock
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def mock_setup():
|
||||||
|
"""Prevent setup."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.meteoclimatic.async_setup_entry",
|
||||||
|
return_value=True,
|
||||||
|
):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
async def test_user(hass, client):
|
||||||
|
"""Test user config."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "user"
|
||||||
|
|
||||||
|
# test with all provided
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_USER},
|
||||||
|
data={CONF_STATION_CODE: TEST_STATION_CODE},
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||||
|
assert result["result"].unique_id == TEST_STATION_CODE
|
||||||
|
assert result["title"] == TEST_STATION_NAME
|
||||||
|
assert result["data"][CONF_STATION_CODE] == TEST_STATION_CODE
|
||||||
|
|
||||||
|
|
||||||
|
async def test_not_found(hass):
|
||||||
|
"""Test when we have the station code is not found."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.meteoclimatic.config_flow.MeteoclimaticClient.weather_at_station",
|
||||||
|
side_effect=StationNotFound(TEST_STATION_CODE),
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_USER},
|
||||||
|
data={CONF_STATION_CODE: TEST_STATION_CODE},
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
|
assert result["step_id"] == "user"
|
||||||
|
assert result["errors"]["base"] == "not_found"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_unknown_error(hass):
|
||||||
|
"""Test when we have an unknown error fetching station data."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.meteoclimatic.config_flow.MeteoclimaticClient.weather_at_station",
|
||||||
|
side_effect=MeteoclimaticError,
|
||||||
|
):
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_USER},
|
||||||
|
data={CONF_STATION_CODE: TEST_STATION_CODE},
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
|
assert result["reason"] == "unknown"
|
Loading…
x
Reference in New Issue
Block a user