220 lines
6.5 KiB
Python
220 lines
6.5 KiB
Python
"""Support for IPMA weather service."""
|
|
import logging
|
|
from datetime import timedelta
|
|
|
|
import async_timeout
|
|
import voluptuous as vol
|
|
|
|
from homeassistant.components.weather import (
|
|
WeatherEntity, PLATFORM_SCHEMA, ATTR_FORECAST_CONDITION,
|
|
ATTR_FORECAST_PRECIPITATION, ATTR_FORECAST_TEMP,
|
|
ATTR_FORECAST_TEMP_LOW, ATTR_FORECAST_TIME)
|
|
from homeassistant.const import \
|
|
CONF_NAME, TEMP_CELSIUS, CONF_LATITUDE, CONF_LONGITUDE
|
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
|
from homeassistant.helpers import config_validation as cv
|
|
from homeassistant.util import Throttle
|
|
|
|
REQUIREMENTS = ['pyipma==1.2.1']
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
ATTRIBUTION = 'Instituto Português do Mar e Atmosfera'
|
|
|
|
ATTR_WEATHER_DESCRIPTION = "description"
|
|
|
|
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30)
|
|
|
|
CONDITION_CLASSES = {
|
|
'cloudy': [4, 5, 24, 25, 27],
|
|
'fog': [16, 17, 26],
|
|
'hail': [21, 22],
|
|
'lightning': [19],
|
|
'lightning-rainy': [20, 23],
|
|
'partlycloudy': [2, 3],
|
|
'pouring': [8, 11],
|
|
'rainy': [6, 7, 9, 10, 12, 13, 14, 15],
|
|
'snowy': [18],
|
|
'snowy-rainy': [],
|
|
'sunny': [1],
|
|
'windy': [],
|
|
'windy-variant': [],
|
|
'exceptional': [],
|
|
}
|
|
|
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
|
vol.Optional(CONF_NAME): cv.string,
|
|
vol.Optional(CONF_LATITUDE): cv.latitude,
|
|
vol.Optional(CONF_LONGITUDE): cv.longitude,
|
|
})
|
|
|
|
|
|
async def async_setup_platform(
|
|
hass, config, async_add_entities, discovery_info=None):
|
|
"""Set up the ipma platform.
|
|
|
|
Deprecated.
|
|
"""
|
|
_LOGGER.warning("Loading IPMA via platform config is deprecated")
|
|
|
|
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
|
|
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
|
|
|
|
if None in (latitude, longitude):
|
|
_LOGGER.error("Latitude or longitude not set in Home Assistant config")
|
|
return
|
|
|
|
station = await async_get_station(hass, latitude, longitude)
|
|
|
|
async_add_entities([IPMAWeather(station, config)], True)
|
|
|
|
|
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
|
"""Add a weather entity from a config_entry."""
|
|
latitude = config_entry.data[CONF_LATITUDE]
|
|
longitude = config_entry.data[CONF_LONGITUDE]
|
|
|
|
station = await async_get_station(hass, latitude, longitude)
|
|
|
|
async_add_entities([IPMAWeather(station, config_entry.data)], True)
|
|
|
|
|
|
async def async_get_station(hass, latitude, longitude):
|
|
"""Retrieve weather station, station name to be used as the entity name."""
|
|
from pyipma import Station
|
|
|
|
websession = async_get_clientsession(hass)
|
|
with async_timeout.timeout(10, loop=hass.loop):
|
|
station = await Station.get(websession, float(latitude),
|
|
float(longitude))
|
|
|
|
_LOGGER.debug("Initializing for coordinates %s, %s -> station %s",
|
|
latitude, longitude, station.local)
|
|
|
|
return station
|
|
|
|
|
|
class IPMAWeather(WeatherEntity):
|
|
"""Representation of a weather condition."""
|
|
|
|
def __init__(self, station, config):
|
|
"""Initialise the platform with a data instance and station name."""
|
|
self._station_name = config.get(CONF_NAME, station.local)
|
|
self._station = station
|
|
self._condition = None
|
|
self._forecast = None
|
|
self._description = None
|
|
|
|
@Throttle(MIN_TIME_BETWEEN_UPDATES)
|
|
async def async_update(self):
|
|
"""Update Condition and Forecast."""
|
|
with async_timeout.timeout(10, loop=self.hass.loop):
|
|
_new_condition = await self._station.observation()
|
|
if _new_condition is None:
|
|
_LOGGER.warning("Could not update weather conditions")
|
|
return
|
|
self._condition = _new_condition
|
|
|
|
_LOGGER.debug("Updating station %s, condition %s",
|
|
self._station.local, self._condition)
|
|
self._forecast = await self._station.forecast()
|
|
self._description = self._forecast[0].description
|
|
|
|
@property
|
|
def unique_id(self) -> str:
|
|
"""Return a unique id."""
|
|
return '{}, {}'.format(self._station.latitude, self._station.longitude)
|
|
|
|
@property
|
|
def attribution(self):
|
|
"""Return the attribution."""
|
|
return ATTRIBUTION
|
|
|
|
@property
|
|
def name(self):
|
|
"""Return the name of the station."""
|
|
return self._station_name
|
|
|
|
@property
|
|
def condition(self):
|
|
"""Return the current condition."""
|
|
if not self._forecast:
|
|
return
|
|
|
|
return next((k for k, v in CONDITION_CLASSES.items()
|
|
if self._forecast[0].idWeatherType in v), None)
|
|
|
|
@property
|
|
def temperature(self):
|
|
"""Return the current temperature."""
|
|
if not self._condition:
|
|
return None
|
|
|
|
return self._condition.temperature
|
|
|
|
@property
|
|
def pressure(self):
|
|
"""Return the current pressure."""
|
|
if not self._condition:
|
|
return None
|
|
|
|
return self._condition.pressure
|
|
|
|
@property
|
|
def humidity(self):
|
|
"""Return the name of the sensor."""
|
|
if not self._condition:
|
|
return None
|
|
|
|
return self._condition.humidity
|
|
|
|
@property
|
|
def wind_speed(self):
|
|
"""Return the current windspeed."""
|
|
if not self._condition:
|
|
return None
|
|
|
|
return self._condition.windspeed
|
|
|
|
@property
|
|
def wind_bearing(self):
|
|
"""Return the current wind bearing (degrees)."""
|
|
if not self._condition:
|
|
return None
|
|
|
|
return self._condition.winddirection
|
|
|
|
@property
|
|
def temperature_unit(self):
|
|
"""Return the unit of measurement."""
|
|
return TEMP_CELSIUS
|
|
|
|
@property
|
|
def forecast(self):
|
|
"""Return the forecast array."""
|
|
if self._forecast:
|
|
fcdata_out = []
|
|
for data_in in self._forecast:
|
|
data_out = {}
|
|
data_out[ATTR_FORECAST_TIME] = data_in.forecastDate
|
|
data_out[ATTR_FORECAST_CONDITION] =\
|
|
next((k for k, v in CONDITION_CLASSES.items()
|
|
if int(data_in.idWeatherType) in v), None)
|
|
data_out[ATTR_FORECAST_TEMP_LOW] = data_in.tMin
|
|
data_out[ATTR_FORECAST_TEMP] = data_in.tMax
|
|
data_out[ATTR_FORECAST_PRECIPITATION] = data_in.precipitaProb
|
|
|
|
fcdata_out.append(data_out)
|
|
|
|
return fcdata_out
|
|
|
|
@property
|
|
def device_state_attributes(self):
|
|
"""Return the state attributes."""
|
|
data = dict()
|
|
|
|
if self._description:
|
|
data[ATTR_WEATHER_DESCRIPTION] = self._description
|
|
|
|
return data
|