diff --git a/docs/source/backends.rst b/docs/source/backends.rst index 8d8253bd3a..f56aa4f3b5 100644 --- a/docs/source/backends.rst +++ b/docs/source/backends.rst @@ -38,7 +38,4 @@ Backends platypush/backend/stt.picovoice.hotword.rst platypush/backend/stt.picovoice.speech.rst platypush/backend/tcp.rst - platypush/backend/weather.buienradar.rst - platypush/backend/weather.darksky.rst - platypush/backend/weather.openweathermap.rst platypush/backend/wiimote.rst diff --git a/docs/source/platypush/backend/weather.buienradar.rst b/docs/source/platypush/backend/weather.buienradar.rst deleted file mode 100644 index 0e65067893..0000000000 --- a/docs/source/platypush/backend/weather.buienradar.rst +++ /dev/null @@ -1,5 +0,0 @@ -``weather.buienradar`` -======================================== - -.. automodule:: platypush.backend.weather.buienradar - :members: diff --git a/docs/source/platypush/backend/weather.darksky.rst b/docs/source/platypush/backend/weather.darksky.rst deleted file mode 100644 index cc2b86aa37..0000000000 --- a/docs/source/platypush/backend/weather.darksky.rst +++ /dev/null @@ -1,5 +0,0 @@ -``weather.darksky`` -===================================== - -.. automodule:: platypush.backend.weather.darksky - :members: diff --git a/docs/source/platypush/backend/weather.openweathermap.rst b/docs/source/platypush/backend/weather.openweathermap.rst deleted file mode 100644 index c9521912c7..0000000000 --- a/docs/source/platypush/backend/weather.openweathermap.rst +++ /dev/null @@ -1,5 +0,0 @@ -``weather.openweathermap`` -============================================ - -.. automodule:: platypush.backend.weather.openweathermap - :members: diff --git a/docs/source/platypush/plugins/weather.darksky.rst b/docs/source/platypush/plugins/weather.darksky.rst deleted file mode 100644 index bb01795cf1..0000000000 --- a/docs/source/platypush/plugins/weather.darksky.rst +++ /dev/null @@ -1,5 +0,0 @@ -``weather.darksky`` -===================================== - -.. automodule:: platypush.plugins.weather.darksky - :members: diff --git a/docs/source/platypush/responses/weather.buienradar.rst b/docs/source/platypush/responses/weather.buienradar.rst deleted file mode 100644 index c78f749dbf..0000000000 --- a/docs/source/platypush/responses/weather.buienradar.rst +++ /dev/null @@ -1,5 +0,0 @@ -``weather.buienradar`` -================================================= - -.. automodule:: platypush.message.response.weather.buienradar - :members: diff --git a/docs/source/plugins.rst b/docs/source/plugins.rst index 8e9a3b785a..d4f63b1b02 100644 --- a/docs/source/plugins.rst +++ b/docs/source/plugins.rst @@ -136,7 +136,6 @@ Plugins platypush/plugins/variable.rst platypush/plugins/wallabag.rst platypush/plugins/weather.buienradar.rst - platypush/plugins/weather.darksky.rst platypush/plugins/weather.openweathermap.rst platypush/plugins/websocket.rst platypush/plugins/wiimote.rst diff --git a/docs/source/responses.rst b/docs/source/responses.rst index 584dc5893e..fcae85e46e 100644 --- a/docs/source/responses.rst +++ b/docs/source/responses.rst @@ -18,4 +18,3 @@ Responses platypush/responses/stt.rst platypush/responses/tensorflow.rst platypush/responses/translate.rst - platypush/responses/weather.buienradar.rst diff --git a/platypush/backend/http/webapp/src/assets/icons.json b/platypush/backend/http/webapp/src/assets/icons.json index f6f2544040..9ae3321d22 100644 --- a/platypush/backend/http/webapp/src/assets/icons.json +++ b/platypush/backend/http/webapp/src/assets/icons.json @@ -134,6 +134,9 @@ "variable": { "class": "fas fa-square-root-variable" }, + "weather.buienradar": { + "class": "fas fa-cloud-sun-rain" + }, "weather.openweathermap": { "class": "fas fa-cloud-sun-rain" }, diff --git a/platypush/backend/http/webapp/src/components/panels/Entities/Weather.vue b/platypush/backend/http/webapp/src/components/panels/Entities/Weather.vue index 098c0de485..c4609bbce0 100644 --- a/platypush/backend/http/webapp/src/components/panels/Entities/Weather.vue +++ b/platypush/backend/http/webapp/src/components/panels/Entities/Weather.vue @@ -19,6 +19,11 @@ class="weather-icon" v-if="value.icon" /> + + @@ -98,6 +103,15 @@ +
+
+
Rain Chance
+
+
+
+
+
+
Wind
@@ -113,7 +127,7 @@
-
Wind
+
Wind Gust
@@ -197,7 +211,7 @@ export default { }, normPrecipIntensity() { - if (this.value.precip_intensity == null) + if (!this.value.precip_intensity) return null return ( diff --git a/platypush/backend/weather/buienradar/__init__.py b/platypush/backend/weather/buienradar/__init__.py deleted file mode 100644 index 64a7dc8a97..0000000000 --- a/platypush/backend/weather/buienradar/__init__.py +++ /dev/null @@ -1,63 +0,0 @@ -import time - -from platypush.backend import Backend -from platypush.context import get_plugin -from platypush.message.event.weather import ( - NewWeatherConditionEvent, - NewPrecipitationForecastEvent, -) -from platypush.plugins.weather.buienradar import WeatherBuienradarPlugin - - -class WeatherBuienradarBackend(Backend): - """ - Buienradar weather forecast backend. Listens for new weather or precipitation updates. - - Requires: - - * The :mod:`platypush.plugins.weather.buienradar` plugin configured - - """ - - def __init__(self, poll_seconds=300, **kwargs): - super().__init__(**kwargs) - self.poll_seconds = poll_seconds - self.last_weather = None - self.last_precip = None - - def run(self): - super().run() - plugin: WeatherBuienradarPlugin = get_plugin('weather.buienradar') - self.logger.info('Initialized weather forecast backend') - - while not self.should_stop(): - weather = plugin.get_weather().output - precip = plugin.get_precipitation().output - del weather['measured'] - - if precip != self.last_precip: - self.bus.post( - NewPrecipitationForecastEvent( - plugin_name='weather.buienradar', - average=precip.get('average'), - total=precip.get('total'), - time_frame=precip.get('time_frame'), - ) - ) - - if weather != self.last_weather: - self.bus.post( - NewWeatherConditionEvent( - **{ - **weather, - 'plugin_name': 'weather.buienradar', - } - ) - ) - - self.last_weather = weather - self.last_precip = precip - time.sleep(self.poll_seconds) - - -# vim:sw=4:ts=4:et: diff --git a/platypush/backend/weather/buienradar/manifest.yaml b/platypush/backend/weather/buienradar/manifest.yaml deleted file mode 100644 index 130a4201a1..0000000000 --- a/platypush/backend/weather/buienradar/manifest.yaml +++ /dev/null @@ -1,9 +0,0 @@ -manifest: - events: - platypush.message.event.weather.NewWeatherConditionEvent: when there is a weather - condition update - install: - pip: - - buienradar - package: platypush.backend.weather.buienradar - type: backend diff --git a/platypush/entities/managers/weather.py b/platypush/entities/managers/weather.py index f973c884e2..3bab2f4765 100644 --- a/platypush/entities/managers/weather.py +++ b/platypush/entities/managers/weather.py @@ -26,12 +26,14 @@ class WeatherEntityManager(EntityManager, ABC): name='Weather', summary=weather.get('summary'), icon=weather.get('icon'), + image=weather.get('image'), precip_intensity=weather.get('precip_intensity'), precip_type=weather.get('precip_type'), temperature=weather.get('temperature'), apparent_temperature=weather.get('apparent_temperature'), humidity=weather.get('humidity'), pressure=weather.get('pressure'), + rain_chance=weather.get('rain_chance'), wind_speed=weather.get('wind_speed'), wind_direction=weather.get('wind_direction'), wind_gust=weather.get('wind_gust'), diff --git a/platypush/entities/weather.py b/platypush/entities/weather.py index 93ccd90d5d..03b3ad798f 100644 --- a/platypush/entities/weather.py +++ b/platypush/entities/weather.py @@ -14,14 +14,16 @@ class Weather(Entity): summary = Column(String) icon = Column(String) + image = Column(String) precip_intensity = Column(Float) precip_type = Column(String) temperature = Column(Float) apparent_temperature = Column(Float) humidity = Column(Float) pressure = Column(Float) + rain_chance = Column(Float) wind_speed = Column(Float) - wind_direction = Column(Float) + wind_direction = Column(String) wind_gust = Column(Float) cloud_cover = Column(Float) visibility = Column(Float) diff --git a/platypush/message/event/weather.py b/platypush/message/event/weather.py index 9dbb9a110d..5aefdb4c99 100644 --- a/platypush/message/event/weather.py +++ b/platypush/message/event/weather.py @@ -15,12 +15,14 @@ class NewWeatherConditionEvent(Event): plugin_name: str, summary: Optional[str] = None, icon: Optional[str] = None, + image: Optional[str] = None, precip_intensity: Optional[float] = None, precip_type: Optional[str] = None, temperature: Optional[float] = None, apparent_temperature: Optional[float] = None, humidity: Optional[float] = None, pressure: Optional[float] = None, + rain_chance: Optional[float] = None, wind_speed: Optional[float] = None, wind_gust: Optional[float] = None, wind_direction: Optional[float] = None, @@ -35,6 +37,7 @@ class NewWeatherConditionEvent(Event): :param plugin_name: Plugin that triggered the event. :param summary: Summary of the weather condition. :param icon: Icon representing the weather condition. + :param image: Image URL representing the weather condition. :param precip_intensity: Intensity of the precipitation. :param precip_type: Type of precipitation. :param temperature: Temperature, in the configured unit system. @@ -42,6 +45,7 @@ class NewWeatherConditionEvent(Event): unit system. :param humidity: Humidity percentage, between 0 and 100. :param pressure: Pressure, in the configured unit system. + :param rain_chance: Chance of rain, between 0 and 100. :param wind_speed: Wind speed, in the configured unit system. :param wind_gust: Wind gust, in the configured unit system. :param wind_direction: Wind direction, in degrees. @@ -56,12 +60,14 @@ class NewWeatherConditionEvent(Event): plugin_name=plugin_name, summary=summary, icon=icon, + image=image, precip_intensity=precip_intensity, precip_type=precip_type, temperature=temperature, apparent_temperature=apparent_temperature, humidity=humidity, pressure=pressure, + rain_chance=rain_chance, wind_speed=wind_speed, wind_gust=wind_gust, wind_direction=wind_direction, diff --git a/platypush/message/response/weather/__init__.py b/platypush/message/response/weather/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/platypush/message/response/weather/buienradar.py b/platypush/message/response/weather/buienradar.py deleted file mode 100644 index 711afe4d71..0000000000 --- a/platypush/message/response/weather/buienradar.py +++ /dev/null @@ -1,113 +0,0 @@ -import datetime - -from typing import List - -from platypush.message import Mapping -from platypush.message.response import Response - - -class BuienradarWeatherResponse(Response): - def __init__(self, - barometer_fc: str, - condition_name: str, - condition_name_long: str, - condition_image: str, - feel_temperature: float, - ground_temperature: float, - humidity: int, - irradiance: int, - measured: datetime.datetime, - precipitation: float, - pressure: float, - rain_last_24_hours: float, - rain_last_hour: float, - station_name: str, - temperature: float, - visibility: int, - wind_azimuth: int, - wind_direction: str, - wind_force: int, - wind_gust: float, - wind_speed: float, - *args, **kwargs): - super().__init__(*args, output={ - 'barometer_fc': barometer_fc, - 'condition_name': condition_name, - 'condition_name_long': condition_name_long, - 'condition_image': condition_image, - 'feel_temperature': feel_temperature, - 'ground_temperature': ground_temperature, - 'humidity': humidity, - 'irradiance': irradiance, - 'measured': measured, - 'precipitation': precipitation, - 'pressure': pressure, - 'rain_last_24_hours': rain_last_24_hours, - 'rain_last_hour': rain_last_hour, - 'station_name': station_name, - 'temperature': temperature, - 'visibility': visibility, - 'wind_azimuth': wind_azimuth, - 'wind_direction': wind_direction, - 'wind_force': wind_force, - 'wind_gust': wind_gust, - 'wind_speed': wind_speed, - }, **kwargs) - - -class BuienradarPrecipitationResponse(Response): - def __init__(self, - average: float, - total: float, - time_frame: int, - *args, **kwargs): - super().__init__(*args, output={ - 'average': average, - 'total': total, - 'time_frame': time_frame, - }, **kwargs) - - -class BuienradarForecast(Mapping): - def __init__(self, - condition_name: str, - condition_name_long: str, - condition_image: str, - date_time: datetime.datetime, - rain: float, - min_rain: float, - max_rain: float, - rain_chance: float, - snow: int, - temperature: float, - wind_azimuth: int, - wind_direction: str, - wind_force: int, - wind_speed: float, - *args, **kwargs): - super().__init__(*args, output={ - 'condition_name': condition_name, - 'condition_name_long': condition_name_long, - 'condition_image': condition_image, - 'date_time': date_time, - 'rain': rain, - 'min_rain': min_rain, - 'max_rain': max_rain, - 'rain_chance': rain_chance, - 'snow': snow, - 'temperature': temperature, - 'wind_azimuth': wind_azimuth, - 'wind_direction': wind_direction, - 'wind_force': wind_force, - 'wind_speed': wind_speed, - }, **kwargs) - - -class BuienradarForecastResponse(Response): - def __init__(self, - forecast=List[BuienradarForecast], - *args, **kwargs): - super().__init__(*args, output=forecast, **kwargs) - - -# vim:sw=4:ts=4:et: diff --git a/platypush/plugins/weather/buienradar/__init__.py b/platypush/plugins/weather/buienradar/__init__.py index 11fb41971f..e9100a8057 100644 --- a/platypush/plugins/weather/buienradar/__init__.py +++ b/platypush/plugins/weather/buienradar/__init__.py @@ -1,24 +1,25 @@ from typing import Optional, Dict, Any -from platypush.plugins import Plugin, action -from platypush.message.response.weather.buienradar import ( - BuienradarWeatherResponse, - BuienradarPrecipitationResponse, - BuienradarForecastResponse, - BuienradarForecast, +from platypush.plugins import action +from platypush.plugins.weather import WeatherPlugin +from platypush.schemas.weather.buienradar import ( + WeatherSchema, + PrecipitationForecastSchema, ) -class WeatherBuienradarPlugin(Plugin): +class WeatherBuienradarPlugin(WeatherPlugin): # pylint: disable=too-many-ancestors """ - Plugin for getting weather updates through Buienradar - a Dutch weather app. + Plugin for getting weather updates through Buienradar - a Dutch weather + app. """ def __init__(self, lat: float, long: float, time_frame: int = 120, **kwargs): """ - :param lat: Default latitude - :param long: Default longitude - :param time_frame: Default number of minutes to look ahead for precipitation forecast + :param lat: Default latitude. + :param long: Default longitude. + :param time_frame: Default number of minutes to look ahead for + precipitation forecast. """ super().__init__(**kwargs) self.lat = lat @@ -32,11 +33,8 @@ class WeatherBuienradarPlugin(Plugin): long: Optional[float] = None, time_frame: Optional[int] = None, ) -> Dict[str, Any]: - # noinspection PyPackageRequirements - from buienradar.buienradar import get_data, parse_data - - # noinspection PyPackageRequirements - from buienradar.constants import SUCCESS, CONTENT, RAINCONTENT, DATA + from buienradar.buienradar import get_data, parse_data # type: ignore + from buienradar.constants import SUCCESS, CONTENT, RAINCONTENT, DATA # type: ignore lat = lat or self.lat long = long or self.long @@ -51,74 +49,33 @@ class WeatherBuienradarPlugin(Plugin): result = parse_data(data, rain_data, lat, long, time_frame) return result.get(DATA, {}) - @action - def get_weather( - self, lat: Optional[float] = None, long: Optional[float] = None - ) -> BuienradarWeatherResponse: + def _get_current_weather( + self, *_, lat: Optional[float] = None, long: Optional[float] = None, **__ + ): """ Get the current weather conditions. :param lat: Weather latitude (default: configured latitude) :param long: Weather longitude (default: configured longitude) + :return: .. schema:: schemas.weather.buienradar.WeatherSchema """ - data = self.get_data(lat, long, 60) - - return BuienradarWeatherResponse( - barometer_fc=data.get('barometerfcname'), - condition_name=data.get('condition', {}).get('condition'), - condition_name_long=data.get('condition', {}).get('exact'), - condition_image=data.get('condition', {}).get('image'), - feel_temperature=data.get('feeltemperature'), - ground_temperature=data.get('groundtemperature'), - humidity=data.get('humidity'), - irradiance=data.get('irradiance'), - measured=data.get('measured'), - precipitation=data.get('precipitation'), - pressure=data.get('pressure'), - rain_last_24_hours=data.get('rainlast24hour'), - rain_last_hour=data.get('rainlasthour'), - station_name=data.get('stationname'), - temperature=data.get('temperature'), - visibility=data.get('visibility'), - wind_azimuth=data.get('windazimuth'), - wind_direction=data.get('wind_irection'), - wind_force=data.get('windforce'), - wind_gust=data.get('windgust'), - wind_speed=data.get('windspeed'), - ) + return WeatherSchema().dump(self.get_data(lat, long, 60)) @action - def get_forecast( - self, lat: Optional[float] = None, long: Optional[float] = None - ) -> BuienradarForecastResponse: + def get_forecast(self, lat: Optional[float] = None, long: Optional[float] = None): """ Get the weather forecast for the next days. :param lat: Weather latitude (default: configured latitude) :param long: Weather longitude (default: configured longitude) """ - data = self.get_data(lat, long, 60).get('forecast', []) - return BuienradarForecastResponse( - [ - BuienradarForecast( - condition_name=d.get('condition', {}).get('condition'), - condition_name_long=d.get('condition', {}).get('exact'), - condition_image=d.get('condition', {}).get('image'), - date_time=d.get('datetime'), - rain=d.get('rain'), - min_rain=d.get('minrain'), - max_rain=d.get('maxrain'), - rain_chance=d.get('rainchance'), - snow=d.get('snow'), - temperature=d.get('temperature'), - wind_azimuth=d.get('windazimuth'), - wind_direction=d.get('winddirection'), - wind_force=d.get('windforce'), - wind_speed=d.get('windspeed'), - ) - for d in data - ] - ) + return [ + { + 'datetime': weather.get('datetime'), + **dict(WeatherSchema().dump(weather)), + } + for weather in self.get_data(lat, long, 60).get('forecast', []) + ] @action def get_precipitation( @@ -126,7 +83,7 @@ class WeatherBuienradarPlugin(Plugin): lat: Optional[float] = None, long: Optional[float] = None, time_frame: Optional[int] = None, - ) -> BuienradarPrecipitationResponse: + ): """ Get the precipitation forecast for the specified time frame. @@ -134,11 +91,8 @@ class WeatherBuienradarPlugin(Plugin): :param long: Weather longitude (default: configured longitude) :param time_frame: Time frame for the forecast in minutes (default: configured time_frame) """ - data = self.get_data(lat, long, time_frame).get('precipitation_forecast', {}) - return BuienradarPrecipitationResponse( - average=data.get('average'), - total=data.get('total'), - time_frame=data.get('timeframe'), + return PrecipitationForecastSchema().dump( + self.get_data(lat, long, time_frame).get('precipitation_forecast', {}) ) diff --git a/platypush/plugins/weather/buienradar/manifest.yaml b/platypush/plugins/weather/buienradar/manifest.yaml index ab494de4f7..23dc8ffb9e 100644 --- a/platypush/plugins/weather/buienradar/manifest.yaml +++ b/platypush/plugins/weather/buienradar/manifest.yaml @@ -1,5 +1,6 @@ manifest: - events: {} + events: + - platypush.message.event.weather.NewWeatherConditionEvent install: pip: - buienradar diff --git a/platypush/schemas/weather/buienradar.py b/platypush/schemas/weather/buienradar.py new file mode 100644 index 0000000000..20992a2f60 --- /dev/null +++ b/platypush/schemas/weather/buienradar.py @@ -0,0 +1,157 @@ +from marshmallow import EXCLUDE, fields +from marshmallow.schema import Schema + + +class WeatherSchema(Schema): + """ + Schema for weather data. + """ + + class Meta: # pylint: disable=too-few-public-methods + """ + Weather schema metadata. + """ + + unknown = EXCLUDE + + summary = fields.Function( + lambda obj: obj.get('condition', {}).get('exact', 'Unknown'), + metadata={ + 'description': 'Summary of the weather condition', + 'example': 'Cloudy', + }, + ) + + image = fields.Function( + lambda obj: obj.get('condition', {}).get('image'), + metadata={ + 'description': 'Image URL representing the weather condition', + 'example': 'https://www.buienradar.nl/resources/images/icons/weather/30x30/cc.png', + }, + ) + + precip_intensity = fields.Float( + attribute='rainlasthour', + metadata={ + 'description': 'Amount of precipitation in the last hour in mm/h', + 'example': 0.0, + }, + ) + + precip_type = fields.Function( + lambda obj: 'snow' if obj.get('snow') else 'rain', + metadata={ + 'description': 'Type of precipitation', + 'example': 'rain', + }, + ) + + temperature = fields.Float( + metadata={ + 'description': 'Temperature in Celsius', + 'example': 10.0, + }, + ) + + apparent_temperature = fields.Float( + attribute='feeltemperature', + metadata={ + 'description': 'Apparent temperature in Celsius', + 'example': 9.0, + }, + ) + + humidity = fields.Float( + metadata={ + 'description': 'Humidity percentage, between 0 and 100', + 'example': 30, + }, + ) + + pressure = fields.Float( + metadata={ + 'description': 'Pressure in hPa', + 'example': 1000.0, + }, + ) + + rain_chance = fields.Float( + attribute='rainchance', + metadata={ + 'description': 'Chance of rain in percentage, between 0 and 100', + 'example': 30, + }, + ) + + wind_speed = fields.Float( + attribute='windspeed', + metadata={ + 'description': 'Wind speed in the configured unit of measure', + 'example': 10.0, + }, + ) + + wind_direction = fields.Float( + attribute='windazimuth', + metadata={ + 'description': 'Wind direction in degrees', + 'example': 180, + }, + ) + + wind_gust = fields.Float( + attribute='windgust', + metadata={ + 'description': 'Wind gust in the configured unit of measure', + 'example': 15.0, + }, + ) + + visibility = fields.Float( + metadata={ + 'description': 'Visibility in meters', + 'example': 2000.0, + }, + ) + + units = fields.Constant( + 'metric', + metadata={ + 'description': 'Unit of measure', + 'example': 'metric', + }, + ) + + +class PrecipitationForecastSchema(Schema): + """ + Schema for precipitation forecast data. + """ + + class Meta: # pylint: disable=too-few-public-methods + """ + Precipitation forecast schema metadata. + """ + + unknown = EXCLUDE + + timeframe = fields.Integer( + metadata={ + 'description': 'Time frame in minutes', + 'example': 60, + }, + ) + + total = fields.Float( + metadata={ + 'description': 'Total precipitation in mm/h', + 'example': 0.5, + }, + ) + + average = fields.Float( + metadata={ + 'description': 'Average precipitation in mm/h', + 'example': 0.25, + }, + ) diff --git a/platypush/utils/mock/modules.py b/platypush/utils/mock/modules.py index 68b54e5f21..1a19217005 100644 --- a/platypush/utils/mock/modules.py +++ b/platypush/utils/mock/modules.py @@ -19,6 +19,7 @@ mock_imports = [ "bleak", "bluetooth", "bluetooth_numbers", + "buienradar", "cpuinfo", "croniter", "cups",