mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
Add system options to config entries (#25926)
* Add system options to config entries * For feedback * Follow most of balloobs comments * Fix balloobs comments * Improvements * Fix second round of Balloobs comments * Fix third round * Add system options to mock config entry * Fix integration tests * Fix the last failing tests * Fix disabled string * Fix failing disabled_by tests * New tests * Config entry WS API tests * Fix comments
This commit is contained in:
parent
fc716a45c9
commit
a2589f56e1
@ -1,6 +1,9 @@
|
|||||||
"""Http views to control the config manager."""
|
"""Http views to control the config manager."""
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant import config_entries, data_entry_flow
|
from homeassistant import config_entries, data_entry_flow
|
||||||
from homeassistant.auth.permissions.const import CAT_CONFIG_ENTRIES
|
from homeassistant.auth.permissions.const import CAT_CONFIG_ENTRIES
|
||||||
|
from homeassistant.components import websocket_api
|
||||||
from homeassistant.components.http import HomeAssistantView
|
from homeassistant.components.http import HomeAssistantView
|
||||||
from homeassistant.exceptions import Unauthorized
|
from homeassistant.exceptions import Unauthorized
|
||||||
from homeassistant.helpers.data_entry_flow import (
|
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(ConfigManagerFlowIndexView(hass.config_entries.flow))
|
||||||
hass.http.register_view(ConfigManagerFlowResourceView(hass.config_entries.flow))
|
hass.http.register_view(ConfigManagerFlowResourceView(hass.config_entries.flow))
|
||||||
hass.http.register_view(ConfigManagerAvailableFlowView)
|
hass.http.register_view(ConfigManagerAvailableFlowView)
|
||||||
|
|
||||||
hass.http.register_view(
|
hass.http.register_view(
|
||||||
OptionManagerFlowIndexView(hass.config_entries.options.flow)
|
OptionManagerFlowIndexView(hass.config_entries.options.flow)
|
||||||
)
|
)
|
||||||
hass.http.register_view(
|
hass.http.register_view(
|
||||||
OptionManagerFlowResourceView(hass.config_entries.options.flow)
|
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
|
return True
|
||||||
|
|
||||||
|
|
||||||
@ -231,3 +239,40 @@ class OptionManagerFlowResourceView(FlowManagerResourceView):
|
|||||||
|
|
||||||
# pylint: disable=no-value-for-parameter
|
# pylint: disable=no-value-for-parameter
|
||||||
return await super().post(request, flow_id)
|
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())
|
||||||
|
@ -127,7 +127,7 @@ async def async_migrate_entry(hass, entry):
|
|||||||
DOMAIN,
|
DOMAIN,
|
||||||
unique_id,
|
unique_id,
|
||||||
suggested_object_id=new_id,
|
suggested_object_id=new_id,
|
||||||
config_entry_id=e_entry.config_entry_id,
|
config_entry=entry,
|
||||||
device_id=e_entry.device_id,
|
device_id=e_entry.device_id,
|
||||||
)
|
)
|
||||||
entry.version = 3
|
entry.version = 3
|
||||||
|
@ -12,13 +12,14 @@ from typing import (
|
|||||||
)
|
)
|
||||||
import weakref
|
import weakref
|
||||||
|
|
||||||
|
import attr
|
||||||
|
|
||||||
from homeassistant import data_entry_flow, loader
|
from homeassistant import data_entry_flow, loader
|
||||||
from homeassistant.core import callback, HomeAssistant
|
from homeassistant.core import callback, HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError, ConfigEntryNotReady
|
from homeassistant.exceptions import HomeAssistantError, ConfigEntryNotReady
|
||||||
from homeassistant.setup import async_setup_component, async_process_deps_reqs
|
from homeassistant.setup import async_setup_component, async_process_deps_reqs
|
||||||
from homeassistant.util.decorator import Registry
|
from homeassistant.util.decorator import Registry
|
||||||
|
|
||||||
|
|
||||||
# mypy: allow-untyped-defs
|
# mypy: allow-untyped-defs
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -88,6 +89,7 @@ class ConfigEntry:
|
|||||||
"title",
|
"title",
|
||||||
"data",
|
"data",
|
||||||
"options",
|
"options",
|
||||||
|
"system_options",
|
||||||
"source",
|
"source",
|
||||||
"connection_class",
|
"connection_class",
|
||||||
"state",
|
"state",
|
||||||
@ -104,6 +106,7 @@ class ConfigEntry:
|
|||||||
data: dict,
|
data: dict,
|
||||||
source: str,
|
source: str,
|
||||||
connection_class: str,
|
connection_class: str,
|
||||||
|
system_options: dict,
|
||||||
options: Optional[dict] = None,
|
options: Optional[dict] = None,
|
||||||
entry_id: Optional[str] = None,
|
entry_id: Optional[str] = None,
|
||||||
state: str = ENTRY_STATE_NOT_LOADED,
|
state: str = ENTRY_STATE_NOT_LOADED,
|
||||||
@ -127,6 +130,9 @@ class ConfigEntry:
|
|||||||
# Entry options
|
# Entry options
|
||||||
self.options = options or {}
|
self.options = options or {}
|
||||||
|
|
||||||
|
# Entry system options
|
||||||
|
self.system_options = SystemOptions(**system_options)
|
||||||
|
|
||||||
# Source of the configuration (user, discovery, cloud)
|
# Source of the configuration (user, discovery, cloud)
|
||||||
self.source = source
|
self.source = source
|
||||||
|
|
||||||
@ -355,6 +361,7 @@ class ConfigEntry:
|
|||||||
"title": self.title,
|
"title": self.title,
|
||||||
"data": self.data,
|
"data": self.data,
|
||||||
"options": self.options,
|
"options": self.options,
|
||||||
|
"system_options": self.system_options.as_dict(),
|
||||||
"source": self.source,
|
"source": self.source,
|
||||||
"connection_class": self.connection_class,
|
"connection_class": self.connection_class,
|
||||||
}
|
}
|
||||||
@ -457,6 +464,8 @@ class ConfigEntries:
|
|||||||
connection_class=entry.get("connection_class", CONN_CLASS_UNKNOWN),
|
connection_class=entry.get("connection_class", CONN_CLASS_UNKNOWN),
|
||||||
# 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", {}),
|
||||||
)
|
)
|
||||||
for entry in config["entries"]
|
for entry in config["entries"]
|
||||||
]
|
]
|
||||||
@ -580,6 +589,7 @@ class ConfigEntries:
|
|||||||
title=result["title"],
|
title=result["title"],
|
||||||
data=result["data"],
|
data=result["data"],
|
||||||
options={},
|
options={},
|
||||||
|
system_options={},
|
||||||
source=flow.context["source"],
|
source=flow.context["source"],
|
||||||
connection_class=flow.CONNECTION_CLASS,
|
connection_class=flow.CONNECTION_CLASS,
|
||||||
)
|
)
|
||||||
@ -722,3 +732,18 @@ class OptionsFlow(data_entry_flow.FlowHandler):
|
|||||||
"""Base class for config option flows."""
|
"""Base class for config option flows."""
|
||||||
|
|
||||||
pass
|
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}
|
||||||
|
@ -343,7 +343,7 @@ class EntityPlatform:
|
|||||||
self.platform_name,
|
self.platform_name,
|
||||||
entity.unique_id,
|
entity.unique_id,
|
||||||
suggested_object_id=suggested_object_id,
|
suggested_object_id=suggested_object_id,
|
||||||
config_entry_id=config_entry_id,
|
config_entry=self.config_entry,
|
||||||
device_id=device_id,
|
device_id=device_id,
|
||||||
known_object_ids=self.entities.keys(),
|
known_object_ids=self.entities.keys(),
|
||||||
disabled_by=disabled_by,
|
disabled_by=disabled_by,
|
||||||
|
@ -33,6 +33,7 @@ EVENT_ENTITY_REGISTRY_UPDATED = "entity_registry_updated"
|
|||||||
SAVE_DELAY = 10
|
SAVE_DELAY = 10
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
_UNDEF = object()
|
_UNDEF = object()
|
||||||
|
DISABLED_CONFIG_ENTRY = "config_entry"
|
||||||
DISABLED_HASS = "hass"
|
DISABLED_HASS = "hass"
|
||||||
DISABLED_USER = "user"
|
DISABLED_USER = "user"
|
||||||
DISABLED_INTEGRATION = "integration"
|
DISABLED_INTEGRATION = "integration"
|
||||||
@ -55,7 +56,13 @@ class RegistryEntry:
|
|||||||
type=str,
|
type=str,
|
||||||
default=None,
|
default=None,
|
||||||
validator=attr.validators.in_(
|
validator=attr.validators.in_(
|
||||||
(DISABLED_HASS, DISABLED_USER, DISABLED_INTEGRATION, None)
|
(
|
||||||
|
DISABLED_HASS,
|
||||||
|
DISABLED_USER,
|
||||||
|
DISABLED_INTEGRATION,
|
||||||
|
DISABLED_CONFIG_ENTRY,
|
||||||
|
None,
|
||||||
|
)
|
||||||
),
|
),
|
||||||
) # type: Optional[str]
|
) # type: Optional[str]
|
||||||
domain = attr.ib(type=str, init=False, repr=False)
|
domain = attr.ib(type=str, init=False, repr=False)
|
||||||
@ -132,13 +139,18 @@ class EntityRegistry:
|
|||||||
unique_id,
|
unique_id,
|
||||||
*,
|
*,
|
||||||
suggested_object_id=None,
|
suggested_object_id=None,
|
||||||
config_entry_id=None,
|
config_entry=None,
|
||||||
device_id=None,
|
device_id=None,
|
||||||
known_object_ids=None,
|
known_object_ids=None,
|
||||||
disabled_by=None,
|
disabled_by=None,
|
||||||
):
|
):
|
||||||
"""Get entity. Create if it doesn't exist."""
|
"""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)
|
entity_id = self.async_get_entity_id(domain, platform, unique_id)
|
||||||
|
|
||||||
if entity_id:
|
if entity_id:
|
||||||
return self._async_update_entity(
|
return self._async_update_entity(
|
||||||
entity_id,
|
entity_id,
|
||||||
@ -159,6 +171,13 @@ class EntityRegistry:
|
|||||||
known_object_ids,
|
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 = RegistryEntry(
|
||||||
entity_id=entity_id,
|
entity_id=entity_id,
|
||||||
config_entry_id=config_entry_id,
|
config_entry_id=config_entry_id,
|
||||||
|
@ -665,6 +665,7 @@ class MockConfigEntry(config_entries.ConfigEntry):
|
|||||||
title="Mock Title",
|
title="Mock Title",
|
||||||
state=None,
|
state=None,
|
||||||
options={},
|
options={},
|
||||||
|
system_options={},
|
||||||
connection_class=config_entries.CONN_CLASS_UNKNOWN,
|
connection_class=config_entries.CONN_CLASS_UNKNOWN,
|
||||||
):
|
):
|
||||||
"""Initialize a mock config entry."""
|
"""Initialize a mock config entry."""
|
||||||
@ -672,6 +673,7 @@ class MockConfigEntry(config_entries.ConfigEntry):
|
|||||||
"entry_id": entry_id or uuid.uuid4().hex,
|
"entry_id": entry_id or uuid.uuid4().hex,
|
||||||
"domain": domain,
|
"domain": domain,
|
||||||
"data": data or {},
|
"data": data or {},
|
||||||
|
"system_options": system_options,
|
||||||
"options": options,
|
"options": options,
|
||||||
"version": version,
|
"version": version,
|
||||||
"title": title,
|
"title": title,
|
||||||
|
@ -57,6 +57,7 @@ async def setup_device(hass):
|
|||||||
ENTRY_CONFIG,
|
ENTRY_CONFIG,
|
||||||
"test",
|
"test",
|
||||||
config_entries.CONN_CLASS_LOCAL_PUSH,
|
config_entries.CONN_CLASS_LOCAL_PUSH,
|
||||||
|
system_options={},
|
||||||
options=ENTRY_OPTIONS,
|
options=ENTRY_OPTIONS,
|
||||||
)
|
)
|
||||||
device = axis.AxisNetworkDevice(hass, config_entry)
|
device = axis.AxisNetworkDevice(hass, config_entry)
|
||||||
|
@ -41,6 +41,7 @@ async def setup_device(hass):
|
|||||||
ENTRY_CONFIG,
|
ENTRY_CONFIG,
|
||||||
"test",
|
"test",
|
||||||
config_entries.CONN_CLASS_LOCAL_PUSH,
|
config_entries.CONN_CLASS_LOCAL_PUSH,
|
||||||
|
system_options={},
|
||||||
options=ENTRY_OPTIONS,
|
options=ENTRY_OPTIONS,
|
||||||
)
|
)
|
||||||
device = axis.AxisNetworkDevice(hass, config_entry)
|
device = axis.AxisNetworkDevice(hass, config_entry)
|
||||||
|
@ -59,6 +59,7 @@ async def setup_device(hass):
|
|||||||
ENTRY_CONFIG,
|
ENTRY_CONFIG,
|
||||||
"test",
|
"test",
|
||||||
config_entries.CONN_CLASS_LOCAL_PUSH,
|
config_entries.CONN_CLASS_LOCAL_PUSH,
|
||||||
|
system_options={},
|
||||||
options=ENTRY_OPTIONS,
|
options=ENTRY_OPTIONS,
|
||||||
)
|
)
|
||||||
device = axis.AxisNetworkDevice(hass, config_entry)
|
device = axis.AxisNetworkDevice(hass, config_entry)
|
||||||
|
@ -584,3 +584,47 @@ async def test_two_step_options_flow(hass, client):
|
|||||||
"description": None,
|
"description": None,
|
||||||
"description_placeholders": 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
|
||||||
|
@ -59,7 +59,8 @@ async def setup_gateway(hass, data, allow_clip_sensor=True):
|
|||||||
ENTRY_CONFIG,
|
ENTRY_CONFIG,
|
||||||
"test",
|
"test",
|
||||||
config_entries.CONN_CLASS_LOCAL_PUSH,
|
config_entries.CONN_CLASS_LOCAL_PUSH,
|
||||||
ENTRY_OPTIONS,
|
system_options={},
|
||||||
|
options=ENTRY_OPTIONS,
|
||||||
)
|
)
|
||||||
gateway = deconz.DeconzGateway(hass, config_entry)
|
gateway = deconz.DeconzGateway(hass, config_entry)
|
||||||
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
||||||
|
@ -71,7 +71,8 @@ async def setup_gateway(hass, data, allow_clip_sensor=True):
|
|||||||
ENTRY_CONFIG,
|
ENTRY_CONFIG,
|
||||||
"test",
|
"test",
|
||||||
config_entries.CONN_CLASS_LOCAL_PUSH,
|
config_entries.CONN_CLASS_LOCAL_PUSH,
|
||||||
ENTRY_OPTIONS,
|
system_options={},
|
||||||
|
options=ENTRY_OPTIONS,
|
||||||
)
|
)
|
||||||
gateway = deconz.DeconzGateway(hass, config_entry)
|
gateway = deconz.DeconzGateway(hass, config_entry)
|
||||||
gateway.api = DeconzSession(hass.loop, session, **config_entry.data)
|
gateway.api = DeconzSession(hass.loop, session, **config_entry.data)
|
||||||
|
@ -63,6 +63,7 @@ async def setup_gateway(hass, data):
|
|||||||
ENTRY_CONFIG,
|
ENTRY_CONFIG,
|
||||||
"test",
|
"test",
|
||||||
config_entries.CONN_CLASS_LOCAL_PUSH,
|
config_entries.CONN_CLASS_LOCAL_PUSH,
|
||||||
|
system_options={},
|
||||||
)
|
)
|
||||||
gateway = deconz.DeconzGateway(hass, config_entry)
|
gateway = deconz.DeconzGateway(hass, config_entry)
|
||||||
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
||||||
|
@ -90,7 +90,8 @@ async def setup_gateway(hass, data, allow_deconz_groups=True):
|
|||||||
ENTRY_CONFIG,
|
ENTRY_CONFIG,
|
||||||
"test",
|
"test",
|
||||||
config_entries.CONN_CLASS_LOCAL_PUSH,
|
config_entries.CONN_CLASS_LOCAL_PUSH,
|
||||||
ENTRY_OPTIONS,
|
system_options={},
|
||||||
|
options=ENTRY_OPTIONS,
|
||||||
)
|
)
|
||||||
gateway = deconz.DeconzGateway(hass, config_entry)
|
gateway = deconz.DeconzGateway(hass, config_entry)
|
||||||
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
||||||
|
@ -46,6 +46,7 @@ async def setup_gateway(hass, data):
|
|||||||
ENTRY_CONFIG,
|
ENTRY_CONFIG,
|
||||||
"test",
|
"test",
|
||||||
config_entries.CONN_CLASS_LOCAL_PUSH,
|
config_entries.CONN_CLASS_LOCAL_PUSH,
|
||||||
|
system_options={},
|
||||||
)
|
)
|
||||||
gateway = deconz.DeconzGateway(hass, config_entry)
|
gateway = deconz.DeconzGateway(hass, config_entry)
|
||||||
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
||||||
|
@ -103,7 +103,8 @@ async def setup_gateway(hass, data, allow_clip_sensor=True):
|
|||||||
ENTRY_CONFIG,
|
ENTRY_CONFIG,
|
||||||
"test",
|
"test",
|
||||||
config_entries.CONN_CLASS_LOCAL_PUSH,
|
config_entries.CONN_CLASS_LOCAL_PUSH,
|
||||||
ENTRY_OPTIONS,
|
system_options={},
|
||||||
|
options=ENTRY_OPTIONS,
|
||||||
)
|
)
|
||||||
gateway = deconz.DeconzGateway(hass, config_entry)
|
gateway = deconz.DeconzGateway(hass, config_entry)
|
||||||
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
||||||
|
@ -67,6 +67,7 @@ async def setup_gateway(hass, data):
|
|||||||
ENTRY_CONFIG,
|
ENTRY_CONFIG,
|
||||||
"test",
|
"test",
|
||||||
config_entries.CONN_CLASS_LOCAL_PUSH,
|
config_entries.CONN_CLASS_LOCAL_PUSH,
|
||||||
|
system_options={},
|
||||||
)
|
)
|
||||||
gateway = deconz.DeconzGateway(hass, config_entry)
|
gateway = deconz.DeconzGateway(hass, config_entry)
|
||||||
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
gateway.api = DeconzSession(loop, session, **config_entry.data)
|
||||||
|
@ -97,6 +97,7 @@ async def test_storage_is_removed_on_config_entry_removal(hass, utcnow):
|
|||||||
pairing_data,
|
pairing_data,
|
||||||
"test",
|
"test",
|
||||||
config_entries.CONN_CLASS_LOCAL_PUSH,
|
config_entries.CONN_CLASS_LOCAL_PUSH,
|
||||||
|
system_options={},
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hkid in hass.data[ENTITY_MAP].storage_data
|
assert hkid in hass.data[ENTITY_MAP].storage_data
|
||||||
|
@ -221,6 +221,7 @@ async def setup_bridge(hass, mock_bridge):
|
|||||||
{"host": "mock-host"},
|
{"host": "mock-host"},
|
||||||
"test",
|
"test",
|
||||||
config_entries.CONN_CLASS_LOCAL_POLL,
|
config_entries.CONN_CLASS_LOCAL_POLL,
|
||||||
|
system_options={},
|
||||||
)
|
)
|
||||||
await hass.config_entries.async_forward_entry_setup(config_entry, "light")
|
await hass.config_entries.async_forward_entry_setup(config_entry, "light")
|
||||||
# To flush out the service call to update the group
|
# To flush out the service call to update the group
|
||||||
|
@ -306,6 +306,7 @@ async def setup_bridge(hass, mock_bridge, hostname=None):
|
|||||||
{"host": hostname},
|
{"host": hostname},
|
||||||
"test",
|
"test",
|
||||||
config_entries.CONN_CLASS_LOCAL_POLL,
|
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, "binary_sensor")
|
||||||
await hass.config_entries.async_forward_entry_setup(config_entry, "sensor")
|
await hass.config_entries.async_forward_entry_setup(config_entry, "sensor")
|
||||||
|
@ -142,7 +142,7 @@ async def test_config_flow_entry_migrate(hass):
|
|||||||
"media_player",
|
"media_player",
|
||||||
"ps4",
|
"ps4",
|
||||||
MOCK_UNIQUE_ID,
|
MOCK_UNIQUE_ID,
|
||||||
config_entry_id=MOCK_ENTRY_ID,
|
config_entry=mock_entry,
|
||||||
device_id=MOCK_DEVICE_ID,
|
device_id=MOCK_DEVICE_ID,
|
||||||
)
|
)
|
||||||
assert len(mock_e_registry.entities) == 1
|
assert len(mock_e_registry.entities) == 1
|
||||||
|
@ -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_unique_id = ps4.format_unique_id(MOCK_CREDS, MOCK_HOST_ID)
|
||||||
mock_e_registry = mock_registry(hass)
|
mock_e_registry = mock_registry(hass)
|
||||||
mock_e_registry.async_get_or_create(
|
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(
|
mock_entity_id = mock_e_registry.async_get_entity_id(
|
||||||
"media_player", DOMAIN, mock_unique_id
|
"media_player", DOMAIN, mock_unique_id
|
||||||
|
@ -56,6 +56,7 @@ async def setup_platform(hass, platform: str, *, devices=None, scenes=None):
|
|||||||
{CONF_INSTALLED_APP_ID: str(uuid4())},
|
{CONF_INSTALLED_APP_ID: str(uuid4())},
|
||||||
SOURCE_USER,
|
SOURCE_USER,
|
||||||
CONN_CLASS_CLOUD_PUSH,
|
CONN_CLASS_CLOUD_PUSH,
|
||||||
|
system_options={},
|
||||||
)
|
)
|
||||||
broker = DeviceBroker(
|
broker = DeviceBroker(
|
||||||
hass, config_entry, Mock(), Mock(), devices or [], scenes or []
|
hass, config_entry, Mock(), Mock(), devices or [], scenes or []
|
||||||
|
@ -145,6 +145,7 @@ async def setup_controller(hass, mock_controller):
|
|||||||
"test",
|
"test",
|
||||||
config_entries.CONN_CLASS_LOCAL_POLL,
|
config_entries.CONN_CLASS_LOCAL_POLL,
|
||||||
entry_id=1,
|
entry_id=1,
|
||||||
|
system_options={},
|
||||||
)
|
)
|
||||||
mock_controller.config_entry = config_entry
|
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.mock_client_all_responses.append([CLIENT_1])
|
||||||
mock_controller.unifi_config = {unifi.CONF_BLOCK_CLIENT: True}
|
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 = await entity_registry.async_get_registry(hass)
|
||||||
registry.async_get_or_create(
|
registry.async_get_or_create(
|
||||||
device_tracker.DOMAIN,
|
device_tracker.DOMAIN,
|
||||||
unifi_dt.UNIFI_DOMAIN,
|
unifi_dt.UNIFI_DOMAIN,
|
||||||
"{}-mock-site".format(CLIENT_1["mac"]),
|
"{}-mock-site".format(CLIENT_1["mac"]),
|
||||||
suggested_object_id=CLIENT_1["hostname"],
|
suggested_object_id=CLIENT_1["hostname"],
|
||||||
config_entry_id=1,
|
config_entry=config_entry,
|
||||||
)
|
)
|
||||||
registry.async_get_or_create(
|
registry.async_get_or_create(
|
||||||
device_tracker.DOMAIN,
|
device_tracker.DOMAIN,
|
||||||
unifi_dt.UNIFI_DOMAIN,
|
unifi_dt.UNIFI_DOMAIN,
|
||||||
"{}-mock-site".format(CLIENT_2["mac"]),
|
"{}-mock-site".format(CLIENT_2["mac"]),
|
||||||
suggested_object_id=CLIENT_2["hostname"],
|
suggested_object_id=CLIENT_2["hostname"],
|
||||||
config_entry_id=1,
|
config_entry=config_entry,
|
||||||
)
|
)
|
||||||
|
|
||||||
await setup_controller(hass, mock_controller)
|
await setup_controller(hass, mock_controller)
|
||||||
|
@ -262,6 +262,7 @@ async def setup_controller(hass, mock_controller):
|
|||||||
"test",
|
"test",
|
||||||
config_entries.CONN_CLASS_LOCAL_POLL,
|
config_entries.CONN_CLASS_LOCAL_POLL,
|
||||||
entry_id=1,
|
entry_id=1,
|
||||||
|
system_options={},
|
||||||
)
|
)
|
||||||
mock_controller.config_entry = config_entry
|
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.mock_client_all_responses.append([CLIENT_1])
|
||||||
mock_controller.unifi_config = {unifi.CONF_BLOCK_CLIENT: ["random mac"]}
|
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 = await entity_registry.async_get_registry(hass)
|
||||||
registry.async_get_or_create(
|
registry.async_get_or_create(
|
||||||
switch.DOMAIN,
|
switch.DOMAIN,
|
||||||
unifi.DOMAIN,
|
unifi.DOMAIN,
|
||||||
"poe-{}".format(CLIENT_1["mac"]),
|
"poe-{}".format(CLIENT_1["mac"]),
|
||||||
suggested_object_id=CLIENT_1["hostname"],
|
suggested_object_id=CLIENT_1["hostname"],
|
||||||
config_entry_id=1,
|
config_entry=config_entry,
|
||||||
)
|
)
|
||||||
registry.async_get_or_create(
|
registry.async_get_or_create(
|
||||||
switch.DOMAIN,
|
switch.DOMAIN,
|
||||||
unifi.DOMAIN,
|
unifi.DOMAIN,
|
||||||
"poe-{}".format(CLIENT_2["mac"]),
|
"poe-{}".format(CLIENT_2["mac"]),
|
||||||
suggested_object_id=CLIENT_2["hostname"],
|
suggested_object_id=CLIENT_2["hostname"],
|
||||||
config_entry_id=1,
|
config_entry=config_entry,
|
||||||
)
|
)
|
||||||
|
|
||||||
await setup_controller(hass, mock_controller)
|
await setup_controller(hass, mock_controller)
|
||||||
|
@ -14,7 +14,13 @@ from homeassistant.components.zha.core.store import async_get_registry
|
|||||||
def config_entry_fixture(hass):
|
def config_entry_fixture(hass):
|
||||||
"""Fixture representing a config entry."""
|
"""Fixture representing a config entry."""
|
||||||
config_entry = config_entries.ConfigEntry(
|
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
|
return config_entry
|
||||||
|
|
||||||
|
@ -287,6 +287,7 @@ async def setup_ozw(hass, mock_openzwave):
|
|||||||
{"usb_path": "mock-path", "network_key": "mock-key"},
|
{"usb_path": "mock-path", "network_key": "mock-key"},
|
||||||
"test",
|
"test",
|
||||||
config_entries.CONN_CLASS_LOCAL_PUSH,
|
config_entries.CONN_CLASS_LOCAL_PUSH,
|
||||||
|
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()
|
||||||
|
@ -8,7 +8,7 @@ import pytest
|
|||||||
from homeassistant.core import valid_entity_id, callback
|
from homeassistant.core import valid_entity_id, callback
|
||||||
from homeassistant.helpers import entity_registry
|
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"
|
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):
|
async def test_loading_saving_data(hass, registry):
|
||||||
"""Test that we load/save data correctly."""
|
"""Test that we load/save data correctly."""
|
||||||
|
mock_config = MockConfigEntry(domain="light")
|
||||||
|
|
||||||
orig_entry1 = registry.async_get_or_create("light", "hue", "1234")
|
orig_entry1 = registry.async_get_or_create("light", "hue", "1234")
|
||||||
orig_entry2 = registry.async_get_or_create(
|
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
|
assert len(registry.entities) == 2
|
||||||
@ -104,7 +106,7 @@ async def test_loading_saving_data(hass, registry):
|
|||||||
assert list(registry.entities) == list(registry2.entities)
|
assert list(registry.entities) == list(registry2.entities)
|
||||||
new_entry1 = registry.async_get_or_create("light", "hue", "1234")
|
new_entry1 = registry.async_get_or_create("light", "hue", "1234")
|
||||||
new_entry2 = registry.async_get_or_create(
|
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
|
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):
|
async def test_updating_config_entry_id(hass, registry, update_events):
|
||||||
"""Test that we update config entry id in registry."""
|
"""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(
|
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(
|
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 entry.entity_id == entry2.entity_id
|
||||||
assert entry2.config_entry_id == "mock-id-2"
|
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):
|
async def test_removing_config_entry_id(hass, registry, update_events):
|
||||||
"""Test that we update config entry id in registry."""
|
"""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(
|
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"
|
assert entry.config_entry_id == "mock-id-1"
|
||||||
registry.async_clear_config_entry("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):
|
async def test_migration(hass):
|
||||||
"""Test migration from old data to new."""
|
"""Test migration from old data to new."""
|
||||||
|
mock_config = MockConfigEntry(domain="test-platform", entry_id="test-config-id")
|
||||||
|
|
||||||
old_conf = {
|
old_conf = {
|
||||||
"light.kitchen": {
|
"light.kitchen": {
|
||||||
"config_entry_id": "test-config-id",
|
"config_entry_id": "test-config-id",
|
||||||
@ -256,7 +265,7 @@ async def test_migration(hass):
|
|||||||
domain="light",
|
domain="light",
|
||||||
platform="test-platform",
|
platform="test-platform",
|
||||||
unique_id="test-unique",
|
unique_id="test-unique",
|
||||||
config_entry_id="test-config-id",
|
config_entry=mock_config,
|
||||||
)
|
)
|
||||||
assert entry.name == "Test Name"
|
assert entry.name == "Test Name"
|
||||||
assert entry.disabled_by == "hass"
|
assert entry.disabled_by == "hass"
|
||||||
@ -326,8 +335,10 @@ async def test_loading_race_condition(hass):
|
|||||||
|
|
||||||
async def test_update_entity_unique_id(registry):
|
async def test_update_entity_unique_id(registry):
|
||||||
"""Test entity's unique_id is updated."""
|
"""Test entity's unique_id is updated."""
|
||||||
|
mock_config = MockConfigEntry(domain="light", entry_id="mock-id-1")
|
||||||
|
|
||||||
entry = registry.async_get_or_create(
|
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"
|
new_unique_id = "1234"
|
||||||
with patch.object(registry, "async_schedule_save") as mock_schedule_save:
|
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):
|
async def test_update_entity_unique_id_conflict(registry):
|
||||||
"""Test migration raises when unique_id already in use."""
|
"""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(
|
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(
|
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(
|
with patch.object(
|
||||||
registry, "async_schedule_save"
|
registry, "async_schedule_save"
|
||||||
@ -356,8 +368,9 @@ async def test_update_entity_unique_id_conflict(registry):
|
|||||||
|
|
||||||
async def test_update_entity(registry):
|
async def test_update_entity(registry):
|
||||||
"""Test updating entity."""
|
"""Test updating entity."""
|
||||||
|
mock_config = MockConfigEntry(domain="light", entry_id="mock-id-1")
|
||||||
entry = registry.async_get_or_create(
|
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 (
|
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")
|
entry2 = registry.async_get_or_create("light", "hue", "1234")
|
||||||
assert entry2.disabled_by is None
|
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"
|
||||||
|
@ -596,6 +596,22 @@ async def test_updating_entry_data(manager):
|
|||||||
assert entry.data == {"second": True}
|
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):
|
async def test_update_entry_options_and_trigger_listener(hass, manager):
|
||||||
"""Test that we can update entry options and trigger listener."""
|
"""Test that we can update entry options and trigger listener."""
|
||||||
entry = MockConfigEntry(domain="test", options={"first": True})
|
entry = MockConfigEntry(domain="test", options={"first": True})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user