Lovelace resource mgmt (#32224)

* Add websockets commands for resource management

* Add LL resource management

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
pull/32255/head
Paulus Schoutsen 2020-02-26 16:43:34 -08:00 committed by GitHub
parent 92988d60a7
commit 483d822272
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 112 additions and 7 deletions

View File

@ -7,7 +7,7 @@ import voluptuous as vol
from homeassistant.components import frontend from homeassistant.components import frontend
from homeassistant.const import CONF_FILENAME, CONF_ICON from homeassistant.const import CONF_FILENAME, CONF_ICON
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import collection, config_validation as cv
from homeassistant.util import sanitize_filename, slugify from homeassistant.util import sanitize_filename, slugify
from . import dashboard, resources, websocket from . import dashboard, resources, websocket
@ -17,7 +17,9 @@ from .const import (
LOVELACE_CONFIG_FILE, LOVELACE_CONFIG_FILE,
MODE_STORAGE, MODE_STORAGE,
MODE_YAML, MODE_YAML,
RESOURCE_CREATE_FIELDS,
RESOURCE_SCHEMA, RESOURCE_SCHEMA,
RESOURCE_UPDATE_FIELDS,
) )
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -111,6 +113,14 @@ async def async_setup(hass, config):
resource_collection = resources.ResourceStorageCollection(hass, default_config) resource_collection = resources.ResourceStorageCollection(hass, default_config)
collection.StorageCollectionWebsocket(
resource_collection,
"lovelace/resources",
"resource",
RESOURCE_CREATE_FIELDS,
RESOURCE_UPDATE_FIELDS,
).async_setup(hass, create_list=False)
hass.components.websocket_api.async_register_command( hass.components.websocket_api.async_register_command(
websocket.websocket_lovelace_config websocket.websocket_lovelace_config
) )

View File

@ -14,13 +14,27 @@ MODE_STORAGE = "storage"
LOVELACE_CONFIG_FILE = "ui-lovelace.yaml" LOVELACE_CONFIG_FILE = "ui-lovelace.yaml"
CONF_RESOURCES = "resources" CONF_RESOURCES = "resources"
CONF_URL_PATH = "url_path" CONF_URL_PATH = "url_path"
CONF_RESOURCE_TYPE_WS = "res_type"
RESOURCE_TYPES = ["js", "css", "module", "html"]
RESOURCE_FIELDS = { RESOURCE_FIELDS = {
CONF_TYPE: vol.In(["js", "css", "module", "html"]), CONF_TYPE: vol.In(RESOURCE_TYPES),
CONF_URL: cv.string, CONF_URL: cv.string,
} }
RESOURCE_SCHEMA = vol.Schema(RESOURCE_FIELDS) RESOURCE_SCHEMA = vol.Schema(RESOURCE_FIELDS)
RESOURCE_CREATE_FIELDS = {
vol.Required(CONF_RESOURCE_TYPE_WS): vol.In(RESOURCE_TYPES),
vol.Required(CONF_URL): cv.string,
}
RESOURCE_UPDATE_FIELDS = {
vol.Optional(CONF_RESOURCE_TYPE_WS): vol.In(RESOURCE_TYPES),
vol.Optional(CONF_URL): cv.string,
}
class ConfigNotFound(HomeAssistantError): class ConfigNotFound(HomeAssistantError):
"""When no config available.""" """When no config available."""

View File

