Compare commits

...

19 Commits

Author SHA1 Message Date
Simon Lamon 495d437f9f Show all actions if none specified 2026-05-31 07:45:46 +00:00
karwosts 75b9fb2e34 Fix untracked legend in detail graph card (#52299) 2026-05-30 07:53:40 +03:00
Jan-Philipp Benecke 38f0ce306b Add box-shadow transition to top app bar (#52292) 2026-05-29 18:01:44 +02:00
Petar Petrov 1ffd19e20b Make battery dialog charge/discharge order consistent (#52295)
Order the energy fields as discharge then charge to match the power
fields in the energy panel battery settings dialog.

Co-authored-by: MindFreeze <noreply@anthropic.com>
2026-05-29 18:01:14 +02:00
Aidan Timson 9a216cae46 Add cover and valve favorite positions to suggestions (#52273) 2026-05-29 08:33:23 +03:00
karwosts 41e6408508 Fix missing location data in calendar (#52291) 2026-05-29 08:32:15 +03:00
renovate[bot] 97e85bc06f Update dependency typescript-eslint to v8.60.0 (#52290)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-29 08:06:38 +03:00
Petar Petrov 5f2ad7fa01 Migrate hui-buttons-base and ha-selector-attribute to states context (partial hass) (#52262) 2026-05-28 16:16:10 +02:00
ildar170975 7b6b70023b Statistics graph card editor: add sub editor (#52182)
* add canEdit

* add canEdit

* add subEditor

* linter

* linter

* linter

* linter

* Remove div

* Update src/components/entity/ha-statistic-picker.ts

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>

* Update src/components/entity/ha-statistic-picker.ts

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>

* Update ha-statistic-picker.ts

* Update ha-statistic-picker.ts

---------

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2026-05-28 17:13:20 +03:00
Paul Bottein 256a06e35f Preserve PNG transparency on area pictures (#52282) 2026-05-28 16:08:16 +02:00
Paul Bottein 4e26c05ac6 Don't lowercase translated default action label (#52283) 2026-05-28 13:47:56 +00:00
Paul Bottein 04ee8ac415 Fix sun condition Between description showing reversed values (#52279) 2026-05-28 13:46:56 +00:00
Petar Petrov 63e144309c Migrate ha-selector choose/period/file/selector to localize context (#52266) 2026-05-28 15:32:21 +02:00
Petar Petrov 77039cda8e Migrate automation icon components to config and connection context (#52261) 2026-05-28 15:22:06 +02:00
Petar Petrov ab5b4ed792 Drop unused hass prop from ha-selector-boolean, ha-selector-duration, ha-sunburst-chart (#52253)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-05-28 13:21:47 +00:00
Petar Petrov a08905cd31 Migrate ha-relative-time and ha-absolute-time to localize/locale/config context (#52259)
* Migrate ha-relative-time and ha-absolute-time off hass property

Consume localize, locale, and config via Lit context so time primitives
only rerender when i18n or config slices change, and drop obsolete .hass
bindings from callers.

* Consume full i18n context in time display components

Use internationalizationContext directly for both localize and locale in
ha-relative-time and ha-absolute-time, avoiding mixed consumption patterns.
2026-05-28 16:16:11 +03:00
Petar Petrov a35349196f Migrate trace logbook components to localize context (#52260) 2026-05-28 15:14:44 +02:00
Petar Petrov dbdfdedd74 Migrate energy total badges to states/locale/localize context (partial hass) (#52255) 2026-05-28 15:01:31 +02:00
Petar Petrov a5c8547b2b Migrate ha-filter-blueprints and ha-filter-voice-assistants to localize context (#52252) 2026-05-28 14:59:13 +02:00
56 changed files with 709 additions and 319 deletions
+1 -1
View File
@@ -198,7 +198,7 @@
"terser-webpack-plugin": "5.6.0",
"ts-lit-plugin": "2.0.2",
"typescript": "6.0.3",
"typescript-eslint": "8.59.4",
"typescript-eslint": "8.60.0",
"vite-tsconfig-paths": "6.1.1",
"vitest": "4.1.7",
"webpack-stats-plugin": "1.1.3",
+67 -19
View File
@@ -11,6 +11,7 @@ import {
} from "../../data/context";
import type { EntityRegistryDisplayEntry } from "../../data/entity/entity_registry";
import type { LocalizeFunc } from "../translations/localize";
import { ensureArray } from "../array/ensure-array";
import { transform } from "./transform";
interface ConsumeEntryConfig {
@@ -26,6 +27,28 @@ const resolveAtPath = (host: unknown, path: readonly string[]) => {
return cur;
};
/** Reuse `previous` when every entry still references the same `HassEntity`. */
export const preserveUnchangedEntityStatesRecord = <
T extends Record<string, HassEntity | undefined>,
>(
previous: T | undefined,
next: T
): T => {
if (!previous) {
return next;
}
const nextKeys = Object.keys(next);
if (Object.keys(previous).length !== nextKeys.length) {
return next;
}
for (const key of nextKeys) {
if (previous[key] !== next[key]) {
return next;
}
}
return previous;
};
const composeDecorator = <T, V>(
context: Parameters<typeof consume>[0]["context"],
watchKey: string | undefined,
@@ -63,27 +86,52 @@ export const consumeEntityState = (config: ConsumeEntryConfig) =>
);
/**
* Like {@link consumeEntityState} but for an array of entity IDs at
* `entityIdPath`. Resolves to a `HassEntity[]` containing one entry per
* currently-available entity (missing entities and non-string IDs are
* filtered out; original order is preserved).
* Like {@link consumeEntityState} but for one or more entity IDs at
* `entityIdPath` (a string or string array; wrapped with {@link ensureArray}).
* Resolves to a record keyed by entity ID containing the currently-available
* entities (missing entities and non-string IDs are filtered out). Returns the
* previous record when none of the selected entities changed.
*/
export const consumeEntityStates = (config: ConsumeEntryConfig) =>
composeDecorator<HassEntities, HassEntity[]>(
statesContext,
config.entityIdPath[0],
function (states) {
const ids = resolveAtPath(this, config.entityIdPath);
if (!Array.isArray(ids) || !states) return undefined;
const result: HassEntity[] = [];
for (const id of ids) {
if (typeof id !== "string") continue;
const state = states[id];
if (state !== undefined) result.push(state);
}
return result;
export const consumeEntityStates = (config: ConsumeEntryConfig) => {
const watchKey = config.entityIdPath[0];
const buildRecord = function (this: unknown, states: HassEntities) {
const ids = ensureArray(resolveAtPath(this, config.entityIdPath));
if (!ids || !states) return undefined;
const result: Record<string, HassEntity> = {};
for (const id of ids) {
if (typeof id !== "string") continue;
const state = states[id];
if (state !== undefined) result[id] = state;
}
);
return result;
};
return (proto: unknown, propertyKey: string) => {
const key = String(propertyKey);
const transformDec = transform<
HassEntities,
Record<string, HassEntity> | undefined
>({
transformer: function (this: unknown, states: HassEntities) {
const next = buildRecord.call(this, states);
if (next === undefined) {
return undefined;
}
const previous = (this as Record<string, unknown>)[
`__transform_${key}`
] as Record<string, HassEntity> | undefined;
return preserveUnchangedEntityStatesRecord(previous, next);
},
watch: watchKey ? [watchKey] : [],
});
const consumeDec = consume<any>({
context: statesContext,
subscribe: true,
});
transformDec(proto as never, propertyKey);
consumeDec(proto as never, propertyKey);
};
};
/**
* Consumes `entitiesContext` and narrows it to the
@@ -6,7 +6,6 @@ import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { getGraphColorByIndex } from "../../common/color/colors";
import type { HaECOption } from "../../resources/echarts/echarts";
import type { HomeAssistant } from "../../types";
import "./ha-chart-base";
import "./ha-chart-tooltip-marker";
@@ -25,8 +24,6 @@ export interface SunburstNode {
@customElement("ha-sunburst-chart")
export class HaSunburstChart extends LitElement {
public hass!: HomeAssistant;
@property({ attribute: false }) public data?: SunburstNode;
@property({ attribute: false }) public valueFormatter?: (
+34 -2
View File
@@ -1,11 +1,16 @@
import type { RenderItemFunction } from "@lit-labs/virtualizer/virtualize";
import { mdiChartLine, mdiHelpCircleOutline, mdiShape } from "@mdi/js";
import {
mdiChartLine,
mdiHelpCircleOutline,
mdiPencil,
mdiShape,
} from "@mdi/js";
import type { HassEntity } from "home-assistant-js-websocket";
import { html, LitElement, nothing, type PropertyValues } from "lit";
import { customElement, property, query } from "lit/decorators";
import memoizeOne from "memoize-one";
import { ensureArray } from "../../common/array/ensure-array";
import { fireEvent } from "../../common/dom/fire_event";
import { type HASSDomEvent, fireEvent } from "../../common/dom/fire_event";
import { computeEntityNameList } from "../../common/entity/compute_entity_name_display";
import { computeStateName } from "../../common/entity/compute_state_name";
import { computeRTL } from "../../common/util/compute_rtl";
@@ -53,6 +58,16 @@ const SEARCH_KEYS = [
{ name: "id", weight: 2 },
];
export interface StatisticElementChangedEvent {
statisticId: string;
}
declare global {
interface HASSDomEvents {
"edit-statistics-element": StatisticElementChangedEvent;
}
}
@customElement("ha-statistic-picker")
export class HaStatisticPicker extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -130,6 +145,8 @@ export class HaStatisticPicker extends LitElement {
@query("ha-generic-picker") private _picker?: HaGenericPicker;
@property({ attribute: "can-edit", type: Boolean }) public canEdit?: boolean;
public willUpdate(changedProps: PropertyValues<this>) {
if (
(!this.hasUpdated && !this.statisticIds) ||
@@ -341,6 +358,15 @@ export class HaStatisticPicker extends LitElement {
${item.secondary
? html`<span slot="supporting-text">${item.secondary}</span>`
: nothing}
${this.canEdit
? html`<ha-icon-button
slot="end"
.value=${statisticId}
.label=${this.hass.localize("ui.common.edit")}
.path=${mdiPencil}
@click=${this._editItem}
></ha-icon-button>`
: nothing}
`;
}
@@ -350,6 +376,12 @@ export class HaStatisticPicker extends LitElement {
private _valueRenderer: PickerValueRenderer = this._makeValueRenderer();
private _editItem(ev: HASSDomEvent<StatisticElementChangedEvent>) {
ev.stopPropagation();
const statisticId = (ev.currentTarget as any).value;
fireEvent(this, "edit-statistics-element", { statisticId });
}
private _computeItem(statisticId: string): StatisticComboBoxItem {
const stateObj = this.hass.states[statisticId];
+17 -1
View File
@@ -1,9 +1,10 @@
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import { fireEvent } from "../../common/dom/fire_event";
import { type HASSDomEvent, fireEvent } from "../../common/dom/fire_event";
import type { ValueChangedEvent, HomeAssistant } from "../../types";
import "./ha-statistic-picker";
import type { StatisticElementChangedEvent } from "./ha-statistic-picker";
@customElement("ha-statistics-picker")
class HaStatisticsPicker extends LitElement {
@@ -59,6 +60,8 @@ class HaStatisticsPicker extends LitElement {
})
public ignoreRestrictionsOnFirstStatistic = false;
@property({ attribute: "can-edit", type: Boolean }) public canEdit?;
protected render() {
if (!this.hass) {
return nothing;
@@ -99,7 +102,9 @@ class HaStatisticsPicker extends LitElement {
.statisticIds=${this.statisticIds}
.excludeStatistics=${this.value}
.allowCustomEntity=${this.allowCustomEntity}
.canEdit=${this.canEdit}
@value-changed=${this._statisticChanged}
@edit-statistics-element=${this._editItem}
></ha-statistic-picker>
</div>
`
@@ -122,6 +127,17 @@ class HaStatisticsPicker extends LitElement {
`;
}
private _editItem(ev: HASSDomEvent<StatisticElementChangedEvent>) {
const statisticId = ev.detail.statisticId;
const index = this._currentStatistics!.findIndex((e) => e === statisticId);
fireEvent(this, "edit-detail-element", {
subElementConfig: {
index,
type: "row",
},
});
}
private get _currentStatistics() {
return this.value || [];
}
-3
View File
@@ -43,7 +43,6 @@ class StateInfo extends LitElement {
)}:
</span>
<ha-relative-time
.hass=${this.hass}
.datetime=${this.stateObj.last_changed}
capitalize
></ha-relative-time>
@@ -55,7 +54,6 @@ class StateInfo extends LitElement {
)}:
</span>
<ha-relative-time
.hass=${this.hass}
.datetime=${this.stateObj.last_updated}
capitalize
></ha-relative-time>
@@ -63,7 +61,6 @@ class StateInfo extends LitElement {
</ha-tooltip>
<ha-relative-time
id="relative-time"
.hass=${this.hass}
.datetime=${this.stateObj.last_changed}
capitalize
></ha-relative-time>
+27 -7
View File
@@ -1,18 +1,34 @@
import { consume } from "@lit/context";
import { addDays, differenceInMilliseconds, startOfDay } from "date-fns";
import type { HassConfig } from "home-assistant-js-websocket";
import type { PropertyValues } from "lit";
import { ReactiveElement } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { transform } from "../common/decorators/transform";
import { absoluteTime } from "../common/datetime/absolute_time";
import type { HomeAssistant } from "../types";
import { configContext, internationalizationContext } from "../data/context";
import type {
HomeAssistantConfig,
HomeAssistantInternationalization,
} from "../types";
const SAFE_MARGIN = 5 * 1000;
@customElement("ha-absolute-time")
class HaAbsoluteTime extends ReactiveElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public datetime?: string | Date;
@state()
@consume({ context: internationalizationContext, subscribe: true })
private _i18n?: HomeAssistantInternationalization;
@state()
@consume({ context: configContext, subscribe: true })
@transform<HomeAssistantConfig, HassConfig>({
transformer: ({ config }) => config,
})
private _config?: HassConfig;
private _timeout?: number;
public disconnectedCallback(): void {
@@ -62,13 +78,17 @@ class HaAbsoluteTime extends ReactiveElement {
}
private _updateAbsolute(): void {
if (!this._i18n || !this._config) {
return;
}
if (!this.datetime) {
this.innerHTML = this.hass.localize("ui.components.absolute_time.never");
this.innerHTML = this._i18n.localize("ui.components.absolute_time.never");
} else {
this.innerHTML = absoluteTime(
new Date(this.datetime),
this.hass.locale,
this.hass.config
this._i18n.locale,
this._config
);
}
}
+22 -7
View File
@@ -11,12 +11,15 @@ import {
mdiStateMachine,
mdiWeatherSunny,
} from "@mdi/js";
import { consume } from "@lit/context";
import { html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { until } from "lit/directives/until";
import type { HassConfig, Connection } from "home-assistant-js-websocket";
import { computeDomain } from "../common/entity/compute_domain";
import { transform } from "../common/decorators/transform";
import { configContext, connectionContext } from "../data/context";
import { conditionIcon, FALLBACK_DOMAIN_ICONS } from "../data/icons";
import type { HomeAssistant } from "../types";
import "./ha-icon";
import "./ha-svg-icon";
@@ -36,12 +39,24 @@ export const CONDITION_ICONS = {
@customElement("ha-condition-icon")
export class HaConditionIcon extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public condition?: string;
@property() public icon?: string;
@state()
@consume({ context: configContext, subscribe: true })
@transform<{ config: HassConfig }, HassConfig>({
transformer: ({ config }) => config,
})
private _config?: HassConfig;
@state()
@consume({ context: connectionContext, subscribe: true })
@transform<{ connection: Connection }, Connection>({
transformer: ({ connection }) => connection,
})
private _connection?: Connection;
protected render() {
if (this.icon) {
return html`<ha-icon .icon=${this.icon}></ha-icon>`;
@@ -51,13 +66,13 @@ export class HaConditionIcon extends LitElement {
return nothing;
}
if (!this.hass) {
if (!this._connection || !this._config) {
return this._renderFallback();
}
const icon = conditionIcon(
this.hass.connection,
this.hass.config,
this._connection,
this._config,
this.condition
).then((icn) => {
if (icn) {
+7 -1
View File
@@ -3,6 +3,8 @@ import { mdiFilterVariantRemove } from "@mdi/js";
import type { CSSResultGroup, PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { consumeLocalize } from "../common/decorators/consume-context-entry";
import type { LocalizeFunc } from "../common/translations/localize";
import { fireEvent } from "../common/dom/fire_event";
import { deepEqual } from "../common/util/deep-equal";
import type { Blueprints } from "../data/blueprint";
@@ -20,6 +22,10 @@ import "./ha-list";
export class HaFilterBlueprints extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state()
@consumeLocalize()
private _localize!: LocalizeFunc;
@property({ attribute: false }) public value?: string[];
@property() public type?: "automation" | "script";
@@ -54,7 +60,7 @@ export class HaFilterBlueprints extends LitElement {
@expanded-changed=${this._expandedChanged}
>
<div slot="header" class="header">
${this.hass.localize("ui.panel.config.blueprint.caption")}
${this._localize("ui.panel.config.blueprint.caption")}
${this.value?.length
? html`<div class="badge">${this.value?.length}</div>
<ha-icon-button
+7 -3
View File
@@ -4,6 +4,8 @@ import type { CSSResultGroup, PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import { consumeLocalize } from "../common/decorators/consume-context-entry";
import type { LocalizeFunc } from "../common/translations/localize";
import { fireEvent } from "../common/dom/fire_event";
import { haStyleScrollbar } from "../resources/styles";
import type { HomeAssistant } from "../types";
@@ -22,6 +24,10 @@ import "../panels/config/voice-assistants/expose/expose-assistant-icon";
export class HaFilterVoiceAssistants extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state()
@consumeLocalize()
private _localize!: LocalizeFunc;
// the list of selected voiceAssistantIds
@property({ attribute: false }) public value: string[] = [];
@@ -44,9 +50,7 @@ export class HaFilterVoiceAssistants extends LitElement {
@expanded-changed=${this._expandedChanged}
>
<div slot="header" class="header">
${this.hass.localize(
"ui.panel.config.dashboard.voice_assistants.main"
)}
${this._localize("ui.panel.config.dashboard.voice_assistants.main")}
${this.value?.length
? html`<div class="badge">${this.value?.length}</div>
<ha-icon-button
+14 -6
View File
@@ -1,19 +1,23 @@
import { consume } from "@lit/context";
import { parseISO } from "date-fns";
import type { PropertyValues } from "lit";
import { ReactiveElement } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { relativeTime } from "../common/datetime/relative_time";
import { capitalizeFirstLetter } from "../common/string/capitalize-first-letter";
import type { HomeAssistant } from "../types";
import { internationalizationContext } from "../data/context";
import type { HomeAssistantInternationalization } from "../types";
@customElement("ha-relative-time")
class HaRelativeTime extends ReactiveElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public datetime?: string | Date;
@property({ type: Boolean }) public capitalize = false;
@state()
@consume({ context: internationalizationContext, subscribe: true })
private _i18n?: HomeAssistantInternationalization;
private _interval?: number;
public disconnectedCallback(): void {
@@ -57,15 +61,19 @@ class HaRelativeTime extends ReactiveElement {
}
private _updateRelative(): void {
if (!this._i18n) {
return;
}
if (!this.datetime) {
this.innerHTML = this.hass.localize("ui.components.relative_time.never");
this.innerHTML = this._i18n.localize("ui.components.relative_time.never");
} else {
const date =
typeof this.datetime === "string"
? parseISO(this.datetime)
: this.datetime;
const relTime = relativeTime(date, this.hass.locale);
const relTime = relativeTime(date, this._i18n.locale);
this.innerHTML = this.capitalize
? capitalizeFirstLetter(relTime)
: relTime;
@@ -1,11 +1,13 @@
import type { HassEntity } from "home-assistant-js-websocket";
import type { PropertyValues } from "lit";
import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { ensureArray } from "../../common/array/ensure-array";
import { consumeEntityStates } from "../../common/decorators/consume-context-entry";
import { fireEvent } from "../../common/dom/fire_event";
import type { AttributeSelector } from "../../data/selector";
import type { HomeAssistant } from "../../types";
import "../entity/ha-entity-attribute-picker";
import { ensureArray } from "../../common/array/ensure-array";
@customElement("ha-selector-attribute")
export class HaSelectorAttribute extends LitElement {
@@ -27,6 +29,10 @@ export class HaSelectorAttribute extends LitElement {
filter_entity?: string | string[];
};
@state()
@consumeEntityStates({ entityIdPath: ["context", "filter_entity"] })
private _filterEntityStates?: Record<string, HassEntity>;
protected render() {
return html`
<ha-entity-attribute-picker
@@ -73,7 +79,7 @@ export class HaSelectorAttribute extends LitElement {
const entityIds = ensureArray(this.context.filter_entity);
invalid = !entityIds.some((entityId) => {
const stateObj = this.hass.states[entityId];
const stateObj = this._filterEntityStates?.[entityId];
return (
stateObj &&
this.value in stateObj.attributes &&
@@ -1,15 +1,12 @@
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import type { HomeAssistant } from "../../types";
import "../ha-formfield";
import "../ha-switch";
import "../ha-input-helper-text";
@customElement("ha-selector-boolean")
export class HaBooleanSelector extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Boolean }) public value = false;
@property() public placeholder?: any;
@@ -2,10 +2,12 @@ import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { consumeLocalize } from "../../common/decorators/consume-context-entry";
import { fireEvent } from "../../common/dom/fire_event";
import { isTemplate } from "../../common/string/has-template";
import type { ChooseSelector, Selector } from "../../data/selector";
import type { HomeAssistant } from "../../types";
import type { LocalizeFunc } from "../../common/translations/localize";
import "../ha-button-toggle-group";
import "./ha-selector";
@@ -28,6 +30,9 @@ export class HaChooseSelector extends LitElement {
@property({ type: Boolean }) public required = true;
@consumeLocalize()
protected _localize?: LocalizeFunc;
@state() public _activeChoice?: string;
protected willUpdate(changedProperties: PropertyValues<this>): void {
@@ -62,7 +67,7 @@ export class HaChooseSelector extends LitElement {
.buttons=${this._toggleButtons(
this.selector.choose.choices,
this.selector.choose.translation_key,
this.hass.localize
this._localize
)}
.active=${this._activeChoice}
@value-changed=${this._choiceChanged}
@@ -83,7 +88,7 @@ export class HaChooseSelector extends LitElement {
(
choices: ChooseSelector["choose"]["choices"],
translationKey?: string,
_localize?: HomeAssistant["localize"]
_localize?: LocalizeFunc
) =>
Object.keys(choices).map((choice) => ({
label:
@@ -2,14 +2,11 @@ import { html, LitElement } from "lit";
import { customElement, property, query } from "lit/decorators";
import memoizeOne from "memoize-one";
import type { DurationSelector } from "../../data/selector";
import type { HomeAssistant } from "../../types";
import "../ha-duration-input";
import type { HaDurationData, HaDurationInput } from "../ha-duration-input";
@customElement("ha-selector-duration")
export class HaTimeDuration extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public selector!: DurationSelector;
@property({ attribute: false }) public value?:
@@ -3,10 +3,12 @@ import type { PropertyValues } from "lit";
import { html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import { consumeLocalize } from "../../common/decorators/consume-context-entry";
import { removeFile, uploadFile } from "../../data/file_upload";
import type { FileSelector } from "../../data/selector";
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
import type { HomeAssistant } from "../../types";
import type { LocalizeFunc } from "../../common/translations/localize";
import "../ha-file-upload";
@customElement("ha-selector-file")
@@ -25,6 +27,9 @@ export class HaFileSelector extends LitElement {
@property({ type: Boolean }) public required = true;
@consumeLocalize()
protected _localize?: LocalizeFunc;
@state() private _filename?: { fileId: string; name: string };
@state() private _busy = false;
@@ -42,7 +47,7 @@ export class HaFileSelector extends LitElement {
.uploading=${this._busy}
.value=${this.value
? this._filename?.name ||
this.hass.localize("ui.components.selectors.file.unknown_file")
this._localize!("ui.components.selectors.file.unknown_file")
: undefined}
@file-picked=${this._uploadFile}
@change=${this._removeFile}
@@ -72,7 +77,7 @@ export class HaFileSelector extends LitElement {
fireEvent(this, "value-changed", { value: fileId });
} catch (err: any) {
showAlertDialog(this, {
text: this.hass.localize("ui.components.selectors.file.upload_failed", {
text: this._localize!("ui.components.selectors.file.upload_failed", {
reason: err.message || err,
}),
});
@@ -1,6 +1,7 @@
import { LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { consumeLocalize } from "../../common/decorators/consume-context-entry";
import { fireEvent } from "../../common/dom/fire_event";
import type { PeriodKey, PeriodSelector } from "../../data/selector";
import type { HomeAssistant } from "../../types";
@@ -41,6 +42,9 @@ export class HaPeriodSelector extends LitElement {
@property({ type: Boolean }) public required = true;
@consumeLocalize()
protected _localize?: LocalizeFunc;
private _schema = memoizeOne(
(
selectedPeriodKey: PeriodKey | undefined,
@@ -78,7 +82,7 @@ export class HaPeriodSelector extends LitElement {
const schema = this._schema(
typeof data.period === "string" ? (data.period as PeriodKey) : undefined,
this.selector,
this.hass.localize
this._localize!
);
return html`
@@ -2,6 +2,7 @@ import type { PropertyValues } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { consumeLocalize } from "../../common/decorators/consume-context-entry";
import { fireEvent } from "../../common/dom/fire_event";
import type {
LocalizeFunc,
@@ -168,6 +169,9 @@ export class HaSelectorSelector extends LitElement {
@property({ type: Boolean, reflect: true }) public required = true;
@consumeLocalize()
protected _localize?: LocalizeFunc;
private _yamlMode = false;
protected shouldUpdate(changedProps: PropertyValues<this>) {
@@ -236,7 +240,7 @@ export class HaSelectorSelector extends LitElement {
};
}
const schema = this._schema(type, this.hass.localize);
const schema = this._schema(type, this._localize!);
return html`<div>
<p>${this.label ? this.label : ""}</p>
@@ -290,7 +294,7 @@ export class HaSelectorSelector extends LitElement {
}
private _computeLabelCallback = (schema: any): string =>
this.hass.localize(
this._localize!(
`ui.components.selectors.selector.${schema.name}` as LocalizeKeys
) || schema.name;
+27 -14
View File
@@ -1,24 +1,39 @@
import { consume } from "@lit/context";
import { html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { until } from "lit/directives/until";
import type { Connection, HassConfig } from "home-assistant-js-websocket";
import { computeDomain } from "../common/entity/compute_domain";
import { transform } from "../common/decorators/transform";
import { configContext, connectionContext } from "../data/context";
import {
DEFAULT_SERVICE_ICON,
FALLBACK_DOMAIN_ICONS,
serviceIcon,
} from "../data/icons";
import type { HomeAssistant } from "../types";
import "./ha-icon";
import "./ha-svg-icon";
@customElement("ha-service-icon")
export class HaServiceIcon extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public service?: string;
@property() public icon?: string;
@state()
@consume({ context: configContext, subscribe: true })
@transform<{ config: HassConfig }, HassConfig>({
transformer: ({ config }) => config,
})
private _config?: HassConfig;
@state()
@consume({ context: connectionContext, subscribe: true })
@transform<{ connection: Connection }, Connection>({
transformer: ({ connection }) => connection,
})
private _connection?: Connection;
protected render() {
if (this.icon) {
return html`<ha-icon .icon=${this.icon}></ha-icon>`;
@@ -28,20 +43,18 @@ export class HaServiceIcon extends LitElement {
return nothing;
}
if (!this.hass) {
if (!this._connection || !this._config) {
return this._renderFallback();
}
const icon = serviceIcon(
this.hass.connection,
this.hass.config,
this.service
).then((icn) => {
if (icn) {
return html`<ha-icon .icon=${icn}></ha-icon>`;
const icon = serviceIcon(this._connection, this._config, this.service).then(
(icn) => {
if (icn) {
return html`<ha-icon .icon=${icn}></ha-icon>`;
}
return this._renderFallback();
}
return this._renderFallback();
});
);
return html`${until(icon)}`;
}
+23 -8
View File
@@ -1,21 +1,36 @@
import { consume } from "@lit/context";
import { html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { until } from "lit/directives/until";
import type { HomeAssistant } from "../types";
import type { Connection, HassConfig } from "home-assistant-js-websocket";
import { transform } from "../common/decorators/transform";
import { configContext, connectionContext } from "../data/context";
import { serviceSectionIcon } from "../data/icons";
import "./ha-icon";
import "./ha-svg-icon";
import { serviceSectionIcon } from "../data/icons";
@customElement("ha-service-section-icon")
export class HaServiceSectionIcon extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public service?: string;
@property() public section?: string;
@property() public icon?: string;
@state()
@consume({ context: configContext, subscribe: true })
@transform<{ config: HassConfig }, HassConfig>({
transformer: ({ config }) => config,
})
private _config?: HassConfig;
@state()
@consume({ context: connectionContext, subscribe: true })
@transform<{ connection: Connection }, Connection>({
transformer: ({ connection }) => connection,
})
private _connection?: Connection;
protected render() {
if (this.icon) {
return html`<ha-icon .icon=${this.icon}></ha-icon>`;
@@ -25,13 +40,13 @@ export class HaServiceSectionIcon extends LitElement {
return nothing;
}
if (!this.hass) {
if (!this._connection || !this._config) {
return this._renderFallback();
}
const icon = serviceSectionIcon(
this.hass.connection,
this.hass.config,
this._connection,
this._config,
this.service,
this.section
).then((icn) => {
+1
View File
@@ -22,6 +22,7 @@ export const haTopAppBarFixedStyles = css`
padding-top: var(--safe-area-inset-top);
padding-right: var(--safe-area-inset-right);
transition:
box-shadow var(--ha-animation-duration-short) ease,
width var(--ha-animation-duration-normal) ease,
padding-left var(--ha-animation-duration-normal) ease,
padding-right var(--ha-animation-duration-normal) ease;
+27 -14
View File
@@ -17,13 +17,16 @@ import {
mdiWeatherSunny,
mdiWebhook,
} from "@mdi/js";
import { consume } from "@lit/context";
import { html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { until } from "lit/directives/until";
import type { Connection, HassConfig } from "home-assistant-js-websocket";
import { computeDomain } from "../common/entity/compute_domain";
import { transform } from "../common/decorators/transform";
import { configContext, connectionContext } from "../data/context";
import { FALLBACK_DOMAIN_ICONS, triggerIcon } from "../data/icons";
import { mdiHomeAssistant } from "../resources/home-assistant-logo-svg";
import type { HomeAssistant } from "../types";
import "./ha-icon";
import "./ha-svg-icon";
@@ -50,12 +53,24 @@ export const TRIGGER_ICONS = {
@customElement("ha-trigger-icon")
export class HaTriggerIcon extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public trigger?: string;
@property() public icon?: string;
@state()
@consume({ context: configContext, subscribe: true })
@transform<{ config: HassConfig }, HassConfig>({
transformer: ({ config }) => config,
})
private _config?: HassConfig;
@state()
@consume({ context: connectionContext, subscribe: true })
@transform<{ connection: Connection }, Connection>({
transformer: ({ connection }) => connection,
})
private _connection?: Connection;
protected render() {
if (this.icon) {
return html`<ha-icon .icon=${this.icon}></ha-icon>`;
@@ -65,20 +80,18 @@ export class HaTriggerIcon extends LitElement {
return nothing;
}
if (!this.hass) {
if (!this._connection || !this._config) {
return this._renderFallback();
}
const icon = triggerIcon(
this.hass.connection,
this.hass.config,
this.trigger
).then((icn) => {
if (icn) {
return html`<ha-icon .icon=${icn}></ha-icon>`;
const icon = triggerIcon(this._connection, this._config, this.trigger).then(
(icn) => {
if (icn) {
return html`<ha-icon .icon=${icn}></ha-icon>`;
}
return this._renderFallback();
}
return this._renderFallback();
});
);
return html`${until(icon)}`;
}
+7 -5
View File
@@ -1,6 +1,8 @@
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { consumeLocalize } from "../../common/decorators/consume-context-entry";
import type { LocalizeFunc } from "../../common/translations/localize";
import type { LogbookEntry } from "../../data/logbook";
import type { HomeAssistant } from "../../types";
import "./hat-logbook-note";
@@ -17,6 +19,9 @@ export class HaTraceLogbook extends LitElement {
@property({ attribute: false }) public logbookEntries!: LogbookEntry[];
@consumeLocalize()
private _localize!: LocalizeFunc;
protected render(): TemplateResult {
return this.logbookEntries.length
? html`
@@ -26,13 +31,10 @@ export class HaTraceLogbook extends LitElement {
.entries=${this.logbookEntries}
.narrow=${this.narrow}
></ha-logbook-renderer>
<hat-logbook-note
.hass=${this.hass}
.domain=${this.trace.domain}
></hat-logbook-note>
<hat-logbook-note .domain=${this.trace.domain}></hat-logbook-note>
`
: html`<div class="padded-box">
${this.hass.localize(
${this._localize(
"ui.panel.config.automation.trace.path.no_logbook_entries"
)}
</div>`;
@@ -374,10 +374,7 @@ export class HaTracePathDetails extends LitElement {
.entries=${entries}
.narrow=${this.narrow}
></ha-logbook-renderer>
<hat-logbook-note
.hass=${this.hass}
.domain=${this.trace.domain}
></hat-logbook-note>
<hat-logbook-note .domain=${this.trace.domain}></hat-logbook-note>
`
: html`<div class="padded-box">
${this.hass!.localize(
+1 -4
View File
@@ -28,10 +28,7 @@ export class HaTraceTimeline extends LitElement {
allow-pick
>
</hat-trace-timeline>
<hat-logbook-note
.hass=${this.hass}
.domain=${this.trace.domain}
></hat-logbook-note>
<hat-logbook-note .domain=${this.trace.domain}></hat-logbook-note>
`;
}
+7 -5
View File
@@ -1,20 +1,22 @@
import { css, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import type { HomeAssistant } from "../../types";
import { consumeLocalize } from "../../common/decorators/consume-context-entry";
import type { LocalizeFunc } from "../../common/translations/localize";
@customElement("hat-logbook-note")
class HatLogbookNote extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public domain: "automation" | "script" = "automation";
@consumeLocalize()
private _localize!: LocalizeFunc;
render() {
if (this.domain === "script") {
return this.hass.localize(
return this._localize(
"ui.panel.config.automation.trace.messages.not_all_entries_are_related_script_note"
);
}
return this.hass.localize(
return this._localize(
"ui.panel.config.automation.trace.messages.not_all_entries_are_related_automation_note"
);
}
+1
View File
@@ -256,6 +256,7 @@ export const normalizeSubscriptionEventData = (
dtstart: eventStart,
dtend: eventEnd,
description: eventData.description ?? undefined,
location: eventData.location ?? undefined,
uid: eventData.uid ?? undefined,
recurrence_id: eventData.recurrence_id ?? undefined,
rrule: eventData.rrule ?? undefined,
@@ -54,14 +54,12 @@ export class HaMoreInfoStateHeader extends LitElement {
${this._absoluteTime
? html`
<ha-absolute-time
.hass=${this.hass}
.datetime=${this.changedOverride ??
this.stateObj.last_changed}
></ha-absolute-time>
`
: html`
<ha-relative-time
.hass=${this.hass}
.datetime=${this.changedOverride ??
this.stateObj.last_changed}
capitalize
@@ -23,7 +23,6 @@ class MoreInfoAutomation extends LitElement {
<div class="flex">
<div>${this.hass.localize("ui.card.automation.last_triggered")}:</div>
<ha-relative-time
.hass=${this.hass}
.datetime=${this.stateObj.attributes.last_triggered}
capitalize
></ha-relative-time>
@@ -36,7 +36,6 @@ class MoreInfoSun extends LitElement {
)}</span
>
<ha-relative-time
.hass=${this.hass}
.datetime=${item === "ris" ? risingDate : settingDate}
></ha-relative-time>
</div>
@@ -201,7 +201,6 @@ class MoreInfoWeather extends LitElement {
<div class="time-ago">
<ha-relative-time
id="relative-time"
.hass=${this.hass}
.datetime=${this.stateObj.last_changed}
capitalize
></ha-relative-time>
@@ -213,7 +212,6 @@ class MoreInfoWeather extends LitElement {
)}:
</span>
<ha-relative-time
.hass=${this.hass}
.datetime=${this.stateObj.last_changed}
capitalize
></ha-relative-time>
@@ -225,7 +223,6 @@ class MoreInfoWeather extends LitElement {
)}:
</span>
<ha-relative-time
.hass=${this.hass}
.datetime=${this.stateObj.last_updated}
capitalize
></ha-relative-time>
@@ -30,7 +30,6 @@ export class HuiPersistentNotificationItem extends LitElement {
<span>
<ha-relative-time
id="relative-time"
.hass=${this.hass}
.datetime=${this.notification.created_at}
capitalize
></ha-relative-time>
@@ -48,8 +48,6 @@ import type { AreaRegistryDetailDialogParams } from "./show-dialog-area-registry
const cropOptions: CropOptions = {
round: false,
type: "image/jpeg",
quality: 0.75,
};
const SENSOR_DOMAINS = ["sensor"];
@@ -40,37 +40,6 @@ export class HaSunCondition extends LitElement implements ConditionElement {
private _schema = memoizeOne(
(localize: LocalizeFunc, formType: FormType) =>
[
...(["between", "before"].includes(formType)
? [
{
name: "before",
type: "select",
default: BEFORE_DEFAULT,
options: [
[
"sunrise",
localize(
"ui.panel.config.automation.editor.conditions.type.sun.sunrise"
),
],
[
"sunset",
localize(
"ui.panel.config.automation.editor.conditions.type.sun.sunset"
),
],
],
},
{
name: "before_offset",
selector: {
duration: {
allow_negative: true,
},
},
},
]
: []),
...(["between", "after"].includes(formType)
? [
{
@@ -102,6 +71,37 @@ export class HaSunCondition extends LitElement implements ConditionElement {
},
]
: []),
...(["between", "before"].includes(formType)
? [
{
name: "before",
type: "select",
default: BEFORE_DEFAULT,
options: [
[
"sunrise",
localize(
"ui.panel.config.automation.editor.conditions.type.sun.sunrise"
),
],
[
"sunset",
localize(
"ui.panel.config.automation.editor.conditions.type.sun.sunset"
),
],
],
},
{
name: "before_offset",
selector: {
duration: {
allow_negative: true,
},
},
},
]
: []),
] as const
);
@@ -142,26 +142,6 @@ export class DialogEnergyBatterySettings
>
${this._error ? html`<p class="error">${this._error}</p>` : nothing}
<ha-statistic-picker
.hass=${this.hass}
.helpMissingEntityUrl=${energyStatisticHelpUrl}
.includeUnitClass=${energyUnitClasses}
.value=${this._source.stat_energy_to}
.label=${this.hass.localize(
"ui.panel.config.energy.battery.dialog.energy_into_battery"
)}
.excludeStatistics=${[
...(this._excludeList || []),
this._source.stat_energy_from,
]}
@value-changed=${this._statisticToChanged}
.helper=${this.hass.localize(
"ui.panel.config.energy.battery.dialog.energy_helper_into",
{ unit: this._energy_units?.join(", ") || "" }
)}
autofocus
></ha-statistic-picker>
<ha-statistic-picker
.hass=${this.hass}
.helpMissingEntityUrl=${energyStatisticHelpUrl}
@@ -179,6 +159,26 @@ export class DialogEnergyBatterySettings
"ui.panel.config.energy.battery.dialog.energy_helper_out",
{ unit: this._energy_units?.join(", ") || "" }
)}
autofocus
></ha-statistic-picker>
<ha-statistic-picker
.hass=${this.hass}
.helpMissingEntityUrl=${energyStatisticHelpUrl}
.includeUnitClass=${energyUnitClasses}
.value=${this._source.stat_energy_to}
.label=${this.hass.localize(
"ui.panel.config.energy.battery.dialog.energy_into_battery"
)}
.excludeStatistics=${[
...(this._excludeList || []),
this._source.stat_energy_from,
]}
@value-changed=${this._statisticToChanged}
.helper=${this.hass.localize(
"ui.panel.config.energy.battery.dialog.energy_helper_into",
{ unit: this._energy_units?.join(", ") || "" }
)}
></ha-statistic-picker>
<ha-input
@@ -173,7 +173,6 @@ export class BluetoothAdvertisementMonitorPanel extends LitElement {
defaultHidden: false,
template: (ad) =>
html`<ha-relative-time
.hass=${this.hass}
.datetime=${ad.datetime}
capitalize
></ha-relative-time>`,
@@ -78,7 +78,6 @@ export class StorageBreakdownChart extends LitElement {
)}
></ha-segmented-bar>`
: html`<ha-sunburst-chart
.hass=${this.hass}
.data=${this._transformToSunburstData(this.storageInfo!)}
.valueFormatter=${this._formatBytes}
></ha-sunburst-chart>`}
-1
View File
@@ -101,7 +101,6 @@ export class HaConfigTags extends SubscribeMixin(LitElement) {
template: (tag) => html`
${tag.last_scanned_datetime
? html`<ha-relative-time
.hass=${this.hass}
.datetime=${tag.last_scanned_datetime}
capitalize
></ha-relative-time>`
@@ -238,7 +238,6 @@ class HaLogbookRenderer extends LitElement {
>
-
<ha-relative-time
.hass=${this.hass}
.datetime=${item.when * 1000}
capitalize
></ha-relative-time>
@@ -1,3 +1,5 @@
import { consume } from "@lit/context";
import type { ContextType } from "@lit/context";
import { mdiFire } from "@mdi/js";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type { PropertyValues } from "lit";
@@ -6,13 +8,20 @@ import { customElement, property, state } from "lit/decorators";
import "../../../../components/ha-badge";
import "../../../../components/ha-svg-icon";
import { formatNumber } from "../../../../common/number/format_number";
import {
internationalizationContext,
statesContext,
} from "../../../../data/context";
import type { EnergyData } from "../../../../data/energy";
import {
computeTotalFlowRate,
getEnergyDataCollection,
} from "../../../../data/energy";
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
import type { HomeAssistant } from "../../../../types";
import type {
HomeAssistant,
HomeAssistantInternationalization,
} from "../../../../types";
import type { LovelaceBadge } from "../../types";
import type { GasTotalBadgeConfig } from "../types";
@@ -23,6 +32,14 @@ export class HuiGasTotalBadge
{
@property({ attribute: false }) public hass!: HomeAssistant;
@state()
@consume({ context: statesContext, subscribe: true })
private _states!: ContextType<typeof statesContext>;
@state()
@consume({ context: internationalizationContext, subscribe: true })
private _i18n?: HomeAssistantInternationalization;
@state() private _config?: GasTotalBadgeConfig;
@state() private _data?: EnergyData;
@@ -50,14 +67,16 @@ export class HuiGasTotalBadge
return true;
}
if (changedProps.has("hass")) {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || !this._entities.size) {
if (changedProps.has("_states")) {
const oldStates = changedProps.get("_states") as
| ContextType<typeof statesContext>
| undefined;
if (!oldStates || !this._entities.size) {
return true;
}
for (const entityId of this._entities) {
if (oldHass.states[entityId] !== this.hass?.states[entityId]) {
if (oldStates[entityId] !== this._states?.[entityId]) {
return true;
}
}
@@ -67,21 +86,21 @@ export class HuiGasTotalBadge
}
protected render() {
if (!this._config || !this._data) {
if (!this._config || !this._data || !this._i18n) {
return nothing;
}
const { value, unit } = computeTotalFlowRate(
"gas",
this._data.prefs,
this.hass.states,
this._states,
this._entities
);
const displayValue = `${formatNumber(value, this.hass.locale, { maximumFractionDigits: 1 })} ${unit}`;
const displayValue = `${formatNumber(value, this._i18n.locale, { maximumFractionDigits: 1 })} ${unit}`;
const name =
this._config.title ||
this.hass.localize("ui.panel.lovelace.cards.energy.gas_total_title");
this._i18n.localize("ui.panel.lovelace.cards.energy.gas_total_title");
return html`
<ha-badge .label=${name}>
@@ -1,3 +1,5 @@
import { consume } from "@lit/context";
import type { ContextType } from "@lit/context";
import { mdiHomeLightningBolt } from "@mdi/js";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type { PropertyValues } from "lit";
@@ -6,13 +8,20 @@ import { customElement, property, state } from "lit/decorators";
import { formatNumber } from "../../../../common/number/format_number";
import "../../../../components/ha-badge";
import "../../../../components/ha-svg-icon";
import {
internationalizationContext,
statesContext,
} from "../../../../data/context";
import type { EnergyData, EnergyPreferences } from "../../../../data/energy";
import {
getEnergyDataCollection,
getPowerFromState,
} from "../../../../data/energy";
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
import type { HomeAssistant } from "../../../../types";
import type {
HomeAssistant,
HomeAssistantInternationalization,
} from "../../../../types";
import type { LovelaceBadge } from "../../types";
import type { PowerTotalBadgeConfig } from "../types";
@@ -23,6 +32,14 @@ export class HuiPowerTotalBadge
{
@property({ attribute: false }) public hass!: HomeAssistant;
@state()
@consume({ context: statesContext, subscribe: true })
private _states!: ContextType<typeof statesContext>;
@state()
@consume({ context: internationalizationContext, subscribe: true })
private _i18n?: HomeAssistantInternationalization;
@state() private _config?: PowerTotalBadgeConfig;
@state() private _data?: EnergyData;
@@ -50,14 +67,16 @@ export class HuiPowerTotalBadge
return true;
}
if (changedProps.has("hass")) {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || !this._entities.size) {
if (changedProps.has("_states")) {
const oldStates = changedProps.get("_states") as
| ContextType<typeof statesContext>
| undefined;
if (!oldStates || !this._entities.size) {
return true;
}
for (const entityId of this._entities) {
if (oldHass.states[entityId] !== this.hass?.states[entityId]) {
if (oldStates[entityId] !== this._states?.[entityId]) {
return true;
}
}
@@ -68,7 +87,7 @@ export class HuiPowerTotalBadge
private _getCurrentPower(entityId: string): number {
this._entities.add(entityId);
return getPowerFromState(this.hass.states[entityId]) ?? 0;
return getPowerFromState(this._states[entityId]) ?? 0;
}
private _computeTotalPower(prefs: EnergyPreferences): number {
@@ -100,7 +119,7 @@ export class HuiPowerTotalBadge
}
protected render() {
if (!this._config || !this._data) {
if (!this._config || !this._data || !this._i18n) {
return nothing;
}
@@ -108,18 +127,18 @@ export class HuiPowerTotalBadge
let displayValue: string;
if (power >= 1000) {
displayValue = `${formatNumber(power / 1000, this.hass.locale, {
displayValue = `${formatNumber(power / 1000, this._i18n.locale, {
maximumFractionDigits: 2,
})} kW`;
} else {
displayValue = `${formatNumber(power, this.hass.locale, {
displayValue = `${formatNumber(power, this._i18n.locale, {
maximumFractionDigits: 0,
})} W`;
}
const name =
this._config.title ||
this.hass.localize("ui.panel.lovelace.cards.energy.power_total_title");
this._i18n.localize("ui.panel.lovelace.cards.energy.power_total_title");
return html`
<ha-badge .label=${name}>
@@ -1,3 +1,5 @@
import { consume } from "@lit/context";
import type { ContextType } from "@lit/context";
import { mdiWater } from "@mdi/js";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type { PropertyValues } from "lit";
@@ -6,13 +8,20 @@ import { customElement, property, state } from "lit/decorators";
import "../../../../components/ha-badge";
import "../../../../components/ha-svg-icon";
import { formatNumber } from "../../../../common/number/format_number";
import {
internationalizationContext,
statesContext,
} from "../../../../data/context";
import type { EnergyData } from "../../../../data/energy";
import {
computeTotalFlowRate,
getEnergyDataCollection,
} from "../../../../data/energy";
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
import type { HomeAssistant } from "../../../../types";
import type {
HomeAssistant,
HomeAssistantInternationalization,
} from "../../../../types";
import type { LovelaceBadge } from "../../types";
import type { WaterTotalBadgeConfig } from "../types";
@@ -23,6 +32,14 @@ export class HuiWaterTotalBadge
{
@property({ attribute: false }) public hass!: HomeAssistant;
@state()
@consume({ context: statesContext, subscribe: true })
private _states!: ContextType<typeof statesContext>;
@state()
@consume({ context: internationalizationContext, subscribe: true })
private _i18n?: HomeAssistantInternationalization;
@state() private _config?: WaterTotalBadgeConfig;
@state() private _data?: EnergyData;
@@ -50,14 +67,16 @@ export class HuiWaterTotalBadge
return true;
}
if (changedProps.has("hass")) {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || !this._entities.size) {
if (changedProps.has("_states")) {
const oldStates = changedProps.get("_states") as
| ContextType<typeof statesContext>
| undefined;
if (!oldStates || !this._entities.size) {
return true;
}
for (const entityId of this._entities) {
if (oldHass.states[entityId] !== this.hass?.states[entityId]) {
if (oldStates[entityId] !== this._states?.[entityId]) {
return true;
}
}
@@ -67,21 +86,21 @@ export class HuiWaterTotalBadge
}
protected render() {
if (!this._config || !this._data) {
if (!this._config || !this._data || !this._i18n) {
return nothing;
}
const { value, unit } = computeTotalFlowRate(
"water",
this._data.prefs,
this.hass.states,
this._states,
this._entities
);
const displayValue = `${formatNumber(value, this.hass.locale, { maximumFractionDigits: 1 })} ${unit}`;
const displayValue = `${formatNumber(value, this._i18n.locale, { maximumFractionDigits: 1 })} ${unit}`;
const name =
this._config.title ||
this.hass.localize("ui.panel.lovelace.cards.energy.water_total_title");
this._i18n.localize("ui.panel.lovelace.cards.energy.water_total_title");
return html`
<ha-badge .label=${name}>
@@ -108,10 +108,12 @@ class HuiCounterActionsCardFeature
return null;
}
const actions = this._config?.actions ?? COUNTER_ACTIONS;
return html`
<ha-control-button-group>
${this._config?.actions
?.filter((action) => COUNTER_ACTIONS.includes(action))
${actions
.filter((action) => COUNTER_ACTIONS.includes(action))
.map((action) => {
const button = COUNTER_ACTIONS_BUTTON[action](this._stateObj!);
return html`
@@ -33,8 +33,10 @@ const DOMAIN_VARIANTS: Record<string, TileVariant[]> = {
TILE_VARIANT,
["cover-open-close"],
["cover-position"],
["cover-position-favorite"],
["cover-tilt"],
["cover-tilt-position"],
["cover-tilt-favorite"],
],
climate: [
TILE_VARIANT,
@@ -71,7 +73,12 @@ const DOMAIN_VARIANTS: Record<string, TileVariant[]> = {
],
vacuum: [TILE_VARIANT, ["vacuum-commands"]],
lawn_mower: [TILE_VARIANT, ["lawn-mower-commands"]],
valve: [TILE_VARIANT, ["valve-open-close"], ["valve-position"]],
valve: [
TILE_VARIANT,
["valve-open-close"],
["valve-position"],
["valve-position-favorite"],
],
alarm_control_panel: [TILE_VARIANT, ["alarm-modes"]],
counter: [TILE_VARIANT, ["counter-actions"]],
input_select: SELECT_VARIANTS,
@@ -161,7 +161,8 @@ export class HuiEnergyDevicesDetailGraphCard
UNIT,
this._compareStart,
this._compareEnd,
this._yAxisFractionDigits
this._yAxisFractionDigits,
this._legendData
)}
click-label-for-more-info
@dataset-hidden=${this._datasetHidden}
@@ -215,7 +216,8 @@ export class HuiEnergyDevicesDetailGraphCard
unit: string | undefined,
compareStart: Date | undefined,
compareEnd: Date | undefined,
yAxisFractionDigits: number
yAxisFractionDigits: number,
legendData: CustomLegendOption["data"]
): HaECOption => {
const commonOptions = getCommonOptions(
start,
@@ -230,8 +232,8 @@ export class HuiEnergyDevicesDetailGraphCard
yAxisFractionDigits
);
const selected = this._legendData
? this._legendData
const selected = legendData
? legendData
.filter(
(d) =>
d.id && this._hiddenStats.includes(this._getStatIdFromId(d.id))
@@ -247,7 +249,7 @@ export class HuiEnergyDevicesDetailGraphCard
legend: {
show: true,
type: "custom",
data: this._legendData,
data: legendData,
selected,
},
grid: {
@@ -311,7 +311,6 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
: entityConf.show_last_changed
? html`
<ha-relative-time
.hass=${this.hass}
.datetime=${stateObj.last_changed}
capitalize
></ha-relative-time>
@@ -657,7 +657,6 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard {
)
: html`<ha-relative-time
capitalize
.hass=${this.hass}
.datetime=${due}
></ha-relative-time>`}
</div>`
@@ -155,7 +155,7 @@ export class HuiActionEditor extends LitElement {
this.defaultAction
? ` (${this.hass!.localize(
`ui.panel.lovelace.editor.action-editor.actions.${this.defaultAction}`
).toLowerCase()})`
)})`
: ""
}`,
};
@@ -1,8 +1,13 @@
import { consume } from "@lit/context";
import type { HassEntities, HassEntity } from "home-assistant-js-websocket";
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, state, property } from "lit/decorators";
import { preserveUnchangedEntityStatesRecord } from "../../../common/decorators/consume-context-entry";
import { transform } from "../../../common/decorators/transform";
import { computeStateName } from "../../../common/entity/compute_state_name";
import "../../../components/entity/state-badge";
import { statesContext } from "../../../data/context";
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
import type { HomeAssistant } from "../../../types";
import type { EntitiesCardEntityConfig } from "../cards/types";
@@ -17,21 +22,38 @@ import { haStyleScrollbar } from "../../../resources/styles";
@customElement("hui-buttons-base")
export class HuiButtonsBase extends LitElement {
@state() public hass!: HomeAssistant;
@property({ attribute: false })
public hass!: HomeAssistant;
@property({ attribute: false })
public configEntities?: EntitiesCardEntityConfig[];
@state()
@consume({ context: statesContext, subscribe: true })
@transform<HassEntities, Record<string, HassEntity | undefined>>({
transformer: function (this: HuiButtonsBase, states) {
const next: Record<string, HassEntity | undefined> = {};
if (states) {
for (const entityConf of this.configEntities || []) {
next[entityConf.entity] = states[entityConf.entity];
}
}
return preserveUnchangedEntityStatesRecord(this._entityStates, next);
},
watch: ["configEntities"],
})
private _entityStates: Record<string, HassEntity | undefined> = {};
protected render(): TemplateResult {
return html`
<ha-chip-set class="ha-scrollbar">
${(this.configEntities || []).map((entityConf) => {
const stateObj = this.hass.states[entityConf.entity];
const stateObj = this._entityStates[entityConf.entity];
const name =
(entityConf.show_name && stateObj) ||
(entityConf.name && entityConf.show_name !== false)
? entityConf.name || computeStateName(stateObj)
? entityConf.name || (stateObj ? computeStateName(stateObj) : "")
: "";
return html`
@@ -115,7 +115,6 @@ export class HuiGenericEntityRow extends LitElement {
</ha-tooltip>
<ha-relative-time
id="last-changed${this._secondaryInfoElementId}"
.hass=${this.hass}
.datetime=${stateObj.last_changed}
capitalize
></ha-relative-time>
@@ -136,7 +135,6 @@ export class HuiGenericEntityRow extends LitElement {
<ha-relative-time
id="last-updated${this
._secondaryInfoElementId}"
.hass=${this.hass}
.datetime=${stateObj.last_updated}
capitalize
></ha-relative-time>
@@ -160,7 +158,6 @@ export class HuiGenericEntityRow extends LitElement {
<ha-relative-time
id="last-triggered${this
._secondaryInfoElementId}"
.hass=${this.hass}
.datetime=${stateObj.attributes
.last_triggered}
capitalize
@@ -219,7 +219,6 @@ export class HuiEntityPickerTable extends LitElement {
hidden: narrow,
template: (entity) => html`
<ha-relative-time
.hass=${this.hass!}
.datetime=${entity.last_changed}
capitalize
></ha-relative-time>
@@ -15,7 +15,10 @@ import {
union,
} from "superstruct";
import { ensureArray } from "../../../../common/array/ensure-array";
import { fireEvent } from "../../../../common/dom/fire_event";
import {
type HASSDomEvent,
fireEvent,
} from "../../../../common/dom/fire_event";
import type { LocalizeFunc } from "../../../../common/translations/localize";
import { deepEqual } from "../../../../common/util/deep-equal";
import { supportedStatTypeMap } from "../../../../components/chart/statistics-chart";
@@ -32,13 +35,19 @@ import {
isExternalStatistic,
statisticsMetaHasType,
} from "../../../../data/recorder";
import type { EntityConfig } from "../../entity-rows/types";
import type { HomeAssistant } from "../../../../types";
import { DEFAULT_DAYS_TO_SHOW } from "../../cards/hui-statistics-graph-card";
import type { StatisticsGraphCardConfig } from "../../cards/types";
import type {
GraphEntityConfig,
StatisticsGraphCardConfig,
} from "../../cards/types";
import { processConfigEntities } from "../../common/process-config-entities";
import type { LovelaceCardEditor } from "../../types";
import "../hui-sub-element-editor";
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import { graphEntitiesConfigStruct } from "../structs/entities-struct";
import type { EditDetailElementEvent, SubElementEditorConfig } from "../types";
import { orderPropertiesGraphCard } from "./order-properties/order-properties-graph";
const statTypeStruct = union([
@@ -114,6 +123,8 @@ export class HuiStatisticsGraphCardEditor
@state() private _metaDatas?: StatisticsMetaData[];
@state() private _subElementEditorConfig?: SubElementEditorConfig;
public setConfig(config: StatisticsGraphCardConfig): void {
assert(config, cardConfigStruct);
this._config = config;
@@ -334,11 +345,54 @@ export class HuiStatisticsGraphCardEditor
}
);
private _subForm = memoizeOne((localize: LocalizeFunc) => ({
schema: [
{ name: "entity", required: true, selector: { statistic: {} } },
{
name: "name",
selector: { entity_name: {} },
context: {
entity: "entity",
},
},
{
name: "color",
selector: { ui_color: {} },
},
] as const,
computeLabel: (item: HaFormSchema) => {
switch (item.name) {
case "entity":
return localize(
"ui.panel.lovelace.editor.card.statistics-graph.picked_statistic"
);
case "name":
case "color":
return localize(`ui.panel.lovelace.editor.card.generic.${item.name}`);
default:
return undefined;
}
},
}));
protected render() {
if (!this.hass || !this._config) {
return nothing;
}
if (this._subElementEditorConfig) {
return html`
<hui-sub-element-editor
.hass=${this.hass}
.config=${this._subElementEditorConfig}
.form=${this._subForm(this.hass.localize)}
@go-back=${this._goBack}
@config-changed=${this._handleSubEntityChanged}
>
</hui-sub-element-editor>
`;
}
const schema = this._schema(
this.hass.localize,
this._configEntities,
@@ -388,11 +442,29 @@ export class HuiStatisticsGraphCardEditor
.ignoreRestrictionsOnFirstStatistic=${true}
.value=${this._configEntities}
.configValue=${"entities"}
can-edit
@value-changed=${this._entitiesChanged}
@edit-detail-element=${this._editDetailElement}
></ha-statistics-picker>
`;
}
private _goBack(): void {
this._subElementEditorConfig = undefined;
}
private _editDetailElement(ev: HASSDomEvent<EditDetailElementEvent>): void {
const index = ev.detail.subElementConfig.index!;
let elementConfig = this._config!.entities[index];
if (typeof elementConfig === "string") {
elementConfig = { entity: elementConfig };
}
this._subElementEditorConfig = {
...ev.detail.subElementConfig,
...{ elementConfig: elementConfig as EntityConfig },
};
}
private _valueChanged(ev: CustomEvent): void {
const config = this._orderProperties(ev.detail.value);
fireEvent(this, "config-changed", { config });
@@ -410,15 +482,66 @@ export class HuiStatisticsGraphCardEditor
});
let config = { ...this._config!, entities: newEntities };
// remove inappropriate stat options dependently on entities
config = await this._cleanConfig(config);
// normalize a generated yaml code
config = this._orderProperties(config);
fireEvent(this, "config-changed", {
config,
});
}
private async _handleSubEntityChanged(ev: CustomEvent): Promise<void> {
ev.stopPropagation();
// get updated entity config
const newEntityConfig = ev.detail.config as GraphEntityConfig;
// update card config with updated entity config
const index = this._subElementEditorConfig!.index!;
const newEntities = [...this._config!.entities];
newEntities[index] = newEntityConfig;
let config = this._config!;
config = { ...config, entities: newEntities };
// remove inappropriate stat options dependently on entities
config = await this._cleanConfig(config);
// normalize a generated yaml code
config = this._orderProperties(config);
this._config = config;
// update sub-element editor config
this._subElementEditorConfig = {
...this._subElementEditorConfig!,
elementConfig: {
...(this._config!.entities[index] as GraphEntityConfig),
},
};
fireEvent(this, "config-changed", { config });
}
// remove inappropriate stat options dependently on entities
private async _cleanConfig(
config: StatisticsGraphCardConfig
): Promise<StatisticsGraphCardConfig> {
const entityIds = config.entities.map((entityConf) => {
if (typeof entityConf === "string") {
return entityConf;
}
return entityConf.entity ?? undefined;
});
if (
newEntityIds?.some((statistic_id) => isExternalStatistic(statistic_id)) &&
entityIds.some((statistic_id) => isExternalStatistic(statistic_id)) &&
config.period === "5minute"
) {
delete config.period;
}
const metadata =
config.stat_types || config.unit
? await getStatisticMetadata(this.hass!, newEntityIds)
? await getStatisticMetadata(this.hass!, entityIds)
: undefined;
if (config.stat_types && config.entities.length) {
config.stat_types = ensureArray(config.stat_types).filter((stat_type) =>
@@ -438,10 +561,8 @@ export class HuiStatisticsGraphCardEditor
) {
delete config.unit;
}
config = this._orderProperties(config);
fireEvent(this, "config-changed", {
config,
});
return config;
}
// normalize a generated yaml code by placing lines in a consistent order
@@ -164,7 +164,6 @@ class HuiWeatherEntityRow extends LitElement implements LovelaceRow {
: this._config.secondary_info === "last-changed"
? html`
<ha-relative-time
.hass=${this.hass}
.datetime=${stateObj.last_changed}
capitalize
></ha-relative-time>
@@ -172,7 +171,6 @@ class HuiWeatherEntityRow extends LitElement implements LovelaceRow {
: this._config.secondary_info === "last-updated"
? html`
<ha-relative-time
.hass=${this.hass}
.datetime=${stateObj.last_updated}
capitalize
></ha-relative-time>
-1
View File
@@ -161,7 +161,6 @@ class StateDisplay extends LitElement {
if (relativeDateTime) {
return html`
<ha-relative-time
.hass=${this.hass}
.datetime=${relativeDateTime}
capitalize
></ha-relative-time>
+1 -1
View File
@@ -5575,7 +5575,7 @@
"sunset": "Sunset",
"description": {
"picker": "Tests if the sun is above or below the horizon.",
"between": "Between {beforeChoice, select, \n sunrise { sunrise}\n sunset { sunset}\n other {}\n}{beforeOffsetChoice, select, \n offset { {beforeOffset}}\n other {}\n} and {afterChoice, select, \n sunrise { sunrise}\n sunset { sunset}\n other {}\n}{afterOffsetChoice, select, \n offset { {afterOffset}}\n other {}\n}",
"between": "Between {afterChoice, select, \n sunrise { sunrise}\n sunset { sunset}\n other {}\n}{afterOffsetChoice, select, \n offset { {afterOffset}}\n other {}\n} and {beforeChoice, select, \n sunrise { sunrise}\n sunset { sunset}\n other {}\n}{beforeOffsetChoice, select, \n offset { {beforeOffset}}\n other {}\n}",
"before": "Before {beforeChoice, select, \n sunrise { sunrise}\n sunset { sunset}\n other {}\n}{beforeOffsetChoice, select, \n offset { {beforeOffset}}\n other {}\n}",
"after": "After {afterChoice, select, \n sunrise { sunrise}\n sunset { sunset}\n other {}\n}{afterOffsetChoice, select, \n offset { {afterOffset}}\n other {}\n}"
}
+73 -73
View File
@@ -4599,105 +4599,105 @@ __metadata:
languageName: node
linkType: hard
"@typescript-eslint/eslint-plugin@npm:8.59.4":
version: 8.59.4
resolution: "@typescript-eslint/eslint-plugin@npm:8.59.4"
"@typescript-eslint/eslint-plugin@npm:8.60.0":
version: 8.60.0
resolution: "@typescript-eslint/eslint-plugin@npm:8.60.0"
dependencies:
"@eslint-community/regexpp": "npm:^4.12.2"
"@typescript-eslint/scope-manager": "npm:8.59.4"
"@typescript-eslint/type-utils": "npm:8.59.4"
"@typescript-eslint/utils": "npm:8.59.4"
"@typescript-eslint/visitor-keys": "npm:8.59.4"
"@typescript-eslint/scope-manager": "npm:8.60.0"
"@typescript-eslint/type-utils": "npm:8.60.0"
"@typescript-eslint/utils": "npm:8.60.0"
"@typescript-eslint/visitor-keys": "npm:8.60.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.4
"@typescript-eslint/parser": ^8.60.0
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: ">=4.8.4 <6.1.0"
checksum: 10/75348fdfc5a69bf9837c7ee3a5997bd7e9176eee00682735e46e199e5b67e4748440dd23c65677860a6caaa1c05b67cfafa57a8623da65a17c2832e322f6b7c4
checksum: 10/aec6f08be04ad0014c80e5cf3bd8ec83d59c44244c9ca357c4cf182b6f0debdd690e64daa88215e937183e97c4bdee6749dbf4162191c5851ae9c648439c8a96
languageName: node
linkType: hard
"@typescript-eslint/parser@npm:8.59.4":
version: 8.59.4
resolution: "@typescript-eslint/parser@npm:8.59.4"
"@typescript-eslint/parser@npm:8.60.0":
version: 8.60.0
resolution: "@typescript-eslint/parser@npm:8.60.0"
dependencies:
"@typescript-eslint/scope-manager": "npm:8.59.4"
"@typescript-eslint/types": "npm:8.59.4"
"@typescript-eslint/typescript-estree": "npm:8.59.4"
"@typescript-eslint/visitor-keys": "npm:8.59.4"
"@typescript-eslint/scope-manager": "npm:8.60.0"
"@typescript-eslint/types": "npm:8.60.0"
"@typescript-eslint/typescript-estree": "npm:8.60.0"
"@typescript-eslint/visitor-keys": "npm:8.60.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/6d07a206cac1aa168f9973ac41c3c3a05d7d391a5fa720b4adab5f7c278b62806269ed4e48fcb5706d87213d64bb25f040c9df4824162f8eabb180bee9a2fc23
checksum: 10/f55fa3547e3d0a0ec88bcb886b9bf6cef9b425c016dfa47e2ad7fbcbaa854640ba3f501cc0115824b58f33be4bf8bdf544505847988688906d11c154b600c54d
languageName: node
linkType: hard
"@typescript-eslint/project-service@npm:8.59.4":
version: 8.59.4
resolution: "@typescript-eslint/project-service@npm:8.59.4"
"@typescript-eslint/project-service@npm:8.60.0":
version: 8.60.0
resolution: "@typescript-eslint/project-service@npm:8.60.0"
dependencies:
"@typescript-eslint/tsconfig-utils": "npm:^8.59.4"
"@typescript-eslint/types": "npm:^8.59.4"
"@typescript-eslint/tsconfig-utils": "npm:^8.60.0"
"@typescript-eslint/types": "npm:^8.60.0"
debug: "npm:^4.4.3"
peerDependencies:
typescript: ">=4.8.4 <6.1.0"
checksum: 10/abd5ac32f8792c9bdabdb510d7ba9f708c7760994f3d4a2c741ecf5baecccf7a111e9706eb46a28a3678998671cdc9912f4e8f2423e091bbaea2b6836e07c14d
checksum: 10/21e233d1292775753861aad32b30448f9fb5508f53d5a12c8ce7e75613df236757377fa877c738cc858ac863f2f8259a1f63bfb15a32ee9c5476fe9b2d12fbb0
languageName: node
linkType: hard
"@typescript-eslint/scope-manager@npm:8.59.4":
version: 8.59.4
resolution: "@typescript-eslint/scope-manager@npm:8.59.4"
"@typescript-eslint/scope-manager@npm:8.60.0":
version: 8.60.0
resolution: "@typescript-eslint/scope-manager@npm:8.60.0"
dependencies:
"@typescript-eslint/types": "npm:8.59.4"
"@typescript-eslint/visitor-keys": "npm:8.59.4"
checksum: 10/945a3498a61e27109ef78fa41fbf6ebca76dcc75c4595019bbc131892658b8204e9872239d4a1347fec2b3ca3c3e26460edabf7941cf727e9a35105d6f383c5d
"@typescript-eslint/types": "npm:8.60.0"
"@typescript-eslint/visitor-keys": "npm:8.60.0"
checksum: 10/c08274fdb38be51d2d655ee32bed271cfedf5f5775709da98b3d6cf5f7eb419e98228fb087b48f4a591f4dd71ebcb27a8bd716fa831442c7cad708288625e454
languageName: node
linkType: hard
"@typescript-eslint/tsconfig-utils@npm:8.59.4, @typescript-eslint/tsconfig-utils@npm:^8.59.4":
version: 8.59.4
resolution: "@typescript-eslint/tsconfig-utils@npm:8.59.4"
"@typescript-eslint/tsconfig-utils@npm:8.60.0, @typescript-eslint/tsconfig-utils@npm:^8.60.0":
version: 8.60.0
resolution: "@typescript-eslint/tsconfig-utils@npm:8.60.0"
peerDependencies:
typescript: ">=4.8.4 <6.1.0"
checksum: 10/c74a356e67f17aa2b61bffbe145548a5b37e6db34077922fb74c5f07393cd3a1ef28742f9a3e3acc53ac0e95c8af6e959bdf44988e036d634492d79a9dd219b1
checksum: 10/d82cac7dec0366c0e680d002b4d20bc2564a198a2d9a80099f4fa7ee2b2f394dd2d47df03f1c4b276c4de6c7b8684a50e7bad0ddd5b33907188e90cc203a9593
languageName: node
linkType: hard
"@typescript-eslint/type-utils@npm:8.59.4":
version: 8.59.4
resolution: "@typescript-eslint/type-utils@npm:8.59.4"
"@typescript-eslint/type-utils@npm:8.60.0":
version: 8.60.0
resolution: "@typescript-eslint/type-utils@npm:8.60.0"
dependencies:
"@typescript-eslint/types": "npm:8.59.4"
"@typescript-eslint/typescript-estree": "npm:8.59.4"
"@typescript-eslint/utils": "npm:8.59.4"
"@typescript-eslint/types": "npm:8.60.0"
"@typescript-eslint/typescript-estree": "npm:8.60.0"
"@typescript-eslint/utils": "npm:8.60.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/6c513a9ab5e9da3fafe165b64da73c984696e3f583ae4a5c7d188f16f9425ec488ed4aff39d8b419a742e92078fb1cb4384dce9e1efa7bb193e3aa0cdcff84c8
checksum: 10/4b29dcc1ee7a006b2df8a50700b43701bedd4f8380e94311a8988102d98fdd89244c233a8063a800cbdee86278bdc98874bfa6a8a3c71f1b278be1be6698961b
languageName: node
linkType: hard
"@typescript-eslint/types@npm:8.59.4, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.59.4":
version: 8.59.4
resolution: "@typescript-eslint/types@npm:8.59.4"
checksum: 10/43e7ab668f1649dee76829a5b1eb0807b7576b39aee5b23fe10936d99536cba350ec1e377c2ea2242971435d460e28f3d3d4307357ac9f8184403573ece85ca6
"@typescript-eslint/types@npm:8.60.0, @typescript-eslint/types@npm:^8.56.0, @typescript-eslint/types@npm:^8.60.0":
version: 8.60.0
resolution: "@typescript-eslint/types@npm:8.60.0"
checksum: 10/8c6967503b3a370af10fea7bfec9adc7a4152e0e8aaa72ee790f105f08721683f6e8829acf610de82bfcdeb56bdf07f6795ccec394edbdac222fd3a1d76fe9cd
languageName: node
linkType: hard
"@typescript-eslint/typescript-estree@npm:8.59.4":
version: 8.59.4
resolution: "@typescript-eslint/typescript-estree@npm:8.59.4"
"@typescript-eslint/typescript-estree@npm:8.60.0":
version: 8.60.0
resolution: "@typescript-eslint/typescript-estree@npm:8.60.0"
dependencies:
"@typescript-eslint/project-service": "npm:8.59.4"
"@typescript-eslint/tsconfig-utils": "npm:8.59.4"
"@typescript-eslint/types": "npm:8.59.4"
"@typescript-eslint/visitor-keys": "npm:8.59.4"
"@typescript-eslint/project-service": "npm:8.60.0"
"@typescript-eslint/tsconfig-utils": "npm:8.60.0"
"@typescript-eslint/types": "npm:8.60.0"
"@typescript-eslint/visitor-keys": "npm:8.60.0"
debug: "npm:^4.4.3"
minimatch: "npm:^10.2.2"
semver: "npm:^7.7.3"
@@ -4705,32 +4705,32 @@ __metadata:
ts-api-utils: "npm:^2.5.0"
peerDependencies:
typescript: ">=4.8.4 <6.1.0"
checksum: 10/6ca75d11bd5e1f44ddc0cd8ef4f4833257bcb1d48b82485b55008cd6761f4dba0671754098ce5b2910432ba05f5f9247c34942f3e8de2857cb5ab0d25b866876
checksum: 10/ad02384fd48152a7d9bb5db1aa5d6cbda1cfa9e549a2d529d801ec1401d1d7d011c5e071f5b4d99c5ed656c95e5e97c46a783b45dcc7c016df7fee37ab5bdc0a
languageName: node
linkType: hard
"@typescript-eslint/utils@npm:8.59.4":
version: 8.59.4
resolution: "@typescript-eslint/utils@npm:8.59.4"
"@typescript-eslint/utils@npm:8.60.0":
version: 8.60.0
resolution: "@typescript-eslint/utils@npm:8.60.0"
dependencies:
"@eslint-community/eslint-utils": "npm:^4.9.1"
"@typescript-eslint/scope-manager": "npm:8.59.4"
"@typescript-eslint/types": "npm:8.59.4"
"@typescript-eslint/typescript-estree": "npm:8.59.4"
"@typescript-eslint/scope-manager": "npm:8.60.0"
"@typescript-eslint/types": "npm:8.60.0"
"@typescript-eslint/typescript-estree": "npm:8.60.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: ">=4.8.4 <6.1.0"
checksum: 10/90d779f1af27e34df7e44f93135c08908e98c4b830329be1220d5edd0e024272f6d5a021506dae7fe6446a8bf05ccd0a5e7fb3ed36399dcf4f52959bb302c62d
checksum: 10/9fc8bc7a62deacd3823d957de8e8ca2012ffa90715734cd89d0e3a62c2c9e2775d3ba9da80e419339893a44af8674d690488cb195c981e8de9fd9dfa4948956d
languageName: node
linkType: hard
"@typescript-eslint/visitor-keys@npm:8.59.4":
version: 8.59.4
resolution: "@typescript-eslint/visitor-keys@npm:8.59.4"
"@typescript-eslint/visitor-keys@npm:8.60.0":
version: 8.60.0
resolution: "@typescript-eslint/visitor-keys@npm:8.60.0"
dependencies:
"@typescript-eslint/types": "npm:8.59.4"
"@typescript-eslint/types": "npm:8.60.0"
eslint-visitor-keys: "npm:^5.0.0"
checksum: 10/b620ba3a702817cc1cff6ac654ef5538be0b5076a478546c9bf42ece25bc3b08a33a4d003b9b42d10bd1041ea246f108863aceca0683b4389cf18d55dcdbee10
checksum: 10/4854d08416e2c97837cc1ecf8dacb50b3337ebb34bd6d703ad40b6585fdf78243074e56994ddc90650586146cebd6ad7390b6fa3ddda4e3532be4b872dd8f541
languageName: node
linkType: hard
@@ -8658,7 +8658,7 @@ __metadata:
tinykeys: "npm:4.0.0"
ts-lit-plugin: "npm:2.0.2"
typescript: "npm:6.0.3"
typescript-eslint: "npm:8.59.4"
typescript-eslint: "npm:8.60.0"
vite-tsconfig-paths: "npm:6.1.1"
vitest: "npm:4.1.7"
webpack-stats-plugin: "npm:1.1.3"
@@ -13641,18 +13641,18 @@ __metadata:
languageName: node
linkType: hard
"typescript-eslint@npm:8.59.4":
version: 8.59.4
resolution: "typescript-eslint@npm:8.59.4"
"typescript-eslint@npm:8.60.0":
version: 8.60.0
resolution: "typescript-eslint@npm:8.60.0"
dependencies:
"@typescript-eslint/eslint-plugin": "npm:8.59.4"
"@typescript-eslint/parser": "npm:8.59.4"
"@typescript-eslint/typescript-estree": "npm:8.59.4"
"@typescript-eslint/utils": "npm:8.59.4"
"@typescript-eslint/eslint-plugin": "npm:8.60.0"
"@typescript-eslint/parser": "npm:8.60.0"
"@typescript-eslint/typescript-estree": "npm:8.60.0"
"@typescript-eslint/utils": "npm:8.60.0"
peerDependencies:
eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
typescript: ">=4.8.4 <6.1.0"
checksum: 10/f065a4ae6abfad6af0a09de7052315cd8fa97a2e8ff0985aa8ad6ba0e059a662ef3b22751fbf58302de634b712a7d019f10f488d8e43b0510842a4f6460e57f4
checksum: 10/625e49e6d06e32adcfe903087d1fb2adc3be925adafe1f4e57f690bb196b35e2aac01760a3d5e17a53ea2feb6fef3a13da4b8faa214f628ec56e64f99f20e4ad
languageName: node
linkType: hard