Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Météo-France platform for the weather component (#18404)
* new weather component for meteofrance * linting * upgrade meteofrance package version * Update .coveragerc * Remove updates to the weather component architecture * Rewrite Météo-France as a component * Update .coveragerc * Update requirements_all.txt * remove Weather Card option * Update conf name Changing conf name to something more universal for worldwide weather forecast (postal code was only relevent for France) * Update meteofrance pypi package * fix line too long * Update requirements_all.txt * prevent from calling an API endpoint if not in monitored conditions * fix stale url and remove blank line * Insure that all cities are unique * rename CONF_ATTRIBUTION * Updating data from component setup * fix missing extra lines
- Loading branch information
1 parent
801401e
commit f4b2573
Showing
5 changed files
with
270 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
""" | ||
Support for Meteo France weather forecast. | ||
For more details about this platform, please refer to the documentation at | ||
https://home-assistant.io/components/meteo_france/ | ||
""" | ||
import logging | ||
import datetime | ||
|
||
import voluptuous as vol | ||
|
||
from homeassistant.const import ( | ||
CONF_MONITORED_CONDITIONS, TEMP_CELSIUS) | ||
from homeassistant.util import Throttle | ||
from homeassistant.helpers.discovery import load_platform | ||
import homeassistant.helpers.config_validation as cv | ||
|
||
REQUIREMENTS = ['meteofrance==0.3.4'] | ||
_LOGGER = logging.getLogger(__name__) | ||
|
||
DOMAIN = 'meteo_france' | ||
SCAN_INTERVAL = datetime.timedelta(minutes=5) | ||
ATTRIBUTION = "Data provided by Météo-France" | ||
CONF_CITY = 'city' | ||
DEFAULT_WEATHER_CARD = True | ||
DATA_METEO_FRANCE = 'data_meteo_france' | ||
|
||
SENSOR_TYPES = { | ||
'rain_chance': ['Rain chance', '%'], | ||
'freeze_chance': ['Freeze chance', '%'], | ||
'thunder_chance': ['Thunder chance', '%'], | ||
'snow_chance': ['Snow chance', '%'], | ||
'weather': ['Weather', None], | ||
'wind_speed': ['Wind Speed', 'km/h'], | ||
'next_rain': ['Next rain', 'min'], | ||
'temperature': ['Temperature', TEMP_CELSIUS], | ||
'uv': ['UV', None], | ||
} | ||
|
||
CONDITION_CLASSES = { | ||
'clear-night': ['Nuit Claire'], | ||
'cloudy': ['Très nuageux'], | ||
'fog': ['Brume ou bancs de brouillard', | ||
'Brouillard', 'Brouillard givrant'], | ||
'hail': ['Risque de grêle'], | ||
'lightning': ["Risque d'orages", 'Orages'], | ||
'lightning-rainy': ['Pluie orageuses', 'Pluies orageuses', | ||
'Averses orageuses'], | ||
'partlycloudy': ['Ciel voilé', 'Ciel voilé nuit', 'Éclaircies'], | ||
'pouring': ['Pluie forte'], | ||
'rainy': ['Bruine / Pluie faible', 'Bruine', 'Pluie faible', | ||
'Pluies éparses / Rares averses', 'Pluies éparses', | ||
'Rares averses', 'Pluie / Averses', 'Averses', 'Pluie'], | ||
'snowy': ['Neige / Averses de neige', 'Neige', 'Averses de neige', | ||
'Neige forte', 'Quelques flocons'], | ||
'snowy-rainy': ['Pluie et neige', 'Pluie verglaçante'], | ||
'sunny': ['Ensoleillé'], | ||
'windy': [], | ||
'windy-variant': [], | ||
'exceptional': [], | ||
} | ||
|
||
|
||
def has_all_unique_cities(value): | ||
"""Validate that all cities are unique.""" | ||
cities = [location[CONF_CITY] for location in value] | ||
vol.Schema(vol.Unique())(cities) | ||
return value | ||
|
||
|
||
CONFIG_SCHEMA = vol.Schema({ | ||
DOMAIN: vol.All(cv.ensure_list, [vol.Schema({ | ||
vol.Required(CONF_CITY): cv.string, | ||
vol.Optional(CONF_MONITORED_CONDITIONS): | ||
vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), | ||
})], has_all_unique_cities) | ||
}, extra=vol.ALLOW_EXTRA) | ||
|
||
|
||
def setup(hass, config): | ||
"""Set up the Meteo-France component.""" | ||
hass.data[DATA_METEO_FRANCE] = {} | ||
|
||
for location in config[DOMAIN]: | ||
|
||
city = location[CONF_CITY] | ||
|
||
from meteofrance.client import meteofranceClient, meteofranceError | ||
|
||
try: | ||
client = meteofranceClient(city) | ||
except meteofranceError as exp: | ||
_LOGGER.error(exp) | ||
return | ||
|
||
client.need_rain_forecast = bool(CONF_MONITORED_CONDITIONS in location | ||
and 'next_rain' in | ||
location[CONF_MONITORED_CONDITIONS]) | ||
|
||
hass.data[DATA_METEO_FRANCE][city] = MeteoFranceUpdater(client) | ||
hass.data[DATA_METEO_FRANCE][city].update() | ||
|
||
if CONF_MONITORED_CONDITIONS in location: | ||
monitored_conditions = location[CONF_MONITORED_CONDITIONS] | ||
load_platform( | ||
hass, | ||
'sensor', | ||
DOMAIN, | ||
{CONF_CITY: city, | ||
CONF_MONITORED_CONDITIONS: monitored_conditions}, | ||
config) | ||
|
||
load_platform( | ||
hass, | ||
'weather', | ||
DOMAIN, | ||
{CONF_CITY: city}, | ||
config) | ||
|
||
return True | ||
|
||
|
||
class MeteoFranceUpdater: | ||
"""Update data from Meteo-France.""" | ||
|
||
def __init__(self, client): | ||
"""Initialize the data object.""" | ||
self._client = client | ||
|
||
def get_data(self): | ||
"""Get the latest data from Meteo-France.""" | ||
return self._client.get_data() | ||
|
||
@Throttle(SCAN_INTERVAL) | ||
def update(self): | ||
"""Get the latest data from Meteo-France.""" | ||
from meteofrance.client import meteofranceError | ||
try: | ||
self._client.update() | ||
except meteofranceError as exp: | ||
_LOGGER.error(exp) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
""" | ||
Support for Meteo france weather service. | ||
For more details about this platform, please refer to the documentation at | ||
https://home-assistant.io/components/weather.meteo_france/ | ||
""" | ||
import logging | ||
from datetime import datetime, timedelta | ||
|
||
from homeassistant.components.meteo_france import (DATA_METEO_FRANCE, | ||
CONDITION_CLASSES, | ||
CONF_CITY, | ||
ATTRIBUTION) | ||
from homeassistant.components.weather import ( | ||
WeatherEntity, ATTR_FORECAST_CONDITION, | ||
ATTR_FORECAST_TEMP, ATTR_FORECAST_TEMP_LOW, ATTR_FORECAST_TIME) | ||
from homeassistant.const import TEMP_CELSIUS | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
def setup_platform(hass, config, add_entities, discovery_info=None): | ||
"""Set up the Meteo-France weather platform.""" | ||
if discovery_info is None: | ||
return | ||
|
||
city = discovery_info[CONF_CITY] | ||
|
||
client = hass.data[DATA_METEO_FRANCE][city] | ||
|
||
add_entities([MeteoFranceWeather(client)], True) | ||
|
||
|
||
class MeteoFranceWeather(WeatherEntity): | ||
"""Representation of a weather condition.""" | ||
|
||
def __init__(self, client): | ||
"""Initialise the platform with a data instance and station name.""" | ||
self._client = client | ||
self._data = {} | ||
|
||
def update(self): | ||
"""Update current conditions.""" | ||
self._client.update() | ||
self._data = self._client.get_data() | ||
|
||
@property | ||
def name(self): | ||
"""Return the name of the sensor.""" | ||
return self._data["name"] | ||
|
||
@property | ||
def condition(self): | ||
"""Return the current condition.""" | ||
return self.format_condition(self._data["weather"]) | ||
|
||
@property | ||
def temperature(self): | ||
"""Return the platform temperature.""" | ||
return self._data["temperature"] | ||
|
||
@property | ||
def humidity(self): | ||
"""Return the platform temperature.""" | ||
return None | ||
|
||
@property | ||
def temperature_unit(self): | ||
"""Return the unit of measurement.""" | ||
return TEMP_CELSIUS | ||
|
||
@property | ||
def wind_speed(self): | ||
"""Return the wind speed.""" | ||
return self._data["wind_speed"] | ||
|
||
@property | ||
def wind_bearing(self): | ||
"""Return the wind bearing.""" | ||
return self._data["wind_bearing"] | ||
|
||
@property | ||
def attribution(self): | ||
"""Return the attribution.""" | ||
return ATTRIBUTION | ||
|
||
@property | ||
def forecast(self): | ||
"""Return the forecast.""" | ||
reftime = datetime.now().replace(hour=12, minute=00) | ||
reftime += timedelta(hours=24) | ||
forecast_data = [] | ||
for key in self._data["forecast"]: | ||
value = self._data["forecast"][key] | ||
data_dict = { | ||
ATTR_FORECAST_TIME: reftime.isoformat(), | ||
ATTR_FORECAST_TEMP: int(value['max_temp']), | ||
ATTR_FORECAST_TEMP_LOW: int(value['min_temp']), | ||
ATTR_FORECAST_CONDITION: | ||
self.format_condition(value["weather"]) | ||
} | ||
reftime = reftime + timedelta(hours=24) | ||
forecast_data.append(data_dict) | ||
return forecast_data | ||
|
||
@staticmethod | ||
def format_condition(condition): | ||
"""Return condition from dict CONDITION_CLASSES.""" | ||
for key, value in CONDITION_CLASSES.items(): | ||
if condition in value: | ||
return key | ||
return condition |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters