[#308] Finalized weather integrations migration.

- Migrated `weather.buienradar` integration.
- Removed last leftovers of the previous weather backends and responses.

Closes: #308
This commit is contained in:
Fabio Manganiello 2023-11-20 03:08:53 +01:00
parent 2dac98c56e
commit a0d8c522a2
Signed by untrusted user: blacklight
GPG key ID: D90FBA7F76362774
21 changed files with 220 additions and 295 deletions

View file

@ -38,7 +38,4 @@ Backends
platypush/backend/stt.picovoice.hotword.rst platypush/backend/stt.picovoice.hotword.rst
platypush/backend/stt.picovoice.speech.rst platypush/backend/stt.picovoice.speech.rst
platypush/backend/tcp.rst platypush/backend/tcp.rst
platypush/backend/weather.buienradar.rst
platypush/backend/weather.darksky.rst
platypush/backend/weather.openweathermap.rst
platypush/backend/wiimote.rst platypush/backend/wiimote.rst

View file

@ -1,5 +0,0 @@
``weather.buienradar``
========================================
.. automodule:: platypush.backend.weather.buienradar
:members:

View file

@ -1,5 +0,0 @@
``weather.darksky``
=====================================
.. automodule:: platypush.backend.weather.darksky
:members:

View file

@ -1,5 +0,0 @@
``weather.openweathermap``
============================================
.. automodule:: platypush.backend.weather.openweathermap
:members:

View file

@ -1,5 +0,0 @@
``weather.darksky``
=====================================
.. automodule:: platypush.plugins.weather.darksky
:members:

View file

@ -1,5 +0,0 @@
``weather.buienradar``
=================================================
.. automodule:: platypush.message.response.weather.buienradar
:members:

View file

@ -136,7 +136,6 @@ Plugins
platypush/plugins/variable.rst platypush/plugins/variable.rst
platypush/plugins/wallabag.rst platypush/plugins/wallabag.rst
platypush/plugins/weather.buienradar.rst platypush/plugins/weather.buienradar.rst
platypush/plugins/weather.darksky.rst
platypush/plugins/weather.openweathermap.rst platypush/plugins/weather.openweathermap.rst
platypush/plugins/websocket.rst platypush/plugins/websocket.rst
platypush/plugins/wiimote.rst platypush/plugins/wiimote.rst

View file

@ -18,4 +18,3 @@ Responses
platypush/responses/stt.rst platypush/responses/stt.rst
platypush/responses/tensorflow.rst platypush/responses/tensorflow.rst
platypush/responses/translate.rst platypush/responses/translate.rst
platypush/responses/weather.buienradar.rst

View file

@ -134,6 +134,9 @@
"variable": { "variable": {
"class": "fas fa-square-root-variable" "class": "fas fa-square-root-variable"
}, },
"weather.buienradar": {
"class": "fas fa-cloud-sun-rain"
},
"weather.openweathermap": { "weather.openweathermap": {
"class": "fas fa-cloud-sun-rain" "class": "fas fa-cloud-sun-rain"
}, },

View file

@ -19,6 +19,11 @@
class="weather-icon" class="weather-icon"
v-if="value.icon" /> v-if="value.icon" />
<img :src="value.image"
:alt="value?.summary"
class="weather-icon"
v-else-if="value.image" />
<span class="temperature" <span class="temperature"
v-text="normTemperature" v-text="normTemperature"
v-if="normTemperature != null" /> v-if="normTemperature != null" />
@ -98,6 +103,15 @@
</div> </div>
</div> </div>
<div class="child" v-if="value.rain_chance != null">
<div class="col-s-12 col-m-6 label">
<div class="name">Rain Chance</div>
</div>
<div class="value">
<div class="name" v-text="normPercentage(value.rain_chance)" />
</div>
</div>
<div class="child" v-if="value.wind_speed != null"> <div class="child" v-if="value.wind_speed != null">
<div class="col-s-12 col-m-6 label"> <div class="col-s-12 col-m-6 label">
<div class="name">Wind</div> <div class="name">Wind</div>
@ -113,7 +127,7 @@
<div class="child" v-if="value.wind_gust != null"> <div class="child" v-if="value.wind_gust != null">
<div class="col-s-12 col-m-6 label"> <div class="col-s-12 col-m-6 label">
<div class="name">Wind</div> <div class="name">Wind Gust</div>
</div> </div>
<div class="value"> <div class="value">
<div class="name"> <div class="name">
@ -197,7 +211,7 @@ export default {
}, },
normPrecipIntensity() { normPrecipIntensity() {
if (this.value.precip_intensity == null) if (!this.value.precip_intensity)
return null return null
return ( return (

View file

@ -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:

View file

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

View file

@ -26,12 +26,14 @@ class WeatherEntityManager(EntityManager, ABC):
name='Weather', name='Weather',
summary=weather.get('summary'), summary=weather.get('summary'),
icon=weather.get('icon'), icon=weather.get('icon'),
image=weather.get('image'),
precip_intensity=weather.get('precip_intensity'), precip_intensity=weather.get('precip_intensity'),
precip_type=weather.get('precip_type'), precip_type=weather.get('precip_type'),
temperature=weather.get('temperature'), temperature=weather.get('temperature'),
apparent_temperature=weather.get('apparent_temperature'), apparent_temperature=weather.get('apparent_temperature'),
humidity=weather.get('humidity'), humidity=weather.get('humidity'),
pressure=weather.get('pressure'), pressure=weather.get('pressure'),
rain_chance=weather.get('rain_chance'),
wind_speed=weather.get('wind_speed'), wind_speed=weather.get('wind_speed'),
wind_direction=weather.get('wind_direction'), wind_direction=weather.get('wind_direction'),
wind_gust=weather.get('wind_gust'), wind_gust=weather.get('wind_gust'),

View file

@ -14,14 +14,16 @@ class Weather(Entity):
summary = Column(String) summary = Column(String)
icon = Column(String) icon = Column(String)
image = Column(String)
precip_intensity = Column(Float) precip_intensity = Column(Float)
precip_type = Column(String) precip_type = Column(String)
temperature = Column(Float) temperature = Column(Float)
apparent_temperature = Column(Float) apparent_temperature = Column(Float)
humidity = Column(Float) humidity = Column(Float)
pressure = Column(Float) pressure = Column(Float)
rain_chance = Column(Float)
wind_speed = Column(Float) wind_speed = Column(Float)
wind_direction = Column(Float) wind_direction = Column(String)
wind_gust = Column(Float) wind_gust = Column(Float)
cloud_cover = Column(Float) cloud_cover = Column(Float)
visibility = Column(Float) visibility = Column(Float)

View file

@ -15,12 +15,14 @@ class NewWeatherConditionEvent(Event):
plugin_name: str, plugin_name: str,
summary: Optional[str] = None, summary: Optional[str] = None,
icon: Optional[str] = None, icon: Optional[str] = None,
image: Optional[str] = None,
precip_intensity: Optional[float] = None, precip_intensity: Optional[float] = None,
precip_type: Optional[str] = None, precip_type: Optional[str] = None,
temperature: Optional[float] = None, temperature: Optional[float] = None,
apparent_temperature: Optional[float] = None, apparent_temperature: Optional[float] = None,
humidity: Optional[float] = None, humidity: Optional[float] = None,
pressure: Optional[float] = None, pressure: Optional[float] = None,
rain_chance: Optional[float] = None,
wind_speed: Optional[float] = None, wind_speed: Optional[float] = None,
wind_gust: Optional[float] = None, wind_gust: Optional[float] = None,
wind_direction: Optional[float] = None, wind_direction: Optional[float] = None,
@ -35,6 +37,7 @@ class NewWeatherConditionEvent(Event):
:param plugin_name: Plugin that triggered the event. :param plugin_name: Plugin that triggered the event.
:param summary: Summary of the weather condition. :param summary: Summary of the weather condition.
:param icon: Icon representing 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_intensity: Intensity of the precipitation.
:param precip_type: Type of precipitation. :param precip_type: Type of precipitation.
:param temperature: Temperature, in the configured unit system. :param temperature: Temperature, in the configured unit system.
@ -42,6 +45,7 @@ class NewWeatherConditionEvent(Event):
unit system. unit system.
:param humidity: Humidity percentage, between 0 and 100. :param humidity: Humidity percentage, between 0 and 100.
:param pressure: Pressure, in the configured unit system. :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_speed: Wind speed, in the configured unit system.
:param wind_gust: Wind gust, in the configured unit system. :param wind_gust: Wind gust, in the configured unit system.
:param wind_direction: Wind direction, in degrees. :param wind_direction: Wind direction, in degrees.
@ -56,12 +60,14 @@ class NewWeatherConditionEvent(Event):
plugin_name=plugin_name, plugin_name=plugin_name,
summary=summary, summary=summary,
icon=icon, icon=icon,
image=image,
precip_intensity=precip_intensity, precip_intensity=precip_intensity,
precip_type=precip_type, precip_type=precip_type,
temperature=temperature, temperature=temperature,
apparent_temperature=apparent_temperature, apparent_temperature=apparent_temperature,
humidity=humidity, humidity=humidity,
pressure=pressure, pressure=pressure,
rain_chance=rain_chance,
wind_speed=wind_speed, wind_speed=wind_speed,
wind_gust=wind_gust, wind_gust=wind_gust,
wind_direction=wind_direction, wind_direction=wind_direction,

View file

@ -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:

View file

@ -1,24 +1,25 @@
from typing import Optional, Dict, Any from typing import Optional, Dict, Any
from platypush.plugins import Plugin, action from platypush.plugins import action
from platypush.message.response.weather.buienradar import ( from platypush.plugins.weather import WeatherPlugin
BuienradarWeatherResponse, from platypush.schemas.weather.buienradar import (
BuienradarPrecipitationResponse, WeatherSchema,
BuienradarForecastResponse, PrecipitationForecastSchema,
BuienradarForecast,
) )
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): def __init__(self, lat: float, long: float, time_frame: int = 120, **kwargs):
""" """
:param lat: Default latitude :param lat: Default latitude.
:param long: Default longitude :param long: Default longitude.
:param time_frame: Default number of minutes to look ahead for precipitation forecast :param time_frame: Default number of minutes to look ahead for
precipitation forecast.
""" """
super().__init__(**kwargs) super().__init__(**kwargs)
self.lat = lat self.lat = lat
@ -32,11 +33,8 @@ class WeatherBuienradarPlugin(Plugin):
long: Optional[float] = None, long: Optional[float] = None,
time_frame: Optional[int] = None, time_frame: Optional[int] = None,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
# noinspection PyPackageRequirements from buienradar.buienradar import get_data, parse_data # type: ignore
from buienradar.buienradar import get_data, parse_data from buienradar.constants import SUCCESS, CONTENT, RAINCONTENT, DATA # type: ignore
# noinspection PyPackageRequirements
from buienradar.constants import SUCCESS, CONTENT, RAINCONTENT, DATA
lat = lat or self.lat lat = lat or self.lat
long = long or self.long long = long or self.long
@ -51,74 +49,33 @@ class WeatherBuienradarPlugin(Plugin):
result = parse_data(data, rain_data, lat, long, time_frame) result = parse_data(data, rain_data, lat, long, time_frame)
return result.get(DATA, {}) return result.get(DATA, {})
@action def _get_current_weather(
def get_weather( self, *_, lat: Optional[float] = None, long: Optional[float] = None, **__
self, lat: Optional[float] = None, long: Optional[float] = None ):
) -> BuienradarWeatherResponse:
""" """
Get the current weather conditions. Get the current weather conditions.
:param lat: Weather latitude (default: configured latitude) :param lat: Weather latitude (default: configured latitude)
:param long: Weather longitude (default: configured longitude) :param long: Weather longitude (default: configured longitude)
:return: .. schema:: schemas.weather.buienradar.WeatherSchema
""" """
data = self.get_data(lat, long, 60) return WeatherSchema().dump(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'),
)
@action @action
def get_forecast( def get_forecast(self, lat: Optional[float] = None, long: Optional[float] = None):
self, lat: Optional[float] = None, long: Optional[float] = None
) -> BuienradarForecastResponse:
""" """
Get the weather forecast for the next days. Get the weather forecast for the next days.
:param lat: Weather latitude (default: configured latitude) :param lat: Weather latitude (default: configured latitude)
:param long: Weather longitude (default: configured longitude) :param long: Weather longitude (default: configured longitude)
""" """
data = self.get_data(lat, long, 60).get('forecast', []) return [
return BuienradarForecastResponse( {
[ 'datetime': weather.get('datetime'),
BuienradarForecast( **dict(WeatherSchema().dump(weather)),
condition_name=d.get('condition', {}).get('condition'), }
condition_name_long=d.get('condition', {}).get('exact'), for weather in self.get_data(lat, long, 60).get('forecast', [])
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
]
)
@action @action
def get_precipitation( def get_precipitation(
@ -126,7 +83,7 @@ class WeatherBuienradarPlugin(Plugin):
lat: Optional[float] = None, lat: Optional[float] = None,
long: Optional[float] = None, long: Optional[float] = None,
time_frame: Optional[int] = None, time_frame: Optional[int] = None,
) -> BuienradarPrecipitationResponse: ):
""" """
Get the precipitation forecast for the specified time frame. Get the precipitation forecast for the specified time frame.
@ -134,11 +91,8 @@ class WeatherBuienradarPlugin(Plugin):
:param long: Weather longitude (default: configured longitude) :param long: Weather longitude (default: configured longitude)
:param time_frame: Time frame for the forecast in minutes (default: configured time_frame) :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 PrecipitationForecastSchema().dump(
return BuienradarPrecipitationResponse( self.get_data(lat, long, time_frame).get('precipitation_forecast', {})
average=data.get('average'),
total=data.get('total'),
time_frame=data.get('timeframe'),
) )

View file

@ -1,5 +1,6 @@
manifest: manifest:
events: {} events:
- platypush.message.event.weather.NewWeatherConditionEvent
install: install:
pip: pip:
- buienradar - buienradar

View file

@ -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,
},
)

View file

@ -19,6 +19,7 @@ mock_imports = [
"bleak", "bleak",
"bluetooth", "bluetooth",
"bluetooth_numbers", "bluetooth_numbers",
"buienradar",
"cpuinfo", "cpuinfo",
"croniter", "croniter",
"cups", "cups",