Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GeoNet NZ Volcanic Alert Level sensor #26901

Merged
merged 26 commits into from Nov 19, 2019
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
aaa3fd2
first version of new integration
exxamalte Sep 5, 2019
7ef10b6
moved icon to shared consts
exxamalte Sep 8, 2019
540b64a
added unit tests
exxamalte Sep 8, 2019
520419a
transformed from geolocation to sensor integration
exxamalte Sep 10, 2019
acc674e
alert level is now the state of the sensor
exxamalte Sep 10, 2019
a57f82c
adopted unit tests
exxamalte Sep 11, 2019
d390a5d
fixed comment
exxamalte Sep 11, 2019
fc8176e
keep all sensors registered even if the feed update fails intermittently
exxamalte Sep 19, 2019
f1fe13f
bumped version of upstream library
exxamalte Sep 20, 2019
045561f
bumped version of integration library
exxamalte Sep 23, 2019
8360d99
regenerated requirements
exxamalte Sep 23, 2019
5a44953
bumped version of integration library
exxamalte Sep 24, 2019
2a91a56
bumped version of integration library
exxamalte Sep 24, 2019
9afda9b
fixed generated file
exxamalte Sep 25, 2019
b66111c
removed commented out code
exxamalte Sep 25, 2019
58144bb
regenerated config flow file
exxamalte Nov 1, 2019
1de9bd1
update to latest integration library version
exxamalte Nov 7, 2019
4d07978
simplified code
exxamalte Nov 8, 2019
cb353ed
removed debug log statement
exxamalte Nov 8, 2019
64ec7c8
simplified code structure
exxamalte Nov 8, 2019
82a6a73
defined constant
exxamalte Nov 8, 2019
95e2db5
use core interfaces
exxamalte Nov 8, 2019
6faef2c
moved test and fixture
exxamalte Nov 8, 2019
1b0a531
sorted imports
exxamalte Nov 8, 2019
779f3a8
simplified patching
exxamalte Nov 10, 2019
820c976
moved fixture to central config file
exxamalte Nov 10, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Expand Up @@ -110,6 +110,7 @@ homeassistant/components/gearbest/* @HerrHofrat
homeassistant/components/geniushub/* @zxdavb
homeassistant/components/geo_rss_events/* @exxamalte
homeassistant/components/geonetnz_quakes/* @exxamalte
homeassistant/components/geonetnz_volcano/* @exxamalte
homeassistant/components/gitter/* @fabaff
homeassistant/components/glances/* @fabaff @engrbm87
homeassistant/components/gntp/* @robbiet480
Expand Down
16 changes: 16 additions & 0 deletions homeassistant/components/geonetnz_volcano/.translations/en.json
@@ -0,0 +1,16 @@
{
"config": {
"error": {
"identifier_exists": "Location already registered"
},
"step": {
"user": {
"data": {
"radius": "Radius"
},
"title": "Fill in your filter details."
}
},
"title": "GeoNet NZ Volcano"
}
}
207 changes: 207 additions & 0 deletions homeassistant/components/geonetnz_volcano/__init__.py
@@ -0,0 +1,207 @@
"""The GeoNet NZ Volcano integration."""
import asyncio
import logging
from datetime import timedelta, datetime
from typing import Optional

import voluptuous as vol
from aio_geojson_geonetnz_volcano import GeonetnzVolcanoFeedManager

from homeassistant.core import callback
from homeassistant.util.unit_system import METRIC_SYSTEM
from homeassistant.config_entries import SOURCE_IMPORT
from homeassistant.const import (
CONF_LATITUDE,
CONF_LONGITUDE,
CONF_RADIUS,
CONF_SCAN_INTERVAL,
CONF_UNIT_SYSTEM_IMPERIAL,
CONF_UNIT_SYSTEM,
LENGTH_MILES,
)
from homeassistant.helpers import config_validation as cv, aiohttp_client
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.event import async_track_time_interval

from .config_flow import configured_instances
from .const import (
DEFAULT_RADIUS,
DEFAULT_SCAN_INTERVAL,
DOMAIN,
FEED,
SIGNAL_NEW_SENSOR,
SIGNAL_UPDATE_ENTITY,
)

_LOGGER = logging.getLogger(__name__)

CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Optional(CONF_LATITUDE): cv.latitude,
vol.Optional(CONF_LONGITUDE): cv.longitude,
vol.Optional(CONF_RADIUS, default=DEFAULT_RADIUS): vol.Coerce(float),
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 Volcano 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)
scan_interval = conf[CONF_SCAN_INTERVAL]

identifier = f"{latitude}, {longitude}"
if identifier in configured_instances(hass):
return True

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_SCAN_INTERVAL: scan_interval,
},
)
)

return True


async def async_setup_entry(hass, config_entry):
"""Set up the GeoNet NZ Volcano component as config entry."""
if DOMAIN not in hass.data:
hass.data[DOMAIN] = {}
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved
if FEED not in hass.data[DOMAIN]:
hass.data[DOMAIN][FEED] = {}

radius = config_entry.data[CONF_RADIUS]
unit_system = config_entry.data[CONF_UNIT_SYSTEM]
if unit_system == CONF_UNIT_SYSTEM_IMPERIAL:
radius = METRIC_SYSTEM.length(radius, LENGTH_MILES)
# Create feed entity manager for all platforms.
manager = GeonetnzVolcanoFeedEntityManager(hass, config_entry, radius, unit_system)
hass.data[DOMAIN][FEED][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 Volcano 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, "sensor")]
)
return True


class GeonetnzVolcanoFeedEntityManager:
"""Feed Entity Manager for GeoNet NZ Volcano feed."""

def __init__(self, hass, config_entry, radius_in_km, unit_system):
"""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 = GeonetnzVolcanoFeedManager(
websession,
self._generate_entity,
self._update_entity,
self._remove_entity,
coordinates,
filter_radius=radius_in_km,
)
self._config_entry_id = config_entry.entry_id
self._scan_interval = timedelta(seconds=config_entry.data[CONF_SCAN_INTERVAL])
self._unit_system = unit_system
self._track_time_remove_callback = None
self.listeners = []

async def async_init(self):
"""Schedule initial and regular updates based on configured time interval."""

self._hass.async_create_task(
self._hass.config_entries.async_forward_entry_setup(
self._config_entry, "sensor"
)
)

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 SIGNAL_NEW_SENSOR.format(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 last_update(self) -> Optional[datetime]:
"""Return the last update of this feed."""
return self._feed_manager.last_update

def last_update_successful(self) -> Optional[datetime]:
"""Return the last successful update of this feed."""
return self._feed_manager.last_update_successful

async def _generate_entity(self, external_id):
"""Generate new entity."""
async_dispatcher_send(
self._hass,
self.async_event_new_entity(),
self,
external_id,
self._unit_system,
)

async def _update_entity(self, external_id):
"""Update entity."""
async_dispatcher_send(self._hass, SIGNAL_UPDATE_ENTITY.format(external_id))

async def _remove_entity(self, external_id):
"""Ignore removing entity."""
76 changes: 76 additions & 0 deletions homeassistant/components/geonetnz_volcano/config_flow.py
@@ -0,0 +1,76 @@
"""Config flow to configure the GeoNet NZ Volcano integration."""
import logging

import voluptuous as vol

from homeassistant import config_entries
from homeassistant.const import (
CONF_LATITUDE,
CONF_LONGITUDE,
CONF_RADIUS,
CONF_SCAN_INTERVAL,
CONF_UNIT_SYSTEM,
CONF_UNIT_SYSTEM_IMPERIAL,
CONF_UNIT_SYSTEM_METRIC,
)
from homeassistant.core import callback
from homeassistant.helpers import config_validation as cv

from .const import DEFAULT_RADIUS, DEFAULT_SCAN_INTERVAL, DOMAIN

_LOGGER = logging.getLogger(__name__)


@callback
def configured_instances(hass):
"""Return a set of configured GeoNet NZ Volcano instances."""
return set(
f"{entry.data[CONF_LATITUDE]}, {entry.data[CONF_LONGITUDE]}"
for entry in hass.config_entries.async_entries(DOMAIN)
)


@config_entries.HANDLERS.register(DOMAIN)
class GeonetnzVolcanoFlowHandler(config_entries.ConfigFlow):
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved
"""Handle a GeoNet NZ Volcano config flow."""

CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL

async def _show_form(self, errors=None):
"""Show the form to the user."""
data_schema = vol.Schema(
{vol.Optional(CONF_RADIUS, default=DEFAULT_RADIUS): cv.positive_int}
)

return self.async_show_form(
step_id="user", data_schema=data_schema, errors=errors or {}
)

async def async_step_import(self, import_config):
"""Import a config entry from configuration.yaml."""
return await self.async_step_user(import_config)

async def async_step_user(self, user_input=None):
"""Handle the start of the config flow."""
_LOGGER.debug("User input: %s", user_input)
MartinHjelmare marked this conversation as resolved.
Show resolved Hide resolved
if not user_input:
return await self._show_form()

latitude = user_input.get(CONF_LATITUDE, self.hass.config.latitude)
user_input[CONF_LATITUDE] = latitude
longitude = user_input.get(CONF_LONGITUDE, self.hass.config.longitude)
user_input[CONF_LONGITUDE] = longitude

identifier = f"{user_input[CONF_LATITUDE]}, {user_input[CONF_LONGITUDE]}"
if identifier in configured_instances(self.hass):
return await self._show_form({"base": "identifier_exists"})

if self.hass.config.units.name == CONF_UNIT_SYSTEM_IMPERIAL:
user_input[CONF_UNIT_SYSTEM] = CONF_UNIT_SYSTEM_IMPERIAL
else:
user_input[CONF_UNIT_SYSTEM] = CONF_UNIT_SYSTEM_METRIC

scan_interval = user_input.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
user_input[CONF_SCAN_INTERVAL] = scan_interval.seconds

return self.async_create_entry(title=identifier, data=user_input)
18 changes: 18 additions & 0 deletions homeassistant/components/geonetnz_volcano/const.py
@@ -0,0 +1,18 @@
"""Define constants for the GeoNet NZ Volcano integration."""
from datetime import timedelta

DOMAIN = "geonetnz_volcano"

FEED = "feed"

ATTR_ACTIVITY = "activity"
ATTR_EXTERNAL_ID = "external_id"
ATTR_HAZARDS = "hazards"

# Icon alias "mdi:mountain" not working.
DEFAULT_ICON = "mdi:image-filter-hdr"
DEFAULT_RADIUS = 50.0
DEFAULT_SCAN_INTERVAL = timedelta(minutes=5)

SIGNAL_NEW_SENSOR = "geonetnz_volcano_new_sensor_{}"
SIGNAL_UPDATE_ENTITY = "geonetnz_volcano_update_{}"
13 changes: 13 additions & 0 deletions homeassistant/components/geonetnz_volcano/manifest.json
@@ -0,0 +1,13 @@
{
"domain": "geonetnz_volcano",
"name": "GeoNet NZ Volcano",
"config_flow": true,
"documentation": "https://www.home-assistant.io/components/geonetnz_volcano",
"requirements": [
"aio_geojson_geonetnz_volcano==0.5"
],
"dependencies": [],
"codeowners": [
"@exxamalte"
]
}