From ee2c950716d810b33fa244438044ca0332ea3a54 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 1 Jun 2021 13:34:31 -0700 Subject: [PATCH] Merge system options into pref properties (#51347) * Make system options future proof * Update tests * Add types --- .../components/config/config_entries.py | 82 ++++++------- homeassistant/config_entries.py | 99 ++++++++-------- homeassistant/helpers/entity_platform.py | 2 +- homeassistant/helpers/entity_registry.py | 2 +- homeassistant/helpers/update_coordinator.py | 2 +- tests/common.py | 6 +- .../bmw_connected_drive/test_config_flow.py | 1 - .../components/config/test_config_entries.py | 95 ++++++--------- .../forked_daapd/test_config_flow.py | 1 - .../forked_daapd/test_media_player.py | 1 - .../components/home_plus_control/conftest.py | 1 - .../homekit_controller/test_storage.py | 1 - .../components/homematicip_cloud/conftest.py | 1 - tests/components/hue/conftest.py | 1 - tests/components/hue/test_bridge.py | 4 - tests/components/hue/test_light.py | 1 - tests/components/huisbaasje/test_init.py | 3 - tests/components/huisbaasje/test_sensor.py | 2 - .../hvv_departures/test_config_flow.py | 3 - tests/components/smartthings/conftest.py | 1 - tests/components/unifi/test_device_tracker.py | 1 - tests/components/unifi/test_switch.py | 2 - tests/components/zwave/test_lock.py | 1 - tests/helpers/test_entity_platform.py | 4 +- tests/helpers/test_entity_registry.py | 6 +- tests/helpers/test_update_coordinator.py | 2 +- tests/test_config_entries.py | 108 ++++++++++-------- 27 files changed, 188 insertions(+), 245 deletions(-) diff --git a/homeassistant/components/config/config_entries.py b/homeassistant/components/config/config_entries.py index 9d88a9b5311..7fe5cb0d190 100644 --- a/homeassistant/components/config/config_entries.py +++ b/homeassistant/components/config/config_entries.py @@ -1,4 +1,6 @@ """Http views to control the config manager.""" +from __future__ import annotations + import aiohttp.web_exceptions import voluptuous as vol @@ -7,7 +9,7 @@ from homeassistant.auth.permissions.const import CAT_CONFIG_ENTRIES, POLICY_EDIT from homeassistant.components import websocket_api from homeassistant.components.http import HomeAssistantView from homeassistant.const import HTTP_FORBIDDEN, HTTP_NOT_FOUND -from homeassistant.core import callback +from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import Unauthorized from homeassistant.helpers.data_entry_flow import ( FlowManagerIndexView, @@ -31,7 +33,6 @@ async def async_setup(hass): hass.components.websocket_api.async_register_command(config_entry_disable) hass.components.websocket_api.async_register_command(config_entry_update) hass.components.websocket_api.async_register_command(config_entries_progress) - hass.components.websocket_api.async_register_command(system_options_update) hass.components.websocket_api.async_register_command(ignore_config_flow) return True @@ -230,14 +231,21 @@ def config_entries_progress(hass, connection, msg): ) -def send_entry_not_found(connection, msg_id): +def send_entry_not_found( + connection: websocket_api.ActiveConnection, msg_id: int +) -> None: """Send Config entry not found error.""" connection.send_error( msg_id, websocket_api.const.ERR_NOT_FOUND, "Config entry not found" ) -def get_entry(hass, connection, entry_id, msg_id): +def get_entry( + hass: HomeAssistant, + connection: websocket_api.ActiveConnection, + entry_id: str, + msg_id: int, +) -> config_entries.ConfigEntry | None: """Get entry, send error message if it doesn't exist.""" entry = hass.config_entries.async_get_entry(entry_id) if entry is None: @@ -249,49 +257,13 @@ def get_entry(hass, connection, entry_id, msg_id): @websocket_api.async_response @websocket_api.websocket_command( { - "type": "config_entries/system_options/update", + "type": "config_entries/update", "entry_id": str, - vol.Optional("disable_new_entities"): bool, - vol.Optional("disable_polling"): bool, + vol.Optional("title"): str, + vol.Optional("pref_disable_new_entities"): bool, + vol.Optional("pref_disable_polling"): bool, } ) -async def system_options_update(hass, connection, msg): - """Update config entry system options.""" - changes = dict(msg) - changes.pop("id") - changes.pop("type") - changes.pop("entry_id") - - entry = get_entry(hass, connection, msg["entry_id"], msg["id"]) - if entry is None: - return - - old_disable_polling = entry.system_options.disable_polling - - hass.config_entries.async_update_entry(entry, system_options=changes) - - result = { - "system_options": entry.system_options.as_dict(), - "require_restart": False, - } - - if ( - old_disable_polling != entry.system_options.disable_polling - and entry.state is config_entries.ConfigEntryState.LOADED - ): - if not await hass.config_entries.async_reload(entry.entry_id): - result["require_restart"] = ( - entry.state is config_entries.ConfigEntryState.FAILED_UNLOAD - ) - - connection.send_result(msg["id"], result) - - -@websocket_api.require_admin -@websocket_api.async_response -@websocket_api.websocket_command( - {"type": "config_entries/update", "entry_id": str, vol.Optional("title"): str} -) async def config_entry_update(hass, connection, msg): """Update config entry.""" changes = dict(msg) @@ -303,8 +275,25 @@ async def config_entry_update(hass, connection, msg): if entry is None: return + old_disable_polling = entry.pref_disable_polling + hass.config_entries.async_update_entry(entry, **changes) - connection.send_result(msg["id"], entry_json(entry)) + + result = { + "config_entry": entry_json(entry), + "require_restart": False, + } + + if ( + old_disable_polling != entry.pref_disable_polling + and entry.state is config_entries.ConfigEntryState.LOADED + ): + if not await hass.config_entries.async_reload(entry.entry_id): + result["require_restart"] = ( + entry.state is config_entries.ConfigEntryState.FAILED_UNLOAD + ) + + connection.send_result(msg["id"], result) @websocket_api.require_admin @@ -391,7 +380,8 @@ def entry_json(entry: config_entries.ConfigEntry) -> dict: "state": entry.state.value, "supports_options": supports_options, "supports_unload": entry.supports_unload, - "system_options": entry.system_options.as_dict(), + "pref_disable_new_entities": entry.pref_disable_new_entities, + "pref_disable_polling": entry.pref_disable_polling, "disabled_by": entry.disabled_by, "reason": entry.reason, } diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 33c18fc0d7c..eeaf0149cc2 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -11,8 +11,6 @@ from types import MappingProxyType, MethodType from typing import Any, Callable, Optional, cast import weakref -import attr - from homeassistant import data_entry_flow, loader from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP from homeassistant.core import CALLBACK_TYPE, CoreState, HomeAssistant, callback @@ -152,7 +150,8 @@ class ConfigEntry: "options", "unique_id", "supports_unload", - "system_options", + "pref_disable_new_entities", + "pref_disable_polling", "source", "state", "disabled_by", @@ -170,7 +169,8 @@ class ConfigEntry: title: str, data: Mapping[str, Any], source: str, - system_options: dict, + pref_disable_new_entities: bool | None = None, + pref_disable_polling: bool | None = None, options: Mapping[str, Any] | None = None, unique_id: str | None = None, entry_id: str | None = None, @@ -197,7 +197,15 @@ class ConfigEntry: self.options = MappingProxyType(options or {}) # Entry system options - self.system_options = SystemOptions(**system_options) + if pref_disable_new_entities is None: + pref_disable_new_entities = False + + self.pref_disable_new_entities = pref_disable_new_entities + + if pref_disable_polling is None: + pref_disable_polling = False + + self.pref_disable_polling = pref_disable_polling # Source of the configuration (user, discovery, cloud) self.source = source @@ -535,7 +543,8 @@ class ConfigEntry: "title": self.title, "data": dict(self.data), "options": dict(self.options), - "system_options": self.system_options.as_dict(), + "pref_disable_new_entities": self.pref_disable_new_entities, + "pref_disable_polling": self.pref_disable_polling, "source": self.source, "unique_id": self.unique_id, "disabled_by": self.disabled_by, @@ -652,7 +661,6 @@ class ConfigEntriesFlowManager(data_entry_flow.FlowManager): title=result["title"], data=result["data"], options=result["options"], - system_options={}, source=flow.context["source"], unique_id=flow.unique_id, ) @@ -845,8 +853,18 @@ class ConfigEntries: self._entries = {} return - self._entries = { - entry["entry_id"]: ConfigEntry( + entries = {} + + for entry in config["entries"]: + pref_disable_new_entities = entry.get("pref_disable_new_entities") + + # Between 0.98 and 2021.6 we stored 'disable_new_entities' in a system options dictionary + if pref_disable_new_entities is None and "system_options" in entry: + pref_disable_new_entities = entry.get("system_options", {}).get( + "disable_new_entities" + ) + + entries[entry["entry_id"]] = ConfigEntry( version=entry["version"], domain=entry["domain"], entry_id=entry["entry_id"], @@ -855,15 +873,16 @@ class ConfigEntries: title=entry["title"], # New in 0.89 options=entry.get("options"), - # New in 0.98 - system_options=entry.get("system_options", {}), # New in 0.104 unique_id=entry.get("unique_id"), # New in 2021.3 disabled_by=entry.get("disabled_by"), + # New in 2021.6 + pref_disable_new_entities=pref_disable_new_entities, + pref_disable_polling=entry.get("pref_disable_polling"), ) - for entry in config["entries"] - } + + self._entries = entries async def async_setup(self, entry_id: str) -> bool: """Set up a config entry. @@ -962,11 +981,12 @@ class ConfigEntries: self, entry: ConfigEntry, *, - unique_id: str | dict | None | UndefinedType = UNDEFINED, - title: str | dict | UndefinedType = UNDEFINED, + unique_id: str | None | UndefinedType = UNDEFINED, + title: str | UndefinedType = UNDEFINED, data: dict | UndefinedType = UNDEFINED, options: Mapping[str, Any] | UndefinedType = UNDEFINED, - system_options: dict | UndefinedType = UNDEFINED, + pref_disable_new_entities: bool | UndefinedType = UNDEFINED, + pref_disable_polling: bool | UndefinedType = UNDEFINED, ) -> bool: """Update a config entry. @@ -978,13 +998,17 @@ class ConfigEntries: """ changed = False - if unique_id is not UNDEFINED and entry.unique_id != unique_id: - changed = True - entry.unique_id = cast(Optional[str], unique_id) + for attr, value in ( + ("unique_id", unique_id), + ("title", title), + ("pref_disable_new_entities", pref_disable_new_entities), + ("pref_disable_polling", pref_disable_polling), + ): + if value == UNDEFINED or getattr(entry, attr) == value: + continue - if title is not UNDEFINED and entry.title != title: + setattr(entry, attr, value) changed = True - entry.title = cast(str, title) if data is not UNDEFINED and entry.data != data: # type: ignore changed = True @@ -994,11 +1018,6 @@ class ConfigEntries: changed = True entry.options = MappingProxyType(options) - if system_options is not UNDEFINED: - old_system_options = entry.system_options.as_dict() - entry.system_options.update(**system_options) - changed = entry.system_options.as_dict() != old_system_options - if not changed: return False @@ -1401,34 +1420,6 @@ class OptionsFlow(data_entry_flow.FlowHandler): handler: str -@attr.s(slots=True) -class SystemOptions: - """Config entry system options.""" - - disable_new_entities: bool = attr.ib(default=False) - disable_polling: bool = attr.ib(default=False) - - def update( - self, - *, - disable_new_entities: bool | UndefinedType = UNDEFINED, - disable_polling: bool | UndefinedType = UNDEFINED, - ) -> None: - """Update properties.""" - if disable_new_entities is not UNDEFINED: - self.disable_new_entities = disable_new_entities - - if disable_polling is not UNDEFINED: - self.disable_polling = disable_polling - - def as_dict(self) -> dict[str, Any]: - """Return dictionary version of this config entries system options.""" - return { - "disable_new_entities": self.disable_new_entities, - "disable_polling": self.disable_polling, - } - - class EntityRegistryDisabledHandler: """Handler to handle when entities related to config entries updating disabled_by.""" diff --git a/homeassistant/helpers/entity_platform.py b/homeassistant/helpers/entity_platform.py index f0d691a1c8d..b22fb9ec2d2 100644 --- a/homeassistant/helpers/entity_platform.py +++ b/homeassistant/helpers/entity_platform.py @@ -397,7 +397,7 @@ class EntityPlatform: raise if ( - (self.config_entry and self.config_entry.system_options.disable_polling) + (self.config_entry and self.config_entry.pref_disable_polling) or self._async_unsub_polling is not None or not any(entity.should_poll for entity in self.entities.values()) ): diff --git a/homeassistant/helpers/entity_registry.py b/homeassistant/helpers/entity_registry.py index dbb3fae0e53..fc9ef575c7d 100644 --- a/homeassistant/helpers/entity_registry.py +++ b/homeassistant/helpers/entity_registry.py @@ -286,7 +286,7 @@ class EntityRegistry: if ( disabled_by is None and config_entry - and config_entry.system_options.disable_new_entities + and config_entry.pref_disable_new_entities ): disabled_by = DISABLED_INTEGRATION diff --git a/homeassistant/helpers/update_coordinator.py b/homeassistant/helpers/update_coordinator.py index e91acfaf82f..e83a2d0edc3 100644 --- a/homeassistant/helpers/update_coordinator.py +++ b/homeassistant/helpers/update_coordinator.py @@ -111,7 +111,7 @@ class DataUpdateCoordinator(Generic[T]): if self.update_interval is None: return - if self.config_entry and self.config_entry.system_options.disable_polling: + if self.config_entry and self.config_entry.pref_disable_polling: return if self._unsub_refresh: diff --git a/tests/common.py b/tests/common.py index 952350fe68c..03b53294db0 100644 --- a/tests/common.py +++ b/tests/common.py @@ -732,7 +732,8 @@ class MockConfigEntry(config_entries.ConfigEntry): title="Mock Title", state=None, options={}, - system_options={}, + pref_disable_new_entities=None, + pref_disable_polling=None, unique_id=None, disabled_by=None, reason=None, @@ -742,7 +743,8 @@ class MockConfigEntry(config_entries.ConfigEntry): "entry_id": entry_id or uuid_util.random_uuid_hex(), "domain": domain, "data": data or {}, - "system_options": system_options, + "pref_disable_new_entities": pref_disable_new_entities, + "pref_disable_polling": pref_disable_polling, "options": options, "version": version, "title": title, diff --git a/tests/components/bmw_connected_drive/test_config_flow.py b/tests/components/bmw_connected_drive/test_config_flow.py index 75ca9b4aa1c..6a0bd210387 100644 --- a/tests/components/bmw_connected_drive/test_config_flow.py +++ b/tests/components/bmw_connected_drive/test_config_flow.py @@ -29,7 +29,6 @@ FIXTURE_CONFIG_ENTRY = { CONF_REGION: FIXTURE_USER_INPUT[CONF_REGION], }, "options": {CONF_READ_ONLY: False, CONF_USE_LOCATION: False}, - "system_options": {"disable_new_entities": False}, "source": config_entries.SOURCE_USER, "unique_id": f"{FIXTURE_USER_INPUT[CONF_REGION]}-{FIXTURE_USER_INPUT[CONF_REGION]}", } diff --git a/tests/components/config/test_config_entries.py b/tests/components/config/test_config_entries.py index 570d847e86e..0e1b471cbd5 100644 --- a/tests/components/config/test_config_entries.py +++ b/tests/components/config/test_config_entries.py @@ -87,10 +87,8 @@ async def test_get_entries(hass, client): "state": core_ce.ConfigEntryState.NOT_LOADED.value, "supports_options": True, "supports_unload": True, - "system_options": { - "disable_new_entities": False, - "disable_polling": False, - }, + "pref_disable_new_entities": False, + "pref_disable_polling": False, "disabled_by": None, "reason": None, }, @@ -101,10 +99,8 @@ async def test_get_entries(hass, client): "state": core_ce.ConfigEntryState.SETUP_ERROR.value, "supports_options": False, "supports_unload": False, - "system_options": { - "disable_new_entities": False, - "disable_polling": False, - }, + "pref_disable_new_entities": False, + "pref_disable_polling": False, "disabled_by": None, "reason": "Unsupported API", }, @@ -115,10 +111,8 @@ async def test_get_entries(hass, client): "state": core_ce.ConfigEntryState.NOT_LOADED.value, "supports_options": False, "supports_unload": False, - "system_options": { - "disable_new_entities": False, - "disable_polling": False, - }, + "pref_disable_new_entities": False, + "pref_disable_polling": False, "disabled_by": core_ce.DISABLED_USER, "reason": None, }, @@ -340,10 +334,8 @@ async def test_create_account(hass, client, enable_custom_integrations): "state": core_ce.ConfigEntryState.LOADED.value, "supports_options": False, "supports_unload": False, - "system_options": { - "disable_new_entities": False, - "disable_polling": False, - }, + "pref_disable_new_entities": False, + "pref_disable_polling": False, "title": "Test Entry", "reason": None, }, @@ -415,10 +407,8 @@ async def test_two_step_flow(hass, client, enable_custom_integrations): "state": core_ce.ConfigEntryState.LOADED.value, "supports_options": False, "supports_unload": False, - "system_options": { - "disable_new_entities": False, - "disable_polling": False, - }, + "pref_disable_new_entities": False, + "pref_disable_polling": False, "title": "user-title", "reason": None, }, @@ -698,7 +688,7 @@ async def test_two_step_options_flow(hass, client): } -async def test_update_system_options(hass, hass_ws_client): +async def test_update_prefrences(hass, hass_ws_client): """Test that we can update system options.""" assert await async_setup_component(hass, "config", {}) ws_client = await hass_ws_client(hass) @@ -706,64 +696,45 @@ async def test_update_system_options(hass, hass_ws_client): entry = MockConfigEntry(domain="demo", state=core_ce.ConfigEntryState.LOADED) entry.add_to_hass(hass) - assert entry.system_options.disable_new_entities is False - assert entry.system_options.disable_polling is False - - await ws_client.send_json( - { - "id": 5, - "type": "config_entries/system_options/update", - "entry_id": entry.entry_id, - "disable_new_entities": True, - } - ) - response = await ws_client.receive_json() - - assert response["success"] - assert response["result"] == { - "require_restart": False, - "system_options": {"disable_new_entities": True, "disable_polling": False}, - } - assert entry.system_options.disable_new_entities is True - assert entry.system_options.disable_polling is False + assert entry.pref_disable_new_entities is False + assert entry.pref_disable_polling is False await ws_client.send_json( { "id": 6, - "type": "config_entries/system_options/update", + "type": "config_entries/update", "entry_id": entry.entry_id, - "disable_new_entities": False, - "disable_polling": True, + "pref_disable_new_entities": True, } ) response = await ws_client.receive_json() assert response["success"] - assert response["result"] == { - "require_restart": True, - "system_options": {"disable_new_entities": False, "disable_polling": True}, - } - assert entry.system_options.disable_new_entities is False - assert entry.system_options.disable_polling is True + assert response["result"]["require_restart"] is False + assert response["result"]["config_entry"]["pref_disable_new_entities"] is True + assert response["result"]["config_entry"]["pref_disable_polling"] is False - -async def test_update_system_options_nonexisting(hass, hass_ws_client): - """Test that we can update entry.""" - assert await async_setup_component(hass, "config", {}) - ws_client = await hass_ws_client(hass) + assert entry.pref_disable_new_entities is True + assert entry.pref_disable_polling is False await ws_client.send_json( { - "id": 5, - "type": "config_entries/system_options/update", - "entry_id": "non_existing", - "disable_new_entities": True, + "id": 7, + "type": "config_entries/update", + "entry_id": entry.entry_id, + "pref_disable_new_entities": False, + "pref_disable_polling": True, } ) response = await ws_client.receive_json() - assert not response["success"] - assert response["error"]["code"] == "not_found" + assert response["success"] + assert response["result"]["require_restart"] is True + assert response["result"]["config_entry"]["pref_disable_new_entities"] is False + assert response["result"]["config_entry"]["pref_disable_polling"] is True + + assert entry.pref_disable_new_entities is False + assert entry.pref_disable_polling is True async def test_update_entry(hass, hass_ws_client): @@ -785,7 +756,7 @@ async def test_update_entry(hass, hass_ws_client): response = await ws_client.receive_json() assert response["success"] - assert response["result"]["title"] == "Updated Title" + assert response["result"]["config_entry"]["title"] == "Updated Title" assert entry.title == "Updated Title" diff --git a/tests/components/forked_daapd/test_config_flow.py b/tests/components/forked_daapd/test_config_flow.py index a99f91d3f91..668f1be0a4f 100644 --- a/tests/components/forked_daapd/test_config_flow.py +++ b/tests/components/forked_daapd/test_config_flow.py @@ -46,7 +46,6 @@ def config_entry_fixture(): title="", data=data, options={}, - system_options={}, source=SOURCE_USER, entry_id=1, ) diff --git a/tests/components/forked_daapd/test_media_player.py b/tests/components/forked_daapd/test_media_player.py index 032e3dde22c..a2e0050c3d9 100644 --- a/tests/components/forked_daapd/test_media_player.py +++ b/tests/components/forked_daapd/test_media_player.py @@ -282,7 +282,6 @@ def config_entry_fixture(): title="", data=data, options={CONF_TTS_PAUSE_TIME: 0}, - system_options={}, source=SOURCE_USER, entry_id=1, ) diff --git a/tests/components/home_plus_control/conftest.py b/tests/components/home_plus_control/conftest.py index 4b60f2623c4..78a0da41fb8 100644 --- a/tests/components/home_plus_control/conftest.py +++ b/tests/components/home_plus_control/conftest.py @@ -35,7 +35,6 @@ def mock_config_entry(): }, source="test", options={}, - system_options={"disable_new_entities": False}, unique_id=DOMAIN, entry_id="home_plus_control_entry_id", ) diff --git a/tests/components/homekit_controller/test_storage.py b/tests/components/homekit_controller/test_storage.py index b1c3ee9ff4c..aa0a5e55057 100644 --- a/tests/components/homekit_controller/test_storage.py +++ b/tests/components/homekit_controller/test_storage.py @@ -95,7 +95,6 @@ async def test_storage_is_removed_on_config_entry_removal(hass, utcnow): "TestData", pairing_data, "test", - system_options={}, ) assert hkid in hass.data[ENTITY_MAP].storage_data diff --git a/tests/components/homematicip_cloud/conftest.py b/tests/components/homematicip_cloud/conftest.py index b5dd6105e0f..c720df4a1bb 100644 --- a/tests/components/homematicip_cloud/conftest.py +++ b/tests/components/homematicip_cloud/conftest.py @@ -62,7 +62,6 @@ def hmip_config_entry_fixture() -> config_entries.ConfigEntry: unique_id=HAPID, data=entry_data, source=SOURCE_IMPORT, - system_options={"disable_new_entities": False}, ) return config_entry diff --git a/tests/components/hue/conftest.py b/tests/components/hue/conftest.py index b5c2aec3042..648337d7539 100644 --- a/tests/components/hue/conftest.py +++ b/tests/components/hue/conftest.py @@ -127,7 +127,6 @@ async def setup_bridge_for_sensors(hass, mock_bridge, hostname=None): domain=hue.DOMAIN, title="Mock Title", data={"host": hostname}, - system_options={}, ) mock_bridge.config_entry = config_entry hass.data[hue.DOMAIN] = {config_entry.entry_id: mock_bridge} diff --git a/tests/components/hue/test_bridge.py b/tests/components/hue/test_bridge.py index eb5c93862fe..034acf88efa 100644 --- a/tests/components/hue/test_bridge.py +++ b/tests/components/hue/test_bridge.py @@ -181,7 +181,6 @@ async def test_hue_activate_scene(hass, mock_api): "Mock Title", {"host": "mock-host", "username": "mock-username"}, "test", - system_options={}, options={CONF_ALLOW_HUE_GROUPS: True, CONF_ALLOW_UNREACHABLE: False}, ) hue_bridge = bridge.HueBridge(hass, config_entry) @@ -215,7 +214,6 @@ async def test_hue_activate_scene_transition(hass, mock_api): "Mock Title", {"host": "mock-host", "username": "mock-username"}, "test", - system_options={}, options={CONF_ALLOW_HUE_GROUPS: True, CONF_ALLOW_UNREACHABLE: False}, ) hue_bridge = bridge.HueBridge(hass, config_entry) @@ -249,7 +247,6 @@ async def test_hue_activate_scene_group_not_found(hass, mock_api): "Mock Title", {"host": "mock-host", "username": "mock-username"}, "test", - system_options={}, options={CONF_ALLOW_HUE_GROUPS: True, CONF_ALLOW_UNREACHABLE: False}, ) hue_bridge = bridge.HueBridge(hass, config_entry) @@ -278,7 +275,6 @@ async def test_hue_activate_scene_scene_not_found(hass, mock_api): "Mock Title", {"host": "mock-host", "username": "mock-username"}, "test", - system_options={}, options={CONF_ALLOW_HUE_GROUPS: True, CONF_ALLOW_UNREACHABLE: False}, ) hue_bridge = bridge.HueBridge(hass, config_entry) diff --git a/tests/components/hue/test_light.py b/tests/components/hue/test_light.py index 5efb74d015f..f4f663c23ae 100644 --- a/tests/components/hue/test_light.py +++ b/tests/components/hue/test_light.py @@ -179,7 +179,6 @@ async def setup_bridge(hass, mock_bridge): "Mock Title", {"host": "mock-host"}, "test", - system_options={}, ) mock_bridge.config_entry = config_entry hass.data[hue.DOMAIN] = {config_entry.entry_id: mock_bridge} diff --git a/tests/components/huisbaasje/test_init.py b/tests/components/huisbaasje/test_init.py index dde62a9c78b..390dc6c304d 100644 --- a/tests/components/huisbaasje/test_init.py +++ b/tests/components/huisbaasje/test_init.py @@ -41,7 +41,6 @@ async def test_setup_entry(hass: HomeAssistant): CONF_PASSWORD: "password", }, source="test", - system_options={}, ) config_entry.add_to_hass(hass) @@ -81,7 +80,6 @@ async def test_setup_entry_error(hass: HomeAssistant): CONF_PASSWORD: "password", }, source="test", - system_options={}, ) config_entry.add_to_hass(hass) @@ -122,7 +120,6 @@ async def test_unload_entry(hass: HomeAssistant): CONF_PASSWORD: "password", }, source="test", - system_options={}, ) config_entry.add_to_hass(hass) diff --git a/tests/components/huisbaasje/test_sensor.py b/tests/components/huisbaasje/test_sensor.py index c753c89627d..45ce20af628 100644 --- a/tests/components/huisbaasje/test_sensor.py +++ b/tests/components/huisbaasje/test_sensor.py @@ -34,7 +34,6 @@ async def test_setup_entry(hass: HomeAssistant): CONF_PASSWORD: "password", }, source="test", - system_options={}, ) config_entry.add_to_hass(hass) @@ -90,7 +89,6 @@ async def test_setup_entry_absent_measurement(hass: HomeAssistant): CONF_PASSWORD: "password", }, source="test", - system_options={}, ) config_entry.add_to_hass(hass) diff --git a/tests/components/hvv_departures/test_config_flow.py b/tests/components/hvv_departures/test_config_flow.py index 4a18e639315..9c510bb3db0 100644 --- a/tests/components/hvv_departures/test_config_flow.py +++ b/tests/components/hvv_departures/test_config_flow.py @@ -256,7 +256,6 @@ async def test_options_flow(hass): title="Wartenau", data=FIXTURE_CONFIG_ENTRY, source=SOURCE_USER, - system_options={"disable_new_entities": False}, options=FIXTURE_OPTIONS, unique_id="1234", ) @@ -306,7 +305,6 @@ async def test_options_flow_invalid_auth(hass): title="Wartenau", data=FIXTURE_CONFIG_ENTRY, source=SOURCE_USER, - system_options={"disable_new_entities": False}, options=FIXTURE_OPTIONS, unique_id="1234", ) @@ -346,7 +344,6 @@ async def test_options_flow_cannot_connect(hass): title="Wartenau", data=FIXTURE_CONFIG_ENTRY, source=SOURCE_USER, - system_options={"disable_new_entities": False}, options=FIXTURE_OPTIONS, unique_id="1234", ) diff --git a/tests/components/smartthings/conftest.py b/tests/components/smartthings/conftest.py index be822371030..2a7b5ed7084 100644 --- a/tests/components/smartthings/conftest.py +++ b/tests/components/smartthings/conftest.py @@ -61,7 +61,6 @@ async def setup_platform(hass, platform: str, *, devices=None, scenes=None): "Test", {CONF_INSTALLED_APP_ID: str(uuid4())}, SOURCE_USER, - system_options={}, ) broker = DeviceBroker( hass, config_entry, Mock(), Mock(), devices or [], scenes or [] diff --git a/tests/components/unifi/test_device_tracker.py b/tests/components/unifi/test_device_tracker.py index 62a52b500f9..d583cad86c3 100644 --- a/tests/components/unifi/test_device_tracker.py +++ b/tests/components/unifi/test_device_tracker.py @@ -943,7 +943,6 @@ async def test_restoring_client(hass, aioclient_mock): title="Mock Title", data=ENTRY_CONFIG, source="test", - system_options={}, options={}, entry_id=1, ) diff --git a/tests/components/unifi/test_switch.py b/tests/components/unifi/test_switch.py index 5c4a65e0a78..ad277f18a8d 100644 --- a/tests/components/unifi/test_switch.py +++ b/tests/components/unifi/test_switch.py @@ -793,7 +793,6 @@ async def test_restore_client_succeed(hass, aioclient_mock): title="Mock Title", data=ENTRY_CONFIG, source="test", - system_options={}, options={}, entry_id=1, ) @@ -884,7 +883,6 @@ async def test_restore_client_no_old_state(hass, aioclient_mock): title="Mock Title", data=ENTRY_CONFIG, source="test", - system_options={}, options={}, entry_id=1, ) diff --git a/tests/components/zwave/test_lock.py b/tests/components/zwave/test_lock.py index f265b36dcb6..04d46620013 100644 --- a/tests/components/zwave/test_lock.py +++ b/tests/components/zwave/test_lock.py @@ -286,7 +286,6 @@ async def setup_ozw(hass, mock_openzwave): "Mock Title", {"usb_path": "mock-path", "network_key": "mock-key"}, "test", - system_options={}, ) await hass.config_entries.async_forward_entry_setup(config_entry, "lock") await hass.async_block_till_done() diff --git a/tests/helpers/test_entity_platform.py b/tests/helpers/test_entity_platform.py index 944f02d46c0..65a46f33cd8 100644 --- a/tests/helpers/test_entity_platform.py +++ b/tests/helpers/test_entity_platform.py @@ -60,9 +60,7 @@ async def test_polling_only_updates_entities_it_should_poll(hass): async def test_polling_disabled_by_config_entry(hass): """Test the polling of only updated entities.""" entity_platform = MockEntityPlatform(hass) - entity_platform.config_entry = MockConfigEntry( - system_options={"disable_polling": True} - ) + entity_platform.config_entry = MockConfigEntry(pref_disable_polling=True) poll_ent = MockEntity(should_poll=True) diff --git a/tests/helpers/test_entity_registry.py b/tests/helpers/test_entity_registry.py index a124e1e6da1..fe445e32c96 100644 --- a/tests/helpers/test_entity_registry.py +++ b/tests/helpers/test_entity_registry.py @@ -513,12 +513,12 @@ async def test_disabled_by(registry): assert entry2.disabled_by is None -async def test_disabled_by_system_options(registry): - """Test system options setting disabled_by.""" +async def test_disabled_by_config_entry_pref(registry): + """Test config entry preference setting disabled_by.""" mock_config = MockConfigEntry( domain="light", entry_id="mock-id-1", - system_options={"disable_new_entities": True}, + pref_disable_new_entities=True, ) entry = registry.async_get_or_create( "light", "hue", "AAAA", config_entry=mock_config diff --git a/tests/helpers/test_update_coordinator.py b/tests/helpers/test_update_coordinator.py index a0ce751aed8..7023798f2b4 100644 --- a/tests/helpers/test_update_coordinator.py +++ b/tests/helpers/test_update_coordinator.py @@ -376,7 +376,7 @@ async def test_async_config_entry_first_refresh_success(crd, caplog): async def test_not_schedule_refresh_if_system_option_disable_polling(hass): """Test we do not schedule a refresh if disable polling in config entry.""" - entry = MockConfigEntry(system_options={"disable_polling": True}) + entry = MockConfigEntry(pref_disable_polling=True) config_entries.current_entry.set(entry) crd = get_crd(hass, DEFAULT_UPDATE_INTERVAL) crd.async_add_listener(lambda: None) diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 18791b1eb2d..1fca4b061cc 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -575,6 +575,13 @@ async def test_saving_and_loading(hass): ) assert len(hass.config_entries.async_entries()) == 2 + entry_1 = hass.config_entries.async_entries()[0] + + hass.config_entries.async_update_entry( + entry_1, + pref_disable_polling=True, + pref_disable_new_entities=True, + ) # To trigger the call_later async_fire_time_changed(hass, dt.utcnow() + timedelta(seconds=1)) @@ -597,6 +604,8 @@ async def test_saving_and_loading(hass): assert orig.data == loaded.data assert orig.source == loaded.source assert orig.unique_id == loaded.unique_id + assert orig.pref_disable_new_entities == loaded.pref_disable_new_entities + assert orig.pref_disable_polling == loaded.pref_disable_polling async def test_forward_entry_sets_up_component(hass): @@ -814,14 +823,19 @@ async def test_updating_entry_system_options(manager): domain="test", data={"first": True}, state=config_entries.ConfigEntryState.SETUP_ERROR, - system_options={"disable_new_entities": True}, + pref_disable_new_entities=True, ) entry.add_to_manager(manager) - assert entry.system_options.disable_new_entities + assert entry.pref_disable_new_entities is True + assert entry.pref_disable_polling is False - entry.system_options.update(disable_new_entities=False) - assert not entry.system_options.disable_new_entities + manager.async_update_entry( + entry, pref_disable_new_entities=False, pref_disable_polling=True + ) + + assert entry.pref_disable_new_entities is False + assert entry.pref_disable_polling is True async def test_update_entry_options_and_trigger_listener(hass, manager): @@ -2558,48 +2572,18 @@ async def test_updating_entry_with_and_without_changes(manager): entry.add_to_manager(manager) assert manager.async_update_entry(entry) is False - assert manager.async_update_entry(entry, data={"second": True}) is True - assert manager.async_update_entry(entry, data={"second": True}) is False - assert ( - manager.async_update_entry(entry, data={"second": True, "third": 456}) is True - ) - assert ( - manager.async_update_entry(entry, data={"second": True, "third": 456}) is False - ) - assert manager.async_update_entry(entry, options={"second": True}) is True - assert manager.async_update_entry(entry, options={"second": True}) is False - assert ( - manager.async_update_entry(entry, options={"second": True, "third": "123"}) - is True - ) - assert ( - manager.async_update_entry(entry, options={"second": True, "third": "123"}) - is False - ) - assert ( - manager.async_update_entry(entry, system_options={"disable_new_entities": True}) - is True - ) - assert ( - manager.async_update_entry(entry, system_options={"disable_new_entities": True}) - is False - ) - assert ( - manager.async_update_entry( - entry, system_options={"disable_new_entities": False} - ) - is True - ) - assert ( - manager.async_update_entry( - entry, system_options={"disable_new_entities": False} - ) - is False - ) - assert manager.async_update_entry(entry, title="thetitle") is False - assert manager.async_update_entry(entry, title="newtitle") is True - assert manager.async_update_entry(entry, unique_id="abc123") is False - assert manager.async_update_entry(entry, unique_id="abc1234") is True + + for change in ( + {"data": {"second": True, "third": 456}}, + {"data": {"second": True}}, + {"options": {"hello": True}}, + {"pref_disable_new_entities": True}, + {"pref_disable_polling": True}, + {"title": "sometitle"}, + {"unique_id": "abcd1234"}, + ): + assert manager.async_update_entry(entry, **change) is True + assert manager.async_update_entry(entry, **change) is False async def test_entry_reload_calls_on_unload_listeners(hass, manager): @@ -2864,3 +2848,35 @@ async def test__async_abort_entries_match(hass, manager, matchers, reason): assert result["type"] == RESULT_TYPE_ABORT assert result["reason"] == reason + + +async def test_loading_old_data(hass, hass_storage): + """Test automatically migrating old data.""" + hass_storage[config_entries.STORAGE_KEY] = { + "version": 1, + "data": { + "entries": [ + { + "version": 5, + "domain": "my_domain", + "entry_id": "mock-id", + "data": {"my": "data"}, + "source": "user", + "title": "Mock title", + "system_options": {"disable_new_entities": True}, + } + ] + }, + } + manager = config_entries.ConfigEntries(hass, {}) + await manager.async_initialize() + + entries = manager.async_entries() + assert len(entries) == 1 + entry = entries[0] + assert entry.version == 5 + assert entry.domain == "my_domain" + assert entry.entry_id == "mock-id" + assert entry.title == "Mock title" + assert entry.data == {"my": "data"} + assert entry.pref_disable_new_entities is True