mirror of
https://github.com/home-assistant/core.git
synced 2026-05-07 10:26:51 +02:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 063a843f4c | |||
| 2dce45888c | |||
| 9b514a4cb1 |
@@ -2,7 +2,7 @@
|
||||
|
||||
import logging
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.const import CONF_NAME, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
@@ -61,6 +61,29 @@ async def async_unload_entry(
|
||||
return await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)
|
||||
|
||||
|
||||
async def async_migrate_entry(
|
||||
hass: HomeAssistant, config_entry: MetWeatherConfigEntry
|
||||
) -> bool:
|
||||
"""Migrate old config entry."""
|
||||
if config_entry.version > 1:
|
||||
return False
|
||||
|
||||
if config_entry.version == 1 and config_entry.minor_version < 2:
|
||||
data = dict(config_entry.data)
|
||||
title = config_entry.title
|
||||
if name := data.pop(CONF_NAME, None):
|
||||
title = name
|
||||
|
||||
hass.config_entries.async_update_entry(
|
||||
config_entry,
|
||||
data=data,
|
||||
title=title,
|
||||
minor_version=2,
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def cleanup_old_device(hass: HomeAssistant) -> None:
|
||||
"""Cleanup device without proper device identifier."""
|
||||
device_reg = dr.async_get(hass)
|
||||
|
||||
@@ -14,7 +14,6 @@ from homeassistant.const import (
|
||||
CONF_ELEVATION,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
CONF_NAME,
|
||||
UnitOfLength,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
@@ -34,6 +33,15 @@ from .const import (
|
||||
)
|
||||
|
||||
|
||||
def _location_data(user_input: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Return config entry data for a fixed location."""
|
||||
return {
|
||||
CONF_LATITUDE: user_input[CONF_LATITUDE],
|
||||
CONF_LONGITUDE: user_input[CONF_LONGITUDE],
|
||||
CONF_ELEVATION: user_input[CONF_ELEVATION],
|
||||
}
|
||||
|
||||
|
||||
@callback
|
||||
def configured_instances(hass: HomeAssistant) -> set[str]:
|
||||
"""Return a set of configured met.no instances."""
|
||||
@@ -56,7 +64,6 @@ def _get_data_schema(
|
||||
if config_entry is None or config_entry.data.get(CONF_TRACK_HOME, False):
|
||||
return vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_NAME, default=HOME_LOCATION_NAME): str,
|
||||
vol.Required(CONF_LATITUDE, default=hass.config.latitude): cv.latitude,
|
||||
vol.Required(
|
||||
CONF_LONGITUDE, default=hass.config.longitude
|
||||
@@ -74,7 +81,6 @@ def _get_data_schema(
|
||||
# Not tracking home, default values come from config entry
|
||||
return vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_NAME, default=config_entry.data.get(CONF_NAME)): str,
|
||||
vol.Required(
|
||||
CONF_LATITUDE, default=config_entry.data.get(CONF_LATITUDE)
|
||||
): cv.latitude,
|
||||
@@ -97,6 +103,7 @@ class MetConfigFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
"""Config flow for Met component."""
|
||||
|
||||
VERSION = 1
|
||||
MINOR_VERSION = 2
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
@@ -105,14 +112,13 @@ class MetConfigFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
errors = {}
|
||||
|
||||
if user_input is not None:
|
||||
data = _location_data(user_input)
|
||||
if (
|
||||
f"{user_input.get(CONF_LATITUDE)}-{user_input.get(CONF_LONGITUDE)}"
|
||||
f"{data[CONF_LATITUDE]}-{data[CONF_LONGITUDE]}"
|
||||
not in configured_instances(self.hass)
|
||||
):
|
||||
return self.async_create_entry(
|
||||
title=user_input[CONF_NAME], data=user_input
|
||||
)
|
||||
errors[CONF_NAME] = "already_configured"
|
||||
return self.async_create_entry(title="", data=data)
|
||||
errors["base"] = "already_configured"
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
@@ -154,13 +160,11 @@ class MetOptionsFlowHandler(OptionsFlowWithReload):
|
||||
"""Configure options for Met."""
|
||||
|
||||
if user_input is not None:
|
||||
# Update config entry with data from user input
|
||||
self.hass.config_entries.async_update_entry(
|
||||
self.config_entry, data=user_input
|
||||
)
|
||||
return self.async_create_entry(
|
||||
title=self.config_entry.title, data=user_input
|
||||
)
|
||||
data = {**self.config_entry.data, **_location_data(user_input)}
|
||||
# Update config entry with data from user input while preserving
|
||||
# existing fields such as legacy stored names.
|
||||
self.hass.config_entries.async_update_entry(self.config_entry, data=data)
|
||||
return self.async_create_entry(title=self.config_entry.title, data=data)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="init",
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
"data": {
|
||||
"elevation": "[%key:common::config_flow::data::elevation%]",
|
||||
"latitude": "[%key:common::config_flow::data::latitude%]",
|
||||
"longitude": "[%key:common::config_flow::data::longitude%]",
|
||||
"name": "[%key:common::config_flow::data::name%]"
|
||||
"longitude": "[%key:common::config_flow::data::longitude%]"
|
||||
},
|
||||
"description": "Meteorologisk institutt",
|
||||
"title": "[%key:common::config_flow::data::location%]"
|
||||
@@ -30,8 +29,7 @@
|
||||
"data": {
|
||||
"elevation": "[%key:common::config_flow::data::elevation%]",
|
||||
"latitude": "[%key:common::config_flow::data::latitude%]",
|
||||
"longitude": "[%key:common::config_flow::data::longitude%]",
|
||||
"name": "[%key:common::config_flow::data::name%]"
|
||||
"longitude": "[%key:common::config_flow::data::longitude%]"
|
||||
},
|
||||
"title": "[%key:common::config_flow::data::location%]"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Support for Met.no weather service."""
|
||||
|
||||
from collections.abc import Mapping
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_FORECAST_CONDITION,
|
||||
@@ -23,7 +23,6 @@ from homeassistant.components.weather import (
|
||||
from homeassistant.const import (
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
CONF_NAME,
|
||||
UnitOfPrecipitationDepth,
|
||||
UnitOfPressure,
|
||||
UnitOfSpeed,
|
||||
@@ -65,9 +64,7 @@ async def async_setup_entry(
|
||||
if config_entry.data.get(CONF_TRACK_HOME, False):
|
||||
name = hass.config.location_name
|
||||
else:
|
||||
name = config_entry.data.get(CONF_NAME, DEFAULT_NAME)
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(name, str)
|
||||
name = config_entry.title or DEFAULT_NAME
|
||||
|
||||
entities = [MetWeather(coordinator, config_entry, name, is_metric)]
|
||||
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from homeassistant.components.met.const import CONF_TRACK_HOME, DOMAIN
|
||||
from homeassistant.const import CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
|
||||
from homeassistant.components.met.const import (
|
||||
CONF_TRACK_HOME,
|
||||
DOMAIN,
|
||||
HOME_LOCATION_NAME,
|
||||
)
|
||||
from homeassistant.const import CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
@@ -14,7 +18,6 @@ async def init_integration(
|
||||
) -> MockConfigEntry:
|
||||
"""Set up the Met integration in Home Assistant."""
|
||||
entry_data = {
|
||||
CONF_NAME: "test",
|
||||
CONF_LATITUDE: 0,
|
||||
CONF_LONGITUDE: 1.0,
|
||||
CONF_ELEVATION: 1.0,
|
||||
@@ -23,7 +26,12 @@ async def init_integration(
|
||||
if track_home:
|
||||
entry_data = {CONF_TRACK_HOME: True}
|
||||
|
||||
entry = MockConfigEntry(domain=DOMAIN, data=entry_data)
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=entry_data,
|
||||
title=HOME_LOCATION_NAME if track_home else "",
|
||||
minor_version=2,
|
||||
)
|
||||
with patch(
|
||||
"homeassistant.components.met.coordinator.metno.MetWeatherData.fetching_data",
|
||||
return_value=True,
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
'elevation': 1.0,
|
||||
'latitude': '**REDACTED**',
|
||||
'longitude': '**REDACTED**',
|
||||
'name': 'test',
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
|
||||
@@ -8,7 +8,7 @@ import pytest
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.met.const import DOMAIN, HOME_LOCATION_NAME
|
||||
from homeassistant.const import CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
|
||||
from homeassistant.const import CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core_config import async_process_ha_core_config
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
@@ -56,7 +56,6 @@ async def test_flow_with_home_location(hass: HomeAssistant) -> None:
|
||||
assert result["step_id"] == "user"
|
||||
|
||||
default_data = result["data_schema"]({})
|
||||
assert default_data["name"] == HOME_LOCATION_NAME
|
||||
assert default_data["latitude"] == 1
|
||||
assert default_data["longitude"] == 2
|
||||
assert default_data["elevation"] == 3
|
||||
@@ -65,7 +64,6 @@ async def test_flow_with_home_location(hass: HomeAssistant) -> None:
|
||||
async def test_create_entry(hass: HomeAssistant) -> None:
|
||||
"""Test create entry from user input."""
|
||||
test_data = {
|
||||
"name": "home",
|
||||
CONF_LONGITUDE: 0,
|
||||
CONF_LATITUDE: 0,
|
||||
CONF_ELEVATION: 0,
|
||||
@@ -76,7 +74,7 @@ async def test_create_entry(hass: HomeAssistant) -> None:
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "home"
|
||||
assert result["title"] == ""
|
||||
assert result["data"] == test_data
|
||||
|
||||
|
||||
@@ -88,12 +86,11 @@ async def test_flow_entry_already_exists(hass: HomeAssistant) -> None:
|
||||
"""
|
||||
first_entry = MockConfigEntry(
|
||||
domain="met",
|
||||
data={"name": "home", CONF_LATITUDE: 0, CONF_LONGITUDE: 0, CONF_ELEVATION: 0},
|
||||
data={CONF_LATITUDE: 0, CONF_LONGITUDE: 0, CONF_ELEVATION: 0},
|
||||
)
|
||||
first_entry.add_to_hass(hass)
|
||||
|
||||
test_data = {
|
||||
"name": "home",
|
||||
CONF_LONGITUDE: 0,
|
||||
CONF_LATITUDE: 0,
|
||||
CONF_ELEVATION: 0,
|
||||
@@ -104,7 +101,7 @@ async def test_flow_entry_already_exists(hass: HomeAssistant) -> None:
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"]["name"] == "already_configured"
|
||||
assert result["errors"]["base"] == "already_configured"
|
||||
|
||||
|
||||
async def test_onboarding_step(hass: HomeAssistant) -> None:
|
||||
@@ -122,7 +119,7 @@ async def test_onboarding_step(hass: HomeAssistant) -> None:
|
||||
("latitude", "longitude"), [(52.3731339, 4.8903147), (0.0, 0.0)]
|
||||
)
|
||||
async def test_onboarding_step_abort_no_home(
|
||||
hass: HomeAssistant, latitude, longitude
|
||||
hass: HomeAssistant, latitude: float, longitude: float
|
||||
) -> None:
|
||||
"""Test entry not created when default step fails."""
|
||||
await async_process_ha_core_config(
|
||||
@@ -145,7 +142,6 @@ async def test_onboarding_step_abort_no_home(
|
||||
async def test_options_flow(hass: HomeAssistant) -> None:
|
||||
"""Test show options form."""
|
||||
update_data = {
|
||||
CONF_NAME: "test",
|
||||
CONF_LATITUDE: 12,
|
||||
CONF_LONGITUDE: 23,
|
||||
CONF_ELEVATION: 456,
|
||||
@@ -168,7 +164,7 @@ async def test_options_flow(hass: HomeAssistant) -> None:
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "Mock Title"
|
||||
assert result["title"] == ""
|
||||
assert result["data"] == update_data
|
||||
weatherdatamock.assert_called_with(
|
||||
{"lat": "12", "lon": "23", "msl": "456"}, ANY, api_url=ANY
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Test the Met integration init."""
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.met.const import (
|
||||
@@ -7,13 +9,16 @@ from homeassistant.components.met.const import (
|
||||
DEFAULT_HOME_LONGITUDE,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.config_entries import SOURCE_USER, ConfigEntryState
|
||||
from homeassistant.const import CONF_ELEVATION, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core_config import async_process_ha_core_config
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
from . import init_integration
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_unload_entry(hass: HomeAssistant) -> None:
|
||||
"""Test successful unload of entry."""
|
||||
@@ -56,7 +61,7 @@ async def test_removing_incorrect_devices(
|
||||
hass: HomeAssistant,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
mock_weather,
|
||||
mock_weather: MagicMock,
|
||||
) -> None:
|
||||
"""Test we remove incorrect devices."""
|
||||
entry = await init_integration(hass)
|
||||
@@ -77,3 +82,36 @@ async def test_removing_incorrect_devices(
|
||||
assert not device_registry.async_get_device(identifiers={(DOMAIN,)})
|
||||
assert device_registry.async_get_device(identifiers={(DOMAIN, entry.entry_id)})
|
||||
assert "Removing improper device Forecast_legacy" in caplog.text
|
||||
|
||||
|
||||
async def test_migrate_name_to_title(
|
||||
hass: HomeAssistant,
|
||||
mock_weather: MagicMock,
|
||||
) -> None:
|
||||
"""Test legacy stored names migrate to the config entry title."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
source=SOURCE_USER,
|
||||
data={
|
||||
CONF_NAME: "Somewhere",
|
||||
CONF_LATITUDE: 10,
|
||||
CONF_LONGITUDE: 20,
|
||||
CONF_ELEVATION: 0,
|
||||
},
|
||||
title="",
|
||||
minor_version=1,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert entry.version == 1
|
||||
assert entry.minor_version == 2
|
||||
assert entry.title == "Somewhere"
|
||||
assert entry.data == {
|
||||
CONF_LATITUDE: 10,
|
||||
CONF_LONGITUDE: 20,
|
||||
CONF_ELEVATION: 0,
|
||||
}
|
||||
assert hass.states.async_entity_ids("weather") == ["weather.forecast_somewhere"]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Test Met weather entity."""
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.met import DOMAIN
|
||||
from homeassistant.components.weather import (
|
||||
@@ -20,7 +22,9 @@ from . import init_integration
|
||||
|
||||
|
||||
async def test_new_config_entry(
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry, mock_weather
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_weather: MagicMock,
|
||||
) -> None:
|
||||
"""Test the expected entities are created."""
|
||||
await hass.config_entries.flow.async_init("met", context={"source": "onboarding"})
|
||||
@@ -32,7 +36,9 @@ async def test_new_config_entry(
|
||||
|
||||
|
||||
async def test_legacy_config_entry(
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry, mock_weather
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_weather: MagicMock,
|
||||
) -> None:
|
||||
"""Test the expected entities are created."""
|
||||
entity_registry.async_get_or_create(
|
||||
@@ -48,7 +54,7 @@ async def test_legacy_config_entry(
|
||||
assert len(er.async_entries_for_config_entry(entity_registry, entry.entry_id)) == 1
|
||||
|
||||
|
||||
async def test_weather(hass: HomeAssistant, mock_weather) -> None:
|
||||
async def test_weather(hass: HomeAssistant, mock_weather: MagicMock) -> None:
|
||||
"""Test states of the weather."""
|
||||
|
||||
await init_integration(hass)
|
||||
@@ -67,7 +73,7 @@ async def test_weather(hass: HomeAssistant, mock_weather) -> None:
|
||||
assert state.attributes[ATTR_WEATHER_UV_INDEX] == 1.1
|
||||
|
||||
|
||||
async def test_tracking_home(hass: HomeAssistant, mock_weather) -> None:
|
||||
async def test_tracking_home(hass: HomeAssistant, mock_weather: MagicMock) -> None:
|
||||
"""Test we track home."""
|
||||
await hass.config_entries.flow.async_init("met", context={"source": "onboarding"})
|
||||
await hass.async_block_till_done()
|
||||
@@ -91,13 +97,13 @@ async def test_tracking_home(hass: HomeAssistant, mock_weather) -> None:
|
||||
assert len(hass.states.async_entity_ids("weather")) == 0
|
||||
|
||||
|
||||
async def test_not_tracking_home(hass: HomeAssistant, mock_weather) -> None:
|
||||
async def test_not_tracking_home(hass: HomeAssistant, mock_weather: MagicMock) -> None:
|
||||
"""Test when we not track home."""
|
||||
|
||||
await hass.config_entries.flow.async_init(
|
||||
"met",
|
||||
context={"source": config_entries.SOURCE_USER},
|
||||
data={"name": "Somewhere", "latitude": 10, "longitude": 20, "elevation": 0},
|
||||
data={"latitude": 10, "longitude": 20, "elevation": 0},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.states.async_entity_ids("weather")) == 1
|
||||
@@ -116,7 +122,9 @@ async def test_not_tracking_home(hass: HomeAssistant, mock_weather) -> None:
|
||||
|
||||
|
||||
async def test_remove_hourly_entity(
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry, mock_weather
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_weather: MagicMock,
|
||||
) -> None:
|
||||
"""Test removing the hourly entity."""
|
||||
|
||||
@@ -135,8 +143,8 @@ async def test_remove_hourly_entity(
|
||||
await hass.config_entries.flow.async_init(
|
||||
"met",
|
||||
context={"source": config_entries.SOURCE_USER},
|
||||
data={"name": "Somewhere", "latitude": 10, "longitude": 20, "elevation": 0},
|
||||
data={"latitude": 10, "longitude": 20, "elevation": 0},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.async_entity_ids("weather") == ["weather.forecast_somewhere"]
|
||||
assert list(entity_registry.entities.keys()) == ["weather.forecast_somewhere"]
|
||||
assert hass.states.async_entity_ids("weather") == ["weather.forecast_met_no"]
|
||||
assert list(entity_registry.entities.keys()) == ["weather.forecast_met_no"]
|
||||
|
||||
Reference in New Issue
Block a user