Merge system options into pref properties (#51347)

* Make system options future proof

* Update tests

* Add types
This commit is contained in:
Paulus Schoutsen 2021-06-01 13:34:31 -07:00 committed by GitHub
parent 9e3ee73b8b
commit ee2c950716
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 188 additions and 245 deletions

View file

@ -1,4 +1,6 @@
"""Http views to control the config manager.""" """Http views to control the config manager."""
from __future__ import annotations
import aiohttp.web_exceptions import aiohttp.web_exceptions
import voluptuous as vol 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 import websocket_api
from homeassistant.components.http import HomeAssistantView from homeassistant.components.http import HomeAssistantView
from homeassistant.const import HTTP_FORBIDDEN, HTTP_NOT_FOUND 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.exceptions import Unauthorized
from homeassistant.helpers.data_entry_flow import ( from homeassistant.helpers.data_entry_flow import (
FlowManagerIndexView, 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_disable)
hass.components.websocket_api.async_register_command(config_entry_update) 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(config_entries_progress)
hass.components.websocket_api.async_register_command(system_options_update)
hass.components.websocket_api.async_register_command(ignore_config_flow) hass.components.websocket_api.async_register_command(ignore_config_flow)
return True 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.""" """Send Config entry not found error."""
connection.send_error( connection.send_error(
msg_id, websocket_api.const.ERR_NOT_FOUND, "Config entry not found" 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.""" """Get entry, send error message if it doesn't exist."""
entry = hass.config_entries.async_get_entry(entry_id) entry = hass.config_entries.async_get_entry(entry_id)
if entry is None: if entry is None:
@ -249,49 +257,13 @@ def get_entry(hass, connection, entry_id, msg_id):
@websocket_api.async_response @websocket_api.async_response
@websocket_api.websocket_command( @websocket_api.websocket_command(
{ {
"type": "config_entries/system_options/update", "type": "config_entries/update",
"entry_id": str, "entry_id": str,
vol.Optional("disable_new_entities"): bool, vol.Optional("title"): str,
vol.Optional("disable_polling"): bool, 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): async def config_entry_update(hass, connection, msg):
"""Update config entry.""" """Update config entry."""
changes = dict(msg) changes = dict(msg)
@ -303,8 +275,25 @@ async def config_entry_update(hass, connection, msg):
if entry is None: if entry is None:
return return
old_disable_polling = entry.pref_disable_polling
hass.config_entries.async_update_entry(entry, **changes) 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 @websocket_api.require_admin
@ -391,7 +380,8 @@ def entry_json(entry: config_entries.ConfigEntry) -> dict:
"state": entry.state.value, "state": entry.state.value,
"supports_options": supports_options, "supports_options": supports_options,
"supports_unload": entry.supports_unload, "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, "disabled_by": entry.disabled_by,
"reason": entry.reason, "reason": entry.reason,
} }

View file

@ -11,8 +11,6 @@ from types import MappingProxyType, MethodType
from typing import Any, Callable, Optional, cast from typing import Any, Callable, Optional, cast
import weakref import weakref
import attr
from homeassistant import data_entry_flow, loader from homeassistant import data_entry_flow, loader
from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import CALLBACK_TYPE, CoreState, HomeAssistant, callback from homeassistant.core import CALLBACK_TYPE, CoreState, HomeAssistant, callback
@ -152,7 +150,8 @@ class ConfigEntry:
"options", "options",
"unique_id", "unique_id",
"supports_unload", "supports_unload",
"system_options", "pref_disable_new_entities",
"pref_disable_polling",
"source", "source",
"state", "state",
"disabled_by", "disabled_by",
@ -170,7 +169,8 @@ class ConfigEntry:
title: str, title: str,
data: Mapping[str, Any], data: Mapping[str, Any],
source: str, source: str,
system_options: dict, pref_disable_new_entities: bool | None = None,
pref_disable_polling: bool | None = None,
options: Mapping[str, Any] | None = None, options: Mapping[str, Any] | None = None,
unique_id: str | None = None, unique_id: str | None = None,
entry_id: str | None = None, entry_id: str | None = None,
@ -197,7 +197,15 @@ class ConfigEntry:
self.options = MappingProxyType(options or {}) self.options = MappingProxyType(options or {})
# Entry system options # 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) # Source of the configuration (user, discovery, cloud)
self.source = source self.source = source
@ -535,7 +543,8 @@ class ConfigEntry:
"title": self.title, "title": self.title,
"data": dict(self.data), "data": dict(self.data),
"options": dict(self.options), "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, "source": self.source,
"unique_id": self.unique_id, "unique_id": self.unique_id,
"disabled_by": self.disabled_by, "disabled_by": self.disabled_by,
@ -652,7 +661,6 @@ class ConfigEntriesFlowManager(data_entry_flow.FlowManager):
title=result["title"], title=result["title"],
data=result["data"], data=result["data"],
options=result["options"], options=result["options"],
system_options={},
source=flow.context["source"], source=flow.context["source"],
unique_id=flow.unique_id, unique_id=flow.unique_id,
) )
@ -845,8 +853,18 @@ class ConfigEntries:
self._entries = {} self._entries = {}
return return
self._entries = { entries = {}
entry["entry_id"]: ConfigEntry(
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"], version=entry["version"],
domain=entry["domain"], domain=entry["domain"],
entry_id=entry["entry_id"], entry_id=entry["entry_id"],
@ -855,15 +873,16 @@ class ConfigEntries:
title=entry["title"], title=entry["title"],
# New in 0.89 # New in 0.89
options=entry.get("options"), options=entry.get("options"),
# New in 0.98
system_options=entry.get("system_options", {}),
# New in 0.104 # New in 0.104
unique_id=entry.get("unique_id"), unique_id=entry.get("unique_id"),
# New in 2021.3 # New in 2021.3
disabled_by=entry.get("disabled_by"), 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: async def async_setup(self, entry_id: str) -> bool:
"""Set up a config entry. """Set up a config entry.
@ -962,11 +981,12 @@ class ConfigEntries:
self, self,
entry: ConfigEntry, entry: ConfigEntry,
*, *,
unique_id: str | dict | None | UndefinedType = UNDEFINED, unique_id: str | None | UndefinedType = UNDEFINED,
title: str | dict | UndefinedType = UNDEFINED, title: str | UndefinedType = UNDEFINED,
data: dict | UndefinedType = UNDEFINED, data: dict | UndefinedType = UNDEFINED,
options: Mapping[str, Any] | 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: ) -> bool:
"""Update a config entry. """Update a config entry.
@ -978,13 +998,17 @@ class ConfigEntries:
""" """
changed = False changed = False
if unique_id is not UNDEFINED and entry.unique_id != unique_id: for attr, value in (
changed = True ("unique_id", unique_id),
entry.unique_id = cast(Optional[str], 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 changed = True
entry.title = cast(str, title)
if data is not UNDEFINED and entry.data != data: # type: ignore if data is not UNDEFINED and entry.data != data: # type: ignore
changed = True changed = True
@ -994,11 +1018,6 @@ class ConfigEntries:
changed = True changed = True
entry.options = MappingProxyType(options) 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: if not changed:
return False return False
@ -1401,34 +1420,6 @@ class OptionsFlow(data_entry_flow.FlowHandler):
handler: str 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: class EntityRegistryDisabledHandler:
"""Handler to handle when entities related to config entries updating disabled_by.""" """Handler to handle when entities related to config entries updating disabled_by."""

View file

@ -397,7 +397,7 @@ class EntityPlatform:
raise raise
if ( 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 self._async_unsub_polling is not None
or not any(entity.should_poll for entity in self.entities.values()) or not any(entity.should_poll for entity in self.entities.values())
): ):

View file

@ -286,7 +286,7 @@ class EntityRegistry:
if ( if (
disabled_by is None disabled_by is None
and config_entry and config_entry
and config_entry.system_options.disable_new_entities and config_entry.pref_disable_new_entities
): ):
disabled_by = DISABLED_INTEGRATION disabled_by = DISABLED_INTEGRATION

View file

@ -111,7 +111,7 @@ class DataUpdateCoordinator(Generic[T]):
if self.update_interval is None: if self.update_interval is None:
return 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 return
if self._unsub_refresh: if self._unsub_refresh:

View file

@ -732,7 +732,8 @@ class MockConfigEntry(config_entries.ConfigEntry):
title="Mock Title", title="Mock Title",
state=None, state=None,
options={}, options={},
system_options={}, pref_disable_new_entities=None,
pref_disable_polling=None,
unique_id=None, unique_id=None,
disabled_by=None, disabled_by=None,
reason=None, reason=None,
@ -742,7 +743,8 @@ class MockConfigEntry(config_entries.ConfigEntry):
"entry_id": entry_id or uuid_util.random_uuid_hex(), "entry_id": entry_id or uuid_util.random_uuid_hex(),
"domain": domain, "domain": domain,
"data": data or {}, "data": data or {},
"system_options": system_options, "pref_disable_new_entities": pref_disable_new_entities,
"pref_disable_polling": pref_disable_polling,
"options": options, "options": options,
"version": version, "version": version,
"title": title, "title": title,

View file

@ -29,7 +29,6 @@ FIXTURE_CONFIG_ENTRY = {
CONF_REGION: FIXTURE_USER_INPUT[CONF_REGION], CONF_REGION: FIXTURE_USER_INPUT[CONF_REGION],
}, },
"options": {CONF_READ_ONLY: False, CONF_USE_LOCATION: False}, "options": {CONF_READ_ONLY: False, CONF_USE_LOCATION: False},
"system_options": {"disable_new_entities": False},
"source": config_entries.SOURCE_USER, "source": config_entries.SOURCE_USER,
"unique_id": f"{FIXTURE_USER_INPUT[CONF_REGION]}-{FIXTURE_USER_INPUT[CONF_REGION]}", "unique_id": f"{FIXTURE_USER_INPUT[CONF_REGION]}-{FIXTURE_USER_INPUT[CONF_REGION]}",
} }

View file

@ -87,10 +87,8 @@ async def test_get_entries(hass, client):
"state": core_ce.ConfigEntryState.NOT_LOADED.value, "state": core_ce.ConfigEntryState.NOT_LOADED.value,
"supports_options": True, "supports_options": True,
"supports_unload": True, "supports_unload": True,
"system_options": { "pref_disable_new_entities": False,
"disable_new_entities": False, "pref_disable_polling": False,
"disable_polling": False,
},
"disabled_by": None, "disabled_by": None,
"reason": None, "reason": None,
}, },
@ -101,10 +99,8 @@ async def test_get_entries(hass, client):
"state": core_ce.ConfigEntryState.SETUP_ERROR.value, "state": core_ce.ConfigEntryState.SETUP_ERROR.value,
"supports_options": False, "supports_options": False,
"supports_unload": False, "supports_unload": False,
"system_options": { "pref_disable_new_entities": False,
"disable_new_entities": False, "pref_disable_polling": False,
"disable_polling": False,
},
"disabled_by": None, "disabled_by": None,
"reason": "Unsupported API", "reason": "Unsupported API",
}, },
@ -115,10 +111,8 @@ async def test_get_entries(hass, client):
"state": core_ce.ConfigEntryState.NOT_LOADED.value, "state": core_ce.ConfigEntryState.NOT_LOADED.value,
"supports_options": False, "supports_options": False,
"supports_unload": False, "supports_unload": False,
"system_options": { "pref_disable_new_entities": False,
"disable_new_entities": False, "pref_disable_polling": False,
"disable_polling": False,
},
"disabled_by": core_ce.DISABLED_USER, "disabled_by": core_ce.DISABLED_USER,
"reason": None, "reason": None,
}, },
@ -340,10 +334,8 @@ async def test_create_account(hass, client, enable_custom_integrations):
"state": core_ce.ConfigEntryState.LOADED.value, "state": core_ce.ConfigEntryState.LOADED.value,
"supports_options": False, "supports_options": False,
"supports_unload": False, "supports_unload": False,
"system_options": { "pref_disable_new_entities": False,
"disable_new_entities": False, "pref_disable_polling": False,
"disable_polling": False,
},
"title": "Test Entry", "title": "Test Entry",
"reason": None, "reason": None,
}, },
@ -415,10 +407,8 @@ async def test_two_step_flow(hass, client, enable_custom_integrations):
"state": core_ce.ConfigEntryState.LOADED.value, "state": core_ce.ConfigEntryState.LOADED.value,
"supports_options": False, "supports_options": False,
"supports_unload": False, "supports_unload": False,
"system_options": { "pref_disable_new_entities": False,
"disable_new_entities": False, "pref_disable_polling": False,
"disable_polling": False,
},
"title": "user-title", "title": "user-title",
"reason": None, "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.""" """Test that we can update system options."""
assert await async_setup_component(hass, "config", {}) assert await async_setup_component(hass, "config", {})
ws_client = await hass_ws_client(hass) 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 = MockConfigEntry(domain="demo", state=core_ce.ConfigEntryState.LOADED)
entry.add_to_hass(hass) entry.add_to_hass(hass)
assert entry.system_options.disable_new_entities is False assert entry.pref_disable_new_entities is False
assert entry.system_options.disable_polling is False assert entry.pref_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
await ws_client.send_json( await ws_client.send_json(
{ {
"id": 6, "id": 6,
"type": "config_entries/system_options/update", "type": "config_entries/update",
"entry_id": entry.entry_id, "entry_id": entry.entry_id,
"disable_new_entities": False, "pref_disable_new_entities": True,
"disable_polling": True,
} }
) )
response = await ws_client.receive_json() response = await ws_client.receive_json()
assert response["success"] assert response["success"]
assert response["result"] == { assert response["result"]["require_restart"] is False
"require_restart": True, assert response["result"]["config_entry"]["pref_disable_new_entities"] is True
"system_options": {"disable_new_entities": False, "disable_polling": True}, assert response["result"]["config_entry"]["pref_disable_polling"] is False
}
assert entry.system_options.disable_new_entities is False
assert entry.system_options.disable_polling is True
assert entry.pref_disable_new_entities is True
async def test_update_system_options_nonexisting(hass, hass_ws_client): assert entry.pref_disable_polling is False
"""Test that we can update entry."""
assert await async_setup_component(hass, "config", {})
ws_client = await hass_ws_client(hass)
await ws_client.send_json( await ws_client.send_json(
{ {
"id": 5, "id": 7,
"type": "config_entries/system_options/update", "type": "config_entries/update",
"entry_id": "non_existing", "entry_id": entry.entry_id,
"disable_new_entities": True, "pref_disable_new_entities": False,
"pref_disable_polling": True,
} }
) )
response = await ws_client.receive_json() response = await ws_client.receive_json()
assert not response["success"] assert response["success"]
assert response["error"]["code"] == "not_found" 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): 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() response = await ws_client.receive_json()
assert response["success"] assert response["success"]
assert response["result"]["title"] == "Updated Title" assert response["result"]["config_entry"]["title"] == "Updated Title"
assert entry.title == "Updated Title" assert entry.title == "Updated Title"

View file

@ -46,7 +46,6 @@ def config_entry_fixture():
title="", title="",
data=data, data=data,
options={}, options={},
system_options={},
source=SOURCE_USER, source=SOURCE_USER,
entry_id=1, entry_id=1,
) )

View file

@ -282,7 +282,6 @@ def config_entry_fixture():
title="", title="",
data=data, data=data,
options={CONF_TTS_PAUSE_TIME: 0}, options={CONF_TTS_PAUSE_TIME: 0},
system_options={},
source=SOURCE_USER, source=SOURCE_USER,
entry_id=1, entry_id=1,
) )

View file

@ -35,7 +35,6 @@ def mock_config_entry():
}, },
source="test", source="test",
options={}, options={},
system_options={"disable_new_entities": False},
unique_id=DOMAIN, unique_id=DOMAIN,
entry_id="home_plus_control_entry_id", entry_id="home_plus_control_entry_id",
) )

View file

@ -95,7 +95,6 @@ async def test_storage_is_removed_on_config_entry_removal(hass, utcnow):
"TestData", "TestData",
pairing_data, pairing_data,
"test", "test",
system_options={},
) )
assert hkid in hass.data[ENTITY_MAP].storage_data assert hkid in hass.data[ENTITY_MAP].storage_data

View file

@ -62,7 +62,6 @@ def hmip_config_entry_fixture() -> config_entries.ConfigEntry:
unique_id=HAPID, unique_id=HAPID,
data=entry_data, data=entry_data,
source=SOURCE_IMPORT, source=SOURCE_IMPORT,
system_options={"disable_new_entities": False},
) )
return config_entry return config_entry