@ -5,11 +5,19 @@ import uuid
import voluptuous as vol import voluptuous as vol
from homeassistant.const import CONF_TYPE
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import collection, storage from homeassistant.helpers import collection, storage
from .const import CONF_RESOURCES, DOMAIN, RESOURCE_SCHEMA from .const import (
CONF_RESOURCE_TYPE_WS,
CONF_RESOURCES,
DOMAIN,
RESOURCE_CREATE_FIELDS,
RESOURCE_SCHEMA,
RESOURCE_UPDATE_FIELDS,
)
from .dashboard import LovelaceConfig from .dashboard import LovelaceConfig
RESOURCE_STORAGE_KEY = f"{DOMAIN}_resources" RESOURCE_STORAGE_KEY = f"{DOMAIN}_resources"
@ -36,6 +44,8 @@ class ResourceStorageCollection(collection.StorageCollection):
"""Collection to store resources.""" """Collection to store resources."""
loaded = False loaded = False
CREATE_SCHEMA = vol.Schema(RESOURCE_CREATE_FIELDS)
UPDATE_SCHEMA = vol.Schema(RESOURCE_UPDATE_FIELDS)
def __init__(self, hass: HomeAssistant, ll_config: LovelaceConfig): def __init__(self, hass: HomeAssistant, ll_config: LovelaceConfig):
"""Initialize the storage collection.""" """Initialize the storage collection."""
@ -84,13 +94,23 @@ class ResourceStorageCollection(collection.StorageCollection):
async def _process_create_data(self, data: dict) -> dict: async def _process_create_data(self, data: dict) -> dict:
"""Validate the config is valid.""" """Validate the config is valid."""
raise NotImplementedError data = self.CREATE_SCHEMA(data)
data[CONF_TYPE] = data.pop(CONF_RESOURCE_TYPE_WS)
return data
@callback @callback
def _get_suggested_id(self, info: dict) -> str: def _get_suggested_id(self, info: dict) -> str:
"""Suggest an ID based on the config.""" """Return unique ID."""
raise NotImplementedError return uuid.uuid4().hex
async def _update_data(self, data: dict, update_data: dict) -> dict: async def _update_data(self, data: dict, update_data: dict) -> dict:
"""Return a new updated data object.""" """Return a new updated data object."""
raise NotImplementedError if not self.loaded:
await self.async_load()
self.loaded = True
update_data = self.UPDATE_SCHEMA(update_data)
if CONF_RESOURCE_TYPE_WS in update_data:
update_data[CONF_TYPE] = update_data.pop(CONF_RESOURCE_TYPE_WS)
return {**data, **update_data}

View File

@ -90,6 +90,67 @@ async def test_storage_resources_import(hass, hass_ws_client, hass_storage):
not in hass_storage[dashboard.CONFIG_STORAGE_KEY_DEFAULT]["data"]["config"] not in hass_storage[dashboard.CONFIG_STORAGE_KEY_DEFAULT]["data"]["config"]
) )
# Add a resource
await client.send_json(
{
"id": 6,
"type": "lovelace/resources/create",
"res_type": "module",
"url": "/local/yo.js",
}
)
response = await client.receive_json()
assert response["success"]
await client.send_json({"id": 7, "type": "lovelace/resources"})
response = await client.receive_json()
assert response["success"]
last_item = response["result"][-1]
assert last_item["type"] == "module"
assert last_item["url"] == "/local/yo.js"
# Update a resource
first_item = response["result"][0]
await client.send_json(
{
"id": 8,
"type": "lovelace/resources/update",
"resource_id": first_item["id"],
"res_type": "css",
"url": "/local/updated.css",
}
)
response = await client.receive_json()
assert response["success"]
await client.send_json({"id": 9, "type": "lovelace/resources"})
response = await client.receive_json()
assert response["success"]
first_item = response["result"][0]
assert first_item["type"] == "css"
assert first_item["url"] == "/local/updated.css"
# Delete resources
await client.send_json(
{
"id": 10,
"type": "lovelace/resources/delete",
"resource_id": first_item["id"],
}
)
response = await client.receive_json()
assert response["success"]
await client.send_json({"id": 11, "type": "lovelace/resources"})
response = await client.receive_json()
assert response["success"]
assert len(response["result"]) == 2
assert first_item["id"] not in (item["id"] for item in response["result"])
async def test_storage_resources_import_invalid(hass, hass_ws_client, hass_storage): async def test_storage_resources_import_invalid(hass, hass_ws_client, hass_storage):
"""Test importing resources from storage config.""" """Test importing resources from storage config."""