diff --git a/CODEOWNERS b/CODEOWNERS index b4773603d6e..8a1c3d9c892 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -48,7 +48,7 @@ homeassistant/components/braviatv/* @robbiet480 homeassistant/components/broadlink/* @danielhiversen homeassistant/components/brunt/* @eavanvalkenburg homeassistant/components/bt_smarthub/* @jxwolstenholme -homeassistant/components/buienradar/* @ties +homeassistant/components/buienradar/* @mjj4791 @ties homeassistant/components/cisco_ios/* @fbradyirl homeassistant/components/cisco_mobility_express/* @fbradyirl homeassistant/components/cisco_webex_teams/* @fbradyirl diff --git a/homeassistant/components/buienradar/manifest.json b/homeassistant/components/buienradar/manifest.json index 1ed313348f7..d25a2526a5d 100644 --- a/homeassistant/components/buienradar/manifest.json +++ b/homeassistant/components/buienradar/manifest.json @@ -3,8 +3,10 @@ "name": "Buienradar", "documentation": "https://www.home-assistant.io/components/buienradar", "requirements": [ - "buienradar==0.91" + "buienradar==1.0.1" ], "dependencies": [], - "codeowners": ["@ties"] + "codeowners": [ + "@mjj4791", "@ties" + ] } diff --git a/homeassistant/components/buienradar/sensor.py b/homeassistant/components/buienradar/sensor.py index 71ad6abb914..2a18fe3212e 100644 --- a/homeassistant/components/buienradar/sensor.py +++ b/homeassistant/components/buienradar/sensor.py @@ -32,28 +32,40 @@ SCHEDULE_NOK = 2 # Key: ['label', unit, icon] SENSOR_TYPES = { 'stationname': ['Stationname', None, None], + # new in json api (>1.0.0): + 'barometerfc': ['Barometer value', None, 'mdi:gauge'], + # new in json api (>1.0.0): + 'barometerfcname': ['Barometer', None, 'mdi:gauge'], + # new in json api (>1.0.0): + 'barometerfcnamenl': ['Barometer', None, 'mdi:gauge'], 'condition': ['Condition', None, None], 'conditioncode': ['Condition code', None, None], 'conditiondetailed': ['Detailed condition', None, None], 'conditionexact': ['Full condition', None, None], 'symbol': ['Symbol', None, None], + # new in json api (>1.0.0): + 'feeltemperature': ['Feel temperature', TEMP_CELSIUS, 'mdi:thermometer'], 'humidity': ['Humidity', '%', 'mdi:water-percent'], 'temperature': ['Temperature', TEMP_CELSIUS, 'mdi:thermometer'], 'groundtemperature': ['Ground temperature', TEMP_CELSIUS, 'mdi:thermometer'], - 'windspeed': ['Wind speed', 'm/s', 'mdi:weather-windy'], + 'windspeed': ['Wind speed', 'km/h', 'mdi:weather-windy'], 'windforce': ['Wind force', 'Bft', 'mdi:weather-windy'], 'winddirection': ['Wind direction', None, 'mdi:compass-outline'], 'windazimuth': ['Wind direction azimuth', '°', 'mdi:compass-outline'], 'pressure': ['Pressure', 'hPa', 'mdi:gauge'], - 'visibility': ['Visibility', 'm', None], - 'windgust': ['Wind gust', 'm/s', 'mdi:weather-windy'], + 'visibility': ['Visibility', 'km', None], + 'windgust': ['Wind gust', 'km/h', 'mdi:weather-windy'], 'precipitation': ['Precipitation', 'mm/h', 'mdi:weather-pouring'], 'irradiance': ['Irradiance', 'W/m2', 'mdi:sunglasses'], 'precipitation_forecast_average': ['Precipitation forecast average', 'mm/h', 'mdi:weather-pouring'], 'precipitation_forecast_total': ['Precipitation forecast total', 'mm', 'mdi:weather-pouring'], + # new in json api (>1.0.0): + 'rainlast24hour': ['Rain last 24h', 'mm', 'mdi:weather-pouring'], + # new in json api (>1.0.0): + 'rainlasthour': ['Rain last hour', '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'], @@ -69,11 +81,18 @@ SENSOR_TYPES = { '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'], + # new in json api (>1.0.0): + 'minrain_1d': ['Minimum rain 1d', 'mm', 'mdi:weather-pouring'], + 'minrain_2d': ['Minimum rain 2d', 'mm', 'mdi:weather-pouring'], + 'minrain_3d': ['Minimum rain 3d', 'mm', 'mdi:weather-pouring'], + 'minrain_4d': ['Minimum rain 4d', 'mm', 'mdi:weather-pouring'], + 'minrain_5d': ['Minimum rain 5d', 'mm', 'mdi:weather-pouring'], + # new in json api (>1.0.0): + 'maxrain_1d': ['Maximum rain 1d', 'mm', 'mdi:weather-pouring'], + 'maxrain_2d': ['Maximum rain 2d', 'mm', 'mdi:weather-pouring'], + 'maxrain_3d': ['Maximum rain 3d', 'mm', 'mdi:weather-pouring'], + 'maxrain_4d': ['Maximum rain 4d', 'mm', 'mdi:weather-pouring'], + 'maxrain_5d': ['Maximum rain 5d', 'mm', 'mdi:weather-pouring'], 'rainchance_1d': ['Rainchance 1d', '%', 'mdi:weather-pouring'], 'rainchance_2d': ['Rainchance 2d', '%', 'mdi:weather-pouring'], 'rainchance_3d': ['Rainchance 3d', '%', 'mdi:weather-pouring'], @@ -89,6 +108,26 @@ SENSOR_TYPES = { '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'], + 'windspeed_1d': ['Wind speed 1d', 'km/h', 'mdi:weather-windy'], + 'windspeed_2d': ['Wind speed 2d', 'km/h', 'mdi:weather-windy'], + 'windspeed_3d': ['Wind speed 3d', 'km/h', 'mdi:weather-windy'], + 'windspeed_4d': ['Wind speed 4d', 'km/h', 'mdi:weather-windy'], + 'windspeed_5d': ['Wind speed 5d', 'km/h', 'mdi:weather-windy'], + 'winddirection_1d': ['Wind direction 1d', None, 'mdi:compass-outline'], + 'winddirection_2d': ['Wind direction 2d', None, 'mdi:compass-outline'], + 'winddirection_3d': ['Wind direction 3d', None, 'mdi:compass-outline'], + 'winddirection_4d': ['Wind direction 4d', None, 'mdi:compass-outline'], + 'winddirection_5d': ['Wind direction 5d', None, 'mdi:compass-outline'], + 'windazimuth_1d': ['Wind direction azimuth 1d', '°', + 'mdi:compass-outline'], + 'windazimuth_2d': ['Wind direction azimuth 2d', '°', + 'mdi:compass-outline'], + 'windazimuth_3d': ['Wind direction azimuth 3d', '°', + 'mdi:compass-outline'], + 'windazimuth_4d': ['Wind direction azimuth 4d', '°', + 'mdi:compass-outline'], + 'windazimuth_5d': ['Wind direction azimuth 5d', '°', + 'mdi:compass-outline'], 'condition_1d': ['Condition 1d', None, None], 'condition_2d': ['Condition 2d', None, None], 'condition_3d': ['Condition 3d', None, None], @@ -113,7 +152,7 @@ SENSOR_TYPES = { 'symbol_2d': ['Symbol 2d', None, None], 'symbol_3d': ['Symbol 3d', None, None], 'symbol_4d': ['Symbol 4d', None, None], - 'symbol_5d': ['Symbol 5d', None, None], + 'symbol_5d': ['Symbol 5d', None, None] } CONF_TIMEFRAME = 'timeframe' @@ -168,7 +207,7 @@ class BrSensor(Entity): def __init__(self, sensor_type, client_name, coordinates): """Initialize the sensor.""" - from buienradar.buienradar import (PRECIPITATION_FORECAST, CONDITION) + from buienradar.constants import (PRECIPITATION_FORECAST, CONDITION) self.client_name = client_name self._name = SENSOR_TYPES[sensor_type][0] @@ -198,11 +237,12 @@ class BrSensor(Entity): def load_data(self, data): """Load the sensor with relevant data.""" # Find sensor - from buienradar.buienradar import (ATTRIBUTION, CONDITION, CONDCODE, - DETAILED, EXACT, EXACTNL, FORECAST, - IMAGE, MEASURED, - PRECIPITATION_FORECAST, STATIONNAME, - TIMEFRAME) + from buienradar.constants import (ATTRIBUTION, CONDITION, CONDCODE, + DETAILED, EXACT, EXACTNL, FORECAST, + IMAGE, MEASURED, + PRECIPITATION_FORECAST, STATIONNAME, + TIMEFRAME, VISIBILITY, WINDGUST, + WINDSPEED) # Check if we have a new measurement, # otherwise we do not have to update the sensor @@ -219,6 +259,7 @@ class BrSensor(Entity): self.type.endswith('_4d') or \ self.type.endswith('_5d'): + # update forcasting sensors: fcday = 0 if self.type.endswith('_2d'): fcday = 1 @@ -229,7 +270,7 @@ class BrSensor(Entity): if self.type.endswith('_5d'): fcday = 4 - # update all other sensors + # update weather symbol & status text if self.type.startswith(SYMBOL) or self.type.startswith(CONDITION): try: condition = data.get(FORECAST)[fcday].get(CONDITION) @@ -256,6 +297,18 @@ class BrSensor(Entity): return True return False + if self.type.startswith(WINDSPEED): + # hass wants windspeeds in km/h not m/s, so convert: + try: + self._state = data.get(FORECAST)[fcday].get(self.type[:-3]) + if self._state is not None: + self._state = round(self._state * 3.6, 1) + return True + except IndexError: + _LOGGER.warning("No forecast for fcday=%s...", fcday) + return False + + # update all other sensors try: self._state = data.get(FORECAST)[fcday].get(self.type[:-3]) return True @@ -294,6 +347,20 @@ class BrSensor(Entity): self._state = nested.get(self.type[len(PRECIPITATION_FORECAST)+1:]) return True + if self.type == WINDSPEED or self.type == WINDGUST: + # hass wants windspeeds in km/h not m/s, so convert: + self._state = data.get(self.type) + if self._state is not None: + self._state = round(data.get(self.type) * 3.6, 1) + return True + + if self.type == VISIBILITY: + # hass wants visibility in km (not m), so convert: + self._state = data.get(self.type) + if self._state is not None: + self._state = round(self._state / 1000, 1) + return True + # update all other sensors self._state = data.get(self.type) return True @@ -331,7 +398,7 @@ class BrSensor(Entity): @property def device_state_attributes(self): """Return the state attributes.""" - from buienradar.buienradar import (PRECIPITATION_FORECAST) + from buienradar.constants import (PRECIPITATION_FORECAST) if self.type.startswith(PRECIPITATION_FORECAST): result = {ATTR_ATTRIBUTION: self._attribution} @@ -399,8 +466,8 @@ class BrData: async def get_data(self, url): """Load data from specified url.""" - from buienradar.buienradar import (CONTENT, - MESSAGE, STATUS_CODE, SUCCESS) + from buienradar.constants import (CONTENT, + MESSAGE, STATUS_CODE, SUCCESS) _LOGGER.debug("Calling url: %s...", url) result = {SUCCESS: False, MESSAGE: None} @@ -427,16 +494,17 @@ class BrData: async def async_update(self, *_): """Update the data from buienradar.""" - from buienradar.buienradar import (parse_data, CONTENT, - DATA, MESSAGE, STATUS_CODE, SUCCESS) + from buienradar.constants import (CONTENT, DATA, MESSAGE, + STATUS_CODE, SUCCESS) + from buienradar.buienradar import (parse_data) + from buienradar.urls import (JSON_FEED_URL, + json_precipitation_forecast_url) - content = await self.get_data('http://xml.buienradar.nl') - if not content.get(SUCCESS, False): - content = await self.get_data('http://api.buienradar.nl') + content = await self.get_data(JSON_FEED_URL) if content.get(SUCCESS) is not True: # unable to get the data - _LOGGER.warning("Unable to retrieve xml data from Buienradar." + _LOGGER.warning("Unable to retrieve json data from Buienradar." "(Msg: %s, status: %s,)", content.get(MESSAGE), content.get(STATUS_CODE),) @@ -445,11 +513,9 @@ class BrData: return # rounding coordinates prevents unnecessary redirects/calls - rainurl = 'http://gadgets.buienradar.nl/data/raintext/?lat={}&lon={}' - rainurl = rainurl.format( - round(self.coordinates[CONF_LATITUDE], 2), - round(self.coordinates[CONF_LONGITUDE], 2) - ) + lat = self.coordinates[CONF_LATITUDE] + lon = self.coordinates[CONF_LONGITUDE] + rainurl = json_precipitation_forecast_url(lat, lon) raincontent = await self.get_data(rainurl) if raincontent.get(SUCCESS) is not True: @@ -466,7 +532,8 @@ class BrData: raincontent.get(CONTENT), self.coordinates[CONF_LATITUDE], self.coordinates[CONF_LONGITUDE], - self.timeframe) + self.timeframe, + False) _LOGGER.debug("Buienradar parsed data: %s", result) if result.get(SUCCESS) is not True: @@ -484,25 +551,25 @@ class BrData: @property def attribution(self): """Return the attribution.""" - from buienradar.buienradar import ATTRIBUTION + from buienradar.constants import ATTRIBUTION return self.data.get(ATTRIBUTION) @property def stationname(self): """Return the name of the selected weatherstation.""" - from buienradar.buienradar import STATIONNAME + from buienradar.constants import STATIONNAME return self.data.get(STATIONNAME) @property def condition(self): """Return the condition.""" - from buienradar.buienradar import CONDITION + from buienradar.constants import CONDITION return self.data.get(CONDITION) @property def temperature(self): """Return the temperature, or None.""" - from buienradar.buienradar import TEMPERATURE + from buienradar.constants import TEMPERATURE try: return float(self.data.get(TEMPERATURE)) except (ValueError, TypeError): @@ -511,7 +578,7 @@ class BrData: @property def pressure(self): """Return the pressure, or None.""" - from buienradar.buienradar import PRESSURE + from buienradar.constants import PRESSURE try: return float(self.data.get(PRESSURE)) except (ValueError, TypeError): @@ -520,7 +587,7 @@ class BrData: @property def humidity(self): """Return the humidity, or None.""" - from buienradar.buienradar import HUMIDITY + from buienradar.constants import HUMIDITY try: return int(self.data.get(HUMIDITY)) except (ValueError, TypeError): @@ -529,7 +596,7 @@ class BrData: @property def visibility(self): """Return the visibility, or None.""" - from buienradar.buienradar import VISIBILITY + from buienradar.constants import VISIBILITY try: return int(self.data.get(VISIBILITY)) except (ValueError, TypeError): @@ -538,7 +605,7 @@ class BrData: @property def wind_speed(self): """Return the windspeed, or None.""" - from buienradar.buienradar import WINDSPEED + from buienradar.constants import WINDSPEED try: return float(self.data.get(WINDSPEED)) except (ValueError, TypeError): @@ -547,7 +614,7 @@ class BrData: @property def wind_bearing(self): """Return the wind bearing, or None.""" - from buienradar.buienradar import WINDAZIMUTH + from buienradar.constants import WINDAZIMUTH try: return int(self.data.get(WINDAZIMUTH)) except (ValueError, TypeError): @@ -556,5 +623,5 @@ class BrData: @property def forecast(self): """Return the forecast data.""" - from buienradar.buienradar import FORECAST + from buienradar.constants import FORECAST return self.data.get(FORECAST) diff --git a/homeassistant/components/buienradar/weather.py b/homeassistant/components/buienradar/weather.py index a8e4f9d424d..0c2a1e0e2cb 100644 --- a/homeassistant/components/buienradar/weather.py +++ b/homeassistant/components/buienradar/weather.py @@ -6,7 +6,8 @@ import voluptuous as vol from homeassistant.components.weather import ( ATTR_FORECAST_CONDITION, ATTR_FORECAST_TEMP, ATTR_FORECAST_TEMP_LOW, ATTR_FORECAST_TIME, PLATFORM_SCHEMA, WeatherEntity, - ATTR_FORECAST_PRECIPITATION) + ATTR_FORECAST_PRECIPITATION, ATTR_FORECAST_WIND_BEARING, + ATTR_FORECAST_WIND_SPEED) from homeassistant.const import ( CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, TEMP_CELSIUS) from homeassistant.helpers import config_validation as cv @@ -104,7 +105,8 @@ class BrWeather(WeatherEntity): @property def condition(self): """Return the current condition.""" - from buienradar.buienradar import (CONDCODE) + from buienradar.constants import (CONDCODE) + if self._data and self._data.condition: ccode = self._data.condition.get(CONDCODE) if ccode: @@ -129,13 +131,17 @@ class BrWeather(WeatherEntity): @property def visibility(self): - """Return the current visibility.""" - return self._data.visibility + """Return the current visibility in km.""" + if self._data.visibility is None: + return None + return round(self._data.visibility / 1000, 1) @property def wind_speed(self): - """Return the current windspeed.""" - return self._data.wind_speed + """Return the current windspeed in km/h.""" + if self._data.wind_speed is None: + return None + return round(self._data.wind_speed * 3.6, 1) @property def wind_bearing(self): @@ -150,25 +156,34 @@ class BrWeather(WeatherEntity): @property def forecast(self): """Return the forecast array.""" - from buienradar.buienradar import (CONDITION, CONDCODE, RAIN, DATETIME, - MIN_TEMP, MAX_TEMP) + from buienradar.constants import (CONDITION, CONDCODE, RAIN, DATETIME, + MIN_TEMP, MAX_TEMP, WINDAZIMUTH, + WINDSPEED) - if self._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) + if not self._forecast: + return None - 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) - data_out[ATTR_FORECAST_PRECIPITATION] = data_in.get(RAIN) + fcdata_out = [] + cond = self.hass.data[DATA_CONDITION] - fcdata_out.append(data_out) + if not self._data.forecast: + return None - return fcdata_out + for data_in in self._data.forecast: + # remap keys from external library to + # keys understood by the weather component: + condcode = data_in.get(CONDITION, []).get(CONDCODE) + data_out = { + ATTR_FORECAST_TIME: data_in.get(DATETIME), + ATTR_FORECAST_CONDITION: cond[condcode], + ATTR_FORECAST_TEMP_LOW: data_in.get(MIN_TEMP), + ATTR_FORECAST_TEMP: data_in.get(MAX_TEMP), + ATTR_FORECAST_PRECIPITATION: data_in.get(RAIN), + ATTR_FORECAST_WIND_BEARING: data_in.get(WINDAZIMUTH), + ATTR_FORECAST_WIND_SPEED: + round(data_in.get(WINDSPEED) * 3.6, 1) + } + + fcdata_out.append(data_out) + + return fcdata_out diff --git a/requirements_all.txt b/requirements_all.txt index 8ec0da0efa8..3d1ea9233f4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -314,7 +314,7 @@ bthomehub5-devicelist==0.1.1 btsmarthub_devicelist==0.1.3 # homeassistant.components.buienradar -buienradar==0.91 +buienradar==1.0.1 # homeassistant.components.caldav caldav==0.6.1