Add options flow for AirVisual (#32634)
* Add options flow for AirVisual * Code review Co-Authored-By: Robert Svensson <Kane610@users.noreply.github.com> Co-authored-by: Robert Svensson <Kane610@users.noreply.github.com>
This commit is contained in:
parent
a3c55b5e96
commit
21cff003f8
6 changed files with 132 additions and 21 deletions
|
@ -11,13 +11,23 @@
|
|||
"data": {
|
||||
"api_key": "API Key",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude",
|
||||
"show_on_map": "Show monitored geography on the map"
|
||||
"longitude": "Longitude"
|
||||
},
|
||||
"description": "Monitor air quality in a geographical location.",
|
||||
"title": "Configure AirVisual"
|
||||
}
|
||||
},
|
||||
"title": "AirVisual"
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"show_on_map": "Show monitored geography on the map"
|
||||
},
|
||||
"description": "Set various options for the AirVisual integration.",
|
||||
"title": "Configure AirVisual"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -139,6 +139,8 @@ async def async_setup_entry(hass, config_entry):
|
|||
hass, refresh, DEFAULT_SCAN_INTERVAL
|
||||
)
|
||||
|
||||
config_entry.add_update_listener(async_update_options)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
@ -154,6 +156,12 @@ async def async_unload_entry(hass, config_entry):
|
|||
return True
|
||||
|
||||
|
||||
async def async_update_options(hass, config_entry):
|
||||
"""Handle an options update."""
|
||||
airvisual = hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id]
|
||||
airvisual.async_update_options(config_entry.options)
|
||||
|
||||
|
||||
class AirVisualData:
|
||||
"""Define a class to manage data from the AirVisual cloud API."""
|
||||
|
||||
|
@ -162,7 +170,7 @@ class AirVisualData:
|
|||
self._client = client
|
||||
self._hass = hass
|
||||
self.data = {}
|
||||
self.show_on_map = config_entry.options[CONF_SHOW_ON_MAP]
|
||||
self.options = config_entry.options
|
||||
|
||||
self.geographies = {
|
||||
async_get_geography_id(geography): geography
|
||||
|
@ -199,3 +207,9 @@ class AirVisualData:
|
|||
|
||||
_LOGGER.debug("Received new data")
|
||||
async_dispatcher_send(self._hass, TOPIC_UPDATE)
|
||||
|
||||
@callback
|
||||
def async_update_options(self, options):
|
||||
"""Update the data manager's options."""
|
||||
self.options = options
|
||||
async_dispatcher_send(self._hass, TOPIC_UPDATE)
|
||||
|
|
|
@ -6,7 +6,12 @@ from pyairvisual.errors import InvalidKeyError
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
CONF_SHOW_ON_MAP,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import aiohttp_client, config_validation as cv
|
||||
|
||||
|
@ -16,7 +21,7 @@ _LOGGER = logging.getLogger("homeassistant.components.airvisual")
|
|||
|
||||
|
||||
class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a AirVisual config flow."""
|
||||
"""Handle an AirVisual config flow."""
|
||||
|
||||
VERSION = 1
|
||||
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
|
||||
|
@ -48,6 +53,12 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
step_id="user", data_schema=self.cloud_api_schema, errors=errors or {},
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(config_entry):
|
||||
"""Define the config flow to handle options."""
|
||||
return AirVisualOptionsFlowHandler(config_entry)
|
||||
|
||||
async def async_step_import(self, import_config):
|
||||
"""Import a config entry from configuration.yaml."""
|
||||
return await self.async_step_user(import_config)
|
||||
|
@ -85,3 +96,28 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
return self.async_create_entry(
|
||||
title=f"Cloud API (API key: {user_input[CONF_API_KEY][:4]}...)", data=data
|
||||
)
|
||||
|
||||
|
||||
class AirVisualOptionsFlowHandler(config_entries.OptionsFlow):
|
||||
"""Handle an AirVisual options flow."""
|
||||
|
||||
def __init__(self, config_entry):
|
||||
"""Initialize."""
|
||||
self.config_entry = config_entry
|
||||
|
||||
async def async_step_init(self, user_input=None):
|
||||
"""Manage the options."""
|
||||
if user_input is not None:
|
||||
return self.async_create_entry(title="", data=user_input)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="init",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(
|
||||
CONF_SHOW_ON_MAP,
|
||||
default=self.config_entry.options.get(CONF_SHOW_ON_MAP),
|
||||
): bool
|
||||
}
|
||||
),
|
||||
)
|
||||
|
|
|
@ -11,6 +11,7 @@ from homeassistant.const import (
|
|||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
CONF_SHOW_ON_MAP,
|
||||
CONF_STATE,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
|
@ -110,15 +111,6 @@ class AirVisualSensor(Entity):
|
|||
ATTR_COUNTRY: airvisual.data[geography_id].get(CONF_COUNTRY),
|
||||
}
|
||||
|
||||
geography = airvisual.geographies[geography_id]
|
||||
if geography.get(CONF_LATITUDE):
|
||||
if airvisual.show_on_map:
|
||||
self._attrs[ATTR_LATITUDE] = geography[CONF_LATITUDE]
|
||||
self._attrs[ATTR_LONGITUDE] = geography[CONF_LONGITUDE]
|
||||
else:
|
||||
self._attrs["lati"] = geography[CONF_LATITUDE]
|
||||
self._attrs["long"] = geography[CONF_LONGITUDE]
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return True if entity is available."""
|
||||
|
@ -199,6 +191,19 @@ class AirVisualSensor(Entity):
|
|||
}
|
||||
)
|
||||
|
||||
geography = self._airvisual.geographies[self._geography_id]
|
||||
if CONF_LATITUDE in geography:
|
||||
if self._airvisual.options[CONF_SHOW_ON_MAP]:
|
||||
self._attrs[ATTR_LATITUDE] = geography[CONF_LATITUDE]
|
||||
self._attrs[ATTR_LONGITUDE] = geography[CONF_LONGITUDE]
|
||||
self._attrs.pop("lati", None)
|
||||
self._attrs.pop("long", None)
|
||||
else:
|
||||
self._attrs["lati"] = geography[CONF_LATITUDE]
|
||||
self._attrs["long"] = geography[CONF_LONGITUDE]
|
||||
self._attrs.pop(ATTR_LATITUDE, None)
|
||||
self._attrs.pop(ATTR_LONGITUDE, None)
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Disconnect dispatcher listener when removed."""
|
||||
for cancel in self._async_unsub_dispatcher_connects:
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
"data": {
|
||||
"api_key": "API Key",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude",
|
||||
"show_on_map": "Show monitored geography on the map"
|
||||
"longitude": "Longitude"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -19,5 +18,16 @@
|
|||
"abort": {
|
||||
"already_configured": "This API key is already in use."
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"title": "Configure AirVisual",
|
||||
"description": "Set various options for the AirVisual integration.",
|
||||
"data": {
|
||||
"show_on_map": "Show monitored geography on the map"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,12 @@ from pyairvisual.errors import InvalidKeyError
|
|||
from homeassistant import data_entry_flow
|
||||
from homeassistant.components.airvisual import CONF_GEOGRAPHIES, DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER
|
||||
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
CONF_SHOW_ON_MAP,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
@ -37,6 +42,34 @@ async def test_invalid_api_key(hass):
|
|||
assert result["errors"] == {CONF_API_KEY: "invalid_api_key"}
|
||||
|
||||
|
||||
async def test_options_flow(hass):
|
||||
"""Test config flow options."""
|
||||
conf = {CONF_API_KEY: "abcde12345"}
|
||||
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
unique_id="abcde12345",
|
||||
data=conf,
|
||||
options={CONF_SHOW_ON_MAP: True},
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.airvisual.async_setup_entry", return_value=True
|
||||
):
|
||||
result = await hass.config_entries.options.async_init(config_entry.entry_id)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], user_input={CONF_SHOW_ON_MAP: False}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert config_entry.options == {CONF_SHOW_ON_MAP: False}
|
||||
|
||||
|
||||
async def test_show_form(hass):
|
||||
"""Test that the form is served with no input."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
|
@ -49,10 +82,13 @@ async def test_show_form(hass):
|
|||
|
||||
async def test_step_import(hass):
|
||||
"""Test that the import step works."""
|
||||
conf = {CONF_API_KEY: "abcde12345"}
|
||||
conf = {
|
||||
CONF_API_KEY: "abcde12345",
|
||||
CONF_GEOGRAPHIES: [{CONF_LATITUDE: 51.528308, CONF_LONGITUDE: -0.3817765}],
|
||||
}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.wwlln.async_setup_entry", return_value=True
|
||||
"homeassistant.components.airvisual.async_setup_entry", return_value=True
|
||||
), patch("pyairvisual.api.API.nearest_city"):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_IMPORT}, data=conf
|
||||
|
@ -62,7 +98,7 @@ async def test_step_import(hass):
|
|||
assert result["title"] == "Cloud API (API key: abcd...)"
|
||||
assert result["data"] == {
|
||||
CONF_API_KEY: "abcde12345",
|
||||
CONF_GEOGRAPHIES: [{CONF_LATITUDE: 32.87336, CONF_LONGITUDE: -117.22743}],
|
||||
CONF_GEOGRAPHIES: [{CONF_LATITUDE: 51.528308, CONF_LONGITUDE: -0.3817765}],
|
||||
}
|
||||
|
||||
|
||||
|
@ -75,7 +111,7 @@ async def test_step_user(hass):
|
|||
}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.wwlln.async_setup_entry", return_value=True
|
||||
"homeassistant.components.airvisual.async_setup_entry", return_value=True
|
||||
), patch("pyairvisual.api.API.nearest_city"):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}, data=conf
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue