"""The GeoNet NZ Quakes integration.""" import asyncio from datetime import timedelta import logging from aio_geojson_geonetnz_quakes import GeonetnzQuakesFeedManager import voluptuous as vol from homeassistant.config_entries import SOURCE_IMPORT from homeassistant.const import ( CONF_LATITUDE, CONF_LONGITUDE, CONF_RADIUS, CONF_SCAN_INTERVAL, CONF_UNIT_SYSTEM_IMPERIAL, LENGTH_MILES, ) from homeassistant.core import callback from homeassistant.helpers import aiohttp_client, config_validation as cv from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.event import async_track_time_interval from homeassistant.util.unit_system import METRIC_SYSTEM from .const import ( CONF_MINIMUM_MAGNITUDE, CONF_MMI, DEFAULT_FILTER_TIME_INTERVAL, DEFAULT_MINIMUM_MAGNITUDE, DEFAULT_MMI, DEFAULT_RADIUS, DEFAULT_SCAN_INTERVAL, DOMAIN, FEED, PLATFORMS, ) _LOGGER = logging.getLogger(__name__) CONFIG_SCHEMA = vol.Schema( { DOMAIN: vol.Schema( { vol.Inclusive(CONF_LATITUDE, "coordinates"): cv.latitude, vol.Inclusive(CONF_LONGITUDE, "coordinates"): cv.longitude, vol.Optional(CONF_MMI, default=DEFAULT_MMI): vol.All( vol.Coerce(int), vol.Range(min=-1, max=8) ), vol.Optional(CONF_RADIUS, default=DEFAULT_RADIUS): vol.Coerce(float), vol.Optional( CONF_MINIMUM_MAGNITUDE, default=DEFAULT_MINIMUM_MAGNITUDE ): vol.All(vol.Coerce(float), vol.Range(min=0)), vol.Optional( CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL ): cv.time_period, } ) }, extra=vol.ALLOW_EXTRA, ) async def async_setup(hass, config): """Set up the GeoNet NZ Quakes component.""" if DOMAIN not in config: return True conf = config[DOMAIN] latitude = conf.get(CONF_LATITUDE, hass.config.latitude) longitude = conf.get(CONF_LONGITUDE, hass.config.longitude) mmi = conf[CONF_MMI] scan_interval = conf[CONF_SCAN_INTERVAL] hass.async_create_task( hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_IMPORT}, data={ CONF_LATITUDE: latitude, CONF_LONGITUDE: longitude, CONF_RADIUS: conf[CONF_RADIUS], CONF_MINIMUM_MAGNITUDE: conf[CONF_MINIMUM_MAGNITUDE], CONF_MMI: mmi, CONF_SCAN_INTERVAL: scan_interval, }, ) ) return True async def async_setup_entry(hass, config_entry): """Set up the GeoNet NZ Quakes component as config entry.""" hass.data.setdefault(DOMAIN, {}) feeds = hass.data[DOMAIN].setdefault(FEED, {}) radius = config_entry.data[CONF_RADIUS] if hass.config.units.name == CONF_UNIT_SYSTEM_IMPERIAL: radius = METRIC_SYSTEM.length(radius, LENGTH_MILES) # Create feed entity manager for all platforms. manager = GeonetnzQuakesFeedEntityManager(hass, config_entry, radius) feeds[config_entry.entry_id] = manager _LOGGER.debug("Feed entity manager added for %s", config_entry.entry_id) await manager.async_init() return True async def async_unload_entry(hass, config_entry): """Unload an GeoNet NZ Quakes component config entry.""" manager = hass.data[DOMAIN][FEED].pop(config_entry.entry_id) await manager.async_stop() await asyncio.wait( [ hass.config_entries.async_forward_entry_unload(config_entry, domain) for domain in PLATFORMS ] ) return True class GeonetnzQuakesFeedEntityManager: """Feed Entity Manager for GeoNet NZ Quakes feed.""" def __init__(self, hass, config_entry, radius_in_km): """Initialize the Feed Entity Manager.""" self._hass = hass self._config_entry = config_entry coordinates = ( config_entry.data[CONF_LATITUDE], config_entry.data[CONF_LONGITUDE], ) websession = aiohttp_client.async_get_clientsession(hass) self._feed_manager = GeonetnzQuakesFeedManager( websession, self._generate_entity, self._update_entity, self._remove_entity, coordinates, mmi=config_entry.data[CONF_MMI], filter_radius=radius_in_km, filter_minimum_magnitude=config_entry.data[CONF_MINIMUM_MAGNITUDE], filter_time=DEFAULT_FILTER_TIME_INTERVAL, status_callback=self._status_update, ) self._config_entry_id = config_entry.entry_id self._scan_interval = timedelta(seconds=config_entry.data[CONF_SCAN_INTERVAL]) self._track_time_remove_callback = None self._status_info = None self.listeners = [] async def async_init(self): """Schedule initial and regular updates based on configured time interval.""" for domain in PLATFORMS: self._hass.async_create_task( self._hass.config_entries.async_forward_entry_setup( self._config_entry, domain ) ) async def update(event_time): """Update.""" await self.async_update() # Trigger updates at regular intervals. self._track_time_remove_callback = async_track_time_interval( self._hass, update, self._scan_interval ) _LOGGER.debug("Feed entity manager initialized") async def async_update(self): """Refresh data.""" await self._feed_manager.update() _LOGGER.debug("Feed entity manager updated") async def async_stop(self): """Stop this feed entity manager from refreshing.""" for unsub_dispatcher in self.listeners: unsub_dispatcher() self.listeners = [] if self._track_time_remove_callback: self._track_time_remove_callback() _LOGGER.debug("Feed entity manager stopped") @callback def async_event_new_entity(self): """Return manager specific event to signal new entity.""" return f"geonetnz_quakes_new_geolocation_{self._config_entry_id}" def get_entry(self, external_id): """Get feed entry by external id.""" return self._feed_manager.feed_entries.get(external_id) def status_info(self): """Return latest status update info received.""" return self._status_info async def _generate_entity(self, external_id): """Generate new entity.""" async_dispatcher_send( self._hass, self.async_event_new_entity(), self, self._config_entry.unique_id, external_id, ) async def _update_entity(self, external_id): """Update entity.""" async_dispatcher_send(self._hass, f"geonetnz_quakes_update_{external_id}") async def _remove_entity(self, external_id): """Remove entity.""" async_dispatcher_send(self._hass, f"geonetnz_quakes_delete_{external_id}") async def _status_update(self, status_info): """Propagate status update.""" _LOGGER.debug("Status update received: %s", status_info) self._status_info = status_info async_dispatcher_send( self._hass, f"geonetnz_quakes_status_{self._config_entry_id}" )