diff --git a/homeassistant/components/config/config_entries.py b/homeassistant/components/config/config_entries.py index 21fc55ebafc7cf..d7c8a6ea8e08c5 100644 --- a/homeassistant/components/config/config_entries.py +++ b/homeassistant/components/config/config_entries.py @@ -1,6 +1,9 @@ """Http views to control the config manager.""" +import voluptuous as vol + from homeassistant import config_entries, data_entry_flow from homeassistant.auth.permissions.const import CAT_CONFIG_ENTRIES +from homeassistant.components import websocket_api from homeassistant.components.http import HomeAssistantView from homeassistant.exceptions import Unauthorized from homeassistant.helpers.data_entry_flow import ( @@ -17,12 +20,17 @@ async def async_setup(hass): hass.http.register_view(ConfigManagerFlowIndexView(hass.config_entries.flow)) hass.http.register_view(ConfigManagerFlowResourceView(hass.config_entries.flow)) hass.http.register_view(ConfigManagerAvailableFlowView) + hass.http.register_view( OptionManagerFlowIndexView(hass.config_entries.options.flow) ) hass.http.register_view( OptionManagerFlowResourceView(hass.config_entries.options.flow) ) + + hass.components.websocket_api.async_register_command(system_options_list) + hass.components.websocket_api.async_register_command(system_options_update) + return True @@ -231,3 +239,40 @@ async def post(self, request, flow_id): # pylint: disable=no-value-for-parameter return await super().post(request, flow_id) + + +@websocket_api.require_admin +@websocket_api.async_response +@websocket_api.websocket_command( + {"type": "config_entries/system_options/list", "entry_id": str} +) +async def system_options_list(hass, connection, msg): + """List all system options for a config entry.""" + entry_id = msg["entry_id"] + entry = hass.config_entries.async_get_entry(entry_id) + + if entry: + connection.send_result(msg["id"], entry.system_options.as_dict()) + + +@websocket_api.require_admin +@websocket_api.async_response +@websocket_api.websocket_command( + { + "type": "config_entries/system_options/update", + "entry_id": str, + vol.Optional("disable_new_entities"): bool, + } +) +async def system_options_update(hass, connection, msg): + """Update config entry system options.""" + changes = dict(msg) + changes.pop("id") + changes.pop("type") + entry_id = changes.pop("entry_id") + entry = hass.config_entries.async_get_entry(entry_id) + + if entry and changes: + entry.system_options.update(**changes) + + connection.send_result(msg["id"], entry.system_options.as_dict()) diff --git a/homeassistant/components/ps4/__init__.py b/homeassistant/components/ps4/__init__.py index 1e6198f1a38ff8..9baf1adbcc2c1b 100644 --- a/homeassistant/components/ps4/__init__.py +++ b/homeassistant/components/ps4/__init__.py @@ -127,7 +127,7 @@ async def async_migrate_entry(hass, entry): DOMAIN, unique_id, suggested_object_id=new_id, - config_entry_id=e_entry.config_entry_id, + config_entry=entry, device_id=e_entry.device_id, ) entry.version = 3 diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 46c324110ecd46..9844aeb9ca679f 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -12,13 +12,14 @@ ) import weakref +import attr + from homeassistant import data_entry_flow, loader from homeassistant.core import callback, HomeAssistant from homeassistant.exceptions import HomeAssistantError, ConfigEntryNotReady from homeassistant.setup import async_setup_component, async_process_deps_reqs from homeassistant.util.decorator import Registry - # mypy: allow-untyped-defs _LOGGER = logging.getLogger(__name__) @@ -88,6 +89,7 @@ class ConfigEntry: "title", "data", "options", + "system_options", "source", "connection_class", "state", @@ -104,6 +106,7 @@ def __init__( data: dict, source: str, connection_class: str, + system_options: dict, options: Optional[dict] = None, entry_id: Optional[str] = None, state: str = ENTRY_STATE_NOT_LOADED, @@ -127,6 +130,9 @@ def __init__( # Entry options self.options = options or {} + # Entry system options + self.system_options = SystemOptions(**system_options) + # Source of the configuration (user, discovery, cloud) self.source = source @@ -355,6 +361,7 @@ def as_dict(self): "title": self.title, "data": self.data, "options": self.options, + "system_options": self.system_options.as_dict(), "source": self.source, "connection_class": self.connection_class, } @@ -457,6 +464,8 @@ async def async_initialize(self) -> None: connection_class=entry.get("connection_class", CONN_CLASS_UNKNOWN), # New in 0.89 options=entry.get("options"), + # New in 0.98 + system_options=entry.get("system_options", {}), ) for entry in config["entries"] ] @@ -580,6 +589,7 @@ async def _async_finish_flow(self, flow, result): title=result["title"], data=result["data"], options={}, + system_options={}, source=flow.context["source"], connection_class=flow.CONNECTION_CLASS, ) @@ -722,3 +732,18 @@ class OptionsFlow(data_entry_flow.FlowHandler): """Base class for config option flows.""" pass + + +@attr.s(slots=True) +class SystemOptions: + """Config entry system options.""" + + disable_new_entities = attr.ib(type=bool, default=False) + + def update(self, *, disable_new_entities): + """Update properties.""" + self.disable_new_entities = disable_new_entities + + def as_dict(self): + """Return dictionary version of this config entrys system options.""" + return {"disable_new_entities": self.disable_new_entities} diff --git a/homeassistant/helpers/entity_platform.py b/homeassistant/helpers/entity_platform.py index dd19fac05c862d..74351ac50af89f 100644 --- a/homeassistant/helpers/entity_platform.py +++ b/homeassistant/helpers/entity_platform.py @@ -343,7 +343,7 @@ async def _async_add_entity( self.platform_name, entity.unique_id, suggested_object_id=suggested_object_id, - config_entry_id=config_entry_id, + config_entry=self.config_entry, device_id=device_id, known_object_ids=self.entities.keys(), disabled_by=disabled_by, diff --git a/homeassistant/helpers/entity_registry.py b/homeassistant/helpers/entity_registry.py index 8ef41eef9f8bee..3d84313a5c650d 100644 --- a/homeassistant/helpers/entity_registry.py +++ b/homeassistant/helpers/entity_registry.py @@ -33,6 +33,7 @@ SAVE_DELAY = 10 _LOGGER = logging.getLogger(__name__) _UNDEF = object() +DISABLED_CONFIG_ENTRY = "config_entry" DISABLED_HASS = "hass" DISABLED_USER = "user" DISABLED_INTEGRATION = "integration" @@ -55,7 +56,13 @@ class RegistryEntry: type=str, default=None, validator=attr.validators.in_( - (DISABLED_HASS, DISABLED_USER, DISABLED_INTEGRATION, None) + ( + DISABLED_HASS, + DISABLED_USER, + DISABLED_INTEGRATION, + DISABLED_CONFIG_ENTRY, + None, + ) ), ) # type: Optional[str] domain = attr.ib(type=str, init=False, repr=False) @@ -132,13 +139,18 @@ def async_get_or_create( unique_id, *, suggested_object_id=None, - config_entry_id=None, + config_entry=None, device_id=None, known_object_ids=None, disabled_by=None, ): """Get entity. Create if it doesn't exist.""" + config_entry_id = None + if config_entry: + config_entry_id = config_entry.entry_id + entity_id = self.async_get_entity_id(domain, platform, unique_id) + if entity_id: return self._async_update_entity( entity_id, @@ -159,6 +171,13 @@ def async_get_or_create( known_object_ids, ) + if ( + disabled_by is None + and config_entry + and config_entry.system_options.disable_new_entities + ): + disabled_by = DISABLED_INTEGRATION + entity = RegistryEntry( entity_id=entity_id, config_entry_id=config_entry_id, diff --git a/tests/common.py b/tests/common.py index f7816bf2192acc..0e2f701c210a75 100644 --- a/tests/common.py +++ b/tests/common.py @@ -665,6 +665,7 @@ def __init__( title="Mock Title", state=None, options={}, + system_options={}, connection_class=config_entries.CONN_CLASS_UNKNOWN, ): """Initialize a mock config entry.""" @@ -672,6 +673,7 @@ def __init__( "entry_id": entry_id or uuid.uuid4().hex, "domain": domain, "data": data or {}, + "system_options": system_options, "options": options, "version": version, "title": title, diff --git a/tests/components/axis/test_binary_sensor.py b/tests/components/axis/test_binary_sensor.py index 6a58812cbafb33..8e5a6f9675df56 100644 --- a/tests/components/axis/test_binary_sensor.py +++ b/tests/components/axis/test_binary_sensor.py @@ -57,6 +57,7 @@ async def setup_device(hass): ENTRY_CONFIG, "test", config_entries.CONN_CLASS_LOCAL_PUSH, + system_options={}, options=ENTRY_OPTIONS, ) device = axis.AxisNetworkDevice(hass, config_entry) diff --git a/tests/components/axis/test_camera.py b/tests/components/axis/test_camera.py index f4871357d0e937..027dc42748e396 100644 --- a/tests/components/axis/test_camera.py +++ b/tests/components/axis/test_camera.py @@ -41,6 +41,7 @@ async def setup_device(hass): ENTRY_CONFIG, "test", config_entries.CONN_CLASS_LOCAL_PUSH, + system_options={}, options=ENTRY_OPTIONS, ) device = axis.AxisNetworkDevice(hass, config_entry) diff --git a/tests/components/axis/test_switch.py b/tests/components/axis/test_switch.py index 6595f172ef0f87..3469106c436637 100644 --- a/tests/components/axis/test_switch.py +++ b/tests/components/axis/test_switch.py @@ -59,6 +59,7 @@ async def setup_device(hass): ENTRY_CONFIG, "test", config_entries.CONN_CLASS_LOCAL_PUSH, + system_options={}, options=ENTRY_OPTIONS, ) device = axis.AxisNetworkDevice(hass, config_entry) diff --git a/tests/components/config/test_config_entries.py b/tests/components/config/test_config_entries.py index 13cd8da0597c44..f0815e7ede8104 100644 --- a/tests/components/config/test_config_entries.py +++ b/tests/components/config/test_config_entries.py @@ -584,3 +584,47 @@ async def async_step_finish(self, user_input=None): "description": None, "description_placeholders": None, } + + +async def test_list_system_options(hass, hass_ws_client): + """Test that we can list an entries system options.""" + assert await async_setup_component(hass, "config", {}) + ws_client = await hass_ws_client(hass) + + entry = MockConfigEntry(domain="demo") + entry.add_to_hass(hass) + + await ws_client.send_json( + { + "id": 5, + "type": "config_entries/system_options/list", + "entry_id": entry.entry_id, + } + ) + response = await ws_client.receive_json() + + assert response["success"] + assert response["result"] == {"disable_new_entities": False} + + +async def test_update_system_options(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) + + entry = MockConfigEntry(domain="demo") + entry.add_to_hass(hass) + + 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"]["disable_new_entities"] + assert entry.system_options.disable_new_entities diff --git a/tests/components/deconz/test_binary_sensor.py b/tests/components/deconz/test_binary_sensor.py index 9e6d4f571ea4e5..acf06728d0d0d5 100644 --- a/tests/components/deconz/test_binary_sensor.py +++ b/tests/components/deconz/test_binary_sensor.py @@ -59,7 +59,8 @@ async def setup_gateway(hass, data, allow_clip_sensor=True): ENTRY_CONFIG, "test", config_entries.CONN_CLASS_LOCAL_PUSH, - ENTRY_OPTIONS, + system_options={}, + options=ENTRY_OPTIONS, ) gateway = deconz.DeconzGateway(hass, config_entry) gateway.api = DeconzSession(loop, session, **config_entry.data) diff --git a/tests/components/deconz/test_climate.py b/tests/components/deconz/test_climate.py index 68d1957f97efb7..f4972564a8ea60 100644 --- a/tests/components/deconz/test_climate.py +++ b/tests/components/deconz/test_climate.py @@ -71,7 +71,8 @@ async def setup_gateway(hass, data, allow_clip_sensor=True): ENTRY_CONFIG, "test", config_entries.CONN_CLASS_LOCAL_PUSH, - ENTRY_OPTIONS, + system_options={}, + options=ENTRY_OPTIONS, ) gateway = deconz.DeconzGateway(hass, config_entry) gateway.api = DeconzSession(hass.loop, session, **config_entry.data) diff --git a/tests/components/deconz/test_cover.py b/tests/components/deconz/test_cover.py index ee68744f999364..f264877b77a5f8 100644 --- a/tests/components/deconz/test_cover.py +++ b/tests/components/deconz/test_cover.py @@ -63,6 +63,7 @@ async def setup_gateway(hass, data): ENTRY_CONFIG, "test", config_entries.CONN_CLASS_LOCAL_PUSH, + system_options={}, ) gateway = deconz.DeconzGateway(hass, config_entry) gateway.api = DeconzSession(loop, session, **config_entry.data) diff --git a/tests/components/deconz/test_light.py b/tests/components/deconz/test_light.py index ee81e476a12f19..afe7ca445e5738 100644 --- a/tests/components/deconz/test_light.py +++ b/tests/components/deconz/test_light.py @@ -90,7 +90,8 @@ async def setup_gateway(hass, data, allow_deconz_groups=True): ENTRY_CONFIG, "test", config_entries.CONN_CLASS_LOCAL_PUSH, - ENTRY_OPTIONS, + system_options={}, + options=ENTRY_OPTIONS, ) gateway = deconz.DeconzGateway(hass, config_entry) gateway.api = DeconzSession(loop, session, **config_entry.data) diff --git a/tests/components/deconz/test_scene.py b/tests/components/deconz/test_scene.py index 48287d5200a1ff..074e943548de40 100644 --- a/tests/components/deconz/test_scene.py +++ b/tests/components/deconz/test_scene.py @@ -46,6 +46,7 @@ async def setup_gateway(hass, data): ENTRY_CONFIG, "test", config_entries.CONN_CLASS_LOCAL_PUSH, + system_options={}, ) gateway = deconz.DeconzGateway(hass, config_entry) gateway.api = DeconzSession(loop, session, **config_entry.data) diff --git a/tests/components/deconz/test_sensor.py b/tests/components/deconz/test_sensor.py index 21efd768be2b39..fa1ba175ed5762 100644 --- a/tests/components/deconz/test_sensor.py +++ b/tests/components/deconz/test_sensor.py @@ -103,7 +103,8 @@ async def setup_gateway(hass, data, allow_clip_sensor=True): ENTRY_CONFIG, "test", config_entries.CONN_CLASS_LOCAL_PUSH, - ENTRY_OPTIONS, + system_options={}, + options=ENTRY_OPTIONS, ) gateway = deconz.DeconzGateway(hass, config_entry) gateway.api = DeconzSession(loop, session, **config_entry.data) diff --git a/tests/components/deconz/test_switch.py b/tests/components/deconz/test_switch.py index 433386a1751424..746d1b6342c4f8 100644 --- a/tests/components/deconz/test_switch.py +++ b/tests/components/deconz/test_switch.py @@ -67,6 +67,7 @@ async def setup_gateway(hass, data): ENTRY_CONFIG, "test", config_entries.CONN_CLASS_LOCAL_PUSH, + system_options={}, ) gateway = deconz.DeconzGateway(hass, config_entry) gateway.api = DeconzSession(loop, session, **config_entry.data) diff --git a/tests/components/homekit_controller/test_storage.py b/tests/components/homekit_controller/test_storage.py index 3c7bd4c8e2c3d6..4fcf035ae4865e 100644 --- a/tests/components/homekit_controller/test_storage.py +++ b/tests/components/homekit_controller/test_storage.py @@ -97,6 +97,7 @@ async def test_storage_is_removed_on_config_entry_removal(hass, utcnow): pairing_data, "test", config_entries.CONN_CLASS_LOCAL_PUSH, + system_options={}, ) assert hkid in hass.data[ENTITY_MAP].storage_data diff --git a/tests/components/hue/test_light.py b/tests/components/hue/test_light.py index 4038f4f9e87a55..1c891b9c84012b 100644 --- a/tests/components/hue/test_light.py +++ b/tests/components/hue/test_light.py @@ -221,6 +221,7 @@ async def setup_bridge(hass, mock_bridge): {"host": "mock-host"}, "test", config_entries.CONN_CLASS_LOCAL_POLL, + system_options={}, ) await hass.config_entries.async_forward_entry_setup(config_entry, "light") # To flush out the service call to update the group diff --git a/tests/components/hue/test_sensor_base.py b/tests/components/hue/test_sensor_base.py index c78192d2572f79..72ac816483a147 100644 --- a/tests/components/hue/test_sensor_base.py +++ b/tests/components/hue/test_sensor_base.py @@ -306,6 +306,7 @@ async def setup_bridge(hass, mock_bridge, hostname=None): {"host": hostname}, "test", config_entries.CONN_CLASS_LOCAL_POLL, + system_options={}, ) await hass.config_entries.async_forward_entry_setup(config_entry, "binary_sensor") await hass.config_entries.async_forward_entry_setup(config_entry, "sensor") diff --git a/tests/components/ps4/test_init.py b/tests/components/ps4/test_init.py index a2eca90e60e542..5b6d6f87cd573a 100644 --- a/tests/components/ps4/test_init.py +++ b/tests/components/ps4/test_init.py @@ -142,7 +142,7 @@ async def test_config_flow_entry_migrate(hass): "media_player", "ps4", MOCK_UNIQUE_ID, - config_entry_id=MOCK_ENTRY_ID, + config_entry=mock_entry, device_id=MOCK_DEVICE_ID, ) assert len(mock_e_registry.entities) == 1 diff --git a/tests/components/ps4/test_media_player.py b/tests/components/ps4/test_media_player.py index b7f4ff53ec801c..f7e2f865cb2af0 100644 --- a/tests/components/ps4/test_media_player.py +++ b/tests/components/ps4/test_media_player.py @@ -335,7 +335,7 @@ async def test_device_info_is_assummed(hass): mock_unique_id = ps4.format_unique_id(MOCK_CREDS, MOCK_HOST_ID) mock_e_registry = mock_registry(hass) mock_e_registry.async_get_or_create( - "media_player", DOMAIN, mock_unique_id, config_entry_id=MOCK_ENTRY_ID + "media_player", DOMAIN, mock_unique_id, config_entry=MOCK_CONFIG ) mock_entity_id = mock_e_registry.async_get_entity_id( "media_player", DOMAIN, mock_unique_id diff --git a/tests/components/smartthings/conftest.py b/tests/components/smartthings/conftest.py index 3f93c442985c1f..b3b172e7606a50 100644 --- a/tests/components/smartthings/conftest.py +++ b/tests/components/smartthings/conftest.py @@ -56,6 +56,7 @@ async def setup_platform(hass, platform: str, *, devices=None, scenes=None): {CONF_INSTALLED_APP_ID: str(uuid4())}, SOURCE_USER, CONN_CLASS_CLOUD_PUSH, + 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 d5783e58818a4f..0da72c924c4e9a 100644 --- a/tests/components/unifi/test_device_tracker.py +++ b/tests/components/unifi/test_device_tracker.py @@ -145,6 +145,7 @@ async def setup_controller(hass, mock_controller): "test", config_entries.CONN_CLASS_LOCAL_POLL, entry_id=1, + system_options={}, ) mock_controller.config_entry = config_entry @@ -235,20 +236,31 @@ async def test_restoring_client(hass, mock_controller): mock_controller.mock_client_all_responses.append([CLIENT_1]) mock_controller.unifi_config = {unifi.CONF_BLOCK_CLIENT: True} + config_entry = config_entries.ConfigEntry( + 1, + unifi.DOMAIN, + "Mock Title", + ENTRY_CONFIG, + "test", + config_entries.CONN_CLASS_LOCAL_POLL, + entry_id=1, + system_options={}, + ) + registry = await entity_registry.async_get_registry(hass) registry.async_get_or_create( device_tracker.DOMAIN, unifi_dt.UNIFI_DOMAIN, "{}-mock-site".format(CLIENT_1["mac"]), suggested_object_id=CLIENT_1["hostname"], - config_entry_id=1, + config_entry=config_entry, ) registry.async_get_or_create( device_tracker.DOMAIN, unifi_dt.UNIFI_DOMAIN, "{}-mock-site".format(CLIENT_2["mac"]), suggested_object_id=CLIENT_2["hostname"], - config_entry_id=1, + config_entry=config_entry, ) await setup_controller(hass, mock_controller) diff --git a/tests/components/unifi/test_switch.py b/tests/components/unifi/test_switch.py index f84efa5dada4dd..05c58abbc9442c 100644 --- a/tests/components/unifi/test_switch.py +++ b/tests/components/unifi/test_switch.py @@ -262,6 +262,7 @@ async def setup_controller(hass, mock_controller): "test", config_entries.CONN_CLASS_LOCAL_POLL, entry_id=1, + system_options={}, ) mock_controller.config_entry = config_entry @@ -468,20 +469,31 @@ async def test_restoring_client(hass, mock_controller): mock_controller.mock_client_all_responses.append([CLIENT_1]) mock_controller.unifi_config = {unifi.CONF_BLOCK_CLIENT: ["random mac"]} + config_entry = config_entries.ConfigEntry( + 1, + unifi.DOMAIN, + "Mock Title", + ENTRY_CONFIG, + "test", + config_entries.CONN_CLASS_LOCAL_POLL, + entry_id=1, + system_options={}, + ) + registry = await entity_registry.async_get_registry(hass) registry.async_get_or_create( switch.DOMAIN, unifi.DOMAIN, "poe-{}".format(CLIENT_1["mac"]), suggested_object_id=CLIENT_1["hostname"], - config_entry_id=1, + config_entry=config_entry, ) registry.async_get_or_create( switch.DOMAIN, unifi.DOMAIN, "poe-{}".format(CLIENT_2["mac"]), suggested_object_id=CLIENT_2["hostname"], - config_entry_id=1, + config_entry=config_entry, ) await setup_controller(hass, mock_controller) diff --git a/tests/components/zha/conftest.py b/tests/components/zha/conftest.py index 5433fc62a61a61..b836c55df17a32 100644 --- a/tests/components/zha/conftest.py +++ b/tests/components/zha/conftest.py @@ -14,7 +14,13 @@ def config_entry_fixture(hass): """Fixture representing a config entry.""" config_entry = config_entries.ConfigEntry( - 1, DOMAIN, "Mock Title", {}, "test", config_entries.CONN_CLASS_LOCAL_PUSH + 1, + DOMAIN, + "Mock Title", + {}, + "test", + config_entries.CONN_CLASS_LOCAL_PUSH, + system_options={}, ) return config_entry diff --git a/tests/components/zwave/test_lock.py b/tests/components/zwave/test_lock.py index 9c6ab7835cd4cf..4a32c3fb07cd29 100644 --- a/tests/components/zwave/test_lock.py +++ b/tests/components/zwave/test_lock.py @@ -287,6 +287,7 @@ async def setup_ozw(hass, mock_openzwave): {"usb_path": "mock-path", "network_key": "mock-key"}, "test", config_entries.CONN_CLASS_LOCAL_PUSH, + 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_registry.py b/tests/helpers/test_entity_registry.py index ce05e914b3de69..aee6b6f19a3965 100644 --- a/tests/helpers/test_entity_registry.py +++ b/tests/helpers/test_entity_registry.py @@ -8,7 +8,7 @@ from homeassistant.core import valid_entity_id, callback from homeassistant.helpers import entity_registry -from tests.common import mock_registry, flush_store +from tests.common import MockConfigEntry, mock_registry, flush_store YAML__OPEN_PATH = "homeassistant.util.yaml.loader.open" @@ -88,9 +88,11 @@ def test_create_triggers_save(hass, registry): async def test_loading_saving_data(hass, registry): """Test that we load/save data correctly.""" + mock_config = MockConfigEntry(domain="light") + orig_entry1 = registry.async_get_or_create("light", "hue", "1234") orig_entry2 = registry.async_get_or_create( - "light", "hue", "5678", config_entry_id="mock-id" + "light", "hue", "5678", config_entry=mock_config ) assert len(registry.entities) == 2 @@ -104,7 +106,7 @@ async def test_loading_saving_data(hass, registry): assert list(registry.entities) == list(registry2.entities) new_entry1 = registry.async_get_or_create("light", "hue", "1234") new_entry2 = registry.async_get_or_create( - "light", "hue", "5678", config_entry_id="mock-id" + "light", "hue", "5678", config_entry=mock_config ) assert orig_entry1 == new_entry1 @@ -198,11 +200,14 @@ def test_async_get_entity_id(registry): async def test_updating_config_entry_id(hass, registry, update_events): """Test that we update config entry id in registry.""" + mock_config_1 = MockConfigEntry(domain="light", entry_id="mock-id-1") entry = registry.async_get_or_create( - "light", "hue", "5678", config_entry_id="mock-id-1" + "light", "hue", "5678", config_entry=mock_config_1 ) + + mock_config_2 = MockConfigEntry(domain="light", entry_id="mock-id-2") entry2 = registry.async_get_or_create( - "light", "hue", "5678", config_entry_id="mock-id-2" + "light", "hue", "5678", config_entry=mock_config_2 ) assert entry.entity_id == entry2.entity_id assert entry2.config_entry_id == "mock-id-2" @@ -218,8 +223,10 @@ async def test_updating_config_entry_id(hass, registry, update_events): async def test_removing_config_entry_id(hass, registry, update_events): """Test that we update config entry id in registry.""" + mock_config = MockConfigEntry(domain="light", entry_id="mock-id-1") + entry = registry.async_get_or_create( - "light", "hue", "5678", config_entry_id="mock-id-1" + "light", "hue", "5678", config_entry=mock_config ) assert entry.config_entry_id == "mock-id-1" registry.async_clear_config_entry("mock-id-1") @@ -237,6 +244,8 @@ async def test_removing_config_entry_id(hass, registry, update_events): async def test_migration(hass): """Test migration from old data to new.""" + mock_config = MockConfigEntry(domain="test-platform", entry_id="test-config-id") + old_conf = { "light.kitchen": { "config_entry_id": "test-config-id", @@ -256,7 +265,7 @@ async def test_migration(hass): domain="light", platform="test-platform", unique_id="test-unique", - config_entry_id="test-config-id", + config_entry=mock_config, ) assert entry.name == "Test Name" assert entry.disabled_by == "hass" @@ -326,8 +335,10 @@ async def test_loading_race_condition(hass): async def test_update_entity_unique_id(registry): """Test entity's unique_id is updated.""" + mock_config = MockConfigEntry(domain="light", entry_id="mock-id-1") + entry = registry.async_get_or_create( - "light", "hue", "5678", config_entry_id="mock-id-1" + "light", "hue", "5678", config_entry=mock_config ) new_unique_id = "1234" with patch.object(registry, "async_schedule_save") as mock_schedule_save: @@ -341,11 +352,12 @@ async def test_update_entity_unique_id(registry): async def test_update_entity_unique_id_conflict(registry): """Test migration raises when unique_id already in use.""" + mock_config = MockConfigEntry(domain="light", entry_id="mock-id-1") entry = registry.async_get_or_create( - "light", "hue", "5678", config_entry_id="mock-id-1" + "light", "hue", "5678", config_entry=mock_config ) entry2 = registry.async_get_or_create( - "light", "hue", "1234", config_entry_id="mock-id-1" + "light", "hue", "1234", config_entry=mock_config ) with patch.object( registry, "async_schedule_save" @@ -356,8 +368,9 @@ async def test_update_entity_unique_id_conflict(registry): async def test_update_entity(registry): """Test updating entity.""" + mock_config = MockConfigEntry(domain="light", entry_id="mock-id-1") entry = registry.async_get_or_create( - "light", "hue", "5678", config_entry_id="mock-id-1" + "light", "hue", "5678", config_entry=mock_config ) for attr_name, new_value in ( @@ -386,3 +399,21 @@ async def test_disabled_by(registry): entry2 = registry.async_get_or_create("light", "hue", "1234") assert entry2.disabled_by is None + + +async def test_disabled_by_system_options(registry): + """Test system options setting disabled_by.""" + mock_config = MockConfigEntry( + domain="light", + entry_id="mock-id-1", + system_options={"disable_new_entities": True}, + ) + entry = registry.async_get_or_create( + "light", "hue", "AAAA", config_entry=mock_config + ) + assert entry.disabled_by == "integration" + + entry2 = registry.async_get_or_create( + "light", "hue", "BBBB", config_entry=mock_config, disabled_by="user" + ) + assert entry2.disabled_by == "user" diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index 361feccc34d97d..6c1b00693dd75e 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -596,6 +596,22 @@ async def test_updating_entry_data(manager): assert entry.data == {"second": True} +async def test_updating_entry_system_options(manager): + """Test that we can update an entry data.""" + entry = MockConfigEntry( + domain="test", + data={"first": True}, + state=config_entries.ENTRY_STATE_SETUP_ERROR, + system_options={"disable_new_entities": True}, + ) + entry.add_to_manager(manager) + + assert entry.system_options.disable_new_entities + + entry.system_options.update(disable_new_entities=False) + assert not entry.system_options.disable_new_entities + + async def test_update_entry_options_and_trigger_listener(hass, manager): """Test that we can update entry options and trigger listener.""" entry = MockConfigEntry(domain="test", options={"first": True})