forked from platypush/platypush
Implemented support for weather entities.
This commit is contained in:
parent
bf8f31545a
commit
b8a4b9e4c5
9 changed files with 430 additions and 12 deletions
|
@ -134,6 +134,9 @@
|
||||||
"variable": {
|
"variable": {
|
||||||
"class": "fas fa-square-root-variable"
|
"class": "fas fa-square-root-variable"
|
||||||
},
|
},
|
||||||
|
"weather.openweathermap": {
|
||||||
|
"class": "fas fa-cloud-sun-rain"
|
||||||
|
},
|
||||||
"zigbee.mqtt": {
|
"zigbee.mqtt": {
|
||||||
"imgUrl": "/icons/zigbee.svg"
|
"imgUrl": "/icons/zigbee.svg"
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,277 @@
|
||||||
|
<template>
|
||||||
|
<div class="entity weather-container">
|
||||||
|
<div class="head">
|
||||||
|
<div class="col-1 icon">
|
||||||
|
<EntityIcon
|
||||||
|
:entity="value"
|
||||||
|
:loading="loading"
|
||||||
|
:error="error" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-5 name">
|
||||||
|
<div class="name" v-text="value.name" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-5 current-weather" @click.stop="isCollapsed = !isCollapsed">
|
||||||
|
<div class="weather-summary">
|
||||||
|
<img :src="`/icons/openweathermap/dark/${value.icon}.png`"
|
||||||
|
:alt="value?.summary"
|
||||||
|
class="weather-icon"
|
||||||
|
v-if="value.icon" />
|
||||||
|
|
||||||
|
<span class="temperature"
|
||||||
|
v-text="normTemperature"
|
||||||
|
v-if="normTemperature != null" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-1 collapse-toggler" @click.stop="isCollapsed = !isCollapsed">
|
||||||
|
<i class="fas"
|
||||||
|
:class="{'fa-chevron-down': isCollapsed, 'fa-chevron-up': !isCollapsed}" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="body children attributes fade-in" v-if="!isCollapsed">
|
||||||
|
<div class="child" v-if="value.summary">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Summary</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name" v-text="value.summary" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="value.temperature">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Temperature</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name" v-text="normTemperature" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="normApparentTemperature">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Feels Like</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name" v-text="normApparentTemperature" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="value.humidity">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Humidity</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name" v-text="normPercentage(value.humidity)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="normPrecipIntensity && precipIconClass">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Precipitation</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name">
|
||||||
|
<i :class="precipIconClass" />
|
||||||
|
<span v-text="normPrecipIntensity" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="value.cloud_cover">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Cloud Cover</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name" v-text="normPercentage(value.cloud_cover)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="normPressure">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Pressure</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name" v-text="normPressure" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="value.wind_speed != null">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Wind</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name">
|
||||||
|
<span v-text="value.wind_speed" />
|
||||||
|
<span v-if="isMetric">m/s</span>
|
||||||
|
<span v-else>mph</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="value.wind_gust != null">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Wind</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name">
|
||||||
|
<span v-text="value.wind_gust" />
|
||||||
|
<span v-if="isMetric">m/s</span>
|
||||||
|
<span v-else>mph</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="value.wind_direction != null">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Wind Direction</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<span class="name" v-text="value.wind_direction" />°
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="value.visibility != null">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Visibility</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name">
|
||||||
|
<span v-text="value.visibility" />
|
||||||
|
<span v-if="isMetric">m</span>
|
||||||
|
<span v-else>mi</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="value.sunrise != null">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Sunrise</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name" v-text="formatDateTime(value.sunrise)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="child" v-if="value.sunset != null">
|
||||||
|
<div class="col-s-12 col-m-6 label">
|
||||||
|
<div class="name">Sunset</div>
|
||||||
|
</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="name" v-text="formatDateTime(value.sunset)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import EntityIcon from "./EntityIcon"
|
||||||
|
import EntityMixin from "./EntityMixin"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {EntityIcon},
|
||||||
|
mixins: [EntityMixin],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isCollapsed: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
normTemperature() {
|
||||||
|
if (this.value.temperature == null)
|
||||||
|
return null
|
||||||
|
|
||||||
|
return Math.round(this.value.temperature).toFixed(1) + "°"
|
||||||
|
},
|
||||||
|
|
||||||
|
normApparentTemperature() {
|
||||||
|
if (this.value.apparent_temperature == null)
|
||||||
|
return null
|
||||||
|
|
||||||
|
return Math.round(this.value.apparent_temperature).toFixed(1) + "°"
|
||||||
|
},
|
||||||
|
|
||||||
|
normPrecipIntensity() {
|
||||||
|
if (this.value.precip_intensity == null)
|
||||||
|
return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
Math.round(this.value.precip_intensity).toFixed(1) +
|
||||||
|
(this.isMetric ? "mm" : "in") + "/h"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
|
normPressure() {
|
||||||
|
if (this.value.pressure == null)
|
||||||
|
return null
|
||||||
|
|
||||||
|
return Math.round(this.value.pressure) + "hPa"
|
||||||
|
},
|
||||||
|
|
||||||
|
precipIconClass() {
|
||||||
|
if (this.value.precip_type == null)
|
||||||
|
return null
|
||||||
|
|
||||||
|
switch (this.value.precip_type.toLowerCase()) {
|
||||||
|
case "rain":
|
||||||
|
return "fas fa-cloud-rain"
|
||||||
|
case "snow":
|
||||||
|
return "fas fa-snowflake"
|
||||||
|
case "sleet":
|
||||||
|
return "fa-cloud-meatball"
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
isMetric() {
|
||||||
|
return this.value.units === "metric"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
normPercentage(value) {
|
||||||
|
if (value == null)
|
||||||
|
return null
|
||||||
|
|
||||||
|
return Math.round(value) + "%"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "common";
|
||||||
|
|
||||||
|
.weather-container {
|
||||||
|
.current-weather {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 1.25em;
|
||||||
|
|
||||||
|
.weather-summary {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
flex-grow: 1;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weather-icon {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.temperature {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -15,6 +15,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"weather": {
|
||||||
|
"name": "Weather",
|
||||||
|
"name_plural": "Weather",
|
||||||
|
"icon": {
|
||||||
|
"class": "fas fa-cloud-sun-rain"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"button": {
|
"button": {
|
||||||
"name": "Button",
|
"name": "Button",
|
||||||
"name_plural": "Buttons",
|
"name_plural": "Buttons",
|
||||||
|
|
49
platypush/entities/managers/weather.py
Normal file
49
platypush/entities/managers/weather.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from platypush.utils import get_plugin_name_by_class
|
||||||
|
|
||||||
|
from . import EntityManager
|
||||||
|
|
||||||
|
|
||||||
|
class WeatherEntityManager(EntityManager, ABC):
|
||||||
|
"""
|
||||||
|
Base class for integrations that support weather reports.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def transform_entities(self, entities: List[dict]):
|
||||||
|
from platypush.entities.weather import Weather
|
||||||
|
|
||||||
|
if not entities:
|
||||||
|
return []
|
||||||
|
|
||||||
|
weather = entities[0]
|
||||||
|
plugin = get_plugin_name_by_class(self.__class__)
|
||||||
|
return super().transform_entities(
|
||||||
|
[
|
||||||
|
Weather(
|
||||||
|
id=plugin,
|
||||||
|
name='Weather',
|
||||||
|
summary=weather.get('summary'),
|
||||||
|
icon=weather.get('icon'),
|
||||||
|
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'),
|
||||||
|
wind_speed=weather.get('wind_speed'),
|
||||||
|
wind_direction=weather.get('wind_direction'),
|
||||||
|
wind_gust=weather.get('wind_gust'),
|
||||||
|
cloud_cover=weather.get('cloud_cover'),
|
||||||
|
visibility=weather.get('visibility'),
|
||||||
|
sunrise=weather.get('sunrise'),
|
||||||
|
sunset=weather.get('sunset'),
|
||||||
|
units=weather.get('units'),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def status(self, *_, **__):
|
||||||
|
raise NotImplementedError
|
35
platypush/entities/weather.py
Normal file
35
platypush/entities/weather.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
from sqlalchemy import Column, DateTime, Float, Integer, ForeignKey, String
|
||||||
|
|
||||||
|
from . import Entity
|
||||||
|
|
||||||
|
|
||||||
|
class Weather(Entity):
|
||||||
|
"""
|
||||||
|
Weather entity.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = 'weather'
|
||||||
|
|
||||||
|
id = Column(Integer, ForeignKey(Entity.id, ondelete='CASCADE'), primary_key=True)
|
||||||
|
|
||||||
|
summary = Column(String)
|
||||||
|
icon = Column(String)
|
||||||
|
precip_intensity = Column(Float)
|
||||||
|
precip_type = Column(String)
|
||||||
|
temperature = Column(Float)
|
||||||
|
apparent_temperature = Column(Float)
|
||||||
|
humidity = Column(Float)
|
||||||
|
pressure = Column(Float)
|
||||||
|
wind_speed = Column(Float)
|
||||||
|
wind_direction = Column(Float)
|
||||||
|
wind_gust = Column(Float)
|
||||||
|
cloud_cover = Column(Float)
|
||||||
|
visibility = Column(Float)
|
||||||
|
sunrise = Column(DateTime)
|
||||||
|
sunset = Column(DateTime)
|
||||||
|
units = Column(String)
|
||||||
|
|
||||||
|
__table_args__ = {'extend_existing': True}
|
||||||
|
__mapper_args__ = {
|
||||||
|
'polymorphic_identity': __tablename__,
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ class NewWeatherConditionEvent(Event):
|
||||||
visibility: Optional[float] = None,
|
visibility: Optional[float] = None,
|
||||||
sunrise: Optional[datetime] = None,
|
sunrise: Optional[datetime] = None,
|
||||||
sunset: Optional[datetime] = None,
|
sunset: Optional[datetime] = None,
|
||||||
|
units: str = 'metric',
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
|
@ -48,6 +49,7 @@ class NewWeatherConditionEvent(Event):
|
||||||
:param visibility: Visibility, in meters.
|
:param visibility: Visibility, in meters.
|
||||||
:param sunrise: Sunrise time.
|
:param sunrise: Sunrise time.
|
||||||
:param sunset: Sunset time.
|
:param sunset: Sunset time.
|
||||||
|
:param units: Unit system (default: metric).
|
||||||
"""
|
"""
|
||||||
super().__init__(
|
super().__init__(
|
||||||
*args,
|
*args,
|
||||||
|
@ -67,6 +69,7 @@ class NewWeatherConditionEvent(Event):
|
||||||
visibility=visibility,
|
visibility=visibility,
|
||||||
sunrise=sunrise,
|
sunrise=sunrise,
|
||||||
sunset=sunset,
|
sunset=sunset,
|
||||||
|
units=units,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
from platypush.entities.managers.weather import WeatherEntityManager
|
||||||
|
|
||||||
from platypush.message.event.weather import NewWeatherConditionEvent
|
from platypush.message.event.weather import NewWeatherConditionEvent
|
||||||
from platypush.plugins import RunnablePlugin, action
|
from platypush.plugins import RunnablePlugin, action
|
||||||
from platypush.utils import get_plugin_name_by_class
|
from platypush.utils import get_plugin_name_by_class
|
||||||
|
|
||||||
|
|
||||||
class WeatherPlugin(RunnablePlugin, ABC):
|
class WeatherPlugin(RunnablePlugin, WeatherEntityManager, ABC):
|
||||||
"""
|
"""
|
||||||
Base class for weather plugins.
|
Base class for weather plugins.
|
||||||
"""
|
"""
|
||||||
|
@ -15,15 +16,39 @@ class WeatherPlugin(RunnablePlugin, ABC):
|
||||||
super().__init__(poll_interval=poll_interval, **kwargs)
|
super().__init__(poll_interval=poll_interval, **kwargs)
|
||||||
self._latest_weather = None
|
self._latest_weather = None
|
||||||
|
|
||||||
|
def _on_weather_data(self, weather: dict, always_publish: bool = False):
|
||||||
|
if weather != self._latest_weather or always_publish:
|
||||||
|
self._bus.post(
|
||||||
|
NewWeatherConditionEvent(
|
||||||
|
plugin_name=get_plugin_name_by_class(self.__class__), **weather
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.publish_entities([weather])
|
||||||
|
|
||||||
|
self._latest_weather = weather
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
def get_current_weather(self, *args, **kwargs) -> dict:
|
||||||
|
weather = self._get_current_weather(*args, **kwargs)
|
||||||
|
self._on_weather_data(weather, always_publish=True)
|
||||||
|
return weather
|
||||||
|
|
||||||
|
@action
|
||||||
|
def status(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Alias for :meth:`get_current_weather`.
|
||||||
|
"""
|
||||||
|
return self.get_current_weather(*args, **kwargs)
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_current_weather(self, *args, **kwargs):
|
def _get_current_weather(self, *args, **kwargs) -> dict:
|
||||||
raise NotImplementedError("get_current_weather not implemented")
|
raise NotImplementedError("_get_current_weather not implemented")
|
||||||
|
|
||||||
def main(self):
|
def main(self):
|
||||||
while not self.should_stop():
|
while not self.should_stop():
|
||||||
try:
|
try:
|
||||||
current_weather = dict(self.get_current_weather().output or {}) # type: ignore
|
current_weather = self._get_current_weather() or {}
|
||||||
current_weather.pop("time", None)
|
current_weather.pop("time", None)
|
||||||
|
|
||||||
if current_weather != self._latest_weather:
|
if current_weather != self._latest_weather:
|
||||||
|
|
|
@ -2,12 +2,11 @@ from typing import Optional
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from platypush.plugins import action
|
|
||||||
from platypush.plugins.weather import WeatherPlugin
|
from platypush.plugins.weather import WeatherPlugin
|
||||||
from platypush.schemas.weather.openweathermap import WeatherSchema
|
from platypush.schemas.weather.openweathermap import WeatherSchema
|
||||||
|
|
||||||
|
|
||||||
class WeatherOpenweathermapPlugin(WeatherPlugin):
|
class WeatherOpenweathermapPlugin(WeatherPlugin): # pylint: disable=too-many-ancestors
|
||||||
"""
|
"""
|
||||||
OpenWeatherMap plugin.
|
OpenWeatherMap plugin.
|
||||||
|
|
||||||
|
@ -76,8 +75,7 @@ class WeatherOpenweathermapPlugin(WeatherPlugin):
|
||||||
assert self._location_query, 'Specify either location, city_id or lat/long'
|
assert self._location_query, 'Specify either location, city_id or lat/long'
|
||||||
return self._location_query
|
return self._location_query
|
||||||
|
|
||||||
@action
|
def _get_current_weather(
|
||||||
def get_current_weather(
|
|
||||||
self,
|
self,
|
||||||
*_,
|
*_,
|
||||||
location: Optional[str] = None,
|
location: Optional[str] = None,
|
||||||
|
@ -99,9 +97,10 @@ class WeatherOpenweathermapPlugin(WeatherPlugin):
|
||||||
:param units: Override the ``units`` configuration value.
|
:param units: Override the ``units`` configuration value.
|
||||||
:return: .. schema:: weather.openweathermap.WeatherSchema
|
:return: .. schema:: weather.openweathermap.WeatherSchema
|
||||||
"""
|
"""
|
||||||
|
units = units or self.units
|
||||||
params = {
|
params = {
|
||||||
'appid': self._token,
|
'appid': self._token,
|
||||||
'units': units or self.units,
|
'units': units,
|
||||||
**self._get_location_query(
|
**self._get_location_query(
|
||||||
location=location,
|
location=location,
|
||||||
city_id=city_id,
|
city_id=city_id,
|
||||||
|
@ -113,4 +112,6 @@ class WeatherOpenweathermapPlugin(WeatherPlugin):
|
||||||
|
|
||||||
rs = requests.get(self.base_url, params=params, timeout=10)
|
rs = requests.get(self.base_url, params=params, timeout=10)
|
||||||
rs.raise_for_status()
|
rs.raise_for_status()
|
||||||
return dict(WeatherSchema().dump(rs.json()))
|
state = rs.json()
|
||||||
|
state['units'] = units
|
||||||
|
return dict(WeatherSchema().dump(state))
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from dateutil.tz import tzutc
|
||||||
from marshmallow import fields, pre_dump
|
from marshmallow import fields, pre_dump
|
||||||
from marshmallow.schema import Schema
|
from marshmallow.schema import Schema
|
||||||
|
from marshmallow.validate import OneOf
|
||||||
|
|
||||||
from platypush.schemas import DateTime
|
from platypush.schemas import DateTime
|
||||||
|
|
||||||
|
@ -135,6 +139,19 @@ class WeatherSchema(Schema):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
units = fields.String(
|
||||||
|
missing='metric',
|
||||||
|
validate=OneOf(['metric', 'imperial']),
|
||||||
|
metadata={
|
||||||
|
'description': 'Unit of measure',
|
||||||
|
'example': 'metric',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _timestamp_to_dt(timestamp: float) -> datetime:
|
||||||
|
return datetime.fromtimestamp(timestamp, tz=tzutc())
|
||||||
|
|
||||||
@pre_dump
|
@pre_dump
|
||||||
def _pre_dump(self, data: dict, **_) -> dict:
|
def _pre_dump(self, data: dict, **_) -> dict:
|
||||||
sun_data = data.pop('sys', {})
|
sun_data = data.pop('sys', {})
|
||||||
|
@ -142,8 +159,8 @@ class WeatherSchema(Schema):
|
||||||
sunset = sun_data.get('sunset')
|
sunset = sun_data.get('sunset')
|
||||||
|
|
||||||
if sunrise is not None:
|
if sunrise is not None:
|
||||||
data['sunrise'] = sunrise
|
data['sunrise'] = self._timestamp_to_dt(sunrise)
|
||||||
if sunset is not None:
|
if sunset is not None:
|
||||||
data['sunset'] = sunset
|
data['sunset'] = self._timestamp_to_dt(sunset)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
Loading…
Reference in a new issue