Remove create_list from StorageCollectionWebsocket.async_setup (#119508)
This commit is contained in:
parent
0ae4903686
commit
e0378f79a4
8 changed files with 142 additions and 69 deletions
|
@ -1609,11 +1609,10 @@ class PipelineStorageCollectionWebsocket(
|
|||
self,
|
||||
hass: HomeAssistant,
|
||||
*,
|
||||
create_list: bool = True,
|
||||
create_create: bool = True,
|
||||
) -> None:
|
||||
"""Set up the websocket commands."""
|
||||
super().async_setup(hass, create_list=create_list, create_create=create_create)
|
||||
super().async_setup(hass, create_create=create_create)
|
||||
|
||||
websocket_api.async_register_command(
|
||||
hass,
|
||||
|
|
|
@ -115,6 +115,17 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
reload_resources_service_handler,
|
||||
schema=RESOURCE_RELOAD_SERVICE_SCHEMA,
|
||||
)
|
||||
# Register lovelace/resources for backwards compatibility, remove in
|
||||
# Home Assistant Core 2025.1
|
||||
for command in ("lovelace/resources", "lovelace/resources/list"):
|
||||
websocket_api.async_register_command(
|
||||
hass,
|
||||
command,
|
||||
websocket.websocket_lovelace_resources,
|
||||
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
||||
{"type": command},
|
||||
),
|
||||
)
|
||||
|
||||
else:
|
||||
default_config = dashboard.LovelaceStorage(hass, None)
|
||||
|
@ -127,22 +138,19 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
|
||||
resource_collection = resources.ResourceStorageCollection(hass, default_config)
|
||||
|
||||
collection.DictStorageCollectionWebsocket(
|
||||
resources.ResourceStorageCollectionWebsocket(
|
||||
resource_collection,
|
||||
"lovelace/resources",
|
||||
"resource",
|
||||
RESOURCE_CREATE_FIELDS,
|
||||
RESOURCE_UPDATE_FIELDS,
|
||||
).async_setup(hass, create_list=False)
|
||||
).async_setup(hass)
|
||||
|
||||
websocket_api.async_register_command(hass, websocket.websocket_lovelace_config)
|
||||
websocket_api.async_register_command(hass, websocket.websocket_lovelace_save_config)
|
||||
websocket_api.async_register_command(
|
||||
hass, websocket.websocket_lovelace_delete_config
|
||||
)
|
||||
websocket_api.async_register_command(hass, websocket.websocket_lovelace_resources)
|
||||
|
||||
websocket_api.async_register_command(hass, websocket.websocket_lovelace_dashboards)
|
||||
|
||||
hass.data[DOMAIN] = {
|
||||
# We store a dictionary mapping url_path: config. None is the default.
|
||||
|
@ -209,13 +217,13 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
dashboards_collection.async_add_listener(storage_dashboard_changed)
|
||||
await dashboards_collection.async_load()
|
||||
|
||||
collection.DictStorageCollectionWebsocket(
|
||||
dashboard.DashboardsCollectionWebSocket(
|
||||
dashboards_collection,
|
||||
"lovelace/dashboards",
|
||||
"dashboard",
|
||||
STORAGE_DASHBOARD_CREATE_FIELDS,
|
||||
STORAGE_DASHBOARD_UPDATE_FIELDS,
|
||||
).async_setup(hass, create_list=False)
|
||||
).async_setup(hass)
|
||||
|
||||
def create_map_dashboard():
|
||||
hass.async_create_task(_create_map_dashboard(hass))
|
||||
|
|
|
@ -11,6 +11,7 @@ from typing import Any
|
|||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import websocket_api
|
||||
from homeassistant.components.frontend import DATA_PANELS
|
||||
from homeassistant.const import CONF_FILENAME
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
|
@ -297,3 +298,24 @@ class DashboardsCollection(collection.DictStorageCollection):
|
|||
updated.pop(CONF_ICON)
|
||||
|
||||
return updated
|
||||
|
||||
|
||||
class DashboardsCollectionWebSocket(collection.DictStorageCollectionWebsocket):
|
||||
"""Class to expose storage collection management over websocket."""
|
||||
|
||||
@callback
|
||||
def ws_list_item(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
connection: websocket_api.ActiveConnection,
|
||||
msg: dict[str, Any],
|
||||
) -> None:
|
||||
"""Send Lovelace UI resources over WebSocket connection."""
|
||||
connection.send_result(
|
||||
msg["id"],
|
||||
[
|
||||
dashboard.config
|
||||
for dashboard in hass.data[DOMAIN]["dashboards"].values()
|
||||
if dashboard.config
|
||||
],
|
||||
)
|
||||
|
|
|
@ -8,6 +8,7 @@ import uuid
|
|||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import websocket_api
|
||||
from homeassistant.const import CONF_ID, CONF_RESOURCES, CONF_TYPE
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
@ -21,6 +22,7 @@ from .const import (
|
|||
RESOURCE_UPDATE_FIELDS,
|
||||
)
|
||||
from .dashboard import LovelaceConfig
|
||||
from .websocket import websocket_lovelace_resources_impl
|
||||
|
||||
RESOURCE_STORAGE_KEY = f"{DOMAIN}_resources"
|
||||
RESOURCES_STORAGE_VERSION = 1
|
||||
|
@ -125,3 +127,38 @@ class ResourceStorageCollection(collection.DictStorageCollection):
|
|||
update_data[CONF_TYPE] = update_data.pop(CONF_RESOURCE_TYPE_WS)
|
||||
|
||||
return {**item, **update_data}
|
||||
|
||||
|
||||
class ResourceStorageCollectionWebsocket(collection.DictStorageCollectionWebsocket):
|
||||
"""Class to expose storage collection management over websocket."""
|
||||
|
||||
@callback
|
||||
def async_setup(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
*,
|
||||
create_create: bool = True,
|
||||
) -> None:
|
||||
"""Set up the websocket commands."""
|
||||
super().async_setup(hass, create_create=create_create)
|
||||
|
||||
# Register lovelace/resources for backwards compatibility, remove in
|
||||
# Home Assistant Core 2025.1
|
||||
websocket_api.async_register_command(
|
||||
hass,
|
||||
self.api_prefix,
|
||||
self.ws_list_item,
|
||||
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
||||
{vol.Required("type"): f"{self.api_prefix}"}
|
||||
),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@websocket_api.async_response
|
||||
async def ws_list_item(
|
||||
hass: HomeAssistant,
|
||||
connection: websocket_api.ActiveConnection,
|
||||
msg: dict[str, Any],
|
||||
) -> None:
|
||||
"""Send Lovelace UI resources over WebSocket connection."""
|
||||
await websocket_lovelace_resources_impl(hass, connection, msg)
|
||||
|
|
|
@ -8,7 +8,7 @@ from typing import Any
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import websocket_api
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.json import json_fragment
|
||||
|
@ -52,14 +52,28 @@ def _handle_errors(func):
|
|||
return send_with_error_handling
|
||||
|
||||
|
||||
@websocket_api.websocket_command({"type": "lovelace/resources"})
|
||||
@websocket_api.async_response
|
||||
async def websocket_lovelace_resources(
|
||||
hass: HomeAssistant,
|
||||
connection: websocket_api.ActiveConnection,
|
||||
msg: dict[str, Any],
|
||||
) -> None:
|
||||
"""Send Lovelace UI resources over WebSocket configuration."""
|
||||
"""Send Lovelace UI resources over WebSocket connection.
|
||||
|
||||
This function is used in YAML mode.
|
||||
"""
|
||||
await websocket_lovelace_resources_impl(hass, connection, msg)
|
||||
|
||||
|
||||
async def websocket_lovelace_resources_impl(
|
||||
hass: HomeAssistant,
|
||||
connection: websocket_api.ActiveConnection,
|
||||
msg: dict[str, Any],
|
||||
) -> None:
|
||||
"""Help send Lovelace UI resources over WebSocket connection.
|
||||
|
||||
This function is called by both Storage and YAML mode WS handlers.
|
||||
"""
|
||||
resources = hass.data[DOMAIN]["resources"]
|
||||
|
||||
if hass.config.safe_mode:
|
||||
|
@ -129,21 +143,3 @@ async def websocket_lovelace_delete_config(
|
|||
) -> None:
|
||||
"""Delete Lovelace UI configuration."""
|
||||
await config.async_delete()
|
||||
|
||||
|
||||
@websocket_api.websocket_command({"type": "lovelace/dashboards/list"})
|
||||
@callback
|
||||
def websocket_lovelace_dashboards(
|
||||
hass: HomeAssistant,
|
||||
connection: websocket_api.ActiveConnection,
|
||||
msg: dict[str, Any],
|
||||
) -> None:
|
||||
"""Send Lovelace dashboard configuration."""
|
||||
connection.send_result(
|
||||
msg["id"],
|
||||
[
|
||||
dashboard.config
|
||||
for dashboard in hass.data[DOMAIN]["dashboards"].values()
|
||||
if dashboard.config
|
||||
],
|
||||
)
|
||||
|
|
|
@ -24,7 +24,6 @@ from homeassistant.const import (
|
|||
ATTR_NAME,
|
||||
CONF_ID,
|
||||
CONF_NAME,
|
||||
CONF_TYPE,
|
||||
EVENT_HOMEASSISTANT_START,
|
||||
SERVICE_RELOAD,
|
||||
STATE_HOME,
|
||||
|
@ -307,6 +306,23 @@ class PersonStorageCollection(collection.DictStorageCollection):
|
|||
raise ValueError("User already taken")
|
||||
|
||||
|
||||
class PersonStorageCollectionWebsocket(collection.DictStorageCollectionWebsocket):
|
||||
"""Class to expose storage collection management over websocket."""
|
||||
|
||||
def ws_list_item(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
connection: websocket_api.ActiveConnection,
|
||||
msg: dict[str, Any],
|
||||
) -> None:
|
||||
"""List persons."""
|
||||
yaml, storage, _ = hass.data[DOMAIN]
|
||||
connection.send_result(
|
||||
msg[ATTR_ID],
|
||||
{"storage": storage.async_items(), "config": yaml.async_items()},
|
||||
)
|
||||
|
||||
|
||||
async def filter_yaml_data(hass: HomeAssistant, persons: list[dict]) -> list[dict]:
|
||||
"""Validate YAML data that we can't validate via schema."""
|
||||
filtered = []
|
||||
|
@ -370,11 +386,9 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
|
||||
hass.data[DOMAIN] = (yaml_collection, storage_collection, entity_component)
|
||||
|
||||
collection.DictStorageCollectionWebsocket(
|
||||
PersonStorageCollectionWebsocket(
|
||||
storage_collection, DOMAIN, DOMAIN, CREATE_FIELDS, UPDATE_FIELDS
|
||||
).async_setup(hass, create_list=False)
|
||||
|
||||
websocket_api.async_register_command(hass, ws_list_person)
|
||||
).async_setup(hass)
|
||||
|
||||
async def _handle_user_removed(event: Event) -> None:
|
||||
"""Handle a user being removed."""
|
||||
|
@ -570,19 +584,6 @@ class Person(
|
|||
self._attr_extra_state_attributes = data
|
||||
|
||||
|
||||
@websocket_api.websocket_command({vol.Required(CONF_TYPE): "person/list"})
|
||||
def ws_list_person(
|
||||
hass: HomeAssistant,
|
||||
connection: websocket_api.ActiveConnection,
|
||||
msg: dict[str, Any],
|
||||
) -> None:
|
||||
"""List persons."""
|
||||
yaml, storage, _ = hass.data[DOMAIN]
|
||||
connection.send_result(
|
||||
msg[ATTR_ID], {"storage": storage.async_items(), "config": yaml.async_items()}
|
||||
)
|
||||
|
||||
|
||||
def _get_latest(prev: State | None, curr: State) -> State:
|
||||
"""Get latest state."""
|
||||
if prev is None or curr.last_updated > prev.last_updated:
|
||||
|
|
|
@ -537,19 +537,17 @@ class StorageCollectionWebsocket[_StorageCollectionT: StorageCollection]:
|
|||
self,
|
||||
hass: HomeAssistant,
|
||||
*,
|
||||
create_list: bool = True,
|
||||
create_create: bool = True,
|
||||
) -> None:
|
||||
"""Set up the websocket commands."""
|
||||
if create_list:
|
||||
websocket_api.async_register_command(
|
||||
hass,
|
||||
f"{self.api_prefix}/list",
|
||||
self.ws_list_item,
|
||||
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
||||
{vol.Required("type"): f"{self.api_prefix}/list"}
|
||||
),
|
||||
)
|
||||
websocket_api.async_register_command(
|
||||
hass,
|
||||
f"{self.api_prefix}/list",
|
||||
self.ws_list_item,
|
||||
websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend(
|
||||
{vol.Required("type"): f"{self.api_prefix}/list"}
|
||||
),
|
||||
)
|
||||
|
||||
if create_create:
|
||||
websocket_api.async_register_command(
|
||||
|
|
|
@ -5,6 +5,8 @@ from typing import Any
|
|||
from unittest.mock import patch
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.lovelace import dashboard, resources
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
@ -17,8 +19,9 @@ RESOURCE_EXAMPLES = [
|
|||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("list_cmd", ["lovelace/resources", "lovelace/resources/list"])
|
||||
async def test_yaml_resources(
|
||||
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
|
||||
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, list_cmd: str
|
||||
) -> None:
|
||||
"""Test defining resources in configuration.yaml."""
|
||||
assert await async_setup_component(
|
||||
|
@ -28,14 +31,15 @@ async def test_yaml_resources(
|
|||
client = await hass_ws_client(hass)
|
||||
|
||||
# Fetch data
|
||||
await client.send_json({"id": 5, "type": "lovelace/resources"})
|
||||
await client.send_json({"id": 5, "type": list_cmd})
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
assert response["result"] == RESOURCE_EXAMPLES
|
||||
|
||||
|
||||
@pytest.mark.parametrize("list_cmd", ["lovelace/resources", "lovelace/resources/list"])
|
||||
async def test_yaml_resources_backwards(
|
||||
hass: HomeAssistant, hass_ws_client: WebSocketGenerator
|
||||
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, list_cmd: str
|
||||
) -> None:
|
||||
"""Test defining resources in YAML ll config (legacy)."""
|
||||
with patch(
|
||||
|
@ -49,16 +53,18 @@ async def test_yaml_resources_backwards(
|
|||
client = await hass_ws_client(hass)
|
||||
|
||||
# Fetch data
|
||||
await client.send_json({"id": 5, "type": "lovelace/resources"})
|
||||
await client.send_json({"id": 5, "type": list_cmd})
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
assert response["result"] == RESOURCE_EXAMPLES
|
||||
|
||||
|
||||
@pytest.mark.parametrize("list_cmd", ["lovelace/resources", "lovelace/resources/list"])
|
||||
async def test_storage_resources(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
hass_storage: dict[str, Any],
|
||||
list_cmd: str,
|
||||
) -> None:
|
||||
"""Test defining resources in storage config."""
|
||||
resource_config = [{**item, "id": uuid.uuid4().hex} for item in RESOURCE_EXAMPLES]
|
||||
|
@ -72,16 +78,18 @@ async def test_storage_resources(
|
|||
client = await hass_ws_client(hass)
|
||||
|
||||
# Fetch data
|
||||
await client.send_json({"id": 5, "type": "lovelace/resources"})
|
||||
await client.send_json({"id": 5, "type": list_cmd})
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
assert response["result"] == resource_config
|
||||
|
||||
|
||||
@pytest.mark.parametrize("list_cmd", ["lovelace/resources", "lovelace/resources/list"])
|
||||
async def test_storage_resources_import(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
hass_storage: dict[str, Any],
|
||||
list_cmd: str,
|
||||
) -> None:
|
||||
"""Test importing resources from storage config."""
|
||||
assert await async_setup_component(hass, "lovelace", {})
|
||||
|
@ -94,7 +102,7 @@ async def test_storage_resources_import(
|
|||
client = await hass_ws_client(hass)
|
||||
|
||||
# Fetch data
|
||||
await client.send_json({"id": 5, "type": "lovelace/resources"})
|
||||
await client.send_json({"id": 5, "type": list_cmd})
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
assert (
|
||||
|
@ -118,7 +126,7 @@ async def test_storage_resources_import(
|
|||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
|
||||
await client.send_json({"id": 7, "type": "lovelace/resources"})
|
||||
await client.send_json({"id": 7, "type": list_cmd})
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
|
||||
|
@ -141,7 +149,7 @@ async def test_storage_resources_import(
|
|||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
|
||||
await client.send_json({"id": 9, "type": "lovelace/resources"})
|
||||
await client.send_json({"id": 9, "type": list_cmd})
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
|
||||
|
@ -160,7 +168,7 @@ async def test_storage_resources_import(
|
|||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
|
||||
await client.send_json({"id": 11, "type": "lovelace/resources"})
|
||||
await client.send_json({"id": 11, "type": list_cmd})
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
|
||||
|
@ -168,10 +176,12 @@ async def test_storage_resources_import(
|
|||
assert first_item["id"] not in (item["id"] for item in response["result"])
|
||||
|
||||
|
||||
@pytest.mark.parametrize("list_cmd", ["lovelace/resources", "lovelace/resources/list"])
|
||||
async def test_storage_resources_import_invalid(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
hass_storage: dict[str, Any],
|
||||
list_cmd: str,
|
||||
) -> None:
|
||||
"""Test importing resources from storage config."""
|
||||
assert await async_setup_component(hass, "lovelace", {})
|
||||
|
@ -184,7 +194,7 @@ async def test_storage_resources_import_invalid(
|
|||
client = await hass_ws_client(hass)
|
||||
|
||||
# Fetch data
|
||||
await client.send_json({"id": 5, "type": "lovelace/resources"})
|
||||
await client.send_json({"id": 5, "type": list_cmd})
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
assert response["result"] == []
|
||||
|
@ -194,10 +204,12 @@ async def test_storage_resources_import_invalid(
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("list_cmd", ["lovelace/resources", "lovelace/resources/list"])
|
||||
async def test_storage_resources_safe_mode(
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
hass_storage: dict[str, Any],
|
||||
list_cmd: str,
|
||||
) -> None:
|
||||
"""Test defining resources in storage config."""
|
||||
|
||||
|
@ -213,7 +225,7 @@ async def test_storage_resources_safe_mode(
|
|||
hass.config.safe_mode = True
|
||||
|
||||
# Fetch data
|
||||
await client.send_json({"id": 5, "type": "lovelace/resources"})
|
||||
await client.send_json({"id": 5, "type": list_cmd})
|
||||
response = await client.receive_json()
|
||||
assert response["success"]
|
||||
assert response["result"] == []
|
||||
|
|
Loading…
Add table
Reference in a new issue