core/homeassistant/components/met/weather.py

230 lines
6.7 KiB
Python
Raw Normal View History

2019-02-14 15:42:03 +00:00
"""Support for Met.no weather service."""
import logging
from random import randrange
import metno
import voluptuous as vol
from homeassistant.components.weather import PLATFORM_SCHEMA, WeatherEntity
2019-02-14 15:42:03 +00:00
from homeassistant.const import (
2019-07-31 19:25:30 +00:00
CONF_ELEVATION,
CONF_LATITUDE,
CONF_LONGITUDE,
CONF_NAME,
EVENT_CORE_CONFIG_UPDATE,
TEMP_CELSIUS,
2019-07-31 19:25:30 +00:00
)
from homeassistant.core import callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.event import async_call_later
import homeassistant.util.dt as dt_util
from .const import CONF_TRACK_HOME
_LOGGER = logging.getLogger(__name__)
2019-07-31 19:25:30 +00:00
ATTRIBUTION = (
"Weather forecast from met.no, delivered by the Norwegian "
"Meteorological Institute."
)
DEFAULT_NAME = "Met.no"
2019-07-31 19:25:30 +00:00
URL = "https://aa015h6buqvih86i1.api.met.no/weatherapi/locationforecast/1.9/"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Inclusive(
CONF_LATITUDE, "coordinates", "Latitude and longitude must exist together"
): cv.latitude,
vol.Inclusive(
CONF_LONGITUDE, "coordinates", "Latitude and longitude must exist together"
): cv.longitude,
vol.Optional(CONF_ELEVATION): int,
}
)
2019-07-31 19:25:30 +00:00
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the Met.no weather platform."""
_LOGGER.warning("Loading Met.no via platform config is deprecated")
# Add defaults.
2019-07-31 19:25:30 +00:00
config = {CONF_ELEVATION: hass.config.elevation, **config}
if config.get(CONF_LATITUDE) is None:
config[CONF_TRACK_HOME] = True
async_add_entities([MetWeather(config)])
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Add a weather entity from a config_entry."""
async_add_entities([MetWeather(config_entry.data)])
class MetWeather(WeatherEntity):
"""Implementation of a Met.no weather condition."""
def __init__(self, config):
"""Initialise the platform with a data instance and site."""
self._config = config
self._unsub_track_home = None
self._unsub_fetch_data = None
self._weather_data = None
self._current_weather_data = {}
self._forecast_data = None
async def async_added_to_hass(self):
"""Start fetching data."""
self._init_data()
await self._fetch_data()
if self._config.get(CONF_TRACK_HOME):
self._unsub_track_home = self.hass.bus.async_listen(
2019-07-31 19:25:30 +00:00
EVENT_CORE_CONFIG_UPDATE, self._core_config_updated
)
@callback
def _init_data(self):
"""Initialize a data object."""
conf = self._config
if self.track_home:
latitude = self.hass.config.latitude
longitude = self.hass.config.longitude
elevation = self.hass.config.elevation
else:
latitude = conf[CONF_LATITUDE]
longitude = conf[CONF_LONGITUDE]
elevation = conf[CONF_ELEVATION]
coordinates = {
2019-07-31 19:25:30 +00:00
"lat": str(latitude),
"lon": str(longitude),
"msl": str(elevation),
}
self._weather_data = metno.MetWeatherData(
2019-07-31 19:25:30 +00:00
coordinates, async_get_clientsession(self.hass), URL
)
async def _core_config_updated(self, _event):
"""Handle core config updated."""
self._init_data()
if self._unsub_fetch_data:
self._unsub_fetch_data()
self._unsub_fetch_data = None
await self._fetch_data()
async def will_remove_from_hass(self):
"""Handle entity will be removed from hass."""
if self._unsub_track_home:
self._unsub_track_home()
self._unsub_track_home = None
if self._unsub_fetch_data:
self._unsub_fetch_data()
self._unsub_fetch_data = None
async def _fetch_data(self, *_):
"""Get the latest data from met.no."""
if not await self._weather_data.fetching_data():
# Retry in 15 to 20 minutes.
minutes = 15 + randrange(6)
_LOGGER.error("Retrying in %i minutes", minutes)
self._unsub_fetch_data = async_call_later(
2019-07-31 19:25:30 +00:00
self.hass, minutes * 60, self._fetch_data
)
return
# Wait between 55-65 minutes. If people update HA on the hour, this
# will make sure it will spread it out.
self._unsub_fetch_data = async_call_later(
2019-07-31 19:25:30 +00:00
self.hass, randrange(55, 65) * 60, self._fetch_data
)
self._update()
def _update(self, *_):
"""Get the latest data from Met.no."""
self._current_weather_data = self._weather_data.get_current_weather()
time_zone = dt_util.DEFAULT_TIME_ZONE
self._forecast_data = self._weather_data.get_forecast(time_zone)
self.async_write_ha_state()
@property
def track_home(self):
"""Return if we are tracking home."""
return self._config.get(CONF_TRACK_HOME, False)
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def unique_id(self):
"""Return unique ID."""
if self.track_home:
2019-07-31 19:25:30 +00:00
return "home"
return f"{self._config[CONF_LATITUDE]}-{self._config[CONF_LONGITUDE]}"
@property
def name(self):
"""Return the name of the sensor."""
name = self._config.get(CONF_NAME)
if name is not None:
return name
if self.track_home:
return self.hass.config.location_name
return DEFAULT_NAME
@property
def condition(self):
"""Return the current condition."""
2019-07-31 19:25:30 +00:00
return self._current_weather_data.get("condition")
@property
def temperature(self):
"""Return the temperature."""
2019-07-31 19:25:30 +00:00
return self._current_weather_data.get("temperature")
@property
def temperature_unit(self):
"""Return the unit of measurement."""
return TEMP_CELSIUS
@property
def pressure(self):
"""Return the pressure."""
2019-07-31 19:25:30 +00:00
return self._current_weather_data.get("pressure")
@property
def humidity(self):
"""Return the humidity."""
2019-07-31 19:25:30 +00:00
return self._current_weather_data.get("humidity")
@property
def wind_speed(self):
"""Return the wind speed."""
2019-07-31 19:25:30 +00:00
return self._current_weather_data.get("wind_speed")
@property
def wind_bearing(self):
"""Return the wind direction."""
2019-07-31 19:25:30 +00:00
return self._current_weather_data.get("wind_bearing")
@property
def attribution(self):
"""Return the attribution."""
return ATTRIBUTION
@property
def forecast(self):
"""Return the forecast array."""
return self._forecast_data