View file

@ -127,7 +127,6 @@ async def setup_bridge_for_sensors(hass, mock_bridge, hostname=None):
domain=hue.DOMAIN, domain=hue.DOMAIN,
title="Mock Title", title="Mock Title",
data={"host": hostname}, data={"host": hostname},
system_options={},
) )
mock_bridge.config_entry = config_entry mock_bridge.config_entry = config_entry
hass.data[hue.DOMAIN] = {config_entry.entry_id: mock_bridge} hass.data[hue.DOMAIN] = {config_entry.entry_id: mock_bridge}

View file

@ -181,7 +181,6 @@ async def test_hue_activate_scene(hass, mock_api):
"Mock Title", "Mock Title",
{"host": "mock-host", "username": "mock-username"}, {"host": "mock-host", "username": "mock-username"},
"test", "test",
system_options={},
options={CONF_ALLOW_HUE_GROUPS: True, CONF_ALLOW_UNREACHABLE: False}, options={CONF_ALLOW_HUE_GROUPS: True, CONF_ALLOW_UNREACHABLE: False},
) )
hue_bridge = bridge.HueBridge(hass, config_entry) hue_bridge = bridge.HueBridge(hass, config_entry)
@ -215,7 +214,6 @@ async def test_hue_activate_scene_transition(hass, mock_api):
"Mock Title", "Mock Title",
{"host": "mock-host", "username": "mock-username"}, {"host": "mock-host", "username": "mock-username"},
"test", "test",
system_options={},
options={CONF_ALLOW_HUE_GROUPS: True, CONF_ALLOW_UNREACHABLE: False}, options={CONF_ALLOW_HUE_GROUPS: True, CONF_ALLOW_UNREACHABLE: False},
) )
hue_bridge = bridge.HueBridge(hass, config_entry) 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", "Mock Title",
{"host": "mock-host", "username": "mock-username"}, {"host": "mock-host", "username": "mock-username"},
"test", "test",
system_options={},
options={CONF_ALLOW_HUE_GROUPS: True, CONF_ALLOW_UNREACHABLE: False}, options={CONF_ALLOW_HUE_GROUPS: True, CONF_ALLOW_UNREACHABLE: False},
) )
hue_bridge = bridge.HueBridge(hass, config_entry) 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", "Mock Title",
{"host": "mock-host", "username": "mock-username"}, {"host": "mock-host", "username": "mock-username"},
"test", "test",
system_options={},
options={CONF_ALLOW_HUE_GROUPS: True, CONF_ALLOW_UNREACHABLE: False}, options={CONF_ALLOW_HUE_GROUPS: True, CONF_ALLOW_UNREACHABLE: False},
) )
hue_bridge = bridge.HueBridge(hass, config_entry) hue_bridge = bridge.HueBridge(hass, config_entry)

