Skip to content

Commit

Permalink
Add system options to config entries (#25926)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Kane610 authored and balloob committed Aug 18, 2019
1 parent fc716a4 commit a2589f5
Show file tree
Hide file tree
Showing 29 changed files with 254 additions and 27 deletions.
45 changes: 45 additions & 0 deletions homeassistant/components/config/config_entries.py
Original file line number Diff line number Diff line change
@@ -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 (
Expand All @@ -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


Expand Down Expand Up @@ -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())
2 changes: 1 addition & 1 deletion homeassistant/components/ps4/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 26 additions & 1 deletion homeassistant/config_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)
Expand Down Expand Up @@ -88,6 +89,7 @@ class ConfigEntry:
"title",
"data",
"options",
"system_options",
"source",
"connection_class",
"state",
Expand All @@ -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,
Expand All @@ -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

Expand Down Expand Up @@ -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,
}
Expand Down Expand Up @@ -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"]
]
Expand Down Expand Up @@ -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,
)
Expand Down Expand Up @@ -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}
2 changes: 1 addition & 1 deletion homeassistant/helpers/entity_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
23 changes: 21 additions & 2 deletions homeassistant/helpers/entity_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -665,13 +665,15 @@ def __init__(
title="Mock Title",
state=None,
options={},
system_options={},
connection_class=config_entries.CONN_CLASS_UNKNOWN,
):
"""Initialize a mock config entry."""
kwargs = {
"entry_id": entry_id or uuid.uuid4().hex,
"domain": domain,
"data": data or {},
"system_options": system_options,
"options": options,
"version": version,
"title": title,
Expand Down
1 change: 1 addition & 0 deletions tests/components/axis/test_binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions tests/components/axis/test_camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions tests/components/axis/test_switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
44 changes: 44 additions & 0 deletions tests/components/config/test_config_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 2 additions & 1 deletion tests/components/deconz/test_binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion tests/components/deconz/test_climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions tests/components/deconz/test_cover.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion tests/components/deconz/test_light.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions tests/components/deconz/test_scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion tests/components/deconz/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions tests/components/deconz/test_switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions tests/components/homekit_controller/test_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions tests/components/hue/test_light.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit a2589f5

Please sign in to comment.