@@ -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",