Compare commits

..

1 Commits

Author SHA1 Message Date
Wendelin
928ac33f94 Add touch area to control-switch 2026-04-30 11:19:51 +02:00
35 changed files with 453 additions and 2411 deletions

View File

@@ -54,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.2",
"@home-assistant/webawesome": "3.3.1-ha.1",
"@lezer/highlight": "1.2.3",
"@lit-labs/motion": "1.1.0",
"@lit-labs/observers": "2.1.0",
@@ -201,7 +201,7 @@
"terser-webpack-plugin": "5.5.0",
"ts-lit-plugin": "2.0.2",
"typescript": "6.0.3",
"typescript-eslint": "8.59.1",
"typescript-eslint": "8.59.0",
"vite-tsconfig-paths": "6.1.1",
"vitest": "4.1.5",
"webpack-stats-plugin": "1.1.3",

View File

@@ -1499,7 +1499,6 @@ 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;

View File

@@ -166,6 +166,7 @@ export class HaEntityToggle extends LitElement {
ha-control-switch {
--control-switch-thickness: 20px;
--control-switch-off-color: var(--state-inactive-color);
--control-switch-touch-area-size: var(--ha-space-2);
}
ha-icon-button {
--ha-icon-button-size: 40px;

View File

@@ -1,42 +0,0 @@
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import type { CompletionItem } from "./ha-code-editor-completion-items";
import "./ha-code-editor-completion-items";
@customElement("ha-code-editor-jinja-arg-hover")
export class HaCodeEditorJinjaArgHover extends LitElement {
/** Bold heading shown above the items grid (e.g. entity/device/area name). */
@property({ attribute: false }) public heading?: string;
@property({ attribute: false }) public items: CompletionItem[] = [];
render() {
return html`
${this.heading
? html`<div class="heading">${this.heading}</div>`
: nothing}
<ha-code-editor-completion-items
.items=${this.items}
></ha-code-editor-completion-items>
`;
}
static styles = css`
:host {
display: block;
padding: 6px 10px;
max-width: 360px;
}
.heading {
font-weight: var(--ha-font-weight-bold);
margin-bottom: 4px;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"ha-code-editor-jinja-arg-hover": HaCodeEditorJinjaArgHover;
}
}

View File

@@ -1,101 +0,0 @@
import type { Completion } from "@codemirror/autocomplete";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { mdiHelpCircleOutline } from "@mdi/js";
import "./ha-svg-icon";
@customElement("ha-code-editor-jinja-hover")
export class HaCodeEditorJinjaHover extends LitElement {
@property({ attribute: false }) public completion!: Completion;
@property({ attribute: false }) public docUrl?: string;
@property({ attribute: false }) public openDocumentation =
"Open documentation";
render() {
const info =
typeof this.completion.info === "string"
? this.completion.info
: undefined;
return html`
<div class="header">
<div class="sig">
<strong>${this.completion.label}</strong>
${this.completion.detail
? html`<span class="detail">(${this.completion.detail})</span>`
: nothing}
</div>
${this.docUrl
? html`<a
class="doc-link"
href=${this.docUrl}
target="_blank"
rel="noreferrer"
title=${this.openDocumentation}
><ha-svg-icon .path=${mdiHelpCircleOutline}></ha-svg-icon
></a>`
: nothing}
</div>
${info ? html`<div class="desc">${info}</div>` : nothing}
`;
}
static styles = css`
:host {
display: block;
padding: 6px 10px;
max-width: 360px;
line-height: 1.5;
}
.header {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 4px;
}
.sig {
font-family: var(--ha-font-family-code);
font-size: 0.9em;
flex: 1;
min-width: 0;
}
.detail {
color: var(--secondary-text-color);
}
.doc-link {
flex-shrink: 0;
display: inline-flex;
align-items: center;
color: var(--secondary-text-color);
opacity: 0.7;
line-height: 1;
}
.doc-link:hover {
opacity: 1;
color: var(--primary-color);
}
.doc-link ha-svg-icon {
width: 16px;
height: 16px;
}
.desc {
font-size: 0.9em;
color: var(--secondary-text-color);
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"ha-code-editor-jinja-hover": HaCodeEditorJinjaHover;
}
}

View File

@@ -36,13 +36,9 @@ import { computeAreaName } from "../common/entity/compute_area_name";
import { computeFloorName } from "../common/entity/compute_floor_name";
import { copyToClipboard } from "../common/util/copy-clipboard";
import { haStyleScrollbar } from "../resources/styles";
import type {
JinjaArgType,
HassArgHoverContext,
} from "../resources/jinja_ha_completions";
import type { JinjaArgType } from "../resources/jinja_ha_completions";
import type { HomeAssistant } from "../types";
import { showToast } from "../util/toast";
import { documentationUrl } from "../util/documentation-url";
import { labelsContext } from "../data/context";
import type { LabelRegistryEntry } from "../data/label/label_registry";
import "./ha-code-editor-completion-items";
@@ -391,16 +387,6 @@ export class HaCodeEditor extends ReactiveElement {
this._loadedCodeMirror.tooltips({
position: "absolute",
}),
this._loadedCodeMirror.hoverTooltip(
(view, pos) =>
this._loadedCodeMirror!.haJinjaHoverSource(
view,
pos,
this.hass ? documentationUrl(this.hass, "") : undefined,
this.hass ? this._hassArgHoverContext() : undefined
),
{ hoverTime: 300 }
),
...(this.placeholder ? [placeholder(this.placeholder)] : []),
];
@@ -650,48 +636,6 @@ export class HaCodeEditor extends ReactiveElement {
}
};
/**
* Builds a HassArgHoverContext from the current hass object so that
* haJinjaHoverSource can resolve entity / device / area friendly names
* without importing the full HomeAssistant type into the resource file.
*/
private _hassArgHoverContext(): HassArgHoverContext {
const hass = this.hass!;
const labelMap: Record<
string,
{ name: string; description?: string | null }
> = {};
for (const label of this._labels ?? []) {
labelMap[label.label_id] = {
name: label.name,
description: label.description,
};
}
return {
states: hass.states as HassArgHoverContext["states"],
devices: hass.devices as HassArgHoverContext["devices"],
areas: hass.areas as HassArgHoverContext["areas"],
floors: hass.floors as HassArgHoverContext["floors"],
entities: hass.entities as HassArgHoverContext["entities"],
labels: labelMap,
formatEntityState: (entityId) =>
hass.formatEntityState(hass.states[entityId]),
formatEntityName: (entityId) => {
const stateObj = hass.states[entityId];
return (
(stateObj?.attributes.friendly_name as string | undefined) ??
hass.entities[entityId]?.name ??
undefined
);
},
formatAttributeName: (entityId, attribute) =>
hass.formatEntityAttributeName(hass.states[entityId], attribute),
formatAttributeValue: (entityId, attribute) =>
hass.formatEntityAttributeValue(hass.states[entityId], attribute),
localize: (key) => hass.localize(key as never),
};
}
private _renderInfo = (completion: Completion): CompletionInfo => {
const key =
typeof completion.apply === "string"

View File

@@ -8,7 +8,7 @@ import {
} from "@egjs/hammerjs";
import type { PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property, query } from "lit/decorators";
import { customElement, property } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { fireEvent } from "../common/dom/fire_event";
import { mainWindow } from "../common/dom/get_main_window";
@@ -71,16 +71,13 @@ export class HaControlSwitch extends LitElement {
this.destroyListeners();
}
@query("#switch")
private switch!: HTMLDivElement;
setupSwipeListeners() {
if (this.disabled) {
return;
}
if (this.switch && !this._mc) {
this._mc = new Manager(this.switch, {
if (!this._mc) {
this._mc = new Manager(this, {
touchAction: this.touchAction ?? (this.vertical ? "pan-x" : "pan-y"),
});
this._mc.add(
@@ -191,6 +188,7 @@ export class HaControlSwitch extends LitElement {
static styles = css`
:host {
display: block;
position: relative;
--control-switch-on-color: var(--primary-color);
--control-switch-off-color: var(--disabled-color);
--control-switch-background-opacity: 0.2;
@@ -198,6 +196,7 @@ export class HaControlSwitch extends LitElement {
--control-switch-thickness: 40px;
--control-switch-border-radius: var(--ha-border-radius-lg);
--control-switch-padding: 4px;
--control-switch-touch-area-size: 0px;
--mdc-icon-size: 20px;
height: var(--control-switch-thickness);
width: 100%;
@@ -206,6 +205,11 @@ export class HaControlSwitch extends LitElement {
transition: box-shadow 180ms ease-in-out;
-webkit-tap-highlight-color: transparent;
}
:host::before {
content: "";
position: absolute;
inset: calc(-1 * var(--control-switch-touch-area-size));
}
.switch:not([disabled]):focus-visible {
box-shadow: 0 0 0 2px var(--control-switch-off-color);
}

View File

@@ -53,10 +53,7 @@ export class HaIconButton extends LitElement {
.download=${this.download}
>
${this.path
? html`<ha-svg-icon
aria-hidden="true"
.path=${this.path}
></ha-svg-icon>`
? html`<ha-svg-icon .path=${this.path}></ha-svg-icon>`
: html`<span><slot></slot></span>`}
</ha-button>
`;

View File

@@ -1,6 +1,8 @@
import { consume, type ContextType } from "@lit/context";
import { mdiMagnify } from "@mdi/js";
import { css, html, type PropertyValues } from "lit";
import { customElement } from "lit/decorators";
import { customElement, state } from "lit/decorators";
import { internationalizationContext } from "../../data/context";
import { HaInput } from "./ha-input";
/**
@@ -15,6 +17,10 @@ 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;
@@ -29,7 +35,7 @@ export class HaInputSearch extends HaInput {
!this.placeholder &&
(!this.hasUpdated || changedProps.has("_i18n"))
) {
this.placeholder = this.i18n?.localize?.("ui.common.search") || "Search";
this.placeholder = this._i18n.localize("ui.common.search");
}
}

View File

@@ -2,21 +2,19 @@ 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 {
css,
html,
LitElement,
nothing,
type PropertyValues,
type TemplateResult,
css,
html,
nothing,
} from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { customElement, property, query } 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";
@@ -127,10 +125,6 @@ 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",
@@ -239,22 +233,19 @@ export class HaInput extends WaInputMixin(LitElement) {
${this.renderStartDefault()}
</slot>
<slot name="end" slot="end"> ${this.renderEndDefault()} </slot>
<slot name="clear-button" slot="clear-button">
<ha-icon-button
@click=${this._handleClearClick}
.label=${this.i18n?.localize?.("ui.components.input.clear") ||
"Clear"}
.path=${mdiClose}
></ha-icon-button>
<slot name="clear-icon" slot="clear-icon">
<ha-icon-button .path=${mdiClose}></ha-icon-button>
</slot>
<slot name="password-toggle-button" slot="password-toggle-button">
<slot name="show-password-icon" slot="show-password-icon">
<ha-icon-button
@keydown=${stopPropagation}
@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}
.path=${mdiEye}
></ha-icon-button>
</slot>
<slot name="hide-password-icon" slot="hide-password-icon">
<ha-icon-button
@keydown=${stopPropagation}
.path=${mdiEyeOff}
></ha-icon-button>
</slot>
<div
@@ -302,14 +293,6 @@ export class HaInput extends WaInputMixin(LitElement) {
}
};
private _handleClearClick() {
this._input?.clear();
}
private _handlePasswordToggle() {
this.passwordVisible = !this.passwordVisible;
}
static styles = [
waInputStyles,
css`
@@ -431,12 +414,6 @@ 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;

View File

@@ -189,20 +189,6 @@ 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,

View File

@@ -134,7 +134,6 @@ type AutomationItem = AutomationEntity & {
formatted_state: string;
category: string | undefined;
label_entries: LabelRegistryEntry[];
labels: string[]; // search only
assistants: string[];
assistants_sortable_key: string | undefined;
};
@@ -257,9 +256,6 @@ 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
@@ -275,8 +271,9 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
category: category
? categoryReg?.find((cat) => cat.category_id === category)?.name
: undefined,
label_entries,
labels: label_entries.map((lbl) => lbl.name),
label_entries: (labels || [])
.map((lbl) => labelReg!.find((label) => label.label_id === lbl)!)
.filter(Boolean),
assistants,
assistants_sortable_key: getAssistantsSortableKey(assistants),
selectable: entityRegEntry !== undefined,

View File

@@ -1,123 +0,0 @@
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;
}
}

View File

@@ -1,154 +0,0 @@
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;
}
}

View File

@@ -1,9 +1,8 @@
import { mdiDotsVertical, mdiPlus, mdiUpload } from "@mdi/js";
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { customElement, property } 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";
@@ -24,7 +23,6 @@ import {
computeBackupAgentName,
generateBackup,
generateBackupWithAutomaticSettings,
saveBackupConfig,
} from "../../../data/backup";
import type { ManagerStateEvent } from "../../../data/backup_manager";
import type { CloudStatus } from "../../../data/cloud";
@@ -34,12 +32,10 @@ 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";
@@ -72,54 +68,10 @@ 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);
@@ -282,41 +234,13 @@ 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>
@@ -346,10 +270,6 @@ class HaConfigBackupOverview extends LitElement {
return [
haStyle,
css`
p {
color: var(--secondary-text-color);
}
.content {
padding: 28px 20px 0;
max-width: 690px;
@@ -363,6 +283,10 @@ class HaConfigBackupOverview extends LitElement {
display: flex;
justify-content: flex-end;
}
.card-content {
padding-left: 0;
padding-right: 0;
}
.loading {
display: flex;
}

View File

@@ -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 { saveBackupConfig } from "../../../data/backup";
import { updateBackupConfig } from "../../../data/backup";
import type { CloudStatus } from "../../../data/cloud";
import {
getSupervisorUpdateConfig,
@@ -27,9 +27,11 @@ 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";
@@ -77,7 +79,7 @@ class HaConfigBackupSettings extends LitElement {
// eslint-disable-next-line no-console
console.error(err);
this._supervisorUpdateConfigError = this.hass.localize(
"ui.panel.config.backup.settings.schedule.error_load",
"ui.panel.config.backup.settings.app_update_backup.error_load",
{
error: err?.message || err,
}
@@ -313,6 +315,57 @@ 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>
`;
@@ -385,6 +438,18 @@ 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
@@ -403,7 +468,7 @@ class HaConfigBackupSettings extends LitElement {
// eslint-disable-next-line no-console
console.error(err);
this._supervisorUpdateConfigError = this.hass.localize(
"ui.panel.config.backup.settings.schedule.error_save",
"ui.panel.config.backup.settings.app_update_backup.error_save",
{
error: err?.message || err?.toString(),
}
@@ -414,7 +479,18 @@ class HaConfigBackupSettings extends LitElement {
private _debounceSave = debounce(() => this._save(), 500);
private async _save() {
await saveBackupConfig(this.hass, this._config!);
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,
});
fireEvent(this, "ha-refresh-backup-config");
}

View File

@@ -125,11 +125,6 @@ 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"),

View File

@@ -244,13 +244,9 @@ 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%;

View File

@@ -9,7 +9,6 @@ import "../../../../components/ha-button";
import "../../../../components/ha-card";
import "../../../../components/ha-code-editor";
import "../../../../components/ha-spinner";
import "../../../../components/ha-tip";
import type { RenderTemplateResult } from "../../../../data/ws-templates";
import { subscribeRenderTemplate } from "../../../../data/ws-templates";
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
@@ -159,14 +158,6 @@ class HaPanelDevTemplate extends LitElement {
${this.hass.localize("ui.common.clear")}
</ha-button>
</div>
<ha-tip .hass=${this.hass}>
${this.hass.localize(
"ui.panel.config.developer-tools.tabs.templates.keyboard_tip",
{
autocomplete: html`<kbd>Ctrl</kbd>+<kbd>Space</kbd>`,
}
)}
</ha-tip>
</ha-card>
<ha-card
@@ -370,22 +361,6 @@ ${type === "object"
color: var(--warning-color);
}
ha-tip {
padding: 0 var(--ha-space-4) var(--ha-space-4);
display: block;
}
kbd {
display: inline-block;
font-family: var(--ha-font-family-code);
font-size: 0.85em;
padding: 1px 5px;
border: 1px solid var(--divider-color);
border-radius: var(--ha-border-radius-xs);
background-color: var(--secondary-background-color);
white-space: nowrap;
}
@media all and (max-width: 870px) {
.content ha-card {
max-width: 100%;

View File

@@ -146,7 +146,6 @@ interface HelperItem {
category: string | undefined;
area?: string;
label_entries: LabelRegistryEntry[];
labels: string[]; // search only
assistants: string[];
assistants_sortable_key: string | undefined;
disabled?: boolean;
@@ -553,9 +552,6 @@ 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 =
@@ -576,8 +572,9 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
`ui.panel.config.helpers.types.${item.type}` as LocalizeKeys
) ||
item.type,
label_entries,
labels: label_entries.map((lbl) => lbl.name),
label_entries: (labels || []).map(
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
),
category: category
? categoryReg?.find((cat) => cat.category_id === category)?.name
: undefined,

View File

@@ -122,7 +122,6 @@ 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;
@@ -240,9 +239,6 @@ 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
@@ -256,8 +252,9 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
category: category
? categoryReg?.find((cat) => cat.category_id === category)?.name
: undefined,
label_entries,
labels: label_entries.map((lbl) => lbl.name),
label_entries: (labels || []).map(
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
),
assistants,
assistants_sortable_key: getAssistantsSortableKey(assistants),
selectable: entityRegEntry !== undefined,

View File

@@ -127,7 +127,6 @@ type ScriptItem = ScriptEntity & {
last_triggered: string | undefined;
category: string | undefined;
label_entries: LabelRegistryEntry[];
labels: string[]; // search only
assistants: string[];
assistants_sortable_key: string | undefined;
};
@@ -246,9 +245,6 @@ 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
@@ -263,8 +259,9 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
category: category
? categoryReg?.find((cat) => cat.category_id === category)?.name
: undefined,
label_entries,
labels: label_entries.map((lbl) => lbl.name),
label_entries: (labels || []).map(
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
),
assistants,
assistants_sortable_key: getAssistantsSortableKey(assistants),
selectable: entityRegEntry !== undefined,

View File

@@ -35,10 +35,7 @@ import {
HOME_SUMMARIES_ICONS,
type HomeSummary,
} from "../strategies/home/helpers/home-summaries";
import {
filterLowBatteryEntities,
filterUnavailableBatteryEntities,
} from "../../maintenance/strategies/maintenance-view-strategy";
import { filterNeedsAttentionEntities } from "../../maintenance/strategies/maintenance-view-strategy";
import type { LovelaceCard, LovelaceGridOptions } from "../types";
import { tileCardStyle } from "./tile/tile-card-style";
import type { HomeSummaryCard } from "./types";
@@ -261,48 +258,19 @@ export class HuiHomeSummaryCard
maintenanceFilters
);
const lowBatteryEntities = filterLowBatteryEntities(
const needsAttentionEntities = filterNeedsAttentionEntities(
this.hass!,
maintenanceEntities
);
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 (needsAttentionEntities.length > 0) {
return this.hass.localize(
"ui.card.home-summary.count_maintenance_issues",
{
count: needsAttentionEntities.length,
}
);
}
if (lowBatteryText) {
return lowBatteryText;
}
if (unavailableText) {
return unavailableText;
}
return this.hass.localize("ui.card.home-summary.all_maintenance_good");
}
case "energy": {

View File

@@ -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, hasAnyAction } from "../common/has-action";
import { hasAction } from "../common/has-action";
import { hasConfigChanged } from "../common/has-changed";
import { createEntityNotFoundWarning } from "../components/hui-warning";
import type { LovelaceCard, LovelaceCardEditor } from "../types";
@@ -52,17 +52,10 @@ export class HuiPictureCard extends LitElement implements LovelaceCard {
throw new Error("Image required");
}
if (config.image_entity) {
this._config = {
tap_action: { action: "more-info" },
...config,
};
} else {
this._config = {
tap_action: { action: "none" },
...config,
};
}
this._config = {
tap_action: { action: "more-info" },
...config,
};
}
protected shouldUpdate(changedProps: PropertyValues): boolean {
@@ -174,11 +167,6 @@ 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}
@@ -192,7 +180,15 @@ export class HuiPictureCard extends LitElement implements LovelaceCard {
: undefined
)}
class=${classMap({
clickable,
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")
),
})}
>
<img

View File

@@ -55,6 +55,7 @@ class HuiEntitiesToggle extends LitElement {
ha-control-switch {
--control-switch-thickness: 20px;
--control-switch-off-color: var(--state-inactive-color);
--control-switch-touch-area-size: var(--ha-space-2);
}
`;

View File

@@ -419,7 +419,6 @@ export class HuiDialogEditBadge
.content {
width: 100%;
max-width: 100%;
gap: var(--ha-space-3);
}
}

View File

@@ -419,7 +419,6 @@ export class HuiDialogEditCard
.content {
width: 100%;
max-width: 100%;
gap: var(--ha-space-3);
}
}

View File

@@ -68,7 +68,9 @@ export class HuiPictureCardEditor
{
name: "tap_action",
selector: {
ui_action: {},
ui_action: {
default_action: "more-info",
},
},
context: ACTION_RELATED_CONTEXT,
},

View File

@@ -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, hasAnyAction } from "../common/has-action";
import { hasAction } from "../common/has-action";
import type { LovelaceHeaderFooter } from "../types";
import type { PictureHeaderFooterConfig } from "./types";
@@ -56,7 +56,9 @@ export class HuiPictureHeaderFooter
return nothing;
}
const clickable = hasAnyAction(this._config);
const clickable = Boolean(
this._config.tap_action || this._config.hold_action
);
return html`
<img

View File

@@ -409,8 +409,6 @@ 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}">

View File

@@ -31,7 +31,7 @@ export const maintenanceEntityFilters: EntityFilter[] = [
const LOW_BATTERY_THRESHOLD = 20;
export const filterLowBatteryEntities = (
export const filterNeedsAttentionEntities = (
hass: HomeAssistant,
entityIds: string[]
): string[] =>
@@ -40,14 +40,6 @@ export const filterLowBatteryEntities = (
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,

View File

@@ -44,7 +44,6 @@ export {
drawSelection,
EditorView,
highlightActiveLine,
hoverTooltip,
keymap,
lineNumbers,
rectangularSelection,
@@ -72,10 +71,9 @@ export const closeBracketsOverride = Prec.highest(
export {
haJinjaCompletionSource,
haJinjaHoverSource,
JINJA_FUNCTION_ARG_TYPES,
} from "./jinja_ha_completions";
export type { HassArgHoverContext, JinjaArgType } from "./jinja_ha_completions";
export type { JinjaArgType } from "./jinja_ha_completions";
export { closePercentBrace };
export const langCompartment = new Compartment();

File diff suppressed because it is too large Load Diff

View File

@@ -218,8 +218,7 @@
"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_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}",
"count_maintenance_issues": "{count} {count, plural,\n one {issue}\n other {issues}\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"
@@ -492,11 +491,6 @@
"markdown": "Markdown"
},
"components": {
"input": {
"clear": "Clear",
"show_password": "Show password",
"hide_password": "Hide password"
},
"selectors": {
"serial_port": {
"enter_manually": "Enter manually",
@@ -1504,9 +1498,6 @@
"area_label": "Area",
"description": "Configure which areas correspond to each vacuum segment"
},
"codemirror": {
"open_documentation": "Open documentation"
},
"safe_mode": {
"title": "Safe mode",
"text": "Home Assistant is running in safe mode, custom integrations and community frontend modules are not available. Restart Home Assistant to exit safe mode."
@@ -3567,7 +3558,7 @@
"show_all": "Show all backups"
},
"settings": {
"title": "Automatic backup",
"title": "Backup settings",
"configure": "Configure backup settings",
"schedule": "Automatic backup schedule and retention",
"schedule_copies_all": "and keep all backups",
@@ -3613,10 +3604,6 @@
"sat": "Sa",
"sun": "Su"
}
},
"app_update_backup": {
"title": "App update backup",
"description": "Backup behavior for app updates"
}
},
"backups": {
@@ -3628,15 +3615,13 @@
"new_backup": "[%key:ui::panel::config::backup::overview::new_backup%]"
},
"settings": {
"header": "Automatic backups",
"header": "Backup settings",
"menu": {
"change_default_location": "Change default action location"
},
"schedule": {
"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}"
"title": "Automatic backups",
"description": "Let Home Assistant take care of your backups by creating a scheduled backup that also removes older backups."
},
"data": {
"title": "Backup data"
@@ -3664,9 +3649,6 @@
"error_save": "Error saving Supervisor update config: {error}"
}
},
"app_update_backups": {
"header": "App update backups"
},
"details": {
"header": "Backup",
"not_found": "Not found",
@@ -3894,8 +3876,7 @@
"no_listeners": "This template does not listen for any events and will not update automatically.",
"listeners": "This template listens for the following state changed events:",
"entity": "Entity",
"domain": "Domain",
"keyboard_tip": "Press {autocomplete} to trigger autocomplete, when your cursor is inside a function that supports it."
"domain": "Domain"
},
"statistics": {
"title": "Statistics",

156
yarn.lock
View File

@@ -1857,9 +1857,9 @@ __metadata:
languageName: node
linkType: hard
"@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"
"@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"
dependencies:
"@ctrl/tinycolor": "npm:4.1.0"
"@floating-ui/dom": "npm:^1.6.13"
@@ -1870,7 +1870,7 @@ __metadata:
lit: "npm:^3.2.1"
nanoid: "npm:^5.1.5"
qr-creator: "npm:^1.0.0"
checksum: 10/c66965544bb34eb9d6d131579c31e4c6d21c8c231bdf565db61195da03a995d17e25d6d997875c902d7d2f3020c70434088efb7299b2626d4a7d1343cb33d726
checksum: 10/903b7e5e44a9c4afa5120b5fbbf139880889798a82262b10b5c5fa51cd56cd344d8027d69658b15c85efc579688476bbd8e5eff73535f903959bbd9ffd00051f
languageName: node
linkType: hard
@@ -4383,105 +4383,105 @@ __metadata:
languageName: node
linkType: hard
"@typescript-eslint/eslint-plugin@npm:8.59.1":
version: 8.59.1
resolution: "@typescript-eslint/eslint-plugin@npm:8.59.1"
"@typescript-eslint/eslint-plugin@npm:8.59.0":
version: 8.59.0
resolution: "@typescript-eslint/eslint-plugin@npm:8.59.0"
dependencies:
"@eslint-community/regexpp": "npm:^4.12.2"
"@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"
"@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"
ignore: "npm:^7.0.5"
natural-compare: "npm:^1.4.0"
ts-api-utils: "npm:^2.5.0"
peerDependencies:
"@typescript-eslint/parser": ^8.59.1
"@typescript-eslint/parser": ^8.59.0
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: ">=4.8.4 <6.1.0"
checksum: 10/c736ee32211a3751e31151b51dacc8cfa5bf18e086f2a87aba7ee325f7e2fa96d8b9febdbaf4dfa70d14954312b7b9740fbe5d5886b3f8561c4a94a9c7ff7688
checksum: 10/fcf2c85cb37d61854d2c03fa11c66ad58d99f4eee731dd09663f20f0395e642b12edeab2a6c368ac1806505b2071a01de01bc30b9011fa309299836e868a293a
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:8.59.1":
version: 8.59.1
resolution: "@typescript-eslint/parser@npm:8.59.1"
"@typescript-eslint/parser@npm:8.59.0":
version: 8.59.0
resolution: "@typescript-eslint/parser@npm:8.59.0"
dependencies:
"@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"
"@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"
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/b014b485e5ec9c7430a87117271836b86fd80083fe6b1d216167313518f26222f45c0ee3f4cbc0616dbd6335cbde50336d8953ca5ffefecc55b2d896ac7645f9
checksum: 10/b8990e1b67e6f55aa4884807e6c3b6bd08c58f96ea4f03f19e7aba3fc1b16f040fe58378490de9bd831c804eb48e633e30e5baf291b8e8dd53960424e5751391
languageName: node
linkType: hard
"@typescript-eslint/project-service@npm:8.59.1":
version: 8.59.1
resolution: "@typescript-eslint/project-service@npm:8.59.1"
"@typescript-eslint/project-service@npm:8.59.0":
version: 8.59.0
resolution: "@typescript-eslint/project-service@npm:8.59.0"
dependencies:
"@typescript-eslint/tsconfig-utils": "npm:^8.59.1"
"@typescript-eslint/types": "npm:^8.59.1"
"@typescript-eslint/tsconfig-utils": "npm:^8.59.0"
"@typescript-eslint/types": "npm:^8.59.0"
debug: "npm:^4.4.3"
peerDependencies:
typescript: ">=4.8.4 <6.1.0"
checksum: 10/dd98f49a407cb21999d31ec527a0f8c2c34422dde9fdb21210d66c3cc3d498d9d3678d95c99d76450af68ce3392692902d9ba044718d6c99122655df7afdc0a7
checksum: 10/b842f1e0623c3a679d21d76c7ca38698787681d40f6a9ce93c8983120fb6795a2395907d530e4f8d89b4ac5bc65e71bbfdf2d8060f210c8487cffdae40baea74
languageName: node
linkType: hard
"@typescript-eslint/scope-manager@npm:8.59.1":
version: 8.59.1
resolution: "@typescript-eslint/scope-manager@npm:8.59.1"
"@typescript-eslint/scope-manager@npm:8.59.0":
version: 8.59.0
resolution: "@typescript-eslint/scope-manager@npm:8.59.0"
dependencies:
"@typescript-eslint/types": "npm:8.59.1"
"@typescript-eslint/visitor-keys": "npm:8.59.1"
checksum: 10/50c941d1af470d3e67a9bd2247c541a676ae6bb2931440a44458682d61382ba1194ce29d0388dd1e538c5a35d7a542febd9519d8170abe758692d1b6cd196eab
"@typescript-eslint/types": "npm:8.59.0"
"@typescript-eslint/visitor-keys": "npm:8.59.0"
checksum: 10/8bb1182559e7676120ba12bdac11edea9fb414bd33d379a1902b035b8b4b68d23ad239d845bfe6943b5da13ecd938ea1482c73e8c6ddb4d7e3e0f8e111467e28
languageName: node
linkType: hard
"@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"
"@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"
peerDependencies:
typescript: ">=4.8.4 <6.1.0"
checksum: 10/9e3351bb182bb02f6f140759472f08ce334c7c96f4ebfeec8e9e404fe60b8fe1865e1a6d1b50526f83f41e7224301485e46459df6c3675923f3b657177415cd7
checksum: 10/9c094c199be4803d696dbf7cb5cdb76741876e412bf97ddde0133a75e11bc47345354b3bb188a0ff101b7ce2c582187e758696ab89c1981892a43162f36d0af1
languageName: node
linkType: hard
"@typescript-eslint/type-utils@npm:8.59.1":
version: 8.59.1
resolution: "@typescript-eslint/type-utils@npm:8.59.1"
"@typescript-eslint/type-utils@npm:8.59.0":
version: 8.59.0
resolution: "@typescript-eslint/type-utils@npm:8.59.0"
dependencies:
"@typescript-eslint/types": "npm:8.59.1"
"@typescript-eslint/typescript-estree": "npm:8.59.1"
"@typescript-eslint/utils": "npm:8.59.1"
"@typescript-eslint/types": "npm:8.59.0"
"@typescript-eslint/typescript-estree": "npm:8.59.0"
"@typescript-eslint/utils": "npm:8.59.0"
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/8a8a71656f8fab446024e55b24f6f6c4b3ee4d4cdcb593ff68ec0ca10530fcb4d451628c03898c929e91445a999cbe980c0cfaec1b53a7c5ddc8ac899ad665fa
checksum: 10/9c2d34c10676d5726f93b975136295971ac7d2a53f74bfba51ae71deefaa36292adda79d016782196b729429143634b7f90224c27dcdb3a884b9771128be7490
languageName: node
linkType: hard
"@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
"@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
languageName: node
linkType: hard
"@typescript-eslint/typescript-estree@npm:8.59.1":
version: 8.59.1
resolution: "@typescript-eslint/typescript-estree@npm:8.59.1"
"@typescript-eslint/typescript-estree@npm:8.59.0":
version: 8.59.0
resolution: "@typescript-eslint/typescript-estree@npm:8.59.0"
dependencies:
"@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"
"@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"
debug: "npm:^4.4.3"
minimatch: "npm:^10.2.2"
semver: "npm:^7.7.3"
@@ -4489,32 +4489,32 @@ __metadata:
ts-api-utils: "npm:^2.5.0"
peerDependencies:
typescript: ">=4.8.4 <6.1.0"
checksum: 10/8ede99640ac8b08ac73905bbc66dd06b2c4dc211240a4a9cb532b0fcf5c36ec9e7639ed7e1c17f86a948499279ff93e9dbcdf9170661d9f8347fcb53e8266772
checksum: 10/48eba6a117a36c4bf569aa1a728463619b131a45a6891cc0a5d2454828d9d3d07a499e9906de0df31de57761ce1d13aebb635a059782f3cc16563e3e63a29713
languageName: node
linkType: hard
"@typescript-eslint/utils@npm:8.59.1":
version: 8.59.1
resolution: "@typescript-eslint/utils@npm:8.59.1"
"@typescript-eslint/utils@npm:8.59.0":
version: 8.59.0
resolution: "@typescript-eslint/utils@npm:8.59.0"
dependencies:
"@eslint-community/eslint-utils": "npm:^4.9.1"
"@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/scope-manager": "npm:8.59.0"
"@typescript-eslint/types": "npm:8.59.0"
"@typescript-eslint/typescript-estree": "npm:8.59.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: ">=4.8.4 <6.1.0"
checksum: 10/26ae39a574e56d92b6fc406113e797c354fce8b377721cc5dd50579a0e9f8c3efe23c7693826ce2c16be96490520dd6ce7e145c4c39c22d8d00f2614791603ba
checksum: 10/70547510f16459ca29e207584676f7c15626b5f7e2562643144fe037a1a9c4ca7116be99e67b9045f0de60db0022affb58c34c553a5370276ff8f542f7b05732
languageName: node
linkType: hard
"@typescript-eslint/visitor-keys@npm:8.59.1":
version: 8.59.1
resolution: "@typescript-eslint/visitor-keys@npm:8.59.1"
"@typescript-eslint/visitor-keys@npm:8.59.0":
version: 8.59.0
resolution: "@typescript-eslint/visitor-keys@npm:8.59.0"
dependencies:
"@typescript-eslint/types": "npm:8.59.1"
"@typescript-eslint/types": "npm:8.59.0"
eslint-visitor-keys: "npm:^5.0.0"
checksum: 10/5343f3424cafdcaf2550fade29eca6b86ad3f6ac953aef6ba1dccd39789a1c38520634fbbc0814419d9227f508789053c1c9f59c2841d72e56431c3fdd93ac65
checksum: 10/b81753b9ddddeb3564e44d1199ba5546028731c7b5b3270938525f1f2b549d1df5fa8f203d9b3eacc120fa6b5af314cb1fb69d3a12d1dcce18a52a0fe316628d
languageName: node
linkType: hard
@@ -8144,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.2"
"@home-assistant/webawesome": "npm:3.3.1-ha.1"
"@html-eslint/eslint-plugin": "npm:0.59.0"
"@lezer/highlight": "npm:1.2.3"
"@lit-labs/motion": "npm:1.1.0"
@@ -8275,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.1"
typescript-eslint: "npm:8.59.0"
vite-tsconfig-paths: "npm:6.1.1"
vitest: "npm:4.1.5"
webpack-stats-plugin: "npm:1.1.3"
@@ -12568,18 +12568,18 @@ __metadata:
languageName: node
linkType: hard
"typescript-eslint@npm:8.59.1":
version: 8.59.1
resolution: "typescript-eslint@npm:8.59.1"
"typescript-eslint@npm:8.59.0":
version: 8.59.0
resolution: "typescript-eslint@npm:8.59.0"
dependencies:
"@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"
"@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"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: ">=4.8.4 <6.1.0"
checksum: 10/d35d30bdcff5b9644c9bf500d3ad74cebbd41612bf2669c3e3208cd74ee43302941666336acfca65bc44352b9b58c0455afe0a9e7106f12e54789b8c1f16dc11
checksum: 10/e015494cae2ae88c291e87d9d8c2c8d9924536f2edfac1a1da5e05f5ee083df7a8d916549f87af8a7b818d01de2bd505e29fdf991a086522a062387b4c2f1f64
languageName: node
linkType: hard