View file

@ -179,7 +179,6 @@ async def setup_bridge(hass, mock_bridge):
"Mock Title", "Mock Title",
{"host": "mock-host"}, {"host": "mock-host"},
"test", "test",
system_options={},
) )
mock_bridge.config_entry = config_entry mock_bridge.config_entry = config_entry
hass.data[hue.DOMAIN] = {config_entry.entry_id: mock_bridge} hass.data[hue.DOMAIN] = {config_entry.entry_id: mock_bridge}

View file

@ -41,7 +41,6 @@ async def test_setup_entry(hass: HomeAssistant):
CONF_PASSWORD: "password", CONF_PASSWORD: "password",
}, },
source="test", source="test",
system_options={},
) )
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
@ -81,7 +80,6 @@ async def test_setup_entry_error(hass: HomeAssistant):
CONF_PASSWORD: "password", CONF_PASSWORD: "password",
}, },
source="test", source="test",
system_options={},
) )
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
@ -122,7 +120,6 @@ async def test_unload_entry(hass: HomeAssistant):
CONF_PASSWORD: "password", CONF_PASSWORD: "password",
}, },
source="test", source="test",
system_options={},
) )
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)

View file

@ -34,7 +34,6 @@ async def test_setup_entry(hass: HomeAssistant):
CONF_PASSWORD: "password", CONF_PASSWORD: "password",
}, },
source="test", source="test",
system_options={},
) )
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
@ -90,7 +89,6 @@ async def test_setup_entry_absent_measurement(hass: HomeAssistant):
CONF_PASSWORD: "password", CONF_PASSWORD: "password",
}, },
source="test", source="test",
system_options={},
) )
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)

