mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 09:47:52 +00:00
Add optional location based region to dwd_weather_warnings (#96027)
* Add device tracker option * Update const name to be more understandable * Clean up sensor code * Clean up init and coordinator * Add tests and update util function and it's usage * Switch to using the registry entry and add tests * Clean up code * Consolidate duplicate code and adjust tests * Fix runtime error * Fix blocking of the event loop * Adjust API object handling * Update homeassistant/components/dwd_weather_warnings/exceptions.py * Optimize coordinator data update --------- Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
parent
e29b301dd1
commit
70d4b4d20d
@ -2,23 +2,16 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dwdwfsapi import DwdWeatherWarningsAPI
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import CONF_REGION_IDENTIFIER, DOMAIN, PLATFORMS
|
||||
from .const import DOMAIN, PLATFORMS
|
||||
from .coordinator import DwdWeatherWarningsCoordinator
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up a config entry."""
|
||||
region_identifier: str = entry.data[CONF_REGION_IDENTIFIER]
|
||||
|
||||
# Initialize the API and coordinator.
|
||||
api = await hass.async_add_executor_job(DwdWeatherWarningsAPI, region_identifier)
|
||||
coordinator = DwdWeatherWarningsCoordinator(hass, api)
|
||||
|
||||
coordinator = DwdWeatherWarningsCoordinator(hass, entry)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
|
@ -8,9 +8,15 @@ from dwdwfsapi import DwdWeatherWarningsAPI
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.selector import EntitySelector, EntitySelectorConfig
|
||||
|
||||
from .const import CONF_REGION_IDENTIFIER, DOMAIN
|
||||
from .const import CONF_REGION_DEVICE_TRACKER, CONF_REGION_IDENTIFIER, DOMAIN
|
||||
from .exceptions import EntityNotFoundError
|
||||
from .util import get_position_data
|
||||
|
||||
EXCLUSIVE_OPTIONS = (CONF_REGION_IDENTIFIER, CONF_REGION_DEVICE_TRACKER)
|
||||
|
||||
|
||||
class DwdWeatherWarningsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
@ -25,27 +31,70 @@ class DwdWeatherWarningsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
errors: dict = {}
|
||||
|
||||
if user_input is not None:
|
||||
region_identifier = user_input[CONF_REGION_IDENTIFIER]
|
||||
# Check, if either CONF_REGION_IDENTIFIER or CONF_GPS_TRACKER has been set.
|
||||
if all(k not in user_input for k in EXCLUSIVE_OPTIONS):
|
||||
errors["base"] = "no_identifier"
|
||||
elif all(k in user_input for k in EXCLUSIVE_OPTIONS):
|
||||
errors["base"] = "ambiguous_identifier"
|
||||
elif CONF_REGION_IDENTIFIER in user_input:
|
||||
# Validate region identifier using the API
|
||||
identifier = user_input[CONF_REGION_IDENTIFIER]
|
||||
|
||||
# Validate region identifier using the API
|
||||
if not await self.hass.async_add_executor_job(
|
||||
DwdWeatherWarningsAPI, region_identifier
|
||||
):
|
||||
errors["base"] = "invalid_identifier"
|
||||
if not await self.hass.async_add_executor_job(
|
||||
DwdWeatherWarningsAPI, identifier
|
||||
):
|
||||
errors["base"] = "invalid_identifier"
|
||||
|
||||
if not errors:
|
||||
# Set the unique ID for this config entry.
|
||||
await self.async_set_unique_id(region_identifier)
|
||||
self._abort_if_unique_id_configured()
|
||||
if not errors:
|
||||
# Set the unique ID for this config entry.
|
||||
await self.async_set_unique_id(identifier)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
return self.async_create_entry(title=region_identifier, data=user_input)
|
||||
return self.async_create_entry(title=identifier, data=user_input)
|
||||
else: # CONF_REGION_DEVICE_TRACKER
|
||||
device_tracker = user_input[CONF_REGION_DEVICE_TRACKER]
|
||||
registry = er.async_get(self.hass)
|
||||
entity_entry = registry.async_get(device_tracker)
|
||||
|
||||
if entity_entry is None:
|
||||
errors["base"] = "entity_not_found"
|
||||
else:
|
||||
try:
|
||||
position = get_position_data(self.hass, entity_entry.id)
|
||||
except EntityNotFoundError:
|
||||
errors["base"] = "entity_not_found"
|
||||
except AttributeError:
|
||||
errors["base"] = "attribute_not_found"
|
||||
else:
|
||||
# Validate position using the API
|
||||
if not await self.hass.async_add_executor_job(
|
||||
DwdWeatherWarningsAPI, position
|
||||
):
|
||||
errors["base"] = "invalid_identifier"
|
||||
|
||||
# Position is valid here, because the API call was successful.
|
||||
if not errors and position is not None and entity_entry is not None:
|
||||
# Set the unique ID for this config entry.
|
||||
await self.async_set_unique_id(entity_entry.id)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
# Replace entity ID with registry ID for more stability.
|
||||
user_input[CONF_REGION_DEVICE_TRACKER] = entity_entry.id
|
||||
|
||||
return self.async_create_entry(
|
||||
title=device_tracker.removeprefix("device_tracker."),
|
||||
data=user_input,
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
errors=errors,
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_REGION_IDENTIFIER): cv.string,
|
||||
vol.Optional(CONF_REGION_IDENTIFIER): cv.string,
|
||||
vol.Optional(CONF_REGION_DEVICE_TRACKER): EntitySelector(
|
||||
EntitySelectorConfig(domain="device_tracker")
|
||||
),
|
||||
}
|
||||
),
|
||||
)
|
||||
|
@ -14,6 +14,7 @@ DOMAIN: Final = "dwd_weather_warnings"
|
||||
|
||||
CONF_REGION_NAME: Final = "region_name"
|
||||
CONF_REGION_IDENTIFIER: Final = "region_identifier"
|
||||
CONF_REGION_DEVICE_TRACKER: Final = "region_device_tracker"
|
||||
|
||||
ATTR_REGION_NAME: Final = "region_name"
|
||||
ATTR_REGION_ID: Final = "region_id"
|
||||
|
@ -4,23 +4,79 @@ from __future__ import annotations
|
||||
|
||||
from dwdwfsapi import DwdWeatherWarningsAPI
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
from homeassistant.util import location
|
||||
|
||||
from .const import DEFAULT_SCAN_INTERVAL, DOMAIN, LOGGER
|
||||
from .const import (
|
||||
CONF_REGION_DEVICE_TRACKER,
|
||||
CONF_REGION_IDENTIFIER,
|
||||
DEFAULT_SCAN_INTERVAL,
|
||||
DOMAIN,
|
||||
LOGGER,
|
||||
)
|
||||
from .exceptions import EntityNotFoundError
|
||||
from .util import get_position_data
|
||||
|
||||
|
||||
class DwdWeatherWarningsCoordinator(DataUpdateCoordinator[None]):
|
||||
"""Custom coordinator for the dwd_weather_warnings integration."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant, api: DwdWeatherWarningsAPI) -> None:
|
||||
config_entry: ConfigEntry
|
||||
api: DwdWeatherWarningsAPI
|
||||
|
||||
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Initialize the dwd_weather_warnings coordinator."""
|
||||
super().__init__(
|
||||
hass, LOGGER, name=DOMAIN, update_interval=DEFAULT_SCAN_INTERVAL
|
||||
)
|
||||
|
||||
self.api = api
|
||||
self._device_tracker = None
|
||||
self._previous_position = None
|
||||
|
||||
async def async_config_entry_first_refresh(self) -> None:
|
||||
"""Perform first refresh."""
|
||||
if region_identifier := self.config_entry.data.get(CONF_REGION_IDENTIFIER):
|
||||
self.api = await self.hass.async_add_executor_job(
|
||||
DwdWeatherWarningsAPI, region_identifier
|
||||
)
|
||||
else:
|
||||
self._device_tracker = self.config_entry.data.get(
|
||||
CONF_REGION_DEVICE_TRACKER
|
||||
)
|
||||
|
||||
await super().async_config_entry_first_refresh()
|
||||
|
||||
async def _async_update_data(self) -> None:
|
||||
"""Get the latest data from the DWD Weather Warnings API."""
|
||||
await self.hass.async_add_executor_job(self.api.update)
|
||||
if self._device_tracker:
|
||||
try:
|
||||
position = get_position_data(self.hass, self._device_tracker)
|
||||
except (EntityNotFoundError, AttributeError) as err:
|
||||
raise UpdateFailed(f"Error fetching position: {repr(err)}") from err
|
||||
|
||||
distance = None
|
||||
if self._previous_position is not None:
|
||||
distance = location.distance(
|
||||
self._previous_position[0],
|
||||
self._previous_position[1],
|
||||
position[0],
|
||||
position[1],
|
||||
)
|
||||
|
||||
if distance is None or distance > 50:
|
||||
# Only create a new object on the first update
|
||||
# or when the distance to the previous position
|
||||
# changes by more than 50 meters (to take GPS
|
||||
# inaccuracy into account).
|
||||
self.api = await self.hass.async_add_executor_job(
|
||||
DwdWeatherWarningsAPI, position
|
||||
)
|
||||
else:
|
||||
# Otherwise update the API to check for new warnings.
|
||||
await self.hass.async_add_executor_job(self.api.update)
|
||||
|
||||
self._previous_position = position
|
||||
else:
|
||||
await self.hass.async_add_executor_job(self.api.update)
|
||||
|
@ -0,0 +1,7 @@
|
||||
"""Exceptions for the dwd_weather_warnings integration."""
|
||||
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
|
||||
class EntityNotFoundError(HomeAssistantError):
|
||||
"""When a referenced entity was not found."""
|
@ -11,6 +11,8 @@ Wetterwarnungen (Stufe 1)
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
@ -93,29 +95,27 @@ class DwdWeatherWarningsSensor(
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
)
|
||||
|
||||
self.api = coordinator.api
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
def native_value(self) -> int | None:
|
||||
"""Return the state of the sensor."""
|
||||
if self.entity_description.key == CURRENT_WARNING_SENSOR:
|
||||
return self.api.current_warning_level
|
||||
return self.coordinator.api.current_warning_level
|
||||
|
||||
return self.api.expected_warning_level
|
||||
return self.coordinator.api.expected_warning_level
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
def extra_state_attributes(self) -> dict[str, Any]:
|
||||
"""Return the state attributes of the sensor."""
|
||||
data = {
|
||||
ATTR_REGION_NAME: self.api.warncell_name,
|
||||
ATTR_REGION_ID: self.api.warncell_id,
|
||||
ATTR_LAST_UPDATE: self.api.last_update,
|
||||
ATTR_REGION_NAME: self.coordinator.api.warncell_name,
|
||||
ATTR_REGION_ID: self.coordinator.api.warncell_id,
|
||||
ATTR_LAST_UPDATE: self.coordinator.api.last_update,
|
||||
}
|
||||
|
||||
if self.entity_description.key == CURRENT_WARNING_SENSOR:
|
||||
searched_warnings = self.api.current_warnings
|
||||
searched_warnings = self.coordinator.api.current_warnings
|
||||
else:
|
||||
searched_warnings = self.api.expected_warnings
|
||||
searched_warnings = self.coordinator.api.expected_warnings
|
||||
|
||||
data[ATTR_WARNING_COUNT] = len(searched_warnings)
|
||||
|
||||
@ -142,4 +142,4 @@ class DwdWeatherWarningsSensor(
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Could the device be accessed during the last update call."""
|
||||
return self.api.data_valid
|
||||
return self.coordinator.api.data_valid
|
||||
|
@ -2,17 +2,22 @@
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "To identify the desired region, the warncell ID / name is required.",
|
||||
"description": "To identify the desired region, either the warncell ID / name or device tracker is required. The provided device tracker has to contain the attributes 'latitude' and 'longitude'.",
|
||||
"data": {
|
||||
"region_identifier": "Warncell ID or name"
|
||||
"region_identifier": "Warncell ID or name",
|
||||
"region_device_tracker": "Device tracker entity"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"invalid_identifier": "The specified region identifier is invalid."
|
||||
"no_identifier": "Either the region identifier or device tracker is required.",
|
||||
"ambiguous_identifier": "The region identifier and device tracker can not be specified together.",
|
||||
"invalid_identifier": "The specified region identifier / device tracker is invalid.",
|
||||
"entity_not_found": "The specified device tracker entity was not found.",
|
||||
"attribute_not_found": "The required `latitude` or `longitude` attribute was not found in the specified device tracker."
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "Warncell ID / name is already configured.",
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"invalid_identifier": "[%key:component::dwd_weather_warnings::config::error::invalid_identifier%]"
|
||||
}
|
||||
},
|
||||
|
39
homeassistant/components/dwd_weather_warnings/util.py
Normal file
39
homeassistant/components/dwd_weather_warnings/util.py
Normal file
@ -0,0 +1,39 @@
|
||||
"""Util functions for the dwd_weather_warnings integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .exceptions import EntityNotFoundError
|
||||
|
||||
|
||||
def get_position_data(
|
||||
hass: HomeAssistant, registry_id: str
|
||||
) -> tuple[float, float] | None:
|
||||
"""Extract longitude and latitude from a device tracker."""
|
||||
registry = er.async_get(hass)
|
||||
registry_entry = registry.async_get(registry_id)
|
||||
if registry_entry is None:
|
||||
raise EntityNotFoundError(f"Failed to find registry entry {registry_id}")
|
||||
|
||||
entity = hass.states.get(registry_entry.entity_id)
|
||||
if entity is None:
|
||||
raise EntityNotFoundError(f"Failed to find entity {registry_entry.entity_id}")
|
||||
|
||||
latitude = entity.attributes.get(ATTR_LATITUDE)
|
||||
if not latitude:
|
||||
raise AttributeError(
|
||||
f"Failed to find attribute '{ATTR_LATITUDE}' in {registry_entry.entity_id}",
|
||||
ATTR_LATITUDE,
|
||||
)
|
||||
|
||||
longitude = entity.attributes.get(ATTR_LONGITUDE)
|
||||
if not longitude:
|
||||
raise AttributeError(
|
||||
f"Failed to find attribute '{ATTR_LONGITUDE}' in {registry_entry.entity_id}",
|
||||
ATTR_LONGITUDE,
|
||||
)
|
||||
|
||||
return (latitude, longitude)
|
@ -6,34 +6,31 @@ from unittest.mock import patch
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.dwd_weather_warnings.const import (
|
||||
ADVANCE_WARNING_SENSOR,
|
||||
CONF_REGION_DEVICE_TRACKER,
|
||||
CONF_REGION_IDENTIFIER,
|
||||
CONF_REGION_NAME,
|
||||
CURRENT_WARNING_SENSOR,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_USER
|
||||
from homeassistant.const import CONF_MONITORED_CONDITIONS, CONF_NAME
|
||||
from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE, STATE_HOME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
DEMO_CONFIG_ENTRY: Final = {
|
||||
DEMO_CONFIG_ENTRY_REGION: Final = {
|
||||
CONF_REGION_IDENTIFIER: "807111000",
|
||||
}
|
||||
|
||||
DEMO_YAML_CONFIGURATION: Final = {
|
||||
CONF_NAME: "Unit Test",
|
||||
CONF_REGION_NAME: "807111000",
|
||||
CONF_MONITORED_CONDITIONS: [CURRENT_WARNING_SENSOR, ADVANCE_WARNING_SENSOR],
|
||||
DEMO_CONFIG_ENTRY_GPS: Final = {
|
||||
CONF_REGION_DEVICE_TRACKER: "device_tracker.test_gps",
|
||||
}
|
||||
|
||||
pytestmark = pytest.mark.usefixtures("mock_setup_entry")
|
||||
|
||||
|
||||
async def test_create_entry(hass: HomeAssistant) -> None:
|
||||
"""Test that the full config flow works."""
|
||||
async def test_create_entry_region(hass: HomeAssistant) -> None:
|
||||
"""Test that the full config flow works for a region identifier."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
@ -45,7 +42,7 @@ async def test_create_entry(hass: HomeAssistant) -> None:
|
||||
return_value=False,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=DEMO_CONFIG_ENTRY
|
||||
result["flow_id"], user_input=DEMO_CONFIG_ENTRY_REGION
|
||||
)
|
||||
|
||||
# Test for invalid region identifier.
|
||||
@ -58,7 +55,7 @@ async def test_create_entry(hass: HomeAssistant) -> None:
|
||||
return_value=True,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=DEMO_CONFIG_ENTRY
|
||||
result["flow_id"], user_input=DEMO_CONFIG_ENTRY_REGION
|
||||
)
|
||||
|
||||
# Test for successfully created entry.
|
||||
@ -70,12 +67,95 @@ async def test_create_entry(hass: HomeAssistant) -> None:
|
||||
}
|
||||
|
||||
|
||||
async def test_create_entry_gps(
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
||||
) -> None:
|
||||
"""Test that the full config flow works for a device tracker."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
|
||||
# Test for missing registry entry error.
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=DEMO_CONFIG_ENTRY_GPS
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "entity_not_found"}
|
||||
|
||||
# Test for missing device tracker error.
|
||||
registry_entry = entity_registry.async_get_or_create(
|
||||
"device_tracker", DOMAIN, "uuid", suggested_object_id="test_gps"
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=DEMO_CONFIG_ENTRY_GPS
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "entity_not_found"}
|
||||
|
||||
# Test for missing attribute error.
|
||||
hass.states.async_set(
|
||||
DEMO_CONFIG_ENTRY_GPS[CONF_REGION_DEVICE_TRACKER],
|
||||
STATE_HOME,
|
||||
{ATTR_LONGITUDE: "7.610263"},
|
||||
)
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=DEMO_CONFIG_ENTRY_GPS
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "attribute_not_found"}
|
||||
|
||||
# Test for invalid provided identifier.
|
||||
hass.states.async_set(
|
||||
DEMO_CONFIG_ENTRY_GPS[CONF_REGION_DEVICE_TRACKER],
|
||||
STATE_HOME,
|
||||
{ATTR_LATITUDE: "50.180454", ATTR_LONGITUDE: "7.610263"},
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.dwd_weather_warnings.config_flow.DwdWeatherWarningsAPI",
|
||||
return_value=False,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=DEMO_CONFIG_ENTRY_GPS
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "invalid_identifier"}
|
||||
|
||||
# Test for successfully created entry.
|
||||
with patch(
|
||||
"homeassistant.components.dwd_weather_warnings.config_flow.DwdWeatherWarningsAPI",
|
||||
return_value=True,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=DEMO_CONFIG_ENTRY_GPS
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "test_gps"
|
||||
assert result["data"] == {
|
||||
CONF_REGION_DEVICE_TRACKER: registry_entry.id,
|
||||
}
|
||||
|
||||
|
||||
async def test_config_flow_already_configured(hass: HomeAssistant) -> None:
|
||||
"""Test aborting, if the warncell ID / name is already configured during the config."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=DEMO_CONFIG_ENTRY.copy(),
|
||||
unique_id=DEMO_CONFIG_ENTRY[CONF_REGION_IDENTIFIER],
|
||||
data=DEMO_CONFIG_ENTRY_REGION.copy(),
|
||||
unique_id=DEMO_CONFIG_ENTRY_REGION[CONF_REGION_IDENTIFIER],
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
@ -92,9 +172,40 @@ async def test_config_flow_already_configured(hass: HomeAssistant) -> None:
|
||||
return_value=True,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=DEMO_CONFIG_ENTRY
|
||||
result["flow_id"], user_input=DEMO_CONFIG_ENTRY_REGION
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert result["type"] is FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_config_flow_with_errors(hass: HomeAssistant) -> None:
|
||||
"""Test error scenarios during the configuration."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
|
||||
# Test error for empty input data.
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "no_identifier"}
|
||||
|
||||
# Test error for setting both options during configuration.
|
||||
demo_input = DEMO_CONFIG_ENTRY_REGION.copy()
|
||||
demo_input.update(DEMO_CONFIG_ENTRY_GPS.copy())
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=demo_input,
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
assert result["errors"] == {"base": "ambiguous_identifier"}
|
||||
|
@ -4,26 +4,40 @@ from typing import Final
|
||||
|
||||
from homeassistant.components.dwd_weather_warnings.const import (
|
||||
ADVANCE_WARNING_SENSOR,
|
||||
CONF_REGION_DEVICE_TRACKER,
|
||||
CONF_REGION_IDENTIFIER,
|
||||
CURRENT_WARNING_SENSOR,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import CONF_MONITORED_CONDITIONS, CONF_NAME
|
||||
from homeassistant.const import (
|
||||
ATTR_LATITUDE,
|
||||
ATTR_LONGITUDE,
|
||||
CONF_MONITORED_CONDITIONS,
|
||||
CONF_NAME,
|
||||
STATE_HOME,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
DEMO_CONFIG_ENTRY: Final = {
|
||||
DEMO_IDENTIFIER_CONFIG_ENTRY: Final = {
|
||||
CONF_NAME: "Unit Test",
|
||||
CONF_REGION_IDENTIFIER: "807111000",
|
||||
CONF_MONITORED_CONDITIONS: [CURRENT_WARNING_SENSOR, ADVANCE_WARNING_SENSOR],
|
||||
}
|
||||
|
||||
DEMO_TRACKER_CONFIG_ENTRY: Final = {
|
||||
CONF_NAME: "Unit Test",
|
||||
CONF_REGION_DEVICE_TRACKER: "device_tracker.test_gps",
|
||||
CONF_MONITORED_CONDITIONS: [CURRENT_WARNING_SENSOR, ADVANCE_WARNING_SENSOR],
|
||||
}
|
||||
|
||||
|
||||
async def test_load_unload_entry(hass: HomeAssistant) -> None:
|
||||
"""Test loading and unloading the integration."""
|
||||
entry = MockConfigEntry(domain=DOMAIN, data=DEMO_CONFIG_ENTRY)
|
||||
"""Test loading and unloading the integration with a region identifier based entry."""
|
||||
entry = MockConfigEntry(domain=DOMAIN, data=DEMO_IDENTIFIER_CONFIG_ENTRY)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
@ -36,3 +50,68 @@ async def test_load_unload_entry(hass: HomeAssistant) -> None:
|
||||
|
||||
assert entry.state is ConfigEntryState.NOT_LOADED
|
||||
assert entry.entry_id not in hass.data[DOMAIN]
|
||||
|
||||
|
||||
async def test_load_invalid_registry_entry(hass: HomeAssistant) -> None:
|
||||
"""Test loading the integration with an invalid registry entry ID."""
|
||||
INVALID_DATA = DEMO_TRACKER_CONFIG_ENTRY.copy()
|
||||
INVALID_DATA[CONF_REGION_DEVICE_TRACKER] = "invalid_registry_id"
|
||||
entry = MockConfigEntry(domain=DOMAIN, data=INVALID_DATA)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert entry.state == ConfigEntryState.SETUP_RETRY
|
||||
|
||||
|
||||
async def test_load_missing_device_tracker(hass: HomeAssistant) -> None:
|
||||
"""Test loading the integration with a missing device tracker."""
|
||||
entry = MockConfigEntry(domain=DOMAIN, data=DEMO_TRACKER_CONFIG_ENTRY)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert entry.state == ConfigEntryState.SETUP_RETRY
|
||||
|
||||
|
||||
async def test_load_missing_required_attribute(hass: HomeAssistant) -> None:
|
||||
"""Test loading the integration with a device tracker missing a required attribute."""
|
||||
entry = MockConfigEntry(domain=DOMAIN, data=DEMO_TRACKER_CONFIG_ENTRY)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
hass.states.async_set(
|
||||
DEMO_TRACKER_CONFIG_ENTRY[CONF_REGION_DEVICE_TRACKER],
|
||||
STATE_HOME,
|
||||
{ATTR_LONGITUDE: "7.610263"},
|
||||
)
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert entry.state == ConfigEntryState.SETUP_RETRY
|
||||
|
||||
|
||||
async def test_load_valid_device_tracker(
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
||||
) -> None:
|
||||
"""Test loading the integration with a valid device tracker based entry."""
|
||||
entry = MockConfigEntry(domain=DOMAIN, data=DEMO_TRACKER_CONFIG_ENTRY)
|
||||
entry.add_to_hass(hass)
|
||||
entity_registry.async_get_or_create(
|
||||
"device_tracker",
|
||||
entry.domain,
|
||||
"uuid",
|
||||
suggested_object_id="test_gps",
|
||||
config_entry=entry,
|
||||
)
|
||||
|
||||
hass.states.async_set(
|
||||
DEMO_TRACKER_CONFIG_ENTRY[CONF_REGION_DEVICE_TRACKER],
|
||||
STATE_HOME,
|
||||
{ATTR_LATITUDE: "50.180454", ATTR_LONGITUDE: "7.610263"},
|
||||
)
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.state == ConfigEntryState.LOADED
|
||||
assert entry.entry_id in hass.data[DOMAIN]
|
||||
|
Loading…
x
Reference in New Issue
Block a user