177 lines
5.4 KiB
Python
177 lines
5.4 KiB
Python
"""The National Weather Service integration."""
|
|
import asyncio
|
|
import datetime
|
|
import logging
|
|
|
|
import aiohttp
|
|
from pynws import SimpleNWS
|
|
import voluptuous as vol
|
|
|
|
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers import discovery
|
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
|
import homeassistant.helpers.config_validation as cv
|
|
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
|
from homeassistant.helpers.event import async_track_time_interval
|
|
|
|
from .const import CONF_STATION, DOMAIN
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
_INDIVIDUAL_SCHEMA = vol.Schema(
|
|
{
|
|
vol.Required(CONF_API_KEY): 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_STATION): cv.string,
|
|
}
|
|
)
|
|
|
|
CONFIG_SCHEMA = vol.Schema(
|
|
{DOMAIN: vol.All(cv.ensure_list, [_INDIVIDUAL_SCHEMA])}, extra=vol.ALLOW_EXTRA,
|
|
)
|
|
|
|
PLATFORMS = ["weather"]
|
|
|
|
DEFAULT_SCAN_INTERVAL = datetime.timedelta(minutes=10)
|
|
|
|
|
|
def base_unique_id(latitude, longitude):
|
|
"""Return unique id for entries in configuration."""
|
|
return f"{latitude}_{longitude}"
|
|
|
|
|
|
def signal_unique_id(latitude, longitude):
|
|
"""Return unique id for signaling to entries in configuration from component."""
|
|
return f"{DOMAIN}_{base_unique_id(latitude,longitude)}"
|
|
|
|
|
|
async def async_setup(hass: HomeAssistant, config: dict):
|
|
"""Set up the National Weather Service integration."""
|
|
if DOMAIN not in config:
|
|
return True
|
|
|
|
hass.data[DOMAIN] = hass.data.get(DOMAIN, {})
|
|
for entry in config[DOMAIN]:
|
|
latitude = entry.get(CONF_LATITUDE, hass.config.latitude)
|
|
longitude = entry.get(CONF_LONGITUDE, hass.config.longitude)
|
|
api_key = entry[CONF_API_KEY]
|
|
|
|
client_session = async_get_clientsession(hass)
|
|
|
|
if base_unique_id(latitude, longitude) in hass.data[DOMAIN]:
|
|
_LOGGER.error(
|
|
"Duplicate entry in config: latitude %s latitude: %s",
|
|
latitude,
|
|
longitude,
|
|
)
|
|
continue
|
|
|
|
nws_data = NwsData(hass, latitude, longitude, api_key, client_session)
|
|
hass.data[DOMAIN][base_unique_id(latitude, longitude)] = nws_data
|
|
async_track_time_interval(hass, nws_data.async_update, DEFAULT_SCAN_INTERVAL)
|
|
|
|
for component in PLATFORMS:
|
|
hass.async_create_task(
|
|
discovery.async_load_platform(hass, component, DOMAIN, {}, config)
|
|
)
|
|
|
|
return True
|
|
|
|
|
|
class NwsData:
|
|
"""Data class for National Weather Service integration."""
|
|
|
|
def __init__(self, hass, latitude, longitude, api_key, websession):
|
|
"""Initialize the data."""
|
|
self.hass = hass
|
|
self.latitude = latitude
|
|
self.longitude = longitude
|
|
ha_api_key = f"{api_key} homeassistant"
|
|
self.nws = SimpleNWS(latitude, longitude, ha_api_key, websession)
|
|
|
|
self.update_observation_success = True
|
|
self.update_forecast_success = True
|
|
self.update_forecast_hourly_success = True
|
|
|
|
async def async_set_station(self, station):
|
|
"""
|
|
Set to desired station.
|
|
|
|
If None, nearest station is used.
|
|
"""
|
|
await self.nws.set_station(station)
|
|
_LOGGER.debug("Nearby station list: %s", self.nws.stations)
|
|
|
|
@property
|
|
def station(self):
|
|
"""Return station name."""
|
|
return self.nws.station
|
|
|
|
@property
|
|
def observation(self):
|
|
"""Return observation."""
|
|
return self.nws.observation
|
|
|
|
@property
|
|
def forecast(self):
|
|
"""Return day+night forecast."""
|
|
return self.nws.forecast
|
|
|
|
@property
|
|
def forecast_hourly(self):
|
|
"""Return hourly forecast."""
|
|
return self.nws.forecast_hourly
|
|
|
|
@staticmethod
|
|
async def _async_update_item(update_call, update_type, station_name, success):
|
|
try:
|
|
_LOGGER.debug("Updating %s for station %s", update_type, station_name)
|
|
await update_call()
|
|
|
|
if success:
|
|
_LOGGER.warning(
|
|
"Success updating %s for station %s", update_type, station_name
|
|
)
|
|
success = True
|
|
except (aiohttp.ClientError, asyncio.TimeoutError) as err:
|
|
if success:
|
|
_LOGGER.warning(
|
|
"Error updating %s for station %s: %s",
|
|
update_type,
|
|
station_name,
|
|
err,
|
|
)
|
|
success = False
|
|
|
|
async def async_update(self, now=None):
|
|
"""Update all data."""
|
|
|
|
await self._async_update_item(
|
|
self.nws.update_observation,
|
|
"observation",
|
|
self.station,
|
|
self.update_observation_success,
|
|
)
|
|
await self._async_update_item(
|
|
self.nws.update_forecast,
|
|
"forecast",
|
|
self.station,
|
|
self.update_forecast_success,
|
|
)
|
|
await self._async_update_item(
|
|
self.nws.update_forecast_hourly,
|
|
"forecast_hourly",
|
|
self.station,
|
|
self.update_forecast_hourly_success,
|
|
)
|
|
|
|
async_dispatcher_send(
|
|
self.hass, signal_unique_id(self.latitude, self.longitude)
|
|
)
|