Add options flow for Recollect Waste (#44234)
* Add options flow for Recollect Waste * Add test * Typing * Typing * Typing AGAIN * Add missing type hints * Code review * Code review * Don't need to block until donepull/37800/head
parent
fbc695e5cf
commit
60ecc8282c
|
@ -14,6 +14,8 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
|||
|
||||
from .const import CONF_PLACE_ID, CONF_SERVICE_ID, DATA_COORDINATOR, DOMAIN, LOGGER
|
||||
|
||||
DATA_LISTENER = "listener"
|
||||
|
||||
DEFAULT_NAME = "recollect_waste"
|
||||
DEFAULT_UPDATE_INTERVAL = timedelta(days=1)
|
||||
|
||||
|
@ -22,7 +24,7 @@ PLATFORMS = ["sensor"]
|
|||
|
||||
async def async_setup(hass: HomeAssistant, config: dict) -> bool:
|
||||
"""Set up the RainMachine component."""
|
||||
hass.data[DOMAIN] = {DATA_COORDINATOR: {}}
|
||||
hass.data[DOMAIN] = {DATA_COORDINATOR: {}, DATA_LISTENER: {}}
|
||||
return True
|
||||
|
||||
|
||||
|
@ -64,9 +66,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
hass.config_entries.async_forward_entry_setup(entry, component)
|
||||
)
|
||||
|
||||
hass.data[DOMAIN][DATA_LISTENER][entry.entry_id] = entry.add_update_listener(
|
||||
async_reload_entry
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
"""Handle an options update."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload an RainMachine config entry."""
|
||||
unload_ok = all(
|
||||
|
@ -79,5 +90,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
)
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN][DATA_COORDINATOR].pop(entry.entry_id)
|
||||
cancel_listener = hass.data[DOMAIN][DATA_LISTENER].pop(entry.entry_id)
|
||||
cancel_listener()
|
||||
|
||||
return unload_ok
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
"""Config flow for ReCollect Waste integration."""
|
||||
from typing import Optional
|
||||
|
||||
from aiorecollect.client import Client
|
||||
from aiorecollect.errors import RecollectError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_FRIENDLY_NAME
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
|
||||
from .const import ( # pylint:disable=unused-import
|
||||
|
@ -24,6 +28,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
VERSION = 1
|
||||
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
config_entry: config_entries.ConfigEntry,
|
||||
) -> config_entries.OptionsFlow:
|
||||
"""Define the config flow to handle options."""
|
||||
return RecollectWasteOptionsFlowHandler(config_entry)
|
||||
|
||||
async def async_step_import(self, import_config: dict = None) -> dict:
|
||||
"""Handle configuration via YAML import."""
|
||||
return await self.async_step_user(import_config)
|
||||
|
@ -62,3 +74,28 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
CONF_SERVICE_ID: user_input[CONF_SERVICE_ID],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class RecollectWasteOptionsFlowHandler(config_entries.OptionsFlow):
|
||||
"""Handle a Recollect Waste options flow."""
|
||||
|
||||
def __init__(self, entry: config_entries.ConfigEntry):
|
||||
"""Initialize."""
|
||||
self._entry = entry
|
||||
|
||||
async def async_step_init(self, user_input: Optional[dict] = 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.Optional(
|
||||
CONF_FRIENDLY_NAME,
|
||||
default=self._entry.options.get(CONF_FRIENDLY_NAME),
|
||||
): bool
|
||||
}
|
||||
),
|
||||
)
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
"""Support for ReCollect Waste sensors."""
|
||||
from typing import Callable
|
||||
from typing import Callable, List
|
||||
|
||||
from aiorecollect.client import PickupType
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
from homeassistant.const import ATTR_ATTRIBUTION
|
||||
from homeassistant.const import ATTR_ATTRIBUTION, CONF_FRIENDLY_NAME
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
|
@ -35,13 +36,26 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
|||
)
|
||||
|
||||
|
||||
@callback
|
||||
def async_get_pickup_type_names(
|
||||
entry: ConfigEntry, pickup_types: List[PickupType]
|
||||
) -> List[str]:
|
||||
"""Return proper pickup type names from their associated objects."""
|
||||
return [
|
||||
t.friendly_name
|
||||
if entry.options.get(CONF_FRIENDLY_NAME) and t.friendly_name
|
||||
else t.name
|
||||
for t in pickup_types
|
||||
]
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
hass: HomeAssistant,
|
||||
config: dict,
|
||||
async_add_entities: Callable,
|
||||
discovery_info: dict = None,
|
||||
):
|
||||
"""Import Awair configuration from YAML."""
|
||||
"""Import Recollect Waste configuration from YAML."""
|
||||
LOGGER.warning(
|
||||
"Loading ReCollect Waste via platform setup is deprecated. "
|
||||
"Please remove it from your configuration."
|
||||
|
@ -70,8 +84,7 @@ class ReCollectWasteSensor(CoordinatorEntity):
|
|||
"""Initialize the sensor."""
|
||||
super().__init__(coordinator)
|
||||
self._attributes = {ATTR_ATTRIBUTION: DEFAULT_ATTRIBUTION}
|
||||
self._place_id = entry.data[CONF_PLACE_ID]
|
||||
self._service_id = entry.data[CONF_SERVICE_ID]
|
||||
self._entry = entry
|
||||
self._state = None
|
||||
|
||||
@property
|
||||
|
@ -97,7 +110,7 @@ class ReCollectWasteSensor(CoordinatorEntity):
|
|||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return a unique ID."""
|
||||
return f"{self._place_id}{self._service_id}"
|
||||
return f"{self._entry.data[CONF_PLACE_ID]}{self._entry.data[CONF_SERVICE_ID]}"
|
||||
|
||||
@callback
|
||||
def _handle_coordinator_update(self) -> None:
|
||||
|
@ -120,11 +133,13 @@ class ReCollectWasteSensor(CoordinatorEntity):
|
|||
self._state = pickup_event.date
|
||||
self._attributes.update(
|
||||
{
|
||||
ATTR_PICKUP_TYPES: [t.name for t in pickup_event.pickup_types],
|
||||
ATTR_PICKUP_TYPES: async_get_pickup_type_names(
|
||||
self._entry, pickup_event.pickup_types
|
||||
),
|
||||
ATTR_AREA_NAME: pickup_event.area_name,
|
||||
ATTR_NEXT_PICKUP_TYPES: [
|
||||
t.name for t in next_pickup_event.pickup_types
|
||||
],
|
||||
ATTR_NEXT_PICKUP_TYPES: async_get_pickup_type_names(
|
||||
self._entry, next_pickup_event.pickup_types
|
||||
),
|
||||
ATTR_NEXT_PICKUP_DATE: next_date,
|
||||
}
|
||||
)
|
||||
|
|
|
@ -14,5 +14,15 @@
|
|||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"title": "Configure Recollect Waste",
|
||||
"data": {
|
||||
"friendly_name": "Use friendly names for pickup types (when possible)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,5 +14,15 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"friendly_name": "Use friendly names for pickup types (when possible)"
|
||||
},
|
||||
"title": "Configure Recollect Waste"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ from homeassistant.components.recollect_waste import (
|
|||
DOMAIN,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER
|
||||
from homeassistant.const import CONF_FRIENDLY_NAME
|
||||
|
||||
from tests.async_mock import patch
|
||||
from tests.common import MockConfigEntry
|
||||
|
@ -45,6 +46,30 @@ async def test_invalid_place_or_service_id(hass):
|
|||
assert result["errors"] == {"base": "invalid_place_or_service_id"}
|
||||
|
||||
|
||||
async def test_options_flow(hass):
|
||||
"""Test config flow options."""
|
||||
conf = {CONF_PLACE_ID: "12345", CONF_SERVICE_ID: "12345"}
|
||||
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, unique_id="12345, 12345", data=conf)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.recollect_waste.async_setup_entry", return_value=True
|
||||
):
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
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_FRIENDLY_NAME: True}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert config_entry.options == {CONF_FRIENDLY_NAME: True}
|
||||
|
||||
|
||||
async def test_show_form(hass):
|
||||
"""Test that the form is served with no input."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
|
|
Loading…
Reference in New Issue