diff --git a/homeassistant/components/config/core.py b/homeassistant/components/config/core.py index 3cfb7c03a40..6f788b1c9f2 100644 --- a/homeassistant/components/config/core.py +++ b/homeassistant/components/config/core.py @@ -61,6 +61,7 @@ class CheckConfigView(HomeAssistantView): vol.Optional("latitude"): cv.latitude, vol.Optional("location_name"): str, vol.Optional("longitude"): cv.longitude, + vol.Optional("radius"): cv.positive_int, vol.Optional("time_zone"): cv.time_zone, vol.Optional("update_units"): bool, vol.Optional("unit_system"): unit_system.validate_unit_system, diff --git a/homeassistant/components/zone/__init__.py b/homeassistant/components/zone/__init__.py index 16784a9e0c3..0fef9961679 100644 --- a/homeassistant/components/zone/__init__.py +++ b/homeassistant/components/zone/__init__.py @@ -302,7 +302,7 @@ def _home_conf(hass: HomeAssistant) -> dict: CONF_NAME: hass.config.location_name, CONF_LATITUDE: hass.config.latitude, CONF_LONGITUDE: hass.config.longitude, - CONF_RADIUS: DEFAULT_RADIUS, + CONF_RADIUS: hass.config.radius, CONF_ICON: ICON_HOME, CONF_PASSIVE: False, } diff --git a/homeassistant/config.py b/homeassistant/config.py index bb3a8fb1cd4..751eaca7376 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -52,6 +52,7 @@ from .const import ( CONF_NAME, CONF_PACKAGES, CONF_PLATFORM, + CONF_RADIUS, CONF_TEMPERATURE_UNIT, CONF_TIME_ZONE, CONF_TYPE, @@ -342,6 +343,7 @@ CORE_CONFIG_SCHEMA = vol.All( CONF_LATITUDE: cv.latitude, CONF_LONGITUDE: cv.longitude, CONF_ELEVATION: vol.Coerce(int), + CONF_RADIUS: cv.positive_int, vol.Remove(CONF_TEMPERATURE_UNIT): cv.temperature_unit, CONF_UNIT_SYSTEM: validate_unit_system, CONF_TIME_ZONE: cv.time_zone, @@ -882,6 +884,7 @@ async def async_process_ha_core_config(hass: HomeAssistant, config: dict) -> Non CONF_CURRENCY, CONF_COUNTRY, CONF_LANGUAGE, + CONF_RADIUS, ) ): hac.config_source = ConfigSource.YAML @@ -898,6 +901,7 @@ async def async_process_ha_core_config(hass: HomeAssistant, config: dict) -> Non (CONF_CURRENCY, "currency"), (CONF_COUNTRY, "country"), (CONF_LANGUAGE, "language"), + (CONF_RADIUS, "radius"), ): if key in config: setattr(hac, attr, config[key]) diff --git a/homeassistant/core.py b/homeassistant/core.py index 108248c9e83..ac287fb2d5f 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -138,7 +138,7 @@ type CALLBACK_TYPE = Callable[[], None] CORE_STORAGE_KEY = "core.config" CORE_STORAGE_VERSION = 1 -CORE_STORAGE_MINOR_VERSION = 3 +CORE_STORAGE_MINOR_VERSION = 4 DOMAIN = "homeassistant" @@ -2835,6 +2835,9 @@ class Config: def __init__(self, hass: HomeAssistant, config_dir: str) -> None: """Initialize a new config object.""" + # pylint: disable-next=import-outside-toplevel + from .components.zone import DEFAULT_RADIUS + self.hass = hass self.latitude: float = 0 @@ -2843,6 +2846,9 @@ class Config: self.elevation: int = 0 """Elevation (always in meters regardless of the unit system).""" + self.radius: int = DEFAULT_RADIUS + """Radius of the Home Zone (always in meters regardless of the unit system).""" + self.debug: bool = False self.location_name: str = "Home" self.time_zone: str = "UTC" @@ -2991,6 +2997,7 @@ class Config: "language": self.language, "safe_mode": self.safe_mode, "debug": self.debug, + "radius": self.radius, } async def async_set_time_zone(self, time_zone_str: str) -> None: @@ -3039,6 +3046,7 @@ class Config: currency: str | None = None, country: str | UndefinedType | None = UNDEFINED, language: str | None = None, + radius: int | None = None, ) -> None: """Update the configuration from a dictionary.""" self.config_source = source @@ -3067,6 +3075,8 @@ class Config: self.country = country if language is not None: self.language = language + if radius is not None: + self.radius = radius async def async_update(self, **kwargs: Any) -> None: """Update the configuration from a dictionary.""" @@ -3115,6 +3125,7 @@ class Config: currency=data.get("currency"), country=data.get("country"), language=data.get("language"), + radius=data["radius"], ) async def _async_store(self) -> None: @@ -3133,6 +3144,7 @@ class Config: "currency": self.currency, "country": self.country, "language": self.language, + "radius": self.radius, } await self._store.async_save(data) @@ -3162,6 +3174,10 @@ class Config: old_data: dict[str, Any], ) -> dict[str, Any]: """Migrate to the new version.""" + + # pylint: disable-next=import-outside-toplevel + from .components.zone import DEFAULT_RADIUS + data = old_data if old_major_version == 1 and old_minor_version < 2: # In 1.2, we remove support for "imperial", replaced by "us_customary" @@ -3198,6 +3214,9 @@ class Config: # pylint: disable-next=broad-except except Exception: _LOGGER.exception("Unexpected error during core config migration") + if old_major_version == 1 and old_minor_version < 4: + # In 1.4, we add the key "radius", initialize it with the default. + data.setdefault("radius", DEFAULT_RADIUS) if old_major_version > 1: raise NotImplementedError diff --git a/tests/components/config/test_core.py b/tests/components/config/test_core.py index 3ee3e3334ea..7d02063b2b9 100644 --- a/tests/components/config/test_core.py +++ b/tests/components/config/test_core.py @@ -120,6 +120,7 @@ async def test_websocket_core_update(hass: HomeAssistant, client) -> None: assert hass.config.currency == "EUR" assert hass.config.country != "SE" assert hass.config.language != "sv" + assert hass.config.radius != 150 with ( patch("homeassistant.util.dt.set_default_time_zone") as mock_set_tz, @@ -142,6 +143,7 @@ async def test_websocket_core_update(hass: HomeAssistant, client) -> None: "currency": "USD", "country": "SE", "language": "sv", + "radius": 150, } ) @@ -162,6 +164,7 @@ async def test_websocket_core_update(hass: HomeAssistant, client) -> None: assert hass.config.currency == "USD" assert hass.config.country == "SE" assert hass.config.language == "sv" + assert hass.config.radius == 150 assert len(mock_set_tz.mock_calls) == 1 assert mock_set_tz.mock_calls[0][1][0] == dt_util.get_time_zone("America/New_York") diff --git a/tests/test_config.py b/tests/test_config.py index 73e14fee10a..8a8cf8f909b 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -522,6 +522,7 @@ def test_core_config_schema() -> None: {"customize": {"entity_id": []}}, {"country": "xx"}, {"language": "xx"}, + {"radius": -10}, ): with pytest.raises(MultipleInvalid): config_util.CORE_CONFIG_SCHEMA(value) @@ -538,6 +539,7 @@ def test_core_config_schema() -> None: "customize": {"sensor.temperature": {"hidden": True}}, "country": "SE", "language": "sv", + "radius": "10", } ) @@ -709,10 +711,11 @@ async def test_loading_configuration_from_storage( "currency": "EUR", "country": "SE", "language": "sv", + "radius": 150, }, "key": "core.config", "version": 1, - "minor_version": 3, + "minor_version": 4, } await config_util.async_process_ha_core_config( hass, {"allowlist_external_dirs": "/etc"} @@ -729,6 +732,7 @@ async def test_loading_configuration_from_storage( assert hass.config.currency == "EUR" assert hass.config.country == "SE" assert hass.config.language == "sv" + assert hass.config.radius == 150 assert len(hass.config.allowlist_external_dirs) == 3 assert "/etc" in hass.config.allowlist_external_dirs assert hass.config.config_source is ConfigSource.STORAGE @@ -798,15 +802,19 @@ async def test_migration_and_updating_configuration( expected_new_core_data["data"]["currency"] = "USD" # 1.1 -> 1.2 store migration with migrated unit system expected_new_core_data["data"]["unit_system_v2"] = "us_customary" - expected_new_core_data["minor_version"] = 3 - # defaults for country and language + # 1.1 -> 1.3 defaults for country and language expected_new_core_data["data"]["country"] = None expected_new_core_data["data"]["language"] = "en" + # 1.1 -> 1.4 defaults for zone radius + expected_new_core_data["data"]["radius"] = 100 + # Bumped minor version + expected_new_core_data["minor_version"] = 4 assert hass_storage["core.config"] == expected_new_core_data assert hass.config.latitude == 50 assert hass.config.currency == "USD" assert hass.config.country is None assert hass.config.language == "en" + assert hass.config.radius == 100 async def test_override_stored_configuration( @@ -860,6 +868,7 @@ async def test_loading_configuration(hass: HomeAssistant) -> None: "currency": "EUR", "country": "SE", "language": "sv", + "radius": 150, }, ) @@ -881,6 +890,7 @@ async def test_loading_configuration(hass: HomeAssistant) -> None: assert hass.config.currency == "EUR" assert hass.config.country == "SE" assert hass.config.language == "sv" + assert hass.config.radius == 150 @pytest.mark.parametrize( diff --git a/tests/test_core.py b/tests/test_core.py index 8be2599f454..4c53e1bbd58 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1936,6 +1936,7 @@ async def test_config_defaults() -> None: assert config.currency == "EUR" assert config.country is None assert config.language == "en" + assert config.radius == 100 async def test_config_path_with_file() -> None: @@ -1983,6 +1984,7 @@ async def test_config_as_dict() -> None: "language": "en", "safe_mode": False, "debug": False, + "radius": 100, } assert expected == config.as_dict()