Add unique ID to config entry in Luftdaten (#62176)
This commit is contained in:
parent
b559d8845e
commit
7fe895e554
4 changed files with 122 additions and 97 deletions
|
@ -7,7 +7,6 @@ from luftdaten import Luftdaten
|
|||
from luftdaten.exceptions import LuftdatenError
|
||||
|
||||
from homeassistant.components.sensor import SensorDeviceClass, SensorEntityDescription
|
||||
from homeassistant.config_entries import SOURCE_IMPORT
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
CONF_MONITORED_CONDITIONS,
|
||||
|
@ -18,13 +17,11 @@ from homeassistant.const import (
|
|||
TEMP_CELSIUS,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
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 .config_flow import duplicate_stations
|
||||
from .const import CONF_SENSOR_ID, DEFAULT_SCAN_INTERVAL, DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -83,13 +80,6 @@ SENSOR_KEYS: list[str] = [desc.key for desc in SENSOR_TYPES]
|
|||
CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
|
||||
|
||||
|
||||
@callback
|
||||
def _async_fixup_sensor_id(hass, config_entry, sensor_id):
|
||||
hass.config_entries.async_update_entry(
|
||||
config_entry, data={**config_entry.data, CONF_SENSOR_ID: int(sensor_id)}
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry):
|
||||
"""Set up Luftdaten as config entry."""
|
||||
hass.data.setdefault(
|
||||
|
@ -100,19 +90,11 @@ async def async_setup_entry(hass, config_entry):
|
|||
},
|
||||
)
|
||||
|
||||
if not isinstance(config_entry.data[CONF_SENSOR_ID], int):
|
||||
_async_fixup_sensor_id(hass, config_entry, config_entry.data[CONF_SENSOR_ID])
|
||||
|
||||
if (
|
||||
config_entry.data[CONF_SENSOR_ID] in duplicate_stations(hass)
|
||||
and config_entry.source == SOURCE_IMPORT
|
||||
):
|
||||
_LOGGER.warning(
|
||||
"Removing duplicate sensors for station %s",
|
||||
config_entry.data[CONF_SENSOR_ID],
|
||||
# For backwards compat, set unique ID
|
||||
if config_entry.unique_id is None:
|
||||
hass.config_entries.async_update_entry(
|
||||
config_entry, unique_id=config_entry.data[CONF_SENSOR_ID]
|
||||
)
|
||||
hass.async_create_task(hass.config_entries.async_remove(config_entry.entry_id))
|
||||
return False
|
||||
|
||||
try:
|
||||
luftdaten = LuftDatenData(
|
||||
|
|
|
@ -18,25 +18,6 @@ import homeassistant.helpers.config_validation as cv
|
|||
from .const import CONF_SENSOR_ID, DEFAULT_SCAN_INTERVAL, DOMAIN
|
||||
|
||||
|
||||
@callback
|
||||
def configured_sensors(hass):
|
||||
"""Return a set of configured Luftdaten sensors."""
|
||||
return {
|
||||
entry.data[CONF_SENSOR_ID]
|
||||
for entry in hass.config_entries.async_entries(DOMAIN)
|
||||
}
|
||||
|
||||
|
||||
@callback
|
||||
def duplicate_stations(hass):
|
||||
"""Return a set of duplicate configured Luftdaten stations."""
|
||||
stations = [
|
||||
int(entry.data[CONF_SENSOR_ID])
|
||||
for entry in hass.config_entries.async_entries(DOMAIN)
|
||||
]
|
||||
return {x for x in stations if stations.count(x) > 1}
|
||||
|
||||
|
||||
class LuftDatenFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a Luftdaten config flow."""
|
||||
|
||||
|
@ -59,10 +40,8 @@ class LuftDatenFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
if not user_input:
|
||||
return self._show_form()
|
||||
|
||||
sensor_id = user_input[CONF_SENSOR_ID]
|
||||
|
||||
if sensor_id in configured_sensors(self.hass):
|
||||
return self._show_form({CONF_SENSOR_ID: "already_configured"})
|
||||
await self.async_set_unique_id(str(user_input[CONF_SENSOR_ID]))
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
luftdaten = Luftdaten(user_input[CONF_SENSOR_ID])
|
||||
try:
|
||||
|
@ -86,4 +65,6 @@ class LuftDatenFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
scan_interval = user_input.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL)
|
||||
user_input.update({CONF_SCAN_INTERVAL: scan_interval.total_seconds()})
|
||||
|
||||
return self.async_create_entry(title=str(sensor_id), data=user_input)
|
||||
return self.async_create_entry(
|
||||
title=str(user_input[CONF_SENSOR_ID]), data=user_input
|
||||
)
|
||||
|
|
32
tests/components/luftdaten/conftest.py
Normal file
32
tests/components/luftdaten/conftest.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
"""Fixtures for Luftdaten tests."""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.luftdaten import DOMAIN
|
||||
from homeassistant.components.luftdaten.const import CONF_SENSOR_ID
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_entry() -> MockConfigEntry:
|
||||
"""Return the default mocked config entry."""
|
||||
return MockConfigEntry(
|
||||
title="12345",
|
||||
domain=DOMAIN,
|
||||
data={CONF_SENSOR_ID: 123456},
|
||||
unique_id="12345",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[None, None, None]:
|
||||
"""Mock setting up a config entry."""
|
||||
with patch(
|
||||
"homeassistant.components.luftdaten.async_setup_entry", return_value=True
|
||||
):
|
||||
yield
|
|
@ -1,84 +1,114 @@
|
|||
"""Define tests for the Luftdaten config flow."""
|
||||
from datetime import timedelta
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from homeassistant import data_entry_flow
|
||||
from homeassistant.components.luftdaten import DOMAIN, config_flow
|
||||
from luftdaten.exceptions import LuftdatenConnectionError
|
||||
|
||||
from homeassistant.components.luftdaten import DOMAIN
|
||||
from homeassistant.components.luftdaten.const import CONF_SENSOR_ID
|
||||
from homeassistant.config_entries import SOURCE_USER
|
||||
from homeassistant.const import CONF_SCAN_INTERVAL, CONF_SHOW_ON_MAP
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import (
|
||||
RESULT_TYPE_ABORT,
|
||||
RESULT_TYPE_CREATE_ENTRY,
|
||||
RESULT_TYPE_FORM,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_duplicate_error(hass):
|
||||
async def test_duplicate_error(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test that errors are shown when duplicates are added."""
|
||||
conf = {CONF_SENSOR_ID: "12345abcde"}
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
MockConfigEntry(domain=DOMAIN, data=conf).add_to_hass(hass)
|
||||
flow = config_flow.LuftDatenFlowHandler()
|
||||
flow.hass = hass
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
result = await flow.async_step_user(user_input=conf)
|
||||
assert result["errors"] == {CONF_SENSOR_ID: "already_configured"}
|
||||
assert result.get("type") == RESULT_TYPE_FORM
|
||||
assert result.get("step_id") == SOURCE_USER
|
||||
assert "flow_id" in result
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_SENSOR_ID: 12345},
|
||||
)
|
||||
|
||||
assert result2.get("type") == RESULT_TYPE_ABORT
|
||||
assert result2.get("reason") == "already_configured"
|
||||
|
||||
|
||||
async def test_communication_error(hass):
|
||||
async def test_communication_error(hass: HomeAssistant) -> None:
|
||||
"""Test that no sensor is added while unable to communicate with API."""
|
||||
conf = {CONF_SENSOR_ID: "12345abcde"}
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
flow = config_flow.LuftDatenFlowHandler()
|
||||
flow.hass = hass
|
||||
assert result.get("type") == RESULT_TYPE_FORM
|
||||
assert result.get("step_id") == SOURCE_USER
|
||||
assert "flow_id" in result
|
||||
|
||||
with patch("luftdaten.Luftdaten.get_data", return_value=None):
|
||||
result = await flow.async_step_user(user_input=conf)
|
||||
assert result["errors"] == {CONF_SENSOR_ID: "invalid_sensor"}
|
||||
with patch("luftdaten.Luftdaten.get_data", side_effect=LuftdatenConnectionError):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_SENSOR_ID: 12345},
|
||||
)
|
||||
|
||||
assert result2.get("type") == RESULT_TYPE_FORM
|
||||
assert result2.get("step_id") == SOURCE_USER
|
||||
assert result2.get("errors") == {CONF_SENSOR_ID: "cannot_connect"}
|
||||
|
||||
|
||||
async def test_invalid_sensor(hass):
|
||||
async def test_invalid_sensor(hass: HomeAssistant) -> None:
|
||||
"""Test that an invalid sensor throws an error."""
|
||||
conf = {CONF_SENSOR_ID: "12345abcde"}
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
flow = config_flow.LuftDatenFlowHandler()
|
||||
flow.hass = hass
|
||||
assert result.get("type") == RESULT_TYPE_FORM
|
||||
assert result.get("step_id") == SOURCE_USER
|
||||
assert "flow_id" in result
|
||||
|
||||
with patch("luftdaten.Luftdaten.get_data", return_value=False), patch(
|
||||
"luftdaten.Luftdaten.validate_sensor", return_value=False
|
||||
):
|
||||
result = await flow.async_step_user(user_input=conf)
|
||||
assert result["errors"] == {CONF_SENSOR_ID: "invalid_sensor"}
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_SENSOR_ID: 12345},
|
||||
)
|
||||
|
||||
assert result2.get("type") == RESULT_TYPE_FORM
|
||||
assert result2.get("step_id") == SOURCE_USER
|
||||
assert result2.get("errors") == {CONF_SENSOR_ID: "invalid_sensor"}
|
||||
|
||||
|
||||
async def test_show_form(hass):
|
||||
"""Test that the form is served with no input."""
|
||||
flow = config_flow.LuftDatenFlowHandler()
|
||||
flow.hass = hass
|
||||
|
||||
result = await flow.async_step_user(user_input=None)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
|
||||
async def test_step_user(hass):
|
||||
async def test_step_user(hass: HomeAssistant, mock_setup_entry: MagicMock) -> None:
|
||||
"""Test that the user step works."""
|
||||
conf = {
|
||||
CONF_SENSOR_ID: "12345abcde",
|
||||
CONF_SHOW_ON_MAP: False,
|
||||
CONF_SCAN_INTERVAL: timedelta(minutes=5),
|
||||
}
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
flow = config_flow.LuftDatenFlowHandler()
|
||||
flow.hass = hass
|
||||
assert result.get("type") == RESULT_TYPE_FORM
|
||||
assert result.get("step_id") == SOURCE_USER
|
||||
assert "flow_id" in result
|
||||
|
||||
with patch("luftdaten.Luftdaten.get_data", return_value=True), patch(
|
||||
"luftdaten.Luftdaten.validate_sensor", return_value=True
|
||||
):
|
||||
result = await flow.async_step_user(user_input=conf)
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
CONF_SENSOR_ID: 12345,
|
||||
CONF_SHOW_ON_MAP: False,
|
||||
},
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == "12345abcde"
|
||||
assert result["data"] == {
|
||||
CONF_SENSOR_ID: "12345abcde",
|
||||
CONF_SHOW_ON_MAP: False,
|
||||
CONF_SCAN_INTERVAL: 300,
|
||||
}
|
||||
assert result2.get("type") == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result2.get("title") == "12345"
|
||||
assert result2.get("data") == {
|
||||
CONF_SENSOR_ID: 12345,
|
||||
CONF_SHOW_ON_MAP: False,
|
||||
CONF_SCAN_INTERVAL: 600.0,
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue