diff --git a/homeassistant/components/sensor/buienradar.py b/homeassistant/components/sensor/buienradar.py index 755d88bb443..8961fa1dc74 100755 --- a/homeassistant/components/sensor/buienradar.py +++ b/homeassistant/components/sensor/buienradar.py @@ -23,12 +23,14 @@ from homeassistant.helpers.event import ( async_track_point_in_utc_time) from homeassistant.util import dt as dt_util -REQUIREMENTS = ['buienradar==0.8'] +REQUIREMENTS = ['buienradar==0.9'] _LOGGER = logging.getLogger(__name__) MEASURED_LABEL = 'Measured' TIMEFRAME_LABEL = 'Timeframe' +SYMBOL = 'symbol' + # Schedule next call after (minutes): SCHEDULE_OK = 10 # When an error occurred, new call after (minutes): @@ -38,6 +40,10 @@ SCHEDULE_NOK = 2 # Key: ['label', unit, icon] SENSOR_TYPES = { 'stationname': ['Stationname', None, None], + 'condition': ['Condition', None, None], + 'conditioncode': ['Condition code', None, None], + 'conditiondetailed': ['Detailed condition', None, None], + 'conditionexact': ['Full condition', None, None], 'symbol': ['Symbol', None, None], 'humidity': ['Humidity', '%', 'mdi:water-percent'], 'temperature': ['Temperature', TEMP_CELSIUS, 'mdi:thermometer'], @@ -55,7 +61,67 @@ SENSOR_TYPES = { 'precipitation_forecast_average': ['Precipitation forecast average', 'mm/h', 'mdi:weather-pouring'], 'precipitation_forecast_total': ['Precipitation forecast total', - 'mm', 'mdi:weather-pouring'] + 'mm', 'mdi:weather-pouring'], + 'temperature_1d': ['Temperature 1d', TEMP_CELSIUS, 'mdi:thermometer'], + 'temperature_2d': ['Temperature 2d', TEMP_CELSIUS, 'mdi:thermometer'], + 'temperature_3d': ['Temperature 3d', TEMP_CELSIUS, 'mdi:thermometer'], + 'temperature_4d': ['Temperature 4d', TEMP_CELSIUS, 'mdi:thermometer'], + 'temperature_5d': ['Temperature 5d', TEMP_CELSIUS, 'mdi:thermometer'], + 'mintemp_1d': ['Minimum temperature 1d', TEMP_CELSIUS, 'mdi:thermometer'], + 'mintemp_2d': ['Minimum temperature 2d', TEMP_CELSIUS, 'mdi:thermometer'], + 'mintemp_3d': ['Minimum temperature 3d', TEMP_CELSIUS, 'mdi:thermometer'], + 'mintemp_4d': ['Minimum temperature 4d', TEMP_CELSIUS, 'mdi:thermometer'], + 'mintemp_5d': ['Minimum temperature 5d', TEMP_CELSIUS, 'mdi:thermometer'], + 'rain_1d': ['Rain 1d', 'mm', 'mdi:weather-pouring'], + 'rain_2d': ['Rain 2d', 'mm', 'mdi:weather-pouring'], + 'rain_3d': ['Rain 3d', 'mm', 'mdi:weather-pouring'], + 'rain_4d': ['Rain 4d', 'mm', 'mdi:weather-pouring'], + 'rain_5d': ['Rain 5d', 'mm', 'mdi:weather-pouring'], + 'snow_1d': ['Snow 1d', 'cm', 'mdi:snowflake'], + 'snow_2d': ['Snow 2d', 'cm', 'mdi:snowflake'], + 'snow_3d': ['Snow 3d', 'cm', 'mdi:snowflake'], + 'snow_4d': ['Snow 4d', 'cm', 'mdi:snowflake'], + 'snow_5d': ['Snow 5d', 'cm', 'mdi:snowflake'], + 'rainchance_1d': ['Rainchance 1d', '%', 'mdi:weather-pouring'], + 'rainchance_2d': ['Rainchance 2d', '%', 'mdi:weather-pouring'], + 'rainchance_3d': ['Rainchance 3d', '%', 'mdi:weather-pouring'], + 'rainchance_4d': ['Rainchance 4d', '%', 'mdi:weather-pouring'], + 'rainchance_5d': ['Rainchance 5d', '%', 'mdi:weather-pouring'], + 'sunchance_1d': ['Sunchance 1d', '%', 'mdi:weather-partlycloudy'], + 'sunchance_2d': ['Sunchance 2d', '%', 'mdi:weather-partlycloudy'], + 'sunchance_3d': ['Sunchance 3d', '%', 'mdi:weather-partlycloudy'], + 'sunchance_4d': ['Sunchance 4d', '%', 'mdi:weather-partlycloudy'], + 'sunchance_5d': ['Sunchance 5d', '%', 'mdi:weather-partlycloudy'], + 'windforce_1d': ['Wind force 1d', 'Bft', 'mdi:weather-windy'], + 'windforce_2d': ['Wind force 2d', 'Bft', 'mdi:weather-windy'], + 'windforce_3d': ['Wind force 3d', 'Bft', 'mdi:weather-windy'], + 'windforce_4d': ['Wind force 4d', 'Bft', 'mdi:weather-windy'], + 'windforce_5d': ['Wind force 5d', 'Bft', 'mdi:weather-windy'], + 'condition_1d': ['Condition 1d', None, None], + 'condition_2d': ['Condition 2d', None, None], + 'condition_3d': ['Condition 3d', None, None], + 'condition_4d': ['Condition 4d', None, None], + 'condition_5d': ['Condition 5d', None, None], + 'conditioncode_1d': ['Condition code 1d', None, None], + 'conditioncode_2d': ['Condition code 2d', None, None], + 'conditioncode_3d': ['Condition code 3d', None, None], + 'conditioncode_4d': ['Condition code 4d', None, None], + 'conditioncode_5d': ['Condition code 5d', None, None], + 'conditiondetailed_1d': ['Detailed condition 1d', None, None], + 'conditiondetailed_2d': ['Detailed condition 2d', None, None], + 'conditiondetailed_3d': ['Detailed condition 3d', None, None], + 'conditiondetailed_4d': ['Detailed condition 4d', None, None], + 'conditiondetailed_5d': ['Detailed condition 5d', None, None], + 'conditionexact_1d': ['Full condition 1d', None, None], + 'conditionexact_2d': ['Full condition 2d', None, None], + 'conditionexact_3d': ['Full condition 3d', None, None], + 'conditionexact_4d': ['Full condition 4d', None, None], + 'conditionexact_5d': ['Full condition 5d', None, None], + 'symbol_1d': ['Symbol 1d', None, None], + 'symbol_2d': ['Symbol 2d', None, None], + 'symbol_3d': ['Symbol 3d', None, None], + 'symbol_4d': ['Symbol 4d', None, None], + 'symbol_5d': ['Symbol 5d', None, None], } CONF_TIMEFRAME = 'timeframe' @@ -126,23 +192,86 @@ class BrSensor(Entity): def load_data(self, data): """Load the sensor with relevant data.""" # Find sensor - from buienradar.buienradar import (ATTRIBUTION, IMAGE, MEASURED, + from buienradar.buienradar import (ATTRIBUTION, CONDITION, CONDCODE, + DETAILED, EXACT, EXACTNL, FORECAST, + IMAGE, MEASURED, PRECIPITATION_FORECAST, STATIONNAME, - SYMBOL, TIMEFRAME) + TIMEFRAME) self._attribution = data.get(ATTRIBUTION) self._stationname = data.get(STATIONNAME) self._measured = data.get(MEASURED) - if self.type == SYMBOL: - # update weather symbol & status text - new_state = data.get(self.type) - img = data.get(IMAGE) - # pylint: disable=protected-access - if new_state != self._state or img != self._entity_picture: - self._state = new_state - self._entity_picture = img - return True + if self.type.endswith('_1d') or \ + self.type.endswith('_2d') or \ + self.type.endswith('_3d') or \ + self.type.endswith('_4d') or \ + self.type.endswith('_5d'): + + fcday = 0 + if self.type.endswith('_2d'): + fcday = 1 + if self.type.endswith('_3d'): + fcday = 2 + if self.type.endswith('_4d'): + fcday = 3 + if self.type.endswith('_5d'): + fcday = 4 + + # update all other sensors + if self.type.startswith(SYMBOL) or self.type.startswith(CONDITION): + condition = data.get(FORECAST)[fcday].get(CONDITION) + if condition: + new_state = condition.get(CONDITION, None) + if self.type.startswith(SYMBOL): + new_state = condition.get(EXACTNL, None) + if self.type.startswith('conditioncode'): + new_state = condition.get(CONDCODE, None) + if self.type.startswith('conditiondetailed'): + new_state = condition.get(DETAILED, None) + if self.type.startswith('conditionexact'): + new_state = condition.get(EXACT, None) + + img = condition.get(IMAGE, None) + + if new_state != self._state or img != self._entity_picture: + self._state = new_state + self._entity_picture = img + return True + return False + else: + new_state = data.get(FORECAST)[fcday].get(self.type[:-3]) + + if new_state != self._state: + self._state = new_state + return True + return False + + return False + + if self.type == SYMBOL or self.type.startswith(CONDITION): + # update weather symbol & status text + condition = data.get(CONDITION, None) + if condition: + if self.type == SYMBOL: + new_state = condition.get(EXACTNL, None) + if self.type == CONDITION: + new_state = condition.get(CONDITION, None) + if self.type == 'conditioncode': + new_state = condition.get(CONDCODE, None) + if self.type == 'conditiondetailed': + new_state = condition.get(DETAILED, None) + if self.type == 'conditionexact': + new_state = condition.get(EXACT, None) + + img = condition.get(IMAGE, None) + + # pylint: disable=protected-access + if new_state != self._state or img != self._entity_picture: + self._state = new_state + self._entity_picture = img + return True + return False if self.type.startswith(PRECIPITATION_FORECAST): @@ -187,11 +316,6 @@ class BrSensor(Entity): @property def entity_picture(self): """Weather symbol if type is symbol.""" - from buienradar.buienradar import SYMBOL - - if self.type != SYMBOL: - return None - return self._entity_picture @property @@ -360,8 +484,8 @@ class BrData(object): @property def condition(self): """Return the condition.""" - from buienradar.buienradar import SYMBOL - return self.data.get(SYMBOL) + from buienradar.buienradar import CONDITION + return self.data.get(CONDITION) @property def temperature(self): @@ -390,6 +514,15 @@ class BrData(object): except (ValueError, TypeError): return None + @property + def visibility(self): + """Return the visibility, or None.""" + from buienradar.buienradar import VISIBILITY + try: + return int(self.data.get(VISIBILITY)) + except (ValueError, TypeError): + return None + @property def wind_speed(self): """Return the windspeed, or None.""" @@ -402,9 +535,9 @@ class BrData(object): @property def wind_bearing(self): """Return the wind bearing, or None.""" - from buienradar.buienradar import WINDDIRECTION + from buienradar.buienradar import WINDAZIMUTH try: - return int(self.data.get(WINDDIRECTION)) + return int(self.data.get(WINDAZIMUTH)) except (ValueError, TypeError): return None diff --git a/homeassistant/components/weather/buienradar.py b/homeassistant/components/weather/buienradar.py index bca50182a16..f37914b3b0f 100755 --- a/homeassistant/components/weather/buienradar.py +++ b/homeassistant/components/weather/buienradar.py @@ -7,7 +7,7 @@ https://home-assistant.io/components/weather.buienradar/ import logging import asyncio from homeassistant.components.weather import ( - WeatherEntity, PLATFORM_SCHEMA) + WeatherEntity, PLATFORM_SCHEMA, ATTR_FORECAST_TEMP, ATTR_FORECAST_TIME) from homeassistant.const import \ CONF_NAME, TEMP_CELSIUS, CONF_LATITUDE, CONF_LONGITUDE from homeassistant.helpers import config_validation as cv @@ -16,14 +16,37 @@ from homeassistant.components.sensor.buienradar import ( BrData) import voluptuous as vol -REQUIREMENTS = ['buienradar==0.8'] +REQUIREMENTS = ['buienradar==0.9'] _LOGGER = logging.getLogger(__name__) +DATA_CONDITION = 'buienradar_condition' + DEFAULT_TIMEFRAME = 60 CONF_FORECAST = 'forecast' +ATTR_FORECAST_CONDITION = 'condition' +ATTR_FORECAST_TEMP_LOW = 'templow' + + +CONDITION_CLASSES = { + 'cloudy': ['c', 'p'], + 'fog': ['d', 'n'], + 'hail': [], + 'lightning': ['g'], + 'lightning-rainy': ['s'], + 'partlycloudy': ['b', 'j', 'o', 'r'], + 'pouring': ['l', 'q'], + 'rainy': ['f', 'h', 'k', 'm'], + 'snowy': ['u', 'i', 'v', 't'], + 'snowy-rainy': ['w'], + 'sunny': ['a'], + 'windy': [], + 'windy-variant': [], + 'exceptional': [], +} + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_LATITUDE): cv.latitude, @@ -50,8 +73,16 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): # create weather device: _LOGGER.debug("Initializing buienradar weather: coordinates %s", coordinates) - async_add_devices([BrWeather(data, config.get(CONF_FORECAST, True), - config.get(CONF_NAME, None))]) + + # create condition helper + if DATA_CONDITION not in hass.data: + cond_keys = [str(chr(x)) for x in range(97, 123)] + hass.data[DATA_CONDITION] = dict.fromkeys(cond_keys) + for cond, condlst in CONDITION_CLASSES.items(): + for condi in condlst: + hass.data[DATA_CONDITION][condi] = cond + + async_add_devices([BrWeather(data, config)]) # schedule the first update in 1 minute from now: yield from data.schedule_update(1) @@ -60,10 +91,10 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): class BrWeather(WeatherEntity): """Representation of a weather condition.""" - def __init__(self, data, forecast, stationname=None): + def __init__(self, data, config): """Initialise the platform with a data instance and station name.""" - self._stationname = stationname - self._forecast = forecast + self._stationname = config.get(CONF_NAME, None) + self._forecast = config.get(CONF_FORECAST) self._data = data @property @@ -79,17 +110,32 @@ class BrWeather(WeatherEntity): @property def condition(self): - """Return the name of the sensor.""" - return self._data.condition + """Return the current condition.""" + from buienradar.buienradar import (CONDCODE) + if self._data and self._data.condition: + ccode = self._data.condition.get(CONDCODE) + if ccode: + conditions = self.hass.data.get(DATA_CONDITION) + if conditions: + return conditions.get(ccode) + + @property + def entity_picture(self): + """Return the entity picture to use in the frontend, if any.""" + from buienradar.buienradar import (IMAGE) + + if self._data and self._data.condition: + return self._data.condition.get(IMAGE, None) + return None @property def temperature(self): - """Return the name of the sensor.""" + """Return the current temperature.""" return self._data.temperature @property def pressure(self): - """Return the name of the sensor.""" + """Return the current pressure.""" return self._data.pressure @property @@ -97,14 +143,19 @@ class BrWeather(WeatherEntity): """Return the name of the sensor.""" return self._data.humidity + @property + def visibility(self): + """Return the current visibility.""" + return self._data.visibility + @property def wind_speed(self): - """Return the name of the sensor.""" + """Return the current windspeed.""" return self._data.wind_speed @property def wind_bearing(self): - """Return the name of the sensor.""" + """Return the current wind bearing (degrees).""" return self._data.wind_bearing @property @@ -114,6 +165,25 @@ class BrWeather(WeatherEntity): @property def forecast(self): - """Return the forecast.""" + """Return the forecast array.""" + from buienradar.buienradar import (CONDITION, CONDCODE, DATETIME, + MIN_TEMP, MAX_TEMP) + if self._forecast: - return self._data.forecast + fcdata_out = [] + cond = self.hass.data[DATA_CONDITION] + if self._data.forecast: + for data_in in self._data.forecast: + # remap keys from external library to + # keys understood by the weather component: + data_out = {} + condcode = data_in.get(CONDITION, []).get(CONDCODE) + + data_out[ATTR_FORECAST_TIME] = data_in.get(DATETIME) + data_out[ATTR_FORECAST_CONDITION] = cond[condcode] + data_out[ATTR_FORECAST_TEMP_LOW] = data_in.get(MIN_TEMP) + data_out[ATTR_FORECAST_TEMP] = data_in.get(MAX_TEMP) + + fcdata_out.append(data_out) + + return fcdata_out diff --git a/requirements_all.txt b/requirements_all.txt index 0d8d5f55843..59ca0a67bb1 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -128,7 +128,7 @@ broadlink==0.5 # homeassistant.components.sensor.buienradar # homeassistant.components.weather.buienradar -buienradar==0.8 +buienradar==0.9 # homeassistant.components.notify.ciscospark ciscosparkapi==0.4.2