View file

@ -256,7 +256,6 @@ async def test_options_flow(hass):
title="Wartenau", title="Wartenau",
data=FIXTURE_CONFIG_ENTRY, data=FIXTURE_CONFIG_ENTRY,
source=SOURCE_USER, source=SOURCE_USER,
system_options={"disable_new_entities": False},
options=FIXTURE_OPTIONS, options=FIXTURE_OPTIONS,
unique_id="1234", unique_id="1234",
) )
@ -306,7 +305,6 @@ async def test_options_flow_invalid_auth(hass):
title="Wartenau", title="Wartenau",
data=FIXTURE_CONFIG_ENTRY, data=FIXTURE_CONFIG_ENTRY,
source=SOURCE_USER, source=SOURCE_USER,
system_options={"disable_new_entities": False},
options=FIXTURE_OPTIONS, options=FIXTURE_OPTIONS,
unique_id="1234", unique_id="1234",
) )
@ -346,7 +344,6 @@ async def test_options_flow_cannot_connect(hass):
title="Wartenau", title="Wartenau",
data=FIXTURE_CONFIG_ENTRY, data=FIXTURE_CONFIG_ENTRY,
source=SOURCE_USER, source=SOURCE_USER,
system_options={"disable_new_entities": False},
options=FIXTURE_OPTIONS, options=FIXTURE_OPTIONS,
unique_id="1234", unique_id="1234",
) )

View file

@ -61,7 +61,6 @@ async def setup_platform(hass, platform: str, *, devices=None, scenes=None):
"Test", "Test",
{CONF_INSTALLED_APP_ID: str(uuid4())}, {CONF_INSTALLED_APP_ID: str(uuid4())},
SOURCE_USER, SOURCE_USER,
system_options={},
) )
broker = DeviceBroker( broker = DeviceBroker(
hass, config_entry, Mock(), Mock(), devices or [], scenes or [] hass, config_entry, Mock(), Mock(), devices or [], scenes or []

View file

@ -943,7 +943,6 @@ async def test_restoring_client(hass, aioclient_mock):
title="Mock Title", title="Mock Title",
data=ENTRY_CONFIG, data=ENTRY_CONFIG,
source="test", source="test",
system_options={},
options={}, options={},
entry_id=1, entry_id=1,
) )

View file

@ -793,7 +793,6 @@ async def test_restore_client_succeed(hass, aioclient_mock):
title="Mock Title", title="Mock Title",
data=ENTRY_CONFIG, data=ENTRY_CONFIG,
source="test", source="test",
system_options={},
options={}, options={},
entry_id=1, entry_id=1,
) )
@ -884,7 +883,6 @@ async def test_restore_client_no_old_state(hass, aioclient_mock):
title="Mock Title", title="Mock Title",
data=ENTRY_CONFIG, data=ENTRY_CONFIG,
source="test", source="test",
system_options={},
options={}, options={},
entry_id=1, entry_id=1,
) )

View file

