diff --git a/.coveragerc b/.coveragerc index 599e155f8f3..dea02d21f56 100644 --- a/.coveragerc +++ b/.coveragerc @@ -721,6 +721,7 @@ omit = homeassistant/components/sensor/luftdaten.py homeassistant/components/sensor/lyft.py homeassistant/components/sensor/magicseaweed.py + homeassistant/components/sensor/meteo_france.py homeassistant/components/sensor/metoffice.py homeassistant/components/sensor/miflora.py homeassistant/components/sensor/mitemp_bt.py diff --git a/homeassistant/components/sensor/meteo_france.py b/homeassistant/components/sensor/meteo_france.py new file mode 100644 index 00000000000..1e18b1518a7 --- /dev/null +++ b/homeassistant/components/sensor/meteo_france.py @@ -0,0 +1,139 @@ +""" +Support for Meteo France raining forecast. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.meteo_france/ +""" + +import logging +import datetime + +import voluptuous as vol + +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import ( + CONF_MONITORED_CONDITIONS, ATTR_ATTRIBUTION, TEMP_CELSIUS) +from homeassistant.helpers.entity import Entity +from homeassistant.util import Throttle +import homeassistant.helpers.config_validation as cv + +REQUIREMENTS = ['meteofrance==0.2.7'] +_LOGGER = logging.getLogger(__name__) + +CONF_ATTRIBUTION = "Data provided by Meteo-France" +CONF_POSTAL_CODE = 'postal_code' + +STATE_ATTR_FORECAST = '1h rain forecast' + +SCAN_INTERVAL = datetime.timedelta(minutes=5) + +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], +} + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_POSTAL_CODE): cv.string, + vol.Required(CONF_MONITORED_CONDITIONS): + vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]), +}) + + +def setup_platform(hass, config, add_entities, discovery_info=None): + """Set up the Meteo-France sensor.""" + postal_code = config[CONF_POSTAL_CODE] + + from meteofrance.client import meteofranceClient, meteofranceError + + try: + meteofrance_client = meteofranceClient(postal_code) + except meteofranceError as exp: + _LOGGER.error(exp) + return + + client = MeteoFranceUpdater(meteofrance_client) + + add_entities([MeteoFranceSensor(variable, client) + for variable in config[CONF_MONITORED_CONDITIONS]], + True) + + +class MeteoFranceSensor(Entity): + """Representation of a Sensor.""" + + def __init__(self, condition, client): + """Initialize the sensor.""" + self._condition = condition + self._client = client + self._state = None + self._data = {} + + @property + def name(self): + """Return the name of the sensor.""" + return "{} {}".format(self._data["name"], + SENSOR_TYPES[self._condition][0]) + + @property + def state(self): + """Return the state of the sensor.""" + return self._state + + @property + def device_state_attributes(self): + """Return the state attributes of the sensor.""" + if self._condition == 'next_rain' and "rain_forecast" in self._data: + return { + **{ + STATE_ATTR_FORECAST: self._data["rain_forecast"], + }, + ** self._data["next_rain_intervals"], + **{ + ATTR_ATTRIBUTION: CONF_ATTRIBUTION + } + } + return {ATTR_ATTRIBUTION: CONF_ATTRIBUTION} + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return SENSOR_TYPES[self._condition][1] + + def update(self): + """Fetch new state data for the sensor.""" + try: + self._client.update() + self._data = self._client.get_data() + self._state = self._data[self._condition] + except KeyError: + _LOGGER.error("No condition `%s` for location `%s`", + self._condition, self._data["name"]) + self._state = None + + +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) diff --git a/requirements_all.txt b/requirements_all.txt index 99caed7b23c..1874b8112cc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -604,6 +604,9 @@ maxcube-api==0.1.0 # homeassistant.components.notify.message_bird messagebird==1.2.0 +# homeassistant.components.sensor.meteo_france +meteofrance==0.2.7 + # homeassistant.components.sensor.mfi # homeassistant.components.switch.mfi mficlient==0.3.0