Set up Met during onboarding (#24622)

* Set up Met during onboarding

* Lint

* Add pyMetNo to test reqs
This commit is contained in:
Paulus Schoutsen
2019-06-19 14:41:27 -07:00
committed by GitHub
parent 6ea92f86a5
commit f2962a0d16
12 changed files with 242 additions and 54 deletions
+10 -1
View File
@@ -7,7 +7,7 @@ from homeassistant.const import (
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from .const import DOMAIN, HOME_LOCATION_NAME
from .const import DOMAIN, HOME_LOCATION_NAME, CONF_TRACK_HOME
@callback
@@ -61,3 +61,12 @@ class MetFlowHandler(data_entry_flow.FlowHandler):
}),
errors=self._errors,
)
async def async_step_onboarding(self, data=None):
"""Handle a flow initialized by onboarding."""
return self.async_create_entry(
title=HOME_LOCATION_NAME,
data={
CONF_TRACK_HOME: True
}
)
+2
View File
@@ -7,6 +7,8 @@ DOMAIN = 'met'
HOME_LOCATION_NAME = 'Home'
CONF_TRACK_HOME = 'track_home'
ENTITY_ID_SENSOR_FORMAT = WEATHER_DOMAIN + ".met_{}"
ENTITY_ID_SENSOR_FORMAT_HOME = ENTITY_ID_SENSOR_FORMAT.format(
HOME_LOCATION_NAME)
+103 -49
View File
@@ -2,17 +2,21 @@
import logging
from random import randrange
import metno
import voluptuous as vol
from homeassistant.core import callback
from homeassistant.components.weather import PLATFORM_SCHEMA, WeatherEntity
from homeassistant.const import (
CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, TEMP_CELSIUS)
CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, TEMP_CELSIUS,
EVENT_CORE_CONFIG_UPDATE)
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, async_track_utc_time_change)
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__)
ATTRIBUTION = "Weather forecast from met.no, delivered by the Norwegian " \
@@ -27,6 +31,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
'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,
})
@@ -35,60 +40,82 @@ async def async_setup_platform(hass, config, async_add_entities,
"""Set up the Met.no weather platform."""
_LOGGER.warning("Loading Met.no via platform config is deprecated")
name = config.get(CONF_NAME)
latitude = config.get(CONF_LATITUDE, hass.config.latitude)
longitude = config.get(CONF_LONGITUDE, hass.config.longitude)
elevation = config.get(CONF_ELEVATION, hass.config.elevation or 0)
# Add defaults.
config = {
CONF_ELEVATION: hass.config.elevation,
**config,
}
if None in (latitude, longitude):
_LOGGER.error("Latitude or longitude not set in Home Assistant config")
return
if config.get(CONF_LATITUDE) is None:
config[CONF_TRACK_HOME] = True
station = await async_get_station(
hass, name, latitude, longitude, elevation)
async_add_entities([station])
async_add_entities([MetWeather(config)])
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Add a weather entity from a config_entry."""
name = config_entry.data.get(CONF_NAME)
latitude = config_entry.data[CONF_LATITUDE]
longitude = config_entry.data[CONF_LONGITUDE]
elevation = config_entry.data[CONF_ELEVATION]
station = await async_get_station(
hass, name, latitude, longitude, elevation)
async_add_entities([station])
async def async_get_station(hass, name, latitude, longitude, elevation):
"""Retrieve weather station, station name to be used as the entity name."""
coordinates = {
'lat': str(latitude),
'lon': str(longitude),
'msl': str(elevation),
}
return MetWeather(name, coordinates, async_get_clientsession(hass))
async_add_entities([MetWeather(config_entry.data)])
class MetWeather(WeatherEntity):
"""Implementation of a Met.no weather condition."""
def __init__(self, name, coordinates, clientsession):
def __init__(self, config):
"""Initialise the platform with a data instance and site."""
import metno
self._name = name
self._weather_data = metno.MetWeatherData(
coordinates, clientsession, URL)
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()
async_track_utc_time_change(
self.hass, self._update, minute=31, second=0)
if self._config.get(CONF_TRACK_HOME):
self._unsub_track_home = self.hass.bus.async_listen(
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 = {
'lat': str(latitude),
'lon': str(longitude),
'msl': str(elevation),
}
self._weather_data = metno.MetWeatherData(
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."""
@@ -96,28 +123,55 @@ class MetWeather(WeatherEntity):
# Retry in 15 to 20 minutes.
minutes = 15 + randrange(6)
_LOGGER.error("Retrying in %i minutes", minutes)
async_call_later(self.hass, minutes*60, self._fetch_data)
self._unsub_fetch_data = async_call_later(
self.hass, minutes*60, self._fetch_data)
return
async_call_later(self.hass, 60*60, self._fetch_data)
await self._update()
# 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(
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
async 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_schedule_update_ha_state()
@property
def unique_id(self):
"""Return unique ID."""
if self.track_home:
return 'home'
return '{}-{}'.format(
self._config[CONF_LATITUDE], self._config[CONF_LONGITUDE])
@property
def name(self):
"""Return the name of the sensor."""
return self._name
name = self._config.get(CONF_NAME)
if name is not None:
return CONF_NAME
if self.track_home:
return self.hass.config.location_name
return DEFAULT_NAME
@property
def condition(self):
@@ -148,6 +148,10 @@ class CoreConfigOnboardingView(_BaseOnboardingView):
await self._async_mark_done(hass)
await hass.config_entries.flow.async_init('met', context={
'source': 'onboarding'
})
return self.json({})
-4
View File
@@ -60,10 +60,6 @@ default_config:
# http:
# base_url: example.duckdns.org:8123
# Weather prediction
weather:
- platform: met
# Text to speech
tts:
- platform: google_translate