@ -286,7 +286,6 @@ async def setup_ozw(hass, mock_openzwave):
"Mock Title", "Mock Title",
{"usb_path": "mock-path", "network_key": "mock-key"}, {"usb_path": "mock-path", "network_key": "mock-key"},
"test", "test",
system_options={},
) )
await hass.config_entries.async_forward_entry_setup(config_entry, "lock") await hass.config_entries.async_forward_entry_setup(config_entry, "lock")
await hass.async_block_till_done() await hass.async_block_till_done()

View file

@ -60,9 +60,7 @@ async def test_polling_only_updates_entities_it_should_poll(hass):
async def test_polling_disabled_by_config_entry(hass): async def test_polling_disabled_by_config_entry(hass):
"""Test the polling of only updated entities.""" """Test the polling of only updated entities."""
entity_platform = MockEntityPlatform(hass) entity_platform = MockEntityPlatform(hass)
entity_platform.config_entry = MockConfigEntry( entity_platform.config_entry = MockConfigEntry(pref_disable_polling=True)
system_options={"disable_polling": True}
)
poll_ent = MockEntity(should_poll=True) poll_ent = MockEntity(should_poll=True)

View file

@ -513,12 +513,12 @@ async def test_disabled_by(registry):
assert entry2.disabled_by is None assert entry2.disabled_by is None
async def test_disabled_by_system_options(registry): async def test_disabled_by_config_entry_pref(registry):
"""Test system options setting disabled_by.""" """Test config entry preference setting disabled_by."""
mock_config = MockConfigEntry( mock_config = MockConfigEntry(
domain="light", domain="light",
entry_id="mock-id-1", entry_id="mock-id-1",
system_options={"disable_new_entities": True}, pref_disable_new_entities=True,
) )
entry = registry.async_get_or_create( entry = registry.async_get_or_create(
"light", "hue", "AAAA", config_entry=mock_config "light", "hue", "AAAA", config_entry=mock_config

View file

@ -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): 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.""" """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) config_entries.current_entry.set(entry)
crd = get_crd(hass, DEFAULT_UPDATE_INTERVAL) crd = get_crd(hass, DEFAULT_UPDATE_INTERVAL)
crd.async_add_listener(lambda: None) crd.async_add_listener(lambda: None)

View file

@ -575,6 +575,13 @@ async def test_saving_and_loading(hass):
) )
assert len(hass.config_entries.async_entries()) == 2 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 # To trigger the call_later
async_fire_time_changed(hass, dt.utcnow() + timedelta(seconds=1)) 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.data == loaded.data
assert orig.source == loaded.source assert orig.source == loaded.source
assert orig.unique_id == loaded.unique_id 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): async def test_forward_entry_sets_up_component(hass):
@ -814,14 +823,19 @@ async def test_updating_entry_system_options(manager):
domain="test", domain="test",
data={"first": True}, data={"first": True},
state=config_entries.ConfigEntryState.SETUP_ERROR, state=config_entries.ConfigEntryState.SETUP_ERROR,
system_options={"disable_new_entities": True}, pref_disable_new_entities=True,
) )
entry.add_to_manager(manager) 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) manager.async_update_entry(
assert not entry.system_options.disable_new_entities 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): 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) entry.add_to_manager(manager)
assert manager.async_update_entry(entry) is False 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 for change in (
assert ( {"data": {"second": True, "third": 456}},
manager.async_update_entry(entry, data={"second": True, "third": 456}) is True {"data": {"second": True}},
) {"options": {"hello": True}},
assert ( {"pref_disable_new_entities": True},
manager.async_update_entry(entry, data={"second": True, "third": 456}) is False {"pref_disable_polling": True},
) {"title": "sometitle"},
assert manager.async_update_entry(entry, options={"second": True}) is True {"unique_id": "abcd1234"},
assert manager.async_update_entry(entry, options={"second": True}) is False ):
assert ( assert manager.async_update_entry(entry, **change) is True
manager.async_update_entry(entry, options={"second": True, "third": "123"}) assert manager.async_update_entry(entry, **change) is False
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
async def test_entry_reload_calls_on_unload_listeners(hass, manager): 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["type"] == RESULT_TYPE_ABORT
assert result["reason"] == reason 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