dashboard: move storage json update to a background task in edit save (#6280)

* dashboard: move storage json update to a background task in edit save

* dashboard: move storage json update to a background task in edit save

* fix typing

* docs
This commit is contained in:
J. Nick Koston 2024-02-24 18:39:47 -10:00 committed by Jesse Hills
parent 2cf6393161
commit 84c6e52be2
No known key found for this signature in database
GPG key ID: BEAAE804EFD8E83A
4 changed files with 31 additions and 7 deletions

View file

@ -8,3 +8,5 @@ MAX_EXECUTOR_WORKERS = 48
SENTINEL = object() SENTINEL = object()
DASHBOARD_COMMAND = ["esphome", "--dashboard"]

View file

@ -1,11 +1,13 @@
from __future__ import annotations from __future__ import annotations
import asyncio import asyncio
import contextlib
import logging import logging
import threading import threading
from dataclasses import dataclass from dataclasses import dataclass
from functools import partial from functools import partial
from typing import TYPE_CHECKING, Any, Callable from typing import TYPE_CHECKING, Any, Callable
from collections.abc import Coroutine
from ..zeroconf import DiscoveredImport from ..zeroconf import DiscoveredImport
from .dns import DNSCache from .dns import DNSCache
@ -71,6 +73,7 @@ class ESPHomeDashboard:
"mdns_status", "mdns_status",
"settings", "settings",
"dns_cache", "dns_cache",
"_background_tasks",
) )
def __init__(self) -> None: def __init__(self) -> None:
@ -85,6 +88,7 @@ class ESPHomeDashboard:
self.mdns_status: MDNSStatus | None = None self.mdns_status: MDNSStatus | None = None
self.settings = DashboardSettings() self.settings = DashboardSettings()
self.dns_cache = DNSCache() self.dns_cache = DNSCache()
self._background_tasks: set[asyncio.Task] = set()
async def async_setup(self) -> None: async def async_setup(self) -> None:
"""Setup the dashboard.""" """Setup the dashboard."""
@ -132,7 +136,19 @@ class ESPHomeDashboard:
if settings.status_use_mqtt: if settings.status_use_mqtt:
status_thread_mqtt.join() status_thread_mqtt.join()
self.mqtt_ping_request.set() self.mqtt_ping_request.set()
for task in self._background_tasks:
task.cancel()
with contextlib.suppress(asyncio.CancelledError):
await task
await asyncio.sleep(0) await asyncio.sleep(0)
def async_create_background_task(
self, coro: Coroutine[Any, Any, Any]
) -> asyncio.Task:
"""Create a background task."""
task = self.loop.create_task(coro)
task.add_done_callback(self._background_tasks.discard)
return task
DASHBOARD = ESPHomeDashboard() DASHBOARD = ESPHomeDashboard()

View file

@ -10,12 +10,14 @@ from esphome import const, util
from esphome.storage_json import StorageJSON, ext_storage_path from esphome.storage_json import StorageJSON, ext_storage_path
from .const import ( from .const import (
DASHBOARD_COMMAND,
EVENT_ENTRY_ADDED, EVENT_ENTRY_ADDED,
EVENT_ENTRY_REMOVED, EVENT_ENTRY_REMOVED,
EVENT_ENTRY_STATE_CHANGED, EVENT_ENTRY_STATE_CHANGED,
EVENT_ENTRY_UPDATED, EVENT_ENTRY_UPDATED,
) )
from .enum import StrEnum from .enum import StrEnum
from .util.subprocess import async_run_system_command
if TYPE_CHECKING: if TYPE_CHECKING:
from .core import ESPHomeDashboard from .core import ESPHomeDashboard
@ -235,6 +237,14 @@ class DashboardEntries:
) )
return path_to_cache_key return path_to_cache_key
def async_schedule_storage_json_update(self, filename: str) -> None:
"""Schedule a task to update the storage JSON file."""
self._dashboard.async_create_background_task(
async_run_system_command(
[*DASHBOARD_COMMAND, "compile", "--only-generate", filename]
)
)
class DashboardEntry: class DashboardEntry:
"""Represents a single dashboard entry. """Represents a single dashboard entry.

View file

@ -9,11 +9,11 @@ import hashlib
import json import json
import logging import logging
import os import os
import time
import secrets import secrets
import shutil import shutil
import subprocess import subprocess
import threading import threading
import time
from collections.abc import Iterable from collections.abc import Iterable
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING, Any, Callable, TypeVar from typing import TYPE_CHECKING, Any, Callable, TypeVar
@ -40,6 +40,7 @@ from esphome.storage_json import StorageJSON, ext_storage_path, trash_storage_pa
from esphome.util import get_serial_ports, shlex_quote from esphome.util import get_serial_ports, shlex_quote
from esphome.yaml_util import FastestAvailableSafeLoader from esphome.yaml_util import FastestAvailableSafeLoader
from .const import DASHBOARD_COMMAND
from .core import DASHBOARD from .core import DASHBOARD
from .entries import EntryState, entry_state_to_bool from .entries import EntryState, entry_state_to_bool
from .util.file import write_file from .util.file import write_file
@ -286,9 +287,6 @@ class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler):
raise NotImplementedError raise NotImplementedError
DASHBOARD_COMMAND = ["esphome", "--dashboard"]
class EsphomePortCommandWebSocket(EsphomeCommandWebSocket): class EsphomePortCommandWebSocket(EsphomeCommandWebSocket):
"""Base class for commands that require a port.""" """Base class for commands that require a port."""
@ -841,9 +839,7 @@ class EditRequestHandler(BaseHandler):
None, self._write_file, config_file, self.request.body None, self._write_file, config_file, self.request.body
) )
# Ensure the StorageJSON is updated as well # Ensure the StorageJSON is updated as well
await async_run_system_command( DASHBOARD.entries.async_schedule_storage_json_update(filename)
[*DASHBOARD_COMMAND, "compile", "--only-generate", config_file]
)
self.set_status(200) self.set_status(200)