mirror of
https://github.com/home-assistant/frontend.git
synced 2026-05-01 23:13:03 +00:00
Compare commits
20 Commits
20260429.1
...
missing-to
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfcab8172e | ||
|
|
c78cfb4012 | ||
|
|
09e993ffd6 | ||
|
|
f8f175426d | ||
|
|
89e3687f22 | ||
|
|
18a20576a9 | ||
|
|
8ee41e5d9b | ||
|
|
cac31ac55a | ||
|
|
8f002f2783 | ||
|
|
df754fcd0d | ||
|
|
bc4437b3b5 | ||
|
|
c99b43dcf3 | ||
|
|
8945b917b3 | ||
|
|
4d75ea5198 | ||
|
|
ba3a63f856 | ||
|
|
fd25d38be6 | ||
|
|
ac22374a00 | ||
|
|
de529cc26b | ||
|
|
126db3e8df | ||
|
|
ed6fd59968 |
@@ -33,6 +33,7 @@
|
||||
"@codemirror/lang-jinja": "6.0.1",
|
||||
"@codemirror/lang-yaml": "6.1.3",
|
||||
"@codemirror/language": "6.12.3",
|
||||
"@codemirror/lint": "6.9.5",
|
||||
"@codemirror/search": "6.7.0",
|
||||
"@codemirror/state": "6.6.0",
|
||||
"@codemirror/view": "6.41.1",
|
||||
@@ -53,7 +54,7 @@
|
||||
"@fullcalendar/list": "6.1.20",
|
||||
"@fullcalendar/luxon3": "6.1.20",
|
||||
"@fullcalendar/timegrid": "6.1.20",
|
||||
"@home-assistant/webawesome": "3.3.1-ha.1",
|
||||
"@home-assistant/webawesome": "3.3.1-ha.2",
|
||||
"@lezer/highlight": "1.2.3",
|
||||
"@lit-labs/motion": "1.1.0",
|
||||
"@lit-labs/observers": "2.1.0",
|
||||
@@ -184,7 +185,7 @@
|
||||
"gulp-rename": "2.1.0",
|
||||
"html-minifier-terser": "7.2.0",
|
||||
"husky": "9.1.7",
|
||||
"jsdom": "29.0.2",
|
||||
"jsdom": "29.1.0",
|
||||
"jszip": "3.10.1",
|
||||
"lint-staged": "16.4.0",
|
||||
"lit-analyzer": "2.0.3",
|
||||
@@ -200,7 +201,7 @@
|
||||
"terser-webpack-plugin": "5.5.0",
|
||||
"ts-lit-plugin": "2.0.2",
|
||||
"typescript": "6.0.3",
|
||||
"typescript-eslint": "8.59.0",
|
||||
"typescript-eslint": "8.59.1",
|
||||
"vite-tsconfig-paths": "6.1.1",
|
||||
"vitest": "4.1.5",
|
||||
"webpack-stats-plugin": "1.1.3",
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20260429.1"
|
||||
version = "20260429.0"
|
||||
license = "Apache-2.0"
|
||||
license-files = ["LICENSE*"]
|
||||
description = "The Home Assistant frontend"
|
||||
|
||||
@@ -1499,6 +1499,7 @@ export class HaChartBase extends LitElement {
|
||||
margin-inline-start: var(--ha-space-1);
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
line-height: 1;
|
||||
}
|
||||
.chart-legend .legend-toggle {
|
||||
background: none;
|
||||
|
||||
@@ -160,6 +160,8 @@ export class HaEntityToggle extends LitElement {
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
min-width: 38px;
|
||||
}
|
||||
|
||||
@@ -95,6 +95,8 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
|
||||
@property({ type: Boolean }) public error = false;
|
||||
|
||||
@property({ type: Boolean }) public lint = false;
|
||||
|
||||
@property({ type: Boolean, attribute: "disable-fullscreen" })
|
||||
public disableFullscreen = false;
|
||||
|
||||
@@ -163,6 +165,40 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
return !!this.renderRoot.querySelector(`span.${className}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a YAML parse error (or null to clear) into the lint gutter as a
|
||||
* diagnostic. Avoids re-parsing the document — the caller (ha-yaml-editor)
|
||||
* already has the error from its own js-yaml load() call.
|
||||
*/
|
||||
public setYamlError(
|
||||
err: {
|
||||
mark?: { position: number; line: number; column: number };
|
||||
reason?: string;
|
||||
} | null
|
||||
): void {
|
||||
if (!this.codemirror || !this._loadedCodeMirror) return;
|
||||
let diagnostics: {
|
||||
from: number;
|
||||
to: number;
|
||||
severity: "error";
|
||||
message: string;
|
||||
}[] = [];
|
||||
if (err) {
|
||||
const doc = this.codemirror.state.doc;
|
||||
const pos = err.mark ? Math.min(err.mark.position, doc.length) : 0;
|
||||
const line = doc.lineAt(pos);
|
||||
const message = `${
|
||||
err.reason ||
|
||||
this.hass?.localize("ui.components.yaml-editor.error") ||
|
||||
"YAML syntax error"
|
||||
}${err.mark ? ` (${this.hass?.localize("ui.components.yaml-editor.error_location", { line: err.mark.line + 1, column: err.mark.column + 1 })})` : ""}`;
|
||||
diagnostics = [{ from: pos, to: line.to, severity: "error", message }];
|
||||
}
|
||||
this.codemirror.dispatch(
|
||||
this._loadedCodeMirror.setDiagnostics(this.codemirror.state, diagnostics)
|
||||
);
|
||||
}
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.classList.toggle("in-dialog", this.inDialog);
|
||||
@@ -220,17 +256,38 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
transactions.push({
|
||||
effects: [
|
||||
this._loadedCodeMirror!.langCompartment!.reconfigure(this._mode),
|
||||
this._loadedCodeMirror!.yamlLintCompartment!.reconfigure(
|
||||
this.lint && !this.readOnly
|
||||
? [this._loadedCodeMirror!.lintGutter()]
|
||||
: []
|
||||
),
|
||||
],
|
||||
});
|
||||
}
|
||||
if (changedProps.has("readOnly")) {
|
||||
transactions.push({
|
||||
effects: this._loadedCodeMirror!.readonlyCompartment!.reconfigure(
|
||||
this._loadedCodeMirror!.EditorView!.editable.of(!this.readOnly)
|
||||
),
|
||||
effects: [
|
||||
this._loadedCodeMirror!.readonlyCompartment!.reconfigure(
|
||||
this._loadedCodeMirror!.EditorView!.editable.of(!this.readOnly)
|
||||
),
|
||||
this._loadedCodeMirror!.yamlLintCompartment!.reconfigure(
|
||||
this.lint && !this.readOnly
|
||||
? [this._loadedCodeMirror!.lintGutter()]
|
||||
: []
|
||||
),
|
||||
],
|
||||
});
|
||||
this._updateToolbarButtons();
|
||||
}
|
||||
if (changedProps.has("lint")) {
|
||||
transactions.push({
|
||||
effects: this._loadedCodeMirror!.yamlLintCompartment!.reconfigure(
|
||||
this.lint && !this.readOnly
|
||||
? [this._loadedCodeMirror!.lintGutter()]
|
||||
: []
|
||||
),
|
||||
});
|
||||
}
|
||||
if (changedProps.has("linewrap")) {
|
||||
transactions.push({
|
||||
effects: this._loadedCodeMirror!.linewrapCompartment!.reconfigure(
|
||||
@@ -312,6 +369,7 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
...this._loadedCodeMirror.searchKeymap,
|
||||
...this._loadedCodeMirror.historyKeymap,
|
||||
...this._loadedCodeMirror.tabKeyBindings,
|
||||
...this._loadedCodeMirror.lintKeymap,
|
||||
saveKeyBinding,
|
||||
]),
|
||||
this._loadedCodeMirror.search({ top: true }),
|
||||
@@ -326,6 +384,9 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
this._loadedCodeMirror.linewrapCompartment.of(
|
||||
this.linewrap ? this._loadedCodeMirror.EditorView.lineWrapping : []
|
||||
),
|
||||
this._loadedCodeMirror.yamlLintCompartment.of(
|
||||
this.lint && !this.readOnly ? [this._loadedCodeMirror.lintGutter()] : []
|
||||
),
|
||||
this._loadedCodeMirror.EditorView.updateListener.of(this._onUpdate),
|
||||
this._loadedCodeMirror.tooltips({
|
||||
position: "absolute",
|
||||
|
||||
@@ -53,7 +53,10 @@ export class HaIconButton extends LitElement {
|
||||
.download=${this.download}
|
||||
>
|
||||
${this.path
|
||||
? html`<ha-svg-icon .path=${this.path}></ha-svg-icon>`
|
||||
? html`<ha-svg-icon
|
||||
aria-hidden="true"
|
||||
.path=${this.path}
|
||||
></ha-svg-icon>`
|
||||
: html`<span><slot></slot></span>`}
|
||||
</ha-button>
|
||||
`;
|
||||
|
||||
@@ -8,7 +8,6 @@ import { copyToClipboard } from "../common/util/copy-clipboard";
|
||||
import { haStyle } from "../resources/styles";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import { showToast } from "../util/toast";
|
||||
import "./ha-alert";
|
||||
import "./ha-button";
|
||||
import "./ha-code-editor";
|
||||
import type { HaCodeEditor } from "./ha-code-editor";
|
||||
@@ -58,15 +57,8 @@ export class HaYamlEditor extends LitElement {
|
||||
@property({ attribute: "has-extra-actions", type: Boolean })
|
||||
public hasExtraActions = false;
|
||||
|
||||
@property({ attribute: "show-errors", type: Boolean })
|
||||
public showErrors = true;
|
||||
|
||||
@state() private _yaml = "";
|
||||
|
||||
@state() private _error = "";
|
||||
|
||||
@state() private _showingError = false;
|
||||
|
||||
@query("ha-code-editor") _codeEditor?: HaCodeEditor;
|
||||
|
||||
public setValue(value): void {
|
||||
@@ -126,16 +118,14 @@ export class HaYamlEditor extends LitElement {
|
||||
.disableFullscreen=${this.disableFullscreen}
|
||||
.inDialog=${this.inDialog}
|
||||
mode="yaml"
|
||||
lint
|
||||
autocomplete-entities
|
||||
autocomplete-icons
|
||||
.error=${this.isValid === false}
|
||||
@value-changed=${this._onChange}
|
||||
@blur=${this._onBlur}
|
||||
@editor-save=${this._onEditorSave}
|
||||
dir="ltr"
|
||||
></ha-code-editor>
|
||||
${this._showingError
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: nothing}
|
||||
${this.copyClipboard || this.hasExtraActions
|
||||
? html`
|
||||
<div class="card-actions">
|
||||
@@ -158,9 +148,13 @@ export class HaYamlEditor extends LitElement {
|
||||
private _onChange(ev: CustomEvent): void {
|
||||
ev.stopPropagation();
|
||||
this._yaml = ev.detail.value;
|
||||
let parsed;
|
||||
let parsed: unknown;
|
||||
let isValid = true;
|
||||
let errorMsg;
|
||||
let errorMsg: string | undefined;
|
||||
let yamlError: {
|
||||
mark?: { position: number; line: number; column: number };
|
||||
message?: string;
|
||||
} | null = null;
|
||||
|
||||
if (this._yaml) {
|
||||
try {
|
||||
@@ -168,15 +162,13 @@ export class HaYamlEditor extends LitElement {
|
||||
} catch (err: any) {
|
||||
// Invalid YAML
|
||||
isValid = false;
|
||||
yamlError = err;
|
||||
errorMsg = `${this.hass.localize("ui.components.yaml-editor.error", { reason: err.reason })}${err.mark ? ` (${this.hass.localize("ui.components.yaml-editor.error_location", { line: err.mark.line + 1, column: err.mark.column + 1 })})` : ""}`;
|
||||
}
|
||||
} else {
|
||||
parsed = {};
|
||||
}
|
||||
this._error = errorMsg ?? "";
|
||||
if (isValid) {
|
||||
this._showingError = false;
|
||||
}
|
||||
this._codeEditor?.setYamlError(yamlError);
|
||||
|
||||
this.value = parsed;
|
||||
this.isValid = isValid;
|
||||
@@ -188,16 +180,23 @@ export class HaYamlEditor extends LitElement {
|
||||
} as any);
|
||||
}
|
||||
|
||||
private _onBlur(): void {
|
||||
if (this.showErrors && this._error) {
|
||||
this._showingError = true;
|
||||
}
|
||||
}
|
||||
|
||||
get yaml() {
|
||||
return this._yaml;
|
||||
}
|
||||
|
||||
get codemirror() {
|
||||
return this._codeEditor?.codemirror;
|
||||
}
|
||||
|
||||
get hasComments(): boolean {
|
||||
return this._codeEditor?.hasComments ?? false;
|
||||
}
|
||||
|
||||
private _onEditorSave(ev: CustomEvent): void {
|
||||
fireEvent(this, "editor-save");
|
||||
ev.stopPropagation();
|
||||
}
|
||||
|
||||
private async _copyYaml(): Promise<void> {
|
||||
if (this.yaml) {
|
||||
await copyToClipboard(this.yaml);
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import { mdiMagnify } from "@mdi/js";
|
||||
import { css, html, type PropertyValues } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { internationalizationContext } from "../../data/context";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { HaInput } from "./ha-input";
|
||||
|
||||
/**
|
||||
@@ -17,10 +15,6 @@ import { HaInput } from "./ha-input";
|
||||
*/
|
||||
@customElement("ha-input-search")
|
||||
export class HaInputSearch extends HaInput {
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.withClear = true;
|
||||
@@ -35,7 +29,7 @@ export class HaInputSearch extends HaInput {
|
||||
!this.placeholder &&
|
||||
(!this.hasUpdated || changedProps.has("_i18n"))
|
||||
) {
|
||||
this.placeholder = this._i18n.localize("ui.common.search");
|
||||
this.placeholder = this.i18n?.localize?.("ui.common.search") || "Search";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,19 +2,21 @@ import "@home-assistant/webawesome/dist/components/animation/animation";
|
||||
import "@home-assistant/webawesome/dist/components/input/input";
|
||||
import type WaInput from "@home-assistant/webawesome/dist/components/input/input";
|
||||
import { HasSlotController } from "@home-assistant/webawesome/dist/internal/slot";
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import { mdiClose, mdiEye, mdiEyeOff } from "@mdi/js";
|
||||
import {
|
||||
LitElement,
|
||||
type PropertyValues,
|
||||
type TemplateResult,
|
||||
css,
|
||||
html,
|
||||
LitElement,
|
||||
nothing,
|
||||
type PropertyValues,
|
||||
type TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { stopPropagation } from "../../common/dom/stop_propagation";
|
||||
import { internationalizationContext } from "../../data/context";
|
||||
import "../ha-icon-button";
|
||||
import "../ha-svg-icon";
|
||||
import "../ha-tooltip";
|
||||
@@ -125,6 +127,10 @@ export class HaInput extends WaInputMixin(LitElement) {
|
||||
@query("wa-input")
|
||||
private _input?: WaInput;
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
protected i18n?: ContextType<typeof internationalizationContext>;
|
||||
|
||||
private readonly _hasSlotController = new HasSlotController(
|
||||
this,
|
||||
"label",
|
||||
@@ -233,19 +239,22 @@ export class HaInput extends WaInputMixin(LitElement) {
|
||||
${this.renderStartDefault()}
|
||||
</slot>
|
||||
<slot name="end" slot="end"> ${this.renderEndDefault()} </slot>
|
||||
<slot name="clear-icon" slot="clear-icon">
|
||||
<ha-icon-button .path=${mdiClose}></ha-icon-button>
|
||||
</slot>
|
||||
<slot name="show-password-icon" slot="show-password-icon">
|
||||
<slot name="clear-button" slot="clear-button">
|
||||
<ha-icon-button
|
||||
@keydown=${stopPropagation}
|
||||
.path=${mdiEye}
|
||||
@click=${this._handleClearClick}
|
||||
.label=${this.i18n?.localize?.("ui.components.input.clear") ||
|
||||
"Clear"}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>
|
||||
</slot>
|
||||
<slot name="hide-password-icon" slot="hide-password-icon">
|
||||
<slot name="password-toggle-button" slot="password-toggle-button">
|
||||
<ha-icon-button
|
||||
@keydown=${stopPropagation}
|
||||
.path=${mdiEyeOff}
|
||||
@click=${this._handlePasswordToggle}
|
||||
.label=${this.i18n?.localize?.(
|
||||
`ui.components.input.${this.passwordVisible ? "hide_password" : "show_password"}`
|
||||
) || (this.passwordVisible ? "Hide password" : "Show password")}
|
||||
.path=${this.passwordVisible ? mdiEyeOff : mdiEye}
|
||||
></ha-icon-button>
|
||||
</slot>
|
||||
<div
|
||||
@@ -293,6 +302,14 @@ export class HaInput extends WaInputMixin(LitElement) {
|
||||
}
|
||||
};
|
||||
|
||||
private _handleClearClick() {
|
||||
this._input?.clear();
|
||||
}
|
||||
|
||||
private _handlePasswordToggle() {
|
||||
this.passwordVisible = !this.passwordVisible;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
waInputStyles,
|
||||
css`
|
||||
@@ -414,6 +431,12 @@ export class HaInput extends WaInputMixin(LitElement) {
|
||||
color: var(--ha-color-text-secondary);
|
||||
}
|
||||
|
||||
ha-icon-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--ha-color-text-secondary);
|
||||
}
|
||||
|
||||
:host([appearance="outlined"]) wa-input.no-label {
|
||||
--ha-icon-button-size: 24px;
|
||||
--mdc-icon-size: 18px;
|
||||
|
||||
@@ -189,6 +189,20 @@ export const updateBackupConfig = (
|
||||
config: BackupMutableConfig
|
||||
) => hass.callWS({ type: "backup/config/update", ...config });
|
||||
|
||||
export const saveBackupConfig = (hass: HomeAssistant, config: BackupConfig) =>
|
||||
updateBackupConfig(hass, {
|
||||
create_backup: {
|
||||
agent_ids: config.create_backup.agent_ids,
|
||||
include_folders: config.create_backup.include_folders ?? [],
|
||||
include_database: config.create_backup.include_database,
|
||||
include_addons: config.create_backup.include_addons ?? [],
|
||||
include_all_addons: config.create_backup.include_all_addons,
|
||||
password: config.create_backup.password,
|
||||
},
|
||||
retention: config.retention,
|
||||
schedule: config.schedule,
|
||||
});
|
||||
|
||||
export const getBackupDownloadUrl = (
|
||||
id: string,
|
||||
agentId: string,
|
||||
|
||||
@@ -546,7 +546,6 @@ export class HaAutomationEditor extends AutomationScriptEditorMixin<AutomationCo
|
||||
.readOnly=${this.readOnly}
|
||||
@value-changed=${this._yamlChanged}
|
||||
@editor-save=${this._handleSaveAutomation}
|
||||
.showErrors=${false}
|
||||
disable-fullscreen
|
||||
></ha-yaml-editor>
|
||||
<ha-button
|
||||
|
||||
@@ -134,6 +134,7 @@ type AutomationItem = AutomationEntity & {
|
||||
formatted_state: string;
|
||||
category: string | undefined;
|
||||
label_entries: LabelRegistryEntry[];
|
||||
labels: string[]; // search only
|
||||
assistants: string[];
|
||||
assistants_sortable_key: string | undefined;
|
||||
};
|
||||
@@ -256,6 +257,9 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
||||
);
|
||||
const category = entityRegEntry?.categories.automation;
|
||||
const labels = labelReg && entityRegEntry?.labels;
|
||||
const label_entries = (labels || [])
|
||||
.map((lbl) => labelReg!.find((label) => label.label_id === lbl)!)
|
||||
.filter(Boolean);
|
||||
const assistants = getEntityVoiceAssistantsIds(
|
||||
entityReg,
|
||||
automation.entity_id
|
||||
@@ -271,9 +275,8 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
||||
category: category
|
||||
? categoryReg?.find((cat) => cat.category_id === category)?.name
|
||||
: undefined,
|
||||
label_entries: (labels || [])
|
||||
.map((lbl) => labelReg!.find((label) => label.label_id === lbl)!)
|
||||
.filter(Boolean),
|
||||
label_entries,
|
||||
labels: label_entries.map((lbl) => lbl.name),
|
||||
assistants,
|
||||
assistants_sortable_key: getAssistantsSortableKey(assistants),
|
||||
selectable: entityRegEntry !== undefined,
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
import { mdiPuzzle } from "@mdi/js";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-icon-next";
|
||||
import "../../../../../components/ha-md-list";
|
||||
import "../../../../../components/ha-md-list-item";
|
||||
import "../../../../../components/ha-svg-icon";
|
||||
import {
|
||||
getSupervisorUpdateConfig,
|
||||
type SupervisorUpdateConfig,
|
||||
} from "../../../../../data/supervisor/update";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
|
||||
@customElement("ha-backup-overview-app-update-backup")
|
||||
class HaBackupOverviewAppUpdateBackup extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _supervisorUpdateConfig?: SupervisorUpdateConfig;
|
||||
|
||||
protected firstUpdated() {
|
||||
this._fetchSupervisorUpdateConfig();
|
||||
}
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
if (this.hasUpdated) {
|
||||
this._fetchSupervisorUpdateConfig();
|
||||
}
|
||||
}
|
||||
|
||||
private async _fetchSupervisorUpdateConfig() {
|
||||
try {
|
||||
this._supervisorUpdateConfig = await getSupervisorUpdateConfig(this.hass);
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
private _appUpdateBackupDescription() {
|
||||
if (!this._supervisorUpdateConfig) {
|
||||
return this.hass.localize(
|
||||
"ui.panel.config.backup.settings.app_update_backup.local_only"
|
||||
);
|
||||
}
|
||||
|
||||
if (!this._supervisorUpdateConfig.add_on_backup_before_update) {
|
||||
return this.hass.localize(
|
||||
"ui.panel.config.backup.schedule.update_preference.skip_backups"
|
||||
);
|
||||
}
|
||||
|
||||
const copies =
|
||||
this._supervisorUpdateConfig.add_on_backup_retain_copies || 1;
|
||||
|
||||
return `${this.hass.localize(
|
||||
"ui.panel.config.backup.schedule.update_preference.backup_before_update"
|
||||
)} ${this.hass.localize(
|
||||
"ui.panel.config.backup.overview.settings.schedule_copies_backups",
|
||||
{ count: copies }
|
||||
)}`;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<ha-card>
|
||||
<div class="card-header">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.overview.app_update_backup.title"
|
||||
)}
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<ha-md-list>
|
||||
<ha-md-list-item
|
||||
type="link"
|
||||
href="/config/backup/app-update-backups"
|
||||
>
|
||||
<ha-svg-icon slot="start" .path=${mdiPuzzle}></ha-svg-icon>
|
||||
<div slot="headline">${this._appUpdateBackupDescription()}</div>
|
||||
<div slot="supporting-text">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.overview.app_update_backup.description"
|
||||
)}
|
||||
</div>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-md-list-item>
|
||||
</ha-md-list>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
.card-header {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
ha-md-list {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-backup-overview-app-update-backup": HaBackupOverviewAppUpdateBackup;
|
||||
}
|
||||
}
|
||||
154
src/panels/config/backup/ha-config-backup-app-update-backups.ts
Normal file
154
src/panels/config/backup/ha-config-backup-app-update-backups.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { debounce } from "../../../common/util/debounce";
|
||||
import "../../../components/ha-alert";
|
||||
import "../../../components/ha-card";
|
||||
import {
|
||||
getSupervisorUpdateConfig,
|
||||
updateSupervisorUpdateConfig,
|
||||
type SupervisorUpdateConfig,
|
||||
} from "../../../data/supervisor/update";
|
||||
import "../../../layouts/hass-subpage";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import "./components/config/ha-backup-config-addon";
|
||||
|
||||
@customElement("ha-config-backup-app-update-backups")
|
||||
class HaConfigBackupAppUpdateBackups extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@state() private _supervisorUpdateConfig?: SupervisorUpdateConfig;
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues<this>): void {
|
||||
super.willUpdate(changedProps);
|
||||
|
||||
if (
|
||||
!this.hasUpdated &&
|
||||
this.hass &&
|
||||
isComponentLoaded(this.hass.config, "hassio")
|
||||
) {
|
||||
this._getSupervisorUpdateConfig();
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<hass-subpage
|
||||
back-path="/config/backup/overview"
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.header=${this.hass.localize(
|
||||
"ui.panel.config.backup.app_update_backups.header"
|
||||
)}
|
||||
>
|
||||
<div class="content">
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.settings.app_update_backup.description"
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.settings.app_update_backup.local_only"
|
||||
)}
|
||||
</p>
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: nothing}
|
||||
<ha-backup-config-addon
|
||||
.hass=${this.hass}
|
||||
.supervisorUpdateConfig=${this._supervisorUpdateConfig}
|
||||
@update-config-changed=${this._supervisorUpdateConfigChanged}
|
||||
></ha-backup-config-addon>
|
||||
</div>
|
||||
</ha-card>
|
||||
</div>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _getSupervisorUpdateConfig() {
|
||||
try {
|
||||
this._supervisorUpdateConfig = await getSupervisorUpdateConfig(this.hass);
|
||||
this._error = undefined;
|
||||
} catch (err: any) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
this._error = this.hass.localize(
|
||||
"ui.panel.config.backup.settings.app_update_backup.error_load",
|
||||
{
|
||||
error: err?.message || err,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async _supervisorUpdateConfigChanged(ev) {
|
||||
const config = ev.detail.value as SupervisorUpdateConfig;
|
||||
this._supervisorUpdateConfig = {
|
||||
...this._supervisorUpdateConfig,
|
||||
...config,
|
||||
} as SupervisorUpdateConfig;
|
||||
this._debounceSaveSupervisorUpdateConfig();
|
||||
}
|
||||
|
||||
private _debounceSaveSupervisorUpdateConfig = debounce(
|
||||
() => this._saveSupervisorUpdateConfig(),
|
||||
500
|
||||
);
|
||||
|
||||
private async _saveSupervisorUpdateConfig() {
|
||||
if (!this._supervisorUpdateConfig) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await updateSupervisorUpdateConfig(
|
||||
this.hass,
|
||||
this._supervisorUpdateConfig
|
||||
);
|
||||
this._error = undefined;
|
||||
} catch (err: any) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
this._error = this.hass.localize(
|
||||
"ui.panel.config.backup.settings.app_update_backup.error_save",
|
||||
{
|
||||
error: err?.message || err?.toString(),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
p {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 28px 20px 0;
|
||||
max-width: 690px;
|
||||
margin: 0 auto;
|
||||
gap: var(--ha-space-6);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-config-backup-app-update-backups": HaConfigBackupAppUpdateBackups;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import { mdiDotsVertical, mdiPlus, mdiUpload } from "@mdi/js";
|
||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { debounce } from "../../../common/util/debounce";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-dropdown";
|
||||
@@ -23,6 +24,7 @@ import {
|
||||
computeBackupAgentName,
|
||||
generateBackup,
|
||||
generateBackupWithAutomaticSettings,
|
||||
saveBackupConfig,
|
||||
} from "../../../data/backup";
|
||||
import type { ManagerStateEvent } from "../../../data/backup_manager";
|
||||
import type { CloudStatus } from "../../../data/cloud";
|
||||
@@ -32,10 +34,12 @@ import { haStyle } from "../../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../../types";
|
||||
import { showAlertDialog } from "../../lovelace/custom-card-helpers";
|
||||
import "./components/overview/ha-backup-overview-backups";
|
||||
import "./components/overview/ha-backup-overview-app-update-backup";
|
||||
import "./components/overview/ha-backup-overview-onboarding";
|
||||
import "./components/overview/ha-backup-overview-progress";
|
||||
import "./components/overview/ha-backup-overview-settings";
|
||||
import "./components/overview/ha-backup-overview-summary";
|
||||
import "./components/config/ha-backup-config-encryption-key";
|
||||
import { showBackupOnboardingDialog } from "./dialogs/show-dialog-backup_onboarding";
|
||||
import { showGenerateBackupDialog } from "./dialogs/show-dialog-generate-backup";
|
||||
import { showNewBackupDialog } from "./dialogs/show-dialog-new-backup";
|
||||
@@ -68,10 +72,54 @@ class HaConfigBackupOverview extends LitElement {
|
||||
{ uploaded_bytes: number; total_bytes: number }
|
||||
> = {};
|
||||
|
||||
@state() private _config?: BackupConfig;
|
||||
|
||||
protected willUpdate(changedProperties: PropertyValues<this>): void {
|
||||
super.willUpdate(changedProperties);
|
||||
if (changedProperties.has("config") && !this._config) {
|
||||
this._config = this.config;
|
||||
}
|
||||
}
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
// Update config when the page is displayed (e.g. when coming back from a settings page)
|
||||
this._config = this.config;
|
||||
}
|
||||
|
||||
private _uploadBackup = async () => {
|
||||
await showUploadBackupDialog(this, {});
|
||||
};
|
||||
|
||||
private _encryptionKeyChanged(ev) {
|
||||
if (!this._config) {
|
||||
return;
|
||||
}
|
||||
|
||||
const password = ev.detail.value as string;
|
||||
this._config = {
|
||||
...this._config,
|
||||
create_backup: {
|
||||
...this._config.create_backup,
|
||||
password,
|
||||
},
|
||||
};
|
||||
|
||||
this._debounceSaveConfig();
|
||||
}
|
||||
|
||||
private _debounceSaveConfig = debounce(() => this._saveConfig(), 500);
|
||||
|
||||
private async _saveConfig() {
|
||||
if (!this._config) {
|
||||
return;
|
||||
}
|
||||
|
||||
await saveBackupConfig(this.hass, this._config);
|
||||
|
||||
fireEvent(this, "ha-refresh-backup-config");
|
||||
}
|
||||
|
||||
private _handleOnboardingButtonClick(ev) {
|
||||
ev.stopPropagation();
|
||||
this._setupAutomaticBackup(true);
|
||||
@@ -234,13 +282,41 @@ class HaConfigBackupOverview extends LitElement {
|
||||
.backups=${this.backups}
|
||||
></ha-backup-overview-backups>
|
||||
|
||||
${!this._needsOnboarding && this.config
|
||||
${!this._needsOnboarding && this._config
|
||||
? html`
|
||||
<ha-card>
|
||||
<div class="card-header">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.settings.encryption_key.title"
|
||||
)}
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.settings.encryption_key.description"
|
||||
)}
|
||||
</p>
|
||||
<ha-backup-config-encryption-key
|
||||
.hass=${this.hass}
|
||||
.value=${this._config.create_backup.password}
|
||||
@value-changed=${this._encryptionKeyChanged}
|
||||
></ha-backup-config-encryption-key>
|
||||
</div>
|
||||
</ha-card>
|
||||
|
||||
<ha-backup-overview-settings
|
||||
.hass=${this.hass}
|
||||
.config=${this.config!}
|
||||
.config=${this._config}
|
||||
.agents=${this.agents}
|
||||
></ha-backup-overview-settings>
|
||||
|
||||
${this.hass.config.components.includes("hassio")
|
||||
? html`
|
||||
<ha-backup-overview-app-update-backup
|
||||
.hass=${this.hass}
|
||||
></ha-backup-overview-app-update-backup>
|
||||
`
|
||||
: nothing}
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
@@ -270,6 +346,10 @@ class HaConfigBackupOverview extends LitElement {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
p {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 28px 20px 0;
|
||||
max-width: 690px;
|
||||
@@ -283,10 +363,6 @@ class HaConfigBackupOverview extends LitElement {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.card-content {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
.loading {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-icon-next";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import type { BackupAgent, BackupConfig } from "../../../data/backup";
|
||||
import { updateBackupConfig } from "../../../data/backup";
|
||||
import { saveBackupConfig } from "../../../data/backup";
|
||||
import type { CloudStatus } from "../../../data/cloud";
|
||||
import {
|
||||
getSupervisorUpdateConfig,
|
||||
@@ -27,11 +27,9 @@ import "../../../layouts/hass-subpage";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { brandsUrl } from "../../../util/brands-url";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import "./components/config/ha-backup-config-addon";
|
||||
import "./components/config/ha-backup-config-agents";
|
||||
import "./components/config/ha-backup-config-data";
|
||||
import type { BackupConfigData } from "./components/config/ha-backup-config-data";
|
||||
import "./components/config/ha-backup-config-encryption-key";
|
||||
import "./components/config/ha-backup-config-schedule";
|
||||
import type { BackupConfigSchedule } from "./components/config/ha-backup-config-schedule";
|
||||
import { showLocalBackupLocationDialog } from "./dialogs/show-dialog-local-backup-location";
|
||||
@@ -79,7 +77,7 @@ class HaConfigBackupSettings extends LitElement {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
this._supervisorUpdateConfigError = this.hass.localize(
|
||||
"ui.panel.config.backup.settings.app_update_backup.error_load",
|
||||
"ui.panel.config.backup.settings.schedule.error_load",
|
||||
{
|
||||
error: err?.message || err,
|
||||
}
|
||||
@@ -315,57 +313,6 @@ class HaConfigBackupSettings extends LitElement {
|
||||
: nothing}
|
||||
</div>
|
||||
</ha-card>
|
||||
${supervisor
|
||||
? html`<ha-card>
|
||||
<div class="card-header">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.settings.app_update_backup.title"
|
||||
)}
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.settings.app_update_backup.description"
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.settings.app_update_backup.local_only"
|
||||
)}
|
||||
</p>
|
||||
${this._supervisorUpdateConfigError
|
||||
? html`<ha-alert alert-type="error">
|
||||
${this._supervisorUpdateConfigError}
|
||||
</ha-alert>`
|
||||
: nothing}
|
||||
<ha-backup-config-addon
|
||||
.hass=${this.hass}
|
||||
.supervisorUpdateConfig=${this._supervisorUpdateConfig}
|
||||
@update-config-changed=${this
|
||||
._supervisorUpdateConfigChanged}
|
||||
></ha-backup-config-addon>
|
||||
</div>
|
||||
</ha-card>`
|
||||
: nothing}
|
||||
<ha-card>
|
||||
<div class="card-header">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.settings.encryption_key.title"
|
||||
)}
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.settings.encryption_key.description"
|
||||
)}
|
||||
</p>
|
||||
<ha-backup-config-encryption-key
|
||||
.hass=${this.hass}
|
||||
.value=${this._config.create_backup.password}
|
||||
@value-changed=${this._encryptionKeyChanged}
|
||||
></ha-backup-config-encryption-key>
|
||||
</div>
|
||||
</ha-card>
|
||||
</div>
|
||||
</hass-subpage>
|
||||
`;
|
||||
@@ -438,18 +385,6 @@ class HaConfigBackupSettings extends LitElement {
|
||||
this._debounceSave();
|
||||
}
|
||||
|
||||
private _encryptionKeyChanged(ev) {
|
||||
const password = ev.detail.value as string;
|
||||
this._config = {
|
||||
...this._config!,
|
||||
create_backup: {
|
||||
...this._config!.create_backup,
|
||||
password: password,
|
||||
},
|
||||
};
|
||||
this._debounceSave();
|
||||
}
|
||||
|
||||
private _debounceSaveSupervisorUpdateConfig = debounce(
|
||||
() => this._saveSupervisorUpdateConfig(),
|
||||
500
|
||||
@@ -468,7 +403,7 @@ class HaConfigBackupSettings extends LitElement {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(err);
|
||||
this._supervisorUpdateConfigError = this.hass.localize(
|
||||
"ui.panel.config.backup.settings.app_update_backup.error_save",
|
||||
"ui.panel.config.backup.settings.schedule.error_save",
|
||||
{
|
||||
error: err?.message || err?.toString(),
|
||||
}
|
||||
@@ -479,18 +414,7 @@ class HaConfigBackupSettings extends LitElement {
|
||||
private _debounceSave = debounce(() => this._save(), 500);
|
||||
|
||||
private async _save() {
|
||||
await updateBackupConfig(this.hass, {
|
||||
create_backup: {
|
||||
agent_ids: this._config!.create_backup.agent_ids,
|
||||
include_folders: this._config!.create_backup.include_folders ?? [],
|
||||
include_database: this._config!.create_backup.include_database,
|
||||
include_addons: this._config!.create_backup.include_addons ?? [],
|
||||
include_all_addons: this._config!.create_backup.include_all_addons,
|
||||
password: this._config!.create_backup.password,
|
||||
},
|
||||
retention: this._config!.retention,
|
||||
schedule: this._config!.schedule,
|
||||
});
|
||||
await saveBackupConfig(this.hass, this._config!);
|
||||
fireEvent(this, "ha-refresh-backup-config");
|
||||
}
|
||||
|
||||
|
||||
@@ -125,6 +125,11 @@ class HaConfigBackup extends SubscribeMixin(HassRouterPage) {
|
||||
load: () => import("./ha-config-backup-settings"),
|
||||
cache: true,
|
||||
},
|
||||
"app-update-backups": {
|
||||
tag: "ha-config-backup-app-update-backups",
|
||||
load: () => import("./ha-config-backup-app-update-backups"),
|
||||
cache: true,
|
||||
},
|
||||
location: {
|
||||
tag: "ha-config-backup-location",
|
||||
load: () => import("./ha-config-backup-location"),
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import { mdiDownload } from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { extractSearchParam } from "../../../common/url/search-params";
|
||||
import "../../../components/ha-analytics";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-md-list";
|
||||
import "../../../components/ha-md-list-item";
|
||||
import "../../../components/ha-spinner";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-switch";
|
||||
import { getSignedPath } from "../../../data/auth";
|
||||
import type { HaSwitch } from "../../../components/ha-switch";
|
||||
import type { Analytics } from "../../../data/analytics";
|
||||
import {
|
||||
@@ -26,6 +30,7 @@ import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { fileDownload } from "../../../util/file_download";
|
||||
|
||||
@customElement("ha-config-analytics")
|
||||
class ConfigAnalytics extends SubscribeMixin(LitElement) {
|
||||
@@ -119,6 +124,18 @@ class ConfigAnalytics extends SubscribeMixin(LitElement) {
|
||||
</ha-md-list-item>
|
||||
</ha-md-list>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-button
|
||||
size="small"
|
||||
appearance="plain"
|
||||
@click=${this._downloadDeviceInfo}
|
||||
>
|
||||
<ha-svg-icon slot="start" .path=${mdiDownload}></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.analytics.download_device_info"
|
||||
)}
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-card>`
|
||||
: nothing}
|
||||
${this._zwaveEntryId !== undefined
|
||||
@@ -290,6 +307,11 @@ class ConfigAnalytics extends SubscribeMixin(LitElement) {
|
||||
this._save();
|
||||
}
|
||||
|
||||
private async _downloadDeviceInfo(): Promise<void> {
|
||||
const signedPath = await getSignedPath(this.hass, "/api/analytics/devices");
|
||||
fileDownload(signedPath.path);
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
import { mdiDotsVertical, mdiDownload } from "@mdi/js";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../components/ha-dropdown";
|
||||
import "../../../components/ha-dropdown-item";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { getSignedPath } from "../../../data/auth";
|
||||
import "../../../layouts/hass-subpage";
|
||||
import type { HomeAssistant, Route } from "../../../types";
|
||||
import { fileDownload } from "../../../util/file_download";
|
||||
import "./ha-config-analytics";
|
||||
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
|
||||
|
||||
@customElement("ha-config-section-analytics")
|
||||
class HaConfigSectionAnalytics extends LitElement {
|
||||
@@ -29,19 +21,6 @@ class HaConfigSectionAnalytics extends LitElement {
|
||||
.narrow=${this.narrow}
|
||||
.header=${this.hass.localize("ui.panel.config.analytics.caption")}
|
||||
>
|
||||
<ha-dropdown
|
||||
@wa-select=${this._handleOverflowAction}
|
||||
slot="toolbar-icon"
|
||||
>
|
||||
<ha-icon-button slot="trigger" .path=${mdiDotsVertical}>
|
||||
</ha-icon-button>
|
||||
<ha-dropdown-item .value=${"download_device_info"}>
|
||||
<ha-svg-icon slot="icon" .path=${mdiDownload}></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.analytics.download_device_info"
|
||||
)}
|
||||
</ha-dropdown-item>
|
||||
</ha-dropdown>
|
||||
<div class="content">
|
||||
<ha-config-analytics .hass=${this.hass}></ha-config-analytics>
|
||||
</div>
|
||||
@@ -49,18 +28,6 @@ class HaConfigSectionAnalytics extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private async _handleOverflowAction(
|
||||
ev: HaDropdownSelectEvent
|
||||
): Promise<void> {
|
||||
if (ev.detail.item.value === "download_device_info") {
|
||||
const signedPath = await getSignedPath(
|
||||
this.hass,
|
||||
"/api/analytics/devices"
|
||||
);
|
||||
fileDownload(signedPath.path);
|
||||
}
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
.content {
|
||||
padding: 28px 20px 0;
|
||||
|
||||
@@ -244,9 +244,13 @@ class HaPanelDevAssist extends SubscribeMixin(LitElement) {
|
||||
max-width: 1040px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--ha-space-4);
|
||||
}
|
||||
.description {
|
||||
margin: 0;
|
||||
margin-bottom: var(--ha-space-4);
|
||||
}
|
||||
ha-textarea {
|
||||
width: 100%;
|
||||
|
||||
@@ -18,7 +18,7 @@ import "./events-list";
|
||||
class HaPanelDevEvent extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
@property({ type: Boolean, reflect: true }) public narrow = false;
|
||||
|
||||
@state() private _eventType = "";
|
||||
|
||||
@@ -94,6 +94,7 @@ class HaPanelDevEvent extends LitElement {
|
||||
|
||||
<event-subscribe-card
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.selectedEventType=${this._selectedEventType}
|
||||
></event-subscribe-card>
|
||||
</div>
|
||||
@@ -158,6 +159,8 @@ class HaPanelDevEvent extends LitElement {
|
||||
padding: var(--ha-space-4);
|
||||
max-width: 1200px;
|
||||
margin: auto;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:host {
|
||||
@@ -165,10 +168,26 @@ class HaPanelDevEvent extends LitElement {
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:host([narrow]) {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
:host([narrow]) .content {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.flex {
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
:host([narrow]) .flex {
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.inputs {
|
||||
@@ -180,11 +199,19 @@ class HaPanelDevEvent extends LitElement {
|
||||
}
|
||||
|
||||
event-subscribe-card {
|
||||
display: block;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
margin-top: var(--ha-space-4);
|
||||
direction: var(--direction);
|
||||
}
|
||||
|
||||
:host([narrow]) event-subscribe-card {
|
||||
flex: none;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,39 @@
|
||||
import {
|
||||
mdiChevronDoubleLeft,
|
||||
mdiChevronDoubleRight,
|
||||
mdiChevronLeft,
|
||||
mdiChevronRight,
|
||||
mdiInformationOutline,
|
||||
} from "@mdi/js";
|
||||
import type { HassEvent } from "home-assistant-js-websocket";
|
||||
import type { TemplateResult, PropertyValues } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import { formatTime } from "../../../../common/datetime/format_time";
|
||||
import { formatTimeWithSeconds } from "../../../../common/datetime/format_time";
|
||||
import "../../../../components/ha-alert";
|
||||
import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-icon-button";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import "../../../../components/ha-tooltip";
|
||||
import "../../../../components/ha-yaml-editor";
|
||||
import "../../../../components/input/ha-input";
|
||||
import type { HaInput } from "../../../../components/input/ha-input";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
|
||||
const MAX_BUFFERED_EVENTS = 100;
|
||||
|
||||
interface SubscribedEvent {
|
||||
id: number;
|
||||
event: HassEvent;
|
||||
}
|
||||
|
||||
@customElement("event-subscribe-card")
|
||||
class EventSubscribeCard extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public narrow = false;
|
||||
|
||||
@property({ attribute: false }) public selectedEventType = "";
|
||||
|
||||
@state() private _eventType = "";
|
||||
@@ -24,13 +42,12 @@ class EventSubscribeCard extends LitElement {
|
||||
|
||||
@state() private _eventFilter = "";
|
||||
|
||||
@state() private _events: {
|
||||
id: number;
|
||||
event: HassEvent;
|
||||
}[] = [];
|
||||
@state() private _events: SubscribedEvent[] = [];
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
@state() private _viewedEventId?: number;
|
||||
|
||||
private _eventCount = 0;
|
||||
|
||||
@state() _ignoredEventsCount = 0;
|
||||
@@ -113,43 +130,161 @@ class EventSubscribeCard extends LitElement {
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<div class="events">
|
||||
${repeat(
|
||||
this._events,
|
||||
(event) => event.id,
|
||||
(event) => html`
|
||||
<div class="event">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.event_fired",
|
||||
{ name: event.id }
|
||||
)}
|
||||
${formatTime(
|
||||
new Date(event.event.time_fired),
|
||||
this.hass!.locale,
|
||||
this.hass!.config
|
||||
)}:
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.defaultValue=${event.event}
|
||||
read-only
|
||||
></ha-yaml-editor>
|
||||
</div>
|
||||
`
|
||||
${this._renderEventsCard()}
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderEventsCard(): TemplateResult {
|
||||
if (!this._events.length) {
|
||||
const message = this._subscribed
|
||||
? this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.waiting_for_events"
|
||||
)
|
||||
: this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.subscribe_prompt"
|
||||
);
|
||||
return html`
|
||||
<ha-card class="events-card">
|
||||
<div class="empty-state">${message}</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
const bufferTotal = this._events.length;
|
||||
const index = this._resolveViewedIndex();
|
||||
const event = this._events[index];
|
||||
const position = event.id + 1;
|
||||
|
||||
const bufferPosition = bufferTotal - index;
|
||||
const atNewest = index === 0;
|
||||
|
||||
const hasRolledOver = this._events[bufferTotal - 1].id > 0;
|
||||
|
||||
return html`
|
||||
<ha-card class="events-card">
|
||||
<div class="events-toolbar">
|
||||
<ha-icon-button
|
||||
.path=${mdiChevronDoubleLeft}
|
||||
.disabled=${index >= bufferTotal - 1}
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.oldest_event"
|
||||
)}
|
||||
@click=${this._showOldest}
|
||||
></ha-icon-button>
|
||||
<ha-icon-button
|
||||
.path=${mdiChevronLeft}
|
||||
.disabled=${index >= bufferTotal - 1}
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.older_event"
|
||||
)}
|
||||
@click=${this._showOlder}
|
||||
></ha-icon-button>
|
||||
<div class="event-info">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.event_fired",
|
||||
{
|
||||
name: position,
|
||||
time: formatTimeWithSeconds(
|
||||
new Date(event.event.time_fired),
|
||||
this.hass!.locale,
|
||||
this.hass!.config
|
||||
),
|
||||
}
|
||||
)}
|
||||
<span class="counter">(${bufferPosition} / ${bufferTotal})</span>
|
||||
${hasRolledOver
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
id="buffer-info"
|
||||
class="buffer-info"
|
||||
.path=${mdiInformationOutline}
|
||||
></ha-svg-icon>
|
||||
<ha-tooltip for="buffer-info" placement="bottom">
|
||||
<span class="buffer-tooltip">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.buffer_disclaimer",
|
||||
{ count: MAX_BUFFERED_EVENTS }
|
||||
)}
|
||||
</span>
|
||||
</ha-tooltip>
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
<ha-icon-button
|
||||
.path=${mdiChevronRight}
|
||||
.disabled=${atNewest}
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.newer_event"
|
||||
)}
|
||||
@click=${this._showNewer}
|
||||
></ha-icon-button>
|
||||
<ha-icon-button
|
||||
.path=${mdiChevronDoubleRight}
|
||||
.disabled=${atNewest}
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.newest_event"
|
||||
)}
|
||||
@click=${this._showNewest}
|
||||
></ha-icon-button>
|
||||
</div>
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.value=${event.event}
|
||||
auto-update
|
||||
read-only
|
||||
></ha-yaml-editor>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: InputEvent): void {
|
||||
private _resolveViewedIndex(): number {
|
||||
if (this._viewedEventId === undefined) {
|
||||
return 0;
|
||||
}
|
||||
const found = this._events.findIndex((e) => e.id === this._viewedEventId);
|
||||
|
||||
// Fall back to the oldest available event when the viewed one has aged out.
|
||||
return found === -1 ? this._events.length - 1 : found;
|
||||
}
|
||||
|
||||
private _showOldest() {
|
||||
if (!this._events.length) {
|
||||
return;
|
||||
}
|
||||
this._viewedEventId = this._events[this._events.length - 1].id;
|
||||
}
|
||||
|
||||
private _showOlder() {
|
||||
if (!this._events.length) {
|
||||
return;
|
||||
}
|
||||
const next = Math.min(
|
||||
this._resolveViewedIndex() + 1,
|
||||
this._events.length - 1
|
||||
);
|
||||
this._viewedEventId = this._events[next].id;
|
||||
}
|
||||
|
||||
private _showNewest() {
|
||||
if (!this._events.length) {
|
||||
return;
|
||||
}
|
||||
this._viewedEventId = this._events[0].id;
|
||||
}
|
||||
|
||||
private _showNewer() {
|
||||
if (!this._events.length) {
|
||||
return;
|
||||
}
|
||||
const next = Math.max(this._resolveViewedIndex() - 1, 0);
|
||||
this._viewedEventId = this._events[next].id;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: InputEvent) {
|
||||
this._eventType = (ev.target as HaInput).value ?? "";
|
||||
this._error = undefined;
|
||||
}
|
||||
|
||||
private _filterChanged(ev: InputEvent): void {
|
||||
private _filterChanged(ev: InputEvent) {
|
||||
this._eventFilter = (ev.target as HaInput).value ?? "";
|
||||
}
|
||||
|
||||
@@ -160,7 +295,7 @@ class EventSubscribeCard extends LitElement {
|
||||
|
||||
const searchStr = this._eventFilter;
|
||||
|
||||
function visit(node) {
|
||||
function visit(node: unknown) {
|
||||
// Handle primitives directly
|
||||
if (node === null || typeof node !== "object") {
|
||||
return String(node).includes(searchStr);
|
||||
@@ -203,55 +338,116 @@ class EventSubscribeCard extends LitElement {
|
||||
return;
|
||||
}
|
||||
const tail =
|
||||
this._events.length > 30
|
||||
? this._events.slice(0, 29)
|
||||
this._events.length >= MAX_BUFFERED_EVENTS
|
||||
? this._events.slice(0, MAX_BUFFERED_EVENTS - 1)
|
||||
: this._events;
|
||||
const id = this._eventCount++;
|
||||
this._events = [
|
||||
{
|
||||
event,
|
||||
id: this._eventCount++,
|
||||
id,
|
||||
},
|
||||
...tail,
|
||||
];
|
||||
if (this._viewedEventId === undefined) {
|
||||
this._viewedEventId = id;
|
||||
}
|
||||
}, this._eventType);
|
||||
} catch (error: any) {
|
||||
} catch (error) {
|
||||
this._error = this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.subscribe_failed",
|
||||
{ error: error.message || "Unknown error" }
|
||||
{
|
||||
error:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: this.hass!.localize(
|
||||
"ui.panel.config.developer-tools.tabs.events.unknown_error"
|
||||
),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _clearEvents(): void {
|
||||
private _clearEvents() {
|
||||
this._events = [];
|
||||
this._eventCount = 0;
|
||||
this._ignoredEventsCount = 0;
|
||||
this._error = undefined;
|
||||
this._viewedEventId = undefined;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
ha-input {
|
||||
margin-bottom: var(--ha-space-2);
|
||||
}
|
||||
.error-message {
|
||||
margin-top: var(--ha-space-2);
|
||||
}
|
||||
.event {
|
||||
border-top: 1px solid var(--divider-color);
|
||||
padding-top: var(--ha-space-2);
|
||||
padding-bottom: var(--ha-space-2);
|
||||
margin: var(--ha-space-4) 0;
|
||||
}
|
||||
.event:last-child {
|
||||
border-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
pre {
|
||||
font-family: var(--ha-font-family-code);
|
||||
}
|
||||
ha-card {
|
||||
margin-bottom: var(--ha-space-1);
|
||||
margin-bottom: var(--ha-space-2);
|
||||
}
|
||||
.events-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 620px;
|
||||
padding: var(--ha-space-2);
|
||||
}
|
||||
:host([narrow]) .events-card {
|
||||
height: auto;
|
||||
min-height: 360px;
|
||||
}
|
||||
.events-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--ha-space-2);
|
||||
}
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: var(--ha-space-8);
|
||||
color: var(--primary-text-color);
|
||||
text-align: center;
|
||||
font-size: var(--ha-font-size-xl);
|
||||
line-height: var(--ha-line-height-normal);
|
||||
}
|
||||
.event-info {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: var(--ha-font-size-m);
|
||||
}
|
||||
.counter {
|
||||
color: var(--secondary-text-color);
|
||||
margin-left: var(--ha-space-2);
|
||||
}
|
||||
.buffer-info {
|
||||
color: var(--secondary-text-color);
|
||||
margin-left: var(--ha-space-1);
|
||||
vertical-align: middle;
|
||||
--mdc-icon-size: 16px;
|
||||
}
|
||||
.buffer-tooltip {
|
||||
white-space: pre-line;
|
||||
display: block;
|
||||
max-width: 320px;
|
||||
}
|
||||
ha-yaml-editor {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
flex: 1;
|
||||
margin-top: var(--ha-space-2);
|
||||
--code-mirror-height: 100%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -146,6 +146,7 @@ interface HelperItem {
|
||||
category: string | undefined;
|
||||
area?: string;
|
||||
label_entries: LabelRegistryEntry[];
|
||||
labels: string[]; // search only
|
||||
assistants: string[];
|
||||
assistants_sortable_key: string | undefined;
|
||||
disabled?: boolean;
|
||||
@@ -552,6 +553,9 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
||||
const entityRegEntry =
|
||||
entityRegistryByEntityId(entityReg)[item.entity_id];
|
||||
const labels = labelReg && entityRegEntry?.labels;
|
||||
const label_entries = (labels || []).map(
|
||||
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
|
||||
);
|
||||
const category = entityRegEntry?.categories.helpers;
|
||||
const deviceId = entityRegEntry?.device_id;
|
||||
const areaId =
|
||||
@@ -572,9 +576,8 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
||||
`ui.panel.config.helpers.types.${item.type}` as LocalizeKeys
|
||||
) ||
|
||||
item.type,
|
||||
label_entries: (labels || []).map(
|
||||
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
|
||||
),
|
||||
label_entries,
|
||||
labels: label_entries.map((lbl) => lbl.name),
|
||||
category: category
|
||||
? categoryReg?.find((cat) => cat.category_id === category)?.name
|
||||
: undefined,
|
||||
|
||||
@@ -122,6 +122,7 @@ type SceneItem = SceneEntity & {
|
||||
area: string | undefined;
|
||||
category: string | undefined;
|
||||
label_entries: LabelRegistryEntry[];
|
||||
labels: string[]; // search only
|
||||
assistants: string[];
|
||||
assistants_sortable_key: string | undefined;
|
||||
editable: boolean;
|
||||
@@ -239,6 +240,9 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
||||
);
|
||||
const category = entityRegEntry?.categories.scene;
|
||||
const labels = labelReg && entityRegEntry?.labels;
|
||||
const label_entries = (labels || []).map(
|
||||
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
|
||||
);
|
||||
const assistants = getEntityVoiceAssistantsIds(
|
||||
entityReg,
|
||||
scene.entity_id
|
||||
@@ -252,9 +256,8 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
||||
category: category
|
||||
? categoryReg?.find((cat) => cat.category_id === category)?.name
|
||||
: undefined,
|
||||
label_entries: (labels || []).map(
|
||||
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
|
||||
),
|
||||
label_entries,
|
||||
labels: label_entries.map((lbl) => lbl.name),
|
||||
assistants,
|
||||
assistants_sortable_key: getAssistantsSortableKey(assistants),
|
||||
selectable: entityRegEntry !== undefined,
|
||||
|
||||
@@ -329,7 +329,6 @@ export class HaSceneEditor extends PreventUnsavedMixin(
|
||||
.defaultValue=${this._config}
|
||||
@value-changed=${this._yamlChanged}
|
||||
@editor-save=${this._saveScene}
|
||||
.showErrors=${false}
|
||||
disable-fullscreen
|
||||
></ha-yaml-editor>`;
|
||||
}
|
||||
|
||||
@@ -464,7 +464,6 @@ export class HaScriptEditor extends SubscribeMixin(
|
||||
disable-fullscreen
|
||||
@value-changed=${this._yamlChanged}
|
||||
@editor-save=${this._handleSaveScript}
|
||||
.showErrors=${false}
|
||||
></ha-yaml-editor>
|
||||
<ha-button
|
||||
slot="fab"
|
||||
|
||||
@@ -127,6 +127,7 @@ type ScriptItem = ScriptEntity & {
|
||||
last_triggered: string | undefined;
|
||||
category: string | undefined;
|
||||
label_entries: LabelRegistryEntry[];
|
||||
labels: string[]; // search only
|
||||
assistants: string[];
|
||||
assistants_sortable_key: string | undefined;
|
||||
};
|
||||
@@ -245,6 +246,9 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
||||
);
|
||||
const category = entityRegEntry?.categories.script;
|
||||
const labels = labelReg && entityRegEntry?.labels;
|
||||
const label_entries = (labels || []).map(
|
||||
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
|
||||
);
|
||||
const assistants = getEntityVoiceAssistantsIds(
|
||||
entityReg,
|
||||
script.entity_id
|
||||
@@ -259,9 +263,8 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
||||
category: category
|
||||
? categoryReg?.find((cat) => cat.category_id === category)?.name
|
||||
: undefined,
|
||||
label_entries: (labels || []).map(
|
||||
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
|
||||
),
|
||||
label_entries,
|
||||
labels: label_entries.map((lbl) => lbl.name),
|
||||
assistants,
|
||||
assistants_sortable_key: getAssistantsSortableKey(assistants),
|
||||
selectable: entityRegEntry !== undefined,
|
||||
|
||||
@@ -35,7 +35,10 @@ import {
|
||||
HOME_SUMMARIES_ICONS,
|
||||
type HomeSummary,
|
||||
} from "../strategies/home/helpers/home-summaries";
|
||||
import { filterNeedsAttentionEntities } from "../../maintenance/strategies/maintenance-view-strategy";
|
||||
import {
|
||||
filterLowBatteryEntities,
|
||||
filterUnavailableBatteryEntities,
|
||||
} from "../../maintenance/strategies/maintenance-view-strategy";
|
||||
import type { LovelaceCard, LovelaceGridOptions } from "../types";
|
||||
import { tileCardStyle } from "./tile/tile-card-style";
|
||||
import type { HomeSummaryCard } from "./types";
|
||||
@@ -258,19 +261,48 @@ export class HuiHomeSummaryCard
|
||||
maintenanceFilters
|
||||
);
|
||||
|
||||
const needsAttentionEntities = filterNeedsAttentionEntities(
|
||||
const lowBatteryEntities = filterLowBatteryEntities(
|
||||
this.hass!,
|
||||
maintenanceEntities
|
||||
);
|
||||
|
||||
if (needsAttentionEntities.length > 0) {
|
||||
return this.hass.localize(
|
||||
"ui.card.home-summary.count_maintenance_issues",
|
||||
{
|
||||
count: needsAttentionEntities.length,
|
||||
}
|
||||
);
|
||||
const unavailableBatteryEntities = filterUnavailableBatteryEntities(
|
||||
this.hass!,
|
||||
maintenanceEntities
|
||||
);
|
||||
|
||||
const lowBatteryText =
|
||||
lowBatteryEntities.length > 0
|
||||
? this.hass.localize(
|
||||
"ui.card.home-summary.count_maintenance_low_battery_issues",
|
||||
{
|
||||
count: lowBatteryEntities.length,
|
||||
}
|
||||
)
|
||||
: undefined;
|
||||
|
||||
const unavailableText =
|
||||
unavailableBatteryEntities.length > 0
|
||||
? this.hass.localize(
|
||||
"ui.card.home-summary.count_maintenance_issues_unavailable_battery_entities",
|
||||
{
|
||||
count: unavailableBatteryEntities.length,
|
||||
}
|
||||
)
|
||||
: undefined;
|
||||
|
||||
if (lowBatteryText && unavailableText) {
|
||||
return `${lowBatteryText}, ${unavailableText}`;
|
||||
}
|
||||
|
||||
if (lowBatteryText) {
|
||||
return lowBatteryText;
|
||||
}
|
||||
|
||||
if (unavailableText) {
|
||||
return unavailableText;
|
||||
}
|
||||
|
||||
return this.hass.localize("ui.card.home-summary.all_maintenance_good");
|
||||
}
|
||||
case "energy": {
|
||||
|
||||
@@ -12,7 +12,7 @@ import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||
import { handleAction } from "../common/handle-action";
|
||||
import { hasAction } from "../common/has-action";
|
||||
import { hasAction, hasAnyAction } from "../common/has-action";
|
||||
import { hasConfigChanged } from "../common/has-changed";
|
||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||
import type { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
@@ -52,10 +52,17 @@ export class HuiPictureCard extends LitElement implements LovelaceCard {
|
||||
throw new Error("Image required");
|
||||
}
|
||||
|
||||
this._config = {
|
||||
tap_action: { action: "more-info" },
|
||||
...config,
|
||||
};
|
||||
if (config.image_entity) {
|
||||
this._config = {
|
||||
tap_action: { action: "more-info" },
|
||||
...config,
|
||||
};
|
||||
} else {
|
||||
this._config = {
|
||||
tap_action: { action: "none" },
|
||||
...config,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
@@ -167,6 +174,11 @@ export class HuiPictureCard extends LitElement implements LovelaceCard {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const clickable = Boolean(
|
||||
(this._config.image_entity && !this._config.tap_action) ||
|
||||
hasAnyAction(this._config)
|
||||
);
|
||||
|
||||
return html`
|
||||
<ha-card
|
||||
@action=${this._handleAction}
|
||||
@@ -180,15 +192,7 @@ export class HuiPictureCard extends LitElement implements LovelaceCard {
|
||||
: undefined
|
||||
)}
|
||||
class=${classMap({
|
||||
clickable: Boolean(
|
||||
(this._config.image_entity && !this._config.tap_action) ||
|
||||
(this._config.tap_action &&
|
||||
this._config.tap_action.action !== "none") ||
|
||||
(this._config.hold_action &&
|
||||
this._config.hold_action.action !== "none") ||
|
||||
(this._config.double_tap_action &&
|
||||
this._config.double_tap_action.action !== "none")
|
||||
),
|
||||
clickable,
|
||||
})}
|
||||
>
|
||||
<img
|
||||
|
||||
@@ -419,6 +419,7 @@ export class HuiDialogEditBadge
|
||||
.content {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
gap: var(--ha-space-3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -419,6 +419,7 @@ export class HuiDialogEditCard
|
||||
.content {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
gap: var(--ha-space-3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,9 +68,7 @@ export class HuiPictureCardEditor
|
||||
{
|
||||
name: "tap_action",
|
||||
selector: {
|
||||
ui_action: {
|
||||
default_action: "more-info",
|
||||
},
|
||||
ui_action: {},
|
||||
},
|
||||
context: ACTION_RELATED_CONTEXT,
|
||||
},
|
||||
|
||||
@@ -5,7 +5,6 @@ import { cache } from "lit/directives/cache";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { handleStructError } from "../../../common/structs/handle-errors";
|
||||
import { debounce } from "../../../common/util/debounce";
|
||||
import { deepEqual } from "../../../common/util/deep-equal";
|
||||
import "../../../components/ha-alert";
|
||||
import "../../../components/ha-spinner";
|
||||
@@ -71,11 +70,6 @@ export abstract class HuiElementEditor<
|
||||
// Error: Configuration broken - do not save
|
||||
@state() private _errors?: string[];
|
||||
|
||||
// Error from unparseable YAML, but don't show it immediately to prevent showing immediately on every keystroke
|
||||
@state() private _pendingYamlError?: string;
|
||||
|
||||
@state() private _yamlError = false;
|
||||
|
||||
// Warning: GUI editor can't handle configuration - ok to save
|
||||
@state() private _warnings?: string[];
|
||||
|
||||
@@ -83,6 +77,8 @@ export abstract class HuiElementEditor<
|
||||
|
||||
@state() private _loading = false;
|
||||
|
||||
@state() private _yamlError = false;
|
||||
|
||||
@query("ha-yaml-editor") _yamlEditor?: HaYamlEditor;
|
||||
|
||||
private _loadCount = 0;
|
||||
@@ -105,7 +101,7 @@ export abstract class HuiElementEditor<
|
||||
}
|
||||
|
||||
private _setConfig(): void {
|
||||
if (!this._errors) {
|
||||
if (!this._errors && !this._yamlError) {
|
||||
try {
|
||||
this._updateConfigElement();
|
||||
} catch (err: any) {
|
||||
@@ -131,7 +127,9 @@ export abstract class HuiElementEditor<
|
||||
}
|
||||
|
||||
public get hasError(): boolean {
|
||||
return this._errors !== undefined && this._errors.length > 0;
|
||||
return (
|
||||
this._yamlError || (this._errors !== undefined && this._errors.length > 0)
|
||||
);
|
||||
}
|
||||
|
||||
public get GUImode(): boolean {
|
||||
@@ -251,10 +249,8 @@ export abstract class HuiElementEditor<
|
||||
.hass=${this.hass}
|
||||
.inDialog=${this.inDialog}
|
||||
@value-changed=${this._handleYAMLChanged}
|
||||
@blur=${this._onBlurYaml}
|
||||
@keydown=${this._ignoreKeydown}
|
||||
dir="ltr"
|
||||
.showErrors=${false}
|
||||
></ha-yaml-editor>
|
||||
</div>
|
||||
`}
|
||||
@@ -274,7 +270,7 @@ export abstract class HuiElementEditor<
|
||||
</ha-alert>
|
||||
`
|
||||
: nothing}
|
||||
${this.hasError
|
||||
${this._errors?.length
|
||||
? html`
|
||||
<ha-alert
|
||||
alert-type="error"
|
||||
@@ -283,7 +279,7 @@ export abstract class HuiElementEditor<
|
||||
)}
|
||||
>
|
||||
<ul>
|
||||
${this._errors!.map((error) => html`<li>${error}</li>`)}
|
||||
${this._errors.map((error) => html`<li>${error}</li>`)}
|
||||
</ul>
|
||||
</ha-alert>
|
||||
`
|
||||
@@ -339,40 +335,14 @@ export abstract class HuiElementEditor<
|
||||
|
||||
private _handleYAMLChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
const config = ev.detail.value;
|
||||
if (ev.detail.isValid) {
|
||||
this._config = config;
|
||||
this._config = ev.detail.value;
|
||||
this._errors = undefined;
|
||||
this._pendingYamlError = undefined;
|
||||
this._yamlError = false;
|
||||
this._debounceYamlError.cancel();
|
||||
this._setConfig();
|
||||
} else if (this._yamlError) {
|
||||
// If we're already showing a yaml error, don't bother to debounce, just update immediately.
|
||||
this._errors = [ev.detail.errorMsg];
|
||||
} else {
|
||||
this._pendingYamlError = ev.detail.errorMsg;
|
||||
this._debounceYamlError();
|
||||
}
|
||||
}
|
||||
|
||||
private _debounceYamlError = debounce(() => {
|
||||
if (this._pendingYamlError) {
|
||||
this._yamlError = true;
|
||||
this._errors = [this._pendingYamlError];
|
||||
this._pendingYamlError = undefined;
|
||||
this._setConfig();
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
private _onBlurYaml() {
|
||||
this._debounceYamlError.cancel();
|
||||
if (this._pendingYamlError) {
|
||||
this._yamlError = true;
|
||||
this._errors = [this._pendingYamlError];
|
||||
this._pendingYamlError = undefined;
|
||||
this._setConfig();
|
||||
}
|
||||
this._setConfig();
|
||||
}
|
||||
|
||||
protected async unloadConfigElement(): Promise<void> {
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||
import { handleAction } from "../common/handle-action";
|
||||
import { hasAction } from "../common/has-action";
|
||||
import { hasAction, hasAnyAction } from "../common/has-action";
|
||||
import type { LovelaceHeaderFooter } from "../types";
|
||||
import type { PictureHeaderFooterConfig } from "./types";
|
||||
|
||||
@@ -56,9 +56,7 @@ export class HuiPictureHeaderFooter
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const clickable = Boolean(
|
||||
this._config.tap_action || this._config.hold_action
|
||||
);
|
||||
const clickable = hasAnyAction(this._config);
|
||||
|
||||
return html`
|
||||
<img
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { undoDepth } from "@codemirror/commands";
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import { dump, load } from "js-yaml";
|
||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
@@ -8,8 +7,8 @@ import { classMap } from "lit/directives/class-map";
|
||||
import { array, assert, object, optional, string, type } from "superstruct";
|
||||
import { deepEqual } from "../../common/util/deep-equal";
|
||||
import "../../components/ha-button";
|
||||
import "../../components/ha-code-editor";
|
||||
import type { HaCodeEditor } from "../../components/ha-code-editor";
|
||||
import "../../components/ha-yaml-editor";
|
||||
import type { HaYamlEditor } from "../../components/ha-yaml-editor";
|
||||
import "../../components/ha-icon-button";
|
||||
import "../../components/ha-top-app-bar-fixed";
|
||||
import type { LovelaceRawConfig } from "../../data/lovelace/config/types";
|
||||
@@ -47,6 +46,10 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
|
||||
@state() private _changed?: boolean;
|
||||
|
||||
private _config?: LovelaceRawConfig;
|
||||
|
||||
private _yamlError?: string;
|
||||
|
||||
protected render(): TemplateResult | undefined {
|
||||
return html`
|
||||
<ha-top-app-bar-fixed .narrow=${this.narrow}>
|
||||
@@ -81,18 +84,14 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
)}</ha-button
|
||||
>
|
||||
<div class="content">
|
||||
<ha-code-editor
|
||||
mode="yaml"
|
||||
<ha-yaml-editor
|
||||
autofocus
|
||||
autocomplete-entities
|
||||
autocomplete-icons
|
||||
.hass=${this.hass}
|
||||
@value-changed=${this._yamlChanged}
|
||||
@editor-save=${this._handleSave}
|
||||
disable-fullscreen
|
||||
dir="ltr"
|
||||
>
|
||||
</ha-code-editor>
|
||||
</ha-yaml-editor>
|
||||
</div>
|
||||
</ha-top-app-bar-fixed>
|
||||
`;
|
||||
@@ -100,7 +99,7 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
|
||||
protected firstUpdated(changedProps: PropertyValues<this>) {
|
||||
super.firstUpdated(changedProps);
|
||||
this.yamlEditor.value = dump(this.lovelace!.rawConfig);
|
||||
this.yamlEditor.setValue(this.lovelace!.rawConfig);
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues<this>) {
|
||||
@@ -112,7 +111,7 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
oldLovelace.rawConfig !== this.lovelace.rawConfig &&
|
||||
!deepEqual(oldLovelace.rawConfig, this.lovelace.rawConfig)
|
||||
) {
|
||||
this.yamlEditor.value = dump(this.lovelace!.rawConfig);
|
||||
this.yamlEditor.setValue(this.lovelace!.rawConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +136,7 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
font-size: var(--ha-font-size-l);
|
||||
}
|
||||
|
||||
ha-code-editor {
|
||||
ha-yaml-editor {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -154,7 +153,9 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
];
|
||||
}
|
||||
|
||||
private _yamlChanged() {
|
||||
private _yamlChanged(ev: CustomEvent) {
|
||||
this._config = ev.detail.isValid ? ev.detail.value : undefined;
|
||||
this._yamlError = ev.detail.errorMsg;
|
||||
this._changed = undoDepth(this.yamlEditor.codemirror!.state) > 0;
|
||||
if (this._changed && !window.onbeforeunload) {
|
||||
window.onbeforeunload = () => true;
|
||||
@@ -204,9 +205,7 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
private async _handleSave() {
|
||||
this._saving = true;
|
||||
|
||||
const value = this.yamlEditor.value;
|
||||
|
||||
if (!value) {
|
||||
if (!this.yamlEditor.yaml) {
|
||||
showConfirmationDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.lovelace.editor.raw_editor.confirm_reset_config_title"
|
||||
@@ -222,6 +221,14 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._yamlError) {
|
||||
showAlertDialog(this, {
|
||||
text: this._yamlError,
|
||||
});
|
||||
this._saving = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.yamlEditor.hasComments) {
|
||||
if (
|
||||
!confirm(
|
||||
@@ -234,19 +241,8 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
let config: LovelaceRawConfig;
|
||||
try {
|
||||
config = load(value) as LovelaceRawConfig;
|
||||
} catch (err: any) {
|
||||
showAlertDialog(this, {
|
||||
text: this.hass.localize(
|
||||
"ui.panel.lovelace.editor.raw_editor.error_parse_yaml",
|
||||
{ error: err }
|
||||
),
|
||||
});
|
||||
this._saving = false;
|
||||
return;
|
||||
}
|
||||
const config: LovelaceRawConfig = this._config!;
|
||||
|
||||
try {
|
||||
if (isStrategyDashboard(config)) {
|
||||
assert(config, strategyStruct);
|
||||
@@ -285,8 +281,8 @@ class LovelaceFullConfigEditor extends LitElement {
|
||||
this._saving = false;
|
||||
}
|
||||
|
||||
private get yamlEditor(): HaCodeEditor {
|
||||
return this.shadowRoot!.querySelector("ha-code-editor")! as HaCodeEditor;
|
||||
private get yamlEditor(): HaYamlEditor {
|
||||
return this.shadowRoot!.querySelector("ha-yaml-editor")! as HaYamlEditor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -409,6 +409,8 @@ class HUIRoot extends LitElement {
|
||||
slot="actionItems"
|
||||
.id="button-${index}"
|
||||
.path=${item.icon}
|
||||
.label=${label}
|
||||
hide-title
|
||||
@click=${item.buttonAction}
|
||||
></ha-icon-button>
|
||||
<ha-tooltip placement="bottom" .for="button-${index}">
|
||||
|
||||
@@ -31,7 +31,7 @@ export const maintenanceEntityFilters: EntityFilter[] = [
|
||||
|
||||
const LOW_BATTERY_THRESHOLD = 20;
|
||||
|
||||
export const filterNeedsAttentionEntities = (
|
||||
export const filterLowBatteryEntities = (
|
||||
hass: HomeAssistant,
|
||||
entityIds: string[]
|
||||
): string[] =>
|
||||
@@ -40,6 +40,14 @@ export const filterNeedsAttentionEntities = (
|
||||
return !isNaN(stateValue) && stateValue <= LOW_BATTERY_THRESHOLD;
|
||||
});
|
||||
|
||||
export const filterUnavailableBatteryEntities = (
|
||||
hass: HomeAssistant,
|
||||
entityIds: string[]
|
||||
): string[] =>
|
||||
entityIds.filter((entityId) => {
|
||||
return hass.states[entityId]?.state === "unavailable";
|
||||
});
|
||||
|
||||
const computeBatteryTileCard = (entityId: string): TileCardConfig => ({
|
||||
type: "tile",
|
||||
entity: entityId,
|
||||
|
||||
@@ -37,6 +37,7 @@ export {
|
||||
search,
|
||||
searchKeymap,
|
||||
} from "@codemirror/search";
|
||||
export { lintGutter, lintKeymap, setDiagnostics } from "@codemirror/lint";
|
||||
export { EditorState } from "@codemirror/state";
|
||||
export {
|
||||
crosshairCursor,
|
||||
@@ -80,6 +81,7 @@ export { closePercentBrace };
|
||||
export const langCompartment = new Compartment();
|
||||
export const readonlyCompartment = new Compartment();
|
||||
export const linewrapCompartment = new Compartment();
|
||||
export const yamlLintCompartment = new Compartment();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// YAML scalar type highlighter
|
||||
@@ -369,6 +371,20 @@ export const haTheme = EditorView.theme({
|
||||
paddingRight: "0",
|
||||
},
|
||||
".cm-gutterElement.lineNumber": { color: "inherit" },
|
||||
|
||||
// Lint gutter
|
||||
".cm-lint-marker-error": { color: "var(--error-color)" },
|
||||
".cm-lint-marker-warning": { color: "var(--warning-color)" },
|
||||
".cm-lint-marker-info": { color: "var(--info-color, var(--primary-color))" },
|
||||
".cm-diagnostic": {
|
||||
fontFamily: "var(--mdc-typography-font-family, var(--ha-font-family-body))",
|
||||
},
|
||||
".cm-diagnostic.cm-diagnostic-error": {
|
||||
borderLeft: "3px solid var(--error-color)",
|
||||
},
|
||||
".cm-diagnostic.cm-diagnostic-warning": {
|
||||
borderLeft: "3px solid var(--warning-color)",
|
||||
},
|
||||
});
|
||||
|
||||
const haHighlightStyle = HighlightStyle.define([
|
||||
|
||||
@@ -218,7 +218,8 @@
|
||||
"all_secure": "All secure",
|
||||
"no_media_playing": "No media playing",
|
||||
"count_media_playing": "{count} {count, plural,\n one {playing}\n other {playing}\n}",
|
||||
"count_maintenance_issues": "{count} {count, plural,\n one {issue}\n other {issues}\n}",
|
||||
"count_maintenance_low_battery_issues": "{count} {count, plural,\n one {low battery}\n other {low batteries}\n}",
|
||||
"count_maintenance_issues_unavailable_battery_entities": "{count} {count, plural,\n one {unavailable device}\n other {unavailable devices}\n}",
|
||||
"all_maintenance_good": "All good",
|
||||
"count_persons_home": "{count} {count, plural,\n one {person}\n other {people}\n} home",
|
||||
"nobody_home": "No one home"
|
||||
@@ -491,6 +492,11 @@
|
||||
"markdown": "Markdown"
|
||||
},
|
||||
"components": {
|
||||
"input": {
|
||||
"clear": "Clear",
|
||||
"show_password": "Show password",
|
||||
"hide_password": "Hide password"
|
||||
},
|
||||
"selectors": {
|
||||
"serial_port": {
|
||||
"enter_manually": "Enter manually",
|
||||
@@ -1427,6 +1433,7 @@
|
||||
},
|
||||
"yaml-editor": {
|
||||
"copy_to_clipboard": "[%key:ui::panel::config::automation::editor::copy_to_clipboard%]",
|
||||
"syntax_error": "YAML syntax error",
|
||||
"error": "Error in parsing YAML: {reason}",
|
||||
"error_location": "line: {line}, column: {column}",
|
||||
"enter_fullscreen": "Enter fullscreen",
|
||||
@@ -3560,7 +3567,7 @@
|
||||
"show_all": "Show all backups"
|
||||
},
|
||||
"settings": {
|
||||
"title": "Backup settings",
|
||||
"title": "Automatic backup",
|
||||
"configure": "Configure backup settings",
|
||||
"schedule": "Automatic backup schedule and retention",
|
||||
"schedule_copies_all": "and keep all backups",
|
||||
@@ -3606,6 +3613,10 @@
|
||||
"sat": "Sa",
|
||||
"sun": "Su"
|
||||
}
|
||||
},
|
||||
"app_update_backup": {
|
||||
"title": "App update backup",
|
||||
"description": "Backup behavior for app updates"
|
||||
}
|
||||
},
|
||||
"backups": {
|
||||
@@ -3617,13 +3628,15 @@
|
||||
"new_backup": "[%key:ui::panel::config::backup::overview::new_backup%]"
|
||||
},
|
||||
"settings": {
|
||||
"header": "Backup settings",
|
||||
"header": "Automatic backups",
|
||||
"menu": {
|
||||
"change_default_location": "Change default action location"
|
||||
},
|
||||
"schedule": {
|
||||
"title": "Automatic backups",
|
||||
"description": "Let Home Assistant take care of your backups by creating a scheduled backup that also removes older backups."
|
||||
"title": "Backup cycle",
|
||||
"description": "Let Home Assistant take care of your backups by creating a scheduled backup that also removes older backups.",
|
||||
"error_load": "Error loading Supervisor update config: {error}",
|
||||
"error_save": "Error saving Supervisor update config: {error}"
|
||||
},
|
||||
"data": {
|
||||
"title": "Backup data"
|
||||
@@ -3651,6 +3664,9 @@
|
||||
"error_save": "Error saving Supervisor update config: {error}"
|
||||
}
|
||||
},
|
||||
"app_update_backups": {
|
||||
"header": "App update backups"
|
||||
},
|
||||
"details": {
|
||||
"header": "Backup",
|
||||
"not_found": "Not found",
|
||||
@@ -3784,7 +3800,7 @@
|
||||
"type": "Event type",
|
||||
"data": "Event data (YAML, optional)",
|
||||
"fire_event": "Fire event",
|
||||
"event_fired": "Event {name} fired",
|
||||
"event_fired": "Event {name} fired at {time}",
|
||||
"active_listeners": "Active listeners",
|
||||
"count_listeners": "({count} {count, plural,\n one {listener}\n other {listeners}\n})",
|
||||
"listen_to_events": "Listen to events",
|
||||
@@ -3798,7 +3814,15 @@
|
||||
"clear_events": "Clear events",
|
||||
"alert_event_type": "Event type is a mandatory field",
|
||||
"notification_event_fired": "Event {type} successfully fired!",
|
||||
"subscribe_failed": "Failed to subscribe to event: {error}"
|
||||
"subscribe_failed": "Failed to subscribe to event: {error}",
|
||||
"unknown_error": "Unknown error",
|
||||
"oldest_event": "Oldest event",
|
||||
"older_event": "Older event",
|
||||
"newer_event": "Newer event",
|
||||
"newest_event": "Newest event",
|
||||
"waiting_for_events": "Waiting for events…",
|
||||
"subscribe_prompt": "Subscribe to an event type above to see events here.",
|
||||
"buffer_disclaimer": "Showing the latest {count} events. Older events are discarded as new ones arrive.\n\nTip: Subscribe to a more specific event type, or use the filter to narrow results."
|
||||
},
|
||||
"actions": {
|
||||
"title": "Actions",
|
||||
@@ -8687,7 +8711,6 @@
|
||||
"confirm_reset_config_text": "Your dashboard will be reset to an empty state. You can start fresh and build your dashboard from scratch.",
|
||||
"confirm_unsaved_changes": "You have unsaved changes, are you sure you want to exit?",
|
||||
"confirm_unsaved_comments": "Your configuration might contain comments, these will not be saved. Do you want to continue?",
|
||||
"error_parse_yaml": "Unable to parse YAML: {error}",
|
||||
"error_invalid_config": "Your configuration is not valid: {error}",
|
||||
"error_save_yaml": "Unable to save YAML: {error}",
|
||||
"resources_moved": "Resources should no longer be added to the dashboard configuration but can be added in the dashboard config panel."
|
||||
|
||||
269
yarn.lock
269
yarn.lock
@@ -18,27 +18,36 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@asamuzakjp/css-color@npm:^5.1.5":
|
||||
version: 5.1.6
|
||||
resolution: "@asamuzakjp/css-color@npm:5.1.6"
|
||||
"@asamuzakjp/css-color@npm:^5.1.11":
|
||||
version: 5.1.11
|
||||
resolution: "@asamuzakjp/css-color@npm:5.1.11"
|
||||
dependencies:
|
||||
"@csstools/css-calc": "npm:^3.1.1"
|
||||
"@csstools/css-color-parser": "npm:^4.0.2"
|
||||
"@asamuzakjp/generational-cache": "npm:^1.0.1"
|
||||
"@csstools/css-calc": "npm:^3.2.0"
|
||||
"@csstools/css-color-parser": "npm:^4.1.0"
|
||||
"@csstools/css-parser-algorithms": "npm:^4.0.0"
|
||||
"@csstools/css-tokenizer": "npm:^4.0.0"
|
||||
checksum: 10/5151369d9369e478e03c0eee0f171b8f86306ebbdf5b352544cd745c360d97343f437bdd0690ff658e47d2876b466bffc8811fcef7f0347cb243c6483a7e95a0
|
||||
checksum: 10/2e337cc94b5a3f9741a27f92b4e4b7dc467a76b1dcf66c40e71808fed71695f10c8cf07c8b13313cbb637154314ca1d8626bb9a045fe94b404b242a390cf3bd3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@asamuzakjp/dom-selector@npm:^7.0.6":
|
||||
version: 7.0.7
|
||||
resolution: "@asamuzakjp/dom-selector@npm:7.0.7"
|
||||
"@asamuzakjp/dom-selector@npm:^7.1.1":
|
||||
version: 7.1.1
|
||||
resolution: "@asamuzakjp/dom-selector@npm:7.1.1"
|
||||
dependencies:
|
||||
"@asamuzakjp/generational-cache": "npm:^1.0.1"
|
||||
"@asamuzakjp/nwsapi": "npm:^2.3.9"
|
||||
bidi-js: "npm:^1.0.3"
|
||||
css-tree: "npm:^3.2.1"
|
||||
is-potential-custom-element-name: "npm:^1.0.1"
|
||||
checksum: 10/18f40def8c775c6008c8fcd75d7d049ff92d99a494929ab2bf742341b348c78cbf4808d29c13b9cd87ca4fd272773cf5aa9e58fee48603c286df48148be8cb67
|
||||
checksum: 10/49a065a64db5f53a3008c231d09606e4b67f509fa20148a67419451c2dc91a421202ed17bfc4bc679ad2f0432d7260720d602c1d5c9c5e165931fff5199c3f12
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@asamuzakjp/generational-cache@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "@asamuzakjp/generational-cache@npm:1.0.1"
|
||||
checksum: 10/e1cf3f1916a334c6153f624982f0eb3d50fa3048435ea5c5b0f441f8f1ab74a0fe992dac214b612d22c0acafad3cd1a1f6b45d99c7b6e3b63cfdf7f6ca5fc144
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1331,7 +1340,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@codemirror/lint@npm:^6.0.0":
|
||||
"@codemirror/lint@npm:6.9.5, @codemirror/lint@npm:^6.0.0":
|
||||
version: 6.9.5
|
||||
resolution: "@codemirror/lint@npm:6.9.5"
|
||||
dependencies:
|
||||
@@ -1381,26 +1390,26 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@csstools/css-calc@npm:^3.1.1":
|
||||
version: 3.1.1
|
||||
resolution: "@csstools/css-calc@npm:3.1.1"
|
||||
"@csstools/css-calc@npm:^3.2.0":
|
||||
version: 3.2.0
|
||||
resolution: "@csstools/css-calc@npm:3.2.0"
|
||||
peerDependencies:
|
||||
"@csstools/css-parser-algorithms": ^4.0.0
|
||||
"@csstools/css-tokenizer": ^4.0.0
|
||||
checksum: 10/faa3aa2736b20757ceafd76e3d2841e8726ec9e7ae78e387684eb462aba73d533ba384039338685c3a52196196300ccdfecb051e59864b1d3b457fe927b7f53b
|
||||
checksum: 10/7eec51a21945a74aa6a407d1e6290d0f4c5d01829a42c01a56ce2055216398540cc3120837b15a0db38601bcb40cf97f1d991fefb3ee9d00d9cec03d67beba4c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@csstools/css-color-parser@npm:^4.0.2":
|
||||
version: 4.0.2
|
||||
resolution: "@csstools/css-color-parser@npm:4.0.2"
|
||||
"@csstools/css-color-parser@npm:^4.1.0":
|
||||
version: 4.1.0
|
||||
resolution: "@csstools/css-color-parser@npm:4.1.0"
|
||||
dependencies:
|
||||
"@csstools/color-helpers": "npm:^6.0.2"
|
||||
"@csstools/css-calc": "npm:^3.1.1"
|
||||
"@csstools/css-calc": "npm:^3.2.0"
|
||||
peerDependencies:
|
||||
"@csstools/css-parser-algorithms": ^4.0.0
|
||||
"@csstools/css-tokenizer": ^4.0.0
|
||||
checksum: 10/6418bfadc8c15d3a65c1e80278df383b542f0437446c0ba21d591dd564bcc19ab0b11243edf62672f4c62cc778f9b386fa4349e9a8d1de2b414148ea8a1ac775
|
||||
checksum: 10/794508011a95ebac3856e67e0333ca4174604d2dfddc101d991f2ebfd52b3c99cd36a08462675c2a07d057ca3787187fcd7eac98bced2eefdd9040b37853426d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1413,15 +1422,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@csstools/css-syntax-patches-for-csstree@npm:^1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "@csstools/css-syntax-patches-for-csstree@npm:1.1.1"
|
||||
"@csstools/css-syntax-patches-for-csstree@npm:^1.1.3":
|
||||
version: 1.1.3
|
||||
resolution: "@csstools/css-syntax-patches-for-csstree@npm:1.1.3"
|
||||
peerDependencies:
|
||||
css-tree: ^3.2.1
|
||||
peerDependenciesMeta:
|
||||
css-tree:
|
||||
optional: true
|
||||
checksum: 10/745ec0f6f7d1c3707af9661d5dcc7e29c12c0416da46e10dda7518c872fef38446d39e13557b3d134e16eb1c78899fa6a712a27fd8ab544813e25a4cd0913cdc
|
||||
checksum: 10/1c91dc03b64ca913eed5064ca0e434da1c0be8def6ce20f932d1db10f9b478ac3830c99a033b0edf75954cf9164c7c267b220ed9faffbc3342bf320870c3bb4b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1848,9 +1857,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@home-assistant/webawesome@npm:3.3.1-ha.1":
|
||||
version: 3.3.1-ha.1
|
||||
resolution: "@home-assistant/webawesome@npm:3.3.1-ha.1"
|
||||
"@home-assistant/webawesome@npm:3.3.1-ha.2":
|
||||
version: 3.3.1-ha.2
|
||||
resolution: "@home-assistant/webawesome@npm:3.3.1-ha.2"
|
||||
dependencies:
|
||||
"@ctrl/tinycolor": "npm:4.1.0"
|
||||
"@floating-ui/dom": "npm:^1.6.13"
|
||||
@@ -1861,7 +1870,7 @@ __metadata:
|
||||
lit: "npm:^3.2.1"
|
||||
nanoid: "npm:^5.1.5"
|
||||
qr-creator: "npm:^1.0.0"
|
||||
checksum: 10/903b7e5e44a9c4afa5120b5fbbf139880889798a82262b10b5c5fa51cd56cd344d8027d69658b15c85efc579688476bbd8e5eff73535f903959bbd9ffd00051f
|
||||
checksum: 10/c66965544bb34eb9d6d131579c31e4c6d21c8c231bdf565db61195da03a995d17e25d6d997875c902d7d2f3020c70434088efb7299b2626d4a7d1343cb33d726
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -4374,105 +4383,105 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/eslint-plugin@npm:8.59.0":
|
||||
version: 8.59.0
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.59.0"
|
||||
"@typescript-eslint/eslint-plugin@npm:8.59.1":
|
||||
version: 8.59.1
|
||||
resolution: "@typescript-eslint/eslint-plugin@npm:8.59.1"
|
||||
dependencies:
|
||||
"@eslint-community/regexpp": "npm:^4.12.2"
|
||||
"@typescript-eslint/scope-manager": "npm:8.59.0"
|
||||
"@typescript-eslint/type-utils": "npm:8.59.0"
|
||||
"@typescript-eslint/utils": "npm:8.59.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.59.0"
|
||||
"@typescript-eslint/scope-manager": "npm:8.59.1"
|
||||
"@typescript-eslint/type-utils": "npm:8.59.1"
|
||||
"@typescript-eslint/utils": "npm:8.59.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.59.1"
|
||||
ignore: "npm:^7.0.5"
|
||||
natural-compare: "npm:^1.4.0"
|
||||
ts-api-utils: "npm:^2.5.0"
|
||||
peerDependencies:
|
||||
"@typescript-eslint/parser": ^8.59.0
|
||||
"@typescript-eslint/parser": ^8.59.1
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/fcf2c85cb37d61854d2c03fa11c66ad58d99f4eee731dd09663f20f0395e642b12edeab2a6c368ac1806505b2071a01de01bc30b9011fa309299836e868a293a
|
||||
checksum: 10/c736ee32211a3751e31151b51dacc8cfa5bf18e086f2a87aba7ee325f7e2fa96d8b9febdbaf4dfa70d14954312b7b9740fbe5d5886b3f8561c4a94a9c7ff7688
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/parser@npm:8.59.0":
|
||||
version: 8.59.0
|
||||
resolution: "@typescript-eslint/parser@npm:8.59.0"
|
||||
"@typescript-eslint/parser@npm:8.59.1":
|
||||
version: 8.59.1
|
||||
resolution: "@typescript-eslint/parser@npm:8.59.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager": "npm:8.59.0"
|
||||
"@typescript-eslint/types": "npm:8.59.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.59.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.59.0"
|
||||
"@typescript-eslint/scope-manager": "npm:8.59.1"
|
||||
"@typescript-eslint/types": "npm:8.59.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.59.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.59.1"
|
||||
debug: "npm:^4.4.3"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/b8990e1b67e6f55aa4884807e6c3b6bd08c58f96ea4f03f19e7aba3fc1b16f040fe58378490de9bd831c804eb48e633e30e5baf291b8e8dd53960424e5751391
|
||||
checksum: 10/b014b485e5ec9c7430a87117271836b86fd80083fe6b1d216167313518f26222f45c0ee3f4cbc0616dbd6335cbde50336d8953ca5ffefecc55b2d896ac7645f9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/project-service@npm:8.59.0":
|
||||
version: 8.59.0
|
||||
resolution: "@typescript-eslint/project-service@npm:8.59.0"
|
||||
"@typescript-eslint/project-service@npm:8.59.1":
|
||||
version: 8.59.1
|
||||
resolution: "@typescript-eslint/project-service@npm:8.59.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/tsconfig-utils": "npm:^8.59.0"
|
||||
"@typescript-eslint/types": "npm:^8.59.0"
|
||||
"@typescript-eslint/tsconfig-utils": "npm:^8.59.1"
|
||||
"@typescript-eslint/types": "npm:^8.59.1"
|
||||
debug: "npm:^4.4.3"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/b842f1e0623c3a679d21d76c7ca38698787681d40f6a9ce93c8983120fb6795a2395907d530e4f8d89b4ac5bc65e71bbfdf2d8060f210c8487cffdae40baea74
|
||||
checksum: 10/dd98f49a407cb21999d31ec527a0f8c2c34422dde9fdb21210d66c3cc3d498d9d3678d95c99d76450af68ce3392692902d9ba044718d6c99122655df7afdc0a7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/scope-manager@npm:8.59.0":
|
||||
version: 8.59.0
|
||||
resolution: "@typescript-eslint/scope-manager@npm:8.59.0"
|
||||
"@typescript-eslint/scope-manager@npm:8.59.1":
|
||||
version: 8.59.1
|
||||
resolution: "@typescript-eslint/scope-manager@npm:8.59.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.59.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.59.0"
|
||||
checksum: 10/8bb1182559e7676120ba12bdac11edea9fb414bd33d379a1902b035b8b4b68d23ad239d845bfe6943b5da13ecd938ea1482c73e8c6ddb4d7e3e0f8e111467e28
|
||||
"@typescript-eslint/types": "npm:8.59.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.59.1"
|
||||
checksum: 10/50c941d1af470d3e67a9bd2247c541a676ae6bb2931440a44458682d61382ba1194ce29d0388dd1e538c5a35d7a542febd9519d8170abe758692d1b6cd196eab
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/tsconfig-utils@npm:8.59.0, @typescript-eslint/tsconfig-utils@npm:^8.59.0":
|
||||
version: 8.59.0
|
||||
resolution: "@typescript-eslint/tsconfig-utils@npm:8.59.0"
|
||||
"@typescript-eslint/tsconfig-utils@npm:8.59.1, @typescript-eslint/tsconfig-utils@npm:^8.59.1":
|
||||
version: 8.59.1
|
||||
resolution: "@typescript-eslint/tsconfig-utils@npm:8.59.1"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/9c094c199be4803d696dbf7cb5cdb76741876e412bf97ddde0133a75e11bc47345354b3bb188a0ff101b7ce2c582187e758696ab89c1981892a43162f36d0af1
|
||||
checksum: 10/9e3351bb182bb02f6f140759472f08ce334c7c96f4ebfeec8e9e404fe60b8fe1865e1a6d1b50526f83f41e7224301485e46459df6c3675923f3b657177415cd7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/type-utils@npm:8.59.0":
|
||||
version: 8.59.0
|
||||
resolution: "@typescript-eslint/type-utils@npm:8.59.0"
|
||||
"@typescript-eslint/type-utils@npm:8.59.1":
|
||||
version: 8.59.1
|
||||
resolution: "@typescript-eslint/type-utils@npm:8.59.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.59.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.59.0"
|
||||
"@typescript-eslint/utils": "npm:8.59.0"
|
||||
"@typescript-eslint/types": "npm:8.59.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.59.1"
|
||||
"@typescript-eslint/utils": "npm:8.59.1"
|
||||
debug: "npm:^4.4.3"
|
||||
ts-api-utils: "npm:^2.5.0"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/9c2d34c10676d5726f93b975136295971ac7d2a53f74bfba51ae71deefaa36292adda79d016782196b729429143634b7f90224c27dcdb3a884b9771128be7490
|
||||
checksum: 10/8a8a71656f8fab446024e55b24f6f6c4b3ee4d4cdcb593ff68ec0ca10530fcb4d451628c03898c929e91445a999cbe980c0cfaec1b53a7c5ddc8ac899ad665fa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/types@npm:8.59.0, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.59.0":
|
||||
version: 8.59.0
|
||||
resolution: "@typescript-eslint/types@npm:8.59.0"
|
||||
checksum: 10/51a773339c58a350d0ddaecba46ba735696f11829cab1f9b3d5d58a4bbd498693296ae742e3959d32f3bb29676c8e6bd120b970379d749a5a9b419393696930d
|
||||
"@typescript-eslint/types@npm:8.59.1, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.59.1":
|
||||
version: 8.59.1
|
||||
resolution: "@typescript-eslint/types@npm:8.59.1"
|
||||
checksum: 10/4d324a01c2314d8e196b43b9dc5fe9a4d82c1b65f4915cd2f965879c5565d4453603b6f7b6bcdc436fb629135f07ad0f9d274e4697b02ce8bc1c0310916f7ace
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/typescript-estree@npm:8.59.0":
|
||||
version: 8.59.0
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:8.59.0"
|
||||
"@typescript-eslint/typescript-estree@npm:8.59.1":
|
||||
version: 8.59.1
|
||||
resolution: "@typescript-eslint/typescript-estree@npm:8.59.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/project-service": "npm:8.59.0"
|
||||
"@typescript-eslint/tsconfig-utils": "npm:8.59.0"
|
||||
"@typescript-eslint/types": "npm:8.59.0"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.59.0"
|
||||
"@typescript-eslint/project-service": "npm:8.59.1"
|
||||
"@typescript-eslint/tsconfig-utils": "npm:8.59.1"
|
||||
"@typescript-eslint/types": "npm:8.59.1"
|
||||
"@typescript-eslint/visitor-keys": "npm:8.59.1"
|
||||
debug: "npm:^4.4.3"
|
||||
minimatch: "npm:^10.2.2"
|
||||
semver: "npm:^7.7.3"
|
||||
@@ -4480,32 +4489,32 @@ __metadata:
|
||||
ts-api-utils: "npm:^2.5.0"
|
||||
peerDependencies:
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/48eba6a117a36c4bf569aa1a728463619b131a45a6891cc0a5d2454828d9d3d07a499e9906de0df31de57761ce1d13aebb635a059782f3cc16563e3e63a29713
|
||||
checksum: 10/8ede99640ac8b08ac73905bbc66dd06b2c4dc211240a4a9cb532b0fcf5c36ec9e7639ed7e1c17f86a948499279ff93e9dbcdf9170661d9f8347fcb53e8266772
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/utils@npm:8.59.0":
|
||||
version: 8.59.0
|
||||
resolution: "@typescript-eslint/utils@npm:8.59.0"
|
||||
"@typescript-eslint/utils@npm:8.59.1":
|
||||
version: 8.59.1
|
||||
resolution: "@typescript-eslint/utils@npm:8.59.1"
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils": "npm:^4.9.1"
|
||||
"@typescript-eslint/scope-manager": "npm:8.59.0"
|
||||
"@typescript-eslint/types": "npm:8.59.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.59.0"
|
||||
"@typescript-eslint/scope-manager": "npm:8.59.1"
|
||||
"@typescript-eslint/types": "npm:8.59.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.59.1"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/70547510f16459ca29e207584676f7c15626b5f7e2562643144fe037a1a9c4ca7116be99e67b9045f0de60db0022affb58c34c553a5370276ff8f542f7b05732
|
||||
checksum: 10/26ae39a574e56d92b6fc406113e797c354fce8b377721cc5dd50579a0e9f8c3efe23c7693826ce2c16be96490520dd6ce7e145c4c39c22d8d00f2614791603ba
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@typescript-eslint/visitor-keys@npm:8.59.0":
|
||||
version: 8.59.0
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:8.59.0"
|
||||
"@typescript-eslint/visitor-keys@npm:8.59.1":
|
||||
version: 8.59.1
|
||||
resolution: "@typescript-eslint/visitor-keys@npm:8.59.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/types": "npm:8.59.0"
|
||||
"@typescript-eslint/types": "npm:8.59.1"
|
||||
eslint-visitor-keys: "npm:^5.0.0"
|
||||
checksum: 10/b81753b9ddddeb3564e44d1199ba5546028731c7b5b3270938525f1f2b549d1df5fa8f203d9b3eacc120fa6b5af314cb1fb69d3a12d1dcce18a52a0fe316628d
|
||||
checksum: 10/5343f3424cafdcaf2550fade29eca6b86ad3f6ac953aef6ba1dccd39789a1c38520634fbbc0814419d9227f508789053c1c9f59c2841d72e56431c3fdd93ac65
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -6712,6 +6721,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"entities@npm:^8.0.0":
|
||||
version: 8.0.0
|
||||
resolution: "entities@npm:8.0.0"
|
||||
checksum: 10/d6e2ba75e444fb101ee2fbb07c839e687306c8a509426b75186619c19196f97c1db9932ca083f823c03e4a20e7407b654aa34de8cbb7770468e20fb2d4573a0e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"env-paths@npm:^2.2.0":
|
||||
version: 2.2.1
|
||||
resolution: "env-paths@npm:2.2.1"
|
||||
@@ -8106,6 +8122,7 @@ __metadata:
|
||||
"@codemirror/lang-jinja": "npm:6.0.1"
|
||||
"@codemirror/lang-yaml": "npm:6.1.3"
|
||||
"@codemirror/language": "npm:6.12.3"
|
||||
"@codemirror/lint": "npm:6.9.5"
|
||||
"@codemirror/search": "npm:6.7.0"
|
||||
"@codemirror/state": "npm:6.6.0"
|
||||
"@codemirror/view": "npm:6.41.1"
|
||||
@@ -8127,7 +8144,7 @@ __metadata:
|
||||
"@fullcalendar/list": "npm:6.1.20"
|
||||
"@fullcalendar/luxon3": "npm:6.1.20"
|
||||
"@fullcalendar/timegrid": "npm:6.1.20"
|
||||
"@home-assistant/webawesome": "npm:3.3.1-ha.1"
|
||||
"@home-assistant/webawesome": "npm:3.3.1-ha.2"
|
||||
"@html-eslint/eslint-plugin": "npm:0.59.0"
|
||||
"@lezer/highlight": "npm:1.2.3"
|
||||
"@lit-labs/motion": "npm:1.1.0"
|
||||
@@ -8223,7 +8240,7 @@ __metadata:
|
||||
idb-keyval: "npm:6.2.2"
|
||||
intl-messageformat: "npm:11.2.2"
|
||||
js-yaml: "npm:4.1.1"
|
||||
jsdom: "npm:29.0.2"
|
||||
jsdom: "npm:29.1.0"
|
||||
jszip: "npm:3.10.1"
|
||||
leaflet: "npm:1.9.4"
|
||||
leaflet-draw: "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch"
|
||||
@@ -8258,7 +8275,7 @@ __metadata:
|
||||
tinykeys: "npm:3.0.0"
|
||||
ts-lit-plugin: "npm:2.0.2"
|
||||
typescript: "npm:6.0.3"
|
||||
typescript-eslint: "npm:8.59.0"
|
||||
typescript-eslint: "npm:8.59.1"
|
||||
vite-tsconfig-paths: "npm:6.1.1"
|
||||
vitest: "npm:4.1.5"
|
||||
webpack-stats-plugin: "npm:1.1.3"
|
||||
@@ -9098,26 +9115,26 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"jsdom@npm:29.0.2":
|
||||
version: 29.0.2
|
||||
resolution: "jsdom@npm:29.0.2"
|
||||
"jsdom@npm:29.1.0":
|
||||
version: 29.1.0
|
||||
resolution: "jsdom@npm:29.1.0"
|
||||
dependencies:
|
||||
"@asamuzakjp/css-color": "npm:^5.1.5"
|
||||
"@asamuzakjp/dom-selector": "npm:^7.0.6"
|
||||
"@asamuzakjp/css-color": "npm:^5.1.11"
|
||||
"@asamuzakjp/dom-selector": "npm:^7.1.1"
|
||||
"@bramus/specificity": "npm:^2.4.2"
|
||||
"@csstools/css-syntax-patches-for-csstree": "npm:^1.1.1"
|
||||
"@csstools/css-syntax-patches-for-csstree": "npm:^1.1.3"
|
||||
"@exodus/bytes": "npm:^1.15.0"
|
||||
css-tree: "npm:^3.2.1"
|
||||
data-urls: "npm:^7.0.0"
|
||||
decimal.js: "npm:^10.6.0"
|
||||
html-encoding-sniffer: "npm:^6.0.0"
|
||||
is-potential-custom-element-name: "npm:^1.0.1"
|
||||
lru-cache: "npm:^11.2.7"
|
||||
parse5: "npm:^8.0.0"
|
||||
lru-cache: "npm:^11.3.5"
|
||||
parse5: "npm:^8.0.1"
|
||||
saxes: "npm:^6.0.0"
|
||||
symbol-tree: "npm:^3.2.4"
|
||||
tough-cookie: "npm:^6.0.1"
|
||||
undici: "npm:^7.24.5"
|
||||
undici: "npm:^7.25.0"
|
||||
w3c-xmlserializer: "npm:^5.0.0"
|
||||
webidl-conversions: "npm:^8.0.1"
|
||||
whatwg-mimetype: "npm:^5.0.0"
|
||||
@@ -9128,7 +9145,7 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
canvas:
|
||||
optional: true
|
||||
checksum: 10/3ad1d9a5b6aba067427bc43be98e1c51fab489bf689a6530e596278c6326fe053c94fc47a9c133f126fbe914f421283ae723fb92214dfe4959ca6cf2ee1666f6
|
||||
checksum: 10/91194be30c10b518c8e21c3a15cf1ef0e6c74722fb8fe402f201efed3e5de55f13005f2a4108d02ccde75d1e16719ca898690be93cd22d498a4c062d035adae5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -9652,10 +9669,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lru-cache@npm:^11.0.0, lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.1, lru-cache@npm:^11.2.7":
|
||||
version: 11.2.7
|
||||
resolution: "lru-cache@npm:11.2.7"
|
||||
checksum: 10/fbff4b8dee8189dde9b52cdfb3ea89b4c9cec094c1538cd30d1f47299477ff312efdb35f7994477ec72328f8e754e232b26a143feda1bd1f79ff22da6664d2c5
|
||||
"lru-cache@npm:^11.0.0, lru-cache@npm:^11.1.0, lru-cache@npm:^11.2.1, lru-cache@npm:^11.3.5":
|
||||
version: 11.3.5
|
||||
resolution: "lru-cache@npm:11.3.5"
|
||||
checksum: 10/3701b77e87765a3aea453402a7850bdbf7e02445210f35bd5ba1561f601f605f488bf9932be4a3851a6664073924f671a1ec99c4a1a98c457e0d126872a3e04f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -10445,12 +10462,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"parse5@npm:^8.0.0":
|
||||
version: 8.0.0
|
||||
resolution: "parse5@npm:8.0.0"
|
||||
"parse5@npm:^8.0.1":
|
||||
version: 8.0.1
|
||||
resolution: "parse5@npm:8.0.1"
|
||||
dependencies:
|
||||
entities: "npm:^6.0.0"
|
||||
checksum: 10/1973850932bb1cbd52ab64502761489fbe1bb43a52dee7ce41aac0b6c33a51a92aaee04661590b0912b739ae9ee316bce4c78c8ea34af42a7e522c983c3c6cf5
|
||||
entities: "npm:^8.0.0"
|
||||
checksum: 10/671dedfe7cbf4714414317bc8c6b2a14c61ef44f8fd90c983b5b1870653af5aa2e3b4e25e38e9538a7120ea2b688c50908830da2bd0930d8fd4bce34aed024eb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -12551,18 +12568,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript-eslint@npm:8.59.0":
|
||||
version: 8.59.0
|
||||
resolution: "typescript-eslint@npm:8.59.0"
|
||||
"typescript-eslint@npm:8.59.1":
|
||||
version: 8.59.1
|
||||
resolution: "typescript-eslint@npm:8.59.1"
|
||||
dependencies:
|
||||
"@typescript-eslint/eslint-plugin": "npm:8.59.0"
|
||||
"@typescript-eslint/parser": "npm:8.59.0"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.59.0"
|
||||
"@typescript-eslint/utils": "npm:8.59.0"
|
||||
"@typescript-eslint/eslint-plugin": "npm:8.59.1"
|
||||
"@typescript-eslint/parser": "npm:8.59.1"
|
||||
"@typescript-eslint/typescript-estree": "npm:8.59.1"
|
||||
"@typescript-eslint/utils": "npm:8.59.1"
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
|
||||
typescript: ">=4.8.4 <6.1.0"
|
||||
checksum: 10/e015494cae2ae88c291e87d9d8c2c8d9924536f2edfac1a1da5e05f5ee083df7a8d916549f87af8a7b818d01de2bd505e29fdf991a086522a062387b4c2f1f64
|
||||
checksum: 10/d35d30bdcff5b9644c9bf500d3ad74cebbd41612bf2669c3e3208cd74ee43302941666336acfca65bc44352b9b58c0455afe0a9e7106f12e54789b8c1f16dc11
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -12665,10 +12682,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"undici@npm:^7.24.5":
|
||||
version: 7.24.5
|
||||
resolution: "undici@npm:7.24.5"
|
||||
checksum: 10/1eef66817e5bd1ee6ba908553452e8e23c6b743221f8435e838a87fcec84db901ad9bca5853e7a6e123520abd8d3dd03215b19bf57c2ffb114bfeb0b7c206027
|
||||
"undici@npm:^7.25.0":
|
||||
version: 7.25.0
|
||||
resolution: "undici@npm:7.25.0"
|
||||
checksum: 10/038d3568c72bb976e3cc389284f7f1cc64cd70d578300e4676a449fbcb624a35fe99ac127b5f3729f18b8246d6c090444ab61b1b67736bb88f52a3e913d76bf8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user